import modbusClient from "#root/config/modbus-client.config.js"; import db from "#root/config/database.config.js"; import * as utils from '#root/utils/index.js' export const modbusService = { /** * @param {import("..").ModbusDevice} device * @return {Promise} */ async readDataFromDevice(device) { const { g_display_reg_format, g_display_reg_addr, g_display_reg_type, display_values } = device; /** @type {import("..").EventDataStream} */ const data = { graph: null, displayValues: [] }; modbusClient.setID(device.id); /* Read data for graph */ if (g_display_reg_addr !== null) { const graphValueBuf = await this.readModbusRegisters(g_display_reg_addr, g_display_reg_type, g_display_reg_format); data.graph = { value: utils.readNumberFromBuf(graphValueBuf, device.g_display_reg_format, 'BE'), format: g_display_reg_format } } /* Read data for display values */ if (display_values) { for (const v of device.display_values) { const displayValueBuf = await this.readModbusRegisters(v.reg_addr, v.reg_type, v.reg_format) v.data = utils.readNumberFromBuf(displayValueBuf, v.reg_format, 'BE') data.displayValues.push(v); } } return data; }, /** * @param {import("..").AppConfig} appConfig * @return {Promise} */ async connect(appConfig) { const { mb_connection_type, mb_tcp_ip, mb_tcp_port, mb_rtu_path, mb_rtu_baud, mb_rtu_data_bits, mb_rtu_parity, mb_rtu_stop_bits } = appConfig if (modbusClient.isOpen) { modbusClient.close(undefined); modbusClient.destroy(undefined); } switch (mb_connection_type) { case 'TCP': { await modbusClient.connectTCP(mb_tcp_ip, { port: mb_tcp_port }); break; } case "RTU": { await modbusClient.connectRTU(mb_rtu_path, { baudRate: mb_rtu_baud, dataBits: mb_rtu_data_bits, parity: mb_rtu_parity, stopBits: mb_rtu_stop_bits, }); break; } default: throw new Error('Unsupported connection type!') } }, /** * @param {number} addr * @param {"HR" | "IR" | "DI"} type * @param {import("..").NumberType} format * @return {Promise} buffer data */ async readModbusRegisters(addr, type, format) { let data; let len = 1; switch (format) { case 'UI32': case 'I32': case 'FP32': len = 2; break; default: break; } switch (type) { case 'HR': data = await modbusClient.readHoldingRegisters(addr, len); break; case 'IR': data = await modbusClient.readInputRegisters(addr, len); break; case 'DI': data = await modbusClient.readDiscreteInputs(addr, len); break; default: throw new Error('Incorrect modbus register type!'); } return data.buffer; }, async getDevices() { /** @type {import("..").ModbusDevice[]} */ const devices = await db.all(` SELECT ms.id AS id, ms.name AS name, g_display_reg_addr, g_display_reg_format, g_display_reg_type, g_y_label, is_logging, CASE WHEN COUNT(dv.id) = 0 THEN '[]' ELSE '[' || GROUP_CONCAT( JSON_OBJECT( 'id', dv.id, 'name', dv.name, 'reg_addr', dv.reg_addr, 'reg_format', dv.reg_format, 'reg_type', dv.reg_type ), ', ' ) || ']' END AS display_values FROM modbus_slaves AS ms LEFT JOIN display_values AS dv ON dv.slave_id = ms.id GROUP BY ms.id, ms.name; `); // @ts-ignore devices.forEach((s) => (s.display_values = JSON.parse(s.display_values))); return devices }, /** * @param {import('express').Request} req * @param {import('express').Response} res * @param {import("..").ModbusDevice} device */ // createPollInterval(req, res, device, pollIntervalMs) { // let intervalId; // res.setHeader("Cache-Control", "no-cache"); // res.setHeader("Content-Type", "text/event-stream"); // res.setHeader("Connection", "keep-alive"); // return { // /** // * @param {number} pollIntervalMs // */ // poll(pollIntervalMs) { // intervalId = setInterval(async () => { // device.display_values = await db.all(`SELECT * FROM "display_values" WHERE slave_id = ?`, [device.id]) // const data = await modbusService.readDataFromDevice(device); // res.write("event: message\n"); // res.wriste(`data: ${JSON.stringify(data)}\n\n`); // }, pollIntervalMs) // req.on("close", () => clearInterval(intervalId)); // } // } // } }