node_info api rework
authorSven Rademakers <sven.rademakers@gmail.com>
Fri, 2 Feb 2024 17:18:26 +0000 (17:18 +0000)
committerSven Rademakers <sven.rademakers@gmail.com>
Fri, 2 Feb 2024 17:18:26 +0000 (17:18 +0000)
Changed the data format and implementation of the node_info
functionality.

Cargo.lock
Cargo.toml
src/api/legacy.rs
src/app/bmc_application.rs
src/hal.rs

index 10cae59f603c70367745bd27ba9a761d598602ad..9531d2b59753f381c0263491bdcd6e52d83fb7a1 100644 (file)
@@ -44,6 +44,21 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "actix-cors"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9e772b3bcafe335042b5db010ab7c09013dad6eac4915c91d8d50902769f331"
+dependencies = [
+ "actix-utils",
+ "actix-web",
+ "derive_more",
+ "futures-util",
+ "log",
+ "once_cell",
+ "smallvec",
+]
+
 [[package]]
 name = "actix-files"
 version = "0.6.5"
@@ -552,6 +567,7 @@ name = "bmcd"
 version = "2.0.5"
 dependencies = [
  "actix",
+ "actix-cors",
  "actix-files",
  "actix-multipart",
  "actix-web",
index b3d773a2f2d3dfea195d10577c29dd80cfd1617c..542eccaae179db28bf76fd07d1591a23a5d7574b 100644 (file)
@@ -8,6 +8,7 @@ rust-version = "1.70.0"
 
 [dependencies]
 actix = "0.13.1"
+actix-cors = "0.7.0"
 actix-files = "0.6.5"
 actix-multipart = "0.6.1"
 actix-web = { version = "4.4.1", features = ["openssl"] }
index dbcf9d3b5211e4dee82fb55ec49cb4d61e1626f4..e79f1ee863d3c1bf602ac08b98e31f06563fe33a 100644 (file)
@@ -14,6 +14,7 @@
 //! Routes for legacy API present in versions <= 2.0.0 of the firmware.
 use crate::api::into_legacy_response::LegacyResponse;
 use crate::api::into_legacy_response::{LegacyResult, Null};
+use crate::app::bmc_application::NodeInfo;
 use crate::app::bmc_application::{BmcApplication, UsbConfig};
 use crate::app::bmc_info::{
     get_fs_stat, get_ipv4_address, get_mac_address, get_net_interfaces, get_storage_info,
@@ -25,7 +26,6 @@ use crate::serial_service::serial::SerialConnections;
 use crate::serial_service::{legacy_serial_get_handler, legacy_serial_set_handler};
 use crate::streaming_data_service::data_transfer::DataTransfer;
 use crate::streaming_data_service::StreamingDataService;
-use crate::utils;
 use actix_files::file_extension_to_mime;
 use actix_multipart::Multipart;
 use actix_web::guard::{fn_guard, GuardContext};
@@ -280,69 +280,17 @@ fn get_node_info(_bmc: &BmcApplication) -> impl Into<LegacyResponse> {
        }]
     }
 }
-
-#[derive(Debug, serde::Deserialize)]
-struct SetNodeInfos {
-    node_info: Vec<SetNodeInfo>,
-}
-
-#[derive(Debug, Clone, serde::Deserialize)]
-pub struct SetNodeInfo {
-    pub node: u8,
-    pub name: Option<String>,
-    pub module_name: Option<String>,
-    pub uart_baud: Option<u32>,
-}
-
 async fn set_node_aux_info(
     bmc: web::Data<BmcApplication>,
-    payload: web::Json<SetNodeInfos>,
+    payload: web::Json<HashMap<NodeId, NodeInfo>>,
 ) -> impl Responder {
-    for info in &payload.node_info {
-        bmc.set_node_info(info.clone()).await?;
-    }
-
+    bmc.set_node_info(payload.into_inner()).await?;
     Ok::<Null, LegacyResponse>(Null)
 }
 
-async fn get_node_aux_info(bmc: &BmcApplication) -> impl Into<LegacyResponse> {
-    let Some(current_time) = utils::get_timestamp_unix() else {
-        anyhow::bail!("Current time before Unix epoch");
-    };
-
-    let infos = bmc.get_node_infos().await;
-
-    let mut output = json!(
-        {
-            "node_info": []
-        }
-    );
-
-    let array = output["node_info"].as_array_mut().expect("key mismatch");
-
-    for (i, n) in infos.data.iter().enumerate() {
-        let insert_idx = array.len();
-        let key = (i + 1).to_string();
-        let uptime = match n.power_on_time {
-            Some(powered_on) => current_time - powered_on,
-            None => 0,
-        };
-
-        let node_info = json!(
-            {
-                key: {
-                    "name": n.name,
-                    "module_name": n.module_name,
-                    "uptime": uptime,
-                    "uart_baud": n.uart_baud,
-                }
-            }
-        );
-
-        array.insert(insert_idx, node_info);
-    }
-
-    Ok(output)
+async fn get_node_aux_info(bmc: &BmcApplication) -> LegacyResult<serde_json::Value> {
+    let infos = bmc.get_node_infos().await?;
+    Ok(serde_json::to_value(infos)?)
 }
 
 async fn set_node_to_msd(bmc: &BmcApplication, query: Query) -> LegacyResult<()> {
@@ -679,3 +627,24 @@ async fn return_transfer_error(ss: web::Data<StreamingDataService>) -> impl Into
         msg.unwrap_or("transfer canceled".to_string()),
     )
 }
+
+#[cfg(test)]
+mod test {
+
+    use super::*;
+
+    #[actix_web::test]
+    async fn test_node_info() {
+        let json = serde_json::json! {
+            {
+                "Node1": {
+                    "module_name": "Raspberry Pi CM4"
+                },
+                "Node3": {
+                    "name": "New jeston"
+                }
+            }
+        };
+        let _: HashMap<NodeId, NodeInfo> = serde_json::from_value(json).unwrap();
+    }
+}
index 5e5225a07e1c8fda1f9f6eac129c3fedaff63e8c..40c4247a5a75aa476e80e75cc3e41ea5f3c7015d 100644 (file)
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 // limitations under the License.
-use crate::api::legacy::SetNodeInfo;
 use crate::hal::helpers::bit_iterator;
 use crate::hal::PowerController;
 use crate::hal::{NodeId, PinController, UsbMode, UsbRoute};
 use crate::persistency::app_persistency::ApplicationPersistency;
 use crate::persistency::app_persistency::PersistencyBuilder;
 use crate::usb_boot::NodeDrivers;
-use crate::utils::get_timestamp_unix;
+use crate::utils::{self, get_timestamp_unix};
 use crate::{
     app::usb_gadget::append_msd_config_to_usb_gadget,
     app::usb_gadget::remove_msd_function_from_usb_gadget,
@@ -28,11 +27,14 @@ use anyhow::{ensure, Context};
 use log::info;
 use log::{debug, trace};
 use serde::{Deserialize, Serialize};
+use std::collections::HashMap;
 use std::path::PathBuf;
 use std::process::Command;
 use std::time::Duration;
 use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite};
 
+pub type NodeInfos = [NodeInfo; 4];
+
 /// Stores which slots are actually used. This information is used to determine
 /// for instance, which nodes need to be powered on, when such command is given
 pub const ACTIVATED_NODES_KEY: &str = "activated_nodes";
@@ -54,34 +56,12 @@ pub enum UsbConfig {
     Flashing(NodeId, UsbRoute),
 }
 
-#[derive(Debug, serde::Serialize, serde::Deserialize)]
-pub struct NodeInfos {
-    pub data: Vec<NodeInfo>,
-}
-
 #[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
 pub struct NodeInfo {
-    pub name: String,
-    pub module_name: String,
+    pub name: Option<String>,
+    pub module_name: Option<String>,
     pub power_on_time: Option<u64>,
-    pub uart_baud: u32,
-}
-
-impl Default for NodeInfos {
-    fn default() -> Self {
-        Self {
-            data: vec![NodeInfo::new(); 4],
-        }
-    }
-}
-
-impl NodeInfo {
-    fn new() -> Self {
-        Self {
-            uart_baud: 115_200,
-            ..Default::default()
-        }
-    }
+    pub uart_baud: Option<u32>,
 }
 
 pub struct BmcApplication {
@@ -202,7 +182,7 @@ impl BmcApplication {
         for (idx, new_state) in bit_iterator(node_states, mask) {
             let current_state = activated_nodes & (1 << idx);
             let current_time = get_timestamp_unix();
-            let node_info = &mut node_infos.data[idx];
+            let node_info = &mut node_infos[idx];
 
             if new_state != current_state {
                 if new_state == 1 {
@@ -316,33 +296,44 @@ impl BmcApplication {
         Ok(())
     }
 
-    pub async fn set_node_info(&self, info: SetNodeInfo) -> anyhow::Result<()> {
-        ensure!(info.node >= 1 && info.node <= 4);
+    pub async fn set_node_info(&self, new_info: HashMap<NodeId, NodeInfo>) -> anyhow::Result<()> {
+        let mut stored_nodes = self.app_db.get::<NodeInfos>(NODE_INFO_KEY).await;
 
-        let node_idx = info.node - 1;
-        let mut node_infos = self.app_db.get::<NodeInfos>(NODE_INFO_KEY).await;
-        let node_info = &mut node_infos.data[usize::from(node_idx)];
+        for (i, info) in &mut new_info.into_iter().map(|(k, v)| (k as usize, v)) {
+            let store_node = &mut stored_nodes[i];
 
-        if let Some(name) = info.name {
-            node_info.name = name;
-        }
+            if let Some(name) = info.name {
+                store_node.name = Some(name);
+            }
 
-        if let Some(module_name) = info.module_name {
-            node_info.module_name = module_name;
-        }
+            if let Some(module_name) = info.module_name {
+                store_node.module_name = Some(module_name);
+            }
 
-        if let Some(uart_baud) = info.uart_baud {
-            node_info.uart_baud = uart_baud;
+            if let Some(uart_baud) = info.uart_baud {
+                store_node.uart_baud = Some(uart_baud);
+            }
         }
 
         self.app_db
-            .set::<NodeInfos>(NODE_INFO_KEY, node_infos)
+            .set::<NodeInfos>(NODE_INFO_KEY, stored_nodes)
             .await;
 
         Ok(())
     }
 
-    pub async fn get_node_infos(&self) -> NodeInfos {
-        self.app_db.get::<NodeInfos>(NODE_INFO_KEY).await
+    pub async fn get_node_infos(&self) -> anyhow::Result<NodeInfos> {
+        let Some(current_time) = utils::get_timestamp_unix() else {
+            anyhow::bail!("Current time before Unix epoch");
+        };
+
+        let mut node_infos = self.app_db.get::<NodeInfos>(NODE_INFO_KEY).await;
+        node_infos.iter_mut().for_each(|info| {
+            if let Some(time) = &mut info.power_on_time {
+                *time = current_time - *time;
+            }
+        });
+
+        Ok(node_infos)
     }
 }
index b4f111f8fc0611cecc9569cbb178a6753c2d91ab..796ffa3b1add84dfcd27cdbc8633cb57bfedba8d 100644 (file)
@@ -39,7 +39,7 @@ conditional_import! {
 }
 
 #[repr(C)]
-#[derive(Debug, Eq, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize)]
+#[derive(Debug, Eq, Hash, PartialEq, Clone, Copy, serde::Serialize, serde::Deserialize)]
 pub enum NodeId {
     Node1,
     Node2,