Skip to content

Commit 4b29c2b

Browse files
committed
ui: screens: usb_overload: display a notification on USB power overload
This should make debugging mis-behaving USB devices a bit easier. Signed-off-by: Leonard Göhrs <l.goehrs@pengutronix.de>
1 parent 8ca1ac3 commit 4b29c2b

File tree

2 files changed

+154
-0
lines changed

2 files changed

+154
-0
lines changed

src/ui/screens.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ mod uart;
4141
mod update_available;
4242
mod update_installation;
4343
mod usb;
44+
mod usb_overload;
4445

4546
use dig_out::DigOutScreen;
4647
use help::HelpScreen;
@@ -57,6 +58,7 @@ use uart::UartScreen;
5758
use update_available::UpdateAvailableScreen;
5859
use update_installation::UpdateInstallationScreen;
5960
use usb::UsbScreen;
61+
use usb_overload::UsbOverloadScreen;
6062

6163
use super::buttons;
6264
use super::widgets;
@@ -84,6 +86,7 @@ pub enum AlertScreen {
8486
RebootConfirm,
8587
UpdateAvailable,
8688
UpdateInstallation,
89+
UsbOverload,
8790
Help,
8891
Setup,
8992
OverTemperature,
@@ -207,5 +210,6 @@ pub(super) fn init(
207210
&res.temperatures.warning,
208211
)),
209212
Box::new(LocatorScreen::new(alerts, locator)),
213+
Box::new(UsbOverloadScreen::new(alerts, &res.usb_hub.overload)),
210214
]
211215
}

src/ui/screens/usb_overload.rs

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// This file is part of tacd, the LXA TAC system daemon
2+
// Copyright (C) 2023 Pengutronix e.K.
3+
//
4+
// This program is free software; you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation; either version 2 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License along
15+
// with this program; if not, write to the Free Software Foundation, Inc.,
16+
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17+
18+
use async_std::prelude::*;
19+
use async_std::sync::Arc;
20+
use async_std::task::spawn;
21+
use async_trait::async_trait;
22+
use embedded_graphics::{
23+
mono_font::MonoTextStyle, pixelcolor::BinaryColor, prelude::*, text::Text,
24+
};
25+
26+
use super::widgets::*;
27+
use super::{
28+
row_anchor, ActivatableScreen, ActiveScreen, AlertList, AlertScreen, Alerter, Display,
29+
InputEvent, Screen, Ui,
30+
};
31+
use crate::broker::Topic;
32+
use crate::measurement::Measurement;
33+
use crate::usb_hub::{OverloadedPort, MAX_PORT_CURRENT, MAX_TOTAL_CURRENT};
34+
35+
const SCREEN_TYPE: AlertScreen = AlertScreen::UsbOverload;
36+
const OFFSET_BAR: Point = Point::new(75, -14);
37+
const OFFSET_VAL: Point = Point::new(160, 0);
38+
const WIDTH_BAR: u32 = 80;
39+
const HEIGHT_BAR: u32 = 18;
40+
41+
pub struct UsbOverloadScreen;
42+
43+
struct Active {
44+
widgets: WidgetContainer,
45+
}
46+
47+
impl UsbOverloadScreen {
48+
pub fn new(
49+
alerts: &Arc<Topic<AlertList>>,
50+
overload: &Arc<Topic<Option<OverloadedPort>>>,
51+
) -> Self {
52+
let (mut overload_events, _) = overload.clone().subscribe_unbounded();
53+
let alerts = alerts.clone();
54+
55+
spawn(async move {
56+
while let Some(overload) = overload_events.next().await {
57+
if overload.is_some() {
58+
alerts.assert(SCREEN_TYPE)
59+
} else {
60+
alerts.deassert(SCREEN_TYPE)
61+
}
62+
}
63+
});
64+
65+
Self
66+
}
67+
}
68+
69+
impl ActivatableScreen for UsbOverloadScreen {
70+
fn my_type(&self) -> Screen {
71+
Screen::Alert(SCREEN_TYPE)
72+
}
73+
74+
fn activate(&mut self, ui: &Ui, display: Display) -> Box<dyn ActiveScreen> {
75+
let ui_text_style: MonoTextStyle<BinaryColor> =
76+
MonoTextStyle::new(&UI_TEXT_FONT, BinaryColor::On);
77+
78+
display.with_lock(|target| {
79+
Text::new(
80+
"USB Power Overload",
81+
row_anchor(0) - (row_anchor(1) - row_anchor(0)),
82+
ui_text_style,
83+
)
84+
.draw(target)
85+
.unwrap();
86+
87+
Text::new(
88+
"Disconnect devices or\nuse a powered hub.",
89+
row_anchor(1),
90+
ui_text_style,
91+
)
92+
.draw(target)
93+
.unwrap();
94+
95+
for (row, name) in &[(4, "Total"), (6, "Port 1"), (7, "Port 2"), (8, "Port 3")] {
96+
Text::new(name, row_anchor(*row), ui_text_style)
97+
.draw(target)
98+
.unwrap();
99+
}
100+
});
101+
102+
let mut widgets = WidgetContainer::new(display);
103+
104+
let ports = [
105+
(0, &ui.res.adc.usb_host_curr.topic, MAX_TOTAL_CURRENT),
106+
(2, &ui.res.adc.usb_host1_curr.topic, MAX_PORT_CURRENT),
107+
(3, &ui.res.adc.usb_host2_curr.topic, MAX_PORT_CURRENT),
108+
(4, &ui.res.adc.usb_host3_curr.topic, MAX_PORT_CURRENT),
109+
];
110+
111+
for (idx, current, max_current) in ports {
112+
let anchor_port = row_anchor(idx + 4);
113+
114+
widgets.push(|display| {
115+
DynamicWidget::bar(
116+
current.clone(),
117+
display,
118+
anchor_port + OFFSET_BAR,
119+
WIDTH_BAR,
120+
HEIGHT_BAR,
121+
Box::new(move |meas: &Measurement| meas.value / max_current),
122+
)
123+
});
124+
125+
widgets.push(|display| {
126+
DynamicWidget::text(
127+
current.clone(),
128+
display,
129+
anchor_port + OFFSET_VAL,
130+
Box::new(|meas: &Measurement| format!("{:>4.0}mA", meas.value * 1000.0)),
131+
)
132+
});
133+
}
134+
135+
Box::new(Active { widgets })
136+
}
137+
}
138+
139+
#[async_trait]
140+
impl ActiveScreen for Active {
141+
fn my_type(&self) -> Screen {
142+
Screen::Alert(SCREEN_TYPE)
143+
}
144+
145+
async fn deactivate(mut self: Box<Self>) -> Display {
146+
self.widgets.destroy().await
147+
}
148+
149+
fn input(&mut self, _ev: InputEvent) {}
150+
}

0 commit comments

Comments
 (0)