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:
@@ -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>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define AQHOMEREACT_UNIT_HIGHPASS_PARAM_NEWVALUE "newValue"
|
||||
|
||||
|
||||
|
||||
AQHREACT_UNIT *AqHomeReact_UnitHighPass_new(AQHOME_REACT *aqh);
|
||||
|
||||
|
||||
|
||||
@@ -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>");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
302
apps/aqhome-react/units/u_suntime.c
Normal file
302
apps/aqhome-react/units/u_suntime.c
Normal 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 */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
33
apps/aqhome-react/units/u_suntime.h
Normal file
33
apps/aqhome-react/units/u_suntime.h
Normal 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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
39
apps/aqhome-react/units/u_suntime_p.h
Normal file
39
apps/aqhome-react/units/u_suntime_p.h
Normal 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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user