Repository for import to local machine

Dependencies:   DMBasicGUI DMSupport

main.cpp

Committer:
jmitc91516
Date:
2016-01-13
Revision:
0:47c880c1463d
Child:
1:a5258871b33d

File content as of revision 0:47c880c1463d:

#include "mbed.h"
#include "DMBoard.h"
#include "lpc_swim.h"
#include "lpc_swim_font.h"

#include <string.h>

#include "GuiLib.h"
#include "GuiDisplay.h"

#include "USBHostGC.h"
#include "TouchListener.h"
#include "TouchPanelPageSelector.h"
#include "GCHeatControl.h"
#include "GetGCStatusLoop.h"
#include "GCComponentStatusColorArea.h"
#include "EthernetTimerHandler.h"


// ** Start of timeout code to guard against multiple presses of the Heat On/Off button **
//    (which is in the same position as the Abort Run button)
Timeout heatOnOffTimeout;

bool heatOnOffAvailable = true;

void MakeHeatOnOffAvailableAgain(void)
{
    heatOnOffAvailable = true;
}

void StartHeatOnOffTimeout(void)
{
    heatOnOffAvailable = false;
    heatOnOffTimeout.attach(&MakeHeatOnOffAvailableAgain, 1.0); // Wait 1.0 sec before accepting touches again on Heat On/Off button
}
// ** End of Heat On/Off timeout code **


// These are 'global' - TouchCallback function, as well as main(), needs to access them
TouchPanelPageSelectors touchPanelPageSelectors;
GCHeatControl* theGCHeatControl;

TouchListener* mainTouchListener = NULL;
GetGCStatusLoop* getGCStatusLoop = NULL;

HomePageGCComponentStatusColorAreas homePageGCComponentStatusColorAreas;
SingleGCComponentPageStatusColorAreas singleGCComponentPageStatusColorAreas;

EthernetTimerHandler* theEthernetTimerHandler = NULL;


GuiConst_INTCOLOR SixteenBitColorValue(GuiConst_INT8U red, GuiConst_INT8U green, GuiConst_INT8U blue)
{
    // Make sure we don't have numeric overflow problems during the conversion
    GuiConst_INT32U red32   = red;
    GuiConst_INT32U green32 = green;
    GuiConst_INT32U blue32  = blue;
    
    GuiConst_INT32U rgb = (blue32 << 16) | (green32 << 8) | red32;
    
    return GuiLib_RgbToPixelColor(rgb);
}

void DebugPrint(char *stuffToPrint, GuiConst_INT16S X, GuiConst_INT16S Y)
{
    char buff[200];
    
    const GuiConst_INT16U fontNo = GuiFont_Helv1;
    
    GuiDisplay_Lock();

    // (Attempt to) clear previous strings from display
    sprintf(buff, "                                        ");
    GuiLib_DrawStr(
        X,                      //GuiConst_INT16S X,
        Y,                      //GuiConst_INT16S Y,
        fontNo,                 //GuiConst_INT16U FontNo,
        buff,                   //GuiConst_TEXT PrefixLocate *String,
        GuiLib_ALIGN_LEFT,      //GuiConst_INT8U Alignment, 
        GuiLib_PS_ON,           //GuiConst_INT8U PsWriting,
        GuiLib_TRANSPARENT_OFF, //GuiConst_INT8U Transparent,
        GuiLib_UNDERLINE_OFF,   //GuiConst_INT8U Underlining,
        0,                      //GuiConst_INT16S BackBoxSizeX,
        0,                      //GuiConst_INT16S BackBoxSizeY1,
        0,                      //GuiConst_INT16S BackBoxSizeY2,
        GuiLib_BBP_NONE,        //GuiConst_INT8U BackBorderPixels,
        SixteenBitColorValue(0, 0, 0xFF),  //GuiConst_INTCOLOR ForeColor,
        SixteenBitColorValue(0, 0xFF, 0)   //GuiConst_INTCOLOR BackColor
    );

    GuiLib_DrawStr(
        X,                      //GuiConst_INT16S X,
        Y,                      //GuiConst_INT16S Y,
        fontNo,                 //GuiConst_INT16U FontNo,
        stuffToPrint,           //GuiConst_TEXT PrefixLocate *String,
        GuiLib_ALIGN_LEFT,      //GuiConst_INT8U Alignment, 
        GuiLib_PS_ON,           //GuiConst_INT8U PsWriting,
        GuiLib_TRANSPARENT_OFF, //GuiConst_INT8U Transparent,
        GuiLib_UNDERLINE_OFF,   //GuiConst_INT8U Underlining,
        0,                      //GuiConst_INT16S BackBoxSizeX,
        0,                      //GuiConst_INT16S BackBoxSizeY1,
        0,                      //GuiConst_INT16S BackBoxSizeY2,
        GuiLib_BBP_NONE,        //GuiConst_INT8U BackBorderPixels,
        SixteenBitColorValue(0, 0, 0xFF),  //GuiConst_INTCOLOR ForeColor,
        SixteenBitColorValue(0, 0xFF, 0)   //GuiConst_INTCOLOR BackColor
    );
    
    GuiLib_Refresh();

    GuiDisplay_Unlock();
}

void EasyGUIDebugPrint(char *stuffToPrint, short X, short Y)
{
#define DEBUG_HERE
#ifdef DEBUG_HERE
    DebugPrint(stuffToPrint, (GuiConst_INT16S) X, (GuiConst_INT16S) Y);
#undef DEBUG_HERE
#endif
}

void DummyEasyGUIDebugPrint(char *stuffToPrint, short X, short Y)
{
    //DebugPrint(stuffToPrint, (GuiConst_INT16S) X, (GuiConst_INT16S) Y);
}

int DummyDebugFunction(int i)
{
    return i^2;
}

bool GCIsReadyToRun(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
{
    while(usbHostGC->ExecutingSetDeviceReport()) {}

    char response[50];
    
    // Ensure we always have valid chars in the positions we are interested in,
    // in case we get "DNAK" or "EPKT" back
    response[6] = '0';
    response[7] = '0';
    
    usbHostGC->SetDeviceReport(usbDevice, "QSTA", response);
    // We expect a response like "QSTA00nn", where "nn" is the status.
    // "33" means ready to run, anything else means "not ready"
    
    return ((response[6] == '3') && (response[7]== '3'));
}

void DrawRunButton(bool enabled)
{
    // Black if enabled, grey if disabled
    GuiConst_INTCOLOR buttonColor = (enabled) ? 0 : SixteenBitColorValue(0x80, 0x80, 0x80);
    

    GuiConst_TEXT *buttonText = "Run";

    // These are hard-coded to match the corresponding definitions in easyGUI
    // (I have not found a way of getting these values from easyGUI at run time)
    const GuiConst_INT16S textX1 = 400;
    const GuiConst_INT16S textY1 = 220;
    const GuiConst_INT16U textFont = GuiFont_Helv1;
    
    const GuiConst_INT16S boxX1 = 338;
    const GuiConst_INT16S boxY1 = 195;
    const GuiConst_INT16S boxX2 = 462;
    const GuiConst_INT16S boxY2 = 235;
    
    GuiLib_Box(boxX1, boxY1, boxX2, boxY2, buttonColor);
    
    GuiLib_DrawStr(
        textX1,                 //GuiConst_INT16S X,
        textY1,                 //GuiConst_INT16S Y,
        textFont,               //GuiConst_INT16U FontNo,
        buttonText,             //GuiConst_TEXT PrefixLocate *String,
        GuiLib_ALIGN_CENTER,    //GuiConst_INT8U Alignment, 
        GuiLib_PS_ON,           //GuiConst_INT8U PsWriting,
        GuiLib_TRANSPARENT_ON,  //GuiConst_INT8U Transparent,
        GuiLib_UNDERLINE_OFF,   //GuiConst_INT8U Underlining,
        0,                      //GuiConst_INT16S BackBoxSizeX,
        0,                      //GuiConst_INT16S BackBoxSizeY1,
        0,                      //GuiConst_INT16S BackBoxSizeY2,
        GuiLib_BBP_NONE,        //GuiConst_INT8U BackBorderPixels,
        buttonColor,            //GuiConst_INTCOLOR ForeColor,
        SixteenBitColorValue(0xFF, 0xFF, 0xFF)   //GuiConst_INTCOLOR BackColor (should be ignored with GuiLib_TRANSPARENT_ON)
    );
}


void DisplayEasyGuiStructure(int structureIndex, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
{
    // If required, query the GC to find out if it is ready to run *before* clearing the display -
    // otherwise the display remains clear while we talk to the GC - causes noticeable flickering
    bool gcIsReadyToRun = false;
    if((structureIndex == GuiStruct_HomePage_1) && (usbDevice != NULL) && (usbHostGC != NULL)) {
        gcIsReadyToRun = GCIsReadyToRun(usbDevice, usbHostGC);
    }
    
    GuiLib_Clear();

    // Note - we draw the status rectangles after GuiLib_Clear - otherwise we wouldn't see the rectangles at all - 
    // and before GuiLib_ShowScreen - so text, etc, is drawn on top of the rectangles
    switch(structureIndex) {
        case GuiStruct_HomePage_1:
            homePageGCComponentStatusColorAreas.DisplayAll();
            break;
        case GuiStruct_ColumnPage1_2:
        case GuiStruct_ColumnPage2_9:
        case GuiStruct_ColumnPage3_10:
            singleGCComponentPageStatusColorAreas.DisplayGCComponentStatus(COLUMN);
            break;
        case GuiStruct_InjectorPage1_3:
            singleGCComponentPageStatusColorAreas.DisplayGCComponentStatus(INJECTOR);
            break;
        case GuiStruct_DetectorPage1_4:
            singleGCComponentPageStatusColorAreas.DisplayGCComponentStatus(DETECTOR);
            break;
        case GuiStruct_GasPage1_6:
            singleGCComponentPageStatusColorAreas.DisplayGCComponentStatus(GAS);
            break;
        default: // Don't need to display status rectangle for this page
            break;
    }
            
    GuiLib_ShowScreen(structureIndex, GuiLib_NO_CURSOR, GuiLib_RESET_AUTO_REDRAW);
    
    // But draw the run button, if required, on top of the fixed part of the home page
    if(structureIndex == GuiStruct_HomePage_1) {
        DrawRunButton(gcIsReadyToRun);
    }
    
    GuiLib_Refresh();
#define DEBUG_HERE
#ifdef DEBUG_HERE
    char dbg[100];
    sprintf(dbg, "After GuiLib_Refresh main 1");
    EasyGUIDebugPrint(dbg, 100, 100);
#undef DEBUG_HERE
#endif
    
    if(getGCStatusLoop != NULL) {
        getGCStatusLoop->SetCurrentPage(structureIndex);
    }
}

void DrawErrorMessage(char *msg)
{
    const GuiConst_INT16U fontNo = GuiFont_Helv1;
    
    GuiLib_DrawStr(
        90,                     //GuiConst_INT16S X,
        240,                    //GuiConst_INT16S Y,
        fontNo,                 //GuiConst_INT16U FontNo,
        msg,                    //GuiConst_TEXT PrefixLocate *String,
        GuiLib_ALIGN_LEFT,      //GuiConst_INT8U Alignment, 
        GuiLib_PS_ON,           //GuiConst_INT8U PsWriting,
        GuiLib_TRANSPARENT_OFF, //GuiConst_INT8U Transparent,
        GuiLib_UNDERLINE_OFF,   //GuiConst_INT8U Underlining,
        0,                      //GuiConst_INT16S BackBoxSizeX,
        0,                      //GuiConst_INT16S BackBoxSizeY1,
        0,                      //GuiConst_INT16S BackBoxSizeY2,
        GuiLib_BBP_NONE,        //GuiConst_INT8U BackBorderPixels,
        SixteenBitColorValue(0, 0, 0xFF),  //GuiConst_INTCOLOR ForeColor,
        SixteenBitColorValue(0, 0xFF, 0)   //GuiConst_INTCOLOR BackColor
    ); 
}

void UpdateHeatOnOffEasyGuiVariable(void)
{
    // Note that the easyGUI variable is not the current status of the heat on the GC,
    // but the command to toggle its current state
    if(theGCHeatControl != NULL) {
        if(theGCHeatControl->IsHeatOn()) {
            strcpy(GuiVar_heatOnOffCommand, "Heat Off");
        } else {
            strcpy(GuiVar_heatOnOffCommand, "Heat On");
        }
    }
}

bool StartGCRun(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
{
    while(usbHostGC->ExecutingSetDeviceReport()) {}

    char response[50];
    usbHostGC->SetDeviceReport(usbDevice, "CRUN", response);
    // We expect a response like this: "DACK" for success, "DNAK" for failure
    
#define DEBUG_HERE
#ifdef DEBUG_HERE
    char dbg[100];
    sprintf(dbg, "CRUN returned %s", response);
    EasyGUIDebugPrint(dbg, 100, 150);    
#endif
#undef DEBUG_HERE

    return (response[1] == 'A');
}

bool StopGCRun(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
{
    while(usbHostGC->ExecutingSetDeviceReport()) {}

    char response[50];
// TODO: Find out which is the correct command here
//    char *cmd = "CSTP";
    char *cmd = "CABT";
    usbHostGC->SetDeviceReport(usbDevice, cmd, response);
    // We expect a response like this: "DACK" for success, "DNAK" for failure
    
#define DEBUG_HERE
#ifdef DEBUG_HERE
    char dbg[100];
    sprintf(dbg, "%s returned %s", cmd, response);
    EasyGUIDebugPrint(dbg, 100, 150);    
#endif
#undef DEBUG_HERE
    
    return (response[1] == 'A');
}

bool ExitGCStandbyMode(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
{
    while(usbHostGC->ExecutingSetDeviceReport()) {}

    char response[50];
    usbHostGC->SetDeviceReport(usbDevice, "CDIS", response);
    // We expect a response like this: "DACK" for success, "DNAK" for failure
    
    char dbg[100];
    sprintf(dbg, "CDIS returned %s", response);
    EasyGUIDebugPrint(dbg, 100, 150);   
    
    if(getGCStatusLoop != NULL) {
        getGCStatusLoop->ExitedGCStandbyMode();
    }
    
    return (response[1] == 'A');
}

bool ClearGCErrors(USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
{
    while(usbHostGC->ExecutingSetDeviceReport()) {}

    char response[50];
    usbHostGC->SetDeviceReport(usbDevice, "CCLR", response);
    // We expect a response like this: "DACK" for success, "DNAK" for failure
    
    char dbg[100];
    sprintf(dbg, "CCLR returned %s", response);
    EasyGUIDebugPrint(dbg, 100, 150);   
    
    return (response[1] == 'A');
}

void TouchCallback(touch_coordinate_t touchCoords, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC, int tickCount, bool newTouch)
{
    GuiConst_INT32S touchAreaIndex = GuiLib_TouchCheck((GuiConst_INT16S)touchCoords.x, (GuiConst_INT16S)touchCoords.y);

    if(touchAreaIndex >= 0) {
        bool dealtWithTouch = false;
        
        // page selector?
        TouchPanelPageSelector* touchPanelPageSelector = touchPanelPageSelectors.GetTouchPanelPageSelector(touchAreaIndex);
        
        if( touchPanelPageSelector != NULL) {
            // Do not keep switching pages if the user keeps 'touching' - 
            // switch only if he 'lets go', then presses again
            if(newTouch) {
                if(touchAreaIndex == 200) {
                    // Stop run - as well as displaying the home page, do this...
                    StopGCRun(usbDevice, usbHostGC);
                }
                
                if(touchAreaIndex == 400) {
                    // Take GC out of standby mode
                    ExitGCStandbyMode(usbDevice, usbHostGC);
                }
                
                if(touchAreaIndex == 600) {
                    // Take GC out of error state
                    ClearGCErrors(usbDevice, usbHostGC);
                }

                DisplayEasyGuiStructure(touchPanelPageSelector->GetPageNumber(), usbDevice, usbHostGC);
            }
            
            dealtWithTouch = true; // The user touched a page selector, so we have still 'dealt' with this, 
                                   // whether we had a 'new touch' or not
                                   
            if(touchAreaIndex == 200) {
                // Is in same place as Heat On/Off button
                StartHeatOnOffTimeout();
            }
        }
        
        if(!dealtWithTouch) {
            if(touchAreaIndex == 100) { // Run button
                if(newTouch) { // As above - do not do this repeatedly - GC does not like it...
                    if(GCIsReadyToRun(usbDevice, usbHostGC)) {
                        // Start run - if this works, display the 'Run' page, else do nothing
                        if(StartGCRun(usbDevice, usbHostGC)) {
                            DisplayEasyGuiStructure(GuiStruct_RunningPage_7, usbDevice, usbHostGC);
                        } else {
                            DrawErrorMessage("*** Run failed to start ***");
                            
                            // Give user time to read error, then erase it
                            Thread::wait(2000);
                            
                            DisplayEasyGuiStructure(GuiStruct_HomePage_1, usbDevice, usbHostGC);
                        }                   
                    }
                    // else GC is not ready to run (button should be greyed out) - ignore
                }
            
                dealtWithTouch = true;
            }
        }

        if(!dealtWithTouch) {
            if(touchAreaIndex == 300) { // Heat on/off button
                if(newTouch) { // As above - do not do this repeatedly - GC does not like it...
                    if(heatOnOffAvailable) {
                        if(theGCHeatControl != NULL) {
                            if(theGCHeatControl->IsHeatOn()) {
                                theGCHeatControl->TurnHeatOff();
                            } else {
                                theGCHeatControl->TurnHeatOn();
                            }
                            UpdateHeatOnOffEasyGuiVariable();
                            
                            // Make GuiVar_heatOnOffCommand update visible on screen
                            DisplayEasyGuiStructure(GuiStruct_HomePage_1, usbDevice, usbHostGC);
        
                            StartHeatOnOffTimeout();
                        }
                    }
                }
                                
                // Whether we changed the heat or not, the user still 'touched' our area,
                // so no-one else should try and deal with this touch
                dealtWithTouch = true;
            }
        }
    }
}
   
void SetupUSBGCTouchListener(DMBoard* board, USBDeviceConnected* usbDevice, USBHostGC* usbHostGC)
{
    // Note that TouchListener is a singleton - we do not need or want there to be more than one instance of it
    // (there is only one board, and only one touch panel)
    mainTouchListener = TouchListener::GetInstance(board->touchPanel(), usbDevice, usbHostGC);

    if(mainTouchListener != NULL) {
        mainTouchListener->SetTouchCallbackFunction(&TouchCallback);

// Not yet...
//    mainTouchListener->SetTouchReleasedCallbackFunction(&TouchReleasedCallback);
//  
//    mainTouchListener->SetTimerOneSecondCallbackFunction(&TimerOneSecondCallback);
    }
}


int main()
{
  DMBoard::BoardError err;
  DMBoard* board = &DMBoard::instance();
  RtosLog* log = board->logger();
  Display* disp = board->display();
  
  do {
    err = board->init();
    if (err != DMBoard::Ok) {
      log->printf("Failed to initialize the board, got error %d\r\n", err);
      break;
    }
    
    log->printf("\n\nHello World!\n\n");
    
    void* fb = disp->allocateFramebuffer();
    if (fb == NULL) {
      log->printf("Failed to allocate memory for a frame buffer\r\n");
      err = DMBoard::MemoryError;
      break;
    }
    
    
//    Display::DisplayError disperr = disp->powerUp(fb, Display::Resolution_24bit_rgb888);
    // Start display in default mode (16-bit) (24-bit uses too much memory)
    Display::DisplayError disperr = disp->powerUp(fb);
    if (disperr != Display::DisplayError_Ok) {
      log->printf("Failed to initialize the display, got error %d\r\n", disperr);
      break;
    }
    
  } while(false);

  if (err != DMBoard::Ok) {
    log->printf("\nTERMINATING\n");
    wait_ms(2000); // allow RtosLog to flush messages
    mbed_die();
  }  

  
  // easyGUI stuff - note function calls require 'extern "C"' in relevant header
    
    // Need to set up heat on/off command easyGUI variable 
    // before we display the home page for the first time - 
    // but we do not have a heat control object at this point - 
    // default to 'Heat On'
    strcpy(GuiVar_heatOnOffCommand, "Heat On");
    
    GuiDisplay_Lock();
    
    GuiLib_Init();
  
    GuiLib_Refresh();
  
    DisplayEasyGuiStructure(GuiStruct_HomePage_1, NULL, NULL);
  
    GuiLib_Refresh();
  
    GuiDisplay_Unlock();
  
  
    // Now the USB 'stuff'
  
    USBHost* usbHost = USBHost::getHostInst();
    USBDeviceConnected* usbDevice;
    USBHostGC usbHostGC;

    usbDevice = NULL;

    DrawErrorMessage("Waiting for USB device...");
    
    // Wait (indefinitely) for a USB device to be connected -
    // note that, if there is no USB device, we will hang at this point -
    // there is no timeout
    while(usbDevice == NULL) {
        for (uint8_t i = 0; i < MAX_DEVICE_CONNECTED; ++i) {
            usbDevice = usbHost->getDevice(i);
            
            if (usbDevice) {
                break;
            }
        }
    }
    
    DrawErrorMessage("                                            ");

    if(usbDevice != NULL) {
        USB_TYPE enumerateRetVal = usbHost->enumerate(usbDevice, &usbHostGC);

        if (usbHostGC.DeviceIsGC()) {
            if (usbHostGC.AttachGCDevice(usbDevice)) {

                DrawErrorMessage("Found GC device                                      ");
                

                SetupUSBGCTouchListener(board, usbDevice, &usbHostGC);
                
                DrawErrorMessage("After call to SetupUSBGCTouchListener                ");


                getGCStatusLoop = GetGCStatusLoop::GetInstance(usbDevice, &usbHostGC);
                
                DrawErrorMessage("After creation of GetGCStatusLoop instance           ");
                
                
                theGCHeatControl = new GCHeatControl(usbDevice, &usbHostGC);
                
                UpdateHeatOnOffEasyGuiVariable();
                
                DrawErrorMessage("After UpdateHeatOnOffEasyGuiVariable                 ");
                
                
                //theEthernetTimerHandler = EthernetTimerHandler::GetInstance(usbDevice, &usbHostGC);
                
                
                getGCStatusLoop->SetHomePageGCComponentStatusColorAreas(&homePageGCComponentStatusColorAreas);
                getGCStatusLoop->SetSingleGCComponentPageStatusColorAreas(&singleGCComponentPageStatusColorAreas);

                DrawErrorMessage("Point 1                                              ");
                
                getGCStatusLoop->SetupAllEasyGUIVariables();

                DrawErrorMessage("Point 2                                              ");
                
                getGCStatusLoop->SetCurrentPage(GuiStruct_HomePage_1);
                
                DrawErrorMessage("Point 3                                              ");
                
                DisplayEasyGuiStructure(GuiStruct_HomePage_1, usbDevice, &usbHostGC);
  
                // Currently, this never returns - but it allows time for the TouchCallback function to be invoked
                //getGCStatusLoop->MainLoop();
                getGCStatusLoop->MainLoopWithEthernet();
                
                // Should never reach this code - but just in case...
                delete theGCHeatControl;

            } else {
                DrawErrorMessage("Failed to attach GC device to host                                ");
            }
        } else {
            DrawErrorMessage("  *** USB device found, is *not* a GC ***                                ");
        }
    } else {
        DrawErrorMessage("  *** No USB device found ***                        ");
    }
  
    while(true) {}
}