ygw_macros/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::{format_ident, quote};
5use syn::{
6    parse::Parser, parse_macro_input, spanned::Spanned, Field, ItemStruct, LitBool, LitStr, Result,
7};
8
9/// parameter_group is a macro that can be used for structs to allow their content to be used as Yamcs parameters
10///
11/// The macro adds a `_meta` member which stores extra information about each parameter as well as a sequence count 
12/// incremented each time data is delivered to Yamcs
13/// 
14/// To create a value of the struct the meta has to be initialized using the `StructNameMeta::new()` method. This method will allocate numeric identifier for parameters which are then used to communicate the values to/from Yamcs.
15/// 
16/// In addition, several helper methods are provided:
17/// * `send_definitions` - sends the parameter definitions to Yamcs. This has to be called at the beginning, before sending any other data. 
18/// * `set_<fieldname>` - can be used to set values of the fields together with the timestamp. This method will set the modified flag of the respective field and that flag is used by the `send_modified_values` function. The fields can also be assigned directly and the send_values method can then be used to send the values to Yamcs.
19/// * `send_values` - sends all the values to Yamcs irrespective of their modified status.
20/// * `send_modified_values` - sends all the modified values to Yamcs.
21
22#[proc_macro_attribute]
23pub fn parameter_group(_attr: TokenStream, input: TokenStream) -> TokenStream {
24    let item_struct = parse_macro_input!(input as ItemStruct);
25
26    match parameter_pool_impl(item_struct) {
27        Ok(v) => v,
28        Err(err) => err.to_compile_error().into(),
29    }
30}
31
32fn parameter_pool_impl(mut item_struct: ItemStruct) -> Result<TokenStream> {
33    let struct_name = &item_struct.ident;
34
35    let mut meta_fields = Vec::new();
36
37    //meta_fields.push(quote!({start_id: u32}));
38
39    let meta_name = format_ident!("{}Meta", struct_name);
40
41    let mut setters = Vec::new();
42    let mut idgetters = Vec::new();
43    let mut meta_inits = Vec::new();
44    let mut param_defs = Vec::new();
45    let mut param_mod_values = Vec::new();
46    let mut param_values = Vec::new();
47
48    let mut field_id = 0;
49    let num_fields;
50
51    if let syn::Fields::Named(ref mut fields) = item_struct.fields {
52        num_fields = fields.named.len() as u32;
53        for f in fields.named.iter_mut() {
54            let (extra_meta_fields, init, setter, getid) = get_field_methods(field_id, f)?;
55            meta_fields.extend(extra_meta_fields);
56            meta_inits.push(init);
57            setters.push(setter);
58            idgetters.push(getid);
59
60            param_defs.push(get_para_def(field_id, &f)?);
61            param_mod_values.push(get_para_mod_value(field_id, &f)?);
62            param_values.push(get_para_value(field_id, &f)?);
63
64            f.attrs.clear();
65            field_id += 1;
66        }
67        fields.named.push(
68            syn::Field::parse_named
69                .parse2(quote!( _meta: #meta_name))
70                .unwrap(),
71        );
72    } else {
73        return Err(syn::Error::new_spanned(
74            &item_struct,
75            "parameter_pool cannot be used with tuple or unit structs",
76        ));
77    }
78
79    let struct_name_str = format!("{}", struct_name);
80
81    let r = quote! {
82        #item_struct
83
84
85        impl #struct_name {
86            #(#setters)*
87
88            #(#idgetters)*
89
90            fn get_definitions(&self) -> ygw::protobuf::ygw::ParameterDefinitionList {
91                let mut definitions = Vec::new();
92                #(#param_defs)*
93
94                return ygw::protobuf::ygw::ParameterDefinitionList{ definitions };
95            }
96
97            fn get_modified_values(&mut self) -> ygw::protobuf::ygw::ParameterData {
98                let mut parameters = Vec::new();
99                #(#param_mod_values)*
100
101                ygw::protobuf::ygw::ParameterData {
102                    parameters,
103                    group: #struct_name_str.to_string(),
104                    seq_num: self._meta.next_seq_count(),
105                    generation_time: None,
106                    acquisition_time: None,
107                }
108            }
109
110            fn get_values(&mut self, gentime: ygw::protobuf::ygw::Timestamp) -> ygw::protobuf::ygw::ParameterData {
111                let mut parameters = Vec::new();
112                #(#param_values)*
113
114                ygw::protobuf::ygw::ParameterData {
115                    parameters,
116                    group: #struct_name_str.to_string(),
117                    seq_num: self._meta.next_seq_count(),
118                    generation_time: Some(gentime),
119                    acquisition_time: None,
120                }
121            }
122
123            /// sends the definitions
124            pub async fn send_definitions(&mut self, tx: &Sender<YgwMessage>) -> ygw::Result<()> {
125                tx.send(ygw::msg::YgwMessage::ParameterDefinitions(self._meta.addr, self.get_definitions()))
126                   .await.map_err(|_| YgwError::ServerShutdown)
127            }
128
129            /// send the values with the current (now) timestamp
130            pub async fn send_values(&mut self, tx: &Sender<YgwMessage>) -> ygw::Result<()> {
131                 tx.send(ygw::msg::YgwMessage::ParameterData(self._meta.addr, self.get_values(ygw::protobuf::now())))
132                    .await.map_err(|_| YgwError::ServerShutdown)
133            }
134
135            /// send the modified values and clear the modified flag
136            pub async fn send_modified_values(&mut self, tx: &Sender<YgwMessage>) -> ygw::Result<()> {
137                tx.send(ygw::msg::YgwMessage::ParameterData(self._meta.addr, self.get_modified_values()))
138                   .await.map_err(|_| YgwError::ServerShutdown)
139           }
140
141        }
142        struct #meta_name {
143            start_id: u32,
144            seq_count: u32,
145            addr: ygw::msg::Addr,
146            #(#meta_fields,)*
147        }
148
149        impl #meta_name {
150            fn new(addr:ygw::msg::Addr, gentime: ygw::protobuf::ygw::Timestamp) -> Self {
151                Self {
152                    addr,
153                    start_id: ygw::generate_pids(#num_fields),
154                    seq_count: 0,
155                    #(#meta_inits)*
156                }
157            }
158            fn next_seq_count(&mut self) -> u32 {
159                let c = self.seq_count;
160                self.seq_count = c + 1;
161                c
162            }
163        }
164    }
165    .into();
166
167    Ok(r)
168}
169
170fn get_para_def(id: u32, field: &Field) -> Result<proc_macro2::TokenStream> {
171    let name_string = format!("{}", field.ident.as_ref().unwrap());
172    let mut relative_name = quote! {#name_string.to_string()};
173    let mut description = quote! { None };
174    let mut unit = quote! { None };
175    let mut writable = quote! { None };
176
177    for attr in &field.attrs {
178        if attr.path().is_ident("mdb") {
179            attr.parse_nested_meta(|meta| {
180                if meta.path.is_ident("relative_name") {
181                    let value = meta.value()?;
182                    let s: LitStr = value.parse()?;
183                    relative_name = quote! {#s.to_string()};
184                } else if meta.path.is_ident("description") {
185                    let value = meta.value()?;
186                    let s: LitStr = value.parse()?;
187                    description = quote! {Some(#s.to_string())};
188                } else if meta.path.is_ident("unit") {
189                    let value = meta.value()?;
190                    let s: LitStr = value.parse()?;
191                    unit = quote! {Some(#s.to_string())};
192                } else if meta.path.is_ident("writable") {
193                    let value = meta.value()?;
194                    let s: LitBool = value.parse()?;
195                    writable = quote! {Some(#s)};
196                }
197                Ok(())
198            })?;
199        };
200    }
201    let types = map_type(&field.ty)?;
202
203    Ok(quote! {
204        definitions.push(ParameterDefinition {
205            relative_name: #relative_name,
206            description: #description,
207            unit: #unit,
208            writable: #writable,
209            ptype: #types.to_string(),
210            id: self._meta.start_id + #id
211        });
212    })
213}
214
215fn get_para_value(id: u32, field: &Field) -> Result<proc_macro2::TokenStream> {
216    let name = field.ident.as_ref().unwrap();
217
218    Ok(quote! {
219        parameters.push(ygw::protobuf::get_pv_eng(self._meta.start_id + #id, None, self.#name));
220    })
221}
222
223fn get_para_mod_value(id: u32, field: &Field) -> Result<proc_macro2::TokenStream> {
224    let name = field.ident.as_ref().unwrap();
225    let gentime_name = format_ident!("{}_gentime", name);
226    let modified_name = format_ident!("{}_modified", name);
227
228    Ok(quote! {
229        if self._meta.#modified_name {
230            parameters.push(ygw::protobuf::get_pv_eng(self._meta.start_id + #id, Some(self._meta.#gentime_name.clone()), self.#name));
231            self._meta.#modified_name = false;
232        }
233    })
234}
235
236fn get_field_methods(
237    id: u32,
238    field: &Field,
239) -> Result<(
240    Vec<Field>,
241    proc_macro2::TokenStream,
242    proc_macro2::TokenStream,
243    proc_macro2::TokenStream,
244)> {
245    let name = field.ident.as_ref().unwrap();
246    let ty = &field.ty;
247    let setter_name = format_ident!("set_{}", name);
248    let id_name = format_ident!("id_{}", name);
249
250    let gentime_name = format_ident!("{}_gentime", name);
251    let modified_name = format_ident!("{}_modified", name);
252
253    let gentime_field = syn::Field::parse_named
254        .parse2(quote! { #gentime_name: ygw::protobuf::ygw::Timestamp })
255        .unwrap();
256    let modified_field = syn::Field::parse_named
257        .parse2(quote! { #modified_name: bool })
258        .unwrap();
259
260    let setter = quote! {
261        pub fn #setter_name(&mut self, #name: #ty, gentime: ygw::protobuf::ygw::Timestamp) {
262        self.#name = #name;
263        self._meta.#gentime_name = gentime;
264        self._meta.#modified_name = true;
265    }};
266
267    let getid = quote! {
268        pub fn #id_name(&self) -> u32{
269            self._meta.start_id + #id
270    }};
271
272    let init = quote! {
273        #gentime_name: gentime.clone(),
274        #modified_name: false,
275    };
276
277    Ok((vec![gentime_field, modified_field], init, setter, getid))
278}
279
280fn map_type(ty: &syn::Type) -> Result<String> {
281    match ty {
282        syn::Type::Path(typepath) if typepath.qself.is_none() => {
283            let type_ident = &typepath.path.segments.first().unwrap().ident;
284
285            match type_ident.to_string().as_ref() {
286                "u32" => Ok("uint32".to_string()),
287                "i32" => Ok("sint32".to_string()),
288                "u64" => Ok("uint64".to_string()),
289                "i64" => Ok("sint64".to_string()),
290                "f32" => Ok("float".to_string()),
291                "f64" => Ok("double".to_string()),
292                "bool" => Ok("boolean".to_string()),
293                "String" => Ok("string".to_string()),
294                _ => Err(syn::Error::new(type_ident.span(), "Invalid Type")),
295            }
296        }
297        _ => Err(syn::Error::new(ty.span(), "Not a simple type")),
298    }
299}