Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 2 additions & 14 deletions client.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Client.REQUESTS = {
3: putTwoWord16be,
// READ_INPUT_REGISTERS
4: putTwoWord16be,
// WRITE_SINGLE_COIL
// WRITE_SINGLE_REGISTER:
5: function(address, value) {
if (typeof value !== 'boolean') throw new Error('"Write Single Coil" expects a \'boolean\' value');
return putTwoWord16be(address, value ? 0xff00 : 0x0000);
Expand All @@ -69,17 +69,5 @@ Client.RESPONSES = {
rtn.push(binary.end().vars.val);
}
return rtn;
},
// WRITE_SINGLE_COIL
5: function(bufferlist) {
var rtn = [];
var binary = Binary(bufferlist)
.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 rtn;
},
}
};
10 changes: 9 additions & 1 deletion modbus-stack.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,14 @@ ModbusResponseStack.prototype._onData = function(chunk) {
} else if (this.requestHeader && this.functionCode >= 1 && this.bufferlist.length >= (this.requestHeader.length-2)) {
// We have the complete request.
if (!server.REQUESTS[this.functionCode]) {
return this.emit('error', new Error('"REQUESTS['+this.functionCode+']" in "server.js" is not implemented!'));
//this._gotRequest = true;
//this.writeException(1);
//return this.emit('error', new Error('"REQUESTS['+this.functionCode+']" in "server.js" is not implemented!'));

console.log(JSON.stringify(this.requestHeader));
this.request = this.requestHeader;
this._gotRequest = true;
return this.emit('not_implemented', this.request);
}
try {
this.request = server.REQUESTS[this.functionCode].call(this, this.bufferlist);
Expand All @@ -206,6 +213,7 @@ ModbusResponseStack.prototype._onData = function(chunk) {
//console.log('bufferlist.length: ' + this.bufferlist.length);
this._gotRequest = true;
this.emit('request', this.request);
//console.log("modbus-stack.js" +JSON.stringify(this.request));
}
}

Expand Down
22 changes: 16 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
"name": "modbus-stack",
"description": "A `StreamStack` implementation of the MODBUS protocol.",
"version": "0.2.1",
"author": "Nathan Rajlich <nathan@tootallnate.net>",
"author": {
"name": "Nathan Rajlich",
"email": "nathan@tootallnate.net"
},
"keywords": [
"MODBUS",
"protocol",
Expand All @@ -19,13 +22,20 @@
},
"main": "./modbus-stack",
"scripts": {
"test": "expresso test/test*.js"
"test": "expresso test/test*.js",
"start": "node server.js"
},
"engines": {
"node": ">= 0.3.0"
},
"directories": {},
"files": [
""
]
}
"files": [],
"readme": "node-modbus-stack\n=================\n### A [StreamStack][] implementation of the [MODBUS][Modbus] protocol for [Node][].\n\nThis module exposes two concrete `StreamStack` implementations:\n`ModbusRequestStack` can be used as a MODBUS client (i.e. Master), and can write\nMODBUS compliant requests and listen for the response.\n`ModbusResponseStack` can be used to create a MODBUS server (i.e. Slave), by\nlistening for requests and providing a convenient API to respond with.\n\n[MODBUS][ModbusWiki] is an open building automation protocol that is widely used\nin various monitoring and controlling equipment. It's used with a variety of\ndifferent transports, including TCP.\n\nCurrently only communication through the TCP protocol is supported, however\nRS-485 serial support should be possible with [node-serialport]. I haven't\nhad a chance to look into it yet.\n\n\nA MODBUS Master (Client)\n------------------------\n\nYou will need to know which _Function Code_ (defined in the MODBUS specification)\nyou are invoking on the remote MODBUS slave. In this example, we'll request to\nread from the current values of the first 50 __Input Registers__ on the slave:\n\n // 'RIR' contains the \"Function Code\" that we are going to invoke on the remote device\n var RIR = require('modbus-stack').FUNCTION_CODES.READ_INPUT_REGISTERS;\n \n // IP and port of the MODBUS slave, default port is 502\n var client = require('modbus-stack/client').createClient(502, '10.0.1.50');\n \n // 'req' is an instance of the low-level `ModbusRequestStack` class\n var req = client.request(RIR, // Function Code: 4\n 0, // Start at address 0\n 50); // Read 50 contiguous registers from 0\n \n // 'response' is emitted after the entire contents of the response has been received.\n req.on('response', function(registers) {\n // An Array of length 50 filled with Numbers of the current registers.\n console.log(registers);\n client.end();\n });\n\n\nA MODBUS Slave (Server)\n-----------------------\n\n`node-modbus-stack` makes it dead simple to create a compliant MODBUS Slave (or Server)\nwritten in pure JavaScript. Here's an example of a server that would respond to the\nrequest above:\n\n var FC = require('modbus-stack').FUNCTION_CODES;\n \n // 'handlers' is an Object with keys containing the \"Function Codes\" that your MODBUS\n // server will handle. Anything function code requested without a handler defined here\n // will have the Server transparently respond with Exception Code 1 (\"Illegal Function\")\n var handlers = {};\n \n // Define a handler for \"Read Input Registers\". We'll just respond with the register\n // number requested. In a real-world situation, you'd probably look up these values from\n // a database, etc.\n handlers[FC.READ_INPUT_REGISTERS] = function(request, response) {\n var start = request.startAddress;\n var length = request.quantity;\n \n var resp = new Array(length);\n for (var i=0; i<length; i++) {\n resp[i] = start + i;\n }\n response.writeResponse(resp);\n }\n\n require('modbus-stack/server').createServer(handlers).listen(502);\n\nA \"catch-all\" function can be passed to `createServer()` instead of a \"handlers\" object, if you'd\nrather have a single callback invoked for _all_ MODBUS requests. Just be sure to call\n`writeException()` manually for any \"Function Codes\" your server isn't going to handle.\n\n\n[StreamStack]: http://github.com/TooTallNate/node-stream-stack\n[node-serialport]: https://github.com/voodootikigod/node-serialport\n[ModbusWiki]: http://en.wikipedia.org/wiki/Modbus\n[Modbus]: http://www.modbus.org\n[Node]: http://nodejs.org\n",
"readmeFilename": "README.md",
"_id": "modbus-stack@0.2.1",
"dist": {
"shasum": "83728431e6534f92d10c091d640f5a1419ab4c3c"
},
"_from": "modbus-stack@0.2.1",
"_resolved": "https://registry.npmjs.org/modbus-stack/-/modbus-stack-0.2.1.tgz"
}
50 changes: 40 additions & 10 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,21 @@ Server.prototype._setupConn = function(stream) {
self._setupConn(stream);
}
});

response.on('not_implemented', function (request) {
//console.log("on not_implemented");
//console.log(JSON.stringify(request));
self.emit('request', request, response);
if (stream.readable && stream.writable) {
self._setupConn(stream);
}
});

}

// Called for every 'request' event, when a "handlers" Object was passed.
Server.prototype._handler = function(request, response) {
//console.log("server.js: " + JSON.stringify(request));
if (request.functionCode in this.handlers) {
this.handlers[request.functionCode].call(this, request, response);
} else {
Expand Down Expand Up @@ -88,20 +99,39 @@ Server.REQUESTS = {
// GET_COMM_EVENT_LOG (Serial Line Only)
12: no_parameters,
// REPORT_SLAVE_ID (Serial Line Only)
17: no_parameters
17: no_parameters,

//unknown function code - error case
//666: no_parameters
};

Server.RESPONSES = {
// READ_INPUT_REGISTERS
4: function(registers) {
if (!Array.isArray(registers) || registers.length != this.request.quantity) {
throw new Error('Expected to write an "Array" of length "'+this.request.quantity+'"');
}
var i=0, l=registers.length, put = Put()
.word8(registers.length*2);
for (; i<l; i++) {
put.word16be(registers[i]);
}
return put.buffer();
if(Buffer.isBuffer(registers) == false)
throw new Error('Expected to write an "Buffer"');
var put = Put();
put.word8(registers.length);
put.put(registers)
return(put.buffer());
},
// READ_HOLDING_REGISTERS
3: function(registers) {
if(Buffer.isBuffer(registers) == false)
throw new Error('Expected to write an "Buffer"');
var put = Put();
put.word8(registers.length);
put.put(registers)
return(put.buffer());
},
//WRITE_SINGLE_REGISTER
6: function(address,value) {
//if(Buffer.isBuffer(registers) == false)
// throw new Error('Expected to write an "Buffer"');
var put = Put();
put.word16be(address);
put.word16be(value);
return(put.buffer());
}

};