This commit is contained in:
Sergey Yarkov 2023-11-02 16:14:13 +03:00
parent 92d5449246
commit d06edd791a
6 changed files with 145 additions and 57 deletions

View File

@ -1,6 +1,5 @@
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 * as utils from '#root/utils/index.js'
import { appService, modbusService } from "#root/services/index.js"; import { appService, modbusService } from "#root/services/index.js";
export const modbusController = { export const modbusController = {
@ -12,30 +11,10 @@ export const modbusController = {
*/ */
async connect(req, res, next) { async connect(req, res, next) {
try { try {
const config = await appService.getConfig(db); const appConfig = await appService.getConfig();
if (!config) return res.status(400).json({ message: "Application is not configured" }); if (!appConfig) return res.status(400).json({ message: "Application is not configured" });
await modbusService.connect(appConfig)
if (modbusClient.isOpen) { return res.status(200).json({ message: "Connected." });
modbusClient.close(undefined);
modbusClient.destroy(undefined);
}
if (config.mb_connection_type === "TCP") {
await modbusClient.connectTCP(config.mb_tcp_ip, { port: config.mb_tcp_port });
return res.status(200).json({ message: "Connected." });
}
if (config.mb_connection_type === "RTU") {
await modbusClient.connectRTU(config.mb_rtu_path, {
baudRate: config.mb_rtu_baud,
dataBits: config.mb_rtu_data_bits,
parity: config.mb_rtu_parity,
stopBits: config.mb_rtu_stop_bits,
});
return res.status(200).json({ message: "Connected." });
}
return res.status(400).json({ message: "Connection type is invalid." });
} catch (error) { } catch (error) {
next(error); next(error);
} }
@ -66,25 +45,8 @@ export const modbusController = {
*/ */
async list(req, res, next) { async list(req, res, next) {
try { try {
const slaves = await db.all(` const devices = await modbusService.getDevices();
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, return res.status(200).json(devices);
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;
`);
slaves.forEach((s) => (s.display_values = JSON.parse(s.display_values)));
return res.status(200).json(slaves);
} catch (error) { } catch (error) {
next(error); next(error);
} }
@ -218,6 +180,8 @@ export const modbusController = {
if (!device) if (!device)
return res.status(404).json({ error: { message: "Device not found." } }); return res.status(404).json({ error: { message: "Device not found." } });
// modbusService.createPollInterval(req, res, device).poll(1000);
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");

BIN
data.db

Binary file not shown.

View File

@ -774,6 +774,14 @@ video {
max-width: 64rem; max-width: 64rem;
} }
.max-w-xl {
max-width: 36rem;
}
.max-w-md {
max-width: 28rem;
}
.flex-1 { .flex-1 {
flex: 1 1 0%; flex: 1 1 0%;
} }
@ -956,6 +964,10 @@ video {
padding: 20rem; padding: 20rem;
} }
.p-5 {
padding: 1.25rem;
}
.px-2 { .px-2 {
padding-left: 0.5rem; padding-left: 0.5rem;
padding-right: 0.5rem; padding-right: 0.5rem;
@ -971,6 +983,21 @@ video {
padding-bottom: 0.75rem; padding-bottom: 0.75rem;
} }
.px-6 {
padding-left: 1.5rem;
padding-right: 1.5rem;
}
.py-4 {
padding-top: 1rem;
padding-bottom: 1rem;
}
.px-5 {
padding-left: 1.25rem;
padding-right: 1.25rem;
}
.pl-0 { .pl-0 {
padding-left: 0px; padding-left: 0px;
} }

View File

@ -264,7 +264,7 @@
<!-- Configuration content --> <!-- Configuration content -->
<template x-if="$store.app.currentPage === 'configuration'"> <template x-if="$store.app.currentPage === 'configuration'">
<main class="mt-6 mb-2" x-data="configAppPage" x-bind:hidden="$store.app.currentPage !== 'configuration'"> <main class="mt-6 mb-2" x-data="configAppPage" x-bind:hidden="$store.app.currentPage !== 'configuration'">
<div class="border rounded-sm p-4 max-w-2xl mx-auto"> <div class="border rounded-sm p-5 max-w-md mx-auto mb-2">
<h2 class="text-2xl font-bold">Setup configuration</h2> <h2 class="text-2xl font-bold">Setup configuration</h2>
<p class="text-gray-700 italic"> <p class="text-gray-700 italic">
Fill in the required configuration settings to connect to the Modbus network. Fill in the required configuration settings to connect to the Modbus network.
@ -273,7 +273,7 @@
<form @submit.prevent="configurateApp()" action="#"> <form @submit.prevent="configurateApp()" action="#">
<div> <div>
<div> <div>
<label class="block" for="connection_type">Network type</label> <label class="block" for="connection_type"><span class="text-red-500">*</span>Network type</label>
<select <select
x-model="$store.app.config.mb_connection_type" x-model="$store.app.config.mb_connection_type"
name="mb_connection_type" name="mb_connection_type"
@ -287,7 +287,7 @@
<div x-show="$store.app.config.mb_connection_type === 'TCP'" class="flex flex-col space-y-3"> <div x-show="$store.app.config.mb_connection_type === 'TCP'" class="flex flex-col space-y-3">
<div> <div>
<label for="tcp_ip">IP</label> <label for="tcp_ip"><span class="text-red-500">*</span>IP</label>
<input <input
x-model="$store.app.config.mb_tcp_ip" x-model="$store.app.config.mb_tcp_ip"
id="tcp_ip" id="tcp_ip"
@ -299,7 +299,7 @@
</div> </div>
<div> <div>
<label for="rtu_port">Port</label> <label for="rtu_port"><span class="text-red-500">*</span>Port</label>
<input <input
x-model.number="$store.app.config.mb_tcp_port" x-model.number="$store.app.config.mb_tcp_port"
id="rtu_port" id="rtu_port"
@ -313,7 +313,7 @@
<div x-show="$store.app.config.mb_connection_type === 'RTU'" class="flex flex-col space-y-3"> <div x-show="$store.app.config.mb_connection_type === 'RTU'" class="flex flex-col space-y-3">
<div> <div>
<label for="rtu_path">Path</label> <label for="rtu_path"><span class="text-red-500">*</span>Path</label>
<input <input
id="rtu_path" id="rtu_path"
name="mb_rtu_path" name="mb_rtu_path"
@ -325,7 +325,7 @@
</div> </div>
<div> <div>
<label for="rtu_baud">Baud</label> <label for="rtu_baud"><span class="text-red-500">*</span>Baud</label>
<select <select
name="mb_rtu_baud" name="mb_rtu_baud"
id="rtu_baud" id="rtu_baud"
@ -342,7 +342,7 @@
</div> </div>
<div> <div>
<label for="rtu_parity">Parity</label> <label for="rtu_parity"><span class="text-red-500">*</span>Parity</label>
<select <select
name="mb_rtu_parity" name="mb_rtu_parity"
id="rtu_parity" id="rtu_parity"
@ -356,7 +356,7 @@
</div> </div>
<div> <div>
<label for="rtu_data_bits">Data bits</label> <label for="rtu_data_bits"><span class="text-red-500">*</span>Data bits</label>
<select <select
name="mb_rtu_data_bits" name="mb_rtu_data_bits"
id="rtu_data_bits" id="rtu_data_bits"
@ -371,7 +371,7 @@
</div> </div>
<div> <div>
<label for="rtu_stop_bits">Stop bits</label> <label for="rtu_stop_bits"><span class="text-red-500">*</span>Stop bits</label>
<select <select
name="mb_rtu_stop_bits" name="mb_rtu_stop_bits"
id="rtu_stop_bits" id="rtu_stop_bits"
@ -386,7 +386,7 @@
</div> </div>
<div class="mt-2"> <div class="mt-2">
<label for="rtu_path">Log Interval (ms)</label> <label for="rtu_path"><span class="text-red-500">*</span>Log Interval (ms)</label>
<input <input
id="log_interval_ms" id="log_interval_ms"
name="log_interval_ms" name="log_interval_ms"

View File

@ -1,10 +1,11 @@
import db from "#root/config/database.config.js";
export const appService = { export const appService = {
/** /**
* Get configuration of app * Get configuration of app
* @param {import('sqlite').Database} db
* @returns {Promise<import('../index').AppConfig | undefined>} * @returns {Promise<import('../index').AppConfig | undefined>}
*/ */
async getConfig(db) { async getConfig() {
const data = await db.get(` const data = await db.get(`
SELECT "mb_connection_type", SELECT "mb_connection_type",
"mb_tcp_ip", "mb_tcp_ip",

View File

@ -1,4 +1,5 @@
import modbusClient from "#root/config/modbus-client.config.js"; 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' import * as utils from '#root/utils/index.js'
export const modbusService = { export const modbusService = {
@ -35,6 +36,48 @@ export const modbusService = {
return data; return data;
}, },
/**
* @param {import("..").AppConfig} appConfig
* @return {Promise<void>}
*/
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 {number} addr
* @param {"HR" | "IR" | "DI"} type * @param {"HR" | "IR" | "DI"} type
@ -70,5 +113,58 @@ export const modbusService = {
} }
return data.buffer; 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));
// }
// }
// }
} }