1use crate::error::Result;
2use crate::PlcValue;
3use std::collections::HashMap;
4
5#[derive(Debug, Clone)]
7pub struct UdtDefinition {
8 pub name: String,
9 pub members: Vec<UdtMember>,
10}
11
12#[derive(Debug, Clone)]
14pub struct UdtMember {
15 pub name: String,
16 pub data_type: u16,
17 pub offset: u32,
18 pub size: u32,
19}
20
21#[derive(Debug)]
23pub struct UdtManager {
24 _definitions: HashMap<String, UdtDefinition>,
25}
26
27impl UdtManager {
28 pub fn new() -> Self {
29 Self {
30 _definitions: HashMap::new(),
31 }
32 }
33
34 pub fn parse_udt_instance(&self, _udt_name: &str, _data: &[u8]) -> Result<PlcValue> {
36 Ok(PlcValue::Udt(HashMap::new()))
39 }
40
41 pub fn serialize_udt_instance(
43 &self,
44 _udt_value: &HashMap<String, PlcValue>,
45 ) -> Result<Vec<u8>> {
46 Ok(Vec::new())
49 }
50}
51
52impl Default for UdtManager {
53 fn default() -> Self {
54 Self::new()
55 }
56}
57
58#[derive(Debug, Clone)]
60pub struct UserDefinedType {
61 pub name: String,
63 pub size: u32,
65 pub members: Vec<UdtMember>,
67 member_offsets: HashMap<String, u32>,
69}
70
71impl UserDefinedType {
72 pub fn new(name: String) -> Self {
74 Self {
75 name,
76 size: 0,
77 members: Vec::new(),
78 member_offsets: HashMap::new(),
79 }
80 }
81
82 pub fn add_member(&mut self, member: UdtMember) {
84 self.member_offsets
85 .insert(member.name.clone(), member.offset);
86 self.members.push(member);
87 self.size = self
89 .members
90 .iter()
91 .map(|m| m.offset + m.size)
92 .max()
93 .unwrap_or(0);
94 }
95
96 pub fn get_member_offset(&self, name: &str) -> Option<u32> {
98 self.member_offsets.get(name).copied()
99 }
100
101 pub fn from_cip_data(_data: &[u8]) -> crate::error::Result<Self> {
103 Ok(Self {
105 name: String::new(),
106 members: Vec::new(),
107 size: 0,
108 member_offsets: HashMap::new(),
109 })
110 }
111
112 pub fn to_hash_map(&self, data: &[u8]) -> crate::error::Result<HashMap<String, PlcValue>> {
114 let mut result = HashMap::new();
115
116 for member in &self.members {
117 let offset = member.offset as usize;
118 if offset + member.size as usize <= data.len() {
119 let member_data = &data[offset..offset + member.size as usize];
120 let value = self.parse_member_value(member, member_data)?;
121 result.insert(member.name.clone(), value);
122 }
123 }
124
125 Ok(result)
126 }
127
128 fn parse_member_value(
130 &self,
131 member: &UdtMember,
132 data: &[u8],
133 ) -> crate::error::Result<PlcValue> {
134 match member.data_type {
135 0x00C1 => Ok(PlcValue::Bool(data[0] != 0)),
136 0x00C4 => {
137 let mut bytes = [0u8; 4];
138 bytes.copy_from_slice(&data[..4]);
139 Ok(PlcValue::Dint(i32::from_le_bytes(bytes)))
140 }
141 0x00CA => {
142 let mut bytes = [0u8; 4];
143 bytes.copy_from_slice(&data[..4]);
144 Ok(PlcValue::Real(f32::from_le_bytes(bytes)))
145 }
146 _ => Err(crate::error::EtherNetIpError::Protocol(
147 "Unsupported data type".to_string(),
148 )),
149 }
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 #[test]
158 fn test_udt_member_offsets() {
159 let mut udt = UserDefinedType::new("TestUDT".to_string());
160
161 udt.add_member(UdtMember {
162 name: "Bool1".to_string(),
163 data_type: 0x00C1,
164 offset: 0,
165 size: 1,
166 });
167
168 udt.add_member(UdtMember {
169 name: "Dint1".to_string(),
170 data_type: 0x00C4,
171 offset: 4,
172 size: 4,
173 });
174
175 assert_eq!(udt.get_member_offset("Bool1"), Some(0));
176 assert_eq!(udt.get_member_offset("Dint1"), Some(4));
177 assert_eq!(udt.size, 8);
178 }
179
180 #[test]
181 fn test_udt_parsing() {
182 let mut udt = UserDefinedType::new("TestUDT".to_string());
183
184 udt.add_member(UdtMember {
185 name: "Bool1".to_string(),
186 data_type: 0x00C1,
187 offset: 0,
188 size: 1,
189 });
190
191 udt.add_member(UdtMember {
192 name: "Dint1".to_string(),
193 data_type: 0x00C4,
194 offset: 4,
195 size: 4,
196 });
197
198 let data = vec![0xFF, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00];
199 let result = udt.to_hash_map(&data).unwrap();
200
201 assert_eq!(result.get("Bool1"), Some(&PlcValue::Bool(true)));
202 assert_eq!(result.get("Dint1"), Some(&PlcValue::Dint(42)));
203 }
204}