/**************************************************************************** * 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 #include #include #include #include /* ------------------------------------------------------------------------------------------------ * forward declarations * ------------------------------------------------------------------------------------------------ */ static time_t _getNewestFiletimeFromFileList(const AQHOME_REACT *aqh, const GWEN_STRINGLIST *sl); static AQHREACT_UNIT_NET_LIST *_readUnitNetFiles(AQHOME_REACT *aqh, const GWEN_STRINGLIST *sl); static int _readAllNetworksFromFileIntoList(AQHOME_REACT *aqh, const char *sFilename, AQHREACT_UNIT_NET_LIST *unitNetList); static GWEN_XMLNODE *_readNetworkFromDatadirIntoXml(AQHOME_REACT *aqh, const char *networkName); static AQHREACT_UNIT_NET *_readUnitNetFromXml(AQHOME_REACT *aqh, GWEN_XMLNODE *unitNetNode); static AQHREACT_UNIT_NET *_readUnitNetWithTemplate(AQHOME_REACT *aqh, GWEN_XMLNODE *unitNetNode); static AQHREACT_UNIT_NET *_readUnitNetNoTemplate(AQHOME_REACT *aqh, GWEN_XMLNODE *unitNetNode); static GWEN_XMLNODE *_readUnitNetFileToXml(AQHOME_REACT *aqh, const char *sFilename); static void _readNetParamDefsWithList(AQHREACT_PARAM_LIST *paramList, GWEN_XMLNODE *unitNetNode); static int _readUnits(AQHOME_REACT *aqh, AQHREACT_UNIT_NET *unitNet, GWEN_XMLNODE *unitNetNode, GWEN_DB_NODE *dbNetParams); static int _readLinks(AQHOME_REACT *aqh, AQHREACT_UNIT_NET *unitNet, GWEN_XMLNODE *unitNetNode, GWEN_DB_NODE *dbNetParams); static AQHREACT_UNIT *_readUnit(AQHOME_REACT *aqh, GWEN_XMLNODE *unitNode, GWEN_DB_NODE *dbNetParams); static int _readParamValuesForList(const AQHREACT_PARAM_LIST *paramList, GWEN_XMLNODE *parentNode, GWEN_DB_NODE *dbNetParams); static int _readParamValueForList(const AQHREACT_PARAM_LIST *paramList, GWEN_XMLNODE *paramNode, GWEN_DB_NODE *dbNetParams); static int _setParamDataFromString(AQHREACT_PARAM *param, const char *value); static AQHREACT_PARAM *_readNetParamDef(GWEN_XMLNODE *paramNode); static int _readLink(AQHOME_REACT *aqh, AQHREACT_UNIT_NET *unitNet, GWEN_XMLNODE *linkNode); static int _setParamDoubleValueFromString(AQHREACT_PARAM *param, const char *s); static int _treeExpandProperties(const GWEN_XMLNODE *n, GWEN_DB_NODE *dbVars); /* ------------------------------------------------------------------------------------------------ * implementations * ------------------------------------------------------------------------------------------------ */ time_t AQHomeReact_GetNewestUnitNetFiletime(const AQHOME_REACT *aqh) { GWEN_STRINGLIST *sl; time_t t1; time_t t2; sl=AQH_GetListOfMatchingDataFiles("aqhome/react/networks", "*.xml"); t1=_getNewestFiletimeFromFileList(aqh, sl); GWEN_StringList_free(sl); sl=AQH_GetListOfMatchingSysconfFiles("aqhome/react/networks", "*.xml"); t2=_getNewestFiletimeFromFileList(aqh, sl); GWEN_StringList_free(sl); return (t1>t2)?t1:t2; } AQHREACT_UNIT_NET_LIST *AQHomeReact_ReadUnitNetFiles(AQHOME_REACT *aqh) { GWEN_STRINGLIST *sl; sl=AQH_GetListOfMatchingSysconfFiles("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; } } time_t _getNewestFiletimeFromFileList(const AQHOME_REACT *aqh, const GWEN_STRINGLIST *sl) { time_t resultTime=0; if (sl) { GWEN_STRINGLISTENTRY *se; se=GWEN_StringList_FirstEntry(sl); while(se) { const char *s; s=GWEN_StringListEntry_Data(se); if (s && *s) { struct stat sb; if (stat(s, &sb)==0) { time_t t; t=sb.st_mtim.tv_sec; if (t>resultTime) resultTime=t; } else { DBG_WARN(NULL, "Error on stat(%s): %s (%d)", s, strerror(errno), errno); } } se=GWEN_StringListEntry_Next(se); } } return resultTime; } GWEN_XMLNODE *_readNetworkFromDatadirIntoXml(AQHOME_REACT *aqh, const char *networkName) { GWEN_XMLNODE *n; GWEN_BUFFER *bufFilename; GWEN_BUFFER *bufPath; bufFilename=GWEN_Buffer_new(0, 256, 0, 1); GWEN_Buffer_AppendString(bufFilename, "aqhome/react/networks/"); GWEN_Buffer_AppendString(bufFilename, networkName); GWEN_Buffer_AppendString(bufFilename, ".xml"); bufPath=AQH_FindPathOfDataFile(GWEN_Buffer_GetStart(bufFilename)); if (bufPath==NULL) { DBG_ERROR(NULL, "Network file \"%s\" not found in sysconf folders", GWEN_Buffer_GetStart(bufFilename)); GWEN_Buffer_free(bufFilename); return NULL; } GWEN_Buffer_free(bufFilename); n=_readUnitNetFileToXml(aqh, GWEN_Buffer_GetStart(bufPath)); if (n==NULL) { DBG_ERROR(NULL, "Error reading network file \"%s\" from sysconf dir", GWEN_Buffer_GetStart(bufPath)); GWEN_Buffer_free(bufPath); return NULL; } GWEN_Buffer_free(bufPath); return n; } 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=_readAllNetworksFromFileIntoList(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 _readAllNetworksFromFileIntoList(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; s=GWEN_XMLNode_GetProperty(unitNetNode, "type", NULL); if (s && *s) unitNet=_readUnitNetWithTemplate(aqh, unitNetNode); else unitNet=_readUnitNetNoTemplate(aqh, unitNetNode); if (unitNet==NULL) { DBG_INFO(NULL, "Error reading unit"); return NULL; } return unitNet; } AQHREACT_UNIT_NET *_readUnitNetWithTemplate(AQHOME_REACT *aqh, GWEN_XMLNODE *unitNetXml) { AQHREACT_UNIT_NET *unitNet; GWEN_XMLNODE *templateNetXml; const char *s; int rv; GWEN_DB_NODE *dbNetParams=NULL; unitNet=AQHREACT_UnitNet_new(); s=GWEN_XMLNode_GetProperty(unitNetXml, "id", NULL); AQHREACT_UnitNet_SetName(unitNet, s); s=GWEN_XMLNode_GetProperty(unitNetXml, "type", NULL); DBG_INFO(NULL, "Loading base network \"%s\"", s); templateNetXml=_readNetworkFromDatadirIntoXml(aqh, s); if (templateNetXml==NULL) { DBG_ERROR(NULL, "Base network \"%s\" not available (error or missing)", s); AQHREACT_UnitNet_free(unitNet); return NULL; } /* read netParam definitions from template file */ _readNetParamDefsWithList(AQHREACT_UnitNet_GetParamList(unitNet), templateNetXml); /* also read netParams from this file (after reading from template) */ rv=_readParamValuesForList(AQHREACT_UnitNet_GetParamList(unitNet), unitNetXml, NULL); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); AQHREACT_UnitNet_free(unitNet); GWEN_XMLNode_free(templateNetXml); return NULL; } dbNetParams=GWEN_DB_Group_new("params"); rv=AQHREACT_Param_List_WriteIntoDb(AQHREACT_UnitNet_GetParamList(unitNet), dbNetParams, GWEN_DB_FLAGS_OVERWRITE_VARS); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); GWEN_DB_Group_free(dbNetParams); AQHREACT_UnitNet_free(unitNet); GWEN_XMLNode_free(templateNetXml); return NULL; } /* first read units from this file (if any) */ rv=_readUnits(aqh, unitNet, unitNetXml, dbNetParams); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); GWEN_DB_Group_free(dbNetParams); AQHREACT_UnitNet_free(unitNet); GWEN_XMLNode_free(templateNetXml); return NULL; } /* then read units from template file */ rv=_readUnits(aqh, unitNet, templateNetXml, dbNetParams); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); GWEN_DB_Group_free(dbNetParams); AQHREACT_UnitNet_free(unitNet); GWEN_XMLNode_free(templateNetXml); return NULL; } /* read links from template file */ rv=_readLinks(aqh, unitNet, templateNetXml, dbNetParams); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); GWEN_DB_Group_free(dbNetParams); AQHREACT_UnitNet_free(unitNet); GWEN_XMLNode_free(templateNetXml); return NULL; } /* read additional links from this file (if any) */ rv=_readLinks(aqh, unitNet, unitNetXml, dbNetParams); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); GWEN_DB_Group_free(dbNetParams); AQHREACT_UnitNet_free(unitNet); GWEN_XMLNode_free(templateNetXml); return NULL; } GWEN_DB_Group_free(dbNetParams); GWEN_XMLNode_free(templateNetXml); return unitNet; } AQHREACT_UNIT_NET *_readUnitNetNoTemplate(AQHOME_REACT *aqh, GWEN_XMLNODE *unitNetNode) { AQHREACT_UNIT_NET *unitNet; const char *s; int rv; GWEN_DB_NODE *dbNetParams=NULL; unitNet=AQHREACT_UnitNet_new(); s=GWEN_XMLNode_GetProperty(unitNetNode, "id", NULL); AQHREACT_UnitNet_SetName(unitNet, s); /* just directly read network from given XML node (no need to load a template file) */ _readNetParamDefsWithList(AQHREACT_UnitNet_GetParamList(unitNet), unitNetNode); dbNetParams=GWEN_DB_Group_new("params"); rv=AQHREACT_Param_List_WriteIntoDb(AQHREACT_UnitNet_GetParamList(unitNet), dbNetParams, GWEN_DB_FLAGS_OVERWRITE_VARS); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); GWEN_DB_Group_free(dbNetParams); AQHREACT_UnitNet_free(unitNet); return NULL; } rv=_readUnits(aqh, unitNet, unitNetNode, dbNetParams); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); GWEN_DB_Group_free(dbNetParams); AQHREACT_UnitNet_free(unitNet); return NULL; } rv=_readLinks(aqh, unitNet, unitNetNode, dbNetParams); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); GWEN_DB_Group_free(dbNetParams); AQHREACT_UnitNet_free(unitNet); return NULL; } GWEN_DB_Group_free(dbNetParams); return unitNet; } GWEN_XMLNODE *_readUnitNetFileToXml(AQHOME_REACT *aqh, const char *sFilename) { GWEN_XMLNODE *rootNode; 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 NULL; } netNode=GWEN_XMLNode_FindFirstTag(rootNode, "network", NULL, NULL); if (netNode) { GWEN_XMLNode_UnlinkChild(rootNode, netNode); GWEN_XMLNode_free(rootNode); return netNode; } else { DBG_ERROR(NULL, "No \"network\" element in network file \"%s\"", sFilename); return NULL; } } int _readUnits(AQHOME_REACT *aqh, AQHREACT_UNIT_NET *unitNet, GWEN_XMLNODE *unitNetNode, GWEN_DB_NODE *dbNetParams) { GWEN_XMLNODE *nGroup; nGroup=GWEN_XMLNode_FindFirstTag(unitNetNode, "units", NULL, NULL); if (nGroup) { GWEN_XMLNODE *n; if (dbNetParams) { int rv; rv=_treeExpandProperties(nGroup, dbNetParams); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); return rv; } } n=GWEN_XMLNode_FindFirstTag(nGroup, "unit", NULL, NULL); while(n) { AQHREACT_UNIT *unit; unit=_readUnit(aqh, n, dbNetParams); if (unit) AQHREACT_UnitNet_AddUnit(unitNet, unit); else { DBG_ERROR(NULL, "Error reading unit in net \"%s\"", AQHREACT_UnitNet_GetName(unitNet)); return GWEN_ERROR_BAD_DATA; } n=GWEN_XMLNode_FindNextTag(n, "unit", NULL, NULL); } } return 0; } int _readLinks(AQHOME_REACT *aqh, AQHREACT_UNIT_NET *unitNet, GWEN_XMLNODE *unitNetNode, GWEN_DB_NODE *dbNetParams) { GWEN_XMLNODE *nGroup; nGroup=GWEN_XMLNode_FindFirstTag(unitNetNode, "links", NULL, NULL); if (nGroup) { GWEN_XMLNODE *n; if (dbNetParams) { int rv; rv=_treeExpandProperties(nGroup, dbNetParams); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); return rv; } } 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); return GWEN_ERROR_BAD_DATA; } n=GWEN_XMLNode_FindNextTag(n, "link", NULL, NULL); } } return 0; } AQHREACT_UNIT *_readUnit(AQHOME_REACT *aqh, GWEN_XMLNODE *unitNode, GWEN_DB_NODE *dbNetParams) { AQHREACT_UNIT *unit; const char *unitType; const char *unitId; int rv; 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); rv=_readParamValuesForList(AQHREACT_Unit_GetParamList(unit), unitNode, dbNetParams); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); AQHREACT_Unit_free(unit); return NULL; } return unit; } int _readParamValuesForList(const AQHREACT_PARAM_LIST *paramList, GWEN_XMLNODE *parentNode, GWEN_DB_NODE *dbNetParams) { GWEN_XMLNODE *nGroup; nGroup=GWEN_XMLNode_FindFirstTag(parentNode, "params", NULL, NULL); if (nGroup) { GWEN_XMLNODE *n; n=GWEN_XMLNode_FindFirstTag(nGroup, "param", NULL, NULL); while(n) { int rv; rv=_readParamValueForList(paramList, n, dbNetParams); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); return rv; } n=GWEN_XMLNode_FindNextTag(n, "param", NULL, NULL); } } return 0; } int _readParamValueForList(const AQHREACT_PARAM_LIST *paramList, GWEN_XMLNODE *paramNode, GWEN_DB_NODE *dbNetParams) { const char *paramName; paramName=GWEN_XMLNode_GetProperty(paramNode, "name", NULL); if (paramName && *paramName) { AQHREACT_PARAM *param; param=AQHREACT_Param_List_GetParamByName(paramList, paramName); if (param) { const char *value; value=GWEN_XMLNode_GetCharValue(paramNode, NULL, NULL); if (value && *value) { if (dbNetParams) { GWEN_BUFFER *buf; int rv; buf=GWEN_Buffer_new(0, 64, 0, 1); rv=GWEN_DB_ReplaceVars(dbNetParams, value, buf); if (rv<0) { DBG_ERROR(NULL, "Error replacing vars for param \"%s\" (%d)", paramName, rv); GWEN_Buffer_free(buf); return rv; } rv=_setParamDataFromString(param, GWEN_Buffer_GetStart(buf)); if (rv<0) { DBG_ERROR(NULL, "Error setting param data for param \"%s\" (%d)", paramName, rv); GWEN_Buffer_free(buf); return rv; } GWEN_Buffer_free(buf); } else { int rv; rv=_setParamDataFromString(param, value); if (rv<0) { DBG_ERROR(NULL, "Error setting param data for param \"%s\" (%d)", paramName, rv); return rv; } } } /* if value */ } else { DBG_ERROR(NULL, "No param name \"%s\" in list", paramName); return GWEN_ERROR_BAD_DATA; } } return 0; } int _setParamDataFromString(AQHREACT_PARAM *param, const char *value) { 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\" [%s]", AQHREACT_Param_GetName(param), value); return rv; } AQHREACT_Param_SetDoubleValue(param, valueAsDouble); break; } case AQHREACT_DATAOBJECTTYPE_STRING: default: AQHREACT_Param_SetStringValue(param, value); break; } } return 0; } void _readNetParamDefsWithList(AQHREACT_PARAM_LIST *paramList, GWEN_XMLNODE *unitNetNode) { GWEN_XMLNODE *nGroup; nGroup=GWEN_XMLNode_FindFirstTag(unitNetNode, "params", NULL, NULL); if (nGroup) { GWEN_XMLNODE *n; n=GWEN_XMLNode_FindFirstTag(nGroup, "param", NULL, NULL); while(n) { AQHREACT_PARAM *param; param=_readNetParamDef(n); if (param) AQHREACT_Param_List_Add(param, paramList); n=GWEN_XMLNode_FindNextTag(n, "param", NULL, NULL); } } } AQHREACT_PARAM *_readNetParamDef(GWEN_XMLNODE *paramNode) { const char *paramName; const char *paramType; const char *paramValue; paramName=GWEN_XMLNode_GetProperty(paramNode, "name", NULL); paramType=GWEN_XMLNode_GetProperty(paramNode, "type", "string"); paramValue=GWEN_XMLNode_GetCharValue(paramNode, NULL, NULL); if (paramName && *paramName) { int t=AQHREACT_DATAOBJECTTYPE_STRING; AQHREACT_PARAM *param; if (paramType) { if (strcasecmp(paramType, "string")==0) t=AQHREACT_DATAOBJECTTYPE_STRING; else if (strcasecmp(paramType, "double")==0) t=AQHREACT_DATAOBJECTTYPE_DOUBLE; else { DBG_ERROR(NULL, "Invalid data type in parameter (%s), assuming string", paramType); t=AQHREACT_DATAOBJECTTYPE_STRING; } } param=AQHREACT_Param_new(); AQHREACT_Param_SetName(param, paramName); AQHREACT_Param_SetDataType(param, t); if (paramValue && *paramValue) { int rv; switch(t) { case AQHREACT_DATAOBJECTTYPE_STRING: AQHREACT_Param_SetStringValue(param, paramValue); break; case AQHREACT_DATAOBJECTTYPE_DOUBLE: rv=_setParamDoubleValueFromString(param, paramValue); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); AQHREACT_Param_free(param); return NULL; } break; default: break; } } return param; } return NULL; } int _setParamDoubleValueFromString(AQHREACT_PARAM *param, const char *s) { int rv; double value; rv=GWEN_Text_StringToDouble(s, &value); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); return rv; } AQHREACT_Param_SetDoubleValue(param, value); return 0; } int _readLink(AQHOME_REACT *aqh, AQHREACT_UNIT_NET *unitNet, GWEN_XMLNODE *linkNode) { const char *sourceNetName; const char *sourceUnitName; const char *sourceSlotName; const char *targetNetName; const char *targetUnitName; const char *targetSlotName; AQHREACT_UNIT *sourceUnit; AQHREACT_UNIT *targetUnit; AQHREACT_INPUT_SLOT *inputSlot; AQHREACT_OUTPUT_SLOT *outputSlot; AQHREACT_LINK *link; sourceNetName=GWEN_XMLNode_GetProperty(linkNode, "sourceNet", NULL); sourceUnitName=GWEN_XMLNode_GetProperty(linkNode, "sourceUnit", NULL); sourceSlotName=GWEN_XMLNode_GetProperty(linkNode, "sourceSlot", NULL); targetNetName=GWEN_XMLNode_GetProperty(linkNode, "targetNet", 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 && sourceNetName && *sourceNetName) sourceUnit=AqHomeReact_FindUnitByNetNameAndUnitId(aqh, sourceNetName, 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 && targetNetName && *targetNetName) targetUnit=AqHomeReact_FindUnitByNetNameAndUnitId(aqh, targetNetName, 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; } int _treeExpandProperties(const GWEN_XMLNODE *n, GWEN_DB_NODE *dbVars) { int rv; const GWEN_XMLNODE *nn; rv=GWEN_XMLNode_ExpandProperties(n, dbVars); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); return rv; } nn=GWEN_XMLNode_GetFirstTag(n); while(nn) { rv=_treeExpandProperties(nn, dbVars); if (rv<0) { DBG_INFO(NULL, "here (%d)", rv); return rv; } nn=GWEN_XMLNode_GetNextTag(nn); } return 0; }