ptnet_core/
trace.rs

1/*!
2This module provides a trait that can be implemented to trace the execution of a net.
3
4The tracer is intended to allow for a client to determine the intermediate activities within the
5simulation between steps. This is useful for statistics, timing, and animation of nets as they are
6executed.
7
8*/
9
10use crate::net::{Arc, Net, Place, Transition};
11use crate::sim::{Marking, Simulation, Step, Tokens};
12use crate::NodeId;
13use std::marker::PhantomData;
14use std::rc::Rc;
15
16// ------------------------------------------------------------------------------------------------
17// Public Types
18// ------------------------------------------------------------------------------------------------
19
20///
21/// This trait is implemented to listen to events generated by the simulation as it executes.
22///
23/// 1. simulation started
24/// 2. *for each step*:
25///    1. step started
26///    2. *for each firing transition*:
27///       1. *for each input place*:
28///          1. place updated
29///       1. transition started
30///       1. *on timed transition complete*
31///       1. *for each output place*:
32///          1. place updated
33///       1. transition ended
34///    1. step ended
35/// 1. simulation ended
36///
37#[allow(unused_variables)]
38pub trait SimulationTracer {
39    type Place: Place;
40    type Transition: Transition;
41    type Arc: Arc;
42    type Net: Net<Place = Self::Place, Transition = Self::Transition, Arc = Self::Arc>;
43    type Tokens: Tokens;
44    type Marking: Marking<Tokens = Self::Tokens>;
45    type Simulation: Simulation<
46        Place = Self::Place,
47        Transition = Self::Transition,
48        Arc = Self::Arc,
49        Tokens = Self::Tokens,
50        Marking = Self::Marking,
51    >;
52
53    ///
54    /// This event is generated when the simulation takes its first step, but before the
55    /// `step_started` event.
56    ///
57    fn started(&self, sim: &Self::Simulation) {}
58
59    ///
60    /// This event is generated when the simulation takes a step. It is generated before any
61    /// evaluation of the net marking.
62    ///
63    fn step_started(&self, step: Step, sim: &Self::Simulation) {}
64
65    ///
66    /// This is generated for any place that has had its tokens updated, either added or
67    /// subtracted.
68    ///
69    fn place_updated(&self, place: NodeId, sim: &Self::Simulation) {}
70
71    ///
72    /// This is generated when a transition is fired. At this point all input tokens have been
73    /// consumed from the preset but no output tokens have been transmitted.
74    ///
75    fn transition_started(&self, transition: NodeId, sim: &Self::Simulation) {}
76
77    ///
78    /// This is generated when a transition has been completed. At this point all output tokens
79    /// have been transmitted to the postset.
80    ///
81    fn transition_ended(&self, transition: NodeId, sim: &Self::Simulation) {}
82
83    ///
84    /// This event is generated when the simulation completes a step. It is generated after all
85    /// enabled transitions have fired.
86    ///
87    fn step_ended(&self, step: Step, sim: &Self::Simulation) {}
88
89    ///
90    /// This event is generated if, and when, the simulation determines that the net is
91    /// terminated.
92    ///
93    fn ended(&self, sim: &Self::Simulation) {}
94}
95
96///
97/// This trait provides for a simulation to add and remove a single tracer implementation.
98///
99/// Note that if you add a tracer after a simulation has started there is no expectation that
100/// missed events will be delivered.
101///
102pub trait TraceableSimulation: Simulation {
103    ///
104    /// Add a tracer to this simulation.
105    ///
106    fn add_tracer<T>(&mut self, tracer: Rc<T>)
107    where
108        T: SimulationTracer<
109                Place = Self::Place,
110                Transition = Self::Transition,
111                Arc = Self::Arc,
112                Net = Self::Net,
113                Tokens = Self::Tokens,
114                Marking = Self::Marking,
115                Simulation = Self,
116            > + 'static;
117
118    ///
119    /// Remove any tracer associated with this simulation. If no tracer is associated this method
120    /// does nothing.
121    ///
122    fn remove_tracer(&mut self);
123}
124
125///
126/// This type implements the tracer trait to output a matrix which contains the tokens value for
127/// each place, and a flag for enabled transitions, on completion of each step.
128///
129/// ## Example
130///
131/// Given the simple net \\(\circ\rightarrow\rule[-1pt]{3pt}{0.75em}\rightarrow\circ\\), the matrix
132/// is as follows:
133///
134/// |    # |   p0 |   p1 |   t0 |
135/// |------|------|------|------|
136/// |    0 |   ●  |      |   Y  |
137/// |    1 |      |   ●  |      |
138///
139#[derive(Debug)]
140pub struct MatrixTracer<P, T, A, N, C, M, S>
141where
142    P: Place,
143    T: Transition,
144    A: Arc,
145    N: Net<Place = P, Transition = T, Arc = A>,
146    C: Tokens,
147    M: Marking<Tokens = C>,
148    S: Simulation<Place = P, Transition = T, Arc = A, Tokens = C, Marking = M>,
149{
150    net: PhantomData<N>,
151    sim: PhantomData<S>,
152    show_empty: bool,
153}
154
155// ------------------------------------------------------------------------------------------------
156// Implementations
157// ------------------------------------------------------------------------------------------------
158
159const FORMAT_FIELD_WIDTH: usize = 6;
160const TRANSITION_ENABLED: &str = "Y";
161const TRANSITION_DISABLED: &str = "";
162
163impl<P, T, A, N, C, M, S> Default for MatrixTracer<P, T, A, N, C, M, S>
164where
165    P: Place,
166    T: Transition,
167    A: Arc,
168    N: Net<Place = P, Transition = T, Arc = A>,
169    C: Tokens,
170    M: Marking<Tokens = C>,
171    S: Simulation<Place = P, Transition = T, Arc = A, Tokens = C, Marking = M>,
172{
173    fn default() -> Self {
174        Self::new(false)
175    }
176}
177
178impl<P, T, A, N, C, M, S> SimulationTracer for MatrixTracer<P, T, A, N, C, M, S>
179where
180    P: Place,
181    T: Transition,
182    A: Arc,
183    N: Net<Place = P, Transition = T, Arc = A>,
184    C: Tokens,
185    M: Marking<Tokens = C>,
186    S: Simulation<Place = P, Transition = T, Arc = A, Tokens = C, Marking = M>,
187{
188    type Place = P;
189    type Transition = T;
190    type Arc = A;
191    type Net = N;
192    type Tokens = C;
193    type Marking = M;
194    type Simulation = S;
195
196    fn started(&self, sim: &Self::Simulation) {
197        let (places, transitions) = self.columns(&sim.net());
198
199        println!(
200            "| {:>FORMAT_FIELD_WIDTH$} | {} |",
201            "#",
202            places
203                .iter()
204                .map(|id| format!("{:^FORMAT_FIELD_WIDTH$}", id.as_ref()))
205                .chain(
206                    transitions
207                        .iter()
208                        .map(|id| format!("{:^FORMAT_FIELD_WIDTH$}", id.as_ref()))
209                )
210                .collect::<Vec<String>>()
211                .join(" | ")
212        );
213
214        let fields = places.len() + transitions.len();
215        let width = FORMAT_FIELD_WIDTH + 2; // for pad spaces.
216        println!(
217            "|{}|",
218            (0..=fields)
219                .map(|_| format!("{:-<width$}", ""))
220                .collect::<Vec<String>>()
221                .join("+")
222        );
223    }
224
225    fn step_ended(&self, step: Step, sim: &Self::Simulation) {
226        let (places, transitions) = self.columns(&sim.net());
227
228        let marking = sim.current_marking();
229        let enabled = sim.enabled();
230
231        println!(
232            "| {:>FORMAT_FIELD_WIDTH$} | {} |",
233            step.as_ref(),
234            places
235                .iter()
236                .map(|id| if self.show_empty {
237                    format!("{:#^FORMAT_FIELD_WIDTH$}", marking.marking(id).to_string())
238                } else {
239                    format!("{:^FORMAT_FIELD_WIDTH$}", marking.marking(id).to_string())
240                })
241                .chain(transitions.iter().map(|id| format!(
242                    "{:^FORMAT_FIELD_WIDTH$}",
243                    if enabled.contains(id) {
244                        TRANSITION_ENABLED
245                    } else {
246                        TRANSITION_DISABLED
247                    }
248                )))
249                .collect::<Vec<String>>()
250                .join(" | ")
251        );
252    }
253}
254
255impl<P, T, A, N, C, M, S> MatrixTracer<P, T, A, N, C, M, S>
256where
257    P: Place,
258    T: Transition,
259    A: Arc,
260    N: Net<Place = P, Transition = T, Arc = A>,
261    C: Tokens,
262    M: Marking<Tokens = C>,
263    S: Simulation<Place = P, Transition = T, Arc = A, Tokens = C, Marking = M>,
264{
265    pub fn new(show_empty: bool) -> Self {
266        Self {
267            net: Default::default(),
268            sim: Default::default(),
269            show_empty,
270        }
271    }
272
273    #[inline(always)]
274    fn columns(&self, net: &<S as Simulation>::Net) -> (Vec<NodeId>, Vec<NodeId>) {
275        let mut places: Vec<NodeId> = net.places().iter().map(|place| place.id()).collect();
276        places.sort();
277        let mut transitions: Vec<NodeId> = net
278            .transitions()
279            .iter()
280            .map(|transition| transition.id())
281            .collect();
282        transitions.sort();
283        (places, transitions)
284    }
285}