John Mitchell / lpc4088_displaymodule_GC500_2_5inch

Dependencies:   DMBasicGUI DMSupport

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers GetGCStatusLoop.cpp Source File

GetGCStatusLoop.cpp

00001 #include "mbed.h"
00002 #include "DMBoard.h"
00003 #include "EthernetInterface.h"
00004 #include "NetworkParameters.h"
00005 
00006 #include <string.h>
00007 #include <float.h>
00008 
00009 #include "GetGCStatusLoop.h"
00010 #include "SettingsHandler.h"
00011 #include "ServiceInterval.h"
00012 #include "GCRealTimeClock.h"
00013 #include "ColumnDHAutoCalibrationPageHandler.h"
00014 #include "ColumnDHPSUDACPageHandler.h"
00015 #include "SwimDraw.h"
00016 #include "USBHostGCUtilities.h"
00017 
00018 #include "GuiLib.h"
00019 
00020 
00021 #define USE_LED_FOR_DEBUGGING
00022 
00023 #ifdef USE_LED_FOR_DEBUGGING
00024 
00025 #include "gpio_api.h"
00026 #include "wait_api.h"
00027 #include "toolchain.h"
00028 #include "mbed_interface.h"
00029 
00030 // We turn on LED 2 during Send Method
00031 static void SetLed2(bool turnLedOn)
00032 {
00033     gpio_t led_2; gpio_init_out(&led_2, LED2);
00034 
00035     if(turnLedOn) {
00036         gpio_write(&led_2, 0); // zero appears to mean "turn LED 2 on"
00037     } else {
00038         gpio_write(&led_2, 1); // one appears to turn it off
00039     }
00040 }
00041 
00042 // We turn on LED 3 when we have sent a message to the GC (received from the Ethernet thread)
00043 // and we are waiting for its response
00044 static void SetLed3(bool turnLedOn)
00045 {
00046     gpio_t led_3; gpio_init_out(&led_3, LED3);
00047 
00048     if(turnLedOn) {
00049         gpio_write(&led_3, 1); // one appears to mean "turn LED 3 on"
00050     } else {
00051         gpio_write(&led_3, 0); // zero appears to turn it off
00052     }
00053 }
00054 
00055 #endif // USE_LED_FOR_DEBUGGING
00056 
00057 
00058 /*
00059     Displays the specified text string at the specified location in the currently-displayed easyGUI page.
00060     
00061     Defined in main.cpp
00062 */
00063 extern void EasyGUIDebugPrint(char *stuffToPrint, short X, short Y);
00064 
00065 /*
00066     Displays the specified text string at the specified location in the currently-displayed easyGUI page.
00067     
00068     Defined in main.cpp - and omits the 'ALLOW_DEBUG_PRINTS' #define
00069 */
00070 extern void SpecialDebugPrint(char *stuffToPrint, GuiConst_INT16S X, GuiConst_INT16S Y);
00071 
00072 
00073 // Version of the above that increments, and displays, a counter each time it is called
00074 extern void EasyGUIDebugPrintWithCounter(char *stuffToPrint, short X, short Y);
00075 
00076 /*
00077     Passed three 8-bit colour components - red, green and blue.
00078     Returns the corresponding 16-bit colour value (5 bits for red, 6 bits for green, 5 bits for blue).
00079     
00080     Defined in main.cpp
00081 */
00082 extern GuiConst_INTCOLOR SixteenBitColorValue(GuiConst_INT8U red, GuiConst_INT8U green, GuiConst_INT8U blue);
00083 
00084 /*
00085     Draws the Run button in the correct place, in the correct enabled/disabled state.
00086     Arg is: true to display the button in an enabled state, false to display it disabled.
00087     No return code.
00088     
00089     Defined in main.cpp
00090 */
00091 extern void DrawRunButton(bool enabled);
00092 
00093 /*
00094     Draws the Heat On/Off button in the correct place, in the correct state (i.e. saying "Heat On"
00095     if it is off, and vice versa - i.e. the text states what the button will do if pressed).
00096     No arguments (finds out the current heat on/off state for itself)
00097     No return code.
00098     
00099     Defined in main.cpp
00100 */
00101 extern void DrawHeatOnOffButton(void);
00102 
00103 
00104 /*
00105     Draws the background bitmap - without the Ellutia logo. It fills the screen, so you do not need to call GuiLib_Clear.
00106     Defined in main.cpp
00107 */
00108 extern void DrawBackgroundBitmap(void);
00109 #define USING_BACKGROUND_BITMAP
00110 
00111 /*
00112     Same as the above, but draws the background bitmap *with* the Ellutia logo.
00113     Defined in main.cpp
00114 */
00115 void DrawBackgroundBitmapWithLogo(void);
00116 
00117 
00118 /*
00119     The colour of the main part of the (fake) bitmap - also from main.cpp
00120 */
00121 extern GuiConst_INTCOLOR GetFakeBackgroundBitmapMainColour(void);
00122 
00123 /*
00124     Depending on the argument passed to it,this function either draws the word "Stabilising" 
00125     at the bottom of the display, in the centre, in green, or erases it.
00126     
00127     Arg is: true to display the text, false to erase it
00128     
00129     No return code.
00130     
00131     Defined in main.cpp
00132 */
00133 extern void DrawStabilisingMessage(bool stabilising);
00134 
00135 /*
00136     Depending on the argument passed to it,this function either draws the word "Equilibrating" 
00137     at the bottom of the display, in the centre, in amber, or erases it.
00138     
00139     Arg is: true to display the text, false to erase it
00140     
00141     No return code.
00142     
00143     Defined in main.cpp
00144 */
00145 extern void DrawEquilibratingMessage(bool equilibrating);
00146 
00147 /*
00148     Depending on the argument passed to it,this function either draws the word "Cooling" 
00149     at the bottom of the display, in the centre, in light blue, or erases it.
00150     
00151     Arg is: true to display the text, false to erase it
00152     
00153     No return code.
00154 */
00155 extern void DrawCoolingMessage(bool cooling);
00156 
00157 /*
00158     Depending on the argument passed to it,this function either draws the word "Ready" 
00159     at the bottom of the display, in the centre, in green, or erases it.
00160     
00161     Arg is: true to display the text, false to erase it
00162     
00163     No return code.
00164 */
00165 extern void DrawReadyMessage(bool ready);
00166 
00167 
00168 /*
00169     Tells the caller whether or not a specified GC command represents the start of a method,
00170     and that therefore a new method is now being sent to the GC.
00171     
00172     Params: pointer to a null-terminated string containing the command in question
00173     
00174     Returns true if the command is one that occurs at the start of a method (and nowhere else),
00175     false if not.
00176     
00177     Defined in main.cpp
00178 */
00179 bool IsStartOfMethodCommand(char *gcCommand);
00180 
00181 /*
00182     Tells the caller whether or not a specified GC command represents the end of a method,
00183     and that therefore a new method has just been sent to the GC.
00184     
00185     Params: pointer to a null-terminated string containing the command in question
00186     
00187     Returns true if the command is one that occurs at the end of a method (and nowhere else),
00188     false if not.
00189     
00190     Defined in main.cpp
00191 */
00192 bool IsEndOfMethodCommand(char *gcCommand);
00193 
00194 
00195 
00196 /*
00197     The original TouchCallback function, called directly by ListenerFunction in the original TouchListener class.
00198     
00199     Defined in main.cpp.
00200     
00201     TODO: Move into this class? Rationalise, at least...
00202 */
00203 extern void TouchCallback(touch_coordinate_t touchCoords, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC, int tickCount, bool newTouch);
00204 
00205 
00206 /*
00207     A function to deal with the 'admin' required when the GC starts running - 
00208     i.e. setting up the relevant easyGUI variables, displaying the correct page, etc.
00209     
00210     Defined in main.cpp
00211 */
00212 extern void SetupForStartOfRun(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC);
00213 
00214 
00215 /*
00216     Reads the current state of the door actuators, and sets up the buttons 
00217     at the bottom of the page appropriately. 
00218     
00219     Defined in main.cpp
00220 */
00221 extern void SetupDoorActuatorCommandUserInterface(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC, bool beforePageDisplay);
00222 
00223 /*
00224     Tells the caller whether or not the door actuator buttons have changed,
00225     from the single "Close"/"Unlock" button to the two "Lock" and "Release" buttons,
00226     or vice versa
00227 
00228     Defined in main.cpp
00229 */
00230 extern bool DoorActuatorButtonsHaveChanged(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC);
00231 
00232 
00233 /*
00234     Sets up the easyGUI variable that controls the colour of the door lock/unlock command.
00235     
00236     Defined in main.cpp
00237 */
00238 extern void SetupDoorActuatorCommandColour(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC, bool actuatorsAreMoving);
00239 
00240 
00241 /*
00242     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 -
00243     by calling the EasyGUI GuiLib_VLine function multiple times.
00244     
00245     Args: colour of the profile
00246           X coords of the left and right edges of the section
00247           Y coord of the bottom
00248           Y coords of the top left and top right
00249           
00250     Returns true if OK, false if it failed (e.g. the coords were invalid).
00251     
00252     Defined in main.cpp
00253 */
00254 extern bool DrawProfileSectionUsingGuiLibVLine(GuiConst_INTCOLOR colour, GuiConst_INT16S xLeft, GuiConst_INT16S xRight, GuiConst_INT16S yBottom, GuiConst_INT16S yTopLeft, GuiConst_INT16S yTopRight);
00255 
00256 
00257 /*
00258     Function to cause the LPC4088 to reboot.
00259     
00260     Defined in main.cpp.
00261 */
00262 extern void reboot();
00263 
00264 
00265 // Note that GetGCStatusLoop is a singleton - we do not need or want there to be more than one instance of it
00266 // (there is only one GC, and only one LPC4088).
00267 // This is the one and only GetGCStatusLoop instance
00268 GetGCStatusLoop * GetGCStatusLoop::theGetGCStatusLoop = NULL;
00269 
00270 //const int GetGCStatusLoop::waitTimeMs = 1000;
00271 const int GetGCStatusLoop::waitTimeMs = 500; 
00272 //const int GetGCStatusLoop::waitTimeMs = 200;
00273 const int GetGCStatusLoop::shortWaitTimeMs = 50; // For use by the thread_wait calls that look for Ethernet transactions
00274                                                  // and touch events at multiple points in the 'main loop'
00275 
00276 #define USE_THREAD_WAIT // Thread::wait lets other threads run while this one is paused
00277                         // wait_ms (the other option) blocks all other threads while waiting - not a good idea.
00278                         // Actually now using Thread::signal_wait, as part of restructuring touch event code
00279                         // (see also the SimplifiedTouchListener class) - and Ethernet code (see EthernetHandler).
00280 
00281 const float GetGCStatusLoop::graphBarIntervalMinutes = 1.0f; // Units are minutes...
00282 const float GetGCStatusLoop::graphBarIntervalSeconds = 0.1f; // ...in both cases
00283 
00284 const float GetGCStatusLoop::methodTimeUnitsThreshold = 5.0f; // If the method takes less than this number of minutes to run,
00285                                                               // use seconds as the time units we display on the profile graphs,
00286                                                               // otherwise use minutes
00287 
00288 // Timer stuff - trying to avoid responding to the same touch event more than once
00289 const uint32_t GetGCStatusLoop::timerIntervalMilliSec = 600; // i.e. 0.6 second
00290 const uint32_t GetGCStatusLoop::minTimerTicksBetweenTouchEvents = 5; // i.e. 1 second
00291 // Increase timer tick interval - does this make Ethernet comms more responsive?
00292 //const uint32_t GetGCStatusLoop::timerIntervalMilliSec = 500; // i.e. 0.5 second
00293 //const uint32_t GetGCStatusLoop::minTimerTicksBetweenTouchEvents = 2; // i.e. 1 second
00294 // Answer - no - possibly worse, in fact
00295 int GetGCStatusLoop::timerTickCount = 0;
00296 
00297 // Trying different ways of preventing the same touch event being processed more than once
00298 //#define MULTI_TOUCH_TECHNIQUE_1 // Enforce a minimum time interval (i.e. number of ticks) between the touch events we respond to
00299 //#define MULTI_TOUCH_TECHNIQUE_2 // Look for successive touch events with different X and/or Y coordinates
00300 //#define MULTI_TOUCH_TECHNIQUE_3 // Have at least one timeout or timer tick (and now, Ethernet message) between touch events
00301 //#define MULTI_TOUCH_TECHNIQUE_4 // Use a Timer object (not an RTosTimer, like MULTI_TOUCH_TECHNIQUE_1) to enforce a minimum time interval between touches
00302 // The above are mutually exclusive - do not un-comment more than one at the same time 
00303 // (you can comment them all out if you wish to see the original problem)
00304 // At present (07 Oct 2016), MULTI_TOUCH_TECHNIQUE_4 seems to be the clear winner - far more effective than the others
00305 // Although now (04 Nov 2016) itself superseded by MULTI_TOUCH_TECHNIQUE_5, in SimplifiedTouchListener
00306 
00307 #ifdef MULTI_TOUCH_TECHNIQUE_1
00308 osThreadId GetGCStatusLoop::timerCallbackThreadToSignal;
00309 #endif // MULTI_TOUCH_TECHNIQUE_1
00310 
00311 /*
00312     Convenient default values for Ethernet parameters
00313 */
00314 //#define FOR_GRAHAM_SEWELL
00315 #ifdef FOR_GRAHAM_SEWELL
00316 const int   GetGCStatusLoop::defaultEthernetPort = 3456;
00317 const char* GetGCStatusLoop::defaultEthernetIP = "192.168.111.100";
00318 const char* GetGCStatusLoop::defaultEthernetMask = "255.255.255.0";
00319 const char* GetGCStatusLoop::defaultEthernetGateway = "192.168.111.254";
00320 #else // Mine
00321 const int   GetGCStatusLoop::defaultEthernetPort = 3456;
00322 const char* GetGCStatusLoop::defaultEthernetIP = "192.168.1.100";
00323 const char* GetGCStatusLoop::defaultEthernetMask = "255.255.255.0";
00324 const char* GetGCStatusLoop::defaultEthernetGateway = "192.168.1.254";
00325 #endif
00326 
00327 // Text for the "Start/Exit Calibration" button on the DH Column Calibration page
00328 const char* GetGCStatusLoop::startDHColumnCalibration = "Start Calibration";
00329 const char* GetGCStatusLoop::exitDHColumnCalibration  = "Exit Calibration";
00330 
00331 // On the easyGUI pages/structures, character code 161 appears as the degree symbol (the little circle at the top left of the character position). See the "Font Editing" page (accessed with the F4 key).
00332 // To type it into NotePad, enable Num Lock on the keyboard, hold down the Alt key, type 0161 on the numeric keypad, then release the Alt key.
00333 // Note that a different character ("¡") then appears in NotePad. If you copy and paste that character into a string in easyGUI, it appears as "¡",
00334 // but in an easyGUI "structure", it appears as the degree symbol. (I don't understand it either.)
00335 // The following is the only way I could find to use the 161 character in code that (a) works, and (b) the compiler does not complain about.
00336 const char GetGCStatusLoop::degSymbol = 161; 
00337 const char GetGCStatusLoop::stringFormatdegCUnits[6] = { '%', 's', ' ', 161, 'C', '\0' };
00338 // Functions to make the above available to the rest of the world, in a controlled manner
00339 const char *GetGCStatusLoop::GetDegCUnitsWithSpace(void)
00340 {
00341     return &stringFormatdegCUnits[2];
00342 }
00343 const char *GetGCStatusLoop::GetDegCUnitsWithoutSpace(void)
00344 {
00345     return &stringFormatdegCUnits[3];
00346 }
00347 #define TRY_DEG_SYMBOL
00348 
00349 
00350 //#define USING_DATASET_4 // This is the 'dot at current time' dataset, used in the profile graphs we display while the GC is running.
00351                         // Applies to the 'running column' and 'running gas' graphs. Omit when we make the 'before now' bars thicker
00352                         // than the 'after now' bars. Without the 'dot at current time', we do not have to clear the graph 
00353                         // when we redraw it - reduces flickering (since the bars are now overwriting themselves)
00354                         
00355 //#define USING_DATASET_4_ON_NON_RUNNING_GRAPHS // Since these graphs do not show a 'current time',
00356                                               // dataset 4 serves little purpose on these graphs either
00357                                               
00358 #define PTV_RAMPS_AVAILABLE // #define this if the GC software includes the commands to set up the PTV ramps - i.e. it is at least version 3.90.
00359                             // If not, comment it out.
00360                             
00361 #define UPDATE_PAGES_CONTINUOUSLY // #define this to cause pages that display data to be updated/redisplayed whether or not the GC status has changed
00362                                   // (otherwise they will be redisplayed only if the status has changed, and the displayed temperatures may get out of date)
00363     
00364 #define DO_NOTHING_ELSE_WHILE_SENDING_METHOD // #define this to disable all other 'events' while downloading a method from Ellution to the GC.
00365                                              // (we need this to be as fast as possible)       
00366 
00367 #define TEST_GUILIB_VLINE_PROFILES // #define this to use the experimental functions that draw a solid profile direct to the display,
00368                                    // without using the easyGUI graph functions
00369 
00370 //#define WANT_STATUS_RECTANGLE_ON_COLUMN_AUTO_CALIB_PAGE // Now moved to Servicing pages - surely don't want status rectangles there?
00371 //#define WANT_STATUS_RECTANGLE_ON_GAS_CALIB_PAGES        // ....
00372 //#define WANT_COLUMN_STATUS_RECTANGLE
00373 //#define WANT_DETECTOR_STATUS_RECTANGLE
00374 //#define WANT_INJECTOR_STATUS_RECTANGLE
00375 //#define WANT_GAS_STATUS_RECTANGLE
00376 //#define WANT_DOOR_ACTUATOR_BUTTONS_ON_COLUMN_PAGES
00377 //#define WANT_COMPONENT_ICON_ON_PROFILE_PAGES
00378 
00379 /*
00380     Singleton class - return the one and only instance, first creating it if necessary.
00381     
00382     The instance is passed pointers to the USBHostGC instance and the USB device corresponding with the GC.
00383 */
00384 GetGCStatusLoop *GetGCStatusLoop::GetInstance(USBDeviceConnected* newUsbDevice, USBHostGC* newUsbHostGC)
00385 {
00386     if (theGetGCStatusLoop == NULL) {
00387         theGetGCStatusLoop = new GetGCStatusLoop(newUsbDevice, newUsbHostGC);
00388     }
00389     return theGetGCStatusLoop;
00390 }
00391 
00392 /*
00393   Static method to return the one and only GetGCStatusLoop instance, if it exists. 
00394   If not, this function will return NULL - it will *not* create a new instance.
00395   
00396   Caller must check for NULL
00397   **************************
00398 */
00399 GetGCStatusLoop *GetGCStatusLoop::GetInstance(void)
00400 {
00401     return theGetGCStatusLoop;
00402 }
00403 
00404 // Singleton class - private constructor
00405 GetGCStatusLoop::GetGCStatusLoop(USBDeviceConnected* newUsbDevice, USBHostGC* newUsbHostGC)
00406 {
00407     usbDevice = newUsbDevice;
00408     usbHostGC = newUsbHostGC;
00409     
00410     currentPage = 9999; // Impossible value
00411     
00412     pageJustChanged = false;
00413     displayingData = false;
00414     needToUpdateProfileGraphs = false;
00415     
00416     ethernetPort = defaultEthernetPort;
00417     strcpy(ethernetIP, defaultEthernetIP);
00418     strcpy(ethernetMask, defaultEthernetMask);
00419     strcpy(ethernetGateway, defaultEthernetGateway);
00420     useDHCPForEthernet = false;
00421     
00422     gcInStandbyMode = false;
00423     
00424     realGCIsRunning = false;
00425     
00426     sendingMethod = false;
00427     
00428     homePageGCComponentStatusColorAreas = NULL;
00429     singleGCComponentPageStatusColorAreas = NULL;
00430     
00431     runningPage1ProgressBar = new ProgressBar(100, 250, 600, 50, horizontal, 100.0, // We will change the calibrated range at the start of every run
00432                                                 SixteenBitColorValue(0xFF, 0, 0),       // bar colour - red
00433                                                 GetFakeBackgroundBitmapMainColour(),    // background colour - as used in the fake background bitmap, drawn in main.cpp
00434                                                 0                                       // border colour - black
00435                                                 );
00436     
00437     CreateGuiLibGraphsAndDataSets();
00438             
00439     SetupColumnAndInjectorAndGasProfileData();
00440     
00441     previousColumnMethodRunTime = -99.0f;
00442     columnMethodFinished = false;
00443     
00444     previousGasMethodRunTime = -99.0f;
00445     gasMethodFinished = false;
00446     
00447     gotAtLeastOneTimeout = false;
00448     
00449     lastColumnStatusDisplayedOnHomePage = NONE;
00450     lastInjectorStatusDisplayedOnHomePage = NONE;
00451     lastDetectorStatusDisplayedOnHomePage = NONE;
00452     lastGasStatusDisplayedOnHomePage = NONE;
00453     
00454     lastColumnStatusDisplayedOnColumnPage = NONE;
00455     lastInjectorStatusDisplayedOnInjectorPage = NONE;
00456     lastDetectorStatusDisplayedOnDetectorPage = NONE;
00457     lastGasStatusDisplayedOnGasInformationPage = NONE;
00458 
00459     lastSimplifiedGCState = GC_IDLE;
00460     
00461     runWasAborted = false;
00462     
00463     handlingEthernetMessage = false;
00464     handlingTouchEvent = false;
00465     
00466 #ifdef MULTI_TOUCH_TECHNIQUE_1
00467     // rtosTimer stuff
00468     rtosTimer = new RtosTimer(GetGCStatusLoop::TimerCallback);
00469     rtosTimer->start(timerIntervalMilliSec);
00470     lastTouchEventTickCount = 0;
00471     timerCallbackThreadToSignal = osThreadGetId();
00472 #endif // MULTI_TOUCH_TECHNIQUE_1
00473 
00474 #ifdef MULTI_TOUCH_TECHNIQUE_2
00475     lastTouchEventX = 9999;
00476     lastTouchEventY = 9999;
00477 #endif // MULTI_TOUCH_TECHNIQUE_2
00478 
00479 #ifdef MULTI_TOUCH_TECHNIQUE_4
00480     touchTimer.stop();
00481     touchTimer.reset();
00482     touchTimer.start();
00483 #endif // MULTI_TOUCH_TECHNIQUE_4
00484 
00485     qspiBitmaps = NULL;
00486     
00487     columnMethodRampData = NULL; // Create this only when we need it
00488     columnMethodPageScrollIndex = 0;
00489     previousColumnMethodPageScrollIndex = columnMethodPageScrollIndex;
00490     
00491     injectorMethodRampData = NULL; // Create this only when we need it
00492     injectorMethodPageScrollIndex = 0;
00493     previousInjectorMethodPageScrollIndex = injectorMethodPageScrollIndex;
00494     
00495     gasMethodRampData = NULL; // Create this only when we need it
00496     gasMethodPageScrollIndex = 0;
00497     previousGasMethodPageScrollIndex = gasMethodPageScrollIndex;
00498 }
00499     
00500 // Private destructor also
00501 GetGCStatusLoop::~GetGCStatusLoop()
00502 {
00503     if(runningPage1ProgressBar != NULL) {
00504         delete runningPage1ProgressBar;
00505     }
00506     
00507     delete runningColumnPageGraph;
00508     
00509     delete runningColumnPageGraphCompleteProfileDataSet;
00510     delete runningColumnPageGraphDataSet0;
00511     delete runningColumnPageGraphDataSet1;
00512     delete runningColumnPageGraphDataSet2;
00513     delete runningColumnPageGraphDataSet3;
00514     delete runningColumnPageGraphDataSet4;
00515 
00516 
00517     delete runningGasPageGraph;
00518     
00519     delete runningGasPageGraphCompleteProfileDataSet;
00520     delete runningGasPageGraphDataSet0;
00521     delete runningGasPageGraphDataSet1;
00522     delete runningGasPageGraphDataSet2;
00523     delete runningGasPageGraphDataSet3;
00524     delete runningGasPageGraphDataSet4;
00525 
00526 
00527     delete injectorTempProfilePageGraph;
00528 
00529     delete injectorTempProfilePageGraphCompleteProfileDataSet;
00530     delete injectorTempProfilePageGraphDataSet0;
00531     delete injectorTempProfilePageGraphDataSet1;
00532     delete injectorTempProfilePageGraphDataSet2;
00533     delete injectorTempProfilePageGraphDataSet3;
00534     delete injectorTempProfilePageGraphDataSet4;
00535 
00536 
00537     delete gasFlowProfilePageGraph;
00538 
00539     delete gasFlowProfilePageGraphCompleteProfileDataSet;
00540     delete gasFlowProfilePageGraphDataSet0;
00541     delete gasFlowProfilePageGraphDataSet1;
00542     delete gasFlowProfilePageGraphDataSet2;
00543     delete gasFlowProfilePageGraphDataSet3;
00544     delete gasFlowProfilePageGraphDataSet4;
00545 
00546 
00547     delete columnTempProfilePageGraph;
00548 
00549     delete columnTempProfilePageGraphCompleteProfileDataSet;
00550     delete columnTempProfilePageGraphDataSet0;
00551     delete columnTempProfilePageGraphDataSet1;
00552     delete columnTempProfilePageGraphDataSet2;
00553     delete columnTempProfilePageGraphDataSet3;
00554     delete columnTempProfilePageGraphDataSet4;
00555 
00556 
00557     delete runningInjectorPageGraph;
00558     
00559     delete runningInjectorPageGraphCompleteProfileDataSet;
00560     delete runningInjectorPageGraphDataSet0;
00561     delete runningInjectorPageGraphDataSet1;
00562     delete runningInjectorPageGraphDataSet2;
00563     delete runningInjectorPageGraphDataSet3;
00564     delete runningInjectorPageGraphDataSet4;
00565     
00566     if(columnMethodRampData != NULL) {
00567         delete columnMethodRampData;
00568     }
00569     
00570     if(injectorMethodRampData != NULL) {
00571         delete injectorMethodRampData;
00572     }
00573 
00574     if(gasMethodRampData != NULL) {
00575         delete gasMethodRampData;
00576     }
00577 
00578 #ifdef MULTI_TOUCH_TECHNIQUE_1
00579     rtosTimer->stop();
00580     delete rtosTimer;
00581 #endif // MULTI_TOUCH_TECHNIQUE_1
00582 }
00583 
00584 void GetGCStatusLoop::SetQSPIBitmaps(QSPIBitmaps* ptrToQSPIBitmaps)
00585 {
00586     qspiBitmaps = ptrToQSPIBitmaps;
00587 }
00588     
00589 
00590 /*
00591     Displays the column lock and release buttons "on top of" the Lock/Unlock (originally Open Door) button 
00592     defined in easyGUI, and displayed on each of the column pages. This allows us to give the user
00593     two options (Lock or Release) when we complete the Close operation - the rest of the time 
00594     we display only one option, and this is how the easyGUI pages for the column are set up.
00595     Since it appears to be impossible to show or hide individual easyGUI controls at run time
00596     (i.e. each easyGUI page is fixed at design time) this seems to be the only way 
00597     to make a change to the user interface at run time.
00598     
00599     No arguments, no return code.
00600 */
00601 void GetGCStatusLoop::DisplayColumnLockAndReleaseButtons(void)
00602 {
00603     // Two adjoining rectangles on top of the current Lock/Unlock button
00604     GuiLib_BorderBox(248, 404, 400, 480, 0, SixteenBitColorValue(128, 128, 128)); // Black border, grey fill
00605     DisplayText("Lock", 324, 450, GuiLib_ALIGN_CENTER, GuiFont_Helv20Bold, GuiVar_doorActuatorCommandFGColour);
00606     GuiLib_BorderBox(400, 404, 552, 480, 0, SixteenBitColorValue(128, 128, 128)); // Black border, grey fill
00607     DisplayText("Release", 476, 450, GuiLib_ALIGN_CENTER, GuiFont_Helv20Bold, GuiVar_doorActuatorCommandFGColour);
00608 }
00609 
00610 
00611 void GetGCStatusLoop::SetupColumnAndInjectorAndGasProfileData(void)
00612 {
00613     // Do this *** before *** setting up the injector and gas profiles
00614     SetupColumnTempProfilePageGraphDataFromGC();
00615 
00616     // Do this *** after *** setting up the column method
00617     SetupInjectorTempProfilePageGraphDataFromGC();
00618 
00619     // Do this *** after *** setting up the column method
00620     SetupGasFlowProfilePageGraphDataFromGC();
00621     
00622     // These easyGUI variables are intended to warn the user if we have 
00623     // no methods set up for these components - but we do now have methods for them
00624     GuiVar_columnTempProfilePageNoMethod[0] = '\0';
00625     GuiVar_injectorTempProfilePageNoMethod[0]= '\0';
00626     GuiVar_gasFlowProfilePageNoMethod[0]= '\0';
00627 }
00628 
00629 /*
00630     Called by the system in response to timer ticks.
00631     
00632     *** Needs to be kept as short as possible ***
00633 */
00634 void GetGCStatusLoop::TimerCallback(void const * argument)
00635 {
00636 #ifdef MULTI_TOUCH_TECHNIQUE_1
00637     ++timerTickCount;
00638     
00639     osSignalSet(timerCallbackThreadToSignal, TIMER_TICK);    
00640 #endif // MULTI_TOUCH_TECHNIQUE_1
00641 }
00642     
00643 /*
00644     Creates (but does not assign any data to) the objects that deal with the easyGUI graphs
00645     and their datasets. We should only need to create these once,
00646     but we may need to setup or change their data any number of times.
00647 */
00648 void GetGCStatusLoop::CreateGuiLibGraphsAndDataSets(void)
00649 {
00650     // The values passed to the GuiLibGraph constructor in creating each instance below
00651     // *must* *match* the graph indices assigned in the corresponding easyGUI 'structures' - 
00652     // there seems to be no way of getting these values from easyGUI at runtime,
00653     // so we must hard code them here
00654     runningColumnPageGraph = new GuiLibGraph(0);
00655     runningGasPageGraph = new GuiLibGraph(1);
00656     injectorTempProfilePageGraph = new GuiLibGraph(2);
00657     gasFlowProfilePageGraph = new GuiLibGraph(3);
00658     columnTempProfilePageGraph = new GuiLibGraph(4);
00659     runningInjectorPageGraph = new GuiLibGraph(6);
00660 
00661 
00662     // Now create the datasets for each graph. 
00663     //
00664     // In each case, the 'xxxGraphCompleteProfileDataSet' object contains the complete profile, but we do not display it (as such)
00665     // on the relevant graph. Instead, we make partial copies of it to each of the 'xxxGraphDataSetn' objects,
00666     // separating the points between the start and 'now' from the points between 'now' and the end of the method.
00667     // This allows us to assign these to different datasets in the relevant easyGUI graph, so that we can display
00668     // them in different colours and in different styles. 
00669     //
00670     // Having the complete profile in one dataset, then copying the relevant parts of it to the others, makes the code simpler, 
00671     // and easier to understand, than if we kept getting partial copies of the profile from the GC.
00672     //
00673     runningColumnPageGraphCompleteProfileDataSet = new GuiLibGraphDataSet;
00674     runningColumnPageGraphDataSet0 = new GuiLibGraphDataSet;
00675     runningColumnPageGraphDataSet1 = new GuiLibGraphDataSet;
00676     runningColumnPageGraphDataSet2 = new GuiLibGraphDataSet;
00677     runningColumnPageGraphDataSet3 = new GuiLibGraphDataSet;
00678     runningColumnPageGraphDataSet4 = new GuiLibGraphDataSet;
00679 
00680     runningGasPageGraphCompleteProfileDataSet = new GuiLibGraphDataSet;
00681     runningGasPageGraphDataSet0 = new GuiLibGraphDataSet;
00682     runningGasPageGraphDataSet1 = new GuiLibGraphDataSet;
00683     runningGasPageGraphDataSet2 = new GuiLibGraphDataSet;
00684     runningGasPageGraphDataSet3 = new GuiLibGraphDataSet;
00685     runningGasPageGraphDataSet4 = new GuiLibGraphDataSet;
00686 
00687     injectorTempProfilePageGraphCompleteProfileDataSet = new GuiLibGraphDataSet;
00688     injectorTempProfilePageGraphDataSet0 = new GuiLibGraphDataSet;
00689     injectorTempProfilePageGraphDataSet1 = new GuiLibGraphDataSet;
00690     injectorTempProfilePageGraphDataSet2 = new GuiLibGraphDataSet;
00691     injectorTempProfilePageGraphDataSet3 = new GuiLibGraphDataSet;
00692     injectorTempProfilePageGraphDataSet4 = new GuiLibGraphDataSet;
00693 
00694     gasFlowProfilePageGraphCompleteProfileDataSet = new GuiLibGraphDataSet;
00695     gasFlowProfilePageGraphDataSet0 = new GuiLibGraphDataSet;
00696     gasFlowProfilePageGraphDataSet1 = new GuiLibGraphDataSet;
00697     gasFlowProfilePageGraphDataSet2 = new GuiLibGraphDataSet;
00698     gasFlowProfilePageGraphDataSet3 = new GuiLibGraphDataSet;
00699     gasFlowProfilePageGraphDataSet4 = new GuiLibGraphDataSet;
00700 
00701     // These are used in both column page graphs (conventional and DH)
00702     columnTempProfilePageGraphCompleteProfileDataSet = new GuiLibGraphDataSet;
00703     columnTempProfilePageGraphDataSet0 = new GuiLibGraphDataSet;
00704     columnTempProfilePageGraphDataSet1 = new GuiLibGraphDataSet;
00705     columnTempProfilePageGraphDataSet2 = new GuiLibGraphDataSet;
00706     columnTempProfilePageGraphDataSet3 = new GuiLibGraphDataSet;
00707     columnTempProfilePageGraphDataSet4 = new GuiLibGraphDataSet;
00708 
00709     runningInjectorPageGraphCompleteProfileDataSet = new GuiLibGraphDataSet;
00710     runningInjectorPageGraphDataSet0 = new GuiLibGraphDataSet;
00711     runningInjectorPageGraphDataSet1 = new GuiLibGraphDataSet;
00712     runningInjectorPageGraphDataSet2 = new GuiLibGraphDataSet;
00713     runningInjectorPageGraphDataSet3 = new GuiLibGraphDataSet;
00714     runningInjectorPageGraphDataSet4 = new GuiLibGraphDataSet;
00715 }
00716 
00717 
00718 /*
00719    Set up the graph on the Column temperature profile page from the method currently set up in the GC.
00720    
00721    Note that we always display this data as if the 'current time' were at the start of the method.
00722 */
00723 void GetGCStatusLoop::SetupColumnTempProfilePageGraphDataFromGC(void)
00724 {
00725     // We do not display 'columnTempProfilePageGraphCompleteProfileDataSet' in the graph - 
00726     // we use it to generate the datasets that we will display.
00727     // Use seconds as the dataset time units if the method duration is less than five minutes,
00728     // otherwise use minutes.
00729     columnTempProfilePageGraphCompleteProfileDataSet->SetupFromColumnTemperatureRampValues(usbDevice, usbHostGC);
00730     
00731     float totalColumnMethodTime = columnTempProfilePageGraphCompleteProfileDataSet->GetTotalMethodTime();
00732     
00733     TimeUnit timeUnit = MINUTES;
00734     float barInterval = graphBarIntervalMinutes;
00735     if(totalColumnMethodTime < methodTimeUnitsThreshold) {
00736         timeUnit = SECONDS;
00737         barInterval = graphBarIntervalSeconds;
00738     }
00739         
00740     // We do not display this graph while the method is running - so for the purposes of displaying the datasets in the graph, 
00741     // we say that 'now' is at the start of the method.
00742     
00743     // Dataset 0 is a line representing the temperature profile of the run from 'now' to the finish
00744     columnTempProfilePageGraphCompleteProfileDataSet->MakePartialCopy(0, totalColumnMethodTime, columnTempProfilePageGraphDataSet0);
00745     
00746     // Dataset 1 is a bar chart representing the temperature profile of the run from 'now' to the finish -
00747     // make sure that we have a bar at the exact end of the profile
00748     columnTempProfilePageGraphCompleteProfileDataSet->MakeInterpolatedPartialCopyWithFinalPoint(0, totalColumnMethodTime, barInterval, columnTempProfilePageGraphDataSet1);
00749     
00750     // Data set 2 is a line representing the temperature profile from the start to 'now'
00751     columnTempProfilePageGraphDataSet2->ClearData();
00752     
00753     // Data set 3 is a bar chart representing the temperature profile of the run from the start to 'now'
00754     columnTempProfilePageGraphDataSet3->ClearData();
00755     
00756     // Data set 4 is a single dot at the current time and temperature
00757     columnTempProfilePageGraphDataSet4->ClearData();
00758 #ifdef USING_DATASET_4_ON_NON_RUNNING_GRAPHS    
00759     GuiConst_INT32S startTemp = columnTempProfilePageGraphCompleteProfileDataSet->GetYCoordAtXCoord(0);
00760     columnTempProfilePageGraphDataSet4->AddDataPoint(0, startTemp);
00761 #endif // USING_DATASET_4_ON_NON_RUNNING_GRAPHS
00762     
00763     SetupColumnTempProfilePageXAxisLabel(timeUnit);
00764 }
00765 
00766 
00767 /*
00768    Set up the graph on the Injector temperature profile page from the method currently set up in the GC.
00769    
00770    Note that we always display this data as if the 'current time' were at the start of the method.
00771    
00772    Note also that the column method dataset must be setup before this function is called - i.e.
00773    SetupColumnTempProfilePageGraphDataFromGC must be called before this function 
00774    *****************************************************************************
00775    
00776    If not, this function will do nothing.
00777    
00778 */
00779 void GetGCStatusLoop::SetupInjectorTempProfilePageGraphDataFromGC(void)
00780 {
00781     // We do not display 'injectorTempProfilePageGraphCompleteProfileDataSet' in the graph - 
00782     // we use it to generate the datasets that we will display.
00783     
00784 #ifdef PTV_RAMPS_AVAILABLE
00785     float totalMethodTime;
00786 #else
00787     // Before setting up the injector temperature profile, we need a column method to base it on
00788     float totalMethodTime = columnTempProfilePageGraphCompleteProfileDataSet->GetTotalMethodTime();
00789     if(totalMethodTime <= 0.0f) {
00790         // Method not set up
00791         return;
00792     }
00793 #endif // PTV_RAMPS_AVAILABLE
00794 
00795     
00796 #ifdef PTV_RAMPS_AVAILABLE
00797     injectorTempProfilePageGraphCompleteProfileDataSet->SetupFromPTVTemperatureRampValues(usbDevice, usbHostGC);
00798     totalMethodTime = injectorTempProfilePageGraphCompleteProfileDataSet->GetTotalMethodTime();
00799 #else
00800     injectorTempProfilePageGraphCompleteProfileDataSet->SetupInjectorTemperatureProfileToMatchColumnMethod(usbDevice, usbHostGC, columnTempProfilePageGraphCompleteProfileDataSet);
00801 #endif // PTV_RAMPS_AVAILABLE
00802 
00803     TimeUnit timeUnit = MINUTES;
00804     float barInterval = graphBarIntervalMinutes;
00805     if(totalMethodTime < methodTimeUnitsThreshold) {
00806         timeUnit = SECONDS;
00807         barInterval = graphBarIntervalSeconds;
00808     }
00809         
00810     
00811     // We do not display this graph while the method is running - so for the purposes of displaying the datasets in the graph, 
00812     // we say that 'now' is at the start of the method.
00813     
00814     // Dataset 0 is a line representing the temperature profile of the run from 'now' to the finish
00815     injectorTempProfilePageGraphCompleteProfileDataSet->MakePartialCopy(0, totalMethodTime, injectorTempProfilePageGraphDataSet0);
00816     
00817     // Dataset 1 is a bar chart representing the temperature profile of the run from 'now' to the finish
00818     injectorTempProfilePageGraphCompleteProfileDataSet->MakeInterpolatedPartialCopyWithFinalPoint(0, totalMethodTime, barInterval, injectorTempProfilePageGraphDataSet1);
00819     
00820     // Data set 2 is a line representing the temperature profile from the start to 'now'
00821     injectorTempProfilePageGraphDataSet2->ClearData();
00822     
00823     // Data set 3 is a bar chart representing the temperature profile of the run from the start to 'now'
00824     injectorTempProfilePageGraphDataSet3->ClearData();
00825     
00826     // Data set 4 is a single dot at the current time and temperature
00827     injectorTempProfilePageGraphDataSet4->ClearData();
00828 #ifdef USING_DATASET_4_ON_NON_RUNNING_GRAPHS    
00829     float startTemp = injectorTempProfilePageGraphCompleteProfileDataSet->GetYCoordAtXCoord(0);
00830     injectorTempProfilePageGraphDataSet4->AddDataPoint(0, startTemp);
00831 #endif // USING_DATASET_4_ON_NON_RUNNING_GRAPHS
00832 
00833     SetupInjectorTempProfilePageXAxisLabel(timeUnit);
00834 }
00835 
00836 
00837 /*
00838    Set up the graph on the Gas flow profile page from the method currently set up in the GC.
00839    
00840    Note that we always display this data as if the 'current time' were at the start of the method.
00841    
00842    Note also that the column method must be set up before calling this function.
00843 */
00844 void GetGCStatusLoop::SetupGasFlowProfilePageGraphDataFromGC(void)
00845 {
00846     // Before setting up the gas pressure profile, we need a column method to base it on
00847     float totalColumnMethodTime = columnTempProfilePageGraphCompleteProfileDataSet->GetTotalMethodTime();
00848     if(totalColumnMethodTime <= 0.0f) {
00849         // Column method not set up
00850         return;
00851     }
00852     
00853     // We do not display 'gasFlowProfilePageGraphCompleteProfileDataSet' in the graph - 
00854     // we use it to generate the datasets that we will display.
00855     gasFlowProfilePageGraphCompleteProfileDataSet->SetupGasPressureProfileWithTimingsFromColumnMethod(usbDevice, usbHostGC, columnTempProfilePageGraphCompleteProfileDataSet);
00856     
00857     float totalGasFlowMethodTime = gasFlowProfilePageGraphCompleteProfileDataSet->GetTotalMethodTime();
00858     
00859     TimeUnit timeUnit = MINUTES;
00860     float barInterval = graphBarIntervalMinutes;
00861     if(totalGasFlowMethodTime < methodTimeUnitsThreshold) {
00862         timeUnit = SECONDS;
00863         barInterval = graphBarIntervalSeconds;
00864     }
00865     
00866         
00867     // We do not display this graph while the method is running - so for the purposes of displaying the datasets in the graph, 
00868     // we say that 'now' is at the start of the method.
00869     
00870     // Dataset 0 is a line representing the pressure profile of the run from 'now' to the finish
00871     gasFlowProfilePageGraphCompleteProfileDataSet->MakePartialCopy(0, totalGasFlowMethodTime, gasFlowProfilePageGraphDataSet0);
00872     
00873     // Dataset 1 is a bar chart representing the pressure profile of the run from 'now' to the finish
00874     gasFlowProfilePageGraphCompleteProfileDataSet->MakeInterpolatedPartialCopyWithFinalPoint(0, totalGasFlowMethodTime, barInterval, gasFlowProfilePageGraphDataSet1);
00875     
00876     // Data set 2 is a line representing the pressure profile from the start to 'now'
00877     gasFlowProfilePageGraphDataSet2->ClearData();
00878     
00879     // Data set 3 is a bar chart representing the pressure profile of the run from the start to 'now'
00880     gasFlowProfilePageGraphDataSet3->ClearData();
00881     
00882     // Data set 4 is a single dot at the current time and pressure 
00883     gasFlowProfilePageGraphDataSet4->ClearData();
00884 #ifdef USING_DATASET_4_ON_NON_RUNNING_GRAPHS    
00885     GuiConst_INT32S startTemp = gasFlowProfilePageGraphCompleteProfileDataSet->GetYCoordAtXCoord(0);
00886     gasFlowProfilePageGraphDataSet4->AddDataPoint(0, startTemp);
00887 #endif // USING_DATASET_4_ON_NON_RUNNING_GRAPHS
00888 
00889     SetupGasFlowProfilePageXAxisLabel(timeUnit);
00890 }
00891 
00892 
00893 /*
00894     Set up the complete temperature profile dataset for the graph on the 'running column' page
00895     to match the method data currently set up in the GC.
00896     
00897     We assume that we will not have to do this again while the current method is running.
00898 */
00899 void GetGCStatusLoop::SetupRunningColumnPageGraphCompleteProfileDataSetFromGC(void)
00900 {
00901     // Use seconds as the dataset time units if the method duration is less than five minutes,
00902     // otherwise use minutes
00903     runningColumnPageGraphCompleteProfileDataSet->SetupFromColumnTemperatureRampValues(usbDevice, usbHostGC);
00904     
00905     runningColumnPageGraph->SetXAxisRange(0, (GuiConst_INT32S) runningColumnPageGraphCompleteProfileDataSet->GetTotalMethodTime());
00906 
00907     if(runningColumnPageGraphCompleteProfileDataSet->GetTotalMethodTime() < methodTimeUnitsThreshold) {
00908         strcpy(GuiVar_runningColumnPageXAxisLabel, "Time (seconds)");
00909     } else {
00910         strcpy(GuiVar_runningColumnPageXAxisLabel, "Time (minutes)");
00911     }
00912 }
00913 
00914 /*
00915     Update the partial datasets for the temperature profile graph on the 'running column' page
00916     (i.e. the datasets that split up the profile into the section from the start to 'now'
00917     and the section from 'now' to the end) to match (a) the complete profile obtained from the GC,
00918     and (b) the current time.
00919     
00920     Caller *must* have previously called the 'SetupRunningColumnPageGraphCompleteProfileDataSetFromGC' function.
00921     
00922     Returns true if the datasets have changed, false if not
00923 */
00924 bool GetGCStatusLoop::SetupRunningColumnPageGraphPartialDataSetsToMatchCurrentRunTime(bool runHasCompleted)
00925 {
00926     if(columnMethodFinished) {
00927         return false; // Our work here is done...
00928     }
00929     
00930     float currentColumnMethodRunTime;
00931     GetRunTime(&currentColumnMethodRunTime);
00932 
00933     
00934     float totalColumnMethodMethodTime = runningColumnPageGraphCompleteProfileDataSet->GetTotalMethodTime();
00935     
00936     float barInterval = graphBarIntervalMinutes;
00937     if(totalColumnMethodMethodTime < methodTimeUnitsThreshold) {
00938         barInterval = graphBarIntervalSeconds;
00939     } else {
00940         // We are using minutes - work in whole minutes, and show the number we have completed 
00941         // (i.e. do not round the number up)
00942         currentColumnMethodRunTime = (float)floor((double)currentColumnMethodRunTime);
00943     }
00944     
00945     if(runHasCompleted) {
00946         // Make sure we show this
00947         currentColumnMethodRunTime = totalColumnMethodMethodTime;
00948     }
00949     
00950 #ifdef USING_DATASET_4 
00951     if((currentColumnMethodRunTime < (previousColumnMethodRunTime + barInterval)) && (!runHasCompleted)) { // Must show the run has completed
00952         // No need to update data sets (there will be no changes)
00953         return false;
00954     }
00955 #endif // USING_DATASET_4 - else update the graph continuously
00956     // 'else'...
00957     previousColumnMethodRunTime = currentColumnMethodRunTime;
00958 
00959     // Stop adding points when the current method has 'expired'
00960     if(currentColumnMethodRunTime < totalColumnMethodMethodTime) {    
00961         // Dataset 0 is a line representing the temperature profile of the run from 'now' to the finish
00962         runningColumnPageGraphCompleteProfileDataSet->MakePartialCopy(currentColumnMethodRunTime, totalColumnMethodMethodTime, runningColumnPageGraphDataSet0);
00963             
00964         // Dataset 1 is a bar chart representing the temperature profile of the run from 'now' to the finish - 
00965         // make sure that we have a bar at the exact end of the profile
00966         runningColumnPageGraphCompleteProfileDataSet->MakeInterpolatedPartialCopyWithFinalPoint(currentColumnMethodRunTime + barInterval, totalColumnMethodMethodTime, barInterval, runningColumnPageGraphDataSet1);
00967         // 'currentTime + barInterval' to prevent overlap with dataset 3
00968                 
00969         // Data set 2 is a line representing the temperature profile from the start to 'now'
00970         runningColumnPageGraphCompleteProfileDataSet->MakePartialCopy(0, currentColumnMethodRunTime, runningColumnPageGraphDataSet2);
00971         
00972         // Data set 3 is a bar chart representing the temperature profile of the run from the start to 'now'
00973         runningColumnPageGraphCompleteProfileDataSet->MakeInterpolatedPartialCopy(0, currentColumnMethodRunTime, barInterval, runningColumnPageGraphDataSet3);
00974         
00975 #ifdef USING_DATASET_4 
00976         // Data set 4 is a single dot at the current time and temperature
00977         runningColumnPageGraphDataSet4->ClearData();
00978         GuiConst_INT32S currentTemp = runningColumnPageGraphCompleteProfileDataSet->GetYCoordAtXCoord(currentColumnMethodRunTime);
00979         runningColumnPageGraphDataSet4->AddDataPoint(currentColumnMethodRunTime, currentTemp);
00980 #endif // USING_DATASET_4
00981     } else {
00982         
00983         columnMethodFinished = true;
00984 
00985         // Do not leave data 'lying around' in the 'now' to the finish datasets
00986         // after we have completed the method
00987         runningColumnPageGraphDataSet0->ClearData();
00988         runningColumnPageGraphDataSet1->ClearData();
00989                 
00990         // Data set 2 is a line representing the temperature profile from the start to 'now'
00991         runningColumnPageGraphCompleteProfileDataSet->MakePartialCopy(0, totalColumnMethodMethodTime, runningColumnPageGraphDataSet2);
00992         
00993         // Data set 3 is a bar chart representing the temperature profile of the run from the start to 'now' - 
00994         // make sure that we have a bar at the exact end of the profile
00995         runningColumnPageGraphCompleteProfileDataSet->MakeInterpolatedPartialCopyWithFinalPoint(0, totalColumnMethodMethodTime, barInterval, runningColumnPageGraphDataSet3);
00996         
00997 #ifdef USING_DATASET_4 
00998         // Data set 4 is a single dot at the current time and temperature
00999         runningColumnPageGraphDataSet4->ClearData();
01000         GuiConst_INT32S currentTemp = runningColumnPageGraphCompleteProfileDataSet->GetYCoordAtXCoord(totalColumnMethodMethodTime);
01001         runningColumnPageGraphDataSet4->AddDataPoint(totalColumnMethodMethodTime, currentTemp);
01002 #endif // USING_DATASET_4
01003     }
01004 
01005     return true;
01006 }
01007 
01008 /*
01009     Set up the complete gas flow profile dataset for the graph on the 'running gas' page
01010     to match the method data currently set up in the GC.
01011     
01012     We assume that we will not have to do this again while the current method is running.
01013 */
01014 void GetGCStatusLoop::SetupRunningGasPageGraphCompleteProfileDataSetFromGC(void)
01015 {
01016     // Before setting up the gas pressure profile, we need a column method to base it on
01017     float totalColumnMethodTime = runningColumnPageGraphCompleteProfileDataSet->GetTotalMethodTime();
01018     if(totalColumnMethodTime <= 0.0f) {
01019         // Column method not set up
01020         return;
01021     }
01022     
01023     runningGasPageGraphCompleteProfileDataSet->SetupGasPressureProfileWithTimingsFromColumnMethod(usbDevice, usbHostGC, runningColumnPageGraphCompleteProfileDataSet);
01024         
01025     runningGasPageGraph->SetXAxisRange(0, (GuiConst_INT32S) runningGasPageGraphCompleteProfileDataSet->GetTotalMethodTime());
01026     
01027     if(runningGasPageGraphCompleteProfileDataSet->GetTotalMethodTime() < methodTimeUnitsThreshold) {
01028         strcpy(GuiVar_runningGasPageXAxisLabel, "Time (seconds)");
01029     } else {
01030         strcpy(GuiVar_runningGasPageXAxisLabel, "Time (minutes)");
01031     }
01032 }
01033 
01034 /*
01035     Update the partial datasets for the gas flow profile graph on the 'running gas' page
01036     (i.e. the datasets that split up the profile into the section from the start to 'now'
01037     and the section from 'now' to the end) to match (a) the complete profile obtained from the GC,
01038     and (b) the current time.
01039     
01040     Caller *must* have previously called the 'SetupRunningGasPageGraphCompleteProfileDataSetFromGC' function.
01041     
01042     Returns true if the datasets have changed, false if not
01043 */
01044 bool GetGCStatusLoop::SetupRunningGasPageGraphPartialDataSetsToMatchCurrentRunTime(bool runHasCompleted)
01045 {
01046     if(gasMethodFinished) {
01047         return false; // Our work here is done...
01048     }
01049 
01050     float currentGasMethodRunTime;
01051     GetRunTime(&currentGasMethodRunTime);
01052 
01053     float totalGasMethodTime = runningGasPageGraphCompleteProfileDataSet->GetTotalMethodTime();
01054     
01055     float barInterval = graphBarIntervalMinutes;
01056     if(totalGasMethodTime < methodTimeUnitsThreshold) {
01057         barInterval = graphBarIntervalSeconds;
01058     } else {
01059         // We are using minutes - work in whole minutes, and show the number we have completed 
01060         // (i.e. do not round the number up)
01061         currentGasMethodRunTime = (float)floor((double)currentGasMethodRunTime);
01062     }
01063     
01064     if(runHasCompleted) {
01065         // Make sure we show this
01066         currentGasMethodRunTime = totalGasMethodTime;
01067     }
01068         
01069 #ifdef USING_DATASET_4 
01070     if((currentGasMethodRunTime < (previousGasMethodRunTime + barInterval)) && (!runHasCompleted)) { // Must show the run has completed
01071         // No need to update data sets (there will be no changes)
01072         return false;
01073     }
01074 #endif // USING_DATASET_4 - else update the graph continuously
01075     // 'else'...
01076     previousGasMethodRunTime = currentGasMethodRunTime;
01077 
01078     // Stop adding points when the current method has 'expired'
01079     if(currentGasMethodRunTime < totalGasMethodTime) {
01080         // Dataset 0 is a line representing the flow profile of the run from 'now' to the finish
01081         runningGasPageGraphCompleteProfileDataSet->MakePartialCopy(currentGasMethodRunTime, totalGasMethodTime, runningGasPageGraphDataSet0);
01082             
01083         // Dataset 1 is a bar chart representing the flow profile of the run from 'now' to the finish -
01084         // make sure that we have a bar at the exact end of the profile
01085         runningGasPageGraphCompleteProfileDataSet->MakeInterpolatedPartialCopyWithFinalPoint(currentGasMethodRunTime + barInterval, totalGasMethodTime, barInterval, runningGasPageGraphDataSet1);
01086         // 'currentTime + barInterval' to prevent overlap with dataset 3
01087                 
01088         // Data set 2 is a line representing the flow profile from the start to 'now'
01089         runningGasPageGraphCompleteProfileDataSet->MakePartialCopy(0, currentGasMethodRunTime, runningGasPageGraphDataSet2);
01090         
01091         // Data set 3 is a bar chart representing the flow profile from the start to 'now'
01092         runningGasPageGraphCompleteProfileDataSet->MakeInterpolatedPartialCopy(0, currentGasMethodRunTime, barInterval, runningGasPageGraphDataSet3);
01093         
01094 #ifdef USING_DATASET_4 
01095         // Data set 4 is a single dot at the current time and flow rate
01096         runningGasPageGraphDataSet4->ClearData();
01097         GuiConst_INT32S currentFlowRate = runningGasPageGraphCompleteProfileDataSet->GetYCoordAtXCoord(currentGasMethodRunTime);
01098         runningGasPageGraphDataSet4->AddDataPoint(currentGasMethodRunTime, currentFlowRate);
01099 #endif // USING_DATASET_4 
01100     } else {
01101 
01102         gasMethodFinished = true;
01103 
01104         // Do not leave data 'lying around' in the 'now' to the finish datasets
01105         // after we have completed the method
01106         runningGasPageGraphDataSet0->ClearData();
01107         runningGasPageGraphDataSet1->ClearData();
01108         
01109         // Data set 2 is a line representing the flow profile from the start to 'now'
01110         runningGasPageGraphCompleteProfileDataSet->MakePartialCopy(0, currentGasMethodRunTime, runningGasPageGraphDataSet2);
01111         
01112         // Data set 3 is a bar chart representing the flow profile from the start to 'now'- 
01113         // make sure that we have a bar at the exact end of the profile
01114         runningGasPageGraphCompleteProfileDataSet->MakeInterpolatedPartialCopyWithFinalPoint(0, currentGasMethodRunTime, barInterval, runningGasPageGraphDataSet3);
01115         
01116 #ifdef USING_DATASET_4
01117         // Data set 4 is a single dot at the current time and flow rate
01118         runningGasPageGraphDataSet4->ClearData();
01119         GuiConst_INT32S currentFlowRate = runningGasPageGraphCompleteProfileDataSet->GetYCoordAtXCoord(currentGasMethodRunTime);
01120         runningGasPageGraphDataSet4->AddDataPoint(currentGasMethodRunTime, currentFlowRate);
01121 #endif // USING_DATASET_4 
01122     }
01123 
01124     return true;
01125 }
01126     
01127 /*
01128     Set up the complete injector temperature profile dataset for the graph on the second injector status
01129     (while running the method) page, to match the method data currently set up in the GC.
01130     
01131     We assume that we will not have to do this again while the current method is running.
01132 */
01133 void GetGCStatusLoop::SetupRunningInjectorPageGraphCompleteProfileDataSetFromGC(void)
01134 {
01135 #ifndef PTV_RAMPS_AVAILABLE
01136     // If we do not have PTV ramps, then before we can set up the injector profile, we need a column method to base it on
01137     if(runningColumnPageGraphCompleteProfileDataSet->GetTotalMethodTime() <= 0.0f) {
01138         // Column method not set up
01139         return;
01140     }
01141 #endif // PTV_RAMPS_AVAILABLE
01142     
01143 #ifdef PTV_RAMPS_AVAILABLE
01144     runningInjectorPageGraphCompleteProfileDataSet->SetupFromPTVTemperatureRampValues(usbDevice, usbHostGC);
01145 #else
01146     runningInjectorPageGraphCompleteProfileDataSet->SetupInjectorTemperatureProfileToMatchColumnMethod(usbDevice, usbHostGC, runningColumnPageGraphCompleteProfileDataSet);
01147 #endif // PTV_RAMPS_AVAILABLE
01148    
01149     runningInjectorPageGraph->SetXAxisRange(0, (GuiConst_INT32S) runningInjectorPageGraphCompleteProfileDataSet->GetTotalMethodTime());
01150     
01151     if(runningInjectorPageGraphCompleteProfileDataSet->GetTotalMethodTime() < methodTimeUnitsThreshold) {
01152         strcpy(GuiVar_runningInjectorPageXAxisLabel, "Time (seconds)");
01153     } else {
01154         strcpy(GuiVar_runningInjectorPageXAxisLabel, "Time (minutes)");
01155     }
01156 }
01157 
01158 /*
01159     Update the partial datasets for the injector temperature profile graph on the 'running injector' page
01160     (i.e. the datasets that split up the profile into the section from the start to 'now'
01161     and the section from 'now' to the end) to match (a) the complete profile obtained from the GC,
01162     and (b) the current time.
01163     
01164     Caller *must* have previously called the 'SetupRunningInjectorPageGraphCompleteProfileDataSetFromGC' function.
01165     
01166     Returns true if the datasets have changed, false if not
01167 */
01168 bool GetGCStatusLoop::SetupRunningInjectorPageGraphPartialDataSetsToMatchCurrentRunTime(bool runHasCompleted)
01169 {
01170     if(injectorMethodFinished) {
01171         return false; // Our work here is done...
01172     }
01173 
01174     float currentInjectorMethodRunTime;
01175     GetRunTime(&currentInjectorMethodRunTime);
01176 
01177     float totalInjectorMethodTime = runningInjectorPageGraphCompleteProfileDataSet->GetTotalMethodTime();
01178     
01179     float barInterval = graphBarIntervalMinutes;
01180     if(totalInjectorMethodTime < methodTimeUnitsThreshold) {
01181         barInterval = graphBarIntervalSeconds;
01182     } else {
01183         // We are using minutes - work in whole minutes, and show the number we have completed 
01184         // (i.e. do not round the number up) - but leave the value unchanged if the injector method has completed - 
01185         // otherwise we will omit the final point/bar if the total time is not a whole number of minutes
01186         if(currentInjectorMethodRunTime < totalInjectorMethodTime) {
01187             currentInjectorMethodRunTime = (float)floor((double)currentInjectorMethodRunTime);
01188         }
01189     }
01190     
01191     if(runHasCompleted) {
01192         // Make sure we show this - but be careful of the fact that the injector method time
01193         // is not necessarily equal to the column method time - and currently the run completes
01194         // at the end of the column method time
01195         if(totalInjectorMethodTime == runningColumnPageGraphCompleteProfileDataSet->GetTotalMethodTime()) {
01196             currentInjectorMethodRunTime = totalInjectorMethodTime;
01197         }
01198         // If the time (i.e. duration) of the injector method is less than the column method,
01199         // we should already have shown it as completed before the column method (and therefore 
01200         // the run as a whole) completes. If it is greater, then it will not have completed 
01201         // by the time the column method completes - and therefore we should not show it 
01202         // as complete at that point (and without the 'if' around the assignment above, 
01203         // that is what we will do)
01204     }
01205         
01206 #ifdef USING_DATASET_4 
01207     if((currentInjectorMethodRunTime < (previousInjectorMethodRunTime + barInterval)) && (!runHasCompleted)) { // Must show the run has completed
01208         // No need to update data sets (there will be no changes)
01209         return false;
01210     }
01211 #endif // USING_DATASET_4 - else update the graph continuously
01212     // 'else'...
01213     previousInjectorMethodRunTime = currentInjectorMethodRunTime;
01214 
01215     // Stop adding points when the current method has 'expired'
01216     if(currentInjectorMethodRunTime < totalInjectorMethodTime) {
01217         // Dataset 0 is a line representing the flow profile of the run from 'now' to the finish
01218         runningInjectorPageGraphCompleteProfileDataSet->MakePartialCopy(currentInjectorMethodRunTime, totalInjectorMethodTime, runningInjectorPageGraphDataSet0);
01219             
01220         // Dataset 1 is a bar chart representing the flow profile of the run from 'now' to the finish -
01221         // make sure that we have a bar at the exact end of the profile
01222         runningInjectorPageGraphCompleteProfileDataSet->MakeInterpolatedPartialCopyWithFinalPoint(currentInjectorMethodRunTime + barInterval, totalInjectorMethodTime, barInterval, runningInjectorPageGraphDataSet1);
01223         // 'currentTime + barInterval' to prevent overlap with dataset 3
01224                 
01225         // Data set 2 is a line representing the flow profile from the start to 'now'
01226         runningInjectorPageGraphCompleteProfileDataSet->MakePartialCopy(0, currentInjectorMethodRunTime, runningInjectorPageGraphDataSet2);
01227         
01228         // Data set 3 is a bar chart representing the flow profile from the start to 'now'
01229         runningInjectorPageGraphCompleteProfileDataSet->MakeInterpolatedPartialCopy(0, currentInjectorMethodRunTime, barInterval, runningInjectorPageGraphDataSet3);
01230         
01231 #ifdef USING_DATASET_4 
01232         // Data set 4 is a single dot at the current time and flow rate
01233         runningInjectorPageGraphDataSet4->ClearData();
01234         GuiConst_INT32S currentFlowRate = runningInjectorPageGraphCompleteProfileDataSet->GetYCoordAtXCoord(currentGasMethodRunTime);
01235         runningGasPageGraphDataSet4->AddDataPoint(currentGasMethodRunTime, currentFlowRate);
01236 #endif // USING_DATASET_4 
01237     } else {
01238 
01239         injectorMethodFinished = true;
01240 
01241         // Remember that it is the *column* method that controls how long the GC runs, *not* the injector method -
01242         // and they do not have to have the same length (i.e. duration). Therefore, the GC may still be running 
01243         // after the injector method has finished - and we do not want to display spurious bars off the right hand end of our graph
01244         if(currentInjectorMethodRunTime > totalInjectorMethodTime) {
01245             currentInjectorMethodRunTime = totalInjectorMethodTime;
01246         }
01247 
01248         // Do not leave data 'lying around' in the 'now' to the finish datasets
01249         // after we have completed the method
01250         runningInjectorPageGraphDataSet0->ClearData();
01251         runningInjectorPageGraphDataSet1->ClearData();
01252         
01253         // Data set 2 is a line representing the flow profile from the start to 'now'
01254         runningInjectorPageGraphCompleteProfileDataSet->MakePartialCopy(0, currentInjectorMethodRunTime, runningInjectorPageGraphDataSet2);
01255         
01256         // Data set 3 is a bar chart representing the flow profile from the start to 'now'- 
01257         // make sure that we have a bar at the exact end of the profile
01258         runningInjectorPageGraphCompleteProfileDataSet->MakeInterpolatedPartialCopyWithFinalPoint(0, currentInjectorMethodRunTime, barInterval, runningInjectorPageGraphDataSet3);
01259         
01260 #ifdef USING_DATASET_4
01261         // Data set 4 is a single dot at the current time and flow rate
01262         runningInjectorPageGraphDataSet4->ClearData();
01263         GuiConst_INT32S currentFlowRate = runningInjectorPageGraphCompleteProfileDataSet->GetYCoordAtXCoord(currentInjectorMethodRunTime);
01264         runningInjectorPageGraphDataSet4->AddDataPoint(currentInjectorMethodRunTime, currentFlowRate);
01265 #endif // USING_DATASET_4 
01266     }
01267 
01268     return true;
01269 }
01270     
01271 /*
01272     Update all the 'complete running profile' datasets from the GC.
01273     
01274     We will want to do this, for example, when we start the GC running.
01275 */
01276 void GetGCStatusLoop::UpdateGCMethodRunningProfiles(void)
01277 {
01278     SetupRunningColumnPageGraphCompleteProfileDataSetFromGC();
01279     SetupRunningGasPageGraphCompleteProfileDataSetFromGC();
01280     SetupRunningInjectorPageGraphCompleteProfileDataSetFromGC();
01281 }
01282 
01283 
01284 /*
01285     Caller is telling us "the GC has started running".
01286 */
01287 void GetGCStatusLoop::SetGCIsRunning(void)
01288 {
01289     previousColumnMethodRunTime = -99.0f;
01290     columnMethodFinished = false;
01291     
01292     previousGasMethodRunTime = -99.0f;
01293     gasMethodFinished = false;
01294     
01295     previousInjectorMethodRunTime = -99.0f;
01296     injectorMethodFinished = false;
01297     
01298     realGCIsRunning = true;
01299     
01300     runWasAborted = false;
01301 }
01302 
01303 /*
01304     Caller is telling us "the GC has stopped running".
01305 */
01306 void GetGCStatusLoop::ClearGCIsRunning(void)
01307 {    
01308     realGCIsRunning = false;
01309 }
01310 
01311 
01312 /*
01313     Tell the caller whether or not we 'think' the GC is running
01314 */
01315 bool GetGCStatusLoop::GetGCIsRunningFlag(void)
01316 {    
01317     return realGCIsRunning;
01318 }
01319 
01320 
01321 /*
01322     Returns the current page selection to the caller.
01323 */
01324 GuiConst_INT16U GetGCStatusLoop::GetCurrentPage(void)
01325 {
01326     return currentPage;
01327 }
01328 
01329 /*
01330     Allows the caller to set the current page. As well as setting the page number,
01331     we may need to take other action - e.g. displaying the data for that page.
01332     
01333     Args: new page (easyGUI "structure") number 
01334     
01335     No return code.
01336 */
01337 void GetGCStatusLoop::SetCurrentPage(GuiConst_INT16U newCurrentPage)
01338 {
01339     if(currentPage != newCurrentPage) {
01340         currentPage = newCurrentPage;
01341         
01342         pageJustChanged = true; // Try this - can it prevent crashes on updating?
01343         
01344         needToUpdateProfileGraphs = true;
01345         
01346 //#define IS_THIS_NECESSARY_NOW
01347 #ifdef IS_THIS_NECESSARY_NOW
01348         // Stop the status rectangles flashing when we display these pages/structures
01349         if((currentPage != GuiStruct_HomePage_1) &&
01350            (currentPage != GuiStruct_ColumnPage1_2) &&
01351            (currentPage != GuiStruct_ColumnPage2_9) &&
01352            (currentPage != GuiStruct_ColumnMethodPage_Def) &&
01353            (currentPage != GuiStruct_ColumnTempProfilePage_60) &&
01354            (currentPage != GuiStruct_InjectorPage1_3) &&
01355            (currentPage != GuiStruct_InjectorTempProfilePage_25) &&
01356            (currentPage != GuiStruct_InjectorGasStatusPage_30) &&
01357            (currentPage != GuiStruct_InjectorConsumablesPage_20) &&
01358            (currentPage != GuiStruct_DetectorFIDPage_4) &&
01359            (currentPage != GuiStruct_DetectorECDPage_12) &&
01360            (currentPage != GuiStruct_DetectorFPDPage_14) &&
01361            (currentPage != GuiStruct_DetectorTCDPage_11) &&
01362            (currentPage != GuiStruct_DetectorNPDPage_28) &&
01363            (currentPage != GuiStruct_DetectorNonePage_31) &&
01364            (currentPage != GuiStruct_DetectorPIDPage_29) &&
01365            (currentPage != GuiStruct_DetectorSPDIDPage_30) &&
01366            (currentPage != GuiStruct_DetectorTXLPage_27) &&
01367            (currentPage != GuiStruct_GasProfilePage_15) &&
01368            (currentPage != GuiStruct_GasInformationPage_6) && 
01369            (currentPage != GuiStruct_GasCalibrationPage_Def) && 
01370            (currentPage != GuiStruct_GasBackPressureDACPage_Def) && 
01371            (currentPage != GuiStruct_GasChannelDACAndADCPage_Def) && 
01372            (currentPage != GuiStruct_RunningDetectorPage_27)) { // It also causes flickering if we display the 'Running Detector' page here
01373             
01374             DisplayCurrentPageData(true);
01375         }
01376 #undef IS_THIS_NECESSARY_NOW
01377 #endif // IS_THIS_NECESSARY_NOW
01378         // Instead of the above, this is all that is necessary -
01379         // without these calls, the graphs on these pages are not displayed
01380         // when the page first appears
01381         if(currentPage == GuiStruct_RunningColumnPage_25) {
01382             DisplayRunningColumnPageData(true, false);
01383         } else if (currentPage == GuiStruct_RunningGasPage_28) {
01384             DisplayRunningGasPageData(true, false);
01385         } else if (currentPage == GuiStruct_RunningInjectorProfilePage_Def) {
01386             DisplayRunningInjectorProfilePageData(true, false);
01387         }
01388     }
01389 }
01390 
01391 /*
01392     Allows the caller to tell us which component status colour areas to use for the home page.
01393     
01394     Args: pointer to the new component status colour areas for the home page
01395     
01396     No return code.
01397 */
01398 void GetGCStatusLoop::SetHomePageGCComponentStatusColorAreas(HomePageGCComponentStatusColorAreas* newColorAreas)
01399 {
01400     homePageGCComponentStatusColorAreas = newColorAreas;
01401     
01402     UpdateHomePageGCComponentStatusColorAreas();
01403 }
01404 
01405 /*
01406     Allows the caller to tell us which component status colour areas to use for the single component pages
01407     (e.g. column, detector, etc).
01408     
01409     Args: pointer to the new component status colour areas for the single component pages
01410     
01411     No return code.
01412 */
01413 void GetGCStatusLoop::SetSingleGCComponentPageStatusColorAreas(SingleGCComponentPageStatusColorAreas* newColorAreas)
01414 {
01415     singleGCComponentPageStatusColorAreas = newColorAreas;
01416     
01417     UpdateSingleGCComponentPageStatusColorArea(COLUMN);
01418     UpdateSingleGCComponentPageStatusColorArea(INJECTOR);
01419     UpdateSingleGCComponentPageStatusColorArea(DETECTOR);
01420     UpdateSingleGCComponentPageStatusColorArea(GAS);
01421 
01422 }
01423 
01424 
01425 /*
01426     Displays the specified text at the specified location, with black text on a white background.
01427     
01428     Args: pointer to the null-terminated string to display
01429           x coordinate
01430           y coordinate
01431           
01432     No return code.
01433 */
01434 void GetGCStatusLoop::DisplayText(char *text, short X, short Y, GuiConst_INT8U alignment, GuiConst_INT16U fontNo, GuiConst_INTCOLOR foreColor)
01435 {
01436     GuiLib_DrawStr(
01437         X,                      //GuiConst_INT16S X,
01438         Y,                      //GuiConst_INT16S Y,
01439         fontNo,                 //GuiConst_INT16U FontNo,
01440         text,                   //GuiConst_TEXT PrefixLocate *String,
01441         alignment,              //GuiConst_INT8U Alignment, 
01442         GuiLib_PS_ON,           //GuiConst_INT8U PsWriting,
01443         GuiLib_TRANSPARENT_ON,  //GuiConst_INT8U Transparent,
01444         GuiLib_UNDERLINE_OFF,   //GuiConst_INT8U Underlining,
01445         0,                      //GuiConst_INT16S BackBoxSizeX,
01446         0,                      //GuiConst_INT16S BackBoxSizeY1,
01447         0,                      //GuiConst_INT16S BackBoxSizeY2,
01448         GuiLib_BBP_NONE,        //GuiConst_INT8U BackBorderPixels,
01449         foreColor,              //GuiConst_INTCOLOR ForeColor,
01450         0xFFFF                  //GuiConst_INTCOLOR BackColor
01451     ); 
01452 }
01453 
01454 /*
01455     Sends a command (known as a 'report') to the GC, and returns the response.
01456     
01457     Args: pointer to (null-terminated) command to use
01458           pointer to buffer to contain the (also null-terminated) response
01459           
01460     No return code.
01461 */
01462 void GetGCStatusLoop::SetGCDeviceReport(char *cmd, char *response)
01463 {
01464 #define USE_GC_UTILS // Testing new class
01465 #ifdef USE_GC_UTILS
01466     USBHostGCUtilities::SendCommandToGCAndGetResponse(usbDevice, usbHostGC, cmd, response);
01467 #else
01468     // Guard against simultaneous calls to usbHostGC->SetDeviceReport - 
01469     // it is not re-entrant (and nor is the GC)
01470     while(usbHostGC->ExecutingSetDeviceReport()) {}
01471 
01472     usbHostGC->SetDeviceReport(usbDevice, cmd, response);
01473 #endif // USE_GC_UTILS
01474 }
01475     
01476 /*
01477     Executes a GC command that returns simply "DACK" if successful,
01478     "DNAK" or "EPKT" if failure.
01479     
01480     Args: a pointer to the command in question, as a null terminated string
01481     
01482     Returns true if the GC returned "DACK", false for anything else
01483 */
01484 bool GetGCStatusLoop::ExecuteCommandWithDACKResponse(char *cmd)
01485 {
01486 #define USE_GC_UTILS // Testing new class
01487 #ifdef USE_GC_UTILS
01488     return USBHostGCUtilities::SendCommandToGCWithDACKResponse(usbDevice, usbHostGC, cmd);
01489 #else
01490     while(usbHostGC->ExecutingSetDeviceReport()) {}
01491 
01492     char response[50];
01493     usbHostGC->SetDeviceReport(usbDevice, cmd, response);
01494     // We expect a response like this: "DACK" for success, "DNAK" for failure, "EPKT" for error
01495     
01496 #define DEBUG_HERE
01497 #ifdef DEBUG_HERE
01498     char dbg[100];
01499     sprintf(dbg, "ECWDKR - %s returned %s", cmd, response);
01500     EasyGUIDebugPrint(dbg, 0, 15);   
01501 #undef DEBUG_HERE
01502 #endif
01503     
01504     return (response[1] == 'A');
01505 #endif // USE_GC_UTILS
01506 }
01507 
01508 /*
01509     The commands to get the GC status ("QSTA") and its fault state ("QFLT") are very similar,
01510     in that they both return a response of the same form, with the (integer) status/fault value
01511     as the final two digits. This function handles both commands, returning the status/fault code
01512     as an integer.
01513     
01514     Args: a pointer to the null-terminated command string to be passed to the GC
01515          (note that this function does not check that this is either "QSTA" or "QFLT" -
01516           this is up to the caller)
01517               
01518     Obtains the status/fault code from the GC, and returns it as an integer. 
01519     NOTE: returns -1 if there is an error. This is *not* a valid GC status/fault code,
01520     and the caller must check for it.
01521 */
01522 int GetGCStatusLoop::GetGCStatusOrFaultCode(char *cmd)
01523 {
01524     char response[GC_MESSAGE_LENGTH+2];
01525 
01526     SetGCDeviceReport(cmd, response);
01527 
01528     int gcStatusCode;
01529 
01530     // We expect a response of the form "Dxxx00nn", where the two digits 'nn' are the status code
01531 
01532     // But check for "EPKT" first...
01533     if(response[0] == 'E') {
01534         gcStatusCode = -1; // *** Caller must check for this ***
01535     } else {
01536         sscanf(&response[6], "%d", &gcStatusCode);
01537     }
01538     
01539     return gcStatusCode;
01540 }
01541 
01542 /*
01543     Obtains the GC status, using the "QSTA" command.
01544     
01545     Returns the status to the caller.
01546     Note that this may be -1 if there was an error. Caller *must* check for this.
01547     Otherwise see the GC_STATE enumeration (GCStateAndFaultCodes.h)
01548     for the meaning of these codes.
01549 */
01550 int GetGCStatusLoop::GetGCStatus(void)
01551 {
01552     return GetGCStatusOrFaultCode("QSTA");
01553 }
01554 
01555 /*
01556     Obtains the GC fault state, using the "QFLT" command.
01557     
01558     Returns the fault state to the caller.
01559     Note that this may be -1 if there was an error - caller *must* check for this. 
01560     Otherwise see the GC_FAULT enumeration (GCStateAndFaultCodes.h)
01561     for the meaning of these codes.
01562 */
01563 int GetGCStatusLoop::GetGCFaultCode(void)
01564 {
01565     return GetGCStatusOrFaultCode("QFLT");
01566 }
01567 
01568 /*
01569     Given GC status and fault codes, returns the corresponding descriptive text
01570     as a null-terminated string, obtained from the GCStateAndFaultCodes class.
01571     
01572     Args are:   the state code
01573                 the fault code
01574                 a pointer to the buffer to contain the null-terminated string
01575                 describing the GC state
01576                 
01577     No return code.
01578 */
01579 void GetGCStatusLoop::GetGCStateAsInfoString(int gcStateCode, int gcFaultCode, char *statusString)
01580 {
01581     char buff[100];
01582 
01583 #ifdef USE_VERSION_102 // See GCStateAndFaultCodes.h
01584     if(gcStateCode == GC_STATE_102_METHOD_FAULTED) {
01585 #else
01586     if(gcStateCode == GC_STATE_FAULTED) {
01587 #endif
01588         if(gcStateAndFaultCodes.GetFaultCodeString(gcFaultCode, buff)) {
01589             sprintf(statusString, "GC faulted: %s", buff);
01590         } else {
01591             sprintf(statusString, "GC faulted: unknown fault code %d", gcFaultCode);
01592         }
01593 
01594     } else {
01595 
01596         if(gcStateAndFaultCodes.GetStateCodeString(gcStateCode, buff)) {
01597             sprintf(statusString, "GC state: %s", buff);
01598         } else {
01599             sprintf(statusString, "GC state: unknown state code %d", gcStateCode);
01600         }
01601     }
01602 }
01603 
01604 /*
01605     Sets the easyGUI variable on the GCNotReadyToRun page
01606     to a string that tells the user why it is not ready to run
01607 */
01608 void GetGCStatusLoop::SetupGCNotReadyStateEasyGUIVariable(void)
01609 {
01610     int gcStatus = GetGCStatus();
01611     GCStateSimplified simplifiedGCState = GCStateOrFaultCode::GetSimplifiedGCState(gcStatus);
01612     gcStateAndFaultCodes.GetSimplifiedStateCodeString(simplifiedGCState, GuiVar_gcNotReadyState);
01613     
01614 }
01615 
01616 
01617 /*
01618     Tells the caller whether or not the GC is in a fault state. 
01619     If so, returns true, and copies a string describing the status to the specified buffer.
01620     If not, returns false (and copies nothing to the buffer).
01621     
01622     Args: the current GC status
01623           pointer to a buffer to contain the null-terminated string describing the status.
01624     
01625     Return code: true if the GC is in a fault state, false if not.
01626 */
01627 bool GetGCStatusLoop::GCHasFaulted(int gcStatus, char* statusString)
01628 {
01629     bool gcHasFaulted = false;   
01630     statusString[0] = '\0';
01631     
01632     if(gcStatus == -1) { // Got "EPKT" as response from GC
01633         strcpy(statusString, "Failed to get status");
01634         gcHasFaulted = true;
01635     } else {
01636 #ifdef USE_VERSION_102 // See GCStateAndFaultCodes.h
01637         int gcFaultCode = GC_FAULT_102_NO_ERROR;
01638         if(gcStatus == GC_STATE_102_METHOD_FAULTED) {
01639             gcFaultCode = GetGCFaultCode();
01640             
01641             if(gcFaultCode != GC_FAULT_102_NO_ERROR) {
01642                 gcHasFaulted = true;
01643             }
01644         }
01645 #else
01646         int gcFaultCode = GC_FAULT_NO_ERROR;
01647         if(gcStatus == GC_STATE_FAULTED) {
01648             gcFaultCode = GetGCFaultCode();
01649             
01650             if(gcFaultCode != GC_FAULT_NO_ERROR) {
01651                 gcHasFaulted = true;
01652             }
01653         }
01654 #endif
01655         GetGCStateAsInfoString(gcStatus, gcFaultCode, statusString);
01656     }
01657     
01658     return gcHasFaulted;
01659 }
01660 
01661 /*
01662     Version of the above that does not require any arguments.
01663 
01664     Tells the caller whether or not the GC is in a fault state. 
01665     
01666     No arguments.
01667     
01668     Return code: true if the GC is in a fault state, false if not.
01669 */
01670 bool GetGCStatusLoop::GCHasFaulted(void)
01671 {
01672     char statusString[100];
01673 
01674     return GCHasFaulted(GetGCStatus(), statusString);
01675 }
01676 
01677 
01678 /*
01679     Get the temperature of a GC component (column, detector, etc), and returns it as a null-terminated string, with a descriptive prefix.
01680     The GC commands for all of the component temperatures give a similar response.
01681     
01682     Note also that this code is intended to be as efficient as possible.
01683     
01684     Args: pointer to the null-terminated string specifying the command to get the temperature
01685           pointer to the buffer to contain the temperature, also as a null-terminated string
01686           optional bool set true if the value is in units of one-tenth of a degree, 
01687           false if whole degrees (default is true)
01688           
01689     No return code.
01690 */
01691 void GetGCStatusLoop::GetComponentTemperature(char *cmd, char *temp, bool wantPrefix, bool wantDegSuffix, bool oneTenthDegree)
01692 {
01693     char response[50];
01694     SetGCDeviceReport(cmd, response);
01695     // We expect a response like this: "Dxxx1234" - temp in units of 0.1 deg
01696 
01697     int index = 0;
01698     
01699     if(wantPrefix) {
01700         temp[index++] = 'T';
01701         temp[index++] = 'e';
01702         temp[index++] = 'm';
01703         temp[index++] = 'p';
01704         temp[index++] = ':';
01705         temp[index++] = ' ';
01706     }
01707         
01708     // But check for "EPKT" first
01709     if(response[0] == 'E') {
01710         temp[index++] = '*';
01711         temp[index++] = '*';
01712         temp[index++] = ' ';
01713         temp[index++] = 'E';
01714         temp[index++] = 'r';
01715         temp[index++] = 'r';
01716         temp[index++] = 'o';
01717         temp[index++] = 'r';
01718         temp[index++] = ' ';
01719         temp[index++] = '*';
01720         temp[index++] = '*';
01721     } else {
01722         // Ignore leading zeroes
01723         bool wantNextChars = false;
01724         if(response[4] != '0') {
01725             temp[index++] = response[4];
01726             wantNextChars = true;
01727         }
01728         if(wantNextChars || (response[5] != '0')) {
01729             temp[index++] = response[5];
01730             wantNextChars = true;
01731         }
01732         // If the value is zero, make sure we return "0.0" - 
01733         // we just don't want any zeroes before that
01734         if(oneTenthDegree) {
01735             temp[index++] = response[6];
01736             temp[index++] = '.';
01737             temp[index++] = response[7];
01738         } else {
01739             if(wantNextChars || (response[6] != '0')) {
01740                 temp[index++] = response[6];
01741             }
01742             temp[index++] = response[7];
01743         }
01744         if(wantDegSuffix) {
01745 #define TRY_DEG_SYMBOL
01746 #ifdef  TRY_DEG_SYMBOL
01747             temp[index++] = ' ';
01748             temp[index++] = degSymbol;
01749             temp[index++] = 'C';
01750 #else
01751             temp[index++] = ' ';
01752             temp[index++] = 'd';
01753             temp[index++] = 'e';
01754             temp[index++] = 'g';
01755             temp[index++] = ' ';
01756             temp[index++] = 'C';
01757 #endif //  TRY_DEG_SYMBOL
01758         }
01759     }
01760 
01761     temp[index++] = '\0';
01762 }
01763 
01764 /*
01765     Get the temperature of a GC component (column, detector, etc), and returns it as a floating-point value.
01766     The GC commands for all of the component temperatures give a similar response.
01767     
01768     Note also that this code is intended to be as efficient as possible.
01769     
01770     Args: pointer to the null-terminated string specifying the command to get the temperature
01771           pointer to the floating-point variable to contain the temperature
01772           optional bool set true if the value is in units of one-tenth of a degree, 
01773           false if whole degrees (default is true)
01774           
01775     No return code.
01776 */
01777 void GetGCStatusLoop::GetComponentTemperature(char *cmd, float *temp, bool oneTenthDegree)
01778 {
01779     char buff[10];
01780     char response[50];
01781     SetGCDeviceReport(cmd, response);
01782     // We expect a response like this: "Dxxx1234" - temp in units of 0.1 deg
01783     
01784     // But check for "EPKT" first
01785     if(response[0] == 'E') {
01786         *temp = -1.0f; // ** Caller must check for this **
01787     } else {
01788         buff[0] = response[4];
01789         buff[1] = response[5];
01790         buff[2] = response[6];
01791         if(oneTenthDegree) {
01792             buff[3] = '.';
01793             buff[4] = response[7];
01794         } else {
01795             buff[3] = response[7];
01796         }
01797 
01798         sscanf(buff, "%f", temp);
01799     }
01800 //#define DEBUG_HERE
01801 #ifdef DEBUG_HERE
01802     char dbg[100];
01803     sprintf(dbg, "GGCSL::GCT - returning : %f", *temp);
01804     EasyGUIDebugPrint(dbg, 0, 20);
01805 #undef DEBUG_HERE
01806 #endif
01807 }
01808 
01809 /*
01810     Gets the column temperature, returning it as a null-terminated string, with a descriptive prefix.
01811     
01812     Args: pointer to a buffer to contain the null-terminated string specifying the temperature.
01813           boolean true if the caller wants an identifying prefix, false if not
01814     
01815     No return code.
01816 */
01817 void GetGCStatusLoop::GetColumnTemperature(char *temp, bool wantPrefix)
01818 {
01819     GetComponentTemperature("QCOL", temp, wantPrefix, true);
01820 }
01821 
01822 /*
01823     Gets the column temperature, returning it as a null-terminated string.
01824     
01825     Args: pointer to a buffer to contain the null-terminated string specifying the temperature.
01826           boolean true if the caller wants an identifying prefix, false if not
01827           boolean true if the caller wants a suffix specifyng the units, false if not
01828     
01829     No return code.
01830 */
01831 void GetGCStatusLoop::GetColumnTemperature(char *temp, bool wantPrefix, bool wantSuffix)
01832 {
01833     GetComponentTemperature("QCOL", temp, wantPrefix, wantSuffix);
01834 }
01835 
01836 /*
01837     Gets the column temperature, returning it as a floating-point value.
01838     
01839     Args: pointer to a floating point variable to contain the temperature
01840     
01841     No return code.
01842 */
01843 void GetGCStatusLoop::GetColumnTemperature(float *temp)
01844 {
01845     GetComponentTemperature("QCOL", temp);
01846 }
01847 
01848 /*
01849     Gets the target column temperature, returning it as a null-terminated string
01850 
01851     Args: pointer to a buffer to contain the null-terminated string specifying the temperature.
01852           pointer to a string specifying the sprintf format to use
01853     No return code.
01854 */
01855 void GetGCStatusLoop::GetColumnTargetTemperature(char *temp, const char *format)
01856 {
01857     char buff[40];
01858     GetComponentTemperature("GCOL", buff, false, false, false); // Target temperature is in whole degrees, not one-tenth
01859     
01860     sprintf(temp, format, buff);
01861 }
01862 
01863 /*
01864     Gets the column temperature, returning it as a null-terminated string, with a descriptive prefix.
01865     
01866     Args: pointer to a buffer to contain the null-terminated string specifying the temperature.
01867           boolean true if the caller wants an identifying prefix, false if not
01868     
01869     No return code.
01870 */
01871 void GetGCStatusLoop::GetDirectlyHeatedColumnTemperature(char *temp, bool wantPrefix)
01872 {
01873     GetComponentTemperature("QDCT", temp, wantPrefix, true);
01874 }
01875 
01876 /*
01877     Gets the column temperature, returning it as a floating-point value.
01878     
01879     Args: pointer to a floating point variable to contain the temperature
01880     
01881     No return code.
01882 */
01883 void GetGCStatusLoop::GetDirectlyHeatedColumnTemperature(float *temp)
01884 {
01885     GetComponentTemperature("QDCT", temp);
01886 }
01887 
01888 /*
01889     Gets the detector temperature, returning it as a null-terminated string, with 'deg C' as suffix.
01890     
01891     Args: pointer to a buffer to contain the null-terminated string specifying the temperature.
01892     
01893     No return code.
01894 */
01895 void GetGCStatusLoop::GetDetectorTemperature(char *temp)
01896 {
01897     char buff[40];
01898     GetComponentTemperature("QDET", buff, true, true);
01899 
01900     // Temporary - omit "Temp: " prefix
01901     strcpy(temp, &buff[6]);
01902 }
01903 
01904 /*
01905     Gets the detector temperature, returning it as a floating-point value.
01906     
01907     Args: pointer to a floating point variable to contain the temperature
01908     
01909     No return code.
01910 */
01911 void GetGCStatusLoop::GetDetectorTemperature(float *temp)
01912 {
01913     GetComponentTemperature("QDET", temp);
01914 }
01915 
01916 /*
01917     Gets the target detector temperature, returning it as a null-terminated string
01918 
01919     Args: pointer to a buffer to contain the null-terminated string specifying the temperature.
01920           string specifying the sprintf format to use
01921           
01922     No return code.
01923 */
01924 void GetGCStatusLoop::GetDetectorTargetTemperature(char *temp, const char *format)
01925 {
01926     char buff[40];
01927     GetComponentTemperature("GDET", buff, false, false, false); // Target temperature is in whole degrees, not one-tenth
01928     
01929     sprintf(temp, format, buff);
01930 }
01931 
01932 
01933 /*
01934     Gets the filament polarity for a TCD detector, and returns it as a null-terminated string.
01935     
01936     Note also that this code is intended to be as efficient as possible.
01937     
01938     Args: pointer to the buffer to contain the polarity, as a null-terminated string
01939           
01940     No return code.
01941 */
01942 void GetGCStatusLoop::GetTCDDetectorFilamentPolarity(char *polarity)
01943 {
01944     char response[50];
01945     SetGCDeviceReport("GPOL", response);
01946 
01947     // We expect a response like this: "DPOL0001" for positive, "DPOL0002" for negative
01948     int index = 0;
01949     // But check for "EPKT" first
01950     if(response[0] == 'E') {
01951         polarity[index++] = '*';
01952         polarity[index++] = '*';
01953         polarity[index++] = ' ';
01954         polarity[index++] = 'E';
01955         polarity[index++] = 'r';
01956         polarity[index++] = 'r';
01957         polarity[index++] = 'o';
01958         polarity[index++] = 'r';
01959         polarity[index++] = ' ';
01960         polarity[index++] = '*';
01961         polarity[index++] = '*';
01962     } else {
01963         switch(response[7]) {
01964             case '1':
01965                 polarity[index++] = 'p';
01966                 polarity[index++] = 'o';
01967                 polarity[index++] = 's';
01968                 polarity[index++] = 'i';
01969                 polarity[index++] = 't';
01970                 polarity[index++] = 'i';
01971                 polarity[index++] = 'v';
01972                 polarity[index++] = 'e';
01973                 break;
01974             case '2':
01975                 polarity[index++] = 'n';
01976                 polarity[index++] = 'e';
01977                 polarity[index++] = 'g';
01978                 polarity[index++] = 'a';
01979                 polarity[index++] = 't';
01980                 polarity[index++] = 'i';
01981                 polarity[index++] = 'v';
01982                 polarity[index++] = 'e';
01983                 break;
01984             default:
01985                 polarity[index++] = 'i';
01986                 polarity[index++] = 'n';
01987                 polarity[index++] = 'v';
01988                 polarity[index++] = 'a';
01989                 polarity[index++] = 'l';
01990                 polarity[index++] = 'd';
01991                 polarity[index++] = 'd';
01992                 break;
01993         }
01994     }
01995     polarity[index] = '\0';
01996 }
01997 
01998 /*
01999     Gets the filament temperature for a TCD detector, and returns it as a null-terminated string.
02000     
02001     Note also that this code is intended to be as efficient as possible.
02002     
02003     Args: pointer to the buffer to contain the temperature, as a null-terminated string
02004           
02005     No return code.
02006 */
02007 void GetGCStatusLoop::GetTCDDetectorFilamentTemperature(char *temp)
02008 {
02009     char buff[40];
02010     GetComponentTemperature("GFIL", buff, true, true);
02011 
02012     // Temporary - omit "Temp: " prefix
02013     strcpy(temp, &buff[6]);
02014 }
02015 
02016 /*
02017     Gets the amplifier range (gain) for a TCD detector, and returns it as a null-terminated string.
02018     
02019     Note also that this code is intended to be as efficient as possible.
02020     
02021     Args: pointer to the buffer to contain the range, as a null-terminated string
02022           
02023     No return code.
02024 */
02025 void GetGCStatusLoop::GetTCDDetectorRange(char *range)
02026 {
02027     char response[50];
02028     SetGCDeviceReport("GRNG", response);
02029 
02030     // We expect a response like this: "DRNG0001" for x1, "DRNG0002" for x10
02031     int index = 0;
02032     // But check for "EPKT" first
02033     if(response[0] == 'E') {
02034         range[index++] = '*';
02035         range[index++] = '*';
02036         range[index++] = ' ';
02037         range[index++] = 'E';
02038         range[index++] = 'r';
02039         range[index++] = 'r';
02040         range[index++] = 'o';
02041         range[index++] = 'r';
02042         range[index++] = ' ';
02043         range[index++] = '*';
02044         range[index++] = '*';
02045     } else {
02046         switch(response[7]) {
02047             case '1':
02048                 range[index++] = 'x';
02049                 range[index++] = '1';
02050                 break;
02051             case '2':
02052                 range[index++] = 'x';
02053                 range[index++] = '1';
02054                 range[index++] = '0';
02055                 break;
02056             default:
02057                 range[index++] = 'i';
02058                 range[index++] = 'n';
02059                 range[index++] = 'v';
02060                 range[index++] = 'a';
02061                 range[index++] = 'l';
02062                 range[index++] = 'd';
02063                 range[index++] = 'd';
02064                 break;
02065         }
02066     }
02067     range[index] = '\0';
02068 }
02069 
02070 /*
02071     Gets the current for an ECD detector, and returns it as a null-terminated string.
02072     
02073     Note also that this code is intended to be as efficient as possible.
02074     
02075     Args: pointer to the buffer to contain the current, as a null-terminated string
02076           
02077     No return code.
02078 */
02079 void GetGCStatusLoop::GetECDDetectorCurrent(char *current)
02080 {
02081     char response[50];
02082     SetGCDeviceReport("GCUR", response);
02083 
02084     // We expect a response like this: "DCURnnnn", where 'nnnn' is the sensitivity.
02085     // TODO: perform appropriate interpretation on the value 'nnnn'.
02086     //       Currently, we just return it unchanged
02087     
02088     int index = 0;
02089     // Check for "EPKT" first
02090     if(response[0] == 'E') {
02091         current[index++] = '*';
02092         current[index++] = '*';
02093         current[index++] = ' ';
02094         current[index++] = 'E';
02095         current[index++] = 'r';
02096         current[index++] = 'r';
02097         current[index++] = 'o';
02098         current[index++] = 'r';
02099         current[index++] = ' ';
02100         current[index++] = '*';
02101         current[index++] = '*';
02102     } else {
02103         // Ignore leading zeroes
02104         bool wantNextChars = false;
02105         if(response[4] != '0') {
02106             current[index++] = response[4];
02107             wantNextChars = true;
02108         }
02109         if(wantNextChars || (response[5] != '0')) {
02110             current[index++] = response[5];
02111             wantNextChars = true;
02112         }
02113         if(wantNextChars || (response[6] != '0')) {
02114             current[index++] = response[6];
02115         }        
02116         // If the value is zero, make sure we return "0" - 
02117         // we just don't want any zeroes before that
02118         current[index++] = response[7];
02119     }
02120     current[index] = '\0';
02121 }
02122 
02123 /*
02124     Gets the range for an FPD detector, and returns it as a null-terminated string.
02125     
02126     Note also that this code is intended to be as efficient as possible.
02127     
02128     Args: pointer to the buffer to contain the current, as a null-terminated string
02129           
02130     No return code.
02131     
02132     *** This detector type now seems to have the same "Get Range" command as all other types ***
02133     *** i.e. "GRNG" - so this function is not currently used                                 ***
02134 */
02135 void GetGCStatusLoop::GetFPDDetectorRange(char *range)
02136 {
02137     char response[50];
02138     SetGCDeviceReport("GRN2", response);
02139 
02140     // We expect a response like this: "DRNG0001" for x1, "DRNG0002" for x10, "DRNG0003" for x100
02141     int index = 0;
02142     // But check for "EPKT" first
02143     if(response[0] == 'E') {
02144         range[index++] = '*';
02145         range[index++] = '*';
02146         range[index++] = ' ';
02147         range[index++] = 'E';
02148         range[index++] = 'r';
02149         range[index++] = 'r';
02150         range[index++] = 'o';
02151         range[index++] = 'r';
02152         range[index++] = ' ';
02153         range[index++] = '*';
02154         range[index++] = '*';
02155     } else {
02156         switch(response[7]) {
02157             case '1':
02158                 range[index++] = 'x';
02159                 range[index++] = '1';
02160                 break;
02161             case '2':
02162                 range[index++] = 'x';
02163                 range[index++] = '1';
02164                 range[index++] = '0';
02165                 break;
02166             case '3':
02167                 range[index++] = 'x';
02168                 range[index++] = '1';
02169                 range[index++] = '0';
02170                 range[index++] = '0';
02171                 break;
02172             default:
02173                 range[index++] = 'i';
02174                 range[index++] = 'n';
02175                 range[index++] = 'v';
02176                 range[index++] = 'a';
02177                 range[index++] = 'l';
02178                 range[index++] = 'd';
02179                 range[index++] = 'd';
02180                 break;
02181         }
02182     }
02183     range[index] = '\0';
02184 }
02185 
02186 /*
02187     Gets the current for an ECD detector, and returns it as a null-terminated string.
02188     
02189     Note also that this code is intended to be as efficient as possible.
02190     
02191     Args: pointer to the buffer to contain the current, as a null-terminated string
02192           
02193     No return code.
02194 */
02195 void GetGCStatusLoop::GetFPDDetectorSensitivity(char *sensitivity)
02196 {
02197     char response[50];
02198     SetGCDeviceReport("GSEN", response);
02199 
02200     // We expect a response like this: "DSENnnnn", where 'nnnn' is the sensitivity.
02201     // TODO: perform appropriate interpretation on the value 'nnnn'.
02202     //       Currently, we just return it unchanged
02203     
02204     int index = 0;
02205     // Check for "EPKT" first
02206     if(response[0] == 'E') {
02207         sensitivity[index++] = '*';
02208         sensitivity[index++] = '*';
02209         sensitivity[index++] = ' ';
02210         sensitivity[index++] = 'E';
02211         sensitivity[index++] = 'r';
02212         sensitivity[index++] = 'r';
02213         sensitivity[index++] = 'o';
02214         sensitivity[index++] = 'r';
02215         sensitivity[index++] = ' ';
02216         sensitivity[index++] = '*';
02217         sensitivity[index++] = '*';
02218     } else {
02219         // Ignore leading zeroes
02220         bool wantNextChars = false;
02221         if(response[4] != '0') {
02222             sensitivity[index++] = response[4];
02223             wantNextChars = true;
02224         }
02225         if(wantNextChars || (response[5] != '0')) {
02226             sensitivity[index++] = response[5];
02227             wantNextChars = true;
02228         }
02229         if(wantNextChars || (response[6] != '0')) {
02230             sensitivity[index++] = response[6];
02231         }        
02232         // If the value is zero, make sure we return "0" - 
02233         // we just don't want any zeroes before that
02234         sensitivity[index++] = response[7];
02235     }
02236     sensitivity[index] = '\0';
02237 }
02238 
02239 /*
02240     Gets the injector temperature, returning it as a null-terminated string, with a descriptive prefix.
02241     
02242     Args: pointer to a buffer to contain the null-terminated string specifying the temperature.
02243     
02244     No return code.
02245 */
02246 void GetGCStatusLoop::GetInjectorTemperature(char *temp, bool wantPrefix)
02247 {
02248     GetComponentTemperature("QINJ", temp, wantPrefix, true);
02249 }
02250 
02251 /*
02252     Gets the injector temperature, returning it as a floating-point value.
02253     
02254     Args: pointer to a floating point variable to contain the temperature
02255     
02256     No return code.
02257 */
02258 void GetGCStatusLoop::GetInjectorTemperature(float *temp)
02259 {
02260     GetComponentTemperature("QINJ", temp);
02261 }
02262 
02263 /*
02264     Gets the target injector temperature, returning it as a null-terminated string
02265 
02266     Args: pointer to a buffer to contain the null-terminated string specifying the temperature.
02267           pointer to a string specifying the sprintf format string to use
02268           
02269     No return code.
02270 */
02271 void GetGCStatusLoop::GetInjectorTargetTemperature(char *temp, const char *format)
02272 {
02273     char buff[40];
02274     GetComponentTemperature("GINJ", buff, false, false, false); // Target temperature is in whole degrees, not one-tenth
02275     
02276     sprintf(temp, format, buff);
02277 }
02278 
02279 /*
02280     Gets a pressure value using the specified command, and returns it as a null-terminated string, with a descriptive prefix.
02281     
02282     Note also that this code is intended to be as efficient as possible.
02283     
02284     Args: pointer to the buffer to contain the pressure, as a null-terminated string
02285           
02286     No return code.
02287 */
02288 void GetGCStatusLoop::GetPressure(char *cmd, char *press, bool wantPrefix, bool wantUnits)
02289 {
02290     char response[50];
02291     SetGCDeviceReport(cmd, response);
02292     // We expect a response like this: "DPRS1234" - pressure in units of 0.1 psi
02293 
02294     int index = 0;
02295     
02296     if(wantPrefix) {
02297         press[index++] = 'P';
02298         press[index++] = 'r';
02299         press[index++] = 'e';
02300         press[index++] = 's';
02301         press[index++] = 's';
02302         press[index++] = 'u';
02303         press[index++] = 'r';
02304         press[index++] = 'e';
02305         press[index++] = ':';
02306         press[index++] = ' ';
02307     }
02308     
02309     // But check for "EPKT" first
02310     if(response[0] == 'E') {
02311         press[index++] = '*';
02312         press[index++] = '*';
02313         press[index++] = ' ';
02314         press[index++] = 'E';
02315         press[index++] = 'r';
02316         press[index++] = 'r';
02317         press[index++] = 'o';
02318         press[index++] = 'r';
02319         press[index++] = ' ';
02320         press[index++] = '*';
02321         press[index++] = '*';
02322     } else {    
02323         bool wantNextChars = false;
02324         if(response[4] != '0') {
02325             press[index++] = response[4];
02326             wantNextChars = true;
02327         }
02328         if(wantNextChars || (response[5] != '0')) {
02329             press[index++] = response[5];
02330         }
02331         // If the value is zero, make sure we return "0.0" - 
02332         // we just don't want any zeroes before that
02333         press[index++] = response[6];
02334         press[index++] = '.';
02335         press[index++] = response[7];
02336         
02337         if(wantUnits) {
02338             press[index++] = ' ';
02339             press[index++] = 'p';
02340             press[index++] = 's';
02341             press[index++] = 'i';
02342         }
02343     }
02344 
02345     press[index++] = '\0';
02346 }
02347 
02348 /*
02349     Gets the gas pressure, and returns it as a null-terminated string, with a descriptive prefix.
02350     
02351     Args: pointer to the buffer to contain the gas pressure, as a null-terminated string
02352           
02353     No return code.
02354 */
02355 void GetGCStatusLoop::GetGasPressure(char *press, bool wantPrefix, bool wantUnits)
02356 {
02357 //    GetPressure("QPRS", press, wantPrefix, wantUnits);
02358 // Use GPRS command, not QPRS - it gets the initial pressure specified as part of the current method, which is what we want
02359     GetPressure("GPRS", press, wantPrefix, wantUnits);
02360 }
02361 
02362 /*
02363     Gets the actual, current, gas pressure (as opposed to the initial pressure specified in the current method), 
02364     and returns it as a null-terminated string, with a descriptive prefix if required.
02365     
02366     Args: pointer to the buffer to contain the current gas pressure, as a null-terminated string
02367           
02368     No return code.
02369 */
02370 void GetGCStatusLoop::GetCurrentGasPressure(char *press, bool wantPrefix, bool wantUnits)
02371 {
02372     GetPressure("QPRS", press, wantPrefix, wantUnits); // QPRS - get current pressure - not GPRS
02373 }
02374 
02375 /*
02376     Gets the pulsed pressure, and returns it as a null-terminated string, with a descriptive prefix.
02377     
02378     Args: pointer to the buffer to contain the pulsed pressure, as a null-terminated string
02379           
02380     No return code.
02381 */
02382 void GetGCStatusLoop::GetGasPulsedPressure(char *pulsedPress)
02383 {
02384     GetPressure("GPPS", pulsedPress, false, true);
02385 }
02386 
02387 /*
02388     Gets the gas pressure, returning it as a floating-point value.
02389     
02390     Args: pointer to a floating point variable to be set to the pressure
02391     
02392     No return code.
02393 */
02394 void GetGCStatusLoop::GetGasPressure(float *press)
02395 {
02396     char buff[100];
02397     char response[50];
02398 //    SetGCDeviceReport("QPRS", response);
02399 // Use GPRS command, not QPRS - it gets the initial pressure specified as part of the current method, which is what we want
02400     SetGCDeviceReport("GPRS", response);
02401     // We expect a response like this: "DPRS1234" - pressure in units of 0.1 psi
02402 
02403     // Allow for "EPKT" being returned from GC
02404     if(response[0] == 'E') {
02405         *press = -1.0f; // ** Caller must check for this **
02406     } else {
02407         buff[0] = response[4];
02408         buff[1] = response[5];
02409         buff[2] = response[6];
02410         buff[3] = '.';
02411         buff[4] = response[7];
02412         
02413         sscanf(buff, "%f", press);
02414     }
02415 //#define DEBUG_HERE
02416 #ifdef DEBUG_HERE
02417     char dbg[100];
02418     sprintf(dbg, "GGCSL::GGP - returning : %f", *press);
02419     EasyGUIDebugPrint(dbg, 0, 20);
02420 #endif
02421 //#undef DEBUG_HERE
02422 }
02423 
02424 /*
02425     Redraw one easyGUI variable on its component page.
02426 
02427     (Trying to reduce "display flickering" by not redrawing the entire page unnecessarily).
02428     
02429     Note that the font must match the one specified in the easyGUI project for this variable - 
02430     we cannot obtain this at runtime.
02431 */
02432 void GetGCStatusLoop::RedrawSingleEasyGUIVariableOnComponentPage(GuiConst_INT16S X, GuiConst_INT16S Y, void *varPtr, GuiConst_INT8U alignment, GCComponent gcComponent)
02433 {
02434     GuiLib_DrawVar(
02435        X, // GuiConst_INT16S X,
02436        Y, // GuiConst_INT16S Y,
02437        GuiFont_Helv20Bold, // GuiConst_INT16U FontNo,
02438        varPtr, // void PrefixLocate *VarPtr,
02439        GuiLib_VAR_STRING, // GuiConst_INT8U VarType,
02440        GuiLib_FORMAT_DEC, // GuiConst_INT8U FormatterFormat,
02441        10, // GuiConst_INT8U FormatterFieldWidth,
02442        GuiLib_FORMAT_ALIGNMENT_RIGHT, // GuiConst_INT8U FormatterAlignment,
02443        0, // GuiConst_INT8U FormatterDecimals,
02444        0, // GuiConst_INT8U FormatterShowSign,
02445        0, // GuiConst_INT8U FormatterZeroPadding,
02446        0, // GuiConst_INT8U FormatterTrailingZeros,
02447        0, // GuiConst_INT8U FormatterThousandsSeparator,
02448        alignment, // GuiConst_INT8U Alignment,
02449        GuiLib_PS_ON, // GuiConst_INT8U PsWriting,
02450        GuiLib_TRANSPARENT_OFF, // GuiConst_INT8U Transparent,
02451        GuiLib_UNDERLINE_OFF, // GuiConst_INT8U Underlining,
02452        200, // GuiConst_INT16S BackBoxSizeX,
02453        0, // GuiConst_INT16S BackBoxSizeY1,
02454        0, // GuiConst_INT16S BackBoxSizeY2,
02455        GuiLib_BBP_NONE, // GuiConst_INT8U BackBorderPixels,
02456        0, // GuiConst_INTCOLOR ForeColor, [Black]
02457        homePageGCComponentStatusColorAreas->GetComponentCurrentColor(gcComponent) // GuiConst_INTCOLOR BackColor [same as status rectangle]
02458        ); 
02459 }
02460 
02461 /*
02462     Redraw one easyGUI variable on the home page. Unlike the single component pages (as of 13 Mar 2017),
02463     we no longer have component status rectangles on the Home page.
02464 
02465     (Trying to reduce "display flickering" by not redrawing the entire page unnecessarily).
02466     
02467     Note that the font must match the one specified in the easyGUI project for this variable - 
02468     we cannot obtain this at runtime.
02469 */
02470 void GetGCStatusLoop::RedrawSingleEasyGUIVariableOnHomePage(GuiConst_INT16S X, GuiConst_INT16S Y, void *varPtr, GuiConst_INT8U alignment)
02471 {
02472     GuiLib_DrawVar(
02473        X, // GuiConst_INT16S X,
02474        Y, // GuiConst_INT16S Y,
02475        GuiFont_Helv20Bold, // GuiConst_INT16U FontNo,
02476        varPtr, // void PrefixLocate *VarPtr,
02477        GuiLib_VAR_STRING, // GuiConst_INT8U VarType,
02478        GuiLib_FORMAT_DEC, // GuiConst_INT8U FormatterFormat,
02479        10, // GuiConst_INT8U FormatterFieldWidth,
02480        GuiLib_FORMAT_ALIGNMENT_RIGHT, // GuiConst_INT8U FormatterAlignment,
02481        0, // GuiConst_INT8U FormatterDecimals,
02482        0, // GuiConst_INT8U FormatterShowSign,
02483        0, // GuiConst_INT8U FormatterZeroPadding,
02484        0, // GuiConst_INT8U FormatterTrailingZeros,
02485        0, // GuiConst_INT8U FormatterThousandsSeparator,
02486        alignment, // GuiConst_INT8U Alignment,
02487        GuiLib_PS_ON, // GuiConst_INT8U PsWriting,
02488        GuiLib_TRANSPARENT_ON, // GuiConst_INT8U Transparent,
02489        GuiLib_UNDERLINE_OFF, // GuiConst_INT8U Underlining,
02490        200, // GuiConst_INT16S BackBoxSizeX,
02491        0, // GuiConst_INT16S BackBoxSizeY1,
02492        0, // GuiConst_INT16S BackBoxSizeY2,
02493        GuiLib_BBP_NONE, // GuiConst_INT8U BackBorderPixels,
02494        0, // GuiConst_INTCOLOR ForeColor, [Black]
02495        0xFFFF // GuiConst_INTCOLOR BackColor [White]
02496        ); 
02497 }
02498 
02499 /*
02500     If the column temperature has changed on the home page,
02501     redraw it manually - do not redisplay the entire page just for this
02502     (trying to reduce "flashing and banging")
02503 */
02504 void GetGCStatusLoop::DrawColumnTemperatureVariableOnHomePage(void)
02505 {
02506     // Hard coded values taken from easyGUI
02507 //    RedrawSingleEasyGUIVariableOnComponentPage(180, 140, &GuiVar_columnTemperature2, GuiLib_ALIGN_CENTER, COLUMN);
02508     RedrawSingleEasyGUIVariableOnHomePage(260, 100, &GuiVar_columnTemperature2, GuiLib_ALIGN_CENTER);
02509 }
02510 
02511 /*
02512     If the column target temperature has changed on the home page,
02513     redraw it manually - do not redisplay the entire page just for this
02514     (trying to reduce "flashing and banging")
02515 */
02516 void GetGCStatusLoop::DrawColumnTargetTemperatureVariableOnHomePage(void)
02517 {
02518     // Hard coded values taken from easyGUI
02519     RedrawSingleEasyGUIVariableOnHomePage(260, 140, &GuiVar_columnTargetTemperature, GuiLib_ALIGN_CENTER);
02520 }
02521 
02522 /*
02523     If the injector temperature has changed on the home page,
02524     redraw it manually - do not redisplay the entire page just for this
02525     (trying to reduce "flashing and banging")
02526 */
02527 void GetGCStatusLoop::DrawInjectorTemperatureVariableOnHomePage(void)
02528 {
02529     // Hard coded values taken from easyGUI
02530 //    RedrawSingleEasyGUIVariableOnComponentPage(590, 140, &GuiVar_injectorTemperature2, GuiLib_ALIGN_CENTER, INJECTOR);
02531     RedrawSingleEasyGUIVariableOnHomePage(260, 300, &GuiVar_injectorTemperature2, GuiLib_ALIGN_CENTER);
02532 }
02533 
02534 /*
02535     If the injector target temperature has changed on the home page,
02536     redraw it manually - do not redisplay the entire page just for this
02537     (trying to reduce "flashing and banging")
02538 */
02539 void GetGCStatusLoop::DrawInjectorTargetTemperatureVariableOnHomePage(void)
02540 {
02541     // Hard coded values taken from easyGUI
02542     RedrawSingleEasyGUIVariableOnHomePage(260, 340, &GuiVar_injectorTargetTemperature, GuiLib_ALIGN_CENTER);
02543 }
02544 
02545 /*
02546     If the detector temperature has changed on the home page,
02547     redraw it manually - do not redisplay the entire page just for this
02548     (trying to reduce "flashing and banging")
02549 */
02550 void GetGCStatusLoop::DrawDetectorTemperatureVariableOnHomePage(void)
02551 {
02552     // Hard coded values taken from easyGUI
02553 //    RedrawSingleEasyGUIVariableOnComponentPage(190, 340, &GuiVar_detectorTemperature2, GuiLib_ALIGN_LEFT, DETECTOR);
02554     RedrawSingleEasyGUIVariableOnHomePage(520, 100, &GuiVar_detectorTemperature2, GuiLib_ALIGN_LEFT);
02555 }
02556 
02557 /*
02558     If the detector target temperature has changed on the home page,
02559     redraw it manually - do not redisplay the entire page just for this
02560     (trying to reduce "flashing and banging")
02561 */
02562 void GetGCStatusLoop::DrawDetectorTargetTemperatureVariableOnHomePage(void)
02563 {
02564     // Hard coded values taken from easyGUI
02565     RedrawSingleEasyGUIVariableOnHomePage(520, 140, &GuiVar_detectorTargetTemperature, GuiLib_ALIGN_LEFT);
02566 }
02567 
02568 /*
02569     If the gas pressure has changed on the home page,
02570     redraw it manually - do not redisplay the entire page just for this
02571     (trying to reduce "flashing and banging")
02572 */
02573 void GetGCStatusLoop::DrawGasPressureVariableOnHomePage(void)
02574 {
02575     // Hard coded values taken from easyGUI
02576 //    RedrawSingleEasyGUIVariableOnComponentPage(590, 340, &GuiVar_gasPressure2, GuiLib_ALIGN_CENTER, GAS);
02577     RedrawSingleEasyGUIVariableOnHomePage(520, 300, &GuiVar_gasPressure2, GuiLib_ALIGN_CENTER);
02578 }
02579 
02580 /*
02581     If the gas target pressures have changed on the home page,
02582     redraw it manually - do not redisplay the entire page just for this
02583     (trying to reduce "flashing and banging")
02584 */
02585 void GetGCStatusLoop::DrawGasTargetPressureVariableOnHomePage(void)
02586 {
02587     // Hard coded values taken from easyGUI
02588     RedrawSingleEasyGUIVariableOnHomePage(520, 340, &GuiVar_gasTargetPressure2, GuiLib_ALIGN_CENTER);
02589 }
02590 
02591 /*
02592     If the gas target pressures have changed on the home page,
02593     redraw it manually - do not redisplay the entire page just for this
02594     (trying to reduce "flashing and banging")
02595 */
02596 void GetGCStatusLoop::DrawGCRealTimeClockVariableOnHomePage(void)
02597 {
02598     // Hard coded values taken from easyGUI
02599     
02600     // Need to redraw background, to erase previous value of variable.
02601     // I do not understand why the first coordinate pair in the call to GuiLib_ShowBitmapArea
02602     // needs to be 0, 0 to display the correct part of the bitmap. I expected them to be the same 
02603     // as the second coordinate pair - but that caused the top left portion of the bitmap to be displayed,
02604     // not the part where the variable actually is.
02605     //(See also DrawBackgroundBitmapOverDoorLockAndReleaseButtons() in main.cpp)
02606     GuiLib_ShowBitmapArea(GuiStruct_Bitmap_BlankBackground, 0, 0, 230, 428, 580, 460, -1); // -1 means 'no transparent colour'
02607     
02608     RedrawSingleEasyGUIVariableOnHomePage(404, 450, &GuiVar_gcTimeOnHomePage, GuiLib_ALIGN_CENTER);
02609 }
02610 
02611 /*
02612     We display both the gas initial pressure and pulsed pressure on the Home page,
02613     in the same string. This function generates that string, in a consistent format.
02614     
02615     Args: a pointer to the string to be updated
02616     
02617     No return code
02618 */
02619 void GetGCStatusLoop::UpdateGasTargetPressuresOnHomePage(char *buffer)
02620 {
02621     char initialPressure[20];
02622     char pulsedPressure[20];
02623     
02624     GetPressure("GPRS", initialPressure, false, false);
02625     GetPressure("GPPS", pulsedPressure, false, false);
02626 
02627     sprintf(buffer, "(Target: %s/%s)", initialPressure, pulsedPressure);
02628 }
02629 
02630 /*
02631     Displays the data on the home page, by copying it to the relevant easyGUI variables,
02632     and calling the relevant functions to actually display it.
02633     
02634     Args: a boolean specifying whether or not the display is actually to be updated.
02635           Even if it is false when we are called, we may set it true if we discover
02636           one or more data items has changed - but note that we will not set it false 
02637           if it is already true. If it is true after we have looked at all 
02638           the home page data, we will then update the display. (If we are called
02639           with this value set to false, the caller is effectively saying
02640           'display the home page data only if it has changed'.)
02641           
02642     No return code.
02643 */
02644 void GetGCStatusLoop::DisplayHomePageData(bool mustUpdateDisplay)
02645 {
02646 //    EasyGUIDebugPrint("Home Page", 100, 100);
02647 
02648 #define DRAW_VARIABLES_INDIVIDUALLY
02649     char buff[40];
02650     
02651     if(GetColumnType() == DIRECTLY_HEATED_COLUMN) {
02652         GetDirectlyHeatedColumnTemperature(buff, false);
02653     } else {
02654         GetColumnTemperature(buff, false);
02655     }
02656     if(strcmp(buff, GuiVar_columnTemperature2) != 0) {
02657         strcpy(GuiVar_columnTemperature2, buff);
02658 #ifdef DRAW_VARIABLES_INDIVIDUALLY
02659         DrawColumnTemperatureVariableOnHomePage();
02660 #else
02661         mustUpdateDisplay = true;
02662 #endif
02663     }
02664     
02665     GetColumnTargetTemperature(buff, "(Target: %s)");
02666     if(strcmp(buff, GuiVar_columnTargetTemperature) != 0) {
02667         strcpy(GuiVar_columnTargetTemperature, buff);
02668 #ifdef DRAW_VARIABLES_INDIVIDUALLY
02669         DrawColumnTargetTemperatureVariableOnHomePage();
02670 #else
02671         mustUpdateDisplay = true;
02672 #endif
02673     }
02674 
02675     GetInjectorTemperature(buff, false);
02676     if(strcmp(buff, GuiVar_injectorTemperature2) != 0) {
02677         strcpy(GuiVar_injectorTemperature2, buff);
02678 #ifdef DRAW_VARIABLES_INDIVIDUALLY
02679         DrawInjectorTemperatureVariableOnHomePage();
02680 #else
02681         mustUpdateDisplay = true;
02682 #endif
02683     }
02684 
02685     GetInjectorTargetTemperature(buff, "(Target: %s)");
02686     if(strcmp(buff, GuiVar_injectorTargetTemperature) != 0) {
02687         strcpy(GuiVar_injectorTargetTemperature, buff);
02688 #ifdef DRAW_VARIABLES_INDIVIDUALLY
02689         DrawInjectorTargetTemperatureVariableOnHomePage();
02690 #else
02691         mustUpdateDisplay = true;
02692 #endif
02693     }
02694 
02695     GetDetectorTemperature(buff);
02696     if(strcmp(buff, GuiVar_detectorTemperature2) != 0) {
02697         strcpy(GuiVar_detectorTemperature2, buff);
02698 #ifdef DRAW_VARIABLES_INDIVIDUALLY
02699         DrawDetectorTemperatureVariableOnHomePage();
02700 #else
02701         mustUpdateDisplay = true;
02702 #endif
02703     }
02704 
02705     GetDetectorTargetTemperature(buff, "(Target: %s)");
02706     if(strcmp(buff, GuiVar_detectorTargetTemperature) != 0) {
02707         strcpy(GuiVar_detectorTargetTemperature, buff);
02708 #ifdef DRAW_VARIABLES_INDIVIDUALLY
02709         DrawDetectorTargetTemperatureVariableOnHomePage();
02710 #else
02711         mustUpdateDisplay = true;
02712 #endif
02713     }
02714 
02715     GetCurrentGasPressure(buff, false, true);
02716     if(strcmp(buff, GuiVar_gasPressure2) != 0) {
02717         strcpy(GuiVar_gasPressure2, buff);
02718 #ifdef DRAW_VARIABLES_INDIVIDUALLY
02719         DrawGasPressureVariableOnHomePage();
02720 #else
02721         mustUpdateDisplay = true;
02722 #endif
02723     }
02724     
02725     UpdateGasTargetPressuresOnHomePage(buff);
02726     if(strcmp(buff, GuiVar_gasTargetPressure2) != 0) {
02727         strcpy(GuiVar_gasTargetPressure2, buff);
02728 #ifdef DRAW_VARIABLES_INDIVIDUALLY
02729         DrawGasTargetPressureVariableOnHomePage();
02730 #else
02731         mustUpdateDisplay = true;
02732 #endif
02733     }
02734 
02735     GetGCRealTimeClockTime(buff);
02736     if(strcmp(buff, GuiVar_gcTimeOnHomePage) != 0) {
02737         strcpy(GuiVar_gcTimeOnHomePage, buff);
02738 #ifdef DRAW_VARIABLES_INDIVIDUALLY
02739         DrawGCRealTimeClockVariableOnHomePage();
02740 #else
02741         mustUpdateDisplay = true;
02742 #endif
02743     }
02744     
02745     if(HomePageGCComponentStatusesHaveChanged()) {        
02746         mustUpdateDisplay = true;
02747     }
02748     
02749     if(mustUpdateDisplay) {
02750 
02751 //#define WANT_STATUS_RECTANGLES_ON_HOME_PAGE // else use QSPIBitmaps
02752 #ifdef WANT_STATUS_RECTANGLES_ON_HOME_PAGE
02753         // Updating the color areas involves getting the component statuses from the GC -
02754         // do this before GuiLib_Clear, otherwise the display stays blank for an annoyingly long time
02755         if(homePageGCComponentStatusColorAreas != NULL) {
02756             UpdateHomePageGCComponentStatusColorAreas();
02757         }
02758 #endif // WANT_STATUS_RECTANGLES_ON_HOME_PAGE
02759 
02760         // Also get the GC state before GuiLib_Clear, for the same reason
02761         int gcStatus = GetGCStatus();
02762         GCStateSimplified simplifiedGCState = GCStateOrFaultCode::GetSimplifiedGCState(gcStatus);
02763 
02764         // Makes the display flicker - but omitting it means old text is not cleared from the display
02765 #define WANT_GUILIB_CLEAR
02766 #ifdef WANT_GUILIB_CLEAR
02767 #ifdef USING_BACKGROUND_BITMAP
02768         DrawBackgroundBitmap(); // We want the status rectangles to be 'on top' of this - 
02769                                 // if we include it in the easyGUI page itself, it gets drawn by GuiLib_ShowScreen,
02770                                 // and overwrites the rectangles
02771 #else
02772         GuiLib_Clear();
02773 #endif
02774 #undef WANT_GUILIB_CLEAR
02775 #endif
02776         //...except that redrawing the status rectangles effectively clears the text on top of the rectangles -
02777         // so we do not need GuiLib_Clear here, since all the 'home page data' appears on top of the rectangles. 
02778         // Without it, only the text flickers, not the rectangles
02779         
02780 
02781 #ifdef WANT_STATUS_RECTANGLES_ON_HOME_PAGE
02782         // Note - we draw the status rectangles after GuiLib_Clear - otherwise we wouldn't see the rectangles at all - 
02783         // and before GuiLib_ShowScreen - so text, etc, is drawn on top of the rectangles.
02784         // (But note that we get the component statuses before GuiLib_Clear above.)
02785         if(homePageGCComponentStatusColorAreas != NULL) {
02786             homePageGCComponentStatusColorAreas->DisplayAll();
02787 
02788             lastColumnStatusDisplayedOnHomePage = homePageGCComponentStatusColorAreas->GetGCComponentStatus(COLUMN);
02789             lastInjectorStatusDisplayedOnHomePage = homePageGCComponentStatusColorAreas->GetGCComponentStatus(INJECTOR);
02790             lastDetectorStatusDisplayedOnHomePage = homePageGCComponentStatusColorAreas->GetGCComponentStatus(DETECTOR);
02791             lastGasStatusDisplayedOnHomePage = homePageGCComponentStatusColorAreas->GetGCComponentStatus(GAS);
02792         }
02793 #else
02794         if(qspiBitmaps != NULL) {
02795             qspiBitmaps->DisplayAllHomePageBitmaps();
02796         }
02797 #endif // WANT_STATUS_RECTANGLES_ON_HOME_PAGE
02798 
02799         GuiLib_ShowScreen(GuiStruct_HomePage_1, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
02800         
02801         // ...but we draw the Run button and the Heat On/Off button on top of the structure
02802 //#define ALWAYS_WANT_RUN_BUTTON
02803 #ifdef ALWAYS_WANT_RUN_BUTTON
02804         DrawRunButton((simplifiedGCState == GC_READY_TO_RUN) || (simplifiedGCState == GC_RUNNING));
02805 #else
02806         if((simplifiedGCState == GC_READY_TO_RUN) || (simplifiedGCState == GC_RUNNING)) {
02807             DrawRunButton(true);
02808         }
02809 #endif // ALWAYS_WANT_RUN_BUTTON
02810         DrawHeatOnOffButton();
02811         
02812         GCStateOrFaultCode::DrawSimplifiedStateMessageOnHomePageRunButton(simplifiedGCState); // *After* drawing the Run button
02813 
02814         // This variable (which sets the colour for the door actuator lock/unlock button text)
02815         // is not used on this page, only on the column pages - but this is the page
02816         // on which we turn the heat on and off. Try and make sure this variable is up to date
02817         // before we display the column pages, by updating it here
02818         SetupDoorActuatorCommandColour(usbDevice, usbHostGC, false);
02819         
02820         
02821         GuiLib_Refresh();    
02822 
02823 #define DEBUG_HERE
02824 #ifdef DEBUG_HERE
02825         static int counter = 0;
02826         char dbg[100];
02827         sprintf(dbg, "After GuiLib_Clear 1 [%d]", ++counter);
02828         EasyGUIDebugPrint(dbg, 0, 20);
02829 #undef DEBUG_HERE
02830 #endif
02831     }
02832 }
02833 
02834 /*
02835     Gets the maximum column temperature, and returns it as a null-terminated string, with a descriptive prefix.
02836     
02837     Note also that this code is intended to be as efficient as possible.
02838     
02839     Args: pointer to the buffer to contain the maximum temperature, as a null-terminated string
02840           boolean set true if the caller wants a full prefix ("Column max temp: "), 
02841           or false for a short prefix ("Max temp: ")
02842           boolean set false if the caller wants no prefix whatsoever
02843           
02844     No return code.
02845 */
02846 void GetGCStatusLoop::GetColumnMaxTemperature(char *maxTemp, bool wantFullPrefix, bool wantAnyPrefix, bool wantUnits)
02847 {
02848     char response[50];
02849 //    SetGCDeviceReport("QCMX", response);
02850 //  Correct command for this is now:
02851     SetGCDeviceReport("GCMX", response);
02852     // This returns a value in whole degrees, not 0.1 degree units
02853     // We expect a response like this: "DCMX1234" - where "1234" is the max temp 
02854     int index = 0;
02855     if(wantAnyPrefix) {
02856         if(wantFullPrefix) {
02857             maxTemp[index++] = 'C';
02858             maxTemp[index++] = 'o';
02859             maxTemp[index++] = 'l';
02860             maxTemp[index++] = 'u';
02861             maxTemp[index++] = 'm';
02862             maxTemp[index++] = 'n';
02863             maxTemp[index++] = ' ';
02864             maxTemp[index++] = 'm';
02865         } else {
02866             maxTemp[index++] = 'M';
02867         }
02868         maxTemp[index++] = 'a';
02869         maxTemp[index++] = 'x';
02870         maxTemp[index++] = ' ';
02871         maxTemp[index++] = 't';
02872         maxTemp[index++] = 'e';
02873         maxTemp[index++] = 'm';
02874         maxTemp[index++] = 'p';
02875         maxTemp[index++] = ':';
02876         maxTemp[index++] = ' ';
02877     }
02878     
02879     // But check for "EPKT" first
02880     if(response[0] == 'E') {
02881         maxTemp[index++] = '*';
02882         maxTemp[index++] = '*';
02883         maxTemp[index++] = ' ';
02884         maxTemp[index++] = 'E';
02885         maxTemp[index++] = 'r';
02886         maxTemp[index++] = 'r';
02887         maxTemp[index++] = 'o';
02888         maxTemp[index++] = 'r';
02889         maxTemp[index++] = ' ';
02890         maxTemp[index++] = '*';
02891         maxTemp[index++] = '*';
02892     } else {
02893         bool wantNextChars = false;
02894         if(response[4] != '0') {
02895             maxTemp[index++] = response[4];
02896             wantNextChars = true;
02897         }
02898         if(wantNextChars || (response[5] != '0')) {
02899             maxTemp[index++] = response[5];
02900             wantNextChars = true;
02901         }
02902         if(wantNextChars || (response[6] != '0')) {
02903             maxTemp[index++] = response[6];
02904         }
02905         // If the value is zero, make sure we return "0" - 
02906         // we just don't want any zeroes before that
02907         maxTemp[index++] = response[7];
02908         
02909         if(wantUnits) {
02910 #ifdef TRY_DEG_SYMBOL
02911             maxTemp[index++] = ' ';
02912             maxTemp[index++] = degSymbol;
02913             maxTemp[index++] = 'C';
02914 #else
02915             maxTemp[index++] = ' ';
02916             maxTemp[index++] = 'd';
02917             maxTemp[index++] = 'e';
02918             maxTemp[index++] = 'g';
02919             maxTemp[index++] = ' ';
02920             maxTemp[index++] = 'C';
02921 #endif // TRY_DEG_SYMBOL
02922         }
02923     }
02924 
02925     maxTemp[index] = '\0';    
02926 }
02927 
02928 /*
02929     Gets the maximum column temperature, returning it as a floating-point value.
02930     
02931     Args: pointer to a floating point variable to be set to the maximum temperature
02932     
02933     No return code.
02934 */
02935 void GetGCStatusLoop::GetColumnMaxTemperature(float *maxTemp)
02936 {
02937     char response[50];
02938     SetGCDeviceReport("GCMX", response);
02939 
02940     // Check for "EPKT" first
02941     if(response[0] == 'E') {
02942         *maxTemp = 0.0;
02943     } else {
02944         sscanf(&response[4], "%f", maxTemp);
02945     }
02946     
02947 //#define DEBUG_HERE
02948 #ifdef DEBUG_HERE
02949     char dbg[100];
02950     sprintf(dbg, "GGCSL::GCMTT - returning : %f", *maxTemp);
02951     EasyGUIDebugPrint(dbg, 0, 20);
02952 #undef DEBUG_HERE
02953 #endif
02954 }
02955 
02956 /*
02957     Gets the column type, and returns it as a null-terminated string, with a descriptive prefix.
02958     
02959     Note also that this code is intended to be as efficient as possible.
02960     
02961     Args: pointer to a buffer to contain the column type, as a null-terminated string
02962           
02963     No return code.
02964 */
02965 void GetGCStatusLoop::GetColumnType(char *type)
02966 {
02967     char response[50];
02968     SetGCDeviceReport("GCTY", response);
02969 
02970     // We expect a response like this: "DCTY0000" for None, "DCTY0001" for Conventional, 
02971     // "DCTY0002" for Directly heated
02972     int index = 0;
02973     // Check for "EPKT" first
02974     if(response[0] == 'E') {
02975         type[index++] = '*';
02976         type[index++] = '*';
02977         type[index++] = ' ';
02978         type[index++] = 'E';
02979         type[index++] = 'r';
02980         type[index++] = 'r';
02981         type[index++] = 'o';
02982         type[index++] = 'r';
02983         type[index++] = ' ';
02984         type[index++] = '*';
02985         type[index++] = '*';
02986     } else {
02987         switch(response[7]) {
02988             case '1':
02989                 type[index++] = 'C';
02990                 type[index++] = 'o';
02991                 type[index++] = 'n';
02992                 type[index++] = 'v';
02993                 type[index++] = 'e';
02994                 type[index++] = 'n';
02995                 type[index++] = 't';
02996                 type[index++] = 'i';
02997                 type[index++] = 'o';
02998                 type[index++] = 'n';
02999                 type[index++] = 'a';
03000                 type[index++] = 'l';
03001                 break;
03002             case '2':
03003                 type[index++] = 'D';
03004                 type[index++] = 'i';
03005                 type[index++] = 'r';
03006                 type[index++] = 'e';
03007                 type[index++] = 'c';
03008                 type[index++] = 't';
03009                 type[index++] = 'l';
03010                 type[index++] = 'y';
03011                 type[index++] = ' ';
03012                 type[index++] = 'h';
03013                 type[index++] = 'e';
03014                 type[index++] = 'a';
03015                 type[index++] = 't';
03016                 type[index++] = 'e';
03017                 type[index++] = 'd';
03018                 break;
03019             default:                
03020                 type[index++] = 'N';
03021                 type[index++] = 'o';
03022                 type[index++] = 'n';
03023                 type[index++] = 'e';
03024                 break;
03025         }
03026     }
03027     
03028     type[index++] = '\0';    
03029 }
03030 
03031 /*
03032     Gets the column type, and returns it as a value in the 'ColumnType' enumeration
03033     
03034     No arguments.
03035           
03036     Return value: the column type
03037 */
03038 ColumnType GetGCStatusLoop::GetColumnType(void)
03039 {
03040     char response[50];
03041     SetGCDeviceReport("GCTY", response);
03042 
03043     // We expect a response like this: "DCTY0000" for None, "DCTY0001" for Conventional, 
03044     // "DCTY0002" for Directly heated
03045     // Check for "EPKT" first
03046     if(response[0] == 'E') {
03047         return NO_COLUMN;
03048     }
03049     
03050     // 'else'...
03051     switch(response[7]) {
03052         case '1':
03053             return CONVENTIONAL_COLUMN;
03054         case '2':
03055             return DIRECTLY_HEATED_COLUMN;
03056         default:                
03057             return NO_COLUMN;
03058     }
03059 }
03060 
03061 
03062 /*
03063     Displays the data on the column page, by copying it to the relevant easyGUI variables,
03064     and calling the relevant functions to actually display it.
03065     
03066     Args: a boolean specifying whether or not the display is actually to be updated.
03067           Even if it is false when we are called, we may set it true if we discover
03068           one or more data items has changed - but note that we will not set it false 
03069           if it is already true. If it is true after we have looked at all 
03070           the home page data, we will then update the display. (If we are called
03071           with this value set to false, the caller is effectively saying
03072           'display the column page data only if it has changed').
03073           
03074           The column type.
03075           
03076           The page number (this varies according to the column type - if the caller knows the column type,
03077           it must also (in effect) know the page number, so it seems ridiculous for this function 
03078           to work it out again from the column type)
03079           
03080     No return code.
03081 */
03082 void GetGCStatusLoop::DisplayColumnPageData(bool mustUpdateDisplay, ColumnType columnType, int pageNumber)
03083 {
03084 //    EasyGUIDebugPrint("Column Page", 100, 100);
03085     // Column temperature and maximum temperature
03086     char buff[40];
03087     
03088 #ifdef WANT_DOOR_ACTUATOR_BUTTONS_ON_COLUMN_PAGES
03089     if(DoorActuatorButtonsHaveChanged(usbDevice, usbHostGC)) {
03090         mustUpdateDisplay = true;
03091     }
03092 
03093     SetupDoorActuatorCommandUserInterface(usbDevice, usbHostGC, true);
03094 #endif // WANT_DOOR_ACTUATOR_BUTTONS_ON_COLUMN_PAGES
03095 
03096     GetColumnTemperature(buff, false, true);
03097     if(strcmp(buff, GuiVar_columnTemperature) != 0) {
03098         strcpy(GuiVar_columnTemperature, buff);
03099 
03100         //mustUpdateDisplay = true;
03101         // No - just do this (don't force entire rectangle to redisplay)
03102         // Hard coded values taken from easyGUI
03103         RedrawSingleEasyGUIVariableOnComponentPage(400, 110, &GuiVar_columnTemperature, GuiLib_ALIGN_LEFT, COLUMN);
03104     }
03105     
03106     GetColumnTargetTemperature(buff, stringFormatdegCUnits);
03107     if(strcmp(buff, GuiVar_columnTargetTemperature2) != 0) {
03108         strcpy(GuiVar_columnTargetTemperature2, buff);
03109 
03110         //mustUpdateDisplay = true;
03111         // No - just do this (don't force entire rectangle to redisplay)
03112         // Hard coded values taken from easyGUI
03113         RedrawSingleEasyGUIVariableOnComponentPage(400, 160, &GuiVar_columnTargetTemperature2, GuiLib_ALIGN_LEFT, COLUMN);
03114     }
03115     
03116     GetColumnMaxTemperature(buff, false, false, true);
03117     if(strcmp(buff, GuiVar_columnMaxTemp2) != 0) {
03118         strcpy(GuiVar_columnMaxTemp2, buff);
03119 
03120         //mustUpdateDisplay = true;
03121         // No - just do this (don't force entire rectangle to redisplay)
03122         // Hard coded values taken from easyGUI
03123         RedrawSingleEasyGUIVariableOnComponentPage(400, 230, &GuiVar_columnMaxTemp2, GuiLib_ALIGN_LEFT, COLUMN);
03124     }
03125 
03126     GetComponentStatusString(COLUMN, buff);
03127     if(strcmp(buff, GuiVar_columnStatus) != 0) {
03128         strcpy(GuiVar_columnStatus, buff);
03129 
03130         //mustUpdateDisplay = true;
03131         // No - just do this (don't force entire rectangle to redisplay)
03132         // Hard coded values taken from easyGUI
03133         RedrawSingleEasyGUIVariableOnComponentPage(400, 290, &GuiVar_columnStatus, GuiLib_ALIGN_LEFT, COLUMN);
03134     }
03135 
03136     if(SinglePageGCComponentStatusHasChanged(COLUMN, lastColumnStatusDisplayedOnColumnPage)) {
03137         mustUpdateDisplay = true;
03138     }
03139 
03140     if(mustUpdateDisplay) {
03141         
03142 #ifdef WANT_COLUMN_STATUS_RECTANGLE
03143         // Reduce display flickering - get the component status from the GC
03144         // *before* we call GuiLib_Clear()
03145         if(singleGCComponentPageStatusColorAreas != NULL) {
03146             UpdateSingleGCComponentPageStatusColorArea(COLUMN);
03147         }
03148 #endif
03149         
03150 #ifndef WANT_COLUMN_STATUS_RECTANGLE
03151 #define WANT_GUILIB_CLEAR // Want this, if no status rectangle
03152 #endif
03153 
03154 #ifdef WANT_GUILIB_CLEAR
03155 #ifdef USING_BACKGROUND_BITMAP
03156         DrawBackgroundBitmap(); // We want the status rectangles to be 'on top' of this - 
03157                                 // if we include it in the easyGUI page itself, it gets drawn by GuiLib_ShowScreen,
03158                                 // and overwrites the rectangles
03159 #else
03160         GuiLib_Clear();
03161 #endif
03162 #undef WANT_GUILIB_CLEAR
03163 #endif
03164         //...except that redrawing the status rectangle effectively clears the text on top of the rectangle -
03165         // so we do not need GuiLib_Clear here, since all the column page data appears on top of the rectangle. 
03166         // Without it, only the text flickers, not the rectangle
03167 
03168 #ifdef WANT_COLUMN_STATUS_RECTANGLE
03169         // Note - we draw the status rectangle after GuiLib_Clear - otherwise we wouldn't see the rectangles at all - 
03170         // and before GuiLib_ShowScreen - so text, etc, is drawn on top of the rectangles
03171         if(singleGCComponentPageStatusColorAreas != NULL) {
03172             singleGCComponentPageStatusColorAreas->DisplayGCComponentStatus(COLUMN);
03173             
03174             lastColumnStatusDisplayedOnColumnPage = singleGCComponentPageStatusColorAreas->GetGCComponentStatus(COLUMN);
03175         }
03176 #endif        
03177         // And (currently) we draw the component bitmap on top of the rectangle
03178         if(qspiBitmaps != NULL) {
03179             qspiBitmaps->DisplayColumnComponentBitmap();
03180         }
03181 
03182         GuiLib_ShowScreen(pageNumber, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
03183     
03184 #ifdef WANT_DOOR_ACTUATOR_BUTTONS_ON_COLUMN_PAGES
03185         SetupDoorActuatorCommandUserInterface(usbDevice, usbHostGC, false);
03186 #endif // WANT_DOOR_ACTUATOR_BUTTONS_ON_COLUMN_PAGES
03187 
03188         GuiLib_Refresh();    
03189 
03190 #define DEBUG_HERE
03191 #ifdef DEBUG_HERE
03192     char dbg[100];
03193     sprintf(dbg, "After GuiLib_Clear 2");
03194     EasyGUIDebugPrint(dbg, 0, 20);
03195 #undef DEBUG_HERE
03196 #endif
03197     }
03198 }
03199 
03200 /*
03201     Gets the column length and returns it as a null-terminated string.
03202     
03203     This involves getting the value from the settings stored in QSPI memory.
03204     
03205     Args: pointer to a buffer to contain the column length, as a null-terminated string.
03206           This buffer must be at least 10 chars long.
03207           
03208     No return code.
03209 */
03210 void GetGCStatusLoop::GetColumnLength(char *columnLengthBuffer)
03211 {
03212     if(SettingsHandler::GetSettingValueFromQSPI("ColumnLength", columnLengthBuffer, 10) == 0) {
03213         // Failed to read the setting - supply a default
03214         columnLengthBuffer[0] = '0';
03215         columnLengthBuffer[1] = '\0';
03216     }
03217 }
03218 
03219 /*
03220     Gets the column inner diameter and returns it as a null-terminated string.
03221     
03222     This involves getting the value from the settings stored in QSPI memory.
03223     
03224     Args: pointer to a buffer to contain the column inner diameter, as a null-terminated string.
03225           This buffer must be at least 10 chars long.
03226           
03227     No return code.
03228 */
03229 void GetGCStatusLoop::GetColumnInnerDiameter(char *columnIDBuffer)
03230 {
03231     if(SettingsHandler::GetSettingValueFromQSPI("ColumnInnerDiameter", columnIDBuffer, 10) == 0) {
03232         // Failed to read the setting - supply a default
03233         columnIDBuffer[0] = '0';
03234         columnIDBuffer[1] = '\0';
03235     }
03236 }
03237 
03238 /*
03239     Gets the column inner diameter and returns it as a null-terminated string.
03240     
03241     This involves getting the value from the settings stored in QSPI memory.
03242     
03243     Args: pointer to a buffer to contain the column outer diameter, as a null-terminated string.
03244           This buffer must be at least 10 chars long.
03245           
03246     No return code.
03247 */
03248 void GetGCStatusLoop::GetColumnOuterDiameter(char *columnODBuffer)
03249 {
03250     if(SettingsHandler::GetSettingValueFromQSPI("ColumnOuterDiameter", columnODBuffer, 10) == 0) {
03251         // Failed to read the setting - supply a default
03252         columnODBuffer[0] = '0';
03253         columnODBuffer[1] = '\0';
03254     }
03255 }
03256 
03257 /*
03258     Gets the number of injections (i.e. runs) since the column was installed
03259     and returns it as a null-terminated string.
03260     
03261     This involves getting both values from the settings stored in QSPI memory.
03262     
03263     Args: pointer to a buffer to contain the injection count, as a null-terminated string
03264           
03265     No return code.
03266 */
03267 void GetGCStatusLoop::GetInjectionCountSinceColumnInstalled(char *injCount)
03268 {
03269     int runCount = SettingsHandler::GetIntegerValueFromQSPISettings("RunCount", 0);
03270     int columnInstalledRunCount = SettingsHandler::GetIntegerValueFromQSPISettings("RunCountWhenColumnInstalled", 0);
03271 
03272     sprintf(injCount, "%d", (runCount - columnInstalledRunCount));
03273 }
03274 
03275 /*
03276     Displays the data on the column information page, by copying it to the relevant easyGUI variables,
03277     and calling the relevant functions to actually display it.
03278     
03279     Args: a boolean specifying whether or not the display is actually to be updated.
03280           Even if it is false when we are called, we may set it true if we discover
03281           one or more data items has changed - but note that we will not set it false 
03282           if it is already true. If it is true after we have looked at all 
03283           the home page data, we will then update the display. (If we are called
03284           with this value set to false, the caller is effectively saying
03285           'display the column page data only if it has changed').
03286           
03287           The column type.
03288           
03289           The page number (this varies according to the column type - if the caller knows the column type,
03290           it must also (in effect) know the page number, so it seems ridiculous for this function 
03291           to work it out again from the column type)
03292           
03293           TODO: We no longer have separate pages for the conventional and directly heated columns -
03294                 *** update this function ***
03295           
03296     No return code.
03297 */
03298 void GetGCStatusLoop::DisplayColumnInformationPageData(bool mustUpdateDisplay, ColumnType columnType, int pageNumber)
03299 {
03300 //    EasyGUIDebugPrint("Column Information Page", 100, 100);
03301     // Column temperature and maximum temperature
03302     char buff[40];
03303     
03304 #ifdef WANT_DOOR_ACTUATOR_BUTTONS_ON_COLUMN_PAGES
03305     if(DoorActuatorButtonsHaveChanged(usbDevice, usbHostGC)) {
03306         mustUpdateDisplay = true;
03307     }
03308 
03309     SetupDoorActuatorCommandUserInterface(usbDevice, usbHostGC, true);
03310 #endif // WANT_DOOR_ACTUATOR_BUTTONS_ON_COLUMN_PAGES
03311 
03312     GetColumnType(buff);
03313     if(strcmp(buff, GuiVar_columnType) != 0) {
03314         mustUpdateDisplay = true;
03315         
03316         strcpy(GuiVar_columnType, buff);
03317     }
03318     
03319     GetColumnMaxTemperature(buff, false, false, true);
03320     if(strcmp(buff, GuiVar_columnMaxTemp2) != 0) {
03321         strcpy(GuiVar_columnMaxTemp2, buff);
03322 
03323         //mustUpdateDisplay = true;
03324         // No - just do this (don't force entire page to redisplay)
03325         // Hard coded values taken from easyGUI
03326         RedrawSingleEasyGUIVariableOnComponentPage(400, 150, &GuiVar_columnMaxTemp2, GuiLib_ALIGN_LEFT, COLUMN);
03327     }
03328 
03329     
03330     GetColumnLength(buff);
03331     if(strcmp(buff, GuiVar_columnLength) != 0) {
03332         mustUpdateDisplay = true;
03333         
03334         strcpy(GuiVar_columnLength, buff);
03335     }
03336     
03337     GetColumnInnerDiameter(buff);
03338     if(strcmp(buff, GuiVar_columnInnerDiameter) != 0) {
03339         mustUpdateDisplay = true;
03340         
03341         strcpy(GuiVar_columnInnerDiameter, buff);
03342     }
03343     
03344     GetColumnOuterDiameter(buff);
03345     if(strcmp(buff, GuiVar_columnOuterDiameter) != 0) {
03346         mustUpdateDisplay = true;
03347         
03348         strcpy(GuiVar_columnOuterDiameter, buff);
03349     }
03350     
03351     if(SinglePageGCComponentStatusHasChanged(COLUMN)) {
03352         mustUpdateDisplay = true;
03353     }
03354 
03355     if(mustUpdateDisplay) {
03356 
03357 #ifdef WANT_COLUMN_STATUS_RECTANGLE        
03358         // Reduce display flickering - get the component status from the GC
03359         // *before* we call GuiLib_Clear()
03360         if(singleGCComponentPageStatusColorAreas != NULL) {
03361             UpdateSingleGCComponentPageStatusColorArea(COLUMN);
03362         }
03363 #endif       
03364 
03365 #ifndef WANT_COLUMN_STATUS_RECTANGLE
03366 #define WANT_GUILIB_CLEAR
03367 #endif
03368 
03369 #ifdef WANT_GUILIB_CLEAR
03370 #ifdef USING_BACKGROUND_BITMAP
03371         DrawBackgroundBitmap(); // We want the status rectangles to be 'on top' of this - 
03372                                 // if we include it in the easyGUI page itself, it gets drawn by GuiLib_ShowScreen,
03373                                 // and overwrites the rectangles
03374 #else
03375         GuiLib_Clear();
03376 #endif
03377 #undef WANT_GUILIB_CLEAR
03378 #endif
03379         //...except that redrawing the status rectangle effectively clears the text on top of the rectangle -
03380         // so we do not need GuiLib_Clear here, since all the column page data appears on top of the rectangle. 
03381         // Without it, only the text flickers, not the rectangle
03382 
03383 #ifdef WANT_COLUMN_STATUS_RECTANGLE
03384         // Note - we draw the status rectangle after GuiLib_Clear - otherwise we wouldn't see the rectangles at all - 
03385         // and before GuiLib_ShowScreen - so text, etc, is drawn on top of the rectangles
03386         if(singleGCComponentPageStatusColorAreas != NULL) {
03387             singleGCComponentPageStatusColorAreas->DisplayGCComponentStatus(COLUMN);
03388         }
03389 #endif
03390 
03391         // And (currently) we draw the component bitmap on top of the rectangle
03392         if(qspiBitmaps != NULL) {
03393             qspiBitmaps->DisplayColumnComponentBitmap();
03394         }
03395 
03396         GuiLib_ShowScreen(pageNumber, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
03397     
03398 #ifdef WANT_DOOR_ACTUATOR_BUTTONS_ON_COLUMN_PAGES
03399         SetupDoorActuatorCommandUserInterface(usbDevice, usbHostGC, false);
03400 #endif // WANT_DOOR_ACTUATOR_BUTTONS_ON_COLUMN_PAGES
03401 
03402         GuiLib_Refresh();    
03403 
03404 #define DEBUG_HERE
03405 #ifdef DEBUG_HERE
03406     char dbg[100];
03407     sprintf(dbg, "After GuiLib_Clear 9");
03408     EasyGUIDebugPrint(dbg, 0, 20);
03409 #undef DEBUG_HERE
03410 #endif
03411     }
03412 }
03413 
03414 
03415 /*
03416     Gets the initial hold time, and returns it as a null-terminated string.
03417     
03418     This code is intended to be as efficient as possible.
03419     
03420     Args: pointer to a buffer to contain the run time, as a null-terminated string
03421           
03422     No return code.
03423 */
03424 void GetGCStatusLoop::GetInitialHoldTime(char *time)
03425 {
03426     char response[50];
03427     SetGCDeviceReport("GTIM", response);
03428 
03429     // We expect a response like this: "DTIM1234", with run time in units of 0.1 min
03430     int index = 0;
03431     
03432     bool wantNextChars = false;
03433     if(response[4] != '0') {
03434         time[index++] = response[4];
03435         wantNextChars = true;
03436     }
03437     if(wantNextChars || (response[5] != '0')) {
03438         time[index++] = response[5];
03439     }
03440     // If the value is zero, make sure we return "0.0" - 
03441     // we just don't want any zeroes before that
03442     time[index++] = response[6];
03443     time[index++] = '.';
03444     time[index++] = response[7];
03445 }
03446 
03447 /*
03448     Shows or hides, as required, the scroll up and down buttons
03449     on whichever "XXX Method" page is being displayed.
03450     Currently, we display the scroll buttons at the same coordinates 
03451     on all three method pages, i.e. column, injector and gas.
03452     If these buttons are ever moved from these positions,
03453     this function will need to be changed.
03454     *************************************
03455     
03456     Args: a boolean saying whether or not to display the scroll buttons
03457     
03458     No return code.
03459     
03460     It is up to the caller (1) to decide whether the ramps need to be scrolled or not,
03461     and (2) to verify that an XXX Method page actually *is* the page currently being displayed
03462 */
03463 void GetGCStatusLoop::ShowMethodPageScrollButtons(bool needToScrollRampData)
03464 {
03465 #define USE_QSPI_ARROW_BITMAPS    
03466     if(needToScrollRampData){
03467         // Hard coded coords obtained - these will need to be changed
03468         // if we change the layout of the Column Method page in easyGUI
03469 #ifdef USE_QSPI_ARROW_BITMAPS
03470         if(qspiBitmaps != NULL) {
03471             qspiBitmaps->DisplayUpArrowBitmap(620, 250);
03472             qspiBitmaps->DisplayDownArrowBitmap(620, 350);
03473         }
03474 #else
03475         GuiLib_ShowBitmap(GuiStruct_Bitmap_UpArrow, 630, 260, -1); // No transparency
03476         GuiLib_ShowBitmap(GuiStruct_Bitmap_DownArrow, 630, 345, -1);
03477 #endif
03478     } else {
03479         // Blank out the whole area where the scroll buttons appear.
03480         // Note that, as always, the first coordinate pair in the call to 'GuiLib_ShowBitmapArea' has to be (0, 0)
03481         // to display the correct part of the bitmap - not, as one might expect, the same as the second coordinate pair
03482 #ifdef USE_QSPI_ARROW_BITMAPS
03483         GuiLib_ShowBitmapArea(GuiStruct_Bitmap_BlankBackground, 0, 0, 620, 250, 670, 390, -1); // -1 means 'no transparent colour'
03484 #undef USE_QSPI_ARROW_BITMAPS
03485 #else
03486         GuiLib_ShowBitmapArea(GuiStruct_Bitmap_BlankBackground, 0, 0, 630, 250, 670, 390, -1); // -1 means 'no transparent colour'
03487 #endif
03488     }
03489 }
03490 
03491 /*
03492     General function to show or hide the scroll buttons on one of the XXX Method pages.
03493     
03494     Args: pointer to the "MethodRampData" instance that is controlling this.
03495 */
03496 void GetGCStatusLoop::ShowMethodPageScrollButtonsIfNecessary(MethodRampData *methodRampData)
03497 {
03498     bool needToScrollRampData = false;
03499     
03500     if(methodRampData != NULL) {
03501         needToScrollRampData = (methodRampData->GetScrollRange() > 0);
03502     }
03503 
03504     ShowMethodPageScrollButtons(needToScrollRampData);
03505 }
03506 
03507 /*
03508     Public function, allowing an external caller to tell us 
03509     to display, or not, the ramp scroll buttons on the Column Method page.
03510     
03511     Assume that the caller knows the Column Method page *is*
03512     currently being displayed.
03513 */
03514 void GetGCStatusLoop::ShowColumnMethodPageScrollButtonsIfNecessary(void)
03515 {
03516     ShowMethodPageScrollButtonsIfNecessary(columnMethodRampData);
03517 }
03518 
03519 void GetGCStatusLoop::ScrollColumnMethodRampsUpIfPossible(void)
03520 {
03521     if(currentPage == GuiStruct_ColumnMethodPage_Def) {
03522         if(columnMethodPageScrollIndex > 0) {
03523             --columnMethodPageScrollIndex;
03524                 
03525             DisplayColumnMethodPageData(false);
03526         }
03527     }
03528 }
03529 
03530 void GetGCStatusLoop::ScrollColumnMethodRampsDownIfPossible(void)
03531 {
03532     if(currentPage == GuiStruct_ColumnMethodPage_Def) {
03533         if(columnMethodRampData != NULL) {
03534             if(columnMethodPageScrollIndex < columnMethodRampData->GetScrollRange()) {
03535                 ++columnMethodPageScrollIndex;
03536                 
03537                 DisplayColumnMethodPageData(false);
03538             }
03539         }
03540     }
03541 }
03542 
03543 /*
03544     Displays the data on the column method page, by copying it to the relevant easyGUI variables,
03545     and calling the relevant functions to actually display it.
03546     
03547     Args: a boolean specifying whether or not the display is actually to be updated.
03548           Even if it is false when we are called, we may set it true if we discover
03549           one or more data items has changed - but note that we will not set it false 
03550           if it is already true. If it is true after we have looked at all 
03551           the home page data, we will then update the display. (If we are called
03552           with this value set to false, the caller is effectively saying
03553           'display the column page data only if it has changed').
03554           
03555     No return code.
03556 */
03557 void GetGCStatusLoop::DisplayColumnMethodPageData(bool mustUpdateDisplay)
03558 {
03559     //    EasyGUIDebugPrint("Column Method Page", 100, 100);
03560     // Column temperature and maximum temperature
03561     char buff[40];
03562     
03563 #ifdef WANT_DOOR_ACTUATOR_BUTTONS_ON_COLUMN_PAGES
03564     if(DoorActuatorButtonsHaveChanged(usbDevice, usbHostGC)) {
03565         mustUpdateDisplay = true;
03566     }
03567 
03568     SetupDoorActuatorCommandUserInterface(usbDevice, usbHostGC, true);
03569 #endif // WANT_DOOR_ACTUATOR_BUTTONS_ON_COLUMN_PAGES
03570 
03571     GetColumnTargetTemperature(buff, "%s");
03572     if(strcmp(buff, GuiVar_columnMethodInitialTemp) != 0) {
03573         strcpy(GuiVar_columnMethodInitialTemp, buff);
03574 
03575         //mustUpdateDisplay = true;
03576         // No - just do this (don't force entire page to redisplay)
03577         // Hard coded values taken from easyGUI
03578         RedrawSingleEasyGUIVariableOnComponentPage(520, 85, &GuiVar_columnMethodInitialTemp, GuiLib_ALIGN_RIGHT, COLUMN);
03579     }
03580 
03581     GetInitialHoldTime(buff);
03582     if(strcmp(buff, GuiVar_columnMethodInitialHold) != 0) {
03583         strcpy(GuiVar_columnMethodInitialHold, buff);
03584 
03585         //mustUpdateDisplay = true;
03586         // No - just do this (don't force entire page to redisplay)
03587         // Hard coded values taken from easyGUI
03588         RedrawSingleEasyGUIVariableOnComponentPage(520, 120, &GuiVar_columnMethodInitialHold, GuiLib_ALIGN_RIGHT, COLUMN);
03589     }
03590 
03591     if(columnMethodRampData == NULL) {
03592         columnMethodRampData = new ColumnMethodRampData(usbDevice, usbHostGC);
03593     }
03594     
03595     if(columnMethodRampData != NULL) {
03596         if(!columnMethodRampData->GotRampData()) {
03597             columnMethodRampData->GetRampDataFromGC();
03598         }
03599         
03600         sprintf(buff, "%u", columnMethodRampData->GetRampCount());
03601         if(strcmp(buff, GuiVar_columnMethodRampCount) != 0) {
03602             strcpy(GuiVar_columnMethodRampCount, buff);
03603     
03604             //mustUpdateDisplay = true;
03605             // No - just do this (don't force entire page to redisplay)
03606             // Hard coded values taken from easyGUI
03607             RedrawSingleEasyGUIVariableOnComponentPage(520, 155, &GuiVar_columnMethodRampCount, GuiLib_ALIGN_RIGHT, COLUMN);
03608         }
03609         
03610         if(columnMethodRampData->NeedToUpdateEasyGUIMethodPageRampVariables()) {
03611             
03612             columnMethodPageScrollIndex = 0;
03613             
03614             columnMethodRampData->UpdateEasyGUIMethodPageVariables(columnMethodPageScrollIndex);
03615             
03616             previousColumnMethodPageScrollIndex = columnMethodPageScrollIndex;
03617             
03618             mustUpdateDisplay = true; // Not practical to write all these variables individually
03619 
03620         } else if (previousColumnMethodPageScrollIndex != columnMethodPageScrollIndex) {
03621 
03622             columnMethodRampData->UpdateEasyGUIMethodPageVariables(columnMethodPageScrollIndex);
03623             
03624             previousColumnMethodPageScrollIndex = columnMethodPageScrollIndex;
03625             
03626             mustUpdateDisplay = true; // Not practical to write all these variables individually
03627         }
03628     }
03629     
03630     if(SinglePageGCComponentStatusHasChanged(COLUMN)) {
03631         mustUpdateDisplay = true;
03632     }
03633 
03634     if(mustUpdateDisplay) {
03635 
03636 #ifdef WANT_COLUMN_STATUS_RECTANGLE        
03637         // Reduce display flickering - get the component status from the GC
03638         // *before* we call GuiLib_Clear()
03639         if(singleGCComponentPageStatusColorAreas != NULL) {
03640             UpdateSingleGCComponentPageStatusColorArea(COLUMN);
03641         }
03642 #endif       
03643 
03644 #ifndef WANT_COLUMN_STATUS_RECTANGLE
03645 #define WANT_GUILIB_CLEAR
03646 #endif
03647 
03648 #ifdef WANT_GUILIB_CLEAR
03649 #ifdef USING_BACKGROUND_BITMAP
03650         DrawBackgroundBitmap(); // We want the status rectangles to be 'on top' of this - 
03651                                 // if we include it in the easyGUI page itself, it gets drawn by GuiLib_ShowScreen,
03652                                 // and overwrites the rectangles
03653 #else
03654         GuiLib_Clear();
03655 #endif
03656 #undef WANT_GUILIB_CLEAR
03657 #endif
03658         //...except that redrawing the status rectangle effectively clears the text on top of the rectangle -
03659         // so we do not need GuiLib_Clear here, since all the column page data appears on top of the rectangle. 
03660         // Without it, only the text flickers, not the rectangle
03661 
03662 #ifdef WANT_COLUMN_STATUS_RECTANGLE
03663         // Note - we draw the status rectangle after GuiLib_Clear - otherwise we wouldn't see the rectangles at all - 
03664         // and before GuiLib_ShowScreen - so text, etc, is drawn on top of the rectangles
03665         if(singleGCComponentPageStatusColorAreas != NULL) {
03666             singleGCComponentPageStatusColorAreas->DisplayGCComponentStatus(COLUMN);
03667         }
03668 #endif
03669 
03670         // And (currently) we draw the component bitmap on top of the rectangle
03671         if(qspiBitmaps != NULL) {
03672             qspiBitmaps->DisplayColumnComponentBitmap();
03673         }
03674 
03675         GuiLib_ShowScreen(GuiStruct_ColumnMethodPage_Def, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
03676     
03677 #ifdef WANT_DOOR_ACTUATOR_BUTTONS_ON_COLUMN_PAGES
03678         SetupDoorActuatorCommandUserInterface(usbDevice, usbHostGC, false);
03679 #endif // WANT_DOOR_ACTUATOR_BUTTONS_ON_COLUMN_PAGES
03680 
03681         ShowMethodPageScrollButtonsIfNecessary(columnMethodRampData);
03682 
03683         GuiLib_Refresh();    
03684     }
03685 }
03686 
03687 
03688 
03689 /*
03690     Column temperature profile X axis units may be minutes or seconds.
03691     Sets up the label easyGUI variable appropriately.
03692 */
03693 void GetGCStatusLoop::SetupColumnTempProfilePageXAxisLabel(TimeUnit timeUnit)
03694 {
03695     if(timeUnit == SECONDS) {
03696         strcpy(GuiVar_columnTempProfilePageXAxisLabel, "Time (seconds)");
03697     } else {
03698         strcpy(GuiVar_columnTempProfilePageXAxisLabel, "Time (minutes)");
03699     }
03700 }
03701 
03702 /*
03703     Injector temperature profile X axis units may be minutes or seconds.
03704     Sets up the label easyGUI variable appropriately.
03705 */
03706 void GetGCStatusLoop::SetupInjectorTempProfilePageXAxisLabel(TimeUnit timeUnit)
03707 {
03708     if(timeUnit == SECONDS) {
03709         strcpy(GuiVar_injectorTempProfilePageXAxisLabel, "Time (seconds)");
03710     } else {
03711         strcpy(GuiVar_injectorTempProfilePageXAxisLabel, "Time (minutes)");
03712     }
03713 }
03714 
03715 
03716 /*
03717     Sets up the data for the graph on either the Column Temperature Profile page,
03718     or the Column (directly heated) Temperature Profile page,
03719     and causes it to be displayed
03720 */
03721 void GetGCStatusLoop::DisplayColumnTempProfilePageGraph(ColumnType columnType)
03722 {
03723     GuiLibGraph* graphToUse = columnTempProfilePageGraph;
03724     
03725     if(graphToUse == NULL) {
03726         return; // Nothing to do
03727     }
03728     
03729     // - dataset 0 is a line representing the temperature profile of the run from 'now' to the finish
03730     // - dataset 1 is a bar chart representing the temperature profile of the run from 'now' to the finish
03731     // - dataset 2 is a line representing the temperature profile from the start to 'now'
03732     // - dataset 3 is a bar chart representing the temperature profile of the run from the start to 'now'
03733     // - dataset 4 is a single dot at the current time and temperature
03734     
03735     TimeUnit timeUnit = MINUTES;
03736     if(columnTempProfilePageGraphCompleteProfileDataSet->GetTotalMethodTime() < methodTimeUnitsThreshold) {
03737         timeUnit = SECONDS;
03738     }
03739     
03740     const float yAxisScaleFactor = 10.0f;
03741     
03742     // Dataset 0
03743     graphToUse->SetDataForGraphDataSetInTenthsOfMinutes(0, yAxisScaleFactor, columnTempProfilePageGraphDataSet0);
03744     
03745     // Dataset 1 
03746     graphToUse->SetDataForGraphDataSetInTenthsOfMinutes(1, yAxisScaleFactor, columnTempProfilePageGraphDataSet1);
03747     
03748     // Dataset 2
03749     graphToUse->SetDataForGraphDataSetInTenthsOfMinutes(2, yAxisScaleFactor, columnTempProfilePageGraphDataSet2);
03750     
03751     // Dataset 3
03752     graphToUse->SetDataForGraphDataSetInTenthsOfMinutes(3, yAxisScaleFactor, columnTempProfilePageGraphDataSet3);
03753     
03754     // Dataset 4
03755     graphToUse->SetDataForGraphDataSetInTenthsOfMinutes(4, yAxisScaleFactor, columnTempProfilePageGraphDataSet4);
03756     
03757     graphToUse->SetXAxisUnits(timeUnit);
03758 
03759     // The tick sizes must match those set in easyGUI - we do not seem 
03760     // to be able to get them at runtime
03761     GuiConst_INT32S minX, maxX;
03762     //GuiConst_INT32S tickSize = (timeUnit == SECONDS) ? 300 : 100;
03763     // Always in 1/10 minutes
03764     GuiConst_INT32S xAxisTickSize = (timeUnit == SECONDS) ? 5 : 100; // 5 == 0.5 minutes (i.e. 30 seconds), 100 == 10 minutes
03765     columnTempProfilePageGraphCompleteProfileDataSet->GetXAxisRangeInTenthsOfMinutes(&minX, &maxX, xAxisTickSize );
03766     graphToUse->SetXAxisRange(0, maxX); // Always start X axis at zero
03767     
03768     GuiConst_INT32S minY, maxY;
03769     GuiConst_INT32S yAxisTickSize = 50;
03770     columnTempProfilePageGraphCompleteProfileDataSet->GetYAxisRange(&minY, &maxY, yAxisTickSize);
03771     graphToUse->SetYAxisRange(0, (maxY * yAxisScaleFactor)); // Always start Y axis at zero
03772     
03773     
03774     graphToUse->DrawAxes();
03775     
03776     // Graph coordinates copied from easyGUI - I cannot find a way of obtaining them at runtime.
03777     // We need to draw the X axis labels ourselves, since our time values are in units of 0.1 minute,
03778     // but easyGUI graphs work only in integers - we therefore have to multiply the time values by 10
03779     // before passing them to easyGUI, and if we let easyGUI draw its own values on the X axis, 
03780     // they would be 10 times the correct values.
03781     if(timeUnit == SECONDS) {
03782         graphToUse->DrawXAxisLabels(0, (maxX * 6), (xAxisTickSize * 6), 130, 340, 500);
03783     } else {
03784         graphToUse->DrawXAxisLabels(0, (maxX / 10), (xAxisTickSize / 10), 130, 340, 500);
03785     }
03786     // Note that we repeat this call by calling 'DrawXAxisLabels' without arguments 
03787     // every time we (re)display this page
03788 
03789     // Similar to the X axis - the values we pass to the easyGUI graph are scaled to be larger
03790     // than the real values, to get round the fact that easyGUI graphs work in integers.
03791     // (This can cause the profile to appear to be 'stepped', which obviously we do not want.)
03792     // We must therefore draw the Y axis values ourselves.
03793     graphToUse->DrawYAxisLabels(0, maxY, yAxisTickSize, 130, 340, 250);
03794     // Note that we repeat this call by calling 'DrawYAxisLabels' without arguments 
03795     // every time we (re)display this page
03796 
03797     graphToUse->DrawDataSet(0);
03798     graphToUse->ShowDataSet(0);
03799 
03800     graphToUse->DrawDataSet(1);
03801     graphToUse->ShowDataSet(1);
03802 
03803     graphToUse->DrawDataSet(2);
03804     graphToUse->ShowDataSet(2);
03805 
03806     graphToUse->DrawDataSet(3);
03807     graphToUse->ShowDataSet(3);
03808 
03809     graphToUse->DrawDataSet(4);
03810     graphToUse->ShowDataSet(4);
03811 
03812     graphToUse->Redraw();
03813     
03814 #ifdef TEST_GUILIB_VLINE_PROFILES
03815     // *** TESTING *** Draw the profile as a solid colour, direct to the display
03816     //                 Coords manually copied from easyGUI application.
03817     //                 Boundary between colours is arbitrary for now
03818     GuiConst_INTCOLOR graphColour1 = SixteenBitColorValue(100, 100, 100);
03819     GuiConst_INTCOLOR graphColour2 = SixteenBitColorValue(200, 200, 200);
03820 //    double colourBoundaryX = (double) columnTempProfilePageGraphCompleteProfileDataSet->GetTotalMethodTime() / 5.0; // Should be one-fifth across the profile
03821     double colourBoundaryX = -1.0; // No - use one colour only
03822     if(timeUnit == SECONDS) {
03823         columnTempProfilePageGraphCompleteProfileDataSet->DrawUsingGuiLibVLine(140, 330, ((double) 500 / (double) (maxX * 6)), ((double) -250 / (double) maxY), graphColour1, graphColour2, colourBoundaryX);
03824     } else {
03825         columnTempProfilePageGraphCompleteProfileDataSet->DrawUsingGuiLibVLine(140, 330, ((double) 500 / (double) (maxX / 10)), ((double) -250 / (double) maxY), graphColour1, graphColour2, colourBoundaryX);
03826     }
03827 #endif // TEST_GUILIB_VLINE_PROFILES
03828 }
03829 
03830 /*
03831     Displays the data on the column temperature profile page, 
03832     by copying it to the relevant easyGUI variables,
03833     and calling the relevant functions to actually display it.
03834     
03835     Args: a boolean specifying whether or not the display is actually to be updated.
03836           Even if it is false when we are called, we may set it true if we discover
03837           one or more data items has changed - but note that we will not set it false 
03838           if it is already true. If it is true after we have looked at all 
03839           the home page data, we will then update the display. (If we are called
03840           with this value set to false, the caller is effectively saying
03841           'display the column temperature page data only if it has changed').
03842           
03843     No return code.
03844 */
03845 void GetGCStatusLoop::DisplayColumnTempProfilePageData(bool mustUpdateDisplay, ColumnType columnType, int pageNumber)
03846 {
03847     // Ensure this is always up to date
03848     if(needToUpdateProfileGraphs) {
03849         SetupColumnAndInjectorAndGasProfileData();
03850         mustUpdateDisplay = true;
03851 
03852         needToUpdateProfileGraphs = false;
03853     }
03854 
03855     DisplayColumnTempProfilePageGraph(columnType);
03856     
03857 #ifdef WANT_DOOR_ACTUATOR_BUTTONS_ON_COLUMN_PAGES
03858     if(DoorActuatorButtonsHaveChanged(usbDevice, usbHostGC)) {
03859         mustUpdateDisplay = true;
03860     }
03861 
03862     SetupDoorActuatorCommandUserInterface(usbDevice, usbHostGC, true);
03863 #endif // WANT_DOOR_ACTUATOR_BUTTONS_ON_COLUMN_PAGES
03864 
03865     if(SinglePageGCComponentStatusHasChanged(COLUMN)) {
03866         mustUpdateDisplay = true;
03867     }
03868 
03869     if(mustUpdateDisplay) {
03870 
03871 #ifdef WANT_COLUMN_STATUS_RECTANGLE
03872         // Reduce display flickering - get the component status from the GC
03873         // *before* we call GuiLib_Clear()
03874         if(singleGCComponentPageStatusColorAreas != NULL) {
03875             UpdateSingleGCComponentPageStatusColorArea(COLUMN);
03876         }
03877 #endif
03878 
03879 #ifndef WANT_COLUMN_STATUS_RECTANGLE        
03880         // Makes the display flicker - but omitting it means old text is not cleared from the display
03881 #define WANT_GUILIB_CLEAR
03882 #endif
03883 
03884 #ifdef WANT_GUILIB_CLEAR
03885 #ifdef USING_BACKGROUND_BITMAP
03886         DrawBackgroundBitmap(); // We want the status rectangles to be 'on top' of this - 
03887                                 // if we include it in the easyGUI page itself, it gets drawn by GuiLib_ShowScreen,
03888                                 // and overwrites the rectangles
03889 #else
03890         GuiLib_Clear();
03891 #endif
03892 #undef WANT_GUILIB_CLEAR
03893 #endif
03894         //...except that redrawing the status rectangle effectively clears the text on top of the rectangle -
03895         // so we do not need GuiLib_Clear here, since all the injector page data appears on top of the rectangle. 
03896         // Without it, only the text flickers, not the rectangle
03897 
03898         // Note - we draw the status rectangle after GuiLib_Clear - otherwise we wouldn't see the rectangles at all - 
03899         // and before GuiLib_ShowScreen - so text, etc, is drawn on top of the rectangles
03900 #ifdef WANT_COLUMN_STATUS_RECTANGLE
03901         if(singleGCComponentPageStatusColorAreas != NULL) {
03902             singleGCComponentPageStatusColorAreas->DisplayGCComponentStatus(COLUMN);
03903         }
03904 #endif
03905 
03906 #ifdef WANT_COMPONENT_ICON_ON_PROFILE_PAGES
03907         // And (currently) we draw the component bitmap on top of the rectangle
03908         if(qspiBitmaps != NULL) {
03909             qspiBitmaps->DisplayColumnComponentBitmap();
03910         }
03911 #endif // WANT_COMPONENT_ICON_ON_PROFILE_PAGES
03912 
03913         GuiLibGraph* graphToUse = columnTempProfilePageGraph;
03914 
03915         if(graphToUse != NULL) {
03916             graphToUse->Redraw();
03917         }
03918     
03919 // Also in DisplayEasyGUIStructure, main.cpp
03920 //#define SWIM_TEST
03921 #ifdef SWIM_TEST
03922         SwimDraw* swimDrawInstance = SwimDraw::GetInstance();
03923         if(swimDrawInstance != NULL) {
03924             // two horizontal boxes
03925             swimDrawInstance->DrawRectangle(SixteenBitColorValue(0xFF, 0, 0), 50, 200, 650, 250);
03926             swimDrawInstance->DrawRectangle(SixteenBitColorValue(0, 0, 0xFF), 50, 350, 650, 400);
03927             
03928             // two vertical boxes
03929             swimDrawInstance->DrawRectangle(SixteenBitColorValue(0xFF, 0, 0), 100, 50, 150, 350);
03930             swimDrawInstance->DrawRectangle(SixteenBitColorValue(0, 0, 0xFF), 500, 50, 550, 350);
03931         }
03932 #else // Draw the same boxes with easyGUI
03933 //        GuiLib_FillBox(50, 200, 650, 250, SixteenBitColorValue(0xFF, 0, 0));
03934 //        GuiLib_FillBox(50, 350, 650, 400, SixteenBitColorValue(0, 0, 0xFF));
03935         
03936 //        GuiLib_FillBox(100, 50, 150, 350, SixteenBitColorValue(0xFF, 0, 0));
03937 //        GuiLib_FillBox(500, 50, 550, 350, SixteenBitColorValue(0, 0, 0xFF));
03938 
03939 // No - draw a dummy profile section
03940 //         DrawProfileSectionUsingGuiLibVLine(SixteenBitColorValue(0xFF, 0, 0), 300, 500, 350, 200, 125);
03941     
03942 #ifdef TEST_GUILIB_VLINE_PROFILES
03943         // Now draw the profile as a solid colour, direct to the display.
03944         // Use the same parameters as the previous call, in DisplayColumnTempProfilePageGraph
03945         columnTempProfilePageGraphCompleteProfileDataSet->DrawUsingGuiLibVLine();
03946 #endif // TEST_GUILIB_VLINE_PROFILES
03947 #endif // SWIM_TEST
03948 
03949         GuiLib_ShowScreen(pageNumber, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
03950         
03951         // Repeat the previous call to the version of this function with parameters,
03952         // made from 'DisplayColumnTempProfilePageGraph()' above (it is more convenient 
03953         // to calculate the parameter values in 'DisplayColumnTempProfilePageGraph()' than here)
03954         if(graphToUse != NULL) {
03955             graphToUse->DrawXAxisLabels();
03956             graphToUse->DrawYAxisLabels();
03957         }
03958             
03959 #ifdef WANT_DOOR_ACTUATOR_BUTTONS_ON_COLUMN_PAGES
03960         SetupDoorActuatorCommandUserInterface(usbDevice, usbHostGC, false);
03961 #endif // WANT_DOOR_ACTUATOR_BUTTONS_ON_COLUMN_PAGES
03962 
03963         GuiLib_Refresh();    
03964         
03965 #define DEBUG_HERE
03966 #ifdef DEBUG_HERE
03967     char dbg[100];
03968     sprintf(dbg, "After GuiLib_Clear 3");
03969     EasyGUIDebugPrint(dbg, 0, 20);
03970 #undef DEBUG_HERE
03971 #endif
03972     }
03973 }
03974 
03975     
03976 /*
03977     Public function that calls DisplayColumnPageData - 
03978     provided so that 'TouchCallback' (main.cpp) can cause 
03979     the column page to be redisplayed in response to the user 
03980     touching one of the up or down buttons on the relevant easyGUI page
03981 */
03982 void GetGCStatusLoop::RedisplayColumnPage(void)
03983 {
03984     if(currentPage == GuiStruct_ColumnPage1_2) {
03985         DisplayColumnPageData(true, CONVENTIONAL_COLUMN, GuiStruct_ColumnPage1_2);
03986     }
03987     // else we are displaying a different page - disastrous to call DisplayColumnPageData()
03988 }
03989 
03990 
03991 void GetGCStatusLoop::DisplayColumnDHAutoCalibrationPageData(bool mustUpdateDisplay)
03992 {
03993     ColumnDHAutoCalibrationPageHandler *columnDHAutoCalibrationPageHandler = ColumnDHAutoCalibrationPageHandler::GetInstance(usbDevice, usbHostGC);
03994     
03995     if(columnDHAutoCalibrationPageHandler != NULL) {
03996         columnDHAutoCalibrationPageHandler->UpdateVolatileEasyGUIVariables();
03997     }             
03998     
03999     if(DoorActuatorButtonsHaveChanged(usbDevice, usbHostGC)) {
04000         mustUpdateDisplay = true;
04001     }
04002 
04003     SetupDoorActuatorCommandUserInterface(usbDevice, usbHostGC, true);
04004     
04005     if(SinglePageGCComponentStatusHasChanged(COLUMN)) {
04006         mustUpdateDisplay = true;
04007     }
04008 
04009     if(mustUpdateDisplay) {
04010 
04011         // Reduce display flickering - get the component status from the GC
04012         // *before* we call GuiLib_Clear()
04013         if(singleGCComponentPageStatusColorAreas != NULL) {
04014             UpdateSingleGCComponentPageStatusColorArea(COLUMN);
04015         }
04016         
04017         // Makes the display flicker - but omitting it means old text is not cleared from the display
04018 //#define WANT_GUILIB_CLEAR
04019 #ifdef WANT_GUILIB_CLEAR
04020 #ifdef USING_BACKGROUND_BITMAP
04021         DrawBackgroundBitmap(); // We want the status rectangles to be 'on top' of this - 
04022                                 // if we include it in the easyGUI page itself, it gets drawn by GuiLib_ShowScreen,
04023                                 // and overwrites the rectangles
04024 #else
04025         GuiLib_Clear();
04026 #endif
04027 #undef WANT_GUILIB_CLEAR
04028 #endif
04029         //...except that redrawing the status rectangle effectively clears the text on top of the rectangle -
04030         // so we do not need GuiLib_Clear here, since all the injector page data appears on top of the rectangle. 
04031         // Without it, only the text flickers, not the rectangle
04032 
04033         // Note - we draw the status rectangle after GuiLib_Clear - otherwise we wouldn't see the rectangles at all - 
04034         // and before GuiLib_ShowScreen - so text, etc, is drawn on top of the rectangles
04035 #ifdef WANT_STATUS_RECTANGLE_ON_COLUMN_AUTO_CALIB_PAGE        
04036         if(singleGCComponentPageStatusColorAreas != NULL) {
04037             singleGCComponentPageStatusColorAreas->DisplayGCComponentStatus(COLUMN);
04038         }
04039 #else // Need to clear old text some other way...
04040 #ifdef USING_BACKGROUND_BITMAP
04041         DrawBackgroundBitmap();
04042 #else
04043         GuiLib_Clear();
04044 #endif // USING_BACKGROUND_BITMAP
04045 #endif // WANT_STATUS_RECTANGLE_ON_COLUMN_AUTO_CALIB_PAGE
04046         GuiLib_ShowScreen(GuiStruct_ColumnDHAutoCalibrationPage_Def, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
04047         
04048         SetupDoorActuatorCommandUserInterface(usbDevice, usbHostGC, false);
04049         
04050         GuiLib_Refresh();    
04051 
04052 #define DEBUG_HERE
04053 #ifdef DEBUG_HERE
04054     char dbg[100];
04055     sprintf(dbg, "After GuiLib_Clear 94");
04056     EasyGUIDebugPrint(dbg, 0, 20);
04057 #undef DEBUG_HERE
04058 #endif
04059     }
04060 }
04061 
04062 
04063 /*
04064     A "simple page" is one that only has fixed text, rectangles, etc, and easyGUI variables.
04065     No status rectangles, etc.
04066     
04067     All these can be displayed using the same sequence of function calls.
04068 */
04069 void GetGCStatusLoop::DisplaySimplePageData(int pageNumber, bool mustUpdateDisplay)
04070 {
04071     // There are no status rectangles on a "simple page"
04072 
04073     // Makes the display flicker - but omitting it means old text is not cleared from the display
04074 #define WANT_GUILIB_CLEAR
04075 #ifdef WANT_GUILIB_CLEAR
04076 #ifdef USING_BACKGROUND_BITMAP
04077     DrawBackgroundBitmap(); 
04078 #else
04079     GuiLib_Clear();
04080 #endif
04081 #undef WANT_GUILIB_CLEAR
04082 #endif
04083     GuiLib_ShowScreen(pageNumber, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
04084     
04085     GuiLib_Refresh();    
04086 
04087 //#define DEBUG_HERE
04088 #ifdef DEBUG_HERE
04089     char dbg[100];
04090     sprintf(dbg, "After GuiLib_Clear 95");
04091     EasyGUIDebugPrint(dbg, 0, 20);
04092 #undef DEBUG_HERE
04093 #endif
04094 }
04095 
04096 void GetGCStatusLoop::DisplayColumnDHManualCalibrationPageData(bool mustUpdateDisplay)
04097 {
04098     DisplaySimplePageData(GuiStruct_ColumnDHManualCalibrationPage_Def, mustUpdateDisplay);
04099 }
04100 
04101 void GetGCStatusLoop::DisplayColumnDHSensorCalibrationPageData(bool mustUpdateDisplay)
04102 {
04103     DisplaySimplePageData(GuiStruct_ColumnDHSensorCalibration_Def, mustUpdateDisplay);
04104 }
04105 
04106 void GetGCStatusLoop::DisplayColumnDHPSUDACPageData(bool mustUpdateDisplay)
04107 {
04108     if(mustUpdateDisplay) {
04109         ColumnDHPSUDACPageHandler *columnDHPSUDACPageHandler = ColumnDHPSUDACPageHandler::GetInstance(usbDevice, usbHostGC);
04110         
04111         if(columnDHPSUDACPageHandler != NULL) {
04112             columnDHPSUDACPageHandler->DisplayingEasyGUIPage(true);
04113         }
04114     }
04115         
04116     DisplaySimplePageData(GuiStruct_PSU_DAC_Page_Def, mustUpdateDisplay);
04117 }
04118 
04119 void GetGCStatusLoop::DisplayColumnOvenNudgeAndDampPageData(bool mustUpdateDisplay)
04120 {
04121     DisplaySimplePageData(GuiStruct_ColumnOvenNudgeAndDampPage_0, mustUpdateDisplay);
04122 }
04123 
04124 void GetGCStatusLoop::DisplayColumnDHNudgeAndDampPageData(bool mustUpdateDisplay)
04125 {
04126     DisplaySimplePageData(GuiStruct_ColumnDHNudgeAndDampPage_0, mustUpdateDisplay);
04127 }
04128 
04129 void GetGCStatusLoop::DisplayInjectorNudgeAndDampPageData(bool mustUpdateDisplay)
04130 {
04131     DisplaySimplePageData(GuiStruct_InjectorNudgeAndDampPage_0, mustUpdateDisplay);
04132 }
04133 
04134 void GetGCStatusLoop::DisplayDetectorNudgeAndDampPageData(bool mustUpdateDisplay)
04135 {
04136     DisplaySimplePageData(GuiStruct_DetectorNudgeAndDampPage_0, mustUpdateDisplay);
04137 }
04138 
04139 void GetGCStatusLoop::DisplayAuxiliaryNudgeAndDampPageData(bool mustUpdateDisplay)
04140 {
04141     DisplaySimplePageData(GuiStruct_AuxiliaryNudgeAndDampPage_0, mustUpdateDisplay);
04142 }
04143 
04144 void GetGCStatusLoop::DisplayFanPowerPageData(bool mustUpdateDisplay)
04145 {
04146     DisplaySimplePageData(GuiStruct_FanPowerPage_0, mustUpdateDisplay);
04147 }
04148 
04149 void GetGCStatusLoop::DisplayDebugCommandsPageData(bool mustUpdateDisplay)
04150 {
04151     DisplaySimplePageData(GuiStruct_DebugCommandsPage_Def, mustUpdateDisplay);
04152 }
04153 
04154 
04155 /*
04156     Gets the injection mode, and returns it as a null-terminated string, with a descriptive prefix.
04157     
04158     Note also that this code is intended to be as efficient as possible.
04159     
04160     Args: pointer to a buffer to contain the injection mode, as a null-terminated string
04161     
04162     Does not put a hard-coded prefix on the string it returns.
04163           
04164     No return code.
04165 */
04166 void GetGCStatusLoop::GetInjectionMode(char *mode)
04167 {
04168     char response[50];
04169     SetGCDeviceReport("GIMD", response);
04170     // We expect a response like this: "DIMD0000" for Split, "DIMD0001" for Splitless,
04171     // "DIMD0002" for On Column, "DIMD0003" for Gas Sampling
04172     
04173     int index = 0;
04174     // Check for "EPKT" first
04175     if(response[0] == 'E') {
04176         mode[index++] = '*';
04177         mode[index++] = '*';
04178         mode[index++] = ' ';
04179         mode[index++] = 'E';
04180         mode[index++] = 'r';
04181         mode[index++] = 'r';
04182         mode[index++] = 'o';
04183         mode[index++] = 'r';
04184         mode[index++] = ' ';
04185         mode[index++] = '*';
04186         mode[index++] = '*';
04187     } else {
04188         switch(response[7]) {
04189             case 1:
04190                 mode[index++] = 'S';
04191                 mode[index++] = 'p';
04192                 mode[index++] = 'l';
04193                 mode[index++] = 'i';
04194                 mode[index++] = 't';
04195                 mode[index++] = 'l';
04196                 mode[index++] = 'e';
04197                 mode[index++] = 's';
04198                 mode[index++] = 's';
04199                 break;
04200             case 2:
04201                 mode[index++] = 'O';
04202                 mode[index++] = 'n';
04203                 mode[index++] = ' ';
04204                 mode[index++] = 'C';
04205                 mode[index++] = 'o';
04206                 mode[index++] = 'l';
04207                 mode[index++] = 'u';
04208                 mode[index++] = 'm';
04209                 mode[index++] = 'n';
04210                 break;
04211             case 3:
04212                 mode[index++] = 'G';
04213                 mode[index++] = 'a';
04214                 mode[index++] = 's';
04215                 mode[index++] = ' ';
04216                 mode[index++] = 's';
04217                 mode[index++] = 'a';
04218                 mode[index++] = 'm';
04219                 mode[index++] = 'p';
04220                 mode[index++] = 'l';
04221                 mode[index++] = 'i';
04222                 mode[index++] = 'n';
04223                 mode[index++] = 'g';
04224                 break;
04225             default:
04226                 mode[index++] = 'S';
04227                 mode[index++] = 'p';
04228                 mode[index++] = 'l';
04229                 mode[index++] = 'i';
04230                 mode[index++] = 't';
04231                 break;
04232         }
04233     }
04234 
04235     mode[index++] = '\0';
04236 }
04237 
04238 /*
04239     Gets the injector type, and returns it as a null-terminated string, with a descriptive prefix.
04240     
04241     Note also that this code is intended to be as efficient as possible.
04242     
04243     Args: pointer to a buffer to contain the injection mode, as a null-terminated string
04244           boolean set true if the caller wants a full prefix ("Injector type: "), 
04245           or false for a short prefix ("Type: ")
04246           
04247     No return code.
04248 */
04249 void GetGCStatusLoop::GetInjectorType(char *mode, bool wantPrefix)
04250 {
04251     char response[50];
04252     SetGCDeviceReport("GITY", response);
04253 
04254     // We expect a response like this: "DITY0000" for None, 
04255     // "DITY0001" for standard (split/splitless), "DITY0002" for PTV
04256     int index = 0;
04257     if(wantPrefix) {
04258         mode[index++]  = 'I';
04259         mode[index++]  = 'n';
04260         mode[index++]  = 'j';
04261         mode[index++]  = 'e';
04262         mode[index++]  = 'c';
04263         mode[index++]  = 't';
04264         mode[index++]  = 'o';
04265         mode[index++]  = 'r';
04266         mode[index++]  = ' ';
04267         mode[index++]  = 't';
04268         mode[index++]  = 'y';
04269         mode[index++]  = 'p';
04270         mode[index++]  = 'e';
04271         mode[index++]  = ':';
04272         mode[index++]  = ' ';
04273     }
04274         
04275     // Check for "EPKT" first
04276     if(response[0] == 'E') {
04277         mode[index++] = '*';
04278         mode[index++] = '*';
04279         mode[index++] = ' ';
04280         mode[index++] = 'E';
04281         mode[index++] = 'r';
04282         mode[index++] = 'r';
04283         mode[index++] = 'o';
04284         mode[index++] = 'r';
04285         mode[index++] = ' ';
04286         mode[index++] = '*';
04287         mode[index++] = '*';
04288         mode[index++] = '\0';
04289     } else {
04290         switch(response[7]) {
04291             case '1':
04292                 mode[index++] = 'S';
04293                 mode[index++] = 't';
04294                 mode[index++] = 'a';
04295                 mode[index++] = 'n';
04296                 mode[index++] = 'd';
04297                 mode[index++] = 'a';
04298                 mode[index++] = 'r';
04299                 mode[index++] = 'd';
04300                 break;
04301             case '2':
04302                 mode[index++] = 'P';
04303                 mode[index++] = 'T';
04304                 mode[index++] = 'V';
04305                 break;
04306             default:
04307                 mode[index++] = 'N';
04308                 mode[index++] = 'o';
04309                 mode[index++] = 'n';
04310                 mode[index++] = 'e';
04311                 break;
04312         }
04313         mode[index++] = '\0';
04314     }
04315 }
04316 
04317 /*
04318     Gets the injection split time, and returns it as a null-terminated string.
04319     
04320     Note also that this code is intended to be as efficient as possible.
04321     
04322     Args: pointer to a buffer to contain the split time, as a null-terminated string
04323     
04324     Does not put a hard-coded prefix on the string it returns,
04325     but does put " min" as a suffix
04326           
04327     No return code.
04328 */
04329 void GetGCStatusLoop::GetInjectorSplitTime(char *splitTime)
04330 {
04331     char response[50];
04332     SetGCDeviceReport("GSPT", response);
04333     // We expect a response like this: "DSPTnnnn", where "nnnn" is the split time,
04334     // in units of 0.1 min.
04335     
04336     int index = 0;
04337     // Check for "EPKT" first
04338     if(response[0] == 'E') {
04339         splitTime[index++] = '*';
04340         splitTime[index++] = '*';
04341         splitTime[index++] = ' ';
04342         splitTime[index++] = 'E';
04343         splitTime[index++] = 'r';
04344         splitTime[index++] = 'r';
04345         splitTime[index++] = 'o';
04346         splitTime[index++] = 'r';
04347         splitTime[index++] = ' ';
04348         splitTime[index++] = '*';
04349         splitTime[index++] = '*';
04350     } else {
04351         bool wantNextChars = false;
04352         if(response[4] != '0') {
04353             splitTime[index++] = response[4];
04354             wantNextChars = true;
04355         }
04356         if(wantNextChars || (response[5] != '0')) {
04357             splitTime[index++] = response[5];
04358         }
04359         // If the value is zero, make sure we return "0.0" - 
04360         // we just don't want any zeroes before that
04361         splitTime[index++] = response[6];
04362         splitTime[index++] = '.';
04363         splitTime[index++] = response[7];
04364         splitTime[index++] = ' ';
04365         splitTime[index++] = 'm';
04366         splitTime[index++] = 'i';
04367         splitTime[index++] = 'n';
04368     }
04369     
04370     splitTime[index] = '\0';
04371 }
04372 
04373 
04374 /*
04375     Displays the data on the injector page, by copying it to the relevant easyGUI variables,
04376     and calling the relevant functions to actually display it.
04377     
04378     Args: a boolean specifying whether or not the display is actually to be updated.
04379           Even if it is false when we are called, we may set it true if we discover
04380           one or more data items has changed - but note that we will not set it false 
04381           if it is already true. If it is true after we have looked at all 
04382           the home page data, we will then update the display. (If we are called
04383           with this value set to false, the caller is effectively saying
04384           'display the injector page data only if it has changed').
04385           
04386     No return code.
04387 */
04388 void GetGCStatusLoop::DisplayInjectorPageData(bool mustUpdateDisplay)
04389 {
04390 //    EasyGUIDebugPrint("Injector Page", 100, 100);
04391     // Injector temperature and mode
04392     char buff[40];
04393     
04394     GetInjectorTemperature(buff, false);
04395     if(strcmp(buff, GuiVar_injectorTemperature) != 0) {
04396         strcpy(GuiVar_injectorTemperature, buff);
04397 
04398         //mustUpdateDisplay = true;
04399         // No - just do this (don't force entire rectangle to redisplay)
04400         // Hard coded values taken from easyGUI
04401         RedrawSingleEasyGUIVariableOnComponentPage(410, 90, &GuiVar_injectorTemperature, GuiLib_ALIGN_LEFT, INJECTOR);
04402     }
04403 
04404     GetInjectorTargetTemperature(buff, stringFormatdegCUnits);
04405     if(strcmp(buff, GuiVar_injectorTargetTemperature2) != 0) {
04406         strcpy(GuiVar_injectorTargetTemperature2, buff);
04407 
04408         //mustUpdateDisplay = true;
04409         // No - just do this (don't force entire rectangle to redisplay)
04410         // Hard coded values taken from easyGUI
04411         RedrawSingleEasyGUIVariableOnComponentPage(410, 130, &GuiVar_injectorTargetTemperature2, GuiLib_ALIGN_LEFT, INJECTOR);
04412     }
04413 
04414     GetInjectionMode(buff);
04415     if(strcmp(buff, GuiVar_injectionMode2) != 0) {
04416         strcpy(GuiVar_injectionMode2, buff);
04417 
04418         //mustUpdateDisplay = true;
04419         // No - just do this (don't force entire rectangle to redisplay)
04420         // Hard coded values taken from easyGUI
04421         RedrawSingleEasyGUIVariableOnComponentPage(410, 170, &GuiVar_injectionMode2, GuiLib_ALIGN_LEFT, INJECTOR);
04422     }
04423 
04424     GetInjectorType(buff, false);
04425     if(strcmp(buff, GuiVar_injectorType) != 0) {
04426         strcpy(GuiVar_injectorType, buff);
04427 
04428         //mustUpdateDisplay = true;
04429         // No - just do this (don't force entire rectangle to redisplay)
04430         // Hard coded values taken from easyGUI
04431         RedrawSingleEasyGUIVariableOnComponentPage(410, 210, &GuiVar_injectorType, GuiLib_ALIGN_LEFT, INJECTOR);
04432     }
04433 
04434     GetComponentStatusString(INJECTOR, buff);
04435     if(strcmp(buff, GuiVar_injectorStatus) != 0) {
04436         strcpy(GuiVar_injectorStatus, buff);
04437 
04438         //mustUpdateDisplay = true;
04439         // No - just do this (don't force entire rectangle to redisplay)
04440         // Hard coded values taken from easyGUI
04441         RedrawSingleEasyGUIVariableOnComponentPage(410, 250, &GuiVar_injectorStatus, GuiLib_ALIGN_LEFT, INJECTOR);
04442     }
04443     
04444     if(SinglePageGCComponentStatusHasChanged(INJECTOR, lastInjectorStatusDisplayedOnInjectorPage)) {
04445         mustUpdateDisplay = true;
04446     }
04447 
04448     if(mustUpdateDisplay) {
04449 
04450 #ifdef WANT_INJECTOR_STATUS_RECTANGLE
04451         // Reduce display flickering - get the component status from the GC
04452         // *before* we call GuiLib_Clear()
04453         if(singleGCComponentPageStatusColorAreas != NULL) {
04454             UpdateSingleGCComponentPageStatusColorArea(INJECTOR);
04455         }
04456 #endif
04457 
04458 #ifndef WANT_INJECTOR_STATUS_RECTANGLE
04459         // Makes the display flicker - but omitting it means old text is not cleared from the display
04460 #define WANT_GUILIB_CLEAR
04461 #endif
04462 
04463 #ifdef WANT_GUILIB_CLEAR
04464 #ifdef USING_BACKGROUND_BITMAP
04465         DrawBackgroundBitmap(); // We want the status rectangles to be 'on top' of this - 
04466                                 // if we include it in the easyGUI page itself, it gets drawn by GuiLib_ShowScreen,
04467                                 // and overwrites the rectangles
04468 #else
04469         GuiLib_Clear();
04470 #endif
04471 #undef WANT_GUILIB_CLEAR
04472 #endif
04473         //...except that redrawing the status rectangle effectively clears the text on top of the rectangle -
04474         // so we do not need GuiLib_Clear here, since all the injector page data appears on top of the rectangle. 
04475         // Without it, only the text flickers, not the rectangle
04476 
04477 #ifdef WANT_INJECTOR_STATUS_RECTANGLE
04478         // Note - we draw the status rectangle after GuiLib_Clear - otherwise we wouldn't see the rectangles at all - 
04479         // and before GuiLib_ShowScreen - so text, etc, is drawn on top of the rectangles
04480         if(singleGCComponentPageStatusColorAreas != NULL) {
04481             singleGCComponentPageStatusColorAreas->DisplayGCComponentStatus(INJECTOR);
04482             
04483             lastInjectorStatusDisplayedOnInjectorPage = singleGCComponentPageStatusColorAreas->GetGCComponentStatus(INJECTOR);
04484         }
04485 #endif
04486         
04487         // And (currently) we draw the component bitmap on top of the rectangle
04488         if(qspiBitmaps != NULL) {
04489             qspiBitmaps->DisplayInjectorComponentBitmap();
04490         }
04491 
04492         GuiLib_ShowScreen(GuiStruct_InjectorPage1_3, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
04493     
04494         GuiLib_Refresh();    
04495 
04496 #define DEBUG_HERE
04497 #ifdef DEBUG_HERE
04498     char dbg[100];
04499     sprintf(dbg, "After GuiLib_Clear 3");
04500     EasyGUIDebugPrint(dbg, 0, 20);
04501 #undef DEBUG_HERE
04502 #endif
04503     }
04504 }
04505 
04506 /*
04507     Public function, allowing an external caller to tell us 
04508     to display, or not, the ramp scroll buttons on the Injector Method page.
04509     
04510     Assume that the caller knows the Injector Method page *is*
04511     currently being displayed.
04512 */
04513 void GetGCStatusLoop::ShowInjectorMethodPageScrollButtonsIfNecessary(void)
04514 {
04515     ShowMethodPageScrollButtonsIfNecessary(injectorMethodRampData);
04516 }
04517 
04518 void GetGCStatusLoop::ScrollInjectorMethodRampsUpIfPossible(void)
04519 {
04520     if(currentPage == GuiStruct_InjectorMethodPage_Def) {
04521         if(injectorMethodPageScrollIndex > 0) {
04522             --injectorMethodPageScrollIndex;
04523                 
04524             DisplayInjectorMethodPageData(false);
04525         }
04526     }
04527 }
04528 
04529 void GetGCStatusLoop::ScrollInjectorMethodRampsDownIfPossible(void)
04530 {
04531     if(currentPage == GuiStruct_InjectorMethodPage_Def) {
04532         if(injectorMethodRampData != NULL) {
04533             if(injectorMethodPageScrollIndex < injectorMethodRampData->GetScrollRange()) {
04534                 ++injectorMethodPageScrollIndex;
04535                 
04536                 DisplayInjectorMethodPageData(false);
04537             }
04538         }
04539     }
04540 }
04541 
04542 /*
04543     Displays the data on the injector method page, by copying it to the relevant easyGUI variables,
04544     and calling the relevant functions to actually display it.
04545     
04546     Args: a boolean specifying whether or not the display is actually to be updated.
04547           Even if it is false when we are called, we may set it true if we discover
04548           one or more data items has changed - but note that we will not set it false 
04549           if it is already true. If it is true after we have looked at all 
04550           the home page data, we will then update the display. (If we are called
04551           with this value set to false, the caller is effectively saying
04552           'display the column page data only if it has changed').
04553           
04554     No return code.
04555 */
04556 void GetGCStatusLoop::DisplayInjectorMethodPageData(bool mustUpdateDisplay)
04557 {
04558     char buff[40];
04559     
04560     GetInjectorTargetTemperature(buff, "%s");
04561     if(strcmp(buff, GuiVar_injectorMethodInitialTemp) != 0) {
04562         strcpy(GuiVar_injectorMethodInitialTemp, buff);
04563 
04564         //mustUpdateDisplay = true;
04565         // No - just do this (don't force entire page to redisplay)
04566         // Hard coded values taken from easyGUI
04567         RedrawSingleEasyGUIVariableOnComponentPage(520, 85, &GuiVar_injectorMethodInitialTemp, GuiLib_ALIGN_RIGHT, COLUMN);
04568     }
04569 
04570     GetInitialHoldTime(buff);
04571     if(strcmp(buff, GuiVar_injectorMethodInitialHold) != 0) {
04572         strcpy(GuiVar_injectorMethodInitialHold, buff);
04573 
04574         //mustUpdateDisplay = true;
04575         // No - just do this (don't force entire page to redisplay)
04576         // Hard coded values taken from easyGUI
04577         RedrawSingleEasyGUIVariableOnComponentPage(520, 120, &GuiVar_injectorMethodInitialHold, GuiLib_ALIGN_RIGHT, COLUMN);
04578     }
04579 
04580     if(injectorMethodRampData == NULL) {
04581         injectorMethodRampData = new InjectorMethodRampData(usbDevice, usbHostGC);
04582     }
04583     
04584     if(injectorMethodRampData != NULL) {
04585         if(!injectorMethodRampData->GotRampData()) {
04586             injectorMethodRampData->GetRampDataFromGC();
04587         }
04588         
04589         sprintf(buff, "%u", injectorMethodRampData->GetRampCount());
04590         if(strcmp(buff, GuiVar_injectorMethodRampCount) != 0) {
04591             strcpy(GuiVar_injectorMethodRampCount, buff);
04592     
04593             //mustUpdateDisplay = true;
04594             // No - just do this (don't force entire page to redisplay)
04595             // Hard coded values taken from easyGUI
04596             RedrawSingleEasyGUIVariableOnComponentPage(520, 155, &GuiVar_injectorMethodRampCount, GuiLib_ALIGN_RIGHT, COLUMN);
04597         }
04598         
04599         if(injectorMethodRampData->NeedToUpdateEasyGUIMethodPageRampVariables()) {
04600             
04601             injectorMethodPageScrollIndex = 0;
04602             
04603             injectorMethodRampData->UpdateEasyGUIMethodPageVariables(injectorMethodPageScrollIndex);
04604             
04605             previousInjectorMethodPageScrollIndex = injectorMethodPageScrollIndex;
04606             
04607             mustUpdateDisplay = true; // Not practical to write all these variables individually
04608 
04609         } else if (previousInjectorMethodPageScrollIndex != injectorMethodPageScrollIndex) {
04610 
04611             injectorMethodRampData->UpdateEasyGUIMethodPageVariables(injectorMethodPageScrollIndex);
04612             
04613             previousInjectorMethodPageScrollIndex = injectorMethodPageScrollIndex;
04614             
04615             mustUpdateDisplay = true; // Not practical to write all these variables individually
04616         }
04617     }
04618     
04619     if(SinglePageGCComponentStatusHasChanged(INJECTOR)) {
04620         mustUpdateDisplay = true;
04621     }
04622 
04623     if(mustUpdateDisplay) {
04624 
04625 #ifdef WANT_INJECTOR_STATUS_RECTANGLE        
04626         // Reduce display flickering - get the component status from the GC
04627         // *before* we call GuiLib_Clear()
04628         if(singleGCComponentPageStatusColorAreas != NULL) {
04629             UpdateSingleGCComponentPageStatusColorArea(INJECTOR);
04630         }
04631 #endif       
04632 
04633 #ifndef WANT_INJECTOR_STATUS_RECTANGLE
04634 #define WANT_GUILIB_CLEAR
04635 #endif
04636 
04637 #ifdef WANT_GUILIB_CLEAR
04638 #ifdef USING_BACKGROUND_BITMAP
04639         DrawBackgroundBitmap(); // We want the status rectangles to be 'on top' of this - 
04640                                 // if we include it in the easyGUI page itself, it gets drawn by GuiLib_ShowScreen,
04641                                 // and overwrites the rectangles
04642 #else
04643         GuiLib_Clear();
04644 #endif
04645 #undef WANT_GUILIB_CLEAR
04646 #endif
04647         //...except that redrawing the status rectangle effectively clears the text on top of the rectangle -
04648         // so we do not need GuiLib_Clear here, since all the column page data appears on top of the rectangle. 
04649         // Without it, only the text flickers, not the rectangle
04650 
04651 #ifdef WANT_INJECTOR_STATUS_RECTANGLE
04652         // Note - we draw the status rectangle after GuiLib_Clear - otherwise we wouldn't see the rectangles at all - 
04653         // and before GuiLib_ShowScreen - so text, etc, is drawn on top of the rectangles
04654         if(singleGCComponentPageStatusColorAreas != NULL) {
04655             singleGCComponentPageStatusColorAreas->DisplayGCComponentStatus(INJECTOR);
04656         }
04657 #endif
04658 
04659         // And (currently) we draw the component bitmap on top of the rectangle
04660         if(qspiBitmaps != NULL) {
04661             qspiBitmaps->DisplayInjectorComponentBitmap();
04662         }
04663 
04664         GuiLib_ShowScreen(GuiStruct_InjectorMethodPage_Def, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
04665     
04666         ShowMethodPageScrollButtonsIfNecessary(injectorMethodRampData);
04667 
04668         GuiLib_Refresh();    
04669     }
04670 }
04671 
04672 
04673 /*
04674     Sets up the data for the graph on the Injector Temperature Profile page,
04675     and causes it to be displayed
04676 */
04677 void GetGCStatusLoop::DisplayInjectorTempProfilePageGraph(void)
04678 {
04679     // - dataset 0 is a line representing the temperature profile of the run from 'now' to the finish
04680     // - dataset 1 is a bar chart representing the temperature profile of the run from 'now' to the finish
04681     // - dataset 2 is a line representing the temperature profile from the start to 'now'
04682     // - dataset 3 is a bar chart representing the temperature profile of the run from the start to 'now'
04683     // - dataset 4 is a single dot at the current time and temperature
04684     
04685     TimeUnit timeUnit = MINUTES;
04686     if(injectorTempProfilePageGraphCompleteProfileDataSet->GetTotalMethodTime() < methodTimeUnitsThreshold) {
04687         timeUnit = SECONDS;
04688     }
04689 
04690     const float yAxisScaleFactor = 10.0f;
04691 
04692     // Dataset 0
04693     injectorTempProfilePageGraph->SetDataForGraphDataSetInTenthsOfMinutes(0, yAxisScaleFactor, injectorTempProfilePageGraphDataSet0);
04694     
04695     // Dataset 1 
04696     injectorTempProfilePageGraph->SetDataForGraphDataSetInTenthsOfMinutes(1, yAxisScaleFactor, injectorTempProfilePageGraphDataSet1);
04697     
04698     // Dataset 2
04699     injectorTempProfilePageGraph->SetDataForGraphDataSetInTenthsOfMinutes(2, yAxisScaleFactor, injectorTempProfilePageGraphDataSet2);
04700     
04701     // Dataset 3
04702     injectorTempProfilePageGraph->SetDataForGraphDataSetInTenthsOfMinutes(3, yAxisScaleFactor, injectorTempProfilePageGraphDataSet3);
04703     
04704     // Dataset 4
04705     injectorTempProfilePageGraph->SetDataForGraphDataSetInTenthsOfMinutes(4, yAxisScaleFactor, injectorTempProfilePageGraphDataSet4);
04706     
04707     injectorTempProfilePageGraph->SetXAxisUnits(timeUnit);
04708     
04709     if(timeUnit == SECONDS) {
04710         strcpy(GuiVar_injectorTempProfilePageXAxisLabel, "Time (seconds)");
04711     } else {
04712         strcpy(GuiVar_injectorTempProfilePageXAxisLabel, "Time (minutes)");
04713     }
04714 
04715     if(injectorTempProfilePageGraphCompleteProfileDataSet->GetPointCount() == 0) {
04716         strcpy(GuiVar_injectorTempProfilePageNoMethod, "No method set up");
04717     } else {
04718         GuiVar_injectorTempProfilePageNoMethod[0] = '\0';
04719     }
04720 
04721     // The tick sizes must match those set in easyGUI - we do not seem 
04722     // to be able to get them at runtime
04723     GuiConst_INT32S minX, maxX;
04724     //GuiConst_INT32S tickSize = (timeUnit == SECONDS) ? 300 : 100;
04725     // Always in 1/10 minutes
04726     GuiConst_INT32S xAxisTickSize = (timeUnit == SECONDS) ? 5 : 100; // 5 == 0.5 minutes (i.e. 30 seconds), 100 == 10 minutes
04727     injectorTempProfilePageGraphCompleteProfileDataSet->GetXAxisRangeInTenthsOfMinutes(&minX, &maxX, xAxisTickSize);
04728     injectorTempProfilePageGraph->SetXAxisRange(0, maxX); // Always start X axis at zero
04729     
04730     GuiConst_INT32S minY, maxY;
04731     GuiConst_INT32S yAxisTickSize = 50;
04732     injectorTempProfilePageGraphCompleteProfileDataSet->GetYAxisRange(&minY, &maxY, yAxisTickSize);
04733     injectorTempProfilePageGraph->SetYAxisRange(0, (maxY * yAxisScaleFactor)); // Always start Y axis at zero
04734     
04735     
04736     injectorTempProfilePageGraph->DrawAxes();
04737 
04738     // We need to draw the X axis labels ourselves, since our time values are in units of 0.1 minute,
04739     // but easyGUI graphs work only in integers - we therefore have to multiply the time values by 10
04740     // before passing them to easyGUI, and if we let easyGUI draw its own values on the X axis, 
04741     // they would be 10 times the correct values.
04742     // Graph coordinates copied from easyGUI - I cannot find a way of obtaining them at runtime.
04743     if(timeUnit == SECONDS) {
04744         injectorTempProfilePageGraph->DrawXAxisLabels(0, (maxX * 6), (xAxisTickSize * 6), 130, 340, 500);
04745     } else {
04746         injectorTempProfilePageGraph->DrawXAxisLabels(0, (maxX / 10), (xAxisTickSize / 10), 130, 340, 500);
04747     }
04748     // Note that we repeat this call by calling 'DrawXAxisLabels' without arguments 
04749     // every time we (re)display this page
04750 
04751     // Similar to the X axis - the values we pass to the easyGUI graph are scaled to be larger
04752     // than the real values, to get round the fact that easyGUI graphs work in integers.
04753     // (This can cause the profile to appear to be 'stepped', which obviously we do not want.)
04754     // We must therefore draw the Y axis values ourselves.
04755     injectorTempProfilePageGraph->DrawYAxisLabels(0, maxY, yAxisTickSize, 130, 340, 250);
04756     // Note that we repeat this call by calling 'DrawYAxisLabels' without arguments 
04757     // every time we (re)display this page
04758 
04759     injectorTempProfilePageGraph->DrawDataSet(0);
04760     injectorTempProfilePageGraph->ShowDataSet(0);
04761 
04762     injectorTempProfilePageGraph->DrawDataSet(1);
04763     injectorTempProfilePageGraph->ShowDataSet(1);
04764 
04765     injectorTempProfilePageGraph->DrawDataSet(2);
04766     injectorTempProfilePageGraph->ShowDataSet(2);
04767 
04768     injectorTempProfilePageGraph->DrawDataSet(3);
04769     injectorTempProfilePageGraph->ShowDataSet(3);
04770 
04771     injectorTempProfilePageGraph->DrawDataSet(4);
04772     injectorTempProfilePageGraph->ShowDataSet(4);
04773 
04774     injectorTempProfilePageGraph->Redraw();
04775 
04776 #ifdef TEST_GUILIB_VLINE_PROFILES
04777     // *** TESTING *** Draw the profile as a solid colour, direct to the display
04778     //                 Coords manually copied from easyGUI application.
04779     //                 Boundary between colours is arbitrary for now
04780     GuiConst_INTCOLOR graphColour1 = SixteenBitColorValue(100, 100, 100);
04781     GuiConst_INTCOLOR graphColour2 = SixteenBitColorValue(200, 200, 200);
04782 //    double colourBoundaryX = (double) injectorTempProfilePageGraphCompleteProfileDataSet->GetTotalMethodTime() / 5.0; // Should be one-fifth across the profile
04783     double colourBoundaryX = -1.0; // No - use one colour only
04784     if(timeUnit == SECONDS) {
04785         injectorTempProfilePageGraphCompleteProfileDataSet->DrawUsingGuiLibVLine(140, 330, ((double) 500 / (double) (maxX * 6)), ((double) -250 / (double) maxY), graphColour1, graphColour2, colourBoundaryX);
04786     } else {
04787         injectorTempProfilePageGraphCompleteProfileDataSet->DrawUsingGuiLibVLine(140, 330, ((double) 500 / (double) (maxX / 10)), ((double) -250 / (double) maxY), graphColour1, graphColour2, colourBoundaryX);
04788     }
04789 #endif // TEST_GUILIB_VLINE_PROFILES
04790 }
04791 
04792 /*
04793     Displays the data on the injector temperature profile page, 
04794     by copying it to the relevant easyGUI variables,
04795     and calling the relevant functions to actually display it.
04796     
04797     Args: a boolean specifying whether or not the display is actually to be updated.
04798           Even if it is false when we are called, we may set it true if we discover
04799           one or more data items has changed - but note that we will not set it false 
04800           if it is already true. If it is true after we have looked at all 
04801           the home page data, we will then update the display. (If we are called
04802           with this value set to false, the caller is effectively saying
04803           'display the injector page data only if it has changed').
04804           
04805     No return code.
04806 */
04807 void GetGCStatusLoop::DisplayInjectorTempProfilePageData(bool mustUpdateDisplay)
04808 {
04809     // Ensure this is always up to date
04810     if(needToUpdateProfileGraphs) {
04811         SetupColumnAndInjectorAndGasProfileData();
04812         mustUpdateDisplay = true;
04813         
04814         needToUpdateProfileGraphs = false;
04815     }
04816 
04817     DisplayInjectorTempProfilePageGraph();
04818     
04819     if(SinglePageGCComponentStatusHasChanged(INJECTOR)) {
04820         mustUpdateDisplay = true;
04821     }
04822 
04823     if(mustUpdateDisplay) {
04824 
04825 #ifdef WANT_INJECTOR_STATUS_RECTANGLE
04826         // Reduce display flickering - get the component status from the GC
04827         // *before* we call GuiLib_Clear()
04828         if(singleGCComponentPageStatusColorAreas != NULL) {
04829             UpdateSingleGCComponentPageStatusColorArea(INJECTOR);
04830         }
04831 #endif
04832         
04833 #ifndef WANT_INJECTOR_STATUS_RECTANGLE
04834         // Makes the display flicker - but omitting it means old text is not cleared from the display
04835 #define WANT_GUILIB_CLEAR
04836 #endif
04837 
04838 #ifdef WANT_GUILIB_CLEAR
04839 #ifdef USING_BACKGROUND_BITMAP
04840         DrawBackgroundBitmap(); // We want the status rectangles to be 'on top' of this - 
04841                                 // if we include it in the easyGUI page itself, it gets drawn by GuiLib_ShowScreen,
04842                                 // and overwrites the rectangles
04843 #else
04844         GuiLib_Clear();
04845 #endif
04846 #undef WANT_GUILIB_CLEAR
04847 #endif
04848         //...except that redrawing the status rectangle effectively clears the text on top of the rectangle -
04849         // so we do not need GuiLib_Clear here, since all the injector page data appears on top of the rectangle. 
04850         // Without it, only the text flickers, not the rectangle
04851 
04852 #ifdef WANT_INJECTOR_STATUS_RECTANGLE
04853         // Note - we draw the status rectangle after GuiLib_Clear - otherwise we wouldn't see the rectangles at all - 
04854         // and before GuiLib_ShowScreen - so text, etc, is drawn on top of the rectangles
04855         if(singleGCComponentPageStatusColorAreas != NULL) {
04856             singleGCComponentPageStatusColorAreas->DisplayGCComponentStatus(INJECTOR);
04857         }
04858 #endif
04859 
04860 #ifdef WANT_COMPONENT_ICON_ON_PROFILE_PAGES
04861         // And (currently) we draw the component bitmap on top of the rectangle
04862         if(qspiBitmaps != NULL) {
04863             qspiBitmaps->DisplayInjectorComponentBitmap();
04864         }
04865 #endif // WANT_COMPONENT_ICON_ON_PROFILE_PAGES
04866 
04867         injectorTempProfilePageGraph->Redraw();
04868     
04869 #ifdef TEST_GUILIB_VLINE_PROFILES
04870         // Now draw the profile as a solid colour, direct to the display.
04871         // Use the same parameters as the previous call, in DisplayInjectorTempProfilePageGraph
04872         injectorTempProfilePageGraphCompleteProfileDataSet->DrawUsingGuiLibVLine();
04873 #endif // TEST_GUILIB_VLINE_PROFILES
04874 
04875         GuiLib_ShowScreen(GuiStruct_InjectorTempProfilePage_25, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
04876     
04877         // Repeat the previous call to the version of this function with parameters,
04878         // made from 'DisplayInjectorTempProfilePageGraph()' above (it is more convenient 
04879         // to calculate the parameter values in 'DisplayInjectorTempProfilePageGraph()' than here)
04880         injectorTempProfilePageGraph->DrawXAxisLabels();
04881         injectorTempProfilePageGraph->DrawYAxisLabels();
04882     
04883         GuiLib_Refresh();    
04884 
04885 #define DEBUG_HERE
04886 #ifdef DEBUG_HERE
04887     char dbg[100];
04888     sprintf(dbg, "After GuiLib_Clear 3");
04889     EasyGUIDebugPrint(dbg, 0, 20);
04890 #undef DEBUG_HERE
04891 #endif
04892     }
04893 }
04894 
04895 
04896 /*
04897     Gets the total flow rate, and returns it as a null-terminated string, with a suffix for the units.
04898     
04899     Note also that this code is intended to be as efficient as possible.
04900     
04901     Args: pointer to a buffer to contain the total flow rate, as a null-terminated string
04902     
04903     No return code.
04904 */
04905 void GetGCStatusLoop::GetTotalFlow(char *totalFlow)
04906 {
04907     GetFlowRate("GTFL", totalFlow);
04908 }
04909 
04910 /*
04911     Gets the total flow rate, and returns it as a floating point value
04912     
04913     Args: pointer to a variable to contain the total flow rate, as a floating point value
04914     
04915     No return code.
04916 */
04917 void GetGCStatusLoop::GetTotalFlow(float *totalFlow)
04918 {
04919     char buff[40];
04920     GetTotalFlow(buff);
04921     if(buff[0] == '*') {
04922         // Assume "EPKT" returned from GC
04923         *totalFlow = 0.0;
04924         return;
04925     }
04926     
04927     // 'else' we received a valid value
04928     buff[4] = '\0'; // Remove the units suffix
04929     sscanf(buff, "%f", totalFlow);
04930 }
04931 
04932 /*
04933     Gets the column flow rate, and returns it as a null-terminated string, with a suffix for the units.
04934     
04935     Args: pointer to a buffer to contain the total flow rate, as a null-terminated string
04936     
04937     No return code.
04938 */
04939 void GetGCStatusLoop::GetColumnFlow(char *columnFlow)
04940 {
04941     GetFlowRate("GCFL", columnFlow);
04942 }    
04943 
04944 /*
04945     Gets the column flow rate, and returns it as a floating point value
04946     
04947     Args: pointer to a variable to contain the column flow rate, as a floating point value
04948     
04949     No return code.
04950 */
04951 void GetGCStatusLoop::GetColumnFlow(float *columnFlow)
04952 {
04953     char buff[40];
04954     GetColumnFlow(buff);
04955     if(buff[0] == '*') {
04956         // Assume "EPKT" returned from GC
04957         *columnFlow = 0.0;
04958         return;
04959     }
04960     
04961     // 'else' we received a valid value
04962     buff[4] = '\0'; // Remove the units suffix
04963     sscanf(buff, "%f", columnFlow);
04964 }
04965 
04966 /*
04967     Gets the split flow rate, and returns it as a floating point value
04968     
04969     Args: pointer to a variable to contain the split flow rate, as a floating point value
04970     
04971     No return code.
04972 */
04973 void GetGCStatusLoop::GetSplitFlow(float *splitFlow)
04974 {
04975     float totalFlow;
04976     float columnFlow;
04977     
04978     GetTotalFlow(&totalFlow);
04979     GetColumnFlow(&columnFlow);
04980     
04981     *splitFlow = totalFlow - columnFlow;
04982 }
04983 
04984 /*
04985     Gets the split flow rate, and returns it (to a precision of one decimal place,
04986     like most floating point values returned by the GC - and with a suffix 
04987     representing the units) as a null terminated string.
04988     
04989     Args: pointer to a buffer to contain the split flow rate, as a null terminated string
04990     
04991     No return code.
04992 */
04993 void GetGCStatusLoop::GetSplitFlow(char *splitFlow)
04994 {
04995     float flt;
04996     GetSplitFlow(&flt);
04997     sprintf(splitFlow, "%.1f ml/min", flt);
04998 }
04999 
05000 /*
05001     Gets the split ratio, and returns it as a floating point value
05002     
05003     Args: pointer to a variable to contain the split ratio, as a floating point value
05004     
05005     No return code.
05006 */
05007 void GetGCStatusLoop::GetSplitRatio(float *splitRatio)
05008 {
05009     float splitFlow;
05010     float columnFlow;
05011     
05012     GetSplitFlow(&splitFlow);
05013     GetColumnFlow(&columnFlow);
05014     
05015     // Avoid divide by zero (and avoid floating point rounding errors)
05016     if(columnFlow > FLT_MIN) {
05017         *splitRatio = splitFlow / columnFlow;
05018     } else {
05019         *splitRatio = 1.0F; // Default (we have to assign *something*)
05020     }
05021 }
05022 
05023 /*
05024     Gets the split ratio, and returns it (to a precision of one decimal place,
05025     like most floating point values returned by the GC) as a null terminated string.
05026     
05027     Args: pointer to a buffer to contain the split flow rate, as a null terminated string
05028     
05029     No return code.
05030 */
05031 void GetGCStatusLoop::GetSplitRatio(char *splitRatio)
05032 {
05033     float flt;
05034     GetSplitRatio(&flt);
05035     sprintf(splitRatio, "%.1f", flt);
05036 }
05037 
05038 /*
05039     Gets the number of injections (i.e. runs) since the liner was changed, 
05040     and returns it as a null-terminated string.
05041     
05042     This involves getting both values from the settings stored in QSPI memory.
05043     
05044     Args: pointer to a buffer to contain the injection count, as a null-terminated string
05045           
05046     No return code.
05047 */
05048 void GetGCStatusLoop::GetInjectionCountSinceLinerChanged(char *injCount)
05049 {
05050     int runCount = SettingsHandler::GetIntegerValueFromQSPISettings("RunCount", 0);
05051     int linerChangedRunCount = SettingsHandler::GetIntegerValueFromQSPISettings("RunCountWhenLinerChanged", 0);
05052 
05053     sprintf(injCount, "%d", (runCount - linerChangedRunCount));
05054 }
05055 
05056 /*
05057     Gets the number of injections (i.e. runs) since the septa was changed, 
05058     and returns it as a null-terminated string.
05059     
05060     This involves getting both values from the settings stored in QSPI memory.
05061     
05062     Args: pointer to a buffer to contain the injection count, as a null-terminated string
05063           
05064     No return code.
05065 */
05066 void GetGCStatusLoop::GetInjectionCountSinceSeptaChanged(char *injCount)
05067 {
05068     int runCount = SettingsHandler::GetIntegerValueFromQSPISettings("RunCount", 0);
05069     int septaChangedRunCount = SettingsHandler::GetIntegerValueFromQSPISettings("RunCountWhenSeptaChanged", 0);
05070 
05071     sprintf(injCount, "%d", (runCount - septaChangedRunCount));
05072 }
05073 
05074 /*
05075     Gets the number of injections (i.e. runs) since the O-ring was changed, 
05076     and returns it as a null-terminated string.
05077     
05078     This involves getting both values from the settings stored in QSPI memory.
05079     
05080     Args: pointer to a buffer to contain the injection count, as a null-terminated string
05081           
05082     No return code.
05083 */
05084 void GetGCStatusLoop::GetInjectionCountSinceOringChanged(char *injCount)
05085 {
05086     int runCount = SettingsHandler::GetIntegerValueFromQSPISettings("RunCount", 0);
05087     int oRingChangedRunCount = SettingsHandler::GetIntegerValueFromQSPISettings("RunCountWhenOringChanged", 0);
05088 
05089     sprintf(injCount, "%d", (runCount - oRingChangedRunCount));
05090 }
05091 
05092 
05093 /*
05094     Gets the detector type, and returns it as a null-terminated string, with a descriptive prefix.
05095     (This is the first version of this function, using the "QDTY" command.)
05096     
05097     Note also that this code is intended to be as efficient as possible.
05098     
05099     Args: pointer to a buffer to contain the detector type, as a null-terminated string
05100           boolean set true if the caller wants a full prefix ("Detector type: "), 
05101           or false for a short prefix ("Type: ")
05102           
05103     No return code.
05104 */
05105 void GetGCStatusLoop::GetDetectorTypeOld(char *type, bool wantFullPrefix)
05106 {
05107     char response[50];
05108     SetGCDeviceReport("QDTY", response);
05109 
05110     // We expect a response like this: "DDTY0000" for FID, "DDTY0001" for TCD,
05111     // "DDTY0002" for ECD, "DDTY0003" for PID, "DDTY0004" for PDID, "DDTY0099" for None
05112     int index = 0;
05113     if(wantFullPrefix) {
05114         type[index++] = 'D';
05115         type[index++] = 'e';
05116         type[index++] = 't';
05117         type[index++] = 'e';
05118         type[index++] = 'c';
05119         type[index++] = 't';
05120         type[index++] = 'o';
05121         type[index++] = 'r';
05122         type[index++] = ' ';
05123         type[index++] = 't';
05124     } else {
05125         type[index++] = 'T';
05126     }
05127 
05128     type[index++]  = 'y';
05129     type[index++]  = 'p';
05130     type[index++]  = 'e';
05131     type[index++]  = ':';
05132     type[index++]  = ' ';
05133 
05134     // Check for "EPKT" first
05135     if(response[0] == 'E') {
05136         type[index++] = '*';
05137         type[index++] = '*';
05138         type[index++] = ' ';
05139         type[index++] = 'E';
05140         type[index++] = 'r';
05141         type[index++] = 'r';
05142         type[index++] = 'o';
05143         type[index++] = 'r';
05144         type[index++] = ' ';
05145         type[index++] = '*';
05146         type[index++] = '*';
05147         type[index++] = '\0';
05148     } else {
05149         switch(response[7] ) {
05150             case '0':
05151                 type[index++] = 'F';
05152                 type[index++] = 'I';
05153                 type[index++] = 'D';
05154                 type[index++] = '\0';
05155                 break;
05156             case '1':
05157                 type[index++] = 'T';
05158                 type[index++] = 'C';
05159                 type[index++] = 'D';
05160                 type[index++] = '\0';
05161                 break;
05162             case '2':
05163                 type[index++] = 'E';
05164                 type[index++] = 'C';
05165                 type[index++] = 'D';
05166                 type[index++] = '\0';
05167                 break;
05168             case '3':
05169                 type[index++] = 'P';
05170                 type[index++] = 'I';
05171                 type[index++] = 'D';
05172                 type[index++] = '\0';
05173                 break;
05174             case '4':
05175                 type[index++] = 'P';
05176                 type[index++] = 'D';
05177                 type[index++] = 'I';
05178                 type[index++] = 'D';
05179                 type[index++] = '\0';
05180                 break;
05181             default:
05182                 type[index++] = 'N';
05183                 type[index++] = 'o';
05184                 type[index++] = 'n';
05185                 type[index++] = 'e';
05186                 type[index++] = '\0';
05187                 break;
05188         }
05189     }
05190 }
05191 
05192 /*
05193     Gets the detector type, and returns it as a null-terminated string, with a descriptive prefix.
05194     (This is the second version of this function, using the new "GDTY" command.)
05195     
05196     Note also that this code is intended to be as efficient as possible.
05197     
05198     Args: pointer to a buffer to contain the detector type, as a null-terminated string
05199           boolean set true if the caller wants a full prefix ("Detector type: "), 
05200           or false for a short prefix ("Type: ")
05201           boolean set true if the caller wants any prefix, false for no prefix at all
05202           
05203     No return code.
05204 */
05205 void GetGCStatusLoop::GetDetectorType(char *type, bool wantFullPrefix, bool wantPrefix)
05206 {
05207     char response[50];
05208     SetGCDeviceReport("GDTY", response);
05209 
05210     // We expect a response like this: "DDTY0000" for FID, "DDTY0001" for TCD,
05211     // "DDTY0002" for ECD, "DDTY0003" for TXL, "DDTY0005" for NPD, "DDTY0006" for PID,
05212     // "DDTY0007" for FPD, "DDTY0008" for SPDID. 
05213     // We assume any other "DDTY00nn" value means "None".
05214     
05215     int index = 0;
05216     if(wantPrefix) {
05217         if(wantFullPrefix) {
05218             type[index++] = 'D';
05219             type[index++] = 'e';
05220             type[index++] = 't';
05221             type[index++] = 'e';
05222             type[index++] = 'c';
05223             type[index++] = 't';
05224             type[index++] = 'o';
05225             type[index++] = 'r';
05226             type[index++] = ' ';
05227             type[index++] = 't';
05228         } else {
05229             type[index++] = 'T';
05230         }
05231 
05232         type[index++]  = 'y';
05233         type[index++]  = 'p';
05234         type[index++]  = 'e';
05235         type[index++]  = ':';
05236         type[index++]  = ' ';
05237     }
05238     
05239     // Check for "EPKT" first
05240     if(response[0] == 'E') {
05241         type[index++] = '*';
05242         type[index++] = '*';
05243         type[index++] = ' ';
05244         type[index++] = 'E';
05245         type[index++] = 'r';
05246         type[index++] = 'r';
05247         type[index++] = 'o';
05248         type[index++] = 'r';
05249         type[index++] = ' ';
05250         type[index++] = '*';
05251         type[index++] = '*';
05252         type[index++] = '\0';
05253     } else {
05254         switch(response[7] ) {
05255             case '0':
05256                 type[index++] = 'F';
05257                 type[index++] = 'I';
05258                 type[index++] = 'D';
05259                 type[index++] = '\0';
05260                 break;
05261             case '1':
05262                 type[index++] = 'T';
05263                 type[index++] = 'C';
05264                 type[index++] = 'D';
05265                 type[index++] = '\0';
05266                 break;
05267             case '2':
05268                 type[index++] = 'E';
05269                 type[index++] = 'C';
05270                 type[index++] = 'D';
05271                 type[index++] = '\0';
05272                 break;
05273             // Omit 4, i.e. "None" - leave for default
05274             case '3':
05275                 type[index++] = 'T';
05276                 type[index++] = 'X';
05277                 type[index++] = 'L';
05278                 type[index++] = '\0';
05279                 break;
05280             case '5':
05281                 type[index++] = 'N';
05282                 type[index++] = 'P';
05283                 type[index++] = 'D';
05284                 type[index++] = '\0';
05285                 break;
05286             case '6':
05287                 type[index++] = 'P';
05288                 type[index++] = 'I';
05289                 type[index++] = 'D';
05290                 type[index++] = '\0';
05291                 break;
05292             case '7':
05293                 type[index++] = 'F';
05294                 type[index++] = 'P';
05295                 type[index++] = 'D';
05296                 type[index++] = '\0';
05297                 break;
05298             case '8':
05299                 type[index++] = 'S';
05300                 type[index++] = 'P';
05301                 type[index++] = 'D';
05302                 type[index++] = 'I';
05303                 type[index++] = 'D';
05304                 type[index++] = '\0';
05305                 break;
05306             default:
05307                 type[index++] = 'N';
05308                 type[index++] = 'o';
05309                 type[index++] = 'n';
05310                 type[index++] = 'e';
05311                 type[index++] = '\0';
05312                 break;
05313         }
05314     }
05315 }
05316 
05317 /*
05318     Gets the detector range, and returns it as a null-terminated string.
05319     
05320     Note also that this code is intended to be as efficient as possible.
05321     
05322     Args: pointer to a buffer to contain the detector range, as a null-terminated string
05323           
05324     No return code.
05325 */
05326 void GetGCStatusLoop::GetDetectorRange(char *range)
05327 {
05328     char response[50];
05329     SetGCDeviceReport("GRNG", response);
05330 
05331     // We expect a response like this: "DRNG0001" for x2, 
05332     // "DRNG0002" for x10, "DRNG0003" for x100, "DRNG0004" for x1000
05333     
05334     int index = 0;
05335     // Check for "EPKT" first
05336     if(response[0] == 'E') {
05337         range[index++] = '*';
05338         range[index++] = '*';
05339         range[index++] = ' ';
05340         range[index++] = 'E';
05341         range[index++] = 'r';
05342         range[index++] = 'r';
05343         range[index++] = 'o';
05344         range[index++] = 'r';
05345         range[index++] = ' ';
05346         range[index++] = '*';
05347         range[index++] = '*';
05348     } else {
05349         range[index++] = 'x';
05350         switch (response[7]) {
05351             case '2': 
05352                 range[index++] = '1';
05353                 range[index++] = '0';
05354                 break;
05355             case '3': 
05356                 range[index++] = '1';
05357                 range[index++] = '0';
05358                 range[index++] = '0';
05359                 break;
05360             case '4': 
05361                 range[index++] = '1';
05362                 range[index++] = '0';
05363                 range[index++] = '0';
05364                 range[index++] = '0';
05365                 break;
05366             default:
05367                 range[index++] = '2';
05368                 break;
05369         }
05370     }
05371             
05372     range[index] = '\0';
05373 }
05374 
05375 /*
05376     Gets the detector ignition state (not lit, igniting, lit), and returns it as a null-terminated string.
05377     
05378     Note also that this code is intended to be as efficient as possible.
05379     
05380     Args: pointer to a buffer to contain the state, as a null-terminated string
05381           
05382     No return code.
05383 */
05384 void GetGCStatusLoop::GetDetectorIgnitionState(char *state)
05385 {
05386     char response[50];
05387     SetGCDeviceReport("QIGN", response);
05388 
05389     // We expect a response like this: "DIGN0001" for "not lit", 
05390     // "DIGN0002" for "igniting", "DIGN0003" for "lit"
05391     
05392     int index = 0;
05393     // Check for "EPKT" first
05394     if(response[0] == 'E') {
05395         state[index++] = '*';
05396         state[index++] = '*';
05397         state[index++] = ' ';
05398         state[index++] = 'E';
05399         state[index++] = 'r';
05400         state[index++] = 'r';
05401         state[index++] = 'o';
05402         state[index++] = 'r';
05403         state[index++] = ' ';
05404         state[index++] = '*';
05405         state[index++] = '*';
05406     } else {
05407         switch (response[7]) {
05408             case '2': 
05409                 state[index++] = 'i';
05410                 state[index++] = 'g';
05411                 state[index++] = 'n';
05412                 state[index++] = 'i';
05413                 state[index++] = 't';
05414                 state[index++] = 'i';
05415                 state[index++] = 'n';
05416                 state[index++] = 'g';
05417                 break;
05418             case '3': 
05419                 state[index++] = 'l';
05420                 state[index++] = 'i';
05421                 state[index++] = 't';
05422                 break;
05423             default:
05424                 state[index++] = 'n';
05425                 state[index++] = 'o';
05426                 state[index++] = 't';
05427                 state[index++] = ' ';
05428                 state[index++] = 'l';
05429                 state[index++] = 'i';
05430                 state[index++] = 't';
05431                 break;
05432         }
05433     }
05434         
05435     state[index] = '\0';
05436 }
05437 
05438 /*
05439     Gets the specified flow rate, and returns it as a null-terminated string.
05440     
05441     Note also that this code is intended to be as efficient as possible.
05442     
05443     Args: pointer to the command characters, as a null-terminated string
05444           pointer to a buffer to contain the flow rate, as a null-terminated string
05445           
05446     No return code.
05447 */
05448 void GetGCStatusLoop::GetFlowRate(char* cmd, char *rate)
05449 {
05450     char response[50];
05451     SetGCDeviceReport(cmd, response);
05452 
05453     // We expect a response like this: "Dxxxnnnn", where 'nnnn' is the flow rate, in ml/min.
05454     // Note that we do *not* want leading zeroes
05455     
05456     int index = 0;
05457     // Check for "EPKT" first
05458     if(response[0] == 'E') {
05459         rate[index++] = '*';
05460         rate[index++] = '*';
05461         rate[index++] = ' ';
05462         rate[index++] = 'E';
05463         rate[index++] = 'r';
05464         rate[index++] = 'r';
05465         rate[index++] = 'o';
05466         rate[index++] = 'r';
05467         rate[index++] = ' ';
05468         rate[index++] = '*';
05469         rate[index++] = '*';
05470     } else {
05471         bool wantNextChars = false;
05472         if(response[4] != '0') {
05473             rate[index++] = response[4];
05474             wantNextChars = true;
05475         }
05476         if(wantNextChars || (response[5] != '0')) {
05477             rate[index++] = response[5];
05478             wantNextChars = true;
05479         }
05480         if(wantNextChars || (response[6] != '0')) {
05481             rate[index++] = response[6];
05482         }
05483         // We always want the final char, even if it's zero - 
05484         // the flow rate may actually be zero
05485         rate[index++] = response[7];
05486         
05487         rate[index++] = ' ';
05488         rate[index++] = 'm';
05489         rate[index++] = 'l';
05490         rate[index++] = '/';
05491         rate[index++] = 'm';
05492         rate[index++] = 'i';
05493         rate[index++] = 'n';
05494     }
05495         
05496     rate[index] = '\0';
05497 }
05498 
05499 /*
05500     Gets the detector fuel flow rate, and returns it as a null-terminated string.
05501     
05502     Args: pointer to a buffer to contain the fuel flow rate, as a null-terminated string
05503           
05504     No return code.
05505 */
05506 void GetGCStatusLoop::GetFuelFlowRate(char *rate)
05507 {
05508     GetFlowRate("GHFL", rate);
05509 }
05510 
05511 /*
05512     Gets the detector air flow rate, and returns it as a null-terminated string.
05513     
05514     Args: pointer to a buffer to contain the air flow rate, as a null-terminated string
05515           
05516     No return code.
05517 */
05518 void GetGCStatusLoop::GetAirFlowRate(char *rate)
05519 {
05520     GetFlowRate("GAFL", rate);
05521 }
05522 
05523 
05524 
05525 /*
05526     If the detector temperature has changed on the detector page,
05527     redraw it manually - do not redisplay the entire page just for this
05528     (trying to reduce "flashing and banging")
05529 */
05530 void GetGCStatusLoop::DrawDetectorTemperatureVariableOnDetectorPage(void)
05531 {
05532     // Hard coded values taken from easyGUI -
05533     // Note that the temperature is at the same coordinates on all the detector pages
05534     //(except TCD, where it does not appear)
05535     RedrawSingleEasyGUIVariableOnComponentPage(380, 290, &GuiVar_detectorTemperature, GuiLib_ALIGN_LEFT, DETECTOR);
05536 }
05537 
05538 /*
05539     If the detector target temperature has changed on the detector page,
05540     redraw it manually - do not redisplay the entire page just for this
05541     (trying to reduce "flashing and banging")
05542 */
05543 void GetGCStatusLoop::DrawDetectorTargetTemperatureVariableOnDetectorPage(void)
05544 {
05545     // Hard coded values taken from easyGUI -
05546     // Note that the target temperature is at the same coordinates on all the detector pages
05547     //(except TCD, where it does not appear)
05548     RedrawSingleEasyGUIVariableOnComponentPage(380, 330, &GuiVar_detectorTargetTemperature2, GuiLib_ALIGN_LEFT, DETECTOR);
05549 }
05550 
05551 /*
05552     Displays the data on one of the detector pages, by copying the data to the relevant easyGUI variables,
05553     and calling the relevant functions to actually display it.
05554     
05555     Args: a boolean specifying whether or not the display is actually to be updated.
05556           Even if it is false when we are called, we may set it true if we discover
05557           one or more data items has changed - but note that we will not set it false 
05558           if it is already true. If it is true after we have looked at all 
05559           the home page data, we will then update the display. (If we are called
05560           with this value set to false, the caller is effectively saying
05561           'display the detector page data only if it has changed').
05562           
05563           the detector type. (It seems ridiculous to have separate functions for each type,
05564           since the values that need to be displayed are mostly the same.)
05565            
05566           the page number (this is derived from the detector type - if the caller knows the detector type,
05567           it must also (in effect) know the page number, so it seems ridiculous for this function 
05568           to work it out again).
05569           
05570     No return code.
05571 */
05572 void GetGCStatusLoop::DisplayDetectorPageData(bool mustUpdateDisplay, DetectorType detectorType, int pageNumber)
05573 {
05574 //    EasyGUIDebugPrint("Detector Page", 100, 100);
05575     // Detector temperature and type
05576     char buff[40];
05577     
05578     if(detectorType != NO_DETECTOR) {
05579         GetDetectorType(buff, false, false);
05580         if(strcmp(buff, GuiVar_detectorType2) != 0) {
05581             strcpy(GuiVar_detectorType2, buff);
05582             mustUpdateDisplay = true;
05583         }
05584         
05585         GetDetectorRange(buff);
05586         if(strcmp(buff, GuiVar_detectorRange) != 0) {
05587             strcpy(GuiVar_detectorRange, buff);
05588             mustUpdateDisplay = true;
05589         }
05590             
05591         GetFuelFlowRate(buff);    
05592         if(strcmp(buff, GuiVar_detectorFuelFlowRate) != 0) {
05593             strcpy(GuiVar_detectorFuelFlowRate, buff);
05594             mustUpdateDisplay = true;
05595         }
05596         
05597         GetAirFlowRate(buff);    
05598         if(strcmp(buff, GuiVar_detectorAirFlowRate) != 0) {
05599             strcpy(GuiVar_detectorAirFlowRate, buff);
05600             mustUpdateDisplay = true;
05601         }
05602         
05603         GetDetectorIgnitionState(buff);
05604         if(strcmp(buff, GuiVar_detectorStatus) != 0) {
05605             strcpy(GuiVar_detectorStatus, buff);
05606             mustUpdateDisplay = true;
05607         }
05608         
05609         // Now - variables that are different between the detector types
05610         if(detectorType != TCD_DETECTOR) {
05611             
05612             GetDetectorTemperature(buff);
05613             if(strcmp(buff, GuiVar_detectorTemperature) != 0) {
05614                 strcpy(GuiVar_detectorTemperature, buff);
05615 
05616                 //mustUpdateDisplay = true;
05617                 // No - just do this (don't force entire rectangle to redisplay)
05618                 DrawDetectorTemperatureVariableOnDetectorPage();
05619             }
05620             
05621             GetDetectorTargetTemperature(buff, stringFormatdegCUnits);
05622             if(strcmp(buff, GuiVar_detectorTargetTemperature2) != 0) {
05623                 strcpy(GuiVar_detectorTargetTemperature2, buff);
05624 
05625                 //mustUpdateDisplay = true;
05626                 // No - just do this (don't force entire rectangle to redisplay)
05627                 DrawDetectorTemperatureVariableOnDetectorPage();
05628             }
05629         }
05630         
05631         if(detectorType == TCD_DETECTOR) {
05632             
05633             GetTCDDetectorFilamentTemperature(buff);
05634             if(strcmp(buff, GuiVar_tcdDetectorFilamentTemperature) != 0) {
05635                 strcpy(GuiVar_tcdDetectorFilamentTemperature, buff);
05636                 mustUpdateDisplay = true;
05637             }
05638             
05639             GetTCDDetectorFilamentPolarity(buff);
05640             if(strcmp(buff, GuiVar_tcdDetectorFilamentPolarity) != 0) {
05641                 strcpy(GuiVar_tcdDetectorFilamentPolarity, buff);
05642                 mustUpdateDisplay = true;
05643             }
05644             
05645         } else if(detectorType == ECD_DETECTOR) {
05646             
05647             GetECDDetectorCurrent(buff);
05648             if(strcmp(buff, GuiVar_ecdDetectorCurrent) != 0) {
05649                 strcpy(GuiVar_ecdDetectorCurrent, buff);
05650                 mustUpdateDisplay = true;
05651             }
05652 
05653         } else if(detectorType == FPD_DETECTOR) {
05654             
05655             GetFPDDetectorSensitivity(buff);
05656             if(strcmp(buff, GuiVar_fpdDetectorSensitivity) != 0) {
05657                 strcpy(GuiVar_fpdDetectorSensitivity, buff);
05658                 mustUpdateDisplay = true;
05659             }
05660         }
05661     }
05662     
05663     if(SinglePageGCComponentStatusHasChanged(DETECTOR, lastDetectorStatusDisplayedOnDetectorPage)) {
05664         mustUpdateDisplay = true;
05665     }
05666 
05667     if(mustUpdateDisplay) {
05668 
05669 #ifdef WANT_DETECTOR_STATUS_RECTANGLE
05670         // Reduce display flickering - get the component status from the GC
05671         // *before* we call GuiLib_Clear()
05672         if(singleGCComponentPageStatusColorAreas != NULL) {
05673             UpdateSingleGCComponentPageStatusColorArea(DETECTOR);
05674         }
05675 #endif
05676         
05677 #ifndef WANT_DETECTOR_STATUS_RECTANGLE
05678         // Makes the display flicker - but omitting it means old text is not cleared from the display
05679 #define WANT_GUILIB_CLEAR
05680 #endif
05681 
05682 #ifdef WANT_GUILIB_CLEAR
05683 #ifdef USING_BACKGROUND_BITMAP
05684         DrawBackgroundBitmap(); // We want the status rectangles to be 'on top' of this - 
05685                                 // if we include it in the easyGUI page itself, it gets drawn by GuiLib_ShowScreen,
05686                                 // and overwrites the rectangles
05687 #else
05688         GuiLib_Clear();
05689 #endif
05690 #undef WANT_GUILIB_CLEAR
05691 #endif
05692         //...except that redrawing the status rectangle effectively clears the text on top of the rectangle -
05693         // so we do not need GuiLib_Clear here, since all the detector page data appears on top of the rectangle. 
05694         // Without it, only the text flickers, not the rectangle
05695 
05696 #ifdef WANT_DETECTOR_STATUS_RECTANGLE
05697         // Note - we draw the status rectangle after GuiLib_Clear - otherwise we wouldn't see the rectangles at all - 
05698         // and before GuiLib_ShowScreen - so text, etc, is drawn on top of the rectangles
05699         if(singleGCComponentPageStatusColorAreas != NULL) {
05700             singleGCComponentPageStatusColorAreas->DisplayGCComponentStatus(DETECTOR);
05701             
05702             lastDetectorStatusDisplayedOnDetectorPage = singleGCComponentPageStatusColorAreas->GetGCComponentStatus(DETECTOR);
05703         }
05704 #endif
05705         
05706         // And (currently) we draw the component bitmap on top of the rectangle
05707         if(qspiBitmaps != NULL) {
05708             qspiBitmaps->DisplayDetectorComponentBitmap();
05709         }
05710 
05711         GuiLib_ShowScreen(pageNumber, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
05712     
05713         GuiLib_Refresh();    
05714 
05715 #define DEBUG_HERE
05716 #ifdef DEBUG_HERE
05717     char dbg[100];
05718     sprintf(dbg, "After GuiLib_Clear 4");
05719     EasyGUIDebugPrint(dbg, 0, 20);
05720 #undef DEBUG_HERE
05721 #endif
05722     }
05723 }
05724 
05725 /*
05726     Gets the gas control mode, and returns it as a null-terminated string, with a descriptive prefix.
05727     
05728     Note also that this code is intended to be as efficient as possible.
05729     
05730     Args: pointer to a buffer to contain the mode, as a null-terminated string
05731           boolean set true if the caller wants a full prefix ("Gas control mode: "), 
05732           or false for a short prefix ("Control mode: ")
05733           boolean set true if the caller wants a prefix, false for no prefix whatsoever
05734           
05735     Returns true if the mode was obtained successfully, false if there was an error.
05736 */
05737 bool GetGCStatusLoop::GetGasControlMode(char *mode, bool wantFullPrefix, bool wantAnyPrefix)
05738 {
05739     bool retval = true; // Return false only if we get "EPKT" from the GC
05740     
05741     char response[50];
05742 //    SetGCDeviceReport("QGAS", response);
05743 //    // We expect a response like this: "DGAS0000" for Manual, "DGAS0001" for EPPC, or "EPKT" for error
05744 // Above command has been removed - now use:
05745     SetGCDeviceReport("GGTY", response);
05746     // We expect a response like this: "DGTY0000" for Manual, "DGTY0001" for EPPC, or "EPKT" for error
05747 
05748     int index = 0;
05749     if(wantAnyPrefix) {
05750         if(wantFullPrefix) {
05751             mode[index++] = 'G';
05752             mode[index++] = 'a';
05753             mode[index++] = 's';
05754             mode[index++] = ' ';
05755             mode[index++] = 'c';
05756         } else {
05757             mode[index++] = 'C';
05758         }
05759     
05760         mode[index++]  = 'o';
05761         mode[index++]  = 'n';
05762         mode[index++]  = 't';
05763         mode[index++]  = 'r';
05764         mode[index++]  = 'o';
05765         mode[index++]  = 'l';
05766         mode[index++]  = ' ';
05767         mode[index++]  = 'm';
05768         mode[index++]  = 'o';
05769         mode[index++]  = 'd';
05770         mode[index++]  = 'e';
05771         mode[index++]  = ':';
05772         mode[index++]  = ' ';
05773     }
05774     
05775     if(response[0] == 'E') {
05776         mode[index++]  = '*';
05777         mode[index++]  = '*';
05778         mode[index++]  = ' ';
05779         mode[index++]  = 'E';
05780         mode[index++]  = 'r';
05781         mode[index++]  = 'r';
05782         mode[index++]  = 'o';
05783         mode[index++]  = 'r';
05784         mode[index++]  = ' ';
05785         mode[index++]  = '*';
05786         mode[index++]  = '*';
05787         mode[index++] = '\0';
05788         
05789         retval = false;
05790     } else {
05791         if(response[7] == '0') {
05792             mode[index++] = 'M';
05793             mode[index++] = 'a';
05794             mode[index++] = 'n';
05795             mode[index++] = 'u';
05796             mode[index++] = 'a';
05797             mode[index++] = 'l';
05798             mode[index++] = '\0';
05799         } else {
05800             mode[index++] = 'E';
05801             mode[index++] = 'P';
05802             mode[index++] = 'P';
05803             mode[index++] = 'C';
05804             mode[index++] = '\0';
05805         }    
05806     }
05807     
05808     return retval;
05809 }
05810 
05811 /*
05812     Gets the carrier gas type, and returns it as a null-terminated string, with a descriptive prefix.
05813     
05814     Note also that this code is intended to be as efficient as possible.
05815     
05816     Args: pointer to the buffer to contain the carrier gas type, as a null-terminated string
05817           
05818     No return code.
05819 */
05820 void GetGCStatusLoop::GetCarrierGasType(char *type)
05821 {
05822     char response[50];
05823     SetGCDeviceReport("GCGS", response);
05824     // We expect a response like this: "DCGS000n", where 'n' = 0 for nitrogen, 1 for helium, and 2 for hydrogen,
05825     // or "EPKT" for error
05826     
05827     int index = 0;
05828     if(response[0] == 'E') {
05829         type[index++]  = '*';
05830         type[index++]  = '*';
05831         type[index++]  = ' ';
05832         type[index++]  = 'E';
05833         type[index++]  = 'r';
05834         type[index++]  = 'r';
05835         type[index++]  = 'o';
05836         type[index++]  = 'r';
05837         type[index++]  = ' ';
05838         type[index++]  = '*';
05839         type[index++]  = '*';
05840     } else {
05841         switch(response[7]) {
05842             case '1':
05843                 type[index++] = 'H';
05844                 type[index++] = 'e';
05845                 type[index++] = 'l';
05846                 type[index++] = 'i';
05847                 type[index++] = 'u';
05848                 type[index++] = 'm';
05849                 break;
05850             case '2':
05851                 type[index++] = 'H';
05852                 type[index++] = 'y';
05853                 type[index++] = 'd';
05854                 type[index++] = 'r';
05855                 type[index++] = 'o';
05856                 type[index++] = 'g';
05857                 type[index++] = 'e';
05858                 type[index++] = 'n';
05859                 break;
05860             default:
05861                 type[index++] = 'N';
05862                 type[index++] = 'i';
05863                 type[index++] = 't';
05864                 type[index++] = 'r';
05865                 type[index++] = 'o';
05866                 type[index++] = 'g';
05867                 type[index++] = 'e';
05868                 type[index++] = 'n';
05869                 break;
05870         }
05871     }
05872 
05873     type[index++] = '\0';
05874 }
05875 
05876 /*
05877     Gets the date the filter was last changed, and returns it as a null-terminated string, with a descriptive prefix.
05878     
05879     Note also that this code is intended to be as efficient as possible.
05880     
05881     Args: pointer to the buffer to contain the date the filter was changed, as a null-terminated string
05882           
05883     No return code.
05884     
05885     ** This function is currently a placeholder until we work out how to implement this facility **
05886 */
05887 void GetGCStatusLoop::GetGasFilterDateChanged(char *date)
05888 {
05889     // TODO: Fill in, i.e. get the date from the GC somehow
05890     int index = 0;
05891     date[index++] = '0';
05892     date[index++] = '1';
05893     date[index++] = '/';
05894     date[index++] = '0';
05895     date[index++] = '1';
05896     date[index++] = '/';
05897     date[index++] = '2';
05898     date[index++] = '0';
05899     date[index++] = '1';
05900     date[index++] = '6';
05901     date[index]   = '\0';
05902 }
05903 
05904 
05905 /*
05906     Displays the data on the gas page, by copying it to the relevant easyGUI variables,
05907     and calling the relevant functions to actually display it.
05908     
05909     Args: a boolean specifying whether or not the display is actually to be updated.
05910           Even if it is false when we are called, we may set it true if we discover
05911           one or more data items has changed - but note that we will not set it false 
05912           if it is already true. If it is true after we have looked at all 
05913           the home page data, we will then update the display. (If we are called
05914           with this value set to false, the caller is effectively saying
05915           'display the gas page data only if it has changed').
05916           
05917     No return code.
05918 */
05919 void GetGCStatusLoop::DisplayGasInformationPageData(bool mustUpdateDisplay)
05920 {
05921     //EasyGUIDebugPrint("Gas Page", 100, 100);
05922     // Gas pressure and control mode
05923     char buff[60];
05924     
05925     GetGasPressure(buff, false, true);
05926     if(strcmp(buff, GuiVar_gasPressure) != 0) {
05927         strcpy(GuiVar_gasPressure, buff);
05928 
05929         //mustUpdateDisplay = true;
05930         // No - just do this (don't force entire rectangle to redisplay)
05931         // Hard coded values taken from easyGUI
05932         RedrawSingleEasyGUIVariableOnComponentPage(430, 90, &GuiVar_gasPressure, GuiLib_ALIGN_LEFT, GAS);
05933     }
05934 
05935     GetGasPulsedPressure(buff);
05936     if(strcmp(buff, GuiVar_gasPulsedPressure) != 0) {
05937         strcpy(GuiVar_gasPulsedPressure, buff);
05938 
05939         //mustUpdateDisplay = true;
05940         // No - just do this (don't force entire rectangle to redisplay)
05941         // Hard coded values taken from easyGUI
05942         RedrawSingleEasyGUIVariableOnComponentPage(430, 130, &GuiVar_gasPulsedPressure, GuiLib_ALIGN_LEFT, GAS);
05943     }
05944 
05945     GetGasControlMode(buff, true, false);
05946     if(strcmp(buff, GuiVar_gasControlMode2) != 0) {
05947         strcpy(GuiVar_gasControlMode2, buff);
05948 
05949         //mustUpdateDisplay = true;
05950         // No - just do this (don't force entire rectangle to redisplay)
05951         // Hard coded values taken from easyGUI
05952         RedrawSingleEasyGUIVariableOnComponentPage(430, 170, &GuiVar_gasControlMode2, GuiLib_ALIGN_LEFT, GAS);
05953     }
05954     
05955     GetCarrierGasType(buff);
05956     if(strcmp(buff, GuiVar_gasCarrierType) != 0) {
05957         strcpy(GuiVar_gasCarrierType, buff);
05958 
05959         //mustUpdateDisplay = true;
05960         // No - just do this (don't force entire rectangle to redisplay)
05961         // Hard coded values taken from easyGUI
05962         RedrawSingleEasyGUIVariableOnComponentPage(430, 210, &GuiVar_gasCarrierType, GuiLib_ALIGN_LEFT, GAS);
05963     }
05964 
05965     GetGasFilterDateChanged(buff);
05966     if(strcmp(buff, GuiVar_gasFilterDateChanged) != 0) {
05967         strcpy(GuiVar_gasFilterDateChanged, buff);
05968 
05969         //mustUpdateDisplay = true;
05970         // No - just do this (don't force entire rectangle to redisplay)
05971         // Hard coded values taken from easyGUI
05972         RedrawSingleEasyGUIVariableOnComponentPage(430, 270, &GuiVar_gasFilterDateChanged, GuiLib_ALIGN_LEFT, GAS);
05973     }
05974 
05975     if(SinglePageGCComponentStatusHasChanged(GAS, lastGasStatusDisplayedOnGasInformationPage)) {
05976         mustUpdateDisplay = true;
05977     }
05978 
05979     if(mustUpdateDisplay) {
05980 
05981 #ifdef WANT_GAS_STATUS_RECTANGLE
05982         // Reduce display flickering - get the component status from the GC
05983         // *before* we call GuiLib_Clear()
05984         if(singleGCComponentPageStatusColorAreas != NULL) {
05985             UpdateSingleGCComponentPageStatusColorArea(GAS);
05986         }
05987 #endif
05988         
05989 #ifndef WANT_GAS_STATUS_RECTANGLE
05990         // Makes the display flicker - but omitting it means old text is not cleared from the display
05991 #define WANT_GUILIB_CLEAR
05992 #endif
05993 
05994 #ifdef WANT_GUILIB_CLEAR
05995 #ifdef USING_BACKGROUND_BITMAP
05996         DrawBackgroundBitmap(); // We want the status rectangles to be 'on top' of this - 
05997                                 // if we include it in the easyGUI page itself, it gets drawn by GuiLib_ShowScreen,
05998                                 // and overwrites the rectangles
05999 #else
06000         GuiLib_Clear();
06001 #endif
06002 #undef WANT_GUILIB_CLEAR
06003 #endif
06004         //...except that redrawing the status rectangle effectively clears the text on top of the rectangle -
06005         // so we do not need GuiLib_Clear here, since all the gas page data appears on top of the rectangle. 
06006         // Without it, only the text flickers, not the rectangle
06007 
06008 #ifdef WANT_GAS_STATUS_RECTANGLE
06009         // Note - we draw the status rectangle after GuiLib_Clear - otherwise we wouldn't see the rectangles at all - 
06010         // and before GuiLib_ShowScreen - so text, etc, is drawn on top of the rectangles
06011         if(singleGCComponentPageStatusColorAreas != NULL) {
06012             singleGCComponentPageStatusColorAreas->DisplayGCComponentStatus(GAS);
06013 
06014             lastGasStatusDisplayedOnGasInformationPage = singleGCComponentPageStatusColorAreas->GetGCComponentStatus(GAS);
06015         }
06016 #endif
06017         
06018         // And (currently) we draw the component bitmap on top of the rectangle
06019         if(qspiBitmaps != NULL) {
06020             qspiBitmaps->DisplayGasComponentBitmap();
06021         }
06022 
06023         GuiLib_ShowScreen(GuiStruct_GasInformationPage_6, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
06024     
06025         GuiLib_Refresh();    
06026 
06027 #define DEBUG_HERE
06028 #ifdef DEBUG_HERE
06029     char dbg[100];
06030     sprintf(dbg, "After GuiLib_Clear 5");
06031     EasyGUIDebugPrint(dbg, 0, 20);
06032 #undef DEBUG_HERE
06033 #endif
06034     }
06035 }
06036 
06037 
06038 /*
06039     Public function, allowing an external caller to tell us 
06040     to display, or not, the ramp scroll buttons on the Gas Method page.
06041     
06042     Assume that the caller knows the Injector Method page *is*
06043     currently being displayed.
06044 */
06045 void GetGCStatusLoop::ShowGasMethodPageScrollButtonsIfNecessary(void)
06046 {
06047     ShowMethodPageScrollButtonsIfNecessary(gasMethodRampData);
06048 }
06049 
06050 void GetGCStatusLoop::ScrollGasMethodRampsUpIfPossible(void)
06051 {
06052     if(currentPage == GuiStruct_GasMethodPage_Def) {
06053         if(gasMethodPageScrollIndex > 0) {
06054             --gasMethodPageScrollIndex;
06055                 
06056             DisplayGasMethodPageData(false);
06057         }
06058     }
06059 }
06060 
06061 void GetGCStatusLoop::ScrollGasMethodRampsDownIfPossible(void)
06062 {
06063     if(currentPage == GuiStruct_GasMethodPage_Def) {
06064         if(gasMethodRampData != NULL) {
06065             if(gasMethodPageScrollIndex < gasMethodRampData->GetScrollRange()) {
06066                 ++gasMethodPageScrollIndex;
06067                 
06068                 DisplayGasMethodPageData(false);
06069             }
06070         }
06071     }
06072 }
06073 
06074 /*
06075     Displays the data on the Gas Method page, by copying it to the relevant easyGUI variables,
06076     and calling the relevant functions to actually display it.
06077     
06078     Args: a boolean specifying whether or not the display is actually to be updated.
06079           Even if it is false when we are called, we may set it true if we discover
06080           one or more data items has changed - but note that we will not set it false 
06081           if it is already true. If it is true after we have looked at all 
06082           the home page data, we will then update the display. (If we are called
06083           with this value set to false, the caller is effectively saying
06084           'display the column page data only if it has changed').
06085           
06086     No return code.
06087 */
06088 void GetGCStatusLoop::DisplayGasMethodPageData(bool mustUpdateDisplay)
06089 {
06090     char buff[40];
06091     
06092     GetGasPressure(buff, false, false);
06093     if(strcmp(buff, GuiVar_gasMethodInitialPressure) != 0) {
06094         strcpy(GuiVar_gasMethodInitialPressure, buff);
06095 
06096         //mustUpdateDisplay = true;
06097         // No - just do this (don't force entire page to redisplay)
06098         // Hard coded values taken from easyGUI
06099         RedrawSingleEasyGUIVariableOnComponentPage(520, 85, &GuiVar_gasMethodInitialPressure, GuiLib_ALIGN_RIGHT, COLUMN);
06100     }
06101 
06102     GetInitialHoldTime(buff);
06103     if(strcmp(buff, GuiVar_gasMethodInitialHold) != 0) {
06104         strcpy(GuiVar_gasMethodInitialHold, buff);
06105 
06106         //mustUpdateDisplay = true;
06107         // No - just do this (don't force entire page to redisplay)
06108         // Hard coded values taken from easyGUI
06109         RedrawSingleEasyGUIVariableOnComponentPage(520, 120, &GuiVar_gasMethodInitialHold, GuiLib_ALIGN_RIGHT, COLUMN);
06110     }
06111 
06112     if(gasMethodRampData == NULL) {
06113         gasMethodRampData = new GasMethodRampData(usbDevice, usbHostGC);
06114     }
06115     
06116     if(gasMethodRampData != NULL) {
06117         if(!gasMethodRampData->GotRampData()) {
06118             gasMethodRampData->GetRampDataFromGC();
06119         }
06120         
06121         sprintf(buff, "%u", gasMethodRampData->GetRampCount());
06122         if(strcmp(buff, GuiVar_gasMethodRampCount) != 0) {
06123             strcpy(GuiVar_gasMethodRampCount, buff);
06124     
06125             //mustUpdateDisplay = true;
06126             // No - just do this (don't force entire page to redisplay)
06127             // Hard coded values taken from easyGUI
06128             RedrawSingleEasyGUIVariableOnComponentPage(520, 155, &GuiVar_gasMethodRampCount, GuiLib_ALIGN_RIGHT, COLUMN);
06129         }
06130         
06131         if(gasMethodRampData->NeedToUpdateEasyGUIMethodPageRampVariables()) {
06132             
06133             gasMethodPageScrollIndex = 0;
06134             
06135             gasMethodRampData->UpdateEasyGUIMethodPageVariables(gasMethodPageScrollIndex);
06136             
06137             previousGasMethodPageScrollIndex = gasMethodPageScrollIndex;
06138             
06139             mustUpdateDisplay = true; // Not practical to write all these variables individually
06140 
06141         } else if (previousGasMethodPageScrollIndex != gasMethodPageScrollIndex) {
06142 
06143             gasMethodRampData->UpdateEasyGUIMethodPageVariables(gasMethodPageScrollIndex);
06144             
06145             previousGasMethodPageScrollIndex = gasMethodPageScrollIndex;
06146             
06147             mustUpdateDisplay = true; // Not practical to write all these variables individually
06148         }
06149     }
06150     
06151     if(SinglePageGCComponentStatusHasChanged(GAS)) {
06152         mustUpdateDisplay = true;
06153     }
06154 
06155     if(mustUpdateDisplay) {
06156 
06157 #ifdef WANT_INJECTOR_STATUS_RECTANGLE        
06158         // Reduce display flickering - get the component status from the GC
06159         // *before* we call GuiLib_Clear()
06160         if(singleGCComponentPageStatusColorAreas != NULL) {
06161             UpdateSingleGCComponentPageStatusColorArea(GAS);
06162         }
06163 #endif       
06164 
06165 #ifndef WANT_INJECTOR_STATUS_RECTANGLE
06166 #define WANT_GUILIB_CLEAR
06167 #endif
06168 
06169 #ifdef WANT_GUILIB_CLEAR
06170 #ifdef USING_BACKGROUND_BITMAP
06171         DrawBackgroundBitmap(); // We want the status rectangles to be 'on top' of this - 
06172                                 // if we include it in the easyGUI page itself, it gets drawn by GuiLib_ShowScreen,
06173                                 // and overwrites the rectangles
06174 #else
06175         GuiLib_Clear();
06176 #endif
06177 #undef WANT_GUILIB_CLEAR
06178 #endif
06179         //...except that redrawing the status rectangle effectively clears the text on top of the rectangle -
06180         // so we do not need GuiLib_Clear here, since all the column page data appears on top of the rectangle. 
06181         // Without it, only the text flickers, not the rectangle
06182 
06183 #ifdef WANT_INJECTOR_STATUS_RECTANGLE
06184         // Note - we draw the status rectangle after GuiLib_Clear - otherwise we wouldn't see the rectangles at all - 
06185         // and before GuiLib_ShowScreen - so text, etc, is drawn on top of the rectangles
06186         if(singleGCComponentPageStatusColorAreas != NULL) {
06187             singleGCComponentPageStatusColorAreas->DisplayGCComponentStatus(GAS);
06188         }
06189 #endif
06190 
06191         // And (currently) we draw the component bitmap on top of the rectangle
06192         if(qspiBitmaps != NULL) {
06193             qspiBitmaps->DisplayGasComponentBitmap();
06194         }
06195 
06196         GuiLib_ShowScreen(GuiStruct_GasMethodPage_Def, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
06197     
06198         ShowMethodPageScrollButtonsIfNecessary(gasMethodRampData);
06199 
06200         GuiLib_Refresh();    
06201     }
06202 }
06203 
06204 
06205 /*
06206     Gas pressure profile X axis units may be minutes or seconds.
06207     Set up the label easyGUI variable appropriately.
06208 */
06209 void GetGCStatusLoop::SetupGasFlowProfilePageXAxisLabel(TimeUnit timeUnit)
06210 {
06211     if(timeUnit == SECONDS) {
06212         strcpy(GuiVar_gasFlowProfilePageXAxisLabel, "Time (seconds)");
06213     } else {
06214         strcpy(GuiVar_gasFlowProfilePageXAxisLabel, "Time (minutes)");
06215     }
06216 }
06217 
06218 /*
06219     Sets up the data for the graph on the Gas Flow Profile page,
06220     and causes it to be displayed
06221 */
06222 void GetGCStatusLoop::DisplayGasFlowProfilePageGraph(void)
06223 {
06224     // Test values only:
06225     // - dataset 0 is a line representing the flow profile of the run from 'now' to the finish
06226     // - dataset 1 is a bar chart representing the flow profile of the run from 'now' to the finish
06227     // - dataset 2 is a line representing the flow profile from the start to 'now'
06228     // - dataset 3 is a bar chart representing the flow profile of the run from the start to 'now'
06229     // - dataset 4 is a single dot at the current time and temperature
06230     
06231     TimeUnit timeUnit = MINUTES;
06232     if(gasFlowProfilePageGraphCompleteProfileDataSet->GetTotalMethodTime() < methodTimeUnitsThreshold) {
06233         timeUnit = SECONDS;
06234     }
06235     
06236     const float yAxisScaleFactor = 10.0f;
06237 
06238     // Dataset 0
06239     gasFlowProfilePageGraph->SetDataForGraphDataSetInTenthsOfMinutes(0, yAxisScaleFactor, gasFlowProfilePageGraphDataSet0);
06240     
06241     // Dataset 1 
06242     gasFlowProfilePageGraph->SetDataForGraphDataSetInTenthsOfMinutes(1, yAxisScaleFactor, gasFlowProfilePageGraphDataSet1);
06243     
06244     // Dataset 2
06245     gasFlowProfilePageGraph->SetDataForGraphDataSetInTenthsOfMinutes(2, yAxisScaleFactor, gasFlowProfilePageGraphDataSet2);
06246     
06247     // Dataset 3
06248     gasFlowProfilePageGraph->SetDataForGraphDataSetInTenthsOfMinutes(3, yAxisScaleFactor, gasFlowProfilePageGraphDataSet3);
06249     
06250     // Dataset 4
06251     gasFlowProfilePageGraph->SetDataForGraphDataSetInTenthsOfMinutes(4, yAxisScaleFactor, gasFlowProfilePageGraphDataSet4);
06252     
06253     gasFlowProfilePageGraph->SetXAxisUnits(timeUnit);
06254     
06255     if(timeUnit == SECONDS) {
06256         strcpy(GuiVar_gasFlowProfilePageXAxisLabel, "Time (seconds)");
06257     } else {
06258         strcpy(GuiVar_gasFlowProfilePageXAxisLabel, "Time (minutes)");
06259     }
06260     
06261     
06262     if(gasFlowProfilePageGraphCompleteProfileDataSet->GetPointCount() == 0) {
06263         strcpy(GuiVar_gasFlowProfilePageNoMethod, "No method set up");
06264     } else {
06265         GuiVar_gasFlowProfilePageNoMethod[0] = '\0';
06266     }
06267 
06268     // The tick sizes must match those set in easyGUI - we do not seem 
06269     // to be able to get them at runtime
06270     GuiConst_INT32S minX, maxX;
06271     //GuiConst_INT32S tickSize = (timeUnit == SECONDS) ? 300 : 100;
06272     // Always in 1/10 minutes
06273     GuiConst_INT32S xAxisTickSize = (timeUnit == SECONDS) ? 5 : 100; // 5 == 0.5 minutes (i.e. 30 seconds), 100 == 10 minutes
06274     gasFlowProfilePageGraphCompleteProfileDataSet->GetXAxisRangeInTenthsOfMinutes(&minX, &maxX, xAxisTickSize);
06275     gasFlowProfilePageGraph->SetXAxisRange(0, maxX); // Always start X axis at zero
06276     
06277     GuiConst_INT32S minY, maxY;
06278     GuiConst_INT32S yAxisTickSize = 10;
06279     gasFlowProfilePageGraphCompleteProfileDataSet->GetYAxisRange(&minY, &maxY, yAxisTickSize);
06280     gasFlowProfilePageGraph->SetYAxisRange(0, (maxY * yAxisScaleFactor)); // Always start Y axis at zero
06281     
06282     
06283     gasFlowProfilePageGraph->DrawAxes();
06284 
06285     // We need to draw the X axis labels ourselves, since our time values are in units of 0.1 minute,
06286     // but easyGUI graphs work only in integers - we therefore have to multiply the time values by 10
06287     // before passing them to easyGUI, and if we let easyGUI draw its own values on the X axis, 
06288     // they would be 10 times the correct values.
06289     // Graph coordinates copied from easyGUI - I cannot find a way of obtaining them at runtime.
06290     if(timeUnit == SECONDS) {
06291         gasFlowProfilePageGraph->DrawXAxisLabels(0, (maxX * 6), (xAxisTickSize * 6), 150, 340, 500);
06292     } else {
06293         gasFlowProfilePageGraph->DrawXAxisLabels(0, (maxX / 10), (xAxisTickSize / 10), 150, 340, 500);
06294     }
06295     // Note that we repeat this call by calling 'DrawXAxisLabels' without arguments 
06296     // every time we (re)display this page
06297 
06298     // Similar to the X axis - the values we pass to the easyGUI graph are scaled to be larger
06299     // than the real values, to get round the fact that easyGUI graphs work in integers.
06300     // (This can cause the profile to appear to be 'stepped', which obviously we do not want.)
06301     // We must therefore draw the Y axis values ourselves.
06302     gasFlowProfilePageGraph->DrawYAxisLabels(0, maxY, yAxisTickSize, 150, 340, 250);
06303     // Note that we repeat this call by calling 'DrawYAxisLabels' without arguments 
06304     // every time we (re)display this page
06305 
06306     gasFlowProfilePageGraph->DrawDataSet(0);
06307     gasFlowProfilePageGraph->ShowDataSet(0);
06308 
06309     gasFlowProfilePageGraph->DrawDataSet(1);
06310     gasFlowProfilePageGraph->ShowDataSet(1);
06311 
06312     gasFlowProfilePageGraph->DrawDataSet(2);
06313     gasFlowProfilePageGraph->ShowDataSet(2);
06314 
06315     gasFlowProfilePageGraph->DrawDataSet(3);
06316     gasFlowProfilePageGraph->ShowDataSet(3);
06317 
06318     gasFlowProfilePageGraph->DrawDataSet(4);
06319     gasFlowProfilePageGraph->ShowDataSet(4);
06320 
06321     gasFlowProfilePageGraph->Redraw();
06322     
06323 #ifdef TEST_GUILIB_VLINE_PROFILES
06324     // *** TESTING *** Draw the profile as a solid colour, direct to the display
06325     //                 Coords manually copied from easyGUI application.
06326     //                 Boundary between colours is arbitrary for now
06327     GuiConst_INTCOLOR graphColour1 = SixteenBitColorValue(100, 100, 100);
06328     GuiConst_INTCOLOR graphColour2 = SixteenBitColorValue(200, 200, 200);
06329 //    double colourBoundaryX = (double) gasFlowProfilePageGraphCompleteProfileDataSet->GetTotalMethodTime() / 5.0; // Should be one-fifth across the profile
06330     double colourBoundaryX = -1.0; // No - use one colour only
06331     if(timeUnit == SECONDS) {
06332         gasFlowProfilePageGraphCompleteProfileDataSet->DrawUsingGuiLibVLine(160, 330, ((double) 500 / (double) (maxX * 6)), ((double) -250 / (double) maxY), graphColour1, graphColour2, colourBoundaryX);
06333     } else {
06334         gasFlowProfilePageGraphCompleteProfileDataSet->DrawUsingGuiLibVLine(160, 330, ((double) 500 / (double) (maxX / 10)), ((double) -250 / (double) maxY), graphColour1, graphColour2, colourBoundaryX);
06335     }
06336 #endif // TEST_GUILIB_VLINE_PROFILES
06337 }
06338 
06339 void GetGCStatusLoop::DisplayGasFlowProfilePageData(bool mustUpdateDisplay)
06340 {
06341     // Ensure this is always up to date
06342     if(needToUpdateProfileGraphs) {
06343         SetupColumnAndInjectorAndGasProfileData();
06344         mustUpdateDisplay = true;
06345 
06346         needToUpdateProfileGraphs = false;
06347     }
06348     
06349     DisplayGasFlowProfilePageGraph();
06350     
06351     if(SinglePageGCComponentStatusHasChanged(GAS)) {
06352         mustUpdateDisplay = true;
06353     }
06354 
06355     if(mustUpdateDisplay) {
06356 
06357 #ifdef WANT_GAS_STATUS_RECTANGLE
06358         // Reduce display flickering - get the component status from the GC
06359         // *before* we call GuiLib_Clear()
06360         if(singleGCComponentPageStatusColorAreas != NULL) {
06361             UpdateSingleGCComponentPageStatusColorArea(GAS);
06362         }
06363 #endif
06364         
06365 #ifndef WANT_GAS_STATUS_RECTANGLE
06366         // Makes the display flicker - but omitting it means old text is not cleared from the display
06367 #define WANT_GUILIB_CLEAR
06368 #endif
06369 
06370 #ifdef WANT_GUILIB_CLEAR
06371 #ifdef USING_BACKGROUND_BITMAP
06372         DrawBackgroundBitmap(); // We want the status rectangles to be 'on top' of this - 
06373                                 // if we include it in the easyGUI page itself, it gets drawn by GuiLib_ShowScreen,
06374                                 // and overwrites the rectangles
06375 #else
06376         GuiLib_Clear();
06377 #endif
06378 #undef WANT_GUILIB_CLEAR
06379 #endif
06380         //...except that redrawing the status rectangle effectively clears the text on top of the rectangle -
06381         // so we do not need GuiLib_Clear here, since all the injector page data appears on top of the rectangle. 
06382         // Without it, only the text flickers, not the rectangle
06383 
06384 #ifdef WANT_GAS_STATUS_RECTANGLE
06385         // Note - we draw the status rectangle after GuiLib_Clear - otherwise we wouldn't see the rectangles at all - 
06386         // and before GuiLib_ShowScreen - so text, etc, is drawn on top of the rectangles
06387         if(singleGCComponentPageStatusColorAreas != NULL) {
06388             singleGCComponentPageStatusColorAreas->DisplayGCComponentStatus(GAS);
06389         }
06390 #endif
06391 
06392 #ifdef WANT_COMPONENT_ICON_ON_PROFILE_PAGES
06393         // And (currently) we draw the component bitmap on top of the rectangle
06394         if(qspiBitmaps != NULL) {
06395             qspiBitmaps->DisplayGasComponentBitmap();
06396         }
06397 #endif // WANT_COMPONENT_ICON_ON_PROFILE_PAGES
06398 
06399         gasFlowProfilePageGraph->Redraw();
06400     
06401 #ifdef TEST_GUILIB_VLINE_PROFILES
06402         // Now draw the profile as a solid colour, direct to the display.
06403         // Use the same parameters as the previous call, in DisplayInjectorTempProfilePageGraph
06404         gasFlowProfilePageGraphCompleteProfileDataSet->DrawUsingGuiLibVLine();
06405 #endif // TEST_GUILIB_VLINE_PROFILES
06406 
06407         GuiLib_ShowScreen(GuiStruct_GasProfilePage_15, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
06408     
06409         // Repeat the previous call to the version of this function with parameters,
06410         // made from 'DisplayGasFlowProfilePageGraph()' above (it is more convenient 
06411         // to calculate the parameter values in 'DisplayGasFlowProfilePageGraph()' than here)
06412         gasFlowProfilePageGraph->DrawXAxisLabels();
06413         gasFlowProfilePageGraph->DrawYAxisLabels();
06414     
06415         GuiLib_Refresh();    
06416 
06417 #define DEBUG_HERE
06418 #ifdef DEBUG_HERE
06419     char dbg[100];
06420     sprintf(dbg, "After GuiLib_Clear 99");
06421     EasyGUIDebugPrint(dbg, 0, 20);
06422 #undef DEBUG_HERE
06423 #endif
06424     }
06425 }
06426 
06427 /*
06428     Displays the data on the gas calibration page, by copying it to the relevant easyGUI variables,
06429     and calling the relevant functions to actually display it.
06430     
06431     Args: a boolean specifying whether or not the display is actually to be updated.
06432           Even if it is false when we are called, we may set it true if we discover
06433           one or more data items has changed - but note that we will not set it false 
06434           if it is already true. If it is true after we have looked at all 
06435           the home page data, we will then update the display. (If we are called
06436           with this value set to false, the caller is effectively saying
06437           'display the gas page data only if it has changed').
06438           
06439     No return code.
06440 */
06441 void GetGCStatusLoop::DisplayGasCalibrationPageData(bool mustUpdateDisplay)
06442 {
06443     //EasyGUIDebugPrint("Gas Calibration Page", 100, 100);
06444     // Gas pressure and control mode
06445     
06446     if(SinglePageGCComponentStatusHasChanged(GAS)) {
06447         mustUpdateDisplay = true;
06448     }
06449 
06450     if(mustUpdateDisplay) {
06451 
06452         // Reduce display flickering - get the component status from the GC
06453         // *before* we call GuiLib_Clear()
06454         if(singleGCComponentPageStatusColorAreas != NULL) {
06455             UpdateSingleGCComponentPageStatusColorArea(GAS);
06456         }
06457         
06458         // Makes the display flicker - but omitting it means old text is not cleared from the display
06459 //#define WANT_GUILIB_CLEAR
06460 #ifdef WANT_GUILIB_CLEAR
06461 #ifdef USING_BACKGROUND_BITMAP
06462         DrawBackgroundBitmap(); // We want the status rectangles to be 'on top' of this - 
06463                                 // if we include it in the easyGUI page itself, it gets drawn by GuiLib_ShowScreen,
06464                                 // and overwrites the rectangles
06465 #else
06466         GuiLib_Clear();
06467 #endif
06468 #undef WANT_GUILIB_CLEAR
06469 #endif
06470         //...except that redrawing the status rectangle effectively clears the text on top of the rectangle -
06471         // so we do not need GuiLib_Clear here, since all the gas page data appears on top of the rectangle. 
06472         // Without it, only the text flickers, not the rectangle
06473 
06474         // Note - we draw the status rectangle after GuiLib_Clear - otherwise we wouldn't see the rectangles at all - 
06475         // and before GuiLib_ShowScreen - so text, etc, is drawn on top of the rectangles
06476 #ifdef WANT_STATUS_RECTANGLE_ON_GAS_CALIB_PAGES        
06477         if(singleGCComponentPageStatusColorAreas != NULL) {
06478             singleGCComponentPageStatusColorAreas->DisplayGCComponentStatus(GAS);
06479         }
06480 #else // Need to clear old text some other way...
06481 #ifdef USING_BACKGROUND_BITMAP
06482         DrawBackgroundBitmap();
06483 #else
06484         GuiLib_Clear();
06485 #endif // USING_BACKGROUND_BITMAP
06486 #endif // WANT_STATUS_RECTANGLE_ON_GAS_CALIB_PAGES
06487 
06488         GuiLib_ShowScreen(GuiStruct_GasCalibrationPage_Def, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
06489     
06490         GuiLib_Refresh();    
06491 
06492 #define DEBUG_HERE
06493 #ifdef DEBUG_HERE
06494     char dbg[100];
06495     sprintf(dbg, "After GuiLib_Clear 105");
06496     EasyGUIDebugPrint(dbg, 0, 20);
06497 #undef DEBUG_HERE
06498 #endif
06499     }
06500 }
06501 
06502 
06503 /*
06504     Displays the data on the gas backpressure DAC page, by copying it to the relevant easyGUI variables,
06505     and calling the relevant functions to actually display it.
06506     
06507     Args: a boolean specifying whether or not the display is actually to be updated.
06508           Even if it is false when we are called, we may set it true if we discover
06509           one or more data items has changed - but note that we will not set it false 
06510           if it is already true. If it is true after we have looked at all 
06511           the home page data, we will then update the display. (If we are called
06512           with this value set to false, the caller is effectively saying
06513           'display the gas page data only if it has changed').
06514           
06515     No return code.
06516 */
06517 void GetGCStatusLoop::DisplayGasBackPressureDACPageData(bool mustUpdateDisplay)
06518 {
06519     //EasyGUIDebugPrint("Gas Backpressure DAC Page", 100, 100);
06520     // Gas pressure and control mode
06521     
06522     if(SinglePageGCComponentStatusHasChanged(GAS)) {
06523         mustUpdateDisplay = true;
06524     }
06525 
06526     if(mustUpdateDisplay) {
06527 
06528         // Reduce display flickering - get the component status from the GC
06529         // *before* we call GuiLib_Clear()
06530         if(singleGCComponentPageStatusColorAreas != NULL) {
06531             UpdateSingleGCComponentPageStatusColorArea(GAS);
06532         }
06533         
06534         // Makes the display flicker - but omitting it means old text is not cleared from the display
06535 //#define WANT_GUILIB_CLEAR
06536 #ifdef WANT_GUILIB_CLEAR
06537 #ifdef USING_BACKGROUND_BITMAP
06538         DrawBackgroundBitmap(); // We want the status rectangles to be 'on top' of this - 
06539                                 // if we include it in the easyGUI page itself, it gets drawn by GuiLib_ShowScreen,
06540                                 // and overwrites the rectangles
06541 #else
06542         GuiLib_Clear();
06543 #endif
06544 #undef WANT_GUILIB_CLEAR
06545 #endif
06546         //...except that redrawing the status rectangle effectively clears the text on top of the rectangle -
06547         // so we do not need GuiLib_Clear here, since all the gas page data appears on top of the rectangle. 
06548         // Without it, only the text flickers, not the rectangle
06549 
06550         // Note - we draw the status rectangle after GuiLib_Clear - otherwise we wouldn't see the rectangles at all - 
06551         // and before GuiLib_ShowScreen - so text, etc, is drawn on top of the rectangles
06552 #ifdef WANT_STATUS_RECTANGLE_ON_GAS_CALIB_PAGES
06553         if(singleGCComponentPageStatusColorAreas != NULL) {
06554             singleGCComponentPageStatusColorAreas->DisplayGCComponentStatus(GAS);
06555         }
06556 #else // Need to clear old text some other way...
06557 #ifdef USING_BACKGROUND_BITMAP
06558         DrawBackgroundBitmap();
06559 #else
06560         GuiLib_Clear();
06561 #endif // USING_BACKGROUND_BITMAP
06562 #endif // WANT_STATUS_RECTANGLE_ON_GAS_CALIB_PAGES
06563 
06564         GuiLib_ShowScreen(GuiStruct_GasBackPressureDACPage_Def, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
06565     
06566         GuiLib_Refresh();    
06567 
06568 #define DEBUG_HERE
06569 #ifdef DEBUG_HERE
06570     char dbg[100];
06571     sprintf(dbg, "After GuiLib_Clear 105");
06572     EasyGUIDebugPrint(dbg, 0, 20);
06573 #undef DEBUG_HERE
06574 #endif
06575     }
06576 }
06577 
06578 
06579 /*
06580     Displays the data on the gas channel DAC and ADC page, by copying it to the relevant easyGUI variables,
06581     and calling the relevant functions to actually display it.
06582     
06583     Args: a boolean specifying whether or not the display is actually to be updated.
06584           Even if it is false when we are called, we may set it true if we discover
06585           one or more data items has changed - but note that we will not set it false 
06586           if it is already true. If it is true after we have looked at all 
06587           the home page data, we will then update the display. (If we are called
06588           with this value set to false, the caller is effectively saying
06589           'display the gas page data only if it has changed').
06590           
06591     No return code.
06592 */
06593 void GetGCStatusLoop::DisplayGasChannelDACAndADCPageData(bool mustUpdateDisplay)
06594 {
06595     //EasyGUIDebugPrint("Gas Backpressure DAC Page", 100, 100);
06596     // Gas pressure and control mode
06597     
06598     if(SinglePageGCComponentStatusHasChanged(GAS)) {
06599         mustUpdateDisplay = true;
06600     }
06601 
06602     if(mustUpdateDisplay) {
06603 
06604         // Reduce display flickering - get the component status from the GC
06605         // *before* we call GuiLib_Clear()
06606         if(singleGCComponentPageStatusColorAreas != NULL) {
06607             UpdateSingleGCComponentPageStatusColorArea(GAS);
06608         }
06609         
06610         // Makes the display flicker - but omitting it means old text is not cleared from the display
06611 //#define WANT_GUILIB_CLEAR
06612 #ifdef WANT_GUILIB_CLEAR
06613 #ifdef USING_BACKGROUND_BITMAP
06614         DrawBackgroundBitmap(); // We want the status rectangles to be 'on top' of this - 
06615                                 // if we include it in the easyGUI page itself, it gets drawn by GuiLib_ShowScreen,
06616                                 // and overwrites the rectangles
06617 #else
06618         GuiLib_Clear();
06619 #endif
06620 #undef WANT_GUILIB_CLEAR
06621 #endif
06622         //...except that redrawing the status rectangle effectively clears the text on top of the rectangle -
06623         // so we do not need GuiLib_Clear here, since all the gas page data appears on top of the rectangle. 
06624         // Without it, only the text flickers, not the rectangle
06625 
06626         // Note - we draw the status rectangle after GuiLib_Clear - otherwise we wouldn't see the rectangles at all - 
06627         // and before GuiLib_ShowScreen - so text, etc, is drawn on top of the rectangles
06628 #ifdef WANT_STATUS_RECTANGLE_ON_GAS_CALIB_PAGES
06629         if(singleGCComponentPageStatusColorAreas != NULL) {
06630             singleGCComponentPageStatusColorAreas->DisplayGCComponentStatus(GAS);
06631         }
06632 #else // Need to clear old text some other way...
06633 #ifdef USING_BACKGROUND_BITMAP
06634         DrawBackgroundBitmap();
06635 #else
06636         GuiLib_Clear();
06637 #endif // USING_BACKGROUND_BITMAP
06638 #endif // WANT_STATUS_RECTANGLE_ON_GAS_CALIB_PAGES
06639 
06640         GuiLib_ShowScreen(GuiStruct_GasChannelDACAndADCPage_Def, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
06641     
06642         GuiLib_Refresh();    
06643 
06644 #define DEBUG_HERE
06645 #ifdef DEBUG_HERE
06646     char dbg[100];
06647     sprintf(dbg, "After GuiLib_Clear 105");
06648     EasyGUIDebugPrint(dbg, 0, 20);
06649 #undef DEBUG_HERE
06650 #endif
06651     }
06652 }
06653 
06654 
06655 /*
06656     A public function allowing other classes (specifically GasCalibrationPageHandler),
06657     having changed something on the gas calibration page, to tell us to redisplay it
06658 */
06659 void GetGCStatusLoop::ForceUpdateOfGasCalibrationPage(void)
06660 {
06661     if(currentPage == GuiStruct_GasCalibrationPage_Def) {
06662         DisplayGasCalibrationPageData(true);
06663     }
06664 }
06665 
06666 
06667 /*
06668     A public function allowing other classes (specifically ColumnDHManualCalibrationPageHandler),
06669     having changed something on the gas calibration page, to tell us to redisplay it
06670 */
06671 void GetGCStatusLoop::ForceUpdateOfColumnDHManualCalibrationPage(void)
06672 {
06673     if(currentPage == GuiStruct_ColumnDHManualCalibrationPage_Def) {
06674         DisplayColumnDHManualCalibrationPageData(true);
06675     }
06676 }
06677 
06678 
06679 /*
06680     A public function allowing other classes (specifically ColumnDHManualCalibrationPageHandler),
06681     having changed something on the manual calibration page, to tell us to redisplay it
06682 */
06683 void GetGCStatusLoop::ForceUpdateOfColumnDHSensorCalibrationPage(void)
06684 {
06685     if(currentPage == GuiStruct_ColumnDHSensorCalibration_Def) {
06686         DisplayColumnDHSensorCalibrationPageData(true);
06687     }
06688 }
06689 
06690 
06691 /*
06692     A public function allowing other classes (specifically ColumnDHPSUDACPageHandler),
06693     having changed something on the PSU DAC page, to tell us to redisplay it
06694 */
06695 void GetGCStatusLoop::ForceUpdateOfColumnDHPSUDACPage(void)
06696 {
06697     if(currentPage == GuiStruct_PSU_DAC_Page_Def) {
06698         DisplayColumnDHPSUDACPageData(true);
06699     }
06700 }
06701 
06702 
06703 /*
06704     A public function allowing other classes (specifically ColumnDHAutoCalibrationPageHandler),
06705     having changed something on the auto calibration page, to tell us to redisplay it
06706 */
06707 void GetGCStatusLoop::ForceUpdateOfColumnDHAutoCalibrationPage(void)
06708 {
06709     if(currentPage == GuiStruct_ColumnDHAutoCalibrationPage_Def) {
06710         DisplayColumnDHAutoCalibrationPageData(true);
06711     }
06712 }
06713 
06714 
06715 /*
06716     A public function allowing other classes (specifically GasBackPressureDACPageHandler),
06717     having changed something on the gas back pressure DAC page, to tell us to redisplay it
06718 */
06719 void GetGCStatusLoop::ForceUpdateOfGasBackPressureDACPage(void)
06720 {
06721     if(currentPage == GuiStruct_GasBackPressureDACPage_Def) {
06722         DisplayGasBackPressureDACPageData(true);
06723     }
06724 }
06725 
06726 
06727 /*
06728     A public function allowing other classes (specifically GasChannelDACAndADCPageHandler),
06729     having changed something on the channel DAC and ADC page, to tell us to redisplay it
06730 */
06731 void GetGCStatusLoop::ForceUpdateOfGasChannelDACAndADCPage(void)
06732 {
06733     if(currentPage == GuiStruct_GasChannelDACAndADCPage_Def) {
06734         DisplayGasChannelDACAndADCPageData(true);
06735     }
06736 }
06737 
06738 
06739 
06740 /*
06741     Returns the length of time remaining until the column method finishes.
06742     Value is in minutes.
06743 */
06744 float GetGCStatusLoop::GetColumnMethodTimeRemaining(void)
06745 {
06746     float currentColumnMethodRunTime;
06747     GetRunTime(&currentColumnMethodRunTime);
06748 //    float totalColumnMethodMethodTime = runningColumnPageGraphCompleteProfileDataSet->GetTotalMethodTime();
06749     float totalColumnMethodMethodTime = runningColumnPageGraphCompleteProfileDataSet->GetNonRoundedTotalMethodTime();
06750     
06751 
06752     char dbg[100];
06753     sprintf(dbg, "GCMTR - %f %f", currentColumnMethodRunTime, totalColumnMethodMethodTime);
06754     EasyGUIDebugPrintWithCounter(dbg, 125, 350);
06755 
06756     // This value will be in minutes 
06757     return (totalColumnMethodMethodTime - currentColumnMethodRunTime);
06758 }
06759 
06760 /*
06761     If the GC is running, updates the easyGUI variables 
06762     that display the run time.
06763     
06764     Returns true if it updated [any of] the variables, false if not
06765 */
06766 bool GetGCStatusLoop::UpdateMethodRunTimeEasyGUIVariables(bool runHasCompleted)
06767 {
06768     bool variableUpdated = false;
06769     char buff[50];
06770     
06771     if(GCIsRunning()) {
06772         float currentColumnMethodRunTime;
06773         GetRunTime(&currentColumnMethodRunTime);
06774 
06775         // Always display in units of 0.1 minute
06776         sprintf(buff, "%.1f", currentColumnMethodRunTime);
06777        
06778         if(strcmp(GuiVar_runTimeElapsed, buff) != 0) {
06779             strcpy(GuiVar_runTimeElapsed, buff);
06780             variableUpdated = true;
06781         }
06782         
06783         
06784         float columnMethodTimeRemaining = GetColumnMethodTimeRemaining();
06785         
06786         if(columnMethodTimeRemaining >= 0.0f) {
06787             // We display the value in 'units' of 0.1 minute
06788             sprintf(buff, "(Time remaining: %.1f minutes)", columnMethodTimeRemaining);
06789         } else {
06790             strcpy(buff, "(Time remaining: 0.0 minutes)"); // *Never* display a negative value
06791         }
06792        
06793         if(strcmp(GuiVar_runTimeRemaining, buff) != 0) {
06794             strcpy(GuiVar_runTimeRemaining, buff);
06795             variableUpdated = true;
06796         }
06797         
06798     } else if (runHasCompleted) {
06799 
06800         strcpy(buff, "0.0");
06801        
06802         if(strcmp(GuiVar_runTimeRemaining, buff) != 0) {
06803             strcpy(GuiVar_runTimeRemaining, buff);
06804             variableUpdated = true;
06805         }
06806         
06807         // GuiVar_runTimeElapsed needs to be an empty string
06808         if(GuiVar_runTimeElapsed[0] != '\0') {
06809             GuiVar_runTimeElapsed[0] = '\0';
06810             variableUpdated = true;
06811         }
06812     }
06813     
06814     return variableUpdated;
06815 }
06816 
06817 /*
06818     Update the easyGUI variable that displays the column status on Column page 1
06819     and Column DH page 1
06820 */
06821 void GetGCStatusLoop::UpdateColumnStatusEasyGUIVariable(void)
06822 {
06823     GetComponentStatusString(COLUMN, GuiVar_columnStatus);
06824 }
06825 
06826 void GetGCStatusLoop::UpdateInjectorStatusEasyGUIVariable(void)
06827 {
06828     GetComponentStatusString(INJECTOR, GuiVar_injectorStatus);
06829 }
06830 
06831 /*
06832     Update the calibrated range and current position of the progress bar
06833     displayed on 'RunningPage1' - then re-display it.
06834     
06835     No arguments, no return code
06836 */
06837 void GetGCStatusLoop::UpdateAndDisplayRunningPage1ProgressBar(bool runHasCompleted)
06838 {
06839     if(runningPage1ProgressBar != NULL) {
06840 
06841         float totalColumnMethodMethodTime = runningColumnPageGraphCompleteProfileDataSet->GetNonRoundedTotalMethodTime();
06842     
06843         if(GCIsRunning()) {
06844             float currentColumnMethodRunTime;
06845             GetRunTime(&currentColumnMethodRunTime);
06846             //float totalColumnMethodMethodTime = runningColumnPageGraphCompleteProfileDataSet->GetTotalMethodTime();
06847             runningPage1ProgressBar->SetCalibratedRange((double)totalColumnMethodMethodTime);
06848             
06849             runningPage1ProgressBar->UpdateCalibratedPosition((double)currentColumnMethodRunTime, true);
06850         } else if (runHasCompleted) {
06851             runningPage1ProgressBar->DisplayBarComplete(true);
06852         }
06853     }    
06854 }
06855 
06856 
06857 void GetGCStatusLoop::SetRunningPage1ProgressBarToZero(void)
06858 {
06859     if(runningPage1ProgressBar != NULL) {
06860         runningPage1ProgressBar->UpdateCalibratedPosition(0.0, true);
06861     }
06862 }
06863 
06864 /*
06865     Displays the data on the 'GC is running' page, by copying it to the relevant easyGUI variables,
06866     and calling the relevant functions to actually display it.
06867     
06868     Args: a boolean specifying whether or not the display is actually to be updated.
06869           Even if it is false when we are called, we may set it true if we discover
06870           one or more data items has changed - but note that we will not set it false 
06871           if it is already true. If it is true after we have looked at all 
06872           the home page data, we will then update the display. (If we are called
06873           with this value set to false, the caller is effectively saying
06874           'display the running page data only if it has changed').
06875           
06876     No return code.
06877 */
06878 void GetGCStatusLoop::DisplayRunningPageData(bool mustUpdateDisplay, bool runHasCompleted)
06879 {
06880     if(UpdateMethodRunTimeEasyGUIVariables(runHasCompleted)) {
06881         mustUpdateDisplay = true;
06882     }
06883     
06884     if(mustUpdateDisplay) {
06885 
06886         // Makes the display flicker - but omitting it means old text is not cleared from the display
06887 #ifdef USING_BACKGROUND_BITMAP
06888         DrawBackgroundBitmap(); 
06889 #else
06890         GuiLib_Clear();
06891 #endif
06892 
06893         GuiLib_ShowScreen(GuiStruct_RunningPage1_7, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
06894     
06895         UpdateAndDisplayRunningPage1ProgressBar(runHasCompleted);
06896     
06897         GuiLib_Refresh();
06898     }
06899 
06900 }
06901 
06902 
06903 /*
06904     When the GC starts running a method, set up the easyGUI variables that display
06905     the column temperature, etc, on the relevant graph/profile pages.
06906     This is so that they are all set to valid values before they are displayed.
06907     
06908     This *must* be called every time the GC starts running, whatever the reason
06909       
06910     No arguments, no return value
06911 */
06912 void GetGCStatusLoop::SetupTemperatureWhileRunningEasyGUIVariables(void)
06913 {
06914 //    Get the column type from the GC, put it in the 'runningColumnType' variable.
06915 //    Provided so that:
06916 //     (1) this is *always* called when we start running
06917 //     (2) we do not have to get the column type every time we read the temperature 
06918 //         (the column type is hardly likely to change while we are running)
06919     runningColumnType = GetColumnType();
06920     
06921     if(runningColumnType == DIRECTLY_HEATED_COLUMN) {
06922         GetDirectlyHeatedColumnTemperature(GuiVar_columnTemperatureWhileRunning, false);
06923     } else {
06924         GetColumnTemperature(GuiVar_columnTemperatureWhileRunning, false);
06925     }
06926 
06927     GetInjectorTemperature(GuiVar_injectorTemperatureWhileRunning, false);
06928 
06929     GetCurrentGasPressure(GuiVar_gasPressureWhileRunning, false, true);
06930 }
06931 
06932 
06933 /*
06934     Displays the data on the 'running column' page.
06935     
06936     Args: a boolean specifying whether or not the display is actually to be updated.
06937           Even if it is false when we are called, we may set it true if we discover
06938           one or more data items has changed - but note that we will not set it false 
06939           if it is already true. If it is true after we have looked at all 
06940           the home page data, we will then update the display. (If we are called
06941           with this value set to false, the caller is effectively saying
06942           'display the running settings page data only if it has changed').
06943           
06944     No return code.
06945 */
06946 void GetGCStatusLoop::DisplayRunningColumnPageData(bool mustUpdateDisplay, bool runHasCompleted)
06947 {
06948     // This page consists principally of an easyGUI graph, to which we assign several datasets:
06949     //
06950     // - dataset 0 is a line representing the temperature profile of the run from 'now' to the finish
06951     // - dataset 1 is a bar chart representing the temperature profile of the run from 'now' to the finish
06952     // - dataset 2 is a line representing the temperature profile from the start to 'now'
06953     // - dataset 3 is a bar chart representing the temperature profile of the run from the start to 'now'
06954     // - dataset 4 is a single dot at the current time and temperature
06955     
06956     if(GCIsRunning() || runHasCompleted) {
06957 
06958         // SetupRunningColumnPageGraphPartialDataSetsToMatchCurrentRunTime 
06959         // returns true if it has updated the datasets, false otherwise
06960         if(SetupRunningColumnPageGraphPartialDataSetsToMatchCurrentRunTime(runHasCompleted)) {
06961             mustUpdateDisplay = true;
06962         }
06963     }
06964     
06965     char buff[40];
06966     if(runningColumnType == DIRECTLY_HEATED_COLUMN) {
06967         GetDirectlyHeatedColumnTemperature(buff, false);
06968     } else {
06969         GetColumnTemperature(buff, false);
06970     }
06971     if(strcmp(buff, GuiVar_columnTemperatureWhileRunning) != 0) {
06972         strcpy(GuiVar_columnTemperatureWhileRunning, buff);
06973         mustUpdateDisplay = true;
06974     }
06975     
06976     // Do this *before* updating the datasets, not after - otherwise we don't see the data at all
06977     if(mustUpdateDisplay) {
06978 
06979 #ifdef USING_DATASET_4 // If we are not using dataset4, we do not need to erase the previous 'dots' - 
06980                        // all elements of the graph will get redrawn anyway, so we do not need to clear it
06981 
06982         // Makes the display flicker - but omitting it means old text is not cleared from the display
06983 #ifdef USING_BACKGROUND_BITMAP
06984         DrawBackgroundBitmap(); 
06985 #else
06986         GuiLib_Clear();
06987 #endif
06988         // But the text does not change - so why do we need this? 
06989         // Answer - because otherwise the 'dot at current time' (dataset 4) does not get erased when we draw the next one
06990 
06991 #else 
06992         // We are not erasing the background, to minimise flickering of the graph.
06993         // Therefore, must manually erase background of 'column temperature while running' variable
06994         GuiLib_FillBox(400, 45, 550, 75, 0xFFFF); // White background
06995         
06996 #endif // USING_DATASET_4
06997 
06998         GuiLib_ShowScreen(GuiStruct_RunningColumnPage_25, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
06999     
07000         GuiLib_Refresh();
07001     }
07002 
07003     TimeUnit timeUnit = MINUTES;
07004     if(runningColumnPageGraphCompleteProfileDataSet->GetTotalMethodTime() < methodTimeUnitsThreshold) {
07005         timeUnit = SECONDS;
07006     }
07007 
07008 
07009     const float yAxisScaleFactor = 10.0f;
07010     
07011     // Dataset 0
07012     runningColumnPageGraph->SetDataForGraphDataSetInTenthsOfMinutes(0, yAxisScaleFactor, runningColumnPageGraphDataSet0);
07013     
07014     // Dataset 1 
07015     runningColumnPageGraph->SetDataForGraphDataSetInTenthsOfMinutes(1, yAxisScaleFactor, runningColumnPageGraphDataSet1);
07016     
07017     // Dataset 2
07018     runningColumnPageGraph->SetDataForGraphDataSetInTenthsOfMinutes(2, yAxisScaleFactor, runningColumnPageGraphDataSet2);
07019     
07020     // Dataset 3
07021     runningColumnPageGraph->SetDataForGraphDataSetInTenthsOfMinutes(3, yAxisScaleFactor, runningColumnPageGraphDataSet3);
07022     
07023 #ifdef USING_DATASET_4
07024     // Dataset 4
07025     runningColumnPageGraph->SetDataForGraphDataSetInTenthsOfMinutes(4, yAxisScaleFactor, runningColumnPageGraphDataSet4);
07026 #endif // USING_DATASET_4
07027     
07028     runningColumnPageGraph->SetXAxisUnits(timeUnit);
07029     
07030     
07031     // The tick sizes must match those set in easyGUI - we do not seem 
07032     // to be able to get them at runtime
07033     GuiConst_INT32S minX, maxX;
07034     //GuiConst_INT32S tickSize = (timeUnit == SECONDS) ? 300 : 100;
07035     // Always in 1/10 minutes
07036     GuiConst_INT32S xAxisTickSize = (timeUnit == SECONDS) ? 5 : 100; // 5 == 0.5 minutes (i.e. 30 seconds), 100 == 10 minutes
07037     runningColumnPageGraphCompleteProfileDataSet->GetXAxisRangeInTenthsOfMinutes(&minX, &maxX, xAxisTickSize );
07038     runningColumnPageGraph->SetXAxisRange(0, maxX); // Always start X axis at zero
07039     
07040     GuiConst_INT32S minY, maxY;
07041     GuiConst_INT32S yAxisTickSize = 50;
07042     runningColumnPageGraphCompleteProfileDataSet->GetYAxisRange(&minY, &maxY, yAxisTickSize);
07043     runningColumnPageGraph->SetYAxisRange(0, (maxY * yAxisScaleFactor)); // Always start Y axis at zero
07044     
07045     
07046     runningColumnPageGraph->DrawAxes();
07047 
07048     // We need to draw the X axis labels ourselves, since our time values are in units of 0.1 minute,
07049     // but easyGUI graphs work only in integers - we therefore have to multiply the time values by 10
07050     // before passing them to easyGUI, and if we let easyGUI draw its own values on the X axis, 
07051     // they would be 10 times the correct values.
07052     // Graph coordinates copied from easyGUI - I cannot find a way of obtaining them at runtime.
07053     if(timeUnit == SECONDS) {
07054         runningColumnPageGraph->DrawXAxisLabels(0, (maxX * 6), (xAxisTickSize * 6), 170, 335, 500);
07055     } else {
07056         runningColumnPageGraph->DrawXAxisLabels(0, (maxX / 10), (xAxisTickSize / 10), 170, 335, 500);
07057     }
07058 
07059     // Similar to the X axis - the values we pass to the easyGUI graph are scaled to be larger
07060     // than the real values, to get round the fact that easyGUI graphs work in integers.
07061     // (This can cause the profile to appear to be 'stepped', which obviously we do not want.)
07062     // We must therefore draw the Y axis values ourselves.
07063     runningColumnPageGraph->DrawYAxisLabels(0, maxY, yAxisTickSize, 170, 335, 250);
07064 
07065     runningColumnPageGraph->DrawDataSet(0);
07066     runningColumnPageGraph->ShowDataSet(0);
07067 
07068     runningColumnPageGraph->DrawDataSet(1);
07069     runningColumnPageGraph->ShowDataSet(1);
07070 
07071     runningColumnPageGraph->DrawDataSet(2);
07072     runningColumnPageGraph->ShowDataSet(2);
07073 
07074     runningColumnPageGraph->DrawDataSet(3);
07075     runningColumnPageGraph->ShowDataSet(3);
07076 
07077 #ifdef USING_DATASET_4
07078     runningColumnPageGraph->DrawDataSet(4);
07079     runningColumnPageGraph->ShowDataSet(4);
07080 #endif // USING_DATASET_4
07081     
07082     runningColumnPageGraph->Redraw(); // This only redraws the graph axes, etc - not the data
07083     
07084 #ifdef TEST_GUILIB_VLINE_PROFILES
07085     GuiConst_INTCOLOR graphColour1 = SixteenBitColorValue(100, 100, 100);
07086     GuiConst_INTCOLOR graphColour2 = SixteenBitColorValue(200, 200, 200);
07087     float runTime;
07088     GetRunTime(&runTime);
07089     double colourBoundaryX = (double) runTime;
07090 //    columnTempProfilePageGraphCompleteProfileDataSet->DrawUsingGuiLibVLine(170, 335, ((double) 500 / (double) (maxX / 10)), ((double) -250 / (double) maxY), graphColour1, graphColour2, colourBoundaryX);
07091 // Surely...
07092     runningColumnPageGraphCompleteProfileDataSet->DrawUsingGuiLibVLine(170, 335, ((double) 500 / (double) (maxX / 10)), ((double) -250 / (double) maxY), graphColour1, graphColour2, colourBoundaryX);
07093     // &&&&
07094 #endif // TEST_GUILIB_VLINE_PROFILES
07095 
07096     // Does this solve 'pause before graph displayed' problem with double buffering?
07097     if(mustUpdateDisplay) {
07098         GuiLib_Refresh();
07099     }
07100     // Answer - yes it does - although there is still a (just noticeable) pause
07101     // (without double buffering, we do not need the above 'if' - 
07102     // the graph gets redisplayed without it)
07103 }
07104 
07105 /*
07106     Displays the data on the 'running detector' page.
07107     
07108     Args: a boolean specifying whether or not the display is actually to be updated.
07109           Even if it is false when we are called, we may set it true if we discover
07110           one or more data items has changed - but note that we will not set it false 
07111           if it is already true. If it is true after we have looked at all 
07112           the data to be displayed, we will then update the display. (If we are called
07113           with this value set to false, the caller is effectively saying
07114           'display the running settings page data only if it has changed').
07115           
07116     No return code.
07117 */
07118 void GetGCStatusLoop::DisplayRunningDetectorPageData(bool mustUpdateDisplay)
07119 {
07120     char buff[40];
07121     
07122     GetDetectorType(buff, false, false);
07123     if(strcmp(buff, GuiVar_detectorType2) != 0) {
07124         mustUpdateDisplay = true;
07125         
07126         strcpy(GuiVar_detectorType2, buff);
07127     }
07128     
07129 //    if(detectorType != FPD_DETECTOR) {
07130         GetDetectorRange(buff);
07131         if(strcmp(buff, GuiVar_detectorRange) != 0) {
07132             mustUpdateDisplay = true;
07133             
07134             strcpy(GuiVar_detectorRange, buff);
07135         }
07136         // FPD detector has a different GC command for range - see below
07137 //    }
07138         
07139     GetFuelFlowRate(buff);    
07140     if(strcmp(buff, GuiVar_detectorFuelFlowRate) != 0) {
07141         mustUpdateDisplay = true;
07142         
07143         strcpy(GuiVar_detectorFuelFlowRate, buff);
07144     }
07145     
07146     GetDetectorTemperature(buff);
07147     if(strcmp(buff, GuiVar_detectorTemperature) != 0) {
07148         strcpy(GuiVar_detectorTemperature, buff);
07149 
07150         mustUpdateDisplay = true;
07151     }
07152     
07153     GetDetectorTargetTemperature(buff, stringFormatdegCUnits);
07154     if(strcmp(buff, GuiVar_detectorTargetTemperature2) != 0) {
07155         strcpy(GuiVar_detectorTargetTemperature2, buff);
07156 
07157         mustUpdateDisplay = true;
07158     }
07159         
07160 // *** 14 MarWhy is this code commented out?? ***
07161 /*
07162     if(detectorType == TCD_DETECTOR) {
07163         
07164         GetTCDDetectorFilamentTemperature(buff);
07165         if(strcmp(buff, GuiVar_tcdDetectorFilamentTemperature) != 0) {
07166             mustUpdateDisplay = true;
07167             
07168             strcpy(GuiVar_tcdDetectorFilamentTemperature, buff);
07169         }
07170         
07171         GetTCDDetectorFilamentPolarity(buff);
07172         if(strcmp(buff, GuiVar_tcdDetectorFilamentPolarity) != 0) {
07173             mustUpdateDisplay = true;
07174             
07175             strcpy(GuiVar_tcdDetectorFilamentPolarity, buff);
07176         }
07177         
07178     } else if(detectorType == ECD_DETECTOR) {
07179         
07180         GetECDDetectorCurrent(buff);
07181         if(strcmp(buff, GuiVar_ecdDetectorCurrent) != 0) {
07182             mustUpdateDisplay = true;
07183             
07184             strcpy(GuiVar_ecdDetectorCurrent, buff);
07185         }
07186 
07187     } else if(detectorType == FPD_DETECTOR) {
07188         
07189         GetFPDDetectorRange(buff);
07190         if(strcmp(buff, GuiVar_fpdDetectorRange) != 0) {
07191             mustUpdateDisplay = true;
07192             
07193             strcpy(GuiVar_fpdDetectorRange, buff);
07194         }
07195 
07196         GetFPDDetectorSensitivity(buff);
07197         if(strcmp(buff, GuiVar_fpdDetectorSensitivity) != 0) {
07198             mustUpdateDisplay = true;
07199             
07200             strcpy(GuiVar_fpdDetectorSensitivity, buff);
07201         }
07202 
07203     } else {
07204         
07205         GetDetectorTemperature(buff);
07206         if(strcmp(buff, GuiVar_detectorTemperature) != 0) {
07207             mustUpdateDisplay = true;
07208             
07209             strcpy(GuiVar_detectorTemperature, buff);
07210         }
07211     }
07212 */
07213 
07214     if(mustUpdateDisplay) {
07215 
07216         // Makes the display flicker - but omitting it means old text is not cleared from the display
07217 #ifdef USING_BACKGROUND_BITMAP
07218         DrawBackgroundBitmap(); 
07219 #else
07220         GuiLib_Clear();
07221 #endif
07222 
07223         GuiLib_ShowScreen(GuiStruct_RunningDetectorPage_27, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
07224     
07225         GuiLib_Refresh();    
07226 
07227     }
07228 }
07229 
07230 /*
07231     Displays the data on the 'running injector' page.
07232     
07233     Args: a boolean specifying whether or not the display is actually to be updated.
07234           Even if it is false when we are called, we may set it true if we discover
07235           one or more data items has changed - but note that we will not set it false 
07236           if it is already true. If it is true after we have looked at all 
07237           the home page data, we will then update the display. (If we are called
07238           with this value set to false, the caller is effectively saying
07239           'display the running settings page data only if it has changed').
07240           
07241     No return code.
07242 */
07243 void GetGCStatusLoop::DisplayRunningInjectorPageData(bool mustUpdateDisplay)
07244 {
07245     char buff[40];
07246     
07247     GetInjectorTemperature(buff, false);
07248     if(strcmp(buff, GuiVar_injectorTemperature) != 0) {
07249         strcpy(GuiVar_injectorTemperature, buff);
07250         mustUpdateDisplay = true;
07251     }
07252 
07253     GetInjectionMode(buff);
07254     if(strcmp(buff, GuiVar_injectionMode2) != 0) {
07255         strcpy(GuiVar_injectionMode2, buff);
07256         mustUpdateDisplay = true;
07257     }
07258 
07259     GetInjectorType(buff, false);
07260     if(strcmp(buff, GuiVar_injectorType) != 0) {
07261         strcpy(GuiVar_injectorType, buff);
07262         mustUpdateDisplay = true;
07263     }
07264 
07265     GetComponentStatusString(INJECTOR, buff);
07266     if(strcmp(buff, GuiVar_injectorStatus) != 0) {
07267         strcpy(GuiVar_injectorStatus, buff);
07268         mustUpdateDisplay = true;
07269     }
07270     
07271     GetInjectorSplitTime(buff);
07272     if(strcmp(buff, GuiVar_injectorSplitTime) != 0) {
07273         strcpy(GuiVar_injectorSplitTime, buff);
07274         mustUpdateDisplay = true;
07275     }
07276     
07277     GetSplitFlow(buff);
07278     if(strcmp(buff, GuiVar_injectorSplitFlowRate) != 0) {
07279         strcpy(GuiVar_injectorSplitFlowRate, buff);
07280         mustUpdateDisplay = true;
07281     }
07282 
07283     GetSplitRatio(buff);
07284     if(strcmp(buff, GuiVar_injectorSplitRatio) != 0) {
07285         strcpy(GuiVar_injectorSplitRatio, buff);
07286         mustUpdateDisplay = true;
07287     }
07288     
07289     if(mustUpdateDisplay) {
07290 
07291         // Makes the display flicker - but omitting it means old text is not cleared from the display
07292 #ifdef USING_BACKGROUND_BITMAP
07293         DrawBackgroundBitmap(); 
07294 #else
07295         GuiLib_Clear();
07296 #endif
07297 
07298         GuiLib_ShowScreen(GuiStruct_RunningInjectorPage_26, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
07299     
07300         GuiLib_Refresh();    
07301 
07302     }
07303 }
07304 
07305 /*
07306     Displays the data on the 'running gas' page.
07307     
07308     Args: a boolean specifying whether or not the display is actually to be updated.
07309           Even if it is false when we are called, we may set it true if we discover
07310           one or more data items has changed - but note that we will not set it false 
07311           if it is already true. If it is true after we have looked at all 
07312           the home page data, we will then update the display. (If we are called
07313           with this value set to false, the caller is effectively saying
07314           'display the running settings page data only if it has changed').
07315           
07316     No return code.
07317 */
07318 void GetGCStatusLoop::DisplayRunningGasPageData(bool mustUpdateDisplay, bool runHasCompleted)
07319 {
07320     // This page consists principally of an easyGUI graph, to which we assign several datasets:
07321     //
07322     // - dataset 0 is a line representing the flow profile of the run from 'now' to the finish
07323     // - dataset 1 is a bar chart representing the flow profile of the run from 'now' to the finish
07324     // - dataset 2 is a line representing the flow profile from the start to 'now'
07325     // - dataset 3 is a bar chart representing the flow profile from the start to 'now'
07326     // - dataset 4 is a single dot at the current time and flow rate
07327     
07328     if(GCIsRunning() || runHasCompleted) {
07329 
07330         // SetupRunningGasPageGraphPartialDataSetsToMatchCurrentRunTime 
07331         // returns true if it has updated the datasets, false otherwise
07332         if(SetupRunningGasPageGraphPartialDataSetsToMatchCurrentRunTime(runHasCompleted)) {
07333             mustUpdateDisplay = true;
07334         }
07335     }
07336     
07337     char buff[40];
07338     GetCurrentGasPressure(buff, false, true);
07339     if(strcmp(buff, GuiVar_gasPressureWhileRunning) != 0) {
07340         strcpy(GuiVar_gasPressureWhileRunning, buff);
07341         mustUpdateDisplay = true;
07342     }
07343 
07344     // Do this *before* updating the datasets, not after - otherwise we don't see the data at all
07345     if(mustUpdateDisplay) {
07346 
07347 #ifdef USING_DATASET_4 
07348         // Makes the display flicker - but omitting it means old text is not cleared from the display
07349 #ifdef USING_BACKGROUND_BITMAP
07350         DrawBackgroundBitmap(); 
07351 #else
07352         GuiLib_Clear();
07353 #endif
07354         // But the text does not change - so why do we need this? 
07355         // Answer - because otherwise the 'dot at current time' (dataset 4) does not get erased when we draw the next one
07356 #else 
07357         // We are not erasing the background, to minimise flickering of the graph.
07358         // Therefore, must manually erase background of 'gas pressure while running' variable
07359         GuiLib_FillBox(400, 45, 550, 75, 0xFFFF); // White background
07360 #endif // USING_DATASET_4 - if we are not displaying dataset 4, we do not need to erase the graph - all its other datapoints will get overwritten anyway
07361 
07362         GuiLib_ShowScreen(GuiStruct_RunningGasPage_28, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
07363     
07364         GuiLib_Refresh();
07365     }
07366 
07367     TimeUnit timeUnit = MINUTES;
07368     if(runningGasPageGraphCompleteProfileDataSet->GetTotalMethodTime() < methodTimeUnitsThreshold) {
07369         timeUnit = SECONDS;
07370     }
07371     
07372 
07373     const float yAxisScaleFactor = 10.0f;
07374     
07375     // Dataset 0
07376     runningGasPageGraph->SetDataForGraphDataSetInTenthsOfMinutes(0, yAxisScaleFactor, runningGasPageGraphDataSet0);
07377     
07378     // Dataset 1 
07379     runningGasPageGraph->SetDataForGraphDataSetInTenthsOfMinutes(1, yAxisScaleFactor, runningGasPageGraphDataSet1);
07380     
07381     // Dataset 2
07382     runningGasPageGraph->SetDataForGraphDataSetInTenthsOfMinutes(2, yAxisScaleFactor, runningGasPageGraphDataSet2);
07383     
07384     // Dataset 3
07385     runningGasPageGraph->SetDataForGraphDataSetInTenthsOfMinutes(3, yAxisScaleFactor, runningGasPageGraphDataSet3);
07386     
07387 #ifdef USING_DATASET_4
07388     // Dataset 4
07389     runningGasPageGraph->SetDataForGraphDataSetInTenthsOfMinutes(4, yAxisScaleFactor, runningGasPageGraphDataSet4);
07390 #endif // USING_DATASET_4    
07391     
07392     runningGasPageGraph->SetXAxisUnits(timeUnit);
07393 
07394 
07395     // The tick sizes must match those set in easyGUI - we do not seem 
07396     // to be able to get them at runtime
07397     GuiConst_INT32S minX, maxX;
07398     //GuiConst_INT32S tickSize = (timeUnit == SECONDS) ? 300 : 100;
07399     // Always in 1/10 minutes
07400     GuiConst_INT32S xAxisTickSize = (timeUnit == SECONDS) ? 5 : 100; // 5 == 0.5 minutes (i.e. 30 seconds), 100 == 10 minutes
07401     runningGasPageGraphCompleteProfileDataSet->GetXAxisRangeInTenthsOfMinutes(&minX, &maxX, xAxisTickSize );
07402     runningGasPageGraph->SetXAxisRange(0, maxX); // Always start X axis at zero
07403     
07404     GuiConst_INT32S minY, maxY;
07405     GuiConst_INT32S yAxisTickSize = 10;
07406     runningGasPageGraphCompleteProfileDataSet->GetYAxisRange(&minY, &maxY, yAxisTickSize);
07407     runningGasPageGraph->SetYAxisRange(0, (maxY * yAxisScaleFactor)); // Always start Y axis at zero
07408     
07409 
07410     runningGasPageGraph->DrawAxes();
07411 
07412     // We need to draw the X axis labels ourselves, since our time values are in units of 0.1 minute,
07413     // but easyGUI graphs work only in integers - we therefore have to multiply the time values by 10
07414     // before passing them to easyGUI, and if we let easyGUI draw its own values on the X axis, 
07415     // they would be 10 times the correct values.
07416     // Graph coordinates copied from easyGUI - I cannot find a way of obtaining them at runtime.
07417     if(timeUnit == SECONDS) {
07418         runningGasPageGraph->DrawXAxisLabels(0, (maxX * 6), (xAxisTickSize * 6), 160, 335, 500);
07419     } else {
07420         runningGasPageGraph->DrawXAxisLabels(0, (maxX / 10), (xAxisTickSize / 10), 160, 335, 500);
07421     }
07422 
07423     // Similar to the X axis - the values we pass to the easyGUI graph are scaled to be larger
07424     // than the real values, to get round the fact that easyGUI graphs work in integers.
07425     // (This can cause the profile to appear to be 'stepped', which obviously we do not want.)
07426     // We must therefore draw the Y axis values ourselves.
07427     runningGasPageGraph->DrawYAxisLabels(0, maxY, yAxisTickSize, 160, 335, 250);
07428 
07429     runningGasPageGraph->DrawDataSet(0);
07430     runningGasPageGraph->ShowDataSet(0);
07431 
07432     runningGasPageGraph->DrawDataSet(1);
07433     runningGasPageGraph->ShowDataSet(1);
07434 
07435     runningGasPageGraph->DrawDataSet(2);
07436     runningGasPageGraph->ShowDataSet(2);
07437 
07438     runningGasPageGraph->DrawDataSet(3);
07439     runningGasPageGraph->ShowDataSet(3);
07440 
07441 #ifdef USING_DATASET_4
07442     runningGasPageGraph->DrawDataSet(4);
07443     runningGasPageGraph->ShowDataSet(4);
07444 #endif // USING_DATASET_4
07445 
07446     runningGasPageGraph->Redraw(); // this only redraws the axes,etc - not the graph data
07447     
07448     // Does this solve 'pause before graph displayed' problem with double buffering?
07449     if(mustUpdateDisplay) {
07450         GuiLib_Refresh();
07451     }
07452     // Answer - yes it does - although there is still a (just noticeable) pause
07453     // (without double buffering, we do not need the above 'if' - 
07454     // the graph gets redisplayed without it)
07455 }
07456 
07457 /*
07458     Displays the data on the 'running gas' page.
07459     
07460     Args: a boolean specifying whether or not the display is actually to be updated.
07461           Even if it is false when we are called, we may set it true if we discover
07462           one or more data items has changed - but note that we will not set it false 
07463           if it is already true. If it is true after we have looked at all 
07464           the home page data, we will then update the display. (If we are called
07465           with this value set to false, the caller is effectively saying
07466           'display the running settings page data only if it has changed').
07467           
07468     No return code.
07469 */
07470 void GetGCStatusLoop::DisplayRunningInjectorProfilePageData(bool mustUpdateDisplay, bool runHasCompleted)
07471 {
07472     // This page consists principally of an easyGUI graph, to which we assign several datasets:
07473     //
07474     // - dataset 0 is a line representing the injector temperature profile of the run from 'now' to the finish
07475     // - dataset 1 is a bar chart representing the injector temperature profile of the run from 'now' to the finish
07476     // - dataset 2 is a line representing the injector temperature profile from the start to 'now'
07477     // - dataset 3 is a bar chart representing the injector temperature profile from the start to 'now'
07478     // - dataset 4 is a single dot at the current time and injector temperature 
07479     
07480     if(GCIsRunning() || runHasCompleted) {
07481 
07482         // SetupRunningInjectorPageGraphPartialDataSetsToMatchCurrentRunTime 
07483         // returns true if it has updated the datasets, false otherwise
07484         if(SetupRunningInjectorPageGraphPartialDataSetsToMatchCurrentRunTime(runHasCompleted)) {
07485             mustUpdateDisplay = true;
07486         }
07487     }
07488     
07489     char buff[40];
07490     GetInjectorTemperature(buff, false);
07491     if(strcmp(buff, GuiVar_injectorTemperatureWhileRunning) != 0) {
07492         strcpy(GuiVar_injectorTemperatureWhileRunning, buff);
07493         mustUpdateDisplay = true;
07494     }
07495 
07496     // Do this *before* updating the datasets, not after - otherwise we don't see the data at all
07497     if(mustUpdateDisplay) {
07498 
07499 #ifdef USING_DATASET_4 
07500         // Makes the display flicker - but omitting it means old text is not cleared from the display
07501 #ifdef USING_BACKGROUND_BITMAP
07502         DrawBackgroundBitmap(); 
07503 #else
07504         GuiLib_Clear();
07505 #endif
07506         // But the text does not change - so why do we need this? 
07507         // Answer - because otherwise the 'dot at current time' (dataset 4) does not get erased when we draw the next one
07508 #else 
07509         // We are not erasing the background, to minimise flickering of the graph.
07510         // Therefore, must manually erase background of 'injector temperature while running' variable
07511         GuiLib_FillBox(400, 45, 550, 75, 0xFFFF); // White background
07512 #endif // USING_DATASET_4 - if we are not displaying dataset 4, we do not need to erase the graph - all its other datapoints will get overwritten anyway
07513 
07514         GuiLib_ShowScreen(GuiStruct_RunningInjectorProfilePage_Def, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
07515     
07516         GuiLib_Refresh();
07517     }
07518 
07519     TimeUnit timeUnit = MINUTES;
07520     if(runningInjectorPageGraphCompleteProfileDataSet->GetTotalMethodTime() < methodTimeUnitsThreshold) {
07521         timeUnit = SECONDS;
07522     }
07523     
07524 
07525     const float yAxisScaleFactor = 10.0f;
07526     
07527     // Dataset 0
07528     runningInjectorPageGraph->SetDataForGraphDataSetInTenthsOfMinutes(0, yAxisScaleFactor, runningInjectorPageGraphDataSet0);
07529     
07530     // Dataset 1 
07531     runningInjectorPageGraph->SetDataForGraphDataSetInTenthsOfMinutes(1, yAxisScaleFactor, runningInjectorPageGraphDataSet1);
07532     
07533     // Dataset 2
07534     runningInjectorPageGraph->SetDataForGraphDataSetInTenthsOfMinutes(2, yAxisScaleFactor, runningInjectorPageGraphDataSet2);
07535     
07536     // Dataset 3
07537     runningInjectorPageGraph->SetDataForGraphDataSetInTenthsOfMinutes(3, yAxisScaleFactor, runningInjectorPageGraphDataSet3);
07538     
07539 #ifdef USING_DATASET_4
07540     // Dataset 4
07541     runningInjectorPageGraph->SetDataForGraphDataSetInTenthsOfMinutes(4, yAxisScaleFactor, runningInjectorPageGraphDataSet4);
07542 #endif // USING_DATASET_4    
07543     
07544     runningInjectorPageGraph->SetXAxisUnits(timeUnit);
07545 
07546 
07547     // The tick sizes must match those set in easyGUI - we do not seem 
07548     // to be able to get them at runtime
07549     GuiConst_INT32S minX, maxX;
07550     //GuiConst_INT32S tickSize = (timeUnit == SECONDS) ? 300 : 100;
07551     // Always in 1/10 minutes
07552     GuiConst_INT32S xAxisTickSize = (timeUnit == SECONDS) ? 5 : 100; // 5 == 0.5 minutes (i.e. 30 seconds), 100 == 10 minutes
07553     runningInjectorPageGraphCompleteProfileDataSet->GetXAxisRangeInTenthsOfMinutes(&minX, &maxX, xAxisTickSize );
07554     runningInjectorPageGraph->SetXAxisRange(0, maxX); // Always start X axis at zero
07555     
07556     GuiConst_INT32S minY, maxY;
07557     GuiConst_INT32S yAxisTickSize = 50;
07558     runningInjectorPageGraphCompleteProfileDataSet->GetYAxisRange(&minY, &maxY, yAxisTickSize);
07559     runningInjectorPageGraph->SetYAxisRange(0, (maxY * yAxisScaleFactor)); // Always start Y axis at zero
07560     
07561 
07562     runningInjectorPageGraph->DrawAxes();
07563 
07564     // We need to draw the X axis labels ourselves, since our time values are in units of 0.1 minute,
07565     // but easyGUI graphs work only in integers - we therefore have to multiply the time values by 10
07566     // before passing them to easyGUI, and if we let easyGUI draw its own values on the X axis, 
07567     // they would be 10 times the correct values.
07568     // Graph coordinates copied from easyGUI - I cannot find a way of obtaining them at runtime.
07569     if(timeUnit == SECONDS) {
07570         runningInjectorPageGraph->DrawXAxisLabels(0, (maxX * 6), (xAxisTickSize * 6), 160, 335, 500);
07571     } else {
07572         runningInjectorPageGraph->DrawXAxisLabels(0, (maxX / 10), (xAxisTickSize / 10), 160, 335, 500);
07573     }
07574 
07575     // Similar to the X axis - the values we pass to the easyGUI graph are scaled to be larger
07576     // than the real values, to get round the fact that easyGUI graphs work in integers.
07577     // (This can cause the profile to appear to be 'stepped', which obviously we do not want.)
07578     // We must therefore draw the Y axis values ourselves.
07579     runningInjectorPageGraph->DrawYAxisLabels(0, maxY, yAxisTickSize, 160, 335, 250);
07580 
07581     runningInjectorPageGraph->DrawDataSet(0);
07582     runningInjectorPageGraph->ShowDataSet(0);
07583 
07584     runningInjectorPageGraph->DrawDataSet(1);
07585     runningInjectorPageGraph->ShowDataSet(1);
07586 
07587     runningInjectorPageGraph->DrawDataSet(2);
07588     runningInjectorPageGraph->ShowDataSet(2);
07589 
07590     runningInjectorPageGraph->DrawDataSet(3);
07591     runningInjectorPageGraph->ShowDataSet(3);
07592 
07593 #ifdef USING_DATASET_4
07594     runningInjectorPageGraph->DrawDataSet(4);
07595     runningInjectorPageGraph->ShowDataSet(4);
07596 #endif // USING_DATASET_4
07597 
07598     runningInjectorPageGraph->Redraw(); // this only redraws the axes,etc - not the graph data
07599     
07600 #ifdef TEST_GUILIB_VLINE_PROFILES
07601     GuiConst_INTCOLOR graphColour1 = SixteenBitColorValue(100, 100, 100);
07602     GuiConst_INTCOLOR graphColour2 = SixteenBitColorValue(200, 200, 200);
07603     float runTime;
07604     GetRunTime(&runTime);
07605     double colourBoundaryX = (double) runTime;
07606     runningInjectorPageGraphCompleteProfileDataSet->DrawUsingGuiLibVLine(170, 335, ((double) 500 / (double) (maxX / 10)), ((double) -250 / (double) maxY), graphColour1, graphColour2, colourBoundaryX);
07607     // &&&&
07608 #endif // TEST_GUILIB_VLINE_PROFILES
07609 
07610     // Does this solve 'pause before graph displayed' problem with double buffering?
07611     if(mustUpdateDisplay) {
07612         GuiLib_Refresh();
07613     }
07614     // Answer - yes it does - although there is still a (just noticeable) pause
07615     // (without double buffering, we do not need the above 'if' - 
07616     // the graph gets redisplayed without it)
07617 }
07618 
07619 
07620 /*
07621     Gets the GC embedded software version, and returns it as a null-terminated string, with a descriptive prefix.
07622     
07623     Note also that this code is intended to be as efficient as possible.
07624     
07625     Args: pointer to a buffer to contain the version, as a null-terminated string
07626           
07627     No return code.
07628 */
07629 void GetGCStatusLoop::GetGCSoftwareVersion(char *version)
07630 {
07631     char response[50];
07632     SetGCDeviceReport("QWHO", response);
07633 
07634     // We expect a response like this: "DWHO0320" -> version 3.20
07635     version[0]  = 'G';
07636     version[1]  = 'C';
07637     version[2]  = ' ';
07638     version[3]  = 's';
07639     version[4]  = '/';
07640     version[5]  = 'w';
07641     version[6]  = ' ';
07642     version[7]  = 'v';
07643     version[8]  = 'e';
07644     version[9]  = 'r';
07645     version[10] = 's';
07646     version[11] = 'i';
07647     version[12] = 'o';
07648     version[13] = 'n';
07649     version[14] = ':';
07650     version[15] = ' ';
07651     version[16] = response[4];
07652     version[17] = response[5];
07653     version[18] = '.';
07654     version[19] = response[6];
07655     version[20] = response[7];
07656     version[21] = '\0';
07657 }
07658 
07659 /*
07660     Gets the actuator software version, and returns it as a null-terminated string, with a descriptive prefix.
07661     
07662     Args: pointer to a buffer to contain the version, as a null-terminated string
07663           
07664     No return code.
07665 */
07666 void GetGCStatusLoop::GetActuatorSoftwareVersion(char *version)
07667 {
07668     char response[50];
07669     SetGCDeviceReport("QACT0004", response);
07670     
07671     // We expect a response like this: "DACnnnnn" (note 5 digits).
07672     // The value contains 14 bits, and the least significant byte is the software version
07673     int value;
07674     sscanf(&response[3], "%d", &value);
07675 
07676     sprintf(version, "Actuator s/w version: %d", (value & 0xFF));
07677 }
07678 
07679 
07680 /*
07681     Gets the GC run time, and returns it as a floating point value.
07682     Note that the GC returns the run time as a four digit value, 
07683     scaled in units of 0.1 min. This function applies that scaling 
07684     to the value it returns - i.e. if the GC returns "1234",
07685     this function returns 123.4 as the float value.
07686 
07687     Remember that the GC sets its runtime to zero at the start of every run.
07688     
07689     Args: pointer to a 'float' variable to contain the run time
07690           
07691     No return code.
07692 */
07693 void GetGCStatusLoop::GetRunTime(float *time)
07694 {
07695     char response[50];
07696     SetGCDeviceReport("QTIM", response);
07697 
07698     if(response[0] == 'E') {
07699         // Got "EPKT" response
07700         *time = -1.0f;
07701     
07702         return;
07703     }
07704     
07705     // Must have received a valid response from the GC. We expect a response like this: 
07706     // "DTIM1234", with run time in units of 0.1 min
07707     char buff[50];
07708     buff[0] = response[4];
07709     buff[1] = response[5];
07710     buff[2] = response[6];
07711     buff[3] = '.';
07712     buff[4] = response[7];
07713     buff[5] = '\0';
07714     
07715     sscanf(buff, "%f", time);
07716 }
07717 
07718 
07719 /*
07720     Gets the GC run time, and returns it as a null-terminated string, with a descriptive prefix.
07721     (The GC sets this to zero at the start of every run.)
07722     
07723     Note also that this code is intended to be as efficient as possible.
07724     
07725     Args: pointer to a buffer to contain the run time, as a null-terminated string
07726           
07727     No return code.
07728 */
07729 void GetGCStatusLoop::GetRunTime(char *time)
07730 {
07731     char response[50];
07732     SetGCDeviceReport("QTIM", response);
07733 
07734     // We expect a response like this: "DTIM1234", with run time in units of 0.1 min
07735     int index = 0;
07736     time[index++]  = 'R';
07737     time[index++]  = 'u';
07738     time[index++]  = 'n';
07739     time[index++]  = ' ';
07740     time[index++]  = 't';
07741     time[index++]  = 'i';
07742     time[index++]  = 'm';
07743     time[index++]  = 'e';
07744     time[index++]  = ':';
07745     time[index++]  = ' ';
07746     
07747     bool wantNextChars = false;
07748     if(response[4] != '0') {
07749         time[index++] = response[4];
07750         wantNextChars = true;
07751     }
07752     if(wantNextChars || (response[5] != '0')) {
07753         time[index++] = response[5];
07754     }
07755     // If the value is zero, make sure we return "0.0" - 
07756     // we just don't want any zeroes before that
07757     time[index++] = response[6];
07758     time[index++] = '.';
07759     time[index++] = response[7];
07760 
07761     time[index++] = ' ';
07762     time[index++] = 'm';
07763     time[index++] = 'i';
07764     time[index++] = 'n';
07765     time[index++] = '\0';
07766 }
07767 
07768 /*
07769     Gets the GC serial number (which consists of eight digits), and returns it as a null-terminated string.
07770     
07771     Note also that this code is intended to be as efficient as possible.
07772     
07773     Args: pointer to a buffer to contain the run time, as a null-terminated string.
07774           This must be at least twelve bytes long, to allow for a possible error message.
07775           
07776     No return code.
07777 */
07778 void GetGCStatusLoop::GetSerialNumber(char *serialNumber)
07779 {
07780     char response1[50];
07781     SetGCDeviceReport("GSN1", response1);
07782     // We expect a response like this: "DSN1nnnn", where 'nnnn' is the first four digits of the serial number
07783 
07784     char response2[50];
07785     SetGCDeviceReport("GSN2", response2);
07786     // We expect a response like this: "DSN2nnnn", where 'nnnn' is the second four digits of the serial number
07787 
07788     int index = 0;
07789     // But check for "EPKT" first
07790     if((response1[0] == 'E') || (response2[0] == 'E')) {
07791         serialNumber[index++] = '*';
07792         serialNumber[index++] = '*';
07793         serialNumber[index++] = ' ';
07794         serialNumber[index++] = 'E';
07795         serialNumber[index++] = 'r';
07796         serialNumber[index++] = 'r';
07797         serialNumber[index++] = 'o';
07798         serialNumber[index++] = 'r';
07799         serialNumber[index++] = ' ';
07800         serialNumber[index++] = '*';
07801         serialNumber[index++] = '*';
07802     } else {
07803         serialNumber[index++] = response1[4];
07804         serialNumber[index++] = response1[5];
07805         serialNumber[index++] = response1[6];
07806         serialNumber[index++] = response1[7];
07807         serialNumber[index++] = response2[4];
07808         serialNumber[index++] = response2[5];
07809         serialNumber[index++] = response2[6];
07810         serialNumber[index++] = response2[7];
07811     }
07812     
07813     serialNumber[index] = '\0';
07814 }
07815 
07816 
07817 /*
07818     Gets the time represented by the GC's real time clock [RTC], and returns it as a null-terminated string.
07819     
07820     Args: pointer to a buffer to contain the time, as a null-terminated string.
07821           This must be at least twenty-five bytes long, to accomodate the string 
07822           returned by the 'ctime' function
07823           
07824     No return code.
07825 */
07826 void GetGCStatusLoop::GetGCRealTimeClockTime(char *clockTime)
07827 {
07828     time_t gcRtcValue;
07829     
07830     GCRealTimeClock::GetGCClockTime(usbDevice, usbHostGC, &gcRtcValue);
07831     
07832     strcpy(clockTime, ctime(&gcRtcValue));
07833     
07834     // Remove the newline character from the string returned by 'ctime'
07835     // (easyGUI displays it as a black rectangle)
07836     char *cp = clockTime;
07837     while(*cp) {
07838         if(*cp == '\n') {
07839             *cp = '\0';
07840             break;
07841         }
07842         ++cp;
07843     }
07844 }
07845 
07846 /*
07847     Displays the data on the settings page, by copying it to the relevant easyGUI variables,
07848     and calling the relevant functions to actually display it.
07849     
07850     Args: a boolean specifying whether or not the display is actually to be updated.
07851           Even if it is false when we are called, we may set it true if we discover
07852           one or more data items has changed - but note that we will not set it false 
07853           if it is already true. If it is true after we have looked at all 
07854           the home page data, we will then update the display. (If we are called
07855           with this value set to false, the caller is effectively saying
07856           'display the settings page data only if it has changed').
07857           
07858     No return code.
07859 */
07860 void GetGCStatusLoop::DisplaySettingsPageData(bool mustUpdateDisplay)
07861 {
07862     //EasyGUIDebugPrint("Settings Page", 100, 100);
07863     // Various settings
07864     char buff[60];
07865 
07866     GetGCSoftwareVersion(GuiVar_gcSoftwareVersion);
07867     // Assume software version cannot change while we are running
07868     
07869     GetActuatorSoftwareVersion(GuiVar_actuatorSoftwareVersion);
07870     
07871     GetGasControlMode(buff, true, true);
07872     if(strcmp(buff, GuiVar_gasControlMode) != 0) {
07873         mustUpdateDisplay = true;
07874         
07875         strcpy(GuiVar_gasControlMode, buff);
07876     }
07877     
07878     GetDetectorType(buff, true, true);
07879     if(strcmp(buff, GuiVar_detectorType) != 0) {
07880         mustUpdateDisplay = true;
07881         
07882         strcpy(GuiVar_detectorType, buff);
07883     }
07884     
07885     GetColumnMaxTemperature(buff, true, true);
07886     if(strcmp(buff, GuiVar_columnMaxTemp) != 0) {
07887         mustUpdateDisplay = true;
07888         
07889         strcpy(GuiVar_columnMaxTemp, buff);
07890     }
07891     
07892 //    GetInjectionMode(buff, true);
07893     GetInjectorType(buff, true);
07894     if(strcmp(buff, GuiVar_injectionMode) != 0) {
07895         mustUpdateDisplay = true;
07896         
07897         strcpy(GuiVar_injectionMode, buff);
07898     }
07899     
07900     GetRunTime(buff);
07901     if(strcmp(buff, GuiVar_runTime) != 0) {
07902         mustUpdateDisplay = true;
07903         
07904         strcpy(GuiVar_runTime, buff);
07905     }
07906 
07907     GetSerialNumber(buff);
07908     if(strcmp(buff, GuiVar_gcSerialNumber) != 0) {
07909         mustUpdateDisplay = true;
07910         
07911         strcpy(GuiVar_gcSerialNumber, buff);
07912     }
07913     
07914     GetGCRealTimeClockTime(buff);
07915     if(strcmp(buff, GuiVar_gcTime) != 0) {
07916         mustUpdateDisplay = true;
07917         
07918         strcpy(GuiVar_gcTime, buff);
07919     }
07920     
07921     if(mustUpdateDisplay) {
07922 
07923         // Makes the display flicker - but omitting it means old text is not cleared from the display
07924 #ifdef USING_BACKGROUND_BITMAP
07925         DrawBackgroundBitmap(); 
07926 #else
07927         GuiLib_Clear();
07928 #endif
07929 
07930         GuiLib_ShowScreen(GuiStruct_SettingsPage_5, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
07931     
07932         GuiLib_Refresh();    
07933 
07934 #define DEBUG_HERE
07935 #ifdef DEBUG_HERE
07936     char dbg[100];
07937     sprintf(dbg, "After GuiLib_Clear 6");
07938     EasyGUIDebugPrint(dbg, 0, 20);
07939 #undef DEBUG_HERE
07940 #endif
07941     }
07942 }
07943 
07944 
07945 /*
07946     Displays the 'establishing ethernet connection' page - 
07947     so the user knows we are doing something, and have not locked up.
07948     
07949     No args, no return code.
07950 */
07951 void GetGCStatusLoop::DisplayEthernetConnectionPage(void)
07952 {
07953 #ifdef USING_BACKGROUND_BITMAP
07954     DrawBackgroundBitmapWithLogo(); // Only on this page and the "Connecting to GC" page
07955 #else
07956     GuiLib_Clear();
07957 #endif
07958 
07959     GuiLib_ShowScreen(GuiStruct_EthernetConnectionPage_Def, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
07960 
07961     GuiLib_Refresh();    
07962 }
07963 
07964 /*
07965     Displays the 'failed to load bitmaps' page
07966     
07967     No args, no return code.
07968 */
07969 void GetGCStatusLoop::DisplayFailedToFindBitmapsPage(void)
07970 {
07971 #ifdef USING_BACKGROUND_BITMAP
07972     DrawBackgroundBitmap(); // Only on this page and the "Connecting to GC" page
07973 #else
07974     GuiLib_Clear();
07975 #endif
07976 
07977     GuiLib_ShowScreen(GuiStruct_FailedToFindBitmapsPage_0, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
07978 
07979     GuiLib_Refresh();    
07980 }
07981 
07982 /*
07983     Display the 'Servicing Required' page, telling the user which components
07984     now need servicing
07985 */
07986 void GetGCStatusLoop::DisplayServicingRequiredPage(void)
07987 {
07988     GetSerialNumber(GuiVar_columnSerialNumber);
07989 
07990     char *easyGuiComponentVariables[6] = { GuiVar_componentNeedsServicing1,
07991                                            GuiVar_componentNeedsServicing2,
07992                                            GuiVar_componentNeedsServicing3,
07993                                            GuiVar_componentNeedsServicing4,
07994                                            GuiVar_componentNeedsServicing5,
07995                                            GuiVar_componentNeedsServicing6
07996                                          };
07997     
07998     // Make sure we clear the 'component needs servicing' easy GUI variables
07999     // before we start - we only want to display the components that 
08000     // *now* need servicing, not whatever needed servicing last time
08001     int componentIndex = 0;
08002     while(componentIndex < 6) {
08003         easyGuiComponentVariables[componentIndex][0] = '\0';
08004         ++componentIndex;
08005     }
08006 
08007     // Now find out which components need servicing, and display their names
08008     componentIndex = 0;
08009     ServiceInterval* expiredServiceInterval = ServiceInterval::GetNextExpiredServiceInterval(NULL);
08010     while((expiredServiceInterval != NULL) && (componentIndex < 6)) {
08011         if(expiredServiceInterval->GetDescriptionLength() < 40) {
08012             expiredServiceInterval->GetDescription(easyGuiComponentVariables[componentIndex]);
08013         }
08014         
08015         ++componentIndex;
08016         
08017         expiredServiceInterval = ServiceInterval::GetNextExpiredServiceInterval(expiredServiceInterval);
08018     }
08019     
08020 #ifdef USING_BACKGROUND_BITMAP
08021     DrawBackgroundBitmap(); 
08022 #else
08023     GuiLib_Clear();
08024 #endif
08025 
08026     GuiLib_ShowScreen(GuiStruct_ServicingRequired_Def, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
08027 
08028     GuiLib_Refresh();    
08029     
08030     SetCurrentPage(GuiStruct_ServicingRequired_Def);
08031 }
08032 
08033 
08034 /*
08035     This needs to be called from SetupAllEasyGUIVariables,
08036     as well as fromDisplayServicingPage
08037 */
08038 void GetGCStatusLoop::SetupServicingPageEasyGUIVariables(void)
08039 {
08040     ServiceInterval* serviceInterval;
08041     
08042     serviceInterval = ServiceInterval::GetServiceInterval(0);
08043     if(serviceInterval != NULL) {
08044         serviceInterval->GetDescription(GuiVar_componentSetupServicing1);
08045     }
08046     
08047     serviceInterval = ServiceInterval::GetServiceInterval(1);
08048     if(serviceInterval != NULL) {
08049         serviceInterval->GetDescription(GuiVar_componentSetupServicing2);
08050     }
08051     
08052     serviceInterval = ServiceInterval::GetServiceInterval(2);
08053     if(serviceInterval != NULL) {
08054         serviceInterval->GetDescription(GuiVar_componentSetupServicing3);
08055     }
08056     
08057     serviceInterval = ServiceInterval::GetServiceInterval(3);
08058     if(serviceInterval != NULL) {
08059         serviceInterval->GetDescription(GuiVar_componentSetupServicing4);
08060     }
08061     
08062     serviceInterval = ServiceInterval::GetServiceInterval(4);
08063     if(serviceInterval != NULL) {
08064         serviceInterval->GetDescription(GuiVar_componentSetupServicing5);
08065     }
08066     
08067     serviceInterval = ServiceInterval::GetServiceInterval(5);
08068     if(serviceInterval != NULL) {
08069         serviceInterval->GetDescription(GuiVar_componentSetupServicing6);
08070     }
08071 }
08072 
08073 /*
08074     Display the 'Servicing' page, allowing our engineer to setup 
08075     the service intervals for each [serviceable] component
08076 */
08077 void GetGCStatusLoop::DisplayServicingPage(void)
08078 {
08079     SetupServicingPageEasyGUIVariables();
08080     
08081 #ifdef USING_BACKGROUND_BITMAP
08082     DrawBackgroundBitmap(); 
08083 #else
08084     GuiLib_Clear();
08085 #endif
08086 
08087     GuiLib_ShowScreen(GuiStruct_ServicingPage_Def, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
08088 
08089     GuiLib_Refresh();    
08090     
08091     SetCurrentPage(GuiStruct_ServicingPage_Def);
08092 }
08093 
08094 
08095 /*
08096     Displays the 'downloading method' page - to try and speed this operation up, 
08097     we do not allow the user to do anything else while we do this -
08098     displaying this page makes that clear to the user
08099     
08100     After completing the download, call 'UndisplayDownloadingMethodPage'
08101     - currently, this always returns to the Running page if the GC is running,
08102      or the Home page if it is not
08103     
08104     No args, no return code.
08105 */
08106 void GetGCStatusLoop::DisplayDownloadingMethodPage(void)
08107 {
08108 #ifdef USING_BACKGROUND_BITMAP
08109     DrawBackgroundBitmap(); 
08110 #else
08111     GuiLib_Clear();
08112 #endif
08113 
08114     GuiLib_ShowScreen(GuiStruct_DownloadingMethodPage_Def, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
08115 
08116     GuiLib_Refresh();   
08117     
08118     currentPage = GuiStruct_DownloadingMethodPage_Def;
08119 }
08120 
08121 /*
08122     Restores our display after the method has downloaded - 
08123     currently, we always return to the Home page unless the GC is running,
08124     in which case we return to the main Running page
08125     
08126     No args, no return code
08127 */
08128 void GetGCStatusLoop::UndisplayDownloadingMethodPage(void)
08129 {
08130 #ifdef USING_BACKGROUND_BITMAP
08131     DrawBackgroundBitmap(); 
08132 #else
08133     GuiLib_Clear();
08134 #endif
08135 
08136     int gcStatus = GetGCStatus();
08137     if(GCStateOrFaultCode::GetSimplifiedGCState(gcStatus) == GC_RUNNING) {
08138         currentPage = GuiStruct_RunningPage1_7; 
08139     } else {
08140         currentPage = GuiStruct_HomePage_1; 
08141     }
08142     
08143     DisplayCurrentPageData(true);
08144 }
08145 
08146 
08147 /*
08148     Displays the data for the current page (easyGUI "structure"), by copying it 
08149     to the relevant easyGUI variables, and calling the relevant functions to actually display it.
08150     
08151     Args: a boolean specifying whether or not the display is actually to be updated.
08152           Even if it is false when we are called, we may set it true if we discover
08153           one or more data items has changed - but note that we will not set it false 
08154           if it is already true. If it is true after we have looked at all 
08155           the home page data, we will then update the display. (If we are called
08156           with this value set to false, the caller is effectively saying
08157           'display the current page data only if it has changed').
08158           
08159     No return code.
08160 */
08161 void GetGCStatusLoop::DisplayCurrentPageData(bool mustUpdateDisplay)
08162 {
08163     // We don't do re-entrancy here - can get random crashes 
08164     // if the user switches between pages too quickly
08165     if(displayingData) {
08166         return;
08167     }
08168     
08169     displayingData = true;
08170     
08171     UpdateAllGCComponentStatusColorAreas();
08172     
08173     switch(currentPage) {
08174         case GuiStruct_HomePage_1:
08175             DisplayHomePageData(mustUpdateDisplay);
08176             break;
08177         case GuiStruct_ColumnPage1_2:
08178             DisplayColumnPageData(mustUpdateDisplay, CONVENTIONAL_COLUMN, GuiStruct_ColumnPage1_2);
08179             break;
08180         case GuiStruct_ColumnPage2_9:
08181             DisplayColumnInformationPageData(mustUpdateDisplay, CONVENTIONAL_COLUMN, GuiStruct_ColumnPage2_9);
08182             break;
08183         case GuiStruct_ColumnMethodPage_Def:
08184             DisplayColumnMethodPageData(mustUpdateDisplay);
08185             break;
08186         case GuiStruct_ColumnTempProfilePage_60:
08187             DisplayColumnTempProfilePageData(mustUpdateDisplay, CONVENTIONAL_COLUMN, GuiStruct_ColumnTempProfilePage_60);
08188             break;
08189         case GuiStruct_ColumnDHAutoCalibrationPage_Def:
08190             DisplayColumnDHAutoCalibrationPageData(mustUpdateDisplay);
08191             break;
08192         case GuiStruct_ColumnDHManualCalibrationPage_Def:
08193             DisplayColumnDHManualCalibrationPageData(mustUpdateDisplay);
08194             break;
08195         case GuiStruct_ColumnDHSensorCalibration_Def:
08196             DisplayColumnDHSensorCalibrationPageData(mustUpdateDisplay);
08197             break;
08198         case GuiStruct_PSU_DAC_Page_Def:
08199             DisplayColumnDHPSUDACPageData(mustUpdateDisplay);
08200             break;
08201         case GuiStruct_ColumnOvenNudgeAndDampPage_0:
08202             DisplayColumnOvenNudgeAndDampPageData(mustUpdateDisplay);
08203             break;
08204         case GuiStruct_ColumnDHNudgeAndDampPage_0:
08205             DisplayColumnDHNudgeAndDampPageData(mustUpdateDisplay);
08206             break;
08207         case GuiStruct_InjectorNudgeAndDampPage_0:
08208             DisplayInjectorNudgeAndDampPageData(mustUpdateDisplay);
08209             break;
08210         case GuiStruct_DetectorNudgeAndDampPage_0:
08211             DisplayDetectorNudgeAndDampPageData(mustUpdateDisplay);
08212             break;
08213         case GuiStruct_AuxiliaryNudgeAndDampPage_0:
08214             DisplayAuxiliaryNudgeAndDampPageData(mustUpdateDisplay);
08215             break;
08216         case GuiStruct_FanPowerPage_0:
08217             DisplayFanPowerPageData(mustUpdateDisplay);
08218             break;
08219         case GuiStruct_DebugCommandsPage_Def:
08220             DisplayDebugCommandsPageData(mustUpdateDisplay);
08221             break;
08222         case GuiStruct_InjectorPage1_3:
08223             DisplayInjectorPageData(mustUpdateDisplay);
08224             break;
08225         case GuiStruct_InjectorMethodPage_Def:
08226             DisplayInjectorMethodPageData(mustUpdateDisplay);
08227             break;
08228         case GuiStruct_InjectorTempProfilePage_25:
08229             DisplayInjectorTempProfilePageData(mustUpdateDisplay);
08230             break;
08231         case GuiStruct_DetectorFIDPage_4:
08232             DisplayDetectorPageData(mustUpdateDisplay, FID_DETECTOR, GuiStruct_DetectorFIDPage_4);
08233             break;
08234         case GuiStruct_DetectorECDPage_12:
08235             DisplayDetectorPageData(mustUpdateDisplay, ECD_DETECTOR, GuiStruct_DetectorECDPage_12);
08236             break;
08237         case GuiStruct_DetectorFPDPage_14:
08238             DisplayDetectorPageData(mustUpdateDisplay, FPD_DETECTOR, GuiStruct_DetectorFPDPage_14);
08239             break;
08240         case GuiStruct_DetectorNPDPage_28:
08241             DisplayDetectorPageData(mustUpdateDisplay, NPD_DETECTOR, GuiStruct_DetectorNPDPage_28);
08242             break;
08243         case GuiStruct_DetectorNonePage_31:
08244             DisplayDetectorPageData(mustUpdateDisplay, NO_DETECTOR, GuiStruct_DetectorNonePage_31);
08245             break;
08246         case GuiStruct_DetectorPIDPage_29:
08247             DisplayDetectorPageData(mustUpdateDisplay, PID_DETECTOR, GuiStruct_DetectorPIDPage_29);
08248             break;
08249         case GuiStruct_DetectorSPDIDPage_30:
08250             DisplayDetectorPageData(mustUpdateDisplay, SPDID_DETECTOR, GuiStruct_DetectorSPDIDPage_30);
08251             break;
08252         case GuiStruct_DetectorTCDPage_11:
08253             DisplayDetectorPageData(mustUpdateDisplay, TCD_DETECTOR, GuiStruct_DetectorTCDPage_11);
08254             break;
08255         case GuiStruct_GasInformationPage_6:
08256             DisplayGasInformationPageData(mustUpdateDisplay);
08257             break;
08258         case GuiStruct_GasMethodPage_Def:
08259             DisplayGasMethodPageData(mustUpdateDisplay);
08260             break;
08261         case GuiStruct_GasProfilePage_15:
08262             DisplayGasFlowProfilePageData(mustUpdateDisplay);
08263             break;
08264         case GuiStruct_GasCalibrationPage_Def:
08265             DisplayGasCalibrationPageData(mustUpdateDisplay);
08266             break;
08267         case GuiStruct_GasBackPressureDACPage_Def:
08268             DisplayGasBackPressureDACPageData(mustUpdateDisplay);
08269             break;
08270         case GuiStruct_GasChannelDACAndADCPage_Def:
08271             DisplayGasChannelDACAndADCPageData(mustUpdateDisplay);
08272             break;
08273         case GuiStruct_RunningPage1_7:
08274             DisplayRunningPageData(mustUpdateDisplay, false);
08275             break;
08276         case GuiStruct_SettingsPage_5:
08277             DisplaySettingsPageData(mustUpdateDisplay);
08278             break;
08279         case GuiStruct_RunningColumnPage_25:
08280             DisplayRunningColumnPageData(mustUpdateDisplay, false);
08281             break;
08282         case GuiStruct_RunningInjectorPage_26:
08283             DisplayRunningInjectorPageData(mustUpdateDisplay);
08284             break;
08285         case GuiStruct_RunningInjectorProfilePage_Def:
08286             DisplayRunningInjectorProfilePageData(mustUpdateDisplay, false);
08287             break;
08288         case GuiStruct_RunningDetectorPage_27:
08289             DisplayRunningDetectorPageData(mustUpdateDisplay);
08290             break;
08291         case GuiStruct_RunningGasPage_28:
08292             DisplayRunningGasPageData(mustUpdateDisplay, false);
08293             break;
08294         case GuiStruct_ServicingPage_Def:
08295             DisplayServicingPage();
08296             break;
08297         case GuiStruct_ServicingRequired_Def:
08298             DisplayServicingRequiredPage();
08299             break;
08300         default:
08301             // Page with no data (e.g. Gas Saver/Standby) - ignore
08302             break;
08303     }
08304     
08305     displayingData = false;
08306 }
08307 
08308 // This effectively duplicates the function of the same name in GCHeatControl - 
08309 // it seems less complicated than calling GCHeatControl::IsHeatOn from here - 
08310 // would need an instance of GCHeatControl in this class, etc - 
08311 // why bother, since both would just call the same GC?
08312 bool GetGCStatusLoop::IsHeatOn(void)
08313 {
08314     char response[50];
08315     SetGCDeviceReport("QHTS", response);
08316     
08317     // Check for "EPKT" first
08318     if(response[0] == 'E') return false;
08319     
08320     return (response[7] != '0');
08321 }
08322 
08323 /*
08324     Gets the GC status code, and returns it as an integer.
08325     
08326     No arguments.
08327     
08328     Returns the status as an integer - see the GC_STATE enumeration (GCStateAndFaultCodes.h) 
08329     for the meaning of each value.
08330 */
08331 int GetGCStatusLoop::GetInstrumentStatus(void)
08332 {
08333     char response[50];
08334     SetGCDeviceReport("QSTA", response);
08335     
08336     // We expect a response of the form "DSTA00nn", where 'nn' is a two-digit code representing the status.
08337     // We convert those two digits to an integer, and return the result to the user
08338     
08339     // But check for "EPKT" first
08340 #ifdef USE_VERSION_102 // See GCStateAndFaultCodes.h
08341     if(response[0] == 'E') return GC_STATE_102_METHOD_FAULTED;
08342 #else
08343     if(response[0] == 'E') return GC_STATE_FAULTED;
08344 #endif
08345 
08346     int retval;
08347     sscanf(&response[6], "%d", &retval);
08348 //#define DEBUG_HERE
08349 #ifdef DEBUG_HERE
08350     char dbg[100];
08351     sprintf(dbg, "GGCSL::GIS - returning : %d", retval);
08352     EasyGUIDebugPrint(dbg, 0, 20);
08353 #undef DEBUG_HERE
08354 #endif
08355     return retval;
08356 }
08357 
08358 /*
08359     Tells the caller whether or not the GC is running, based on its current state
08360     (note that there is not one single 'GC_IS_RUNNING' state).
08361     
08362     Returns true if the GC is running, false if not.
08363 */
08364 bool GetGCStatusLoop::GCIsRunning(void)
08365 {
08366     int currentGCState = GetInstrumentStatus();
08367     
08368     return(GCStateOrFaultCode::GetSimplifiedGCState(currentGCState) == GC_RUNNING);
08369 }
08370 
08371 
08372 /*
08373     Tells the caller whether or not the GC is in the 'stabilising' state
08374     
08375     Returns true if the GC is ing, false if not.
08376 */
08377 bool GetGCStatusLoop::GCIsStabilising(void)
08378 {
08379     int currentGCState = GetInstrumentStatus();
08380     
08381     return(GCStateOrFaultCode::GetSimplifiedGCState(currentGCState) == GC_STABILISING);
08382 }
08383 
08384 
08385 /*
08386     Returns the status of the specified component.
08387     
08388     Args: the component whose status is to be obtained, as a value in the GCComponent enumeration.
08389     
08390     Return value: the status of the specified component, as a value in the GCComponentStatus enumeration.
08391     
08392     The GCComponent and GCComponentStatus enumerations are defined in GCComponentStatusEnums.h.
08393 */
08394 GCComponentStatus GetGCStatusLoop::GetComponentStatus(GCComponent component)
08395 {
08396     int currentGCState = GetInstrumentStatus();
08397     GCStateSimplified simplifiedGCState = GCStateOrFaultCode::GetSimplifiedGCState(currentGCState);
08398     
08399     if(simplifiedGCState == GC_RUNNING) {
08400         return READY; // It must be ready, if the GC is running
08401     }         
08402     // 'else'...
08403 
08404     switch (simplifiedGCState) {
08405         case GC_READY_TO_RUN:
08406             return READY;
08407         case GC_FAULTED:
08408             return FAULTED;
08409         default:
08410             break; // Fall through to code below...
08411     }
08412     
08413     char buff[100];
08414     float temperature, maxTemperature;
08415     float pressure;
08416     
08417     switch (component) {
08418         case COLUMN:
08419             GetColumnTemperature(&temperature);
08420             if(temperature < 0.0f) {// Got "EPKT" response
08421                 return FAULTED;
08422             }
08423             if(temperature < 0.1f) { // Allow for floating-point rounding errors
08424                 return COLD;
08425             }
08426             GetColumnMaxTemperature(&maxTemperature);
08427             if(temperature < maxTemperature) {
08428                 return HEATING_UP;
08429             } else {
08430                 return READY;
08431             }
08432         case INJECTOR:
08433             GetInjectorTemperature(&temperature);
08434             if(temperature < 0.0f) {// Got "EPKT" response
08435                 return FAULTED;
08436             }
08437             if(temperature < 0.1f) { // Allow for floating-point rounding errors
08438                 return COLD;
08439             }
08440             //TODO: Fill in...
08441             return HEATING_UP;
08442         case DETECTOR:
08443             GetDetectorTemperature(&temperature);
08444             if(temperature < 0.0f) {// Got "EPKT" response
08445                 return FAULTED;
08446             }
08447             if(temperature < 0.1f) { // Allow for floating-point rounding errors
08448                 return COLD;
08449             }
08450             //TODO: Fill in...
08451             return HEATING_UP;
08452         case GAS:
08453             if(GetGasControlMode(buff, false, false) == false) {
08454                 // Got "EPKT"
08455                 return FAULTED;
08456             }
08457             GetGasPressure(&pressure);
08458             if(pressure < 0.0f) {// Got "EPKT" response
08459                 return FAULTED;
08460             }
08461             if(pressure < 0.1f) { // Allow for floating-point rounding errors
08462                 return COLD;
08463             }
08464             //TODO: Fill in...
08465             return HEATING_UP;
08466         default:
08467             sprintf(buff, "Unknown component: %d", component);
08468             EasyGUIDebugPrint(buff, 0, 20);
08469             break;
08470     }
08471 
08472     return FAULTED;
08473 }
08474 
08475 /*
08476     Gets the status of the specified component, amd returns an appropriate null-terminated descriptive string
08477     in the buffer provided
08478     
08479     Note also that this code is intended to be as efficient as possible.
08480     
08481     Args: the component whose status is to be obtained, as a value in the GCComponent enumeration.
08482           pointer to the buffer to contain the null-terminated descriptive string
08483     
08484     The GCComponent and GCComponentStatus enumerations are defined in GCComponentStatusEnums.h.
08485     
08486     No return value.
08487 */
08488 void GetGCStatusLoop::GetComponentStatusString(GCComponent component, char *buff)
08489 {
08490     int index = 0;
08491     switch (GetComponentStatus(component)) {
08492         case COLD:
08493             buff[index++] = 'C';
08494             buff[index++] = 'o';
08495             buff[index++] = 'l';
08496             buff[index++] = 'd';
08497             break;
08498         case HEATING_UP:
08499             if(IsHeatOn()) {
08500                 if(GCIsStabilising()) {
08501                     buff[index++] = 'S';
08502                     buff[index++] = 't';
08503                     buff[index++] = 'a';
08504                     buff[index++] = 'b';
08505                     buff[index++] = 'i';
08506                     buff[index++] = 'l';
08507                     buff[index++] = 'i';
08508                     buff[index++] = 's';
08509                     buff[index++] = 'i';
08510                     buff[index++] = 'n';
08511                     buff[index++] = 'g';
08512                 } else {
08513                     // Assume it is equilibrating
08514                     buff[index++] = 'E';
08515                     buff[index++] = 'q';
08516                     buff[index++] = 'u';
08517                     buff[index++] = 'i';
08518                     buff[index++] = 'l';
08519                     buff[index++] = 'i';
08520                     buff[index++] = 'b';
08521                     buff[index++] = 'r';
08522                     buff[index++] = 'a';
08523                     buff[index++] = 't';
08524                     buff[index++] = 'i';
08525                     buff[index++] = 'n';
08526                     buff[index++] = 'g';
08527                 }
08528             } else {
08529                 buff[index++] = 'I';
08530                 buff[index++] = 'd';
08531                 buff[index++] = 'l';
08532                 buff[index++] = 'e';
08533             }
08534             break;
08535         case READY:
08536             buff[index++] = 'R';
08537             buff[index++] = 'e';
08538             buff[index++] = 'a';
08539             buff[index++] = 'd';
08540             buff[index++] = 'y';
08541             break;        
08542         default: // Including FAULTED
08543             buff[index++] = 'F';
08544             buff[index++] = 'a';
08545             buff[index++] = 'u';
08546             buff[index++] = 'l';
08547             buff[index++] = 't';
08548             buff[index++] = 'e';
08549             buff[index++] = 'd';
08550             break;
08551     }
08552     buff[index++] = '\0';
08553 }
08554 
08555 
08556 /*
08557     Returns true if the GC is in standby mode, false if not.
08558 */
08559 bool GetGCStatusLoop::GCIsInStandbyMode(void)
08560 {
08561     char response[50];
08562     SetGCDeviceReport("QDIS", response);
08563     
08564     // We expect a response of the form "DDIS000n", where 'n' == '0' means the GC is in standby mode,
08565     // while 'n' == '1' means that it is not in standby mode
08566 
08567     bool retval = (response[7] == '0');
08568 
08569     // Also check for "EPKT" 
08570     if(response[0] == 'E') retval = false;
08571     
08572 //#define DEBUG_HERE
08573 #ifdef DEBUG_HERE
08574     if(retval) {
08575         EasyGUIDebugPrint("GGCSL::GCIISM - returning true", 0, 20);
08576     } else {
08577         EasyGUIDebugPrint("GGCSL::GCIISM - returning false", 0, 20);
08578     }
08579 #endif
08580 //#undef DEBUG_HERE
08581     return retval;
08582 }
08583 
08584 /*
08585     Caller uses this to tell us that the GC has exited from standby mode
08586 */
08587 void GetGCStatusLoop::ExitedGCStandbyMode(void)
08588 {
08589     gcInStandbyMode = false;
08590 }
08591 
08592 /*
08593     Takes the GC out of standby mode, by passing it the "CDIS" command.
08594     
08595     Returns true if the GC returned "DACK" in response, false if it returned "DNAK".
08596 */
08597 bool GetGCStatusLoop::ExitGCStandbyMode(void)
08598 {
08599     if(ExecuteCommandWithDACKResponse("CDIS")) {
08600  
08601         ExitedGCStandbyMode();
08602     
08603         return true;
08604     }
08605     
08606     return false;
08607 }
08608 
08609 /*
08610     Displays the standby mode page (easyGUI "structure")
08611 */
08612 void GetGCStatusLoop::DisplayStandbyModePage(void)
08613 {
08614 #ifdef USING_BACKGROUND_BITMAP
08615     DrawBackgroundBitmap(); 
08616 #else
08617     GuiLib_Clear();
08618 #endif
08619 
08620     GuiLib_ShowScreen(GuiStruct_GasSaver_9, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
08621 
08622     GuiLib_Refresh();
08623     
08624 #define DEBUG_HERE
08625 #ifdef DEBUG_HERE
08626     char dbg[100];
08627     sprintf(dbg, "After GuiLib_Clear 7");
08628     EasyGUIDebugPrint(dbg, 0, 20);
08629 #undef DEBUG_HERE
08630 #endif
08631         
08632     SetCurrentPage(GuiStruct_GasSaver_9);
08633 }
08634 
08635 /*
08636     Displays the easyGUI page (or "structure") that shows the user that the GC has faulted
08637 */
08638 void GetGCStatusLoop::DisplayGCInFaultStatePage(bool clearDisplay)
08639 {
08640     if(clearDisplay) {
08641 #ifdef USING_BACKGROUND_BITMAP
08642         DrawBackgroundBitmap(); 
08643 #else
08644         GuiLib_Clear();
08645 #endif
08646     }
08647 
08648     // Display red background to the error message, same as for a single component page
08649     GCComponentStatusColorArea::DisplayErrorRectangle();
08650         
08651     GuiLib_ShowScreen(GuiStruct_GCInFaultStatePage_11, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
08652     
08653     GuiLib_Refresh();
08654 
08655 #define DEBUG_HERE
08656 #ifdef DEBUG_HERE
08657     char dbg[100];
08658     sprintf(dbg, "After GuiLib_Clear 8");
08659     EasyGUIDebugPrint(dbg, 0, 20);
08660 #undef DEBUG_HERE
08661 #endif
08662         
08663     SetCurrentPage(GuiStruct_GCInFaultStatePage_11);
08664 }
08665 
08666 void GetGCStatusLoop::DisplayRunCompletePage(void)
08667 {
08668 #ifdef USING_BACKGROUND_BITMAP
08669     DrawBackgroundBitmap(); 
08670 #else
08671     GuiLib_Clear();
08672 #endif
08673 
08674     GuiLib_ShowScreen(GuiStruct_RunCompletePage_41, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
08675     
08676     GuiLib_Refresh();
08677     
08678     SetCurrentPage(GuiStruct_RunCompletePage_41);
08679 }
08680 
08681 void GetGCStatusLoop::DisplayRunAbortedPage(void)
08682 {
08683 #ifdef USING_BACKGROUND_BITMAP
08684     DrawBackgroundBitmap(); 
08685 #else
08686     GuiLib_Clear();
08687 #endif
08688 
08689     GuiLib_ShowScreen(GuiStruct_RunAbortedPage_Def, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
08690     
08691     GuiLib_Refresh();
08692     
08693     SetCurrentPage(GuiStruct_RunAbortedPage_Def);
08694 }
08695 
08696 /*
08697     Takes the GC out of its error state, by passing it the "CCLR" command.
08698     
08699     Returns true if the GC returned "DACK" in response, false if it returned "DNAK".
08700 */
08701 bool GetGCStatusLoop::ClearGCErrors(void)
08702 {
08703     return ExecuteCommandWithDACKResponse("CCLR");
08704 }
08705 
08706 /*
08707     Aborts the current GC run, by passing it the "CABT" command.
08708     
08709     Returns true if the GC returned "DACK" in response, false if it returned "DNAK".
08710 */
08711 bool GetGCStatusLoop::AbortGCRun(void)
08712 {
08713     return ExecuteCommandWithDACKResponse("CABT");
08714 }
08715 
08716 
08717 /*
08718     Make sure that the component statuses, recorded in each of the colour areas for the home page,
08719     are up to date.
08720 */
08721 void GetGCStatusLoop::UpdateHomePageGCComponentStatusColorAreas(void)
08722 {
08723     GCComponentStatus columnStatus = GetComponentStatus(COLUMN);
08724     GCComponentStatus injectorStatus = GetComponentStatus(INJECTOR);
08725     GCComponentStatus detectorStatus = GetComponentStatus(DETECTOR);
08726     GCComponentStatus gasStatus = GetComponentStatus(GAS);
08727     
08728     if(homePageGCComponentStatusColorAreas != NULL) {
08729         homePageGCComponentStatusColorAreas->SetGCComponentStatus(COLUMN, columnStatus);
08730         homePageGCComponentStatusColorAreas->SetGCComponentStatus(INJECTOR, injectorStatus);
08731         homePageGCComponentStatusColorAreas->SetGCComponentStatus(DETECTOR, detectorStatus);
08732         homePageGCComponentStatusColorAreas->SetGCComponentStatus(GAS, gasStatus);
08733     }
08734 }
08735 
08736 /*
08737     Returns true if the current status of any of the components on the home page
08738     is now different to that displayed in the relevant status rectangle
08739     *** when we last displayed it ***
08740 */
08741 bool GetGCStatusLoop::HomePageGCComponentStatusesHaveChanged(void)
08742 {
08743     if(homePageGCComponentStatusColorAreas != NULL) {
08744         if(GetComponentStatus(COLUMN) != lastColumnStatusDisplayedOnHomePage) {
08745             return true;
08746         }
08747         
08748         if(GetComponentStatus(INJECTOR) != lastInjectorStatusDisplayedOnHomePage) {
08749             return true;
08750         }
08751         
08752         if(GetComponentStatus(DETECTOR) != lastDetectorStatusDisplayedOnHomePage) {
08753             return true;
08754         }
08755         
08756         if(GetComponentStatus(GAS) != lastGasStatusDisplayedOnHomePage) {
08757             return true;
08758         }
08759     }
08760 
08761     return false;    
08762 }
08763 
08764 /*
08765     Make sure that the component status, recorded in the colour area for a single component page,
08766     is up to date.
08767     
08768     Args: the component whose colour area is to be updated, as a value in the GCComponent enumeration.
08769     
08770     The GCComponent enumeration is defined in GCComponentStatusEnums.h.
08771 */
08772 void GetGCStatusLoop::UpdateSingleGCComponentPageStatusColorArea(GCComponent component)
08773 {
08774     GCComponentStatus componentStatus = GetComponentStatus(component);
08775     
08776     if(singleGCComponentPageStatusColorAreas != NULL) {
08777         singleGCComponentPageStatusColorAreas->SetGCComponentStatus(component, componentStatus);
08778     }
08779 }
08780 
08781 /*
08782     Update all the component status colour areas (i.e. the home page colour areas
08783     and the single component page colour areas), so they all match
08784 */
08785 void GetGCStatusLoop::UpdateAllGCComponentStatusColorAreas(void)
08786 {
08787     GCComponentStatus columnStatus = GetComponentStatus(COLUMN);
08788     GCComponentStatus injectorStatus = GetComponentStatus(INJECTOR);
08789     GCComponentStatus detectorStatus = GetComponentStatus(DETECTOR);
08790     GCComponentStatus gasStatus = GetComponentStatus(GAS);
08791     
08792     if(homePageGCComponentStatusColorAreas != NULL) {
08793         homePageGCComponentStatusColorAreas->SetGCComponentStatus(COLUMN, columnStatus);
08794         homePageGCComponentStatusColorAreas->SetGCComponentStatus(INJECTOR, injectorStatus);
08795         homePageGCComponentStatusColorAreas->SetGCComponentStatus(DETECTOR, detectorStatus);
08796         homePageGCComponentStatusColorAreas->SetGCComponentStatus(GAS, gasStatus);
08797     }
08798     
08799     if(singleGCComponentPageStatusColorAreas != NULL) {
08800         singleGCComponentPageStatusColorAreas->SetGCComponentStatus(COLUMN, columnStatus);
08801         singleGCComponentPageStatusColorAreas->SetGCComponentStatus(INJECTOR, injectorStatus);
08802         singleGCComponentPageStatusColorAreas->SetGCComponentStatus(DETECTOR, detectorStatus);
08803         singleGCComponentPageStatusColorAreas->SetGCComponentStatus(GAS, gasStatus);
08804     }
08805 }
08806 
08807 /*
08808     Returns true if the status of the specified component is now different 
08809     to that recorded in the status rectangle for the relevant single component page
08810 */
08811 bool GetGCStatusLoop::SinglePageGCComponentStatusHasChanged(GCComponent component)
08812 {
08813     if(singleGCComponentPageStatusColorAreas != NULL) {
08814         if(GetComponentStatus(component) != singleGCComponentPageStatusColorAreas->GetGCComponentStatus(component)) {
08815             return true;
08816         }
08817     }
08818 
08819     return false;    
08820 }
08821     
08822 /*
08823     Returns true if the status of the specified component is now different 
08824     to the last status displayed in the relevant status rectangle
08825 */
08826 bool GetGCStatusLoop::SinglePageGCComponentStatusHasChanged(GCComponent component, GCComponentStatus lastStatusDisplayed)
08827 {
08828     if(GetComponentStatus(component) != lastStatusDisplayed) {
08829         return true;
08830     }
08831 
08832     return false;    
08833 }
08834     
08835 /*
08836     Set up (i.e. get from the GC) the values for all the EasyGUI variables used by the various pages/structures we display.
08837     Caller should do this before entering our main loop - this ensures these variables are set up and ready
08838     before each of the pages is displayed
08839 
08840     No arguments.
08841     
08842     No return code.
08843 */
08844 void GetGCStatusLoop::SetupAllEasyGUIVariables(void)
08845 {
08846     // Set up (i.e. get from the GC) the values for all the EasyGUI variables used by the various pages/structures we display.
08847     // Caller should do this before entering our main loop - this ensures these variables are set up and ready
08848     // before each of the pages is displayed
08849 
08850     // Home page
08851 #define SETUP_HOME_PAGE
08852 #ifdef SETUP_HOME_PAGE
08853     if(GetColumnType() == DIRECTLY_HEATED_COLUMN) {
08854         GetDirectlyHeatedColumnTemperature(GuiVar_columnTemperature2, false);
08855     } else {
08856         GetColumnTemperature(GuiVar_columnTemperature2, false);
08857     }
08858     GetColumnTargetTemperature(GuiVar_columnTargetTemperature, "(Target: %s)");
08859     GetDetectorTemperature(GuiVar_detectorTemperature2);
08860     GetDetectorTargetTemperature(GuiVar_detectorTargetTemperature, "(Target: %s)");
08861     GetInjectorTemperature(GuiVar_injectorTemperature2, false);
08862     GetInjectorTargetTemperature(GuiVar_injectorTargetTemperature, "(Target: %s)");
08863     GetCurrentGasPressure(GuiVar_gasPressure2, false, true);
08864     GetGCRealTimeClockTime(GuiVar_gcTimeOnHomePage);
08865 #undef SETUP_HOME_PAGE
08866 #endif
08867     // We were omitting the home page variables here, on the theory that the home page is the first one displayed, 
08868     // so its variables will get updated anyway, and if we set them here, the page does not get updated when first displayed. 
08869     // However, this no longer appears to be true (and I cannot now remember why I thought it was). 
08870     // If we do *not* update these variables here, then we see the component status rectangles in their correct colours at startup, 
08871     // but the text does not appear for several seconds. If we *do* update these variables, the text is displayed at startup
08872     // at the same time as the rectangles, i.e. we get the behaviour we want.
08873         
08874     // Column page
08875     GetColumnTemperature(GuiVar_columnTemperature, false, true);
08876     GetColumnTargetTemperature(GuiVar_columnTargetTemperature2, stringFormatdegCUnits);
08877     GetColumnMaxTemperature(GuiVar_columnMaxTemp2, false, false, true);
08878     GetComponentStatusString(COLUMN, GuiVar_columnStatus);
08879     
08880     // Column Information page
08881     GetColumnType(GuiVar_columnType);
08882     GetColumnLength(GuiVar_columnLength);
08883     GetColumnInnerDiameter(GuiVar_columnInnerDiameter);
08884     GetColumnOuterDiameter(GuiVar_columnOuterDiameter);
08885     GetSerialNumber(GuiVar_columnSerialNumber);
08886            
08887     // Column Method page
08888     UpdateColumnMethodPageData();
08889 
08890     // Injector page
08891     GetInjectorTemperature(GuiVar_injectorTemperature, false);
08892     GetInjectorTargetTemperature(GuiVar_injectorTargetTemperature2, stringFormatdegCUnits);
08893     GetInjectionMode(GuiVar_injectionMode2);
08894     GetInjectorType(GuiVar_injectorType, false);
08895     GetComponentStatusString(INJECTOR, GuiVar_injectorStatus);
08896     
08897     // Injector Method page
08898     UpdateInjectorMethodPageData();
08899 
08900     // Detector page(s)
08901     GetDetectorType(GuiVar_detectorType2, false, false);    
08902     GetDetectorRange(GuiVar_detectorRange);
08903     GetFuelFlowRate(GuiVar_detectorFuelFlowRate);    
08904     GetAirFlowRate(GuiVar_detectorAirFlowRate);    
08905     GetDetectorIgnitionState(GuiVar_detectorStatus);
08906     GetDetectorTemperature(GuiVar_detectorTemperature);
08907     GetDetectorTargetTemperature(GuiVar_detectorTargetTemperature2, stringFormatdegCUnits);
08908     GetTCDDetectorFilamentPolarity(GuiVar_tcdDetectorFilamentPolarity);
08909     GetTCDDetectorFilamentTemperature(GuiVar_tcdDetectorFilamentTemperature);
08910     GetTCDDetectorRange(GuiVar_tcdDetectorRange);
08911     GetECDDetectorCurrent(GuiVar_ecdDetectorCurrent);
08912     //GetFPDDetectorRange(GuiVar_fpdDetectorRange);
08913     // No - now same command as all other detector types
08914     GetDetectorRange(GuiVar_fpdDetectorRange);
08915     GetFPDDetectorSensitivity(GuiVar_fpdDetectorSensitivity);
08916     
08917     // Gas page
08918     GetGasPressure(GuiVar_gasPressure, false, true);
08919     GetGasPulsedPressure(GuiVar_gasPulsedPressure);
08920     GetGasControlMode(GuiVar_gasControlMode2, true, false);
08921     GetCarrierGasType(GuiVar_gasCarrierType);
08922     GetGasFilterDateChanged(GuiVar_gasFilterDateChanged);    
08923     
08924     // Gas Method page
08925     UpdateGasMethodPageData();
08926 
08927     // Settings page
08928     GetGCSoftwareVersion(GuiVar_gcSoftwareVersion);
08929     GetActuatorSoftwareVersion(GuiVar_actuatorSoftwareVersion);
08930     GetRunTime(GuiVar_runTime);    
08931 //    GetInjectionMode(GuiVar_injectionMode, true);
08932     GetInjectorType(GuiVar_injectionMode, true);
08933     GetColumnMaxTemperature(GuiVar_columnMaxTemp, true, true);
08934     GetDetectorType(GuiVar_detectorType, true, true);
08935     GetGasControlMode(GuiVar_gasControlMode, true, true);
08936     GetSerialNumber(GuiVar_gcSerialNumber);    
08937     GetGCRealTimeClockTime(GuiVar_gcTime);
08938 
08939     // Servicing page
08940     SetupServicingPageEasyGUIVariables();
08941 }
08942 
08943 
08944 /*
08945     Tells the caller whether or not a specified GC command is (potentially)
08946     part of a SendMethod sequence.
08947     
08948     Params: pointer to a null-terminated string containing the command in question
08949     
08950     Returns true if the command is one that is commonly part of a method specification,
08951     false if not.
08952     
08953     This code is intended to be as efficient as possible.
08954 */
08955 bool GetGCStatusLoop::IsSendMethodCommand(char *gcCommand)
08956 {
08957     // First of all - must be an "Sxxx" (i.e. set) command
08958     if(gcCommand[0] != 'S') {
08959         // Cannot be a 'set' command
08960         return false;
08961     }
08962     
08963     if((gcCommand[1] == 'C') && (gcCommand[2] == 'O') && (gcCommand[3] == 'L')) {
08964         // "SCOL" command
08965         return true;
08966     }
08967     
08968     if((gcCommand[1] == 'I') && (gcCommand[2] == 'N') && (gcCommand[3] == 'J')) {
08969         // "SINJ" command
08970         return true;
08971     }
08972     
08973     if((gcCommand[1] == 'D') && (gcCommand[2] == 'E') && (gcCommand[3] == 'T')) {
08974         // "SDET" command
08975         return true;
08976     }
08977     
08978     if((gcCommand[1] == 'A') && (gcCommand[2] == 'U') && (gcCommand[3] == 'X')) {
08979         // "SAUX" command
08980         return true;
08981     }
08982     
08983     if((gcCommand[1] == 'S') && (gcCommand[2] == 'T') && (gcCommand[3] == 'B')) {
08984         // "SSTB" command
08985         return true;
08986     }
08987     
08988     if((gcCommand[1] == 'T') && (gcCommand[2] == 'I') && (gcCommand[3] == 'M')) {
08989         // "STIM" command
08990         return true;
08991     }
08992     
08993     if((gcCommand[1] == 'R') && (gcCommand[2] == 'P')) {
08994         // "SRPn" command
08995         return true;
08996     }
08997     
08998     if((gcCommand[1] == 'R') && (gcCommand[2] == 'C')) {
08999         // "SRCn" command
09000         return true;
09001     }
09002     
09003     if((gcCommand[1] == 'R') && (gcCommand[2] == 'S')) {
09004         // "SRSn" command
09005         return true;
09006     }
09007     
09008     if((gcCommand[1] == 'P') && (gcCommand[2] == 'R')) {
09009         // "SPRS" or "SPRn" command
09010         return true;
09011     }
09012     
09013     if((gcCommand[1] == 'P') && (gcCommand[2] == 'P') && (gcCommand[3] == 'S')) {
09014         // "SPPS" command
09015         return true;
09016     }
09017     
09018     if((gcCommand[1] == 'P') && (gcCommand[2] == 'U')) {
09019         // "SPUn" command
09020         return true;
09021     }
09022     
09023     if((gcCommand[1] == 'I') && (gcCommand[2] == 'M') && (gcCommand[3] == 'D')) {
09024         // "SIMD" command
09025         return true;
09026     }
09027     
09028     if((gcCommand[1] == 'S') && (gcCommand[2] == 'P') && (gcCommand[3] == 'T')) {
09029         // "SSPT" command
09030         return true;
09031     }
09032     
09033     if((gcCommand[1] == 'C') && (gcCommand[2] == 'F') && (gcCommand[3] == 'L')) {
09034         // "SCFL" command
09035         return true;
09036     }
09037     
09038     if((gcCommand[1] == 'T') && (gcCommand[2] == 'F') && (gcCommand[3] == 'L')) {
09039         // "STFL" command
09040         return true;
09041     }
09042     
09043     if((gcCommand[1] == 'M') && (gcCommand[2] == 'A') && (gcCommand[3] == 'K')) {
09044         // "SMAK" command
09045         return true;
09046     }
09047     
09048     if((gcCommand[1] == 'C') && (gcCommand[2] == 'L') && (gcCommand[3] == 'B')) {
09049         // "SCLB" command
09050         return true;
09051     }
09052     
09053     return false; // None of the above
09054 }
09055 
09056 
09057 /*
09058     Forces the MainLoopWithEthernet function to exit its while loop
09059     and reboot the whole application
09060 */
09061 void GetGCStatusLoop::ForceRestart(void)
09062 {
09063     restartRequired = true;
09064 }
09065 
09066 
09067 /*
09068     Deals with the user touching the screen. Works out if the user touched an area
09069     we are interested in, and if so, handles it appropriately.
09070     
09071     Args: the X, Y and Z cordinates where the user touched (the touch panel gives us 
09072           all three coordinates, although currently we do not use the Z coordinate).
09073     
09074     No return code.
09075     
09076     The intention is that this function will, in effect, supersede the 'TouchCallback' function in main.cpp.
09077     For now, we are calling that function from here.
09078     
09079     Also note that this function sets 'handlingTouchEvent' true while it is dealing with the touch event.
09080     Caller should not call this function if this flag is true.
09081 */
09082 void GetGCStatusLoop::HandleTouchEvent(short touchX, short touchY, short touchZ, int debugValue)
09083 {
09084     handlingTouchEvent = true;
09085     
09086 #if defined MULTI_TOUCH_TECHNIQUE_1
09087     // Enforce a minimum time interval between the touch events we respond to 
09088     if((timerTickCount - lastTouchEventTickCount) > minTimerTicksBetweenTouchEvents) {
09089 #endif
09090 #if defined MULTI_TOUCH_TECHNIQUE_2
09091     // Assume we are unlikely to get separate 'touches' in succession at exactly the same coordinates
09092     //if((x != lastTouchEventX) && (y != lastTouchEventY)) { // was '||', i.e. OR, not AND - we still got 'multiple touches'
09093     //Try and apply a minimum 'movement'...
09094     if((abs(touchX - lastTouchEventX) > 2) || (abs(touchY - lastTouchEventY) > 2)) { // Note the '||' - we miss too many 'touches' if we use '&&'
09095 #endif
09096 #if defined MULTI_TOUCH_TECHNIQUE_3
09097     if(gotAtLeastOneTimeout) {
09098 #endif
09099 #if defined MULTI_TOUCH_TECHNIQUE_4
09100     if(touchTimer.read_ms() > timerIntervalMilliSec) {
09101 #endif
09102         char dbg[200];
09103         sprintf(dbg, "*** Got touch event at %d, %d, %d, %d ***", touchX, touchY, touchZ, timerTickCount);
09104         EasyGUIDebugPrintWithCounter(dbg, 430, 450);
09105                 
09106         touch_coordinate_t touchCoords;
09107         touchCoords.x = touchX;
09108         touchCoords.y = touchY;
09109         touchCoords.z = touchZ;
09110         
09111         TouchCallback(touchCoords, usbDevice, usbHostGC, 1, true);
09112             
09113 #if defined MULTI_TOUCH_TECHNIQUE_1
09114         lastTouchEventTickCount = timerTickCount;
09115     }
09116 #endif
09117 #if defined MULTI_TOUCH_TECHNIQUE_2
09118         lastTouchEventX = touchX;
09119         lastTouchEventY = touchY;
09120     }
09121 #endif
09122 #if defined MULTI_TOUCH_TECHNIQUE_3
09123         gotAtLeastOneTimeout = false;
09124     }
09125 #endif
09126 #if defined MULTI_TOUCH_TECHNIQUE_4
09127         touchTimer.stop();
09128         touchTimer.reset();
09129         touchTimer.start();
09130     }
09131 #endif
09132     
09133     handlingTouchEvent = false;
09134 }
09135 
09136 /*
09137     Tells the caller if the specified command was a Run command,
09138     and if it succeeded - i.e. the command was "CRUN", and 
09139     the GC responded with "DACK".
09140     
09141     Args: pointers to null-terminated strings containing the command in question,
09142           and the GC response
09143           
09144     True if it was a "CRUN" command, and the response was "DACK", false otherwise
09145 */
09146 bool GetGCStatusLoop::SuccessfulRunCommand(char* gcCommand, char* gcResponse)
09147 {
09148     if(gcCommand[0] != 'C') {
09149         return false;
09150     }
09151 
09152     if(gcCommand[1] != 'R') {
09153         return false;
09154     }
09155 
09156     if(gcCommand[2] != 'U') {
09157         return false;
09158     }
09159 
09160     if(gcCommand[3] != 'N') {
09161         return false;
09162     }
09163     
09164     
09165     if(gcResponse[0] != 'D') {
09166         return false;
09167     }
09168 
09169     if(gcResponse[1] != 'A') {
09170         return false;
09171     }
09172 
09173     if(gcResponse[2] != 'C') {
09174         return false;
09175     }
09176 
09177     if(gcResponse[3] != 'K') {
09178         return false;
09179     }
09180 
09181     // All the above were true
09182     return true;
09183 }
09184 
09185 
09186 /*
09187     Tells the caller if the specified command was an Abort command,
09188     and if it succeeded - i.e. the command was "CABT" or "CSTP"
09189     (currently these two GC commands are equivalent), 
09190     and the GC responded with "DACK".
09191     
09192     Args: pointers to null-terminated strings containing the command in question,
09193           and the GC response
09194           
09195     True if it was a "CRUN" command, and the response was "DACK", false otherwise
09196 */
09197 bool GetGCStatusLoop::SuccessfulAbortCommand(char* gcCommand, char* gcResponse)
09198 {
09199     if(gcCommand[0] != 'C') {
09200         // Not a command at all - give up
09201         return false;
09202     }
09203     
09204     bool wasAbortCommand = false;
09205     
09206     if((gcCommand[1]== 'A') && (gcCommand[2]== 'B') && (gcCommand[3]== 'T')) {
09207         wasAbortCommand = true;
09208     } else if ((gcCommand[1]== 'S') && (gcCommand[2]== 'T') && (gcCommand[3]== 'P')) {
09209         wasAbortCommand = true;
09210     }
09211     
09212     if(!wasAbortCommand) {
09213         return false;
09214     }
09215     
09216         
09217     // Now the response...
09218         
09219     if(gcResponse[0] != 'D') {
09220         return false;
09221     }
09222 
09223     if(gcResponse[1] != 'A') {
09224         return false;
09225     }
09226 
09227     if(gcResponse[2] != 'C') {
09228         return false;
09229     }
09230 
09231     if(gcResponse[3] != 'K') {
09232         return false;
09233     }
09234 
09235     // Must have been an abort command, acknowledged with "DACK"
09236     return true;
09237 }
09238 
09239 /*
09240     Tells the caller if the specified page includes one or more component status rectangles,
09241     or a component status displayed as text, and will therefore need to be redisplayed 
09242     if the component status changes
09243     
09244     Args: the number of the page in question
09245     
09246     Returns: true if the page contains the status of one or more components, false if not
09247 */
09248 bool GetGCStatusLoop::PageIncludesComponentStatus(int pageNumber)
09249 {
09250     // Quicker to decide which pages do *not* include a component status...
09251     
09252     if(pageNumber == GuiStruct_AbortRunPage_19) {
09253         return false;
09254     }
09255 
09256 //#define USING_REAL_BACKGROUND_BITMAP
09257 #ifdef USING_REAL_BACKGROUND_BITMAP
09258     if(pageNumber == GuiStruct_BackgroundBitmapPage_Def) {
09259         return false;
09260     }
09261 #endif // USING_REAL_BACKGROUND_BITMAP
09262 
09263     if(pageNumber == GuiStruct_DownloadingMethodPage_Def) {
09264         return false;
09265     }
09266     
09267     if(pageNumber == GuiStruct_EthernetConnectionPage_Def) {
09268         return false;
09269     }
09270     
09271     if(pageNumber == GuiStruct_EthernetParametersPage_50) {
09272         return false;
09273     }
09274     
09275     if(pageNumber == GuiStruct_GCConnectionPage_Def) {
09276         return false;
09277     }
09278     
09279     if(pageNumber == GuiStruct_GasSaver_9) {
09280         return false;
09281     }
09282     
09283     if(pageNumber == GuiStruct_RunAbortedPage_Def) {
09284         return false;
09285     }
09286     
09287     if(pageNumber == GuiStruct_RunCompletePage_41) {
09288         return false;
09289     }
09290     
09291     if(pageNumber == GuiStruct_RunningColumnPage_25) {
09292         return false;
09293     }
09294     
09295     if(pageNumber == GuiStruct_RunningDetectorPage_27) {
09296         return false;
09297     }
09298     
09299     if(pageNumber == GuiStruct_RunningGasPage_28) {
09300         return false;
09301     }
09302     
09303     if(pageNumber == GuiStruct_RunningInjectorPage_26) {
09304         return false;
09305     }
09306     
09307     if(pageNumber == GuiStruct_RunningPage1_7) {
09308         return false;
09309     }
09310     
09311     if(pageNumber == GuiStruct_ServicingPage_Def) {
09312         return false;
09313     }
09314     
09315     if(pageNumber == GuiStruct_ServicingRequired_Def) {
09316         return false;
09317     }
09318     
09319     if(pageNumber == GuiStruct_SettingsPage_5) {
09320         return false;
09321     }
09322     
09323     if(pageNumber == GuiStruct_NumericKeypadPage_Def) {
09324         return false;
09325     }
09326     
09327     // 'else' - none of the above
09328     return true;    
09329 }
09330 
09331 
09332 /*
09333     Should be done at startup, and after a new method has been downloaded
09334 */
09335 void GetGCStatusLoop::UpdateColumnMethodPageData(void)
09336 {
09337     GetColumnTargetTemperature(GuiVar_columnMethodInitialTemp, "%s");
09338     GetInitialHoldTime(GuiVar_columnMethodInitialHold);
09339     if(columnMethodRampData == NULL) {
09340         columnMethodRampData = new ColumnMethodRampData(usbDevice, usbHostGC);
09341     }
09342     if(columnMethodRampData != NULL) {
09343         columnMethodRampData->GetRampDataFromGC();
09344         
09345         sprintf(GuiVar_columnMethodRampCount, "%u", columnMethodRampData->GetRampCount());
09346 
09347         if(columnMethodRampData->NeedToUpdateEasyGUIMethodPageRampVariables()) {
09348             columnMethodPageScrollIndex = 0;
09349             
09350             columnMethodRampData->UpdateEasyGUIMethodPageVariables(columnMethodPageScrollIndex);
09351             
09352             previousColumnMethodPageScrollIndex = columnMethodPageScrollIndex;
09353         }
09354     }
09355 }
09356 
09357 
09358 /*
09359     Should be done at startup, and after a new method has been downloaded
09360 */
09361 void GetGCStatusLoop::UpdateInjectorMethodPageData(void)
09362 {
09363     GetInjectorTargetTemperature(GuiVar_injectorMethodInitialTemp, "%s");
09364     GetInitialHoldTime(GuiVar_injectorMethodInitialHold);
09365     if(injectorMethodRampData == NULL) {
09366         injectorMethodRampData = new InjectorMethodRampData(usbDevice, usbHostGC);
09367     }
09368     if(injectorMethodRampData != NULL) {
09369         injectorMethodRampData->GetRampDataFromGC();
09370         
09371         sprintf(GuiVar_injectorMethodRampCount, "%u", injectorMethodRampData->GetRampCount());
09372 
09373         if(injectorMethodRampData->NeedToUpdateEasyGUIMethodPageRampVariables()) {
09374             injectorMethodPageScrollIndex = 0;
09375             
09376             injectorMethodRampData->UpdateEasyGUIMethodPageVariables(injectorMethodPageScrollIndex);
09377             
09378             previousInjectorMethodPageScrollIndex = injectorMethodPageScrollIndex;
09379         }
09380     }
09381 }
09382 
09383 
09384 /*
09385     Should be done at startup, and after a new method has been downloaded
09386 */
09387 void GetGCStatusLoop::UpdateGasMethodPageData(void)
09388 {
09389     GetGasPressure(GuiVar_gasMethodInitialPressure, false, false);
09390     GetInitialHoldTime(GuiVar_gasMethodInitialHold);
09391     if(gasMethodRampData == NULL) {
09392         gasMethodRampData = new GasMethodRampData(usbDevice, usbHostGC);
09393     }
09394     if(gasMethodRampData != NULL) {
09395         gasMethodRampData->GetRampDataFromGC();
09396         
09397         sprintf(GuiVar_gasMethodRampCount, "%u", gasMethodRampData->GetRampCount());
09398 
09399         if(gasMethodRampData->NeedToUpdateEasyGUIMethodPageRampVariables()) {
09400             gasMethodPageScrollIndex = 0;
09401             
09402             gasMethodRampData->UpdateEasyGUIMethodPageVariables(gasMethodPageScrollIndex);
09403             
09404             previousGasMethodPageScrollIndex = gasMethodPageScrollIndex;
09405         }
09406     }
09407 }
09408 
09409 
09410 /*
09411     This function is called when the Ethernet handler has signaled us that it has 
09412     received a message from the Ethernet client. This function gets the message 
09413     from the Ethernet handler, passes it on to the GC, then passes the response back to the handler,
09414     to be passed back to the client. 
09415     
09416     Note that this function does no other processing on the message - 
09417     we simply assume that the message must be a GC command without looking at it.
09418 */
09419 void GetGCStatusLoop::HandleEthernetMessage(Thread* ethernetThread, int debugValue)
09420 {
09421     // TODO: What if 'handlingEthernetMessage' is already true??
09422     
09423     char gcCommand[256];
09424     char gcResponse[GC_MESSAGE_LENGTH + 2];
09425     int responseReadyThreadSignalCode;
09426 
09427     handlingEthernetMessage = true;
09428     
09429     EthernetHandler::GetGCCommand(gcCommand, &responseReadyThreadSignalCode);
09430     
09431     ethernetThread->signal_set(GOT_GC_COMMAND);       
09432     
09433 //#define ALLOW_DEBUG_PRINTS_HERE
09434 #ifdef ALLOW_DEBUG_PRINTS_HERE
09435     char buff[300];
09436     sprintf(buff, "[%d] Command sent to GC: \"%s\"", debugValue, gcCommand);
09437     SpecialDebugPrint(buff, 50, 50);
09438 #endif // ALLOW_DEBUG_PRINTS_HERE
09439     
09440 #ifdef USE_LED_FOR_DEBUGGING
09441     SetLed3(true);
09442 #endif
09443 
09444     gcResponseTimer.reset();
09445     gcResponseTimer.start();
09446     
09447     SetGCDeviceReport(gcCommand, gcResponse);
09448 
09449     gcResponseTimer.stop();
09450     
09451 #ifdef USE_LED_FOR_DEBUGGING
09452     SetLed3(false);
09453 #endif
09454     
09455     // Turn on LED 2 while we are sending a method to the GC
09456     
09457     if(IsStartOfMethodCommand(gcCommand)) {
09458         // We have just started sending a method to the GC
09459 #ifdef USE_LED_FOR_DEBUGGING
09460         SetLed2(true);
09461 #endif
09462 
09463 #ifdef DO_NOTHING_ELSE_WHILE_SENDING_METHOD
09464         sendingMethod = true;
09465         // This is not set true anywhere else - we only need this '#ifdef' here
09466 #endif // DO_NOTHING_ELSE_WHILE_SENDING_METHOD
09467         
09468         DisplayDownloadingMethodPage();
09469     }
09470         
09471     if(IsEndOfMethodCommand(gcCommand)) {
09472         // We have just finished sending a method to the GC
09473 #ifdef USE_LED_FOR_DEBUGGING
09474         SetLed2(false);
09475 #endif        
09476         sendingMethod = false;
09477         
09478         UndisplayDownloadingMethodPage();
09479 
09480         //needToUpdateProfileGraphs = true;
09481         // Just do this here - don't wait - make sure graphs are updated 
09482         // *before* the user sees them
09483         SetupColumnAndInjectorAndGasProfileData();
09484         
09485         UpdateColumnMethodPageData();    
09486         UpdateInjectorMethodPageData();    
09487         UpdateGasMethodPageData();    
09488     }
09489     
09490     if(SuccessfulRunCommand(gcCommand, gcResponse)) {
09491         SetupForStartOfRun(usbDevice, usbHostGC);
09492     }
09493     
09494     if(SuccessfulAbortCommand(gcCommand, gcResponse)) {
09495         runWasAborted = true;
09496     }
09497         
09498 #ifdef ALLOW_DEBUG_PRINTS_HERE
09499     sprintf(buff, "[%d] Response received from GC: \"%s\"", debugValue, gcResponse);
09500     SpecialDebugPrint(buff, 50, 100);
09501 
09502     if((gcResponse[1] == gcCommand[1]) && (gcResponse[2] == gcCommand[2]) && (gcResponse[3] == gcCommand[3])) {
09503         SpecialDebugPrint("Command and response match", 50, 120);
09504     } else {
09505         SpecialDebugPrint("Command and response *** do not match ***", 50, 120);
09506     }
09507     
09508     sprintf(buff, "[%d] Time taken by GC: %d ms", debugValue, gcResponseTimer.read_ms());
09509     SpecialDebugPrint(buff, 50, 140);    
09510 #undef ALLOW_DEBUG_PRINTS_HERE
09511 #endif // ALLOW_DEBUG_PRINTS_HERE
09512 
09513     // TODO: do this immediately after 'SetGCDeviceReport' above?
09514     
09515     EthernetHandler::SetGCResponse(gcResponse);
09516     
09517     ethernetThread->signal_set(responseReadyThreadSignalCode);       
09518     
09519     handlingEthernetMessage = false;
09520 }
09521 
09522 /*
09523     Attempt to speed up main thread's response to the Ethernet thread - 
09524     perform a short wait for Ethernet transactions at multiple points in the main loop.
09525 */
09526 void GetGCStatusLoop::ShortEthernetThreadWait(Thread* ethernetThread, int debugValue)
09527 {
09528     // Try and keep comms in sync
09529     if(!handlingEthernetMessage) {
09530         
09531         Thread::signal_wait(GC_COMMAND_READY, shortWaitTimeMs); // Wait for GC commands over Ethernet link, nothing else
09532     
09533         if(EthernetHandler::GCCommandReceived()) {
09534     
09535             EasyGUIDebugPrintWithCounter("*** Ethernet command received ***", 125, 405);
09536     
09537             HandleEthernetMessage(ethernetThread, debugValue);
09538         }
09539         // else must have timed out - do nothing
09540     }
09541 }
09542 
09543 /*
09544     Attempt to speed up main thread's response to touch events - 
09545     perform a short wait for signals from the touch thread at multiple points in the main loop.
09546 */
09547 void GetGCStatusLoop::ShortTouchThreadWait(SimplifiedTouchListener* stl, int debugValue)
09548 {
09549     if(!handlingTouchEvent) {
09550         
09551         Thread::signal_wait(TOUCH_EVENT, shortWaitTimeMs); // Wait for touch events (signaled by SimplifiedTouchListener 
09552                                                            // on touch thread), nothing else
09553         short x, y, z;
09554         if(stl->GotTouchEvent(&x, &y, &z)) {
09555 
09556 //#define ALLOW_DEBUG_PRINTS_HERE
09557 #ifdef ALLOW_DEBUG_PRINTS_HERE
09558             char buff[300];
09559             sprintf(buff, "[%d] Got touch event", debugValue);
09560             SpecialDebugPrint(buff, 400, 80);
09561 #endif // ALLOW_DEBUG_PRINTS_HERE
09562 
09563             HandleTouchEvent(x, y, z, debugValue);
09564         }
09565         // else must have timed out - do nothing
09566     }
09567 }
09568 
09569 
09570 /*
09571     Attempt to speed up main thread's response to the Touch and Ethernet threads - 
09572     perform a short wait for signals from any thread at multiple points in the main loop.
09573 */
09574 void GetGCStatusLoop::ShortThreadWait(SimplifiedTouchListener* stl, Thread* ethernetThread, int debugValue)
09575 {
09576     osEvent signalWaitRetcode = Thread::signal_wait(0, shortWaitTimeMs); // Wait for any signal
09577     
09578 //#define ALLOW_DEBUG_PRINTS_HERE
09579 #ifdef ALLOW_DEBUG_PRINTS_HERE
09580     if(signalWaitRetcode.value.signals != 0) {
09581         char buff[300];
09582         sprintf(buff, "Short Thread::signal_wait returned %X", signalWaitRetcode.value.signals);
09583         SpecialDebugPrint(buff, 400, 50);
09584     }
09585 #undef ALLOW_DEBUG_PRINTS_HERE
09586 #endif // ALLOW_DEBUG_PRINTS_HERE
09587     
09588     switch(signalWaitRetcode.value.signals) {
09589         case TOUCH_EVENT:
09590             if(!handlingTouchEvent) {
09591                 
09592                 short x, y, z;
09593                 if(stl->GotTouchEvent(&x, &y, &z)) {
09594                     
09595 //#define ALLOW_DEBUG_PRINTS_HERE
09596 #ifdef ALLOW_DEBUG_PRINTS_HERE
09597                     char buff[300];
09598                     sprintf(buff, "[%d] Got touch event", debugValue);
09599                     SpecialDebugPrint(buff, 400, 80);
09600 #undef ALLOW_DEBUG_PRINTS_HERE
09601 #endif // ALLOW_DEBUG_PRINTS_HERE
09602 
09603                     HandleTouchEvent(x, y, z, debugValue);
09604                 }
09605             }
09606             break;        
09607                             
09608         case GC_COMMAND_READY:
09609             // Try and keep comms in sync
09610             if(!handlingEthernetMessage) {
09611                 
09612                 if(EthernetHandler::GCCommandReceived()) {
09613             
09614 //#define ALLOW_DEBUG_PRINTS_HERE
09615 #ifdef ALLOW_DEBUG_PRINTS_HERE
09616                     char buff[300];
09617                     sprintf(buff, "[%d] Got Ethernet event", debugValue);
09618                     SpecialDebugPrint(buff, 400, 80);
09619 #undef ALLOW_DEBUG_PRINTS_HERE
09620 #endif // ALLOW_DEBUG_PRINTS_HERE
09621 
09622                     HandleEthernetMessage(ethernetThread, debugValue);
09623                 }
09624                 // else must have timed out - do nothing
09625             }
09626             break;     
09627             
09628         case STARTED_DOWNLOADING_METHOD: 
09629             // Ethernet thread has signalled us that we have just started sending a method to the GC
09630 #ifdef USE_LED_FOR_DEBUGGING
09631             // Turn on LED 2 while we are sending a method to the GC
09632             SetLed2(true);
09633 #endif
09634 
09635 #ifdef DO_NOTHING_ELSE_WHILE_SENDING_METHOD
09636             sendingMethod = true;
09637             // This is not set true anywhere else - we only need this '#ifdef' here
09638 #endif // DO_NOTHING_ELSE_WHILE_SENDING_METHOD
09639         
09640             DisplayDownloadingMethodPage();
09641             break;   
09642                             
09643         case FINISHED_DOWNLOADING_METHOD:
09644             // Ethernet thread has signalled us that we have just finished sending a method to the GC
09645 #ifdef USE_LED_FOR_DEBUGGING
09646             SetLed2(false);
09647 #endif        
09648             sendingMethod = false;
09649             
09650             UndisplayDownloadingMethodPage();
09651     
09652             //needToUpdateProfileGraphs = true;
09653             // Just do this here - don't wait - make sure graphs are updated 
09654             // *before* the user sees them
09655             SetupColumnAndInjectorAndGasProfileData();
09656         
09657             UpdateColumnMethodPageData();
09658             UpdateInjectorMethodPageData();
09659             UpdateGasMethodPageData();
09660             break;   
09661             
09662         case CRUN_COMMAND_SENT:
09663             SetupForStartOfRun(usbDevice, usbHostGC);
09664             break;
09665                             
09666         case CHON_COMMAND_SENT:
09667             DisplayCurrentPageData(true);
09668             break;
09669                             
09670         case CHOF_COMMAND_SENT:
09671             if(realGCIsRunning) {
09672                 runWasAborted = true;
09673             } else {
09674                 DisplayCurrentPageData(true);
09675             }
09676             break;
09677                          
09678         case ABORT_RUN_COMMAND_SENT:
09679             if(realGCIsRunning) {
09680                 runWasAborted = true;
09681             }   
09682 
09683         default: // Unknown signal or timeout - ignore
09684             break;
09685     }       
09686 }
09687 
09688 
09689 /*
09690     Find out and display the GC's current status
09691 */
09692 void GetGCStatusLoop::PollGC(SimplifiedTouchListener* stl, Thread* ethernetThread)
09693 {
09694     char statusString[100];
09695     
09696     if(GCIsInStandbyMode()) {
09697         if(!gcInStandbyMode) {
09698             gcInStandbyMode = true;
09699             
09700             DisplayStandbyModePage();
09701         }
09702     }
09703     
09704     //ShortEthernetThreadWait(ethernetThread, 2);
09705     //ShortTouchThreadWait(stl, 2);
09706     ShortThreadWait(stl, ethernetThread, 2);
09707     
09708     // Deal with GC status
09709     int gcStatus = GetGCStatus();
09710     if(GCHasFaulted(gcStatus, statusString)) {
09711         if(realGCIsRunning) {
09712             
09713             // GC has faulted during a run
09714             
09715             realGCIsRunning = false;
09716         
09717 #ifdef SERVICE_INTERVALS_ACTIVE
09718             ServiceInterval::TellAllServiceIntervalsInstrumentHasCycled();
09719             // Some components need servicing based on how many cycles the instrument has performed
09720             // (others are time-based, i.e. every twelve months)
09721             if(ServiceInterval::AtLeastOneServiceIntervalHasExpired()) {
09722                 DisplayServicingRequiredPage();
09723             }
09724 #endif // SERVICE_INTERVALS_ACTIVE
09725         }
09726         
09727         //ShortEthernetThreadWait(ethernetThread, 3);
09728         //ShortTouchThreadWait(stl, 3);
09729         ShortThreadWait(stl, ethernetThread, 3);
09730 
09731 
09732         if((currentPage != GuiStruct_GCInFaultStatePage_11)
09733         || (strcmp(GuiVar_gcState, statusString) != 0)) {
09734             strcpy(GuiVar_gcState, statusString);
09735             
09736             DisplayGCInFaultStatePage(currentPage != GuiStruct_GCInFaultStatePage_11);
09737         }
09738 
09739         //ShortEthernetThreadWait(ethernetThread, 4);
09740         //ShortTouchThreadWait(stl, 4);
09741         ShortThreadWait(stl, ethernetThread, 4);
09742         
09743 
09744     } else { // GC has not faulted
09745 
09746         if(currentPage == GuiStruct_GCInFaultStatePage_11) {
09747             
09748             // No longer in fault state
09749             
09750             currentPage = GuiStruct_HomePage_1;
09751             
09752             lastSimplifiedGCState = GC_FAULTED;
09753             
09754             // The code below will now display the home page
09755             
09756             // ...but first, we need to do this - without it, the red rectangle (showing the error state)
09757             // is left in the background when we display the home page
09758 #ifdef USING_BACKGROUND_BITMAP
09759             DrawBackgroundBitmap(); 
09760 #else
09761             GuiLib_Clear();
09762 #endif
09763         }
09764 
09765         //ShortEthernetThreadWait(ethernetThread, 5);
09766         //ShortTouchThreadWait(stl, 5);
09767         ShortThreadWait(stl, ethernetThread, 5);
09768     
09769         // Bug #4 fix - make sure GC status is up to date - we may have done something
09770         // to change it in 'ShortThreadWait'
09771         gcStatus = GetGCStatus();
09772 
09773         GCStateSimplified simplifiedGCState = GCStateOrFaultCode::GetSimplifiedGCState(gcStatus);
09774         
09775         char buff[200];
09776         sprintf(buff, "simplifiedGCState is: %d", simplifiedGCState);
09777         EasyGUIDebugPrintWithCounter(buff, 400, 300);
09778         
09779 #ifdef UPDATE_PAGES_CONTINUOUSLY
09780         if(!handlingEthernetMessage) { // Try and keep comms in sync
09781 #else
09782         if(simplifiedGCState != lastSimplifiedGCState) {
09783 #endif // UPDATE_PAGES_CONTINUOUSLY           
09784 
09785             // Re-display all pages that display a status (heat on/off, column ready/equilibrating, etc)
09786             // - i.e. a coloured status rectangle, and/or descriptive status text
09787             if(PageIncludesComponentStatus(currentPage)) {
09788                 DisplayCurrentPageData(true);
09789             }
09790             /*
09791             if(currentPage == GuiStruct_HomePage_1) {
09792                 DisplayHomePageData(true);
09793             } else if(currentPage == GuiStruct_ColumnPage1_2) {
09794                 DisplayColumnPageData(true, CONVENTIONAL_COLUMN, GuiStruct_ColumnPage1_2);
09795             } else if(currentPage == GuiStruct_ColumnDHPage1_40) {
09796                 DisplayColumnPageData(true, DIRECTLY_HEATED_COLUMN, GuiStruct_ColumnDHPage1_40);
09797             } else if(currentPage == GuiStruct_InjectorPage1_3) {
09798                 DisplayInjectorPageData(true);
09799             }
09800             */
09801         }
09802         
09803         if((simplifiedGCState == GC_RUNNING) && (!realGCIsRunning)) {
09804             // GC has started running without our knowledge 
09805             // (e.g. it can be started by a hardware trigger)
09806             SetupForStartOfRun(usbDevice, usbHostGC);
09807 //#define BUG_4_ALLOW_DEBUG_PRINTS_HERE
09808 #ifdef BUG_4_ALLOW_DEBUG_PRINTS_HERE
09809             SpecialDebugPrint("WOK run started", 150, 460);
09810             // WOK = "without our knowledge" -
09811             // mistakenly executing this code after the run has been aborted
09812             // causes bug #4 - but *why* is it executed?
09813             // Ans - because we get the GC status early on in this function,
09814             // and we call 'ShortThreadWait' several times between there and here.
09815             // If a 'ShortThreadWait' sees a touch on ABORT_RUN_YES, we will stop 
09816             // the run *after* getting the status in this function, and 
09817             // 'simplifiedGCState' will be out of date. In this case, we will see bug #4...
09818 #undef BUG_4_ALLOW_DEBUG_PRINTS_HERE
09819 #endif // BUG_4_ALLOW_DEBUG_PRINTS_HERE
09820         }
09821 
09822         lastSimplifiedGCState = simplifiedGCState;
09823     }
09824     // End of dealing with GC status
09825     
09826     //ShortEthernetThreadWait(ethernetThread, 6);
09827     //ShortTouchThreadWait(stl, 6);
09828     ShortThreadWait(stl, ethernetThread, 6);
09829     
09830     if(pageJustChanged) {
09831         // Don't display page data if it has just been done - leave till next time - 
09832         // (a) it's unnecessary, (b) it appears to cause random crashes
09833         pageJustChanged = false;
09834     } else {
09835         DisplayCurrentPageData(false);
09836     }
09837     
09838     //ShortEthernetThreadWait(ethernetThread, 7);
09839     //ShortTouchThreadWait(stl, 7);
09840     ShortThreadWait(stl, ethernetThread, 7);
09841 }
09842 
09843     
09844 /*
09845     Deal with the user interface and the Ethernet interface - both are signaled to us by other threads,
09846     so we need to use the same Thread::signal_wait for both.
09847     
09848     One of the most important functions in this application.
09849     ********************************************************
09850 */
09851 void GetGCStatusLoop::HandleUserInterfaceAndEthernet(SimplifiedTouchListener* stl, Thread* ethernetThread)
09852 {
09853 #ifdef USE_THREAD_WAIT
09854 //        Thread::wait(waitTimeMs); // Let other things happen
09855 //        Thread::signal_wait(TOUCH_EVENT, waitTimeMs);
09856     osEvent signalWaitRetcode;
09857     
09858     if(sendingMethod) {
09859         // Try and make sending a method as fast as possible - ignore other events 
09860         // signalWaitRetcode = Thread::signal_wait(GC_COMMAND_READY); // Wait for GC commands over Ethernet link, nothing else -
09861         //                                                            // and don't timeout
09862         // No - the Ethernet thread, not this thread, now handles the USB traffic with the GC -
09863         // so while the download is in progress, wait only for the signal the Ethernet thread sends us 
09864         // to tell us it has finished downloading the method
09865         signalWaitRetcode = Thread::signal_wait(FINISHED_DOWNLOADING_METHOD);
09866     } else {
09867         signalWaitRetcode = Thread::signal_wait(0, waitTimeMs); // Wait for any signal, not just touch events
09868     }
09869 //#define ALLOW_DEBUG_PRINTS_HERE
09870 #ifdef ALLOW_DEBUG_PRINTS_HERE
09871     if(signalWaitRetcode.value.signals != 0) {
09872         char buff[300];
09873         sprintf(buff, "Main Thread::signal_wait returned %X", signalWaitRetcode.value.signals);
09874         SpecialDebugPrint(buff, 400, 50);
09875     }
09876 #undef ALLOW_DEBUG_PRINTS_HERE
09877 #endif // ALLOW_DEBUG_PRINTS_HERE
09878     
09879     if(signalWaitRetcode.value.signals == TOUCH_EVENT) {
09880         
09881         if(!handlingTouchEvent) {
09882             short x, y, z;
09883             if(stl->GotTouchEvent(&x, &y, &z)) {
09884 //#define ALLOW_DEBUG_PRINTS_HERE
09885 #ifdef ALLOW_DEBUG_PRINTS_HERE
09886                 SpecialDebugPrint("[1] Got touch event", 400, 80);
09887 #endif // ALLOW_DEBUG_PRINTS_HERE
09888 
09889                 HandleTouchEvent(x, y, z, 1);
09890             }
09891         }
09892         
09893     } else if (signalWaitRetcode.value.signals == GC_COMMAND_READY) {
09894         if(EthernetHandler::GCCommandReceived()) {
09895         
09896             EasyGUIDebugPrintWithCounter("*** Ethernet command received ***", 125, 405);
09897     
09898             // Try and keep comms in sync
09899             if(!handlingEthernetMessage) {
09900                 HandleEthernetMessage(ethernetThread, 1);
09901             }
09902             
09903 #if defined MULTI_TOUCH_TECHNIQUE_3
09904             gotAtLeastOneTimeout = true; // i.e. 'one non touch event'
09905 #endif
09906         }
09907         
09908     } else if (signalWaitRetcode.value.signals == STARTED_DOWNLOADING_METHOD) {
09909         
09910         // Ethernet thread has signaled us that we have just started sending a method to the GC
09911 #ifdef USE_LED_FOR_DEBUGGING
09912         // Turn on LED 2 while we are sending a method to the GC
09913         SetLed2(true);
09914 #endif
09915 
09916 #ifdef DO_NOTHING_ELSE_WHILE_SENDING_METHOD
09917         sendingMethod = true;
09918         // This is not set true anywhere else - we only need this '#ifdef' here
09919 #endif // DO_NOTHING_ELSE_WHILE_SENDING_METHOD
09920         
09921         DisplayDownloadingMethodPage();
09922         
09923     } else if (signalWaitRetcode.value.signals == FINISHED_DOWNLOADING_METHOD) {
09924         
09925         // Ethernet thread has signaled us that we have just finished sending a method to the GC
09926 #ifdef USE_LED_FOR_DEBUGGING
09927         // Turn on LED 2 while we are sending a method to the GC
09928         SetLed2(false);
09929 #endif        
09930         sendingMethod = false;
09931 
09932         UndisplayDownloadingMethodPage();
09933         
09934         //needToUpdateProfileGraphs = true;
09935         // Just do this here - don't wait - make sure graphs are updated 
09936         // *before* the user sees them
09937         SetupColumnAndInjectorAndGasProfileData();
09938         // ...but note that we do this *after* un-displaying the downloading method page - 
09939         // otherwise there is an annoying lag between Ellution saying it has finished sending the method
09940         // and our showing our normal UI again (and we hope the user will not want to look
09941         // at the profiles immediately after the download finishes)
09942         
09943         UpdateColumnMethodPageData();
09944         UpdateInjectorMethodPageData();
09945         UpdateGasMethodPageData();
09946         
09947     } else if (signalWaitRetcode.value.signals == CRUN_COMMAND_SENT) {
09948         SetupForStartOfRun(usbDevice, usbHostGC);
09949     } else if (signalWaitRetcode.value.signals == CHON_COMMAND_SENT) {
09950         DisplayCurrentPageData(true);
09951     } else if (signalWaitRetcode.value.signals == CHOF_COMMAND_SENT) {
09952         if(realGCIsRunning) {
09953             runWasAborted = true;
09954         } else {
09955             DisplayCurrentPageData(true);
09956         }
09957     } else if (signalWaitRetcode.value.signals == ABORT_RUN_COMMAND_SENT) {
09958         if(realGCIsRunning) {
09959             runWasAborted = true;
09960         }   
09961     } else { // Assume Thread::signal_wait timed out
09962     
09963 #ifdef MULTI_TOUCH_TECHNIQUE_1
09964         EasyGUIDebugPrintWithCounter("*** Timer event or timeout ***", 125, 405);
09965 #else
09966         // Timer used only with MULTI_TOUCH_TECHNIQUE_1
09967         EasyGUIDebugPrintWithCounter("*** Timeout ***", 125, 405);
09968 #endif // MULTI_TOUCH_TECHNIQUE_1
09969 
09970 #if defined MULTI_TOUCH_TECHNIQUE_3
09971         gotAtLeastOneTimeout = true;
09972 #endif
09973         
09974         // While sending a method, do nothing else - otherwise method download is ridiculously slow
09975         //                         ***************
09976         if(!sendingMethod) {
09977             PollGC(stl, ethernetThread);
09978 //        }
09979 // Include everything else in this if - *really* do nothing else
09980 
09981             if(GCIsRunning()) {
09982     
09983                 // While the GC is running, all pages below need to be continuously updated -
09984                 // but do not force them to (re)display their data if it has not changed
09985                 if(currentPage == GuiStruct_RunningColumnPage_25) {
09986                     DisplayRunningColumnPageData(false, false);
09987                 } else if (currentPage == GuiStruct_RunningGasPage_28) {
09988                     DisplayRunningGasPageData(false, false);
09989                 } else if (currentPage == GuiStruct_RunningInjectorProfilePage_Def) {
09990                     DisplayRunningInjectorProfilePageData(false, false);
09991                 } else if (currentPage == GuiStruct_RunningPage1_7) {
09992                     DisplayRunningPageData(false, false);
09993                 }
09994                             
09995             } else if(realGCIsRunning) {
09996                 
09997                 // The GC has stopped running, but our "the GC is running" flag is still set - 
09998                 // either the run has ended normally, it has been aborted, or the GC has faulted
09999                 // (although now we should be dealing with the latter situation above,
10000                 // when we detect that the GC has faulted)
10001                 
10002                 if(!GCHasFaulted()) {
10003                     
10004                     if(runWasAborted) {
10005     
10006                         DisplayRunAbortedPage();
10007                         
10008                         // Prepare for next run...
10009                         runWasAborted = false;
10010     
10011                     } else {
10012     
10013                         // The run has ended normally - give the progress bar and method profiles a chance to show this to the user
10014                         if(currentPage == GuiStruct_RunningColumnPage_25) {
10015                             DisplayRunningColumnPageData(false, true);
10016                         } else if (currentPage == GuiStruct_RunningGasPage_28) {
10017                             DisplayRunningGasPageData(false, true);
10018                         } else if (currentPage == GuiStruct_RunningInjectorProfilePage_Def) {
10019                             DisplayRunningInjectorProfilePageData(false, true);
10020                         } else if (currentPage == GuiStruct_RunningPage1_7) {
10021                             DisplayRunningPageData(false, true);
10022                         }
10023                         
10024                         // Let the user briefly see the above before we display "Run Completed"
10025                         Thread::wait(500); // 0.5 second
10026     
10027                         DisplayRunCompletePage();
10028                         
10029                         // Want to display "Run Complete" for 5 seconds, then the Home page.
10030                         // Maybe this is all that is needed...
10031                         // Thread::wait(5000);
10032                         // DisplayHomePageData(true);
10033 
10034 
10035 //#define BUG_4_ALLOW_DEBUG_PRINTS_HERE
10036 #ifdef BUG_4_ALLOW_DEBUG_PRINTS_HERE
10037                         char bug4[100];
10038                         sprintf(bug4, "+++ realGCIsRunning %d +++", realGCIsRunning);
10039                         SpecialDebugPrint(bug4, 150, 440);
10040                         sprintf(bug4, "+++ Thread %X +++", osThreadGetId());
10041                         SpecialDebugPrint(bug4, 150, 460);
10042 #undef BUG_4_ALLOW_DEBUG_PRINTS_HERE
10043 #endif // BUG_4_ALLOW_DEBUG_PRINTS_HERE
10044                     }
10045                 }
10046                 // 'else' the GC has faulted while the run was in progress - we will display the 'GCInFaultStatePage' anyway
10047                 // (see the top of this function)
10048                 
10049                 // Now tidy up...
10050                 ClearGCIsRunning();
10051                 
10052 #ifdef SERVICE_INTERVALS_ACTIVE
10053                 ServiceInterval::TellAllServiceIntervalsInstrumentHasCycled();
10054                 // Some components need servicing based on how many cycles the instrument has performed
10055                 // (others are time-based, i.e. every twelve months)
10056                 if(ServiceInterval::AtLeastOneServiceIntervalHasExpired()) {
10057                     DisplayServicingRequiredPage();
10058                 }
10059 #endif // SERVICE_INTERVALS_ACTIVE
10060             }
10061         } // if (!sendingMethod)
10062     }
10063 #else // USE_THREAD_WAIT
10064     wait_ms(waitTimeMs); // Let other things happen
10065 #endif // USE_THREAD_WAIT
10066 
10067 }
10068 
10069 /*
10070     Along with TouchCallBack (main.cpp), this is one of the most important functions in this application.
10071     ****************************************************************************************************
10072     
10073     Once called, this function never returns - it enters a loop in which it sets up the Ethernet interface,
10074     which in turn enters an inner loop calling HandleUserInterfaceAndEthernet (see above) repeatedly. 
10075     This inner loop exits only if the Ethernet parameters have changed, 
10076     and we have to set up the Ethernet interface again.
10077 */
10078 void GetGCStatusLoop::MainLoopWithEthernet(DMBoard* board)
10079 {
10080     // Need to do this once only - it is nothing to do with the Ethernet interface
10081     SimplifiedTouchListener* stl = SimplifiedTouchListener::GetInstance(osThreadGetId(), board->touchPanel());
10082     
10083     Thread* ethernetThread;
10084     EthernetInterface eth;
10085     TCPSocketServer server;
10086     
10087     DisplayEthernetConnectionPage();
10088     
10089     EasyGUIDebugPrintWithCounter("Ethernet -2", 100, 450);
10090 //#define ALLOW_DEBUG_PRINTS_HERE
10091 #ifdef ALLOW_DEBUG_PRINTS_HERE
10092     char buff[300];
10093     sprintf(buff, "Ethernet -2 - stl %X", stl);
10094     SpecialDebugPrint(buff, 100, 400);
10095 #endif // ALLOW_DEBUG_PRINTS_HERE
10096     
10097     NetworkParameters *networkParameters = NetworkParameters::GetInstance(usbDevice, usbHostGC);
10098     if(networkParameters != NULL) {
10099         // We are assuming here that the NetworkParameters instance will have read its values
10100         // from QSPI memory
10101         ethernetPort = networkParameters->GetPortNumber();
10102         networkParameters->GetIPAddressAsString(ethernetIP);
10103         networkParameters->GetSubnetMaskAsString(ethernetMask);
10104         networkParameters->GetGatewayAddressAsString(ethernetGateway);
10105         useDHCPForEthernet = networkParameters->GetUseDHCP();
10106 //#define WANT_ETHERNET_DEBUG_2
10107 #ifdef WANT_ETHERNET_DEBUG_2
10108         char dbg[300];
10109         sprintf(dbg, "networkParameters IP: \"%s\", mask: \"%s\", gateway: \"%s\"", ethernetIP, ethernetMask, ethernetGateway);
10110         SpecialDebugPrint(dbg, 100, 20);
10111 #endif // WANT_ETHERNET_DEBUG_2
10112     }
10113             
10114     EasyGUIDebugPrintWithCounter("Ethernet -1", 100, 450);
10115 
10116 //#define WANT_ETHERNET_DEBUG
10117     if(useDHCPForEthernet) {
10118         eth.init(); // With no args, init function uses DHCP
10119 
10120 #ifdef WANT_ETHERNET_DEBUG
10121         char dbg[300];
10122         sprintf(dbg, "DHCP Ethernet IP: \"%s\", mask: \"%s\", gateway: \"%s\"", eth.getIPAddress(), eth.getNetworkMask(), eth.getGateway());
10123         SpecialDebugPrint(dbg, 100, 20);
10124 #endif // WANT_ETHERNET_DEBUG
10125         
10126     } else {
10127 
10128 #ifdef WANT_ETHERNET_DEBUG
10129         char dbg[300];
10130         sprintf(dbg, "Ethernet IP: \"%s\", mask: \"%s\", gateway: \"%s\"", ethernetIP, ethernetMask, ethernetGateway);
10131         SpecialDebugPrint(dbg, 100, 20);
10132 #undef WANT_ETHERNET_DEBUG
10133 #endif // WANT_ETHERNET_DEBUG
10134     
10135         eth.init(ethernetIP, ethernetMask, ethernetGateway); // Use addresses from our member variables
10136     }
10137     
10138     EasyGUIDebugPrintWithCounter("Ethernet -1 A", 100, 450);
10139     
10140     eth.connect();
10141     
10142     EasyGUIDebugPrintWithCounter("Ethernet -1 B", 100, 450);
10143     
10144     server.bind(ethernetPort);
10145     server.listen();
10146 
10147     EasyGUIDebugPrintWithCounter("Ethernet 0", 100, 450);
10148     
10149     // Set up Ethernet Handler, and start it in its own thread
10150     EthernetHandler::SetMainThreadId(osThreadGetId());
10151     EthernetHandler::SetEthernetServer(&server);
10152     EthernetHandler::SetUsbGC(usbDevice, usbHostGC);
10153 #define ETHERNET_THREAD_RAISE_PRIORITY
10154 #ifdef ETHERNET_THREAD_RAISE_PRIORITY
10155     ethernetThread = new Thread(EthernetHandler::HandlerFunction, NULL, osPriorityHigh);
10156 #undef ETHERNET_THREAD_RAISE_PRIORITY
10157 #else // Give the Ethernet thread normal priority
10158     ethernetThread = new Thread(EthernetHandler::HandlerFunction);
10159 #endif
10160     
10161     restartRequired = false;
10162 
10163     EasyGUIDebugPrintWithCounter("Ethernet 1", 100, 450);
10164 
10165     // We are no longer trying to establish the Ethernet connection
10166 #ifdef USING_BACKGROUND_BITMAP
10167     DrawBackgroundBitmap(); 
10168 #else
10169     GuiLib_Clear();
10170 #endif
10171 
10172     if((qspiBitmaps != NULL) && (qspiBitmaps->AllBitmapsLoaded())) {
10173         currentPage = GuiStruct_HomePage_1;
10174         DisplayCurrentPageData(true);
10175     } else {
10176         currentPage = GuiStruct_FailedToFindBitmapsPage_0;
10177         DisplayFailedToFindBitmapsPage();
10178     }
10179         
10180 //#define WANT_ETHERNET_DEBUG_3
10181     if(useDHCPForEthernet) {
10182 #ifdef WANT_ETHERNET_DEBUG_3
10183         char dbg[100];
10184         sprintf(dbg, "DHCP Ethernet IP: \"%s\", mask: \"%s\", gateway: \"%s\"", eth.getIPAddress(), eth.getNetworkMask(), eth.getGateway());
10185         SpecialDebugPrint(dbg, 100, 20);
10186 #endif // WANT_ETHERNET_DEBUG_3
10187 
10188         // Let the user see (in the Network Parameters page) the actual IP address, etc, we are using
10189         if(networkParameters != NULL) {
10190             networkParameters->SetIPAddressFromString(eth.getIPAddress());
10191             networkParameters->SetSubnetMaskFromString(eth.getNetworkMask());
10192             networkParameters->SetGatewayAddressFromString(eth.getGateway());
10193         }
10194 #ifdef WANT_ETHERNET_DEBUG_3
10195     } else {
10196         char dbg[600];
10197         int dbg1;
10198         char dbg2[100];
10199         char dbg3[100];
10200         char dbg4[100];
10201         int dbg5;
10202 
10203         dbg1 = networkParameters->GetPortNumber();
10204         networkParameters->GetIPAddressAsString(dbg2);
10205         networkParameters->GetSubnetMaskAsString(dbg3);
10206         networkParameters->GetGatewayAddressAsString(dbg4);
10207         dbg5 = networkParameters->GetUseDHCP();
10208         
10209         sprintf(dbg, "Non-DHCP : %d, IP: \"%s\", mask: \"%s\", gateway: \"%s\"", dbg1, dbg2, dbg3, dbg4);
10210         SpecialDebugPrint(dbg, 100, 20);
10211     }
10212 #else
10213     }
10214 #endif
10215 
10216     if(networkParameters != NULL) {
10217         networkParameters->SaveRealValues(&eth);
10218     }
10219     
10220     // We will need to restart (i.e. reboot) if the Ethernet parameters have changed - 
10221     // if you try and call the EthernetInterface init method again (which is the obvious way 
10222     // of doing this) it crashes
10223     while (!restartRequired) 
10224     {
10225         HandleUserInterfaceAndEthernet(stl, ethernetThread);
10226     }
10227     
10228     EasyGUIDebugPrintWithCounter("Ethernet 2", 100, 400);
10229 
10230     eth.disconnect();
10231     ethernetThread->terminate();
10232     delete ethernetThread;
10233     
10234     EasyGUIDebugPrintWithCounter("Ethernet 3", 100, 400);
10235     
10236     reboot(); // In main.cpp
10237 }
10238