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.
- package/.readthedocs.yaml +35 -0
- package/LICENSE.md +21 -21
- package/README.md +163 -48
- package/docs/Makefile +20 -0
- package/docs/client/channel.rst +209 -0
- package/docs/client/nodbus_master_serial.rst +559 -0
- package/docs/client/nodbus_master_tcp.rst +573 -0
- package/docs/conf.py +41 -0
- package/docs/images/01-readcoils.png +0 -0
- package/docs/images/02-readinputs.png +0 -0
- package/docs/images/03-readholding.png +0 -0
- package/docs/images/04-readinputsreg.png +0 -0
- package/docs/images/05-writecoil.png +0 -0
- package/docs/images/06-writeregister.png +0 -0
- package/docs/images/15-writecoil.png +0 -0
- package/docs/images/16.png +0 -0
- package/docs/images/22-mask.png +0 -0
- package/docs/images/23.png +0 -0
- package/docs/images/7.png +0 -0
- package/docs/images/modbus_pdu.png +0 -0
- package/docs/images/serial_adu.png +0 -0
- package/docs/images/tcp_adu.png +0 -0
- package/docs/index.rst +30 -0
- package/docs/make.bat +35 -0
- package/docs/protocol/modbus_master.rst +276 -0
- package/docs/protocol/modbus_master_serial.rst +290 -0
- package/docs/protocol/modbus_master_tcp.rst +296 -0
- package/docs/protocol/modbus_server.rst +469 -0
- package/docs/protocol/modbus_server_serial.rst +543 -0
- package/docs/protocol/modbus_server_tcp.rst +365 -0
- package/docs/requirements.txt +4 -0
- package/docs/server/net_server.rst +242 -0
- package/docs/server/nodbus_serial_server.rst +652 -0
- package/docs/server/nodbus_tcp_server.rst +505 -0
- package/docs/starting.rst +192 -0
- package/docs/static/simple logo.jpg +0 -0
- package/package.json +39 -30
- package/samples/mb_serial_server.js +77 -0
- package/samples/mb_tcp_client.js +114 -0
- package/samples/mb_tcp_server.js +58 -0
- package/src/client/net/serialchannel.js +195 -0
- package/src/client/net/tcpchannel.js +233 -0
- package/src/client/net/udpchannel.js +243 -0
- package/src/client/nodbus_serial_client.js +577 -0
- package/src/client/nodbus_tcp_client.js +542 -0
- package/src/nodbus-plus.js +157 -110
- package/src/protocol/modbus_master.js +298 -961
- package/src/protocol/modbus_master_serial.js +247 -0
- package/src/protocol/modbus_master_tcp.js +219 -0
- package/src/protocol/modbus_server.js +936 -0
- package/src/protocol/modbus_server_serial.js +368 -0
- package/src/protocol/modbus_server_tcp.js +129 -0
- package/src/protocol/utils.js +296 -0
- package/src/server/net/serialserver.js +184 -0
- package/src/server/net/tcpserver.js +290 -0
- package/src/server/net/udpserver.js +242 -0
- package/src/server/nodbus_serial_server.js +238 -0
- package/src/server/nodbus_tcp_server.js +249 -0
- package/test/modbus_master.test.js +279 -0
- package/test/modbus_master_serial.test.js +124 -0
- package/test/modbus_master_tcp.test.js +178 -0
- package/test/modbus_server.test.js +506 -0
- package/test/modbus_server_serial.test.js +328 -0
- package/test/modbus_server_tcp.test.js +91 -0
- package/test/nodbus_client_serial.test.js +307 -0
- package/test/nodbus_client_tcp.test.js +334 -0
- package/test/nodbus_server_serial.test.js +255 -0
- package/test/nodbus_server_tcp.test.js +216 -0
- package/CHANGELOG.md +0 -27
- package/src/client/m_stcp_client.js +0 -214
- package/src/client/m_tcp_client.js +0 -234
- package/src/net/tcpclient.js +0 -173
- package/src/net/tcpserver.js +0 -329
- package/src/protocol/adu.js +0 -40
- package/src/protocol/ascii_adu.js +0 -139
- package/src/protocol/boolean_register.js +0 -78
- package/src/protocol/functions/Force_Multiple_Coils.js +0 -76
- package/src/protocol/functions/Force_Single_Coil.js +0 -54
- package/src/protocol/functions/Mask_Holding_Register.js +0 -47
- package/src/protocol/functions/Preset_Multiple_Registers.js +0 -53
- package/src/protocol/functions/Preset_Single_Register.js +0 -39
- package/src/protocol/functions/Read_Coil_Status.js +0 -59
- package/src/protocol/functions/Read_Holding_Registers.js +0 -52
- package/src/protocol/functions/Read_Input_Registers.js +0 -52
- package/src/protocol/functions/Read_Input_Status.js +0 -58
- package/src/protocol/mbap.js +0 -60
- package/src/protocol/modbus_device.js +0 -35
- package/src/protocol/modbus_slave.js +0 -522
- package/src/protocol/pdu.js +0 -70
- package/src/protocol/rtu_adu.js +0 -122
- package/src/protocol/serial_adu.js +0 -29
- package/src/protocol/tcp_adu.js +0 -84
- package/src/protocol/word_register.js +0 -122
- package/src/server/m_stcp_server.js +0 -310
- package/src/server/m_tcp_server.js +0 -295
- package/test/modbus-stcp-server-test.js +0 -72
- package/test/modbus-stcp-server-test1.js +0 -72
- package/test/modbus-tcp-client-test.js +0 -159
- package/test/modbus-tcp-server-test.js +0 -75
- package/test/modbus-tcp-server-test2.js +0 -75
- 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;
|