This commit is contained in:
Sergey Yarkov 2023-10-26 23:03:09 +03:00
parent 8932402d3b
commit 0e10984830
No known key found for this signature in database
GPG Key ID: E91CE1FABC619B6F
4 changed files with 80 additions and 51 deletions

BIN
data.db

Binary file not shown.

View File

@ -39,6 +39,21 @@ CREATE TABLE IF NOT EXISTS "display_values" (
FOREIGN KEY("slave_id") REFERENCES modbus_slaves("id") ON DELETE CASCADE
);
-- CREATE TABLE IF NOT EXISTS "modbus_log" (
-- "id" INTEGER,
-- "date" DATE NOT NULL,
-- "slave_id" INTEGER NOT NULL,
-- -- JSON Format:
-- -- {
-- -- graph: [{ reg_addr: 1 value: 4 }],
-- -- display_values: [{ reg_addr: 1, name: "CV1" }]
-- -- }
-- "data" TEXT NOT NULL,
-- PRIMARY KEY("id"),
-- FOREIGN KEY("slave_id") REFERENCES modbus_slaves("id") ON DELETE CASCADE
-- )
INSERT INTO "modbus_slaves" (
"id", "name", "g_display_reg_addr", "g_display_reg_format", "g_y_label", "is_logging"
) VALUES

View File

@ -28,30 +28,35 @@
<div class="border-r min-w-[256px] p-2.5 pt-5">
<div class="flex flex-col space-y-1">
<span class="italic text-sm">Devices:</span>
<span x-show="devices.length === 0" class="text-gray-500 text-sm text-center block">No devices added.</span>
<span x-show="devices.length === 0" class="text-gray-500 text-sm text-center block"
>No devices added.</span
>
<div class="flex flex-col max-h-[520px] overflow-y-auto space-y-2">
<template x-for="device in devices">
<button @click="selectDevice(device)" :class="{'bg-gray-100': selectedDevice?.id === device.id}" class="w-full select-none border px-2.5 py-3 text-left rounded-sm hover:bg-gray-100 cursor-pointer">
<button
@click="selectDevice(device)"
:class="{'bg-gray-100': selectedDevice?.id === device.id}"
class="w-full select-none border px-2.5 py-3 text-left rounded-sm hover:bg-gray-100 cursor-pointer"
>
<span x-text="device.name" class="text-md font-bold"></span>
</button>
</template>
</div>
<button class="w-full text-center border-2 border-dashed rounded-sm hover:bg-gray-50 cursor-pointer text-left">
<button class="w-full text-center border-2 border-dashed rounded-sm hover:bg-gray-50 cursor-pointer">
<span class="text-4xl text-gray-400 select-none">+</span>
</button>
</div>
<hr class="my-4" />
<div>
<span class="italic text-sm">Connection:</span>
<div class="flex flex-col mt-1.5">
<span class="text-sm">Status:
<b x-show="$store.app.is_opened" class="text-green-700">Opened</b>
<b x-show="!$store.app.is_opened" class="text-red-700">Closed</b>
</span>
<a class="text-sm" @click="$store.app.setPage('configuration')" href="#">
Reconfigurate
</a>
</div>
<span class="italic text-sm">Connection:</span>
<div class="flex flex-col mt-1.5">
<span class="text-sm"
>Status:
<b x-show="$store.app.is_opened" class="text-green-700">Opened</b>
<b x-show="!$store.app.is_opened" class="text-red-700">Closed</b>
</span>
<a class="text-sm" @click="$store.app.setPage('configuration')" href="#"> Reconfigurate </a>
</div>
</div>
</div>
<div class="flex-auto pl-2">
@ -60,7 +65,12 @@
<h2 x-text="selectedDevice?.name" class="font-bold text-3xl my-2"></h2>
<div>
<a href="#">Edit</a>
<a @click="$store.confirmDialog.open('Remove device', `Delete ${selectedDevice.name}? All logs will be cleared.`, removeDevice, selectedDevice.id)" class="text-red-500" href="#">Delete</a>
<a
@click="$store.confirmDialog.open('Remove device', `Delete ${selectedDevice.name}? All logs will be cleared.`, removeDevice, selectedDevice.id)"
class="text-red-500"
href="#"
>Delete</a
>
</div>
</div>
<div class="mt-4">
@ -285,15 +295,25 @@
</main>
</template>
<!-- Confirm dialog -->
<div x-show="$store.confirmDialog.isOpen" class="backdrop z-50">
<div role="dialog" @click.away="$store.confirmDialog.close()" class="px-4 py-3.5 ml-auto mr-auto mt-20 bg-white max-w-md border rounded-sm">
<div
role="dialog"
@click.away="$store.confirmDialog.close()"
class="px-4 py-3.5 ml-auto mr-auto mt-20 bg-white max-w-md border rounded-sm"
>
<div class="flex justify-between items-center">
<h2 x-text="$store.confirmDialog.labels.title" class="font-bold text-lg"></h2>
<button @click="$store.confirmDialog.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>
<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>
<hr />
<p x-text="$store.confirmDialog.labels.body"></p>
<div class="mt-4 flex justify-end space-x-2">
<button @click="$store.confirmDialog.confirm()" class="btn">Confirm</button>

View File

@ -4,13 +4,7 @@ import api from "./api.js";
import * as utils from "./utils/index.js";
const pages = ["loading", "monitoring", "configuration", "404"];
let graphData = [];
const t = new Date();
for (var i = 10; i >= 0; i--) {
var x = new Date(t.getTime() - i * 1000);
graphData.push([x, Math.random()]);
}
let graphData = [[0, 0]];
document.addEventListener("alpine:init", async () => {
Alpine.store("app", {
@ -31,7 +25,7 @@ document.addEventListener("alpine:init", async () => {
error: null,
async init() {
try {
const connStatusEventSource = new EventSource('/api/modbus/status');
const connStatusEventSource = new EventSource("/api/modbus/status");
const { config } = await api.get("/app/config");
const { is_configured } = await api.get("/app/is_configured");
@ -39,7 +33,7 @@ document.addEventListener("alpine:init", async () => {
if (config) this.config = config;
if (is_configured) {
connStatusEventSource.onmessage = (e) => (this.is_opened = e.data === 'true');
connStatusEventSource.onmessage = (e) => (this.is_opened = e.data === "true");
await utils.wait(500);
this.currentPage = "monitoring";
return;
@ -63,28 +57,28 @@ document.addEventListener("alpine:init", async () => {
},
});
Alpine.data('monitoringPage', () => ({
Alpine.data("monitoringPage", () => ({
selectedDevice: null,
devices: [],
async init() {
try {
this.$store.app.currentPage = 'loading'
this.$store.app.currentPage = "loading";
var g = new Dygraph(document.getElementById("div_g"), graphData, {
drawPoints: false,
showRoller: false,
ylabel: 'Y Label',
ylabel: "Y Label",
valueRange: [0.0, 1.8],
//labels: ["Time", "Random"],
});
this.devices = await api.get('/modbus/devices');
this.devices = await api.get("/modbus/devices");
window.intervalId = setInterval(() => {
graphData.push([new Date(), Math.random()]);
g.updateOptions({ file: graphData })
}, 500)
g.updateOptions({ file: graphData });
}, 500);
} catch (error) {
this.$store.app.error = error;
} finally {
this.$store.app.currentPage = 'monitoring'
this.$store.app.currentPage = "monitoring";
}
},
selectDevice(device) {
@ -95,13 +89,13 @@ document.addEventListener("alpine:init", async () => {
async removeDevice(id, ctx) {
try {
//await api.post('/modbus/remove_device', { id });
this.devices = _this.devices.filter(d => d.id != id);
this.devices = _this.devices.filter((d) => d.id != id);
this.selectedDevice = null;
} catch (error) {
console.error('Error while removing device.', error)
console.error("Error while removing device.", error);
}
}
}))
},
}));
Alpine.data("configAppPage", () => ({
selectedConnectionType: "TCP",
@ -137,12 +131,12 @@ document.addEventListener("alpine:init", async () => {
Alpine.store("confirmDialog", {
isOpen: false,
labels: {
title: 'Confirm dialog',
body: 'Are you sure you want to perform this action?',
title: "Confirm dialog",
body: "Are you sure you want to perform this action?",
},
cb: {
fn: null,
args: []
args: [],
},
open(title, body, cb, ...args) {
this.isOpen = true;
@ -156,6 +150,6 @@ document.addEventListener("alpine:init", async () => {
},
close() {
this.isOpen = false;
}
})
},
});
});