mirror of
https://github.com/sergeyyarkov/modbus-logger.git
synced 2026-10-11 18:38:51 +03:00
396 lines
11 KiB
JavaScript
396 lines
11 KiB
JavaScript
// @ts-nocheck
|
|
import "./alpine.js";
|
|
import api from "./api.js";
|
|
import * as utils from "./utils/index.js";
|
|
|
|
var pages = ["loading", "monitoring", "configuration", "404"];
|
|
var dataStreamSource;
|
|
var graphData = [];
|
|
var g = null;
|
|
|
|
document.addEventListener("alpine:init", async () => {
|
|
Alpine.store("app", {
|
|
is_configured: false,
|
|
is_opened: false,
|
|
config: {
|
|
mb_connection_type: "TCP",
|
|
mb_tcp_ip: "127.0.0.1",
|
|
mb_tcp_port: 502,
|
|
mb_rtu_path: "",
|
|
mb_rtu_baud: 9600,
|
|
mb_rtu_data_bits: 8,
|
|
mb_rtu_stop_bits: 1,
|
|
mb_rtu_parity: "even",
|
|
log_interval_ms: 1000,
|
|
},
|
|
currentPage: "loading",
|
|
error: null,
|
|
registersTypesMap: {
|
|
'IR': 'Input Registers',
|
|
'HR': 'Holding Registers',
|
|
"DI": "Discrete Input",
|
|
"DO": "Coils"
|
|
},
|
|
async init() {
|
|
try {
|
|
const connStatusEventSource = new EventSource("/api/modbus/status");
|
|
const { config } = await api.get("/app/config");
|
|
const { is_configured } = await api.get("/app/is_configured");
|
|
|
|
this.is_configured = is_configured;
|
|
connStatusEventSource.onmessage = (e) => (this.is_opened = e.data === "true");
|
|
|
|
if (config) this.config = config;
|
|
if (is_configured) {
|
|
await utils.wait(500);
|
|
this.currentPage = "monitoring";
|
|
return;
|
|
}
|
|
|
|
this.currentPage = "configuration";
|
|
} catch (error) {
|
|
console.error(error);
|
|
this.currentPage = "error";
|
|
this.error = error;
|
|
}
|
|
},
|
|
|
|
setPage(page) {
|
|
if (!pages.includes(page)) {
|
|
this.currentPage = "404";
|
|
return;
|
|
}
|
|
this.currentPage = page;
|
|
},
|
|
});
|
|
|
|
Alpine.data("deviceModal", () => ({
|
|
isOpen: false,
|
|
isLoading: false,
|
|
error: null,
|
|
graphView: "",
|
|
data: {
|
|
oldId: null,
|
|
id: null,
|
|
name: "",
|
|
g_display_reg_addr: null,
|
|
g_display_reg_format: "UI16",
|
|
g_display_reg_type: "IR",
|
|
g_y_label: "",
|
|
},
|
|
labels: {
|
|
title: '',
|
|
submit: '',
|
|
cancel: ''
|
|
},
|
|
actionType: 'CREATE',
|
|
resetDataFields() {
|
|
this.data.id = null;
|
|
this.data.name = "";
|
|
this.data.g_display_reg_addr = null;
|
|
this.data.g_display_reg_format = "UI16";
|
|
this.g_display_reg_type = "IR";
|
|
this.data.g_y_label = "";
|
|
},
|
|
async create(cb) {
|
|
try {
|
|
this.isLoading = true;
|
|
this.error = null;
|
|
await api.post("/modbus/create_device", utils.dellNullableKeys({ ...this.data }));
|
|
this.$dispatch("add-device", { ...this.data, display_values: [] }); // add device to state
|
|
this.close();
|
|
cb && cb();
|
|
} catch (error) {
|
|
this.error = error;
|
|
console.error(error);
|
|
} finally {
|
|
this.isLoading = false;
|
|
}
|
|
},
|
|
async edit() {
|
|
try {
|
|
this.isLoading = true;
|
|
this.error = null;
|
|
if (this.graphView !== 'new-value') {
|
|
this.data.g_display_reg_addr = null;
|
|
this.data.g_display_reg_format = null;
|
|
this.data.g_y_label = null;
|
|
this.data.g_display_reg_type = null;
|
|
}
|
|
await api.post('/modbus/update_device', utils.dellNullableKeys({ ...this.data, id: this.data.oldId, newId: this.data.id }));
|
|
window.location.reload();
|
|
} catch (error) {
|
|
this.error = error;
|
|
console.error(error);
|
|
} finally {
|
|
this.isLoading = false;
|
|
}
|
|
},
|
|
async submit() {
|
|
switch (this.actionType) {
|
|
case 'CREATE':
|
|
await this.create()
|
|
this.graphView = 'none'
|
|
break;
|
|
case 'EDIT':
|
|
await this.edit()
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
},
|
|
open(event) {
|
|
const { detail } = event;
|
|
this.labels = {
|
|
title: detail.labels.title,
|
|
body: detail.labels.body,
|
|
submit: detail.labels.submit,
|
|
cancel: detail.labels.cancel
|
|
};
|
|
this.actionType = detail.actionType;
|
|
if (detail.device) {
|
|
this.data.oldId = detail.device.id;
|
|
this.data.id = detail.device.id;
|
|
this.data.name = detail.device.name;
|
|
if (detail.device.g_display_reg_addr !== null) {
|
|
this.graphView = 'new-value'
|
|
this.data.g_display_reg_addr = detail.device.g_display_reg_addr;
|
|
this.data.g_display_reg_format = detail.device.g_display_reg_format;
|
|
this.data.g_display_reg_type = detail.device.g_display_reg_type;
|
|
this.data.g_y_label = detail.device.g_y_label;
|
|
}
|
|
}
|
|
this.isOpen = true;
|
|
},
|
|
close() {
|
|
this.isOpen = false;
|
|
this.resetDataFields();
|
|
this.graphView = "";
|
|
},
|
|
}));
|
|
|
|
Alpine.data('displayValueModal', () => ({
|
|
isOpen: false,
|
|
isLoading: false,
|
|
error: null,
|
|
data: {
|
|
name: '',
|
|
reg_addr: 0,
|
|
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() {
|
|
this.isLoading = true;
|
|
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);
|
|
} finally {
|
|
this.isLoading = false;
|
|
}
|
|
},
|
|
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;
|
|
this.resetDataFields();
|
|
},
|
|
}))
|
|
|
|
Alpine.data("monitoringPage", () => ({
|
|
selectedDevice: null,
|
|
devices: [],
|
|
isLoading: false,
|
|
error: null,
|
|
resetGraph() {
|
|
if (g instanceof Dygraph) {
|
|
g.destroy();
|
|
graphData = [];
|
|
g = null;
|
|
}
|
|
},
|
|
async init() {
|
|
try {
|
|
this.$store.app.currentPage = "loading";
|
|
this.devices = await api.get("/modbus/devices");
|
|
} catch (error) {
|
|
this.$store.app.error = error;
|
|
} finally {
|
|
this.$store.app.currentPage = "monitoring";
|
|
}
|
|
},
|
|
selectDevice(device) {
|
|
if (device.id === this.selectedDevice?.id) return;
|
|
|
|
this.error = null;
|
|
this.selectedDevice = device;
|
|
this.resetGraph();
|
|
this.isLoading = true;
|
|
|
|
dataStreamSource?.close();
|
|
dataStreamSource = new EventSource(`/api/modbus/data_stream?slave_id=${device.id}`);
|
|
dataStreamSource.onmessage = (e) => {
|
|
const data = JSON.parse(e.data);
|
|
|
|
if (data.error) {
|
|
this.isLoading = false;
|
|
this.error = data.error;
|
|
console.error('[Modbus Error]:', data.error);
|
|
return;
|
|
}
|
|
|
|
this.error = null;
|
|
|
|
console.log("[SSE Message]", data);
|
|
if (data.graph !== null) {
|
|
this.selectedDevice.g_value = data.graph.value;
|
|
graphData.push([new Date(), data.graph.value]);
|
|
if (g === null) {
|
|
g = new Dygraph(document.getElementById("div_g"), graphData, {
|
|
drawPoints: false,
|
|
showRoller: false,
|
|
ylabel: device.g_y_label,
|
|
});
|
|
g.resize(720, 300);
|
|
}
|
|
g.updateOptions({ file: graphData });
|
|
}
|
|
data.displayValues.forEach((v, i) => (this.selectedDevice.display_values[i] = v));
|
|
this.isLoading = false;
|
|
};
|
|
},
|
|
async removeDevice(id) {
|
|
try {
|
|
await api.post("/modbus/remove_device", { id });
|
|
this.devices = this.devices.filter((d) => d.id != id);
|
|
this.selectedDevice = null;
|
|
} catch (error) {
|
|
console.error("Error while removing device.", error);
|
|
}
|
|
},
|
|
async removeDisplayValue(id) {
|
|
try {
|
|
await api.post("/modbus/remove_display-value", { id });
|
|
this.selectedDevice.display_values = this.selectedDevice.display_values.filter((v) => v.id != id);
|
|
} catch (error) {
|
|
console.error("Error while removing display value", error);
|
|
}
|
|
},
|
|
}));
|
|
|
|
Alpine.data("configAppPage", () => ({
|
|
selectedConnectionType: "TCP",
|
|
isLoading: false,
|
|
errorMessage: "",
|
|
async configurateApp() {
|
|
try {
|
|
this.isLoading = true;
|
|
this.errorMessage = "";
|
|
const data = { ...this.$store.app.config };
|
|
const body = {
|
|
RTU: utils.objKeysExclude(data, ["mb_tcp_ip", "mb_tcp_port"]),
|
|
TCP: utils.objKeysExclude(data, [
|
|
"mb_rtu_path",
|
|
"mb_rtu_baud",
|
|
"mb_rtu_parity",
|
|
"mb_rtu_data_bits",
|
|
"mb_rtu_stop_bits",
|
|
]),
|
|
};
|
|
await api.patch("/app/config", body[data.mb_connection_type]);
|
|
await api.post("/modbus/connect");
|
|
await utils.wait(500);
|
|
this.$store.app.currentPage = "monitoring";
|
|
} catch (error) {
|
|
this.errorMessage = `${error.message}`;
|
|
} finally {
|
|
this.isLoading = false;
|
|
}
|
|
},
|
|
}));
|
|
|
|
Alpine.data("dialog", () => ({
|
|
isOpen: false,
|
|
isLoading: false,
|
|
labels: {
|
|
title: "Confirm dialog",
|
|
body: "Are you sure you want to perform this action?",
|
|
},
|
|
onConfirm: () => undefined,
|
|
onError: () => undefined,
|
|
confirm() {
|
|
if (this.onConfirm.constructor.name === "AsyncFunction") {
|
|
this.isLoading = true;
|
|
this.onConfirm()
|
|
.then(() => this.close())
|
|
.catch((e) => this.onError(e))
|
|
.finally(() => (this.isLoading = false));
|
|
} else {
|
|
this.onConfirm();
|
|
this.close();
|
|
}
|
|
},
|
|
close() {
|
|
this.isOpen = false;
|
|
},
|
|
|
|
/**
|
|
* Update dialog state on event
|
|
* @param {CustomEvent} event
|
|
*/
|
|
open(event) {
|
|
const { detail } = event;
|
|
this.isOpen = detail.isOpen;
|
|
this.onConfirm = detail.onConfirm;
|
|
this.labels = { title: detail.title, body: detail.body };
|
|
}
|
|
}));
|
|
});
|