Browse files

The individual REQUEST and RESPONSE parsing functions no longer need …

…to parse the Function Code, as it is now handled internally.

Also, READ_INPUT_REGISTERS, and any other "list" type response should directly return an Array instance.
  • Loading branch information...
1 parent b39b01f commit 5f0492f8b2ffa0d3b76797a353ff88c8f0cd9ee5 @TooTallNate committed Dec 31, 2010
Showing with 48 additions and 25 deletions.
  1. +1 −1 examples/readInputRegisters.js
  2. +7 −8 lib/client.js
  3. +39 −13 lib/modbus-stack.js
  4. +0 −2 lib/server.js
  5. +1 −1 package.json
View
2 examples/readInputRegisters.js
@@ -17,7 +17,7 @@ client.request(fc, startAddress, numToRead, function(err, response) {
throw err;
}
- [].slice.call(response).forEach(function(register, i) {
+ response.forEach(function(register, i) {
console.log(
("Sensor " + String(startAddress + i).bold + ":\t").blue +
(String(register/10).bold + '\u00B0F').green
View
15 lib/client.js
@@ -8,7 +8,6 @@ exports.REQUESTS = {
// READ_INPUT_REGISTERS
4: function(startAddress, quantity) {
return Put()
- .word8(4)
.word16be(startAddress)
.word16be(quantity)
.buffer();
@@ -18,14 +17,14 @@ exports.REQUESTS = {
exports.RESPONSES = {
// READ_INPUT_REGISTERS
4: function(bufferlist) {
+ var rtn = [];
var binary = Binary(bufferlist)
- .getWord8('functionCode')
- .getWord8('byteCount').end();
- binary.vars.length = binary.vars.byteCount;
- for (var i=0, l=binary.vars.length; i<l; i++) {
- binary.getWord16be(i+"");
+ .getWord8('byteLength').end();
+ rtn.byteLength = binary.vars.byteLength;
+ for (var i=0, l=binary.vars.byteLength/2; i<l; i++) {
+ binary.getWord16be("val");
+ rtn.push(binary.end().vars.val);
}
- return binary.end().vars;
+ return rtn;
}
};
-
View
52 lib/modbus-stack.js
@@ -7,9 +7,18 @@ var client = require('./client');
var server = require('./server');
var slice = Array.prototype.slice;
-// The length of the "MODBUS Application Protocol" header.
+// The byte length of the "MODBUS Application Protocol" header.
const MBAP_LENGTH = 7;
+// The byte length of the "MODBUS Function Code".
+const FUNCTION_CODE_LENGTH = 1;
+
+// An exception response from a MODBUS slave (server) will have
+// the high-bit (0x80) set on it's function code.
+const EXCEPTION_BIT = 1 << 7;
+
+// If it's an exception response, then the next byte will be one
+// these exception codes, indicating the reason for the failure.
exports.EXCEPTION_CODES = {
1 : 'Illegal Function',
2 : 'Illegal Data Address',
@@ -22,6 +31,9 @@ exports.EXCEPTION_CODES = {
11: 'Gateway Target Path Failed to Respond'
};
+// Each of the function codes has a potentially different body payload
+// and potentially different parameters to send. Each function code needs
+// a 'request' and 'response' parser in the "client.js" and "server.js" files.
exports.FUNCTION_CODES = {
READ_COILS: 1,
READ_DISCRETE_INPUTS: 2,
@@ -89,8 +101,9 @@ ModbusRequestStack.prototype.request = function(functionCode) {
return Put()
.word16be(this.stream._reqNum)
.word16be(this.protocolVersion)
- .word16be(pdu.length+1)
+ .word16be(pdu.length+2)
.word8(this.unitIdentifier)
+ .word8(functionCode)
.put(pdu)
.write(this.stream);
}
@@ -102,13 +115,21 @@ ModbusRequestStack.prototype._onData = function(chunk) {
this.responseHeader = readMBAP(this.bufferlist);
// Re-check the bufferlist to see if we have the rest of the response already
this._onData();
- } else if (this.bufferlist.length >= (this.responseHeader.length-1)) {
+ } else if (!this._resFunctionCode && this.responseHeader && this.bufferlist.length >= 1) {
+ // Get the function code
+ this._resFunctionCode = readFunctionCode(this.bufferlist);
+ //console.log(this._resFunctionCode);
+ this._onData();
+ } else if (this.responseHeader && this._resFunctionCode >= 1 && this.bufferlist.length >= (this.responseHeader.length-2)) {
// We have the complete response.
- var response = client.RESPONSES[this.functionCode].call(this, this.bufferlist);
- this.bufferlist.advance(this.responseHeader.length-1);
+ var response = client.RESPONSES[this._resFunctionCode].call(this, this.bufferlist);
+ this.bufferlist.advance(this.responseHeader.length-2);
+ // Explicitly set the 'functionCode' property.
+ response.functionCode = this._resFunctionCode;
+ delete this._resFunctionCode;
+ // Modbus request/response complete; invoke callbacks and cleanup!
this.emit('response', response);
this.cleanup();
- // Modbus request/response complete!
}
}
@@ -133,21 +154,18 @@ ModbusResponseStack.prototype._onData = function(chunk) {
this._onData();
} else if (!this.functionCode && this.requestHeader && this.bufferlist.length >= 1) {
// Get the function code
- this.functionCode = this.bufferlist.take(1).charCodeAt(0);
+ this.functionCode = readFunctionCode(this.bufferlist);
//console.log(this.functionCode);
- // Don't advance the bufferlist, since the server.REQUEST function should
- // parse the 'functionCode' as a property.
- //this.bufferlist.advance(1);
this._onData();
- } else if (this.requestHeader && this.bufferlist.length >= (this.requestHeader.length-2)) {
+ } else if (this.requestHeader && this.functionCode >= 1 && this.bufferlist.length >= (this.requestHeader.length-2)) {
// We have the complete request.
this.request = server.REQUESTS[this.functionCode].call(this, this.bufferlist);
this.request.functionCode = this.functionCode;
delete this.request.length; // Questionable...
for (var key in this.requestHeader) {
this.request[key] = this.requestHeader[key];
}
- this.bufferlist.advance(this.requestHeader.length-1);
+ this.bufferlist.advance(this.requestHeader.length-2);
//console.log('bufferlist.length: ' + this.bufferlist.length);
this._gotRequest = true;
this.emit('request', this.request);
@@ -163,8 +181,9 @@ ModbusResponseStack.prototype.writeResponse = function() {
return Put()
.word16be(this.request.transactionId)
.word16be(this.request.protocolVersion)
- .word16be(pdu.length+1)
+ .word16be(pdu.length+2)
.word8(this.request.unitIdentifier)
+ .word8(this.functionCode)
.put(pdu)
.write(this.stream);
}
@@ -185,3 +204,10 @@ function readMBAP(bufferlist) {
}
exports.readMBAP = readMBAP;
+// Reads the "MODBUS Function Code", which comes immediately after the MBAP.
+function readFunctionCode(bufferlist) {
+ var rtn = bufferlist.take(FUNCTION_CODE_LENGTH)[0];
+ bufferlist.advance(FUNCTION_CODE_LENGTH);
+ return rtn;
+}
+exports.readFunctionCode = readFunctionCode;
View
2 lib/server.js
@@ -8,7 +8,6 @@ exports.REQUESTS = {
// READ_INPUT_REGISTERS
4: function(bufferlist) {
return Binary(bufferlist)
- .getWord8('functionCode')
.getWord16be('startAddress')
.getWord16be('quantity')
.end().vars;
@@ -20,7 +19,6 @@ exports.RESPONSES = {
4: function(registers) {
if (!Array.isArray(registers)) throw new Error('"Read Input Registers" expects to write an Array of Numbers');
var i=0, l=registers.length, put = Put()
- .word8(4)
.word8(registers.length*2);
for (; i<l; i++) {
put.word16be(registers[i]);
View
2 package.json
@@ -6,7 +6,7 @@
"keywords": ["MODBUS", "protocol", "StreamStack"],
"dependencies": {
"stream-stack": ">= 1.0.1",
- "bufferlist": "*",
+ "bufferlist": ">= 0.0.6",
"put": "*"
},
"devDependencies" : {

0 comments on commit 5f0492f

Please sign in to comment.