mirror of
https://github.com/Djeeberjr/fw-anwesenheit.git
synced 2026-04-30 18:49:09 +00:00
Compare commits
5 Commits
4781570f8e
...
5c16aaa9fe
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c16aaa9fe | |||
| 24b48f6705 | |||
| 434353b1e3 | |||
| 6b9ef20187 | |||
| 3c1290aec3 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
/target
|
/target
|
||||||
|
/build
|
||||||
|
|||||||
61
Makefile
Normal file
61
Makefile
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
PACKAGE_NAME := fwa
|
||||||
|
VERSION := 1.0
|
||||||
|
ARCH := armhf
|
||||||
|
BUILD_DIR := build
|
||||||
|
DEB_DIR := $(BUILD_DIR)/$(PACKAGE_NAME)-$(VERSION)
|
||||||
|
BIN_DIR := $(DEB_DIR)/usr/local/bin
|
||||||
|
SERVICE_DIR := $(DEB_DIR)/lib/systemd/system
|
||||||
|
CONFIG_DIR := $(DEB_DIR)/etc
|
||||||
|
PM3_DIR := $(DEB_DIR)/usr/share/pm3
|
||||||
|
|
||||||
|
.PHONY: all build clean package prepare_package
|
||||||
|
|
||||||
|
all: package
|
||||||
|
|
||||||
|
build: $(BUILD_DIR)/fwa
|
||||||
|
|
||||||
|
$(BUILD_DIR)/fwa: web/dist
|
||||||
|
cross build --release --target arm-unknown-linux-gnueabihf
|
||||||
|
cp ./target/arm-unknown-linux-gnueabihf/release/fw-anwesenheit $@
|
||||||
|
|
||||||
|
prepare_package: $(DEB_DIR)/DEBIAN $(BIN_DIR)/fwa
|
||||||
|
mkdir -p $(SERVICE_DIR)
|
||||||
|
cp ./service/fwa.service $(SERVICE_DIR)/
|
||||||
|
cp ./service/fwa-fail.service $(SERVICE_DIR)/
|
||||||
|
|
||||||
|
mkdir -p $(CONFIG_DIR)
|
||||||
|
cp ./service/fwa.env $(CONFIG_DIR)/
|
||||||
|
|
||||||
|
mkdir -p $(PM3_DIR)
|
||||||
|
cp -r ./pre-compiled/* $(PM3_DIR)/
|
||||||
|
|
||||||
|
mkdir -p $(DEB_DIR)/var/lib/fwa/
|
||||||
|
|
||||||
|
$(BIN_DIR)/fwa: $(BUILD_DIR)/fwa
|
||||||
|
mkdir -p $(BIN_DIR)
|
||||||
|
cp $< $@
|
||||||
|
|
||||||
|
|
||||||
|
$(DEB_DIR)/DEBIAN:
|
||||||
|
mkdir -p $(DEB_DIR)/DEBIAN
|
||||||
|
echo "Package: $(PACKAGE_NAME)" > $(DEB_DIR)/DEBIAN/control
|
||||||
|
echo "Version: $(VERSION)" >> $(DEB_DIR)/DEBIAN/control
|
||||||
|
echo "Section: utils" >> $(DEB_DIR)/DEBIAN/control
|
||||||
|
echo "Priority: optional" >> $(DEB_DIR)/DEBIAN/control
|
||||||
|
echo "Architecture: $(ARCH)" >> $(DEB_DIR)/DEBIAN/control
|
||||||
|
echo "Depends: libc6 (>= 2.28)" >> $(DEB_DIR)/DEBIAN/control
|
||||||
|
echo "Maintainer: Niklas Kapelle <niklas@kapelle.org>" >> $(DEB_DIR)/DEBIAN/control
|
||||||
|
echo "Description: Feuerwehr anwesenheit" >> $(DEB_DIR)/DEBIAN/control
|
||||||
|
|
||||||
|
echo "/etc/fwa.env" > $(DEB_DIR)/DEBIAN/conffiles
|
||||||
|
|
||||||
|
web/dist:
|
||||||
|
(cd web && npm run build)
|
||||||
|
|
||||||
|
package: prepare_package
|
||||||
|
dpkg-deb --build $(DEB_DIR)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
cargo clean
|
||||||
|
rm -rf web/dist
|
||||||
|
rm -rf $(BUILD_DIR)
|
||||||
12
README.md
12
README.md
@@ -9,6 +9,10 @@ I²C fpr RTC `sudo raspi-config` -> interface -> enable I²C
|
|||||||
|
|
||||||
# Config
|
# Config
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
|
||||||
|
`--error` or `-e`: Enters error state. The LED turns red and the hotspot is activated. This state gets called from systemd if the service is in a failure state.
|
||||||
|
|
||||||
Environment variables:
|
Environment variables:
|
||||||
|
|
||||||
- `PM3_BIN`: Path to the pm3 binary. Seach in path if not set. Can also be set to the `pm3_mock.sh` for testing.
|
- `PM3_BIN`: Path to the pm3 binary. Seach in path if not set. Can also be set to the `pm3_mock.sh` for testing.
|
||||||
@@ -18,3 +22,11 @@ Environment variables:
|
|||||||
- `HOTSPOT_SSID`: Set the hotspot ssid. Defaults to "fwa".
|
- `HOTSPOT_SSID`: Set the hotspot ssid. Defaults to "fwa".
|
||||||
- `HOTSPOT_PW`: Set the hotspot password. Default to "a9LG2kUVrsRRVUo1". Recommended to change.
|
- `HOTSPOT_PW`: Set the hotspot password. Default to "a9LG2kUVrsRRVUo1". Recommended to change.
|
||||||
|
|
||||||
|
Systemd:
|
||||||
|
|
||||||
|
The service is run as a systemd service. There are two service `fwa.service` and `fwa-fail.service`. They read their config
|
||||||
|
from a env file located at `/etc/fwa.env`. See example [env file](service/fwa.env).
|
||||||
|
|
||||||
|
# Building
|
||||||
|
|
||||||
|
Run `make package` to create `.deb` file. [Cross](https://github.com/cross-rs/cross) is used for building the rust code.
|
||||||
|
|||||||
19
service/fwa-fail.service
Normal file
19
service/fwa-fail.service
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Failure state for fwa.service
|
||||||
|
Requires=local-fs.target
|
||||||
|
After=local-fs.target
|
||||||
|
StartLimitIntervalSec=500
|
||||||
|
StartLimitBurst=5
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
ExecStart=/usr/local/bin/fwa --error
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
User=root
|
||||||
|
Group=root
|
||||||
|
WorkingDirectory=/var/lib/fwa
|
||||||
|
EnvironmentFile=/etc/fwa.env
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
5
service/fwa.env
Normal file
5
service/fwa.env
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
PM3_BIN=/usr/share/pm3/pm3
|
||||||
|
LOG_LEVEL=warn
|
||||||
|
HOTSPOT_IDS=578B5DF2;c1532b57
|
||||||
|
HOTSPOT_SSID=fwa
|
||||||
|
HOTSPOT_PW=a9LG2kUVrsRRVUo1
|
||||||
@@ -4,6 +4,7 @@ Requires=local-fs.target
|
|||||||
After=local-fs.target
|
After=local-fs.target
|
||||||
StartLimitIntervalSec=500
|
StartLimitIntervalSec=500
|
||||||
StartLimitBurst=5
|
StartLimitBurst=5
|
||||||
|
OnFailure= fwa-fail.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
@@ -13,12 +14,7 @@ RestartSec=5
|
|||||||
User=root
|
User=root
|
||||||
Group=root
|
Group=root
|
||||||
WorkingDirectory=/var/lib/fwa
|
WorkingDirectory=/var/lib/fwa
|
||||||
|
EnvironmentFile=/etc/fwa.env
|
||||||
Environment="PM3_BIN=/usr/local/bin/pm3/pm3"
|
|
||||||
#Environment="LOG_LEVEL=warn"
|
|
||||||
#Environment="HOTSPOT_IDS=578B5DF2;c1532b57"
|
|
||||||
#Environment="HOTSPOT_SSID=fwa"
|
|
||||||
#Environment="HOTSPOT_PW=a9LG2kUVrsRRVUo1"
|
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
@@ -42,6 +42,12 @@ impl<B: Buzzer, L: StatusLed> Feedback<B, L> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn activate_error_state(&mut self) -> Result<()> {
|
||||||
|
self.led.turn_on(RED)?;
|
||||||
|
Self::beep_nak(&mut self.buzzer).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn blink_led_for_duration(led: &mut L, color: RGB8, duration: Duration) -> Result<()> {
|
async fn blink_led_for_duration(led: &mut L, color: RGB8, duration: Duration) -> Result<()> {
|
||||||
led.turn_on(color)?;
|
led.turn_on(color)?;
|
||||||
sleep(duration).await;
|
sleep(duration).await;
|
||||||
|
|||||||
34
src/main.rs
34
src/main.rs
@@ -1,15 +1,21 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use activity_fairing::{ActivityNotifier, spawn_idle_watcher};
|
use activity_fairing::{ActivityNotifier, spawn_idle_watcher};
|
||||||
|
use anyhow::Result;
|
||||||
use feedback::{Feedback, FeedbackImpl};
|
use feedback::{Feedback, FeedbackImpl};
|
||||||
use hardware::{Hotspot, create_hotspot};
|
use hardware::{Hotspot, create_hotspot};
|
||||||
use id_store::IDStore;
|
use id_store::IDStore;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
use pm3::run_pm3;
|
use pm3::run_pm3;
|
||||||
use std::{env, sync::Arc, time::Duration};
|
use std::{
|
||||||
|
env::{self, args},
|
||||||
|
sync::Arc,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
use tally_id::TallyID;
|
use tally_id::TallyID;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
fs,
|
fs,
|
||||||
|
signal::unix::{SignalKind, signal},
|
||||||
sync::{
|
sync::{
|
||||||
Mutex,
|
Mutex,
|
||||||
broadcast::{self, Receiver, Sender},
|
broadcast::{self, Receiver, Sender},
|
||||||
@@ -17,7 +23,6 @@ use tokio::{
|
|||||||
try_join,
|
try_join,
|
||||||
};
|
};
|
||||||
use webserver::start_webserver;
|
use webserver::start_webserver;
|
||||||
use anyhow::Result;
|
|
||||||
|
|
||||||
mod activity_fairing;
|
mod activity_fairing;
|
||||||
mod feedback;
|
mod feedback;
|
||||||
@@ -123,20 +128,36 @@ async fn handle_ids_loop(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn enter_error_state(mut feedback: FeedbackImpl, hotspot: Arc<Mutex<impl Hotspot>>) {
|
||||||
|
let _ = feedback.activate_error_state().await;
|
||||||
|
let _ = hotspot.lock().await.enable_hotspot().await;
|
||||||
|
|
||||||
|
let mut sigterm = signal(SignalKind::terminate()).unwrap();
|
||||||
|
sigterm.recv().await;
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
logger::setup_logger();
|
logger::setup_logger();
|
||||||
|
|
||||||
info!("Starting application");
|
info!("Starting application");
|
||||||
|
|
||||||
let (tx, rx) = broadcast::channel::<String>(32);
|
|
||||||
let sse_tx = tx.clone();
|
|
||||||
|
|
||||||
let store: Arc<Mutex<IDStore>> = Arc::new(Mutex::new(load_or_create_store().await?));
|
|
||||||
let user_feedback = Feedback::new()?;
|
let user_feedback = Feedback::new()?;
|
||||||
let hotspot = Arc::new(Mutex::new(create_hotspot()?));
|
let hotspot = Arc::new(Mutex::new(create_hotspot()?));
|
||||||
|
|
||||||
|
let error_flag_set = args().any(|e| e == "--error" || e == "-e");
|
||||||
|
if error_flag_set {
|
||||||
|
error!("Error flag set. Entering error state");
|
||||||
|
enter_error_state(user_feedback, hotspot).await;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let store: Arc<Mutex<IDStore>> = Arc::new(Mutex::new(load_or_create_store().await?));
|
||||||
let hotspot_enable_ids = get_hotspot_enable_ids();
|
let hotspot_enable_ids = get_hotspot_enable_ids();
|
||||||
|
|
||||||
|
let (tx, rx) = broadcast::channel::<String>(32);
|
||||||
|
let sse_tx = tx.clone();
|
||||||
|
|
||||||
let pm3_handle = run_pm3(tx);
|
let pm3_handle = run_pm3(tx);
|
||||||
|
|
||||||
let loop_handle = handle_ids_loop(
|
let loop_handle = handle_ids_loop(
|
||||||
@@ -153,6 +174,7 @@ async fn main() -> Result<()> {
|
|||||||
|
|
||||||
if let Err(e) = run_result {
|
if let Err(e) = run_result {
|
||||||
error!("Failed to run application: {e}");
|
error!("Failed to run application: {e}");
|
||||||
|
return Err(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -62,10 +62,15 @@ pub async fn run_pm3(tx: broadcast::Sender<String>) -> Result<()> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let status = cmd.wait().await?;
|
let status = cmd.wait().await?;
|
||||||
if status.success() {
|
|
||||||
|
// We use the exit code here because status.success() is false if the child was terminated by a
|
||||||
|
// signal
|
||||||
|
let code = status.code().unwrap_or(0);
|
||||||
|
|
||||||
|
if code == 0 {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("PM3 exited with a non-zero exit code"))
|
Err(anyhow!("PM3 exited with a non-zero exit code: {code}"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user