1use crate::parser::{OmgBaseType, OmgType, OmgTypeDef, OmgTypes};
2
3use super::ScxmlModel;
4use scan_core::channel_system::{Action, Event, EventType};
5use scan_core::{Time, TraceWriter, Tracer, Val};
6
7#[derive(Debug)]
8pub struct TracePrinter {
9 writer: csv::Writer<TraceWriter>,
10}
11
12impl Drop for TracePrinter {
13 fn drop(&mut self) {
14 self.writer.flush().expect("flush writer");
15 }
16}
17
18impl TracePrinter {
19 const HEADER: [&'static str; 5] = ["Time", "Event", "Origin", "Target", "Params"];
20
21 fn format_state(&self, model: &ScxmlModel, event: &Event, ports: &[Vec<Val>]) -> Vec<String> {
22 model
24 .port_vars
25 .iter()
26 .map(move |(_, omg_type, exprs)| {
27 format_val(
28 exprs
29 .iter()
30 .map(|expr| {
31 expr.eval_deterministic(&|atom| match atom {
32 scan_core::Atom::State(channel, i) => ports
33 .get(model.ports.binary_search(&channel).unwrap())
34 .unwrap()[i],
35 scan_core::Atom::Event(channel) => {
36 if event.channel == channel
37 && let EventType::Send(_) = event.event_type
38 {
39 Val::from(true)
40 } else {
41 Val::from(false)
42 }
43 }
44 })
45 })
46 .collect::<Vec<_>>()
47 .as_slice(),
48 omg_type,
49 &model.omg_types,
50 )
51 })
52 .collect()
53 }
54}
55
56impl Tracer for TracePrinter {
57 const EXTENSION: &'static str = "csv";
58
59 type ModelData = ScxmlModel;
60
61 fn init(writer: TraceWriter, data: &ScxmlModel) -> Self {
62 let mut writer = csv::Writer::from_writer(writer);
63 writer
64 .write_record(
65 Self::HEADER.into_iter().map(String::from).chain(
66 data.port_vars.iter().map(|(name, omg_type, _)| {
67 format!("{name}: {}", format_omg_type(omg_type))
68 }),
69 ),
70 )
71 .expect("write header");
72
73 Self { writer }
74 }
75
76 fn trace(
77 &mut self,
78 data: &ScxmlModel,
79 _action: Action,
80 event: &Event,
81 time: Time,
82 ports: &[Vec<Val>],
83 ) {
84 let mut fields = Vec::new();
85 let time = time.to_string();
86 let origin_name;
87 let target_name;
88 let event_name;
89 let param_types;
90 let mut params = String::new();
91 fields.push(time.as_str());
92
93 if let Some((src, trg, event_idx)) = data.parameters.get(&event.channel) {
94 origin_name = data.fsm_names.get(&(*src).into()).unwrap().to_owned();
95 target_name = data.fsm_names.get(&(*trg).into()).unwrap().to_owned();
96 (event_name, param_types) = data.events.get(*event_idx).unwrap().clone();
97 if let EventType::Send(ref vals) = event.event_type {
98 params =
99 format_val_from_def(vals, param_types.as_ref().unwrap(), &data.omg_types, true);
100 } else {
101 return;
102 }
103 } else if let Some(trg) = data.ext_queues.get(&event.channel) {
104 target_name = data.fsm_names.get(&(*trg).into()).unwrap().to_owned();
105 if let EventType::Send(ref vals) = event.event_type {
106 if let (Val::Natural(sent_event), Val::Natural(origin)) = (vals[0], vals[1]) {
107 origin_name = data.fsm_names.get(&(origin as u16)).unwrap().to_owned();
108 (event_name, param_types) = data.events[sent_event as usize].clone();
109 if param_types.is_some() {
110 return;
112 }
113 } else {
114 panic!("events should be pairs");
115 }
116 } else {
117 return;
118 }
119 } else if data.int_queues.contains(&event.channel) {
120 origin_name = data.fsm_names.get(&event.pg_id.into()).unwrap().to_owned();
121 target_name = origin_name.clone();
122 if let EventType::Send(ref vals) = event.event_type {
123 if let Val::Natural(sent_event) = vals[0] {
124 (event_name, param_types) = data.events[sent_event as usize].clone();
125 if param_types.is_some() {
126 return;
128 }
129 } else {
130 panic!("events should be indexed by natural");
131 }
132 } else {
133 return;
134 }
135 } else {
136 panic!("Events should all be either internal or external events");
137 }
138
139 let state = self.format_state(data, event, ports);
140 self.writer
141 .write_record(
142 [time, event_name, origin_name, target_name, params]
143 .into_iter()
144 .chain(state),
145 )
146 .expect("write record");
147 }
148}
149
150fn format_omg_type(omg_type: &OmgType) -> String {
151 match omg_type {
152 OmgType::Base(omg_base_type) => format_omg_base_type(*omg_base_type).to_string(),
153 OmgType::Array(omg_base_type, _) => {
154 format!("[{}]", format_omg_base_type(*omg_base_type))
155 }
156 OmgType::Custom(name) => name.clone(),
157 }
158}
159
160fn format_omg_base_type(omg_base_type: OmgBaseType) -> &'static str {
161 match omg_base_type {
162 OmgBaseType::Boolean => "bool",
163 OmgBaseType::Int64 => "int32",
164 OmgBaseType::F64 => "float64",
165 OmgBaseType::Uri => "uri",
166 OmgBaseType::String => "string",
167 OmgBaseType::Uint64 => "uint64",
168 }
169}
170
171fn format_val(vals: &[Val], omg_type: &OmgType, omg_types: &OmgTypes) -> String {
172 match omg_type {
173 OmgType::Base(OmgBaseType::String) => {
174 if let Val::Natural(index) = vals
175 .first()
176 .expect("strings should be encoded as exactly one natural number variable")
177 {
178 format!(
179 "'{}'",
180 omg_types
181 .get_string(*index as usize)
182 .expect("all string codes should correspond to a string")
183 )
184 } else {
185 panic!("string not encoded as a natural number")
186 }
187 }
188 OmgType::Base(_omg_base_type) => format_base_val(vals[0]),
189 OmgType::Array(_omg_base_type, _len) => format!(
190 "{:?}",
191 vals.iter()
192 .map(|val: &Val| format_base_val(*val))
193 .collect::<Vec<String>>()
194 )
195 .replace("\"", ""),
196 OmgType::Custom(omg_name) => format!(
197 "{omg_name}: {}",
198 format_val_from_def(
199 vals,
200 omg_types.type_defs.get(omg_name).expect("type def"),
201 omg_types,
202 false
203 )
204 ),
205 }
206}
207
208fn format_val_from_def(
209 vals: &[Val],
210 omg_type: &OmgTypeDef,
211 omg_types: &OmgTypes,
212 spread_structs: bool,
213) -> String {
214 match omg_type {
215 OmgTypeDef::Enumeration(items) => {
216 if let Val::Natural(int) = vals[0] {
217 items[int as usize].clone()
218 } else {
219 panic!("enumeration is not represented as Natural")
220 }
221 }
222 OmgTypeDef::Structure(btree_map) => {
223 let mut prev_size_acc = 0;
224 let mut size_acc = 0;
225 let fields = btree_map
226 .iter()
227 .map(|(name, omg_type)| {
228 let size = omg_type.size(omg_types).unwrap();
229 prev_size_acc = size_acc;
230 size_acc += size;
231 let field = format_val(&vals[prev_size_acc..size_acc], omg_type, omg_types);
232 format!("{name}: {field}")
233 })
234 .collect::<Vec<_>>();
235 if spread_structs {
236 fields.join("\n")
238 } else {
239 format!("{fields:?}")
240 }
241 .replace("\"", "")
242 }
243 }
244}
245
246fn format_base_val(val: Val) -> String {
247 match val {
248 Val::Boolean(true) => "true".to_string(),
249 Val::Boolean(false) => "false".to_string(),
250 Val::Integer(i) => i.to_string(),
251 Val::Float(ordered_float) => ordered_float.to_string(),
252 Val::Natural(n) => n.to_string(),
253 }
254}