_origen/timesets/
timeset.rs

1use super::super::pins::pins_to_backend_lookup_fields;
2use super::timeset_container::{
3    EventContainer, WaveContainer, WaveGroupContainer, WavetableContainer,
4};
5use origen::Error;
6use origen::DUT;
7use pyo3::prelude::*;
8use pyo3::types::{PyAny, PyDict, PyTuple};
9
10#[macro_export]
11macro_rules! pytimeset {
12    ($py:expr, $model:expr, $model_id:expr, $name:expr) => {
13        if $model.contains_timeset($name) {
14            Ok(Py::new(
15                $py,
16                crate::timesets::timeset::Timeset {
17                    name: String::from($name),
18                    model_id: $model_id,
19                },
20            )
21            .unwrap()
22            .to_object($py))
23        } else {
24            // Note: Errors here shouldn't happen. Any errors that arise are either
25            // bugs or from the user meta-programming their way into the backend DB.
26            Err(PyErr::from(error!(
27                "No timeset {} has been added on block {}",
28                $name, $model.name
29            )))
30        }
31    };
32}
33
34// Returns a (Python) Timeset or NoneType instance.
35// Note: this does NOT return a Rust Option::None, but
36#[macro_export]
37macro_rules! pytimeset_or_pynone {
38    ($py:expr, $model:expr, $model_id:expr, $name:expr) => {
39        if $model.contains_timeset($name) {
40            Py::new(
41                $py,
42                crate::timesets::timeset::Timeset {
43                    name: String::from($name),
44                    model_id: $model_id,
45                },
46            )
47            .unwrap()
48            .to_object($py)
49        } else {
50            $py.None()
51        }
52    };
53}
54
55#[macro_export]
56macro_rules! pywavetable {
57    ($py:expr, $timeset:expr, $t_id:expr, $name:expr) => {
58        if $timeset.contains_wavetable($name) {
59            Ok(Py::new(
60                $py,
61                crate::timesets::timeset::Wavetable {
62                    name: String::from($name),
63                    model_id: $timeset.model_id,
64                    timeset_id: $timeset.id,
65                },
66            )
67            .unwrap()
68            .to_object($py))
69        } else {
70            // Note: Errors here shouldn't happen. Any errors that arise are either
71            // bugs or from the user meta-programming their way into the backend DB.
72            Err(PyErr::from(error!(
73                "No wavetable {} has been added on block {}",
74                $name, $timeset.name
75            )))
76        }
77    };
78}
79
80#[macro_export]
81macro_rules! pywave_group {
82    ($py:expr, $wavetable:expr, $name:expr) => {
83        if $wavetable.contains_wave_group($name) {
84            Ok(Py::new(
85                $py,
86                crate::timesets::timeset::WaveGroup {
87                    name: String::from($name),
88                    model_id: $wavetable.model_id,
89                    timeset_id: $wavetable.timeset_id,
90                    wavetable_id: $wavetable.id,
91                },
92            )
93            .unwrap()
94            .to_object($py))
95        } else {
96            // Note: Errors here shouldn't happen. Any errors that arise are either
97            // bugs or from the user meta-programming their way into the backend DB.
98            Err(PyErr::from(error!(
99                "No wave group {} has been added on block {}",
100                $name, $wavetable.name
101            )))
102        }
103    };
104}
105
106#[macro_export]
107macro_rules! pywave {
108    ($py:expr, $wave_group:expr, $name:expr) => {
109        if $wave_group.contains_wave($name) {
110            Ok(Py::new(
111                $py,
112                crate::timesets::timeset::Wave {
113                    name: String::from($name),
114                    model_id: $wave_group.model_id,
115                    timeset_id: $wave_group.timeset_id,
116                    wavetable_id: $wave_group.wavetable_id,
117                    wave_group_id: $wave_group.id,
118                },
119            )
120            .unwrap()
121            .to_object($py))
122        } else {
123            // Note: Errors here shouldn't happen. Any errors that arise are either
124            // bugs or from the user meta-programming their way into the backend DB.
125            Err(PyErr::from(error!(
126                "No wave {} has been added on block {}",
127                $name, $wave_group.name
128            )))
129        }
130    };
131}
132
133#[macro_export]
134macro_rules! pyevent {
135    ($py:expr, $wave:expr, $event_index:expr) => {
136        if $wave.events.len() > $event_index {
137            Ok(Py::new(
138                $py,
139                crate::timesets::timeset::Event {
140                    model_id: $wave.model_id,
141                    timeset_id: $wave.timeset_id,
142                    wavetable_id: $wave.wavetable_id,
143                    wave_group_id: $wave.wave_group_id,
144                    wave_id: $wave.wave_id,
145                    wave_indicator: $wave.indicator.clone(),
146                    index: $event_index,
147                },
148            )
149            .unwrap()
150            .to_object($py))
151        } else {
152            // Note: Errors here shouldn't happen. Any errors that arise are either
153            // bugs or from the user meta-programming their way into the backend DB.
154            Err(PyErr::from(error!(
155                "No event at {} has been added on wave {}",
156                $event_index, $wave.indicator
157            )))
158        }
159    };
160}
161
162#[pyclass]
163pub struct Timeset {
164    pub name: String,
165    pub model_id: usize,
166}
167
168#[pymethods]
169impl Timeset {
170    #[getter]
171    fn get_name(&self) -> PyResult<String> {
172        let dut = DUT.lock().unwrap();
173        let timeset = dut.get_timeset(self.model_id, &self.name);
174        Ok(timeset.unwrap().name.clone())
175    }
176
177    #[allow(non_snake_case)]
178    #[getter]
179    fn get___origen__model_id__(&self) -> PyResult<usize> {
180        Ok(self.model_id)
181    }
182
183    #[getter]
184    fn get_period(&self) -> PyResult<f64> {
185        let dut = DUT.lock().unwrap();
186        let timeset = dut._get_timeset(self.model_id, &self.name)?;
187        Ok(timeset.eval(Option::None)?)
188    }
189
190    #[getter]
191    fn get_default_period(&self, py: Python) -> PyResult<PyObject> {
192        let dut = DUT.lock().unwrap();
193        let timeset = dut._get_timeset(self.model_id, &self.name)?;
194
195        Ok(match timeset.default_period {
196            Some(p) => p.to_object(py),
197            None => py.None(),
198        })
199    }
200
201    #[allow(non_snake_case)]
202    #[getter]
203    fn get___eval_str__(&self) -> PyResult<String> {
204        let dut = DUT.lock().unwrap();
205        let timeset = dut._get_timeset(self.model_id, &self.name)?;
206        Ok(timeset.eval_str().clone())
207    }
208
209    #[allow(non_snake_case)]
210    #[getter]
211    fn get___period__(&self, py: Python) -> PyResult<PyObject> {
212        let dut = DUT.lock().unwrap();
213        let timeset = dut._get_timeset(self.model_id, &self.name)?;
214        Ok(match &timeset.period_as_string {
215            Some(p) => p.clone().to_object(py),
216            None => py.None(),
217        })
218    }
219
220    #[getter]
221    fn wavetables(&self, py: Python) -> PyResult<Py<WavetableContainer>> {
222        let t_id;
223        {
224            t_id = self.get_origen_id()?;
225        }
226        Ok(pywavetable_container!(py, self.model_id, t_id, &self.name))
227    }
228
229    #[pyo3(signature=(name, **_kwargs))]
230    fn add_wavetable(
231        &self,
232        py: Python,
233        name: &str,
234        _kwargs: Option<&PyDict>,
235    ) -> PyResult<PyObject> {
236        let mut dut = DUT.lock().unwrap();
237        let t_id;
238        {
239            t_id = dut._get_timeset(self.model_id, &self.name).unwrap().id;
240        }
241        dut.create_wavetable(t_id, name)?;
242
243        let tset = dut._get_timeset(self.model_id, &self.name).unwrap();
244        Ok(pywavetable!(py, tset, t_id, name)?)
245    }
246
247    #[getter]
248    fn symbol_map(&self, py: Python) -> PyResult<PyObject> {
249        let dut = DUT.lock().unwrap();
250        let t_id;
251        {
252            t_id = dut._get_timeset(self.model_id, &self.name).unwrap().id;
253        }
254        let tester = origen::tester();
255
256        Ok(Py::new(
257            py,
258            crate::timesets::timeset::SymbolMap {
259                timeset_id: t_id,
260                target_name: {
261                    match tester.focused_tester_name() {
262                        Some(n) => n,
263                        None => return Ok(py.None()),
264                    }
265                },
266            },
267        )
268        .unwrap()
269        .to_object(py))
270    }
271
272    #[getter]
273    fn symbol_maps(&self, py: Python) -> PyResult<Vec<PyObject>> {
274        let dut = DUT.lock().unwrap();
275        let t = dut._get_timeset(self.model_id, &self.name).unwrap();
276        let t_id;
277        {
278            t_id = t.id;
279        }
280
281        let retn = t
282            .pin_action_resolvers
283            .keys()
284            .map({
285                |target| {
286                    Py::new(
287                        py,
288                        crate::timesets::timeset::SymbolMap {
289                            timeset_id: t_id,
290                            target_name: target.to_string(),
291                        },
292                    )
293                    .unwrap()
294                    .to_object(py)
295                }
296            })
297            .collect::<Vec<PyObject>>();
298        Ok(retn)
299    }
300}
301
302impl Timeset {
303    pub fn new(name: &str, model_id: usize) -> Self {
304        Self {
305            name: String::from(name),
306            model_id: model_id,
307        }
308    }
309
310    pub fn get_origen_id(&self) -> Result<usize, Error> {
311        let dut = DUT.lock().unwrap();
312        let timeset = dut._get_timeset(self.model_id, &self.name)?;
313        Ok(timeset.id)
314    }
315}
316
317#[pyclass]
318pub struct Wavetable {
319    pub timeset_id: usize,
320    pub name: String,
321    pub model_id: usize,
322}
323
324#[pymethods]
325impl Wavetable {
326    #[pyo3(signature=(name, **_kwargs))]
327    fn add_waves(&self, py: Python, name: &str, _kwargs: Option<&PyDict>) -> PyResult<PyObject> {
328        let mut dut = DUT.lock().unwrap();
329        let w_id;
330        {
331            w_id = dut.get_wavetable(self.timeset_id, &self.name).unwrap().id;
332        }
333        dut.create_wave_group(w_id, name, Option::None)?;
334
335        let wt = dut.get_wavetable(self.timeset_id, &self.name).unwrap();
336        Ok(pywave_group!(py, wt, name)?)
337    }
338
339    #[pyo3(signature=(name, **_kwargs))]
340    fn add_wave(&self, py: Python, name: &str, _kwargs: Option<&PyDict>) -> PyResult<PyObject> {
341        self.add_waves(py, name, _kwargs)
342    }
343
344    #[getter]
345    fn get_waves(&self, py: Python) -> PyResult<Py<WaveGroupContainer>> {
346        let w_id;
347        {
348            w_id = self.get_origen_id()?;
349        }
350        Ok(pywave_group_container!(
351            py,
352            self.model_id,
353            self.timeset_id,
354            w_id,
355            &self.name
356        ))
357    }
358
359    /// Retrieves all applied waves as a dictionary whose keys are the physical pins which has a corresponding
360    /// wave. The values are another dictionary whose key-value pair is the indicator finally pointing to the wave
361    /// which defines the wave.
362    ///
363    /// .. code: python
364    ///     {
365    ///         porta1: {
366    ///             "0": <Wave>,
367    ///             "1": <Wave>,
368    ///             "h": <Wave>,
369    ///             "l": <Wave>,
370    ///         },
371    ///         porta0: {
372    ///             "0": <Wave>,
373    ///             "1": <Wave>,
374    ///             "h": <Wave>,
375    ///             "l": <Wave>,
376    ///         },
377    ///         clk: {
378    ///             "0": <Wave>,
379    ///             "1": <Wave>,
380    ///         }
381    ///     }
382    ///
383    fn applied_waves(&self, py: Python) -> PyResult<PyObject> {
384        let empty: [PyObject; 0] = [];
385        let t = PyTuple::new(py, &empty);
386        self.applied_waves_for(py, t, None)
387    }
388
389    /// Same as :meth:`applied_waves` but supports internal filtering of the return values.
390    #[pyo3(signature=(*pins, indicators))]
391    fn applied_waves_for(
392        &self,
393        py: Python,
394        pins: &PyTuple,
395        indicators: Option<Vec<String>>,
396    ) -> PyResult<PyObject> {
397        let dut = DUT.lock().unwrap();
398        let wt = dut._get_wavetable(self.timeset_id, &self.name)?;
399        let waves = wt.applied_waves(
400            &dut,
401            &pins_to_backend_lookup_fields(py, &pins)?,
402            &indicators.unwrap_or(vec![]),
403        )?;
404        Ok(waves.to_object(py))
405    }
406
407    #[getter]
408    fn get_symbol_map(&self, py: Python) -> PyResult<PyObject> {
409        let tester = origen::tester();
410        match tester.focused_tester_name() {
411            Some(name) => Ok(Py::new(py, SymbolMap::new(self.timeset_id, name))
412                .unwrap()
413                .to_object(py)),
414            None => Ok(py.None()),
415        }
416    }
417
418    #[getter]
419    fn get_name(&self) -> PyResult<String> {
420        let dut = DUT.lock().unwrap();
421        let wt = dut.get_wavetable(self.timeset_id, &self.name);
422        Ok(wt.unwrap().name.clone())
423    }
424
425    // Evaluates and returns the period.
426    // Returns None if no period was specified or an error if it could not be evaluated.
427    #[getter]
428    pub fn get_period(&self, py: Python) -> PyResult<PyObject> {
429        let dut = DUT.lock().unwrap();
430        let wt = dut.get_wavetable(self.timeset_id, &self.name);
431        let p = wt.unwrap().eval(Option::None)?;
432
433        match p {
434            Some(_p) => Ok(_p.to_object(py)),
435            None => Ok(py.None()),
436        }
437    }
438
439    // From the Python side, want to support receiving input as either an expression (String)
440    // or as hard coded integer/float values..
441    #[setter]
442    pub fn set_period(&self, period: &PyAny) -> PyResult<()> {
443        let mut dut = DUT.lock().unwrap();
444        let wt = dut.get_mut_wavetable(self.timeset_id, &self.name).unwrap();
445        if let Ok(p) = period.extract::<String>() {
446            wt.set_period(Some(Box::new(p)))?;
447        } else if let Ok(p) = period.extract::<f64>() {
448            wt.set_period(Some(Box::new(p)))?;
449        } else if period.get_type().qualname()? == "NoneType" {
450            wt.set_period(Option::None)?;
451        } else {
452            return super::super::type_error!(format!("Could not interpret 'period' argument as Numeric, String, or NoneType! (class '{}')", period.get_type().qualname()?));
453        };
454        Ok(())
455    }
456
457    // Returns the period as a string before evaluation.
458    #[allow(non_snake_case)]
459    #[getter]
460    pub fn get___period__(&self, py: Python) -> PyResult<PyObject> {
461        let dut = DUT.lock().unwrap();
462        let wt = dut.get_wavetable(self.timeset_id, &self.name);
463        let p = &wt.unwrap().period;
464
465        match p {
466            Some(_p) => Ok(_p.to_object(py)),
467            None => Ok(py.None()),
468        }
469    }
470}
471
472impl Wavetable {
473    pub fn new(model_id: usize, timeset_id: usize, name: &str) -> Self {
474        Self {
475            timeset_id: timeset_id,
476            name: String::from(name),
477            model_id: model_id,
478        }
479    }
480
481    pub fn get_origen_id(&self) -> Result<usize, Error> {
482        let dut = DUT.lock().unwrap();
483        let timeset = &dut.timesets[self.timeset_id];
484        let w_id = timeset.get_wavetable_id(&self.name).unwrap();
485        Ok(w_id)
486    }
487}
488
489#[pyclass]
490pub struct WaveGroup {
491    pub model_id: usize,
492    pub timeset_id: usize,
493    pub wavetable_id: usize,
494    pub name: String,
495}
496
497#[pymethods]
498impl WaveGroup {
499    #[pyo3(signature=(name, **_kwargs))]
500    fn add_wave(&self, py: Python, name: &str, _kwargs: Option<&PyDict>) -> PyResult<PyObject> {
501        let mut dut = DUT.lock().unwrap();
502        let wgrp_id;
503        {
504            wgrp_id = dut
505                .get_wave_group(self.wavetable_id, &self.name)
506                .unwrap()
507                .id;
508        }
509        let mut derived_from = Option::None;
510        if let Some(args) = _kwargs {
511            if let Some(_derived_from) = args.get_item("derived_from")? {
512                if let Ok(_waves) = _derived_from.extract::<String>() {
513                    derived_from = Some(vec![_waves]);
514                } else if let Ok(_waves) = _derived_from.extract::<Vec<String>>() {
515                    derived_from = Some(_waves);
516                } else {
517                    return type_error!("Could not interpret 'derived_From' argument as a string or as a list of strings!");
518                }
519            }
520        }
521        dut.create_wave(wgrp_id, name, derived_from)?;
522
523        let wgrp = dut.get_wave_group(self.wavetable_id, &self.name).unwrap();
524        Ok(pywave!(py, wgrp, name)?)
525    }
526
527    #[getter]
528    fn get_waves(&self, py: Python) -> PyResult<Py<WaveContainer>> {
529        let wgrp_id;
530        {
531            wgrp_id = self.get_origen_id()?;
532        }
533        Ok(pywave_container!(
534            py,
535            self.model_id,
536            self.timeset_id,
537            self.wavetable_id,
538            wgrp_id,
539            &self.name
540        ))
541    }
542
543    #[getter]
544    fn get_name(&self) -> PyResult<String> {
545        let dut = DUT.lock().unwrap();
546        let wt = dut.get_wavetable(self.timeset_id, &self.name);
547        Ok(wt.unwrap().name.clone())
548    }
549}
550
551impl WaveGroup {
552    pub fn new(model_id: usize, timeset_id: usize, wavetable_id: usize, name: &str) -> Self {
553        Self {
554            model_id: model_id,
555            timeset_id: timeset_id,
556            wavetable_id: wavetable_id,
557            name: String::from(name),
558        }
559    }
560
561    pub fn get_origen_id(&self) -> Result<usize, Error> {
562        let dut = DUT.lock().unwrap();
563        let wavetable = &dut.wavetables[self.wavetable_id];
564        let wgrp_id = wavetable.get_wave_group_id(&self.name).unwrap();
565        Ok(wgrp_id)
566    }
567}
568
569#[pyclass]
570pub struct Wave {
571    pub model_id: usize,
572    pub timeset_id: usize,
573    pub wavetable_id: usize,
574    pub wave_group_id: usize,
575    pub name: String,
576}
577
578#[pymethods]
579impl Wave {
580    #[getter]
581    fn get_events(&self, py: Python) -> PyResult<Py<EventContainer>> {
582        let wave_id;
583        {
584            wave_id = self.get_origen_id()?;
585        }
586        Ok(pyevent_container!(
587            py,
588            self.model_id,
589            self.timeset_id,
590            self.wavetable_id,
591            self.wave_group_id,
592            wave_id,
593            &self.name
594        ))
595    }
596
597    #[pyo3(signature=(**event))]
598    fn push_event(&self, py: Python, event: Option<&PyDict>) -> PyResult<PyObject> {
599        let mut dut = DUT.lock().unwrap();
600        let (w_id, e_index);
601        {
602            w_id = dut
603                .get_wave(self.wave_group_id, &self.name)
604                .unwrap()
605                .wave_id;
606        }
607
608        if event.is_none() {
609            return type_error!("Keywords 'at' and 'action' are required to push a new event!");
610        }
611
612        let (at, unit, action) = (
613            event.unwrap().get_item("at")?,
614            event.unwrap().get_item("unit")?,
615            event.unwrap().get_item("action")?,
616        );
617        {
618            // Resolve the 'action' keyword first because rust is a pain in the butt.
619            // This is required and can only be a String.
620            let temp: String;
621            match action {
622                Some(_action) => {
623                    if let Ok(val) = _action.extract::<String>() {
624                        temp = val;
625                    } else if _action.is_none() {
626                        return type_error!("'action' keyword is required (found None)!");
627                    } else {
628                        return type_error!("Could not interpret 'action' argument as String!");
629                    }
630                }
631                None => return type_error!("'action' keyword is required!"),
632            }
633            let e = dut.create_event(
634                w_id,
635                // Resolve the 'at' keyword. This is required and can be either a String or a numeric.
636                match at {
637                    Some(_at) => {
638                        if let Ok(val) = _at.extract::<String>() {
639                            Box::new(val)
640                        } else if let Ok(val) = _at.extract::<f64>() {
641                            Box::new(val)
642                        } else if _at.is_none() {
643                            return type_error!("'at' keyword is required (found None)!");
644                        } else {
645                            return type_error!(
646                                "Could not interpret 'at' argument as String or Numeric!"
647                            );
648                        }
649                    }
650                    None => return type_error!("'at' keyword is required!"),
651                },
652                // Resolve the 'unit' keyword. This is optional and can only be a string.
653                match unit {
654                    Some(_unit) => {
655                        if let Ok(val) = _unit.extract::<String>() {
656                            Some(val)
657                        } else if _unit.is_none() {
658                            Option::None
659                        } else {
660                            return type_error!(
661                                "Could not interpret 'unit' argument as String or NoneType!"
662                            );
663                        }
664                    }
665                    None => Option::None,
666                },
667                &temp,
668            )?;
669            e_index = e.event_index;
670        }
671
672        // Return the newly created event
673        let w = dut.get_wave(self.wave_group_id, &self.name).unwrap();
674        Ok(pyevent!(py, w, e_index)?)
675    }
676
677    #[getter]
678    fn get_indicator(&self) -> PyResult<String> {
679        let dut = DUT.lock().unwrap();
680        let w = dut.get_wave(self.wave_group_id, &self.name).unwrap();
681        Ok(w.indicator.clone())
682    }
683
684    #[setter]
685    fn set_indicator(&self, indicator: &str) -> PyResult<()> {
686        let mut dut = DUT.lock().unwrap();
687        let w = dut.get_mut_wave(self.wave_group_id, &self.name).unwrap();
688        w.set_indicator(&indicator)?;
689        Ok(())
690    }
691
692    #[getter]
693    fn get_applied_to(&self, py: Python) -> PyResult<Vec<PyObject>> {
694        let dut = DUT.lock().unwrap();
695        let w = dut.get_wave(self.wave_group_id, &self.name).unwrap();
696
697        let mut pins: Vec<PyObject> = vec![];
698        for p in w.applied_pin_ids.iter() {
699            let ppin = &dut.pins[*p];
700            pins.push(
701                super::super::pins::pin::Pin {
702                    name: ppin.name.clone(),
703                    model_id: ppin.model_id,
704                }
705                .into_py(py),
706            );
707        }
708        Ok(pins)
709    }
710
711    #[pyo3(signature=(*pins))]
712    fn apply_to(&self, py: Python, pins: Vec<String>) -> PyResult<PyObject> {
713        let mut dut = DUT.lock().unwrap();
714        let wid;
715        {
716            wid = dut
717                .get_wave(self.wave_group_id, &self.name)
718                .unwrap()
719                .wave_id;
720        }
721        let pins_with_model_id: Vec<(usize, String)> =
722            pins.iter().map(|pin| (0, pin.clone())).collect();
723        dut.apply_wave_id_to_pins(wid, &pins_with_model_id)?;
724
725        Ok(Py::new(
726            py,
727            crate::timesets::timeset::Wave {
728                name: self.name.clone(),
729                model_id: self.model_id,
730                timeset_id: self.timeset_id,
731                wavetable_id: self.wavetable_id,
732                wave_group_id: self.wave_group_id,
733            },
734        )
735        .unwrap()
736        .to_object(py))
737    }
738
739    #[allow(non_snake_case)]
740    #[getter]
741    pub fn get_name(&self) -> PyResult<String> {
742        Ok(self.name.clone())
743    }
744
745    #[allow(non_snake_case)]
746    #[getter]
747    pub fn get_DriveHigh(&self) -> PyResult<String> {
748        Ok(String::from("DriveHigh"))
749    }
750
751    #[allow(non_snake_case)]
752    #[getter]
753    pub fn get_DriveLow(&self) -> PyResult<String> {
754        Ok(String::from("DriveLow"))
755    }
756
757    #[allow(non_snake_case)]
758    #[getter]
759    pub fn get_HighZ(&self) -> PyResult<String> {
760        Ok(String::from("HighZ"))
761    }
762
763    #[allow(non_snake_case)]
764    #[getter]
765    pub fn get_VerifyHigh(&self) -> PyResult<String> {
766        Ok(String::from("VerifyHigh"))
767    }
768
769    #[allow(non_snake_case)]
770    #[getter]
771    pub fn get_VerifyLow(&self) -> PyResult<String> {
772        Ok(String::from("VerifyLow"))
773    }
774
775    #[allow(non_snake_case)]
776    #[getter]
777    pub fn get_VerifyZ(&self) -> PyResult<String> {
778        Ok(String::from("VerifyZ"))
779    }
780
781    #[allow(non_snake_case)]
782    #[getter]
783    pub fn get_Capture(&self) -> PyResult<String> {
784        Ok(String::from("Capture"))
785    }
786}
787
788impl Wave {
789    pub fn new(
790        model_id: usize,
791        timeset_id: usize,
792        wavetable_id: usize,
793        wave_group_id: usize,
794        name: &str,
795    ) -> Self {
796        Self {
797            model_id: model_id,
798            timeset_id: timeset_id,
799            wavetable_id: wavetable_id,
800            wave_group_id: wave_group_id,
801            name: String::from(name),
802        }
803    }
804
805    pub fn get_origen_id(&self) -> Result<usize, Error> {
806        let dut = DUT.lock().unwrap();
807        let wgrp = &dut.wave_groups[self.wave_group_id];
808        let w_id = wgrp.get_wave_id(&self.name).unwrap();
809        Ok(w_id)
810    }
811}
812
813#[pyclass]
814pub struct Event {
815    pub model_id: usize,
816    pub timeset_id: usize,
817    pub wavetable_id: usize,
818    pub wave_group_id: usize,
819    pub wave_id: usize,
820    pub wave_indicator: String,
821    pub index: usize,
822}
823
824#[pymethods]
825impl Event {
826    #[getter]
827    pub fn get_action(&self) -> PyResult<String> {
828        let dut = DUT.lock().unwrap();
829        let e = dut.get_event(self.wave_id, self.index).unwrap();
830        Ok(e.action.clone())
831    }
832
833    #[setter]
834    pub fn action(&self, action: &str) -> PyResult<()> {
835        let mut dut = DUT.lock().unwrap();
836        let e = dut.get_mut_event(self.wave_id, self.index).unwrap();
837        e.set_action(action)?;
838        Ok(())
839    }
840
841    #[getter]
842    pub fn unit(&self, py: Python) -> PyResult<PyObject> {
843        let dut = DUT.lock().unwrap();
844        let e = dut.get_event(self.wave_id, self.index).unwrap();
845
846        match &e.unit {
847            Some(unit) => Ok(unit.clone().to_object(py)),
848            None => Ok(py.None()),
849        }
850    }
851
852    #[getter]
853    pub fn at(&self, py: Python) -> PyResult<PyObject> {
854        let dut = DUT.lock().unwrap();
855        let e = dut.get_event(self.wave_id, self.index).unwrap();
856        Ok(e.eval(&dut, Option::None)?.to_object(py))
857    }
858
859    #[getter]
860    pub fn __at__(&self) -> PyResult<String> {
861        let dut = DUT.lock().unwrap();
862        let e = dut.get_event(self.wave_id, self.index).unwrap();
863        Ok(e.at.clone())
864    }
865}
866
867impl Event {
868    pub fn new(
869        model_id: usize,
870        timeset_id: usize,
871        wavetable_id: usize,
872        wave_group_id: usize,
873        wave_id: usize,
874        wave_indicator: &str,
875        id: usize,
876    ) -> Self {
877        Self {
878            model_id: model_id,
879            timeset_id: timeset_id,
880            wavetable_id: wavetable_id,
881            wave_group_id: wave_group_id,
882            wave_id: wave_id,
883            wave_indicator: String::from(wave_indicator),
884            index: id,
885        }
886    }
887}
888
889fn action_from_pyany(action: &PyAny) -> PyResult<origen::core::model::pins::pin::PinAction> {
890    Ok(
891        origen::core::model::pins::pin::PinAction::from_delimiter_optional(
892            {
893                let t;
894                if let Ok(a) = action.extract::<String>() {
895                    t = a.clone();
896                } else if action.get_type().qualname()? == "PinActions" {
897                    let pin_actions = action
898                        .extract::<PyRef<super::super::pins::pin_actions::PinActions>>()
899                        .unwrap();
900                    if pin_actions.actions.len() == 1 {
901                        t = pin_actions.actions.first().unwrap().to_string();
902                    } else {
903                        return Err(pyo3::exceptions::PyValueError::new_err(
904                            "SymbolMap lookups can only retrieve single symbols at a time",
905                        ));
906                    }
907                } else {
908                    return super::super::type_error!(&format!(
909                        "Cannot cast type {} to a valid PinAction",
910                        action.get_type().qualname()?
911                    ));
912                }
913                t
914            }
915            .as_str(),
916        )?,
917    )
918}
919
920#[pyclass]
921pub struct SymbolMap {
922    timeset_id: usize,
923    target_name: String,
924}
925
926impl SymbolMap {
927    pub fn new(timeset_id: usize, target_name: String) -> Self {
928        Self {
929            timeset_id: timeset_id,
930            target_name: target_name,
931        }
932    }
933}
934
935#[pymethods]
936impl SymbolMap {
937    fn keys(&self) -> PyResult<Vec<String>> {
938        let dut = DUT.lock().unwrap();
939        let resolver = &dut.timesets[self.timeset_id].pin_action_resolvers[&self.target_name];
940        Ok(resolver
941            .mapping()
942            .iter()
943            .map(|(k, _)| k.to_string())
944            .collect())
945    }
946
947    fn values(&self) -> PyResult<Vec<String>> {
948        let dut = DUT.lock().unwrap();
949        let resolver = &dut.timesets[self.timeset_id].pin_action_resolvers[&self.target_name];
950        Ok(resolver
951            .mapping()
952            .iter()
953            .map(|(_, v)| v.to_string())
954            .collect::<Vec<String>>())
955    }
956
957    fn items(&self) -> PyResult<Vec<(String, String)>> {
958        let dut = DUT.lock().unwrap();
959        let resolver = &dut.timesets[self.timeset_id].pin_action_resolvers[&self.target_name];
960
961        Ok(resolver
962            .mapping()
963            .iter()
964            .map(|(k, v)| (k.to_string(), v.to_string()))
965            .collect::<Vec<(String, String)>>())
966    }
967
968    fn get(&self, py: Python, action: &PyAny) -> PyResult<PyObject> {
969        match self.__getitem__(action) {
970            Ok(a) => Ok(a.into_py(py)),
971            Err(_) => Ok(py.None()),
972        }
973    }
974
975    fn set_symbol(
976        &mut self,
977        action: &PyAny,
978        new_resolution: String,
979        target: Option<String>,
980    ) -> PyResult<()> {
981        if let Some(t) = target {
982            let mut dut = DUT.lock().unwrap();
983            let tset = &mut dut.timesets[self.timeset_id];
984            if let Some(resolver) = tset.pin_action_resolvers.get_mut(&t) {
985                resolver.update_mapping(action_from_pyany(action)?, new_resolution.clone());
986                Ok(())
987            } else {
988                Err(pyo3::exceptions::PyKeyError::new_err(format!(
989                    "Timeset '{}' does not have a symbol map targeting '{}' (The target must be set prior to timeset creation)",
990                    tset.name,
991                    t
992                )))
993            }
994        } else {
995            self.__setitem__(action, new_resolution)
996        }
997    }
998
999    fn for_target(&self, py: Python, target: String) -> PyResult<PyObject> {
1000        let dut = DUT.lock().unwrap();
1001        {
1002            let t = &dut.timesets[self.timeset_id];
1003            if !t.pin_action_resolvers.contains_key(&target) {
1004                return Err(pyo3::exceptions::PyKeyError::new_err(format!(
1005                    "Timeset '{}' does not have a symbol map targeting '{}' (The target must be set prior to timeset creation)",
1006                    t.name,
1007                    target
1008                )));
1009            }
1010        }
1011
1012        Ok(Py::new(
1013            py,
1014            crate::timesets::timeset::SymbolMap {
1015                timeset_id: self.timeset_id,
1016                target_name: target,
1017            },
1018        )
1019        .unwrap()
1020        .to_object(py))
1021    }
1022
1023    fn __getitem__(&self, action: &PyAny) -> PyResult<String> {
1024        let dut = DUT.lock().unwrap();
1025        let resolver = &dut.timesets[self.timeset_id].pin_action_resolvers[&self.target_name];
1026
1027        if let Some(r) = resolver.resolve(&action_from_pyany(action)?) {
1028            Ok(r)
1029        } else {
1030            Err(pyo3::exceptions::PyKeyError::new_err(format!(
1031                "No symbol found for {}",
1032                action
1033            )))
1034        }
1035    }
1036
1037    fn __setitem__(&mut self, action: &PyAny, new_resolution: String) -> PyResult<()> {
1038        let mut dut = DUT.lock().unwrap();
1039        let tester = origen::tester();
1040        for target in tester.targets_as_strs().iter() {
1041            // let resolver = &mut dut.timesets[self.timeset_id].pin_action_resolvers[&self.target_name];
1042            let resolver = &mut dut.timesets[self.timeset_id].pin_action_resolvers[target];
1043            resolver.update_mapping(action_from_pyany(action)?, new_resolution.clone())
1044        }
1045        Ok(())
1046    }
1047
1048    fn __len__(&self) -> PyResult<usize> {
1049        let dut = DUT.lock().unwrap();
1050        let resolver = &dut.timesets[self.timeset_id].pin_action_resolvers[&self.target_name];
1051        Ok(resolver.mapping().len())
1052    }
1053
1054    fn __contains__(&self, item: &PyAny) -> PyResult<bool> {
1055        match self.__getitem__(&item) {
1056            Ok(_) => Ok(true),
1057            Err(_) => Ok(false),
1058        }
1059    }
1060
1061    fn __iter__(slf: PyRefMut<Self>) -> PyResult<SymbolMapIter> {
1062        Ok(SymbolMapIter {
1063            keys: slf.keys().unwrap(),
1064            i: 0,
1065        })
1066    }
1067}
1068
1069#[pyclass]
1070pub struct SymbolMapIter {
1071    pub keys: Vec<String>,
1072    pub i: usize,
1073}
1074
1075#[pymethods]
1076impl SymbolMapIter {
1077    fn __iter__(slf: PyRefMut<Self>) -> PyResult<Py<Self>> {
1078        Ok(slf.into())
1079    }
1080
1081    fn __next__(mut slf: PyRefMut<Self>) -> PyResult<Option<String>> {
1082        if slf.i >= slf.keys.len() {
1083            return Ok(None);
1084        }
1085        let name = slf.keys[slf.i].clone();
1086        slf.i += 1;
1087        Ok(Some(name))
1088    }
1089}