1use crate::{
2 displayed_item::{DisplayedItem, DisplayedItemRef},
3 message::{Message, MessageTarget},
4 wave_container::{ScopeRefExt, VariableRef, VariableRefExt},
5 wave_data::WaveData,
6 wave_source::{string_to_wavesource, LoadOptions, WaveSource},
7 SystemState, WcpClientCapabilities,
8};
9
10use futures::executor::block_on;
11use itertools::Itertools;
12use log::{trace, warn};
13use std::sync::atomic::Ordering;
14use surfer_translation_types::ScopeRef;
15
16use super::proto::{ItemInfo, 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 match message {
45 WcpCSMessage::greeting { .. } => (),
46 _ => {
47 self.send_error("WCP server has not received greeting messages", vec![], "");
48 return;
49 }
50 }
51 }
52 match message {
53 WcpCSMessage::command(command) => {
54 match command {
55 WcpCommand::get_item_list => {
56 if let Some(waves) = &self.user.waves {
57 let ids: Vec<crate::wcp::proto::DisplayedItemRef> = self
58 .get_displayed_items(waves)
59 .iter()
60 .map(|r| r.into())
61 .collect_vec();
62 self.send_response(WcpResponse::get_item_list { ids });
63 } else {
64 self.send_error("No waveform loaded", vec![], "No waveform loaded");
65 }
66 }
67 WcpCommand::get_item_info { ids } => {
68 let Some(waves) = &self.user.waves else {
69 self.send_error("remove_items", vec![], "No waveform loaded");
70 return;
71 };
72 let mut items: Vec<ItemInfo> = Vec::new();
73 for id in ids {
74 if let Some(item) = waves.displayed_items.get(&id.into()) {
75 let (name, item_type) = match item {
76 DisplayedItem::Variable(var) => (
77 var.manual_name.clone().unwrap_or(var.display_name.clone()),
78 "Variable".to_string(),
79 ),
80 DisplayedItem::Divider(item) => (
81 item.name.clone().unwrap_or("Name not found!".to_string()),
82 "Divider".to_string(),
83 ),
84 DisplayedItem::Marker(item) => (
85 item.name.clone().unwrap_or("Name not found!".to_string()),
86 "Marker".to_string(),
87 ),
88 DisplayedItem::TimeLine(item) => (
89 item.name.clone().unwrap_or("Name not found!".to_string()),
90 "TimeLine".to_string(),
91 ),
92 DisplayedItem::Placeholder(item) => (
93 item.manual_name
94 .clone()
95 .unwrap_or("Name not found!".to_string()),
96 "Placeholder".to_string(),
97 ),
98 DisplayedItem::Stream(item) => (
99 item.manual_name
100 .clone()
101 .unwrap_or(item.display_name.clone()),
102 "Stream".to_string(),
103 ),
104 DisplayedItem::Group(item) => {
105 (item.name.clone(), "Group".to_string())
106 }
107 };
108 items.push(ItemInfo {
109 name,
110 t: item_type,
111 id: *id,
112 });
113 } else {
114 self.send_error(
115 "get_item_info",
116 vec![],
117 &format!("No item with id {:?}", id),
118 );
119 return;
120 }
121 }
122 self.send_response(WcpResponse::get_item_info { results: items });
123 }
124 WcpCommand::add_variables { variables } => {
125 if self.user.waves.is_some() {
126 self.save_current_canvas(format!("Add {} variables", variables.len()));
127 }
128 if let Some(waves) = self.user.waves.as_mut() {
129 let variable_refs = variables
130 .iter()
131 .map(|n| VariableRef::from_hierarchy_string(n))
132 .collect_vec();
133 let (cmd, ids) =
134 waves.add_variables(&self.translators, variable_refs, None, true);
135 if let Some(cmd) = cmd {
136 self.load_variables(cmd);
137 }
138 self.send_response(WcpResponse::add_variables {
139 ids: ids.into_iter().map(|id| id.into()).collect_vec(),
140 });
141 self.invalidate_draw_commands();
142 } else {
143 self.send_error(
144 "add_variables",
145 vec![],
146 "Can't add signals. No waveform loaded",
147 )
148 }
149 }
150 WcpCommand::add_scope { scope, recursive } => {
151 if self.user.waves.is_some() {
152 self.save_current_canvas(format!("Add scope {}", scope));
153 }
154 let scope = ScopeRef::from_hierarchy_string(scope);
155 let variables = self.get_scope(scope, *recursive);
156 if let Some(waves) = self.user.waves.as_mut() {
157 let (cmd, ids) =
158 waves.add_variables(&self.translators, variables, None, true);
159 if let Some(cmd) = cmd {
160 self.load_variables(cmd);
161 }
162 self.send_response(WcpResponse::add_scope {
163 ids: ids.into_iter().map(|id| id.into()).collect_vec(),
164 });
165 self.invalidate_draw_commands();
166 } else {
167 self.send_error("scope_add", vec![], "No waveform loaded");
168 }
169 }
170 WcpCommand::reload => {
171 self.update(Message::ReloadWaveform(false));
172 self.send_response(WcpResponse::ack);
173 }
174 WcpCommand::set_viewport_to { timestamp } => {
175 self.update(Message::GoToTime(Some(timestamp.clone()), 0));
176 self.send_response(WcpResponse::ack);
177 }
178 WcpCommand::set_item_color { id, color } => {
179 let Some(waves) = &self.user.waves else {
180 self.send_error("set_item_color", vec![], "No waveform loaded");
181 return;
182 };
183
184 if let Some(idx) = waves.get_displayed_item_index(&id.into()) {
185 self.update(Message::ItemColorChange(
186 MessageTarget::Explicit(idx),
187 Some(color.clone()),
188 ));
189 self.send_response(WcpResponse::ack);
190 } else {
191 self.send_error(
192 "set_item_color",
193 vec![],
194 format!("Item {id:?} not found").as_str(),
195 );
196 }
197 }
198 WcpCommand::remove_items { ids } => {
199 let Some(_) = self.user.waves.as_mut() else {
200 self.send_error("remove_items", vec![], "No waveform loaded");
201 return;
202 };
203 let msgs =
204 vec![Message::RemoveItems(ids.iter().map(|d| d.into()).collect())];
205 self.update(Message::Batch(msgs));
206
207 self.send_response(WcpResponse::ack);
208 }
209 WcpCommand::focus_item { id } => {
210 let Some(waves) = &self.user.waves else {
211 self.send_error("remove_items", vec![], "No waveform loaded");
212 return;
213 };
214 if let Some(vidx) = waves.get_displayed_item_index(&id.into()) {
217 self.update(Message::FocusItem(vidx));
218
219 self.send_response(WcpResponse::ack);
220 } else {
221 self.send_error(
222 "focus_item",
223 vec![],
224 format!("No item with ID {id:?}").as_str(),
225 );
226 }
227 }
228 WcpCommand::clear => {
229 if let Some(wave) = &self.user.waves {
230 self.update(Message::RemoveItems(self.get_displayed_items(wave)));
231 }
232
233 self.send_response(WcpResponse::ack);
234 }
235 WcpCommand::load { source } => {
236 match string_to_wavesource(source) {
237 WaveSource::Url(url) => {
238 self.update(Message::LoadWaveformFileFromUrl(
239 url,
240 LoadOptions::clean(),
241 ));
242 self.send_response(WcpResponse::ack)
243 }
244 WaveSource::File(file) => {
245 let msg = Message::LoadFile(file, LoadOptions::clean());
247 self.update(msg);
248 self.send_response(WcpResponse::ack)
249 }
250 _ => {
251 self.send_error(
252 "load",
253 vec![],
254 format!("{source} is not legal wave source").as_str(),
255 );
256 }
257 }
258 }
259 WcpCommand::zoom_to_fit { viewport_idx } => {
260 self.update(Message::ZoomToFit {
261 viewport_idx: *viewport_idx,
262 });
263 self.send_response(WcpResponse::ack);
264 }
265 WcpCommand::shutdowmn => {
266 warn!("WCP Shutdown message should not reach this place")
267 }
268 };
269 }
270 WcpCSMessage::greeting { version, commands } => {
271 if version != "0" {
272 self.send_error(
273 "greeting",
274 vec![],
275 &format!(
276 "Surfer only supports WCP version 0, client requested {}",
277 version
278 ),
279 )
280 } else {
281 self.wcp_client_capabilities = WcpClientCapabilities::new();
282 if commands.iter().any(|s| s == "waveforms_loaded") {
283 self.wcp_client_capabilities.waveforms_loaded = true;
284 }
285 if commands.iter().any(|s| s == "goto_declaration") {
286 self.wcp_client_capabilities.goto_declaration = true;
287 }
288 if commands.iter().any(|s| s == "add_drivers") {
289 self.wcp_client_capabilities.add_drivers = true;
290 }
291 if commands.iter().any(|s| s == "add_loads") {
292 self.wcp_client_capabilities.add_loads = true;
293 }
294 self.wcp_greeted_signal.store(true, Ordering::Relaxed);
295 self.wcp_greeted_signal.store(true, Ordering::Relaxed);
296 self.send_greeting()
297 }
298 }
299 }
300 }
301
302 fn send_greeting(&self) {
303 let commands = vec![
304 "add_variables",
305 "set_viewport_to",
306 "cursor_set",
307 "reload",
308 "add_scope",
309 "get_item_list",
310 "set_item_color",
311 "get_item_info",
312 "clear_item",
313 "focus_item",
314 "clear",
315 "load",
316 "zoom_to_fit",
317 ]
318 .into_iter()
319 .map(str::to_string)
320 .collect_vec();
321
322 let greeting = WcpSCMessage::create_greeting(0, commands);
323
324 self.channels
325 .wcp_s2c_sender
326 .as_ref()
327 .map(|ch| block_on(ch.send(greeting)));
328 }
329
330 fn send_response(&self, result: WcpResponse) {
331 self.channels
332 .wcp_s2c_sender
333 .as_ref()
334 .map(|ch| block_on(ch.send(WcpSCMessage::response(result))));
335 }
336
337 fn send_error(&self, error: &str, arguments: Vec<String>, message: &str) {
338 self.channels.wcp_s2c_sender.as_ref().map(|ch| {
339 block_on(ch.send(WcpSCMessage::create_error(
340 error.to_string(),
341 arguments,
342 message.to_string(),
343 )))
344 });
345 }
346
347 fn get_displayed_items(&self, waves: &WaveData) -> Vec<DisplayedItemRef> {
348 waves
350 .items_tree
351 .iter_visible()
352 .map(|node| node.item_ref)
353 .collect_vec()
354 }
355}