libsurfer/
wave_container.rs

1use std::sync::Mutex;
2
3use chrono::prelude::{DateTime, Utc};
4use color_eyre::{eyre::bail, Result};
5use num::BigUint;
6use serde::{Deserialize, Serialize};
7use surfer_translation_types::VariableValue;
8
9use crate::cxxrtl_container::CxxrtlContainer;
10use crate::time::{TimeScale, TimeUnit};
11use crate::wellen::{BodyResult, LoadSignalsCmd, LoadSignalsResult, WellenContainer};
12
13pub type FieldRef = surfer_translation_types::FieldRef<VarId, ScopeId>;
14pub type ScopeRef = surfer_translation_types::ScopeRef<ScopeId>;
15pub type VariableRef = surfer_translation_types::VariableRef<VarId, ScopeId>;
16pub type VariableMeta = surfer_translation_types::VariableMeta<VarId, ScopeId>;
17
18#[derive(Debug, Clone)]
19pub enum SimulationStatus {
20    Paused,
21    Running,
22    Finished,
23}
24
25pub struct MetaData {
26    pub date: Option<DateTime<Utc>>,
27    pub version: Option<String>,
28    pub timescale: TimeScale,
29}
30
31/// A backend-specific, numeric reference for fast access to the associated scope.
32#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
33pub enum ScopeId {
34    None,
35    Wellen(wellen::ScopeRef),
36}
37
38impl Default for ScopeId {
39    fn default() -> Self {
40        Self::None
41    }
42}
43
44/// A backend-specific, numeric reference for fast access to the associated variable.
45#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
46pub enum VarId {
47    None,
48    Wellen(wellen::VarRef),
49}
50
51impl Default for VarId {
52    fn default() -> Self {
53        Self::None
54    }
55}
56
57#[derive(Debug, Default)]
58pub struct QueryResult {
59    pub current: Option<(BigUint, VariableValue)>,
60    pub next: Option<BigUint>,
61}
62
63#[local_impl::local_impl]
64impl ScopeRefExt for ScopeRef {
65    fn empty() -> Self {
66        Self {
67            strs: vec![],
68            id: ScopeId::default(),
69        }
70    }
71
72    fn from_strs<S: ToString>(s: &[S]) -> Self {
73        Self::from_strs_with_id(s, ScopeId::default())
74    }
75
76    fn from_strs_with_id(s: &[impl ToString], id: ScopeId) -> Self {
77        let strs = s.iter().map(std::string::ToString::to_string).collect();
78        Self { strs, id }
79    }
80
81    /// Creates a ScopeRef from a string with each scope separated by `.`
82    fn from_hierarchy_string(s: &str) -> Self {
83        let strs = s.split('.').map(std::string::ToString::to_string).collect();
84        let id = ScopeId::default();
85        Self { strs, id }
86    }
87
88    fn with_subscope(&self, subscope: String, id: ScopeId) -> Self {
89        let mut result = self.clone();
90        result.strs.push(subscope);
91        // the result refers to a different scope, which we do not know the ID of
92        result.id = id;
93        result
94    }
95
96    fn name(&self) -> String {
97        self.strs.last().cloned().unwrap_or_default()
98    }
99
100    fn strs(&self) -> &[String] {
101        &self.strs
102    }
103
104    fn with_id(&self, id: ScopeId) -> Self {
105        let mut out = self.clone();
106        out.id = id;
107        out
108    }
109
110    fn cxxrtl_repr(&self) -> String {
111        self.strs.join(" ")
112    }
113
114    fn has_empty_strs(&self) -> bool {
115        self.strs.is_empty()
116    }
117}
118
119#[local_impl::local_impl]
120impl VariableRefExt for VariableRef {
121    fn new(path: ScopeRef, name: String) -> Self {
122        Self::new_with_id(path, name, VarId::default())
123    }
124
125    fn new_with_id(path: ScopeRef, name: String, id: VarId) -> Self {
126        Self { path, name, id }
127    }
128
129    fn from_hierarchy_string(s: &str) -> Self {
130        let components = s
131            .split('.')
132            .map(std::string::ToString::to_string)
133            .collect::<Vec<_>>();
134
135        if components.is_empty() {
136            Self {
137                path: ScopeRef::empty(),
138                name: String::new(),
139                id: VarId::default(),
140            }
141        } else {
142            Self {
143                path: ScopeRef::from_strs(&components[..(components.len()) - 1]),
144                name: components.last().unwrap().to_string(),
145                id: VarId::default(),
146            }
147        }
148    }
149
150    /// A human readable full path to the scope
151    fn full_path_string(&self) -> String {
152        if self.path.has_empty_strs() {
153            self.name.clone()
154        } else {
155            format!("{}.{}", self.path, self.name)
156        }
157    }
158
159    fn full_path(&self) -> Vec<String> {
160        self.path
161            .strs()
162            .iter()
163            .cloned()
164            .chain([self.name.clone()])
165            .collect()
166    }
167
168    fn from_strs(s: &[&str]) -> Self {
169        Self {
170            path: ScopeRef::from_strs(&s[..(s.len() - 1)]),
171            name: s
172                .last()
173                .expect("from_strs called with an empty string")
174                .to_string(),
175            id: VarId::default(),
176        }
177    }
178
179    fn clear_id(&mut self) {
180        self.id = VarId::default();
181    }
182
183    fn cxxrtl_repr(&self) -> String {
184        self.full_path().join(" ")
185    }
186}
187
188#[local_impl::local_impl]
189impl FieldRefExt for FieldRef {
190    fn without_fields(root: VariableRef) -> Self {
191        Self {
192            root,
193            field: vec![],
194        }
195    }
196
197    fn from_strs(root: &[&str], field: &[&str]) -> Self {
198        Self {
199            root: VariableRef::from_strs(root),
200            field: field.iter().map(std::string::ToString::to_string).collect(),
201        }
202    }
203}
204
205pub enum WaveContainer {
206    Wellen(Box<WellenContainer>),
207    /// A wave container that contains nothing. Currently, the only practical use for this is
208    /// a placehodler when serializing and deserializing wave state.
209    Empty,
210    Cxxrtl(Box<Mutex<CxxrtlContainer>>),
211}
212
213impl WaveContainer {
214    pub fn new_waveform(hierarchy: std::sync::Arc<wellen::Hierarchy>) -> Self {
215        WaveContainer::Wellen(Box::new(WellenContainer::new(hierarchy, None)))
216    }
217
218    pub fn new_remote_waveform(
219        server_url: String,
220        hierarchy: std::sync::Arc<wellen::Hierarchy>,
221    ) -> Self {
222        WaveContainer::Wellen(Box::new(WellenContainer::new(hierarchy, Some(server_url))))
223    }
224
225    /// Creates a new empty wave container. Should only be used as a default for serde. If
226    /// no wave container is present, the WaveData should be None, rather than this being
227    /// Empty
228    pub fn __new_empty() -> Self {
229        WaveContainer::Empty
230    }
231
232    // Perform tasks that are done on the main thread each frame
233    pub fn tick(&self) {
234        match self {
235            WaveContainer::Wellen(_) => {}
236            WaveContainer::Empty => {}
237            WaveContainer::Cxxrtl(c) => c.lock().unwrap().tick(),
238        }
239    }
240
241    pub fn wants_anti_aliasing(&self) -> bool {
242        match self {
243            WaveContainer::Wellen(_) => true,
244            WaveContainer::Empty => true,
245            // FIXME: Once we do AA on the server side, we can set this to false
246            WaveContainer::Cxxrtl(_) => true,
247        }
248    }
249
250    /// Returns true if all requested signals have been loaded.
251    /// Used for testing to make sure the GUI is at its final state before taking a
252    /// snapshot.
253    pub fn is_fully_loaded(&self) -> bool {
254        match self {
255            WaveContainer::Wellen(f) => f.is_fully_loaded(),
256            WaveContainer::Empty => true,
257            WaveContainer::Cxxrtl(_) => true,
258        }
259    }
260
261    /// Returns the full names of all variables in the design.
262    pub fn variable_names(&self) -> Vec<String> {
263        match self {
264            WaveContainer::Wellen(f) => f.variable_names(),
265            WaveContainer::Empty => vec![],
266            // I don't know if we can do
267            WaveContainer::Cxxrtl(_) => vec![], // FIXME: List variable names
268        }
269    }
270
271    pub fn variables(&self) -> Vec<VariableRef> {
272        match self {
273            WaveContainer::Wellen(f) => f.variables(),
274            WaveContainer::Empty => vec![],
275            WaveContainer::Cxxrtl(_) => vec![],
276        }
277    }
278
279    /// Return all variables (excluding parameters) in a scope.
280    pub fn variables_in_scope(&self, scope: &ScopeRef) -> Vec<VariableRef> {
281        match self {
282            WaveContainer::Wellen(f) => f.variables_in_scope(scope),
283            WaveContainer::Empty => vec![],
284            WaveContainer::Cxxrtl(c) => c.lock().unwrap().variables_in_module(scope),
285        }
286    }
287
288    /// Return all parameters in a scope.
289    pub fn parameters_in_scope(&self, scope: &ScopeRef) -> Vec<VariableRef> {
290        match self {
291            WaveContainer::Wellen(f) => f.parameters_in_scope(scope),
292            WaveContainer::Empty => vec![],
293            // No parameters in Cxxrtl
294            WaveContainer::Cxxrtl(_) => vec![],
295        }
296    }
297
298    /// Return true if there are no variables or parameters in the scope.
299    pub fn no_variables_in_scope(&self, scope: &ScopeRef) -> bool {
300        match self {
301            WaveContainer::Wellen(f) => f.no_variables_in_scope(scope),
302            WaveContainer::Empty => true,
303            WaveContainer::Cxxrtl(c) => c.lock().unwrap().no_variables_in_module(scope),
304        }
305    }
306
307    /// Loads multiple variables at once. This is useful when we want to add multiple variables in one go.
308    pub fn load_variables<S: AsRef<VariableRef>, T: Iterator<Item = S>>(
309        &mut self,
310        variables: T,
311    ) -> Result<Option<LoadSignalsCmd>> {
312        match self {
313            WaveContainer::Wellen(f) => f.load_variables(variables),
314            WaveContainer::Empty => bail!("Cannot load variables from empty container."),
315            WaveContainer::Cxxrtl(c) => {
316                c.get_mut().unwrap().load_variables(variables);
317                Ok(None)
318            }
319        }
320    }
321    /// Load all the parameters in the design so that the value can be displayed.
322    pub fn load_parameters(&mut self) -> Result<Option<LoadSignalsCmd>> {
323        match self {
324            WaveContainer::Wellen(f) => f.load_all_params(),
325            WaveContainer::Empty => bail!("Cannot load parameters from empty container."),
326            WaveContainer::Cxxrtl(_) => {
327                // Cxxrtl does not deal with parameters
328                Ok(None)
329            }
330        }
331    }
332
333    /// Callback for when wellen signals have been loaded. Might lead to a new load variable
334    /// command since new variables might have been requested in the meantime.
335    pub fn on_signals_loaded(&mut self, res: LoadSignalsResult) -> Result<Option<LoadSignalsCmd>> {
336        match self {
337            WaveContainer::Wellen(f) => f.on_signals_loaded(res),
338            WaveContainer::Empty => {
339                bail!("on_load_signals should only be called with the wellen backend.")
340            }
341            WaveContainer::Cxxrtl(_) => {
342                bail!("on_load_signals should only be called with the wellen backend.")
343            }
344        }
345    }
346
347    pub fn variable_meta<'a>(&'a self, variable: &'a VariableRef) -> Result<VariableMeta> {
348        match self {
349            WaveContainer::Wellen(f) => f.variable_to_meta(variable),
350            WaveContainer::Empty => bail!("Getting meta from empty wave container"),
351            WaveContainer::Cxxrtl(c) => c.lock().unwrap().variable_meta(variable),
352        }
353    }
354
355    /// Query the value of the variable at a certain time step.
356    /// Returns `None` if we do not have any values for the variable.
357    /// That generally happens if the corresponding variable is still being loaded.
358    pub fn query_variable(
359        &self,
360        variable: &VariableRef,
361        time: &BigUint,
362    ) -> Result<Option<QueryResult>> {
363        match self {
364            WaveContainer::Wellen(f) => f.query_variable(variable, time),
365            WaveContainer::Empty => bail!("Querying variable from empty wave container"),
366            WaveContainer::Cxxrtl(c) => Ok(c.lock().unwrap().query_variable(variable, time)),
367        }
368    }
369
370    /// Looks up the variable _by name_ and returns a new reference with an updated `id` if the variable is found.
371    pub fn update_variable_ref(&self, variable: &VariableRef) -> Option<VariableRef> {
372        match self {
373            WaveContainer::Wellen(f) => f.update_variable_ref(variable),
374            WaveContainer::Empty => None,
375            WaveContainer::Cxxrtl(_) => None,
376        }
377    }
378
379    /// Returns the full names of all scopes in the design.
380    pub fn scope_names(&self) -> Vec<String> {
381        match self {
382            WaveContainer::Wellen(f) => f.scope_names(),
383            WaveContainer::Empty => vec![],
384            WaveContainer::Cxxrtl(c) => c
385                .lock()
386                .unwrap()
387                .modules()
388                .iter()
389                .map(|m| m.strs().last().cloned().unwrap_or("root".to_string()))
390                .collect(),
391        }
392    }
393
394    pub fn metadata(&self) -> MetaData {
395        match self {
396            WaveContainer::Wellen(f) => f.metadata(),
397            WaveContainer::Empty => MetaData {
398                date: None,
399                version: None,
400                timescale: TimeScale {
401                    unit: TimeUnit::None,
402                    multiplier: None,
403                },
404            },
405            WaveContainer::Cxxrtl(_) => {
406                MetaData {
407                    date: None,
408                    version: None,
409                    timescale: TimeScale {
410                        // Cxxrtl always uses FemtoSeconds
411                        unit: TimeUnit::FemtoSeconds,
412                        multiplier: None,
413                    },
414                }
415            }
416        }
417    }
418
419    pub fn root_scopes(&self) -> Vec<ScopeRef> {
420        match self {
421            WaveContainer::Wellen(f) => f.root_scopes(),
422            WaveContainer::Empty => vec![],
423            WaveContainer::Cxxrtl(c) => c.lock().unwrap().root_modules(),
424        }
425    }
426
427    pub fn child_scopes(&self, scope: &ScopeRef) -> Result<Vec<ScopeRef>> {
428        match self {
429            WaveContainer::Wellen(f) => f.child_scopes(scope),
430            WaveContainer::Empty => bail!("Getting child modules from empty wave container"),
431            WaveContainer::Cxxrtl(c) => Ok(c.lock().unwrap().child_scopes(scope)),
432        }
433    }
434
435    pub fn max_timestamp(&self) -> Option<BigUint> {
436        match self {
437            WaveContainer::Wellen(f) => f.max_timestamp(),
438            WaveContainer::Empty => None,
439            WaveContainer::Cxxrtl(c) => c
440                .lock()
441                .unwrap()
442                .max_displayed_timestamp()
443                .map(|t| t.as_femtoseconds()),
444        }
445    }
446
447    pub fn scope_exists(&self, scope: &ScopeRef) -> bool {
448        match self {
449            WaveContainer::Wellen(f) => f.scope_exists(scope),
450            WaveContainer::Empty => false,
451            WaveContainer::Cxxrtl(c) => c.lock().unwrap().module_exists(scope),
452        }
453    }
454
455    /// Returns a human readable string with information about a scope.
456    /// The scope name itself should not be included, since it will be prepended automatically.
457    pub fn get_scope_tooltip_data(&self, scope: &ScopeRef) -> String {
458        match self {
459            WaveContainer::Wellen(f) => f.get_scope_tooltip_data(scope),
460            WaveContainer::Empty => String::new(),
461            // FIXME: Tooltip
462            WaveContainer::Cxxrtl(_) => String::new(),
463        }
464    }
465
466    /// Returns the simulation status for this wave source if it exists. Wave sources which have no
467    /// simulation status should return None here, otherwise buttons for controlling simulation
468    /// will be shown
469    pub fn simulation_status(&self) -> Option<SimulationStatus> {
470        match self {
471            WaveContainer::Wellen(_) => None,
472            WaveContainer::Empty => None,
473            WaveContainer::Cxxrtl(c) => c.lock().unwrap().simulation_status(),
474        }
475    }
476
477    /// If [`WaveContainer::simulation_status`] is `Some(SimulationStatus::Paused)`, attempt to unpause the
478    /// simulation otherwise does nothing
479    pub fn unpause_simulation(&self) {
480        match self {
481            WaveContainer::Wellen(_) => {}
482            WaveContainer::Empty => {}
483            WaveContainer::Cxxrtl(c) => c.lock().unwrap().unpause(),
484        }
485    }
486
487    /// See [`WaveContainer::unpause_simulation`]
488    pub fn pause_simulation(&self) {
489        match self {
490            WaveContainer::Wellen(_) => {}
491            WaveContainer::Empty => {}
492            WaveContainer::Cxxrtl(c) => c.lock().unwrap().pause(),
493        }
494    }
495
496    /// Called for `wellen` container, when the body of the waveform file has been parsed.
497    pub fn wellen_add_body(&mut self, body: BodyResult) -> Result<Option<LoadSignalsCmd>> {
498        match self {
499            WaveContainer::Wellen(inner) => inner.add_body(body),
500            _ => {
501                bail!("Should never call this function on a non wellen container!")
502            }
503        }
504    }
505
506    pub fn body_loaded(&self) -> bool {
507        match self {
508            WaveContainer::Wellen(inner) => inner.body_loaded(),
509            WaveContainer::Empty => true,
510            WaveContainer::Cxxrtl(_) => true,
511        }
512    }
513}