diff --git a/.github/workflows/build-binaries.yml b/.github/workflows/build-binaries.yml index 30a40f22..838c8d93 100644 --- a/.github/workflows/build-binaries.yml +++ b/.github/workflows/build-binaries.yml @@ -1,6 +1,6 @@ # create binary on windows -name: build_binaries +name: Build Binaries on: push: @@ -12,7 +12,7 @@ jobs: # --------------------------------------------------------------------------- build-on-ubuntu: - runs-on: windows-latest + runs-on: ubuntu-latest steps: - name: Checkout project uses: actions/checkout@v4 @@ -21,7 +21,27 @@ jobs: run: cargo --version - name: Try to Build - run: cargo build --features log,built-in-drivers --verbose --release + run: cargo build --verbose --release + + # Release version is supposed to have a faster runtime than debug + # - name: Upload windows binaries release + # uses: actions/upload-artifact@v4 + # with: + # name: windows_binaries + # path: target/release/panduza-rust-platform.exe + + # --------------------------------------------------------------------------- + build-on-ubuntu-builtin: + runs-on: ubuntu-latest + steps: + - name: Checkout project + uses: actions/checkout@v4 + + - name: Display cargo version + run: cargo --version + + - name: Try to Build + run: cargo build --features built-in-drivers --verbose --release # Release version is supposed to have a faster runtime than debug # - name: Upload windows binaries release @@ -41,7 +61,20 @@ jobs: run: cargo --version - name: Try to Build - run: cargo build --features log,built-in-drivers --verbose --release + run: cargo build --verbose --release + + # --------------------------------------------------------------------------- + build-on-windows-builtin: + runs-on: windows-latest + steps: + - name: Checkout project + uses: actions/checkout@v4 + + - name: Display cargo version + run: cargo --version + + - name: Try to Build + run: cargo build --features built-in-drivers --verbose --release # Release version is supposed to have a faster runtime than debug - name: Upload windows binaries release @@ -50,4 +83,4 @@ jobs: name: windows_binaries path: target/release/panduza-rust-platform.exe - \ No newline at end of file + diff --git a/.gitignore b/.gitignore index b59c1e98..70b4ec74 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ target log.csv +platform-log.csv *.swp -Cargo.lock \ No newline at end of file +Cargo.lock +src/sys_info.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 343a5195..48875292 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,14 @@ ], "rust-analyzer.showUnlinkedFileNotification": false, "rust-analyzer.cargo.features": [ - "built-in-drivers" - ] + // "built-in-drivers" + ], + "rainbow_csv.virtual_alignment_mode": "always", + "rainbow_csv.autodetect_separators": [ + ";", + "|", + ], + "[csv]": { + "editor.inlayHints.maximumLength": 0 + }, } \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index ffcbc33f..8d615168 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,23 +1,20 @@ [package] name = "panduza-rust-platform" -version = "0.4.2" +version = "0.5.0" edition = "2021" [dependencies] # Main base code for Panduza platform and plugins -# panduza-platform-core = { git = "https://github.com/Panduza/panduza-platform-core", branch = "main", features = [ -# "log", -# ] } -panduza-platform-core = { git = "https://github.com/Panduza/panduza-platform-core", tag = "0.1.4", features = [ - # "serial", - "log", -] } - +panduza-platform-core = { git = "https://github.com/Panduza/panduza-platform-core", tag = "0.1.5" } # Main async framework for the platform tokio = { version = "1.40.0", features = ["full", "tracing"] } + +clap = { version = "4.5.21", features = ["derive"] } + + libloading = "0.8" # @@ -77,9 +74,9 @@ tracing = { version = "0.1", features = [ # "release_max_level_off", # "release_max_level_error", # "release_max_level_warn", - "release_max_level_info", + # "release_max_level_info", # "release_max_level_debug", - # "release_max_level_trace" + "release_max_level_trace", ] } # @@ -101,29 +98,23 @@ rust_decimal_macros = "1.35" # --- BUILT-IN DRIVERS # crate-type = ["lib"] => because plugins need to be compile as simple lib when built-in # --- +# Fake devices +pza-plugin-fakes = { git = "https://github.com/Panduza/pza-plugin-fakes", tag = "0.1.0", optional = true } +# --- # Korad devices -pza-plugin-korad = { git = "https://github.com/Panduza/pza-plugin-korad", tag = "1.0.3", features = [ - "log", -], optional = true } +pza-plugin-korad = { git = "https://github.com/Panduza/pza-plugin-korad", tag = "1.0.4", optional = true } # --- # Hameg devices -pza-plugin-hameg = { git = "https://github.com/Panduza/pza-plugin-hameg", tag = "1.0.2", features = [ - "log", -], optional = true } +pza-plugin-hameg = { git = "https://github.com/Panduza/pza-plugin-hameg", tag = "1.0.3", optional = true } -# --- FEATURES -[features] - -# Enable tracing in stdout -log = [] -log-issue = [] -broker-log = [] -# Enable tracing in tokio console -# trac-console = [ "console-subscriber" ] +[build-dependencies] +toml = "0.8.19" +# --- FEATURES +[features] # --- # Integrate some generic drivers directly into the platform # -built-in-drivers = ["pza-plugin-korad", "pza-plugin-hameg"] +built-in-drivers = ["pza-plugin-korad", "pza-plugin-hameg", "pza-plugin-fakes"] diff --git a/README.md b/README.md index 7fccdc26..3022a025 100644 --- a/README.md +++ b/README.md @@ -7,44 +7,20 @@ You must install Rust and Cargo then execute this commands ```bash # Enable fmt tracing (std terminal logs) -cargo run --features log +cargo run + +# Log options +cargo run -- -h +# -l to log on terminal +# -b to enable broker logs on terminal +# -d to enable debug logs (terminal + file) +# -t to enable trace logs (terminal + file) ``` To embbed built-in drivers ```bash # Enable fmt tracing (std terminal logs) -cargo run --features log,built-in-drivers -cargo build --features log,built-in-drivers +cargo run --features built-in-drivers +cargo build --features built-in-drivers ``` - -## Manage log levels - -To set the log level at build time, please go to the Cargo.tml - -Then edit the 'tacing' entry to set the level you want - -```toml -tracing = { version = "0.1", features = [ - # "max_level_off", - # "max_level_error", - # "max_level_warn", - "max_level_info", - # "max_level_debug", - # "max_level_trace", - # "release_max_level_off", - # "release_max_level_error", - # "release_max_level_warn", - "release_max_level_info", - # "release_max_level_debug", - # "release_max_level_trace" -]} -``` - -# Others - - -```bash -# Enable tokio console tracing -RUSTFLAGS="--cfg tokio_unstable" cargo run --features trac-console -``` \ No newline at end of file diff --git a/build.rs b/build.rs new file mode 100644 index 00000000..f4250bc2 --- /dev/null +++ b/build.rs @@ -0,0 +1,32 @@ +use std::fs::File; +use std::io::Write; +use std::process::Command; + +fn main() { + // Get rustc version + let output = Command::new("rustc") + .args(&["--version"]) + .output() + .expect("failed to execute process"); + let rustc_version = String::from_utf8_lossy(&output.stdout); + + // Get version from Cargo.toml (assuming it's in the same directory) + let cargo_toml = include_str!("Cargo.toml"); + let package: toml::value::Table = toml::from_str(cargo_toml).unwrap(); + let version = package["package"]["version"].as_str().unwrap(); + + // Format information for writing + let info = format!( + "pub static RUSTC_VERSION: &str = \"{}\";\n +pub static PLATFORM_VERSION: &str = \"{}\";\n", + rustc_version.trim_end_matches("\n"), + version + ); + + // Write information to file + let mut file = File::create("src/sys_info.rs").expect("Failed to create sys_info.rs"); + file.write_all(info.as_bytes()) + .expect("Failed to write to sys_info.rs"); + + println!("Information written to sys_info.rs"); +} diff --git a/run.sh b/run.sh new file mode 100644 index 00000000..e631d043 --- /dev/null +++ b/run.sh @@ -0,0 +1 @@ +cargo run -- -l diff --git a/run_with_builtin.sh b/run_with_builtin.sh new file mode 100644 index 00000000..4e272660 --- /dev/null +++ b/run_with_builtin.sh @@ -0,0 +1 @@ +cargo run --features built-in-drivers -- -l diff --git a/src/__builtin_devices/cobolt/s0501/mod.rs b/src/__builtin_devices/cobolt/s0501/mod.rs index 6eae5201..fe06d7a4 100644 --- a/src/__builtin_devices/cobolt/s0501/mod.rs +++ b/src/__builtin_devices/cobolt/s0501/mod.rs @@ -2,42 +2,32 @@ use async_trait::async_trait; use panduza_core::device::Device; use serde_json::json; +use panduza_core::device::{traits::DeviceActions, traits::Hunter, traits::Producer}; use panduza_core::Error as PlatformError; -use panduza_core::device::{ traits::DeviceActions, traits::Producer, traits::Hunter }; use panduza_core::interface::builder::Builder as InterfaceBuilder; - use panduza_connectors::serial::tty::Config as SerialConfig; use tokio_serial; mod itf_cobolt_0501_blc; - - static VID: u16 = 0x25dc; static PID: u16 = 0x0006; pub struct DeviceHunter; - #[async_trait] impl Hunter for DeviceHunter { - async fn hunt(&self) -> Option> { - let mut bag = Vec::new(); - // println!("DeviceHunter::hunt"); - let ports = match tokio_serial::available_ports() { Ok(p) => p, - Err(_e) => return None + Err(_e) => return None, }; for port in ports { - // println!("{:?}", port); - match port.port_type { tokio_serial::SerialPortType::UsbPort(info) => { if info.vid == VID && info.pid == PID { @@ -56,29 +46,24 @@ impl Hunter for DeviceHunter { } )) } - }, + } _ => {} } } if bag.is_empty() { return None; - } - else { + } else { return Some(bag); } } - } struct S0501; impl DeviceActions for S0501 { - /// Create the interfaces - fn interface_builders(&self, device: &Device) - -> Result, PlatformError> - { + fn interface_builders(&self, device: &Device) -> Result, PlatformError> { let logger = device.clone_logger().clone(); let device_settings = device.settings.clone(); @@ -92,20 +77,14 @@ impl DeviceActions for S0501 { serial_conf.serial_baudrate = Some(115200); let mut list = Vec::new(); - list.push( - itf_cobolt_0501_blc::build("blc", &serial_conf) - ); + list.push(itf_cobolt_0501_blc::build("blc", &serial_conf)); return Ok(list); } } - - - pub struct DeviceProducer; impl Producer for DeviceProducer { - fn settings_props(&self) -> serde_json::Value { return json!([ { @@ -131,10 +110,7 @@ impl Producer for DeviceProducer { ]); } - fn produce(&self) -> Result, PlatformError> { - return Ok(Box::new(S0501{})); + return Ok(Box::new(S0501 {})); } - } - diff --git a/src/__builtin_devices/oxxius/lbx_488/itf_lbx_488_blc.rs b/src/__builtin_devices/oxxius/lbx_488/itf_lbx_488_blc.rs index f32123aa..08e5338e 100644 --- a/src/__builtin_devices/oxxius/lbx_488/itf_lbx_488_blc.rs +++ b/src/__builtin_devices/oxxius/lbx_488/itf_lbx_488_blc.rs @@ -1,15 +1,14 @@ use async_trait::async_trait; - -use panduza_core::meta::blc::BlcAttributes; -use panduza_core::Error as PlatformError; -use panduza_core::platform_error_result; use panduza_core::interface::builder::Builder as InterfaceBuilder; -use panduza_core::meta::blc; use panduza_core::interface::AmInterface; +use panduza_core::meta::blc; +use panduza_core::meta::blc::BlcAttributes; +use panduza_core::platform_error_result; +use panduza_core::Error as PlatformError; -use panduza_connectors::usb::usb::{self, UsbConnector}; use panduza_connectors::usb::usb::Config as UsbConfig; +use panduza_connectors::usb::usb::{self, UsbConnector}; use rust_decimal::prelude::FromPrimitive; use rust_decimal::prelude::ToPrimitive; @@ -17,7 +16,7 @@ use rust_decimal::Decimal; use rust_decimal_macros::dec; /// -/// +/// struct LBX488BlcActions { connector_usb: usb::UsbConnector, serial_config: UsbConfig, @@ -26,15 +25,13 @@ struct LBX488BlcActions { power_max: f64, power_value: f64, current_value: f64, - analog_modulation: bool + analog_modulation: bool, } impl LBX488BlcActions { - /// Wrapper to format the commands - /// + /// async fn ask(&mut self, command: &[u8]) -> Result { - let mut cmd = vec![0; 32]; cmd[..command.len()].copy_from_slice(command); @@ -42,8 +39,8 @@ impl LBX488BlcActions { Ok(self.connector_usb.read().await?) } - /// Parse the data into f64 using 2 decimals - /// + /// Parse the data into f64 using 2 decimals + /// async fn ask_float(&mut self, command: &[u8]) -> Result { match self.ask(command).await?.trim_end_matches("\0").to_string().parse::() { Ok(f) => { @@ -68,23 +65,23 @@ impl LBX488BlcActions { #[async_trait] impl blc::BlcActions for LBX488BlcActions { - /// Initialize the interface - /// + /// async fn initializating(&mut self, interface: &AmInterface) -> Result<(), PlatformError> { - self.connector_usb = match usb::get(&self.serial_config).await { Some(connector) => connector, - None => return platform_error_result!("Unable to create USB connector for Oxxius LBX488") + None => { + return platform_error_result!("Unable to create USB connector for Oxxius LBX488") + } }; self.connector_usb.init().await?; let result = self.ask("?HID".as_bytes()).await?; - interface.lock().await.log_info( - format!("LBX_488 - initializing: {}", result) - ); - + interface + .lock() + .await + .log_info(format!("LBX_488 - initializing: {}", result)); return Ok(()); } @@ -95,9 +92,11 @@ impl blc::BlcActions for LBX488BlcActions { // ---------------------------------------------------------------------------- /// Read the analog modulation - /// - async fn read_analog_modulation(&mut self, interface: &AmInterface) -> Result { - + /// + async fn read_analog_modulation( + &mut self, + interface: &AmInterface, + ) -> Result { let answer = self.ask("?AM".as_bytes()).await?; if answer == "0\x00" { self.analog_modulation = false; @@ -107,19 +106,24 @@ impl blc::BlcActions for LBX488BlcActions { self.analog_modulation = true; } - interface.lock().await.log_info( - format!("read analog modulation value : {}", self.analog_modulation) - ); + interface.lock().await.log_info(format!( + "read analog modulation value : {}", + self.analog_modulation + )); return Ok(self.analog_modulation); } /// Write the analog modulation - /// - async fn write_analog_modulation(&mut self, interface: &AmInterface, v: bool) -> Result<(), PlatformError> { - - interface.lock().await.log_info( - format!("write analog modulation value : {}", v) - ); + /// + async fn write_analog_modulation( + &mut self, + interface: &AmInterface, + v: bool, + ) -> Result<(), PlatformError> { + interface + .lock() + .await + .log_info(format!("write analog modulation value : {}", v)); let cmd; @@ -143,16 +147,15 @@ impl blc::BlcActions for LBX488BlcActions { // ---------------------------------------------------------------------------- /// Read the mode value - /// + /// async fn read_mode_value(&mut self, _interface: &AmInterface) -> Result { - self.mode_value = "no_regulation".to_string(); let acc = self.ask("?ACC".as_bytes()).await?; if acc == "1\x00" { self.mode_value = "constant_current".to_string(); } - + let apc = self.ask("?APC".as_bytes()).await?; if apc == "1\x00" { self.mode_value = "constant_power".to_string(); @@ -162,17 +165,21 @@ impl blc::BlcActions for LBX488BlcActions { } /// Write the mode value - /// - async fn write_mode_value(&mut self, interface: &AmInterface, v: String) -> Result<(), PlatformError> { - - interface.lock().await.log_info( - format!("write mode value : {}", v) - ); + /// + async fn write_mode_value( + &mut self, + interface: &AmInterface, + v: String, + ) -> Result<(), PlatformError> { + interface + .lock() + .await + .log_info(format!("write mode value : {}", v)); let command = match v.as_str() { "constant_current" => format!("ACC 1"), "constant_power" => format!("APC 1"), - _ => return platform_error_result!("Unexpected command for mode value") + _ => return platform_error_result!("Unexpected command for mode value"), }; self.ask(command.as_bytes()).await?; @@ -185,9 +192,8 @@ impl blc::BlcActions for LBX488BlcActions { // ---------------------------------------------------------------------------- /// Read the enable value - /// + /// async fn read_enable_value(&mut self, _interface: &AmInterface) -> Result { - let emission = self.ask("?L".as_bytes()).await?; if emission == "1\x00" { self.enable_value = true; @@ -199,21 +205,25 @@ impl blc::BlcActions for LBX488BlcActions { } /// Write the enable value - /// - async fn write_enable_value(&mut self, interface: &AmInterface, v: bool) -> Result<(), PlatformError> { - + /// + async fn write_enable_value( + &mut self, + interface: &AmInterface, + v: bool, + ) -> Result<(), PlatformError> { let val_int = match v { true => 1, - false => 0 + false => 0, }; let command = format!("L {}", val_int); let status = self.ask(command.as_bytes()).await?; - - interface.lock().await.log_info( - format!("write enable value : {}", status) - ); + + interface + .lock() + .await + .log_info(format!("write enable value : {}", status)); return Ok(()); } @@ -223,56 +233,66 @@ impl blc::BlcActions for LBX488BlcActions { // ---------------------------------------------------------------------------- /// Read the power value - /// + /// async fn read_power_max(&mut self, interface: &AmInterface) -> Result { - self.power_max = self.ask_float(b"?MAXLP").await?; - interface.lock().await.log_info( - format!("read max power : {}", self.power_max) - ); + interface + .lock() + .await + .log_info(format!("read max power : {}", self.power_max)); return Ok(self.power_max); } /// Read the power value - /// + /// async fn read_power_value(&mut self, interface: &AmInterface) -> Result { - self.power_value = self.ask_float(b"?SP\r").await?; - println!("Success reading power value : {:?}", self.power_value); - - interface.lock().await.log_info( - format!("read power : {}", self.power_value) - ); + interface + .lock() + .await + .log_info(format!("read power : {}", self.power_value)); return Ok(self.power_value); } /// Write the power value - /// - async fn write_power_value(&mut self, interface: &AmInterface, v: f64) -> Result<(), PlatformError> { - + /// + async fn write_power_value( + &mut self, + interface: &AmInterface, + v: f64, + ) -> Result<(), PlatformError> { let value_dec = match Decimal::from_f64(v) { Some(value) => value, - None => return platform_error_result!("Unexpected answer form Oxxius LBX488 : could not parse as Decimal") + None => { + return platform_error_result!( + "Unexpected answer form Oxxius LBX488 : could not parse as Decimal" + ) + } }; - + // 3 represent the number of decimal who can manage by the device, // here for oxxius it is 3 let val_mw = (value_dec * dec!(1000.0)).round_dp(2); - + let val_f64 = match val_mw.to_f64() { Some(value) => value, - None => return platform_error_result!("Unexpected answer form Oxxius LBX488 : could not parse as f64") + None => { + return platform_error_result!( + "Unexpected answer form Oxxius LBX488 : could not parse as f64" + ) + } }; let command = format!("PM {}", val_f64); - interface.lock().await.log_info( - format!("write power : {}", command) - ); + interface + .lock() + .await + .log_info(format!("write power : {}", command)); self.ask(command.as_bytes()).await?; return Ok(()); @@ -284,88 +304,103 @@ impl blc::BlcActions for LBX488BlcActions { // ---------------------------------------------------------------------------- /// Read max current value - /// - - async fn read_max_current_value(&mut self, interface: &AmInterface) -> Result { + /// + async fn read_max_current_value( + &mut self, + interface: &AmInterface, + ) -> Result { let value_max_f64 = self.ask_float(b"?MAXLC").await?; - println!("max current : {}", value_max_f64); - // Oxxius give 125% of max current but only 100% can be used else // the laser need something to be reboot let value_max_dec = match Decimal::from_f64(value_max_f64) { Some(value) => (value * dec!(100) / dec!(125)).round_dp(3), - None => return platform_error_result!("Unexpected answer form Oxxius LBX488 : could not parse as Decimal") + None => { + return platform_error_result!( + "Unexpected answer form Oxxius LBX488 : could not parse as Decimal" + ) + } }; let value_max = match value_max_dec.to_f64() { Some(value) => value, - None => return platform_error_result!("Unexpected answer form Oxxius LBX488 : could not parse as f64") + None => { + return platform_error_result!( + "Unexpected answer form Oxxius LBX488 : could not parse as f64" + ) + } }; self.current_value = value_max; - interface.lock().await.log_info( - format!("read max current : {}", self.current_value) - ); + interface + .lock() + .await + .log_info(format!("read max current : {}", self.current_value)); return Ok(self.current_value); } /// Read the current value - /// + /// async fn read_current_value(&mut self, interface: &AmInterface) -> Result { - self.current_value = self.ask_float(b"?SC").await?; - interface.lock().await.log_info( - format!("read current : {}", self.current_value) - ); + interface + .lock() + .await + .log_info(format!("read current : {}", self.current_value)); return Ok(self.current_value); } /// Write the current value - /// - async fn write_current_value(&mut self, interface: &AmInterface, v: f64) -> Result<(), PlatformError> { - + /// + async fn write_current_value( + &mut self, + interface: &AmInterface, + v: f64, + ) -> Result<(), PlatformError> { let value_dec = match Decimal::from_f64(v) { Some(value) => value, - None => return platform_error_result!("Unexpected answer form Oxxius LBX488 : could not parse as Decimal") + None => { + return platform_error_result!( + "Unexpected answer form Oxxius LBX488 : could not parse as Decimal" + ) + } }; - + // 3 represent the number of decimal who can manage by the device, // here for oxxius it is 3 let val_ma = (value_dec * dec!(1000.0)).round_dp(2); - + let val_f64 = match val_ma.to_f64() { Some(value) => value, - None => return platform_error_result!("Unexpected answer form Oxxius LBX488 : could not parse as f64") + None => { + return platform_error_result!( + "Unexpected answer form Oxxius LBX488 : could not parse as f64" + ) + } }; let command = format!("CM {}", val_f64); - interface.lock().await.log_info( - format!("write current : {}", val_ma) - ); + interface + .lock() + .await + .log_info(format!("write current : {}", val_ma)); self.ask(command.as_bytes()).await?; return Ok(()); } } - - /// Interface -/// -pub fn build>( - name: A, - serial_config: &UsbConfig -) -> InterfaceBuilder { - +/// +pub fn build>(name: A, serial_config: &UsbConfig) -> InterfaceBuilder { return blc::build( - name, + name, blc::BlcParams { power_min: 0.0, // power_max: 0.04, @@ -373,7 +408,7 @@ pub fn build>( current_min: 0.0, current_decimals: 5, - }, + }, Box::new(LBX488BlcActions { connector_usb: UsbConnector::new(None), serial_config: serial_config.clone(), @@ -382,9 +417,8 @@ pub fn build>( power_max: 0.0, power_value: 0.0, current_value: 0.0, - analog_modulation: true + analog_modulation: true, }), - BlcAttributes::all_attributes() - ) + BlcAttributes::all_attributes(), + ); } - diff --git a/src/built_in.rs b/src/built_in.rs index 7eba0c12..476480d6 100644 --- a/src/built_in.rs +++ b/src/built_in.rs @@ -1,21 +1,21 @@ use panduza_platform_core::Producer; use panduza_platform_core::Scanner; -#[cfg(feature = "built-in-drivers")] // Export the producers of the plugin // pub fn plugin_producers() -> Vec> { let mut producers: Vec> = vec![]; + producers.extend(pza_plugin_fakes::plugin_producers()); producers.extend(pza_plugin_korad::plugin_producers()); producers.extend(pza_plugin_hameg::plugin_producers()); return producers; } -#[cfg(feature = "built-in-drivers")] // // pub fn plugin_scanners() -> Vec> { let mut scanners: Vec> = vec![]; + scanners.extend(pza_plugin_fakes::plugin_scanners()); scanners.extend(pza_plugin_korad::plugin_scanners()); scanners.extend(pza_plugin_hameg::plugin_scanners()); return scanners; diff --git a/src/main.rs b/src/main.rs index 3175abb0..4c8ed3a6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ #![deny( while_true, improper_ctypes, -// non_shorthand_field_patterns, + non_shorthand_field_patterns, // no_mangle_generic_items, // overflowing_literals, // path_statements, @@ -15,38 +15,126 @@ // unused_parens, )] +#[cfg(feature = "built-in-drivers")] mod built_in; + mod device_tree; mod platform; mod plugins_manager; +mod sys_info; mod underscore_device; +use panduza_platform_core::env::system_default_device_tree_file; +use panduza_platform_core::env::system_default_log_dir; pub use platform::Platform; -// use panduza_platform_core::Plugin; -// use panduza_platform_core::ProductionOrder; -// use rumqttd::Broker; -// use rumqttd::Config; +use clap::Parser; + +/// Simple program to greet a person +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +pub struct Args { + /// Enable logs on stdout + #[arg(short, long)] + log_stdout_enable: bool, + + /// Also display broker logs + #[arg(short, long)] + broker_log_enable: bool, + + /// Enable debug logs + #[arg(short, long)] + debug_log: bool, + + /// Enable trace logs + #[arg(short, long)] + trace_log: bool, +} + +/// At least print arguments when the platform is started +/// If the use start without log, he can understand why there is none. +/// +fn print_platform_header(args: &Args) { + println!("----------------------------------------"); + println!("# Panduza Platform"); + println!(""); + println!( + "- Stdout logs : {}", + if args.log_stdout_enable { + "ENABLED" + } else { + "DISABLED" + } + ); + println!( + "- Broker logs : {}", + if args.broker_log_enable { + "ENABLED" + } else { + "DISABLED" + } + ); + println!( + "- Debug logs : {}", + if args.debug_log || args.trace_log { + "ENABLED" + } else { + "DISABLED" + } + ); + println!( + "- Trace logs : {}", + if args.trace_log { + "ENABLED" + } else { + "DISABLED" + } + ); + println!(""); + println!( + "- Log dir : {:?}", + system_default_log_dir().unwrap() + ); + println!( + "- Tree file : {:?}", + system_default_device_tree_file().unwrap() + ); + + println!("----------------------------------------"); +} #[tokio::main] async fn main() { - // Init tracing subscribers - panduza_platform_core::log::init(); + // + // Manage args + let args = Args::parse(); + + // + // Give some information when the platform start + print_platform_header(&args); + + // + // Manage logs + // Init tracing subscriber + panduza_platform_core::tracing::init( + args.log_stdout_enable, + args.broker_log_enable, + args.debug_log, + args.trace_log, + ); // Create platform runner // La platform c'est l'assemblage de // - 1 broker // - 1 runtime pour les services de bases // - N plugins runtime - let mut platform = Platform::new(); - // std::thread::spawn(move || { - // broker.start().unwrap(); - // }); + let mut platform = Platform::new(args.log_stdout_enable, args.debug_log, args.trace_log); + + // + // Log minimal set of information + platform.log_starting_info(&args, sys_info::PLATFORM_VERSION, sys_info::RUSTC_VERSION); + // // Platform loop platform.run().await; - - // for p in plugins { - // unsafe { (p.join)() }; - // } } diff --git a/src/platform.rs b/src/platform.rs index 279a9980..756d40eb 100644 --- a/src/platform.rs +++ b/src/platform.rs @@ -1,4 +1,6 @@ +#[cfg(feature = "built-in-drivers")] use crate::built_in; + use crate::device_tree::DeviceTree; use crate::plugins_manager::PluginsManager; use crate::underscore_device::pack::InfoPack; @@ -7,7 +9,7 @@ use crate::underscore_device::store::data::SharedStore; use crate::underscore_device::UnderscoreDevice; use futures::FutureExt; use panduza_platform_core::{ - create_task_channel, env, Factory, InstanceMonitor, Notification, NotificationGroup, + create_task_channel, env, log_debug, Factory, InstanceMonitor, Notification, NotificationGroup, ProductionOrder, Runtime, Store, TaskReceiver, TaskResult, TaskSender, }; use panduza_platform_core::{PlatformLogger, Reactor, ReactorSettings}; @@ -119,7 +121,7 @@ pub struct Platform { impl Platform { /// Create a new instance of the Platform /// - pub fn new() -> Self { + pub fn new(enable_stdout: bool, debug: bool, trace: bool) -> Self { // // Task creation request channel let (main_tx, main_rx) = create_task_channel::(20); @@ -141,7 +143,7 @@ impl Platform { request_receiver: Some(rqst_rx), reactor: None, - plugin_manager: PluginsManager::new(), + plugin_manager: PluginsManager::new(enable_stdout, debug, trace), notifications: Arc::new(Mutex::new(Vec::new())), new_notifications_notifier: Arc::new(Notify::new()), @@ -155,12 +157,23 @@ impl Platform { }; } + /// + /// + pub fn log_starting_info( + &self, + args: &super::Args, + platform_version: &str, + rustc_version: &str, + ) { + log_info!(self.logger, "-- Platform Start --"); + log_info!(self.logger, "Args: {:?}", args); + log_info!(self.logger, "Platform Version: {}", platform_version); + log_info!(self.logger, "Rustc Version: {}", rustc_version); + } + /// Main platform run loop /// pub async fn run(&mut self) { - // Info log - self.logger.info("Platform Version ..."); - // // self.request_sender.try_send(ServiceRequest::Boot).unwrap(); @@ -264,7 +277,7 @@ impl Platform { } // // Reaching here means that there is no task anymore - self.logger.warn("All tasks completed"); + log_debug!(self.logger, "All tasks completed"); match self.must_stop.load(Ordering::Relaxed) { true => { // No task and stop request => quit this loop @@ -272,7 +285,7 @@ impl Platform { } false => { // Wait for an other task to be loaded - self.logger.warn("Wait for new tasks"); + log_debug!(self.logger, "Wait for new tasks"); self.new_task_notifier.notified().await; true } @@ -474,8 +487,8 @@ impl Platform { self.logger.info("----- SERVICE : LOAD LOCAL RUNTIME -----"); // - // - // mut + // Allow mut here because it depend of features enabled + #[allow(unused_mut)] let mut factory = Factory::new(); // @@ -601,6 +614,7 @@ impl Platform { self.logger.info("----- SERVICE : START SCANNING -----"); // self.logger.info(format!("ORDER: {:?}", po)); + #[cfg(feature = "built-in-drivers")] for scanner in built_in::plugin_scanners() { let result = scanner.scan(); } diff --git a/src/plugins_manager.rs b/src/plugins_manager.rs index e2e5dcf1..5c490e95 100644 --- a/src/plugins_manager.rs +++ b/src/plugins_manager.rs @@ -29,7 +29,12 @@ impl PluginHandler { /// /// Load a plugin from a file /// - pub fn from_filename(filename: PathBuf) -> Result { + pub fn from_filename( + filename: PathBuf, + enable_stdout: bool, + debug: bool, + trace: bool, + ) -> Result { unsafe { // // Load library object @@ -42,14 +47,15 @@ impl PluginHandler { // // Get plugin interface from entry point - let plugin_entry_point: libloading::Symbol Plugin> = - object.get(b"plugin_entry_point").map_err(|e| { - Error::PluginError(format!( - "Unable to load plugin_entry_point [{:?}] - ({:?})", - filename, e - )) - })?; - let interface = plugin_entry_point(); + let plugin_entry_point: libloading::Symbol< + extern "C" fn(enable_stdout: bool, debug: bool, trace: bool) -> Plugin, + > = object.get(b"plugin_entry_point").map_err(|e| { + Error::PluginError(format!( + "Unable to load plugin_entry_point [{:?}] - ({:?})", + filename, e + )) + })?; + let interface = plugin_entry_point(enable_stdout, debug, trace); // // @@ -85,8 +91,7 @@ impl PluginHandler { unsafe { if self.store.contains(&order.dref) { let order_as_c_string = order.to_c_string()?; - let ret = (self.interface.produce)(order_as_c_string.as_c_str().as_ptr()); - println!("==> {}", ret); + let _ret = (self.interface.produce)(order_as_c_string.as_c_str().as_ptr()); return Ok(true); } } @@ -122,8 +127,6 @@ impl PluginHandler { )) })?; - // println!("pulll {:?}", obj); - Ok(obj) } } @@ -175,17 +178,25 @@ pub struct PluginsManager { /// Plugin handlers /// handlers: Vec, + + enable_stdout: bool, + debug: bool, + trace: bool, } impl PluginsManager { /// /// Create a new object /// - pub fn new() -> Self { + pub fn new(enable_stdout: bool, debug: bool, trace: bool) -> Self { Self { logger: PlatformLogger::new(), handlers: Vec::new(), + + enable_stdout: enable_stdout, + debug: debug, + trace: trace, } } @@ -233,7 +244,8 @@ impl PluginsManager { /// pub fn register_plugin(&mut self, filename: PathBuf) -> Result<(), Error> { // - let handler = PluginHandler::from_filename(filename)?; + let handler = + PluginHandler::from_filename(filename, self.enable_stdout, self.debug, self.trace)?; // Info self.logger diff --git a/src/underscore_device.rs b/src/underscore_device.rs index 944698f5..6bfb3e21 100644 --- a/src/underscore_device.rs +++ b/src/underscore_device.rs @@ -1,4 +1,5 @@ pub mod att; +mod devices; pub mod pack; pub mod pack_inner; pub mod scanner; @@ -7,12 +8,10 @@ pub mod structure; pub mod topic; use async_trait::async_trait; -use futures::lock::Mutex; use pack::InfoPack; -use panduza_platform_core::{DriverOperations, Error, Instance, JsonAttServer}; +use panduza_platform_core::{DriverOperations, Error, Instance}; use scanner::data::ScannerDriver; -use serde_json::json; -use std::{collections::HashMap, sync::Arc, time::Duration}; +use std::time::Duration; use store::data::SharedStore; use tokio::time::sleep; pub use topic::Topic; @@ -33,12 +32,6 @@ pub struct UnderscoreDevice { store: SharedStore, scanner_driver: ScannerDriver, - - /// - /// Each device have an attribute to share its state - /// This Map hold those attribute, the name of the device is the key. - /// - instance_attributes: Arc>>, } impl UnderscoreDevice { @@ -52,7 +45,6 @@ impl UnderscoreDevice { pack: pack.clone(), store: store, scanner_driver: scanner_driver, - instance_attributes: Arc::new(Mutex::new(HashMap::new())), }; (device, pack) @@ -64,7 +56,7 @@ impl DriverOperations for UnderscoreDevice { /// /// /// - async fn mount(&mut self, mut instance: Instance) -> Result<(), Error> { + async fn mount(&mut self, instance: Instance) -> Result<(), Error> { // // Mount the store store::mount(instance.clone(), self.store.clone()).await?; @@ -74,88 +66,12 @@ impl DriverOperations for UnderscoreDevice { scanner::mount(instance.clone(), self.scanner_driver.clone()).await?; // - // state of each devices - let mut interface_devices = instance.create_class("devices").finish(); - - // I need to spawn a task to watch if a device status has changed, if yes update - // It is a better design to create a task that will always live here - let pack_clone2 = self.pack.clone(); - let instance_attributes_clone = self.instance_attributes.clone(); - instance - .spawn(async move { - // - // Clone the notifier from info pack - let device_status_change = pack_clone2.instance_status_change_notifier(); - - // - loop { - // - // Wait for next status change - device_status_change.notified().await; - - println!("$$$$$$$$$$ status change"); - - let pack_status = pack_clone2.pack_instance_status(); - - println!("{:?}", pack_status); - - let mut lock = instance_attributes_clone.lock().await; - for status in pack_status { - if !lock.contains_key(&status.0) { - let att = interface_devices - .create_attribute(status.0.clone()) - .with_ro() - .finish_as_json() - .await?; - - lock.insert(status.0.clone(), att); - } - - lock.get_mut(&status.0) - .unwrap() - .set(json!({ - "state": status.1.to_string(), - "alerts": status.2 - })) - .await?; - } - drop(lock); - } - // Ok(()) - }) - .await; + // Mount devices + devices::mount(instance.clone(), self.pack.clone()).await?; // - // Structure of the devices - let structure_att = instance - .create_attribute("structure") - .with_ro() - .finish_as_json() - .await?; - - let pack_clone3 = self.pack.clone(); - instance - .spawn(async move { - // - // - let structure_change = pack_clone3.instance_structure_change_notifier().await; - // let pack_clone4 = pack_clone3.clone(); - - loop { - // - // Wait for next status change - structure_change.notified().await; - - println!("$$$$$$$$$$ structure change ****"); - - let structure = pack_clone3.device_structure_as_json_value().await.unwrap(); - println!("structure {:?}", structure); - - structure_att.set(structure).await.unwrap(); - } - // Ok(()) - }) - .await; + // Mount structure + structure::mount(instance.clone(), self.pack.clone()).await?; Ok(()) } diff --git a/src/underscore_device/att/devices.rs b/src/underscore_device/att/devices.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/src/underscore_device/att/structure.rs b/src/underscore_device/att/structure.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/src/underscore_device/devices.rs b/src/underscore_device/devices.rs new file mode 100644 index 00000000..6ce254b7 --- /dev/null +++ b/src/underscore_device/devices.rs @@ -0,0 +1,105 @@ +use std::{collections::HashMap, sync::Arc}; + +use super::pack::InfoPack; +use panduza_platform_core::{log_trace, Error, Instance}; +use serde_json::json; +use tokio::sync::Mutex; + +/// +/// +/// +pub async fn mount(mut instance: Instance, pack: InfoPack) -> Result<(), Error> { + // + // Get logger + let logger = instance.logger.clone(); + + // Each device have an attribute to share its state + // This Map hold those attribute, the name of the device is the key. + // instance_attributes: Arc>>, + let instance_attributes = Arc::new(Mutex::new(HashMap::new())); + + // + // state of each devices + let mut interface_devices = instance.create_class("devices").finish(); + + // I need to spawn a task to watch if a device status has changed, if yes update + // It is a better design to create a task that will always live here + let pack_clone2 = pack.clone(); + let instance_attributes_clone = instance_attributes.clone(); + instance + .spawn(async move { + // + // Clone the notifier from info pack + let device_status_change = pack_clone2.instance_status_change_notifier(); + + // + loop { + // + // Wait for next status change + device_status_change.notified().await; + log_trace!(logger, "status change notification"); + + let pack_status = pack_clone2.pack_instance_status(); + log_trace!(logger, "{:?}", pack_status); + + let mut lock = instance_attributes_clone.lock().await; + for status in pack_status { + if !lock.contains_key(&status.0) { + let att = interface_devices + .create_attribute(status.0.clone()) + .with_ro() + .finish_as_json() + .await?; + + lock.insert(status.0.clone(), att); + } + + lock.get_mut(&status.0) + .unwrap() + .set(json!({ + "state": status.1.to_string(), + "alerts": status.2 + })) + .await?; + } + drop(lock); + } + // Ok(()) + }) + .await; + + // // + // // Structure of the devices + // let structure_att = instance + // .create_attribute("structure") + // .with_ro() + // .finish_as_json() + // .await?; + + // let pack_clone3 = pack.clone(); + // instance + // .spawn(async move { + // // + // // + // let structure_change = pack_clone3.instance_structure_change_notifier().await; + // // let pack_clone4 = pack_clone3.clone(); + + // loop { + // // + // // Wait for next status change + // structure_change.notified().await; + // log_trace!(logger, "structure change notification"); + + // let structure = pack_clone3.device_structure_as_json_value().await.unwrap(); + // log_trace!(logger, "new structure {:?}", structure); + + // structure_att.set(structure).await.unwrap(); + // } + // // Ok(()) + // }) + // .await; + + // + // + Ok(()) +} diff --git a/src/underscore_device/scanner.rs b/src/underscore_device/scanner.rs index 856cf345..f3d76f49 100644 --- a/src/underscore_device/scanner.rs +++ b/src/underscore_device/scanner.rs @@ -2,9 +2,7 @@ pub mod data; use data::ScannerDriver; use panduza_platform_core::log_debug; -use panduza_platform_core::{ - spawn_on_command, BooleanAttServer, DeviceLogger, Instance, Error, -}; +use panduza_platform_core::{spawn_on_command, BooleanAttServer, Error, Instance, InstanceLogger}; /// /// Mount the scanner attribute @@ -46,7 +44,7 @@ pub async fn mount(mut instance: Instance, driver: ScannerDriver) -> Result<(), /// /// async fn on_running_command( - logger: DeviceLogger, + logger: InstanceLogger, mut att_running: BooleanAttServer, mut driver: ScannerDriver, ) -> Result<(), Error> { diff --git a/src/underscore_device/scanner/data.rs b/src/underscore_device/scanner/data.rs index 8398a92d..bb4e691a 100644 --- a/src/underscore_device/scanner/data.rs +++ b/src/underscore_device/scanner/data.rs @@ -1,7 +1,7 @@ -use panduza_platform_core::Error; -use panduza_platform_core::ProductionOrder; -use panduza_platform_core::Store; -use serde_json::Value as JsonValue; +// use panduza_platform_core::Error; +// use panduza_platform_core::ProductionOrder; +// use panduza_platform_core::Store; +// use serde_json::Value as JsonValue; use std::sync::Arc; use tokio::sync::Mutex; use tokio::sync::Notify; @@ -19,13 +19,12 @@ pub struct ScannerDriver { /// /// When something new happened from platform /// - pub update_notifier: Arc, + // pub update_notifier: Arc, // pub is_running: Arc>, - // - pub found_instances: Arc>>, + // pub found_instances: Arc>>, } impl ScannerDriver { @@ -35,9 +34,9 @@ impl ScannerDriver { pub fn new() -> Self { Self { request_notifier: Arc::new(Notify::new()), - update_notifier: Arc::new(Notify::new()), + // update_notifier: Arc::new(Notify::new()), is_running: Arc::new(Mutex::new(false)), - found_instances: Arc::new(Mutex::new(Vec::new())), + // found_instances: Arc::new(Mutex::new(Vec::new())), } } diff --git a/src/underscore_device/structure.rs b/src/underscore_device/structure.rs index c3b7bbd2..ae11ff59 100644 --- a/src/underscore_device/structure.rs +++ b/src/underscore_device/structure.rs @@ -3,10 +3,12 @@ pub mod class; pub mod instance; use instance::{Alert, InstanceElement}; -use panduza_platform_core::instance::State; +use panduza_platform_core::{instance::State, log_trace, Error, Instance}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; +use super::pack::InfoPack; + /// /// Structure that represent the json maintained in '_/structure' /// @@ -63,3 +65,47 @@ impl Structure { r } } + +/// +/// +/// +pub async fn mount(mut instance: Instance, pack: InfoPack) -> Result<(), Error> { + // + // Get logger + let logger = instance.logger.clone(); + + // + // Structure of the devices + let structure_att = instance + .create_attribute("structure") + .with_ro() + .finish_as_json() + .await?; + + let pack_clone3 = pack.clone(); + instance + .spawn(async move { + // + // + let structure_change = pack_clone3.instance_structure_change_notifier().await; + // let pack_clone4 = pack_clone3.clone(); + + loop { + // + // Wait for next status change + structure_change.notified().await; + log_trace!(logger, "structure change notification"); + + let structure = pack_clone3.device_structure_as_json_value().await.unwrap(); + log_trace!(logger, "new structure {:?}", structure); + + structure_att.set(structure).await.unwrap(); + } + // Ok(()) + }) + .await; + + // + // + Ok(()) +} diff --git a/src/underscore_device/structure/class.rs b/src/underscore_device/structure/class.rs index 32bde504..8681ddad 100644 --- a/src/underscore_device/structure/class.rs +++ b/src/underscore_device/structure/class.rs @@ -64,7 +64,6 @@ impl ClassElement { pub fn get_mut_class_from_layers(&mut self, layers: Vec) -> Option<&mut ClassElement> { // // low level debug - println!("class::get_mut_class_from_layers({:?})", layers); if layers.len() == 1 { let name = layers.first().unwrap(); diff --git a/src/underscore_device/structure/instance.rs b/src/underscore_device/structure/instance.rs index e112d735..7fe75fa1 100644 --- a/src/underscore_device/structure/instance.rs +++ b/src/underscore_device/structure/instance.rs @@ -87,7 +87,6 @@ impl InstanceElement { pub fn get_mut_class_from_layers(&mut self, layers: &Vec) -> Option<&mut ClassElement> { // // low level debug - println!("instance::get_mut_class_from_layers({:?})", layers); if layers.len() == 1 { let name = layers.first().unwrap();