diff --git a/Cargo.toml b/Cargo.toml index 4398a793..c056eb87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,14 +6,13 @@ edition = "2021" [dependencies] -# 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.0", features = [ +# 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.0", features = [ +# "log", +# ] } # Main async framework for the platform tokio = { version = "1.40.0", features = ["full", "tracing"] } @@ -38,7 +37,7 @@ async-trait = "0.1.77" futures = "0.3.17" -serde = "1.0.0" +serde = { "version" = "1.0.0", features = ["derive"] } # Json serialization & deserialization serde_json = "1.0.114" diff --git a/src/__builtin_devices/korad/ka3005/itf_ammeter.rs b/src/__builtin_devices/korad/ka3005/itf_ammeter.rs deleted file mode 100644 index 38406ed5..00000000 --- a/src/__builtin_devices/korad/ka3005/itf_ammeter.rs +++ /dev/null @@ -1,68 +0,0 @@ -use async_trait::async_trait; -use panduza_core::Error as PlatformError; -use panduza_core::interface::AmInterface; -use panduza_core::interface::builder::Builder as InterfaceBuilder; - -use panduza_core::meta::ammeter; - -// use panduza_connectors::serial::tty::Tty; -use panduza_connectors::serial::tty2::{self, TtyConnector}; -use panduza_connectors::serial::tty2::Config as SerialConfig; -// use crate::platform_error_result; - -/// -/// -struct Ka3005pAmmeterActions { - connector_tty: TtyConnector, - serial_config: SerialConfig -} - -#[async_trait] -impl ammeter::AmmeterActions for Ka3005pAmmeterActions { - - /// Initialize the interface - /// - async fn initializating(&mut self, _interface: &AmInterface) -> Result<(), PlatformError> { - self.connector_tty = tty2::get(&self.serial_config).await.unwrap(); - self.connector_tty.init().await; - - return Ok(()); - } - - async fn read_measure_value(&mut self, interface: &AmInterface) -> Result { - - let mut response: &mut [u8] = &mut [0; 1024]; - let _result = self.connector_tty.write_then_read( - b"VOUT1?", - &mut response, - ).await; - - let value = String::from_utf8(response.to_vec()).unwrap().parse::().expect("bad measure"); - - interface.lock().await.log_info( - format!("KA3005 - read_amage_value: {}", value) - ); - return Ok(value); - } -} - - - -/// Interface to emulate a Bench Power Channel -/// -pub fn build>( - name: A, - serial_config: &SerialConfig -) -> InterfaceBuilder { - - return ammeter::build( - name, - ammeter::AmmeterParams { - measure_decimals: 2 - }, - Box::new(Ka3005pAmmeterActions { - connector_tty: TtyConnector::new(None), - serial_config: serial_config.clone() - }) - ) -} diff --git a/src/__builtin_devices/korad/ka3005/itf_bpc.rs b/src/__builtin_devices/korad/ka3005/itf_bpc.rs deleted file mode 100644 index 143a3e2b..00000000 --- a/src/__builtin_devices/korad/ka3005/itf_bpc.rs +++ /dev/null @@ -1,188 +0,0 @@ -use async_trait::async_trait; -use panduza_core::FunctionResult as PlatformFunctionResult; -use panduza_core::Error as PlatformError; -use panduza_core::meta::bpc::{self, BpcAttributes}; -use panduza_core::interface::AmInterface; -use panduza_core::interface::builder::Builder as InterfaceBuilder; - - -// use panduza_connectors::serial::tty::Tty; -use panduza_connectors::serial::tty2::{self, TtyConnector}; -use panduza_connectors::serial::tty2::Config as SerialConfig; -// use crate::platform_error_result; - -/// -/// -struct Ka3005pBpcActions { - connector_tty: TtyConnector, - serial_config: SerialConfig, - enable_value: bool, - voltage_value: f64, - current_value: f64, - time_lock_duration: Option, -} - -#[async_trait] -impl bpc::BpcActions for Ka3005pBpcActions { - - /// Initialize the interface - /// - async fn initializating(&mut self, _interface: &AmInterface) -> PlatformFunctionResult { - - self.connector_tty = tty2::get(&self.serial_config).await.unwrap(); - let _ = self.connector_tty.init().await; - - let mut response: &mut [u8] = &mut [0; 1024]; - let _result = self.connector_tty.write_then_read( - b"*IDN?", - &mut response - ).await - .map(|c| { - let pp = &response[0..c]; - let sss = String::from_utf8(pp.to_vec()).unwrap(); - println!("Ka3005pBpcActions - initializating: {:?}", sss); - }); - - - return Ok(()); - } - - /// Read the enable value - /// - async fn read_enable_value(&mut self, interface: &AmInterface) -> Result { - - let mut response: &mut [u8] = &mut [0; 1024]; - let _result = self.connector_tty.write_then_read( - b"STATUS?", - &mut response - ).await - .map(|c| { - println!("c: {:?}", c); - let pp = &response[0..c]; - if (pp[0] & (1 << 6)) == 0 { - self.enable_value = false; - } else { - self.enable_value = true; - } - }); - - interface.lock().await.log_info( - format!("KA3005 - read_enable_value: {}", self.enable_value) - ); - return Ok(self.enable_value); - } - - /// Write the enable value - /// - async fn write_enable_value(&mut self, interface: &AmInterface, v: bool) -> PlatformFunctionResult { - - let command = format!("OUT{}", if v { 1 } else { 0 }); - - let _result = self.connector_tty.write( - command.as_bytes(), - self.time_lock_duration - ).await; - - interface.lock().await.log_info( - format!("KA3005 - write_enable_value: {}", self.enable_value) - ); - - self.enable_value = v; - - Ok(()) - } - - async fn read_voltage_value(&mut self, interface: &AmInterface) -> Result { - - let mut response: &mut [u8] = &mut [0; 1024]; - let _result = self.connector_tty.write_then_read( - b"VSET1?", - &mut response - ).await; - - let value = String::from_utf8(response.to_vec()).unwrap().parse::().expect("bad measure"); - - interface.lock().await.log_info( - format!("KA3005 - read_voltage_value: {}", value) - ); - return Ok(value); - } - - async fn write_voltage_value(&mut self, interface: &AmInterface, v: f64) { - let command = format!("VSET1:{:05.2}", v); - - let _result = self.connector_tty.write( - command.as_bytes(), - self.time_lock_duration - ).await; - - interface.lock().await.log_warn( - format!("NOT IMPLEMENTED KA3005 - write_voltage_value: {}", v) - ); - - self.voltage_value = v; - } - - async fn read_current_value(&mut self, interface: &AmInterface) -> Result { - - let mut response: &mut [u8] = &mut [0; 1024]; - let _result = self.connector_tty.write_then_read( - b"ISET1?", - &mut response - ).await; - - let value = String::from_utf8(response.to_vec()).unwrap().parse::().expect("bad measure"); - - interface.lock().await.log_info( - format!("KA3005 - read_current_value: {}", value) - ); - return Ok(value); - } - - async fn write_current_value(&mut self, interface: &AmInterface, c: f64) { - let command = format!("ISET1:{:05.3}", c); - - let _result = self.connector_tty.write( - command.as_bytes(), - self.time_lock_duration - ).await; - - interface.lock().await.log_warn( - format!("NOT IMPLEMENTED KA3005 - write_current_value: {}", c) - ); - - self.current_value = c; - } -} - - - -/// Interface to emulate a Bench Power Channel -/// -pub fn build>( - name: A, - serial_config: &SerialConfig -) -> InterfaceBuilder { - - return bpc::build( - name, - bpc::BpcParams { - voltage_min: 0.0, - voltage_max: 30.0, - voltage_decimals: 2, - - current_min: 0.0, - current_max: 3.0, - current_decimals: 3, - }, - Box::new(Ka3005pBpcActions { - connector_tty: TtyConnector::new(None), - serial_config: serial_config.clone(), - enable_value: false, - voltage_value: 0.0, - current_value: 0.0, - time_lock_duration: Some(tokio::time::Duration::from_millis(100)), - }), - BpcAttributes::all_attributes() - ) -} diff --git a/src/__builtin_devices/korad/ka3005/itf_voltmeter.rs b/src/__builtin_devices/korad/ka3005/itf_voltmeter.rs deleted file mode 100644 index fce419fe..00000000 --- a/src/__builtin_devices/korad/ka3005/itf_voltmeter.rs +++ /dev/null @@ -1,68 +0,0 @@ -use async_trait::async_trait; -use panduza_core::Error as PlatformError; -use panduza_core::interface::AmInterface; -use panduza_core::interface::builder::Builder as InterfaceBuilder; - -use panduza_core::meta::voltmeter; - -// use panduza_connectors::serial::tty::Tty; -use panduza_connectors::serial::tty2::{self, TtyConnector}; -use panduza_connectors::serial::tty2::Config as SerialConfig; -// use crate::platform_error_result; - -/// -/// -struct Ka3005pVoltmeterActions { - connector_tty: TtyConnector, - serial_config: SerialConfig -} - -#[async_trait] -impl voltmeter::VoltmeterActions for Ka3005pVoltmeterActions { - - /// Initialize the interface - /// - async fn initializating(&mut self, _interface: &AmInterface) -> Result<(), PlatformError> { - self.connector_tty = tty2::get(&self.serial_config).await.unwrap(); - self.connector_tty.init().await; - - return Ok(()); - } - - async fn read_measure_value(&mut self, interface: &AmInterface) -> Result { - - let mut response: &mut [u8] = &mut [0; 1024]; - let _result = self.connector_tty.write_then_read( - b"VOUT1?", - &mut response, - ).await; - - let value = String::from_utf8(response.to_vec()).unwrap().parse::().expect("bad measure"); - - interface.lock().await.log_info( - format!("KA3005 - read_voltage_value: {}", value) - ); - return Ok(value); - } -} - - - -/// Interface to emulate a Bench Power Channel -/// -pub fn build>( - name: A, - serial_config: &SerialConfig -) -> InterfaceBuilder { - - return voltmeter::build( - name, - voltmeter::VoltmeterParams { - measure_decimals: 2 - }, - Box::new(Ka3005pVoltmeterActions { - connector_tty: TtyConnector::new(None), - serial_config: serial_config.clone() - }) - ) -} diff --git a/src/__builtin_devices/korad/ka3005/mod.rs b/src/__builtin_devices/korad/ka3005/mod.rs deleted file mode 100644 index e23a3057..00000000 --- a/src/__builtin_devices/korad/ka3005/mod.rs +++ /dev/null @@ -1,153 +0,0 @@ -use async_trait::async_trait; -use panduza_core::device::Device; -use serde_json::json; - -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::tty2::Config as SerialConfig; - -use tokio_serial; - -mod itf_bpc; -mod itf_voltmeter; -mod itf_ammeter; - - -static VID: u16 = 0x0416; -static PID: u16 = 0x5011; -static BAUDRATE: u32 = 115200; - -pub struct DeviceHunter; - - -#[async_trait] -impl Hunter for DeviceHunter { - - async fn hunt(&self) -> Option> { - - let mut bag = Vec::new(); - - // println!("DeviceHunter::hunt : Korad"); - - let ports = tokio_serial::available_ports(); - for port in ports.unwrap() { - // println!("{:?}", port); - - match port.port_type { - tokio_serial::SerialPortType::UsbPort(info) => { - if info.vid == VID && info.pid == PID { - println!("Found device : Korad"); - - bag.push(json!( - { - "name": "Korad KA3005", - "ref": "korad.ka3005", - "settings": { - "usb_vendor": format!("{:04x}", info.vid), - "usb_model": format!("{:04x}", info.pid), - "usb_serial": info.serial_number, - } - } - )) - } - }, - _ => {} - } - } - - if bag.is_empty() { - return None; - } - else { - return Some(bag); - } - } - -} - -struct Ka3005p; - -impl DeviceActions for Ka3005p { - - /// Create the interfaces - fn interface_builders(&self, device: &Device) - -> Result, PlatformError> - { - - let device_settings = &device.settings; - - println!("Ka3005p::interface_builders"); - println!("{}", device_settings); - - let mut serial_conf = SerialConfig::new(); - let port = device_settings["serial_port_name"].as_str().unwrap_or_else(|| { - panic!("serial_port_name is not defined"); - }).to_string(); - - - serial_conf.fill(SerialConfig { - serial_port_name: Some(port), - serial_baudrate: Some(BAUDRATE), - usb_model: Some(PID), - usb_vendor: Some(VID), - time_lock_duration: Some(tokio::time::Duration::from_millis(500)), - ..Default::default() - }); - - let mut list = Vec::new(); - list.push(itf_voltmeter::build("channel_0:_voltmeter", &serial_conf)); - list.push(itf_ammeter::build("channel_0:_ammeter", &serial_conf)); - list.push(itf_bpc::build("channel_0:_control", &serial_conf)); - return Ok(list); - } -} - - - - -pub struct DeviceProducer; - -impl Producer for DeviceProducer { - - // fn manufacturer(&self) -> String { - // return "korad".to_string(); - // } - // fn model(&self) -> String { - // return "KA3005".to_string(); - // } - - fn settings_props(&self) -> serde_json::Value { - return json!([ - { - "name": "usb_vendor", - "type": "string", - "default": format!("{:04x}", VID) - }, - { - "name": "usb_model", - "type": "string", - "default": format!("{:04x}", PID) - }, - { - "name": "usb_serial", - "type": "string", - "default": "" - }, - { - "name": "serial_port_name", - "type": "string", - "default": "" - } - ]); - } - - - fn produce(&self) -> Result, PlatformError> { - return Ok(Box::new(Ka3005p{})); - } - -} diff --git a/src/__builtin_devices/korad/mod.rs b/src/__builtin_devices/korad/mod.rs deleted file mode 100644 index b01746dd..00000000 --- a/src/__builtin_devices/korad/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -use panduza_core::device::Factory as DeviceFactory; - -mod ka3005; - -pub fn import_plugin_producers(factory: &mut DeviceFactory) -{ - factory.add_producer("korad.ka3005", Box::new(ka3005::DeviceProducer{})); - factory.add_hunter(Box::new(ka3005::DeviceHunter{})); -} - diff --git a/src/main.rs b/src/main.rs index ec7d4acd..65216809 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ -// #![deny( -// while_true, +#![deny( + while_true, // improper_ctypes, // non_shorthand_field_patterns, // no_mangle_generic_items, @@ -13,7 +13,7 @@ // unused_allocation, // unused_comparisons, // unused_parens, -// )] +)] mod platform; pub use platform::Platform; diff --git a/src/underscore_device.rs b/src/underscore_device.rs index 8c992bb6..7de08543 100644 --- a/src/underscore_device.rs +++ b/src/underscore_device.rs @@ -1,6 +1,6 @@ -pub mod element; pub mod pack; pub mod pack_inner; +pub mod structure; pub mod topic; use async_trait::async_trait; @@ -55,6 +55,12 @@ impl DeviceOperations for UnderscoreDevice { // state of each devices let mut interface_devices = device.create_interface("devices").finish(); + // store -> json with all the possible device that can be created + hunted instances found on the computer + // hunt -> interface to control a hunting session + // - running boolean + // - total_hunter number + // - joined_hunter number + // 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(); @@ -93,7 +99,8 @@ impl DeviceOperations for UnderscoreDevice { lock.get_mut(&status.0) .unwrap() .set(JsonCodec::from(json!({ - "state": status.1.to_string() + "state": status.1.to_string(), + "alerts": status.2 }))) .await?; } @@ -127,8 +134,8 @@ impl DeviceOperations for UnderscoreDevice { println!("$$$$$$$$$$ structure change ****"); - let structure = pack_clone3.device_structure_as_json_value().await; - // println!("structure {:?}", structure); + let structure = pack_clone3.device_structure_as_json_value().await.unwrap(); + println!("structure {:?}", structure); structure_att.set(JsonCodec::from(structure)).await.unwrap(); } diff --git a/src/underscore_device/element.rs b/src/underscore_device/element.rs deleted file mode 100644 index ff89667e..00000000 --- a/src/underscore_device/element.rs +++ /dev/null @@ -1,87 +0,0 @@ -pub mod attribute; -mod instance; -mod interface; - -pub use attribute::ElementAttribute; -pub use instance::InfoElementInstance; -pub use interface::ElementInterface; -use panduza_platform_core::{Error, StructuralNotification}; - -use super::Topic; - -/// -/// Element at the basis of Instance structure -/// -#[derive(Debug)] -pub enum InfoElement { - Instance(InfoElementInstance), - Attribute(ElementAttribute), - Interface(ElementInterface), -} - -impl InfoElement { - pub fn name(&self) -> &String { - match self { - InfoElement::Attribute(a) => &a.name(), - InfoElement::Interface(i) => &i.name(), - InfoElement::Instance(_info_element_instance) => todo!(), - } - } - - pub fn into_json_value(&self) -> serde_json::Value { - match self { - InfoElement::Attribute(a) => a.into_json_value(), - InfoElement::Interface(i) => i.into_json_value(), - InfoElement::Instance(inn) => inn.into_json_value(), - } - } - - // pub fn is_element_exist(&self, layers: Vec) -> Result { - // match self { - // InfoElement::Attribute(a) => a.is_element_exist(layers), - // InfoElement::Interface(i) => i.is_element_exist(layers), - // InfoElement::Instance(info_element_instance) => todo!(), - // } - // } - - /// - /// - /// - pub fn insert(&mut self, layers: Vec, element: InfoElement) -> Result<(), Error> { - // println!(">>>>>>> {:?} -- {:?}", layers, element); - match self { - InfoElement::Attribute(_) => Err(Error::InternalLogic( - "Cannot insert an element inside an Attribute".to_string(), - )), - InfoElement::Interface(interface) => interface.insert(layers, element), - InfoElement::Instance(_info_element_instance) => { - _info_element_instance.insert(layers, element) - } - } - } -} - -/// -/// -/// -impl From for InfoElement { - fn from(value: StructuralNotification) -> Self { - match value { - StructuralNotification::Attribute(attribute_notification) => { - let t = Topic::from_string(attribute_notification.name()); - InfoElement::Attribute(ElementAttribute::new( - t.last_layer(), - attribute_notification.typee(), - attribute_notification.mode().clone(), - )) - } - StructuralNotification::Interface(interface_notification) => { - let t = Topic::from_string(interface_notification.topic()); - InfoElement::Interface(ElementInterface::new( - t.last_layer(), - interface_notification.tags, - )) - } - } - } -} diff --git a/src/underscore_device/element/attribute.rs b/src/underscore_device/element/attribute.rs deleted file mode 100644 index 40bcd577..00000000 --- a/src/underscore_device/element/attribute.rs +++ /dev/null @@ -1,50 +0,0 @@ -use serde_json::json; - -use panduza_platform_core::AttributeMode; - -#[derive(Debug)] -pub struct ElementAttribute { - name: String, - typee: String, - mode: AttributeMode, -} - -impl ElementAttribute { - /// - /// - /// - pub fn new, T: Into>(name: N, typee: T, mode: AttributeMode) -> Self { - Self { - name: name.into(), - typee: typee.into(), - mode, - } - } - - pub fn into_json_value(&self) -> serde_json::Value { - json!({ - // "name": self.name, - "type": self.typee, - "mode": self.mode - }) - } - - /// - pub fn name(&self) -> &String { - &self.name - } - // /// - // pub fn typee(&self) -> &String { - // &self.typee - // } - // pub fn mode(&self) -> &AttributeMode { - // &self.mode - // } - - // /// - // /// Attribute does not hold any elements - // /// - // pub fn is_element_exist(&self, layers: Vec) -> Result { - // Ok(false) - // } -} diff --git a/src/underscore_device/element/instance.rs b/src/underscore_device/element/instance.rs deleted file mode 100644 index bfd012e3..00000000 --- a/src/underscore_device/element/instance.rs +++ /dev/null @@ -1,102 +0,0 @@ -use super::InfoElement; -use panduza_platform_core::{device::State, Error}; -use std::collections::HashMap; - -/// -/// Represent an instance in the structure -/// -#[derive(Debug)] -pub struct InfoElementInstance { - /// - /// Name of the instance - /// - pub name: String, - - /// - /// State of the instance - /// - pub state: State, - - /// - /// Children of the instance - /// - pub children: HashMap, -} - -impl InfoElementInstance { - /// - /// Create a new element - /// - pub fn new>(name: A) -> Self { - Self { - name: name.into(), - state: State::Booting, - children: HashMap::new(), - } - } - - /// - /// Define the state - /// - pub fn set_state(&mut self, new_state: State) { - self.state = new_state; - } - - /// - /// Convert the instance into a json format - /// - pub fn into_json_value(&self) -> serde_json::Value { - let mut p = serde_json::Map::new(); - - for (name, e) in &self.children { - p.insert(name.clone(), e.into_json_value()); - } - - p.into() - } - - /// - /// Insert a sub element inside this instance - /// - pub fn insert(&mut self, layers: Vec, element: InfoElement) -> Result<(), Error> { - if layers.len() == 1 { - // Insert HERE - // new element name = layers.get(0) - let _layer_name = match layers.get(0) { - Some(value) => { - self.children.insert(value.clone(), element); - } - None => { - // None - // TODO SO UGLY - return Err(Error::Generic("layer name not found 2".to_string())); - // cannot find the layer => error - } - }; - - // insert here - } else { - // Insert inside the sub layer - let layer_name = match layers.get(0) { - Some(value) => Some(value.to_string()), - None => { - None - // Err(Error::Generic("layer name not found".to_string())) - // cannot find the layer => error - } - }; - - match layer_name { - Some(n) => { - let mut new_layers = layers; - new_layers.remove(0); - let sublayer = self.children.get_mut(&n).unwrap(); - sublayer.insert(new_layers, element)?; - } - None => todo!(), - } - } - - Ok(()) - } -} diff --git a/src/underscore_device/element/interface.rs b/src/underscore_device/element/interface.rs deleted file mode 100644 index 692f9b47..00000000 --- a/src/underscore_device/element/interface.rs +++ /dev/null @@ -1,129 +0,0 @@ -use serde_json::json; - -use super::InfoElement; - -use panduza_platform_core::Error; - -/// -/// -/// -#[derive(Debug)] -pub struct ElementInterface { - name: String, - tags: Vec, - elements: Vec, -} - -impl ElementInterface { - pub fn new>(name: N, tags: Vec) -> Self { - Self { - name: name.into(), - tags, - elements: Vec::new(), - } - } - - pub fn into_json_value(&self) -> serde_json::Value { - // - let mut children = serde_json::Map::new(); - for e in &self.elements { - children.insert(e.name().clone(), e.into_json_value()); - } - - return json!({ - "tags": self.tags, - "children": children - }); - } - - // pub fn is_element_exist(&self, layers: Vec) -> Result { - // // TODO Control layers == 0 - - // if layers.len() == 1 { - // let name = layers.get(0).ok_or(Error::Wtf)?; - // for element in &self.elements { - // if element.name() == name { - // return Ok(true); - // } - // } - // return Ok(false); - // } else { - // let name = layers.get(0).ok_or(Error::Wtf)?; - // let sublayer = self.find_layer(&name); - - // let mut new_la = layers; - // new_la.remove(0); - // return sublayer.is_element_exist(new_la); - // } - // } - - /// - // pub fn tags(&self) -> &Vec { - // &self.tags - // } - - /// - pub fn name(&self) -> &String { - &self.name - } - - // pub fn find_layer(&self, name: &str) -> &InfoElement { - // self.elements - // .iter() - // .find(|element| element.name() == name) - // .unwrap_or_else(|| { - // panic!("Layer '{}' not found in device", name); - // }) - // } - - pub fn find_layer_mut(&mut self, name: &str) -> &mut InfoElement { - self.elements - .iter_mut() - .find(|element| element.name() == name) - .unwrap_or_else(|| { - panic!("Layer '{}' not found in device", name); - }) - } - - pub fn insert(&mut self, layers: Vec, element: InfoElement) -> Result<(), Error> { - if layers.len() == 1 { - // Insert HERE - // new element name = layers.get(0) - let _layer_name = match layers.get(0) { - Some(_value) => { - self.elements.push(element); - } - None => { - // None - // TODO SO UGLY - return Err(Error::Generic("layer name not found 2".to_string())); - // cannot find the layer => error - } - }; - - // insert here - } else { - // Insert inside the sub layer - let layer_name = match layers.get(0) { - Some(value) => Some(value.to_string()), - None => { - None - // Err(Error::Generic("layer name not found".to_string())) - // cannot find the layer => error - } - }; - - match layer_name { - Some(n) => { - let mut new_layers = layers; - new_layers.remove(0); - let sublayer = self.find_layer_mut(&n); - sublayer.insert(new_layers, element)?; - } - None => todo!(), - } - } - - Ok(()) - } -} diff --git a/src/underscore_device/pack.rs b/src/underscore_device/pack.rs index 795676dd..f86bd383 100644 --- a/src/underscore_device/pack.rs +++ b/src/underscore_device/pack.rs @@ -1,9 +1,9 @@ use std::sync::Arc; -use panduza_platform_core::{device::State, Notification}; +use panduza_platform_core::{device::State, Error, Notification}; use tokio::sync::Notify; -use super::pack_inner::InfoPackInner; +use super::{pack_inner::InfoPackInner, structure::instance::Alert}; #[derive(Clone)] pub struct InfoPack { @@ -24,7 +24,7 @@ impl InfoPack { } pub fn process_notifications(&mut self, notifications: Vec) { - for not in ¬ifications { + for not in notifications { match not { Notification::StateChanged(state_notification) => { // println!("state {:?}", state_notification); @@ -32,31 +32,29 @@ impl InfoPack { self.inner .lock() .unwrap() - .process_state_changed(state_notification); + .process_state_changed(&state_notification); } Notification::ElementCreated(structural_notification) => { - // println!("create {:?}", structural_notification); - self.inner .lock() .unwrap() - .process_element_creation(structural_notification); + .process_element_creation(structural_notification) + .unwrap(); } Notification::ElementDeleted(_structural_notification) => { // println!("deleted {:?}", structural_notification); } + Notification::Alert(alert_notification) => { + self.inner.lock().unwrap().process_alert(alert_notification); + } } } } - pub fn pack_instance_status(&self) -> Vec<(String, State)> { + pub fn pack_instance_status(&self) -> Vec<(String, State, Vec)> { self.inner.lock().unwrap().pack_instance_status() } - // pub fn devices(&self) -> Arc> { - // self.inner.clone() - // } - /// /// pub fn instance_status_change_notifier(&self) -> Arc { @@ -72,7 +70,7 @@ impl InfoPack { .instance_structure_change_notifier() } - pub async fn device_structure_as_json_value(&self) -> serde_json::Value { + pub async fn device_structure_as_json_value(&self) -> Result { self.inner.lock().unwrap().structure_into_json_value() } } diff --git a/src/underscore_device/pack_inner.rs b/src/underscore_device/pack_inner.rs index ed2d08dc..06531f58 100644 --- a/src/underscore_device/pack_inner.rs +++ b/src/underscore_device/pack_inner.rs @@ -1,18 +1,23 @@ +use super::{ + structure::{ + attribute::AttributElement, + instance::{Alert, InstanceElement}, + Structure, + }, + Topic, +}; +use crate::underscore_device::structure::class::ClassElement; +use panduza_platform_core::{ + device::State, AlertNotification, Error, StateNotification, StructuralNotification, +}; use std::sync::Arc; - -use crate::underscore_device::element::InfoElement; -use panduza_platform_core::{device::State, StateNotification, StructuralNotification}; -use std::collections::HashMap; use tokio::sync::Notify; -use super::{element::InfoElementInstance, Topic}; -// use panduza_platform_core - pub struct InfoPackInner { /// /// /// - instances: HashMap, + structure: Structure, /// /// Notified when a device status change @@ -30,12 +35,22 @@ impl InfoPackInner { /// pub fn new() -> InfoPackInner { InfoPackInner { - instances: HashMap::new(), + structure: Structure::default(), instance_status_change_notifier: Arc::new(Notify::new()), instance_structure_change_notifier: Arc::new(Notify::new()), } } + /// + /// Create a new instance if this instance does not already exist + /// + pub fn create_instance_if_not_exists(&mut self, instance_name: &String) { + if !self.structure.contains_instance(&instance_name) { + self.structure + .insert_instance(instance_name.clone(), InstanceElement::default()); + } + } + /// /// /// @@ -43,106 +58,131 @@ impl InfoPackInner { let topic = Topic::from_string(n.topic.clone()); // println!("{:?}", p.device); - let instance_name = topic.device; + let instance_name = &topic.instance; - // if the instance does not exist, create it - if !self.instances.contains_key(&instance_name) { - self.instances.insert( - instance_name.clone(), - InfoElement::Instance(InfoElementInstance::new(instance_name.clone())), - ); - } + // + // Create the instance if not already created + self.create_instance_if_not_exists(instance_name); - let instance = self.instances.get_mut(&instance_name).unwrap(); - match instance { - InfoElement::Instance(info_element_instance) => { - info_element_instance.set_state(n.state.clone()); - } - InfoElement::Attribute(_element_attribute) => todo!(), - InfoElement::Interface(_element_interface) => todo!(), - } + // + // Instance MUST now exist + let instance = self + .structure + .get_mut_instance(instance_name) + .ok_or(Error::Wtf) + .unwrap(); + + instance.set_state(n.state.clone()); self.instance_status_change_notifier.notify_waiters(); } /// - /// Process an element creation notification /// - pub fn process_element_creation(&mut self, n: &StructuralNotification) { - let topic = Topic::from_string(n.topic()); - - let instance_name = topic.device; - - if !self.instances.contains_key(&instance_name) { - self.instances.insert( - instance_name.clone(), - InfoElement::Instance(InfoElementInstance::new(instance_name.clone())), - ); - } - - let instance: &mut InfoElement = self.instances.get_mut(&instance_name).unwrap(); - - let o = InfoElement::from(n.clone()); + /// + pub fn process_alert(&mut self, n: AlertNotification) { + let topic = Topic::from_string(n.topic.clone()); + // println!("{:?}", p.device); - instance.insert(topic.layers, o).unwrap(); + let instance_name = &topic.instance; - // match n { - // StructuralNotification::Attribute(_attribute_notification) => { + // + // Create the instance if not already created + self.create_instance_if_not_exists(instance_name); - // } - // StructuralNotification::Interface(_interface_notification) => { - // let instance_name = topic.device; + // + // Instance MUST now exist + let instance = self + .structure + .get_mut_instance(instance_name) + .ok_or(Error::Wtf) + .unwrap(); - // if !self.instances.contains_key(&instance_name) { - // self.instances.insert( - // instance_name.clone(), - // InfoElement::Instance(InfoElementInstance::new(instance_name.clone())), - // ); - // } + instance.add_alert(n.into()); - // let instance: &mut InfoElement = self.instances.get_mut(&instance_name).unwrap(); + self.instance_status_change_notifier.notify_waiters(); + } - // let o = InfoElement::from(n.clone()); + /// + /// Process an element creation notification + /// + pub fn process_element_creation(&mut self, n: StructuralNotification) -> Result<(), Error> { + let topic = Topic::from_string(n.topic()); - // instance.insert(topic.layers, o).unwrap(); - // } - // } + let instance_name = &topic.instance; + + // + // Create the instance if not already created + self.create_instance_if_not_exists(instance_name); + + // + // Instance MUST now exist + let instance = self + .structure + .get_mut_instance(instance_name) + .ok_or(Error::Wtf) + .unwrap(); + + match n { + StructuralNotification::Attribute(_attribute_notification) => { + let new_attribute = AttributElement::from(_attribute_notification); + // + // You have to insert the element in the instance + if topic.layers_len() == 1 { + instance.insert_attribute(topic.first_layer().clone(), new_attribute); + } + // + // + else { + let mut layers = topic.layers.clone(); + // println!("---------- {:?}", layers); + layers.remove(layers.len() - 1); + // println!("---------- {:?}", layers); + let class = instance.get_mut_class_from_layers(&layers).unwrap(); + class.insert_attribute(topic.last_layer().clone(), new_attribute); + } + } + StructuralNotification::Interface(_interface_notification) => { + let new_class = ClassElement::from(_interface_notification); + // + // You have to insert the element in the instance + if topic.layers_len() == 1 { + instance.insert_class(topic.first_layer().clone(), new_class); + } + // + // + else { + let mut layers = topic.layers.clone(); + layers.remove(layers.len() - 1); + let class = + instance + .get_mut_class_from_layers(&layers) + .ok_or(Error::InternalLogic(format!( + "cannot find class from layer {:?}", + &layers + )))?; + class.insert_class(topic.last_layer().clone(), new_class); + } + } + } self.instance_structure_change_notifier.notify_waiters(); + + Ok(()) } /// /// - pub fn pack_instance_status(&self) -> Vec<(String, State)> { - let mut r = Vec::new(); - // instance name - // instance state - for (_key, value) in (&self.instances).into_iter() { - match value { - InfoElement::Instance(info_element_instance) => { - r.push(( - info_element_instance.name.clone(), - info_element_instance.state.clone(), - )); - } - InfoElement::Attribute(_element_attribute) => todo!(), - InfoElement::Interface(_element_interface) => todo!(), - } - } - r + pub fn pack_instance_status(&self) -> Vec<(String, State, Vec)> { + self.structure.pack_instance_status() } /// /// /// - pub fn structure_into_json_value(&self) -> serde_json::Value { - let mut p = serde_json::Map::new(); - - for (name, e) in &self.instances { - p.insert(name.clone(), e.into_json_value()); - } - - p.into() + pub fn structure_into_json_value(&self) -> Result { + serde_json::to_value(&self.structure) + .map_err(|e| Error::SerializeFailure(format!("{:?}", e))) } /// diff --git a/src/underscore_device/structure.rs b/src/underscore_device/structure.rs new file mode 100644 index 00000000..a69962d2 --- /dev/null +++ b/src/underscore_device/structure.rs @@ -0,0 +1,65 @@ +pub mod attribute; +pub mod class; +pub mod instance; + +use instance::{Alert, InstanceElement}; +use panduza_platform_core::device::State; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +/// +/// Structure that represent the json maintained in '_/structure' +/// +#[derive(Default, Debug, Serialize, Deserialize)] +pub struct Structure { + /// + /// Instances managed + /// + driver_instances: HashMap, + + /// + /// User information about the structure + /// + info: Option, +} + +impl Structure { + /// + /// + /// + pub fn insert_instance(&mut self, name: String, instance: InstanceElement) { + self.driver_instances.insert(name, instance); + } + + // /// + // /// + // /// + // pub fn insert_class(topic, class) { + + // } + + /// + /// + /// + pub fn contains_instance(&mut self, name: &String) -> bool { + self.driver_instances.contains_key(name) + } + + /// + /// + /// + pub fn get_mut_instance(&mut self, name: &String) -> Option<&mut InstanceElement> { + self.driver_instances.get_mut(name) + } + + /// + /// + /// + pub fn pack_instance_status(&self) -> Vec<(String, State, Vec)> { + let mut r = Vec::new(); + for (_key, value) in (&self.driver_instances).into_iter() { + r.push((_key.clone(), value.state.clone(), value.alerts.clone())); + } + r + } +} diff --git a/src/underscore_device/structure/attribute.rs b/src/underscore_device/structure/attribute.rs new file mode 100644 index 00000000..d4ab95be --- /dev/null +++ b/src/underscore_device/structure/attribute.rs @@ -0,0 +1,65 @@ +use panduza_platform_core::{AttributeMode, AttributeNotification}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +/// +/// Attribute element in the structure representation +/// +pub struct AttributElement { + /// + /// Type of the attribute + /// + #[serde(rename = "type")] + // allow serde to convert this member into the good name "type" because + // type is protected keyword in rust + typee: String, + + /// + /// Mode of the attribute + /// + mode: AttributeMode, + + /// + /// User information about the structure + /// + info: Option, + + /// + /// Settings of the attribute + /// + settings: Option, +} + +impl AttributElement { + /// + /// + /// + pub fn new>( + typee: T, + mode: AttributeMode, + info: Option, + settings: Option, + ) -> Self { + Self { + typee: typee.into(), + mode, + info, + settings, + } + } +} + +/// +/// +/// +impl From for AttributElement { + fn from(notif: AttributeNotification) -> Self { + // TODO: here notif members should be moved ! + AttributElement::new( + notif.typee(), + notif.mode().clone(), + notif.info().clone(), + notif.settings().clone(), + ) + } +} diff --git a/src/underscore_device/structure/class.rs b/src/underscore_device/structure/class.rs new file mode 100644 index 00000000..32bde504 --- /dev/null +++ b/src/underscore_device/structure/class.rs @@ -0,0 +1,93 @@ +use super::attribute::AttributElement; +use panduza_platform_core::InterfaceNotification; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +// use panduza_platform_core::not + +/// +/// +/// +#[derive(Debug, Serialize, Deserialize)] +pub struct ClassElement { + /// + /// + /// + tags: Vec, + + /// + /// Sub classes + /// + pub classes: HashMap, + + /// + /// Sub attributes + /// + pub attributes: HashMap, + + /// + /// User information about the structure + /// + info: Option, +} + +impl ClassElement { + /// + /// Constructor + /// + pub fn new(tags: Vec, info: Option) -> Self { + Self { + tags, + classes: HashMap::default(), + attributes: HashMap::default(), + info: info, + } + } + + /// + /// + /// + pub fn insert_class(&mut self, name: String, class: ClassElement) { + self.classes.insert(name, class); + } + + /// + /// + /// + pub fn insert_attribute(&mut self, name: String, attribute: AttributElement) { + self.attributes.insert(name, attribute); + } + + /// + /// + /// + 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(); + self.classes.get_mut(name) + } else if layers.len() > 1 { + let name = layers.first().unwrap(); + let mut sub_layers = layers.clone(); + sub_layers.remove(0); + self.classes + .get_mut(name) + .unwrap() + .get_mut_class_from_layers(sub_layers) + } else { + None + } + } +} + +/// +/// +/// +impl From for ClassElement { + fn from(notif: InterfaceNotification) -> Self { + ClassElement::new(notif.tags, None) + } +} diff --git a/src/underscore_device/structure/instance.rs b/src/underscore_device/structure/instance.rs new file mode 100644 index 00000000..6497f861 --- /dev/null +++ b/src/underscore_device/structure/instance.rs @@ -0,0 +1,107 @@ +use panduza_platform_core::{device::State, AlertNotification}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +use super::{attribute::AttributElement, class::ClassElement}; + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct Alert { + topic: String, + message: String, +} + +impl From for Alert { + fn from(value: AlertNotification) -> Self { + Self { + topic: value.topic, + message: value.message, + } + } +} + +/// +/// Represent an instance in the structure +/// +#[derive(Default, Debug, Serialize, Deserialize)] +pub struct InstanceElement { + /// + /// State of the instance + /// + #[serde(skip)] + pub state: State, + + /// + /// State of the instance + /// + #[serde(skip)] + pub alerts: Vec, + + /// + /// Sub classes + /// + pub classes: HashMap, + + /// + /// Sub attributes + /// + pub attributes: HashMap, + + /// + /// User information about the structure + /// + info: Option, +} + +impl InstanceElement { + /// + /// Define the state + /// + pub fn set_state(&mut self, new_state: State) { + self.state = new_state; + } + + /// + /// + /// + pub fn add_alert(&mut self, alert: Alert) { + self.alerts.push(alert); + } + + /// + /// + /// + pub fn insert_class(&mut self, name: String, class: ClassElement) { + self.classes.insert(name, class); + } + + /// + /// + /// + pub fn insert_attribute(&mut self, name: String, attribute: AttributElement) { + self.attributes.insert(name, attribute); + } + + /// + /// Get a class from its layers, it means that it will dig to find a sub class if needed + /// + 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(); + self.classes.get_mut(name) + } else if layers.len() > 1 { + let name = layers.first().unwrap(); + let mut sub_layers = layers.clone(); + sub_layers.remove(0); + self.classes + .get_mut(name) + .unwrap() + .get_mut_class_from_layers(sub_layers) + } else { + None + } + } +} diff --git a/src/underscore_device/topic.rs b/src/underscore_device/topic.rs index c071997f..1b69d65d 100644 --- a/src/underscore_device/topic.rs +++ b/src/underscore_device/topic.rs @@ -4,8 +4,7 @@ #[derive(Debug)] pub struct Topic { pub _namespace: String, - pub _host: String, - pub device: String, + pub instance: String, pub layers: Vec, } @@ -35,17 +34,23 @@ impl Topic { // // let namespace = namespace_parts.join("/"); - let host = layers.remove(0).to_string(); let device = layers.remove(0).to_string(); Self { _namespace: namespace, - _host: host, - device, + instance: device, layers: layers.into_iter().map(|l| l.to_string()).collect(), } } + pub fn layers_len(&self) -> usize { + self.layers.len() + } + + pub fn first_layer(&self) -> String { + self.layers.first().unwrap().clone() + } + pub fn last_layer(&self) -> String { self.layers.last().unwrap().clone() }