@iobroker/modbus 7.0.2
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/LICENSE +21 -0
- package/README.md +40 -0
- package/build/convert.d.ts +2 -0
- package/build/convert.js +102 -0
- package/build/convert.js.map +1 -0
- package/build/index.d.ts +88 -0
- package/build/index.js +1005 -0
- package/build/index.js.map +1 -0
- package/build/lib/Master.d.ts +27 -0
- package/build/lib/Master.js +811 -0
- package/build/lib/Master.js.map +1 -0
- package/build/lib/Put.d.ts +19 -0
- package/build/lib/Put.js +113 -0
- package/build/lib/Put.js.map +1 -0
- package/build/lib/Slave.d.ts +15 -0
- package/build/lib/Slave.js +545 -0
- package/build/lib/Slave.js.map +1 -0
- package/build/lib/common.d.ts +3 -0
- package/build/lib/common.js +371 -0
- package/build/lib/common.js.map +1 -0
- package/build/lib/crc16modbus.d.ts +1 -0
- package/build/lib/crc16modbus.js +33 -0
- package/build/lib/crc16modbus.js.map +1 -0
- package/build/lib/jsmodbus/modbus-client-core.d.ts +78 -0
- package/build/lib/jsmodbus/modbus-client-core.js +528 -0
- package/build/lib/jsmodbus/modbus-client-core.js.map +1 -0
- package/build/lib/jsmodbus/modbus-server-core.d.ts +33 -0
- package/build/lib/jsmodbus/modbus-server-core.js +363 -0
- package/build/lib/jsmodbus/modbus-server-core.js.map +1 -0
- package/build/lib/jsmodbus/transports/errors.d.ts +7 -0
- package/build/lib/jsmodbus/transports/errors.js +40 -0
- package/build/lib/jsmodbus/transports/errors.js.map +1 -0
- package/build/lib/jsmodbus/transports/modbus-client-serial.d.ts +23 -0
- package/build/lib/jsmodbus/transports/modbus-client-serial.js +154 -0
- package/build/lib/jsmodbus/transports/modbus-client-serial.js.map +1 -0
- package/build/lib/jsmodbus/transports/modbus-client-tcp-rtu.d.ts +24 -0
- package/build/lib/jsmodbus/transports/modbus-client-tcp-rtu.js +166 -0
- package/build/lib/jsmodbus/transports/modbus-client-tcp-rtu.js.map +1 -0
- package/build/lib/jsmodbus/transports/modbus-client-tcp-ssl.d.ts +34 -0
- package/build/lib/jsmodbus/transports/modbus-client-tcp-ssl.js +138 -0
- package/build/lib/jsmodbus/transports/modbus-client-tcp-ssl.js.map +1 -0
- package/build/lib/jsmodbus/transports/modbus-client-tcp.d.ts +27 -0
- package/build/lib/jsmodbus/transports/modbus-client-tcp.js +123 -0
- package/build/lib/jsmodbus/transports/modbus-client-tcp.js.map +1 -0
- package/build/lib/jsmodbus/transports/modbus-server-serial.d.ts +29 -0
- package/build/lib/jsmodbus/transports/modbus-server-serial.js +206 -0
- package/build/lib/jsmodbus/transports/modbus-server-serial.js.map +1 -0
- package/build/lib/jsmodbus/transports/modbus-server-tcp.d.ts +25 -0
- package/build/lib/jsmodbus/transports/modbus-server-tcp.js +112 -0
- package/build/lib/jsmodbus/transports/modbus-server-tcp.js.map +1 -0
- package/build/lib/loggingUtils.d.ts +11 -0
- package/build/lib/loggingUtils.js +37 -0
- package/build/lib/loggingUtils.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,811 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Master = void 0;
|
|
7
|
+
const common_1 = require("./common");
|
|
8
|
+
const modbus_client_serial_1 = __importDefault(require("./jsmodbus/transports/modbus-client-serial"));
|
|
9
|
+
const modbus_client_tcp_1 = __importDefault(require("./jsmodbus/transports/modbus-client-tcp"));
|
|
10
|
+
const modbus_client_tcp_rtu_1 = __importDefault(require("./jsmodbus/transports/modbus-client-tcp-rtu"));
|
|
11
|
+
const modbus_client_tcp_ssl_1 = __importDefault(require("./jsmodbus/transports/modbus-client-tcp-ssl"));
|
|
12
|
+
const loggingUtils_1 = require("./loggingUtils");
|
|
13
|
+
class Master {
|
|
14
|
+
modbusClient;
|
|
15
|
+
connected = false;
|
|
16
|
+
connectTimer;
|
|
17
|
+
nextPoll;
|
|
18
|
+
pollTime;
|
|
19
|
+
errorCount = 0;
|
|
20
|
+
ackObjects = {};
|
|
21
|
+
objects;
|
|
22
|
+
isStop = false;
|
|
23
|
+
pulseList = {};
|
|
24
|
+
sendBuffer = {};
|
|
25
|
+
devices;
|
|
26
|
+
deviceIds;
|
|
27
|
+
reconnectTimeout;
|
|
28
|
+
keepAliveTimeout;
|
|
29
|
+
adapter;
|
|
30
|
+
options;
|
|
31
|
+
scaleFactors = {};
|
|
32
|
+
showDebug;
|
|
33
|
+
constructor(options, adapter) {
|
|
34
|
+
this.adapter = adapter;
|
|
35
|
+
this.options = options;
|
|
36
|
+
this.devices = options.devices;
|
|
37
|
+
this.objects = options.objects;
|
|
38
|
+
this.deviceIds = Object.keys(options.devices).map(id => parseInt(id, 10));
|
|
39
|
+
this.showDebug = adapter.common?.loglevel === 'debug' || adapter.common?.loglevel === 'silly';
|
|
40
|
+
void adapter.setState('info.connection', false, true);
|
|
41
|
+
// delete all server connection information
|
|
42
|
+
void adapter.getStatesAsync('info.clients.*').then(async (states) => {
|
|
43
|
+
for (const id in states) {
|
|
44
|
+
await adapter.delForeignObjectAsync(id);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
if (options.config.type === 'tcp') {
|
|
48
|
+
const tcp = options.config.tcp;
|
|
49
|
+
if (!tcp || !tcp.bind || tcp.bind === '0.0.0.0') {
|
|
50
|
+
adapter.log.error('IP address is not defined');
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const logWrapper = (0, loggingUtils_1.createLoggingWrapper)(adapter.log, options.config.disableLogging);
|
|
55
|
+
this.modbusClient = new modbus_client_tcp_1.default({
|
|
56
|
+
tcp: {
|
|
57
|
+
host: tcp.bind,
|
|
58
|
+
port: tcp.port || 502,
|
|
59
|
+
autoReconnect: false,
|
|
60
|
+
},
|
|
61
|
+
logger: logWrapper,
|
|
62
|
+
timeout: options.config.timeout,
|
|
63
|
+
unitId: options.config.defaultDeviceId,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
adapter.log.error(`Cannot connect to "${tcp.bind}:${tcp.port || 502}": ${e}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (options.config.type === 'tcp-ssl') {
|
|
71
|
+
const tcp = options.config.tcp;
|
|
72
|
+
if (!tcp || !tcp.bind || tcp.bind === '0.0.0.0') {
|
|
73
|
+
adapter.log.error('IP address is not defined');
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const logWrapper = (0, loggingUtils_1.createLoggingWrapper)(adapter.log, options.config.disableLogging);
|
|
78
|
+
if (!options.config.ssl?.cert || !options.config.ssl?.key) {
|
|
79
|
+
adapter.log.error('SSL certificate or key is not defined');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// Prepare SSL configuration
|
|
83
|
+
const sslConfig = {
|
|
84
|
+
rejectUnauthorized: options.config.ssl?.rejectUnauthorized !== false,
|
|
85
|
+
cert: options.config.ssl.cert,
|
|
86
|
+
key: options.config.ssl.key,
|
|
87
|
+
ca: options.config.ssl.ca,
|
|
88
|
+
};
|
|
89
|
+
this.modbusClient = new modbus_client_tcp_ssl_1.default({
|
|
90
|
+
tcp: {
|
|
91
|
+
host: tcp.bind,
|
|
92
|
+
port: tcp.port || 502,
|
|
93
|
+
autoReconnect: false,
|
|
94
|
+
},
|
|
95
|
+
ssl: sslConfig,
|
|
96
|
+
logger: logWrapper,
|
|
97
|
+
timeout: options.config.timeout,
|
|
98
|
+
unitId: options.config.defaultDeviceId,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
adapter.log.error(`Cannot connect to SSL "${tcp.bind}:${tcp.port || 502}": ${e}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else if (options.config.type === 'tcprtu') {
|
|
106
|
+
const tcp = options.config.tcp;
|
|
107
|
+
if (!tcp || !tcp.bind || tcp.bind === '0.0.0.0') {
|
|
108
|
+
adapter.log.error('IP address is not defined');
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
const logWrapper = (0, loggingUtils_1.createLoggingWrapper)(adapter.log, options.config.disableLogging);
|
|
113
|
+
this.modbusClient = new modbus_client_tcp_rtu_1.default({
|
|
114
|
+
tcp: {
|
|
115
|
+
host: tcp.bind,
|
|
116
|
+
port: tcp.port || 502,
|
|
117
|
+
autoReconnect: false,
|
|
118
|
+
},
|
|
119
|
+
logger: logWrapper,
|
|
120
|
+
timeout: options.config.timeout,
|
|
121
|
+
unitId: options.config.defaultDeviceId,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
catch (e) {
|
|
125
|
+
adapter.log.error(`Cannot connect to "${tcp.bind}:${tcp.port || 502}": ${e}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else if (options.config.type === 'serial') {
|
|
129
|
+
const serial = options.config.serial;
|
|
130
|
+
if (!serial || !serial.comName) {
|
|
131
|
+
adapter.log.error('Serial device name is not defined');
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const logWrapper = (0, loggingUtils_1.createLoggingWrapper)(adapter.log, options.config.disableLogging);
|
|
136
|
+
this.modbusClient = new modbus_client_serial_1.default({
|
|
137
|
+
serial: {
|
|
138
|
+
portName: serial.comName,
|
|
139
|
+
baudRate: serial.baudRate || 9600,
|
|
140
|
+
dataBits: serial.dataBits || 8,
|
|
141
|
+
stopBits: serial.stopBits || 1,
|
|
142
|
+
parity: serial.parity || 'none',
|
|
143
|
+
},
|
|
144
|
+
logger: logWrapper,
|
|
145
|
+
timeout: options.config.timeout,
|
|
146
|
+
unitId: options.config.multiDeviceId ? undefined : options.config.defaultDeviceId,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
catch (e) {
|
|
150
|
+
adapter.log.error(`Cannot open port "${serial.comName}" [${serial.baudRate || 9600}]: ${e}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
adapter.log.error(`Unsupported type ${options.config.type}"`);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (!this.modbusClient) {
|
|
158
|
+
adapter.log.error('Cannot create modbus master!');
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
this.modbusClient
|
|
162
|
+
.on('connect', () => {
|
|
163
|
+
if (!this.connected) {
|
|
164
|
+
if (options.config.type === 'tcp') {
|
|
165
|
+
adapter.log.info(`Connected to slave ${options.config.tcp?.bind}`);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
adapter.log.info('Connected to slave');
|
|
169
|
+
}
|
|
170
|
+
this.connected = true;
|
|
171
|
+
void this.adapter.setState('info.connection', true, true);
|
|
172
|
+
}
|
|
173
|
+
if (this.nextPoll) {
|
|
174
|
+
adapter.clearTimeout(this.nextPoll);
|
|
175
|
+
this.nextPoll = null;
|
|
176
|
+
}
|
|
177
|
+
void this.#poll().catch(err => this.adapter.log.error(err));
|
|
178
|
+
this.keepAliveTimeout && adapter.clearTimeout(this.keepAliveTimeout);
|
|
179
|
+
this.keepAliveTimeout = adapter.setTimeout(() => {
|
|
180
|
+
this.keepAliveTimeout = undefined;
|
|
181
|
+
this.#pollBinariesBlockWithKeepAlive();
|
|
182
|
+
}, options.config.keepAliveInterval || 1000);
|
|
183
|
+
})
|
|
184
|
+
.on('disconnect', () => {
|
|
185
|
+
if (this.isStop) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
this.reconnectTimeout ||= adapter.setTimeout(() => this.#reconnect(), 1000);
|
|
189
|
+
});
|
|
190
|
+
this.modbusClient.on('close', () => {
|
|
191
|
+
if (this.isStop) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
this.reconnectTimeout = adapter.setTimeout(() => this.#reconnect(), 1000);
|
|
195
|
+
});
|
|
196
|
+
this.modbusClient.on('error', err => {
|
|
197
|
+
if (this.isStop) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (err.code === 'ECONNREFUSED') {
|
|
201
|
+
adapter.log.warn(`Connection refused ${err.address}:${err.port}`);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
adapter.log.warn(`On error: ${JSON.stringify(err)}`);
|
|
205
|
+
}
|
|
206
|
+
this.reconnectTimeout = adapter.setTimeout(() => this.#reconnect(), 1000);
|
|
207
|
+
});
|
|
208
|
+
this.modbusClient.on('trashCurrentRequest', err => {
|
|
209
|
+
if (this.isStop) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
adapter.log.warn(`Error: ${JSON.stringify(err)}`);
|
|
213
|
+
this.reconnectTimeout = adapter.setTimeout(() => this.#reconnect(), 1000);
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
#waitAsync(ms) {
|
|
217
|
+
if (!ms) {
|
|
218
|
+
return Promise.resolve();
|
|
219
|
+
}
|
|
220
|
+
return new Promise(resolve => this.adapter.setTimeout(resolve, ms));
|
|
221
|
+
}
|
|
222
|
+
#reconnect(isImmediately) {
|
|
223
|
+
if (this.reconnectTimeout) {
|
|
224
|
+
this.adapter.clearTimeout(this.reconnectTimeout);
|
|
225
|
+
this.reconnectTimeout = null;
|
|
226
|
+
}
|
|
227
|
+
if (this.nextPoll) {
|
|
228
|
+
this.adapter.clearTimeout(this.nextPoll);
|
|
229
|
+
this.nextPoll = null;
|
|
230
|
+
}
|
|
231
|
+
if (this.keepAliveTimeout) {
|
|
232
|
+
this.adapter.clearTimeout(this.keepAliveTimeout);
|
|
233
|
+
this.keepAliveTimeout = null;
|
|
234
|
+
}
|
|
235
|
+
try {
|
|
236
|
+
this.modbusClient?.close();
|
|
237
|
+
}
|
|
238
|
+
catch (e) {
|
|
239
|
+
this.adapter.log.error(`Cannot close master: ${e}`);
|
|
240
|
+
}
|
|
241
|
+
if (this.connected) {
|
|
242
|
+
if (this.options.config.tcp) {
|
|
243
|
+
this.adapter.log.info(`Disconnected from slave ${this.options.config.tcp?.bind}`);
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
this.adapter.log.info('Disconnected from slave');
|
|
247
|
+
}
|
|
248
|
+
this.connected = false;
|
|
249
|
+
void this.adapter.setState('info.connection', false, true);
|
|
250
|
+
}
|
|
251
|
+
this.connectTimer ||= this.adapter.setTimeout(() => {
|
|
252
|
+
this.connectTimer = null;
|
|
253
|
+
if (typeof this.modbusClient?.connect === 'function') {
|
|
254
|
+
this.modbusClient.connect();
|
|
255
|
+
}
|
|
256
|
+
}, isImmediately ? 1000 : this.options.config.recon);
|
|
257
|
+
}
|
|
258
|
+
async #pollBinariesBlock(device, regType, block) {
|
|
259
|
+
const regs = device[regType];
|
|
260
|
+
const regBlock = regs.blocks[block];
|
|
261
|
+
if (regBlock.startIndex === regBlock.endIndex) {
|
|
262
|
+
regBlock.endIndex++;
|
|
263
|
+
}
|
|
264
|
+
if (this.showDebug) {
|
|
265
|
+
this.adapter.log.debug(`[DevID_${regs.deviceId}/${regType}] Poll address ${regBlock.start} - ${regBlock.count} bits`);
|
|
266
|
+
}
|
|
267
|
+
if (this.modbusClient) {
|
|
268
|
+
let response;
|
|
269
|
+
try {
|
|
270
|
+
if (regType === 'disInputs') {
|
|
271
|
+
response = await this.modbusClient.readDiscreteInputs(regs.deviceId, regBlock.start, regBlock.count);
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
response = await this.modbusClient.readCoils(regs.deviceId, regBlock.start, regBlock.count);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
catch (err) {
|
|
278
|
+
const errorMsg = `[DevID_${regs.deviceId}/${regType}] Block ${regBlock.start}-${regBlock.start + regBlock.count - 1}: ${JSON.stringify(err)}`;
|
|
279
|
+
this.adapter.log.warn(errorMsg);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (response.data?.length) {
|
|
283
|
+
for (let n = regBlock.startIndex; n < regBlock.endIndex; n++) {
|
|
284
|
+
const id = regs.config[n].id;
|
|
285
|
+
const val = response.data[regs.config[n].address - regBlock.start];
|
|
286
|
+
if (this.options.config.alwaysUpdate ||
|
|
287
|
+
this.ackObjects[id] === undefined ||
|
|
288
|
+
this.ackObjects[id].val !== val) {
|
|
289
|
+
this.ackObjects[id] = { val };
|
|
290
|
+
void this.adapter.setState(id, val, true, err => {
|
|
291
|
+
// analyze if the state could be set (because of permissions)
|
|
292
|
+
err && this.adapter.log.error(`Can not set state ${id}: ${err}`);
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
this.adapter.log.warn(`Null buffer length for ${regType} ${regBlock.start}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
else {
|
|
302
|
+
this.adapter.log.debug(`Poll canceled, because no connection`);
|
|
303
|
+
throw new Error('No connection');
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
#pollBinariesBlockWithKeepAlive() {
|
|
307
|
+
if (this.options.config.keepAliveInterval > 0) {
|
|
308
|
+
if (this.keepAliveTimeout) {
|
|
309
|
+
this.keepAliveTimeout && this.adapter.clearTimeout(this.keepAliveTimeout);
|
|
310
|
+
this.keepAliveTimeout = null;
|
|
311
|
+
}
|
|
312
|
+
if (this.modbusClient) {
|
|
313
|
+
this.modbusClient
|
|
314
|
+
.readDiscreteInputs(this.deviceIds[0], 0, 1)
|
|
315
|
+
.then(response => this.adapter.log.silly(`Keep alive response = ${JSON.stringify(response)}`))
|
|
316
|
+
.catch(() => { });
|
|
317
|
+
this.keepAliveTimeout = this.adapter.setTimeout(() => {
|
|
318
|
+
this.keepAliveTimeout = null;
|
|
319
|
+
this.#pollBinariesBlockWithKeepAlive();
|
|
320
|
+
}, this.options.config.keepAliveInterval);
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
this.adapter.log.debug(`Poll canceled, because no connection`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
this.adapter.log.silly('Keepalive is disabled!');
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
async #pollBinariesBlocks(device, regType) {
|
|
331
|
+
const regs = device[regType];
|
|
332
|
+
for (let n = 0; n < regs.length; n++) {
|
|
333
|
+
if (this.connected && !this.isStop) {
|
|
334
|
+
await this.#pollBinariesBlock(device, regType, n);
|
|
335
|
+
await this.#waitAsync(this.options.config.readInterval);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
async #pollFloatBlock(device, regType, block) {
|
|
340
|
+
const regs = device[regType];
|
|
341
|
+
const regBlock = regs.blocks[block];
|
|
342
|
+
if (regBlock.startIndex === regBlock.endIndex) {
|
|
343
|
+
regBlock.endIndex++;
|
|
344
|
+
}
|
|
345
|
+
if (!this.scaleFactors[regs.deviceId]) {
|
|
346
|
+
this.adapter.log.debug('Initialization of scale factors done!');
|
|
347
|
+
this.scaleFactors[regs.deviceId] = {};
|
|
348
|
+
}
|
|
349
|
+
if (this.showDebug) {
|
|
350
|
+
this.adapter.log.debug(`[DevID_${regs.deviceId}/${regType}] Poll address ${regBlock.start} - ${regBlock.count} registers`);
|
|
351
|
+
}
|
|
352
|
+
if (this.modbusClient && this.connected && !this.isStop) {
|
|
353
|
+
let response;
|
|
354
|
+
try {
|
|
355
|
+
if (regType === 'inputRegs') {
|
|
356
|
+
response = await this.modbusClient.readInputRegisters(regs.deviceId, regBlock.start, regBlock.count);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
response = await this.modbusClient.readHoldingRegisters(regs.deviceId, regBlock.start, regBlock.count);
|
|
360
|
+
}
|
|
361
|
+
if (this.showDebug) {
|
|
362
|
+
this.adapter.log.debug(`[DevID_${regs.deviceId}/${regType}] Poll address ${regBlock.start} DONE`);
|
|
363
|
+
}
|
|
364
|
+
if (response.payload?.length) {
|
|
365
|
+
// first process all the scale factor values inside the block
|
|
366
|
+
for (let n = regBlock.startIndex; n < regBlock.endIndex; n++) {
|
|
367
|
+
if (regs.config[n].isScale) {
|
|
368
|
+
const prefixAddr = `DevID_${device.coils.deviceId}/${regType}/${regs.config[n]._address}`;
|
|
369
|
+
try {
|
|
370
|
+
let val = (0, common_1.extractValue)(regs.config[n].type, regs.config[n].len, response.payload, regs.config[n].address - regBlock.start);
|
|
371
|
+
const formula = regs.config[n].formula;
|
|
372
|
+
// If value must be calculated with formula
|
|
373
|
+
if (formula) {
|
|
374
|
+
if (this.showDebug) {
|
|
375
|
+
this.adapter.log.debug(`[${prefixAddr}] _Input Value = ${val}`);
|
|
376
|
+
this.adapter.log.debug(`[${prefixAddr}] _Formula = ${formula}`);
|
|
377
|
+
}
|
|
378
|
+
try {
|
|
379
|
+
const scaleAddress = parseInt(formula.substring(formula.indexOf('sf[') + 4));
|
|
380
|
+
if (scaleAddress !== null && !isNaN(scaleAddress)) {
|
|
381
|
+
this.adapter.log.warn(`[${prefixAddr}] Calculation of a scaleFactor which is based on another scaleFactor seems strange. Please check the config for address ${regs.config[n].address} !`);
|
|
382
|
+
}
|
|
383
|
+
// calculate value from formula or report an error
|
|
384
|
+
const func = new Function('x', 'sf', `return ${formula}`);
|
|
385
|
+
val = func(val, this.scaleFactors[regs.deviceId]);
|
|
386
|
+
if (typeof val === 'number') {
|
|
387
|
+
val =
|
|
388
|
+
Math.round(val * this.options.config.round) / this.options.config.round;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
catch (e) {
|
|
392
|
+
this.adapter.log.warn(`[${prefixAddr}] Calculation: eval(${formula}) not possible: ${e}`);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
else if (typeof val === 'number') {
|
|
396
|
+
// no formula used, so just scale with factor and offset
|
|
397
|
+
val = val * regs.config[n].factor + regs.config[n].offset;
|
|
398
|
+
val = Math.round(val * this.options.config.round) / this.options.config.round;
|
|
399
|
+
}
|
|
400
|
+
// store the finally calculated value as scaleFactor
|
|
401
|
+
this.scaleFactors[regs.deviceId][regs.config[n]._address] = val;
|
|
402
|
+
if (this.showDebug) {
|
|
403
|
+
this.adapter.log.debug(`[${prefixAddr}] Scale factor value stored from this address = ${val}`);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
catch (err) {
|
|
407
|
+
this.adapter.log.error(`Can not set value for [DevID_${regs.deviceId}]: ${err.message}`);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
// now process all values and store to the states
|
|
412
|
+
for (let n = regBlock.startIndex; n < regBlock.endIndex; n++) {
|
|
413
|
+
const id = regs.config[n].id;
|
|
414
|
+
try {
|
|
415
|
+
let val = (0, common_1.extractValue)(regs.config[n].type, regs.config[n].len, response.payload, regs.config[n].address - regBlock.start);
|
|
416
|
+
// If value must be calculated with formula
|
|
417
|
+
const prefixAddr = this.showDebug
|
|
418
|
+
? `DevID_${device.coils.deviceId}/${regType}/${regs.config[n]._address}`
|
|
419
|
+
: '';
|
|
420
|
+
if (regs.config[n].formula) {
|
|
421
|
+
if (this.showDebug) {
|
|
422
|
+
this.adapter.log.debug(`[${prefixAddr}] Input Value = ${val}`);
|
|
423
|
+
this.adapter.log.debug(`[${prefixAddr}] Formula = ${regs.config[n].formula}`);
|
|
424
|
+
}
|
|
425
|
+
try {
|
|
426
|
+
if (this.showDebug) {
|
|
427
|
+
// scaleAddress is used only for debug output
|
|
428
|
+
const m = regs.config[n].formula.match(/sf\[(['"`\d]+)]/g);
|
|
429
|
+
m?.forEach(sf => {
|
|
430
|
+
const num = sf.match(/\d+/);
|
|
431
|
+
if (num) {
|
|
432
|
+
const scaleAddress = parseInt(num[1]);
|
|
433
|
+
if (scaleAddress !== null && !isNaN(scaleAddress)) {
|
|
434
|
+
// it seems that the current formula uses a scaleFactor, therefore check the validity
|
|
435
|
+
if (this.showDebug) {
|
|
436
|
+
this.adapter.log.debug(`[${prefixAddr}] Scale factor address is = ${scaleAddress}`);
|
|
437
|
+
this.adapter.log.debug(`[${prefixAddr}] Scale factor address is inside current read range = ${scaleAddress > regBlock.start && scaleAddress < regBlock.start + regBlock.count}`);
|
|
438
|
+
}
|
|
439
|
+
// check if the scaleFactor address is in the current read block or outside this block
|
|
440
|
+
if (scaleAddress < regBlock.start ||
|
|
441
|
+
scaleAddress > regBlock.start + regBlock.count) {
|
|
442
|
+
// the scaleFactor address is not in the current read block. So it cannot be ensured that the values are in sync / valid
|
|
443
|
+
this.adapter.log.warn(`[DevID_${device.coils.deviceId}/${regType}/${regs.config[n]._address}] The current range for reading the values was from address ${regBlock.start} up to address ${regBlock.start + regBlock.count}!`);
|
|
444
|
+
this.adapter.log.warn(`[DevID_${device.coils.deviceId}/${regType}/${regs.config[n]._address}] Please make sure to configure the read process that both adresses are read in the same block!`);
|
|
445
|
+
this.adapter.log.warn(`[DevID_${device.coils.deviceId}/${regType}/${regs.config[n]._address}] The used scaleFactor from address ${scaleAddress} is not inside the same read block as the parameter on address ${regs.config[n].address}`);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
// calculate value from formula or report an error
|
|
452
|
+
const func = new Function('x', 'sf', `return ${regs.config[n].formula}`);
|
|
453
|
+
val = func(val, this.scaleFactors[regs.deviceId]);
|
|
454
|
+
if (this.showDebug) {
|
|
455
|
+
this.adapter.log.debug(`[${prefixAddr}] Calculation result = ${val}, type = ${typeof val}`);
|
|
456
|
+
}
|
|
457
|
+
// only do rounding in case the calculation result is a number
|
|
458
|
+
if (typeof val === 'number') {
|
|
459
|
+
val = Math.round(val * this.options.config.round) / this.options.config.round;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
catch (e) {
|
|
463
|
+
this.adapter.log.warn(`[DevID_${device.coils.deviceId}/${regType}/${regs.config[n]._address}] Calculation: eval(${regs.config[n].formula}) not possible: ${e}`);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
else if (typeof val === 'number') {
|
|
467
|
+
val = val * regs.config[n].factor + regs.config[n].offset;
|
|
468
|
+
val = Math.round(val * this.options.config.round) / this.options.config.round;
|
|
469
|
+
}
|
|
470
|
+
if (val !== null &&
|
|
471
|
+
(this.options.config.alwaysUpdate ||
|
|
472
|
+
this.ackObjects[id] === undefined ||
|
|
473
|
+
this.ackObjects[id].val !== val)) {
|
|
474
|
+
this.ackObjects[id] = { val };
|
|
475
|
+
void this.adapter.setState(id, val, true, err =>
|
|
476
|
+
// analyze if the state could be set (because of permissions)
|
|
477
|
+
err && this.adapter.log.error(`Can not set state ${id}: ${err}`));
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
catch (err) {
|
|
481
|
+
this.adapter.log.error(`Can not set value: ${err.message}`);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
this.adapter.log.warn(`Null buffer length for ${regType} ${regBlock.start}`);
|
|
487
|
+
}
|
|
488
|
+
// special case for cyclic write (cw)
|
|
489
|
+
if (this.options.config.maxBlock < 2 && regs.config[regBlock.startIndex].cw) {
|
|
490
|
+
// write immediately the current value
|
|
491
|
+
const fullId = regs.config[regBlock.startIndex].fullId;
|
|
492
|
+
await this.#writeFloatsReg(fullId);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
catch (err) {
|
|
496
|
+
const errorMsg = `[DevID_${regs.deviceId}/${regType}] Block ${regBlock.start}-${regBlock.start + regBlock.count - 1}: ${JSON.stringify(err)}`;
|
|
497
|
+
this.adapter.log.warn(errorMsg);
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
else {
|
|
502
|
+
this.adapter.log.debug(`Poll canceled, because no connection`);
|
|
503
|
+
throw new Error('No connection');
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
async #pollFloatsBlocks(device, regType) {
|
|
507
|
+
const regs = device[regType];
|
|
508
|
+
for (let n = 0; n < regs.blocks.length; n++) {
|
|
509
|
+
if (this.connected && !this.isStop) {
|
|
510
|
+
await this.#pollFloatBlock(device, regType, n);
|
|
511
|
+
await this.#waitAsync(this.options.config.readInterval);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
async #writeFloatsReg(fullId) {
|
|
516
|
+
const obj = this.objects[fullId];
|
|
517
|
+
if (obj?.native?.len) {
|
|
518
|
+
const id = obj._id.substring(this.adapter.namespace.length + 1);
|
|
519
|
+
if (!this.modbusClient || !this.connected || this.isStop) {
|
|
520
|
+
throw new Error('client disconnected');
|
|
521
|
+
}
|
|
522
|
+
if (this.ackObjects[id]) {
|
|
523
|
+
await this.#writeValue(id, this.ackObjects[id].val);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
async #writeFloatsRegs(device) {
|
|
528
|
+
const regs = device.holdingRegs;
|
|
529
|
+
if (regs.cyclicWrite) {
|
|
530
|
+
for (const fullId of regs.cyclicWrite) {
|
|
531
|
+
await this.#writeFloatsReg(fullId);
|
|
532
|
+
await this.#waitAsync(this.options.config.readInterval);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
#pollResult(startTime, deviceId, err) {
|
|
537
|
+
if (err) {
|
|
538
|
+
this.errorCount++;
|
|
539
|
+
this.adapter.log.warn(`[DevID_${deviceId}] Poll error count: ${this.errorCount} code: ${JSON.stringify(err)}`);
|
|
540
|
+
void this.adapter.setState('info.connection', false, true);
|
|
541
|
+
if (this.errorCount > 12 * this.deviceIds.length) {
|
|
542
|
+
// 2 re-connects did not help, restart adapter
|
|
543
|
+
this.adapter.log.error('Reconnect did not help, restart adapter');
|
|
544
|
+
typeof this.adapter.terminate === 'function' ? this.adapter.terminate(156) : process.exit(156);
|
|
545
|
+
}
|
|
546
|
+
else if (this.errorCount < 6 * this.deviceIds.length && this.connected) {
|
|
547
|
+
// tolerate up to 6 errors per device
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
else {
|
|
551
|
+
return new Error('disconnect');
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
else {
|
|
555
|
+
const currentPollTime = new Date().valueOf() - startTime;
|
|
556
|
+
if (this.pollTime !== undefined) {
|
|
557
|
+
if (Math.abs(this.pollTime - currentPollTime) > 100) {
|
|
558
|
+
this.pollTime = currentPollTime;
|
|
559
|
+
void this.adapter.setState('info.pollTime', currentPollTime, true);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
else {
|
|
563
|
+
this.pollTime = currentPollTime;
|
|
564
|
+
void this.adapter.setState('info.pollTime', currentPollTime, true);
|
|
565
|
+
}
|
|
566
|
+
if (this.errorCount > 0) {
|
|
567
|
+
void this.adapter.setState('info.connection', true, true);
|
|
568
|
+
this.errorCount = 0;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
async #pollDevice(device) {
|
|
573
|
+
this.adapter.log.debug(`[DevID_${device.coils.deviceId}] Poll start ---------------------`);
|
|
574
|
+
const startTime = new Date().valueOf();
|
|
575
|
+
// Track errors from each register type but continue polling
|
|
576
|
+
const pollErrors = [];
|
|
577
|
+
// Poll discrete inputs
|
|
578
|
+
try {
|
|
579
|
+
await this.#pollBinariesBlocks(device, 'disInputs');
|
|
580
|
+
}
|
|
581
|
+
catch (err) {
|
|
582
|
+
pollErrors.push(err);
|
|
583
|
+
}
|
|
584
|
+
// Poll coils
|
|
585
|
+
try {
|
|
586
|
+
await this.#pollBinariesBlocks(device, 'coils');
|
|
587
|
+
}
|
|
588
|
+
catch (err) {
|
|
589
|
+
pollErrors.push(err);
|
|
590
|
+
}
|
|
591
|
+
// Poll input registers
|
|
592
|
+
try {
|
|
593
|
+
await this.#pollFloatsBlocks(device, 'inputRegs');
|
|
594
|
+
}
|
|
595
|
+
catch (err) {
|
|
596
|
+
pollErrors.push(err);
|
|
597
|
+
}
|
|
598
|
+
// Poll holding registers
|
|
599
|
+
try {
|
|
600
|
+
await this.#pollFloatsBlocks(device, 'holdingRegs');
|
|
601
|
+
}
|
|
602
|
+
catch (err) {
|
|
603
|
+
pollErrors.push(err);
|
|
604
|
+
}
|
|
605
|
+
if (device.holdingRegs.cyclicWrite?.length && this.options.config.maxBlock >= 2) {
|
|
606
|
+
try {
|
|
607
|
+
await this.#writeFloatsRegs(device);
|
|
608
|
+
await this.#waitAsync(this.options.config.writeInterval);
|
|
609
|
+
}
|
|
610
|
+
catch (err) {
|
|
611
|
+
pollErrors.push(err);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
if (this.connected && !this.isStop) {
|
|
615
|
+
// If all polls failed, report error, otherwise report success
|
|
616
|
+
const allFailed = pollErrors.length === 4;
|
|
617
|
+
const error = allFailed ? pollErrors[0] : null; // Report first error if all failed
|
|
618
|
+
if (pollErrors.length && pollErrors.length < 4) {
|
|
619
|
+
this.adapter.log.warn(`[DevID_${device.coils.deviceId}] Some register types failed but continuing: ${pollErrors.length}/4 errors`);
|
|
620
|
+
}
|
|
621
|
+
this.#pollResult(startTime, device.coils.deviceId, error);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
async #poll() {
|
|
625
|
+
let anyError;
|
|
626
|
+
for (const id of this.deviceIds) {
|
|
627
|
+
try {
|
|
628
|
+
await this.#pollDevice(this.devices[id]);
|
|
629
|
+
}
|
|
630
|
+
catch (err) {
|
|
631
|
+
anyError = err;
|
|
632
|
+
}
|
|
633
|
+
await this.#waitAsync(this.options.config.waitTime);
|
|
634
|
+
}
|
|
635
|
+
if (anyError) {
|
|
636
|
+
if (!this.reconnectTimeout) {
|
|
637
|
+
this.#reconnect();
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
else {
|
|
641
|
+
this.nextPoll = this.adapter.setTimeout(() => {
|
|
642
|
+
this.nextPoll = null;
|
|
643
|
+
this.#poll().catch(e => this.adapter.log.error(`Cannot poll: ${e}`));
|
|
644
|
+
}, this.options.config.poll);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
async #writeValue(id, val) {
|
|
648
|
+
const obj = this.objects[id];
|
|
649
|
+
if (!obj || !this.modbusClient) {
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
const type = obj.native.regType;
|
|
653
|
+
try {
|
|
654
|
+
if (type === 'coils') {
|
|
655
|
+
if (val === 'true' || val === true) {
|
|
656
|
+
val = 1;
|
|
657
|
+
}
|
|
658
|
+
if (val === 'false' || val === false) {
|
|
659
|
+
val = 0;
|
|
660
|
+
}
|
|
661
|
+
val = parseFloat(val);
|
|
662
|
+
await this.modbusClient.writeSingleCoil(obj.native.deviceId, obj.native.address, !!val);
|
|
663
|
+
}
|
|
664
|
+
else if (type === 'holdingRegs') {
|
|
665
|
+
if (obj.native.float === undefined) {
|
|
666
|
+
obj.native.float =
|
|
667
|
+
obj.native.type === 'floatle' ||
|
|
668
|
+
obj.native.type === 'floatbe' ||
|
|
669
|
+
obj.native.type === 'floatsw' ||
|
|
670
|
+
obj.native.type === 'doublele' ||
|
|
671
|
+
obj.native.type === 'doublebe' ||
|
|
672
|
+
obj.native.type === 'floatsb';
|
|
673
|
+
}
|
|
674
|
+
if (!['string', 'stringle', 'string16', 'string16le', 'rawhex'].includes(obj.native.type)) {
|
|
675
|
+
val = parseFloat(val);
|
|
676
|
+
val = (val - obj.native.offset) / obj.native.factor;
|
|
677
|
+
if (!obj.native.float) {
|
|
678
|
+
val = Math.round(val);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
if (!obj.native.type) {
|
|
682
|
+
this.adapter.log.error('No type defined for write.');
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
// FC16
|
|
686
|
+
if (this.options.config.onlyUseWriteMultipleRegisters ||
|
|
687
|
+
(obj.native.len > 1 && !this.options.config.doNotUseWriteMultipleRegisters)) {
|
|
688
|
+
const hrBuffer = (0, common_1.writeValue)(obj.native.type, val, obj.native.len);
|
|
689
|
+
await this.modbusClient.writeMultipleRegisters(obj.native.deviceId, obj.native.address, hrBuffer);
|
|
690
|
+
}
|
|
691
|
+
else {
|
|
692
|
+
// FC06
|
|
693
|
+
const buffer = (0, common_1.writeValue)(obj.native.type, val, 1);
|
|
694
|
+
if (obj.native.len > 1) {
|
|
695
|
+
for (let r = 0; r < obj.native.len / 2; r++) {
|
|
696
|
+
const subBuffer = buffer.subarray(r * 2, r * 2 + 2);
|
|
697
|
+
await this.modbusClient.writeSingleRegister(obj.native.deviceId, obj.native.address + r, subBuffer);
|
|
698
|
+
await this.#waitAsync(this.options.config.writeInterval);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
else {
|
|
702
|
+
await this.modbusClient.writeSingleRegister(obj.native.deviceId, obj.native.address, buffer);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
if (this.showDebug) {
|
|
707
|
+
this.adapter.log.debug(`Write successfully [${obj.native.address}]: ${val}`);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
catch (err) {
|
|
711
|
+
this.adapter.log.warn(`Can not write value ${val}: ${err}`);
|
|
712
|
+
if (!this.isStop && !this.reconnectTimeout) {
|
|
713
|
+
this.#reconnect(true);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
async #send() {
|
|
718
|
+
if (!this.modbusClient) {
|
|
719
|
+
this.adapter.log.error('Client not connected');
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
const id = Object.keys(this.sendBuffer)[0];
|
|
723
|
+
await this.#writeValue(id, this.sendBuffer[id]);
|
|
724
|
+
delete this.sendBuffer[id];
|
|
725
|
+
if (Object.keys(this.sendBuffer).length) {
|
|
726
|
+
this.adapter.setTimeout(() => this.#send(), this.options.config.writeInterval || 0);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
#writeHelper(id, state) {
|
|
730
|
+
this.sendBuffer[id] = state.val;
|
|
731
|
+
if (Object.keys(this.sendBuffer).length === 1) {
|
|
732
|
+
this.#send().catch(e => this.adapter.log.error(`Cannot send: ${e}`));
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
async write(id, state) {
|
|
736
|
+
if (!this.objects[id]?.native) {
|
|
737
|
+
this.adapter.log.error(`Can not set state ${id}: unknown object`);
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
if (this.objects[id].native.regType === 'coils' || this.objects[id].native.regType === 'holdingRegs') {
|
|
741
|
+
if (!this.objects[id].native.wp) {
|
|
742
|
+
this.#writeHelper(id, state);
|
|
743
|
+
// TODO: may be here we should calculate options.config.readInterval too
|
|
744
|
+
await this.#waitAsync(this.options.config.poll * 1.5);
|
|
745
|
+
const _id = id.substring(this.adapter.namespace.length + 1);
|
|
746
|
+
await this.adapter.setState(id, this.ackObjects[_id] ? this.ackObjects[_id].val : null, true);
|
|
747
|
+
}
|
|
748
|
+
else {
|
|
749
|
+
if (this.pulseList[id] === undefined) {
|
|
750
|
+
const _id = id.substring(this.adapter.namespace.length + 1);
|
|
751
|
+
this.pulseList[id] = this.ackObjects[_id] ? this.ackObjects[_id].val : !state.val;
|
|
752
|
+
this.#writeHelper(id, state);
|
|
753
|
+
await this.#waitAsync(this.options.config.pulseTime);
|
|
754
|
+
this.#writeHelper(id, { val: this.pulseList[id] });
|
|
755
|
+
await this.#waitAsync(this.options.config.poll * 1.5);
|
|
756
|
+
if (this.ackObjects[_id]) {
|
|
757
|
+
await this.adapter.setState(id, this.ackObjects[_id].val, true);
|
|
758
|
+
}
|
|
759
|
+
delete this.pulseList[id];
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
else {
|
|
764
|
+
this.adapter.setTimeout(() => {
|
|
765
|
+
const _id = id.substring(this.adapter.namespace.length + 1);
|
|
766
|
+
void this.adapter.setState(id, this.ackObjects[_id] ? this.ackObjects[_id].val : null, true, err =>
|
|
767
|
+
// analyse if the state could be set (because of permissions)
|
|
768
|
+
err && this.adapter.log.error(`Can not set state ${id}: ${err}`));
|
|
769
|
+
}, 0);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
start() {
|
|
773
|
+
if (this.modbusClient && typeof this.modbusClient.connect === 'function') {
|
|
774
|
+
try {
|
|
775
|
+
this.modbusClient.connect();
|
|
776
|
+
}
|
|
777
|
+
catch (e) {
|
|
778
|
+
this.adapter.log.error(`Can not open Modbus connection: ${e} . Please check your settings`);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
close() {
|
|
783
|
+
this.isStop = true;
|
|
784
|
+
if (this.reconnectTimeout) {
|
|
785
|
+
this.adapter.clearTimeout(this.reconnectTimeout);
|
|
786
|
+
this.reconnectTimeout = null;
|
|
787
|
+
}
|
|
788
|
+
if (this.connectTimer) {
|
|
789
|
+
this.adapter.clearTimeout(this.connectTimer);
|
|
790
|
+
this.connectTimer = null;
|
|
791
|
+
}
|
|
792
|
+
if (this.keepAliveTimeout) {
|
|
793
|
+
this.adapter.clearTimeout(this.keepAliveTimeout);
|
|
794
|
+
this.keepAliveTimeout = null;
|
|
795
|
+
}
|
|
796
|
+
if (this.nextPoll) {
|
|
797
|
+
this.adapter.clearTimeout(this.nextPoll);
|
|
798
|
+
this.nextPoll = null;
|
|
799
|
+
}
|
|
800
|
+
if (this.modbusClient) {
|
|
801
|
+
try {
|
|
802
|
+
this.modbusClient.close();
|
|
803
|
+
}
|
|
804
|
+
catch {
|
|
805
|
+
// ignore
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
exports.Master = Master;
|
|
811
|
+
//# sourceMappingURL=Master.js.map
|