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}