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/OutputTask/OutputTask.cpp
- Revision:
- 2:02cb20446785
- Parent:
- 1:b2e90cda7a5a
--- a/ICE-Application/src/OutputTask/OutputTask.cpp Tue Jan 24 19:06:45 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,545 +0,0 @@ -/****************************************************************************** - * - * File: OutputTask.cpp - * Desciption: source for the ICE Output task - * - *****************************************************************************/ -#include <vector> -#include <string> -#include <algorithm> -#include <assert.h> -#include <time.h> -#include "OutputTask.h" -#include "global.h" -#include "cJSON.h" -#include "ModbusMasterApi.h" -#include "LoggerApi.h" -#include "Control.h" -#include "rtc.h" -#include "ICELog.h" - -//#include "../add-ons/MTSLog/MTSLog.h" - - -// local functions -static int createOutput(const char *controlFile); -static int loadPersistentOutputs(void); -static void writeOutputs(const std::string, const std::string); -static int enableOutputReqHandler(OutputControlMsg_t *msg); -static int disableOutputReqHandler(OutputControlMsg_t *msg); -static int unregisterControl(const char *id, unsigned int pri, const char *output); -static void dumpEventRecord(EventReasonStruct_t &ev); - -// -// The Output Map -// -// this is the main data structure used to distinguish which control has -// priority of an output. the layout is as-follows: -// -// outputMap["o_rly1"]-> Control<"ManControl_rly1", 100, ON > <-- highest pri control manipulates relay -// Control<"Flowswitch", 700 OFF> <-- lower pri (queued up) -// Control<"SetpointControl_rly1", 800, OFF> <-- lowest pri (queued up) -// -// outputMap["o_rly2"]-> Control<"Flowswitch", 700, OFF> -// -> Control<"SetpointControl_rly2", 800, OFF> -// -// outputMap["o_rly3"]-> Control<"ManControl_rly3", 100, ON > -// Control<"Flowswitch", 700, ON > -// Control<"TimerControl_rly3", 750, ON > -// -// -// The Control Vector (per relay) is always sorted by priority, whereas -// the highest priority control (lower priority number) is at the front -// (v.front()) of the list. -// - -typedef std::map<std::string, std::vector<Control> > StringOutputVector_t; -static StringOutputVector_t outputMap; - -// operator for sorting the outputs vectors -bool operator<(const Control &control1, const Control &control2) -{ - return control1.getPriority() < control2.getPriority(); -} - -/***************************************************************************** - * Function: OutputTask - * Description: Main entry point for the Output Task - * - * @param args -> not used - * @return none - *****************************************************************************/ -void OutputTask(void const *args) -{ - int rc; - (void)args; - - printf("\r%s has started...\n", __func__); - -#ifdef LOAD_PERSISTENT_CONFIGURATIONS - if ( loadPersistentOutputs() != 0 ) { - logError("Failed to load persistent outputs"); - } -#endif - - // signal the main thread to continue - osSignalSet(mainThreadId, sig_config_continue); - - while (true) { - // wait for a message - osEvent evt = OutputMasterMailBox.get(); - if (evt.status == osEventMail) { - - OutputControlMsg_t *msg = (OutputControlMsg_t*) evt.value.p; - - switch ( msg->action ) { - case ACTION_NEW: - // read the file and and create an output entry - rc = createOutput(msg->controlFile); - if ( rc != 0 ) { - logError("%s: failed to create output %s\n", - __func__, msg->controlFile); - } - break; - case ACTION_CONTROL_ON: - logInfo("%s is requesting ON control of %s", msg->id, msg->output_tag); - rc = enableOutputReqHandler(msg); - if ( rc != 0 ) { - logError("%s: failed to enable output for %s", - __func__, msg->id); - } - break; - case ACTION_CONTROL_OFF: - logInfo("%s is requesting OFF control of %s", msg->id, msg->output_tag); - rc = disableOutputReqHandler(msg); - if ( rc != 0 ) { - logError("%s: failed to disable output for %s", - __func__, msg->id); - } - break; - case ACTION_CONTROL_UNREGISTER: - logInfo("%s is requesting its deletion from %s", msg->id, msg->output_tag); - rc = unregisterControl(msg->id, msg->priority, msg->output_tag); - if ( rc != 0 ) { - logError("%s: failed to unregister control %s", - __func__, msg->id); - } - break; - default: - logError("%s unknown action %u", __func__, msg->action); - break; - } - - // free the message - OutputMasterMailBox.free(msg); - - // refresh the outputs - writeOutputs(msg->id, msg->output_tag); - } - } -} - -/***************************************************************************** - * Function: publishEvent - * Description: publish an event to the logger - * - * @param output -> the output channel - * @param control -> the control, this can be null - * @return none - *****************************************************************************/ -void publishEvent(std::string output, const Control *control) -{ - - EventReasonStruct_t ev; - ModbusValue input_value; - ModbusValue output_value; - memset(&ev, 0, sizeof(ev)); - - - // if there's no control, that means the only control that was on - // the relay stack has been destroyed, so we'll send a NO CONTROL - // event code. - if ( !control ) { - ev.eventReason = EVENT_REASON_NO_CONTROL; - ModbusMasterReadRegister(output, &output_value); - strncpy(ev.outputTag, output.c_str(), sizeof(ev.outputTag)); - ev.outputValue = output_value.value; - dumpEventRecord(ev); - EventLoggerApi(ev); - return; - } - - switch ( control->getControlType() ) { - case CONTROL_SETPOINT: - case CONTROL_COMPOSITE: - ev.eventReason = EVENT_REASON_AUTO; - strncpy(ev.inputTag, control->getInput().c_str(), sizeof(ev.inputTag)); - strncpy(ev.outputTag, output.c_str(), sizeof(ev.outputTag)); - ModbusMasterReadRegister(control->getInput(), &input_value); - ModbusMasterReadRegister(output, &output_value); - ev.inputValue = input_value.value; - ev.outputValue = output_value.value; - dumpEventRecord(ev); - EventLoggerApi(ev); - break; - - case CONTROL_MANUAL: - ev.eventReason = EVENT_REASON_MANUAL; - strncpy(ev.outputTag, output.c_str(), sizeof(ev.outputTag)); - ModbusMasterReadRegister(output, &output_value); - ev.outputValue = output_value.value; - dumpEventRecord(ev); - EventLoggerApi(ev); - break; - - case CONTROL_TIMER: - ev.eventReason = EVENT_REASON_TIMER; - strncpy(ev.outputTag, output.c_str(), sizeof(ev.outputTag)); - ModbusMasterReadRegister(output, &output_value); - ev.outputValue = output_value.value; - dumpEventRecord(ev); - EventLoggerApi(ev); - break; - - case CONTROL_FAILSAFE: - case CONTROL_SENSOR_ERROR: - ev.eventReason = EVENT_REASON_FAILSAFE; - strncpy(ev.inputTag, control->getInput().c_str(), sizeof(ev.inputTag)); - strncpy(ev.outputTag, output.c_str(), sizeof(ev.outputTag)); - ModbusMasterReadRegister(control->getInput(), &input_value); - ModbusMasterReadRegister(output, &output_value); - ev.inputValue = input_value.value; - ev.outputValue = output_value.value; - dumpEventRecord(ev); - EventLoggerApi(ev); - break; - - default: - logError("%s: unknown control type\n", __func__); - break; - } -} - -/***************************************************************************** - * Function: writeOutputs - * Description: send a message to the modbus master of who's in control - * - * - * @param[in] id -> control identifier - * @param[in] output_tag -> the output to write - * @return none - *****************************************************************************/ -static void writeOutputs(const std::string id, const std::string output_tag) -{ - (void) id; - - if ( output_tag.empty() ) { - logError("%s: invalid output tag", __func__); - return; - } - - StringOutputVector_t::const_iterator pos; - - // find the output - pos = outputMap.find(output_tag); - if ( pos != outputMap.end() ) { - if ( pos->second.empty() ) { - // we found the output but nothing's controlling it... - ModbusMasterWriteRegister(pos->first, RELAY_STATUS_NOT_CONTROLLED); - publishEvent(pos->first, NULL); - } else { - // get the mapped state for the highest priority control - int mappedState = pos->second.begin()->getMappedState(); - - // read the register value for this output - ModbusValue value; - ModbusMasterReadRegister(pos->first, &value); - - // a new higher priority control is manipulating the output, - // so send the new values to the modbus and publish an event - if ( value.value != mappedState ) { - ModbusMasterWriteRegister(pos->first, mappedState); - Control t = *(pos->second.begin()); - publishEvent(pos->first, &t); - } - } - // TODO: This may be a virtual output, so we'll need to see if it - // exists in the vregmap - } else { - logError("%s failed to find the selected output %s", - __func__, output_tag.c_str()); - } -} - -/***************************************************************************** - * Function: createOutput - * Description: - * - * @param outputFile -> name of output file - * @return none - *****************************************************************************/ -static int createOutput(const char *outputFile) -{ - char dataBuf[MAX_FILE_SIZE]; - bool status = GLOBAL_mdot->readUserFile(outputFile, (void *)dataBuf, sizeof(dataBuf)); - if ( status != true ) { - logError("%s failed to read %s", __func__, outputFile); - return -1; - } - - cJSON * root = cJSON_Parse(dataBuf); - if ( !cJSON_HasObjectItem(root, "id") ) { - logError("%s: error extracting expected tags", __func__); - cJSON_Delete(root); - return -1; - } - - std::string id = cJSON_GetObjectItem(root,"id")->valuestring; - cJSON_Delete(root); - - std::vector<Control> v; - outputMap[id] = v; - - return 0; -} - -/***************************************************************************** - * Function: enableOutputReqHandler - * Description: handle a request to enable an output - * - * @param[in] msg -> the message request - * @return -1 on error - *****************************************************************************/ -static int enableOutputReqHandler(OutputControlMsg_t *msg) -{ - // attempt to find the output in the map - StringOutputVector_t::iterator pos; - - pos = outputMap.find(msg->output_tag); - if ( pos == outputMap.end() ) { - logError("%s: failed to find the designated output %s\n", - __func__, msg->output_tag); - return -1; - } - - if ( pos->second.empty() ) { - // this is a new request - std::string cid(msg->id); - std::string input(msg->input_tag); - Control c(cid, msg->controlType, input, msg->priority, CONTROL_ON); - pos->second.push_back(c); - } else { - // find this control in the list - std::vector<Control>::iterator v; - for ( v = pos->second.begin(); v != pos->second.end(); ++v ) { - if ( strcmp(v->getId().c_str(), msg->id) == 0 ) { - v->setState(CONTROL_ON); - break; - } - } - if ( v == pos->second.end() ) { - // this is a new request, so add it and sort the vector - std::string cid(msg->id); - std::string input(msg->input_tag); - Control c(cid, msg->controlType, input, msg->priority, CONTROL_ON); - pos->second.push_back(c); - std::sort(pos->second.begin(), pos->second.end()); - } - } - - return 0; -} - -/***************************************************************************** - * Function: disableOutputReqHandler - * Description: handle a request to disable an output - * - * @param[in] msg -> the message request - * @return none - *****************************************************************************/ -static int disableOutputReqHandler(OutputControlMsg_t *msg) -{ - // attempt to find the output in the map - StringOutputVector_t::iterator pos; - - pos = outputMap.find(msg->output_tag); - if ( pos == outputMap.end() ) { - logError("%s: failed to find the designated output %s\n", - __func__, msg->output_tag); - return -1; - } - - // if the control list is empty, push this control on the list - if ( pos->second.empty() ) { - std::string cid(msg->id); - std::string input(msg->input_tag); - Control c(cid, msg->controlType, input, msg->priority, CONTROL_OFF); - pos->second.push_back(c); - } else { - // find this control in the list - std::vector<Control>::iterator v; - for ( v = pos->second.begin(); v != pos->second.end(); ++v ) { - if ( strcmp(v->getId().c_str(), msg->id) == 0 ) { - v->setState(CONTROL_OFF); - break; - } - } - - if ( v == pos->second.end() ) { - // this is a new request, so add it and sort the vector - std::string cid(msg->id); - std::string input(msg->input_tag); - Control c(cid, msg->controlType, input, msg->priority, CONTROL_OFF); - pos->second.push_back(c); - std::sort(pos->second.begin(), pos->second.end()); - } - } - - return 0; -} - -/***************************************************************************** - * Function: unregisterControl - * Description: - * - * @param id -> control identifier - * @param pri -> priority - * @param output -> output (e.g. "o_rly05") - * - * @return 0 on success; -1 on error - *****************************************************************************/ -static int unregisterControl(const char *id, unsigned int pri, const char *output) -{ - // attempt to find the output in the map - StringOutputVector_t::iterator pos; - bool found = false; - - pos = outputMap.find(output); - if ( pos == outputMap.end() ) { - logError("%s: failed to find the designated output %s\n", - __func__, output); - return -1; - } - - // find the control in the list - std::vector<Control>::iterator v; - for ( v = pos->second.begin(); v != pos->second.end(); ++v) { - if ( strcmp(v->getId().c_str(), id) == 0 ) { - pos->second.erase(v); - found = true; - break; - } - } - - if ( !found ) { - logError("%s: failed to find control %s in list", __func__, id); - return -1; - } - return 0; -} - -/***************************************************************************** - * Function: loadPersistentOutputs - * Description: build the output map - * - * @param none - * @return 0 on success; -1 of failure - *****************************************************************************/ -static int loadPersistentOutputs(void) -{ - bool status; - cJSON *root; - int rc = 0; - - printf("\rLoading persistent outputs:\n"); - - std::vector<std::string> file_list = GLOBAL_mdot->listUserFiles(); - - for (std::vector<std::string>::const_iterator i = file_list.begin(); i != file_list.end(); ++i) { - // load in all of the output files - if( strncmp( i->c_str(), OUTPUT_STR, strlen(OUTPUT_STR)) == 0 || - strncmp( i->c_str(), VOUTPUT_STR, strlen(VOUTPUT_STR)) == 0) { - char scratchBuf[MAX_FILE_SIZE]; - - status = GLOBAL_mdot->readUserFile(i->c_str(), scratchBuf, MAX_FILE_SIZE); - if( status != true ) { - logError("%s: failed to read %s", __func__, i->c_str()); - rc = -1; - } else { - logInfo("%s: successfully read %s", __func__, i->c_str()); - } - - root = cJSON_Parse(scratchBuf); - std::string id = cJSON_GetObjectItem(root,"id")->valuestring; - printf("\r output %s loaded\n", i->c_str()); - cJSON_Delete(root); - - // emplace the empty control vector into the output map - std::vector<Control> v; - outputMap[id] = v; - } - } - return rc; -} - -/***************************************************************************** - * Function: dumpEventRecord - * Description: display the contents of an event record - * - * @param[in] ev -> the event structure - * @return none - *****************************************************************************/ -static void dumpEventRecord(EventReasonStruct_t &ev) -{ - const char *mapper[] = { "auto", - "manual", - "timer", - "flow", - "failsafe", - "no control" - }; - - time_t rawtime; - time(&rawtime); - - int iyr=0, imo=0, idy=0, ihr=0, imn=0, isc=0; -#undef TODO_ICE -#if 0 - rtc_get_time(&iyr, &imo, &idy, &ihr, &imn, &isc); -#endif - - printf("\rEVENT RECORD (%04d-%02d-%02d %02d:%02d:%02d)\r\n", - iyr, imo, idy, ihr, imn, isc); - printf("\rev.eventReason = %d (%s)\n", ev.eventReason, mapper[ev.eventReason]); - printf("\rev.inputTag = %s\n", ev.inputTag[0] ? ev.inputTag : "empty"); - printf("\rev.outputTag = %s\n", ev.outputTag[0] ? ev.outputTag : "empty"); - printf("\rev.inputValue = %.02f\n", ev.inputValue); - printf("\rev.outputValue = %.02f (%s)\n", ev.outputValue, - ((unsigned int)ev.outputValue & 1 == 1) ? "on" : "off"); -} - -/***************************************************************************** - * Function: DisplayOutputs - * Description: Display a list of outputs and their controls - * - * @param none - * @return none - *****************************************************************************/ -void DisplayOutputs(void) -{ - StringOutputVector_t::iterator pos; - - for ( pos = outputMap.begin(); pos != outputMap.end(); ++pos ) { - if ( pos->second.empty() ) { - printf("\r [%s]-> [no controls] \n", pos->first.c_str()); - } else { - printf("\r [%s]-> ", pos->first.c_str()); - std::vector<Control>::iterator i; - for ( i = pos->second.begin(); i != pos->second.end(); ++i ) { - i->display(); - } - printf("\n"); - } - } - printf("\r\n"); -}