Skip to content

Commit

Permalink
Merge pull request #9 from ssnover/flesh-out-control-page
Browse files Browse the repository at this point in the history
Flesh out control page
  • Loading branch information
ssnover authored May 4, 2024
2 parents 6c5e744 + ea96c84 commit 88a827b
Show file tree
Hide file tree
Showing 38 changed files with 1,102 additions and 312 deletions.
6 changes: 3 additions & 3 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
// Uncomment a target corresponding to the target you're working on (or leave all commented if you're not cross-compiling)
//"rust-analyzer.cargo.target": "thumbv7em-none-eabi",
//"rust-analyzer.cargo.target": "thumbv6m-none-eabi",
//"rust-analyzer.cargo.target": "wasm32-unknown-unknown",
"rust-analyzer.cargo.target": "wasm32-unknown-unknown",
"rust-analyzer.linkedProjects": [
// Uncomment for the project you're actively working on
"Cargo.toml",
//"Cargo.toml",
//"coproc-embassy/nano-ble-rgb/Cargo.toml",
//"coproc-embassy/pi-pico-rgb/Cargo.toml",
//"console/frontend/Cargo.toml",
"console/frontend/Cargo.toml",
],
}
3 changes: 3 additions & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ exclude = ["app-sdk", "coproc-embassy", "example-apps", "simulator/frontend", "c
# Internal
megabit-runner-msgs = { path = "runner/runner_msgs" }
megabit-serial-protocol = { path = "serial-protocol" }
megabit-utils = { path = "utils" }
megabit-utils = { path = "utils", features = ["web-server"] }
# External
anyhow = { version = "1" }
async-channel = { version = "2.1" }
Expand Down
49 changes: 49 additions & 0 deletions console/frontend/Cargo.lock

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

2 changes: 2 additions & 0 deletions console/frontend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ gloo-net = { version = "0.5", features = ["websocket"] }
js-sys = "0.3"
log = { version = "0.4" }
megabit-runner-msgs = { path = "../../runner/runner_msgs" }
megabit-utils = { path = "../../utils" }
serde_json = "1"
wasm-bindgen = "0.2"
wasm-bindgen-futures = { version = "0.4" }
wasm-logger = { version = "0.2" }
web-sys = { version = "0.3", features = ["CanvasRenderingContext2d", "HtmlCanvasElement", "HtmlDivElement", "HtmlElement", "ImageData"]}
yew = { version = "0.21", features = ["csr"] }
yew-router = "0.18"
4 changes: 2 additions & 2 deletions console/frontend/index.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<!doctype html>
<html lang="en">
<html lang="en" style="margin: 0; height: 100%">
<head>
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
<link data-trunk rel="css" href="vendor/bootstrap-5.3.3-dist/css/bootstrap.min.css"/>
</head>
<body>
<body class="text-light bg-dark" style="margin: 0; height: 100%">
<script data-trunk src="vendor/bootstrap-5.3.3-dist/js/bootstrap.min.js"></script>
</body>
</html>
10 changes: 5 additions & 5 deletions console/frontend/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use websocket_provider::WebsocketProvider;
use providers::ConsoleProviders;
use yew::functional::*;
use yew::prelude::*;
use yew_router::prelude::*;

mod providers;
mod tabs;
mod utils;
mod websocket_provider;

#[function_component(App)]
fn app() -> Html {
Expand All @@ -28,13 +28,13 @@ fn switch(routes: Route) -> Html {
};

html! {
<WebsocketProvider>
<ConsoleProviders>
{ page_contents }
</WebsocketProvider>
</ConsoleProviders>
}
}

fn main() {
wasm_logger::init(wasm_logger::Config::new(log::Level::Info));
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
yew::Renderer::<App>::new().render();
}
44 changes: 44 additions & 0 deletions console/frontend/src/providers/app_config_provider.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use yew::{
function_component, hook, html, use_context, Children, ContextProvider, Html, Properties,
};

const DEFAULT_WIDTH: u32 = 64;
const DEFAULT_HEIGHT: u32 = 32;

#[hook]
pub fn use_app_config() -> AppConfig {
use_context().unwrap()
}

#[derive(Clone, PartialEq)]
pub struct AppConfig {
display_width: u32,
display_height: u32,
}

impl AppConfig {
pub fn width(&self) -> u32 {
self.display_width
}

pub fn height(&self) -> u32 {
self.display_height
}
}

#[function_component]
pub fn AppConfigProvider(props: &AppConfigProviderProps) -> Html {
let context = AppConfig {
display_width: DEFAULT_WIDTH,
display_height: DEFAULT_HEIGHT,
};

html! {
<ContextProvider<AppConfig> {context}>{props.children.clone()}</ContextProvider<AppConfig>>
}
}

#[derive(Properties, PartialEq)]
pub struct AppConfigProviderProps {
pub children: Children,
}
25 changes: 25 additions & 0 deletions console/frontend/src/providers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
mod app_config_provider;
mod msg_subscriber_provider;
mod websocket_provider;
pub use app_config_provider::{use_app_config, AppConfigProvider};
pub use msg_subscriber_provider::{use_subscription_manager, MsgSubscriberProvider};
pub use websocket_provider::{use_websocket, WebsocketProvider};
use yew::{function_component, html, Children, Html, Properties};

#[function_component(ConsoleProviders)]
pub fn console_providers(props: &ConsoleProvidersProps) -> Html {
html! {
<MsgSubscriberProvider>
<WebsocketProvider>
<AppConfigProvider>
{ props.children.clone() }
</AppConfigProvider>
</WebsocketProvider>
</MsgSubscriberProvider>
}
}

#[derive(PartialEq, Properties)]
pub struct ConsoleProvidersProps {
pub children: Children,
}
71 changes: 71 additions & 0 deletions console/frontend/src/providers/msg_subscriber_provider.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use megabit_runner_msgs::ConsoleMessage;
use std::{cell::RefCell, collections::HashMap, rc::Rc};
use yew::{
function_component, hook, html, use_context, Callback, Children, ContextProvider, Html,
Properties,
};

#[hook]
pub fn use_subscription_manager() -> SubscriptionManager {
use_context().unwrap()
}

type SubscriptionKey = &'static str;
type MsgCallback = Callback<ConsoleMessage>;
type SubscriptionRegistry = HashMap<SubscriptionKey, Vec<(String, MsgCallback)>>;

#[derive(Clone, PartialEq)]
pub struct SubscriptionManager {
registry: Rc<RefCell<SubscriptionRegistry>>,
}

impl SubscriptionManager {
pub fn subscribe(&self, client: &str, message_kind: &'static str, callback: MsgCallback) {
log::debug!("Adding subscription for kind {message_kind} for client {client}");
let mut registry = self.registry.borrow_mut();
match registry.get_mut(message_kind) {
Some(subscriptions) => {
if let Some((_, existing_cb)) = subscriptions
.iter_mut()
.find(|(sub_client, _)| client == sub_client.as_str())
{
*existing_cb = callback;
} else {
subscriptions.push((client.into(), callback));
}
}
None => {
let _ = registry.insert(message_kind, vec![(client.to_string(), callback)]);
}
}
}

pub fn handle_message(&self, msg: ConsoleMessage) -> bool {
log::debug!("Handling message: {}", msg.as_ref());
let registry = self.registry.borrow();
if let Some(subscriptions) = registry.get(msg.as_ref()) {
for (_, cb) in subscriptions {
cb.emit(msg.clone());
}
true
} else {
false
}
}
}

#[function_component]
pub fn MsgSubscriberProvider(props: &Props) -> Html {
let context = SubscriptionManager {
registry: Rc::new(RefCell::new(HashMap::new())),
};

html! {
<ContextProvider<SubscriptionManager> {context}>{props.children.clone()}</ContextProvider<SubscriptionManager>>
}
}

#[derive(Properties, PartialEq)]
pub struct Props {
pub children: Children,
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ use yew::{
ContextProvider, Html, Properties, UseStateHandle,
};

use super::{msg_subscriber_provider::SubscriptionManager, use_subscription_manager};

#[hook]
pub fn use_websocket() -> WebsocketHandle {
use_context().unwrap()
Expand All @@ -32,6 +34,8 @@ impl WebsocketHandle {

#[function_component]
pub fn WebsocketProvider(props: &WebsocketProviderProps) -> Html {
let subscription_manager = use_subscription_manager();

let connection = use_state(|| {
let endpoint = if let (Ok(hostname), Ok(port)) =
(window().location().hostname(), window().location().port())
Expand All @@ -54,7 +58,7 @@ pub fn WebsocketProvider(props: &WebsocketProviderProps) -> Html {
};
move || {
spawn_local(async move {
start_ws_context(connection).await;
start_ws_context(connection, subscription_manager).await;
});
}
});
Expand Down Expand Up @@ -107,19 +111,19 @@ impl WebsocketConnection {
let mut reader = self.inner.1.try_borrow_mut().unwrap();
reader.next().await
}

pub fn route_message(&self, msg: Vec<u8>) {
let msg = std::str::from_utf8(msg.as_slice()).unwrap_or("Non-UTF8 str");
log::info!("Received msg: {msg}");
}
}

async fn start_ws_context(connection: WebsocketConnection) {
async fn start_ws_context(
connection: WebsocketConnection,
subscription_manager: SubscriptionManager,
) {
loop {
if let Some(msg) = connection.read().await {
match msg {
Ok(Message::Bytes(msg)) => {
connection.route_message(msg);
if let Ok(msg) = serde_json::from_slice(&msg[..]) {
subscription_manager.handle_message(msg);
}
}
Ok(_) => {
log::debug!("Got a non-bytes WebSocket message");
Expand Down
Loading

0 comments on commit 88a827b

Please sign in to comment.