nodbus-plus 0.8.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/.readthedocs.yaml +35 -0
  2. package/LICENSE.md +21 -21
  3. package/README.md +163 -48
  4. package/docs/Makefile +20 -0
  5. package/docs/client/channel.rst +209 -0
  6. package/docs/client/nodbus_master_serial.rst +559 -0
  7. package/docs/client/nodbus_master_tcp.rst +573 -0
  8. package/docs/conf.py +41 -0
  9. package/docs/images/01-readcoils.png +0 -0
  10. package/docs/images/02-readinputs.png +0 -0
  11. package/docs/images/03-readholding.png +0 -0
  12. package/docs/images/04-readinputsreg.png +0 -0
  13. package/docs/images/05-writecoil.png +0 -0
  14. package/docs/images/06-writeregister.png +0 -0
  15. package/docs/images/15-writecoil.png +0 -0
  16. package/docs/images/16.png +0 -0
  17. package/docs/images/22-mask.png +0 -0
  18. package/docs/images/23.png +0 -0
  19. package/docs/images/7.png +0 -0
  20. package/docs/images/modbus_pdu.png +0 -0
  21. package/docs/images/serial_adu.png +0 -0
  22. package/docs/images/tcp_adu.png +0 -0
  23. package/docs/index.rst +30 -0
  24. package/docs/make.bat +35 -0
  25. package/docs/protocol/modbus_master.rst +276 -0
  26. package/docs/protocol/modbus_master_serial.rst +290 -0
  27. package/docs/protocol/modbus_master_tcp.rst +296 -0
  28. package/docs/protocol/modbus_server.rst +469 -0
  29. package/docs/protocol/modbus_server_serial.rst +543 -0
  30. package/docs/protocol/modbus_server_tcp.rst +365 -0
  31. package/docs/requirements.txt +4 -0
  32. package/docs/server/net_server.rst +242 -0
  33. package/docs/server/nodbus_serial_server.rst +652 -0
  34. package/docs/server/nodbus_tcp_server.rst +505 -0
  35. package/docs/starting.rst +192 -0
  36. package/docs/static/simple logo.jpg +0 -0
  37. package/package.json +39 -30
  38. package/samples/mb_serial_server.js +77 -0
  39. package/samples/mb_tcp_client.js +114 -0
  40. package/samples/mb_tcp_server.js +58 -0
  41. package/src/client/net/serialchannel.js +195 -0
  42. package/src/client/net/tcpchannel.js +233 -0
  43. package/src/client/net/udpchannel.js +243 -0
  44. package/src/client/nodbus_serial_client.js +577 -0
  45. package/src/client/nodbus_tcp_client.js +542 -0
  46. package/src/nodbus-plus.js +157 -110
  47. package/src/protocol/modbus_master.js +298 -961
  48. package/src/protocol/modbus_master_serial.js +247 -0
  49. package/src/protocol/modbus_master_tcp.js +219 -0
  50. package/src/protocol/modbus_server.js +936 -0
  51. package/src/protocol/modbus_server_serial.js +368 -0
  52. package/src/protocol/modbus_server_tcp.js +129 -0
  53. package/src/protocol/utils.js +296 -0
  54. package/src/server/net/serialserver.js +184 -0
  55. package/src/server/net/tcpserver.js +290 -0
  56. package/src/server/net/udpserver.js +242 -0
  57. package/src/server/nodbus_serial_server.js +238 -0
  58. package/src/server/nodbus_tcp_server.js +249 -0
  59. package/test/modbus_master.test.js +279 -0
  60. package/test/modbus_master_serial.test.js +124 -0
  61. package/test/modbus_master_tcp.test.js +178 -0
  62. package/test/modbus_server.test.js +506 -0
  63. package/test/modbus_server_serial.test.js +328 -0
  64. package/test/modbus_server_tcp.test.js +91 -0
  65. package/test/nodbus_client_serial.test.js +307 -0
  66. package/test/nodbus_client_tcp.test.js +334 -0
  67. package/test/nodbus_server_serial.test.js +255 -0
  68. package/test/nodbus_server_tcp.test.js +216 -0
  69. package/CHANGELOG.md +0 -27
  70. package/src/client/m_stcp_client.js +0 -214
  71. package/src/client/m_tcp_client.js +0 -234
  72. package/src/net/tcpclient.js +0 -173
  73. package/src/net/tcpserver.js +0 -329
  74. package/src/protocol/adu.js +0 -40
  75. package/src/protocol/ascii_adu.js +0 -139
  76. package/src/protocol/boolean_register.js +0 -78
  77. package/src/protocol/functions/Force_Multiple_Coils.js +0 -76
  78. package/src/protocol/functions/Force_Single_Coil.js +0 -54
  79. package/src/protocol/functions/Mask_Holding_Register.js +0 -47
  80. package/src/protocol/functions/Preset_Multiple_Registers.js +0 -53
  81. package/src/protocol/functions/Preset_Single_Register.js +0 -39
  82. package/src/protocol/functions/Read_Coil_Status.js +0 -59
  83. package/src/protocol/functions/Read_Holding_Registers.js +0 -52
  84. package/src/protocol/functions/Read_Input_Registers.js +0 -52
  85. package/src/protocol/functions/Read_Input_Status.js +0 -58
  86. package/src/protocol/mbap.js +0 -60
  87. package/src/protocol/modbus_device.js +0 -35
  88. package/src/protocol/modbus_slave.js +0 -522
  89. package/src/protocol/pdu.js +0 -70
  90. package/src/protocol/rtu_adu.js +0 -122
  91. package/src/protocol/serial_adu.js +0 -29
  92. package/src/protocol/tcp_adu.js +0 -84
  93. package/src/protocol/word_register.js +0 -122
  94. package/src/server/m_stcp_server.js +0 -310
  95. package/src/server/m_tcp_server.js +0 -295
  96. package/test/modbus-stcp-server-test.js +0 -72
  97. package/test/modbus-stcp-server-test1.js +0 -72
  98. package/test/modbus-tcp-client-test.js +0 -159
  99. package/test/modbus-tcp-server-test.js +0 -75
  100. package/test/modbus-tcp-server-test2.js +0 -75
  101. package/test/modbus_stcp_client.js +0 -149
@@ -0,0 +1,542 @@
1
+ /**
2
+ ** Modbus Tcp Client module.
3
+ * @module client/modbus_tcp_client
4
+ * @author Hector E. Socarras.
5
+ * @version 0.8.0
6
+ */
7
+
8
+ const ModbusTcpMaster = require('../protocol/modbus_master_tcp');
9
+ const TcpChannel = require('./net/tcpchannel');
10
+ const UdpChannel = require('./net/udpchannel');
11
+
12
+
13
+
14
+
15
+ /**
16
+ * Class representing a modbus tcp client ready to use.
17
+ * @extends ModbusTcpMaster
18
+ */
19
+ class NodbusTcpClient extends ModbusTcpMaster {
20
+ /**
21
+ * Create a Modbus Tcp Client.
22
+ */
23
+ constructor(){
24
+ super();
25
+
26
+ var self = this;
27
+
28
+ /**
29
+ * channel's constructors
30
+ * @type {object}
31
+ */
32
+ this.channelType = new Map();
33
+ this.channelType.set('tcp1', TcpChannel);
34
+ this.channelType.set('udp1', UdpChannel);
35
+
36
+
37
+ this.channels = new Map();
38
+
39
+ }
40
+
41
+ /**
42
+ * Function to add a channel object to master
43
+ * @param {string} id: channel's id. Unique por channel
44
+ * @param {string} ip: channel's ip address. Default 'localhost'
45
+ * @param {number} port: channel's port. Default 502
46
+ * @param {number} timeout time in miliseconds to emit timeout for a request.
47
+ */
48
+ addChannel(id, type = 'tcp1', channelCfg = {ip: 'localhost', port: 502, timeout:250}){
49
+
50
+
51
+ if(channelCfg.ip == undefined){ channelCfg.ip = 'localhost'}
52
+ if(channelCfg.port == undefined){ channelCfg.port = 502}
53
+ if(channelCfg.timeout == undefined){ channelCfg.timeout = 250}
54
+ channelCfg.tcpCoalescingDetection = true;
55
+
56
+ let Channel = this.channelType.get(type);
57
+ if (Channel == undefined){
58
+ Channel = TcpChannel;
59
+ }
60
+
61
+ let channel = new Channel(channelCfg);
62
+
63
+ /**
64
+ * Emit connect and ready events
65
+ * @param {object} target Socket object
66
+ * @fires ModbusTCPClient#connect {object}
67
+ * @fires ModbusTCPClient#ready
68
+ */
69
+ channel.onConnectHook = () => {
70
+ /**
71
+ * connection event.
72
+ * Emited when new connecton is sablished
73
+ * @event NodbusTcpClient#connection
74
+ * @type {object}
75
+ * @see https://nodejs.org/api/net.html
76
+ */
77
+ this.emit('connection', id);
78
+ };
79
+
80
+ channel.onCloseHook = () => {
81
+ /**
82
+ * connection-closed event.
83
+ * @event ModbusnetServer#connection-closed
84
+ * @type {object}
85
+ */
86
+ this.emit('connection-closed', id)
87
+ };
88
+
89
+ channel.onDataHook = (dataFrame) => {
90
+ /**
91
+ * indication event.
92
+ * @event ModbusnetServer#indication
93
+ */
94
+ this.emit('data', id, dataFrame);
95
+ };
96
+
97
+ channel.onMbAduHook = (resAdu) => {
98
+
99
+ let res = {};
100
+
101
+ res.timeStamp = Date.now();
102
+ res.transactionId = resAdu.readUint16BE(0);
103
+ res.unitId = resAdu[6];
104
+ res.functionCode = resAdu[7];
105
+ res.data = resAdu.subarray(8);
106
+
107
+ this.processResAdu(resAdu);
108
+ this.emit('response', id, res)
109
+
110
+ }
111
+
112
+ channel.onErrorHook = (err) =>{
113
+ /**
114
+ * error event.
115
+ * @event ModbusNetServer#error
116
+ */
117
+ this.emit('error', id, err);
118
+ };
119
+
120
+ channel.onWriteHook = (reqAdu) => {
121
+
122
+ let req = {};
123
+
124
+ req.timeStamp = Date.now();
125
+ req.transactionId = reqAdu.readUint16BE(0);
126
+ req.unitId = reqAdu[6];
127
+ req.functionCode = reqAdu[7];
128
+ req.data = reqAdu.subarray(8);
129
+
130
+ this.setReqTimer(req.transactionId, channelCfg.timeout); //start the timer for timeout event
131
+ this.emit('request', id, req);
132
+
133
+ /**
134
+ * response event.
135
+ * @event ModbusnetServer#response
136
+ */
137
+ this.emit('write', id, reqAdu);
138
+
139
+ };
140
+
141
+ channel.validateFrame = (frame)=>{
142
+ if(frame.length > 7){
143
+
144
+ let expectedLength = frame.readUInt16BE(4) + 6;
145
+ let protocolId = frame.readUInt16BE(2);
146
+ return frame.length == expectedLength & protocolId == 0;
147
+ }
148
+ return false;
149
+ }
150
+
151
+ this.channels.set(id, channel);
152
+ }
153
+
154
+ /**
155
+ * Function to delete a channel from the list
156
+ * @param {string} id
157
+ */
158
+ delChannel(id){
159
+
160
+ if(this.channels.has(id)){
161
+ this.channels.delete(id);
162
+ }
163
+ }
164
+
165
+ isChannelReady(id){
166
+
167
+ if(this.channels.has(id)){
168
+ let channel = this.channels.get(id);
169
+ return channel.isConnected();
170
+ }
171
+ else return false;
172
+ }
173
+
174
+
175
+ /**
176
+ *Stablish connection
177
+ */
178
+ connect(id){
179
+
180
+ let self = this;
181
+ let successPromise;
182
+ let channel = self.channels.get(id);
183
+
184
+ if(channel == undefined){
185
+ return Promise.reject(channel.ip, channel.port);
186
+ }
187
+ else if(channel.isConnected()){
188
+
189
+ return Promise.resolve(id);
190
+ }
191
+ else{
192
+ successPromise = channel.connect();
193
+ return successPromise
194
+ }
195
+
196
+ }
197
+
198
+ /**
199
+ *disconnect from server
200
+ */
201
+ disconnect(id){
202
+
203
+ let self = this;
204
+ let successPromise;
205
+ let channel = self.channels.get(id)
206
+
207
+ if(channel == undefined){
208
+ return Promise.resolve(id);
209
+
210
+ }
211
+ else if(channel.isConnected()){
212
+ successPromise = channel.disconnect();
213
+ return successPromise;
214
+ }
215
+ else{
216
+ return Promise.resolve(id);
217
+
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Function to send read coils status request to a modbus server.
223
+ * @param {string} channelId Identifier use as key on channels dictionary.
224
+ * @param {number} unitId Modbus address. A value between 1 -255
225
+ * @param {number} startCoil Starting coils at 0 address
226
+ * @param {number} coilsCuantity
227
+ * @returns {Boolean} true if succses otherwise false
228
+ */
229
+ readCoils(channelId, unitId, startCoil, coilsCuantity){
230
+ let self = this;
231
+ //check if channel is connected
232
+ if(this.isChannelReady(channelId)){
233
+
234
+ let channel = this.channels.get(channelId);
235
+ let pdu = this.readCoilStatusPdu(startCoil, coilsCuantity);
236
+ let reqAdu = this.makeRequest(unitId, pdu);
237
+
238
+ if(self.storeRequest(reqAdu)){
239
+
240
+ return channel.write(reqAdu);
241
+
242
+ }
243
+ else{
244
+ return false
245
+ }
246
+ }
247
+ else{
248
+ return false
249
+ }
250
+
251
+ }
252
+
253
+ /**
254
+ * Function to send read inputs status request to a modbus server.
255
+ * @param {string} channelId Identifier use as key on channels dictionary.
256
+ * @param {number} unitId Modbus address. A value between 1 -255
257
+ * @param {number} startInput Starting inputs at 0 address
258
+ * @param {number} inputsCuantity
259
+ * @returns {Boolean} true if succses otherwise false
260
+ */
261
+ readInputs(channelId, unitId, startInput, inputsCuantity){
262
+ let self = this;
263
+ //check if channel is connected
264
+ if(this.isChannelReady(channelId)){
265
+
266
+ let channel = this.channels.get(channelId);
267
+ let pdu = this.readInputStatusPdu(startInput, inputsCuantity);
268
+ let reqAdu = this.makeRequest(unitId, pdu);
269
+
270
+ if(self.storeRequest(reqAdu)){
271
+
272
+ return channel.write(reqAdu);
273
+
274
+ }
275
+ else{
276
+ return false
277
+ }
278
+ }
279
+ else{
280
+ return false
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Function to send read holding registers request to a modbus server.
286
+ * @param {string} channelId Identifier use as key on channels dictionary.
287
+ * @param {number} unitId Modbus address. A value between 1 -255
288
+ * @param {number} startRegister Starting coils at 0 address
289
+ * @param {number} registersCuantity
290
+ * @returns {Boolean} true if succses otherwise false
291
+ */
292
+ readHoldingRegisters(channelId, unitId, startRegister, registersCuantity){
293
+ let self = this;
294
+ //check if channel is connected
295
+ if(this.isChannelReady(channelId)){
296
+
297
+ let channel = this.channels.get(channelId);
298
+ let pdu = this.readHoldingRegistersPdu(startRegister, registersCuantity);
299
+ let reqAdu = this.makeRequest(unitId, pdu);
300
+
301
+ if(self.storeRequest(reqAdu)){
302
+
303
+ return channel.write(reqAdu);
304
+
305
+ }
306
+ else{
307
+ return false
308
+ }
309
+ }
310
+ else{
311
+ return false
312
+ }
313
+ }
314
+
315
+ /**
316
+ * Function to send read inputs registers request to a modbus server.
317
+ * @param {string} channelId Identifier use as key on channels dictionary.
318
+ * @param {number} unitId Modbus address. A value between 1 -255
319
+ * @param {number} startRegister Starting register at 0 address
320
+ * @param {number} registersCuantity
321
+ * @returns {Boolean} true if succses otherwise false
322
+ */
323
+ readInputRegisters(channelId, unitId, startRegister, registersCuantity){
324
+ let self = this;
325
+ //check if channel is connected
326
+ if(this.isChannelReady(channelId)){
327
+
328
+ let channel = this.channels.get(channelId);
329
+ let pdu = this.readInputRegistersPdu(startRegister, registersCuantity);
330
+ let reqAdu = this.makeRequest(unitId, pdu);
331
+
332
+ if(self.storeRequest(reqAdu)){
333
+
334
+ return channel.write(reqAdu);
335
+
336
+ }
337
+ else{
338
+ return false
339
+ }
340
+ }
341
+ else{
342
+ return false
343
+ }
344
+ }
345
+
346
+ /**
347
+ * Function to send forse single coil request to a modbus server.
348
+ * @param {boolean} value to force.
349
+ * @param {string} channelId Identifier use as key on channels dictionary.
350
+ * @param {number} unitId Modbus address. A value between 1 -255
351
+ * @param {number} startCoil Starting coils at 0 address
352
+ * @returns {Boolean} true if succses otherwise false
353
+ */
354
+ forceSingleCoil(value, channelId, unitId, startCoil){
355
+ let self = this;
356
+ //check if channel is connected
357
+ if(this.isChannelReady(channelId)){
358
+
359
+ let channel = this.channels.get(channelId);
360
+ let bufValue = this.boolToBuffer(value);
361
+ let pdu = this.forceSingleCoilPdu(bufValue, startCoil);
362
+ let reqAdu = this.makeRequest(unitId, pdu);
363
+
364
+ if(self.storeRequest(reqAdu)){
365
+
366
+ return channel.write(reqAdu);
367
+
368
+ }
369
+ else{
370
+ return false
371
+ }
372
+ }
373
+ else{
374
+ return false
375
+ }
376
+ }
377
+
378
+ /**
379
+ * Function to send preset single register request to a modbus server.
380
+ * @param {Buffer} value Two bytes length buffer.
381
+ * @param {string} channelId Identifier use as key on channels dictionary.
382
+ * @param {number} unitId Modbus address. A value between 1 -255
383
+ * @param {number} startRegister Starting coils at 0 address
384
+ * @returns {Boolean} true if succses otherwise false
385
+ */
386
+ presetSingleRegister(value, channelId, unitId, startRegister){
387
+ let self = this;
388
+ //check if channel is connected
389
+ if(this.isChannelReady(channelId)){
390
+
391
+ let channel = this.channels.get(channelId);
392
+ let pdu = this.presetSingleRegisterPdu(value, startRegister);
393
+ let reqAdu = this.makeRequest(unitId, pdu);
394
+
395
+ if(self.storeRequest(reqAdu)){
396
+
397
+ return channel.write(reqAdu);
398
+
399
+ }
400
+ else{
401
+ return false
402
+ }
403
+ }
404
+ else{
405
+ return false
406
+ }
407
+ }
408
+
409
+ /**
410
+ * Function to send force multiples coils request to a modbus server.
411
+ * @param {Array} values a boolean array with values to force. arrays index 0 correspond to start coil. Arrays length correspond to numbers of coils to force.
412
+ * @param {string} channelId Identifier use as key on channels dictionary.
413
+ * @param {number} unitId Modbus address. A value between 1 -255
414
+ * @param {number} startCoil Starting coils at 0 address
415
+ * @returns {Boolean} true if succses otherwise false
416
+ */
417
+ forceMultipleCoils(values, channelId, unitId, startCoil){
418
+ let self = this;
419
+ //check if channel is connected
420
+ if(this.isChannelReady(channelId)){
421
+
422
+ let channel = this.channels.get(channelId);
423
+ let bufValues = this.boolsToBuffer(values);
424
+ let pdu = this.forceMultipleCoilsPdu(bufValues, startCoil, values.length);
425
+ let reqAdu = this.makeRequest(unitId, pdu);
426
+
427
+ if(self.storeRequest(reqAdu)){
428
+
429
+ return channel.write(reqAdu);
430
+
431
+ }
432
+ else{
433
+ return false
434
+ }
435
+ }
436
+ else{
437
+ return false
438
+ }
439
+ }
440
+
441
+ /**
442
+ * Function to send preset multiples registers request to a modbus server.
443
+ * @param {Buffer} values an buffer with values to force. arrays index 0 correspond to start register.
444
+ * @param {string} channelId Identifier use as key on channels dictionary.
445
+ * @param {number} unitId Modbus address. A value between 1-255
446
+ * @param {number} startRegister Starting register at 0 address.
447
+ * @returns {Boolean} true if succses otherwise false
448
+ */
449
+ presetMultiplesRegisters(values, channelId, unitId, startRegister){
450
+ let self = this;
451
+ //check if channel is connected
452
+ if(this.isChannelReady(channelId)){
453
+
454
+ let channel = this.channels.get(channelId);
455
+ let pdu = this.presetMultipleRegistersPdu(values, startRegister, Math.floor(values.length/2));
456
+ let reqAdu = this.makeRequest(unitId, pdu);
457
+
458
+ if(self.storeRequest(reqAdu)){
459
+
460
+ return channel.write(reqAdu);
461
+
462
+ }
463
+ else{
464
+ return false
465
+ }
466
+ }
467
+ else{
468
+ return false
469
+ }
470
+ }
471
+
472
+ /**
473
+ * Function to send preset multiples registers request to a modbus server.
474
+ * @param {Array} values an 16 number length array with values to force. Index 0 is de less significant bit.
475
+ * A value off 1 force to 1 the corresponding bit, 0 force to 0, other values don't change the bit value.
476
+ * @param {string} channelId Identifier use as key on channels dictionary.
477
+ * @param {number} unitId Modbus address. A value between 1-255
478
+ * @param {number} startRegister Starting register at 0 address.
479
+ * @returns true if succses otherwise false
480
+ */
481
+ maskHoldingRegister(values, channelId, unitId, startRegister){
482
+ let self = this;
483
+ //check if channel is connected
484
+ if(this.isChannelReady(channelId)){
485
+
486
+ let channel = this.channels.get(channelId);
487
+ let bufValues = this.getMaskRegisterBuffer(values);
488
+ let pdu = this.maskHoldingRegisterPdu(bufValues, startRegister, Math.floor(values.length));
489
+ let reqAdu = this.makeRequest(unitId, pdu);
490
+
491
+ if(self.storeRequest(reqAdu)){
492
+
493
+ return channel.write(reqAdu);
494
+
495
+ }
496
+ else{
497
+ return false
498
+ }
499
+ }
500
+ else{
501
+ return false
502
+ }
503
+ }
504
+
505
+ /**
506
+ * Function to send preset multiples registers request to a modbus server.
507
+ * @param {Buffer} values a buffer with values to writes. arrays index 0 correspond to start register.
508
+ * @param {string} channelId Identifier use as key on channels dictionary.
509
+ * @param {number} unitId Modbus address. A value between 1-255
510
+ * @param {number} readStartingRegister Starting register at 0 address.
511
+ * @param {number} readRegisterCuantity Number of register to read
512
+ * @param {number} writeStartingRegister Starting register at 0 address to write.
513
+ * @returns true if succses otherwise false
514
+ */
515
+ readWriteMultiplesRegisters(values, channelId, unitId, readStartingRegister, readRegisterCuantity, writeStartingRegister){
516
+ let self = this;
517
+ //check if channel is connected
518
+ if(this.isChannelReady(channelId)){
519
+
520
+ let channel = this.channels.get(channelId);
521
+ let pdu = this.readWriteMultipleRegistersPdu(values, readStartingRegister, readRegisterCuantity, writeStartingRegister, Math.floor(values.length/2));
522
+ let reqAdu = this.makeRequest(unitId, pdu);
523
+
524
+ if(self.storeRequest(reqAdu)){
525
+
526
+ return channel.write(reqAdu);
527
+
528
+ }
529
+ else{
530
+ return false
531
+ }
532
+ }
533
+ else{
534
+ return false
535
+ }
536
+ }
537
+
538
+ }
539
+
540
+
541
+
542
+ module.exports = NodbusTcpClient;