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