snap7_client/proto/s7commplus/
multivar.rs1use crate::proto::error::ProtoError;
2use crate::proto::s7commplus::data::DataArea;
3use crate::proto::s7commplus::session::{FC_GET_MULTI_VAR, FC_SET_MULTI_VAR, OPCODE_REQUEST};
4use bytes::{Buf, BufMut, Bytes, BytesMut};
5
6#[derive(Debug, Clone)]
8pub struct VarSpec {
9 pub crc: u32,
10 pub lid: u32,
11}
12
13#[derive(Debug, Clone)]
15pub struct VarResult {
16 pub return_code: u8,
17 pub value: Bytes,
18}
19
20#[derive(Debug)]
22pub struct GetMultiVarRequest {
23 pub seqnum: u16,
24 pub session_id: u32,
25 pub items: Vec<VarSpec>,
26}
27
28impl GetMultiVarRequest {
29 pub fn encode(&self, buf: &mut BytesMut) {
30 let mut payload = BytesMut::new();
31 payload.put_u8(self.items.len() as u8);
32 for item in &self.items {
33 payload.put_u32(item.crc);
34 payload.put_u32(item.lid);
35 }
36 DataArea {
37 opcode: OPCODE_REQUEST,
38 function_code: FC_GET_MULTI_VAR,
39 seqnum: self.seqnum,
40 session_id: self.session_id,
41 transport_flags: 0,
42 payload: payload.freeze(),
43 }
44 .encode(buf);
45 }
46}
47
48#[derive(Debug)]
50pub struct GetMultiVarResponse {
51 pub items: Vec<VarResult>,
52}
53
54impl GetMultiVarResponse {
55 pub fn decode(buf: &mut Bytes, item_count: usize) -> Result<Self, ProtoError> {
56 let da = DataArea::decode(buf)?;
57 let mut payload = da.payload;
58 let mut items = Vec::with_capacity(item_count);
59 for _ in 0..item_count {
60 if payload.remaining() < 3 {
61 return Err(ProtoError::BufferTooShort { need: 3, have: payload.remaining() });
62 }
63 let return_code = payload.get_u8();
64 let data_len = payload.get_u16() as usize;
65 if payload.remaining() < data_len {
66 return Err(ProtoError::BufferTooShort { need: data_len, have: payload.remaining() });
67 }
68 let value = payload.copy_to_bytes(data_len);
69 items.push(VarResult { return_code, value });
70 }
71 Ok(GetMultiVarResponse { items })
72 }
73}
74
75#[derive(Debug)]
77pub struct GetVarRequest {
78 pub seqnum: u16,
79 pub session_id: u32,
80 pub crc: u32,
81 pub lid: u32,
82}
83
84impl GetVarRequest {
85 pub fn encode(&self, buf: &mut BytesMut) {
86 let req = GetMultiVarRequest {
87 seqnum: self.seqnum,
88 session_id: self.session_id,
89 items: vec![VarSpec { crc: self.crc, lid: self.lid }],
90 };
91 req.encode(buf);
92 }
93}
94
95#[derive(Debug)]
96pub struct GetVarResponse {
97 pub return_code: u8,
98 pub value: Bytes,
99}
100
101impl GetVarResponse {
102 pub fn decode(buf: &mut Bytes) -> Result<Self, ProtoError> {
103 let multi = GetMultiVarResponse::decode(buf, 1)?;
104 if multi.items.is_empty() {
105 return Err(ProtoError::BufferTooShort { need: 3, have: 0 });
106 }
107 Ok(GetVarResponse {
108 return_code: multi.items[0].return_code,
109 value: multi.items[0].value.clone(),
110 })
111 }
112}
113
114#[derive(Debug)]
116pub struct SetMultiVarRequest {
117 pub seqnum: u16,
118 pub session_id: u32,
119 pub items: Vec<SetVarItem>,
120}
121
122#[derive(Debug, Clone)]
123pub struct SetVarItem {
124 pub crc: u32,
125 pub lid: u32,
126 pub value: Bytes,
127}
128
129impl SetMultiVarRequest {
130 pub fn encode(&self, buf: &mut BytesMut) {
131 let mut payload = BytesMut::new();
132 payload.put_u8(self.items.len() as u8);
133 for item in &self.items {
134 payload.put_u32(item.crc);
135 payload.put_u32(item.lid);
136 payload.put_u16(item.value.len() as u16);
137 payload.put_slice(&item.value);
138 }
139 DataArea {
140 opcode: OPCODE_REQUEST,
141 function_code: FC_SET_MULTI_VAR,
142 seqnum: self.seqnum,
143 session_id: self.session_id,
144 transport_flags: 0,
145 payload: payload.freeze(),
146 }
147 .encode(buf);
148 }
149}
150
151#[derive(Debug)]
153pub struct SetVarRequest {
154 pub seqnum: u16,
155 pub session_id: u32,
156 pub crc: u32,
157 pub lid: u32,
158 pub value: Bytes,
159}
160
161impl SetVarRequest {
162 pub fn encode(&self, buf: &mut BytesMut) {
163 let req = SetMultiVarRequest {
164 seqnum: self.seqnum,
165 session_id: self.session_id,
166 items: vec![SetVarItem {
167 crc: self.crc,
168 lid: self.lid,
169 value: self.value.clone(),
170 }],
171 };
172 req.encode(buf);
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179 use bytes::{Bytes, BytesMut};
180
181 #[test]
182 fn get_var_request_function_code() {
183 let req = GetVarRequest {
184 seqnum: 1,
185 session_id: 0xDEAD0001,
186 crc: 0xABCD1234,
187 lid: 2,
188 };
189 let mut buf = BytesMut::new();
190 req.encode(&mut buf);
191 assert_eq!(u16::from_be_bytes([buf[3], buf[4]]), 0x054C);
192 }
193
194 #[test]
195 fn get_var_request_session_id_position() {
196 let req = GetVarRequest {
197 seqnum: 2,
198 session_id: 0x12345678,
199 crc: 0xAABBCCDD,
200 lid: 1,
201 };
202 let mut buf = BytesMut::new();
203 req.encode(&mut buf);
204 let sid = u32::from_be_bytes([buf[9], buf[10], buf[11], buf[12]]);
206 assert_eq!(sid, 0x12345678);
207 }
208
209 #[test]
210 fn set_var_request_function_code() {
211 let req = SetVarRequest {
212 seqnum: 3,
213 session_id: 5,
214 crc: 0x11223344,
215 lid: 1,
216 value: Bytes::from_static(&[0x3F, 0x80, 0x00, 0x00]),
217 };
218 let mut buf = BytesMut::new();
219 req.encode(&mut buf);
220 assert_eq!(u16::from_be_bytes([buf[3], buf[4]]), 0x0542);
221 }
222
223 #[test]
224 fn get_var_response_decode() {
225 use bytes::BufMut;
226 let mut buf = BytesMut::new();
227 buf.put_u8(0x32);
228 buf.put_u16(0x0000);
229 buf.put_u16(0x054C);
230 buf.put_u16(0x0000);
231 buf.put_u16(0x0001);
232 buf.put_u32(0x00000005);
233 buf.put_u8(0x00);
234 buf.put_u8(0x0A); buf.put_u16(4);
236 buf.put_slice(&[0x3F, 0x80, 0x00, 0x00]);
237 let mut b = buf.freeze();
238 let resp = GetVarResponse::decode(&mut b).unwrap();
239 assert_eq!(resp.return_code, 0x0A);
240 assert_eq!(&resp.value[..], &[0x3F, 0x80, 0x00, 0x00]);
241 }
242}