@halsystems/red-bacnet 2.0.1 → 2.0.3

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.0.3]
4
+ ### Fixed
5
+ - Added unique job id to same node type to avoid same time triggered nodes job being coalesced
6
+
7
+ ## [2.0.2]
8
+ ### Fixed
9
+ - CI pipeline adjustments
10
+
3
11
  ## [2.0.1]
4
12
  ### Fixed
5
13
  - CI pipeline adjustments
package/common/func.js CHANGED
@@ -1,5 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ const { randomUUID } = require('crypto');
4
+
3
5
  // ---------------------------------- export ----------------------------------
4
6
  module.exports = {
5
7
  /**
@@ -63,4 +65,14 @@ module.exports = {
63
65
  else
64
66
  return err
65
67
  },
66
- };
68
+
69
+ /**
70
+ * Generate a unique job ID using node ID and uuid
71
+ * @param {string} nodeId - The Node-RED node instance ID.
72
+ * @param {string} prefix - The prefix for the job ID (e.g., 'readPoints', 'writePoints').
73
+ * @returns {string} - A unique job ID.
74
+ */
75
+ generateUniqueJobId: function (nodeId, prefix) {
76
+ return `${prefix}-${nodeId}-${randomUUID()}`;
77
+ },
78
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@halsystems/red-bacnet",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"
@@ -1,114 +1,117 @@
1
- 'use strict';
2
- require('./_alias.js');
3
-
4
- const EventEmitter = require('events');
5
-
6
- const { print } = require('@root/common/core/util.js')
7
- const { nowFormatted } = require('@root/common/core/util.js')
8
- const { DiscoverDeviceJob } = require('@root/common/job/discover_device.js')
9
- const { getQueue } = require('@root/common/job/global_queue_manager.js')
10
- const {
11
- EVENT_UPDATE_STATUS, EVENT_ERROR, EVENT_INPUT, EVENT_OUTPUT, EVENT_CLOSE
12
- } = require('@root/common/core/constant.js')
13
-
14
-
15
- // -------------------------------- functions --------------------------------
16
- // coalesced job remove on start and on stop, move it to here
17
- module.exports = function (RED) {
18
- class DiscoverDevice {
19
- #eventEmitter = new EventEmitter();
20
-
21
- constructor(config) {
22
- RED.nodes.createNode(this, config);
23
-
24
- // get config
25
- this.client = RED.nodes.getNode(config.client).instance;
26
-
27
- this.network = +config.network
28
- this.lowLimit = +config.lowLimit
29
- this.highLimit = +config.highLimit
30
- this.whoIsTimeout = +config.whoIsTimeout
31
-
32
- // events
33
- this.#subscribeListeners();
34
-
35
- // configure job queue - use global queue
36
- this.job = getQueue();
37
-
38
- // init task
39
- this.task = new DiscoverDeviceJob(
40
- this.client,
41
- this.#eventEmitter,
42
- this.network,
43
- this.lowLimit,
44
- this.highLimit,
45
- this.whoIsTimeout,
46
- );
47
- this.task.onStart();
48
- }
49
-
50
- #subscribeListeners() {
51
- /**
52
- * @param {Object} msg - The message object.
53
- */
54
- // @ts-ignore
55
- this.on(EVENT_INPUT, async function (msg) {
56
- /*
57
- BACnet iAm handler is causing memory leak due to unable to detach after use
58
- as a result, workaround is introduce before strategy to reuse discoverDeviceJob instead
59
- of creating new one every time onInput is triggered
60
- */
61
- const jobId = (typeof msg.id === 'string' || typeof msg.id === 'number') ? msg.id : 'discoverDevices';
62
-
63
- if (this.job.queue.map(item => item.id).includes(jobId)) {
64
- // @ts-ignore
65
- print(`Coalesced job: ${jobId}`, true)
66
- return;
67
- }
68
-
69
- this.task.network = (msg.network === undefined) ? this.network : msg.network;
70
- this.task.lowLimit = (msg.lowLimit === undefined) ? this.lowLimit : msg.lowLimit;
71
- this.task.highLimit = (msg.highLimit === undefined) ? this.highLimit : msg.highLimit;
72
-
73
- this.job.addJob({
74
- id: jobId,
75
- task: this.task,
76
- priority: Number.isFinite(msg.priority) ? msg.priority : 5,
77
- });
78
- // @ts-ignore
79
- this.status({ fill: 'yellow', shape: 'dot', text: `in queue` });
80
- });
81
-
82
- // @ts-ignore
83
- this.on(EVENT_CLOSE, () => {
84
- this.task.onStop();
85
- })
86
-
87
- this.#eventEmitter.on(EVENT_OUTPUT, (data) => {
88
- const msg = {
89
- payload: data
90
- };
91
- // @ts-ignore
92
- this.send(msg);
93
- });
94
-
95
- this.#eventEmitter.on(EVENT_UPDATE_STATUS, (msg) => {
96
- if (msg === 100)
97
- // @ts-ignore
98
- this.status({ fill: 'green', shape: 'dot', text: `completed: ${nowFormatted()}` });
99
- else
100
- // @ts-ignore
101
- this.status({ fill: 'yellow', shape: 'dot', text: `progress: ${msg} %` });
102
- });
103
-
104
- this.#eventEmitter.on(EVENT_ERROR, (err) => {
105
- // @ts-ignore
106
- this.error(err);
107
- });
108
- }
109
- }
110
-
111
- // ----- register node -----
112
- RED.nodes.registerType('discover device', DiscoverDevice);
113
- }
114
-
1
+ 'use strict';
2
+ require('./_alias.js');
3
+
4
+ const EventEmitter = require('events');
5
+
6
+ const { print } = require('@root/common/core/util.js')
7
+ const { nowFormatted } = require('@root/common/core/util.js')
8
+ const { DiscoverDeviceJob } = require('@root/common/job/discover_device.js')
9
+ const { getQueue } = require('@root/common/job/global_queue_manager.js')
10
+ const { generateUniqueJobId } = require('@root/common/func.js')
11
+ const {
12
+ EVENT_UPDATE_STATUS, EVENT_ERROR, EVENT_INPUT, EVENT_OUTPUT, EVENT_CLOSE
13
+ } = require('@root/common/core/constant.js')
14
+
15
+
16
+ // -------------------------------- functions --------------------------------
17
+ // coalesced job remove on start and on stop, move it to here
18
+ module.exports = function (RED) {
19
+ class DiscoverDevice {
20
+ #eventEmitter = new EventEmitter();
21
+
22
+ constructor(config) {
23
+ RED.nodes.createNode(this, config);
24
+
25
+ // get config
26
+ this.client = RED.nodes.getNode(config.client).instance;
27
+
28
+ // @ts-ignore
29
+ this.jobId = generateUniqueJobId(this.id, 'discoverDevices');
30
+ this.network = +config.network
31
+ this.lowLimit = +config.lowLimit
32
+ this.highLimit = +config.highLimit
33
+ this.whoIsTimeout = +config.whoIsTimeout
34
+
35
+ // events
36
+ this.#subscribeListeners();
37
+
38
+ // configure job queue - use global queue
39
+ this.job = getQueue();
40
+
41
+ // init task
42
+ this.task = new DiscoverDeviceJob(
43
+ this.client,
44
+ this.#eventEmitter,
45
+ this.network,
46
+ this.lowLimit,
47
+ this.highLimit,
48
+ this.whoIsTimeout,
49
+ );
50
+ this.task.onStart();
51
+ }
52
+
53
+ #subscribeListeners() {
54
+ /**
55
+ * @param {Object} msg - The message object.
56
+ */
57
+ // @ts-ignore
58
+ this.on(EVENT_INPUT, async function (msg) {
59
+ /*
60
+ BACnet iAm handler is causing memory leak due to unable to detach after use
61
+ as a result, workaround is introduce before strategy to reuse discoverDeviceJob instead
62
+ of creating new one every time onInput is triggered
63
+ */
64
+ const jobId = (typeof msg.id === 'string' || typeof msg.id === 'number') ? msg.id : this.jobId;
65
+
66
+ if (this.job.queue.map(item => item.id).includes(jobId)) {
67
+ // @ts-ignore
68
+ print(`Coalesced job: ${jobId}`, true)
69
+ return;
70
+ }
71
+
72
+ this.task.network = (msg.network === undefined) ? this.network : msg.network;
73
+ this.task.lowLimit = (msg.lowLimit === undefined) ? this.lowLimit : msg.lowLimit;
74
+ this.task.highLimit = (msg.highLimit === undefined) ? this.highLimit : msg.highLimit;
75
+
76
+ this.job.addJob({
77
+ id: jobId,
78
+ task: this.task,
79
+ priority: Number.isFinite(msg.priority) ? msg.priority : 5,
80
+ });
81
+ // @ts-ignore
82
+ this.status({ fill: 'yellow', shape: 'dot', text: `in queue` });
83
+ });
84
+
85
+ // @ts-ignore
86
+ this.on(EVENT_CLOSE, () => {
87
+ this.task.onStop();
88
+ })
89
+
90
+ this.#eventEmitter.on(EVENT_OUTPUT, (data) => {
91
+ const msg = {
92
+ payload: data
93
+ };
94
+ // @ts-ignore
95
+ this.send(msg);
96
+ });
97
+
98
+ this.#eventEmitter.on(EVENT_UPDATE_STATUS, (msg) => {
99
+ if (msg === 100)
100
+ // @ts-ignore
101
+ this.status({ fill: 'green', shape: 'dot', text: `completed: ${nowFormatted()}` });
102
+ else
103
+ // @ts-ignore
104
+ this.status({ fill: 'yellow', shape: 'dot', text: `progress: ${msg} %` });
105
+ });
106
+
107
+ this.#eventEmitter.on(EVENT_ERROR, (err) => {
108
+ // @ts-ignore
109
+ this.error(err);
110
+ });
111
+ }
112
+ }
113
+
114
+ // ----- register node -----
115
+ RED.nodes.registerType('discover device', DiscoverDevice);
116
+ }
117
+
@@ -1,93 +1,97 @@
1
- 'use strict';
2
- require('./_alias.js');
3
-
4
- const EventEmitter = require('events');
5
-
6
- const { print } = require('@root/common/core/util.js')
7
- const { nowFormatted } = require('@root/common/core/util.js')
8
- const { DiscoverPointJob } = require('@root/common/job/discover_point.js')
9
- const { getQueue } = require('@root/common/job/global_queue_manager.js')
10
- const {
11
- EVENT_UPDATE_STATUS, EVENT_ERROR, EVENT_INPUT, EVENT_OUTPUT
12
- } = require('@root/common/core/constant.js')
13
-
14
- // -------------------------------- functions --------------------------------
15
- module.exports = function (RED) {
16
- class DiscoverPoint {
17
- #eventEmitter = new EventEmitter();
18
-
19
- constructor(config) {
20
- RED.nodes.createNode(this, config);
21
-
22
- // get config
23
- this.client = RED.nodes.getNode(config.client).instance;
24
- this.discoverMode = +config.discoverMode
25
- this.readMethod = +config.readMethod
26
- this.groupExportDeviceCount = +config.groupExportDeviceCount
27
- this.maxConcurrentDeviceRead = +config.maxConcurrentDeviceRead
28
- this.maxConcurrentSinglePointRead = +config.maxConcurrentSinglePointRead
29
- this.concurrentTaskDelay = +config.concurrentTaskDelay
30
-
31
- // events
32
- this.#subscribeListeners();
33
-
34
- // configure job queue - use global queue
35
- this.job = getQueue();
36
- }
37
-
38
- #subscribeListeners() {
39
- /**
40
- * @param {Object} msg - The message object.
41
- */
42
- // @ts-ignore
43
- this.on(EVENT_INPUT, async function (msg) {
44
- const jobId = (typeof msg.id === 'string' || typeof msg.id === 'number') ? msg.id : 'discoverPoints';
45
-
46
- if (this.job.queue.map(item => item.id).includes(jobId)) {
47
- // @ts-ignore
48
- print(`Coalesced job: ${jobId}`, true)
49
- return;
50
- }
51
-
52
- const task = new DiscoverPointJob(
53
- this.client, this.#eventEmitter, msg?.devices, this.discoverMode, this.readMethod,
54
- this.groupExportDeviceCount, this.maxConcurrentDeviceRead,
55
- this.maxConcurrentSinglePointRead, this.concurrentTaskDelay
56
- );
57
-
58
- this.job.addJob({
59
- id: jobId,
60
- task: task,
61
- priority: Number.isFinite(msg.priority) ? msg.priority : 5,
62
- });
63
- // @ts-ignore
64
- this.status({ fill: 'yellow', shape: 'dot', text: `in queue` });
65
- });
66
-
67
- this.#eventEmitter.on(EVENT_OUTPUT, (data) => {
68
- const msg = {
69
- payload: data
70
- };
71
- // @ts-ignore
72
- this.send(msg);
73
- });
74
-
75
- this.#eventEmitter.on(EVENT_UPDATE_STATUS, (msg) => {
76
- if (msg === 100)
77
- // @ts-ignore
78
- this.status({ fill: 'green', shape: 'dot', text: `completed: ${nowFormatted()}` });
79
- else
80
- // @ts-ignore
81
- this.status({ fill: 'yellow', shape: 'dot', text: `progress: ${msg} %` });
82
- });
83
-
84
- this.#eventEmitter.on(EVENT_ERROR, (err) => {
85
- // @ts-ignore
86
- this.error(err);
87
- });
88
- }
89
- }
90
-
91
- // ----- register node -----
92
- RED.nodes.registerType('discover point', DiscoverPoint);
93
- }
1
+ 'use strict';
2
+ require('./_alias.js');
3
+
4
+ const EventEmitter = require('events');
5
+
6
+ const { print } = require('@root/common/core/util.js')
7
+ const { nowFormatted } = require('@root/common/core/util.js')
8
+ const { DiscoverPointJob } = require('@root/common/job/discover_point.js')
9
+ const { getQueue } = require('@root/common/job/global_queue_manager.js')
10
+ const { generateUniqueJobId } = require('@root/common/func.js')
11
+ const {
12
+ EVENT_UPDATE_STATUS, EVENT_ERROR, EVENT_INPUT, EVENT_OUTPUT
13
+ } = require('@root/common/core/constant.js')
14
+
15
+ // -------------------------------- functions --------------------------------
16
+ module.exports = function (RED) {
17
+ class DiscoverPoint {
18
+ #eventEmitter = new EventEmitter();
19
+
20
+ constructor(config) {
21
+ RED.nodes.createNode(this, config);
22
+
23
+ // get config
24
+ this.client = RED.nodes.getNode(config.client).instance;
25
+ this.discoverMode = +config.discoverMode
26
+ this.readMethod = +config.readMethod
27
+ this.groupExportDeviceCount = +config.groupExportDeviceCount
28
+ this.maxConcurrentDeviceRead = +config.maxConcurrentDeviceRead
29
+ this.maxConcurrentSinglePointRead = +config.maxConcurrentSinglePointRead
30
+ this.concurrentTaskDelay = +config.concurrentTaskDelay
31
+
32
+ // @ts-ignore
33
+ this.jobId = generateUniqueJobId(this.id, 'discoverPoints');
34
+
35
+ // events
36
+ this.#subscribeListeners();
37
+
38
+ // configure job queue - use global queue
39
+ this.job = getQueue();
40
+ }
41
+
42
+ #subscribeListeners() {
43
+ /**
44
+ * @param {Object} msg - The message object.
45
+ */
46
+ // @ts-ignore
47
+ this.on(EVENT_INPUT, async function (msg) {
48
+ const jobId = (typeof msg.id === 'string' || typeof msg.id === 'number') ? msg.id : this.jobId;
49
+
50
+ if (this.job.queue.map(item => item.id).includes(jobId)) {
51
+ // @ts-ignore
52
+ print(`Coalesced job: ${jobId}`, true)
53
+ return;
54
+ }
55
+
56
+ const task = new DiscoverPointJob(
57
+ this.client, this.#eventEmitter, msg?.devices, this.discoverMode, this.readMethod,
58
+ this.groupExportDeviceCount, this.maxConcurrentDeviceRead,
59
+ this.maxConcurrentSinglePointRead, this.concurrentTaskDelay
60
+ );
61
+
62
+ this.job.addJob({
63
+ id: jobId,
64
+ task: task,
65
+ priority: Number.isFinite(msg.priority) ? msg.priority : 5,
66
+ });
67
+ // @ts-ignore
68
+ this.status({ fill: 'yellow', shape: 'dot', text: `in queue` });
69
+ });
70
+
71
+ this.#eventEmitter.on(EVENT_OUTPUT, (data) => {
72
+ const msg = {
73
+ payload: data
74
+ };
75
+ // @ts-ignore
76
+ this.send(msg);
77
+ });
78
+
79
+ this.#eventEmitter.on(EVENT_UPDATE_STATUS, (msg) => {
80
+ if (msg === 100)
81
+ // @ts-ignore
82
+ this.status({ fill: 'green', shape: 'dot', text: `completed: ${nowFormatted()}` });
83
+ else
84
+ // @ts-ignore
85
+ this.status({ fill: 'yellow', shape: 'dot', text: `progress: ${msg} %` });
86
+ });
87
+
88
+ this.#eventEmitter.on(EVENT_ERROR, (err) => {
89
+ // @ts-ignore
90
+ this.error(err);
91
+ });
92
+ }
93
+ }
94
+
95
+ // ----- register node -----
96
+ RED.nodes.registerType('discover point', DiscoverPoint);
97
+ }
@@ -1,97 +1,101 @@
1
- 'use strict';
2
- require('./_alias.js');
3
-
4
- const EventEmitter = require('events');
5
-
6
- const { print } = require('@root/common/core/util.js')
7
- const { nowFormatted } = require('@root/common/core/util.js')
8
- const { ReadPointJob } = require('@root/common/job/read_point.js')
9
- const { getQueue } = require('@root/common/job/global_queue_manager.js')
10
- const {
11
- EVENT_UPDATE_STATUS, EVENT_ERROR, EVENT_INPUT, EVENT_OUTPUT
12
- } = require('@root/common/core/constant.js')
13
-
14
-
15
- // -------------------------------- functions --------------------------------
16
- module.exports = function (RED) {
17
- class ReadPoint {
18
- #eventEmitter = new EventEmitter();
19
-
20
- constructor(config) {
21
- RED.nodes.createNode(this, config);
22
-
23
- // get config
24
- this.client = RED.nodes.getNode(config.client).instance;
25
- this.readMethod = +config.readMethod
26
- this.maxConcurrentDeviceRead = +config.maxConcurrentDeviceRead
27
- this.maxConcurrentSinglePointRead = +config.maxConcurrentSinglePointRead
28
- this.concurrentTaskDelay = +config.concurrentTaskDelay
29
-
30
- // events
31
- this.#subscribeListeners();
32
-
33
- // configure job queue - use global queue
34
- this.job = getQueue();
35
- }
36
-
37
- #subscribeListeners() {
38
- /**
39
- * @param {Object} msg - The message object.
40
- */
41
- // @ts-ignore
42
- this.on(EVENT_INPUT, async function (msg) {
43
- const task = new ReadPointJob(
44
- this.client,
45
- this.#eventEmitter,
46
- msg.devices,
47
- msg.points,
48
- this.readMethod,
49
- this.maxConcurrentDeviceRead,
50
- this.maxConcurrentSinglePointRead,
51
- this.concurrentTaskDelay
52
- );
53
-
54
- const jobId = (typeof msg.id === 'string' || typeof msg.id === 'number') ? msg.id : 'readPoints';
55
-
56
- if (this.job.queue.map(item => item.id).includes(jobId)) {
57
- // @ts-ignore
58
- print(`Coalesced job: ${jobId}`, true)
59
- return;
60
- }
61
-
62
- this.job.addJob({
63
- id: jobId,
64
- task: task,
65
- priority: Number.isFinite(msg.priority) ? msg.priority : 2,
66
- });
67
- // @ts-ignore
68
- this.status({ fill: 'yellow', shape: 'dot', text: `in queue` });
69
- });
70
-
71
- this.#eventEmitter.on(EVENT_OUTPUT, (data) => {
72
- const msg = {
73
- payload: data
74
- };
75
- // @ts-ignore
76
- this.send(msg);
77
- });
78
-
79
- this.#eventEmitter.on(EVENT_UPDATE_STATUS, (msg) => {
80
- if (msg === 100)
81
- // @ts-ignore
82
- this.status({ fill: 'green', shape: 'dot', text: `completed: ${nowFormatted()}` });
83
- else
84
- // @ts-ignore
85
- this.status({ fill: 'yellow', shape: 'dot', text: `progress: ${msg} %` });
86
- });
87
-
88
- this.#eventEmitter.on(EVENT_ERROR, (err) => {
89
- // @ts-ignore
90
- this.error(err);
91
- });
92
- }
93
- }
94
-
95
- // ----- register node -----
96
- RED.nodes.registerType('read point', ReadPoint);
97
- }
1
+ 'use strict';
2
+ require('./_alias.js');
3
+
4
+ const EventEmitter = require('events');
5
+
6
+ const { print } = require('@root/common/core/util.js')
7
+ const { nowFormatted } = require('@root/common/core/util.js')
8
+ const { ReadPointJob } = require('@root/common/job/read_point.js')
9
+ const { getQueue } = require('@root/common/job/global_queue_manager.js')
10
+ const { generateUniqueJobId } = require('@root/common/func.js')
11
+ const {
12
+ EVENT_UPDATE_STATUS, EVENT_ERROR, EVENT_INPUT, EVENT_OUTPUT
13
+ } = require('@root/common/core/constant.js')
14
+
15
+
16
+ // -------------------------------- functions --------------------------------
17
+ module.exports = function (RED) {
18
+ class ReadPoint {
19
+ #eventEmitter = new EventEmitter();
20
+
21
+ constructor(config) {
22
+ RED.nodes.createNode(this, config);
23
+
24
+ // get config
25
+ this.client = RED.nodes.getNode(config.client).instance;
26
+ this.readMethod = +config.readMethod
27
+ this.maxConcurrentDeviceRead = +config.maxConcurrentDeviceRead
28
+ this.maxConcurrentSinglePointRead = +config.maxConcurrentSinglePointRead
29
+ this.concurrentTaskDelay = +config.concurrentTaskDelay
30
+
31
+ // @ts-ignore
32
+ this.jobId = generateUniqueJobId(this.id, 'readPoints');
33
+
34
+ // events
35
+ this.#subscribeListeners();
36
+
37
+ // configure job queue - use global queue
38
+ this.job = getQueue();
39
+ }
40
+
41
+ #subscribeListeners() {
42
+ /**
43
+ * @param {Object} msg - The message object.
44
+ */
45
+ // @ts-ignore
46
+ this.on(EVENT_INPUT, async function (msg) {
47
+ const task = new ReadPointJob(
48
+ this.client,
49
+ this.#eventEmitter,
50
+ msg.devices,
51
+ msg.points,
52
+ this.readMethod,
53
+ this.maxConcurrentDeviceRead,
54
+ this.maxConcurrentSinglePointRead,
55
+ this.concurrentTaskDelay
56
+ );
57
+
58
+ const jobId = (typeof msg.id === 'string' || typeof msg.id === 'number') ? msg.id : this.jobId;
59
+
60
+ if (this.job.queue.map(item => item.id).includes(jobId)) {
61
+ // @ts-ignore
62
+ print(`Coalesced job: ${jobId}`, true)
63
+ return;
64
+ }
65
+
66
+ this.job.addJob({
67
+ id: jobId,
68
+ task: task,
69
+ priority: Number.isFinite(msg.priority) ? msg.priority : 2,
70
+ });
71
+ // @ts-ignore
72
+ this.status({ fill: 'yellow', shape: 'dot', text: `in queue` });
73
+ });
74
+
75
+ this.#eventEmitter.on(EVENT_OUTPUT, (data) => {
76
+ const msg = {
77
+ payload: data
78
+ };
79
+ // @ts-ignore
80
+ this.send(msg);
81
+ });
82
+
83
+ this.#eventEmitter.on(EVENT_UPDATE_STATUS, (msg) => {
84
+ if (msg === 100)
85
+ // @ts-ignore
86
+ this.status({ fill: 'green', shape: 'dot', text: `completed: ${nowFormatted()}` });
87
+ else
88
+ // @ts-ignore
89
+ this.status({ fill: 'yellow', shape: 'dot', text: `progress: ${msg} %` });
90
+ });
91
+
92
+ this.#eventEmitter.on(EVENT_ERROR, (err) => {
93
+ // @ts-ignore
94
+ this.error(err);
95
+ });
96
+ }
97
+ }
98
+
99
+ // ----- register node -----
100
+ RED.nodes.registerType('read point', ReadPoint);
101
+ }
@@ -1,95 +1,99 @@
1
- 'use strict';
2
- require('./_alias.js');
3
-
4
- const EventEmitter = require('events');
5
-
6
- const { print } = require('@root/common/core/util.js')
7
- const { nowFormatted } = require('@root/common/core/util.js')
8
- const { WritePointJob } = require('@root/common/job/write_point.js')
9
- const { getQueue } = require('@root/common/job/global_queue_manager.js')
10
- const {
11
- EVENT_UPDATE_STATUS, EVENT_ERROR, EVENT_INPUT, EVENT_OUTPUT
12
- } = require('@root/common/core/constant.js')
13
-
14
-
15
- // -------------------------------- functions --------------------------------
16
- module.exports = function (RED) {
17
- class WritePoint {
18
- #eventEmitter = new EventEmitter();
19
-
20
- constructor(config) {
21
- RED.nodes.createNode(this, config);
22
-
23
- // get config
24
- this.client = RED.nodes.getNode(config.client).instance;
25
- this.maxConcurrentDeviceWrite = +config.maxConcurrentDeviceWrite
26
- this.maxConcurrentPointWrite = +config.maxConcurrentPointWrite
27
- this.concurrentTaskDelay = +config.concurrentTaskDelay
28
-
29
- // events
30
- this.#subscribeListeners();
31
-
32
- // configure job queue - use global queue
33
- this.job = getQueue();
34
- }
35
-
36
- #subscribeListeners() {
37
- /**
38
- * @param {Object} msg - The message object.
39
- */
40
- // @ts-ignore
41
- this.on(EVENT_INPUT, async function (msg) {
42
- const task = new WritePointJob(
43
- this.client,
44
- this.#eventEmitter,
45
- msg.devices,
46
- msg.points,
47
- msg.writePoints,
48
- this.maxConcurrentDeviceWrite,
49
- this.maxConcurrentPointWrite,
50
- this.concurrentTaskDelay
51
- );
52
- const jobId = (typeof msg.id === 'string' || typeof msg.id === 'number') ? msg.id : 'writePoints';
53
-
54
- if (this.job.queue.map(item => item.id).includes(jobId)) {
55
- // @ts-ignore
56
- print(`Coalesced job: ${jobId}`, true)
57
- return;
58
- }
59
-
60
- this.job.addJob({
61
- id: jobId,
62
- task: task,
63
- priority: Number.isFinite(msg.priority) ? msg.priority : 2,
64
- });
65
- // @ts-ignore
66
- this.status({ fill: 'yellow', shape: 'dot', text: `in queue` });
67
- });
68
-
69
- this.#eventEmitter.on(EVENT_OUTPUT, (data) => {
70
- const msg = {
71
- payload: data
72
- };
73
- // @ts-ignore
74
- this.send(msg);
75
- });
76
-
77
- this.#eventEmitter.on(EVENT_UPDATE_STATUS, (msg) => {
78
- if (msg === 100)
79
- // @ts-ignore
80
- this.status({ fill: 'green', shape: 'dot', text: `completed: ${nowFormatted()}` });
81
- else
82
- // @ts-ignore
83
- this.status({ fill: 'yellow', shape: 'dot', text: `progress: ${msg} %` });
84
- });
85
-
86
- this.#eventEmitter.on(EVENT_ERROR, (err) => {
87
- // @ts-ignore
88
- this.error(err);
89
- });
90
- }
91
- }
92
-
93
- // ----- register node -----
94
- RED.nodes.registerType('write point', WritePoint);
95
- }
1
+ 'use strict';
2
+ require('./_alias.js');
3
+
4
+ const EventEmitter = require('events');
5
+
6
+ const { print } = require('@root/common/core/util.js')
7
+ const { nowFormatted } = require('@root/common/core/util.js')
8
+ const { WritePointJob } = require('@root/common/job/write_point.js')
9
+ const { getQueue } = require('@root/common/job/global_queue_manager.js')
10
+ const { generateUniqueJobId } = require('@root/common/func.js')
11
+ const {
12
+ EVENT_UPDATE_STATUS, EVENT_ERROR, EVENT_INPUT, EVENT_OUTPUT
13
+ } = require('@root/common/core/constant.js')
14
+
15
+
16
+ // -------------------------------- functions --------------------------------
17
+ module.exports = function (RED) {
18
+ class WritePoint {
19
+ #eventEmitter = new EventEmitter();
20
+
21
+ constructor(config) {
22
+ RED.nodes.createNode(this, config);
23
+
24
+ // get config
25
+ this.client = RED.nodes.getNode(config.client).instance;
26
+ this.maxConcurrentDeviceWrite = +config.maxConcurrentDeviceWrite
27
+ this.maxConcurrentPointWrite = +config.maxConcurrentPointWrite
28
+ this.concurrentTaskDelay = +config.concurrentTaskDelay
29
+
30
+ // @ts-ignore
31
+ this.jobId = generateUniqueJobId(this.id, 'writePoints');
32
+
33
+ // events
34
+ this.#subscribeListeners();
35
+
36
+ // configure job queue - use global queue
37
+ this.job = getQueue();
38
+ }
39
+
40
+ #subscribeListeners() {
41
+ /**
42
+ * @param {Object} msg - The message object.
43
+ */
44
+ // @ts-ignore
45
+ this.on(EVENT_INPUT, async function (msg) {
46
+ const task = new WritePointJob(
47
+ this.client,
48
+ this.#eventEmitter,
49
+ msg.devices,
50
+ msg.points,
51
+ msg.writePoints,
52
+ this.maxConcurrentDeviceWrite,
53
+ this.maxConcurrentPointWrite,
54
+ this.concurrentTaskDelay
55
+ );
56
+ const jobId = (typeof msg.id === 'string' || typeof msg.id === 'number') ? msg.id : this.jobId;
57
+
58
+ if (this.job.queue.map(item => item.id).includes(jobId)) {
59
+ // @ts-ignore
60
+ print(`Coalesced job: ${jobId}`, true)
61
+ return;
62
+ }
63
+
64
+ this.job.addJob({
65
+ id: jobId,
66
+ task: task,
67
+ priority: Number.isFinite(msg.priority) ? msg.priority : 2,
68
+ });
69
+ // @ts-ignore
70
+ this.status({ fill: 'yellow', shape: 'dot', text: `in queue` });
71
+ });
72
+
73
+ this.#eventEmitter.on(EVENT_OUTPUT, (data) => {
74
+ const msg = {
75
+ payload: data
76
+ };
77
+ // @ts-ignore
78
+ this.send(msg);
79
+ });
80
+
81
+ this.#eventEmitter.on(EVENT_UPDATE_STATUS, (msg) => {
82
+ if (msg === 100)
83
+ // @ts-ignore
84
+ this.status({ fill: 'green', shape: 'dot', text: `completed: ${nowFormatted()}` });
85
+ else
86
+ // @ts-ignore
87
+ this.status({ fill: 'yellow', shape: 'dot', text: `progress: ${msg} %` });
88
+ });
89
+
90
+ this.#eventEmitter.on(EVENT_ERROR, (err) => {
91
+ // @ts-ignore
92
+ this.error(err);
93
+ });
94
+ }
95
+ }
96
+
97
+ // ----- register node -----
98
+ RED.nodes.registerType('write point', WritePoint);
99
+ }