diff --git a/apps/aqhome-nodes/0BUILD b/apps/aqhome-nodes/0BUILD index b1deec1..5c0eca9 100644 --- a/apps/aqhome-nodes/0BUILD +++ b/apps/aqhome-nodes/0BUILD @@ -49,6 +49,9 @@ tty_log.h devicesread.h devicesdump.h + requests.h + n_setdata.h + r_setdata.h @@ -67,6 +70,9 @@ tty_log.c devicesread.c devicesdump.c + requests.c + n_setdata.c + r_setdata.c diff --git a/apps/aqhome-nodes/n_setdata.c b/apps/aqhome-nodes/n_setdata.c new file mode 100644 index 0000000..01e5286 --- /dev/null +++ b/apps/aqhome-nodes/n_setdata.c @@ -0,0 +1,166 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2024 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include +#endif + + +#include "./n_setdata.h" +#include "./aqhomed_p.h" +#include "./r_setdata.h" + +#include "aqhome/data/value.h" +#include "aqhome/ipc/data/msg_data_set.h" +#include "aqhome/ipc/data/ipc_data.h" +#include "aqhome/ipc/msg_ipc_result.h" +#include "aqhome/ipc/endpoint_ipc.h" + +#include + + + +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ + + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static int _readDataFromString(const char *s, uint16_t *pDataVal, uint16_t *pDataDenom); +static AQH_NODE_INFO *_getNodeInfoFromValue(AQHOMED *aqh, const AQH_VALUE *value); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +void AqHomeNodes_HandleNodeSetData(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *recvdMsg) +{ + uint32_t msgId; + + DBG_DEBUG(AQH_LOGDOMAIN, "Received IPC SetDataRequest message"); + msgId=GWEN_IpcMsg_GetMsgId(recvdMsg); + + if (aqh->ttyEndpoint && GWEN_MsgEndpoint_GetState(aqh->ttyEndpoint)==GWEN_MSG_ENDPOINT_STATE_CONNECTED) { + AQH_VALUE *value; + + AQH_SetDataIpcMsg_Parse(recvdMsg, 0); + value=AQH_SetDataIpcMsg_ReadValue(recvdMsg); + if (value) { + char *data; + + data=AQH_SetDataIpcMsg_ReadData(recvdMsg); + 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_REQUEST *rq; + int destAddr; + + destAddr=AQH_NodeInfo_GetBusAddress(nodeInfo); + + rq=AqHomeNodes_MkRequest_SetData(aqh, ep, msgId, destAddr, valueId, dataVal, dataDenom); + AqHomed_AddRequestToTree(aqh, rq); + /* done */ + } + else { + DBG_ERROR(AQH_LOGDOMAIN, "Invalid value id \"%s\"", s); + AQH_IpcEndpoint_SendResponseResult(ep, msgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_INVALID); + } + } + else { + DBG_ERROR(AQH_LOGDOMAIN, "No matching node found"); + AQH_IpcEndpoint_SendResponseResult(ep, msgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_NOTFOUND); + } + } + else { + DBG_ERROR(AQH_LOGDOMAIN, "Bad data \"%s\"", data); + AQH_IpcEndpoint_SendResponseResult(ep, msgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_BADDATA); + } + free(data); + } + else { + DBG_ERROR(AQH_LOGDOMAIN, "No data"); + AQH_IpcEndpoint_SendResponseResult(ep, msgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_NODATA); + } + AQH_Value_free(value); + } + else { + DBG_ERROR(AQH_LOGDOMAIN, "Could not read value from message"); + } + } + else { + DBG_ERROR(AQH_LOGDOMAIN, "TTY endpoint not connected"); + AQH_IpcEndpoint_SendResponseResult(ep, msgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_IO); + } +} + + + +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; +} + + diff --git a/apps/aqhome-nodes/n_setdata.h b/apps/aqhome-nodes/n_setdata.h new file mode 100644 index 0000000..e705750 --- /dev/null +++ b/apps/aqhome-nodes/n_setdata.h @@ -0,0 +1,26 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2024 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifndef AQHOMED_N_SETDATA_H +#define AQHOMED_N_SETDATA_H + + +#include "./aqhomed.h" + +#include + + + +void AqHomeNodes_HandleNodeSetData(AQHOMED *aqh, GWEN_MSG_ENDPOINT *ep, GWEN_MSG *recvdMsg); + + + + +#endif + + diff --git a/apps/aqhome-nodes/r_setdata.c b/apps/aqhome-nodes/r_setdata.c new file mode 100644 index 0000000..a721ae7 --- /dev/null +++ b/apps/aqhome-nodes/r_setdata.c @@ -0,0 +1,209 @@ +/**************************************************************************** + * 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 "./r_setdata.h" +#include "./aqhomed_p.h" + +#include "aqhome/ipc/endpoint_ipc.h" +#include "aqhome/ipc/msg_ipc_result.h" +#include "aqhome/ipc/data/msg_data_set.h" +#include "aqhome/ipc/data/ipc_data.h" +#include "aqhome/msg/msg_value3.h" + +#include + + +/* ------------------------------------------------------------------------------------------------ + * definitions + * ------------------------------------------------------------------------------------------------ + */ + + +#define R_SETDATA_REQUEST_EXPIRE_SECS 20 +#define R_SETDATA_SUBREQUEST_EXPIRE_SECS 10 + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static void _rqSubRequestFinished(GWEN_MSG_REQUEST *rq, GWEN_MSG_REQUEST *subRq, int reason); +static void _rqAbort(GWEN_MSG_REQUEST *rq); + +static GWEN_MSG_REQUEST *_mkSubRequest_SetData(AQHOMED *aqh, int destAddr, int valueId, uint16_t dataVal, uint16_t dataDenom); +static int _subRqHandleResponse(GWEN_MSG_REQUEST *rq, GWEN_MSG *msg); +static void _subRqAbort(GWEN_MSG_REQUEST *rq); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +GWEN_MSG_REQUEST *AqHomeNodes_MkRequest_SetData(AQHOMED *aqh, + GWEN_MSG_ENDPOINT *ep, uint32_t requestMsgId, + int destAddr, int valueId, uint16_t dataVal, uint16_t dataDenom) +{ + GWEN_MSG_REQUEST *rq; + GWEN_MSG_REQUEST *subRq; + + rq=GWEN_MsgRequest_new(); + GWEN_MsgRequest_SetPrivateData(rq, aqh); + GWEN_MsgRequest_SetEndpoint(rq, ep); + GWEN_MsgRequest_SetRequestMsgId(rq, requestMsgId); + GWEN_MsgRequest_SetSubRequestFinishedFn(rq, _rqSubRequestFinished); + GWEN_MsgRequest_SetAbortFn(rq, _rqAbort); + GWEN_MsgRequest_SetTimestamps(rq, R_SETDATA_REQUEST_EXPIRE_SECS); + + subRq=_mkSubRequest_SetData(aqh, destAddr, valueId, dataVal, dataDenom); + GWEN_MsgRequest_Tree2_AddChild(rq, subRq); + + return rq; +} + + + +void _rqSubRequestFinished(GWEN_MSG_REQUEST *rq, GWEN_MSG_REQUEST *subRq, int reason) +{ + GWEN_MSG_ENDPOINT *ep; + uint32_t refMsgId; + int result; + + DBG_INFO(NULL, "SubRequest finished"); + refMsgId=GWEN_MsgRequest_GetRequestMsgId(rq); + ep=GWEN_MsgRequest_GetEndpoint(rq); + result=GWEN_MsgRequest_GetResult(subRq); + + if (reason==GWEN_MSG_REQUEST_REASON_ABORTED) + AQH_IpcEndpoint_SendResponseResult(ep, refMsgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_GENERIC); + else + AQH_IpcEndpoint_SendResponseResult(ep, refMsgId, AQH_MSGTYPE_IPC_DATA_RESULT, result); + + GWEN_MsgRequest_SetResult(rq, result); + GWEN_MsgRequest_SetState(rq, GWEN_MSG_REQUEST_STATE_DONE); +} + + + +void _rqAbort(GWEN_MSG_REQUEST *rq) +{ + GWEN_MSG_ENDPOINT *ep; + uint32_t refMsgId; + GWEN_MSG_REQUEST *rqParent; + + DBG_INFO(NULL, "Aborting request"); + refMsgId=GWEN_MsgRequest_GetRequestMsgId(rq); + ep=GWEN_MsgRequest_GetEndpoint(rq); + AQH_IpcEndpoint_SendResponseResult(ep, refMsgId, AQH_MSGTYPE_IPC_DATA_RESULT, AQH_MSG_IPC_ERROR_GENERIC); + GWEN_MsgRequest_SetState(rq, GWEN_MSG_REQUEST_STATE_DONE); + + rqParent=GWEN_MsgRequest_Tree2_GetParent(rq); + if (rqParent) + GWEN_MsgRequest_SubRequestFinished(rqParent, rq, GWEN_MSG_REQUEST_REASON_ABORTED); +} + + + + + + + + +GWEN_MSG_REQUEST *_mkSubRequest_SetData(AQHOMED *aqh, int destAddr, int valueId, uint16_t dataVal, uint16_t dataDenom) +{ + GWEN_MSG_REQUEST *rq; + uint16_t msgId; + GWEN_MSG *msgOut; + + rq=GWEN_MsgRequest_new(); + GWEN_MsgRequest_SetPrivateData(rq, aqh); + GWEN_MsgRequest_SetEndpoint(rq, aqh->ttyEndpoint); + + GWEN_MsgRequest_SetHandleResponseFn(rq, _subRqHandleResponse); + GWEN_MsgRequest_SetAbortFn(rq, _subRqAbort); + + msgId=GWEN_MsgEndpoint_GetNextMessageId(aqh->ttyEndpoint) & 0xffff; + GWEN_MsgRequest_SetRequestMsgId(rq, msgId); + GWEN_MsgRequest_SetTimestamps(rq, R_SETDATA_SUBREQUEST_EXPIRE_SECS); + + msgOut=AQH_Value3Msg_new(aqh->nodeAddress, destAddr, AQH_MSG_TYPE_VALUE_SET, msgId, valueId, dataVal, dataDenom); + GWEN_MsgEndpoint_AddSendMessage(aqh->ttyEndpoint, msgOut); + + return rq; +} + + + +int _subRqHandleResponse(GWEN_MSG_REQUEST *rq, GWEN_MSG *msg) +{ + AQHOMED *aqh; + uint8_t destAddr; + + DBG_DEBUG(NULL, "Checking message from %02x", AQH_NodeMsg_GetSourceAddress(msg)); + aqh=(AQHOMED*)GWEN_MsgRequest_GetPrivateData(rq); + + destAddr=AQH_NodeMsg_GetDestAddress(msg); + if (destAddr==0xff || destAddr==aqh->nodeAddress) { + uint8_t msgCode; + + msgCode=AQH_NodeMsg_GetMsgType(msg); + if (msgCode==AQH_MSG_TYPE_VALUE_SET_ACK || msgCode==AQH_MSG_TYPE_VALUE_SET_NACK) { + uint16_t msgId; + + msgId=AQH_Value3Msg_GetMsgId(msg); + if (msgId==GWEN_MsgRequest_GetRequestMsgId(rq)) { + GWEN_MSG_REQUEST *rqParent; + + DBG_INFO(NULL, + "Received response (%02x) for msg id %04x from %02x", + msgCode, msgId, AQH_NodeMsg_GetSourceAddress(msg)); + GWEN_MsgRequest_SetResult(rq, (msgCode==AQH_MSG_TYPE_VALUE_SET_ACK)?AQH_MSG_IPC_SUCCESS:AQH_MSG_IPC_ERROR_GENERIC); + GWEN_MsgRequest_SetState(rq, GWEN_MSG_REQUEST_STATE_DONE); + rqParent=GWEN_MsgRequest_Tree2_GetParent(rq); + if (rqParent) + GWEN_MsgRequest_SubRequestFinished(rqParent, rq, GWEN_MSG_REQUEST_REASON_DONE); + return GWEN_MSG_REQUEST_RESULT_HANDLED; + } + else { + DBG_DEBUG(NULL, " Non-matching message id"); + } + } + else { + DBG_DEBUG(NULL, " Non-matching message code"); + } + } + return GWEN_MSG_REQUEST_RESULT_NOT_HANDLED; +} + + + +void _subRqAbort(GWEN_MSG_REQUEST *rq) +{ + GWEN_MSG_REQUEST *rqParent; + + DBG_INFO(NULL, "Aborting request"); + + GWEN_MsgRequest_SetResult(rq, AQH_MSG_IPC_ERROR_GENERIC); + GWEN_MsgRequest_SetState(rq, GWEN_MSG_REQUEST_STATE_DONE); + + rqParent=GWEN_MsgRequest_Tree2_GetParent(rq); + if (rqParent) + GWEN_MsgRequest_SubRequestFinished(rqParent, rq, GWEN_MSG_REQUEST_REASON_ABORTED); +} + + + + diff --git a/apps/aqhome-nodes/r_setdata.h b/apps/aqhome-nodes/r_setdata.h new file mode 100644 index 0000000..e829f9c --- /dev/null +++ b/apps/aqhome-nodes/r_setdata.h @@ -0,0 +1,28 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2024 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifndef AQHOMED_R_SETDATA_H +#define AQHOMED_R_SETDATA_H + + +#include "./aqhomed.h" + +#include + + + +GWEN_MSG_REQUEST *AqHomeNodes_MkRequest_SetData(AQHOMED *aqh, + GWEN_MSG_ENDPOINT *ep, uint32_t requestMsgId, + int destAddr, int valueId, uint16_t dataVal, uint16_t dataDenom); + + + + +#endif + + diff --git a/apps/aqhome-nodes/requests.c b/apps/aqhome-nodes/requests.c new file mode 100644 index 0000000..6cf6d4b --- /dev/null +++ b/apps/aqhome-nodes/requests.c @@ -0,0 +1,138 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2024 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include +#endif + + +#include "./requests.h" +#include "./aqhomed_p.h" + +#include "aqhome/ipc/endpoint_ipc.h" +#include "aqhome/ipc/data/ipc_data.h" + +#include +#include + + + +static void _freeFinishedRequests(GWEN_MSG_REQUEST *rq); + + + + +void AqHomeNodes_Requests_CheckTimeouts(AQHOMED *aqh) +{ + if (aqh && aqh->requestTree) { + GWEN_MSG_REQUEST *rq; + GWEN_TIMESTAMP *now; + + now=GWEN_Timestamp_NowInLocalTime(); + rq=GWEN_MsgRequest_Tree2_GetFirstChild(aqh->requestTree); + while(rq) { + const GWEN_TIMESTAMP *ts; + + ts=GWEN_MsgRequest_GetExpiresAt(rq); + if (GWEN_Timestamp_Compare(now, ts)>=0) { + /* timeout */ + DBG_INFO(NULL, "Request timed out, aborting"); + GWEN_MsgRequest_Abort(rq); + } + rq=GWEN_MsgRequest_Tree2_GetBelow(rq); + } + GWEN_Timestamp_free(now); + } +} + + + +void AqHomeNodes_Requests_Cleanup(AQHOMED *aqh) +{ + if (aqh && aqh->requestTree) { + GWEN_MSG_REQUEST *rq; + + rq=GWEN_MsgRequest_Tree2_GetFirstChild(aqh->requestTree); + while(rq) { + GWEN_MSG_REQUEST *nextSubRq; + + nextSubRq=GWEN_MsgRequest_Tree2_GetNext(rq); + _freeFinishedRequests(rq); + rq=nextSubRq; + } + } +} + + + +int AqHomeNodes_Requests_HandleIpcMsg(AQHOMED *aqh, GWEN_MSG_ENDPOINT *srcEp, GWEN_MSG *recvdMsg) +{ + if (aqh && aqh->requestTree) { + uint32_t refMsgId; + + refMsgId=GWEN_IpcMsg_GetRefMsgId(recvdMsg); + if (refMsgId) { + GWEN_MSG_REQUEST *rq; + + rq=GWEN_MsgRequest_Tree2_GetFirstChild(aqh->requestTree); + while(rq) { + if (srcEp==GWEN_MsgRequest_GetEndpoint(rq) && refMsgId==GWEN_MsgRequest_GetRequestMsgId(rq)) { + if (GWEN_MsgRequest_HandleResponse(rq, recvdMsg)==GWEN_MSG_REQUEST_RESULT_HANDLED) + return GWEN_MSG_REQUEST_RESULT_HANDLED; + } + + rq=GWEN_MsgRequest_Tree2_GetBelow(rq); + } + } + else { + DBG_INFO(NULL, "Message has no reference msg id, not a response"); + } + } + return GWEN_MSG_REQUEST_RESULT_NOT_HANDLED; +} + + + +int AqHomeNodes_Requests_HandleTtyMsg(AQHOMED *aqh, GWEN_MSG_ENDPOINT *srcEp, GWEN_MSG *recvdMsg) +{ + if (aqh && aqh->requestTree) { + GWEN_MSG_REQUEST *rq; + + rq=GWEN_MsgRequest_Tree2_GetFirstChild(aqh->requestTree); + while(rq) { + if (srcEp==GWEN_MsgRequest_GetEndpoint(rq)) { + if (GWEN_MsgRequest_HandleResponse(rq, recvdMsg)==GWEN_MSG_REQUEST_RESULT_HANDLED) + return GWEN_MSG_REQUEST_RESULT_HANDLED; + } + + rq=GWEN_MsgRequest_Tree2_GetBelow(rq); + } + } + return GWEN_MSG_REQUEST_RESULT_NOT_HANDLED; +} + + + +void _freeFinishedRequests(GWEN_MSG_REQUEST *rq) +{ + GWEN_MSG_REQUEST *subRq; + + subRq=GWEN_MsgRequest_Tree2_GetFirstChild(rq); + while(subRq) { + GWEN_MSG_REQUEST *nextSubRq; + + nextSubRq=GWEN_MsgRequest_Tree2_GetNext(subRq); + _freeFinishedRequests(subRq); + subRq=nextSubRq; + } + + if (GWEN_MsgRequest_GetState(rq)==GWEN_MSG_REQUEST_STATE_DONE) + GWEN_MsgRequest_free(rq); +} + + diff --git a/apps/aqhome-nodes/requests.h b/apps/aqhome-nodes/requests.h new file mode 100644 index 0000000..c2827eb --- /dev/null +++ b/apps/aqhome-nodes/requests.h @@ -0,0 +1,30 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2024 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifndef AQHOMED_REQUESTS_H +#define AQHOMED_REQUESTS_H + + +#include "./aqhomed.h" + +#include + + + +void AqHomeNodes_Requests_CheckTimeouts(AQHOMED *aqh); +void AqHomeNodes_Requests_Cleanup(AQHOMED *aqh); + +int AqHomeNodes_Requests_HandleIpcMsg(AQHOMED *aqh, GWEN_MSG_ENDPOINT *srcEp, GWEN_MSG *recvdMsg); +int AqHomeNodes_Requests_HandleTtyMsg(AQHOMED *aqh, GWEN_MSG_ENDPOINT *srcEp, GWEN_MSG *recvdMsg); + + + + +#endif + +