From e0476924c16a6b3dce6bf9f4d4508ff8adb72587 Mon Sep 17 00:00:00 2001 From: Martin Preuss Date: Sun, 24 Mar 2024 00:46:13 +0100 Subject: [PATCH] aqhome-react: read network files. --- apps/aqhome-react/0BUILD | 2 + apps/aqhome-react/init.c | 26 ++- apps/aqhome-react/init.h | 2 + apps/aqhome-react/net_read.c | 342 +++++++++++++++++++++++++++++++++ apps/aqhome-react/net_read.h | 21 ++ apps/aqhome-react/types/unit.h | 1 + 6 files changed, 393 insertions(+), 1 deletion(-) create mode 100644 apps/aqhome-react/net_read.c create mode 100644 apps/aqhome-react/net_read.h diff --git a/apps/aqhome-react/0BUILD b/apps/aqhome-react/0BUILD index 415ba5f..0d3de92 100644 --- a/apps/aqhome-react/0BUILD +++ b/apps/aqhome-react/0BUILD @@ -42,6 +42,7 @@ init.h fini.h loop.h + net_read.h @@ -50,6 +51,7 @@ init.c fini.c loop.c + net_read.c main.c diff --git a/apps/aqhome-react/init.c b/apps/aqhome-react/init.c index 8fb40fd..541a7b2 100644 --- a/apps/aqhome-react/init.c +++ b/apps/aqhome-react/init.c @@ -11,6 +11,7 @@ #endif #include "./init.h" +#include "./net_read.h" #include "./aqhome_react_p.h" #include "aqhome-react/units/u_timer.h" #include "aqhome-react/units/u_varchanges.h" @@ -95,7 +96,7 @@ int AqHomeReact_Init(AQHOME_REACT *aqh, int argc, char **argv) } } - _setupBuiltinUnits(aqh); + AqHomeReact_ReloadUnitNets(aqh); rv=_setupBroker(aqh, dbArgs); if (rv<0) { @@ -108,6 +109,29 @@ int AqHomeReact_Init(AQHOME_REACT *aqh, int argc, char **argv) +void AqHomeReact_ReloadUnitNets(AQHOME_REACT *aqh) +{ + AQHREACT_UNIT_NET_LIST *unitNetList; + + AQHREACT_UnitNet_List_Clear(aqh->unitNetList); + AQHREACT_Unit_List_Clear(aqh->unitList); + aqh->timerUnit=NULL; + aqh->varChangeUnit=NULL; + + _setupBuiltinUnits(aqh); + + unitNetList=AQHREACT_ReadUnitNetFiles(aqh); + if (unitNetList) { + AQHREACT_UnitNet_List_free(aqh->unitNetList); + aqh->unitNetList=unitNetList; + } + else { + DBG_INFO(NULL, "No unit nets read"); + } +} + + + int _createPidFile(const char *pidFilename) { FILE *f; diff --git a/apps/aqhome-react/init.h b/apps/aqhome-react/init.h index b520658..800a83b 100644 --- a/apps/aqhome-react/init.h +++ b/apps/aqhome-react/init.h @@ -15,6 +15,8 @@ int AqHomeReact_Init(AQHOME_REACT *aqh, int argc, char **argv); +void AqHomeReact_ReloadUnitNets(AQHOME_REACT *aqh); + #endif diff --git a/apps/aqhome-react/net_read.c b/apps/aqhome-react/net_read.c new file mode 100644 index 0000000..03628fb --- /dev/null +++ b/apps/aqhome-react/net_read.c @@ -0,0 +1,342 @@ +/**************************************************************************** + * 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 "./net_read.h" +#include "aqhome-react/types/unit.h" +#include "aqhome-react/types/unitnet.h" + +#include "aqhome/aqhome.h" + +#include +#include +#include + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +AQHREACT_UNIT_NET_LIST *_readUnitNetFiles(AQHOME_REACT *aqh, const GWEN_STRINGLIST *sl); +static int _readUnitNetFileToList(AQHOME_REACT *aqh, const char *sFilename, AQHREACT_UNIT_NET_LIST *unitNetList); +static AQHREACT_UNIT_NET *_readUnitNetFromXml(AQHOME_REACT *aqh, GWEN_XMLNODE *unitNetNode); +static AQHREACT_UNIT *_readUnit(AQHOME_REACT *aqh, GWEN_XMLNODE *unitNode); +static int _readParam(AQHREACT_UNIT *unit, GWEN_XMLNODE *n); +static int _readLink(AQHOME_REACT *aqh, AQHREACT_UNIT_NET *unitNet, GWEN_XMLNODE *linkNode); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +AQHREACT_UNIT_NET_LIST *AQHREACT_ReadUnitNetFiles(AQHOME_REACT *aqh) +{ + GWEN_STRINGLIST *sl; + + sl=AQH_GetListOfMatchingDataFiles("aqhome/react/networks", "*.xml"); + if (sl) { + AQHREACT_UNIT_NET_LIST *unitNetList; + + unitNetList=_readUnitNetFiles(aqh, sl); + GWEN_StringList_free(sl); + if (unitNetList==NULL) { + DBG_INFO(NULL, "Error reading unit network files"); + return NULL; + } + return unitNetList; + } + else { + DBG_ERROR(NULL, "No unit network files"); + return NULL; + } +} + + + +AQHREACT_UNIT_NET_LIST *_readUnitNetFiles(AQHOME_REACT *aqh, const GWEN_STRINGLIST *sl) +{ + GWEN_STRINGLISTENTRY *se; + AQHREACT_UNIT_NET_LIST *unitNetList; + + unitNetList=AQHREACT_UnitNet_List_new(); + se=GWEN_StringList_FirstEntry(sl); + while(se) { + const char *s; + + s=GWEN_StringListEntry_Data(se); + if (s && *s) { + int rv; + + DBG_INFO(NULL, "Reading unit network file \"%s\"", s); + rv=_readUnitNetFileToList(aqh, s, unitNetList); + if (rv<0 && rv!=GWEN_ERROR_NO_DATA) { + DBG_WARN(NULL, "Error reading unit network file \"%s\" (%d), ignoring", s, rv); + } + } + se=GWEN_StringListEntry_Next(se); + } + + if (AQHREACT_UnitNet_List_GetCount(unitNetList)<1) { + AQHREACT_UnitNet_List_free(unitNetList); + return NULL; + } + + return unitNetList; +} + + + +int _readUnitNetFileToList(AQHOME_REACT *aqh, const char *sFilename, AQHREACT_UNIT_NET_LIST *unitNetList) +{ + GWEN_XMLNODE *rootNode; + GWEN_XMLNODE *netListNode; + GWEN_XMLNODE *netNode; + int rv; + + rootNode=GWEN_XMLNode_new(GWEN_XMLNodeTypeTag, NULL); + rv=GWEN_XML_ReadFile(rootNode, sFilename, GWEN_XML_FLAGS_DEFAULT); + if (rv<0) { + DBG_ERROR(NULL, "Error reading XML file \"%s\": %d", sFilename, rv); + GWEN_XMLNode_free(rootNode); + return rv; + } + + netListNode=GWEN_XMLNode_FindFirstTag(rootNode, "networks", NULL, NULL); + if (netListNode==NULL) + netListNode=rootNode; + + netNode=GWEN_XMLNode_FindFirstTag(netListNode, "network", NULL, NULL); + while(netNode) { + AQHREACT_UNIT_NET *unitNet; + + unitNet=_readUnitNetFromXml(aqh, netNode); + if (unitNet) + AQHREACT_UnitNet_List_Add(unitNet, unitNetList); + else { + DBG_ERROR(NULL, "Error loading network from file \"%s\", ignoring.", sFilename); + } + netNode=GWEN_XMLNode_FindNextTag(netNode, "network", NULL, NULL); + } + + return 0; +} + + + +AQHREACT_UNIT_NET *_readUnitNetFromXml(AQHOME_REACT *aqh, GWEN_XMLNODE *unitNetNode) +{ + AQHREACT_UNIT_NET *unitNet; + const char *s; + GWEN_XMLNODE *nGroup; + + unitNet=AQHREACT_UnitNet_new(); + s=GWEN_XMLNode_GetProperty(unitNetNode, "id", NULL); + AQHREACT_UnitNet_SetName(unitNet, s); + + nGroup=GWEN_XMLNode_FindFirstTag(unitNetNode, "units", NULL, NULL); + if (nGroup) { + GWEN_XMLNODE *n; + + n=GWEN_XMLNode_FindFirstTag(nGroup, "unit", NULL, NULL); + while(n) { + AQHREACT_UNIT *unit; + + unit=_readUnit(aqh, n); + if (unit) + AQHREACT_UnitNet_AddUnit(unitNet, unit); + else { + DBG_ERROR(NULL, "Error reading unit in net \"%s\"", AQHREACT_UnitNet_GetName(unitNet)); + AQHREACT_UnitNet_free(unitNet); + return NULL; + } + n=GWEN_XMLNode_FindNextTag(n, "unit", NULL, NULL); + } + } + + nGroup=GWEN_XMLNode_FindFirstTag(unitNetNode, "links", NULL, NULL); + if (nGroup) { + GWEN_XMLNODE *n; + + n=GWEN_XMLNode_FindFirstTag(nGroup, "link", NULL, NULL); + while(n) { + int rv; + + rv=_readLink(aqh, unitNet, n); + if (rv<0) { + DBG_ERROR(NULL, "Error reading link in net \"%s\" (%d)", AQHREACT_UnitNet_GetName(unitNet), rv); + AQHREACT_UnitNet_free(unitNet); + return NULL; + } + n=GWEN_XMLNode_FindNextTag(n, "link", NULL, NULL); + } + } + + return unitNet; +} + + + + +AQHREACT_UNIT *_readUnit(AQHOME_REACT *aqh, GWEN_XMLNODE *unitNode) +{ + AQHREACT_UNIT *unit; + const char *unitType; + const char *unitId; + GWEN_XMLNODE *nGroup; + + unitType=GWEN_XMLNode_GetProperty(unitNode, "type", NULL); + if (!(unitType && *unitType)) { + DBG_ERROR(NULL, "No type name in unit node"); + return NULL; + } + unit=AqHomeReact_CreateUnitByName(aqh, unitType); + + unitId=GWEN_XMLNode_GetProperty(unitNode, "id", NULL); + AQHREACT_Unit_SetId(unit, unitId); + + nGroup=GWEN_XMLNode_FindFirstTag(unitNode, "params", NULL, NULL); + if (nGroup) { + GWEN_XMLNODE *n; + + n=GWEN_XMLNode_FindFirstTag(nGroup, "param", NULL, NULL); + while(n) { + int rv; + + rv=_readParam(unit, n); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + AQHREACT_Unit_free(unit); + return NULL; + } + n=GWEN_XMLNode_FindNextTag(n, "param", NULL, NULL); + } + } + + return unit; +} + + + +int _readParam(AQHREACT_UNIT *unit, GWEN_XMLNODE *paramNode) +{ + const char *paramName; + + paramName=GWEN_XMLNode_GetProperty(paramNode, "name", NULL); + if (paramName && *paramName) { + AQHREACT_PARAM *param; + + param=AQHREACT_Unit_GetParamByName(unit, paramName); + if (param) { + const char *value; + + value=GWEN_XMLNode_GetCharValue(paramNode, NULL, NULL); + if (value && *value) { + int dataType; + + dataType=AQHREACT_Param_GetDataType(param); + switch(dataType) { + case AQHREACT_DATAOBJECTTYPE_DOUBLE: + { + int rv; + double valueAsDouble; + + rv=GWEN_Text_StringToDouble(value, &valueAsDouble); + if (rv<0) { + DBG_ERROR(NULL, "Not a DOUBLE value for param %s in unit %s [%s]", paramName, AQHREACT_Unit_GetId(unit), value); + return rv; + } + AQHREACT_Param_SetDoubleValue(param, valueAsDouble); + break; + } + case AQHREACT_DATAOBJECTTYPE_STRING: + default: + AQHREACT_Param_SetStringValue(param, value); + break; + } + } + } + else { + DBG_ERROR(NULL, "No param name \"%s\" in unit %s", paramName, AQHREACT_Unit_GetId(unit)); + return GWEN_ERROR_BAD_DATA; + } + } + + return 0; +} + + + +int _readLink(AQHOME_REACT *aqh, AQHREACT_UNIT_NET *unitNet, GWEN_XMLNODE *linkNode) +{ + const char *sourceUnitName; + const char *sourceSlotName; + const char *targetUnitName; + const char *targetSlotName; + AQHREACT_UNIT *sourceUnit; + AQHREACT_UNIT *targetUnit; + AQHREACT_INPUT_SLOT *inputSlot; + AQHREACT_OUTPUT_SLOT *outputSlot; + AQHREACT_LINK *link; + + sourceUnitName=GWEN_XMLNode_GetProperty(linkNode, "sourceUnit", NULL); + sourceSlotName=GWEN_XMLNode_GetProperty(linkNode, "sourceSlot", NULL); + targetUnitName=GWEN_XMLNode_GetProperty(linkNode, "targetUnit", NULL); + targetSlotName=GWEN_XMLNode_GetProperty(linkNode, "targetSlot", NULL); + + if (!(sourceUnitName && *sourceUnitName && sourceSlotName && *sourceSlotName && + targetUnitName && *targetUnitName && targetSlotName && *targetSlotName)) { + DBG_ERROR(NULL, + "Link in net \"%s\" needs properties sourceUnit, sourceSlot, targetUnit and targetSlot", + AQHREACT_UnitNet_GetName(unitNet)); + return GWEN_ERROR_BAD_DATA; + } + + sourceUnit=AQHREACT_UnitNet_GetUnitById(unitNet, sourceUnitName); + if (sourceUnit==NULL) + sourceUnit=AqHomeReact_FindUnitByNetNameAndUnitId(aqh, NULL, sourceUnitName); + if (sourceUnit==NULL) { + DBG_ERROR(NULL, "Source unit \"%s\" not found", sourceUnitName); + return GWEN_ERROR_NOT_FOUND; + } + outputSlot=AQHREACT_Unit_GetOutputSlotByName(sourceUnit, sourceSlotName); + if (outputSlot==NULL) { + DBG_ERROR(NULL, "Output slot \"%s\" not found for source unit \"%s\"", sourceSlotName, sourceUnitName); + return GWEN_ERROR_NOT_FOUND; + } + + targetUnit=AQHREACT_UnitNet_GetUnitById(unitNet, targetUnitName); + if (targetUnit==NULL) + targetUnit=AqHomeReact_FindUnitByNetNameAndUnitId(aqh, NULL, targetUnitName); + if (targetUnit==NULL) { + DBG_ERROR(NULL, "Target unit \"%s\" not found", targetUnitName); + return GWEN_ERROR_NOT_FOUND; + } + inputSlot=AQHREACT_Unit_GetOrCreateUnusedInputSlotByName(targetUnit, targetSlotName); + if (inputSlot==NULL) { + DBG_ERROR(NULL, "Input slot \"%s\" not found for target unit \"%s\"", targetSlotName, targetUnitName); + return GWEN_ERROR_NOT_FOUND; + } + + link=AQHREACT_Link_new(); + AQHREACT_Link_SetTargetUnitId(link, targetUnitName); + AQHREACT_Link_SetTargetUnit(link, targetUnit); + AQHREACT_Link_SetTargetInputSlotIdForUnit(link, AQHREACT_InputSlot_GetIdForUnit(inputSlot)); + AQHREACT_OutputSlot_AddLink(outputSlot, link); + return 0; +} + + + + diff --git a/apps/aqhome-react/net_read.h b/apps/aqhome-react/net_read.h new file mode 100644 index 0000000..de78638 --- /dev/null +++ b/apps/aqhome-react/net_read.h @@ -0,0 +1,21 @@ +/**************************************************************************** + * 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_NET_READ_H +#define AQHOMEREACT_NET_READ_H + + +#include "./aqhome_react.h" + + +AQHREACT_UNIT_NET_LIST *AQHREACT_ReadUnitNetFiles(AQHOME_REACT *aqh); + + +#endif + + diff --git a/apps/aqhome-react/types/unit.h b/apps/aqhome-react/types/unit.h index 0225d69..626fbf7 100644 --- a/apps/aqhome-react/types/unit.h +++ b/apps/aqhome-react/types/unit.h @@ -65,6 +65,7 @@ AQHREACT_INPUT_SLOT_LIST *AQHREACT_Unit_GetInputSlots(const AQHREACT_UNIT *unit) void AQHREACT_Unit_AddInputSlot(AQHREACT_UNIT *unit, AQHREACT_INPUT_SLOT *inSlot); AQHREACT_INPUT_SLOT *AQHREACT_Unit_GetInputSlotByIdForUnit(const AQHREACT_UNIT *unit, int id); AQHREACT_INPUT_SLOT *AQHREACT_Unit_GetInputSlotByName(const AQHREACT_UNIT *unit, const char *s); +AQHREACT_INPUT_SLOT *AQHREACT_Unit_GetOrCreateUnusedInputSlotByName(AQHREACT_UNIT *unit, const char *s); AQHREACT_OUTPUT_SLOT_LIST *AQHREACT_Unit_GetOutputSlots(const AQHREACT_UNIT *unit);