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/SequenceControl.cpp
- Revision:
- 2:02cb20446785
- Parent:
- 1:b2e90cda7a5a
--- a/ICE-Application/src/ConfigurationHandler/Controls/SequenceControl.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,546 +0,0 @@ -/****************************************************************************** -* -* File: SequenceControl.cpp -* Desciption: ICE Sequence Control class implementation -* -*****************************************************************************/ - -#include "SequenceControl.h" -#include "ICELog.h" -#include "cJSON.h" -#include "global.h" - -#include <stdlib.h> -#include <vector> -#include <string> -#include <iostream> -#include <iomanip> -#include <stdarg.h> -#include <assert.h> -#include "ModbusMasterApi.h" -#include "ConfigurationHandler.h" - -// for debugging - this can be set via the console command: -// "debug-se 1 -bool debugSequenceControl = false; - -static void debug(const char *fmt, ...) -{ - if ( debugSequenceControl ) { - va_list vargs; - va_start(vargs, fmt); - vfprintf(stdout, fmt, vargs); - va_end(vargs); - } -} - -// -// method: load -// decsription: load data from the control file -// -// @param[in] _controlFile -> the control file -// @param[out] none -// @return true on success; false on error -// -bool SequenceControl::load(std::string _controlFile) -{ - controlFile = _controlFile; - - // read the data into a buffer - char dataBuf[MAX_FILE_SIZE*3]; - bool rc = GLOBAL_mdot->readUserFile(controlFile.c_str(), (void *)dataBuf, sizeof(dataBuf)); - if ( rc != true ) { - logError("%s: failed to read %d bytes from %s", __func__, sizeof(dataBuf), controlFile.c_str()); - // caller should destroy the object - return false; - } - - // validate the JSON tags in the control file - if ( validateControlData(dataBuf) != true ) { - logError("%s: invalid control data.", __func__); - return false; - } - - // assign object data from control file - copyControlData(dataBuf); - - // TODO: perform additional validation - - return true; -} - - - -// method: validateControlData -// description: validates the data in the control file -// -// @param[in] dataBuf -> JSON formatted string -// @param[out] non -// @return true if valid; false otherwise -// -bool SequenceControl::validateControlData(const char *dataBuf) -{ - // parse the json data - bool rc = true; - cJSON * root = cJSON_Parse(dataBuf); - - // parse the control header - if ( !cJSON_HasObjectItem(root, "id") || - !cJSON_HasObjectItem(root, "startTrigger") || - !cJSON_HasObjectItem(root, "sequence") ) { - logError("Sequence Control is missing expected tags"); - cJSON_Delete(root); - return false; - } - - // validate the sequence table - cJSON *sequenceTable = cJSON_GetObjectItem(root, "sequence"); - for ( int i = 0; i < cJSON_GetArraySize(sequenceTable); ++i ) { - cJSON *entry = cJSON_GetArrayItem(sequenceTable, i); - if ( !cJSON_HasObjectItem(entry, "startTrigger") ) { - logError("Sequence Table missing startTrigger tag"); - rc = false; - break; - } - if ( !cJSON_HasObjectItem(entry, "actions") ) { - logError("Sequence table is missing actions tag"); - rc = false; - // FIXME: finish the internals - break; - } - if ( !cJSON_HasObjectItem(entry, "stopTrigger") ) { - logError("Sequence table is missing stopTrigger tag"); - rc = false; - break; - } - } - - cJSON_Delete(root); - return rc; -} - -// -// method: copyControlData -// description: copy JSON formatted control data to object -// -// @param[in] dataBuf -> JSON formatted data -// @param[out] none -// @return none -// -void SequenceControl::copyControlData(const char* dataBuf) -{ - cJSON *root = cJSON_Parse(dataBuf); - - id = cJSON_GetObjectItem(root, "id")->valuestring; - startTrigger = cJSON_GetObjectItem(root, "startTrigger")->valuestring; - - // validate the sequence table - cJSON *sequenceTable = cJSON_GetObjectItem(root, "sequence"); - for ( int i = 0; i < cJSON_GetArraySize(sequenceTable); ++i ) { - cJSON *entry = cJSON_GetArrayItem(sequenceTable, i); - - SequenceEntry n; - - n.startTrigger = cJSON_GetObjectItem(entry, "startTrigger")->valuestring; - n.stopTrigger = cJSON_GetObjectItem(entry, "stopTrigger")->valuestring; - - cJSON *actionList = cJSON_GetObjectItem(entry, "actions"); - for ( int j = 0; j < cJSON_GetArraySize(actionList); ++j ) { - cJSON *list = cJSON_GetArrayItem(actionList, j); - Action_t a; - a.action = cJSON_GetObjectItem(list, "action")->valuestring; - a.id = cJSON_GetObjectItem(list, "id")->valuestring; - if ( a.action == "assign" ) { - a.value = atof(cJSON_GetObjectItem(list, "val")->valuestring); - } else { - a.value = 0; - } - n.actions.push_back(a); - } - // push this entry onto this sequence table - this->sequenceTable.push_back(n); - } - - cJSON_Delete(root); -} - -// -// method: start -// decsription: start the sequence control -// -// @param[in] none -// @param[out] none -// @return none -// -void SequenceControl::start(void) -{ - currentState = SEQ_STATE_START; -} - -// -// method: run -// decsription: run the sequence control (performs the updates) -// -// @param[in] none -// @param[out] none -// @return OK on success; error otherwise -// -SequenceControlError_t SequenceControl::run(void) -{ - SequenceControlError_t rc = SEQUENCE_CONTROL_OK; - - switch ( this->currentState ) { - case SEQ_STATE_INIT: - // do nothing - break; - case SEQ_STATE_START: - // here we need to wait for the start trigger to happen - if ( this->isControlStartTriggerOn() ) { - debug("\r%s: [START]->start trigger->[LOADING]\n", id.c_str()); - ModbusValue val; - ModbusMasterReadRegister(startTrigger, &val); - debug("\r%s:%s: val.value = %f\n", id.c_str(), startTrigger.c_str(), val.value); - currentState = SEQ_STATE_LOADING_NEXT_ENTRY; - } else { - // just continue waiting for the start trigger to fire - } - break; - case SEQ_STATE_LOADING_NEXT_ENTRY: { - // this is a transient state - bool rc = this->loadNextEntry(); - if ( rc ) { - debug("\r%s: [LOADING]->next entry->[WAIT-START]\n", id.c_str()); - currentState = SEQ_STATE_WAITING_START; - } else { - debug("\r%s: [LOADING]->no entries->[FINISHED]\n", id.c_str()); - currentState = SEQ_STATE_FINISHED; - } - break; - } - case SEQ_STATE_WAITING_START: - // wait for the start triggers to evaluate to true - if ( this->isCurrentEntryStartTriggerOn() ) { - debug("\r%s: [WAIT-START]->perform actions->[WAIT-STOP]\n", id.c_str()); - performActions(); - currentState = SEQ_STATE_WAITING_STOP; - } else { - // wait until the start trigger fires - } - break; - case SEQ_STATE_WAITING_STOP: - // wait for the stop triggers to evaluate to true - if ( this->isCurrentEntryStopTriggerOn() ) { - debug("\r%s: [WAIT-STOP]->stop trigger ON->[LOAD NEXT]\n", id.c_str()); - currentState = SEQ_STATE_LOADING_NEXT_ENTRY; - } else { - // continue waiting - } - break; - case SEQ_STATE_FINISHED: - // do any cleanup work that's needed here. - debug("\r%s: [FINISHED]->clearing the reg->[START]\n", id.c_str()); - ModbusMasterWriteRegister(this->startTrigger, 0); - this->currentState = SEQ_STATE_START; - break; - case SEQ_STATE_MAX: - default: - logError("%s: unknown state %u\n", __func__, this->currentState); - rc = SEQUENCE_CONTROL_UNK_STATE; - break; - } - return rc; -} - -// -// method: isControlStartTriggerOn -// description: true if the start trigger evals to true -// -// @param[in] none -// @param[out] none -// @return true if start trigger > 0; false otherwise -// -bool SequenceControl::isControlStartTriggerOn(void) -{ - ModbusValue value; - bool rc = ModbusMasterReadRegister(this->startTrigger, &value); - if ( rc && value.value ) { - return true; - } - - return false; -} - -// -// method: isCurrentEntryStartTriggerOn -// description; true the current sequence entry start trigger evals to true -// -// @param[in] none -// @param[out] none -// @return none -// -bool SequenceControl::isCurrentEntryStartTriggerOn(void) -{ - ModbusValue value; - bool rc = ModbusMasterReadRegister(this->currentEntry.startTrigger, &value); - if ( rc != true ) { - logError("%s: failed to read %s from modbus master", - __func__, this->currentEntry.startTrigger.c_str()); - return rc; - } else if ( value.value ) { - debug("\r%s:%s-> returning true\n", __func__, id.c_str()); - return true; - } - return false; -} - -// -// method: isCurrentEntryStopTriggerOn -// description; true the current sequence entry start trigger evals to true -// -// @param[in] none -// @param[out] none -// @return none -// -bool SequenceControl::isCurrentEntryStopTriggerOn(void) -{ - ModbusValue value; - bool rc = ModbusMasterReadRegister(this->currentEntry.stopTrigger, &value); - if ( rc != true ) { - logError("%s: failed to read %s from modbus master", - __func__, this->currentEntry.stopTrigger.c_str()); - return rc; - } else if ( value.value ) { - debug("\r%s:%s-> returning true\n", __func__, id.c_str()); - return true; - } - return false; -} - -// -// method: loadNextEntry -// description: load the next entry from the sequence table -// -// @param[in] none -// @param[out[ none -// @return true is loaded; false otherwise -// -bool SequenceControl::loadNextEntry(void) -{ - if ( this->sequenceTable.empty() ) { - debug("\r%s: sequence table is empty\n", id.c_str()); - return false; - } - if ( nextEntryIndex < this->sequenceTable.size() ) { - currentEntry = sequenceTable.at(nextEntryIndex++); - printf("\r...successfully loaded new entry\n"); - return true; - } - return false; -} - -// -// method: performActions -// description: perform the actions associated with the current entry -// -// @param[in] none -// @param[out] none -// @return none -// -SequenceControlError_t SequenceControl::performActions(void) -{ - SequenceControlError_t rc = SEQUENCE_CONTROL_OK; - - // possible action types: - // create -> create a control - // delete -> delete a control - // modify -> modify a control - // assign -> assign a value to a modbus register - // execute -> execute a script - if ( !currentEntry.actions.empty() ) { - std::vector<Action_t>::const_iterator pos; - for ( pos = currentEntry.actions.begin(); pos != currentEntry.actions.end(); ++pos ) { - debug("\raction->%s on ID->%s\n", pos->action.c_str(), pos->id.c_str()); - // based on the action, we need to determine what to do - // 1: determine the action - // 2: see if the file exists (catastrophic) - // 3: if it's a create or delete action, send a message to the - // configuration handler - if ( pos->action == "createsp" ) { - rc = createSubControl(pos->id, CONTROL_SETPOINT); - assert(!rc); - } else if ( pos->action == "createtm" ) { - rc = createSubControl(pos->id, CONTROL_TIMER); - assert(!rc); - } else if ( pos->action == "deletesp" ) { - rc = destroySubControl(pos->id, CONTROL_SETPOINT); - assert(!rc); - } else if ( pos->action == "deletetm" ) { - rc = destroySubControl(pos->id, CONTROL_TIMER); - assert(!rc); - } else if ( pos->action == "execute" ) { - // not implemented - } else if ( pos->action == "assign" ) { - if ( assignRegister(pos->id, pos->value) != true ) { - assert(0); - } - } else { - logError("%s: unknown action %s (%s)", __func__, pos->action.c_str(), pos->id.c_str()); - rc = SEQUENCE_CONTROL_BAD_ACTION; - } - } - } else { - logInfo("%s: no entries in the action table", __func__); - } - return rc; -} - -// -// method: createSubControl -// description: create a control that's listed in the sequence table -// -// @param[in] action (create, destroy, etc.) -// @param[in] id -> control identifier -// @param[out[ none -// @return OK on success; error otherwise -// -SequenceControlError_t SequenceControl::createSubControl(const std::string controlId, - Control_t type) -{ - std::string file_prefix; - - switch (type) { - case CONTROL_SETPOINT: - file_prefix = CONTROL_SP_STR; - break; - case CONTROL_TIMER: - file_prefix = CONTROL_TM_STR; - break; - default: - logError("%s %s unsupported type %d", __func__, id.c_str(), type); - return SEQUENCE_CONTROL_BAD_CONTROL_TYPE; - } - - char filename[64]; - snprintf(filename, sizeof(filename), "%s%s_%s%s.json", - SEQUENCE_CONTROL_FILENAME_PREFIX, - this->id.c_str(), - file_prefix.c_str(), - controlId.c_str()); - - debug("\r%s: searching for %s\n", id.c_str(), filename); - - // send a message to the configuration handler to create the control - ConfigMessage_t *msg = ConfigHandlerMailBox.alloc(); - memset(msg, 0, sizeof(ConfigMessage_t)); - msg->action = ACTION_CREATE; - msg->control = (Control_t)type; - strncpy(msg->controlFile, filename, sizeof(msg->controlFile)-1); - - printf("%s: Sending a create request for control %s\r\n", - __func__, msg->controlFile); - - ConfigHandlerMailBox.put(msg); - - return SEQUENCE_CONTROL_OK; -} - -// -// method: destroySubControl -// description: destroy a control that's listed in the sequence table -// -// @param[in] action (create, destroy, etc.) -// @param[in] id -> control identifier -// @param[out[ none -// @return OK on success; error otherwise -// -SequenceControlError_t SequenceControl::destroySubControl(const std::string controlId, - Control_t type) -{ - std::string file_prefix; - - switch (type) { - case CONTROL_SETPOINT: - file_prefix = CONTROL_SP_STR; - break; - case CONTROL_TIMER: - file_prefix = CONTROL_TM_STR; - break; - default: - logError("%s %s unsupported type %d", __func__, id.c_str(), type); - return SEQUENCE_CONTROL_BAD_CONTROL_TYPE; - } - - char filename[64]; - snprintf(filename, sizeof(filename), "%s%s_%s%s.json", - SEQUENCE_CONTROL_FILENAME_PREFIX, - this->id.c_str(), - file_prefix.c_str(), - controlId.c_str()); - - debug("\r%s: searching for %s\n", id.c_str(), filename); - - // send a message to the configuration handler to create the control - ConfigMessage_t *msg = ConfigHandlerMailBox.alloc(); - memset(msg, 0, sizeof(ConfigMessage_t)); - msg->action = ACTION_DESTROY; - msg->control = (Control_t)type; - strncpy(msg->controlFile, filename, sizeof(msg->controlFile)-1); - - printf("%s: Sending a create request for control %s\r\n", - __func__, msg->controlFile); - - ConfigHandlerMailBox.put(msg); - - return SEQUENCE_CONTROL_OK; -} - -bool SequenceControl::assignRegister(const std::string id, float value) -{ - return (ModbusMasterWriteRegister(id, value)); -} - -// -// method: display -// decsription: display the control data -// -// @param[in] none -// @param[out] none -// @return none -// -void SequenceControl::display(void) -{ - const char *mapper[] = { "INIT", - "START", - "LOADING", - "WAIT-START", - "WAIT-STOP", - "FINISHED", - "FAILED", - "NULL" - }; - - printf("\rid-> %s\n", id.c_str()); - printf("\rstartTrigger-> %s\n", startTrigger.c_str()); - printf("\rSequenceTable: \n"); - - std::vector<SequenceEntry>::const_iterator seqIt; - - int entry = 0; - - for ( seqIt = sequenceTable.begin(); seqIt != sequenceTable.end(); ++seqIt ) { - printf("\rEntry %d: %s\n \r startTrigger: %s\n", - ++entry, - (entry == nextEntryIndex-1) ? "<-currently active" : "", - seqIt->startTrigger.c_str()); - std::vector<Action_t>::const_iterator actIt; - for ( actIt = seqIt->actions.begin(); actIt != seqIt->actions.end(); ++actIt ) { - printf("\r\taction-> %s, id-> %s ", actIt->action.c_str(), actIt->id.c_str()); - if ( actIt->action == "assign" ) printf("value -> %f", actIt->value); - printf("\r\n"); - } - printf("\r stopTrigger: %s\n", seqIt->stopTrigger.c_str()); - } - - printf("\r\n\rcurrent state = %s\r\n\r\n", mapper[this->currentState]); -} -