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
}
}
