From 5fdb33c1927b7a144ac29d705000c1763b288a11 Mon Sep 17 00:00:00 2001 From: Martin Preuss Date: Mon, 14 Aug 2023 02:00:37 +0200 Subject: [PATCH] started working on aqhome-data. this will be the data daemon storing datapoints, accessable via IPC. --- apps/0BUILD | 1 + apps/aqhome-data/0BUILD | 76 ++++++ apps/aqhome-data/aqhome_data.c | 117 +++++++++ apps/aqhome-data/aqhome_data.h | 42 ++++ apps/aqhome-data/aqhome_data_p.h | 43 ++++ apps/aqhome-data/fini.c | 79 ++++++ apps/aqhome-data/fini.h | 23 ++ apps/aqhome-data/init.c | 330 ++++++++++++++++++++++++++ apps/aqhome-data/init.h | 23 ++ apps/aqhome-data/loop.c | 181 ++++++++++++++ apps/aqhome-data/loop.h | 26 ++ apps/aqhome-data/main.c | 243 +++++++++++++++++++ aqhome/data/datapoint.t2d | 18 +- aqhome/ipc/0BUILD | 2 + aqhome/ipc/data/0BUILD | 4 + aqhome/ipc/data/ipc_data.h | 20 ++ aqhome/ipc/data/msg_data_datapoints.c | 218 +++++++++++++++++ aqhome/ipc/data/msg_data_datapoints.h | 54 +++++ aqhome/ipc/data/msg_data_values.c | 251 ++++++++++++++++++++ aqhome/ipc/data/msg_data_values.h | 49 ++++ aqhome/ipc/msg_ipc_qwords.c | 148 ++++++++++++ aqhome/ipc/msg_ipc_qwords.h | 34 +++ aqhome/ipc/msg_ipc_result.c | 16 +- aqhome/ipc/msg_ipc_result.h | 4 +- 24 files changed, 1976 insertions(+), 26 deletions(-) create mode 100644 apps/aqhome-data/0BUILD create mode 100644 apps/aqhome-data/aqhome_data.c create mode 100644 apps/aqhome-data/aqhome_data.h create mode 100644 apps/aqhome-data/aqhome_data_p.h create mode 100644 apps/aqhome-data/fini.c create mode 100644 apps/aqhome-data/fini.h create mode 100644 apps/aqhome-data/init.c create mode 100644 apps/aqhome-data/init.h create mode 100644 apps/aqhome-data/loop.c create mode 100644 apps/aqhome-data/loop.h create mode 100644 apps/aqhome-data/main.c create mode 100644 aqhome/ipc/data/msg_data_datapoints.c create mode 100644 aqhome/ipc/data/msg_data_datapoints.h create mode 100644 aqhome/ipc/data/msg_data_values.c create mode 100644 aqhome/ipc/data/msg_data_values.h create mode 100644 aqhome/ipc/msg_ipc_qwords.c create mode 100644 aqhome/ipc/msg_ipc_qwords.h diff --git a/apps/0BUILD b/apps/0BUILD index 9a167cd..9aeb2dc 100644 --- a/apps/0BUILD +++ b/apps/0BUILD @@ -7,6 +7,7 @@ aqhome-tool aqhome-mqttlog aqhome-storage + aqhome-data diff --git a/apps/aqhome-data/0BUILD b/apps/aqhome-data/0BUILD new file mode 100644 index 0000000..4c92afa --- /dev/null +++ b/apps/aqhome-data/0BUILD @@ -0,0 +1,76 @@ + + + + + + + + $(gwenhywfar_cflags) + -I$(topsrcdir) + -I$(topbuilddir) + -I$(builddir) + -I$(srcdir) + + + + --include=$(builddir) + --include=$(srcdir) + + + $(visibility_cflags) + + + + + + + + + + + + + + + + + + aqhome_data.h + aqhome_data_p.h + fini.h + init.h + loop.h + + + + $(local/typefiles) + + aqhome_data.c + fini.c + init.c + loop.c + main.c + + + + aqhome + + + + $(gwenhywfar_libs) + + + + + + + + + + + + + + + + diff --git a/apps/aqhome-data/aqhome_data.c b/apps/aqhome-data/aqhome_data.c new file mode 100644 index 0000000..75afd47 --- /dev/null +++ b/apps/aqhome-data/aqhome_data.c @@ -0,0 +1,117 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2023 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_data_p.h" + +#include +#include + + + + + +AQHOME_DATA *AqHomeData_new() +{ + AQHOME_DATA *aqh; + + GWEN_NEW_OBJECT(AQHOME_DATA, aqh); + aqh->storageMutex=GWEN_Mutex_new(); + + return aqh; +} + + + +void AqHomeData_free(AQHOME_DATA *aqh) +{ + if (aqh) { + GWEN_Mutex_free(aqh->storageMutex); + + GWEN_MsgEndpoint_free(aqh->ipcdEndpoint); + GWEN_DB_Group_free(aqh->dbArgs); + AQH_Storage_free(aqh->storage); + free(aqh->pidFile); + + GWEN_FREE_OBJECT(aqh); + } +} + + + +GWEN_MSG_ENDPOINT *AqHomeData_GetIpcdEndpoint(const AQHOME_DATA *aqh) +{ + return aqh?(aqh->ipcdEndpoint):NULL; +} + + + +GWEN_DB_NODE *AqHomeData_GetDbArgs(const AQHOME_DATA *aqh) +{ + return aqh?(aqh->dbArgs):NULL; +} + + + +AQH_STORAGE *AqHomeData_GetStorage(const AQHOME_DATA *aqh) +{ + return aqh?(aqh->storage):NULL; +} + + + +const char *AqHomeData_GetPidFile(const AQHOME_DATA *aqh) +{ + return aqh?aqh->pidFile:NULL; +} + + + +int AqHomeData_GetTimeout(const AQHOME_DATA *aqh) +{ + return aqh?aqh->timeout:0; +} + + + +int AqHomeData_LockStorage(AQHOME_DATA *aqh) +{ + int rv; + + rv=GWEN_Mutex_Lock(aqh->storageMutex); + if (rv<0) { + DBG_ERROR(AQH_LOGDOMAIN, "Error obtaining lock on storage mutex"); + return rv; + } + return rv; +} + + + +int AqHomeData_UnlockStorage(AQHOME_DATA *aqh) +{ + int rv; + + rv=GWEN_Mutex_Unlock(aqh->storageMutex); + if (rv<0) { + DBG_ERROR(AQH_LOGDOMAIN, "Error releasing lock on storage mutex"); + return rv; + } + return rv; +} + + + + + + + diff --git a/apps/aqhome-data/aqhome_data.h b/apps/aqhome-data/aqhome_data.h new file mode 100644 index 0000000..3c89906 --- /dev/null +++ b/apps/aqhome-data/aqhome_data.h @@ -0,0 +1,42 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2023 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 AQHOME_DATA_H +#define AQHOME_DATA_H + + +#include "aqhome/data/storage.h" + +#include + + + +typedef struct AQHOME_DATA AQHOME_DATA; + + +AQHOME_DATA *AqHomeData_new(); +void AqHomeData_free(AQHOME_DATA *aqh); + +GWEN_MSG_ENDPOINT *AqHomeData_GetIpcdEndpoint(const AQHOME_DATA *aqh); + +GWEN_DB_NODE *AqHomeData_GetDbArgs(const AQHOME_DATA *aqh); + +AQH_STORAGE *AqHomeData_GetStorage(const AQHOME_DATA *aqh); + +const char *AqHomeData_GetPidFile(const AQHOME_DATA *aqh); + +int AqHomeData_GetTimeout(const AQHOME_DATA *aqh); + +int AqHomeData_LockStorage(AQHOME_DATA *aqh); +int AqHomeData_UnlockStorage(AQHOME_DATA *aqh); + + + + +#endif + diff --git a/apps/aqhome-data/aqhome_data_p.h b/apps/aqhome-data/aqhome_data_p.h new file mode 100644 index 0000000..d2b24bf --- /dev/null +++ b/apps/aqhome-data/aqhome_data_p.h @@ -0,0 +1,43 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2023 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 AQHOME_DATA_P_H +#define AQHOME_DATA_P_H + + +#include "./aqhome_data.h" + +#include + + +#define AQHOME_DATA_DEFAULT_PIDFILE "/var/run/aqhome-data.pid" +#define AQHOME_DATA_DEFAULT_DATADIR "/var/lib/aqhome-data/data" +#define AQHOME_DATA_DEFAULT_STATEFILE "/var/lib/aqhome-data/statefile" + +#define AQHOME_DATA_DEFAULT_IPC_PORT 45456 + + + +struct AQHOME_DATA { + GWEN_MSG_ENDPOINT *ipcdEndpoint; + + GWEN_DB_NODE *dbArgs; + + AQH_STORAGE *storage; + + char *pidFile; + + int timeout; /* timeout for run e.g. inside valgrind */ + + GWEN_MUTEX *storageMutex; + +}; + + +#endif + diff --git a/apps/aqhome-data/fini.c b/apps/aqhome-data/fini.c new file mode 100644 index 0000000..764a867 --- /dev/null +++ b/apps/aqhome-data/fini.c @@ -0,0 +1,79 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2023 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_data_p.h" + +#include +#include +#include +#include + +#include + + + +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ + + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static void _disconnectTree(GWEN_MSG_ENDPOINT *ep); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +void AqHomeData_Fini(AQHOME_DATA *aqh) +{ + if (aqh) { + if (aqh->ipcdEndpoint) { + _disconnectTree(aqh->ipcdEndpoint); + GWEN_MsgEndpoint_Disconnect(aqh->ipcdEndpoint); + } + GWEN_MsgEndpoint_free(aqh->ipcdEndpoint); + + if (aqh->pidFile) + remove(aqh->pidFile); + } +} + + + +void _disconnectTree(GWEN_MSG_ENDPOINT *ep) +{ + GWEN_MSG_ENDPOINT *epChild; + + epChild=GWEN_MsgEndpoint_Tree2_GetFirstChild(ep); + while(epChild) { + _disconnectTree(epChild); + epChild=GWEN_MsgEndpoint_Tree2_GetNext(epChild); + } /* while */ + + GWEN_MsgEndpoint_Disconnect(ep); +} + + + + diff --git a/apps/aqhome-data/fini.h b/apps/aqhome-data/fini.h new file mode 100644 index 0000000..93564fd --- /dev/null +++ b/apps/aqhome-data/fini.h @@ -0,0 +1,23 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2023 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 AQHOME_DATA_FINI_H +#define AQHOME_DATA_FINI_H + + +#include "./aqhome_data.h" + + + +void AqHomeData_Fini(AQHOME_DATA *aqh); + + + +#endif + + diff --git a/apps/aqhome-data/init.c b/apps/aqhome-data/init.c new file mode 100644 index 0000000..f22c27c --- /dev/null +++ b/apps/aqhome-data/init.c @@ -0,0 +1,330 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2023 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_data_p.h" + +#include "aqhome/ipc/endpoint_ipc.h" + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_TYPES_H +# include +#endif + +#ifdef HAVE_SYS_STAT_H +# include +#endif + +#include +#include +#include +#include +#include +#include + + + +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ + +//#define I18N(msg) msg +#define I18S(msg) msg + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static int _setupStorage(AQHOME_DATA *aqh, GWEN_DB_NODE *dbArgs); + +static void _setupIpc(AQHOME_DATA *aqh, GWEN_DB_NODE *dbArgs); + +static GWEN_MSG_ENDPOINT *_acceptIpcFn(GWEN_MSG_ENDPOINT *ep, GWEN_SOCKET *sk, const GWEN_INETADDRESS *addr, void *data); + +static int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs); +static int _createPidFile(const char *pidFilename); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +int AqHomeData_Init(AQHOME_DATA *aqh, int argc, char **argv) +{ + GWEN_DB_NODE *dbArgs; + int rv; + 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->dbArgs=dbArgs; + + aqh->timeout=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 0); + + s=GWEN_DB_GetCharValue(dbArgs, "pidfile", 0, AQHOME_DATA_DEFAULT_PIDFILE); + if (s && *s) { + free(aqh->pidFile); + aqh->pidFile=strdup(s); + rv=_createPidFile(s); + if (rv<0) { + DBG_ERROR(NULL, "Error creating PID file (%d)", rv); + return rv; + } + } + + rv=_setupStorage(aqh, dbArgs); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + return rv; + } + + _setupIpc(aqh, dbArgs); + + return 0; +} + + + +int _setupStorage(AQHOME_DATA *aqh, GWEN_DB_NODE *dbArgs) +{ + const char *dataFolder; + const char *stateFile; + + dataFolder=GWEN_DB_GetCharValue(dbArgs, "dataFolder", 0, AQHOME_DATA_DEFAULT_DATADIR); + stateFile=GWEN_DB_GetCharValue(dbArgs, "stateFile", 0, NULL); + if (stateFile && *stateFile) { + AQH_STORAGE *sto; + int rv; + + sto=AQH_Storage_new(); + AQH_Storage_SetStateFile(sto, stateFile); + + AQH_Storage_SetDataFileFolder(sto, (dataFolder && *dataFolder)?dataFolder:NULL); + + rv=AQH_Storage_Init(sto); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + AQH_Storage_free(sto); + return rv; + } + aqh->storage=sto; + } + else { + DBG_ERROR(NULL, "No state file given"); + return GWEN_ERROR_GENERIC; + } + return 0; +} + + + +void _setupIpc(AQHOME_DATA *aqh, GWEN_DB_NODE *dbArgs) +{ + const char *tcpAddress; + int tcpPort; + + tcpAddress=GWEN_DB_GetCharValue(dbArgs, "tcpAddress", 0, NULL); + tcpPort=GWEN_DB_GetIntValue(dbArgs, "tcpPort", 0, AQHOME_DATA_DEFAULT_IPC_PORT); + + if (tcpAddress && *tcpAddress && tcpPort) { + GWEN_MSG_ENDPOINT *ep; + + ep=GWEN_TcpdEndpoint_new(tcpAddress, tcpPort, NULL, 0); + GWEN_TcpdEndpoint_SetAcceptFn(ep, _acceptIpcFn, aqh); + + GWEN_MsgEndpoint_Tree2_AddChild(aqh->ipcdEndpoint, ep); + aqh->ipcdEndpoint=ep; + } +} + + + +GWEN_MSG_ENDPOINT *_acceptIpcFn(GWEN_MSG_ENDPOINT *ep, + GWEN_SOCKET *sk, + const GWEN_INETADDRESS *addr, + GWEN_UNUSED void *data) +{ +/* AQHOME_DATA *aqh; + * + * aqh=(AQHOME_DATA*) data; + */ + DBG_INFO(NULL, "Incoming IPC connection"); + return AQH_IpcEndpoint_CreateIpcTcpServiceForSocket(sk, NULL, 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 _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 */ + "tcpAddress", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "t", /* short option */ + "tcpaddress", /* long option */ + I18S("Specify the TCP address to listen on (disabled if missing)"), + I18S("Specify the TCP address to listen on (disabled if missing)") + }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Int, /* type */ + "tcpPort", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "P", /* short option */ + "tcpport", /* long option */ + I18S("Specify the TCP port to listen on"), + I18S("Specify the TCP port to listen on") + }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Char, /* type */ + "datafolder", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + NULL, /* short option */ + "datafolder", /* long option */ + I18S("Folder where data files are stored"), + I18S("Folder where data files are stored") + }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_Char, /* type */ + "statefile", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "S", /* short option */ + "statefile", /* long option */ + I18S("File where rooms, devices and values etc. are stored"), + I18S("File where rooms, devices and values etc. are stored") + }, + { + 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_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-data/init.h b/apps/aqhome-data/init.h new file mode 100644 index 0000000..082bbc2 --- /dev/null +++ b/apps/aqhome-data/init.h @@ -0,0 +1,23 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2023 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 AQHOME_DATA_INIT_H +#define AQHOME_DATA_INIT_H + + +#include "./aqhome_data.h" + + + +int AqHomeData_Init(AQHOME_DATA *aqh, int argc, char **argv); + + + +#endif + + diff --git a/apps/aqhome-data/loop.c b/apps/aqhome-data/loop.c new file mode 100644 index 0000000..d4cf192 --- /dev/null +++ b/apps/aqhome-data/loop.c @@ -0,0 +1,181 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2023 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_data_p.h" +#include "aqhome/ipc/data/ipc_data.h" + +#include +#include +#include +#include +#include + + + +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static void _readAndHandleIpcMessages(AQHOME_DATA *aqh); +static void _handleIpcEndpoint(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep); +static void _handleIpcMsg(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg); +static void _handleGetValues(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg); +static void _handleAddValues(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg); +static void _handleEditValues(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg); +static void _handleAddDataPoints(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg); +static void _handleGetDataPoints(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg); +static void _handleGetLastDataPoint(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +void AqHomeData_Loop(AQHOME_DATA *aqh, int timeoutInMsecs) +{ + if (aqh) { + GWEN_MsgEndpoint_ChildrenIoLoop(aqh->ipcdEndpoint, timeoutInMsecs); + _readAndHandleIpcMessages(aqh); + } +} + + + +int AqHomeData_WriteStorageIfChanged(AQHOME_DATA *aqh) +{ + if (AQH_Storage_GetRuntimeFlags(aqh->storage) & AQH_STORAGE_RTFLAGS_MODIFIED) { + int rv; + + DBG_INFO(NULL, "Storage modified, writing statefile"); + rv=AqHomeData_LockStorage(aqh); + if (rv<0) { + DBG_INFO(NULL, "Error locking storage (%d)", rv); + return rv; + } + rv=AQH_Storage_WriteState(aqh->storage); + if (rv<0) { + DBG_INFO(NULL, "Error writing state file (%d)", rv); + AqHomeData_UnlockStorage(aqh); + return rv; + } + + rv=AqHomeData_UnlockStorage(aqh); + if (rv<0) { + DBG_INFO(NULL, "Error unlocking storage (%d)", rv); + return rv; + } + } + + return 0; +} + + + +void _readAndHandleIpcMessages(AQHOME_DATA *aqh) +{ + if (aqh->ipcdEndpoint) { + GWEN_MSG_ENDPOINT *ep; + + ep=GWEN_MsgEndpoint_Tree2_GetFirstChild(aqh->ipcdEndpoint); + while(ep) { + _handleIpcEndpoint(aqh, ep); + ep=GWEN_MsgEndpoint_Tree2_GetNext(ep); + } + } +} + + + +void _handleIpcEndpoint(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep) +{ + GWEN_MSG *msg; + + while( (msg=GWEN_MsgEndpoint_TakeFirstReceivedMessage(ep)) ) { + _handleIpcMsg(aqh, ep, msg); + GWEN_Msg_free(msg); + } +} + + + +void _handleIpcMsg(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg) +{ + uint16_t code; + + /* exec IPC message */ + code=GWEN_IpcMsg_GetCode(msg); + DBG_ERROR(AQH_LOGDOMAIN, "Received IPC packet"); + switch(code) { + case AQH_MSGTYPE_IPC_DATA_GETVALUES_REQ: _handleGetValues(aqh, ep, msg); break; + case AQH_MSGTYPE_IPC_DATA_ADDVALUES_REQ: _handleAddValues(aqh, ep, msg); break; + case AQH_MSGTYPE_IPC_DATA_EDITVALUE_REQ: _handleEditValues(aqh, ep, msg); break; + case AQH_MSGTYPE_IPC_DATA_ADDDATAPOINTS_REQ: _handleAddDataPoints(aqh, ep, msg); break; + case AQH_MSGTYPE_IPC_DATA_GETDATAPOINTS_REQ: _handleGetDataPoints(aqh, ep, msg); break; + case AQH_MSGTYPE_IPC_DATA_GETLASTDATAPOINT_REQ: _handleGetLastDataPoint(aqh, ep, msg); break; + default: break; + } +} + + + +void _handleGetValues(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg) +{ +} + + + +void _handleAddValues(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg) +{ +} + + + +void _handleEditValues(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg) +{ +} + + + +void _handleAddDataPoints(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg) +{ +} + + + +void _handleGetDataPoints(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg) +{ +} + + + +void _handleGetLastDataPoint(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg) +{ +} + + + + + + + diff --git a/apps/aqhome-data/loop.h b/apps/aqhome-data/loop.h new file mode 100644 index 0000000..0da8397 --- /dev/null +++ b/apps/aqhome-data/loop.h @@ -0,0 +1,26 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2023 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 AQHOME_DATA_LOOP_H +#define AQHOME_DATA_LOOP_H + + +#include "./aqhome_data.h" + + +void AqHomeData_Loop(AQHOME_DATA *aqh, int timeoutInMsecs); + +int AqHomeData_WriteStorageIfChanged(AQHOME_DATA *aqh); + + +#endif + + + + + diff --git a/apps/aqhome-data/main.c b/apps/aqhome-data/main.c new file mode 100644 index 0000000..9087ba6 --- /dev/null +++ b/apps/aqhome-data/main.c @@ -0,0 +1,243 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2023 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 +#include + +#include "./aqhome_data.h" +#include "./init.h" +#include "./fini.h" +#include "./loop.h" + +#include +#include +#include +#include + +#ifdef HAVE_SIGNAL_H +# include +#endif + + + +//#define WRITE_INTERVAL_IN_SECS (5*60) + +#define WRITE_INTERVAL_IN_SECS (60) +#define PING_INTERVAL 120 + + + +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ + + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +#ifdef HAVE_SIGNAL_H +static int _setSignalHandlers(void); +static int _setupSigAction(struct sigaction *sa, int sig); +static void _signalHandler(int s); +#endif + +static void _runService(AQHOME_DATA *aqh); +static void _writeCurrentState(AQHOME_DATA *aqh); + + + +/* ------------------------------------------------------------------------------------------------ + * static vars + * ------------------------------------------------------------------------------------------------ + */ + +#ifdef HAVE_SIGNAL_H +static struct sigaction saINT,saTERM, saHUP, saTSTP, saCONT; +#endif + +static int stopService=0; + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +int main(int argc, char **argv) +{ + int rv; + AQHOME_DATA *aqh; + GWEN_GUI *gui; + + rv=GWEN_Init(); + if (rv) { + fprintf(stderr, "ERROR: Unable to init Gwen.\n"); + return 2; + } + + GWEN_Logger_Open(0, "aqhome-data", 0, GWEN_LoggerType_Console, GWEN_LoggerFacility_User); + //GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Warning); + GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Info); + + rv=_setSignalHandlers(); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + return rv; + } + + rv=AQH_Init(); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + return 2; + } + + gui=GWEN_Gui_CGui_new(); + GWEN_Gui_SetGui(gui); + + aqh=AqHomeData_new(); + rv=AqHomeData_Init(aqh, argc, argv); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + return 2; + } + + _runService(aqh); + + AqHomeData_Fini(aqh); + AqHomeData_free(aqh); + + GWEN_Gui_SetGui(NULL); + GWEN_Gui_free(gui); + + return 0; +} + + + +void _runService(AQHOME_DATA *aqh) +{ + time_t timeStart; + time_t timeLastWrite; + int timeout; + + timeout=AqHomeData_GetTimeout(aqh); + timeStart=time(NULL); + timeLastWrite=time(NULL); + + while(!stopService) { + time_t now; + + DBG_DEBUG(NULL, "Next loop"); + AqHomeData_Loop(aqh, 2000); + + now=time(NULL); + + if (((int)difftime(now, timeLastWrite))>WRITE_INTERVAL_IN_SECS) { + DBG_INFO(NULL, "Write time"); + _writeCurrentState(aqh); + timeLastWrite=now; + } + + if (timeout && ((int)difftime(now, timeStart))>timeout) { + DBG_INFO(NULL, "Timeout"); + _writeCurrentState(aqh); + break; + } + } /* while */ +} + + + +void _writeCurrentState(AQHOME_DATA *aqh) +{ + int rv; + + rv=AqHomeData_WriteStorageIfChanged(aqh); + if (rv<0) { + DBG_ERROR(NULL, "ATTENTION: Could not write storage statefile (%d)", rv); + } +} + + + + +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/aqhome/data/datapoint.t2d b/aqhome/data/datapoint.t2d index 00de5b7..d80614b 100644 --- a/aqhome/data/datapoint.t2d +++ b/aqhome/data/datapoint.t2d @@ -18,8 +18,6 @@
aqhome/api.h
-
aqdatabase/aqdb_value.h
-
gwenhywfar/timestamp.h
@@ -27,21 +25,7 @@ - - 0 - 0 - with_getbymember - public - - - - 0 - 0 - - public - - - + public with_sortbymember 0 diff --git a/aqhome/ipc/0BUILD b/aqhome/ipc/0BUILD index 226d2b2..0395db3 100644 --- a/aqhome/ipc/0BUILD +++ b/aqhome/ipc/0BUILD @@ -47,6 +47,7 @@ endpoint_ipc.h msg_ipc_result.h + msg_ipc_qwords.h @@ -60,6 +61,7 @@ endpoint_ipc.c msg_ipc_result.c + msg_ipc_qwords.c diff --git a/aqhome/ipc/data/0BUILD b/aqhome/ipc/data/0BUILD index f601838..b3baaad 100644 --- a/aqhome/ipc/data/0BUILD +++ b/aqhome/ipc/data/0BUILD @@ -48,6 +48,8 @@ ipc_data.h msg_data_getvalues_rsp.c msg_data_getvalues_rsp.h + msg_data_values.h + msg_data_datapoints.h @@ -61,6 +63,8 @@ ipc_data.c msg_data_getvalues_req.c msg_data_getvalues_rsp.c + msg_data_values.c + msg_data_datapoints.c diff --git a/aqhome/ipc/data/ipc_data.h b/aqhome/ipc/data/ipc_data.h index fb7202d..4616ef5 100644 --- a/aqhome/ipc/data/ipc_data.h +++ b/aqhome/ipc/data/ipc_data.h @@ -19,6 +19,26 @@ #define AQH_IPC_PROTOCOL_DATA_VERSION 1 +#define AQH_MSGTYPE_IPC_DATA_RESULT 0x001 /* AQH_ResultIpcMsg */ + +#define AQH_MSGTYPE_IPC_DATA_GETVALUES_REQ 0x100 /* AQH_QwordsIpcMsg */ +#define AQH_MSGTYPE_IPC_DATA_GETVALUES_RSP 0x200 /* AQH_ValuesDataIpcMsg */ + +#define AQH_MSGTYPE_IPC_DATA_ADDVALUES_REQ 0x300 /* AQH_ValuesDataIpcMsg */ +#define AQH_MSGTYPE_IPC_DATA_ADDVALUES_RSP 0x400 /* AQH_ResultIpcMsg */ + +#define AQH_MSGTYPE_IPC_DATA_EDITVALUE_REQ 0x500 /* AQH_ValuesDataIpcMsg */ +#define AQH_MSGTYPE_IPC_DATA_EDITVALUE_RSP 0x600 /* AQH_ResultIpcMsg */ + +#define AQH_MSGTYPE_IPC_DATA_ADDDATAPOINTS_REQ 0x700 /* AQH_DataPointsDataIpcMsg */ +#define AQH_MSGTYPE_IPC_DATA_ADDDATAPOINTS_RSP 0x800 /* AQH_ResultIpcMsg */ + +#define AQH_MSGTYPE_IPC_DATA_GETDATAPOINTS_REQ 0x900 /* AQH_DataPointsDataIpcMsg (1 pair: fromTime, toTime) */ +#define AQH_MSGTYPE_IPC_DATA_GETDATAPOINTS_RSP 0xa00 /* AQH_DataPointsDataIpcMsg */ + +#define AQH_MSGTYPE_IPC_DATA_GETLASTDATAPOINT_REQ 0xb00 /* AQH_DataPointsDataIpcMsg (0 datapoints) */ +#define AQH_MSGTYPE_IPC_DATA_GETLASTDATAPOINT_RSP 0xc00 /* AQH_DataPointsDataIpcMsg */ + diff --git a/aqhome/ipc/data/msg_data_datapoints.c b/aqhome/ipc/data/msg_data_datapoints.c new file mode 100644 index 0000000..b228e17 --- /dev/null +++ b/aqhome/ipc/data/msg_data_datapoints.c @@ -0,0 +1,218 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2023 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 +#include + +#include +#include + +#include +#include + +#include + + +#define AQH_MSGDATA_DATAPOINTS_OFFS_FLAGS 0 /* 4 bytes */ +#define AQH_MSGDATA_DATAPOINTS_OFFS_NUMVALUES 4 /* 4 bytes */ + +#define AQH_MSGDATA_DATAPOINTS_OFFS_VALUEID 8 /* 8 byte */ +#define AQH_MSGDATA_DATAPOINTS_OFFS_VALUENAME 16 /* 104 byte */ +# define AQH_MSGDATA_DATAPOINTS_SIZE_VALUENAME 104 +#define AQH_MSGDATA_DATAPOINTS_OFFS_VALUEUNITS 120 /* 16 byte */ +# define AQH_MSGDATA_DATAPOINTS_SIZE_VALUEUNITS 16 +#define AQH_MSGDATA_DATAPOINTS_OFFS_VALUES 136 + + + +#define AQH_MSGDATA_DATAPOINTS_MINSIZE (GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_OFFS_VALUES) + + +static void _writeQword(uint64_t i64, uint8_t *ptr); + + + + +GWEN_MSG *AQH_DataPointsDataIpcMsg_new(uint16_t code, uint32_t flags, + uint64_t valueId, const char *valueName, const char *units, + const uint64_t *i64Ptr, int numOfDataPoints) +{ + GWEN_MSG *msg; + uint8_t *ptr; + int payloadSize; + int i; + + payloadSize=AQH_MSGDATA_DATAPOINTS_OFFS_VALUES+(numOfDataPoints*16); + + msg=GWEN_IpcMsg_new(AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION, code, payloadSize, NULL); + ptr=GWEN_Msg_GetBuffer(msg)+GWEN_MSGIPC_OFFS_PAYLOAD; + *(ptr++)=flags & 0xff; + *(ptr++)=(flags>>8) & 0xff; + *(ptr++)=(flags>>16) & 0xff; + *(ptr++)=(flags>>24) & 0xff; + + *(ptr++)=numOfDataPoints & 0xff; + *(ptr++)=(numOfDataPoints>>8) & 0xff; + *(ptr++)=(numOfDataPoints>>16) & 0xff; + *(ptr++)=(numOfDataPoints>>24) & 0xff; + + _writeQword(valueId, ptr); + ptr+=8; + + + if (valueName) { + strncpy((char*) ptr, valueName, AQH_MSGDATA_DATAPOINTS_SIZE_VALUENAME-1); + ptr[AQH_MSGDATA_DATAPOINTS_SIZE_VALUENAME-1]=0; + } + else + memset(ptr, 0, AQH_MSGDATA_DATAPOINTS_SIZE_VALUENAME); + ptr+=AQH_MSGDATA_DATAPOINTS_SIZE_VALUENAME; + + if (units) { + strncpy((char*) ptr, units, AQH_MSGDATA_DATAPOINTS_SIZE_VALUEUNITS-1); + ptr[AQH_MSGDATA_DATAPOINTS_SIZE_VALUEUNITS-1]=0; + } + else + memset(ptr, 0, AQH_MSGDATA_DATAPOINTS_SIZE_VALUEUNITS); + ptr+=AQH_MSGDATA_DATAPOINTS_SIZE_VALUEUNITS; + + + for (i=0; i>8) & 0xff; + *(ptr++)=(i64>>16) & 0xff; + *(ptr++)=(i64>>24) & 0xff; + *(ptr++)=(i64>>32) & 0xff; + *(ptr++)=(i64>>40) & 0xff; + *(ptr++)=(i64>>48) & 0xff; + *(ptr++)=(i64>>56) & 0xff; +} + + + +uint32_t AQH_DataPointsDataIpcMsg_GetFlags(const GWEN_MSG *msg) +{ + return GWEN_Msg_GetUint32At(msg, GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_OFFS_FLAGS, 0); +} + + + +uint32_t AQH_DataPointsDataIpcMsg_GetNumValues(const GWEN_MSG *msg) +{ + return GWEN_Msg_GetUint32At(msg, GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_OFFS_NUMVALUES, 0); +} + + + +uint64_t AQH_DataPointsDataIpcMsg_GetValueId(const GWEN_MSG *msg) +{ + return GWEN_Msg_GetUint64At(msg, GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_OFFS_VALUEID, 0); +} + + + +const char *AQH_DataPointsDataIpcMsg_GetValueName(const GWEN_MSG *msg) +{ + if (GWEN_Msg_GetBytesInBuffer(msg)>=GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_MINSIZE) + return (const char*) (GWEN_Msg_GetConstBuffer(msg)+GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_OFFS_VALUENAME); + return NULL; +} + + + +const char *AQH_DataPointsDataIpcMsg_GetUnits(const GWEN_MSG *msg) +{ + if (GWEN_Msg_GetBytesInBuffer(msg)>=GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_MINSIZE) + return (const char*) (GWEN_Msg_GetConstBuffer(msg)+GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_OFFS_VALUEUNITS); + return NULL; +} + + + +const uint64_t *AQH_DataPointsDataIpcMsg_GetDataPoints(const GWEN_MSG *msg) +{ + if (GWEN_Msg_GetBytesInBuffer(msg)>=GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_MINSIZE) + return (const uint64_t*) (GWEN_Msg_GetConstBuffer(msg)+GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_OFFS_VALUES); + return NULL; + +} + + + +int AQH_DataPointsDataIpcMsg_IsValid(const GWEN_MSG *msg) +{ + int msgLen; + int numValues; + const uint8_t *ptr; + + msgLen=GWEN_Msg_GetBytesInBuffer(msg); + if (msgLen=AQH_MSGDATA_DATAPOINTS_MINSIZE) { + GWEN_Buffer_AppendArgs(dbuf, + "DATAPOINTS (code=%d, proto=%d, proto version=%d, flags=0x%08x, values=%d)\n", + GWEN_IpcMsg_GetCode(msg), + GWEN_IpcMsg_GetProtoId(msg), + GWEN_IpcMsg_GetProtoVersion(msg), + (unsigned int)AQH_DataPointsDataIpcMsg_GetFlags(msg), + AQH_DataPointsDataIpcMsg_GetNumValues(msg)); + } +} + + + + + + + diff --git a/aqhome/ipc/data/msg_data_datapoints.h b/aqhome/ipc/data/msg_data_datapoints.h new file mode 100644 index 0000000..7d896f4 --- /dev/null +++ b/aqhome/ipc/data/msg_data_datapoints.h @@ -0,0 +1,54 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2023 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 AQH_MSG_IPC_DATA_DATAPOINTS_H +#define AQH_MSG_IPC_DATA_DATAPOINTS_H + + +#include + +#include + +#include + + +#define AQH_MSGDATA_DATAPOINTS_FLAGS_LASTMSG 0x0001 + + + +AQHOME_API GWEN_MSG *AQH_DataPointsDataIpcMsg_new(uint16_t code, uint32_t flags, + uint64_t valueId, const char *valueName, const char *units, + const uint64_t *i64Ptr, int numOfDataPoints); + +AQHOME_API uint32_t AQH_DataPointsDataIpcMsg_GetFlags(const GWEN_MSG *msg); +AQHOME_API uint32_t AQH_DataPointsDataIpcMsg_GetNumValues(const GWEN_MSG *msg); +AQHOME_API uint64_t AQH_DataPointsDataIpcMsg_GetValueId(const GWEN_MSG *msg); +AQHOME_API const char *AQH_DataPointsDataIpcMsg_GetValueName(const GWEN_MSG *msg); +AQHOME_API const char *AQH_DataPointsDataIpcMsg_GetUnits(const GWEN_MSG *msg); +AQHOME_API const uint64_t *AQH_DataPointsDataIpcMsg_GetDataPoints(const GWEN_MSG *msg); + +AQHOME_API int AQH_DataPointsDataIpcMsg_IsValid(const GWEN_MSG *msg); +AQHOME_API void AQH_DataPointsDataIpcMsg_DumpToBuffer(const GWEN_MSG *msg, GWEN_BUFFER *dbuf, const char *sText); + + + + + + + + + + + + + + +#endif + + + diff --git a/aqhome/ipc/data/msg_data_values.c b/aqhome/ipc/data/msg_data_values.c new file mode 100644 index 0000000..7b16986 --- /dev/null +++ b/aqhome/ipc/data/msg_data_values.c @@ -0,0 +1,251 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2023 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 +#include + +#include +#include + +#include +#include + +#include + + +#define AQH_MSGDATA_VALUES_OFFS_FLAGS 0 /* 4 bytes */ +#define AQH_MSGDATA_VALUES_OFFS_NUMVALUES 4 /* 4 bytes */ + +#define AQH_MSGDATA_VALUES_OFFS_VALUES 8 /* 8 byte */ + + +#define AQH_MSGDATA_VALUES_VALUES_OFFS_ID 0 /* 8 byte */ +#define AQH_MSGDATA_VALUES_VALUES_OFFS_NAME 8 /* 104 byte */ +# define AQH_MSGDATA_VALUES_VALUES_SIZE_NAME 104 /* 104 byte */ +#define AQH_MSGDATA_VALUES_VALUES_OFFS_UNITS 112 /* 16 bytes */ +# define AQH_MSGDATA_VALUES_VALUES_SIZE_UNITS 16 +#define AQH_MSGDATA_VALUES_VALUES_SIZE 128 + + +#define AQH_MSGDATA_VALUES_MINSIZE (GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_VALUES_OFFS_VALUES) + + + + +static void _writeValue(const AQH_VALUE *value, uint8_t *ptr); + + + + +GWEN_MSG *AQH_ValuesDataIpcMsg_new(uint16_t code, uint32_t flags, const AQH_VALUE_LIST *valueList) +{ + GWEN_MSG *msg; + uint8_t *ptr; + int count; + int payloadSize; + + count=AQH_Value_List_GetCount(valueList); + payloadSize=AQH_MSGDATA_VALUES_OFFS_VALUES+(count*AQH_MSGDATA_VALUES_VALUES_SIZE); + + msg=GWEN_IpcMsg_new(AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION, code, payloadSize, NULL); + ptr=GWEN_Msg_GetBuffer(msg)+GWEN_MSGIPC_OFFS_PAYLOAD; + *(ptr++)=flags & 0xff; + *(ptr++)=(flags>>8) & 0xff; + *(ptr++)=(flags>>16) & 0xff; + *(ptr++)=(flags>>24) & 0xff; + + *(ptr++)=count & 0xff; + *(ptr++)=(count>>8) & 0xff; + *(ptr++)=(count>>16) & 0xff; + *(ptr++)=(count>>24) & 0xff; + + if (count>0) { + const AQH_VALUE *value; + + value=AQH_Value_List_First(valueList); + while(value) { + _writeValue(value, ptr); + ptr+=AQH_MSGDATA_VALUES_VALUES_SIZE; + value=AQH_Value_List_Next(value); + } + } + return msg; +} + + + +GWEN_MSG *AQH_ValuesDataIpcMsg_newForOneValue(uint16_t code, uint32_t flags, const AQH_VALUE *value) +{ + GWEN_MSG *msg; + uint8_t *ptr; + int count; + int payloadSize; + + count=1; + payloadSize=AQH_MSGDATA_VALUES_OFFS_VALUES+AQH_MSGDATA_VALUES_VALUES_SIZE; + + msg=GWEN_IpcMsg_new(AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION, code, payloadSize, NULL); + ptr=GWEN_Msg_GetBuffer(msg)+GWEN_MSGIPC_OFFS_PAYLOAD; + *(ptr++)=flags & 0xff; + *(ptr++)=(flags>>8) & 0xff; + *(ptr++)=(flags>>16) & 0xff; + *(ptr++)=(flags>>24) & 0xff; + + *(ptr++)=count & 0xff; + *(ptr++)=(count>>8) & 0xff; + *(ptr++)=(count>>16) & 0xff; + *(ptr++)=(count>>24) & 0xff; + + _writeValue(value, ptr); + + return msg; +} + + + +void _writeValue(const AQH_VALUE *value, uint8_t *ptr) +{ + uint64_t i64; + const char *name; + const char *units; + + i64=AQH_Value_GetId(value); + name=AQH_Value_GetName(value); + units=AQH_Value_GetValueUnits(value); + + *(ptr++)=i64 & 0xff; + *(ptr++)=(i64>>8) & 0xff; + *(ptr++)=(i64>>16) & 0xff; + *(ptr++)=(i64>>24) & 0xff; + *(ptr++)=(i64>>32) & 0xff; + *(ptr++)=(i64>>40) & 0xff; + *(ptr++)=(i64>>48) & 0xff; + *(ptr++)=(i64>>56) & 0xff; + if (name) { + strncpy((char*) ptr, name, AQH_MSGDATA_VALUES_VALUES_SIZE_NAME-1); + ptr[AQH_MSGDATA_VALUES_VALUES_SIZE_NAME-1]=0; + } + else + memset(ptr, 0, AQH_MSGDATA_VALUES_VALUES_SIZE_NAME); + ptr+=AQH_MSGDATA_VALUES_VALUES_SIZE_NAME; + + if (units) { + strncpy((char*) ptr, units, AQH_MSGDATA_VALUES_VALUES_SIZE_UNITS-1); + ptr[AQH_MSGDATA_VALUES_VALUES_SIZE_UNITS-1]=0; + } + else + memset(ptr, 0, AQH_MSGDATA_VALUES_VALUES_SIZE_UNITS); + ptr+=AQH_MSGDATA_VALUES_VALUES_SIZE_UNITS; +} + + + +uint32_t AQH_ValuesDataIpcMsg_GetFlags(const GWEN_MSG *msg) +{ + return GWEN_Msg_GetUint32At(msg, GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_VALUES_OFFS_FLAGS, 0); +} + + + +uint32_t AQH_ValuesDataIpcMsg_GetNumValues(const GWEN_MSG *msg) +{ + return GWEN_Msg_GetUint32At(msg, GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_VALUES_OFFS_NUMVALUES, 0); +} + + + +uint64_t AQH_ValuesDataIpcMsg_GetValueId(const GWEN_MSG *msg, int idx) +{ + uint32_t pos; + + pos= + AQH_MSGDATA_VALUES_OFFS_VALUES+ + (idx*AQH_MSGDATA_VALUES_VALUES_SIZE)+ + AQH_MSGDATA_VALUES_VALUES_OFFS_ID; + return GWEN_Msg_GetUint64At(msg, GWEN_MSGIPC_OFFS_PAYLOAD+pos, 0); +} + + + +const char *AQH_ValuesDataIpcMsg_GetValueName(const GWEN_MSG *msg, int idx) +{ + uint32_t pos; + + pos= + AQH_MSGDATA_VALUES_OFFS_VALUES+ + (idx*AQH_MSGDATA_VALUES_VALUES_SIZE)+ + AQH_MSGDATA_VALUES_VALUES_OFFS_NAME; + + if (GWEN_Msg_GetBytesInBuffer(msg)>=pos+GWEN_MSGIPC_OFFS_PAYLOAD) + return (const char*) (GWEN_Msg_GetConstBuffer(msg)+GWEN_MSGIPC_OFFS_PAYLOAD+pos); + return NULL; +} + + + +int AQH_ValuesDataIpcMsg_IsValid(const GWEN_MSG *msg) +{ + int msgLen; + int numValues; + const uint8_t *ptr; + int i; + + msgLen=GWEN_Msg_GetBytesInBuffer(msg); + if (msgLen=AQH_MSGDATA_VALUES_MINSIZE) { + GWEN_Buffer_AppendArgs(dbuf, + "VALUES (code=%d, proto=%d, proto version=%d, flags=0x%08x, values=%d)\n", + GWEN_IpcMsg_GetCode(msg), + GWEN_IpcMsg_GetProtoId(msg), + GWEN_IpcMsg_GetProtoVersion(msg), + (unsigned int)AQH_ValuesDataIpcMsg_GetFlags(msg), + AQH_ValuesDataIpcMsg_GetNumValues(msg)); + } +} + + + + + + + diff --git a/aqhome/ipc/data/msg_data_values.h b/aqhome/ipc/data/msg_data_values.h new file mode 100644 index 0000000..2817f8b --- /dev/null +++ b/aqhome/ipc/data/msg_data_values.h @@ -0,0 +1,49 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2023 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 AQH_MSG_IPC_DATA_VALUES_H +#define AQH_MSG_IPC_DATA_VALUES_H + + +#include + +#include + +#include + + +/** + * This message is used in request AQH_MSGTYPE_IPC_DATA_ADDVALUES_REQ and in response AQH_MSGTYPE_IPC_DATA_GETVALUES_RSP. + */ + +#define AQH_MSGDATA_VALUES_FLAGS_LASTMSG 0x0001 + + +AQHOME_API GWEN_MSG *AQH_ValuesDataIpcMsg_new(uint16_t code, uint32_t flags, const AQH_VALUE_LIST *valueList); +AQHOME_API GWEN_MSG *AQH_ValuesDataIpcMsg_newForOneValue(uint16_t code, uint32_t flags, const AQH_VALUE *value); + + +AQHOME_API uint32_t AQH_ValuesDataIpcMsg_GetFlags(const GWEN_MSG *msg); +AQHOME_API uint32_t AQH_ValuesDataIpcMsg_GetNumValues(const GWEN_MSG *msg); + +AQHOME_API uint64_t AQH_ValuesDataIpcMsg_GetValueId(const GWEN_MSG *msg, int idx); +AQHOME_API const char *AQH_ValuesDataIpcMsg_GetValueName(const GWEN_MSG *msg, int idx); + +AQHOME_API int AQH_ValuesDataIpcMsg_IsValid(const GWEN_MSG *msg); +AQHOME_API void AQH_ValuesDataIpcMsg_DumpToBuffer(const GWEN_MSG *msg, GWEN_BUFFER *dbuf, const char *sText); + + + + + + + +#endif + + + diff --git a/aqhome/ipc/msg_ipc_qwords.c b/aqhome/ipc/msg_ipc_qwords.c new file mode 100644 index 0000000..957ed1a --- /dev/null +++ b/aqhome/ipc/msg_ipc_qwords.c @@ -0,0 +1,148 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2023 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 + +#include +#include + +#include +#include +#include + + +#define AQH_MSGDATA_QWORDS_OFFS_FLAGS 0 /* 4 bytes */ +#define AQH_MSGDATA_QWORDS_OFFS_NUMVALUES 4 /* 4 bytes */ + +#define AQH_MSGDATA_QWORDS_OFFS_VALUES 8 /* 8 byte */ + + +#define AQH_MSGDATA_QWORDS_MINSIZE (GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_QWORDS_OFFS_VALUES) + + +static void _writeQword(uint64_t i64, uint8_t *ptr); + + + + +GWEN_MSG *AQH_QwordsIpcMsg_new(uint8_t protoId, uint8_t protoVer, uint16_t code, uint32_t flags, const uint64_t *i64Ptr, int count) +{ + GWEN_MSG *msg; + uint8_t *ptr; + int payloadSize; + int i; + + payloadSize=AQH_MSGDATA_QWORDS_OFFS_VALUES+(count*8); + + msg=GWEN_IpcMsg_new(protoId, protoVer, code, payloadSize, NULL); + ptr=GWEN_Msg_GetBuffer(msg)+GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_QWORDS_OFFS_VALUES; + + *(ptr++)=flags & 0xff; + *(ptr++)=(flags>>8) & 0xff; + *(ptr++)=(flags>>16) & 0xff; + *(ptr++)=(flags>>24) & 0xff; + + *(ptr++)=count & 0xff; + *(ptr++)=(count>>8) & 0xff; + *(ptr++)=(count>>16) & 0xff; + *(ptr++)=(count>>24) & 0xff; + + for(i=0; i>8) & 0xff; + *(ptr++)=(i64>>16) & 0xff; + *(ptr++)=(i64>>24) & 0xff; + *(ptr++)=(i64>>32) & 0xff; + *(ptr++)=(i64>>40) & 0xff; + *(ptr++)=(i64>>48) & 0xff; + *(ptr++)=(i64>>56) & 0xff; +} + + + +uint32_t AQH_QwordsIpcMsg_GetFlags(const GWEN_MSG *msg) +{ + return GWEN_Msg_GetUint32At(msg, GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_QWORDS_OFFS_FLAGS, 0); +} + + + +uint32_t AQH_QwordsIpcMsg_GetNumValues(const GWEN_MSG *msg) +{ + return GWEN_Msg_GetUint32At(msg, GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_QWORDS_OFFS_NUMVALUES, 0); +} + + + +uint64_t AQH_QwordsIpcMsg_GetValue(const GWEN_MSG *msg, int idx) +{ + uint32_t pos; + + pos=AQH_MSGDATA_QWORDS_OFFS_VALUES+(idx*8); + return GWEN_Msg_GetUint64At(msg, GWEN_MSGIPC_OFFS_PAYLOAD+pos, 0); +} + + + +int AQH_QwordsIpcMsg_IsValid(const GWEN_MSG *msg) +{ + int msgLen; + int numValues; + + msgLen=GWEN_Msg_GetBytesInBuffer(msg); + if (msgLen=AQH_MSGDATA_QWORDS_MINSIZE) { + GWEN_Buffer_AppendArgs(dbuf, + "QWORDS (code=%d, proto=%d, proto version=%d, flags=0x%08x, values=%d)\n", + GWEN_IpcMsg_GetCode(msg), + GWEN_IpcMsg_GetProtoId(msg), + GWEN_IpcMsg_GetProtoVersion(msg), + (unsigned int)AQH_QwordsIpcMsg_GetFlags(msg), + AQH_QwordsIpcMsg_GetNumValues(msg)); + } +} + + + + + + + diff --git a/aqhome/ipc/msg_ipc_qwords.h b/aqhome/ipc/msg_ipc_qwords.h new file mode 100644 index 0000000..1765fec --- /dev/null +++ b/aqhome/ipc/msg_ipc_qwords.h @@ -0,0 +1,34 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2023 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 AQH_MSG_IPC_QWORDS_H +#define AQH_MSG_IPC_QWORDS_H + + +#include +#include +#include + +#include +#include + + +AQHOME_API GWEN_MSG *AQH_QwordsIpcMsg_new(uint8_t protoId, uint8_t protoVer, uint16_t code, + uint32_t flags, const uint64_t *i64Ptr, int count); + +AQHOME_API uint32_t AQH_QwordsIpcMsg_GetFlags(const GWEN_MSG *msg); +AQHOME_API uint32_t AQH_QwordsIpcMsg_GetNumValues(const GWEN_MSG *msg); +AQHOME_API uint64_t AQH_QwordsIpcMsg_GetValue(const GWEN_MSG *msg, int idx); +AQHOME_API int AQH_QwordsIpcMsg_IsValid(const GWEN_MSG *msg); +AQHOME_API void AQH_QwordsIpcMsg_DumpToBuffer(const GWEN_MSG *msg, GWEN_BUFFER *dbuf, const char *sText); + + +#endif + + + diff --git a/aqhome/ipc/msg_ipc_result.c b/aqhome/ipc/msg_ipc_result.c index 2510f3f..58a0b79 100644 --- a/aqhome/ipc/msg_ipc_result.c +++ b/aqhome/ipc/msg_ipc_result.c @@ -24,31 +24,33 @@ -#define AQH_MSGIPC_RESULT_OFFS_RESULTCODE 0 /* 2 bytes */ +#define AQH_MSGIPC_RESULT_OFFS_RESULTCODE 0 /* 4 bytes */ -#define AQH_MSGIPC_RESULT_MINSIZE (GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGIPC_RESULT_OFFS_RESULTCODE+2) +#define AQH_MSGIPC_RESULT_MINSIZE (GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGIPC_RESULT_OFFS_RESULTCODE+4) -GWEN_MSG *AQH_ResultIpcMsg_new(uint16_t code, uint16_t errorCode) +GWEN_MSG *AQH_ResultIpcMsg_new(uint16_t code, uint32_t resultCode) { GWEN_MSG *msg; uint8_t *ptr; msg=GWEN_IpcMsg_new(AQH_IPC_PROTOCOL_RESULT_ID, AQH_IPC_PROTOCOL_RESULT_VERSION, code, AQH_MSGIPC_RESULT_MINSIZE, NULL); ptr=GWEN_Msg_GetBuffer(msg); - ptr[GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGIPC_RESULT_OFFS_RESULTCODE+0]=errorCode & 0xff; - ptr[GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGIPC_RESULT_OFFS_RESULTCODE+1]=errorCode & 0xff; + ptr[GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGIPC_RESULT_OFFS_RESULTCODE+0]=resultCode & 0xff; + ptr[GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGIPC_RESULT_OFFS_RESULTCODE+1]=(resultCode>>8) & 0xff; + ptr[GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGIPC_RESULT_OFFS_RESULTCODE+2]=(resultCode>>16) & 0xff; + ptr[GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGIPC_RESULT_OFFS_RESULTCODE+3]=(resultCode>>24) & 0xff; return msg; } -uint16_t AQH_ResultIpcMsg_GetResultCode(const GWEN_MSG *msg) +uint32_t AQH_ResultIpcMsg_GetResultCode(const GWEN_MSG *msg) { - return GWEN_Msg_GetUint16At(msg, GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGIPC_RESULT_OFFS_RESULTCODE, 0); + return GWEN_Msg_GetUint32At(msg, GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGIPC_RESULT_OFFS_RESULTCODE, 0); } diff --git a/aqhome/ipc/msg_ipc_result.h b/aqhome/ipc/msg_ipc_result.h index 0b54916..ee3434c 100644 --- a/aqhome/ipc/msg_ipc_result.h +++ b/aqhome/ipc/msg_ipc_result.h @@ -26,8 +26,8 @@ #define AQH_MSG_IPC_ERROR_NODATA 1 -AQHOME_API GWEN_MSG *AQH_ResultIpcMsg_new(uint16_t code, uint16_t errorCode); -AQHOME_API uint16_t AQH_ResultIpcMsg_GetResultCode(const GWEN_MSG *msg); +AQHOME_API GWEN_MSG *AQH_ResultIpcMsg_new(uint16_t code, uint32_t resultCode); +AQHOME_API uint32_t AQH_ResultIpcMsg_GetResultCode(const GWEN_MSG *msg); AQHOME_API void AQH_ResultIpcMsg_DumpToBuffer(const GWEN_MSG *msg, GWEN_BUFFER *dbuf, const char *sText);