System Management code

Dependencies:   mbed CANBuffer Watchdog MODSERIAL mbed-rtos xbeeRelay IAP

Fork of SystemManagement by Martin Deng

inCommands/inCommands.cpp

Committer:
pspatel321
Date:
2015-02-07
Revision:
38:8efacce315ae
Parent:
36:0afc0fc8f86b
Child:
39:ddf38df9699e

File content as of revision 38:8efacce315ae:

#include "inCommands.h"
#include "runTime.h"
#include "inMacros.h"

// Compare string to a word in the serial input, shorter to type
#define CMP(w, string)                                              if (!strcasecmp(word[w-1], string))

// Serial input
int serviceSerial()
{
    static int end = 0;                              // End of string position

    int c=0;
    if (pc.readable()) c = pc.getc();
    if (c == -1 || c == 0) return 0;

    char b = c;                                      // Casted to char type
    bool process = false;                            // Is string complete (ready to parse)?

    if (b == '\n' || b == '\r') {                    // Enter key was pressed, dump for processing
        tempData.inputStr[end] = 0;                  // Null terminate
        end = 0;                                     // Reset to start
        process = true;                              // Flag for processing

    } else if (b == '\b' || b == 127) {              // Backspace or delete
        if (end > 0) end--;                          // Move back one char
        tempData.inputStr[end] = 0;                  // Erase char

    } else if (b > 31 && b < 127) {                  // New valid displayable char
        tempData.inputStr[end++] = b;                // Add to buffer
        tempData.inputStr[end] = 0;                  // Add null terminator
        if (end >= RX_SIZE) {
            end = 0;                                 // Reset end location
            process = true;                          // Flag for processing
        }
    }
    // Continue to parsing section only if flagged as complete and string not empty
    if (!process || strlen((char*)tempData.inputStr) == 0) return 0;

    static char word[3][RX_SIZE+1];                  // Hold 3 words
    int pieces = sscanf(tempData.inputStr, "%s %s %s", word[0], word[1], word[2]);      // Populate words
    tempData.inputStr[0] = 0;                        // Empty the string displayed on screen
    char *next;                                      // Used by strtof and strtoul

    // One word commands
    if (pieces == 1) {
        // Reset the microcontroller
        CMP(1, "reset") {
            NVIC_SystemReset();
            return 1;
        }
        // Clear non-volatile fault flags, then reset the microcontroller
        CMP(1, "resetClear") {
            runTime::clearFaults();
            NVIC_SystemReset();
            return 1;
        }
        return -1;
    }
    // Two word commands
    if (pieces == 2) {
        // Change the serial dashboard display mode
        CMP(1, "display") {
            CMP(2, "compact") {
                if (param->change_extendedSerial(0)) {
                    op->profileModded=true;
                    return 1;  // Change function defined by macro
                }
            }
            CMP(2, "extended") {
                if (param->change_extendedSerial(1)) {
                    op->profileModded=true;
                    return 1;  // Change function defined by macro
                }
            }
        }
        // Change ACK setting for CAN (noAck allows testing of CAN without other nodes)
        CMP(1, "CANack") {
            CMP(2, "noack") {                           // NoAck test mode
                if (param->change_CANnoAck(1)) {
                    op->profileModded=true;
                    return 1;
                }
            }
            CMP(2, "ack") {                             // Normal CAN protocol
                if (param->change_CANnoAck(0)) {
                    op->profileModded=true;
                    return 1;
                }
            }
            return -1;
        }
        // Artificially capture a freeze frame, save to flash
        CMP(1, "capture") {
            CMP(2, "freeze") {
                if (!FreezeFrame::getError()) {         // Only allow capture if spot available (last error frame was cleared manually)
                    if (FreezeFrame::writeFrame()) {    // Copy RAM frame to freezree sector in flash
                        return 1;
                    }
                }
            }
            return -1;
        }
        // Clear fault conditions
        CMP(1, "clear") {
            CMP(2, "freeze") {                          // Clear the freeze frame (mark as read)
                FreezeFrame::clearError();              // Clear the non-volatile error marker
                return 1;
            }
            CMP(2, "faults") {                          // Clear everything
                runTime::clearFaults();
                return 1;
            }
            return -1;
        }
        // Change the display contents
        CMP(1, "view") {
            CMP(2, "freeze") {                          // View the last stored freeze frame
                if (FreezeFrame::getFrame(&tempData.freeze)) {  // Fetch the pointer from flash
                    return 1;
                }
            }
            CMP(2, "live") {                            // View live data from RAM
                tempData.freeze = NULL;                 // Zero the pointer
                return 1;
            }
            return -1;
        }
        // Artificially update the SOC (battery life %)
        CMP(1, "SOC") {
            CMP(2, "Reset") {                           // Command was "SOC reset" - reset to 100%, do this after a full charge
                glvBat.resetToSOC(1);
                return 1;
            }
            float soc = strtod(word[1], &next);         // Command was "SOC xxx" where xxx is float between 0 and 1
            if (*next == 0) {
                if (glvBat.resetToSOC(soc)) return 1;
            }
            return -1;
        }
        // Artificially update the AmpHours count (linked with SOC)
        CMP(1, "Ah") {
            CMP(2, "Reset") {                           // Command was "Amphours reset", equivalent to "SOC reset"
                glvBat.resetToSOC(1);
                return 1;
            }
            float ah = strtod(word[1], &next);          // Command was "Amphours xxx" where xxx is a float in amphours
            if (*next == 0) {
                if (glvBat.resetToAh(ah)) return 1;
            }
            return -1;
        }
        // Change the battery capacity setting for calculating Amphours
        CMP(1, "Capacity") {
            float cap = strtod(word[1], &next);         // Command was "SOC xxx" where xxx is float between 0 and 1
            if (*next == 0) {
                if (glvBat.changeCapacity(cap)) return 1;
            }
            return -1;
        }

        bool parsed=false;

        CHANGE_VAR("GLVchar",       chargeCurrent)
        CHANGE_VAR("GLVdisch",      dischargeCurrent)
        CHANGE_VAR("GLVnomCap",     nominalCapacity)
        CHANGE_VAR("GLVtaps",       glvBat_taps)
        CHANGE_VAR("dcdcThres",     dcdcThreshold)
        CHANGE_VAR("dcdcOver",      dcdcOverCurrent)
        CHANGE_VAR("dcdcStart",     dcdcStartDelay)
        CHANGE_VAR("dcdcStop",      dcdcStopDelay)
        CHANGE_VAR("dcdcTaps",      dcdc_taps)
        CHANGE_VAR("imdStart",      imdStartDelay)
        CHANGE_VAR("amsStart",      amsStartDelay)
        CHANGE_VAR("IntOverT",      internalOverTemp)
        CHANGE_VAR("CANtxSize",     CANtxSize)
        CHANGE_VAR("CANrxSize",     CANrxSize)
        CHANGE_VAR("SerialBaud",    SerialBaud)
        CHANGE_VAR("SerialTx",      SerialTxSize)
        CHANGE_VAR("XbeeBaud",      XbeeBaud)
        CHANGE_VAR("XbeeTxSize",    XbeeTxSize)
        CHANGE_VAR("XbeeRxSize",    XbeeRxSize)
        CHANGE_VAR("CANack",        CANnoAck)

        if (!parsed) return -1;

        CMP(1, "GLVnomCap")             return glvBat.changeCapacity(param->nominalCapacity)?1:-1;
        CMP(1, "GLVtaps")               return glvBat.size(param->glvBat_taps)?1:-1;
        CMP(1, "dcdcTaps")              return dcdc.size(param->dcdc_taps)?1:-1;
        CMP(1, "CANtxSize")             return can.txSize(param->CANtxSize)?1:-1;
        CMP(1, "CANrxSize")             return can.rxSize(param->CANrxSize)?1:-1;
        CMP(1, "SerialBaud")            pc.baud(param->SerialBaud);
        CMP(1, "SerialTx")              return (pc.txBufferSetSize(param->SerialTxSize) == 0)?1:-1;
        CMP(1, "XbeeBaud")              xbeeRelay.baud(param->XbeeBaud);
        CMP(1, "XbeeTxSize")            return (xbeeRelay.txSize(param->XbeeTxSize))?1:-1;
        CMP(1, "XbeeRxSize")            return (xbeeRelay.rxSize(param->XbeeRxSize))?1:-1;

        return 1;
    }
    // Three word commands
    if (pieces == 3) {
        // Fan Duty
        CMP(1, "Fan") {
            float val1 = strtod(word[1], &next);
            if (*next == 0) {
                float val2 = strtod(word[2], &next);
                if (*next == 0) {
                    op->dcdc.request.fan1 = val1;
                    op->dcdc.request.fan2 = val2;
                    return 1;
                }
            }
            return -1;
        }

        // Pump Duty
        CMP(1, "Pump") {
            float val1 = strtod(word[1], &next);
            if (*next == 0) {
                float val2 = strtod(word[2], &next);
                if (*next == 0) {
                    op->dcdc.request.pump1 = val1;
                    op->dcdc.request.pump2 = val2;
                    return 1;
                }
            }
            return -1;
        }
        // Set the system time (RTC)
        CMP(1, "Time") {
            struct tm t;    // Time & date struct
            int ret = sscanf(word[1], "%d/%d/%d", &t.tm_mon, &t.tm_mday, &t.tm_year);   // Populate date
            t.tm_year = t.tm_year - 1900;       // Year mod to fix 0 index
            t.tm_mon = t.tm_mon - 1;            // Month mod to fix 0 index
            if (ret == 3) {                     // All 3 items found
                ret = sscanf(word[2], "%d:%d:%d", &t.tm_hour, &t.tm_min, &t.tm_sec);    // Populate time
                if (ret == 3) {                 // All 3 items found
                    set_time(mktime(&t));       // Set the RTC
                    time_t diff = time(NULL) - op->SysTime;     // Get change in time from old to new
                    op->startTime += diff;                      // Shift the startTime to new timebase
                    return 1;
                }
            }
            return -1;
        }

        // Profile manipulations between RAM and flash
        CMP(1, "Profile") {
            // Write, copy RAM to a flash sector
            CMP(2, "Write") {
                unsigned int index = strtoul(word[2], &next, 10);               // Get index from command "profile write xxx"
                if (index <= NUM_STORED_PROFILES && index > 0 && *next == 0) {  // Check within bounds
                    bool s = Profile::saveProfile(index);                       // Write to flash
                    if (s) {                                                    // Successful?
                        op->profileModded = false;                              // Mark it as a fresh, unmodified profile
                        op->profileIndex = Profile::usingProfile();             // Change the currently loaded profile marker
                        return 1;
                    }
                }
                return -1;
            }
            // Load, read from flash to RAM
            CMP(2, "Load") {
                CMP(3, "default") {                                             // The hard-coded flash profile (found in FreezeFrame.cpp)
                    Profile::loadProfile(0);                                    // Copy default to RAM
                    op->profileIndex = Profile::usingProfile();                 // Change the currently loaded profile marker
                    op->profileModded = false;                                  // Mark it as a fresh, unmodified profile
                    return 1;
                }
                CMP(3, "freeze") {                                              // Get the profile portion of the last freeze frame captured
                    if(Profile::loadProfile(-1)) {                              // Attemp to retrieve and copy to RAM
                        op->profileModded = false;                              // Mark it as a fresh, unmodified profile
                        op->profileIndex = Profile::usingProfile();             // Change the currently loaded profile marker
                        return 1;
                    }
                }
                int index = strtol(word[2], &next, 10);                          // Command was "profile load xxx"
                if (index <= NUM_STORED_PROFILES && index >= -1 && *next == 0) { // Valid index found?
                    if (Profile::loadProfile(index)) {                           // Attempt to retrieve and copy to RAM
                        op->profileModded = false;                               // Mark it as a fresh, unmodified profile
                        op->profileIndex = Profile::usingProfile();              // Change the currently loaded profile marker
                        return 1;
                    }
                }
                return -1;
            }
            // View the profile only (NOT loaded to RAM, just display changed)
            CMP(2, "view") {
                CMP(3, "default") {                                             // View the hard-coded flash profile
                    if (Profile::getProfile(&tempData.viewProfile, 0)) {        // Attempt to fetch pointer
                        tempData.viewProfileNum = 0;                            // Mark the index of the fetched profile
                        return 1;
                    }
                }
                CMP(3, "freeze") {                                              // View the profile portion only of the last captured freeze frame
                    if (Profile::getProfile(&tempData.viewProfile, -1)) {       // Attempt to fetch pointer
                        tempData.viewProfileNum = -1;                           // Mark the index of the fetched profile
                        return 1;
                    }
                }
                CMP(3, "live") {                                                // Revert to normal, live view
                    tempData.viewProfileNum = -2;                               // Mark live
                    tempData.viewProfile = NULL;                                // Clear the pointer
                    return 1;
                }

                int index = strtol(word[2], &next, 10);                          // Command was "profile view xxx"
                if (index <= NUM_STORED_PROFILES && index >= -1 && *next == 0) { // Valid index found?
                    if (Profile::getProfile(&tempData.viewProfile, index)) {     // Attempt to fetch pointer
                        tempData.viewProfileNum = index;                         // Mark the index of the fetched profile
                        return 1;
                    }
                }
                return -1;
            }
        }
    }
    return -1;
}
// Called when AMS AIRs Mode message stops coming in
Timeout timer_AIRS_CLOSED;
void timeout_AIRS_CLOSED()
{
    op->signals  &= ~AIRS_CLOSED;
}

// Called when Charger CAN messages stop coming in
Timeout timer_CHARGER_DET;
void timeout_CHARGER_DET()
{
    op->signals  &= ~CHARGER_DET;
}

// Called when PCM messages stop coming in
Timeout timer_FANS;
void timeout_FANS()
{
    op->dcdc.request.fan1  = 0;
    op->dcdc.request.fan2  = 0;
}
Timeout timer_PUMPS;
void timeout_PUMPS()
{
    op->dcdc.request.pump1  = 0;
    op->dcdc.request.pump2  = 0;
}

#define REFRESH_TIMEOUT(NAME)           \
timer_##NAME.detach();                  \
timer_##NAME.attach(&timeout_##NAME, CAN_DEVICE_TIMEOUT);

bool serviceCAN(CANMessage* fromXbee)
{
    CANMessage msg;
    if (fromXbee != NULL) {
        memcpy((void*)&msg, (void*)fromXbee, sizeof(CANMessage));
    } else {
        if (!can.rxRead(msg)) return false;
    }
    // Redirect global car reset
    if (msg.id == GLOBAL_CAR_RESET_RX_ID) msg.id = RESETCLEAR_RX_ID;

    switch (msg.id) {
            // Reset microntroller
        case (RESET_RX_ID):
            if (msg.len == 0) {     // Length must = 0
                NVIC_SystemReset();
                CAN_SUCCESS
            }
            CAN_FAIL

        
        // Clear non-volatile fault flags, then reset microcontroller
        case (RESETCLEAR_RX_ID):
            if (msg.len == 0) {                 // Length must = 0
                FreezeFrame::clearError();
                NVIC_SystemReset();
                CAN_SUCCESS
            }
            CAN_FAIL

        // Artificially capture a freeze frame
        case (CAPTURE_RX_ID):
            if (msg.len == 0) {                         // Length must = 0
                if (!FreezeFrame::getError()) {         // Only allow capture if freeze frame from the last error was read
                    if (FreezeFrame::writeFrame()) {    // Capture the RAM contents to flash
                        CAN_SUCCESS
                    }
                }
            }
            CAN_FAIL

        // Clear fault conditions
        case (CLEAR_RX_ID):
            if (msg.len == 1) {                         // One data byte
                if (msg.data[0] == 0) {                 // Clear only freeze frame error if = 0
                    FreezeFrame::clearError();          // Clear non-volatile freeze frame marker
                    CAN_SUCCESS
                }
                if (msg.data[0] == 1) {                 // Clear everything if = 1
                    runTime::clearFaults();
                    op->faultCode = 0;
                    CAN_SUCCESS
                }
            }
            CAN_FAIL

            // Set the time (RTC)
        case (TIME_RX_ID):
            if (msg.len == 6*sizeof(char)) {                    // 6 Bytes
                struct tm t;                                    // Time & date struct
                t.tm_mon  = msg.data[0];                        // Month in byte[0]
                t.tm_mday = msg.data[1];                        // Day
                t.tm_year = msg.data[2];                        // Year (offset from 2000)
                t.tm_year = t.tm_year - 1900 + 2000;            // Apply year index mod and offset
                t.tm_mon  = t.tm_mon - 1;                       // Month index mod
                t.tm_hour = msg.data[3];                        // Get hour of time in byte[3] (24 hr format)
                t.tm_min  = msg.data[4];                        // Minutes
                t.tm_sec  = msg.data[5];                        // Seconds
                set_time(mktime(&t));                           // Set RTC
                time_t diff = time(NULL) - op->SysTime;         // Old time to new time change
                op->startTime += diff;                          // Shift the startTime to new timebase
                CAN_SUCCESS
            }
            CAN_FAIL

            // RAM and flash profile manipulations
        case (PROFILE_RX_ID):
            if (msg.len == 2*sizeof(char)) {                    // 2 command bytes
                if (msg.data[0] == 0) {                         // Load profile from a flash location to RAM
                    int index = msg.data[1];                    // Second byte contains profile index
                    if (msg.data[1] == 0xff) index = -1;        // If freeze (special case)
                    if (Profile::loadProfile(index)) {          // Attempt to load (copy flash to RAM)
                        op->profileIndex = Profile::usingProfile();     // Change the currently loaded profile marker
                        op->profileModded = false;                      // Mark it as a fresh, unmodified profile
                        CAN_SUCCESS
                    }
                }
                if (msg.data[0] == 1) {                         // Write profile to flash from RAM
                    int index = msg.data[1];                    // Get which slot to write to from message
                    if (msg.data[1] == 0xff) index = -1;        // If freeze (special case)
                    bool s = Profile::saveProfile(index);       // Write profile to flash slot
                    if (s) {
                        op->profileIndex = Profile::usingProfile();     // Change the currently loaded profile marker
                        op->profileModded = false;                      // Mark it as a fresh, unmodified profile
                        CAN_SUCCESS
                    }
                }
            }
            CAN_FAIL

        case FAN_CONTROL_ID:
            if (msg.len != 2*sizeof(float)) return false;
            REFRESH_TIMEOUT(FANS)
            op->dcdc.request.fan1 = *((float*)((void*)(&msg.data[0])));
            op->dcdc.request.fan2 = *((float*)((void*)(&msg.data[4])));
            return true;

        case PUMP_CONTROL_ID:
            if (msg.len != 2*sizeof(float)) return false;
            REFRESH_TIMEOUT(PUMPS)
            op->dcdc.request.pump1 = *((float*)((void*)(&msg.data[0])));
            op->dcdc.request.pump2 = *((float*)((void*)(&msg.data[4])));
            return true;

        case AMS_MODE_ID:
            if (msg.len != sizeof(char)) return false;
            REFRESH_TIMEOUT(AIRS_CLOSED)
            if (msg.data[0] & 1<<2) {     // AIRs closed?
                op->signals |= AIRS_CLOSED;
            } else {
                op->signals &= ~AIRS_CLOSED;
            }
            return true;
        case CHARGER_ERR_ID:
            REFRESH_TIMEOUT(CHARGER_DET)
            op->signals |= CHARGER_DET;
            return true;
        default:
            break;
    }
    bool parsed=false;

    CAN_CHANGE(chargeCurrent,           PROFILE_CHARGECURRENT_RX_ID     )
    CAN_CHANGE(dischargeCurrent,        PROFILE_DISCHARGECURRENT_RX_ID  )
    CAN_CHANGE(nominalCapacity,         PROFILE_NOMINALCAPACITY_RX_ID   )
    CAN_CHANGE(glvBat_taps,             PROFILE_GLVBATTAPS_RX_ID        )
    CAN_CHANGE(dcdcThreshold,           PROFILE_DCDCONTHRESHOLD_RX_ID   )
    CAN_CHANGE(dcdcOverCurrent,         PROFILE_DCDCOVERCURRENT_RX_ID   )
    CAN_CHANGE(dcdcStartDelay,          PROFILE_DCDCSTARTDELAY_RX_ID    )
    CAN_CHANGE(dcdcStopDelay,           PROFILE_DCDCSTOPDELAY_RX_ID     )
    CAN_CHANGE(dcdc_taps,               PROFILE_DCDC_TAPS_RX_ID         )
    CAN_CHANGE(imdStartDelay,           PROFILE_IMDSTARTDELAY_RX_ID     )
    CAN_CHANGE(internalOverTemp,        PROFILE_INTERNALOVERTEMP_RX_ID  )
    CAN_CHANGE(CANnoAck,                PROFILE_CANNOACK_RX_ID          )
    CAN_CHANGE(extendedSerial,          PROFILE_EXTENDSERIAL_RX_ID      )
    CAN_CHANGE(CANtxSize,               PROFILE_CANTXSIZE_RX_ID         )
    CAN_CHANGE(CANrxSize,               PROFILE_CANRXSIZE_RX_ID         )
    CAN_CHANGE(SerialBaud,              PROFILE_SERIALBAUD_RX_ID        )
    CAN_CHANGE(SerialTxSize,            PROFILE_SERIALTXSIZE_RX_ID      )
    CAN_CHANGE(XbeeBaud,                PROFILE_XBEEBAUD_RX_ID          )
    CAN_CHANGE(XbeeTxSize,              PROFILE_XBEETXSIZE_RX_ID        )
    CAN_CHANGE(XbeeRxSize,              PROFILE_XBEERXSIZE_RX_ID        )

    if (!parsed) return false;

    if (msg.id == PROFILE_NOMINALCAPACITY_RX_ID )   return glvBat.changeCapacity(param->nominalCapacity)?1:-1;
    if (msg.id == PROFILE_GLVBATTAPS_RX_ID      )   return glvBat.size(param->glvBat_taps)?1:-1;
    if (msg.id == PROFILE_DCDC_TAPS_RX_ID       )   return dcdc.size(param->dcdc_taps)?1:-1;
    if (msg.id == PROFILE_CANTXSIZE_RX_ID       )   return can.txSize(param->CANtxSize)?1:-1;
    if (msg.id == PROFILE_CANRXSIZE_RX_ID       )   return can.rxSize(param->CANrxSize)?1:-1;
    if (msg.id == PROFILE_SERIALBAUD_RX_ID      )   pc.baud(param->SerialBaud);
    if (msg.id == PROFILE_SERIALTXSIZE_RX_ID    )   return (pc.txBufferSetSize(param->SerialTxSize) == 0)?1:-1;
    if (msg.id == PROFILE_XBEEBAUD_RX_ID        )   xbeeRelay.baud(param->XbeeBaud);
    if (msg.id == PROFILE_XBEETXSIZE_RX_ID      )   return (xbeeRelay.txSize(param->XbeeTxSize))?1:-1;
    if (msg.id == PROFILE_XBEERXSIZE_RX_ID      )   return (xbeeRelay.rxSize(param->XbeeRxSize))?1:-1;

    return true;
}
// Check for incoming messages from the xbees, relay them to the CAN function and send them out on the bus
bool receiveMsgXbee()
{
    CANMessage msg;
    if (xbeeRelay.receive(msg)) {                       // Incoming CAN message string received
        if (!can.txWrite(msg)) op->faultCode |= CAN_FAULT;    // Send it out on the CAN bus
        serviceCAN(&msg);                               // Send it into the local serviceCAN routine
        return true;
    } else return false;
}


void inCommands::thread_getInputs(void const* args)
{
    while(1) {
        serviceCAN(0);
        receiveMsgXbee();

        int ret = serviceSerial();
        if (ret == -1) tempData.parseGoodChar = 'x';
        if (ret == 1)  tempData.parseGoodChar = 251;
        osSignalSet((osThreadId)(tempData.wdtThreadId), 1<<2);      // Signal watchdog thread
    }
}