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/TimerControl.cpp
- Revision:
- 0:61364762ee0e
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ICE-Application/src/ConfigurationHandler/Controls/TimerControl.cpp Tue Jan 24 19:05:33 2017 +0000 @@ -0,0 +1,467 @@ +/****************************************************************************** + * + * File: TimerControl.cpp + * Desciption: ICE Timer Control Class implementation + * + *****************************************************************************/ +#include "TimerControl.h" +#include "ModbusMasterApi.h" +#include "ICELog.h" +#include "cJSON.h" +#include "global.h" +#include "timerUtils.h" +#include "utilities.h" +#include <string> +#include <iostream> +#include <iomanip> + +// for debugging - this can be set via the console command: +// "debug-sp 1 or debug-sp 0 +bool debugTimerControl = false; + +static void debug(const char *fmt, ...) +{ + if ( debugTimerControl ) { + 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: load the pertinents from the control file +// +// @param _controlFile +// @return true if loaded; false otherwise +// +bool TimerControl::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()); + return false; + } + + // validate the control data + if ( !validateControlData(dataBuf) ) { + logError("%s: failed to validate control data", __func__); + return false; + } + + // copy the control data + copyControlData(dataBuf); + + ModbusValue val; + // validate the output + if ( ModbusMasterReadRegister(output, &val) == false ) { + logError("%s failed to find %s", id.c_str(), output.c_str()); + return false; + } + + isVirtualOutput = Util_isVirtualOutput(output) ? true : false; + + 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 TimerControl::validateControlData(const char *buf) +{ + bool rc = true; + cJSON * root = cJSON_Parse(buf); + + if ( !cJSON_HasObjectItem(root, "id") || + !cJSON_HasObjectItem(root, "output") || + !cJSON_HasObjectItem(root, "priority") || + !cJSON_HasObjectItem(root, "day") || + !cJSON_HasObjectItem(root, "startHour") || + !cJSON_HasObjectItem(root, "startMin") || + !cJSON_HasObjectItem(root, "startSec") || + !cJSON_HasObjectItem(root, "duration") || + !cJSON_HasObjectItem(root, "week")) { + logError("%s: control file is missing expected tags", __func__); + rc = false; + } + 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 TimerControl::copyControlData(const char *buf) +{ + cJSON *root = cJSON_Parse(buf); + + id = cJSON_GetObjectItem(root,"id")->valuestring; + output = cJSON_GetObjectItem(root, "output")->valuestring; + priority = (unsigned int)atoi(cJSON_GetObjectItem(root, "priority")->valuestring); + std::string day_str = cJSON_GetObjectItem(root, "day")->valuestring; + + if ( day_str == "sun" ) { + day = DAY_SCHEDULE_SUNDAY_MASK; + } else if ( day_str == "mon" ) { + day = DAY_SCHEDULE_MONDAY_MASK; + } else if ( day_str == "tue" ) { + day = DAY_SCHEDULE_TUESDAY_MASK; + } else if ( day_str == "wed" ) { + day = DAY_SCHEDULE_WEDNESDAY_MASK; + } else if ( day_str == "thu" ) { + day = DAY_SCHEDULE_THURSDAY_MASK; + } else if ( day_str == "fri" ) { + day = DAY_SCHEDULE_FRIDAY_MASK; + } else if ( day_str == "sat" ) { + day = DAY_SCHEDULE_SATURDAY_MASK; + } else { + debug("\r%s:%s-> one-shot timer found\n", __func__, id.c_str()); + day = DAY_SCHEDULE_NOT_SPECIFIED; // one-shot timer + } + + startHour = atoi(cJSON_GetObjectItem(root, "startHour")->valuestring); + startMin = atoi(cJSON_GetObjectItem(root, "startMin")->valuestring); + startSec = atoi(cJSON_GetObjectItem(root, "startSec")->valuestring); + duration = atoi(cJSON_GetObjectItem(root, "duration")->valuestring); + + std::string week_str = cJSON_GetObjectItem(root, "week")->valuestring; + if ( week_str == "every" ) { + week = WEEKLY_CHOICE_EVERY_WEEK; + } else if ( week_str == "first" ) { + week = WEEKLY_CHOICE_FIRST_WEEK; + } else if ( week_str == "second" ) { + week = WEEKLY_CHOICE_SECOND_WEEK; + } else if ( week_str == "third" ) { + week = WEEKLY_CHOICE_THIRD_WEEK; + } else if ( week_str == "fourth" ) { + week = WEEKLY_CHOICE_FOURTH_WEEK; + } else if ( week_str == "last" ) { + week = WEEKLY_CHOICE_LAST_WEEK; + } else if ( week_str == "everyother") { + week = WEEKLY_CHOICE_EVERYOTHER_WEEK; + } else { + week = WEEKLY_CHOICE_NOT_SPECIFIED; // one-shot timer + } + + cJSON_Delete(root); +} + +// +// method: start +// description: initialize the control +// +// @param none +// @return none +// +void TimerControl::start(void) +{ + // calculate the next scheduled start time + if ( day == DAY_SCHEDULE_NOT_SPECIFIED ) { + // start running right away + nextScheduledStartTime = time(0); + debug("\r%s: %s is a one-shot timer.\n", __func__, id.c_str()); + } else { + nextScheduledStartTime = time(0) + calcStartTime(); + } + struct tm * timeinfo; + timeinfo = localtime((time_t*)&nextScheduledStartTime); + debug("Timer %s started, begins at %s", this->id.c_str(), asctime(timeinfo)); + + currentState = STATE_OFF; +} + +// +// method: update +// description: run the state machine +// +// @param none +// @return OK on success; error otherwise +// +TimerControlError_t TimerControl::update(void) +{ + TimerControlError_t rc = TIMER_CONTROL_OK; + + unsigned long currentTime = time(0); + + switch ( this->currentState ) { + case STATE_INIT: + // do nothing; timer is waiting for start signal + break; + case STATE_OFF: { + if ( currentTime >= nextScheduledStartTime && + currentTime <= nextScheduledStartTime + duration ) { + currentState = STATE_RUNNING; + this->startFeed(); + startTime = currentTime; // record the actual start time + debug("\r%s: [OFF]->in timing window->[RUNNING] %s\n", id.c_str(), displayTime()); + } + break; + } + case STATE_RUNNING: + if ( currentTime >= startTime + duration ) { + this->stopFeed(); + currentState = STATE_FINISHED; + debug("\r%s: [RUNNING]->time elapsed->[OFF] %s\n", id.c_str(), displayTime()); + } + break; + case STATE_FINISHED: + if ( day == DAY_SCHEDULE_NOT_SPECIFIED || week == WEEKLY_CHOICE_NOT_SPECIFIED ) { + // one shot-timer, just disable it. + nextScheduledStartTime = 0; + startTime = 0; + currentState = STATE_DISABLED; + debug("\r%s: [FINISHED]->one shot timer->[DISABLED] %s\n", id.c_str(), displayTime()); + } else { + // calculate the next scheduled start time + nextScheduledStartTime = time(0) + calcStartTime(); + startTime = 0; + // unregister with the output task + currentState = STATE_OFF; + debug("\r%s: [FINISHED]->time elapsed->[OFF] %s\n", id.c_str(), displayTime()); + } + this->unregisterControl(); + break; + case STATE_DISABLED: + // do nothing + break; + default: + logError("%s: unknown state %d", __func__, this->currentState); + rc = TIMER_CONTROL_UNK_STATE; + break; + } + return rc; +} + +// +// method: startFeed +// description: signal the output thread to start a feed +// +// @param none +// @return none +void TimerControl::startFeed(void) +{ + logInfo("%s: %s attempting to start feed on relay %s\n", + __func__, controlFile.c_str(), output.c_str()); + + if ( isVirtualOutput ) { + // write to the virtual register map + ModbusMasterWriteRegister(output, 1.0); + } else { + // send a message to the output thread + OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc(); + memset(output_mail, 0, sizeof(OutputControlMsg_t)); + + output_mail->action = ACTION_CONTROL_ON; + output_mail->controlType = CONTROL_TIMER; + output_mail->priority = 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: stopFeed +// description: signal the output thread to stop a feed +// +// @param none +// @return none +// +void TimerControl::stopFeed(void) +{ + logInfo("%s: %s attempting to start feed on relay %s\n", + __func__, controlFile.c_str(), output.c_str()); + + if ( isVirtualOutput ) { + // write to the virtual register map + ModbusMasterWriteRegister(output, 0.0); + } else { + // send a message to the output thread + OutputControlMsg_t *output_mail = OutputMasterMailBox.alloc(); + memset(output_mail, 0, sizeof(OutputControlMsg_t)); + + output_mail->action = ACTION_CONTROL_OFF; + output_mail->controlType = CONTROL_TIMER; + output_mail->priority = 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: unregisterControl +// description: send OFF indication to Output Master for this control's +// relay +// +// @param none +// @return none +// +void TimerControl::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_TIMER; + output_mail->priority = 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: calcStarTime +// description: calculate the number of seconds until the next +// timer starts. +// +// @param[in] none +// @param[out] none +// @return none +// +unsigned long TimerControl::calcStartTime(void) const +{ + TSC_SCHED_ELEM schedTimeDate; + TSC_SCHED_ELEM pNowTimeDate; + unsigned long pDiffInSeconds; + CNTL_HR_MIN_STRUCT startTime; + + startTime.hour = startHour; + startTime.minute = startMin; + startTime.second = startSec; + + pNowTimeDate.calTime = time( NULL ); + localtime_r( &pNowTimeDate.calTime, &pNowTimeDate.requestTime ); + + pNowTimeDate.schedule.startDate.year = pNowTimeDate.requestTime.tm_year - 100; /* years since 1900 */ + pNowTimeDate.schedule.startDate.year += 2000; + pNowTimeDate.schedule.startDate.month = pNowTimeDate.requestTime.tm_mon + 1; /* months 1 - 12 */ + pNowTimeDate.schedule.startDate.day = pNowTimeDate.requestTime.tm_mday; + pNowTimeDate.schedule.startTime.hour = pNowTimeDate.requestTime.tm_hour; + pNowTimeDate.schedule.startTime.minute = pNowTimeDate.requestTime.tm_min; + pNowTimeDate.schedule.startTime.second = pNowTimeDate.requestTime.tm_sec; + + /* setup schedTimeDate with the current date, time and the required schedule */ + schedTimeDate.schedule.startDate = pNowTimeDate.schedule.startDate; + schedTimeDate.schedule.startTime = startTime; + schedTimeDate.schedule.dailySchedule = day; + schedTimeDate.schedule.weeklySchedule = (WEEKLY_CHOICES)week; + schedTimeDate.calTime = pNowTimeDate.calTime; + schedTimeDate.requestTime = pNowTimeDate.requestTime; + + TSCutilComputeStartDate( &schedTimeDate ); + Util_dateTimeDiff( &pNowTimeDate.schedule, &schedTimeDate.schedule, &pDiffInSeconds ); + + return pDiffInSeconds; +} + +const char* TimerControl::displayTime(void) +{ + struct tm * timeinfo; + time_t currentTime = time(0); + timeinfo = localtime(¤tTime); + return asctime(timeinfo); +} + +// +// method: display +// description: display the elements of this timer control object +// +// @param none +// @return none +// +void TimerControl::display(void) +{ + string mapper[] = { "INIT", + "OFF", + "RUNNING", + "FINISHED", + "DISABLED" + }; + + printf("\r\n"); + std::string day_str; + if ( day == DAY_SCHEDULE_SUNDAY_MASK ) { + day_str = "Sun"; + } else if ( day == DAY_SCHEDULE_MONDAY_MASK ) { + day_str = "Mon"; + } else if ( day == DAY_SCHEDULE_TUESDAY_MASK ) { + day_str = "Tue"; + } else if ( day == DAY_SCHEDULE_WEDNESDAY_MASK ) { + day_str = "Wed"; + } else if ( day == DAY_SCHEDULE_THURSDAY_MASK ) { + day_str = "Thu"; + } else if ( day == DAY_SCHEDULE_FRIDAY_MASK ) { + day_str = "Fri"; + } else if ( day == DAY_SCHEDULE_SATURDAY_MASK ) { + day_str = "Sat"; + } + std::string week_str; + if ( week == WEEKLY_CHOICE_EVERY_WEEK ) { + week_str = "every"; + } else if ( week == WEEKLY_CHOICE_FIRST_WEEK ) { + week_str = "first"; + } else if ( week == WEEKLY_CHOICE_SECOND_WEEK ) { + week_str = "second"; + } else if ( week == WEEKLY_CHOICE_THIRD_WEEK ) { + week_str = "third"; + } else if ( week == WEEKLY_CHOICE_FOURTH_WEEK ) { + week_str = "fourth"; + } else if ( week == WEEKLY_CHOICE_LAST_WEEK ) { + week_str = "last"; + } else if ( week == WEEKLY_CHOICE_EVERYOTHER_WEEK ) { + week_str = "everyother"; + } + + char time_str[12]; + snprintf(time_str, sizeof(time_str), "%02d:%02d:%02d", startHour, startMin, startSec); + + cout << left << setw(8) << setfill(' ') << "timer:"; + cout << left << setw(32) << setfill(' ') << controlFile; + cout << left << setw(12) << setfill(' ') << id; + cout << "output: " << left << setw(16) << setfill(' ') << output; + cout << left << setw(6) << setfill(' ') << priority; + cout << "(" << setw(12) << week_str << ")" << day_str << "-> "; + cout << left << setw(12) << setfill(' ') << time_str; + cout << left << setw(10) << "duration: " << left << setw(6) << setfill(' ') << duration; + cout << left << setw(10) << setfill(' ') << mapper[currentState]; + if ( currentState == STATE_RUNNING ) { + cout << left << setw(11) << setfill(' ') << "ends in: " << (startTime + duration) - time(NULL) << " seconds "; + } else if ( currentState == STATE_FINISHED || currentState == STATE_DISABLED ) { + // show nothing + } else { + cout << left << setw(11) << setfill(' ') << "starts in: " << calcStartTime() << " seconds "; + } + cout.flush(); +}