@@ -157,8 +157,20 @@
/>
-
-
+
+
+
+
+
+ Error while connection to Modbus network
+
diff --git a/public/js/api.js b/public/js/api.js
index 82caf60..ea1ef7a 100644
--- a/public/js/api.js
+++ b/public/js/api.js
@@ -1,5 +1,15 @@
const urlPrefix = "/api";
+/**
+ * Response handler
+ * @param {Response} response
+ */
+async function handle(response) {
+ const data = await response.json();
+ if (!response.ok) return Promise.reject((data && data.message) || response.statusText);
+ return data;
+}
+
export default {
async patch(path, data) {
const response = await fetch(`${urlPrefix}${path}`, {
@@ -9,7 +19,7 @@ export default {
"Content-Type": "application/x-www-form-urlencoded",
},
});
- return response;
+ return handle(response);
},
async post(path, data) {
@@ -20,6 +30,13 @@ export default {
"Content-Type": "application/x-www-form-urlencoded",
},
});
- return response;
+ return await handle(response);
+ },
+
+ async get(path) {
+ const response = await fetch(`${urlPrefix}${path}`, {
+ method: "GET",
+ });
+ return await handle(response);
},
};
diff --git a/public/js/app.js b/public/js/app.js
index 3f6b371..6523735 100644
--- a/public/js/app.js
+++ b/public/js/app.js
@@ -21,69 +21,78 @@ document.addEventListener("alpine:init", async () => {
},
currentPage: "loading",
async init() {
- const config = await fetch("/api/app/config");
- const is_configured = await fetch("/api/app/is_configured");
+ try {
+ const { config } = await api.get("/app/config");
+ const { is_configured } = await api.get("/app/is_configured");
- if (config.ok && is_configured.ok) {
- this.config = (await config.json()).config;
- this.is_configured = (await is_configured.json()).is_configured;
+ this.is_configured = is_configured;
+
+ if (config) this.config = config;
+ if (is_configured) {
+ this.currentPage = "monitoring";
+ return;
+ }
+
+ this.currentPage = "configuration";
+ } catch (error) {
+ throw new Error("Error on initializing app.");
}
-
- if (this.is_configured) {
- this.currentPage = "monitoring";
- return;
- }
-
- this.currentPage = "configuration";
},
});
Alpine.data("configApp", () => ({
selectedConnectionType: "TCP",
isLoading: false,
+ errorMessage: "",
async configurateApp() {
- try {
- const {
+ this.isLoading = true;
+ this.errorMessage = "";
+ let data = {};
+ const {
+ mb_connection_type,
+ mb_tcp_ip,
+ mb_tcp_port,
+ mb_rtu_path,
+ mb_rtu_baud,
+ mb_rtu_parity,
+ mb_rtu_data_bits,
+ mb_rtu_stop_bits,
+ log_interval_ms,
+ } = this.$store.app.config;
+
+ if (mb_connection_type === "TCP") {
+ data = {
mb_connection_type,
mb_tcp_ip,
mb_tcp_port,
+ log_interval_ms,
+ };
+ }
+
+ if (mb_connection_type === "RTU") {
+ data = {
+ mb_connection_type,
mb_rtu_path,
- mb_rtu_baud,
mb_rtu_parity,
mb_rtu_data_bits,
mb_rtu_stop_bits,
+ mb_rtu_baud,
log_interval_ms,
- } = this.$store.app.config;
-
- if (mb_connection_type === "TCP") {
- await api.patch("/app/config", {
- mb_connection_type,
- mb_tcp_ip,
- mb_tcp_port,
- log_interval_ms,
- });
-
- await api.post("/modbus/connect");
-
- this.$store.app.currentPage = "monitoring";
- return;
- }
-
- if (mb_connection_type === "RTU") {
- await api.patch("/app/config", {
- mb_rtu_path,
- mb_rtu_parity,
- mb_rtu_data_bits,
- mb_rtu_stop_bits,
- mb_rtu_baud,
- });
- await api.post("/modbus_connect");
- this.$store.app.currentPage = "monitoring";
- return;
- }
- } catch (error) {
- console.error("Error while configuring app.");
+ };
}
+
+ if (data) {
+ await api.patch("/app/config", data);
+
+ try {
+ await api.post("/modbus/connect");
+ this.$store.app.currentPage = "monitoring";
+ } catch (error) {
+ this.errorMessage = "Modbus connection error.";
+ }
+ }
+
+ this.isLoading = false;
},
}));
});
diff --git a/public/js/services/app.service.js b/public/js/services/app.service.js
deleted file mode 100644
index 912f6f2..0000000
--- a/public/js/services/app.service.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import api from "../api";
-
-export async function configurateApp(config) {
- const {
- mb_connection_type,
- mb_tcp_ip,
- mb_tcp_port,
- mb_rtu_path,
- mb_rtu_parity,
- mb_rtu_data_bits,
- mb_rtu_stop_bits,
- mb_rtu_baud,
- log_interval_ms,
- } = config;
- let data = {};
-
- if (config.mb_connection_type === "TCP") {
- data = { mb_connection_type, mb_tcp_ip, mb_tcp_port, log_interval_ms };
- }
-
- if (config.mb_connection_type === "RTU") {
- data = { mb_rtu_path, mb_rtu_parity, mb_rtu_data_bits, mb_rtu_stop_bits, mb_rtu_baud };
- }
-
- const configRes = await api.patch("/app/config", data);
- if (configRes.ok) await api.post("/modbus/connect");
-}
diff --git a/routes/api.router.js b/routes/api.router.js
index 6d8f088..4eb4bf4 100644
--- a/routes/api.router.js
+++ b/routes/api.router.js
@@ -25,7 +25,7 @@ const apiRouter = express.Router();
apiRouter.get("/app/config", async (req, res, next) => {
try {
const data = await appService.getConfig(db);
- if (!data) return res.status(404).json({ config: null });
+ if (!data) return res.status(200).json({ config: null });
return res.status(200).json({ config: data });
} catch (error) {
next(error);
@@ -52,7 +52,7 @@ apiRouter.patch("/app/config", validateBodyMiddleware(appConfigSchema), async (r
[mb_tcp_ip, mb_tcp_port, payload.log_interval_ms],
);
- return res.status(200).send("Config Updated.");
+ return res.status(200).json({ message: "Config Updated." });
}
/* Update RTU configuration */
@@ -71,10 +71,10 @@ apiRouter.patch("/app/config", validateBodyMiddleware(appConfigSchema), async (r
`,
[mb_rtu_path, mb_rtu_baud, mb_rtu_parity, mb_rtu_data_bits, mb_rtu_stop_bits, payload.log_interval_ms],
);
- return res.status(200).send("Config updated.");
+ return res.status(200).json({ message: "Config updated." });
}
- return res.status(400).send("Undefined Modbus connection type.");
+ return res.status(400).json({ message: "Undefined Modbus connection type." });
} catch (error) {
next(error);
}
@@ -88,7 +88,7 @@ apiRouter.get("/app/is_configured", async (req, res, next) => {
const config = await appService.getConfig(db);
let is_configured = false;
- if (!config) return res.status(200).send({ is_configured });
+ if (!config) return res.status(200).json({ is_configured });
const isValuesExists = (colPrefix) => {
const keys = Object.keys(config).filter((k) => k.includes(colPrefix));
@@ -99,7 +99,7 @@ apiRouter.get("/app/is_configured", async (req, res, next) => {
if (config.mb_connection_type === "TCP") is_configured = isValuesExists("mb_tcp");
if (config.mb_connection_type === "RTU") is_configured = isValuesExists("mb_rtu");
- return res.status(200).send({ is_configured });
+ return res.status(200).json({ is_configured });
} catch (error) {
next(error);
}
@@ -111,7 +111,7 @@ apiRouter.get("/app/is_configured", async (req, res, next) => {
apiRouter.post("/modbus/connect", async (req, res, next) => {
try {
const config = await appService.getConfig(db);
- if (!config) return res.status(400).send("Application is not configured");
+ if (!config) return res.status(400).json({ message: "Application is not configured" });
if (modbusClient.isOpen) {
modbusClient.close(undefined);
@@ -120,7 +120,7 @@ apiRouter.post("/modbus/connect", async (req, res, next) => {
if (config.mb_connection_type === "TCP") {
await modbusClient.connectTCP(config.mb_tcp_ip, { port: config.mb_tcp_port });
- return res.status(200).send("Connected.");
+ return res.status(200).json({ message: "Connected." });
}
if (config.mb_connection_type === "RTU") {
@@ -130,10 +130,10 @@ apiRouter.post("/modbus/connect", async (req, res, next) => {
parity: config.mb_rtu_parity,
stopBits: config.mb_rtu_stop_bits,
});
- return res.status(200).send("Connected.");
+ return res.status(200).json({ message: "Connected." });
}
- return res.status(400).send("Connection type is invalid.");
+ return res.status(400).json({ message: "Connection type is invalid." });
} catch (error) {
next(error);
}
@@ -141,7 +141,7 @@ apiRouter.post("/modbus/connect", async (req, res, next) => {
apiRouter.post("/modbus/close", (req, res, next) => {
try {
- if (!modbusClient.isOpen) return res.status(400).send("Already closed.");
+ if (!modbusClient.isOpen) return res.status(400).json({ message: "Already closed." });
modbusClient.close(undefined);
modbusClient.destroy(undefined);
} catch (error) {
@@ -149,4 +149,24 @@ apiRouter.post("/modbus/close", (req, res, next) => {
}
});
+apiRouter.get("/modbus/data_stream", async (req, res, next) => {
+ try {
+ if (!modbusClient.isOpen) return res.status(503).json({ message: "Modbus connection closed." });
+
+ res.setHeader("Cache-Control", "no-cache");
+ res.setHeader("Content-Type", "text/event-stream");
+ res.setHeader("Connection", "keep-alive");
+
+ let interval = setInterval(async () => {
+ const { data } = await modbusClient.readHoldingRegisters(0, 1);
+ res.write("event: messsage\n");
+ res.write(`data: ${JSON.stringify({ data })}`);
+ res.write("\n\n");
+ }, 1000);
+ req.on("close", () => clearInterval(interval));
+ } catch (error) {
+ next(error);
+ }
+});
+
export { apiRouter };
diff --git a/sql/init-schema.sql b/sql/init-schema.sql
index 5cce2fb..fb62ccc 100644
--- a/sql/init-schema.sql
+++ b/sql/init-schema.sql
@@ -1,13 +1,13 @@
CREATE TABLE IF NOT EXISTS "app_config" (
"id" INTEGER,
"mb_connection_type" VARCHAR(3) NOT NULL DEFAULT "RTU" CHECK(mb_connection_type IN ('TCP','RTU')),
- "mb_tcp_ip" VARCHAR(15),
- "mb_tcp_port" INTEGER,
- "mb_rtu_path" VARCHAR(10),
- "mb_rtu_baud" REAL,
- "mb_rtu_parity" VARCHAR(5) CHECK(mb_rtu_parity IN ("even", "odd", "none")),
- "mb_rtu_data_bits" INTEGER,
- "mb_rtu_stop_bits" INTEGER,
+ "mb_tcp_ip" VARCHAR(15) NOT NULL DEFAULT "127.0.0.1",
+ "mb_tcp_port" INTEGER NOT NULL DEFAULT 502,
+ "mb_rtu_path" VARCHAR(10) NOT NULL DEFAULT "",
+ "mb_rtu_baud" INTEGER NOT NULL DEFAULT 9600,
+ "mb_rtu_parity" VARCHAR(5) NOT NULL DEFAULT "none" CHECK(mb_rtu_parity IN ("even", "odd", "none")),
+ "mb_rtu_data_bits" INTEGER NOT NULL DEFAULT 8,
+ "mb_rtu_stop_bits" INTEGER NOT NULL DEFAULT 1,
"log_interval_ms" INTEGER DEFAULT 1000 NOT NULL,
CONSTRAINT "id_unique" CHECK(id=0),
CONSTRAINT "min_interval_check" CHECK(log_interval_ms >= 1000)