Étape 11 : Le programme, partie 3
Il est maintenant temps pour le « noyau » du programme, le fichier « robot.c ».Tout en haut du fichier « robot.c » nous avons besoin :
#include "robot.h" #include "string_arrays.c" #include <PalmTypes.h> #include <PalmCompatibility.h> #include <System/SystemPublic.h> #include <UI/UIPublic.h> void SetField (UInt16 formID, UInt16 fieldID, MemPtr str); Int16 Connect(); Boolean Disconnect(); void DisplaySensors(); Boolean MenuHandler (EventPtr event); Boolean SelectFormHandler (EventPtr event); Int16 SendScript (char length); void Display (char length); Boolean ScriptFormHandler (EventPtr event); Boolean MainFormHandler (EventPtr event); Boolean AppHandleEvent (EventPtr event); void AppEventLoop(); void AppStart(); void AppStop(); UInt32 PilotMain (UInt16 cmd, MemPtr cmdPBP, UInt16 launchFlags); // GlobalsUInt16 port = 0; char prog = 0; unsigned char script[30][2];
Ce cadre est de la bibliothèque générique de Palm OS. Je suis également déclarer toutes les fonctions que j’ai utilisé en haut, donc vous n’avez pas à vous soucier de toute commande spéciale lorsque vous modifiez le fichier robot.c. Au fond, j’ai aussi quelques variables globales que j’utilise à divers points dans le programme.
La fonction « PilotMain » est le point d’entrée au programme. Il y a plusieurs raisons pourquoi un programme pourrait être lancé sur un Palm Pilot, mais nous voulons seulement s’inquiéter environ un ; Si le programme a été lancé par l’utilisateur PilotMain() commencera le reste du programme.
UInt32 PilotMain (UInt16 cmd, MemPtr cmdPBP, UInt16 launchFlags) { if (cmd == sysAppLaunchCmdNormalLaunch) { AppStart(); AppEventLoop(); AppStop(); } return 0; }
Les fonctions AppStart() et AppStop() sont assez basique, AppStart() Assurez-vous que l’application démarre correctement, et AppStop() s’assure de l’application se ferme correctement.
void AppStart() { FrmGotoForm(FormMain); } void AppStop() { Disconnect(); FrmCloseAllForms(); }
AppEventLoop() est au cœur du programme. Chaque fois que quelque chose se passe AppEventLoop() récupérera le « événement » et essayez de le manipuler. Tout d’abord, il envoie l’événement à quelques fonctions intégrées pour essayer de laisser le système gérer l’événement. Si le système ne gère entièrement l’événement AppEventLoop(), puis transmet l’événement à nos propres fonctions pour tenter de le manipuler. FrmDispatchEvent() est la fonction de gestionnaire d’événements que nous avons attribué à gérer les événements de formulaire actif.
void AppEventLoop() { EventType event; short error; do { EvtGetEvent (&event, 50); // Wait 100 ticks before sending a nilEvent // Allow the system to attemt to handle events before trying ourself if (SysHandleEvent(&event)) continue; if (MenuHandleEvent((void*)0, &event, &error)) continue; if (AppHandleEvent(&event)) continue; if (MenuHandler(&event)) continue; // Send the event to the form event handler FrmDispatchEvent(&event); } while (event.eType != appStopEvent); }
AppHandleEvent() est la première fonction non-système AppEventLoop() envoie des événements. Il vérifie pour voir si l’événement signifie le chargement de l’une des formes. Dans l’affirmative il met en place le formulaire et assigne un gestionnaire d’événements par défaut (pour FrmDispatchEvent()).
Boolean AppHandleEvent (EventPtr event) { FormPtr frm; Int frmID; Boolean handled = false; char str[255]; str[0] = '\0'; if (event->eType == frmLoadEvent) { frmID = event->data.frmLoad.formID; frm = FrmInitForm(frmID); FrmSetActiveForm(frm); switch (frmID) { case FormMain: FrmSetEventHandler(frm, MainFormHandler); FrmDrawForm(frm); StrCat(str, PROGRAM[prog]); SetField(frmID, FldProg, str); handled = true; break; case FormSelect: FrmSetEventHandler(frm, SelectFormHandler); FrmDrawForm(frm); StrCat (str, DISCRIPIONS[prog]); SetField (frmID, FldDescription, str); handled = true; break; case FormMacro: FrmSetEventHandler(frm, ScriptFormHandler); FrmDrawForm(frm); handled = true; break; } } return handled; }
MenuHandler() est la fonction suivante sur la liste de AppEventLoop (). Si l’utilisateur fait un menu de sélection MenuHandler() Il gérera.
Boolean MenuHandler (EventPtr event) { Boolean handled = false; Err err; // The command that runs the demo program that corosponds to 'prog' char data[] = {128, 136, prog}; // Handle the menu button's if (event->eType == menuEvent) { MenuEraseStatus(NULL); switch (event->data.menu.itemID) { // Go to the verious forms case MnuStatus: FrmGotoForm(FormMain); handled = true; break; case MnuScript: FrmGotoForm(FormSelect); handled = true; break; case MnuMacro: FrmGotoForm(FormMacro); handled = true; break; // Open the serial connection case MnuConnect: Connect(); handled = true; break; // Close the serial connection case MnuDisconnect: Disconnect(); handled = true; break; // Start/Stop the currently selected program or macro case MnuRun: if (!Connect()) break; // Send the command to the iRobot SrmSend(port, data, 3, &err); SrmSendFlush(port); handled = true; break; case MnuStop: // Replace the 'prog' byte with -1 (255) to stop the currently running program data[2] = 255; // Send the command to the iRobot if (!Connect()) break; SrmSend(port, data, 3, &err); SrmSendFlush(port); handled = true; break; case MnuAbout: FrmAlert(AboutAlert); handled = true; break; } } return handled; }
Connect() et Disconnect() servent à ouvrir et fermer le port série de la Palm Pilot. Météo ou pas le robot est branché à la Palm Pilot et activée lorsque ces fonctions sont utilisées n’est pas trop important. Dur, si le robot ne reçoit pas au moins une fois la commande « Start » (il est envoyé lorsque la fonction Connect() est appelée) vous pourriez avoir quelques problèmes pour exécuter vos propres scripts.
Int16 Connect() { Err err; char data = 128; // Open the serial port if (!port) SrmOpen(serPortCradlePort, 57600, &port); // Send the "Start" command to make sure the Create comes out of "Off" mode SrmSend(port, &data, 1, &err); SrmSendFlush(port); return port; } Boolean Disconnect() { // Close the serial port if (port && !SrmClose(port)) { port = 0; return true; } else return false; }
SetFiled() est utilisé par diverses fonctions pour afficher des données dans les champs de texte définis dans le fichier « robot.rcp ».
void SetField (UInt16 formID, UInt16 fieldID, MemPtr str){ FormPtr frm; FieldPtr fld; UInt16 obj; CharPtr p; VoidHand h; frm = FrmGetFormPtr(formID); obj = FrmGetObjectIndex(frm, fieldID); fld = (FieldPtr)FrmGetObjectPtr(frm, obj); h = (VoidHand)FldGetTextHandle(fld); if (h == NULL) { h = MemHandleNew (FldGetMaxChars(fld)+1); ErrFatalDisplayIf(!h, "No Memory"); } p = (CharPtr)MemHandleLock(h); StrCopy(p, str); MemHandleUnlock(h); FldSetTextHandle(fld, (Handle)h); FldDrawField(fld); }
DisplaySensors() est utilisé par le formulaire principal (la forme de « Status ») pour récupérer les info capteur du robot et ensuite l’afficher à l’utilisateur. Tout d’abord, il envoie la commande « List Query » au robot avec une liste de capteurs qui nous intéresse. DisplaySensors() alors analyser la réponse du robot et mettre à jour l’affichage en conséquence.
void DisplaySensors() { // The data packet sent to the create that requests the sensor data char data[] = {128, 149, 8, 8, 7, 9, 10, 11, 12, 22, 21}; // The byte array that holds the sensor data retrived by the create char inData[9]; char str[20]; UInt16 tmp, *volt; Err err; VoidHand bitmapHandle; BitmapPtr bitmap; Boolean wheall = false, whealr = false; str[0] = '\0'; if (port) { // Only perform this loop if the serial connection is open FrmDrawForm(FrmGetActiveForm()); // Re-draw the form, to clear the old data // Send the request to the create for the sensor data, and put the reply in 'inData' SrmReceiveFlush(port, 0); SrmSend(port, data, 11, &err); SrmSendFlush(port); SrmReceive(port, inData, 9, 20, &err); // The wall sensor if (inData[0]) { bitmapHandle = DmGetResource('Tbmp', PicWall); bitmap = MemHandleLock(bitmapHandle); WinDrawBitmap(bitmap, 110, 25); MemHandleUnlock(bitmapHandle); } // The bumper's and wheal drop sensors tmp = (int)inData[1]; if (tmp >= 16) tmp=tmp-16; // Caster wheal if (tmp >= 8) { // Left wheal wheall = true; tmp = tmp - 8; } if (tmp >= 4) { // Right wheal whealr = true; tmp = tmp - 4; } if (tmp >= 2) { // Left wheal bitmapHandle = DmGetResource('Tbmp', PicBumpL); bitmap = MemHandleLock(bitmapHandle); WinDrawBitmap(bitmap, 25, 30); MemHandleUnlock(bitmapHandle); tmp = tmp - 2; } if (tmp == 1) { // Right wheal bitmapHandle = DmGetResource('Tbmp', PicBumpR); bitmap = MemHandleLock(bitmapHandle); WinDrawBitmap(bitmap, 60, 30); MemHandleUnlock(bitmapHandle); } // We draw the wheal's after drawing the bumpers, so the // pictures overlap properly if (wheall) { bitmapHandle = DmGetResource('Tbmp', PicWhealL); bitmap = MemHandleLock(bitmapHandle); WinDrawBitmap(bitmap, 37, 52); MemHandleUnlock(bitmapHandle); } if (whealr) { bitmapHandle = DmGetResource('Tbmp', PicWhealR); bitmap = MemHandleLock(bitmapHandle); WinDrawBitmap(bitmap, 71, 52); MemHandleUnlock(bitmapHandle); } // The 4 clif sensors if (inData[2]+inData[3]+inData[4]+inData[5] > 0) { bitmapHandle = DmGetResource('Tbmp', PicCliff); bitmap = MemHandleLock(bitmapHandle); if (inData[2] > 0) WinDrawBitmap(bitmap, 21, 35); // Left if (inData[3] > 0) WinDrawBitmap(bitmap, 31, 25); // Top left if (inData[4] > 0) WinDrawBitmap(bitmap, 80, 25); // Top Right if (inData[5] > 0) WinDrawBitmap(bitmap, 90, 35); // Right MemHandleUnlock(bitmapHandle); } // The raw battery voltage volt = (UInt16*)(inData + 6); StrIToA(str, *volt); StrCat(str, " mV"); if (inData[8] > 0 && inData[8] <= 3) StrCat(str, " (crg)"); SetField(FormMain, FldVoltage, str); } }
MainFormHandler() est la fonction attribuée à l’écran principale (statut). Il est conçu pour gérer les événements spécifiques à votre situation. Le gestionnaire formulaire individuel surtout juste gérer les événements causés par l’utilisateur, mais le gestionnaire de formulaire principal sera également répondre aux nillEvent qui sont générés chaque 50 « ticks » (une graduation est une mesure du temps qui utilise Palm OS) de AppEventLoop().
Boolean MainFormHandler (EventPtr event) { Boolean handled = false; switch (event->eType) { // Every 50 ticks we check the create's sensors (pending a serial connection) case nilEvent: DisplaySensors(); handled = true; break; case ctlSelectEvent: if (event->data.ctlSelect.controlID == BtnDisconnect) { Disconnect(); handled = true; } break; } return handled; }
SelectFormHandler() gère les événements du formulaire select.
Boolean SelectFormHandler (EventPtr event) { Boolean handled = false; char str[255]; str[0] = '\0'; switch (event->eType) { case lstSelectEvent: prog = event->data.lstSelect.selection; StrCat (str, DISCRIPIONS[prog]); SetField (FormSelect, FldDescription, str); handled = true; break; } return handled; }
ScriptFormHandler() gère les événements pour la forme de scripts. Une grande partie des ScriptFormHandler() est une distinction entre les différents boutons de programmation. Parce que le bouton de programmation ID sont des numéros consécutifs, au lieu de chercher des événements de bouton spécifique nous pouvons chercher des gammes de bouton.
Boolean ScriptFormHandler (EventPtr event) { const char MAX_SCRIPT = 8; static char length = 0; Boolean handled = false; UInt16 id; Int16 x; FieldPtr fld; FormPtr frm; switch (event->eType) { case frmOpenEvent: Display(length); handled=true; break; // Handle the various buttons case ctlSelectEvent: id = event->data.ctlSelect.controlID; switch (id) { case BtnSend: SendScript(length); handled = true; break; case BtnClear: length = 0; Display(length); handled = true; break; case BtnDel: length--; if (length < 0) length = 0; Display(length); handled = true; break; } // The script action buttons if (id >= BtnUp && id <= BtnPause) { if (length < MAX_SCRIPT) { script[length][0] = id-3100; script[length][1] = 1; length++; } Display(length); handled = true; } // The script time buttons if (id >= BtnP1 && id <= BtnP16) { x = script[length-1][1]; script[length-1][1] = (x+(id-3200) <= 25) ? x+(id-3200) : x; Display(length); handled = true; } break; // The scroll bar case sclRepeatEvent: x = event->data.sclRepeat.newValue - event->data.sclRepeat.value; frm = FrmGetActiveForm(); fld = (FieldPtr)FrmGetObjectPtr (frm, FrmGetObjectIndex(frm, FldScript)); if (x >= 0) FldScrollField (fld, x, winDown); else FldScrollField (fld, x*-1, winUp); break; } return handled; }
La fonction de Display() traduit les données dans le tableau de scripts dans une chaîne lisible par un humain et l’affiche dans un champ de texte que l’utilisateur peut voir.
void Display (char length) { char str[1000]; char x; char tmp[10]; str[0] = '\0'; // Turn the script array into a string and display it for (x = 0; x < length; x++) { StrCat (str, COMMANDS[script[x][0]]); StrCat (str, " for \0"); StrIToA (tmp, script[x][1]); StrCat (str, tmp); StrCat (str, " Seconds\n\0"); } SetField (FormMacro, FldScript, str); }
Enfin la fonction SendScript() prend la variable de script et traduire en commandes que l’iRobot Create peut comprendre et ensuite envoyer le résultat à votre robot. Il est appelé par la fonction ScriptFormHandler().
Int16 SendScript (char length) { char safe[] = {128, 131}; char data[150]; char i; char x = 0; char y; char cmds = 0; Err err; data[x] = 131; data[++x] = 152; y = ++x; for (i = 0; i < length; i++) { switch (script[i][0]) { case 0: data[++x] = 137; data[++x] = 0; data[++x] = 150; data[++x] = 128; data[++x] = 0; data[++x] = 155; data[++x] = script[i][1]*10; data[++x] = 137; data[++x] = 0; data[++x] = 0; data[++x] = 128; data[++x] = 0; cmds += 3; break; case 1: data[++x] = 137; data[++x] = 255; data[++x] = 106; data[++x] = 128; data[++x] = 0; data[++x] = 155; data[++x] = script[i][1]*10; data[++x] = 137; data[++x] = 0; data[++x] = 0; data[++x] = 128; data[++x] = 0; cmds += 3; break; case 2: data[++x] = 137; data[++x] = 0; data[++x] = 150; data[++x] = 1; data[++x] = 44; data[++x] = 155; data[++x] = script[i][1]*10; data[++x] = 137; data[++x] = 0; data[++x] = 0; data[++x] = 128; data[++x] = 0; cmds += 3; break; case 3: data[++x] = 137; data[++x] = 0; data[++x] = 150; data[++x] = 254; data[++x] = 212; data[++x] = 155; data[++x] = script[i][1]*10; data[++x] = 137; data[++x] = 0; data[++x] = 0; data[++x] = 128; data[++x] = 0; cmds += 3; break; case 4: data[++x] = 137; data[++x] = 0; data[++x] = 125; data[++x] = 0; data[++x] = 1; data[++x] = 155; data[++x] = script[i][1]*10; data[++x] = 137; data[++x] = 0; data[++x] = 0; data[++x] = 128; data[++x] = 0; cmds += 3; break; case 5: data[++x] = 137; data[++x] = 0; data[++x] = 125; data[++x] = 255; data[++x] = 255; data[++x] = 155; data[++x] = script[i][1]*10; data[++x] = 137; data[++x] = 0; data[++x] = 0; data[++x] = 128; data[++x] = 0; cmds += 3; break; case 6: data[++x] = 155; data[++x] = script[i][1]*10; cmds += 1; break; } } data[y] = x+1; Connect(); // Send the script SrmSend(port, data, x+1, &err); SrmSendWait(port); SrmSendFlush(port); return err; }