1#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum AddressSpace {
13 Coil,
14 DiscreteInput,
15 InputRegister,
16 HoldingRegister,
17}
18
19#[derive(Clone, Copy, Debug, PartialEq, Eq)]
21pub enum StorageType {
22 Unspecified,
25 Bit,
26 U16,
27 S16,
28 U24,
29 U32,
30 S32,
31 U48,
32 S48,
33 U64,
34 S64,
35 F32,
36 F64,
37 StringAscii,
38 StringUtf8,
39 BytesRaw,
40 Bcd,
41 Composed,
42}
43
44impl StorageType {
45 pub const fn bits(self, words: usize) -> u32 {
46 match self {
47 StorageType::Bit => 1,
48 StorageType::U16 | StorageType::S16 => 16,
49 StorageType::U24 => 24,
50 StorageType::U32 | StorageType::S32 => 32,
51 StorageType::U48 | StorageType::S48 => 48,
52 StorageType::U64 | StorageType::S64 => 64,
53 _ => {
54 let w = if words == 0 { 1 } else { words };
55 if w * 16 > 64 {
56 64
57 } else {
58 (w * 16) as u32
59 }
60 }
61 }
62 }
63
64 pub const fn signed(self) -> bool {
65 matches!(
66 self,
67 StorageType::S16 | StorageType::S32 | StorageType::S48 | StorageType::S64
68 )
69 }
70
71 pub const fn default_words(self) -> usize {
73 match self {
74 StorageType::U32 | StorageType::S32 | StorageType::F32 | StorageType::U24 => 2,
75 StorageType::U48 | StorageType::S48 => 3,
76 StorageType::U64 | StorageType::S64 | StorageType::F64 => 4,
77 _ => 1,
78 }
79 }
80}
81
82#[derive(Clone, Copy, Debug, PartialEq, Eq)]
84pub enum Access {
85 ReadOnly,
86 WriteOnly,
87 ReadWrite,
88 Command,
89}
90
91#[derive(Clone, Copy, Debug, PartialEq, Eq)]
93pub struct Rational {
94 pub num: i64,
95 pub den: i64,
96}
97
98#[derive(Clone, Copy, Debug, PartialEq, Eq)]
100pub enum ScaleMode {
101 Pow10,
102 Multiply,
103}
104
105#[derive(Clone, Copy, Debug)]
106pub struct ScaleRefDesc<'a> {
107 pub point_id: &'a str,
108 pub mode: ScaleMode,
109 pub denominator: i64,
111}
112
113#[derive(Clone, Copy, Debug)]
115pub struct SelectorCaseDesc {
116 pub key: i64,
117 pub scale: Option<Rational>,
118 pub offset: Option<Rational>,
119}
120
121#[derive(Clone, Copy, Debug)]
122pub struct SelectorDesc<'a> {
123 pub point_id: &'a str,
124 pub cases: &'a [SelectorCaseDesc],
125}
126
127#[derive(Clone, Copy, Debug)]
129pub struct NaDesc<'a> {
130 pub raw: i64,
131 pub meaning: &'a str,
132}
133
134#[derive(Clone, Copy, Debug)]
136pub struct FieldDesc<'a> {
137 pub id: &'a str,
138 pub bit_offset: u32,
139 pub bit_length: u32,
140}
141
142#[derive(Clone, Copy, Debug, PartialEq, Eq)]
144pub enum DateTimeEncoding {
145 EpochSeconds,
146 EpochMillis,
147}
148
149#[derive(Clone, Copy, Debug, PartialEq, Eq)]
151pub enum StringPadding {
152 None,
153 Null,
154 Space,
155}
156
157#[derive(Clone, Copy, Debug, PartialEq, Eq)]
158pub enum StringTermination {
159 FixedLength,
160 NullTerminated,
161}
162
163#[derive(Clone, Copy, Debug)]
165pub enum ValueKind<'a> {
166 Decimal,
168 Bool,
169 Uint,
171 Int,
173 Enum,
175 Flags(&'a [(u8, &'a str)]),
177 Fields(&'a [FieldDesc<'a>]),
179 Str {
180 padding: StringPadding,
181 termination: StringTermination,
182 },
183 Bytes,
184 DateTime(DateTimeEncoding),
185 Composed {
187 base: i64,
188 mantissa: ComposedSub,
189 exponent: ComposedSub,
190 },
191}
192
193#[derive(Clone, Copy, Debug, PartialEq, Eq)]
197pub struct ComposedSub {
198 pub offset: u16,
199 pub words: u8,
200 pub bit_offset: u8,
202 pub bit_length: u8,
204 pub width_bits: u8,
206 pub signed: bool,
209}
210
211#[derive(Clone, Copy, Debug, Default)]
213pub struct WriteDesc<'a> {
214 pub min: Option<Rational>,
215 pub max: Option<Rational>,
216 pub step: Option<Rational>,
217 pub allowed: &'a [i64],
218}
219
220#[derive(Clone, Copy, Debug)]
222pub struct PointDesc<'a> {
223 pub id: &'a str,
224 pub space: AddressSpace,
225 pub offset: u16,
226 pub model_relative_offset: u16,
229 pub length_words: u16,
230 pub storage: StorageType,
231 pub value: ValueKind<'a>,
232 pub byte_big: bool,
234 pub word_big: bool,
236 pub scale: Option<Rational>,
237 pub offset_add: Option<Rational>,
238 pub scale_ref: Option<ScaleRefDesc<'a>>,
239 pub selector: Option<SelectorDesc<'a>>,
240 pub na: &'a [NaDesc<'a>],
241 pub access: Access,
242 pub write: Option<WriteDesc<'a>>,
243}
244
245impl<'a> PointDesc<'a> {
246 pub const fn words(&self) -> usize {
248 if self.length_words > 0 {
249 self.length_words as usize
250 } else {
251 self.storage.default_words()
252 }
253 }
254
255 pub const fn readable(&self) -> bool {
256 matches!(self.access, Access::ReadOnly | Access::ReadWrite)
257 }
258
259 pub const fn writable(&self) -> bool {
260 matches!(
261 self.access,
262 Access::ReadWrite | Access::WriteOnly | Access::Command
263 )
264 }
265}
266
267pub const fn point(
269 id: &str,
270 space: AddressSpace,
271 offset: u16,
272 storage: StorageType,
273) -> PointDesc<'_> {
274 PointDesc {
275 id,
276 space,
277 offset,
278 model_relative_offset: 0,
279 length_words: 0,
280 storage,
281 value: ValueKind::Decimal,
282 byte_big: true,
283 word_big: true,
284 scale: None,
285 offset_add: None,
286 scale_ref: None,
287 selector: None,
288 na: &[],
289 access: Access::ReadOnly,
290 write: None,
291 }
292}