Просмотр исходного кода

dnetview: major refactor of Model/ underlying data structure

we create 3 types of info objects, NodeInfo, ConnectInfo, and
SessionInfo. NodeInfo is the parent and ConnectInfo and SessionInfo are children.

These info objects are passed into an enum:

enum SelectableObject {
    Node(NodeId)
    Session(SessionId)
    Connect(ConnectId)
}

We protect this data using mutexes and write it to a hashmap:

Mutex<FxHashMap<u32, SelectableObject>

View has not yet been updated with this new design.
lunar-mining 4 лет назад
Родитель
Сommit
53f9ba6b2c
4 измененных файлов с 245 добавлено и 229 удалено
  1. 2 2
      bin/dnetview/src/lib.rs
  2. 143 83
      bin/dnetview/src/main.rs
  3. 58 102
      bin/dnetview/src/model.rs
  4. 42 42
      bin/dnetview/src/ui.rs

+ 2 - 2
bin/dnetview/src/lib.rs

@@ -5,7 +5,7 @@ pub mod ui;
 pub mod view;
 
 pub use config::{DnvConfig, CONFIG_FILE_CONTENTS};
-pub use model::{IdList, InfoList, Model, NodeInfo};
+//pub use model::Model;
 pub use options::ProgramOptions;
 pub use ui::ui;
-pub use view::{IdListView, InfoListView, View};
+//pub use view::{IdListView, InfoListView, View};

+ 143 - 83
bin/dnetview/src/main.rs

@@ -1,9 +1,10 @@
-use async_std::sync::Arc;
+use async_std::sync::{Arc, Mutex};
 use std::{fs::File, io, io::Read, path::PathBuf};
 
 use easy_parallel::Parallel;
 use fxhash::{FxHashMap, FxHashSet};
 use log::{debug, info};
+use rand::{thread_rng, Rng};
 use serde_json::{json, Value};
 use simplelog::*;
 use smol::Executor;
@@ -27,14 +28,10 @@ use darkfi::{
 
 use dnetview::{
     config::{DnvConfig, CONFIG_FILE_CONTENTS},
-    model::{
-        Channel, ConnectInfo, IdList, InboundInfo, InfoList, ManualInfo, NodeInfo, OutboundInfo,
-        SelectableObject, SessionInfo, Slot,
-    },
+    model::{ConnectInfo, Model, NodeInfo, SelectableObject, SessionInfo},
     options::ProgramOptions,
     ui,
-    view::{IdListView, InfoListView},
-    Model, View,
+    view::{IdListView, InfoListView, View},
 };
 
 struct DNetView {
@@ -110,11 +107,12 @@ async fn main() -> Result<()> {
 
     terminal.clear()?;
 
-    let info_list = InfoList::new();
-    let ids = FxHashSet::default();
-    let id_list = IdList::new(ids);
+    let id_set = Mutex::new(FxHashSet::default());
+    let node_info = Mutex::new(FxHashMap::default());
+    let session_info = Mutex::new(FxHashMap::default());
+    let connect_info = Mutex::new(FxHashMap::default());
 
-    let model = Arc::new(Model::new(id_list, info_list));
+    let model = Arc::new(Model::new(id_set, node_info, session_info, connect_info));
 
     let nthreads = num_cpus::get();
     let (signal, shutdown) = async_channel::unbounded::<()>();
@@ -158,131 +156,193 @@ async fn poll(client: DNetView, model: Arc<Model>) -> Result<()> {
     }
 }
 
+// TODO: split into parse maunal/ inbound/ outbound functions
+// make if/else into switch statements for clarity
 async fn parse_data(
     reply: &serde_json::Map<String, Value>,
     client: &DNetView,
     model: Arc<Model>,
 ) -> io::Result<()> {
-    // TODO: we are ignoring this value for now
+    // TODO
     let _ext_addr = reply.get("external_addr");
 
     let inbound_obj = &reply["session_inbound"];
+    // TODO
     let manual_obj = &reply["session_manual"];
     let outbound_obj = &reply["session_outbound"];
 
-    // vectors we will copy the data to
     let mut model_vec: Vec<SelectableObject> = Vec::new();
-    let mut iconnects = Vec::new();
-    let mut mconnects = Vec::new();
-    let mut oconnects = Vec::new();
-    let mut slots = Vec::new();
-    let mut addrs = Vec::new();
-    let mut msgs = Vec::new();
-
-    // parse inbound connection data
+    let connections: Vec<ConnectInfo> = Vec::new();
+    let sessions: Vec<SessionInfo> = Vec::new();
+
+    let node_id = generate_id();
+    let node_name = &client.name;
+
+    parse_inbound(inbound_obj, connections.clone(), sessions.clone(), model_vec.clone(), node_id);
+    parse_outbound(outbound_obj, connections.clone(), sessions.clone(), model_vec.clone(), node_id);
+    parse_manual(manual_obj, connections.clone(), sessions.clone(), model_vec.clone(), node_id);
+
+    let node_info = NodeInfo::new(node_id, node_name.to_string(), sessions);
+    let node = SelectableObject::Node(node_info.clone());
+    model_vec.push(node);
+
+    // TODO: write data to HashMaps and HashSets
+    Ok(())
+}
+
+fn parse_inbound(
+    inbound_obj: &Value,
+    mut connections: Vec<ConnectInfo>,
+    mut sessions: Vec<SessionInfo>,
+    mut model_vec: Vec<SelectableObject>,
+    node_id: u32,
+) {
     let i_connected = &inbound_obj["connected"];
+    let i_session_id = generate_id();
     if i_connected.as_object().unwrap().is_empty() {
         // channel is empty. initialize with empty values
-        let connected = "Empty".to_string();
+        let i_connect_id = generate_id();
+        let addr = "Null".to_string();
         let msg = "Null".to_string();
         let status = "Null".to_string();
-        let channel = Channel::new(msg, status);
         let is_empty = true;
-        let iinfo = InboundInfo::new(is_empty, connected, channel);
-        iconnects.push(iinfo);
+        let parent = i_session_id;
+        // TODO
+        let state = "Null".to_string();
+        // TODO
+        let msg_log = Vec::new();
+        let connect_info =
+            ConnectInfo::new(i_connect_id, addr, is_empty, msg, status, state, msg_log, parent);
+        connections.push(connect_info.clone());
+        let connect = SelectableObject::Connect(connect_info.clone());
+        model_vec.push(connect);
     } else {
         // channel is not empty. initialize with whole values
+        let i_connect_id = generate_id();
         let ic = i_connected.as_object().unwrap();
         for k in ic.keys() {
             let node = ic.get(k);
             let addr = k.to_string();
             let msg = node.unwrap().get("last_msg").unwrap().as_str().unwrap().to_string();
             let status = node.unwrap().get("last_status").unwrap().as_str().unwrap().to_string();
-            let channel = Channel::new(msg.clone(), status);
+            let state = node.unwrap().get("state").unwrap().as_str().unwrap().to_string();
             let is_empty = false;
-            let iinfo = InboundInfo::new(is_empty, addr.clone(), channel);
-            iconnects.push(iinfo);
-            addrs.push(addr);
-            msgs.push(msg.clone());
+            let parent = i_session_id;
+            // TODO
+            let msg_log = Vec::new();
+            let connect_info =
+                ConnectInfo::new(i_connect_id, addr, is_empty, msg, status, state, msg_log, parent);
+            connections.push(connect_info.clone());
+            let connect = SelectableObject::Connect(connect_info.clone());
+            model_vec.push(connect);
         }
     }
+    let i_session_info = SessionInfo::new(i_session_id, node_id, connections.clone());
+    sessions.push(i_session_info.clone());
+    let session = SelectableObject::Session(i_session_info.clone());
+    model_vec.push(session);
+}
 
-    // parse manual connection data
-    let minfo: ManualInfo = serde_json::from_value(manual_obj.clone())?;
-    mconnects.push(minfo);
+fn parse_manual(
+    manual_obj: &Value,
+    mut connections: Vec<ConnectInfo>,
+    mut sessions: Vec<SessionInfo>,
+    mut model_vec: Vec<SelectableObject>,
+    node_id: u32,
+) {
+    let m_session_id = generate_id();
+    let m_connect_id = generate_id();
+    let addr = "Null".to_string();
+    let msg = "Null".to_string();
+    let status = "Null".to_string();
+    let is_empty = true;
+    let parent = m_session_id;
+    // TODO
+    let state = "Null".to_string();
+    // TODO
+    let msg_log = Vec::new();
+    let m_connect_info =
+        ConnectInfo::new(m_connect_id, addr, is_empty, msg, status, state, msg_log, parent);
+    connections.push(m_connect_info.clone());
+    let connect = SelectableObject::Connect(m_connect_info.clone());
+    model_vec.push(connect);
+}
 
+fn parse_outbound(
+    outbound_obj: &Value,
+    mut connections: Vec<ConnectInfo>,
+    mut sessions: Vec<SessionInfo>,
+    mut model_vec: Vec<SelectableObject>,
+    node_id: u32,
+) {
     // parse outbound connection data
     let outbound_slots = &outbound_obj["slots"];
+    let o_session_id = generate_id();
     for slot in outbound_slots.as_array().unwrap() {
+        let o_connect_id = generate_id();
         if slot["channel"].is_null() {
             // channel is empty. initialize with empty values
             let is_empty = true;
+            let addr = "Null".to_string();
             let state = &slot["state"];
             let msg = "Null".to_string();
             let status = "Null".to_string();
-            let channel = Channel::new(msg, status);
-            let new_slot =
-                Slot::new(is_empty, String::new(), channel, state.as_str().unwrap().to_string());
-            slots.push(new_slot.clone())
+            // placeholder for now
+            let msg_log = Vec::new();
+            let parent = o_session_id;
+            let connect_info = ConnectInfo::new(
+                o_connect_id,
+                addr,
+                is_empty,
+                msg,
+                status,
+                state.as_str().unwrap().to_string(),
+                msg_log,
+                parent,
+            );
+            connections.push(connect_info.clone());
+            let connect = SelectableObject::Connect(connect_info.clone());
+            model_vec.push(connect);
         } else {
+            // TODO: cleanup/ make style consistent
             // channel is not empty. initialize with whole values
             let is_empty = false;
             let addr = &slot["addr"];
             let state = &slot["state"];
-            let channel: Channel = serde_json::from_value(slot["channel"].clone())?;
-            let new_slot = Slot::new(
-                is_empty,
+            let msg = &slot["last_msg"];
+            let status = &slot["last_status"];
+            let parent = o_session_id;
+            // TODO
+            let msg_log = Vec::new();
+            let connect_info = ConnectInfo::new(
+                o_connect_id,
                 addr.as_str().unwrap().to_string(),
-                channel.clone(),
+                is_empty,
+                msg.as_str().unwrap().to_string(),
+                status.as_str().unwrap().to_string(),
                 state.as_str().unwrap().to_string(),
+                msg_log,
+                parent,
             );
-            slots.push(new_slot);
-            addrs.push(addr.as_str().unwrap().to_string());
-            msgs.push(channel.last_msg.clone());
+            connections.push(connect_info.clone());
+            let connect = SelectableObject::Connect(connect_info.clone());
+            model_vec.push(connect);
         }
     }
-
-    // assign each selectable a random ID
-    // push data to Info<Hashmap<ID, Info>>
-
-    // create node_info
-    let is_empty = is_empty_outbound(slots.clone());
-    let oinfo = OutboundInfo::new(is_empty, slots.clone());
-    oconnects.push(oinfo);
-
-    let infos = NodeInfo { outbound: oconnects, manual: mconnects, inbound: iconnects };
-    let node = SelectableObject::Node(infos.clone());
-    model_vec.push(node);
-
-    // NodeInfo = Hashmap<NodeId, NodeInfo>
-    let session_infos = SessionInfo::new();
-    // SessionInfo = Hashmap<SessionId, SessionInfo>
-    let session = SelectableObject::Session(session_infos);
+    let o_session_info = SessionInfo::new(o_session_id, node_id, connections.clone());
+    sessions.push(o_session_info.clone());
+    let session = SelectableObject::Session(o_session_info.clone());
     model_vec.push(session);
-
-    // ConnectInfo = Hashmap<ConnectId, ConnectInfo>
-    let connect_infos = ConnectInfo::new();
-    let connect = SelectableObject::Connect(connect_infos);
-    model_vec.push(connect);
-
-    let mut node_info = FxHashMap::default();
-    let node_name = &client.name.as_str();
-    node_info.insert(&node_name, infos.clone());
-
-    debug!("INDEX OF MODEL VEC {}", model_vec.len());
-
-    // insert into model
-    for (key, value) in node_info.clone() {
-        model.id_list.node_id.lock().await.insert(key.to_string().clone());
-        // value
-        model.info_list.infos.lock().await.insert(key.to_string(), value);
-    }
-    Ok(())
 }
 
-fn is_empty_outbound(slots: Vec<Slot>) -> bool {
-    return slots.iter().all(|slot| slot.is_empty)
+fn generate_id() -> u32 {
+    let mut rng = thread_rng();
+    let id: u32 = rng.gen();
+    id
 }
+//fn is_empty_outbound(slots: Vec<Slot>) -> bool {
+//    return slots.iter().all(|slot| slot.is_empty);
+//}
 
 async fn render<B: Backend>(terminal: &mut Terminal<B>, model: Arc<Model>) -> io::Result<()> {
     let mut asi = async_stdin();
@@ -297,7 +357,7 @@ async fn render<B: Backend>(terminal: &mut Terminal<B>, model: Arc<Model>) -> io
     view.info_list.index = 0;
 
     loop {
-        view.update(model.info_list.infos.lock().await.clone());
+        //view.update(model.info_list.infos.lock().await.clone());
         terminal.draw(|f| {
             ui::ui(f, view.clone());
         })?;

+ 58 - 102
bin/dnetview/src/model.rs

@@ -6,36 +6,40 @@ use tui::widgets::ListState;
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
 pub enum SelectableObject {
-    // top level selectable object
     Node(NodeInfo),
-    // outgoing or incoming sessions
     Session(SessionInfo),
-    // connections: outgoing, incoming or manual
     Connect(ConnectInfo),
 }
 
 pub struct Model {
-    pub id_list: IdList,
-    pub info_list: InfoList,
+    pub id_set: Mutex<FxHashSet<u32>>,
+    pub node_info: Mutex<FxHashMap<u32, SelectableObject>>,
+    pub session_info: Mutex<FxHashMap<u32, SelectableObject>>,
+    pub connect_info: Mutex<FxHashMap<u32, SelectableObject>>,
 }
 
 impl Model {
-    pub fn new(id_list: IdList, info_list: InfoList) -> Model {
-        Model { id_list, info_list }
+    pub fn new(
+        id_set: Mutex<FxHashSet<u32>>,
+        node_info: Mutex<FxHashMap<u32, SelectableObject>>,
+        session_info: Mutex<FxHashMap<u32, SelectableObject>>,
+        connect_info: Mutex<FxHashMap<u32, SelectableObject>>,
+    ) -> Model {
+        Model { id_set, node_info, session_info, connect_info }
     }
 }
 
-pub struct IdList {
-    pub state: Mutex<ListState>,
-    pub node_id: Mutex<FxHashSet<String>>,
-}
-
-impl IdList {
-    pub fn new(node_id: FxHashSet<String>) -> IdList {
-        let node_id = Mutex::new(node_id);
-        IdList { state: Mutex::new(ListState::default()), node_id }
-    }
-}
+//pub struct IdList {
+//    pub state: Mutex<ListState>,
+//    pub node_id: Mutex<FxHashSet<String>>,
+//}
+//
+//impl IdList {
+//    pub fn new(node_id: FxHashSet<String>) -> IdList {
+//        let node_id = Mutex::new(node_id);
+//        IdList { state: Mutex::new(ListState::default()), node_id }
+//    }
+//}
 
 pub struct InfoList {
     pub index: Mutex<usize>,
@@ -58,107 +62,59 @@ impl Default for InfoList {
     }
 }
 
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+type NodeId = u32;
+type SessionId = u32;
+type ConnectId = u32;
+
+#[derive(Clone, Deserialize, Debug, PartialEq, Eq, Hash)]
 pub struct NodeInfo {
-    pub outbound: Vec<OutboundInfo>,
-    pub manual: Vec<ManualInfo>,
-    pub inbound: Vec<InboundInfo>,
+    pub node_id: NodeId,
+    pub node_name: String,
+    pub children: Vec<SessionInfo>,
 }
 
 impl NodeInfo {
-    pub fn new() -> NodeInfo {
-        NodeInfo { outbound: Vec::new(), manual: Vec::new(), inbound: Vec::new() }
-    }
-}
-
-impl Default for NodeInfo {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-
-#[derive(Clone, Debug, PartialEq, Deserialize, Eq, Hash)]
-pub struct ManualInfo {
-    pub key: u64,
-}
-
-impl ManualInfo {
-    pub fn new(key: u64) -> ManualInfo {
-        ManualInfo { key }
-    }
-}
-
-#[derive(Clone, Debug, PartialEq, Deserialize, Eq, Hash)]
-pub struct OutboundInfo {
-    pub is_empty: bool,
-    pub slots: Vec<Slot>,
-}
-
-impl OutboundInfo {
-    pub fn new(is_empty: bool, slots: Vec<Slot>) -> OutboundInfo {
-        OutboundInfo { is_empty, slots }
-    }
-}
-
-#[derive(Clone, Debug, PartialEq, Deserialize, Eq, Hash)]
-pub struct Slot {
-    pub is_empty: bool,
-    pub addr: String,
-    pub channel: Channel,
-    pub state: String,
-}
-
-impl Slot {
-    pub fn new(is_empty: bool, addr: String, channel: Channel, state: String) -> Slot {
-        Slot { is_empty, addr, channel, state }
-    }
-}
-
-#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Hash)]
-pub struct Channel {
-    pub last_msg: String,
-    pub last_status: String,
-}
-
-impl Channel {
-    pub fn new(last_msg: String, last_status: String) -> Channel {
-        Channel { last_msg, last_status }
+    pub fn new(node_id: NodeId, node_name: String, children: Vec<SessionInfo>) -> NodeInfo {
+        NodeInfo { node_id, node_name, children }
     }
 }
 
 #[derive(Clone, Deserialize, Debug, PartialEq, Eq, Hash)]
-pub struct InboundInfo {
-    pub is_empty: bool,
-    pub connected: String,
-    pub channel: Channel,
+pub struct SessionInfo {
+    pub session_id: SessionId,
+    pub parent: NodeId,
+    pub children: Vec<ConnectInfo>,
 }
 
-impl InboundInfo {
-    pub fn new(is_empty: bool, connected: String, channel: Channel) -> InboundInfo {
-        InboundInfo { is_empty, connected, channel }
+impl SessionInfo {
+    pub fn new(session_id: SessionId, parent: NodeId, children: Vec<ConnectInfo>) -> SessionInfo {
+        SessionInfo { session_id, parent, children }
     }
 }
 
 #[derive(Clone, Deserialize, Debug, PartialEq, Eq, Hash)]
 pub struct ConnectInfo {
-    pub message: String,
+    pub connect_id: ConnectId,
+    pub addr: String,
+    pub is_empty: bool,
+    pub last_msg: String,
+    pub last_status: String,
+    pub state: String,
+    pub msg_log: Vec<String>,
+    pub parent: SessionId,
 }
 
 impl ConnectInfo {
-    pub fn new() -> ConnectInfo {
-        let message = "This is the connect screen".to_string();
-        ConnectInfo { message }
-    }
-}
-
-#[derive(Clone, Deserialize, Debug, PartialEq, Eq, Hash)]
-pub struct SessionInfo {
-    pub message: String,
-}
-
-impl SessionInfo {
-    pub fn new() -> SessionInfo {
-        let message = "This is the connect screen".to_string();
-        SessionInfo { message }
+    pub fn new(
+        connect_id: ConnectId,
+        addr: String,
+        is_empty: bool,
+        last_msg: String,
+        last_status: String,
+        state: String,
+        msg_log: Vec<String>,
+        parent: SessionId,
+    ) -> ConnectInfo {
+        ConnectInfo { connect_id, addr, is_empty, last_msg, last_status, state, msg_log, parent }
     }
 }

+ 42 - 42
bin/dnetview/src/ui.rs

@@ -27,48 +27,48 @@ pub fn ui<B: Backend>(f: &mut Frame<'_, B>, mut view: View) {
         // render as a sub node
         match &view.info_list.infos.get(id) {
             Some(node) => {
-                if !node.outbound.iter().all(|node| node.is_empty) {
-                    lines.push(Spans::from(Span::styled("   Outgoing", Style::default())));
-                }
-                for outbound in &node.outbound.clone() {
-                    for slot in outbound.slots.clone() {
-                        let addr = Span::styled(format!("       {}", slot.addr), style);
-                        let msg: Span = match slot.channel.last_status.as_str() {
-                            "recv" => Span::styled(
-                                format!("               [R: {}]", slot.channel.last_msg),
-                                style,
-                            ),
-                            "sent" => Span::styled(
-                                format!("               [S: {}]", slot.channel.last_msg),
-                                style,
-                            ),
-                            a => Span::styled(a.to_string(), style),
-                        };
-                        lines.push(Spans::from(vec![addr, msg]));
-                    }
-                }
-                if !node.inbound.iter().all(|node| node.is_empty) {
-                    lines.push(Spans::from(Span::styled("   Incoming", Style::default())));
-                }
-                for inbound in &node.inbound {
-                    let addr = Span::styled(format!("       {}", inbound.connected), style);
-                    let msg: Span = match inbound.channel.last_status.as_str() {
-                        "recv" => Span::styled(
-                            format!("               [R: {}]", inbound.channel.last_msg),
-                            style,
-                        ),
-                        "sent" => Span::styled(
-                            format!("               [R: {}]", inbound.channel.last_msg),
-                            style,
-                        ),
-                        a => Span::styled(a.to_string(), style),
-                    };
-                    lines.push(Spans::from(vec![addr, msg]));
-                }
-                lines.push(Spans::from(Span::styled("   Manual", Style::default())));
-                for connect in &node.manual {
-                    lines.push(Spans::from(Span::styled(format!("       {}", connect.key), style)));
-                }
+                //if !node.outbound.iter().all(|node| node.is_empty) {
+                //    lines.push(Spans::from(Span::styled("   Outgoing", Style::default())));
+                //}
+                //for outbound in &node.outbound.clone() {
+                //    for slot in outbound.slots.clone() {
+                //        let addr = Span::styled(format!("       {}", slot.addr), style);
+                //        let msg: Span = match slot.channel.last_status.as_str() {
+                //            "recv" => Span::styled(
+                //                format!("               [R: {}]", slot.channel.last_msg),
+                //                style,
+                //            ),
+                //            "sent" => Span::styled(
+                //                format!("               [S: {}]", slot.channel.last_msg),
+                //                style,
+                //            ),
+                //            a => Span::styled(a.to_string(), style),
+                //        };
+                //        lines.push(Spans::from(vec![addr, msg]));
+                //    }
+                //}
+                //if !node.inbound.iter().all(|node| node.is_empty) {
+                //    lines.push(Spans::from(Span::styled("   Incoming", Style::default())));
+                //}
+                //for inbound in &node.inbound {
+                //    let addr = Span::styled(format!("       {}", inbound.connected), style);
+                //    let msg: Span = match inbound.channel.last_status.as_str() {
+                //        "recv" => Span::styled(
+                //            format!("               [R: {}]", inbound.channel.last_msg),
+                //            style,
+                //        ),
+                //        "sent" => Span::styled(
+                //            format!("               [R: {}]", inbound.channel.last_msg),
+                //            style,
+                //        ),
+                //        a => Span::styled(a.to_string(), style),
+                //    };
+                //    lines.push(Spans::from(vec![addr, msg]));
+                //}
+                //lines.push(Spans::from(Span::styled("   Manual", Style::default())));
+                //for connect in &node.manual {
+                //    lines.push(Spans::from(Span::styled(format!("       {}", connect.key), style)));
+                //}
             }
             None => {
                 // TODO