Skip to content

Commit

Permalink
feat(clap): add CLI to start the application
Browse files Browse the repository at this point in the history
Use clap to start the application, also taking into account possible
environment variables set by the user.

Signed-off-by: Riccardo Gallo <riccardo.gallo@secomind.com>
  • Loading branch information
rgallor committed Aug 29, 2024
1 parent 211b88f commit 316d90d
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 100 deletions.
114 changes: 114 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ description = "Astarte Rust SDK based data stream test."

[dependencies]
astarte-device-sdk = "0.8.2"
clap = { version = "=4.4.18", features = ["derive", "env", "string"] }
color-eyre = "0.6.3"
tokio = { version = "1.37.0", features = ["rt-multi-thread", "sync", "macros", "signal"] }
tracing = "0.1.37"
Expand Down
4 changes: 2 additions & 2 deletions scripts/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ export DEVICE_ID=""
export CREDENTIALS_SECRET=""
#export PAIRING_TOKEN=""
export PAIRING_URL="http://api.astarte.localhost/pairing"
export STORE_DIR="/tmp/edgehog/store"
export STORE_DIR="/tmp/stream-rust-test/store"
export IGNORE_SSL_ERRORS="true"
export MATH_FUNCTION="sin"
export INTERVAL_BTW_SAMPLES=1000
export INTERVAL_BTW_SAMPLES=500
export SCALE=3

# Navigate to the project root
Expand Down
92 changes: 0 additions & 92 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,95 +8,3 @@
#![warn(clippy::dbg_macro, missing_docs, rustdoc::missing_crate_level_docs)]

pub mod math;

use crate::math::MathFunction;
use color_eyre::eyre;
use color_eyre::eyre::Context;
use std::env;
use std::path::{Path, PathBuf};
use std::str::FromStr;

const INTERFACE_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/interfaces");

/// Default interval value expressed in milliseconds between two samples sent to Astarte
const DEFAULT_INTERVAL: u64 = 1000;

/// Default scale for the generation of the data to send to Astarte
const DEFAULT_SCALE: f64 = 1.0;

/// Astarte device configuration.
#[derive(Debug, Clone)]
pub struct Config {
/// Astarte realm
pub realm: String,
/// Device ID
pub device_id: String,
/// Device credential secret
pub credentials_secret: Option<String>,
/// Device pairing token
pub pairing_token: Option<String>,
/// Astarte pairing url
pub pairing_url: String,
/// Astarte store directory
pub store_directory: String,
/// Flag to ignore Astarte SSL errors
pub ignore_ssl_errors: bool,
/// Path to folder containing the Astarte Device interfaces
pub interfaces_folder: PathBuf,
/// Math function the device will use to send data to Astarte
pub math_function: MathFunction,
/// Interface name to send data to
pub interface_datastream_do: String,
/// Milliseconds the device must wait before sending data to Astarte
pub interval_btw_samples: u64,
/// Scale for the generation of the data to send
pub scale: f64,
}

fn read_env(name: &str) -> eyre::Result<String> {
env::var(name).wrap_err_with(|| format!("couldn't read environment variable {name}"))
}

impl Config {
/// Initialize the device configuration
pub fn init() -> eyre::Result<Self> {
// read values from environment variables
let realm = read_env("REALM")?;
let device_id = read_env("DEVICE_ID")?;
let credentials_secret = read_env("CREDENTIALS_SECRET").ok();
let pairing_token = read_env("PAIRING_TOKEN").ok();
let pairing_url = read_env("PAIRING_URL")?;
let store_directory = read_env("STORE_DIR")?;
let ignore_ssl_errors = read_env("IGNORE_SSL_ERRORS")
.map(|s| bool::from_str(&s).unwrap_or(true))
.unwrap_or(true);

let interfaces_folder = Path::new(INTERFACE_DIR).to_owned();

let interface_datastream_do = "org.astarte-platform.genericsensors.Values".to_string();

let math_function = read_env("MATH_FUNCTION").unwrap_or_default().into();

let interval_btw_samples = env::var("INTERVAL_BTW_SAMPLES")
.map(|s| s.parse::<u64>().unwrap_or(DEFAULT_INTERVAL))
.unwrap_or(DEFAULT_INTERVAL);

let scale =
env::var("SCALE").map_or(DEFAULT_SCALE, |s| s.parse::<f64>().unwrap_or(DEFAULT_SCALE));

Ok(Self {
realm,
device_id,
credentials_secret,
pairing_token,
pairing_url,
store_directory,
ignore_ssl_errors,
interfaces_folder,
math_function,
interface_datastream_do,
interval_btw_samples,
scale,
})
}
}
61 changes: 56 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,65 @@ use astarte_device_sdk::builder::{DeviceBuilder, DeviceSdkBuild};
use astarte_device_sdk::store::SqliteStore;
use astarte_device_sdk::transport::mqtt::{Credential, MqttConfig};
use astarte_device_sdk::{Client, DeviceClient, EventLoop};
use clap::Parser;
use color_eyre::eyre;
use color_eyre::eyre::{bail, Context};
use color_eyre::eyre::bail;
use std::path::PathBuf;
use std::time::SystemTime;
use stream_rust_test::math::{BaseValue, MathFunction};
use stream_rust_test::Config;
use tokio::task::JoinSet;
use tracing::error;
use tracing::log::debug;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::EnvFilter;

/// Astarte device configuration.
#[derive(Debug, Clone, Parser)]
#[clap(version, about)]
pub struct Config {
/// Astarte realm
#[clap(long, env = "REALM")]
pub realm: String,
/// Device ID
#[clap(long, env = "DEVICE_ID")]
pub device_id: String,
/// Device credential secret
#[clap(long, env = "CREDENTIALS_SECRET")]
pub credentials_secret: Option<String>,
/// Device pairing token
#[clap(long, env = "PAIRING_TOKEN")]
pub pairing_token: Option<String>,
/// Astarte pairing url
#[clap(long, env = "PAIRING_URL")]
pub pairing_url: String,
/// Astarte store directory
#[clap(long, env = "STORE_DIR")]
pub store_dir: String,
/// Flag to ignore Astarte SSL errors
#[clap(long, default_value = "true", env = "IGNORE_SSL_ERRORS")]
pub ignore_ssl_errors: bool,
/// Path to folder containing the Astarte Device interfaces
#[clap(long, default_value = PathBuf::from("interfaces").into_os_string())]
pub interfaces_folder: PathBuf,
/// Math function the device will use to send data to Astarte
#[clap(long, default_value = "default", env = "MATH_FUNCTION")]
pub math_function: MathFunction,
/// Interface name to send data to
#[clap(
long,
default_value = "org.astarte-platform.genericsensors.Values",
env = "INTERFACE_NAME"
)]
pub interface_datastream_do: String,
/// Milliseconds the device must wait before sending data to Astarte
#[clap(long, default_value = "1000", env = "INTERVAL_BTW_SAMPLES")]
pub interval_btw_samples: u64,
/// Scale for the generation of the data to send
#[clap(long, default_value = "1.0", env = "SCALE")]
pub scale: f64,
}

#[tokio::main]
async fn main() -> eyre::Result<()> {
color_eyre::install()?;
Expand All @@ -33,7 +80,9 @@ async fn main() -> eyre::Result<()> {
let now = SystemTime::now();

// initialize configuration options
let cfg = Config::init().wrap_err("Failed configuration initialization")?;
let cfg = Config::parse();

debug!("Parsed config: {:#?}", cfg);

// define type of credential (pairing token or credential secret) to use to establish an MQTT
// connection with Astarte
Expand All @@ -54,7 +103,7 @@ async fn main() -> eyre::Result<()> {

// connect to Astarte
let (client, mut connection) = DeviceBuilder::new()
.store_dir(cfg.store_directory)
.store_dir(cfg.store_dir)
.await?
.interface_directory(cfg.interfaces_folder.as_path())?
.connect(mqtt_config)
Expand Down Expand Up @@ -106,6 +155,8 @@ async fn send_data(
) -> eyre::Result<()> {
let mut base_value = BaseValue::try_from_system_time(now, scale)?;

debug!("sending data to Astarte with {math_function} math function");

loop {
// Send data to Astarte
let value = math_function.compute(base_value.value());
Expand All @@ -114,7 +165,7 @@ async fn send_data(
.send(&interface_datastream_do, "/test/value", value)
.await?;

debug!("Data sent on endpoint /test/value, content: {value}");
debug!("data sent on endpoint /test/value, content: {value}");

// update the data to send at the next iteration
base_value.update();
Expand Down
Loading

0 comments on commit 316d90d

Please sign in to comment.