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)
This commit is contained in:
Martin Preuss
2024-04-20 17:28:20 +02:00
parent f3c68a8bba
commit 87114cecea
17 changed files with 641 additions and 104 deletions

View File

@@ -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 <gwenhywfar/misc.h>
@@ -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;

View File

@@ -8,6 +8,15 @@
</params>
</unit>
<unit id="suntime1" type="suntime" invert="TRUE">
<params>
<param name="latitude">52.619425</param>
<param name="longitude">10.087891</param>
<param name="offsetMinsForSunrise">0.0</param>
<param name="offsetMinsForSunset">30.0</param>
</params>
</unit>
<unit id="delayedOff1" type="delayedOff">
<params>
<param name="delayTime">30</param>
@@ -15,6 +24,17 @@
</params>
</unit>
<unit id="and1" type="and" />
<unit id="zeroPosNegString1" type="zeroPosNegString">
<params>
<param name="valueIfNegative">OFF</param>
<param name="valueIfZero">OFF</param>
<param name="valueIfPositive">ON</param>
</params>
</unit>
<unit id="valueSetOutPlug" type="valueSet">
<params>
<param name="valueName">mqtt/109C2F/power</param>
@@ -26,6 +46,10 @@
<!-- (sourceNet), sourceUnit, sourcePort, (targetNet), targetUnit, targetPort -->
<link sourceUnit=".updatedValue" sourcePort="output" targetUnit="valueFilterInPlug" targetPort="input" />
<link sourceUnit="valueFilterInPlug" sourcePort="output" targetUnit="delayedOff1" targetPort="input" />
<link sourceUnit="delayedOff1" sourcePort="output" targetUnit="valueSetOutPlug" targetPort="input" />
<link sourceUnit=".timer" sourcePort="output" targetUnit="suntime1" targetPort="timer" />
<link sourceUnit="suntime1" sourcePort="output" targetUnit="and1" targetPort="input" />
<link sourceUnit="delayedOff1" sourcePort="output" targetUnit="and1" targetPort="input" />
<link sourceUnit="and1" sourcePort="output" targetUnit="zeroPosNegString1" targetPort="input" />
<link sourceUnit="zeroPosNegString1" sourcePort="output" targetUnit="valueSetOutPlug" targetPort="input" />
</links>
</network>

View File

@@ -20,15 +20,6 @@
<units>
<unit id="highPass1" type="highPass" />
<unit id="zeroPosNegString1" type="zeroPosNegString">
<params>
<param name="valueIfNegative">OFF</param>
<param name="valueIfZero">OFF</param>
<param name="valueIfPositive">ON</param>
</params>
</unit>
<unit id="stabilize1" type="stabilize">
<params>
<param name="holdTimeHigh">0.0</param>
@@ -44,9 +35,7 @@
<link sourceUnit="." sourcePort="input" targetUnit="highPass1" targetPort="input" />
<link sourceUnit="highPass1" sourcePort="output" targetUnit="stabilize1" targetPort="input" />
<link sourceUnit="stabilize1" sourcePort="output" targetUnit="zeroPosNegString1" targetPort="input" />
<link sourceUnit="zeroPosNegString1" sourcePort="output" targetUnit="." targetPort="output" />
<link sourceUnit="stabilize1" sourcePort="output" targetUnit="." targetPort="output" />
</links>
</network>

View File

@@ -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:"<no id>",
unitName?unitName:"<no name>",
portName?portName:"<no port>");
portName?portName:"<no port>",
(lnk->targetPort)?AQHREACT_Port_GetIdForUnit(lnk->targetPort):0);
}
}

View File

@@ -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):"<no name>",
port->idForUnit,
AQHREACT_DataObjectType_toString(port->dataType),
port->flags);
if (port->linkList && AQHREACT_Link_List_GetCount(port->linkList))

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -24,6 +24,7 @@ struct AQHREACT_UNIT {
uint32_t flags;
uint64_t gpTimestamp;
int gpInt;
int nextInputPortId;

View File

@@ -49,6 +49,8 @@
u_zeroposnegstring.h
u_module.h
u_module_p.h
u_suntime.h
u_suntime_p.h
</headers>
<sources>
@@ -64,6 +66,7 @@
u_valueset.c
u_zeroposnegstring.c
u_module.c
u_suntime.c
</sources>
<useTargets>

View File

@@ -18,6 +18,7 @@
#define AQHOMEREACT_UNIT_HIGHPASS_PARAM_NEWVALUE "newValue"
AQHREACT_UNIT *AqHomeReact_UnitHighPass_new(AQHOME_REACT *aqh);

View File

@@ -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:"<unnamed>");
}
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:"<unnamed>");
DBG_ERROR(NULL, "Data at input slot \"%s\" isn't DOUBLE, ignoring", portName?portName:"<unnamed>");
}
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:"<unnamed>");
}
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:"<unnamed>");
}
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:"<unnamed>");
DBG_ERROR(NULL, "Data at input slot \"%s\" isn't DOUBLE, ignoring", portName?portName:"<unnamed>");
}
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:"<unnamed>", 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:"<unnamed>");
}
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:"<unnamed>");
DBG_ERROR(NULL, "Data at input slot \"%s\" isn't DOUBLE, ignoring", portName?portName:"<unnamed>");
}
port=AQHREACT_Port_List_Next(port);
}
else {
DBG_ERROR(NULL, "Data at input slot \"%s\" has not current data, ignoring", portName?portName:"<unnamed>");
}
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:"<unnamed>");
}
}
else {
DBG_ERROR(NULL, "Data at input slot \"%s\" has not current data, ignoring", portName?portName:"<unnamed>");
}
}
}
}

View File

@@ -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);

View File

@@ -16,7 +16,6 @@
#include <gwenhywfar/xml.h>
#include <gwenhywfar/text.h>
//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);

View File

@@ -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 <config.h>
#endif
#include "./u_suntime_p.h"
#include "aqhome-react/suntimes.h"
#include <gwenhywfar/debug.h>
#include <gwenhywfar/xml.h>
#include <gwenhywfar/text.h>
/* ------------------------------------------------------------------------------------------------
* 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.0<difftime(now, xunit->lastCheckTime)) {
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 */
}

View File

@@ -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

View File

@@ -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 <gwenhywfar/gwentime.h>
#include <gwenhywfar/gwendate.h>
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

View File

@@ -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