1use std::{
2 collections::{HashMap, HashSet, VecDeque},
3 mem,
4 path::PathBuf,
5};
6
7use crate::{
8 CanvasState, StartupParams,
9 annotation_list::{AnnotationGroup, DEFAULT_GROUP_NAME},
10 clock_highlighting::ClockHighlightType,
11 config::{
12 ArrowKeyBindings, AutoLoad, FocusHighlight, PrimaryMouseDrag, SurferConfig, TransitionValue,
13 },
14 data_container::DataContainer,
15 dialog::{OpenSiblingStateFileDialog, ReloadWaveformDialog},
16 displayed_item_tree::{DisplayedItemTree, VisibleItemIndex},
17 frame_buffer::FrameBufferSettings,
18 hierarchy::{HierarchyStyle, ParameterDisplayLocation},
19 message::Message,
20 system_state::SystemState,
21 time::{TimeStringFormatting, TimeUnit},
22 trace_style::TraceStyle,
23 transaction_container::TransactionContainer,
24 variable_filter::VariableFilter,
25 viewport::Viewport,
26 wave_container::{ScopeRef, VariableRef, WaveContainer},
27 wave_data::WaveData,
28 wave_source::{LoadOptions, WaveFormat, WaveSource},
29};
30use egui::{
31 Visuals,
32 style::{Selection, WidgetVisuals, Widgets},
33};
34use epaint::{CornerRadius, Stroke};
35use eyre::{Result, WrapErr as _};
36use itertools::Itertools;
37use num::{BigInt, Zero};
38use serde::{Deserialize, Serialize};
39use surfer_translation_types::Translator;
40use surver::SurverFileInfo;
41use tracing::{error, info, trace, warn};
42
43#[derive(Serialize, Deserialize)]
45#[serde(default)]
46pub struct UserState {
47 #[serde(skip)]
48 pub config: SurferConfig,
49
50 pub(crate) show_hierarchy: Option<bool>,
54 pub(crate) show_menu: Option<bool>,
55 pub(crate) show_ticks: Option<bool>,
56 pub(crate) show_toolbar: Option<bool>,
57 pub(crate) show_tooltip: Option<bool>,
58 pub(crate) show_scope_tooltip: Option<bool>,
59 pub(crate) show_default_timeline: Option<bool>,
60 pub(crate) show_overview: Option<bool>,
61 pub(crate) show_statusbar: Option<bool>,
62 pub(crate) align_names_right: Option<bool>,
63 pub(crate) show_variable_indices: Option<bool>,
64 pub(crate) show_variable_direction: Option<bool>,
65 pub(crate) show_empty_scopes: Option<bool>,
66 pub(crate) show_hierarchy_icons: Option<bool>,
67 pub(crate) show_parameters_in_scopes: Option<bool>,
68 #[serde(default)]
69 pub(crate) parameter_display_location: Option<ParameterDisplayLocation>,
70 #[serde(default)]
71 pub(crate) fill_high_values: Option<bool>,
72 #[serde(default)]
73 pub(crate) primary_button_drag_behavior: Option<PrimaryMouseDrag>,
74 #[serde(default)]
75 pub(crate) arrow_key_bindings: Option<ArrowKeyBindings>,
76 #[serde(default)]
77 pub(crate) clock_highlight_type: Option<ClockHighlightType>,
78 #[serde(default)]
79 pub(crate) hierarchy_style: Option<HierarchyStyle>,
80 #[serde(default)]
81 pub(crate) autoload_sibling_state_files: Option<AutoLoad>,
82 #[serde(default)]
83 pub(crate) autoreload_files: Option<AutoLoad>,
84
85 pub(crate) waves: Option<WaveData>,
86 pub(crate) drag_started: bool,
87 pub(crate) drag_source_idx: Option<VisibleItemIndex>,
88 pub(crate) drag_target_idx: Option<crate::displayed_item_tree::TargetPosition>,
89
90 pub(crate) previous_waves: Option<WaveData>,
91
92 pub(crate) count: Option<String>,
94
95 pub(crate) blacklisted_translators: HashSet<(VariableRef, String)>,
97
98 pub(crate) show_about: bool,
99 pub(crate) show_keys: bool,
100 pub(crate) show_gestures: bool,
101 pub(crate) show_quick_start: bool,
102 pub(crate) show_license: bool,
103 pub(crate) show_performance: bool,
104 pub(crate) show_logs: bool,
105 pub(crate) show_cursor_window: bool,
106 #[serde(default)]
107 pub(crate) frame_buffer: FrameBufferSettings,
108 pub(crate) wanted_timeunit: TimeUnit,
109 pub(crate) time_string_format: Option<TimeStringFormatting>,
110 pub(crate) show_url_entry: bool,
111 #[serde(skip, default)]
114 pub(crate) show_reload_suggestion: Option<ReloadWaveformDialog>,
115 #[serde(skip, default)]
116 pub(crate) show_open_sibling_state_file_suggestion: Option<OpenSiblingStateFileDialog>,
117 pub(crate) variable_name_filter_focused: bool,
119 pub(crate) variable_filter: VariableFilter,
120 pub(crate) sidepanel_width: Option<f32>,
122 pub(crate) ui_zoom_factor: Option<f32>,
124 #[serde(default)]
125 pub(crate) animation_enabled: Option<bool>,
126 #[serde(skip, default)]
127 pub(crate) use_dinotrace_style: Option<bool>,
128 #[serde(default)]
129 pub(crate) trace_style: Option<TraceStyle>,
130 #[serde(skip, default)]
131 pub(crate) show_server_file_window: bool,
132 #[serde(skip, default)]
133 pub(crate) selected_server_file_index: Option<usize>,
134 #[serde(skip, default)]
135 pub(crate) surver_file_infos: Option<Vec<SurverFileInfo>>,
136 #[serde(skip, default)]
137 pub(crate) surver_url: Option<String>,
138 #[serde(default)]
139 pub(crate) transition_value: Option<TransitionValue>,
140 #[serde(default)]
141 pub(crate) toolbar_group_enabled: HashMap<String, Option<bool>>,
142 #[serde(default)]
143 pub(crate) toolbar_group_rows: Vec<Vec<String>>,
144 #[serde(default)]
145 pub(crate) draw_vector_unknowns_as_line: Option<bool>,
146 #[serde(default)]
147 pub(crate) focus_highlight: Option<FocusHighlight>,
148
149 #[serde(skip)]
155 pub state_file: Option<PathBuf>,
156
157 pub(crate) show_annotation_list: bool,
158 #[serde(default)]
159 pub(crate) enable_time_offset: Option<bool>,
160}
161
162impl std::fmt::Debug for UserState {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 write!(f, "SharedState {{ <snipped> }}")
167 }
168}
169
170impl UserState {
171 pub fn new(force_default_config: bool) -> Result<UserState> {
172 let config = SurferConfig::new(force_default_config)
173 .with_context(|| "Failed to load config file")?;
174
175 Ok(UserState {
176 config,
177 ..Default::default()
178 })
179 }
180}
181
182impl Default for UserState {
183 fn default() -> Self {
184 Self {
185 config: SurferConfig::default(),
186 show_hierarchy: None,
187 show_menu: None,
188 show_ticks: None,
189 show_toolbar: None,
190 show_tooltip: None,
191 show_scope_tooltip: None,
192 show_default_timeline: None,
193 show_overview: None,
194 show_statusbar: None,
195 align_names_right: None,
196 show_variable_indices: None,
197 show_variable_direction: None,
198 show_empty_scopes: None,
199 show_hierarchy_icons: None,
200 show_parameters_in_scopes: None,
201 parameter_display_location: None,
202 fill_high_values: None,
203 primary_button_drag_behavior: None,
204 arrow_key_bindings: None,
205 clock_highlight_type: None,
206 hierarchy_style: None,
207 autoload_sibling_state_files: None,
208 autoreload_files: None,
209 enable_time_offset: None,
210 waves: None,
211 drag_started: false,
212 drag_source_idx: None,
213 drag_target_idx: None,
214 previous_waves: None,
215 count: None,
216 blacklisted_translators: HashSet::new(),
217 show_about: false,
218 show_keys: false,
219 show_gestures: false,
220 show_quick_start: false,
221 show_license: false,
222 show_performance: false,
223 show_logs: false,
224 show_cursor_window: false,
225 frame_buffer: FrameBufferSettings::default(),
226 wanted_timeunit: TimeUnit::None,
227 time_string_format: None,
228 show_url_entry: false,
229 show_reload_suggestion: None,
230 show_open_sibling_state_file_suggestion: None,
231 variable_name_filter_focused: false,
232 variable_filter: VariableFilter::new(),
233 sidepanel_width: None,
234 ui_zoom_factor: None,
235 state_file: None,
236 animation_enabled: None,
237 use_dinotrace_style: None,
238 trace_style: None,
239 selected_server_file_index: None,
240 show_server_file_window: false,
241 surver_file_infos: None,
242 surver_url: None,
243 transition_value: None,
244 show_annotation_list: false,
245 toolbar_group_enabled: HashMap::new(),
246 toolbar_group_rows: Vec::new(),
247 draw_vector_unknowns_as_line: None,
248 focus_highlight: None,
249 }
250 }
251}
252
253impl SystemState {
254 pub fn with_params(mut self, args: StartupParams) -> Self {
255 self.user.previous_waves = self.user.waves;
256 self.user.waves = None;
257
258 self.batch_messages = VecDeque::new();
260
261 match args.waves {
262 Some(WaveSource::Url(url)) => {
263 self.add_batch_message(Message::LoadWaveformFileFromUrl(url, LoadOptions::KeepAll));
264 }
265 Some(WaveSource::File(file)) => {
266 self.add_batch_message(Message::LoadFile(file, LoadOptions::KeepAll));
267 }
268 Some(WaveSource::Data) => error!("Attempted to load data at startup"),
269 Some(WaveSource::Cxxrtl(url)) => {
270 self.add_batch_message(Message::SetupCxxrtl(url));
271 }
272 Some(WaveSource::DragAndDrop(_)) => {
273 error!("Attempted to load from drag and drop at startup (how?)");
274 }
275 None => {}
276 }
277
278 if let Some(port) = args.wcp_initiate {
279 let addr = format!("127.0.0.1:{port}");
280 self.add_batch_message(Message::StartWcpServer {
281 address: Some(addr),
282 initiate: true,
283 });
284 }
285
286 self.add_batch_commands(args.startup_commands);
287
288 self
289 }
290
291 pub fn wcp(&mut self) {
292 self.handle_wcp_commands();
293 }
294
295 pub(crate) fn get_scope(&mut self, scope: &ScopeRef, recursive: bool) -> Vec<VariableRef> {
296 let Some(waves) = self.user.waves.as_mut() else {
297 return vec![];
298 };
299
300 let wave_cont = waves.inner.as_waves().unwrap();
301
302 let children = wave_cont.child_scopes(scope);
303 let mut variables = wave_cont
304 .variables_in_scope(scope)
305 .iter()
306 .sorted_by(|a, b| numeric_sort::cmp(&a.name, &b.name))
307 .cloned()
308 .collect_vec();
309
310 if recursive && let Ok(children) = children {
311 for child in children {
312 variables.append(&mut self.get_scope(&child, true));
313 }
314 }
315
316 variables
317 }
318
319 pub(crate) fn on_waves_loaded(
320 &mut self,
321 filename: WaveSource,
322 format: WaveFormat,
323 new_waves: Box<WaveContainer>,
324 load_options: LoadOptions,
325 ) {
326 let filename_for_title = filename.clone();
327 info!("{format} file loaded");
328 let viewport = Viewport::new();
329 let viewports = [viewport].to_vec();
330
331 for translator in self.translators.all_translators() {
332 translator.set_wave_source(Some(filename.into_translation_type()));
333 }
334
335 let ((new_wave, load_commands), is_reload) =
336 if load_options != LoadOptions::Clear && self.user.waves.is_some() {
337 (
338 self.user.waves.take().unwrap().update_with_waves(
339 new_waves,
340 filename,
341 format,
342 &self.translators,
343 load_options == LoadOptions::KeepAll,
344 ),
345 true,
346 )
347 } else if let Some(old) = self.user.previous_waves.take() {
348 (
349 old.update_with_waves(
350 new_waves,
351 filename,
352 format,
353 &self.translators,
354 load_options == LoadOptions::KeepAll,
355 ),
356 true,
357 )
358 } else {
359 (
360 (
361 WaveData {
362 inner: DataContainer::Waves(*new_waves),
363 source: filename,
364 format,
365 active_scope: None,
366 items_tree: DisplayedItemTree::default(),
367 displayed_items: HashMap::new(),
368 viewports,
369 cursor: None,
370 markers: HashMap::new(),
371 annotations: Vec::new(),
372 selected_annotation: None,
373 annotation_counter: 0,
374 last_active_viewport_idx: 0,
375 annotation_menu_pos: None,
376 annotation_menu_time: None,
377 focused_item: None,
378 focused_transaction: (None, None),
379 default_variable_name_type: self.user.config.default_variable_name_type,
380 display_variable_indices: self.show_variable_indices(),
381 scroll_offset: 0.,
382 drawing_infos: vec![],
383 top_item_draw_offset: 0.,
384 total_height: 0.,
385 display_item_ref_counter: 0,
386 old_max_timestamp: None,
387 graphics: HashMap::new(),
388 cache_generation: 0,
389 inflight_caches: HashMap::new(),
390 annotation_groups: vec![],
391 annotation_list_visible: false,
392 cached_time_offset: BigInt::zero(),
393 },
394 None,
395 ),
396 false,
397 )
398 };
399
400 if let Some(cmd) = load_commands {
401 self.load_variables(cmd);
402 }
403 self.invalidate_draw_commands();
404
405 let enable_time_offset = self.enable_time_offset();
407 self.user.waves = Some(new_wave);
408
409 if let Some(waves) = &mut self.user.waves {
411 waves.refresh_time_offset(enable_time_offset);
412 }
413
414 let title = match &filename_for_title {
416 WaveSource::File(path) => {
417 if let Some(name) = path.file_name() {
418 format!("Surfer - {name}")
419 } else {
420 "Surfer".to_string()
421 }
422 }
423 WaveSource::Url(url) => format!("Surfer - {url}"),
424 _ => "Surfer".to_string(),
425 };
426 if let Some(ctx) = self.context.as_ref() {
427 ctx.send_viewport_cmd(egui::ViewportCommand::Title(title));
428 }
429
430 self.record_file_history(&filename_for_title);
431
432 if !is_reload && let Some(waves) = &mut self.user.waves {
433 self.user.wanted_timeunit = waves.inner.metadata().timescale.unit;
435
436 let ungrouped = AnnotationGroup {
437 name: String::from(DEFAULT_GROUP_NAME),
438 cycle_counter: 0,
439 annotations: Vec::new(),
440 };
441
442 waves.annotation_groups.push(ungrouped);
443
444 if waves.source.sibling_state_file().is_some() {
446 self.update(Message::SuggestOpenSiblingStateFile);
447 }
448 }
449 }
450
451 pub(crate) fn on_transaction_streams_loaded(
452 &mut self,
453 filename: WaveSource,
454 format: WaveFormat,
455 new_ftr: TransactionContainer,
456 _loaded_options: LoadOptions,
457 ) {
458 info!("Transaction streams are loaded.");
459 self.record_file_history(&filename);
460
461 let viewport = Viewport::new();
462 let viewports = [viewport].to_vec();
463
464 let mut new_transaction_streams = WaveData {
465 inner: DataContainer::Transactions(new_ftr),
466 source: filename,
467 format,
468 active_scope: None,
469 items_tree: DisplayedItemTree::default(),
470 displayed_items: HashMap::new(),
471 viewports,
472 cursor: None,
473 markers: HashMap::new(),
474 annotations: Vec::new(),
475 selected_annotation: None,
476 annotation_counter: 1,
477 last_active_viewport_idx: 0,
478 annotation_menu_pos: None,
479 annotation_menu_time: None,
480 focused_item: None,
481 focused_transaction: (None, None),
482 default_variable_name_type: self.user.config.default_variable_name_type,
483 display_variable_indices: self.show_variable_indices(),
484 scroll_offset: 0.,
485 drawing_infos: vec![],
486 top_item_draw_offset: 0.,
487 total_height: 0.,
488 display_item_ref_counter: 0,
489 old_max_timestamp: None,
490 graphics: HashMap::new(),
491 cache_generation: 0,
492 inflight_caches: HashMap::new(),
493 annotation_groups: vec![],
494 annotation_list_visible: false,
495 cached_time_offset: BigInt::zero(),
496 };
497
498 let enable_time_offset = self.enable_time_offset();
499 new_transaction_streams.refresh_time_offset(enable_time_offset);
500
501 self.invalidate_draw_commands();
502
503 self.user.config.theme.alt_frequency = 0;
504 self.user.wanted_timeunit = new_transaction_streams.inner.metadata().timescale.unit;
505 self.user.waves = Some(new_transaction_streams);
506 }
507
508 fn record_file_history(&mut self, source: &WaveSource) {
509 if let Some(path) = source.path() {
510 self.file_history.add(path);
511 }
512 }
513
514 #[cfg(test)]
515 pub(crate) fn handle_async_messages(&mut self) {
516 let mut msgs = vec![];
517 loop {
518 match self.channels.msg_receiver.try_recv() {
519 Ok(msg) => msgs.push(msg),
520 Err(std::sync::mpsc::TryRecvError::Empty) => break,
521 Err(std::sync::mpsc::TryRecvError::Disconnected) => {
522 trace!("Message sender disconnected");
523 break;
524 }
525 }
526 }
527
528 while let Some(msg) = msgs.pop() {
529 self.update(msg);
530 }
531 }
532
533 pub(crate) fn push_async_messages(&mut self, msgs: &mut Vec<Message>) {
534 loop {
535 match self.channels.msg_receiver.try_recv() {
536 Ok(msg) => msgs.push(msg),
537 Err(std::sync::mpsc::TryRecvError::Empty) => break,
538 Err(std::sync::mpsc::TryRecvError::Disconnected) => {
539 trace!("Message sender disconnected");
540 break;
541 }
542 }
543 }
544 }
545
546 pub fn get_visuals(&self) -> Visuals {
547 let widget_style = WidgetVisuals {
548 bg_fill: self.user.config.theme.secondary_ui_color.background,
549 fg_stroke: Stroke {
550 color: self.user.config.theme.secondary_ui_color.foreground,
551 width: 1.0,
552 },
553 weak_bg_fill: self.user.config.theme.secondary_ui_color.background,
554 bg_stroke: Stroke {
555 color: self.user.config.theme.border_color,
556 width: 1.0,
557 },
558 corner_radius: CornerRadius::same(2),
559 expansion: 0.0,
560 };
561
562 Visuals {
563 override_text_color: Some(self.user.config.theme.foreground),
564 extreme_bg_color: self.user.config.theme.secondary_ui_color.background,
565 panel_fill: self.user.config.theme.secondary_ui_color.background,
566 window_fill: self.user.config.theme.primary_ui_color.background,
567 window_stroke: Stroke {
568 width: 1.0,
569 color: self.user.config.theme.border_color,
570 },
571 selection: Selection {
572 bg_fill: self.user.config.theme.selected_elements_colors.background,
573 stroke: Stroke {
574 color: self.user.config.theme.selected_elements_colors.foreground,
575 width: 1.0,
576 },
577 },
578 widgets: Widgets {
579 noninteractive: widget_style,
580 inactive: widget_style,
581 hovered: widget_style,
582 active: widget_style,
583 open: widget_style,
584 },
585 ..Visuals::dark()
586 }
587 }
588
589 pub(crate) fn load_state(&mut self, mut loaded_state: Box<UserState>, path: Option<PathBuf>) {
590 mem::swap(&mut self.user, &mut loaded_state);
592
593 mem::swap(&mut loaded_state.waves, &mut self.user.waves);
596 let load_commands = if let (Some(waves), Some(new_waves)) =
597 (&mut self.user.waves, &mut loaded_state.waves)
598 {
599 mem::swap(&mut waves.active_scope, &mut new_waves.active_scope);
600 let items = std::mem::take(&mut new_waves.displayed_items);
601 let items_tree = std::mem::take(&mut new_waves.items_tree);
602 let load_commands = waves.update_with_items(&items, items_tree, &self.translators);
603
604 mem::swap(&mut waves.viewports, &mut new_waves.viewports);
605 mem::swap(&mut waves.cursor, &mut new_waves.cursor);
606 mem::swap(&mut waves.markers, &mut new_waves.markers);
607 mem::swap(&mut waves.focused_item, &mut new_waves.focused_item);
608
609 mem::swap(&mut waves.annotations, &mut new_waves.annotations);
610 mem::swap(
612 &mut waves.annotation_groups,
613 &mut new_waves.annotation_groups,
614 );
615 mem::swap(
616 &mut waves.annotation_list_visible,
617 &mut new_waves.annotation_list_visible,
618 );
619 mem::swap(
620 &mut waves.annotation_counter,
621 &mut new_waves.annotation_counter,
622 );
623 mem::swap(
624 &mut waves.selected_annotation,
625 &mut new_waves.selected_annotation,
626 );
627 waves.default_variable_name_type = new_waves.default_variable_name_type;
628 waves.scroll_offset = new_waves.scroll_offset;
629 load_commands
630 } else {
631 None
632 };
633 if let Some(load_commands) = load_commands {
634 self.load_variables(load_commands);
635 }
636
637 self.user.drag_started = false;
639 self.user.drag_source_idx = None;
640 self.user.drag_target_idx = None;
641
642 self.user.previous_waves = None;
644 self.user.count = None;
645
646 self.user.state_file = path;
648
649 let enable_time_offset = self.enable_time_offset();
650 self.invalidate_draw_commands();
651 if let Some(waves) = &mut self.user.waves {
652 waves.refresh_time_offset(enable_time_offset);
653 waves.update_viewports();
654 }
655 }
656
657 pub fn waves_fully_loaded(&self) -> bool {
661 self.user
662 .waves
663 .as_ref()
664 .is_some_and(|w| w.inner.is_fully_loaded())
665 }
666
667 pub fn analog_caches_ready(&self) -> bool {
669 self.user
670 .waves
671 .as_ref()
672 .is_none_or(|w| w.inflight_caches.is_empty())
673 }
674
675 pub(crate) fn current_canvas_state(waves: &WaveData, message: String) -> CanvasState {
677 CanvasState {
678 message,
679 focused_item: waves.focused_item,
680 focused_transaction: waves.focused_transaction.clone(),
681 items_tree: waves.items_tree.clone(),
682 displayed_items: waves.displayed_items.clone(),
683 markers: waves.markers.clone(),
684 annotations: waves.annotations.clone(),
685 annotation_group: waves.annotation_groups.clone(),
686 annotation_list: waves.annotation_list_visible,
687 selected_annotation: waves.selected_annotation,
688 annotation_counter: waves.annotation_counter,
689 }
690 }
691
692 pub(crate) fn save_current_canvas(&mut self, message: String) {
694 if let Some(waves) = &self.user.waves {
695 self.undo_stack
696 .push(SystemState::current_canvas_state(waves, message));
697
698 if self.undo_stack.len() > self.user.config.undo_stack_size {
699 self.undo_stack.remove(0);
700 }
701 self.redo_stack.clear();
702 }
703 }
704
705 #[cfg(not(target_arch = "wasm32"))]
706 pub(crate) fn start_wcp_server(&mut self, address: Option<String>, initiate: bool) {
707 use wcp::wcp_server::WcpServer;
708
709 use crate::wcp;
710
711 if self.wcp_server_thread.as_ref().is_some()
712 || self
713 .wcp_running_signal
714 .load(std::sync::atomic::Ordering::Relaxed)
715 {
716 warn!("WCP HTTP server is already running");
717 return;
718 }
719 let (wcp_s2c_sender, wcp_s2c_receiver) = tokio::sync::mpsc::channel(100);
721 let (wcp_c2s_sender, wcp_c2s_receiver) = tokio::sync::mpsc::channel(100);
722
723 self.channels.wcp_c2s_receiver = Some(wcp_c2s_receiver);
724 self.channels.wcp_s2c_sender = Some(wcp_s2c_sender);
725 let stop_signal_copy = self.wcp_stop_signal.clone();
726 stop_signal_copy.store(false, std::sync::atomic::Ordering::Relaxed);
727 let running_signal_copy = self.wcp_running_signal.clone();
728 running_signal_copy.store(true, std::sync::atomic::Ordering::Relaxed);
729 let greeted_signal_copy = self.wcp_greeted_signal.clone();
730 greeted_signal_copy.store(true, std::sync::atomic::Ordering::Relaxed);
731
732 let ctx = self.context.clone();
733 let address = address.unwrap_or(self.user.config.wcp.address.clone());
734 self.wcp_server_address = Some(address.clone());
735 self.wcp_server_thread = Some(tokio::spawn(async move {
736 let server = WcpServer::new(
737 address,
738 initiate,
739 wcp_c2s_sender,
740 wcp_s2c_receiver,
741 stop_signal_copy,
742 running_signal_copy,
743 greeted_signal_copy,
744 ctx,
745 )
746 .await;
747 match server {
748 Ok(mut server) => server.run().await,
749 Err(m) => {
750 error!("Could not start WCP server. {m:?}");
751 }
752 }
753 }));
754 }
755
756 #[cfg_attr(target_arch = "wasm32", allow(dead_code))]
757 pub(crate) fn stop_wcp_server(&mut self) {
758 if self.wcp_server_address.is_some() && self.wcp_server_thread.is_some() {
761 self.wcp_stop_signal
763 .store(true, std::sync::atomic::Ordering::Relaxed);
764
765 self.wcp_server_thread = None;
766 self.wcp_server_address = None;
767 self.channels.wcp_s2c_sender = None;
768 self.channels.wcp_c2s_receiver = None;
769 info!("Stopped WCP server");
770 }
771 }
772}