330 lines
8.5 KiB
C
330 lines
8.5 KiB
C
/****************************************************************************
|
|
* 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 <config.h>
|
|
#endif
|
|
|
|
#include "./u_timeprogram_p.h"
|
|
|
|
#include "aqhome-react/types/prgrule.h"
|
|
|
|
#include <gwenhywfar/debug.h>
|
|
#include <gwenhywfar/xml.h>
|
|
#include <gwenhywfar/text.h>
|
|
#include <gwenhywfar/gwendate.h>
|
|
#include <gwenhywfar/gwentime.h>
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* 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(AQH_OBJECT *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))<action->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;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|