Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
067b0ed
Tis a beginning
andrewjstone Sep 22, 2022
fc05980
wip
andrewjstone Sep 23, 2022
9a7bfe3
First display
andrewjstone Sep 23, 2022
5355953
start stylin
andrewjstone Sep 23, 2022
14fee51
Add banner widget and use it for watermark
andrewjstone Sep 23, 2022
8aeedeb
Add some event handling and logging
andrewjstone Sep 23, 2022
3c10cf2
Some interactivity!
andrewjstone Sep 23, 2022
4a45c5c
better rack drawing
andrewjstone Sep 24, 2022
b752d21
properly draw rack sleds
andrewjstone Sep 25, 2022
2c1ad82
Start tracking rack state in prep for interactivity
andrewjstone Sep 27, 2022
71178c9
Add a Rack Widget with tab interaction
andrewjstone Sep 27, 2022
3a8b1df
fix tab indexing for power shelves
andrewjstone Sep 27, 2022
957666b
Draw some U.2 bays on the sleds
andrewjstone Sep 27, 2022
ff51e89
Better rack styling
andrewjstone Sep 27, 2022
488e674
new styling
andrewjstone Sep 27, 2022
81c54d6
start adding component modal
andrewjstone Sep 28, 2022
6251d6e
Start implementing MgsManager
andrewjstone Sep 28, 2022
1a40437
Start adding some power state info
andrewjstone Sep 29, 2022
e6fe8f2
Use much less cpu
andrewjstone Sep 29, 2022
7ed8b19
Draw power state in modal
andrewjstone Sep 29, 2022
7cfc58a
Add some inventory to the inventory modal
andrewjstone Sep 29, 2022
ca0fc27
Update inventory modal styling
andrewjstone Sep 29, 2022
267d23a
Center and underling inventory header
andrewjstone Sep 29, 2022
ef4284d
Change how we keep track of component rects in prep for mouse events
andrewjstone Sep 29, 2022
e91046a
Add mouse hover support
andrewjstone Sep 29, 2022
b0e9d88
Much better graceful degradation!
andrewjstone Sep 30, 2022
7c5f560
Mouse clicks work
andrewjstone Oct 3, 2022
d6aa794
Add splash screen animation
andrewjstone Oct 3, 2022
777fb12
Better splash styling and do not draw rack w/ modal
andrewjstone Oct 4, 2022
ae0c429
Delay splash screen painting for a few frames
andrewjstone Oct 5, 2022
a018a2f
Add hamburger icon
andrewjstone Oct 5, 2022
9c8b8ed
Add a help menu
andrewjstone Oct 5, 2022
8840a92
Add some help menu animation
andrewjstone Oct 6, 2022
1c62515
No tabs while help open
andrewjstone Oct 7, 2022
c246340
Start restructuring to remove modal and make component a screen
andrewjstone Oct 7, 2022
8287014
Move RackState inside global state and make Rack widget impl Widget i…
andrewjstone Oct 7, 2022
9598c52
HelpMenu is now contextual
andrewjstone Oct 7, 2022
b07d817
Move tracking of rack TabIndex into RackState
andrewjstone Oct 7, 2022
e37916f
Enable help access via key press
andrewjstone Oct 7, 2022
7493832
Start implementing ComponentScreen
andrewjstone Oct 7, 2022
4c80117
Handle events in ComponentScreen
andrewjstone Oct 7, 2022
b511a6f
Draw inventory
andrewjstone Oct 7, 2022
ed2367a
Remove unused files
andrewjstone Oct 7, 2022
019a648
Add screen transition button to ComponentScreen
andrewjstone Oct 7, 2022
9754657
Properly handle hover state on screen transitions
andrewjstone Oct 7, 2022
cff3fbc
Some fun
andrewjstone Oct 7, 2022
a5864bc
more knight rider mode
andrewjstone Oct 7, 2022
73b2491
Controls now have an Id
andrewjstone Oct 10, 2022
e53a107
get rid of warnings
andrewjstone Oct 10, 2022
cce6aee
Clippy clean
andrewjstone Oct 10, 2022
5a2c054
More consistent controls
andrewjstone Oct 10, 2022
8c47546
Dedupe a bunch of HelpMenu behavior
andrewjstone Oct 11, 2022
783850c
Remove unused dep
andrewjstone Oct 11, 2022
6f25185
fix knight rider mode
andrewjstone Oct 11, 2022
2cf0864
Start a README
andrewjstone Oct 11, 2022
cb56aa8
fix readme links
andrewjstone Oct 11, 2022
1313f43
More README
andrewjstone Oct 11, 2022
d6621b7
wrap up README for now
andrewjstone Oct 11, 2022
5466a0d
Cleanup README
andrewjstone Oct 12, 2022
6898392
Properly number rack components
andrewjstone Oct 13, 2022
a7dbb40
Fix outdated comment
andrewjstone Oct 13, 2022
fc2bcd9
fix comments in inventory
andrewjstone Oct 13, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ tools/cockroach*
/clickhouse/
/cockroachdb/
smf/nexus/root.json
core
70 changes: 70 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ members = [
"oximeter/oximeter-macro-impl",
"oximeter-client",
"test-utils",
"wicket",
]

default-members = [
Expand Down
18 changes: 18 additions & 0 deletions wicket/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "wicket"
version = "0.1.0"
edition = "2021"

[dependencies]
crossterm = { version = "0.25.0", features = ["event-stream"] }
tui = "0.19.0"
tokio = { version = "1.21.1", features = ["full"] }
anyhow = "1.0.65"
slog = { version = "2.7.0", features = [ "max_level_trace", "release_max_level_debug" ] }
slog-term = "2.9.0"
slog-async = "2.7.0"
futures = "0.3.24"

[[bin]]
name = "wicket"
doc = false
124 changes: 124 additions & 0 deletions wicket/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Overview

Wicket is a TUI built for operator usage at the technician port. It is intended to support a limited set of responsibilities including:
* Rack Initialization
* Boundary service setup
* Disaster Recovery
* Minimal rack update / emergency update

Wicket is built on top of [crossterm](https://github.com/crossterm-rs/crossterm)
and [tui-rs](https://github.com/fdehau/tui-rs), although the objects
themselves, and the placement of objects on the screen is mostly custom.

# Navigating

* `banners` - Files containing "banner-like" output using `#` characters for
glyph drawing
* `src/lib.rs` - Contains the main `Wizard` type which manages the UI,
downstream services, incoming events, and rendering
* `src/mgs.rs` - Source code for interacting with the [Management Gatewway
Service (MGS)](https://github.com/oxidecomputer/management-gateway-service)
* `src/inventory.rs` - Contains rack related components to show in the `Component` `Screen`
* `src/screens` - Each file represents a full terminal size view in the UI.
`Screen`s manage specific events and controls, and render `Widget`s.
* `src/widgets` - These are implementations of a [`tui::Widget`](https://github.com/fdehau/tui-rs/blob/master/src/widgets/mod.rs#L63-L68)
specific to wicket. Widgets are created only to be rendered immediately into a
[`tui::Frame`](https://github.com/fdehau/tui-rs/blob/9806217a6a4c240462bba3b32cb1bc59524f1bc2/src/terminal.rs#L58-L70).
Most widgets contain a reference to state which is mutated by input events, and
defined in the same file as the widget. The state often implements a `Control`
that allows it to be manipulated by the rest of the system in a unified
manner.

# Design

The main type of the wicket crate is the `Wizard`. The wizard is run by the `wicket` binary and is in charge of:
* Handling user input (mouse, keyboard, resize) events
* Sending requests to downstream services (MGS + RSS)
* Handling events from downstream services
* Managing the active screen and triggering terminal rendering

There is a main thread that runs an infinite loop in the `Wizard::mainloop`
method. The loop's job is to receive `Event`s from a single MPSC channel
and update internal state, either directly or by forwarding events to the
currently active screen by calling its `on` method. The active screen
processes events, updates its internal state (possibly including global state
passed in via the `on` method), and returns a list of `Action`s that instructs
the wizard what to do next. Currently there are only two types of `Action`s:

* `Action::Redraw` - which instructs the Wizard to call the active screen's `draw` method
* `Action::SwitchScreen(ScreenId)` - which instructs the wizard to transition between screens

It's important to notice that the internal state of the system is only updated
upon event receipt, and that a screen never processes an event that can
mutate state and render in the same method. This makes it very easy to test
the internal state mutations and behavior of a screen. It also means that all
drawing code is effectively stateless and fully immediate. While rendering
`Widget`s relies on the current state of the system, the state of the system
does not change at all during rendering, and so an immutable borrow can be
utilized for this state. This fits well with the `tui-rs` immediate drawing
paradigm where widgets are created right before rendering and passed by value
to the render function, which consumes them.

Besides the main thread, which runs `mainloop`, there is a separate tokio
runtime which is used to drive communications with MGS and RSS, and to manage
inputs and timers. Requests are driven by MGS and RSS clients and all replies
are handled by these clients in the tokio runtime. Any important information
in these replies is forwarded as an `Event` over a channel to be received
in `mainloop`. All `Event`s, whether respones from downstream services, user
input, or timer ticks, are sent over the same channel in an `Event` enum. This
keeps the `mainloop` simple and provides a total ordering of all events, which
can allow for easier debugging.

As mentioned above, a timer tick is sent as an `Event::Tick` message over
a channel to the mainloop. Timers currently fire every 25ms, and help drive
any animations. We don't redraw on every timer tick, since it's relatively
expensive to calculate widget positions, and since the screens themselves
return actions when they need to be redrawn. However, the wizard also doesn't
know when a screen animation is ongoing, and so it forwards all ticks to the
currently active screen which returns an `Action::Redraw` if the screen needs
to be redrawn.

# Screens, Widgets, and Controls

A `Screen` represents the current visual state of the `Wizard` to the user, and
what inputs are available to the user. Each `Screen` maintains its own internal
state which can be mutated in response to events delivered to it via its `on`
method. The `on` method also provides mutable access to a globl `State` which
is relevant across sceens. As mentioned above, a `Screen::draw` method is
called to render the current screen.

Screens abstract the terminal display, or tty, which itself can be modeled
as a buffer of characters or a rectangle with a width and height, and x
and y coordinates for the upper left hand corner. This rectangle can be
further divided into rectangles that can be independently styled and drawn.
These rectangles can be manipulated directly, but in the common case this
manipulation is abstracted into a drawable `Widget`. We have implemented
several of our own widgets includng the rack view. Each screen has manual
placement code for these widgets which allows full flexibility and responsive
design.

Widgets get consumed when drawn to the screen. And the placement code
determines where the widget rectangles are drawn. However, how do we change the
styling of the Widgets, such that we know when a mouse hover is occurring or a
button was clicked? For this purpose, we must track the minimal state required
to render the widgets and implement a `Control`. Controls provide two key
things: access to the rectangle, or `Rect`, that we need in order to draw the
widget on the next render, and a unique ID, that allows screens to keep track
of which control is currently `active` or being hovered over. For example, when
a mouse movement event comes in, we can use rectangle intersection to see if
the mouse is currently over a given Control, and mark it as `hovered'.


# What's left?

There are currently 3 screens implemented:
* Splash screen
* Rack view screen
* Component (Sled, Switch, PSC) view

Navigation and UI for these screens works well, but there is no backend
functionality implemented. All the inventory and power data shown in the
`Component` screen is fake. We also aren't currently really talking to the MGS
and RSS. Lastly, we don't have a way to take rack updates and install them, or
initialize the rack (including trust quorum). This is a lot of functionality
that will be implemented incrementally.
7 changes: 7 additions & 0 deletions wicket/banners/ox.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#####
## ##
## # ## ## ##
## # ## ## ##
## # ## ###
## ## ## ##
##### ## ##
7 changes: 7 additions & 0 deletions wicket/banners/oxide.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
##### ## ##
## ## ##
## # ## ## ## ### ### ## ####
## # ## ## ## ## ## ### ## ##
## # ## ### ## ## ## ########
## ## ## ## ## ## ### ##
##### ## ## ###### ### ## ####
13 changes: 13 additions & 0 deletions wicket/src/bin/wicket.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use std::error::Error;
use wicket::Wizard;

fn main() -> Result<(), Box<dyn Error>> {
let mut wizard = Wizard::new();
wizard.run()?;

Ok(())
}
Loading