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 pub b9600: u16,
90 pub b19200: u16,
92 pub b31250: u16,
94 pub b45450: u16,
96 pub b93750: u16,
98 pub b187500: u16,
100 pub b500000: u16,
102 pub b1500000: u16,
104 pub b3000000: u16,
106 pub b6000000: u16,
108 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 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 hardware_release: String,
318 pub software_release: String,
319 pub implementation_type: String,
323 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 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 pub available_modules: Vec<Arc<Module>>,
347 pub slots: Vec<Slot>,
348 pub user_prm_data: UserPrmData,
349 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}