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
--- /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