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/FailsafeControl.cpp	Tue Jan 24 19:05:33 2017 +0000
@@ -0,0 +1,534 @@
+/******************************************************************************
+ *
+ * File:                FailsafeControl.cpp
+ * Desciption:          ICE Failsafe Control Class implementation
+ *
+ *****************************************************************************/
+#include "FailsafeControl.h"
+#include "cJSON.h"
+#include "ICELog.h"
+#include "global.h"
+#include "ModbusMasterApi.h"
+#include "utilities.h"
+#include <string>
+#include <iostream>
+#include <iomanip>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <time.h>
+
+using namespace std;
+
+#ifdef MDOT_ICE
+extern mDot *GLOBAL_mdot;
+#endif 
+
+// for debugging - this can be set via the console command:
+//      "debug-fs 1 or debug-fs 0
+bool debugFailsafeControl = false;
+
+static void debug(const char *fmt, ...)
+{
+    if ( debugFailsafeControl ) {
+        va_list vargs;
+        va_start(vargs, fmt);
+        vfprintf(stdout, fmt, vargs);
+        va_end(vargs);
+    }
+}
+
+//
+// method:          load
+// description:     load a composite control
+//
+// @param           none
+// @return          none
+//
+bool FailsafeControl::load(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;
+    }
+
+
+    if ( !validateControlData(dataBuf) ) {
+        logError("%s: failed to validate control data", __func__);
+        return false;
+    }
+
+    copyControlData(dataBuf);
+
+    ModbusValue val;
+    // validate the input & output 
+    if ( ModbusMasterReadRegister(input, &val) == false ) {
+        logError("%s failed to find input %s", id.c_str(), input.c_str());
+        return false;
+    }
+    if ( ModbusMasterReadRegister(output, &val) == false ) {
+        logError("%s failed to find output %s", id.c_str(), output.c_str());
+        return false;
+    }
+    
+    isVirtualOutput = Util_isVirtualOutput(output) ? true : false;
+    return true;
+}
+//
+// method:                  validateControlData
+// description:             validate the JSON formatted strings for
+//                          expected tags
+//
+// @param[in]               buf -> JSON formatted string
+// @param[out]              none
+// @return                  true if valid; false otherwise
+//
+bool FailsafeControl::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, "hfsValue") ||
+            !cJSON_HasObjectItem(root, "hfsDutyCycle") ||
+            !cJSON_HasObjectItem(root, "hfsInterval") ||
+            !cJSON_HasObjectItem(root, "lfsValue") ||
+            !cJSON_HasObjectItem(root, "lfsDutyCycle") ||
+            !cJSON_HasObjectItem(root, "lfsInterval") ) {
+        logError("%s: control file is missing expected tags", __func__);
+        rc = false;
+    }
+
+    cJSON_Delete(root);
+    return rc;
+}
+
+//
+// method:                  copyControlData
+// description:             copy data from JSON buffer to object
+//
+// @param[in]               buf -> JSON formattted string
+// @param[out]              none
+// @return                  none
+//
+void FailsafeControl::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;
+
+    // high failsafe data
+    hfs_data.value      = atof(cJSON_GetObjectItem(root, "hfsValue")->valuestring);
+    hfs_data.dutyCycle  = atoi(cJSON_GetObjectItem(root, "hfsDutyCycle")->valuestring);
+    hfs_data.interval   = atoi(cJSON_GetObjectItem(root, "hfsInterval")->valuestring);
+
+    // low failsafe data
+    lfs_data.value      = atof(cJSON_GetObjectItem(root, "lfsValue")->valuestring);
+    lfs_data.dutyCycle  = atoi(cJSON_GetObjectItem(root, "lfsDutyCycle")->valuestring);
+    lfs_data.interval   = atoi(cJSON_GetObjectItem(root, "lfsInterval")->valuestring);
+
+    cJSON_Delete(root);
+}
+
+//
+// method:      start
+// description: start the failsafe control
+//
+// @param       none
+// @return      none
+//
+void FailsafeControl::start(void)
+{
+    currentState = STATE_START;
+}
+
+//
+// method:      update
+// description: update the faisafe control using the FSM
+//
+// @param       none
+// @return      none
+//
+FailsafeControlError_t FailsafeControl::update(void)
+{
+    FailsafeControlError_t rc = FAILSAFE_CONTROL_OK;
+
+    switch (this->currentState) {
+        case STATE_INIT:
+            // do nothing - control must be programatically started
+            break;
+        case STATE_START:
+            // control has programmatically started, do some checking
+            if ( this->aboveHighFailsafe() ) {
+                if ( hfs_data.dutyCycle) {
+                    debug("\r%s: [START]->above hfs->[ON_HFS]\n", id.c_str());
+                    // start high failsafe duty cycle
+                    this->currentState = STATE_CONTROL_ON_HFS;
+                    // turn the pump ON for the duty cycle, start the timer
+                    sendMailToOutput(ACTION_CONTROL_ON);
+                    this->startHfsDutyTimer();
+                } else {
+                    // no duty cycle specified, so go into the high-faisafe
+                    // fixed-off state
+                    debug("\r%s: [START]->above hfs && !dc->[OFF_HFS]\n", id.c_str());
+                    this->currentState = STATE_CONTROL_OFF_HFS;
+                    sendMailToOutput(ACTION_CONTROL_OFF);
+                    // no need to start the duty timer
+                }
+            } else if ( this->belowLowFailsafe() ) {
+                debug("\r%s: [START]->below lfs->[ON_LFS]\n", id.c_str());
+                // start low failsafe duty cycle
+                if ( lfs_data.dutyCycle ) {
+                    this->currentState = STATE_CONTROL_ON_LFS;
+                    // turn the pump ON for the duty cycle, start the timer
+                    sendMailToOutput(ACTION_CONTROL_ON);
+                    this->startLfsDutyTimer();
+                } else {
+                    // no duty cycle specified, so go into the low-failsafe
+                    // fixed off-state
+                    this->currentState = STATE_CONTROL_OFF_LFS;
+                    sendMailToOutput(ACTION_CONTROL_OFF);
+                    // no need to start the timer
+                }
+            } else {
+                debug("\r%s: [START]->no conditions->[OFF]\n", id.c_str());
+                this->currentState = STATE_CONTROL_OFF;
+            }
+            break;
+        case STATE_CONTROL_OFF:
+            // control is acting normal, within bounds
+            if ( this->aboveHighFailsafe() ) {
+                if ( hfs_data.dutyCycle ) {
+                    debug("\r%s: [OFF]->above hfs->[ON HFS]\n", id.c_str());
+                    this->currentState = STATE_CONTROL_ON_HFS;
+                    sendMailToOutput(ACTION_CONTROL_ON);
+                    this->startHfsDutyTimer();
+                } else {
+                    debug("\r%s: [OFF]->above hfs && no dc->[OFF HFS]\n", id.c_str());
+                    this->currentState = STATE_CONTROL_OFF_HFS;
+                    sendMailToOutput(ACTION_CONTROL_OFF);
+                }
+            } else if ( this->belowLowFailsafe() ) {
+                // restart the low failsafe duty cycle
+                debug("\r%s: [OFF]->below lfs->[ON_LFS]\n", id.c_str());
+                if ( lfs_data.dutyCycle ) {
+                    this->currentState = STATE_CONTROL_ON_LFS;
+                    sendMailToOutput(ACTION_CONTROL_ON);
+                    this->startLfsDutyTimer();
+                } else {
+                    this->currentState = STATE_CONTROL_OFF_LFS;
+                    sendMailToOutput(ACTION_CONTROL_OFF);
+                }
+            } else {
+                // do nothing
+            }
+            break;
+        case STATE_CONTROL_ON_LFS:
+            // control is in low-failsafe with duty cycle ON
+            if ( !this->belowLowFailsafe() ) {
+                // input has fallen below the failsafe, so turn off the control
+                debug("\r%s: [ON_LFS]->above lfs->[OFF]\n", id.c_str());
+                this->currentState = STATE_CONTROL_OFF;
+                this->unregisterControl();
+            } else if ( this->dutyOnExpired() ) {
+                // duty ON has expired, so let's idle in the OFF-LFS state now
+                debug("\r%s: [ON_LFS]->duty on expired->[OFF LFS]\n", id.c_str());
+                this->currentState = STATE_CONTROL_OFF_LFS;
+                sendMailToOutput(ACTION_CONTROL_OFF);
+            } else {
+                // do nothing
+            }
+            break;
+        case STATE_CONTROL_OFF_LFS:
+            // control is in low-failsafe with duty cycle OFF
+            if ( !this->belowLowFailsafe() ) {
+                // no longer in dangerous waters, so transition to OFF
+                debug("\r%s: [OFF_LFS]->above lfs->[OFF]\n", id.c_str());
+                this->currentState = STATE_CONTROL_OFF;
+                this->unregisterControl();
+            } else if ( this->dutyOffExpired() && lfs_data.dutyCycle ) {
+                debug("\r%s: [OFF_LFS]->duty OFF exp->[ON_LFS]", id.c_str());
+                // turn the PUMP back on and restart the duty timer
+                this->currentState = STATE_CONTROL_ON_LFS;
+                sendMailToOutput(ACTION_CONTROL_ON);
+                this->startLfsDutyTimer();
+
+            } else {
+                // do nothing
+            }
+            break;
+        case STATE_CONTROL_ON_HFS:
+            // control is in high-failsafe with duty cycle ON
+            if ( !this->aboveHighFailsafe() ) {
+                debug("\r%s: [ON_HFS]->below hfs->[OFF]\n", id.c_str());
+                this->currentState = STATE_CONTROL_OFF;
+                this->unregisterControl();
+            } else if ( this->dutyOnExpired() ) {
+                debug("\r%s: [ON_HFS]->duty ON expired->[OFF_HFS]\n", id.c_str());
+                this->currentState = STATE_CONTROL_OFF_HFS;
+                sendMailToOutput(ACTION_CONTROL_OFF);
+            } else {
+                // do nothing
+            }
+            break;
+        case STATE_CONTROL_OFF_HFS:
+            // control is in high-failsafe with cuty cycle OFF
+            if ( !this->aboveHighFailsafe() ) {
+                debug("\r%s: [OFF_HFS]->below hfs->[OFF]\n", id.c_str());
+                this->currentState = STATE_CONTROL_OFF;
+                this->unregisterControl();
+            } else if ( this->dutyOffExpired() && hfs_data.dutyCycle ) {
+                debug("\r%s: [OFF_HFS]->duty OFF expired->[ON_HFS]\n", id.c_str());
+                this->currentState = STATE_CONTROL_ON_HFS;
+                sendMailToOutput(ACTION_CONTROL_ON);
+                this->startHfsDutyTimer();
+            } else {
+                // do nothing
+            }
+            break;
+        default:
+            logError("%s: unknown state %d", __func__, this->currentState);
+            rc = FAILSAFE_CONTROL_UNK_STATE;
+            break;
+    }
+    return rc;
+}
+
+//
+// method:              belowFailsafe
+// description:         returns true if the input signal is below the
+//                      failsafe mark
+//
+// @param[in]           none
+// @param[out]          none
+// @return              true if below; false otherwise
+//
+bool FailsafeControl::belowLowFailsafe(void)
+{
+    // read the modbus input
+    ModbusValue value;
+    ModbusMasterReadRegister(input, &value);
+    return ( value.value <= lfs_data.value );
+}
+
+//
+// method:              aboveHighFailsafe
+// description:         returns true if the input signal is above the
+//                      failsafe mark
+//
+// @param[in]           none
+// @param[out]          none
+// @return              true if above; false otherwise
+//
+bool FailsafeControl::aboveHighFailsafe(void)
+{
+    // read the modbus input
+    ModbusValue value;
+    ModbusMasterReadRegister(input, &value);
+    return ( value.value >= hfs_data.value );
+}
+
+//
+// method:          startHfsDutyTimer
+// description:     start the high failsafe duty timer
+//
+// @param           none
+// @return          none
+//
+void FailsafeControl::startHfsDutyTimer(void)
+{
+    unsigned long currentTime = time(NULL);
+    unsigned long period = hfs_data.interval * 60;
+
+    duty_timer.offTime        = currentTime + ((double)period * ((double)hfs_data.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:          stopHfsDutyTimer
+// description:     stop the high failsafe duty timer
+//
+// @param [in]      none
+// @param[out]      none
+// @return          none
+//
+void FailsafeControl::stopHfsDutyTimer(void)
+{
+    debug("\r%sL%s-> invoked\n", __func__, id.c_str());
+    memset(&duty_timer, 0, sizeof(duty_timer));
+}
+
+//
+// method:          startLfsDutyTimer
+// descrption:      start the low failsafe duty-timer
+//
+// @param[in]       none
+// @param[out]      none
+// @return          none
+//
+void FailsafeControl::startLfsDutyTimer(void)
+{
+    unsigned long currentTime = time(NULL);
+    unsigned long period      = lfs_data.interval * 60;
+
+    duty_timer.offTime        = currentTime + ((double)period * ((double)lfs_data.dutyCycle/100.0));
+    duty_timer.expirationTime = currentTime + period;
+
+    debug("\r%s:%s-> next off time = %lu\n", __func__, id.c_str(), duty_timer.offTime);
+    debug("\r%s:%s->expiration time = %lu\n", __func__, id.c_str(), duty_timer.expirationTime);
+}
+
+//
+// method:          stopLfsDutyTimer
+// description:     stop the low failsafe duty-timer
+//
+// @param           none
+// @return          none
+//
+void FailsafeControl::stopLfsDutyTimer(void)
+{
+    debug("\r%s:5s-> invoked\n", __func__, id.c_str());
+    memset(&duty_timer, 0, sizeof(duty_timer));
+}
+
+//
+// method:          dutyOnExpired
+// description:     returns true if ON cycle has expired; false otherwise
+//
+// @param           none
+// @return          none
+//
+bool FailsafeControl::dutyOnExpired(void)
+{
+    return (time(0) >= duty_timer.offTime);
+}
+
+//
+// method:          dutyOffExpired
+// description:     returns true if OFF cycle has expired; false otherwise
+//
+// @param           none
+// @return          none
+//
+bool FailsafeControl::dutyOffExpired(void)
+{
+    return (duty_timer.expirationTime < time(NULL));
+}
+
+//
+// method:      sendMailToOutput
+// description: send mail to the output task
+//
+// @param       io_tag  -> input/output tag
+// @param       action  -> ON, OFF, UNREGISTER
+// @return      none
+//
+void FailsafeControl::sendMailToOutput(OutputAction action)
+{
+    logInfo("%s: failsafe control %s attempting to send action %d\n",
+            __func__, id.c_str(), 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 {
+        OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc();
+        memset(output_mail, 0, sizeof(OutputControlMsg_t));
+
+        output_mail->action         = action;
+        output_mail->controlType    = CONTROL_FAILSAFE;
+        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 this control with the output task
+//
+// @param           none
+// @return          none
+//
+void FailsafeControl::unregisterControl(void)
+{
+    logInfo("%s: %s attempting to unregister %s\n",
+            __func__, id.c_str(), controlFile.c_str());
+
+    if ( isVirtualOutput ) {
+        debug("%s: %s attempting to unregister virtual output %s\n", 
+            __func__, id.c_str(), output.c_str());
+        ModbusMasterWriteRegister(output, 0.0);
+    } else {
+        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: display the pertinents
+//
+// @param       none
+// @return      none
+//
+void FailsafeControl::display(void)
+{
+    const char *mapper[] = { "INIT",
+                             "START",
+                             "OFF",
+                             "ON_LFS",
+                             "OFF_LFS",
+                             "ON_HFS",
+                             "OFF_HFS",
+                             "SENSOR_ERR",
+                             "invalid"
+                           };
+
+    printf("\r\n");
+    std::cout << left << setw(10) << setfill(' ') << "failsafe: ";
+    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 << left  << setw(12) << setfill(' ') << "lfs-> "
+              << lfs_data.value << ":" << lfs_data.dutyCycle << ":" << lfs_data.interval;
+    std::cout << right << setw(12) << setfill(' ') << "hfs-> "
+              << hfs_data.value << ":" << hfs_data.dutyCycle << ":" << hfs_data.interval;
+
+    std::cout.flush();
+}
\ No newline at end of file