/**************************************************************************** * This file is part of the project AqHome. * AqHome (c) by 2024 Martin Preuss, all rights reserved. * * The license for this file can be found in the file COPYING which you * should have received along with this file. ****************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include "./u_timeprogram_p.h" #include #include #include #include #include /* ------------------------------------------------------------------------------------------------ * defines * ------------------------------------------------------------------------------------------------ */ #define AQHOMEREACT_UNIT_TIMEPROGRAM_OUTSLOT_OUTPUT 0 GWEN_INHERIT(AQHREACT_UNIT, AQHREACT_UNIT_TIMEPROGRAM); /* ------------------------------------------------------------------------------------------------ * forward declarations * ------------------------------------------------------------------------------------------------ */ static MODULE_TIMER_ACTION *ModuleTimerAction_new(int hourTime, double value); static void ModuleTimerAction_free(MODULE_TIMER_ACTION *act); static void GWENHYWFAR_CB _freeData(void *bp, void *p); static int _cbProcess(AQHREACT_UNIT *unit); static int _readRules(AQHREACT_UNIT *unit); static int _handleActionsForGivenTime(AQHREACT_UNIT *unit, int hourMins); static int _checkAndAddActions(AQHREACT_UNIT *unit); static int _addActionsForGivenTime(const GWEN_TIME *ti, int hour, const AQHREACT_PRGRULE_LIST *ruleList, MODULE_TIMER_ACTION_LIST *actionList); /* ------------------------------------------------------------------------------------------------ * implementations * ------------------------------------------------------------------------------------------------ */ GWEN_LIST_FUNCTIONS(MODULE_TIMER_ACTION, ModuleTimerAction); /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * ModuleTimerAction * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ MODULE_TIMER_ACTION *ModuleTimerAction_new(int hourTime, double value) { MODULE_TIMER_ACTION *act; GWEN_NEW_OBJECT(MODULE_TIMER_ACTION, act); GWEN_LIST_INIT(MODULE_TIMER_ACTION, act); act->hourMinutes=hourTime; act->value=value; return act; } void ModuleTimerAction_free(MODULE_TIMER_ACTION *act) { if (act) { GWEN_LIST_FINI(MODULE_TIMER_ACTION, act); GWEN_FREE_OBJECT(act); } } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * AqHomeReact_UnitTimeProgram * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ AQHREACT_UNIT *AqHomeReact_UnitTimeProgram_new(AQHOME_REACT *aqh) { AQHREACT_UNIT *unit; AQHREACT_UNIT_TIMEPROGRAM *xunit; AQHREACT_PORT *port; AQHREACT_PARAM *param; unit=AQHREACT_Unit_new(aqh); AQHREACT_Unit_SetTypeName(unit, "timeraction"); AQHREACT_Unit_SetDescription(unit, "Programmable timer"); GWEN_NEW_OBJECT(AQHREACT_UNIT_TIMEPROGRAM, xunit); GWEN_INHERIT_SETDATA(AQHREACT_UNIT, AQHREACT_UNIT_TIMEPROGRAM, unit, xunit, _freeData); xunit->actionList=ModuleTimerAction_List_new(); AQHREACT_Unit_SetProcessFn(unit, _cbProcess); port=AQHREACT_Port_new(); AQHREACT_Port_SetName(port, "output"); AQHREACT_Port_SetIdForUnit(port, AQHOMEREACT_UNIT_TIMEPROGRAM_OUTSLOT_OUTPUT); AQHREACT_Port_SetDataType(port, AQHREACT_DATAOBJECTTYPE_DOUBLE); AQHREACT_Unit_AddOutputPort(unit, port); param=AQHREACT_Param_new(); AQHREACT_Param_SetName(param, AQHOMEREACT_UNIT_TIMEPROGRAM_PARAM_RULES); AQHREACT_Param_SetDataType(param, AQHREACT_DATAOBJECTTYPE_STRING); AQHREACT_Unit_AddParam(unit, param); return unit; } void GWENHYWFAR_CB _freeData(GWEN_UNUSED void *bp, void *p) { AQHREACT_UNIT_TIMEPROGRAM *xunit; xunit=(AQHREACT_UNIT_TIMEPROGRAM*) p; ModuleTimerAction_List_free(xunit->actionList); AQHREACT_PrgRule_List_free(xunit->ruleList); GWEN_FREE_OBJECT(xunit); } int _cbProcess(AQHREACT_UNIT *unit) { int didSomething=0; if (unit) { AQHREACT_UNIT_TIMEPROGRAM *xunit; xunit=GWEN_INHERIT_GETDATA(AQHREACT_UNIT, AQHREACT_UNIT_TIMEPROGRAM, unit); if (xunit) { int rv; if (xunit->ruleList==NULL) { rv=_readRules(unit); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); return rv; } didSomething=1; } rv=_checkAndAddActions(unit); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); return rv; } if (rv>0) didSomething=1; } } return didSomething?1:0; } int _checkAndAddActions(AQHREACT_UNIT *unit) { AQHREACT_UNIT_TIMEPROGRAM *xunit; GWEN_TIME *ti; int didSomething=0; xunit=GWEN_INHERIT_GETDATA(AQHREACT_UNIT, AQHREACT_UNIT_TIMEPROGRAM, unit); ti=GWEN_CurrentTime(); if (ti) { int hours; int mins; int secs; int hourMins; int rv; /* translate current time */ rv=GWEN_Time_GetBrokenDownTime(ti, &hours, &mins, &secs); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); GWEN_Time_free(ti); return rv; } hourMins=(hours*60)+mins; rv=GWEN_Time_AddSeconds(ti, 60*60); /* next hour */ if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); GWEN_Time_free(ti); return rv; } rv=GWEN_Time_GetBrokenDownTime(ti, &hours, &mins, &secs); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); GWEN_Time_free(ti); return rv; } if (hours!=xunit->lastActionHour) { DBG_INFO(NULL, "Adding actions for next hour %02d", hours); _addActionsForGivenTime(ti, hours, xunit->ruleList, xunit->actionList); xunit->lastActionHour=hours; didSomething=1; } GWEN_Time_free(ti); rv=_handleActionsForGivenTime(unit, hourMins); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); return rv; } if (rv>0) didSomething=1; } return didSomething?1:0; } int _handleActionsForGivenTime(AQHREACT_UNIT *unit, int hourMins) { AQHREACT_UNIT_TIMEPROGRAM *xunit; MODULE_TIMER_ACTION *action; int valueSent=0; xunit=GWEN_INHERIT_GETDATA(AQHREACT_UNIT, AQHREACT_UNIT_TIMEPROGRAM, unit); action=ModuleTimerAction_List_First(xunit->actionList); while(action) { MODULE_TIMER_ACTION *nextAction; nextAction=ModuleTimerAction_List_Next(action); if ((hourMins>action->hourMinutes) || (hourMins+(23*60))hourMinutes) { ModuleTimerAction_List_Del(action); DBG_INFO(NULL, "Sending output value %.2f", action->value); AQHREACT_Unit_OutputDoubleData(unit, AQHOMEREACT_UNIT_TIMEPROGRAM_OUTSLOT_OUTPUT, action->value); ModuleTimerAction_free(action); valueSent=1; } action=nextAction; } return valueSent?1:0; } int _addActionsForGivenTime(const GWEN_TIME *ti, int hour, const AQHREACT_PRGRULE_LIST *ruleList, MODULE_TIMER_ACTION_LIST *actionList) { int actionsAdded=0; GWEN_DATE *dt; int dayOfMonth; int month; int dayOfWeek; int mins; dt=GWEN_Date_fromTime(ti); month=GWEN_Date_GetMonth(dt); dayOfMonth=GWEN_Date_GetDay(dt); dayOfWeek=GWEN_Date_WeekDay(dt); GWEN_Date_free(dt); for (mins=0; mins<60; mins++) { const AQHREACT_PRGRULE *rule; rule=AQHREACT_PrgRule_List_First(ruleList); while(rule) { if (AQHREACT_PrgRule_Matches(rule, mins, hour, dayOfMonth, month, dayOfWeek)>0) { MODULE_TIMER_ACTION *act; act=ModuleTimerAction_new((hour*60)+mins, AQHREACT_PrgRule_GetValue(rule)); ModuleTimerAction_List_Add(act, actionList); actionsAdded=1; } rule=AQHREACT_PrgRule_List_Next(rule); } } return actionsAdded?1:0; } int _readRules(AQHREACT_UNIT *unit) { AQHREACT_UNIT_TIMEPROGRAM *xunit; const char *s; xunit=GWEN_INHERIT_GETDATA(AQHREACT_UNIT, AQHREACT_UNIT_TIMEPROGRAM, unit); AQHREACT_PrgRule_List_free(xunit->ruleList); xunit->ruleList=NULL; s=AQHREACT_Unit_GetParamValueString(unit, AQHOMEREACT_UNIT_TIMEPROGRAM_PARAM_RULES, NULL); if (s) { xunit->ruleList=AQHREACT_PrgRule_ReadRules(s); if (xunit->ruleList==NULL) { DBG_INFO(NULL, "Error reading rules from [%s]", s); return GWEN_ERROR_BAD_DATA; } return 0; } else { DBG_INFO(NULL, "No rules"); return GWEN_ERROR_NO_DATA; } }