Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Diff: ICE-Application/src/ConfigurationHandler/Controls/SensorErrorControl.cpp
- Revision:
- 0:61364762ee0e
diff -r 000000000000 -r 61364762ee0e ICE-Application/src/ConfigurationHandler/Controls/SensorErrorControl.cpp
--- /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();
+}
+