add mapper. add v2 handler. add tag description short and full versions. delete topic[1-5].
This commit is contained in:
85
app/src/handlers/demo-plc.ts
Normal file
85
app/src/handlers/demo-plc.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { parseJson } from '../helpers/parse.js'
|
||||
import type { TopicHandler } from '../types.js'
|
||||
|
||||
export const TOPIC = 'data/demo/plc/v1'
|
||||
|
||||
const EDGE = 'demo'
|
||||
const postUrl =
|
||||
process.env.CLOUD_INGEST_URL ?? 'https://demo.backend.drill.greact.ru/ingest'
|
||||
|
||||
type DemoPlcPayload = {
|
||||
tag: string
|
||||
value: number
|
||||
}
|
||||
|
||||
type DemoPlcMetric = DemoPlcPayload & {
|
||||
edge: string
|
||||
timestamp: number
|
||||
}
|
||||
|
||||
function isDemoPlcPayload(value: unknown): value is DemoPlcPayload {
|
||||
if (!value || typeof value !== 'object') {
|
||||
return false
|
||||
}
|
||||
|
||||
const candidate = value as Partial<DemoPlcPayload>
|
||||
|
||||
return (
|
||||
typeof candidate.tag === 'string' &&
|
||||
candidate.tag.length > 0 &&
|
||||
typeof candidate.value === 'number' &&
|
||||
Number.isFinite(candidate.value)
|
||||
)
|
||||
}
|
||||
|
||||
function toMetric(payload: DemoPlcPayload, timestamp: number): DemoPlcMetric {
|
||||
return {
|
||||
edge: EDGE,
|
||||
timestamp,
|
||||
tag: payload.tag,
|
||||
value: payload.value,
|
||||
}
|
||||
}
|
||||
|
||||
async function postMetric(metric: DemoPlcMetric): Promise<void> {
|
||||
const response = await fetch(postUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(metric),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text()
|
||||
console.error('demo.plc.post_error', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
body: text.slice(0, 500),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
console.log('demo.plc.posted', { tag: metric.tag })
|
||||
}
|
||||
|
||||
export const handleDemoPlc: TopicHandler = async (topic, payload) => {
|
||||
console.log('demo.plc.received', { topic, bytes: payload.length })
|
||||
|
||||
let value: unknown
|
||||
|
||||
try {
|
||||
value = parseJson(payload)
|
||||
} catch (err) {
|
||||
console.warn('demo.plc.invalid_json', { topic, err })
|
||||
return
|
||||
}
|
||||
|
||||
if (!isDemoPlcPayload(value)) {
|
||||
console.warn('demo.plc.invalid_payload', { topic })
|
||||
return
|
||||
}
|
||||
|
||||
const metric = toMetric(value, Date.now())
|
||||
await postMetric(metric)
|
||||
}
|
||||
71
app/src/handlers/edge5-modbus-v2.ts
Normal file
71
app/src/handlers/edge5-modbus-v2.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { createMetricMapper, type MappedMetric } from '../mapping.js'
|
||||
import type { TopicHandler } from '../types.js'
|
||||
|
||||
export const TOPIC = 'data/edge5/modbus/v2'
|
||||
|
||||
const mapper = createMetricMapper({
|
||||
filePath:
|
||||
process.env.EDGE5_MODBUS_V2_MAPPING_FILE ??
|
||||
new URL('../mappings/edge5-modbus.json', import.meta.url),
|
||||
})
|
||||
const REGISTER_COUNT = mapper.tagCount
|
||||
const postUrl =
|
||||
process.env.CLOUD_INGEST_URL ?? 'https://demo.backend.drill.greact.ru/ingest'
|
||||
|
||||
function parseValues(payload: Buffer): number[] {
|
||||
let value: unknown
|
||||
|
||||
try {
|
||||
value = JSON.parse(payload.toString('utf8')) as unknown
|
||||
} catch {
|
||||
throw new Error(`Expected JSON number[${REGISTER_COUNT}]`)
|
||||
}
|
||||
|
||||
if (
|
||||
Array.isArray(value) &&
|
||||
value.length === REGISTER_COUNT &&
|
||||
value.every((item) => typeof item === 'number' && Number.isFinite(item))
|
||||
) {
|
||||
return value
|
||||
}
|
||||
|
||||
throw new Error(`Expected JSON number[${REGISTER_COUNT}]`)
|
||||
}
|
||||
|
||||
async function postMetrics(metrics: MappedMetric[]): Promise<void> {
|
||||
const response = await fetch(postUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(metrics),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text()
|
||||
console.error('edge5.modbus.v2.post_error', {
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
body: text.slice(0, 500),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
console.log('edge5.modbus.v2.posted', { count: metrics.length })
|
||||
}
|
||||
|
||||
export const handleEdge5ModbusV2: TopicHandler = async (topic, payload) => {
|
||||
console.log('edge5.modbus.v2.received', { topic, bytes: payload.length })
|
||||
|
||||
let values: number[]
|
||||
|
||||
try {
|
||||
values = parseValues(payload)
|
||||
} catch (err) {
|
||||
console.warn('edge5.modbus.v2.invalid_payload', { topic, err })
|
||||
return
|
||||
}
|
||||
|
||||
const metrics = mapper.mapValues(values, Date.now())
|
||||
await postMetrics(metrics)
|
||||
}
|
||||
@@ -5,7 +5,7 @@ export const TOPIC = 'data/edge5/modbus/v1'
|
||||
const EDGE = 'edge5'
|
||||
const REGISTER_COUNT = 125
|
||||
const postUrl =
|
||||
process.env.CLOUD_INGEST_URL ?? 'https://demo.backend.drill.greact.ru/api/ingest'
|
||||
process.env.CLOUD_INGEST_URL ?? 'https://demo.backend.drill.greact.ru/ingest'
|
||||
|
||||
type Edge5ModbusMetric = {
|
||||
edge: string
|
||||
@@ -44,7 +44,6 @@ function toMetrics(values: number[], timestamp: number): Edge5ModbusMetric[] {
|
||||
}
|
||||
|
||||
async function postMetrics(metrics: Edge5ModbusMetric[]): Promise<void> {
|
||||
|
||||
const response = await fetch(postUrl, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
import { register } from '../registry.js'
|
||||
import { handleTopic1, TOPIC as topic1 } from './topic1.js'
|
||||
import { handleTopic2, TOPIC as topic2 } from './topic2.js'
|
||||
import { handleTopic3, TOPIC as topic3 } from './topic3.js'
|
||||
import { handleTopic4, TOPIC as topic4 } from './topic4.js'
|
||||
import { handleTopic5, TOPIC as topic5 } from './topic5.js'
|
||||
import { handleDemoPlc, TOPIC as demoPlc } from './demo-plc.js'
|
||||
import { handleEdge5Modbus, TOPIC as edge5Modbus } from './edge5-modbus.js'
|
||||
import {
|
||||
handleEdge5ModbusV2,
|
||||
TOPIC as edge5ModbusV2,
|
||||
} from './edge5-modbus-v2.js'
|
||||
import { handleEdge5Video, TOPIC as edge5Video } from './edge5-video.js'
|
||||
|
||||
register(topic1, handleTopic1)
|
||||
register(topic2, handleTopic2)
|
||||
register(topic3, handleTopic3)
|
||||
register(topic4, handleTopic4)
|
||||
register(topic5, handleTopic5)
|
||||
register(demoPlc, handleDemoPlc)
|
||||
register(edge5Modbus, handleEdge5Modbus)
|
||||
register(edge5ModbusV2, handleEdge5ModbusV2)
|
||||
register(edge5Video, handleEdge5Video)
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import { parseJson } from '../helpers/parse.js'
|
||||
import { isArrayOfChunks125 } from '../helpers/validate.js'
|
||||
import type { TopicHandler } from '../types.js'
|
||||
|
||||
export const TOPIC = 'poc/topic1'
|
||||
|
||||
export const handleTopic1: TopicHandler = async (topic, payload) => {
|
||||
const raw = parseJson(payload)
|
||||
|
||||
if (!isArrayOfChunks125(raw)) {
|
||||
console.warn('topic1.invalid_payload', { topic })
|
||||
return
|
||||
}
|
||||
|
||||
console.log('topic1.received', { topic, chunks: raw.length })
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { parseJson } from '../helpers/parse.js'
|
||||
import { isArrayOfLength125 } from '../helpers/validate.js'
|
||||
import type { TopicHandler } from '../types.js'
|
||||
|
||||
export const TOPIC = 'poc/topic2'
|
||||
|
||||
export const handleTopic2: TopicHandler = async (topic, payload) => {
|
||||
const raw = parseJson(payload)
|
||||
|
||||
if (!isArrayOfLength125(raw)) {
|
||||
console.warn('topic2.invalid_payload', { topic })
|
||||
return
|
||||
}
|
||||
|
||||
console.log('topic2.received', { topic, length: raw.length })
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { parseUtf8 } from '../helpers/parse.js'
|
||||
import { isNonEmptyString } from '../helpers/validate.js'
|
||||
import type { TopicHandler } from '../types.js'
|
||||
|
||||
export const TOPIC = 'poc/topic3'
|
||||
|
||||
export const handleTopic3: TopicHandler = async (topic, payload) => {
|
||||
const text = parseUtf8(payload)
|
||||
|
||||
if (!isNonEmptyString(text)) {
|
||||
console.warn('topic3.invalid_payload', { topic })
|
||||
return
|
||||
}
|
||||
|
||||
console.log('topic3.received', { topic, text })
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { parseJson } from '../helpers/parse.js'
|
||||
import { isFiniteNumber } from '../helpers/validate.js'
|
||||
import type { TopicHandler } from '../types.js'
|
||||
|
||||
export const TOPIC = 'poc/topic4'
|
||||
|
||||
type Topic4Payload = {
|
||||
value: number
|
||||
}
|
||||
|
||||
function isTopic4Payload(value: unknown): value is Topic4Payload {
|
||||
return (
|
||||
typeof value === 'object' &&
|
||||
value !== null &&
|
||||
'value' in value &&
|
||||
isFiniteNumber((value as Topic4Payload).value)
|
||||
)
|
||||
}
|
||||
|
||||
export const handleTopic4: TopicHandler = async (topic, payload) => {
|
||||
const raw = parseJson(payload)
|
||||
|
||||
if (!isTopic4Payload(raw)) {
|
||||
console.warn('topic4.invalid_payload', { topic })
|
||||
return
|
||||
}
|
||||
|
||||
console.log('topic4.received', { topic, value: raw.value })
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { parseJsonArray } from '../helpers/parse.js'
|
||||
import type { TopicHandler } from '../types.js'
|
||||
|
||||
export const TOPIC = 'poc/topic5'
|
||||
|
||||
export const handleTopic5: TopicHandler = async (topic, payload) => {
|
||||
const items = parseJsonArray(payload)
|
||||
|
||||
console.log('topic5.received', { topic, count: items.length })
|
||||
}
|
||||
Reference in New Issue
Block a user