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_offset: u16,
189 mantissa_words: u8,
190 exponent_offset: u16,
191 exponent_words: u8,
192 },
193}
194
195#[derive(Clone, Copy, Debug, Default)]
197pub struct WriteDesc<'a> {
198 pub min: Option<Rational>,
199 pub max: Option<Rational>,
200 pub step: Option<Rational>,
201 pub allowed: &'a [i64],
202}
203
204#[derive(Clone, Copy, Debug)]
206pub struct PointDesc<'a> {
207 pub id: &'a str,
208 pub space: AddressSpace,
209 pub offset: u16,
210 pub model_relative_offset: u16,
213 pub length_words: u8,
214 pub storage: StorageType,
215 pub value: ValueKind<'a>,
216 pub byte_big: bool,
218 pub word_big: bool,
220 pub scale: Option<Rational>,
221 pub offset_add: Option<Rational>,
222 pub scale_ref: Option<ScaleRefDesc<'a>>,
223 pub selector: Option<SelectorDesc<'a>>,
224 pub na: &'a [NaDesc<'a>],
225 pub access: Access,
226 pub write: Option<WriteDesc<'a>>,
227}
228
229impl<'a> PointDesc<'a> {
230 pub const fn words(&self) -> usize {
232 if self.length_words > 0 {
233 self.length_words as usize
234 } else {
235 self.storage.default_words()
236 }
237 }
238
239 pub const fn readable(&self) -> bool {
240 matches!(self.access, Access::ReadOnly | Access::ReadWrite)
241 }
242
243 pub const fn writable(&self) -> bool {
244 matches!(
245 self.access,
246 Access::ReadWrite | Access::WriteOnly | Access::Command
247 )
248 }
249}
250
251pub const fn point(
253 id: &str,
254 space: AddressSpace,
255 offset: u16,
256 storage: StorageType,
257) -> PointDesc<'_> {
258 PointDesc {
259 id,
260 space,
261 offset,
262 model_relative_offset: 0,
263 length_words: 0,
264 storage,
265 value: ValueKind::Decimal,
266 byte_big: true,
267 word_big: true,
268 scale: None,
269 offset_add: None,
270 scale_ref: None,
271 selector: None,
272 na: &[],
273 access: Access::ReadOnly,
274 write: None,
275 }
276}