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,247 @@
1
+ /**
2
+ ** Modbus Serial Master Base Class module.
3
+ * Only deal with basic ADU operations
4
+ * @module protocol/modbus_master_tcp
5
+ * @author Hector E. Socarras.
6
+ * @version 1.0.0
7
+ */
8
+
9
+ const valByte2Chars = require('./utils.js').valByteToChars;
10
+
11
+ const ModbusClient = require('./modbus_master');
12
+
13
+ /**
14
+ * Class representing a modbus master.
15
+ * @extends ModbusClient
16
+ */
17
+ class ModbusSerialClient extends ModbusClient {
18
+ /**
19
+ * Create a Modbus Master.
20
+ */
21
+ constructor(){
22
+ super();
23
+
24
+
25
+ /**
26
+ * Current pending request
27
+ * @type {Buffer}
28
+ */
29
+ this.activeRequest = null;
30
+
31
+ this._asciiRequest = false;
32
+
33
+ /**
34
+ * Variable that holds the timer's id for request timeout
35
+ */
36
+ this.activeRequestTimerId = -1;
37
+
38
+ /**
39
+ * variable that holds the timer used when broadcast request is sended.
40
+ */
41
+ this.turnAroundDelay = -1;
42
+
43
+
44
+
45
+ }
46
+
47
+
48
+
49
+ /**
50
+ * Function to make a request buffer from pdu buffer and unit id.
51
+ * @param {number} address Legacy modbus address
52
+ * @param {Buffer} pdu Buffer with modbus pdu
53
+ * @param {boolean} asciiMode. If true the request is made in ascii format.
54
+ * @returns {Buffer} buffer with request if succesfull or null.
55
+ */
56
+ makeRequest(address, pdu, asciiMode = false){
57
+
58
+ if(pdu instanceof Buffer & address >= 0 & address <= 247){
59
+
60
+ let reqBuffer = Buffer.alloc(3 + pdu.length);
61
+ reqBuffer[0] = address;
62
+ pdu.copy(reqBuffer, 1);
63
+
64
+ if (asciiMode){
65
+ let reqAsciiBuffer = this.aduRtuToAscii(reqBuffer);
66
+ return reqAsciiBuffer
67
+ }
68
+ else{
69
+ //calculating crc and appending to request
70
+ let crc = this.calcCRC(reqBuffer);
71
+ reqBuffer.writeUInt16BE(crc, reqBuffer.length-2);
72
+ return reqBuffer;
73
+ }
74
+
75
+ }
76
+ else{
77
+ return null;
78
+ }
79
+
80
+ }
81
+
82
+ /**
83
+ * Function to store the request in activeRequest and set the ascii flag is the request is in ascii Format.
84
+ * @param {Buffer} bufferReq
85
+ * @returns {boolean} true if is succesful stored, false otherwise
86
+ */
87
+ storeRequest(bufferReq, asciiMode = false){
88
+
89
+ let len = bufferReq.length;
90
+
91
+ //storing request on the pool
92
+ if(this.activeRequest == null){
93
+ if(asciiMode){
94
+ this._asciiRequest = true;
95
+ }
96
+ else{
97
+ this._asciiRequest = false;
98
+ }
99
+ this.activeRequest = bufferReq;
100
+ return true
101
+ }
102
+ else{
103
+ return false;
104
+ }
105
+
106
+ }
107
+
108
+ /**
109
+ * Function to initialize a timer for timeout detection.
110
+ * @param {number} transactionId
111
+ * @param {number} timeout
112
+ * @return {number} timer id for cleartimeout function or -1 if no timer was set.
113
+ * @emits req-timeout. Timeout event
114
+ */
115
+ setReqTimer(timeout = 100){
116
+
117
+ let self = this;
118
+
119
+ if(this.activeRequest instanceof Buffer & typeof timeout === 'number' & timeout >= 1){
120
+
121
+ let timerId = setTimeout(()=>{
122
+ self.emit('req-timeout', self.activeRequest); //what to do when timeout occurs is desition for the user app
123
+ }, timeout);
124
+
125
+ self.activeRequestTimerId = timerId;
126
+
127
+ return timerId
128
+ }
129
+ else{
130
+ return -1;
131
+ }
132
+
133
+ }
134
+
135
+ /**
136
+ * Function to initialize a timer for timeout detection on broadcast request.
137
+ * @param {number} transactionId
138
+ * @param {number} timeout
139
+ * @return {number} timer id for cleartimeout function or -1 if no timer was set.
140
+ * @emits req-timeout. Timeout event
141
+ */
142
+ setTurnAroundDelay(timeout = 100){
143
+
144
+ let self = this;
145
+
146
+ if(this.activeRequest instanceof Buffer & typeof timeout === 'number' & timeout >= 1){
147
+
148
+ let timerId = setTimeout(()=>{
149
+ self.activeRequest = null;
150
+ self.emit('broadcast-timeout'); //what to do when timeout occurs is desition for the user app
151
+ }, timeout);
152
+
153
+ self.turnAroundDelay = timerId;
154
+
155
+ return timerId
156
+ }
157
+ else{
158
+ return -1;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Funcion to clear the timeout imer for a given request
164
+ * @param {number} transactionId
165
+ */
166
+ clearReqTimer(){
167
+
168
+
169
+ clearTimeout(this.activeRequestTimerId);
170
+
171
+ }
172
+
173
+ /**
174
+ * Function to make basic processing to response
175
+ * @param {buffer} bufferAdu adu buffer
176
+ * @emits transaction. Event emited when a response is received.
177
+ */
178
+ processResAdu(bufferAdu, ascii = false){
179
+
180
+ //check that client is waiting for replay (this.activerequest is diferent than null)
181
+ if(bufferAdu instanceof Buffer & this.activeRequest instanceof Buffer){
182
+ if(ascii){
183
+ if(bufferAdu.length >= 9){
184
+ //validating error check
185
+ //Modbus Ascii Frame
186
+ let calcErrorCheck = this.calcLRC(bufferAdu);
187
+ let frameErrorCheck = Number('0x' + bufferAdu.toString('ascii', bufferAdu.length-4, bufferAdu.length-2));
188
+
189
+ if(calcErrorCheck == frameErrorCheck){
190
+
191
+ //valid frame
192
+ let res = this.aduAsciiToRtu(bufferAdu);
193
+ let req = this.aduAsciiToRtu(this.activeRequest);
194
+
195
+ if(res[0] == req[0]){
196
+ //if address match
197
+ this.clearReqTimer();
198
+ this.activeRequest = null;
199
+ this._asciiRequest = false;
200
+ this.emit('transaction', req, res);
201
+
202
+ }
203
+
204
+ }
205
+
206
+ }
207
+ }
208
+ else{
209
+ if(bufferAdu.length >= 4){
210
+ //validating error check
211
+ //Modbus Ascii Frame
212
+ let calcErrorCheck = this.calcCRC(bufferAdu);
213
+ let frameErrorCheck = bufferAdu.readUInt16BE(bufferAdu.length - 2);
214
+
215
+ if(calcErrorCheck == frameErrorCheck){
216
+ //valid frame
217
+ let res = bufferAdu;
218
+ if(res[0] == this.activeRequest[0]){
219
+ //if address match
220
+ this.clearReqTimer();
221
+
222
+ let req = this.activeRequest;
223
+ this.activeRequest = null;
224
+ this.emit('transaction', req, res);
225
+
226
+ }
227
+
228
+ }
229
+
230
+ }
231
+ }
232
+ }
233
+
234
+
235
+ }
236
+
237
+ }
238
+
239
+ ModbusSerialClient.prototype.aduAsciiToRtu = require('./utils.js').aduAsciiToRtu;
240
+
241
+ ModbusSerialClient.prototype.aduRtuToAscii = require('./utils.js').aduRtuToAscii;
242
+
243
+ ModbusSerialClient.prototype.calcCRC = require('./utils.js').calcCRC;
244
+
245
+ ModbusSerialClient.prototype.calcLRC = require('./utils.js').calcLRC;
246
+
247
+ module.exports = ModbusSerialClient;
@@ -0,0 +1,219 @@
1
+ /**
2
+ ** Modbus TCP Master Base Class module.
3
+ * Only deal with basic ADU operations
4
+ * @module protocol/modbus_master_tcp
5
+ * @author Hector E. Socarras.
6
+ * @version 1.0.0
7
+ */
8
+
9
+
10
+ const ModbusClient = require('./modbus_master');
11
+
12
+ /**
13
+ * Class representing a modbus master.
14
+ * @extends EventEmitter
15
+ */
16
+ class ModbusTcpClient extends ModbusClient {
17
+ /**
18
+ * Create a Modbus Master.
19
+ */
20
+ constructor(){
21
+ super();
22
+
23
+ /**
24
+ * transaction counter
25
+ * @type {number}
26
+ */
27
+ this._transactionCount = 0;
28
+
29
+ /**
30
+ * Max number of transaction
31
+ * @type {number}
32
+ */
33
+ this.maxNumberOfTransaction = 64;
34
+
35
+ /**
36
+ * Pool with pending request
37
+ * @type {map}
38
+ */
39
+ this.reqPool = new Map();
40
+
41
+ this.reqTimersPool = new Map();
42
+
43
+
44
+
45
+ }
46
+
47
+ get transactionCount(){
48
+ return this._transactionCount;
49
+ }
50
+
51
+ set transactionCount(count){
52
+ if(typeof count === 'number'){
53
+ if(count >= 0 & count <= 0xFF){
54
+ this._transactionCount = count;
55
+ }
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Function to make the modbus tcp header
61
+ * @param {number} unitId Legacy modbus address
62
+ * @param {number} pduLength pdu's legth in bytes
63
+ * @return {buffer} header buffer.
64
+ */
65
+ makeHeader(unitId, pduLength){
66
+
67
+ let header = Buffer.alloc(7);
68
+ header.writeUInt16BE(this.transactionCount);
69
+ header.writeUInt16BE(0,2);
70
+ header.writeUInt16BE(pduLength + 1, 4);
71
+ header.writeUInt8(unitId, 6);
72
+
73
+ return header;
74
+ }
75
+
76
+ /**
77
+ * Function to extract data from modbus tcp header.
78
+ * @param {Buffer} bufferHeader buffer with modbus tcp header .
79
+ * @return {object} object containing header's field {transactionID, protocolID, length, unitID}.
80
+ * @throws {TypeError} if bufferHeader is not a Buffer.
81
+ * @throws {RangeError} if bufferHeader's length is diferent than 7..
82
+ */
83
+ parseHeader(bufferHeader){
84
+
85
+ let header = {};
86
+ if(bufferHeader instanceof Buffer){
87
+ if(bufferHeader.length == 7){
88
+ header.transactionId = bufferHeader.readUInt16BE(0);
89
+ header.protocolId = bufferHeader.readUInt16BE(2);
90
+ header.length = bufferHeader.readUInt16BE(4);
91
+ header.unitId = bufferHeader.readUInt8(6);
92
+ return header;
93
+ }
94
+ else{
95
+ throw new RangeError('Error: Header must be 7 bytes long');
96
+ }
97
+ }
98
+ else{
99
+ throw new TypeError('Error: Header must be a buffer instance');
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Function to make a request buffer from pdu buffer and unit id.
105
+ * @param {number} unitId Legacy modbus address
106
+ * @param {Buffer} pdu Buffer with modbus pdu
107
+ * @returns {object} buffer with request if succesfull or null.
108
+ */
109
+ makeRequest(unitId, pdu){
110
+
111
+ if(pdu instanceof Buffer & unitId <= 255){
112
+
113
+ //Increment of transaction count
114
+ this.transactionCount++;
115
+
116
+ let header = this.makeHeader(unitId, pdu.length);
117
+
118
+ //allocating memory for request
119
+ let reqBuffer = Buffer.alloc(7 + pdu.length);
120
+
121
+ //filling request buffer with data
122
+ header.copy(reqBuffer);
123
+ pdu.copy(reqBuffer, 7);
124
+
125
+ return reqBuffer;
126
+
127
+ }
128
+ else{
129
+ return null;
130
+ }
131
+
132
+ }
133
+
134
+ /**
135
+ * Function to store the request on the request pool.
136
+ * A modbus Tcp client can have more than one request sended to same server and other server.
137
+ * @param {Buffer} bufferReq
138
+ * @returns {boolean} true if is succesful stored on the pool, false otherwise
139
+ */
140
+ storeRequest(bufferReq){
141
+ //storing request on the pool
142
+ if(this.reqPool.size <= this.maxNumberOfTransaction){
143
+ let transactionId = bufferReq.readUInt16BE(0);
144
+ //using the transaction Id as key
145
+ this.reqPool.set(transactionId, bufferReq);
146
+
147
+ return true;
148
+ }
149
+ else{
150
+ return false;
151
+ }
152
+
153
+ }
154
+
155
+ /**
156
+ * Function to initialize a timer for timeout detection.
157
+ * @param {number} transactionId
158
+ * @param {number} timeout
159
+ * @return {number} timer id for cleartimeout function or -1 if no timer was set.
160
+ * @emits req-timeout. Timeout event
161
+ */
162
+ setReqTimer(transactionId, timeout = 100){
163
+
164
+ let self = this;
165
+
166
+ //first, check thar the request exist on the request pool.
167
+ if(this.reqPool.has(transactionId) & typeof timeout === 'number' & timeout >= 1){
168
+
169
+ let req = self.reqPool.get(transactionId);
170
+ let timerId = setTimeout(()=>{
171
+ self.emit('req-timeout', transactionId, req); //what to do when timeout occurs is decision for the user app
172
+ }, timeout);
173
+ //storing timer on the timer's pool
174
+ self.reqTimersPool.set(transactionId, timerId)
175
+
176
+ return timerId
177
+ }
178
+ else{
179
+ return -1;
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Funcion to clear the timeout imer for a given request
185
+ * @param {number} transactionId
186
+ */
187
+ clearReqTimer(transactionId){
188
+
189
+ if(this.reqTimersPool.has(transactionId)){
190
+ let timerId = this.reqTimersPool.get(transactionId);
191
+ clearTimeout(timerId);
192
+ this.reqTimersPool.delete(transactionId);
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Function to make basic processing to response
198
+ * @param {buffer} bufferAdu adu buffer
199
+ * @emits transaction. Event emited when a response is received.
200
+ */
201
+ processResAdu(bufferAdu){
202
+
203
+ let transactionId = bufferAdu.readUInt16BE(0); //getting the transaction id
204
+
205
+ if(this.reqPool.has(transactionId)){
206
+
207
+ //removing timer
208
+ this.clearReqTimer(transactionId);
209
+ //getting the original req
210
+ let req = this.reqPool.get(transactionId);
211
+ this.reqPool.delete(transactionId);
212
+ this.emit('transaction', req, bufferAdu);
213
+ }
214
+
215
+ }
216
+
217
+ }
218
+
219
+ module.exports = ModbusTcpClient;