1use std::time::Duration;
2
3#[derive(Debug, Clone)]
4pub struct ConnectParams {
5 pub rack: u8,
6 pub slot: u8,
7 pub pdu_size: u16,
8 pub connect_timeout: Duration,
9 pub request_timeout: Duration,
10}
11
12impl Default for ConnectParams {
13 fn default() -> Self {
14 Self {
15 rack: 0,
16 slot: 1,
17 pdu_size: 480,
18 connect_timeout: Duration::from_secs(5),
19 request_timeout: Duration::from_secs(10),
20 }
21 }
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum PlcStatus {
27 Unknown = 0x00,
29 Stop = 0x04,
31 Run = 0x08,
33}
34
35#[derive(Debug, Clone)]
37pub struct OrderCode {
38 pub code: String,
40 pub v1: u8,
42 pub v2: u8,
44 pub v3: u8,
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub enum Protocol {
54 S7,
56 S7Plus,
58}
59
60impl std::fmt::Display for Protocol {
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 match self {
63 Protocol::S7 => write!(f, "S7"),
64 Protocol::S7Plus => write!(f, "S7+"),
65 }
66 }
67}
68
69#[derive(Debug, Clone)]
71pub struct CpuInfo {
72 pub module_type: String,
74 pub serial_number: String,
76 pub as_name: String,
78 pub copyright: String,
80 pub module_name: String,
82 pub protocol: Protocol,
84}
85
86#[derive(Debug, Clone)]
88pub struct CpInfo {
89 pub max_pdu_len: u32,
91 pub max_connections: u32,
93 pub max_mpi_rate: u32,
95 pub max_bus_rate: u32,
97}
98
99#[derive(Debug, Clone)]
101pub struct Protection {
102 pub scheme_szl: u16,
104 pub scheme_module: u16,
106 pub scheme_bus: u16,
108 pub level: u16,
110 pub password_set: bool,
112}
113
114pub fn encrypt_password(password: &str) -> [u8; 8] {
120 let bytes = password.as_bytes();
121 let mut pw = [0x20u8; 8]; let len = bytes.len().min(8);
123 pw[..len].copy_from_slice(&bytes[..len]);
124 let mut result = [0u8; 8];
125 for i in 0..8 {
126 result[i] = (pw[i] << 4) | (pw[i] >> 4);
128 result[i] ^= 0x55;
129 }
130 result
131}
132
133#[derive(Debug, Clone)]
135pub struct ModuleEntry {
136 pub module_type: u16,
138}
139
140#[derive(Debug, Clone)]
142pub struct BlockListEntry {
143 pub block_type: u16,
145 pub count: u16,
147}
148
149#[derive(Debug, Clone)]
151pub struct BlockList {
152 pub total_count: u32,
154 pub entries: Vec<BlockListEntry>,
156}
157
158#[derive(Debug, Clone)]
166pub struct BlockData {
167 pub block_type: u16,
169 pub block_number: u16,
171 pub format: u16,
173 pub total_length: u32,
175 pub flags: u16,
177 pub crc1: u16,
179 pub crc2: u16,
181 pub payload: Vec<u8>,
183}
184
185impl BlockData {
186 pub fn from_bytes(data: &[u8]) -> Option<Self> {
188 if data.len() < 20 {
189 return None;
190 }
191 let block_type = u16::from_be_bytes([data[0], data[1]]);
192 let block_number = u16::from_be_bytes([data[2], data[3]]);
193 let format = u16::from_be_bytes([data[4], data[5]]);
194 let total_length = u32::from_be_bytes([data[6], data[7], data[8], data[9]]);
195 let flags = u16::from_be_bytes([data[10], data[11]]);
196 let crc1 = u16::from_be_bytes([data[12], data[13]]);
197 let crc2 = u16::from_be_bytes([data[14], data[15]]);
198 let payload = data[20..].to_vec();
200 Some(BlockData {
201 block_type,
202 block_number,
203 format,
204 total_length,
205 flags,
206 crc1,
207 crc2,
208 payload,
209 })
210 }
211
212 pub fn to_bytes(&self) -> Vec<u8> {
214 let mut buf = Vec::with_capacity(20 + self.payload.len());
215 buf.extend_from_slice(&self.block_type.to_be_bytes());
216 buf.extend_from_slice(&self.block_number.to_be_bytes());
217 buf.extend_from_slice(&self.format.to_be_bytes());
218 buf.extend_from_slice(&self.total_length.to_be_bytes());
219 buf.extend_from_slice(&self.flags.to_be_bytes());
220 buf.extend_from_slice(&self.crc1.to_be_bytes());
221 buf.extend_from_slice(&self.crc2.to_be_bytes());
222 buf.extend_from_slice(&[0u8; 4]); buf.extend_from_slice(&self.payload);
224 buf
225 }
226}
227
228#[derive(Debug, Clone)]
232pub struct BlockInfo {
233 pub block_type: u16,
234 pub block_number: u16,
235 pub language: u16,
236 pub flags: u16,
237 pub size: u16,
238 pub size_ram: u16,
239 pub mc7_size: u16,
240 pub local_data: u16,
241 pub checksum: u16,
242 pub version: u16,
243 pub author: String,
244 pub family: String,
245 pub header: String,
246 pub date: String,
247}
248
249#[derive(Debug, Clone, Copy, PartialEq, Eq)]
250#[repr(u8)]
251pub enum BlockType {
252 OB = 0x38,
253 DB = 0x41,
254 SDB = 0x42,
255 FC = 0x43,
256 SFC = 0x44,
257 FB = 0x45,
258 SFB = 0x46,
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264
265 #[test]
266 fn connect_params_default() {
267 let p = ConnectParams::default();
268 assert_eq!(p.rack, 0);
269 assert_eq!(p.slot, 1);
270 assert_eq!(p.pdu_size, 480);
271 }
272
273 #[test]
274 fn block_data_roundtrip() {
275 let bd = super::BlockData {
276 block_type: 0x41, block_number: 1,
278 format: 0,
279 total_length: 24,
280 flags: 0,
281 crc1: 0x1234,
282 crc2: 0x5678,
283 payload: vec![0xDE, 0xAD],
284 };
285 let bytes = bd.to_bytes();
286 assert_eq!(bytes.len(), 22); let parsed = super::BlockData::from_bytes(&bytes).unwrap();
288 assert_eq!(parsed.block_type, 0x41);
289 assert_eq!(parsed.block_number, 1);
290 assert_eq!(parsed.payload, vec![0xDE, 0xAD]);
291 }
292
293 #[test]
294 fn block_data_short_input_returns_none() {
295 let result = super::BlockData::from_bytes(&[0u8; 10]);
296 assert!(result.is_none());
297 }
298
299 #[test]
300 fn encrypt_8_char_password() {
301 let result = super::encrypt_password("PASSWORD");
303 assert_eq!(result.len(), 8);
304 let result2 = super::encrypt_password("PASSWORD");
310 assert_eq!(result, result2);
311 }
312
313 #[test]
314 fn encrypt_short_password_padded() {
315 let result = super::encrypt_password("abc");
316 assert_eq!((0x61u8 << 4) | (0x61u8 >> 4), 0x16);
319 assert_eq!(0x16 ^ 0x55, 0x43);
320 assert_eq!(result[0], 0x43);
321 assert_eq!((0x20u8 << 4) | (0x20u8 >> 4), 0x02);
323 assert_eq!(0x02 ^ 0x55, 0x57);
324 assert_eq!(result[3], 0x57);
325 }
326
327 #[test]
328 fn encrypt_long_password_truncated() {
329 let result = super::encrypt_password("1234567890");
330 assert_eq!(result.len(), 8);
331 let result8 = super::encrypt_password("12345678");
332 assert_eq!(result, result8);
333 }
334
335 #[test]
336 fn block_type_discriminants() {
337 assert_eq!(BlockType::DB as u8, 0x41);
338 assert_eq!(BlockType::OB as u8, 0x38);
339 }
340}