From 71f5ce8c7ec0abdfd6b17cb70b527735b5dac5d3 Mon Sep 17 00:00:00 2001 From: Martin Preuss Date: Mon, 11 Sep 2023 22:55:38 +0200 Subject: [PATCH] Implemented GETLASTDATA in server and aqhome-tool. --- apps/aqhome-data/c_getlastdatapoint.c | 64 +++++- apps/aqhome-data/loop.c | 2 +- apps/aqhome-tool/data/0BUILD | 2 + apps/aqhome-tool/data/adddata.c | 5 +- apps/aqhome-tool/data/getdatapoints.c | 24 +-- apps/aqhome-tool/data/getlastdatapoint.c | 254 +++++++++++++++++++++++ apps/aqhome-tool/data/getlastdatapoint.h | 21 ++ apps/aqhome-tool/main.c | 2 + apps/aqhome-tool/utils.c | 20 ++ apps/aqhome-tool/utils.h | 2 + aqhome/data/storage.c | 29 +++ aqhome/data/storage.h | 1 + aqhome/ipc/data/ipc_data.h | 21 +- aqhome/ipc/data/msg_data_datapoints.c | 12 +- 14 files changed, 408 insertions(+), 51 deletions(-) create mode 100644 apps/aqhome-tool/data/getlastdatapoint.c create mode 100644 apps/aqhome-tool/data/getlastdatapoint.h diff --git a/apps/aqhome-data/c_getlastdatapoint.c b/apps/aqhome-data/c_getlastdatapoint.c index 3844e52..ed83f2a 100644 --- a/apps/aqhome-data/c_getlastdatapoint.c +++ b/apps/aqhome-data/c_getlastdatapoint.c @@ -14,10 +14,11 @@ #include "./c_getlastdatapoint.h" #include "./aqhome_data_p.h" #include "aqhome/ipc/data/ipc_data.h" -#include "aqhome/ipc/data/msg_data_values.h" +#include "aqhome/ipc/data/msg_data_datapoints.h" #include "aqhome/ipc/msg_ipc_result.h" #include +#include @@ -41,8 +42,67 @@ */ -void AqHomeData_HandleGetLastDataPoint(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg) +void AqHomeData_HandleGetLastDataPoint(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *recvdMsg) { + GWEN_MSG *outMsg; + int resultCode=0; + + if (AQH_DataPointsDataIpcMsg_IsValid(recvdMsg)) { + const char *valueName; + + valueName=AQH_DataPointsDataIpcMsg_GetValueName(recvdMsg); + if (valueName) { + const AQH_VALUE *value; + + value=AQH_Storage_GetValueByNameForSystem(aqh->storage, valueName); + if (value) { + uint64_t valueId; + uint64_t timestamp=0; + union {double f; uint64_t i;} u; + int rv; + + valueId=AQH_Value_GetId(value); + rv=AQH_Storage_GetLastDataPoint(aqh->storage, valueId, ×tamp, &(u.f)); + if (rv<0) { + switch(rv) { + case GWEN_ERROR_INVALID: resultCode=AQH_MSG_IPC_ERROR_INVALID; break; + case GWEN_ERROR_NO_DATA: resultCode=AQH_MSG_IPC_ERROR_NODATA; break; + default: resultCode=AQH_MSG_IPC_ERROR_GENERIC; break; + } + } + else { + uint64_t array[2]; + + array[0]=timestamp; + array[1]=u.i; + outMsg=AQH_DataPointsDataIpcMsg_new(AQH_MSGTYPE_IPC_DATA_GETLASTDATA_RSP, AQH_MSGDATA_DATAPOINTS_FLAGS_LASTMSG, + valueId, + AQH_Value_GetNameForSystem(value), + AQH_Value_GetValueUnits(value), + array, 1); + GWEN_MsgEndpoint_AddSendMessage(ep, outMsg); + return; + } + } + else { + DBG_INFO(NULL, "Value \"%s\" not found", valueName); + resultCode=AQH_MSG_IPC_ERROR_NOTFOUND; + } + } + else { + DBG_INFO(NULL, "No value name in request"); + resultCode=AQH_MSG_IPC_ERROR_INVALID; + } + } + else { + DBG_INFO(NULL, "Invalid request message"); + resultCode=AQH_MSG_IPC_ERROR_INVALID; + } + + outMsg=AQH_ResultIpcMsg_new(AQH_MSGTYPE_IPC_DATA_RESULT, resultCode); + GWEN_MsgEndpoint_AddSendMessage(ep, outMsg); } + + diff --git a/apps/aqhome-data/loop.c b/apps/aqhome-data/loop.c index 27cc0ca..2834a84 100644 --- a/apps/aqhome-data/loop.c +++ b/apps/aqhome-data/loop.c @@ -133,7 +133,7 @@ void _handleIpcMsg(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *msg) case AQH_MSGTYPE_IPC_DATA_UPDATEDATA: AqHomeData_HandleUpdateData(aqh, ep, msg); break; case AQH_MSGTYPE_IPC_DATA_GETVALUES_REQ: AqHomeData_HandleGetValues(aqh, ep, msg); break; case AQH_MSGTYPE_IPC_DATA_GETDATA_REQ: AqHomeData_HandleGetDataPoints(aqh, ep, msg); break; - case AQH_MSGTYPE_IPC_DATA_GETLASTDATAPOINT_REQ: AqHomeData_HandleGetLastDataPoint(aqh, ep, msg); break; + case AQH_MSGTYPE_IPC_DATA_GETLASTDATA_REQ: AqHomeData_HandleGetLastDataPoint(aqh, ep, msg); break; default: break; } } diff --git a/apps/aqhome-tool/data/0BUILD b/apps/aqhome-tool/data/0BUILD index 21385ed..c0876d6 100644 --- a/apps/aqhome-tool/data/0BUILD +++ b/apps/aqhome-tool/data/0BUILD @@ -36,6 +36,7 @@ getvalues.h adddata.h getdatapoints.h + getlastdatapoint.h @@ -44,6 +45,7 @@ getvalues.c adddata.c getdatapoints.c + getlastdatapoint.c diff --git a/apps/aqhome-tool/data/adddata.c b/apps/aqhome-tool/data/adddata.c index 542b53b..c78445a 100644 --- a/apps/aqhome-tool/data/adddata.c +++ b/apps/aqhome-tool/data/adddata.c @@ -253,14 +253,13 @@ int _doAddData(GWEN_DB_NODE *dbArgs) for (;;) { uint16_t code; - msg=Utils_WaitForSpecificIpcMessage(epTcp, AQH_MSGTYPE_IPC_DATA_ADDDATAPOINTS_RSP, timeoutInSeconds); + 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_ADDDATAPOINTS_RSP || - code==AQH_MSGTYPE_IPC_DATA_RESULT) { + if (code==AQH_MSGTYPE_IPC_DATA_RESULT) { uint32_t resultCode; resultCode=AQH_ResultIpcMsg_GetResultCode(msg); diff --git a/apps/aqhome-tool/data/getdatapoints.c b/apps/aqhome-tool/data/getdatapoints.c index 29e4727..458d8ed 100644 --- a/apps/aqhome-tool/data/getdatapoints.c +++ b/apps/aqhome-tool/data/getdatapoints.c @@ -225,27 +225,9 @@ int _doGetDataPoints(GWEN_DB_NODE *dbArgs) code=GWEN_IpcMsg_GetCode(msg); if (code==AQH_MSGTYPE_IPC_DATA_GETDATA_RSP) { if (AQH_DataPointsDataIpcMsg_IsValid(msg)) { - const uint64_t *dataPoints; - const char *valueUnits; - uint32_t numValues; - uint32_t i; - - dataPoints=AQH_DataPointsDataIpcMsg_GetDataPoints(msg); - valueUnits=AQH_DataPointsDataIpcMsg_GetUnits(msg); - - numValues=AQH_DataPointsDataIpcMsg_GetNumValues(msg); - for(i=0; i +#endif + +#include "./getlastdatapoint.h" +#include "../utils.h" + +#include "aqhome/msg/msg_node.h" +#include "aqhome/ipc/msg_ipc_result.h" +#include "aqhome/ipc/data/msg_data_datapoints.h" +#include "aqhome/ipc/data/ipc_data.h" + +#include +#include +#include +#include + +#include +#include + + +#define I18S(msg) msg +#define I18N(msg) GWEN_I18N_Translate(PACKAGE, msg) + + +static int _doGetLastDataPoint(GWEN_DB_NODE *dbArgs); +static void _sendCommand(GWEN_MSG_ENDPOINT *epTcp, const char *valueName); + + + + +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 */ + "tcpAddress", /* 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 */ + "tcpPort", /* name */ + 0, /* minnum */ + 1, /* maxnum */ + "P", /* short option */ + "tcpport", /* long option */ + I18S("Specify the TCP port to listen on"), + I18S("Specify the TCP port to listen on") + }, + { + GWEN_ARGS_FLAGS_HAS_ARGUMENT, /* flags */ + GWEN_ArgsType_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 */ + "clientId", /* 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; + } + + return _doGetLastDataPoint(dbLocalArgs); +} + + + +int _doGetLastDataPoint(GWEN_DB_NODE *dbArgs) +{ + GWEN_MSG_ENDPOINT *epTcp; + int timeoutInSeconds; + GWEN_MSG *msg; + const char *valueName; + + timeoutInSeconds=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 5); + valueName=GWEN_DB_GetCharValue(dbArgs, "valueName", 0, NULL); + + epTcp=Utils_OpenConnection(dbArgs, 0, timeoutInSeconds); + if (epTcp==NULL) { + DBG_ERROR(NULL, "ERROR creating TCP connection"); + return 2; + } + + fprintf(stdout, "Sending GetLastDataPoint request (%s)\n", valueName); + + _sendCommand(epTcp, valueName); + + for (;;) { + uint16_t code; + + msg=Utils_WaitForSpecificIpcMessage(epTcp, AQH_MSGTYPE_IPC_DATA_GETLASTDATA_RSP, timeoutInSeconds); + if (msg==NULL) { + DBG_ERROR(NULL, "No response received"); + return 2; + } + code=GWEN_IpcMsg_GetCode(msg); + if (code==AQH_MSGTYPE_IPC_DATA_GETLASTDATA_RSP) { + if (AQH_DataPointsDataIpcMsg_IsValid(msg)) { + Utils_PrintDataPoints(AQH_DataPointsDataIpcMsg_GetDataPoints(msg), + AQH_DataPointsDataIpcMsg_GetNumValues(msg), + AQH_DataPointsDataIpcMsg_GetUnits(msg)); + if (AQH_DataPointsDataIpcMsg_GetFlags(msg) & AQH_MSGDATA_DATAPOINTS_FLAGS_LASTMSG) { + DBG_INFO(NULL, "Last message received"); + break; + } + } + else { + DBG_ERROR(NULL, "Invalid message received"); + GWEN_MsgEndpoint_free(epTcp); + return 3; + } + } + else if (code==AQH_MSGTYPE_IPC_DATA_RESULT) { + uint32_t resultCode; + + resultCode=AQH_ResultIpcMsg_GetResultCode(msg); + fprintf(stderr, "ERROR: %d\n", resultCode); + GWEN_MsgEndpoint_free(epTcp); + return 3; + } + 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) +{ + GWEN_MSG *msgOut; + + msgOut=AQH_DataPointsDataIpcMsg_new(AQH_MSGTYPE_IPC_DATA_GETLASTDATA_REQ, 0, 0, valueName, NULL, NULL, 0); + GWEN_MsgEndpoint_AddSendMessage(epTcp, msgOut); +} + + + +uint64_t _getTimeStampFromString(const char *s) +{ + if (s && *s) { + unsigned long int x; + + if (1!=sscanf("%lu", s, &x)) { + DBG_ERROR(NULL, "ERROR: Invalid timestamp"); + return (uint64_t) (-1); + } + return (uint64_t) x; + } + return 0; +} + + + + diff --git a/apps/aqhome-tool/data/getlastdatapoint.h b/apps/aqhome-tool/data/getlastdatapoint.h new file mode 100644 index 0000000..957a868 --- /dev/null +++ b/apps/aqhome-tool/data/getlastdatapoint.h @@ -0,0 +1,21 @@ +/**************************************************************************** + * This file is part of the project AqHome. + * AqHome (c) by 2023 Martin Preuss, all rights reserved. + * + * The license for this file can be found in the file COPYING which you + * should have received along with this file. + ****************************************************************************/ + +#ifndef AQHOME_TOOL_GETLASTDATAPOINT_H +#define AQHOME_TOOL_GETLASTDATAPOINT_H + + +#include + + + +int AQH_Tool_GetLastDataPoint(GWEN_DB_NODE *dbGlobalArgs, int argc, char **argv); + + +#endif + diff --git a/apps/aqhome-tool/main.c b/apps/aqhome-tool/main.c index b02006d..4605c6a 100644 --- a/apps/aqhome-tool/main.c +++ b/apps/aqhome-tool/main.c @@ -16,6 +16,7 @@ #include "./data/getvalues.h" #include "./data/adddata.h" #include "./data/getdatapoints.h" +#include "./data/getlastdatapoint.h" #include #include @@ -75,6 +76,7 @@ int main(int argc, char **argv) GWEN_FE_DAH("getvalues", AQH_Tool_GetValues, I18N("Request list of known values on the data server")), GWEN_FE_DAH("adddata", AQH_Tool_AddDataPoint, I18N("Send a datapoint to the data server")), GWEN_FE_DAH("getdata", AQH_Tool_GetDataPoints, I18N("Request list of datapoints for a value on the data server")), + GWEN_FE_DAH("getlastdata", AQH_Tool_GetLastDataPoint, I18N("Request last datapoint for a value on the data server")), GWEN_FE_END(), }; const GWEN_FUNCS *func; diff --git a/apps/aqhome-tool/utils.c b/apps/aqhome-tool/utils.c index 9977113..53605a7 100644 --- a/apps/aqhome-tool/utils.c +++ b/apps/aqhome-tool/utils.c @@ -232,5 +232,25 @@ GWEN_MSG_ENDPOINT *Utils_OpenConnection(GWEN_DB_NODE *dbArgs, uint32_t flags, in +void Utils_PrintDataPoints(const uint64_t *dataPoints, uint32_t numValues, const char *valueUnits) +{ + uint32_t i; + + for(i=0; i remove */ -#define AQH_MSGTYPE_IPC_DATA_ADDVALUES_RSP 0xf02 /* AQH_ResultIpcMsg -> remove */ - -#define AQH_MSGTYPE_IPC_DATA_EDITVALUE_REQ 0xf03 /* AQH_ValuesDataIpcMsg -> remove */ -#define AQH_MSGTYPE_IPC_DATA_EDITVALUE_RSP 0xf04 /* AQH_ResultIpcMsg -> remove */ - -#define AQH_MSGTYPE_IPC_DATA_ADDDATAPOINTS_REQ 0xf05 /* AQH_DataPointsDataIpcMsg */ -#define AQH_MSGTYPE_IPC_DATA_ADDDATAPOINTS_RSP 0xf06 /* AQH_ResultIpcMsg */ - -#define AQH_MSGTYPE_IPC_DATA_GETDATAPOINTS_REQ 0xf07 /* AQH_DataPointsDataIpcMsg (1 pair: fromTime, toTime) */ -#define AQH_MSGTYPE_IPC_DATA_GETDATAPOINTS_RSP 0xf08 /* AQH_DataPointsDataIpcMsg */ - -#define AQH_MSGTYPE_IPC_DATA_GETLASTDATAPOINT_REQ 0xf09 /* AQH_DataPointsDataIpcMsg (0 datapoints) */ -#define AQH_MSGTYPE_IPC_DATA_GETLASTDATAPOINT_RSP 0xf0a /* AQH_DataPointsDataIpcMsg */ - - - - #endif diff --git a/aqhome/ipc/data/msg_data_datapoints.c b/aqhome/ipc/data/msg_data_datapoints.c index b228e17..75718d8 100644 --- a/aqhome/ipc/data/msg_data_datapoints.c +++ b/aqhome/ipc/data/msg_data_datapoints.c @@ -137,7 +137,7 @@ uint64_t AQH_DataPointsDataIpcMsg_GetValueId(const GWEN_MSG *msg) const char *AQH_DataPointsDataIpcMsg_GetValueName(const GWEN_MSG *msg) { - if (GWEN_Msg_GetBytesInBuffer(msg)>=GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_MINSIZE) + if (GWEN_Msg_GetBytesInBuffer(msg)>=AQH_MSGDATA_DATAPOINTS_MINSIZE) return (const char*) (GWEN_Msg_GetConstBuffer(msg)+GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_OFFS_VALUENAME); return NULL; } @@ -146,7 +146,7 @@ const char *AQH_DataPointsDataIpcMsg_GetValueName(const GWEN_MSG *msg) const char *AQH_DataPointsDataIpcMsg_GetUnits(const GWEN_MSG *msg) { - if (GWEN_Msg_GetBytesInBuffer(msg)>=GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_MINSIZE) + if (GWEN_Msg_GetBytesInBuffer(msg)>=AQH_MSGDATA_DATAPOINTS_MINSIZE) return (const char*) (GWEN_Msg_GetConstBuffer(msg)+GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_OFFS_VALUEUNITS); return NULL; } @@ -155,7 +155,7 @@ const char *AQH_DataPointsDataIpcMsg_GetUnits(const GWEN_MSG *msg) const uint64_t *AQH_DataPointsDataIpcMsg_GetDataPoints(const GWEN_MSG *msg) { - if (GWEN_Msg_GetBytesInBuffer(msg)>=GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_MINSIZE) + if (GWEN_Msg_GetBytesInBuffer(msg)>=AQH_MSGDATA_DATAPOINTS_MINSIZE) return (const uint64_t*) (GWEN_Msg_GetConstBuffer(msg)+GWEN_MSGIPC_OFFS_PAYLOAD+AQH_MSGDATA_DATAPOINTS_OFFS_VALUES); return NULL; @@ -200,12 +200,16 @@ int AQH_DataPointsDataIpcMsg_IsValid(const GWEN_MSG *msg) void AQH_DataPointsDataIpcMsg_DumpToBuffer(const GWEN_MSG *msg, GWEN_BUFFER *dbuf, const char *sText) { if (GWEN_Msg_GetBytesInBuffer(msg)>=AQH_MSGDATA_DATAPOINTS_MINSIZE) { + const char *valueName; + + valueName=AQH_DataPointsDataIpcMsg_GetValueName(msg); GWEN_Buffer_AppendArgs(dbuf, - "DATAPOINTS (code=%d, proto=%d, proto version=%d, flags=0x%08x, values=%d)\n", + "DATAPOINTS (code=%d, proto=%d, proto version=%d, flags=0x%08x, valueName=%s, values=%d)\n", GWEN_IpcMsg_GetCode(msg), GWEN_IpcMsg_GetProtoId(msg), GWEN_IpcMsg_GetProtoVersion(msg), (unsigned int)AQH_DataPointsDataIpcMsg_GetFlags(msg), + valueName?valueName:"", AQH_DataPointsDataIpcMsg_GetNumValues(msg)); } }