From 7c67b9e34ab7caca606782dcac5bd00d29e59386 Mon Sep 17 00:00:00 2001 From: Nikita Biziaev Date: Sun, 5 Jul 2026 16:39:15 +0500 Subject: [PATCH] data/edge5/modbus/v3 --- app/src/handlers/edge5-modbus-v3.ts | 121 ++++++++++++++++++++++++++++ app/src/handlers/index.ts | 2 + 2 files changed, 123 insertions(+) create mode 100644 app/src/handlers/edge5-modbus-v3.ts diff --git a/app/src/handlers/edge5-modbus-v3.ts b/app/src/handlers/edge5-modbus-v3.ts new file mode 100644 index 0000000..a1eaf09 --- /dev/null +++ b/app/src/handlers/edge5-modbus-v3.ts @@ -0,0 +1,121 @@ +import { postCloudIngest } from '../cloud-ingest.js' +import { log } from '../helpers/log.js' +import type { TopicHandler } from '../types.js' + +const EDGE = 'edge5-v3' +const REGISTER_COUNT = 24 + +// Фиксируем интерпретацию +const TAGS = [ + 'edge5-v3-wk', + 'edge5-v3-pin', + 'edge5-v3-hk', + 'edge5-v3-mk', + 'edge5-v3-mrot', + 'edge5-v3-h', + 'edge5-v3-vw', + 'edge5-v3-pdk', + 'edge5-v3-h2s', + 'edge5-v3-t', + 'edge5-v3-nrot', + 'edge5-v3-vsp', +] as const + +// из них выводим на дашборд семь виджетов +const CLOUD_INGEST_TAGS = new Set([ + 'edge5-v3-wk', + 'edge5-v3-hk', + 'edge5-v3-vw', + 'edge5-v3-pdk', + 'edge5-v3-h2s', + 'edge5-v3-nrot', + 'edge5-v3-vsp', +]) + +type Edge5ModbusV3Metric = { + edge: string + timestamp: string + tag: string + value: number +} + +function toFloatCDAB(reg0: number, reg1: number): number { + const buf = Buffer.allocUnsafe(4) + buf.writeUInt16BE(reg1, 0) + buf.writeUInt16BE(reg0, 2) + return buf.readFloatBE(0) +} + +function parseValues(payload: Buffer): number[] { + const values = JSON.parse(payload.toString('utf8')) as number[] + assertRegisterCount(values) + return values +} + +function assertRegisterCount(values: number[]): void { + if (values.length !== REGISTER_COUNT) { + throw new Error(`Expected ${REGISTER_COUNT} modbus values, got ${values.length}`) + } +} + +function toMetrics(values: number[], timestamp: string): Edge5ModbusV3Metric[] { + const metrics: Edge5ModbusV3Metric[] = [] + + for (let i = 0; i < values.length; i += 2) { + const tag = TAGS[i / 2] + const value = toFloatCDAB(values[i], values[i + 1]) + if (isNaN(value)) { + if (CLOUD_INGEST_TAGS.has(tag)) { + log( + `edge5.modbus.v3: не удалось собрать число для ${tag}, ` + + `исходные values[${i}]=${values[i]}, values[${i + 1}]=${values[i + 1]}, пропуск`, + ) + } + continue + } + + metrics.push({ + edge: EDGE, + timestamp, + tag, + value, + }) + } + + return metrics +} + +async function postMetrics(metrics: Edge5ModbusV3Metric[]): Promise { + const toPost = metrics.filter((metric) => CLOUD_INGEST_TAGS.has(metric.tag)) + const errors: Error[] = [] + + for (const metric of toPost) { + try { + const response = await postCloudIngest(metric) + + if (!response.ok) { + const text = await response.text() + throw new Error(text || `${response.status} ${response.statusText}`) + } + } catch (error) { + const message = error instanceof Error ? error.message : String(error) + errors.push(new Error(`${metric.tag}: ${message}`)) + console.error('edge5.modbus.v3.metric.failed', metric.tag, message) + } + } + + log('edge5.modbus.v3.posted', { total: toPost.length }); + + if (errors.length) { + throw new Error(`Failed to post ${errors.length}/${toPost.length} edge5 modbus v3 metrics`) + } +} + +export const handleEdge5ModbusV3: TopicHandler = async (topic, payload) => { + const timestamp = new Date().toISOString(); + log('edge5.modbus.v3.received', { topic, bytes: payload.length, timestamp }) + + const values = parseValues(payload); + const metrics = toMetrics(values, timestamp); + await postMetrics(metrics); +} diff --git a/app/src/handlers/index.ts b/app/src/handlers/index.ts index 83b8aaf..721c367 100644 --- a/app/src/handlers/index.ts +++ b/app/src/handlers/index.ts @@ -3,6 +3,7 @@ import { register } from '../registry.js'; import { handleDemoPlc } from './demo-plc.js'; import { handleDemoModbus } from './demo-modbus.js'; import { handleEdge5ModbusV2 } from './edge5-modbus-v2.js'; +import { handleEdge5ModbusV3 } from './edge5-modbus-v3.js'; import { handleEdge5Video } from './edge5-video.js'; import { handleEdge5VideoV2 } from './edge5-video-v2.js'; @@ -22,3 +23,4 @@ register(EDGE5_VIDEO_TOPIC, handleEdge5Video); register('data/edge5i/modbus', interpretation); register('data/edge5cdab/modbus', cdab); register('data/edge5/video/v2/+', handleEdge5VideoV2); +register('data/edge5/modbus/v3', handleEdge5ModbusV3);