Repository for import to local machine
Dependencies: DMBasicGUI DMSupport
ColumnDHAutoCalibrationPageHandler.cpp
- Committer:
- jmitc91516
- Date:
- 2017-07-31
- Revision:
- 8:26e49e6955bd
- Parent:
- 1:a5258871b33d
File content as of revision 8:26e49e6955bd:
#include "ColumnDHAutoCalibrationPageHandler.h" #include "EasyGUITouchAreaIndices.h" #include "GetGCStatusLoop.h" #include "USBHostGCUtilities.h" #include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> /* Passed three 8-bit colour components - red, green and blue. Returns the corresponding 16-bit colour value (5 bits for red, 6 bits for green, 5 bits for blue). Defined in main.cpp */ extern GuiConst_INTCOLOR SixteenBitColorValue(GuiConst_INT8U red, GuiConst_INT8U green, GuiConst_INT8U blue); /* Allows us to tell the world not to unlock the door actuators while we are calibrating. Defined in main.cpp */ extern void CanUnlockDoorActuators(bool value); /* Displays the specified text string at the specified location in the currently-displayed easyGUI page. Defined in main.cpp - and omits the 'ALLOW_DEBUG_PRINTS' #define */ extern void SpecialDebugPrint(char *stuffToPrint, GuiConst_INT16S X, GuiConst_INT16S Y); /* Draws the background bitmap - without the Ellutia logo. It fills the screen, so you do not need to call GuiLib_Clear. Defined in main.cpp */ extern void DrawBackgroundBitmap(void); #define USING_BACKGROUND_BITMAP /* Note that this class is a singleton - we do not need or want there to be more than one instance of it (we do not want multiple values for the calibration point selection, etc, and nor will we show more than one easyGUI 'ColumnDHAutoCalibrationPage' to the user at the same time). */ ColumnDHAutoCalibrationPageHandler * ColumnDHAutoCalibrationPageHandler::theColumnDHAutoCalibrationPageHandlerInstance = NULL; /* String saying "we do not yet know this value" */ const char* ColumnDHAutoCalibrationPageHandler::notAvailable = "-"; /* Singleton class - return the one and only instance, first creating it if necessary. */ ColumnDHAutoCalibrationPageHandler * ColumnDHAutoCalibrationPageHandler::GetInstance(USBDeviceConnected* newUsbDevice, USBHostGC* newUsbHostGC) { if (theColumnDHAutoCalibrationPageHandlerInstance == NULL) { theColumnDHAutoCalibrationPageHandlerInstance = new ColumnDHAutoCalibrationPageHandler(newUsbDevice, newUsbHostGC); } return theColumnDHAutoCalibrationPageHandlerInstance; } /* Overriden version of the above, that does not take any arguments and does not create the instance if it does not already exist. Provided for callers that do not have the 'usbDevice' and'usbHostGC' pointers, and just want access to the instance if it exists */ ColumnDHAutoCalibrationPageHandler * ColumnDHAutoCalibrationPageHandler::GetInstance(void) { return theColumnDHAutoCalibrationPageHandlerInstance; } // Singleton class - private constructor ColumnDHAutoCalibrationPageHandler::ColumnDHAutoCalibrationPageHandler(USBDeviceConnected* newUsbDevice, USBHostGC* newUsbHostGC) { usbDevice = newUsbDevice; usbHostGC = newUsbHostGC; strcpy(GuiVar_columnDHAutoCalibCalibrateButtonText, "Calibrate"); for (int index = 0; index < 3; ++index) { calibTemperature[index] = GetColumnTemperaturePointFromGC(index + 1); calibResistance[index] = GetColumnResistancePointFromGC(index + 1); } currentCalibrationState = GetCalibrationState(); previousCalibrationState = INVALID; SetEasyGUICalibrationState(); ShowSetCalibrationButtonEnabledState(); SetEasyGUIHeatState(); SetEasyGUIOvenTemperature(); stabilisationTime = GetStabilisationTimeFromGC(); actualColumnResistance = -1.0f; needToGetColumnResistance = false; #ifndef USE_STABILISATION_TIMER gcTimeStabilisationStarted = -1.0f; #endif #ifdef USE_ENABLE_SET_BUTTON_FLAG enableSetButton = false; #endif } // Private destructor also ColumnDHAutoCalibrationPageHandler::~ColumnDHAutoCalibrationPageHandler() { } /* Tells the caller whether or not the specified touch index is on the easyGUI 'ColumnDHAutoCalibrationPage', and therefore needs to be handled by this class. Args: the touch area index in question Return code: true if the touch area is 'one of ours', false if not */ bool ColumnDHAutoCalibrationPageHandler::TouchAreaIsOnCalibrationPage(int touchAreaIndex) { if((touchAreaIndex >= MIN_COLUMN_DH_AUTO_CALIB_TOUCHINDEX) && (touchAreaIndex <= MAX_COLUMN_DH_AUTO_CALIB_TOUCHINDEX)) { return true; } // 'else' return false; } /* If the specified touch area represents a key or field on the easyGUI 'ColumnDHCalibrationPage', this function performs whatever action is appropriate for it. Provided so that the caller can (in effect) say "if this is one of yours, deal with it", without needing to know anything about what this class does, or how it handles the touch areas on that easyGUI page. Args: the touch area index in question Returns true if it dealt with the touch area (so the caller need not do anything else with the value), or false if it did not deal with the touch area (implying that it was not a touch area on the easyGUI 'Directly Heated Column Auto CalibrationPage', and so the caller must deal with it some other way). */ bool ColumnDHAutoCalibrationPageHandler::DealWithTouch(int touchAreaIndex) { bool dealtWithTouch = false; // Don't allow the user to select a different calibration point, // or change any calibration parameters, while we are calibrating the current point switch(touchAreaIndex) { case COLUMN_DH_AUTO_CALIB_SELECT_POINT_1: if(!CalibratingNow()) { if(GuiVar_columnDHAutoCalibPointSelection != 0) { GuiVar_columnDHAutoCalibPointSelection = 0; calibTemperature[GuiVar_columnDHAutoCalibPointSelection] = GetColumnTemperaturePointFromGC(GuiVar_columnDHAutoCalibPointSelection + 1); SetEasyGUICalibrationPointTemperatureFromInternalValue(true); calibResistance[GuiVar_columnDHAutoCalibPointSelection] = GetColumnResistancePointFromGC(GuiVar_columnDHAutoCalibPointSelection + 1); SetEasyGUICalibrationPointResistanceFromInternalValue(); #ifdef USE_ENABLE_SET_BUTTON_FLAG enableSetButton = false; #endif UpdateEasyGUIPage(); } } dealtWithTouch = true; break; case COLUMN_DH_AUTO_CALIB_SELECT_POINT_2: if(!CalibratingNow()) { if(GuiVar_columnDHAutoCalibPointSelection != 1) { GuiVar_columnDHAutoCalibPointSelection = 1; calibTemperature[GuiVar_columnDHAutoCalibPointSelection] = GetColumnTemperaturePointFromGC(GuiVar_columnDHAutoCalibPointSelection + 1); SetEasyGUICalibrationPointTemperatureFromInternalValue(true); calibResistance[GuiVar_columnDHAutoCalibPointSelection] = GetColumnResistancePointFromGC(GuiVar_columnDHAutoCalibPointSelection + 1); SetEasyGUICalibrationPointResistanceFromInternalValue(); #ifdef USE_ENABLE_SET_BUTTON_FLAG enableSetButton = false; #endif UpdateEasyGUIPage(); } } dealtWithTouch = true; break; case COLUMN_DH_AUTO_CALIB_SELECT_POINT_3: if(!CalibratingNow()) { if(GuiVar_columnDHAutoCalibPointSelection != 2) { GuiVar_columnDHAutoCalibPointSelection = 2; calibTemperature[GuiVar_columnDHAutoCalibPointSelection] = GetColumnTemperaturePointFromGC(GuiVar_columnDHAutoCalibPointSelection + 1); SetEasyGUICalibrationPointTemperatureFromInternalValue(true); calibResistance[GuiVar_columnDHAutoCalibPointSelection] = GetColumnResistancePointFromGC(GuiVar_columnDHAutoCalibPointSelection + 1); SetEasyGUICalibrationPointResistanceFromInternalValue(); #ifdef USE_ENABLE_SET_BUTTON_FLAG enableSetButton = false; #endif UpdateEasyGUIPage(); } } dealtWithTouch = true; break; case COLUMN_DH_AUTO_CALIB_INC_STABILISATION_TIME: if(!CalibratingNow()) { ++stabilisationTime; SetEasyGUIStabilisationTimeFromInternalValue(); UpdateEasyGUIPage(); } dealtWithTouch = true; break; case COLUMN_DH_AUTO_CALIB_DEC_STABILISATION_TIME: if(!CalibratingNow()) { if(stabilisationTime > 0) { --stabilisationTime; SetEasyGUIStabilisationTimeFromInternalValue(); UpdateEasyGUIPage(); } } dealtWithTouch = true; break; case COLUMN_DH_AUTO_CALIB_GET_STABILISATION_TIME: if(!CalibratingNow()) { stabilisationTime = GetStabilisationTimeFromGC(); SetEasyGUIStabilisationTimeFromInternalValue(); UpdateEasyGUIPage(); } dealtWithTouch = true; break; case COLUMN_DH_AUTO_CALIB_SET_STABILISATION_TIME: if(!CalibratingNow()) { SetStabilisationTimeInGC(stabilisationTime); } dealtWithTouch = true; break; case COLUMN_DH_AUTO_CALIB_INC_CALIB_TEMP: if(!CalibratingNow()) { calibTemperature[GuiVar_columnDHAutoCalibPointSelection] += 1.0f; SetEasyGUICalibrationPointTemperatureFromInternalValue(false); UpdateEasyGUIPage(); } dealtWithTouch = true; break; case COLUMN_DH_AUTO_CALIB_DEC_CALIB_TEMP: if(!CalibratingNow()) { if(calibTemperature[GuiVar_columnDHAutoCalibPointSelection] >= 1.0f) { calibTemperature[GuiVar_columnDHAutoCalibPointSelection] -= 1.0f; SetEasyGUICalibrationPointTemperatureFromInternalValue(false); UpdateEasyGUIPage(); } } dealtWithTouch = true; break; case COLUMN_DH_AUTO_CALIB_START_CALIBRATION: if(CalibratingNow()) { // Need to stop calibrating char commandBuffer[30]; ConstructStopCalibrationCommand(commandBuffer); if(SendCommandToGCWithDACKResponse(commandBuffer)) { // Stop calibration succeeded currentCalibrationState = IDLE; needToGetColumnResistance = false; strcpy(GuiVar_columnDHAutoCalibCalibrateButtonText, "Calibrate"); #ifdef USE_ENABLE_SET_BUTTON_FLAG enableSetButton = false; #endif // Let user change pages again TouchPanelPageSelector::SetPageChangeEnabled(true); } } else { // Need to start calibrating char commandBuffer[30]; ConstructStartCalibrationCommand(commandBuffer); if(SendCommandToGCWithDACKResponse(commandBuffer)) { // Start calibration succeeded currentCalibrationState = GetCalibrationState(); previousCalibrationState = INVALID; // Make explicit - we have started a new calibration operation // (do not leave STAGE_3 as the previous calibration state) // Make sure the stabilisation time is the same as in the GC - // i.e. that it matches the value the GC is actually using stabilisationTime = GetStabilisationTimeFromGC(); SetEasyGUIStabilisationTimeFromInternalValue(); // Make sure we have a valid stabilisation start time, // in case we somehow skip stage 1 and go straight to stage 1A // (see function SetEasyGUICalibrationTimeRemaining()) gcTimeStabilisationStarted = GetTimeFromGC(); strcpy(GuiVar_columnDHAutoCalibCalibrateButtonText, "Stop"); needToGetColumnResistance = true; #ifdef USE_ENABLE_SET_BUTTON_FLAG enableSetButton = false; #endif // Do not allow user to change pages while we are calibrating TouchPanelPageSelector::SetPageChangeEnabled(false); } } UpdateEasyGUIPage(); dealtWithTouch = true; break; case COLUMN_DH_AUTO_CALIB_SET_CURRENT_CALIBRATION: #ifdef USE_ENABLE_SET_BUTTON_FLAG if(enableSetButton) { #else // 'Set' is valid during *** and after *** stage 3 if((currentCalibrationState == STAGE_3) || (previousCalibrationState == STAGE_3)) { #endif // TODO: Use the 'new' values shown to the user on the easyGUI page, // which we got from the GC at the end of calibration stage 3. // These are in: // GuiVar_columnDHAutoCalibTemperatureToSet for temperature // GuiVar_columnDHAutoCalibResistance for resistance // DON'T get the current values from the GC - they will have changed since // the calibration process ended float temp; sscanf(GuiVar_columnDHAutoCalibTemperatureToSet, "%f", &temp); SetColumnTemperaturePointInGC(GuiVar_columnDHAutoCalibPointSelection + 1, temp); sscanf(GuiVar_columnDHAutoCalibResistance, "%f", &temp); SetColumnResistancePointInGC(GuiVar_columnDHAutoCalibPointSelection + 1, temp); // Update the user interface to show the new resistance value for the current calibration point calibResistance[GuiVar_columnDHAutoCalibPointSelection] = GetColumnResistancePointFromGC(GuiVar_columnDHAutoCalibPointSelection + 1); SetEasyGUICalibrationPointResistanceFromInternalValue(); // ...and to show the new temperature value calibTemperature[GuiVar_columnDHAutoCalibPointSelection] = GetColumnTemperaturePointFromGC(GuiVar_columnDHAutoCalibPointSelection + 1); SetEasyGUICalibrationPointTemperatureFromInternalValue(true); #ifdef USE_ENABLE_SET_BUTTON_FLAG enableSetButton = false; #endif UpdateEasyGUIPage(); } // 'else'- ignore break; default: // Not recognised - do nothing break; } return dealtWithTouch; } bool ColumnDHAutoCalibrationPageHandler::CalibratingNow(void) { if((currentCalibrationState == INVALID) || (currentCalibrationState == IDLE)) { return false; } // 'else' return true; } /* Caller is telling us it is about to display the easyGUI 'ColumnDHAutoCalibrationPage', and that we should do whatever we have to do to get it ready, before the caller displays it. No arguments, no return code */ void ColumnDHAutoCalibrationPageHandler::DisplayingEasyGUIPage(void) { strcpy(GuiVar_columnDHAutoCalibStabilisationTimeRemaining, notAvailable); SetEasyGUICalibrationPointTemperatureFromInternalValue(false); SetEasyGUIStabilisationTimeFromInternalValue(); UpdateVolatileEasyGUIVariables(); } /* (Re)display the easyGUI 'ColumnDHAutoCalibrationPage' - e.g. after the caller has updated one or more of the easyGUI variables included in the page, and wants to display the new value to the user. No arguments, no return code */ void ColumnDHAutoCalibrationPageHandler::UpdateEasyGUIPage(void) { GetGCStatusLoop *getGCStatusLoop = GetGCStatusLoop::GetInstance(); if(getGCStatusLoop != NULL) { // The Gas Calibration page has a status rectangle for the gas - // GetGCStatusLoop can display this, we cannot getGCStatusLoop->ForceUpdateOfColumnDHAutoCalibrationPage(); } } /* Update the easyGUI variable that displays the calibration temperature to match the current value stored in our own internal variable. Note that there are two - one always displays the value stored in the GC, the other displays the value the user wants to set. Provided so that we do this consistently everywhere. */ void ColumnDHAutoCalibrationPageHandler::SetEasyGUICalibrationPointTemperatureFromInternalValue(bool updateBoth) { // We display only the integer part of the temperature - pointless displaying the fractional part // if we only allow the user to change the value in increments of 1.0 (i.e. we give them // no way to change the fractional part) float temp = floor(calibTemperature[GuiVar_columnDHAutoCalibPointSelection] + 0.5f); sprintf(GuiVar_columnDHAutoCalibTemperatureToSet, "%1.0f", temp); if(updateBoth) { // But we do display the temperature obtained from the GC, to one decimal place // (since the GC returns it in that format) sprintf(GuiVar_columnDHAutoCalibCurrentTemp, "%1.1f", calibTemperature[GuiVar_columnDHAutoCalibPointSelection]); } } /* Update the easyGUI variable that displays the calibration point resistance to match the current value stored in our own internal variable. Also update the 'actual' resistance to show 'not available', since we have not calibrated yet. Intended to be called when the user selects a different calibration point. Provided so that we do this consistently everywhere. */ void ColumnDHAutoCalibrationPageHandler::SetEasyGUICalibrationPointResistanceFromInternalValue(void) { if(calibResistance[GuiVar_columnDHAutoCalibPointSelection] >= 0.0f) { sprintf(GuiVar_columnDHAutoCalibCurrentRes, "%1.2f", calibResistance[GuiVar_columnDHAutoCalibPointSelection]); } else { strcpy(GuiVar_columnDHAutoCalibCurrentRes, notAvailable); } strcpy(GuiVar_columnDHAutoCalibResistance, notAvailable); } /* Update the easyGUI variable that displays the calibration stabilisation time to match the current value stored in our own internal variable. Provided so that we do this consistently everywhere */ void ColumnDHAutoCalibrationPageHandler::SetEasyGUIStabilisationTimeFromInternalValue(void) { sprintf(GuiVar_columnDHAutoCalibStabilisationTimeToSet, "%1d", stabilisationTime); } /* Construct the GC command to start the calibration process, using the current parameter values. Args: pointer to a buffer in which to store the command, as a null-terminated string No return code */ void ColumnDHAutoCalibrationPageHandler::ConstructStartCalibrationCommand(char* commandBuffer) { // Command is "CCONnttt", where 'n' is the calibration point (1, 2 or 3), // and "ttt" is the temperature commandBuffer[0] = 'C'; commandBuffer[1] = 'C'; commandBuffer[2] = 'O'; commandBuffer[3] = 'N'; switch (GuiVar_columnDHAutoCalibPointSelection) { case 0: commandBuffer[4] = '1'; break; case 1: commandBuffer[4] = '2'; break; default: // Assume the index is 2 commandBuffer[4] = '3'; break; } sprintf(&commandBuffer[5], "%03.0f", calibTemperature[GuiVar_columnDHAutoCalibPointSelection]); } /* Construct the GC command to stop the calibration process. (Provided to match the 'ConstructStartCalibrationCommand' function.) Args: pointer to a buffer in which to store the command, as a null-terminated string No return code */ void ColumnDHAutoCalibrationPageHandler::ConstructStopCalibrationCommand(char* commandBuffer) { // Command is "CCOF" commandBuffer[0] = 'C'; commandBuffer[1] = 'C'; commandBuffer[2] = 'O'; commandBuffer[3] = 'F'; commandBuffer[4] = '\0'; } /* As the name implies, sends a command to the GC and returns the response. Args: pointer to a buffer containing the command, as a null-terminated string pointer to a buffer to contain the response, as a null-terminated string No return code (it is up to the caller to examine the response to see whether the command succeeded or failed) */ void ColumnDHAutoCalibrationPageHandler::SendCommandToGCAndGetResponse(char* command, char* response) { #define USE_GC_UTILS // Testing new class #ifdef USE_GC_UTILS USBHostGCUtilities::SendCommandToGCAndGetResponse(usbDevice, usbHostGC, command, response); #else while(usbHostGC->ExecutingSetDeviceReport()) {} usbHostGC->SetDeviceReport(usbDevice, command, response); //#define DEBUG_PRINT_HERE #ifdef DEBUG_PRINT_HERE char dbg[100]; sprintf(dbg, "SCTGC cmd \"%s\", response \"%s\"", command, response); SpecialDebugPrint(dbg, 10, 275); #endif // DEBUG_PRINT_HERE #endif // USE_GC_UTILS } /* Sends a command to the GC for which we expect a response of "DACK" if successful, "DNAK" or "EPKT" if failure. Args: a pointer to the command in question, as a null terminated string Returns true if the GC responded with "DACK", false for anything else */ bool ColumnDHAutoCalibrationPageHandler::SendCommandToGCWithDACKResponse(char *cmd) { #define USE_GC_UTILS // Testing new class #ifdef USE_GC_UTILS return USBHostGCUtilities::SendCommandToGCWithDACKResponse(usbDevice, usbHostGC, cmd); #else char response[50]; SendCommandToGCAndGetResponse(cmd, response); // We expect a response like this: "DACK" for success, "DNAK" for failure, "EPKT" for error return (response[1] == 'A'); #endif // USE_GC_UTILS } /* Obtains the current calibration from the GC, and returns it as a value in the 'CalibrationState' enumeration. */ CalibrationState ColumnDHAutoCalibrationPageHandler::GetCalibrationState(void) { char response[30]; SendCommandToGCAndGetResponse("QDCS", response); // We expect a response of the form "DDCSnnnn", where "nnnn" is the calibration state: // 0000 = idle // 0001 = stage 1 // 0002 = stage 1A // 0003 = stage 2 // 0004 = stage 3 if(response[0] != 'D') { return INVALID; } if(response[1] != 'D') { return INVALID; } if(response[2] != 'C') { return INVALID; } if(response[3] != 'S') { return INVALID; } if(response[4] != '0') { return INVALID; } if(response[5] != '0') { return INVALID; } if(response[6] != '0') { return INVALID; } switch (response[7]) { case '0': return IDLE; case '1': return STAGE_1; case '2': return STAGE_1A; case '3': return STAGE_2; case '4': return STAGE_3; default: return INVALID; } } /* Set the easyGUI variable that displays the calibration state, to correspond with the current mode. No arguments, no return value */ void ColumnDHAutoCalibrationPageHandler::SetEasyGUICalibrationState(void) { switch (currentCalibrationState) { case IDLE: strcpy(GuiVar_columnDHAutoCalibCalibrationState, "Idle"); break; case STAGE_1: strcpy(GuiVar_columnDHAutoCalibCalibrationState, "Heat to Calib + 10"); break; case STAGE_1A: strcpy(GuiVar_columnDHAutoCalibCalibrationState, "Stabilising"); break; case STAGE_2: strcpy(GuiVar_columnDHAutoCalibCalibrationState, "Cool to Calib"); break; case STAGE_3: strcpy(GuiVar_columnDHAutoCalibCalibrationState, "Measuring"); break; default: strcpy(GuiVar_columnDHAutoCalibCalibrationState, "Invalid"); break; } } /* The Set Calibration button should be enabled during stage 3 and afterwards - but not before. This function sets the colour of the "Set" text on the button appropriately. No arguments, no return code */ void ColumnDHAutoCalibrationPageHandler::ShowSetCalibrationButtonEnabledState(void) { #ifdef USE_ENABLE_SET_BUTTON_FLAG if(enableSetButton) { #else if((currentCalibrationState == STAGE_3) || (previousCalibrationState == STAGE_3)) { #endif GuiVar_columnDHAutoCalibSetColour = 0; // Black - enabled } else { GuiVar_columnDHAutoCalibSetColour = SixteenBitColorValue(192, 192, 192); // Light grey - disabled } } /* Set the easyGUI variable that displays the heat state (i.e. on/off), to correspond with the current state. No arguments, no return value */ void ColumnDHAutoCalibrationPageHandler::SetEasyGUIHeatState(void) { char response[30]; SendCommandToGCAndGetResponse("QHTS", response); bool heatIsOn = false; // We expect a response like "DHTS000n", where 'n' is '1' for heat on, // '0' for heat off. Guard against "EPKT" as well if((response[0] == 'D') && (strlen(response) >= 8) && (response[7] == '1')) { heatIsOn = true; } strcpy(GuiVar_columnDHAutoCalibHeatState, heatIsOn ? "On" : "Off"); } /* Set the easyGUI variable that displays the oven temperature to correspond with the current oven temperature (i.e. not a calibration point value, but the actual temperature). No arguments, no return value */ void ColumnDHAutoCalibrationPageHandler::SetEasyGUIOvenTemperature(void) { char response[30]; SendCommandToGCAndGetResponse("QCOL", response); // We expect a response like "DCOLnnnn", where "nnnn" // is the current oven temperaure in units of 0.1 deg C // Guard against "EPKT" as well strcpy(GuiVar_columnDHAutoCalibActualOvenTemp, notAvailable); // default if(response[0] != 'D') { return; } if(response[1] != 'C') { return; } if(response[2] != 'O') { return; } if(response[3] != 'L') { return; } GuiVar_columnDHAutoCalibActualOvenTemp[0] = response[4]; GuiVar_columnDHAutoCalibActualOvenTemp[1] = response[5]; GuiVar_columnDHAutoCalibActualOvenTemp[2] = response[6]; GuiVar_columnDHAutoCalibActualOvenTemp[3] = '.'; GuiVar_columnDHAutoCalibActualOvenTemp[4] = response[7]; GuiVar_columnDHAutoCalibActualOvenTemp[5] = '\0'; } /* Gets the current oven temperature, returns it as a floating point value No arguments Return value: the current oven temperature, as a value of type float */ float ColumnDHAutoCalibrationPageHandler::GetCurrentOvenTemperature(void) { char response[30]; SendCommandToGCAndGetResponse("QCOL", response); // We expect a response like "DCOLnnnn", where "nnnn" // is the current oven temperaure in units of 0.1 deg C // Guard against "EPKT" as well if(response[0] != 'D') { return -1.0f; } if(response[1] != 'C') { return -1.0f; } if(response[2] != 'O') { return -1.0f; } if(response[3] != 'L') { return -1.0f; } float temp; sscanf(&response[4], "%f", &temp); return (temp / 10.0f); } /* Set the easyGUI variable that displays the calibration time remaining to the correct value - according to whether we are actually calibrating now or not. No arguments, no return code. */ void ColumnDHAutoCalibrationPageHandler::SetEasyGUICalibrationTimeRemaining(void) { if(CalibratingNow()) { // "Time remaining" is stabilisation time remaining - applies only in calibration stage 1A if(currentCalibrationState == STAGE_1A) { if(previousCalibrationState == STAGE_1) { #ifdef USE_STABILISATION_TIMER stabilisationTimer.reset(); stabilisationTimer.start(); #else gcTimeStabilisationStarted = GetTimeFromGC(); #endif } #ifdef USE_STABILISATION_TIMER double timeTakenSoFarInSeconds = ((double)stabilisationTimer.read_ms()) / 1000.0; // Note that we zeroed the timer when we entered stage 1A int timeTakenSoFarInMinutes = (int) floor((timeTakenSoFarInSeconds / 60.0) + 0.5); #else float gcTimeNow = GetTimeFromGC(); float timeTakenSoFarInMinutes = gcTimeNow - gcTimeStabilisationStarted; #endif sprintf(GuiVar_columnDHAutoCalibStabilisationTimeRemaining, "%1.1f", ((float)stabilisationTime - timeTakenSoFarInMinutes)); } else { if (previousCalibrationState == STAGE_1A) { #ifdef USE_STABILISATION_TIMER stabilisationTimer.stop(); #endif strcpy(GuiVar_columnDHAutoCalibStabilisationTimeRemaining, notAvailable); } } } else { strcpy(GuiVar_columnDHAutoCalibStabilisationTimeRemaining, notAvailable); } } /* This updates all the easyGUI variables that display something that may change while this page is displayed, without this page's knowledge. It is intended that will (e.g.) be called as part of GetGCStatusLoop's regular polling of the GC's state. An important consideration, which affects the way we treat each of the easyGUI variables, is the current calibration state of the GC. No arguments, no return code */ void ColumnDHAutoCalibrationPageHandler::UpdateVolatileEasyGUIVariables(void) { previousCalibrationState = currentCalibrationState; currentCalibrationState = GetCalibrationState(); SetEasyGUICalibrationState(); ShowSetCalibrationButtonEnabledState(); SetEasyGUIHeatState(); SetEasyGUIOvenTemperature(); SetEasyGUICalibrationTimeRemaining(); if(CalibratingNow()) { strcpy(GuiVar_columnDHAutoCalibCalibrateButtonText, "Stop"); CanUnlockDoorActuators(false); // Not while we are calibrating } else { strcpy(GuiVar_columnDHAutoCalibCalibrateButtonText, "Calibrate"); CanUnlockDoorActuators(true); // We are not calibrating } // if((currentCalibrationState == STAGE_3) || (previousCalibrationState == STAGE_3)) { // No - only *in* stage 3, not after - buffer length (for resistance measurement) in embedded code may have changed if(currentCalibrationState == STAGE_3) { // Can now get directly heated column resistance if(needToGetColumnResistance) { // But do not get it twice // Do we need to get the column resistance ourselves, and give the user the option to update the current calibration point afterwards, // or does the embedded code measure the resistance, then update the current point, automatically by itself in stage 3? // If we don't do "CCSM", does it ever come out of calibration stage 3? //#define GET_COLUMN_RESISTANCE_OURSELVES #ifdef GET_COLUMN_RESISTANCE_OURSELVES DisplayGettingColumnResistancePage(); // Tell user what we are doing // Send CCSM command TellGCToMeasureColumnResistance(); // Wait 2 seconds + a margin Thread::wait(2100); actualColumnResistance = GetActualColumnResistanceFromGC(); sprintf(GuiVar_columnDHAutoCalibResistance, "%.2f", actualColumnResistance); #ifdef USE_ENABLE_SET_BUTTON_FLAG enableSetButton = true; #endif // TODO: Update 'New' temperature and resistance - user can then decide // whether or not to press "Set" to copy those values to the current point // GuiVar_columnDHAutoCalibTemperatureToSet for temperature // GuiVar_columnDHAutoCalibResistance for resistance sprintf(GuiVar_columnDHAutoCalibTemperatureToSet, "%.0f", GetCurrentOvenTemperature()); sprintf(GuiVar_columnDHAutoCalibResistance, "%.2f", actualColumnResistance); RedisplayColumnCalibrationPage(); // Normal service is resumed #endif // GET_COLUMN_RESISTANCE_OURSELVES needToGetColumnResistance = false; } } #ifndef GET_COLUMN_RESISTANCE_OURSELVES if(previousCalibrationState == STAGE_3){ // Update the displayed temperature and resistance for the currently selected point calibTemperature[GuiVar_columnDHAutoCalibPointSelection] = GetColumnTemperaturePointFromGC(GuiVar_columnDHAutoCalibPointSelection + 1); SetEasyGUICalibrationPointTemperatureFromInternalValue(true); calibResistance[GuiVar_columnDHAutoCalibPointSelection] = GetColumnResistancePointFromGC(GuiVar_columnDHAutoCalibPointSelection + 1); SetEasyGUICalibrationPointResistanceFromInternalValue(); } #endif // Bodge to fix problem where the calibrated resistance is initially displayed as "99.99" - // this is the initial value that easyGUI assigns to that variable if(strcmp(GuiVar_columnDHAutoCalibCurrentRes, "99.99") == 0) { SetEasyGUICalibrationPointResistanceFromInternalValue(); } } /* Gets the current stabilisation time from the GC, returns it as an integer No arguments Return value: the stabilisation time obtained from the GC, or -1 if this failed */ int ColumnDHAutoCalibrationPageHandler::GetStabilisationTimeFromGC(void) { char response[30]; SendCommandToGCAndGetResponse("GCST", response); // We expect a response like "DCST00nn", where "nn" is the stabilisation time // in minutes, 0 to 99 if(response[0] != 'D') { return -1; } if(response[1] != 'C') { return -1; } if(response[2] != 'S') { return -1; } if(response[3] != 'T') { return -1; } if(response[4] != '0') { return -1; } if(response[5] != '0') { return -1; } int retVal; sscanf(&response[6], "%d", &retVal); return retVal; } /* Sets the current stabilisation time in the GC from the integer value passed to it Argument: the new value for the stabilisation time, in (whole) minutes Returns true if the GC responded with "DACK", false if anything else */ bool ColumnDHAutoCalibrationPageHandler::SetStabilisationTimeInGC(int value) { char commandBuffer[30]; sprintf(commandBuffer, "SCST%04d", value); return SendCommandToGCWithDACKResponse(commandBuffer); } /* Gets and returns the temperature value for the specified calibration point. Argument: the index of the required calibration point (1, 2 or 3) Return code: the corresponding temperature value, as a floating point value (the temperature is returned in units of 1/10 ohm) */ float ColumnDHAutoCalibrationPageHandler::GetColumnTemperaturePointFromGC(int pointIndex) { char commandBuffer[30]; commandBuffer[0] = 'G'; commandBuffer[1] = 'C'; commandBuffer[2] = 'T'; switch(pointIndex) { case 2: commandBuffer[3] = '2'; break; case 3: commandBuffer[3] = '3'; break; default: // Assume 1 commandBuffer[3] = '1'; break; } commandBuffer[4] = '\0'; char response[30]; SendCommandToGCAndGetResponse(commandBuffer, response); // We expect a response like "DCRnrrrr", where 'n' is the point index // and "rrrr" is the resistance in ohms multiplied by 100 if(response[0] != 'D') { return -1.0f; } if(response[1] != 'C') { return -1.0f; } if(response[2] != 'T') { return -1.0f; } if(response[3] != commandBuffer[3]) { return -1.0f; } int temp; sscanf(&response[4], "%d", &temp); float retVal = (float) temp; retVal /= 10.0f; return retVal; } /* Sets the temperature value for the specified calibration point. Arguments: the index of the required calibration point (1, 2 or 3) the temperature value to set (deg C) No return code */ void ColumnDHAutoCalibrationPageHandler::SetColumnTemperaturePointInGC(int pointIndex, float temperatureToSet) { char commandBuffer[30]; commandBuffer[0] = 'S'; commandBuffer[1] = 'C'; commandBuffer[2] = 'T'; switch(pointIndex) { case 2: commandBuffer[3] = '2'; break; case 3: commandBuffer[3] = '3'; break; default: // Assume 1 commandBuffer[3] = '1'; break; } // Value is sent in units of 1/10 deg C double dTemp = (double) temperatureToSet * 10.0; int valueToSend = (int) floor(dTemp + 0.5); sprintf(&commandBuffer[4], "%04d", valueToSend); commandBuffer[8] = '\0'; char response[30]; SendCommandToGCAndGetResponse(commandBuffer, response); } /* Gets and returns the resistance value for the specified calibration point. Argument: the index of the required calibration point (1, 2 or 3) Return code: the corresponding resistance value, as a floating point value (the resistance is returned in units of 1/100 ohm) */ float ColumnDHAutoCalibrationPageHandler::GetColumnResistancePointFromGC(int pointIndex) { char commandBuffer[30]; commandBuffer[0] = 'G'; commandBuffer[1] = 'C'; commandBuffer[2] = 'R'; switch(pointIndex) { case 2: commandBuffer[3] = '2'; break; case 3: commandBuffer[3] = '3'; break; default: // Assume 1 commandBuffer[3] = '1'; break; } commandBuffer[4] = '\0'; char response[30]; SendCommandToGCAndGetResponse(commandBuffer, response); // We expect a response like "DCRnrrrr", where 'n' is the point index // and "rrrr" is the resistance in ohms multiplied by 100 if(response[0] != 'D') { return -1.0f; } if(response[1] != 'C') { return -1.0f; } if(response[2] != 'R') { return -1.0f; } if(response[3] != commandBuffer[3]) { return -1.0f; } int temp; sscanf(&response[4], "%d", &temp); float retVal = (float) temp; retVal /= 100.0f; return retVal; } /* Sets the resistance value for the specified calibration point. Arguments: the index of the required calibration point (1, 2 or 3) the corresponding resistance value (in ohms) No return code */ void ColumnDHAutoCalibrationPageHandler::SetColumnResistancePointInGC(int pointIndex, float resistanceToSet) { char commandBuffer[30]; commandBuffer[0] = 'S'; commandBuffer[1] = 'C'; commandBuffer[2] = 'R'; switch(pointIndex) { case 2: commandBuffer[3] = '2'; break; case 3: commandBuffer[3] = '3'; break; default: // Assume 1 commandBuffer[3] = '1'; break; } // Value is sent in units of 1/100 deg C double dTemp = (double) resistanceToSet * 100.0; int valueToSend = (int) floor(dTemp + 0.5); sprintf(&commandBuffer[4], "%04d", valueToSend); commandBuffer[8] = '\0'; char response[30]; SendCommandToGCAndGetResponse(commandBuffer, response); } /* Gets and returns the actual resistance of the directly heated column. Note that this will only be valid during and after calibration stage 3. No arguments Return code: the column resistance, as a floating point value (the resistance is returned in units of 1/100 ohm) */ float ColumnDHAutoCalibrationPageHandler::GetActualColumnResistanceFromGC(void) { char commandBuffer[30]; commandBuffer[0] = 'Q'; commandBuffer[1] = 'D'; commandBuffer[2] = 'C'; commandBuffer[3] = 'R'; commandBuffer[4] = '\0'; char response[30]; SendCommandToGCAndGetResponse(commandBuffer, response); // We expect a response like "DDCrrrrr", where "rrrrr" (note 5 digits, not 4) // is the resistance in ohms multiplied by 100 if(response[0] != 'D') { return -1.0f; } if(response[1] != 'D') { return -1.0f; } if(response[2] != 'C') { return -1.0f; } int temp; sscanf(&response[3], "%d", &temp); float retVal = (float) temp; retVal /= 100.0f; return retVal; } /* Sends a "CCSM" command to the GC, to tell it to start the process of measuring the column resistance. *** We cannot request the resistance value until at least two seconds after this. *** No arguments. Returns true if the command succeeded ("DACK" response), false if not. */ bool ColumnDHAutoCalibrationPageHandler::TellGCToMeasureColumnResistance(void) { char commandBuffer[20]; commandBuffer[0] = 'C'; commandBuffer[1] = 'C'; commandBuffer[2] = 'S'; commandBuffer[3] = 'M'; commandBuffer[4] = '\0'; return SendCommandToGCWithDACKResponse(commandBuffer); } #ifndef USE_STABILISATION_TIMER /* Gets the GC run time, and returns it as a floating point value. Note that the GC returns the run time as a four digit value, scaled in units of 0.1 min. This function applies that scaling to the value it returns - i.e. if the GC returns "1234", this function returns 123.4 as the float value. Returns the time obtained from the GC, or -1 if this failed */ float ColumnDHAutoCalibrationPageHandler::GetTimeFromGC(void) { char response[50]; SendCommandToGCAndGetResponse("QTIM", response); if(response[0] == 'E') { // Got "EPKT" response return -1.0f; } // Assume we have received a valid response from the GC. We expect a response like this: // "DTIM1234", with run time in units of 0.1 min char buff[10]; buff[0] = response[4]; buff[1] = response[5]; buff[2] = response[6]; buff[3] = '.'; buff[4] = response[7]; buff[5] = '\0'; float gcTime; sscanf(buff, "%f", &gcTime); return gcTime; } #endif /* Displays the 'getting column resistance' page. At this point, we have issued the "CCSM" command to the GC, to start the process of getting the column resistance. We have to wait two seconds before we can get the resistance value. We display this page during those two seconds, to make it clear to the user that we cannot do anything else during that time. After getting the resistance, call 'RedisplayColumnCalibrationPage'. No args, no return code. */ void ColumnDHAutoCalibrationPageHandler::DisplayGettingColumnResistancePage(void) { #ifdef USING_BACKGROUND_BITMAP DrawBackgroundBitmap(); #else GuiLib_Clear(); #endif GuiLib_ShowScreen(GuiStruct_GettingColumnResistancePage_Def, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW); GuiLib_Refresh(); } /* After issuing the "CCSM" command, we display the 'getting column resistance' page, wait two seconds, then get the column resistance. During this period, we display the "Getting Column Resistance" page. After this, we redisplay our normal page, by calling this function. This function currently contains only a single function call. It is implemented for clarity in the code - and in case we decide something more complicated is required in future. No args, no return code. */ void ColumnDHAutoCalibrationPageHandler::RedisplayColumnCalibrationPage(void) { UpdateVolatileEasyGUIVariables(); UpdateEasyGUIPage(); }