Added datafile and handling of MQTT publish message.
This commit is contained in:
@@ -17,6 +17,8 @@
|
||||
#include <gwenhywfar/debug.h>
|
||||
#include <gwenhywfar/xml.h>
|
||||
#include <gwenhywfar/directory.h>
|
||||
#include <gwenhywfar/text.h>
|
||||
#include <gwenhywfar/json_read.h>
|
||||
|
||||
|
||||
|
||||
@@ -26,6 +28,17 @@
|
||||
*/
|
||||
|
||||
|
||||
static void _handleJsonTopic(AQH_STORAGE *sto, const AQH_MQTT_TOPIC *topic, const char *sValue);
|
||||
static void _handleNumTopic(AQH_STORAGE *sto, const AQH_MQTT_TOPIC *topic, const char *sValue);
|
||||
static void _handleValueForJsonElement(AQH_STORAGE *sto,
|
||||
const AQH_MQTT_TOPIC *topic,
|
||||
GWEN_JSON_ELEM *json,
|
||||
time_t timestamp,
|
||||
const AQH_VALUE *value);
|
||||
static AQH_DATAFILE *_getDataFileByValueId(AQH_STORAGE *sto, uint64_t valueId);
|
||||
static AQH_DATAFILE *_findDataFileByValueId(const AQH_STORAGE *sto, uint64_t valueId);
|
||||
static AQH_DATAFILE *_openOrCreateDataFileByValueId(AQH_STORAGE *sto, uint64_t valueId);
|
||||
static GWEN_BUFFER *_getDataFilePathForValueId(const AQH_STORAGE *sto, uint64_t valueId);
|
||||
|
||||
|
||||
|
||||
@@ -44,6 +57,7 @@ AQH_STORAGE *AQH_Storage_new(void)
|
||||
sto->deviceList=AQH_Device_List_new();
|
||||
sto->mqttTopicList=AQH_MqttTopic_List_new();
|
||||
sto->valueList=AQH_Value_List_new();
|
||||
sto->dataFileList=AQH_DataFile_List_new();
|
||||
|
||||
return sto;
|
||||
}
|
||||
@@ -53,10 +67,12 @@ AQH_STORAGE *AQH_Storage_new(void)
|
||||
void AQH_Storage_free(AQH_STORAGE *sto)
|
||||
{
|
||||
if (sto) {
|
||||
AQH_DataFile_List_free(sto->dataFileList);
|
||||
AQH_Value_List_free(sto->valueList);
|
||||
AQH_MqttTopic_List_free(sto->mqttTopicList);
|
||||
AQH_Device_List_free(sto->deviceList);
|
||||
AQH_Room_List_free(sto->roomList);
|
||||
free(sto->dataFileFolder);
|
||||
free(sto->stateFile);
|
||||
|
||||
GWEN_FREE_OBJECT(sto);
|
||||
@@ -249,13 +265,6 @@ void AQH_Storage_SubRuntimeFlags(AQH_STORAGE *sto, uint32_t flags)
|
||||
|
||||
|
||||
|
||||
void AQH_Storage_HandleMqttPublish(AQH_STORAGE *sto, const char *topic, const char *value)
|
||||
{
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
|
||||
|
||||
int AQH_Storage_Init(AQH_STORAGE *sto)
|
||||
{
|
||||
int rv;
|
||||
@@ -281,6 +290,31 @@ int AQH_Storage_Init(AQH_STORAGE *sto)
|
||||
|
||||
|
||||
|
||||
int AQH_Storage_Fini(AQH_STORAGE *sto)
|
||||
{
|
||||
int errors=0;
|
||||
|
||||
if (sto) {
|
||||
AQH_DATAFILE *df;
|
||||
|
||||
while( (df=AQH_DataFile_List_First(sto->dataFileList)) ) {
|
||||
int rv;
|
||||
|
||||
AQH_DataFile_List_Del(df);
|
||||
rv=AQH_DataFile_Close(df);
|
||||
if (rv<0) {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Error closing file \"%s\"", AQH_DataFile_GetFileName(df));
|
||||
errors++;
|
||||
}
|
||||
AQH_DataFile_free(df);
|
||||
}
|
||||
}
|
||||
|
||||
return (errors==0)?0:GWEN_ERROR_GENERIC;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int AQH_Storage_WriteState(AQH_STORAGE *sto)
|
||||
{
|
||||
int rv;
|
||||
@@ -297,5 +331,234 @@ int AQH_Storage_WriteState(AQH_STORAGE *sto)
|
||||
|
||||
|
||||
|
||||
const char *AQH_Storage_GetDataFileFolder(const AQH_STORAGE *sto)
|
||||
{
|
||||
return sto?sto->dataFileFolder:NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AQH_Storage_SetDataFileFolder(AQH_STORAGE *sto, const char *s)
|
||||
{
|
||||
if (sto) {
|
||||
free(sto->dataFileFolder);
|
||||
sto->dataFileFolder=s?strdup(s):NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void AQH_Storage_HandleMqttPublish(AQH_STORAGE *sto, const char *sTopic, const char *sValue)
|
||||
{
|
||||
if (sto) {
|
||||
const AQH_MQTT_TOPIC *topic;
|
||||
|
||||
topic=AQH_Storage_GetMqttTopicByTopic(sto, sTopic);
|
||||
if (topic) {
|
||||
if (AQH_MqttTopic_GetDataType(topic)==AQH_MqttTopicType_Json)
|
||||
_handleJsonTopic(sto, topic, sValue);
|
||||
else
|
||||
_handleNumTopic(sto, topic, sValue);
|
||||
}
|
||||
else {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Unknown MQTT topic \"%s\"", sTopic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _handleJsonTopic(AQH_STORAGE *sto, const AQH_MQTT_TOPIC *topic, const char *sValue)
|
||||
{
|
||||
time_t now;
|
||||
GWEN_JSON_ELEM *json;
|
||||
|
||||
now=time(NULL);
|
||||
json=GWEN_JsonElement_fromString(sValue);
|
||||
if (json) {
|
||||
AQH_VALUE *value;
|
||||
uint64_t topicId;
|
||||
|
||||
topicId=AQH_MqttTopic_GetId(topic);
|
||||
value=AQH_Value_List_First(sto->valueList);
|
||||
while(value) {
|
||||
if (AQH_Value_GetTopicId(value)==topicId) {
|
||||
_handleValueForJsonElement(sto, topic, json, now, value);
|
||||
}
|
||||
value=AQH_Value_List_Next(value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(AQH_LOGDOMAIN, "Error parsing JSON data [%s]", sValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _handleValueForJsonElement(AQH_STORAGE *sto,
|
||||
const AQH_MQTT_TOPIC *topic,
|
||||
GWEN_JSON_ELEM *json,
|
||||
time_t timestamp,
|
||||
const AQH_VALUE *value)
|
||||
{
|
||||
const char *dataPath;
|
||||
|
||||
dataPath=AQH_Value_GetDataPath(value);
|
||||
if (dataPath) {
|
||||
GWEN_JSON_ELEM *jsonElem;
|
||||
|
||||
jsonElem=GWEN_JsonElement_GetElementByPath(json, dataPath, GWEN_PATH_FLAGS_PATHMUSTEXIST);
|
||||
if (jsonElem) {
|
||||
const char *s;
|
||||
|
||||
s=GWEN_JsonElement_GetData(jsonElem);
|
||||
if (s && *s) {
|
||||
int rv;
|
||||
double v;
|
||||
|
||||
rv=GWEN_Text_StringToDouble(s, &v);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(AQH_LOGDOMAIN, "Invalid value \"%s\" for topic %s:%s", s, AQH_MqttTopic_GetTopic(topic), dataPath);
|
||||
}
|
||||
else {
|
||||
AQH_DATAFILE *df;
|
||||
|
||||
df=_getDataFileByValueId(sto, AQH_Value_GetId(value));
|
||||
if (df) {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Appending record to datafile");
|
||||
AQH_DataFile_AppendRecord(df, timestamp, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Empty JSON element \"%s\"", dataPath);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_ERROR(AQH_LOGDOMAIN, "No value for topic %s:%s", AQH_MqttTopic_GetTopic(topic), dataPath);
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "No datapath in value \"%s\"", AQH_Value_GetName(value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _handleNumTopic(AQH_STORAGE *sto, const AQH_MQTT_TOPIC *topic, const char *sValue)
|
||||
{
|
||||
int rv;
|
||||
double v;
|
||||
time_t now;
|
||||
|
||||
now=time(NULL);
|
||||
|
||||
rv=GWEN_Text_StringToDouble(sValue, &v);
|
||||
if (rv<0) {
|
||||
DBG_ERROR(AQH_LOGDOMAIN, "Invalid value \"%s\" for topic %s, ignoring", sValue, AQH_MqttTopic_GetTopic(topic));
|
||||
}
|
||||
else {
|
||||
AQH_VALUE *value;
|
||||
uint64_t topicId;
|
||||
|
||||
topicId=AQH_MqttTopic_GetId(topic);
|
||||
value=AQH_Value_List_First(sto->valueList);
|
||||
while(value) {
|
||||
if (AQH_Value_GetTopicId(value)==topicId)
|
||||
break;
|
||||
value=AQH_Value_List_Next(value);
|
||||
}
|
||||
if (value) {
|
||||
AQH_DATAFILE *df;
|
||||
|
||||
df=_getDataFileByValueId(sto, AQH_Value_GetId(value));
|
||||
if (df) {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Appending record to datafile");
|
||||
AQH_DataFile_AppendRecord(df, now, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
AQH_DATAFILE *_getDataFileByValueId(AQH_STORAGE *sto, uint64_t valueId)
|
||||
{
|
||||
AQH_DATAFILE *df;
|
||||
|
||||
df=_findDataFileByValueId(sto, valueId);
|
||||
if (df==NULL) {
|
||||
df=_openOrCreateDataFileByValueId(sto, valueId);
|
||||
AQH_DataFile_List_Add(df, sto->dataFileList);
|
||||
}
|
||||
|
||||
return df;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQH_DATAFILE *_findDataFileByValueId(const AQH_STORAGE *sto, uint64_t valueId)
|
||||
{
|
||||
if (sto && sto->dataFileList)
|
||||
return AQH_DataFile_List_GetByValueId(sto->dataFileList, valueId);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AQH_DATAFILE *_openOrCreateDataFileByValueId(AQH_STORAGE *sto, uint64_t valueId)
|
||||
{
|
||||
AQH_DATAFILE *df;
|
||||
GWEN_BUFFER *nameBuf;
|
||||
int rv;
|
||||
|
||||
nameBuf=_getDataFilePathForValueId(sto, valueId);
|
||||
df=AQH_DataFile_new(GWEN_Buffer_GetStart(nameBuf), valueId);
|
||||
|
||||
rv=GWEN_Directory_GetPath(GWEN_Buffer_GetStart(nameBuf),
|
||||
GWEN_PATH_FLAGS_CHECKROOT |
|
||||
GWEN_PATH_FLAGS_PATHMUSTEXIST |
|
||||
GWEN_PATH_FLAGS_NAMEMUSTEXIST |
|
||||
GWEN_PATH_FLAGS_VARIABLE);
|
||||
if (rv==0) {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "File \"%s\" exists, trying to open", GWEN_Buffer_GetStart(nameBuf));
|
||||
rv=AQH_DataFile_Open(df);
|
||||
if (rv<0) {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Error opening data file \"%s\" (%d)", GWEN_Buffer_GetStart(nameBuf), rv);
|
||||
GWEN_Buffer_free(nameBuf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Creating file \"%s\"", GWEN_Buffer_GetStart(nameBuf));
|
||||
rv=AQH_DataFile_Create(df);
|
||||
if (rv<0) {
|
||||
DBG_INFO(AQH_LOGDOMAIN, "Error creating data file \"%s\" (%d)", GWEN_Buffer_GetStart(nameBuf), rv);
|
||||
GWEN_Buffer_free(nameBuf);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
GWEN_Buffer_free(nameBuf);
|
||||
|
||||
return df;
|
||||
}
|
||||
|
||||
|
||||
|
||||
GWEN_BUFFER *_getDataFilePathForValueId(const AQH_STORAGE *sto, uint64_t valueId)
|
||||
{
|
||||
GWEN_BUFFER *buf;
|
||||
|
||||
buf=GWEN_Buffer_new(0, 256, 0, 1);
|
||||
if (sto->dataFileFolder) {
|
||||
GWEN_Buffer_AppendString(buf, sto->dataFileFolder);
|
||||
GWEN_Buffer_AppendString(buf, GWEN_DIR_SEPARATOR_S);
|
||||
}
|
||||
GWEN_Buffer_AppendArgs(buf, "%" PRIu64 ".data", valueId);
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user