mirror of
https://github.com/sergeyyarkov/modbus-logger.git
synced 2026-10-11 02:18:51 +03:00
progress
This commit is contained in:
parent
ac2e27cf6a
commit
53056a3362
@ -1,6 +1,7 @@
|
|||||||
import db from "#root/config/database.config.js";
|
import db from "#root/config/database.config.js";
|
||||||
import modbusClient from "#root/config/modbus-client.config.js";
|
import modbusClient from "#root/config/modbus-client.config.js";
|
||||||
import { appService } from "#root/services/index.js";
|
import * as utils from '#root/utils/index.js'
|
||||||
|
import { appService, modbusService } from "#root/services/index.js";
|
||||||
|
|
||||||
export const modbusController = {
|
export const modbusController = {
|
||||||
/**
|
/**
|
||||||
@ -204,49 +205,35 @@ export const modbusController = {
|
|||||||
async streamData(req, res, next) {
|
async streamData(req, res, next) {
|
||||||
try {
|
try {
|
||||||
if (!modbusClient.isOpen) return res.status(503).json({ error: { message: "Modbus connection closed." } });
|
if (!modbusClient.isOpen) return res.status(503).json({ error: { message: "Modbus connection closed." } });
|
||||||
|
|
||||||
|
const { slave_id } = req.query;
|
||||||
|
|
||||||
|
if (!slave_id)
|
||||||
|
return res.status(400).json({ error: { message: "'slave_id' parameter is required." } });
|
||||||
|
|
||||||
|
const device = await db.get(`SELECT * FROM "modbus_slaves" WHERE id = ?`, [slave_id]);
|
||||||
|
|
||||||
|
if (!device)
|
||||||
|
return res.status(404).json({ error: { message: "Device not found." } });
|
||||||
|
|
||||||
if (!req.query.slave_id) return res.status(400).json({ error: { message: "'slave_id' parameter is required." } });
|
device.display_values = await db.all(`SELECT * FROM "display_values" WHERE slave_id = ?`, [slave_id])
|
||||||
|
|
||||||
const device = await db.get(`SELECT * FROM "modbus_slaves" WHERE id = ?`, [req.query.slave_id]);
|
|
||||||
const displayValues = await db.all(`SELECT * FROM "display_values" WHERE slave_id = ?`, [req.query.slave_id])
|
|
||||||
|
|
||||||
if (!device) return res.status(404).json({ error: { message: "Device not found." } });
|
|
||||||
|
|
||||||
res.setHeader("Cache-Control", "no-cache");
|
res.setHeader("Cache-Control", "no-cache");
|
||||||
res.setHeader("Content-Type", "text/event-stream");
|
res.setHeader("Content-Type", "text/event-stream");
|
||||||
res.setHeader("Connection", "keep-alive");
|
res.setHeader("Connection", "keep-alive");
|
||||||
|
|
||||||
let interval = setInterval(async () => {
|
const streamInterval = setInterval(async () => {
|
||||||
let graphValue = 0;
|
try {
|
||||||
let len = 1; // 16 bits default
|
const data = await modbusService.readDataFromDevice(device, 'HR');
|
||||||
|
res.write("event: message\n");
|
||||||
for (const value of displayValues) {
|
res.write(`data: ${JSON.stringify(data)}\n\n`);
|
||||||
value.data = Buffer.from((await modbusClient.readHoldingRegisters(value.reg_addr, 1)).buffer).readUInt16BE();
|
} catch (error) {
|
||||||
}
|
clearInterval(streamInterval);
|
||||||
|
next(error);
|
||||||
if (device.g_display_reg_format === 32) len = 2;
|
|
||||||
// ...64, 128, 256...
|
|
||||||
|
|
||||||
const result = await modbusClient.readHoldingRegisters(device.g_display_reg_addr, len);
|
|
||||||
|
|
||||||
switch (device.g_display_reg_format) {
|
|
||||||
case 32:
|
|
||||||
graphValue = Buffer.from(result.buffer).readUInt32BE();
|
|
||||||
break;
|
|
||||||
case 16:
|
|
||||||
graphValue = Buffer.from(result.buffer).readUint16BE();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res.write("event: message\n");
|
|
||||||
res.write(`data: ${JSON.stringify({
|
|
||||||
graph: { value: graphValue, format: device.g_display_reg_format },
|
|
||||||
displayValues
|
|
||||||
})}\n\n`);
|
|
||||||
}, 1000);
|
}, 1000);
|
||||||
req.on("close", () => clearInterval(interval));
|
|
||||||
|
req.on("close", () => clearInterval(streamInterval));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,9 +2,6 @@ import EventEmmiter from "events";
|
|||||||
|
|
||||||
const globalEventEmmiter = new EventEmmiter();
|
const globalEventEmmiter = new EventEmmiter();
|
||||||
|
|
||||||
/**
|
|
||||||
* Start logging data from modbus slaves
|
|
||||||
*/
|
|
||||||
globalEventEmmiter.on("log:start", (pollInterval) => {});
|
globalEventEmmiter.on("log:start", (pollInterval) => {});
|
||||||
|
|
||||||
globalEventEmmiter.on("log:stop", () => {});
|
globalEventEmmiter.on("log:stop", () => {});
|
||||||
|
|||||||
20
index.d.ts
vendored
20
index.d.ts
vendored
@ -10,3 +10,23 @@ export type AppConfig = {
|
|||||||
log_interval_ms: number;
|
log_interval_ms: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type NumberType = 'UI16' | 'I16' | 'UI32' | 'I32' | 'FP32'
|
||||||
|
|
||||||
|
export type DisplayValue = {
|
||||||
|
data?: number;
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
slave_id: number;
|
||||||
|
reg_addr: number;
|
||||||
|
reg_format: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ModbusDevice = {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
g_display_reg_addr: number | null;
|
||||||
|
g_display_reg_format: NumberType | null;
|
||||||
|
g_y_label: string | null;
|
||||||
|
is_logging: boolean;
|
||||||
|
display_values?: DisplayValue[]
|
||||||
|
}
|
||||||
@ -21,10 +21,9 @@ CREATE TABLE IF NOT EXISTS "app_config" (
|
|||||||
CREATE TABLE IF NOT EXISTS "modbus_slaves" (
|
CREATE TABLE IF NOT EXISTS "modbus_slaves" (
|
||||||
"id" INTEGER CHECK (id >= 1 AND id <= 255),
|
"id" INTEGER CHECK (id >= 1 AND id <= 255),
|
||||||
"name" VARCHAR(256) NOT NULL,
|
"name" VARCHAR(256) NOT NULL,
|
||||||
-- Holding register
|
|
||||||
"g_display_reg_addr" INTEGER CHECK(g_display_reg_addr >= 0 AND g_display_reg_addr <= 65534) DEFAULT NULL,
|
"g_display_reg_addr" INTEGER CHECK(g_display_reg_addr >= 0 AND g_display_reg_addr <= 65534) DEFAULT NULL,
|
||||||
-- Use 1 or 2 bytes
|
"g_display_reg_type" VARCHAR(3) CHECK(g_display_reg_type, IN("HR", "IR")) DEFAULT "HR",
|
||||||
"g_display_reg_format" INTEGER CHECK(g_display_reg_format IN(16, 32)) DEFAULT 16,
|
"g_display_reg_format" VARCHAR(5) CHECK(g_display_reg_format IN("UI16", "I16", "UI32", "I32", "FP32")) DEFAULT "UI16",
|
||||||
"g_y_label" VARCHAR(40) DEFAULT "Label",
|
"g_y_label" VARCHAR(40) DEFAULT "Label",
|
||||||
"is_logging" BOOLEAN NOT NULL DEFAULT FALSE,
|
"is_logging" BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
PRIMARY KEY("id")
|
PRIMARY KEY("id")
|
||||||
@ -58,9 +57,9 @@ CREATE TABLE IF NOT EXISTS "display_values" (
|
|||||||
INSERT INTO "modbus_slaves" (
|
INSERT INTO "modbus_slaves" (
|
||||||
"id", "name", "g_display_reg_addr", "g_display_reg_format", "g_y_label", "is_logging"
|
"id", "name", "g_display_reg_addr", "g_display_reg_format", "g_y_label", "is_logging"
|
||||||
) VALUES
|
) VALUES
|
||||||
(1, "Device #1", 0, 16, "Label 1", 1),
|
(1, "Device #1", 0, "UI16", "Label 1", 1),
|
||||||
(2, "Device #2", 1, 32, "Label 2", 1),
|
(2, "Device #2", 1, "UI32", "Label 2", 1),
|
||||||
(3, "Device #3", 3, 16, "Label 3", 0),
|
(3, "Device #3", 3, "UI16", "Label 3", 0),
|
||||||
(4, "Device #4", NULL, NULL, "Label 4", 0);
|
(4, "Device #4", NULL, NULL, "Label 4", 0);
|
||||||
|
|
||||||
INSERT INTO "display_values" (
|
INSERT INTO "display_values" (
|
||||||
|
|||||||
@ -28,7 +28,7 @@
|
|||||||
<span x-show="devices.length === 0" class="text-gray-500 text-sm text-center block">
|
<span x-show="devices.length === 0" class="text-gray-500 text-sm text-center block">
|
||||||
No devices added.
|
No devices added.
|
||||||
</span>
|
</span>
|
||||||
<div @add-device.window="devices = [...devices, $event.detail]; selectedDevice = $event.detail" class="flex flex-col max-h-[400px] overflow-y-auto space-y-2">
|
<div @add-device.window="devices = [...devices, $event.detail];" class="flex flex-col max-h-[400px] overflow-y-auto space-y-2">
|
||||||
<template x-for="device in devices" :key="device.id">
|
<template x-for="device in devices" :key="device.id">
|
||||||
<button
|
<button
|
||||||
@click="selectDevice(device)"
|
@click="selectDevice(device)"
|
||||||
@ -59,7 +59,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<form x-data="{ graphView: '' }" @submit.prevent="create(() => graphView = 'none')" action="#">
|
<form @submit.prevent="create(() => graphView = 'none')" action="#">
|
||||||
<div x-id="['id', 'name', 'graph-display', 'g_display_reg_addr', 'g_display_reg_format', 'g_y_label', 'is_logging']" class="flex flex-col space-y-2">
|
<div x-id="['id', 'name', 'graph-display', 'g_display_reg_addr', 'g_display_reg_format', 'g_y_label', 'is_logging']" class="flex flex-col space-y-2">
|
||||||
<div>
|
<div>
|
||||||
<label :for="$id('id')"><span class="text-red-500">*</span>Slave ID:</label>
|
<label :for="$id('id')"><span class="text-red-500">*</span>Slave ID:</label>
|
||||||
@ -86,10 +86,13 @@
|
|||||||
<input x-model="data.g_display_reg_addr" :id="$id('g_display_reg_addr')" name="g_display_reg_addr" placeholder="1" type="number" min="0" max="65534" :required="graphView === 'new-value'" />
|
<input x-model="data.g_display_reg_addr" :id="$id('g_display_reg_addr')" name="g_display_reg_addr" placeholder="1" type="number" min="0" max="65534" :required="graphView === 'new-value'" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label :for="$id('g_display_reg_format')"><span class="text-red-500">*</span>Format (bits):</label>
|
<label :for="$id('g_display_reg_format')"><span class="text-red-500">*</span>Format (type):</label>
|
||||||
<select x-model.number="data.g_display_reg_format" :id="$id('g_display_reg_format')" name="g_display_reg_format">
|
<select x-model="data.g_display_reg_format" :id="$id('g_display_reg_format')" name="g_display_reg_format">
|
||||||
<option value="16">16</option>
|
<option value="UI16">Unsigned Int 16</option>
|
||||||
<option value="32">32</option>
|
<option value="I16">Signed Int 16</option>
|
||||||
|
<option value="UI32">Unsigned Int 32</option>
|
||||||
|
<option value="I32">Signed Int 32</option>
|
||||||
|
<option value="FP32">Floating Point 32</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@ -152,9 +155,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Error message -->
|
<!-- Error message -->
|
||||||
<!-- <div>
|
<div>
|
||||||
<span class="text-red-500" x-text="error?.message"></span>
|
<span class="text-red-500" x-text="error?.message"></span>
|
||||||
</div> -->
|
</div>
|
||||||
<!-- Display Graph -->
|
<!-- Display Graph -->
|
||||||
<div x-show="!isLoading && selectedDevice?.g_display_reg_addr !== null" class="mt-4">
|
<div x-show="!isLoading && selectedDevice?.g_display_reg_addr !== null" class="mt-4">
|
||||||
<div id="div_g" style="width: 720px; height: 300px"></div>
|
<div id="div_g" style="width: 720px; height: 300px"></div>
|
||||||
@ -225,8 +228,8 @@
|
|||||||
<hr />
|
<hr />
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<span>Value: <span x-text="value?.data"></span></span>
|
<span>Value: <span x-text="value?.data"></span></span>
|
||||||
<span>Reg Addr: <span x-text="value.reg_addr"></span></span>
|
<span>Reg Addr: <span x-text="value?.reg_addr"></span></span>
|
||||||
<span>Format: <span x-text="value.reg_format"></span></span>
|
<span>Format: <span x-text="value?.reg_format"></span></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -5,8 +5,8 @@ import * as utils from "./utils/index.js";
|
|||||||
|
|
||||||
var pages = ["loading", "monitoring", "configuration", "404"];
|
var pages = ["loading", "monitoring", "configuration", "404"];
|
||||||
var dataStreamSource;
|
var dataStreamSource;
|
||||||
var graphData = [[0,0]];
|
var graphData = [];
|
||||||
var g;
|
var g = null;
|
||||||
|
|
||||||
document.addEventListener("alpine:init", async () => {
|
document.addEventListener("alpine:init", async () => {
|
||||||
Alpine.store("app", {
|
Alpine.store("app", {
|
||||||
@ -62,11 +62,12 @@ document.addEventListener("alpine:init", async () => {
|
|||||||
isOpen: false,
|
isOpen: false,
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
error: null,
|
error: null,
|
||||||
|
graphView: '',
|
||||||
data: {
|
data: {
|
||||||
id: null,
|
id: null,
|
||||||
name: "",
|
name: "",
|
||||||
g_display_reg_addr: null,
|
g_display_reg_addr: null,
|
||||||
g_display_reg_format: 16,
|
g_display_reg_format: "UI16",
|
||||||
g_y_label: "",
|
g_y_label: "",
|
||||||
is_logging: false,
|
is_logging: false,
|
||||||
},
|
},
|
||||||
@ -74,7 +75,7 @@ document.addEventListener("alpine:init", async () => {
|
|||||||
this.data.id = null;
|
this.data.id = null;
|
||||||
this.data.name = "";
|
this.data.name = "";
|
||||||
this.data.g_display_reg_addr = null;
|
this.data.g_display_reg_addr = null;
|
||||||
this.data.g_display_reg_format = 16;
|
this.data.g_display_reg_format = "UI16";
|
||||||
this.data.g_y_label = "";
|
this.data.g_y_label = "";
|
||||||
this.data.is_logging = false;
|
this.data.is_logging = false;
|
||||||
},
|
},
|
||||||
@ -83,7 +84,6 @@ document.addEventListener("alpine:init", async () => {
|
|||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
this.error = null;
|
this.error = null;
|
||||||
await api.post("/modbus/create_device", utils.dellNullableKeys({ ...this.data }));
|
await api.post("/modbus/create_device", utils.dellNullableKeys({ ...this.data }));
|
||||||
graphData = []; // todo: update graph data
|
|
||||||
this.$dispatch("add-device", { ...this.data }); // add device to state
|
this.$dispatch("add-device", { ...this.data }); // add device to state
|
||||||
this.resetDataFields();
|
this.resetDataFields();
|
||||||
this.close();
|
this.close();
|
||||||
@ -100,6 +100,8 @@ document.addEventListener("alpine:init", async () => {
|
|||||||
},
|
},
|
||||||
close() {
|
close() {
|
||||||
this.isOpen = false;
|
this.isOpen = false;
|
||||||
|
this.resetDataFields();
|
||||||
|
this.graphView = '';
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -111,6 +113,8 @@ document.addEventListener("alpine:init", async () => {
|
|||||||
resetGraph() {
|
resetGraph() {
|
||||||
if (g instanceof Dygraph) {
|
if (g instanceof Dygraph) {
|
||||||
g.destroy();
|
g.destroy();
|
||||||
|
graphData = [];
|
||||||
|
g = null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async init() {
|
async init() {
|
||||||
@ -125,7 +129,8 @@ document.addEventListener("alpine:init", async () => {
|
|||||||
},
|
},
|
||||||
selectDevice(device) {
|
selectDevice(device) {
|
||||||
if (device.id === this.selectedDevice?.id) return;
|
if (device.id === this.selectedDevice?.id) return;
|
||||||
|
|
||||||
|
this.error = null;
|
||||||
this.selectedDevice = device;
|
this.selectedDevice = device;
|
||||||
this.resetGraph();
|
this.resetGraph();
|
||||||
|
|
||||||
@ -134,26 +139,29 @@ document.addEventListener("alpine:init", async () => {
|
|||||||
|
|
||||||
dataStreamSource?.close();
|
dataStreamSource?.close();
|
||||||
dataStreamSource = new EventSource(`/api/modbus/data_stream?slave_id=${device.id}`);
|
dataStreamSource = new EventSource(`/api/modbus/data_stream?slave_id=${device.id}`);
|
||||||
g = new Dygraph(document.getElementById("div_g"), graphData, {
|
|
||||||
drawPoints: false,
|
|
||||||
showRoller: false,
|
|
||||||
ylabel: device.g_y_label,
|
|
||||||
// valueRange: [0, 1000],
|
|
||||||
// labels: ["Time", "Random"],
|
|
||||||
});
|
|
||||||
g.resize(720, 300);
|
|
||||||
dataStreamSource.onerror = (e) => {
|
dataStreamSource.onerror = (e) => {
|
||||||
dataStreamSource.close();
|
dataStreamSource.close();
|
||||||
|
this.isLoading = false;
|
||||||
|
this.error = { message: 'Something went wrong.' }
|
||||||
}
|
}
|
||||||
dataStreamSource.onmessage = (e) => {
|
dataStreamSource.onmessage = (e) => {
|
||||||
const data = JSON.parse(e.data);
|
const data = JSON.parse(e.data);
|
||||||
console.log(data);
|
|
||||||
graphData.push([new Date(), data.graph.value]);
|
if (data.graph !== null) {
|
||||||
g.updateOptions({ file: graphData });
|
graphData.push([new Date(), data.graph.value]);
|
||||||
|
if (g === null) {
|
||||||
|
g = new Dygraph(document.getElementById("div_g"), graphData, {
|
||||||
|
drawPoints: false,
|
||||||
|
showRoller: false,
|
||||||
|
ylabel: device.g_y_label,
|
||||||
|
});
|
||||||
|
g.resize(720, 300);
|
||||||
|
}
|
||||||
|
g.updateOptions({ file: graphData });
|
||||||
|
}
|
||||||
data.displayValues.forEach((v, i) => this.selectedDevice.display_values[i] = v);
|
data.displayValues.forEach((v, i) => this.selectedDevice.display_values[i] = v);
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
};
|
};
|
||||||
graphData = [];
|
|
||||||
},
|
},
|
||||||
async removeDevice(id) {
|
async removeDevice(id) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -1 +1,2 @@
|
|||||||
export * from "./app.service.js";
|
export * from "./app.service.js";
|
||||||
|
export * from './modbus.service.js'
|
||||||
39
services/modbus.service.js
Normal file
39
services/modbus.service.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import modbusClient from "#root/config/modbus-client.config.js";
|
||||||
|
import * as utils from '#root/utils/index.js'
|
||||||
|
|
||||||
|
export const modbusService = {
|
||||||
|
/**
|
||||||
|
* @param {import("..").ModbusDevice} device
|
||||||
|
* @param {"HR" | "IR"} type
|
||||||
|
*/
|
||||||
|
async readDataFromDevice(device, type = 'HR') {
|
||||||
|
const { g_display_reg_format: g_format, g_display_reg_addr: g_reg_addr } = device;
|
||||||
|
let readLength = 1; // 16 bits default
|
||||||
|
|
||||||
|
modbusClient.setID(device.id);
|
||||||
|
|
||||||
|
if (g_format?.includes('32')) readLength = 2;
|
||||||
|
if (g_format?.includes('64')) readLength = 4;
|
||||||
|
if (g_format?.includes('128')) readLength = 8;
|
||||||
|
|
||||||
|
let graphValue;
|
||||||
|
if (type === 'HR') graphValue = await modbusClient.readHoldingRegisters(g_reg_addr, readLength);
|
||||||
|
if (type === 'IR') graphValue = await modbusClient.readInputRegisters(g_reg_addr, readLength);
|
||||||
|
|
||||||
|
if (device.display_values) {
|
||||||
|
for (const value of device.display_values) {
|
||||||
|
value.data = Buffer.from((await modbusClient.readHoldingRegisters(value.reg_addr, 1)).buffer).readUInt16BE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const graph = (g_reg_addr !== null ? {
|
||||||
|
value: utils.readNumberFromBuf(graphValue.buffer, device.g_display_reg_format, 'BE'),
|
||||||
|
format: g_format
|
||||||
|
} : null);
|
||||||
|
|
||||||
|
return {
|
||||||
|
graph,
|
||||||
|
displayValues: device.display_values
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
utils/index.js
Normal file
1
utils/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './number.js'
|
||||||
19
utils/number.js
Normal file
19
utils/number.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* Read number from buffer
|
||||||
|
* @param {Buffer} buf
|
||||||
|
* @param {import("..").NumberType} type
|
||||||
|
* @param {"BE" | "LE"} order
|
||||||
|
*/
|
||||||
|
export function readNumberFromBuf(buf, type, order) {
|
||||||
|
/* Big-Endian */
|
||||||
|
if (type === 'UI16' && order === 'BE') return Buffer.from(buf).readUInt16BE();
|
||||||
|
if (type === 'I16' && order === 'BE') return Buffer.from(buf).readInt16BE();
|
||||||
|
if (type === 'UI32' && order === 'BE') return Buffer.from(buf).readUint32BE();
|
||||||
|
if (type === 'FP32' && order === 'BE') return Buffer.from(buf).readFloatBE();
|
||||||
|
|
||||||
|
/* Little-Endian */
|
||||||
|
if (type === 'UI16' && order === 'LE') return Buffer.from(buf).readUInt16LE();
|
||||||
|
if (type === 'I16' && order === 'LE') return Buffer.from(buf).readInt16LE();
|
||||||
|
if (type === 'UI32' && order === 'LE') return Buffer.from(buf).readUint32LE();
|
||||||
|
if (type === 'FP32' && order === 'LE') return Buffer.from(buf).readFloatLE();
|
||||||
|
}
|
||||||
@ -4,7 +4,7 @@ export const modbusDeviceSchema = object({
|
|||||||
id: number().required(),
|
id: number().required(),
|
||||||
name: string().required(),
|
name: string().required(),
|
||||||
g_display_reg_addr: number().min(0).max(65534).nullable(),
|
g_display_reg_addr: number().min(0).max(65534).nullable(),
|
||||||
g_display_reg_format: number().oneOf([16, 32]).nullable(),
|
g_display_reg_format: string().oneOf(["UI16", "I16", "UI32", "I32", "FP32"]).nullable(),
|
||||||
g_y_label: string().max(40).nullable(),
|
g_y_label: string().max(40).nullable(),
|
||||||
is_logging: boolean().required()
|
is_logging: boolean().required()
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user