diff --git a/crates/communication/src/client/communication.rs b/crates/communication/src/client/communication.rs index a4912fe2f5..b7fb8dd395 100644 --- a/crates/communication/src/client/communication.rs +++ b/crates/communication/src/client/communication.rs @@ -24,7 +24,6 @@ use super::{ id_tracker::id_tracker, output_subscription_manager::{self, output_subscription_manager}, responder::responder, - CyclerOutput, }; #[derive(Clone)] @@ -113,14 +112,14 @@ impl Communication { pub async fn subscribe_output( &self, - output: CyclerOutput, + path: Path, format: Format, ) -> (Uuid, Receiver) { let (subscriber_sender, subscriber_receiver) = mpsc::channel(10); let (response_sender, response_receiver) = oneshot::channel(); self.output_subscription_manager .send(output_subscription_manager::Message::Subscribe { - output, + path, format, subscriber: subscriber_sender, response_sender, diff --git a/crates/communication/src/client/mod.rs b/crates/communication/src/client/mod.rs index 50e21e83cf..4108f9893d 100644 --- a/crates/communication/src/client/mod.rs +++ b/crates/communication/src/client/mod.rs @@ -10,4 +10,4 @@ mod types; pub use crate::client::communication::Communication; pub use connector::ConnectionStatus; -pub use types::{Cycler, CyclerOutput, HierarchyType, Output, OutputHierarchy, SubscriberMessage}; +pub use types::{HierarchyType, OutputHierarchy, SubscriberMessage}; diff --git a/crates/communication/src/client/output_subscription_manager.rs b/crates/communication/src/client/output_subscription_manager.rs index 91ff376ff5..dc3b4e05ea 100644 --- a/crates/communication/src/client/output_subscription_manager.rs +++ b/crates/communication/src/client/output_subscription_manager.rs @@ -11,15 +11,14 @@ use uuid::Uuid; use crate::{ client::{ id_tracker::{self, get_message_id}, - responder, Output, SubscriberMessage, + responder, SubscriberMessage, }, messages::{ - Fields, Format, OutputsRequest, Request, - TextualDataOrBinaryReference::{self, BinaryReference, TextualData}, + Fields, Format, OutputsRequest, Path, Request, TextualDataOrBinaryReference::{self, BinaryReference, TextualData} }, }; -use super::{responder::Response, CyclerOutput}; +use super::responder::Response; #[derive(Debug)] pub enum Message { @@ -28,7 +27,7 @@ pub enum Message { }, Disconnect, Subscribe { - output: CyclerOutput, + path: Path, format: Format, subscriber: mpsc::Sender, response_sender: oneshot::Sender, @@ -52,9 +51,9 @@ pub enum Message { #[derive(Default)] struct SubscriptionManager { - ids_to_outputs: HashMap, + ids_to_outputs: HashMap, outputs_to_subscribers: - HashMap<(CyclerOutput, Format), HashMap>>, + HashMap<(Path, Format), HashMap>>, } pub async fn output_subscription_manager( @@ -68,7 +67,7 @@ pub async fn output_subscription_manager( let mut requester = None; let mut fields = None; let mut binary_data_waiting_for_references: HashMap> = HashMap::new(); - let mut binary_references_waiting_for_data: HashMap = HashMap::new(); + let mut binary_references_waiting_for_data: HashMap = HashMap::new(); while let Some(message) = receiver.recv().await { match message { @@ -107,7 +106,7 @@ pub async fn output_subscription_manager( manager.ids_to_outputs.clear(); } Message::Subscribe { - output, + path, format, subscriber: output_sender, response_sender, @@ -118,7 +117,7 @@ pub async fn output_subscription_manager( add_subscription( &mut manager, uuid, - output, + path, format, output_sender, &id_tracker, @@ -274,7 +273,7 @@ async fn query_output_fields( async fn add_subscription( manager: &mut SubscriptionManager, uuid: Uuid, - output: CyclerOutput, + path: Path, format: Format, output_sender: mpsc::Sender, id_tracker: &mpsc::Sender, @@ -283,7 +282,7 @@ async fn add_subscription( ) { match manager .outputs_to_subscribers - .entry((output.clone(), format)) + .entry((path.clone(), format)) { Entry::Occupied(mut entry) => { entry.get_mut().insert(uuid, output_sender); @@ -291,7 +290,7 @@ async fn add_subscription( Entry::Vacant(entry) => { if let Some(requester) = requester { if let Some(subscription_id) = subscribe( - output.clone(), + path.clone(), format, vec![output_sender.clone()], id_tracker, @@ -302,7 +301,7 @@ async fn add_subscription( { manager .ids_to_outputs - .insert(subscription_id, (output, format)); + .insert(subscription_id, (path, format)); } }; entry.insert(HashMap::new()).insert(uuid, output_sender); @@ -311,7 +310,7 @@ async fn add_subscription( } async fn subscribe( - output: CyclerOutput, + path: Path, format: Format, subscribers: Vec>, id_tracker: &mpsc::Sender, @@ -330,13 +329,8 @@ async fn subscribe( error!("{error}"); return None; } - let path = match output.output { - Output::Main { path } => format!("main_outputs.{path}"), - Output::Additional { path } => format!("additional_outputs.{path}"), - }; let request = Request::Outputs(OutputsRequest::Subscribe { id: message_id, - cycler_instance: output.cycler.to_string(), path, format, }); diff --git a/crates/communication/src/client/types.rs b/crates/communication/src/client/types.rs index 030b0ae7d4..8e6f8fda0b 100644 --- a/crates/communication/src/client/types.rs +++ b/crates/communication/src/client/types.rs @@ -1,85 +1,9 @@ -use std::{ - collections::BTreeMap, - fmt::{self, Display, Formatter}, - str::FromStr, -}; +use std::collections::BTreeMap; -use color_eyre::{ - eyre::{bail, eyre}, - Report, Result, -}; -use serde::{Deserialize, Serialize}; +use serde::Deserialize; use serde_json::Value; -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub struct CyclerOutput { - pub cycler: Cycler, - pub output: Output, -} - -impl FromStr for CyclerOutput { - type Err = Report; - - fn from_str(string: &str) -> Result { - let (cycler_str, output_str) = string.split_once('.').ok_or_else(|| { - eyre!("expected '.' in subscription path (e.g. 'control.main.foo_bar')") - })?; - let cycler = Cycler::from_str(cycler_str)?; - let (output_str, path) = output_str.split_once('.').ok_or_else(|| { - eyre!("expected '.' after output source (e.g. 'control.main.foo_bar')") - })?; - let output = match output_str { - "main" | "main_outputs" => Output::Main { - path: path.to_string(), - }, - "additional" | "additional_outputs" => Output::Additional { - path: path.to_string(), - }, - _ => bail!("unknown output '{output_str}'"), - }; - Ok(CyclerOutput { cycler, output }) - } -} - -#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -pub enum Cycler { - Control, - VisionTop, - VisionBottom, - BehaviorSimulator, -} - -impl Display for Cycler { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - Cycler::Control => f.write_str("Control"), - Cycler::VisionTop => f.write_str("VisionTop"), - Cycler::VisionBottom => f.write_str("VisionBottom"), - Cycler::BehaviorSimulator => f.write_str("BehaviorSimulator"), - } - } -} - -impl FromStr for Cycler { - type Err = Report; - - fn from_str(string: &str) -> Result { - Ok(match string { - "Control" => Cycler::Control, - "VisionTop" => Cycler::VisionTop, - "VisionBottom" => Cycler::VisionBottom, - "BehaviorSimulator" => Cycler::BehaviorSimulator, - _ => bail!("unknown cycler '{string}'"), - }) - } -} - -#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] -#[serde(tag = "type")] -pub enum Output { - Main { path: String }, - Additional { path: String }, -} +use crate::messages::Path; #[derive(Debug, Clone)] pub enum SubscriberMessage { @@ -123,6 +47,6 @@ pub struct OutputHierarchy { #[derive(Debug, Deserialize)] pub struct SubscribedOutput { - pub output: Output, + pub output: Path, pub data: Value, } diff --git a/crates/communication/src/messages.rs b/crates/communication/src/messages.rs index dcfb557476..dce44051eb 100644 --- a/crates/communication/src/messages.rs +++ b/crates/communication/src/messages.rs @@ -72,13 +72,11 @@ pub enum OutputsRequest { }, GetNext { id: usize, - cycler_instance: CyclerInstance, path: Path, format: Format, }, Subscribe { id: usize, - cycler_instance: CyclerInstance, path: Path, format: Format, }, diff --git a/crates/communication/src/server/outputs/provider.rs b/crates/communication/src/server/outputs/provider.rs index dd1c06920a..eb3050a1f7 100644 --- a/crates/communication/src/server/outputs/provider.rs +++ b/crates/communication/src/server/outputs/provider.rs @@ -102,18 +102,20 @@ where } OutputsRequest::GetNext { id, - cycler_instance: received_cycler_instance, - path, + path: received_path, format, } | OutputsRequest::Subscribe { id, - cycler_instance: received_cycler_instance, - path, + path: received_path, format, } => { + let (received_cycler_instance, path) = received_path + .split_once('.') + .expect("path does not contain cycler"); + assert_eq!(cycler_instance, received_cycler_instance); - if Outputs::exists(&path) { + if Outputs::exists(path) { match subscriptions.entry((request.client.clone(), id)) { Entry::Occupied(_) => { let error_message = format!("already subscribed with id {id}"); @@ -139,7 +141,7 @@ where } Entry::Vacant(entry) => { entry.insert(Subscription { - path, + path: path.to_string(), format, once: is_get_next, }); @@ -533,8 +535,7 @@ mod tests { .send(ClientRequest { request: OutputsRequest::Subscribe { id: ID, - cycler_instance: cycler_instance.clone(), - path: path.clone(), + path: format!("{cycler_instance}.{path}"), format, }, client: Client { @@ -570,8 +571,7 @@ mod tests { .send(ClientRequest { request: OutputsRequest::Subscribe { id: ID, - cycler_instance, - path: path.clone(), + path: format!("{cycler_instance}.{path}"), format, }, client: Client { @@ -634,8 +634,7 @@ mod tests { .send(ClientRequest { request: OutputsRequest::Subscribe { id: ID, - cycler_instance: cycler_instance.clone(), - path: path.clone(), + path: format!("{cycler_instance}.{path}"), format, }, client: Client { @@ -671,8 +670,7 @@ mod tests { .send(ClientRequest { request: OutputsRequest::Subscribe { id: ID, - cycler_instance, - path: path.clone(), + path: format!("{cycler_instance}.{path}"), format, }, client: Client { @@ -735,8 +733,7 @@ mod tests { .send(ClientRequest { request: OutputsRequest::Subscribe { id: 42, - cycler_instance: cycler_instance.clone(), - path: path.clone(), + path: format!("{cycler_instance}.{path}"), format, }, client: Client { @@ -772,8 +769,7 @@ mod tests { .send(ClientRequest { request: OutputsRequest::Subscribe { id: 1337, - cycler_instance, - path: path.clone(), + path: format!("{cycler_instance}.{path}"), format, }, client: Client { @@ -889,8 +885,7 @@ mod tests { .send(ClientRequest { request: OutputsRequest::Subscribe { id: SUBSCRIPTION_ID, - cycler_instance: cycler_instance.to_string(), - path: path.clone(), + path: format!("{cycler_instance}.{path}"), format: Format::Textual, }, client: Client { @@ -1013,8 +1008,7 @@ mod tests { .send(ClientRequest { request: OutputsRequest::Subscribe { id: 42, - cycler_instance: cycler_instance.to_string(), - path: path.clone(), + path: format!("{cycler_instance}.{path}"), format: Format::Textual, }, client: Client { @@ -1129,8 +1123,7 @@ mod tests { .send(ClientRequest { request: OutputsRequest::Subscribe { id: SUBSCRIPTION_ID, - cycler_instance: cycler_instance.to_string(), - path: path.clone(), + path: format!("{cycler_instance}.{path}"), format: Format::Textual, }, client: Client { @@ -1256,8 +1249,7 @@ mod tests { .send(ClientRequest { request: OutputsRequest::Subscribe { id: SUBSCRIPTION_ID, - cycler_instance: cycler_instance.to_string(), - path: path.clone(), + path: format!("{cycler_instance}.{path}"), format: Format::Binary, }, client: Client { @@ -1391,8 +1383,7 @@ mod tests { .send(ClientRequest { request: OutputsRequest::Subscribe { id: SUBSCRIPTION_ID, - cycler_instance: cycler_instance.to_string(), - path: path.clone(), + path: format!("{cycler_instance}.{path}"), format: Format::Textual, }, client: Client { @@ -1429,8 +1420,7 @@ mod tests { .send(ClientRequest { request: OutputsRequest::Subscribe { id: SUBSCRIPTION_ID, - cycler_instance: cycler_instance.to_string(), - path: path.clone(), + path: format!("{cycler_instance}.{path}"), format: Format::Textual, }, client: Client { @@ -1613,8 +1603,7 @@ mod tests { .send(ClientRequest { request: OutputsRequest::GetNext { id: SUBSCRIPTION_ID, - cycler_instance: cycler_instance.to_string(), - path: path.clone(), + path: format!("{cycler_instance}.{path}"), format: Format::Textual, }, client: Client { @@ -1691,8 +1680,7 @@ mod tests { .send(ClientRequest { request: OutputsRequest::GetNext { id: SUBSCRIPTION_ID, - cycler_instance: cycler_instance.to_string(), - path: path.clone(), + path: format!("{cycler_instance}.{path}"), format: Format::Binary, }, client: Client { diff --git a/crates/communication/src/server/outputs/router.rs b/crates/communication/src/server/outputs/router.rs index c65d6d9e70..6abf68a0de 100644 --- a/crates/communication/src/server/outputs/router.rs +++ b/crates/communication/src/server/outputs/router.rs @@ -7,7 +7,9 @@ use tokio::{ }; use crate::{ - messages::{OutputsRequest, Path, Response, TextualOutputsResponse, TextualResponse}, + messages::{ + OutputsRequest, Path, Response, TextualOutputsResponse, TextualResponse, + }, server::{client::Client, client_request::ClientRequest}, }; @@ -67,19 +69,36 @@ async fn handle_request( .await .expect("receiver should always wait for all senders"); } - OutputsRequest::GetNext { - id, - cycler_instance, - .. - } - | OutputsRequest::Subscribe { - id, - cycler_instance, - .. - } => { + OutputsRequest::GetNext { id, path, .. } | OutputsRequest::Subscribe { id, path, .. } => { + let cycler_instance = match path.split_once('.') { + Some((cycler_instance, _)) => cycler_instance, + None => { + let error_message = format!("cannot parse path {path}"); + request + .client + .response_sender + .send(Response::Textual(TextualResponse::Outputs( + if matches!(request.request, OutputsRequest::GetNext { .. }) { + TextualOutputsResponse::GetNext { + id: *id, + result: Err(error_message), + } + } else { + TextualOutputsResponse::Subscribe { + id: *id, + result: Err(error_message), + } + }, + ))) + .await + .expect("receiver should always wait for all senders"); + return; + } + }; + if matches!(request.request, OutputsRequest::Subscribe { .. }) { cached_cycler_instances - .insert((request.client.clone(), *id), cycler_instance.clone()); + .insert((request.client.clone(), *id), cycler_instance.to_owned()); } match request_channels_of_cyclers.get(cycler_instance) { @@ -245,8 +264,7 @@ mod tests { .send(Request::ClientRequest(ClientRequest { request: OutputsRequest::GetNext { id: 42, - cycler_instance: "CyclerInstance".to_string(), - path: "a.b.c".to_string(), + path: "CyclerInstance.a.b.c".to_string(), format: Format::Textual, }, client: Client { @@ -296,8 +314,7 @@ mod tests { let sent_client_request = ClientRequest { request: OutputsRequest::GetNext { id: 42, - cycler_instance: "CyclerInstance".to_string(), - path: "a.b.c".to_string(), + path: "CyclerInstance.a.b.c".to_string(), format: Format::Textual, }, client: Client { @@ -340,8 +357,7 @@ mod tests { let sent_client_request = ClientRequest { request: OutputsRequest::Subscribe { id: 42, - cycler_instance: "CyclerInstance".to_string(), - path: "a.b.c".to_string(), + path: "CyclerInstance.a.b.c".to_string(), format: Format::Textual, }, client: client.clone(), diff --git a/tools/fanta/src/main.rs b/tools/fanta/src/main.rs index 8cc24cfc1c..a5dcb52995 100644 --- a/tools/fanta/src/main.rs +++ b/tools/fanta/src/main.rs @@ -1,9 +1,7 @@ -use std::str::FromStr; - use clap::Parser; use color_eyre::{eyre::bail, Result}; use communication::{ - client::{Communication, CyclerOutput, SubscriberMessage}, + client::{Communication, SubscriberMessage}, messages::Format, }; use log::{error, info}; @@ -25,7 +23,7 @@ async fn main() -> Result<()> { setup_logger()?; let arguments = CommandlineArguments::parse(); - let output_to_subscribe = CyclerOutput::from_str(&arguments.path)?; + let output_to_subscribe = arguments.path; let communication = Communication::new(Some(format!("ws://{}:1337", arguments.address)), true); let (_uuid, mut receiver) = communication .subscribe_output(output_to_subscribe, Format::Textual) diff --git a/tools/twix/src/image_buffer.rs b/tools/twix/src/image_buffer.rs index 9adf08c917..abc974d558 100644 --- a/tools/twix/src/image_buffer.rs +++ b/tools/twix/src/image_buffer.rs @@ -1,4 +1,4 @@ -use communication::client::{Communication, CyclerOutput, SubscriberMessage}; +use communication::client::{Communication, SubscriberMessage}; use log::error; use tokio::{ select, spawn, @@ -23,7 +23,7 @@ pub struct ImageBuffer { } impl ImageBuffer { - pub fn new(communication: Communication, output: CyclerOutput) -> Self { + pub fn new(communication: Communication, output: String) -> Self { let (command_sender, command_receiver) = mpsc::channel(10); spawn(async move { let (uuid, receiver) = communication diff --git a/tools/twix/src/nao.rs b/tools/twix/src/nao.rs index a49d82b1a5..555ce12f06 100644 --- a/tools/twix/src/nao.rs +++ b/tools/twix/src/nao.rs @@ -1,7 +1,7 @@ use std::{collections::BTreeSet, sync::Mutex}; use communication::{ - client::{Communication, ConnectionStatus, CyclerOutput}, + client::{Communication, ConnectionStatus}, messages::{Fields, Path}, }; @@ -57,17 +57,17 @@ impl Nao { ); } - pub fn subscribe_output(&self, output: CyclerOutput) -> ValueBuffer { + pub fn subscribe_output(&self, output: impl ToString) -> ValueBuffer { let _guard = self.runtime.enter(); - ValueBuffer::output(self.communication.clone(), output) + ValueBuffer::output(self.communication.clone(), output.to_string()) } - pub fn subscribe_image(&self, output: CyclerOutput) -> ImageBuffer { + pub fn subscribe_image(&self, output: impl ToString) -> ImageBuffer { let _guard = self.runtime.enter(); - ImageBuffer::new(self.communication.clone(), output) + ImageBuffer::new(self.communication.clone(), output.to_string()) } - pub fn subscribe_parameter(&self, path: &str) -> ValueBuffer { + pub fn subscribe_parameter(&self, path: impl ToString) -> ValueBuffer { let _guard = self.runtime.enter(); ValueBuffer::parameter(self.communication.clone(), path.to_string()) } diff --git a/tools/twix/src/panels/behavior_simulator.rs b/tools/twix/src/panels/behavior_simulator.rs index 15c67316f9..c2bd46ac22 100644 --- a/tools/twix/src/panels/behavior_simulator.rs +++ b/tools/twix/src/panels/behavior_simulator.rs @@ -1,6 +1,5 @@ -use std::{str::FromStr, sync::Arc}; +use std::sync::Arc; -use communication::client::CyclerOutput; use eframe::egui::{Response, Slider, Ui, Widget}; use serde_json::Value; use tokio::sync::mpsc; @@ -28,9 +27,8 @@ impl Panel for BehaviorSimulatorPanel { let (update_notify_sender, update_notify_receiver) = mpsc::channel(1); value_buffer.listen_to_updates(update_notify_sender); - let frame_count = nao.subscribe_output( - CyclerOutput::from_str("BehaviorSimulator.main_outputs.frame_count").unwrap(), - ); + let frame_count = nao.subscribe_output("BehaviorSimulator.main_outputs.frame_count"); + Self { nao, update_notify_receiver, diff --git a/tools/twix/src/panels/image/cycler_selector.rs b/tools/twix/src/panels/image/cycler_selector.rs index 6d2df8b293..0bf1621f01 100644 --- a/tools/twix/src/panels/image/cycler_selector.rs +++ b/tools/twix/src/panels/image/cycler_selector.rs @@ -1,13 +1,14 @@ -use communication::client::Cycler; use eframe::egui::{ComboBox, Response, Ui, Widget}; +use super::overlay::VisionCycler; + #[derive(Debug)] pub struct VisionCyclerSelector { - cycler: Cycler, + cycler: VisionCycler, } impl VisionCyclerSelector { - pub fn selected_cycler(&self) -> Cycler { + pub fn selected_cycler(&self) -> VisionCycler { self.cycler } } @@ -15,13 +16,13 @@ impl VisionCyclerSelector { impl Default for VisionCyclerSelector { fn default() -> Self { Self { - cycler: Cycler::VisionTop, + cycler: VisionCycler::VisionTop, } } } impl VisionCyclerSelector { - pub fn new(cycler: Cycler) -> Self { + pub fn new(cycler: VisionCycler) -> Self { Self { cycler } } } @@ -33,13 +34,13 @@ impl Widget for &mut VisionCyclerSelector { .selected_text(format!("{:?}", self.cycler)) .show_ui(ui, |ui| { if ui - .selectable_value(&mut self.cycler, Cycler::VisionTop, "VisionTop") + .selectable_value(&mut self.cycler, VisionCycler::VisionTop, "VisionTop") .clicked() { camera_selection_changed = true; }; if ui - .selectable_value(&mut self.cycler, Cycler::VisionBottom, "VisionBottom") + .selectable_value(&mut self.cycler, VisionCycler::VisionBottom, "VisionBottom") .clicked() { camera_selection_changed = true; diff --git a/tools/twix/src/panels/image/mod.rs b/tools/twix/src/panels/image/mod.rs index 3fc36403df..fc45271635 100644 --- a/tools/twix/src/panels/image/mod.rs +++ b/tools/twix/src/panels/image/mod.rs @@ -1,7 +1,6 @@ use std::{str::FromStr, sync::Arc}; use color_eyre::{eyre::eyre, Result}; -use communication::client::{Cycler, CyclerOutput, Output}; use eframe::{ egui::{ComboBox, Image, Response, TextureOptions, Ui, Widget}, epaint::Vec2, @@ -19,7 +18,10 @@ use crate::{ twix_painter::{CoordinateSystem, TwixPainter}, }; -use self::{cycler_selector::VisionCyclerSelector, overlay::Overlays}; +use self::{ + cycler_selector::VisionCyclerSelector, + overlay::{Overlays, VisionCycler}, +}; mod cycler_selector; mod overlay; @@ -28,18 +30,12 @@ mod overlays; #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy)] enum ImageKind { YCbCr422, - Luminance, } impl ImageKind { - fn as_output(&self) -> Output { + fn as_output(&self) -> String { match self { - ImageKind::YCbCr422 => Output::Main { - path: "image.jpeg".to_string(), - }, - ImageKind::Luminance => Output::Additional { - path: "robot_detection.luminance_image.jpeg".to_string(), - }, + ImageKind::YCbCr422 => "main_outputs.image.jpeg".to_string(), } } } @@ -59,27 +55,21 @@ impl Panel for ImagePanel { let cycler = value .and_then(|value| value.get("cycler")) .and_then(|value| value.as_str()) - .map(Cycler::from_str) + .map(VisionCycler::from_str) .and_then(|cycler| match cycler { - Ok(cycler @ (Cycler::VisionTop | Cycler::VisionBottom)) => Some(cycler), - Ok(cycler) => { - error!("Invalid vision cycler: {cycler}"); - None - } + Ok(cycler) => Some(cycler), Err(error) => { error!("{error}"); None } }) - .unwrap_or(Cycler::VisionTop); + .unwrap_or(VisionCycler::VisionTop); let image_kind = value .and_then(|value| value.get("image_kind")) .and_then(|value| from_value(value.clone()).ok()) .unwrap_or(ImageKind::YCbCr422); - let output = CyclerOutput { - cycler, - output: image_kind.as_output(), - }; + let output = format!("{}.{}", cycler.to_string(), image_kind.as_output()); + let image_buffer = nao.subscribe_image(output); let cycler_selector = VisionCyclerSelector::new(cycler); let overlays = Overlays::new( @@ -113,10 +103,11 @@ impl Widget for &mut ImagePanel { fn ui(self, ui: &mut Ui) -> Response { ui.horizontal(|ui| { if self.cycler_selector.ui(ui).changed() { - let output = CyclerOutput { - cycler: self.cycler_selector.selected_cycler(), - output: self.image_kind.as_output(), - }; + let output = format!( + "{}.{}", + self.cycler_selector.selected_cycler().to_string(), + self.image_kind.as_output(), + ); self.image_buffer = self.nao.subscribe_image(output); self.overlays .update_cycler(self.cycler_selector.selected_cycler()); @@ -131,18 +122,13 @@ impl Widget for &mut ImagePanel { { image_selection_changed = true; }; - if ui - .selectable_value(&mut self.image_kind, ImageKind::Luminance, "Luminance") - .changed() - { - image_selection_changed = true; - } }); if image_selection_changed { - let output = CyclerOutput { - cycler: self.cycler_selector.selected_cycler(), - output: self.image_kind.as_output(), - }; + let output = format!( + "{}.{}", + self.cycler_selector.selected_cycler().to_string(), + self.image_kind.as_output(), + ); self.image_buffer = self.nao.subscribe_image(output); self.overlays .update_cycler(self.cycler_selector.selected_cycler()); diff --git a/tools/twix/src/panels/image/overlay.rs b/tools/twix/src/panels/image/overlay.rs index bbddac41ec..87374c89f2 100644 --- a/tools/twix/src/panels/image/overlay.rs +++ b/tools/twix/src/panels/image/overlay.rs @@ -1,7 +1,6 @@ -use std::sync::Arc; +use std::{str::FromStr, sync::Arc}; -use color_eyre::Result; -use communication::client::Cycler; +use color_eyre::{Report, Result}; use convert_case::Casing; use eframe::egui::Ui; use serde_json::{json, Value}; @@ -10,9 +9,37 @@ use crate::{nao::Nao, twix_painter::TwixPainter}; use super::overlays::{BallDetection, FeetDetection, LineDetection, PenaltyBoxes}; +#[derive(PartialEq, Debug, Clone, Copy)] +pub enum VisionCycler { + VisionTop, + VisionBottom, +} + +impl ToString for VisionCycler { + fn to_string(&self) -> String { + match self { + VisionCycler::VisionTop => "VisionTop", + VisionCycler::VisionBottom => "VisionBottom", + } + .to_string() + } +} + +impl FromStr for VisionCycler { + type Err = Report; + + fn from_str(cycler: &str) -> Result { + match cycler { + "VisionTop" => Ok(VisionCycler::VisionTop), + "VisionBottom" => Ok(VisionCycler::VisionBottom), + _ => Err(Report::msg(format!("Unknown vision cycler: {}", cycler))), + } + } +} + pub trait Overlay { const NAME: &'static str; - fn new(nao: Arc, selected_cycler: Cycler) -> Self; + fn new(nao: Arc, selected_cycler: VisionCycler) -> Self; fn paint(&self, painter: &TwixPainter) -> Result<()>; } @@ -33,7 +60,7 @@ where nao: Arc, value: Option<&Value>, active: bool, - selected_cycler: Cycler, + selected_cycler: VisionCycler, ) -> Self { let active = value .and_then(|value| value.get(T::NAME.to_case(convert_case::Case::Snake))) @@ -48,13 +75,13 @@ where } } - pub fn update_cycler(&mut self, selected_cycler: Cycler) { + pub fn update_cycler(&mut self, selected_cycler: VisionCycler) { if let Some(overlay) = self.overlay.as_mut() { *overlay = T::new(self.nao.clone(), selected_cycler); } } - pub fn checkbox(&mut self, ui: &mut Ui, selected_cycler: Cycler) { + pub fn checkbox(&mut self, ui: &mut Ui, selected_cycler: VisionCycler) { if ui.checkbox(&mut self.active, T::NAME).changed() { match (self.active, self.overlay.is_some()) { (true, false) => self.overlay = Some(T::new(self.nao.clone(), selected_cycler)), @@ -84,7 +111,7 @@ pub struct Overlays { } impl Overlays { - pub fn new(nao: Arc, storage: Option<&Value>, selected_cycler: Cycler) -> Self { + pub fn new(nao: Arc, storage: Option<&Value>, selected_cycler: VisionCycler) -> Self { let line_detection = EnabledOverlay::new(nao.clone(), storage, true, selected_cycler); let ball_detection = EnabledOverlay::new(nao.clone(), storage, true, selected_cycler); let penalty_boxes = EnabledOverlay::new(nao.clone(), storage, true, selected_cycler); @@ -97,14 +124,14 @@ impl Overlays { } } - pub fn update_cycler(&mut self, selected_cycler: Cycler) { + pub fn update_cycler(&mut self, selected_cycler: VisionCycler) { self.line_detection.update_cycler(selected_cycler); self.ball_detection.update_cycler(selected_cycler); self.penalty_boxes.update_cycler(selected_cycler); self.feet_detection.update_cycler(selected_cycler); } - pub fn combo_box(&mut self, ui: &mut Ui, selected_cycler: Cycler) { + pub fn combo_box(&mut self, ui: &mut Ui, selected_cycler: VisionCycler) { ui.menu_button("Overlays", |ui| { self.line_detection.checkbox(ui, selected_cycler); self.ball_detection.checkbox(ui, selected_cycler); diff --git a/tools/twix/src/panels/image/overlays/ball_detection.rs b/tools/twix/src/panels/image/overlays/ball_detection.rs index 15b9efc277..e34d278b4f 100644 --- a/tools/twix/src/panels/image/overlays/ball_detection.rs +++ b/tools/twix/src/panels/image/overlays/ball_detection.rs @@ -1,12 +1,12 @@ -use std::str::FromStr; - use color_eyre::Result; -use communication::client::{Cycler, CyclerOutput}; use eframe::epaint::{Color32, Stroke}; use geometry::circle::Circle; use types::ball::{Ball, CandidateEvaluation}; -use crate::{panels::image::overlay::Overlay, value_buffer::ValueBuffer}; +use crate::{ + panels::image::overlay::{Overlay, VisionCycler}, + value_buffer::ValueBuffer, +}; pub struct BallDetection { balls: ValueBuffer, @@ -17,27 +17,24 @@ pub struct BallDetection { impl Overlay for BallDetection { const NAME: &'static str = "Ball Detection"; - fn new(nao: std::sync::Arc, selected_cycler: Cycler) -> Self { + fn new(nao: std::sync::Arc, selected_cycler: VisionCycler) -> Self { let camera_position = match selected_cycler { - Cycler::VisionTop => "top", - Cycler::VisionBottom => "bottom", - cycler => panic!("Invalid vision cycler: {cycler}"), + VisionCycler::VisionTop => "top", + VisionCycler::VisionBottom => "bottom", }; Self { - balls: nao.subscribe_output( - CyclerOutput::from_str(&format!("{}.main.balls", selected_cycler)).unwrap(), - ), - filtered_balls: nao.subscribe_output( - CyclerOutput::from_str(&format!( - "Control.additional.filtered_balls_in_image_{}", - camera_position, - )) - .unwrap(), - ), - ball_candidates: nao.subscribe_output( - CyclerOutput::from_str(&format!("{}.additional.ball_candidates", selected_cycler)) - .unwrap(), - ), + balls: nao.subscribe_output(format!( + "{}.main_outputs.balls", + selected_cycler.to_string() + )), + filtered_balls: nao.subscribe_output(format!( + "Control.additional.filtered_balls_in_image_{}", + camera_position, + )), + ball_candidates: nao.subscribe_output(format!( + "{}.additional_outputs.ball_candidates", + selected_cycler.to_string() + )), } } diff --git a/tools/twix/src/panels/image/overlays/feet_detection.rs b/tools/twix/src/panels/image/overlays/feet_detection.rs index 24d300bf71..6a0e27a7ad 100644 --- a/tools/twix/src/panels/image/overlays/feet_detection.rs +++ b/tools/twix/src/panels/image/overlays/feet_detection.rs @@ -1,12 +1,14 @@ use std::sync::Arc; use color_eyre::Result; -use communication::client::{Cycler, CyclerOutput, Output}; use eframe::epaint::Color32; use types::detected_feet::ClusterPoint; use crate::{ - nao::Nao, panels::image::overlay::Overlay, twix_painter::TwixPainter, value_buffer::ValueBuffer, + nao::Nao, + panels::image::overlay::{Overlay, VisionCycler}, + twix_painter::TwixPainter, + value_buffer::ValueBuffer, }; pub struct FeetDetection { @@ -16,14 +18,12 @@ pub struct FeetDetection { impl Overlay for FeetDetection { const NAME: &'static str = "Feet Detection"; - fn new(nao: Arc, selected_cycler: Cycler) -> Self { + fn new(nao: Arc, selected_cycler: VisionCycler) -> Self { Self { - cluster_points: nao.subscribe_output(CyclerOutput { - cycler: selected_cycler, - output: Output::Additional { - path: "feet_detection.cluster_points".to_string(), - }, - }), + cluster_points: nao.subscribe_output(format!( + "{}.additional_outputs.feet_detection.cluster_points", + selected_cycler.to_string() + )), } } diff --git a/tools/twix/src/panels/image/overlays/line_detection.rs b/tools/twix/src/panels/image/overlays/line_detection.rs index 7f8a07e717..13e09e08d2 100644 --- a/tools/twix/src/panels/image/overlays/line_detection.rs +++ b/tools/twix/src/panels/image/overlays/line_detection.rs @@ -1,11 +1,9 @@ -use std::str::FromStr; - use color_eyre::Result; -use communication::client::{Cycler, CyclerOutput}; use eframe::epaint::{Color32, Stroke}; use types::line_data::ImageLines; use types::line_data::LineDiscardReason; +use crate::panels::image::overlay::VisionCycler; use crate::{ panels::image::overlay::Overlay, twix_painter::TwixPainter, value_buffer::ValueBuffer, }; @@ -17,12 +15,12 @@ pub struct LineDetection { impl Overlay for LineDetection { const NAME: &'static str = "Line Detection"; - fn new(nao: std::sync::Arc, selected_cycler: Cycler) -> Self { + fn new(nao: std::sync::Arc, selected_cycler: VisionCycler) -> Self { Self { - lines_in_image: nao.subscribe_output( - CyclerOutput::from_str(&format!("{selected_cycler}.additional.lines_in_image")) - .unwrap(), - ), + lines_in_image: nao.subscribe_output(format!( + "{}.additional_outputs.lines_in_image", + selected_cycler.to_string() + )), } } diff --git a/tools/twix/src/panels/image/overlays/penalty_boxes.rs b/tools/twix/src/panels/image/overlays/penalty_boxes.rs index 656ff53d1c..fd30e87d51 100644 --- a/tools/twix/src/panels/image/overlays/penalty_boxes.rs +++ b/tools/twix/src/panels/image/overlays/penalty_boxes.rs @@ -1,12 +1,13 @@ use std::sync::Arc; use color_eyre::Result; -use communication::client::{Cycler, CyclerOutput, Output}; use eframe::epaint::{Color32, Stroke}; use types::line::Line2; use crate::{ - panels::image::overlay::Overlay, twix_painter::TwixPainter, value_buffer::ValueBuffer, + panels::image::overlay::{Overlay, VisionCycler}, + twix_painter::TwixPainter, + value_buffer::ValueBuffer, }; pub struct PenaltyBoxes { @@ -16,19 +17,15 @@ pub struct PenaltyBoxes { impl Overlay for PenaltyBoxes { const NAME: &'static str = "Penalty Boxes"; - fn new(nao: Arc, selected_cycler: Cycler) -> Self { + fn new(nao: Arc, selected_cycler: VisionCycler) -> Self { let top_or_bottom = match selected_cycler { - Cycler::VisionTop => "top", - Cycler::VisionBottom => "bottom", - cycler => panic!("Invalid vision cycler: {cycler}"), + VisionCycler::VisionTop => "top", + VisionCycler::VisionBottom => "bottom", }; Self { - penalty_boxes: nao.subscribe_output(CyclerOutput { - cycler: Cycler::Control, - output: Output::Additional { - path: format!("projected_field_lines.{top_or_bottom}"), - }, - }), + penalty_boxes: nao.subscribe_output(format!( + "Control.additional_outputs.projected_field_lines.{top_or_bottom}" + )), } } diff --git a/tools/twix/src/panels/image_segments.rs b/tools/twix/src/panels/image_segments.rs index c8f9afbe07..b1d0eb9584 100644 --- a/tools/twix/src/panels/image_segments.rs +++ b/tools/twix/src/panels/image_segments.rs @@ -1,6 +1,5 @@ -use std::{str::FromStr, sync::Arc}; +use std::sync::Arc; -use communication::client::CyclerOutput; use eframe::{ egui::{ComboBox, Response, Ui, Widget}, epaint::{Color32, Stroke}, @@ -44,8 +43,8 @@ impl Panel for ImageSegmentsPanel { const NAME: &'static str = "Image Segments"; fn new(nao: Arc, _value: Option<&Value>) -> Self { - let value_buffer = - nao.subscribe_output(CyclerOutput::from_str("VisionTop.main.image_segments").unwrap()); + let value_buffer = nao.subscribe_output("VisionTop.main_outputs.image_segments"); + Self { nao, value_buffer, @@ -84,18 +83,10 @@ impl Widget for &mut ImageSegmentsPanel { ui.checkbox(&mut self.use_filtered_segments, "Filtered Segments"); if camera_selection_changed || filtered_segments_checkbox.changed() { let output = match (self.camera_position, self.use_filtered_segments) { - (CameraPosition::Top, false) => { - CyclerOutput::from_str("VisionTop.main.image_segments").unwrap() - } - (CameraPosition::Top, true) => { - CyclerOutput::from_str("VisionTop.main.filtered_segments").unwrap() - } - (CameraPosition::Bottom, false) => { - CyclerOutput::from_str("VisionBottom.main.image_segments").unwrap() - } - (CameraPosition::Bottom, true) => { - CyclerOutput::from_str("VisionBottom.main.filtered_segments").unwrap() - } + (CameraPosition::Top, false) => "VisionTop.main_outputs.image_segments", + (CameraPosition::Top, true) => "VisionTop.main_outputs.filtered_segments", + (CameraPosition::Bottom, false) => "VisionBottom.main_outputs.image_segments", + (CameraPosition::Bottom, true) => "VisionBottom.main_outputs.filtered_segments", }; self.value_buffer = self.nao.subscribe_output(output); } diff --git a/tools/twix/src/panels/look_at.rs b/tools/twix/src/panels/look_at.rs index 98eb29c099..ef1b88dfef 100644 --- a/tools/twix/src/panels/look_at.rs +++ b/tools/twix/src/panels/look_at.rs @@ -1,12 +1,11 @@ use crate::{nao::Nao, panel::Panel, value_buffer::ValueBuffer}; -use communication::client::CyclerOutput; use eframe::{ egui::{Response, Slider, TextFormat, Ui, Widget}, epaint::{text::LayoutJob, Color32, FontId}, }; use nalgebra::{point, Point2}; use serde_json::Value; -use std::{ops::RangeInclusive, str::FromStr, sync::Arc}; +use std::{ops::RangeInclusive, sync::Arc}; use types::{ camera_position::CameraPosition, field_dimensions::FieldDimensions, @@ -38,10 +37,7 @@ impl Panel for LookAtPanel { fn new(nao: Arc, _: Option<&Value>) -> Self { let field_dimensions_buffer = nao.subscribe_parameter("field_dimensions"); - let motion_command_buffer = nao.subscribe_output( - CyclerOutput::from_str("Control.main_outputs.motion_command") - .expect("Failed to subscribe to main_outputs.motion_command"), - ); + let motion_command_buffer = nao.subscribe_output("Control.main_outputs.motion_command"); Self { nao, diff --git a/tools/twix/src/panels/map/layers/ball_filter.rs b/tools/twix/src/panels/map/layers/ball_filter.rs index dfa901136b..5e942366a7 100644 --- a/tools/twix/src/panels/map/layers/ball_filter.rs +++ b/tools/twix/src/panels/map/layers/ball_filter.rs @@ -1,7 +1,6 @@ use std::sync::Arc; use color_eyre::Result; -use communication::client::{Cycler, CyclerOutput, Output}; use eframe::epaint::{Color32, Stroke}; use nalgebra::{Isometry2, Point2}; use types::{ @@ -22,18 +21,9 @@ impl Layer for BallFilter { const NAME: &'static str = "Ball Filter"; fn new(nao: Arc) -> Self { - let robot_to_field = nao.subscribe_output(CyclerOutput { - cycler: Cycler::Control, - output: Output::Main { - path: "robot_to_field".to_string(), - }, - }); - let ball_hypotheses = nao.subscribe_output(CyclerOutput { - cycler: Cycler::Control, - output: Output::Additional { - path: "best_ball_state".to_string(), - }, - }); + let robot_to_field = nao.subscribe_output("Control.robot_to_field"); + let ball_hypotheses = nao.subscribe_output("Control.best_ball_state"); + Self { robot_to_field, ball_state: ball_hypotheses, diff --git a/tools/twix/src/panels/map/layers/ball_position.rs b/tools/twix/src/panels/map/layers/ball_position.rs index 0c5eec9337..cc607f5099 100644 --- a/tools/twix/src/panels/map/layers/ball_position.rs +++ b/tools/twix/src/panels/map/layers/ball_position.rs @@ -1,7 +1,6 @@ -use std::{str::FromStr, sync::Arc}; +use std::sync::Arc; use color_eyre::Result; -use communication::client::CyclerOutput; use eframe::epaint::Color32; use nalgebra::Isometry2; use types::field_dimensions::FieldDimensions; @@ -19,11 +18,9 @@ impl Layer for BallPosition { const NAME: &'static str = "Ball Position"; fn new(nao: Arc) -> Self { - let robot_to_field = - nao.subscribe_output(CyclerOutput::from_str("Control.main.robot_to_field").unwrap()); + let robot_to_field = nao.subscribe_output("Control.main.robot_to_field"); robot_to_field.reserve(100); - let ball_position = - nao.subscribe_output(CyclerOutput::from_str("Control.main.ball_position").unwrap()); + let ball_position = nao.subscribe_output("Control.main.ball_position"); ball_position.reserve(100); Self { robot_to_field, diff --git a/tools/twix/src/panels/map/layers/behavior_simulator.rs b/tools/twix/src/panels/map/layers/behavior_simulator.rs index 147cbc51b1..f6b146ea28 100644 --- a/tools/twix/src/panels/map/layers/behavior_simulator.rs +++ b/tools/twix/src/panels/map/layers/behavior_simulator.rs @@ -1,7 +1,6 @@ -use std::{str::FromStr, sync::Arc}; +use std::sync::Arc; use color_eyre::Result; -use communication::client::CyclerOutput; use eframe::epaint::{Color32, Stroke}; use nalgebra::{point, Isometry2, Point2, UnitComplex}; use types::{field_dimensions::FieldDimensions, motion_command::MotionCommand}; @@ -43,9 +42,8 @@ impl Layer for BehaviorSimulator { "main_outputs.sensor_data.positions.head.yaw", ) .unwrap(); - let ball = nao.subscribe_output( - CyclerOutput::from_str("BehaviorSimulator.main_outputs.ball.position").unwrap(), - ); + let ball = nao.subscribe_output("BehaviorSimulator.main_outputs.ball.position"); + Self { robot_to_field, motion_command, diff --git a/tools/twix/src/panels/map/layers/feet_detection.rs b/tools/twix/src/panels/map/layers/feet_detection.rs index c64e95b17f..cd0db976f6 100644 --- a/tools/twix/src/panels/map/layers/feet_detection.rs +++ b/tools/twix/src/panels/map/layers/feet_detection.rs @@ -1,7 +1,6 @@ use std::sync::Arc; use color_eyre::Result; -use communication::client::{Cycler, CyclerOutput, Output}; use eframe::epaint::Color32; use nalgebra::{Isometry2, Point2}; @@ -23,36 +22,12 @@ impl Layer for FeetDetection { const NAME: &'static str = "FeetDetection"; fn new(nao: Arc) -> Self { - let robot_to_field = nao.subscribe_output(CyclerOutput { - cycler: Cycler::Control, - output: Output::Main { - path: "robot_to_field".to_string(), - }, - }); - let cluster_bottom = nao.subscribe_output(CyclerOutput { - cycler: Cycler::VisionBottom, - output: Output::Additional { - path: "feet_detection.clusters_in_ground".to_string(), - }, - }); - let cluster_top = nao.subscribe_output(CyclerOutput { - cycler: Cycler::VisionTop, - output: Output::Additional { - path: "feet_detection.clusters_in_ground".to_string(), - }, - }); - let segments_bottom = nao.subscribe_output(CyclerOutput { - cycler: Cycler::VisionBottom, - output: Output::Additional { - path: "feet_detection.cluster_points".to_string(), - }, - }); - let segments_top = nao.subscribe_output(CyclerOutput { - cycler: Cycler::VisionTop, - output: Output::Additional { - path: "feet_detection.cluster_points".to_string(), - }, - }); + let robot_to_field = nao.subscribe_output("Control.robot_to_field"); + let cluster_bottom = nao.subscribe_output("VisionBottom.feet_detection.clusters_in_ground"); + let cluster_top = nao.subscribe_output("VisionTop.feet_detection.clusters_in_ground"); + let segments_bottom = nao.subscribe_output("VisionBottom.feet_detection.cluster_points"); + let segments_top = nao.subscribe_output("VisionTop.feet_detection.cluster_points"); + Self { robot_to_field, cluster_bottom, diff --git a/tools/twix/src/panels/map/layers/image_segments.rs b/tools/twix/src/panels/map/layers/image_segments.rs index b4c2e8386e..4d00cd52ca 100644 --- a/tools/twix/src/panels/map/layers/image_segments.rs +++ b/tools/twix/src/panels/map/layers/image_segments.rs @@ -1,7 +1,4 @@ -use std::str::FromStr; - use color_eyre::Result; -use communication::client::CyclerOutput; use eframe::epaint::{Color32, Stroke}; use nalgebra::{point, vector, Isometry2, Point2}; use projection::Projection; @@ -21,16 +18,12 @@ impl Layer for ImageSegments { const NAME: &'static str = "Image Segments"; fn new(nao: std::sync::Arc) -> Self { - let robot_to_field = - nao.subscribe_output(CyclerOutput::from_str("Control.main.robot_to_field").unwrap()); - let image_segments_bottom = nao - .subscribe_output(CyclerOutput::from_str("VisionBottom.main.image_segments").unwrap()); - let camera_matrix_bottom = nao - .subscribe_output(CyclerOutput::from_str("VisionBottom.main.camera_matrix").unwrap()); - let image_segments_top = - nao.subscribe_output(CyclerOutput::from_str("VisionTop.main.image_segments").unwrap()); - let camera_matrix_top = - nao.subscribe_output(CyclerOutput::from_str("VisionTop.main.camera_matrix").unwrap()); + let robot_to_field = nao.subscribe_output("Control.main.robot_to_field"); + let image_segments_bottom = nao.subscribe_output("VisionBottom.main.image_segments"); + let camera_matrix_bottom = nao.subscribe_output("VisionBottom.main.camera_matrix"); + let image_segments_top = nao.subscribe_output("VisionTop.main.image_segments"); + let camera_matrix_top = nao.subscribe_output("VisionTop.main.camera_matrix"); + Self { robot_to_field, image_segments_bottom, diff --git a/tools/twix/src/panels/map/layers/kick_decisions.rs b/tools/twix/src/panels/map/layers/kick_decisions.rs index bc1c57c044..61c13f0450 100644 --- a/tools/twix/src/panels/map/layers/kick_decisions.rs +++ b/tools/twix/src/panels/map/layers/kick_decisions.rs @@ -1,7 +1,6 @@ -use std::{str::FromStr, sync::Arc}; +use std::sync::Arc; use color_eyre::Result; -use communication::client::CyclerOutput; use eframe::epaint::{Color32, Stroke}; use nalgebra::{Isometry2, Point2}; use types::{field_dimensions::FieldDimensions, kick_decision::KickDecision}; @@ -22,18 +21,12 @@ impl Layer for KickDecisions { const NAME: &'static str = "Kick Decisions"; fn new(nao: Arc) -> Self { - let robot_to_field = - nao.subscribe_output(CyclerOutput::from_str("Control.main.robot_to_field").unwrap()); - let kick_decisions = - nao.subscribe_output(CyclerOutput::from_str("Control.main.kick_decisions").unwrap()); - let instant_kick_decisions = nao.subscribe_output( - CyclerOutput::from_str("Control.main.instant_kick_decisions").unwrap(), - ); - let kick_targets = nao - .subscribe_output(CyclerOutput::from_str("Control.additional.kick_targets").unwrap()); - let instant_kick_targets = nao.subscribe_output( - CyclerOutput::from_str("Control.additional.instant_kick_targets").unwrap(), - ); + let robot_to_field = nao.subscribe_output("Control.main.robot_to_field"); + let kick_decisions = nao.subscribe_output("Control.main.kick_decisions"); + let instant_kick_decisions = nao.subscribe_output("Control.main.instant_kick_decisions"); + let kick_targets = nao.subscribe_output("Control.additional.kick_targets"); + let instant_kick_targets = nao.subscribe_output("Control.additional.instant_kick_targets"); + Self { robot_to_field, kick_decisions, diff --git a/tools/twix/src/panels/map/layers/line_correspondences.rs b/tools/twix/src/panels/map/layers/line_correspondences.rs index ce99adbb9c..068d54f963 100644 --- a/tools/twix/src/panels/map/layers/line_correspondences.rs +++ b/tools/twix/src/panels/map/layers/line_correspondences.rs @@ -1,7 +1,6 @@ -use std::{str::FromStr, sync::Arc}; +use std::sync::Arc; use color_eyre::Result; -use communication::client::CyclerOutput; use eframe::epaint::{Color32, Stroke}; use types::{field_dimensions::FieldDimensions, line::Line2}; @@ -19,14 +18,10 @@ impl Layer for LineCorrespondences { const NAME: &'static str = "Line Correspondences"; fn new(nao: Arc) -> Self { - let lines_in_robot_bottom = nao.subscribe_output( - CyclerOutput::from_str("VisionBottom.additional.localization.correspondence_lines") - .unwrap(), - ); - let lines_in_robot_top = nao.subscribe_output( - CyclerOutput::from_str("VisionTop.additional.localization.correspondence_lines") - .unwrap(), - ); + let lines_in_robot_bottom = + nao.subscribe_output("VisionBottom.additional.localization.correspondence_lines"); + let lines_in_robot_top = + nao.subscribe_output("VisionTop.additional.localization.correspondence_lines"); Self { lines_in_robot_bottom, lines_in_robot_top, diff --git a/tools/twix/src/panels/map/layers/lines.rs b/tools/twix/src/panels/map/layers/lines.rs index 1d86d7060e..e100e2cf76 100644 --- a/tools/twix/src/panels/map/layers/lines.rs +++ b/tools/twix/src/panels/map/layers/lines.rs @@ -1,7 +1,6 @@ -use std::{str::FromStr, sync::Arc}; +use std::sync::Arc; use color_eyre::Result; -use communication::client::CyclerOutput; use eframe::epaint::{Color32, Stroke}; use nalgebra::Isometry2; @@ -21,14 +20,11 @@ impl Layer for Lines { const NAME: &'static str = "Lines"; fn new(nao: Arc) -> Self { - let robot_to_field = - nao.subscribe_output(CyclerOutput::from_str("Control.main.robot_to_field").unwrap()); - let lines_in_robot_bottom = nao.subscribe_output( - CyclerOutput::from_str("VisionBottom.main.line_data.lines_in_robot").unwrap(), - ); - let lines_in_robot_top = nao.subscribe_output( - CyclerOutput::from_str("VisionTop.main.line_data.lines_in_robot").unwrap(), - ); + let robot_to_field = nao.subscribe_output("Control.main.robot_to_field"); + let lines_in_robot_bottom = + nao.subscribe_output("VisionBottom.main.line_data.lines_in_robot"); + let lines_in_robot_top = nao.subscribe_output("VisionTop.main.line_data.lines_in_robot"); + Self { robot_to_field, lines_in_robot_bottom, diff --git a/tools/twix/src/panels/map/layers/obstacle_filter.rs b/tools/twix/src/panels/map/layers/obstacle_filter.rs index ef6e6e8985..7b392d62a8 100644 --- a/tools/twix/src/panels/map/layers/obstacle_filter.rs +++ b/tools/twix/src/panels/map/layers/obstacle_filter.rs @@ -1,7 +1,6 @@ use std::sync::Arc; use color_eyre::Result; -use communication::client::{Cycler, CyclerOutput, Output}; use eframe::epaint::{Color32, Stroke}; use nalgebra::{Isometry2, Point2}; use types::{field_dimensions::FieldDimensions, obstacle_filter::Hypothesis}; @@ -19,18 +18,9 @@ impl Layer for ObstacleFilter { const NAME: &'static str = "Obstacle Filter"; fn new(nao: Arc) -> Self { - let robot_to_field = nao.subscribe_output(CyclerOutput { - cycler: Cycler::Control, - output: Output::Main { - path: "robot_to_field".to_string(), - }, - }); - let hypotheses = nao.subscribe_output(CyclerOutput { - cycler: Cycler::Control, - output: Output::Additional { - path: "obstacle_filter_hypotheses".to_string(), - }, - }); + let robot_to_field = nao.subscribe_output("Control.main_outputs.robot_to_field"); + let hypotheses = nao.subscribe_output("Control.main_outputs.obstacle_filter_hypotheses"); + Self { robot_to_field, hypotheses, diff --git a/tools/twix/src/panels/map/layers/obstacles.rs b/tools/twix/src/panels/map/layers/obstacles.rs index cf6ae26656..2247ea46e6 100644 --- a/tools/twix/src/panels/map/layers/obstacles.rs +++ b/tools/twix/src/panels/map/layers/obstacles.rs @@ -1,7 +1,6 @@ -use std::{str::FromStr, sync::Arc}; +use std::sync::Arc; use color_eyre::Result; -use communication::client::CyclerOutput; use eframe::epaint::{Color32, Stroke}; use nalgebra::Isometry2; use types::{field_dimensions::FieldDimensions, obstacles::Obstacle}; @@ -19,10 +18,8 @@ impl Layer for Obstacles { const NAME: &'static str = "Obstacles"; fn new(nao: Arc) -> Self { - let robot_to_field = - nao.subscribe_output(CyclerOutput::from_str("Control.main.robot_to_field").unwrap()); - let obstacles = - nao.subscribe_output(CyclerOutput::from_str("Control.main.obstacles").unwrap()); + let robot_to_field = nao.subscribe_output("Control.main.robot_to_field"); + let obstacles = nao.subscribe_output("Control.main.obstacles"); Self { robot_to_field, obstacles, diff --git a/tools/twix/src/panels/map/layers/path.rs b/tools/twix/src/panels/map/layers/path.rs index ef0523950f..a16e345724 100644 --- a/tools/twix/src/panels/map/layers/path.rs +++ b/tools/twix/src/panels/map/layers/path.rs @@ -1,7 +1,6 @@ -use std::{str::FromStr, sync::Arc}; +use std::sync::Arc; use color_eyre::Result; -use communication::client::CyclerOutput; use eframe::epaint::Color32; use nalgebra::Isometry2; use types::{field_dimensions::FieldDimensions, motion_command::MotionCommand}; @@ -19,10 +18,8 @@ impl Layer for Path { const NAME: &'static str = "Path"; fn new(nao: Arc) -> Self { - let robot_to_field = - nao.subscribe_output(CyclerOutput::from_str("Control.main.robot_to_field").unwrap()); - let motion_command = - nao.subscribe_output(CyclerOutput::from_str("Control.main.motion_command").unwrap()); + let robot_to_field = nao.subscribe_output("Control.main.robot_to_field"); + let motion_command = nao.subscribe_output("Control.main.motion_command"); Self { robot_to_field, motion_command, diff --git a/tools/twix/src/panels/map/layers/path_obstacles.rs b/tools/twix/src/panels/map/layers/path_obstacles.rs index 1b2ffb118c..eb87c8d862 100644 --- a/tools/twix/src/panels/map/layers/path_obstacles.rs +++ b/tools/twix/src/panels/map/layers/path_obstacles.rs @@ -1,7 +1,6 @@ -use std::{str::FromStr, sync::Arc}; +use std::sync::Arc; use color_eyre::Result; -use communication::client::CyclerOutput; use eframe::epaint::{Color32, Stroke}; use nalgebra::Isometry2; use types::{field_dimensions::FieldDimensions, path_obstacles::PathObstacle}; @@ -19,10 +18,8 @@ impl Layer for PathObstacles { const NAME: &'static str = "Path Obstacles"; fn new(nao: Arc) -> Self { - let robot_to_field = - nao.subscribe_output(CyclerOutput::from_str("Control.main.robot_to_field").unwrap()); - let path_obstacles = nao - .subscribe_output(CyclerOutput::from_str("Control.additional.path_obstacles").unwrap()); + let robot_to_field = nao.subscribe_output("Control.main.robot_to_field"); + let path_obstacles = nao.subscribe_output("Control.additional.path_obstacles"); Self { robot_to_field, path_obstacles, diff --git a/tools/twix/src/panels/map/layers/robot_pose.rs b/tools/twix/src/panels/map/layers/robot_pose.rs index e9fa697ccc..b49f9c3a30 100644 --- a/tools/twix/src/panels/map/layers/robot_pose.rs +++ b/tools/twix/src/panels/map/layers/robot_pose.rs @@ -1,7 +1,6 @@ -use std::{str::FromStr, sync::Arc}; +use std::sync::Arc; use color_eyre::Result; -use communication::client::CyclerOutput; use eframe::epaint::{Color32, Stroke}; use nalgebra::Isometry2; use types::field_dimensions::FieldDimensions; @@ -18,8 +17,7 @@ impl Layer for RobotPose { const NAME: &'static str = "Robot Pose"; fn new(nao: Arc) -> Self { - let robot_to_field = - nao.subscribe_output(CyclerOutput::from_str("Control.main.robot_to_field").unwrap()); + let robot_to_field = nao.subscribe_output("Control.main.robot_to_field"); Self { robot_to_field } } diff --git a/tools/twix/src/panels/plot.rs b/tools/twix/src/panels/plot.rs index 36850cfaf5..8c2385f9dc 100644 --- a/tools/twix/src/panels/plot.rs +++ b/tools/twix/src/panels/plot.rs @@ -1,7 +1,6 @@ -use std::{str::FromStr, sync::Arc}; +use std::sync::Arc; use color_eyre::eyre::{eyre, Result, WrapErr}; -use communication::client::CyclerOutput; use eframe::{ egui::{ Button, CollapsingHeader, DragValue, Response, RichText, TextEdit, TextStyle, Ui, Widget, @@ -9,7 +8,7 @@ use eframe::{ epaint::Color32, }; use egui_plot::{Line, Plot as EguiPlot, PlotPoints}; -use log::{error, info}; +use log::info; use mlua::{Function, Lua, LuaSerdeExt}; use serde::{Deserialize, Serialize}; use serde_json::{json, to_string_pretty, Value}; @@ -171,16 +170,10 @@ impl LineData { } fn subscribe_key(&mut self, nao: Arc, buffer_size: usize) { - self.value_buffer = match CyclerOutput::from_str(&self.output_key) { - Ok(output) => { - let buffer = nao.subscribe_output(output); - buffer.reserve(buffer_size); - Some(buffer) - } - Err(error) => { - error!("Failed to subscribe: {:#}", error); - None - } + self.value_buffer = { + let buffer = nao.subscribe_output(&self.output_key); + buffer.reserve(buffer_size); + Some(buffer) }; } } diff --git a/tools/twix/src/panels/text.rs b/tools/twix/src/panels/text.rs index aac63bc0b5..658d95638d 100644 --- a/tools/twix/src/panels/text.rs +++ b/tools/twix/src/panels/text.rs @@ -1,15 +1,13 @@ -use std::{str::FromStr, sync::Arc}; +use std::sync::Arc; -use communication::client::CyclerOutput; use eframe::egui::{Label, ScrollArea, Sense, Widget}; -use log::error; use serde_json::{json, Value}; use crate::{completion_edit::CompletionEdit, nao::Nao, panel::Panel, value_buffer::ValueBuffer}; pub struct TextPanel { nao: Arc, - output: String, + path: String, values: Option, } @@ -17,49 +15,33 @@ impl Panel for TextPanel { const NAME: &'static str = "Text"; fn new(nao: Arc, value: Option<&Value>) -> Self { - let output = match value.and_then(|value| value.get("subscribe_key")) { + let path = match value.and_then(|value| value.get("subscribe_key")) { Some(Value::String(string)) => string.to_string(), _ => String::new(), }; - let values = if !output.is_empty() { - let output = CyclerOutput::from_str(&output); - match output { - Ok(output) => Some(nao.subscribe_output(output)), - Err(error) => { - error!("Failed to subscribe: {error:?}"); - None - } - } + let values = if !path.is_empty() { + Some(nao.subscribe_output(&path)) } else { None }; - Self { - nao, - output, - values, - } + + Self { nao, path, values } } fn save(&self) -> Value { json!({ - "subscribe_key": self.output.clone() + "subscribe_key": self.path.clone() }) } } impl Widget for &mut TextPanel { fn ui(self, ui: &mut eframe::egui::Ui) -> eframe::egui::Response { - let edit_response = ui.add(CompletionEdit::outputs(&mut self.output, self.nao.as_ref())); + let edit_response = ui.add(CompletionEdit::outputs(&mut self.path, self.nao.as_ref())); if edit_response.changed() { - match CyclerOutput::from_str(&self.output) { - Ok(output) => { - self.values = Some(self.nao.subscribe_output(output)); - } - Err(error) => { - error!("Failed to subscribe: {error:#?}"); - } - } + self.values = Some(self.nao.subscribe_output(&self.path)); } + let scroll_area = ScrollArea::vertical() .auto_shrink([false, false]) .show(ui, |ui| { diff --git a/tools/twix/src/panels/vision_tuner.rs b/tools/twix/src/panels/vision_tuner.rs index 4756eb5ec6..1d194d0ce7 100644 --- a/tools/twix/src/panels/vision_tuner.rs +++ b/tools/twix/src/panels/vision_tuner.rs @@ -1,4 +1,3 @@ -use communication::client::Cycler; use eframe::egui::{ComboBox, Response, Slider, Ui, Widget}; use nalgebra::{Isometry2, Rotation2, Translation2}; use serde_json::{to_value, Value}; @@ -11,9 +10,24 @@ use types::interpolated::Interpolated; use crate::{nao::Nao, panel::Panel, value_buffer::ValueBuffer}; +#[derive(Debug, PartialEq, Clone, Copy)] +enum VisionCycler { + VisionTop, + VisionBottom, +} + +impl ToString for VisionCycler { + fn to_string(&self) -> String { + match self { + VisionCycler::VisionTop => "vision_top".to_string(), + VisionCycler::VisionBottom => "vision_bottom".to_string(), + } + } +} + pub struct VisionTunerPanel { nao: Arc, - cycler: Cycler, + cycler: VisionCycler, position: Position, buffers: Buffers, } @@ -22,7 +36,7 @@ impl Panel for VisionTunerPanel { const NAME: &'static str = "Vision Tuner"; fn new(nao: Arc, _value: Option<&Value>) -> Self { - let cycler = Cycler::VisionTop; + let cycler = VisionCycler::VisionTop; let buffers = Buffers::from(&nao, cycler); Self { @@ -205,7 +219,7 @@ struct Buffers { } impl Buffers { - fn from(nao: &Nao, cycler: Cycler) -> Self { + fn from(nao: &Nao, cycler: VisionCycler) -> Self { let vertical_edge_threshold_buffer = nao.subscribe_parameter(get_vertical_edge_threshold_path(cycler)); let red_chromaticity_threshold_buffer = @@ -261,7 +275,7 @@ impl Display for Position { fn add_selector_row( ui: &mut Ui, nao: &Nao, - cycler: &mut Cycler, + cycler: &mut VisionCycler, position: &mut Position, buffers: &mut Buffers, ) -> Response { @@ -299,7 +313,7 @@ fn add_selector_row( fn add_vision_cycler_selector( ui: &mut Ui, nao: &Nao, - cycler: &mut Cycler, + cycler: &mut VisionCycler, buffers: &mut Buffers, ) -> Response { let mut changed = false; @@ -307,17 +321,17 @@ fn add_vision_cycler_selector( .selected_text(format!("{:?}", cycler)) .show_ui(ui, |ui| { if ui - .selectable_value(cycler, Cycler::VisionTop, "VisionTop") + .selectable_value(cycler, VisionCycler::VisionTop, "VisionTop") .clicked() { - *cycler = Cycler::VisionTop; + *cycler = VisionCycler::VisionTop; changed = true; }; if ui - .selectable_value(cycler, Cycler::VisionBottom, "VisionBottom") + .selectable_value(cycler, VisionCycler::VisionBottom, "VisionBottom") .clicked() { - *cycler = Cycler::VisionBottom; + *cycler = VisionCycler::VisionBottom; changed = true; }; }) @@ -380,55 +394,59 @@ fn add_position_selector(ui: &mut Ui, position: &mut Position) -> Response { combo_box.response } -fn get_vertical_edge_threshold_path(cycler: Cycler) -> &'static str { +fn get_vertical_edge_threshold_path(cycler: VisionCycler) -> &'static str { match cycler { - Cycler::VisionTop => "image_segmenter.vision_top.vertical_edge_threshold", - Cycler::VisionBottom => "image_segmenter.vision_bottom.vertical_edge_threshold", - _ => panic!("not implemented"), + VisionCycler::VisionTop => "image_segmenter.vision_top.vertical_edge_threshold", + VisionCycler::VisionBottom => "image_segmenter.vision_bottom.vertical_edge_threshold", } } -fn get_red_chromaticity_threshold_path(cycler: Cycler) -> &'static str { +fn get_red_chromaticity_threshold_path(cycler: VisionCycler) -> &'static str { match cycler { - Cycler::VisionTop => "field_color_detection.vision_top.red_chromaticity_threshold", - Cycler::VisionBottom => "field_color_detection.vision_bottom.red_chromaticity_threshold", - _ => panic!("not implemented"), + VisionCycler::VisionTop => "field_color_detection.vision_top.red_chromaticity_threshold", + VisionCycler::VisionBottom => { + "field_color_detection.vision_bottom.red_chromaticity_threshold" + } } } -fn get_blue_chromaticity_threshold_path(cycler: Cycler) -> &'static str { +fn get_blue_chromaticity_threshold_path(cycler: VisionCycler) -> &'static str { match cycler { - Cycler::VisionTop => "field_color_detection.vision_top.blue_chromaticity_threshold", - Cycler::VisionBottom => "field_color_detection.vision_bottom.blue_chromaticity_threshold", - _ => panic!("not implemented"), + VisionCycler::VisionTop => "field_color_detection.vision_top.blue_chromaticity_threshold", + VisionCycler::VisionBottom => { + "field_color_detection.vision_bottom.blue_chromaticity_threshold" + } } } -fn get_lower_green_chromaticity_threshold_path(cycler: Cycler) -> &'static str { +fn get_lower_green_chromaticity_threshold_path(cycler: VisionCycler) -> &'static str { match cycler { - Cycler::VisionTop => "field_color_detection.vision_top.lower_green_chromaticity_threshold", - Cycler::VisionBottom => { + VisionCycler::VisionTop => { + "field_color_detection.vision_top.lower_green_chromaticity_threshold" + } + VisionCycler::VisionBottom => { "field_color_detection.vision_bottom.lower_green_chromaticity_threshold" } - _ => panic!("not implemented"), } } -fn get_upper_green_chromaticity_threshold_path(cycler: Cycler) -> &'static str { +fn get_upper_green_chromaticity_threshold_path(cycler: VisionCycler) -> &'static str { match cycler { - Cycler::VisionTop => "field_color_detection.vision_top.upper_green_chromaticity_threshold", - Cycler::VisionBottom => { + VisionCycler::VisionTop => { + "field_color_detection.vision_top.upper_green_chromaticity_threshold" + } + VisionCycler::VisionBottom => { "field_color_detection.vision_bottom.upper_green_chromaticity_threshold" } - _ => panic!("not implemented"), } } -fn get_green_luminance_threshold_path(cycler: Cycler) -> &'static str { +fn get_green_luminance_threshold_path(cycler: VisionCycler) -> &'static str { match cycler { - Cycler::VisionTop => "field_color_detection.vision_top.green_luminance_threshold", - Cycler::VisionBottom => "field_color_detection.vision_bottom.green_luminance_threshold", - _ => panic!("not implemented"), + VisionCycler::VisionTop => "field_color_detection.vision_top.green_luminance_threshold", + VisionCycler::VisionBottom => { + "field_color_detection.vision_bottom.green_luminance_threshold" + } } } diff --git a/tools/twix/src/players_value_buffer.rs b/tools/twix/src/players_value_buffer.rs index 9133b2eacb..90fc2da922 100644 --- a/tools/twix/src/players_value_buffer.rs +++ b/tools/twix/src/players_value_buffer.rs @@ -1,8 +1,7 @@ -use std::{str::FromStr, sync::Arc}; +use std::sync::Arc; use color_eyre::Result; -use communication::client::CyclerOutput; use types::players::Players; use crate::{nao::Nao, value_buffer::ValueBuffer}; @@ -12,15 +11,13 @@ pub struct PlayersValueBuffer(pub Players); impl PlayersValueBuffer { pub fn try_new(nao: Arc, prefix: &str, output: &str) -> Result { let buffers = Players { - one: nao.subscribe_output(CyclerOutput::from_str(&format!("{prefix}.one.{output}"))?), - two: nao.subscribe_output(CyclerOutput::from_str(&format!("{prefix}.two.{output}"))?), - three: nao - .subscribe_output(CyclerOutput::from_str(&format!("{prefix}.three.{output}"))?), - four: nao.subscribe_output(CyclerOutput::from_str(&format!("{prefix}.four.{output}"))?), - five: nao.subscribe_output(CyclerOutput::from_str(&format!("{prefix}.five.{output}"))?), - six: nao.subscribe_output(CyclerOutput::from_str(&format!("{prefix}.six.{output}"))?), - seven: nao - .subscribe_output(CyclerOutput::from_str(&format!("{prefix}.seven.{output}"))?), + one: nao.subscribe_output(format!("{prefix}.one.{output}")), + two: nao.subscribe_output(format!("{prefix}.two.{output}")), + three: nao.subscribe_output(format!("{prefix}.three.{output}")), + four: nao.subscribe_output(format!("{prefix}.four.{output}")), + five: nao.subscribe_output(format!("{prefix}.five.{output}")), + six: nao.subscribe_output(format!("{prefix}.six.{output}")), + seven: nao.subscribe_output(format!("{prefix}.seven.{output}")), }; Ok(Self(buffers)) diff --git a/tools/twix/src/value_buffer.rs b/tools/twix/src/value_buffer.rs index 3ddc1325b0..58f3ddc7ec 100644 --- a/tools/twix/src/value_buffer.rs +++ b/tools/twix/src/value_buffer.rs @@ -5,8 +5,8 @@ use color_eyre::{ Result, }; use communication::{ - client::{Communication, CyclerOutput, SubscriberMessage}, - messages::Format, + client::{Communication, SubscriberMessage}, + messages::{Format, Path}, }; use log::error; use serde::Deserialize; @@ -46,11 +46,11 @@ pub struct ValueBuffer { } impl ValueBuffer { - pub fn output(communication: Communication, output: CyclerOutput) -> Self { + pub fn output(communication: Communication, path: Path) -> Self { let (command_sender, command_receiver) = mpsc::channel(10); spawn(async move { let (uuid, receiver) = communication - .subscribe_output(output.clone(), Format::Textual) + .subscribe_output(path.clone(), Format::Textual) .await; value_buffer(receiver, command_receiver, communication.clone(), None).await; communication.unsubscribe_output(uuid).await;