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/SequenceControl.cpp
- Committer:
- jmarkel44
- Date:
- 2017-01-24
- Revision:
- 0:61364762ee0e
File content as of revision 0:61364762ee0e:
/****************************************************************************** * * 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]); }