Files
aqhomecontrol/apps/aqhome-nodes/r_setdata.c
Martin Preuss d0c8b3b284 let setData use double values instead of strings.
this allows for storing value set with setData which can then be used in
the cgi module to retrieve the last value set.
2025-10-07 23:50:50 +02:00

383 lines
12 KiB
C

/****************************************************************************
* This file is part of the project AqHome.
* AqHome (c) by 2025 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 <config.h>
#endif
#include "./r_setdata.h"
#include "./server_p.h"
#include "aqhome/aqhome.h"
#include "aqhome/data/value.h"
#include "aqhome/msg/ipc/m_ipc.h"
#include "aqhome/msg/ipc/m_ipc_result.h"
#include "aqhome/msg/ipc/m_ipc_tag16.h"
#include "aqhome/msg/ipc/data/m_ipcd.h"
#include "aqhome/msg/ipc/data/m_ipcd_setdata.h"
#include "aqhome/msg/node/m_node.h"
#include "aqhome/msg/node/m_value.h"
#include "aqhome/ipc2/endpoint.h"
#include <gwenhywfar/debug.h>
/* ------------------------------------------------------------------------------------------------
* definitions
* ------------------------------------------------------------------------------------------------
*/
#define R_SETDATA_REQUEST_EXPIRE_SECS 20
#define R_SETDATA_SUBREQUEST_EXPIRE_SECS 10
/* ------------------------------------------------------------------------------------------------
* forward declarations
* ------------------------------------------------------------------------------------------------
*/
static AQH_NODE_INFO *_getNodeInfoFromValue(AQH_NODE_SERVER *xo, const AQH_VALUE *value);
static AQH_MSG_REQUEST *_mkRequest_SetData(AQH_OBJECT *o, AQH_NODE_SERVER *xo,
AQH_OBJECT *ep, uint32_t requestMsgId,
int destAddr, int valueId, uint16_t dataVal, uint16_t dataDenom);
static void _rqSubRequestFinished(AQH_MSG_REQUEST *rq, AQH_MSG_REQUEST *subRq, int reason);
static void _rqAbort(AQH_MSG_REQUEST *rq, int reason);
static AQH_MSG_REQUEST *_mkSubRequest_SetData(AQH_OBJECT *o, AQH_NODE_SERVER *xo,
int destAddr, int valueId, uint16_t dataVal, uint16_t dataDenom);
static int _subRqHandleResponse(AQH_MSG_REQUEST *rq, const AQH_MESSAGE *msg);
static void _subRqAbort(AQH_MSG_REQUEST *rq, int reason);
static void _sendResponseResultToBroker(AQH_OBJECT *ep, uint32_t refMsgId, int result);
/* ------------------------------------------------------------------------------------------------
* implementations
* ------------------------------------------------------------------------------------------------
*/
void AQH_NodeServer_HandleSetData(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *recvdMsg)
{
if (o) {
AQH_NODE_SERVER *xo;
xo=AQH_NodeServer_GetServerData(o);
if (xo) {
uint32_t msgId;
DBG_INFO(NULL, "Received IPC SetDataRequest message");
msgId=AQH_IpcMessage_GetMsgId(recvdMsg);
if (xo->ttyEndpoint) {
GWEN_TAG16_LIST *tagList;
tagList=AQH_IpcMessageTag16_ParsePayload(recvdMsg, 0);
if (tagList) {
AQH_VALUE *value;
value=AQH_IpcdMessageSetData_ReadValue(tagList);
if (value) {
const char *varName;
varName=AQH_Value_GetName(value);
if (varName) {
double data;
data=AQH_IpcdMessageSetData_ReadData(tagList);
if (data) {
AQH_NODE_INFO *nodeInfo;
nodeInfo=_getNodeInfoFromValue(xo, value);
if (nodeInfo) {
const char *devName;
devName=AQH_NodeInfo_GetDeviceId(nodeInfo);
if (devName) {
const AQHNODE_DEVICE *devInfo;
devInfo=AQH_NodeServer_GetDeviceDefByName(o, devName);
if (devInfo) {
const AQHNODE_VALUE *devValue;
devValue=AQHNODE_Value_List_GetByName(AQHNODE_Device_GetValueList(devInfo), varName);
if (devValue) {
uint16_t dataVal=0;
uint16_t dataDenom=0;
if (AQH_ReadDataFromDouble(AQHNODE_Value_GetDataType(devValue), data, &dataVal, &dataDenom)==0) {
AQH_MSG_REQUEST *rq;
int destAddr;
destAddr=AQH_NodeInfo_GetBusAddress(nodeInfo);
DBG_DEBUG(NULL, "Creating SETDATA request");
rq=_mkRequest_SetData(o, xo, ep, msgId, destAddr, AQHNODE_Value_GetId(devValue), dataVal, dataDenom);
AQH_NodeServer_AddRequestToTree(o, rq);
/* done */
}
else {
DBG_ERROR(NULL, "Bad data \"%s\"", data);
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_BADDATA);
}
}
else {
DBG_ERROR(NULL, "Invalid value name \"%s\"", varName);
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_INVALID);
}
}
else {
DBG_ERROR(NULL, "Unknown node \"%s\"", devName);
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_INVALID);
}
}
else {
DBG_ERROR(NULL, "Node not yet fully identified, come back later");
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_TRYAGAIN);
}
}
else {
DBG_ERROR(NULL, "No matching nodeinfo");
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_INVALID);
}
}
else {
DBG_ERROR(NULL, "No data");
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_NODATA);
}
}
else {
DBG_ERROR(NULL, "No var name");
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_NODATA);
}
AQH_Value_free(value);
}
else {
DBG_ERROR(NULL, "Could not read value from message");
}
GWEN_Tag16_List_free(tagList);
}
}
else {
DBG_ERROR(NULL, "TTY endpoint not connected");
_sendResponseResultToBroker(ep, msgId, AQH_MSGDATA_RESULT_ERROR_IO);
}
}
}
}
/* ------------------------------------------------------------------------------------------------
* IPC Request SETDATA
*/
AQH_MSG_REQUEST *_mkRequest_SetData(AQH_OBJECT *o, AQH_NODE_SERVER *xo,
AQH_OBJECT *ep, uint32_t requestMsgId,
int destAddr, int valueId, uint16_t dataVal, uint16_t dataDenom)
{
AQH_MSG_REQUEST *rq;
AQH_MSG_REQUEST *subRq;
rq=AQH_MsgRequest_new();
AQH_MsgRequest_SetPrivateData(rq, o);
AQH_MsgRequest_SetEndpoint(rq, ep);
AQH_MsgRequest_SetRequestMsgId(rq, requestMsgId);
AQH_MsgRequest_SetSubRequestFinishedFn(rq, _rqSubRequestFinished);
AQH_MsgRequest_SetAbortFn(rq, _rqAbort);
AQH_MsgRequest_SetTimestamps(rq, R_SETDATA_REQUEST_EXPIRE_SECS);
subRq=_mkSubRequest_SetData(o, xo, destAddr, valueId, dataVal, dataDenom);
AQH_MsgRequest_Tree2_AddChild(rq, subRq);
return rq;
}
void _rqSubRequestFinished(AQH_MSG_REQUEST *rq, AQH_MSG_REQUEST *subRq, int reason)
{
AQH_OBJECT *ep;
uint32_t refMsgId;
int result;
DBG_INFO(NULL, "SubRequest finished (reason: %d)", reason);
refMsgId=AQH_MsgRequest_GetRequestMsgId(rq);
ep=AQH_MsgRequest_GetEndpoint(rq);
result=AQH_MsgRequest_GetResult(subRq);
if (reason==AQH_MSG_REQUEST_REASON_ABORTED)
_sendResponseResultToBroker(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_GENERIC);
else
_sendResponseResultToBroker(ep, refMsgId, result);
AQH_MsgRequest_SetResult(rq, result);
AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
}
void _rqAbort(AQH_MSG_REQUEST *rq, int reason)
{
AQH_OBJECT *ep;
uint32_t refMsgId;
AQH_MSG_REQUEST *rqParent;
DBG_INFO(NULL, "Aborting request");
refMsgId=AQH_MsgRequest_GetRequestMsgId(rq);
ep=AQH_MsgRequest_GetEndpoint(rq);
_sendResponseResultToBroker(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_GENERIC);
AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
rqParent=AQH_MsgRequest_Tree2_GetParent(rq);
if (rqParent)
AQH_MsgRequest_SubRequestFinished(rqParent, rq, reason);
}
/* ------------------------------------------------------------------------------------------------
* TTY Request SETDATA
*/
AQH_MSG_REQUEST *_mkSubRequest_SetData(AQH_OBJECT *o, AQH_NODE_SERVER *xo,
int destAddr, int valueId, uint16_t dataVal, uint16_t dataDenom)
{
AQH_MSG_REQUEST *rq;
uint16_t msgId;
AQH_MESSAGE *msgOut;
rq=AQH_MsgRequest_new();
AQH_MsgRequest_SetPrivateData(rq, o);
AQH_MsgRequest_SetEndpoint(rq, xo->ttyEndpoint);
AQH_MsgRequest_SetHandleResponseFn(rq, _subRqHandleResponse);
AQH_MsgRequest_SetAbortFn(rq, _subRqAbort);
msgId=AQH_Endpoint_GetNextMessageId(xo->ttyEndpoint) & 0xffff;
AQH_MsgRequest_SetRequestMsgId(rq, msgId);
AQH_MsgRequest_SetTimestamps(rq, R_SETDATA_SUBREQUEST_EXPIRE_SECS);
msgOut=AQH_ValueMessage_new(destAddr, xo->nodeAddress, AQH_MSG_TYPE_VALUE_SET, msgId, valueId, dataVal, dataDenom);
AQH_NodeServer_WriteTtyMsgToLogFile(o, msgOut, "sending");
AQH_Endpoint_AddMsgOut(xo->ttyEndpoint, msgOut);
return rq;
}
int _subRqHandleResponse(AQH_MSG_REQUEST *rq, const AQH_MESSAGE *msg)
{
AQH_OBJECT *o;
DBG_DEBUG(NULL, "Checking message from %02x", AQH_NodeMessage_GetSourceAddress(msg));
o=(AQH_OBJECT*)AQH_MsgRequest_GetPrivateData(rq);
if (o) {
AQH_NODE_SERVER *xo;
xo=AQH_NodeServer_GetServerData(o);
if (xo) {
uint8_t destAddr;
destAddr=AQH_NodeMessage_GetDestAddress(msg);
if (destAddr==0xff || destAddr==xo->nodeAddress) {
uint8_t msgCode;
msgCode=AQH_NodeMessage_GetMsgType(msg);
if (msgCode==AQH_MSG_TYPE_VALUE_SET_ACK || msgCode==AQH_MSG_TYPE_VALUE_SET_NACK) {
uint16_t msgId;
msgId=AQH_ValueMessage_GetMsgId(msg);
if (msgId==AQH_MsgRequest_GetRequestMsgId(rq)) {
AQH_MSG_REQUEST *rqParent;
DBG_INFO(NULL,
"Received response (%02x) for msg id %04x from %02x",
msgCode, msgId, AQH_NodeMessage_GetSourceAddress(msg));
AQH_MsgRequest_SetResult(rq,
(msgCode==AQH_MSG_TYPE_VALUE_SET_ACK)?AQH_MSGDATA_RESULT_SUCCESS:AQH_MSGDATA_RESULT_ERROR_GENERIC);
AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
rqParent=AQH_MsgRequest_Tree2_GetParent(rq);
if (rqParent)
AQH_MsgRequest_SubRequestFinished(rqParent, rq, AQH_MSG_REQUEST_REASON_DONE);
return AQH_MSG_REQUEST_RESULT_HANDLED;
}
else {
DBG_INFO(NULL, " Non-matching message id");
}
}
else {
DBG_INFO(NULL, " Non-matching message code");
}
}
}
}
return AQH_MSG_REQUEST_RESULT_NOT_HANDLED;
}
void _subRqAbort(AQH_MSG_REQUEST *rq, int reason)
{
AQH_MSG_REQUEST *rqParent;
DBG_INFO(NULL, "Aborting request");
AQH_MsgRequest_SetResult(rq, AQH_MSGDATA_RESULT_ERROR_GENERIC);
AQH_MsgRequest_SetState(rq, AQH_MSG_REQUEST_STATE_DONE);
rqParent=AQH_MsgRequest_Tree2_GetParent(rq);
if (rqParent)
AQH_MsgRequest_SubRequestFinished(rqParent, rq, reason);
}
AQH_NODE_INFO *_getNodeInfoFromValue(AQH_NODE_SERVER *xo, 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(xo->nodeDb, uid);
if (ni==NULL) {
DBG_ERROR(NULL, "Node \"%08lx\" not found", uid);
return NULL;
}
return ni;
}
return NULL;
}
void _sendResponseResultToBroker(AQH_OBJECT *ep, uint32_t refMsgId, int result)
{
AQH_MESSAGE *msg;
msg=AQH_IpcMessageResult_new(AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION, AQH_MSGTYPE_IPC_DATA_RESULT,
AQH_Endpoint_GetNextMessageId(ep), refMsgId, result, NULL);
AQH_Endpoint_AddMsgOut(ep, msg);
}