@frangoteam/fuxa-min 1.2.10 → 1.3.0
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.
- package/api/auth/index.js +141 -3
- package/api/command/index.js +10 -4
- package/api/diagnose/index.js +12 -4
- package/api/index.js +41 -8
- package/api/jwt-helper.js +15 -2
- package/api/path-helper.js +41 -0
- package/api/projects/index.js +27 -14
- package/api/reports/reports.service.ts +12 -2
- package/api/resources/index.js +30 -9
- package/api/scheduler/index.js +21 -1
- package/dist/3rdpartylicenses.txt +139 -7
- package/dist/assets/i18n/de.json +10 -0
- package/dist/assets/i18n/en.json +17 -3
- package/dist/assets/i18n/es.json +12 -0
- package/dist/assets/i18n/fr.json +10 -0
- package/dist/assets/i18n/ja.json +15 -6
- package/dist/assets/i18n/ko.json +12 -0
- package/dist/assets/i18n/pt.json +9 -2
- package/dist/assets/i18n/ru.json +11 -0
- package/dist/assets/i18n/sv.json +10 -1
- package/dist/assets/i18n/tr.json +8 -1
- package/dist/assets/i18n/ua.json +9 -2
- package/dist/assets/i18n/zh-cn.json +10 -0
- package/dist/assets/i18n/zh-tw.json +11 -1
- package/dist/index.html +2 -2
- package/dist/main.bafae830903d548e.js +329 -0
- package/dist/polyfills.d7de05f9af2fb559.js +1 -0
- package/dist/reports.service.js +11 -1
- package/dist/{runtime.8ef63094e52a66ba.js → runtime.9136a61a9b98f987.js} +1 -1
- package/dist/{scripts.40b60f02658462e4.js → scripts.d9e6ee984bf6f3b7.js} +1 -1
- package/dist/styles.545e37beb3e671ba.css +1 -0
- package/integrations/node-red/index.js +91 -24
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-daq.html +56 -5
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-daq.js +8 -2
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag-change.html +56 -5
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag-change.js +12 -12
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag-daq-settings.html +56 -5
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag-daq-settings.js +14 -10
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag.html +56 -5
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag.js +8 -2
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-set-tag-daq-settings.html +56 -5
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-set-tag-daq-settings.js +24 -20
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-set-tag.html +56 -5
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-set-tag.js +8 -2
- package/main.js +41 -17
- package/package.json +10 -5
- package/runtime/devices/adsclient/index.js +1 -1
- package/runtime/devices/bacnet/index.js +66 -32
- package/runtime/devices/ethernetip/index.js +1 -1
- package/runtime/devices/gpio/index.js +1 -1
- package/runtime/devices/odbc/index.js +5 -5
- package/runtime/devices/template/index.js +14 -14
- package/runtime/storage/daqstorage.js +28 -2
- package/runtime/storage/influxdb/index.js +1 -1
- package/runtime/storage/questdb/index.js +224 -0
- package/runtime/utils.js +5 -0
- package/settings.default.js +13 -3
- package/dist/main.020ca34630a3363a.js +0 -329
- package/dist/polyfills.c8e7db9850a3ad8b.js +0 -1
- package/dist/styles.03cc550382689976.css +0 -1
|
@@ -24,7 +24,7 @@ function ODBCclient(_data, _logger, _events) {
|
|
|
24
24
|
* initialize the device type
|
|
25
25
|
*/
|
|
26
26
|
this.init = function (_type) {
|
|
27
|
-
console.error('Not supported!');
|
|
27
|
+
console.error('Not supported! (odbc.init)');
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/**
|
|
@@ -165,14 +165,14 @@ function ODBCclient(_data, _logger, _events) {
|
|
|
165
165
|
* Return Tags values array { id: <name>, value: <value> }
|
|
166
166
|
*/
|
|
167
167
|
this.getValues = function () {
|
|
168
|
-
console.error('Not supported!');
|
|
168
|
+
console.error('Not supported! (odbc.getValues)');
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
/**
|
|
172
172
|
* Return Tag value { id: <name>, value: <value>, ts: <lastTimestampValue> }
|
|
173
173
|
*/
|
|
174
174
|
this.getValue = function (tagid) {
|
|
175
|
-
console.error(
|
|
175
|
+
console.error(`Not supported! (odbc.getValue) (TagID: ${tagid})`);
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
/**
|
|
@@ -186,14 +186,14 @@ function ODBCclient(_data, _logger, _events) {
|
|
|
186
186
|
* Return Tag property to show in frontend
|
|
187
187
|
*/
|
|
188
188
|
this.getTagProperty = function (tagid) {
|
|
189
|
-
console.error(
|
|
189
|
+
console.error(`Not supported! (odbc.getTagProperty) (TagID: ${tagid})`);
|
|
190
190
|
}
|
|
191
191
|
|
|
192
192
|
/**
|
|
193
193
|
* Set the Tag value to device
|
|
194
194
|
*/
|
|
195
195
|
this.setValue = function (tagid, value) {
|
|
196
|
-
console.error(
|
|
196
|
+
console.error(`Not supported! (odbc.setValue) (TagID: ${tagid}, Value: ${value})`);
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
/**
|
|
@@ -14,7 +14,7 @@ function DeviceTemplate(_data, _logger, _events) {
|
|
|
14
14
|
* initialize the device type
|
|
15
15
|
*/
|
|
16
16
|
this.init = function (_type) {
|
|
17
|
-
console.error('Not supported!');
|
|
17
|
+
console.error('Not supported! (template.init)');
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -22,7 +22,7 @@ function DeviceTemplate(_data, _logger, _events) {
|
|
|
22
22
|
* Emit connection status to clients, clear all Tags values
|
|
23
23
|
*/
|
|
24
24
|
this.connect = function () {
|
|
25
|
-
console.error('Not supported!');
|
|
25
|
+
console.error('Not supported! (template.connect)');
|
|
26
26
|
// events.emit('device-status:changed', { id: data.id, status: 'connect-ok' });
|
|
27
27
|
// events.emit('device-status:changed', { id: data.id, status: 'connect-error' });
|
|
28
28
|
}
|
|
@@ -33,7 +33,7 @@ function DeviceTemplate(_data, _logger, _events) {
|
|
|
33
33
|
* Emit connection status to clients, clear all Tags values
|
|
34
34
|
*/
|
|
35
35
|
this.disconnect = function () {
|
|
36
|
-
console.error('Not supported!');
|
|
36
|
+
console.error('Not supported! (template.disconnect)');
|
|
37
37
|
// events.emit('device-status:changed', { id: data.id, status: 'connect-off' });
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -42,7 +42,7 @@ function DeviceTemplate(_data, _logger, _events) {
|
|
|
42
42
|
* Update the tags values list, save in DAQ if value changed or in interval and emit values to clients
|
|
43
43
|
*/
|
|
44
44
|
this.polling = async function () {
|
|
45
|
-
console.error('Not supported!');
|
|
45
|
+
console.error('Not supported! (template.polling)');
|
|
46
46
|
// events.emit('device-value:changed', { id: data.name, values: values });
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -50,49 +50,49 @@ function DeviceTemplate(_data, _logger, _events) {
|
|
|
50
50
|
* Load Tags attribute to read with polling
|
|
51
51
|
*/
|
|
52
52
|
this.load = function (_data) {
|
|
53
|
-
console.error(
|
|
53
|
+
console.error(`Not supported! (template.load) (Data: ${_data})`);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* Return Tags values array { id: <name>, value: <value> }
|
|
58
58
|
*/
|
|
59
59
|
this.getValues = function () {
|
|
60
|
-
console.error('Not supported!');
|
|
60
|
+
console.error('Not supported! (template.getValues)');
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
/**
|
|
64
64
|
* Return Tag value { id: <name>, value: <value>, ts: <lastTimestampValue> }
|
|
65
65
|
*/
|
|
66
66
|
this.getValue = function (tagid) {
|
|
67
|
-
console.error(
|
|
67
|
+
console.error(`Not supported! (template.getValue) (TagID: ${tagid})`);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
71
|
* Return connection status 'connect-off', 'connect-ok', 'connect-error', 'connect-busy'
|
|
72
72
|
*/
|
|
73
73
|
this.getStatus = function () {
|
|
74
|
-
console.error('Not supported!');
|
|
74
|
+
console.error('Not supported! (template.getStatus)');
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
/**
|
|
78
78
|
* Return Tag property to show in frontend
|
|
79
79
|
*/
|
|
80
80
|
this.getTagProperty = function (tagid) {
|
|
81
|
-
console.error(
|
|
81
|
+
console.error(`Not supported! (template.getTagProperty) (TagID: ${tagid})`);
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
85
|
* Set the Tag value to device
|
|
86
86
|
*/
|
|
87
87
|
this.setValue = function (tagid, value) {
|
|
88
|
-
console.error(
|
|
88
|
+
console.error(`Not supported! (template.setValue) (TagID: ${tagid}, Value: ${value})`);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
92
|
* Return if device is connected
|
|
93
93
|
*/
|
|
94
94
|
this.isConnected = function () {
|
|
95
|
-
console.error('Not supported!');
|
|
95
|
+
console.error('Not supported! (template.isConnected)');
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
/**
|
|
@@ -107,7 +107,7 @@ function DeviceTemplate(_data, _logger, _events) {
|
|
|
107
107
|
* @returns
|
|
108
108
|
*/
|
|
109
109
|
this.lastReadTimestamp = () => {
|
|
110
|
-
console.error('Not supported!');
|
|
110
|
+
console.error('Not supported! (template.lastReadTimestamp)');
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/**
|
|
@@ -115,7 +115,7 @@ function DeviceTemplate(_data, _logger, _events) {
|
|
|
115
115
|
* @returns
|
|
116
116
|
*/
|
|
117
117
|
this.getTagDaqSettings = (tagId) => {
|
|
118
|
-
console.error(
|
|
118
|
+
console.error(`Not supported! (template.getTagDaqSettings) (TagID: ${tagId})`);
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
/**
|
|
@@ -123,7 +123,7 @@ function DeviceTemplate(_data, _logger, _events) {
|
|
|
123
123
|
* @returns
|
|
124
124
|
*/
|
|
125
125
|
this.setTagDaqSettings = (tagId, settings) => {
|
|
126
|
-
console.error(
|
|
126
|
+
console.error(`Not supported! (template.setTagDaqSettings) (TagID: ${tagId}, Settings: ${settings})`);
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
|
|
@@ -21,6 +21,7 @@ var logger;
|
|
|
21
21
|
var daqDB = {}; // list of daqDB node: SQlite one pro device, influxDB only one
|
|
22
22
|
var currentStorateDB;
|
|
23
23
|
var runtime;
|
|
24
|
+
var questDbModule = null;
|
|
24
25
|
|
|
25
26
|
function init(_settings, _log, _runtime) {
|
|
26
27
|
settings = _settings;
|
|
@@ -42,7 +43,7 @@ function reset() {
|
|
|
42
43
|
function addDaqNode(_id, fncgetprop) {
|
|
43
44
|
var id = _id;
|
|
44
45
|
const dbType = _getDbType();
|
|
45
|
-
if (dbType === DaqStoreTypeEnum.influxDB || dbType === DaqStoreTypeEnum.influxDB18 || dbType === DaqStoreTypeEnum.TDengine) {
|
|
46
|
+
if (dbType === DaqStoreTypeEnum.influxDB || dbType === DaqStoreTypeEnum.influxDB18 || dbType === DaqStoreTypeEnum.TDengine || dbType === DaqStoreTypeEnum.questDB) {
|
|
46
47
|
id = dbType;
|
|
47
48
|
}
|
|
48
49
|
if (!daqDB[id]) {
|
|
@@ -50,6 +51,14 @@ function addDaqNode(_id, fncgetprop) {
|
|
|
50
51
|
daqDB[id] = InfluxDB.create(settings, logger, currentStorateDB);
|
|
51
52
|
} else if(id === DaqStoreTypeEnum.TDengine){
|
|
52
53
|
daqDB[id] = TDengine.create(settings, logger, currentStorateDB);
|
|
54
|
+
} else if(id === DaqStoreTypeEnum.questDB){
|
|
55
|
+
const QuestDB = _getQuestDbModule();
|
|
56
|
+
if (QuestDB) {
|
|
57
|
+
daqDB[id] = QuestDB.create(settings, logger, currentStorateDB);
|
|
58
|
+
} else {
|
|
59
|
+
logger.warn('daqstorage: QuestDB storage selected but package dependencies are not installed. Falling back to SQLite.');
|
|
60
|
+
daqDB[id] = SqliteDB.create(settings, logger, id, currentStorateDB);
|
|
61
|
+
}
|
|
53
62
|
} else {
|
|
54
63
|
daqDB[id] = SqliteDB.create(settings, logger, id, currentStorateDB);
|
|
55
64
|
}
|
|
@@ -160,16 +169,33 @@ function _getDaqNode(tagid) {
|
|
|
160
169
|
|
|
161
170
|
function _getDbType() {
|
|
162
171
|
if (settings.daqstore && settings.daqstore.type) {
|
|
172
|
+
if (settings.daqstore.type === 'QuestDB') {
|
|
173
|
+
return DaqStoreTypeEnum.questDB;
|
|
174
|
+
}
|
|
163
175
|
return settings.daqstore.type;
|
|
164
176
|
}
|
|
165
177
|
return DaqStoreTypeEnum.SQlite;
|
|
166
178
|
}
|
|
167
179
|
|
|
180
|
+
function _getQuestDbModule() {
|
|
181
|
+
if (questDbModule) {
|
|
182
|
+
return questDbModule;
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
questDbModule = require("./questdb");
|
|
186
|
+
} catch (err) {
|
|
187
|
+
questDbModule = null;
|
|
188
|
+
logger.warn(`daqstorage: QuestDB module unavailable (${err.message})`);
|
|
189
|
+
}
|
|
190
|
+
return questDbModule;
|
|
191
|
+
}
|
|
192
|
+
|
|
168
193
|
var DaqStoreTypeEnum = {
|
|
169
194
|
SQlite: 'SQlite',
|
|
170
195
|
influxDB: 'influxDB',
|
|
171
196
|
influxDB18: 'influxDB18',
|
|
172
197
|
TDengine: 'TDengine',
|
|
198
|
+
questDB: 'questDB',
|
|
173
199
|
}
|
|
174
200
|
|
|
175
201
|
function _getValue(value) {
|
|
@@ -187,4 +213,4 @@ module.exports = {
|
|
|
187
213
|
getNodesValues: getNodesValues,
|
|
188
214
|
checkRetention: checkRetention,
|
|
189
215
|
getCurrentStorageFnc: getCurrentStorageFnc,
|
|
190
|
-
};
|
|
216
|
+
};
|
|
@@ -123,7 +123,7 @@ function Influx(_settings, _log, _currentStorate) {
|
|
|
123
123
|
} else {
|
|
124
124
|
const point = new Point(tag.id)
|
|
125
125
|
.tag('id', tag.id)
|
|
126
|
-
.tag('name', tag.name || tag.tagref
|
|
126
|
+
.tag('name', tag.name || tag.tagref?.name)
|
|
127
127
|
.tag('type', tag.type)
|
|
128
128
|
.timestamp(new Date(tag.timestamp || new Date().getTime()));
|
|
129
129
|
if (deviceName) {
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { Pool } = require('pg');
|
|
4
|
+
const { Sender } = require('@questdb/nodejs-client');
|
|
5
|
+
let utils = require('../../utils');
|
|
6
|
+
|
|
7
|
+
function QuestDB(_settings, _log, _currentStorage) {
|
|
8
|
+
let settings = _settings; // Application settings
|
|
9
|
+
const logger = _log; // Application logger
|
|
10
|
+
const currentStorage = _currentStorage; // Database to set the last value (current)
|
|
11
|
+
let pool = null;
|
|
12
|
+
let sender = null;
|
|
13
|
+
const tableName = getTableName();
|
|
14
|
+
let writeQueue = Promise.resolve();
|
|
15
|
+
let initPromise = Promise.resolve();
|
|
16
|
+
|
|
17
|
+
this.setCall = function (_fncGetProp) {
|
|
18
|
+
fncGetTagProp = _fncGetProp;
|
|
19
|
+
return this.addDaqValues;
|
|
20
|
+
}
|
|
21
|
+
var fncGetTagProp = null;
|
|
22
|
+
|
|
23
|
+
this.init = async function () {
|
|
24
|
+
try {
|
|
25
|
+
pool = new Pool(getQueryClientConfig());
|
|
26
|
+
sender = await Sender.fromConfig(getIngestConfigString());
|
|
27
|
+
await ensureSchema();
|
|
28
|
+
logger.info('QuestDB connected');
|
|
29
|
+
} catch (error) {
|
|
30
|
+
logger.error(`questdb-init failed! ${error}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
this.addDaqValues = function (tagsValues, deviceName, deviceId) {
|
|
35
|
+
var dataToRestore = [];
|
|
36
|
+
var rowsToWrite = [];
|
|
37
|
+
|
|
38
|
+
for (const tagid in tagsValues) {
|
|
39
|
+
let tag = tagsValues[tagid];
|
|
40
|
+
if (!tag.daq || utils.isNullOrUndefined(tag.value) || Number.isNaN(tag.value)) {
|
|
41
|
+
if (tag.daq && tag.daq.restored) {
|
|
42
|
+
dataToRestore.push({ id: tag.id, deviceId: deviceId, value: tag.value });
|
|
43
|
+
}
|
|
44
|
+
if (tag.daq && !tag.daq.enabled) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
rowsToWrite.push({
|
|
50
|
+
tagid,
|
|
51
|
+
deviceId,
|
|
52
|
+
deviceName: deviceName || '',
|
|
53
|
+
unsPath: normalizeUnsPath(tag.unsPath),
|
|
54
|
+
value: tag.value,
|
|
55
|
+
timestamp: tag.timestamp || Date.now(),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (rowsToWrite.length) {
|
|
60
|
+
writeQueue = writeQueue.then(async () => {
|
|
61
|
+
await initPromise;
|
|
62
|
+
if (!sender) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const row of rowsToWrite) {
|
|
67
|
+
const parsedValue = normalizeValue(row.value);
|
|
68
|
+
let line = sender
|
|
69
|
+
.table(tableName)
|
|
70
|
+
.symbol('tag_id', row.tagid)
|
|
71
|
+
.symbol('device_id', row.deviceId)
|
|
72
|
+
.stringColumn('device_name', row.deviceName);
|
|
73
|
+
if (!utils.isNullOrUndefined(row.unsPath)) {
|
|
74
|
+
line = line.stringColumn('uns_path', row.unsPath);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!utils.isNullOrUndefined(parsedValue.numberValue)) {
|
|
78
|
+
line = line.floatColumn('number_value', parsedValue.numberValue);
|
|
79
|
+
}
|
|
80
|
+
if (!utils.isNullOrUndefined(parsedValue.stringValue)) {
|
|
81
|
+
line = line.stringColumn('string_value', parsedValue.stringValue);
|
|
82
|
+
}
|
|
83
|
+
await line.at(Number(row.timestamp), 'ms');
|
|
84
|
+
}
|
|
85
|
+
await sender.flush();
|
|
86
|
+
}).catch((error) => {
|
|
87
|
+
logger.error(`questdb-addDaqValues failed! ${error}`);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (dataToRestore.length && currentStorage) {
|
|
92
|
+
currentStorage.setValues(dataToRestore);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
this.getDaqValue = function (tagid, fromts, tots) {
|
|
97
|
+
return new Promise(function (resolve, reject) {
|
|
98
|
+
initPromise.then(() => {
|
|
99
|
+
if (!pool) {
|
|
100
|
+
resolve([]);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const query = `SELECT timestamp, number_value, string_value
|
|
105
|
+
FROM ${tableName}
|
|
106
|
+
WHERE tag_id = $1
|
|
107
|
+
AND timestamp >= $2
|
|
108
|
+
AND timestamp < $3
|
|
109
|
+
ORDER BY timestamp`;
|
|
110
|
+
const params = [tagid, new Date(fromts), new Date(tots)];
|
|
111
|
+
|
|
112
|
+
pool.query(query, params)
|
|
113
|
+
.then((result) => {
|
|
114
|
+
let data = [];
|
|
115
|
+
result.rows.forEach((row) => {
|
|
116
|
+
const value = !utils.isNullOrUndefined(row.number_value) ? Number(row.number_value) : row.string_value;
|
|
117
|
+
data.push({ dt: new Date(row.timestamp).getTime(), value });
|
|
118
|
+
});
|
|
119
|
+
resolve(data)
|
|
120
|
+
})
|
|
121
|
+
.catch((error) => {
|
|
122
|
+
logger.error(`questdb-getDaqValue failed! ${error}`)
|
|
123
|
+
reject(error)
|
|
124
|
+
})
|
|
125
|
+
}).catch((error) => {
|
|
126
|
+
logger.error(`questdb-getDaqValue failed! ${error}`)
|
|
127
|
+
reject(error)
|
|
128
|
+
});
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
this.close = function () {
|
|
133
|
+
if (sender) {
|
|
134
|
+
sender.close().catch((error) => {
|
|
135
|
+
logger.error(`questdb-close sender failed! ${error}`);
|
|
136
|
+
});
|
|
137
|
+
sender = null;
|
|
138
|
+
}
|
|
139
|
+
if (pool) {
|
|
140
|
+
pool.end().catch((error) => {
|
|
141
|
+
logger.error(`questdb-close pool failed! ${error}`);
|
|
142
|
+
});
|
|
143
|
+
pool = null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
this.getDaqMap = function (tagid) {
|
|
148
|
+
var dummy = {};
|
|
149
|
+
dummy[tagid] = true;
|
|
150
|
+
return dummy;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function ensureSchema() {
|
|
154
|
+
if (!pool) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
await pool.query(`CREATE TABLE IF NOT EXISTS ${tableName} (
|
|
158
|
+
timestamp TIMESTAMP,
|
|
159
|
+
tag_id SYMBOL,
|
|
160
|
+
number_value FLOAT,
|
|
161
|
+
string_value STRING,
|
|
162
|
+
device_id SYMBOL,
|
|
163
|
+
device_name STRING,
|
|
164
|
+
uns_path SYMBOL
|
|
165
|
+
) TIMESTAMP(timestamp) PARTITION BY DAY`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function getIngestConfigString() {
|
|
169
|
+
return settings.daqstore.configurationString || 'http::addr=localhost:9000;';
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function getQueryClientConfig() {
|
|
173
|
+
return {
|
|
174
|
+
host: settings.daqstore.host || '127.0.0.1',
|
|
175
|
+
port: 8812, // Standard port
|
|
176
|
+
database: 'qdb', // Standard database
|
|
177
|
+
user: settings.daqstore.credentials?.username || 'admin',
|
|
178
|
+
password: settings.daqstore.credentials?.password || 'quest',
|
|
179
|
+
max: 10, // Pool with 10 connections
|
|
180
|
+
idleTimeoutMillis: 30000, // 30s
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function getTableName() {
|
|
185
|
+
const name = (settings.daqstore.tableName || 'meters').trim();
|
|
186
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
|
|
187
|
+
return name.toLowerCase();
|
|
188
|
+
}
|
|
189
|
+
logger.warn(`questdb invalid tableName "${name}", fallback to meters`);
|
|
190
|
+
return 'meters';
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function normalizeValue(value) {
|
|
194
|
+
if (utils.isNullOrUndefined(value)) {
|
|
195
|
+
return { numberValue: null, stringValue: null };
|
|
196
|
+
}
|
|
197
|
+
if (utils.isBoolean(value)) {
|
|
198
|
+
return {
|
|
199
|
+
numberValue: value ? 1 : 0,
|
|
200
|
+
stringValue: null
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
204
|
+
return { numberValue: value, stringValue: null };
|
|
205
|
+
}
|
|
206
|
+
return { numberValue: null, stringValue: String(value) };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function normalizeUnsPath(value) {
|
|
210
|
+
if (utils.isNullOrUndefined(value)) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
const normalized = String(value).trim();
|
|
214
|
+
return normalized.length ? normalized : null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
initPromise = this.init();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
module.exports = {
|
|
221
|
+
create: function (data, logger, currentStorage) {
|
|
222
|
+
return new QuestDB(data, logger, currentStorage);
|
|
223
|
+
}
|
|
224
|
+
};
|
package/runtime/utils.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const os = require('os');
|
|
2
2
|
const ip = require('ip');
|
|
3
|
+
const crypto = require('crypto');
|
|
3
4
|
|
|
4
5
|
'use strict';
|
|
5
6
|
var utils = module.exports = {
|
|
@@ -361,5 +362,9 @@ var utils = module.exports = {
|
|
|
361
362
|
}
|
|
362
363
|
}
|
|
363
364
|
return target;
|
|
365
|
+
},
|
|
366
|
+
|
|
367
|
+
generateSecretCode: function(byteLength = 32) {
|
|
368
|
+
return crypto.randomBytes(byteLength).toString('hex');
|
|
364
369
|
}
|
|
365
370
|
}
|
package/settings.default.js
CHANGED
|
@@ -24,6 +24,7 @@ module.exports = {
|
|
|
24
24
|
// - 'common': Less detailed than 'combined', omitting the referrer and user-agent.
|
|
25
25
|
// - 'short': Shorter format that includes the remote address and request details.
|
|
26
26
|
// - 'tiny': Minimalist format, showing just the method, URL, status, response length, and response time.
|
|
27
|
+
// - 'none': Completely disables HTTP request logging to clean the console for custom debugging scripts and reduce I/O overhead.
|
|
27
28
|
//
|
|
28
29
|
// Default Value:
|
|
29
30
|
// - 'combined': By default, logApiLevel is set to 'combined', providing detailed logs suitable for thorough tracking and analysis.
|
|
@@ -91,8 +92,10 @@ module.exports = {
|
|
|
91
92
|
|
|
92
93
|
// Used to enable security, authentication and authorization and crypt Token
|
|
93
94
|
//secureEnabled: true,
|
|
94
|
-
//secretCode: '
|
|
95
|
-
//tokenExpiresIn: '1h' // '1h'=1hour, 60=60seconds, '1d'=1day
|
|
95
|
+
//secretCode: '<set-a-strong-random-secret>',
|
|
96
|
+
//tokenExpiresIn: '1h', // '1h'=1hour, 60=60seconds, '1d'=1day
|
|
97
|
+
//enableRefreshCookieAuth: false, // if true, use refresh token HttpOnly cookie flow
|
|
98
|
+
//refreshTokenExpiresIn: '7d' // '7d'=7days, 12h=12hours, 3600=3600seconds
|
|
96
99
|
|
|
97
100
|
// Heartbeat interval in seconds (1-20)
|
|
98
101
|
heartbeatIntervalSec: 10,
|
|
@@ -110,4 +113,11 @@ module.exports = {
|
|
|
110
113
|
swaggerEnabled: false,
|
|
111
114
|
|
|
112
115
|
nodeRedEnabled: false,
|
|
113
|
-
|
|
116
|
+
|
|
117
|
+
// Node-RED access mode: "secure" (auth required) or "legacy-open" (no auth)
|
|
118
|
+
nodeRedAuthMode: "secure",
|
|
119
|
+
|
|
120
|
+
// Node-RED: allow unsafe stdlib modules in functionGlobalContext
|
|
121
|
+
// WARNING: Enabling this exposes modules like child_process/net to flows.
|
|
122
|
+
nodeRedUnsafeModules: false,
|
|
123
|
+
}
|