shapes_demo_viewer/
shapes_demo_viewer.rs1#![allow(clippy::print_stdout, clippy::print_stderr)]
32
33use std::collections::HashMap;
34use std::env;
35use std::io::Write;
36use std::sync::Arc;
37use std::sync::atomic::{AtomicBool, Ordering};
38use std::thread;
39use std::time::Duration;
40
41use zerodds_dcps::interop::ShapeType;
42use zerodds_dcps::{
43 DataReaderQos, DomainParticipantFactory, DomainParticipantQos, SubscriberQos, TopicQos,
44};
45
46const VIEW_W: usize = 80;
47const VIEW_H: usize = 30;
48const SHAPES_CANVAS_W: i32 = 240;
49const SHAPES_CANVAS_H: i32 = 270;
50
51fn ansi_color(name: &str) -> &'static str {
53 match name.to_ascii_uppercase().as_str() {
54 "BLUE" => "\x1B[34m",
55 "RED" => "\x1B[31m",
56 "GREEN" => "\x1B[32m",
57 "ORANGE" => "\x1B[38;5;208m",
58 "YELLOW" => "\x1B[33m",
59 "MAGENTA" => "\x1B[35m",
60 "CYAN" => "\x1B[36m",
61 "PURPLE" => "\x1B[38;5;93m",
62 _ => "\x1B[37m",
63 }
64}
65
66const ANSI_RESET: &str = "\x1B[0m";
67
68fn glyph(topic: &str) -> char {
69 match topic {
70 "Square" => '■',
71 "Circle" => '●',
72 "Triangle" => '▲',
73 _ => '?',
74 }
75}
76
77fn map_x(x: i32) -> usize {
78 let r = x.clamp(0, SHAPES_CANVAS_W - 1);
79 (usize::try_from(r).unwrap_or(0) * VIEW_W) / usize::try_from(SHAPES_CANVAS_W).unwrap_or(1)
80}
81
82fn map_y(y: i32) -> usize {
83 let r = y.clamp(0, SHAPES_CANVAS_H - 1);
84 (usize::try_from(r).unwrap_or(0) * VIEW_H) / usize::try_from(SHAPES_CANVAS_H).unwrap_or(1)
85}
86
87fn install_signal_handler(stop: Arc<AtomicBool>) {
88 let s = stop.clone();
90 ctrlc_setter(move || s.store(true, Ordering::Relaxed));
91}
92
93#[cfg(target_os = "linux")]
94fn ctrlc_setter<F: Fn() + Send + Sync + 'static>(f: F) {
95 use std::sync::Mutex;
96 static HOOK: Mutex<Option<Box<dyn Fn() + Send + Sync>>> = Mutex::new(None);
97 if let Ok(mut g) = HOOK.lock() {
98 *g = Some(Box::new(f));
99 }
100 extern "C" fn handler(_: i32) {
101 if let Ok(g) = HOOK.lock() {
102 if let Some(h) = g.as_ref() {
103 h();
104 }
105 }
106 }
107 unsafe {
110 libc::signal(libc::SIGINT, handler as usize);
111 }
112}
113
114#[cfg(not(target_os = "linux"))]
115fn ctrlc_setter<F: Fn() + Send + Sync + 'static>(_: F) {}
116
117fn main() -> Result<(), Box<dyn std::error::Error>> {
118 let args: Vec<String> = env::args().collect();
119 let domain_id: i32 = args.get(1).and_then(|s| s.parse().ok()).unwrap_or(0);
120
121 let stop = Arc::new(AtomicBool::new(false));
122 install_signal_handler(stop.clone());
123
124 let factory = DomainParticipantFactory::instance();
125 let participant = factory.create_participant(domain_id, DomainParticipantQos::default())?;
126 let subscriber = participant.create_subscriber(SubscriberQos::default());
127
128 let topics = ["Square", "Circle", "Triangle"];
129 let mut readers = Vec::new();
130 for t in &topics {
131 let topic = participant.create_topic::<ShapeType>(t, TopicQos::default())?;
132 let reader = subscriber.create_datareader::<ShapeType>(&topic, DataReaderQos::default())?;
133 readers.push((*t, reader));
134 }
135
136 eprintln!(
137 "shapes_demo_viewer: Domain={domain_id} — subscribed to Square / Circle / Triangle. Ctrl-C beendet."
138 );
139 eprintln!("Warte auf Discovery + erste Samples...");
140
141 print!("\x1B[?25l\x1B[2J");
143 let mut stdout = std::io::stdout();
144 stdout.flush().ok();
145
146 let mut shapes: HashMap<(String, String), (i32, i32)> = HashMap::new();
148 let mut sample_count: u64 = 0;
149 let mut grid: Vec<Vec<(char, &'static str)>> = vec![vec![(' ', ""); VIEW_W]; VIEW_H];
150
151 while !stop.load(Ordering::Relaxed) {
152 for (topic_name, reader) in &readers {
154 if let Ok(samples) = reader.take() {
155 for sample in samples {
156 shapes.insert(
157 ((*topic_name).to_string(), sample.color.clone()),
158 (sample.x, sample.y),
159 );
160 sample_count += 1;
161 }
162 }
163 }
164
165 for row in &mut grid {
167 for cell in row {
168 *cell = (' ', "");
169 }
170 }
171
172 for ((topic, color), (x, y)) in &shapes {
174 let gx = map_x(*x);
175 let gy = map_y(*y);
176 if gy < VIEW_H && gx < VIEW_W {
177 grid[gy][gx] = (glyph(topic), ansi_color(color));
178 }
179 }
180
181 print!("\x1B[H");
183 print!("┌");
185 for _ in 0..VIEW_W {
186 print!("─");
187 }
188 println!("┐");
189 for row in &grid {
190 print!("│");
191 for (ch, color) in row {
192 if color.is_empty() {
193 print!(" ");
194 } else {
195 print!("{color}{ch}{ANSI_RESET}");
196 }
197 }
198 println!("│");
199 }
200 print!("└");
201 for _ in 0..VIEW_W {
202 print!("─");
203 }
204 println!("┘");
205 println!(
206 "shapes={:3} samples={:6} domain={} — Ctrl-C beendet ",
207 shapes.len(),
208 sample_count,
209 domain_id,
210 );
211 stdout.flush().ok();
212
213 thread::sleep(Duration::from_millis(60));
214 }
215
216 print!("\x1B[?25h");
218 println!("[shapes_demo_viewer] beendet. Total samples empfangen: {sample_count}");
219 Ok(())
220}