From a3efa18a6159281db954032fb23a2432ed8265d8 Mon Sep 17 00:00:00 2001 From: Martin Preuss Date: Sat, 9 Mar 2024 01:23:06 +0100 Subject: [PATCH] More work on aqhome-react. --- apps/aqhome-react/0BUILD | 9 + apps/aqhome-react/aqhome_react.c | 86 +++++++ apps/aqhome-react/aqhome_react.h | 28 +-- apps/aqhome-react/aqhome_react_p.h | 5 +- apps/aqhome-react/fini.c | 61 +++++ apps/aqhome-react/fini.h | 21 ++ apps/aqhome-react/init.c | 312 ++++++++++++++++++++++++ apps/aqhome-react/init.h | 21 ++ apps/aqhome-react/loop.c | 103 ++++++++ apps/aqhome-react/loop.h | 21 ++ apps/aqhome-react/main.c | 240 ++++++++++++++++++ apps/aqhome-react/types/inputslot.c | 5 +- apps/aqhome-react/types/link.c | 10 +- apps/aqhome-react/types/link.h | 4 +- apps/aqhome-react/types/link_p.h | 2 +- apps/aqhome-react/types/outputslot.c | 16 ++ apps/aqhome-react/types/outputslot.h | 3 + apps/aqhome-react/types/outputslot_p.h | 2 + apps/aqhome-react/types/unit.c | 291 +++++++++++++++++++--- apps/aqhome-react/types/unit.h | 24 +- apps/aqhome-react/types/unit_p.h | 2 + apps/aqhome-react/units/0BUILD | 71 ++++++ apps/aqhome-react/units/u_or.c | 136 +++++++++++ apps/aqhome-react/units/u_or.h | 23 ++ apps/aqhome-react/units/u_valuefilter.c | 96 ++++++++ apps/aqhome-react/units/u_valuefilter.h | 26 ++ apps/aqhome-react/units/u_varchanges.c | 81 ++++++ apps/aqhome-react/units/u_varchanges.h | 23 ++ 28 files changed, 1661 insertions(+), 61 deletions(-) create mode 100644 apps/aqhome-react/aqhome_react.c create mode 100644 apps/aqhome-react/fini.c create mode 100644 apps/aqhome-react/fini.h create mode 100644 apps/aqhome-react/init.c create mode 100644 apps/aqhome-react/init.h create mode 100644 apps/aqhome-react/loop.c create mode 100644 apps/aqhome-react/loop.h create mode 100644 apps/aqhome-react/units/0BUILD create mode 100644 apps/aqhome-react/units/u_or.c create mode 100644 apps/aqhome-react/units/u_or.h create mode 100644 apps/aqhome-react/units/u_valuefilter.c create mode 100644 apps/aqhome-react/units/u_valuefilter.h create mode 100644 apps/aqhome-react/units/u_varchanges.c create mode 100644 apps/aqhome-react/units/u_varchanges.h diff --git a/apps/aqhome-react/0BUILD b/apps/aqhome-react/0BUILD index 374c643..415ba5f 100644 --- a/apps/aqhome-react/0BUILD +++ b/apps/aqhome-react/0BUILD @@ -39,16 +39,24 @@ aqhome_react.h aqhome_react_p.h + init.h + fini.h + loop.h $(local/typefiles) + aqhome_react.c + init.c + fini.c + loop.c main.c aqhome aqhreact_types + aqhreact_units @@ -57,6 +65,7 @@ types + units diff --git a/apps/aqhome-react/aqhome_react.c b/apps/aqhome-react/aqhome_react.c new file mode 100644 index 0000000..f615349 --- /dev/null +++ b/apps/aqhome-react/aqhome_react.c @@ -0,0 +1,86 @@ +/**************************************************************************** + * 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 "./aqhome_react_p.h" + +#include +#include + + + +AQHOME_REACT *AqHomeReact_new() +{ + AQHOME_REACT *aqh; + + GWEN_NEW_OBJECT(AQHOME_REACT, aqh); + + return aqh; +} + + + +void AqHomeReact_free(AQHOME_REACT *aqh) +{ + if (aqh) { + GWEN_MsgEndpoint_free(aqh->brokerEndpoint); + GWEN_DB_Group_free(aqh->dbArgs); + free(aqh->pidFile); + + GWEN_FREE_OBJECT(aqh); + + } +} + + + +GWEN_MSG_ENDPOINT *AqHomeReact_GetBrokerEndpoint(const AQHOME_REACT *aqh) +{ + return aqh?(aqh->brokerEndpoint):NULL; +} + + + +GWEN_DB_NODE *AqHomeReact_GetDbArgs(const AQHOME_REACT *aqh) +{ + return aqh?(aqh->dbArgs):NULL; +} + + + +const char *AqHomeReact_GetPidFile(const AQHOME_REACT *aqh) +{ + return aqh?aqh->pidFile:NULL; +} + + + +void AqHomeReact_SetPidFile(AQHOME_REACT *aqh, const char *s) +{ + if (aqh) { + free(aqh->pidFile); + aqh->pidFile=s?strdup(s):NULL; + } +} + + + +int AqHomeReact_GetTimeout(const AQHOME_REACT *aqh) +{ + return aqh?aqh->timeout:0; +} + + + + + + + diff --git a/apps/aqhome-react/aqhome_react.h b/apps/aqhome-react/aqhome_react.h index fe4e6bb..8e6863a 100644 --- a/apps/aqhome-react/aqhome_react.h +++ b/apps/aqhome-react/aqhome_react.h @@ -1,6 +1,6 @@ /**************************************************************************** * This file is part of the project AqHome. - * AqHome (c) by 2023 Martin Preuss, all rights reserved. + * 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. @@ -13,24 +13,22 @@ #include -#define AQHOME_REACT_DEFAULT_PIDFILE "/var/run/aqhome-react.pid" -#define AQHOME_REACT_DEFAULT_DATADIR "/var/lib/aqhome-react" - -#define AQHOME_REACT_DEFAULT_BROKER_PORT 1899 -#define AQHOME_REACT_DEFAULT_BROKER_CLIENTID "react" +typedef struct AQHOME_REACT AQHOME_REACT; +AQHOME_REACT *AqHomeReact_new(); +void AqHomeReact_free(AQHOME_REACT *aqh); + +GWEN_MSG_ENDPOINT *AqHomeReact_GetBrokerEndpoint(const AQHOME_REACT *aqh); + +GWEN_DB_NODE *AqHomeReact_GetDbArgs(const AQHOME_REACT *aqh); + +const char *AqHomeReact_GetPidFile(const AQHOME_REACT *aqh); +void AqHomeReact_SetPidFile(AQHOME_REACT *aqh, const char *s); + +int AqHomeReact_GetTimeout(const AQHOME_REACT *aqh); -struct AQHOME_REACT { - GWEN_MSG_ENDPOINT *rootEndpoint; - GWEN_MSG_ENDPOINT *brokerEndpoint; /* do not free (is part of tree pointed to by rootEndpoint) */ - - GWEN_DB_NODE *dbArgs; - char *pidFile; - int timeout; /* timeout for run e.g. inside valgrind */ - -}; #endif diff --git a/apps/aqhome-react/aqhome_react_p.h b/apps/aqhome-react/aqhome_react_p.h index 3b3d38c..7db0113 100644 --- a/apps/aqhome-react/aqhome_react_p.h +++ b/apps/aqhome-react/aqhome_react_p.h @@ -23,15 +23,12 @@ struct AQHOME_REACT { - GWEN_MSG_ENDPOINT *rootEndpoint; - GWEN_MSG_ENDPOINT *brokerEndpoint; /* do not free (is part of tree pointed to by rootEndpoint) */ + GWEN_MSG_ENDPOINT *brokerEndpoint; GWEN_DB_NODE *dbArgs; char *pidFile; int timeout; /* timeout for run e.g. inside valgrind */ - AQH_STORAGE *storage; - }; diff --git a/apps/aqhome-react/fini.c b/apps/aqhome-react/fini.c new file mode 100644 index 0000000..79e9378 --- /dev/null +++ b/apps/aqhome-react/fini.c @@ -0,0 +1,61 @@ +/**************************************************************************** + * 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 "./fini.h" +#include "aqhome-react/aqhome_react_p.h" + +#include +#include +#include +#include + +#include + + + +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +int AqHomeReact_Fini(AQHOME_REACT *aqh) +{ + + GWEN_MsgEndpoint_Disconnect(aqh->brokerEndpoint); + GWEN_MsgEndpoint_free(aqh->brokerEndpoint); + aqh->brokerEndpoint=NULL; + + if (aqh->pidFile) + remove(aqh->pidFile); + + return 0; +} + + + + + + diff --git a/apps/aqhome-react/fini.h b/apps/aqhome-react/fini.h new file mode 100644 index 0000000..8531d37 --- /dev/null +++ b/apps/aqhome-react/fini.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_FINI_H +#define AQHOMEREACT_FINI_H + + +#include "./aqhome_react.h" + + +int AqHomeReact_Fini(AQHOME_REACT *aqh); + + +#endif + + diff --git a/apps/aqhome-react/init.c b/apps/aqhome-react/init.c new file mode 100644 index 0000000..e6add70 --- /dev/null +++ b/apps/aqhome-react/init.c @@ -0,0 +1,312 @@ +/**************************************************************************** + * 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 "./init.h" +#include "./aqhome_react_p.h" + +#include "aqhome/aqhome.h" +#include "aqhome/ipc/endpoint_ipc.h" +#include "aqhome/ipc/endpoint_ipcclient.h" +#include "aqhome/mqtt/endpoint_mqttc.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + + +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ + +#define I18N(msg) msg +#define I18S(msg) msg + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static int _createPidFile(const char *pidFilename); +static int _setupBroker(AQHOME_REACT *aqh, GWEN_DB_NODE *dbArgs); +static int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +int AqHomeReact_Init(AQHOME_REACT *aqh, int argc, char **argv) +{ + int rv; + GWEN_DB_NODE *dbArgs; + const char *s; + + dbArgs=GWEN_DB_Group_new("args"); + rv=_readArgs(argc, argv, dbArgs); + if (rv<0) { + DBG_ERROR(NULL, "Error reading args (%d)", rv); + return rv; + } + AQH_MergeConfigFileIntoConfig(dbArgs, "ConfigFile"); + aqh->dbArgs=dbArgs; + + aqh->timeout=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 0); + + s=GWEN_DB_GetCharValue(dbArgs, "pidfile", 0, AQHOME_REACT_DEFAULT_PIDFILE); + if (s && *s) { + AqHomeReact_SetPidFile(aqh, s); + rv=_createPidFile(s); + if (rv<0) { + DBG_ERROR(NULL, "Error creating PID file (%d)", rv); + return rv; + } + } + + /* TODO: setup react units */ + + rv=_setupBroker(aqh, dbArgs); + if (rv<0) { + DBG_ERROR(NULL, "Error setting up connection to broker (%d)", rv); + return rv; + } + + return 0; +} + + + +int _createPidFile(const char *pidFilename) +{ + FILE *f; + int pidfd; + + if (remove(pidFilename)==0) { + DBG_ERROR(0, "Old PID file existed, removed. (Unclean shutdown?)"); + } + +#ifdef HAVE_SYS_STAT_H + pidfd = open(pidFilename, O_EXCL|O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + if (pidfd < 0) { + DBG_ERROR(NULL, "Could not create PID file \"%s\" (%s), aborting.", pidFilename, strerror(errno)); + return GWEN_ERROR_IO; + } + + f = fdopen(pidfd, "w"); +#else /* HAVE_STAT_H */ + f=fopen(pidFilename,"w+"); +#endif /* HAVE_STAT_H */ + + /* write pid */ +#ifdef HAVE_GETPID + fprintf(f,"%d\n",getpid()); +#else + fprintf(f,"-1\n"); +#endif + if (fclose(f)) { + DBG_ERROR(0, "Could not close PID file \"%s\" (%s), aborting.", pidFilename, strerror(errno)); + return GWEN_ERROR_IO; + } + return 0; +} + + + +int _setupBroker(AQHOME_REACT *aqh, GWEN_DB_NODE *dbArgs) +{ + const char *brokerAddress; + int brokerPort; + const char *brokerClientId; + + brokerAddress=GWEN_DB_GetCharValue(dbArgs, "brokerAddress", 0, NULL); + if (!(brokerAddress && *brokerAddress)) + brokerAddress=GWEN_DB_GetCharValue(dbArgs, "ConfigFile/brokerAddress", 0, "127.0.0.1"); + + brokerPort=GWEN_DB_GetIntValue(dbArgs, "brokerPort", 0, -1); + if (brokerPort<0) + brokerPort=GWEN_DB_GetIntValue(dbArgs, "ConfigFile/brokerPort", 0, AQHOME_REACT_DEFAULT_BROKER_PORT); + + brokerClientId=GWEN_DB_GetCharValue(dbArgs, "brokerClientId", 0, AQHOME_REACT_DEFAULT_BROKER_CLIENTID); + + if (brokerAddress && *brokerAddress && brokerPort) { + GWEN_MSG_ENDPOINT *ep; + GWEN_MSG_ENDPOINT *ipcBaseEndpoint; + int rv; + + ep=AQH_ClientIpcEndpoint_new("brokerIpcClient", 0); + ipcBaseEndpoint=AQH_IpcEndpoint_CreateIpcTcpClient(brokerAddress, brokerPort, "brokerPhysEndpoint", 0); + AQH_IpcEndpoint_SetServiceName(ipcBaseEndpoint, brokerClientId); + GWEN_MsgEndpoint_Tree2_AddChild(ep, ipcBaseEndpoint); + + aqh->brokerEndpoint=ep; + + rv=GWEN_MultilayerEndpoint_StartConnect(ep); + if (rv<0 && rv!=GWEN_ERROR_IN_PROGRESS) { + DBG_ERROR(NULL, "Error connecting to broker server %s:%d (%d), will retry later", brokerAddress, brokerPort, rv); + return rv; + } + } + + return 0; +} + + + +int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs) +{ + int rv; + const GWEN_ARGS args[]= { + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Char, /* type */ + "cfgdir", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "D", /* short option */ + "cfgdir", /* long option */ + I18S("Specify the configuration folder"), + I18S("Specify the configuration folder") + }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Char, /* type */ + "charset", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + 0, /* short option */ + "charset", /* long option */ + I18S("Specify the output character set"), /* short description */ + I18S("Specify the output character set") /* long description */ + }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Char, /* type */ + "brokerAddress", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "ba", /* short option */ + "brokeraddress", /* long option */ + I18S("Specify the address of the broker server to connect to (disabled if missing)"), + I18S("Specify the address of the broker server to connect to (disabled if missing)") + }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Int, /* type */ + "brokerPort", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "bp", /* short option */ + "brokerport", /* long option */ + I18S("Specify the port of the broker server (default: 1899)"), + I18S("Specify the port of the broker server (default: 1899)") + }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Char, /* type */ + "brokerClientId", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + NULL, /* short option */ + "brokerclientid", /* long option */ + I18S("Specify client id for the broker server (default: \"nodes\")"), + I18S("Specify client id for the broker server (default: \"nodes\")") + }, + + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Char, /* type */ + "pidfile", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "p", /* short option */ + "pidfile", /* long option */ + I18S("Specify the PID file"), + I18S("Specify the PID file") + }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Int, /* type */ + "timeout", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "T", /* short option */ + "timeout", /* long option */ + I18S("Specify timeout in second (default: no timeout)"), + I18S("Specify timeout in second (default: no timeout)") + }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Char, /* type */ + "devicefile", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "d", /* short option */ + "devicefile", /* long option */ + I18S("Specify the device file"), + I18S("Specify the device file") + }, + + { + GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST, /* flags */ + GWEN_ArgsType_Int, /* type */ + "help", /* name */ + 0, /* minnum */ + 0, /* maxnum */ + "h", /* short option */ + "help", + I18S("Show this help screen."), + I18S("Show this help screen.") + } + }; + + rv=GWEN_Args_Check(argc, argv, 1, 0, args, dbArgs); + if (rv==GWEN_ARGS_RESULT_ERROR) { + fprintf(stderr, "ERROR: Could not parse arguments main\n"); + return GWEN_ERROR_INVALID; + } + else if (rv==GWEN_ARGS_RESULT_HELP) { + GWEN_BUFFER *ubuf; + + ubuf=GWEN_Buffer_new(0, 1024, 0, 1); + GWEN_Buffer_AppendArgs(ubuf, + I18N("This is version %s.\nUsage: %s [OPTIONS]\n\nOptions:\n"), + AQHOME_VERSION_STRING, + argv[0]); + if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) { + fprintf(stderr, "ERROR: Could not create help string\n"); + return 1; + } + GWEN_Buffer_AppendString(ubuf, "\n"); + + fprintf(stdout, "%s\n", GWEN_Buffer_GetStart(ubuf)); + GWEN_Buffer_free(ubuf); + return GWEN_ERROR_CLOSE; + } + return 0; +} + + + + + diff --git a/apps/aqhome-react/init.h b/apps/aqhome-react/init.h new file mode 100644 index 0000000..b520658 --- /dev/null +++ b/apps/aqhome-react/init.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_INIT_H +#define AQHOMEREACT_INIT_H + + +#include "./aqhome_react.h" + + +int AqHomeReact_Init(AQHOME_REACT *aqh, int argc, char **argv); + + +#endif + + diff --git a/apps/aqhome-react/loop.c b/apps/aqhome-react/loop.c new file mode 100644 index 0000000..726d832 --- /dev/null +++ b/apps/aqhome-react/loop.c @@ -0,0 +1,103 @@ +/**************************************************************************** + * 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 "./loop.h" +#include "./aqhome_react_p.h" + +#include "aqhome/ipc/data/ipc_data.h" +#include "aqhome/ipc/data/msg_data_multidata.h" +#include "aqhome/ipc/msg_ipc_tag16.h" + +#include + +#include + + + +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static void _handleDataResponse(GWEN_MSG *msg); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +void AqHomeReact_Loop(AQHOME_REACT *aqh, int timeoutInMilliSecs) +{ + GWEN_MSG *msg; + + GWEN_MsgEndpoint_IoLoop(aqh->brokerEndpoint, timeoutInMilliSecs); + msg=GWEN_MsgEndpoint_TakeFirstReceivedMessage(aqh->brokerEndpoint); + if (msg) { + uint16_t code; + + code=GWEN_IpcMsg_GetCode(msg); + if (code==AQH_MSGTYPE_IPC_DATA_DATACHANGED) { + DBG_INFO(NULL, "Received expected IPC message"); + _handleDataResponse(msg); + } + else if (code==AQH_MSGTYPE_IPC_DATA_RESULT) { + DBG_INFO(NULL, "Received IPC result message, ignoring"); + } + else { + DBG_INFO(NULL, "Received unexpected message %d (%x)", code, code); + } + GWEN_Msg_free(msg); + } +} + + + +void _handleDataResponse(GWEN_MSG *msg) +{ + AQH_VALUE *value; + const GWEN_TAG16 *tag; + unsigned int numberOfPoints; + const uint64_t *dataPoints; + + AQH_MultiDataDataIpcMsg_Parse(msg, 0); + value=AQH_MultiDataDataIpcMsg_ReadValue(msg); + + tag=AQH_Tag16IpcMsg_FindFirstTagByType(msg, AQH_MSGDATA_MULTIDATA_TAGS_DATA); + numberOfPoints=(tag?GWEN_Tag16_GetTagLength(tag):0)/(2*sizeof(uint64_t)); + dataPoints=tag?((const uint64_t*) GWEN_Tag16_GetTagData(tag)):NULL; + if (numberOfPoints>0 && dataPoints) { + uint32_t i; + + for(i=0; i +#endif + +#include "./init.h" +#include "./fini.h" +#include "./loop.h" + +#include "aqhome/aqhome.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SIGNAL_H +# include +#endif + +#include +#include +#include +#include +#include +#include +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ + +#define I18N(msg) msg +#define I18S(msg) msg + + +#define FULL_DEBUG + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static void _serve(AQHOME_REACT *aqh); + +#ifdef HAVE_SIGNAL_H +static int _setSignalHandlers(void); +static int _setupSigAction(struct sigaction *sa, int sig); +static void _signalHandler(int s); +static struct sigaction saINT,saTERM, saHUP, saTSTP, saCONT; +#endif + + + +/* ------------------------------------------------------------------------------------------------ + * static vars + * ------------------------------------------------------------------------------------------------ + */ + +static int stopService=0; + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ int main(int argc, char **argv) { + AQHOME_REACT *aqh; + GWEN_DB_NODE *dbArgs; + int rv; + GWEN_GUI *gui; + const char *s; + + rv=GWEN_Init(); + if (rv) { + fprintf(stderr, "ERROR: Unable to init Gwen.\n"); + return 2; + } + + GWEN_Logger_Open(0, "aqhome-react", 0, GWEN_LoggerType_Console, GWEN_LoggerFacility_User); + GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Warning); + //GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Info); + + rv=AQH_Init(); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + return 2; + } + + aqh=AqHomeReact_new(); + rv=AqHomeReact_Init(aqh, argc, argv); + if (rv<0) { + if (rv==GWEN_ERROR_CLOSE) + return 1; + DBG_INFO(NULL, "here (%d)", rv); + return 2; + } + + dbArgs=AqHomeReact_GetDbArgs(aqh); + + gui=GWEN_Gui_CGui_new(); + s=GWEN_DB_GetCharValue(dbArgs, "charset", 0, NULL); + if (s && *s) + GWEN_Gui_SetCharSet(gui, s); + GWEN_Gui_SetGui(gui); + + _serve(aqh); + + AqHomeReact_Fini(aqh); + AqHomeReact_free(aqh); + + GWEN_Gui_SetGui(NULL); + GWEN_Gui_free(gui); + + return 0; } + +void _serve(AQHOME_REACT *aqh) +{ + int rv; + int timeout; + time_t startTime; + time_t lastTimerTime; + GWEN_DB_NODE *dbArgs; + + startTime=time(NULL); + lastTimerTime=startTime; + + dbArgs=AqHomeReact_GetDbArgs(aqh); + + rv=_setSignalHandlers(); + if (rv<0) { + DBG_ERROR(NULL, "Error setting signal handlers (%d)", rv); + return; + } + + timeout=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 0); + + while(!stopService) { + time_t now; + + DBG_DEBUG(NULL, "Next loop"); + AqHomeReact_Loop(aqh, 500); + + now=time(NULL); + if (now!=lastTimerTime) { + lastTimerTime=now; + /* TODO: fire timer */ + } + + if (timeout) { + if ((now-startTime)>timeout) { + DBG_ERROR(NULL, "Timeout, stopping service"); + break; + } + } + + } /* while */ + +} + + + +int _setSignalHandlers(void) +{ +#ifdef HAVE_SIGNAL_H + int rv; + + rv=_setupSigAction(&saINT, SIGINT); + if (rv) + return rv; + + rv=_setupSigAction(&saTERM, SIGTERM); + if (rv) + return rv; + + rv=_setupSigAction(&saHUP, SIGHUP); + if (rv) + return rv; + +# ifdef SIGTSTP + rv=_setupSigAction(&saTSTP, SIGTSTP); + if (rv) + return rv; +# endif + +# ifdef SIGCONT + rv=_setupSigAction(&saCONT, SIGCONT); + if (rv) + return rv; +# endif +#endif + return 0; +} + + + +int _setupSigAction(struct sigaction *sa, int sig) +{ + sa->sa_handler=_signalHandler; + sigemptyset(&sa->sa_mask); + sa->sa_flags=0; + if (sigaction(sig, sa, 0)) { + DBG_ERROR(NULL, "Could not setup signal handler for signal %d", sig); + return GWEN_ERROR_IO; + } + + return 0; +} + + + +void _signalHandler(int s) +{ + switch(s) { + case SIGINT: + case SIGTERM: + case SIGHUP: + DBG_WARN(0, "Received signal %d, stopping service in next loop.",s); + stopService=1; + break; + default: + DBG_WARN(0, "Unknown signal %d",s); + break; + } +} + + + + diff --git a/apps/aqhome-react/types/inputslot.c b/apps/aqhome-react/types/inputslot.c index dbcf5dd..c8b6221 100644 --- a/apps/aqhome-react/types/inputslot.c +++ b/apps/aqhome-react/types/inputslot.c @@ -12,6 +12,7 @@ #include "./inputslot_p.h" +#include "aqhome-react/types/unit.h" #include #include @@ -27,6 +28,8 @@ AQHREACT_INPUT_SLOT *AQHREACT_InputSlot_new() GWEN_NEW_OBJECT(AQHREACT_INPUT_SLOT, inSlot); GWEN_LIST_INIT(AQHREACT_INPUT_SLOT, inSlot); + inSlot->idForUnit=-1; + return inSlot; } @@ -54,7 +57,7 @@ AQHREACT_INPUT_SLOT *AQHREACT_InputSlot_dup(const AQHREACT_INPUT_SLOT *origSlot) AQHREACT_InputSlot_SetName(inSlot, origSlot->name); AQHREACT_InputSlot_SetDescription(inSlot, origSlot->description); inSlot->idForUnit=origSlot->idForUnit; - inSlot->flags=origSlot->flags; + inSlot->flags=origSlot->flags & ~AQHREACT_UNIT_FLAGS_MULTI; /* don't copy MULTI flag */ inSlot->acceptedDataType=origSlot->acceptedDataType; /* don't copy current dataObject */ return inSlot; diff --git a/apps/aqhome-react/types/link.c b/apps/aqhome-react/types/link.c index 33b3019..81fe0cf 100644 --- a/apps/aqhome-react/types/link.c +++ b/apps/aqhome-react/types/link.c @@ -25,7 +25,7 @@ AQHREACT_LINK *AQHREACT_Link_new() GWEN_NEW_OBJECT(AQHREACT_LINK, lnk); GWEN_LIST_INIT(AQHREACT_LINK, lnk); - lnk->targetInputSlotIdx=-1; + lnk->targetInputSlotIdForUnit=-1; return lnk; } @@ -60,17 +60,17 @@ void AQHREACT_Link_SetTargetUnitId(AQHREACT_LINK *lnk, const char *s) -int AQHREACT_Link_GetTargetInputSlotIdx(const AQHREACT_LINK *lnk) +int AQHREACT_Link_GetTargetInputSlotIdForUnit(const AQHREACT_LINK *lnk) { - return lnk?lnk->targetInputSlotIdx:-1; + return lnk?lnk->targetInputSlotIdForUnit:-1; } -void AQHREACT_Link_SetTargetInputSlotIdx(AQHREACT_LINK *lnk, int i) +void AQHREACT_Link_SetTargetInputSlotIdForUnit(AQHREACT_LINK *lnk, int i) { if (lnk) - lnk->targetInputSlotIdx=i;; + lnk->targetInputSlotIdForUnit=i; } diff --git a/apps/aqhome-react/types/link.h b/apps/aqhome-react/types/link.h index 1de37b1..c6ee371 100644 --- a/apps/aqhome-react/types/link.h +++ b/apps/aqhome-react/types/link.h @@ -27,8 +27,8 @@ void AQHREACT_Link_free(AQHREACT_LINK *lnk); const char *AQHREACT_Link_GetTargetUnitId(const AQHREACT_LINK *lnk); void AQHREACT_Link_SetTargetUnitId(AQHREACT_LINK *lnk, const char *s); -int AQHREACT_Link_GetTargetInputSlotIdx(const AQHREACT_LINK *lnk); -void AQHREACT_Link_SetTargetInputSlotIdx(AQHREACT_LINK *lnk, int i); +int AQHREACT_Link_GetTargetInputSlotIdForUnit(const AQHREACT_LINK *lnk); +void AQHREACT_Link_SetTargetInputSlotIdForUnit(AQHREACT_LINK *lnk, int i); AQHREACT_UNIT *AQHREACT_Link_GetTargetUnit(const AQHREACT_LINK *lnk); void AQHREACT_Link_SetTargetUnit(AQHREACT_LINK *lnk, AQHREACT_UNIT *unit); diff --git a/apps/aqhome-react/types/link_p.h b/apps/aqhome-react/types/link_p.h index 19bd55e..538b467 100644 --- a/apps/aqhome-react/types/link_p.h +++ b/apps/aqhome-react/types/link_p.h @@ -17,7 +17,7 @@ struct AQHREACT_LINK { GWEN_LIST_ELEMENT(AQHREACT_LINK) char *targetUnitId; - int targetInputSlotIdx; + int targetInputSlotIdForUnit; AQHREACT_UNIT *targetUnit; }; diff --git a/apps/aqhome-react/types/outputslot.c b/apps/aqhome-react/types/outputslot.c index 3cd58c9..82a3b54 100644 --- a/apps/aqhome-react/types/outputslot.c +++ b/apps/aqhome-react/types/outputslot.c @@ -28,6 +28,7 @@ AQHREACT_OUTPUT_SLOT *AQHREACT_OutputSlot_new() GWEN_NEW_OBJECT(AQHREACT_OUTPUT_SLOT, outSlot); GWEN_LIST_INIT(AQHREACT_OUTPUT_SLOT, outSlot); outSlot->linkList=AQHREACT_Link_List_new(); + outSlot->id=-1; return outSlot; } @@ -46,6 +47,21 @@ void AQHREACT_OutputSlot_free(AQHREACT_OUTPUT_SLOT *outSlot) +int AQHREACT_OutputSlot_GetIdForUnit(const AQHREACT_OUTPUT_SLOT *outSlot) +{ + return outSlot?outSlot->id:-1; +} + + + +void AQHREACT_OutputSlot_SetIdForUnit(AQHREACT_OUTPUT_SLOT *outSlot, int i) +{ + if (outSlot) + outSlot->id=i; +} + + + const char *AQHREACT_OutputSlot_GetName(const AQHREACT_OUTPUT_SLOT *outSlot) { return outSlot?outSlot->name:NULL; diff --git a/apps/aqhome-react/types/outputslot.h b/apps/aqhome-react/types/outputslot.h index ed848f5..08790e0 100644 --- a/apps/aqhome-react/types/outputslot.h +++ b/apps/aqhome-react/types/outputslot.h @@ -31,6 +31,9 @@ void AQHREACT_OutputSlot_SetName(AQHREACT_OUTPUT_SLOT *outSlot, const char *s); const char *AQHREACT_OutputSlot_GetDescription(const AQHREACT_OUTPUT_SLOT *outSlot); void AQHREACT_OutputSlot_SetDescription(AQHREACT_OUTPUT_SLOT *outSlot, const char *s); +int AQHREACT_OutputSlot_GetIdForUnit(const AQHREACT_OUTPUT_SLOT *outSlot); +void AQHREACT_OutputSlot_SetIdForUnit(AQHREACT_OUTPUT_SLOT *outSlot, int i); + uint32_t AQHREACT_OutputSlot_GetFlags(const AQHREACT_OUTPUT_SLOT *outSlot); void AQHREACT_OutputSlot_SetFlags(AQHREACT_OUTPUT_SLOT *outSlot, uint32_t f); void AQHREACT_OutputSlot_AddFlags(AQHREACT_OUTPUT_SLOT *outSlot, uint32_t f); diff --git a/apps/aqhome-react/types/outputslot_p.h b/apps/aqhome-react/types/outputslot_p.h index 35b692a..67aa9bd 100644 --- a/apps/aqhome-react/types/outputslot_p.h +++ b/apps/aqhome-react/types/outputslot_p.h @@ -16,8 +16,10 @@ struct AQHREACT_OUTPUT_SLOT { GWEN_LIST_ELEMENT(AQHREACT_OUTPUT_SLOT) + int id; char *name; char *description; + int idForUnit; uint32_t flags; int emittedDataType; diff --git a/apps/aqhome-react/types/unit.c b/apps/aqhome-react/types/unit.c index 766956c..70f1f65 100644 --- a/apps/aqhome-react/types/unit.c +++ b/apps/aqhome-react/types/unit.c @@ -105,6 +105,21 @@ void AQHREACT_Unit_SetId(AQHREACT_UNIT *unit, const char *s) +int AQHREACT_Unit_GetNextInputSlotId(const AQHREACT_UNIT *unit) +{ + return unit?unit->nextInputSlotId:-1; +} + + + +void AQHREACT_Unit_SetNextInputSlotId(AQHREACT_UNIT *unit, int i) +{ + if (unit) + unit->nextInputSlotId=i; +} + + + uint32_t AQHREACT_Unit_GetFlags(const AQHREACT_UNIT *unit) { return unit?unit->flags:0; @@ -181,6 +196,108 @@ void AQHREACT_Unit_AddParam(AQHREACT_UNIT *unit, AQHREACT_PARAM *param) +AQHREACT_PARAM *AQHREACT_Unit_GetParamByName(const AQHREACT_UNIT *unit, const char *paramName) +{ + if (unit && unit->paramList && paramName && *paramName) { + AQHREACT_PARAM *param; + + param=AQHREACT_Param_List_First(unit->paramList); + while(param) { + const char *s; + + s=AQHREACT_Param_GetName(param); + if (s && *s && strcasecmp(paramName, s)==0) + return param; + param=AQHREACT_Param_List_Next(param); + } + } + return NULL; +} + + + +double AQHREACT_Unit_GetParamValueDouble(const AQHREACT_UNIT *unit, const char *paramName, double defVal) +{ + AQHREACT_PARAM *param; + + param=AQHREACT_Unit_GetParamByName(unit, paramName); + if (param) { + if (AQHREACT_Param_GetDataType(param)==AQHREACT_DATAOBJECTTYPE_DOUBLE) { + return AQHREACT_Param_GetDoubleValue(param); + } + else { + DBG_INFO(NULL, "Datatype for param \"%s/%s\" is not DOUBLE", (unit->name)?unit->name:"", paramName); + } + } + + return defVal; +} + + + +void AQHREACT_Unit_SetParamValueDouble(AQHREACT_UNIT *unit, const char *paramName, double val) +{ + AQHREACT_PARAM *param; + + param=AQHREACT_Unit_GetParamByName(unit, paramName); + if (param) { + if (AQHREACT_Param_GetDataType(param)==AQHREACT_DATAOBJECTTYPE_DOUBLE) { + AQHREACT_Param_SetDoubleValue(param, val); + } + else { + DBG_INFO(NULL, "Datatype for param \"%s/%s\" is not DOUBLE", (unit->name)?unit->name:"", paramName); + } + } + else { + DBG_INFO(NULL, "Param \"%s\" not found in unit %s", paramName, (unit->name)?unit->name:""); + } +} + + + +const char *AQHREACT_Unit_GetParamValueString(const AQHREACT_UNIT *unit, const char *paramName, const char *defVal) +{ + AQHREACT_PARAM *param; + + param=AQHREACT_Unit_GetParamByName(unit, paramName); + if (param) { + if (AQHREACT_Param_GetDataType(param)==AQHREACT_DATAOBJECTTYPE_STRING) { + return AQHREACT_Param_GetStringValue(param); + } + else { + DBG_INFO(NULL, "Datatype for param \"%s/%s\" is not STRING", (unit->name)?unit->name:"", paramName); + } + } + else { + DBG_INFO(NULL, "Param \"%s\" not found in unit %s", paramName, (unit->name)?unit->name:""); + } + + return defVal; +} + + + +void AQHREACT_Unit_SetParamValueString(AQHREACT_UNIT *unit, const char *paramName, const char *val) +{ + AQHREACT_PARAM *param; + + param=AQHREACT_Unit_GetParamByName(unit, paramName); + if (param) { + if (AQHREACT_Param_GetDataType(param)==AQHREACT_DATAOBJECTTYPE_STRING) { + AQHREACT_Param_SetStringValue(param, val); + } + else { + DBG_INFO(NULL, "Datatype for param \"%s\" is not STRING", paramName); + } + } + else { + DBG_INFO(NULL, "Param \"%s\" not found in unit %s", paramName, (unit->name)?unit->name:""); + } +} + + + + AQHREACT_UNIT_INPUTDATA_FN AQHREACT_Unit_SetInputDataFn(AQHREACT_UNIT *unit, AQHREACT_UNIT_INPUTDATA_FN f) { if (unit) { @@ -209,12 +326,12 @@ AQHREACT_UNIT_PROCESS_FN AQHREACT_Unit_SetProcessFn(AQHREACT_UNIT *unit, AQHREAC -void AQHREACT_Unit_OutputData(AQHREACT_UNIT *unit, int slotIndex, const AQHREACT_DATAOBJECT *dataObject) +void AQHREACT_Unit_OutputData(AQHREACT_UNIT *unit, int slotIdForUnit, const AQHREACT_DATAOBJECT *dataObject) { if (unit && unit->outputSlotList) { AQHREACT_OUTPUT_SLOT *slot; - slot=AQHREACT_Unit_GetOutputSlotAt(unit, slotIndex); + slot=AQHREACT_Unit_GetOutputSlotByIdForUnit(unit, slotIdForUnit); if (slot) { AQHREACT_LINK_LIST *linkList; @@ -225,12 +342,12 @@ void AQHREACT_Unit_OutputData(AQHREACT_UNIT *unit, int slotIndex, const AQHREACT lnk=AQHREACT_Link_List_First(linkList); while(lnk) { AQHREACT_UNIT *targetUnit; - int idx; + int targetSlotIdForUnit; targetUnit=AQHREACT_Link_GetTargetUnit(lnk); - idx=AQHREACT_Link_GetTargetInputSlotIdx(lnk); - if (targetUnit && idx>=0) - AQHREACT_Unit_InputData(targetUnit, idx, dataObject); + targetSlotIdForUnit=AQHREACT_Link_GetTargetInputSlotIdForUnit(lnk); + if (targetUnit && targetSlotIdForUnit>=0) + AQHREACT_Unit_InputData(targetUnit, targetSlotIdForUnit, dataObject); lnk=AQHREACT_Link_List_Next(lnk); } /* while */ } /* if linkList */ @@ -240,11 +357,41 @@ void AQHREACT_Unit_OutputData(AQHREACT_UNIT *unit, int slotIndex, const AQHREACT -void AQHREACT_Unit_InputData(AQHREACT_UNIT *unit, int slotIndex, const AQHREACT_DATAOBJECT *dataObject) +void AQHREACT_Unit_OutputDoubleData(AQHREACT_UNIT *unit, int slotIndex, double data) +{ + if (unit && unit->outputSlotList) { + AQHREACT_DATAOBJECT *dataObject; + + dataObject=AQHREACT_DataObject_new(); + AQHREACT_DataObject_SetDataType(dataObject, AQHREACT_DATAOBJECTTYPE_DOUBLE); + AQHREACT_DataObject_SetDoubleData(dataObject, data); + AQHREACT_Unit_OutputData(unit, slotIndex, dataObject); + AQHREACT_DataObject_free(dataObject); + } +} + + + +void AQHREACT_Unit_OutputStringData(AQHREACT_UNIT *unit, int slotIndex, const char *data) +{ + if (unit && unit->outputSlotList) { + AQHREACT_DATAOBJECT *dataObject; + + dataObject=AQHREACT_DataObject_new(); + AQHREACT_DataObject_SetDataType(dataObject, AQHREACT_DATAOBJECTTYPE_STRING); + AQHREACT_DataObject_SetStringData(dataObject, data); + AQHREACT_Unit_OutputData(unit, slotIndex, dataObject); + AQHREACT_DataObject_free(dataObject); + } +} + + + +void AQHREACT_Unit_InputData(AQHREACT_UNIT *unit, int slotIdForUnit, const AQHREACT_DATAOBJECT *dataObject) { if (unit) { if (unit->inputDataFn) - (unit->inputDataFn)(unit, slotIndex, dataObject); + (unit->inputDataFn)(unit, slotIdForUnit, dataObject); else { AQHREACT_Unit_ClearChangeFlagsInUnitAndInputSlots(unit); } @@ -261,16 +408,8 @@ int AQHREACT_Unit_Process(AQHREACT_UNIT *unit) if (unit) { if (unit->processFn) return (unit->processFn)(unit); - else { - AQHREACT_INPUT_SLOT *slot; - - unit->flags&=~AQHREACT_UNIT_FLAGS_CHANGED; - slot=AQHREACT_InputSlot_List_First(unit->inputSlotList); - while(slot) { - AQHREACT_InputSlot_SubFlags(slot, AQHREACT_UNIT_FLAGS_CHANGED); - slot=AQHREACT_InputSlot_List_Next(slot); - } - } + else + AQHREACT_Unit_ClearChangeFlagsInUnitAndInputSlots(unit); } return 0; @@ -278,18 +417,17 @@ int AQHREACT_Unit_Process(AQHREACT_UNIT *unit) -AQHREACT_INPUT_SLOT *AQHREACT_Unit_GetInputSlotAt(AQHREACT_UNIT *unit, int idx) +AQHREACT_INPUT_SLOT *AQHREACT_Unit_GetInputSlotByIdForUnit(const AQHREACT_UNIT *unit, int id) { - if (unit && unit->inputSlotList && idx>=0) { + if (unit && unit->inputSlotList && id>=0) { AQHREACT_INPUT_SLOT *slot; slot=AQHREACT_InputSlot_List_First(unit->inputSlotList); - while(slot && idx) { + while(slot) { + if (AQHREACT_InputSlot_GetIdForUnit(slot)==id) + return slot; slot=AQHREACT_InputSlot_List_Next(slot); - idx--; } - - return slot; } return NULL; @@ -297,15 +435,92 @@ AQHREACT_INPUT_SLOT *AQHREACT_Unit_GetInputSlotAt(AQHREACT_UNIT *unit, int idx) -AQHREACT_OUTPUT_SLOT *AQHREACT_Unit_GetOutputSlotAt(AQHREACT_UNIT *unit, int idx) +AQHREACT_INPUT_SLOT *AQHREACT_Unit_GetInputSlotByName(const AQHREACT_UNIT *unit, const char *s) { - if (unit && unit->outputSlotList && idx>=0) { + if (unit && unit->inputSlotList && s && *s) { + AQHREACT_INPUT_SLOT *slot; + + slot=AQHREACT_InputSlot_List_First(unit->inputSlotList); + while(slot) { + const char *slotName; + + slotName=AQHREACT_InputSlot_GetName(slot); + if (slotName && *slotName && strcasecmp(slotName, s)==0) + return slot; + slot=AQHREACT_InputSlot_List_Next(slot); + } + } + + return NULL; +} + + + +AQHREACT_INPUT_SLOT *AQHREACT_Unit_GetOrCreateUnusedInputSlotByName(AQHREACT_UNIT *unit, const char *s) +{ + if (unit && unit->inputSlotList && s && *s) { + AQHREACT_INPUT_SLOT *slot; + + slot=AQHREACT_Unit_GetInputSlotByName(unit, s); + if (slot) { + uint32_t flags; + const char *slotName; + + slotName=AQHREACT_InputSlot_GetName(slot); + flags=AQHREACT_InputSlot_GetFlags(slot); + if (!(flags & AQHREACT_UNIT_FLAGS_INUSE)) + return slot; + if (flags & AQHREACT_UNIT_FLAGS_MULTI) { /* can we just duplicate existing? */ + AQHREACT_INPUT_SLOT *newSlot; + + newSlot=AQHREACT_InputSlot_dup(slot); + AQHREACT_InputSlot_SetIdForUnit(newSlot, (unit->nextInputSlotId)++); + AQHREACT_Unit_AddInputSlot(unit, newSlot); + return newSlot; + } + else { + DBG_INFO(NULL, "Slot \"%s\" found but in use, no multi flag", slotName); + } + } + } + + return NULL; +} + + + +AQHREACT_OUTPUT_SLOT *AQHREACT_Unit_GetOutputSlotByIdForUnit(const AQHREACT_UNIT *unit, int id) +{ + if (unit && unit->outputSlotList && id>=0) { AQHREACT_OUTPUT_SLOT *slot; slot=AQHREACT_OutputSlot_List_First(unit->outputSlotList); - while(slot && idx) { + while(slot) { + if (AQHREACT_OutputSlot_GetIdForUnit(slot)==id) + return slot; + slot=AQHREACT_OutputSlot_List_Next(slot); + } + } + + return NULL; +} + + + +AQHREACT_OUTPUT_SLOT *AQHREACT_Unit_GetOutputSlotByName(const AQHREACT_UNIT *unit, const char *s) +{ + if (unit && unit->outputSlotList && s && *s) { + AQHREACT_OUTPUT_SLOT *slot; + + slot=AQHREACT_OutputSlot_List_First(unit->outputSlotList); + while(slot) { + const char *slotName; + + slotName=AQHREACT_OutputSlot_GetName(slot); + if (slotName && *slotName && strcasecmp(slotName, s)==0) + return slot; + slot=AQHREACT_OutputSlot_List_Next(slot); - idx--; } return slot; } @@ -331,5 +546,25 @@ void AQHREACT_Unit_ClearChangeFlagsInUnitAndInputSlots(AQHREACT_UNIT *unit) +int AQHREACT_Unit_InputHasChanged(const AQHREACT_UNIT *unit) +{ + if (unit) { + AQHREACT_INPUT_SLOT *slot; + + if (unit->flags & AQHREACT_UNIT_FLAGS_CHANGED) + return 1; + + slot=AQHREACT_InputSlot_List_First(unit->inputSlotList); + while(slot) { + if (AQHREACT_InputSlot_GetFlags(slot) & AQHREACT_UNIT_FLAGS_CHANGED) + return 1; + slot=AQHREACT_InputSlot_List_Next(slot); + } + } + return 0; +} + + + diff --git a/apps/aqhome-react/types/unit.h b/apps/aqhome-react/types/unit.h index f89a4e7..db20906 100644 --- a/apps/aqhome-react/types/unit.h +++ b/apps/aqhome-react/types/unit.h @@ -31,7 +31,7 @@ GWEN_INHERIT_FUNCTION_DEFS(AQHREACT_UNIT) #include "aqhome-react/types/param.h" -typedef void (*AQHREACT_UNIT_INPUTDATA_FN)(AQHREACT_UNIT *unit, int slotIndex, const AQHREACT_DATAOBJECT *dataObject); +typedef void (*AQHREACT_UNIT_INPUTDATA_FN)(AQHREACT_UNIT *unit, int slotIdForUnit, const AQHREACT_DATAOBJECT *dataObject); typedef int (*AQHREACT_UNIT_PROCESS_FN)(AQHREACT_UNIT *unit); @@ -48,6 +48,9 @@ void AQHREACT_Unit_SetDescription(AQHREACT_UNIT *unit, const char *s); const char *AQHREACT_Unit_GetId(const AQHREACT_UNIT *unit); void AQHREACT_Unit_SetId(AQHREACT_UNIT *unit, const char *s); +int AQHREACT_Unit_GetNextInputSlotId(const AQHREACT_UNIT *unit); +void AQHREACT_Unit_SetNextInputSlotId(AQHREACT_UNIT *unit, int i); + uint32_t AQHREACT_Unit_GetFlags(const AQHREACT_UNIT *unit); void AQHREACT_Unit_SetFlags(AQHREACT_UNIT *unit, uint32_t i); void AQHREACT_Unit_AddFlags(AQHREACT_UNIT *unit, uint32_t i); @@ -55,22 +58,33 @@ void AQHREACT_Unit_SubFlags(AQHREACT_UNIT *unit, uint32_t i); 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_GetInputSlotAt(AQHREACT_UNIT *unit, int idx); +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_OUTPUT_SLOT_LIST *AQHREACT_Unit_GetOutputSlots(const AQHREACT_UNIT *unit); void AQHREACT_Unit_AddOutputSlot(AQHREACT_UNIT *unit, AQHREACT_OUTPUT_SLOT *outSlot); -AQHREACT_OUTPUT_SLOT *AQHREACT_Unit_GetOutputSlotAt(AQHREACT_UNIT *unit, int idx); +AQHREACT_OUTPUT_SLOT *AQHREACT_Unit_GetOutputSlotByIdForUnit(const AQHREACT_UNIT *unit, int idx); +AQHREACT_OUTPUT_SLOT *AQHREACT_Unit_GetOutputSlotByName(const AQHREACT_UNIT *unit, const char *s); void AQHREACT_Unit_ClearChangeFlagsInUnitAndInputSlots(AQHREACT_UNIT *unit); +int AQHREACT_Unit_InputHasChanged(const AQHREACT_UNIT *unit); AQHREACT_PARAM_LIST *AQHREACT_Unit_GetParamList(const AQHREACT_UNIT *unit); void AQHREACT_Unit_AddParam(AQHREACT_UNIT *unit, AQHREACT_PARAM *param); - +AQHREACT_PARAM *AQHREACT_Unit_GetParamByName(const AQHREACT_UNIT *unit, const char *s); +double AQHREACT_Unit_GetParamValueDouble(const AQHREACT_UNIT *unit, const char *s, double defVal); +void AQHREACT_Unit_SetParamValueDouble(AQHREACT_UNIT *unit, const char *paramName, double val); +const char *AQHREACT_Unit_GetParamValueString(const AQHREACT_UNIT *unit, const char *paramName, const char *defVal); +void AQHREACT_Unit_SetParamValueString(AQHREACT_UNIT *unit, const char *paramName, const char *val); void AQHREACT_Unit_OutputData(AQHREACT_UNIT *unit, int slotIndex, const AQHREACT_DATAOBJECT *dataObject); +void AQHREACT_Unit_OutputDoubleData(AQHREACT_UNIT *unit, int slotIndex, double data); +void AQHREACT_Unit_OutputStringData(AQHREACT_UNIT *unit, int slotIndex, const char *data); -void AQHREACT_Unit_InputData(AQHREACT_UNIT *unit, int slotIndex, const AQHREACT_DATAOBJECT *dataObject); + +void AQHREACT_Unit_InputData(AQHREACT_UNIT *unit, int slotIdForUnit, const AQHREACT_DATAOBJECT *dataObject); int AQHREACT_Unit_Process(AQHREACT_UNIT *unit); diff --git a/apps/aqhome-react/types/unit_p.h b/apps/aqhome-react/types/unit_p.h index ab82de0..0b03e3e 100644 --- a/apps/aqhome-react/types/unit_p.h +++ b/apps/aqhome-react/types/unit_p.h @@ -23,6 +23,8 @@ struct AQHREACT_UNIT { uint32_t flags; + int nextInputSlotId; + AQHREACT_INPUT_SLOT_LIST *inputSlotList; AQHREACT_OUTPUT_SLOT_LIST *outputSlotList; diff --git a/apps/aqhome-react/units/0BUILD b/apps/aqhome-react/units/0BUILD new file mode 100644 index 0000000..1dbf4ef --- /dev/null +++ b/apps/aqhome-react/units/0BUILD @@ -0,0 +1,71 @@ + + + + + + + + $(gwenhywfar_cflags) + -I$(topsrcdir) + -I$(topbuilddir) + -I$(topsrcdir)/apps + -I$(topbuilddir)/apps + -I$(builddir) + -I$(srcdir) + + + + --include=$(builddir) + --include=$(srcdir) + + + $(visibility_cflags) + + + + + + + + + + + + + + + + + + u_or.h + u_varchanges.h + u_valuefilter.h + + + + $(local/typefiles) + u_or.c + u_varchanges.c + u_valuefilter.c + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/aqhome-react/units/u_or.c b/apps/aqhome-react/units/u_or.c new file mode 100644 index 0000000..8ac5458 --- /dev/null +++ b/apps/aqhome-react/units/u_or.c @@ -0,0 +1,136 @@ +/**************************************************************************** + * 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_or.h" + +#include + + + +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ + +#define AQHOMEREACT_UNIT_OR_INSLOT_INPUT 0 +#define AQHOMEREACT_UNIT_OR_INSLOT_AUTOINPUT 100 + +#define AQHOMEREACT_UNIT_OR_OUTSLOT_RESULT 0 + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static int _cbProcessFn(AQHREACT_UNIT *unit); +static int _sampleInputSlots(const AQHREACT_INPUT_SLOT_LIST *inputSlotList); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +AQHREACT_UNIT *AqHomeReact_UnitOr_new(AQHOME_REACT *aqh) +{ + AQHREACT_UNIT *unit; + AQHREACT_OUTPUT_SLOT *outputSlot; + AQHREACT_INPUT_SLOT *inputSlot; + + unit=AQHREACT_Unit_new(); + AQHREACT_Unit_SetName(unit, "or"); + AQHREACT_Unit_SetDescription(unit, "Logical OR inputs"); + AQHREACT_Unit_SetProcessFn(unit, _cbProcessFn); + + AQHREACT_Unit_SetNextInputSlotId(unit, AQHOMEREACT_UNIT_OR_INSLOT_AUTOINPUT); /* for auto-gen multi-slots */ + + outputSlot=AQHREACT_OutputSlot_new(); + AQHREACT_OutputSlot_SetName(outputSlot, "output"); + AQHREACT_OutputSlot_SetIdForUnit(outputSlot, AQHOMEREACT_UNIT_OR_OUTSLOT_RESULT); + AQHREACT_OutputSlot_SetEmittedDataType(outputSlot, AQHREACT_DATAOBJECTTYPE_DOUBLE); + AQHREACT_Unit_AddOutputSlot(unit, outputSlot); + + inputSlot=AQHREACT_InputSlot_new(); + AQHREACT_InputSlot_SetName(inputSlot, "input"); + AQHREACT_InputSlot_SetIdForUnit(inputSlot, AQHOMEREACT_UNIT_OR_INSLOT_INPUT); + AQHREACT_InputSlot_SetAcceptedDataType(inputSlot, AQHREACT_DATAOBJECTTYPE_DOUBLE); + AQHREACT_InputSlot_AddFlags(inputSlot, AQHREACT_UNIT_FLAGS_MULTI); + AQHREACT_Unit_AddInputSlot(unit, inputSlot); + + return unit; +} + + + +int _cbProcessFn(AQHREACT_UNIT *unit) +{ + if (unit && AQHREACT_Unit_InputHasChanged(unit)) { + AQHREACT_INPUT_SLOT_LIST *inputSlotList; + + inputSlotList=AQHREACT_Unit_GetInputSlots(unit); + if (inputSlotList) { + int result; + + result=_sampleInputSlots(inputSlotList); + AQHREACT_Unit_ClearChangeFlagsInUnitAndInputSlots(unit); + AQHREACT_Unit_OutputDoubleData(unit, AQHOMEREACT_UNIT_OR_OUTSLOT_RESULT, result?1.0:0.0); + } + } + + return 0; +} + + + +int _sampleInputSlots(const AQHREACT_INPUT_SLOT_LIST *inputSlotList) +{ + const AQHREACT_INPUT_SLOT *inputSlot; + int result=0; + + inputSlot=AQHREACT_InputSlot_List_First(inputSlotList); + while(inputSlot) { + const char *slotName; + const AQHREACT_DATAOBJECT *dataObject; + + slotName=AQHREACT_InputSlot_GetName(inputSlot); + dataObject=AQHREACT_InputSlot_GetCurrentDataObject(inputSlot); + 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", slotName?slotName:""); + } + } + else { + DBG_ERROR(NULL, "Data at input slot \"%s\" has not current data, ignoring", slotName?slotName:""); + } + + inputSlot=AQHREACT_InputSlot_List_Next(inputSlot); + } + + return result; +} + + + + + + + diff --git a/apps/aqhome-react/units/u_or.h b/apps/aqhome-react/units/u_or.h new file mode 100644 index 0000000..bd7b1aa --- /dev/null +++ b/apps/aqhome-react/units/u_or.h @@ -0,0 +1,23 @@ +/**************************************************************************** + * 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_OR_H +#define AQHOMEREACT_U_OR_H + + +#include "aqhome-react/aqhome_react.h" +#include "aqhome-react/types/unit.h" + + +AQHREACT_UNIT *AqHomeReact_UnitOr_new(AQHOME_REACT *aqh); + + + +#endif + + diff --git a/apps/aqhome-react/units/u_valuefilter.c b/apps/aqhome-react/units/u_valuefilter.c new file mode 100644 index 0000000..160311a --- /dev/null +++ b/apps/aqhome-react/units/u_valuefilter.c @@ -0,0 +1,96 @@ +/**************************************************************************** + * 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_valuefilter.h" + +#include + + + +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ + +#define AQHOMEREACT_UNIT_VALUEFILTER_INSLOT_VALUE 0 + +#define AQHOMEREACT_UNIT_VALUEFILTER_OUTSLOT_RESULT 0 + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static void _cbInputData(AQHREACT_UNIT *unit, int slotIdForUnit, const AQHREACT_DATAOBJECT *dataObject); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +AQHREACT_UNIT *AqHomeReact_UnitValueFilter_new(AQHOME_REACT *aqh) +{ + AQHREACT_UNIT *unit; + AQHREACT_OUTPUT_SLOT *outputSlot; + AQHREACT_INPUT_SLOT *inputSlot; + AQHREACT_PARAM *param; + + unit=AQHREACT_Unit_new(); + AQHREACT_Unit_SetName(unit, "valuefilter"); + AQHREACT_Unit_SetDescription(unit, "Filter values received from unit VarChanges by value path"); + AQHREACT_Unit_SetInputDataFn(unit, _cbInputData); + + outputSlot=AQHREACT_OutputSlot_new(); + AQHREACT_OutputSlot_SetName(outputSlot, "output"); + AQHREACT_OutputSlot_SetIdForUnit(outputSlot, AQHOMEREACT_UNIT_VALUEFILTER_OUTSLOT_RESULT); + AQHREACT_OutputSlot_SetEmittedDataType(outputSlot, AQHREACT_DATAOBJECTTYPE_DOUBLE); + AQHREACT_Unit_AddOutputSlot(unit, outputSlot); + + inputSlot=AQHREACT_InputSlot_new(); + AQHREACT_InputSlot_SetName(inputSlot, "input"); + AQHREACT_InputSlot_SetIdForUnit(inputSlot, AQHOMEREACT_UNIT_VALUEFILTER_INSLOT_VALUE); + AQHREACT_InputSlot_SetAcceptedDataType(inputSlot, AQHREACT_DATAOBJECTTYPE_DOUBLE); + AQHREACT_Unit_AddInputSlot(unit, inputSlot); + + param=AQHREACT_Param_new(); + AQHREACT_Param_SetName(param, AQHOMEREACT_UNIT_VALUEFILTER_PARAM_VALUENAME); + AQHREACT_Param_SetDataType(param, AQHREACT_DATAOBJECTTYPE_STRING); + AQHREACT_Unit_AddParam(unit, param); + + return unit; +} + + + +void _cbInputData(AQHREACT_UNIT *unit, int slotIdForUnit, const AQHREACT_DATAOBJECT *dataObject) +{ + if (unit && dataObject && slotIdForUnit==AQHOMEREACT_UNIT_VALUEFILTER_INSLOT_VALUE) { + const char *sSystemValueId; + + sSystemValueId=AQHREACT_DataObject_GetSystemValueId(dataObject); + if (sSystemValueId && *sSystemValueId) { + const char *sFilter; + + sFilter=AQHREACT_Unit_GetParamValueString(unit, AQHOMEREACT_UNIT_VALUEFILTER_PARAM_VALUENAME, NULL); + if (sFilter && *sFilter && strcasecmp(sSystemValueId, sFilter)==0) + AQHREACT_Unit_OutputData(unit, AQHOMEREACT_UNIT_VALUEFILTER_OUTSLOT_RESULT, dataObject); + } + } +} + + + + diff --git a/apps/aqhome-react/units/u_valuefilter.h b/apps/aqhome-react/units/u_valuefilter.h new file mode 100644 index 0000000..183ac53 --- /dev/null +++ b/apps/aqhome-react/units/u_valuefilter.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_VALUEFILTER_H +#define AQHOMEREACT_U_VALUEFILTER_H + + +#include "aqhome-react/aqhome_react.h" +#include "aqhome-react/types/unit.h" + + +#define AQHOMEREACT_UNIT_VALUEFILTER_PARAM_VALUENAME "valueName" + + +AQHREACT_UNIT *AqHomeReact_UnitValueFilter_new(AQHOME_REACT *aqh); + + + +#endif + + diff --git a/apps/aqhome-react/units/u_varchanges.c b/apps/aqhome-react/units/u_varchanges.c new file mode 100644 index 0000000..497276e --- /dev/null +++ b/apps/aqhome-react/units/u_varchanges.c @@ -0,0 +1,81 @@ +/**************************************************************************** + * 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_varchanges.h" + +#include + + + +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ + +#define AQHOMEREACT_UNIT_VARCHANGES_INSLOT_CHANGE 0 + +#define AQHOMEREACT_UNIT_VARCHANGES_OUTSLOT_RESULT 0 + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static void _cbInputData(AQHREACT_UNIT *unit, int slotIdForUnit, const AQHREACT_DATAOBJECT *dataObject); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +AQHREACT_UNIT *AqHomeReact_UnitVarChanges_new(AQHOME_REACT *aqh) +{ + AQHREACT_UNIT *unit; + AQHREACT_OUTPUT_SLOT *outputSlot; + AQHREACT_INPUT_SLOT *inputSlot; + + unit=AQHREACT_Unit_new(); + AQHREACT_Unit_SetName(unit, "varchanges"); + AQHREACT_Unit_SetDescription(unit, "Propagates changes of values on the data server"); + AQHREACT_Unit_SetInputDataFn(unit, _cbInputData); + + outputSlot=AQHREACT_OutputSlot_new(); + AQHREACT_OutputSlot_SetName(outputSlot, "output"); + AQHREACT_OutputSlot_SetIdForUnit(outputSlot, AQHOMEREACT_UNIT_VARCHANGES_OUTSLOT_RESULT); + AQHREACT_OutputSlot_SetEmittedDataType(outputSlot, AQHREACT_DATAOBJECTTYPE_DOUBLE); + AQHREACT_Unit_AddOutputSlot(unit, outputSlot); + + inputSlot=AQHREACT_InputSlot_new(); + AQHREACT_InputSlot_SetName(inputSlot, "input"); + AQHREACT_InputSlot_SetIdForUnit(inputSlot, AQHOMEREACT_UNIT_VARCHANGES_INSLOT_CHANGE); + AQHREACT_InputSlot_SetAcceptedDataType(inputSlot, AQHREACT_DATAOBJECTTYPE_DOUBLE); + AQHREACT_Unit_AddInputSlot(unit, inputSlot); + + return unit; +} + + + + +void _cbInputData(AQHREACT_UNIT *unit, int slotIdForUnit, const AQHREACT_DATAOBJECT *dataObject) +{ + if (unit && dataObject && slotIdForUnit==0) + AQHREACT_Unit_OutputData(unit, AQHOMEREACT_UNIT_VARCHANGES_OUTSLOT_RESULT, dataObject); +} + + + + diff --git a/apps/aqhome-react/units/u_varchanges.h b/apps/aqhome-react/units/u_varchanges.h new file mode 100644 index 0000000..3897593 --- /dev/null +++ b/apps/aqhome-react/units/u_varchanges.h @@ -0,0 +1,23 @@ +/**************************************************************************** + * 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_VARCHANGES_H +#define AQHOMEREACT_U_VARCHANGES_H + + +#include "aqhome-react/aqhome_react.h" +#include "aqhome-react/types/unit.h" + + +AQHREACT_UNIT *AqHomeReact_UnitVarChanges_new(AQHOME_REACT *aqh); + + + +#endif + +