mqtt-devices-parser 1.0.23 → 1.0.25
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/Changelog.md +55 -0
- package/index.js +3 -2
- package/models/devices.models.js +10 -0
- package/models/logs_sensor.models.js +1 -1
- package/models/mqtt.models.js +14 -0
- package/models/mqttTemplate.models.js +8 -0
- package/models/sensors.models.js +26 -2
- package/models/sensorsTemplate.models.js +43 -0
- package/package.json +1 -1
- package/src/aux/parser.js +36 -0
- package/src/db/data.js +56 -0
- package/src/db/device.js +116 -20
- package/src/db/model.js +2 -2
- package/src/db/sensor.js +14 -1
- package/src/device/device.js +196 -84
- package/src/kafka/consumer.js +10 -2
package/Changelog.md
CHANGED
|
@@ -1,5 +1,60 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.25
|
|
4
|
+
|
|
5
|
+
Sensors template (#9)
|
|
6
|
+
|
|
7
|
+
* new table sensorsTemplate
|
|
8
|
+
|
|
9
|
+
* store topics which match a sensor
|
|
10
|
+
for each mqtt msg received, check if there is a sensor associated to that topic.
|
|
11
|
+
If there is, update current value and insert new log
|
|
12
|
+
|
|
13
|
+
* db: models/sensors
|
|
14
|
+
changes localUnixTs field to remoteUnixTs
|
|
15
|
+
adds value, error
|
|
16
|
+
changes property: allow null
|
|
17
|
+
adds foreign keys to model_id and device_id
|
|
18
|
+
|
|
19
|
+
* handle change db column localUnixTs to remoteUnixTs
|
|
20
|
+
|
|
21
|
+
* fix getSensorsByRef
|
|
22
|
+
ref is not unique so it can have more than 1 row
|
|
23
|
+
|
|
24
|
+
* src/db/data: Implements missing function pathIntoObject
|
|
25
|
+
|
|
26
|
+
* src/aux/parser: adds functions to handle mqtt topics parsing
|
|
27
|
+
index: uses global $.parser as a pointer to class src/aux/parser
|
|
28
|
+
src/device/device: fixes null iterations, sets updateSensor and handleMqttTopic as public functions. Removes local mqtt parser functions. Adapts calls to those functions
|
|
29
|
+
|
|
30
|
+
mqtt: always check if topic is registered
|
|
31
|
+
store values in db
|
|
32
|
+
|
|
33
|
+
## 1.0.24
|
|
34
|
+
src/device/device: fix log
|
|
35
|
+
src/kafka/consumer: increase initialRetryTime from 100 to 300ms
|
|
36
|
+
src/device/device: parseMqttMessage: get remote configs if not known
|
|
37
|
+
synch configs if they mismatch
|
|
38
|
+
models/devices.models: new column synch
|
|
39
|
+
synchs mqtt topics
|
|
40
|
+
Explanation
|
|
41
|
+
**
|
|
42
|
+
When a message is received, it checks if mqtt topic is associated to the device without "set" if present.
|
|
43
|
+
If topic exists and ends with set, updates localData column. If topic exists and doesn't end with set, updates remoteData column.
|
|
44
|
+
If localData was updated sets topic as unsynched
|
|
45
|
+
If remoteData was received and differs from localData and synch is enabled, tries to synch topic
|
|
46
|
+
**
|
|
47
|
+
adds synch and synched columns to mqtt table
|
|
48
|
+
Adds methods getMqttTopic, updateRemoteTopic, setSynchedTopic
|
|
49
|
+
kafka: adds random number to kafka when program is in dev mode.
|
|
50
|
+
Avoids to read all queue messages since last session
|
|
51
|
+
mqtt templates: preparing mqtt topics synch
|
|
52
|
+
new column synch and localData for mqttTemplates table
|
|
53
|
+
new column mqttTemplate_id for mqtt table
|
|
54
|
+
add property "property" to table sensors
|
|
55
|
+
src/device/device: decode json payload and get property associated
|
|
56
|
+
models/devices: add synched column (for future implementations)
|
|
57
|
+
|
|
3
58
|
## 1.0.23
|
|
4
59
|
src/device/device: fix tech, version and app_version db update
|
|
5
60
|
npm warn audit Updating websocket-stream to 5.3.0, which is a SemVer major change.
|
package/index.js
CHANGED
|
@@ -17,6 +17,7 @@ $.db_firmware = require('./src/db/firmware');
|
|
|
17
17
|
$.db_sensor = require('./src/db/sensor');
|
|
18
18
|
$.db_fota = require('./src/db/fota');
|
|
19
19
|
$.mqtt_client = null;
|
|
20
|
+
$.parser = require('./src/aux/parser')
|
|
20
21
|
|
|
21
22
|
const packageJson = require(__dirname+'/package.json');
|
|
22
23
|
const packageVersion = packageJson.version;
|
|
@@ -174,7 +175,7 @@ function mqtt_connect(){
|
|
|
174
175
|
$.mqtt_client.subscribe(project+"/#", (err) => {
|
|
175
176
|
if(err){
|
|
176
177
|
console.log("[MQTT] error");
|
|
177
|
-
console.
|
|
178
|
+
console.error(err);
|
|
178
179
|
}
|
|
179
180
|
else
|
|
180
181
|
console.log("[MQTT] subscribed to project:",project);
|
|
@@ -230,6 +231,6 @@ function mqtt_connect(){
|
|
|
230
231
|
|
|
231
232
|
$.mqtt_client.on("error",(error)=>{
|
|
232
233
|
console.log("[MQTT] error");
|
|
233
|
-
console.
|
|
234
|
+
console.error(error)
|
|
234
235
|
})
|
|
235
236
|
}
|
package/models/devices.models.js
CHANGED
|
@@ -71,6 +71,16 @@ module.exports = (sequelize,DataTypes)=>{
|
|
|
71
71
|
type: DataTypes.STRING,
|
|
72
72
|
allowNull: true
|
|
73
73
|
},
|
|
74
|
+
synch: { // set to 1 to enable auto synch
|
|
75
|
+
type: DataTypes.INTEGER,
|
|
76
|
+
default: 0,
|
|
77
|
+
allowNull: true
|
|
78
|
+
},
|
|
79
|
+
synched: { // handle internally
|
|
80
|
+
type: DataTypes.INTEGER,
|
|
81
|
+
default: 0,
|
|
82
|
+
allowNull: true
|
|
83
|
+
},
|
|
74
84
|
},
|
|
75
85
|
{
|
|
76
86
|
tableName: 'devices',
|
package/models/mqtt.models.js
CHANGED
|
@@ -32,6 +32,20 @@ module.exports = (sequelize,DataTypes)=>{
|
|
|
32
32
|
template_id: {
|
|
33
33
|
type: DataTypes.INTEGER,
|
|
34
34
|
allowNull: true,
|
|
35
|
+
},
|
|
36
|
+
mqttTemplate_id: {
|
|
37
|
+
type: DataTypes.INTEGER,
|
|
38
|
+
allowNull: true,
|
|
39
|
+
},
|
|
40
|
+
synch: {
|
|
41
|
+
type: DataTypes.INTEGER,
|
|
42
|
+
default: 0,
|
|
43
|
+
allowNull: true,
|
|
44
|
+
},
|
|
45
|
+
synched: {
|
|
46
|
+
type: DataTypes.INTEGER,
|
|
47
|
+
default: 0,
|
|
48
|
+
allowNull: true,
|
|
35
49
|
}
|
|
36
50
|
},
|
|
37
51
|
{
|
|
@@ -13,10 +13,18 @@ module.exports = (sequelize,DataTypes)=>{
|
|
|
13
13
|
type: DataTypes.JSON,
|
|
14
14
|
allowNull: true
|
|
15
15
|
},
|
|
16
|
+
localData: {
|
|
17
|
+
type: DataTypes.JSON,
|
|
18
|
+
allowNull: true
|
|
19
|
+
},
|
|
16
20
|
readInterval: {
|
|
17
21
|
type: DataTypes.INTEGER,
|
|
18
22
|
allowNull: true
|
|
19
23
|
},
|
|
24
|
+
synch: {
|
|
25
|
+
type: DataTypes.INTEGER,
|
|
26
|
+
allowNull: true
|
|
27
|
+
},
|
|
20
28
|
template_id: {
|
|
21
29
|
type: DataTypes.INTEGER,
|
|
22
30
|
allowNull: false
|
package/models/sensors.models.js
CHANGED
|
@@ -3,11 +3,19 @@ module.exports = (sequelize,DataTypes)=>{
|
|
|
3
3
|
return sequelize.define("sensors", {
|
|
4
4
|
model_id: { // reference a model
|
|
5
5
|
type: DataTypes.INTEGER,
|
|
6
|
-
allowNull:
|
|
6
|
+
allowNull: false,
|
|
7
|
+
references: {
|
|
8
|
+
model: 'models',
|
|
9
|
+
key: 'id'
|
|
10
|
+
}
|
|
7
11
|
},
|
|
8
12
|
device_id: { // reference a device id
|
|
9
13
|
type: DataTypes.INTEGER,
|
|
10
|
-
allowNull:
|
|
14
|
+
allowNull: false,
|
|
15
|
+
references: {
|
|
16
|
+
model: 'devices',
|
|
17
|
+
key: 'id'
|
|
18
|
+
}
|
|
11
19
|
},
|
|
12
20
|
active: {
|
|
13
21
|
type: DataTypes.BOOLEAN,
|
|
@@ -26,6 +34,22 @@ module.exports = (sequelize,DataTypes)=>{
|
|
|
26
34
|
type: DataTypes.STRING,
|
|
27
35
|
allowNull: false,
|
|
28
36
|
},
|
|
37
|
+
property: {
|
|
38
|
+
type: DataTypes.STRING,
|
|
39
|
+
allowNull: true
|
|
40
|
+
},
|
|
41
|
+
value: {
|
|
42
|
+
type: DataTypes.STRING,
|
|
43
|
+
allowNull: true
|
|
44
|
+
},
|
|
45
|
+
error: {
|
|
46
|
+
type: DataTypes.STRING,
|
|
47
|
+
allowNull: true,
|
|
48
|
+
},
|
|
49
|
+
remoteUnixTs: {
|
|
50
|
+
type: DataTypes.BIGINT,
|
|
51
|
+
allowNull: true,
|
|
52
|
+
},
|
|
29
53
|
graph: {
|
|
30
54
|
type: DataTypes.JSON,
|
|
31
55
|
allowNull: true,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
|
|
2
|
+
module.exports = (sequelize,DataTypes)=>{
|
|
3
|
+
return sequelize.define("sensorsTemplate", {
|
|
4
|
+
model_id: { // reference a model
|
|
5
|
+
type: DataTypes.INTEGER,
|
|
6
|
+
allowNull: false,
|
|
7
|
+
references: {
|
|
8
|
+
model: 'models',
|
|
9
|
+
key: 'id'
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
active: {
|
|
13
|
+
type: DataTypes.BOOLEAN,
|
|
14
|
+
allowNull: false,
|
|
15
|
+
defaultValue: true
|
|
16
|
+
},
|
|
17
|
+
ref: {
|
|
18
|
+
type: DataTypes.STRING,
|
|
19
|
+
allowNull: false,
|
|
20
|
+
},
|
|
21
|
+
name: {
|
|
22
|
+
type: DataTypes.STRING,
|
|
23
|
+
allowNull: false,
|
|
24
|
+
},
|
|
25
|
+
type: {
|
|
26
|
+
type: DataTypes.STRING,
|
|
27
|
+
allowNull: false,
|
|
28
|
+
},
|
|
29
|
+
property: {
|
|
30
|
+
type: DataTypes.STRING,
|
|
31
|
+
allowNull: true
|
|
32
|
+
},
|
|
33
|
+
graph: {
|
|
34
|
+
type: DataTypes.JSON,
|
|
35
|
+
allowNull: true,
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
tableName: 'sensorsTemplate',
|
|
40
|
+
freezeTableName: true
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
package/package.json
CHANGED
package/src/aux/parser.js
CHANGED
|
@@ -16,5 +16,41 @@ module.exports = {
|
|
|
16
16
|
}
|
|
17
17
|
});
|
|
18
18
|
return obj;
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
getFirstWord : (str)=>{
|
|
22
|
+
const slashIndex = str.indexOf('/');
|
|
23
|
+
if (slashIndex === -1) {
|
|
24
|
+
// No slash found, return the original string
|
|
25
|
+
return str;
|
|
26
|
+
}
|
|
27
|
+
return str.substring(0,slashIndex);
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
getTopicAfterSlash : (str)=>{
|
|
31
|
+
const slashIndex = str.indexOf('/');
|
|
32
|
+
if (slashIndex === -1) {
|
|
33
|
+
// No slash found, return empty string
|
|
34
|
+
return "";
|
|
35
|
+
}
|
|
36
|
+
return str.substring(slashIndex + 1);
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
getWordAfterLastSlash : (str)=>{
|
|
40
|
+
const lastSlashIndex = str.lastIndexOf('/');
|
|
41
|
+
if (lastSlashIndex === -1) {
|
|
42
|
+
// No slash found, return the original string
|
|
43
|
+
return str;
|
|
44
|
+
}
|
|
45
|
+
return str.substring(lastSlashIndex + 1);
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
getWordBeforeLastSlash : (str)=>{
|
|
49
|
+
const lastSlashIndex = str.lastIndexOf('/');
|
|
50
|
+
if (lastSlashIndex === -1) {
|
|
51
|
+
// No slash found, return the original string
|
|
52
|
+
return str;
|
|
53
|
+
}
|
|
54
|
+
return str.substring(0,lastSlashIndex);
|
|
19
55
|
}
|
|
20
56
|
}
|
package/src/db/data.js
CHANGED
|
@@ -372,3 +372,59 @@ var self = module.exports = {
|
|
|
372
372
|
});
|
|
373
373
|
}
|
|
374
374
|
};
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Build a nested object from a path string and a leaf value.
|
|
378
|
+
*
|
|
379
|
+
* Example:
|
|
380
|
+
* pathIntoObject('foo/bar/baz', 123) -> { foo: { bar: { baz: 123 } } }
|
|
381
|
+
*
|
|
382
|
+
* Notes:
|
|
383
|
+
* - By default, splits on '/'.
|
|
384
|
+
* - Leading/trailing/duplicate delimiters are ignored (e.g., '/a//b/' -> ['a','b']).
|
|
385
|
+
* - Value is used as-is. If you pass JSON.stringify(data), the leaf will be that string.
|
|
386
|
+
*
|
|
387
|
+
* @param {string} path - The path to turn into nested object keys (e.g., 'a/b/c').
|
|
388
|
+
* @param {*} value - The value to place at the final key.
|
|
389
|
+
* @param {Object} [options]
|
|
390
|
+
* @param {string} [options.delimiter='/'] - Path delimiter.
|
|
391
|
+
* @returns {*}
|
|
392
|
+
*/
|
|
393
|
+
const parser = {
|
|
394
|
+
pathIntoObject(path, value, options = {}) {
|
|
395
|
+
const delimiter = options.delimiter || '/';
|
|
396
|
+
|
|
397
|
+
if (typeof path !== 'string') {
|
|
398
|
+
throw new TypeError('path must be a string');
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Normalize path: remove empty segments caused by leading/trailing or duplicate delimiters
|
|
402
|
+
const segments = path
|
|
403
|
+
.split(delimiter)
|
|
404
|
+
.map(s => s.trim())
|
|
405
|
+
.filter(Boolean);
|
|
406
|
+
|
|
407
|
+
// If no segments, just return the value directly
|
|
408
|
+
if (segments.length === 0) return value;
|
|
409
|
+
|
|
410
|
+
// Build nested object
|
|
411
|
+
let root = {};
|
|
412
|
+
let cursor = root;
|
|
413
|
+
|
|
414
|
+
for (let i = 0; i < segments.length; i++) {
|
|
415
|
+
const key = segments[i];
|
|
416
|
+
const isLast = i === segments.length - 1;
|
|
417
|
+
|
|
418
|
+
if (isLast) {
|
|
419
|
+
cursor[key] = value;
|
|
420
|
+
} else {
|
|
421
|
+
// Create the next level if not present
|
|
422
|
+
cursor[key] = {};
|
|
423
|
+
cursor = cursor[key];
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return root;
|
|
428
|
+
},
|
|
429
|
+
};
|
|
430
|
+
|
package/src/db/device.js
CHANGED
|
@@ -194,7 +194,6 @@ var self = module.exports = {
|
|
|
194
194
|
});
|
|
195
195
|
},
|
|
196
196
|
|
|
197
|
-
|
|
198
197
|
addLog : async(id,column,value)=>{
|
|
199
198
|
return new Promise((resolve,reject) => {
|
|
200
199
|
|
|
@@ -255,26 +254,26 @@ var self = module.exports = {
|
|
|
255
254
|
|
|
256
255
|
getLocalSettings : async(deviceId)=>{
|
|
257
256
|
|
|
258
|
-
|
|
257
|
+
return new Promise((resolve,reject) => {
|
|
259
258
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
259
|
+
let query = "SELECT local_settings FROM ?? where id = ?";
|
|
260
|
+
let table = ["devices", deviceId];
|
|
261
|
+
query = mysql.format(query,table);
|
|
263
262
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
263
|
+
$.db.queryRow(query)
|
|
264
|
+
.then( rows => {
|
|
265
|
+
if(rows?.length > 0)
|
|
266
|
+
return resolve(rows[0]?.local_settings);
|
|
267
|
+
else
|
|
268
|
+
return resolve(null);
|
|
269
|
+
})
|
|
270
|
+
.catch( err => {
|
|
271
|
+
return reject(err);
|
|
272
|
+
});
|
|
274
273
|
});
|
|
275
|
-
|
|
274
|
+
},
|
|
276
275
|
|
|
277
|
-
|
|
276
|
+
updateLocalSettings : async(settings, deviceId)=>{
|
|
278
277
|
|
|
279
278
|
return new Promise((resolve,reject) => {
|
|
280
279
|
let obj = {
|
|
@@ -299,7 +298,7 @@ var self = module.exports = {
|
|
|
299
298
|
});
|
|
300
299
|
},
|
|
301
300
|
|
|
302
|
-
|
|
301
|
+
getRemoteSettings : async(deviceId)=>{
|
|
303
302
|
|
|
304
303
|
return new Promise((resolve,reject) => {
|
|
305
304
|
|
|
@@ -389,13 +388,34 @@ var self = module.exports = {
|
|
|
389
388
|
});
|
|
390
389
|
},
|
|
391
390
|
|
|
392
|
-
|
|
391
|
+
getSensorsByRef : async(deviceId, ref)=>{
|
|
393
392
|
return new Promise((resolve,reject) => {
|
|
394
393
|
|
|
395
394
|
let query = "SELECT * FROM ?? where device_id = ? and ref = ?";
|
|
396
395
|
let args = ["sensors",deviceId,ref];
|
|
397
396
|
query = mysql.format(query,args);
|
|
398
397
|
|
|
398
|
+
$.db.queryRow(query)
|
|
399
|
+
.then( rows => {
|
|
400
|
+
if(rows.length > 0)
|
|
401
|
+
return resolve(rows);
|
|
402
|
+
else
|
|
403
|
+
return resolve(null);
|
|
404
|
+
})
|
|
405
|
+
.catch( err => {
|
|
406
|
+
console.log(err);
|
|
407
|
+
return resolve(null);
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
},
|
|
411
|
+
|
|
412
|
+
getMqttTopic : async(deviceId, topic)=>{
|
|
413
|
+
return new Promise((resolve,reject) => {
|
|
414
|
+
|
|
415
|
+
let query = "SELECT * FROM ?? where device_id = ? and topic = ?";
|
|
416
|
+
let args = ["mqtt",deviceId,topic];
|
|
417
|
+
query = mysql.format(query,args);
|
|
418
|
+
|
|
399
419
|
$.db.queryRow(query)
|
|
400
420
|
.then( rows => {
|
|
401
421
|
if(rows.length > 0)
|
|
@@ -408,6 +428,82 @@ var self = module.exports = {
|
|
|
408
428
|
return resolve(null);
|
|
409
429
|
});
|
|
410
430
|
});
|
|
411
|
-
}
|
|
431
|
+
},
|
|
432
|
+
|
|
433
|
+
updateLocalTopic : async(id, data)=>{
|
|
434
|
+
|
|
435
|
+
let jsonData = {
|
|
436
|
+
value : data
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
return new Promise((resolve,reject) => {
|
|
440
|
+
let obj = {
|
|
441
|
+
localData : JSON.stringify(jsonData),
|
|
442
|
+
updatedAt : moment().utc().format('YYYY-MM-DD HH:mm:ss')
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
let filter = {
|
|
446
|
+
id
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
$.db.update("mqtt",obj,filter)
|
|
450
|
+
.then (rows => {
|
|
451
|
+
return resolve(rows[0]);
|
|
452
|
+
})
|
|
453
|
+
.catch(error => {
|
|
454
|
+
return reject(error);
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
});
|
|
458
|
+
},
|
|
459
|
+
|
|
460
|
+
updateRemoteTopic : async(id, data)=>{
|
|
412
461
|
|
|
462
|
+
let jsonData = {
|
|
463
|
+
value : data
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
return new Promise((resolve,reject) => {
|
|
467
|
+
let obj = {
|
|
468
|
+
remoteData : JSON.stringify(jsonData),
|
|
469
|
+
updatedAt : moment().utc().format('YYYY-MM-DD HH:mm:ss')
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
let filter = {
|
|
473
|
+
id
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
$.db.update("mqtt",obj,filter)
|
|
477
|
+
.then (rows => {
|
|
478
|
+
return resolve(rows[0]);
|
|
479
|
+
})
|
|
480
|
+
.catch(error => {
|
|
481
|
+
return reject(error);
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
});
|
|
485
|
+
},
|
|
486
|
+
|
|
487
|
+
setSynchedTopic : async(id, data)=>{
|
|
488
|
+
|
|
489
|
+
return new Promise((resolve,reject) => {
|
|
490
|
+
let obj = {
|
|
491
|
+
synched : data ? 1 : 0,
|
|
492
|
+
updatedAt : moment().utc().format('YYYY-MM-DD HH:mm:ss')
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
let filter = {
|
|
496
|
+
id,
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
$.db.update("mqtt",obj,filter)
|
|
500
|
+
.then (rows => {
|
|
501
|
+
return resolve(rows[0]);
|
|
502
|
+
})
|
|
503
|
+
.catch(error => {
|
|
504
|
+
return reject(error);
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
});
|
|
508
|
+
},
|
|
413
509
|
}
|
package/src/db/model.js
CHANGED
|
@@ -112,7 +112,7 @@ var self = module.exports = {
|
|
|
112
112
|
});
|
|
113
113
|
},
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
getSensorsByRef : async(modelId, ref)=>{
|
|
116
116
|
return new Promise((resolve,reject) => {
|
|
117
117
|
|
|
118
118
|
let query = "SELECT * FROM ?? where model_id = ? and ref = ?";
|
|
@@ -122,7 +122,7 @@ var self = module.exports = {
|
|
|
122
122
|
$.db.queryRow(query)
|
|
123
123
|
.then( rows => {
|
|
124
124
|
if(rows.length > 0)
|
|
125
|
-
return resolve(rows
|
|
125
|
+
return resolve(rows);
|
|
126
126
|
else
|
|
127
127
|
return resolve(null);
|
|
128
128
|
})
|
package/src/db/sensor.js
CHANGED
|
@@ -26,6 +26,7 @@ var self = module.exports = {
|
|
|
26
26
|
});
|
|
27
27
|
},
|
|
28
28
|
|
|
29
|
+
// not used !!
|
|
29
30
|
getByRef : async (ref)=>{
|
|
30
31
|
|
|
31
32
|
return new Promise((resolve,reject) => {
|
|
@@ -98,7 +99,7 @@ var self = module.exports = {
|
|
|
98
99
|
value : payload?.value,
|
|
99
100
|
error : payload?.error,
|
|
100
101
|
obj : payload?.object,
|
|
101
|
-
|
|
102
|
+
remoteUnixTs : payload?.timestamp, // local timestamp
|
|
102
103
|
createdAt : moment().utc().format('YYYY-MM-DD HH:mm:ss'),
|
|
103
104
|
updatedAt : moment().utc().format('YYYY-MM-DD HH:mm:ss')
|
|
104
105
|
}
|
|
@@ -113,4 +114,16 @@ var self = module.exports = {
|
|
|
113
114
|
});
|
|
114
115
|
},
|
|
115
116
|
|
|
117
|
+
update : async(table,obj,filter)=>{
|
|
118
|
+
return new Promise((resolve,reject) => {
|
|
119
|
+
|
|
120
|
+
$.db.update(table,obj,filter)
|
|
121
|
+
.then (rows => {
|
|
122
|
+
return resolve(rows);
|
|
123
|
+
})
|
|
124
|
+
.catch(error => {
|
|
125
|
+
return reject(error);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
},
|
|
116
129
|
}
|
package/src/device/device.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const moment = require('moment');
|
|
2
|
+
const { isDeepStrictEqual } = require('node:util');
|
|
2
3
|
|
|
3
4
|
var _project = [];
|
|
5
|
+
const table = "sensors"
|
|
4
6
|
const logs_table = "logs_sensor";
|
|
5
7
|
const semver = require('semver');
|
|
6
8
|
|
|
@@ -29,8 +31,6 @@ var self = module.exports = {
|
|
|
29
31
|
})
|
|
30
32
|
})
|
|
31
33
|
});
|
|
32
|
-
|
|
33
|
-
|
|
34
34
|
},
|
|
35
35
|
|
|
36
36
|
parseMessage : async (client,topic,payload,retain)=>{
|
|
@@ -48,22 +48,22 @@ var self = module.exports = {
|
|
|
48
48
|
}catch(err){};
|
|
49
49
|
|
|
50
50
|
// --- project ---
|
|
51
|
-
project_name = getFirstWord(topic);
|
|
51
|
+
project_name = $.parser.getFirstWord(topic);
|
|
52
52
|
project = await $.db_project.getByName(project_name);
|
|
53
53
|
if(project == null)
|
|
54
54
|
return;
|
|
55
55
|
|
|
56
56
|
project_id = project?.id;
|
|
57
|
-
topic =
|
|
57
|
+
topic = $.parser.getTopicAfterSlash(topic)
|
|
58
58
|
|
|
59
59
|
// check if is from lwm2m gw
|
|
60
60
|
if(topic.startsWith("responses") || topic.startsWith("requests")){
|
|
61
|
-
action = getFirstWord(topic);
|
|
62
|
-
topic =
|
|
61
|
+
action = $.parser.getFirstWord(topic);
|
|
62
|
+
topic = $.parser.getTopicAfterSlash(topic);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
// --- uid ---
|
|
66
|
-
uid = getFirstWord(topic);
|
|
66
|
+
uid = $.parser.getFirstWord(topic);
|
|
67
67
|
// check if topic corresponds to a device
|
|
68
68
|
if(!uid.startsWith(project.uidPrefix) || uid.length > project?.uidLength)
|
|
69
69
|
return;
|
|
@@ -72,14 +72,13 @@ var self = module.exports = {
|
|
|
72
72
|
if(!device)
|
|
73
73
|
return;
|
|
74
74
|
|
|
75
|
-
topic =
|
|
75
|
+
topic = $.parser.getTopicAfterSlash(topic);
|
|
76
76
|
|
|
77
77
|
if(device.protocol.toLowerCase() === "lwm2m"){
|
|
78
78
|
parseLwm2mMessage(client, project_name, device, topic, payload, action);
|
|
79
79
|
}else if(device.protocol.toLowerCase() === "mqtt"){
|
|
80
80
|
parseMqttMessage(client, project_name, device, topic, payload, retain);
|
|
81
81
|
}
|
|
82
|
-
|
|
83
82
|
},
|
|
84
83
|
|
|
85
84
|
deleteLogs : async()=>{
|
|
@@ -104,6 +103,9 @@ var self = module.exports = {
|
|
|
104
103
|
|
|
105
104
|
const models = await $.db_model.getAll();
|
|
106
105
|
|
|
106
|
+
if(!models || !models?.length)
|
|
107
|
+
return;
|
|
108
|
+
|
|
107
109
|
for (const model of models) {
|
|
108
110
|
|
|
109
111
|
//console.log("model:",model?.name);
|
|
@@ -180,6 +182,9 @@ var self = module.exports = {
|
|
|
180
182
|
|
|
181
183
|
const devices = await $.db_fota.getUpdatable(release);
|
|
182
184
|
|
|
185
|
+
if(!devices || !!devices?.length)
|
|
186
|
+
return;
|
|
187
|
+
|
|
183
188
|
const batchSize = 10;
|
|
184
189
|
for (let i = 0; i < devices.length; i += batchSize) {
|
|
185
190
|
const batch = devices.slice(i, i + batchSize);
|
|
@@ -246,6 +251,91 @@ var self = module.exports = {
|
|
|
246
251
|
}
|
|
247
252
|
},
|
|
248
253
|
|
|
254
|
+
|
|
255
|
+
updateSensor : async (device,ref,payload)=>{
|
|
256
|
+
|
|
257
|
+
let sensors = await $.db_device.getSensorsByRef(device.id,ref)
|
|
258
|
+
|
|
259
|
+
if(!sensors?.length)
|
|
260
|
+
return;
|
|
261
|
+
|
|
262
|
+
object = payload;
|
|
263
|
+
value = null;
|
|
264
|
+
error = null;
|
|
265
|
+
timestamp = null;
|
|
266
|
+
|
|
267
|
+
sensors.map( (sensor,index) =>{
|
|
268
|
+
let value = null;
|
|
269
|
+
let error = null;
|
|
270
|
+
let remoteUnixTs = null;
|
|
271
|
+
if(sensor?.type === "json" && typeof object === 'object'){
|
|
272
|
+
if(object.hasOwnProperty(sensor?.property)){
|
|
273
|
+
value = object[sensor.property];
|
|
274
|
+
}
|
|
275
|
+
}else{
|
|
276
|
+
if (typeof object === 'object') {
|
|
277
|
+
value = object?.value || object?.v;
|
|
278
|
+
error = object?.error || object?.e;
|
|
279
|
+
remoteUnixTs = object?.timestamp || objects?.ts;
|
|
280
|
+
}else{
|
|
281
|
+
value = payload;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if(value || error){
|
|
285
|
+
|
|
286
|
+
if(!remoteUnixTs)
|
|
287
|
+
remoteUnixTs = moment().unix()
|
|
288
|
+
|
|
289
|
+
const data = {
|
|
290
|
+
value,
|
|
291
|
+
error,
|
|
292
|
+
remoteUnixTs
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
let filter = {
|
|
296
|
+
id : sensor.id
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
$.db_sensor.update(table,data,filter);
|
|
300
|
+
$.db_sensor.insert(logs_table,device.id,sensor.id,data);
|
|
301
|
+
}
|
|
302
|
+
})
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
handleMqttTopic : async(device, dbTopic, payload, set)=>{
|
|
306
|
+
if(set){
|
|
307
|
+
// update local topic
|
|
308
|
+
$.db_device.updateLocalTopic(dbTopic.id,payload)
|
|
309
|
+
.then(()=>{
|
|
310
|
+
// set as not synched
|
|
311
|
+
$.db_device.setSynchedTopic(dbTopic.id,false);
|
|
312
|
+
})
|
|
313
|
+
.catch((error)=>{
|
|
314
|
+
console.error(error);
|
|
315
|
+
})
|
|
316
|
+
}else{
|
|
317
|
+
// update remote topic
|
|
318
|
+
$.db_device.updateRemoteTopic(dbTopic.id,payload)
|
|
319
|
+
.then(async (res)=>{
|
|
320
|
+
// check if topics mismatch
|
|
321
|
+
if(isDeepStrictEqual(dbTopic?.localData, dbTopic?.remoteData)){
|
|
322
|
+
$.db_device.setSynchedTopic(dbTopic.id,true);
|
|
323
|
+
}else{
|
|
324
|
+
// set as not synched
|
|
325
|
+
await $.db_device.setSynchedTopic(dbTopic.id,false);
|
|
326
|
+
if(dbTopic?.synch){ // check if sync is enabled
|
|
327
|
+
// synch topic
|
|
328
|
+
synchMqttTopic(device,dbTopic);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
})
|
|
332
|
+
.catch((error)=>{
|
|
333
|
+
console.error(error);
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
|
|
249
339
|
}
|
|
250
340
|
|
|
251
341
|
async function parseLwm2mMessage(client, project_name, device, topic, payload, action){
|
|
@@ -256,8 +346,8 @@ async function parseLwm2mMessage(client, project_name, device, topic, payload, a
|
|
|
256
346
|
payload = JSON.parse(payload);
|
|
257
347
|
}catch(error){}
|
|
258
348
|
|
|
259
|
-
let word = getFirstWord(topic);
|
|
260
|
-
topic =
|
|
349
|
+
let word = $.parser.getFirstWord(topic);
|
|
350
|
+
topic = $.parser.getTopicAfterSlash(topic);
|
|
261
351
|
|
|
262
352
|
switch (word) {
|
|
263
353
|
case "status":
|
|
@@ -268,7 +358,7 @@ async function parseLwm2mMessage(client, project_name, device, topic, payload, a
|
|
|
268
358
|
return;
|
|
269
359
|
break;
|
|
270
360
|
case "sensor":
|
|
271
|
-
updateSensor(device,topic,payload)
|
|
361
|
+
self.updateSensor(device,topic,payload)
|
|
272
362
|
}
|
|
273
363
|
|
|
274
364
|
if(_project[project_name]){
|
|
@@ -281,14 +371,15 @@ async function parseMqttMessage(client, project_name, device, topic, payload, re
|
|
|
281
371
|
if(topic.endsWith("/get"))
|
|
282
372
|
return;
|
|
283
373
|
|
|
374
|
+
const topicBck = topic;
|
|
284
375
|
//console.log("[MQTT] parse topic: ",topic);
|
|
285
376
|
|
|
286
377
|
try{
|
|
287
378
|
payload = JSON.parse(payload);
|
|
288
379
|
}catch(error){}
|
|
289
380
|
|
|
290
|
-
let word = getFirstWord(topic);
|
|
291
|
-
topic =
|
|
381
|
+
let word = $.parser.getFirstWord(topic);
|
|
382
|
+
topic = $.parser.getTopicAfterSlash(topic);
|
|
292
383
|
|
|
293
384
|
switch (word) {
|
|
294
385
|
case "status":
|
|
@@ -296,7 +387,32 @@ async function parseMqttMessage(client, project_name, device, topic, payload, re
|
|
|
296
387
|
$.db_device.update(device.id, "status", payload);
|
|
297
388
|
$.db_device.addLog(device.id,"status",payload);
|
|
298
389
|
}
|
|
299
|
-
|
|
390
|
+
|
|
391
|
+
// list mqtt topics and do calls to readable topics
|
|
392
|
+
if (payload === "online"){
|
|
393
|
+
let mqtt_prefix = `${project_name}/${device.uid}`;
|
|
394
|
+
// check if remote settings are known, if not require it
|
|
395
|
+
if(!device.remote_settings?.log){
|
|
396
|
+
let topic = `${mqtt_prefix}/settings/log/get`
|
|
397
|
+
$.mqtt_client.publish(topic,"",{qos:1,retain:false});
|
|
398
|
+
}
|
|
399
|
+
if(!device.remote_settings?.keepalive){
|
|
400
|
+
let topic = `${mqtt_prefix}/settings/keepalive/get`
|
|
401
|
+
$.mqtt_client.publish(topic,"",{qos:1,retain:false});
|
|
402
|
+
}
|
|
403
|
+
if(!device.remote_settings?.modem){
|
|
404
|
+
let topic = `${mqtt_prefix}/settings/modem/get`
|
|
405
|
+
$.mqtt_client.publish(topic,"",{qos:1,retain:false});
|
|
406
|
+
}
|
|
407
|
+
if(!device.remote_settings?.wifi){
|
|
408
|
+
let topic = `${mqtt_prefix}/settings/wifi/get`
|
|
409
|
+
$.mqtt_client.publish(topic,"",{qos:1,retain:false});
|
|
410
|
+
}
|
|
411
|
+
if(!device.remote_settings?.mqtt){
|
|
412
|
+
let topic = `${mqtt_prefix}/settings/mqtt/get`
|
|
413
|
+
$.mqtt_client.publish(topic,"",{qos:1,retain:false});
|
|
414
|
+
}
|
|
415
|
+
}
|
|
300
416
|
break;
|
|
301
417
|
case "model":
|
|
302
418
|
let res = await $.db_model.getByName(payload);
|
|
@@ -305,14 +421,12 @@ async function parseMqttMessage(client, project_name, device, topic, payload, re
|
|
|
305
421
|
$.db_device.update(device.id, "model_id", model_id);
|
|
306
422
|
$.db_device.addLog(device.id,"model_id",model_id);
|
|
307
423
|
}
|
|
308
|
-
return;
|
|
309
424
|
break;
|
|
310
425
|
case "tech":
|
|
311
426
|
if (payload != null && payload != device?.tech) {
|
|
312
427
|
$.db_device.update(device.id, "tech", payload);
|
|
313
428
|
$.db_device.addLog(device.id,"tech",payload);
|
|
314
429
|
}
|
|
315
|
-
return;
|
|
316
430
|
break;
|
|
317
431
|
case "version":
|
|
318
432
|
if (payload != null && payload != device?.version) {
|
|
@@ -320,7 +434,6 @@ async function parseMqttMessage(client, project_name, device, topic, payload, re
|
|
|
320
434
|
$.db_device.update(device.id, "version", payload);
|
|
321
435
|
handleFotaSuccess(device.id);
|
|
322
436
|
}
|
|
323
|
-
return;
|
|
324
437
|
break;
|
|
325
438
|
case "app_version":
|
|
326
439
|
if (payload != null && payload != device?.app_version) {
|
|
@@ -328,7 +441,6 @@ async function parseMqttMessage(client, project_name, device, topic, payload, re
|
|
|
328
441
|
$.db_device.update(device.id, "app_version", payload);
|
|
329
442
|
handleFotaSuccess(device.id);
|
|
330
443
|
}
|
|
331
|
-
return;
|
|
332
444
|
break;
|
|
333
445
|
case "fw":
|
|
334
446
|
if(topic === "fota/update/status"){
|
|
@@ -344,7 +456,7 @@ async function parseMqttMessage(client, project_name, device, topic, payload, re
|
|
|
344
456
|
}else if(typeof payload !== 'object' && payload !== null){
|
|
345
457
|
// change it to topic.startsWith("fw")
|
|
346
458
|
try{
|
|
347
|
-
const column = getWordAfterLastSlash(topic);
|
|
459
|
+
const column = $.parser.getWordAfterLastSlash(topic);
|
|
348
460
|
let rows = await $.db_data.update("fw",device.id,column,payload);
|
|
349
461
|
rows = await $.db_data.addLog("logs_fw",device.id,column,payload);
|
|
350
462
|
}catch(error){
|
|
@@ -352,7 +464,6 @@ async function parseMqttMessage(client, project_name, device, topic, payload, re
|
|
|
352
464
|
}
|
|
353
465
|
}
|
|
354
466
|
}
|
|
355
|
-
return;
|
|
356
467
|
break;
|
|
357
468
|
case "settings":
|
|
358
469
|
if(topic.endsWith("/set")){
|
|
@@ -360,25 +471,54 @@ async function parseMqttMessage(client, project_name, device, topic, payload, re
|
|
|
360
471
|
}else{
|
|
361
472
|
updateRemoteSettings(device,topic,payload);
|
|
362
473
|
}
|
|
363
|
-
return;
|
|
364
|
-
break;
|
|
365
|
-
case "sensor":
|
|
366
|
-
let ref = getFirstWord(topic)
|
|
367
|
-
updateSensor(device,ref,payload)
|
|
368
|
-
return;
|
|
369
474
|
break;
|
|
370
475
|
// Optional: default case if needed
|
|
371
476
|
default:
|
|
372
|
-
// handle other topics or do nothing
|
|
373
477
|
break;
|
|
374
478
|
}
|
|
375
479
|
|
|
480
|
+
// check if topic is associated with device
|
|
481
|
+
let findTopic = topicBck;
|
|
482
|
+
let set = false;
|
|
483
|
+
if(topicBck.endsWith("/set")){ // remove set if exists on topic
|
|
484
|
+
set = true;
|
|
485
|
+
findTopic = $.parser.getWordBeforeLastSlash(topicBck);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
const dbTopic = await $.db_device.getMqttTopic(device.id,findTopic)
|
|
489
|
+
if(dbTopic != null){
|
|
490
|
+
self.handleMqttTopic(device,dbTopic,payload,set);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// check if topic is a sensor
|
|
494
|
+
if(!set)
|
|
495
|
+
self.updateSensor(device,topicBck,payload);
|
|
496
|
+
|
|
376
497
|
if(_project[project_name]){
|
|
377
498
|
_project[project_name]?.module?.parseMessage(client,project_name,device,`${word}/${topic}`,payload,retain,()=>{});
|
|
378
499
|
}
|
|
379
500
|
}
|
|
380
501
|
|
|
381
|
-
async function
|
|
502
|
+
async function synchMqttTopic(device,dbTopic){
|
|
503
|
+
|
|
504
|
+
const project = await $.db_project.getById(device.project_id);
|
|
505
|
+
let mqtt_prefix = `${project.name}/${device.uid}`;
|
|
506
|
+
let topic = `${mqtt_prefix}/${dbTopic.topic}/set`
|
|
507
|
+
let payload = "";
|
|
508
|
+
if(dbTopic?.remoteData && typeof dbTopic?.remoteData === 'object'){
|
|
509
|
+
try{
|
|
510
|
+
payload = JSON.stringify(dbTopic?.remoteData);
|
|
511
|
+
}catch(err){
|
|
512
|
+
console.error(err);
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
}else{
|
|
516
|
+
payload = dbTopic?.remoteData;
|
|
517
|
+
}
|
|
518
|
+
$.mqtt_client.publish(topic,payload,{qos:1,retain:false});
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
async function updateLocalSettings(device,topic,payload){
|
|
382
522
|
|
|
383
523
|
$.db_device.addLog(device.id,"local_settings",JSON.stringify(payload));
|
|
384
524
|
|
|
@@ -469,45 +609,42 @@ async function updateRemoteSettings(device,topic,payload){
|
|
|
469
609
|
|
|
470
610
|
try {
|
|
471
611
|
await $.db_device.updateRemoteSettings(JSON.stringify(settings), device.id);
|
|
612
|
+
if(device?.synch)
|
|
613
|
+
synchSettings(device,route[0]);
|
|
472
614
|
} catch (err) {
|
|
473
615
|
console.error("Failed to update local settings:", err);
|
|
474
616
|
}
|
|
475
617
|
}
|
|
476
618
|
|
|
477
|
-
async function
|
|
478
|
-
let res = await $.db_device.getSensorByRef(device.id,ref)
|
|
479
|
-
if(res == null)
|
|
480
|
-
res = await $.db_model.getSensorByRef(device.model_id,ref)
|
|
481
|
-
if(res == null)
|
|
482
|
-
return;
|
|
483
|
-
|
|
484
|
-
object = payload;
|
|
485
|
-
value = null;
|
|
486
|
-
error = null;
|
|
487
|
-
timestamp = null;
|
|
619
|
+
async function synchSettings(device,key){
|
|
488
620
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
if(
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
621
|
+
console.log(`check topic ${key} from ${device.uid}`);
|
|
622
|
+
// Retrieve existing settings
|
|
623
|
+
let localSettings = await $.db_device.getLocalSettings(device.id);
|
|
624
|
+
console.log(localSettings)
|
|
625
|
+
let remoteSettings = await $.db_device.getRemoteSettings(device.id);
|
|
626
|
+
console.log(remoteSettings)
|
|
627
|
+
|
|
628
|
+
//let keys = await checkSettings(localSettings,remoteSettings);
|
|
629
|
+
if(localSettings?.hasOwnProperty(key) && remoteSettings?.hasOwnProperty(key)){
|
|
630
|
+
if(!isDeepStrictEqual(localSettings?.[key], remoteSettings?.[key])){
|
|
631
|
+
const project = await $.db_project.getById(device.project_id);
|
|
632
|
+
let mqtt_prefix = `${project.name}/${device.uid}`;
|
|
633
|
+
let topic = `${mqtt_prefix}/settings/${key}/set`
|
|
634
|
+
try{
|
|
635
|
+
const payload = JSON.stringify(localSettings[key]);
|
|
636
|
+
console.log(`updating key: ${key} with data:`);
|
|
637
|
+
console.log(payload);
|
|
638
|
+
$.mqtt_client.publish(topic,payload,{qos:1,retain:false});
|
|
639
|
+
}catch(err){
|
|
640
|
+
console.error(localSettings[key])
|
|
641
|
+
console.error(err);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
505
644
|
}
|
|
506
|
-
|
|
507
|
-
$.db_sensor.insert(logs_table,device.id,res.id,data);
|
|
508
|
-
return;
|
|
509
645
|
}
|
|
510
646
|
|
|
647
|
+
|
|
511
648
|
function handleFotaSuccess (deviceId){
|
|
512
649
|
let object = {
|
|
513
650
|
"nAttempts" : 0,
|
|
@@ -527,29 +664,4 @@ function handleFotaError (deviceId, error){
|
|
|
527
664
|
$.db_fota.updateLog(deviceId,object);
|
|
528
665
|
}
|
|
529
666
|
|
|
530
|
-
function getFirstWord(str){
|
|
531
|
-
const slashIndex = str.indexOf('/');
|
|
532
|
-
if (slashIndex === -1) {
|
|
533
|
-
// No slash found, return the original string
|
|
534
|
-
return str;
|
|
535
|
-
}
|
|
536
|
-
return str.substring(0,slashIndex);
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
function getWordAfterSlash(str){
|
|
540
|
-
const slashIndex = str.indexOf('/');
|
|
541
|
-
if (slashIndex === -1) {
|
|
542
|
-
// No slash found, return empty string
|
|
543
|
-
return "";
|
|
544
|
-
}
|
|
545
|
-
return str.substring(slashIndex + 1);
|
|
546
|
-
}
|
|
547
667
|
|
|
548
|
-
function getWordAfterLastSlash(str){
|
|
549
|
-
const lastSlashIndex = str.lastIndexOf('/');
|
|
550
|
-
if (lastSlashIndex === -1) {
|
|
551
|
-
// No slash found, return the original string
|
|
552
|
-
return str;
|
|
553
|
-
}
|
|
554
|
-
return str.substring(lastSlashIndex + 1);
|
|
555
|
-
}
|
package/src/kafka/consumer.js
CHANGED
|
@@ -58,9 +58,17 @@ var self = module.exports = {
|
|
|
58
58
|
|
|
59
59
|
kafka = new Kafka(kafkaConfig);
|
|
60
60
|
|
|
61
|
+
let kafkaGroupId = "";
|
|
62
|
+
if(config.env === 'development'){
|
|
63
|
+
const n = Math.floor(Math.random() * 1000) + 1; // 1..1000
|
|
64
|
+
kafkaGroupId = `${config.kafka.groupId}-${String(n)}`;
|
|
65
|
+
}else{
|
|
66
|
+
kafkaGroupId = config.kafka.groupId;
|
|
67
|
+
}
|
|
68
|
+
|
|
61
69
|
// Create consumer with shared subscription support
|
|
62
70
|
consumer = kafka.consumer({
|
|
63
|
-
groupId:
|
|
71
|
+
groupId: kafkaGroupId,
|
|
64
72
|
sessionTimeout: 30000,
|
|
65
73
|
rebalanceTimeout: 60000,
|
|
66
74
|
heartbeatInterval: 3000,
|
|
@@ -69,7 +77,7 @@ var self = module.exports = {
|
|
|
69
77
|
maxBytes: 10485760,
|
|
70
78
|
maxWaitTimeInMs: 5000,
|
|
71
79
|
retry: {
|
|
72
|
-
initialRetryTime:
|
|
80
|
+
initialRetryTime: 300,
|
|
73
81
|
retries: 8
|
|
74
82
|
}
|
|
75
83
|
});
|