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,368 @@
1
+ /**
2
+ ** Modbus Serial Server Base Class module.
3
+ * @module protocol/modbus_server_serial
4
+ * @author Hector E. Socarras.
5
+ * @version 1.0.0
6
+ */
7
+
8
+ const { Buffer } = require('node:buffer');
9
+
10
+ const ModbusServer = require('./modbus_server.js');
11
+ const valByte2Chars = require('./utils.js').valByteToChars
12
+
13
+ //Default Server's Configuration object
14
+ const defaultCfg = {
15
+ transmitionMode : 0, //transmition mode 0 - rtu or 1 - ascii
16
+ address : 1,
17
+ inputs : 2048,
18
+ coils : 2048,
19
+ holdingRegisters : 2048,
20
+ inputRegisters : 2048
21
+ }
22
+
23
+ /**
24
+ * Class representing a modbus serial server.
25
+ * @extends ModbusServer
26
+ */
27
+ class ModbusSerialServer extends ModbusServer {
28
+ /**
29
+ * Create a Modbus Slave.
30
+ */
31
+ constructor(mbSerialServerCfg = defaultCfg){
32
+ super(mbSerialServerCfg);
33
+
34
+ var self = this;
35
+
36
+ //arguments check
37
+ if(mbSerialServerCfg.transmitionMode == undefined | (mbSerialServerCfg.transmitionMode != 0 & mbSerialServerCfg.transmitionMode != 1)){
38
+ mbSerialServerCfg.transmitionMode = defaultCfg.transmitionMode;
39
+ }
40
+ if(mbSerialServerCfg.address == undefined){ mbSerialServerCfg.address = defaultCfg.address;}
41
+
42
+ /**
43
+ * address property
44
+ * @type {number}
45
+ * @private
46
+ */
47
+ this._address;
48
+ this.address = mbSerialServerCfg.address;
49
+
50
+ /**
51
+ * transmition mode property
52
+ * Enum {auto, rtu, asscii}
53
+ * @type {number}
54
+ * @private
55
+ */
56
+ this.transmitionMode = mbSerialServerCfg.transmitionMode;
57
+
58
+ //add serial function codes
59
+ this._internalFunctionCode.set(7, 'readExceptionCoilsService');
60
+
61
+ /**
62
+ * Exception Coils
63
+ * @type {buffer}
64
+ * @public
65
+ */
66
+ this.exceptionCoils = Buffer.alloc(1);
67
+
68
+ //diagnostic counters
69
+ this.busMessageCount = 0;
70
+ this.busCommunicationErrorCount = 0; //this counter is managed by network layer, this propety takes a pointer to the counter.
71
+ this.slaveExceptionErrorCount = 0;
72
+ this.slaveMessageCount = 0;
73
+ this.slaveNoResponseCount = 0;
74
+ this.slaveNAKCount = 0;
75
+ this.slaveBusyCount = 0;
76
+ this.busCharacterOverrunCount = 0; //this counter is managed by network layer, this propety takes a pointer to the counter.
77
+
78
+ this.on('exception', (functionCode, exception, message) =>{
79
+ if(exception == 5){
80
+ this.slaveNAKCount++;
81
+ }
82
+ else if(exception == 6){
83
+ this.slaveBusyCount++
84
+ }
85
+ })
86
+
87
+ }
88
+
89
+ /**
90
+ * getter for address property
91
+ *
92
+ */
93
+ get address(){
94
+ return this._address;
95
+ }
96
+ set address(addr){
97
+
98
+ if(typeof addr == 'number'){
99
+ if(addr > 0 & addr <= 247){
100
+ this._address = addr;
101
+ }
102
+ else{
103
+ this._address = 1;
104
+ }
105
+ }
106
+ else{
107
+ this._address = 1;
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Function to get the address on a serial adu request in rtu format.
113
+ * @param {Buffer} reqAduBuffer
114
+ * @returns {number} server address on requesr.
115
+ */
116
+ getAddress(reqAduBuffer){
117
+
118
+ if(this.transmitionMode == 1){
119
+ //Modbus Ascii Frame
120
+ return parseInt('0x' + String.fromCharCode(reqAduBuffer[1]) + String.fromCharCode(reqAduBuffer[2]));
121
+ }
122
+ else {
123
+ //Modbus Rtu Frame
124
+ return reqAduBuffer[0];
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Function to get the pdu buffer from a serial adu request in rtu format
130
+ * @param {Buffer} reqAduBuffer
131
+ * @returns {Buffer} Pdu's buffer.
132
+ */
133
+ getPdu(reqAduBuffer){
134
+ if(this.transmitionMode == 1){
135
+ //Modbus Ascii Frame
136
+ let reqAduRtuBuffer = this.aduAsciiToRtu(reqAduBuffer);
137
+ return reqAduRtuBuffer.subarray(1,reqAduRtuBuffer.length-2);
138
+ }
139
+ else {
140
+ //Modbus Rtu Frame
141
+ return reqAduBuffer.subarray(1,reqAduBuffer.length-2);
142
+ }
143
+
144
+ }
145
+
146
+ /**
147
+ * Function to get the pdu buffer from a serial adu request in rtu format
148
+ * @param {Buffer} reqAduBuffer
149
+ * @returns {Buffer} Pdu's buffer.
150
+ */
151
+ getChecksum(reqAduBuffer){
152
+ if(this.transmitionMode == 1){
153
+ //Modbus Ascii Frame
154
+ return this.calcLRC(reqAduBuffer);
155
+ }
156
+ else {
157
+ //Modbus Rtu Frame
158
+ return this.calcCRC(reqAduBuffer);
159
+ }
160
+
161
+ }
162
+
163
+
164
+ /**
165
+ * Function to be called when request adu is received. Imlement the Counters Management Diagram.
166
+ * See MODBUS over serial line specification and implementation guide V1.02
167
+ * @param {Buffer} modbus indication's frame
168
+ * @return {Buffer} Response Adu
169
+ *
170
+ */
171
+ getResponseAdu(reqAduBuffer){
172
+
173
+ var self = this;
174
+
175
+ if(reqAduBuffer instanceof Buffer){
176
+
177
+ //check CRC or LRC
178
+ if(reqAduBuffer.length >= 3){
179
+
180
+ let reqPduBuffer = this.getPdu(reqAduBuffer);
181
+
182
+ this.slaveMessageCount++;
183
+
184
+ let resPduBuffer = this.processReqPdu(reqPduBuffer);
185
+
186
+ if(resPduBuffer[0] == reqPduBuffer[0] + 0x80){ //exception response
187
+ this.slaveExceptionErrorCount++;
188
+ }
189
+
190
+ //calculando la adu
191
+ let resAduBuffer;
192
+ let resRtuAduBuffer = Buffer.alloc(resPduBuffer.length + 3);
193
+ resRtuAduBuffer[0] = this.address;
194
+ resPduBuffer.copy(resRtuAduBuffer, 1)
195
+
196
+ if(this.transmitionMode == 1){
197
+
198
+ resAduBuffer = this.aduRtuToAscii(resRtuAduBuffer)
199
+
200
+ }
201
+ else {
202
+ //calculating CRC
203
+ let crc = this.calcCRC(resRtuAduBuffer);
204
+
205
+ resAduBuffer = resRtuAduBuffer;
206
+ resAduBuffer.writeUint16BE(crc, resAduBuffer.length - 2);
207
+ }
208
+
209
+ return resAduBuffer;
210
+
211
+ }
212
+ else{
213
+ throw new RangeError("Pdu's length must be between 0 an 253");
214
+ }
215
+ }
216
+ else{
217
+ throw new TypeError("request adu must be Buffer objects");
218
+ }
219
+
220
+
221
+
222
+
223
+ }
224
+
225
+ /**
226
+ * @brief Similar to getResponseAdu but return nothing, just execute the request service without response. Used when broadcast address is receivedd
227
+ * @param {Buffer} reqAduBuffer request buffer
228
+ * @fires ModbusServer#error
229
+ * @return {Buffer} buffer containing a protocol data unit
230
+ */
231
+ executeBroadcastReq(reqAduBuffer) {
232
+
233
+ let self = this;
234
+
235
+ if(reqAduBuffer instanceof Buffer){
236
+
237
+ //check CRC or LRC
238
+ if(reqAduBuffer.length >= 3){
239
+
240
+
241
+ let reqPduBuffer = this.getPdu(reqAduBuffer);
242
+
243
+ this.slaveMessageCount++;
244
+ //is Broadcast
245
+ this.slaveNoResponseCount++;
246
+
247
+ let noResPdu = this.processReqPdu(reqPduBuffer);
248
+
249
+ if(noResPdu[0] == reqPduBuffer[0] + 0x80){
250
+ this.slaveExceptionErrorCount++;
251
+ }
252
+
253
+ }
254
+ else{
255
+ throw new RangeError("Pdu's length must be between 0 an 253");
256
+ }
257
+ }
258
+ else{
259
+ throw new TypeError("request adu must be Buffer objects");
260
+ }
261
+
262
+ }
263
+
264
+ /**
265
+ * @brief Function to implement Read Exception Status service on server. Function code 07.
266
+ * @param {Buffer} pduReqData buffer containing only data from a request pdu
267
+ * @fires ModbusServer#exception
268
+ * @return {Buffer} resPduBuffer. Response pdu.
269
+ */
270
+ readExceptionCoilsService(pduReqData){
271
+
272
+ //Defining function code for this service
273
+ const FUNCTION_CODE = 7;
274
+
275
+ let resPduBuffer;
276
+
277
+ if(pduReqData.length == 0){
278
+
279
+ resPduBuffer = Buffer.alloc(2);
280
+ resPduBuffer[0] = FUNCTION_CODE;
281
+ this.exceptionCoils.copy(resPduBuffer,1);
282
+
283
+ }
284
+ else{
285
+ //reply modbus exception 3
286
+ resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 3);
287
+ }
288
+ return resPduBuffer;
289
+ }
290
+
291
+ /**
292
+ * Check the address field of the frame.
293
+ * @param {Buffer} frame frame off modbus indication
294
+ * @return {boolean} return true if the frame's address field match withserver address or is 0.
295
+ */
296
+ validateAddress(frame){
297
+
298
+ let addressField = this.getAddress(frame);
299
+
300
+ if (addressField == this.address | addressField == 0){
301
+ return true;
302
+ }
303
+ else {
304
+ return false;
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Check the CRC or LRC on the frame.
310
+ * @param {Buffer} frame frame off modbus indication
311
+ * @return {boolean} check sum ok return true.
312
+ */
313
+ validateCheckSum(frame){
314
+
315
+ let calcErrorCheck = 0;
316
+ let frameErrorCheck = 0;
317
+
318
+ if(this.transmitionMode == 1){
319
+ //Modbus Ascii Frame
320
+ calcErrorCheck = this.calcLRC(frame);
321
+ frameErrorCheck = Number('0x' + frame.toString('ascii', frame.length-4, frame.length-2));
322
+ }
323
+ else {
324
+ //Modbus Rtu Frame
325
+ calcErrorCheck = this.calcCRC(frame);
326
+ frameErrorCheck = frame.readUInt16BE(frame.length - 2);
327
+ }
328
+
329
+ //cheking checsum
330
+ if(calcErrorCheck == frameErrorCheck){
331
+ return true;
332
+ }
333
+ else{
334
+ //check sum not ok
335
+ return false;
336
+ }
337
+
338
+
339
+ }
340
+
341
+
342
+ /**
343
+ * Function to restart the counter's value
344
+ */
345
+ resetCounters(){
346
+ //diagnostic counters
347
+ this.busMessageCount = 0;
348
+ this.busCommunicationErrorCount = 0; //this counter is managed by network layer, this propety takes a pointer to the counter.
349
+ this.slaveExceptionErrorCount = 0;
350
+ this.slaveMessageCount = 0;
351
+ this.slaveNoResponseCount = 0;
352
+ this.slaveNAKCount = 0;
353
+ this.slaveBusyCount = 0;
354
+ this.busCharacterOverrunCount = 0; //this counter is managed by network layer, this propety takes a pointer to the counter.
355
+ }
356
+
357
+ }
358
+
359
+ ModbusSerialServer.prototype.aduAsciiToRtu = require('./utils.js').aduAsciiToRtu;
360
+
361
+ ModbusSerialServer.prototype.aduRtuToAscii = require('./utils.js').aduRtuToAscii;
362
+
363
+ ModbusSerialServer.prototype.calcCRC = require('./utils.js').calcCRC;
364
+
365
+ ModbusSerialServer.prototype.calcLRC = require('./utils.js').calcLRC;
366
+
367
+ module.exports = ModbusSerialServer;
368
+
@@ -0,0 +1,129 @@
1
+ /**
2
+ ** Modbus TCP Server Base Class module.
3
+ * @module protocol/modbus_server_tcp
4
+ * @author Hector E. Socarras.
5
+ * @version 1.0.0
6
+ */
7
+
8
+ const ModbusServer = require('./modbus_server');
9
+
10
+
11
+ //Default Server's Configuration object
12
+ const defaultCfg = {
13
+ inputs : 2048,
14
+ coils : 2048,
15
+ holdingRegisters : 2048,
16
+ inputRegisters : 2048
17
+ }
18
+
19
+ /**
20
+ * Class representing a modbus tcp server.
21
+ * @extends ModbusServer
22
+ */
23
+ class ModbusTcpServer extends ModbusServer {
24
+ /**
25
+ * Create a Modbus TCP Server.
26
+ * @param {Object} mbTcpServercfg Configuration object.
27
+ */
28
+ constructor(mbTcpServercfg = defaultCfg){
29
+ super(mbTcpServercfg);
30
+
31
+ var self = this;
32
+
33
+ }
34
+
35
+ /**
36
+ * Function to get the pdu buffer from a tcp adu request.
37
+ * @param {Buffer} reqAduBuffer
38
+ * @returns {Buffer} Pdu's buffer.
39
+ */
40
+ getPdu(reqAduBuffer){
41
+ if(reqAduBuffer.length > 7) {
42
+ return reqAduBuffer.subarray(7);
43
+ }
44
+ else{
45
+ return null
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Function to get the header buffer from a tcp adu request.
51
+ * @param {Buffer} reqAduBuffer
52
+ * @returns {Buffer} Header's buffer.
53
+ */
54
+ getMbapHeader(reqAduBuffer){
55
+ if(reqAduBuffer.length >= 7) {
56
+ return reqAduBuffer.subarray(0, 7);
57
+ }
58
+ else{
59
+ return null
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Function to parse a mbap buffer.
65
+ * See Fig 16 on Modbus Messaging on TCPIP Implementation Guide V1.0b
66
+ * @param {Buffer} mbapBuffer 7 bytes buffer with the modbus tcp transaction's header
67
+ * @returns {bool} true if is a valid mbap header.
68
+ */
69
+ validateMbapHeader(mbapBuffer){
70
+ if(mbapBuffer.length == 7){
71
+ if(mbapBuffer.readUInt16BE(2) == 0){
72
+ return true
73
+ }
74
+ else{
75
+ return false;
76
+ }
77
+ }
78
+ else{
79
+ return false;
80
+ }
81
+ }
82
+
83
+ /**
84
+ *
85
+ * @param {Object} req Object containing a header property type buffer, and pdu property type buffer.
86
+ * @returns {Buffer}
87
+ * @throws {TypeError} if header or pdu are not Buffer instances.
88
+ * @throws {RangeError} if header and pdu buffer has incorrect length.
89
+ */
90
+ getResponseAdu(reqAduBuffer){
91
+
92
+ //Type check
93
+
94
+ if(reqAduBuffer instanceof Buffer){
95
+
96
+ //Range check
97
+ if(reqAduBuffer.length > 7 & reqAduBuffer.length <= 260){
98
+
99
+ let header = reqAduBuffer.subarray(0, 7);
100
+ let pdu = reqAduBuffer.subarray(7);
101
+
102
+ //Get the response Pdu
103
+ let resPdu = this.processReqPdu(pdu);
104
+ //Crating response Buffer
105
+ let resAdu = Buffer.alloc(7+resPdu.length);
106
+ //copying values fron req header
107
+ header.copy(resAdu);
108
+ //copying pdu to response
109
+ resPdu.copy(resAdu, 7);
110
+ //Calculating header lenth field. Unit Id Byte plus pdu length
111
+ resAdu.writeUint16BE(resPdu.length + 1, 4)
112
+
113
+ return resAdu;
114
+ }
115
+ else{
116
+ throw new RangeError("header's length musb be 7, and pdu's length must be between 0 an 253");
117
+ }
118
+ }
119
+ else{
120
+ throw new TypeError("header and pdu must be Buffer objects");
121
+ }
122
+ }
123
+
124
+
125
+ }
126
+
127
+
128
+
129
+ module.exports = ModbusTcpServer;