Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: DMBasicGUI DMSupport
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(¤tColumnMethodRunTime); 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(¤tGasMethodRunTime); 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(¤tInjectorMethodRunTime); 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(¤tColumnMethodRunTime); 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(¤tColumnMethodRunTime); 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(¤tColumnMethodRunTime); 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(ð); 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
Generated on Tue Jul 19 2022 00:31:06 by
1.7.2