You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

440 lines
13 KiB

/* jshint -W097 */
/* jshint strict: false */
/* jslint node: true */
'use strict';
const utils = require(__dirname + '/lib/utils');
let yuanqu = null;
let fs;
let adapter = utils.Adapter({
name: 'yuanqu',
unload: stop
});
process.on('SIGINT', stop);
adapter.on('ready', function () {
adapter.setState('info.connection', adapter.config.params.slave ? 0 : false, true);
main();
});
adapter.on('message', function (obj) {
if (obj) {
switch (obj.command) {
case 'listUart':
adapter.log.warn('listUart');
adapter.sendTo(obj.from, obj.command, [{comName: 'Not available'}], obj.callback);
break;
}
}
});
function stop(callback) {
if (yuanqu) {
yuanqu.close();
yuanqu = null;
}
if (adapter && adapter.setState && adapter.config && adapter.config.params) {
adapter.setState('info.connection', adapter.config.params.slave ? 0 : false, true);
}
if (typeof callback === 'function') callback();
setTimeout(function() {
process.exit();
}, 5000);
}
let objects = {};
let enums = {};
let infoRegExp = new RegExp(adapter.namespace.replace('.', '\\.') + '\\.info\\.');
adapter.on('stateChange', (id, state) => {
if (state && !state.ack && id && !infoRegExp.test(id)) {
if (!yuanqu) {
adapter.log.warn('No connection')
} else {
if (objects[id]) {
yuanqu.write(id, state);
} else {
adapter.getObject(id, (err, data) => {
if (!err) {
objects[id] = data;
yuanqu.write(id, state);
}
});
}
}
}
});
function addToEnum(enumName, id, callback) {
adapter.getForeignObject(enumName, function (err, obj) {
if (!err && obj) {
let pos = obj.common.members.indexOf(id);
if (pos === -1) {
obj.common.members.push(id);
adapter.setForeignObject(obj._id, obj, function (err) {
if (callback) callback(err);
});
} else {
if (callback) callback(err);
}
} else {
if (callback) callback(err);
}
});
}
function removeFromEnum(enumName, id, callback) {
adapter.getForeignObject(enumName, function (err, obj) {
if (!err && obj) {
let pos = obj.common.members.indexOf(id);
if (pos !== -1) {
obj.common.members.splice(pos, 1);
adapter.setForeignObject(obj._id, obj, function (err) {
if (callback) callback(err);
});
} else {
if (callback) callback(err);
}
} else {
if (callback) callback(err);
}
});
}
function syncEnums(enumGroup, id, newEnumName, callback) {
if (!enums[enumGroup]) {
adapter.getEnum(enumGroup, function (err, _enums) {
enums[enumGroup] = _enums;
syncEnums(enumGroup, id, newEnumName, callback);
});
return;
}
// try to find this id in enums
let found = false;
let count = 0;
for (let e in enums[enumGroup]) {
if (enums[enumGroup].hasOwnProperty(e) &&
enums[enumGroup][e].common &&
enums[enumGroup][e].common.members &&
enums[enumGroup][e].common.members.indexOf(id) !== -1) {
if (enums[enumGroup][e]._id !== newEnumName) {
count++;
removeFromEnum(enums[enumGroup][e]._id, id, function () {
if (!--count && typeof callback === 'function') callback();
});
} else {
found = true;
}
}
}
if (!found && newEnumName) {
count++;
addToEnum(newEnumName, id, function () {
if (!--count&& typeof callback === 'function') callback();
});
}
if (!count && typeof callback === 'function') callback();
}
const type_items_len = {
'uint8be': 1,
'string': 0,
'ieee754': 2
};
const _rmap = {
0: 15,
1: 14
};
const _dmap = {
0: 0,
1: 1
};
function address2alias(id, address, isDirect, offset) {
if (typeof address === 'string') address = parseInt(address, 10);
if (id === 'disInputs' || id === 'coils') {
address = Math.floor(address / 16) * 16 + (isDirect ? _dmap[address % 16] : _rmap[address % 16]);
address += offset;
return address;
} else {
return address + offset;
}
}
function createExtendObject(id, objData, callback) {
adapter.getObject(id, function (err, oldObj) {
if (!err && oldObj) {
adapter.extendObject(id, objData, callback);
} else {
adapter.setObjectNotExists(id, objData, callback);
}
});
}
function processTasks(tasks, callback) {
if (!tasks || !tasks.length) {
if (typeof callback === 'function') callback();
return;
}
let task = tasks.shift();
if (task.name === 'add') {
createExtendObject(task.id, task.obj, function () {
setTimeout(processTasks, 0, tasks, callback);
});
} else if (task.name === 'del') {
adapter.delObject(task.id, function () {
setTimeout(processTasks, 0, tasks, callback);
});
} else if (task.name === 'syncEnums') {
syncEnums('rooms', task.id, task.obj, function () {
setTimeout(processTasks, 0, tasks, callback);
});
} else {
throw 'Unknown task';
}
}
function prepareConfig(config) {
let params = config.params;
params.slave = parseInt(params.slave, 10) || 0; // required in stop
let options = {
config: {
type: params.type || 'tcp',
slave: params.slave,
round: parseInt(params.round, 10) || 0,
timeout: parseInt(params.timeout, 10) || 5000,
defaultDeviceId: (params.deviceId === undefined || params.deviceId === null) ? 1 : (parseInt(params.deviceId, 10) || 0),
},
devices: {}
};
options.config.round = Math.pow(10, options.config.round);
if (!options.config.slave) {
options.config.multiDeviceId = params.multiDeviceId === true || params.multiDeviceId === 'true';
}
let deviceIds = [];
// settings for master
if (!options.config.slave) {
options.config.poll = parseInt(params.poll, 10) || 1000; // default is 1 second
options.config.recon = parseInt(params.recon, 10) || 60000;
options.config.maxBlock = parseInt(params.maxBlock, 10) || 100;
options.config.maxBoolBlock = parseInt(params.maxBoolBlock, 10) || 128;
options.config.pulsetime = parseInt(params.pulsetime || 1000);
}
if (params.type === 'tcp' || params.type === 'tcprtu') {
options.config.tcp = {
port: parseInt(params.port, 10) || 502,
bind: params.bind
};
}
options.objects = objects;
adapter.log.warn('objects=' + objects[0]);
return options;
}
function checkDeviceIds(options, config, deviceIds) {
for (let i = config.length - 1; i >= 0; i--) {
config[i].deviceId = !options.config.multiDeviceId ? options.config.defaultDeviceId : (config[i].deviceId !== undefined ? parseInt(config[i].deviceId, 10) : options.config.defaultDeviceId);
if (isNaN(config[i].deviceId)) config[i].deviceId = options.config.defaultDeviceId;
if (deviceIds.indexOf(config[i].deviceId) === -1) {
deviceIds.push(config[i].deviceId);
}
}
}
function checkObjects(options, regType, regName, regFullName, tasks, newObjects) {
let regs = options[regType];
adapter.log.warn('regs=' + JSON.stringify(regs));
for (let i = 0; regs.length > i; i++) {
const id = adapter.namespace + '.' + regType + '.' + regs[i]._address + '.' + regs[i].name;
regs[i].id = id;
adapter.log.warn('id=' + id);
regs[i].fullId = id;
objects[id] = {
_id: regs[i].id,
type: 'state',
common: {
name: regs[i].description,
role: regs[i].role,
type: regs[i].type,
read: true,
write: true,
def: regs[i].type === 'boolean' ? false : regs[i].value
},
native: {
regType: regType,
address: regs[i].address,
deviceId: regs[i].deviceId
}
};
if (regType === 'coils') {
objects[id].native.poll = regs[i].poll;
objects[id].native.wp = regs[i].wp;
}
tasks.push({
id: regs[i].id,
name: 'add',
obj: objects[id]
});
tasks.push({
id: id,
name: 'syncEnums',
obj: regs[i].room
});
newObjects.push(id);
adapter.setState(id, regs[i].type === 'boolean' ? false : regs[i].value);
}
if (regs.length) {
tasks.push({
id: regName,
name: 'add',
obj: {
type: 'channel',
common: {
name: regFullName
},
native: {}
}
});
}
}
function parseConfig(callback) {
adapter.log.warn('adapter.config=' + adapter.config);
let options = prepareConfig(adapter.config);
const params = adapter.config.params;
//adapter.log.warn('params=' + params);
adapter.getForeignObjects(adapter.namespace + '.*', (err, list) => {
let oldObjects = list;
let newObjects = [];
adapter.config.yz.sort(sortByAddress);
let tasks = [];
checkObjects(adapter.config, 'config', 'config', 'Config Info', tasks, newObjects);
checkObjects(adapter.config, 'yz', 'yz', '闸机', tasks, newObjects);
checkObjects(adapter.config, 'dz', 'dz', '车道', tasks, newObjects);
checkObjects(adapter.config, 'video', 'video', '视频', tasks, newObjects);
checkObjects(adapter.config, 'power', 'power', '电源控制', tasks, newObjects);
checkObjects(adapter.config, 'ludeng', 'ludeng', '路灯', tasks, newObjects);
checkObjects(adapter.config, 'dianti', 'dianti', '电梯', tasks, newObjects);
tasks.push({
id: 'info',
name: 'add',
obj: {
type: 'channel',
common: {
name: 'info'
},
native: {}
}
});
// create/ update 'info.connection' object
adapter.getObject('info.connection', function (err, obj) {
if (!obj) {
obj = {
type: 'state',
common: {
name: 'Number of connected partners',
role: 'indicator.connected',
write: false,
read: true,
type: options.config.slave ? 'number' : 'boolean'
},
native: {}
};
adapter.setObjectNotExists('info.connection', obj);
} else if (options.config.slave && obj.common.type !== 'number') {
obj.common.type = 'number';
obj.common.name = 'Number of connected masters';
adapter.setObjectNotExists('info.connection', obj);
} else if (!options.config.slave && obj.common.type !== 'boolean') {
obj.common.type = 'boolean';
obj.common.name = 'If master connected';
adapter.setObjectNotExists('info.connection', obj);
}
});
newObjects.push(adapter.namespace + '.info.connection');
// clear unused states
for (let id_ in oldObjects) {
if (oldObjects.hasOwnProperty(id_) && newObjects.indexOf(id_) === -1) {
tasks.push({
id: id_,
name: 'del'
});
}
}
processTasks(tasks, function () {
oldObjects = [];
newObjects = [];
adapter.subscribeStates('*');
callback(options);
});
});
}
function main() {
parseConfig(options => {
});
}
function sortByAddress(a, b) {
let ad = parseFloat(a.address);
let bd = parseFloat(b.address);
return ((ad < bd) ? -1 : ((ad > bd) ? 1 : 0));
}