Skip to main content

plc_comm_slmp/
model.rs

1use std::fmt;
2use std::time::Duration;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
5pub enum SlmpTransportMode {
6    Tcp,
7    Udp,
8}
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
11pub enum SlmpFrameType {
12    Frame3E,
13    Frame4E,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
17pub enum SlmpCompatibilityMode {
18    Legacy,
19    Iqr,
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
23pub enum SlmpTraceDirection {
24    Send,
25    Receive,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29#[repr(u16)]
30pub enum SlmpCommand {
31    DeviceRead = 0x0401,
32    DeviceWrite = 0x1401,
33    DeviceReadRandom = 0x0403,
34    DeviceWriteRandom = 0x1402,
35    DeviceReadBlock = 0x0406,
36    DeviceWriteBlock = 0x1406,
37    MonitorRegister = 0x0801,
38    Monitor = 0x0802,
39    ReadTypeName = 0x0101,
40    LabelArrayRead = 0x041A,
41    LabelArrayWrite = 0x141A,
42    LabelReadRandom = 0x041C,
43    LabelWriteRandom = 0x141B,
44    MemoryRead = 0x0613,
45    MemoryWrite = 0x1613,
46    ExtendUnitRead = 0x0601,
47    ExtendUnitWrite = 0x1601,
48    RemoteRun = 0x1001,
49    RemoteStop = 0x1002,
50    RemotePause = 0x1003,
51    RemoteLatchClear = 0x1005,
52    RemoteReset = 0x1006,
53    RemotePasswordUnlock = 0x1630,
54    RemotePasswordLock = 0x1631,
55    SelfTest = 0x0619,
56    ClearError = 0x1617,
57}
58
59impl SlmpCommand {
60    pub fn as_u16(self) -> u16 {
61        self as u16
62    }
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
66#[repr(u16)]
67pub enum SlmpDeviceCode {
68    SM = 0x0091,
69    SD = 0x00A9,
70    X = 0x009C,
71    Y = 0x009D,
72    M = 0x0090,
73    L = 0x0092,
74    F = 0x0093,
75    V = 0x0094,
76    B = 0x00A0,
77    D = 0x00A8,
78    W = 0x00B4,
79    TS = 0x00C1,
80    TC = 0x00C0,
81    TN = 0x00C2,
82    LTS = 0x0051,
83    LTC = 0x0050,
84    LTN = 0x0052,
85    STS = 0x00C7,
86    STC = 0x00C6,
87    STN = 0x00C8,
88    LSTS = 0x0059,
89    LSTC = 0x0058,
90    LSTN = 0x005A,
91    LCC = 0x0054,
92    LCS = 0x0055,
93    LCN = 0x0056,
94    CS = 0x00C4,
95    CC = 0x00C3,
96    CN = 0x00C5,
97    SB = 0x00A1,
98    SW = 0x00B5,
99    DX = 0x00A2,
100    DY = 0x00A3,
101    Z = 0x00CC,
102    LZ = 0x0062,
103    R = 0x00AF,
104    ZR = 0x00B0,
105    RD = 0x002C,
106    G = 0x00AB,
107    HG = 0x002E,
108}
109
110impl SlmpDeviceCode {
111    pub fn as_u16(self) -> u16 {
112        self as u16
113    }
114
115    pub fn as_u8(self) -> u8 {
116        (self.as_u16() & 0x00FF) as u8
117    }
118
119    pub fn prefix(self) -> &'static str {
120        match self {
121            Self::SM => "SM",
122            Self::SD => "SD",
123            Self::X => "X",
124            Self::Y => "Y",
125            Self::M => "M",
126            Self::L => "L",
127            Self::F => "F",
128            Self::V => "V",
129            Self::B => "B",
130            Self::D => "D",
131            Self::W => "W",
132            Self::TS => "TS",
133            Self::TC => "TC",
134            Self::TN => "TN",
135            Self::LTS => "LTS",
136            Self::LTC => "LTC",
137            Self::LTN => "LTN",
138            Self::STS => "STS",
139            Self::STC => "STC",
140            Self::STN => "STN",
141            Self::LSTS => "LSTS",
142            Self::LSTC => "LSTC",
143            Self::LSTN => "LSTN",
144            Self::LCC => "LCC",
145            Self::LCS => "LCS",
146            Self::LCN => "LCN",
147            Self::CS => "CS",
148            Self::CC => "CC",
149            Self::CN => "CN",
150            Self::SB => "SB",
151            Self::SW => "SW",
152            Self::DX => "DX",
153            Self::DY => "DY",
154            Self::Z => "Z",
155            Self::LZ => "LZ",
156            Self::R => "R",
157            Self::ZR => "ZR",
158            Self::RD => "RD",
159            Self::G => "G",
160            Self::HG => "HG",
161        }
162    }
163
164    pub fn is_hex_addressed(self) -> bool {
165        matches!(
166            self,
167            Self::X | Self::Y | Self::B | Self::W | Self::SB | Self::SW | Self::DX | Self::DY
168        )
169    }
170
171    pub fn is_bit_device(self) -> bool {
172        matches!(
173            self,
174            Self::SM
175                | Self::X
176                | Self::Y
177                | Self::M
178                | Self::L
179                | Self::F
180                | Self::V
181                | Self::B
182                | Self::TS
183                | Self::TC
184                | Self::LTS
185                | Self::LTC
186                | Self::STS
187                | Self::STC
188                | Self::LSTS
189                | Self::LSTC
190                | Self::CS
191                | Self::CC
192                | Self::LCS
193                | Self::LCC
194                | Self::SB
195                | Self::DX
196                | Self::DY
197        )
198    }
199
200    pub fn is_word_device(self) -> bool {
201        matches!(
202            self,
203            Self::SD
204                | Self::D
205                | Self::W
206                | Self::TN
207                | Self::LTN
208                | Self::STN
209                | Self::LSTN
210                | Self::CN
211                | Self::LCN
212                | Self::SW
213                | Self::Z
214                | Self::LZ
215                | Self::R
216                | Self::ZR
217                | Self::RD
218                | Self::G
219                | Self::HG
220        )
221    }
222
223    pub fn is_word_batchable(self) -> bool {
224        matches!(
225            self,
226            Self::SD
227                | Self::D
228                | Self::W
229                | Self::TN
230                | Self::LTN
231                | Self::STN
232                | Self::LSTN
233                | Self::CN
234                | Self::LCN
235                | Self::SW
236                | Self::Z
237                | Self::LZ
238                | Self::R
239                | Self::ZR
240                | Self::RD
241        )
242    }
243
244    pub fn parse_prefix(prefix: &str) -> Option<Self> {
245        match prefix {
246            "LSTS" => Some(Self::LSTS),
247            "LSTC" => Some(Self::LSTC),
248            "LSTN" => Some(Self::LSTN),
249            "LTS" => Some(Self::LTS),
250            "LTC" => Some(Self::LTC),
251            "LTN" => Some(Self::LTN),
252            "STS" => Some(Self::STS),
253            "STC" => Some(Self::STC),
254            "STN" => Some(Self::STN),
255            "SM" => Some(Self::SM),
256            "SD" => Some(Self::SD),
257            "TS" => Some(Self::TS),
258            "TC" => Some(Self::TC),
259            "TN" => Some(Self::TN),
260            "CS" => Some(Self::CS),
261            "CC" => Some(Self::CC),
262            "CN" => Some(Self::CN),
263            "SB" => Some(Self::SB),
264            "SW" => Some(Self::SW),
265            "DX" => Some(Self::DX),
266            "DY" => Some(Self::DY),
267            "LCS" => Some(Self::LCS),
268            "LCC" => Some(Self::LCC),
269            "LCN" => Some(Self::LCN),
270            "LZ" => Some(Self::LZ),
271            "ZR" => Some(Self::ZR),
272            "RD" => Some(Self::RD),
273            "HG" => Some(Self::HG),
274            "X" => Some(Self::X),
275            "Y" => Some(Self::Y),
276            "M" => Some(Self::M),
277            "L" => Some(Self::L),
278            "F" => Some(Self::F),
279            "V" => Some(Self::V),
280            "B" => Some(Self::B),
281            "D" => Some(Self::D),
282            "W" => Some(Self::W),
283            "Z" => Some(Self::Z),
284            "R" => Some(Self::R),
285            "G" => Some(Self::G),
286            _ => None,
287        }
288    }
289}
290
291impl fmt::Display for SlmpDeviceCode {
292    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
293        f.write_str(self.prefix())
294    }
295}
296
297#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
298pub struct SlmpTargetAddress {
299    pub network: u8,
300    pub station: u8,
301    pub module_io: u16,
302    pub multidrop: u8,
303}
304
305impl Default for SlmpTargetAddress {
306    fn default() -> Self {
307        Self {
308            network: 0x00,
309            station: 0xFF,
310            module_io: 0x03FF,
311            multidrop: 0x00,
312        }
313    }
314}
315
316#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
317pub struct SlmpDeviceAddress {
318    pub code: SlmpDeviceCode,
319    pub number: u32,
320}
321
322impl SlmpDeviceAddress {
323    pub const fn new(code: SlmpDeviceCode, number: u32) -> Self {
324        Self { code, number }
325    }
326}
327
328impl fmt::Display for SlmpDeviceAddress {
329    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330        write!(f, "{}{}", self.code, self.number)
331    }
332}
333
334#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
335pub struct SlmpTypeNameInfo {
336    pub model: String,
337    pub model_code: u16,
338    pub has_model_code: bool,
339}
340
341#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
342pub struct SlmpBlockRead {
343    pub device: SlmpDeviceAddress,
344    pub points: u16,
345}
346
347#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
348pub struct SlmpBlockWrite {
349    pub device: SlmpDeviceAddress,
350    pub values: Vec<u16>,
351}
352
353#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, Default)]
354pub struct SlmpBlockWriteOptions {
355    pub split_mixed_blocks: bool,
356    pub retry_mixed_on_error: bool,
357}
358
359#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, Default)]
360pub struct SlmpRandomReadResult {
361    pub word_values: Vec<u16>,
362    pub dword_values: Vec<u32>,
363}
364
365#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, Default)]
366pub struct SlmpBlockReadResult {
367    pub word_values: Vec<u16>,
368    pub bit_values: Vec<u16>,
369}
370
371#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
372pub struct SlmpLongTimerResult {
373    pub index: u32,
374    pub device: String,
375    pub current_value: u32,
376    pub contact: bool,
377    pub coil: bool,
378    pub status_word: u16,
379    pub raw_words: Vec<u16>,
380}
381
382#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
383pub struct SlmpExtensionSpec {
384    pub extension_specification: u16,
385    pub extension_specification_modification: u8,
386    pub device_modification_index: u8,
387    pub device_modification_flags: u8,
388    pub direct_memory_specification: u8,
389}
390
391impl Default for SlmpExtensionSpec {
392    fn default() -> Self {
393        Self {
394            extension_specification: 0x0000,
395            extension_specification_modification: 0x00,
396            device_modification_index: 0x00,
397            device_modification_flags: 0x00,
398            direct_memory_specification: 0x00,
399        }
400    }
401}
402
403#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
404pub struct SlmpQualifiedDeviceAddress {
405    pub device: SlmpDeviceAddress,
406    pub extension_specification: Option<u16>,
407    pub direct_memory_specification: Option<u8>,
408}
409
410#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
411pub struct SlmpNamedTarget {
412    pub name: String,
413    pub target: SlmpTargetAddress,
414}
415
416#[derive(Debug, Clone)]
417pub struct SlmpConnectionOptions {
418    pub host: String,
419    pub port: u16,
420    pub timeout: Duration,
421    pub frame_type: SlmpFrameType,
422    pub compatibility_mode: SlmpCompatibilityMode,
423    pub target: SlmpTargetAddress,
424    pub transport_mode: SlmpTransportMode,
425    pub monitoring_timer: u16,
426}
427
428impl SlmpConnectionOptions {
429    pub fn new(host: impl Into<String>) -> Self {
430        Self {
431            host: host.into(),
432            port: 1025,
433            timeout: Duration::from_secs(3),
434            frame_type: SlmpFrameType::Frame4E,
435            compatibility_mode: SlmpCompatibilityMode::Iqr,
436            target: SlmpTargetAddress::default(),
437            transport_mode: SlmpTransportMode::Tcp,
438            monitoring_timer: 0x0010,
439        }
440    }
441}