diff --git a/apps/aqhome-data/0BUILD b/apps/aqhome-data/0BUILD index ced58b2..97fde3b 100644 --- a/apps/aqhome-data/0BUILD +++ b/apps/aqhome-data/0BUILD @@ -45,6 +45,12 @@ s_connect.h s_getdevices.h s_getvalues.h + s_addvalue.h + s_annvalue.h + s_updatedata.h + s_setdata.h + s_getdatapoints.h + s_moddevice.h c_connect.h c_updatedata.h c_getvalues.h @@ -76,6 +82,12 @@ s_connect.c s_getdevices.c s_getvalues.c + s_addvalue.c + s_annvalue.c + s_updatedata.c + s_setdata.c + s_getdatapoints.c + s_moddevice.c main.c diff --git a/apps/aqhome-data/s_addvalue.c b/apps/aqhome-data/s_addvalue.c new file mode 100644 index 0000000..6583fd2 --- /dev/null +++ b/apps/aqhome-data/s_addvalue.c @@ -0,0 +1,71 @@ +/**************************************************************************** + * 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 +#endif + + +#include "./s_addvalue.h" +#include "./server_p.h" +#include "aqhome/ipc2/endpoint.h" +#include "aqhome/msg/ipc/m_ipc.h" +#include "aqhome/msg/ipc/data/m_ipcd.h" +#include "aqhome/msg/ipc/data/m_ipcd_values.h" +#include "aqhome/msg/ipc/m_ipc_result.h" +#include "aqhome/msg/ipc/m_ipc_tag16.h" + +#include + + + +/* ------------------------------------------------------------------------------------------------ + * code + * ------------------------------------------------------------------------------------------------ + */ + +void AqHomeDataServer_HandleAddValue(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg) +{ + AQHOME_SERVER *xo; + + xo=AqHomeDataServer_GetServerData(o); + if (xo) { + AQH_MESSAGE *outMsg; + GWEN_TAG16_LIST *tagList; + int resultCode=AQH_MSGDATA_RESULT_SUCCESS; + + tagList=AQH_IpcMessageTag16_ParsePayload(msg, 0); + if (tagList) { + AQH_VALUE *recvdValue; + + recvdValue=AQH_IpcdMessageValues_ReadFirstValue(tagList); + if (recvdValue) { + AQH_VALUE *value; + + value=AqHomeDataServer_GetOrCreateValueForDriverWithTemplate(o, ep, recvdValue); + if (value==NULL) + resultCode=AQH_MSGDATA_RESULT_ERROR_PERMS; + AQH_Value_free(recvdValue); + } + GWEN_Tag16_List_free(tagList); + } + else + resultCode=AQH_MSGDATA_RESULT_ERROR_BADDATA; + + outMsg=AQH_IpcMessageResult_new(AQH_IPC_PROTOCOL_DATA_ID, + AQH_IPC_PROTOCOL_DATA_VERSION, + AQH_MSGTYPE_IPC_DATA_RESULT, + AQH_Endpoint_GetNextMessageId(ep), + AQH_IpcMessage_GetMsgId(msg), + resultCode, NULL); + AQH_Endpoint_AddMsgOut(ep, outMsg); + } +} + + + diff --git a/apps/aqhome-tool/data/addjsondata.h b/apps/aqhome-data/s_addvalue.h similarity index 58% rename from apps/aqhome-tool/data/addjsondata.h rename to apps/aqhome-data/s_addvalue.h index a2bdf16..aa1033b 100644 --- a/apps/aqhome-tool/data/addjsondata.h +++ b/apps/aqhome-data/s_addvalue.h @@ -1,21 +1,25 @@ /**************************************************************************** * This file is part of the project AqHome. - * AqHome (c) by 2024 Martin Preuss, all rights reserved. + * 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. ****************************************************************************/ -#ifndef AQHOME_TOOL_ADDJSONDATA_H -#define AQHOME_TOOL_ADDJSONDATA_H +#ifndef AQHOME_DATA_S_ADDVALUE_H +#define AQHOME_DATA_S_ADDVALUE_H -#include +#include "./server.h" +void AqHomeDataServer_HandleAddValue(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg); -int AQH_Tool_AddJsonDataPoint(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv); #endif + + + + diff --git a/apps/aqhome-data/s_annvalue.c b/apps/aqhome-data/s_annvalue.c new file mode 100644 index 0000000..a3074e4 --- /dev/null +++ b/apps/aqhome-data/s_annvalue.c @@ -0,0 +1,54 @@ +/**************************************************************************** + * 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 +#endif + + +#include "./s_annvalue.h" +#include "./server_p.h" +#include "aqhome/ipc2/endpoint.h" +#include "aqhome/msg/ipc/m_ipc.h" +#include "aqhome/msg/ipc/data/m_ipcd.h" +#include "aqhome/msg/ipc/data/m_ipcd_values.h" +#include "aqhome/msg/ipc/m_ipc_result.h" +#include "aqhome/msg/ipc/m_ipc_tag16.h" + +#include + + + +/* ------------------------------------------------------------------------------------------------ + * code + * ------------------------------------------------------------------------------------------------ + */ + +void AqHomeDataServer_HandleAnnounceValue(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg) +{ + AQHOME_SERVER *xo; + + xo=AqHomeDataServer_GetServerData(o); + if (xo) { + GWEN_TAG16_LIST *tagList; + + tagList=AQH_IpcMessageTag16_ParsePayload(msg, 0); + if (tagList) { + AQH_VALUE *recvdValue; + + recvdValue=AQH_IpcdMessageValues_ReadFirstValue(tagList); + if (recvdValue) { + AqHomeDataServer_GetOrCreateValueForDriverWithTemplate(o, ep, recvdValue); + AQH_Value_free(recvdValue); + } + GWEN_Tag16_List_free(tagList); + } + } +} + + diff --git a/apps/aqhome-tool/data/getlastdatapoint.h b/apps/aqhome-data/s_annvalue.h similarity index 57% rename from apps/aqhome-tool/data/getlastdatapoint.h rename to apps/aqhome-data/s_annvalue.h index 957a868..b5bbf77 100644 --- a/apps/aqhome-tool/data/getlastdatapoint.h +++ b/apps/aqhome-data/s_annvalue.h @@ -1,21 +1,25 @@ /**************************************************************************** * This file is part of the project AqHome. - * AqHome (c) by 2023 Martin Preuss, all rights reserved. + * 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. ****************************************************************************/ -#ifndef AQHOME_TOOL_GETLASTDATAPOINT_H -#define AQHOME_TOOL_GETLASTDATAPOINT_H +#ifndef AQHOME_DATA_S_ANNVALUE_H +#define AQHOME_DATA_S_ANNVALUE_H -#include +#include "./server.h" +void AqHomeDataServer_HandleAnnounceValue(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg); -int AQH_Tool_GetLastDataPoint(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv); #endif + + + + diff --git a/apps/aqhome-data/s_getdatapoints.c b/apps/aqhome-data/s_getdatapoints.c new file mode 100644 index 0000000..795cbbd --- /dev/null +++ b/apps/aqhome-data/s_getdatapoints.c @@ -0,0 +1,236 @@ +/**************************************************************************** + * 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 "./s_getdatapoints.h" + +#include "./server_p.h" +#include "aqhome/aqhome.h" +#include "aqhome/ipc2/endpoint.h" +#include "aqhome/msg/ipc/m_ipc.h" +#include "aqhome/msg/ipc/m_ipc_result.h" +#include "aqhome/msg/ipc/data/m_ipcd.h" +#include "aqhome/msg/ipc/data/m_ipcd_getdata.h" +#include "aqhome/msg/ipc/data/m_ipcd_multidata.h" +#include "aqhome/msg/ipc/m_ipc_result.h" +#include "aqhome/msg/ipc/m_ipc_tag16.h" + +#include + + + +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ + +#define AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES 2048 +#define AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS 1024 + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static int _getAndSendDataPoints(AQH_STORAGE *storage, AQH_OBJECT *ep, + const AQH_VALUE *value, + uint64_t tsBegin, uint64_t tsEnd, uint64_t num, uint32_t refMsgId); +static int _getAndSendDataPointsNoNum(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value, uint64_t tsBegin, uint64_t tsEnd, + uint32_t refMsgId); +static int _getAndSendDataPointsWithNum(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value, uint64_t num, uint32_t refMsgId); +static void _sendDataPointsResponse(AQH_OBJECT *ep, const AQH_VALUE *value, const uint64_t *tablePtr, + uint32_t refMsgId); +static void _getAndSendLastDatapoint(AQH_STORAGE *storage, AQH_OBJECT *ep, const AQH_VALUE *value, uint32_t refMsgId); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + + +void AqHomeDataServer_HandleGetDataPoints(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *recvdMsg) +{ + AQHOME_SERVER *xo; + + xo=AqHomeDataServer_GetServerData(o); + if (xo) { + uint32_t refMsgId; + + refMsgId=AQH_IpcMessage_GetMsgId(recvdMsg); + if (AQH_Endpoint_GetPermissions(ep) & AQH_ENDPOINT_PERMS_READDATA) { + GWEN_TAG16_LIST *tagList; + + tagList=AQH_IpcMessageTag16_ParsePayload(recvdMsg, 0); + if (tagList) { + char *valueName; + + valueName=AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSGDATA_GETDATA_TAGS_NAME, NULL); + if (valueName && *valueName) { + AQH_VALUE *value; + uint64_t tsBegin; + uint64_t tsEnd; + uint64_t numRequested; + + tsBegin=AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_BEGIN, 0); + tsEnd=AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_END, 0); + numRequested=AQH_Tag16_GetTagDataAsUint64(tagList, AQH_MSGDATA_GETDATA_TAGS_NUM, 0); + + value=AQH_Storage_GetValueByNameForSystem(xo->storage, valueName); + if (value) { + int resultCode; + + resultCode=_getAndSendDataPoints(xo->storage, ep, value, tsBegin, tsEnd, numRequested, refMsgId); + AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, resultCode); + } + else { + DBG_INFO(NULL, "Value \"%s\" does not exist", valueName); + AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_NOTFOUND); + } + free(valueName); + } + else { + DBG_INFO(NULL, "Missing value name"); + AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_BADDATA); + } + GWEN_Tag16_List_free(tagList); + } + else { + DBG_ERROR(AQH_LOGDOMAIN, "No value"); + AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_BADDATA); + } + } + else { + DBG_ERROR(AQH_LOGDOMAIN, "No permissions to read data"); + AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_PERMS); + } + } +} + + + +int _getAndSendDataPoints(AQH_STORAGE *storage, AQH_OBJECT *ep, + const AQH_VALUE *value, + uint64_t tsBegin, uint64_t tsEnd, uint64_t num, + uint32_t refMsgId) +{ + if (num==0) + return _getAndSendDataPointsNoNum(storage, ep, value, tsBegin, tsEnd, refMsgId); + else if (num==1) { + _getAndSendLastDatapoint(storage, ep, value, refMsgId); + return AQH_MSGDATA_RESULT_SUCCESS; + } + else + return _getAndSendDataPointsWithNum(storage, ep, value, num, refMsgId); +} + + + +int _getAndSendDataPointsNoNum(AQH_STORAGE *storage, AQH_OBJECT *ep, + const AQH_VALUE *value, uint64_t tsBegin, uint64_t tsEnd, + uint32_t refMsgId) +{ + uint64_t valueId; + uint64_t *tablePtr; + + valueId=AQH_Value_GetId(value); + tablePtr=AQH_Storage_GetDataPoints(storage, valueId, tsBegin, tsEnd, AQHOMEDATA_HANDLEGETDATAPOINTS_MAXTABLEENTRIES); + if (tablePtr) { + _sendDataPointsResponse(ep, value, tablePtr, refMsgId); + free(tablePtr); + return AQH_MSGDATA_RESULT_SUCCESS; + } + else { + DBG_INFO(NULL, "No matching datapoints for value \"%s\"", AQH_Value_GetNameForSystem(value)); + return AQH_MSGDATA_RESULT_ERROR_NODATA; + } +} + + + +int _getAndSendDataPointsWithNum(AQH_STORAGE *storage, AQH_OBJECT *ep, + const AQH_VALUE *value, uint64_t num, + uint32_t refMsgId) +{ + uint64_t valueId; + uint64_t *tablePtr; + + if (num>AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS) + num=AQHOMEDATA_HANDLEGETDATAPOINTS_MAXDATAPOINTS; + valueId=AQH_Value_GetId(value); + tablePtr=AQH_Storage_GetLastNDataPoints(storage, valueId, num); + if (tablePtr) { + _sendDataPointsResponse(ep, value, tablePtr, refMsgId); + free(tablePtr); + return AQH_MSGDATA_RESULT_SUCCESS; + } + else { + DBG_INFO(NULL, "No matching datapoints for value \"%s\"", AQH_Value_GetNameForSystem(value)); + return AQH_MSGDATA_RESULT_ERROR_NODATA; + } +} + + + +void _sendDataPointsResponse(AQH_OBJECT *ep, + const AQH_VALUE *value, const uint64_t *tablePtr, + uint32_t refMsgId) +{ + int numTableEntries; + int numDataPoints; + AQH_MESSAGE *outMsg; + + numTableEntries=(int)(tablePtr[0]); + numDataPoints=numTableEntries/2; + outMsg=AQH_IpcdMessageMultiData_new(AQH_MSGTYPE_IPC_DATA_GETDATA_RSP, + AQH_Endpoint_GetNextMessageId(ep), refMsgId, + value, &(tablePtr[1]), numDataPoints); + AQH_Endpoint_AddMsgOut(ep, outMsg); +} + + + +void _getAndSendLastDatapoint(AQH_STORAGE *storage, AQH_OBJECT *ep, + const AQH_VALUE *value, uint32_t refMsgId) +{ + int rv; + uint64_t timestamp=0; + double data=0.0; + + rv=AQH_Storage_GetLastDataPoint(storage, AQH_Value_GetId(value), ×tamp, &data); + if (rv<0) { + int resultCode; + + switch(rv) { + case GWEN_ERROR_INVALID: resultCode=AQH_MSGDATA_RESULT_ERROR_INVALID; break; + case GWEN_ERROR_NO_DATA: resultCode=AQH_MSGDATA_RESULT_ERROR_NODATA; break; + default: resultCode=AQH_MSGDATA_RESULT_ERROR_GENERIC; break; + } + AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, resultCode); + } + else { + AQH_MESSAGE *outMsg; + + outMsg=AQH_IpcdMessageMultiData_newForOne(AQH_MSGTYPE_IPC_DATA_GETDATA_RSP, + AQH_Endpoint_GetNextMessageId(ep), refMsgId, + value, timestamp, data); + AQH_Endpoint_AddMsgOut(ep, outMsg); + } +} + + + + + + diff --git a/apps/aqhome-data/s_getdatapoints.h b/apps/aqhome-data/s_getdatapoints.h new file mode 100644 index 0000000..a47c69f --- /dev/null +++ b/apps/aqhome-data/s_getdatapoints.h @@ -0,0 +1,25 @@ +/**************************************************************************** + * 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. + ****************************************************************************/ + +#ifndef AQHOME_DATA_S_GETDATAPOINTS_H +#define AQHOME_DATA_S_GETDATAPOINTS_H + + +#include "./server.h" + + +void AqHomeDataServer_HandleGetDataPoints(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *recvdMsg); + + + +#endif + + + + + diff --git a/apps/aqhome-data/s_moddevice.c b/apps/aqhome-data/s_moddevice.c new file mode 100644 index 0000000..2b5c85f --- /dev/null +++ b/apps/aqhome-data/s_moddevice.c @@ -0,0 +1,112 @@ +/**************************************************************************** + * 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 "./s_moddevice.h" + +#include "./server_p.h" +#include "aqhome/aqhome.h" +#include "aqhome/ipc2/endpoint.h" +#include "aqhome/msg/ipc/m_ipc.h" +#include "aqhome/msg/ipc/m_ipc_result.h" +#include "aqhome/msg/ipc/data/m_ipcd.h" +#include "aqhome/msg/ipc/data/m_ipcd_devices.h" +#include "aqhome/msg/ipc/m_ipc_result.h" +#include "aqhome/msg/ipc/m_ipc_tag16.h" + +#include +#include + + + +/* ------------------------------------------------------------------------------------------------ + * code + * ------------------------------------------------------------------------------------------------ + */ + + +void AqHomeDataServer_HandleModDevice(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *recvdMsg) +{ + AQHOME_SERVER *xo; + + xo=AqHomeDataServer_GetServerData(o); + if (xo) { + int resultCode=AQH_MSGDATA_RESULT_SUCCESS; + + if (AQH_Endpoint_GetPermissions(ep) & AQH_ENDPOINT_PERMS_MODDEVICE) { + GWEN_TAG16_LIST *tagList; + + tagList=AQH_IpcMessageTag16_ParsePayload(recvdMsg, 0); + if (tagList) { + AQH_DEVICE *device; + + device=AQH_IpcdMessageDevices_ReadFirstDevice(tagList); + if (device) { + const char *deviceNameForSystem; + + deviceNameForSystem=AQH_Device_GetNameForSystem(device); + if (deviceNameForSystem && *deviceNameForSystem) { + AQH_DEVICE *storedDevice; + + storedDevice=AQH_Storage_GetDeviceByNameForSystem(xo->storage, deviceNameForSystem); + if (storedDevice) { + const char *s; + + s=AQH_Device_GetNameForGui(device); + if (s && *s) + AQH_Device_SetNameForGui(storedDevice, s); + + s=AQH_Device_GetRoomName(device); + if (s && *s) + AQH_Device_SetRoomName(storedDevice, s); + + s=AQH_Device_GetLocation(device); + if (s && *s) + AQH_Device_SetLocation(storedDevice, s); + + s=AQH_Device_GetDescription(device); + if (s && *s) + AQH_Device_SetDescription(storedDevice, s); + + AQH_Storage_AddRuntimeFlags(xo->storage, AQH_STORAGE_RTFLAGS_MODIFIED); + resultCode=AQH_MSGDATA_RESULT_SUCCESS; + } + else { + DBG_INFO(NULL, "Device \"%s\" not found", deviceNameForSystem); + resultCode=AQH_MSGDATA_RESULT_ERROR_NOTFOUND; + } + } + else { + DBG_INFO(NULL, "No name for value"); + resultCode=AQH_MSGDATA_RESULT_ERROR_NOTFOUND; + } + } + else { + DBG_INFO(NULL, "No device info in message"); + resultCode=AQH_MSGDATA_RESULT_ERROR_INVALID; + } + } + else { + DBG_INFO(NULL, "No tag16 list in message"); + resultCode=AQH_MSGDATA_RESULT_ERROR_BADDATA; + } + } + else { + DBG_ERROR(NULL, "No permissions to read data"); + resultCode=AQH_MSGDATA_RESULT_ERROR_PERMS; + } + AqHomeDataServer_SendResponseResultToEndpoint(ep, AQH_IpcMessage_GetMsgId(recvdMsg), resultCode); + } +} + + + diff --git a/apps/aqhome-data/s_moddevice.h b/apps/aqhome-data/s_moddevice.h new file mode 100644 index 0000000..c359915 --- /dev/null +++ b/apps/aqhome-data/s_moddevice.h @@ -0,0 +1,25 @@ +/**************************************************************************** + * 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. + ****************************************************************************/ + +#ifndef AQHOME_DATA_S_MODDEVICE_H +#define AQHOME_DATA_S_MODDEVICE_H + + +#include "./server.h" + + +void AqHomeDataServer_HandleModDevice(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *recvdMsg); + + + +#endif + + + + + diff --git a/apps/aqhome-data/s_setdata.c b/apps/aqhome-data/s_setdata.c new file mode 100644 index 0000000..ebffd39 --- /dev/null +++ b/apps/aqhome-data/s_setdata.c @@ -0,0 +1,286 @@ +/**************************************************************************** + * 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 +#endif + + +#include "./s_setdata.h" + +#include "./server_p.h" +#include "aqhome/aqhome.h" +#include "aqhome/ipc2/endpoint.h" +#include "aqhome/msg/ipc/m_ipc.h" +#include "aqhome/msg/ipc/m_ipc_result.h" +#include "aqhome/msg/ipc/data/m_ipcd.h" +#include "aqhome/msg/ipc/data/m_ipcd_setdata.h" +#include "aqhome/msg/ipc/m_ipc_result.h" +#include "aqhome/msg/ipc/m_ipc_tag16.h" + +#include + + + +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ + +#define R_SETDATA_REQUEST_EXPIRE_SECS 20 +#define R_SETDATA_SUBREQUEST_EXPIRE_SECS 10 + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static AQH_MSG_REQUEST *_mkRequest_SetData(AQH_OBJECT *o, + AQH_OBJECT *epSrc, uint32_t requestMsgId, + AQH_OBJECT *epDriver, + const AQH_VALUE *v, const char *data); +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_OBJECT *epDriver, const AQH_VALUE *v, const char *data); +static int _subRqHandleResponse(AQH_MSG_REQUEST *rq, const AQH_MESSAGE *msg); +static void _subRqAbort(AQH_MSG_REQUEST *rq, int reason); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +void AqHomeDataServer_HandleSetData(AQH_OBJECT *o, AQH_OBJECT *epSrc, const AQH_MESSAGE *recvdMsg) +{ + AQHOME_SERVER *xo; + + xo=AqHomeDataServer_GetServerData(o); + if (xo) { + uint32_t msgId; + GWEN_TAG16_LIST *tagList; + + msgId=AQH_IpcMessage_GetMsgId(recvdMsg); + DBG_INFO(NULL, "Received IPC SetDataRequest message (msgId=%d)", msgId); + + tagList=AQH_IpcMessageTag16_ParsePayload(recvdMsg, 0); + if (tagList) { + AQH_VALUE *recvdValue; + + recvdValue=AQH_IpcdMessageSetData_ReadValue(tagList); + if (recvdValue) { + const char *valueName; + char *valueDataFreeable; + AQH_VALUE *systemValue; + + valueName=AQH_Value_GetNameForSystem(recvdValue); + valueDataFreeable=AQH_IpcdMessageSetData_ReadData(tagList); + + systemValue=AQH_Storage_GetValueByNameForSystem(xo->storage, valueName); + if (systemValue) { + if (AQH_Value_GetValueType(systemValue)==AQH_ValueType_Actor) { + const char *driverName; + + driverName=AQH_Value_GetDriver(systemValue); + if (driverName && *driverName) { + AQH_OBJECT *epDriver; + + epDriver=AqHomeDataServer_GetIpcEndpointByServiceName(o, driverName); + if (epDriver) { + AQH_MSG_REQUEST *rq; + + DBG_ERROR(NULL, "Creating SETDATA request for driver endpoint (%s)", AQH_Endpoint_GetServiceName(epDriver)); + rq=_mkRequest_SetData(o, epSrc, msgId, epDriver, systemValue, valueDataFreeable); + AqHomeDataServer_AddRequestToTree(o, rq); + } + else { + DBG_ERROR(NULL, "Driver \"%s\" not available", driverName); + AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_GENERIC); + } + } + else { + DBG_ERROR(NULL, "No driver name"); + AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_GENERIC); + } + } /* if actor */ + else { + DBG_ERROR(NULL, "Value \"%s\" is not an actor", valueName); + AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_INVALID); + } + } + else { + DBG_ERROR(NULL, "Unknown value \"%s\"", valueName); + AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_NOTFOUND); + } + AQH_Value_free(recvdValue); + free(valueDataFreeable); + } /* if recvdValue */ + else { + DBG_ERROR(NULL, "No value in message"); + AqHomeDataServer_SendResponseResultToEndpoint(epSrc, msgId, AQH_MSGDATA_RESULT_ERROR_BADDATA); + } + GWEN_Tag16_List_free(tagList); + } + } +} + + + +/* ------------------------------------------------------------------------------------------------ + * IPC Request SETDATA + */ + +AQH_MSG_REQUEST *_mkRequest_SetData(AQH_OBJECT *o, + AQH_OBJECT *epSrc, uint32_t requestMsgId, + AQH_OBJECT *epDriver, + const AQH_VALUE *v, const char *data) +{ + AQH_MSG_REQUEST *rq; + AQH_MSG_REQUEST *subRq; + + rq=AQH_MsgRequest_new(); + AQH_MsgRequest_SetPrivateData(rq, o); + AQH_MsgRequest_SetEndpoint(rq, epSrc); + 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, epDriver, v, data); + 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_DEBUG(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) + AqHomeDataServer_SendResponseResultToEndpoint(ep, refMsgId, AQH_MSGDATA_RESULT_ERROR_GENERIC); + else + AqHomeDataServer_SendResponseResultToEndpoint(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); + AqHomeDataServer_SendResponseResultToEndpoint(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); +} + + + +/* ------------------------------------------------------------------------------------------------ + * Driver Request SETDATA + */ + + +AQH_MSG_REQUEST *_mkSubRequest_SetData(AQH_OBJECT *o, AQH_OBJECT *epDriver, const AQH_VALUE *v, const char *data) +{ + AQH_MSG_REQUEST *rq; + uint16_t msgId; + AQH_MESSAGE *driverMsg; + + rq=AQH_MsgRequest_new(); + AQH_MsgRequest_SetPrivateData(rq, o); + AQH_MsgRequest_SetEndpoint(rq, epDriver); + + AQH_MsgRequest_SetHandleResponseFn(rq, _subRqHandleResponse); + AQH_MsgRequest_SetAbortFn(rq, _subRqAbort); + + msgId=AQH_Endpoint_GetNextMessageId(epDriver); + AQH_MsgRequest_SetRequestMsgId(rq, msgId); + AQH_MsgRequest_SetTimestamps(rq, R_SETDATA_SUBREQUEST_EXPIRE_SECS); + + driverMsg=AQH_IpcdMessageSetData_new(AQH_MSGTYPE_IPC_DATA_SETDATA, msgId, 0, v, data); + AQH_Endpoint_AddMsgOut(epDriver, driverMsg); + + return rq; +} + + + +int _subRqHandleResponse(AQH_MSG_REQUEST *rq, const AQH_MESSAGE *msg) +{ + DBG_DEBUG(NULL, "Checking message from driver"); + if (AQH_IpcMessage_GetCode(msg)==AQH_MSGTYPE_IPC_DATA_RESULT) { + GWEN_TAG16_LIST *tagList; + + tagList=AQH_IpcMessageTag16_ParsePayload(msg, 0); + if (tagList) { + uint32_t result; + AQH_MSG_REQUEST *rqParent; + + result=AQH_IpcMessageResult_GetResult(tagList); + DBG_INFO(NULL, "Received result for request: %d", result); + AQH_MsgRequest_SetResult(rq, result); + 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); + + GWEN_Tag16_List_free(tagList); + return AQH_MSG_REQUEST_RESULT_HANDLED; + } + else { + DBG_ERROR(NULL, "Bad message %d (no TAG16 data)", AQH_IpcMessage_GetCode(msg)); + } + } + else { + DBG_ERROR(NULL, "Unexpected response message %d", AQH_IpcMessage_GetCode(msg)); + } + + 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); +} + + diff --git a/apps/aqhome-data/s_setdata.h b/apps/aqhome-data/s_setdata.h new file mode 100644 index 0000000..40c62ca --- /dev/null +++ b/apps/aqhome-data/s_setdata.h @@ -0,0 +1,25 @@ +/**************************************************************************** + * 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. + ****************************************************************************/ + +#ifndef AQHOME_DATA_S_SETDATA_H +#define AQHOME_DATA_S_SETDATA_H + + +#include "./server.h" + + +void AqHomeDataServer_HandleSetData(AQH_OBJECT *o, AQH_OBJECT *epSrc, const AQH_MESSAGE *recvdMsg); + + + +#endif + + + + + diff --git a/apps/aqhome-data/s_updatedata.c b/apps/aqhome-data/s_updatedata.c new file mode 100644 index 0000000..9ce3495 --- /dev/null +++ b/apps/aqhome-data/s_updatedata.c @@ -0,0 +1,175 @@ +/**************************************************************************** + * 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 +#endif + + +#include "./s_updatedata.h" + +#include "./server_p.h" +#include +#include "aqhome/ipc2/endpoint.h" +#include "aqhome/msg/ipc/m_ipc.h" +#include "aqhome/msg/ipc/data/m_ipcd.h" +#include "aqhome/msg/ipc/data/m_ipcd_multidata.h" +#include "aqhome/msg/ipc/m_ipc_result.h" +#include "aqhome/msg/ipc/m_ipc_tag16.h" + +#include + + + +/* ------------------------------------------------------------------------------------------------ + * defines + * ------------------------------------------------------------------------------------------------ + */ + +#define DISABLE_DEBUGLOG + + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static int _storeDataPoints(AQHOME_SERVER *xo, const AQH_VALUE *v, const uint64_t *dataPoints, unsigned int numValues); +static void _sendDataChangedMsgToAllClients(AQHOME_SERVER *xo, AQH_OBJECT *epSrc, + const AQH_VALUE *v, const uint64_t *dataPoints, int numValues); + + + +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ + +void AqHomeDataServer_HandleUpdateData(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg) +{ + AQHOME_SERVER *xo; + + xo=AqHomeDataServer_GetServerData(o); + if (xo) { + AQH_MESSAGE *outMsg; + GWEN_TAG16_LIST *tagList; + int resultCode=AQH_MSGDATA_RESULT_SUCCESS; + + tagList=AQH_IpcMessageTag16_ParsePayload(msg, 0); + if (tagList) { + AQH_VALUE *recvdValue; + + recvdValue=AQH_IpcdMessageMultiData_ReadValue(tagList); + if (recvdValue) { + const char *valueName; + const uint64_t *dataPoints=NULL; + uint64_t numberOfPoints=0; + + valueName=recvdValue?AQH_Value_GetName(recvdValue):NULL; + AQH_IpcdMessageMultiData_ReadDatapoints(tagList, &dataPoints, &numberOfPoints); + if (numberOfPoints>0) { + AQH_VALUE *value; + + value=AqHomeDataServer_GetOrCreateValueForDriverWithTemplate(o, ep, recvdValue); + if (value) { + if (AQH_Endpoint_GetPermissions(ep) & AQH_ENDPOINT_PERMS_ADDDATA) { + resultCode=_storeDataPoints(xo, value, dataPoints, numberOfPoints); + if (resultCode==AQH_MSGDATA_RESULT_SUCCESS) + _sendDataChangedMsgToAllClients(xo, ep, value, dataPoints, numberOfPoints); + } + else { + DBG_INFO(NULL, "No permissions to add data to value \"%s\"", valueName); + resultCode=AQH_MSGDATA_RESULT_ERROR_PERMS; + } + } + else { + DBG_INFO(NULL, "No permissions to add/create value \"%s\"", valueName); + resultCode=AQH_MSGDATA_RESULT_ERROR_PERMS; + } + } + else { + DBG_INFO(NULL, "No datapoints"); + resultCode=AQH_MSGDATA_RESULT_ERROR_INVALID; + } + AQH_Value_free(recvdValue); + } + else { + DBG_INFO(NULL, "No value"); + resultCode=AQH_MSGDATA_RESULT_ERROR_INVALID; + } + GWEN_Tag16_List_free(tagList); + } + + outMsg=AQH_IpcMessageResult_new(AQH_IPC_PROTOCOL_DATA_ID, + AQH_IPC_PROTOCOL_DATA_VERSION, + AQH_MSGTYPE_IPC_DATA_RESULT, + AQH_Endpoint_GetNextMessageId(ep), + AQH_IpcMessage_GetMsgId(msg), + resultCode, NULL); + AQH_Endpoint_AddMsgOut(ep, outMsg); + } +} + + + +int _storeDataPoints(AQHOME_SERVER *xo, const AQH_VALUE *v, const uint64_t *dataPoints, unsigned int numValues) +{ + uint32_t i; + + for(i=0; istorage, AQH_Value_GetId(v), timestamp, u.f); + if (rv<0) { + DBG_INFO(NULL, "here (%d)", rv); + return AQH_MSGDATA_RESULT_ERROR_GENERIC; + } + else { + DBG_DEBUG(NULL, "Datapoint added for value \"%s\"", AQH_Value_GetNameForSystem(v)); + } + } /* for */ + + return AQH_MSGDATA_RESULT_SUCCESS; +} + + + +void _sendDataChangedMsgToAllClients(AQHOME_SERVER *xo, AQH_OBJECT *epSrc, const AQH_VALUE *v, const uint64_t *dataPoints, int numValues) +{ + AQH_OBJECT *ep; + + ep=AQH_Object_List_First(xo->tcpClientList); + while(ep) { + if (ep!=epSrc) { + if (AQH_Endpoint_GetFlags(ep) & AQH_ENDPOINT_FLAGS_WANTUPDATES) { + AQH_MESSAGE *msg; + + DBG_DEBUG(AQH_LOGDOMAIN, "Sending update msg to endpoint"); + msg=AQH_IpcdMessageMultiData_new(AQH_MSGTYPE_IPC_DATA_DATACHANGED, + AQH_Endpoint_GetNextMessageId(ep), 0, + v, dataPoints, numValues); + AQH_Endpoint_AddMsgOut(ep, msg); + } + else { + DBG_DEBUG(AQH_LOGDOMAIN, "Endpoint doesn't want updates"); + } + } + else { + DBG_DEBUG(AQH_LOGDOMAIN, "Not sending update msg to source of updates"); + } + ep=AQH_Object_List_Next(ep); + } +} + + + diff --git a/apps/aqhome-data/s_updatedata.h b/apps/aqhome-data/s_updatedata.h new file mode 100644 index 0000000..888155a --- /dev/null +++ b/apps/aqhome-data/s_updatedata.h @@ -0,0 +1,25 @@ +/**************************************************************************** + * 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. + ****************************************************************************/ + +#ifndef AQHOME_DATA_S_UPDATEDATA_H +#define AQHOME_DATA_S_UPDATEDATA_H + + +#include "./server.h" + + +void AqHomeDataServer_HandleUpdateData(AQH_OBJECT *o, AQH_OBJECT *ep, const AQH_MESSAGE *msg); + + + +#endif + + + + + diff --git a/apps/aqhome-data/server.c b/apps/aqhome-data/server.c index a6efb7e..246f18b 100644 --- a/apps/aqhome-data/server.c +++ b/apps/aqhome-data/server.c @@ -15,11 +15,18 @@ #include "./s_connect.h" #include "./s_getdevices.h" #include "./s_getvalues.h" +#include "./s_addvalue.h" +#include "./s_annvalue.h" +#include "./s_updatedata.h" +#include "./s_setdata.h" +#include "./s_getdatapoints.h" +#include "./s_moddevice.h" #include #include #include #include +#include #include #include @@ -71,6 +78,7 @@ static int _handleNewClient(AQH_OBJECT *o, AQH_OBJECT *clientEndpoint); static int _handleClientDown(AQH_OBJECT *o, AQH_OBJECT *clientEndpoint); static void _handleMsgsFromClient(AQH_OBJECT *o, AQHOME_SERVER *xo, AQH_OBJECT *ep); static void _handleMsgFromClient(AQH_OBJECT *o, AQH_OBJECT *ep, AQH_MESSAGE *msg); +static AQH_DEVICE *_getOrCreateDeviceForDriver(AQHOME_SERVER *xo, AQH_OBJECT *epDriver, const char *deviceName); static int _createPidFile(const char *pidFilename); static int _readArgs(int argc, char **argv, GWEN_DB_NODE *dbArgs); @@ -421,22 +429,55 @@ void _handleMsgFromClient(AQH_OBJECT *o, AQH_OBJECT *ep, AQH_MESSAGE *msg) if (protoId==AQH_IPC_PROTOCOL_DATA_ID) { DBG_ERROR(NULL, "Received IPC packet %d (%x)", (int) code, code); switch(code) { - case AQH_MSGTYPE_IPC_DATA_CONNECT_REQ: AqHomeDataServer_HandleConnect(o, ep, msg); break; - case AQH_MSGTYPE_IPC_DATA_UPDATEDATA: break; - case AQH_MSGTYPE_IPC_DATA_GETVALUES_REQ: AqHomeDataServer_HandleGetValues(o, ep, msg); break; - case AQH_MSGTYPE_IPC_DATA_GETDATA_REQ: break; - case AQH_MSGTYPE_IPC_DATA_SETDATA: break; - case AQH_MSGTYPE_IPC_DATA_ADDVALUE: break; - case AQH_MSGTYPE_IPC_DATA_ANNOUNCEVALUE: break; - case AQH_MSGTYPE_IPC_DATA_GETDEVICES_REQ: AqHomeDataServer_HandleGetDevices(o, ep, msg); break; - case AQH_MSGTYPE_IPC_DATA_MODDEVICE_REQ: break; + case AQH_MSGTYPE_IPC_DATA_CONNECT_REQ: AqHomeDataServer_HandleConnect(o, ep, msg); break; + case AQH_MSGTYPE_IPC_DATA_UPDATEDATA: AqHomeDataServer_HandleUpdateData(o, ep, msg); break; + case AQH_MSGTYPE_IPC_DATA_GETVALUES_REQ: AqHomeDataServer_HandleGetValues(o, ep, msg); break; + case AQH_MSGTYPE_IPC_DATA_GETDATA_REQ: AqHomeDataServer_HandleGetDataPoints(o, ep, msg); break; + case AQH_MSGTYPE_IPC_DATA_SETDATA: AqHomeDataServer_HandleSetData(o, ep, msg); break; + case AQH_MSGTYPE_IPC_DATA_ADDVALUE: AqHomeDataServer_HandleAddValue(o, ep, msg); break; + case AQH_MSGTYPE_IPC_DATA_ANNOUNCEVALUE: AqHomeDataServer_HandleAnnounceValue(o, ep, msg); break; + case AQH_MSGTYPE_IPC_DATA_GETDEVICES_REQ: AqHomeDataServer_HandleGetDevices(o, ep, msg); break; + case AQH_MSGTYPE_IPC_DATA_MODDEVICE_REQ: AqHomeDataServer_HandleModDevice(o, ep, msg); break; default: break; } } else { DBG_ERROR(NULL, "Invalid IPC protocol %d (%02x)", protoId, protoId); } +} + + +AQH_OBJECT *AqHomeDataServer_GetIpcEndpointByServiceName(const AQH_OBJECT *o, const char *serviceName) +{ + AQHOME_SERVER *xo; + + xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o); + if (xo) { + AQH_OBJECT *ep; + + ep=AQH_Object_List_First(xo->tcpClientList); + while(ep) { + const char *s; + + s=AQH_Endpoint_GetServiceName(ep); + if (s && *s && strcasecmp(s, serviceName)==0) + return ep; + ep=AQH_Object_List_Next(ep); + } + } + return NULL; +} + + + +void AqHomeDataServer_SendResponseResultToEndpoint(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); } @@ -563,6 +604,98 @@ int AqHomeDataServer_WriteStorageIfChanged(AQH_OBJECT *o) +AQH_VALUE *AqHomeDataServer_GetOrCreateValueForDriverWithTemplate(AQH_OBJECT *o, AQH_OBJECT *epDriver, const AQH_VALUE *valueTemplate) +{ + AQHOME_SERVER *xo; + + xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQHOME_SERVER, o); + if (xo) { + const char *serviceName; + AQH_VALUE *v; + GWEN_BUFFER *buf; + const char *valueName; + const char *deviceName; + + serviceName=AQH_Endpoint_GetServiceName(epDriver); + valueName=AQH_Value_GetName(valueTemplate); + deviceName=AQH_Value_GetDeviceName(valueTemplate); + + buf=GWEN_Buffer_new(0, 256, 0, 1); + if (deviceName && *deviceName) + GWEN_Buffer_AppendArgs(buf, "%s/%s/%s", (serviceName && *serviceName)?serviceName:"unknown", deviceName, valueName); + else + GWEN_Buffer_AppendArgs(buf, "%s/%s", (serviceName && *serviceName)?serviceName:"unknown", valueName); + + v=AQH_Storage_GetValueByNameForSystem(xo->storage, GWEN_Buffer_GetStart(buf)); + if (v==NULL) { + if (AQH_Endpoint_GetPermissions(epDriver) & AQH_ENDPOINT_PERMS_ADDVALUE) { + AQH_DEVICE *device; + + DBG_INFO(AQH_LOGDOMAIN, "Creating value \"%s\"", GWEN_Buffer_GetStart(buf)); + device=(deviceName && *deviceName)?_getOrCreateDeviceForDriver(xo, epDriver, deviceName):NULL; + + v=AQH_Value_new(); + AQH_Value_SetDriver(v, serviceName); + AQH_Value_SetName(v, AQH_Value_GetName(valueTemplate)); + AQH_Value_SetNameForSystem(v, GWEN_Buffer_GetStart(buf)); + AQH_Value_SetValueUnits(v, AQH_Value_GetValueUnits(valueTemplate)); + AQH_Value_SetValueType(v, AQH_Value_GetValueType(valueTemplate)); + AQH_Value_SetModality(v, AQH_Value_GetModality(valueTemplate)); + AQH_Value_SetTimestampCreation(v, (uint64_t) time(NULL)); + if (device) { + AQH_Value_SetDeviceNameForSystem(v, AQH_Device_GetNameForSystem(device)); + AQH_Value_SetDeviceName(v, AQH_Device_GetName(device)); + } + AQH_Storage_AddValue(xo->storage, v); + } + else { + DBG_ERROR(AQH_LOGDOMAIN, "No permissions to create value \"%s\"", GWEN_Buffer_GetStart(buf)); + GWEN_Buffer_free(buf); + return NULL; + } + } + GWEN_Buffer_free(buf); + return v; + } + return NULL; +} + + + +AQH_DEVICE *_getOrCreateDeviceForDriver(AQHOME_SERVER *xo, AQH_OBJECT *epDriver, const char *deviceName) +{ + const char *serviceName; + AQH_DEVICE *device; + GWEN_BUFFER *buf; + + serviceName=AQH_Endpoint_GetServiceName(epDriver); + + buf=GWEN_Buffer_new(0, 256, 0, 1); + GWEN_Buffer_AppendArgs(buf, "%s/%s", (serviceName && *serviceName)?serviceName:"unknown", deviceName); + + device=AQH_Storage_GetDeviceByNameForSystem(xo->storage, GWEN_Buffer_GetStart(buf)); + if (device==NULL) { + if (AQH_Endpoint_GetPermissions(epDriver) & AQH_ENDPOINT_PERMS_ADDDEVICE) { + DBG_INFO(AQH_LOGDOMAIN, "Creating device \"%s\"", GWEN_Buffer_GetStart(buf)); + device=AQH_Device_new(); + AQH_Device_SetDriver(device, serviceName); + AQH_Device_SetName(device, deviceName); + AQH_Device_SetNameForSystem(device, GWEN_Buffer_GetStart(buf)); + AQH_Device_SetTimestampCreation(device, (uint64_t) time(NULL)); + AQH_Storage_AddDevice(xo->storage, device); + } + else { + DBG_ERROR(AQH_LOGDOMAIN, "No permissions to create device \"%s\"", GWEN_Buffer_GetStart(buf)); + GWEN_Buffer_free(buf); + return NULL; + } + } + GWEN_Buffer_free(buf); + return device; +} + + + /* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx * helper functions diff --git a/apps/aqhome-data/server.h b/apps/aqhome-data/server.h index 30cf8b1..d9cf2ab 100644 --- a/apps/aqhome-data/server.h +++ b/apps/aqhome-data/server.h @@ -15,8 +15,6 @@ #include -#define AQH_ENDPOINT_FLAGS_WANTUPDATES 0x0001 - #define AQH_ENDPOINT_PERMS_LISTVALUES 0x0001 #define AQH_ENDPOINT_PERMS_READVALUE 0x0002 #define AQH_ENDPOINT_PERMS_ADDVALUE 0x0004 @@ -39,9 +37,10 @@ int AqHomeDataServer_Init(AQH_OBJECT *o, int argc, char **argv); int AqHomeDataServer_GetTimeout(const AQH_OBJECT *o); int AqHomeDataServer_GetClientNum(const AQH_OBJECT *o); - void AqHomeDataServer_CleanupClients(AQH_OBJECT *o); void AqHomeDataServer_HandleClientMsgs(AQH_OBJECT *o); +AQH_OBJECT *AqHomeDataServer_GetIpcEndpointByServiceName(const AQH_OBJECT *o, const char *serviceName); +void AqHomeDataServer_SendResponseResultToEndpoint(AQH_OBJECT *ep, uint32_t refMsgId, int result); AQH_MSG_REQUEST *AqHomeDataServer_GetRequestTree(const AQH_OBJECT *o); void AqHomeDataServer_AddRequestToTree(AQH_OBJECT *o, AQH_MSG_REQUEST *rq); @@ -50,6 +49,7 @@ void AqHomeDataServer_CleanupRequests(AQH_OBJECT *o); int AqHomeDataServer_LockStorage(AQH_OBJECT *o); int AqHomeDataServer_UnlockStorage(AQH_OBJECT *o); int AqHomeDataServer_WriteStorageIfChanged(AQH_OBJECT *o); +AQH_VALUE *AqHomeDataServer_GetOrCreateValueForDriverWithTemplate(AQH_OBJECT *o, AQH_OBJECT *epDriver, const AQH_VALUE *valueTemplate); diff --git a/apps/aqhome-tool/data/0BUILD b/apps/aqhome-tool/data/0BUILD index ae8f4e5..fc821bc 100644 --- a/apps/aqhome-tool/data/0BUILD +++ b/apps/aqhome-tool/data/0BUILD @@ -38,9 +38,7 @@ getvalues.h getdevices.h adddata.h - addjsondata.h getdatapoints.h - getlastdatapoint.h setdata.h moddevice.h watch.h @@ -53,9 +51,7 @@ getvalues.c getdevices.c adddata.c - addjsondata.c getdatapoints.c - getlastdatapoint.c setdata.c moddevice.c watch.c diff --git a/apps/aqhome-tool/data/adddata.c b/apps/aqhome-tool/data/adddata.c index e46bd02..27eceb5 100644 --- a/apps/aqhome-tool/data/adddata.c +++ b/apps/aqhome-tool/data/adddata.c @@ -11,217 +11,105 @@ #endif #include "./adddata.h" +#include "./client.h" #include "../utils.h" -#include "aqhome/aqhome.h" -#include "aqhome/msg/msg_node.h" -#include "aqhome/ipc/msg_ipc_result.h" -#include "aqhome/ipc/data/msg_data_multidata.h" -#include "aqhome/ipc/data/ipc_data.h" +#include "aqhome/msg/ipc/m_ipc.h" +#include "aqhome/msg/ipc/m_ipc_result.h" +#include "aqhome/msg/ipc/data/m_ipcd.h" +#include "aqhome/msg/ipc/data/m_ipcd_multidata.h" #include #include #include #include +#include +#include -#include -#include +/* ------------------------------------------------------------------------------------------------ + * defs + * ------------------------------------------------------------------------------------------------ + */ #define I18S(msg) msg #define I18N(msg) GWEN_I18N_Translate(PACKAGE, msg) +#define A_ARG GWEN_ARGS_FLAGS_HAS_ARGUMENT +#define A_END (GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST) +#define A_CHAR GWEN_ArgsType_Char +#define A_INT GWEN_ArgsType_Int -static int _doAddData(GWEN_DB_NODE *dbArgs); -static void _sendCommand(GWEN_MSG_ENDPOINT *epTcp, const char *valueName, const char *valueUnits, const char *deviceName, - uint64_t timestampToSend, double dataToSend); + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static AQH_MESSAGE *_createRequestMessage(AQH_OBJECT *o, uint32_t msgId); +static GWEN_JSON_ELEM *_readJsonFile(const char *filename); +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ int AQH_Tool_AddDataPoint(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv) { - GWEN_DB_NODE *dbLocalArgs; + AQH_EVENT_LOOP *eventLoop; + AQH_OBJECT *o; int rv; const GWEN_ARGS args[]= { - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "brokerAddress", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "t", /* short option */ - "tcpaddress", /* long option */ - I18S("Specify TCP address to connect to (defaults to 127.0.0.1)"), - I18S("Specify TCP address to connect to (defaults to 127.0.0.1)") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Int, /* type */ - "brokerPort", /* 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_Int, /* type */ - "timeout", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "T", /* short option */ - NULL, /* long option */ - I18S("Specify timeout in seconds for response"), - I18S("Specify timeout in seconds for response") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "valueName", /* name */ - 1, /* minnum */ - 1, /* maxnum */ - "N", /* short option */ - "valuename", /* long option */ - I18S("Name/path of the value to add (e.g. server/temp/system)"), - I18S("Name/path of the value to add (e.g. server/temp/system)") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "valueUnits", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "U", /* short option */ - "valueunits", /* long option */ - I18S("Units of the value to add (e.g. \"Grad Celsius\")"), - I18S("Units of the value to add (e.g. \"Grad Celsius\")") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "timestamp", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "t", /* short option */ - "timestamp", /* long option */ - I18S("Timestamp of the data (now if omitted)"), - I18S("Timestamp of the data (now if omitted)") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "value", /* name */ - 1, /* minnum */ - 1, /* maxnum */ - "v", /* short option */ - "value", /* long option */ - I18S("Value to write"), - I18S("Value to write") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "device", /* name */ - 1, /* minnum */ - 1, /* maxnum */ - "d", /* short option */ - "device", /* long option */ - I18S("device name"), - I18S("device name") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "brokerClientId", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "c", /* short option */ - "clientid", /* long option */ - I18S("Specify CLIENTID"), - I18S("Specify CLIENTID") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "userId", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "u", /* short option */ - "userid", /* long option */ - I18S("Specify user id"), - I18S("Specify user id") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "password", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "p", /* short option */ - "password", /* long option */ - I18S("Specify service password"), - I18S("Specify service password") - }, - { - GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST, /* flags */ - GWEN_ArgsType_Int, /* type */ - "help", /* name */ - 0, /* minnum */ - 0, /* maxnum */ - "h", /* short option */ - "help", /* long option */ - "Show this help screen", /* short description */ - "Show this help screen" /* long description */ - } + /* flags type name min max s long short_descr, long_descr */ + { A_ARG, A_CHAR, "brokerAddress", 0, 1, "t", "tcpaddress", I18S("TCP address to connect to [127.0.0.1]"), NULL}, + { A_ARG, A_INT, "brokerPort", 0, 1, "P", "tcpport", I18S("Specify the TCP port to listen on"), NULL}, + { A_ARG, A_INT, "timeout", 0, 1, "T", NULL, I18S("Specify timeout in seconds for response"), NULL}, + { A_ARG, A_CHAR, "brokerClientId", 0, 1, "c", "clientid", I18S("Specify CLIENTID"), NULL}, + { A_ARG, A_CHAR, "userId", 0, 1, "u", "userid", I18S("Specify user id"), NULL}, + { A_ARG, A_CHAR, "password", 0, 1, "p", "password", I18S("Specify service password"), NULL}, + { A_ARG, A_CHAR, "valueName", 1, 1, "N", "valuename", I18S("Value name (e.g. server/temp/system)"), NULL}, + { A_ARG, A_CHAR, "valueUnits", 0, 1, "U", "valueunits", I18S("Value units (e.g. \"Grad Celsius\")"), NULL}, + { A_ARG, A_CHAR, "value", 1, 1, "v", "value", I18S("Value to set"), NULL}, + { A_ARG, A_CHAR, "device", 1, 1, "d", "device", I18S("device name"), NULL}, + { A_ARG, A_CHAR, "timestamp", 0, 1, "ts", "timestamp", I18S("Timestamp of the data (now if omitted)"), NULL}, + { A_ARG, A_CHAR, "jsonFile", 0, 1, "J", "jsonfile", I18S("Name/path of a JSON file to read"), NULL}, + { A_END, A_INT, "help", 0, 0, "h", "help", I18S("Show this help screen"), NULL} }; - dbLocalArgs=GWEN_DB_GetGroup(dbGlobalArgs, GWEN_DB_FLAGS_DEFAULT, "local"); - rv=GWEN_Args_Check(argc, argv, 1, - GWEN_ARGS_MODE_ALLOW_FREEPARAM, - args, - dbLocalArgs); - if (rv==GWEN_ARGS_RESULT_ERROR) { - fprintf(stderr, "ERROR: Could not parse arguments\n"); - return 1; - } - else if (rv==GWEN_ARGS_RESULT_HELP) { - GWEN_BUFFER *ubuf; - - ubuf=GWEN_Buffer_new(0, 1024, 0, 1); - if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) { - fprintf(stderr, "ERROR: Could not create help string\n"); - return 1; - } - fprintf(stderr, "%s\n", GWEN_Buffer_GetStart(ubuf)); - GWEN_Buffer_free(ubuf); - return 0; - } - - AQH_MergeConfigFileIntoConfig(dbLocalArgs, "ConfigFile"); - - return _doAddData(dbLocalArgs); + eventLoop=AQH_EventLoop_new(); + o=AQH_ToolClient_new(eventLoop, dbGlobalArgs, args); + AQH_ToolClient_SetCreateRequestMessageFn(o, _createRequestMessage); + rv=AQH_ToolClient_ReadLocalArgs(o, argc, argv); + if (rv!=0) + return rv; + rv=AQH_ToolClient_Run(o); + AQH_Object_free(o); + AQH_EventLoop_free(eventLoop); + return rv; } -int _doAddData(GWEN_DB_NODE *dbArgs) +AQH_MESSAGE *_createRequestMessage(GWEN_UNUSED AQH_OBJECT *o, uint32_t msgId) { - GWEN_MSG_ENDPOINT *epTcp; - int timeoutInSeconds; + AQH_MESSAGE *msg; + GWEN_DB_NODE *dbArgs; const char *valueName; - const char *deviceName; const char *valueUnits; - const char *valueTimestamp; const char *valueData; + const char *deviceName; + const char *valueTimestamp; uint64_t timestampToSend; double dataToSend; - GWEN_MSG *msg; + const char *jsonFile; + GWEN_JSON_ELEM *jRoot=NULL; + AQH_VALUE *v; int rv; - timeoutInSeconds=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 5); + dbArgs=AQH_ToolClient_GetDbLocalArgs(o); + jsonFile=GWEN_DB_GetCharValue(dbArgs, "jsonFile", 0, NULL); deviceName=GWEN_DB_GetCharValue(dbArgs, "device", 0, NULL); valueName=GWEN_DB_GetCharValue(dbArgs, "valueName", 0, NULL); valueUnits=GWEN_DB_GetCharValue(dbArgs, "valueUnits", 0, NULL); @@ -229,12 +117,12 @@ int _doAddData(GWEN_DB_NODE *dbArgs) valueData=GWEN_DB_GetCharValue(dbArgs, "value", 0, NULL); if (!(valueName && *valueName)) { DBG_ERROR(NULL, "ERROR: Missing value name"); - return 1; + return NULL; } if (!(valueData && *valueData)) { DBG_ERROR(NULL, "ERROR: Missing data"); - return 1; + return NULL; } if (valueTimestamp && *valueTimestamp) { @@ -242,92 +130,103 @@ int _doAddData(GWEN_DB_NODE *dbArgs) if (1!=sscanf("%lu", valueTimestamp, &x)) { DBG_ERROR(NULL, "ERROR: Invalid timestamp"); - return 1; + return NULL; } timestampToSend=(uint64_t) x; } else timestampToSend=(uint64_t) time(NULL); + if (jsonFile && *jsonFile) { + const char *s; + + jRoot=_readJsonFile(jsonFile); + if (jRoot==NULL) { + DBG_ERROR(NULL, "ERROR: Error reading JSON file \"%s\"", jsonFile); + return NULL; + } + + if (valueData[0]=='@') { + GWEN_JSON_ELEM *j; + + j=GWEN_JsonElement_GetElementByPath(jRoot, valueData+1, GWEN_PATH_FLAGS_PATHMUSTEXIST); + if (j==NULL) { + DBG_ERROR(NULL, "ERROR: Path \"%s\" not found in JSON file \"%s\"", valueData+1, jsonFile); + GWEN_JsonElement_free(jRoot); + return NULL; + } + + s=GWEN_JsonElement_GetData(j); + if (!(s && *s)) { + DBG_ERROR(NULL, "ERROR: Path \"%s\" in JSON file \"%s\" has no data", valueData+1, jsonFile); + GWEN_JsonElement_free(jRoot); + return NULL; + } + valueData=s; + } + + if (valueUnits) { + if (valueUnits[0]=='@') { + GWEN_JSON_ELEM *j; + + j=GWEN_JsonElement_GetElementByPath(jRoot, valueUnits+1, GWEN_PATH_FLAGS_PATHMUSTEXIST); + if (j==NULL) { + DBG_ERROR(NULL, "ERROR: Path \"%s\" not found in JSON file \"%s\"", valueUnits+1, jsonFile); + return NULL; + } + s=GWEN_JsonElement_GetData(j); + if (!(s && *s)) { + DBG_ERROR(NULL, "ERROR: Path \"%s\" in JSON file \"%s\" has no data", valueUnits+1, jsonFile); + return NULL; + } + valueUnits=s; + } + } + } /* if json */ + rv=GWEN_Text_StringToDouble(valueData, &dataToSend); if (rv<0) { DBG_ERROR(NULL, "ERROR: Invalid data"); - return 1; + GWEN_JsonElement_free(jRoot); + return NULL; } - /*fprintf(stdout, "Sending AddData request\n");*/ + v=AQH_Value_new(); + AQH_Value_SetName(v, valueName); + AQH_Value_SetValueUnits(v, valueUnits); + AQH_Value_SetDeviceName(v, deviceName); - epTcp=Utils_SetupBrokerClientEndpoint(dbArgs, 0); - if (epTcp==NULL) { - DBG_ERROR(NULL, "ERROR creating TCP connection"); - return 2; - } - - _sendCommand(epTcp, valueName, valueUnits, deviceName, timestampToSend, dataToSend); - - for (;;) { - uint16_t code; - - msg=Utils_WaitForSpecificIpcMessage(epTcp, AQH_MSGTYPE_IPC_DATA_RESULT, timeoutInSeconds); - if (msg==NULL) { - DBG_ERROR(NULL, "No response received"); - return 2; - } - code=GWEN_IpcMsg_GetCode(msg); - if (code==AQH_MSGTYPE_IPC_DATA_RESULT) { - uint32_t resultCode; - - resultCode=AQH_ResultIpcMsg_GetResultCode(msg); - if (resultCode!=AQH_MSG_IPC_SUCCESS) { - fprintf(stderr, "ERROR: %d\n", resultCode); - GWEN_MsgEndpoint_free(epTcp); - return 3; - } - else { - /*fprintf(stdout, "Data added.\n");*/ - break; - } - } - else { - DBG_INFO(NULL, "Unexpected message \"%d\"", code); - GWEN_MsgEndpoint_free(epTcp); - return 3; - } - } /* for */ - - GWEN_MsgEndpoint_free(epTcp); - return 0; + msg=AQH_IpcdMessageMultiData_newForOne(AQH_MSGTYPE_IPC_DATA_UPDATEDATA, msgId, 0, v, timestampToSend, dataToSend); + AQH_Value_free(v); + GWEN_JsonElement_free(jRoot); + return msg; } -void _sendCommand(GWEN_MSG_ENDPOINT *epTcp, const char *valueName, const char *valueUnits, - const char *deviceName, - uint64_t timestampToSend, double dataToSend) +GWEN_JSON_ELEM *_readJsonFile(const char *filename) { - GWEN_MSG *msgOut; - union {double f; uint64_t i;} u; - uint64_t arrayToSend[2]; - AQH_VALUE *value; + GWEN_BUFFER *buf; + int rv; + GWEN_JSON_ELEM *j; - u.f=dataToSend; - arrayToSend[0]=timestampToSend; - arrayToSend[1]=u.i; + buf=GWEN_Buffer_new(0, 256, 0, 1); + rv=GWEN_SyncIo_Helper_ReadFile(filename, buf); + if (rv<0) { + DBG_ERROR(NULL, "Error reading file \"%s\" (%d)", filename, rv); + GWEN_Buffer_free(buf); + return NULL; + } - value=AQH_Value_new(); - AQH_Value_SetName(value, valueName); - AQH_Value_SetValueUnits(value, valueUnits); - AQH_Value_SetDeviceName(value, deviceName); + j=GWEN_JsonElement_fromString(GWEN_Buffer_GetStart(buf)); + GWEN_Buffer_free(buf); + if (j==NULL) { + DBG_ERROR(NULL, "Invalid or no JSON data in file \"%s\"", filename); + return NULL; + } - msgOut=AQH_MultiDataDataIpcMsg_new(AQH_MSGTYPE_IPC_DATA_UPDATEDATA, - GWEN_MsgEndpoint_GetNextMessageId(epTcp), 0, - value, arrayToSend, 1); - AQH_Value_free(value); - GWEN_MsgEndpoint_AddSendMessage(epTcp, msgOut); + return j; } - - - diff --git a/apps/aqhome-tool/data/addjsondata.c b/apps/aqhome-tool/data/addjsondata.c deleted file mode 100644 index c3ad390..0000000 --- a/apps/aqhome-tool/data/addjsondata.c +++ /dev/null @@ -1,425 +0,0 @@ -/**************************************************************************** - * 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 "./adddata.h" -#include "../utils.h" - -#include "aqhome/aqhome.h" -#include "aqhome/msg/msg_node.h" -#include "aqhome/ipc/msg_ipc_result.h" -#include "aqhome/ipc/data/msg_data_multidata.h" -#include "aqhome/ipc/data/ipc_data.h" - -#include -#include -#include -#include -#include -#include - -#include -#include - - -#define I18S(msg) msg -#define I18N(msg) GWEN_I18N_Translate(PACKAGE, msg) - - -static int _doAddData(GWEN_DB_NODE *dbArgs); -static void _sendCommand(GWEN_MSG_ENDPOINT *epTcp, const char *valueName, const char *valueUnits, const char *deviceName, - uint64_t timestampToSend, double dataToSend); -static GWEN_JSON_ELEM *_readJsonFile(const char *filename); - - - - -int AQH_Tool_AddJsonDataPoint(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv) -{ - GWEN_DB_NODE *dbLocalArgs; - int rv; - const GWEN_ARGS args[]= { - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "brokerAddress", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "t", /* short option */ - "tcpaddress", /* long option */ - I18S("Specify TCP address to connect to (defaults to 127.0.0.1)"), - I18S("Specify TCP address to connect to (defaults to 127.0.0.1)") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Int, /* type */ - "brokerPort", /* 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_Int, /* type */ - "timeout", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "T", /* short option */ - NULL, /* long option */ - I18S("Specify timeout in seconds for response"), - I18S("Specify timeout in seconds for response") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "jsonFile", /* name */ - 1, /* minnum */ - 1, /* maxnum */ - "J", /* short option */ - "jsonfile", /* long option */ - I18S("Name/path of the JSON file to read"), - I18S("Name/path of the JSON file to read") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "valueName", /* name */ - 1, /* minnum */ - 1, /* maxnum */ - "N", /* short option */ - "valuename", /* long option */ - I18S("Name/path of the value to add (e.g. server/temp/system)"), - I18S("Name/path of the value to add (e.g. server/temp/system)") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "valueUnits", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "U", /* short option */ - "valueunits", /* long option */ - I18S("Units of the value to add (e.g. \"Grad Celsius\")"), - I18S("Units of the value to add (e.g. \"Grad Celsius\")") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "timestamp", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "t", /* short option */ - "timestamp", /* long option */ - I18S("Timestamp of the data (now if omitted)"), - I18S("Timestamp of the data (now if omitted)") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "value", /* name */ - 1, /* minnum */ - 1, /* maxnum */ - "v", /* short option */ - "value", /* long option */ - I18S("Value to write"), - I18S("Value to write") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "device", /* name */ - 1, /* minnum */ - 1, /* maxnum */ - "d", /* short option */ - "device", /* long option */ - I18S("device name"), - I18S("device name") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "brokerClientId", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "c", /* short option */ - "clientid", /* long option */ - I18S("Specify CLIENTID"), - I18S("Specify CLIENTID") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "userId", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "u", /* short option */ - "userid", /* long option */ - I18S("Specify user id"), - I18S("Specify user id") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "password", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "p", /* short option */ - "password", /* long option */ - I18S("Specify service password"), - I18S("Specify service password") - }, - { - GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST, /* flags */ - GWEN_ArgsType_Int, /* type */ - "help", /* name */ - 0, /* minnum */ - 0, /* maxnum */ - "h", /* short option */ - "help", /* long option */ - "Show this help screen", /* short description */ - "Show this help screen" /* long description */ - } - }; - - dbLocalArgs=GWEN_DB_GetGroup(dbGlobalArgs, GWEN_DB_FLAGS_DEFAULT, "local"); - rv=GWEN_Args_Check(argc, argv, 1, - GWEN_ARGS_MODE_ALLOW_FREEPARAM, - args, - dbLocalArgs); - if (rv==GWEN_ARGS_RESULT_ERROR) { - fprintf(stderr, "ERROR: Could not parse arguments\n"); - return 1; - } - else if (rv==GWEN_ARGS_RESULT_HELP) { - GWEN_BUFFER *ubuf; - - ubuf=GWEN_Buffer_new(0, 1024, 0, 1); - if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) { - fprintf(stderr, "ERROR: Could not create help string\n"); - return 1; - } - fprintf(stderr, "%s\n", GWEN_Buffer_GetStart(ubuf)); - GWEN_Buffer_free(ubuf); - return 0; - } - - AQH_MergeConfigFileIntoConfig(dbLocalArgs, "ConfigFile"); - - return _doAddData(dbLocalArgs); -} - - - -int _doAddData(GWEN_DB_NODE *dbArgs) -{ - GWEN_MSG_ENDPOINT *epTcp; - int timeoutInSeconds; - GWEN_JSON_ELEM *jRoot; - const char *jsonFile; - const char *valueName; - const char *deviceName; - const char *valueUnits; - const char *valueTimestamp; - const char *valueData; - uint64_t timestampToSend; - double dataToSend; - const char *s; - GWEN_MSG *msg; - int rv; - - timeoutInSeconds=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 5); - jsonFile=GWEN_DB_GetCharValue(dbArgs, "jsonFile", 0, NULL); - deviceName=GWEN_DB_GetCharValue(dbArgs, "device", 0, NULL); - valueName=GWEN_DB_GetCharValue(dbArgs, "valueName", 0, NULL); - valueUnits=GWEN_DB_GetCharValue(dbArgs, "valueUnits", 0, NULL); - valueTimestamp=GWEN_DB_GetCharValue(dbArgs, "timestamp", 0, NULL); - valueData=GWEN_DB_GetCharValue(dbArgs, "value", 0, NULL); - - if (!(jsonFile && *jsonFile)) { - DBG_ERROR(NULL, "ERROR: Missing JSON file name"); - return 1; - } - - if (!(valueName && *valueName)) { - DBG_ERROR(NULL, "ERROR: Missing value name"); - return 1; - } - - if (!(valueData && *valueData)) { - DBG_ERROR(NULL, "ERROR: Missing data"); - return 1; - } - - if (valueTimestamp && *valueTimestamp) { - unsigned long int x; - - if (1!=sscanf("%lu", valueTimestamp, &x)) { - DBG_ERROR(NULL, "ERROR: Invalid timestamp"); - return 1; - } - timestampToSend=(uint64_t) x; - } - else - timestampToSend=(uint64_t) time(NULL); - - jRoot=_readJsonFile(jsonFile); - if (jRoot==NULL) { - DBG_ERROR(NULL, "ERROR: Error reading JSON file \"%s\"", jsonFile); - return 2; - } - - if (valueData[0]=='@') { - GWEN_JSON_ELEM *j; - - j=GWEN_JsonElement_GetElementByPath(jRoot, valueData+1, GWEN_PATH_FLAGS_PATHMUSTEXIST); - if (j==NULL) { - DBG_ERROR(NULL, "ERROR: Path \"%s\" not found in JSON file \"%s\"", valueData+1, jsonFile); - GWEN_JsonElement_free(jRoot); - return 1; - } - - s=GWEN_JsonElement_GetData(j); - if (!(s && *s)) { - DBG_ERROR(NULL, "ERROR: Path \"%s\" in JSON file \"%s\" has no data", valueData+1, jsonFile); - GWEN_JsonElement_free(jRoot); - return 1; - } - valueData=s; - } - - if (valueUnits) { - if (valueUnits[0]=='@') { - GWEN_JSON_ELEM *j; - - j=GWEN_JsonElement_GetElementByPath(jRoot, valueUnits+1, GWEN_PATH_FLAGS_PATHMUSTEXIST); - if (j==NULL) { - DBG_ERROR(NULL, "ERROR: Path \"%s\" not found in JSON file \"%s\"", valueUnits+1, jsonFile); - return 1; - } - s=GWEN_JsonElement_GetData(j); - if (!(s && *s)) { - DBG_ERROR(NULL, "ERROR: Path \"%s\" in JSON file \"%s\" has no data", valueUnits+1, jsonFile); - return 1; - } - valueUnits=s; - } - } - - rv=GWEN_Text_StringToDouble(valueData, &dataToSend); - if (rv<0) { - DBG_ERROR(NULL, "ERROR: Invalid data"); - return 1; - } - - /*fprintf(stdout, "Sending AddData request\n");*/ - - epTcp=Utils_SetupBrokerClientEndpoint(dbArgs, 0); - if (epTcp==NULL) { - DBG_ERROR(NULL, "ERROR creating TCP connection"); - return 2; - } - - _sendCommand(epTcp, valueName, valueUnits, deviceName, timestampToSend, dataToSend); - - GWEN_JsonElement_free(jRoot); - - for (;;) { - uint16_t code; - - msg=Utils_WaitForSpecificIpcMessage(epTcp, AQH_MSGTYPE_IPC_DATA_RESULT, timeoutInSeconds); - if (msg==NULL) { - DBG_ERROR(NULL, "No response received"); - return 2; - } - code=GWEN_IpcMsg_GetCode(msg); - if (code==AQH_MSGTYPE_IPC_DATA_RESULT) { - uint32_t resultCode; - - resultCode=AQH_ResultIpcMsg_GetResultCode(msg); - if (resultCode!=AQH_MSG_IPC_SUCCESS) { - fprintf(stderr, "ERROR: %d\n", resultCode); - GWEN_MsgEndpoint_free(epTcp); - return 3; - } - else { - /*fprintf(stdout, "Data added.\n");*/ - break; - } - } - else { - DBG_INFO(NULL, "Unexpected message \"%d\"", code); - GWEN_MsgEndpoint_free(epTcp); - return 3; - } - } /* for */ - - GWEN_MsgEndpoint_free(epTcp); - return 0; -} - - - -void _sendCommand(GWEN_MSG_ENDPOINT *epTcp, const char *valueName, const char *valueUnits, - const char *deviceName, - uint64_t timestampToSend, double dataToSend) -{ - GWEN_MSG *msgOut; - union {double f; uint64_t i;} u; - uint64_t arrayToSend[2]; - AQH_VALUE *value; - - u.f=dataToSend; - arrayToSend[0]=timestampToSend; - arrayToSend[1]=u.i; - - value=AQH_Value_new(); - AQH_Value_SetName(value, valueName); - AQH_Value_SetValueUnits(value, valueUnits); - AQH_Value_SetDeviceName(value, deviceName); - - msgOut=AQH_MultiDataDataIpcMsg_new(AQH_MSGTYPE_IPC_DATA_UPDATEDATA, - GWEN_MsgEndpoint_GetNextMessageId(epTcp), 0, - value, arrayToSend, 1); - AQH_Value_free(value); - GWEN_MsgEndpoint_AddSendMessage(epTcp, msgOut); -} - - - -GWEN_JSON_ELEM *_readJsonFile(const char *filename) -{ - GWEN_BUFFER *buf; - int rv; - GWEN_JSON_ELEM *j; - - buf=GWEN_Buffer_new(0, 256, 0, 1); - rv=GWEN_SyncIo_Helper_ReadFile(filename, buf); - if (rv<0) { - DBG_ERROR(NULL, "Error reading file \"%s\" (%d)", filename, rv); - GWEN_Buffer_free(buf); - return NULL; - } - - j=GWEN_JsonElement_fromString(GWEN_Buffer_GetStart(buf)); - GWEN_Buffer_free(buf); - if (j==NULL) { - DBG_ERROR(NULL, "Invalid or no JSON data in file \"%s\"", filename); - return NULL; - } - - return j; -} - - - diff --git a/apps/aqhome-tool/data/client.c b/apps/aqhome-tool/data/client.c index 443b1b3..d366408 100644 --- a/apps/aqhome-tool/data/client.c +++ b/apps/aqhome-tool/data/client.c @@ -17,6 +17,8 @@ #include "aqhome/msg/ipc/m_ipc.h" #include "aqhome/msg/ipc/m_ipc_tag16.h" #include "aqhome/msg/ipc/m_ipc_result.h" +#include "aqhome/msg/ipc/data/m_ipcd.h" +#include "aqhome/msg/ipc/data/m_ipcd_connect.h" #include "aqhome/ipc2/endpoint.h" #include @@ -34,8 +36,10 @@ GWEN_INHERIT(AQH_OBJECT, AQH_TOOL_CLIENT) static void GWENHYWFAR_CB _freeData(void *bp, void *p); static int _sendWaitHandle(AQH_OBJECT *o, AQH_TOOL_CLIENT *xo); +static int _waitAndHandle(AQH_OBJECT *o, AQH_TOOL_CLIENT *xo, uint32_t msgId); +static int _exchangeConnect(AQH_OBJECT *o, AQH_TOOL_CLIENT *xo, uint32_t flags); static AQH_MESSAGE *_createRequestMessage(AQH_OBJECT *o, uint32_t msgId); -static int _handleResponseMessage(AQH_OBJECT *o, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList); +static int _handleResponseMessage(AQH_OBJECT *o, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList, int first); @@ -62,8 +66,6 @@ void GWENHYWFAR_CB _freeData(GWEN_UNUSED void *bp, void *p) AQH_TOOL_CLIENT *xo; xo=(AQH_TOOL_CLIENT*)p; - GWEN_DB_Group_free(xo->dbLocalArgs); - GWEN_DB_Group_free(xo->dbGlobalArgs); AQH_Object_free(xo->ipcEndpoint); GWEN_FREE_OBJECT(xo); } @@ -131,17 +133,94 @@ void AQH_ToolClient_SetHandleResponseMessageFn(AQH_OBJECT *o, AQH_TOOLCLIENT_HAN +GWEN_DB_NODE *AQH_ToolClient_GetDbGlobalArgs(const AQH_OBJECT *o) +{ + AQH_TOOL_CLIENT *xo; + + xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQH_TOOL_CLIENT, o); + if (xo) + return xo->dbGlobalArgs; + return NULL; +} + + + +GWEN_DB_NODE *AQH_ToolClient_GetDbLocalArgs(const AQH_OBJECT *o) +{ + AQH_TOOL_CLIENT *xo; + + xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQH_TOOL_CLIENT, o); + if (xo) + return xo->dbLocalArgs; + return NULL; +} + + + +uint32_t AQH_ToolClient_GetFlags(const AQH_OBJECT *o) +{ + AQH_TOOL_CLIENT *xo; + + xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQH_TOOL_CLIENT, o); + if (xo) + return xo->flags; + return 0; +} + + + +void AQH_ToolClient_SetFlags(AQH_OBJECT *o, uint32_t f) +{ + AQH_TOOL_CLIENT *xo; + + xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQH_TOOL_CLIENT, o); + if (xo) + xo->flags=f; +} + + + +void AQH_ToolClient_AddFlags(AQH_OBJECT *o, uint32_t f) +{ + AQH_TOOL_CLIENT *xo; + + xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQH_TOOL_CLIENT, o); + if (xo) + xo->flags|=f; +} + + + +void AQH_ToolClient_SubFlags(AQH_OBJECT *o, uint32_t f) +{ + AQH_TOOL_CLIENT *xo; + + xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQH_TOOL_CLIENT, o); + if (xo) + xo->flags&=~f; +} + + + int AQH_ToolClient_Run(AQH_OBJECT *o) { AQH_TOOL_CLIENT *xo; xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQH_TOOL_CLIENT, o); if (xo) { + int rv; + xo->ipcEndpoint=Utils2_SetupBrokerClientEndpoint(AQH_Object_GetEventLoop(o), xo->dbLocalArgs, 0); if (xo->ipcEndpoint==NULL) { DBG_ERROR(NULL, "ERROR creating TCP connection"); return 2; } + + rv=_exchangeConnect(o, xo, xo->flags); + if (rv!=AQH_MSGDATA_RESULT_SUCCESS) { + DBG_ERROR(NULL, "Connect response: %d", rv); + return 2; + } return _sendWaitHandle(o, xo); } return GWEN_ERROR_INVALID; @@ -149,13 +228,54 @@ int AQH_ToolClient_Run(AQH_OBJECT *o) +int AQH_ToolClient_Watch(AQH_OBJECT *o) +{ + AQH_TOOL_CLIENT *xo; + + xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQH_TOOL_CLIENT, o); + if (xo) { + int rv; + + xo->ipcEndpoint=Utils2_SetupBrokerClientEndpoint(AQH_Object_GetEventLoop(o), xo->dbLocalArgs, 0); + if (xo->ipcEndpoint==NULL) { + DBG_ERROR(NULL, "ERROR creating TCP connection"); + return 2; + } + + rv=_exchangeConnect(o, xo, xo->flags); + if (rv!=AQH_MSGDATA_RESULT_SUCCESS) { + DBG_ERROR(NULL, "Connect response: %d", rv); + return 2; + } + return _waitAndHandle(o, xo, 0); + } + return GWEN_ERROR_INVALID; +} + + + +int AQH_ToolClient_HandleResultMsg(GWEN_UNUSED const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList, GWEN_UNUSED int first) +{ + int result; + + result=AQH_IpcMessageResult_GetResult(tagList); + if (result!=AQH_MSGDATA_RESULT_SUCCESS) { + char *text; + + text=AQH_IpcMessageResult_GetText(tagList); + DBG_ERROR(NULL, "ERROR: %d (%s)", result, text?text:""); + return 3; + } + return 0; +} + + + int _sendWaitHandle(AQH_OBJECT *o, AQH_TOOL_CLIENT *xo) { - AQH_EVENT_LOOP *eventLoop; AQH_MESSAGE *msgOut; uint32_t msgId; - eventLoop=AQH_Object_GetEventLoop(o); msgId=AQH_Endpoint_GetNextMessageId(xo->ipcEndpoint); msgOut=_createRequestMessage(o, msgId); if (msgOut==NULL) { @@ -164,6 +284,18 @@ int _sendWaitHandle(AQH_OBJECT *o, AQH_TOOL_CLIENT *xo) } AQH_Endpoint_AddMsgOut(xo->ipcEndpoint, msgOut); + return _waitAndHandle(o, xo, msgId); +} + + + +int _waitAndHandle(AQH_OBJECT *o, AQH_TOOL_CLIENT *xo, uint32_t msgId) +{ + AQH_EVENT_LOOP *eventLoop; + int first=1; + + eventLoop=AQH_Object_GetEventLoop(o); + for (;;) { AQH_MESSAGE *msgIn; @@ -175,8 +307,10 @@ int _sendWaitHandle(AQH_OBJECT *o, AQH_TOOL_CLIENT *xo) if (tagList) { int rv; - rv=_handleResponseMessage(o, msgIn, tagList); + rv=_handleResponseMessage(o, msgIn, tagList, first); + GWEN_Tag16_List_free(tagList); AQH_Message_free(msgIn); + first=0; if (rv<0) { DBG_ERROR(NULL, "here (%d)", rv); return 3; @@ -193,6 +327,60 @@ int _sendWaitHandle(AQH_OBJECT *o, AQH_TOOL_CLIENT *xo) +int _exchangeConnect(AQH_OBJECT *o, AQH_TOOL_CLIENT *xo, uint32_t flags) +{ + AQH_EVENT_LOOP *eventLoop; + AQH_MESSAGE *msgOut; + uint32_t msgId; + const char *clientId; + const char *userId; + const char *passw; + + clientId=GWEN_DB_GetCharValue(xo->dbLocalArgs, "brokerClientId", 0, "aqhome-tool"); + userId=GWEN_DB_GetCharValue(xo->dbLocalArgs, "userId", 0, NULL); + passw=GWEN_DB_GetCharValue(xo->dbLocalArgs, "password", 0, NULL); + + eventLoop=AQH_Object_GetEventLoop(o); + msgId=AQH_Endpoint_GetNextMessageId(xo->ipcEndpoint); + msgOut=AQH_IpcdMessageConnect_new(AQH_MSGTYPE_IPC_DATA_CONNECT_REQ, msgId, 0, clientId, userId, passw, flags); + AQH_Endpoint_AddMsgOut(xo->ipcEndpoint, msgOut); + + for (;;) { + AQH_MESSAGE *msgIn; + + msgIn=Utils2_WaitForResponseMsg(eventLoop, xo->ipcEndpoint, msgId, xo->timeoutInSeconds); + if (msgIn) { + GWEN_TAG16_LIST *tagList; + + tagList=AQH_IpcMessageTag16_ParsePayload(msgIn, 0); + if (tagList) { + int code; + + code=AQH_IpcMessage_GetCode(msgIn); + if (code==AQH_MSGTYPE_IPC_DATA_RESULT) { + int result; + + result=AQH_IpcMessageResult_GetResult(tagList); + GWEN_Tag16_List_free(tagList); + AQH_Message_free(msgIn); + return result; + } + else { + DBG_ERROR(NULL, "Unexpected message received (%x)", code); + GWEN_Tag16_List_free(tagList); + AQH_Message_free(msgIn); + return AQH_MSGDATA_RESULT_ERROR_GENERIC; + } + GWEN_Tag16_List_free(tagList); + } + AQH_Message_free(msgIn); + } + } /* for */ + return AQH_MSGDATA_RESULT_ERROR_GENERIC; +} + + + AQH_MESSAGE *_createRequestMessage(AQH_OBJECT *o, uint32_t msgId) { AQH_TOOL_CLIENT *xo; @@ -207,14 +395,25 @@ AQH_MESSAGE *_createRequestMessage(AQH_OBJECT *o, uint32_t msgId) -int _handleResponseMessage(AQH_OBJECT *o, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList) +int _handleResponseMessage(AQH_OBJECT *o, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList, int first) { AQH_TOOL_CLIENT *xo; xo=GWEN_INHERIT_GETDATA(AQH_OBJECT, AQH_TOOL_CLIENT, o); if (xo) { if (xo->handleResponseMessageFn) - return xo->handleResponseMessageFn(o, msg, tagList); + return xo->handleResponseMessageFn(o, msg, tagList, first); + else { + uint16_t code; + + code=AQH_IpcMessage_GetCode(msg); + if (code==AQH_MSGTYPE_IPC_DATA_RESULT) + return AQH_ToolClient_HandleResultMsg(msg, tagList, first); + else { + DBG_INFO(NULL, "Unexpected message \"%d\"", code); + return 3; + } + } } return 0; } diff --git a/apps/aqhome-tool/data/client.h b/apps/aqhome-tool/data/client.h index 49a4b35..cd4775e 100644 --- a/apps/aqhome-tool/data/client.h +++ b/apps/aqhome-tool/data/client.h @@ -17,12 +17,30 @@ typedef AQH_MESSAGE* (*AQH_TOOLCLIENT_CREATEREQUESTMESSAGE_FN)(AQH_OBJECT *o, uint32_t msgId); -typedef int (*AQH_TOOLCLIENT_HANDLERESPONSEMESSAGE_FN)(AQH_OBJECT *o, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList); +typedef int (*AQH_TOOLCLIENT_HANDLERESPONSEMESSAGE_FN)(AQH_OBJECT *o, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList, int first); AQH_OBJECT *AQH_ToolClient_new(AQH_EVENT_LOOP *eventLoop, GWEN_DB_NODE *dbGlobalArgs, const GWEN_ARGS *argDescrs); int AQH_ToolClient_ReadLocalArgs(AQH_OBJECT *o, int argc, char **argv); int AQH_ToolClient_Run(AQH_OBJECT *o); +int AQH_ToolClient_Watch(AQH_OBJECT *o); + +GWEN_DB_NODE *AQH_ToolClient_GetDbGlobalArgs(const AQH_OBJECT *o); +GWEN_DB_NODE *AQH_ToolClient_GetDbLocalArgs(const AQH_OBJECT *o); + +uint32_t AQH_ToolClient_GetFlags(const AQH_OBJECT *o); +void AQH_ToolClient_SetFlags(AQH_OBJECT *o, uint32_t f); +void AQH_ToolClient_AddFlags(AQH_OBJECT *o, uint32_t f); +void AQH_ToolClient_SubFlags(AQH_OBJECT *o, uint32_t f); + + +/** + * @param o client object + * @param msg received message + * @param tagList list of GWEN_TAG16 objects + * @param first msg is the first response received + */ +int AQH_ToolClient_HandleResultMsg(const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList, int first); void AQH_ToolClient_SetCreateRequestMessageFn(AQH_OBJECT *o, AQH_TOOLCLIENT_CREATEREQUESTMESSAGE_FN f); void AQH_ToolClient_SetHandleResponseMessageFn(AQH_OBJECT *o, AQH_TOOLCLIENT_HANDLERESPONSEMESSAGE_FN f); diff --git a/apps/aqhome-tool/data/client_p.h b/apps/aqhome-tool/data/client_p.h index cb5378d..c1cc813 100644 --- a/apps/aqhome-tool/data/client_p.h +++ b/apps/aqhome-tool/data/client_p.h @@ -26,7 +26,7 @@ struct AQH_TOOL_CLIENT { AQH_OBJECT *ipcEndpoint; int timeoutInSeconds; - + uint32_t flags; }; diff --git a/apps/aqhome-tool/data/getdatapoints.c b/apps/aqhome-tool/data/getdatapoints.c index ecca2c4..4d8c58f 100644 --- a/apps/aqhome-tool/data/getdatapoints.c +++ b/apps/aqhome-tool/data/getdatapoints.c @@ -1,6 +1,6 @@ /**************************************************************************** * This file is part of the project AqHome. - * AqHome (c) by 2023 Martin Preuss, all rights reserved. + * 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. @@ -10,334 +10,169 @@ # include #endif -#include "./getdatapoints.h" +#include "./getvalues.h" +#include "./client.h" #include "../utils.h" -#include "aqhome/aqhome.h" -#include "aqhome/msg/msg_node.h" -#include "aqhome/ipc/msg_ipc_result.h" -#include "aqhome/ipc/data/msg_data_multidata.h" -#include "aqhome/ipc/data/msg_data_getdata.h" -#include "aqhome/ipc/data/ipc_data.h" -#include "aqhome/ipc/msg_ipc_tag16.h" +#include "aqhome/msg/ipc/m_ipc.h" +#include "aqhome/msg/ipc/m_ipc_result.h" +#include "aqhome/msg/ipc/data/m_ipcd.h" +#include "aqhome/msg/ipc/data/m_ipcd_getdata.h" +#include "aqhome/msg/ipc/data/m_ipcd_multidata.h" #include #include #include #include -#include -#include #include + +/* ------------------------------------------------------------------------------------------------ + * defs + * ------------------------------------------------------------------------------------------------ + */ + #define I18S(msg) msg #define I18N(msg) GWEN_I18N_Translate(PACKAGE, msg) +#define A_ARG GWEN_ARGS_FLAGS_HAS_ARGUMENT +#define A_END (GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST) +#define A_CHAR GWEN_ArgsType_Char +#define A_INT GWEN_ArgsType_Int -static int _doGetDataPoints(GWEN_DB_NODE *dbArgs); -static int _awaitAndCalcAndPrintResponse(GWEN_MSG_ENDPOINT *epTcp, uint32_t msgId, int timeoutInSeconds, int printMean, int printDiff); -static void _handleDataResponse(GWEN_MSG *msg, int printMean, int printDiff); + + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static AQH_MESSAGE *_createRequestMessage(AQH_OBJECT *o, uint32_t msgId); +static int _handleResponseMessage(AQH_OBJECT *o, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList, int first); +static void _handleDataResponse(const GWEN_TAG16_LIST *tagList, int printMean, int printDiff); static uint64_t _getTimeStampFromString(const char *s); +/* ------------------------------------------------------------------------------------------------ + * code + * ------------------------------------------------------------------------------------------------ + */ int AQH_Tool_GetDataPoints(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv) { - GWEN_DB_NODE *dbLocalArgs; + AQH_EVENT_LOOP *eventLoop; + AQH_OBJECT *o; int rv; const GWEN_ARGS args[]= { - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "brokerAddress", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "t", /* short option */ - "tcpaddress", /* long option */ - I18S("Specify TCP address to connect to (defaults to 127.0.0.1)"), - I18S("Specify TCP address to connect to (defaults to 127.0.0.1)") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Int, /* type */ - "brokerPort", /* 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_Int, /* type */ - "timeout", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "T", /* short option */ - NULL, /* long option */ - I18S("Specify timeout in seconds for response"), - I18S("Specify timeout in seconds for response") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "valueName", /* name */ - 1, /* minnum */ - 1, /* maxnum */ - "N", /* short option */ - "valuename", /* long option */ - I18S("Name/path of the value to add (e.g. server/temp/system)"), - I18S("Name/path of the value to add (e.g. server/temp/system)") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "tsBegin", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "tb", /* short option */ - "tsbegin", /* long option */ - I18S("Get data from this timestamp on (earliest timestamp if omitted)"), - I18S("Get data from this timestamp on (earliest timestamp if omitted)") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "tsEnd", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "te", /* short option */ - "tsend", /* long option */ - I18S("Get data up until this timestamp (latest timestamp if omitted)"), - I18S("Get data up until this timestamp (latest timestamp if omitted)") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Int, /* type */ - "numOfLastDatapoints", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "n", /* short option */ - NULL, /* long option */ - I18S("Get last n datapoints"), - I18S("Get last n datapoints") - }, - { - 0, /* flags */ - GWEN_ArgsType_Int, /* type */ - "printMean", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "M", /* short option */ - "mean", /* long option */ - I18S("Print mean value of data received"), - I18S("Print mean value of data received") - }, - { - 0, /* flags */ - GWEN_ArgsType_Int, /* type */ - "printDiff", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "D", /* short option */ - "diff", /* long option */ - I18S("Print difference between last and first datapoint received"), - I18S("Print difference between last and first datapoint received") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "brokerClientId", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "c", /* short option */ - "clientid", /* long option */ - I18S("Specify CLIENTID"), - I18S("Specify CLIENTID") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "userId", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "u", /* short option */ - "userid", /* long option */ - I18S("Specify user id"), - I18S("Specify user id") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "password", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "p", /* short option */ - "password", /* long option */ - I18S("Specify service password"), - I18S("Specify service password") - }, - { - GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST, /* flags */ - GWEN_ArgsType_Int, /* type */ - "help", /* name */ - 0, /* minnum */ - 0, /* maxnum */ - "h", /* short option */ - "help", /* long option */ - "Show this help screen", /* short description */ - "Show this help screen" /* long description */ - } + /* flags type name min max s long short_descr, long_descr */ + { A_ARG, A_CHAR, "brokerAddress", 0, 1, "t", "tcpaddress", I18S("TCP address to connect to [127.0.0.1]"), NULL}, + { A_ARG, A_INT, "brokerPort", 0, 1, "P", "tcpport", I18S("Specify the TCP port to listen on"), NULL}, + { A_ARG, A_INT, "timeout", 0, 1, "T", NULL, I18S("Specify timeout in seconds for response"), NULL}, + { A_ARG, A_CHAR, "brokerClientId", 0, 1, "c", "clientid", I18S("Specify CLIENTID"), NULL}, + { A_ARG, A_CHAR, "userId", 0, 1, "u", "userid", I18S("Specify user id"), NULL}, + { A_ARG, A_CHAR, "password", 0, 1, "p", "password", I18S("Specify service password"), NULL}, + { A_ARG, A_CHAR, "valueName", 1, 1, "N", "valuename", I18S("Value name (e.g. server/temp/system)"), NULL}, + { A_ARG, A_CHAR, "tsBegin", 0, 1, "tb", "tsbegin", I18S("Timestamp range begin"), NULL}, + { A_ARG, A_CHAR, "tsEnd", 0, 1, "te", "tsend", I18S("Timestamp range end"), NULL}, + { A_ARG, A_INT, "numOfLastDatapoints", 0, 1, "n", NULL, I18S("Get last n datapoints"), NULL}, + { 0, A_INT, "printMean", 0, 1, "M", "mean", I18S("Print mean value of data received"), NULL}, + { 0, A_INT, "printDiff", 0, 1, "D", "diff", I18S("Print diff last-first value"), NULL}, + { A_END, A_INT, "help", 0, 0, "h", "help", I18S("Show this help screen"), NULL} }; - dbLocalArgs=GWEN_DB_GetGroup(dbGlobalArgs, GWEN_DB_FLAGS_DEFAULT, "local"); - rv=GWEN_Args_Check(argc, argv, 1, - GWEN_ARGS_MODE_ALLOW_FREEPARAM, - args, - dbLocalArgs); - if (rv==GWEN_ARGS_RESULT_ERROR) { - fprintf(stderr, "ERROR: Could not parse arguments\n"); - return 1; - } - else if (rv==GWEN_ARGS_RESULT_HELP) { - GWEN_BUFFER *ubuf; - - ubuf=GWEN_Buffer_new(0, 1024, 0, 1); - if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) { - fprintf(stderr, "ERROR: Could not create help string\n"); - return 1; - } - fprintf(stderr, "%s\n", GWEN_Buffer_GetStart(ubuf)); - GWEN_Buffer_free(ubuf); - return 0; - } - - AQH_MergeConfigFileIntoConfig(dbLocalArgs, "ConfigFile"); - - return _doGetDataPoints(dbLocalArgs); + eventLoop=AQH_EventLoop_new(); + o=AQH_ToolClient_new(eventLoop, dbGlobalArgs, args); + AQH_ToolClient_SetCreateRequestMessageFn(o, _createRequestMessage); + AQH_ToolClient_SetHandleResponseMessageFn(o, _handleResponseMessage); + rv=AQH_ToolClient_ReadLocalArgs(o, argc, argv); + if (rv!=0) + return rv; + rv=AQH_ToolClient_Run(o); + AQH_Object_free(o); + AQH_EventLoop_free(eventLoop); + return rv; } -int _doGetDataPoints(GWEN_DB_NODE *dbArgs) +AQH_MESSAGE *_createRequestMessage(GWEN_UNUSED AQH_OBJECT *o, uint32_t msgId) { - GWEN_MSG_ENDPOINT *epTcp; - int timeoutInSeconds; + GWEN_DB_NODE *dbArgs; const char *valueName; uint64_t tsBegin; uint64_t tsEnd; uint64_t num; - GWEN_MSG *msgOut; - int printMean; - int printDiff; - int rv; - uint32_t msgId; - printMean=GWEN_DB_GetIntValue(dbArgs, "printMean", 0, 0); - printDiff=GWEN_DB_GetIntValue(dbArgs, "printDiff", 0, 0); - timeoutInSeconds=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 5); + dbArgs=AQH_ToolClient_GetDbLocalArgs(o); valueName=GWEN_DB_GetCharValue(dbArgs, "valueName", 0, NULL); num=GWEN_DB_GetIntValue(dbArgs, "numOfLastDatapoints", 0, 0); tsBegin=_getTimeStampFromString(GWEN_DB_GetCharValue(dbArgs, "tsBegin", 0, NULL)); if (tsBegin==(uint64_t) (-1)) { DBG_ERROR(NULL, "Bad begin timestamp"); - return 2; + return NULL; } tsEnd=_getTimeStampFromString(GWEN_DB_GetCharValue(dbArgs, "tsEnd", 0, NULL)); if (tsEnd==(uint64_t) (-1)) { DBG_ERROR(NULL, "Bad end timestamp"); - return 2; + return NULL; } - epTcp=Utils_SetupBrokerClientEndpoint(dbArgs, 0); - if (epTcp==NULL) { - DBG_ERROR(NULL, "ERROR creating TCP connection"); - return 2; - } - - msgId=GWEN_MsgEndpoint_GetNextMessageId(epTcp); - msgOut=AQH_GetDataDataIpcMsg_new(AQH_MSGTYPE_IPC_DATA_GETDATA_REQ, msgId, 0, valueName, tsBegin, tsEnd, num); - GWEN_MsgEndpoint_AddSendMessage(epTcp, msgOut); - - rv=_awaitAndCalcAndPrintResponse(epTcp, msgId, timeoutInSeconds, printMean?1:0, printDiff?1:0); - if (rv!=0) { - GWEN_MsgEndpoint_free(epTcp); - return rv; - } - - GWEN_MsgEndpoint_free(epTcp); - return 0; + return AQH_IpcdMessageGetData_new(AQH_MSGTYPE_IPC_DATA_GETDATA_REQ, msgId, 0, valueName, tsBegin, tsEnd, num); } -int _awaitAndCalcAndPrintResponse(GWEN_MSG_ENDPOINT *epTcp, uint32_t msgId, int timeoutInSeconds, int printMean, int printDiff) +int _handleResponseMessage(AQH_OBJECT *o, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList, int first) { - for (;;) { - GWEN_MSG *msg; + GWEN_DB_NODE *dbArgs; + int printMean; + int printDiff; + uint16_t code; - msg=Utils_WaitForResponse(epTcp, msgId, timeoutInSeconds); - if (msg) { - uint16_t code; + dbArgs=AQH_ToolClient_GetDbLocalArgs(o); + printMean=GWEN_DB_GetIntValue(dbArgs, "printMean", 0, 0); + printDiff=GWEN_DB_GetIntValue(dbArgs, "printDiff", 0, 0); - code=GWEN_IpcMsg_GetCode(msg); - if (code==AQH_MSGTYPE_IPC_DATA_GETDATA_RSP) { - _handleDataResponse(msg, printMean, printDiff); - GWEN_Msg_free(msg); - return 0; - } - else if (code==AQH_MSGTYPE_IPC_DATA_RESULT) { - uint32_t resultCode; - - resultCode=AQH_ResultIpcMsg_GetResultCode(msg); - fprintf(stderr, "ERROR: %d\n", resultCode); - GWEN_Msg_free(msg); - return 3; - } - else { - DBG_INFO(NULL, "Unexpected message \"%d\"", code); - GWEN_Msg_free(msg); - return 3; - } - } - else { - DBG_ERROR(NULL, "No response received"); - return 2; - } - } /* for */ - return 0; + code=AQH_IpcMessage_GetCode(msg); + if (code==AQH_MSGTYPE_IPC_DATA_GETDATA_RSP) { + _handleDataResponse(tagList, printMean, printDiff); + return 1; + } + else if (code==AQH_MSGTYPE_IPC_DATA_RESULT) + return AQH_ToolClient_HandleResultMsg(msg, tagList, first); + else { + DBG_INFO(NULL, "Unexpected message \"%d\"", code); + return 3; + } } -void _handleDataResponse(GWEN_MSG *msg, int printMean, int printDiff) +void _handleDataResponse(const GWEN_TAG16_LIST *tagList, int printMean, int printDiff) { AQH_VALUE *value; - const GWEN_TAG16 *tag; - const char *valueUnits; - unsigned int numberOfPoints; - const uint64_t *dataPoints; - AQH_MultiDataDataIpcMsg_Parse(msg, 0); - value=AQH_MultiDataDataIpcMsg_ReadValue(msg); - valueUnits=value?AQH_Value_GetValueUnits(value):NULL; + value=AQH_IpcdMessageMultiData_ReadValue(tagList); + if (value) { + const char *valueUnits; + uint64_t numberOfPoints; + const uint64_t *dataPoints; - tag=AQH_Tag16IpcMsg_FindFirstTagByType(msg, AQH_MSGDATA_MULTIDATA_TAGS_DATA); - numberOfPoints=(tag?GWEN_Tag16_GetTagLength(tag):0)/(2*sizeof(uint64_t)); - dataPoints=tag?((const uint64_t*) GWEN_Tag16_GetTagData(tag)):NULL; - if (numberOfPoints>0 && dataPoints) { - if (printMean) - Utils_PrintMeanData(dataPoints, numberOfPoints, valueUnits); - else if (printDiff) - Utils_PrintDiffData(dataPoints, numberOfPoints, valueUnits); - else - Utils_PrintDataPoints(dataPoints, numberOfPoints, valueUnits); + valueUnits=AQH_Value_GetValueUnits(value); + AQH_IpcdMessageMultiData_ReadDatapoints(tagList, &dataPoints, &numberOfPoints); + if (numberOfPoints>0 && dataPoints) { + if (printMean) + Utils_PrintMeanData(dataPoints, numberOfPoints, valueUnits); + else if (printDiff) + Utils_PrintDiffData(dataPoints, numberOfPoints, valueUnits); + else + Utils_PrintDataPoints(dataPoints, numberOfPoints, valueUnits); + } + AQH_Value_free(value); } - - AQH_Value_free(value); } diff --git a/apps/aqhome-tool/data/getdevices.c b/apps/aqhome-tool/data/getdevices.c index 5455210..e3b9d49 100644 --- a/apps/aqhome-tool/data/getdevices.c +++ b/apps/aqhome-tool/data/getdevices.c @@ -1,6 +1,6 @@ /**************************************************************************** * This file is part of the project AqHome. - * AqHome (c) by 2023 Martin Preuss, all rights reserved. + * 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. @@ -10,250 +10,134 @@ # include #endif -#include "./getdevices.h" +#include "./getvalues.h" +#include "./client.h" #include "../utils.h" -#include "aqhome/aqhome.h" -#include "aqhome/msg/msg_node.h" -#include "aqhome/ipc/msg_ipc_result.h" -#include "aqhome/ipc/data/msg_data_devices.h" -#include "aqhome/ipc/data/ipc_data.h" - #include "aqhome/msg/ipc/m_ipc.h" -#include "aqhome/msg/ipc/m_ipc_tag16.h" #include "aqhome/msg/ipc/m_ipc_result.h" +#include "aqhome/msg/ipc/data/m_ipcd.h" #include "aqhome/msg/ipc/data/m_ipcd_devices.h" -#include "aqhome/ipc2/endpoint.h" #include #include #include #include -#include -#include +/* ------------------------------------------------------------------------------------------------ + * defs + * ------------------------------------------------------------------------------------------------ + */ + #define I18S(msg) msg #define I18N(msg) GWEN_I18N_Translate(PACKAGE, msg) - -static int _doGetDevices(GWEN_DB_NODE *dbArgs); +#define A_ARG GWEN_ARGS_FLAGS_HAS_ARGUMENT +#define A_END (GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST) +#define A_CHAR GWEN_ArgsType_Char +#define A_INT GWEN_ArgsType_Int +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static AQH_MESSAGE *_createRequestMessage(AQH_OBJECT *o, uint32_t msgId); +static int _handleResponseMessage(AQH_OBJECT *o, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList, int first); + + + +/* ------------------------------------------------------------------------------------------------ + * code + * ------------------------------------------------------------------------------------------------ + */ int AQH_Tool_GetDevices(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv) { - GWEN_DB_NODE *dbLocalArgs; + AQH_EVENT_LOOP *eventLoop; + AQH_OBJECT *o; int rv; const GWEN_ARGS args[]= { - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "brokerAddress", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "t", /* short option */ - "tcpaddress", /* long option */ - I18S("Specify TCP address to connect to (defaults to 127.0.0.1)"), - I18S("Specify TCP address to connect to (defaults to 127.0.0.1)") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Int, /* type */ - "brokerPort", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "P", /* short option */ - "tcpport", /* long option */ - I18S("Specify the TCP port to connect to"), - I18S("Specify the TCP port to connect to") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Int, /* type */ - "timeout", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "T", /* short option */ - NULL, /* long option */ - I18S("Specify timeout in seconds for response"), - I18S("Specify timeout in seconds for response") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "brokerClientId", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "c", /* short option */ - "clientid", /* long option */ - I18S("Specify CLIENTID"), - I18S("Specify CLIENTID") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "userId", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "u", /* short option */ - "userid", /* long option */ - I18S("Specify user id"), - I18S("Specify user id") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "password", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "p", /* short option */ - "password", /* long option */ - I18S("Specify service password"), - I18S("Specify service password") - }, - { - 0, /* flags */ - GWEN_ArgsType_Int, /* type */ - "printHeader", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "H", /* short option */ - "printheader", /* long option */ - I18S("Print header if given"), - I18S("Print header if given") - }, - { - GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST, /* flags */ - GWEN_ArgsType_Int, /* type */ - "help", /* name */ - 0, /* minnum */ - 0, /* maxnum */ - "h", /* short option */ - "help", /* long option */ - "Show this help screen", /* short description */ - "Show this help screen" /* long description */ - } + /* flags type name min max s long short_descr, long_descr */ + { A_ARG, A_CHAR, "brokerAddress", 0, 1, "t", "tcpaddress", I18S("TCP address to connect to [127.0.0.1]"), NULL}, + { A_ARG, A_INT, "brokerPort", 0, 1, "P", "tcpport", I18S("Specify the TCP port to listen on"), NULL}, + { A_ARG, A_INT, "timeout", 0, 1, "T", NULL, I18S("Specify timeout in seconds for response"), NULL}, + { A_ARG, A_CHAR, "brokerClientId", 0, 1, "c", "clientid", I18S("Specify CLIENTID"), NULL}, + { A_ARG, A_CHAR, "userId", 0, 1, "u", "userid", I18S("Specify user id"), NULL}, + { A_ARG, A_CHAR, "password", 0, 1, "p", "password", I18S("Specify service password"), NULL}, + { 0, A_INT, "printHeader", 0, 1, "H", "printheader", I18S("Print header if given"), NULL}, + { A_END, A_INT, "help", 0, 0, "h", "help", I18S("Show this help screen"), NULL} }; - dbLocalArgs=GWEN_DB_GetGroup(dbGlobalArgs, GWEN_DB_FLAGS_DEFAULT, "local"); - rv=GWEN_Args_Check(argc, argv, 1, - GWEN_ARGS_MODE_ALLOW_FREEPARAM, - args, - dbLocalArgs); - if (rv==GWEN_ARGS_RESULT_ERROR) { - fprintf(stderr, "ERROR: Could not parse arguments\n"); - return 1; - } - else if (rv==GWEN_ARGS_RESULT_HELP) { - GWEN_BUFFER *ubuf; - - ubuf=GWEN_Buffer_new(0, 1024, 0, 1); - if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) { - fprintf(stderr, "ERROR: Could not create help string\n"); - return 1; - } - fprintf(stderr, "%s\n", GWEN_Buffer_GetStart(ubuf)); - GWEN_Buffer_free(ubuf); - return 0; - } - - AQH_MergeConfigFileIntoConfig(dbLocalArgs, "ConfigFile"); - - return _doGetDevices(dbLocalArgs); + eventLoop=AQH_EventLoop_new(); + o=AQH_ToolClient_new(eventLoop, dbGlobalArgs, args); + AQH_ToolClient_SetCreateRequestMessageFn(o, _createRequestMessage); + AQH_ToolClient_SetHandleResponseMessageFn(o, _handleResponseMessage); + rv=AQH_ToolClient_ReadLocalArgs(o, argc, argv); + if (rv!=0) + return rv; + rv=AQH_ToolClient_Run(o); + AQH_Object_free(o); + AQH_EventLoop_free(eventLoop); + return rv; } -int _doGetDevices(GWEN_DB_NODE *dbArgs) +AQH_MESSAGE *_createRequestMessage(GWEN_UNUSED AQH_OBJECT *o, uint32_t msgId) { - AQH_EVENT_LOOP *eventLoop; - AQH_OBJECT *epTcp; - int timeoutInSeconds; - int printHeader; - AQH_MESSAGE *msgOut; - uint32_t msgId; - - timeoutInSeconds=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 5); - printHeader=GWEN_DB_GetIntValue(dbArgs, "printHeader", 0, 0); - - eventLoop=AQH_EventLoop_new(); - epTcp=Utils2_SetupBrokerClientEndpoint(eventLoop, dbArgs, 0); - if (epTcp==NULL) { - DBG_ERROR(NULL, "ERROR creating TCP connection"); - AQH_EventLoop_free(eventLoop); - return 2; - } - - /*fprintf(stdout, "Sending GetDevices request\n");*/ - msgId=AQH_Endpoint_GetNextMessageId(epTcp); - msgOut=AQH_IpcMessage_new(AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION, AQH_MSGTYPE_IPC_DATA_GETDEVICES_REQ, + return AQH_IpcMessage_new(AQH_IPC_PROTOCOL_DATA_ID, + AQH_IPC_PROTOCOL_DATA_VERSION, + AQH_MSGTYPE_IPC_DATA_GETDEVICES_REQ, msgId, 0, 0, NULL); - AQH_Endpoint_AddMsgOut(epTcp, msgOut); - - for (;;) { - AQH_MESSAGE *msgIn; - uint16_t code; - - msgIn=Utils2_WaitForResponseMsg(eventLoop, epTcp, msgId, timeoutInSeconds); - if (msgIn) { - GWEN_TAG16_LIST *tagList; - - code=AQH_IpcMessage_GetCode(msgIn); - tagList=AQH_IpcMessageTag16_ParsePayload(msgIn, 0); - if (tagList) { - if (code==AQH_MSGTYPE_IPC_DATA_GETDEVICES_RSP) { - AQH_DEVICE_LIST *deviceList; - - deviceList=AQH_IpcdMessageDevices_ReadDeviceList(tagList); - if (deviceList) { - AQH_DEVICE *device; - - device=AQH_Device_List_First(deviceList); - while(device) { - Utils_PrintDevice(device, printHeader); - printHeader=0; - device=AQH_Device_List_Next(device); - } - AQH_Device_List_free(deviceList); - } - - DBG_ERROR(NULL, "Flags: %08x", AQH_IpcdMessageDevices_GetFlags(tagList)); - if (AQH_IpcdMessageDevices_GetFlags(tagList) & AQH_MSGDATA_DEVICES_FLAGS_LASTMSG) { - DBG_ERROR(NULL, "Last message received"); - GWEN_Tag16_List_free(tagList); - break; - } - } - else if (code==AQH_MSGTYPE_IPC_DATA_RESULT) { - uint32_t resultCode; - - resultCode=AQH_IpcMessageResult_GetResult(tagList); - fprintf(stderr, "ERROR: %d\n", resultCode); - GWEN_Tag16_List_free(tagList); - AQH_Object_free(epTcp); - AQH_EventLoop_free(eventLoop); - return 3; - } - else { - DBG_INFO(NULL, "Unexpected message \"%d\"", code); - GWEN_Tag16_List_free(tagList); - AQH_Object_free(epTcp); - AQH_EventLoop_free(eventLoop); - return 3; - } - GWEN_Tag16_List_free(tagList); - } - } - } /* for */ - - AQH_Object_free(epTcp); - return 0; } +int _handleResponseMessage(AQH_OBJECT *o, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList, int first) +{ + uint16_t code; + GWEN_DB_NODE *dbArgs; + int printHeader; + + dbArgs=AQH_ToolClient_GetDbLocalArgs(o); + printHeader=first?GWEN_DB_GetIntValue(dbArgs, "printHeader", 0, 0):0; + + code=AQH_IpcMessage_GetCode(msg); + if (code==AQH_MSGTYPE_IPC_DATA_GETDEVICES_RSP) { + AQH_DEVICE_LIST *deviceList; + + deviceList=AQH_IpcdMessageDevices_ReadDeviceList(tagList); + if (deviceList) { + AQH_DEVICE *device; + + device=AQH_Device_List_First(deviceList); + while(device) { + Utils_PrintDevice(device, printHeader); + printHeader=0; + device=AQH_Device_List_Next(device); + } + AQH_Device_List_free(deviceList); + } + + if (AQH_IpcdMessageDevices_GetFlags(tagList) & AQH_MSGDATA_DEVICES_FLAGS_LASTMSG) { + DBG_INFO(NULL, "Last message received"); + return 1; + } + return 0; + } + else if (code==AQH_MSGTYPE_IPC_DATA_RESULT) + return AQH_ToolClient_HandleResultMsg(msg, tagList, first); + else { + DBG_INFO(NULL, "Unexpected message \"%d\"", code); + return 3; + } +} + + diff --git a/apps/aqhome-tool/data/getlastdatapoint.c b/apps/aqhome-tool/data/getlastdatapoint.c deleted file mode 100644 index 286c880..0000000 --- a/apps/aqhome-tool/data/getlastdatapoint.c +++ /dev/null @@ -1,291 +0,0 @@ -/**************************************************************************** - * 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 "./getlastdatapoint.h" -#include "../utils.h" - -#include "aqhome/aqhome.h" -#include "aqhome/msg/msg_node.h" -#include "aqhome/ipc/msg_ipc_result.h" -#include "aqhome/ipc/data/msg_data_getdata.h" -#include "aqhome/ipc/data/msg_data_multidata.h" -#include "aqhome/ipc/data/ipc_data.h" -#include "aqhome/ipc/msg_ipc_tag16.h" - -#include -#include -#include -#include - -#include -#include - - -#define I18S(msg) msg -#define I18N(msg) GWEN_I18N_Translate(PACKAGE, msg) - - -/* ------------------------------------------------------------------------------------------------ - * forward declarations - * ------------------------------------------------------------------------------------------------ - */ - -static int _doGetLastDataPoint(GWEN_DB_NODE *dbArgs); -static void _sendCommand(GWEN_MSG_ENDPOINT *epTcp, const char *valueName); -static int _awaitAndHandleResponse(GWEN_MSG_ENDPOINT *epTcp, int timeoutInSeconds); -static int _handleDataResponse(GWEN_MSG *msg); - - - -/* ------------------------------------------------------------------------------------------------ - * code - * ------------------------------------------------------------------------------------------------ - */ - -int AQH_Tool_GetLastDataPoint(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv) -{ - GWEN_DB_NODE *dbLocalArgs; - int rv; - const GWEN_ARGS args[]= { - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "brokerAddress", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "t", /* short option */ - "tcpaddress", /* long option */ - I18S("Specify TCP address to connect to (defaults to 127.0.0.1)"), - I18S("Specify TCP address to connect to (defaults to 127.0.0.1)") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Int, /* type */ - "brokerPort", /* 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_Int, /* type */ - "timeout", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "T", /* short option */ - NULL, /* long option */ - I18S("Specify timeout in seconds for response"), - I18S("Specify timeout in seconds for response") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "valueName", /* name */ - 1, /* minnum */ - 1, /* maxnum */ - "N", /* short option */ - "valuename", /* long option */ - I18S("Name/path of the value to add (e.g. server/temp/system)"), - I18S("Name/path of the value to add (e.g. server/temp/system)") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "brokerClientId", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "c", /* short option */ - "clientid", /* long option */ - I18S("Specify CLIENTID"), - I18S("Specify CLIENTID") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "userId", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "u", /* short option */ - "userid", /* long option */ - I18S("Specify user id"), - I18S("Specify user id") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "password", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "p", /* short option */ - "password", /* long option */ - I18S("Specify service password"), - I18S("Specify service password") - }, - { - GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST, /* flags */ - GWEN_ArgsType_Int, /* type */ - "help", /* name */ - 0, /* minnum */ - 0, /* maxnum */ - "h", /* short option */ - "help", /* long option */ - "Show this help screen", /* short description */ - "Show this help screen" /* long description */ - } - }; - - dbLocalArgs=GWEN_DB_GetGroup(dbGlobalArgs, GWEN_DB_FLAGS_DEFAULT, "local"); - rv=GWEN_Args_Check(argc, argv, 1, - GWEN_ARGS_MODE_ALLOW_FREEPARAM, - args, - dbLocalArgs); - if (rv==GWEN_ARGS_RESULT_ERROR) { - fprintf(stderr, "ERROR: Could not parse arguments\n"); - return 1; - } - else if (rv==GWEN_ARGS_RESULT_HELP) { - GWEN_BUFFER *ubuf; - - ubuf=GWEN_Buffer_new(0, 1024, 0, 1); - if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) { - fprintf(stderr, "ERROR: Could not create help string\n"); - return 1; - } - fprintf(stderr, "%s\n", GWEN_Buffer_GetStart(ubuf)); - GWEN_Buffer_free(ubuf); - return 0; - } - - AQH_MergeConfigFileIntoConfig(dbLocalArgs, "ConfigFile"); - - return _doGetLastDataPoint(dbLocalArgs); -} - - - -int _doGetLastDataPoint(GWEN_DB_NODE *dbArgs) -{ - GWEN_MSG_ENDPOINT *epTcp; - int timeoutInSeconds; - const char *valueName; - int rv; - - timeoutInSeconds=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 5); - valueName=GWEN_DB_GetCharValue(dbArgs, "valueName", 0, NULL); - - epTcp=Utils_SetupBrokerClientEndpoint(dbArgs, 0); - if (epTcp==NULL) { - DBG_ERROR(NULL, "ERROR creating TCP connection"); - return 2; - } - - /*fprintf(stdout, "Sending GetLastDataPoint request (%s)\n", valueName);*/ - - _sendCommand(epTcp, valueName); - - rv=_awaitAndHandleResponse(epTcp, timeoutInSeconds); - if (rv!=0) { - DBG_INFO(NULL, "here (%d)", rv); - GWEN_MsgEndpoint_free(epTcp); - return rv; - } - GWEN_MsgEndpoint_free(epTcp); - return 0; -} - - - -void _sendCommand(GWEN_MSG_ENDPOINT *epTcp, const char *valueName) -{ - GWEN_MSG *msgOut; - - msgOut=AQH_GetDataDataIpcMsg_new(AQH_MSGTYPE_IPC_DATA_GETLASTDATA_REQ, - GWEN_MsgEndpoint_GetNextMessageId(epTcp), 0, - valueName, 0, 0, 1); - GWEN_MsgEndpoint_AddSendMessage(epTcp, msgOut); -} - - - -int _awaitAndHandleResponse(GWEN_MSG_ENDPOINT *epTcp, int timeoutInSeconds) -{ - GWEN_MSG *msg; - uint16_t code; - - msg=Utils_WaitForSpecificIpcMessage(epTcp, AQH_MSGTYPE_IPC_DATA_GETLASTDATA_RSP, timeoutInSeconds); - if (msg) { - code=GWEN_IpcMsg_GetCode(msg); - if (code==AQH_MSGTYPE_IPC_DATA_GETLASTDATA_RSP) { - int rv; - - rv=_handleDataResponse(msg); - GWEN_Msg_free(msg); - return rv; - } - else if (code==AQH_MSGTYPE_IPC_DATA_RESULT) { - uint32_t resultCode; - - resultCode=AQH_ResultIpcMsg_GetResultCode(msg); - fprintf(stderr, "ERROR: %d\n", resultCode); - GWEN_Msg_free(msg); - return 3; - } - else { - DBG_INFO(NULL, "Unexpected message \"%d\"", code); - GWEN_Msg_free(msg); - return 3; - } - } - else { - DBG_ERROR(NULL, "No response received"); - return 2; - } -} - - - -int _handleDataResponse(GWEN_MSG *msg) -{ - AQH_VALUE *value; - const GWEN_TAG16 *tag; - const char *valueUnits; - unsigned int numberOfPoints; - const uint64_t *dataPoints; - - AQH_MultiDataDataIpcMsg_Parse(msg, 0); - value=AQH_MultiDataDataIpcMsg_ReadValue(msg); - valueUnits=value?AQH_Value_GetValueUnits(value):NULL; - - tag=AQH_Tag16IpcMsg_FindFirstTagByType(msg, AQH_MSGDATA_MULTIDATA_TAGS_DATA); - numberOfPoints=(tag?GWEN_Tag16_GetTagLength(tag):0)/(2*sizeof(uint64_t)); - dataPoints=tag?((const uint64_t*) GWEN_Tag16_GetTagData(tag)):NULL; - if (numberOfPoints>0 && dataPoints) { - uint64_t timestamp; - union {double f; uint64_t i;} u; - - timestamp=dataPoints[0]; - u.i=dataPoints[1]; - Utils_PrintSingleDataPoint(timestamp, u.f, valueUnits); - } - - AQH_Value_free(value); - return 0; -} - - - - - - diff --git a/apps/aqhome-tool/data/getvalues.c b/apps/aqhome-tool/data/getvalues.c index b1f1def..4fb6ae0 100644 --- a/apps/aqhome-tool/data/getvalues.c +++ b/apps/aqhome-tool/data/getvalues.c @@ -1,6 +1,6 @@ /**************************************************************************** * This file is part of the project AqHome. - * AqHome (c) by 2023 Martin Preuss, all rights reserved. + * 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. @@ -11,246 +11,131 @@ #endif #include "./getvalues.h" +#include "./client.h" #include "../utils.h" -#include "aqhome/aqhome.h" -#include "aqhome/msg/msg_node.h" -#include "aqhome/ipc/msg_ipc_result.h" -#include "aqhome/ipc/data/msg_data_values.h" -#include "aqhome/ipc/data/ipc_data.h" - #include "aqhome/msg/ipc/m_ipc.h" -#include "aqhome/msg/ipc/m_ipc_tag16.h" #include "aqhome/msg/ipc/m_ipc_result.h" +#include "aqhome/msg/ipc/data/m_ipcd.h" #include "aqhome/msg/ipc/data/m_ipcd_values.h" -#include "aqhome/ipc2/endpoint.h" #include #include #include #include -#include -#include +/* ------------------------------------------------------------------------------------------------ + * defs + * ------------------------------------------------------------------------------------------------ + */ + #define I18S(msg) msg #define I18N(msg) GWEN_I18N_Translate(PACKAGE, msg) +#define A_ARG GWEN_ARGS_FLAGS_HAS_ARGUMENT +#define A_END (GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST) +#define A_CHAR GWEN_ArgsType_Char +#define A_INT GWEN_ArgsType_Int -static int _doGetValues(GWEN_DB_NODE *dbArgs); + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static AQH_MESSAGE *_createRequestMessage(AQH_OBJECT *o, uint32_t msgId); +static int _handleResponseMessage(AQH_OBJECT *o, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList, int first); +/* ------------------------------------------------------------------------------------------------ + * code + * ------------------------------------------------------------------------------------------------ + */ int AQH_Tool_GetValues(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv) { - GWEN_DB_NODE *dbLocalArgs; + AQH_EVENT_LOOP *eventLoop; + AQH_OBJECT *o; int rv; const GWEN_ARGS args[]= { - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "brokerAddress", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "t", /* short option */ - "tcpaddress", /* long option */ - I18S("Specify TCP address to connect to (defaults to 127.0.0.1)"), - I18S("Specify TCP address to connect to (defaults to 127.0.0.1)") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Int, /* type */ - "brokerPort", /* 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_Int, /* type */ - "timeout", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "T", /* short option */ - NULL, /* long option */ - I18S("Specify timeout in seconds for response"), - I18S("Specify timeout in seconds for response") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "brokerClientId", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "c", /* short option */ - "clientid", /* long option */ - I18S("Specify CLIENTID"), - I18S("Specify CLIENTID") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "userId", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "u", /* short option */ - "userid", /* long option */ - I18S("Specify user id"), - I18S("Specify user id") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "password", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "p", /* short option */ - "password", /* long option */ - I18S("Specify service password"), - I18S("Specify service password") - }, - { - 0, /* flags */ - GWEN_ArgsType_Int, /* type */ - "printHeader", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "H", /* short option */ - "printheader", /* long option */ - I18S("Print header if given"), - I18S("Print header if given") - }, - { - GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST, /* flags */ - GWEN_ArgsType_Int, /* type */ - "help", /* name */ - 0, /* minnum */ - 0, /* maxnum */ - "h", /* short option */ - "help", /* long option */ - "Show this help screen", /* short description */ - "Show this help screen" /* long description */ - } + /* flags type name min max s long short_descr, long_descr */ + { A_ARG, A_CHAR, "brokerAddress", 0, 1, "t", "tcpaddress", I18S("TCP address to connect to [127.0.0.1]"), NULL}, + { A_ARG, A_INT, "brokerPort", 0, 1, "P", "tcpport", I18S("Specify the TCP port to listen on"), NULL}, + { A_ARG, A_INT, "timeout", 0, 1, "T", NULL, I18S("Specify timeout in seconds for response"), NULL}, + { A_ARG, A_CHAR, "brokerClientId", 0, 1, "c", "clientid", I18S("Specify CLIENTID"), NULL}, + { A_ARG, A_CHAR, "userId", 0, 1, "u", "userid", I18S("Specify user id"), NULL}, + { A_ARG, A_CHAR, "password", 0, 1, "p", "password", I18S("Specify service password"), NULL}, + { 0, A_INT, "printHeader", 0, 1, "H", "printheader", I18S("Print header if given"), NULL}, + { A_END, A_INT, "help", 0, 0, "h", "help", I18S("Show this help screen"), NULL} }; - dbLocalArgs=GWEN_DB_GetGroup(dbGlobalArgs, GWEN_DB_FLAGS_DEFAULT, "local"); - rv=GWEN_Args_Check(argc, argv, 1, - GWEN_ARGS_MODE_ALLOW_FREEPARAM, - args, - dbLocalArgs); - if (rv==GWEN_ARGS_RESULT_ERROR) { - fprintf(stderr, "ERROR: Could not parse arguments\n"); - return 1; - } - else if (rv==GWEN_ARGS_RESULT_HELP) { - GWEN_BUFFER *ubuf; - - ubuf=GWEN_Buffer_new(0, 1024, 0, 1); - if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) { - fprintf(stderr, "ERROR: Could not create help string\n"); - return 1; - } - fprintf(stderr, "%s\n", GWEN_Buffer_GetStart(ubuf)); - GWEN_Buffer_free(ubuf); - return 0; - } - - AQH_MergeConfigFileIntoConfig(dbLocalArgs, "ConfigFile"); - - return _doGetValues(dbLocalArgs); + eventLoop=AQH_EventLoop_new(); + o=AQH_ToolClient_new(eventLoop, dbGlobalArgs, args); + AQH_ToolClient_SetCreateRequestMessageFn(o, _createRequestMessage); + AQH_ToolClient_SetHandleResponseMessageFn(o, _handleResponseMessage); + rv=AQH_ToolClient_ReadLocalArgs(o, argc, argv); + if (rv!=0) + return rv; + rv=AQH_ToolClient_Run(o); + AQH_Object_free(o); + AQH_EventLoop_free(eventLoop); + return rv; } -int _doGetValues(GWEN_DB_NODE *dbArgs) +AQH_MESSAGE *_createRequestMessage(GWEN_UNUSED AQH_OBJECT *o, uint32_t msgId) { - AQH_EVENT_LOOP *eventLoop; - AQH_OBJECT *epTcp; - int timeoutInSeconds; - int printHeader; - AQH_MESSAGE *msgOut; - uint32_t msgId; - - timeoutInSeconds=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 5); - printHeader=GWEN_DB_GetIntValue(dbArgs, "printHeader", 0, 0); - - eventLoop=AQH_EventLoop_new(); - epTcp=Utils2_SetupBrokerClientEndpoint(eventLoop, dbArgs, 0); - if (epTcp==NULL) { - DBG_ERROR(NULL, "ERROR creating TCP connection"); - AQH_EventLoop_free(eventLoop); - return 2; - } - - msgId=AQH_Endpoint_GetNextMessageId(epTcp); - msgOut=AQH_IpcMessage_new(AQH_IPC_PROTOCOL_DATA_ID, AQH_IPC_PROTOCOL_DATA_VERSION, AQH_MSGTYPE_IPC_DATA_GETVALUES_REQ, + return AQH_IpcMessage_new(AQH_IPC_PROTOCOL_DATA_ID, + AQH_IPC_PROTOCOL_DATA_VERSION, + AQH_MSGTYPE_IPC_DATA_GETVALUES_REQ, msgId, 0, 0, NULL); - AQH_Endpoint_AddMsgOut(epTcp, msgOut); - - for (;;) { - AQH_MESSAGE *msgIn; - uint16_t code; - - msgIn=Utils2_WaitForResponseMsg(eventLoop, epTcp, msgId, timeoutInSeconds); - if (msgIn) { - GWEN_TAG16_LIST *tagList; - - code=AQH_IpcMessage_GetCode(msgIn); - tagList=AQH_IpcMessageTag16_ParsePayload(msgIn, 0); - if (tagList) { - if (code==AQH_MSGTYPE_IPC_DATA_GETVALUES_RSP) { - AQH_VALUE_LIST *valueList; - - valueList=AQH_IpcdMessageValues_ReadValueList(tagList); - if (valueList) { - AQH_VALUE *value; - - value=AQH_Value_List_First(valueList); - while(value) { - Utils_PrintValue(value, printHeader); - printHeader=0; - value=AQH_Value_List_Next(value); - } - AQH_Value_List_free(valueList); - } - - if (AQH_IpcdMessageValues_GetFlags(tagList) & AQH_MSGDATA_VALUES_FLAGS_LASTMSG) { - DBG_INFO(NULL, "Last message received"); - GWEN_Tag16_List_free(tagList); - break; - } - } - else if (code==AQH_MSGTYPE_IPC_DATA_RESULT) { - uint32_t resultCode; - - resultCode=AQH_IpcMessageResult_GetResult(tagList); - fprintf(stderr, "ERROR: %d\n", resultCode); - GWEN_Tag16_List_free(tagList); - AQH_Object_free(epTcp); - AQH_EventLoop_free(eventLoop); - return 3; - } - else { - DBG_INFO(NULL, "Unexpected message \"%d\"", code); - GWEN_Tag16_List_free(tagList); - AQH_Object_free(epTcp); - AQH_EventLoop_free(eventLoop); - return 3; - } - GWEN_Tag16_List_free(tagList); - } - } - } /* for */ - - AQH_Object_free(epTcp); - return 0; } +int _handleResponseMessage(AQH_OBJECT *o, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList, int first) +{ + uint16_t code; + GWEN_DB_NODE *dbArgs; + int printHeader; + + dbArgs=AQH_ToolClient_GetDbLocalArgs(o); + printHeader=first?GWEN_DB_GetIntValue(dbArgs, "printHeader", 0, 0):0; + + code=AQH_IpcMessage_GetCode(msg); + if (code==AQH_MSGTYPE_IPC_DATA_GETVALUES_RSP) { + AQH_VALUE_LIST *valueList; + + valueList=AQH_IpcdMessageValues_ReadValueList(tagList); + if (valueList) { + AQH_VALUE *value; + + value=AQH_Value_List_First(valueList); + while(value) { + Utils_PrintValue(value, printHeader); + printHeader=0; + value=AQH_Value_List_Next(value); + } + AQH_Value_List_free(valueList); + } + + if (AQH_IpcdMessageValues_GetFlags(tagList) & AQH_MSGDATA_VALUES_FLAGS_LASTMSG) { + DBG_INFO(NULL, "Last message received"); + return 1; + } + return 0; + } + else if (code==AQH_MSGTYPE_IPC_DATA_RESULT) + return AQH_ToolClient_HandleResultMsg(msg, tagList, first); + else { + DBG_INFO(NULL, "Unexpected message \"%d\"", code); + return 3; + } +} + + diff --git a/apps/aqhome-tool/data/moddevice.c b/apps/aqhome-tool/data/moddevice.c index 7133c35..d3baa15 100644 --- a/apps/aqhome-tool/data/moddevice.c +++ b/apps/aqhome-tool/data/moddevice.c @@ -1,6 +1,6 @@ /**************************************************************************** * This file is part of the project AqHome. - * AqHome (c) by 2023 Martin Preuss, all rights reserved. + * 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. @@ -11,266 +11,93 @@ #endif #include "./moddevice.h" +#include "./client.h" #include "../utils.h" -#include "aqhome/aqhome.h" -#include "aqhome/msg/msg_node.h" -#include "aqhome/ipc/msg_ipc_result.h" -#include "aqhome/ipc/data/msg_data_devices.h" -#include "aqhome/ipc/data/ipc_data.h" +#include "aqhome/msg/ipc/m_ipc.h" +#include "aqhome/msg/ipc/m_ipc_result.h" +#include "aqhome/msg/ipc/data/m_ipcd.h" +#include "aqhome/msg/ipc/data/m_ipcd_devices.h" #include #include #include #include -#include -#include +/* ------------------------------------------------------------------------------------------------ + * defs + * ------------------------------------------------------------------------------------------------ + */ #define I18S(msg) msg #define I18N(msg) GWEN_I18N_Translate(PACKAGE, msg) +#define A_ARG GWEN_ARGS_FLAGS_HAS_ARGUMENT +#define A_END (GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST) +#define A_CHAR GWEN_ArgsType_Char +#define A_INT GWEN_ArgsType_Int -static int _doModDevice(GWEN_DB_NODE *dbArgs); -static void _sendCommand(GWEN_MSG_ENDPOINT *epTcp, const AQH_DEVICE *device); + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static AQH_MESSAGE *_createRequestMessage(AQH_OBJECT *o, uint32_t msgId); +/* ------------------------------------------------------------------------------------------------ + * implementations + * ------------------------------------------------------------------------------------------------ + */ int AQH_Tool_ModDevice(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv) { - GWEN_DB_NODE *dbLocalArgs; + AQH_EVENT_LOOP *eventLoop; + AQH_OBJECT *o; int rv; const GWEN_ARGS args[]= { - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "brokerAddress", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "t", /* short option */ - "tcpaddress", /* long option */ - I18S("Specify TCP address to connect to (defaults to 127.0.0.1)"), - I18S("Specify TCP address to connect to (defaults to 127.0.0.1)") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Int, /* type */ - "brokerPort", /* 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_Int, /* type */ - "timeout", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "T", /* short option */ - NULL, /* long option */ - I18S("Specify timeout in seconds for response"), - I18S("Specify timeout in seconds for response") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "brokerClientId", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "c", /* short option */ - "clientid", /* long option */ - I18S("Specify CLIENTID"), - I18S("Specify CLIENTID") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "userId", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "u", /* short option */ - "userid", /* long option */ - I18S("Specify user id"), - I18S("Specify user id") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "password", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "p", /* short option */ - "password", /* long option */ - I18S("Specify service password"), - I18S("Specify service password") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "device", /* name */ - 1, /* minnum */ - 1, /* maxnum */ - "d", /* short option */ - "device", /* long option */ - I18S("Specify name of the device to modify"), - I18S("Specify name of the device to modify") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "roomName", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "R", /* short option */ - "room", /* long option */ - I18S("Specify room for a device"), - I18S("Specify room for a device") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "nameForGui", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "G", /* short option */ - "guiname", /* long option */ - I18S("Specify GUI name of the device"), - I18S("Specify GUI name of the device") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "location", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "L", /* short option */ - "location", /* long option */ - I18S("Specify location of the device"), - I18S("Specify location of the device") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "description", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - NULL, /* short option */ - "description", /* long option */ - I18S("Specify description for the device"), - I18S("Specify description for the device") - }, - { - GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST, /* flags */ - GWEN_ArgsType_Int, /* type */ - "help", /* name */ - 0, /* minnum */ - 0, /* maxnum */ - "h", /* short option */ - "help", /* long option */ - "Show this help screen", /* short description */ - "Show this help screen" /* long description */ - } + /* flags type name min max s long short_descr, long_descr */ + { A_ARG, A_CHAR, "brokerAddress", 0, 1, "t", "tcpaddress", I18S("TCP address to connect to [127.0.0.1]"), NULL}, + { A_ARG, A_INT, "brokerPort", 0, 1, "P", "tcpport", I18S("Specify the TCP port to listen on"), NULL}, + { A_ARG, A_INT, "timeout", 0, 1, "T", NULL, I18S("Specify timeout in seconds for response"), NULL}, + { A_ARG, A_CHAR, "brokerClientId", 0, 1, "c", "clientid", I18S("Specify CLIENTID"), NULL}, + { A_ARG, A_CHAR, "userId", 0, 1, "u", "userid", I18S("Specify user id"), NULL}, + { A_ARG, A_CHAR, "password", 0, 1, "p", "password", I18S("Specify service password"), NULL}, + { A_ARG, A_CHAR, "device", 1, 1, "d", "device", I18S("device name"), NULL}, + { A_ARG, A_CHAR, "roomName", 0, 1, "R", "room", I18S("Specify room for a device"), NULL}, + { A_ARG, A_CHAR, "nameForGui", 0, 1, "G", "guiname", I18S("Specify GUI name of the device"), NULL}, + { A_ARG, A_CHAR, "location", 0, 1, "L", "location", I18S("Specify location of the device"), NULL}, + { A_ARG, A_CHAR, "description", 0, 1, NULL, "description", I18S("Specify description for the device"), NULL}, + { A_END, A_INT, "help", 0, 0, "h", "help", I18S("Show this help screen"), NULL} }; - dbLocalArgs=GWEN_DB_GetGroup(dbGlobalArgs, GWEN_DB_FLAGS_DEFAULT, "local"); - rv=GWEN_Args_Check(argc, argv, 1, - GWEN_ARGS_MODE_ALLOW_FREEPARAM, - args, - dbLocalArgs); - if (rv==GWEN_ARGS_RESULT_ERROR) { - fprintf(stderr, "ERROR: Could not parse arguments\n"); - return 1; - } - else if (rv==GWEN_ARGS_RESULT_HELP) { - GWEN_BUFFER *ubuf; - - ubuf=GWEN_Buffer_new(0, 1024, 0, 1); - if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) { - fprintf(stderr, "ERROR: Could not create help string\n"); - return 1; - } - fprintf(stderr, "%s\n", GWEN_Buffer_GetStart(ubuf)); - GWEN_Buffer_free(ubuf); - return 0; - } - - AQH_MergeConfigFileIntoConfig(dbLocalArgs, "ConfigFile"); - - return _doModDevice(dbLocalArgs); + eventLoop=AQH_EventLoop_new(); + o=AQH_ToolClient_new(eventLoop, dbGlobalArgs, args); + AQH_ToolClient_SetCreateRequestMessageFn(o, _createRequestMessage); + rv=AQH_ToolClient_ReadLocalArgs(o, argc, argv); + if (rv!=0) + return rv; + rv=AQH_ToolClient_Run(o); + AQH_Object_free(o); + AQH_EventLoop_free(eventLoop); + return rv; } -int _doModDevice(GWEN_DB_NODE *dbArgs) +AQH_MESSAGE *_createRequestMessage(AQH_OBJECT *o, uint32_t msgId) { - GWEN_MSG_ENDPOINT *epTcp; - int timeoutInSeconds; - GWEN_MSG *msg; + AQH_MESSAGE *msg; + GWEN_DB_NODE *dbArgs; AQH_DEVICE *device; - timeoutInSeconds=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 5); + dbArgs=AQH_ToolClient_GetDbLocalArgs(o); device=Utils_DeviceFromArgs(dbArgs); - - epTcp=Utils_SetupBrokerClientEndpoint(dbArgs, 0); - if (epTcp==NULL) { - DBG_ERROR(NULL, "ERROR creating TCP connection"); - return 2; - } - - /*fprintf(stdout, "Sending GetValues request\n");*/ - - _sendCommand(epTcp, device); - - for (;;) { - uint16_t code; - - msg=Utils_WaitForSpecificIpcMessage(epTcp, AQH_MSGTYPE_IPC_DATA_RESULT, timeoutInSeconds); - if (msg==NULL) { - DBG_ERROR(NULL, "No response received"); - return 2; - } - code=GWEN_IpcMsg_GetCode(msg); - if (code==AQH_MSGTYPE_IPC_DATA_RESULT) { - uint32_t resultCode; - - resultCode=AQH_ResultIpcMsg_GetResultCode(msg); - if (resultCode!=AQH_MSG_IPC_SUCCESS) { - fprintf(stderr, "ERROR: %d\n", resultCode); - GWEN_MsgEndpoint_free(epTcp); - return 3; - } - break; - } - else { - DBG_INFO(NULL, "Unexpected message \"%d\"", code); - GWEN_MsgEndpoint_free(epTcp); - return 3; - } - } /* for */ - - GWEN_MsgEndpoint_free(epTcp); - return 0; + msg=AQH_IpcdMessageDevices_newForOneDevice(AQH_MSGTYPE_IPC_DATA_MODDEVICE_REQ, msgId, 0, AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, device); + return msg; } - -void _sendCommand(GWEN_MSG_ENDPOINT *epTcp, const AQH_DEVICE *device) -{ - GWEN_MSG *msgOut; - - msgOut=AQH_DevicesDataIpcMsg_newForOneDevice(AQH_MSGTYPE_IPC_DATA_MODDEVICE_REQ, - GWEN_MsgEndpoint_GetNextMessageId(epTcp), 0, - AQH_MSGDATA_DEVICES_FLAGS_LASTMSG, device); - GWEN_MsgEndpoint_AddSendMessage(epTcp, msgOut); -} - - - - - - diff --git a/apps/aqhome-tool/data/setdata.c b/apps/aqhome-tool/data/setdata.c index d25cc35..8047cce 100644 --- a/apps/aqhome-tool/data/setdata.c +++ b/apps/aqhome-tool/data/setdata.c @@ -1,6 +1,6 @@ /**************************************************************************** * This file is part of the project AqHome. - * AqHome (c) by 2023 Martin Preuss, all rights reserved. + * 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. @@ -11,35 +11,40 @@ #endif #include "./setdata.h" +#include "./client.h" #include "../utils.h" -#include "aqhome/aqhome.h" -#include "aqhome/msg/msg_node.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/ipc/m_ipc.h" +#include "aqhome/msg/ipc/m_ipc_result.h" +#include "aqhome/msg/ipc/data/m_ipcd.h" +#include "aqhome/msg/ipc/data/m_ipcd_setdata.h" #include #include #include #include -#include -#include +/* ------------------------------------------------------------------------------------------------ + * defs + * ------------------------------------------------------------------------------------------------ + */ #define I18S(msg) msg #define I18N(msg) GWEN_I18N_Translate(PACKAGE, msg) +#define A_ARG GWEN_ARGS_FLAGS_HAS_ARGUMENT +#define A_END (GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST) +#define A_CHAR GWEN_ArgsType_Char +#define A_INT GWEN_ArgsType_Int + /* ------------------------------------------------------------------------------------------------ * forward declarations * ------------------------------------------------------------------------------------------------ */ -static int _doSetData(GWEN_DB_NODE *dbArgs); -static uint32_t _sendRequest(GWEN_MSG_ENDPOINT *epTcp, const char *valueName, const char *valueUnits, const char *valueData); -static int _awaitResponse(GWEN_MSG_ENDPOINT *epTcp, uint32_t msgId, int timeoutInSeconds); +static AQH_MESSAGE *_createRequestMessage(AQH_OBJECT *o, uint32_t msgId); @@ -50,249 +55,63 @@ static int _awaitResponse(GWEN_MSG_ENDPOINT *epTcp, uint32_t msgId, int timeoutI int AQH_Tool_SetData(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv) { - GWEN_DB_NODE *dbLocalArgs; + AQH_EVENT_LOOP *eventLoop; + AQH_OBJECT *o; int rv; const GWEN_ARGS args[]= { - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "brokerAddress", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "t", /* short option */ - "tcpaddress", /* long option */ - I18S("Specify TCP address to connect to (defaults to 127.0.0.1)"), - I18S("Specify TCP address to connect to (defaults to 127.0.0.1)") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Int, /* type */ - "brokerPort", /* 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_Int, /* type */ - "timeout", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "T", /* short option */ - NULL, /* long option */ - I18S("Specify timeout in seconds for response"), - I18S("Specify timeout in seconds for response") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "valueName", /* name */ - 1, /* minnum */ - 1, /* maxnum */ - "N", /* short option */ - "valuename", /* long option */ - I18S("Name/path of the value to set (e.g. server/temp/system)"), - I18S("Name/path of the value to set (e.g. server/temp/system)") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "valueUnits", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "U", /* short option */ - "valueunits", /* long option */ - I18S("Units of the value to set (e.g. \"Grad Celsius\")"), - I18S("Units of the value to set (e.g. \"Grad Celsius\")") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "value", /* name */ - 1, /* minnum */ - 1, /* maxnum */ - "v", /* short option */ - "value", /* long option */ - I18S("Value to set"), - I18S("Value to set") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "brokerClientId", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "c", /* short option */ - "clientid", /* long option */ - I18S("Specify CLIENTID"), - I18S("Specify CLIENTID") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "userId", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "u", /* short option */ - "userid", /* long option */ - I18S("Specify user id"), - I18S("Specify user id") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "password", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "p", /* short option */ - "password", /* long option */ - I18S("Specify service password"), - I18S("Specify service password") - }, - { - GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST, /* flags */ - GWEN_ArgsType_Int, /* type */ - "help", /* name */ - 0, /* minnum */ - 0, /* maxnum */ - "h", /* short option */ - "help", /* long option */ - "Show this help screen", /* short description */ - "Show this help screen" /* long description */ - } + /* flags type name min max s long short_descr, long_descr */ + { A_ARG, A_CHAR, "brokerAddress", 0, 1, "t", "tcpaddress", I18S("TCP address to connect to [127.0.0.1]"), NULL}, + { A_ARG, A_INT, "brokerPort", 0, 1, "P", "tcpport", I18S("Specify the TCP port to listen on"), NULL}, + { A_ARG, A_INT, "timeout", 0, 1, "T", NULL, I18S("Specify timeout in seconds for response"), NULL}, + { A_ARG, A_CHAR, "brokerClientId", 0, 1, "c", "clientid", I18S("Specify CLIENTID"), NULL}, + { A_ARG, A_CHAR, "userId", 0, 1, "u", "userid", I18S("Specify user id"), NULL}, + { A_ARG, A_CHAR, "password", 0, 1, "p", "password", I18S("Specify service password"), NULL}, + { A_ARG, A_CHAR, "valueName", 1, 1, "N", "valuename", I18S("Value name (e.g. server/temp/system)"), NULL}, + { A_ARG, A_CHAR, "valueUnits", 0, 1, "U", "valueunits", I18S("Value units (e.g. \"Grad Celsius\")"), NULL}, + { A_ARG, A_CHAR, "value", 1, 1, "v", "value", I18S("Value to set"), NULL}, + { A_END, A_INT, "help", 0, 0, "h", "help", I18S("Show this help screen"), NULL} }; - dbLocalArgs=GWEN_DB_GetGroup(dbGlobalArgs, GWEN_DB_FLAGS_DEFAULT, "local"); - rv=GWEN_Args_Check(argc, argv, 1, - GWEN_ARGS_MODE_ALLOW_FREEPARAM, - args, - dbLocalArgs); - if (rv==GWEN_ARGS_RESULT_ERROR) { - fprintf(stderr, "ERROR: Could not parse arguments\n"); - return 1; - } - else if (rv==GWEN_ARGS_RESULT_HELP) { - GWEN_BUFFER *ubuf; - - ubuf=GWEN_Buffer_new(0, 1024, 0, 1); - if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) { - fprintf(stderr, "ERROR: Could not create help string\n"); - return 1; - } - fprintf(stderr, "%s\n", GWEN_Buffer_GetStart(ubuf)); - GWEN_Buffer_free(ubuf); - return 0; - } - - AQH_MergeConfigFileIntoConfig(dbLocalArgs, "ConfigFile"); - - return _doSetData(dbLocalArgs); -} - - - -int _doSetData(GWEN_DB_NODE *dbArgs) -{ - GWEN_MSG_ENDPOINT *epTcp; - int timeoutInSeconds; - const char *valueName; - const char *valueUnits; - const char *valueData; - uint32_t msgId; - int rv; - - timeoutInSeconds=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 5); - valueName=GWEN_DB_GetCharValue(dbArgs, "valueName", 0, NULL); - valueUnits=GWEN_DB_GetCharValue(dbArgs, "valueUnits", 0, NULL); - valueData=GWEN_DB_GetCharValue(dbArgs, "value", 0, NULL); - if (!(valueName && *valueName)) { - DBG_ERROR(NULL, "ERROR: Missing value name"); - return 1; - } - - if (!(valueData && *valueData)) { - DBG_ERROR(NULL, "ERROR: Missing data"); - return 1; - } - - epTcp=Utils_SetupBrokerClientEndpoint(dbArgs, 0); - if (epTcp==NULL) { - DBG_ERROR(NULL, "ERROR creating TCP connection"); - return 2; - } - - msgId=_sendRequest(epTcp, valueName, valueUnits, valueData); - rv=_awaitResponse(epTcp, msgId, timeoutInSeconds); - if (rv!=0) { - DBG_ERROR(NULL, "here (%d)", rv); - } - GWEN_MsgEndpoint_free(epTcp); + eventLoop=AQH_EventLoop_new(); + o=AQH_ToolClient_new(eventLoop, dbGlobalArgs, args); + AQH_ToolClient_SetCreateRequestMessageFn(o, _createRequestMessage); + rv=AQH_ToolClient_ReadLocalArgs(o, argc, argv); + if (rv!=0) + return rv; + rv=AQH_ToolClient_Run(o); + AQH_Object_free(o); + AQH_EventLoop_free(eventLoop); return rv; } -uint32_t _sendRequest(GWEN_MSG_ENDPOINT *epTcp, const char *valueName, const char *valueUnits, const char *valueData) +AQH_MESSAGE *_createRequestMessage(GWEN_UNUSED AQH_OBJECT *o, uint32_t msgId) { - GWEN_MSG *msgOut; + AQH_MESSAGE *msg; + GWEN_DB_NODE *dbArgs; + const char *valueName; + const char *valueUnits; + const char *valueData; AQH_VALUE *v; - uint32_t msgId; + + dbArgs=AQH_ToolClient_GetDbLocalArgs(o); + valueName=GWEN_DB_GetCharValue(dbArgs, "valueName", 0, NULL); + valueUnits=GWEN_DB_GetCharValue(dbArgs, "valueUnits", 0, NULL); + valueData=GWEN_DB_GetCharValue(dbArgs, "value", 0, NULL); + if (!(valueName && *valueName)) { + DBG_ERROR(NULL, "ERROR: Missing value name"); + return NULL; + } v=AQH_Value_new(); AQH_Value_SetNameForSystem(v, valueName); AQH_Value_SetValueUnits(v, valueUnits); - msgId=GWEN_MsgEndpoint_GetNextMessageId(epTcp); - msgOut=AQH_SetDataIpcMsg_new(AQH_MSGTYPE_IPC_DATA_SETDATA, msgId, 0, v, valueData); + msg=AQH_IpcdMessageSetData_new(AQH_MSGTYPE_IPC_DATA_SETDATA, msgId, 0, v, valueData); AQH_Value_free(v); - GWEN_MsgEndpoint_AddSendMessage(epTcp, msgOut); - return msgId; + return msg; } -int _awaitResponse(GWEN_MSG_ENDPOINT *epTcp, uint32_t msgId, int timeoutInSeconds) -{ - GWEN_MSG *msgResponse; - - msgResponse=Utils_WaitForResponse(epTcp, msgId, timeoutInSeconds); - if (msgResponse) { - uint16_t code; - - code=GWEN_IpcMsg_GetCode(msgResponse); - if (code==AQH_MSGTYPE_IPC_DATA_RESULT) { - uint32_t resultCode; - - resultCode=AQH_ResultIpcMsg_GetResultCode(msgResponse); - if (resultCode!=AQH_MSG_IPC_SUCCESS) { - fprintf(stderr, "ERROR: %d\n", resultCode); - GWEN_Msg_free(msgResponse); - return 3; - } - else { - fprintf(stdout, "Value set.\n"); - GWEN_Msg_free(msgResponse); - return 0; - } - } - else { - DBG_INFO(NULL, "Unexpected message \"%d\"", code); - GWEN_Msg_free(msgResponse); - return 3; - } - } - else { - DBG_ERROR(NULL, "Timeout"); - return 3; - } - - return 0; -} - - - - - diff --git a/apps/aqhome-tool/data/watch.c b/apps/aqhome-tool/data/watch.c index 3670c8e..45d1b08 100644 --- a/apps/aqhome-tool/data/watch.c +++ b/apps/aqhome-tool/data/watch.c @@ -1,6 +1,6 @@ /**************************************************************************** * This file is part of the project AqHome. - * AqHome (c) by 2023 Martin Preuss, all rights reserved. + * 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. @@ -10,254 +10,131 @@ # include #endif -#include "./getdatapoints.h" +#include "./watch.h" +#include "./client.h" #include "../utils.h" -#include "aqhome/aqhome.h" -#include "aqhome/msg/msg_node.h" -#include "aqhome/ipc/msg_ipc_result.h" -#include "aqhome/ipc/data/msg_data_multidata.h" -#include "aqhome/ipc/data/ipc_data.h" -#include "aqhome/ipc/endpoint_ipc.h" -#include "aqhome/ipc/msg_ipc_tag16.h" +#include "aqhome/ipc2/endpoint.h" +#include "aqhome/msg/ipc/m_ipc.h" +#include "aqhome/msg/ipc/m_ipc_result.h" +#include "aqhome/msg/ipc/data/m_ipcd.h" +#include "aqhome/msg/ipc/data/m_ipcd_multidata.h" #include #include #include #include -#include -#include +/* ------------------------------------------------------------------------------------------------ + * defs + * ------------------------------------------------------------------------------------------------ + */ + #define I18S(msg) msg #define I18N(msg) GWEN_I18N_Translate(PACKAGE, msg) +#define A_ARG GWEN_ARGS_FLAGS_HAS_ARGUMENT +#define A_END (GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST) +#define A_CHAR GWEN_ArgsType_Char +#define A_INT GWEN_ArgsType_Int -static int _doWatch(GWEN_DB_NODE *dbArgs); -static void _awaitAndPrintChanges(GWEN_MSG_ENDPOINT *epTcp, int timeoutInSeconds, const char *tmpl); -static void _handleDataResponse(GWEN_MSG *msg, const char *tmpl); + +/* ------------------------------------------------------------------------------------------------ + * forward declarations + * ------------------------------------------------------------------------------------------------ + */ + +static int _handleResponseMessage(AQH_OBJECT *o, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList, int first); +static void _handleDataResponse(const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList, const char *tmpl); +/* ------------------------------------------------------------------------------------------------ + * code + * ------------------------------------------------------------------------------------------------ + */ + int AQH_Tool_Watch(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv) { - GWEN_DB_NODE *dbLocalArgs; + AQH_EVENT_LOOP *eventLoop; + AQH_OBJECT *o; int rv; const GWEN_ARGS args[]= { - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "brokerAddress", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "t", /* short option */ - "tcpaddress", /* long option */ - I18S("Specify TCP address to connect to (defaults to 127.0.0.1)"), - I18S("Specify TCP address to connect to (defaults to 127.0.0.1)") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Int, /* type */ - "brokerPort", /* 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_Int, /* type */ - "timeout", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "T", /* short option */ - NULL, /* long option */ - I18S("Specify timeout in seconds for response"), - I18S("Specify timeout in seconds for response") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "brokerClientId", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "c", /* short option */ - "clientid", /* long option */ - I18S("Specify CLIENTID"), - I18S("Specify CLIENTID") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "userId", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "u", /* short option */ - "userid", /* long option */ - I18S("Specify user id"), - I18S("Specify user id") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "password", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - "p", /* short option */ - "password", /* long option */ - I18S("Specify service password"), - I18S("Specify service password") - }, - { - GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ - GWEN_ArgsType_Char, /* type */ - "template", /* name */ - 0, /* minnum */ - 1, /* maxnum */ - NULL, /* short option */ - "tmpl", /* long option */ - I18S("Specify template for output"), - I18S("Specify template for output") - }, - { - GWEN_ARGS_FLAGS_HELP | GWEN_ARGS_FLAGS_LAST, /* flags */ - GWEN_ArgsType_Int, /* type */ - "help", /* name */ - 0, /* minnum */ - 0, /* maxnum */ - "h", /* short option */ - "help", /* long option */ - "Show this help screen", /* short description */ - "Show this help screen" /* long description */ - } + /* flags type name min max s long short_descr, long_descr */ + { A_ARG, A_CHAR, "brokerAddress", 0, 1, "t", "tcpaddress", I18S("TCP address to connect to [127.0.0.1]"), NULL}, + { A_ARG, A_INT, "brokerPort", 0, 1, "P", "tcpport", I18S("Specify the TCP port to listen on"), NULL}, + { A_ARG, A_INT, "timeout", 0, 1, "T", NULL, I18S("Specify timeout in seconds for response"), NULL}, + { A_ARG, A_CHAR, "brokerClientId", 0, 1, "c", "clientid", I18S("Specify CLIENTID"), NULL}, + { A_ARG, A_CHAR, "userId", 0, 1, "u", "userid", I18S("Specify user id"), NULL}, + { A_ARG, A_CHAR, "password", 0, 1, "p", "password", I18S("Specify service password"), NULL}, + { A_ARG, A_CHAR, "template", 0, 1, NULL, "tmpl", I18S("Specify template for output"), NULL}, + { A_END, A_INT, "help", 0, 0, "h", "help", I18S("Show this help screen"), NULL} }; - dbLocalArgs=GWEN_DB_GetGroup(dbGlobalArgs, GWEN_DB_FLAGS_DEFAULT, "local"); - rv=GWEN_Args_Check(argc, argv, 1, - GWEN_ARGS_MODE_ALLOW_FREEPARAM, - args, - dbLocalArgs); - if (rv==GWEN_ARGS_RESULT_ERROR) { - fprintf(stderr, "ERROR: Could not parse arguments\n"); - return 1; - } - else if (rv==GWEN_ARGS_RESULT_HELP) { - GWEN_BUFFER *ubuf; - - ubuf=GWEN_Buffer_new(0, 1024, 0, 1); - if (GWEN_Args_Usage(args, ubuf, GWEN_ArgsOutType_Txt)) { - fprintf(stderr, "ERROR: Could not create help string\n"); - return 1; - } - fprintf(stderr, "%s\n", GWEN_Buffer_GetStart(ubuf)); - GWEN_Buffer_free(ubuf); - return 0; - } - - AQH_MergeConfigFileIntoConfig(dbLocalArgs, "ConfigFile"); - - return _doWatch(dbLocalArgs); + eventLoop=AQH_EventLoop_new(); + o=AQH_ToolClient_new(eventLoop, dbGlobalArgs, args); + AQH_ToolClient_AddFlags(o, AQH_ENDPOINT_FLAGS_WANTUPDATES); + AQH_ToolClient_SetHandleResponseMessageFn(o, _handleResponseMessage); + rv=AQH_ToolClient_ReadLocalArgs(o, argc, argv); + if (rv!=0) + return rv; + rv=AQH_ToolClient_Watch(o); + AQH_Object_free(o); + AQH_EventLoop_free(eventLoop); + return rv; } -int _doWatch(GWEN_DB_NODE *dbArgs) +int _handleResponseMessage(AQH_OBJECT *o, const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList, int first) { - GWEN_MSG_ENDPOINT *epTcp; - int timeoutInSeconds; + uint16_t code; + GWEN_DB_NODE *dbArgs; const char *tmpl; - timeoutInSeconds=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 0); + dbArgs=AQH_ToolClient_GetDbLocalArgs(o); tmpl=GWEN_DB_GetCharValue(dbArgs, "template", 0, "$(datapoint/timestampAsDate)\t$(datapoint/datapoint)\t$(value/nameForSystem)"); - epTcp=Utils_SetupBrokerClientEndpoint(dbArgs, AQH_IPCENDPOINT_FLAGS_WANTUPDATES); - if (epTcp==NULL) { - DBG_ERROR(NULL, "ERROR creating TCP connection"); - return 2; + code=AQH_IpcMessage_GetCode(msg); + if (code==AQH_MSGTYPE_IPC_DATA_DATACHANGED) { + _handleDataResponse(msg, tagList, tmpl); + } + else { + DBG_INFO(NULL, "Ignore message \"%d\"", code); } - - _awaitAndPrintChanges(epTcp, timeoutInSeconds, tmpl); - - GWEN_MsgEndpoint_free(epTcp); return 0; } -void _awaitAndPrintChanges(GWEN_MSG_ENDPOINT *epTcp, int timeoutInSeconds, const char *tmpl) -{ - time_t tStart; - - tStart=time(NULL); - - for (;;) { - GWEN_MSG *msg; - - msg=Utils_WaitForSpecificIpcMessage(epTcp, AQH_MSGTYPE_IPC_DATA_DATACHANGED, 5); - if (msg) { - uint16_t code; - - code=GWEN_IpcMsg_GetCode(msg); - if (code==AQH_MSGTYPE_IPC_DATA_DATACHANGED) { - _handleDataResponse(msg, tmpl); - GWEN_Msg_free(msg); - } - else { - DBG_INFO(NULL, "Unexpected message \"%d\"", code); - GWEN_Msg_free(msg); - } - } - if (timeoutInSeconds) { - time_t now; - double delta; - - now=time(NULL); - delta=difftime(now, tStart); - if ((int)delta>=timeoutInSeconds) { - DBG_INFO(NULL, "Timeout"); - break; - } - } - } /* for */ -} - - - -void _handleDataResponse(GWEN_MSG *msg, const char *tmpl) +void _handleDataResponse(const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList, const char *tmpl) { AQH_VALUE *value; - const GWEN_TAG16 *tag; - unsigned int numberOfPoints; - const uint64_t *dataPoints; - AQH_MultiDataDataIpcMsg_Parse(msg, 0); - value=AQH_MultiDataDataIpcMsg_ReadValue(msg); + value=AQH_IpcdMessageMultiData_ReadValue(tagList); + if (value) { + uint64_t numberOfPoints; + const uint64_t *dataPoints; - tag=AQH_Tag16IpcMsg_FindFirstTagByType(msg, AQH_MSGDATA_MULTIDATA_TAGS_DATA); - numberOfPoints=(tag?GWEN_Tag16_GetTagLength(tag):0)/(2*sizeof(uint64_t)); - dataPoints=tag?((const uint64_t*) GWEN_Tag16_GetTagData(tag)):NULL; - if (numberOfPoints>0 && dataPoints) { - uint32_t i; - - for(i=0; i0 && dataPoints) { + uint32_t i; + + for(i=0; itimeoutInSeconds) { - DBG_ERROR(NULL, "Timeout"); + DBG_INFO(NULL, "Timeout"); break; } } diff --git a/aqhome/ipc2/endpoint.h b/aqhome/ipc2/endpoint.h index 66b3af8..2a0f524 100644 --- a/aqhome/ipc2/endpoint.h +++ b/aqhome/ipc2/endpoint.h @@ -13,6 +13,8 @@ #include +#define AQH_ENDPOINT_FLAGS_WANTUPDATES 0x0001 + enum { AQH_ENDPOINT_SIGNAL_CLOSED=AQH_OBJECT_SIGNAL_LAST, diff --git a/aqhome/ipc2/msgrequest.c b/aqhome/ipc2/msgrequest.c index 953fe21..eb8a648 100644 --- a/aqhome/ipc2/msgrequest.c +++ b/aqhome/ipc2/msgrequest.c @@ -209,7 +209,7 @@ void AQH_MsgRequest_SetTimestamps(AQH_MSG_REQUEST *rq, int expiresInSecs) -int AQH_MsgRequest_HandleResponse(AQH_MSG_REQUEST *rq, AQH_MESSAGE *msg) +int AQH_MsgRequest_HandleResponse(AQH_MSG_REQUEST *rq, const AQH_MESSAGE *msg) { if (rq && rq->handleResponseFn) return (rq->handleResponseFn)(rq, msg); diff --git a/aqhome/ipc2/msgrequest.h b/aqhome/ipc2/msgrequest.h index e24a922..bdb8495 100644 --- a/aqhome/ipc2/msgrequest.h +++ b/aqhome/ipc2/msgrequest.h @@ -35,7 +35,7 @@ typedef struct AQH_MSG_REQUEST AQH_MSG_REQUEST; GWEN_INHERIT_FUNCTION_LIB_DEFS(AQH_MSG_REQUEST, AQHOME_API) GWEN_TREE2_FUNCTION_LIB_DEFS(AQH_MSG_REQUEST, AQH_MsgRequest, AQHOME_API) -typedef int (*AQH_MSG_REQUEST_HANDLERESPONSE_FN)(AQH_MSG_REQUEST *rq, AQH_MESSAGE *msg); +typedef int (*AQH_MSG_REQUEST_HANDLERESPONSE_FN)(AQH_MSG_REQUEST *rq, const AQH_MESSAGE *msg); typedef void (*AQH_MSG_REQUEST_SUBREQUESTFINISHED_FN)(AQH_MSG_REQUEST *rq, AQH_MSG_REQUEST *subRq, int reason); typedef void (*AQH_MSG_REQUEST_ABORT_FN)(AQH_MSG_REQUEST *rq, int reason); @@ -74,7 +74,7 @@ AQHOME_API int AQH_MsgRequest_GetState(const AQH_MSG_REQUEST *rq); AQHOME_API void AQH_MsgRequest_SetState(AQH_MSG_REQUEST *rq, int i); -AQHOME_API int AQH_MsgRequest_HandleResponse(AQH_MSG_REQUEST *rq, AQH_MESSAGE *msg); +AQHOME_API int AQH_MsgRequest_HandleResponse(AQH_MSG_REQUEST *rq, const AQH_MESSAGE *msg); AQHOME_API void AQH_MsgRequest_SubRequestFinished(AQH_MSG_REQUEST *rq, AQH_MSG_REQUEST *subRq, int reason); AQHOME_API void AQH_MsgRequest_Abort(AQH_MSG_REQUEST *rq, int reason); diff --git a/aqhome/msg/ipc/m_ipc_result.c b/aqhome/msg/ipc/m_ipc_result.c index b06c709..75e153e 100644 --- a/aqhome/msg/ipc/m_ipc_result.c +++ b/aqhome/msg/ipc/m_ipc_result.c @@ -60,6 +60,13 @@ uint32_t AQH_IpcMessageResult_GetResult(const GWEN_TAG16_LIST *tagList) +char *AQH_IpcMessageResult_GetText(const GWEN_TAG16_LIST *tagList) +{ + return tagList?AQH_Tag16_GetTagDataAsNewString(tagList, AQH_MSGDATA_RESULT_TAGS_TEXT, NULL):0; +} + + + void AQH_IpcMessageResult_DumpToBuffer(const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList, GWEN_BUFFER *dbuf, const char *sText) { int result=0; diff --git a/aqhome/msg/ipc/m_ipc_result.h b/aqhome/msg/ipc/m_ipc_result.h index e95d170..b5f6d95 100644 --- a/aqhome/msg/ipc/m_ipc_result.h +++ b/aqhome/msg/ipc/m_ipc_result.h @@ -40,6 +40,7 @@ AQHOME_API AQH_MESSAGE *AQH_IpcMessageResult_new(uint8_t protoId, uint8_t protoV int result, const char *text); AQHOME_API uint32_t AQH_IpcMessageResult_GetResult(const GWEN_TAG16_LIST *tagList); +AQHOME_API char *AQH_IpcMessageResult_GetText(const GWEN_TAG16_LIST *tagList); AQHOME_API void AQH_IpcMessageResult_DumpToBuffer(const AQH_MESSAGE *msg, const GWEN_TAG16_LIST *tagList, GWEN_BUFFER *dbuf, const char *sText);