Heavy work on IPC.
We will now have a broker (aqhome-data) which stores data and distributes value change messages among connected clients. aqhomed will connect to that broker and send its values there. aqhome-mqtt will also connect to the broker and send its values there. Other clients can later connect to check for changes and react according to rules.
This commit is contained in:
311
apps/aqhome-tool/data/getdatapoints.c
Normal file
311
apps/aqhome-tool/data/getdatapoints.c
Normal file
@@ -0,0 +1,311 @@
|
||||
/****************************************************************************
|
||||
* 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 <config.h>
|
||||
#endif
|
||||
|
||||
#include "./getdatapoints.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 <gwenhywfar/args.h>
|
||||
#include <gwenhywfar/i18n.h>
|
||||
#include <gwenhywfar/debug.h>
|
||||
#include <gwenhywfar/text.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#define I18S(msg) msg
|
||||
#define I18N(msg) GWEN_I18N_Translate(PACKAGE, msg)
|
||||
|
||||
|
||||
static int _doGetDataPoints(GWEN_DB_NODE *dbArgs);
|
||||
static void _sendCommand(GWEN_MSG_ENDPOINT *epTcp, const char *valueName, uint64_t tsBegin, uint64_t tsEnd);
|
||||
static uint64_t _getTimeStampFromString(const char *s);
|
||||
|
||||
|
||||
|
||||
|
||||
int AQH_Tool_GetDataPoints(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 */
|
||||
"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_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 _doGetDataPoints(dbLocalArgs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _doGetDataPoints(GWEN_DB_NODE *dbArgs)
|
||||
{
|
||||
GWEN_MSG_ENDPOINT *epTcp;
|
||||
int timeoutInSeconds;
|
||||
GWEN_MSG *msg;
|
||||
const char *valueName;
|
||||
uint64_t tsBegin;
|
||||
uint64_t tsEnd;
|
||||
|
||||
timeoutInSeconds=GWEN_DB_GetIntValue(dbArgs, "timeout", 0, 5);
|
||||
valueName=GWEN_DB_GetCharValue(dbArgs, "valueName", 0, NULL);
|
||||
tsBegin=_getTimeStampFromString(GWEN_DB_GetCharValue(dbArgs, "tsBegin", 0, NULL));
|
||||
if (tsBegin==(uint64_t) (-1)) {
|
||||
DBG_ERROR(NULL, "Bad begin timestamp");
|
||||
return 2;
|
||||
}
|
||||
tsEnd=_getTimeStampFromString(GWEN_DB_GetCharValue(dbArgs, "tsEnd", 0, NULL));
|
||||
if (tsEnd==(uint64_t) (-1)) {
|
||||
DBG_ERROR(NULL, "Bad end timestamp");
|
||||
return 2;
|
||||
}
|
||||
|
||||
epTcp=Utils_OpenConnection(dbArgs, 0, timeoutInSeconds);
|
||||
if (epTcp==NULL) {
|
||||
DBG_ERROR(NULL, "ERROR creating TCP connection");
|
||||
return 2;
|
||||
}
|
||||
|
||||
fprintf(stdout, "Sending GetDataPoints request\n");
|
||||
|
||||
_sendCommand(epTcp, valueName, tsBegin, tsEnd);
|
||||
|
||||
for (;;) {
|
||||
uint16_t code;
|
||||
|
||||
msg=Utils_WaitForSpecificIpcMessage(epTcp, AQH_MSGTYPE_IPC_DATA_GETDATA_RSP, timeoutInSeconds);
|
||||
if (msg==NULL) {
|
||||
DBG_ERROR(NULL, "No response received");
|
||||
return 2;
|
||||
}
|
||||
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<numValues; i++) {
|
||||
uint64_t timestamp;
|
||||
union {double f; uint64_t i;} u;
|
||||
|
||||
timestamp=*(dataPoints++);
|
||||
u.i=*(dataPoints++);
|
||||
|
||||
fprintf(stdout, "%lu\t%lf\t%s\n",
|
||||
(unsigned long int) timestamp,
|
||||
u.f,
|
||||
valueUnits?valueUnits:"");
|
||||
}
|
||||
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, uint64_t tsBegin, uint64_t tsEnd)
|
||||
{
|
||||
GWEN_MSG *msgOut;
|
||||
uint64_t array[2];
|
||||
|
||||
array[0]=tsBegin;
|
||||
array[1]=tsEnd;
|
||||
|
||||
msgOut=AQH_DataPointsDataIpcMsg_new(AQH_MSGTYPE_IPC_DATA_GETDATA_REQ, 0, 0, valueName, NULL, array, 2);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user