/* 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', 'menjin', tasks, newObjects); checkObjects(adapter.config, 'dz', 'dz', 'chedao', tasks, newObjects); checkObjects(adapter.config, 'video', 'video', 'video', tasks, newObjects); checkObjects(adapter.config, 'power', 'power', 'room power', 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)); }