/**************************************************************************** * 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_ipc.h" #include "./aqhomed_p.h" #include "./tty_log.h" #include "./db.h" #include "aqhome/msg/endpoint_tty.h" #include "aqhome/msg/msg_node.h" #include "aqhome/msg/msg_value2.h" #include "aqhome/msg/msg_value3.h" #include "aqhome/msg/msg_ping.h" #include "aqhome/ipc/endpoint_ipc.h" #include "aqhome/ipc/nodes/ipc_nodes.h" #include "aqhome/ipc/nodes/msg_ipc_forward.h" #include "aqhome/ipc/nodes/msg_ipc_value.h" #include "aqhome/ipc/nodes/msg_ipc_ping.h" #include "aqhome/ipc/nodes/msg_ipc_setaccmsggrps.h" #include "aqhome/ipc/nodes/msg_ipc_getdevices_rsp.h" #include "aqhome/ipc/data/ipc_data.h" #include "aqhome/ipc/data/msg_data_set.h" #include "aqhome/ipc/msg_ipc_result.h" #include #include #include #include #include /* ------------------------------------------------------------------------------------------------ * defines * ------------------------------------------------------------------------------------------------ */ #define I18N(msg) msg #define I18S(msg) msg /* ------------------------------------------------------------------------------------------------ * forward declarations * ------------------------------------------------------------------------------------------------ */ static void _handleIpcEndpoint(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep); static void _handleIpcMsg(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg); void _handleIpcMsgPing(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg); void _handleIpcMsgSetAccMsgGrps(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg); void _handleIpcMsgForward(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg); void _handleIpcMsgGetDevicesReq(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg); void _handleIpcMsgSetData(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg); static int _readDataFromString(const char *s, uint16_t *pDataVal, uint16_t *pValDenom); static AQH_NODE_INFO *_getNodeInfoFromValue(AQHOMED *aqh, const AQH_VALUE *value); /* ------------------------------------------------------------------------------------------------ * implementations * ------------------------------------------------------------------------------------------------ */ void AqHomed_ReadAndHandleIpcMessages(AQHOMED *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(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep) { GWEN_MSG *msg; while( (msg=GWEN_MsgEndpoint_TakeFirstReceivedMessage(ep)) ) { _handleIpcMsg(aqh, ep, msg); GWEN_Msg_free(msg); } } void _handleIpcMsg(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg) { uint16_t code; /* exec IPC message */ code=GWEN_IpcMsg_GetCode(msg); DBG_INFO(AQH_LOGDOMAIN, "Received IPC packet (%d, %04x)", code, code); switch(code) { case AQH_MSGTYPE_IPC_NODES_PING: _handleIpcMsgPing(aqh, ep, msg); break; case AQH_MSGTYPE_IPC_NODES_SETACCMSGGRPS: _handleIpcMsgSetAccMsgGrps(aqh, ep, msg); break; case AQH_MSGTYPE_IPC_NODES_FORWARD: _handleIpcMsgForward(aqh, ep, msg); break; case AQH_MSGTYPE_IPC_NODES_GETDEVICES_REQ: _handleIpcMsgGetDevicesReq(aqh, ep, msg); break; case AQH_MSGTYPE_IPC_DATA_SETDATA: _handleIpcMsgSetData(aqh, ep, msg); break; default: break; } } void _handleIpcMsgPing(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg) { if (aqh->ttyEndpoint && GWEN_MsgEndpoint_GetState(aqh->ttyEndpoint)==GWEN_MSG_ENDPOINT_STATE_CONNECTED) { GWEN_MSG *msgOut; DBG_DEBUG(AQH_LOGDOMAIN, "Received IPC PING message"); msgOut=AQH_PingMsg_new(aqh->nodeAddress, AQH_PingIpcMsg_GetDestAddr(msg), AQH_MSG_TYPE_PING); GWEN_MsgEndpoint_AddSendMessage(aqh->ttyEndpoint, msgOut); } } void _handleIpcMsgSetAccMsgGrps(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg) { uint32_t groups; DBG_INFO(AQH_LOGDOMAIN, "Received IPC SET_ACCEPTED_MSG_GROUPS message"); groups=AQH_SetAcceptedMsgGroupsIpcMsg_GetMsgGroups(msg); AQH_IpcEndpoint_SetAcceptedMsgGroups(ep, groups); // TODO: send response? } void _handleIpcMsgForward(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg) { if (aqh->ttyEndpoint && GWEN_MsgEndpoint_GetState(aqh->ttyEndpoint)==GWEN_MSG_ENDPOINT_STATE_CONNECTED) { GWEN_MSG *msgOut; DBG_DEBUG(AQH_LOGDOMAIN, "Received IPC FORWARD message"); msgOut=AQH_ForwardIpcMsg_GetCopyOfNodeMsg(msg); if (msgOut) GWEN_MsgEndpoint_AddSendMessage(aqh->ttyEndpoint, msgOut); } } void _handleIpcMsgGetDevicesReq(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg) { AQH_NODE_INFO_LIST *nodeInfoList; DBG_INFO(AQH_LOGDOMAIN, "Received IPC GetDevicesRequest message"); nodeInfoList=AQH_NodeDb_GetAllNodeInfos(aqh->nodeDb); if (nodeInfoList && AQH_NodeInfo_List_GetCount(nodeInfoList)) { const AQH_NODE_INFO *ni; ni=AQH_NodeInfo_List_First(nodeInfoList); while(ni) { const AQH_NODE_INFO *niNext; GWEN_MSG *msgOut; niNext=AQH_NodeInfo_List_Next(ni); DBG_INFO(AQH_LOGDOMAIN, "Sending response for node %02x (%08x)", AQH_NodeInfo_GetBusAddress(ni), AQH_NodeInfo_GetUid(ni)); msgOut=AQH_GetDevicesResponseIpcMsg_new(AQH_MSGTYPE_IPC_NODES_GETDEVICES_RSP, GWEN_MsgEndpoint_GetNextMessageId(ep), GWEN_IpcMsg_GetMsgId(msg), niNext?0:AQH_MSGIPC_GETDEVICES_RSP_FLAGS_LAST, ni); GWEN_MsgEndpoint_AddSendMessage(ep, msgOut); ni=niNext; } } else { GWEN_MSG *msgOut; DBG_INFO(AQH_LOGDOMAIN, "No nodes"); msgOut=AQH_ResultIpcMsg_new(AQH_MSGTYPE_IPC_NODES_RESULT, GWEN_MsgEndpoint_GetNextMessageId(ep), GWEN_IpcMsg_GetMsgId(msg), AQH_MSG_IPC_ERROR_NODATA); GWEN_MsgEndpoint_AddSendMessage(ep, msgOut); } } void _handleIpcMsgSetData(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *msg) { if (aqh->ttyEndpoint && GWEN_MsgEndpoint_GetState(aqh->ttyEndpoint)==GWEN_MSG_ENDPOINT_STATE_CONNECTED) { AQH_VALUE *value; DBG_DEBUG(AQH_LOGDOMAIN, "Received IPC SetDataRequest message"); AQH_SetDataIpcMsg_Parse(msg, 0); value=AQH_SetDataIpcMsg_ReadValue(msg); if (value) { char *data; data=AQH_SetDataIpcMsg_ReadData(msg); if (data) { uint16_t dataVal=0; uint16_t dataDenom=0; if (_readDataFromString(data, &dataVal, &dataDenom)==0) { AQH_NODE_INFO *nodeInfo; nodeInfo=_getNodeInfoFromValue(aqh, value); if (nodeInfo) { int valueId=0; const char *s; s=AQH_Value_GetName(value); if (s && *s && 1==sscanf(s, "%d", &valueId)) { GWEN_MSG *msgOut; int destAddr; destAddr=AQH_NodeInfo_GetBusAddress(nodeInfo); msgOut=AQH_Value3Msg_new(aqh->nodeAddress, destAddr, AQH_MSG_TYPE_VALUE_SET, 0 /* msgid*/, valueId, dataVal, dataDenom); GWEN_MsgEndpoint_AddSendMessage(aqh->ttyEndpoint, msgOut); } else { DBG_ERROR(AQH_LOGDOMAIN, "Invalid value id \"%s\"", s); } } else { DBG_ERROR(AQH_LOGDOMAIN, "No matching node found"); } } free(data); } AQH_Value_free(value); } else { DBG_ERROR(AQH_LOGDOMAIN, "Could not read value from message"); } } } int _readDataFromString(const char *s, uint16_t *pDataVal, uint16_t *pDataDenom) { if (s && *s) { if (*s=='&') { unsigned long int v=0; s++; if (1==sscanf(s, "%lx", &v)) { *pDataVal=(v>>16) & 0xffff; *pDataDenom=v & 0xffff; return 0; } else { DBG_ERROR(AQH_LOGDOMAIN, "Bad hex value \"%s\"", s); } } } return GWEN_ERROR_GENERIC; } AQH_NODE_INFO *_getNodeInfoFromValue(AQHOMED *aqh, const AQH_VALUE *value) { const char *s; unsigned long int uid; s=AQH_Value_GetDeviceName(value); if (s && *s && 1==sscanf(s, "%lx", &uid)) { AQH_NODE_INFO *ni; ni=AQH_NodeDb_GetNodeInfoByUid(aqh->nodeDb, uid); if (ni==NULL) { DBG_INFO(AQH_LOGDOMAIN, "Node \"%08lx\" not found", uid); return NULL; } return ni; } return NULL; }