Merge branch 'master' of http://git.spacen.net/yunkong2/yunkong2.rsonoff
commit
8f6c6a0555
File diff suppressed because it is too large
Load Diff
@ -1,406 +0,0 @@
|
||||
'use strict';
|
||||
const common = require(__dirname + '/common.js');
|
||||
const Modbus = require('./jsmodbus');
|
||||
|
||||
// expected
|
||||
// let options = {
|
||||
// config: {
|
||||
// round: 1,
|
||||
// tcp: {
|
||||
// port: 502
|
||||
// }
|
||||
// },
|
||||
// objects: {
|
||||
// }
|
||||
// coils: {
|
||||
// config: ...
|
||||
// changed: true,
|
||||
// addressHigh: 0,
|
||||
// addressLow: 0,
|
||||
// values: [],
|
||||
// mapping: {}
|
||||
// },
|
||||
// inputRegs: ...,
|
||||
// disInputs: ...,
|
||||
// holdingRegs: ...
|
||||
// };
|
||||
|
||||
function Slave(options, adapter) {
|
||||
let objects = options.objects;
|
||||
let device = options.devices[Object.keys(options.devices)[0]];
|
||||
let delayStart = true;
|
||||
let modbusServer;
|
||||
|
||||
function getListOfClients(clients) {
|
||||
let list = [];
|
||||
for(let c in clients) {
|
||||
if (clients.hasOwnProperty(c)) {
|
||||
let address = clients[c].address().address;
|
||||
if (address) list.push(address);
|
||||
}
|
||||
}
|
||||
return list.join(',');
|
||||
}
|
||||
|
||||
this.write = (id, state) => {
|
||||
if (!objects[id] || !objects[id].native) {
|
||||
adapter.log.error('Can not set state ' + id + ': unknown object');
|
||||
return;
|
||||
}
|
||||
|
||||
if (objects[id].native.float === undefined) {
|
||||
objects[id].native.float =
|
||||
objects[id].native.type === 'floatle' || objects[id].native.type === 'floatbe' || objects[id].native.type === 'floatsw' ||
|
||||
objects[id].native.type === 'doublele' || objects[id].native.type === 'doublebe' || objects[id].native.type === 'floatsb';
|
||||
}
|
||||
let val;
|
||||
let buffer;
|
||||
let b;
|
||||
|
||||
let t = typeof state.val;
|
||||
let type = objects[id].native.regType;
|
||||
let regs = device[type];
|
||||
if (!regs) {
|
||||
adapter.log.error('Invalid type ' + type);
|
||||
return;
|
||||
}
|
||||
regs.changed = true;
|
||||
|
||||
if (type === 'disInputs' || type === 'coils') {
|
||||
if (t === 'boolean' || t === 'number') {
|
||||
regs.values[objects[id].native.address - regs.addressLow] = state.val ? 1 : 0;
|
||||
} else {
|
||||
regs.values[objects[id].native.address - regs.addressLow] = parseInt(state.val, 10) ? 1 : 0;
|
||||
}
|
||||
} else if (type === 'inputRegs' || type === 'holdingRegs') {
|
||||
if (objects[id].native.type !== 'string') {
|
||||
if (t === 'boolean') {
|
||||
val = state.val ? 1 : 0;
|
||||
} else if (t === 'number') {
|
||||
val = state.val;
|
||||
} else {
|
||||
val = parseFloat(state.val);
|
||||
}
|
||||
val = (val - objects[id].native.offset) / objects[id].native.factor;
|
||||
if (!objects[id].native.float) val = Math.round(val);
|
||||
} else {
|
||||
val = state.val;
|
||||
}
|
||||
buffer = common.writeValue(objects[id].native.type, val, objects[id].native.len);
|
||||
for (b = 0; b < buffer.length; b++) {
|
||||
regs.values[(objects[id].native.address - regs.addressLow) * 2 + b] = buffer[b];
|
||||
}
|
||||
} else {
|
||||
adapter.log.error('Unknown state "' + id + '" type: ' + objects[id].native.regType);
|
||||
}
|
||||
};
|
||||
|
||||
this.start = () => {
|
||||
if (!delayStart && !modbusServer) {
|
||||
modbusServer = Modbus('server', 'tcp')({
|
||||
options: {
|
||||
log: adapter.log,
|
||||
tcp: {
|
||||
port: parseInt(options.config.tcp.port, 10) || 502,
|
||||
hostname: options.config.tcp.bind || '127.0.0.1',
|
||||
}
|
||||
},
|
||||
responseDelay: 100,
|
||||
coils: new Buffer((device.coils.addressHigh >> 3) + ((device.coils.addressHigh - 1) % 8 ? 1 : 0)),
|
||||
discrete: new Buffer((device.disInputs.addressHigh >> 3) + ((device.disInputs.addressHigh - 1) % 8 ? 1 : 0)),
|
||||
input: new Buffer(device.inputRegs.addressHigh * 2),
|
||||
holding: new Buffer(device.holdingRegs.addressHigh * 2)
|
||||
});
|
||||
|
||||
modbusServer.on('readCoilsRequest', function (start, quantity) {
|
||||
let regs = device.coils;
|
||||
if (regs.changed || (regs.lastStart > start || regs.lastEnd < start + quantity)) {
|
||||
regs.lastStart = start;
|
||||
regs.lastEnd = start + quantity;
|
||||
regs.changed = false;
|
||||
let resp = new Array(Math.ceil(quantity / 16) * 2);
|
||||
let i = 0;
|
||||
let data = this.getCoils();
|
||||
let j;
|
||||
for (j = 0; j < resp.length && start + j < data.byteLength; j++) {
|
||||
resp[j] = data.readUInt8(start + j);
|
||||
}
|
||||
for (; j < resp.length; j++) {
|
||||
resp[j] = 0;
|
||||
}
|
||||
|
||||
while (i < quantity && i + start < regs.addressHigh) {
|
||||
if (regs.values[i + start - regs.addressLow]) {
|
||||
resp[Math.floor(i / 8)] |= 1 << (i % 8);
|
||||
} else {
|
||||
resp[Math.floor(i / 8)] &= ~(1 << (i % 8));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
let len = data.length;
|
||||
for (i = 0; i < resp.length; i++) {
|
||||
if (start + i >= len) break;
|
||||
data.writeUInt8(resp[i], start + i);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
modbusServer.on('readDiscreteInputsRequest', function (start, quantity) {
|
||||
let regs = device.disInputs;
|
||||
if (regs.changed || (regs.lastStart > start || regs.lastEnd < start + quantity)) {
|
||||
regs.lastStart = start;
|
||||
regs.lastEnd = start + quantity;
|
||||
regs.changed = false;
|
||||
let resp = new Array(Math.ceil(quantity / 16) * 2);
|
||||
let i = 0;
|
||||
let data = this.getDiscrete();
|
||||
let j;
|
||||
for (j = 0; j < resp.length && start + j < data.byteLength; j++) {
|
||||
resp[j] = data.readUInt8(start + j);
|
||||
}
|
||||
for (; j < resp.length; j++) {
|
||||
resp[j] = 0;
|
||||
}
|
||||
while (i < quantity && i + start < regs.addressHigh) {
|
||||
if (regs.values[i + start - regs.addressLow]) {
|
||||
resp[Math.floor(i / 8)] |= 1 << (i % 8);
|
||||
} else {
|
||||
resp[Math.floor(i / 8)] &= ~(1 << (i % 8));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
let len = data.length;
|
||||
for (i = 0; i < resp.length; i++) {
|
||||
if (start + i >= len) break;
|
||||
data.writeUInt8(resp[i], start + i);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
modbusServer.on('readInputRegistersRequest', function (start, quantity) {
|
||||
let regs = device.inputRegs;
|
||||
if (regs.changed || (regs.lastStart > start || regs.lastEnd < start + quantity)) {
|
||||
regs.lastStart = start;
|
||||
regs.lastEnd = start + quantity;
|
||||
regs.changed = false;
|
||||
let data = this.getInput();
|
||||
const end = start + quantity * 2;
|
||||
const low = regs.addressLow * 2;
|
||||
const high = regs.addressHigh * 2;
|
||||
for (let i = start; i < end; i++) {
|
||||
if (i >= data.length) break;
|
||||
if (i >= low && i < high) {
|
||||
data.writeUInt8(regs.values[i - low], i);
|
||||
} else {
|
||||
data.writeUInt8(0, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
modbusServer.on('readHoldingRegistersRequest', function (start, quantity) {
|
||||
let regs = device.holdingRegs;
|
||||
if (regs.changed || (regs.lastStart > start || regs.lastEnd < start + quantity)) {
|
||||
regs.lastStart = start;
|
||||
regs.lastEnd = start + quantity;
|
||||
regs.changed = false;
|
||||
let data = this.getHolding();
|
||||
const end = start + quantity * 2;
|
||||
const low = regs.addressLow * 2;
|
||||
const high = regs.addressHigh * 2;
|
||||
for (let i = start; i < end; i++) {
|
||||
if (i >= data.length) break;
|
||||
if (i >= low && i < high) {
|
||||
data.writeUInt8(regs.values[i - low], i);
|
||||
} else {
|
||||
data.writeUInt8(0, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
modbusServer.on('postWriteSingleCoilRequest', function (start, value) {
|
||||
let regs = device.coils;
|
||||
let a = start - regs.addressLow;
|
||||
|
||||
if (a >= 0 && regs.mapping[a]) {
|
||||
adapter.setState(regs.mapping[a], value, true, function (err) {
|
||||
// analyse if the state could be set (because of permissions)
|
||||
if (err) adapter.log.error('Can not set state: ' + err);
|
||||
});
|
||||
regs.values[a] = value;
|
||||
}
|
||||
});
|
||||
|
||||
const mPow2 = [
|
||||
0x01,
|
||||
0x02,
|
||||
0x04,
|
||||
0x08,
|
||||
0x10,
|
||||
0x20,
|
||||
0x40,
|
||||
0x80
|
||||
];
|
||||
|
||||
modbusServer.on('postWriteMultipleCoilsRequest', function (start, length /* , byteLength*/) {
|
||||
let regs = device.coils;
|
||||
let i = 0;
|
||||
let data = this.getCoils();
|
||||
if (start < regs.addressLow) {
|
||||
start = regs.addressLow;
|
||||
}
|
||||
|
||||
while (i < length && i + start < regs.addressHigh) {
|
||||
let a = i + start - regs.addressLow;
|
||||
if (a >= 0 && regs.mapping[a]) {
|
||||
let value = data.readUInt8((i + start) >> 3);
|
||||
value = value & mPow2[(i + start) % 8];
|
||||
adapter.setState(regs.mapping[a], !!value, true, function (err) {
|
||||
// analyse if the state could be set (because of permissions)
|
||||
if (err) adapter.log.error('Can not set state: ' + err);
|
||||
});
|
||||
regs.values[a] = !!value;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
});
|
||||
|
||||
modbusServer.on('postWriteSingleRegisterRequest', function (start, value) {
|
||||
let regs = device.holdingRegs;
|
||||
start = start >> 1;
|
||||
let a = start - regs.addressLow;
|
||||
|
||||
if (a >= 0 && regs.mapping[a]) {
|
||||
let native = options.objects[regs.mapping[a]].native;
|
||||
let buf = new Buffer(2);
|
||||
buf.writeUInt16BE(value);
|
||||
let val = common.extractValue(native.type, native.len, buf, 0);
|
||||
|
||||
if (native.type !== 'string') {
|
||||
val = (val - native.offset) / native.factor;
|
||||
val = Math.round(val * options.config.round) / options.config.round;
|
||||
}
|
||||
|
||||
adapter.setState(regs.mapping[a], val, true, function (err) {
|
||||
// analyse if the state could be set (because of permissions)
|
||||
if (err) adapter.log.error('Can not set state: ' + err);
|
||||
});
|
||||
|
||||
regs.values[a] = buf[0];
|
||||
regs.values[a + 1] = buf[1];
|
||||
}
|
||||
});
|
||||
|
||||
modbusServer.on('postWriteMultipleRegistersRequest', function (start, length /* , byteLength*/) {
|
||||
let regs = device.holdingRegs;
|
||||
let data = this.getHolding();
|
||||
let i = 0;
|
||||
start = start >> 1;
|
||||
|
||||
if (start < regs.addressLow) {
|
||||
start = regs.addressLow;
|
||||
}
|
||||
|
||||
while (i < length && i + start < regs.addressHigh) {
|
||||
let a = i + start - regs.addressLow;
|
||||
if (a >= 0 && regs.mapping[a]) {
|
||||
let native = options.objects[regs.mapping[a]].native;
|
||||
|
||||
let val = common.extractValue(native.type, native.len, data, i + start);
|
||||
if (native.type !== 'string') {
|
||||
val = val * native.factor + native.offset;
|
||||
val = Math.round(val * options.config.round) / options.config.round;
|
||||
}
|
||||
adapter.setState(regs.mapping[a], val, true, function (err) {
|
||||
// analyse if the state could be set (because of permissions)
|
||||
if (err) adapter.log.error('Can not set state: ' + err);
|
||||
});
|
||||
for (let k = 0; k < native.len * 2; k++) {
|
||||
regs.values[a * 2 + k] = data.readUInt8(start * 2 + k);
|
||||
}
|
||||
i += native.len;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
modbusServer.on('connection', client => {
|
||||
let list = getListOfClients(modbusServer.getClients());
|
||||
adapter.log.debug('+ Clients connected: ' + list);
|
||||
adapter.setState('info.connection', list, true);
|
||||
}).on('close', client => {
|
||||
let list = getListOfClients(modbusServer.getClients());
|
||||
adapter.log.debug('- Client connected: ' + list);
|
||||
adapter.setState('info.connection', list, true);
|
||||
}).on('error', err => {
|
||||
let list = getListOfClients(modbusServer.getClients());
|
||||
adapter.log.info('- Clients connected: ' + list);
|
||||
adapter.setState('info.connection', list, true);
|
||||
adapter.log.warn('Error on connection: ' + JSON.stringify(err));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.close = () => {
|
||||
if (modbusServer) {
|
||||
try {
|
||||
modbusServer.close();
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
modbusServer = null;
|
||||
}
|
||||
};
|
||||
|
||||
this._initValues = (states, regs) => {
|
||||
// build ready arrays
|
||||
for (let i = 0; regs.fullIds.length > i; i++) {
|
||||
let id = regs.fullIds[i];
|
||||
if (states[id] && states[id].val !== undefined) {
|
||||
this.write(id, states[id]);
|
||||
} else {
|
||||
adapter.setState(id, 0, true, err => {
|
||||
// analyse if the state could be set (because of permissions)
|
||||
if (err) adapter.log.error('Can not set state ' + id + ': ' + err);
|
||||
});
|
||||
}
|
||||
}
|
||||
// fill with 0 empty values
|
||||
for (let i = 0; i < regs.values.length; i++) {
|
||||
if (regs.values[i] === undefined || regs.values[i] === null) {
|
||||
regs.values[i] = 0;
|
||||
} else if (typeof regs.values[i] === 'boolean') {
|
||||
regs.values[i] = regs.values[i] ? 1 : 0;
|
||||
} else if (typeof regs.values[i] !== 'number') {
|
||||
regs.values[i] = parseInt(regs.values[i], 10) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.initValues = callback => {
|
||||
// read all states
|
||||
adapter.getStates('*', (err, states) => {
|
||||
this._initValues(states, device.disInputs);
|
||||
this._initValues(states, device.coils);
|
||||
this._initValues(states, device.inputRegs);
|
||||
this._initValues(states, device.holdingRegs);
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
(function _constructor() {
|
||||
adapter.setState('info.connection', 0, true);
|
||||
|
||||
this.initValues(() => {
|
||||
delayStart = false;
|
||||
adapter.log.debug('Slave ready to start');
|
||||
this.start();
|
||||
});
|
||||
}.bind(this))();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
module.exports = Slave;
|
Loading…
Reference in new issue