1use proc_macro::TokenStream;
4use proc_macro2::{Ident, Span};
5use quote::quote;
6use regex::Regex;
7use std::fs::File;
8use std::io::{BufRead, BufReader};
9use std::path::{Path, PathBuf};
10use std::{env, fs};
11use syn::parse::{Parse, ParseStream};
12use syn::{parse_macro_input, Result};
13
14struct Args {
16 control: syn::Ident,
18 }
21impl Parse for Args {
22 fn parse(input: ParseStream) -> Result<Self> {
24 let model = input.parse()?;
25 Ok(Self { control: model })
28 }
29}
30#[derive(Debug, Default)]
32struct IO {
33 pub name: String,
35 pub size: Option<usize>,
37}
38impl IO {
39 fn new(name: &str, size: Option<&str>) -> Self {
41 Self {
42 name: name.to_string(),
43 size: size.and_then(|s| s.parse().ok()),
44 }
45 }
46 fn var(&self) -> Ident {
48 Ident::new(&self.name, Span::call_site())
49 }
50}
51#[derive(Debug, Default)]
52struct List(Vec<IO>);
53impl List {
54 fn quote(&self) -> proc_macro2::TokenStream {
55 self.0
56 .iter()
57 .fold(proc_macro2::TokenStream::default(), |t, io| {
58 let var = io.var();
59 if let Some(size) = io.size {
60 quote! {
61 #t
62 #var: [0f64;#size],
63 }
64 } else {
65 quote! {
66 #t
67 #var: 0f64,
68 }
69 }
70 })
71 }
72}
73
74fn parse_io(lines: &mut std::io::Lines<BufReader<File>>, io: &str) -> Option<List> {
76 let re = Regex::new(r"_T (?P<name>\w+)(?:\[(?P<size>\d+)\])?").unwrap();
77 match lines.next() {
78 Some(Ok(line)) if line.starts_with("typedef struct") => {
79 println!("| {}:", io);
80 let mut io_data = vec![];
81 while let Some(Ok(line)) = lines.next() {
82 if line.contains(io) {
83 break;
84 } else {
85 if let Some(caps) = re.captures(&line) {
86 let size = caps.name("size").map(|m| m.as_str());
87 println!("| - {:<22}: {:>5}", &caps["name"], size.unwrap_or("1"),);
88 io_data.push(IO::new(&caps["name"], size))
89 }
90 }
91 }
92 Some(List(io_data))
93 }
94 _ => None,
95 }
96}
97
98#[proc_macro]
106pub fn import(input: TokenStream) -> TokenStream {
107 let Args { control } = parse_macro_input!(input);
108
109 let sys = Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap()).join("sys");
110 let mut file_name = PathBuf::new();
111 if let Ok(entries) = fs::read_dir(sys) {
112 for entry in entries {
113 if let Ok(entry) = entry {
114 file_name = entry.path();
115 if let Some(extension) = file_name.extension() {
116 match extension.to_str() {
117 Some("h") => {
118 if file_name
119 .to_str()
120 .filter(|f| {
121 !(f.ends_with("rtwtypes.h")
122 || f.ends_with("rt_defines.h")
123 || f.ends_with("_private.h")
124 || f.ends_with("_types.h"))
125 })
126 .is_some()
127 {
128 break;
129 }
130 }
131 _ => (),
132 }
133 }
134 }
135 }
136 }
137 let file = File::open(&file_name).expect(&format!("file {:?} not found", file_name));
138 let reader = BufReader::new(file);
139 let mut lines = reader.lines();
140
141 let model = loop {
142 if let Some(Ok(line)) = lines.next() {
143 if line.contains("File:") {
144 let regex = regex::Regex::new(r"File:\s*(\w+)\.h").unwrap();
145 if let Some(captures) = regex.captures(&line) {
146 let name = captures.get(1).unwrap().as_str();
147 break Ident::new(name, Span::call_site());
148 }
149 }
150 }
151 };
152 println!("Parsing Simulink model {}:", model);
153
154 let mut model_inputs = List::default();
155 let mut model_outputs = List::default();
156 let mut model_states = List::default();
157 while let Some(Ok(line)) = lines.next() {
158 if line.contains("External inputs") {
159 if let Some(io) = parse_io(&mut lines, "ExtU") {
160 model_inputs = io;
161 }
162 }
163 if line.contains("External outputs") {
164 if let Some(io) = parse_io(&mut lines, "ExtY") {
165 model_outputs = io;
166 }
167 }
168 if line.contains("Block states") {
169 if let Some(io) = parse_io(&mut lines, "DW") {
170 model_states = io;
171 }
172 }
173 }
174
175 let var_u = model_inputs.quote();
176 let var_y = model_outputs.quote();
177 let var_s = model_states.quote();
178
179 let code = quote! {
180 include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
181
182 pub type #control = #model;
183
184 paste::paste!{
185 #[derive(Debug, Clone, Copy, Default)]
187 pub struct #model {
188 pub inputs: [<ExtU_ #model _T>],
190 pub outputs: [<ExtY_ #model _T>],
192 states: [<DW_ #model _T>],
193 }
194 impl Default for [<ExtU_ #model _T>] {
195 fn default() -> Self {
196 Self { #var_u }
197 }
198 }
199 impl Default for [<ExtY_ #model _T>] {
200 fn default() -> Self {
201 Self { #var_y }
202 }
203 }
204 impl Default for [<DW_ #model _T>] {
205 fn default() -> Self {
206 Self { #var_s }
207 }
208 }
209 impl #model {
210 pub fn new() -> Self {
212 let mut this: Self = Default::default();
213 let mut data: [<RT_MODEL_ #model _T>] = [<tag_RTM_ #model _T>] {
214 dwork: &mut this.states as *mut _,
215 };
216 unsafe {
217 [< #model _initialize>](
218 &mut data as *mut _,
219 &mut this.inputs as *mut _,
220 &mut this.outputs as *mut _,
221 )
222 }
223 this
224 }
225 pub fn step(&mut self) {
227 let mut data: [<RT_MODEL_ #model _T>] = [<tag_RTM_ #model _T>] {
228 dwork: &mut self.states as *mut _,
229 };
230 unsafe {
231 [<#model _step>](
232 &mut data as *mut _,
233 &mut self.inputs as *mut _,
234 &mut self.outputs as *mut _,
235 )
236 }
237 }
238 } }
239 };
240 code.into()
241}