mirror of
https://github.com/sergeyyarkov/modbus-logger.git
synced 2026-10-11 02:18: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 modbusClient from "#root/config/modbus-client.config.js";
|
||||
import * as utils from '#root/utils/index.js'
|
||||
import { RowNotFoundError } from '#root/errors/index.js'
|
||||
import { appService, modbusService } from "#root/services/index.js";
|
||||
|
||||
export const modbusController = {
|
||||
@ -136,7 +137,7 @@ export const modbusController = {
|
||||
async removeDevice(req, res, next) {
|
||||
try {
|
||||
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]);
|
||||
return res.status(200).json({ message: "Device removed." });
|
||||
} catch (error) {
|
||||
@ -158,7 +159,12 @@ export const modbusController = {
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
`, [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) {
|
||||
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
|
||||
* @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." } });
|
||||
|
||||
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);
|
||||
|
||||
|
||||
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");
|
||||
const status = error.statusCode || 500;
|
||||
const message = error.message || "Internal Server Error";
|
||||
res.status(500).json({
|
||||
res.status(status).json({
|
||||
error: {
|
||||
status,
|
||||
message,
|
||||
|
||||
@ -928,6 +928,11 @@ video {
|
||||
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: #2563eb;
|
||||
}
|
||||
@ -987,8 +992,8 @@ video {
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
.pl-2 {
|
||||
padding-left: 0.5rem;
|
||||
.pl-\[260px\] {
|
||||
padding-left: 260px;
|
||||
}
|
||||
|
||||
.pr-6 {
|
||||
@ -999,14 +1004,6 @@ video {
|
||||
padding-top: 1.25rem;
|
||||
}
|
||||
|
||||
.pl-\[250px\] {
|
||||
padding-left: 250px;
|
||||
}
|
||||
|
||||
.pl-\[260px\] {
|
||||
padding-left: 260px;
|
||||
}
|
||||
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
<template x-if="$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="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">
|
||||
<!-- List of devices -->
|
||||
<span class="italic text-sm">Devices:</span>
|
||||
@ -290,7 +290,12 @@
|
||||
<h3 x-text="value.name" class="text-lg font-bold"></h3>
|
||||
<div class="flex">
|
||||
<!-- 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">
|
||||
<path
|
||||
fill="#eab308"
|
||||
@ -330,23 +335,27 @@
|
||||
</template>
|
||||
<!-- Create Display Value -->
|
||||
<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"
|
||||
>
|
||||
<span class="text-5xl text-gray-400 font-bold select-none">+</span>
|
||||
</button>
|
||||
<!-- MODAL: Create new display value -->
|
||||
<!-- MODAL: Display value -->
|
||||
<template x-teleport="body">
|
||||
<div
|
||||
x-data="displayValueModal"
|
||||
@open-create-display-value-modal.window="open()"
|
||||
@open-display-value-modal.window="open($event)"
|
||||
@keyup.escape.window="close()"
|
||||
class="backdrop z-50"
|
||||
x-show="isOpen"
|
||||
>
|
||||
<div class="modal">
|
||||
<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">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path
|
||||
@ -357,7 +366,7 @@
|
||||
</button>
|
||||
</div>
|
||||
<hr />
|
||||
<form @submit.prevent="create(selectedDevice)" action="#">
|
||||
<form @submit.prevent="submit" action="#">
|
||||
<div
|
||||
x-id="['name', 'reg_addr', 'reg_type', 'reg_format']"
|
||||
class="flex flex-col space-y-2"
|
||||
@ -424,10 +433,10 @@
|
||||
<button
|
||||
x-bind:disabled="isLoading"
|
||||
x-bind:class="isLoading && 'opacity-70'"
|
||||
x-text="labels.submit"
|
||||
type="submit"
|
||||
class="btn"
|
||||
>
|
||||
Create
|
||||
</button>
|
||||
<button @click="close()" class="btn bg-red-500">Cancel</button>
|
||||
</div>
|
||||
|
||||
@ -172,23 +172,44 @@ document.addEventListener("alpine:init", async () => {
|
||||
reg_type: 'IR',
|
||||
reg_format: 'UI16'
|
||||
},
|
||||
labels: {
|
||||
title: '',
|
||||
submit: '',
|
||||
},
|
||||
selectedDevice: null,
|
||||
resetDataFields() {
|
||||
this.data.name = '';
|
||||
this.data.reg_addr = 0;
|
||||
this.data.reg_type = 'IR';
|
||||
this.data.reg_format = 'UI16';
|
||||
},
|
||||
async create(selectedDevice) {
|
||||
try {
|
||||
async create() {
|
||||
this.isLoading = true;
|
||||
const data = {
|
||||
...this.data,
|
||||
slave_id: selectedDevice.id
|
||||
}
|
||||
await api.post('/modbus/create_display-value', data);
|
||||
selectedDevice?.display_values?.push(this.data);
|
||||
const data = { ...this.data, slave_id: this.selectedDevice.id };
|
||||
const result = await api.post('/modbus/create_display-value', data);
|
||||
this.selectedDevice?.display_values?.push(result.data);
|
||||
this.resetDataFields();
|
||||
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) {
|
||||
this.error = error;
|
||||
console.error(error);
|
||||
@ -196,8 +217,19 @@ document.addEventListener("alpine:init", async () => {
|
||||
this.isLoading = false;
|
||||
}
|
||||
},
|
||||
open() {
|
||||
open(event) {
|
||||
const { labels, actionType, selectedDevice, displayValue } = event.detail
|
||||
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() {
|
||||
this.isOpen = false;
|
||||
|
||||
@ -6,13 +6,18 @@ import { validateBodyMiddleware } from "#root/middlewares/index.js";
|
||||
const modbusRouter = express.Router();
|
||||
|
||||
modbusRouter.get("/devices", modbusController.list);
|
||||
|
||||
modbusRouter.post("/create_device", validateBodyMiddleware(modbusDeviceSchema), modbusController.createDevice)
|
||||
modbusRouter.post('/remove_device', validateBodyMiddleware(removeModbusDeviceSchema), modbusController.removeDevice);
|
||||
modbusRouter.post('/update_device', validateBodyMiddleware(modbusDeviceSchema), modbusController.updateDevice);
|
||||
|
||||
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("/connect", modbusController.connect);
|
||||
modbusRouter.post("/close", modbusController.close);
|
||||
|
||||
modbusRouter.get("/data_stream", modbusController.streamData);
|
||||
modbusRouter.get('/status', modbusController.status);
|
||||
|
||||
|
||||
@ -10,6 +10,4 @@ export const displayValueSchema = object({
|
||||
})
|
||||
|
||||
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'])
|
||||
Loading…
x
Reference in New Issue
Block a user