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:
- 1:b2e90cda7a5a
- Parent:
- 0:61364762ee0e
File content as of revision 1:b2e90cda7a5a:
/******************************************************************************
*
* 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();
}