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
--- /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(); +} +