This commit is contained in:
Sergey Yarkov 2023-11-10 16:31:47 +03:00
parent ffcfe6767d
commit 4edb0f3a9d
9 changed files with 142 additions and 116 deletions

View File

@ -3,7 +3,7 @@ import modbusClient from "#root/config/modbus-client.config.js";
import * as utils from '#root/utils/index.js' import * as utils from '#root/utils/index.js'
import { RowNotFoundError } from '#root/errors/index.js' import { RowNotFoundError } from '#root/errors/index.js'
import { appService, modbusService } from "#root/services/index.js"; import { appService, modbusService } from "#root/services/index.js";
import { modbusDeviceModel } from "#root/models/index.js"; import { modbusDeviceModel, displayValueModel } from "#root/models/index.js";
export const modbusController = { export const modbusController = {
/** /**
@ -63,19 +63,14 @@ export const modbusController = {
*/ */
async createDevice(req, res, next) { async createDevice(req, res, next) {
try { try {
const { id, name, g_display_reg_addr, g_display_reg_format, g_display_reg_type, g_y_label } = await modbusDeviceModel.create({
req.body; id: req.body.id,
await db.run( name: req.body.name,
`INSERT INTO "modbus_slaves" ( g_display_reg_addr: req.body.g_display_reg_addr,
"id", g_display_reg_format: req.body.g_display_reg_format,
"name", g_display_reg_type: req.body.g_display_reg_type,
"g_display_reg_addr", g_y_label: req.body.g_y_label
"g_display_reg_format", })
"g_display_reg_type",
"g_y_label")
VALUES (?, ?, ?, ?, ?, ?)`,
[id, name, g_display_reg_addr, g_display_reg_format, g_display_reg_type, g_y_label],
);
return res.status(200).json({ message: "Device created." }); return res.status(200).json({ message: "Device created." });
} catch (error) { } catch (error) {
next(error); next(error);
@ -124,9 +119,10 @@ export const modbusController = {
*/ */
async removeDevice(req, res, next) { async removeDevice(req, res, next) {
try { try {
const device = await db.get(`SELECT id FROM "modbus_slaves" WHERE id = ?`, [req.body.id]); const { id } = req.body;
const device = await modbusDeviceModel.getById(id)
if (!device) throw new RowNotFoundError(); if (!device) throw new RowNotFoundError();
await db.run(`DELETE FROM "modbus_slaves" WHERE id = ?`, [req.body.id]); await modbusDeviceModel.delById(id);
return res.status(200).json({ message: "Device removed." }); return res.status(200).json({ message: "Device removed." });
} catch (error) { } catch (error) {
next(error); next(error);
@ -167,7 +163,10 @@ export const modbusController = {
*/ */
async removeDisplayValue(req, res, next) { async removeDisplayValue(req, res, next) {
try { try {
await db.run(`DELETE FROM "display_values" WHERE id = ?`, req.body.id); const { id } = req.body;
const value = await displayValueModel.getById(id);
if (!value) throw new RowNotFoundError();
await displayValueModel.delById(id);
return res.status(200).json({ message: "Display value removed." }); return res.status(200).json({ message: "Display value removed." });
} catch (error) { } catch (error) {
next(error); next(error);
@ -199,7 +198,7 @@ export const modbusController = {
}, },
/** /**
* Creates event stream aboud modbus connection status * Creates event stream about modbus connection status
* @param {import('express').Request} req * @param {import('express').Request} req
* @param {import('express').Response} res * @param {import('express').Response} res
* @param {import('express').NextFunction} next * @param {import('express').NextFunction} next

BIN
data.db

Binary file not shown.

View File

@ -57,26 +57,27 @@ CREATE TABLE IF NOT EXISTS "display_values" (
INSERT INTO "modbus_slaves" ( INSERT INTO "modbus_slaves" (
"id", "name", "g_display_reg_addr", "g_display_reg_type", "g_display_reg_format", "g_y_label" "id", "name", "g_display_reg_addr", "g_display_reg_type", "g_display_reg_format", "g_y_label"
) VALUES ) VALUES
(1, "Device #1", 0, "IR", "UI16", "Label 1"), (1, "MKP #1", 132, "HR", "UI16", "PID Out"),
(2, "Device #2", 1, "IR", "UI32", "Label 2"), (2, "Test Device #1", 1, "IR", "UI32", "Label 2"),
(3, "Device #3", 3, "IR", "UI16", "Label 3"), (3, "Test Device #2", 3, "IR", "UI16", "Label 3"),
(4, "Device #4", NULL, NULL, NULL, "Label 4"); (4, "Test Device #3", NULL, NULL, NULL, "Label 4");
INSERT INTO "display_values" ( INSERT INTO "display_values" (
"id", "name", "slave_id", "reg_addr", "reg_format", "reg_type" "id", "name", "slave_id", "reg_addr", "reg_format", "reg_type"
) VALUES ) VALUES
(1, "CV1", 1, 10, "UI16", "IR"), (1, "SP", 1, 138, "UI32", "HR"),
(2, "CV2", 1, 11, "I16", "IR"), (2, "Status Device", 1, 89, "UI16", "HR"),
(3, "SP", 1, 12, "UI32", "IR"), (3, "Control action type", 1, 2301, "BOOL", "DI"),
(4, "CV1", 2, 13, "I32", "IR"), (4, "S.L.Bd", 1, 528, "UI16", "HR"),
(5, "CV2", 2, 14, "I16", "IR"), (5, "VAL1", 2, 0, "I32", "IR"),
(6, "SP", 2, 15, "UI16", "IR"), (6, "VAL2", 2, 2, "I16", "IR"),
(7, "CV1", 3, 16, "UI16", "HR"), (7, "VAL3", 2, 3, "UI16", "IR"),
(8, "CV2", 3, 17, "FP32", "HR"), (8, "VAL4", 3, 4, "UI16", "HR"),
(9, "SP", 3, 15, "UI16", "IR"), (9, "VAL5", 3, 5, "FP32", "HR"),
(10, "Control action type", 3, 0, "BOOL", "DI"), (10, "VAL6", 3, 7, "UI16", "IR"),
(11, "Status ALARM1", 3, 1, "BOOL", "DO"), (11, "VAL7", 3, 0, "BOOL", "DI"),
(12, "Status ALARM2", 3, 2, "BOOL", "DO"); (12, "VAL8", 3, 1, "BOOL", "DO"),
(13, "VAL9", 3, 2, "BOOL", "DO");
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- Down -- Down
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

55
models/base.model.js Normal file
View File

@ -0,0 +1,55 @@
import db from '#root/config/database.config.js'
export class BaseModel {
/**
* @param {string} table
*/
constructor(table) {
this.table = table;
}
/**
* @param {number | string} id
* @param {string[]} keys
* @return {Promise<any>}
*/
async getById(id, keys = ['*']) {
const query = `SELECT ${keys.join(',')} FROM ${this.table} WHERE id = ?`;
const params = [id];
const res = await db.get(query, params);
return res;
}
/**
* @param {number | string} id
* @return {Promise<void>}
*/
async delById(id) {
const query = `DELETE FROM ${this.table} WHERE id = ?`;
await db.run(query, [id]);
}
/**
* @param {string[]} keys
*/
async getList(keys = ['*']) {
const query = `SELECT ${keys.join(',')} FROM ${this.table}`;
const res = await db.all(query);
return res;
}
/**
* @param {{ [key: string]: any }} data
* @returns {Promise<any>}
*/
async create(data) {
const fields = Object.keys(data);
const values = Object.values(data);
const query = `
INSERT INTO ${this.table}
(${fields.join(', ')})
VALUES (${values.map(() => '?').join(',')})`;
await db.run(query, values);
return data;
}
}

View File

@ -0,0 +1,7 @@
import { BaseModel } from './base.model.js'
export class DisplayValueModel extends BaseModel {
constructor() {
super('display_values')
}
}

View File

@ -1,79 +1,5 @@
import db from '#root/config/database.config.js' import { ModbusDeviceModel } from './modbus-device.model.js';
import { DisplayValueModel } from './display-value.model.js';
class BaseModel { export const modbusDeviceModel = new ModbusDeviceModel();
/** export const displayValueModel = new DisplayValueModel();
* @param {string} table
*/
constructor(table) {
this.table = table;
}
/**
* @param {number | string} id
* @param {string[]} keys
* @return {Promise<any>}
*/
async getById(id, keys = ['*']) {
const query = `SELECT ${keys.join(',')} FROM ${this.table} WHERE id = ?`;
const params = [id];
const res = await db.get(query, params);
return res;
}
/**
* @param {number | string} id
* @return {Promise<void>}
*/
async delById(id) {
const query = `DELETE FROM ${this.table} WHERE id = ?`;
await db.run(query, [id]);
}
async getList(keys = ['*']) {
const query = `SELECT ${keys.join(',')} FROM ${this.table}`;
const res = await db.all(query);
return res;
}
async create(fields, values) {
const query = `
INSERT INTO ${this.table}
(${fields.join(',')})
VALUES (${values.map(() => '?').join(',')})`;
await db.run(query, values);
return this;
}
}
class ModbusDeviceModel extends BaseModel {
constructor(table) {
super(table);
}
/**
* @return {Promise<import('..').ModbusDevice[]>}
*/
async getListWithDisplayValues() {
const query = `
SELECT ms.id AS id, ms.name AS name, g_display_reg_addr, g_display_reg_format, g_display_reg_type, g_y_label,
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 ${this.table} AS ms
LEFT JOIN display_values AS dv ON dv.slave_id = ms.id
GROUP BY ms.id, ms.name;`;
const devices = await db.all(query);
devices.forEach((s) => (s.display_values = JSON.parse(s.display_values)));
return devices;
}
}
export const modbusDeviceModel = new ModbusDeviceModel('modbus_slaves');

View File

@ -0,0 +1,33 @@
import { BaseModel } from './base.model.js'
import db from '#root/config/database.config.js'
export class ModbusDeviceModel extends BaseModel {
constructor() {
super('modbus_slaves');
}
/**
* @return {Promise<import('..').ModbusDevice[]>}
*/
async getListWithDisplayValues() {
const query = `
SELECT ms.id AS id, ms.name AS name, g_display_reg_addr, g_display_reg_format, g_display_reg_type, g_y_label,
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 ${this.table} AS ms
LEFT JOIN display_values AS dv ON dv.slave_id = ms.id
GROUP BY ms.id, ms.name;`;
const devices = await db.all(query);
devices.forEach((s) => (s.display_values = JSON.parse(s.display_values)));
return devices;
}
}

View File

@ -326,9 +326,10 @@
<span x-show="value?.reg_format === 'BOOL'"><input :value="Boolean(value?.data)" type="checkbox" onclick="return false;" /></span> <span x-show="value?.reg_format === 'BOOL'"><input :value="Boolean(value?.data)" type="checkbox" onclick="return false;" /></span>
<span x-show="value?.reg_format !== 'BOOL'" x-text="Number.isInteger(value?.data) ? value?.data : value?.data?.toFixed(3)"></span> <span x-show="value?.reg_format !== 'BOOL'" x-text="Number.isInteger(value?.data) ? value?.data : value?.data?.toFixed(3)"></span>
</span> </span>
<span>Reg Addr: <span x-text="value?.reg_addr"></span></span> <span>Addr: <span x-text="value?.reg_addr"></span></span>
<span>Reg Type: <span x-text="value?.reg_type"></span></span> <span>Type: <span x-text="$store.app.registersTypesMap[value?.reg_type]"></span></span>
<span>Format: <span x-text="value?.reg_format"></span></span> <span>Format: <span x-text="value?.reg_format"></span></span>
<span x-show="value.desc" x-text="value?.desc" class="text-sm text-gray-500 italic"></span>
</div> </div>
</div> </div>
</template> </template>

View File

@ -25,6 +25,12 @@ document.addEventListener("alpine:init", async () => {
}, },
currentPage: "loading", currentPage: "loading",
error: null, error: null,
registersTypesMap: {
'IR': 'Input Registers',
'HR': 'Holding Registers',
"DI": "Discrete Input",
"DO": "Coils"
},
async init() { async init() {
try { try {
const connStatusEventSource = new EventSource("/api/modbus/status"); const connStatusEventSource = new EventSource("/api/modbus/status");
@ -266,8 +272,6 @@ document.addEventListener("alpine:init", async () => {
this.error = null; this.error = null;
this.selectedDevice = device; this.selectedDevice = device;
this.resetGraph(); this.resetGraph();
// if (device.g_display_reg_addr !== null)
this.isLoading = true; this.isLoading = true;
dataStreamSource?.close(); dataStreamSource?.close();