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:
210
apps/aqhome-data/c_updatedata.c
Normal file
210
apps/aqhome-data/c_updatedata.c
Normal file
@@ -0,0 +1,210 @@
|
||||
/****************************************************************************
|
||||
* 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 "./c_updatedata.h"
|
||||
#include "./aqhome_data_p.h"
|
||||
#include "aqhome/ipc/data/ipc_data.h"
|
||||
#include "aqhome/ipc/data/msg_data_datapoints.h"
|
||||
#include "aqhome/ipc/endpoint_ipc.h"
|
||||
#include "aqhome/ipc/msg_ipc_result.h"
|
||||
|
||||
#include <gwenhywfar/debug.h>
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* defines
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* forward declarations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
static int _readDataPoints(AQHOME_DATA *aqh, const AQH_VALUE *v, const uint64_t *dataPoints, uint32_t numValues);
|
||||
static void _sendDataChangedMsgToAllClients(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *epSrc, const AQH_VALUE *v,
|
||||
const uint64_t *dataPoints, uint32_t numValues);
|
||||
static AQH_VALUE *_getOrCreateValue(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const char *nameForDriver, const char *units);
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* implementations
|
||||
* ------------------------------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void AqHomeData_HandleUpdateData(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const GWEN_MSG *recvdMsg)
|
||||
{
|
||||
GWEN_MSG *outMsg;
|
||||
int resultCode=AQH_MSG_IPC_SUCCESS;
|
||||
|
||||
if (AQH_IpcEndpoint_GetPermissions(ep) & AQH_IPCENDPOINT_PERMS_ADDDATA) {
|
||||
if (AQH_DataPointsDataIpcMsg_IsValid(recvdMsg)) {
|
||||
uint32_t numValues;
|
||||
|
||||
numValues=AQH_DataPointsDataIpcMsg_GetNumValues(recvdMsg);
|
||||
if (numValues) {
|
||||
const char *s;
|
||||
|
||||
s=AQH_DataPointsDataIpcMsg_GetValueName(recvdMsg);
|
||||
if (s && *s) {
|
||||
AQH_VALUE *v;
|
||||
|
||||
v=_getOrCreateValue(aqh, ep, s, AQH_DataPointsDataIpcMsg_GetUnits(recvdMsg));
|
||||
if (v==NULL) {
|
||||
resultCode=AQH_MSG_IPC_ERROR_PERMS;
|
||||
}
|
||||
else {
|
||||
const uint64_t *dataPoints;
|
||||
|
||||
dataPoints=AQH_DataPointsDataIpcMsg_GetDataPoints(recvdMsg);
|
||||
if (dataPoints)
|
||||
resultCode=_readDataPoints(aqh, v, dataPoints, numValues);
|
||||
else {
|
||||
DBG_INFO(NULL, "No datapoints");
|
||||
resultCode=AQH_MSG_IPC_ERROR_BADDATA;
|
||||
}
|
||||
if (resultCode==AQH_MSG_IPC_SUCCESS)
|
||||
_sendDataChangedMsgToAllClients(aqh, ep, v, dataPoints, numValues);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "Value without name ");
|
||||
resultCode=AQH_MSG_IPC_ERROR_INVALID;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "No datapoints");
|
||||
resultCode=AQH_MSG_IPC_ERROR_BADDATA;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "Invalid message received");
|
||||
resultCode=AQH_MSG_IPC_ERROR_BADDATA;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(AQH_LOGDOMAIN, "No permissions to add data");
|
||||
resultCode=AQH_MSG_IPC_ERROR_PERMS;
|
||||
}
|
||||
|
||||
outMsg=AQH_ResultIpcMsg_new(AQH_MSGTYPE_IPC_DATA_RESULT, resultCode);
|
||||
GWEN_MsgEndpoint_AddSendMessage(ep, outMsg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int _readDataPoints(AQHOME_DATA *aqh, const AQH_VALUE *v, const uint64_t *dataPoints, uint32_t numValues)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for(i=0; i<numValues; i++) {
|
||||
uint64_t timestamp;
|
||||
union {double f; uint64_t i;} u;
|
||||
int rv;
|
||||
|
||||
timestamp=*(dataPoints++);
|
||||
u.i=*(dataPoints++);
|
||||
rv=AQH_Storage_AddDatapoint(aqh->storage, AQH_Value_GetId(v), timestamp, u.f);
|
||||
if (rv<0) {
|
||||
DBG_INFO(NULL, "here (%d)", rv);
|
||||
return AQH_MSG_IPC_ERROR_GENERIC;
|
||||
}
|
||||
else {
|
||||
DBG_INFO(NULL, "Datapoint added for value \"%s\"", AQH_Value_GetNameForSystem(v));
|
||||
}
|
||||
} /* for */
|
||||
|
||||
return AQH_MSG_IPC_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _sendDataChangedMsgToAllClients(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *epSrc, const AQH_VALUE *v,
|
||||
const uint64_t *dataPoints, uint32_t numValues)
|
||||
{
|
||||
GWEN_MSG_ENDPOINT *ep;
|
||||
|
||||
ep=GWEN_MsgEndpoint_Tree2_GetFirstChild(aqh->ipcdEndpoint);
|
||||
while(ep) {
|
||||
if (ep!=epSrc) {
|
||||
if (GWEN_MsgEndpoint_GetFlags(ep) & AQH_IPCENDPOINT_FLAGS_WANTUPDATES) {
|
||||
GWEN_MSG *msg;
|
||||
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Sending update msg to endpoint %s", GWEN_MsgEndpoint_GetName(ep));
|
||||
msg=AQH_DataPointsDataIpcMsg_new(AQH_MSGTYPE_IPC_DATA_DATACHANGED,
|
||||
0, /* flags */
|
||||
AQH_Value_GetId(v),
|
||||
AQH_Value_GetNameForSystem(v),
|
||||
AQH_Value_GetValueUnits(v),
|
||||
dataPoints, numValues);
|
||||
GWEN_MsgEndpoint_AddSendMessage(ep, msg);
|
||||
}
|
||||
else {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Endpoint %s doesn't want updates", GWEN_MsgEndpoint_GetName(ep));
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Not sending update msg to source of updates");
|
||||
}
|
||||
ep=GWEN_MsgEndpoint_Tree2_GetNext(ep);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQH_VALUE *_getOrCreateValue(AQHOME_DATA *aqh, GWEN_MSG_ENDPOINT *ep, const char *nameForDriver, const char *units)
|
||||
{
|
||||
const char *serviceName;
|
||||
AQH_VALUE *v;
|
||||
GWEN_BUFFER *buf;
|
||||
|
||||
serviceName=AQH_IpcEndpoint_GetServiceName(ep);
|
||||
|
||||
buf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||
if (serviceName && *serviceName) {
|
||||
GWEN_Buffer_AppendString(buf, serviceName);
|
||||
GWEN_Buffer_AppendString(buf, "/");
|
||||
}
|
||||
else {
|
||||
GWEN_Buffer_AppendString(buf, "unknown/");
|
||||
}
|
||||
GWEN_Buffer_AppendString(buf, nameForDriver);
|
||||
|
||||
v=AQH_Storage_GetValueByNameForSystem(aqh->storage, GWEN_Buffer_GetStart(buf));
|
||||
if (v==NULL) {
|
||||
if (AQH_IpcEndpoint_GetPermissions(ep) & AQH_IPCENDPOINT_PERMS_ADDVALUE) {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Creating value \"%s\"", GWEN_Buffer_GetStart(buf));
|
||||
v=AQH_Value_new();
|
||||
AQH_Value_SetDriver(v, serviceName);
|
||||
AQH_Value_SetNameForDriver(v, nameForDriver);
|
||||
AQH_Value_SetNameForSystem(v, GWEN_Buffer_GetStart(buf));
|
||||
AQH_Value_SetValueUnits(v, units);
|
||||
AQH_Storage_AddValue(aqh->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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user