Compare commits
40
Commits
58bacd2f6d
...
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 |
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",
|
"name": "Debug main.rs",
|
||||||
"type": "lldb",
|
"type": "lldb",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceFolder}/target/debug/table-server",
|
"program": "${workspaceFolder}/target/debug/paramod",
|
||||||
"args": [],
|
"args": [],
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"sourceLanguages": ["rust"],
|
"sourceLanguages": ["rust"],
|
||||||
"preLaunchTask": "cargo build"
|
"preLaunchTask": "cargo build",
|
||||||
|
"postDebugTask": "cargo reset version placeholder"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
Vendored
+14
-2
@@ -4,12 +4,24 @@
|
|||||||
{
|
{
|
||||||
"label": "cargo build",
|
"label": "cargo build",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "cargo build",
|
"command": ".vscode/cargo-build-version.sh",
|
||||||
"group": {
|
"group": {
|
||||||
"kind": "build",
|
"kind": "build",
|
||||||
"isDefault": true
|
"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
+319
-21
@@ -8,7 +8,7 @@ version = "0.5.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a"
|
checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.10.0",
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
@@ -29,7 +29,7 @@ dependencies = [
|
|||||||
"actix-service",
|
"actix-service",
|
||||||
"actix-utils",
|
"actix-utils",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"bitflags",
|
"bitflags 2.10.0",
|
||||||
"bytes",
|
"bytes",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@@ -53,7 +53,7 @@ dependencies = [
|
|||||||
"actix-service",
|
"actix-service",
|
||||||
"actix-utils",
|
"actix-utils",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bitflags",
|
"bitflags 2.10.0",
|
||||||
"brotli",
|
"brotli",
|
||||||
"bytes",
|
"bytes",
|
||||||
"bytestring",
|
"bytestring",
|
||||||
@@ -300,6 +300,12 @@ version = "0.22.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.10.0"
|
version = "2.10.0"
|
||||||
@@ -401,7 +407,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
|
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"serde",
|
||||||
|
"wasm-bindgen",
|
||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -690,12 +699,32 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"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]]
|
[[package]]
|
||||||
name = "futures-core"
|
name = "futures-core"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
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]]
|
[[package]]
|
||||||
name = "futures-sink"
|
name = "futures-sink"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@@ -715,6 +744,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-macro",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
@@ -774,7 +804,7 @@ version = "0.9.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757"
|
checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.10.0",
|
||||||
"ignore",
|
"ignore",
|
||||||
"walkdir",
|
"walkdir",
|
||||||
]
|
]
|
||||||
@@ -834,6 +864,17 @@ dependencies = [
|
|||||||
"itoa",
|
"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]]
|
[[package]]
|
||||||
name = "http-range"
|
name = "http-range"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -861,6 +902,44 @@ dependencies = [
|
|||||||
"libm",
|
"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]]
|
[[package]]
|
||||||
name = "iana-time-zone"
|
name = "iana-time-zone"
|
||||||
version = "0.1.64"
|
version = "0.1.64"
|
||||||
@@ -1019,6 +1098,29 @@ dependencies = [
|
|||||||
"hashbrown 0.16.0",
|
"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]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.15"
|
version = "1.0.15"
|
||||||
@@ -1215,6 +1317,26 @@ dependencies = [
|
|||||||
"hashbrown 0.14.5",
|
"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]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.5"
|
version = "0.12.5"
|
||||||
@@ -1471,7 +1593,7 @@ version = "0.5.18"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.10.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1509,6 +1631,47 @@ version = "0.8.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
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]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.17.14"
|
version = "0.17.14"
|
||||||
@@ -1530,7 +1693,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
|
checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.21.7",
|
"base64 0.21.7",
|
||||||
"bitflags",
|
"bitflags 2.10.0",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
@@ -1658,7 +1821,7 @@ version = "2.11.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags 2.10.0",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -1869,6 +2032,12 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sync_wrapper"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "synstructure"
|
name = "synstructure"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
@@ -1881,21 +2050,24 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "table-server"
|
name = "system-configuration"
|
||||||
version = "0.1.0"
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-files",
|
"bitflags 1.3.2",
|
||||||
"actix-web",
|
"core-foundation",
|
||||||
"config",
|
"system-configuration-sys",
|
||||||
"rumqttc",
|
]
|
||||||
"serde",
|
|
||||||
"serde_derive",
|
[[package]]
|
||||||
"serde_json",
|
name = "system-configuration-sys"
|
||||||
"serde_yaml",
|
version = "0.5.0"
|
||||||
"tera",
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
"tokio",
|
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
|
||||||
"tokio-modbus",
|
dependencies = [
|
||||||
"toml",
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2098,6 +2270,12 @@ version = "0.1.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-service"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.41"
|
version = "0.1.41"
|
||||||
@@ -2130,6 +2308,12 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "try-lock"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.19.0"
|
version = "1.19.0"
|
||||||
@@ -2218,6 +2402,15 @@ dependencies = [
|
|||||||
"winapi-util",
|
"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]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.1+wasi-snapshot-preview1"
|
version = "0.11.1+wasi-snapshot-preview1"
|
||||||
@@ -2246,6 +2439,19 @@ dependencies = [
|
|||||||
"wasm-bindgen-shared",
|
"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]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.105"
|
version = "0.2.105"
|
||||||
@@ -2278,6 +2484,22 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"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]]
|
[[package]]
|
||||||
name = "winapi-util"
|
name = "winapi-util"
|
||||||
version = "0.1.11"
|
version = "0.1.11"
|
||||||
@@ -2346,6 +2568,15 @@ dependencies = [
|
|||||||
"windows-link",
|
"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]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
@@ -2373,6 +2604,21 @@ dependencies = [
|
|||||||
"windows-link",
|
"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]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -2406,6 +2652,12 @@ dependencies = [
|
|||||||
"windows_x86_64_msvc 0.53.1",
|
"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]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -2418,6 +2670,12 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -2430,6 +2688,12 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -2454,6 +2718,12 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.48.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -2466,6 +2736,12 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
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]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -2478,6 +2754,12 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
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]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -2490,6 +2772,12 @@ version = "0.53.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
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]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
@@ -2511,6 +2799,16 @@ dependencies = [
|
|||||||
"memchr",
|
"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]]
|
[[package]]
|
||||||
name = "wit-bindgen"
|
name = "wit-bindgen"
|
||||||
version = "0.46.0"
|
version = "0.46.0"
|
||||||
|
|||||||
+5
-3
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "table-server"
|
name = "paramod"
|
||||||
version = "0.1.0"
|
version = "__VERSION__"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde_yaml = "0.9"
|
serde_yaml = "0.9"
|
||||||
@@ -19,3 +19,5 @@ serde_derive = "1.0"
|
|||||||
tokio-modbus = "0.7"
|
tokio-modbus = "0.7"
|
||||||
|
|
||||||
rumqttc = "0.23"
|
rumqttc = "0.23"
|
||||||
|
influxdb = "0.7"
|
||||||
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
+11
-19
@@ -1,44 +1,36 @@
|
|||||||
|
|
||||||
# Build Stage
|
# Build Stage
|
||||||
FROM rust:1.75 as builder
|
FROM rust:1.85 as builder
|
||||||
|
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
# Copy manifest files
|
|
||||||
COPY Cargo.toml ./
|
COPY Cargo.toml ./
|
||||||
|
COPY Cargo.lock ./
|
||||||
# Copy source code and templates
|
COPY VERSION ./
|
||||||
COPY src ./src
|
COPY src ./src
|
||||||
COPY templates ./templates
|
COPY templates ./templates
|
||||||
COPY static ./static
|
COPY static ./static
|
||||||
|
|
||||||
# Build the application
|
RUN VERSION=$(cat VERSION) \
|
||||||
RUN cargo build --release
|
&& sed -i 's/^version = "__VERSION__"/version = "'"$VERSION"'"/' Cargo.toml \
|
||||||
|
&& cargo build --release
|
||||||
|
|
||||||
# Runtime Stage
|
# Runtime Stage
|
||||||
FROM debian:bookworm-slim
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
# Install required dependencies
|
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Copy the binary from builder
|
COPY --from=builder /usr/src/app/target/release/paramod /app/paramod
|
||||||
COPY --from=builder /usr/src/app/target/release/table-server /app/table-server
|
|
||||||
|
|
||||||
# Copy templates and static files
|
|
||||||
COPY --from=builder /usr/src/app/templates /app/templates
|
COPY --from=builder /usr/src/app/templates /app/templates
|
||||||
COPY --from=builder /usr/src/app/static /app/static
|
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
|
EXPOSE 8080
|
||||||
|
|
||||||
# Set environment to use the data directory
|
ENV CONFIG_PATH=/app/paramod.yaml
|
||||||
ENV CONFIG_PATH=/app/data/table_config.json
|
|
||||||
|
|
||||||
# Run the binary
|
CMD ["/app/paramod"]
|
||||||
CMD ["/app/table-server"]
|
|
||||||
@@ -1,212 +1,103 @@
|
|||||||
# Tabellen Webserver
|
# Paramod
|
||||||
|
|
||||||
Ein einfacher Rust-Webserver, der eine editierbare 3x3-Tabelle bereitstellt und in einer JSON-Konfigurationsdatei persistiert.
|
Rust-Webserver zur Anzeige und Bearbeitung von Modbus-Tabellen mit Persistierung in `paramod.yaml`.
|
||||||
|
|
||||||
## Projektstruktur
|
|
||||||
|
|
||||||
```
|
|
||||||
table-server/
|
|
||||||
├── src/
|
|
||||||
│ └── main.rs
|
|
||||||
├── templates/
|
|
||||||
│ ├── index.html
|
|
||||||
│ └── settings.html
|
|
||||||
├── static/
|
|
||||||
│ ├── style.css
|
|
||||||
│ ├── script.js
|
|
||||||
│ └── settings.js
|
|
||||||
├── Cargo.toml
|
|
||||||
├── Dockerfile
|
|
||||||
├── .gitignore
|
|
||||||
└── README.md
|
|
||||||
```
|
|
||||||
|
|
||||||
## Funktionen
|
## Funktionen
|
||||||
|
|
||||||
- **3 separate Tabellen** für verschiedene Sensor-Gruppen
|
- Tabellen für `modbus_input_register`, `modbus_holding_register` und `modbus_coils`
|
||||||
- **Navigation** mit aktivem Status-Indikator
|
- Web-UI zum Bearbeiten und Speichern der Tabellen
|
||||||
- **Header mit Logo** für professionelles Erscheinungsbild
|
- Einstellungsseite für `default`, `modbus`, `mqtt` und `influxdb`
|
||||||
- **Zeilen hinzufügen/löschen** dynamisch zur Laufzeit
|
- MQTT/Influx-Integration gemäß Konfiguration
|
||||||
- **Einstellungsseite** für MQTT und InfluxDB Konfiguration
|
- Speicherung aller Änderungen in `paramod.yaml`
|
||||||
- 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
|
|
||||||
|
|
||||||
## Lokale Entwicklung
|
## Lokale Entwicklung
|
||||||
|
|
||||||
### Voraussetzungen
|
### Voraussetzungen
|
||||||
|
|
||||||
- Rust (Version 1.75 oder höher)
|
- Rust + Cargo
|
||||||
- Cargo
|
|
||||||
|
|
||||||
### Installation und Start
|
### Start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Projekt erstellen
|
|
||||||
cargo new table-server
|
|
||||||
cd table-server
|
|
||||||
|
|
||||||
# Dependencies installieren und starten
|
|
||||||
cargo run
|
cargo run
|
||||||
```
|
```
|
||||||
|
|
||||||
Der Server läuft dann auf `http://localhost:8080`
|
Server: `http://localhost:8080`
|
||||||
|
|
||||||
## Docker
|
## Docker (Podman)
|
||||||
|
|
||||||
### Container bauen
|
### Image bauen
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker build -t table-server .
|
podman build -t paramod:latest .
|
||||||
```
|
```
|
||||||
|
|
||||||
### Container starten
|
### Container starten
|
||||||
|
|
||||||
```bash
|
```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
|
## API Endpoints
|
||||||
|
|
||||||
- `GET /` - Zeigt Tabelle 1
|
- `GET /`
|
||||||
- `GET /table/table2` - Zeigt Tabelle 2
|
- `GET /table/modbus_input_register`
|
||||||
- `GET /table/table3` - Zeigt Tabelle 3
|
- `GET /table/modbus_holding_register`
|
||||||
- `GET /settings` - Zeigt Einstellungsseite
|
- `GET /table/modbus_coils`
|
||||||
- `POST /api/save` - Speichert eine Tabelle
|
- `GET /settings`
|
||||||
- `POST /api/save-settings` - Speichert die Einstellungen
|
- `GET /api/config`
|
||||||
- `GET /static/*` - Statische Dateien (CSS, JS)
|
- `POST /api/save`
|
||||||
|
- `POST /api/save-settings`
|
||||||
|
|
||||||
### Beispiel API-Request (Tabelle speichern)
|
## API-Beispiel: Tabelle speichern
|
||||||
|
|
||||||
|
Beispiel für Holding-Register:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST http://localhost:8080/api/save \
|
curl -X POST http://localhost:8080/api/save \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{
|
-d '{
|
||||||
"table_id": "table1",
|
"table_id": "modbus_holding_register",
|
||||||
"rows": [
|
"rows": [
|
||||||
{
|
{
|
||||||
"bezeichnung": "Sensor 1",
|
"TVsoll": {
|
||||||
"adresse": "192.168.1.100",
|
"addr": 2,
|
||||||
"type": "Temperatur",
|
"type": "INT16",
|
||||||
"faktor": "1.0",
|
"factor": 0.1,
|
||||||
"mqtt": true,
|
"write": true,
|
||||||
"influxdb": false
|
"mqtt": true,
|
||||||
|
"influxdb": true,
|
||||||
|
"comment": "Vorlauf Soll"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|
||||||
### Beispiel API-Request (Einstellungen speichern)
|
Beispiel für Coils:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -X POST http://localhost:8080/api/save-settings \
|
curl -X POST http://localhost:8080/api/save \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{
|
-d '{
|
||||||
"mqtt_broker": "localhost",
|
"table_id": "modbus_coils",
|
||||||
"mqtt_port": "1883",
|
"rows": [
|
||||||
"influxdb_url": "http://localhost:8086",
|
{
|
||||||
"influxdb_token": "your-token-here"
|
"MgtSystem": {
|
||||||
|
"addr": 0,
|
||||||
|
"write": true,
|
||||||
|
"mqtt": true,
|
||||||
|
"influxdb": false,
|
||||||
|
"comment": "Leitsystem aktiv"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Konfigurationsdatei
|
## Konfiguration
|
||||||
|
|
||||||
Die komplette Anwendungskonfiguration wird in `table_config.json` gespeichert:
|
Die komplette Konfiguration liegt in `paramod.yaml`.
|
||||||
|
|
||||||
```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": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Lizenz
|
## Lizenz
|
||||||
|
|
||||||
|
|||||||
@@ -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}
|
|
||||||
|
|
||||||
+62
-61
@@ -7,22 +7,23 @@ mqtt:
|
|||||||
user: admin
|
user: admin
|
||||||
password: 97sm3pHNSMZ4M5qUj0x8
|
password: 97sm3pHNSMZ4M5qUj0x8
|
||||||
path: heizung/paradigma
|
path: heizung/paradigma
|
||||||
button_circulation: zigbee2mqtt/WirelessButton
|
leitsystem_path: heizung/leitsystem2
|
||||||
|
set_write_interval_ms: 2000
|
||||||
influxdb:
|
influxdb:
|
||||||
bucket: Paradigma
|
bucket: Paradigma
|
||||||
location: Radebeul
|
|
||||||
measurement: ParadigmaModbus
|
|
||||||
org: skaville
|
org: skaville
|
||||||
token: i-sXFQbEkSC1XVzqFEaFwXwzasbsEIciVlK4SaAUOEvk0VjQPkD3fr8d7_3SPeyseTZkqj7ZMZU78b3n2F6_SQ==
|
token: i-sXFQbEkSC1XVzqFEaFwXwzasbsEIciVlK4SaAUOEvk0VjQPkD3fr8d7_3SPeyseTZkqj7ZMZU78b3n2F6_SQ==
|
||||||
url: 192.168.178.2:8086
|
url: http://192.168.178.2:8086
|
||||||
|
location: Radebeul
|
||||||
|
measurement: ParadigmaModbus
|
||||||
modbus:
|
modbus:
|
||||||
host: 192.168.178.10
|
host: 192.168.178.10
|
||||||
port: 502
|
port: 502
|
||||||
max_coils_addr: 8
|
max_coils_addr: 8
|
||||||
max_holding_addr: 61
|
|
||||||
max_input_addr: 45
|
max_input_addr: 45
|
||||||
|
max_holding_addr: 61
|
||||||
modbus_coils:
|
modbus_coils:
|
||||||
- MgtSystem: {addr: 0, write: false, mqtt: false, influxdb: false, comment: Leitsystem aktiv}
|
- MgtSystem: {addr: 0, write: false, mqtt: true, influxdb: false, comment: Leitsystem aktiv}
|
||||||
- HK1pres: {addr: 1, write: false, mqtt: false, influxdb: false, comment: HK1 vorhanden}
|
- HK1pres: {addr: 1, write: false, mqtt: false, influxdb: false, comment: HK1 vorhanden}
|
||||||
- HK2pres: {addr: 2, write: false, mqtt: false, influxdb: false, comment: HK2 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}
|
- HK3pres: {addr: 3, write: false, mqtt: false, influxdb: false, comment: HK3 vorhanden}
|
||||||
@@ -78,58 +79,58 @@ modbus_input_register:
|
|||||||
- VKW: {addr: 43, 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}
|
- VSPm: {addr: 44, type: INT16, factor: 0.1, mqtt: false, influxdb: true}
|
||||||
modbus_holding_register:
|
modbus_holding_register:
|
||||||
- nothing: {addr: 0, 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, mqtt: false, influxdb: true}
|
- ErrLS: {addr: 1, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||||
- TVsoll: {addr: 2, type: INT16, factor: 0.1, 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, mqtt: false, influxdb: false}
|
- TV2soll: {addr: 3, type: INT16, factor: 0.1, write: false, mqtt: false, influxdb: false}
|
||||||
- TV3soll: {addr: 4, type: INT16, factor: 0.1, 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, mqtt: false, influxdb: true}
|
- HK1soll: {addr: 5, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||||
- HK2soll: {addr: 6, type: UINT16, factor: 1, mqtt: false, influxdb: false}
|
- HK2soll: {addr: 6, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||||
- HK3soll: {addr: 7, type: UINT16, factor: 1, 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, mqtt: false, influxdb: true}
|
- TWWsoll: {addr: 8, type: INT16, factor: 0.1, write: false, mqtt: false, influxdb: true}
|
||||||
- TV1max: {addr: 9, type: INT16, factor: 0.1, 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, mqtt: false, influxdb: false}
|
- TV2max: {addr: 10, type: INT16, factor: 0.1, write: false, mqtt: false, influxdb: false}
|
||||||
- TV3max: {addr: 11, type: INT16, factor: 0.1, 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, mqtt: false, influxdb: true}
|
- ErrHR: {addr: 12, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||||
- ErrSR: {addr: 13, type: UINT16, factor: 1, 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, 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, mqtt: false, influxdb: false}
|
- ErrWE1_2: {addr: 15, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||||
- ErrWE1_3: {addr: 16, type: UINT16, factor: 1, 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, 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, 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, mqtt: false, influxdb: true}
|
- KollLei: {addr: 19, type: UINT16, factor: 0.1, write: false, mqtt: false, influxdb: true}
|
||||||
- TagesS: {addr: 20, type: UINT16, factor: 0.1, mqtt: true, influxdb: true}
|
- TagesS: {addr: 20, type: UINT16, factor: 0.1, write: false, mqtt: true, influxdb: true}
|
||||||
- GesS: {addr: 21, type: UINT32, factor: 0.1, mqtt: false, influxdb: true}
|
- GesS: {addr: 21, type: UINT32, factor: 0.1, write: false, mqtt: false, influxdb: true}
|
||||||
- GesWW: {addr: 23, type: UINT32, factor: 0.1, 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, 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, mqtt: false, influxdb: true}
|
- HGesK1: {addr: 27, type: UINT32, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||||
- StartK1: {addr: 29, type: UINT32, factor: 1, mqtt: false, influxdb: true}
|
- StartK1: {addr: 29, type: UINT32, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||||
- HGesPel: {addr: 31, type: UINT32, factor: 1, 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, 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, mqtt: false, influxdb: true}
|
- StatWW: {addr: 34, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||||
- StatZ: {addr: 35, type: UINT16, factor: 1, mqtt: false, influxdb: true}
|
- StatZ: {addr: 35, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||||
- StatHK1: {addr: 36, type: UINT16, factor: 1, mqtt: false, influxdb: true}
|
- StatHK1: {addr: 36, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||||
- StatHK2: {addr: 37, type: UINT16, factor: 1, mqtt: false, influxdb: false}
|
- StatHK2: {addr: 37, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||||
- StatHK3: {addr: 38, type: UINT16, factor: 1, mqtt: false, influxdb: false}
|
- StatHK3: {addr: 38, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||||
- StatS: {addr: 39, type: UINT16, factor: 1, mqtt: false, influxdb: true}
|
- StatS: {addr: 39, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||||
- StatSB: {addr: 40, type: UINT16, factor: 1, mqtt: false, influxdb: false}
|
- StatSB: {addr: 40, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||||
- StatK1: {addr: 41, type: UINT16, factor: 1, mqtt: false, influxdb: true}
|
- StatK1: {addr: 41, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||||
- StatPel: {addr: 42, type: UINT16, factor: 1, mqtt: false, influxdb: true}
|
- StatPel: {addr: 42, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||||
- StatKH: {addr: 43, type: UINT16, factor: 1, mqtt: false, influxdb: false}
|
- StatKH: {addr: 43, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||||
- TPOsoll: {addr: 44, type: UINT16, factor: 0.1, mqtt: false, influxdb: true}
|
- TPOsoll: {addr: 44, type: UINT16, factor: 0.1, write: false, mqtt: false, influxdb: true}
|
||||||
- FATVsoll: {addr: 45, type: UINT16, factor: 0.1, 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, mqtt: false, influxdb: false}
|
- TSBsollHK: {addr: 46, type: INT16, factor: 0.1, write: false, mqtt: false, influxdb: false}
|
||||||
- TSBsollS: {addr: 47, type: INT16, factor: 0.1, 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, mqtt: false, influxdb: true}
|
- BetrHK1: {addr: 48, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: true}
|
||||||
- BetrHK2: {addr: 49, type: UINT16, factor: 1, mqtt: false, influxdb: false}
|
- BetrHK2: {addr: 49, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||||
- BetrHK3: {addr: 50, type: UINT16, factor: 1, mqtt: false, influxdb: false}
|
- BetrHK3: {addr: 50, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||||
- BetrSB: {addr: 51, type: UINT16, factor: 1, 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, 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, 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, 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, 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, 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, mqtt: false, influxdb: false}
|
- ErrWE1: {addr: 57, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||||
- ErrWE2: {addr: 58, type: UINT16, factor: 1, mqtt: false, influxdb: false}
|
- ErrWE2: {addr: 58, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||||
- ErrWE3: {addr: 59, type: UINT16, factor: 1, mqtt: false, influxdb: false}
|
- ErrWE3: {addr: 59, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||||
- ErrWE4: {addr: 60, type: UINT16, factor: 1, mqtt: false, influxdb: false}
|
- ErrWE4: {addr: 60, type: UINT16, factor: 1, write: false, mqtt: false, influxdb: false}
|
||||||
+2
-1
@@ -24,7 +24,8 @@ pub struct MqttConfig {
|
|||||||
pub user: Option<String>,
|
pub user: Option<String>,
|
||||||
pub password: Option<String>,
|
pub password: Option<String>,
|
||||||
pub path: Option<String>,
|
pub path: Option<String>,
|
||||||
pub button_circulation: Option<String>,
|
pub leitsystem_path: Option<String>,
|
||||||
|
pub set_write_interval_ms: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
|||||||
+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
|
||||||
|
}
|
||||||
+38
-33
@@ -10,11 +10,16 @@ mod config;
|
|||||||
mod modbus;
|
mod modbus;
|
||||||
mod app_state;
|
mod app_state;
|
||||||
mod mqtt;
|
mod mqtt;
|
||||||
|
mod influx;
|
||||||
pub mod modbus_types;
|
pub mod modbus_types;
|
||||||
use crate::config::{AppConfig, ModbusValueMaps};
|
use crate::config::{AppConfig, ModbusValueMaps};
|
||||||
use crate::modbus_types::{ModbusInputRegisterConfig, ModbusHoldingRegisterConfig, ModbusCoilsConfig};
|
use crate::modbus_types::{ModbusInputRegisterConfig, ModbusHoldingRegisterConfig, ModbusCoilsConfig};
|
||||||
use crate::app_state::AppState;
|
use crate::app_state::AppState;
|
||||||
|
|
||||||
|
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> {
|
async fn index(data: web::Data<AppState>) -> Result<HttpResponse> {
|
||||||
let config = data.config.lock().unwrap();
|
let config = data.config.lock().unwrap();
|
||||||
let value_maps = data.value_maps.lock().unwrap();
|
let value_maps = data.value_maps.lock().unwrap();
|
||||||
@@ -126,18 +131,10 @@ async fn settings_page(data: web::Data<AppState>) -> Result<HttpResponse> {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
enum SaveTableRows {
|
|
||||||
InputRegister(Vec<HashMap<String, ModbusInputRegisterConfig>>),
|
|
||||||
HoldingRegister(Vec<HashMap<String, ModbusHoldingRegisterConfig>>),
|
|
||||||
Coils(Vec<HashMap<String, ModbusCoilsConfig>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct SaveTableRequest {
|
struct SaveTableRequest {
|
||||||
table_id: String,
|
table_id: String,
|
||||||
rows: SaveTableRows,
|
rows: serde_json::Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn save_table(
|
async fn save_table(
|
||||||
@@ -145,28 +142,31 @@ async fn save_table(
|
|||||||
req: web::Json<SaveTableRequest>,
|
req: web::Json<SaveTableRequest>,
|
||||||
) -> Result<HttpResponse> {
|
) -> Result<HttpResponse> {
|
||||||
let mut config = data.config.lock().unwrap();
|
let mut config = data.config.lock().unwrap();
|
||||||
let conf_path = "paramod.yaml";
|
let conf_path = get_config_path();
|
||||||
match req.table_id.as_str() {
|
match req.table_id.as_str() {
|
||||||
"modbus_input_register" => {
|
"modbus_input_register" => {
|
||||||
if let SaveTableRows::InputRegister(rows) = req.rows.clone() {
|
let rows: Vec<HashMap<String, ModbusInputRegisterConfig>> =
|
||||||
config.modbus_input_register = Some(rows);
|
match serde_json::from_value(req.rows.clone()) {
|
||||||
} else {
|
Ok(v) => v,
|
||||||
return Ok(HttpResponse::BadRequest().body("Falscher Typ für input_register"));
|
Err(e) => return Ok(HttpResponse::BadRequest().body(format!("Falscher Typ für input_register: {}", e))),
|
||||||
}
|
};
|
||||||
|
config.modbus_input_register = Some(rows);
|
||||||
}
|
}
|
||||||
"modbus_holding_register" => {
|
"modbus_holding_register" => {
|
||||||
if let SaveTableRows::HoldingRegister(rows) = req.rows.clone() {
|
let rows: Vec<HashMap<String, ModbusHoldingRegisterConfig>> =
|
||||||
config.modbus_holding_register = Some(rows);
|
match serde_json::from_value(req.rows.clone()) {
|
||||||
} else {
|
Ok(v) => v,
|
||||||
return Ok(HttpResponse::BadRequest().body("Falscher Typ für holding_register"));
|
Err(e) => return Ok(HttpResponse::BadRequest().body(format!("Falscher Typ für holding_register: {}", e))),
|
||||||
}
|
};
|
||||||
|
config.modbus_holding_register = Some(rows);
|
||||||
}
|
}
|
||||||
"modbus_coils" => {
|
"modbus_coils" => {
|
||||||
if let SaveTableRows::Coils(rows) = req.rows.clone() {
|
let rows: Vec<HashMap<String, ModbusCoilsConfig>> =
|
||||||
config.modbus_coils = Some(rows);
|
match serde_json::from_value(req.rows.clone()) {
|
||||||
} else {
|
Ok(v) => v,
|
||||||
return Ok(HttpResponse::BadRequest().body("Falscher Typ für coils"));
|
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")),
|
_ => return Ok(HttpResponse::BadRequest().body("Invalid table_id")),
|
||||||
}
|
}
|
||||||
@@ -174,7 +174,7 @@ async fn save_table(
|
|||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => return Ok(HttpResponse::InternalServerError().body(format!("Serialisierungsfehler: {}", e))),
|
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)));
|
return Ok(HttpResponse::InternalServerError().body(format!("Fehler beim Schreiben: {}", e)));
|
||||||
}
|
}
|
||||||
Ok(HttpResponse::Ok().body("success"))
|
Ok(HttpResponse::Ok().body("success"))
|
||||||
@@ -199,16 +199,16 @@ async fn save_settings(
|
|||||||
// Value-Maps neu initialisieren
|
// Value-Maps neu initialisieren
|
||||||
let mut value_maps = data.value_maps.lock().unwrap();
|
let mut value_maps = data.value_maps.lock().unwrap();
|
||||||
*value_maps = ModbusValueMaps::from_config(&new_config);
|
*value_maps = ModbusValueMaps::from_config(&new_config);
|
||||||
let conf_path = "paramod.yaml";
|
let conf_path = get_config_path();
|
||||||
let yaml_str = match serde_yaml::to_string(&new_config) {
|
let yaml_str = match serde_yaml::to_string(&new_config) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => return Ok(HttpResponse::InternalServerError().body(format!("Serialisierungsfehler: {}", e))),
|
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)));
|
return Ok(HttpResponse::InternalServerError().body(format!("Fehler beim Schreiben: {}", e)));
|
||||||
}
|
}
|
||||||
// Reload config from file
|
// Reload config from file
|
||||||
let conf_str = match std::fs::read_to_string(conf_path) {
|
let conf_str = match std::fs::read_to_string(&conf_path) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => return Ok(HttpResponse::InternalServerError().body(format!("Fehler beim Lesen: {}", e))),
|
Err(e) => return Ok(HttpResponse::InternalServerError().body(format!("Fehler beim Lesen: {}", e))),
|
||||||
};
|
};
|
||||||
@@ -230,9 +230,8 @@ async fn get_config(data: web::Data<AppState>) -> HttpResponse {
|
|||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
|
|
||||||
|
let config_path = get_config_path();
|
||||||
let config_path = "paramod.yaml";
|
let app_state = web::Data::new(AppState::load_from_conf(&config_path));
|
||||||
let app_state = web::Data::new(AppState::load_from_conf(config_path));
|
|
||||||
|
|
||||||
// Starte Modbus-Polling-Thread
|
// Starte Modbus-Polling-Thread
|
||||||
{
|
{
|
||||||
@@ -244,7 +243,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
&config.modbus_holding_register,
|
&config.modbus_holding_register,
|
||||||
&config.modbus_coils,
|
&config.modbus_coils,
|
||||||
value_maps,
|
value_maps,
|
||||||
std::time::Duration::from_secs(2), // Poll-Intervall
|
std::time::Duration::from_secs(10), // Poll-Intervall
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,6 +253,12 @@ async fn main() -> std::io::Result<()> {
|
|||||||
mqtt::start_mqtt_thread(Arc::clone(&app_state.config), 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");
|
println!("Server läuft auf http://0.0.0.0:8080");
|
||||||
|
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
|
|||||||
+107
@@ -1,5 +1,6 @@
|
|||||||
use crate::config::{ModbusValueMaps, ModbusConfig};
|
use crate::config::{ModbusValueMaps, ModbusConfig};
|
||||||
use crate::modbus_types::{ModbusInputRegisterConfig, ModbusHoldingRegisterConfig, ModbusCoilsConfig};
|
use crate::modbus_types::{ModbusInputRegisterConfig, ModbusHoldingRegisterConfig, ModbusCoilsConfig};
|
||||||
|
use crate::config::AppConfig;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
@@ -9,6 +10,112 @@ use tokio_modbus::prelude::*;
|
|||||||
use tokio_modbus::client::tcp;
|
use tokio_modbus::client::tcp;
|
||||||
use std::net::SocketAddr;
|
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
|
/// Startet einen Thread, der zyklisch Modbus-Register abfragt und ModbusValueMaps aktualisiert
|
||||||
pub fn start_modbus_polling_thread(
|
pub fn start_modbus_polling_thread(
|
||||||
modbus_config: &ModbusConfig,
|
modbus_config: &ModbusConfig,
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,6 @@ pub struct ModbusCoilsConfig {
|
|||||||
pub comment: Option<String>,
|
pub comment: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct ModbusInputRegisterConfig {
|
pub struct ModbusInputRegisterConfig {
|
||||||
pub addr: u16,
|
pub addr: u16,
|
||||||
@@ -25,6 +24,7 @@ pub struct ModbusHoldingRegisterConfig {
|
|||||||
pub addr: u16,
|
pub addr: u16,
|
||||||
pub r#type: Option<String>,
|
pub r#type: Option<String>,
|
||||||
pub factor: Option<f64>,
|
pub factor: Option<f64>,
|
||||||
|
pub write: Option<bool>,
|
||||||
pub mqtt: Option<bool>,
|
pub mqtt: Option<bool>,
|
||||||
pub influxdb: Option<bool>,
|
pub influxdb: Option<bool>,
|
||||||
pub comment: Option<String>,
|
pub comment: Option<String>,
|
||||||
|
|||||||
+157
-31
@@ -1,8 +1,11 @@
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::process;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::{Duration, Instant};
|
||||||
use crate::config::{AppConfig, ModbusValueMaps};
|
use crate::config::{AppConfig, ModbusValueMaps};
|
||||||
use rumqttc::{MqttOptions, Client, QoS};
|
use crate::modbus;
|
||||||
|
use rumqttc::{Event, MqttOptions, Packet, Client, QoS, RecvTimeoutError};
|
||||||
|
|
||||||
pub fn start_mqtt_thread(config: Arc<Mutex<AppConfig>>, values: Arc<Mutex<ModbusValueMaps>>) {
|
pub fn start_mqtt_thread(config: Arc<Mutex<AppConfig>>, values: Arc<Mutex<ModbusValueMaps>>) {
|
||||||
let mqtt_config = {
|
let mqtt_config = {
|
||||||
@@ -12,59 +15,182 @@ pub fn start_mqtt_thread(config: Arc<Mutex<AppConfig>>, values: Arc<Mutex<Modbus
|
|||||||
let broker = if mqtt_config.broker.is_empty() { "localhost".to_string() } else { mqtt_config.broker.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 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 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 user = mqtt_config.user.clone().unwrap_or_default();
|
||||||
let password = mqtt_config.password.clone().unwrap_or_default();
|
let password = mqtt_config.password.clone().unwrap_or_default();
|
||||||
let user_is_empty = user.is_empty();
|
let user_is_empty = user.is_empty();
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let mut mqttoptions = MqttOptions::new("paramod-client", broker, port);
|
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 {
|
if !user_is_empty {
|
||||||
mqttoptions.set_credentials(user, password);
|
mqttoptions.set_credentials(user, password);
|
||||||
}
|
}
|
||||||
let (mut client, mut connection) = Client::new(mqttoptions, 10);
|
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 {
|
loop {
|
||||||
{
|
if last_publish.elapsed() >= publish_interval {
|
||||||
let values = values.lock().unwrap();
|
{
|
||||||
// Input Register
|
let values = values.lock().unwrap();
|
||||||
for (name, val) in &values.modbus_input_register_values {
|
// Input Register
|
||||||
if let Some(v) = val {
|
for (name, val) in &values.modbus_input_register_values {
|
||||||
if should_publish(&config, name, "input_register") {
|
if let Some(v) = val {
|
||||||
let topic = format!("{}/{}", path, name);
|
if should_publish(&config, name, "input_register") {
|
||||||
let payload = format!("{}", v);
|
let topic = format!("{}/{}/state", path, name);
|
||||||
let _ = client.publish(topic, QoS::AtLeastOnce, false, payload);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Holding Register
|
|
||||||
for (name, val) in &values.modbus_holding_register_values {
|
let state_payload = if leitsystem_enabled { "ON" } else { "OFF" };
|
||||||
if let Some(v) = val {
|
let _ = client.publish(
|
||||||
if should_publish(&config, name, "holding_register") {
|
leitsystem_state_topic.clone(),
|
||||||
let topic = format!("{}/{}", path, name);
|
QoS::AtLeastOnce,
|
||||||
let payload = format!("{}", v);
|
true,
|
||||||
let _ = client.publish(topic, QoS::AtLeastOnce, false, payload);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Coils
|
|
||||||
for (name, val) in &values.modbus_coils_values {
|
last_set_write = Instant::now();
|
||||||
if let Some(v) = val {
|
}
|
||||||
if should_publish(&config, name, "coils") {
|
|
||||||
let topic = format!("{}/{}", path, name);
|
match connection.recv_timeout(Duration::from_millis(200)) {
|
||||||
let payload = format!("{}", v);
|
Ok(Ok(Event::Incoming(Packet::Publish(publish)))) => {
|
||||||
let _ = client.publish(topic, QoS::AtLeastOnce, false, payload);
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Handle MQTT connection events
|
|
||||||
for _event in connection.iter().take(1) {
|
|
||||||
// Optionally log or handle events
|
|
||||||
}
|
|
||||||
thread::sleep(Duration::from_secs(5));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
fn should_publish(config: &Arc<Mutex<AppConfig>>, name: &str, reg_type: &str) -> bool {
|
||||||
let cfg = config.lock().unwrap();
|
let cfg = config.lock().unwrap();
|
||||||
match reg_type {
|
match reg_type {
|
||||||
|
|||||||
+47
-4
@@ -56,6 +56,34 @@ function addRow() {
|
|||||||
<button class="delete-btn" onclick="deleteRow(this)">🗑️</button>
|
<button class="delete-btn" onclick="deleteRow(this)">🗑️</button>
|
||||||
</td>
|
</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 {
|
} else {
|
||||||
newRow.innerHTML = `
|
newRow.innerHTML = `
|
||||||
<td><input type='text' class='text-input' data-field='bezeichnung' value='' /></td>
|
<td><input type='text' class='text-input' data-field='bezeichnung' value='' /></td>
|
||||||
@@ -119,13 +147,27 @@ async function saveTable() {
|
|||||||
influxdb: influxdb,
|
influxdb: influxdb,
|
||||||
comment: comment
|
comment: comment
|
||||||
};
|
};
|
||||||
} else {
|
} else if (typeof tableId !== 'undefined' && tableId === 'modbus_holding_register') {
|
||||||
const rtype = row.querySelector("input[data-field='type']")?.value || null;
|
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 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;
|
const comment = row.querySelector("input[data-field='comment']")?.value || null;
|
||||||
value = {
|
value = {
|
||||||
addr: addr,
|
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,
|
factor: factor,
|
||||||
mqtt: mqtt,
|
mqtt: mqtt,
|
||||||
influxdb: influxdb,
|
influxdb: influxdb,
|
||||||
@@ -154,8 +196,9 @@ async function saveTable() {
|
|||||||
messageDiv.className = 'message success';
|
messageDiv.className = 'message success';
|
||||||
messageDiv.textContent = '✓ Erfolgreich gespeichert!';
|
messageDiv.textContent = '✓ Erfolgreich gespeichert!';
|
||||||
} else {
|
} else {
|
||||||
|
const errorText = (await response.text()) || 'Unbekannter Fehler';
|
||||||
messageDiv.className = 'message error';
|
messageDiv.className = 'message error';
|
||||||
messageDiv.textContent = '✗ Fehler beim Speichern!';
|
messageDiv.textContent = `✗ Fehler beim Speichern: ${errorText}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
+2
-1
@@ -55,7 +55,8 @@ async function saveSettings() {
|
|||||||
user: document.getElementById('mqtt_user').value || null,
|
user: document.getElementById('mqtt_user').value || null,
|
||||||
password: document.getElementById('mqtt_password').value || null,
|
password: document.getElementById('mqtt_password').value || null,
|
||||||
path: document.getElementById('mqtt_path')?.value || null,
|
path: document.getElementById('mqtt_path')?.value || null,
|
||||||
button_circulation: document.getElementById('mqtt_button_circulation').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
|
// InfluxDB
|
||||||
|
|||||||
+2
-2
@@ -96,8 +96,8 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.logo-text {
|
.logo-text {
|
||||||
font-size: 20px;
|
font-size: 40px;
|
||||||
font-weight: 700;
|
font-weight: 800;
|
||||||
color: var(--text-light);
|
color: var(--text-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-3
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<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">
|
<link rel="stylesheet" href="/static/style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
<rect width="40" height="40" rx="8" fill="#667eea"/>
|
<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"/>
|
<path d="M12 20L18 26L28 14" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
<span class="logo-text">Sensor Manager</span>
|
<span class="logo-text">Paramod</span>
|
||||||
</div>
|
</div>
|
||||||
<nav class="nav">
|
<nav class="nav">
|
||||||
<a href="/table/modbus_input_register" class="nav-link {% if active_page == 'modbus_input_register' %}active{% endif %}">Input Register</a>
|
<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>
|
</header>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>🔧 Sensor Konfiguration - {{ table_id | upper }}</h1>
|
<h1>🔧 Konfiguration - {{ table_id | upper }}</h1>
|
||||||
<div id="message" class="message"></div>
|
<div id="message" class="message"></div>
|
||||||
<div class="table-wrapper">
|
<div class="table-wrapper">
|
||||||
<table id="sensorTable">
|
<table id="sensorTable">
|
||||||
@@ -37,6 +37,10 @@
|
|||||||
<th>Adresse</th>
|
<th>Adresse</th>
|
||||||
{% if table_id == "modbus_coils" %}
|
{% if table_id == "modbus_coils" %}
|
||||||
<th>Write</th>
|
<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 %}
|
{% else %}
|
||||||
<th>Type</th>
|
<th>Type</th>
|
||||||
<th>Faktor</th>
|
<th>Faktor</th>
|
||||||
@@ -63,6 +67,15 @@
|
|||||||
<span class='slider'></span>
|
<span class='slider'></span>
|
||||||
</label>
|
</label>
|
||||||
</td>
|
</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 %}
|
{% 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='type' value='{{ row.type | default(value="") }}' /></td>
|
||||||
<td><input type='text' class='text-input' data-field='factor' value='{{ row.factor | default(value="") }}' /></td>
|
<td><input type='text' class='text-input' data-field='factor' value='{{ row.factor | default(value="") }}' /></td>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<rect width="40" height="40" rx="8" fill="#667eea"/>
|
<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"/>
|
<path d="M12 20L18 26L28 14" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
<span class="logo-text">Sensor Manager</span>
|
<span class="logo-text">Paramod</span>
|
||||||
</div>
|
</div>
|
||||||
<nav class="nav">
|
<nav class="nav">
|
||||||
<a href="/table/modbus_input_register" class="nav-link {% if active_page == 'modbus_input_register' %}active{% endif %}">Input Register</a>
|
<a href="/table/modbus_input_register" class="nav-link {% if active_page == 'modbus_input_register' %}active{% endif %}">Input Register</a>
|
||||||
@@ -98,8 +98,12 @@
|
|||||||
<input type="text" id="mqtt_path" class="text-input" value="{{ mqtt.path | default(value="") }}" />
|
<input type="text" id="mqtt_path" class="text-input" value="{{ mqtt.path | default(value="") }}" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="mqtt_button_circulation">Button Circulation:</label>
|
<label for="mqtt_leitsystem_path">Leitsystem Pfad:</label>
|
||||||
<input type="text" id="mqtt_button_circulation" class="text-input" value="{{ mqtt.button_circulation | default(value="") }}" />
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user