@frangoteam/fuxa-min 1.3.0 → 1.3.1
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/command/index.js +1 -1
- package/api/index.js +3 -0
- package/api/scripts/index.js +7 -3
- package/dist/assets/i18n/en.json +10 -3
- package/dist/assets/i18n/fr.json +2 -1
- package/dist/assets/i18n/ja.json +2 -1
- package/dist/assets/i18n/sv.json +2 -1
- package/dist/assets/i18n/zh-cn.json +2 -1
- package/dist/assets/i18n/zh-tw.json +2 -1
- package/dist/assets/lib/svgeditor/fuxa-editor.min.js +17 -23
- package/dist/index.html +1 -1
- package/dist/{main.bafae830903d548e.js → main.72bdfed42c527918.js} +6 -6
- package/docs/openapi.yaml +3 -0
- package/integrations/node-red/index.js +4 -5
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag.html +39 -35
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-get-tag.js +43 -17
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-set-tag.html +42 -34
- package/integrations/node-red/node-red-contrib-fuxa/nodes/fuxa-set-tag.js +32 -12
- package/main.js +3 -0
- package/package.json +1 -1
- package/runtime/alarms/alarmstorage.js +45 -30
- package/runtime/apikeys/apiKeysStorage.js +34 -22
- package/runtime/devices/odbc/index.js +9 -4
- package/runtime/devices/s7/index.js +2 -2
- package/runtime/notificator/notifystorage.js +34 -23
- package/runtime/project/index.js +16 -0
- package/runtime/project/prjstorage.js +78 -40
- package/runtime/scripts/index.js +6 -2
- package/runtime/storage/calculator.js +17 -13
- package/runtime/storage/influxdb/index.js +8 -3
- package/runtime/storage/questdb/index.js +1 -1
- package/runtime/storage/sqlite/index.js +11 -8
- package/runtime/storage/tdengine/index.js +24 -9
- package/runtime/users/usrstorage.js +65 -61
- package/settings.default.js +3 -0
|
@@ -263,12 +263,12 @@ function ODBCclient(_data, _logger, _events) {
|
|
|
263
263
|
* Return tables list
|
|
264
264
|
*/
|
|
265
265
|
function getTables(endpoint, fncGetProperty, packagerManager) {
|
|
266
|
-
return new Promise(
|
|
267
|
-
if (loadOdbcLib(packagerManager)) {
|
|
266
|
+
return new Promise(async function (resolve, reject) {
|
|
267
|
+
if (loadOdbcLib(packagerManager)) {
|
|
268
268
|
var connection;
|
|
269
269
|
try {
|
|
270
270
|
var security
|
|
271
|
-
await fncGetProperty({query: 'security', name: endpoint.id}).then((result, error) => {
|
|
271
|
+
await fncGetProperty({ query: 'security', name: endpoint.id }).then((result, error) => {
|
|
272
272
|
if (result) {
|
|
273
273
|
security = utils.JsonTryToParse(result.value);
|
|
274
274
|
}
|
|
@@ -286,7 +286,12 @@ function getTables(endpoint, fncGetProperty, packagerManager) {
|
|
|
286
286
|
loginTimeout: 10,
|
|
287
287
|
}
|
|
288
288
|
connection = await odbc.connect(connectionConfig)
|
|
289
|
-
var tables =
|
|
289
|
+
var tables = [];
|
|
290
|
+
|
|
291
|
+
try {
|
|
292
|
+
tables = await connection.tables(null, 'dbo', null, null);
|
|
293
|
+
} catch (err) { }
|
|
294
|
+
|
|
290
295
|
if (tables.length <= 0) {
|
|
291
296
|
tables = await connection.tables(null, null, null, null);
|
|
292
297
|
}
|
|
@@ -348,7 +348,7 @@ function S7client(_data, _logger, _events, _runtime) {
|
|
|
348
348
|
type: items[itemidx].type,
|
|
349
349
|
daq: items[itemidx].daq,
|
|
350
350
|
changed: changed,
|
|
351
|
-
tagref: items[itemidx]
|
|
351
|
+
tagref: data.tags[items[itemidx].id] // <--- Passes full tag config including scaling
|
|
352
352
|
};
|
|
353
353
|
someval = true;
|
|
354
354
|
}
|
|
@@ -701,4 +701,4 @@ function DbItems(dbnum) {
|
|
|
701
701
|
this.DBNumber = dbnum;
|
|
702
702
|
this.MaxSize = 0;
|
|
703
703
|
this.Items = {};
|
|
704
|
-
}
|
|
704
|
+
}
|
|
@@ -13,6 +13,18 @@ var settings // Application settings
|
|
|
13
13
|
var logger; // Application logger
|
|
14
14
|
var db_notifications; // Database of notifications
|
|
15
15
|
|
|
16
|
+
function _run(sql, params = []) {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
db_notifications.run(sql, params, function (err) {
|
|
19
|
+
if (err) {
|
|
20
|
+
reject(err);
|
|
21
|
+
} else {
|
|
22
|
+
resolve(this);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
16
28
|
/**
|
|
17
29
|
* Init and bind the database resource
|
|
18
30
|
* @param {*} _settings
|
|
@@ -120,23 +132,24 @@ function getNotifications() {
|
|
|
120
132
|
*/
|
|
121
133
|
function setNotification(notification) {
|
|
122
134
|
return new Promise(function (resolve, reject) {
|
|
123
|
-
// prepare query
|
|
124
135
|
if (notification) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
notification.
|
|
131
|
-
notification.text
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
const sql = "INSERT OR REPLACE INTO chronicle (id, name, type, receiver, text, notifytime, notifytype) VALUES(?, ?, ?, ?, ?, ?, ?)";
|
|
137
|
+
_run(sql, [
|
|
138
|
+
notification.id,
|
|
139
|
+
notification.name,
|
|
140
|
+
notification.type,
|
|
141
|
+
notification.receiver,
|
|
142
|
+
notification.text,
|
|
143
|
+
notification.notifytime,
|
|
144
|
+
notification.notifytype
|
|
145
|
+
]).then(function () {
|
|
146
|
+
resolve();
|
|
147
|
+
}).catch(function (err) {
|
|
148
|
+
logger.error('notifystorage.failed-to-set: ' + err);
|
|
149
|
+
reject();
|
|
139
150
|
});
|
|
151
|
+
} else {
|
|
152
|
+
resolve();
|
|
140
153
|
}
|
|
141
154
|
});
|
|
142
155
|
}
|
|
@@ -156,14 +169,12 @@ function close() {
|
|
|
156
169
|
function removeNotification(notification) {
|
|
157
170
|
return new Promise(function (resolve, reject) {
|
|
158
171
|
// prepare query
|
|
159
|
-
var sql = "DELETE FROM notifications WHERE id =
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
resolve();
|
|
166
|
-
}
|
|
172
|
+
var sql = "DELETE FROM notifications WHERE id = ?";
|
|
173
|
+
_run(sql, [notification.id]).then(function () {
|
|
174
|
+
resolve();
|
|
175
|
+
}).catch(function (err) {
|
|
176
|
+
logger.error('notificationsstorage.failed-to-remove: ' + err);
|
|
177
|
+
reject();
|
|
167
178
|
});
|
|
168
179
|
});
|
|
169
180
|
}
|
package/runtime/project/index.js
CHANGED
|
@@ -928,6 +928,22 @@ function _filterProjectPermission(userPermission) {
|
|
|
928
928
|
// from device remove the not used (no permission)
|
|
929
929
|
// delete result.devices;
|
|
930
930
|
delete result.server;
|
|
931
|
+
if (Array.isArray(result.scripts)) {
|
|
932
|
+
// Keep only scripts authorised for the current user. For authorised
|
|
933
|
+
// server-side scripts, retain just the metadata needed for event
|
|
934
|
+
// bindings and execution requests; do not expose source code.
|
|
935
|
+
result.scripts = result.scripts.filter(script => {
|
|
936
|
+
return script && runtime.scriptsMgr.isAuthorised(script, userPermission);
|
|
937
|
+
}).map(script => {
|
|
938
|
+
delete script.permission;
|
|
939
|
+
delete script.permissionRoles;
|
|
940
|
+
if (script.mode === 'CLIENT') {
|
|
941
|
+
return script;
|
|
942
|
+
}
|
|
943
|
+
delete script.code;
|
|
944
|
+
return script;
|
|
945
|
+
});
|
|
946
|
+
}
|
|
931
947
|
// check navigation permission
|
|
932
948
|
if (result.hmi.layout && result.hmi.layout.navigation.items) {
|
|
933
949
|
for (var i = result.hmi.layout.navigation.items.length - 1; i >= 0; i--) {
|
|
@@ -13,6 +13,38 @@ var settings // Application settings
|
|
|
13
13
|
var logger; // Application logger
|
|
14
14
|
var db_prj; // Database of project
|
|
15
15
|
|
|
16
|
+
function _run(sql, params = []) {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
db_prj.run(sql, params, function (err) {
|
|
19
|
+
if (err) {
|
|
20
|
+
reject(err);
|
|
21
|
+
} else {
|
|
22
|
+
resolve(this);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function _all(sql, params = []) {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
db_prj.all(sql, params, function (err, rows) {
|
|
31
|
+
if (err) {
|
|
32
|
+
reject(err);
|
|
33
|
+
} else {
|
|
34
|
+
resolve(rows);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function _ensureValidTable(table) {
|
|
41
|
+
const tables = Object.values(TableType);
|
|
42
|
+
if (!tables.includes(table)) {
|
|
43
|
+
throw new Error(`invalid table '${table}'`);
|
|
44
|
+
}
|
|
45
|
+
return table;
|
|
46
|
+
}
|
|
47
|
+
|
|
16
48
|
/**
|
|
17
49
|
* Init and bind the database resource
|
|
18
50
|
* @param {*} _settings
|
|
@@ -83,21 +115,23 @@ function setDefault() {
|
|
|
83
115
|
* @param {*} sections
|
|
84
116
|
*/
|
|
85
117
|
function setSections(sections) {
|
|
86
|
-
return new Promise(function (resolve, reject) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
db_prj.exec(sql, function (err) {
|
|
94
|
-
if (err) {
|
|
95
|
-
logger.error(`prjstorage.set failed! ${err}`);
|
|
96
|
-
reject();
|
|
97
|
-
} else {
|
|
98
|
-
resolve();
|
|
118
|
+
return new Promise(async function (resolve, reject) {
|
|
119
|
+
try {
|
|
120
|
+
await _run('BEGIN TRANSACTION');
|
|
121
|
+
for (var i = 0; i < sections.length; i++) {
|
|
122
|
+
var table = _ensureValidTable(sections[i].table);
|
|
123
|
+
var value = JSON.stringify(sections[i].value);
|
|
124
|
+
await _run(`INSERT OR REPLACE INTO ${table} (name, value) VALUES(?, ?)`, [sections[i].name, value]);
|
|
99
125
|
}
|
|
100
|
-
|
|
126
|
+
await _run('COMMIT');
|
|
127
|
+
resolve();
|
|
128
|
+
} catch (err) {
|
|
129
|
+
try {
|
|
130
|
+
await _run('ROLLBACK');
|
|
131
|
+
} catch (_) {}
|
|
132
|
+
logger.error(`prjstorage.set failed! ${err}`);
|
|
133
|
+
reject();
|
|
134
|
+
}
|
|
101
135
|
});
|
|
102
136
|
}
|
|
103
137
|
|
|
@@ -108,16 +142,19 @@ function setSections(sections) {
|
|
|
108
142
|
*/
|
|
109
143
|
function setSection(section) {
|
|
110
144
|
return new Promise(function (resolve, reject) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
145
|
+
try {
|
|
146
|
+
var table = _ensureValidTable(section.table);
|
|
147
|
+
var value = JSON.stringify(section.value);
|
|
148
|
+
_run(`INSERT OR REPLACE INTO ${table} (name, value) VALUES(?, ?)`, [section.name, value]).then(function () {
|
|
149
|
+
resolve();
|
|
150
|
+
}).catch(function (err) {
|
|
115
151
|
logger.error(`prjstorage.set failed! ${err}`);
|
|
116
152
|
reject();
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
153
|
+
});
|
|
154
|
+
} catch (err) {
|
|
155
|
+
logger.error(`prjstorage.set failed! ${err}`);
|
|
156
|
+
reject();
|
|
157
|
+
}
|
|
121
158
|
});
|
|
122
159
|
}
|
|
123
160
|
|
|
@@ -129,17 +166,18 @@ function setSection(section) {
|
|
|
129
166
|
*/
|
|
130
167
|
function getSection(table, name) {
|
|
131
168
|
return new Promise(function (resolve, reject) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
sql
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
} else {
|
|
140
|
-
resolve(rows);
|
|
169
|
+
try {
|
|
170
|
+
var safeTable = _ensureValidTable(table);
|
|
171
|
+
var sql = `SELECT name, value FROM ${safeTable}`;
|
|
172
|
+
var params = [];
|
|
173
|
+
if (name) {
|
|
174
|
+
sql += " WHERE name = ?";
|
|
175
|
+
params.push(name);
|
|
141
176
|
}
|
|
142
|
-
|
|
177
|
+
_all(sql, params).then(resolve).catch(reject);
|
|
178
|
+
} catch (err) {
|
|
179
|
+
reject(err);
|
|
180
|
+
}
|
|
143
181
|
});
|
|
144
182
|
}
|
|
145
183
|
|
|
@@ -150,14 +188,14 @@ function getSection(table, name) {
|
|
|
150
188
|
*/
|
|
151
189
|
function deleteSection(section) {
|
|
152
190
|
return new Promise(function (resolve, reject) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
reject(err);
|
|
157
|
-
} else {
|
|
191
|
+
try {
|
|
192
|
+
var table = _ensureValidTable(section.table);
|
|
193
|
+
_run(`DELETE FROM ${table} WHERE name = ?`, [section.name]).then(function () {
|
|
158
194
|
resolve();
|
|
159
|
-
}
|
|
160
|
-
})
|
|
195
|
+
}).catch(reject);
|
|
196
|
+
} catch (err) {
|
|
197
|
+
reject(err);
|
|
198
|
+
}
|
|
161
199
|
});
|
|
162
200
|
}
|
|
163
201
|
|
|
@@ -222,4 +260,4 @@ module.exports = {
|
|
|
222
260
|
deleteSection: deleteSection,
|
|
223
261
|
setDefault: setDefault,
|
|
224
262
|
TableType: TableType,
|
|
225
|
-
};
|
|
263
|
+
};
|
package/runtime/scripts/index.js
CHANGED
|
@@ -88,6 +88,10 @@ function ScriptsManager(_runtime) {
|
|
|
88
88
|
const st = scriptModule.getScript(_script);
|
|
89
89
|
var admin = (permission === -1 || permission === 255) ? true : false;
|
|
90
90
|
if (runtime.settings.userRole) {
|
|
91
|
+
admin = admin || permission?.groups === -1 || permission?.groups === 255;
|
|
92
|
+
if (admin) {
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
91
95
|
if (!st.permissionRoles || !st.permissionRoles.enabled) {
|
|
92
96
|
return true;
|
|
93
97
|
}
|
|
@@ -106,7 +110,7 @@ function ScriptsManager(_runtime) {
|
|
|
106
110
|
this.isAuthorisedByScriptName = function (scriptName, permission) {
|
|
107
111
|
const script = scriptModule.getScriptByName(scriptName);
|
|
108
112
|
if (!script) {
|
|
109
|
-
return
|
|
113
|
+
return false;
|
|
110
114
|
}
|
|
111
115
|
return this.isAuthorised(script, permission);
|
|
112
116
|
}
|
|
@@ -366,4 +370,4 @@ const ScriptSchedulingMode = {
|
|
|
366
370
|
const SchedulerType = {
|
|
367
371
|
weekly: 0,
|
|
368
372
|
date: 1,
|
|
369
|
-
}
|
|
373
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Calculator for DAQ value array (integrale, max, min, average...)
|
|
2
|
+
* Calculator for DAQ value array (integrale, max, min, average...)
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
'use strict';
|
|
@@ -19,11 +19,11 @@ function getFunctionValues(values, fromts, tots, fnc, interval, format) {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
23
|
-
* @param {*} values
|
|
24
|
-
* @param {*} fnc
|
|
25
|
-
* @param {*} intervalType hour / day
|
|
26
|
-
* @returns
|
|
22
|
+
*
|
|
23
|
+
* @param {*} values
|
|
24
|
+
* @param {*} fnc
|
|
25
|
+
* @param {*} intervalType hour / day
|
|
26
|
+
* @returns
|
|
27
27
|
*/
|
|
28
28
|
function getMin(timeserie, fromts, tots, intervalType) {
|
|
29
29
|
let result = getInterval(fromts, tots, intervalType, Number.MAX_VALUE);
|
|
@@ -39,7 +39,7 @@ function getMin(timeserie, fromts, tots, intervalType) {
|
|
|
39
39
|
intervals[intervalIndex] = value;
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
for (let i = 0; i < sorted.length; i++) {
|
|
44
44
|
let intervalIndex = getIntervalTime(sorted[i].dt, intervalType, false).getTime();
|
|
45
45
|
addToInterval(result, intervalIndex, utils.parseFloat(sorted[i].value, 5));
|
|
@@ -61,7 +61,7 @@ function getMax(timeserie, fromts, tots, intervalType) {
|
|
|
61
61
|
intervals[intervalIndex] = value;
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
for (let i = 0; i < sorted.length; i++) {
|
|
66
66
|
let intervalIndex = getIntervalTime(sorted[i].dt, intervalType, false).getTime();
|
|
67
67
|
addToInterval(result, intervalIndex, utils.parseFloat(sorted[i].value, 5));
|
|
@@ -85,7 +85,7 @@ function getAverage(timeserie, fromts, tots, intervalType, format) {
|
|
|
85
85
|
}
|
|
86
86
|
counters[intervalIndex]++;
|
|
87
87
|
}
|
|
88
|
-
|
|
88
|
+
|
|
89
89
|
for (let i = 0; i < sorted.length; i++) {
|
|
90
90
|
let intervalIndex = getIntervalTime(sorted[i].dt, intervalType, false).getTime();
|
|
91
91
|
addToInterval(result, counts, intervalIndex, parseFloat(sorted[i].value));
|
|
@@ -96,7 +96,7 @@ function getAverage(timeserie, fromts, tots, intervalType, format) {
|
|
|
96
96
|
result[k] = utils.parseFloat(result[k] / counts[k], format || 5);
|
|
97
97
|
}
|
|
98
98
|
});
|
|
99
|
-
return result;
|
|
99
|
+
return result;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
function getSum(timeserie, fromts, tots, intervalType) {
|
|
@@ -114,12 +114,12 @@ function getSum(timeserie, fromts, tots, intervalType) {
|
|
|
114
114
|
intervals[intervalIndex] = utils.parseFloat(intervals[intervalIndex], 5)
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
|
-
|
|
117
|
+
|
|
118
118
|
for (let i = 0; i < sorted.length; i++) {
|
|
119
119
|
let intervalIndex = getIntervalTime(sorted[i].dt, intervalType, false).getTime();
|
|
120
120
|
addToInterval(result, intervalIndex, parseFloat(sorted[i].value));
|
|
121
121
|
}
|
|
122
|
-
return result;
|
|
122
|
+
return result;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
|
|
@@ -137,7 +137,7 @@ function getIntegral(timeserie, fromts, tots, intervalType) {
|
|
|
137
137
|
intervals[intervalIndex] += value;
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
|
-
|
|
140
|
+
|
|
141
141
|
let lastRecord = null;// : TimeValue { dt: number, value: number };
|
|
142
142
|
let lastIntervalIndex = null;
|
|
143
143
|
for (let i = 0; i < sorted.length; i++) {
|
|
@@ -191,6 +191,10 @@ function getStepDate(ts, type, toadd) {
|
|
|
191
191
|
dtRange = new Date(dt.getFullYear(), dt.getMonth(), dt.getDate(), dt.getHours(), 0, 0);
|
|
192
192
|
var minutesRange = parseInt(dt.getMinutes() / 30);
|
|
193
193
|
dtRange.setMinutes((minutesRange + toadd) * 30);
|
|
194
|
+
} else if (type === ReportIntervalType.min15) {
|
|
195
|
+
dtRange = new Date(dt.getFullYear(), dt.getMonth(), dt.getDate(), dt.getHours(), 0, 0);
|
|
196
|
+
var minutesRange = parseInt(dt.getMinutes() / 15);
|
|
197
|
+
dtRange.setMinutes((minutesRange + toadd) * 15);
|
|
194
198
|
} else if (type === ReportIntervalType.min10) {
|
|
195
199
|
dtRange = new Date(dt.getFullYear(), dt.getMonth(), dt.getDate(), dt.getHours(), 0, 0);
|
|
196
200
|
var minutesRange = parseInt(dt.getMinutes() / 10);
|
|
@@ -12,6 +12,10 @@ var { InfluxDB, Point, flux } = require('@influxdata/influxdb-client');
|
|
|
12
12
|
const VERSION_18_FLUX = '1.8-flux';
|
|
13
13
|
const VERSION_20 = '2.0';
|
|
14
14
|
|
|
15
|
+
function escapeInfluxIdentifier(value) {
|
|
16
|
+
return String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
17
|
+
}
|
|
18
|
+
|
|
15
19
|
function Influx(_settings, _log, _currentStorate) {
|
|
16
20
|
|
|
17
21
|
var settings = _settings; // Application settings
|
|
@@ -157,7 +161,8 @@ function Influx(_settings, _log, _currentStorate) {
|
|
|
157
161
|
if (influxdbVersion === VERSION_18_FLUX) {
|
|
158
162
|
fromts *= 1000000;
|
|
159
163
|
tots *= 1000000;
|
|
160
|
-
const
|
|
164
|
+
const measurement = escapeInfluxIdentifier(tagid);
|
|
165
|
+
const query = `SELECT * FROM "${measurement}" WHERE time >= ${Number(fromts)} AND time <= ${Number(tots)}`;
|
|
161
166
|
client.query(query).then((result) => {
|
|
162
167
|
resolve(result.map(row => {
|
|
163
168
|
return {
|
|
@@ -171,7 +176,7 @@ function Influx(_settings, _log, _currentStorate) {
|
|
|
171
176
|
reject(error);
|
|
172
177
|
});
|
|
173
178
|
} else {
|
|
174
|
-
const query = flux`from(bucket:
|
|
179
|
+
const query = flux`from(bucket: ${settings.daqstore.bucket}) |> range(start: ${new Date(fromts)}, stop: ${new Date(tots)}) |> filter(fn: (r) => r.id == ${String(tagid)})`;
|
|
175
180
|
var result = [];
|
|
176
181
|
queryApi.queryRows(query, {
|
|
177
182
|
next(row, tableMeta) {
|
|
@@ -234,4 +239,4 @@ module.exports = {
|
|
|
234
239
|
var InfluxDBStatusEnum = {
|
|
235
240
|
OPEN: 'open',
|
|
236
241
|
CLOSE: 'close',
|
|
237
|
-
}
|
|
242
|
+
}
|
|
@@ -71,7 +71,7 @@ function QuestDB(_settings, _log, _currentStorage) {
|
|
|
71
71
|
.symbol('device_id', row.deviceId)
|
|
72
72
|
.stringColumn('device_name', row.deviceName);
|
|
73
73
|
if (!utils.isNullOrUndefined(row.unsPath)) {
|
|
74
|
-
line = line.
|
|
74
|
+
line = line.symbol('uns_path', row.unsPath);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
if (!utils.isNullOrUndefined(parsedValue.numberValue)) {
|
|
@@ -227,14 +227,14 @@ function DaqNode(_settings, _log, _id, _currentStorate) {
|
|
|
227
227
|
_checkDataWorking(false);
|
|
228
228
|
});
|
|
229
229
|
}
|
|
230
|
-
// check function to add daq data
|
|
230
|
+
// check function to add daq data
|
|
231
231
|
if (addDaqfnc.length > 0) {
|
|
232
232
|
if (_checkDataWorking(true)) {
|
|
233
233
|
Promise.all(addDaqfnc).then(result => {
|
|
234
234
|
// check db tokenizer after inserted
|
|
235
235
|
var lastts = 0 || result[0];
|
|
236
236
|
if (daqNextToken && daqNextToken < lastts) {
|
|
237
|
-
// close data DB, open and bind the new db, rename and move the closed db
|
|
237
|
+
// close data DB, open and bind the new db, rename and move the closed db
|
|
238
238
|
db_daqdata.close(function () {
|
|
239
239
|
var suffix = _getDateTimeSuffix(new Date());
|
|
240
240
|
var oldfile = db_daqdata_file;
|
|
@@ -262,6 +262,9 @@ function DaqNode(_settings, _log, _id, _currentStorate) {
|
|
|
262
262
|
}
|
|
263
263
|
_checkDataWorking(false);
|
|
264
264
|
});
|
|
265
|
+
} else {
|
|
266
|
+
// overload: attach no-op catch handlers to prevent UnhandledPromiseRejection
|
|
267
|
+
addDaqfnc.forEach(function (p) { p.catch(function () {}); });
|
|
265
268
|
}
|
|
266
269
|
}
|
|
267
270
|
if (dataToRestore.length && currentStorage) {
|
|
@@ -554,8 +557,8 @@ function DaqNode(_settings, _log, _id, _currentStorate) {
|
|
|
554
557
|
|
|
555
558
|
function _insertTagToMap(tagid, name, type) {
|
|
556
559
|
return new Promise(function (resolve, reject) {
|
|
557
|
-
var sqlRequest = "INSERT INTO data (id, name, type) ";
|
|
558
|
-
db_daqmap.run(sqlRequest
|
|
560
|
+
var sqlRequest = "INSERT INTO data (id, name, type) VALUES (?, ?, ?)";
|
|
561
|
+
db_daqmap.run(sqlRequest, [tagid, name, type], function (err) {
|
|
559
562
|
if (err !== null) {
|
|
560
563
|
reject(err);
|
|
561
564
|
} else {
|
|
@@ -615,10 +618,10 @@ function DaqNode(_settings, _log, _id, _currentStorate) {
|
|
|
615
618
|
function _insertTagValue(mapid, value) {
|
|
616
619
|
return new Promise(function (resolve, reject) {
|
|
617
620
|
var ts = new Date().getTime();
|
|
618
|
-
var sqlRequest = "INSERT INTO data (dt, id, value) ";
|
|
619
|
-
db_daqdata.run(sqlRequest
|
|
621
|
+
var sqlRequest = "INSERT INTO data (dt, id, value) VALUES (?, ?, ?)";
|
|
622
|
+
db_daqdata.run(sqlRequest, [ts, mapid, value], function (err) {
|
|
620
623
|
if (err !== null) {
|
|
621
|
-
reject();
|
|
624
|
+
reject(err);
|
|
622
625
|
}
|
|
623
626
|
else {
|
|
624
627
|
resolve(ts);
|
|
@@ -692,4 +695,4 @@ module.exports = {
|
|
|
692
695
|
return new DaqNode(data, logger, id, currentStorate);
|
|
693
696
|
},
|
|
694
697
|
checkRetention: checkRetention
|
|
695
|
-
};
|
|
698
|
+
};
|
|
@@ -3,11 +3,20 @@
|
|
|
3
3
|
let { options, connect } = require("@tdengine/rest");
|
|
4
4
|
let utils = require('../../utils');
|
|
5
5
|
|
|
6
|
+
function quoteTdIdentifier(value) {
|
|
7
|
+
return '`' + String(value).replace(/`/g, '``') + '`';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function escapeTdString(value) {
|
|
11
|
+
return String(value).replace(/'/g, "''");
|
|
12
|
+
}
|
|
13
|
+
|
|
6
14
|
function TDengine(_settings, _log, _currentStorage) {
|
|
7
15
|
let settings = _settings; // Application settings
|
|
8
16
|
const logger = _log; // Application logger
|
|
9
17
|
const currentStorage = _currentStorage; // Database to set the last value (current)
|
|
10
18
|
const database = settings.daqstore.database || 'fuxa';
|
|
19
|
+
const databaseRef = quoteTdIdentifier(database);
|
|
11
20
|
let conn;
|
|
12
21
|
|
|
13
22
|
this.setCall = function (_fncGetProp) {
|
|
@@ -25,8 +34,8 @@ function TDengine(_settings, _log, _currentStorage) {
|
|
|
25
34
|
const cursor = conn.cursor();
|
|
26
35
|
try {
|
|
27
36
|
//TODO add retention
|
|
28
|
-
let res = await cursor.query(`CREATE DATABASE IF NOT EXISTS ${
|
|
29
|
-
res = await cursor.query(`CREATE STABLE IF NOT EXISTS ${
|
|
37
|
+
let res = await cursor.query(`CREATE DATABASE IF NOT EXISTS ${databaseRef} `);
|
|
38
|
+
res = await cursor.query(`CREATE STABLE IF NOT EXISTS ${databaseRef}.meters (dt TIMESTAMP,tag_id VARCHAR(200), tag_value BINARY(20)) TAGS (device_id VARCHAR(200),device_name BINARY(256) )`);
|
|
30
39
|
|
|
31
40
|
} catch (error) {
|
|
32
41
|
console.error(error);
|
|
@@ -47,8 +56,13 @@ function TDengine(_settings, _log, _currentStorage) {
|
|
|
47
56
|
continue;
|
|
48
57
|
}
|
|
49
58
|
}
|
|
50
|
-
|
|
51
|
-
|
|
59
|
+
const safeDeviceId = quoteTdIdentifier(deviceId);
|
|
60
|
+
const safeDeviceTag = escapeTdString(deviceId);
|
|
61
|
+
const safeDeviceName = escapeTdString(deviceName);
|
|
62
|
+
const safeTagId = escapeTdString(tagid);
|
|
63
|
+
const safeTagValue = escapeTdString(tag.value);
|
|
64
|
+
let insertSql = `INSERT INTO ${databaseRef}.${safeDeviceId} USING ${databaseRef}.meters TAGS('${safeDeviceTag}','${safeDeviceName}')
|
|
65
|
+
VALUES (NOW, '${safeTagId}', '${safeTagValue}')`;
|
|
52
66
|
//async
|
|
53
67
|
|
|
54
68
|
cursor.query(insertSql).then((rst) => {
|
|
@@ -71,11 +85,12 @@ function TDengine(_settings, _log, _currentStorage) {
|
|
|
71
85
|
const cursor = conn.cursor()
|
|
72
86
|
let data = []
|
|
73
87
|
//add by J, the tagid is missed in the sql, should be one of the filter condition
|
|
88
|
+
const safeTagId = escapeTdString(tagid);
|
|
74
89
|
cursor.query(`SELECT CAST(dt as BIGINT) as dt, tag_value
|
|
75
|
-
FROM ${
|
|
76
|
-
WHERE tag_id = '${
|
|
77
|
-
and dt >= ${fromts}
|
|
78
|
-
and dt < ${tots} `).then((result) => {
|
|
90
|
+
FROM ${databaseRef}.meters
|
|
91
|
+
WHERE tag_id = '${safeTagId}'
|
|
92
|
+
and dt >= ${Number(fromts)}
|
|
93
|
+
and dt < ${Number(tots)} `).then((result) => {
|
|
79
94
|
// logger.debug(result)
|
|
80
95
|
result.getData().forEach((row) => {
|
|
81
96
|
data.push({ dt: row[0], value: row[1] })
|
|
@@ -105,4 +120,4 @@ module.exports = {
|
|
|
105
120
|
create: function (data, logger, currentStorage) {
|
|
106
121
|
return new TDengine(data, logger, currentStorage);
|
|
107
122
|
}
|
|
108
|
-
};
|
|
123
|
+
};
|