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/SetpointControl.cpp
- Revision:
- 0:61364762ee0e
diff -r 000000000000 -r 61364762ee0e ICE-Application/src/ConfigurationHandler/Controls/SetpointControl.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ICE-Application/src/ConfigurationHandler/Controls/SetpointControl.cpp Tue Jan 24 19:05:33 2017 +0000 @@ -0,0 +1,369 @@ +/****************************************************************************** +* +* File: SetpointControl.cpp +* Desciption: ICE Setpoint Control class implementation +* +*****************************************************************************/ +#include "SetpointControl.h" +#include "ICELog.h" +#include "cJSON.h" +#include "ModbusMasterApi.h" +#include "global.h" +#include "utilities.h" +#include <string> +#include <iostream> +#include <iomanip> +#include <stdarg.h> +#include <stdlib.h> + +// for debugging - this can be set via the console command: +// "debug-sp 1 or debug-sp 0 +bool debugSetpointControl = false; + +static void debug(const char *fmt, ...) +{ + if ( debugSetpointControl ) { + va_list vargs; + va_start(vargs, fmt); + vfprintf(stdout, fmt, vargs); + va_end(vargs); + } +} + +#ifdef MDOT_ICE +extern mDot *GLOBAL_mdot; +#endif + +// +// method: load +// description: open the configuration file and assign data to the +// setpoint control object +// +// @param controlFile -> name of the control file +// @return true if data is assigned; false on error +// +bool SetpointControl::load(std::string _controlFile) +{ + controlFile = _controlFile; + + char dataBuf[MAX_FILE_SIZE]; + + // read the control data + 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 control data + if ( !validateControlData(dataBuf) ) { + logError("%s: failed to validate control data", __func__); + return false; + } + + // copy control data + copyControlData(dataBuf); + + ModbusValue val; + // validate the input & output + if ( ModbusMasterReadRegister(input, &val) == false ) { + logError("%s failed to find the 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 JSON formatted string +// +bool SetpointControl::validateControlData(const char *buf) +{ + bool rc = true; + 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, "setpoint") || + !cJSON_HasObjectItem(root, "prodfact") || + !cJSON_HasObjectItem(root, "actingDir") || + !cJSON_HasObjectItem(root, "tol") ) { + logError("Setpoint control is missing expected tags\n"); + rc = false; + } + + cJSON_Delete(root); + return rc; +} + +// +// method: copyControlData +// description: copy JSON formatted data +// +void SetpointControl::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; + setpoint = atof(cJSON_GetObjectItem(root,"setpoint")->valuestring); + productFactor = atof(cJSON_GetObjectItem(root, "prodfact")->valuestring); + actingDir = atoi(cJSON_GetObjectItem(root, "actingDir")->valuestring); + tolerance = atof(cJSON_GetObjectItem(root, "tol")->valuestring); + + cJSON_Delete(root); +} + +// +// method: start +// description: start the setpoint control +// +// @param none +// @return none +// +void SetpointControl::start(void) +{ + // this is the initial state; what else needs to be done?? + this->currentState = STATE_STARTUP; +} + +// +// method: +// description: based on the state of the control, check for +// under limit and over limit values, adjust the +// state accordingly +// +// @param none +// @return none +// +SetpointControlError_t SetpointControl::update(void) +{ + SetpointControlError_t rc = SETPOINT_CONTROL_OK; + + switch (this->currentState) { + case STATE_INIT: + // do nothing + break; + case STATE_STARTUP: + if ( this->underLimit() ) { + // start the feed right away + this->startFeed(); + this->currentState = STATE_CONTROL_ON; + debug("\r%s: [START]->under limit->[ON]\n", id.c_str()); + } else { + this->currentState = STATE_CONTROL_OFF; + this->stopFeed(); + debug("\r%s: [START]->!under limit->[OFF]\n", id.c_str()); + } + break; + case STATE_CONTROL_ON: + if ( this->overLimit() ) { + // stop the feed + this->stopFeed(); + this->currentState = STATE_CONTROL_OFF; + debug("\r%s: [ON]->over limit->[OFF]\n", id.c_str()); + } else { + // do nothing + } + break; + case STATE_CONTROL_OFF: + if ( this->underLimit() ) { + // start the feed + this->startFeed(); + this->currentState = STATE_CONTROL_ON; + debug("\r%s: [OFF]->under limit->[ON]\n", id.c_str()); + } else { + // do nothing + } + break; + default: + rc = SETPOINT_CONTROL_UNK_STATE; + break; + } + return rc; +} + +// +// method: overLimit +// description: (see @return) +// +// @param none +// @return true if product is over the upper limit for normal mode +// or under the limit for reverse mode; false otherwise +// +bool SetpointControl::overLimit(void) +{ + ModbusValue value; + ModbusMasterReadRegister( input, &value ); + float flimit; + + if ( !actingDir ) { + flimit = setpoint + tolerance; + return (value.value >= flimit); + } else { + flimit = setpoint - tolerance; + return (value.value <= flimit); + } +} + +// +// method: underLimit +// description: (see @return) +// +// @param none +// @return true if product is under lower limit for normal mode or +// over the upper limit for reverse mode; false otherwise +// +bool SetpointControl::underLimit(void) +{ + ModbusValue value; + ModbusMasterReadRegister( input, &value ); + float flimit; + + if ( !actingDir ) { + flimit = setpoint - tolerance; + return (value.value <= flimit); + } else { + flimit = setpoint + tolerance; + return (value.value >= flimit); + } +} + +// +// method: startFeed() +// description: send ON indication to Output Master for this control's +// relay +// +// @param none +// @return none +// +void SetpointControl::startFeed(void) +{ + logInfo("%s: %s attempting to start feed on relay %s\n", + __func__, controlFile.c_str(), output.c_str()); + + if ( isVirtualOutput ) { + ModbusMasterWriteRegister(output, 1.0); + } else { + OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc(); + memset(output_mail, 0, sizeof(OutputControlMsg_t)); + + output_mail->action = ACTION_CONTROL_ON; + output_mail->controlType = CONTROL_SETPOINT; + 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); + output_mail->priority = this->priority; + strncpy(output_mail->id, this->id.c_str(), sizeof(output_mail->id)-1); + OutputMasterMailBox.put(output_mail); + } +} + +// +// method: stopFeed +// description: send OFF indication to Output Master for this control's +// relay +// +// @param none +// @return none +// +void SetpointControl::stopFeed(void) +{ + logInfo("%s: %s attempting to stop feed on relay %s\n", + __func__, controlFile.c_str(), output.c_str()); + + if ( isVirtualOutput ) { + ModbusMasterWriteRegister(output, 0.0); + } else { + OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc(); + memset(output_mail, 0, sizeof(OutputControlMsg_t)); + + output_mail->action = ACTION_CONTROL_OFF; + output_mail->controlType = CONTROL_SETPOINT; + 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); + output_mail->priority = this->priority; + strncpy(output_mail->id, this->id.c_str(), sizeof(output_mail->id)-1); + OutputMasterMailBox.put(output_mail); + } +} + +// +// method: unregisterControl +// description: send OFF indication to Output Master for this control's +// relay +// +// @param none +// @return none +// +void SetpointControl::unregisterControl(void) +{ + logInfo("%s: %s attempting to unregister %s\n", + __func__, id.c_str(), controlFile.c_str()); + + if ( isVirtualOutput ) { + 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_MANUAL; + 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 control's information +// +// @param none +// @return none +// +void SetpointControl::display(void) +{ + // NOTE: this mapping must be 1:1 with "State" enumeration in SetpointControl.h + std::string mapper[] = { "INIT", + "STARTUP", + "CONTROL_OFF", + "CONTROL_ON", + "CONTROL_DISABLE", + "CONTROL_PAUSE", + "CONTROL_MAX" + }; + + + ModbusValue inputValue; + ModbusMasterReadRegister(input, &inputValue); + + printf("\r\n"); + std::cout << std::left << std::setw(10) << std::setfill(' ') << "setpoint: "; + std::cout << std::left << std::setw(40) << std::setfill(' ') << controlFile; + std::cout << std::left << std::setw(20) << std::setfill(' ') << id; + std::cout << std::left << std::setw(6) << std::setfill(' ') << priority; + std::cout << std::left << std::setw(20) << std::setfill(' ') << input; + std::cout << std::left << std::setw(20) << std::setfill(' ') << output; + std::cout << std::left << std::setw(8) << std::setfill(' ') << setpoint; + std::cout << std::left << std::setw(12) << std::setfill(' ') << (actingDir ? "direct" : "indirect"); + std::cout << std::left << std::setw(16) << std::setfill(' ') << mapper[currentState]; + std::cout << std::right << std::setw(8) << std::setfill(' ') << setpoint + tolerance << " <- "; + std::cout << std::left << std::setw(8) << std::setfill(' ') << inputValue.value << " -> "; + std::cout << std::left << std::setw(8) << std::setfill(' ') << setpoint - tolerance; + //std::cout << left << setw(10) << setfill(' ') << highFailsafe << " : " << lowFailsafe; + + std::cout.flush(); +} \ No newline at end of file