Compare commits
5 Commits
e96af3ac7e
...
5b0f9013aa
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b0f9013aa | |||
| 9d33984c97 | |||
| 89f02422e7 | |||
| 58f5522442 | |||
| e6c60d0707 |
@ -12,10 +12,6 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Set version in Cargo.toml
|
|
||||||
run: |
|
|
||||||
VERSION=$(cat VERSION)
|
|
||||||
sed -i "s/^version = \"__VERSION__\"/version = \"$VERSION\"/" Cargo.toml
|
|
||||||
- name: Build image with Podman
|
- name: Build image with Podman
|
||||||
env:
|
env:
|
||||||
BUILDAH_ISOLATION: chroot
|
BUILDAH_ISOLATION: chroot
|
||||||
|
|||||||
3
.vscode/cargo-build-version.sh
vendored
3
.vscode/cargo-build-version.sh
vendored
@ -1,5 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
# Patch Cargo.toml version from VERSION file before build
|
# Patch Cargo.toml version from VERSION file before build
|
||||||
VERSION=$(cat VERSION)
|
VERSION=$(cat VERSION)
|
||||||
sed -i "s/^version = ".*"/version = \"$VERSION\"/" Cargo.toml
|
sed -i 's/^version = ".*"/version = "'"$VERSION"'"/' Cargo.toml
|
||||||
exec cargo build
|
exec cargo build
|
||||||
|
|||||||
3
.vscode/cargo-reset-version.sh
vendored
Normal file
3
.vscode/cargo-reset-version.sh
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
sed -i 's/^version = ".*"/version = "__VERSION__"/' Cargo.toml
|
||||||
12
.vscode/cargo-run-version.sh
vendored
Normal file
12
.vscode/cargo-run-version.sh
vendored
Normal file
@ -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
|
||||||
3
.vscode/launch.json
vendored
3
.vscode/launch.json
vendored
@ -9,7 +9,8 @@
|
|||||||
"args": [],
|
"args": [],
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}",
|
||||||
"sourceLanguages": ["rust"],
|
"sourceLanguages": ["rust"],
|
||||||
"preLaunchTask": "cargo build"
|
"preLaunchTask": "cargo build",
|
||||||
|
"postDebugTask": "cargo reset version placeholder"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
14
.vscode/tasks.json
vendored
14
.vscode/tasks.json
vendored
@ -9,7 +9,19 @@
|
|||||||
"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"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,11 +6,14 @@ WORKDIR /usr/src/app
|
|||||||
|
|
||||||
COPY Cargo.toml ./
|
COPY Cargo.toml ./
|
||||||
COPY Cargo.lock ./
|
COPY Cargo.lock ./
|
||||||
|
COPY VERSION ./
|
||||||
COPY src ./src
|
COPY src ./src
|
||||||
COPY templates ./templates
|
COPY templates ./templates
|
||||||
COPY static ./static
|
COPY static ./static
|
||||||
|
|
||||||
RUN cargo build --release
|
RUN VERSION=$(cat VERSION) \
|
||||||
|
&& sed -i 's/^version = "__VERSION__"/version = "'"$VERSION"'"/' Cargo.toml \
|
||||||
|
&& cargo build --release
|
||||||
|
|
||||||
# Runtime Stage
|
# Runtime Stage
|
||||||
FROM debian:bookworm-slim
|
FROM debian:bookworm-slim
|
||||||
|
|||||||
187
README.md
187
README.md
@ -1,190 +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
|
|
||||||
|
|
||||||
```
|
|
||||||
paramod/
|
|
||||||
├── 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 paramod
|
|
||||||
cd paramod
|
|
||||||
|
|
||||||
# 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
|
||||||
podman build -t paramod .
|
podman build -t paramod:latest .
|
||||||
```
|
```
|
||||||
|
|
||||||
### Container starten
|
### Container starten
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
poddman run -p 8080:8080 -v $(pwd)/data:/app/data paramod
|
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.
|
|
||||||
|
|
||||||
## 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 `paramod.yaml` 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,
|
||||||
|
"write": true,
|
||||||
"mqtt": true,
|
"mqtt": true,
|
||||||
"influxdb": false
|
"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
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ mqtt:
|
|||||||
password: 97sm3pHNSMZ4M5qUj0x8
|
password: 97sm3pHNSMZ4M5qUj0x8
|
||||||
path: heizung/paradigma
|
path: heizung/paradigma
|
||||||
leitsystem_path: heizung/leitsystem2
|
leitsystem_path: heizung/leitsystem2
|
||||||
|
set_write_interval_ms: 2000
|
||||||
influxdb:
|
influxdb:
|
||||||
bucket: Paradigma
|
bucket: Paradigma
|
||||||
org: skaville
|
org: skaville
|
||||||
|
|||||||
58
src/main.rs
58
src/main.rs
@ -16,6 +16,10 @@ 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();
|
||||||
@ -127,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(
|
||||||
@ -146,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>> =
|
||||||
|
match serde_json::from_value(req.rows.clone()) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => return Ok(HttpResponse::BadRequest().body(format!("Falscher Typ für input_register: {}", e))),
|
||||||
|
};
|
||||||
config.modbus_input_register = Some(rows);
|
config.modbus_input_register = Some(rows);
|
||||||
} else {
|
|
||||||
return Ok(HttpResponse::BadRequest().body("Falscher Typ für input_register"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
"modbus_holding_register" => {
|
"modbus_holding_register" => {
|
||||||
if let SaveTableRows::HoldingRegister(rows) = req.rows.clone() {
|
let rows: Vec<HashMap<String, ModbusHoldingRegisterConfig>> =
|
||||||
|
match serde_json::from_value(req.rows.clone()) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => return Ok(HttpResponse::BadRequest().body(format!("Falscher Typ für holding_register: {}", e))),
|
||||||
|
};
|
||||||
config.modbus_holding_register = Some(rows);
|
config.modbus_holding_register = Some(rows);
|
||||||
} else {
|
|
||||||
return Ok(HttpResponse::BadRequest().body("Falscher Typ für holding_register"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
"modbus_coils" => {
|
"modbus_coils" => {
|
||||||
if let SaveTableRows::Coils(rows) = req.rows.clone() {
|
let rows: Vec<HashMap<String, ModbusCoilsConfig>> =
|
||||||
|
match serde_json::from_value(req.rows.clone()) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => return Ok(HttpResponse::BadRequest().body(format!("Falscher Typ für coils: {}", e))),
|
||||||
|
};
|
||||||
config.modbus_coils = Some(rows);
|
config.modbus_coils = Some(rows);
|
||||||
} else {
|
|
||||||
return Ok(HttpResponse::BadRequest().body("Falscher Typ für coils"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => return Ok(HttpResponse::BadRequest().body("Invalid table_id")),
|
_ => return Ok(HttpResponse::BadRequest().body("Invalid table_id")),
|
||||||
}
|
}
|
||||||
@ -175,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"))
|
||||||
@ -200,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))),
|
||||||
};
|
};
|
||||||
@ -231,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
|
||||||
{
|
{
|
||||||
@ -245,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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -37,7 +37,7 @@ pub fn start_mqtt_thread(config: Arc<Mutex<AppConfig>>, values: Arc<Mutex<Modbus
|
|||||||
if let Err(e) = client.subscribe(set_topic, QoS::AtLeastOnce) {
|
if let Err(e) = client.subscribe(set_topic, QoS::AtLeastOnce) {
|
||||||
eprintln!("MQTT Subscribe fehlgeschlagen: {}", e);
|
eprintln!("MQTT Subscribe fehlgeschlagen: {}", e);
|
||||||
}
|
}
|
||||||
let leitsystem_state_topic = format!("{}/state", leitsystem_path.trim_end_matches('/'));
|
let leitsystem_state_topic = format!("{}/active", leitsystem_path.trim_end_matches('/'));
|
||||||
if let Err(e) = client.subscribe(leitsystem_state_topic.clone(), QoS::AtLeastOnce) {
|
if let Err(e) = client.subscribe(leitsystem_state_topic.clone(), QoS::AtLeastOnce) {
|
||||||
eprintln!("MQTT Subscribe Leitsystem fehlgeschlagen: {}", e);
|
eprintln!("MQTT Subscribe Leitsystem fehlgeschlagen: {}", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,7 +56,7 @@ 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_registers') {
|
} else if (typeof tableId !== 'undefined' && tableId === 'modbus_holding_register') {
|
||||||
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>
|
||||||
<td><input type='text' class='text-input' data-field='adresse' value='' /></td>
|
<td><input type='text' class='text-input' data-field='adresse' value='' /></td>
|
||||||
@ -147,14 +147,14 @@ async function saveTable() {
|
|||||||
influxdb: influxdb,
|
influxdb: influxdb,
|
||||||
comment: comment
|
comment: comment
|
||||||
};
|
};
|
||||||
} else if (typeof tableId !== 'undefined' && tableId === 'modbus_holding_registers') {
|
} else if (typeof tableId !== 'undefined' && tableId === 'modbus_holding_register') {
|
||||||
const write = row.querySelector("input[data-field='write']")?.checked || false;
|
const write = row.querySelector("input[data-field='write']")?.checked || false;
|
||||||
const rtype = row.querySelector("input[data-field='type']")?.value || null;
|
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,
|
factor: factor,
|
||||||
write: write,
|
write: write,
|
||||||
mqtt: mqtt,
|
mqtt: mqtt,
|
||||||
@ -162,12 +162,12 @@ async function saveTable() {
|
|||||||
comment: comment
|
comment: comment
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const rtype = row.querySelector("input[data-field='type']")?.value || null;
|
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,
|
factor: factor,
|
||||||
mqtt: mqtt,
|
mqtt: mqtt,
|
||||||
influxdb: influxdb,
|
influxdb: influxdb,
|
||||||
@ -196,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(() => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user