Repository for import to local machine

Dependencies:   DMBasicGUI DMSupport

Revision:
1:a5258871b33d
Parent:
0:47c880c1463d
Child:
2:6e94a7fd1e37
--- a/main.cpp	Wed Jan 13 13:17:05 2016 +0000
+++ b/main.cpp	Thu Jul 20 08:42:29 2017 +0000
@@ -5,16 +5,127 @@
 
 #include <string.h>
 
+#include <stdio.h>
+#include <stdlib.h>
+
 #include "GuiLib.h"
 #include "GuiDisplay.h"
 
 #include "USBHostGC.h"
-#include "TouchListener.h"
 #include "TouchPanelPageSelector.h"
 #include "GCHeatControl.h"
 #include "GetGCStatusLoop.h"
 #include "GCComponentStatusColorArea.h"
-#include "EthernetTimerHandler.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 "20 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 **
@@ -35,20 +146,44 @@
 }
 // ** 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;
 
-TouchListener* mainTouchListener = NULL;
+
 GetGCStatusLoop* getGCStatusLoop = NULL;
 
 HomePageGCComponentStatusColorAreas homePageGCComponentStatusColorAreas;
 SingleGCComponentPageStatusColorAreas singleGCComponentPageStatusColorAreas;
 
-EthernetTimerHandler* theEthernetTimerHandler = NULL;
+#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
@@ -56,13 +191,80 @@
     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;
@@ -108,32 +310,59 @@
     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)
 {
-#define DEBUG_HERE
-#ifdef DEBUG_HERE
+#ifdef ALLOW_DEBUG_PRINTS
     DebugPrint(stuffToPrint, (GuiConst_INT16S) X, (GuiConst_INT16S) Y);
-#undef DEBUG_HERE
-#endif
+#endif // ALLOW_DEBUG_PRINTS
 }
 
-void DummyEasyGUIDebugPrint(char *stuffToPrint, short X, short Y)
+/*
+    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)
 {
-    //DebugPrint(stuffToPrint, (GuiConst_INT16S) X, (GuiConst_INT16S) 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
 }
 
-int DummyDebugFunction(int i)
-{
-    return i^2;
+extern "C" {
+    void EasyGUIDebugPrintWithCounterCalledFromC(char *stuffToPrint, short X, short Y)
+    {
+        EasyGUIDebugPrintWithCounter(stuffToPrint, X, Y);
+    }
 }
 
-bool GCIsReadyToRun(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
+/*
+    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
@@ -141,17 +370,130 @@
     response[7] = '0';
     
     usbHostGC->SetDeviceReport(usbDevice, "QSTA", response);
-    // We expect a response like "QSTA00nn", where "nn" is the status.
-    // "33" means ready to run, anything else means "not ready"
+    // 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
     
-    return ((response[6] == '3') && (response[7]== '3'));
+    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_RunButton2;
+    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";
 
@@ -184,65 +526,609 @@
         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
+
+
+/*
+    Depending on the argument passed to it, this function either draws the specified text 
+    on top of the Heat On button - i.e. at the bottom of the display, in the centre, 
+    in the specified colour, or erases it.
+    
+    Args: boolean, true to display the text, false to erase it
+          pointer to the (null-terminated) text
+          the foregound (i.e. text) colour
+          the background colour
+    
+    No return code.
+*/
+static void DrawTextOnHeatOnOffButton(bool drawIt, char* text, GuiConst_INTCOLOR foreColor, GuiConst_INTCOLOR backColor)
+{
+    if(drawIt) {
+        const GuiConst_INT16U fontNo = GuiFont_Helv20Bold;
+        
+        const GuiConst_INT16S X = 75; // Approx centre of button
+        const GuiConst_INT16S Y = 450; // 220 to put it on top of the Run button, 450 for the Heat On/Off button
+        
+        GuiLib_DrawStr(
+            X,                      //GuiConst_INT16S X,
+            Y,                      //GuiConst_INT16S Y,
+            fontNo,                 //GuiConst_INT16U FontNo,
+            text,                   //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,
+            foreColor,              //GuiConst_INTCOLOR ForeColor,
+            backColor               //GuiConst_INTCOLOR BackColor
+        ); 
+    } else {
+        // TODO: Erase it somehow
+    }
 }
 
 
-void DisplayEasyGuiStructure(int structureIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
+/*
+    Depending on the argument passed to it,this function either draws the word "Stabilising" 
+    at the bottom of the display, in the centre, in green, or erases it.
+    
+    Arg is: true to display the text, false to erase it
+    
+    No return code.
+*/
+void DrawStabilisingMessage(bool stabilising)
+{
+    DrawTextOnHeatOnOffButton(stabilising, "Stabilising", SixteenBitColorValue(0, 0xFF, 0), SixteenBitColorValue(0, 0, 0));
+}
+
+/*
+    Depending on the argument passed to it,this function either draws the word "Equilibrating" 
+    at the bottom of the display, in the centre, in amber, or erases it.
+    
+    Arg is: true to display the text, false to erase it
+    
+    No return code.
+*/
+void DrawEquilibratingMessage(bool equilibrating)
+{
+    DrawTextOnHeatOnOffButton(equilibrating, "Equilibrating", SixteenBitColorValue(255, 255, 50), SixteenBitColorValue(0, 0, 0));
+}
+
+/*
+    Depending on the argument passed to it,this function either draws the word "Cooling" 
+    at the bottom of the display, in the centre, in light blue, or erases it.
+    
+    Arg is: true to display the text, false to erase it
+    
+    No return code.
+*/
+void DrawCoolingMessage(bool cooling)
+{
+    DrawTextOnHeatOnOffButton(cooling, "Cooling", SixteenBitColorValue(0, 243, 255), SixteenBitColorValue(0, 0, 0));
+}
+
+/*
+    Depending on the argument passed to it,this function either draws the word "Ready" 
+    at the bottom of the display, in the centre, in green, or erases it.
+    
+    Arg is: true to display the text, false to erase it
+    
+    No return code.
+*/
+void DrawReadyMessage(bool ready)
+{
+    DrawTextOnHeatOnOffButton(ready, "Ready", SixteenBitColorValue(0, 0xFF, 0), SixteenBitColorValue(0, 0, 0));
+}
+
+
+/*
+    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)
 {
-    // If required, query the GC to find out if it is ready to run *before* clearing the display -
+    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
     bool gcIsReadyToRun = false;
+    bool gcIsStabilising = false;
+    bool gcIsEquilibrating = false;
+    bool gcIsCooling = false;
+    bool gcIsRunning = false;
     if((structureIndex == GuiStruct_HomePage_1) && (usbDevice != NULL) && (usbHostGC != NULL)) {
-        gcIsReadyToRun = GCIsReadyToRun(usbDevice, usbHostGC);
+        int gcStatus = GetGCStatus(usbDevice, usbHostGC);
+        GCStateSimplified simplifiedGCState = GCStateOrFaultCode::GetSimplifiedGCState(gcStatus);
+        
+        gcIsReadyToRun = (simplifiedGCState == GC_READY_TO_RUN);
+        gcIsStabilising = (simplifiedGCState == GC_STABILISING);
+        gcIsEquilibrating = (simplifiedGCState == GC_EQUILIBRATING);
+        gcIsCooling = (simplifiedGCState == GC_COOLING);
+        gcIsRunning = (simplifiedGCState == GC_RUNNING);
     }
     
-    GuiLib_Clear();
+#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
+    // 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_ColumnPage3_10:
+        case GuiStruct_ColumnTempProfilePage_60:
+        case GuiStruct_ColumnDHPage1_40:
+        case GuiStruct_ColumnDHPage2_50:
+        case GuiStruct_ColumnDHTempProfilePage_61:
+#ifdef WANT_STATUS_RECTANGLE_ON_COLUMN_AUTO_CALIB_PAGE
+        case GuiStruct_ColumnDHAutoCalibrationPage_Def:
+#endif // WANT_STATUS_RECTANGLE_ON_COLUMN_AUTO_CALIB_PAGE
             singleGCComponentPageStatusColorAreas.DisplayGCComponentStatus(COLUMN);
+            qspiBitmaps.DisplayColumnComponentBitmap();
             break;
         case GuiStruct_InjectorPage1_3:
+        case GuiStruct_InjectorTempProfilePage_25:
+        case GuiStruct_InjectorGasStatusPage_30:
+        case GuiStruct_InjectorConsumablesPage_20:
             singleGCComponentPageStatusColorAreas.DisplayGCComponentStatus(INJECTOR);
+            qspiBitmaps.DisplayInjectorComponentBitmap();
             break;
-        case GuiStruct_DetectorPage1_4:
+        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:
             singleGCComponentPageStatusColorAreas.DisplayGCComponentStatus(DETECTOR);
+            qspiBitmaps.DisplayDetectorComponentBitmap();
             break;
-        case GuiStruct_GasPage1_6:
+        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
             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) {
-        DrawRunButton(gcIsReadyToRun);
+        DrawRunButton(gcIsReadyToRun || gcIsRunning);
+#ifdef USE_HEAT_ONOFF_BUTTON_BITMAPS
+        DrawHeatOnOffButton();
+#endif
+
+#define USE_IF_HERE
+#ifdef USE_IF_HERE
+        // Pointless always calling all four DrawxxxMessage functions - 
+        // only one can be true
+        
+        if(gcIsStabilising) {
+            DrawStabilisingMessage(true);
+        } else if (gcIsEquilibrating) {
+            DrawEquilibratingMessage(true);
+        } else if (gcIsCooling) {
+            DrawCoolingMessage(true);
+        } else if (gcIsReadyToRun) {
+            DrawReadyMessage(true);
+        }
+#undef USE_IF_HERE
+#else
+        DrawStabilisingMessage(gcIsStabilising);
+        DrawEquilibratingMessage(gcIsEquilibrating);
+        DrawCoolingMessage(gcIsCooling);
+        DrawReadyMessage(gcIsReadyToRun);
+#endif // USE_IF_HERE
     }
     
+    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 dbg[100];
-    sprintf(dbg, "After GuiLib_Refresh main 1");
-    EasyGUIDebugPrint(dbg, 100, 100);
+    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;
@@ -265,6 +1151,10 @@
     ); 
 }
 
+/*
+    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,
@@ -278,33 +1168,50 @@
     }
 }
 
-bool StartGCRun(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
+void DrawHeatOnOffButton(void)
 {
-    while(usbHostGC->ExecutingSetDeviceReport()) {}
-
-    char response[50];
-    usbHostGC->SetDeviceReport(usbDevice, "CRUN", response);
-    // We expect a response like this: "DACK" for success, "DNAK" for failure
+    // 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, "CRUN returned %s", response);
-    EasyGUIDebugPrint(dbg, 100, 150);    
+    sprintf(dbg, "DHOOB - bitmapIndex is %d", bitmapIndex);
+    EasyGUIDebugPrint(dbg, 0, 440);    
 #endif
 #undef DEBUG_HERE
 
-    return (response[1] == 'A');
+    // 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
 }
 
-bool StopGCRun(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
+
+/*
+    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];
-// TODO: Find out which is the correct command here
-//    char *cmd = "CSTP";
-    char *cmd = "CABT";
     usbHostGC->SetDeviceReport(usbDevice, cmd, response);
     // We expect a response like this: "DACK" for success, "DNAK" for failure
     
@@ -312,95 +1219,709 @@
 #ifdef DEBUG_HERE
     char dbg[100];
     sprintf(dbg, "%s returned %s", cmd, response);
-    EasyGUIDebugPrint(dbg, 100, 150);    
+    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.
     
-    return (response[1] == 'A');
+    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;    
 }
 
-bool ExitGCStandbyMode(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
+/*
+    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, "CDIS", response);
-    // We expect a response like this: "DACK" for success, "DNAK" for failure
+    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);
     
-    char dbg[100];
-    sprintf(dbg, "CDIS returned %s", response);
-    EasyGUIDebugPrint(dbg, 100, 150);   
+//    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;
     
-    if(getGCStatusLoop != NULL) {
-        getGCStatusLoop->ExitedGCStandbyMode();
+    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;
     }
     
-    return (response[1] == 'A');
+    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"
 }
 
-bool ClearGCErrors(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
+
+/*
+    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, "CCLR", response);
+    usbHostGC->SetDeviceReport(usbDevice, cmd, response);
     // We expect a response like this: "DACK" for success, "DNAK" for failure
     
-    char dbg[100];
-    sprintf(dbg, "CCLR returned %s", response);
-    EasyGUIDebugPrint(dbg, 100, 150);   
-    
     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) {
-                if(touchAreaIndex == 200) {
-                    // Stop run - as well as displaying the home page, do this...
-                    StopGCRun(usbDevice, usbHostGC);
-                }
+
+                // Depending on the touch area, we may need to do extra stuff here
+                // before displaying the appropriate page
                 
-                if(touchAreaIndex == 400) {
+                if(touchAreaIndex == GAS_SAVER_RETURN_TO_READY) {
                     // Take GC out of standby mode
                     ExitGCStandbyMode(usbDevice, usbHostGC);
                 }
                 
-                if(touchAreaIndex == 600) {
+                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);
+                    }
                 }
 
-                DisplayEasyGuiStructure(touchPanelPageSelector->GetPageNumber(), usbDevice, usbHostGC);
+                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
-                                   
-            if(touchAreaIndex == 200) {
-                // Is in same place as Heat On/Off button
-                StartHeatOnOffTimeout();
-            }
-        }
+        } // TouchPanelPageSelector
         
         if(!dealtWithTouch) {
-            if(touchAreaIndex == 100) { // Run button
+            
+            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, else do nothing
+                        // Start run - if this works, display the 'Run' page, etc, else do nothing
                         if(StartGCRun(usbDevice, usbHostGC)) {
-                            DisplayEasyGuiStructure(GuiStruct_RunningPage_7, usbDevice, usbHostGC);
+                            SetupForStartOfRun(usbDevice, usbHostGC);
                         } else {
                             DrawErrorMessage("*** Run failed to start ***");
                             
@@ -409,16 +1930,21 @@
                             
                             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);
                     }
-                    // else GC is not ready to run (button should be greyed out) - ignore
                 }
             
                 dealtWithTouch = true;
             }
-        }
+        } // Run button
 
         if(!dealtWithTouch) {
-            if(touchAreaIndex == 300) { // Heat on/off button
+
+            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) {
@@ -427,12 +1953,22 @@
                             } 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);
                         }
                     }
                 }
@@ -441,111 +1977,425 @@
                 // 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
     }
 }
    
-void SetupUSBGCTouchListener(DMBoard* board, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
-{
-    // Note that TouchListener is a singleton - we do not need or want there to be more than one instance of it
-    // (there is only one board, and only one touch panel)
-    mainTouchListener = TouchListener::GetInstance(board->touchPanel(), usbDevice, usbHostGC);
+
+/*
+    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. 
 
-    if(mainTouchListener != NULL) {
-        mainTouchListener->SetTouchCallbackFunction(&TouchCallback);
+    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;
 
-// Not yet...
-//    mainTouchListener->SetTouchReleasedCallbackFunction(&TouchReleasedCallback);
-//  
-//    mainTouchListener->SetTimerOneSecondCallbackFunction(&TimerOneSecondCallback);
+    while(usbDevice == NULL) {
+        for (uint8_t i = 0; i < MAX_DEVICE_CONNECTED; ++i) {
+            usbDevice = usbHost->getDevice(i);
+
+            if (usbDevice) {
+                return usbDevice;
+            }
+        }
     }
+    
+    return NULL;
 }
 
-
-int main()
+/*
+    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)
 {
-  DMBoard::BoardError err;
-  DMBoard* board = &DMBoard::instance();
-  RtosLog* log = board->logger();
-  Display* disp = board->display();
-  
-  do {
-    err = board->init();
-    if (err != DMBoard::Ok) {
-      log->printf("Failed to initialize the board, got error %d\r\n", err);
-      break;
-    }
-    
-    log->printf("\n\nHello World!\n\n");
+#include "VarInit.h" // Renamed from the easyGUI file "VarInit.c", so as not to confuse the mbed compiler
+}
     
-    void* fb = disp->allocateFramebuffer();
-    if (fb == NULL) {
-      log->printf("Failed to allocate memory for a frame buffer\r\n");
-      err = DMBoard::MemoryError;
-      break;
-    }
-    
-    
-//    Display::DisplayError disperr = disp->powerUp(fb, Display::Resolution_24bit_rgb888);
-    // Start display in default mode (16-bit) (24-bit uses too much memory)
-    Display::DisplayError disperr = disp->powerUp(fb);
-    if (disperr != Display::DisplayError_Ok) {
-      log->printf("Failed to initialize the display, got error %d\r\n", disperr);
-      break;
-    }
-    
-  } while(false);
+
+/*
+    Sets up the easyGUI user interface in an acceptable initial state.
 
-  if (err != DMBoard::Ok) {
-    log->printf("\nTERMINATING\n");
-    wait_ms(2000); // allow RtosLog to flush messages
-    mbed_die();
-  }  
-
-  
-  // easyGUI stuff - note function calls require 'extern "C"' in relevant header
+    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();
   
-    GuiLib_Refresh();
-  
-    DisplayEasyGuiStructure(GuiStruct_HomePage_1, NULL, NULL);
-  
-    GuiLib_Refresh();
+    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;
+        }
+    
+    } 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();
-    USBDeviceConnected* usbDevice;
     USBHostGC usbHostGC;
 
-    usbDevice = NULL;
-
     DrawErrorMessage("Waiting for USB device...");
-    
-    // Wait (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
-    while(usbDevice == NULL) {
-        for (uint8_t i = 0; i < MAX_DEVICE_CONNECTED; ++i) {
-            usbDevice = usbHost->getDevice(i);
-            
-            if (usbDevice) {
-                break;
-            }
-        }
-    }
+    // Note that 'GetUSBDevice' will not return until it finds a USB device -
+    // it does *not* timeout
+    USBDeviceConnected* usbDevice = GetUSBDevice(usbHost);
     
     DrawErrorMessage("                                            ");
 
@@ -558,10 +2408,11 @@
                 DrawErrorMessage("Found GC device                                      ");
                 
 
+#ifdef OLD_TOUCH_LISTENER
                 SetupUSBGCTouchListener(board, usbDevice, &usbHostGC);
                 
                 DrawErrorMessage("After call to SetupUSBGCTouchListener                ");
-
+#endif
 
                 getGCStatusLoop = GetGCStatusLoop::GetInstance(usbDevice, &usbHostGC);
                 
@@ -570,32 +2421,49 @@
                 
                 theGCHeatControl = new GCHeatControl(usbDevice, &usbHostGC);
                 
+#ifdef USE_HEAT_ONOFF_BUTTON_BITMAPS
+                DrawHeatOnOffButton();
+#else
                 UpdateHeatOnOffEasyGuiVariable();
-                
+#endif // USE_HEAT_ONOFF_BUTTON_BITMAPS
+
                 DrawErrorMessage("After UpdateHeatOnOffEasyGuiVariable                 ");
                 
-                
-                //theEthernetTimerHandler = EthernetTimerHandler::GetInstance(usbDevice, &usbHostGC);
-                
-                
+                                
                 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();
-
-                DrawErrorMessage("Point 2                                              ");
+                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 3                                              ");
+                DrawErrorMessage("Point 5                                              ");
                 
-                DisplayEasyGuiStructure(GuiStruct_HomePage_1, usbDevice, &usbHostGC);
-  
-                // Currently, this never returns - but it allows time for the TouchCallback function to be invoked
-                //getGCStatusLoop->MainLoop();
-                getGCStatusLoop->MainLoopWithEthernet();
+                //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;