@iotready/nextjs-components-library 1.0.0-preview12 → 1.0.0-preview14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,12 +7,13 @@ type Measure = {
|
|
7
7
|
unit: string;
|
8
8
|
stepped: boolean;
|
9
9
|
};
|
10
|
-
declare const TrendChart: ({ deviceId,
|
10
|
+
declare const TrendChart: ({ deviceId, measures1, measures2, enableDatePicker, handleGetInfluxData, handleExportDataCB, theme }: {
|
11
11
|
deviceId: string;
|
12
|
-
|
13
|
-
|
12
|
+
measures1?: Array<Measure>;
|
13
|
+
measures2?: Array<Measure>;
|
14
14
|
enableDatePicker: boolean;
|
15
|
-
handleGetInfluxData: (measure: string, timeStart: number, timeEnd: number, deviceId: string, timeGroup: string, raw: boolean) => Promise<any>;
|
15
|
+
handleGetInfluxData: (measure: string, timeStart: number, timeEnd: number, deviceId: string, timeGroup: string, raw: boolean, fill?: boolean) => Promise<any>;
|
16
|
+
handleExportDataCB?: (measure: string, timeStart: number, timeEnd: number, deviceId: string) => Promise<any>;
|
16
17
|
theme: Theme;
|
17
18
|
}) => import("react/jsx-runtime").JSX.Element;
|
18
19
|
export default TrendChart;
|
@@ -64,7 +64,19 @@ const lineOptions = {
|
|
64
64
|
day: 'DD MMM YY',
|
65
65
|
hour: 'DD MMM HH:mm',
|
66
66
|
minute: 'HH:mm'
|
67
|
-
}
|
67
|
+
},
|
68
|
+
y1: {
|
69
|
+
type: 'linear',
|
70
|
+
position: 'left',
|
71
|
+
title: { display: false, text: 'Primary Axis' },
|
72
|
+
display: false
|
73
|
+
},
|
74
|
+
y2: {
|
75
|
+
type: 'linear',
|
76
|
+
position: 'right',
|
77
|
+
title: { display: false, text: 'Secondary Axis' },
|
78
|
+
display: false
|
79
|
+
},
|
68
80
|
},
|
69
81
|
title: {
|
70
82
|
display: false,
|
@@ -76,7 +88,6 @@ const lineOptions = {
|
|
76
88
|
drawOnChartArea: false,
|
77
89
|
}
|
78
90
|
},
|
79
|
-
y: {},
|
80
91
|
},
|
81
92
|
};
|
82
93
|
const chartConfigByPeriod = {
|
@@ -175,7 +186,7 @@ function getCsvData(data, measures) {
|
|
175
186
|
return csvData.map(row => row.join(',')).join('\n');
|
176
187
|
}
|
177
188
|
// eslint-disable-next-line no-unused-vars
|
178
|
-
const TrendChart = ({ deviceId,
|
189
|
+
const TrendChart = ({ deviceId, measures1, measures2, enableDatePicker, handleGetInfluxData, handleExportDataCB, theme }) => {
|
179
190
|
const [chartJsLoaded, setChartJsLoaded] = useState(false);
|
180
191
|
const [dataMeasures, setDataMeasures] = useState(null);
|
181
192
|
const [chartPeriod, setChartPeriod] = useState('1D');
|
@@ -191,6 +202,10 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
191
202
|
const csvLinkRef = useRef(null);
|
192
203
|
const [csvData, setCsvData] = useState('');
|
193
204
|
const [spanGapsOption, setSpanGapsOption] = useState(true);
|
205
|
+
const enableExportData = handleExportDataCB ? true : false;
|
206
|
+
const measures = measures1 && measures2 ? [...measures1, ...measures2] :
|
207
|
+
measures1 ? [...measures1] :
|
208
|
+
measures2 ? [...measures2] : [];
|
194
209
|
const [options, setOptions] = useState({
|
195
210
|
...lineOptions,
|
196
211
|
plugins: {
|
@@ -220,8 +235,13 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
220
235
|
...options,
|
221
236
|
scales: {
|
222
237
|
...options.scales,
|
223
|
-
|
224
|
-
...options.scales.
|
238
|
+
y1: {
|
239
|
+
...options.scales.y1,
|
240
|
+
min: 0,
|
241
|
+
max: 1
|
242
|
+
},
|
243
|
+
y2: {
|
244
|
+
...options.scales.y2,
|
225
245
|
min: 0,
|
226
246
|
max: 1
|
227
247
|
}
|
@@ -258,12 +278,14 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
258
278
|
};
|
259
279
|
const handleExportData = async () => {
|
260
280
|
setLoadingButton(true);
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
281
|
+
if (handleExportDataCB) {
|
282
|
+
const data = await Promise.all(measures.map(async (measure) => {
|
283
|
+
return await handleExportDataCB(measure.name, timeStart, timeEnd, deviceId);
|
284
|
+
}));
|
285
|
+
const csvData = getCsvData(data, measures);
|
286
|
+
setCsvData(csvData);
|
287
|
+
setLoadingButton(false);
|
288
|
+
}
|
267
289
|
};
|
268
290
|
useEffect(() => {
|
269
291
|
if (csvData.length > 0) {
|
@@ -272,11 +294,15 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
272
294
|
}, [csvData]);
|
273
295
|
const loadDatasets = async (chartPeriod) => {
|
274
296
|
let intervalInSeconds = chartPeriod === "ALL" && !datePickerUsed && !zoomed ? 31536000 : timeEnd - timeStart;
|
275
|
-
const rawQuery = intervalInSeconds < 86400;
|
276
|
-
//
|
277
|
-
const
|
297
|
+
const rawQuery = intervalInSeconds < 86400;
|
298
|
+
// Combine measures and track their source
|
299
|
+
const allMeasures = [
|
300
|
+
...(measures1?.map(m => ({ ...m, source: 'measures1' })) || []),
|
301
|
+
...(measures2?.map(m => ({ ...m, source: 'measures2' })) || [])
|
302
|
+
];
|
303
|
+
const datasetsPromises = allMeasures.map(async (measure) => {
|
278
304
|
const polltime = getPollTime(intervalInSeconds, measure.polltime || 30);
|
279
|
-
const influxData = await handleGetInfluxData(measure.name, timeStart, timeEnd, deviceId, polltime, !measure.polltime && rawQuery);
|
305
|
+
const influxData = await handleGetInfluxData(measure.name, timeStart, timeEnd, deviceId, polltime, !measure.polltime && rawQuery, !measure.polltime);
|
280
306
|
const points = getChartPoints(influxData);
|
281
307
|
return {
|
282
308
|
label: measure.name,
|
@@ -288,13 +314,14 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
288
314
|
pointHoverBackgroundColor: 'rgba(52, 125, 236, 0.5)',
|
289
315
|
change: !measure.polltime,
|
290
316
|
stepped: measure.stepped,
|
291
|
-
|
317
|
+
yAxisID: measure.source === 'measures1' ? 'y1' : 'y2'
|
292
318
|
};
|
293
319
|
});
|
294
|
-
// Risolvi tutte le promesse per popolare i dataset
|
295
320
|
const datasets = await Promise.all(datasetsPromises);
|
296
|
-
let
|
297
|
-
let
|
321
|
+
let min1 = null;
|
322
|
+
let max1 = null;
|
323
|
+
let min2 = null;
|
324
|
+
let max2 = null;
|
298
325
|
let time;
|
299
326
|
let minTime = null;
|
300
327
|
datasets.forEach(dataset => {
|
@@ -304,37 +331,59 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
304
331
|
}
|
305
332
|
const datasetMin = Math.min(...values);
|
306
333
|
const datasetMax = Math.max(...values);
|
307
|
-
if (
|
308
|
-
|
309
|
-
|
310
|
-
|
334
|
+
if (dataset.yAxisID === 'y1') {
|
335
|
+
if (min1 === null || datasetMin < min1)
|
336
|
+
min1 = datasetMin;
|
337
|
+
if (max1 === null || datasetMax > max1)
|
338
|
+
max1 = datasetMax;
|
339
|
+
}
|
340
|
+
else {
|
341
|
+
if (min2 === null || datasetMin < min2)
|
342
|
+
min2 = datasetMin;
|
343
|
+
if (max2 === null || datasetMax > max2)
|
344
|
+
max2 = datasetMax;
|
345
|
+
}
|
311
346
|
if (time && (minTime === null || time.unix() < minTime))
|
312
347
|
minTime = time.unix() - 86400;
|
313
348
|
});
|
314
|
-
|
315
|
-
|
316
|
-
|
349
|
+
const getPaddedMinMax = (min, max) => {
|
350
|
+
if (min === null || max === null)
|
351
|
+
return { paddedMin: null, paddedMax: null };
|
317
352
|
const diff = ((max - min) * 0.2) < 0.1 ? 0.1 : (max - min) * 0.2;
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
353
|
+
return {
|
354
|
+
paddedMin: Math.floor((min - diff) * 10) / 10,
|
355
|
+
paddedMax: Math.ceil((max + diff) * 10) / 10
|
356
|
+
};
|
357
|
+
};
|
358
|
+
const { paddedMin: paddedMin1, paddedMax: paddedMax1 } = getPaddedMinMax(min1, max1);
|
359
|
+
const { paddedMin: paddedMin2, paddedMax: paddedMax2 } = getPaddedMinMax(min2, max2);
|
360
|
+
setDataMeasures(datasets);
|
322
361
|
setTimeStartPicker(minTime || timeStart);
|
362
|
+
const scalesOptions = {
|
363
|
+
...options.scales,
|
364
|
+
y1: {
|
365
|
+
...options.scales.y1,
|
366
|
+
min: paddedMin1,
|
367
|
+
max: paddedMax1,
|
368
|
+
position: "left",
|
369
|
+
display: "auto"
|
370
|
+
},
|
371
|
+
y2: {
|
372
|
+
...options.scales.y2,
|
373
|
+
min: paddedMin2,
|
374
|
+
max: paddedMax2,
|
375
|
+
position: "right",
|
376
|
+
display: "auto"
|
377
|
+
},
|
378
|
+
x: {
|
379
|
+
...options.scales.x,
|
380
|
+
min: moment.unix(minTime || timeStart).toString(),
|
381
|
+
max: moment.unix(timeEnd).toString()
|
382
|
+
}
|
383
|
+
};
|
323
384
|
setOptions({
|
324
385
|
...options,
|
325
|
-
scales:
|
326
|
-
...options.scales,
|
327
|
-
y: {
|
328
|
-
...options.scales.y,
|
329
|
-
min: paddedMin,
|
330
|
-
max: paddedMax,
|
331
|
-
},
|
332
|
-
x: {
|
333
|
-
...options.scales.x,
|
334
|
-
min: moment.unix(minTime || timeStart).toString(),
|
335
|
-
max: moment.unix(timeEnd).toString()
|
336
|
-
}
|
337
|
-
}
|
386
|
+
scales: scalesOptions
|
338
387
|
});
|
339
388
|
};
|
340
389
|
useEffect(() => {
|
@@ -367,13 +416,13 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
367
416
|
const prevMeasuresValue = prevMeasures.current || [];
|
368
417
|
//@ts-ignore
|
369
418
|
prevMeasures.current = measures;
|
370
|
-
if (!prevMeasuresValue || prevMeasuresValue.length !== measures
|
419
|
+
if (!prevMeasuresValue || prevMeasuresValue.length !== measures?.length || (prevMeasuresValue[0] && prevMeasuresValue[0].name !== measures[0].name)) {
|
371
420
|
setChartLoading(true);
|
372
421
|
}
|
373
422
|
loadDatasets(chartPeriod).then(() => {
|
374
423
|
setChartLoading(false);
|
375
424
|
});
|
376
|
-
}, [
|
425
|
+
}, [measures1, timeEnd, timeStart, measures2]);
|
377
426
|
useEffect(() => {
|
378
427
|
const loadZoomPlugin = async () => {
|
379
428
|
const zoomPlugin = (await import('chartjs-plugin-zoom')).default;
|
@@ -384,8 +433,24 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
384
433
|
const activePoint = chart.tooltip._active[0];
|
385
434
|
const ctx = chart.ctx;
|
386
435
|
const x = activePoint.element.x;
|
387
|
-
|
388
|
-
|
436
|
+
let topY1 = 0;
|
437
|
+
let topY2 = 0;
|
438
|
+
let bottomY1 = 0;
|
439
|
+
let bottomY2 = 0;
|
440
|
+
if (chart.scales.y1?.top) {
|
441
|
+
topY1 = chart.scales.y1.top;
|
442
|
+
}
|
443
|
+
if (chart.scales.y2?.top) {
|
444
|
+
topY2 = chart.scales.y2.top;
|
445
|
+
}
|
446
|
+
if (chart.scales.y1?.bottom) {
|
447
|
+
bottomY1 = chart.scales.y1.bottom;
|
448
|
+
}
|
449
|
+
if (chart.scales.y2?.bottom) {
|
450
|
+
bottomY2 = chart.scales.y2.bottom;
|
451
|
+
}
|
452
|
+
const topY = Math.max(topY1, topY2);
|
453
|
+
const bottomY = Math.min(bottomY1, bottomY2);
|
389
454
|
ctx.save();
|
390
455
|
ctx.beginPath();
|
391
456
|
ctx.moveTo(x, topY);
|
@@ -423,7 +488,7 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
423
488
|
//@ts-ignore
|
424
489
|
, {
|
425
490
|
//@ts-ignore
|
426
|
-
ref: csvLinkRef, data: csvData, filename: `export_${moment().toISOString()}.csv`, separator: ';' })] })), zoomed && (_jsxs(Button, { sx: { minWidth: '40px !important', boxShadow: 1, px: 1 }, variant: "contained", color: "secondary", size: 'small', onClick: () => {
|
491
|
+
ref: csvLinkRef, data: csvData, filename: `export_${moment().toISOString()}.csv`, separator: ';' })] })), zoomed && measures.length > 0 && (_jsxs(Button, { sx: { minWidth: '40px !important', boxShadow: 1, px: 1 }, variant: "contained", color: "secondary", size: 'small', onClick: () => {
|
427
492
|
setChartLoading(true);
|
428
493
|
setZoomed(false);
|
429
494
|
if (chartPeriod === "ALL") {
|
@@ -445,9 +510,11 @@ const TrendChart = ({ deviceId, measures, enableExportData, enableDatePicker, ha
|
|
445
510
|
: moment().subtract(1, 'day').unix());
|
446
511
|
setTimeEnd(moment().unix());
|
447
512
|
}
|
448
|
-
}, children: [_jsx(ZoomOut, { fontSize: 'small' }), _jsx(Box, { sx: { display: { xs: 'none', lg: 'block' }, ml: { md: 1 } }, children: "Reset" })] }))] }), _jsxs(Box, { sx: { display: 'flex', justifyContent: 'flex-end' }, children: [enableDatePicker && _jsx(Box, { sx: { display: { xs: 'none', lg: 'flex' } }, children: datePicker }), _jsxs(ToggleButtonGroup, { color: "primary", value: !datePickerUsed && !zoomed ? chartPeriod : null, exclusive: true, onChange: handleChange, size: "small",
|
449
|
-
|
450
|
-
|
513
|
+
}, children: [_jsx(ZoomOut, { fontSize: 'small' }), _jsx(Box, { sx: { display: { xs: 'none', lg: 'block' }, ml: { md: 1 } }, children: "Reset" })] }))] }), _jsxs(Box, { sx: { display: 'flex', justifyContent: 'flex-end' }, children: [enableDatePicker && _jsx(Box, { sx: { display: { xs: 'none', lg: 'flex' } }, children: datePicker }), _jsxs(ToggleButtonGroup, { color: "primary", value: !datePickerUsed && !zoomed ? chartPeriod : null, exclusive: true, onChange: handleChange, size: "small", sx: {
|
514
|
+
'& .MuiToggleButton-root': {
|
515
|
+
color: 'text.primary', fontSize: '0.95rem', fontWeight: 'normal', paddingTop: '6px', paddingBottom: '6px'
|
516
|
+
}
|
517
|
+
}, disabled: chartLoading, children: [_jsx(ToggleButton, { value: "1D", sx: { px: 1 }, children: "1d" }), _jsx(ToggleButton, { value: "1W", sx: { px: 1 }, children: "1w" }), _jsx(ToggleButton, { value: "1M", sx: { px: 1 }, children: "1M" }), _jsx(ToggleButton, { value: "3M", sx: { px: 1 }, children: "3M" }), _jsx(ToggleButton, { value: "6M", sx: { px: 1 }, children: "6M" }), _jsx(ToggleButton, { value: "1Y", sx: { px: 1 }, children: "1Y" }), _jsx(ToggleButton, { value: "ALL", sx: { px: 1 }, children: "ALL" })] }), _jsx(MuiTooltip, { placement: "top", arrow: true, title: "Connect point values", children: _jsx(ToggleButton, { value: "check", color: "primary", size: "small", selected: spanGapsOption, disabled: chartLoading, onChange: () => handleSpanGaps(!spanGapsOption), sx: { ml: 2 }, children: _jsx(TimelineIcon, {}) }) })] })] }), _jsx(Box, { component: 'div', className: "chart-container", sx: { mt: 2, height: '100%' }, children: chartJsLoaded && !chartLoading && typeof window !== 'undefined' ?
|
451
518
|
_jsx(_Fragment, { children: dataMeasures && (dataMeasures.length > 1 || (dataMeasures.length === 1 && dataMeasures[0].data?.length)) ?
|
452
519
|
(_jsx(Line, { options: options, data: {
|
453
520
|
// datasets: dataMeasures || [{ data: [] }]
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@iotready/nextjs-components-library",
|
3
|
-
"version": "1.0.0-
|
3
|
+
"version": "1.0.0-preview14",
|
4
4
|
"main": "index.js",
|
5
5
|
"scripts": {
|
6
6
|
"build": "rm -rf dist && tsc --project tsconfig.build.json && cp package.json dist/",
|
@@ -19,6 +19,7 @@
|
|
19
19
|
"chartjs-adapter-moment": "^1.0.1",
|
20
20
|
"chartjs-plugin-annotation": "^3.1.0",
|
21
21
|
"chartjs-plugin-zoom": "^2.0.1",
|
22
|
+
"csv-parse": "^5.6.0",
|
22
23
|
"firebase": "^10.13.1",
|
23
24
|
"leaflet": "^1.9.4",
|
24
25
|
"leaflet-defaulticon-compatibility": "^0.1.2",
|
@@ -8,6 +8,6 @@ export type InfluxConfig = {
|
|
8
8
|
username: string;
|
9
9
|
password: string;
|
10
10
|
};
|
11
|
-
export declare function
|
12
|
-
export declare function
|
13
|
-
export declare function
|
11
|
+
export declare function getInfluxAlarms(influxConfig: InfluxConfig, fields: string[], limit: number, offset: number, sort: any, deviceID: string, timeStart: number, timeEnd: number): Promise<any>;
|
12
|
+
export declare function getInfluxDataV1(influxConfig: InfluxConfig, field: string, timeStart: number, timeEnd: number, deviceID: string, timeGroup: string, raw: boolean, fill?: boolean): Promise<any>;
|
13
|
+
export declare function exportDataV1(influxConfig: InfluxConfig, field: string, timeStart: number, timeEnd: number, deviceID: string): Promise<any>;
|
package/server-actions/influx.js
CHANGED
@@ -1,44 +1,83 @@
|
|
1
1
|
"use server";
|
2
|
+
import { parse } from "csv-parse";
|
2
3
|
import moment from "moment";
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
4
|
+
export async function getInfluxAlarms(influxConfig, fields, limit, offset, sort, deviceID, timeStart, timeEnd) {
|
5
|
+
const conditions = fields
|
6
|
+
.map((field) => `r["valueName"] == "${field}"`)
|
7
|
+
.join(" or ");
|
8
|
+
let query = `
|
9
|
+
import "contrib/tomhollingworth/events"
|
10
|
+
from(bucket: "${influxConfig.bucket}")`;
|
11
|
+
if (timeStart && timeEnd) {
|
12
|
+
query += ` |> range(start: ${timeStart}, stop: ${timeEnd})`;
|
13
|
+
}
|
14
|
+
query += `
|
15
|
+
|> filter(fn: (r) => r["_measurement"] == "${influxConfig.measurement}" and r["deviceid"] == "${deviceID}" and (${conditions}))
|
16
|
+
`;
|
17
|
+
const queryCount = `${query} |> group() |> count()`;
|
18
|
+
query += `
|
19
|
+
|> sort(columns: ["_time"]) // Ordina gli eventi cronologicamente
|
20
|
+
|> group(columns: ["valueName"]) // Raggruppa per il tag
|
21
|
+
|> events.duration(unit: 1s, stop: 2020-01-02T00:00:00Z)
|
22
|
+
|> keep(columns: ["_time", "valueName", "_value", "duration"])
|
23
|
+
|> group() // Raggruppa tutti i dati in un unico gruppo
|
12
24
|
`;
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
25
|
+
if (sort && sort.field === "time" && sort.sort === "desc") {
|
26
|
+
query += ` |> sort(columns: ["_time"], desc: true)`;
|
27
|
+
}
|
28
|
+
else {
|
29
|
+
query += ` |> sort(columns: ["_time"])`;
|
30
|
+
}
|
31
|
+
query += ` |> limit(n:${limit}, offset:${offset})`;
|
32
|
+
const responses = await Promise.all([
|
33
|
+
fetch(encodeURI(`${influxConfig.url}/api/v2/query?org=${influxConfig.orgId}`), {
|
34
|
+
method: "POST",
|
35
|
+
headers: {
|
36
|
+
Authorization: `Token ${influxConfig.accessToken}`,
|
37
|
+
"Content-Type": "application/json"
|
38
|
+
},
|
39
|
+
body: JSON.stringify({
|
40
|
+
query: query,
|
41
|
+
type: "flux"
|
42
|
+
})
|
43
|
+
}),
|
44
|
+
fetch(encodeURI(`${influxConfig.url}/api/v2/query?org=${influxConfig.orgId}`), {
|
45
|
+
method: "POST",
|
46
|
+
headers: {
|
47
|
+
Authorization: `Token ${influxConfig.accessToken}`,
|
48
|
+
"Content-Type": "application/json"
|
49
|
+
},
|
50
|
+
body: JSON.stringify({
|
51
|
+
query: queryCount,
|
52
|
+
type: "flux"
|
53
|
+
})
|
54
|
+
})
|
55
|
+
]);
|
56
|
+
if (!responses[0].ok) {
|
57
|
+
throw new Error(`Failed to fetch data from InfluxDB: ${responses[0].statusText}`);
|
36
58
|
}
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
59
|
+
const data = await responses[0].text();
|
60
|
+
const count = await responses[1].text();
|
61
|
+
const rows = [];
|
62
|
+
const parsedData = parse(data.trim(), { columns: true });
|
63
|
+
parsedData.forEach((row) => {
|
64
|
+
rows.push({
|
65
|
+
time: row["_time"],
|
66
|
+
duration: parseInt(row["duration"], 10),
|
67
|
+
value: parseInt(row["_value"], 10),
|
68
|
+
valueName: row["valueName"]
|
69
|
+
});
|
70
|
+
});
|
71
|
+
const parsedCount = count.split("\n")[1].split(",");
|
72
|
+
let countData = parsedCount[5];
|
73
|
+
return {
|
74
|
+
data: rows,
|
75
|
+
count: countData
|
76
|
+
};
|
77
|
+
}
|
78
|
+
export async function getInfluxDataV1(influxConfig, field, timeStart, timeEnd, deviceID, timeGroup, raw, fill) {
|
41
79
|
let query;
|
80
|
+
let preStartValue = null;
|
42
81
|
if (raw) {
|
43
82
|
query = `SELECT ("value") FROM "${influxConfig.measurement}" WHERE "deviceid" = '${deviceID}' AND "valueName" = '${field}' AND time >= '${moment
|
44
83
|
.unix(timeStart)
|
@@ -49,7 +88,29 @@ export async function getInfluxDataV1(influxConfig, field, timeStart, timeEnd, d
|
|
49
88
|
.unix(timeStart)
|
50
89
|
.toISOString()}' AND time <= '${moment
|
51
90
|
.unix(timeEnd)
|
52
|
-
.toISOString()}' GROUP BY time(${timeGroup})
|
91
|
+
.toISOString()}' GROUP BY time(${timeGroup})`;
|
92
|
+
if (fill) {
|
93
|
+
query += ` fill(none)`;
|
94
|
+
}
|
95
|
+
else {
|
96
|
+
query += ` fill(null)`;
|
97
|
+
}
|
98
|
+
}
|
99
|
+
if (fill) {
|
100
|
+
// Query to get the last data point before timeStart
|
101
|
+
const preStartQuery = `SELECT last("value") FROM "${influxConfig.measurement}" WHERE "deviceid" = '${deviceID}' AND "valueName" = '${field}' AND time < '${moment
|
102
|
+
.unix(timeStart)
|
103
|
+
.toISOString()}'`;
|
104
|
+
const preStartResponse = await fetch(encodeURI(`${influxConfig.url}/query?db=${influxConfig.dbName}&epoch=s&q=${preStartQuery}`), {
|
105
|
+
headers: {
|
106
|
+
Authorization: `Basic ${btoa(`${influxConfig.username}:${influxConfig.password}`)}`
|
107
|
+
}
|
108
|
+
});
|
109
|
+
if (!preStartResponse.ok) {
|
110
|
+
throw new Error(`Failed to fetch pre-start data from InfluxDB: ${preStartResponse.statusText}`);
|
111
|
+
}
|
112
|
+
const preStartData = await preStartResponse.json();
|
113
|
+
preStartValue = preStartData.results[0].series?.[0]?.values?.[0]?.[1];
|
53
114
|
}
|
54
115
|
const response = await fetch(encodeURI(`${influxConfig.url}/query?db=${influxConfig.dbName}&epoch=s&q=${query}`), {
|
55
116
|
headers: {
|
@@ -57,7 +118,6 @@ export async function getInfluxDataV1(influxConfig, field, timeStart, timeEnd, d
|
|
57
118
|
}
|
58
119
|
});
|
59
120
|
if (!response.ok) {
|
60
|
-
console.log(response);
|
61
121
|
throw new Error(`Failed to fetch data from InfluxDB: ${response.statusText}`);
|
62
122
|
}
|
63
123
|
const data = await response.json();
|
@@ -72,61 +132,34 @@ export async function getInfluxDataV1(influxConfig, field, timeStart, timeEnd, d
|
|
72
132
|
}
|
73
133
|
];
|
74
134
|
}
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
.join(" OR ");
|
88
|
-
let queryCount = `SELECT count(*) FROM "${influxConfig.measurement}" WHERE "deviceid" = '${deviceID}' AND (${conditions})`;
|
89
|
-
let queryPagination = `SELECT "valueName", "value" FROM "${influxConfig.measurement}" WHERE "deviceid" = '${deviceID}' AND (${conditions})`;
|
90
|
-
if (timeStart) {
|
91
|
-
queryCount += ` AND time >= '${moment.unix(timeStart).toISOString()}'`;
|
92
|
-
queryPagination += ` AND time >= '${moment
|
93
|
-
.unix(timeStart)
|
94
|
-
.toISOString()}'`;
|
95
|
-
}
|
96
|
-
if (timeEnd) {
|
97
|
-
queryCount += ` AND time <= '${moment.unix(timeEnd).toISOString()}'`;
|
98
|
-
queryPagination += ` AND time <= '${moment.unix(timeEnd).toISOString()}'`;
|
99
|
-
}
|
100
|
-
if (sort && sort.field === "time") {
|
101
|
-
queryPagination = `${queryPagination} ORDER BY "${sort.field}" ${sort.sort}`;
|
102
|
-
}
|
103
|
-
queryPagination = `${queryPagination} LIMIT ${limit} OFFSET ${offset}`;
|
104
|
-
const responses = await Promise.all([
|
105
|
-
fetch(encodeURI(`${influxConfig.url}/query?db=${influxConfig.dbName}&epoch=s&q=${queryPagination}`), {
|
106
|
-
headers: {
|
107
|
-
Authorization: `Basic ${btoa(`${influxConfig.username}:${influxConfig.password}`)}`
|
108
|
-
}
|
109
|
-
}),
|
110
|
-
fetch(encodeURI(`${influxConfig.url}/query?db=${influxConfig.dbName}&epoch=s&q=${queryCount}`), {
|
111
|
-
headers: {
|
112
|
-
Authorization: `Basic ${btoa(`${influxConfig.username}:${influxConfig.password}`)}`
|
113
|
-
}
|
114
|
-
})
|
115
|
-
]);
|
116
|
-
if (!responses[0].ok) {
|
117
|
-
throw new Error(`Failed to fetch data from InfluxDB: ${responses[0].statusText}`);
|
135
|
+
// Always override the name to be the field
|
136
|
+
data.results[0].series.forEach((series) => {
|
137
|
+
series.name = field; // Force the series name to be the field name
|
138
|
+
});
|
139
|
+
// 1000000 REMOVED AND ADDED TO MOVE THE POINT AWAY IN THE CHART
|
140
|
+
if (fill) {
|
141
|
+
if (preStartValue !== null && preStartValue !== undefined) {
|
142
|
+
// Insert the pre-start value at the beginning of the dataset
|
143
|
+
data.results[0].series[0].values.unshift([
|
144
|
+
timeStart - 1000000,
|
145
|
+
preStartValue
|
146
|
+
]);
|
118
147
|
}
|
119
|
-
|
120
|
-
const
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
148
|
+
// Set the last point as the timeEnd and last value of the dataset
|
149
|
+
const lastSeries = data.results[0].series[0];
|
150
|
+
const lastValue = lastSeries?.values &&
|
151
|
+
lastSeries.values.length > 0 &&
|
152
|
+
lastSeries.values.slice(-1)[0].length > 1
|
153
|
+
? lastSeries.values.slice(-1)[0][1]
|
154
|
+
: null;
|
155
|
+
data.results[0].series[0].values.push([timeEnd + 1000000, lastValue]);
|
125
156
|
}
|
157
|
+
return data;
|
126
158
|
}
|
127
|
-
export async function
|
128
|
-
|
129
|
-
|
159
|
+
export async function exportDataV1(influxConfig, field, timeStart, timeEnd, deviceID) {
|
160
|
+
const query = `SELECT ("value") FROM "${influxConfig.measurement}" WHERE "deviceid" = '${deviceID}' AND "valueName" = '${field}' AND time >= '${moment
|
161
|
+
.unix(timeStart)
|
162
|
+
.toISOString()}' AND time <= '${moment.unix(timeEnd).toISOString()}'`;
|
130
163
|
const response = await fetch(encodeURI(`${influxConfig.url}/query?db=${influxConfig.dbName}&epoch=s&q=${query}`), {
|
131
164
|
headers: {
|
132
165
|
Authorization: `Basic ${btoa(`${influxConfig.username}:${influxConfig.password}`)}`
|
@@ -136,10 +169,22 @@ export async function getFirstTimestamp(influxConfig, deviceID) {
|
|
136
169
|
throw new Error(`Failed to fetch data from InfluxDB: ${response.statusText}`);
|
137
170
|
}
|
138
171
|
const data = await response.json();
|
139
|
-
//
|
140
|
-
if (data
|
141
|
-
//
|
142
|
-
|
172
|
+
// Ensure the name is manually set to the field
|
173
|
+
if (!data.results[0].series) {
|
174
|
+
// Set default value with null time and null value
|
175
|
+
data.results[0].series = [
|
176
|
+
{
|
177
|
+
name: field, // Manually set the series name as the field
|
178
|
+
columns: ["time", "value"],
|
179
|
+
values: [] // Set null point for time and value
|
180
|
+
}
|
181
|
+
];
|
182
|
+
}
|
183
|
+
else {
|
184
|
+
// Always override the name to be the field
|
185
|
+
data.results[0].series.forEach((series) => {
|
186
|
+
series.name = field; // Force the series name to be the field name
|
187
|
+
});
|
143
188
|
}
|
144
|
-
return
|
189
|
+
return data;
|
145
190
|
}
|