Penn Electric Racing / Mbed 2 deprecated SystemManagement

Dependencies:   mbed CANBuffer Watchdog MODSERIAL mbed-rtos xbeeRelay IAP

Fork of SystemManagement by Martin Deng

outDiagnostics/outDiagnostics.cpp

Committer:
pspatel321
Date:
2015-01-22
Revision:
36:0afc0fc8f86b
Parent:
34:18bcf276d3bf
Child:
38:8efacce315ae

File content as of revision 36:0afc0fc8f86b:

#include "outDiagnostics.h"

osThreadId serialID = 0;                         // RTOS thread ID of thread_serialOut
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
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);
}


// Macros for working with the strings
#define ADD_SPRINTF_LINE    padCenter(max_charsPerLine, temp, ' ');   // Cetner the string, then add newlines, and add to chunk
#define DIVIDER_LINE        padCenter(max_charsPerLine, "", 196);     // Generate a line full of divider char 196, add to chunk
#define TITLE(string)       padCenter(max_charsPerLine, string, 196); // Generate a title line (centered, surrounded by char 196), add to chunk
#define BLANK_LINE          padCenter(max_charsPerLine, "", ' ');     // Generate a line full of spaces (blank), add to chunk
#define BOOL(VAR)           (VAR)?"ERR":"OK"

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

    tempData.parseGoodChar = ' ';
    tempData.inputStr[0] = 0;
    
    Timer serialLoop;                                // Timer to track the serial loop time, since this thread paces itself
    int serialTime_ms = 0;
    serialLoop.reset();
    serialLoop.start();                              // Start the counter for tracking serial loop time

    //const char barSpace[4] = { ' ', 179, ' ', 0 };   // Commonly used string with char 179

    while(1) {

        serialTime_ms = serialLoop.read_ms();        // Update loop timer, reset for next loop
        serialLoop.reset();

        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
        
        TITLE(" GLV Battery ")
        BLANK_LINE
        sprintf(temp, "Current: %4.3fA          Capacity: %4.3fAh", data.glvCurrent, data.glvCapacity);
        ADD_SPRINTF_LINE
        sprintf(temp, "Amphours: %4.3fAh    SOC: %5.3f    Overcurrent: %s", data.glvAmphours, data.glvSOC, BOOL(data.glvOverCurrent));
        ADD_SPRINTF_LINE

        BLANK_LINE
        TITLE(" DC-DC Converter ")
        BLANK_LINE
        char DCDC = data.dcdcStatus;
        char dcdcModesStr[5][12] = {"INVALID","POWER-UP","POWER-DOWN","ON","OFF"};
        int dcdcMode = 0;
        if (DCDC & PowerUp)        dcdcMode = 1;
        else if (DCDC & PowerDown) dcdcMode = 2;
        else if (DCDC & SetOn)     dcdcMode = 3;
        else if (!(DCDC & SetOn))  dcdcMode = 4;
        sprintf(temp, "Active: %3s     Mode: %10s     AIRS: %6s     StatusByte: 0x%02x", (DCDC & ConvOn)?"YES":"NO", dcdcModesStr[dcdcMode], CANdata.airsClosed?"CLOSED":"OPEN", DCDC);
        ADD_SPRINTF_LINE
        sprintf(temp, "Current: %5.2fA     Overcurrent: %3s     SensorFault: %3s", data.dcdcCurrent, BOOL(DCDC & OverCurrent), BOOL(DCDC & SensorFault));
        ADD_SPRINTF_LINE
        sprintf(temp, "StartFault: %3s     StopFault:   %3s     CritErrors:  %3s", BOOL(DCDC & FailStart), BOOL(DCDC & FailStop), BOOL(data.dcdcError));
        ADD_SPRINTF_LINE

        BLANK_LINE
        TITLE(" PWM Channels ")
        BLANK_LINE
        sprintf(temp, "Actual:     FAN1: %5.3f   FAN2: %5.3f   PUMP1: %5.3f   PUMP2: %5.3f", data.dcdcFan1Duty, data.dcdcFan2Duty, data.dcdcPump1Duty, data.dcdcPump2Duty);
        ADD_SPRINTF_LINE
        sprintf(temp, "Requestd:   FAN1: %5.3f   FAN2: %5.3f   PUMP1: %5.3f   PUMP2: %5.3f", CANdata.dcdcFan1Duty, CANdata.dcdcFan2Duty, CANdata.dcdcPump1Duty, CANdata.dcdcPump2Duty);
        ADD_SPRINTF_LINE

        BLANK_LINE
        TITLE(" IMD ")
        BLANK_LINE
        const char IMDstr[7][12] = {"OFF","NORMAL","UNDERVOLT","SPEEDSTART","ERROR","GROUNDFLT","INVALID"};
        sprintf(temp, "Status: %10s Resistance: %7.0fkohm CritError: %3s",IMDstr[data.imdStatus], data.imdResistance/1e3, BOOL(data.imdError));
        ADD_SPRINTF_LINE

        BLANK_LINE
        TITLE(" Latch Circuit Monitors ")
        BLANK_LINE
        char AMSerr = data.AMSlatchError;
        char IMDerr = data.IMDlatchError;
        sprintf(temp, "AMS -    OK: %4s Latch: %3s   SoftFault: %3s   HardFault: %3s", (AMSerr & 1)?"LOW":"HIGH", BOOL(AMSerr & 2), BOOL(AMSerr & 4), BOOL(AMSerr & 8));
        ADD_SPRINTF_LINE
        sprintf(temp, "IMD -    OK: %4s Latch: %3s   SoftFault: %3s   HardFault: %3s", (IMDerr & 1)?"LOW":"HIGH", BOOL(IMDerr & 2), BOOL(IMDerr & 4), BOOL(IMDerr & 8));
        ADD_SPRINTF_LINE

        BLANK_LINE
        TITLE(" Shutdown Switches ")
        BLANK_LINE
        char switches = data.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

        BLANK_LINE
        TITLE(" Telemetry ")
        BLANK_LINE
        sprintf(temp, "Channel 1 -    MessagesIn: %5d   MessagesOut: %5d", xbeeRelay.counterX1in, xbeeRelay.counterX1out);
        ADD_SPRINTF_LINE
        sprintf(temp, "Channel 2 -    MessagesIn: %5d   MessagesOut: %5d", xbeeRelay.counterX2in, xbeeRelay.counterX2out);
        ADD_SPRINTF_LINE

        BLANK_LINE
        TITLE(" Miscellaneous ")
        BLANK_LINE
        sprintf(temp, "Temp: %5.1fC %3s canFault: %3s Watchdog: %3s ErrorCode: 0x%02x SerialTime: %3dms", data.internalTemp, BOOL(data.internalOverTemp), BOOL(data.canFault), BOOL(data.watchdogReset), data.errorFrame, serialTime_ms);
        ADD_SPRINTF_LINE

        // Erase screen every 5sec to remove glitches
        static int count=0;
        if (count%50==0) {
            pc.printf("\033[2J");        // Clear the screen
        }
        count++;
    }
}

// Macros to streamline can bus message formatting and sending
#define SEND_CAN(LEN, ID)               msg.len=LEN; msg.id=ID; msg.type = CANData; msg.format = CANStandard; if (!can.txWrite(msg)) data.canFault = true; xbeeRelay.send(msg);
#define FLOAT(DATA, ID)                 *((float*)((void*)&msg.data[0]))    = DATA;    SEND_CAN(sizeof(float), ID)
#define UINT(DATA, ID)                  *((uint32_t*)((void*)&msg.data[0])) = DATA;    SEND_CAN(sizeof(uint32_t), ID)
#define FLOAT_PAIR(DATA1, DATA2, ID)    *((float*)((void*)&msg.data[0]))    = DATA1;   *((float*)((void*)&msg.data[4]))    = DATA2;   SEND_CAN(2*sizeof(float), ID)
#define UINT_PAIR(DATA1, DATA2, ID)     *((uint32_t*)((void*)&msg.data[0])) = DATA1;   *((uint32_t*)((void*)&msg.data[4])) = DATA2;   SEND_CAN(2*sizeof(uint32_t), ID)
#define CHAR(DATA, ID)                  msg.data[0]                         = DATA;    SEND_CAN(sizeof(char), ID)

void outDiagnostics::thread_canOut(void const *args)
{
    CANMessage msg;
    while(1) {

        // Sys Mgmt Error Frame
        CHAR(data.errorFrame, SYS_ERROR_ID)

        // Xbee1 Counter
        UINT_PAIR(xbeeRelay.counterX1in, xbeeRelay.counterX1out, SYS_XBEE1_ID)

        // Xbee2 Counter
        UINT_PAIR(xbeeRelay.counterX2in, xbeeRelay.counterX2out, SYS_XBEE2_ID)

        // Internal temperature
        FLOAT(data.internalTemp, SYS_TEMP_ID)

        // GLV Battery
        FLOAT(data.glvCurrent, SYS_GLV_CURRENT_ID)
        FLOAT(data.glvCapacity, SYS_GLV_CAPACITY_ID)
        FLOAT(data.glvAmphours, SYS_GLV_AH_ID)
        FLOAT(data.glvSOC, SYS_GLV_SOC_ID)
        
        // DC-DC Converter
        FLOAT(data.dcdcCurrent, SYS_DCDC_CURRENT_ID)
        CHAR(data.dcdcStatus, SYS_DCDC_STATUS_ID)

        // PWM Channels
        FLOAT_PAIR(data.dcdcFan1Duty, data.dcdcFan2Duty, SYS_PWM_FAN_ID)
        FLOAT_PAIR(data.dcdcPump1Duty, data.dcdcPump2Duty, SYS_PWM_PUMP_ID)

        // IMD
        FLOAT(data.imdStatus, SYS_IMD_STATUS_ID)
        CHAR(data.imdResistance, SYS_IMD_RESIST_ID)

        // Latches
        CHAR(data.IMDlatchError, SYS_IMD_LATCH_ID)
        CHAR(data.AMSlatchError, SYS_AMS_LATCH_ID)

        // Shutdown Switches
        CHAR(data.switchState, SYS_SWITCHES_ID)
        
        osSignalSet((osThreadId)(tempData.wdtThreadId), 1<<4);      // Signal watchdog thread
        Thread::wait(CAN_LOOP*1000);
    }
}