From 87114cecea1536489047770529aaabd5db4a87cb Mon Sep 17 00:00:00 2001 From: Martin Preuss Date: Sat, 20 Apr 2024 17:28:20 +0200 Subject: [PATCH] aqhome-react: more work on modules and networks. - tested AND network and new suntime units. - add unit XML property "invert" (inverts output for logical units) --- apps/aqhome-react/aqhome_react.c | 3 + apps/aqhome-react/examples/001-tvlight.xml | 26 +- apps/aqhome-react/networks/delayedoff.xml | 13 +- apps/aqhome-react/types/link.c | 5 +- apps/aqhome-react/types/port.c | 3 +- apps/aqhome-react/types/unit.c | 19 ++ apps/aqhome-react/types/unit.h | 5 + apps/aqhome-react/types/unit_p.h | 1 + apps/aqhome-react/units/0BUILD | 3 + apps/aqhome-react/units/u_highpass.h | 1 + apps/aqhome-react/units/u_logical.c | 270 ++++++++++++------ apps/aqhome-react/units/u_logical.h | 5 + apps/aqhome-react/units/u_module.c | 10 +- apps/aqhome-react/units/u_suntime.c | 302 +++++++++++++++++++++ apps/aqhome-react/units/u_suntime.h | 33 +++ apps/aqhome-react/units/u_suntime_p.h | 39 +++ apps/aqhome-react/units/u_valueset.c | 7 + 17 files changed, 641 insertions(+), 104 deletions(-) create mode 100644 apps/aqhome-react/units/u_suntime.c create mode 100644 apps/aqhome-react/units/u_suntime.h create mode 100644 apps/aqhome-react/units/u_suntime_p.h diff --git a/apps/aqhome-react/aqhome_react.c b/apps/aqhome-react/aqhome_react.c index 060a51e..eb26316 100644 --- a/apps/aqhome-react/aqhome_react.c +++ b/apps/aqhome-react/aqhome_react.c @@ -19,6 +19,7 @@ #include "aqhome-react/units/u_lowpass.h" #include "aqhome-react/units/u_highpass.h" #include "aqhome-react/units/u_zeroposnegstring.h" +#include "aqhome-react/units/u_suntime.h" #include @@ -168,6 +169,8 @@ AQHREACT_UNIT *AqHomeReact_CreateUnitByName(AQHOME_REACT *aqh, const char *unitT return AqHomeReact_UnitHighPass_new(aqh); else if (strcasecmp(unitType, "zeroPosNegString")==0) return AqHomeReact_UnitZeroPosNegString_new(aqh); + else if (strcasecmp(unitType, "suntime")==0) + return AqHomeReact_UnitSuntime_new(aqh); else { AQHREACT_UNIT *unit; diff --git a/apps/aqhome-react/examples/001-tvlight.xml b/apps/aqhome-react/examples/001-tvlight.xml index b1bf788..f763e22 100644 --- a/apps/aqhome-react/examples/001-tvlight.xml +++ b/apps/aqhome-react/examples/001-tvlight.xml @@ -8,6 +8,15 @@ + + + 52.619425 + 10.087891 + 0.0 + 30.0 + + + 30 @@ -15,6 +24,17 @@ + + + + + OFF + OFF + ON + + + + mqtt/109C2F/power @@ -26,6 +46,10 @@ - + + + + + diff --git a/apps/aqhome-react/networks/delayedoff.xml b/apps/aqhome-react/networks/delayedoff.xml index 062acad..ec1fe52 100644 --- a/apps/aqhome-react/networks/delayedoff.xml +++ b/apps/aqhome-react/networks/delayedoff.xml @@ -20,15 +20,6 @@ - - - OFF - OFF - ON - - - - 0.0 @@ -44,9 +35,7 @@ - - - + diff --git a/apps/aqhome-react/types/link.c b/apps/aqhome-react/types/link.c index d9994cc..d376da0 100644 --- a/apps/aqhome-react/types/link.c +++ b/apps/aqhome-react/types/link.c @@ -83,10 +83,11 @@ void AQHREACT_Link_Dump(const AQHREACT_LINK *lnk, GWEN_BUFFER *buf, int indent) portName=(lnk->targetPort)?AQHREACT_Port_GetName(lnk->targetPort):NULL; if (indent) GWEN_Buffer_FillWithBytes(buf, ' ', indent); - GWEN_Buffer_AppendArgs(buf, "- target unit id: %s (%s), target slot name: %s\n", + GWEN_Buffer_AppendArgs(buf, "- target unit id: %s (%s), target slot name: %s [%d]\n", unitId?unitId:"", unitName?unitName:"", - portName?portName:""); + portName?portName:"", + (lnk->targetPort)?AQHREACT_Port_GetIdForUnit(lnk->targetPort):0); } } diff --git a/apps/aqhome-react/types/port.c b/apps/aqhome-react/types/port.c index a4292d2..9064225 100644 --- a/apps/aqhome-react/types/port.c +++ b/apps/aqhome-react/types/port.c @@ -263,8 +263,9 @@ void AQHREACT_Port_Dump(const AQHREACT_PORT *port, GWEN_BUFFER *buf, int indent) if (port && buf) { if (indent) GWEN_Buffer_FillWithBytes(buf, ' ', indent); - GWEN_Buffer_AppendArgs(buf, "- \"%s\" (%s, %08x)\n", + GWEN_Buffer_AppendArgs(buf, "- \"%s\"[%d] (%s, %08x)\n", (port->name)?(port->name):"", + port->idForUnit, AQHREACT_DataObjectType_toString(port->dataType), port->flags); if (port->linkList && AQHREACT_Link_List_GetCount(port->linkList)) diff --git a/apps/aqhome-react/types/unit.c b/apps/aqhome-react/types/unit.c index 43c58c4..e3e7f9e 100644 --- a/apps/aqhome-react/types/unit.c +++ b/apps/aqhome-react/types/unit.c @@ -167,6 +167,24 @@ void AQHREACT_Unit_SetGpTimestamp(AQHREACT_UNIT *unit, uint64_t t) +int AQHREACT_Unit_GetGpInt(const AQHREACT_UNIT *unit) +{ + return unit?unit->gpInt:0; +} + + + +void AQHREACT_Unit_SetGpInt(AQHREACT_UNIT *unit, int i) +{ + if (unit) + unit->gpInt=i; +} + + + + + + AQHREACT_PORT_LIST *AQHREACT_Unit_GetInputPortList(const AQHREACT_UNIT *unit) { return unit?unit->inputPortList:NULL; @@ -214,6 +232,7 @@ AQHREACT_PORT *AQHREACT_Unit_GetOrCreateUnusedInputPortByName(AQHREACT_UNIT *uni AQHREACT_PORT *newPort; newPort=AQHREACT_Port_dup(port); + AQHREACT_Port_SetIdForUnit(port, (unit->nextInputPortId)++); AQHREACT_Unit_AddInputPort(unit, newPort); return newPort; } diff --git a/apps/aqhome-react/types/unit.h b/apps/aqhome-react/types/unit.h index 32dca05..e64e1ad 100644 --- a/apps/aqhome-react/types/unit.h +++ b/apps/aqhome-react/types/unit.h @@ -22,6 +22,7 @@ GWEN_INHERIT_FUNCTION_DEFS(AQHREACT_UNIT) #define AQHREACT_UNIT_FLAGS_CHANGED 0x80000000 #define AQHREACT_UNIT_FLAGS_INUSE 0x40000000 #define AQHREACT_UNIT_FLAGS_MULTI 0x20000000 +#define AQHREACT_UNIT_FLAGS_INVERT 0x10000000 /** invert output of boolean units */ #include "aqhome-react/aqhome_react.h" @@ -62,6 +63,10 @@ void AQHREACT_Unit_SubFlags(AQHREACT_UNIT *unit, uint32_t i); uint64_t AQHREACT_Unit_GetGpTimestamp(const AQHREACT_UNIT *unit); void AQHREACT_Unit_SetGpTimestamp(AQHREACT_UNIT *unit, uint64_t t); +/** general purpose integer, unit is free to use it */ +int AQHREACT_Unit_GetGpInt(const AQHREACT_UNIT *unit); +void AQHREACT_Unit_SetGpInt(AQHREACT_UNIT *unit, int i); + AQHREACT_PORT_LIST *AQHREACT_Unit_GetInputPortList(const AQHREACT_UNIT *unit); void AQHREACT_Unit_AddInputPort(AQHREACT_UNIT *unit, AQHREACT_PORT *port); diff --git a/apps/aqhome-react/types/unit_p.h b/apps/aqhome-react/types/unit_p.h index daef767..1bd7301 100644 --- a/apps/aqhome-react/types/unit_p.h +++ b/apps/aqhome-react/types/unit_p.h @@ -24,6 +24,7 @@ struct AQHREACT_UNIT { uint32_t flags; uint64_t gpTimestamp; + int gpInt; int nextInputPortId; diff --git a/apps/aqhome-react/units/0BUILD b/apps/aqhome-react/units/0BUILD index fde3420..9359677 100644 --- a/apps/aqhome-react/units/0BUILD +++ b/apps/aqhome-react/units/0BUILD @@ -49,6 +49,8 @@ u_zeroposnegstring.h u_module.h u_module_p.h + u_suntime.h + u_suntime_p.h @@ -64,6 +66,7 @@ u_valueset.c u_zeroposnegstring.c u_module.c + u_suntime.c diff --git a/apps/aqhome-react/units/u_highpass.h b/apps/aqhome-react/units/u_highpass.h index 4fd98c9..7452d3f 100644 --- a/apps/aqhome-react/units/u_highpass.h +++ b/apps/aqhome-react/units/u_highpass.h @@ -18,6 +18,7 @@ #define AQHOMEREACT_UNIT_HIGHPASS_PARAM_NEWVALUE "newValue" + AQHREACT_UNIT *AqHomeReact_UnitHighPass_new(AQHOME_REACT *aqh); diff --git a/apps/aqhome-react/units/u_logical.c b/apps/aqhome-react/units/u_logical.c index 4a2dfe4..ec2e934 100644 --- a/apps/aqhome-react/units/u_logical.c +++ b/apps/aqhome-react/units/u_logical.c @@ -21,8 +21,8 @@ * ------------------------------------------------------------------------------------------------ */ -#define AQHOMEREACT_UNIT_LOGICAL_INSLOT_INPUT 0 -#define AQHOMEREACT_UNIT_LOGICAL_INSLOT_AUTOINPUT 100 +#define AQHOMEREACT_UNIT_LOGICAL_INSLOT_INPUT 100 +#define AQHOMEREACT_UNIT_LOGICAL_INSLOT_AUTOINPUT 101 #define AQHOMEREACT_UNIT_LOGICAL_OUTSLOT_RESULT 0 @@ -34,9 +34,11 @@ */ static AQHREACT_UNIT *_unitLogical_new(AQHOME_REACT *aqh); +static int _outputResult(AQHREACT_UNIT *unit, AQHREACT_PORT *port, int result); static int _cbProcessOr(AQHREACT_UNIT *unit); static int _cbProcessAnd(AQHREACT_UNIT *unit); static int _cbProcessXor(AQHREACT_UNIT *unit); +static int _cbProcessInvert(AQHREACT_UNIT *unit); @@ -59,6 +61,20 @@ AQHREACT_UNIT *AqHomeReact_UnitOr_new(AQHOME_REACT *aqh) +AQHREACT_UNIT *AqHomeReact_UnitNor_new(AQHOME_REACT *aqh) +{ + AQHREACT_UNIT *unit; + + unit=_unitLogical_new(aqh); + AQHREACT_Unit_SetTypeName(unit, "or"); + AQHREACT_Unit_SetDescription(unit, "Logical NOR inputs"); + AQHREACT_Unit_AddFlags(unit, AQHREACT_UNIT_FLAGS_INVERT); + AQHREACT_Unit_SetProcessFn(unit, _cbProcessOr); + return unit; +} + + + AQHREACT_UNIT *AqHomeReact_UnitAnd_new(AQHOME_REACT *aqh) { AQHREACT_UNIT *unit; @@ -72,6 +88,20 @@ AQHREACT_UNIT *AqHomeReact_UnitAnd_new(AQHOME_REACT *aqh) +AQHREACT_UNIT *AqHomeReact_UnitNand_new(AQHOME_REACT *aqh) +{ + AQHREACT_UNIT *unit; + + unit=_unitLogical_new(aqh); + AQHREACT_Unit_SetTypeName(unit, "and"); + AQHREACT_Unit_SetDescription(unit, "Logical NAND inputs"); + AQHREACT_Unit_AddFlags(unit, AQHREACT_UNIT_FLAGS_INVERT); + AQHREACT_Unit_SetProcessFn(unit, _cbProcessAnd); + return unit; +} + + + AQHREACT_UNIT *AqHomeReact_UnitXor_new(AQHOME_REACT *aqh) { AQHREACT_UNIT *unit; @@ -85,6 +115,19 @@ AQHREACT_UNIT *AqHomeReact_UnitXor_new(AQHOME_REACT *aqh) +AQHREACT_UNIT *AqHomeReact_UnitInvert_new(AQHOME_REACT *aqh) +{ + AQHREACT_UNIT *unit; + + unit=_unitLogical_new(aqh); + AQHREACT_Unit_SetTypeName(unit, "invert"); + AQHREACT_Unit_SetDescription(unit, "Logical invert input"); + AQHREACT_Unit_SetProcessFn(unit, _cbProcessInvert); + return unit; +} + + + AQHREACT_UNIT *_unitLogical_new(AQHOME_REACT *aqh) { @@ -92,9 +135,8 @@ AQHREACT_UNIT *_unitLogical_new(AQHOME_REACT *aqh) AQHREACT_PORT *port; unit=AQHREACT_Unit_new(aqh); - AQHREACT_Unit_SetTypeName(unit, "or"); - AQHREACT_Unit_SetDescription(unit, "Logical OR inputs"); + AQHREACT_Unit_SetGpInt(unit, -1); AQHREACT_Unit_SetNextInputPortId(unit, AQHOMEREACT_UNIT_LOGICAL_INSLOT_AUTOINPUT); /* for auto-gen multi-slots */ port=AQHREACT_Port_new(); @@ -115,6 +157,24 @@ AQHREACT_UNIT *_unitLogical_new(AQHOME_REACT *aqh) +int _outputResult(AQHREACT_UNIT *unit, AQHREACT_PORT *port, int result) +{ + int currentState; + + currentState=AQHREACT_Unit_GetGpInt(unit); + if (AQHREACT_Unit_GetFlags(unit) & AQHREACT_UNIT_FLAGS_INVERT) + result^=1; + if (currentState!=result) { + DBG_INFO(NULL, "%s: Changing result to %d", AQHREACT_Unit_GetId(unit), result); + AQHREACT_Unit_OutputDoubleData(unit, port, result?1.0:0.0); + AQHREACT_Unit_SetGpInt(unit, result); + return 1; /* we changed something */ + } + return 0; /* nothing changed */ +} + + + int _cbProcessOr(AQHREACT_UNIT *unit) { if (unit && AQHREACT_Unit_InputHasChanged(unit)) { @@ -122,42 +182,39 @@ int _cbProcessOr(AQHREACT_UNIT *unit) outputPort=AQHREACT_Unit_GetOutputPortByIdForUnit(unit, AQHOMEREACT_UNIT_LOGICAL_OUTSLOT_RESULT); if (outputPort) { - AQHREACT_PORT_LIST *portList; + const AQHREACT_PORT *port; + int result=0; + int numHandledInputs=0; - portList=AQHREACT_Unit_GetInputPortList(unit); - if (portList) { - const AQHREACT_PORT *port; - int result=0; + port=AQHREACT_Port_List_First(AQHREACT_Unit_GetInputPortList(unit)); + while(port) { + const char *portName; + const AQHREACT_DATAOBJECT *dataObject; - port=AQHREACT_Port_List_First(portList); - while(port) { - const char *portName; - const AQHREACT_DATAOBJECT *dataObject; + portName=AQHREACT_Port_GetName(port); + dataObject=AQHREACT_Port_GetCurrentDataObject(port); + if (dataObject) { + if (AQHREACT_DataObject_GetDataType(dataObject)==AQHREACT_DATAOBJECTTYPE_DOUBLE) { + double d; - portName=AQHREACT_Port_GetName(port); - dataObject=AQHREACT_Port_GetCurrentDataObject(port); - if (dataObject) { - if (AQHREACT_DataObject_GetDataType(dataObject)==AQHREACT_DATAOBJECTTYPE_DOUBLE) { - double d; - - d=AQHREACT_DataObject_GetDoubleData(dataObject); - if (d>0.0) - result|=1; - } - else { - DBG_ERROR(NULL, "Data at input slot \"%s\" isn't DOUBLE, ignoring", portName?portName:""); - } + d=AQHREACT_DataObject_GetDoubleData(dataObject); + if (d>0.0) + result|=1; + numHandledInputs++; } else { - DBG_ERROR(NULL, "Data at input slot \"%s\" has not current data, ignoring", portName?portName:""); + DBG_ERROR(NULL, "Data at input slot \"%s\" isn't DOUBLE, ignoring", portName?portName:""); } - port=AQHREACT_Port_List_Next(port); } - - AQHREACT_Unit_ClearChangeFlagsInUnitAndInputPorts(unit); - AQHREACT_Unit_OutputDoubleData(unit, outputPort, result?1.0:0.0); - return 1; + else { + DBG_ERROR(NULL, "Data at input slot \"%s\" has not current data, ignoring", portName?portName:""); + } + port=AQHREACT_Port_List_Next(port); } + + AQHREACT_Unit_ClearChangeFlagsInUnitAndInputPorts(unit); + if (numHandledInputs) + return _outputResult(unit, outputPort, result); } } @@ -173,42 +230,41 @@ int _cbProcessAnd(AQHREACT_UNIT *unit) outputPort=AQHREACT_Unit_GetOutputPortByIdForUnit(unit, AQHOMEREACT_UNIT_LOGICAL_OUTSLOT_RESULT); if (outputPort) { - AQHREACT_PORT_LIST *portList; + AQHREACT_PORT *port; + int result=1; + int numHandledInputs=0; - portList=AQHREACT_Unit_GetInputPortList(unit); - if (portList) { - AQHREACT_PORT *port; - int result=1; + port=AQHREACT_Port_List_First(AQHREACT_Unit_GetInputPortList(unit)); + while(port) { + const char *portName; + const AQHREACT_DATAOBJECT *dataObject; - port=AQHREACT_Port_List_First(portList); - while(port) { - const char *portName; - const AQHREACT_DATAOBJECT *dataObject; + portName=AQHREACT_Port_GetName(port); + dataObject=AQHREACT_Port_GetCurrentDataObject(port); + if (dataObject) { + if (AQHREACT_DataObject_GetDataType(dataObject)==AQHREACT_DATAOBJECTTYPE_DOUBLE) { + double d; - portName=AQHREACT_Port_GetName(port); - dataObject=AQHREACT_Port_GetCurrentDataObject(port); - if (dataObject) { - if (AQHREACT_DataObject_GetDataType(dataObject)==AQHREACT_DATAOBJECTTYPE_DOUBLE) { - double d; - - d=AQHREACT_DataObject_GetDoubleData(dataObject); - if (!(d>0.0)) - result=0; - } - else { - DBG_ERROR(NULL, "Data at input slot \"%s\" isn't DOUBLE, ignoring", portName?portName:""); - } + d=AQHREACT_DataObject_GetDoubleData(dataObject); + if (!(d>0.0)) + result=0; + numHandledInputs++; } else { - DBG_ERROR(NULL, "Data at input slot \"%s\" has not current data, ignoring", portName?portName:""); + DBG_ERROR(NULL, "Data at input slot \"%s\" isn't DOUBLE, ignoring", portName?portName:""); } - port=AQHREACT_Port_List_Next(port); } - - AQHREACT_Unit_ClearChangeFlagsInUnitAndInputPorts(unit); - AQHREACT_Unit_OutputDoubleData(unit, port, result?1.0:0.0); - return 1; + else { + DBG_ERROR(NULL, "Data at input slot \"%s\" (%d) has no current data, assuming 0", + portName?portName:"", AQHREACT_Port_GetIdForUnit(port)); + result=0; + } + port=AQHREACT_Port_List_Next(port); } + + AQHREACT_Unit_ClearChangeFlagsInUnitAndInputPorts(unit); + if (numHandledInputs) + return _outputResult(unit, outputPort, result); } } @@ -224,41 +280,81 @@ int _cbProcessXor(AQHREACT_UNIT *unit) outputPort=AQHREACT_Unit_GetOutputPortByIdForUnit(unit, AQHOMEREACT_UNIT_LOGICAL_OUTSLOT_RESULT); if (outputPort) { - AQHREACT_PORT_LIST *portList; + AQHREACT_PORT *port; + int result=0; + int numHandledInputs=0; - portList=AQHREACT_Unit_GetInputPortList(unit); - if (portList) { - AQHREACT_PORT *port; - int result=0; + port=AQHREACT_Port_List_First(AQHREACT_Unit_GetInputPortList(unit)); + while(port) { + const char *portName; + const AQHREACT_DATAOBJECT *dataObject; - port=AQHREACT_Port_List_First(portList); - while(port) { - const char *portName; - const AQHREACT_DATAOBJECT *dataObject; + portName=AQHREACT_Port_GetName(port); + dataObject=AQHREACT_Port_GetCurrentDataObject(port); + if (dataObject) { + if (AQHREACT_DataObject_GetDataType(dataObject)==AQHREACT_DATAOBJECTTYPE_DOUBLE) { + double d; - portName=AQHREACT_Port_GetName(port); - dataObject=AQHREACT_Port_GetCurrentDataObject(port); - if (dataObject) { - if (AQHREACT_DataObject_GetDataType(dataObject)==AQHREACT_DATAOBJECTTYPE_DOUBLE) { - double d; - - d=AQHREACT_DataObject_GetDoubleData(dataObject); - if (d>0.0) - result^=1; /*only xor when >0.0, otherwise no change (x XOR 0 is still x) */ - } - else { - DBG_ERROR(NULL, "Data at input slot \"%s\" isn't DOUBLE, ignoring", portName?portName:""); - } + d=AQHREACT_DataObject_GetDoubleData(dataObject); + if (d>0.0) + result^=1; /*only xor when >0.0, otherwise no change (x XOR 0 is still x) */ + numHandledInputs++; } else { - DBG_ERROR(NULL, "Data at input slot \"%s\" has not current data, ignoring", portName?portName:""); + DBG_ERROR(NULL, "Data at input slot \"%s\" isn't DOUBLE, ignoring", portName?portName:""); } - port=AQHREACT_Port_List_Next(port); } + else { + DBG_ERROR(NULL, "Data at input slot \"%s\" has not current data, ignoring", portName?portName:""); + } + port=AQHREACT_Port_List_Next(port); + } - AQHREACT_Unit_ClearChangeFlagsInUnitAndInputPorts(unit); - AQHREACT_Unit_OutputDoubleData(unit, port, result?1.0:0.0); - return 1; + AQHREACT_Unit_ClearChangeFlagsInUnitAndInputPorts(unit); + if (numHandledInputs) + return _outputResult(unit, outputPort, result); + } + } + + return 0; +} + + + +int _cbProcessInvert(AQHREACT_UNIT *unit) +{ + if (unit && AQHREACT_Unit_InputHasChanged(unit)) { + AQHREACT_PORT *outputPort; + + outputPort=AQHREACT_Unit_GetOutputPortByIdForUnit(unit, AQHOMEREACT_UNIT_LOGICAL_OUTSLOT_RESULT); + if (outputPort) { + AQHREACT_PORT *port; + int result=1; + + port=AQHREACT_Port_List_First(AQHREACT_Unit_GetInputPortList(unit)); + if(port) { + const char *portName; + const AQHREACT_DATAOBJECT *dataObject; + + portName=AQHREACT_Port_GetName(port); + dataObject=AQHREACT_Port_GetCurrentDataObject(port); + if (dataObject) { + if (AQHREACT_DataObject_GetDataType(dataObject)==AQHREACT_DATAOBJECTTYPE_DOUBLE) { + double d; + + d=AQHREACT_DataObject_GetDoubleData(dataObject); + if (d>0.0) + result=0; /* invert */ + AQHREACT_Unit_ClearChangeFlagsInUnitAndInputPorts(unit); + return _outputResult(unit, outputPort, result); + } + else { + DBG_ERROR(NULL, "Data at input slot \"%s\" isn't DOUBLE, ignoring", portName?portName:""); + } + } + else { + DBG_ERROR(NULL, "Data at input slot \"%s\" has not current data, ignoring", portName?portName:""); + } } } } diff --git a/apps/aqhome-react/units/u_logical.h b/apps/aqhome-react/units/u_logical.h index 7f44ef8..a0ed0b4 100644 --- a/apps/aqhome-react/units/u_logical.h +++ b/apps/aqhome-react/units/u_logical.h @@ -15,8 +15,13 @@ AQHREACT_UNIT *AqHomeReact_UnitOr_new(AQHOME_REACT *aqh); +AQHREACT_UNIT *AqHomeReact_UnitNor_new(AQHOME_REACT *aqh); + AQHREACT_UNIT *AqHomeReact_UnitAnd_new(AQHOME_REACT *aqh); +AQHREACT_UNIT *AqHomeReact_UnitNand_new(AQHOME_REACT *aqh); + AQHREACT_UNIT *AqHomeReact_UnitXor_new(AQHOME_REACT *aqh); +AQHREACT_UNIT *AqHomeReact_UnitInvert_new(AQHOME_REACT *aqh); diff --git a/apps/aqhome-react/units/u_module.c b/apps/aqhome-react/units/u_module.c index afc3752..7e94d68 100644 --- a/apps/aqhome-react/units/u_module.c +++ b/apps/aqhome-react/units/u_module.c @@ -16,7 +16,6 @@ #include #include -//TODO: read modules /* ------------------------------------------------------------------------------------------------ @@ -486,16 +485,25 @@ AQHREACT_UNIT *_readOneUnitFromXml(AQHOME_REACT *aqh, GWEN_XMLNODE *xmlNode) { const char *id; const char *t; + const char *s; id=GWEN_XMLNode_GetProperty(xmlNode, "id", NULL); t=GWEN_XMLNode_GetProperty(xmlNode, "type", NULL); + s=GWEN_XMLNode_GetProperty(xmlNode, "invert", NULL); if (id && t) { AQHREACT_UNIT *subUnit; int rv; subUnit=AqHomeReact_CreateUnitByName(aqh, t); + if (subUnit==NULL) { + DBG_INFO(NULL, "Could not create unit of type \"%s\"", t); + return NULL; + } AQHREACT_Unit_SetId(subUnit, id); + if (s && strcasecmp(s, "TRUE")==0) + AQHREACT_Unit_AddFlags(subUnit, AQHREACT_UNIT_FLAGS_INVERT); + rv=_readParamsFromXml(subUnit, xmlNode, "params"); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); diff --git a/apps/aqhome-react/units/u_suntime.c b/apps/aqhome-react/units/u_suntime.c new file mode 100644 index 0000000..c39a44d --- /dev/null +++ b/apps/aqhome-react/units/u_suntime.c @@ -0,0 +1,302 @@ +/**************************************************************************** + * 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_suntime_p.h" +#include "aqhome-react/suntimes.h" + +#include +#include +#include + + + +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ + +#define AQHOMEREACT_UNIT_SUNTIME_OUTSLOT_OUTPUT 0 +#define AQHOMEREACT_UNIT_SUNTIME_INSLOT_TIMER 0 + +GWEN_INHERIT(AQHREACT_UNIT, AQHREACT_UNIT_SUNTIME); + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static void GWENHYWFAR_CB _freeData(void *bp, void *p); +static int _cbProcess(AQHREACT_UNIT *unit); +static int _checkState(AQHREACT_UNIT *unit); +static int _isInsideSuntime(AQHREACT_UNIT *unit); +static void _updateSuntimes(AQHREACT_UNIT *unit); +static int _hasDateChanged(AQHREACT_UNIT *unit); +static int _gwenTimeToMinutes(const GWEN_TIME *t); +static int _outputResult(AQHREACT_UNIT *unit, int result); +static void _readParams(AQHREACT_UNIT *unit); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +AQHREACT_UNIT *AqHomeReact_UnitSuntime_new(AQHOME_REACT *aqh) +{ + AQHREACT_UNIT *unit; + AQHREACT_UNIT_SUNTIME *xunit; + AQHREACT_PORT *port; + AQHREACT_PARAM *param; + + unit=AQHREACT_Unit_new(aqh); + AQHREACT_Unit_SetTypeName(unit, "suntime"); + AQHREACT_Unit_SetDescription(unit, "Make sunset and sunrise times available"); + AQHREACT_Unit_SetGpInt(unit, -1); + + GWEN_NEW_OBJECT(AQHREACT_UNIT_SUNTIME, xunit); + GWEN_INHERIT_SETDATA(AQHREACT_UNIT, AQHREACT_UNIT_SUNTIME, unit, xunit, _freeData); + + AQHREACT_Unit_SetProcessFn(unit, _cbProcess); + + port=AQHREACT_Port_new(); + AQHREACT_Port_SetName(port, "output"); + AQHREACT_Port_SetIdForUnit(port, AQHOMEREACT_UNIT_SUNTIME_OUTSLOT_OUTPUT); + AQHREACT_Port_SetDataType(port, AQHREACT_DATAOBJECTTYPE_DOUBLE); + AQHREACT_Unit_AddOutputPort(unit, port); + + port=AQHREACT_Port_new(); + AQHREACT_Port_SetName(port, "timer"); + AQHREACT_Port_SetIdForUnit(port, AQHOMEREACT_UNIT_SUNTIME_INSLOT_TIMER); + AQHREACT_Port_SetDataType(port, AQHREACT_DATAOBJECTTYPE_DOUBLE); + AQHREACT_Unit_AddInputPort(unit, port); + + param=AQHREACT_Param_new(); + AQHREACT_Param_SetName(param, AQHOMEREACT_UNIT_SUNTIME_PARAM_LAT); + AQHREACT_Param_SetDataType(param, AQHREACT_DATAOBJECTTYPE_DOUBLE); + AQHREACT_Unit_AddParam(unit, param); + + param=AQHREACT_Param_new(); + AQHREACT_Param_SetName(param, AQHOMEREACT_UNIT_SUNTIME_PARAM_LONG); + AQHREACT_Param_SetDataType(param, AQHREACT_DATAOBJECTTYPE_DOUBLE); + AQHREACT_Unit_AddParam(unit, param); + + param=AQHREACT_Param_new(); + AQHREACT_Param_SetName(param, AQHOMEREACT_UNIT_SUNTIME_PARAM_OFFS_RISE); + AQHREACT_Param_SetDataType(param, AQHREACT_DATAOBJECTTYPE_DOUBLE); + AQHREACT_Unit_AddParam(unit, param); + + param=AQHREACT_Param_new(); + AQHREACT_Param_SetName(param, AQHOMEREACT_UNIT_SUNTIME_PARAM_OFFS_SET); + AQHREACT_Param_SetDataType(param, AQHREACT_DATAOBJECTTYPE_DOUBLE); + AQHREACT_Unit_AddParam(unit, param); + + return unit; + +} + + + +void _freeData(GWEN_UNUSED void *bp, void *p) +{ + AQHREACT_UNIT_SUNTIME *xunit; + + xunit=(AQHREACT_UNIT_SUNTIME*) p; + + GWEN_Date_free(xunit->date); + GWEN_FREE_OBJECT(xunit); +} + + + +int _cbProcess(AQHREACT_UNIT *unit) +{ + AQHREACT_UNIT_SUNTIME *xunit; + + xunit=GWEN_INHERIT_GETDATA(AQHREACT_UNIT, AQHREACT_UNIT_SUNTIME, unit); + if (xunit) { + if (unit && AQHREACT_Unit_InputHasChanged(unit)) { + int rv; + + rv=_checkState(unit); + AQHREACT_Unit_ClearChangeFlagsInUnitAndInputPorts(unit); + return rv; + } + return 0; + } + + return 0; +} + + + +int _checkState(AQHREACT_UNIT *unit) +{ + AQHREACT_UNIT_SUNTIME *xunit; + + xunit=GWEN_INHERIT_GETDATA(AQHREACT_UNIT, AQHREACT_UNIT_SUNTIME, unit); + if (xunit) { + time_t now; + + now=time(NULL); + if (60.0lastCheckTime)) { + if (_hasDateChanged(unit)) + _updateSuntimes(unit); + xunit->lastCheckTime=now; + return _outputResult(unit, _isInsideSuntime(unit)); + } + } + return 0; +} + + + +int _isInsideSuntime(AQHREACT_UNIT *unit) +{ + AQHREACT_UNIT_SUNTIME *xunit; + + xunit=GWEN_INHERIT_GETDATA(AQHREACT_UNIT, AQHREACT_UNIT_SUNTIME, unit); + if (xunit) { + GWEN_TIME *now; + int startTimeInMinutes; + int endTimeInMinutes; + int nowInMinutes; + int result; + + now=GWEN_CurrentTime(); + nowInMinutes=_gwenTimeToMinutes(now); + GWEN_Time_free(now); + + startTimeInMinutes=xunit->sunRiseTimeInMinutes+xunit->offsetMinsForSunrise; + endTimeInMinutes=xunit->sunSetTimeInMinutes+xunit->offsetMinsForSunset; + + result=(nowInMinutes>=startTimeInMinutes && nowInMinutes<=endTimeInMinutes)?1:0; + DBG_INFO(NULL, "Is inside suntime: %d", result); + return result; + } + return 0; +} + + + +void _updateSuntimes(AQHREACT_UNIT *unit) +{ + AQHREACT_UNIT_SUNTIME *xunit; + + xunit=GWEN_INHERIT_GETDATA(AQHREACT_UNIT, AQHREACT_UNIT_SUNTIME, unit); + if (xunit) { + GWEN_TIME *t; + + GWEN_Date_free(xunit->date); + xunit->date=GWEN_Date_CurrentDate(); + + _readParams(unit); + + t=AQHomeReact_GetSunriseTimeForDateAndLoc(xunit->date, xunit->latitude, xunit->longitude); + xunit->sunRiseTimeInMinutes=_gwenTimeToMinutes(t); + DBG_INFO(NULL, "Sunrise at %02d:%02d", xunit->sunRiseTimeInMinutes/60, xunit->sunRiseTimeInMinutes%60); + GWEN_Time_free(t); + + t=AQHomeReact_GetSunsetTimeForDateAndLoc(xunit->date, xunit->latitude, xunit->longitude); + xunit->sunSetTimeInMinutes=_gwenTimeToMinutes(t); + DBG_INFO(NULL, "Sunset at %02d:%02d", xunit->sunSetTimeInMinutes/60, xunit->sunSetTimeInMinutes%60); + GWEN_Time_free(t); + + } +} + + + +int _gwenTimeToMinutes(const GWEN_TIME *t) +{ + if (t) { + int hours; + int mins; + int secs; + + GWEN_Time_GetBrokenDownUtcTime(t, &hours, &mins, &secs); + return (hours*60)+mins; + } + return 0; +} + + +int _hasDateChanged(AQHREACT_UNIT *unit) +{ + AQHREACT_UNIT_SUNTIME *xunit; + + xunit=GWEN_INHERIT_GETDATA(AQHREACT_UNIT, AQHREACT_UNIT_SUNTIME, unit); + if (xunit) { + if (xunit->date==NULL) + return 1; + else { + GWEN_DATE *today; + + today=GWEN_Date_CurrentDate(); + if (GWEN_Date_Compare(today, xunit->date)!=0) + return 1; + } + } + + return 0; +} + + +void _readParams(AQHREACT_UNIT *unit) +{ + AQHREACT_UNIT_SUNTIME *xunit; + + xunit=GWEN_INHERIT_GETDATA(AQHREACT_UNIT, AQHREACT_UNIT_SUNTIME, unit); + if (xunit) { + /* default to "Neues Rathaus" Celle ;-) */ + xunit->latitude=AQHREACT_Unit_GetParamValueDouble(unit, AQHOMEREACT_UNIT_SUNTIME_PARAM_LAT, 52.619425); + xunit->longitude=AQHREACT_Unit_GetParamValueDouble(unit, AQHOMEREACT_UNIT_SUNTIME_PARAM_LONG, 10.087891); + xunit->offsetMinsForSunrise=AQHREACT_Unit_GetParamValueDouble(unit, AQHOMEREACT_UNIT_SUNTIME_PARAM_OFFS_RISE, 0.0); + xunit->offsetMinsForSunset=AQHREACT_Unit_GetParamValueDouble(unit, AQHOMEREACT_UNIT_SUNTIME_PARAM_OFFS_SET, 0.0); + } +} + + + +int _outputResult(AQHREACT_UNIT *unit, int result) +{ + AQHREACT_PORT *port; + + port=AQHREACT_Unit_GetOutputPortByIdForUnit(unit, AQHOMEREACT_UNIT_SUNTIME_OUTSLOT_OUTPUT); + if (port) { + int currentState; + + currentState=AQHREACT_Unit_GetGpInt(unit); + if (AQHREACT_Unit_GetFlags(unit) & AQHREACT_UNIT_FLAGS_INVERT) + result^=1; + if (currentState!=result) { + DBG_INFO(NULL, "%s: Changing result to %d", AQHREACT_Unit_GetId(unit), result); + AQHREACT_Unit_OutputDoubleData(unit, port, result?1.0:0.0); + AQHREACT_Unit_SetGpInt(unit, result); + return 1; /* we changed something */ + } + } + return 0; /* nothing changed */ +} + + + + + + + + + + + + diff --git a/apps/aqhome-react/units/u_suntime.h b/apps/aqhome-react/units/u_suntime.h new file mode 100644 index 0000000..f5f1049 --- /dev/null +++ b/apps/aqhome-react/units/u_suntime.h @@ -0,0 +1,33 @@ +/**************************************************************************** + * 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. + ****************************************************************************/ + +#ifndef AQHOMEREACT_U_SUNTIME_H +#define AQHOMEREACT_U_SUNTIME_H + + +#include "aqhome-react/aqhome_react.h" +#include "aqhome-react/types/unit.h" + + +#define AQHOMEREACT_UNIT_SUNTIME_PARAM_LAT "latitude" +#define AQHOMEREACT_UNIT_SUNTIME_PARAM_LONG "longitude" + +#define AQHOMEREACT_UNIT_SUNTIME_PARAM_OFFS_RISE "offsetMinsForSunrise" +#define AQHOMEREACT_UNIT_SUNTIME_PARAM_OFFS_SET "offsetMinsForSunset" + + + +AQHREACT_UNIT *AqHomeReact_UnitSuntime_new(AQHOME_REACT *aqh); + + +#endif + + + + + diff --git a/apps/aqhome-react/units/u_suntime_p.h b/apps/aqhome-react/units/u_suntime_p.h new file mode 100644 index 0000000..6fd98da --- /dev/null +++ b/apps/aqhome-react/units/u_suntime_p.h @@ -0,0 +1,39 @@ +/**************************************************************************** + * 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. + ****************************************************************************/ + +#ifndef AQHOMEREACT_U_SUNTIME_P_H +#define AQHOMEREACT_U_SUNTIME_P_H + + +#include "./u_suntime.h" + +#include "aqhome-react/aqhome_react.h" +#include "aqhome-react/types/unit.h" + +#include +#include + + +typedef struct AQHREACT_UNIT_SUNTIME AQHREACT_UNIT_SUNTIME; +struct AQHREACT_UNIT_SUNTIME { + GWEN_DATE *date; + int sunRiseTimeInMinutes; + int sunSetTimeInMinutes; + + double latitude; + double longitude; + int offsetMinsForSunrise; + int offsetMinsForSunset; + + time_t lastCheckTime; +}; + + +#endif + + diff --git a/apps/aqhome-react/units/u_valueset.c b/apps/aqhome-react/units/u_valueset.c index 23d9939..1bdbb69 100644 --- a/apps/aqhome-react/units/u_valueset.c +++ b/apps/aqhome-react/units/u_valueset.c @@ -101,6 +101,13 @@ void _cbInputData(AQHREACT_UNIT *unit, AQHREACT_PORT *port, const AQHREACT_DATAO } if (msgOut) { #if DEBUG_DRY_RUN + GWEN_BUFFER *dbuf; + + dbuf=GWEN_Buffer_new(0, 256, 0, 1); + GWEN_Buffer_AppendArgs(dbuf, "Would send data for value \"%s\": ", sValueName); + AQHREACT_DataObject_Dump(dataObject, dbuf, 0); + DBG_ERROR(NULL, "%s\n", GWEN_Buffer_GetStart(dbuf)); + GWEN_Buffer_free(dbuf); DBG_ERROR(NULL, "Would send data for value \"%s\"", sValueName); GWEN_Msg_free(msgOut); #else