Compare commits
50
Commits
84eb4b2365
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b0f9013aa | ||
|
|
9d33984c97 | ||
|
|
89f02422e7 | ||
|
|
58f5522442 | ||
|
|
e6c60d0707 | ||
|
|
e96af3ac7e | ||
|
|
87343e354b | ||
|
|
ca43d6b9a5 | ||
|
|
68140a0c45 | ||
|
|
3ed80f4b68 | ||
|
|
497a0e48e3 | ||
|
|
825547889c | ||
|
|
923a35cad1 | ||
|
|
0c10ff4051 | ||
|
|
451008b895 | ||
|
|
2ca79a7b9f | ||
|
|
a78d49ef7e | ||
|
|
56982cc26a | ||
|
|
3168d21c5f | ||
|
|
5b7b4fa57e | ||
|
|
ee2c69b4c2 | ||
|
|
cb7e8d19e4 | ||
|
|
59de54e80a | ||
|
|
10bdc8fb5a | ||
|
|
c134a20da8 | ||
|
|
0d696a44e4 | ||
|
|
839fd24b4e | ||
|
|
1c8dd77946 | ||
|
|
60823714a6 | ||
|
|
4dd58ecaaa | ||
|
|
e15484c460 | ||
|
|
4673ff3ff1 | ||
|
|
e15b1566f0 | ||
|
|
e75764da43 | ||
|
|
41b88d9e3e | ||
|
|
241a9ec240 | ||
|
|
55e7260ee5 | ||
|
|
f47cd3a97b | ||
|
|
67b0e4ebd7 | ||
|
|
adac95129d | ||
|
|
58bacd2f6d | ||
|
|
483d27750b | ||
|
|
698be221c0 | ||
|
|
79cbde6b4a | ||
|
|
7fb98b95f8 | ||
|
|
459b97d95c | ||
|
|
c499f1b97c | ||
|
|
e8d77ec18e | ||
|
|
4bc399b44f | ||
|
|
b9bc0e52d3 |
No files matched your search
@@ -0,0 +1,28 @@
|
||||
name: Build Docker Image (Podman)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
# workflow_call:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: podman-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- name: Build image with Podman
|
||||
env:
|
||||
BUILDAH_ISOLATION: chroot
|
||||
run: |
|
||||
VERSION=$(cat VERSION)
|
||||
podman build --network=host -t paramod:latest -t git.skaville.rocks/eneuber/paramod:$VERSION .
|
||||
- name: Login to Gitea Registry
|
||||
run: echo ${{ secrets.REGISTRY_TOKEN }} | podman login -u ${{ secrets.USER }} --password-stdin git.skaville.rocks
|
||||
- name: Push image with version
|
||||
run: |
|
||||
VERSION=$(cat VERSION)
|
||||
podman push git.skaville.rocks/eneuber/paramod:$VERSION
|
||||
- name: Push image latest
|
||||
run: podman push git.skaville.rocks/eneuber/paramod:latest
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
# Patch Cargo.toml version from VERSION file before build
|
||||
VERSION=$(cat VERSION)
|
||||
sed -i 's/^version = ".*"/version = "'"$VERSION"'"/' Cargo.toml
|
||||
exec cargo build
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
sed -i 's/^version = ".*"/version = "__VERSION__"/' Cargo.toml
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
reset_version() {
|
||||
sed -i 's/^version = ".*"/version = "__VERSION__"/' Cargo.toml
|
||||
}
|
||||
|
||||
trap reset_version EXIT
|
||||
|
||||
VERSION=$(cat VERSION)
|
||||
sed -i 's/^version = ".*"/version = "'"$VERSION"'"/' Cargo.toml
|
||||
cargo run
|
||||
Vendored
+3
-2
@@ -5,11 +5,12 @@
|
||||
"name": "Debug main.rs",
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/target/debug/table-server",
|
||||
"program": "${workspaceFolder}/target/debug/paramod",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"sourceLanguages": ["rust"],
|
||||
"preLaunchTask": "cargo build"
|
||||
"preLaunchTask": "cargo build",
|
||||
"postDebugTask": "cargo reset version placeholder"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+14
-2
@@ -4,12 +4,24 @@
|
||||
{
|
||||
"label": "cargo build",
|
||||
"type": "shell",
|
||||
"command": "cargo build",
|
||||
"command": ".vscode/cargo-build-version.sh",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": ["$rustc"]
|
||||
"problemMatcher": ["$codelldb-rustc"]
|
||||
},
|
||||
{
|
||||
"label": "cargo reset version placeholder",
|
||||
"type": "shell",
|
||||
"command": "bash .vscode/cargo-reset-version.sh",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "cargo run",
|
||||
"type": "shell",
|
||||
"command": "bash .vscode/cargo-run-version.sh",
|
||||
"problemMatcher": ["$codelldb-rustc"]
|
||||
}
|
||||
]
|
||||
}
|
||||
Generated
+531
-18
@@ -8,7 +8,7 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.10.0",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@@ -29,7 +29,7 @@ dependencies = [
|
||||
"actix-service",
|
||||
"actix-utils",
|
||||
"actix-web",
|
||||
"bitflags",
|
||||
"bitflags 2.10.0",
|
||||
"bytes",
|
||||
"derive_more",
|
||||
"futures-core",
|
||||
@@ -53,7 +53,7 @@ dependencies = [
|
||||
"actix-service",
|
||||
"actix-utils",
|
||||
"base64 0.22.1",
|
||||
"bitflags",
|
||||
"bitflags 2.10.0",
|
||||
"brotli",
|
||||
"bytes",
|
||||
"bytestring",
|
||||
@@ -300,6 +300,12 @@ version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.10.0"
|
||||
@@ -355,6 +361,12 @@ version = "3.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.11.0"
|
||||
@@ -395,7 +407,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
@@ -480,6 +495,16 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.7"
|
||||
@@ -642,6 +667,17 @@ dependencies = [
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
@@ -663,12 +699,32 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.31"
|
||||
@@ -688,9 +744,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -745,7 +804,7 @@ version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.10.0",
|
||||
"ignore",
|
||||
"walkdir",
|
||||
]
|
||||
@@ -805,6 +864,17 @@ dependencies = [
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-range"
|
||||
version = "0.1.5"
|
||||
@@ -832,6 +902,44 @@ dependencies = [
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"socket2 0.5.10",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"http",
|
||||
"hyper",
|
||||
"rustls",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.64"
|
||||
@@ -990,6 +1098,29 @@ dependencies = [
|
||||
"hashbrown 0.16.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "influxdb"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "601aa12a5876c044ea2a94a9443d0f086e6fc1f7bb4264bd7120e63c1462d1c8"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"futures-util",
|
||||
"http",
|
||||
"lazy_static",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
@@ -1170,6 +1301,12 @@ version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
|
||||
|
||||
[[package]]
|
||||
name = "ordered-multimap"
|
||||
version = "0.7.3"
|
||||
@@ -1180,6 +1317,26 @@ dependencies = [
|
||||
"hashbrown 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paramod"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"actix-files",
|
||||
"actix-web",
|
||||
"chrono",
|
||||
"config",
|
||||
"influxdb",
|
||||
"rumqttc",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"tera",
|
||||
"tokio",
|
||||
"tokio-modbus",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.5"
|
||||
@@ -1436,7 +1593,7 @@ version = "0.5.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 2.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1474,6 +1631,61 @@ version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"hyper-rustls",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustls",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"webpki-roots",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"getrandom 0.2.16",
|
||||
"libc",
|
||||
"untrusted",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ron"
|
||||
version = "0.8.1"
|
||||
@@ -1481,11 +1693,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"bitflags",
|
||||
"bitflags 2.10.0",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rumqttc"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d8941c6791801b667d52bfe9ff4fc7c968d4f3f9ae8ae7abdaaa1c966feafc8"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"flume",
|
||||
"futures-util",
|
||||
"log",
|
||||
"rustls-native-certs",
|
||||
"rustls-pemfile",
|
||||
"rustls-webpki",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-ini"
|
||||
version = "0.20.0"
|
||||
@@ -1496,6 +1726,49 @@ dependencies = [
|
||||
"ordered-multimap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.21.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"rustls-webpki",
|
||||
"sct",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-native-certs"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
|
||||
dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls-pemfile",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.101.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
@@ -1517,12 +1790,54 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1"
|
||||
dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "321c8673b092a9a42605034a9879d73cb79101ed5fd117bc9a597b89b4e9e61a"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
@@ -1691,6 +2006,15 @@ dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
@@ -1708,6 +2032,12 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.2"
|
||||
@@ -1720,19 +2050,24 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "table-server"
|
||||
version = "0.1.0"
|
||||
name = "system-configuration"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
|
||||
dependencies = [
|
||||
"actix-files",
|
||||
"actix-web",
|
||||
"config",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"tera",
|
||||
"tokio",
|
||||
"toml",
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation",
|
||||
"system-configuration-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration-sys"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1757,6 +2092,26 @@ dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.44"
|
||||
@@ -1835,6 +2190,32 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-modbus"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e82dfb1a3265ca939b7a76103c89ad770f8cc0ea8de76c83d352e7c1fc5b2c8b"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"log",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.17"
|
||||
@@ -1889,6 +2270,12 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.41"
|
||||
@@ -1921,6 +2308,12 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.19.0"
|
||||
@@ -1963,6 +2356,12 @@ version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.7"
|
||||
@@ -2003,6 +2402,15 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
|
||||
dependencies = [
|
||||
"try-lock",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.1+wasi-snapshot-preview1"
|
||||
@@ -2031,6 +2439,19 @@ dependencies = [
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.55"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.105"
|
||||
@@ -2063,6 +2484,22 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.25.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.11"
|
||||
@@ -2131,6 +2568,15 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
@@ -2158,6 +2604,21 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.48.5",
|
||||
"windows_aarch64_msvc 0.48.5",
|
||||
"windows_i686_gnu 0.48.5",
|
||||
"windows_i686_msvc 0.48.5",
|
||||
"windows_x86_64_gnu 0.48.5",
|
||||
"windows_x86_64_gnullvm 0.48.5",
|
||||
"windows_x86_64_msvc 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
@@ -2191,6 +2652,12 @@ dependencies = [
|
||||
"windows_x86_64_msvc 0.53.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
@@ -2203,6 +2670,12 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
@@ -2215,6 +2688,12 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
@@ -2239,6 +2718,12 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
@@ -2251,6 +2736,12 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
@@ -2263,6 +2754,12 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
@@ -2275,6 +2772,12 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
@@ -2296,6 +2799,16 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.50.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.46.0"
|
||||
|
||||
+8
-3
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "table-server"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
name = "paramod"
|
||||
version = "__VERSION__"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
serde_yaml = "0.9"
|
||||
@@ -16,3 +16,8 @@ tera = "1.19"
|
||||
config = "0.14"
|
||||
toml = "0.8"
|
||||
serde_derive = "1.0"
|
||||
tokio-modbus = "0.7"
|
||||
|
||||
rumqttc = "0.23"
|
||||
influxdb = "0.7"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
+11
-19
@@ -1,44 +1,36 @@
|
||||
|
||||
# Build Stage
|
||||
FROM rust:1.75 as builder
|
||||
FROM rust:1.85 as builder
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Copy manifest files
|
||||
COPY Cargo.toml ./
|
||||
|
||||
# Copy source code and templates
|
||||
COPY Cargo.lock ./
|
||||
COPY VERSION ./
|
||||
COPY src ./src
|
||||
COPY templates ./templates
|
||||
COPY static ./static
|
||||
|
||||
# Build the application
|
||||
RUN cargo build --release
|
||||
RUN VERSION=$(cat VERSION) \
|
||||
&& sed -i 's/^version = "__VERSION__"/version = "'"$VERSION"'"/' Cargo.toml \
|
||||
&& cargo build --release
|
||||
|
||||
# Runtime Stage
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
# Install required dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy the binary from builder
|
||||
COPY --from=builder /usr/src/app/target/release/table-server /app/table-server
|
||||
|
||||
# Copy templates and static files
|
||||
COPY --from=builder /usr/src/app/target/release/paramod /app/paramod
|
||||
COPY --from=builder /usr/src/app/templates /app/templates
|
||||
COPY --from=builder /usr/src/app/static /app/static
|
||||
COPY paramod.yaml /app/paramod.yaml
|
||||
|
||||
# Create directory for config file
|
||||
RUN mkdir -p /app/data
|
||||
|
||||
# Expose port
|
||||
EXPOSE 8080
|
||||
|
||||
# Set environment to use the data directory
|
||||
ENV CONFIG_PATH=/app/data/table_config.json
|
||||
ENV CONFIG_PATH=/app/paramod.yaml
|
||||
|
||||
# Run the binary
|
||||
CMD ["/app/table-server"]
|
||||
CMD ["/app/paramod"]
|
||||
@@ -1,212 +1,103 @@
|
||||
# Tabellen Webserver
|
||||
# Paramod
|
||||
|
||||
Ein einfacher Rust-Webserver, der eine editierbare 3x3-Tabelle bereitstellt und in einer JSON-Konfigurationsdatei persistiert.
|
||||
|
||||
## Projektstruktur
|
||||
|
||||
```
|
||||
table-server/
|
||||
├── src/
|
||||
│ └── main.rs
|
||||
├── templates/
|
||||
│ ├── index.html
|
||||
│ └── settings.html
|
||||
├── static/
|
||||
│ ├── style.css
|
||||
│ ├── script.js
|
||||
│ └── settings.js
|
||||
├── Cargo.toml
|
||||
├── Dockerfile
|
||||
├── .gitignore
|
||||
└── README.md
|
||||
```
|
||||
Rust-Webserver zur Anzeige und Bearbeitung von Modbus-Tabellen mit Persistierung in `paramod.yaml`.
|
||||
|
||||
## Funktionen
|
||||
|
||||
- **3 separate Tabellen** für verschiedene Sensor-Gruppen
|
||||
- **Navigation** mit aktivem Status-Indikator
|
||||
- **Header mit Logo** für professionelles Erscheinungsbild
|
||||
- **Zeilen hinzufügen/löschen** dynamisch zur Laufzeit
|
||||
- **Einstellungsseite** für MQTT und InfluxDB Konfiguration
|
||||
- Editierbare Textfelder (Bezeichnung, Adresse, Type, Faktor)
|
||||
- Toggle-Schalter für Boolean-Werte (MQTT, InfluxDB)
|
||||
- **Zentrale JSON-Persistierung** für alle Tabellen und Einstellungen
|
||||
- REST-API für Daten-Management
|
||||
- Docker-Unterstützung
|
||||
- Responsive Design
|
||||
- Tabellen für `modbus_input_register`, `modbus_holding_register` und `modbus_coils`
|
||||
- Web-UI zum Bearbeiten und Speichern der Tabellen
|
||||
- Einstellungsseite für `default`, `modbus`, `mqtt` und `influxdb`
|
||||
- MQTT/Influx-Integration gemäß Konfiguration
|
||||
- Speicherung aller Änderungen in `paramod.yaml`
|
||||
|
||||
## Lokale Entwicklung
|
||||
|
||||
### Voraussetzungen
|
||||
|
||||
- Rust (Version 1.75 oder höher)
|
||||
- Cargo
|
||||
- Rust + Cargo
|
||||
|
||||
### Installation und Start
|
||||
### Start
|
||||
|
||||
```bash
|
||||
# Projekt erstellen
|
||||
cargo new table-server
|
||||
cd table-server
|
||||
|
||||
# Dependencies installieren und starten
|
||||
cargo run
|
||||
```
|
||||
|
||||
Der Server läuft dann auf `http://localhost:8080`
|
||||
Server: `http://localhost:8080`
|
||||
|
||||
## Docker
|
||||
## Docker (Podman)
|
||||
|
||||
### Container bauen
|
||||
### Image bauen
|
||||
|
||||
```bash
|
||||
docker build -t table-server .
|
||||
podman build -t paramod:latest .
|
||||
```
|
||||
|
||||
### Container starten
|
||||
|
||||
```bash
|
||||
docker run -p 8080:8080 -v $(pwd)/data:/app/data table-server
|
||||
podman run --rm -p 8080:8080 -v "$(pwd)/paramod.yaml:/app/paramod.yaml" paramod:latest
|
||||
```
|
||||
|
||||
Mit Volume-Mount bleibt die Konfigurationsdatei auch nach Container-Neustarts erhalten.
|
||||
|
||||
### Docker Compose (optional)
|
||||
|
||||
Erstelle eine `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
table-server:
|
||||
build: .
|
||||
ports:
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
restart: unless-stopped
|
||||
```
|
||||
|
||||
Starten mit:
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## Verwendung
|
||||
|
||||
1. Öffne `http://localhost:8080` im Browser
|
||||
2. Navigiere zwischen den Tabellen über das Menü:
|
||||
- **Tabelle 1, 2, 3**: Verschiedene Sensor-Gruppen
|
||||
- **⚙️ Einstellungen**: MQTT und InfluxDB Konfiguration
|
||||
3. In den Tabellen:
|
||||
- **➕ Zeile hinzufügen**: Neue Sensor-Einträge erstellen
|
||||
- **🗑️ Löschen**: Einzelne Zeilen entfernen
|
||||
- **Felder bearbeiten**:
|
||||
- Bezeichnung: Name des Sensors
|
||||
- Adresse: IP-Adresse oder Identifier
|
||||
- Type: Sensor-Typ (z.B. Temperatur, Luftfeuchtigkeit)
|
||||
- Faktor: Numerischer Korrekturfaktor
|
||||
- MQTT: Toggle-Schalter für MQTT-Aktivierung
|
||||
- InfluxDB: Toggle-Schalter für InfluxDB-Aktivierung
|
||||
4. **💾 Speichern**: Änderungen persistieren
|
||||
5. Alle Daten werden zentral in `table_config.json` gespeichert
|
||||
|
||||
## API Endpoints
|
||||
|
||||
- `GET /` - Zeigt Tabelle 1
|
||||
- `GET /table/table2` - Zeigt Tabelle 2
|
||||
- `GET /table/table3` - Zeigt Tabelle 3
|
||||
- `GET /settings` - Zeigt Einstellungsseite
|
||||
- `POST /api/save` - Speichert eine Tabelle
|
||||
- `POST /api/save-settings` - Speichert die Einstellungen
|
||||
- `GET /static/*` - Statische Dateien (CSS, JS)
|
||||
- `GET /`
|
||||
- `GET /table/modbus_input_register`
|
||||
- `GET /table/modbus_holding_register`
|
||||
- `GET /table/modbus_coils`
|
||||
- `GET /settings`
|
||||
- `GET /api/config`
|
||||
- `POST /api/save`
|
||||
- `POST /api/save-settings`
|
||||
|
||||
### Beispiel API-Request (Tabelle speichern)
|
||||
## API-Beispiel: Tabelle speichern
|
||||
|
||||
Beispiel für Holding-Register:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8080/api/save \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"table_id": "table1",
|
||||
"table_id": "modbus_holding_register",
|
||||
"rows": [
|
||||
{
|
||||
"bezeichnung": "Sensor 1",
|
||||
"adresse": "192.168.1.100",
|
||||
"type": "Temperatur",
|
||||
"faktor": "1.0",
|
||||
"mqtt": true,
|
||||
"influxdb": false
|
||||
"TVsoll": {
|
||||
"addr": 2,
|
||||
"type": "INT16",
|
||||
"factor": 0.1,
|
||||
"write": true,
|
||||
"mqtt": true,
|
||||
"influxdb": true,
|
||||
"comment": "Vorlauf Soll"
|
||||
}
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
### Beispiel API-Request (Einstellungen speichern)
|
||||
Beispiel für Coils:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8080/api/save-settings \
|
||||
curl -X POST http://localhost:8080/api/save \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"mqtt_broker": "localhost",
|
||||
"mqtt_port": "1883",
|
||||
"influxdb_url": "http://localhost:8086",
|
||||
"influxdb_token": "your-token-here"
|
||||
"table_id": "modbus_coils",
|
||||
"rows": [
|
||||
{
|
||||
"MgtSystem": {
|
||||
"addr": 0,
|
||||
"write": true,
|
||||
"mqtt": true,
|
||||
"influxdb": false,
|
||||
"comment": "Leitsystem aktiv"
|
||||
}
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
## Konfigurationsdatei
|
||||
## Konfiguration
|
||||
|
||||
Die komplette Anwendungskonfiguration wird in `table_config.json` gespeichert:
|
||||
|
||||
```json
|
||||
{
|
||||
"table1": [
|
||||
{
|
||||
"bezeichnung": "Temp Sensor 1",
|
||||
"adresse": "192.168.1.100",
|
||||
"type": "Temperatur",
|
||||
"faktor": "1.0",
|
||||
"mqtt": true,
|
||||
"influxdb": false
|
||||
},
|
||||
{
|
||||
"bezeichnung": "Temp Sensor 2",
|
||||
"adresse": "192.168.1.101",
|
||||
"type": "Temperatur",
|
||||
"faktor": "1.0",
|
||||
"mqtt": false,
|
||||
"influxdb": true
|
||||
}
|
||||
],
|
||||
"table2": [
|
||||
{
|
||||
"bezeichnung": "Humidity Sensor 1",
|
||||
"adresse": "192.168.1.200",
|
||||
"type": "Luftfeuchtigkeit",
|
||||
"faktor": "0.5",
|
||||
"mqtt": true,
|
||||
"influxdb": true
|
||||
}
|
||||
],
|
||||
"table3": [
|
||||
{
|
||||
"bezeichnung": "Pressure Sensor 1",
|
||||
"adresse": "192.168.1.300",
|
||||
"type": "Druck",
|
||||
"faktor": "2.0",
|
||||
"mqtt": true,
|
||||
"influxdb": false
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"mqtt_broker": "localhost",
|
||||
"mqtt_port": "1883",
|
||||
"influxdb_url": "http://localhost:8086",
|
||||
"influxdb_token": ""
|
||||
}
|
||||
}
|
||||
```
|
||||
Die komplette Konfiguration liegt in `paramod.yaml`.
|
||||
|
||||
## Lizenz
|
||||
|
||||
|
||||
+4
-1
@@ -6,6 +6,9 @@ Zusätzlich soll noch die Möglichkeit gegeben werden, über MQTT Werte zu setze
|
||||
## Technische Details
|
||||
|
||||
### Konfiguration
|
||||
Die Konfiguration soll sowohl über Webservice als auch über eine Konfigurationsdatei möglich sein. Änderungen an der Weboberfläche sollen direkten Einfluss haben und in der Konfigurationsdatei persistiert werden. Die Konfigurationsdatei soll beim Start genutzt werden.
|
||||
Die Konfiguration soll sowohl über Webservice als auch über eine Konfigurationsdatei möglich sein. Änderungen an der Weboberfläche sollen direkten Einfluss haben und in der Konfigurationsdatei persistiert werden. Die Konfigurationsdatei soll beim Start genutzt werden. Die Konfiguration soll im yaml-Format vorliegen.
|
||||
|
||||
An der Weboberfläche soll es drei Tabellen geben, um die Modbus-Einstellungen für coils, input_register und holding_register vorzunehmen. Weiterhin soll es ein Einstellungsmenü geben, um den MQTT-Broker, den Modbus-Server, den InfluxDB-Server und allgemeine Einstellungen vorzunehmen.
|
||||
In den Tabellen soll es jeweils eine Spalte mit dem aktuellen Wert geben. Dieser wird aus den Modbus-Werten auf Rust-Seite ausgelesen und wird je nach Type aus einer bzw. zwei Adressen berechnet und mit dem Faktor multipliziert. Das trifft für input_register und holding_register zu. Bei coils handelt es sich um 1-Bit Datenpunkte und damit Boolean.
|
||||
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
[default]
|
||||
loglevel = "DEBUG"
|
||||
|
||||
[mqtt]
|
||||
broker = "192.168.178.2"
|
||||
button_circulation = "zigbee2mqtt/WirelessButton"
|
||||
password = "97sm3pHNSMZ4M5qUj0x8"
|
||||
port = 1883
|
||||
user = "admin"
|
||||
|
||||
[influxdb]
|
||||
bucket = "Paradigma"
|
||||
location = "Radebeul"
|
||||
measurement = "ParadigmaModbus"
|
||||
org = "skaville"
|
||||
token = "i-sXFQbEkSC1XVzqFEaFwXwzasbsEIciVlK4SaAUOEvk0VjQPkD3fr8d7_3SPeyseTZkqj7ZMZU78b3n2F6_SQ=="
|
||||
url = "192.168.178.2:8086"
|
||||
|
||||
[modbus]
|
||||
host = "192.168.178.10"
|
||||
max_coils_addr = 8
|
||||
max_holding_addr = 61
|
||||
max_input_addr = 45
|
||||
port = 502
|
||||
|
||||
[[modbus_coils]]
|
||||
MgtSystem = {addr = 0, write = false, mqtt = false, influxdb = false, comment = "Leitsystem aktiv"}
|
||||
HK1pres = {addr = 1, write = false, mqtt = false, influxdb = false, comment = "HK1 vorhanden"}
|
||||
HK2pres = {addr = 2, write = false, mqtt = false, influxdb = false, comment = "HK2 vorhanden"}
|
||||
HK3pres = {addr = 3, write = false, mqtt = false, influxdb = false, comment = "HK3 vorhanden"}
|
||||
TWrelease = {addr = 4, write = true, mqtt = false, influxdb = false, comment = "Trinkwassererwärmung freigegeben"}
|
||||
TWlock = {addr = 5, write = true, mqtt = false, influxdb = false, comment = "Trinkwassererwärmung gesprerrt"}
|
||||
Zrelease = {addr = 6, write = true, mqtt = false, influxdb = false, comment = "Zirkulation freigegeben"}
|
||||
Zlock = {addr = 7, write = true, mqtt = false, influxdb = false, comment = "Zirkulation gesperrt"}
|
||||
SHKpres = {addr = 8, write = false, mqtt = false, influxdb = false, comment = "Schwimmbadheizkrei vorhanden"}
|
||||
|
||||
[[modbus_input_register]]
|
||||
TA = {addr = 0, type = "INT16", factor = 0.1, mqtt = true, influxdb = true}
|
||||
TV = {addr = 1, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
TR = {addr = 2, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
TWO = {addr = 3, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
TPO = {addr = 4, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
TPU = {addr = 5, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
TZR = {addr = 6, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TV2 = {addr = 7, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TR2 = {addr = 8, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
RI1 = {addr = 9, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
RI2 = {addr = 10, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TSA = {addr = 11, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
FATV = {addr = 12, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
FATR = {addr = 13, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
TVKH = {addr = 14, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TRKH = {addr = 15, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TPOKH = {addr = 16, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TV3 = {addr = 17, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TR3 = {addr = 18, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TSB = {addr = 19, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TVSB = {addr = 20, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TRSB = {addr = 21, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TWE = {addr = 22, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TWA = {addr = 23, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TWS = {addr = 24, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TVSI = {addr = 25, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TK = {addr = 26, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
FATV1 = {addr = 27, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
FATV2 = {addr = 28, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
FATV3 = {addr = 29, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
FATV4 = {addr = 30, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TSE = {addr = 31, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
TW = {addr = 32, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TSV = {addr = 33, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
TW2 = {addr = 34, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
S = {addr = 35, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
TAM = {addr = 36, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
TAM2 = {addr = 37, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TSA1 = {addr = 38, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
TSA2 = {addr = 39, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TSP = {addr = 40, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
TWW = {addr = 41, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
TKW = {addr = 42, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
VKW = {addr = 43, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
VSPm = {addr = 44, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
|
||||
[[modbus_holding_register]]
|
||||
nothing = {addr = 0, type = "UINT16", factor = 1, mqtt = false, influxdb = false}
|
||||
ErrLS = {addr = 1, type = "UINT16", factor = 1, mqtt = false, influxdb = true}
|
||||
TVsoll = {addr = 2, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
TV2soll = {addr = 3, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TV3soll = {addr = 4, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
HK1soll = {addr = 5, type = "UINT16", factor = 1, mqtt = false, influxdb = true}
|
||||
HK2soll = {addr = 6, type = "UINT16", factor = 1, mqtt = false, influxdb = false}
|
||||
HK3soll = {addr = 7, type = "UINT16", factor = 1, mqtt = false, influxdb = false}
|
||||
TWWsoll = {addr = 8, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
TV1max = {addr = 9, type = "INT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
TV2max = {addr = 10, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TV3max = {addr = 11, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
ErrHR = {addr = 12, type = "UINT16", factor = 1, mqtt = false, influxdb = true}
|
||||
ErrSR = {addr = 13, type = "UINT16", factor = 1, mqtt = false, influxdb = true}
|
||||
ErrWE1_1 = {addr = 14, type = "UINT16", factor = 1, mqtt = false, influxdb = true}
|
||||
ErrWE1_2 = {addr = 15, type = "UINT16", factor = 1, mqtt = false, influxdb = false}
|
||||
ErrWE1_3 = {addr = 16, type = "UINT16", factor = 1, mqtt = false, influxdb = false}
|
||||
ErrWE1_4 = {addr = 17, type = "UINT16", factor = 1, mqtt = false, influxdb = false}
|
||||
ErrWE1_5 = {addr = 18, type = "UINT16", factor = 1, mqtt = false, influxdb = false}
|
||||
KollLei = {addr = 19, type = "UINT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
TagesS = {addr = 20, type = "UINT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
GesS = {addr = 21, type = "UINT32", factor = 0.1, mqtt = false, influxdb = true}
|
||||
GesWW = {addr = 23, type = "UINT32", factor = 0.1, mqtt = false, influxdb = true}
|
||||
GesZ = {addr = 25, type = "UINT32", factor = 0.1, mqtt = false, influxdb = true}
|
||||
HGesK1 = {addr = 27, type = "UINT32", factor = 1, mqtt = false, influxdb = true}
|
||||
StartK1 = {addr = 29, type = "UINT32", factor = 1, mqtt = false, influxdb = true}
|
||||
HGesPel = {addr = 31, type = "UINT32", factor = 1, mqtt = false, influxdb = true}
|
||||
VGesPel = {addr = 33, type = "UINT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
StatWW = {addr = 34, type = "UINT16", factor = 1, mqtt = false, influxdb = true}
|
||||
StatZ = {addr = 35, type = "UINT16", factor = 1, mqtt = false, influxdb = true}
|
||||
StatHK1 = {addr = 36, type = "UINT16", factor = 1, mqtt = false, influxdb = true}
|
||||
StatHK2 = {addr = 37, type = "UINT16", factor = 1, mqtt = false, influxdb = false}
|
||||
StatHK3 = {addr = 38, type = "UINT16", factor = 1, mqtt = false, influxdb = false}
|
||||
StatS = {addr = 39, type = "UINT16", factor = 1, mqtt = false, influxdb = true}
|
||||
StatSB = {addr = 40, type = "UINT16", factor = 1, mqtt = false, influxdb = false}
|
||||
StatK1 = {addr = 41, type = "UINT16", factor = 1, mqtt = false, influxdb = true}
|
||||
StatPel = {addr = 42, type = "UINT16", factor = 1, mqtt = false, influxdb = true}
|
||||
StatKH = {addr = 43, type = "UINT16", factor = 1, mqtt = false, influxdb = false}
|
||||
TPOsoll = {addr = 44, type = "UINT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
FATVsoll = {addr = 45, type = "UINT16", factor = 0.1, mqtt = false, influxdb = true}
|
||||
TSBsollHK = {addr = 46, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
TSBsollS = {addr = 47, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
BetrHK1 = {addr = 48, type = "UINT16", factor = 1, mqtt = false, influxdb = true}
|
||||
BetrHK2 = {addr = 49, type = "UINT16", factor = 1, mqtt = false, influxdb = false}
|
||||
BetrHK3 = {addr = 50, type = "UINT16", factor = 1, mqtt = false, influxdb = false}
|
||||
BetrSB = {addr = 51, type = "UINT16", factor = 1, mqtt = false, influxdb = false}
|
||||
GesKKsoll = {addr = 52, type = "INT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
KKsollWE1 = {addr = 53, type = "UINT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
KKsollWE2 = {addr = 54, type = "UINT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
KKsollWE3 = {addr = 55, type = "UINT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
KKsollWE4 = {addr = 56, type = "UINT16", factor = 0.1, mqtt = false, influxdb = false}
|
||||
ErrWE1 = {addr = 57, type = "UINT16", factor = 1, mqtt = false, influxdb = false}
|
||||
ErrWE2 = {addr = 58, type = "UINT16", factor = 1, mqtt = false, influxdb = false}
|
||||
ErrWE3 = {addr = 59, type = "UINT16", factor = 1, mqtt = false, influxdb = false}
|
||||
ErrWE4 = {addr = 60, type = "UINT16", factor = 1, mqtt = false, influxdb = false}
|
||||
|
||||
+126
-130
@@ -1,140 +1,136 @@
|
||||
default:
|
||||
loglevel: "DEBUG"
|
||||
loglevel: DEBUG
|
||||
darkmode: true
|
||||
|
||||
mqtt:
|
||||
broker: "192.168.178.2"
|
||||
button_circulation: "zigbee2mqtt/WirelessButton"
|
||||
password: "97sm3pHNSMZ4M5qUj0x8"
|
||||
broker: 192.168.178.2
|
||||
port: 1883
|
||||
user: "admin"
|
||||
|
||||
user: admin
|
||||
password: 97sm3pHNSMZ4M5qUj0x8
|
||||
path: heizung/paradigma
|
||||
leitsystem_path: heizung/leitsystem2
|
||||
set_write_interval_ms: 2000
|
||||
influxdb:
|
||||
bucket: "Paradigma"
|
||||
location: "Radebeul"
|
||||
measurement: "ParadigmaModbus"
|
||||
org: "skaville"
|
||||
token: "i-sXFQbEkSC1XVzqFEaFwXwzasbsEIciVlK4SaAUOEvk0VjQPkD3fr8d7_3SPeyseTZkqj7ZMZU78b3n2F6_SQ=="
|
||||
url: "192.168.178.2:8086"
|
||||
|
||||
bucket: Paradigma
|
||||
org: skaville
|
||||
token: i-sXFQbEkSC1XVzqFEaFwXwzasbsEIciVlK4SaAUOEvk0VjQPkD3fr8d7_3SPeyseTZkqj7ZMZU78b3n2F6_SQ==
|
||||
url: http://192.168.178.2:8086
|
||||
location: Radebeul
|
||||
measurement: ParadigmaModbus
|
||||
modbus:
|
||||
host: "192.168.178.10"
|
||||
max_coils_addr: 8
|
||||
max_holding_addr: 61
|
||||
max_input_addr: 45
|
||||
host: 192.168.178.10
|
||||
port: 502
|
||||
|
||||
max_coils_addr: 8
|
||||
max_input_addr: 45
|
||||
max_holding_addr: 61
|
||||
modbus_coils:
|
||||
- MgtSystem: {addr: 0, write: false, mqtt: false, influxdb: false, comment: "Leitsystem aktiv"}
|
||||
- HK1pres: {addr: 1, write: false, mqtt: false, influxdb: false, comment: "HK1 vorhanden"}
|
||||
- HK2pres: {addr: 2, write: false, mqtt: false, influxdb: false, comment: "HK2 vorhanden"}
|
||||
- HK3pres: {addr: 3, write: false, mqtt: false, influxdb: false, comment: "HK3 vorhanden"}
|
||||
- TWrelease: {addr: 4, write: true, mqtt: false, influxdb: false, comment: "Trinkwassererwärmung freigegeben"}
|
||||
- TWlock: {addr: 5, write: true, mqtt: false, influxdb: false, comment: "Trinkwassererwärmung gesperrt"}
|
||||
- Zrelease: {addr: 6, write: true, mqtt: false, influxdb: false, comment: "Zirkulation freigegeben"}
|
||||
- Zlock: {addr: 7, write: true, mqtt: false, influxdb: false, comment: "Zirkulation gesperrt"}
|
||||
- SHKpres: {addr: 8, write: false, mqtt: false, influxdb: false, comment: "Schwimmbadheizkrei vorhanden"}
|
||||
|
||||
- MgtSystem: {addr: 0, write: false, mqtt: true, influxdb: false, comment: Leitsystem aktiv}
|
||||
- HK1pres: {addr: 1, write: false, mqtt: false, influxdb: false, comment: HK1 vorhanden}
|
||||
- HK2pres: {addr: 2, write: false, mqtt: false, influxdb: false, comment: HK2 vorhanden}
|
||||
- HK3pres: {addr: 3, write: false, mqtt: false, influxdb: false, comment: HK3 vorhanden}
|
||||
- TWrelease: {addr: 4, write: true, mqtt: false, influxdb: false, comment: Trinkwassererwärmung freigegeben}
|
||||
- TWlock: {addr: 5, write: true, mqtt: false, influxdb: false, comment: Trinkwassererwärmung gesperrt}
|
||||
- Zrelease: {addr: 6, write: true, mqtt: false, influxdb: false, comment: Zirkulation freigegeben}
|
||||
- Zlock: {addr: 7, write: true, mqtt: false, influxdb: false, comment: Zirkulation gesperrt}
|
||||
- SHKpres: {addr: 8, write: false, mqtt: false, influxdb: false, comment: Schwimmbadheizkrei vorhanden}
|
||||
modbus_input_register:
|
||||
- TA: {addr: 0, type: "INT16", factor: 0.1, mqtt: true, influxdb: true}
|
||||
- TV: {addr: 1, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TR: {addr: 2, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TWO: {addr: 3, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TPO: {addr: 4, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TPU: {addr: 5, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TZR: {addr: 6, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TV2: {addr: 7, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TR2: {addr: 8, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- RI1: {addr: 9, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- RI2: {addr: 10, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TSA: {addr: 11, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- FATV: {addr: 12, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- FATR: {addr: 13, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TVKH: {addr: 14, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TRKH: {addr: 15, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TPOKH: {addr: 16, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TV3: {addr: 17, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TR3: {addr: 18, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TSB: {addr: 19, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TVSB: {addr: 20, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TRSB: {addr: 21, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TWE: {addr: 22, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TWA: {addr: 23, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TWS: {addr: 24, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TVSI: {addr: 25, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TK: {addr: 26, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- FATV1: {addr: 27, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- FATV2: {addr: 28, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- FATV3: {addr: 29, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- FATV4: {addr: 30, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TSE: {addr: 31, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TW: {addr: 32, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TSV: {addr: 33, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TW2: {addr: 34, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- S: {addr: 35, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TAM: {addr: 36, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TAM2: {addr: 37, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TSA1: {addr: 38, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TSA2: {addr: 39, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TSP: {addr: 40, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TWW: {addr: 41, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TKW: {addr: 42, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- VKW: {addr: 43, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- VSPm: {addr: 44, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
|
||||
- TA: {addr: 0, type: INT16, factor: 0.1, mqtt: true, influxdb: true}
|
||||
- TV: {addr: 1, type: INT16, factor: 0.1, mqtt: true, influxdb: true}
|
||||
- TR: {addr: 2, type: INT16, factor: 0.1, mqtt: true, influxdb: true}
|
||||
- TWO: {addr: 3, type: INT16, factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TPO: {addr: 4, type: INT16, factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TPU: {addr: 5, type: INT16, factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TZR: {addr: 6, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TV2: {addr: 7, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TR2: {addr: 8, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- RI1: {addr: 9, type: INT16, factor: 0.1, mqtt: false, influxdb: true}
|
||||
- RI2: {addr: 10, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TSA: {addr: 11, type: INT16, factor: 0.1, mqtt: false, influxdb: true}
|
||||
- FATV: {addr: 12, type: INT16, factor: 0.1, mqtt: false, influxdb: true}
|
||||
- FATR: {addr: 13, type: INT16, factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TVKH: {addr: 14, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TRKH: {addr: 15, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TPOKH: {addr: 16, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TV3: {addr: 17, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TR3: {addr: 18, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TSB: {addr: 19, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TVSB: {addr: 20, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TRSB: {addr: 21, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TWE: {addr: 22, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TWA: {addr: 23, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TWS: {addr: 24, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TVSI: {addr: 25, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TK: {addr: 26, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- FATV1: {addr: 27, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- FATV2: {addr: 28, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- FATV3: {addr: 29, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- FATV4: {addr: 30, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TSE: {addr: 31, type: INT16, factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TW: {addr: 32, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TSV: {addr: 33, type: INT16, factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TW2: {addr: 34, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- S: {addr: 35, type: INT16, factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TAM: {addr: 36, type: INT16, factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TAM2: {addr: 37, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TSA1: {addr: 38, type: INT16, factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TSA2: {addr: 39, type: INT16, factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TSP: {addr: 40, type: INT16, factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TWW: {addr: 41, type: INT16, factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TKW: {addr: 42, type: INT16, factor: 0.1, mqtt: false, influxdb: true}
|
||||
- VKW: {addr: 43, type: INT16, factor: 0.1, mqtt: false, influxdb: true}
|
||||
- VSPm: {addr: 44, type: INT16, factor: 0.1, mqtt: false, influxdb: true}
|
||||
modbus_holding_register:
|
||||
- nothing: {addr: 0, type: "UINT16", factor: 1, mqtt: false, influxdb: false}
|
||||
- ErrLS: {addr: 1, type: "UINT16", factor: 1, mqtt: false, influxdb: true}
|
||||
- TVsoll: {addr: 2, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TV2soll: {addr: 3, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TV3soll: {addr: 4, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- HK1soll: {addr: 5, type: "UINT16", factor: 1, mqtt: false, influxdb: true}
|
||||
- HK2soll: {addr: 6, type: "UINT16", factor: 1, mqtt: false, influxdb: false}
|
||||
- HK3soll: {addr: 7, type: "UINT16", factor: 1, mqtt: false, influxdb: false}
|
||||
- TWWsoll: {addr: 8, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TV1max: {addr: 9, type: "INT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TV2max: {addr: 10, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TV3max: {addr: 11, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- ErrHR: {addr: 12, type: "UINT16", factor: 1, mqtt: false, influxdb: true}
|
||||
- ErrSR: {addr: 13, type: "UINT16", factor: 1, mqtt: false, influxdb: true}
|
||||
- ErrWE1_1: {addr: 14, type: "UINT16", factor: 1, mqtt: false, influxdb: true}
|
||||
- ErrWE1_2: {addr: 15, type: "UINT16", factor: 1, mqtt: false, influxdb: false}
|
||||
- ErrWE1_3: {addr: 16, type: "UINT16", factor: 1, mqtt: false, influxdb: false}
|
||||
- ErrWE1_4: {addr: 17, type: "UINT16", factor: 1, mqtt: false, influxdb: false}
|
||||
- ErrWE1_5: {addr: 18, type: "UINT16", factor: 1, mqtt: false, influxdb: false}
|
||||
- KollLei: {addr: 19, type: "UINT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TagesS: {addr: 20, type: "UINT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- GesS: {addr: 21, type: "UINT32", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- GesWW: {addr: 23, type: "UINT32", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- GesZ: {addr: 25, type: "UINT32", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- HGesK1: {addr: 27, type: "UINT32", factor: 1, mqtt: false, influxdb: true}
|
||||
- StartK1: {addr: 29, type: "UINT32", factor: 1, mqtt: false, influxdb: true}
|
||||
- HGesPel: {addr: 31, type: "UINT32", factor: 1, mqtt: false, influxdb: true}
|
||||
- VGesPel: {addr: 33, type: "UINT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- StatWW: {addr: 34, type: "UINT16", factor: 1, mqtt: false, influxdb: true}
|
||||
- StatZ: {addr: 35, type: "UINT16", factor: 1, mqtt: false, influxdb: true}
|
||||
- StatHK1: {addr: 36, type: "UINT16", factor: 1, mqtt: false, influxdb: true}
|
||||
- StatHK2: {addr: 37, type: "UINT16", factor: 1, mqtt: false, influxdb: false}
|
||||
- StatHK3: {addr: 38, type: "UINT16", factor: 1, mqtt: false, influxdb: false}
|
||||
- StatS: {addr: 39, type: "UINT16", factor: 1, mqtt: false, influxdb: true}
|
||||
- StatSB: {addr: 40, type: "UINT16", factor: 1, mqtt: false, influxdb: false}
|
||||
- StatK1: {addr: 41, type: "UINT16", factor: 1, mqtt: false, influxdb: true}
|
||||
- StatPel: {addr: 42, type: "UINT16", factor: 1, mqtt: false, influxdb: true}
|
||||
- StatKH: {addr: 43, type: "UINT16", factor: 1, mqtt: false, influxdb: false}
|
||||
- TPOsoll: {addr: 44, type: "UINT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- FATVsoll: {addr: 45, type: "UINT16", factor: 0.1, mqtt: false, influxdb: true}
|
||||
- TSBsollHK: {addr: 46, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- TSBsollS: {addr: 47, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- BetrHK1: {addr: 48, type: "UINT16", factor: 1, mqtt: false, influxdb: true}
|
||||
- BetrHK2: {addr: 49, type: "UINT16", factor: 1, mqtt: false, influxdb: false}
|
||||
- BetrHK3: {addr: 50, type: "UINT16", factor: 1, mqtt: false, influxdb: false}
|
||||
- BetrSB: {addr: 51, type: "UINT16", factor: 1, mqtt: false, influxdb: false}
|
||||
- GesKKsoll: {addr: 52, type: "INT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- KKsollWE1: {addr: 53, type: "UINT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- KKsollWE2: {addr: 54, type: "UINT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- KKsollWE3: {addr: 55, type: "UINT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- KKsollWE4: {addr: 56, type: "UINT16", factor: 0.1, mqtt: false, influxdb: false}
|
||||
- ErrWE1: {addr: 57, type: "UINT16", factor: 1, mqtt: false, influxdb: false}
|
||||
- ErrWE2: {addr: 58, type: "UINT16", factor: 1, mqtt: false, influxdb: false}
|
||||
- ErrWE3: {addr: 59, type: "UINT16", factor: 1, mqtt: false, influxdb: false}
|
||||
- ErrWE4: {addr: 60, type: "UINT16", factor: 1, mqtt: false, influxdb: false}
|
||||
- nothing: {addr: 0, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||
- ErrLS: {addr: 1, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||
- TVsoll: {addr: 2, type: INT16, factor: 0.1, write: true, mqtt: true, influxdb: true}
|
||||
- TV2soll: {addr: 3, type: INT16, factor: 0.1, write: false, mqtt: false, influxdb: false}
|
||||
- TV3soll: {addr: 4, type: INT16, factor: 0.1, write: false, mqtt: false, influxdb: false}
|
||||
- HK1soll: {addr: 5, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||
- HK2soll: {addr: 6, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||
- HK3soll: {addr: 7, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||
- TWWsoll: {addr: 8, type: INT16, factor: 0.1, write: false, mqtt: false, influxdb: true}
|
||||
- TV1max: {addr: 9, type: INT16, factor: 0.1, write: false, mqtt: false, influxdb: true}
|
||||
- TV2max: {addr: 10, type: INT16, factor: 0.1, write: false, mqtt: false, influxdb: false}
|
||||
- TV3max: {addr: 11, type: INT16, factor: 0.1, write: false, mqtt: false, influxdb: false}
|
||||
- ErrHR: {addr: 12, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||
- ErrSR: {addr: 13, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||
- ErrWE1_1: {addr: 14, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||
- ErrWE1_2: {addr: 15, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||
- ErrWE1_3: {addr: 16, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||
- ErrWE1_4: {addr: 17, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||
- ErrWE1_5: {addr: 18, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||
- KollLei: {addr: 19, type: UINT16, factor: 0.1, write: false, mqtt: false, influxdb: true}
|
||||
- TagesS: {addr: 20, type: UINT16, factor: 0.1, write: false, mqtt: true, influxdb: true}
|
||||
- GesS: {addr: 21, type: UINT32, factor: 0.1, write: false, mqtt: false, influxdb: true}
|
||||
- GesWW: {addr: 23, type: UINT32, factor: 0.1, write: false, mqtt: false, influxdb: true}
|
||||
- GesZ: {addr: 25, type: UINT32, factor: 0.1, write: false, mqtt: false, influxdb: true}
|
||||
- HGesK1: {addr: 27, type: UINT32, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||
- StartK1: {addr: 29, type: UINT32, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||
- HGesPel: {addr: 31, type: UINT32, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||
- VGesPel: {addr: 33, type: UINT16, factor: 0.1, write: false, mqtt: false, influxdb: true}
|
||||
- StatWW: {addr: 34, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||
- StatZ: {addr: 35, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||
- StatHK1: {addr: 36, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||
- StatHK2: {addr: 37, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||
- StatHK3: {addr: 38, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||
- StatS: {addr: 39, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||
- StatSB: {addr: 40, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||
- StatK1: {addr: 41, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||
- StatPel: {addr: 42, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||
- StatKH: {addr: 43, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||
- TPOsoll: {addr: 44, type: UINT16, factor: 0.1, write: false, mqtt: false, influxdb: true}
|
||||
- FATVsoll: {addr: 45, type: UINT16, factor: 0.1, write: false, mqtt: false, influxdb: true}
|
||||
- TSBsollHK: {addr: 46, type: INT16, factor: 0.1, write: false, mqtt: false, influxdb: false}
|
||||
- TSBsollS: {addr: 47, type: INT16, factor: 0.1, write: false, mqtt: false, influxdb: false}
|
||||
- BetrHK1: {addr: 48, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||
- BetrHK2: {addr: 49, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||
- BetrHK3: {addr: 50, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||
- BetrSB: {addr: 51, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||
- GesKKsoll: {addr: 52, type: INT16, factor: 0.1, write: false, mqtt: false, influxdb: false}
|
||||
- KKsollWE1: {addr: 53, type: UINT16, factor: 0.1, write: false, mqtt: false, influxdb: false}
|
||||
- KKsollWE2: {addr: 54, type: UINT16, factor: 0.1, write: false, mqtt: false, influxdb: false}
|
||||
- KKsollWE3: {addr: 55, type: UINT16, factor: 0.1, write: false, mqtt: false, influxdb: false}
|
||||
- KKsollWE4: {addr: 56, type: UINT16, factor: 0.1, write: false, mqtt: false, influxdb: false}
|
||||
- ErrWE1: {addr: 57, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||
- ErrWE2: {addr: 58, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||
- ErrWE3: {addr: 59, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||
- ErrWE4: {addr: 60, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||
@@ -0,0 +1,33 @@
|
||||
use std::sync::{Mutex, Arc};
|
||||
use tera::Tera;
|
||||
use crate::config::{AppConfig, ModbusValueMaps};
|
||||
|
||||
pub struct AppState {
|
||||
pub config: Arc<Mutex<AppConfig>>,
|
||||
pub value_maps: Arc<Mutex<ModbusValueMaps>>,
|
||||
pub templates: Tera,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub fn load_from_conf(conf_path: &str) -> Self {
|
||||
let conf_str = std::fs::read_to_string(conf_path).expect("Config-Datei konnte nicht gelesen werden");
|
||||
|
||||
let config: AppConfig = serde_yaml::from_str(&conf_str).expect("Config-Deserialisierung fehlgeschlagen");
|
||||
let config = Arc::new(Mutex::new(config));
|
||||
let value_maps = Arc::new(Mutex::new(ModbusValueMaps::from_config(&config.lock().unwrap())));
|
||||
|
||||
let tera = match Tera::new("templates/**/*") {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
println!("Template parsing error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
AppState {
|
||||
config,
|
||||
value_maps,
|
||||
templates: tera,
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-19
@@ -1,5 +1,6 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use crate::modbus_types::{ModbusCoilsConfig, ModbusInputRegisterConfig, ModbusHoldingRegisterConfig};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct DefaultConfig {
|
||||
@@ -22,7 +23,9 @@ pub struct MqttConfig {
|
||||
pub port: u16,
|
||||
pub user: Option<String>,
|
||||
pub password: Option<String>,
|
||||
pub button_circulation: Option<String>,
|
||||
pub path: Option<String>,
|
||||
pub leitsystem_path: Option<String>,
|
||||
pub set_write_interval_ms: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
@@ -35,15 +38,7 @@ pub struct InfluxConfig {
|
||||
pub measurement: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct ModbusRegisterConfig {
|
||||
pub addr: u16,
|
||||
pub r#type: Option<String>,
|
||||
pub factor: Option<f64>,
|
||||
pub mqtt: Option<bool>,
|
||||
pub influxdb: Option<bool>,
|
||||
pub comment: Option<String>,
|
||||
}
|
||||
// ModbusRegisterConfig und ModbusCoilsConfig werden jetzt aus modbus_types.rs importiert
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct AppConfig {
|
||||
@@ -51,16 +46,16 @@ pub struct AppConfig {
|
||||
pub mqtt: MqttConfig,
|
||||
pub influxdb: InfluxConfig,
|
||||
pub modbus: ModbusConfig,
|
||||
pub modbus_coils: Option<Vec<HashMap<String, ModbusRegisterConfig>>>,
|
||||
pub modbus_input_register: Option<Vec<HashMap<String, ModbusRegisterConfig>>>,
|
||||
pub modbus_holding_register: Option<Vec<HashMap<String, ModbusRegisterConfig>>>,
|
||||
pub modbus_coils: Option<Vec<HashMap<String, ModbusCoilsConfig>>>,
|
||||
pub modbus_input_register: Option<Vec<HashMap<String, ModbusInputRegisterConfig>>>,
|
||||
pub modbus_holding_register: Option<Vec<HashMap<String, ModbusHoldingRegisterConfig>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct ModbusValueMaps {
|
||||
pub modbus_coils_values: HashMap<String, f64>,
|
||||
pub modbus_input_register_values: HashMap<String, f64>,
|
||||
pub modbus_holding_register_values: HashMap<String, f64>,
|
||||
pub modbus_coils_values: HashMap<String, Option<f64>>,
|
||||
pub modbus_input_register_values: HashMap<String, Option<f64>>,
|
||||
pub modbus_holding_register_values: HashMap<String, Option<f64>>,
|
||||
}
|
||||
|
||||
impl ModbusValueMaps {
|
||||
@@ -69,7 +64,7 @@ impl ModbusValueMaps {
|
||||
if let Some(ref vec) = config.modbus_coils {
|
||||
for map in vec {
|
||||
for key in map.keys() {
|
||||
coils.entry(key.clone()).or_insert(0.0);
|
||||
coils.entry(key.clone()).or_insert(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,7 +72,7 @@ impl ModbusValueMaps {
|
||||
if let Some(ref vec) = config.modbus_input_register {
|
||||
for map in vec {
|
||||
for key in map.keys() {
|
||||
input.entry(key.clone()).or_insert(0.0);
|
||||
input.entry(key.clone()).or_insert(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,7 +80,7 @@ impl ModbusValueMaps {
|
||||
if let Some(ref vec) = config.modbus_holding_register {
|
||||
for map in vec {
|
||||
for key in map.keys() {
|
||||
holding.entry(key.clone()).or_insert(0.0);
|
||||
holding.entry(key.clone()).or_insert(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use crate::config::{AppConfig, ModbusValueMaps};
|
||||
use influxdb::{Client, WriteQuery, Timestamp};
|
||||
use chrono::Utc;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
pub fn start_influx_thread(config: Arc<Mutex<AppConfig>>, values: Arc<Mutex<ModbusValueMaps>>) {
|
||||
let influx_config = {
|
||||
let cfg = config.lock().unwrap();
|
||||
cfg.influxdb.clone()
|
||||
};
|
||||
let url = influx_config.url.clone();
|
||||
let bucket = influx_config.bucket.clone();
|
||||
let _org = influx_config.org.clone();
|
||||
let token = influx_config.token.clone();
|
||||
let measurement = influx_config.measurement.clone().unwrap_or_else(|| "modbus".to_string());
|
||||
let location = influx_config.location.clone().unwrap_or_else(|| "default".to_string());
|
||||
|
||||
thread::spawn(move || {
|
||||
let client = Client::new(url, bucket).with_token(token);
|
||||
let rt = Runtime::new().unwrap();
|
||||
loop {
|
||||
let mut points = Vec::new();
|
||||
{
|
||||
let values = values.lock().unwrap();
|
||||
// Input Register
|
||||
for (name, val) in &values.modbus_input_register_values {
|
||||
if let Some(v) = val {
|
||||
if should_write(&config, name, "input_register") {
|
||||
let point = WriteQuery::new(Timestamp::from(Utc::now()), measurement.clone())
|
||||
.add_field(name, *v)
|
||||
// .add_tag("type", "input_register")
|
||||
.add_tag("location", location.as_str())
|
||||
.to_owned();
|
||||
points.push(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Holding Register
|
||||
for (name, val) in &values.modbus_holding_register_values {
|
||||
if let Some(v) = val {
|
||||
if should_write(&config, name, "holding_register") {
|
||||
let point = WriteQuery::new(Timestamp::from(Utc::now()), measurement.clone())
|
||||
.add_field(name, *v)
|
||||
// .add_tag("type", "holding_register")
|
||||
.add_tag("location", location.as_str())
|
||||
.to_owned();
|
||||
points.push(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Coils
|
||||
for (name, val) in &values.modbus_coils_values {
|
||||
if let Some(v) = val {
|
||||
if should_write(&config, name, "coils") {
|
||||
let point = WriteQuery::new(Timestamp::from(Utc::now()), measurement.clone())
|
||||
.add_field(name, *v)
|
||||
// .add_tag("type", "coils")
|
||||
.add_tag("location", location.as_str())
|
||||
.to_owned();
|
||||
points.push(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !points.is_empty() {
|
||||
let res = rt.block_on(client.query(points));
|
||||
if let Err(e) = res {
|
||||
eprintln!("Influx write error: {}", e);
|
||||
}
|
||||
}
|
||||
thread::sleep(Duration::from_secs(10));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn should_write(config: &Arc<Mutex<AppConfig>>, name: &str, reg_type: &str) -> bool {
|
||||
let cfg = config.lock().unwrap();
|
||||
match reg_type {
|
||||
"input_register" => {
|
||||
if let Some(regs) = &cfg.modbus_input_register {
|
||||
for map in regs {
|
||||
if let Some(reg) = map.get(name) {
|
||||
return reg.influxdb.unwrap_or(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"holding_register" => {
|
||||
if let Some(regs) = &cfg.modbus_holding_register {
|
||||
for map in regs {
|
||||
if let Some(reg) = map.get(name) {
|
||||
return reg.influxdb.unwrap_or(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"coils" => {
|
||||
if let Some(coils) = &cfg.modbus_coils {
|
||||
for map in coils {
|
||||
if let Some(coil) = map.get(name) {
|
||||
return coil.influxdb.unwrap_or(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
false
|
||||
}
|
||||
+130
-84
@@ -1,43 +1,23 @@
|
||||
use actix_web::{web, App, HttpResponse, HttpServer, Result};
|
||||
use actix_files as actix_fs;
|
||||
use std::sync::{Mutex, Arc};
|
||||
use tera::{Context, Tera};
|
||||
use std::sync::{Arc};
|
||||
use tera::Context;
|
||||
use std::fs;
|
||||
use serde_yaml;
|
||||
use std::collections::HashMap;
|
||||
use serde::{Serialize, Deserialize};
|
||||
mod config;
|
||||
mod modbus;
|
||||
use crate::config::{AppConfig, ModbusRegisterConfig, ModbusValueMaps};
|
||||
mod app_state;
|
||||
mod mqtt;
|
||||
mod influx;
|
||||
pub mod modbus_types;
|
||||
use crate::config::{AppConfig, ModbusValueMaps};
|
||||
use crate::modbus_types::{ModbusInputRegisterConfig, ModbusHoldingRegisterConfig, ModbusCoilsConfig};
|
||||
use crate::app_state::AppState;
|
||||
|
||||
|
||||
struct AppState {
|
||||
config: Mutex<AppConfig>,
|
||||
value_maps: Arc<Mutex<ModbusValueMaps>>,
|
||||
templates: Tera,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
fn load_from_conf(conf_path: &str) -> Self {
|
||||
let conf_str = std::fs::read_to_string(conf_path).expect("Config-Datei konnte nicht gelesen werden");
|
||||
let config: AppConfig = serde_yaml::from_str(&conf_str).expect("Config-Deserialisierung fehlgeschlagen");
|
||||
|
||||
let value_maps = Arc::new(Mutex::new(ModbusValueMaps::from_config(&config)));
|
||||
|
||||
let tera = match Tera::new("templates/**/*") {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
println!("Template parsing error: {}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
AppState {
|
||||
config: Mutex::new(config),
|
||||
value_maps,
|
||||
templates: tera,
|
||||
}
|
||||
}
|
||||
fn get_config_path() -> String {
|
||||
std::env::var("CONFIG_PATH").unwrap_or_else(|_| "paramod.yaml".to_string())
|
||||
}
|
||||
|
||||
async fn index(data: web::Data<AppState>) -> Result<HttpResponse> {
|
||||
@@ -67,37 +47,63 @@ async fn table_page(data: web::Data<AppState>, path: web::Path<String>) -> Resul
|
||||
let config = data.config.lock().unwrap();
|
||||
let value_maps = data.value_maps.lock().unwrap();
|
||||
let mut context = Context::new();
|
||||
let (rows, value_map) = match table_id.as_str() {
|
||||
"modbus_input_register" => (
|
||||
config.modbus_input_register.clone().unwrap_or_default(),
|
||||
&value_maps.modbus_input_register_values
|
||||
),
|
||||
"modbus_holding_register" => (
|
||||
config.modbus_holding_register.clone().unwrap_or_default(),
|
||||
&value_maps.modbus_holding_register_values
|
||||
),
|
||||
"modbus_coils" => (
|
||||
config.modbus_coils.clone().unwrap_or_default(),
|
||||
&value_maps.modbus_coils_values
|
||||
),
|
||||
_ => return Ok(HttpResponse::NotFound().body("Table not found")),
|
||||
};
|
||||
let mut sorted_rows = rows;
|
||||
sorted_rows.sort_by(|a, b| {
|
||||
let addr_a = a.values().next().map(|v| v.addr).unwrap_or(0);
|
||||
let addr_b = b.values().next().map(|v| v.addr).unwrap_or(0);
|
||||
addr_a.cmp(&addr_b)
|
||||
});
|
||||
context.insert("rows", &sorted_rows);
|
||||
context.insert("table_id", &table_id);
|
||||
context.insert("active_page", &table_id);
|
||||
context.insert("value_map", value_map);
|
||||
let html = data.templates.render("index.html", &context)
|
||||
.map_err(|e| {
|
||||
eprintln!("Template error: {}", e);
|
||||
actix_web::error::ErrorInternalServerError("Template error")
|
||||
})?;
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(html))
|
||||
match table_id.as_str() {
|
||||
"modbus_input_register" => {
|
||||
let mut rows = config.modbus_input_register.clone().unwrap_or_default();
|
||||
rows.sort_by(|a, b| {
|
||||
let addr_a = a.values().next().map(|v| v.addr).unwrap_or(0);
|
||||
let addr_b = b.values().next().map(|v| v.addr).unwrap_or(0);
|
||||
addr_a.cmp(&addr_b)
|
||||
});
|
||||
context.insert("rows", &rows);
|
||||
context.insert("table_id", &table_id);
|
||||
context.insert("active_page", &table_id);
|
||||
context.insert("value_map", &value_maps.modbus_input_register_values);
|
||||
let html = data.templates.render("index.html", &context)
|
||||
.map_err(|e| {
|
||||
eprintln!("Template error: {}", e);
|
||||
actix_web::error::ErrorInternalServerError("Template error")
|
||||
})?;
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(html))
|
||||
}
|
||||
"modbus_holding_register" => {
|
||||
let mut rows = config.modbus_holding_register.clone().unwrap_or_default();
|
||||
rows.sort_by(|a, b| {
|
||||
let addr_a = a.values().next().map(|v| v.addr).unwrap_or(0);
|
||||
let addr_b = b.values().next().map(|v| v.addr).unwrap_or(0);
|
||||
addr_a.cmp(&addr_b)
|
||||
});
|
||||
context.insert("rows", &rows);
|
||||
context.insert("table_id", &table_id);
|
||||
context.insert("active_page", &table_id);
|
||||
context.insert("value_map", &value_maps.modbus_holding_register_values);
|
||||
let html = data.templates.render("index.html", &context)
|
||||
.map_err(|e| {
|
||||
eprintln!("Template error: {}", e);
|
||||
actix_web::error::ErrorInternalServerError("Template error")
|
||||
})?;
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(html))
|
||||
}
|
||||
"modbus_coils" => {
|
||||
let mut rows = config.modbus_coils.clone().unwrap_or_default();
|
||||
rows.sort_by(|a, b| {
|
||||
let addr_a = a.values().next().map(|v| v.addr).unwrap_or(0);
|
||||
let addr_b = b.values().next().map(|v| v.addr).unwrap_or(0);
|
||||
addr_a.cmp(&addr_b)
|
||||
});
|
||||
context.insert("rows", &rows);
|
||||
context.insert("table_id", &table_id);
|
||||
context.insert("active_page", &table_id);
|
||||
context.insert("value_map", &value_maps.modbus_coils_values);
|
||||
let html = data.templates.render("index.html", &context)
|
||||
.map_err(|e| {
|
||||
eprintln!("Template error: {}", e);
|
||||
actix_web::error::ErrorInternalServerError("Template error")
|
||||
})?;
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(html))
|
||||
}
|
||||
_ => Ok(HttpResponse::NotFound().body("Table not found")),
|
||||
}
|
||||
}
|
||||
|
||||
async fn settings_page(data: web::Data<AppState>) -> Result<HttpResponse> {
|
||||
@@ -117,7 +123,10 @@ async fn settings_page(data: web::Data<AppState>) -> Result<HttpResponse> {
|
||||
eprintln!("Template error: {}", e);
|
||||
actix_web::error::ErrorInternalServerError("Template error")
|
||||
})?;
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(html))
|
||||
Ok(HttpResponse::Ok()
|
||||
.content_type("text/html")
|
||||
.insert_header(("Cache-Control", "no-store, must-revalidate"))
|
||||
.body(html))
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +134,7 @@ async fn settings_page(data: web::Data<AppState>) -> Result<HttpResponse> {
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct SaveTableRequest {
|
||||
table_id: String,
|
||||
rows: Vec<HashMap<String, ModbusRegisterConfig>>,
|
||||
rows: serde_json::Value,
|
||||
}
|
||||
|
||||
async fn save_table(
|
||||
@@ -133,24 +142,39 @@ async fn save_table(
|
||||
req: web::Json<SaveTableRequest>,
|
||||
) -> Result<HttpResponse> {
|
||||
let mut config = data.config.lock().unwrap();
|
||||
let conf_path = "paramod.yaml";
|
||||
let key = match req.table_id.as_str() {
|
||||
"modbus_input_register" => "modbus_input_register",
|
||||
"modbus_holding_register" => "modbus_holding_register",
|
||||
"modbus_coils" => "modbus_coils",
|
||||
let conf_path = get_config_path();
|
||||
match req.table_id.as_str() {
|
||||
"modbus_input_register" => {
|
||||
let rows: Vec<HashMap<String, ModbusInputRegisterConfig>> =
|
||||
match serde_json::from_value(req.rows.clone()) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Ok(HttpResponse::BadRequest().body(format!("Falscher Typ für input_register: {}", e))),
|
||||
};
|
||||
config.modbus_input_register = Some(rows);
|
||||
}
|
||||
"modbus_holding_register" => {
|
||||
let rows: Vec<HashMap<String, ModbusHoldingRegisterConfig>> =
|
||||
match serde_json::from_value(req.rows.clone()) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Ok(HttpResponse::BadRequest().body(format!("Falscher Typ für holding_register: {}", e))),
|
||||
};
|
||||
config.modbus_holding_register = Some(rows);
|
||||
}
|
||||
"modbus_coils" => {
|
||||
let rows: Vec<HashMap<String, ModbusCoilsConfig>> =
|
||||
match serde_json::from_value(req.rows.clone()) {
|
||||
Ok(v) => v,
|
||||
Err(e) => return Ok(HttpResponse::BadRequest().body(format!("Falscher Typ für coils: {}", e))),
|
||||
};
|
||||
config.modbus_coils = Some(rows);
|
||||
}
|
||||
_ => return Ok(HttpResponse::BadRequest().body("Invalid table_id")),
|
||||
};
|
||||
match key {
|
||||
"modbus_input_register" => config.modbus_input_register = Some(req.rows.clone()),
|
||||
"modbus_holding_register" => config.modbus_holding_register = Some(req.rows.clone()),
|
||||
"modbus_coils" => config.modbus_coils = Some(req.rows.clone()),
|
||||
_ => {}
|
||||
}
|
||||
let yaml_str = match serde_yaml::to_string(&*config) {
|
||||
Ok(s) => s,
|
||||
Err(e) => return Ok(HttpResponse::InternalServerError().body(format!("Serialisierungsfehler: {}", e))),
|
||||
};
|
||||
if let Err(e) = fs::write(conf_path, yaml_str) {
|
||||
if let Err(e) = fs::write(&conf_path, yaml_str) {
|
||||
return Ok(HttpResponse::InternalServerError().body(format!("Fehler beim Schreiben: {}", e)));
|
||||
}
|
||||
Ok(HttpResponse::Ok().body("success"))
|
||||
@@ -175,15 +199,26 @@ async fn save_settings(
|
||||
// Value-Maps neu initialisieren
|
||||
let mut value_maps = data.value_maps.lock().unwrap();
|
||||
*value_maps = ModbusValueMaps::from_config(&new_config);
|
||||
*config = new_config;
|
||||
let conf_path = "paramod.yaml";
|
||||
let yaml_str = match serde_yaml::to_string(&*config) {
|
||||
let conf_path = get_config_path();
|
||||
let yaml_str = match serde_yaml::to_string(&new_config) {
|
||||
Ok(s) => s,
|
||||
Err(e) => return Ok(HttpResponse::InternalServerError().body(format!("Serialisierungsfehler: {}", e))),
|
||||
};
|
||||
if let Err(e) = fs::write(conf_path, yaml_str) {
|
||||
if let Err(e) = fs::write(&conf_path, yaml_str) {
|
||||
return Ok(HttpResponse::InternalServerError().body(format!("Fehler beim Schreiben: {}", e)));
|
||||
}
|
||||
// Reload config from file
|
||||
let conf_str = match std::fs::read_to_string(&conf_path) {
|
||||
Ok(s) => s,
|
||||
Err(e) => return Ok(HttpResponse::InternalServerError().body(format!("Fehler beim Lesen: {}", e))),
|
||||
};
|
||||
let updated_config: AppConfig = match serde_yaml::from_str(&conf_str) {
|
||||
Ok(cfg) => cfg,
|
||||
Err(e) => return Ok(HttpResponse::InternalServerError().body(format!("Deserialisierungsfehler: {}", e))),
|
||||
};
|
||||
// darkmode-Konvertierung: String zu bool
|
||||
// (YAML kann bool als string interpretieren)
|
||||
*config = updated_config;
|
||||
Ok(HttpResponse::Ok().body("success"))
|
||||
}
|
||||
|
||||
@@ -195,9 +230,8 @@ async fn get_config(data: web::Data<AppState>) -> HttpResponse {
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
|
||||
|
||||
let config_path = "paramod.yaml";
|
||||
let app_state = web::Data::new(AppState::load_from_conf(config_path));
|
||||
let config_path = get_config_path();
|
||||
let app_state = web::Data::new(AppState::load_from_conf(&config_path));
|
||||
|
||||
// Starte Modbus-Polling-Thread
|
||||
{
|
||||
@@ -209,10 +243,22 @@ async fn main() -> std::io::Result<()> {
|
||||
&config.modbus_holding_register,
|
||||
&config.modbus_coils,
|
||||
value_maps,
|
||||
std::time::Duration::from_secs(2), // Poll-Intervall
|
||||
std::time::Duration::from_secs(10), // Poll-Intervall
|
||||
);
|
||||
}
|
||||
|
||||
// Starte MQTT-Thread
|
||||
{
|
||||
let value_maps = Arc::clone(&app_state.value_maps);
|
||||
mqtt::start_mqtt_thread(Arc::clone(&app_state.config), value_maps);
|
||||
}
|
||||
|
||||
// Starte InfluxDB-Thread
|
||||
{
|
||||
let value_maps = Arc::clone(&app_state.value_maps);
|
||||
influx::start_influx_thread(Arc::clone(&app_state.config), value_maps);
|
||||
}
|
||||
|
||||
println!("Server läuft auf http://0.0.0.0:8080");
|
||||
|
||||
HttpServer::new(move || {
|
||||
|
||||
+200
-42
@@ -1,66 +1,224 @@
|
||||
use crate::config::{ModbusValueMaps, ModbusRegisterConfig, ModbusConfig};
|
||||
use crate::config::{ModbusValueMaps, ModbusConfig};
|
||||
use crate::modbus_types::{ModbusInputRegisterConfig, ModbusHoldingRegisterConfig, ModbusCoilsConfig};
|
||||
use crate::config::AppConfig;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use tokio_modbus::prelude::*;
|
||||
use tokio_modbus::client::tcp;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
pub fn write_value_by_name(config: &AppConfig, name: &str, payload: &str) -> Result<(), String> {
|
||||
let addr: SocketAddr = format!("{}:{}", config.modbus.host, config.modbus.port)
|
||||
.parse()
|
||||
.map_err(|e| format!("Ungültige Modbus-Adresse: {}", e))?;
|
||||
|
||||
if let Some(ref coils) = config.modbus_coils {
|
||||
for map in coils {
|
||||
if let Some(coil) = map.get(name) {
|
||||
if !coil.write.unwrap_or(false) {
|
||||
return Err(format!("Schreiben für '{}' nicht erlaubt", name));
|
||||
}
|
||||
let bool_value = parse_bool_payload(payload)?;
|
||||
let rt = tokio::runtime::Runtime::new()
|
||||
.map_err(|e| format!("Tokio Runtime Fehler: {}", e))?;
|
||||
rt.block_on(async {
|
||||
let mut client = tcp::connect_slave(addr, 1u8.into())
|
||||
.await
|
||||
.map_err(|e| format!("Modbus Verbindung fehlgeschlagen: {}", e))?;
|
||||
client
|
||||
.write_single_coil(coil.addr, bool_value)
|
||||
.await
|
||||
.map_err(|e| format!("Coil schreiben fehlgeschlagen: {}", e))
|
||||
})?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref holding_registers) = config.modbus_holding_register {
|
||||
for map in holding_registers {
|
||||
if let Some(reg) = map.get(name) {
|
||||
if !reg.write.unwrap_or(false) {
|
||||
return Err(format!("Schreiben für '{}' nicht erlaubt", name));
|
||||
}
|
||||
let numeric_value = payload
|
||||
.trim()
|
||||
.parse::<f64>()
|
||||
.map_err(|e| format!("Ungültiger Zahlenwert '{}': {}", payload, e))?;
|
||||
let factor = reg.factor.unwrap_or(1.0);
|
||||
let raw = if factor == 0.0 {
|
||||
numeric_value
|
||||
} else {
|
||||
numeric_value / factor
|
||||
};
|
||||
|
||||
let rt = tokio::runtime::Runtime::new()
|
||||
.map_err(|e| format!("Tokio Runtime Fehler: {}", e))?;
|
||||
|
||||
return rt.block_on(async {
|
||||
let mut client = tcp::connect_slave(addr, 1u8.into())
|
||||
.await
|
||||
.map_err(|e| format!("Modbus Verbindung fehlgeschlagen: {}", e))?;
|
||||
|
||||
match reg.r#type.as_deref().unwrap_or("UINT16") {
|
||||
"INT16" => {
|
||||
let int_value = raw.round();
|
||||
if int_value < i16::MIN as f64 || int_value > i16::MAX as f64 {
|
||||
return Err(format!("Wert außerhalb INT16-Bereich für '{}': {}", name, numeric_value));
|
||||
}
|
||||
let register = int_value as i16 as u16;
|
||||
client
|
||||
.write_multiple_registers(reg.addr, &[register])
|
||||
.await
|
||||
.map_err(|e| format!("Holding Register schreiben fehlgeschlagen: {}", e))?;
|
||||
}
|
||||
"UINT32" => {
|
||||
let int_value = raw.round();
|
||||
if int_value < 0.0 || int_value > u32::MAX as f64 {
|
||||
return Err(format!("Wert außerhalb UINT32-Bereich für '{}': {}", name, numeric_value));
|
||||
}
|
||||
let val = int_value as u32;
|
||||
let high = ((val >> 16) & 0xFFFF) as u16;
|
||||
let low = (val & 0xFFFF) as u16;
|
||||
client
|
||||
.write_multiple_registers(reg.addr, &[high, low])
|
||||
.await
|
||||
.map_err(|e| format!("Holding Register (UINT32) schreiben fehlgeschlagen: {}", e))?;
|
||||
}
|
||||
_ => {
|
||||
let int_value = raw.round();
|
||||
if int_value < 0.0 || int_value > u16::MAX as f64 {
|
||||
return Err(format!("Wert außerhalb UINT16-Bereich für '{}': {}", name, numeric_value));
|
||||
}
|
||||
client
|
||||
.write_multiple_registers(reg.addr, &[int_value as u16])
|
||||
.await
|
||||
.map_err(|e| format!("Holding Register schreiben fehlgeschlagen: {}", e))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(format!("Kein schreibbares Modbus-Mapping für '{}' gefunden", name))
|
||||
}
|
||||
|
||||
fn parse_bool_payload(payload: &str) -> Result<bool, String> {
|
||||
match payload.trim().to_ascii_lowercase().as_str() {
|
||||
"1" | "true" | "on" => Ok(true),
|
||||
"0" | "false" | "off" => Ok(false),
|
||||
_ => Err(format!("Ungültiger bool-Wert '{}', erwartet 0/1 oder true/false", payload)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Startet einen Thread, der zyklisch Modbus-Register abfragt und ModbusValueMaps aktualisiert
|
||||
pub fn start_modbus_polling_thread(
|
||||
_modbus_config: &ModbusConfig,
|
||||
input_registers: &Option<Vec<HashMap<String, ModbusRegisterConfig>>>,
|
||||
holding_registers: &Option<Vec<HashMap<String, ModbusRegisterConfig>>>,
|
||||
coils: &Option<Vec<HashMap<String, ModbusRegisterConfig>>>,
|
||||
modbus_config: &ModbusConfig,
|
||||
input_registers: &Option<Vec<HashMap<String, ModbusInputRegisterConfig>>>,
|
||||
holding_registers: &Option<Vec<HashMap<String, ModbusHoldingRegisterConfig>>>,
|
||||
coils: &Option<Vec<HashMap<String, ModbusCoilsConfig>>>,
|
||||
value_maps: Arc<Mutex<ModbusValueMaps>>,
|
||||
poll_interval: Duration,
|
||||
) {
|
||||
let host = modbus_config.host.clone();
|
||||
let port = modbus_config.port;
|
||||
|
||||
let input_registers = input_registers.clone();
|
||||
let holding_registers = holding_registers.clone();
|
||||
let coils = coils.clone();
|
||||
thread::spawn(move || {
|
||||
let addr: SocketAddr = format!("{}:{}", host, port).parse().unwrap();
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
loop {
|
||||
// Input Register
|
||||
if let Some(ref vec) = input_registers {
|
||||
for map in vec {
|
||||
for (key, reg) in map {
|
||||
let typ = reg.r#type.as_deref().unwrap_or("");
|
||||
let value = match typ {
|
||||
"INT16" => { let raw: i16 = 0; raw as f64 },
|
||||
"UINT16" => { let raw: u16 = 0; raw as f64 },
|
||||
"UINT32" => { let raw1: u16 = 0; let raw2: u16 = 0; let raw32: u32 = ((raw1 as u32) << 16) | (raw2 as u32); raw32 as f64 },
|
||||
_ => 0.0,
|
||||
};
|
||||
if let Ok(mut maps) = value_maps.lock() {
|
||||
maps.modbus_input_register_values.insert(key.clone(), value);
|
||||
// Verbindungsaufbau
|
||||
let connect_result = rt.block_on(async {
|
||||
tcp::connect_slave(addr, 1u8.into()).await
|
||||
});
|
||||
match connect_result {
|
||||
Ok(mut client) => {
|
||||
// Werte abfragen
|
||||
// Input Register
|
||||
if let Some(ref vec) = input_registers {
|
||||
for map in vec {
|
||||
for (key, reg) in map {
|
||||
let addr = reg.addr;
|
||||
let typ = reg.r#type.as_deref().unwrap_or("");
|
||||
let factor = reg.factor.unwrap_or(1.0);
|
||||
let value: Option<f64> = rt.block_on(async {
|
||||
match typ {
|
||||
"INT16" => client.read_input_registers(addr, 1).await.ok().map(|v| {
|
||||
let raw = v[0] as i16;
|
||||
if raw == -32768 || raw == 32767 { None } else { Some(((raw as f64 * factor) * 10.0).round() / 10.0) }
|
||||
}).flatten(),
|
||||
"UINT16" => client.read_input_registers(addr, 1).await.ok().map(|v| {
|
||||
if v[0] == 0xFFFF { None } else { Some(((v[0] as f64 * factor) * 10.0).round() / 10.0) }
|
||||
}).flatten(),
|
||||
"UINT32" => client.read_input_registers(addr, 2).await.ok().map(|v| {
|
||||
let raw = (v[0] as u32) << 16 | (v[1] as u32);
|
||||
if raw == 0xFFFFFFFF { None } else { Some(((raw as f64 * factor) * 10.0).round() / 10.0) }
|
||||
}).flatten(),
|
||||
_ => Some(0.0),
|
||||
}
|
||||
});
|
||||
if let Ok(mut maps) = value_maps.lock() {
|
||||
maps.modbus_input_register_values.insert(key.clone(), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Holding Register
|
||||
if let Some(ref vec) = holding_registers {
|
||||
for map in vec {
|
||||
for (key, reg) in map {
|
||||
let typ = reg.r#type.as_deref().unwrap_or("");
|
||||
let value = match typ {
|
||||
"INT16" => { let raw: i16 = 0; raw as f64 },
|
||||
"UINT16" => { let raw: u16 = 0; raw as f64 },
|
||||
"UINT32" => { let raw1: u16 = 0; let raw2: u16 = 0; let raw32: u32 = ((raw1 as u32) << 16) | (raw2 as u32); raw32 as f64 },
|
||||
_ => 0.0,
|
||||
};
|
||||
if let Ok(mut maps) = value_maps.lock() {
|
||||
maps.modbus_holding_register_values.insert(key.clone(), value);
|
||||
// Holding Register
|
||||
if let Some(ref vec) = holding_registers {
|
||||
for map in vec {
|
||||
for (key, reg) in map {
|
||||
let addr = reg.addr;
|
||||
let typ = reg.r#type.as_deref().unwrap_or("");
|
||||
let factor = reg.factor.unwrap_or(1.0);
|
||||
let value: Option<f64> = rt.block_on(async {
|
||||
match typ {
|
||||
"INT16" => client.read_holding_registers(addr, 1).await.ok().map(|v| {
|
||||
let raw = v[0] as i16;
|
||||
if raw == -32768 || raw == 32767 { None } else { Some(((raw as f64 * factor) * 10.0).round() / 10.0) }
|
||||
}).flatten(),
|
||||
"UINT16" => client.read_holding_registers(addr, 1).await.ok().map(|v| {
|
||||
if v[0] == 0xFFFF { None } else { Some(((v[0] as f64 * factor) * 10.0).round() / 10.0) }
|
||||
}).flatten(),
|
||||
"UINT32" => client.read_holding_registers(addr, 2).await.ok().map(|v| {
|
||||
let raw = (v[0] as u32) << 16 | (v[1] as u32);
|
||||
if raw == 0xFFFFFFFF { None } else { Some(((raw as f64 * factor) * 10.0).round() / 10.0) }
|
||||
}).flatten(),
|
||||
_ => Some(0.0),
|
||||
}
|
||||
});
|
||||
if let Ok(mut maps) = value_maps.lock() {
|
||||
maps.modbus_holding_register_values.insert(key.clone(), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Coils
|
||||
if let Some(ref vec) = coils {
|
||||
for map in vec {
|
||||
for (key, _reg) in map {
|
||||
let value = 0.0; // Hier Coil-Wert abfragen
|
||||
if let Ok(mut maps) = value_maps.lock() {
|
||||
maps.modbus_coils_values.insert(key.clone(), value);
|
||||
// Coils
|
||||
if let Some(ref vec) = coils {
|
||||
for map in vec {
|
||||
for (key, reg) in map {
|
||||
let addr = reg.addr;
|
||||
let value = rt.block_on(async {
|
||||
client.read_coils(addr, 1).await.ok().map(|v| if v[0] { 1.0 } else { 0.0 })
|
||||
}).unwrap_or(0.0);
|
||||
if let Ok(mut maps) = value_maps.lock() {
|
||||
maps.modbus_coils_values.insert(key.clone(), Some(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
eprintln!("Modbus: Verbindung fehlgeschlagen, retry in {:?}", poll_interval);
|
||||
thread::sleep(poll_interval);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
thread::sleep(poll_interval);
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct ModbusCoilsConfig {
|
||||
pub addr: u16,
|
||||
pub write: Option<bool>,
|
||||
pub mqtt: Option<bool>,
|
||||
pub influxdb: Option<bool>,
|
||||
pub comment: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct ModbusInputRegisterConfig {
|
||||
pub addr: u16,
|
||||
pub r#type: Option<String>,
|
||||
pub factor: Option<f64>,
|
||||
pub mqtt: Option<bool>,
|
||||
pub influxdb: Option<bool>,
|
||||
pub comment: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct ModbusHoldingRegisterConfig {
|
||||
pub addr: u16,
|
||||
pub r#type: Option<String>,
|
||||
pub factor: Option<f64>,
|
||||
pub write: Option<bool>,
|
||||
pub mqtt: Option<bool>,
|
||||
pub influxdb: Option<bool>,
|
||||
pub comment: Option<String>,
|
||||
}
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::process;
|
||||
use std::collections::HashMap;
|
||||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
use crate::config::{AppConfig, ModbusValueMaps};
|
||||
use crate::modbus;
|
||||
use rumqttc::{Event, MqttOptions, Packet, Client, QoS, RecvTimeoutError};
|
||||
|
||||
pub fn start_mqtt_thread(config: Arc<Mutex<AppConfig>>, values: Arc<Mutex<ModbusValueMaps>>) {
|
||||
let mqtt_config = {
|
||||
let cfg = config.lock().unwrap();
|
||||
cfg.mqtt.clone()
|
||||
};
|
||||
let broker = if mqtt_config.broker.is_empty() { "localhost".to_string() } else { mqtt_config.broker.clone() };
|
||||
let port = if mqtt_config.port == 0 { 1883 } else { mqtt_config.port };
|
||||
let path = if let Some(ref p) = mqtt_config.path { p.clone() } else { "paramod/values".to_string() };
|
||||
let leitsystem_path = mqtt_config
|
||||
.leitsystem_path
|
||||
.clone()
|
||||
.unwrap_or_else(|| format!("{}/leitsystem", path));
|
||||
let set_write_interval = Duration::from_millis(mqtt_config.set_write_interval_ms.unwrap_or(2000).max(100));
|
||||
let user = mqtt_config.user.clone().unwrap_or_default();
|
||||
let password = mqtt_config.password.clone().unwrap_or_default();
|
||||
let user_is_empty = user.is_empty();
|
||||
|
||||
thread::spawn(move || {
|
||||
let client_id = format!("paramod-client-{}", process::id());
|
||||
let mut mqttoptions = MqttOptions::new(client_id, broker, port);
|
||||
mqttoptions.set_keep_alive(Duration::from_secs(5));
|
||||
if !user_is_empty {
|
||||
mqttoptions.set_credentials(user, password);
|
||||
}
|
||||
let (mut client, mut connection) = Client::new(mqttoptions, 10);
|
||||
|
||||
let set_topic = format!("{}/+/set", path);
|
||||
if let Err(e) = client.subscribe(set_topic, QoS::AtLeastOnce) {
|
||||
eprintln!("MQTT Subscribe fehlgeschlagen: {}", e);
|
||||
}
|
||||
let leitsystem_state_topic = format!("{}/active", leitsystem_path.trim_end_matches('/'));
|
||||
if let Err(e) = client.subscribe(leitsystem_state_topic.clone(), QoS::AtLeastOnce) {
|
||||
eprintln!("MQTT Subscribe Leitsystem fehlgeschlagen: {}", e);
|
||||
}
|
||||
|
||||
let publish_interval = Duration::from_secs(5);
|
||||
let mut last_publish = Instant::now() - publish_interval;
|
||||
let mut last_set_write = Instant::now() - set_write_interval;
|
||||
let mut pending_set_values: HashMap<String, String> = HashMap::new();
|
||||
let mut leitsystem_enabled = false;
|
||||
|
||||
loop {
|
||||
if last_publish.elapsed() >= publish_interval {
|
||||
{
|
||||
let values = values.lock().unwrap();
|
||||
// Input Register
|
||||
for (name, val) in &values.modbus_input_register_values {
|
||||
if let Some(v) = val {
|
||||
if should_publish(&config, name, "input_register") {
|
||||
let topic = format!("{}/{}/state", path, name);
|
||||
let payload = format!("{}", v);
|
||||
let _ = client.publish(topic, QoS::AtLeastOnce, false, payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Holding Register
|
||||
for (name, val) in &values.modbus_holding_register_values {
|
||||
if let Some(v) = val {
|
||||
if should_publish(&config, name, "holding_register") {
|
||||
let topic = format!("{}/{}/state", path, name);
|
||||
let payload = format!("{}", v);
|
||||
let _ = client.publish(topic, QoS::AtLeastOnce, false, payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Coils
|
||||
for (name, val) in &values.modbus_coils_values {
|
||||
if let Some(v) = val {
|
||||
if should_publish(&config, name, "coils") {
|
||||
let topic = format!("{}/{}/state", path, name);
|
||||
let payload = format!("{}", v);
|
||||
let _ = client.publish(topic, QoS::AtLeastOnce, false, payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let state_payload = if leitsystem_enabled { "ON" } else { "OFF" };
|
||||
let _ = client.publish(
|
||||
leitsystem_state_topic.clone(),
|
||||
QoS::AtLeastOnce,
|
||||
true,
|
||||
state_payload,
|
||||
);
|
||||
last_publish = Instant::now();
|
||||
}
|
||||
|
||||
if last_set_write.elapsed() >= set_write_interval {
|
||||
let cfg = {
|
||||
let lock = config.lock().unwrap();
|
||||
lock.clone()
|
||||
};
|
||||
|
||||
if leitsystem_enabled {
|
||||
for (name, payload) in &pending_set_values {
|
||||
if let Err(e) = modbus::write_value_by_name(&cfg, name, payload) {
|
||||
eprintln!("MQTT set -> Modbus Fehler ({}): {}", name, e);
|
||||
} else {
|
||||
mirror_set_value(&values, &cfg, name, payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
last_set_write = Instant::now();
|
||||
}
|
||||
|
||||
match connection.recv_timeout(Duration::from_millis(200)) {
|
||||
Ok(Ok(Event::Incoming(Packet::Publish(publish)))) => {
|
||||
if publish.topic == leitsystem_state_topic {
|
||||
let payload = String::from_utf8_lossy(&publish.payload).trim().to_string();
|
||||
match parse_boolish(&payload) {
|
||||
Some(v) => leitsystem_enabled = v,
|
||||
None => eprintln!("Ungültiger Leitsystem state '{}', erwartet ON/OFF", payload),
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Ok(name) = extract_name_from_set_topic(&path, &publish.topic) {
|
||||
let payload = String::from_utf8_lossy(&publish.payload).trim().to_string();
|
||||
pending_set_values.insert(name.clone(), payload.clone());
|
||||
}
|
||||
}
|
||||
Ok(Ok(_)) => {}
|
||||
Ok(Err(e)) => {
|
||||
eprintln!("MQTT Connection Fehler: {}", e);
|
||||
eprintln!("Hinweis: Häufige Ursache ist eine zweite MQTT-Session mit derselben Client-ID.");
|
||||
}
|
||||
Err(RecvTimeoutError::Timeout) => {}
|
||||
Err(RecvTimeoutError::Disconnected) => {
|
||||
eprintln!("MQTT Verbindung getrennt (request channel geschlossen)");
|
||||
thread::sleep(Duration::from_millis(500));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn parse_boolish(payload: &str) -> Option<bool> {
|
||||
match payload.trim().to_ascii_lowercase().as_str() {
|
||||
"1" | "true" | "on" => Some(true),
|
||||
"0" | "false" | "off" => Some(false),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn mirror_set_value(values: &Arc<Mutex<ModbusValueMaps>>, cfg: &AppConfig, name: &str, payload: &str) {
|
||||
if let Ok(mut maps) = values.lock() {
|
||||
if let Some(ref coils) = cfg.modbus_coils {
|
||||
for map in coils {
|
||||
if map.contains_key(name) {
|
||||
if let Some(v) = parse_boolish(payload) {
|
||||
maps.modbus_coils_values.insert(name.to_string(), Some(if v { 1.0 } else { 0.0 }));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref holding) = cfg.modbus_holding_register {
|
||||
for map in holding {
|
||||
if map.contains_key(name) {
|
||||
if let Ok(v) = payload.parse::<f64>() {
|
||||
maps.modbus_holding_register_values.insert(name.to_string(), Some(v));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_name_from_set_topic(base_path: &str, topic: &str) -> Result<String, String> {
|
||||
let prefix = format!("{}/", base_path.trim_end_matches('/'));
|
||||
if !topic.starts_with(&prefix) || !topic.ends_with("/set") {
|
||||
return Err("Topic passt nicht zum /set-Schema".to_string());
|
||||
}
|
||||
let without_prefix = &topic[prefix.len()..];
|
||||
let name = without_prefix.strip_suffix("/set").unwrap_or_default();
|
||||
if name.is_empty() || name.contains('/') {
|
||||
return Err("Ungültiger Variablenname im Topic".to_string());
|
||||
}
|
||||
Ok(name.to_string())
|
||||
}
|
||||
|
||||
fn should_publish(config: &Arc<Mutex<AppConfig>>, name: &str, reg_type: &str) -> bool {
|
||||
let cfg = config.lock().unwrap();
|
||||
match reg_type {
|
||||
"input_register" => {
|
||||
if let Some(regs) = &cfg.modbus_input_register {
|
||||
for map in regs {
|
||||
if let Some(reg) = map.get(name) {
|
||||
return reg.mqtt.unwrap_or(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"holding_register" => {
|
||||
if let Some(regs) = &cfg.modbus_holding_register {
|
||||
for map in regs {
|
||||
if let Some(reg) = map.get(name) {
|
||||
return reg.mqtt.unwrap_or(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"coils" => {
|
||||
if let Some(coils) = &cfg.modbus_coils {
|
||||
for map in coils {
|
||||
if let Some(coil) = map.get(name) {
|
||||
return coil.mqtt.unwrap_or(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
false
|
||||
}
|
||||
+47
-4
@@ -56,6 +56,34 @@ function addRow() {
|
||||
<button class="delete-btn" onclick="deleteRow(this)">🗑️</button>
|
||||
</td>
|
||||
`;
|
||||
} else if (typeof tableId !== 'undefined' && tableId === 'modbus_holding_register') {
|
||||
newRow.innerHTML = `
|
||||
<td><input type='text' class='text-input' data-field='bezeichnung' value='' /></td>
|
||||
<td><input type='text' class='text-input' data-field='adresse' value='' /></td>
|
||||
<td><input type='text' class='text-input' data-field='type' value='' /></td>
|
||||
<td><input type='text' class='text-input' data-field='faktor' value='1.0' /></td>
|
||||
<td>
|
||||
<label class='switch'>
|
||||
<input type='checkbox' class='bool-input' data-field='write' />
|
||||
<span class='slider'></span>
|
||||
</label>
|
||||
</td>
|
||||
<td>
|
||||
<label class='switch'>
|
||||
<input type='checkbox' class='bool-input' data-field='mqtt' />
|
||||
<span class='slider'></span>
|
||||
</label>
|
||||
</td>
|
||||
<td>
|
||||
<label class='switch'>
|
||||
<input type='checkbox' class='bool-input' data-field='influxdb' />
|
||||
<span class='slider'></span>
|
||||
</label>
|
||||
</td>
|
||||
<td>
|
||||
<button class="delete-btn" onclick="deleteRow(this)">🗑️</button>
|
||||
</td>
|
||||
`;
|
||||
} else {
|
||||
newRow.innerHTML = `
|
||||
<td><input type='text' class='text-input' data-field='bezeichnung' value='' /></td>
|
||||
@@ -119,13 +147,27 @@ async function saveTable() {
|
||||
influxdb: influxdb,
|
||||
comment: comment
|
||||
};
|
||||
} else {
|
||||
const rtype = row.querySelector("input[data-field='type']")?.value || null;
|
||||
} else if (typeof tableId !== 'undefined' && tableId === 'modbus_holding_register') {
|
||||
const write = row.querySelector("input[data-field='write']")?.checked || false;
|
||||
const type = row.querySelector("input[data-field='type']")?.value || null;
|
||||
const factor = parseFloat(row.querySelector("input[data-field='factor']")?.value || row.querySelector("input[data-field='faktor']")?.value || '1.0');
|
||||
const comment = row.querySelector("input[data-field='comment']")?.value || null;
|
||||
value = {
|
||||
addr: addr,
|
||||
rtype: rtype,
|
||||
type: type,
|
||||
factor: factor,
|
||||
write: write,
|
||||
mqtt: mqtt,
|
||||
influxdb: influxdb,
|
||||
comment: comment
|
||||
};
|
||||
} else {
|
||||
const type = row.querySelector("input[data-field='type']")?.value || null;
|
||||
const factor = parseFloat(row.querySelector("input[data-field='factor']")?.value || row.querySelector("input[data-field='faktor']")?.value || '1.0');
|
||||
const comment = row.querySelector("input[data-field='comment']")?.value || null;
|
||||
value = {
|
||||
addr: addr,
|
||||
type: type,
|
||||
factor: factor,
|
||||
mqtt: mqtt,
|
||||
influxdb: influxdb,
|
||||
@@ -154,8 +196,9 @@ async function saveTable() {
|
||||
messageDiv.className = 'message success';
|
||||
messageDiv.textContent = '✓ Erfolgreich gespeichert!';
|
||||
} else {
|
||||
const errorText = (await response.text()) || 'Unbekannter Fehler';
|
||||
messageDiv.className = 'message error';
|
||||
messageDiv.textContent = '✗ Fehler beim Speichern!';
|
||||
messageDiv.textContent = `✗ Fehler beim Speichern: ${errorText}`;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
|
||||
+21
-8
@@ -8,11 +8,20 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
if (el) {
|
||||
if (config && config.default && (config.default.darkmode === true || config.default.darkmode === 'true')) {
|
||||
el.checked = true;
|
||||
document.body.classList.add('darkmode');
|
||||
} else {
|
||||
el.checked = false;
|
||||
document.body.classList.remove('darkmode');
|
||||
}
|
||||
// Event Listener für Umschalten
|
||||
el.addEventListener('change', function() {
|
||||
if (el.checked) {
|
||||
document.body.classList.add('darkmode');
|
||||
} else {
|
||||
document.body.classList.remove('darkmode');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Falls das Element noch nicht existiert, erneut versuchen
|
||||
setTimeout(setSwitch, 50);
|
||||
}
|
||||
}
|
||||
@@ -35,8 +44,8 @@ async function saveSettings() {
|
||||
max_input_addr: parseInt(document.getElementById('modbus_max_input_addr').value, 10) || null,
|
||||
max_holding_addr: parseInt(document.getElementById('modbus_max_holding_addr').value, 10) || null,
|
||||
modbus_coils: null,
|
||||
modbus_input_register: null,
|
||||
modbus_holding_register: null
|
||||
modbus_input_register: null,
|
||||
modbus_holding_register: null,
|
||||
};
|
||||
|
||||
// MQTT
|
||||
@@ -45,7 +54,9 @@ async function saveSettings() {
|
||||
port: parseInt(document.getElementById('mqtt_port').value, 10),
|
||||
user: document.getElementById('mqtt_user').value || null,
|
||||
password: document.getElementById('mqtt_password').value || null,
|
||||
button_circulation: document.getElementById('mqtt_button_circulation').value || null
|
||||
path: document.getElementById('mqtt_path')?.value || null,
|
||||
leitsystem_path: document.getElementById('mqtt_leitsystem_path')?.value || null,
|
||||
set_write_interval_ms: parseInt(document.getElementById('mqtt_set_write_interval_ms')?.value, 10) || 2000
|
||||
};
|
||||
|
||||
// InfluxDB
|
||||
@@ -78,14 +89,16 @@ async function saveSettings() {
|
||||
if (response.ok) {
|
||||
messageDiv.className = 'message success';
|
||||
messageDiv.textContent = '✓ Einstellungen erfolgreich gespeichert!';
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
} else {
|
||||
messageDiv.className = 'message error';
|
||||
messageDiv.textContent = '✗ Fehler beim Speichern der Einstellungen!';
|
||||
setTimeout(() => {
|
||||
messageDiv.style.display = 'none';
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
messageDiv.style.display = 'none';
|
||||
}, 3000);
|
||||
} catch (error) {
|
||||
const messageDiv = document.getElementById('message');
|
||||
messageDiv.className = 'message error';
|
||||
|
||||
+3
-32
@@ -32,7 +32,7 @@
|
||||
--error-border: #f5c6cb;
|
||||
}
|
||||
|
||||
body.darkmode, body.darkmode :root {
|
||||
body.darkmode {
|
||||
--background: #181a1b;
|
||||
--background-alt: #23272b;
|
||||
--container-bg: #23272b;
|
||||
@@ -60,35 +60,6 @@ body.darkmode, body.darkmode :root {
|
||||
--error-border: #b71c1c;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #181a1b;
|
||||
--background-alt: #23272b;
|
||||
--container-bg: #23272b;
|
||||
--header-bg: #23272b;
|
||||
--primary: #3399ff;
|
||||
--primary-dark: #1a2a3a;
|
||||
--text: #eee;
|
||||
--text-light: #bbb;
|
||||
--nav-link: #bbb;
|
||||
--nav-link-hover: #222;
|
||||
--table-header: #222e3a;
|
||||
--table-header-border: #3399ff;
|
||||
--table-row-hover: #23272b;
|
||||
--input-border: #444;
|
||||
--input-focus: #3399ff;
|
||||
--button-gradient: linear-gradient(135deg, #3399ff 0%, #1a2a3a 100%);
|
||||
--button-gradient-alt: linear-gradient(135deg, #388e3c 0%, #1b5e20 100%);
|
||||
--delete-btn: #b71c1c;
|
||||
--delete-btn-hover: #7f0000;
|
||||
--success-bg: #234d2c;
|
||||
--success-text: #b9f6ca;
|
||||
--success-border: #388e3c;
|
||||
--error-bg: #4a2323;
|
||||
--error-text: #ff8a80;
|
||||
--error-border: #b71c1c;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
@@ -125,8 +96,8 @@ body {
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
font-size: 40px;
|
||||
font-weight: 800;
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
|
||||
+16
-3
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Sensor Konfiguration</title>
|
||||
<title>Konfiguration</title>
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
</head>
|
||||
<body>
|
||||
@@ -14,7 +14,7 @@
|
||||
<rect width="40" height="40" rx="8" fill="#667eea"/>
|
||||
<path d="M12 20L18 26L28 14" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<span class="logo-text">Sensor Manager</span>
|
||||
<span class="logo-text">Paramod</span>
|
||||
</div>
|
||||
<nav class="nav">
|
||||
<a href="/table/modbus_input_register" class="nav-link {% if active_page == 'modbus_input_register' %}active{% endif %}">Input Register</a>
|
||||
@@ -26,7 +26,7 @@
|
||||
</header>
|
||||
|
||||
<div class="container">
|
||||
<h1>🔧 Sensor Konfiguration - {{ table_id | upper }}</h1>
|
||||
<h1>🔧 Konfiguration - {{ table_id | upper }}</h1>
|
||||
<div id="message" class="message"></div>
|
||||
<div class="table-wrapper">
|
||||
<table id="sensorTable">
|
||||
@@ -37,6 +37,10 @@
|
||||
<th>Adresse</th>
|
||||
{% if table_id == "modbus_coils" %}
|
||||
<th>Write</th>
|
||||
{% elif table_id == "modbus_holding_registers" or table_id == "modbus_holding_register" %}
|
||||
<th>Type</th>
|
||||
<th>Faktor</th>
|
||||
<th>Write</th>
|
||||
{% else %}
|
||||
<th>Type</th>
|
||||
<th>Faktor</th>
|
||||
@@ -63,6 +67,15 @@
|
||||
<span class='slider'></span>
|
||||
</label>
|
||||
</td>
|
||||
{% elif table_id == "modbus_holding_registers" or table_id == "modbus_holding_register" %}
|
||||
<td><input type='text' class='text-input' data-field='type' value='{{ row.type | default(value="") }}' /></td>
|
||||
<td><input type='text' class='text-input' data-field='factor' value='{{ row.factor | default(value="") }}' /></td>
|
||||
<td>
|
||||
<label class='switch'>
|
||||
<input type='checkbox' class='bool-input' data-field='write' {% if row.write %}checked{% endif %} />
|
||||
<span class='slider'></span>
|
||||
</label>
|
||||
</td>
|
||||
{% else %}
|
||||
<td><input type='text' class='text-input' data-field='type' value='{{ row.type | default(value="") }}' /></td>
|
||||
<td><input type='text' class='text-input' data-field='factor' value='{{ row.factor | default(value="") }}' /></td>
|
||||
|
||||
+12
-4
@@ -6,7 +6,7 @@
|
||||
<title>Einstellungen</title>
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<body{% if default.darkmode == true or default.darkmode == "true" %} class="darkmode"{% endif %}>
|
||||
<header class="header">
|
||||
<div class="header-content">
|
||||
<div class="logo">
|
||||
@@ -14,7 +14,7 @@
|
||||
<rect width="40" height="40" rx="8" fill="#667eea"/>
|
||||
<path d="M12 20L18 26L28 14" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<span class="logo-text">Sensor Manager</span>
|
||||
<span class="logo-text">Paramod</span>
|
||||
</div>
|
||||
<nav class="nav">
|
||||
<a href="/table/modbus_input_register" class="nav-link {% if active_page == 'modbus_input_register' %}active{% endif %}">Input Register</a>
|
||||
@@ -94,8 +94,16 @@
|
||||
<input type="password" id="mqtt_password" class="text-input" value="{{ mqtt.password | default(value="") }}" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="mqtt_button_circulation">Button Circulation:</label>
|
||||
<input type="text" id="mqtt_button_circulation" class="text-input" value="{{ mqtt.button_circulation | default(value="") }}" />
|
||||
<label for="mqtt_path">MQTT Pfad:</label>
|
||||
<input type="text" id="mqtt_path" class="text-input" value="{{ mqtt.path | default(value="") }}" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="mqtt_leitsystem_path">Leitsystem Pfad:</label>
|
||||
<input type="text" id="mqtt_leitsystem_path" class="text-input" value="{{ mqtt.leitsystem_path | default(value="") }}" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="mqtt_set_write_interval_ms">Set-Sendeintervall (ms):</label>
|
||||
<input type="number" min="100" id="mqtt_set_write_interval_ms" class="text-input" value="{{ mqtt.set_write_interval_ms | default(value="2000") }}" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in new issue
Block a user