diff --git a/controllers/modbus.controller.js b/controllers/modbus.controller.js
index e25fe86..6bb9f09 100644
--- a/controllers/modbus.controller.js
+++ b/controllers/modbus.controller.js
@@ -85,7 +85,16 @@ export const modbusController = {
* @param {import('express').Response} res
* @param {import('express').NextFunction} next
*/
- async removeSlaveDevice(req, res, next) {},
+ async removeSlaveDevice(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.' });
+ await db.run(`DELETE FROM "modbus_slaves" WHERE id = ?`, [req.body.id]);
+ return res.status(200).json({ message: 'Device removed.' });
+ } catch (error) {
+ next(error);
+ }
+ },
/**
* Create display value for modbus slave device
@@ -112,7 +121,27 @@ export const modbusController = {
async getLatestLog(req, res, next) {},
/**
- * Create event stream for sending data from modbus device
+ * Creates event stream aboud modbus connection status
+ * @param {import('express').Request} req
+ * @param {import('express').Response} res
+ * @param {import('express').NextFunction} next
+ */
+ async status(req, res, next) {
+ try {
+ res.setHeader("Cache-Control", "no-cache");
+ res.setHeader("Content-Type", "text/event-stream");
+ res.setHeader("Connection", "keep-alive");
+ let intervalId = setInterval(() => {
+ res.write(`event: message\ndata: ${JSON.stringify(modbusClient.isOpen)}\n\n`);
+ }, 500);
+ req.on('close', () => clearInterval(intervalId));
+ } catch (error) {
+ next(error);
+ }
+ },
+
+ /**
+ * Creates event stream for sending data from modbus device
* @param {import('express').Request} req
* @param {import('express').Response} res
* @param {import('express').NextFunction} next
diff --git a/data.db b/data.db
index c7fe9ac..3027c91 100644
Binary files a/data.db and b/data.db differ
diff --git a/index.d.ts b/index.d.ts
index 94acd34..257292a 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -10,9 +10,10 @@ export type AppConfig = {
log_interval_ms: number;
};
-export type ModbusSlave = {
+export type SlaveModbusDevice = {
id: number;
name: string;
- display_addr: number;
+ display_reg_addr: number | null;
+ display_reg_format: number | null;
is_logging: boolean;
}
\ No newline at end of file
diff --git a/migrations/001-init.sql b/migrations/001-init.sql
index beee752..0d1c201 100644
--- a/migrations/001-init.sql
+++ b/migrations/001-init.sql
@@ -21,8 +21,11 @@ CREATE TABLE IF NOT EXISTS "app_config" (
CREATE TABLE IF NOT EXISTS "modbus_slaves" (
"id" INTEGER CHECK (id >= 1 AND id <= 255),
"name" VARCHAR(256) NOT NULL,
- "display_reg_addr" INTEGER CHECK(display_reg_addr >= 0 AND display_reg_addr <= 65534),
- "display_reg_format" INTEGER CHECK(display_reg_format IN(16, 32)),
+ -- Holding register
+ "g_display_reg_addr" INTEGER CHECK(g_display_reg_addr >= 0 AND g_display_reg_addr <= 65534),
+ -- Use 1 or 2 bytes
+ "g_display_reg_format" INTEGER CHECK(g_display_reg_format IN(16, 32)),
+ "g_y_label" VARCHAR(40),
"is_logging" BOOLEAN NOT NULL,
PRIMARY KEY("id")
);
@@ -36,6 +39,14 @@ CREATE TABLE IF NOT EXISTS "display_values" (
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
+ (1, "Device #1", 1, 16, "Label 1", 1),
+ (2, "Device #2", 2, 32, "Label 2", 1),
+ (3, "Device #3", 8, 16, "Label 3", 0),
+ (4, "Device #4", NULL, NULL, "Label 4", 0);
+
--------------------------------------------------------------------------------
-- Down
--------------------------------------------------------------------------------
diff --git a/public/css/dist.css b/public/css/dist.css
index 9b6b569..9bb17d3 100644
--- a/public/css/dist.css
+++ b/public/css/dist.css
@@ -603,19 +603,27 @@ video {
position: relative;
}
+.z-50 {
+ z-index: 50;
+}
+
.mx-auto {
margin-left: auto;
margin-right: auto;
}
+.my-2 {
+ margin-top: 0.5rem;
+ margin-bottom: 0.5rem;
+}
+
.my-4 {
margin-top: 1rem;
margin-bottom: 1rem;
}
-.my-2 {
- margin-top: 0.5rem;
- margin-bottom: 0.5rem;
+.mb-3 {
+ margin-bottom: 0.75rem;
}
.ml-auto {
@@ -630,6 +638,22 @@ video {
margin-right: 0.5rem;
}
+.mr-auto {
+ margin-right: auto;
+}
+
+.mt-1 {
+ margin-top: 0.25rem;
+}
+
+.mt-1\.5 {
+ margin-top: 0.375rem;
+}
+
+.mt-10 {
+ margin-top: 2.5rem;
+}
+
.mt-16 {
margin-top: 4rem;
}
@@ -638,6 +662,10 @@ video {
margin-top: 0.5rem;
}
+.mt-20 {
+ margin-top: 5rem;
+}
+
.mt-3 {
margin-top: 0.75rem;
}
@@ -646,8 +674,12 @@ video {
margin-top: 1rem;
}
-.mt-10 {
- margin-top: 2.5rem;
+.mt-5 {
+ margin-top: 1.25rem;
+}
+
+.mt-6 {
+ margin-top: 1.5rem;
}
.block {
@@ -706,14 +738,14 @@ video {
max-width: 42rem;
}
-.max-w-6xl {
- max-width: 72rem;
-}
-
.max-w-5xl {
max-width: 64rem;
}
+.max-w-md {
+ max-width: 28rem;
+}
+
.flex-1 {
flex: 1 1 0%;
}
@@ -762,6 +794,10 @@ video {
align-items: center;
}
+.justify-end {
+ justify-content: flex-end;
+}
+
.justify-between {
justify-content: space-between;
}
@@ -772,6 +808,12 @@ video {
margin-left: calc(0.5rem * calc(1 - var(--tw-space-x-reverse)));
}
+.space-y-1 > :not([hidden]) ~ :not([hidden]) {
+ --tw-space-y-reverse: 0;
+ margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse)));
+ margin-bottom: calc(0.25rem * var(--tw-space-y-reverse));
+}
+
.space-y-2 > :not([hidden]) ~ :not([hidden]) {
--tw-space-y-reverse: 0;
margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse)));
@@ -816,6 +858,21 @@ video {
border-style: dashed;
}
+.bg-gray-100 {
+ --tw-bg-opacity: 1;
+ background-color: rgb(243 244 246 / var(--tw-bg-opacity));
+}
+
+.bg-red-500 {
+ --tw-bg-opacity: 1;
+ 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;
}
@@ -862,15 +919,37 @@ video {
padding-right: 0.625rem;
}
+.px-4 {
+ padding-left: 1rem;
+ padding-right: 1rem;
+}
+
.py-3 {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
}
+.py-3\.5 {
+ padding-top: 0.875rem;
+ padding-bottom: 0.875rem;
+}
+
+.pl-2 {
+ padding-left: 0.5rem;
+}
+
+.pr-6 {
+ padding-right: 1.5rem;
+}
+
.pt-5 {
padding-top: 1.25rem;
}
+.text-left {
+ text-align: left;
+}
+
.text-center {
text-align: center;
}
@@ -885,9 +964,14 @@ video {
line-height: 2.25rem;
}
-.text-5xl {
- font-size: 3rem;
- line-height: 1;
+.text-4xl {
+ font-size: 2.25rem;
+ line-height: 2.5rem;
+}
+
+.text-lg {
+ font-size: 1.125rem;
+ line-height: 1.75rem;
}
.text-sm {
@@ -895,6 +979,11 @@ video {
line-height: 1.25rem;
}
+.text-xl {
+ font-size: 1.25rem;
+ line-height: 1.75rem;
+}
+
.font-bold {
font-weight: 700;
}
@@ -913,16 +1002,40 @@ video {
color: rgb(156 163 175 / var(--tw-text-opacity));
}
+.text-gray-500 {
+ --tw-text-opacity: 1;
+ color: rgb(107 114 128 / var(--tw-text-opacity));
+}
+
.text-gray-700 {
--tw-text-opacity: 1;
color: rgb(55 65 81 / var(--tw-text-opacity));
}
+.text-green-700 {
+ --tw-text-opacity: 1;
+ color: rgb(21 128 61 / var(--tw-text-opacity));
+}
+
+.text-red-500 {
+ --tw-text-opacity: 1;
+ color: rgb(239 68 68 / var(--tw-text-opacity));
+}
+
.text-red-600 {
--tw-text-opacity: 1;
color: rgb(220 38 38 / var(--tw-text-opacity));
}
+.text-red-700 {
+ --tw-text-opacity: 1;
+ color: rgb(185 28 28 / var(--tw-text-opacity));
+}
+
+.underline {
+ text-decoration-line: underline;
+}
+
.opacity-70 {
opacity: 0.7;
}
@@ -943,6 +1056,24 @@ video {
display: none !important;
}
+.backdrop {
+ position: fixed;
+ top: 0px;
+ left: 0px;
+ bottom: 0px;
+ right: 0px;
+ overflow-y: auto;
+ background-color: rgba(0,0,0,.15);
+ padding-left: 0.625rem;
+ padding-right: 0.625rem;
+}
+
+a {
+ --tw-text-opacity: 1;
+ color: rgb(37 99 235 / var(--tw-text-opacity));
+ text-decoration-line: underline;
+}
+
input,
select {
margin-top: 0.125rem;
@@ -971,6 +1102,11 @@ button:disabled {
pointer-events: all !important;
}
+.hover\:bg-gray-100:hover {
+ --tw-bg-opacity: 1;
+ background-color: rgb(243 244 246 / var(--tw-bg-opacity));
+}
+
.hover\:bg-gray-50:hover {
--tw-bg-opacity: 1;
background-color: rgb(249 250 251 / var(--tw-bg-opacity));
diff --git a/public/css/main.css b/public/css/main.css
index 40caec4..a9f2852 100644
--- a/public/css/main.css
+++ b/public/css/main.css
@@ -12,6 +12,14 @@
display: none !important;
}
+.backdrop {
+ @apply fixed top-0 left-0 bottom-0 right-0 bg-[rgba(0,0,0,.15)] px-2.5 overflow-y-auto;
+}
+
+a {
+ @apply underline text-blue-600;
+}
+
input,
select {
@apply w-full border py-1 rounded-sm my-0.5 bg-white;
diff --git a/public/index.html b/public/index.html
index f7909db..e1c06ac 100644
--- a/public/index.html
+++ b/public/index.html
@@ -5,6 +5,7 @@
Modbus Logger App
+
@@ -17,243 +18,289 @@
-
-
-
-
-
-
Devices list:
-
-
-
Device #1
+
+
+
+
+
+
+
Devices:
+
No devices added.
+
+
+
+
-
-
Device #2
-
-
-
Device #3
-
-
-
- +
-
-
-
-
Connection:
-
-
-
-
-
-
-
-
-
-
Setup configuration
-
- Fill in the required configuration settings to connect to the Modbus network.
-
-
-
-
-
+
+
+
+ No device selected.
+
+
+
+
+
+
+
+
+
+
+
+
Setup configuration
+
+ Fill in the required configuration settings to connect to the Modbus network.
+
+
+
+
+
+
-
-
-
-
+
+
+
- Loading...
-
-
+
+
-
-
-
Something went wrong.
-
Refresh page to try again.
-
-
-
+
+
+
+
Something went wrong.
+
Refresh page to try again.
+
+
-
-
+
+
-
-
-
Page not found
+
+
+
+
Page not found
+
return to monitoring
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+