This commit is contained in:
Sergey Yarkov 2023-11-03 15:31:11 +03:00
parent 8f1404c299
commit 3dd5ce81c9
8 changed files with 186 additions and 22 deletions

View File

@ -5,5 +5,6 @@ import { open } from "sqlite";
const sqlite = process.env.NODE_ENV == "development" ? sqlite3.verbose() : sqlite3; const sqlite = process.env.NODE_ENV == "development" ? sqlite3.verbose() : sqlite3;
const db = await open({ filename: "./data.db", driver: sqlite.Database }); const db = await open({ filename: "./data.db", driver: sqlite.Database });
await db.run(`PRAGMA foreign_keys = ON`);
export default db; export default db;

View File

@ -116,7 +116,19 @@ export const modbusController = {
* @param {import('express').Response} res * @param {import('express').Response} res
* @param {import('express').NextFunction} next * @param {import('express').NextFunction} next
*/ */
async createDisplayValue(req, res, next) {}, async createDisplayValue(req, res, next) {
try {
const { name, slave_id, reg_addr, reg_format, reg_type } = req.body
await db.run(`INSERT INTO "display_values"
("name", "slave_id", "reg_addr", "reg_format", "reg_type")
VALUES (?, ?, ?, ?, ?)
`, [name, slave_id, reg_addr, reg_format, reg_type]
);
return res.status(200).json({ message: 'Created.' });
} catch (error) {
next(error);
}
},
/** /**
* Remove display value from modbus slave device * Remove display value from modbus slave device
@ -133,14 +145,6 @@ export const modbusController = {
} }
}, },
/**
* Get latest data log from modbus slave device
* @param {import('express').Request} req
* @param {import('express').Response} res
* @param {import('express').NextFunction} next
*/
async getLatestLog(req, res, next) {},
/** /**
* Creates event stream aboud modbus connection status * Creates event stream aboud modbus connection status
* @param {import('express').Request} req * @param {import('express').Request} req

BIN
data.db

Binary file not shown.

View File

@ -610,6 +610,10 @@ video {
visibility: collapse; visibility: collapse;
} }
.fixed {
position: fixed;
}
.absolute { .absolute {
position: absolute; position: absolute;
} }
@ -995,6 +999,14 @@ 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;
} }

View File

@ -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 min-w-[256px] p-2.5 pl-0 pt-5"> <div class="border-r fixed 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>
@ -209,7 +209,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="flex-auto pl-2"> <div class="flex-auto pl-[260px]">
<div x-show="selectedDevice != null"> <div x-show="selectedDevice != null">
<div class="pt-5 border-b flex flex-wrap justify-between items-center pr-6"> <div class="pt-5 border-b flex flex-wrap justify-between items-center pr-6">
<div class="mb-2"> <div class="mb-2">
@ -301,11 +301,11 @@
<!-- Remove display value --> <!-- Remove display value -->
<button <button
@click="$dispatch('dialog', { @click="$dispatch('dialog', {
title: 'Remove display value', title: 'Remove display value',
body: `Remove display value ${value.name}?`, body: `Remove display value ${value.name}?`,
isOpen: true, isOpen: true,
onConfirm: async () => await removeDisplayValue(value.id) onConfirm: async () => await removeDisplayValue(value.id)
});" });"
> >
<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
@ -328,13 +328,113 @@
</div> </div>
</div> </div>
</template> </template>
<!-- Create Display --> <!-- Create Display Value -->
<div <button
@click="$dispatch('open-create-display-value-modal')" @click="$dispatch('open-create-display-value-modal')"
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>
</div> </button>
<!-- MODAL: Create new display value -->
<template x-teleport="body">
<div
x-data="displayValueModal"
@open-create-display-value-modal.window="open()"
@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>
<button @click="close()" aria-label="Close">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M6.4 19L5 17.6l5.6-5.6L5 6.4L6.4 5l5.6 5.6L17.6 5L19 6.4L13.4 12l5.6 5.6l-1.4 1.4l-5.6-5.6L6.4 19Z"
/>
</svg>
</button>
</div>
<hr />
<form @submit.prevent="create(selectedDevice)" action="#">
<div
x-id="['name', 'reg_addr', 'reg_type', 'reg_format']"
class="flex flex-col space-y-2"
>
<div>
<label :for="$id('name')"><span class="text-red-500">*</span>Name:</label>
<input
x-model="data.name"
:id="$id('name')"
name="name"
placeholder="Value Name"
type="text"
required
/>
</div>
<div>
<label :for="$id('reg_addr')"><span class="text-red-500">*</span>Register Address:</label>
<input
x-model.number="data.reg_addr"
:id="$id('reg_addr')"
name="reg_addr"
placeholder="0"
type="number"
min="0"
max="65534"
required
/>
</div>
<div>
<label :for="$id('reg_type')"><span class="text-red-500">*</span>Registers Type:</label>
<select
x-model="data.reg_type"
:id="$id('reg_type')"
name="reg_type"
required
>
<option value="IR">Input Registers</option>
<option value="HR">Holding Registers</option>
<option value="DI">Discrete Inputs</option>
<option value="DO">Coils</option>
</select>
</div>
<div>
<label :for="$id('reg_format')"><span class="text-red-500">*</span>Format (type):</label>
<select
x-model="data.reg_format"
:id="$id('reg_format')"
name="reg_format"
required
>
<option value="UI16">Unsigned Int 16</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>
<option value="BOOL">Boolean</option>
</select>
</div>
</div>
<div>
<span x-text="error?.message" class="text-red-500"></span>
</div>
<div class="mt-4 flex justify-end space-x-2">
<button
x-bind:disabled="isLoading"
x-bind:class="isLoading && 'opacity-70'"
type="submit"
class="btn"
>
Create
</button>
<button @click="close()" class="btn bg-red-500">Cancel</button>
</div>
</form>
</div>
</div>
</template>
</div> </div>
</div> </div>
</div> </div>

View File

@ -107,6 +107,49 @@ document.addEventListener("alpine:init", async () => {
}, },
})); }));
Alpine.data('displayValueModal', () => ({
isOpen: false,
isLoading: false,
error: null,
data: {
name: '',
reg_addr: 0,
reg_type: 'IR',
reg_format: 'UI16'
},
resetDataFields() {
this.data.name = '';
this.data.reg_addr = 0;
this.data.reg_type = 'IR';
this.data.reg_format = 'UI16';
},
async create(selectedDevice) {
try {
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);
this.resetDataFields();
this.close();
} catch (error) {
this.error = error;
console.error(error);
} finally {
this.isLoading = false;
}
},
open() {
this.isOpen = true;
},
close() {
this.isOpen = false;
this.resetDataFields();
},
}))
Alpine.data("monitoringPage", () => ({ Alpine.data("monitoringPage", () => ({
selectedDevice: null, selectedDevice: null,
devices: [], devices: [],

View File

@ -1,6 +1,6 @@
import express from "express"; import express from "express";
import { modbusController } from "#root/controllers/index.js" import { modbusController } from "#root/controllers/index.js"
import { modbusDeviceSchema, removeDisplayValueSchema, removeModbusDeviceSchema } from "#root/validators/index.js"; import { createDisplayValueSchema, displayValueSchema, modbusDeviceSchema, removeDisplayValueSchema, removeModbusDeviceSchema } from "#root/validators/index.js";
import { validateBodyMiddleware } from "#root/middlewares/index.js"; import { validateBodyMiddleware } from "#root/middlewares/index.js";
const modbusRouter = express.Router(); const modbusRouter = express.Router();
@ -9,6 +9,7 @@ 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('/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);

View File

@ -1,4 +1,4 @@
import { object, number, boolean, string } from 'yup'; import { object, number, string } from 'yup';
export const displayValueSchema = object({ export const displayValueSchema = object({
id: number().required(), id: number().required(),
@ -10,3 +10,6 @@ export const displayValueSchema = object({
}) })
export const removeDisplayValueSchema = displayValueSchema.pick(['id']); export const removeDisplayValueSchema = displayValueSchema.pick(['id']);
// TODO: On creating "reg_format" can be BOOL if "reg_type" is "DI" or "DO"
export const createDisplayValueSchema = displayValueSchema.omit(['id'])