Stap 11: Het programma, deel 3
Nu is het tijd voor de "core" van het programma, het bestand "robot.c".Op de top van de "robot.c" bestand dat we nodig:
#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];
Onderdeel hiervan is de generieke bibliotheek voor Palm OS. Ik ben ook verklaren dat alle van de functies die ik bij de bovenkant, gebruikt heb zodat u niet hoeft te maken over eventuele speciale bestelling bij het bewerken van het bestand robot.c. Ik heb ook aan de onderkant een paar globale variabelen die ik gebruik op verschillende punten in het programma.
De "PilotMain"-functie is de toegangspoort naar het programma. Er zijn verschillende redenen waarom een programma kan worden gestart op een Palm-Pilot, maar we willen alleen maar zorgen over een; Als het programma is gestart door de gebruiker PilotMain() de rest van het programma zal starten.
UInt32 PilotMain (UInt16 cmd, MemPtr cmdPBP, UInt16 launchFlags) { if (cmd == sysAppLaunchCmdNormalLaunch) { AppStart(); AppEventLoop(); AppStop(); } return 0; }
De AppStart() en AppStop() functies zijn vrij basic, AppStart() Zorg ervoor dat de toepassing naar behoren wordt gestart, en AppStop() zorgt ervoor dat de toepassing sluit netjes.
void AppStart() { FrmGotoForm(FormMain); } void AppStop() { Disconnect(); FrmCloseAllForms(); }
AppEventLoop() is de kern van het programma. Wanneer iets gebeurt zal AppEventLoop() ophalen van de "event" en proberen om het te behandelen. Eerst stuurt het evenement naar een paar ingebouwde functies om te proberen en laat het systeem de gebeurtenis verwerken. Als het systeem niet volledig de gebeurtenis AppEventLoop() verwerken en geeft vervolgens de gebeurtenis aan onze eigen functies om te proberen en omgaan. FrmDispatchEvent() is de gebeurtenishandlerfunctie die wij toegewezen aan het verwerken van gebeurtenissen voor het actieve formulier.
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() is de eerste niet-systeem-functie die AppEventLoop() gebeurtenissen naar verstuurt. Het wordt gecontroleerd als de gebeurtenis het laden van een van de vormen betekent. Als dat zo is het ingesteld opwaarts naar de vorm en kent een standaard gebeurtenis-handler (voor 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() is de volgende functie op AppEventLoop () in de lijst. Als de gebruiker een menu maakt selectie MenuHandler() zal omgaan.
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() en Disconnect() worden gebruikt voor het openen en sluiten van de Palm-Pilot seriële poort. Weer of niet de robot tot de Palm-Pilot wordt vastgehaakt en ingeschakeld wanneer deze functies worden gebruikt, is niet te belangrijk. Tough, als de robot niet ten minste eenmaal de "Start"-opdracht ontvangt (het wordt verzonden wanneer de Connect()-functie wordt aangeroepen) u zou kunnen hebben enkele problemen bij het uitvoeren van uw eigen 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() wordt gebruikt door verschillende functies om gegevens te geven in de tekstvelden die is gedefinieerd in het bestand "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() wordt gebruikt door het hoofdformulier (het formulier "Status") sensor info ophalen met de robot en vervolgens weer te geven voor de gebruiker. Eerst wordt de opdracht "Query lijst" verzonden naar de robot met een lijst van sensoren die wij zijn geïnteresseerd in. DisplaySensors() zal vervolgens analyseren van de robot de antwoord en de weergave dienovereenkomstig bijwerken.
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() is de functie die is toegewezen aan de main (status) vorm. Het is ontworpen om het afhandelen van gebeurtenissen die specifiek zijn voor het formulier status. De individuele formulierhandler meestal gewoon afhandelen van gebeurtenissen die zijn veroorzaakt door de gebruiker, maar de belangrijkste formulierhandler zal ook reageren op nillEvent die zijn gegenereerd voor elke 50 "teken" (een teek is een meting van de tijd dat de Palm OS gebruikt) door 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() omgaat met gebeurtenissen voor het formulier selecteren.
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() omgaat met gebeurtenissen voor het formulier scripts. Een groot deel van de ScriptFormHandler() is onderscheid te maken tussen de diverse programmeren knoppen. Omdat de programmeerknop id's opeenvolgende getallen zijn, in plaats van op zoek naar specifieke knop gebeurtenissen we voor knop bereiken kijken kunnen.
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; }
De display ()-functie de gegevens in de array script vertaalt een string van mens-vriendelijke en weergegeven in een tekstveld dat de gebruiker kan zien.
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); }
Ten slotte de functie SendScript() duurt het script variabele en vertalen in opdrachten dat de iRobot Create kan begrijpen, en het resultaat vervolgens verzenden in uw robot. Het wordt aangeroepen vanuit de ScriptFormHandler() functie.
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; }