System Management code

Dependencies:   mbed CANBuffer Watchdog MODSERIAL mbed-rtos xbeeRelay IAP

Fork of SystemManagement by Martin Deng

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