System Management code
Dependencies: mbed CANBuffer Watchdog MODSERIAL mbed-rtos xbeeRelay IAP
Fork of SystemManagement by
inCommands/inCommands.cpp
- Committer:
- pspatel321
- Date:
- 2015-02-11
- Revision:
- 39:ddf38df9699e
- Parent:
- 38:8efacce315ae
File content as of revision 39:ddf38df9699e:
#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("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; 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; } xbee.receive(msg); // 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 // Clear fault conditions case (CLEAR_RX_ID): if (msg.len == 0) { // No data byte 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 int index=-2; for (int i = 0; i < NUM_STORED_PROFILES+1; i++) { // Get the profile number if (msg.data[1] == (1<<i)) index=i; } if (msg.data[1] == 1<<6) index=-1; // Special case for Freeze if (index == -2) { // Not matched to anything, fail CAN_FAIL } if (msg.data[0] == 0) { // Load profile from a flash location to RAM 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 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_RX_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_RX_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_RX_ID: if (msg.len != sizeof(char)) return false; REFRESH_TIMEOUT(AIRS_CLOSED) if (msg.data[0] & 1<<3) { // AIRs closed? op->signals |= AIRS_CLOSED; } else { op->signals &= ~AIRS_CLOSED; } return true; case CHARGER_ERR_RX_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(amsStartDelay, PROFILE_AMSSTARTDELAY_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 ) 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; 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 } }