System Management code
Dependencies: mbed CANBuffer Watchdog MODSERIAL mbed-rtos xbeeRelay IAP
Fork of SystemManagement by
outDiagnostics/outDiagnostics.cpp
- Committer:
- pspatel321
- Date:
- 2015-02-11
- Revision:
- 39:ddf38df9699e
- Parent:
- 38:8efacce315ae
File content as of revision 39:ddf38df9699e:
#include "outDiagnostics.h"
#include "outMacros.h"
Timer serialLoop;
Timer CANloop;
int serialTime_ms = 0;
int CANtime_ms = 0;
const int max_charsPerLine = 80; // Max chars per line of printed information
// Use the txEmpty interrupt from MODSERIAL to pace the thread so it always runs as fast as possible
osThreadId serialID=0;
void empty(MODSERIAL_IRQ_INFO *q)
{
osSignalSet(serialID, 1);
}
// Output a string to the MODSERIAL tx buffer, wait when buffer full
void AddtoBuffer(char *str, bool newline=true)
{
const int waitT = TX_SIZE * CHAR_TIME*1000; // Max wait time to empty the tx buffer = max time to send out all chars
int len = strlen(str);
int times = newline ? (len + 2) : (len); // If newline requested, add 2 chars for "\r\n"
for (int i = 0; i < times; i++) {
if (!pc.writeable()) { // Keep writing till it fills
osSignalSet((osThreadId)(tempData.wdtThreadId), 1<<3); // Check-in with watchdog thread
Thread::signal_wait(1, waitT); // Wait for empty signal from MODSERIAL tx empty interrupt, timeout in waitT ms
}
if (i < len) pc.putc(str[i]); // Print the string
else if (i == len) pc.putc('\r'); // Add carriage return
else if (i > len) pc.putc('\n'); // Add newline
}
}
// Print to internal string buffer, pad to maxLen chars and center it with char pad, str must be null terminated!
void padCenter(int LineLen, char *str, char pad)
{
static char line[max_charsPerLine+5]; // String buffer to work with one line at a time
int len = strlen(str); // Length of input string
int padL = (LineLen-len)/2; // How many pad chars needed on left side?
for (int i=0; i<padL; i++) line[i] = pad; // Fill in the left padding chars
strcpy(line+padL, str); // Copy to line string
for (int i = padL+len; i<LineLen; i++) line[i] = pad; // Fill remaining with padding chars
line[LineLen-1] = '\0'; // Add null terminator
AddtoBuffer(line);
}
// Generates the serial dashboard, uses MODSERIAL, self-paced (thread yields when buffer is full, resumes when empty)
void outDiagnostics::thread_serialOut(void const *args)
{
serialID = Thread::gettid(); // Record thread ID so empty() can signal this thread
char temp[max_charsPerLine+5]; // String buffer to sprintf into, max 1 line
pc.attach(&empty, MODSERIAL::TxEmpty); // Attach the tx empty interrupt which paces this thread
pc.printf("\033[2J"); // Clear the screen to get rid of reset message
// Use these bools to track changes in display mode between iterations
bool lastModeExtended = param->extendedSerial;
bool inExtendedMode = param->extendedSerial;
// For determining where the data displayed is coming from
bool freeze = false; // Not a freeze frame
bool notLiveProfile = false; // Live data
const char barSpace[4] = { ' ', 179, ' ', 0 }; // Commonly used string with char 179
OperatingInfo* dashOp = op;
Profile* dashParam = param;
serialLoop.reset();
serialLoop.start(); // Start the counter for tracking serial loop time
while(1) {
serialTime_ms = serialLoop.read_ms(); // Update loop timer, reset for next loop
serialLoop.reset();
// Update display mode, change detection used at end of while(1) loop
lastModeExtended = inExtendedMode;
inExtendedMode = param->extendedSerial;
// Determine whether to display freeze frame, change pointers accordingly
if (tempData.freeze != NULL) {
freeze = true; // Indicate showing freeze
dashOp = &(tempData.freeze->op.op); // Point to freeze
dashParam = &(tempData.freeze->param.param); // Point to freeze
// Determine whether to display a non-live profile, change pointers accordingly
} else if (tempData.viewProfile != NULL && tempData.viewProfileNum != -2) {
dashParam = tempData.viewProfile; // Point to other profile
notLiveProfile = true; // Indicate notLive
// Show live data only
} else {
freeze = false; // Not freeze
notLiveProfile = false; // Live profile
dashOp = op; // Point to live RAM data
dashParam = param; // Point to live RAM data
}
sprintf(temp, "\033[0;0H\033[0;0H");
AddtoBuffer(temp, false); // Move to 0,0, do not append newline
DIVIDER_LINE
TITLE(" Penn Electric Racing - REV0 System Management Controller Dashboard ")
DIVIDER_LINE
int tempLength=0;
tempLength += sprintf(temp, "Command Input:%c %s%c", tempData.parseGoodChar, tempData.inputStr, 176); // Command input: print header, reply, input string, and cursor marker
for (int i = 0; i < max_charsPerLine - tempLength - 1; i++) { // Fill in the rest of the line with blanks
tempLength += sprintf(temp+tempLength, " "); // Append spaces
}
AddtoBuffer(temp); // Add this to the chunk
const char profile[NUM_STORED_PROFILES+2][8] = {"Freeze", "Default", "1", "2", "3"};
if (inExtendedMode) {
// Parameters Section
snprintf(temp, max_charsPerLine, " Configuration Parameters %s%s%s%s", freeze?"(Viewing Freeze of Last Fault) ":"", (notLiveProfile && !freeze)?" (Viewing Profile ":"", (notLiveProfile && !freeze)?profile[tempData.viewProfileNum+1]:"", (notLiveProfile && !freeze)?") ":"");
TITLE(temp)
snprintf(temp, max_charsPerLine, "GLVchar: %5.2fA%sGLVdisch: %5.2fA%sGLVnomCap: %5.2fAh%sGLVtaps: %3d", dashParam->chargeCurrent,barSpace, dashParam->dischargeCurrent,barSpace, dashParam->nominalCapacity,barSpace, dashParam->glvBat_taps);
ADD_SPRINTF_LINE
snprintf(temp, max_charsPerLine, "dcdcThres: %5.2fA%sdcdcOver: %5.2fA%sdcdcStart: %5.2fs %sdcdcStop: %5.2fs", dashParam->dcdcThreshold,barSpace, dashParam->dcdcOverCurrent,barSpace, dashParam->dcdcStartDelay,barSpace, dashParam->dcdcStopDelay);
ADD_SPRINTF_LINE
snprintf(temp, max_charsPerLine, "dcdcTaps: %3d %simdStart: %5.1fs%samsStart: %5.1fs %sIntOverT: %5.1fC", dashParam->dcdc_taps,barSpace, dashParam->imdStartDelay,barSpace, dashParam->amsStartDelay,barSpace, dashParam->internalOverTemp);
ADD_SPRINTF_LINE
snprintf(temp, max_charsPerLine, "CANtxSize: %4d %sCANrxSize: %4d %sSerialBaud: %6d%sSerialTx: %5d", dashParam->CANtxSize,barSpace, dashParam->CANrxSize,barSpace, dashParam->SerialBaud,barSpace, dashParam->SerialTxSize);
ADD_SPRINTF_LINE
BLANK_LINE
}
snprintf(temp, max_charsPerLine, " Operating Info %s", freeze?"(Viewing Freeze of Last Fault) ":"");
TITLE(temp)
// Operating mode
const char modeStr[3][8] = {"FAULT", "OKAY", "INVALID"};
tempLength = 0;
tempLength += sprintf(temp+tempLength, "Sloop: %3dms CANloop: %3dms Time: %s", serialTime_ms, CANtime_ms, ctime(&dashOp->SysTime));
tempLength--;
char modeN = dashOp->mode;
if (modeN == FAULT) modeN=0;
else if (modeN == OKAY) modeN=1;
else modeN=2;
tempLength += sprintf(temp+tempLength, " Uptime: %5ds", dashOp->SysTime - dashOp->startTime);
ADD_SPRINTF_LINE
tempLength = snprintf(temp, max_charsPerLine, "Profile: %7s%10s Mode: %7s%6s Faults: ", profile[dashOp->profileIndex+1], dashOp->profileModded?", MODIFIED":"", modeStr[modeN], dashOp->faultCode?"+ERROR":"");
// Fault codes
const char topErrStr[12][20] = {"WATCHDOG", "BROWNOUT", "CAN_FAULT", "INT_OVER_TEMP", "IMD_LATCH", "AMS_LATCH", "IMD_FAULT", "DCDC_FAULT", "GLVBAT_FAULT", "FREEZE_FRAME"};
int numFaults = 0;
for (int i = 0; i < sizeof(dashOp->faultCode)*8; i++) {
if (dashOp->faultCode & (1<<i)) numFaults++;
}
if (numFaults == 0) tempLength+=sprintf(temp+tempLength, "No Faults"); // Start new appending chain string, faults not present
else tempLength+=sprintf(temp+tempLength, "(%d 0x%04x)", numFaults, dashOp->faultCode); // Start new appending chain string, faults present
ADD_SPRINTF_LINE
tempLength = 0;
temp[0] = 0;
// Print max number of strings that will fit per line, then dump line and continue till finished all error flags
int num = 0;
for (int i = 0; i < 29; i++) {
if (dashOp->faultCode & (1<<i)) { // Fault found
// Room for fault string?
if (max_charsPerLine-tempLength > strlen(topErrStr[i])+2) {
// Yes, append
tempLength += sprintf(temp+tempLength, "%s ", topErrStr[i]);
num++;
} else {
// No, Dump then start new line
ADD_SPRINTF_LINE
tempLength = 0;
tempLength += sprintf(temp+tempLength, "%s ", topErrStr[i]);
num++;
}
}
if (num >= numFaults || numFaults == 0) {
// Done printing all faults
tempLength = 0;
num = 0;
if (numFaults != 0) {
ADD_SPRINTF_LINE
}
break;
}
}
BLANK_LINE
snprintf(temp, max_charsPerLine, "ShtdwnCrct: %6s %s AIRs: %6s %s Charger: %4s %s IntTemp: %5.1f", (dashOp->signals & SHUTDOWN_CLOSED)?"CLOSED":"OPEN", barSpace, (dashOp->signals & AIRS_CLOSED)?"CLOSED":"OPEN", barSpace, (dashOp->signals & CHARGER_DET)?"DET":"NDET", barSpace, dashOp->internalTemp);
ADD_SPRINTF_LINE
BLANK_LINE
snprintf(temp, max_charsPerLine, " GLV Battery %s", freeze?"(Viewing Freeze of Last Fault) ":"");
TITLE(temp)
sprintf(temp, "Current: %4.3fA %s Cap: %4.3fAh %s Ah: %4.3fAh %s SOC: %5.3f", dashOp->glvBat.current, barSpace, dashOp->glvBat.capacity, barSpace, dashOp->glvBat.Ah, barSpace, dashOp->glvBat.SOC);
ADD_SPRINTF_LINE
// GLV Fault codes
const char glvBatErrStr[12][20] = {"OVER_CHARGE_I", "OVER_DISCHARGE_I", "CAP_INIT", "SOC_INIT" };
numFaults = 0;
for (int i = 0; i < sizeof(dashOp->glvBat.error)*8; i++) {
if (dashOp->glvBat.error & (1<<i)) numFaults++;
}
if (numFaults == 0) tempLength+=sprintf(temp+tempLength, "No Faults"); // Start new appending chain string, faults not present
else tempLength+=sprintf(temp+tempLength, "(%d 0x%04x)", numFaults, dashOp->glvBat.error); // Start new appending chain string, faults present
ADD_SPRINTF_LINE
tempLength = 0;
temp[0] = 0;
// Print max number of strings that will fit per line, then dump line and continue till finished all error flags
num = 0;
for (int i = 0; i < 29; i++) {
if (dashOp->glvBat.error & (1<<i)) { // Fault found
// Room for fault string?
if (max_charsPerLine-tempLength > strlen(glvBatErrStr[i])+2) {
// Yes, append
tempLength += sprintf(temp+tempLength, "%s ", glvBatErrStr[i]);
num++;
} else {
// No, Dump then start new line
ADD_SPRINTF_LINE
tempLength = 0;
tempLength += sprintf(temp+tempLength, "%s ", glvBatErrStr[i]);
num++;
}
}
if (num >= numFaults || numFaults == 0) {
// Done printing all faults
tempLength = 0;
num = 0;
if (numFaults != 0) {
ADD_SPRINTF_LINE
}
break;
}
}
BLANK_LINE
snprintf(temp, max_charsPerLine, " DC-DC Converter %s", freeze?"(Viewing Freeze of Last Fault) ":"");
TITLE(temp)
char DCDC = dashOp->dcdc.status;
const char dcdcModesStr[5][12] = {"INVALID","POWER-UP","POWER-DOWN","SET-ON","SET-OFF"};
int dcdcMode = 0;
if (DCDC & POWER_UP) dcdcMode = 1;
else if (DCDC & POWER_DOWN) dcdcMode = 2;
else if (DCDC & SET_ON) dcdcMode = 3;
else if (!(DCDC & SET_ON)) dcdcMode = 4;
sprintf(temp, "Active: %3s %s Mode: %10s %s Status: 0x%02x", (DCDC & CONV_ON)?"YES":"NO", barSpace, dcdcModesStr[dcdcMode], barSpace, DCDC);
ADD_SPRINTF_LINE
sprintf(temp, "Current: %5.2fA %s Overcurrent: %3s %s SensorFault: %3s", dashOp->dcdc.current,barSpace, (DCDC & OVER_CURRENT)?"ERR":"OK",barSpace, (DCDC & SENSOR_FAULT)?"ERR":"OK");
ADD_SPRINTF_LINE
BLANK_LINE
snprintf(temp, max_charsPerLine, " DC-DC PWM Channels %s", freeze?"(Viewing Freeze of Last Fault) ":"");
TITLE(temp)
sprintf(temp, "Actual: FAN1: %5.3f FAN2: %5.3f %s PUMP1: %5.3f PUMP2: %5.3f", dashOp->dcdc.actual.fan1, dashOp->dcdc.actual.fan2, barSpace, dashOp->dcdc.actual.pump1, dashOp->dcdc.actual.pump2);
ADD_SPRINTF_LINE
sprintf(temp, "Requested: FAN1: %5.3f FAN2: %5.3f %s PUMP1: %5.3f PUMP2: %5.3f", dashOp->dcdc.request.fan1, dashOp->dcdc.request.fan2, barSpace, dashOp->dcdc.request.pump1, dashOp->dcdc.request.pump2);
ADD_SPRINTF_LINE
BLANK_LINE
snprintf(temp, max_charsPerLine, " IMD %s", freeze?"(Viewing Freeze of Last Fault) ":"");
TITLE(temp)
char imdStatN=6;
if (dashOp->imd.status & OFF) imdStatN=0;
if (dashOp->imd.status & NORMAL) imdStatN=1;
if (dashOp->imd.status & UNDERVOLT) imdStatN=2;
if (dashOp->imd.status & SPEEDSTART) imdStatN=3;
if (dashOp->imd.status & ERROR) imdStatN=4;
if (dashOp->imd.status & GROUNDERR) imdStatN=5;
const char IMDstr[7][12] = {"OFF","NORMAL","UNDERVOLT","SPEEDSTART","ERROR","GROUNDERR","INVALID"};
sprintf(temp, "Status: %10s Resistance: %7.0fKohm Error: %3s", IMDstr[imdStatN], dashOp->imd.resistance/1.0e3, dashOp->imd.error?"ERR":"OK");
ADD_SPRINTF_LINE
BLANK_LINE
snprintf(temp, max_charsPerLine, " Latch Monitors %s", freeze?"(Viewing Freeze of Last Fault) ":"");
TITLE(temp)
char AMSerr = dashOp->latch.ams;
char IMDerr = dashOp->latch.imd;
sprintf(temp, "AMS - OK: %4s Latch: %3s SoftFault: %3s HardFault: %3s", (AMSerr & OK_FAULT)?"LOW":"HIGH", (AMSerr & LATCHED_HARD)?"ERR":"OK", (AMSerr & LATCHED_SOFT)?"ERR":"OK", (AMSerr & HARD_FAULT)?"ERR":"OK");
ADD_SPRINTF_LINE
sprintf(temp, "IMD - OK: %4s Latch: %3s SoftFault: %3s HardFault: %3s", (IMDerr & OK_FAULT)?"LOW":"HIGH", (IMDerr & LATCHED_HARD)?"ERR":"OK", (IMDerr & LATCHED_SOFT)?"ERR":"OK", (IMDerr & HARD_FAULT)?"ERR":"OK");
ADD_SPRINTF_LINE
BLANK_LINE
snprintf(temp, max_charsPerLine, " Shutdown Switches %s", freeze?"(Viewing Freeze of Last Fault) ":"");
TITLE(temp)
char switches = dashOp->switchState;
const char switchNames[12][26] = {"FUSE","AMS LATCH","IMD LATCH","PCM RELAY","BRAKE PLAUSIBILITY RELAY","LEFT E-STOP","INERTIA SWITCH","BRAKE OVER-TRAVEL SWITCH","COCKPIT E-STOP","RIGHT E-STOP","HVD","TSMS"};
if (switches == 0) sprintf(temp, "All switches are CLOSED.");
else sprintf(temp, "%s is OPEN.", switchNames[switches-1]);
ADD_SPRINTF_LINE
// Erase screen every few counts to remove glitches
static int count = 0;
if (count % 50 == 0 || lastModeExtended != inExtendedMode) {
sprintf(temp, "\033[2J");
AddtoBuffer(temp, false); // Clear the screen
}
count++;
}
}
void outDiagnostics::thread_canOut(void const *args)
{
bool lastCANnoAck = param->CANnoAck; // Get initial noAck mode setting
bool thisCANnoAck = param->CANnoAck;
if (param->CANnoAck) { // Turn on noack mode
can.mode(NoAck);
} else { // Setup normal mode
can.mode(Normal);
}
can.mode(FIFO);
CANloop.reset();
CANloop.start();
while(1) {
CANtime_ms = CANloop.read_ms();
CANloop.reset();
// Update last and this variables to check for changes in noAck mode
lastCANnoAck = thisCANnoAck;
thisCANnoAck = param->CANnoAck;
if (thisCANnoAck && !lastCANnoAck) { // NoAck mode turn on
can.mode(NoAck);
can.mode(FIFO);
can.txFlush(); // Must flush buffer when switching modes, or else buffer gets stuck
}
if (!thisCANnoAck && lastCANnoAck) { // NoAck mode turn off
can.mode(Normal);
can.mode(FIFO);
can.txFlush(); // Must flush buffer when switching modes, or else buffer gets stuck
}
// OPERATING DIAGNOSTICS
// Error Frame
CAN_SINGLE(faultCode, FAULTCODE_TX_ID)
// Mode
CAN_SINGLE(mode, MODE_TX_ID)
// Flags
CAN_SINGLE(signals, SIGNALS_TX_ID)
// Profile
char byte=0;
byte = (op->profileIndex != -1)? 1 << op->profileIndex : 1<<6;
byte |= (op->profileModded) ? 1<<7 : 0; // Mark the last bit of the byte if the profile was modified (OR'd with profile id from above)
SEND_CAN_SINGLE(byte, PROFILE_TX_ID);
// Time
CAN_PAIR(SysTime, startTime, TIME_TX_ID)
// Internal temperature
CAN_SINGLE(internalTemp, TEMP_TX_ID)
// GLV Battery
CAN_SINGLE(glvBat.current, GLV_CURRENT_TX_ID)
CAN_SINGLE(glvBat.capacity, GLV_CAPACITY_TX_ID)
CAN_SINGLE(glvBat.Ah, GLV_AH_TX_ID)
CAN_SINGLE(glvBat.SOC, GLV_SOC_TX_ID)
CAN_SINGLE(glvBat.error, GLV_ERROR_TX_ID)
// DC-DC Converter
CAN_SINGLE(dcdc.current, DCDC_CURRENT_TX_ID)
CAN_SINGLE(dcdc.status, DCDC_STATUS_TX_ID)
// PWM Channels
CAN_PAIR(dcdc.actual.fan1, dcdc.actual.fan2, PWM_FAN_TX_ID)
CAN_PAIR(dcdc.actual.pump1, dcdc.actual.pump2, PWM_PUMP_TX_ID)
// IMD
CAN_SINGLE(imd.status, IMD_STATUS_TX_ID)
CAN_SINGLE(imd.resistance, IMD_RESIST_TX_ID)
// Latches
CAN_SINGLE(latch.imd, IMD_LATCH_TX_ID)
CAN_SINGLE(latch.ams, AMS_LATCH_TX_ID)
// Shutdown Switches
uint16_t tmp=0;
if (op->switchState != 0) tmp |= 1 << (op->switchState-1);
SEND_CAN_SINGLE(tmp, SWITCHES_TX_ID);
osSignalSet((osThreadId)(tempData.wdtThreadId), 1<<4); // Signal watchdog thread
Thread::wait(CAN_LOOP*1000);
}
}
