Erick / Mbed 2 deprecated ICE-F412

Dependencies:   mbed-rtos mbed

Revision:
0:61364762ee0e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ICE-Application/src/ConfigurationHandler/Controls/SensorErrorControl.cpp	Tue Jan 24 19:05:33 2017 +0000
@@ -0,0 +1,418 @@
+/******************************************************************************
+*
+* File:                SensorErrorControl.cpp
+* Desciption:          ICE Sensor Error Control class implementation
+*
+*****************************************************************************/
+#include "SensorErrorControl.h"
+#include "cJSON.h"
+#include "ModbusMasterApi.h"
+#include "utilities.h"
+#include "ICELog.h"
+#include <string>
+#include <iostream>
+#include <iomanip>
+#include <stdarg.h>
+
+using namespace std;
+
+// for debugging - this can be set via the console command:
+//      "debug-se 1
+bool debugSensorError = false;
+
+static void debug(const char *fmt, ...)
+{
+    if ( debugSensorError ) {
+        va_list vargs;
+        va_start(vargs, fmt);
+        vfprintf(stdout, fmt, vargs);
+        va_end(vargs);
+    }
+}
+
+//
+// method:              load
+// description:         load data from the control file
+//
+// @param[in]           _controlFile -> the control file
+// @return              true if loaded; false otherwise
+//
+bool SensorErrorControl::load(const std::string _controlFile)
+{
+    controlFile = _controlFile;
+
+    // read the data into a buffer
+    char dataBuf[MAX_FILE_SIZE];
+
+    bool rc = GLOBAL_mdot->readUserFile(controlFile.c_str(), (void *)dataBuf, sizeof(dataBuf));
+    if ( rc != true ) { 
+        logError("%s: failed to read %s", __func__, controlFile.c_str());
+        // caller should destroy the object
+        return false;
+    }
+
+    // validate the JSON formatted control data
+    if ( !validateControlData(dataBuf) ) {
+        logError("%s: failed to validate control data", __func__);
+        return false;
+    }
+
+    // copy the control data
+    copyControlData(dataBuf);
+    
+    ModbusValue val;
+    // validate the input 
+    if ( ModbusMasterReadRegister(input, &val) == false ) {
+        logError("%s failed to find input %s", id.c_str(), input.c_str());
+        return false;
+    }
+
+    isVirtualOutput = Util_isVirtualOutput(output) ? true : false;
+    return true;
+}
+
+//
+// @method:             validateControlData
+// @description:        vaqlidate the JSON formatted string
+//
+// @param[in]           buf -> JSON formatted string
+// @param[out]          none
+// @return              true if valid; false otherwise
+//
+bool SensorErrorControl::validateControlData(const char *buf)
+{
+    bool rc = true;
+
+    // parse the data
+    cJSON * root    = cJSON_Parse(buf);
+
+    if ( !cJSON_HasObjectItem(root, "id") ||
+            !cJSON_HasObjectItem(root, "priority") ||
+            !cJSON_HasObjectItem(root, "input") ||
+            !cJSON_HasObjectItem(root, "output") ||
+            !cJSON_HasObjectItem(root, "dutyCycle") ||
+            !cJSON_HasObjectItem(root, "interval") ) {
+        logError("%s: control file is missing expected tags", __func__);
+        rc = false;
+    }
+
+    cJSON_Delete(root);
+    return rc;
+}
+
+//
+// method:              copyControlData
+// description:         copy the data from the control file to the object
+//
+// @param[in]           buf -> JSON formatted string
+// @param[out]          none
+// @return              none
+//
+void SensorErrorControl::copyControlData(const char *buf)
+{
+    cJSON *root = cJSON_Parse(buf);
+
+    id                  = cJSON_GetObjectItem(root, "id")->valuestring;
+    priority            = atoi(cJSON_GetObjectItem(root, "priority")->valuestring);
+    input               = cJSON_GetObjectItem(root, "input")->valuestring;
+    output              = cJSON_GetObjectItem(root, "output")->valuestring;
+    dutyCycle.dutyCycle = atoi(cJSON_GetObjectItem(root, "dutyCycle")->valuestring);
+    dutyCycle.interval  = atoi(cJSON_GetObjectItem(root, "interval")->valuestring);
+
+    cJSON_Delete(root);
+}
+
+//
+// @method:             start
+// @description:        put the control in the start state
+//
+// @param[in]           none
+// @param[out]          none
+// @return              none
+//
+void SensorErrorControl::start(void)
+{
+    currentState = STATE_START;
+}
+
+//
+// @method              update
+// @description         run the simplified state machine
+//
+// @param[in]           none
+// @param[out]          none
+// @return              OK on success; error otherwise
+//
+SensorErrorControlError_t SensorErrorControl::update(void)
+{
+    SensorErrorControlError_t rc = SENSOR_ERROR_CONTROL_OK;
+
+    switch (this->currentState) {
+        case STATE_INIT:
+            // do nothing: control must be programmatically started
+            break;
+        case STATE_START:
+            if ( this->isSensorError() ) {
+                if ( dutyCycle.dutyCycle > 0 ) {
+                    this->startDutyTimer();
+                    this->currentState = STATE_DUTY_CYCLE_ON;
+                    sendMailToOutput(ACTION_CONTROL_ON);
+                    debug("\r%s: [STARTUP]->sensor error->[DUTY_CYCLE_ON] time=%lu\n", id.c_str(), time(0));
+                } else {
+                    // this is a fixed-off state
+                    this->stopDutyTimer();  // probably not needed
+                    this->currentState = STATE_DUTY_CYCLE_OFF;
+                    // tell the output task to turn off the relay
+                    sendMailToOutput(ACTION_CONTROL_OFF);
+                    debug("\r%s: [STARTUP]->sensor error (no duty)->[DUTY_CYCLE_OFF] %lu\n", id.c_str(), time(0));
+                }
+            } else {
+                // input signal is OK
+                debug("\r%s: [STARTUP]->OK->[OFF] %lu\n", id.c_str(), time(0));
+                this->currentState = STATE_CONTROL_OFF;
+            }
+            break;
+        case STATE_CONTROL_OFF:
+            if ( isSensorError() ) {
+                if ( dutyCycle.dutyCycle > 0 ) {
+                    this->startDutyTimer();
+                    this->currentState = STATE_DUTY_CYCLE_ON;
+                    sendMailToOutput(ACTION_CONTROL_ON);
+                    debug("\r%s: [STARTUP]->sensor error->[DUTY_CYCLE_ON] %lu\n", id.c_str(), time(0));
+                } else {
+                    // this is a fixed off state
+                    this->stopDutyTimer();  // probably not needed
+                    this->currentState = STATE_DUTY_CYCLE_OFF;
+                    // tell the output task to turn off the relay
+                    sendMailToOutput(ACTION_CONTROL_OFF);
+                    debug("\r%s: [STARTUP]->sensor error (no duty)->[DUTY_CYCLE_OFF] %lu\n", id.c_str(), time(0));
+                }
+            } else {
+                // input signal is OK, so don't do anything
+            }
+            break;
+        case STATE_DUTY_CYCLE_ON:
+            if ( !isSensorError() ) {
+                this->stopDutyTimer();
+                this->currentState = STATE_CONTROL_OFF;
+                this->unregisterControl();
+                debug("\r%s: [DUTY_CYCLE_ON]->no error->[OFF] %lu\n", id.c_str(), time(0));
+
+            } else if ( dutyOnExpired() )
+                if ( dutyCycle.dutyCycle < 100 ) {
+                    this->currentState = STATE_DUTY_CYCLE_OFF;
+                    sendMailToOutput(ACTION_CONTROL_OFF);
+                    debug("\r%s: [DUTY_CYCLE_ON]->on expired->[DUTY_CYCLE_OFF] %lu\n", id.c_str(), time(0));
+                } else {
+                    // fixed-on state
+                    // do nothing - we'll need to wait for the sensor error
+                    // to clear before something can happen
+                }
+            break;
+        case STATE_DUTY_CYCLE_OFF:
+            if ( !isSensorError() ) {
+                // there is no sensor error, so unregister the control and cleanup
+                this->stopDutyTimer();
+                this->currentState = STATE_CONTROL_OFF;
+                this->unregisterControl();
+                debug("\r%s: [DUTY_CYCLE_OFF]->no error->[OFF] %lu\n", id.c_str(), time(0));
+                stopDutyTimer();
+            } else if ( dutyOffExpired() ) {
+                if ( dutyCycle.dutyCycle > 0 ) {
+                    // we go back to the OFF state so the start and expiration
+                    // times are recalculated
+                    this->currentState = STATE_CONTROL_OFF;
+                    sendMailToOutput(ACTION_CONTROL_OFF);
+                    debug("\r%s: [DUTY_CYCLE_OFF]->off expired->[OFF] %lu\n", id.c_str(), time(0));
+                } else {
+                    // fixed-off state 
+                    // do nothing - we'll need to wait for the sensor error
+                    // to clear before something can happen
+                }
+            }
+            break;
+        default:
+            logError("%s: unknown state\n", __func__);
+            rc = SENSOR_ERROR_CONTROL_UNK_STATE;
+            break;
+    }
+    return rc;
+}
+
+//
+// @method:             isSensorError
+// @description:        reads the modbus register to get the error condition of
+//                      the input signal
+//
+// @param[in]           none
+// @param[out]          none
+// @return              true if in error; false otherwise
+//
+bool SensorErrorControl::isSensorError()
+{
+    ModbusValue val;
+
+    if ( ModbusMasterReadRegister(this->input, &val) != true ) {
+        logError("Failed to read %s\n", this->input.c_str());
+        return true;
+    } else if ( val.errflag ) {
+        // input is in sensor error
+        return true;
+    }
+    return false;
+}
+
+//
+// @method:             dutyOnExpired
+// @description:        returns true of duty ON time expired
+//
+// @param[in]           none
+// @param[out]          none
+// @return              true if expired; false otherwise
+//
+bool SensorErrorControl::dutyOnExpired(void)
+{
+    return (time(0) >= duty_timer.offTime);
+}
+
+//
+// @method:             dutyOffExpired
+// @description:        returns true of duty OFF time expired
+//
+// @param[in]           none
+// @param[out]          none
+// @return              true if expired; false otherwise
+//
+bool SensorErrorControl::dutyOffExpired(void)
+{
+    return (duty_timer.expirationTime < time(0));
+}
+
+//
+// method:          startDutyTimer
+// descrption:      start the sensor error duty-timer
+//
+// @param           none
+// @return          none
+//
+void SensorErrorControl::startDutyTimer(void)
+{
+    unsigned long currentTime = time(0);
+    unsigned long period      = dutyCycle.interval * 60;
+
+    duty_timer.offTime        = currentTime + ((double)period * ((double)dutyCycle.dutyCycle/100.0));
+    duty_timer.expirationTime = currentTime + period;
+
+    debug("\r%s:%s-> currentTime = %lu\n", __func__, id.c_str(), currentTime);
+    debug("\r%s:%s-> off Time    = %lu\n", __func__, id.c_str(), duty_timer.offTime);
+    debug("\r%s:%s-> expiration  = %lu\n", __func__, id.c_str(), duty_timer.expirationTime);
+}
+
+//
+// method:          stopDutyTimer
+// descrption:      stop the sensor error duty-timer
+//
+// @param           none
+// @return          none
+//
+void SensorErrorControl::stopDutyTimer(void)
+{
+    memset(&duty_timer, 0, sizeof(duty_timer));
+}
+
+//
+// method:          sendMailToOutput
+// description:     send a message to the output thread
+//
+// @param[in]       action -> on, off, etc.
+// @param[out]      none
+// @return
+//
+void SensorErrorControl::sendMailToOutput(OutputAction action)
+{
+
+    if ( isVirtualOutput ) {
+        debug("%s:%s updating the virtual output %s\n", __func__, id.c_str(), output.c_str());
+        ModbusMasterWriteRegister(this->output,
+                                  (action == ACTION_CONTROL_ON) ? 1.0 : 0.0);
+    } else {
+        debug("%s:%s sensor error control attempting to send action %d\n",
+              __func__, id.c_str(), action);
+
+        OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc();
+        memset(output_mail, 0, sizeof(OutputControlMsg_t));
+
+        output_mail->action         = action;
+        output_mail->controlType    = CONTROL_SENSOR_ERROR;
+        output_mail->priority       = this->priority;
+
+        strncpy(output_mail->input_tag,  this->input.c_str(),  sizeof(output_mail->input_tag)-1);
+        strncpy(output_mail->output_tag, this->output.c_str(), sizeof(output_mail->output_tag)-1);
+        strncpy(output_mail->id, this->id.c_str(), sizeof(output_mail->id)-1);
+
+        OutputMasterMailBox.put(output_mail);
+    }
+}
+
+//
+// @method:             unregisterControl
+// @description:        unregister the control with the output task
+// 
+// @param[in]           none
+// @param[out]          none
+// @return              none
+//
+void SensorErrorControl::unregisterControl(void)
+{
+    if ( isVirtualOutput ) {
+        debug("%s: %s attempting to unregister virtual output %s\n", __func__, id.c_str(), output.c_str());
+        ModbusMasterWriteRegister(output, 0.0);
+    } else {
+        debug("%s: %s attempting to unregister output %s\n",
+              __func__, id.c_str(), output.c_str());
+
+        OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc();
+        memset(output_mail, 0, sizeof(OutputControlMsg_t));
+
+        output_mail->action      = ACTION_CONTROL_UNREGISTER;
+        output_mail->controlType = CONTROL_FAILSAFE;
+        output_mail->priority     = this->priority;
+        strncpy(output_mail->output_tag, this->output.c_str(), sizeof(output_mail->output_tag)-1);
+        strncpy(output_mail->id, this->id.c_str(), sizeof(output_mail->id)-1);
+
+        OutputMasterMailBox.put(output_mail);
+    }
+}
+
+//
+// @method:             display
+// @description:        show the pertinents
+//
+// @param[in]           none
+// @param[out]          none
+// @return              none
+//    
+void SensorErrorControl::display(void)
+{
+    const char *mapper[] = { "INIT",
+                             "START",
+                             "OFF",
+                             "DUTY CYCLE ON",
+                             "DUTY CYCLE OFF",
+                             "invalid"
+                           };
+
+    printf("\r\n");
+    std::cout << left << setw(10) << setfill(' ') << "sensor error: ";
+    std::cout << left << setw(40) << setfill(' ') << controlFile;
+    std::cout << left << setw(20) << setfill(' ') << id;
+    std::cout << left << setw(6)  << setfill(' ') << priority;
+    std::cout << left << setw(20) << setfill(' ') << input;
+    std::cout << left << setw(20) << setfill(' ') << output;
+    std::cout << left << setw(16) << setfill(' ') << mapper[currentState];
+    std::cout << "duty cycle: " << left << setw(4) << setfill(' ') << dutyCycle.dutyCycle;
+    std::cout << "interval: "   << left << setw(4) << setfill(' ') << dutyCycle.interval;
+
+    std::cout.flush();
+}
+