Skip to content

Commit

Permalink
MQTT config update / some fixes and refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
askrejans committed Jan 28, 2024
1 parent 43f7a89 commit 4c20a04
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 31 deletions.
46 changes: 39 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "speeduino-to-mqtt"
version = "0.1.1"
version = "0.1.4"
edition = "2021"

[dependencies]
Expand All @@ -11,6 +11,7 @@ futures = "0.3.30"
lazy_static = "1.4.0"
tokio = { version = "1", features = ["full"] }
openssl = { version = "0.10.63", features = ["vendored"] }
gumdrop = "0.8.1"

[dev-dependencies]
tempdir = "0.3.7"
tempdir = "0.3.7"
58 changes: 43 additions & 15 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use config::Config;
use config::{Config, File};
use std::path::Path;

/// Struct to hold the application configuration.
#[derive(Clone)]
pub struct AppConfig {
/// The name of the serial port.
pub port_name: String,
Expand All @@ -19,6 +21,9 @@ pub struct AppConfig {

/// Refresh rate in milliseconds.
pub refresh_rate_ms: Option<u64>,

// Optional: Path to the configuration file
pub config_path: Option<String>,
}

impl Default for AppConfig {
Expand All @@ -30,29 +35,51 @@ impl Default for AppConfig {
mqtt_port: 1883, // Provide a default MQTT port value
mqtt_base_topic: String::new(),
refresh_rate_ms: Some(1000), // Set the default refresh rate to 1000ms
config_path: None
}
}
}

/// Load application configuration from a TOML file.
///
/// This function reads the configuration settings from a TOML file named "settings.toml".
/// It expects the following keys in the TOML file: "port_name", "baud_rate", "mqtt_host", "mqtt_port", and "mqtt_base_topic".
/// This function reads the configuration settings from a TOML file.
///
/// # Panics
/// Panics if any of the required configuration keys are missing or if there is an error reading the configuration file.
/// # Arguments
/// - `config_path`: An optional path to the configuration file.
///
/// # Returns
/// Returns an `AppConfig` struct containing the loaded configuration.
pub fn load_configuration() -> AppConfig {
// Build a new Config object with a file source.
let config_file_path =
std::env::var("CONFIG_FILE_PATH").unwrap_or_else(|_| String::from("settings.toml"));
/// Returns a `Result` containing either the `AppConfig` struct with the loaded configuration or an error message.
pub fn load_configuration(config_path: Option<&str>) -> Result<AppConfig, String> {
// Create a default configuration
let mut settings = Config::default();

// Try to load from the passed config_path
if let Some(path) = config_path {
match Config::builder().add_source(File::with_name(path)).build() {
Ok(config) => settings = config,
Err(err) => return Err(format!("{}", err)),
}
} else {
// Try to load from the executable's directory
if let Ok(exe_dir) = std::env::current_exe() {
let exe_dir = exe_dir.parent().unwrap_or_else(|| Path::new("."));
let default_path = exe_dir.join("settings.toml");

if let Ok(config) =
Config::builder().add_source(File::with_name(default_path.to_str().unwrap())).build()
{
settings = config;
}
}

let settings = Config::builder()
.add_source(config::File::with_name(&config_file_path))
.build()
.expect("Failed to build configuration");
// Try to load from /etc/g86-car-telemetry/speeduino-to-mqtt.toml
if let Ok(config) = Config::builder()
.add_source(File::with_name("/usr/etc/g86-car-telemetry/speeduino-to-mqtt.toml"))
.build()
{
settings = config;
}
}

// Create an AppConfig struct by extracting values from the configuration.
let mut app_config = AppConfig {
Expand All @@ -75,13 +102,14 @@ pub fn load_configuration() -> AppConfig {
.get_int("refresh_rate_ms")
.map(|value| value as u64)
.ok(),
config_path: config_path.map(|p| p.to_string()), // Convert &str to String
};
// If refresh_rate_ms is not specified in the config, use the default value (1000ms)
if app_config.refresh_rate_ms.is_none() {
app_config.refresh_rate_ms = Some(1000);
}

app_config
Ok(app_config)
}

#[cfg(test)]
Expand Down
11 changes: 6 additions & 5 deletions src/ecu_serial_comms_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use std::time::{Duration, Instant};
lazy_static! {
/// Interval between commands sent to the ECU.
static ref COMMAND_INTERVAL: Duration = Duration::from_millis(
load_configuration().refresh_rate_ms.unwrap_or(1000)
load_configuration(None).unwrap().refresh_rate_ms.unwrap_or(1000)
);

/// Length of the engine data message.
Expand Down Expand Up @@ -41,8 +41,8 @@ pub fn setup_serial_port(config: &AppConfig) -> Result<Box<dyn SerialPort>, seri
///
/// This function continuously reads data from the serial port, processes the engine data,
/// and communicates with the MQTT broker based on the provided configuration.
pub fn start_ecu_communication() {
let config = load_configuration();
pub fn start_ecu_communication(config: AppConfig) {

let arc_config = Arc::new(config);

// Setup MQTT outside the loop
Expand All @@ -68,8 +68,9 @@ pub fn start_ecu_communication() {
// Flag to indicate whether the program should exit
let should_exit = Arc::new(Mutex::new(false));

let arc_config_thread = arc_config.clone();

thread::spawn({
let arc_config = arc_config.clone();
let mqtt_client = mqtt_client.clone();
let port = port.clone();
let should_exit = should_exit.clone();
Expand All @@ -87,7 +88,7 @@ pub fn start_ecu_communication() {

// Process the engine data only if it's not empty
if !engine_data.is_empty() {
process_speeduino_realtime_data(&engine_data, &arc_config, &mqtt_client);
process_speeduino_realtime_data(&engine_data, &arc_config_thread, &mqtt_client);

// Print the connection message only if not connected
if !connected {
Expand Down
44 changes: 42 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,31 @@
///
/// - `main()`: The main function that starts the ECU communication and displays the welcome message.
/// - `displayWelcome()`: Function to display a graphical welcome message.
mod config;
mod ecu_data_parser;
mod ecu_serial_comms_handler;
mod mqtt_handler;

use gumdrop::Options;
use ecu_serial_comms_handler::start_ecu_communication;
use crate::config::load_configuration;

/// Define options for the program.
#[derive(Debug, Options)]
struct MyOptions {
#[options(help = "print help message")]
help: bool,

#[options(help = "Sets a custom config file", meta = "FILE")]
config: Option<String>,
}

fn print_help() {
println!("Usage: gps-to-mqtt [options]");
println!("Options:");
println!(" -h, --help Print this help message");
println!(" -c, --config FILE Sets a custom config file path");
}

/// Displays a graphical welcome message.
fn display_welcome() {
Expand All @@ -37,11 +56,32 @@ fn display_welcome() {
#[tokio::main]
/// The main function that starts the ECU communication and displays the welcome message.
async fn main() {

// Parse CLI arguments using gumdrop
let opts = MyOptions::parse_args_default_or_exit();

if opts.help {
// Use custom print_help function to display help and exit
print_help();
std::process::exit(0);
}

// Display welcome message
display_welcome();

// Load configuration, set up serial port, and start processing
let config_path = opts.config.as_deref();
let config = match load_configuration(config_path) {
Ok(config) => config,
Err(err) => {
// Handle the error gracefully, print a message, and exit
eprintln!("Error loading configuration: {}", err);
std::process::exit(1);
}
};

// Start ECU communication
start_ecu_communication();
start_ecu_communication(config.clone());
}

#[cfg(test)]
Expand Down

0 comments on commit 4c20a04

Please sign in to comment.