diff --git a/apps/aqhomed/0BUILD b/apps/aqhomed/0BUILD
index 9b5043f..c5fc923 100644
--- a/apps/aqhomed/0BUILD
+++ b/apps/aqhomed/0BUILD
@@ -33,11 +33,26 @@
+ aqhomed.h
+ aqhomed_p.h
+ init.h
+ fini.h
+ loop.h
+ db.h
+ tty_log.h
+ tty_write.h
$(local/typefiles)
main.c
+ aqhomed.c
+ init.c
+ fini.c
+ loop.c
+ db.c
+ tty_log.c
+ tty_write.c
diff --git a/apps/aqhomed/aqhomed.c b/apps/aqhomed/aqhomed.c
new file mode 100644
index 0000000..2d10c68
--- /dev/null
+++ b/apps/aqhomed/aqhomed.c
@@ -0,0 +1,184 @@
+/****************************************************************************
+ * 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 "./aqhomed_p.h"
+#include "./tty_log.h"
+#include "./tty_write.h"
+
+#include "aqhome/msg/endpoint2_tty.h"
+#include "aqhome/ipc/endpoint2_ipc.h"
+#include "aqhome/mqtt/endpoint2_mqttc.h"
+
+#include
+#include
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * defines
+ * ------------------------------------------------------------------------------------------------
+ */
+
+#define I18N(msg) msg
+#define I18S(msg) msg
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * forward declarations
+ * ------------------------------------------------------------------------------------------------
+ */
+
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * implementations
+ * ------------------------------------------------------------------------------------------------
+ */
+
+AQHOMED *AqHomed_new(void)
+{
+ AQHOMED *aqh;
+
+ GWEN_NEW_OBJECT(AQHOMED, aqh);
+ aqh->rootEndpoint=GWEN_MsgEndpoint2_new("root", 0);
+ aqh->nodeDb=AQH_NodeDb_new();
+
+ return aqh;
+}
+
+
+
+void AqHomed_free(AQHOMED *aqh)
+{
+ if (aqh) {
+ GWEN_MsgEndpoint2_free(aqh->rootEndpoint);
+ aqh->rootEndpoint=NULL;
+ aqh->ttyEndpoint=NULL;
+ aqh->ipcdEndpoint=NULL;
+ aqh->mqttEndpoint=NULL;
+ GWEN_DB_Group_free(aqh->dbArgs);
+ AQH_NodeDb_free(aqh->nodeDb);
+ aqh->dbArgs=NULL;
+ free(aqh->logFile);
+ free(aqh->writeFolder);
+ free(aqh->pidFile);
+ free(aqh->dbFile);
+
+ GWEN_FREE_OBJECT(aqh);
+ }
+}
+
+
+
+GWEN_MSG_ENDPOINT2 *AqHomed_GetTtyEndpoint(const AQHOMED *aqh)
+{
+ return aqh?aqh->ttyEndpoint:NULL;
+}
+
+
+
+GWEN_MSG_ENDPOINT2 *AqHomed_GetIpcdEndpoint(const AQHOMED *aqh)
+{
+ return aqh?aqh->ipcdEndpoint:NULL;
+}
+
+
+
+GWEN_MSG_ENDPOINT2 *AqHomed_GetMqttEndpoint(const AQHOMED *aqh)
+{
+ return aqh?aqh->mqttEndpoint:NULL;
+}
+
+
+
+GWEN_DB_NODE *AqHomed_GetDbArgs(const AQHOMED *aqh)
+{
+ return aqh?aqh->dbArgs:NULL;
+}
+
+
+
+const char *AqHomed_GetLogFile(const AQHOMED *aqh)
+{
+ return aqh?aqh->logFile:NULL;
+}
+
+
+
+void AqHomed_SetLogFile(AQHOMED *aqh, const char *s)
+{
+ if (aqh) {
+ free(aqh->logFile);
+ aqh->logFile=s?strdup(s):NULL;
+ }
+}
+
+
+
+const char *AqHomed_GetWriteFolder(const AQHOMED *aqh)
+{
+ return aqh?aqh->writeFolder:NULL;
+}
+
+
+
+void AqHomed_SetWriteFolder(AQHOMED *aqh, const char *s)
+{
+ if (aqh) {
+ free(aqh->writeFolder);
+ aqh->writeFolder=s?strdup(s):NULL;
+ }
+}
+
+
+
+const char *AqHomed_GetPidFile(const AQHOMED *aqh)
+{
+ return aqh?aqh->pidFile:NULL;
+}
+
+
+
+void AqHomed_SetPidFile(AQHOMED *aqh, const char *s)
+{
+ if (aqh) {
+ free(aqh->pidFile);
+ aqh->pidFile=s?strdup(s):NULL;
+ }
+}
+
+
+
+const char *AqHomed_GetDbFile(const AQHOMED *aqh)
+{
+ return aqh?aqh->dbFile:NULL;
+}
+
+
+
+void AqHomed_SetDbFile(AQHOMED *aqh, const char *s)
+{
+ if (aqh) {
+ free(aqh->dbFile);
+ aqh->dbFile=s?strdup(s):NULL;
+ }
+}
+
+
+
+
+
+
+
+
diff --git a/apps/aqhomed/aqhomed.h b/apps/aqhomed/aqhomed.h
new file mode 100644
index 0000000..c4e48c0
--- /dev/null
+++ b/apps/aqhomed/aqhomed.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+ * 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 AQHOMED_H
+#define AQHOMED_H
+
+
+#include
+#include
+
+
+#define AQHOME_ENDPOINTGROUP_NODE 1
+#define AQHOME_ENDPOINTGROUP_IPC 2
+#define AQHOME_ENDPOINTGROUP_MQTT 4
+
+
+typedef struct AQHOMED AQHOMED;
+
+AQHOMED *AqHomed_new(void);
+void AqHomed_free(AQHOMED *aqh);
+
+GWEN_MSG_ENDPOINT2 *AqHomed_GetTtyEndpoint(const AQHOMED *aqh);
+GWEN_MSG_ENDPOINT2 *AqHomed_GetIpcdEndpoint(const AQHOMED *aqh);
+GWEN_MSG_ENDPOINT2 *AqHomed_GetMqttEndpoint(const AQHOMED *aqh);
+
+GWEN_DB_NODE *AqHomed_GetDbArgs(const AQHOMED *aqh);
+
+const char *AqHomed_GetLogFile(const AQHOMED *aqh);
+void AqHomed_SetLogFile(AQHOMED *aqh, const char *s);
+
+const char *AqHomed_GetWriteFolder(const AQHOMED *aqh);
+void AqHomed_SetWriteFolder(AQHOMED *aqh, const char *s);
+
+const char *AqHomed_GetPidFile(const AQHOMED *aqh);
+void AqHomed_SetPidFile(AQHOMED *aqh, const char *s);
+
+const char *AqHomed_GetDbFile(const AQHOMED *aqh);
+void AqHomed_SetDbFile(AQHOMED *aqh, const char *s);
+
+
+#endif
+
+
diff --git a/apps/aqhomed/aqhomed_p.h b/apps/aqhomed/aqhomed_p.h
new file mode 100644
index 0000000..60c3258
--- /dev/null
+++ b/apps/aqhomed/aqhomed_p.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+ * 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 AQHOMED_P_H
+#define AQHOMED_P_H
+
+
+#include "./aqhomed.h"
+
+#include "aqhome/nodes/nodedb.h"
+
+
+/* default values */
+#define AQHOMED_DEFAULT_NODEADDR 240
+#define AQHOMED_DEFAULT_PIDFILE "/var/run/aqhomed.pid"
+#define AQHOMED_DEFAULT_DEVICE "/dev/ttyUSB0"
+#define AQHOMED_DEFAULT_IPC_PORT 45454
+#define AQHOMED_DEFAULT_MQTT_CLIENTID "aqhomed"
+#define AQHOMED_DEFAULT_MQTT_TOPIC_PREFIX "aqhome/sensors"
+#define AQHOMED_DEFAULT_MQTT_KEEPALIVE 600
+#define AQHOMED_DEFAULT_MQTT_PORT 1883
+
+
+
+struct AQHOMED {
+ GWEN_MSG_ENDPOINT2 *rootEndpoint;
+
+ GWEN_MSG_ENDPOINT2 *ttyEndpoint;
+ GWEN_MSG_ENDPOINT2 *ipcdEndpoint;
+ GWEN_MSG_ENDPOINT2 *mqttEndpoint;
+
+ AQH_NODE_DB *nodeDb;
+
+ GWEN_DB_NODE *dbArgs;
+
+ char *dbFile;
+ char *logFile;
+ char *writeFolder;
+ char *pidFile;
+
+ int nodeAddress;
+};
+
+#endif
+
diff --git a/apps/aqhomed/db.c b/apps/aqhomed/db.c
new file mode 100644
index 0000000..521ccc7
--- /dev/null
+++ b/apps/aqhomed/db.c
@@ -0,0 +1,278 @@
+/****************************************************************************
+ * 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 "./db.h"
+#include "./aqhomed_p.h"
+
+#include "aqhome/msg/msg_node.h"
+#include "aqhome/msg/msg_sendstats.h"
+#include "aqhome/msg/msg_recvstats.h"
+#include "aqhome/msg/msg_value2.h"
+#include "aqhome/msg/msg_needaddr.h"
+#include "aqhome/msg/msg_claimaddr.h"
+#include "aqhome/msg/msg_haveaddr.h"
+#include "aqhome/msg/msg_device.h"
+#include "aqhome/msg/msg_flashready.h"
+
+
+#include
+#include
+#include
+#include
+#include
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * forward declarations
+ * ------------------------------------------------------------------------------------------------
+ */
+
+static void _handleMsgValue2(AQHOMED *aqh, const GWEN_MSG *msg);
+static void _handleMsgNeedAddress(AQHOMED *aqh, const GWEN_MSG *msg);
+static void _handleMsgClaimAddress(AQHOMED *aqh, const GWEN_MSG *msg);
+static void _handleMsgHaveAddress(AQHOMED *aqh, const GWEN_MSG *msg);
+static void _handleMsgComSendStat(AQHOMED *aqh, const GWEN_MSG *msg);
+static void _handleMsgComRecvStat(AQHOMED *aqh, const GWEN_MSG *msg);
+static void _handleMsgDevice(AQHOMED *aqh, const GWEN_MSG *msg);
+static void _handleMsgFlashReady(AQHOMED *aqh, const GWEN_MSG *msg);
+
+static AQH_NODE_INFO *_getOrCreateNodeAndUpdateUidAddr(AQHOMED *aqh, const GWEN_MSG *msg, uint32_t uid);
+static void _updateTimestampLastChange(AQH_NODE_INFO *ni);
+
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * implementations
+ * ------------------------------------------------------------------------------------------------
+ */
+
+void AqHomed_NodeMsgToDb(AQHOMED *aqh, const GWEN_MSG *msg)
+{
+ int msgIsValid;
+ uint8_t msgType;
+
+ DBG_INFO(AQH_LOGDOMAIN,
+ " - msg %d (%s) from %d to %d",
+ AQH_NodeMsg_GetMsgType(msg),
+ AQH_NodeMsg_MsgTypeToChar(AQH_NodeMsg_GetMsgType(msg)),
+ AQH_NodeMsg_GetSourceAddress(msg),
+ AQH_NodeMsg_GetDestAddress(msg));
+
+ msgIsValid=(AQH_NodeMsg_IsChecksumValid(msg) && AQH_NodeMsg_IsMsgComplete(msg));
+ msgType=AQH_NodeMsg_GetMsgType(msg);
+
+ if (msgIsValid) {
+ switch(msgType) {
+ case AQH_MSG_TYPE_COMSENDSTATS: _handleMsgComSendStat(aqh, msg); break;
+ case AQH_MSG_TYPE_COMRECVSTATS: _handleMsgComRecvStat(aqh, msg); break;
+ case AQH_MSG_TYPE_VALUE2: _handleMsgValue2(aqh, msg); break;
+ case AQH_MSG_TYPE_NEED_ADDRESS: _handleMsgNeedAddress(aqh, msg); break;
+ case AQH_MSG_TYPE_CLAIM_ADDRESS: _handleMsgClaimAddress(aqh, msg); break;
+ case AQH_MSG_TYPE_HAVE_ADDRESS: _handleMsgHaveAddress(aqh, msg); break;
+ case AQH_MSG_TYPE_DEVICE: _handleMsgDevice(aqh, msg); break;
+ case AQH_MSG_TYPE_FLASH_READY: _handleMsgFlashReady(aqh, msg); break;
+ default: break;
+ }
+ }
+}
+
+
+
+void _handleMsgValue2(AQHOMED *aqh, const GWEN_MSG *msg)
+{
+ AQH_NODE_INFO *ni;
+ uint32_t uid;
+
+ uid=AQH_Value2Msg_GetUid(msg);
+ ni=_getOrCreateNodeAndUpdateUidAddr(aqh, msg, uid);
+ if (ni==NULL) {
+ DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
+ }
+}
+
+
+
+void _handleMsgNeedAddress(AQHOMED *aqh, const GWEN_MSG *msg)
+{
+ AQH_NODE_INFO *ni;
+ uint32_t uid;
+
+ uid=AQH_NeedAddrMsg_GetUid(msg);
+ ni=_getOrCreateNodeAndUpdateUidAddr(aqh, msg, uid);
+ if (ni==NULL) {
+ DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
+ }
+}
+
+
+
+void _handleMsgClaimAddress(AQHOMED *aqh, const GWEN_MSG *msg)
+{
+ AQH_NODE_INFO *ni;
+ uint32_t uid;
+
+ uid=AQH_ClaimAddrMsg_GetUid(msg);
+ ni=_getOrCreateNodeAndUpdateUidAddr(aqh, msg, uid);
+ if (ni==NULL) {
+ DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
+ }
+}
+
+
+
+void _handleMsgHaveAddress(AQHOMED *aqh, const GWEN_MSG *msg)
+{
+ AQH_NODE_INFO *ni;
+ uint32_t uid;
+
+ uid=AQH_HaveAddrMsg_GetUid(msg);
+ ni=_getOrCreateNodeAndUpdateUidAddr(aqh, msg, uid);
+ if (ni==NULL) {
+ DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
+ }
+}
+
+
+
+void _handleMsgComSendStat(AQHOMED *aqh, const GWEN_MSG *msg)
+{
+ AQH_NODE_INFO *ni;
+ uint32_t uid;
+
+ uid=AQH_SendStatsMsg_GetUid(msg);
+ ni=_getOrCreateNodeAndUpdateUidAddr(aqh, msg, uid);
+ if (ni==NULL) {
+ DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
+ }
+ AQH_NodeInfo_SetStatsPacketsOut(ni, AQH_SendStatsMsg_GetPacketsOut(msg));
+ AQH_NodeInfo_SetStatsCollisions(ni, AQH_SendStatsMsg_GetCollisions(msg));
+ AQH_NodeInfo_SetStatsBusy(ni, AQH_SendStatsMsg_GetBusyErrors(msg));
+ AQH_NodeDb_SetModified(aqh->nodeDb);
+ _updateTimestampLastChange(ni);
+}
+
+
+
+void _handleMsgComRecvStat(AQHOMED *aqh, const GWEN_MSG *msg)
+{
+ AQH_NODE_INFO *ni;
+ uint32_t uid;
+
+ uid=AQH_RecvStatsMsg_GetUid(msg);
+ ni=_getOrCreateNodeAndUpdateUidAddr(aqh, msg, uid);
+ if (ni==NULL) {
+ DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
+ }
+ AQH_NodeInfo_SetStatsPacketsIn(ni, AQH_RecvStatsMsg_GetPacketsIn(msg));
+ AQH_NodeInfo_SetStatsCrcErrors(ni, AQH_RecvStatsMsg_GetCrcErrors(msg));
+ AQH_NodeInfo_SetStatsIoErrors(ni, AQH_RecvStatsMsg_GetIoErrors(msg));
+ AQH_NodeDb_SetModified(aqh->nodeDb);
+ _updateTimestampLastChange(ni);
+}
+
+
+
+void _handleMsgDevice(AQHOMED *aqh, const GWEN_MSG *msg)
+{
+ AQH_NODE_INFO *ni;
+ uint32_t uid;
+
+ uid=AQH_DeviceMsg_GetUid(msg);
+ ni=_getOrCreateNodeAndUpdateUidAddr(aqh, msg, uid);
+ if (ni) {
+ AQH_NodeInfo_SetFirmwareType(ni, AQH_DeviceMsg_GetFirmwareType(msg));
+ AQH_NodeInfo_SetFirmwareVersion(ni, (AQH_DeviceMsg_GetFirmwareHigh(msg)<<8) | AQH_DeviceMsg_GetFirmwareLow(msg));
+ AQH_NodeInfo_SetModules(ni, AQH_DeviceMsg_GetModuleMask(msg));
+ _updateTimestampLastChange(ni);
+ AQH_NodeDb_SetModified(aqh->nodeDb);
+ }
+ else {
+ DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
+ }
+}
+
+
+
+void _handleMsgFlashReady(AQHOMED *aqh, const GWEN_MSG *msg)
+{
+ AQH_NODE_INFO *ni;
+ uint32_t uid;
+
+ uid=AQH_FlashReadyMsg_GetUid(msg);
+ ni=_getOrCreateNodeAndUpdateUidAddr(aqh, msg, uid);
+ if (ni) {
+ AQH_NodeInfo_SetFirmwareType(ni, AQH_FlashReadyMsg_GetFirmwareType(msg));
+ AQH_NodeInfo_SetFirmwareVersion(ni, AQH_FlashReadyMsg_GetFirmwareVersion(msg));
+ _updateTimestampLastChange(ni);
+ AQH_NodeDb_SetModified(aqh->nodeDb);
+ }
+ else {
+ DBG_INFO(AQH_LOGDOMAIN, "Error handling message");
+ }
+}
+
+
+
+AQH_NODE_INFO *_getOrCreateNodeAndUpdateUidAddr(AQHOMED *aqh, const GWEN_MSG *msg, uint32_t uid)
+{
+ uint8_t busAddr;
+ AQH_NODE_INFO *ni;
+
+ busAddr=AQH_NodeMsg_GetSourceAddress(msg);
+ ni=AQH_NodeDb_GetNodeInfoByUid(aqh->nodeDb, uid);
+ if (ni) {
+ uint8_t storedBusAddr;
+
+ storedBusAddr=AQH_NodeInfo_GetBusAddress(ni);
+ if (busAddr!=0 && storedBusAddr!=busAddr) {
+ DBG_INFO(AQH_LOGDOMAIN, "Changed busaddr for %08x from %02x to %02x", uid, storedBusAddr, busAddr);
+ AQH_NodeInfo_SetBusAddress(ni, busAddr);
+ _updateTimestampLastChange(ni);
+ AQH_NodeDb_SetModified(aqh->nodeDb);
+ }
+ }
+ else {
+ int rv;
+
+ ni=AQH_NodeInfo_new();
+ AQH_NodeInfo_SetBusAddress(ni, busAddr);
+ AQH_NodeInfo_SetUid(ni, uid);
+ _updateTimestampLastChange(ni);
+ rv=AQH_NodeDb_AddNodeInfo(aqh->nodeDb, ni);
+ if (rv<0) {
+ DBG_INFO(AQH_LOGDOMAIN, "here (%d)", rv);
+ AQH_NodeInfo_free(ni);
+ return NULL;
+ }
+ else {
+ DBG_INFO(AQH_LOGDOMAIN, "Added node %08x (%02x)", uid, busAddr);
+ }
+ }
+ return ni;
+}
+
+
+
+void _updateTimestampLastChange(AQH_NODE_INFO *ni)
+{
+ GWEN_TIMESTAMP *t;
+
+ t=GWEN_Timestamp_NowInLocalTime();
+ AQH_NodeInfo_SetTimestampLastChange(ni, t);
+ GWEN_Timestamp_free(t);
+}
+
+
+
diff --git a/apps/aqhomed/db.h b/apps/aqhomed/db.h
new file mode 100644
index 0000000..cdd5f01
--- /dev/null
+++ b/apps/aqhomed/db.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 AQHOMED_DB_H
+#define AQHOMED_DB_H
+
+
+#include "./aqhomed.h"
+
+
+
+void AqHomed_NodeMsgToDb(AQHOMED *aqh, const GWEN_MSG *msg);
+
+
+
+#endif
+
+
diff --git a/apps/aqhomed/fini.c b/apps/aqhomed/fini.c
new file mode 100644
index 0000000..50b27a6
--- /dev/null
+++ b/apps/aqhomed/fini.c
@@ -0,0 +1,85 @@
+/****************************************************************************
+ * 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 "./aqhomed_p.h"
+
+#include
+#include
+#include
+#include
+
+
+#include
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * defines
+ * ------------------------------------------------------------------------------------------------
+ */
+
+#define I18N(msg) msg
+#define I18S(msg) msg
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * forward declarations
+ * ------------------------------------------------------------------------------------------------
+ */
+
+static void _disconnectTree(GWEN_MSG_ENDPOINT2 *ep);
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * implementations
+ * ------------------------------------------------------------------------------------------------
+ */
+
+void AqHomed_Fini(AQHOMED *aqh)
+{
+ if (aqh) {
+ if (aqh->rootEndpoint) {
+ _disconnectTree(aqh->rootEndpoint);
+ GWEN_MsgEndpoint2_Disconnect(aqh->rootEndpoint);
+ }
+ aqh->rootEndpoint=NULL;
+ aqh->ttyEndpoint=NULL;
+ aqh->ipcdEndpoint=NULL;
+ aqh->mqttEndpoint=NULL;
+
+ if (aqh->pidFile)
+ remove(aqh->pidFile);
+ }
+}
+
+
+
+void _disconnectTree(GWEN_MSG_ENDPOINT2 *ep)
+{
+ GWEN_MSG_ENDPOINT2 *epChild;
+
+ epChild=GWEN_MsgEndpoint2_Tree2_GetFirstChild(ep);
+ while(epChild) {
+ _disconnectTree(epChild);
+ epChild=GWEN_MsgEndpoint2_Tree2_GetNext(epChild);
+ } /* while */
+
+ GWEN_MsgEndpoint2_Disconnect(ep);
+}
+
+
+
+
diff --git a/apps/aqhomed/fini.h b/apps/aqhomed/fini.h
new file mode 100644
index 0000000..4e41d91
--- /dev/null
+++ b/apps/aqhomed/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 AQHOMED_FINI_H
+#define AQHOMED_FINI_H
+
+
+#include "./aqhomed.h"
+
+
+
+void AqHomed_Fini(AQHOMED *aqh);
+
+
+
+#endif
+
+
diff --git a/apps/aqhomed/init.c b/apps/aqhomed/init.c
new file mode 100644
index 0000000..7461657
--- /dev/null
+++ b/apps/aqhomed/init.c
@@ -0,0 +1,519 @@
+/****************************************************************************
+ * 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 "./aqhomed_p.h"
+#include "./tty_log.h"
+#include "./tty_write.h"
+
+#include "aqhome/msg/endpoint2_tty.h"
+#include "aqhome/ipc/endpoint2_ipc.h"
+#include "aqhome/mqtt/endpoint2_mqttc.h"
+
+#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 _setupTty(AQHOMED *aqh, GWEN_DB_NODE *dbArgs);
+static void _setupIpc(AQHOMED *aqh, GWEN_DB_NODE *dbArgs);
+static GWEN_MSG_ENDPOINT2 *_acceptIpcFn(GWEN_MSG_ENDPOINT2 *ep, GWEN_SOCKET *sk, const GWEN_INETADDRESS *addr, void *data);
+static void _setupMqtt(AQHOMED *aqh, GWEN_DB_NODE *dbArgs);
+static void _setupLog(AQHOMED *aqh, GWEN_DB_NODE *dbArgs);
+static void _setupWriter(AQHOMED *aqh, GWEN_DB_NODE *dbArgs);
+static void _setupDb(AQHOMED *aqh, GWEN_DB_NODE *dbArgs);
+
+static int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs);
+static int _createPidFile(const char *pidFilename);
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * implementations
+ * ------------------------------------------------------------------------------------------------
+ */
+
+int AqHomed_Init(AQHOMED *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;
+
+ s=GWEN_DB_GetCharValue(dbArgs, "pidfile", 0, AQHOMED_DEFAULT_PIDFILE);
+ if (s && *s) {
+ AqHomed_SetPidFile(aqh, s);
+ rv=_createPidFile(s);
+ if (rv<0) {
+ DBG_ERROR(NULL, "Error creating PID file (%d)", rv);
+ return rv;
+ }
+ }
+
+ aqh->nodeAddress=GWEN_DB_GetIntValue(dbArgs, "nodeAddress", 0, AQHOMED_DEFAULT_NODEADDR);
+
+ _setupDb(aqh, dbArgs);
+
+ rv=_setupTty(aqh, dbArgs);
+ if (rv<0) {
+ DBG_ERROR(NULL, "Error setting up TTY endpoint (%d)", rv);
+ return rv;
+ }
+
+ _setupIpc(aqh, dbArgs);
+ _setupMqtt(aqh, dbArgs);
+ _setupLog(aqh, dbArgs);
+ _setupWriter(aqh, dbArgs);
+
+ return 0;
+}
+
+
+
+int _setupTty(AQHOMED *aqh, GWEN_DB_NODE *dbArgs)
+{
+ const char *devicePath;
+
+ devicePath=GWEN_DB_GetCharValue(dbArgs, "device", 0, AQHOMED_DEFAULT_DEVICE);
+ if (devicePath && *devicePath) {
+ GWEN_MSG_ENDPOINT2 *epTty;
+
+ epTty=AQH_TtyEndpoint2_new(devicePath, AQHOME_ENDPOINTGROUP_NODE);
+ if (epTty==NULL) {
+ DBG_ERROR(NULL, "Error creating endpoint TTY");
+ return GWEN_ERROR_GENERIC;
+ }
+ GWEN_MsgEndpoint2_Tree2_AddChild(aqh->rootEndpoint, epTty);
+ aqh->ttyEndpoint=epTty;
+ }
+ else {
+ DBG_ERROR(NULL, "Missing device path");
+ return GWEN_ERROR_GENERIC;
+ }
+
+ return 0;
+}
+
+
+
+void _setupIpc(AQHOMED *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, AQHOMED_DEFAULT_IPC_PORT);
+
+ if (tcpAddress && *tcpAddress && tcpPort) {
+ GWEN_MSG_ENDPOINT2 *ep;
+
+ ep=GWEN_TcpdEndpoint2_new(tcpAddress, tcpPort, NULL, AQHOME_ENDPOINTGROUP_IPC);
+ GWEN_TcpdEndpoint2_SetAcceptFn(ep, _acceptIpcFn, aqh);
+
+ GWEN_MsgEndpoint2_Tree2_AddChild(aqh->rootEndpoint, ep);
+ aqh->ipcdEndpoint=ep;
+ }
+}
+
+
+
+void _setupMqtt(AQHOMED *aqh, GWEN_DB_NODE *dbArgs)
+{
+ const char *mqttAddress;
+ int mqttPort;
+ const char *mqttClientId;
+ const char *mqttTopicPrefix;
+ int mqttKeepAlive;
+
+ mqttAddress=GWEN_DB_GetCharValue(dbArgs, "mqttAddress", 0, NULL);
+ mqttPort=GWEN_DB_GetIntValue(dbArgs, "mqttPort", 0, AQHOMED_DEFAULT_MQTT_PORT);
+ mqttClientId=GWEN_DB_GetCharValue(dbArgs, "mqttClientId", 0, AQHOMED_DEFAULT_MQTT_CLIENTID);
+ mqttTopicPrefix=GWEN_DB_GetCharValue(dbArgs, "mqttTopicPrefix", 0, AQHOMED_DEFAULT_MQTT_TOPIC_PREFIX);
+ mqttKeepAlive=GWEN_DB_GetIntValue(dbArgs, "mqttKeepAlive", 0, AQHOMED_DEFAULT_MQTT_KEEPALIVE);
+
+ if (mqttAddress && *mqttAddress && mqttPort) {
+ GWEN_MSG_ENDPOINT2 *ep;
+ int rv;
+
+ ep=AQH_MqttClientEndpoint2_new(mqttClientId, mqttAddress, mqttPort, NULL, AQHOME_ENDPOINTGROUP_MQTT);
+ AQH_MqttClientEndpoint2_SetTopicPrefix(ep, mqttTopicPrefix);
+ AQH_MqttClientEndpoint2_SetKeepAliveTime(ep, mqttKeepAlive);
+
+ GWEN_MsgEndpoint2_Tree2_AddChild(aqh->rootEndpoint, ep);
+ aqh->mqttEndpoint=ep;
+
+ rv=AQH_MqttClientEndpoint2_StartConnect(ep);
+ if (rv<0 && rv!=GWEN_ERROR_IN_PROGRESS) {
+ DBG_ERROR(NULL, "Error connecting to MQTT server %s:%d (%d), will retry later", mqttAddress, mqttPort, rv);
+ }
+ }
+}
+
+
+
+GWEN_MSG_ENDPOINT2 *_acceptIpcFn(GWEN_MSG_ENDPOINT2 *ep,
+ GWEN_SOCKET *sk,
+ const GWEN_INETADDRESS *addr,
+ GWEN_UNUSED void *data)
+{
+/* AQHOMED *aqh;
+ *
+ * aqh=(AQHOMED*) data;
+ */
+ DBG_INFO(NULL, "Incoming IPC connection");
+ return AQH_IpcEndpoint2_CreateIpcTcpServiceForSocket(sk, NULL, AQHOME_ENDPOINTGROUP_IPC);
+}
+
+
+
+void _setupLog(AQHOMED *aqh, GWEN_DB_NODE *dbArgs)
+{
+ const char *logFile;
+
+ logFile=GWEN_DB_GetCharValue(dbArgs, "logfile", 0, NULL);
+ if (logFile && *logFile)
+ AqHomed_SetLogFile(aqh, logFile);
+}
+
+
+
+void _setupWriter(AQHOMED *aqh, GWEN_DB_NODE *dbArgs)
+{
+ const char *writeToFolder;
+
+ writeToFolder=GWEN_DB_GetCharValue(dbArgs, "writeToFolder", 0, NULL);
+ if (writeToFolder && *writeToFolder)
+ AqHomed_SetWriteFolder(aqh, writeToFolder);
+}
+
+
+
+void _setupDb(AQHOMED *aqh, GWEN_DB_NODE *dbArgs)
+{
+ const char *s;
+
+ s=GWEN_DB_GetCharValue(dbArgs, "dbfile", 0, NULL);
+ if (s && *s) {
+ GWEN_DB_NODE *dbNodeDb;
+ int rv;
+
+ AqHomed_SetDbFile(aqh, s);
+
+ dbNodeDb=GWEN_DB_Group_new("dbNodes");
+ rv=GWEN_DB_ReadFile(dbNodeDb, s, GWEN_DB_FLAGS_DEFAULT);
+ if (rv==0) {
+ AQH_NodeDb_fromDb(aqh->nodeDb, dbNodeDb);
+ }
+ GWEN_DB_Group_free(dbNodeDb);
+ }
+}
+
+
+
+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 */
+ "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 */
+ "device", /* name */
+ 0, /* minnum */
+ 1, /* maxnum */
+ "d", /* short option */
+ "device", /* long option */
+ I18S("Specify the device to communicate with (e.g. /dev/ttyUSB0)"),
+ I18S("Specify the device to communicate with (e.g. /dev/ttyUSB0)")
+ },
+ {
+ GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
+ GWEN_ArgsType_Int, /* type */
+ "nodeAddress", /* name */
+ 0, /* minnum */
+ 1, /* maxnum */
+ "n", /* short option */
+ "node", /* long option */
+ I18S("Specify the node address for the AqHome node adaptor (default 240)"),
+ I18S("Specify the node address for the AqHome node adaptor (default 240)")
+ },
+ {
+ GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
+ GWEN_ArgsType_Char, /* type */
+ "logFile", /* name */
+ 0, /* minnum */
+ 1, /* maxnum */
+ "l", /* short option */
+ "logfile", /* long option */
+ I18S("Specify a logfile to log received messages to"),
+ I18S("Specify a logfile to log received messages to")
+ },
+ {
+ 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 */
+ "mqttAddress", /* name */
+ 0, /* minnum */
+ 1, /* maxnum */
+ "ma", /* short option */
+ "mqttaddress", /* long option */
+ I18S("Specify the address of the MQTT server to connect to (disabled if missing)"),
+ I18S("Specify the address of the MQTT server to connect to (disabled if missing)")
+ },
+ {
+ GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
+ GWEN_ArgsType_Int, /* type */
+ "mqttPort", /* name */
+ 0, /* minnum */
+ 1, /* maxnum */
+ "mp", /* short option */
+ "mqttport", /* long option */
+ I18S("Specify the port of the MQTT server (default: 1883)"),
+ I18S("Specify the port of the MQTT server (default: 1883)")
+ },
+ {
+ GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
+ GWEN_ArgsType_Char, /* type */
+ "mqttClientId", /* name */
+ 0, /* minnum */
+ 1, /* maxnum */
+ NULL, /* short option */
+ "mqttclientid", /* long option */
+ I18S("Specify client id for the MQTT server (default: \"aqhomed\")"),
+ I18S("Specify client id for the MQTT server (default: \"aqhomed\")")
+ },
+ {
+ GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
+ GWEN_ArgsType_Char, /* type */
+ "mqttTopicPrefix", /* name */
+ 0, /* minnum */
+ 1, /* maxnum */
+ "mt", /* short option */
+ "mqtttopicprefix", /* long option */
+ I18S("Specify prefix of MQTT topics when publishing (defaults to \"aqhome/sensors\")"),
+ I18S("Specify prefix of MQTT topics when publishing (defaults to \"aqhome/sensors\")")
+ },
+ {
+ GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
+ GWEN_ArgsType_Int, /* type */
+ "mqttKeepAlive", /* name */
+ 0, /* minnum */
+ 1, /* maxnum */
+ "mk", /* short option */
+ "mqttkeepalive", /* long option */
+ I18S("Specify keepalive time in seconds (defaults: 600)"),
+ I18S("Specify keepalive time in seconds (defaults: 600)")
+ },
+ {
+ GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
+ GWEN_ArgsType_Char, /* type */
+ "dbfile", /* name */
+ 0, /* minnum */
+ 1, /* maxnum */
+ "db", /* short option */
+ "dbfile", /* long option */
+ I18S("Specify DB file to read/write node database"),
+ I18S("Specify DB file to read/write node database")
+ },
+ {
+ GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
+ GWEN_ArgsType_Char, /* type */
+ "writeToFolder", /* name */
+ 0, /* minnum */
+ 1, /* maxnum */
+ "W", /* short option */
+ NULL, /* long option */
+ I18S("Specify folder to write received values to"),
+ I18S("Specify folder to write received values to")
+ },
+ {
+ 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/aqhomed/init.h b/apps/aqhomed/init.h
new file mode 100644
index 0000000..8935383
--- /dev/null
+++ b/apps/aqhomed/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 AQHOMED_INIT_H
+#define AQHOMED_INIT_H
+
+
+#include "./aqhomed.h"
+
+
+
+int AqHomed_Init(AQHOMED *aqh, int argc, char **argv);
+
+
+
+#endif
+
+
diff --git a/apps/aqhomed/loop.c b/apps/aqhomed/loop.c
new file mode 100644
index 0000000..430618a
--- /dev/null
+++ b/apps/aqhomed/loop.c
@@ -0,0 +1,168 @@
+/****************************************************************************
+ * 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 "./aqhomed_p.h"
+#include "./tty_log.h"
+#include "./tty_write.h"
+#include "./db.h"
+
+#include "aqhome/msg/endpoint2_tty.h"
+#include "aqhome/msg/msg_node.h"
+#include "aqhome/msg/msg_value2.h"
+#include "aqhome/ipc/endpoint2_ipc.h"
+#include "aqhome/ipc/msg_ipc_forward.h"
+#include "aqhome/ipc/msg_ipc_value.h"
+#include "aqhome/mqtt/endpoint2_mqttc.h"
+
+#include
+#include
+#include
+#include
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * defines
+ * ------------------------------------------------------------------------------------------------
+ */
+
+#define I18N(msg) msg
+#define I18S(msg) msg
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * forward declarations
+ * ------------------------------------------------------------------------------------------------
+ */
+
+static void _readTtyMessages(AQHOMED *aqh);
+static void _handleTtyMsg(AQHOMED *aqh, const GWEN_MSG *msg);
+static void _forwardTtyMsgToIpcClients(AQHOMED *aqh, const GWEN_MSG *msg);
+static void _forwardValue2MsgToIpc(GWEN_MSG_ENDPOINT2 *ep, const GWEN_MSG *nodeMsg);
+static void _forwardAnyMsgToIpc(GWEN_MSG_ENDPOINT2 *ep, const GWEN_MSG *nodeMsg);
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * implementations
+ * ------------------------------------------------------------------------------------------------
+ */
+
+void AqHomed_Loop(AQHOMED *aqh, int timeoutInMsecs)
+{
+ if (aqh) {
+ GWEN_MsgEndpoint2_ChildrenIoLoop(aqh->rootEndpoint, timeoutInMsecs);
+ _readTtyMessages(aqh);
+
+ if (AQH_NodeDb_IsModified(aqh->nodeDb)) {
+ if (aqh->dbFile) {
+ GWEN_DB_NODE *dbNodeDb;
+
+ dbNodeDb=GWEN_DB_Group_new("nodeDb");
+ AQH_NodeDb_toDb(aqh->nodeDb, dbNodeDb);
+ GWEN_DB_WriteFile(dbNodeDb, aqh->dbFile, GWEN_DB_FLAGS_DEFAULT);
+ GWEN_DB_Group_free(dbNodeDb);
+ }
+ }
+ }
+}
+
+
+
+void _readTtyMessages(AQHOMED *aqh)
+{
+ GWEN_MSG *msg;
+
+ while( (msg=GWEN_MsgEndpoint2_TakeFirstReceivedMessage(aqh->ttyEndpoint)) ) {
+ _handleTtyMsg(aqh, msg);
+ GWEN_Msg_free(msg);
+ }
+}
+
+
+
+void _handleTtyMsg(AQHOMED *aqh, const GWEN_MSG *msg)
+{
+ if (aqh->logFile)
+ AqHomed_LogTtyMsg(aqh, msg);
+ if (aqh->writeFolder)
+ AqHomed_WriteTtyMsg(aqh, msg);
+ if (aqh->nodeDb)
+ AqHomed_NodeMsgToDb(aqh, msg);
+ if (aqh->ipcdEndpoint)
+ _forwardTtyMsgToIpcClients(aqh, msg);
+}
+
+
+
+void _forwardTtyMsgToIpcClients(AQHOMED *aqh, const GWEN_MSG *msg)
+{
+ uint32_t msgGroup;
+
+ msgGroup=AQH_NodeMsg_GetMsgGroup(AQH_NodeMsg_GetMsgType(msg));
+ if (msgGroup) {
+ GWEN_MSG_ENDPOINT2 *ep;
+
+ ep=GWEN_MsgEndpoint2_Tree2_GetFirstChild(aqh->ipcdEndpoint);
+ while(ep) {
+ if (msgGroup & AQH_IpcEndpoint2_GetAcceptedMsgGroups(ep)) {
+ DBG_INFO(NULL, "Endpoint accepts msg group %d", msgGroup);
+ switch(AQH_NodeMsg_GetMsgType(msg)) {
+ case AQH_MSG_TYPE_VALUE2:
+ _forwardValue2MsgToIpc(ep, msg);
+ break;
+ default:
+ _forwardAnyMsgToIpc(ep, msg);
+ break;
+ }
+
+ }
+ ep=GWEN_MsgEndpoint2_Tree2_GetNext(ep);
+ }
+ }
+ else {
+ DBG_ERROR(NULL, "Message type %d not in any message group, ignoring message", AQH_NodeMsg_GetMsgType(msg));
+ }
+}
+
+
+
+void _forwardValue2MsgToIpc(GWEN_MSG_ENDPOINT2 *ep, const GWEN_MSG *nodeMsg)
+{
+ GWEN_MSG *ipcMsg;
+
+ ipcMsg=AQH_ValueIpcMsg_new(AQH_MSGTYPE_IPC_VALUE,
+ AQH_Value2Msg_GetUid(nodeMsg),
+ AQH_Value2Msg_GetValueId(nodeMsg),
+ AQH_Value2Msg_GetValueType(nodeMsg),
+ AQH_Value2Msg_GetValueNom(nodeMsg),
+ AQH_Value2Msg_GetValueDenom(nodeMsg));
+ GWEN_MsgEndpoint2_AddSendMessage(ep, ipcMsg);
+}
+
+
+
+void _forwardAnyMsgToIpc(GWEN_MSG_ENDPOINT2 *ep, const GWEN_MSG *nodeMsg)
+{
+ GWEN_MSG *ipcMsg;
+
+ ipcMsg=AQH_ForwardIpcMsg_new(AQH_MSGTYPE_IPC_FORWARD, GWEN_Msg_GetConstBuffer(nodeMsg), GWEN_Msg_GetBytesInBuffer(nodeMsg));
+ GWEN_MsgEndpoint2_AddSendMessage(ep, ipcMsg);
+}
+
+
+
+
+
diff --git a/apps/aqhomed/loop.h b/apps/aqhomed/loop.h
new file mode 100644
index 0000000..c715173
--- /dev/null
+++ b/apps/aqhomed/loop.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 AQHOMED_LOOP_H
+#define AQHOMED_LOOP_H
+
+
+#include "./aqhomed.h"
+
+
+
+void AqHomed_Loop(AQHOMED *aqh, int timeoutInMsecs);
+
+
+
+#endif
+
+
diff --git a/apps/aqhomed/main.c b/apps/aqhomed/main.c
index 041d8a2..87c1050 100644
--- a/apps/aqhomed/main.c
+++ b/apps/aqhomed/main.c
@@ -13,64 +13,51 @@
#include
#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
+#include "./aqhomed.h"
+#include "./init.h"
+#include "./fini.h"
+#include "./loop.h"
#include
-#include
#include
-#include
-#include
#include
+#include
#ifdef HAVE_SIGNAL_H
# include
#endif
-#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
-
-static int _serve(GWEN_DB_NODE *dbArgs);
-static GWEN_MSG_ENDPOINT_MGR *_setupService(GWEN_DB_NODE *dbArgs);
-
-static int _setupTty(GWEN_MSG_ENDPOINT_MGR *emgr, GWEN_DB_NODE *dbArgs);
-static int _setupLog(GWEN_MSG_ENDPOINT_MGR *emgr, GWEN_DB_NODE *dbArgs);
-static int _setupWriter(GWEN_MSG_ENDPOINT_MGR *emgr, GWEN_DB_NODE *dbArgs);
-static int _setupIpc(GWEN_MSG_ENDPOINT_MGR *emgr, GWEN_DB_NODE *dbArgs);
-static int _setupMqtt(GWEN_MSG_ENDPOINT_MGR *emgr, GWEN_DB_NODE *dbArgs);
-
-static int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs);
-static int _createPidFile(const char *pidFilename);
-
+/* ------------------------------------------------------------------------------------------------
+ * 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 vars
+ * ------------------------------------------------------------------------------------------------
+ */
+
+#ifdef HAVE_SIGNAL_H
static struct sigaction saINT,saTERM, saHUP, saTSTP, saCONT;
#endif
@@ -78,13 +65,16 @@ static int stopService=0;
+/* ------------------------------------------------------------------------------------------------
+ * implementations
+ * ------------------------------------------------------------------------------------------------
+ */
int main(int argc, char **argv)
{
- GWEN_DB_NODE *dbArgs;
int rv;
+ AQHOMED *aqh;
GWEN_GUI *gui;
- const char *s;
rv=GWEN_Init();
if (rv) {
@@ -93,509 +83,47 @@ int main(int argc, char **argv)
}
GWEN_Logger_Open(0, "aqhomed", 0, GWEN_LoggerType_Console, GWEN_LoggerFacility_User);
- GWEN_Logger_SetLevel(0, GWEN_LoggerLevel_Warning);
+ //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;
- }
-
- dbArgs=GWEN_DB_Group_new("arguments");
- rv=_readArgs(argc, argv, dbArgs);
- if (rv<0) {
- DBG_INFO(NULL, "here (%d)", rv);
- return 2;
- }
- else if (rv==1) {
- DBG_INFO(NULL, "Help printed, done");
- return 0;
- }
-
- 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);
-
- rv=_serve(dbArgs);
- if (rv<0) {
- DBG_INFO(NULL, "here (%d)", rv);
- return 2;
- }
-
- GWEN_DB_Group_free(dbArgs);
- GWEN_Gui_SetGui(NULL);
- GWEN_Gui_free(gui);
-
- return 0;
-}
-
-
-
-int _serve(GWEN_DB_NODE *dbArgs)
-{
- const char *pidFile;
- GWEN_MSG_ENDPOINT_MGR *emgr;
- int rv;
- int timeout;
- time_t startTime;
-
- startTime=time(NULL);
-
rv=_setSignalHandlers();
if (rv<0) {
DBG_INFO(NULL, "here (%d)", rv);
return rv;
}
- timeout=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 0);
- pidFile=GWEN_DB_GetCharValue(dbArgs, "pidfile", 0, "aqhomed.pid");
- if (pidFile && *pidFile) {
- rv=_createPidFile(pidFile);
- 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;
}
- emgr=_setupService(dbArgs);
- if (emgr==NULL) {
- DBG_INFO(NULL, "Error setting up service");
- return GWEN_ERROR_GENERIC;
+ gui=GWEN_Gui_CGui_new();
+ GWEN_Gui_SetGui(gui);
+
+ aqh=AqHomed_new();
+ rv=AqHomed_Init(aqh, argc, argv);
+ if (rv<0) {
+ DBG_INFO(NULL, "here (%d)", rv);
+ return 2;
}
while(!stopService) {
DBG_DEBUG(NULL, "Next loop");
- AQH_MsgManager_LoopOnce(emgr);
-
- if (timeout) {
- time_t now;
-
- now=time(NULL);
- if ((now-startTime)>timeout) {
- DBG_INFO(NULL, "Timeout, stopping service");
- break;
- }
- }
+ AqHomed_Loop(aqh, 2000);
}
- if (pidFile && *pidFile)
- remove(pidFile);
+ AqHomed_Fini(aqh);
+ AqHomed_free(aqh);
- GWEN_MsgEndpointMgr_free(emgr);
+ GWEN_Gui_SetGui(NULL);
+ GWEN_Gui_free(gui);
return 0;
}
-GWEN_MSG_ENDPOINT_MGR *_setupService(GWEN_DB_NODE *dbArgs)
-{
- GWEN_MSG_ENDPOINT_MGR *emgr;
- int nodeAddress;
- const char *dbfile;
- int rv;
-
- nodeAddress=GWEN_DB_GetIntValue(dbArgs, "nodeAddress", 0, 240);
- dbfile=GWEN_DB_GetCharValue(dbArgs, "dbfile", 0, "aqhome.db");
-
- emgr=AQH_MsgManager_new(nodeAddress & 0xff);
-
- if (dbfile && *dbfile)
- AQH_MsgManager_SetDbFilename(emgr, dbfile);
-
- rv=_setupTty(emgr, dbArgs);
- if (rv<0) {
- DBG_INFO(NULL, "here (%d)", rv);
- GWEN_MsgEndpointMgr_free(emgr);
- return NULL;
- }
-
- rv=_setupLog(emgr, dbArgs);
- if (rv<0) {
- DBG_INFO(NULL, "here (%d)", rv);
- GWEN_MsgEndpointMgr_free(emgr);
- return NULL;
- }
-
- rv=_setupWriter(emgr, dbArgs);
- if (rv<0) {
- DBG_INFO(NULL, "here (%d)", rv);
- GWEN_MsgEndpointMgr_free(emgr);
- return NULL;
- }
-
- rv=_setupIpc(emgr, dbArgs);
- if (rv<0) {
- DBG_INFO(NULL, "here (%d)", rv);
- GWEN_MsgEndpointMgr_free(emgr);
- return NULL;
- }
-
- rv=_setupMqtt(emgr, dbArgs);
- if (rv<0) {
- DBG_INFO(NULL, "here (%d)", rv);
- GWEN_MsgEndpointMgr_free(emgr);
- return NULL;
- }
-
- return emgr;
-}
-
-
-
-int _setupTty(GWEN_MSG_ENDPOINT_MGR *emgr, GWEN_DB_NODE *dbArgs)
-{
- const char *devicePath;
-
- devicePath=GWEN_DB_GetCharValue(dbArgs, "device", 0, "/dev/ttyUSB0");
- if (devicePath && *devicePath) {
- GWEN_MSG_ENDPOINT *epTty;
-
- epTty=AQH_TtyNodeEndpoint_new(devicePath, AQH_MSGMGR_ENDPOINTGROUP_NODE);
- if (epTty==NULL) {
- DBG_ERROR(NULL, "Error creating endpoint TTY");
- return GWEN_ERROR_GENERIC;
- }
- GWEN_MsgEndpoint_SetAcceptedGroupIds(epTty, AQH_MSGMGR_ENDPOINTGROUP_NODE);
- GWEN_MsgEndpointMgr_AddEndpoint(emgr, epTty);
- }
- else {
- DBG_ERROR(NULL, "Missing device path");
- return GWEN_ERROR_GENERIC;
- }
-
- return 0;
-}
-
-
-
-int _setupLog(GWEN_MSG_ENDPOINT_MGR *emgr, GWEN_DB_NODE *dbArgs)
-{
- const char *logFile;
-
- logFile=GWEN_DB_GetCharValue(dbArgs, "logfile", 0, NULL);
- if (logFile && *logFile) {
- GWEN_MSG_ENDPOINT *epLog;
-
- epLog=AQH_LogEndpoint_new(logFile, AQH_MSGMGR_ENDPOINTGROUP_NODE);
- if (epLog==NULL) {
- DBG_ERROR(AQH_LOGDOMAIN, "Error creating endpoint LOG");
- return GWEN_ERROR_GENERIC;
- }
- GWEN_MsgEndpoint_SetAcceptedGroupIds(epLog, AQH_MSGMGR_ENDPOINTGROUP_NODE);
- GWEN_MsgEndpointMgr_AddEndpoint(emgr, epLog);
- }
- return 0;
-}
-
-
-
-int _setupWriter(GWEN_MSG_ENDPOINT_MGR *emgr, GWEN_DB_NODE *dbArgs)
-{
- const char *writeToFolder;
-
- writeToFolder=GWEN_DB_GetCharValue(dbArgs, "writeToFolder", 0, NULL);
- if (writeToFolder && *writeToFolder) {
- GWEN_MSG_ENDPOINT *epWrite;
-
- epWrite=AQH_WriteEndpoint_new(writeToFolder, AQH_MSGMGR_ENDPOINTGROUP_NODE);
- if (epWrite==NULL) {
- DBG_ERROR(AQH_LOGDOMAIN, "Error creating endpoint WRITE");
- return GWEN_ERROR_GENERIC;
- }
- GWEN_MsgEndpoint_SetAcceptedGroupIds(epWrite, AQH_MSG_TYPEGROUP_INFO | AQH_MSG_TYPEGROUP_VALUES);
- GWEN_MsgEndpointMgr_AddEndpoint(emgr, epWrite);
- }
- return 0;
-}
-
-
-
-int _setupIpc(GWEN_MSG_ENDPOINT_MGR *emgr, GWEN_DB_NODE *dbArgs)
-{
- const char *tcpAddress;
- int tcpPort;
-
- tcpAddress=GWEN_DB_GetCharValue(dbArgs, "tcpAddress", 0, NULL);
- tcpPort=GWEN_DB_GetIntValue(dbArgs, "tcpPort", 0, 45454);
-
- if (tcpAddress && *tcpAddress && tcpPort) {
- GWEN_MSG_ENDPOINT *epTcp;
-
- epTcp=AQH_TcpdIpcNodeEndpoint_new(tcpAddress, tcpPort, NULL, AQH_MSGMGR_ENDPOINTGROUP_NODE|AQH_MSGMGR_ENDPOINTGROUP_IPC);
- if (epTcp==NULL) {
- DBG_ERROR(AQH_LOGDOMAIN, "Error creating endpoint TCP");
- return GWEN_ERROR_GENERIC;
- }
- GWEN_MsgEndpoint_SetAcceptedGroupIds(epTcp, AQH_MSGMGR_ENDPOINTGROUP_NODE | AQH_MSGMGR_ENDPOINTGROUP_IPC);
- GWEN_MsgEndpointMgr_AddEndpoint(emgr, epTcp);
- }
- return 0;
-}
-
-
-
-int _setupMqtt(GWEN_MSG_ENDPOINT_MGR *emgr, GWEN_DB_NODE *dbArgs)
-{
- const char *mqttAddress;
- int mqttPort;
- const char *mqttClientId;
- const char *mqttTopicPrefix;
- int mqttKeepAlive;
-
- mqttAddress=GWEN_DB_GetCharValue(dbArgs, "mqttAddress", 0, NULL);
- mqttPort=GWEN_DB_GetIntValue(dbArgs, "mqttPort", 0, 1883);
- mqttClientId=GWEN_DB_GetCharValue(dbArgs, "mqttClientId", 0, "aqhomed");
- mqttTopicPrefix=GWEN_DB_GetCharValue(dbArgs, "mqttTopicPrefix", 0, "aqhome/sensors");
- mqttKeepAlive=GWEN_DB_GetIntValue(dbArgs, "mqttKeepAlive", 0, 600);
-
- if (mqttAddress && *mqttAddress && mqttPort) {
- GWEN_MSG_ENDPOINT *epMqtt;
-
- DBG_INFO(AQH_LOGDOMAIN, "Connecting to %s (port %d)", mqttAddress, mqttPort);
- epMqtt=AQH_MqttClientEndpoint_new(mqttAddress, mqttPort, NULL, AQH_MSGMGR_ENDPOINTGROUP_MQTT);
- if (epMqtt==NULL) {
- DBG_ERROR(AQH_LOGDOMAIN, "Error creating endpoint TCP");
- return GWEN_ERROR_GENERIC;
- }
- GWEN_MsgEndpoint_SetAcceptedGroupIds(epMqtt, AQH_MSGMGR_ENDPOINTGROUP_NODE | AQH_MSGMGR_ENDPOINTGROUP_MQTT);
- if (mqttClientId && *mqttClientId)
- AQH_MqttClientEndpoint_SetClientId(epMqtt, mqttClientId);
- if (mqttTopicPrefix && *mqttTopicPrefix)
- AQH_MqttClientEndpoint_SetTopicPrefix(epMqtt, mqttTopicPrefix);
- AQH_MqttClientEndpoint_SetKeepAliveTime(epMqtt, mqttKeepAlive);
-
- GWEN_MsgEndpointMgr_AddEndpoint(emgr, epMqtt);
- }
- 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 */
- "device", /* name */
- 0, /* minnum */
- 1, /* maxnum */
- "d", /* short option */
- "device", /* long option */
- I18S("Specify the device to communicate with (e.g. /dev/ttyUSB0)"),
- I18S("Specify the device to communicate with (e.g. /dev/ttyUSB0)")
- },
- {
- GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
- GWEN_ArgsType_Int, /* type */
- "nodeAddress", /* name */
- 0, /* minnum */
- 1, /* maxnum */
- "n", /* short option */
- "node", /* long option */
- I18S("Specify the node address for the AqHome node adaptor (default 240)"),
- I18S("Specify the node address for the AqHome node adaptor (default 240)")
- },
- {
- GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
- GWEN_ArgsType_Char, /* type */
- "logFile", /* name */
- 0, /* minnum */
- 1, /* maxnum */
- "l", /* short option */
- "logfile", /* long option */
- I18S("Specify a logfile to log received messages to"),
- I18S("Specify a logfile to log received messages to")
- },
- {
- 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 */
- "mqttAddress", /* name */
- 0, /* minnum */
- 1, /* maxnum */
- "ma", /* short option */
- "mqttaddress", /* long option */
- I18S("Specify the address of the MQTT server to connect to (disabled if missing)"),
- I18S("Specify the address of the MQTT server to connect to (disabled if missing)")
- },
- {
- GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
- GWEN_ArgsType_Int, /* type */
- "mqttPort", /* name */
- 0, /* minnum */
- 1, /* maxnum */
- "mp", /* short option */
- "mqttport", /* long option */
- I18S("Specify the port of the MQTT server (default: 1883)"),
- I18S("Specify the port of the MQTT server (default: 1883)")
- },
- {
- GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
- GWEN_ArgsType_Char, /* type */
- "mqttClientId", /* name */
- 0, /* minnum */
- 1, /* maxnum */
- NULL, /* short option */
- "mqttclientid", /* long option */
- I18S("Specify client id for the MQTT server (default: \"aqhomed\")"),
- I18S("Specify client id for the MQTT server (default: \"aqhomed\")")
- },
- {
- GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
- GWEN_ArgsType_Char, /* type */
- "mqttTopicPrefix", /* name */
- 0, /* minnum */
- 1, /* maxnum */
- "mt", /* short option */
- "mqtttopicprefix", /* long option */
- I18S("Specify prefix of MQTT topics when publishing (defaults to \"aqhome/sensors\")"),
- I18S("Specify prefix of MQTT topics when publishing (defaults to \"aqhome/sensors\")")
- },
- {
- GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
- GWEN_ArgsType_Int, /* type */
- "mqttKeepAlive", /* name */
- 0, /* minnum */
- 1, /* maxnum */
- "mk", /* short option */
- "mqttkeepalive", /* long option */
- I18S("Specify keepalive time in seconds (defaults: 600)"),
- I18S("Specify keepalive time in seconds (defaults: 600)")
- },
- {
- GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
- GWEN_ArgsType_Char, /* type */
- "dbfile", /* name */
- 0, /* minnum */
- 1, /* maxnum */
- "db", /* short option */
- "dbfile", /* long option */
- I18S("Specify DB file to read/write node database"),
- I18S("Specify DB file to read/write node database")
- },
- {
- GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */
- GWEN_ArgsType_Char, /* type */
- "writeToFolder", /* name */
- 0, /* minnum */
- 1, /* maxnum */
- "W", /* short option */
- NULL, /* long option */
- I18S("Specify folder to write received values to"),
- I18S("Specify folder to write received values to")
- },
- {
- 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;
-}
-
-
-
int _setSignalHandlers(void)
{
#ifdef HAVE_SIGNAL_H
@@ -661,44 +189,3 @@ void _signalHandler(int s)
}
-
-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;
-}
-
-
-
-
-
-
-
diff --git a/apps/aqhomed/tty_log.c b/apps/aqhomed/tty_log.c
new file mode 100644
index 0000000..0ac05aa
--- /dev/null
+++ b/apps/aqhomed/tty_log.c
@@ -0,0 +1,131 @@
+/****************************************************************************
+ * 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 "./tty_log.h"
+#include "./aqhomed_p.h"
+
+#include "aqhome/msg/msg_value.h"
+#include "aqhome/msg/msg_value2.h"
+#include "aqhome/msg/msg_sendstats.h"
+#include "aqhome/msg/msg_recvstats.h"
+#include "aqhome/msg/msg_memstats.h"
+#include "aqhome/msg/msg_sysstats.h"
+#include "aqhome/msg/msg_ping.h"
+#include "aqhome/msg/msg_pong.h"
+#include "aqhome/msg/msg_needaddr.h"
+#include "aqhome/msg/msg_claimaddr.h"
+#include "aqhome/msg/msg_haveaddr.h"
+#include "aqhome/msg/msg_denyaddr.h"
+#include "aqhome/msg/msg_device.h"
+#include "aqhome/msg/msg_flashready.h"
+#include "aqhome/msg/msg_flashstart.h"
+#include "aqhome/msg/msg_flashresponse.h"
+#include "aqhome/msg/msg_flashend.h"
+#include "aqhome/msg/msg_flashdata.h"
+#include "aqhome/msg/msg_reboot.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * forward declarations
+ * ------------------------------------------------------------------------------------------------
+ */
+
+static void _writeToLogFile(const char *filename, const char *txt);
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * implementations
+ * ------------------------------------------------------------------------------------------------
+ */
+
+void AqHomed_LogTtyMsg(AQHOMED *aqh, const GWEN_MSG *msg)
+{
+ if (aqh && aqh->logFile) {
+ uint8_t msgType;
+ int msgIsValid;
+ GWEN_BUFFER *dbuf;
+ GWEN_TIME *ti;
+
+ dbuf=GWEN_Buffer_new(0, 256, 0, 1);
+ ti=GWEN_CurrentTime();
+ GWEN_Time_toString(ti, "YYYY-MM-DD hh:mm:ss ", dbuf);
+ GWEN_Time_free(ti);
+ ti=NULL;
+
+ msgIsValid=(AQH_NodeMsg_IsChecksumValid(msg) && AQH_NodeMsg_IsMsgComplete(msg));
+ msgType=AQH_NodeMsg_GetMsgType(msg);
+
+ if (msgIsValid) {
+ switch(msgType) {
+ case AQH_MSG_TYPE_PING: AQH_PingMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ case AQH_MSG_TYPE_PONG: AQH_PongMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ case AQH_MSG_TYPE_COMSENDSTATS: AQH_SendStatsMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ case AQH_MSG_TYPE_COMRECVSTATS: AQH_RecvStatsMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ case AQH_MSG_TYPE_TWIBUSMEMBER: AQH_NodeMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ case AQH_MSG_TYPE_DEBUG: AQH_NodeMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ case AQH_MSG_TYPE_VALUE: AQH_ValueMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ case AQH_MSG_TYPE_VALUE2: AQH_Value2Msg_DumpToBuffer(msg, dbuf, "received"); break;
+ case AQH_MSG_TYPE_NEED_ADDRESS: AQH_NeedAddrMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ case AQH_MSG_TYPE_CLAIM_ADDRESS: AQH_ClaimAddrMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ case AQH_MSG_TYPE_HAVE_ADDRESS: AQH_HaveAddrMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ case AQH_MSG_TYPE_DENY_ADDRESS: AQH_DenyAddrMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ case AQH_MSG_TYPE_DEVICE: AQH_DeviceMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ case AQH_MSG_TYPE_MEMSTATS: AQH_MemStatsMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ case AQH_MSG_TYPE_SYSSTATS: AQH_SysStatsMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ case AQH_MSG_TYPE_FLASH_READY: AQH_FlashReadyMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ case AQH_MSG_TYPE_FLASH_START: AQH_FlashStartMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ case AQH_MSG_TYPE_FLASH_RSP: AQH_FlashResponseMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ case AQH_MSG_TYPE_FLASH_END: AQH_FlashEndMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ case AQH_MSG_TYPE_FLASH_DATA: AQH_FlashDataMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ case AQH_MSG_TYPE_REBOOT_REQ: AQH_RebootRequestMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ case AQH_MSG_TYPE_REBOOT_RSP: AQH_RebootResponseMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ default: AQH_NodeMsg_DumpToBuffer(msg, dbuf, "received"); break;
+ }
+ }
+ else {
+ AQH_NodeMsg_DumpToBuffer(msg, dbuf, "(invalid) received");
+ }
+ _writeToLogFile(aqh->logFile, GWEN_Buffer_GetStart(dbuf));
+ GWEN_Buffer_free(dbuf);
+ }
+}
+
+
+
+void _writeToLogFile(const char *filename, const char *txt)
+{
+ if (txt && *txt) {
+ FILE *f;
+
+ f=fopen(filename, "a+");
+ if (f) {
+ if (1!=fwrite(txt, strlen(txt), 1, f)) {
+ DBG_ERROR(AQH_LOGDOMAIN, "Error logging.");
+ }
+ fclose(f);
+ }
+ }
+}
+
+
+
diff --git a/apps/aqhomed/tty_log.h b/apps/aqhomed/tty_log.h
new file mode 100644
index 0000000..29add37
--- /dev/null
+++ b/apps/aqhomed/tty_log.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 AQHOMED_TTY_LOG_H
+#define AQHOMED_TTY_LOG_H
+
+
+#include "./aqhomed.h"
+
+
+
+void AqHomed_LogTtyMsg(AQHOMED *aqh, const GWEN_MSG *msg);
+
+
+
+#endif
+
+
diff --git a/apps/aqhomed/tty_write.c b/apps/aqhomed/tty_write.c
new file mode 100644
index 0000000..3c6fb3d
--- /dev/null
+++ b/apps/aqhomed/tty_write.c
@@ -0,0 +1,225 @@
+/****************************************************************************
+ * 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 "./tty_write.h"
+#include "./aqhomed_p.h"
+
+#include "aqhome/msg/msg_value2.h"
+#include "aqhome/msg/msg_sendstats.h"
+#include "aqhome/msg/msg_recvstats.h"
+
+#include
+#include
+
+#include
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * forward declarations
+ * ------------------------------------------------------------------------------------------------
+ */
+
+static void _processValue2Message(AQHOMED *aqh, const GWEN_MSG *nodeMsg);
+static void _processSendStatsMessage(AQHOMED *aqh, const GWEN_MSG *nodeMsg);
+static void _processRecvStatsMessage(AQHOMED *aqh, const GWEN_MSG *nodeMsg);
+static void _writeDouble(AQHOMED *aqh, uint32_t uid, int valueId, const char *valuePath, double v);
+static void _writeInt(AQHOMED *aqh, uint32_t uid, int valueId, const char *valuePath, int v);
+static void _writeString(AQHOMED *aqh, uint32_t uid, int valueId, const char *valuePath, const char *v);
+static void _writeToFile(const char *filename, const char *txt);
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * implementations
+ * ------------------------------------------------------------------------------------------------
+ */
+
+void AqHomed_WriteTtyMsg(AQHOMED *aqh, const GWEN_MSG *msg)
+{
+ if (aqh && aqh->writeFolder) {
+ switch(AQH_NodeMsg_GetMsgType(msg)) {
+ case AQH_MSG_TYPE_VALUE2:
+ _processValue2Message(aqh, msg);
+ break;
+ case AQH_MSG_TYPE_COMSENDSTATS:
+ _processSendStatsMessage(aqh, msg);
+ break;
+ case AQH_MSG_TYPE_COMRECVSTATS:
+ _processRecvStatsMessage(aqh, msg);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+
+
+void _processValue2Message(AQHOMED *aqh, const GWEN_MSG *nodeMsg)
+{
+ const char *sType;
+
+ sType=AQH_Value2Msg_GetValueTypeName(nodeMsg);
+ if (sType && *sType) {
+ if (AQH_Value2Msg_GetValueType(nodeMsg)==AQH_MSG_VALUE2_TYPE_DOOR)
+ _writeString(aqh,
+ AQH_Value2Msg_GetUid(nodeMsg),
+ AQH_Value2Msg_GetValueId(nodeMsg),
+ sType,
+ AQH_Value2Msg_GetValueAsWindowStateString(nodeMsg));
+ else
+ _writeDouble(aqh,
+ AQH_Value2Msg_GetUid(nodeMsg),
+ AQH_Value2Msg_GetValueId(nodeMsg),
+ sType,
+ AQH_Value2Msg_GetValue(nodeMsg));
+ }
+ _writeDouble(aqh,
+ AQH_Value2Msg_GetUid(nodeMsg),
+ AQH_Value2Msg_GetValueId(nodeMsg),
+ "value",
+ AQH_Value2Msg_GetValue(nodeMsg));
+}
+
+
+
+void _processSendStatsMessage(AQHOMED *aqh, const GWEN_MSG *nodeMsg)
+{
+ uint16_t packetsOutInt;
+
+ packetsOutInt=AQH_SendStatsMsg_GetPacketsOut(nodeMsg);
+ if (packetsOutInt) {
+ double packetsOut;
+ double collisions;
+ double busy;
+ double collisionsPercentage=0.0;
+ double busyPercentage=0.0;
+
+ packetsOut=(double) packetsOutInt;
+ collisions=(double)AQH_SendStatsMsg_GetCollisions(nodeMsg);
+ busy=(double)AQH_SendStatsMsg_GetBusyErrors(nodeMsg);
+
+ collisionsPercentage=collisions*100.0/packetsOut;
+ busyPercentage=busy*100.0/packetsOut;
+
+ _writeInt(aqh, AQH_SendStatsMsg_GetUid(nodeMsg), 0, "net/packetsOut", packetsOutInt);
+ _writeInt(aqh, AQH_SendStatsMsg_GetUid(nodeMsg), 0, "net/collisions", (int) AQH_SendStatsMsg_GetCollisions(nodeMsg));
+ _writeDouble(aqh, AQH_SendStatsMsg_GetUid(nodeMsg), 0, "net/collisionsPercent", collisionsPercentage);
+ _writeDouble(aqh, AQH_SendStatsMsg_GetUid(nodeMsg), 0, "net/busyPercent", busyPercentage);
+ }
+}
+
+
+
+void _processRecvStatsMessage(AQHOMED *aqh, const GWEN_MSG *nodeMsg)
+{
+ uint16_t packetsInInt;
+
+ packetsInInt=AQH_RecvStatsMsg_GetPacketsIn(nodeMsg);
+ if (packetsInInt) {
+ double packetsIn;
+ double crcErrors;
+ double ioErrors;
+ double crcErrorsPercentage=0.0;
+ double ioErrorsPercentage=0.0;
+
+ packetsIn=(double) packetsInInt;
+ crcErrors=(double)AQH_RecvStatsMsg_GetCrcErrors(nodeMsg);
+ ioErrors=(double)AQH_RecvStatsMsg_GetIoErrors(nodeMsg);
+
+ crcErrorsPercentage=crcErrors*100.0/packetsIn;
+ ioErrorsPercentage=ioErrors*100.0/packetsIn;
+
+ _writeInt(aqh, AQH_RecvStatsMsg_GetUid(nodeMsg), 0, "net/packetsIn", packetsInInt);
+ _writeInt(aqh, AQH_RecvStatsMsg_GetUid(nodeMsg), 0, "net/crcerrors", (int) AQH_RecvStatsMsg_GetCrcErrors(nodeMsg));
+ _writeInt(aqh, AQH_RecvStatsMsg_GetUid(nodeMsg), 0, "net/ioerrors", (int) AQH_RecvStatsMsg_GetIoErrors(nodeMsg));
+ _writeDouble(aqh, AQH_RecvStatsMsg_GetUid(nodeMsg), 0, "net/crcerrorsPercent", crcErrorsPercentage);
+ _writeDouble(aqh, AQH_RecvStatsMsg_GetUid(nodeMsg), 0, "net/ioerrorsPercent", ioErrorsPercentage);
+ }
+}
+
+
+
+void _writeDouble(AQHOMED *aqh, uint32_t uid, int valueId, const char *valuePath, double v)
+{
+ char numBuf[16];
+
+ snprintf(numBuf, sizeof(numBuf)-1, "%f", v);
+ numBuf[sizeof(numBuf)-1]=0;
+ _writeString(aqh, uid, valueId, valuePath, numBuf);
+}
+
+
+
+void _writeInt(AQHOMED *aqh, uint32_t uid, int valueId, const char *valuePath, int v)
+{
+ char numBuf[16];
+
+ snprintf(numBuf, sizeof(numBuf)-1, "%d", v);
+ numBuf[sizeof(numBuf)-1]=0;
+ _writeString(aqh, uid, valueId, valuePath, numBuf);
+}
+
+
+
+void _writeString(AQHOMED *aqh, uint32_t uid, int valueId, const char *valuePath, const char *v)
+{
+ GWEN_BUFFER *bufFilename;
+
+ bufFilename=GWEN_Buffer_new(0, 64, 0, 1);
+ if (valueId>0)
+ GWEN_Buffer_AppendArgs(bufFilename, "%s/%08x/%d/%s", aqh->writeFolder, uid, valueId, valuePath);
+ else
+ GWEN_Buffer_AppendArgs(bufFilename, "%s/%08x/%s", aqh->writeFolder, uid, valuePath);
+ _writeToFile(GWEN_Buffer_GetStart(bufFilename), v);
+ GWEN_Buffer_free(bufFilename);
+}
+
+
+
+void _writeToFile(const char *filename, const char *txt)
+{
+ if (txt && *txt) {
+ GWEN_BUFFER *tmpNameBuf;
+ int rv;
+
+ tmpNameBuf=GWEN_Buffer_new(0, 256, 0, 1);
+ GWEN_Buffer_AppendString(tmpNameBuf, filename);
+ GWEN_Buffer_AppendString(tmpNameBuf, ".tmp");
+
+ rv=GWEN_Directory_GetPath(GWEN_Buffer_GetStart(tmpNameBuf), GWEN_PATH_FLAGS_VARIABLE);
+ if (rv<0) {
+ DBG_INFO(NULL, "Error getting path for %s (%d)", GWEN_Buffer_GetStart(tmpNameBuf), rv);
+ }
+ else {
+ FILE *f;
+
+ f=fopen(GWEN_Buffer_GetStart(tmpNameBuf), "w");
+ if (f) {
+ if (1!=fwrite(txt, strlen(txt), 1, f)) {
+ DBG_ERROR(NULL, "Error writing.");
+ fclose(f);
+ }
+ else {
+ fclose(f);
+ rename(GWEN_Buffer_GetStart(tmpNameBuf), filename);
+ }
+ }
+ }
+ GWEN_Buffer_free(tmpNameBuf);
+ }
+}
+
+
+
diff --git a/apps/aqhomed/tty_write.h b/apps/aqhomed/tty_write.h
new file mode 100644
index 0000000..40fc111
--- /dev/null
+++ b/apps/aqhomed/tty_write.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 AQHOMED_TTY_WRITE_H
+#define AQHOMED_TTY_WRITE_H
+
+
+#include "./aqhomed.h"
+
+
+
+void AqHomed_WriteTtyMsg(AQHOMED *aqh, const GWEN_MSG *msg);
+
+
+
+#endif
+
+
diff --git a/aqhome/ipc/0BUILD b/aqhome/ipc/0BUILD
index dd84d0f..71d3962 100644
--- a/aqhome/ipc/0BUILD
+++ b/aqhome/ipc/0BUILD
@@ -45,6 +45,7 @@
+ endpoint2_ipc.h
endpoint_node_ipc.h
endpoint_node_ipc_tcpd.h
endpoint_ipc_tcpc.h
@@ -61,12 +62,14 @@
+ endpoint2_ipc_p.h
$(local/typefiles)
+ endpoint2_ipc.c
endpoint_node_ipc.c
endpoint_node_ipc_tcpd.c
endpoint_ipc_tcpc.c
diff --git a/aqhome/ipc/endpoint2_ipc.c b/aqhome/ipc/endpoint2_ipc.c
new file mode 100644
index 0000000..274d17c
--- /dev/null
+++ b/aqhome/ipc/endpoint2_ipc.c
@@ -0,0 +1,139 @@
+/****************************************************************************
+ * 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/ipc/endpoint2_ipc_p.h"
+
+#include
+#include
+
+
+
+
+#define AQH_MSG_ENDPOINT2_IPC_NAME "ipc"
+
+
+GWEN_INHERIT(GWEN_MSG_ENDPOINT, AQH_ENDPOINT2_IPC)
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * forward declarations
+ * ------------------------------------------------------------------------------------------------
+ */
+
+static void GWENHYWFAR_CB _freeData(void *bp, void *p);
+
+
+
+/* ------------------------------------------------------------------------------------------------
+ * implementations
+ * ------------------------------------------------------------------------------------------------
+ */
+
+void AQH_IpcEndpoint2_Extend(GWEN_MSG_ENDPOINT2 *ep)
+{
+ AQH_ENDPOINT2_IPC *xep;
+
+ GWEN_NEW_OBJECT(AQH_ENDPOINT2_IPC, xep);
+ GWEN_INHERIT_SETDATA(GWEN_MSG_ENDPOINT2, AQH_ENDPOINT2_IPC, ep, xep, _freeData);
+}
+
+
+
+void _freeData(void *bp, void *p)
+{
+ AQH_ENDPOINT2_IPC *xep;
+
+ xep=(AQH_ENDPOINT2_IPC*) p;
+ GWEN_FREE_OBJECT(xep);
+}
+
+
+
+uint32_t AQH_IpcEndpoint2_GetAcceptedMsgGroups(const GWEN_MSG_ENDPOINT2 *ep)
+{
+ if (ep) {
+ AQH_ENDPOINT2_IPC *xep;
+
+ xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT2, AQH_ENDPOINT2_IPC, ep);
+ if (xep) {
+ return xep->acceptedMsgGroups;
+ }
+ }
+
+ return 0;
+}
+
+
+
+void AQH_IpcEndpoint2_SetAcceptedMsgGroups(GWEN_MSG_ENDPOINT2 *ep, uint32_t i)
+{
+ if (ep) {
+ AQH_ENDPOINT2_IPC *xep;
+
+ xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT2, AQH_ENDPOINT2_IPC, ep);
+ if (xep)
+ xep->acceptedMsgGroups=i;
+ }
+}
+
+
+
+void AQH_IpcEndpoint2_AddAcceptedMsgGroups(GWEN_MSG_ENDPOINT2 *ep, uint32_t i)
+{
+ if (ep) {
+ AQH_ENDPOINT2_IPC *xep;
+
+ xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT2, AQH_ENDPOINT2_IPC, ep);
+ if (xep)
+ xep->acceptedMsgGroups|=i;
+ }
+}
+
+
+
+void AQH_IpcEndpoint2_SubAcceptedMsgGroups(GWEN_MSG_ENDPOINT2 *ep, uint32_t i)
+{
+ if (ep) {
+ AQH_ENDPOINT2_IPC *xep;
+
+ xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT2, AQH_ENDPOINT2_IPC, ep);
+ if (xep)
+ xep->acceptedMsgGroups&=~i;
+ }
+}
+
+
+
+GWEN_MSG_ENDPOINT2 *AQH_IpcEndpoint2_CreateIpcTcpClient(const char *host, int port, const char *name, int groupId)
+{
+ GWEN_MSG_ENDPOINT2 *ep;
+
+ ep=GWEN_IpcEndpoint2_CreateIpcTcpClient(host, port, name?name:AQH_MSG_ENDPOINT2_IPC_NAME, groupId);
+ AQH_IpcEndpoint2_Extend(ep);
+ return ep;
+}
+
+
+
+GWEN_MSG_ENDPOINT2 *AQH_IpcEndpoint2_CreateIpcTcpServiceForSocket(GWEN_SOCKET *sk, const char *name, int groupId)
+{
+ GWEN_MSG_ENDPOINT2 *ep;
+
+ ep=GWEN_IpcEndpoint2_CreateIpcTcpServiceForSocket(sk, name?name:AQH_MSG_ENDPOINT2_IPC_NAME, groupId);
+ AQH_IpcEndpoint2_Extend(ep);
+ return ep;
+}
+
+
+
diff --git a/aqhome/ipc/endpoint2_ipc.h b/aqhome/ipc/endpoint2_ipc.h
new file mode 100644
index 0000000..3203a63
--- /dev/null
+++ b/aqhome/ipc/endpoint2_ipc.h
@@ -0,0 +1,31 @@
+/****************************************************************************
+ * 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_ENDPOINT2_IPC_H
+#define AQH_ENDPOINT2_IPC_H
+
+
+#include
+
+#include
+
+
+
+AQHOME_API void AQH_IpcEndpoint2_Extend(GWEN_MSG_ENDPOINT2 *ep);
+
+AQHOME_API GWEN_MSG_ENDPOINT2 *AQH_IpcEndpoint2_CreateIpcTcpClient(const char *host, int port, const char *name, int groupId);
+AQHOME_API GWEN_MSG_ENDPOINT2 *AQH_IpcEndpoint2_CreateIpcTcpServiceForSocket(GWEN_SOCKET *sk, const char *name, int groupId);
+
+AQHOME_API uint32_t AQH_IpcEndpoint2_GetAcceptedMsgGroups(const GWEN_MSG_ENDPOINT2 *ep);
+AQHOME_API void AQH_IpcEndpoint2_SetAcceptedMsgGroups(GWEN_MSG_ENDPOINT2 *ep, uint32_t i);
+AQHOME_API void AQH_IpcEndpoint2_AddAcceptedMsgGroups(GWEN_MSG_ENDPOINT2 *ep, uint32_t i);
+AQHOME_API void AQH_IpcEndpoint2_SubAcceptedMsgGroups(GWEN_MSG_ENDPOINT2 *ep, uint32_t i);
+
+
+#endif
+
diff --git a/aqhome/ipc/endpoint2_ipc_p.h b/aqhome/ipc/endpoint2_ipc_p.h
new file mode 100644
index 0000000..aa46ce4
--- /dev/null
+++ b/aqhome/ipc/endpoint2_ipc_p.h
@@ -0,0 +1,38 @@
+/****************************************************************************
+ * 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_ENDPOINT2_IPC_P_H
+#define AQH_ENDPOINT2_IPC_P_H
+
+
+#include
+
+#include
+
+#include "aqhome/ipc/endpoint2_ipc.h"
+
+
+typedef struct AQH_ENDPOINT2_IPC AQH_ENDPOINT2_IPC;
+struct AQH_ENDPOINT2_IPC {
+ uint32_t acceptedMsgGroups;
+};
+
+
+
+
+
+#endif
+
+
+
+
+
+
+
+
+
diff --git a/aqhome/mqtt/endpoint2_mqttc.c b/aqhome/mqtt/endpoint2_mqttc.c
index 71d8d94..6db06b0 100644
--- a/aqhome/mqtt/endpoint2_mqttc.c
+++ b/aqhome/mqtt/endpoint2_mqttc.c
@@ -155,6 +155,34 @@ uint16_t AQH_MqttClientEndpoint2_GetNextPacketId(const GWEN_MSG_ENDPOINT2 *ep)
+const char *AQH_MqttClientEndpoint2_GetTopicPrefix(const GWEN_MSG_ENDPOINT2 *ep)
+{
+ if (ep) {
+ GWEN_MSG_ENDPOINT2 *epChild;
+
+ epChild=GWEN_MsgEndpoint2_Tree2_GetFirstChild(ep);
+ if (epChild)
+ return AQH_MqttEndpoint2_GetTopicPrefix(epChild);
+ }
+ return NULL;
+}
+
+
+
+void AQH_MqttClientEndpoint2_SetTopicPrefix(GWEN_MSG_ENDPOINT2 *ep, const char *s)
+{
+ if (ep) {
+ GWEN_MSG_ENDPOINT2 *epChild;
+
+ epChild=GWEN_MsgEndpoint2_Tree2_GetFirstChild(ep);
+ if (epChild)
+ AQH_MqttEndpoint2_SetTopicPrefix(epChild, s);
+ }
+}
+
+
+
+
void _addSockets(GWEN_MSG_ENDPOINT2 *ep, GWEN_SOCKETSET *readSet, GWEN_SOCKETSET *writeSet, GWEN_SOCKETSET *xSet)
diff --git a/aqhome/mqtt/endpoint2_mqttc.h b/aqhome/mqtt/endpoint2_mqttc.h
index 8324c29..6597295 100644
--- a/aqhome/mqtt/endpoint2_mqttc.h
+++ b/aqhome/mqtt/endpoint2_mqttc.h
@@ -31,6 +31,9 @@ AQHOME_API void AQH_MqttClientEndpoint2_SetKeepAliveTime(GWEN_MSG_ENDPOINT2 *ep,
AQHOME_API uint16_t AQH_MqttClientEndpoint2_GetNextPacketId(const GWEN_MSG_ENDPOINT2 *ep);
+AQHOME_API const char *AQH_MqttClientEndpoint2_GetTopicPrefix(const GWEN_MSG_ENDPOINT2 *ep);
+AQHOME_API void AQH_MqttClientEndpoint2_SetTopicPrefix(GWEN_MSG_ENDPOINT2 *ep, const char *s);
+
AQHOME_API int AQH_MqttClientEndpoint2_StartConnect(GWEN_MSG_ENDPOINT2 *ep);
diff --git a/aqhome/msg/endpoint2_tty.c b/aqhome/msg/endpoint2_tty.c
index 5a9764c..1377dc8 100644
--- a/aqhome/msg/endpoint2_tty.c
+++ b/aqhome/msg/endpoint2_tty.c
@@ -69,7 +69,7 @@ static int _isAttnLow(GWEN_MSG_ENDPOINT2 *ep);
*/
-GWEN_MSG_ENDPOINT2 *AQH_TtyEndpoint_new(const char *devicePath, int groupId)
+GWEN_MSG_ENDPOINT2 *AQH_TtyEndpoint2_new(const char *devicePath, int groupId)
{
GWEN_MSG_ENDPOINT2 *ep;
AQH_MSG_ENDPOINT2_TTY *xep;
@@ -117,7 +117,7 @@ void _addSockets(GWEN_MSG_ENDPOINT2 *ep, GWEN_SOCKETSET *readSet, GWEN_SOCKETSET
int rv;
/* (re)connect, set state */
- DBG_INFO(GWEN_LOGDOMAIN, "Starting to (re-)connect");
+ DBG_INFO(AQH_LOGDOMAIN, "Starting to (re-)connect");
rv=GWEN_TtyEndpoint2_Connect(ep);
if (rv<0) {
DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv);
@@ -141,6 +141,7 @@ int GWEN_TtyEndpoint2_Connect(GWEN_MSG_ENDPOINT2 *ep)
if (GWEN_MsgEndpoint2_GetState(ep)==GWEN_MSG_ENDPOINT_STATE_UNCONNECTED) {
int fd;
+ DBG_INFO(AQH_LOGDOMAIN, "Connecting TTY device");
fd=_openDevice(ep);
if (fd<0) {
DBG_INFO(AQH_LOGDOMAIN, "here (%d)", fd);
@@ -152,6 +153,8 @@ int GWEN_TtyEndpoint2_Connect(GWEN_MSG_ENDPOINT2 *ep)
sk=GWEN_Socket_fromFile(fd);
GWEN_MsgEndpoint2_SetSocket(ep, sk);
GWEN_MsgEndpoint2_SetState(ep, GWEN_MSG_ENDPOINT_STATE_CONNECTED);
+ GWEN_MsgEndpoint2_DiscardInput(ep);
+ _attnHigh(ep);
return 0;
}
}
@@ -171,8 +174,9 @@ int _getSocketFd(GWEN_MSG_ENDPOINT2 *ep)
GWEN_SOCKET *sk;
sk=GWEN_MsgEndpoint2_GetSocket(ep);
- if (sk)
+ if (sk) {
return GWEN_Socket_GetSocketInt(sk);
+ }
}
return GWEN_ERROR_GENERIC;
}
@@ -188,11 +192,13 @@ int _openDevice(GWEN_MSG_ENDPOINT2 *ep)
xep=GWEN_INHERIT_GETDATA(GWEN_MSG_ENDPOINT2, AQH_MSG_ENDPOINT2_TTY, ep);
assert(xep);
+ DBG_INFO(AQH_LOGDOMAIN, "Opening device %s", xep->deviceName);
fd=open(xep->deviceName, O_NOCTTY | O_NDELAY | O_RDWR);
if (fd<0) {
DBG_ERROR(AQH_LOGDOMAIN, "Error on open(%s): %s (%d)", xep->deviceName, strerror(errno), errno);
return GWEN_ERROR_IO;
}
+ DBG_INFO(AQH_LOGDOMAIN, "Device %s open (socket %d)", xep->deviceName, fd);
rv=tcgetattr(fd, &(xep->previousOptions));
if (rv<0) {
DBG_ERROR(AQH_LOGDOMAIN, "Error on tcgetattr(%s): %s (%d)", xep->deviceName, strerror(errno), errno);
@@ -284,7 +290,7 @@ int _getBytesNeededForMessage(GWEN_UNUSED GWEN_MSG_ENDPOINT2 *ep, GWEN_MSG *msg)
bytesInMsg=GWEN_Msg_GetBytesInBuffer(msg);
if (bytesInMsg