diff --git a/worker/README.md b/worker/README.md new file mode 100644 index 0000000..ae12303 --- /dev/null +++ b/worker/README.md @@ -0,0 +1,72 @@ +#### What can I do with this module ? + +This class makes it easy to grab some data from device. +Also, it handles typecast to int32, uint32, float etc. +Type 'double' is not supported by nodejs... + +#### Examples + +###### Setup client and worker +```` javascript +const client = new ModbusRTU() + +client.connect.......... + +client.setWorkerOptions({ + maxConcurrentRequests: 10, // it will send 10 requests or less at a time if any + debug: true +}) +```` +###### Read some data +``` javascript +// Read 4 values starting from 10009 register. +// Under the hood it will read 8 registers due to int32 type +const response = await client.send({ + unit: 1, + fc: 3, + address: 10009, + quantity: 4, + type: 'int32', +}); +```` +###### Write some data +``` javascript +// Write 2 values to address: 10009 and 10011 +const response = await client.send({ + unit: 1, + fc: 16, + address: 10009, + value: [10999, 10888], + type: 'int32', +}); +```` +###### Poll some data +``` javascript +// It will build all READ requests for you in optimal way +const response = await client.poll({ + unit: 1, + map: [ + { fc: 3, address: [10011, 10013, 10018], type: "int32" }, + { fc: 3, address: 10003, type: "int32" }, + { fc: 3, address: 10005, type: "int32" }, + { fc: 3, address: 10007, type: "int32" }, + { fc: 3, address: 10009, type: "int32" }, + { fc: 2, address: [1,2,3]}, + { fc: 1, address: [1,2,3]}, + { fc: 1, address: 4}, + { fc: 1, address: 5}, + { fc: 1, address: 6}, + { fc: 3, address: [10001]}, // default type is int16 + { fc: 3, address: [10020, 10023, 10026], type: "float"}, + { fc: 3, address: [10030, 10034], type: "double"} + ], + onProgress: (progress, data) => { + console.log( + progress, // Poll progress from 0...1 where 1 means 100% + data, // Data from the current request + ); + }, + maxChunkSize: 32, // max registers per request + skipErrors: false, // if false it will stop poll and return PARTIAL result +}) +``` diff --git a/worker/index.js b/worker/index.js index dfd8401..5b1e965 100644 --- a/worker/index.js +++ b/worker/index.js @@ -7,11 +7,8 @@ function getByteLength(type) { return 2; case "int32": case "uint32": - return 4; case "float": - return 6; - case "double": - return 8; + return 4; default: throw new Error("Unsupported type"); } @@ -96,8 +93,6 @@ Worker.prototype.bufferize = function(data, type) { buffer.writeUInt32BE(data[i], i * byteLength); } else if(type === "float") { buffer.writeFloatBE(data[i], i * byteLength); - } else if(type === "double") { - buffer.writeDoubleBE(data[i], i * byteLength); } } @@ -119,8 +114,6 @@ Worker.prototype.unbufferize = function(buffer, type) { data.push(buffer.readUInt32BE(i * byteLength)); } else if(type === "float") { data.push(buffer.readFloatBE(i * byteLength)); - } else if(type === "double") { - data.push(buffer.readDoubleBE(i * byteLength)); } } @@ -141,14 +134,25 @@ Worker.prototype.send = function({ fc, unit, address, value, quantity, arg, type arg = arg || quantity || value; + if(fc === 1 || fc === 2) { + arg = arg || 1; + } + if(fc === 3 || fc === 4) { type = type || "int16"; - arg = arg * getByteLength(type) / 2; + arg = (arg || 1) * getByteLength(type) / 2; } if(fc === 6 || fc === 16) { type = type || "int16"; arg = this.bufferize(arg, type); + if(fc === 6 && arg.length > 2) { + fc = 16; + } + } + + if(fc === 5 && arg instanceof Array && arg.length > 1) { + fc = 15; } const id = this.nextId(); @@ -189,7 +193,7 @@ Worker.prototype.run = function() { if(typeof request.checkBeforeQueuing === "function") { if(request.checkBeforeQueuing() === false) { - return this.process(); + return this.process(); // Skip current request and go on } } @@ -228,7 +232,9 @@ Worker.prototype.run = function() { .catch((error) => { this._running.delete(request.id); - this.emit("failed", { request, error }); + error.request = request; + + this.emit("failed", error); request.reject(error); @@ -245,7 +251,7 @@ Worker.prototype._poll_send = function(result, { i, fc, unit, address, arg, item this.log("scheduled push", "poll #" + result.id, "req #" + i, "#" + id, fc, length, type); const resolve = function(response) { - const data = items.map((address, index) => [fc, address, response[index]]); + const data = items.map((address, index) => ({ address, value: response[index] })); result._req += 1; result.done += 1; result.data = [...result.data, ...data]; @@ -275,10 +281,15 @@ Worker.prototype.poll = function({ unit, map, onProgress, maxChunkSize, skipErro skipErrors = Boolean(skipErrors); defaultType = defaultType || "int16"; + if(unit < 1 || unit > 250 || isNaN(unit) || unit === undefined) { + throw new Error("invalid unit"); + } + this.log("poll", `unit=${unit}`, "map size=" + Object.keys(map).length, `maxChunkSize=${maxChunkSize}`, `skipErrors=${skipErrors}`); const result = { id: this.nextId(), + unit, total: 0, done: 0, data: [], @@ -360,15 +371,14 @@ Worker.prototype.poll = function({ unit, map, onProgress, maxChunkSize, skipErro result.total = requests.length; - return new Promise(((resolve, reject) => { - + return new Promise(((resolve) => { const check = function() { if(result._req === result.total) { result.dt = Date.now() - result.dt; resolve(result); } else if(result.error && skipErrors !== true) { result.dt = Date.now() - result.dt; - reject(result); + resolve(result); } };