1use crate::{
2 SystemState, WcpClientCapabilities,
3 displayed_item::{DisplayedItem, DisplayedItemRef},
4 message::{Message, MessageTarget},
5 wave_container::{ScopeRefExt, VariableRef, VariableRefExt},
6 wave_data::WaveData,
7 wave_source::{LoadOptions, WaveSource, string_to_wavesource},
8};
9
10use futures::executor::block_on;
11use itertools::Itertools;
12use std::sync::atomic::Ordering;
13use surfer_translation_types::ScopeRef;
14use tracing::{trace, warn};
15
16use surfer_wcp::{ItemInfo, MarkerInfo, WcpCSMessage, WcpCommand, WcpResponse, WcpSCMessage};
17
18impl SystemState {
19 pub fn handle_wcp_commands(&mut self) {
20 let Some(receiver) = &mut self.channels.wcp_c2s_receiver else {
21 return;
22 };
23
24 let mut messages = vec![];
25 loop {
26 match receiver.try_recv() {
27 Ok(command) => {
28 messages.push(command);
29 }
30 Err(tokio::sync::mpsc::error::TryRecvError::Empty) => break,
31 Err(tokio::sync::mpsc::error::TryRecvError::Disconnected) => {
32 trace!("WCP Command sender disconnected");
33 break;
34 }
35 }
36 }
37 for message in messages {
38 self.handle_wcp_cs_message(&message);
39 }
40 }
41
42 fn handle_wcp_cs_message(&mut self, message: &WcpCSMessage) {
43 if !self.wcp_greeted_signal.load(Ordering::Relaxed) {
44 if let WcpCSMessage::greeting { .. } = message {
45 } else {
46 self.send_error("WCP server has not received greeting messages", vec![], "");
47 return;
48 }
49 }
50 match message {
51 WcpCSMessage::command(command) => {
52 match command {
53 WcpCommand::get_item_list => {
54 if let Some(waves) = &self.user.waves {
55 let ids: Vec<surfer_wcp::DisplayedItemRef> = self
56 .get_displayed_items(waves)
57 .iter()
58 .map(std::convert::Into::into)
59 .collect_vec();
60 self.send_response(WcpResponse::get_item_list { ids });
61 } else {
62 self.send_error("No waveform loaded", vec![], "No waveform loaded");
63 }
64 }
65 WcpCommand::get_item_info { ids } => {
66 let Some(waves) = &self.user.waves else {
67 self.send_error("remove_items", vec![], "No waveform loaded");
68 return;
69 };
70 let mut items: Vec<ItemInfo> = Vec::new();
71 for id in ids {
72 if let Some(item) = waves.displayed_items.get(&id.into()) {
73 let (name, item_type) = match item {
74 DisplayedItem::Variable(var) => (
75 var.manual_name.clone().unwrap_or(var.display_name.clone()),
76 "Variable".to_string(),
77 ),
78 DisplayedItem::Divider(item) => (
79 item.name.clone().unwrap_or("Name not found!".to_string()),
80 "Divider".to_string(),
81 ),
82 DisplayedItem::Marker(item) => (
83 item.name.clone().unwrap_or("Name not found!".to_string()),
84 "Marker".to_string(),
85 ),
86 DisplayedItem::TimeLine(item) => (
87 item.name.clone().unwrap_or("Name not found!".to_string()),
88 "TimeLine".to_string(),
89 ),
90 DisplayedItem::Placeholder(item) => (
91 item.manual_name
92 .clone()
93 .unwrap_or("Name not found!".to_string()),
94 "Placeholder".to_string(),
95 ),
96 DisplayedItem::Stream(item) => (
97 item.manual_name
98 .clone()
99 .unwrap_or(item.display_name.clone()),
100 "Stream".to_string(),
101 ),
102 DisplayedItem::Group(item) => {
103 (item.name.clone(), "Group".to_string())
104 }
105 };
106 items.push(ItemInfo {
107 name,
108 t: item_type,
109 id: *id,
110 });
111 } else {
112 self.send_error(
113 "get_item_info",
114 vec![],
115 &format!("No item with id {id:?}"),
116 );
117 return;
118 }
119 }
120 self.send_response(WcpResponse::get_item_info { results: items });
121 }
122 WcpCommand::add_variables { variables } => {
123 if self.user.waves.is_some() {
124 self.save_current_canvas(format!("Add {} variables", variables.len()));
125 }
126 if let Some(waves) = self.user.waves.as_mut() {
127 let variable_refs = variables
128 .iter()
129 .map(|n| VariableRef::from_hierarchy_string(n))
130 .collect_vec();
131 let (cmd, ids) = waves.add_variables(
132 &self.translators,
133 variable_refs,
134 None,
135 true,
136 false,
137 None,
138 );
139 if let Some(cmd) = cmd {
140 self.load_variables(cmd);
141 }
142 self.send_response(WcpResponse::add_variables {
143 ids: ids.into_iter().map(std::convert::Into::into).collect_vec(),
144 });
145 self.invalidate_draw_commands();
146 } else {
147 self.send_error(
148 "add_variables",
149 vec![],
150 "Can't add signals. No waveform loaded",
151 );
152 }
153 }
154 WcpCommand::add_scope { scope, recursive } => {
155 if self.user.waves.is_some() {
156 self.save_current_canvas(format!("Add scope {scope}"));
157 }
158 let scope = ScopeRef::from_hierarchy_string(scope);
159 let variables = self.get_scope(scope, *recursive);
160 if let Some(waves) = self.user.waves.as_mut() {
161 let (cmd, ids) = waves.add_variables(
162 &self.translators,
163 variables,
164 None,
165 true,
166 false,
167 None,
168 );
169 if let Some(cmd) = cmd {
170 self.load_variables(cmd);
171 }
172 self.send_response(WcpResponse::add_scope {
173 ids: ids.into_iter().map(std::convert::Into::into).collect_vec(),
174 });
175 self.invalidate_draw_commands();
176 } else {
177 self.send_error("scope_add", vec![], "No waveform loaded");
178 }
179 }
180 WcpCommand::add_items { items, recursive } => {
181 if self.user.waves.is_some() {
182 self.save_current_canvas(format!("Add {} items", items.len()));
183 }
184
185 let mut variables: Vec<VariableRef> = Vec::new();
186 for item in items {
187 let variable_ref = VariableRef::from_hierarchy_string(item);
188 let scope = ScopeRef::from_hierarchy_string(item);
189 let scope_variables = self.get_scope(scope, *recursive);
190 variables.push(variable_ref);
191 variables.extend(scope_variables);
192 }
193
194 if let Some(waves) = self.user.waves.as_mut() {
195 let (cmd, ids) = waves.add_variables(
196 &self.translators,
197 variables,
198 None,
199 true,
200 true,
201 None,
202 );
203 if let Some(cmd) = cmd {
204 self.load_variables(cmd);
205 }
206 self.send_response(WcpResponse::add_items {
207 ids: ids.into_iter().map(std::convert::Into::into).collect_vec(),
208 });
209 self.invalidate_draw_commands();
210 } else {
211 self.send_error(
212 "add_items",
213 vec![],
214 "Can't add items. No waveform loaded",
215 );
216 }
217 }
218 WcpCommand::add_markers { markers } => {
219 if self.user.waves.is_some() {
220 self.save_current_canvas(format!("Add {} markers", markers.len()));
221 }
222 if let Some(waves) = self.user.waves.as_mut() {
223 let mut ids = vec![];
224 for marker in markers {
225 let MarkerInfo {
226 time,
227 name,
228 move_focus,
229 } = marker;
230 if let Some(id) = waves.add_marker(time, name.clone(), *move_focus)
231 {
232 ids.push(id.into());
233 } else {
234 self.send_error("add_markers", vec![], "Cannot add marker");
235 return;
236 }
237 }
238 self.send_response(WcpResponse::add_markers { ids });
239 } else {
240 self.send_error("add_markers", vec![], "No waveform loaded");
241 }
242 }
243 WcpCommand::reload => {
244 self.update(Message::ReloadWaveform(false));
245 self.send_response(WcpResponse::ack);
246 }
247 WcpCommand::set_viewport_to { timestamp } => {
248 self.update(Message::GoToTime(Some(timestamp.clone()), 0));
249 self.send_response(WcpResponse::ack);
250 }
251 WcpCommand::set_viewport_range { start, end } => {
252 self.update(Message::ZoomToRange {
253 start: start.clone(),
254 end: end.clone(),
255 viewport_idx: 0,
256 });
257 self.send_response(WcpResponse::ack);
258 }
259 WcpCommand::set_item_color { id, color } => {
260 let Some(waves) = &self.user.waves else {
261 self.send_error("set_item_color", vec![], "No waveform loaded");
262 return;
263 };
264
265 if let Some(idx) = waves.get_displayed_item_index(&id.into()) {
266 self.update(Message::ItemColorChange(
267 MessageTarget::Explicit(idx),
268 Some(color.clone()),
269 ));
270 self.send_response(WcpResponse::ack);
271 } else {
272 self.send_error(
273 "set_item_color",
274 vec![],
275 format!("Item {id:?} not found").as_str(),
276 );
277 }
278 }
279 WcpCommand::remove_items { ids } => {
280 let Some(_) = self.user.waves.as_mut() else {
281 self.send_error("remove_items", vec![], "No waveform loaded");
282 return;
283 };
284 let msgs = vec![Message::RemoveItems(
285 ids.iter().map(std::convert::Into::into).collect(),
286 )];
287 self.update(Message::Batch(msgs));
288
289 self.send_response(WcpResponse::ack);
290 }
291 WcpCommand::focus_item { id } => {
292 let Some(waves) = &self.user.waves else {
293 self.send_error("remove_items", vec![], "No waveform loaded");
294 return;
295 };
296 if let Some(vidx) = waves.get_displayed_item_index(&id.into()) {
299 self.update(Message::FocusItem(vidx));
300
301 self.send_response(WcpResponse::ack);
302 } else {
303 self.send_error(
304 "focus_item",
305 vec![],
306 format!("No item with ID {id:?}").as_str(),
307 );
308 }
309 }
310 WcpCommand::clear => {
311 if let Some(wave) = &self.user.waves {
312 self.update(Message::RemoveItems(self.get_displayed_items(wave)));
313 }
314
315 self.send_response(WcpResponse::ack);
316 }
317 WcpCommand::load { source } => {
318 match string_to_wavesource(source) {
319 WaveSource::Url(url) => {
320 self.update(Message::LoadWaveformFileFromUrl(
321 url,
322 LoadOptions::Clear,
323 ));
324 self.send_response(WcpResponse::ack);
325 }
326 WaveSource::File(file) => {
327 let msg = Message::LoadFile(file, LoadOptions::Clear);
329 self.update(msg);
330 self.send_response(WcpResponse::ack);
331 }
332 _ => {
333 self.send_error(
334 "load",
335 vec![],
336 format!("{source} is not legal wave source").as_str(),
337 );
338 }
339 }
340 }
341 WcpCommand::zoom_to_fit { viewport_idx } => {
342 self.update(Message::ZoomToFit {
343 viewport_idx: *viewport_idx,
344 });
345 self.send_response(WcpResponse::ack);
346 }
347 WcpCommand::set_cursor { timestamp } => {
348 self.update(Message::CursorSet(timestamp.to_owned()));
349 self.send_response(WcpResponse::ack);
350 }
351 WcpCommand::shutdown => {
352 warn!("WCP Shutdown message should not reach this place");
353 }
354 }
355 }
356 WcpCSMessage::greeting { version, commands } => {
357 if version == "0" {
358 self.wcp_client_capabilities = WcpClientCapabilities::new();
359 if commands.iter().any(|s| s == "waveforms_loaded") {
360 self.wcp_client_capabilities.waveforms_loaded = true;
361 }
362 if commands.iter().any(|s| s == "goto_declaration") {
363 self.wcp_client_capabilities.goto_declaration = true;
364 }
365 if commands.iter().any(|s| s == "add_drivers") {
366 self.wcp_client_capabilities.add_drivers = true;
367 }
368 if commands.iter().any(|s| s == "add_loads") {
369 self.wcp_client_capabilities.add_loads = true;
370 }
371 self.wcp_greeted_signal.store(true, Ordering::Relaxed);
372 self.wcp_greeted_signal.store(true, Ordering::Relaxed);
373 self.send_greeting();
374 } else {
375 self.send_error(
376 "greeting",
377 vec![],
378 &format!("Surfer only supports WCP version 0, client requested {version}"),
379 );
380 }
381 }
382 }
383 }
384
385 fn send_greeting(&self) {
386 let commands = vec![
387 "add_variables",
388 "set_viewport_to",
389 "cursor_set",
390 "reload",
391 "add_scope",
392 "add_items",
393 "get_item_list",
394 "set_item_color",
395 "get_item_info",
396 "clear_item",
397 "focus_item",
398 "clear",
399 "load",
400 "zoom_to_fit",
401 "add_markers",
402 "set_viewport_range_to",
403 ]
404 .into_iter()
405 .map(str::to_string)
406 .collect_vec();
407
408 let greeting = WcpSCMessage::create_greeting(0, commands);
409
410 self.channels
411 .wcp_s2c_sender
412 .as_ref()
413 .map(|ch| block_on(ch.send(greeting)));
414 }
415
416 fn send_response(&self, result: WcpResponse) {
417 self.channels
418 .wcp_s2c_sender
419 .as_ref()
420 .map(|ch| block_on(ch.send(WcpSCMessage::response(result))));
421 }
422
423 fn send_error(&self, error: &str, arguments: Vec<String>, message: &str) {
424 self.channels.wcp_s2c_sender.as_ref().map(|ch| {
425 block_on(ch.send(WcpSCMessage::create_error(
426 error.to_string(),
427 arguments,
428 message.to_string(),
429 )))
430 });
431 }
432
433 fn get_displayed_items(&self, waves: &WaveData) -> Vec<DisplayedItemRef> {
434 waves
436 .items_tree
437 .iter_visible()
438 .map(|node| node.item_ref)
439 .collect_vec()
440 }
441}