mirror of
https://github.com/sergeyyarkov/modbus-logger.git
synced 2026-10-11 10:28:51 +03:00
refactor
This commit is contained in:
parent
582cbd285e
commit
da4cd16ade
@ -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 * as utils from '#root/utils/index.js'
|
import * as utils from '#root/utils/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";
|
||||||
|
|
||||||
export const modbusController = {
|
export const modbusController = {
|
||||||
@ -136,7 +137,7 @@ 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 device = await db.get(`SELECT id FROM "modbus_slaves" WHERE id = ?`, [req.body.id]);
|
||||||
if (!device) return res.status(404).json({ message: "Device not found." });
|
if (!device) throw new RowNotFoundError();
|
||||||
await db.run(`DELETE FROM "modbus_slaves" WHERE id = ?`, [req.body.id]);
|
await db.run(`DELETE FROM "modbus_slaves" WHERE id = ?`, [req.body.id]);
|
||||||
return res.status(200).json({ message: "Device removed." });
|
return res.status(200).json({ message: "Device removed." });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -158,7 +159,12 @@ export const modbusController = {
|
|||||||
VALUES (?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?)
|
||||||
`, [name, slave_id, reg_addr, reg_format, reg_type]
|
`, [name, slave_id, reg_addr, reg_format, reg_type]
|
||||||
);
|
);
|
||||||
return res.status(200).json({ message: 'Created.' });
|
const createdDisplayValue = await db.get(`SELECT * FROM "display_values" WHERE id = last_insert_rowid()`);
|
||||||
|
|
||||||
|
return res.status(200).json({
|
||||||
|
message: 'Created.',
|
||||||
|
data: createdDisplayValue,
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
@ -179,6 +185,30 @@ export const modbusController = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update display value
|
||||||
|
* @param {import('express').Request} req
|
||||||
|
* @param {import('express').Response} res
|
||||||
|
* @param {import('express').NextFunction} next
|
||||||
|
*/
|
||||||
|
async updateDisplayValue(req, res, next) {
|
||||||
|
try {
|
||||||
|
const { id, name, slave_id, reg_addr, reg_type, reg_format } = req.body;
|
||||||
|
const row = await db.get(`SELECT id from "display_values" WHERE id = ?`, [id]);
|
||||||
|
if (!row) throw new RowNotFoundError();
|
||||||
|
await db.run(`UPDATE "display_values" SET
|
||||||
|
name = ?,
|
||||||
|
slave_id = ?,
|
||||||
|
reg_addr = ?,
|
||||||
|
reg_type = ?,
|
||||||
|
reg_format = ?
|
||||||
|
WHERE id = ?`, [name, slave_id, reg_addr, reg_type, reg_format, id]);
|
||||||
|
return res.status(200).json({ message: 'Updated.' });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates event stream aboud modbus connection status
|
* Creates event stream aboud modbus connection status
|
||||||
* @param {import('express').Request} req
|
* @param {import('express').Request} req
|
||||||
@ -209,7 +239,7 @@ export const modbusController = {
|
|||||||
if (!slave_id) return res.status(400).json({ error: { message: "'slave_id' parameter is required." } });
|
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]);
|
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 (!device) throw new RowNotFoundError();
|
||||||
|
|
||||||
utils.setSSEHeaders(res);
|
utils.setSSEHeaders(res);
|
||||||
|
|
||||||
|
|||||||
14
errors/index.js
Normal file
14
errors/index.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export class DomainError extends Error {
|
||||||
|
constructor(message) {
|
||||||
|
super(message);
|
||||||
|
this.name = this.constructor.name;
|
||||||
|
Error.captureStackTrace(this, this.constructor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class RowNotFoundError extends DomainError {
|
||||||
|
constructor() {
|
||||||
|
super('Row not found.');
|
||||||
|
this.statusCode = 404;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,7 +9,7 @@ export function errorHandlerMiddleware() {
|
|||||||
req.log.error(error, "Something went wrong");
|
req.log.error(error, "Something went wrong");
|
||||||
const status = error.statusCode || 500;
|
const status = error.statusCode || 500;
|
||||||
const message = error.message || "Internal Server Error";
|
const message = error.message || "Internal Server Error";
|
||||||
res.status(500).json({
|
res.status(status).json({
|
||||||
error: {
|
error: {
|
||||||
status,
|
status,
|
||||||
message,
|
message,
|
||||||
|
|||||||
@ -928,6 +928,11 @@ video {
|
|||||||
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
background-color: rgb(239 68 68 / var(--tw-bg-opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bg-white {
|
||||||
|
--tw-bg-opacity: 1;
|
||||||
|
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
||||||
|
}
|
||||||
|
|
||||||
.fill-blue-600 {
|
.fill-blue-600 {
|
||||||
fill: #2563eb;
|
fill: #2563eb;
|
||||||
}
|
}
|
||||||
@ -987,8 +992,8 @@ video {
|
|||||||
padding-left: 0px;
|
padding-left: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pl-2 {
|
.pl-\[260px\] {
|
||||||
padding-left: 0.5rem;
|
padding-left: 260px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pr-6 {
|
.pr-6 {
|
||||||
@ -999,14 +1004,6 @@ video {
|
|||||||
padding-top: 1.25rem;
|
padding-top: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pl-\[250px\] {
|
|
||||||
padding-left: 250px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pl-\[260px\] {
|
|
||||||
padding-left: 260px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-left {
|
.text-left {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,7 +21,7 @@
|
|||||||
<template x-if="$store.app.currentPage === 'monitoring'">
|
<template x-if="$store.app.currentPage === 'monitoring'">
|
||||||
<main x-data="monitoringPage" class="flex-1" x-bind:hidden="$store.app.currentPage !== 'monitoring'">
|
<main x-data="monitoringPage" class="flex-1" x-bind:hidden="$store.app.currentPage !== 'monitoring'">
|
||||||
<div class="flex h-full flex-row space-x-2">
|
<div class="flex h-full flex-row space-x-2">
|
||||||
<div class="border-r fixed h-screen min-w-[256px] p-2.5 pl-0 pt-5">
|
<div class="border-r fixed bg-white h-screen min-w-[256px] p-2.5 pl-0 pt-5">
|
||||||
<div class="flex flex-col space-y-1">
|
<div class="flex flex-col space-y-1">
|
||||||
<!-- List of devices -->
|
<!-- List of devices -->
|
||||||
<span class="italic text-sm">Devices:</span>
|
<span class="italic text-sm">Devices:</span>
|
||||||
@ -290,7 +290,12 @@
|
|||||||
<h3 x-text="value.name" class="text-lg font-bold"></h3>
|
<h3 x-text="value.name" class="text-lg font-bold"></h3>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<!-- Edit display value -->
|
<!-- Edit display value -->
|
||||||
<button>
|
<button @click="$dispatch('open-display-value-modal', {
|
||||||
|
labels: { title: `Edit ${value.name}`, submit: 'Edit' },
|
||||||
|
actionType: 'EDIT',
|
||||||
|
selectedDevice,
|
||||||
|
displayValue: value
|
||||||
|
})">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||||
<path
|
<path
|
||||||
fill="#eab308"
|
fill="#eab308"
|
||||||
@ -330,23 +335,27 @@
|
|||||||
</template>
|
</template>
|
||||||
<!-- Create Display Value -->
|
<!-- Create Display Value -->
|
||||||
<button
|
<button
|
||||||
@click="$dispatch('open-create-display-value-modal')"
|
@click="$dispatch('open-display-value-modal', {
|
||||||
|
labels: { title: 'Create new display value', submit: 'Create' },
|
||||||
|
actionType: 'CREATE',
|
||||||
|
selectedDevice,
|
||||||
|
})"
|
||||||
class="flex justify-center min-h-[140px] items-center border-4 text-center border-dashed p-2.5 rounded-sm cursor-pointer hover:bg-gray-50"
|
class="flex justify-center min-h-[140px] items-center border-4 text-center border-dashed p-2.5 rounded-sm cursor-pointer hover:bg-gray-50"
|
||||||
>
|
>
|
||||||
<span class="text-5xl text-gray-400 font-bold select-none">+</span>
|
<span class="text-5xl text-gray-400 font-bold select-none">+</span>
|
||||||
</button>
|
</button>
|
||||||
<!-- MODAL: Create new display value -->
|
<!-- MODAL: Display value -->
|
||||||
<template x-teleport="body">
|
<template x-teleport="body">
|
||||||
<div
|
<div
|
||||||
x-data="displayValueModal"
|
x-data="displayValueModal"
|
||||||
@open-create-display-value-modal.window="open()"
|
@open-display-value-modal.window="open($event)"
|
||||||
@keyup.escape.window="close()"
|
@keyup.escape.window="close()"
|
||||||
class="backdrop z-50"
|
class="backdrop z-50"
|
||||||
x-show="isOpen"
|
x-show="isOpen"
|
||||||
>
|
>
|
||||||
<div class="modal">
|
<div class="modal">
|
||||||
<div class="flex justify-between items-center">
|
<div class="flex justify-between items-center">
|
||||||
<h2 class="font-bold text-lg">Create new display value</h2>
|
<h2 x-text="labels.title" class="font-bold text-lg"></h2>
|
||||||
<button @click="close()" aria-label="Close">
|
<button @click="close()" aria-label="Close">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||||
<path
|
<path
|
||||||
@ -357,7 +366,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr />
|
||||||
<form @submit.prevent="create(selectedDevice)" action="#">
|
<form @submit.prevent="submit" action="#">
|
||||||
<div
|
<div
|
||||||
x-id="['name', 'reg_addr', 'reg_type', 'reg_format']"
|
x-id="['name', 'reg_addr', 'reg_type', 'reg_format']"
|
||||||
class="flex flex-col space-y-2"
|
class="flex flex-col space-y-2"
|
||||||
@ -424,10 +433,10 @@
|
|||||||
<button
|
<button
|
||||||
x-bind:disabled="isLoading"
|
x-bind:disabled="isLoading"
|
||||||
x-bind:class="isLoading && 'opacity-70'"
|
x-bind:class="isLoading && 'opacity-70'"
|
||||||
|
x-text="labels.submit"
|
||||||
type="submit"
|
type="submit"
|
||||||
class="btn"
|
class="btn"
|
||||||
>
|
>
|
||||||
Create
|
|
||||||
</button>
|
</button>
|
||||||
<button @click="close()" class="btn bg-red-500">Cancel</button>
|
<button @click="close()" class="btn bg-red-500">Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -172,23 +172,44 @@ document.addEventListener("alpine:init", async () => {
|
|||||||
reg_type: 'IR',
|
reg_type: 'IR',
|
||||||
reg_format: 'UI16'
|
reg_format: 'UI16'
|
||||||
},
|
},
|
||||||
|
labels: {
|
||||||
|
title: '',
|
||||||
|
submit: '',
|
||||||
|
},
|
||||||
|
selectedDevice: null,
|
||||||
resetDataFields() {
|
resetDataFields() {
|
||||||
this.data.name = '';
|
this.data.name = '';
|
||||||
this.data.reg_addr = 0;
|
this.data.reg_addr = 0;
|
||||||
this.data.reg_type = 'IR';
|
this.data.reg_type = 'IR';
|
||||||
this.data.reg_format = 'UI16';
|
this.data.reg_format = 'UI16';
|
||||||
},
|
},
|
||||||
async create(selectedDevice) {
|
async create() {
|
||||||
try {
|
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
const data = {
|
const data = { ...this.data, slave_id: this.selectedDevice.id };
|
||||||
...this.data,
|
const result = await api.post('/modbus/create_display-value', data);
|
||||||
slave_id: selectedDevice.id
|
this.selectedDevice?.display_values?.push(result.data);
|
||||||
}
|
|
||||||
await api.post('/modbus/create_display-value', data);
|
|
||||||
selectedDevice?.display_values?.push(this.data);
|
|
||||||
this.resetDataFields();
|
this.resetDataFields();
|
||||||
this.close();
|
this.close();
|
||||||
|
},
|
||||||
|
async update() {
|
||||||
|
const data = { ...this.data, slave_id: this.selectedDevice.id };
|
||||||
|
await api.post('/modbus/update_display-value', data);
|
||||||
|
this.resetDataFields();
|
||||||
|
this.close();
|
||||||
|
},
|
||||||
|
async submit() {
|
||||||
|
try {
|
||||||
|
this.isLoading = true;
|
||||||
|
switch (this.actionType) {
|
||||||
|
case 'CREATE':
|
||||||
|
await this.create();
|
||||||
|
break;
|
||||||
|
case 'EDIT':
|
||||||
|
await this.update();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.error = error;
|
this.error = error;
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -196,8 +217,19 @@ document.addEventListener("alpine:init", async () => {
|
|||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
open() {
|
open(event) {
|
||||||
|
const { labels, actionType, selectedDevice, displayValue } = event.detail
|
||||||
this.isOpen = true;
|
this.isOpen = true;
|
||||||
|
this.labels = { title: labels.title, submit: labels.submit };
|
||||||
|
this.actionType = actionType;
|
||||||
|
this.selectedDevice = selectedDevice;
|
||||||
|
if (displayValue) {
|
||||||
|
this.data.id = displayValue.id;
|
||||||
|
this.data.name = displayValue.name;
|
||||||
|
this.data.reg_addr = displayValue.reg_addr;
|
||||||
|
this.data.reg_format = displayValue.reg_format;
|
||||||
|
this.data.reg_type = displayValue.reg_type;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
close() {
|
close() {
|
||||||
this.isOpen = false;
|
this.isOpen = false;
|
||||||
|
|||||||
@ -6,13 +6,18 @@ import { validateBodyMiddleware } from "#root/middlewares/index.js";
|
|||||||
const modbusRouter = express.Router();
|
const modbusRouter = express.Router();
|
||||||
|
|
||||||
modbusRouter.get("/devices", modbusController.list);
|
modbusRouter.get("/devices", modbusController.list);
|
||||||
|
|
||||||
modbusRouter.post("/create_device", validateBodyMiddleware(modbusDeviceSchema), modbusController.createDevice)
|
modbusRouter.post("/create_device", validateBodyMiddleware(modbusDeviceSchema), modbusController.createDevice)
|
||||||
modbusRouter.post('/remove_device', validateBodyMiddleware(removeModbusDeviceSchema), modbusController.removeDevice);
|
modbusRouter.post('/remove_device', validateBodyMiddleware(removeModbusDeviceSchema), modbusController.removeDevice);
|
||||||
modbusRouter.post('/update_device', validateBodyMiddleware(modbusDeviceSchema), modbusController.updateDevice);
|
modbusRouter.post('/update_device', validateBodyMiddleware(modbusDeviceSchema), modbusController.updateDevice);
|
||||||
|
|
||||||
modbusRouter.post('/create_display-value', validateBodyMiddleware(createDisplayValueSchema), modbusController.createDisplayValue);
|
modbusRouter.post('/create_display-value', validateBodyMiddleware(createDisplayValueSchema), modbusController.createDisplayValue);
|
||||||
|
modbusRouter.post('/update_display-value', validateBodyMiddleware(displayValueSchema), modbusController.updateDisplayValue)
|
||||||
modbusRouter.post('/remove_display-value', validateBodyMiddleware(removeDisplayValueSchema), modbusController.removeDisplayValue);
|
modbusRouter.post('/remove_display-value', validateBodyMiddleware(removeDisplayValueSchema), modbusController.removeDisplayValue);
|
||||||
|
|
||||||
modbusRouter.post("/connect", modbusController.connect);
|
modbusRouter.post("/connect", modbusController.connect);
|
||||||
modbusRouter.post("/close", modbusController.close);
|
modbusRouter.post("/close", modbusController.close);
|
||||||
|
|
||||||
modbusRouter.get("/data_stream", modbusController.streamData);
|
modbusRouter.get("/data_stream", modbusController.streamData);
|
||||||
modbusRouter.get('/status', modbusController.status);
|
modbusRouter.get('/status', modbusController.status);
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,4 @@ export const displayValueSchema = object({
|
|||||||
})
|
})
|
||||||
|
|
||||||
export const removeDisplayValueSchema = displayValueSchema.pick(['id']);
|
export const removeDisplayValueSchema = displayValueSchema.pick(['id']);
|
||||||
|
|
||||||
// TODO: "reg_format" can be BOOL if "reg_type" is "DI" or "DO"
|
|
||||||
export const createDisplayValueSchema = displayValueSchema.omit(['id'])
|
export const createDisplayValueSchema = displayValueSchema.omit(['id'])
|
||||||
Loading…
x
Reference in New Issue
Block a user