Compare commits
	
		
			38 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 7180acd809 | ||
|  | dc6b49dd44 | ||
|  | fbdf1ea24b | ||
| 06ce74da9f | |||
| 0acb2a2538 | |||
| bd300f163e | |||
| 25da1ac04b | |||
|  | 183764f2bb | ||
|  | ccbb23ebc7 | ||
|  | 35d65b6475 | ||
| ce7afca13c | |||
|  | 5c2f2f60ad | ||
|  | 2fcf37bfdc | ||
|  | cef76ad9aa | ||
|  | d4726a2b9a | ||
|  | a526e5fb22 | ||
|  | 0041d1c30c | ||
|  | 9d3dfda37e | ||
|  | c8d4ab3ba3 | ||
|  | c07d6db238 | ||
|  | 8e3f4f4286 | ||
|  | 922c023216 | ||
|  | 4473fa7f8b | ||
|  | f9a3451281 | ||
|  | efeb9d8227 | ||
|  | 955bc37afe | ||
|  | bdd65e740d | ||
|  | 0afbc1c508 | ||
|  | 3ecafd4534 | ||
|  | 1afa8fbe81 | ||
|  | b4a4cdd1ac | ||
|  | 5930417ae4 | ||
|  | 4b6afda278 | ||
|  | 256fdeee47 | ||
|  | c39f944b5a | ||
|  | 76fed4a4de | ||
|  | 3951b4e41c | ||
|  | e4a1788698 | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -4,3 +4,4 @@ | |||||||
| .vscode/launch.json | .vscode/launch.json | ||||||
| .vscode/ipch | .vscode/ipch | ||||||
| todo.txt | todo.txt | ||||||
|  | *.ini | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								README.md
									
									
									
									
									
								
							| @@ -25,3 +25,17 @@ stored in folder **/client** | |||||||
| go to **/client** | go to **/client** | ||||||
| * cargo build | * cargo build | ||||||
| * cargo run | * cargo run | ||||||
|  |  | ||||||
|  | ### Deamon | ||||||
|  | Requires ''systemd'' | ||||||
|  |  | ||||||
|  | Install by creating a link to this project | ||||||
|  | ``` | ||||||
|  | /etc/systemd/system# ln -s /home/c3ma/led-board/client/ledBoard.service ledBoard.service | ||||||
|  | systemctl daemon-reload | ||||||
|  | systemctl enable ledBoard.service | ||||||
|  | ``` | ||||||
|  | Start deamon with | ||||||
|  | ``` | ||||||
|  | systemctl start ledBoard.service | ||||||
|  | ``` | ||||||
|   | |||||||
							
								
								
									
										51
									
								
								client/MQTT.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								client/MQTT.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,51 @@ | |||||||
|  |  # MQTT Configuration | ||||||
|  |  | ||||||
|  |  This project can publish weather and public transport data to an MQTT broker. | ||||||
|  |  To enable MQTT, follow these steps: | ||||||
|  |  | ||||||
|  |  ## 1. Install dependencies | ||||||
|  |  Ensure you have Rust and Cargo installed. The MQTT support uses the Paho MQTT client crate. | ||||||
|  |  Run: | ||||||
|  |  ```bash | ||||||
|  |  cargo update | ||||||
|  |  ``` | ||||||
|  |  | ||||||
|  |  ## 2. Set the MQTT_BROKER environment variable | ||||||
|  |  Before running the client, define `MQTT_BROKER` to your broker address. | ||||||
|  |  - Without URI scheme (defaults to TCP): | ||||||
|  |    ```bash | ||||||
|  |    export MQTT_BROKER=localhost:1883 | ||||||
|  |    ``` | ||||||
|  |  - With URI scheme: | ||||||
|  |    ```bash | ||||||
|  |    export MQTT_BROKER=tcp://broker.example.com:1883 | ||||||
|  |    ``` | ||||||
|  |  | ||||||
|  |  ## 3. Run the LED board client | ||||||
|  |  Pass the LED board IP address as the only argument: | ||||||
|  |  ```bash | ||||||
|  |  export MQTT_BROKER=localhost:1883 | ||||||
|  |  cargo run --bin ledboard_client -- 192.168.1.50 | ||||||
|  |  ``` | ||||||
|  |  | ||||||
|  |  ## Topics and Payloads | ||||||
|  |  The client publishes two topics: | ||||||
|  |  | ||||||
|  |  ### weather | ||||||
|  |  JSON payload with fields: | ||||||
|  |  - `dt`: timestamp (Unix seconds) | ||||||
|  |  - `temp`: temperature in °C | ||||||
|  |  - `weather`: object with `main`, `description`, `icon` | ||||||
|  |  - `rain`: rain volume in last 3h (optional) | ||||||
|  |  - `pop`: probability of precipitation | ||||||
|  |  - `wind`: object with `speed`, `deg`, `gust` | ||||||
|  |  | ||||||
|  |  ### straba | ||||||
|  |  JSON payload with fields: | ||||||
|  |  - `outbound_station`: name of outbound station | ||||||
|  |  - `outbound_diff`: seconds until outbound departure | ||||||
|  |  - `inbound_station`: name of inbound station | ||||||
|  |  - `inbound_diff`: seconds until inbound departure | ||||||
|  |  | ||||||
|  |  ## Customization | ||||||
|  |  You can adjust MQTT topics, QoS, and message formats in `client/bin/src/main.rs` under the `publish_to_mqtt` function. | ||||||
| @@ -22,3 +22,9 @@ serde_derive = "1.0" | |||||||
| serde_json = "1.0" | serde_json = "1.0" | ||||||
| # end of web stuff | # end of web stuff | ||||||
| ping = "0.4.1" | ping = "0.4.1" | ||||||
|  | paho-mqtt = "0.13.2" | ||||||
|  | async-trait = "0.1" | ||||||
|  | # Ini File parser | ||||||
|  | rust-ini = "0.21" | ||||||
|  | lazy_static = "1.4" | ||||||
|  | futures = "0.3" | ||||||
|   | |||||||
							
								
								
									
										78
									
								
								client/bin/src/config.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								client/bin/src/config.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | |||||||
|  | // config.rs | ||||||
|  |  | ||||||
|  | /// @file | ||||||
|  | /// @brief Configuration settings for the application. | ||||||
|  | /// | ||||||
|  | /// This file defines the configuration settings that are used throughout the application. | ||||||
|  |  | ||||||
|  | use lazy_static::lazy_static; | ||||||
|  | use std::sync::Mutex; | ||||||
|  |  | ||||||
|  | use ini::Ini; | ||||||
|  | use std::path::Path; | ||||||
|  |  | ||||||
|  | const DEFAULT_REFRESH_INTERVAL: u32 = 50; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // Define a struct to hold the INI configuration | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct Config { | ||||||
|  |     pub mqttPrefix: String, | ||||||
|  |     pub mqttIPAddress: String, | ||||||
|  |     pub panelIPAddress: String, | ||||||
|  |     pub refreshInterval: u32 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Config { | ||||||
|  |     // Constructor for Config | ||||||
|  |     pub fn new(mqtt_prefix: &str, mqtt_ip_address: &str, panel_ip_address: &str, interval: u32) -> Self { | ||||||
|  |         Config { | ||||||
|  |             mqttPrefix: mqtt_prefix.to_string(), | ||||||
|  |             mqttIPAddress: mqtt_ip_address.to_string(), | ||||||
|  |             panelIPAddress: panel_ip_address.to_string(), | ||||||
|  |             refreshInterval: interval | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     pub fn newDefault() -> Self { | ||||||
|  |         Config { | ||||||
|  |             mqttPrefix: "".to_string(), | ||||||
|  |             mqttIPAddress: "".to_string(), | ||||||
|  |             panelIPAddress: "".to_string(), | ||||||
|  |             refreshInterval: DEFAULT_REFRESH_INTERVAL | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Function to read the INI file | ||||||
|  | pub fn read_ini_file(filename: String) -> Config { | ||||||
|  |      | ||||||
|  |     let mut config = Config::newDefault(); | ||||||
|  |     let i = Ini::load_from_file(filename).unwrap(); | ||||||
|  |     for (sec, prop) in i.iter() { | ||||||
|  |          | ||||||
|  |         for (k, v) in prop.iter() | ||||||
|  |         { | ||||||
|  |             println!("{:?} {}:{}", sec, k, v); | ||||||
|  |             if (sec.is_some()) && (sec.unwrap() == "mqtt") | ||||||
|  |             { | ||||||
|  |                 if k == "path" | ||||||
|  |                 { | ||||||
|  |                     config.mqttPrefix = v.trim().to_string(); | ||||||
|  |                 } | ||||||
|  |                 else if k == "server" | ||||||
|  |                 { | ||||||
|  |                     config.mqttIPAddress = v.trim().to_string(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             else if (sec.is_some()) && (sec.unwrap() == "panel") | ||||||
|  |             { | ||||||
|  |                 if k == "ip" | ||||||
|  |                 { | ||||||
|  |                     config.panelIPAddress = v.trim().to_string(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return config; | ||||||
|  | } | ||||||
| @@ -1,10 +1,13 @@ | |||||||
| use std::{time::Duration, fmt::format}; | use std::{time::Duration, fmt::format}; | ||||||
|  | use std::sync::{RwLock, Arc}; | ||||||
|  | use async_trait::async_trait; | ||||||
|  | use paho_mqtt::{CreateOptionsBuilder, ConnectOptionsBuilder, Client ,Message as MqttMessage}; | ||||||
| use str; | use str; | ||||||
| use bit::BitIndex; | use bit::BitIndex; | ||||||
| use chrono_tz::Europe::Berlin; | use chrono_tz::Europe::Berlin; | ||||||
| use chrono::{DateTime, NaiveDateTime, Utc, Timelike}; | use chrono::{DateTime, NaiveDateTime, Utc, Timelike}; | ||||||
| use std::time::{SystemTime, UNIX_EPOCH}; | use std::time::{SystemTime, UNIX_EPOCH}; | ||||||
| use openweathermap::forecast::Weather; | use openweathermap::{forecast::Weather, Receiver}; | ||||||
| use substring::Substring; | use substring::Substring; | ||||||
| use tinybmp::Bmp; | use tinybmp::Bmp; | ||||||
| use core::time; | use core::time; | ||||||
| @@ -18,15 +21,23 @@ use embedded_graphics::{ | |||||||
|  |  | ||||||
| use std::net::UdpSocket; | use std::net::UdpSocket; | ||||||
| use std::{env, thread}; | use std::{env, thread}; | ||||||
|  | use std::path::Path; | ||||||
| use std::io; | use std::io; | ||||||
| use std::process::ExitCode; | use std::process::ExitCode; | ||||||
|  |  | ||||||
| use openweathermap::forecast::Forecast; | use openweathermap::forecast::Forecast; | ||||||
| use straba::NextDeparture; | use straba::NextDeparture; | ||||||
| // This declaration will look for a file named `straba.rs` and will | // This declaration will look for a file named `straba.rs` and will | ||||||
| // insert its contents inside a module named `straba` under this scope | // insert its contents inside a module named `straba` under this scope | ||||||
| mod straba; | mod straba; | ||||||
|  |  | ||||||
|  | use std::sync::Mutex; | ||||||
|  | use lazy_static::lazy_static; | ||||||
|  | use futures::executor::block_on; | ||||||
|  | // Load INI-File handling module | ||||||
|  | mod config; | ||||||
|  | use config::Config; | ||||||
|  | use crate::config::read_ini_file; | ||||||
|  |  | ||||||
| const IMAGE_SIZE_BYTE: usize = (IMAGE_WIDTH_BYTE * IMAGE_HEIGHT) as usize; /* one byte contains 8 LEDs, one in each bit */ | const IMAGE_SIZE_BYTE: usize = (IMAGE_WIDTH_BYTE * IMAGE_HEIGHT) as usize; /* one byte contains 8 LEDs, one in each bit */ | ||||||
| const IMAGE_WIDTH: u32 = 5 * 32; | const IMAGE_WIDTH: u32 = 5 * 32; | ||||||
| const IMAGE_WIDTH_BYTE: u32 = IMAGE_WIDTH / 8; /* one byte contains 8 LEDs, one in each bit */ | const IMAGE_WIDTH_BYTE: u32 = IMAGE_WIDTH / 8; /* one byte contains 8 LEDs, one in each bit */ | ||||||
| @@ -36,6 +47,37 @@ const IMAGE_HEIGHT_BYTE: u32 = 40; | |||||||
| const IMAGE_LENGTH: usize = (IMAGE_WIDTH_BYTE * IMAGE_HEIGHT_BYTE) as usize; | const IMAGE_LENGTH: usize = (IMAGE_WIDTH_BYTE * IMAGE_HEIGHT_BYTE) as usize; | ||||||
| const PACKAGE_LENGTH: usize = (IMAGE_LENGTH + 1) as usize; | const PACKAGE_LENGTH: usize = (IMAGE_LENGTH + 1) as usize; | ||||||
|  |  | ||||||
|  | /// @struct MqttClient | ||||||
|  | /// @brief A struct to hold application configuration settings. | ||||||
|  | /// | ||||||
|  | /// The `MqttClient` struct contains various configuration parameters that control the behavior of the application. | ||||||
|  | pub struct MqttClient { | ||||||
|  |     /// MQTT client option | ||||||
|  |     pub mqtt_client: Option<paho_mqtt::Client>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl MqttClient { | ||||||
|  |     /// Creates a new instance of MqttClient with mqtt_client initialized to None. | ||||||
|  |     pub fn new() -> Self { | ||||||
|  |         MqttClient {  | ||||||
|  |             mqtt_client: None, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Setter for mqtt_client | ||||||
|  |     pub fn set_mqtt_client(&mut self, client: Option<paho_mqtt::Client>) { | ||||||
|  |         self.mqtt_client = client; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Getter for mqtt_client | ||||||
|  |     pub fn get_mqtt_client(&self) -> &Option<paho_mqtt::Client> { | ||||||
|  |         &self.mqtt_client | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | lazy_static! { | ||||||
|  |     static ref MQTTCLIENT: Mutex<MqttClient> = Mutex::new(MqttClient::new()); | ||||||
|  | } | ||||||
|  |  | ||||||
| struct UdpDisplay { | struct UdpDisplay { | ||||||
|     image: [u8; IMAGE_SIZE_BYTE], |     image: [u8; IMAGE_SIZE_BYTE], | ||||||
| @@ -243,6 +285,13 @@ fn render_clock(display: &mut UdpDisplay){ | |||||||
|     .unwrap(); |     .unwrap(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | fn render_mqtt_message(display: &mut UdpDisplay, mqtt_message: String){ | ||||||
|  |     let text_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On); | ||||||
|  |     Text::new(&mqtt_message, Point::new((1) as i32, 37), text_style) | ||||||
|  |     .draw(display) | ||||||
|  |     .unwrap(); | ||||||
|  | } | ||||||
|  |  | ||||||
| fn render_strab_partial(display: &mut UdpDisplay, station: &String, diff: i64, height: i32) { | fn render_strab_partial(display: &mut UdpDisplay, station: &String, diff: i64, height: i32) { | ||||||
|     let text_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On); |     let text_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On); | ||||||
|     let mut diff_str = format!("{}min", (diff / 60)); |     let mut diff_str = format!("{}min", (diff / 60)); | ||||||
| @@ -276,13 +325,15 @@ fn render_strab_partial(display: &mut UdpDisplay, station: &String, diff: i64, h | |||||||
| } | } | ||||||
|  |  | ||||||
| fn render_strab(display: &mut UdpDisplay, straba_res: &NextDeparture) { | fn render_strab(display: &mut UdpDisplay, straba_res: &NextDeparture) { | ||||||
|     render_strab_partial(display, &straba_res.outbound_station, straba_res.outbound_diff, 15); |     render_strab_partial(display, &straba_res.outbound_station, straba_res.outbound_diff, 17); | ||||||
|     render_strab_partial(display, &straba_res.inbound_station, straba_res.inbound_diff, 25); |     render_strab_partial(display, &straba_res.inbound_station, straba_res.inbound_diff, 27); | ||||||
| } | } | ||||||
|  | ///////////////////////////////////////////////////////////////////////////// | ||||||
|  |  | ||||||
| fn send_package(ipaddress: String,  | fn send_package(ipaddress: String,  | ||||||
|                 data: &Option<Result<Forecast, String>>, |                 data: &Option<Result<Forecast, String>>, | ||||||
|                 straba_res: &NextDeparture) { |                 straba_res: &NextDeparture, | ||||||
|  |                 mqtt_message: Option<String>) { | ||||||
|     let mut package: [u8; PACKAGE_LENGTH] = [0; PACKAGE_LENGTH]; |     let mut package: [u8; PACKAGE_LENGTH] = [0; PACKAGE_LENGTH]; | ||||||
|  |  | ||||||
|     // Brightness |     // Brightness | ||||||
| @@ -300,14 +351,17 @@ fn send_package(ipaddress: String, | |||||||
|         render_strab(&mut display, straba_res); |         render_strab(&mut display, straba_res); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if mqtt_message.is_some() { | ||||||
|  |         render_mqtt_message(&mut display, mqtt_message.unwrap());        | ||||||
|  |     } | ||||||
|  |  | ||||||
|     render_clock(&mut display); |     render_clock(&mut display); | ||||||
|  |  | ||||||
|  |  | ||||||
|     package[1..PACKAGE_LENGTH].copy_from_slice(&display.image); |     package[1..PACKAGE_LENGTH].copy_from_slice(&display.image); | ||||||
|     // client need to bind to client port (1 before 4242) |     let target = format!("{}:4242", ipaddress); | ||||||
|     let socket = UdpSocket::bind("0.0.0.0:14242").expect("couldn't bind to address"); |     let socket = UdpSocket::bind("0.0.0.0:0").expect("couldn't bind to address"); | ||||||
|     socket |     socket | ||||||
|         .send_to(&package, ipaddress + ":4242") |         .send_to(&package, &target) | ||||||
|         .expect("couldn't send data"); |         .expect("couldn't send data"); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -318,34 +372,399 @@ LEDboardClient <ip address>" | |||||||
|     ); |     ); | ||||||
|     println!("one argument necessary!"); |     println!("one argument necessary!"); | ||||||
|     println!("<ip address>"); |     println!("<ip address>"); | ||||||
|  |     println!("second argument is optional:"); | ||||||
|  |     println!("<ip of mqtt server>"); | ||||||
|  |     println!(""); | ||||||
|  |     println!("Config mode"); | ||||||
|  |     println!("--config <file.ini>"); | ||||||
| } | } | ||||||
|  |  | ||||||
| fn check_connection(ipaddress: String) -> bool { | fn check_connection(ipaddress: String) -> bool { | ||||||
|     let device_online; |     let device_online; | ||||||
|     // generate a faulty package length |  | ||||||
|     let mut package: [u8; PACKAGE_LENGTH/2] = [0; PACKAGE_LENGTH/2]; |     let mut package: [u8; PACKAGE_LENGTH/2] = [0; PACKAGE_LENGTH/2]; | ||||||
|     // client need to bind to client port (1 before 4242) |      | ||||||
|     let socket = UdpSocket::bind("0.0.0.0:14242").expect("couldn't bind to address"); |     // Use a random local port instead of hardcoding | ||||||
|     socket.set_read_timeout(Some(Duration::from_secs(10))).unwrap(); /* 10 seconds timeout */ |     let socket = UdpSocket::bind("0.0.0.0:0").expect("couldn't bind to address"); | ||||||
|     socket |     socket.set_read_timeout(Some(Duration::from_secs(10))).unwrap(); | ||||||
|         .send_to(&package, ipaddress + ":4242") |      | ||||||
|         .expect("couldn't send data"); |     let target = format!("{}:4242", ipaddress); | ||||||
|  |     match socket.send_to(&package, &target) { | ||||||
|  |         Ok(_) => { | ||||||
|  |             // Continue with receive | ||||||
|  |             match socket.recv_from(&mut package) { | ||||||
|  |                 Ok((_n, _addr)) => device_online = true, | ||||||
|  |                 Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => device_online = false, | ||||||
|  |                 Err(_) => device_online = false, | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         Err(_) => device_online = false, | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     device_online | ||||||
|  | } | ||||||
|  | /// Publishes weather and transit data to MQTT broker | ||||||
|  | fn publish_to_mqtt(client: &Client, data: &Option<Result<Forecast, String>>, straba_res: &NextDeparture) { | ||||||
|  |     let payload = if let Some(Ok(forecast)) = data { | ||||||
|  |         if let Some(f) = forecast.list.first() { | ||||||
|  |             let temp = f.main.temp; | ||||||
|  |             let weather = f.weather.get(0).map(|w| w.main.clone()).unwrap_or_default(); | ||||||
|  |             format!("temp:{:.1}C,weather:{}", | ||||||
|  |                 temp, | ||||||
|  |                 weather) | ||||||
|  |        } else { | ||||||
|  |             "no_forecast".to_string() | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         "no_data".to_string() | ||||||
|  |     }; | ||||||
|  |     let msg = MqttMessage::new("ledboard/forecast", payload, 1); | ||||||
|  |     if let Err(e) = client.publish(msg) { | ||||||
|  |         eprintln!("Error publishing MQTT message: {}", e); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // self.recv_buff is a [u8; 8092] |     let payloadPT = { | ||||||
|     let answer = socket.recv_from(&mut package); |             format!("out:{}min,in:{}min", | ||||||
|     match answer { |                 straba_res.outbound_diff / 60, | ||||||
|         Ok((_n, _addr)) => { |                 straba_res.inbound_diff / 60) | ||||||
|             //println!("{} bytes response from {:?} {:?}", n, addr, &package[..n]); |     }; | ||||||
|             device_online = true; |     let ptmsg = MqttMessage::new("ledboard/public_transportation", payloadPT, 1); | ||||||
|         } |     if let Err(e) = client.publish(ptmsg) { | ||||||
|         Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { |         eprintln!("Error publishing MQTT message: {}", e); | ||||||
|             device_online = false; |     } | ||||||
|         } | } | ||||||
|         Err(_e) => { |  | ||||||
|             device_online = false; | struct Message { | ||||||
|  |     string: Option<String> | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Lazy static to load the config file content | ||||||
|  | lazy_static! { | ||||||
|  |     pub static ref GlobalConfiguration: Mutex<Config> = Mutex::new({ | ||||||
|  |         Config::newDefault() | ||||||
|  |      }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /// Asynchronously publishes a message to the specified topic. | ||||||
|  | ///  | ||||||
|  | /// @author Gwen2.5 | ||||||
|  | ///  | ||||||
|  | /// # Arguments | ||||||
|  | /// * `topic` - The MQTT topic to which the message will be published. | ||||||
|  | /// * `message` - The message to be published. | ||||||
|  | ///  | ||||||
|  | /// # Returns | ||||||
|  | /// True if the message was successfully published, false otherwise. | ||||||
|  | fn publish_message(topic: &str, message: &str) { | ||||||
|  |     let msg = MqttMessage::new(topic, message, 0); | ||||||
|  |  | ||||||
|  |     let mqtt_client =  <std::option::Option<Client> as Clone>::clone(&(MQTTCLIENT.lock().unwrap().get_mqtt_client())).unwrap(); | ||||||
|  |     // Publish the message and ensure it completes without error | ||||||
|  |    let result =  mqtt_client.publish(msg); | ||||||
|  |     match result { | ||||||
|  |         Ok(_) => (), | ||||||
|  |         Err(error) => { | ||||||
|  |             println!("Error publishing {error:?}"); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     return device_online; | } | ||||||
|  |  | ||||||
|  | fn main_function(parameter1: String, parameter2: Option<String>) -> ExitCode { | ||||||
|  |  | ||||||
|  |     // Read configuration file | ||||||
|  |     if  (parameter1 == "--config") && (parameter2.is_some()) | ||||||
|  |     { | ||||||
|  |         let configOrMqttAddress: String = parameter2.unwrap(); | ||||||
|  |         if Path::new(&configOrMqttAddress).exists() | ||||||
|  |         { | ||||||
|  |             let mut gc = GlobalConfiguration.lock().unwrap(); | ||||||
|  |             let c = read_ini_file(configOrMqttAddress); | ||||||
|  |              | ||||||
|  |             //update configuration | ||||||
|  |             gc.mqttIPAddress = c.mqttIPAddress; | ||||||
|  |             gc.panelIPAddress = c.panelIPAddress; | ||||||
|  |             gc.mqttPrefix = c.mqttPrefix; | ||||||
|  |             println!("Read INI {:} @ {:}", gc.mqttPrefix, gc.mqttIPAddress); | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             /* Panel and MQTT Configured*/ | ||||||
|  |             println!("INI file not found"); | ||||||
|  |             return ExitCode::FAILURE; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |         let mut gc = GlobalConfiguration.lock().unwrap();    | ||||||
|  |         gc.panelIPAddress = parameter1; | ||||||
|  |         gc.mqttIPAddress = parameter2.unwrap(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     let mut device_online = false; | ||||||
|  |     if GlobalConfiguration.lock().unwrap().panelIPAddress.len() > 0 | ||||||
|  |     { | ||||||
|  |         device_online = check_connection(GlobalConfiguration.lock().unwrap().panelIPAddress.clone()); | ||||||
|  |         if !device_online { | ||||||
|  |             println!("{:} not online", &GlobalConfiguration.lock().unwrap().panelIPAddress); | ||||||
|  |             return ExitCode::FAILURE; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     let mut gmc = MQTTCLIENT.lock().unwrap(); | ||||||
|  |     let mqtt_message: Arc<Mutex<Message>> = Arc::new(Mutex::new(Message{ string: None })); | ||||||
|  |  | ||||||
|  |     if GlobalConfiguration.lock().is_ok() && (GlobalConfiguration.lock().unwrap().mqttIPAddress.len() > 0) | ||||||
|  |     { | ||||||
|  |         // Define the set of options for the create. | ||||||
|  |         // Use an ID for a persistent session. | ||||||
|  |         let create_opts = paho_mqtt::CreateOptionsBuilder::new() | ||||||
|  |         .server_uri(GlobalConfiguration.lock().unwrap().mqttIPAddress.clone()) | ||||||
|  |         .client_id("ledboard") | ||||||
|  |         .finalize(); | ||||||
|  |         // Create a client. | ||||||
|  |         let local_mqtt = paho_mqtt::Client::new(create_opts).unwrap(); | ||||||
|  |          | ||||||
|  |         println!("MQTT | Connecting to {:} MQTT server...", GlobalConfiguration.lock().unwrap().mqttIPAddress); | ||||||
|  |          | ||||||
|  |         let lwt = paho_mqtt::Message::new(&format!("{}/lwt", GlobalConfiguration.lock().unwrap().mqttPrefix), "lost connection", 1); | ||||||
|  |  | ||||||
|  |         // The connect options. Defaults to an MQTT v3.x connection. | ||||||
|  |         let conn_opts = paho_mqtt::ConnectOptionsBuilder::new() | ||||||
|  |             .keep_alive_interval(Duration::from_secs(20)) | ||||||
|  |             .will_message(lwt) | ||||||
|  |             .finalize(); | ||||||
|  |  | ||||||
|  |         // Make the connection to the broker | ||||||
|  |         println!("MQTT | Connecting to the MQTT server..."); | ||||||
|  |         let result = local_mqtt.connect(conn_opts); | ||||||
|  |  | ||||||
|  |         match result { | ||||||
|  |             Ok(_) => { | ||||||
|  |                 println!("MQTT | Server connected"); | ||||||
|  |             }, | ||||||
|  |             Err(error) => { | ||||||
|  |                 println!("MQTT | Server connecting {error:?}"); | ||||||
|  |             }, | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Subscribe to the desired topic(s). | ||||||
|  |         local_mqtt.subscribe("room/ledboard", paho_mqtt::QOS_0); | ||||||
|  |  | ||||||
|  |         // Starts the client receiving messages | ||||||
|  |         let rx_queue = local_mqtt.start_consuming(); | ||||||
|  |  | ||||||
|  |         // Attach a closure to the client to receive callback | ||||||
|  |         // on incoming messages. | ||||||
|  |         let mqtt_message_for_callback = mqtt_message.clone(); | ||||||
|  |          | ||||||
|  |         // Create a thread that stays pending over incoming messages. | ||||||
|  |         let handle = thread::spawn(move || { | ||||||
|  |             for mqttmsg in rx_queue.iter() { | ||||||
|  |                 if let Some(mqttmsg) = mqttmsg { | ||||||
|  |                     let topic = mqttmsg.topic(); | ||||||
|  |                     let payload_str = mqttmsg.payload_str(); | ||||||
|  |  | ||||||
|  |                     //println!("MQTT | {} - {}", topic, payload_str); | ||||||
|  |                     let mut lock = mqtt_message_for_callback.lock().unwrap(); | ||||||
|  |                     lock.string = Some(payload_str.to_string()); | ||||||
|  |                     println!("Received: -> {}", mqttmsg.payload_str()); | ||||||
|  |                 } else { | ||||||
|  |                     println!("Unsubscribe: connection closed"); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |         // Define the set of options for the connection | ||||||
|  |  | ||||||
|  |         // move local instance to global scope | ||||||
|  |         gmc.set_mqtt_client(Some(local_mqtt)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     let receiver = openweathermap::init_forecast("Mannheim", | ||||||
|  |     "metric", | ||||||
|  |     "de", | ||||||
|  |     "978882ab9dd05e7122ff2b0aef2d3e55", | ||||||
|  |     60,1); | ||||||
|  |  | ||||||
|  |     let mut last_data = Option::None; | ||||||
|  |      | ||||||
|  |     // Test Webcrawler for public transportataion | ||||||
|  |     let mut straba_res = straba::fetch_data(Some(true)); | ||||||
|  |     println!("{:?} {:?}s", straba_res.outbound_station, straba_res.outbound_diff); | ||||||
|  |     println!("{:?} {:?}s", straba_res.inbound_station , straba_res.inbound_diff); | ||||||
|  |  | ||||||
|  |     if GlobalConfiguration.lock().is_ok() && (GlobalConfiguration.lock().unwrap().panelIPAddress.len() > 0) | ||||||
|  |     { | ||||||
|  |         // Render start | ||||||
|  |         send_package(GlobalConfiguration.lock().unwrap().panelIPAddress.clone(), &last_data, &straba_res, Some("MQTT: room/ledboard".to_string())); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     loop { | ||||||
|  |         let st_now = SystemTime::now(); | ||||||
|  |         let seconds = st_now.duration_since(UNIX_EPOCH).unwrap().as_secs(); | ||||||
|  |         let delay = time::Duration::from_millis(500); | ||||||
|  |         thread::sleep(delay); | ||||||
|  |         // Only request, if the device is present | ||||||
|  |         if device_online == true { | ||||||
|  |             let answer = openweathermap::update_forecast(&receiver); | ||||||
|  |             match answer { | ||||||
|  |                 Some(_) => { | ||||||
|  |                     last_data = answer; | ||||||
|  |                 } | ||||||
|  |                 None => { | ||||||
|  |      | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (straba_res.request_time + (GlobalConfiguration.lock().unwrap().refreshInterval as i64)) < seconds as i64 { | ||||||
|  |             if GlobalConfiguration.lock().is_ok() && GlobalConfiguration.lock().unwrap().mqttIPAddress.len() > 0 | ||||||
|  |             { | ||||||
|  |                 device_online = check_connection(GlobalConfiguration.lock().unwrap().mqttIPAddress.clone()); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if GlobalConfiguration.lock().is_ok() && GlobalConfiguration.lock().unwrap().mqttPrefix.len() > 0 | ||||||
|  |             { | ||||||
|  |                 //FIXME if mqtt_client.is_some() | ||||||
|  |                 fun_publishinfoviamqtt(&straba_res); | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             // request once a minute new data | ||||||
|  |             straba_res = straba::fetch_data(None); | ||||||
|  |             println!("Update {:?} {:?}s", straba_res.outbound_station, straba_res.outbound_diff); | ||||||
|  |             println!("Update {:?} {:?}s", straba_res.inbound_station , straba_res.inbound_diff); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         let lock = mqtt_message.lock().unwrap(); | ||||||
|  |         let mqtt_message: Option<String>; | ||||||
|  |         if lock.string.is_some() { | ||||||
|  |             mqtt_message = lock.string.clone(); | ||||||
|  |         } else { | ||||||
|  |             mqtt_message = None; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (GlobalConfiguration.lock().is_ok()) && (GlobalConfiguration.lock().unwrap().mqttIPAddress.len() > 0) && (device_online == true) { | ||||||
|  |             // Render new image | ||||||
|  |             send_package(GlobalConfiguration.lock().unwrap().mqttIPAddress.clone(), &last_data, &straba_res, mqtt_message); | ||||||
|  |         } | ||||||
|  |         // Handle MQTT messages | ||||||
|  |          | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn fun_publishinfoviamqtt(straba_res: &NextDeparture) { | ||||||
|  |     let topic_in_station: String = format!("{}{}", GlobalConfiguration.lock().unwrap().mqttPrefix, "/inbound/station"); | ||||||
|  |     let station_name: String = format!("{}", straba_res.inbound_station); | ||||||
|  |     // Execute async publish synchronously | ||||||
|  |     let _ = publish_message(topic_in_station.as_str(), station_name.as_str()); | ||||||
|  |     println!("MQTT published {:?} = {:?}s", topic_in_station, straba_res.outbound_station); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn main_function2(ip: String) -> ExitCode | ||||||
|  | { | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     let mut device_online = check_connection(ip.to_string()); | ||||||
|  |     if !device_online { | ||||||
|  |         println!("{} not online", ip); | ||||||
|  |        // return ExitCode::FAILURE; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |      | ||||||
|  |  | ||||||
|  |     let receiver = openweathermap::init_forecast("Mannheim", | ||||||
|  |     "metric", | ||||||
|  |     "de", | ||||||
|  |     "978882ab9dd05e7122ff2b0aef2d3e55", | ||||||
|  |     60,1); | ||||||
|  |  | ||||||
|  |     let mut last_data = Option::None; | ||||||
|  |      | ||||||
|  |     // Test Webcrawler for public transportataion | ||||||
|  |     let mut straba_res = straba::fetch_data(Some(true)); | ||||||
|  |     println!("{:?} {:?}s", straba_res.outbound_station, straba_res.outbound_diff); | ||||||
|  |     println!("{:?} {:?}s", straba_res.inbound_station , straba_res.inbound_diff); | ||||||
|  |  | ||||||
|  |     // Initialize MQTT client from MQTT_BROKER env var (else disabled) | ||||||
|  |     let mqtt_client: Option<Client> = { | ||||||
|  |         // Read broker URL from environment | ||||||
|  |         let broker = match std::env::var("MQTT_BROKER") { | ||||||
|  |             Ok(val) if !val.is_empty() => val, | ||||||
|  |             _ => { | ||||||
|  |                 eprintln!("Environment variable MQTT_BROKER not set or empty, MQTT disabled"); | ||||||
|  |                 String::new() | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |         if broker.is_empty() { | ||||||
|  |             None | ||||||
|  |         } else { | ||||||
|  |             let create_opts = CreateOptionsBuilder::new() | ||||||
|  |                 .server_uri(&broker) | ||||||
|  |                 .client_id("ledboard_client") | ||||||
|  |                 .finalize(); | ||||||
|  |             match Client::new(create_opts) { | ||||||
|  |                 Ok(cli) => { | ||||||
|  |                     let conn_opts = ConnectOptionsBuilder::new() | ||||||
|  |                         .keep_alive_interval(Duration::from_secs(20)) | ||||||
|  |                         .clean_session(true) | ||||||
|  |                         .finalize(); | ||||||
|  |                     match cli.connect(conn_opts) { | ||||||
|  |                         Ok(_) => Some(cli), | ||||||
|  |                         Err(e) => { | ||||||
|  |                             eprintln!("Failed to connect to MQTT broker '{}': {}", broker, e); | ||||||
|  |                             None | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 Err(e) => { | ||||||
|  |                     eprintln!("Failed to create MQTT client for '{}': {}", broker, e); | ||||||
|  |                     None | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     // Render start | ||||||
|  |     send_package(ip.to_string(), &last_data, &straba_res, None); | ||||||
|  |     loop { | ||||||
|  |         let st_now = SystemTime::now(); | ||||||
|  |         let seconds = st_now.duration_since(UNIX_EPOCH).unwrap().as_secs(); | ||||||
|  |         let delay = time::Duration::from_millis(500); | ||||||
|  |         thread::sleep(delay); | ||||||
|  |         // Only request, if the device is present | ||||||
|  |         if device_online == true { | ||||||
|  |             let answer = openweathermap::update_forecast(&receiver); | ||||||
|  |             match answer { | ||||||
|  |                 Some(_) => { | ||||||
|  |                     last_data = answer; | ||||||
|  |                 } | ||||||
|  |                 None => { | ||||||
|  |      | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (straba_res.request_time + 50) < seconds as i64 { | ||||||
|  |             device_online = check_connection(ip.to_string()); | ||||||
|  |             // request once a minute new data | ||||||
|  |             if device_online == true { | ||||||
|  |                 straba_res = straba::fetch_data(None); | ||||||
|  |                 println!("Update {:?} {:?}s", straba_res.outbound_station, straba_res.outbound_diff); | ||||||
|  |                 println!("Update {:?} {:?}s", straba_res.inbound_station , straba_res.inbound_diff); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if device_online == true { | ||||||
|  |             // Render new image | ||||||
|  |             send_package(ip.to_string(), &last_data, &straba_res, None); | ||||||
|  |             // Publish data to MQTT | ||||||
|  |        } | ||||||
|  |          if let Some(ref client) = mqtt_client { | ||||||
|  |                 publish_to_mqtt(client, &last_data, &straba_res); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| fn main() -> ExitCode { | fn main() -> ExitCode { | ||||||
| @@ -360,62 +779,16 @@ fn main() -> ExitCode { | |||||||
|         // one argument passed |         // one argument passed | ||||||
|         2 => { |         2 => { | ||||||
|             let ip = &args[1]; |             let ip = &args[1]; | ||||||
|  |             // Only one parameter uses the logic, generated in o4mini-llm | ||||||
|  |             return main_function2(ip.to_string()); | ||||||
|             let mut device_online = check_connection(ip.to_string()); |         } | ||||||
|             if !device_online { |         // two argument passed | ||||||
|                 println!("{} not online", ip); |         3 => { | ||||||
|                 return ExitCode::FAILURE; |             let ip = &args[1]; | ||||||
|             } |             let mqtt = &args[2]; | ||||||
|  |             return main_function(   ip.to_string(), | ||||||
|             let receiver = openweathermap::init_forecast("Mannheim", |                                     Some(mqtt.to_string()) | ||||||
|             "metric", |                                 ); | ||||||
|             "de", |  | ||||||
|             "978882ab9dd05e7122ff2b0aef2d3e55", |  | ||||||
|             60,1); |  | ||||||
|  |  | ||||||
|             let mut last_data = Option::None; |  | ||||||
|              |  | ||||||
|             // Test Webcrawler for public transportataion |  | ||||||
|             let mut straba_res = straba::fetch_data(Some(true)); |  | ||||||
|             println!("{:?} {:?}s", straba_res.outbound_station, straba_res.outbound_diff); |  | ||||||
|             println!("{:?} {:?}s", straba_res.inbound_station , straba_res.inbound_diff); |  | ||||||
|  |  | ||||||
|             // Render start |  | ||||||
|             send_package(ip.to_string(), &last_data, &straba_res); |  | ||||||
|             loop { |  | ||||||
|                 let st_now = SystemTime::now(); |  | ||||||
|                 let seconds = st_now.duration_since(UNIX_EPOCH).unwrap().as_secs(); |  | ||||||
|                 let delay = time::Duration::from_millis(500); |  | ||||||
|                 thread::sleep(delay); |  | ||||||
|                 // Only request, if the device is present |  | ||||||
|                 if device_online == true { |  | ||||||
|                     let answer = openweathermap::update_forecast(&receiver); |  | ||||||
|                     match answer { |  | ||||||
|                         Some(_) => { |  | ||||||
|                             last_data = answer; |  | ||||||
|                         } |  | ||||||
|                         None => { |  | ||||||
|              |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 if (straba_res.request_time + 50) < seconds as i64 { |  | ||||||
|                     device_online = check_connection(ip.to_string()); |  | ||||||
|                     // request once a minute new data |  | ||||||
|                     if device_online == true { |  | ||||||
|                         straba_res = straba::fetch_data(None); |  | ||||||
|                         println!("Update {:?} {:?}s", straba_res.outbound_station, straba_res.outbound_diff); |  | ||||||
|                         println!("Update {:?} {:?}s", straba_res.inbound_station , straba_res.inbound_diff); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 if device_online == true { |  | ||||||
|                     // Render new image |  | ||||||
|                     send_package(ip.to_string(), &last_data, &straba_res); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|         // all the other cases |         // all the other cases | ||||||
|         _ => { |         _ => { | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| use chrono::DateTime; | use chrono::DateTime; | ||||||
| use std::time::{SystemTime, UNIX_EPOCH}; | use std::time::{SystemTime, UNIX_EPOCH}; | ||||||
|  | use chrono::Local; | ||||||
| use serde::Deserialize; | use serde::Deserialize; | ||||||
|  |  | ||||||
| const STATION_URL:&str = "https://www.rnv-online.de/rest/departure/2494"; | const STATION_URL:&str = "https://www.rnv-online.de/rest/departure/2494"; | ||||||
| @@ -79,10 +79,11 @@ pub struct NextDeparture { | |||||||
| } | } | ||||||
|  |  | ||||||
| pub fn fetch_data(debug_print : Option<bool>) -> NextDeparture { | pub fn fetch_data(debug_print : Option<bool>) -> NextDeparture { | ||||||
|  |     let date = Local::now(); | ||||||
|     let st_now = SystemTime::now(); |     let st_now = SystemTime::now(); | ||||||
|     let seconds = st_now.duration_since(UNIX_EPOCH).unwrap().as_secs(); |     let seconds = st_now.duration_since(UNIX_EPOCH).unwrap().as_secs(); | ||||||
|     let url = &format!("{}?datetime={}", STATION_URL, seconds); |     let timeString = date.format("%Y-%m-%d %H:%M:%S"); | ||||||
|  |     let url = &format!("{}?datetime={}", STATION_URL, timeString); | ||||||
|     let result = reqwest::blocking::get(url); |     let result = reqwest::blocking::get(url); | ||||||
|      |      | ||||||
|     let mut return_value = NextDeparture { |     let mut return_value = NextDeparture { | ||||||
| @@ -118,11 +119,25 @@ pub fn fetch_data(debug_print : Option<bool>) -> NextDeparture { | |||||||
|         return return_value; |         return return_value; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if debug_print.is_some() && debug_print.unwrap() == true { | ||||||
|  |         println!("----------- Seconds {:} {:} requesting ... -----------", seconds, timeString); | ||||||
|  |     } | ||||||
|     // parse JSON result.. search of both directions |     // parse JSON result.. search of both directions | ||||||
|     let json = body.unwrap(); |     let json = body.unwrap(); | ||||||
|  |      | ||||||
|  |     if debug_print.is_some() && debug_print.unwrap() == true { | ||||||
|  |         println!("Requesting {:}", json.graph_ql.response.name); | ||||||
|  |         println!("Elements {:}", json.graph_ql.response.journeys.elements.len() ); | ||||||
|  |         //println!("------------------------- %< ----------------------------"); | ||||||
|  |         //println!("{}", &raw_text); | ||||||
|  |         //println!("------------------------- %< ----------------------------"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     for el in json.graph_ql.response.journeys.elements { |     for el in json.graph_ql.response.journeys.elements { | ||||||
|  |  | ||||||
|         if debug_print.is_some() && debug_print.unwrap() == true { |         if debug_print.is_some() && debug_print.unwrap() == true { | ||||||
|             println!("Line {:}", el.line.line_group.label);   |             println!("Requesting {:}", json.graph_ql.response.name); | ||||||
|  |             println!("Line {:}", el.line.line_group.label); | ||||||
|         } |         } | ||||||
|         for stop in el.stops { |         for stop in el.stops { | ||||||
|             // use only valid data |             // use only valid data | ||||||
| @@ -140,6 +155,8 @@ pub fn fetch_data(debug_print : Option<bool>) -> NextDeparture { | |||||||
|                     if diff <  return_value.outbound_diff { |                     if diff <  return_value.outbound_diff { | ||||||
|                         return_value.outbound_station = stop.destination_label; |                         return_value.outbound_station = stop.destination_label; | ||||||
|                         return_value.outbound_diff = diff; |                         return_value.outbound_diff = diff; | ||||||
|  |                     } else if debug_print.is_some() && debug_print.unwrap() == true { | ||||||
|  |                         println!("Unkown diff Stop   {:} {:} (in {:} seconds)", stop.destination_label, txt_departure, diff ); | ||||||
|                     } |                     } | ||||||
|                 } else if stop.destination_label.contains("Hochschule") || |                 } else if stop.destination_label.contains("Hochschule") || | ||||||
|                             stop.destination_label.contains("Hauptbahnhof") || |                             stop.destination_label.contains("Hauptbahnhof") || | ||||||
| @@ -147,13 +164,19 @@ pub fn fetch_data(debug_print : Option<bool>) -> NextDeparture { | |||||||
|                     if diff <  return_value.inbound_diff { |                     if diff <  return_value.inbound_diff { | ||||||
|                         return_value.inbound_station = stop.destination_label; |                         return_value.inbound_station = stop.destination_label; | ||||||
|                         return_value.inbound_diff = diff; |                         return_value.inbound_diff = diff; | ||||||
|  |                     } else if debug_print.is_some() && debug_print.unwrap() == true { | ||||||
|  |                         println!("Unkown diff Stop   {:} {:} (in {:} seconds)", stop.destination_label, txt_departure, diff ); | ||||||
|                     } |                     } | ||||||
|  |                 } else if debug_print.is_some() && debug_print.unwrap() == true { | ||||||
|  |                     println!("Unkown Stop   {:} {:} (in {:} seconds)", stop.destination_label, txt_departure, diff ); | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 println!("Planned {:} {:?}", stop.destination_label, stop.planned_departure.iso_string) |                 println!("Planned {:} {:?}", stop.destination_label, stop.planned_departure.iso_string) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |     if debug_print.is_some() && debug_print.unwrap() == true { | ||||||
|  |         println!("----------- end of straba.rs -----------"); | ||||||
|  |     } | ||||||
|     return_value |     return_value | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								client/ledBoard.service
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								client/ledBoard.service
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | [Unit] | ||||||
|  | Description=Log uptime in scoreboard | ||||||
|  | DefaultDependencies=no | ||||||
|  |  | ||||||
|  | [Service] | ||||||
|  | Type=simple | ||||||
|  | Restart=on-failure | ||||||
|  | User=c3ma | ||||||
|  |  | ||||||
|  | # Specify users home as working directory | ||||||
|  | WorkingDirectory=/home/c3ma/ | ||||||
|  | # Define wrapper to update and start project | ||||||
|  | ExecStart=/usr/bin/bash <project home>/client/ledboard.sh | ||||||
|  | TimeoutStartSec=0 | ||||||
|  |  | ||||||
|  | [Install] | ||||||
|  | WantedBy=network.target | ||||||
							
								
								
									
										12
									
								
								client/ledboard.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										12
									
								
								client/ledboard.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | #!/bin/bash | ||||||
|  | # Wrapper script to update project and build project | ||||||
|  | # | ||||||
|  | #Set target IP address | ||||||
|  | IP= | ||||||
|  | # Path to this project | ||||||
|  | HOSTCLIENT= | ||||||
|  | cd $HOSTCLIENT | ||||||
|  | /usr/bin/pkill LEDboardClient | ||||||
|  | git pull | ||||||
|  | cargo build | ||||||
|  | $HOSTCLIENT/target/debug/LEDboardClient $IP | ||||||
		Reference in New Issue
	
	Block a user