Skip to content

Commit 9266b14

Browse files
committed
Toggle for connection time
1 parent a4dbcbb commit 9266b14

4 files changed

Lines changed: 137 additions & 77 deletions

File tree

src/app.rs

Lines changed: 96 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,7 +1602,7 @@ impl App {
16021602
}
16031603
// Currently this runs even for action chains that don't need the port,
16041604
// but that's such a niche/rare thing right now, so meh.
1605-
InnerPortStatus::Idle | InnerPortStatus::PrematureDisconnect => {
1605+
InnerPortStatus::Idle { .. } | InnerPortStatus::PrematureDisconnect { .. } => {
16061606
// InnerPortStatus::Idle | InnerPortStatus::PrematureDisconnect if action.requires_connection() => {
16071607
let text = if self.action_queue.len() == 1 {
16081608
"Port isn't ready! Not running action...".into()
@@ -4259,78 +4259,85 @@ impl App {
42594259
let [terminal_area, line_area, whole_input_area] = vertical![*=1, ==1, ==1].areas(area);
42604260
let [input_symbol_area, input_area] = horizontal![==1, *=1].areas(whole_input_area);
42614261
let dark_gray = Style::new().dark_gray();
4262-
// let start = Instant::now();
4263-
if self.settings.rendering.hex_view {
4264-
self.buffer.render_hex(terminal_area, frame.buffer_mut());
4265-
} else {
4266-
frame.render_widget(&mut self.buffer, terminal_area);
4267-
}
4268-
// debug!("1: {:?}", start.elapsed());
4269-
// let start = Instant::now();
4270-
4271-
// TODO toggle and layering options
4272-
let mut render_connection_timer =
4273-
|duration_opt: Option<Duration>, style: Style, lent_out_time: bool| {
4274-
let conn_stopwatch_string: Cow<'_, str> = if let Some(duration) = duration_opt {
4275-
let secs = duration.as_secs();
4276-
let day = secs / 86400;
4277-
let hour = (secs % 86400) / 3600;
4278-
let min = (secs % 3600) / 60;
4279-
let sec = secs % 60;
4280-
4281-
let output = if day > 0 {
4282-
format!("{day:02}:{hour:02}:{min:02}:{sec:02}")
4283-
} else {
4284-
format!("{hour:02}:{min:02}:{sec:02}")
4285-
};
42864262

4287-
let output = if lent_out_time {
4288-
format!("({output})")
4289-
} else {
4290-
output
4291-
};
4263+
fn render_connection_timer(
4264+
frame: &mut Frame,
4265+
area: Rect,
4266+
duration_opt: Option<Duration>,
4267+
style: Style,
4268+
lent_out_time: bool,
4269+
) {
4270+
let conn_stopwatch_string: Cow<'_, str> = if let Some(duration) = duration_opt {
4271+
let secs = duration.as_secs();
4272+
let day = secs / 86400;
4273+
let hour = (secs % 86400) / 3600;
4274+
let min = (secs % 3600) / 60;
4275+
let sec = secs % 60;
4276+
4277+
let output = if day > 0 {
4278+
format!("{day:02}:{hour:02}:{min:02}:{sec:02}")
4279+
} else {
4280+
format!("{hour:02}:{min:02}:{sec:02}")
4281+
};
42924282

4293-
output.into()
4283+
let output = if lent_out_time {
4284+
format!("({output})")
42944285
} else {
4295-
"XX:XX:XX".into()
4286+
output
42964287
};
42974288

4298-
let [top_line] = vertical![==1].areas(area.inner(Margin {
4299-
horizontal: 1,
4300-
vertical: lent_out_time as u16,
4301-
}));
4289+
output.into()
4290+
} else {
4291+
"XX:XX:XX".into()
4292+
};
43024293

4303-
let line = Line::from(Span::styled(conn_stopwatch_string, style)).right_aligned();
4294+
let [top_line] = vertical![==1].areas(area.inner(Margin {
4295+
horizontal: 1,
4296+
vertical: lent_out_time as u16,
4297+
}));
43044298

4305-
frame.render_widget(line, top_line);
4306-
};
4299+
let line = Line::from(Span::styled(conn_stopwatch_string, style)).right_aligned();
43074300

4308-
let (port_state, serial_signals, port_text) = {
4309-
let port_status_guard = self.serial.port_status.load();
4310-
let port_state = port_status_guard.status();
4301+
frame.render_widget(line, top_line);
4302+
}
43114303

4304+
fn render_connection_timers(frame: &mut Frame, area: Rect, port_state: &InnerPortStatus) {
4305+
let dark_gray = Style::new().dark_gray();
43124306
match port_state {
4313-
InnerPortStatus::Connected { connected_at } => {
4314-
render_connection_timer(Some(connected_at.elapsed()), dark_gray, false)
4315-
}
4307+
InnerPortStatus::Connected { connected_at } => render_connection_timer(
4308+
frame,
4309+
area,
4310+
Some(connected_at.elapsed()),
4311+
dark_gray,
4312+
false,
4313+
),
43164314
#[cfg(feature = "espflash")]
43174315
InnerPortStatus::LentOut {
4318-
initial_connection_at,
4316+
connected_at,
43194317
lent_out_at,
43204318
} => {
43214319
render_connection_timer(
4322-
Some(initial_connection_at.elapsed()),
4320+
frame,
4321+
area,
4322+
Some(connected_at.elapsed()),
43234323
dark_gray,
43244324
false,
43254325
);
43264326
render_connection_timer(
4327+
frame,
4328+
area,
43274329
Some(lent_out_at.elapsed()),
43284330
Color::Yellow.into(),
43294331
true,
43304332
);
43314333
}
4332-
_ => render_connection_timer(None, dark_gray, false),
4334+
_ => render_connection_timer(frame, area, None, dark_gray, false),
43334335
}
4336+
}
4337+
4338+
let (port_status, serial_signals, port_text) = {
4339+
let port_status_guard = self.serial.port_status.load();
4340+
let port_state = port_status_guard.status();
43344341

43354342
let port_text = match &port_status_guard.current_port() {
43364343
Some(port_info) => {
@@ -4352,7 +4359,44 @@ impl App {
43524359
(port_state, port_status_guard.signals().clone(), port_text)
43534360
};
43544361

4355-
repeating_pattern_widget(frame, line_area, self.repeating_line_flip, port_state);
4362+
let time_shown = self.settings.rendering.show_connection_time;
4363+
4364+
// const SHOW_ABOVE_TIME: Duration = Duration::from_secs(30);
4365+
4366+
// let time_above_text = match (self.settings.rendering.show_connection_time, port_status) {
4367+
// (Ct::BelowText, InnerPortStatus::Connected { connected_at })
4368+
// if connected_at.elapsed() <= SHOW_ABOVE_TIME =>
4369+
// {
4370+
// true
4371+
// }
4372+
// (Ct::BelowText, InnerPortStatus::LentOut { connected_at, .. })
4373+
// if connected_at.elapsed() <= SHOW_ABOVE_TIME =>
4374+
// {
4375+
// true
4376+
// }
4377+
// (Ct::BelowText, InnerPortStatus::PrematureDisconnect { disconnected_at })
4378+
// if disconnected_at.elapsed() <= SHOW_ABOVE_TIME =>
4379+
// {
4380+
// true
4381+
// }
4382+
// (Ct::AboveText, _) => true,
4383+
// (_, _) => false,
4384+
// };
4385+
// if time_shown && !time_above_text {
4386+
// render_connection_timers(frame, area, &port_status);
4387+
// }
4388+
4389+
if self.settings.rendering.hex_view {
4390+
self.buffer.render_hex(terminal_area, frame.buffer_mut());
4391+
} else {
4392+
frame.render_widget(&mut self.buffer, terminal_area);
4393+
}
4394+
4395+
if time_shown {
4396+
render_connection_timers(frame, area, &port_status);
4397+
}
4398+
4399+
repeating_pattern_widget(frame, line_area, self.repeating_line_flip, port_status);
43564400

43574401
let widget_margin: u16 = if area.width >= 100 { 3 } else { 0 };
43584402

@@ -4453,7 +4497,7 @@ impl App {
44534497
};
44544498

44554499
if self.settings.behavior.pseudo_shell {
4456-
let input_symbol_style = if port_state.is_connected() {
4500+
let input_symbol_style = if port_status.is_connected() {
44574501
input_style.not_reversed().green()
44584502
} else {
44594503
input_style.red()
@@ -5155,8 +5199,8 @@ pub fn repeating_pattern_widget(
51555199
InnerPortStatus::Connected { .. } => pattern_widget.green(),
51565200
#[cfg(feature = "espflash")]
51575201
InnerPortStatus::LentOut { .. } => pattern_widget.yellow(),
5158-
InnerPortStatus::PrematureDisconnect => pattern_widget.red(),
5159-
InnerPortStatus::Idle => pattern_widget.red(),
5202+
InnerPortStatus::PrematureDisconnect { .. } => pattern_widget.red(),
5203+
InnerPortStatus::Idle { .. } => pattern_widget.red(),
51605204
};
51615205
frame.render_widget(pattern_widget, area);
51625206
}

src/serial/port_status.rs

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::settings::PortSettings;
1313
#[cfg(feature = "espflash")]
1414
use crate::serial::worker::NativePort;
1515

16-
#[derive(Debug, Clone, Default)]
16+
#[derive(Debug, Clone)]
1717
/// Port status, shared with the main+UI thread.
1818
pub struct PortStatus {
1919
/// The actual state of the port.
@@ -33,26 +33,33 @@ pub struct PortStatus {
3333
impl PortStatus {
3434
pub fn new_idle(settings: &PortSettings) -> Self {
3535
Self {
36+
inner: InnerPortStatus::Idle {
37+
_disconnected_at: Instant::now(),
38+
},
3639
signals: SerialSignals {
3740
dtr: settings.dtr_on_connect,
3841
rts: settings.rts_on_connect,
3942
..Default::default()
4043
},
41-
..Default::default()
44+
current_port: None,
4245
}
4346
}
4447
/// Used when a port disconnects without the user's stated intent to do so.
45-
pub fn into_unhealthy(self) -> Self {
48+
pub fn into_unhealthy(self, disconnected_at: Instant) -> Self {
4649
Self {
47-
inner: InnerPortStatus::PrematureDisconnect,
50+
inner: InnerPortStatus::PrematureDisconnect {
51+
_disconnected_at: disconnected_at,
52+
},
4853

4954
..self
5055
}
5156
}
5257
/// Used when the user chooses to disconnect from the serial port
53-
pub fn into_idle(self, settings: &PortSettings) -> Self {
58+
pub fn into_idle(self, disconnected_at: Instant, settings: &PortSettings) -> Self {
5459
Self {
55-
inner: InnerPortStatus::Idle,
60+
inner: InnerPortStatus::Idle {
61+
_disconnected_at: disconnected_at,
62+
},
5663
current_port: None,
5764
signals: SerialSignals {
5865
dtr: settings.dtr_on_connect,
@@ -70,7 +77,7 @@ impl PortStatus {
7077

7178
Self {
7279
inner: InnerPortStatus::LentOut {
73-
initial_connection_at: connected_at,
80+
connected_at,
7481
lent_out_at,
7582
},
7683
..self
@@ -85,21 +92,15 @@ impl PortStatus {
8592
) -> Result<Self, serialport::Error> {
8693
use crate::serial::SerialSignals;
8794

88-
let InnerPortStatus::LentOut {
89-
initial_connection_at,
90-
..
91-
} = self.inner
92-
else {
95+
let InnerPortStatus::LentOut { connected_at, .. } = self.inner else {
9396
panic!("can't return a non-lent port")
9497
};
9598

9699
port.write_data_terminal_ready(self.signals.dtr)?;
97100
port.write_request_to_send(self.signals.rts)?;
98101

99102
let status = Self {
100-
inner: InnerPortStatus::Connected {
101-
connected_at: initial_connection_at,
102-
},
103+
inner: InnerPortStatus::Connected { connected_at },
103104
signals: SerialSignals {
104105
dtr: settings.dtr_on_connect,
105106
rts: settings.rts_on_connect,
@@ -125,8 +126,8 @@ impl PortStatus {
125126
current_port: Some(port_info),
126127
inner: InnerPortStatus::Connected { connected_at },
127128
signals: SerialSignals {
128-
rts: settings.dtr_on_connect,
129129
dtr: settings.dtr_on_connect,
130+
rts: settings.rts_on_connect,
130131
..signals
131132
},
132133
};
@@ -144,17 +145,16 @@ impl PortStatus {
144145
}
145146
}
146147

147-
#[derive(Debug, Clone, Copy, Default, strum::EnumIs)]
148+
#[derive(Debug, Clone, Copy, strum::EnumIs)]
148149
pub enum InnerPortStatus {
149-
#[default]
150150
/// No connection has been made.
151-
Idle,
151+
Idle { _disconnected_at: Instant },
152152
/// Port was lost unexpectedly.
153-
PrematureDisconnect,
153+
PrematureDisconnect { _disconnected_at: Instant },
154154
#[cfg(feature = "espflash")]
155155
/// Port is temporarily owned by espflash.
156156
LentOut {
157-
initial_connection_at: Instant,
157+
connected_at: Instant,
158158
lent_out_at: Instant,
159159
},
160160
/// Port is owned by us and we can read/write to it.

src/serial/worker.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ impl SerialWorker {
251251
}
252252

253253
fn unhealthy_disconnection(&mut self) {
254+
let now = Instant::now();
254255
// This used to be an assertion but if the main+UI thread accidentally sends a command
255256
// when the port is missing (it's supposed to check for port health, but that introduces
256257
// time-of-check/time-of-use race conditions, so we'll just gracefully disconnect),
@@ -287,7 +288,7 @@ impl SerialWorker {
287288
last_status
288289
// Ensure we keep around the old SerialPortInfo to use
289290
// as a reference for reconnections!
290-
.into_unhealthy()
291+
.into_unhealthy(now)
291292
};
292293
self.shared_status.store(Arc::new(disconnected_status));
293294
// Disconnection Event TX should be done by caller.
@@ -364,8 +365,9 @@ impl SerialWorker {
364365

365366
let previous_status = { self.shared_status.load().as_ref().clone() };
366367

367-
self.shared_status
368-
.store(Arc::new(previous_status.into_idle(&settings)));
368+
self.shared_status.store(Arc::new(
369+
previous_status.into_idle(Instant::now(), &settings),
370+
));
369371
self.port.drop();
370372
self.event_tx
371373
.send(SerialDisconnectReason::Intentional.into())?;

src/settings/mod.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,21 @@ pub struct Rendering {
158158
#[table(values = HexHighlightStyle::VARIANTS)]
159159
/// Show user input in buffer after sending.
160160
pub hex_view_highlights: HexHighlightStyle,
161-
}
161+
162+
#[derivative(Default(value = "true"))]
163+
/// Show how long the connection to the port has been active on the top-right.
164+
pub show_connection_time: bool,
165+
}
166+
167+
// #[derive(
168+
// Debug, Clone, Copy, PartialEq, Serialize, Deserialize, strum::Display, strum::VariantArray,
169+
// )]
170+
// #[strum(serialize_all = "title_case")]
171+
// pub enum ConnectionTime {
172+
// Hidden,
173+
// BelowText,
174+
// AboveText,
175+
// }
162176

163177
#[derive(
164178
Debug, Clone, Copy, PartialEq, Serialize, Deserialize, strum::Display, strum::VariantArray,

0 commit comments

Comments
 (0)