1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
// Copyright (c) 2021 Open Community Project Association https://ocpa.ch
// This software is published under the AGPLv3 license.

//! # Router module functions

use prost::Message;
use super::rpc::Rpc;

/// include generated protobuf RPC rust definition file
mod proto { include!("../../../libqaul/src/rpc/protobuf_generated/rust/qaul.rpc.router.rs"); }

/// router module function handling
pub struct Router {}

impl Router {
    /// CLI command interpretation
    /// 
    /// The CLI commands of router module are processed here
    pub fn cli(command: &str) {
        match command {
            // request routing table,
            // with per module connectivity per user.
            cmd if cmd.starts_with("table list") => {
                Self::request_routing_table();
            },
            // request neighbours list of all neighbouring nodes.
            cmd if cmd.starts_with("neighbours list") => {
                Self::request_neighbours_list();
            },
            // request connections table, with all known connections 
            // per connection module.
            cmd if cmd.starts_with("connections list") => {
                Self::request_connections_list();
            },
            // unknown command
            _ => log::error!("unknown router command"),
        }
    }

    /// create rpc request for routing table list
    fn request_routing_table() {
        // create request message
        let proto_message = proto::Router {
            message: Some(proto::router::Message::RoutingTableRequest (
                proto::RoutingTableRequest {}
            )),
        };

        // send message
        Self::send_message(proto_message);
    }

    /// create rpc request for neighbours list
    fn request_neighbours_list() {
        // create request message
        let proto_message = proto::Router {
            message: Some(proto::router::Message::NeighboursRequest (
                proto::NeighboursRequest {}
            )),
        };

        // send message
        Self::send_message(proto_message);
    }

    /// create rpc request for connections list
    fn request_connections_list() {
        // create request message
        let proto_message = proto::Router {
            message: Some(proto::router::Message::ConnectionsRequest (
                proto::ConnectionsRequest {}
            )),
        };

        // send message
        Self::send_message(proto_message);
    }

    /// Encode and send protobuf message
    fn send_message(message: proto::Router) {
        // encode message
        let mut buf = Vec::with_capacity(message.encoded_len());
        message.encode(&mut buf).expect("Vec<u8> provides capacity as needed");

        // send message
        Rpc::send_message(buf, super::rpc::proto::Modules::Router.into(), "".to_string());
    }

    /// Process received RPC message
    /// 
    /// Decodes received protobuf encoded binary RPC message
    /// of the router module.
    pub fn rpc(data: Vec<u8>) {
        match proto::Router::decode(&data[..]) {
            Ok(router) => {
                match router.message {
                    Some(proto::router::Message::RoutingTable(proto_message)) => {
                        println!("");
                        println!("Routing Table");
                        println!("No. | User ID");
                        println!("      * Connection Module | RTT in ms | hop count | Via Neighbour Node Id");
        
                        let mut line = 1;

                        // loop through all routing table entries
                        for entry in proto_message.routing_table {
                            // print routing table entry header
                            println!("{} | {}", line, bs58::encode(entry.user_id).into_string());

                            // loop through all connection entries
                            for connection in &entry.connections {
                                // get node id as string
                                let via = bs58::encode(connection.via.clone());

                                // get enum name as string
                                let module = match proto::ConnectionModule::from_i32(connection.module) {
                                    Some(proto::ConnectionModule::None) => "None",
                                    Some(proto::ConnectionModule::Lan) => "Lan",
                                    Some(proto::ConnectionModule::Internet) => "Internet",
                                    Some(proto::ConnectionModule::Ble) => "Ble",
                                    Some(proto::ConnectionModule::Local) => "Local",
                                    None => "Unknown",
                                };

                                // print connection entry
                                println!("      * {} | {} | {} | {}", module, connection.rtt, connection.hop_count, via.into_string());
                            }

                            line += 1;
                        }
                        println!("");
                    },
                    Some(proto::router::Message::NeighboursList(proto_message)) => {
                        println!("");
                        println!("Neighbours List - All Nodes that are Direct Neighbours");
                        println!("");
                        
                        println!("LAN Neighbours");
                        Self::rpc_display_neighbours_list(proto_message.lan);

                        println!("Internet Neighbours");
                        Self::rpc_display_neighbours_list(proto_message.internet);
                    },
                    Some(proto::router::Message::ConnectionsList(proto_message)) => {
                        println!("");
                        println!("Connections List - All Connections of this Node");
                        println!("");

                        println!("LAN Connections");
                        Self::rpc_display_connections_list(proto_message.lan);

                        println!("Internet Connections");
                        Self::rpc_display_connections_list(proto_message.internet);
                    },
                    _ => {
                        log::error!("unprocessable RPC router message");
                    },
                }    
            },
            Err(error) => {
                log::error!("{:?}", error);
            },
        }
    }

    /// Display Neighbours list
    fn rpc_display_neighbours_list(neighbours_list: Vec<proto::NeighboursEntry>) {
        for entry in neighbours_list {
            println!("{}, {} rtt", bs58::encode(entry.node_id).into_string(), entry.rtt);
        }
        println!("");
    }

    /// Display Connection per Module
    fn rpc_display_connections_list(connections_list: Vec<proto::ConnectionsUserEntry>) {
        println!("No. | User ID");
        println!("      * RTT in ms | hop count | Via Neighbour Node Id");

        let mut line = 1;

        for entry in connections_list {
            println!("{} | {:?}", line, bs58::encode(entry.user_id).into_string());
            // loop through all neighbour entries of a user entry
            for connection in entry.connections {
                println!("      * {} | {} | {:?}", connection.rtt, connection.hop_count, bs58::encode(connection.via).into_string());
            }
            line += 1;
        }
        println!("");
    }
}