1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::{format_ident, quote};
5use std::fs::read_to_string;
6use syn::meta::parser;
7use syn::Fields::{Named, Unnamed};
8use syn::{
9 parse_macro_input, parse_quote, parse_str, Field, Fields, ItemImpl, ItemStruct, LitStr, Type,
10 TypeTuple,
11};
12
13use crate::utils::config_id_to_enum;
14use cu29_runtime::config::CuConfig;
15use cu29_runtime::config::{read_configuration, CuGraph};
16use cu29_runtime::curuntime::{
17 compute_runtime_plan, find_task_type_for_id, CuExecutionLoop, CuExecutionUnit, CuTaskType,
18};
19use cu29_traits::CuResult;
20use proc_macro2::{Ident, Span};
21
22#[cfg(feature = "macro_debug")]
23use crate::format::rustfmt_generated_code;
24
25mod format;
26mod utils;
27
28const DEFAULT_CLNB: usize = 10;
30
31#[inline]
32fn int2sliceindex(i: u32) -> syn::Index {
33 syn::Index::from(i as usize)
34}
35
36#[inline(always)]
37fn return_error(msg: String) -> TokenStream {
38 syn::Error::new(Span::call_site(), msg)
39 .to_compile_error()
40 .into()
41}
42
43#[proc_macro]
47pub fn gen_cumsgs(config_path_lit: TokenStream) -> TokenStream {
48 let config = parse_macro_input!(config_path_lit as LitStr).value();
49 if !std::path::Path::new(&config_full_path(&config)).exists() {
50 return return_error(format!(
51 "The configuration file `{config}` does not exist. Please provide a valid path."
52 ));
53 }
54 #[cfg(feature = "macro_debug")]
55 eprintln!("[gen culist support with {config:?}]");
56 let cuconfig = match read_config(&config) {
57 Ok(cuconfig) => cuconfig,
58 Err(e) => return return_error(e.to_string()),
59 };
60 let graph = cuconfig
61 .get_graph(None) .expect("Could not find the specified mission for gen_cumsgs");
63 let runtime_plan: CuExecutionLoop = match compute_runtime_plan(graph) {
64 Ok(plan) => plan,
65 Err(e) => return return_error(format!("Could not compute runtime plan: {e}")),
66 };
67
68 let all_tasks_member_ids: Vec<String> = graph
70 .get_all_nodes()
71 .iter()
72 .map(|(_, node)| utils::config_id_to_struct_member(node.get_id().as_str()))
73 .collect();
74
75 let taskid_order: Vec<usize> = runtime_plan
78 .steps
79 .iter()
80 .filter_map(|unit| match unit {
81 CuExecutionUnit::Step(step) => Some(step.node_id as usize),
82 _ => None,
83 })
84 .collect();
85
86 #[cfg(feature = "macro_debug")]
87 eprintln!(
88 "[The CuMsgs matching tasks ids are {:?}]",
89 taskid_order
90 .iter()
91 .map(|i| all_tasks_member_ids[*i].clone())
92 .collect::<Vec<_>>()
93 );
94
95 let support = gen_culist_support(&runtime_plan, &taskid_order, &all_tasks_member_ids);
96
97 let with_uses = quote! {
98 mod cumsgs {
99 use cu29::bincode::Encode;
100 use cu29::bincode::enc::Encoder;
101 use cu29::bincode::error::EncodeError;
102 use cu29::bincode::Decode;
103 use cu29::bincode::de::Decoder;
104 use cu29::bincode::error::DecodeError;
105 use cu29::copperlist::CopperList;
106 use cu29::cutask::CuMsgMetadata;
107 use cu29::cutask::CuMsg;
108 use cu29::prelude::ErasedCuMsg;
109 use cu29::prelude::ErasedCuMsgs;
110 use cu29::prelude::MatchingTasks;
111 use cu29::prelude::Serialize;
112 #support
113 }
114 use cumsgs::CuMsgs;
115 };
116 with_uses.into()
117}
118
119fn gen_culist_support(
121 runtime_plan: &CuExecutionLoop,
122 taskid_call_order: &[usize],
123 all_tasks_as_struct_member_name: &Vec<String>,
124) -> proc_macro2::TokenStream {
125 #[cfg(feature = "macro_debug")]
126 eprintln!("[Extract msgs types]");
127 let all_msgs_types_in_culist_order = extract_msg_types(runtime_plan);
128
129 let culist_size = all_msgs_types_in_culist_order.len();
130 let task_indices: Vec<_> = taskid_call_order
131 .iter()
132 .map(|i| syn::Index::from(*i))
133 .collect();
134
135 #[cfg(feature = "macro_debug")]
136 eprintln!("[build the copperlist struct]");
137 let msgs_types_tuple: TypeTuple = build_culist_tuple(&all_msgs_types_in_culist_order);
138
139 #[cfg(feature = "macro_debug")]
140 eprintln!("[build the copperlist tuple bincode support]");
141 let msgs_types_tuple_encode = build_culist_tuple_encode(&all_msgs_types_in_culist_order);
142 let msgs_types_tuple_decode = build_culist_tuple_decode(&all_msgs_types_in_culist_order);
143
144 #[cfg(feature = "macro_debug")]
145 eprintln!("[build the copperlist tuple debug support]");
146 let msgs_types_tuple_debug = build_culist_tuple_debug(&all_msgs_types_in_culist_order);
147
148 #[cfg(feature = "macro_debug")]
149 eprintln!("[build the copperlist tuple serialize support]");
150 let msgs_types_tuple_serialize = build_culist_tuple_serialize(&all_msgs_types_in_culist_order);
151
152 let erasedmsg_trait_impl = build_culist_erasedcumsgs(&all_msgs_types_in_culist_order);
153
154 let collect_metadata_function = quote! {
155 pub fn collect_metadata<'a>(culist: &'a CuList) -> [&'a CuMsgMetadata; #culist_size] {
156 [#( &culist.msgs.0.#task_indices.metadata, )*]
157 }
158 };
159
160 let methods = itertools::multizip((all_tasks_as_struct_member_name, taskid_call_order)).map(
161 |(name, output_position)| {
162 let fn_name = format_ident!("get_{}_output", name);
163 let payload_type = all_msgs_types_in_culist_order[*output_position].clone();
164 let index = syn::Index::from(*output_position);
165 quote! {
166 #[allow(dead_code)]
167 pub fn #fn_name(&self) -> &CuMsg<#payload_type> {
168 &self.0.#index
169 }
170 }
171 },
172 );
173
174 quote! {
176 #collect_metadata_function
177
178 pub struct CuMsgs(pub #msgs_types_tuple);
179
180 pub type CuList = CopperList<CuMsgs>;
181
182 impl CuMsgs {
183 #(#methods)*
184
185 #[allow(dead_code)]
186 fn get_tuple(&self) -> &#msgs_types_tuple {
187 &self.0
188 }
189
190 #[allow(dead_code)]
191 fn get_tuple_mut(&mut self) -> &mut #msgs_types_tuple {
192 &mut self.0
193 }
194 }
195
196 impl MatchingTasks for CuMsgs {
197 #[allow(dead_code)]
198 fn get_all_task_ids() -> &'static [&'static str] {
199 &[#(#all_tasks_as_struct_member_name),*]
200 }
201 }
202
203 #msgs_types_tuple_encode
205 #msgs_types_tuple_decode
206
207 #msgs_types_tuple_debug
209
210 #msgs_types_tuple_serialize
212
213 #erasedmsg_trait_impl
215 }
216}
217
218fn gen_sim_support(runtime_plan: &CuExecutionLoop) -> proc_macro2::TokenStream {
219 #[cfg(feature = "macro_debug")]
220 eprintln!("[Sim: Build SimEnum]");
221 let plan_enum: Vec<proc_macro2::TokenStream> = runtime_plan
222 .steps
223 .iter()
224 .map(|unit| match unit {
225 CuExecutionUnit::Step(step) => {
226 let enum_entry_name = config_id_to_enum(step.node.get_id().as_str());
227 let enum_ident = Ident::new(&enum_entry_name, proc_macro2::Span::call_site());
228 let inputs: Vec<Type> = step
229 .input_msg_indices_types
230 .iter()
231 .map(|(_, t)| parse_str::<Type>(format!("CuMsg<{t}>").as_str()).unwrap())
232 .collect();
233 let output: Option<Type> = step
234 .output_msg_index_type
235 .as_ref()
236 .map(|(_, t)| parse_str::<Type>(format!("CuMsg<{t}>").as_str()).unwrap());
237 let no_output = parse_str::<Type>("CuMsg<()>").unwrap();
238 let output = output.as_ref().unwrap_or(&no_output);
239
240 let inputs_type = if inputs.len() == 1 {
241 quote! { &'a #(#inputs)* }
242 } else {
243 quote! { (#(&'a #inputs),*) }
244 };
245
246 quote! {
247 #enum_ident(cu29::simulation::CuTaskCallbackState<#inputs_type, &'a mut #output>)
248 }
249 }
250 CuExecutionUnit::Loop(_) => {
251 todo!("Needs to be implemented")
252 }
253 })
254 .collect();
255 quote! {
256 #[allow(dead_code)]
258 pub enum SimStep<'a> {
259 #(#plan_enum),*
260 }
261 }
262}
263
264#[proc_macro_attribute]
268pub fn copper_runtime(args: TokenStream, input: TokenStream) -> TokenStream {
269 #[cfg(feature = "macro_debug")]
270 eprintln!("[entry]");
271 let mut application_struct = parse_macro_input!(input as ItemStruct);
272 let application_name = &application_struct.ident;
273 let builder_name = format_ident!("{}Builder", application_name);
274
275 let mut config_file: Option<LitStr> = None;
276 let mut sim_mode = false;
277
278 let attribute_config_parser = parser(|meta| {
280 if meta.path.is_ident("config") {
281 config_file = Some(meta.value()?.parse()?);
282 Ok(())
283 } else if meta.path.is_ident("sim_mode") {
284 if meta.input.peek(syn::Token![=]) {
286 meta.input.parse::<syn::Token![=]>()?;
287 let value: syn::LitBool = meta.input.parse()?;
288 sim_mode = value.value();
289 Ok(())
290 } else {
291 sim_mode = true;
293 Ok(())
294 }
295 } else {
296 Err(meta.error("unsupported property"))
297 }
298 });
299
300 #[cfg(feature = "macro_debug")]
301 eprintln!("[parse]");
302 parse_macro_input!(args with attribute_config_parser);
304
305 let config_file = match config_file {
307 Some(file) => file.value(),
308 None => {
309 return return_error(
310 "Expected config file attribute like #[CopperRuntime(config = \"path\")]"
311 .to_string(),
312 )
313 }
314 };
315
316 if !std::path::Path::new(&config_full_path(&config_file)).exists() {
317 return return_error(format!(
318 "The configuration file `{config_file}` does not exist. Please provide a valid path."
319 ));
320 }
321
322 let copper_config = match read_config(&config_file) {
323 Ok(cuconfig) => cuconfig,
324 Err(e) => return return_error(e.to_string()),
325 };
326 let copper_config_content = match read_to_string(config_full_path(config_file.as_str())) {
327 Ok(ok) => ok,
328 Err(e) => return return_error(format!("Could not read the config file (should not happen because we just succeeded just before). {e}"))
329 };
330
331 #[cfg(feature = "macro_debug")]
332 eprintln!("[build monitor type]");
333 let monitor_type = if let Some(monitor_config) = copper_config.get_monitor_config() {
334 let monitor_type = parse_str::<Type>(monitor_config.get_type())
335 .expect("Could not transform the monitor type name into a Rust type.");
336 quote! { #monitor_type }
337 } else {
338 quote! { NoMonitor }
339 };
340
341 #[cfg(feature = "macro_debug")]
343 eprintln!("[build runtime field]");
344 let runtime_field: Field = if sim_mode {
346 parse_quote! {
347 copper_runtime: cu29::curuntime::CuRuntime<CuSimTasks, CuMsgs, #monitor_type, #DEFAULT_CLNB>
348 }
349 } else {
350 parse_quote! {
351 copper_runtime: cu29::curuntime::CuRuntime<CuTasks, CuMsgs, #monitor_type, #DEFAULT_CLNB>
352 }
353 };
354
355 #[cfg(feature = "macro_debug")]
356 eprintln!("[match struct anonymity]");
357 match &mut application_struct.fields {
358 Named(fields_named) => {
359 fields_named.named.push(runtime_field);
360 }
361 Unnamed(fields_unnamed) => {
362 fields_unnamed.unnamed.push(runtime_field);
363 }
364 Fields::Unit => {
365 panic!("This struct is a unit struct, it should have named or unnamed fields. use struct Something {{}} and not struct Something;")
366 }
367 };
368
369 let all_missions = copper_config.graphs.get_all_missions_graphs();
370 let mut all_missions_tokens = Vec::<proc_macro2::TokenStream>::new();
371 for (mission, graph) in &all_missions {
372 let mission_mod = parse_str::<Ident>(mission.as_str())
373 .expect("Could not make an identifier of the mission name");
374
375 #[cfg(feature = "macro_debug")]
376 eprintln!("[runtime plan for mission {mission}]");
377 let runtime_plan: CuExecutionLoop = match compute_runtime_plan(graph) {
378 Ok(plan) => plan,
379 Err(e) => return return_error(format!("Could not compute runtime plan: {e}")),
380 };
381 #[cfg(feature = "macro_debug")]
382 eprintln!("{runtime_plan:?}");
383
384 #[cfg(feature = "macro_debug")]
385 eprintln!("[extract tasks ids & types]");
386 let (all_tasks_ids, all_tasks_cutype, all_tasks_types_names, all_tasks_types) =
387 extract_tasks_types(graph);
388
389 let all_sim_tasks_types: Vec<Type> = all_tasks_ids
390 .iter()
391 .zip(&all_tasks_cutype)
392 .zip(&all_tasks_types)
393 .map(|((task_id, cutype), stype)| match cutype {
394 CuTaskType::Source => {
395 let msg_type = graph
396 .get_node_output_msg_type(task_id.as_str())
397 .unwrap_or_else(|| panic!("CuSrcTask {task_id} should have an outgoing connection with a valid output msg type"));
398 let sim_task_name = format!("cu29::simulation::CuSimSrcTask<{msg_type}>");
399 parse_str(sim_task_name.as_str()).unwrap_or_else(|_| panic!("Could not build the placeholder for simulation: {sim_task_name}"))
400 }
401 CuTaskType::Regular => stype.clone(),
402 CuTaskType::Sink => {
403 let msg_type = graph
404 .get_node_input_msg_type(task_id.as_str())
405 .unwrap_or_else(|| panic!("CuSinkTask {task_id} should have an incoming connection with a valid input msg type"));
406 let sim_task_name = format!("cu29::simulation::CuSimSinkTask<{msg_type}>");
407 parse_str(sim_task_name.as_str()).unwrap_or_else(|_| panic!("Could not build the placeholder for simulation: {sim_task_name}"))
408 }
409 })
410 .collect();
411
412 #[cfg(feature = "macro_debug")]
413 eprintln!("[build task tuples]");
414 let task_types_tuple: TypeTuple = parse_quote! {
417 (#(#all_tasks_types),*,)
418 };
419
420 let task_types_tuple_sim: TypeTuple = parse_quote! {
421 (#(#all_sim_tasks_types),*,)
422 };
423
424 #[cfg(feature = "macro_debug")]
425 eprintln!("[gen instances]");
426
427 let task_sim_instances_init_code = all_sim_tasks_types.iter().enumerate().map(|(index, ty)| {
428 let additional_error_info = format!(
429 "Failed to get create instance for {}, instance index {}.",
430 all_tasks_types_names[index], index
431 );
432
433 quote! {
434 <#ty>::new(all_instances_configs[#index]).map_err(|e| e.add_cause(#additional_error_info))?
435 }
436 }).collect::<Vec<_>>();
437
438 let (task_instances_init_code,
441 task_restore_code,
442 start_calls,
443 stop_calls,
444 preprocess_calls,
445 postprocess_calls): (Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>) = itertools::multiunzip(all_tasks_types
446 .iter()
447 .enumerate()
448 .map(|(index, ty)| {
449 let task_index = int2sliceindex(index as u32);
450 let task_tuple_index = syn::Index::from(index);
451 let task_enum_name = config_id_to_enum(&all_tasks_ids[index]);
452 let enum_name = Ident::new(&task_enum_name, proc_macro2::Span::call_site());
453 let additional_error_info = format!(
454 "Failed to get create instance for {}, instance index {}.",
455 all_tasks_types_names[index], index
456 );
457 ( quote! {
459 #ty::new(all_instances_configs[#index]).map_err(|e| e.add_cause(#additional_error_info))?
460 },
461 quote! {
463 tasks.#task_tuple_index.thaw(&mut decoder).map_err(|e| CuError::new_with_cause("Failed to thaw", e))?
464 },
465 { let monitoring_action = quote! {
467 let decision = self.copper_runtime.monitor.process_error(#index, CuTaskState::Start, &error);
468 match decision {
469 Decision::Abort => {
470 debug!("Start: ABORT decision from monitoring. Task '{}' errored out \
471 during start. Aborting all the other starts.", #mission_mod::TASKS_IDS[#index]);
472 return Ok(());
473
474 }
475 Decision::Ignore => {
476 debug!("Start: IGNORE decision from monitoring. Task '{}' errored out \
477 during start. The runtime will continue.", #mission_mod::TASKS_IDS[#index]);
478 }
479 Decision::Shutdown => {
480 debug!("Start: SHUTDOWN decision from monitoring. Task '{}' errored out \
481 during start. The runtime cannot continue.", #mission_mod::TASKS_IDS[#index]);
482 return Err(CuError::new_with_cause("Task errored out during start.", error));
483 }
484 }
485 };
486
487 let call_sim_callback = if sim_mode {
488 quote! {
489 let ovr = sim_callback(SimStep::#enum_name(cu29::simulation::CuTaskCallbackState::Start));
491
492 let doit = if let cu29::simulation::SimOverride::Errored(reason) = ovr {
493 let error: CuError = reason.into();
494 #monitoring_action
495 false
496 }
497 else {
498 ovr == cu29::simulation::SimOverride::ExecuteByRuntime
499 };
500 }
501 } else {
502 quote! {
503 let doit = true; }
505 };
506
507
508 quote! {
509 #call_sim_callback
510 if doit {
511 let task = &mut self.copper_runtime.tasks.#task_index;
512 if let Err(error) = task.start(&self.copper_runtime.clock) {
513 #monitoring_action
514 }
515 }
516 }
517 },
518 { let monitoring_action = quote! {
520 let decision = self.copper_runtime.monitor.process_error(#index, CuTaskState::Stop, &error);
521 match decision {
522 Decision::Abort => {
523 debug!("Stop: ABORT decision from monitoring. Task '{}' errored out \
524 during stop. Aborting all the other starts.", #mission_mod::TASKS_IDS[#index]);
525 return Ok(());
526
527 }
528 Decision::Ignore => {
529 debug!("Stop: IGNORE decision from monitoring. Task '{}' errored out \
530 during stop. The runtime will continue.", #mission_mod::TASKS_IDS[#index]);
531 }
532 Decision::Shutdown => {
533 debug!("Stop: SHUTDOWN decision from monitoring. Task '{}' errored out \
534 during stop. The runtime cannot continue.", #mission_mod::TASKS_IDS[#index]);
535 return Err(CuError::new_with_cause("Task errored out during stop.", error));
536 }
537 }
538 };
539 let call_sim_callback = if sim_mode {
540 quote! {
541 let ovr = sim_callback(SimStep::#enum_name(cu29::simulation::CuTaskCallbackState::Stop));
543
544 let doit = if let cu29::simulation::SimOverride::Errored(reason) = ovr {
545 let error: CuError = reason.into();
546 #monitoring_action
547 false
548 }
549 else {
550 ovr == cu29::simulation::SimOverride::ExecuteByRuntime
551 };
552 }
553 } else {
554 quote! {
555 let doit = true; }
557 };
558 quote! {
559 #call_sim_callback
560 if doit {
561 let task = &mut self.copper_runtime.tasks.#task_index;
562 if let Err(error) = task.stop(&self.copper_runtime.clock) {
563 #monitoring_action
564 }
565 }
566 }
567 },
568 { let monitoring_action = quote! {
570 let decision = monitor.process_error(#index, CuTaskState::Preprocess, &error);
571 match decision {
572 Decision::Abort => {
573 debug!("Preprocess: ABORT decision from monitoring. Task '{}' errored out \
574 during preprocess. Aborting all the other starts.", #mission_mod::TASKS_IDS[#index]);
575 return Ok(());
576
577 }
578 Decision::Ignore => {
579 debug!("Preprocess: IGNORE decision from monitoring. Task '{}' errored out \
580 during preprocess. The runtime will continue.", #mission_mod::TASKS_IDS[#index]);
581 }
582 Decision::Shutdown => {
583 debug!("Preprocess: SHUTDOWN decision from monitoring. Task '{}' errored out \
584 during preprocess. The runtime cannot continue.", #mission_mod::TASKS_IDS[#index]);
585 return Err(CuError::new_with_cause("Task errored out during preprocess.", error));
586 }
587 }
588 };
589 let call_sim_callback = if sim_mode {
590 quote! {
591 let ovr = sim_callback(SimStep::#enum_name(cu29::simulation::CuTaskCallbackState::Preprocess));
593
594 let doit = if let cu29::simulation::SimOverride::Errored(reason) = ovr {
595 let error: CuError = reason.into();
596 #monitoring_action
597 false
598 } else {
599 ovr == cu29::simulation::SimOverride::ExecuteByRuntime
600 };
601 }
602 } else {
603 quote! {
604 let doit = true; }
606 };
607 quote! {
608 #call_sim_callback
609 if doit {
610 if let Err(error) = tasks.#task_index.preprocess(clock) {
611 #monitoring_action
612 }
613 }
614 }
615 },
616 { let monitoring_action = quote! {
618 let decision = monitor.process_error(#index, CuTaskState::Postprocess, &error);
619 match decision {
620 Decision::Abort => {
621 debug!("Postprocess: ABORT decision from monitoring. Task '{}' errored out \
622 during postprocess. Aborting all the other starts.", #mission_mod::TASKS_IDS[#index]);
623 return Ok(());
624
625 }
626 Decision::Ignore => {
627 debug!("Postprocess: IGNORE decision from monitoring. Task '{}' errored out \
628 during postprocess. The runtime will continue.", #mission_mod::TASKS_IDS[#index]);
629 }
630 Decision::Shutdown => {
631 debug!("Postprocess: SHUTDOWN decision from monitoring. Task '{}' errored out \
632 during postprocess. The runtime cannot continue.", #mission_mod::TASKS_IDS[#index]);
633 return Err(CuError::new_with_cause("Task errored out during postprocess.", error));
634 }
635 }
636 };
637 let call_sim_callback = if sim_mode {
638 quote! {
639 let ovr = sim_callback(SimStep::#enum_name(cu29::simulation::CuTaskCallbackState::Postprocess));
641
642 let doit = if let cu29::simulation::SimOverride::Errored(reason) = ovr {
643 let error: CuError = reason.into();
644 #monitoring_action
645 false
646 } else {
647 ovr == cu29::simulation::SimOverride::ExecuteByRuntime
648 };
649 }
650 } else {
651 quote! {
652 let doit = true; }
654 };
655 quote! {
656 #call_sim_callback
657 if doit {
658 if let Err(error) = tasks.#task_index.postprocess(clock) {
659 #monitoring_action
660 }
661 }
662 }
663 }
664 )
665 })
666 );
667
668 let mut taskid_call_order: Vec<usize> = Vec::new();
671
672 let runtime_plan_code: Vec<proc_macro2::TokenStream> = runtime_plan.steps
673 .iter()
674 .map(|unit| {
675 match unit {
676 CuExecutionUnit::Step(step) => {
677 #[cfg(feature = "macro_debug")]
678 eprintln!(
679 "{} -> {} as {:?}. task_id: {} Input={:?}, Output={:?}",
680 step.node.get_id(),
681 step.node.get_type(),
682 step.task_type,
683 step.node_id,
684 step.input_msg_indices_types,
685 step.output_msg_index_type
686 );
687
688 let node_index = int2sliceindex(step.node_id);
689 let task_instance = quote! { tasks.#node_index };
690 let comment_str = format!(
691 "DEBUG ->> {} ({:?}) Id:{} I:{:?} O:{:?}",
692 step.node.get_id(),
693 step.task_type,
694 step.node_id,
695 step.input_msg_indices_types,
696 step.output_msg_index_type
697 );
698 let comment_tokens = quote! {
699 {
700 let _ = stringify!(#comment_str);
701 }
702 };
703 let tid = step.node_id as usize;
705 taskid_call_order.push(tid);
706
707 let task_enum_name = config_id_to_enum(&all_tasks_ids[tid]);
708 let enum_name = Ident::new(&task_enum_name, proc_macro2::Span::call_site());
709
710 let process_call = match step.task_type {
711 CuTaskType::Source => {
712 if let Some((index, _)) = &step.output_msg_index_type {
713 let output_culist_index = int2sliceindex(*index);
714
715 let monitoring_action = quote! {
716 debug!("Task {}: Error during process: {}", #mission_mod::TASKS_IDS[#tid], &error);
717 let decision = monitor.process_error(#tid, CuTaskState::Process, &error);
718 match decision {
719 Decision::Abort => {
720 debug!("Process: ABORT decision from monitoring. Task '{}' errored out \
721 during process. Skipping the processing of CL {}.", #mission_mod::TASKS_IDS[#tid], clid);
722 monitor.process_copperlist(&#mission_mod::collect_metadata(&culist))?;
723 cl_manager.end_of_processing(clid);
724 return Ok(()); }
727 Decision::Ignore => {
728 debug!("Process: IGNORE decision from monitoring. Task '{}' errored out \
729 during process. The runtime will continue with a forced empty message.", #mission_mod::TASKS_IDS[#tid]);
730 let cumsg_output = &mut msgs.#output_culist_index;
731 cumsg_output.clear_payload();
732 }
733 Decision::Shutdown => {
734 debug!("Process: SHUTDOWN decision from monitoring. Task '{}' errored out \
735 during process. The runtime cannot continue.", #mission_mod::TASKS_IDS[#tid]);
736 return Err(CuError::new_with_cause("Task errored out during process.", error));
737 }
738 }
739 };
740 let call_sim_callback = if sim_mode {
741 quote! {
742 let doit = {
743 let cumsg_output = &mut msgs.#output_culist_index;
744 let state = cu29::simulation::CuTaskCallbackState::Process((), cumsg_output);
745 let ovr = sim_callback(SimStep::#enum_name(state));
746 if let cu29::simulation::SimOverride::Errored(reason) = ovr {
747 let error: CuError = reason.into();
748 #monitoring_action
749 false
750 } else {
751 ovr == cu29::simulation::SimOverride::ExecuteByRuntime
752 }
753 };
754 }
755 } else {
756 quote! {
757 let doit = true; }
759 };
760
761 quote! {
762 {
763 #comment_tokens
764 {
765 kf_manager.freeze_task(clid, &#task_instance)?;
767 #call_sim_callback
768 let cumsg_output = &mut msgs.#output_culist_index;
769 cumsg_output.metadata.process_time.start = clock.now().into();
770 let maybe_error = if doit {
771 #task_instance.process(clock, cumsg_output)
772 } else {
773 Ok(())
774 };
775 cumsg_output.metadata.process_time.end = clock.now().into();
776 if let Err(error) = maybe_error {
777 #monitoring_action
778 }
779 }
780 }
781 }
782 } else {
783 panic!("Source task should have an output message index.");
784 }
785 }
786 CuTaskType::Sink => {
787 let indices = step.input_msg_indices_types.iter().map(|(index, _)| int2sliceindex(*index));
789 if let Some((output_index, _)) = &step.output_msg_index_type {
790 let output_culist_index = int2sliceindex(*output_index);
791
792 let monitoring_action = quote! {
793 debug!("Task {}: Error during process: {}", #mission_mod::TASKS_IDS[#tid], &error);
794 let decision = monitor.process_error(#tid, CuTaskState::Process, &error);
795 match decision {
796 Decision::Abort => {
797 debug!("Process: ABORT decision from monitoring. Task '{}' errored out \
798 during process. Skipping the processing of CL {}.", #mission_mod::TASKS_IDS[#tid], clid);
799 monitor.process_copperlist(&#mission_mod::collect_metadata(&culist))?;
800 cl_manager.end_of_processing(clid);
801 return Ok(()); }
804 Decision::Ignore => {
805 debug!("Process: IGNORE decision from monitoring. Task '{}' errored out \
806 during process. The runtime will continue with a forced empty message.", #mission_mod::TASKS_IDS[#tid]);
807 let cumsg_output = &mut msgs.#output_culist_index;
808 cumsg_output.clear_payload();
809 }
810 Decision::Shutdown => {
811 debug!("Process: SHUTDOWN decision from monitoring. Task '{}' errored out \
812 during process. The runtime cannot continue.", #mission_mod::TASKS_IDS[#tid]);
813 return Err(CuError::new_with_cause("Task errored out during process.", error));
814 }
815 }
816 };
817
818 let call_sim_callback = if sim_mode {
819
820 let inputs_type = if indices.len() == 1 {
821 quote! { #(&msgs.#indices)* }
823 } else {
824 quote! { (#(&msgs.#indices),*) }
826 };
827
828 quote! {
829 let doit = {
830 let cumsg_input = #inputs_type;
831 let cumsg_output = &mut msgs.#output_culist_index;
833 let state = cu29::simulation::CuTaskCallbackState::Process(cumsg_input, cumsg_output);
834 let ovr = sim_callback(SimStep::#enum_name(state));
835
836 if let cu29::simulation::SimOverride::Errored(reason) = ovr {
837 let error: CuError = reason.into();
838 #monitoring_action
839 false
840 } else {
841 ovr == cu29::simulation::SimOverride::ExecuteByRuntime
842 }
843 };
844 }
845 } else {
846 quote! {
847 let doit = true; }
849 };
850
851 let indices = step.input_msg_indices_types.iter().map(|(index, _)| int2sliceindex(*index));
852
853 let inputs_type = if indices.len() == 1 {
854 quote! { #(&msgs.#indices)* }
856 } else {
857 quote! { (#(&msgs.#indices),*) }
859 };
860
861 quote! {
862 {
863 #comment_tokens
864 kf_manager.freeze_task(clid, &#task_instance)?;
866 #call_sim_callback
867 let cumsg_input = #inputs_type;
868 let cumsg_output = &mut msgs.#output_culist_index;
870 cumsg_output.metadata.process_time.start = clock.now().into();
871 let maybe_error = if doit {#task_instance.process(clock, cumsg_input)} else {Ok(())};
872 cumsg_output.metadata.process_time.end = clock.now().into();
873 if let Err(error) = maybe_error {
874 #monitoring_action
875 }
876 }
877 }
878 } else {
879 panic!("Sink tasks should have a virtual output message index.");
880 }
881 }
882 CuTaskType::Regular => {
883 let indices = step.input_msg_indices_types.iter().map(|(index, _)| int2sliceindex(*index));
884 if let Some((output_index, _)) = &step.output_msg_index_type {
885 let output_culist_index = int2sliceindex(*output_index);
886
887 let monitoring_action = quote! {
888 debug!("Task {}: Error during process: {}", #mission_mod::TASKS_IDS[#tid], &error);
889 let decision = monitor.process_error(#tid, CuTaskState::Process, &error);
890 match decision {
891 Decision::Abort => {
892 debug!("Process: ABORT decision from monitoring. Task '{}' errored out \
893 during process. Skipping the processing of CL {}.", #mission_mod::TASKS_IDS[#tid], clid);
894 monitor.process_copperlist(&#mission_mod::collect_metadata(&culist))?;
895 cl_manager.end_of_processing(clid);
896 return Ok(()); }
899 Decision::Ignore => {
900 debug!("Process: IGNORE decision from monitoring. Task '{}' errored out \
901 during process. The runtime will continue with a forced empty message.", #mission_mod::TASKS_IDS[#tid]);
902 let cumsg_output = &mut msgs.#output_culist_index;
903 cumsg_output.clear_payload();
904 }
905 Decision::Shutdown => {
906 debug!("Process: SHUTDOWN decision from monitoring. Task '{}' errored out \
907 during process. The runtime cannot continue.", #mission_mod::TASKS_IDS[#tid]);
908 return Err(CuError::new_with_cause("Task errored out during process.", error));
909 }
910 }
911 };
912
913 let call_sim_callback = if sim_mode {
914 let inputs_type = if indices.len() == 1 {
915 quote! { #(&msgs.#indices)* }
917 } else {
918 quote! { (#(&msgs.#indices),*) }
920 };
921
922 quote! {
923 let doit = {
924 let cumsg_input = #inputs_type;
925 let cumsg_output = &mut msgs.#output_culist_index;
926 let state = cu29::simulation::CuTaskCallbackState::Process(cumsg_input, cumsg_output);
927 let ovr = sim_callback(SimStep::#enum_name(state));
928
929 if let cu29::simulation::SimOverride::Errored(reason) = ovr {
930 let error: CuError = reason.into();
931 #monitoring_action
932 false
933 }
934 else {
935 ovr == cu29::simulation::SimOverride::ExecuteByRuntime
936 }
937 };
938 }
939 } else {
940 quote! {
941 let doit = true; }
943 };
944
945 let indices = step.input_msg_indices_types.iter().map(|(index, _)| int2sliceindex(*index));
946 let inputs_type = if indices.len() == 1 {
947 quote! { #(&msgs.#indices)* }
949 } else {
950 quote! { (#(&msgs.#indices),*) }
952 };
953 quote! {
954 {
955 #comment_tokens
956 kf_manager.freeze_task(clid, &#task_instance)?;
958 #call_sim_callback
959 let cumsg_input = #inputs_type;
960 let cumsg_output = &mut msgs.#output_culist_index;
961 cumsg_output.metadata.process_time.start = clock.now().into();
962 let maybe_error = if doit {#task_instance.process(clock, cumsg_input, cumsg_output)} else {Ok(())};
963 cumsg_output.metadata.process_time.end = clock.now().into();
964 if let Err(error) = maybe_error {
965 #monitoring_action
966 }
967 }
968 }
969 } else {
970 panic!("Regular task should have an output message index.");
971 }
972 }
973 };
974
975 process_call
976 }
977 CuExecutionUnit::Loop(_) => todo!("Needs to be implemented"),
978 }
979 }).collect();
980 #[cfg(feature = "macro_debug")]
981 eprintln!("[Culist access order: {taskid_call_order:?}]");
982
983 let all_tasks_member_ids: Vec<String> = all_tasks_ids
985 .iter()
986 .map(|name| utils::config_id_to_struct_member(name.as_str()))
987 .collect();
988
989 #[cfg(feature = "macro_debug")]
990 eprintln!("[build the copperlist support]");
991 let culist_support: proc_macro2::TokenStream =
992 gen_culist_support(&runtime_plan, &taskid_call_order, &all_tasks_member_ids);
993
994 #[cfg(feature = "macro_debug")]
995 eprintln!("[build the sim support]");
996 let sim_support: proc_macro2::TokenStream = gen_sim_support(&runtime_plan);
997
998 let (new, run_one_iteration, start_all_tasks, stop_all_tasks, run) = if sim_mode {
999 (
1000 quote! {
1001 fn new(clock:RobotClock, unified_logger: Arc<Mutex<UnifiedLoggerWrite>>, config_override: Option<CuConfig>, sim_callback: &mut impl FnMut(SimStep) -> cu29::simulation::SimOverride) -> CuResult<Self>
1002 },
1003 quote! {
1004 fn run_one_iteration(&mut self, sim_callback: &mut impl FnMut(SimStep) -> cu29::simulation::SimOverride) -> CuResult<()>
1005 },
1006 quote! {
1007 fn start_all_tasks(&mut self, sim_callback: &mut impl FnMut(SimStep) -> cu29::simulation::SimOverride) -> CuResult<()>
1008 },
1009 quote! {
1010 fn stop_all_tasks(&mut self, sim_callback: &mut impl FnMut(SimStep) -> cu29::simulation::SimOverride) -> CuResult<()>
1011 },
1012 quote! {
1013 fn run(&mut self, sim_callback: &mut impl FnMut(SimStep) -> cu29::simulation::SimOverride) -> CuResult<()>
1014 },
1015 )
1016 } else {
1017 (
1018 quote! {
1019 fn new(clock:RobotClock, unified_logger: Arc<Mutex<UnifiedLoggerWrite>>, config_override: Option<CuConfig>) -> CuResult<Self>
1020 },
1021 quote! {
1022 fn run_one_iteration(&mut self) -> CuResult<()>
1023 },
1024 quote! {
1025 fn start_all_tasks(&mut self) -> CuResult<()>
1026 },
1027 quote! {
1028 fn stop_all_tasks(&mut self) -> CuResult<()>
1029 },
1030 quote! {
1031 fn run(&mut self) -> CuResult<()>
1032 },
1033 )
1034 };
1035
1036 let sim_callback_arg = if sim_mode {
1037 Some(quote!(sim_callback))
1038 } else {
1039 None
1040 };
1041
1042 let sim_callback_on_new_calls = all_tasks_ids.iter().enumerate().map(|(i, id)| {
1043 let enum_name = config_id_to_enum(id);
1044 let enum_ident = Ident::new(&enum_name, Span::call_site());
1045 quote! {
1046 sim_callback(SimStep::#enum_ident(cu29::simulation::CuTaskCallbackState::New(all_instances_configs[#i].cloned())));
1048 }
1049 });
1050
1051 let sim_callback_on_new = if sim_mode {
1052 Some(quote! {
1053 let graph = config.get_graph(Some(#mission)).expect("Could not find the mission #mission");
1054 let all_instances_configs: Vec<Option<&ComponentConfig>> = graph
1055 .get_all_nodes()
1056 .iter()
1057 .map(|(_, node)| node.get_instance_config())
1058 .collect();
1059 #(#sim_callback_on_new_calls)*
1060 })
1061 } else {
1062 None
1063 };
1064
1065 #[cfg(feature = "macro_debug")]
1066 eprintln!("[build the run methods]");
1067 let run_methods = quote! {
1068
1069 #run_one_iteration {
1070
1071 let runtime = &mut self.copper_runtime;
1073 let clock = &runtime.clock;
1074 let monitor = &mut runtime.monitor;
1075 let tasks = &mut runtime.tasks;
1076 let cl_manager = &mut runtime.copperlists_manager;
1077 let kf_manager = &mut runtime.keyframes_manager;
1078
1079 #(#preprocess_calls)*
1081
1082 let culist = cl_manager.inner.create().expect("Ran out of space for copper lists"); let clid = culist.id;
1084 kf_manager.reset(clid, clock); culist.change_state(cu29::copperlist::CopperListState::Processing);
1086 {
1087 let msgs = &mut culist.msgs.0;
1088 #(#runtime_plan_code)*
1089 } monitor.process_copperlist(&#mission_mod::collect_metadata(&culist))?;
1091 cl_manager.end_of_processing(clid);
1092
1093 #(#postprocess_calls)*
1095 Ok(())
1096 }
1097
1098 fn restore_keyframe(&mut self, keyframe: &KeyFrame) -> CuResult<()> {
1099 let runtime = &mut self.copper_runtime;
1100 let clock = &runtime.clock;
1101 let tasks = &mut runtime.tasks;
1102 let config = cu29::bincode::config::standard();
1103 let reader = cu29::bincode::de::read::SliceReader::new(&keyframe.serialized_tasks);
1104 let mut decoder = DecoderImpl::new(reader, config, ());
1105 #(#task_restore_code);*;
1106 Ok(())
1107 }
1108
1109 #start_all_tasks {
1110 #(#start_calls)*
1111 self.copper_runtime.monitor.start(&self.copper_runtime.clock)?;
1112 Ok(())
1113 }
1114
1115 #stop_all_tasks {
1116 #(#stop_calls)*
1117 self.copper_runtime.monitor.stop(&self.copper_runtime.clock)?;
1118 Ok(())
1119 }
1120
1121 #run {
1122 self.start_all_tasks(#sim_callback_arg)?;
1123 let error = loop {
1124 let error = self.run_one_iteration(#sim_callback_arg);
1125 if error.is_err() {
1126 break error;
1127 }
1128 };
1129 debug!("A task errored out: {}", &error);
1130 self.stop_all_tasks(#sim_callback_arg)?;
1131 error
1132 }
1133 };
1134
1135 let tasks_type = if sim_mode {
1136 quote!(CuSimTasks)
1137 } else {
1138 quote!(CuTasks)
1139 };
1140
1141 let tasks_instanciator = if sim_mode {
1142 quote!(tasks_instanciator_sim)
1143 } else {
1144 quote!(tasks_instanciator)
1145 };
1146
1147 let app_impl_decl = if sim_mode {
1148 quote!(impl CuSimApplication for #application_name)
1149 } else {
1150 quote!(impl CuApplication for #application_name)
1151 };
1152 let simstep_type_decl = if sim_mode {
1153 quote!(
1154 type Step<'z> = SimStep<'z>;
1155 )
1156 } else {
1157 quote!()
1158 };
1159
1160 #[cfg(feature = "macro_debug")]
1161 eprintln!("[build result]");
1162 let application_impl = quote! {
1163 #app_impl_decl {
1164 #simstep_type_decl
1165
1166 #new {
1167 let config_filename = #config_file;
1168 let config = if config_override.is_some() {
1169 let overridden_config = config_override.unwrap();
1170 debug!("CuConfig: Overridden programmatically: {}", &overridden_config.serialize_ron());
1171 overridden_config
1172 } else if std::path::Path::new(config_filename).exists() {
1173 debug!("CuConfig: Reading configuration from file: {}", config_filename);
1174 cu29::config::read_configuration(config_filename)?
1175 } else {
1176 let original_config = Self::get_original_config();
1177 debug!("CuConfig: Using the original configuration the project was compiled with: {}", &original_config);
1178 cu29::config::read_configuration_str(original_config, None)?
1179 };
1180
1181 let mut default_section_size = std::mem::size_of::<super::#mission_mod::CuList>() * 64;
1184 if let Some(section_size_mib) = config.logging.as_ref().and_then(|l| l.section_size_mib) {
1186 default_section_size = section_size_mib as usize * 1024usize * 1024usize;
1188 }
1189 let copperlist_stream = stream_write::<#mission_mod::CuList>(
1190 unified_logger.clone(),
1191 UnifiedLogType::CopperList,
1192 default_section_size,
1193 );
1197
1198 let keyframes_stream = stream_write::<KeyFrame>(
1199 unified_logger.clone(),
1200 UnifiedLogType::FrozenTasks,
1201 1024 * 1024 * 10, );
1203
1204 let application = Ok(#application_name {
1205 copper_runtime: CuRuntime::<#mission_mod::#tasks_type, #mission_mod::CuMsgs, #monitor_type, #DEFAULT_CLNB>::new(
1206 clock,
1207 &config,
1208 Some(#mission),
1209 #mission_mod::#tasks_instanciator,
1210 #mission_mod::monitor_instanciator,
1211 copperlist_stream,
1212 keyframes_stream)?, });
1214
1215 #sim_callback_on_new
1216
1217 application
1218 }
1219
1220 fn get_original_config() -> String {
1221 #copper_config_content.to_string()
1222 }
1223
1224 #run_methods
1225 }
1226 };
1227
1228 let (
1229 builder_struct,
1230 builder_new,
1231 builder_impl,
1232 builder_sim_callback_method,
1233 builder_build_sim_callback_arg,
1234 ) = if sim_mode {
1235 (
1236 quote! {
1237 #[allow(dead_code)]
1238 pub struct #builder_name <'a, F> {
1239 clock: Option<RobotClock>,
1240 unified_logger: Option<Arc<Mutex<UnifiedLoggerWrite>>>,
1241 config_override: Option<CuConfig>,
1242 sim_callback: Option<&'a mut F>
1243 }
1244 },
1245 quote! {
1246 #[allow(dead_code)]
1247 pub fn new() -> Self {
1248 Self {
1249 clock: None,
1250 unified_logger: None,
1251 config_override: None,
1252 sim_callback: None,
1253 }
1254 }
1255 },
1256 quote! {
1257 impl<'a, F> #builder_name <'a, F>
1258 where
1259 F: FnMut(SimStep) -> cu29::simulation::SimOverride,
1260 },
1261 Some(quote! {
1262 pub fn with_sim_callback(mut self, sim_callback: &'a mut F) -> Self
1263 {
1264 self.sim_callback = Some(sim_callback);
1265 self
1266 }
1267 }),
1268 Some(quote! {
1269 self.sim_callback
1270 .ok_or(CuError::from("Sim callback missing from builder"))?,
1271 }),
1272 )
1273 } else {
1274 (
1275 quote! {
1276 #[allow(dead_code)]
1277 pub struct #builder_name {
1278 clock: Option<RobotClock>,
1279 unified_logger: Option<Arc<Mutex<UnifiedLoggerWrite>>>,
1280 config_override: Option<CuConfig>,
1281 }
1282 },
1283 quote! {
1284 #[allow(dead_code)]
1285 pub fn new() -> Self {
1286 Self {
1287 clock: None,
1288 unified_logger: None,
1289 config_override: None,
1290 }
1291 }
1292 },
1293 quote! {
1294 impl #builder_name
1295 },
1296 None,
1297 None,
1298 )
1299 };
1300
1301 let application_builder = quote! {
1302 #builder_struct
1303
1304 #builder_impl
1305 {
1306 #builder_new
1307
1308 #[allow(dead_code)]
1309 pub fn with_clock(mut self, clock: RobotClock) -> Self {
1310 self.clock = Some(clock);
1311 self
1312 }
1313
1314 #[allow(dead_code)]
1315 pub fn with_unified_logger(mut self, unified_logger: Arc<Mutex<UnifiedLoggerWrite>>) -> Self {
1316 self.unified_logger = Some(unified_logger);
1317 self
1318 }
1319
1320 #[allow(dead_code)]
1321 pub fn with_context(mut self, copper_ctx: &CopperContext) -> Self {
1322 self.clock = Some(copper_ctx.clock.clone());
1323 self.unified_logger = Some(copper_ctx.unified_logger.clone());
1324 self
1325 }
1326
1327 #[allow(dead_code)]
1328 pub fn with_config(mut self, config_override: CuConfig) -> Self {
1329 self.config_override = Some(config_override);
1330 self
1331 }
1332
1333 #builder_sim_callback_method
1334
1335 #[allow(dead_code)]
1336 pub fn build(self) -> CuResult<#application_name> {
1337 #application_name::new(
1338 self.clock
1339 .ok_or(CuError::from("Clock missing from builder"))?,
1340 self.unified_logger
1341 .ok_or(CuError::from("Unified logger missing from builder"))?,
1342 self.config_override,
1343 #builder_build_sim_callback_arg
1344 )
1345 }
1346 }
1347 };
1348
1349 let mission_mod_tokens = quote! {
1351 mod #mission_mod {
1352 use super::*; use cu29::bincode::Encode;
1355 use cu29::bincode::enc::Encoder;
1356 use cu29::bincode::error::EncodeError;
1357 use cu29::bincode::Decode;
1358 use cu29::bincode::de::Decoder;
1359 use cu29::bincode::de::DecoderImpl;
1360 use cu29::bincode::error::DecodeError;
1361 use cu29::clock::RobotClock;
1362 use cu29::config::CuConfig;
1363 use cu29::config::ComponentConfig;
1364 use cu29::curuntime::CuRuntime;
1365 use cu29::curuntime::KeyFrame;
1366 use cu29::curuntime::CopperContext;
1367 use cu29::CuResult;
1368 use cu29::CuError;
1369 use cu29::cutask::CuSrcTask;
1370 use cu29::cutask::CuSinkTask;
1371 use cu29::cutask::CuTask;
1372 use cu29::cutask::CuMsg;
1373 use cu29::cutask::CuMsgMetadata;
1374 use cu29::copperlist::CopperList;
1375 use cu29::monitoring::CuMonitor; use cu29::monitoring::CuTaskState;
1377 use cu29::monitoring::Decision;
1378 use cu29::prelude::app::CuApplication;
1379 use cu29::prelude::debug;
1380 use cu29::prelude::stream_write;
1381 use cu29::prelude::UnifiedLoggerWrite;
1382 use cu29::prelude::UnifiedLogType;
1383 use std::sync::Arc;
1384 use std::sync::Mutex;
1385
1386 #[allow(unused_imports)]
1388 use cu29::prelude::app::CuSimApplication;
1389
1390 #[allow(unused_imports)]
1392 use cu29::monitoring::NoMonitor;
1393
1394 pub type CuTasks = #task_types_tuple;
1398
1399 #[allow(dead_code)]
1402 pub type CuSimTasks = #task_types_tuple_sim;
1403
1404 pub const TASKS_IDS: &'static [&'static str] = &[#( #all_tasks_ids ),*];
1405
1406 #culist_support
1407
1408 #sim_support
1409
1410 pub fn tasks_instanciator(all_instances_configs: Vec<Option<&ComponentConfig>>) -> CuResult<CuTasks> {
1411 Ok(( #(#task_instances_init_code),*, ))
1412 }
1413
1414 #[allow(dead_code)]
1415 pub fn tasks_instanciator_sim(all_instances_configs: Vec<Option<&ComponentConfig>>) -> CuResult<CuSimTasks> {
1416 Ok(( #(#task_sim_instances_init_code),*, ))
1417 }
1418
1419 pub fn monitor_instanciator(config: &CuConfig) -> #monitor_type {
1420 #monitor_type::new(config, #mission_mod::TASKS_IDS).expect("Failed to create the given monitor.")
1421 }
1422
1423 pub #application_struct
1425
1426 #application_impl
1427
1428 #application_builder
1429 }
1430
1431 };
1432 all_missions_tokens.push(mission_mod_tokens);
1433 }
1434
1435 let default_application_tokens = if all_missions.contains_key("default") {
1436 quote! {
1437 #[allow(unused_imports)]
1439 use default::#builder_name;
1440
1441 #[allow(unused_imports)]
1442 use default::#application_name;
1443 }
1444 } else {
1445 quote! {}
1446 };
1447
1448 let result: proc_macro2::TokenStream = quote! {
1449 #(#all_missions_tokens)*
1450 #default_application_tokens
1451 };
1452
1453 #[cfg(feature = "macro_debug")]
1455 {
1456 let formatted_code = rustfmt_generated_code(result.to_string());
1457 eprintln!("\n === Gen. Runtime ===\n");
1458 eprintln!("{formatted_code}");
1459 eprintln!("\n === === === === === ===\n");
1462 }
1463 result.into()
1464}
1465
1466fn read_config(config_file: &str) -> CuResult<CuConfig> {
1467 let filename = config_full_path(config_file);
1468
1469 read_configuration(filename.as_str())
1470}
1471
1472fn config_full_path(config_file: &str) -> String {
1473 let mut config_full_path = utils::caller_crate_root();
1474 config_full_path.push(config_file);
1475 let filename = config_full_path
1476 .as_os_str()
1477 .to_str()
1478 .expect("Could not interpret the config file name");
1479 filename.to_string()
1480}
1481
1482fn extract_tasks_types(graph: &CuGraph) -> (Vec<String>, Vec<CuTaskType>, Vec<String>, Vec<Type>) {
1484 let all_id_nodes = graph.get_all_nodes();
1485
1486 let all_tasks_ids: Vec<String> = all_id_nodes
1488 .iter()
1489 .map(|(_, node)| node.get_id().to_string())
1490 .collect();
1491
1492 let all_task_cutype: Vec<CuTaskType> = all_id_nodes
1493 .iter()
1494 .map(|(id, _)| find_task_type_for_id(graph, *id))
1495 .collect();
1496
1497 let all_types_names: Vec<String> = all_id_nodes
1499 .iter()
1500 .map(|(_, node)| node.get_type().to_string())
1501 .collect();
1502
1503 let all_types: Vec<Type> = all_types_names
1505 .iter()
1506 .map(|name| {
1507 parse_str(name)
1508 .unwrap_or_else(|_| panic!("Could not transform {name} into a Task Rust type."))
1509 })
1510 .collect();
1511 (all_tasks_ids, all_task_cutype, all_types_names, all_types)
1512}
1513
1514fn extract_msg_types(runtime_plan: &CuExecutionLoop) -> Vec<Type> {
1515 runtime_plan
1516 .steps
1517 .iter()
1518 .filter_map(|unit| match unit {
1519 CuExecutionUnit::Step(step) => {
1520 if let Some((_, output_msg_type)) = &step.output_msg_index_type {
1521 Some(
1522 parse_str::<Type>(output_msg_type.as_str()).unwrap_or_else(|_| {
1523 panic!(
1524 "Could not transform {output_msg_type} into a message Rust type."
1525 )
1526 }),
1527 )
1528 } else {
1529 None
1530 }
1531 }
1532 CuExecutionUnit::Loop(_) => todo!("Needs to be implemented"),
1533 })
1534 .collect()
1535}
1536
1537fn build_culist_tuple(all_msgs_types_in_culist_order: &[Type]) -> TypeTuple {
1539 if all_msgs_types_in_culist_order.is_empty() {
1540 parse_quote! { () }
1541 } else {
1542 parse_quote! {
1543 ( #( CuMsg<#all_msgs_types_in_culist_order> ),* )
1544 }
1545 }
1546}
1547
1548fn build_culist_tuple_encode(all_msgs_types_in_culist_order: &[Type]) -> ItemImpl {
1550 let indices: Vec<usize> = (0..all_msgs_types_in_culist_order.len()).collect();
1551
1552 let encode_fields: Vec<_> = indices
1554 .iter()
1555 .map(|i| {
1556 let idx = syn::Index::from(*i);
1557 quote! { self.0.#idx.encode(encoder)?; }
1558 })
1559 .collect();
1560
1561 parse_quote! {
1562 impl Encode for CuMsgs {
1563 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
1564 #(#encode_fields)*
1565 Ok(())
1566 }
1567 }
1568 }
1569}
1570
1571fn build_culist_tuple_decode(all_msgs_types_in_culist_order: &[Type]) -> ItemImpl {
1573 let indices: Vec<usize> = (0..all_msgs_types_in_culist_order.len()).collect();
1574
1575 let decode_fields: Vec<_> = indices
1577 .iter()
1578 .map(|i| {
1579 let t = &all_msgs_types_in_culist_order[*i];
1580 quote! { CuMsg::<#t>::decode(decoder)? }
1581 })
1582 .collect();
1583
1584 parse_quote! {
1585 impl Decode<()> for CuMsgs {
1586 fn decode<D: Decoder<Context=()>>(decoder: &mut D) -> Result<Self, DecodeError> {
1587 Ok(CuMsgs ((
1588 #(#decode_fields),*
1589 )))
1590 }
1591 }
1592 }
1593}
1594
1595fn build_culist_erasedcumsgs(all_msgs_types_in_culist_order: &[Type]) -> ItemImpl {
1596 let indices: Vec<usize> = (0..all_msgs_types_in_culist_order.len()).collect();
1597 let casted_fields: Vec<_> = indices
1598 .iter()
1599 .map(|i| {
1600 let idx = syn::Index::from(*i);
1601 quote! { &self.0.#idx as &dyn ErasedCuMsg }
1602 })
1603 .collect();
1604 parse_quote! {
1605 impl ErasedCuMsgs for CuMsgs {
1606 fn erased_cumsgs(&self) -> Vec<&dyn ErasedCuMsg> {
1607 vec![
1608 #(#casted_fields),*
1609 ]
1610 }
1611 }
1612 }
1613}
1614
1615fn build_culist_tuple_debug(all_msgs_types_in_culist_order: &[Type]) -> ItemImpl {
1616 let indices: Vec<usize> = (0..all_msgs_types_in_culist_order.len()).collect();
1617
1618 let debug_fields: Vec<_> = indices
1619 .iter()
1620 .map(|i| {
1621 let idx = syn::Index::from(*i);
1622 quote! { .field(&self.0.#idx) }
1623 })
1624 .collect();
1625
1626 parse_quote! {
1627 impl std::fmt::Debug for CuMsgs {
1628 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1629 f.debug_tuple("CuMsgs")
1630 #(#debug_fields)*
1631 .finish()
1632 }
1633 }
1634 }
1635}
1636
1637fn build_culist_tuple_serialize(all_msgs_types_in_culist_order: &[Type]) -> ItemImpl {
1639 let indices: Vec<usize> = (0..all_msgs_types_in_culist_order.len()).collect();
1640 let tuple_len = all_msgs_types_in_culist_order.len();
1641
1642 let serialize_fields: Vec<_> = indices
1644 .iter()
1645 .map(|i| {
1646 let idx = syn::Index::from(*i);
1647 quote! { &self.0.#idx }
1648 })
1649 .collect();
1650
1651 parse_quote! {
1652 impl Serialize for CuMsgs {
1653 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1654 where
1655 S: serde::Serializer,
1656 {
1657 use serde::ser::SerializeTuple;
1658 let mut tuple = serializer.serialize_tuple(#tuple_len)?;
1659 #(tuple.serialize_element(#serialize_fields)?;)*
1660 tuple.end()
1661 }
1662 }
1663 }
1664}
1665
1666#[cfg(test)]
1667mod tests {
1668 #[test]
1670 fn test_compile_fail() {
1671 let t = trybuild::TestCases::new();
1672 t.compile_fail("tests/compile_fail/*/*.rs");
1673 }
1674}