Repository for import to local machine
Dependencies: DMBasicGUI DMSupport
main.cpp
- Committer:
- jmitc91516
- Date:
- 2017-07-21
- Revision:
- 3:010aeeacd7d7
- Parent:
- 2:6e94a7fd1e37
- Child:
- 4:6840cf2b153a
File content as of revision 3:010aeeacd7d7:
#include "mbed.h" #include "DMBoard.h" #include "lpc_swim.h" #include "lpc_swim_font.h" #include <string.h> #include <stdio.h> #include <stdlib.h> #include "GuiLib.h" #include "GuiDisplay.h" #include "USBHostGC.h" #include "TouchPanelPageSelector.h" #include "GCHeatControl.h" #include "GetGCStatusLoop.h" #include "GCComponentStatusColorArea.h" #include "GCStateAndFaultCodes.h" #include "ProgressBar.h" #include "EasyGUITouchAreaIndices.h" #include "NetworkParameters.h" #include "ServiceInterval.h" #include "SettingsHandler.h" #include "GasCalibrationPageHandler.h" #include "ColumnDHAutoCalibrationPageHandler.h" #include "ColumnDHManualCalibrationPageHandler.h" #include "ColumnDHSensorCalibrationPageHandler.h" #include "ColumnDHPSUDACPageHandler.h" #include "GasBackPressureDACPageHandler.h" #include "GasChannelDACAndADCPageHandler.h" #include "NudgeAndDampPageHandler.h" #include "NumericKeypadPageHandler.h" #include "EthernetKeypadPageHandler.h" #include "DebugCommandsPageHandler.h" #include "QSPIBitmap.h" #include "DetectorIgnitionHandler.h" #include "SwimDraw.h" #include "USBHostGCUtilities.h" #define BUILD_DATE "21 July 2017" // Copied to easyGUI variable "GuiVar_buildDate", displayed on both Settings pages (normal and Running). // *** MUST update for each 'delivered' build, MUST correspond with date on 'delivered' binary file *** // This should be updated first thing every morning, and immediately after every 'delivery' // (to e.g. "dd mmm yyyy #2"), so that it is ready for the next one. // Also - 'deliver' the ZIP file produced by the 'Export Program' command (right-click on the project // in 'Program Workspace' at the left of this screen), as well as the binary executable and the ReadMe.txt file, // and now the EasyGUI project file as well. // Defined in GuiDisplay.c - set the display frame address at runtime extern "C" { void GuiDisplay_SetFrameAddress(void *newFrameAddress); } // Used by QSPIBitmaps class bool qspiAlreadyFormatted = false; //#define MEMORY_TEST_OCT_2016 #ifdef MEMORY_TEST_OCT_2016 const int clogLength = 1000; char clogUpMemory[clogLength]; #endif // MEMORY_TEST_OCT_2016 /* This application provides a draft implementation of the LPC4088 user interface to the GC. It talks to the GC over a USB link. The associated easyGUI project is C:/easyGUI Projects/GC500_5inch.gui It is intended for use with the Embedded Artists LPC4088 board, 5 inch display. Note that the two most important functions in this application are: main (obviously) or, more specifically, getGCStatusLoop->MainLoopWithEthernet(), called from main TouchCallback, which handles the user's interaction with the LPC4088 touch screen Most other functions are (ultimately) called by those two. */ // Forward declarations GuiConst_INTCOLOR SixteenBitColorValue(GuiConst_INT8U red, GuiConst_INT8U green, GuiConst_INT8U blue); void DrawHeatOnOffButton(void); void SetupDoorActuatorCommandUserInterface(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC, bool beforePageDisplay); static bool canUnlockDoorActuators = true; // Set false if there is some reason why they cannot be unlocked even when the GC is idle /* Function to gives the rest of the world access to the above flag */ void CanUnlockDoorActuators(bool value) { canUnlockDoorActuators = value; } //#define ALLOW_DEBUG_PRINTS // Without this, our 'debug print' functions contain no code, and do nothing. // So we do not have to remove all the debug prints throughout this application - // just comment out this #define. // Note that we are currently *not* applying this to the code in the 'DrawErrorMessage()' function, // which is only used here in main.cpp. /* Code received from Embedded Artists, to cause the LPC4088 to reboot (thus allowing us to restart the Ethernet connection with a different IP address) */ void reboot() { /* Disable watchdog */ LPC_WDT->MOD = 0; LPC_WDT->TC = 0xFF; /* Set WDT timeout to 0.1s using the 500kHz oscillator and the fixed pre-scaler of 4 */ LPC_WDT->TC = ((500000 / 4) / 10); /* Make sure a watchdog timeout causes a reset */ LPC_WDT->MOD |= (1<<1); /* Enable the watchdog */ LPC_WDT->MOD |= (1<<0); /* Initial feed to start the watchdog */ LPC_WDT->FEED = 0xAA; LPC_WDT->FEED = 0x55; /* Should reboot after 100ms */ while(true); } // ** Start of timeout code to guard against multiple presses of the Heat On/Off button ** // (which is in the same position as the Abort Run button) Timeout heatOnOffTimeout; bool heatOnOffAvailable = true; void MakeHeatOnOffAvailableAgain(void) { heatOnOffAvailable = true; } void StartHeatOnOffTimeout(void) { heatOnOffAvailable = false; heatOnOffTimeout.attach(&MakeHeatOnOffAvailableAgain, 1.0); // Wait 1.0 sec before accepting touches again on Heat On/Off button } // ** End of Heat On/Off timeout code ** #define USE_HEAT_ONOFF_BUTTON_BITMAPS //#define TURN_HEAT_OFF_ON_ABORT // Doing this may or may not be a good idea... // These are 'global' - TouchCallback function, as well as main(), needs to access them TouchPanelPageSelectors touchPanelPageSelectors; GCHeatControl* theGCHeatControl; GetGCStatusLoop* getGCStatusLoop = NULL; HomePageGCComponentStatusColorAreas homePageGCComponentStatusColorAreas; SingleGCComponentPageStatusColorAreas singleGCComponentPageStatusColorAreas; #define USE_QSPI_BITMAPS #ifdef USE_QSPI_BITMAPS QSPIBitmaps qspiBitmaps; #endif // USE_QSPI_BITMAPS //#define WANT_STATUS_RECTANGLE_ON_COLUMN_AUTO_CALIB_PAGE //#define WANT_STATUS_RECTANGLE_ON_GAS_CALIB_PAGES /* Bodge so GuiDisplay.c ('easyGUIFixed' file) can call Thread::wait (giving it an accurate millisecond timer) */ extern "C" { void EasyGUIWaitMs(GuiConst_INT32U msec) { Thread::wait(msec); } } /* Passed three 8-bit colour components - red, green and blue. Returns the corresponding 16-bit colour value (5 bits for red, 6 bits for green, 5 bits for blue). */ GuiConst_INTCOLOR SixteenBitColorValue(GuiConst_INT8U red, GuiConst_INT8U green, GuiConst_INT8U blue) { // Make sure we don't have numeric overflow problems during the conversion GuiConst_INT32U red32 = red; GuiConst_INT32U green32 = green; GuiConst_INT32U blue32 = blue; //#define REVERSE_RED_AND_BLUE #ifdef REVERSE_RED_AND_BLUE GuiConst_INT32U rgb = (red32 << 16) | (green32 << 8) | blue32; #else GuiConst_INT32U rgb = (blue32 << 16) | (green32 << 8) | red32; #endif return GuiLib_RgbToPixelColor(rgb); } // DebugPrint without '#ifdef ALLOW_DEBUG_PRINTS' void SpecialDebugPrint(char *stuffToPrint, GuiConst_INT16S X, GuiConst_INT16S Y) { static int counter = 0; char buff[300]; const GuiConst_INT16U fontNo = GuiFont_Helv1; GuiDisplay_Lock(); // (Attempt to) clear previous strings from display sprintf(buff, " "); GuiLib_DrawStr( X, //GuiConst_INT16S X, Y, //GuiConst_INT16S Y, fontNo, //GuiConst_INT16U FontNo, buff, //GuiConst_TEXT PrefixLocate *String, GuiLib_ALIGN_LEFT, //GuiConst_INT8U Alignment, GuiLib_PS_ON, //GuiConst_INT8U PsWriting, GuiLib_TRANSPARENT_OFF, //GuiConst_INT8U Transparent, GuiLib_UNDERLINE_OFF, //GuiConst_INT8U Underlining, 0, //GuiConst_INT16S BackBoxSizeX, 0, //GuiConst_INT16S BackBoxSizeY1, 0, //GuiConst_INT16S BackBoxSizeY2, GuiLib_BBP_NONE, //GuiConst_INT8U BackBorderPixels, SixteenBitColorValue(0, 0, 0xFF), //GuiConst_INTCOLOR ForeColor, SixteenBitColorValue(0, 0xFF, 0) //GuiConst_INTCOLOR BackColor ); sprintf(buff, "%s [%d]", stuffToPrint, ++counter); GuiLib_DrawStr( X, //GuiConst_INT16S X, Y, //GuiConst_INT16S Y, fontNo, //GuiConst_INT16U FontNo, buff, //GuiConst_TEXT PrefixLocate *String, GuiLib_ALIGN_LEFT, //GuiConst_INT8U Alignment, GuiLib_PS_ON, //GuiConst_INT8U PsWriting, GuiLib_TRANSPARENT_OFF, //GuiConst_INT8U Transparent, GuiLib_UNDERLINE_OFF, //GuiConst_INT8U Underlining, 0, //GuiConst_INT16S BackBoxSizeX, 0, //GuiConst_INT16S BackBoxSizeY1, 0, //GuiConst_INT16S BackBoxSizeY2, GuiLib_BBP_NONE, //GuiConst_INT8U BackBorderPixels, SixteenBitColorValue(0, 0, 0xFF), //GuiConst_INTCOLOR ForeColor, SixteenBitColorValue(0, 0xFF, 0) //GuiConst_INTCOLOR BackColor ); GuiLib_Refresh(); GuiDisplay_Unlock(); } /* Prints (i.e. displays) the specified text at the specified coordinates on the screen, blue text on a green background, using easyGUI. Args are: null-terminated string to print, x coord, y coord. No return code. */ void DebugPrint(char *stuffToPrint, GuiConst_INT16S X, GuiConst_INT16S Y) { #ifdef ALLOW_DEBUG_PRINTS char buff[200]; const GuiConst_INT16U fontNo = GuiFont_Helv1; GuiDisplay_Lock(); // (Attempt to) clear previous strings from display sprintf(buff, " "); GuiLib_DrawStr( X, //GuiConst_INT16S X, Y, //GuiConst_INT16S Y, fontNo, //GuiConst_INT16U FontNo, buff, //GuiConst_TEXT PrefixLocate *String, GuiLib_ALIGN_LEFT, //GuiConst_INT8U Alignment, GuiLib_PS_ON, //GuiConst_INT8U PsWriting, GuiLib_TRANSPARENT_OFF, //GuiConst_INT8U Transparent, GuiLib_UNDERLINE_OFF, //GuiConst_INT8U Underlining, 0, //GuiConst_INT16S BackBoxSizeX, 0, //GuiConst_INT16S BackBoxSizeY1, 0, //GuiConst_INT16S BackBoxSizeY2, GuiLib_BBP_NONE, //GuiConst_INT8U BackBorderPixels, SixteenBitColorValue(0, 0, 0xFF), //GuiConst_INTCOLOR ForeColor, SixteenBitColorValue(0, 0xFF, 0) //GuiConst_INTCOLOR BackColor ); GuiLib_DrawStr( X, //GuiConst_INT16S X, Y, //GuiConst_INT16S Y, fontNo, //GuiConst_INT16U FontNo, stuffToPrint, //GuiConst_TEXT PrefixLocate *String, GuiLib_ALIGN_LEFT, //GuiConst_INT8U Alignment, GuiLib_PS_ON, //GuiConst_INT8U PsWriting, GuiLib_TRANSPARENT_OFF, //GuiConst_INT8U Transparent, GuiLib_UNDERLINE_OFF, //GuiConst_INT8U Underlining, 0, //GuiConst_INT16S BackBoxSizeX, 0, //GuiConst_INT16S BackBoxSizeY1, 0, //GuiConst_INT16S BackBoxSizeY2, GuiLib_BBP_NONE, //GuiConst_INT8U BackBorderPixels, SixteenBitColorValue(0, 0, 0xFF), //GuiConst_INTCOLOR ForeColor, SixteenBitColorValue(0, 0xFF, 0) //GuiConst_INTCOLOR BackColor ); GuiLib_Refresh(); GuiDisplay_Unlock(); #endif //ALLOW_DEBUG_PRINTS } /* A 'wrapper' function for the above, so that the caller does not need access to the easyGUI declarations Intended to be used as an 'extern' in other modules. */ void EasyGUIDebugPrint(char *stuffToPrint, short X, short Y) { #ifdef ALLOW_DEBUG_PRINTS DebugPrint(stuffToPrint, (GuiConst_INT16S) X, (GuiConst_INT16S) Y); #endif // ALLOW_DEBUG_PRINTS } /* Version of the above that increments, and displays, a counter each time it is called - so the user can see if it is being called repeatedly, or if the application has simply hung */ void EasyGUIDebugPrintWithCounter(char *stuffToPrint, short X, short Y) { #ifdef ALLOW_DEBUG_PRINTS static int counter = 0; char buff[300]; sprintf(buff, "%s [%d]", stuffToPrint, ++counter); DebugPrint(buff, (GuiConst_INT16S) X, (GuiConst_INT16S) Y); #endif // ALLOW_DEBUG_PRINTS } extern "C" { void EasyGUIDebugPrintWithCounterCalledFromC(char *stuffToPrint, short X, short Y) { EasyGUIDebugPrintWithCounter(stuffToPrint, X, Y); } } /* Gets the GC status. Args are: a pointer to the USBHostGC instance that corresponds to the GC, a pointer to the USBDeviceConnected instance that (also) corresponds to the GC Returns the GC status as an integer - see the GC_STATE enumeration in GCStateAndFaultCodes.h for the possible values. */ int GetGCStatus(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) { while(usbHostGC->ExecutingSetDeviceReport()) {} char response[50]; int status; // Ensure we always have valid chars in the positions we are interested in, // in case we get "DNAK" or "EPKT" back response[6] = '0'; response[7] = '0'; usbHostGC->SetDeviceReport(usbDevice, "QSTA", response); // We expect a response like "DSTA00nn", where "nn" is the status. sscanf(&response[6], "%d", &status); return status; } /* Tells the caller whether or not the GC is ready to run Args are: a pointer to the USBHostGC instance that corresponds to the GC, a pointer to the USBDeviceConnected instance that (also) corresponds to the GC Returns true if the GC is ready to run, false if not. */ bool GCIsReadyToRun(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) { #ifdef USE_VERSION_102 // See GCStateAndFaultCodes.h return (GetGCStatus(usbDevice, usbHostGC) == GC_STATE_102_METHOD_READY_TO_RUN); #else return (GetGCStatus(usbDevice, usbHostGC) == GC_STATE_READY_TO_RUN); #endif } /* Tells the caller whether or not the GC is in the 'stabilising' state Args are: a pointer to the USBHostGC instance that corresponds to the GC, a pointer to the USBDeviceConnected instance that (also) corresponds to the GC Returns true if the GC is stabilising, false if not. */ bool GCIsStabilising(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) { #ifdef USE_VERSION_102 // See GCStateAndFaultCodes.h return (GetGCStatus(usbDevice, usbHostGC) == GC_STATE_102_METHOD_STABILISING); #else return false; // No "stabilising" state before version 1.02 #endif } /* Tells the caller whether or not the GC is in the 'equilibrating' state Args are: a pointer to the USBHostGC instance that corresponds to the GC, a pointer to the USBDeviceConnected instance that (also) corresponds to the GC Returns true if the GC is equilibrating, false if not. */ bool GCIsEquilibrating(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) { #ifdef USE_VERSION_102 // See GCStateAndFaultCodes.h return (GetGCStatus(usbDevice, usbHostGC) == GC_STATE_102_METHOD_EQUILIBRATING); #else return (GetGCStatus(usbDevice, usbHostGC) == GC_STATE_EQUILIBRATING); #endif } /* Tells the caller whether or not the GC is running Args are: a pointer to the USBHostGC instance that corresponds to the GC, a pointer to the USBDeviceConnected instance that (also) corresponds to the GC Returns true if the GC is running, false if not. */ bool GCIsRunning(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) { int gcStatus = GetGCStatus(usbDevice, usbHostGC); #ifdef USE_VERSION_102 // See GCStateAndFaultCodes.h return ((gcStatus >= GC_STATE_102_METHOD_RUNNING_MINIMUM) && (gcStatus <= GC_STATE_102_METHOD_RUNNING_MAXIMUM)); #else return ((gcStatus >= GC_STATE_RUNNING_MINIMUM) && (gcStatus <= GC_STATE_RUNNING_MAXIMUM)); #endif } /* Pass the current value of 'GuiVar_columnMaxTemp2' to the GC as the maximum column temperature. Intended to be called by the NumericKeypadPageHandler if the user presses the Apply button when we are editing the maximum column temperature. We pass a pointer to this function to the NumericKeypadPageHandler instance when we start editing. See the definition of 'ApplyFunctionPtr' in NumericKeypadPageHandler.h */ void SetColumnMaxTempFromEasyGuiVariable(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) { int maxTemp; sscanf(GuiVar_columnMaxTemp2, "%d", &maxTemp); char buff[40]; sprintf(buff, "SCMX%.4d", maxTemp); while(usbHostGC->ExecutingSetDeviceReport()) {} char response[50]; usbHostGC->SetDeviceReport(usbDevice, buff, response); } /* Draws the Run button in the correct place, in the correct enabled/disabled state. Arg is: true to display the button in an enabled state, false to display it disabled. No return code. */ void DrawRunButton(bool enabled) { #define USE_BITMAPS #ifdef USE_BITMAPS GuiConst_INT8U bitmapIndex = GuiStruct_Bitmap_RunButtonBlank; if(enabled) { bitmapIndex = GuiStruct_Bitmap_RunButtonGreen; } GuiConst_INTCOLOR transparentColor = SixteenBitColorValue(255, 255, 255); // In the 'corners' of the bitmap // Hard coded coordinates copied from easyGUI (I have not found // a way of getting them from the easyGUI code at runtime) GuiLib_ShowBitmap(bitmapIndex, 327, 170, transparentColor); #else // Draw a 'button' that consists of a box containing the word 'Run'. // Black if enabled, grey if disabled GuiConst_INTCOLOR buttonColor = (enabled) ? 0 : SixteenBitColorValue(0x80, 0x80, 0x80); GuiConst_TEXT *buttonText = "Run"; // These are hard-coded to match the corresponding definitions in easyGUI // (I have not found a way of getting these values from easyGUI at run time) const GuiConst_INT16S textX1 = 400; const GuiConst_INT16S textY1 = 220; const GuiConst_INT16U textFont = GuiFont_Helv1; const GuiConst_INT16S boxX1 = 338; const GuiConst_INT16S boxY1 = 195; const GuiConst_INT16S boxX2 = 462; const GuiConst_INT16S boxY2 = 235; GuiLib_Box(boxX1, boxY1, boxX2, boxY2, buttonColor); GuiLib_DrawStr( textX1, //GuiConst_INT16S X, textY1, //GuiConst_INT16S Y, textFont, //GuiConst_INT16U FontNo, buttonText, //GuiConst_TEXT PrefixLocate *String, GuiLib_ALIGN_CENTER, //GuiConst_INT8U Alignment, GuiLib_PS_ON, //GuiConst_INT8U PsWriting, GuiLib_TRANSPARENT_ON, //GuiConst_INT8U Transparent, GuiLib_UNDERLINE_OFF, //GuiConst_INT8U Underlining, 0, //GuiConst_INT16S BackBoxSizeX, 0, //GuiConst_INT16S BackBoxSizeY1, 0, //GuiConst_INT16S BackBoxSizeY2, GuiLib_BBP_NONE, //GuiConst_INT8U BackBorderPixels, buttonColor, //GuiConst_INTCOLOR ForeColor, SixteenBitColorValue(0xFF, 0xFF, 0xFF) //GuiConst_INTCOLOR BackColor (should be ignored with GuiLib_TRANSPARENT_ON) ); #endif // USE_BITMAPS } // Make these values available to the rest of the application GuiConst_INTCOLOR GetFakeBackgroundBitmapMainColour(void) { return SixteenBitColorValue(173, 222, 231); // From Background317 bitmap //return SixteenBitColorValue(0, 148, 165); // From Background320 bitmap } GuiConst_INTCOLOR GetFakeBackgroundBitmapButtonBandColour(void) { //return SixteenBitColorValue(173, 173, 173); // Grey band at bottom return SixteenBitColorValue(192, 192, 192); } /* Draws the background bitmap. It fills the screen - so you do not need to call GuiLib_Clear if you call this. (*** BUT - maybe this causes easyGUI to lose track of which page is displayed, and/or of its touch areas - so reinstated as a test with '#define WANT_GUILIB_CLEAR_BEFORE_DRAWING_BITMAP' ***) Alternatively - if the bitmap we are using has blocks of solid colour anyway - draw the areas directly (we could then save memory by not including the actual bitmap in the application) */ static void DrawSpecifiedBackgroundBitmap(GuiConst_INT8U bitmapIndex) { // Does this solve bug #4? //#define WANT_GUILIB_CLEAR_BEFORE_DRAWING_BITMAP #ifdef WANT_GUILIB_CLEAR_BEFORE_DRAWING_BITMAP GuiLib_Clear(); #undef WANT_GUILIB_CLEAR_BEFORE_DRAWING_BITMAP #endif //WANT_GUILIB_CLEAR_BEFORE_DRAWING_BITMAP // Answer - no, it doesn't //#define FAKE_BITMAP #ifdef FAKE_BITMAP GuiLib_FillBox(0, 0, 800, 423, GetFakeBackgroundBitmapMainColour()); GuiLib_FillBox(0, 424, 800, 480, GetFakeBackgroundBitmapButtonBandColour()); #undef FAKE_BITMAP #else GuiLib_ShowBitmap(bitmapIndex, 0, 0, -1); // -1 means 'no transparent colour' #endif // FAKE_BITMAP } /* Draws the default background bitmap, i.e. *without* the Ellutia logo */ void DrawBackgroundBitmap(void) { DrawSpecifiedBackgroundBitmap(GuiStruct_Bitmap_BlankBackground); } /* Same as the above, but draws the background bitmap *with* the Ellutia logo */ void DrawBackgroundBitmapWithLogo(void) { DrawSpecifiedBackgroundBitmap(GuiStruct_Bitmap_BootScreen); } /* For Display (LPC4088) Bug #11, draw a background bitmap without a grey bar at the bottom. For now, fake this with a page full of one colour */ void DrawFakeBackgroundBitmapForNumericKeypadPage(void) { // GuiLib_FillBox(0, 0, 800, 480, SixteenBitColorValue(0, 90, 99)); // Above is for the old background bitmap. For the new "BlankBackground", we need... GuiLib_FillBox(0, 0, 800, 480, SixteenBitColorValue(255, 255, 255)); } /* This functions erases the portions of the door Lock and Release buttons (on the Column pages) that remain visible when they are replaced by the Close or Unlock buttons. I have to admit that I still do not understand why simply redrawing the whole page (starting with the background bitmap) does not do this. No arguments, no return code. */ void DrawBackgroundBitmapOverDoorLockAndReleaseButtons(void) { // I also do not understand why the first coordinate pair in the call to GuiLib_ShowBitmapArea // needs to be 0, 0 to display the correct part of the bitmap. I expected them to be the same // as the second coordinate pair - but that caused the top left portion of the bitmap to be displayed, // not the part where the buttons actually are. GuiLib_ShowBitmapArea(GuiStruct_Bitmap_BlankBackground, 0, 0, 248, 405, 323, 480, -1); // -1 means 'no transparent colour' GuiLib_ShowBitmapArea(GuiStruct_Bitmap_BlankBackground, 0, 0, 477, 405, 552, 480, -1); // -1 means 'no transparent colour' // GuiLib_FillBox(248, 405, 323, 480, SixteenBitColorValue(165, 165, 173)); // GuiLib_FillBox(477, 405, 552, 480, SixteenBitColorValue(115, 123, 132)); } #define USING_BACKGROUND_BITMAP /* Copies the build date (#define'd as BUILD_DATE at the top of this file) to the easyGUI variable that displays it on the Settings page */ void SetupEasyGUIBuildDateVariable(void) { strcpy(GuiVar_buildDate, BUILD_DATE); } /* Draw a part of a profile - which will be a quadrilateral, vertical at left and right, horizontal at the bottom, but a sloping straight line at the top - by calling the EasyGUI GuiLib_VLine function multiple times. Args: colour of the profile to the left of the boundary colour of the profile to the right of the boundary X coords of the left and right edges of the section X coord of the colour boundary Y coord of the bottom Y coords of the top left and top right Returns true if OK, false if it failed (e.g. the coords were invalid). */ bool DrawProfileSectionUsingGuiLibVLine(GuiConst_INTCOLOR colour1, GuiConst_INTCOLOR colour2, GuiConst_INT16S xLeft, GuiConst_INT16S xRight, GuiConst_INT16S xColourBoundary, GuiConst_INT16S yBottom, GuiConst_INT16S yTopLeft, GuiConst_INT16S yTopRight) { if(xRight <= xLeft) return false; // Would cause divide by zero if they were equal // y = mx + c double yDiff = (double) (yTopRight - yTopLeft); double xDiff = (double) (xRight - xLeft); double m = yDiff / xDiff; double c = (double) yTopLeft - (m * (double) xLeft); GuiConst_INT16S lineX; double lineYTop; // Draw first colour up to boundary. // Allow for the boundary being past the right-hand end for (lineX = xLeft; (lineX <= xColourBoundary) && (lineX <= xRight); ++lineX) { lineYTop = (((double) lineX) * m) + c; GuiLib_VLine(lineX, yBottom, (GuiConst_INT16S) lineYTop, colour1); } // Draw second colour after boundary, if we have not already reached the right-hand end for (; lineX <= xRight; ++lineX) { lineYTop = (((double) lineX) * m) + c; GuiLib_VLine(lineX, yBottom, (GuiConst_INT16S) lineYTop, colour2); } return true; } /* Displays the specified easyGUI 'structure' (or page, to use a more easily understood term). Args are: the index of the structure to be displayed, a pointer to the USBHostGC instance that corresponds to the GC, a pointer to the USBDeviceConnected instance that (also) corresponds to the GC a boolean - defaulting to true - that gives the caller the option to stop the 'structure' updating its easyGUI variables (either from the GC or from its own internal values) when it is redisplayed. This is set false by the NumericKeypadHandler, when the user has just pressed the Apply button on the Numeric Keypad page and NumericKeypadHandler has updated the corresponding easyGUI variable on the page that called it, to stop the calling page/structure from promptly overwriting that same variable. No return code. TODO: "Refactor" this function. It has had code added to it piecemeal over a long period, and has become too large to easily understand. */ void DisplayEasyGuiStructure(int structureIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC, bool updateEasyGUIVariables = true) { // If required, query the GC to find out if it is ready to run, etc, *before* clearing the display - // otherwise the display remains clear while we talk to the GC - causes noticeable flickering GCStateSimplified simplifiedGCState = GC_IDLE; if((structureIndex == GuiStruct_HomePage_1) && (usbDevice != NULL) && (usbHostGC != NULL)) { int gcStatus = GetGCStatus(usbDevice, usbHostGC); simplifiedGCState = GCStateOrFaultCode::GetSimplifiedGCState(gcStatus); } #define DEBUG_HERE #ifdef DEBUG_HERE char dbg[100]; sprintf(dbg, "DisplayEasyGuiStructure - structure is %d", structureIndex); EasyGUIDebugPrint(dbg, 0, 20); #undef DEBUG_HERE #endif #ifdef USING_BACKGROUND_BITMAP // We want the status rectangles to be 'on top' of this - // if we include it in the easyGUI page itself, it gets drawn by GuiLib_ShowScreen, // and overwrites the rectangles if((structureIndex == GuiStruct_GCConnectionPage_Def) || (structureIndex == GuiStruct_EthernetConnectionPage_Def)) { DrawBackgroundBitmapWithLogo(); } else if ((structureIndex == GuiStruct_NumericKeypadPage_Def) || (structureIndex == GuiStruct_EthernetKeypadPage_Def)) { DrawFakeBackgroundBitmapForNumericKeypadPage(); } else { DrawBackgroundBitmap(); } #else GuiLib_Clear(); // Don't need this if we have drawn the background bitmap - it covers the entire screen #endif // Now, display the status rectangles and the component bitmaps, as appropriate for the page we are displaying. // Note - we draw the status rectangles after GuiLib_Clear - otherwise we wouldn't see the rectangles at all - // and before GuiLib_ShowScreen - so text, etc, is drawn on top of the rectangles. // We draw the component bitmaps, after - therefore, on top of - the status rectangles. switch(structureIndex) { case GuiStruct_HomePage_1: #ifdef USE_QSPI_BITMAPS qspiBitmaps.DisplayAllHomePageBitmaps(); #else homePageGCComponentStatusColorAreas.DisplayAll(); #endif // USE_QSPI_BITMAPS break; case GuiStruct_ColumnPage1_2: case GuiStruct_ColumnPage2_9: case GuiStruct_ColumnDHPage1_40: case GuiStruct_ColumnDHPage2_50: // Column status rectangle no longer used - and don't want icon on profile page (not enough space) //singleGCComponentPageStatusColorAreas.DisplayGCComponentStatus(COLUMN); qspiBitmaps.DisplayColumnComponentBitmap(); break; case GuiStruct_InjectorPage1_3: case GuiStruct_InjectorTempProfilePage_25: case GuiStruct_InjectorGasStatusPage_30: case GuiStruct_InjectorConsumablesPage_20: // Injector status rectangle no longer used //singleGCComponentPageStatusColorAreas.DisplayGCComponentStatus(INJECTOR); qspiBitmaps.DisplayInjectorComponentBitmap(); break; case GuiStruct_DetectorFIDPage_4: case GuiStruct_DetectorECDPage_12: case GuiStruct_DetectorFPDPage_14: case GuiStruct_DetectorTCDPage_11: case GuiStruct_DetectorNonePage_31: case GuiStruct_DetectorNPDPage_28: case GuiStruct_DetectorPIDPage_29: case GuiStruct_DetectorSPDIDPage_30: case GuiStruct_DetectorTXLPage_27: // Detector status rectangle no longer used //singleGCComponentPageStatusColorAreas.DisplayGCComponentStatus(DETECTOR); qspiBitmaps.DisplayDetectorComponentBitmap(); break; case GuiStruct_GasProfilePage_15: case GuiStruct_GasInformationPage_6: #ifdef WANT_STATUS_RECTANGLE_ON_GAS_CALIB_PAGES case GuiStruct_GasCalibrationPage_Def: case GuiStruct_GasBackPressureDACPage_Def: case GuiStruct_GasChannelDACAndADCPage_Def: #endif // Gas status rectangle no longer used //singleGCComponentPageStatusColorAreas.DisplayGCComponentStatus(GAS); qspiBitmaps.DisplayGasComponentBitmap(); break; default: // Don't need to display status rectangle for this page break; } if(structureIndex == GuiStruct_RunningPage1_7) { if(getGCStatusLoop != NULL) { getGCStatusLoop->UpdateMethodRunTimeEasyGUIVariables(false); } } if((structureIndex == GuiStruct_ColumnPage1_2) || (structureIndex == GuiStruct_ColumnDHPage1_40)){ if(getGCStatusLoop != NULL) { getGCStatusLoop->UpdateColumnStatusEasyGUIVariable(); } } if(structureIndex == GuiStruct_InjectorPage1_3){ if(getGCStatusLoop != NULL) { getGCStatusLoop->UpdateInjectorStatusEasyGUIVariable(); } } if(structureIndex == GuiStruct_EthernetParametersPage_50) { NetworkParameters *networkParameters = NetworkParameters::GetInstance(usbDevice, usbHostGC); if(networkParameters != NULL) { networkParameters->DisplayingEasyGUIPage(updateEasyGUIVariables); } } if(structureIndex == GuiStruct_GasCalibrationPage_Def) { GasCalibrationPageHandler *gasCalibrationPageHandler = GasCalibrationPageHandler::GetInstance(usbDevice, usbHostGC); if(gasCalibrationPageHandler != NULL) { gasCalibrationPageHandler->DisplayingEasyGUIPage(updateEasyGUIVariables); } } if(structureIndex == GuiStruct_GasBackPressureDACPage_Def) { GasBackPressureDACPageHandler *gasBackPressureDACPageHandler = GasBackPressureDACPageHandler::GetInstance(usbDevice, usbHostGC); if(gasBackPressureDACPageHandler != NULL) { gasBackPressureDACPageHandler->DisplayingEasyGUIPage(updateEasyGUIVariables); } } if(structureIndex == GuiStruct_GasChannelDACAndADCPage_Def) { GasChannelDACAndADCPageHandler *gasChannelDACAndADCPageHandler = GasChannelDACAndADCPageHandler::GetInstance(usbDevice, usbHostGC); if(gasChannelDACAndADCPageHandler != NULL) { gasChannelDACAndADCPageHandler->DisplayingEasyGUIPage(); } } if(structureIndex == GuiStruct_ColumnDHAutoCalibrationPage_Def) { ColumnDHAutoCalibrationPageHandler *columnDHAutoCalibrationPageHandler = ColumnDHAutoCalibrationPageHandler::GetInstance(usbDevice, usbHostGC); if(columnDHAutoCalibrationPageHandler != NULL) { columnDHAutoCalibrationPageHandler->DisplayingEasyGUIPage(); } } if(structureIndex == GuiStruct_ColumnDHManualCalibrationPage_Def) { ColumnDHManualCalibrationPageHandler *columnDHManualCalibrationPageHandler = ColumnDHManualCalibrationPageHandler::GetInstance(usbDevice, usbHostGC); if(columnDHManualCalibrationPageHandler != NULL) { columnDHManualCalibrationPageHandler->DisplayingEasyGUIPage(updateEasyGUIVariables); } } if(structureIndex == GuiStruct_ColumnDHSensorCalibration_Def) { ColumnDHSensorCalibrationPageHandler *columnDHSensorCalibrationPageHandler = ColumnDHSensorCalibrationPageHandler::GetInstance(usbDevice, usbHostGC); if(columnDHSensorCalibrationPageHandler != NULL) { columnDHSensorCalibrationPageHandler->DisplayingEasyGUIPage(updateEasyGUIVariables); } } if(structureIndex == GuiStruct_PSU_DAC_Page_Def) { ColumnDHPSUDACPageHandler *columnDHPSUDACPageHandler = ColumnDHPSUDACPageHandler::GetInstance(usbDevice, usbHostGC); if(columnDHPSUDACPageHandler != NULL) { columnDHPSUDACPageHandler->DisplayingEasyGUIPage(updateEasyGUIVariables); } } if(NudgeAndDampPageHandler::PageIsANudgeAndDampPage(structureIndex)) { NudgeAndDampPageHandler *nudgeAndDampPageHandler = NudgeAndDampPageHandler::GetInstance(usbDevice, usbHostGC); if(nudgeAndDampPageHandler != NULL) { nudgeAndDampPageHandler->DisplayingEasyGUIPage(structureIndex, updateEasyGUIVariables); } } if(structureIndex == GuiStruct_ServicingHomePage_Def) { #ifdef USE_QSPI_BITMAPS // Use same bitmaps on Servicing Home Page, as on the main Home Page qspiBitmaps.DisplayAllHomePageBitmaps(); #endif } GuiLib_ShowScreen(structureIndex, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW); // But draw the run button, if required, on top of the fixed part of the home page // Same for the Heat On/Off button, and the Stabilising or Equilibrating messages (again, if required) if(structureIndex == GuiStruct_HomePage_1) { //#define ALWAYS_WANT_RUN_BUTTON #ifdef ALWAYS_WANT_RUN_BUTTON DrawRunButton((simplifiedGCState == GC_READY_TO_RUN) || (simplifiedGCState == GC_RUNNING)); #else if((simplifiedGCState == GC_READY_TO_RUN) || (simplifiedGCState == GC_RUNNING)) { DrawRunButton(true); } #endif // ALWAYS_WANT_RUN_BUTTON #ifdef USE_HEAT_ONOFF_BUTTON_BITMAPS DrawHeatOnOffButton(); #endif GCStateOrFaultCode::DrawSimplifiedStateMessageOnHomePageRunButton(simplifiedGCState); } if(structureIndex == GuiStruct_InjectorTempProfilePage_25) { // Make sure the injector temperature profile graph is drawn // on entering this page (note that we must do this *after* // drawing the injector status rectangle and *after* 'GuiLib_ShowScreen') if(getGCStatusLoop != NULL) { getGCStatusLoop->DisplayInjectorTempProfilePageGraph(); } } if(structureIndex == GuiStruct_GasProfilePage_15) { // Make sure the gas pressure profile graph is drawn // on entering this page (note that we must do this *after* // drawing the gas status rectangle and *after* 'GuiLib_ShowScreen') if(getGCStatusLoop != NULL) { getGCStatusLoop->DisplayGasFlowProfilePageGraph(); } } if(structureIndex == GuiStruct_ColumnTempProfilePage_60) { // Make sure the column temperature profile graph is drawn // on entering this page (note that we must do this *after* // drawing the column status rectangle and *after* 'GuiLib_ShowScreen') if(getGCStatusLoop != NULL) { getGCStatusLoop->DisplayColumnTempProfilePageGraph(CONVENTIONAL_COLUMN); } //Also in GetGCStatusLoop::DisplayColumnTempProfilePageData //#define SWIM_TEST #ifdef SWIM_TEST SwimDraw* swimDrawInstance = SwimDraw::GetInstance(); if(swimDrawInstance != NULL) { // two horizontal boxes swimDrawInstance->DrawRectangle(SixteenBitColorValue(0xFF, 0, 0), 50, 200, 650, 250); swimDrawInstance->DrawRectangle(SixteenBitColorValue(0, 0, 0xFF), 50, 350, 650, 400); // two vertical boxes swimDrawInstance->DrawRectangle(SixteenBitColorValue(0xFF, 0, 0), 100, 50, 150, 350); swimDrawInstance->DrawRectangle(SixteenBitColorValue(0, 0, 0xFF), 500, 50, 550, 350); } #else // Draw the same boxes with easyGUI // GuiLib_FillBox(50, 200, 650, 250, SixteenBitColorValue(0xFF, 0, 0)); // GuiLib_FillBox(50, 350, 650, 400, SixteenBitColorValue(0, 0, 0xFF)); // GuiLib_FillBox(100, 50, 150, 350, SixteenBitColorValue(0xFF, 0, 0)); // GuiLib_FillBox(500, 50, 550, 350, SixteenBitColorValue(0, 0, 0xFF)); // No - draw a dummy profile section // DrawProfileSectionUsingGuiLibVLine(SixteenBitColorValue(0xFF, 0, 0), 300, 500, 350, 200, 125); #endif // SWIM_TEST } if(structureIndex == GuiStruct_ColumnDHTempProfilePage_61) { // Make sure the directly heated column temperature profile graph is drawn // on entering this page (note that we must do this *after* // drawing the column status rectangle and *after* 'GuiLib_ShowScreen') if(getGCStatusLoop != NULL) { getGCStatusLoop->DisplayColumnTempProfilePageGraph(DIRECTLY_HEATED_COLUMN); } } // Column page - must draw Lock/Release buttons if required if((structureIndex == GuiStruct_ColumnDHAutoCalibrationPage_Def) || (structureIndex == GuiStruct_ColumnDHPage1_40) || (structureIndex == GuiStruct_ColumnDHPage2_50) || (structureIndex == GuiStruct_ColumnDHTempProfilePage_61) || (structureIndex == GuiStruct_ColumnPage1_2) || (structureIndex == GuiStruct_ColumnPage2_9) || (structureIndex == GuiStruct_ColumnTempProfilePage_60)) { SetupDoorActuatorCommandUserInterface(usbDevice, usbHostGC, false); } if(structureIndex == GuiStruct_RunningPage1_7) { if(getGCStatusLoop != NULL) { getGCStatusLoop->UpdateAndDisplayRunningPage1ProgressBar(false); } } GuiLib_Refresh(); #define DEBUG_HERE #ifdef DEBUG_HERE char dbg2[100]; sprintf(dbg2, "After GuiLib_Refresh main 1"); EasyGUIDebugPrint(dbg2, 0, 40); #undef DEBUG_HERE #endif if(getGCStatusLoop != NULL) { getGCStatusLoop->SetCurrentPage(structureIndex); } } // End of "DisplayEasyGUIStructure" /* Draws the specified error message in a standard position (X = 90, Y = 240), blue text on a green background. The argument is a pointer to the (null-terminated) message text. */ void DrawErrorMessage(char *msg) { const GuiConst_INT16U fontNo = GuiFont_Helv1; GuiLib_DrawStr( 90, //GuiConst_INT16S X, 240, //GuiConst_INT16S Y, fontNo, //GuiConst_INT16U FontNo, msg, //GuiConst_TEXT PrefixLocate *String, GuiLib_ALIGN_LEFT, //GuiConst_INT8U Alignment, GuiLib_PS_ON, //GuiConst_INT8U PsWriting, GuiLib_TRANSPARENT_OFF, //GuiConst_INT8U Transparent, GuiLib_UNDERLINE_OFF, //GuiConst_INT8U Underlining, 0, //GuiConst_INT16S BackBoxSizeX, 0, //GuiConst_INT16S BackBoxSizeY1, 0, //GuiConst_INT16S BackBoxSizeY2, GuiLib_BBP_NONE, //GuiConst_INT8U BackBorderPixels, SixteenBitColorValue(0, 0, 0xFF), //GuiConst_INTCOLOR ForeColor, SixteenBitColorValue(0, 0xFF, 0) //GuiConst_INTCOLOR BackColor ); } /* Update the easyGUI variable that displays the command to turn the GC heat on or off, so that it matches the GC's current state. */ void UpdateHeatOnOffEasyGuiVariable(void) { // Note that the easyGUI variable is not the current status of the heat on the GC, // but the command to toggle its current state if(theGCHeatControl != NULL) { if(theGCHeatControl->IsHeatOn()) { strcpy(GuiVar_heatOnOffCommand, "Heat Off"); } else { strcpy(GuiVar_heatOnOffCommand, "Heat On"); } } } void DrawHeatOnOffButton(void) { // Note that the text on the button is not the current status of the heat on the GC, // but the command to toggle its current state GuiConst_INT8U bitmapIndex = GuiStruct_Bitmap_HeatOn; if(theGCHeatControl != NULL) { if(theGCHeatControl->IsHeatOn()) { bitmapIndex = GuiStruct_Bitmap_HeatOff; } } #define DEBUG_HERE #ifdef DEBUG_HERE char dbg[100]; sprintf(dbg, "DHOOB - bitmapIndex is %d", bitmapIndex); EasyGUIDebugPrint(dbg, 0, 440); #endif #undef DEBUG_HERE // Hard coded coordinates copied from easyGUI (I have not found // a way of getting them from the easyGUI code at runtime) GuiLib_ShowBitmap(bitmapIndex, 0, 404, -1); // No transparent colour } /* General function to pass a command to the GC, and return success or failure to the caller. Args are: the command (null-terminated string) a pointer to the USBHostGC instance that corresponds to the GC, a pointer to the USBDeviceConnected instance that (also) corresponds to the GC Returns true if the GC returned "DACK" in response, false if it returned "DNAK" (or "EPKT", or anything other than "DACK"). */ bool ExecuteGCCommand(char *cmd, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) { #define USE_GC_UTILS // Testing new class #ifdef USE_GC_UTILS return USBHostGCUtilities::SendCommandToGCWithDACKResponse(usbDevice, usbHostGC, cmd); #else while(usbHostGC->ExecutingSetDeviceReport()) {} char response[50]; usbHostGC->SetDeviceReport(usbDevice, cmd, response); // We expect a response like this: "DACK" for success, "DNAK" for failure #define DEBUG_HERE #ifdef DEBUG_HERE char dbg[100]; sprintf(dbg, "%s returned %s", cmd, response); EasyGUIDebugPrint(dbg, 0, 20); #endif #undef DEBUG_HERE return (response[1] == 'A'); #endif // USE_GC_UTILS } /* Starts the GC running, by passing it the "CRUN" command. Args are: a pointer to the USBHostGC instance that corresponds to the GC, a pointer to the USBDeviceConnected instance that (also) corresponds to the GC Returns true if the GC returned "DACK" in response, false if it returned "DNAK". */ bool StartGCRun(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) { return ExecuteGCCommand("CRUN", usbDevice, usbHostGC); } /* Stops the GC running, by passing it the "CSTP" or "CABT" command. Args are: a pointer to the USBHostGC instance that corresponds to the GC, a pointer to the USBDeviceConnected instance that (also) corresponds to the GC Returns true if the GC returned "DACK" in response, false if it returned "DNAK". */ bool StopGCRun(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) { // TODO: Find out which is the correct command here // char *cmd = "CSTP"; char *cmd = "CABT"; return ExecuteGCCommand(cmd, usbDevice, usbHostGC); } /* Takes the GC out of standby mode, by passing it the "CDIS" command. Args are: a pointer to the USBHostGC instance that corresponds to the GC, a pointer to the USBDeviceConnected instance that (also) corresponds to the GC Returns true if the GC returned "DACK" in response, false if it returned "DNAK". */ bool ExitGCStandbyMode(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) { if( ExecuteGCCommand("CDIS", usbDevice, usbHostGC)) { if(getGCStatusLoop != NULL) { getGCStatusLoop->ExitedGCStandbyMode(); } return true; } // 'else' return false; } /* Takes the GC out of its error state, by passing it the "CCLR" command. Args are: a pointer to the USBHostGC instance that corresponds to the GC, a pointer to the USBDeviceConnected instance that (also) corresponds to the GC Returns true if the GC returned "DACK" in response, false if it returned "DNAK". */ bool ClearGCErrors(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) { return ExecuteGCCommand("CCLR", usbDevice, usbHostGC); } /* Opens the (column) door on the GC, by passing it the "COPN" command. Args are: a pointer to the USBHostGC instance that corresponds to the GC, a pointer to the USBDeviceConnected instance that (also) corresponds to the GC Returns true if the GC returned "DACK" in response, false if it returned "DNAK". */ bool OpenDoor(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) { return ExecuteGCCommand("COPN", usbDevice, usbHostGC); } /* Set up the variable "GuiVar_doorActuatorCommandFGColour", to show - depending on the current state of the GC - whether the Lock/Unlock facility is enabled or not, by displaying the command in black (enabled) or grey (disabled). Args are: a pointer to the USBHostGC instance that corresponds to the GC, a pointer to the USBDeviceConnected instance that (also) corresponds to the GC No return code. */ void SetupDoorActuatorCommandColour(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC, bool actuatorsAreMoving) { // We allow the user to change the state of the actuators only if the GC is in the idle or fault states, // and if there is no other reason why they should be disabled (e.g. we are calibrating, and the oven is hot // even though the heat is not on. Show this to the user by setting the text colour to black (enabled) // or grey (disabled) bool enabled = false; if(!actuatorsAreMoving) { GCStateSimplified simplifiedGCState = GCStateOrFaultCode::GetSimplifiedGCState(GetGCStatus(usbDevice, usbHostGC)); if((simplifiedGCState == GC_IDLE) || (simplifiedGCState == GC_FAULTED)) { if(canUnlockDoorActuators) { enabled = true; } } } // All other situations - must be disabled if(enabled) { GuiVar_doorActuatorCommandFGColour = 0; // Black } else { GuiVar_doorActuatorCommandFGColour = SixteenBitColorValue(192, 192, 192); // Grey - but bright enough to show up against grey button colour } } typedef enum enumDoorActuatorStatus { LOCKED, UNLOCKED, CLOSED_BUT_NOT_LOCKED, INTERMEDIATE, MOVING } DoorActuatorStatus; static bool lockingActuators; // Do not display "Release" while we are doing this /* Finds out, and returns, the status of the door actuators. Args are: a pointer to the USBHostGC instance that corresponds to the GC, a pointer to the USBDeviceConnected instance that (also) corresponds to the GC Returns the status, as a value in the 'DoorActuatorStatus' enumeration defined above. */ DoorActuatorStatus GetDoorActuatorStatus(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) { while(usbHostGC->ExecutingSetDeviceReport()) {} char response[50]; usbHostGC->SetDeviceReport(usbDevice, "QACT1004", response); // We expect a response of the form "DACnnnnn", where "nnnnn" is the decimal value // of two bytes, the most significant of which contains the states of each of the three actuators, // while the least significant contains the software version (which we ignore). int actuatorStatus; sscanf(&response[3], "%d", &actuatorStatus); if(actuatorStatus & 0x8000) { // Top bit set return MOVING; } int actuator1Status = (actuatorStatus & 0x300) >> 8; int actuator2Status = (actuatorStatus & 0xC00) >> 10; int actuator3Status = (actuatorStatus & 0x3000) >> 12; // Status 1 means locked, 2 means unlocked, 3 means closed but not locked (actuators 1 and 2 only), // 0 means intermediate if ((actuator1Status == 1) && (actuator2Status == 1) && (actuator3Status == 1)) { return LOCKED; } // 'else' ... if((actuator1Status == 2) && (actuator2Status == 2) && (actuator3Status == 2)) { return UNLOCKED; } // 'else' ... if((actuator1Status == 3) && (actuator2Status == 3) && (actuator3Status == 2)) { return CLOSED_BUT_NOT_LOCKED; } // 'else' ... return INTERMEDIATE; } /* We have now replaced the "Open Door" button with a button to toggle the state of the door actuators - i.e. lock them if they are unlocked and vice versa. We display the corresponding command in the easyGUI variable "GuiVar_doorActuatorCommand". This function reads the current state of the actuators, and sets up that variable appropriately. It also sets up the variable "GuiVar_doorActuatorCommandFGColour", to show - depending on the current state of the GC - whether the facility is enabled or not, by displaying the command in black (enabled) or grey (disabled). It also now - since we have a separate state for "closed but not locked" - displays the two "Lock" and "Release" buttons if required. Args are: a pointer to the USBHostGC instance that corresponds to the GC, a pointer to the USBDeviceConnected instance that (also) corresponds to the GC a boolean set to true before displaying the easyGUI page (GuiLib_ShowScreen), false if afterwards No return code. */ void SetupDoorActuatorCommandUserInterface(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC, bool beforePageDisplay) { //#define DEBUG_PRINT_HERE #ifdef DEBUG_PRINT_HERE char buff[100]; sprintf(buff, "%s", beforePageDisplay ? "Before page display" : "After page display"); SpecialDebugPrint(buff, 150, 430); #endif DoorActuatorStatus doorActuatorStatus = GetDoorActuatorStatus(usbDevice, usbHostGC); // SetupDoorActuatorCommandColour(usbDevice, usbHostGC, (doorActuatorStatus == MOVING)); SetupDoorActuatorCommandColour(usbDevice, usbHostGC, false); if(beforePageDisplay) { // Do nothing - do not change the button text, etc - if the actuators are moving if(doorActuatorStatus != MOVING) { if(doorActuatorStatus == UNLOCKED) { strcpy(GuiVar_doorActuatorCommand, "Close"); } else if (doorActuatorStatus == LOCKED) { strcpy(GuiVar_doorActuatorCommand, "Unlock"); lockingActuators = false; } else { // i.e. all other states, including INTERMEDIATE (possible emergency stop) if(!lockingActuators) { // Do not display this while we are locking - confusing strcpy(GuiVar_doorActuatorCommand, "Release"); } } } } else { if(doorActuatorStatus == CLOSED_BUT_NOT_LOCKED) { GetGCStatusLoop::DisplayColumnLockAndReleaseButtons(); #ifdef DEBUG_PRINT_HERE char buff2[100]; sprintf(buff2, "%s", "Drawing lock and release buttons"); SpecialDebugPrint(buff2, 150, 460); #endif } else { // Including MOVING DrawBackgroundBitmapOverDoorLockAndReleaseButtons(); #ifdef DEBUG_PRINT_HERE char buff2[100]; sprintf(buff2, "%s", "*** NOT *** drawing lock and release buttons"); SpecialDebugPrint(buff2, 150, 460); #undef DEBUG_PRINT_HERE #endif } } } /* Tells the caller whether or not the door actuator buttons have changed, from the single "Close"/"Unlock" button to the two "Lock" and "Release" buttons, or vice versa */ bool DoorActuatorButtonsHaveChanged(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) { bool doorActuatorButtonsHaveChanged = false; static DoorActuatorStatus previousDoorActuatorStatus = INTERMEDIATE; DoorActuatorStatus doorActuatorStatus = GetDoorActuatorStatus(usbDevice, usbHostGC); // Do not change buttons if actuators are moving if(doorActuatorStatus != MOVING) { if((doorActuatorStatus == LOCKED) || (doorActuatorStatus == UNLOCKED)) { if((previousDoorActuatorStatus == CLOSED_BUT_NOT_LOCKED) || (previousDoorActuatorStatus == INTERMEDIATE)) { doorActuatorButtonsHaveChanged = true; } } else if (doorActuatorStatus == CLOSED_BUT_NOT_LOCKED) { if(previousDoorActuatorStatus != CLOSED_BUT_NOT_LOCKED) { doorActuatorButtonsHaveChanged = true; } } // We ignore the 'moving' state previousDoorActuatorStatus = doorActuatorStatus; } return doorActuatorButtonsHaveChanged; } /* Toggles the state of the door actuator on the GC - i.e. if it is locked, unlocks it, and vice versa. Args are: the index of the touch area the user touched (note that we assume this must be one of the 'door actuator areas' (seeEasyGUITouchAreaIndices.h) a pointer to the USBHostGC instance that corresponds to the GC, a pointer to the USBDeviceConnected instance that (also) corresponds to the GC Returns true if the GC executed the operation successfully, false if not */ bool DealWithDoorActuatorButtons(int touchAreaIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) { // Disallow movement unless GC is in idle or faulted states int gcStatus = GetGCStatus(usbDevice, usbHostGC); GCStateSimplified simplifiedGCState = GCStateOrFaultCode::GetSimplifiedGCState(gcStatus); if((simplifiedGCState != GC_IDLE) && (simplifiedGCState != GC_FAULTED)) { return false; } bool bOK = false; DoorActuatorStatus doorActuatorStatus = GetDoorActuatorStatus(usbDevice, usbHostGC); if(doorActuatorStatus != MOVING) { if(doorActuatorStatus == UNLOCKED) { // Two touch areas, but only one button - treat both the same // Close actuator, but do not lock it bOK = ExecuteGCCommand("CACT1005", usbDevice, usbHostGC); } else if (doorActuatorStatus == LOCKED) { // Two touch areas, but only one button - treat both the same // Unlock actuator bOK = ExecuteGCCommand("CACT1006", usbDevice, usbHostGC); } else if (doorActuatorStatus == CLOSED_BUT_NOT_LOCKED) { // Unlock or Lock, depending on the touch area if(touchAreaIndex == DOOR_ACTUATOR_AREA_1) { // Lock bOK = ExecuteGCCommand("CACT1007", usbDevice, usbHostGC); if(bOK) { lockingActuators = true; // Get the text ready for the Close/Unlock button strcpy(GuiVar_doorActuatorCommand, "Unlock"); } } else { // Assume DOOR_ACTUATOR_AREA_2 - release, i.e. unlock bOK = ExecuteGCCommand("CACT1006", usbDevice, usbHostGC); if(bOK) { // Get the text ready for the Close/Unlock button strcpy(GuiVar_doorActuatorCommand, "Close"); } } } else { // Assume INTERMEDIATE - for safety, always unlock bOK = ExecuteGCCommand("CACT1006", usbDevice, usbHostGC); } } if(bOK) { SetupDoorActuatorCommandUserInterface(usbDevice, usbHostGC, false); } return bOK; } /* Tells the caller whether or not a specified GC command represents the start of a method, and that therefore a new method is now being sent to the GC. Params: pointer to a null-terminated string containing the command in question Returns true if the command is one that occurs at the start of a method (and nowhere else), false if not. This code is intended to be as efficient as possible. Currently, called from GetGCStatusLoop class and EthernetThread class */ bool IsStartOfMethodCommand(char *gcCommand) { // We are looking for "CLCK" - lock local keyboard. // Ellution only sends this at the start of a method. if((gcCommand[0] == 'C') && (gcCommand[1] == 'L') && (gcCommand[2] == 'C') && (gcCommand[3] == 'K')) { return true; } return false; // Not "CLCK" } /* Tells the caller whether or not a specified GC command represents the end of a method, and that therefore a new method has just been sent to the GC. Params: pointer to a null-terminated string containing the command in question Returns true if the command is one that occurs at the end of a method (and nowhere else), false if not. This code is intended to be as efficient as possible. Currently, called from GetGCStatusLoop class and EthernetThread class */ bool IsEndOfMethodCommand(char *gcCommand) { // We are looking for "CULK" - unlock local keyboard. // Ellution only sends this at the end of a method. if((gcCommand[0] == 'C') && (gcCommand[1] == 'U') && (gcCommand[2] == 'L') && (gcCommand[3] == 'K')) { return true; } return false; // Not "CULK" } /* Tells the caller whether or not a specified GC command is a control command, which received "DACK" in acknowledgement, and is therefore likely to have caused the GC to change its state. Params: pointer to a null-terminated string containing the command in question Returns true if the command is a control command and if it succeeded, false if not. This code is intended to be as efficient as possible. Currently, called from GetGCStatusLoop class and EthernetThread class */ bool IsSuccessfulControlCommand(char* gcCommand, char* gcResponse) { // Simple criteria - does the command start with 'C'? // - was the response "DACK"? if(gcCommand[0] != 'C') { return false; } if(gcResponse[0] != 'D') { return false; } if(gcResponse[1] != 'A') { return false; } if(gcResponse[2] != 'C') { return false; } if(gcResponse[3] != 'K') { return false; } // All the above were true return true; } /* Increment the run count recorded in the QSPI settings. This would be called (for example) after the Run button has been pressed. */ void IncrementRunCountInQSPISettings(void) { int runCount = SettingsHandler::GetIntegerValueFromQSPISettings("RunCount", 0); // Debug prints char dbg[100]; sprintf(dbg, "Run count read: %d", runCount); EasyGUIDebugPrint(dbg, 500, 460); SettingsHandler::DisplayQSPIDirectory(600, 250); // End of debug prints ++runCount; SettingsHandler::PutIntegerValueToQSPISettings("RunCount", runCount); } /* Update the values of all the easyGUI variables whose values are derived from the run count. This should be called (for example) after the Run button has been pressed. */ void UpdateAllEasyGUIVariablesThatDependOnRunCount(void) { int runCount = SettingsHandler::GetIntegerValueFromQSPISettings("RunCount", 0); int columnInstalledRunCount = SettingsHandler::GetIntegerValueFromQSPISettings("RunCountWhenColumnInstalled", 0); sprintf(GuiVar_columnInjectionsSinceInstallation, "%d", (runCount - columnInstalledRunCount)); int linerChangedRunCount = SettingsHandler::GetIntegerValueFromQSPISettings("RunCountWhenLinerChanged", 0); sprintf(GuiVar_injectionCountSinceLinerChanged, "%d", (runCount - linerChangedRunCount)); int septaChangedRunCount = SettingsHandler::GetIntegerValueFromQSPISettings("RunCountWhenSeptaChanged", 0); sprintf(GuiVar_injectionCountSinceSeptaChanged, "%d", (runCount - septaChangedRunCount)); int oRingChangedRunCount = SettingsHandler::GetIntegerValueFromQSPISettings("RunCountWhenOringChanged", 0); sprintf(GuiVar_injectionCountSinceOringChanged, "%d", (runCount - oRingChangedRunCount)); } /* Sends the specified command to the GC. This must be a "Sxxx" command, that only expects "DACK" or "DNAK" back, not actual data. Args are: a pointer to the USBHostGC instance that corresponds to the GC, a pointer to the USBDeviceConnected instance that (also) corresponds to the GC Returns true if the GC returned "DACK" in response, false if it returned "DNAK". */ bool SendAnyCommandToGC(char *cmd, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) { #define USE_GC_UTILS // Testing new class #ifdef USE_GC_UTILS return USBHostGCUtilities::SendCommandToGCWithDACKResponse(usbDevice, usbHostGC, cmd); #else while(usbHostGC->ExecutingSetDeviceReport()) {} char response[50]; usbHostGC->SetDeviceReport(usbDevice, cmd, response); // We expect a response like this: "DACK" for success, "DNAK" for failure return (response[1] == 'A'); #endif // USE_GC_UTILS } /* This function performs the "set up" operations required when the GC starts running. It is intended to be called whenever the GC starts running - note that this may happen not only when the user presses our "Run" button, but if he sends a "CRUN" command to the GC from the PC (e.g. using Ellution), or if something triggers the "Run" pin on the GC board itself (and that does not pass through the LPC4088, so it cannot be detected directly by this software). Args are: a pointer to the USBHostGC instance that corresponds to the GC, a pointer to the USBDeviceConnected instance that (also) corresponds to the GC No return code */ void SetupForStartOfRun(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC) { if(getGCStatusLoop != NULL) { getGCStatusLoop->SetGCIsRunning(); IncrementRunCountInQSPISettings(); UpdateAllEasyGUIVariablesThatDependOnRunCount(); getGCStatusLoop->UpdateGCMethodRunningProfiles(); getGCStatusLoop->SetRunningPage1ProgressBarToZero(); getGCStatusLoop->SetupTemperatureWhileRunningEasyGUIVariables(); DisplayEasyGuiStructure(GuiStruct_RunningPage1_7, usbDevice, usbHostGC); } } static void RemoveUnitsFromEasyGUIStringIfFound(char* destination, GuiConst_TEXT* easyGUIString, char* units) { strcpy(destination, easyGUIString); int unitsLength = strlen(units); int stringLength = strlen(destination); if(strcmp(units, &destination[stringLength - unitsLength]) == 0) { destination[stringLength - unitsLength] = '\0'; } } /* This is the main function dealing with user interaction via the LPC4088 touch panel. ************************************************************************************ It is (currently, until we rationalise this code) called by the GetGCStatusLoop code that deals with touch events. (Ideally, we should move it into the GetGCStatusLoop class at some point.) Args are: the touch coordinates (x, y and z - we do not use the z coordinate) a pointer to the USBHostGC instance that corresponds to the GC, a pointer to the USBDeviceConnected instance that (also) corresponds to the GC a count of timer ticks a boolean set to true if the touch is 'new', false if not (a touch is 'new' if it occurs after a period when the user appears not to have touched the screen). Looks to see if the user has touched a defined easyGUI touch area, and responds accordingly. No return code. See EasyGUITouchAreaIndices.h for the touch area index values. */ void TouchCallback(touch_coordinate_t touchCoords, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC, int tickCount, bool newTouch) { EasyGUIDebugPrintWithCounter("TouchCallback 0 ", 500, 440); GuiConst_INT32S touchAreaIndex = GuiLib_TouchCheck((GuiConst_INT16S)touchCoords.x, (GuiConst_INT16S)touchCoords.y); EasyGUIDebugPrintWithCounter("TouchCallback 1 ", 500, 440); if(touchAreaIndex >= 0) { //#define ALLOW_DEBUG_PRINTS_HERE #ifdef ALLOW_DEBUG_PRINTS_HERE char buff[300]; sprintf(buff, "tAI: %d", touchAreaIndex); SpecialDebugPrint(buff, 150, 410); #undef ALLOW_DEBUG_PRINTS_HERE #endif // ALLOW_DEBUG_PRINTS_HERE bool dealtWithTouch = false; // page selector? TouchPanelPageSelector* touchPanelPageSelector = touchPanelPageSelectors.GetTouchPanelPageSelector(touchAreaIndex); if( touchPanelPageSelector != NULL) { // User has touched a page selector // ******************************** // Do not keep switching pages if the user keeps 'touching' - // switch only if he 'lets go', then presses again if(newTouch) { // Depending on the touch area, we may need to do extra stuff here // before displaying the appropriate page if(touchAreaIndex == GAS_SAVER_RETURN_TO_READY) { // Take GC out of standby mode ExitGCStandbyMode(usbDevice, usbHostGC); } if(touchAreaIndex == CLEAR_ERRORS_BUTTON) { // Take GC out of error state ClearGCErrors(usbDevice, usbHostGC); // Make sure the component status rectangles change colour immediately, // not after ~10 seconds if(getGCStatusLoop != NULL) { getGCStatusLoop->UpdateHomePageGCComponentStatusColorAreas(); getGCStatusLoop->UpdateSingleGCComponentPageStatusColorArea(COLUMN); getGCStatusLoop->UpdateSingleGCComponentPageStatusColorArea(INJECTOR); getGCStatusLoop->UpdateSingleGCComponentPageStatusColorArea(DETECTOR); getGCStatusLoop->UpdateSingleGCComponentPageStatusColorArea(GAS); } } if(touchAreaIndex == ABORT_RUN_YES) { // Abort Run request confirmed //#define DEBUG_PRINT_ON_FAILURE #ifdef DEBUG_PRINT_ON_FAILURE if(!StopGCRun(usbDevice, usbHostGC)) { SpecialDebugPrint("*** StopGCRun failed ***", 150, 430); } #undef DEBUG_PRINT_ON_FAILURE #else StopGCRun(usbDevice, usbHostGC); #endif // DEBUG_PRINT_ON_FAILURE getGCStatusLoop->ClearGCIsRunning(); #ifdef SERVICE_INTERVALS_ACTIVE ServiceInterval::TellAllServiceIntervalsInstrumentHasCycled(); #endif // SERVICE_INTERVALS_ACTIVE #ifdef TURN_HEAT_OFF_ON_ABORT // Turn heat off if(heatOnOffAvailable) { if(theGCHeatControl != NULL) { if(theGCHeatControl->IsHeatOn()) { theGCHeatControl->TurnHeatOff(); #ifdef USE_HEAT_ONOFF_BUTTON_BITMAPS DrawHeatOnOffButton(); #else UpdateHeatOnOffEasyGuiVariable(); #endif // USE_HEAT_ONOFF_BUTTON_BITMAPS } StartHeatOnOffTimeout(); } } #endif // TURN_HEAT_OFF_ON_ABORT } if(touchAreaIndex == SETTINGS_TO_NETWORK_PARAMS_BUTTON) { // We are about to display the Network Parameters page - so tell the class // to display the real IP address, etc - i.e. the values we are actually using NetworkParameters *networkParameters = NetworkParameters::GetInstance(usbDevice, usbHostGC); if(networkParameters != NULL) { networkParameters->RestoreRealValues(); } } if(touchAreaIndex == SETTINGS_TO_SERVICING_PAGE_BUTTON) { strcpy(GuiVar_engineersLockCodeMessage, "No lock code entered"); } if(touchAreaIndex == SERVICING_PAGE_GC_CMDS_BUTTON ) { // Clear the easyGUI variables that display the command and the response GuiVar_debugCommandTx[0] = '\0'; GuiVar_debugCommandRx[0] = '\0'; } if(touchPanelPageSelector->CanChangePage()) { DisplayEasyGuiStructure(touchPanelPageSelector->GetPageNumber(usbDevice, usbHostGC), usbDevice, usbHostGC); } } dealtWithTouch = true; // The user touched a page selector, so we have still 'dealt' with this, // whether we had a 'new touch' or not } // TouchPanelPageSelector if(!dealtWithTouch) { if(touchAreaIndex == RUN_BUTTON) { if(newTouch) { // As above - do not do this repeatedly - GC does not like it... EasyGUIDebugPrintWithCounter("TouchCallback 4.1 ", 500, 440); if(GCIsReadyToRun(usbDevice, usbHostGC)) { // Start run - if this works, display the 'Run' page, etc, else do nothing if(StartGCRun(usbDevice, usbHostGC)) { SetupForStartOfRun(usbDevice, usbHostGC); } else { DrawErrorMessage("*** Run failed to start ***"); // Give user time to read error, then erase it Thread::wait(2000); DisplayEasyGuiStructure(GuiStruct_HomePage_1, usbDevice, usbHostGC); } } else { // GC is not ready to run - tell the user why getGCStatusLoop->SetupGCNotReadyStateEasyGUIVariable(); DisplayEasyGuiStructure(GuiStruct_GCNotReadyToRunPage_Def, usbDevice, usbHostGC); } } dealtWithTouch = true; } } // Run button if(!dealtWithTouch) { if(touchAreaIndex == HEAT_ON_BUTTON) { // Heat on/off button if(newTouch) { // As above - do not do this repeatedly - GC does not like it... if(heatOnOffAvailable) { if(theGCHeatControl != NULL) { if(theGCHeatControl->IsHeatOn()) { theGCHeatControl->TurnHeatOff(); } else { theGCHeatControl->TurnHeatOn(); } #ifdef USE_HEAT_ONOFF_BUTTON_BITMAPS // If we do not do this first, we do not see the bitmap change on the button // (I don't understand why...) DisplayEasyGuiStructure(GuiStruct_HomePage_1, usbDevice, usbHostGC); DrawHeatOnOffButton(); #else UpdateHeatOnOffEasyGuiVariable(); // Make GuiVar_heatOnOffCommand update visible on screen DisplayEasyGuiStructure(GuiStruct_HomePage_1, usbDevice, usbHostGC); #endif // USE_HEAT_ONOFF_BUTTON_BITMAPS StartHeatOnOffTimeout(); // Update the door lock/unlock command colour // *before* we display the column page(s) SetupDoorActuatorCommandColour(usbDevice, usbHostGC, false); } } } // Whether we changed the heat or not, the user still 'touched' our area, // so no-one else should try and deal with this touch dealtWithTouch = true; } } // Heat ON/Off button if(!dealtWithTouch) { if((touchAreaIndex == COLUMN_PAGE1_EDIT_COLUMN_MAX_TEMP) || (touchAreaIndex == COLUMN_DH_PAGE1_EDIT_COLUMN_MAX_TEMP)) { NumericKeypadPageHandler* numericKeypadPageHandler = NumericKeypadPageHandler::GetInstance(usbDevice, usbHostGC); if(numericKeypadPageHandler != NULL) { // Remove units ("deg C")from the initial value to be displayed in the keypad char temp[50]; RemoveUnitsFromEasyGUIStringIfFound(temp, GuiVar_columnMaxTemp2, " deg C"); // Remove space before units as well as the units themselves numericKeypadPageHandler->StartEditing(temp); numericKeypadPageHandler->SetEasyGUIVariableToEdit(GuiVar_columnMaxTemp2); numericKeypadPageHandler->SetEasyGUICallingPage((touchAreaIndex == COLUMN_DH_PAGE1_EDIT_COLUMN_MAX_TEMP) ? GuiStruct_ColumnDHPage1_40 : GuiStruct_ColumnPage1_2); numericKeypadPageHandler->SetEditVariableRange(0, 500); numericKeypadPageHandler->SetEditVariableName("Col. Max Temp"); numericKeypadPageHandler->SetEditVariableUnits("deg C"); numericKeypadPageHandler->SetApplyFunctionPtr(&SetColumnMaxTempFromEasyGuiVariable); numericKeypadPageHandler->DisplayEasyGUIPage(); } dealtWithTouch = true; } } if(!dealtWithTouch) { if(touchAreaIndex == ENTER_ENGINEERS_LOCK_CODE) { NumericKeypadPageHandler* numericKeypadPageHandler = NumericKeypadPageHandler::GetInstance(usbDevice, usbHostGC); if(numericKeypadPageHandler != NULL) { numericKeypadPageHandler->StartEditingInLockMode(842, GuiStruct_ServicingHomePage_Def, GuiVar_engineersLockCodeMessage, "Invalid lock code"); numericKeypadPageHandler->SetEasyGUICallingPage(GuiStruct_EngineersLockPage_Def); numericKeypadPageHandler->SetEditVariableRange(0, 32767); numericKeypadPageHandler->SetEditVariableName("Lock Code"); numericKeypadPageHandler->DisplayEasyGUIPage(); } dealtWithTouch = true; } } if(!dealtWithTouch) { if((touchAreaIndex == DOOR_ACTUATOR_AREA_1) || (touchAreaIndex == DOOR_ACTUATOR_AREA_2)) { DealWithDoorActuatorButtons(touchAreaIndex, usbDevice, usbHostGC); dealtWithTouch = true; } } // Open door if(!dealtWithTouch) { NetworkParameters *networkParameters = NetworkParameters::GetInstance(usbDevice, usbHostGC); if(networkParameters != NULL) { if(networkParameters->TouchAreaIsNetworkParameter(touchAreaIndex)) { networkParameters->DealWithTouch(touchAreaIndex); dealtWithTouch = true; } } } // Network parameter #define USE_GAS_CALIBRATION_PAGE_HANDLER #ifdef USE_GAS_CALIBRATION_PAGE_HANDLER if(!dealtWithTouch) { GasCalibrationPageHandler *gasCalibrationPageHandler = GasCalibrationPageHandler::GetInstance(usbDevice, usbHostGC); if(gasCalibrationPageHandler != NULL) { if(gasCalibrationPageHandler->TouchAreaIsOnCalibrationPage(touchAreaIndex)) { dealtWithTouch = gasCalibrationPageHandler->DealWithTouch(touchAreaIndex); } } } // Touch area on Gas Calibration page #undef USE_GAS_CALIBRATION_PAGE_HANDLER #endif // USE_GAS_CALIBRATION_PAGE_HANDLER if(!dealtWithTouch) { ColumnDHAutoCalibrationPageHandler *columnDHAutoCalibrationPageHandler = ColumnDHAutoCalibrationPageHandler::GetInstance(usbDevice, usbHostGC); if(columnDHAutoCalibrationPageHandler != NULL) { if(columnDHAutoCalibrationPageHandler->TouchAreaIsOnCalibrationPage(touchAreaIndex)) { dealtWithTouch = columnDHAutoCalibrationPageHandler->DealWithTouch(touchAreaIndex); } } } // Touch area on DH Column Auto Calibration page if(!dealtWithTouch) { ColumnDHManualCalibrationPageHandler *columnDHManualCalibrationPageHandler = ColumnDHManualCalibrationPageHandler::GetInstance(usbDevice, usbHostGC); if(columnDHManualCalibrationPageHandler != NULL) { if(columnDHManualCalibrationPageHandler->TouchAreaIsOnCalibrationPage(touchAreaIndex)) { dealtWithTouch = columnDHManualCalibrationPageHandler->DealWithTouch(touchAreaIndex); } } } // Touch area on DH Column Manual Calibration page if(!dealtWithTouch) { ColumnDHSensorCalibrationPageHandler *columnDHSensorCalibrationPageHandler = ColumnDHSensorCalibrationPageHandler::GetInstance(usbDevice, usbHostGC); if(columnDHSensorCalibrationPageHandler != NULL) { if(columnDHSensorCalibrationPageHandler->TouchAreaIsOnCalibrationPage(touchAreaIndex)) { dealtWithTouch = columnDHSensorCalibrationPageHandler->DealWithTouch(touchAreaIndex); } } } // Touch area on DH Column Sensor Calibration page if(!dealtWithTouch) { ColumnDHPSUDACPageHandler *columnDHPSUDACPageHandler = ColumnDHPSUDACPageHandler::GetInstance(usbDevice, usbHostGC); if(columnDHPSUDACPageHandler != NULL) { if(columnDHPSUDACPageHandler->TouchAreaIsOnPSUDACPage(touchAreaIndex)) { dealtWithTouch = columnDHPSUDACPageHandler->DealWithTouch(touchAreaIndex); } } } // Touch area on DH PSU DAC page if(!dealtWithTouch) { NudgeAndDampPageHandler *nudgeAndDampPageHandler = NudgeAndDampPageHandler::GetInstance(usbDevice, usbHostGC); if(nudgeAndDampPageHandler != NULL) { if(nudgeAndDampPageHandler->TouchAreaIsOnNudgeAndDampPage(touchAreaIndex)) { dealtWithTouch = nudgeAndDampPageHandler->DealWithTouch(touchAreaIndex); } } } // Touch area on one of the Nudge and Damp pages if(!dealtWithTouch) { DebugCommandsPageHandler *debugCommandsPageHandler = DebugCommandsPageHandler::GetInstance(usbDevice, usbHostGC); if(debugCommandsPageHandler != NULL) { if(debugCommandsPageHandler->TouchAreaIsOnDebugCommandsPage(touchAreaIndex)) { dealtWithTouch = debugCommandsPageHandler->DealWithTouch(touchAreaIndex); } } } // Touch area on the GC (Debug) Commands page if(!dealtWithTouch) { // Try Column Oven Fan page - no 'PageHandler' class for this - // trivial - only two touch areas/buttons, each of which simply sends // a command (without arguments) to the GC - no values displayed if(touchAreaIndex == COLUMN_OVEN_FAN_NORMAL) { ExecuteGCCommand("CFNO",usbDevice, usbHostGC); dealtWithTouch = true; } else if(touchAreaIndex == COLUMN_OVEN_FAN_COOLING) { ExecuteGCCommand("CFCO",usbDevice, usbHostGC); dealtWithTouch = true; } } if(!dealtWithTouch) { GasBackPressureDACPageHandler *gasBackPressureDACPageHandler = GasBackPressureDACPageHandler::GetInstance(usbDevice, usbHostGC); if(gasBackPressureDACPageHandler != NULL) { if(gasBackPressureDACPageHandler->TouchAreaIsOnGasBackPressureDACPage(touchAreaIndex)) { dealtWithTouch = gasBackPressureDACPageHandler->DealWithTouch(touchAreaIndex); } } } // Touch area on Gas Backpressure DAC page if(!dealtWithTouch) { GasChannelDACAndADCPageHandler *gasChannelDACAndADCPageHandler = GasChannelDACAndADCPageHandler::GetInstance(usbDevice, usbHostGC); if(gasChannelDACAndADCPageHandler != NULL) { if(gasChannelDACAndADCPageHandler->TouchAreaIsOnGasChannelDACAndADCPage(touchAreaIndex)) { dealtWithTouch = gasChannelDACAndADCPageHandler->DealWithTouch(touchAreaIndex); } } } // Touch area on Gas channel DAC and ADC page if(!dealtWithTouch) { NumericKeypadPageHandler *numericKeypadPageHandler = NumericKeypadPageHandler::GetInstance(usbDevice, usbHostGC); if(numericKeypadPageHandler != NULL) { if(numericKeypadPageHandler->TouchAreaIsOnNumericKeypadPage(touchAreaIndex)) { dealtWithTouch = numericKeypadPageHandler->DealWithTouch(touchAreaIndex); } } } // Touch area on numeric keypad page if(!dealtWithTouch) { EthernetKeypadPageHandler *ethernetKeypadPageHandler = EthernetKeypadPageHandler::GetInstance(usbDevice, usbHostGC); if(ethernetKeypadPageHandler != NULL) { if(ethernetKeypadPageHandler->TouchAreaIsOnEthernetKeypadPage(touchAreaIndex)) { dealtWithTouch = ethernetKeypadPageHandler->DealWithTouch(touchAreaIndex); } } } // Touch area on numeric keypad page if(!dealtWithTouch) { DetectorIgnitionHandler *detectorIgnitionHandler = DetectorIgnitionHandler::GetInstance(usbDevice, usbHostGC); if(detectorIgnitionHandler != NULL) { if(detectorIgnitionHandler->TouchAreaIsDetectorIgniteButton(touchAreaIndex)) { dealtWithTouch = detectorIgnitionHandler->DealWithTouch(touchAreaIndex); } } } // Handling detector ignition if(!dealtWithTouch) { #ifdef SERVICE_INTERVALS_ACTIVE if(ServiceInterval::IsServicedTouchArea(touchAreaIndex)) { ServiceInterval::DealWithServicedTouchArea(touchAreaIndex); // This is a ServiceInterval touch area - // nothing else can deal with it, // whether we actually did anything or not dealtWithTouch = true; } #endif // SERVICE_INTERVALS_ACTIVE } // Service interval } } /* Waits (indefinitely) for a USB device to be connected - note that, if there is no USB device, we will hang at this point - there is no timeout. Args are: a pointer to the USBHost instance on which we are to look for the USB device Returns a pointer to the USB device */ USBDeviceConnected* GetUSBDevice(USBHost* usbHost) { USBDeviceConnected* usbDevice = NULL; while(usbDevice == NULL) { for (uint8_t i = 0; i < MAX_DEVICE_CONNECTED; ++i) { usbDevice = usbHost->getDevice(i); if (usbDevice) { return usbDevice; } } } return NULL; } /* Function that executes the easyGUI code in VarInit.c, to initialise all the easyGUI variables to the values we have specified in the easyGUI application. */ static void InitialiseAllEasyGUIVariables(void) { #include "VarInit.h" // Renamed from the easyGUI file "VarInit.c", so as not to confuse the mbed compiler } /* Sets up the easyGUI user interface in an acceptable initial state. No return code. */ void InitEasyGUIDisplay(int initialEasyGUIPage) { // easyGUI stuff - note function calls require 'extern "C"' in relevant header // Need to set up heat on/off command easyGUI variable // before we display the home page for the first time - // but we do not have a heat control object at this point - // default to 'Heat On' strcpy(GuiVar_heatOnOffCommand, "Heat On"); #ifdef USE_HEAT_ONOFF_BUTTON_BITMAPS DrawHeatOnOffButton(); #endif GuiDisplay_Lock(); GuiLib_Init(); DisplayEasyGuiStructure(initialEasyGUIPage, NULL, NULL); GuiDisplay_Unlock(); #ifdef USE_QSPI_BITMAPS qspiBitmaps.SetupArray(); #endif } void InitialiseLPC4088(DMBoard* board, void **frameBufferAddress1, void **frameBufferAddress2) { RtosLog* log = board->logger(); Display* disp = board->display(); DMBoard::BoardError err; do { err = board->init(); if (err != DMBoard::Ok) { log->printf("Failed to initialize the board, got error %d\r\n", err); break; } #ifdef MEMORY_TEST_OCT_2016 for (int ind = 0; ind < clogLength; ++ind) { clogUpMemory[ind] = 'A'; } #endif // MEMORY_TEST_OCT_2016 #define COLOR_FLICKERING_FIX_1 #ifdef COLOR_FLICKERING_FIX_1 // Possible fix for display flickering on LPC4088 uint32_t* reg = ((uint32_t*)0x400fc188); *reg |= (3<<10); #undef COLOR_FLICKERING_FIX_1 #endif log->printf("\n\nHello World!\n\n"); void* fb = disp->allocateFramebuffer(); if (fb == NULL) { log->printf("Failed to allocate memory for a frame buffer\r\n"); err = DMBoard::MemoryError; break; } *frameBufferAddress1 = fb; // Allocate a second frame buffer - what will its address be? void* fb2 = disp->allocateFramebuffer(); *frameBufferAddress2 = fb2; Display::DisplayError disperr; // disperr = disp->powerUp(fb, Display::Resolution_24bit_rgb888); // Start display in default mode (16-bit) (24-bit uses too much memory) #define COLOR_FLICKERING_FIX_2 #ifdef COLOR_FLICKERING_FIX_2 // Second possible fix for colour flickering problem, // suggested by Embedded Artists - specify low frame rate disp->powerDown(); disperr = disp->powerUp(fb, Display::Resolution_16bit_rgb565, FrameRate_Low); #undef COLOR_FLICKERING_FIX_2 #else disperr = disp->powerUp(fb); #endif if (disperr != Display::DisplayError_Ok) { log->printf("Failed to initialize the display, got error %d\r\n", disperr); break; } #define TEST_BACKLIGHT #ifdef TEST_BACKLIGHT disp->backlight(100); #endif // TEST_BACKLIGHT } while(false); if (err != DMBoard::Ok) { log->printf("\nTERMINATING\n"); wait_ms(2000); // allow RtosLog to flush messages mbed_die(); } } int main() { DMBoard* board = &DMBoard::instance(); void *frameBuffer1; void *frameBuffer2; InitialiseLPC4088(board, &frameBuffer1, &frameBuffer2); GuiDisplay_SetFrameAddress(frameBuffer1); SwimDraw* swimDrawInstance = SwimDraw::GetInstance(); if(swimDrawInstance != NULL) { swimDrawInstance->Initialise(board, frameBuffer1); } InitialiseAllEasyGUIVariables(); // The first thing we do is try to connect to the GC - // so tell the user this first of all... InitEasyGUIDisplay(GuiStruct_GCConnectionPage_Def); SetupEasyGUIBuildDateVariable(); char dbgBuff[100]; sprintf(dbgBuff, "FrameBuffers are: %X, %X", frameBuffer1, frameBuffer2); EasyGUIDebugPrint(dbgBuff, 100, 100); // Now the USB 'stuff' USBHost* usbHost = USBHost::getHostInst(); USBHostGC usbHostGC; DrawErrorMessage("Waiting for USB device..."); // Note that 'GetUSBDevice' will not return until it finds a USB device - // it does *not* timeout USBDeviceConnected* usbDevice = GetUSBDevice(usbHost); DrawErrorMessage(" "); if(usbDevice != NULL) { USB_TYPE enumerateRetVal = usbHost->enumerate(usbDevice, &usbHostGC); if (usbHostGC.DeviceIsGC()) { if (usbHostGC.AttachGCDevice(usbDevice)) { DrawErrorMessage("Found GC device "); #ifdef OLD_TOUCH_LISTENER SetupUSBGCTouchListener(board, usbDevice, &usbHostGC); DrawErrorMessage("After call to SetupUSBGCTouchListener "); #endif getGCStatusLoop = GetGCStatusLoop::GetInstance(usbDevice, &usbHostGC); DrawErrorMessage("After creation of GetGCStatusLoop instance "); theGCHeatControl = new GCHeatControl(usbDevice, &usbHostGC); #ifdef USE_HEAT_ONOFF_BUTTON_BITMAPS DrawHeatOnOffButton(); #else UpdateHeatOnOffEasyGuiVariable(); #endif // USE_HEAT_ONOFF_BUTTON_BITMAPS DrawErrorMessage("After UpdateHeatOnOffEasyGuiVariable "); getGCStatusLoop->SetHomePageGCComponentStatusColorAreas(&homePageGCComponentStatusColorAreas); getGCStatusLoop->SetSingleGCComponentPageStatusColorAreas(&singleGCComponentPageStatusColorAreas); DrawErrorMessage("Point 1 "); #ifdef SERVICE_INTERVALS_ACTIVE ServiceInterval::SetupAllServiceIntervals(); // Note these two functions *** have different names *** ServiceInterval::StartAllServiceIntervals(); #endif // SERVICE_INTERVALS_ACTIVE DrawErrorMessage("Point 2 "); getGCStatusLoop->SetupAllEasyGUIVariables(); SetupDoorActuatorCommandUserInterface(usbDevice, &usbHostGC, true); DrawErrorMessage("Point 3 "); #ifdef USE_QSPI_BITMAPS getGCStatusLoop->SetQSPIBitmaps(&qspiBitmaps); // Test - what does it do if it cannot find the bitmaps?? #endif DrawErrorMessage("Point 4 "); getGCStatusLoop->SetCurrentPage(GuiStruct_HomePage_1); DrawErrorMessage("Point 5 "); //DisplayEasyGuiStructure(GuiStruct_HomePage_1, usbDevice, &usbHostGC); DebugPrint("Before MainLoopWithEthernet", 100, 450); // Currently, this never returns - but it allows time for the TouchCallback function to be invoked //getGCStatusLoop->MainLoop(board); getGCStatusLoop->MainLoopWithEthernet(board); // Should never reach this code - but just in case... delete theGCHeatControl; } else { DrawErrorMessage("Failed to attach GC device to host "); } } else { DrawErrorMessage(" *** USB device found, is *not* a GC *** "); } } else { DrawErrorMessage(" *** No USB device found *** "); } while(true) {} }