280 lines
8.8 KiB
C
280 lines
8.8 KiB
C
/****************************************************************************
|
|
* 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 <config.h>
|
|
#endif
|
|
|
|
|
|
#include "./mdevices_vgraph.h"
|
|
|
|
#include "aqhome-cgi/service/module.h"
|
|
#include "aqhome-cgi/modules/mdataclient.h"
|
|
|
|
#include <aqdiagram/graph/timegraph.h>
|
|
#include <aqdiagram/graph/w_graph.h>
|
|
#include <aqdiagram/draw/context_cairo.h>
|
|
//#include <aqdiagram/data/date.h>
|
|
//#include <aqdiagram/data/floatingavg.h>
|
|
|
|
#include <gwenhywfar/debug.h>
|
|
#include <gwenhywfar/timestamp.h>
|
|
#include <gwenhywfar/text.h>
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* defs and enums
|
|
* ------------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
#define GBAS GWEN_Buffer_AppendString
|
|
#define GBAA GWEN_Buffer_AppendArgs
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* vars
|
|
* ------------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
typedef struct MY_GRAPH_PARAMS MY_GRAPH_PARAMS;
|
|
struct MY_GRAPH_PARAMS {
|
|
const char *name;
|
|
const char *title;
|
|
const char *modifiers;
|
|
int startTimeDiff;
|
|
int acceptedAgeInSeconds;
|
|
};
|
|
|
|
|
|
|
|
static MY_GRAPH_PARAMS _graphParams[]={
|
|
{"4h", "last 4 hours", "Lm5", 4*60*60, 2*60},
|
|
{"1d", "last 24 hours", "Lm30", 24*60*60, 5*60},
|
|
{"1w", "last 7 days", "Lm240", 7*24*60*60, 15*60},
|
|
{"1m", "last 30 days", "Lm480", 30*24*60*60, 60*60},
|
|
{"6m", "last 6 months", "Lm720", 182*24*60*60, 60*60},
|
|
{"12m","last 12 months", "Lm1440", 365*24*60*60, 60*60},
|
|
{NULL, NULL, NULL, 0, 0}
|
|
};
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* forward declarations
|
|
* ------------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
static void _runGraphValueWithArgs(AQH_MODULE *m,
|
|
AQCGI_REQUEST *rq,
|
|
AQH_DATACLIENT *dc,
|
|
const char *sDeviceName,
|
|
const char *sValueName,
|
|
GWEN_BUFFER *dbuf);
|
|
static void _createGraph(AQH_DATACLIENT *dc,
|
|
const AQH_VALUE *v,
|
|
const MY_GRAPH_PARAMS *graphParams,
|
|
const char *graphTitle, int precision, const char *curveLabel,
|
|
const char *sImgFile, int imgWidth, int imgHeight, uint64_t numDataPoints);
|
|
static AQDG_GRAPH *_mkGraphObjectWithTitle(const char *graphTitle, const MY_GRAPH_PARAMS *graphParams, int precision);
|
|
static void _mkPathForValueAndPeriod(AQH_MODULE *m, const AQH_VALUE *v, const MY_GRAPH_PARAMS *graphParams, GWEN_BUFFER *dbuf);
|
|
static const MY_GRAPH_PARAMS *_getParamsByName(const char *s);
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------------------------------
|
|
* code
|
|
* ------------------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
void AQH_ModDevices_RunGraphValue(AQH_MODULE *m, AQCGI_REQUEST *rq, AQH_SESSION *session, AQH_DATACLIENT *dc, GWEN_BUFFER *dbuf)
|
|
{
|
|
GWEN_DB_NODE *dbQuery;
|
|
const char *sDeviceName;
|
|
const char *sValueName;
|
|
|
|
DBG_DEBUG(NULL, "GraphValue");
|
|
dbQuery=AQCGI_Request_GetDbQuery(rq);
|
|
sDeviceName=GWEN_DB_GetCharValue(dbQuery, "device", 0, NULL);
|
|
sValueName=GWEN_DB_GetCharValue(dbQuery, "value", 0, NULL);
|
|
DBG_DEBUG(NULL, "Device=%s, value=%s", sDeviceName?sDeviceName:"<empty>", sValueName?sValueName:"<empty>");
|
|
if (sDeviceName && *sDeviceName && sValueName && *sValueName) {
|
|
GWEN_BUFFER *bufDeviceName;
|
|
GWEN_BUFFER *bufValueName;
|
|
|
|
bufDeviceName=GWEN_Buffer_new(0, 64, 0, 1);
|
|
GWEN_Text_UnescapeToBufferTolerant(sDeviceName, bufDeviceName);
|
|
bufValueName=GWEN_Buffer_new(0, 64, 0, 1);
|
|
GWEN_Text_UnescapeToBufferTolerant(sValueName, bufValueName);
|
|
_runGraphValueWithArgs(m, rq, dc,
|
|
GWEN_Buffer_GetStart(bufDeviceName),
|
|
GWEN_Buffer_GetStart(bufValueName),
|
|
dbuf);
|
|
GWEN_Buffer_free(bufValueName);
|
|
GWEN_Buffer_free(bufDeviceName);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void _runGraphValueWithArgs(AQH_MODULE *m,
|
|
AQCGI_REQUEST *rq,
|
|
AQH_DATACLIENT *dc,
|
|
const char *sDeviceName,
|
|
const char *sValueName,
|
|
GWEN_BUFFER *dbuf)
|
|
{
|
|
GWEN_DB_NODE *dbQuery;
|
|
AQH_VALUE *value;
|
|
const MY_GRAPH_PARAMS *graphParams;
|
|
const char *sPeriod;
|
|
|
|
DBG_DEBUG(NULL, "GraphValue with args");
|
|
dbQuery=AQCGI_Request_GetDbQuery(rq);
|
|
sPeriod=GWEN_DB_GetCharValue(dbQuery, "period", 0, NULL);
|
|
graphParams=_getParamsByName(sPeriod);
|
|
if (graphParams==NULL)
|
|
graphParams=&_graphParams[0];
|
|
DBG_DEBUG(NULL, "Device=%s, value=%s, period=%s",
|
|
sDeviceName?sDeviceName:"<empty>", sValueName?sValueName:"<empty>",
|
|
sPeriod?sPeriod:"<empty>");
|
|
|
|
value=AQH_ModDevices_GetValueForDevice(dc, sDeviceName, sValueName);
|
|
if (value) {
|
|
GWEN_BUFFER *fbuf;
|
|
|
|
fbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
|
_mkPathForValueAndPeriod(m, value, graphParams, fbuf);
|
|
if (!AQH_ModService_FileIsCurrent(GWEN_Buffer_GetStart(fbuf), graphParams->acceptedAgeInSeconds)) {
|
|
DBG_DEBUG(NULL, "Creating graph");
|
|
_createGraph(dc,
|
|
value,
|
|
graphParams,
|
|
sValueName,
|
|
2,
|
|
AQH_ValueModality_toString(AQH_Value_GetModality(value)),
|
|
GWEN_Buffer_GetStart(fbuf),
|
|
AQH_MODDEVICES_GRAPH_WIDTH, AQH_MODDEVICES_GRAPH_HEIGHT,
|
|
100000);
|
|
}
|
|
|
|
AQH_ModService_RespondWithMimeFile(rq, GWEN_Buffer_GetStart(fbuf), "image/png", dbuf);
|
|
|
|
GWEN_Buffer_free(fbuf);
|
|
AQH_Value_free(value);
|
|
}
|
|
else {
|
|
DBG_ERROR(NULL, "Could not get value \"%s/%s\"", sDeviceName, sValueName);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void _createGraph(AQH_DATACLIENT *dc,
|
|
const AQH_VALUE *v,
|
|
const MY_GRAPH_PARAMS *graphParams,
|
|
const char *graphTitle, int precision,
|
|
const char *curveLabel,
|
|
const char *sImgFile, int imgWidth, int imgHeight, uint64_t numDataPoints)
|
|
{
|
|
const char *sValue;
|
|
AQDG_GRAPH *g;
|
|
AQDG_DRAW_CONTEXT *drawContext;
|
|
AQDG_OBJECT *graphObject;
|
|
uint64_t tsBegin;
|
|
uint64_t tsEnd;
|
|
AQDG_GRAPH_DATAPAIR_LIST *dpList;
|
|
|
|
sValue=AQH_Value_GetNameForSystem(v);
|
|
tsEnd=time(0);
|
|
tsBegin=time(0)-(graphParams->startTimeDiff);
|
|
g=_mkGraphObjectWithTitle(graphTitle, graphParams, precision);
|
|
|
|
dpList=AQH_ModDevices_RequestDataPairList(dc, sValue, tsBegin, tsEnd, numDataPoints);
|
|
if (dpList) {
|
|
DBG_DEBUG(NULL, "Adding data for %s", sValue);
|
|
AQDG_TimeGraph_ModifyDataAndAddCurve(g, curveLabel?curveLabel:sValue, graphParams->modifiers, dpList);
|
|
}
|
|
else {
|
|
DBG_ERROR(NULL, "No data for %s", sValue);
|
|
AQDG_Graph_free(g);
|
|
return;
|
|
}
|
|
|
|
AQDG_TimeGraph_SetupTicks(g, 0, 0.0, 0.0);
|
|
|
|
DBG_DEBUG(NULL, "Draw graph for %s", sValue);
|
|
drawContext=AQDG_Draw_ContextCairo_Png_new(sImgFile, imgWidth, imgHeight);
|
|
graphObject=AQDG_GraphWidget_new(NULL, AQDG_OBJECT_OPTIONS_STRETCHX | AQDG_OBJECT_OPTIONS_STRETCHY, drawContext);
|
|
AQDG_Object_SetWidth(graphObject, imgWidth);
|
|
AQDG_Object_SetHeight(graphObject, imgHeight);
|
|
|
|
AQDG_GraphWidget_SetupDefaultPens(graphObject);
|
|
AQDG_GraphWidget_SetupDefaultFonts(graphObject);
|
|
|
|
AQDG_GraphWidget_FinishWithGraph(graphObject, g);
|
|
|
|
AQDG_Object_free(graphObject);
|
|
}
|
|
|
|
|
|
|
|
AQDG_GRAPH *_mkGraphObjectWithTitle(const char *graphTitle, const MY_GRAPH_PARAMS *graphParams, int precision)
|
|
{
|
|
AQDG_GRAPH *g;
|
|
GWEN_BUFFER *tbuf;
|
|
|
|
tbuf=GWEN_Buffer_new(0, 256, 0, 1);
|
|
|
|
GBAA(tbuf, "%s - %s", graphTitle, graphParams->title);
|
|
|
|
g=AQDG_TimeGraph_new(GWEN_Buffer_GetStart(tbuf), NULL, "Value", NULL, precision);
|
|
GWEN_Buffer_free(tbuf);
|
|
return g;
|
|
}
|
|
|
|
|
|
void _mkPathForValueAndPeriod(AQH_MODULE *m, const AQH_VALUE *v, const MY_GRAPH_PARAMS *graphParams, GWEN_BUFFER *dbuf)
|
|
{
|
|
AQH_SERVICE *sv;
|
|
const char *s;
|
|
|
|
sv=AQH_ModService_GetService(m);
|
|
|
|
/* cache folder */
|
|
s=AQH_Service_GetCacheFolder(sv);
|
|
GBAA(dbuf, "%s%s", s, GWEN_DIR_SEPARATOR_S);
|
|
|
|
/* var name */
|
|
s=AQH_Value_GetNameForSystem(v);
|
|
AQH_ModService_EscapeToBuffer(s, dbuf);
|
|
|
|
GBAA(dbuf, "-%s.png", graphParams->name);
|
|
}
|
|
|
|
|
|
|
|
const MY_GRAPH_PARAMS *_getParamsByName(const char *s)
|
|
{
|
|
const MY_GRAPH_PARAMS *p;
|
|
|
|
p=_graphParams;
|
|
while(p->name) {
|
|
if (strcasecmp(p->name, s)==0)
|
|
return p;
|
|
p++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|