From 1d51ef02595b404585a822d4334b5cac1fccdaab Mon Sep 17 00:00:00 2001 From: Martin Preuss Date: Wed, 15 May 2024 22:50:20 +0200 Subject: [PATCH] aqhome-react: added statistics modules (average, min, max) --- apps/aqhome-react/aqhome_react.c | 11 +- apps/aqhome-react/units/0BUILD | 2 + apps/aqhome-react/units/u_statfns.c | 277 ++++++++++++++++++++++++++++ apps/aqhome-react/units/u_statfns.h | 26 +++ 4 files changed, 315 insertions(+), 1 deletion(-) create mode 100644 apps/aqhome-react/units/u_statfns.c create mode 100644 apps/aqhome-react/units/u_statfns.h diff --git a/apps/aqhome-react/aqhome_react.c b/apps/aqhome-react/aqhome_react.c index 3540a4c..091588f 100644 --- a/apps/aqhome-react/aqhome_react.c +++ b/apps/aqhome-react/aqhome_react.c @@ -22,7 +22,8 @@ #include "aqhome-react/units/u_zeroposnegstring.h" #include "aqhome-react/units/u_suntime.h" #include "aqhome-react/units/u_varchanges.h" - +#include "aqhome-react/units/u_timeprogram.h" +#include "aqhome-react/units/u_statfns.h" #include #include @@ -302,6 +303,14 @@ AQHREACT_UNIT *AqHomeReact_CreateUnitByName(AQHOME_REACT *aqh, const char *unitT return AqHomeReact_UnitZeroPosNegString_new(aqh); else if (strcasecmp(unitType, "suntime")==0) return AqHomeReact_UnitSuntime_new(aqh); + else if (strcasecmp(unitType, "timeraction")==0) + return AqHomeReact_UnitTimeProgram_new(aqh); + else if (strcasecmp(unitType, "average")==0) + return AqHomeReact_UnitAverage_new(aqh); + else if (strcasecmp(unitType, "minvalue")==0) + return AqHomeReact_UnitMinValue_new(aqh); + else if (strcasecmp(unitType, "maxvalue")==0) + return AqHomeReact_UnitMaxValue_new(aqh); else { AQHREACT_UNIT *unit; diff --git a/apps/aqhome-react/units/0BUILD b/apps/aqhome-react/units/0BUILD index 0f9d377..3f519c9 100644 --- a/apps/aqhome-react/units/0BUILD +++ b/apps/aqhome-react/units/0BUILD @@ -54,6 +54,7 @@ u_suntime_p.h u_timeprogram.h u_timeprogram_p.h + u_statfns.h @@ -72,6 +73,7 @@ u_module.c u_suntime.c u_timeprogram.c + u_statfns.c diff --git a/apps/aqhome-react/units/u_statfns.c b/apps/aqhome-react/units/u_statfns.c new file mode 100644 index 0000000..3fe5f7f --- /dev/null +++ b/apps/aqhome-react/units/u_statfns.c @@ -0,0 +1,277 @@ +/**************************************************************************** + * 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_statfns.h" + +#include + + + +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ + +#define AQHOMEREACT_UNIT_STATFNS_INSLOT_INPUT 100 +#define AQHOMEREACT_UNIT_STATFNS_INSLOT_AUTOINPUT 101 + +#define AQHOMEREACT_UNIT_STATFNS_OUTSLOT_RESULT 0 + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static AQHREACT_UNIT *_unitStatFns_new(AQHOME_REACT *aqh); +static int _outputResult(AQHREACT_UNIT *unit, AQHREACT_PORT *port, double result); +static int _cbProcessAvg(AQHREACT_UNIT *unit); +static int _cbProcessMin(AQHREACT_UNIT *unit); +static int _cbProcessMax(AQHREACT_UNIT *unit); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + + +AQHREACT_UNIT *AqHomeReact_UnitAverage_new(AQHOME_REACT *aqh) +{ + AQHREACT_UNIT *unit; + + unit=_unitStatFns_new(aqh); + AQHREACT_Unit_SetTypeName(unit, "average"); + AQHREACT_Unit_SetDescription(unit, "Average over inputs"); + AQHREACT_Unit_SetProcessFn(unit, _cbProcessAvg); + return unit; +} + + + +AQHREACT_UNIT *AqHomeReact_UnitMinValue_new(AQHOME_REACT *aqh) +{ + AQHREACT_UNIT *unit; + + unit=_unitStatFns_new(aqh); + AQHREACT_Unit_SetTypeName(unit, "minvalue"); + AQHREACT_Unit_SetDescription(unit, "Smallest value from all inputs"); + AQHREACT_Unit_SetProcessFn(unit, _cbProcessMin); + return unit; +} + + + +AQHREACT_UNIT *AqHomeReact_UnitMaxValue_new(AQHOME_REACT *aqh) +{ + AQHREACT_UNIT *unit; + + unit=_unitStatFns_new(aqh); + AQHREACT_Unit_SetTypeName(unit, "maxvalue"); + AQHREACT_Unit_SetDescription(unit, "Highest value from all inputs"); + AQHREACT_Unit_SetProcessFn(unit, _cbProcessMax); + return unit; +} + + + +AQHREACT_UNIT *_unitStatFns_new(AQHOME_REACT *aqh) +{ + AQHREACT_UNIT *unit; + AQHREACT_PORT *port; + + unit=AQHREACT_Unit_new(aqh); + + AQHREACT_Unit_SetNextInputPortId(unit, AQHOMEREACT_UNIT_STATFNS_INSLOT_AUTOINPUT); /* for auto-gen multi-slots */ + + port=AQHREACT_Port_new(); + AQHREACT_Port_SetName(port, "output"); + AQHREACT_Port_SetIdForUnit(port, AQHOMEREACT_UNIT_STATFNS_OUTSLOT_RESULT); + AQHREACT_Port_SetDataType(port, AQHREACT_DATAOBJECTTYPE_DOUBLE); + AQHREACT_Unit_AddOutputPort(unit, port); + + port=AQHREACT_Port_new(); + AQHREACT_Port_SetName(port, "input"); + AQHREACT_Port_SetIdForUnit(port, AQHOMEREACT_UNIT_STATFNS_INSLOT_INPUT); + AQHREACT_Port_SetDataType(port, AQHREACT_DATAOBJECTTYPE_DOUBLE); + AQHREACT_Port_AddFlags(port, AQHREACT_UNIT_FLAGS_MULTI); + AQHREACT_Unit_AddInputPort(unit, port); + + return unit; +} + + + +int _outputResult(AQHREACT_UNIT *unit, AQHREACT_PORT *port, double result) +{ + DBG_INFO(NULL, "%s: Sending result %f", AQHREACT_Unit_GetId(unit), result); + AQHREACT_Unit_OutputDoubleData(unit, port, result); + return 1; /* we changed something */ +} + + + +int _cbProcessAvg(AQHREACT_UNIT *unit) +{ + if (unit && AQHREACT_Unit_InputHasChanged(unit)) { + AQHREACT_PORT *outputPort; + + outputPort=AQHREACT_Unit_GetOutputPortByIdForUnit(unit, AQHOMEREACT_UNIT_STATFNS_OUTSLOT_RESULT); + if (outputPort) { + const AQHREACT_PORT *port; + double sum=0.0; + int numHandledInputs=0; + + port=AQHREACT_Port_List_First(AQHREACT_Unit_GetInputPortList(unit)); + 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; + + d=AQHREACT_DataObject_GetDoubleData(dataObject); + sum+=d; + numHandledInputs++; + } + 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 no current data, ignoring", portName?portName:""); + } + port=AQHREACT_Port_List_Next(port); + } + + AQHREACT_Unit_ClearChangeFlagsInUnitAndInputPorts(unit); + if (numHandledInputs) { + double result; + + result=sum/numHandledInputs; + return _outputResult(unit, outputPort, result); + } + } + } + + return 0; +} + + + +int _cbProcessMin(AQHREACT_UNIT *unit) +{ + if (unit && AQHREACT_Unit_InputHasChanged(unit)) { + AQHREACT_PORT *outputPort; + + outputPort=AQHREACT_Unit_GetOutputPortByIdForUnit(unit, AQHOMEREACT_UNIT_STATFNS_OUTSLOT_RESULT); + if (outputPort) { + const AQHREACT_PORT *port; + double value=0.0; + int numHandledInputs=0; + + port=AQHREACT_Port_List_First(AQHREACT_Unit_GetInputPortList(unit)); + 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; + + d=AQHREACT_DataObject_GetDoubleData(dataObject); + if (numHandledInputs==0) + value=d; + else + value=(d"); + } + } + else { + DBG_ERROR(NULL, "Data at input slot \"%s\" has no current data, ignoring", portName?portName:""); + } + port=AQHREACT_Port_List_Next(port); + } + + AQHREACT_Unit_ClearChangeFlagsInUnitAndInputPorts(unit); + if (numHandledInputs) { + return _outputResult(unit, outputPort, value); + } + } + } + + return 0; +} + + + +int _cbProcessMax(AQHREACT_UNIT *unit) +{ + if (unit && AQHREACT_Unit_InputHasChanged(unit)) { + AQHREACT_PORT *outputPort; + + outputPort=AQHREACT_Unit_GetOutputPortByIdForUnit(unit, AQHOMEREACT_UNIT_STATFNS_OUTSLOT_RESULT); + if (outputPort) { + const AQHREACT_PORT *port; + double value=0.0; + int numHandledInputs=0; + + port=AQHREACT_Port_List_First(AQHREACT_Unit_GetInputPortList(unit)); + 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; + + d=AQHREACT_DataObject_GetDoubleData(dataObject); + if (numHandledInputs==0) + value=d; + else + value=(d>value)?d:value; + numHandledInputs++; + } + 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 no current data, ignoring", portName?portName:""); + } + port=AQHREACT_Port_List_Next(port); + } + + AQHREACT_Unit_ClearChangeFlagsInUnitAndInputPorts(unit); + if (numHandledInputs) { + return _outputResult(unit, outputPort, value); + } + } + } + + return 0; +} + + + diff --git a/apps/aqhome-react/units/u_statfns.h b/apps/aqhome-react/units/u_statfns.h new file mode 100644 index 0000000..3a7c7a2 --- /dev/null +++ b/apps/aqhome-react/units/u_statfns.h @@ -0,0 +1,26 @@ +/**************************************************************************** + * 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_STATFNS_H +#define AQHOMEREACT_U_STATFNS_H + + +#include "aqhome-react/aqhome_react.h" +#include "aqhome-react/types/unit.h" + + +AQHREACT_UNIT *AqHomeReact_UnitAverage_new(AQHOME_REACT *aqh); +AQHREACT_UNIT *AqHomeReact_UnitMinValue_new(AQHOME_REACT *aqh); +AQHREACT_UNIT *AqHomeReact_UnitMaxValue_new(AQHOME_REACT *aqh); + + + + +#endif + +