Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
Dunitrust
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container Registry
Model registry
Operate
Environments
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
This is an archived project. Repository and other project resources are read-only.
Show more breadcrumbs
nodes
rust
Dunitrust
Commits
f1e6e70e
Commit
f1e6e70e
authored
5 years ago
by
Éloïs
Browse files
Options
Downloads
Patches
Plain Diff
[fix] whole: project must be compile on windows
parent
3dfede1b
No related branches found
Branches containing commit
No related tags found
1 merge request
!259
[fix] whole: project must be compile on windows
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
Cargo.lock
+0
-1
0 additions, 1 deletion
Cargo.lock
lib/modules/tui/Cargo.toml
+4
-3
4 additions, 3 deletions
lib/modules/tui/Cargo.toml
lib/modules/tui/lib.rs
+4
-636
4 additions, 636 deletions
lib/modules/tui/lib.rs
lib/modules/tui/tui.rs
+651
-0
651 additions, 0 deletions
lib/modules/tui/tui.rs
with
659 additions
and
640 deletions
Cargo.lock
+
0
−
1
View file @
f1e6e70e
...
@@ -1194,7 +1194,6 @@ dependencies = [
...
@@ -1194,7 +1194,6 @@ dependencies = [
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"structopt 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"termion 1.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
"termion 1.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
...
...
This diff is collapsed.
Click to expand it.
lib/modules/tui/Cargo.toml
+
4
−
3
View file @
f1e6e70e
...
@@ -21,11 +21,12 @@ durs-network = { path = "../../core/network" }
...
@@ -21,11 +21,12 @@ durs-network = { path = "../../core/network" }
durs-network-documents
=
{
path
=
"../../dunp/network-documents"
}
durs-network-documents
=
{
path
=
"../../dunp/network-documents"
}
failure
=
"0.1.5"
failure
=
"0.1.5"
log
=
"0.4.*"
log
=
"0.4.*"
serde
=
"1.0.*"
serde
=
{
version
=
"1.0.*"
,
features
=
[
"derive"
]
}
serde_derive
=
"1.0.*"
serde_json
=
"1.0.*"
serde_json
=
"1.0.*"
structopt
=
"0.3.4"
structopt
=
"0.3.4"
termion
=
"1.5.2"
unwrap
=
"1.2.1"
unwrap
=
"1.2.1"
[target.'cfg(unix)'.dependencies]
termion
=
"1.5.2"
[features]
[features]
This diff is collapsed.
Click to expand it.
lib/modules/tui/lib.rs
+
4
−
636
View file @
f1e6e70e
...
@@ -16,640 +16,8 @@
...
@@ -16,640 +16,8 @@
//! Defined the few global types used by all modules,
//! Defined the few global types used by all modules,
//! as well as the DursModule trait that all modules must implement.
//! as well as the DursModule trait that all modules must implement.
#![deny(
#[cfg(unix)]
clippy::option_unwrap_used,
pub
mod
tui
;
clippy::result_unwrap_used,
missing_docs,
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications
)]
#[macro_use]
#[cfg(unix)]
extern
crate
log
;
pub
use
tui
::
TuiModule
;
#[macro_use]
extern
crate
serde_derive
;
#[macro_use]
extern
crate
structopt
;
use
dubp_currency_params
::
CurrencyName
;
use
durs_common_tools
::
fatal_error
;
use
durs_common_tools
::
traits
::
merge
::
Merge
;
use
durs_conf
::
DuRsConf
;
use
durs_message
::
events
::
*
;
use
durs_message
::
*
;
use
durs_module
::
*
;
use
durs_network
::
events
::
NetworkEvent
;
use
durs_network_documents
::
network_head
::
NetworkHead
;
use
durs_network_documents
::
NodeFullId
;
use
std
::
collections
::
HashMap
;
use
std
::
io
::{
stdout
,
Write
};
use
std
::
ops
::
Deref
;
use
std
::
sync
::
mpsc
::{
channel
,
Receiver
,
RecvTimeoutError
,
Sender
};
use
std
::
thread
;
use
std
::
time
::{
Duration
,
SystemTime
};
use
termion
::
event
::
*
;
use
termion
::
input
::{
MouseTerminal
,
TermRead
};
use
termion
::
raw
::{
IntoRawMode
,
RawTerminal
};
use
termion
::{
clear
,
color
,
cursor
,
style
};
use
unwrap
::
unwrap
;
#[derive(Debug,
Copy,
Clone,
PartialEq,
Eq,
Hash,
Serialize,
Deserialize)]
/// Tui Module Configuration (For future use)
pub
struct
TuiConf
{}
impl
Default
for
TuiConf
{
fn
default
()
->
Self
{
TuiConf
{}
}
}
impl
Merge
for
TuiConf
{
fn
merge
(
self
,
_
:
Self
)
->
Self
{
self
}
}
#[derive(Debug,
Clone)]
/// Format of messages received by the tui module
pub
enum
TuiMess
{
/// Message from another module
DursMsg
(
Box
<
DursMsg
>
),
/// Message from stdin (user event)
TermionEvent
(
Event
),
}
#[derive(Debug,
Copy,
Clone)]
/// Tui module
pub
struct
TuiModule
{}
#[derive(StructOpt,
Debug,
Copy,
Clone)]
#[structopt(name
=
"tui"
,
setting(structopt::clap::AppSettings::ColoredHelp))]
/// Tui subcommand options
pub
struct
TuiOpt
{}
#[derive(Debug,
Clone)]
/// Network connexion (data to display)
pub
struct
Connection
{
/// Connexion status
status
:
u32
,
/// Endpoint url
url
:
String
,
/// Node uid at the other end of the connection (member nodes only)
uid
:
Option
<
String
>
,
}
#[derive(Debug,
Clone)]
/// Data that the Tui module needs to cache
pub
struct
TuiModuleDatas
{
/// Sender of all other modules
pub
router_sender
:
Sender
<
RouterThreadMessage
<
DursMsg
>>
,
/// HEADs cache content
pub
heads_cache
:
HashMap
<
NodeFullId
,
NetworkHead
>
,
/// Position of the 1st head displayed on the screen
pub
heads_index
:
usize
,
/// Connections cache content
pub
connections_status
:
HashMap
<
NodeFullId
,
Connection
>
,
/// Number of connections in `Established` status
pub
established_conns_count
:
usize
,
}
impl
TuiModuleDatas
{
/// Draw terminal
fn
draw_term
<
W
:
Write
>
(
&
self
,
stdout
:
&
mut
RawTerminal
<
W
>
,
start_time
:
SystemTime
,
heads_cache
:
&
HashMap
<
NodeFullId
,
NetworkHead
>
,
heads_index
:
usize
,
out_connections_status
:
&
HashMap
<
NodeFullId
,
Connection
>
,
_in_connections_status
:
&
HashMap
<
NodeFullId
,
Connection
>
,
)
->
Result
<
(),
std
::
io
::
Error
>
{
// Get Terminal size
let
(
w
,
h
)
=
termion
::
terminal_size
()
.expect
(
"Fail to get terminal size !"
);
// Prepare connections screen
let
mut
out_never_try_conns_count
=
0
;
let
mut
out_unreachable_conns_count
=
0
;
let
mut
out_trying_conns_count
=
0
;
let
mut
out_denial_conns_count
=
0
;
let
mut
out_disconnected_conns_count
=
0
;
let
mut
out_established_conns
=
Vec
::
new
();
for
(
node_full_id
,
connection
)
in
out_connections_status
{
match
connection
.status
{
0
=>
out_never_try_conns_count
+=
1
,
2
|
4
=>
out_unreachable_conns_count
+=
1
,
1
|
3
|
5
|
7
|
8
|
9
=>
out_trying_conns_count
+=
1
,
10
=>
out_denial_conns_count
+=
1
,
11
=>
out_disconnected_conns_count
+=
1
,
12
=>
out_established_conns
.push
((
node_full_id
,
connection
.uid
.clone
(),
connection
.url
.clone
(),
)),
_
=>
{}
}
}
// Prepare HEADs screen
let
mut
heads
=
heads_cache
.values
()
.collect
::
<
Vec
<&
NetworkHead
>>
();
heads
.sort_unstable_by
(|
a
,
b
|
b
.cmp
(
a
));
let
heads_window_size
=
h
as
isize
-
8
-
out_established_conns
.len
()
as
isize
;
let
heads_index_max
=
if
heads_window_size
>
0
&&
heads
.len
()
>
heads_window_size
as
usize
{
heads
.len
()
-
heads_window_size
as
usize
}
else
{
0
};
// Clear term and reset background color
write!
(
stdout
,
"{}{}{}"
,
color
::
Bg
(
color
::
Black
),
clear
::
All
,
cursor
::
Goto
(
1
,
1
)
)
?
;
// Draw headers
let
mut
line
=
1
;
write!
(
stdout
,
"{}{}{} established connections : "
,
cursor
::
Goto
(
1
,
line
),
color
::
Fg
(
color
::
White
),
out_established_conns
.len
()
)
?
;
line
+=
1
;
write!
(
stdout
,
"{}{}{} NodeId-PubKey"
,
cursor
::
Goto
(
1
,
line
),
color
::
Fg
(
color
::
White
),
style
::
Italic
,
)
?
;
// Draw inter-nodes established connections
if
out_established_conns
.is_empty
()
{
line
+=
1
;
write!
(
stdout
,
"{}{}{}No established connections !"
,
cursor
::
Goto
(
2
,
line
),
color
::
Fg
(
color
::
Red
),
style
::
Bold
,
)
?
;
}
else
{
for
(
ref
node_full_id
,
ref
uid
,
ref
url
)
in
out_established_conns
{
line
+=
1
;
let
mut
uid_string
=
uid
.clone
()
.unwrap_or_else
(||
String
::
from
(
"----------------"
));
uid_string
.truncate
(
16
);
write!
(
stdout
,
"{}{} {} {:16} {}"
,
cursor
::
Goto
(
2
,
line
),
color
::
Fg
(
color
::
Green
),
node_full_id
.to_human_string
(),
uid_string
,
url
,
)
?
;
}
}
// Draw number of conns per state
line
+=
1
;
write!
(
stdout
,
"{}{}{} know endpoints : {} Never try, {} Unreach, {} on trial, {} Denial, {} Close."
,
cursor
::
Goto
(
2
,
line
),
color
::
Fg
(
color
::
Rgb
(
128
,
128
,
128
)),
out_connections_status
.len
(),
out_never_try_conns_count
,
out_unreachable_conns_count
,
out_trying_conns_count
,
out_denial_conns_count
,
out_disconnected_conns_count
,
)
?
;
// Draw separated line
line
+=
1
;
let
mut
separated_line
=
String
::
with_capacity
(
w
as
usize
);
for
_
in
0
..
w
as
usize
{
separated_line
.push
(
'-'
);
}
write!
(
stdout
,
"{}{}{}"
,
cursor
::
Goto
(
1
,
line
),
color
::
Fg
(
color
::
White
),
separated_line
,
)
?
;
// Draw HEADs
line
+=
1
;
write!
(
stdout
,
"{}{}{} HEADs :"
,
cursor
::
Goto
(
1
,
line
),
color
::
Fg
(
color
::
White
),
heads
.len
()
)
?
;
line
+=
1
;
if
heads_index
>
0
{
write!
(
stdout
,
"{}{}/
\\
"
,
cursor
::
Goto
(
35
,
line
),
color
::
Fg
(
color
::
Green
),
)
?
;
}
else
{
write!
(
stdout
,
"{}{}/
\\
"
,
cursor
::
Goto
(
35
,
line
),
color
::
Fg
(
color
::
Black
),
)
?
;
}
line
+=
1
;
write!
(
stdout
,
"{}{}Step NodeId-Pubkey BlockId-BlockHash Soft:Ver Pre [ Api ] MeR:MiR uid"
,
cursor
::
Goto
(
1
,
line
),
color
::
Fg
(
color
::
White
)
)
?
;
for
head
in
&
heads
[
heads_index
..
]
{
if
line
<
(
h
-
2
)
{
line
+=
1
;
if
head
.step
()
==
0
{
write!
(
stdout
,
"{}{}{}"
,
cursor
::
Goto
(
1
,
line
),
color
::
Fg
(
color
::
Blue
),
head
.to_human_string
(
w
as
usize
),
)
?
;
}
else
{
write!
(
stdout
,
"{}{}{}"
,
cursor
::
Goto
(
1
,
line
),
color
::
Fg
(
color
::
Green
),
head
.to_human_string
(
w
as
usize
),
)
?
;
}
}
else
{
break
;
}
}
line
+=
1
;
if
heads_index
<
heads_index_max
{
write!
(
stdout
,
"{}{}
\\
/"
,
cursor
::
Goto
(
35
,
line
),
color
::
Fg
(
color
::
Green
),
)
?
;
}
else
{
write!
(
stdout
,
"{}{}
\\
/"
,
cursor
::
Goto
(
35
,
line
),
color
::
Fg
(
color
::
Black
),
)
?
;
}
// Draw footer
let
mut
runtime_in_secs
=
SystemTime
::
now
()
.duration_since
(
start_time
)
.expect
(
"Fail to get runtime"
)
.as_secs
();
let
runtime_hours
=
runtime_in_secs
/
3600
;
runtime_in_secs
-=
runtime_hours
*
3600
;
let
runtime_mins
=
runtime_in_secs
/
60
;
let
runtime_secs
=
runtime_in_secs
%
60
;
let
runtime_str
=
format!
(
"{:02}:{:02}:{:02}"
,
runtime_hours
,
runtime_mins
,
runtime_secs
);
write!
(
stdout
,
"{}{}{}runtime : {}"
,
cursor
::
Goto
(
1
,
h
),
color
::
Bg
(
color
::
Blue
),
color
::
Fg
(
color
::
White
),
runtime_str
,
)
?
;
write!
(
stdout
,
"{}{}{}q : quit{}"
,
cursor
::
Goto
(
w
-
7
,
h
),
color
::
Bg
(
color
::
Blue
),
color
::
Fg
(
color
::
White
),
cursor
::
Hide
,
)
?
;
// Flush stdout (i.e. make the output appear).
stdout
.flush
()
?
;
Ok
(())
}
/// Restore Terminal
fn
restore_term
<
W
:
Write
>
(
stdout
:
&
RawTerminal
<
W
>
)
{
let
_
=
stdout
.suspend_raw_mode
();
}
}
impl
Default
for
TuiModule
{
fn
default
()
->
TuiModule
{
TuiModule
{}
}
}
impl
DursModule
<
DuRsConf
,
DursMsg
>
for
TuiModule
{
type
ModuleUserConf
=
TuiConf
;
type
ModuleConf
=
TuiConf
;
type
ModuleOpt
=
TuiOpt
;
fn
name
()
->
ModuleStaticName
{
ModuleStaticName
(
"tui"
)
}
fn
priority
()
->
ModulePriority
{
ModulePriority
::
Recommended
()
}
fn
ask_required_keys
()
->
RequiredKeys
{
RequiredKeys
::
None
()
}
fn
generate_module_conf
(
_currency_name
:
Option
<&
CurrencyName
>
,
_global_conf
:
&<
DuRsConf
as
DursConfTrait
>
::
GlobalConf
,
_module_user_conf
:
Option
<
Self
::
ModuleUserConf
>
,
)
->
Result
<
(
Self
::
ModuleConf
,
Option
<
Self
::
ModuleUserConf
>
),
ModuleConfError
>
{
Ok
((
TuiConf
{},
None
))
}
fn
start
(
_soft_meta_datas
:
&
SoftwareMetaDatas
<
DuRsConf
>
,
_keys
:
RequiredKeysContent
,
_conf
:
Self
::
ModuleConf
,
router_sender
:
Sender
<
RouterThreadMessage
<
DursMsg
>>
,
)
->
Result
<
(),
failure
::
Error
>
{
let
start_time
=
SystemTime
::
now
();
//: DateTime<Utc> = Utc::now();
// Instanciate Tui module datas
let
mut
tui
=
TuiModuleDatas
{
router_sender
:
router_sender
.clone
(),
heads_cache
:
HashMap
::
new
(),
heads_index
:
0
,
connections_status
:
HashMap
::
new
(),
established_conns_count
:
0
,
};
// Create tui main thread channel
let
(
tui_sender
,
tui_receiver
):
(
Sender
<
TuiMess
>
,
Receiver
<
TuiMess
>
)
=
channel
();
// Create proxy channel
let
(
proxy_sender
,
proxy_receiver
):
(
Sender
<
DursMsg
>
,
Receiver
<
DursMsg
>
)
=
channel
();
// Launch a proxy thread that transform DursMsg() to TuiMess::DursMsg(DursMsg())
let
tui_sender_clone
=
tui_sender
.clone
();
thread
::
spawn
(
move
||
{
// Send proxy sender to main
router_sender
.send
(
RouterThreadMessage
::
ModuleRegistration
{
static_name
:
TuiModule
::
name
(),
sender
:
proxy_sender
,
roles
:
vec!
[
ModuleRole
::
UserInterface
],
events_subscription
:
vec!
[
ModuleEvent
::
NewValidBlock
,
ModuleEvent
::
ConnectionsChangeNodeNetwork
,
ModuleEvent
::
NewValidHeadFromNetwork
,
ModuleEvent
::
NewValidPeerFromNodeNetwork
,
],
reserved_apis_parts
:
vec!
[],
endpoints
:
vec!
[],
})
.expect
(
"Fatal error : tui module fail to send is sender channel !"
);
debug!
(
"Send tui sender to main thread."
);
loop
{
match
proxy_receiver
.recv
()
{
Ok
(
message
)
=>
{
let
stop
=
if
let
DursMsg
::
Stop
=
message
{
true
}
else
{
false
};
match
tui_sender_clone
.send
(
TuiMess
::
DursMsg
(
Box
::
new
(
message
)))
{
Ok
(
_
)
=>
{
if
stop
{
break
;
};
}
Err
(
_
)
=>
{
debug!
(
"tui proxy : fail to relay DursMsg to tui main thread !"
);
break
;
}
}
}
Err
(
e
)
=>
{
warn!
(
"Tui: {}"
,
e
);
break
;
}
}
}
});
// Enter raw mode.
// Wait a short while before modifying stdout, in case errors should be reported regarding the launch of the other modules.
thread
::
sleep
(
Duration
::
from_millis
(
500
));
let
mut
stdout
=
MouseTerminal
::
from
(
unwrap!
(
stdout
()
.into_raw_mode
()));
// Initial draw
let
mut
last_draw
=
SystemTime
::
now
();
unwrap!
(
tui
.draw_term
(
&
mut
stdout
,
start_time
,
&
tui
.heads_cache
,
tui
.heads_index
,
&
tui
.connections_status
,
&
HashMap
::
with_capacity
(
0
),
));
// Launch stdin thread
let
_stdin_thread
=
thread
::
spawn
(
move
||
{
// Get the standard input stream.
let
stdin
=
std
::
io
::
stdin
();
// Get stdin events
for
c
in
stdin
.events
()
{
tui_sender
.send
(
TuiMess
::
TermionEvent
(
c
.expect
(
"error to read stdin event !"
),
))
.expect
(
"Fatal error : tui stdin thread module fail to send message !"
);
trace!
(
"Send stdin event to tui main thread."
);
}
});
// ui main loop
loop
{
let
mut
user_event
=
false
;
// Get messages
match
tui_receiver
.recv_timeout
(
Duration
::
from_millis
(
250
))
{
Ok
(
ref
message
)
=>
match
*
message
{
TuiMess
::
DursMsg
(
ref
durs_message
)
=>
match
durs_message
.deref
()
{
DursMsg
::
Stop
=>
{
unwrap!
(
writeln!
(
stdout
,
"{}{}{}{}{}"
,
color
::
Fg
(
color
::
Reset
),
cursor
::
Goto
(
1
,
1
),
color
::
Bg
(
color
::
Reset
),
cursor
::
Show
,
clear
::
All
,
));
break
;
}
DursMsg
::
Event
{
ref
event_content
,
..
}
=>
match
*
event_content
{
DursEvent
::
BlockchainEvent
(
ref
dal_event
)
=>
match
*
dal_event
.deref
()
{
BlockchainEvent
::
StackUpValidBlock
(
ref
_block
)
=>
{}
BlockchainEvent
::
RevertBlocks
(
ref
_blocks
)
=>
{}
_
=>
{}
},
DursEvent
::
NetworkEvent
(
ref
network_event_box
)
=>
{
match
*
network_event_box
.deref
()
{
NetworkEvent
::
ConnectionStateChange
(
ref
node_full_id
,
ref
status
,
ref
uid
,
ref
url
,
)
=>
{
if
let
Some
(
conn
)
=
tui
.connections_status
.get
(
&
node_full_id
)
{
if
*
status
==
12
&&
(
*
conn
)
.status
!=
12
{
tui
.established_conns_count
+=
1
;
}
else
if
*
status
!=
12
&&
(
*
conn
)
.status
==
12
&&
tui
.established_conns_count
>
0
{
tui
.established_conns_count
-=
1
;
}
};
tui
.connections_status
.insert
(
*
node_full_id
,
Connection
{
status
:
*
status
,
url
:
url
.clone
(),
uid
:
uid
.clone
(),
},
);
}
NetworkEvent
::
ReceiveHeads
(
ref
heads
)
=>
{
heads
.iter
()
.map
(|
h
|
{
tui
.heads_cache
.insert
(
h
.node_full_id
(),
h
.clone
())
})
.for_each
(
drop
);
}
_
=>
{}
}
}
_
=>
{}
},
_
=>
{}
},
TuiMess
::
TermionEvent
(
ref
term_event
)
=>
match
*
term_event
{
Event
::
Key
(
Key
::
Char
(
'q'
))
=>
{
// Exit
unwrap!
(
writeln!
(
stdout
,
"{}{}{}{}{}"
,
color
::
Fg
(
color
::
Reset
),
cursor
::
Goto
(
1
,
1
),
color
::
Bg
(
color
::
Reset
),
cursor
::
Show
,
clear
::
All
,
));
TuiModuleDatas
::
restore_term
(
&
stdout
);
if
tui
.router_sender
.send
(
RouterThreadMessage
::
ModuleMessage
(
DursMsg
::
Stop
))
.is_err
()
{
warn!
(
"Tui: Fail to send STOP message to router !"
);
}
break
;
}
Event
::
Mouse
(
ref
me
)
=>
match
*
me
{
MouseEvent
::
Press
(
ref
button
,
ref
_a
,
ref
_b
)
=>
match
*
button
{
MouseButton
::
WheelDown
=>
{
// Get Terminal size
let
(
_w
,
h
)
=
termion
::
terminal_size
()
.expect
(
"Fail to get terminal size !"
);
// heads_index
if
h
>
16
{
let
heads_index_max
=
if
tui
.heads_cache
.len
()
>
(
h
-
16
)
as
usize
{
tui
.heads_cache
.len
()
-
(
h
-
16
)
as
usize
}
else
{
0
};
if
tui
.heads_index
<
heads_index_max
{
tui
.heads_index
+=
1
;
user_event
=
true
;
}
else
{
tui
.heads_index
=
heads_index_max
;
}
}
}
MouseButton
::
WheelUp
=>
{
// heads_index
if
tui
.heads_index
>
0
{
tui
.heads_index
-=
1
;
user_event
=
true
;
}
}
_
=>
{}
},
MouseEvent
::
Release
(
ref
_a
,
ref
_b
)
|
MouseEvent
::
Hold
(
ref
_a
,
ref
_b
)
=>
{}
},
_
=>
{}
},
},
Err
(
e
)
=>
match
e
{
RecvTimeoutError
::
Disconnected
=>
{
fatal_error!
(
"Disconnected tui module !"
);
}
RecvTimeoutError
::
Timeout
=>
{}
},
}
let
now
=
SystemTime
::
now
();
if
user_event
||
now
.duration_since
(
last_draw
)
.expect
(
"Tui : Fatal error : fail to get duration since last draw !"
)
.subsec_nanos
()
>
250_000_000
{
last_draw
=
now
;
unwrap!
(
tui
.draw_term
(
&
mut
stdout
,
start_time
,
&
tui
.heads_cache
,
tui
.heads_index
,
&
tui
.connections_status
,
&
HashMap
::
with_capacity
(
0
),
));
}
}
Ok
(())
}
}
This diff is collapsed.
Click to expand it.
lib/modules/tui/tui.rs
0 → 100644
+
651
−
0
View file @
f1e6e70e
// Copyright (C) 2017-2019 The AXIOM TEAM Association.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Defined the few global types used by all modules,
//! as well as the DursModule trait that all modules must implement.
#![deny(
clippy::option_unwrap_used,
clippy::result_unwrap_used,
missing_docs,
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications
)]
use
dubp_currency_params
::
CurrencyName
;
use
durs_common_tools
::
fatal_error
;
use
durs_common_tools
::
traits
::
merge
::
Merge
;
use
durs_conf
::
DuRsConf
;
use
durs_message
::
events
::
*
;
use
durs_message
::
*
;
use
durs_module
::
*
;
use
durs_network
::
events
::
NetworkEvent
;
use
durs_network_documents
::
network_head
::
NetworkHead
;
use
durs_network_documents
::
NodeFullId
;
use
log
::{
debug
,
error
,
trace
,
warn
};
use
serde
::{
Deserialize
,
Serialize
};
use
std
::
collections
::
HashMap
;
use
std
::
io
::{
stdout
,
Write
};
use
std
::
ops
::
Deref
;
use
std
::
sync
::
mpsc
::{
channel
,
Receiver
,
RecvTimeoutError
,
Sender
};
use
std
::
thread
;
use
std
::
time
::{
Duration
,
SystemTime
};
use
structopt
::
StructOpt
;
use
termion
::
event
::
*
;
use
termion
::
input
::{
MouseTerminal
,
TermRead
};
use
termion
::
raw
::{
IntoRawMode
,
RawTerminal
};
use
termion
::{
clear
,
color
,
cursor
,
style
};
use
unwrap
::
unwrap
;
#[derive(Debug,
Copy,
Clone,
PartialEq,
Eq,
Hash,
Serialize,
Deserialize)]
/// Tui Module Configuration (For future use)
pub
struct
TuiConf
{}
impl
Default
for
TuiConf
{
fn
default
()
->
Self
{
TuiConf
{}
}
}
impl
Merge
for
TuiConf
{
fn
merge
(
self
,
_
:
Self
)
->
Self
{
self
}
}
#[derive(Debug,
Clone)]
/// Format of messages received by the tui module
pub
enum
TuiMess
{
/// Message from another module
DursMsg
(
Box
<
DursMsg
>
),
/// Message from stdin (user event)
TermionEvent
(
Event
),
}
#[derive(Debug,
Copy,
Clone)]
/// Tui module
pub
struct
TuiModule
{}
#[derive(StructOpt,
Debug,
Copy,
Clone)]
#[structopt(name
=
"tui"
,
setting(structopt::clap::AppSettings::ColoredHelp))]
/// Tui subcommand options
pub
struct
TuiOpt
{}
#[derive(Debug,
Clone)]
/// Network connexion (data to display)
pub
struct
Connection
{
/// Connexion status
status
:
u32
,
/// Endpoint url
url
:
String
,
/// Node uid at the other end of the connection (member nodes only)
uid
:
Option
<
String
>
,
}
#[derive(Debug,
Clone)]
/// Data that the Tui module needs to cache
pub
struct
TuiModuleDatas
{
/// Sender of all other modules
pub
router_sender
:
Sender
<
RouterThreadMessage
<
DursMsg
>>
,
/// HEADs cache content
pub
heads_cache
:
HashMap
<
NodeFullId
,
NetworkHead
>
,
/// Position of the 1st head displayed on the screen
pub
heads_index
:
usize
,
/// Connections cache content
pub
connections_status
:
HashMap
<
NodeFullId
,
Connection
>
,
/// Number of connections in `Established` status
pub
established_conns_count
:
usize
,
}
impl
TuiModuleDatas
{
/// Draw terminal
fn
draw_term
<
W
:
Write
>
(
&
self
,
stdout
:
&
mut
RawTerminal
<
W
>
,
start_time
:
SystemTime
,
heads_cache
:
&
HashMap
<
NodeFullId
,
NetworkHead
>
,
heads_index
:
usize
,
out_connections_status
:
&
HashMap
<
NodeFullId
,
Connection
>
,
_in_connections_status
:
&
HashMap
<
NodeFullId
,
Connection
>
,
)
->
Result
<
(),
std
::
io
::
Error
>
{
// Get Terminal size
let
(
w
,
h
)
=
termion
::
terminal_size
()
.expect
(
"Fail to get terminal size !"
);
// Prepare connections screen
let
mut
out_never_try_conns_count
=
0
;
let
mut
out_unreachable_conns_count
=
0
;
let
mut
out_trying_conns_count
=
0
;
let
mut
out_denial_conns_count
=
0
;
let
mut
out_disconnected_conns_count
=
0
;
let
mut
out_established_conns
=
Vec
::
new
();
for
(
node_full_id
,
connection
)
in
out_connections_status
{
match
connection
.status
{
0
=>
out_never_try_conns_count
+=
1
,
2
|
4
=>
out_unreachable_conns_count
+=
1
,
1
|
3
|
5
|
7
|
8
|
9
=>
out_trying_conns_count
+=
1
,
10
=>
out_denial_conns_count
+=
1
,
11
=>
out_disconnected_conns_count
+=
1
,
12
=>
out_established_conns
.push
((
node_full_id
,
connection
.uid
.clone
(),
connection
.url
.clone
(),
)),
_
=>
{}
}
}
// Prepare HEADs screen
let
mut
heads
=
heads_cache
.values
()
.collect
::
<
Vec
<&
NetworkHead
>>
();
heads
.sort_unstable_by
(|
a
,
b
|
b
.cmp
(
a
));
let
heads_window_size
=
h
as
isize
-
8
-
out_established_conns
.len
()
as
isize
;
let
heads_index_max
=
if
heads_window_size
>
0
&&
heads
.len
()
>
heads_window_size
as
usize
{
heads
.len
()
-
heads_window_size
as
usize
}
else
{
0
};
// Clear term and reset background color
write!
(
stdout
,
"{}{}{}"
,
color
::
Bg
(
color
::
Black
),
clear
::
All
,
cursor
::
Goto
(
1
,
1
)
)
?
;
// Draw headers
let
mut
line
=
1
;
write!
(
stdout
,
"{}{}{} established connections : "
,
cursor
::
Goto
(
1
,
line
),
color
::
Fg
(
color
::
White
),
out_established_conns
.len
()
)
?
;
line
+=
1
;
write!
(
stdout
,
"{}{}{} NodeId-PubKey"
,
cursor
::
Goto
(
1
,
line
),
color
::
Fg
(
color
::
White
),
style
::
Italic
,
)
?
;
// Draw inter-nodes established connections
if
out_established_conns
.is_empty
()
{
line
+=
1
;
write!
(
stdout
,
"{}{}{}No established connections !"
,
cursor
::
Goto
(
2
,
line
),
color
::
Fg
(
color
::
Red
),
style
::
Bold
,
)
?
;
}
else
{
for
(
ref
node_full_id
,
ref
uid
,
ref
url
)
in
out_established_conns
{
line
+=
1
;
let
mut
uid_string
=
uid
.clone
()
.unwrap_or_else
(||
String
::
from
(
"----------------"
));
uid_string
.truncate
(
16
);
write!
(
stdout
,
"{}{} {} {:16} {}"
,
cursor
::
Goto
(
2
,
line
),
color
::
Fg
(
color
::
Green
),
node_full_id
.to_human_string
(),
uid_string
,
url
,
)
?
;
}
}
// Draw number of conns per state
line
+=
1
;
write!
(
stdout
,
"{}{}{} know endpoints : {} Never try, {} Unreach, {} on trial, {} Denial, {} Close."
,
cursor
::
Goto
(
2
,
line
),
color
::
Fg
(
color
::
Rgb
(
128
,
128
,
128
)),
out_connections_status
.len
(),
out_never_try_conns_count
,
out_unreachable_conns_count
,
out_trying_conns_count
,
out_denial_conns_count
,
out_disconnected_conns_count
,
)
?
;
// Draw separated line
line
+=
1
;
let
mut
separated_line
=
String
::
with_capacity
(
w
as
usize
);
for
_
in
0
..
w
as
usize
{
separated_line
.push
(
'-'
);
}
write!
(
stdout
,
"{}{}{}"
,
cursor
::
Goto
(
1
,
line
),
color
::
Fg
(
color
::
White
),
separated_line
,
)
?
;
// Draw HEADs
line
+=
1
;
write!
(
stdout
,
"{}{}{} HEADs :"
,
cursor
::
Goto
(
1
,
line
),
color
::
Fg
(
color
::
White
),
heads
.len
()
)
?
;
line
+=
1
;
if
heads_index
>
0
{
write!
(
stdout
,
"{}{}/
\\
"
,
cursor
::
Goto
(
35
,
line
),
color
::
Fg
(
color
::
Green
),
)
?
;
}
else
{
write!
(
stdout
,
"{}{}/
\\
"
,
cursor
::
Goto
(
35
,
line
),
color
::
Fg
(
color
::
Black
),
)
?
;
}
line
+=
1
;
write!
(
stdout
,
"{}{}Step NodeId-Pubkey BlockId-BlockHash Soft:Ver Pre [ Api ] MeR:MiR uid"
,
cursor
::
Goto
(
1
,
line
),
color
::
Fg
(
color
::
White
)
)
?
;
for
head
in
&
heads
[
heads_index
..
]
{
if
line
<
(
h
-
2
)
{
line
+=
1
;
if
head
.step
()
==
0
{
write!
(
stdout
,
"{}{}{}"
,
cursor
::
Goto
(
1
,
line
),
color
::
Fg
(
color
::
Blue
),
head
.to_human_string
(
w
as
usize
),
)
?
;
}
else
{
write!
(
stdout
,
"{}{}{}"
,
cursor
::
Goto
(
1
,
line
),
color
::
Fg
(
color
::
Green
),
head
.to_human_string
(
w
as
usize
),
)
?
;
}
}
else
{
break
;
}
}
line
+=
1
;
if
heads_index
<
heads_index_max
{
write!
(
stdout
,
"{}{}
\\
/"
,
cursor
::
Goto
(
35
,
line
),
color
::
Fg
(
color
::
Green
),
)
?
;
}
else
{
write!
(
stdout
,
"{}{}
\\
/"
,
cursor
::
Goto
(
35
,
line
),
color
::
Fg
(
color
::
Black
),
)
?
;
}
// Draw footer
let
mut
runtime_in_secs
=
SystemTime
::
now
()
.duration_since
(
start_time
)
.expect
(
"Fail to get runtime"
)
.as_secs
();
let
runtime_hours
=
runtime_in_secs
/
3600
;
runtime_in_secs
-=
runtime_hours
*
3600
;
let
runtime_mins
=
runtime_in_secs
/
60
;
let
runtime_secs
=
runtime_in_secs
%
60
;
let
runtime_str
=
format!
(
"{:02}:{:02}:{:02}"
,
runtime_hours
,
runtime_mins
,
runtime_secs
);
write!
(
stdout
,
"{}{}{}runtime : {}"
,
cursor
::
Goto
(
1
,
h
),
color
::
Bg
(
color
::
Blue
),
color
::
Fg
(
color
::
White
),
runtime_str
,
)
?
;
write!
(
stdout
,
"{}{}{}q : quit{}"
,
cursor
::
Goto
(
w
-
7
,
h
),
color
::
Bg
(
color
::
Blue
),
color
::
Fg
(
color
::
White
),
cursor
::
Hide
,
)
?
;
// Flush stdout (i.e. make the output appear).
stdout
.flush
()
?
;
Ok
(())
}
/// Restore Terminal
fn
restore_term
<
W
:
Write
>
(
stdout
:
&
RawTerminal
<
W
>
)
{
let
_
=
stdout
.suspend_raw_mode
();
}
}
impl
Default
for
TuiModule
{
fn
default
()
->
TuiModule
{
TuiModule
{}
}
}
impl
DursModule
<
DuRsConf
,
DursMsg
>
for
TuiModule
{
type
ModuleUserConf
=
TuiConf
;
type
ModuleConf
=
TuiConf
;
type
ModuleOpt
=
TuiOpt
;
fn
name
()
->
ModuleStaticName
{
ModuleStaticName
(
"tui"
)
}
fn
priority
()
->
ModulePriority
{
ModulePriority
::
Recommended
()
}
fn
ask_required_keys
()
->
RequiredKeys
{
RequiredKeys
::
None
()
}
fn
generate_module_conf
(
_currency_name
:
Option
<&
CurrencyName
>
,
_global_conf
:
&<
DuRsConf
as
DursConfTrait
>
::
GlobalConf
,
_module_user_conf
:
Option
<
Self
::
ModuleUserConf
>
,
)
->
Result
<
(
Self
::
ModuleConf
,
Option
<
Self
::
ModuleUserConf
>
),
ModuleConfError
>
{
Ok
((
TuiConf
{},
None
))
}
fn
start
(
_soft_meta_datas
:
&
SoftwareMetaDatas
<
DuRsConf
>
,
_keys
:
RequiredKeysContent
,
_conf
:
Self
::
ModuleConf
,
router_sender
:
Sender
<
RouterThreadMessage
<
DursMsg
>>
,
)
->
Result
<
(),
failure
::
Error
>
{
let
start_time
=
SystemTime
::
now
();
//: DateTime<Utc> = Utc::now();
// Instanciate Tui module datas
let
mut
tui
=
TuiModuleDatas
{
router_sender
:
router_sender
.clone
(),
heads_cache
:
HashMap
::
new
(),
heads_index
:
0
,
connections_status
:
HashMap
::
new
(),
established_conns_count
:
0
,
};
// Create tui main thread channel
let
(
tui_sender
,
tui_receiver
):
(
Sender
<
TuiMess
>
,
Receiver
<
TuiMess
>
)
=
channel
();
// Create proxy channel
let
(
proxy_sender
,
proxy_receiver
):
(
Sender
<
DursMsg
>
,
Receiver
<
DursMsg
>
)
=
channel
();
// Launch a proxy thread that transform DursMsg() to TuiMess::DursMsg(DursMsg())
let
tui_sender_clone
=
tui_sender
.clone
();
thread
::
spawn
(
move
||
{
// Send proxy sender to main
router_sender
.send
(
RouterThreadMessage
::
ModuleRegistration
{
static_name
:
TuiModule
::
name
(),
sender
:
proxy_sender
,
roles
:
vec!
[
ModuleRole
::
UserInterface
],
events_subscription
:
vec!
[
ModuleEvent
::
NewValidBlock
,
ModuleEvent
::
ConnectionsChangeNodeNetwork
,
ModuleEvent
::
NewValidHeadFromNetwork
,
ModuleEvent
::
NewValidPeerFromNodeNetwork
,
],
reserved_apis_parts
:
vec!
[],
endpoints
:
vec!
[],
})
.expect
(
"Fatal error : tui module fail to send is sender channel !"
);
debug!
(
"Send tui sender to main thread."
);
loop
{
match
proxy_receiver
.recv
()
{
Ok
(
message
)
=>
{
let
stop
=
if
let
DursMsg
::
Stop
=
message
{
true
}
else
{
false
};
match
tui_sender_clone
.send
(
TuiMess
::
DursMsg
(
Box
::
new
(
message
)))
{
Ok
(
_
)
=>
{
if
stop
{
break
;
};
}
Err
(
_
)
=>
{
debug!
(
"tui proxy : fail to relay DursMsg to tui main thread !"
);
break
;
}
}
}
Err
(
e
)
=>
{
warn!
(
"Tui: {}"
,
e
);
break
;
}
}
}
});
// Enter raw mode.
// Wait a short while before modifying stdout, in case errors should be reported regarding the launch of the other modules.
thread
::
sleep
(
Duration
::
from_millis
(
500
));
let
mut
stdout
=
MouseTerminal
::
from
(
unwrap!
(
stdout
()
.into_raw_mode
()));
// Initial draw
let
mut
last_draw
=
SystemTime
::
now
();
unwrap!
(
tui
.draw_term
(
&
mut
stdout
,
start_time
,
&
tui
.heads_cache
,
tui
.heads_index
,
&
tui
.connections_status
,
&
HashMap
::
with_capacity
(
0
),
));
// Launch stdin thread
let
_stdin_thread
=
thread
::
spawn
(
move
||
{
// Get the standard input stream.
let
stdin
=
std
::
io
::
stdin
();
// Get stdin events
for
c
in
stdin
.events
()
{
tui_sender
.send
(
TuiMess
::
TermionEvent
(
c
.expect
(
"error to read stdin event !"
),
))
.expect
(
"Fatal error : tui stdin thread module fail to send message !"
);
trace!
(
"Send stdin event to tui main thread."
);
}
});
// ui main loop
loop
{
let
mut
user_event
=
false
;
// Get messages
match
tui_receiver
.recv_timeout
(
Duration
::
from_millis
(
250
))
{
Ok
(
ref
message
)
=>
match
*
message
{
TuiMess
::
DursMsg
(
ref
durs_message
)
=>
match
durs_message
.deref
()
{
DursMsg
::
Stop
=>
{
unwrap!
(
writeln!
(
stdout
,
"{}{}{}{}{}"
,
color
::
Fg
(
color
::
Reset
),
cursor
::
Goto
(
1
,
1
),
color
::
Bg
(
color
::
Reset
),
cursor
::
Show
,
clear
::
All
,
));
break
;
}
DursMsg
::
Event
{
ref
event_content
,
..
}
=>
match
*
event_content
{
DursEvent
::
BlockchainEvent
(
ref
dal_event
)
=>
match
*
dal_event
.deref
()
{
BlockchainEvent
::
StackUpValidBlock
(
ref
_block
)
=>
{}
BlockchainEvent
::
RevertBlocks
(
ref
_blocks
)
=>
{}
_
=>
{}
},
DursEvent
::
NetworkEvent
(
ref
network_event_box
)
=>
{
match
*
network_event_box
.deref
()
{
NetworkEvent
::
ConnectionStateChange
(
ref
node_full_id
,
ref
status
,
ref
uid
,
ref
url
,
)
=>
{
if
let
Some
(
conn
)
=
tui
.connections_status
.get
(
&
node_full_id
)
{
if
*
status
==
12
&&
(
*
conn
)
.status
!=
12
{
tui
.established_conns_count
+=
1
;
}
else
if
*
status
!=
12
&&
(
*
conn
)
.status
==
12
&&
tui
.established_conns_count
>
0
{
tui
.established_conns_count
-=
1
;
}
};
tui
.connections_status
.insert
(
*
node_full_id
,
Connection
{
status
:
*
status
,
url
:
url
.clone
(),
uid
:
uid
.clone
(),
},
);
}
NetworkEvent
::
ReceiveHeads
(
ref
heads
)
=>
{
heads
.iter
()
.map
(|
h
|
{
tui
.heads_cache
.insert
(
h
.node_full_id
(),
h
.clone
())
})
.for_each
(
drop
);
}
_
=>
{}
}
}
_
=>
{}
},
_
=>
{}
},
TuiMess
::
TermionEvent
(
ref
term_event
)
=>
match
*
term_event
{
Event
::
Key
(
Key
::
Char
(
'q'
))
=>
{
// Exit
unwrap!
(
writeln!
(
stdout
,
"{}{}{}{}{}"
,
color
::
Fg
(
color
::
Reset
),
cursor
::
Goto
(
1
,
1
),
color
::
Bg
(
color
::
Reset
),
cursor
::
Show
,
clear
::
All
,
));
TuiModuleDatas
::
restore_term
(
&
stdout
);
if
tui
.router_sender
.send
(
RouterThreadMessage
::
ModuleMessage
(
DursMsg
::
Stop
))
.is_err
()
{
warn!
(
"Tui: Fail to send STOP message to router !"
);
}
break
;
}
Event
::
Mouse
(
ref
me
)
=>
match
*
me
{
MouseEvent
::
Press
(
ref
button
,
ref
_a
,
ref
_b
)
=>
match
*
button
{
MouseButton
::
WheelDown
=>
{
// Get Terminal size
let
(
_w
,
h
)
=
termion
::
terminal_size
()
.expect
(
"Fail to get terminal size !"
);
// heads_index
if
h
>
16
{
let
heads_index_max
=
if
tui
.heads_cache
.len
()
>
(
h
-
16
)
as
usize
{
tui
.heads_cache
.len
()
-
(
h
-
16
)
as
usize
}
else
{
0
};
if
tui
.heads_index
<
heads_index_max
{
tui
.heads_index
+=
1
;
user_event
=
true
;
}
else
{
tui
.heads_index
=
heads_index_max
;
}
}
}
MouseButton
::
WheelUp
=>
{
// heads_index
if
tui
.heads_index
>
0
{
tui
.heads_index
-=
1
;
user_event
=
true
;
}
}
_
=>
{}
},
MouseEvent
::
Release
(
ref
_a
,
ref
_b
)
|
MouseEvent
::
Hold
(
ref
_a
,
ref
_b
)
=>
{}
},
_
=>
{}
},
},
Err
(
e
)
=>
match
e
{
RecvTimeoutError
::
Disconnected
=>
{
fatal_error!
(
"Disconnected tui module !"
);
}
RecvTimeoutError
::
Timeout
=>
{}
},
}
let
now
=
SystemTime
::
now
();
if
user_event
||
now
.duration_since
(
last_draw
)
.expect
(
"Tui : Fatal error : fail to get duration since last draw !"
)
.subsec_nanos
()
>
250_000_000
{
last_draw
=
now
;
unwrap!
(
tui
.draw_term
(
&
mut
stdout
,
start_time
,
&
tui
.heads_cache
,
tui
.heads_index
,
&
tui
.connections_status
,
&
HashMap
::
with_capacity
(
0
),
));
}
}
Ok
(())
}
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment