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,936 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modbus Server Base Class module. Can only deal with modbus PDU.
|
|
3
|
+
* Implement the base class for a modbus server stack.
|
|
4
|
+
* @module protocol/modbus-server
|
|
5
|
+
* @author Hector E. Socarras.
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const EventEmitter = require('node:events');
|
|
11
|
+
const { Buffer } = require('node:buffer');
|
|
12
|
+
const utils = require('./utils');
|
|
13
|
+
|
|
14
|
+
//define max number of coil, inputs or register
|
|
15
|
+
const MAX_ITEM_NUMBER = 65535;
|
|
16
|
+
|
|
17
|
+
//Default Server's Configuration object
|
|
18
|
+
const defaultCfg = {
|
|
19
|
+
inputs : 2048,
|
|
20
|
+
coils : 2048,
|
|
21
|
+
holdingRegisters : 2048,
|
|
22
|
+
inputRegisters : 2048
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Class representing a modbus slave.
|
|
27
|
+
* @extends EventEmitter
|
|
28
|
+
*/
|
|
29
|
+
class ModbusServer extends EventEmitter {
|
|
30
|
+
/**
|
|
31
|
+
* Create a Modbus Basic server.
|
|
32
|
+
* @param {object} mbServerCfg - Configuration object, must have the following properties:
|
|
33
|
+
* {
|
|
34
|
+
* inputs : {int} cuantity of inputs, 0 means that imputs uses same buffer thar input registers.
|
|
35
|
+
* coils : {int} cuantity of coils, 0 means that coils uses same buffer thar coils registers.
|
|
36
|
+
* holdingRegisters : {int} cuantity of holding registers with 2 bytes per register
|
|
37
|
+
* inputRegisters : {int} cuantity of input registers with 2 bytes per register
|
|
38
|
+
* }
|
|
39
|
+
*/
|
|
40
|
+
constructor(mbServerCfg = defaultCfg){
|
|
41
|
+
super();
|
|
42
|
+
|
|
43
|
+
var self = this;
|
|
44
|
+
|
|
45
|
+
//arguments check
|
|
46
|
+
if(mbServerCfg.inputs == undefined){mbServerCfg.inputs = defaultCfg.inputs;}
|
|
47
|
+
if(mbServerCfg.coils == undefined){ mbServerCfg.coils = defaultCfg.coils;}
|
|
48
|
+
if(mbServerCfg.holdingRegisters == undefined){mbServerCfg.holdingRegisters = defaultCfg.holdingRegisters;}
|
|
49
|
+
if(mbServerCfg.inputRegisters == undefined){mbServerCfg.inputRegisters = defaultCfg.inputRegisters;}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get server's modbus functions code supported
|
|
53
|
+
* @return {set object} a set objects with funcion codes suported for the server
|
|
54
|
+
*
|
|
55
|
+
*/
|
|
56
|
+
this._internalFunctionCode = new Map();
|
|
57
|
+
this._internalFunctionCode.set(1, 'readCoilsService');
|
|
58
|
+
this._internalFunctionCode.set(2, 'readDiscreteInputsService');
|
|
59
|
+
this._internalFunctionCode.set(3, 'readHoldingRegistersService');
|
|
60
|
+
this._internalFunctionCode.set(4, 'readInputRegistersService');
|
|
61
|
+
this._internalFunctionCode.set(5, 'writeSingleCoilService');
|
|
62
|
+
this._internalFunctionCode.set(6, 'writeSingleRegisterService');
|
|
63
|
+
this._internalFunctionCode.set(15, 'writeMultipleCoilsService');
|
|
64
|
+
this._internalFunctionCode.set(16, 'writeMultipleRegistersService');
|
|
65
|
+
this._internalFunctionCode.set(22, 'maskWriteRegisterService');
|
|
66
|
+
this._internalFunctionCode.set(23, 'readWriteMultipleRegistersService');
|
|
67
|
+
Object.defineProperty(self, 'supportedFunctionCode',{
|
|
68
|
+
get: function(){
|
|
69
|
+
return this._internalFunctionCode.keys();
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Holding Registers.
|
|
76
|
+
* @type {Object}
|
|
77
|
+
* @public
|
|
78
|
+
*/
|
|
79
|
+
if(mbServerCfg.holdingRegisters <= MAX_ITEM_NUMBER & mbServerCfg.holdingRegisters > 0){
|
|
80
|
+
this.holdingRegisters = Buffer.alloc(mbServerCfg.holdingRegisters*2);
|
|
81
|
+
}
|
|
82
|
+
else if(mbServerCfg.holdingRegisters >= MAX_ITEM_NUMBER){
|
|
83
|
+
this.holdingRegisters = Buffer.alloc(MAX_ITEM_NUMBER*2);
|
|
84
|
+
}
|
|
85
|
+
else{
|
|
86
|
+
this.holdingRegisters = Buffer.alloc(defaultCfg.holdingRegisters*2);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Input Registers.
|
|
91
|
+
* @type {Buffer}
|
|
92
|
+
* @public
|
|
93
|
+
*/
|
|
94
|
+
if(mbServerCfg.inputRegisters <= MAX_ITEM_NUMBER & mbServerCfg.inputRegisters > 0){
|
|
95
|
+
this.inputRegisters = Buffer.alloc(mbServerCfg.inputRegisters * 2);
|
|
96
|
+
}
|
|
97
|
+
else if(mbServerCfg.inputRegisters >= MAX_ITEM_NUMBER){
|
|
98
|
+
this.inputRegisters = Buffer.alloc(MAX_ITEM_NUMBER*2);
|
|
99
|
+
}
|
|
100
|
+
else{
|
|
101
|
+
this.inputRegisters = Buffer.alloc(defaultCfg.inputRegisters*2);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Inputs. Reference 1x.
|
|
106
|
+
* @type {Buffer}
|
|
107
|
+
* @public
|
|
108
|
+
*/
|
|
109
|
+
if(mbServerCfg.inputs <= MAX_ITEM_NUMBER & mbServerCfg.inputs > 0){
|
|
110
|
+
this.inputs = Buffer.alloc(Math.ceil(mbServerCfg.inputs/8));
|
|
111
|
+
}
|
|
112
|
+
else if(mbServerCfg.inputs >= MAX_ITEM_NUMBER){
|
|
113
|
+
this.inputs = Buffer.alloc(Math.ceil(MAX_ITEM_NUMBER/8));
|
|
114
|
+
}
|
|
115
|
+
else if (mbServerCfg.inputs <= 0){
|
|
116
|
+
this.inputs = this.inputRegisters;
|
|
117
|
+
}
|
|
118
|
+
else{
|
|
119
|
+
this.inputs = Buffer.alloc(Math.ceil(defaultCfg.inputs/8));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Coils. Reference 0x
|
|
125
|
+
* @type {buffer}
|
|
126
|
+
* @public
|
|
127
|
+
*/
|
|
128
|
+
if(mbServerCfg.coils <= MAX_ITEM_NUMBER & mbServerCfg.coils > 0){
|
|
129
|
+
this.coils = Buffer.alloc(Math.ceil(mbServerCfg.coils/8));
|
|
130
|
+
}
|
|
131
|
+
else if(mbServerCfg.coils >= MAX_ITEM_NUMBER){
|
|
132
|
+
this.coils = Buffer.alloc(Math.ceil(MAX_ITEM_NUMBER/8));
|
|
133
|
+
}
|
|
134
|
+
else if (mbServerCfg.coils <= 0){
|
|
135
|
+
this.coils = this.holdingRegisters;
|
|
136
|
+
}
|
|
137
|
+
else{
|
|
138
|
+
this.coils = Buffer.alloc(Math.ceil(defaultCfg.coils / 8));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @brief Main server function. Entry point for client request. Process request pdu, execute de service and return a response pdu.
|
|
147
|
+
* @param {Buffer} reqPduBuffe buffer containing a protocol data unit
|
|
148
|
+
* @fires ModbusServer#exception
|
|
149
|
+
* @fires ModbusServer#write
|
|
150
|
+
* @fires ModbusServer#error
|
|
151
|
+
* @return {Buffer} buffer containing a protocol data unit
|
|
152
|
+
*/
|
|
153
|
+
processReqPdu(reqPduBuffer) {
|
|
154
|
+
|
|
155
|
+
let self = this;
|
|
156
|
+
let functionCode = reqPduBuffer[0];
|
|
157
|
+
|
|
158
|
+
//Check for function code
|
|
159
|
+
if(this._internalFunctionCode.has(functionCode)){
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
//gets pdu data
|
|
163
|
+
let reqPduData = Buffer.alloc(reqPduBuffer.length - 1);
|
|
164
|
+
reqPduBuffer.copy(reqPduData,0,1);
|
|
165
|
+
|
|
166
|
+
let serviceName = this._internalFunctionCode.get(functionCode); //get de function code prossesing function
|
|
167
|
+
var resPduBuffer = this[serviceName](reqPduData); //execute service procesing
|
|
168
|
+
|
|
169
|
+
return resPduBuffer;
|
|
170
|
+
}
|
|
171
|
+
catch(e){
|
|
172
|
+
//reply modbus exception 4
|
|
173
|
+
resPduBuffer = this.makeExceptionResPdu(functionCode, 4); //Slave failure exception response
|
|
174
|
+
this.emit('error', e);
|
|
175
|
+
return resPduBuffer;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
else{
|
|
179
|
+
//reply modbus exception 1
|
|
180
|
+
resPduBuffer = this.makeExceptionResPdu(functionCode, 1);
|
|
181
|
+
return resPduBuffer;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @brief Build a modbus exception response PDU
|
|
189
|
+
* @param {number} mbFunctionCode modbus function code
|
|
190
|
+
* @param {number} exceptionCode code of modbus exception
|
|
191
|
+
* @fires ModbusServer#exception
|
|
192
|
+
* @return {Buffer} Exception response pdu
|
|
193
|
+
*/
|
|
194
|
+
makeExceptionResPdu(mbFunctionCode, exceptionCode){
|
|
195
|
+
|
|
196
|
+
//setting modbus function to exception
|
|
197
|
+
let excepFunctionCode = mbFunctionCode | 0x80;
|
|
198
|
+
//setting exeption code
|
|
199
|
+
let excepResBuffer = Buffer.alloc(2);
|
|
200
|
+
excepResBuffer[0] = excepFunctionCode;
|
|
201
|
+
excepResBuffer[1] = exceptionCode;
|
|
202
|
+
|
|
203
|
+
switch(exceptionCode){
|
|
204
|
+
case 1:
|
|
205
|
+
this.emit('exception', mbFunctionCode, exceptionCode, 'ILLEGAL FUNCTION');
|
|
206
|
+
break;
|
|
207
|
+
case 2:
|
|
208
|
+
this.emit('exception', mbFunctionCode, exceptionCode, 'ILLEGAL DATA ADDRESS');
|
|
209
|
+
break;
|
|
210
|
+
case 3:
|
|
211
|
+
this.emit('exception', mbFunctionCode, exceptionCode, 'ILLEGAL DATA VALUE');
|
|
212
|
+
break;
|
|
213
|
+
case 4:
|
|
214
|
+
this.emit('exception', mbFunctionCode, exceptionCode, 'SLAVE DEVICE FAILURE');
|
|
215
|
+
break;
|
|
216
|
+
case 5:
|
|
217
|
+
this.emit('exception', mbFunctionCode, exceptionCode, 'ACKNOWLEDGE');
|
|
218
|
+
break;
|
|
219
|
+
case 6:
|
|
220
|
+
this.emit('exception', mbFunctionCode, exceptionCode, 'SLAVE DEVICE BUSY');
|
|
221
|
+
break;
|
|
222
|
+
case 8:
|
|
223
|
+
this.emit('exception', mbFunctionCode, exceptionCode, 'MEMORY PARITY ERROR');
|
|
224
|
+
break;
|
|
225
|
+
case 0x0A:
|
|
226
|
+
this.emit('exception', mbFunctionCode, exceptionCode, 'GATEWAY PATH UNAVAILABLE');
|
|
227
|
+
break;
|
|
228
|
+
case 0x0B:
|
|
229
|
+
this.emit('exception', mbFunctionCode, exceptionCode, 'GATEWAY TARGET DEVICE FAILED TO RESPOND');
|
|
230
|
+
break;
|
|
231
|
+
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return excepResBuffer;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* @brief Function to implement Read Coil stauts service on server. Function code 01.
|
|
239
|
+
* @param {Buffer} pduReqData buffer containing only data from a request pdu
|
|
240
|
+
* @fires ModbusServer#exception
|
|
241
|
+
* @return {Buffer} resPduBuffer. Response pdu.
|
|
242
|
+
*/
|
|
243
|
+
readCoilsService(pduReqData){
|
|
244
|
+
|
|
245
|
+
//Defining function code for this service
|
|
246
|
+
const FUNCTION_CODE = 1;
|
|
247
|
+
|
|
248
|
+
let resPduBuffer;
|
|
249
|
+
|
|
250
|
+
if(pduReqData.length == 4){
|
|
251
|
+
//registers to read
|
|
252
|
+
let registersToRead = pduReqData.readUInt16BE(2);
|
|
253
|
+
|
|
254
|
+
//Validating Data Value. Max number of coils to read is 2000 acording to Modbus Aplication Protocol V1.1b3 2006
|
|
255
|
+
if(registersToRead >=1 && registersToRead <= 2000){
|
|
256
|
+
//initial register. Example coil 20 addressing as 0x13 (19)
|
|
257
|
+
let startAddress = pduReqData.readUInt16BE(0);
|
|
258
|
+
|
|
259
|
+
//Validating data address
|
|
260
|
+
if(startAddress + registersToRead < this.coils.length * 8 & startAddress + registersToRead <= MAX_ITEM_NUMBER){
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
//Calculando cantidad de bytes de la respuesta 12%8=1
|
|
264
|
+
//ejemplo 12 coils necesitan 2 bytes
|
|
265
|
+
let byteCount = registersToRead % 8 ? Math.ceil(registersToRead/8) : (registersToRead/8);
|
|
266
|
+
let masks = [0x01, 0x02, 0x04, 0x08, 0x010, 0x20, 0x40, 0x80];
|
|
267
|
+
let values = Buffer.alloc(byteCount);
|
|
268
|
+
resPduBuffer = Buffer.alloc(byteCount + 2);
|
|
269
|
+
resPduBuffer[0] = FUNCTION_CODE;
|
|
270
|
+
resPduBuffer[1] = byteCount;
|
|
271
|
+
|
|
272
|
+
for(let i = 0; i < registersToRead; i++){
|
|
273
|
+
if(this.getBoolFromBuffer(this.coils, startAddress + i)){
|
|
274
|
+
values[Math.floor(i/8)] = values[Math.floor(i/8)] | masks[i%8];
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
values.copy(resPduBuffer, 2);
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
}
|
|
282
|
+
//Making modbus exeption 2
|
|
283
|
+
else{
|
|
284
|
+
//reply modbus exception 2
|
|
285
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 2);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
else{
|
|
289
|
+
//reply modbus exception 3
|
|
290
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 3);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
else{
|
|
294
|
+
//reply modbus exception 3
|
|
295
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 3);
|
|
296
|
+
}
|
|
297
|
+
return resPduBuffer;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* @brief Function to implement Read Input status service on server. Function code 02.
|
|
302
|
+
* @param {Buffer} pduReqData buffer containing only data from a request pdu
|
|
303
|
+
* @fires ModbusServer#exception
|
|
304
|
+
* @return {Buffer} resPduBuffer. Response pdu.
|
|
305
|
+
*/
|
|
306
|
+
readDiscreteInputsService(pduReqData){
|
|
307
|
+
|
|
308
|
+
//Defining function code for this service
|
|
309
|
+
const FUNCTION_CODE = 2;
|
|
310
|
+
|
|
311
|
+
let resPduBuffer;
|
|
312
|
+
if(pduReqData.length == 4){
|
|
313
|
+
//registers to read
|
|
314
|
+
let registersToRead = pduReqData.readUInt16BE(2);
|
|
315
|
+
|
|
316
|
+
//Validating Data Value. Max number of inputss to read is 2000 acording to Modbus Aplication Protocol V1.1b3 2006
|
|
317
|
+
if(registersToRead >=1 && registersToRead <= 2000){
|
|
318
|
+
//initial register. Example coil 20 addressing as 0x13 (19)
|
|
319
|
+
let startAddress = pduReqData.readUInt16BE(0);
|
|
320
|
+
|
|
321
|
+
//Validating data address
|
|
322
|
+
if(startAddress + registersToRead < this.inputs.length * 8 & startAddress + registersToRead <= MAX_ITEM_NUMBER){
|
|
323
|
+
|
|
324
|
+
//Calculando cantidad de bytes de la respuesta 12%8=1
|
|
325
|
+
//example 12 inputss needs 2 bytes
|
|
326
|
+
let byteCount = registersToRead % 8 ? Math.ceil(registersToRead/8) : (registersToRead/8);
|
|
327
|
+
let masks = [0x01, 0x02, 0x04, 0x08, 0x010, 0x20, 0x40, 0x80];
|
|
328
|
+
let values = Buffer.alloc(byteCount);
|
|
329
|
+
resPduBuffer = Buffer.alloc(byteCount + 2);
|
|
330
|
+
resPduBuffer[0] = FUNCTION_CODE;
|
|
331
|
+
resPduBuffer[1] = byteCount;
|
|
332
|
+
|
|
333
|
+
for(let i = 0; i < registersToRead; i++){
|
|
334
|
+
if(this.getBoolFromBuffer(this.inputs, startAddress + i)){
|
|
335
|
+
values[Math.floor(i/8)] = values[Math.floor(i/8)] | masks[i%8];
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
values.copy(resPduBuffer, 2);
|
|
340
|
+
}
|
|
341
|
+
//Making modbus exeption 2
|
|
342
|
+
else{
|
|
343
|
+
//reply modbus exception 2
|
|
344
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 2);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
//Making modbus exeption 3
|
|
348
|
+
else{
|
|
349
|
+
//reply modbus exception 3
|
|
350
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 3);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
else{
|
|
354
|
+
//reply modbus exception 3
|
|
355
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 3);
|
|
356
|
+
}
|
|
357
|
+
return resPduBuffer;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* @brief Function to implement Read Holdings registers service on server. Function code 03.
|
|
362
|
+
* @param {Buffer} pduReqData buffer containing only data from a request pdu
|
|
363
|
+
* @fires ModbusServer#exception
|
|
364
|
+
* @return {Buffer} resPduBuffer. Response pdu.
|
|
365
|
+
*/
|
|
366
|
+
readHoldingRegistersService(pduReqData){
|
|
367
|
+
|
|
368
|
+
//Defining function code for this service
|
|
369
|
+
const FUNCTION_CODE = 3;
|
|
370
|
+
|
|
371
|
+
let resPduBuffer;
|
|
372
|
+
|
|
373
|
+
if(pduReqData.length == 4){
|
|
374
|
+
//registers to read
|
|
375
|
+
let registersToRead = pduReqData.readUInt16BE(2);
|
|
376
|
+
|
|
377
|
+
//Validating Data Value. Max number of registers to read is 125 acording to Modbus Aplication Protocol V1.1b3 2006
|
|
378
|
+
if(registersToRead >=1 && registersToRead <= 0x007D){
|
|
379
|
+
//initial register.
|
|
380
|
+
let startAddress = pduReqData.readUInt16BE(0);
|
|
381
|
+
|
|
382
|
+
//Validating data address
|
|
383
|
+
if(startAddress + registersToRead < this.holdingRegisters.length / 2 ){
|
|
384
|
+
|
|
385
|
+
//Calculando cantidad de bytes de la respuesta
|
|
386
|
+
//example 12 registers needs 2 bytes
|
|
387
|
+
let byteCount = registersToRead * 2;
|
|
388
|
+
let values = Buffer.alloc(byteCount);
|
|
389
|
+
resPduBuffer = Buffer.alloc(byteCount + 2);
|
|
390
|
+
resPduBuffer[0] = FUNCTION_CODE;
|
|
391
|
+
resPduBuffer[1] = byteCount;
|
|
392
|
+
|
|
393
|
+
for(let i = 0; i < registersToRead; i++){
|
|
394
|
+
let word = this.getWordFromBuffer(this.holdingRegisters, startAddress + i);
|
|
395
|
+
word.copy(values, i * 2) ;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
values.copy(resPduBuffer, 2);
|
|
399
|
+
|
|
400
|
+
}
|
|
401
|
+
//Making modbus exeption 2
|
|
402
|
+
else{
|
|
403
|
+
//reply modbus exception 3
|
|
404
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 2);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
else{
|
|
408
|
+
//reply modbus exception 3
|
|
409
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 3);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
else{
|
|
413
|
+
//reply modbus exception 3
|
|
414
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 3);
|
|
415
|
+
}
|
|
416
|
+
return resPduBuffer;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* @brief Function to implement Read Input registers service on server. Function code 04.
|
|
421
|
+
* @param {Buffer} pduReqData buffer containing only data from a request pdu
|
|
422
|
+
* @fires ModbusServer#exception
|
|
423
|
+
* @return {Buffer} resPduBuffer. Response pdu.
|
|
424
|
+
*/
|
|
425
|
+
readInputRegistersService(pduReqData){
|
|
426
|
+
|
|
427
|
+
//Defining function code for this service
|
|
428
|
+
const FUNCTION_CODE = 4;
|
|
429
|
+
|
|
430
|
+
let resPduBuffer;
|
|
431
|
+
if(pduReqData.length == 4){
|
|
432
|
+
//registers to read
|
|
433
|
+
let registersToRead = pduReqData.readUInt16BE(2);
|
|
434
|
+
|
|
435
|
+
//Validating Data Value. Max number of registers to read is 125 acording to Modbus Aplication Protocol V1.1b3 2006
|
|
436
|
+
if(registersToRead >=1 && registersToRead <= 0x007D){
|
|
437
|
+
//initial register.
|
|
438
|
+
let startAddress = pduReqData.readUInt16BE(0);
|
|
439
|
+
|
|
440
|
+
//Validating data address
|
|
441
|
+
if(startAddress + registersToRead < this.inputRegisters.length / 2 ){
|
|
442
|
+
|
|
443
|
+
//Calculando cantidad de bytes de la respuesta
|
|
444
|
+
//example 12 registers needs 2 bytes
|
|
445
|
+
let byteCount = registersToRead * 2;
|
|
446
|
+
let values = Buffer.alloc(byteCount);
|
|
447
|
+
resPduBuffer = Buffer.alloc(byteCount + 2);
|
|
448
|
+
resPduBuffer[0] = FUNCTION_CODE;
|
|
449
|
+
resPduBuffer[1] = byteCount;
|
|
450
|
+
|
|
451
|
+
for(let i = 0; i < registersToRead; i++){
|
|
452
|
+
let word = this.getWordFromBuffer(this.inputRegisters, startAddress + i);
|
|
453
|
+
word.copy(values, i * 2) ;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
values.copy(resPduBuffer, 2);
|
|
457
|
+
|
|
458
|
+
}
|
|
459
|
+
//Making modbus exeption 2
|
|
460
|
+
else{
|
|
461
|
+
//reply modbus exception 3
|
|
462
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 2);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
else{
|
|
466
|
+
//reply modbus exception 3
|
|
467
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 3);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
else{
|
|
471
|
+
//reply modbus exception 3
|
|
472
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 3);
|
|
473
|
+
}
|
|
474
|
+
return resPduBuffer;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* @brief Function to implement force single Coil service on server. Function code 05.
|
|
479
|
+
* @param {Buffer} pduReqData buffer containing only data from a request pdu
|
|
480
|
+
* @fires ModbusServer#exception
|
|
481
|
+
* @fires ModbusServer#write-coils
|
|
482
|
+
* @return {Buffer} resPduBuffer. Response pdu.
|
|
483
|
+
*/
|
|
484
|
+
writeSingleCoilService(pduReqData){
|
|
485
|
+
|
|
486
|
+
//Defining function code for this service
|
|
487
|
+
const FUNCTION_CODE = 5;
|
|
488
|
+
|
|
489
|
+
let resPduBuffer;
|
|
490
|
+
if(pduReqData.length == 4){
|
|
491
|
+
//value to write
|
|
492
|
+
let value = pduReqData.readUInt16BE(2);
|
|
493
|
+
|
|
494
|
+
//Validating Data Value. output value must be 0x00 or 0xFF00 see Modbus Aplication Protocol V1.1b3 2006
|
|
495
|
+
if(value == 0x00 || value == 0xFF00){
|
|
496
|
+
|
|
497
|
+
//coil to write
|
|
498
|
+
let targetCoil = pduReqData.readUInt16BE(0);
|
|
499
|
+
|
|
500
|
+
//Validating data address
|
|
501
|
+
if(targetCoil < this.coils.length * 8 & targetCoil <= MAX_ITEM_NUMBER){
|
|
502
|
+
|
|
503
|
+
resPduBuffer = Buffer.alloc(5);
|
|
504
|
+
resPduBuffer[0] = FUNCTION_CODE;
|
|
505
|
+
|
|
506
|
+
//writing values on register
|
|
507
|
+
this.setBoolToBuffer(value, this.coils, targetCoil);
|
|
508
|
+
pduReqData.copy(resPduBuffer, 1);
|
|
509
|
+
|
|
510
|
+
//creating object of values writed
|
|
511
|
+
//let values = new Map();
|
|
512
|
+
//let coilValue = this.getBoolFromBuffer(this.coils, targetCoil);
|
|
513
|
+
//values.set(targetCoil, coilValue);
|
|
514
|
+
//telling user app that some coils was writed
|
|
515
|
+
this.emit('write-coils', targetCoil, 1);
|
|
516
|
+
|
|
517
|
+
}
|
|
518
|
+
//Making modbus exeption 2
|
|
519
|
+
else{
|
|
520
|
+
//reply modbus exception 3
|
|
521
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 2);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
else{
|
|
525
|
+
//reply modbus exception 3
|
|
526
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 3);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
else{
|
|
530
|
+
//reply modbus exception 3
|
|
531
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 3);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
return resPduBuffer;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* @brief Function to implement write single Register service on server. Function code 06.
|
|
539
|
+
* @param {Buffer} pduReqData buffer containing only data from a request pdu
|
|
540
|
+
* @fires ModbusServer#exception
|
|
541
|
+
* @fires ModbusServer#write-registers
|
|
542
|
+
* @return {Buffer} resPduBuffer. Response pdu.
|
|
543
|
+
*/
|
|
544
|
+
writeSingleRegisterService(pduReqData){
|
|
545
|
+
|
|
546
|
+
//Defining function code for this service
|
|
547
|
+
const FUNCTION_CODE = 6;
|
|
548
|
+
|
|
549
|
+
let resPduBuffer;
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
//Validating Data Value. output value must be 0x00 or 0xFF00 see Modbus Aplication Protocol V1.1b3 2006
|
|
553
|
+
if(pduReqData.length == 4){
|
|
554
|
+
|
|
555
|
+
//registers to write
|
|
556
|
+
let value = Buffer.alloc(2);
|
|
557
|
+
value[0] = pduReqData[2];
|
|
558
|
+
value[1] = pduReqData[3];
|
|
559
|
+
|
|
560
|
+
//register to write
|
|
561
|
+
let targetRegister = pduReqData.readUInt16BE(0);
|
|
562
|
+
|
|
563
|
+
//Validating data address
|
|
564
|
+
if(targetRegister < this.holdingRegisters.length / 2){
|
|
565
|
+
|
|
566
|
+
resPduBuffer = Buffer.alloc(5);
|
|
567
|
+
resPduBuffer[0] = FUNCTION_CODE;
|
|
568
|
+
|
|
569
|
+
//writing values on register
|
|
570
|
+
this.setWordToBuffer(value, this.holdingRegisters, targetRegister);
|
|
571
|
+
pduReqData.copy(resPduBuffer, 1);
|
|
572
|
+
|
|
573
|
+
//creating object of values writed
|
|
574
|
+
//let values = new Map();
|
|
575
|
+
//let registerValue = this.getWordFromBuffer(this.holdingRegisters, targetRegister);
|
|
576
|
+
//values.set(targetRegister, registerValue);
|
|
577
|
+
//telling user app that some coils was writed
|
|
578
|
+
this.emit('write-registers', targetRegister, 1);
|
|
579
|
+
|
|
580
|
+
}
|
|
581
|
+
else{
|
|
582
|
+
//reply modbus exception 2
|
|
583
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 2);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
else{
|
|
587
|
+
//reply modbus exception 3
|
|
588
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 3);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
return resPduBuffer;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* @brief Function to implement write multiple Coils service on server. Function code 15.
|
|
596
|
+
* @param {Buffer} pduReqData buffer containing only data from a request pdu
|
|
597
|
+
* @fires ModbusServer#exception
|
|
598
|
+
* @fires ModbusServer#write-coils
|
|
599
|
+
* @return {Buffer} resPduBuffer. Response pdu.
|
|
600
|
+
*/
|
|
601
|
+
writeMultipleCoilsService(pduReqData){
|
|
602
|
+
|
|
603
|
+
//Defining function code for this service
|
|
604
|
+
const FUNCTION_CODE = 15;
|
|
605
|
+
|
|
606
|
+
let resPduBuffer;
|
|
607
|
+
if(pduReqData.length >=6){
|
|
608
|
+
//amount coils to write
|
|
609
|
+
let cuantityOfOutputs = pduReqData.readUInt16BE(2);
|
|
610
|
+
//byte count
|
|
611
|
+
let byteCount = pduReqData[4];
|
|
612
|
+
//values to force
|
|
613
|
+
let outputValues = pduReqData.subarray(5);
|
|
614
|
+
|
|
615
|
+
//Validating Data Value. output value must be 0x00 or 0xFF00 see Modbus Aplication Protocol V1.1b3 2006
|
|
616
|
+
if(cuantityOfOutputs >= 1 && cuantityOfOutputs <= 0x07B0 && byteCount == Math.ceil(cuantityOfOutputs/8) && byteCount == outputValues.length){
|
|
617
|
+
|
|
618
|
+
//start
|
|
619
|
+
let startingAddress = pduReqData.readUInt16BE(0);
|
|
620
|
+
|
|
621
|
+
//Validating data address
|
|
622
|
+
if(startingAddress + cuantityOfOutputs < this.coils.length * 8 & (startingAddress + cuantityOfOutputs) <= MAX_ITEM_NUMBER){
|
|
623
|
+
|
|
624
|
+
resPduBuffer = Buffer.alloc(5);
|
|
625
|
+
resPduBuffer[0] = FUNCTION_CODE;
|
|
626
|
+
|
|
627
|
+
//writing values on register
|
|
628
|
+
for(let i=0; i < cuantityOfOutputs; i++){
|
|
629
|
+
let value = this.getBoolFromBuffer(outputValues, i);
|
|
630
|
+
this.setBoolToBuffer(value, this.coils, startingAddress + i);
|
|
631
|
+
}
|
|
632
|
+
pduReqData.copy(resPduBuffer, 1);
|
|
633
|
+
|
|
634
|
+
//creating object of values writed
|
|
635
|
+
/*let values = new Map();
|
|
636
|
+
for(let i = 0; i < cuantityOfOutputs; i++){
|
|
637
|
+
let coilValue = this.getBoolFromBuffer(this.coils, startingAddress + i);
|
|
638
|
+
values.set(startingAddress + i, coilValue);
|
|
639
|
+
} */
|
|
640
|
+
//telling user app that some coils was writed
|
|
641
|
+
this.emit('write-coils', startingAddress, cuantityOfOutputs);
|
|
642
|
+
|
|
643
|
+
}
|
|
644
|
+
//Making modbus exeption 2
|
|
645
|
+
else{
|
|
646
|
+
//reply modbus exception 3
|
|
647
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 2);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
else{
|
|
651
|
+
//reply modbus exception 3
|
|
652
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 3);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
else{
|
|
656
|
+
//reply modbus exception 3
|
|
657
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 3);
|
|
658
|
+
}
|
|
659
|
+
return resPduBuffer;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* @brief Function to implement write multiple registers service on server. Function code 16.
|
|
664
|
+
* @param {Buffer} pduReqData buffer containing only data from a request pdu
|
|
665
|
+
* @fires ModbusServer#exception
|
|
666
|
+
* @fires ModbusServer#write
|
|
667
|
+
* @return {Buffer} resPduBuffer. Response pdu.
|
|
668
|
+
*/
|
|
669
|
+
writeMultipleRegistersService(pduReqData){
|
|
670
|
+
|
|
671
|
+
//Defining function code for this service
|
|
672
|
+
const FUNCTION_CODE = 16;
|
|
673
|
+
|
|
674
|
+
let resPduBuffer;
|
|
675
|
+
|
|
676
|
+
if(pduReqData.length >=6){
|
|
677
|
+
//amount to write
|
|
678
|
+
let cuantityOfRegisters = pduReqData.readUInt16BE(2);
|
|
679
|
+
//byte count
|
|
680
|
+
let byteCount = pduReqData[4];;
|
|
681
|
+
//values to force
|
|
682
|
+
let registerValues = pduReqData.subarray(5);
|
|
683
|
+
|
|
684
|
+
//Validating Data Value. output value must be 0x00 or 0xFF00 see Modbus Aplication Protocol V1.1b3 2006
|
|
685
|
+
if(cuantityOfRegisters >= 1 && cuantityOfRegisters <= 0x07B0 && byteCount == cuantityOfRegisters*2 & byteCount == registerValues.length){
|
|
686
|
+
|
|
687
|
+
//start
|
|
688
|
+
let startingAddress = pduReqData.readUInt16BE(0);
|
|
689
|
+
|
|
690
|
+
//Validating data address
|
|
691
|
+
if(startingAddress + cuantityOfRegisters < this.holdingRegisters.length / 2 ){
|
|
692
|
+
|
|
693
|
+
resPduBuffer = Buffer.alloc(5);
|
|
694
|
+
resPduBuffer[0] = FUNCTION_CODE;
|
|
695
|
+
|
|
696
|
+
//writing values on register
|
|
697
|
+
for(let i=0; i < cuantityOfRegisters; i++){
|
|
698
|
+
let value = this.getWordFromBuffer(registerValues, i);
|
|
699
|
+
this.setWordToBuffer(value, this.holdingRegisters, startingAddress + i);
|
|
700
|
+
}
|
|
701
|
+
pduReqData.copy(resPduBuffer, 1);
|
|
702
|
+
|
|
703
|
+
//creating object of values writed
|
|
704
|
+
/*let values = new Map();
|
|
705
|
+
for(let i = 0; i < cuantityOfRegisters; i++){
|
|
706
|
+
let registerValue = this.getWordFromBuffer(this.holdingRegisters, startingAddress + i);
|
|
707
|
+
values.set(startingAddress + i, registerValue);
|
|
708
|
+
} */
|
|
709
|
+
//telling user app that some coils was writed
|
|
710
|
+
this.emit('write-registers', startingAddress, cuantityOfRegisters);
|
|
711
|
+
|
|
712
|
+
}
|
|
713
|
+
//Making modbus exeption 2
|
|
714
|
+
else{
|
|
715
|
+
//reply modbus exception 3
|
|
716
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 2);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
else{
|
|
720
|
+
//reply modbus exception 3
|
|
721
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 3);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
else{
|
|
725
|
+
//reply modbus exception 3
|
|
726
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 3);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
return resPduBuffer;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* @brief Function to implement mask holding register service on server. Function code 22.
|
|
734
|
+
* @param {Buffer} pduReqData buffer containing only data from a request pdu
|
|
735
|
+
* @fires ModbusServer#exception
|
|
736
|
+
* @fires ModbusServer#write-registers
|
|
737
|
+
* @return {Buffer} resPduBuffer. Response pdu.
|
|
738
|
+
*/
|
|
739
|
+
maskWriteRegisterService(pduReqData){
|
|
740
|
+
|
|
741
|
+
//Defining function code for this service
|
|
742
|
+
const FUNCTION_CODE = 22;
|
|
743
|
+
|
|
744
|
+
let resPduBuffer;
|
|
745
|
+
|
|
746
|
+
//Validating data address
|
|
747
|
+
if(pduReqData.length == 6 ){
|
|
748
|
+
|
|
749
|
+
//register to mask
|
|
750
|
+
let referenceAddress = pduReqData.readUInt16BE(0);
|
|
751
|
+
|
|
752
|
+
if(referenceAddress < this.holdingRegisters.length / 2){
|
|
753
|
+
|
|
754
|
+
//masks
|
|
755
|
+
let andMask = pduReqData.readUInt16BE(2);
|
|
756
|
+
let orMask = pduReqData.readUInt16BE(4);
|
|
757
|
+
|
|
758
|
+
resPduBuffer = Buffer.alloc(7);
|
|
759
|
+
resPduBuffer[0] = FUNCTION_CODE;
|
|
760
|
+
|
|
761
|
+
//writing values on register
|
|
762
|
+
let actualValue = this.getWordFromBuffer(this.holdingRegisters, referenceAddress);
|
|
763
|
+
let maskValue = Buffer.alloc(2);
|
|
764
|
+
maskValue.writeUint16BE((actualValue.readUint16BE() & andMask) | (orMask & ~andMask));
|
|
765
|
+
this.setWordToBuffer(maskValue, this.holdingRegisters, referenceAddress)
|
|
766
|
+
|
|
767
|
+
pduReqData.copy(resPduBuffer, 1);
|
|
768
|
+
|
|
769
|
+
//creating object of values writed
|
|
770
|
+
//let values = new Map();
|
|
771
|
+
//let registerValue = this.getWordFromBuffer(this.holdingRegisters, referenceAddress);
|
|
772
|
+
//values.set(referenceAddress, registerValue);
|
|
773
|
+
//telling user app that some coils was writed
|
|
774
|
+
this.emit('write-registers', referenceAddress, 1);
|
|
775
|
+
|
|
776
|
+
}
|
|
777
|
+
else{
|
|
778
|
+
//reply modbus exception 2
|
|
779
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 2);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
else{
|
|
783
|
+
//reply modbus exception 3
|
|
784
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 3);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
return resPduBuffer;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* Function to implement read and write multiple registers service on server. Function code 23.
|
|
792
|
+
* @param {Buffer} pduReqData buffer containing only data from a request pdu
|
|
793
|
+
* @fires ModbusServer#exception
|
|
794
|
+
* @fires ModbusServer#write-registers
|
|
795
|
+
* @return {Buffer} resPduBuffer. Response pdu.
|
|
796
|
+
*/
|
|
797
|
+
readWriteMultipleRegistersService(pduReqData){
|
|
798
|
+
|
|
799
|
+
//Defining function code for this service
|
|
800
|
+
const FUNCTION_CODE = 23;
|
|
801
|
+
|
|
802
|
+
let resPduBuffer;
|
|
803
|
+
|
|
804
|
+
if(pduReqData.length >= 11){
|
|
805
|
+
|
|
806
|
+
//values to read and write
|
|
807
|
+
let cuantityToRead = pduReqData.readUInt16BE(2);
|
|
808
|
+
let cuantityToWrite = pduReqData.readUInt16BE(6);
|
|
809
|
+
//byte count
|
|
810
|
+
let byteCount = pduReqData[8];
|
|
811
|
+
//values to force
|
|
812
|
+
let writeRegisterValues = pduReqData.subarray(9);
|
|
813
|
+
|
|
814
|
+
//Validating Data Value. See Modbus Aplication Protocol V1.1b3 2006
|
|
815
|
+
if(cuantityToRead > 0 && cuantityToRead <= 0x7D && cuantityToWrite > 0 && cuantityToWrite <= 0x79 && byteCount == cuantityToWrite*2 & byteCount == writeRegisterValues.length){
|
|
816
|
+
|
|
817
|
+
//starting addresses
|
|
818
|
+
let readStartingAddress = pduReqData.readUInt16BE(0);
|
|
819
|
+
let writeStartingAddress = pduReqData.readUInt16BE(4);
|
|
820
|
+
|
|
821
|
+
//Validating data address
|
|
822
|
+
if(readStartingAddress + cuantityToRead < this.holdingRegisters.length / 2 && writeStartingAddress + cuantityToWrite < this.holdingRegisters.length / 2){
|
|
823
|
+
|
|
824
|
+
//Calculando cantidad de bytes de la respuesta
|
|
825
|
+
//example 12 registers needs 2 bytes
|
|
826
|
+
let byteCount = cuantityToRead * 2;
|
|
827
|
+
let readValues = Buffer.alloc(byteCount);
|
|
828
|
+
resPduBuffer = Buffer.alloc(byteCount + 2);
|
|
829
|
+
resPduBuffer[0] = FUNCTION_CODE;
|
|
830
|
+
resPduBuffer[1] = byteCount;
|
|
831
|
+
|
|
832
|
+
for(let i = 0; i < cuantityToRead; i++){
|
|
833
|
+
let word = this.getWordFromBuffer(this.holdingRegisters, readStartingAddress + i);
|
|
834
|
+
word.copy(readValues, i * 2) ;
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
readValues.copy(resPduBuffer, 2);
|
|
838
|
+
|
|
839
|
+
//writing values on register
|
|
840
|
+
for(let i=0; i < cuantityToWrite; i++){
|
|
841
|
+
let value = this.getWordFromBuffer(writeRegisterValues, i);
|
|
842
|
+
this.setWordToBuffer(value, this.holdingRegisters, writeStartingAddress + i);
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
//creating object of values writed
|
|
846
|
+
/*let values = new Map();
|
|
847
|
+
for(let i = 0; i < cuantityToWrite; i++){
|
|
848
|
+
let registerValue = this.getWordFromBuffer(this.holdingRegisters, writeStartingAddress + i);
|
|
849
|
+
values.set(writeStartingAddress + i, registerValue);
|
|
850
|
+
} */
|
|
851
|
+
//telling user app that some coils was writed
|
|
852
|
+
this.emit('write-registers', writeStartingAddress, cuantityToWrite);
|
|
853
|
+
|
|
854
|
+
}
|
|
855
|
+
//Making modbus exeption 2
|
|
856
|
+
else{
|
|
857
|
+
//reply modbus exception 3
|
|
858
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 2);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
else{
|
|
862
|
+
//reply modbus exception 3
|
|
863
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 3);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
else{
|
|
867
|
+
//reply modbus exception 3
|
|
868
|
+
resPduBuffer = this.makeExceptionResPdu(FUNCTION_CODE, 3);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
return resPduBuffer;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
/**
|
|
875
|
+
* Low level api function to get a boolean value from buffer.
|
|
876
|
+
* @param {Buffer} targetBuffer buffer object to read
|
|
877
|
+
* @param {number} offset integer value with bit address.
|
|
878
|
+
* @return {boolean} bit value
|
|
879
|
+
* @throws {RangeError} if offset is out of buffer's bound.
|
|
880
|
+
*/
|
|
881
|
+
getBoolFromBuffer(targetBuffer, offset = 0){
|
|
882
|
+
|
|
883
|
+
if(offset < targetBuffer.length * 8){
|
|
884
|
+
|
|
885
|
+
let targetByte = targetBuffer[Math.floor(offset/8)]; //Byte where the bit is place inside the buffer
|
|
886
|
+
let byteOffset = offset % 8; //offset of bit inside the byte
|
|
887
|
+
let masks = [0x01, 0x02, 0x04, 0x08, 0x010, 0x20, 0x40, 0x80];
|
|
888
|
+
|
|
889
|
+
let value = (targetByte & masks[byteOffset]) > 0;
|
|
890
|
+
|
|
891
|
+
return value;
|
|
892
|
+
|
|
893
|
+
}
|
|
894
|
+
else{
|
|
895
|
+
throw new RangeError("offset is out of buffer bounds");
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
/**
|
|
900
|
+
* Low level api function to set a boolean value into a buffer.
|
|
901
|
+
* @param {bool} value boolean value to write
|
|
902
|
+
* @param {Buffer} targetBuffer buffer object to read
|
|
903
|
+
* @param {number} offset integer value with bit address.
|
|
904
|
+
* @throws {RangeError} if offset is out of buffer's bound.
|
|
905
|
+
*/
|
|
906
|
+
setBoolToBuffer(value, targetBuffer, offset = 0){
|
|
907
|
+
|
|
908
|
+
if(offset < targetBuffer.length * 8){
|
|
909
|
+
|
|
910
|
+
let targetOffset = Math.floor(offset / 8); //byte inside the buffer where the bit is placed
|
|
911
|
+
let byteOffset = offset % 8; //offset inside the byte
|
|
912
|
+
let masks = [0x01, 0x02, 0x04, 0x08, 0x010, 0x20, 0x40, 0x80];
|
|
913
|
+
|
|
914
|
+
let previousValue = targetBuffer[targetOffset];
|
|
915
|
+
|
|
916
|
+
if(value){
|
|
917
|
+
targetBuffer[targetOffset] = previousValue | masks[byteOffset];
|
|
918
|
+
}
|
|
919
|
+
else{
|
|
920
|
+
targetBuffer[targetOffset] = previousValue & (~masks[byteOffset]);
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
}
|
|
924
|
+
else{
|
|
925
|
+
throw new RangeError("offset is out of buffer bounds");
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
ModbusServer.prototype.getWordFromBuffer = utils.getWordFromBuffer;
|
|
932
|
+
|
|
933
|
+
ModbusServer.prototype.setWordToBuffer = utils.setWordToBuffer;
|
|
934
|
+
|
|
935
|
+
|
|
936
|
+
module.exports = ModbusServer;
|