From f2b35bbadf87bed908548964855b507861a8e535 Mon Sep 17 00:00:00 2001 From: dadna <101567215+dadna@users.noreply.github.com> Date: Thu, 25 Aug 2022 15:57:04 +0200 Subject: [PATCH] unified modbus readings into a single mqtt payload (#30) --- lib/modbus.js | 73 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 29 deletions(-) diff --git a/lib/modbus.js b/lib/modbus.js index 58023f3..0adb354 100644 --- a/lib/modbus.js +++ b/lib/modbus.js @@ -7,6 +7,8 @@ const { default: ModbusRTU } = require('modbus-serial'); const delay = ms => new Promise(r => setTimeout(r, ms)); class Modbus extends events.EventEmitter { + supportedFCs = [3, 4]; + constructor(mqtt, modbus, device) { super(); this.mqtt = mqtt; @@ -41,38 +43,52 @@ class Modbus extends events.EventEmitter { /** * the modbus function code. now supports only 3 and 4. default is 4 - * @param {number} functionCode default is 4 + * @param {number[]} functionCodes default is [3, 4] */ - async pollRegistersByFC(functionCode = 4) { - if (!functionCode) throw Error('function code must be provided for polling'); - if (functionCode !== 3 && functionCode !== 4) throw Error('only FC3 and FC4 are supported for polling'); - const descriptorRegistersProperty = functionCode === 4 ? 'input' : 'keep'; + async pollRegisters(functionCodes = this.supportedFCs) { + if (!functionCodes) throw Error('function code must be provided for polling'); await this.modbus.setID(this.modbusId); const result = {}; - const input = _.get(this.descriptor, `fromModbus.${descriptorRegistersProperty}`); - const keys = Object.keys(input); - // eslint-disable-next-line - for (let key of keys) { - const addressDescriptor = _.get(input, key); - const address = _.get(addressDescriptor, 'address'); - let value; - try { - const promise = functionCode === 4 - ? this.modbus.readInputRegisters.bind(this.modbus) - : this.modbus.readHoldingRegisters.bind(this.modbus); - // eslint-disable-next-line no-await-in-loop - value = await promise(address, 1); - // eslint-disable-next-line no-await-in-loop - await delay(300); - } catch (err) { - logger.error(err); + for (const fc of functionCodes) { + const descriptorRegistersProperty = fc === 4 ? 'input' : 'keep'; + const input = _.get(this.descriptor, `fromModbus.${descriptorRegistersProperty}`); + const keys = Object.keys(input); + console.log(`reading with FC${fc}`); + if (!this.supportedFCs.includes(fc)) { + console.error(`FC${fc} not supported. skipping to next FC...`); + continue; + } + let promise; + if (fc === 3) { + promise = this.modbus.readHoldingRegisters.bind(this.modbus) + } else if (fc === 4) { + promise = this.modbus.readInputRegisters.bind(this.modbus) + } else { + console.error(`FC${fc} not supported. skipping to next FC...`); + continue; + } + // eslint-disable-next-line + for (let key of keys) { + const addressDescriptor = _.get(input, key); + const address = _.get(addressDescriptor, 'address'); + let value; + const registersCountToRead = _.get(addressDescriptor, 'count') || 1; + try { + // eslint-disable-next-line no-await-in-loop + value = await promise(address, registersCountToRead); + // eslint-disable-next-line no-await-in-loop + await delay(300); + const { post } = addressDescriptor; + const interpreted = registersCountToRead === 1 ? _.get(value, 'data[0]') : value.data; + const raw = _.get(value, 'buffer'); + value = (post) ? post(interpreted, raw) : interpreted; + _.set(result, key, value); + } catch (err) { + logger.error(err); + } } - const { post } = addressDescriptor; - const interpreted = _.get(value, 'data[0]'); - const raw = _.get(value, 'buffer'); - value = (post) ? post(interpreted, raw) : interpreted; - _.set(result, key, value); } + const topic = `${this.id}`; const payload = JSON.stringify(result); this.mqtt.publish(topic, payload); @@ -82,8 +98,7 @@ class Modbus extends events.EventEmitter { try { await this.modbus.setID(this.modbusId); // error handling is done within the invoked function so we can call both polls sequentially - await this.pollRegistersByFC(3); - await this.pollRegistersByFC(4); + await this.pollRegisters(); } catch (err) { logger.error(err); }