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.
ICE-Application/src/ConfigurationHandler/Controls/SensorErrorControl.cpp
- Committer:
- jmarkel44
- Date:
- 2017-01-24
- Revision:
- 1:b2e90cda7a5a
- Parent:
- 0:61364762ee0e
File content as of revision 1:b2e90cda7a5a:
/******************************************************************************
*
* 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();
}