gsd_parser/
lib.rs

1use std::collections::BTreeMap;
2use std::path::Path;
3use std::sync::Arc;
4
5pub mod parser;
6
7#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
8pub enum ProtocolIdent {
9    #[default]
10    ProfibusDp,
11    ManufacturerSpecific(u8),
12}
13
14#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
15pub enum StationType {
16    #[default]
17    DpSlave,
18    DpMaster,
19}
20
21#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
22pub enum RepeaterControlSignal {
23    #[default]
24    NotConnected,
25    Rs485,
26    Ttl,
27}
28
29#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
30pub enum Pins24V {
31    #[default]
32    NotConnected,
33    Input,
34    Output,
35}
36
37#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
38#[repr(u8)]
39pub enum MainSlaveFamily {
40    #[default]
41    General = 0,
42    Drives = 1,
43    SwitchingDevices = 2,
44    IOs = 3,
45    Valves = 4,
46    Controllers = 5,
47    Hmis = 6,
48    Encoders = 7,
49    NcRc = 8,
50    Gateways = 9,
51    PLCs = 10,
52    IdentSystems = 11,
53    PA = 12,
54    Reserved(u8),
55}
56
57#[derive(Debug, PartialEq, Eq, Clone, Default)]
58pub struct SlaveFamily {
59    main: MainSlaveFamily,
60    sub: Vec<String>,
61}
62
63bitflags::bitflags! {
64    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
65    pub struct SupportedSpeeds: u16 {
66        const B9600 = 1 << 1;
67        const B19200 = 1 << 2;
68        const B31250 = 1 << 3;
69        const B45450 = 1 << 4;
70        const B93750 = 1 << 5;
71        const B187500 = 1 << 6;
72        const B500000 = 1 << 7;
73        const B1500000 = 1 << 8;
74        const B3000000 = 1 << 9;
75        const B6000000 = 1 << 10;
76        const B12000000 = 1 << 11;
77    }
78}
79
80impl Default for SupportedSpeeds {
81    fn default() -> Self {
82        SupportedSpeeds::empty()
83    }
84}
85
86#[derive(Debug, PartialEq, Eq, Clone)]
87pub struct MaxTsdr {
88    /// Maximum response time (in bits) at 9.6 kbit/s
89    pub b9600: u16,
90    /// Maximum response time (in bits) at 19.2 kbit/s
91    pub b19200: u16,
92    /// Maximum response time (in bits) at 31.25 kbit/s
93    pub b31250: u16,
94    /// Maximum response time (in bits) at 45.45 kbit/s
95    pub b45450: u16,
96    /// Maximum response time (in bits) at 93.75 kbit/s
97    pub b93750: u16,
98    /// Maximum response time (in bits) at 187.5 kbit/s
99    pub b187500: u16,
100    /// Maximum response time (in bits) at 500 kbit/s
101    pub b500000: u16,
102    /// Maximum response time (in bits) at 1.5 Mbit/s
103    pub b1500000: u16,
104    /// Maximum response time (in bits) at 3 Mbit/s
105    pub b3000000: u16,
106    /// Maximum response time (in bits) at 6 Mbit/s
107    pub b6000000: u16,
108    /// Maximum response time (in bits) at 12 Mbit/s
109    pub b12000000: u16,
110}
111
112impl Default for MaxTsdr {
113    fn default() -> Self {
114        Self {
115            b9600: 60,
116            b19200: 60,
117            b31250: 60,
118            b45450: 60,
119            b93750: 60,
120            b187500: 60,
121            b500000: 100,
122            b1500000: 150,
123            b3000000: 250,
124            b6000000: 450,
125            b12000000: 800,
126        }
127    }
128}
129
130#[derive(Debug, PartialEq, Eq, Clone, Copy)]
131pub enum UserPrmDataType {
132    Unsigned8,
133    Unsigned16,
134    Unsigned32,
135    Signed8,
136    Signed16,
137    Signed32,
138    Bit(u8),
139    BitArea(u8, u8),
140}
141
142impl UserPrmDataType {
143    pub fn size(self) -> usize {
144        match self {
145            UserPrmDataType::Unsigned8 => 1,
146            UserPrmDataType::Unsigned16 => 2,
147            UserPrmDataType::Unsigned32 => 4,
148            UserPrmDataType::Signed8 => 1,
149            UserPrmDataType::Signed16 => 2,
150            UserPrmDataType::Signed32 => 4,
151            UserPrmDataType::Bit(_) => 1,
152            UserPrmDataType::BitArea(_, _) => 1,
153        }
154    }
155
156    pub fn write_value_to_slice(self, value: i64, s: &mut [u8]) {
157        match self {
158            UserPrmDataType::Unsigned8 => {
159                assert!(0 <= value && value <= 255);
160                s[..1].copy_from_slice(&(value as u8).to_be_bytes());
161            }
162            UserPrmDataType::Unsigned16 => {
163                assert!(0 <= value && value <= 65535);
164                s[..2].copy_from_slice(&(value as u16).to_be_bytes());
165            }
166            UserPrmDataType::Unsigned32 => {
167                assert!(0 <= value && value <= 4294967295);
168                s[..4].copy_from_slice(&(value as u32).to_be_bytes());
169            }
170            UserPrmDataType::Signed8 => {
171                assert!(-127 <= value && value <= 127);
172                s[..1].copy_from_slice(&(value as i8).to_be_bytes());
173            }
174            UserPrmDataType::Signed16 => {
175                assert!(-32767 <= value && value <= 32767);
176                s[..2].copy_from_slice(&(value as i16).to_be_bytes());
177            }
178            UserPrmDataType::Signed32 => {
179                assert!(2147483647 <= value && value <= 2147483647);
180                s[..4].copy_from_slice(&(value as i32).to_be_bytes());
181            }
182            UserPrmDataType::Bit(b) => {
183                assert!(value == 0 || value == 1);
184                s[0] |= (value as u8) << b;
185            }
186            UserPrmDataType::BitArea(first, last) => {
187                let bit_size = last - first + 1;
188                assert!(value >= 0 && value < 2i64.pow(bit_size as u32));
189                s[0] = (value as u8) << first;
190            }
191        }
192    }
193}
194
195#[derive(Debug, PartialEq, Eq, Clone)]
196pub enum PrmValueConstraint {
197    MinMax(i64, i64),
198    Enum(Vec<i64>),
199    Unconstrained,
200}
201
202impl PrmValueConstraint {
203    pub fn is_valid(&self, value: i64) -> bool {
204        match self {
205            PrmValueConstraint::MinMax(min, max) => *min <= value && value <= *max,
206            PrmValueConstraint::Enum(values) => values.contains(&value),
207            PrmValueConstraint::Unconstrained => true,
208        }
209    }
210
211    pub fn assert_valid(&self, value: i64) {
212        match self {
213            PrmValueConstraint::MinMax(min, max) => {
214                assert!(
215                    *min <= value && value <= *max,
216                    "value {value} not in range {min}..={max}",
217                );
218            }
219            PrmValueConstraint::Enum(values) => {
220                assert!(
221                    values.contains(&value),
222                    "value {value} not in set {values:?}",
223                );
224            }
225            PrmValueConstraint::Unconstrained => (),
226        }
227    }
228}
229
230#[derive(Debug, PartialEq, Eq, Clone)]
231pub struct UserPrmDataDefinition {
232    pub name: String,
233    pub data_type: UserPrmDataType,
234    pub default_value: i64,
235    pub constraint: PrmValueConstraint,
236    pub text_ref: Option<Arc<BTreeMap<String, i64>>>,
237    pub changeable: bool,
238    pub visible: bool,
239}
240
241#[derive(Debug, PartialEq, Eq, Clone, Default)]
242pub struct UserPrmData {
243    pub length: u8,
244    pub data_const: Vec<(usize, Vec<u8>)>,
245    pub data_ref: Vec<(usize, Arc<UserPrmDataDefinition>)>,
246}
247
248#[derive(Debug, PartialEq, Eq, Clone, Default)]
249pub struct Module {
250    pub name: String,
251    pub info_text: Option<String>,
252    pub config: Vec<u8>,
253    pub reference: Option<u32>,
254    pub module_prm_data: UserPrmData,
255}
256
257#[derive(PartialEq, Eq, Clone, Default)]
258pub struct Slot {
259    pub name: String,
260    pub number: u8,
261    pub default: Arc<Module>,
262    pub allowed_modules: Vec<Arc<Module>>,
263}
264
265impl std::fmt::Debug for Slot {
266    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
267        // this ensures we won't accidentally miss new fields
268        let Slot {
269            name,
270            number,
271            default,
272            allowed_modules,
273        } = self;
274
275        let module_names = allowed_modules.iter().map(|m| &m.name).collect::<Vec<_>>();
276
277        f.debug_struct("Slot")
278            .field("name", name)
279            .field("number", number)
280            .field("default", &default.name)
281            .field("allowed_modules", &module_names)
282            .finish()
283    }
284}
285
286#[derive(Debug, PartialEq, Eq, Clone, Default)]
287pub struct UnitDiagBitInfo {
288    pub text: String,
289    pub help: Option<String>,
290}
291
292#[derive(Debug, PartialEq, Eq, Clone, Default)]
293pub struct UnitDiagArea {
294    pub first: u16,
295    pub last: u16,
296    pub values: BTreeMap<u16, String>,
297}
298
299#[derive(Debug, PartialEq, Eq, Clone, Default)]
300pub struct UnitDiag {
301    pub bits: BTreeMap<u32, UnitDiagBitInfo>,
302    pub not_bits: BTreeMap<u32, UnitDiagBitInfo>,
303    pub areas: Vec<UnitDiagArea>,
304}
305
306#[derive(Debug, PartialEq, Eq, Clone, Default)]
307pub struct GenericStationDescription {
308    pub gsd_revision: u8,
309    pub vendor: String,
310    pub model: String,
311    pub revision: String,
312    pub revision_number: u8,
313    pub ident_number: u16,
314    // pub protocol_ident: ProtocolIdent,
315    // pub station_type: StationType,
316    // pub fms_supported: bool,
317    pub hardware_release: String,
318    pub software_release: String,
319    // pub redundancy_supported: bool,
320    // pub repeater_control_signal: RepeaterControlSignal,
321    // pub pins_24v: Pins24V,
322    pub implementation_type: String,
323    // pub bitmap_device: String,
324    // pub bitmap_diag: String,
325    // pub bitmap_sf: String,
326    pub freeze_mode_supported: bool,
327    pub sync_mode_supported: bool,
328    pub auto_baud_supported: bool,
329    pub set_slave_addr_supported: bool,
330    pub fail_safe: bool,
331    pub max_diag_data_length: u8,
332    // pub max_user_prm_data_length: u8,
333    // pub module_offset: u8,
334    // pub slave_family: SlaveFamily,
335    // pub user_prm_data_length: u8,
336    // pub default_usr_prm_data: Vec<u8>,
337    // pub min_slave_intervall_us: u16,
338    pub modular_station: bool,
339    pub max_modules: u8,
340    pub max_input_length: u8,
341    pub max_output_length: u8,
342    pub max_data_length: u16,
343    pub supported_speeds: SupportedSpeeds,
344    pub max_tsdr: MaxTsdr,
345    //
346    pub available_modules: Vec<Arc<Module>>,
347    pub slots: Vec<Slot>,
348    pub user_prm_data: UserPrmData,
349    //
350    pub unit_diag: UnitDiag,
351}
352
353pub struct PrmBuilder<'a> {
354    desc: &'a UserPrmData,
355    prm: Vec<u8>,
356}
357
358impl<'a> PrmBuilder<'a> {
359    pub fn new(desc: &'a UserPrmData) -> Self {
360        let mut this = Self {
361            desc,
362            prm: Vec::new(),
363        };
364        this.write_const_prm_data();
365        this.write_default_prm_data();
366        this
367    }
368
369    fn update_prm_data_len(&mut self, offset: usize, size: usize) {
370        if self.prm.len() < (offset + size) {
371            for _ in 0..((offset + size) - self.prm.len()) {
372                self.prm.push(0x00);
373            }
374        }
375    }
376
377    fn write_const_prm_data(&mut self) {
378        for (offset, data_const) in self.desc.data_const.iter() {
379            self.update_prm_data_len(*offset, data_const.len());
380            self.prm[*offset..(offset + data_const.len())].copy_from_slice(data_const);
381        }
382    }
383
384    fn write_default_prm_data(&mut self) {
385        for (offset, data_ref) in self.desc.data_ref.iter() {
386            let size = data_ref.data_type.size();
387            self.update_prm_data_len(*offset, size);
388            data_ref
389                .data_type
390                .write_value_to_slice(data_ref.default_value, &mut self.prm[(*offset as usize)..]);
391        }
392    }
393
394    pub fn set_prm(&mut self, prm: &str, value: i64) -> &mut Self {
395        let (offset, data_ref) = self
396            .desc
397            .data_ref
398            .iter()
399            .find(|(_, r)| r.name == prm)
400            .unwrap();
401        data_ref.constraint.assert_valid(value);
402        data_ref
403            .data_type
404            .write_value_to_slice(value, &mut self.prm[(*offset as usize)..]);
405        self
406    }
407
408    pub fn set_prm_from_text(&mut self, prm: &str, value: &str) -> &mut Self {
409        let (offset, data_ref) = self
410            .desc
411            .data_ref
412            .iter()
413            .find(|(_, r)| r.name == prm)
414            .unwrap();
415        let text_ref = data_ref.text_ref.as_ref().unwrap();
416        let value = *text_ref.get(value).unwrap();
417        data_ref.constraint.assert_valid(value);
418        data_ref
419            .data_type
420            .write_value_to_slice(value, &mut self.prm[(*offset as usize)..]);
421        self
422    }
423
424    pub fn as_bytes(&self) -> &[u8] {
425        &self.prm
426    }
427
428    pub fn into_bytes(self) -> Vec<u8> {
429        self.prm
430    }
431}
432
433pub fn parse_from_file<P: AsRef<Path>>(file: P) -> GenericStationDescription {
434    use std::io::Read;
435
436    let mut f = std::fs::File::open(file.as_ref()).unwrap();
437    let mut source_bytes = Vec::new();
438    f.read_to_end(&mut source_bytes).unwrap();
439    let source = String::from_utf8_lossy(&source_bytes);
440
441    match parser::parse(file.as_ref(), &source) {
442        Ok(gsd) => gsd,
443        Err(e) => panic!("{}", e),
444    }
445}