stackforge_core/layer/zwave/
builder.rs1use super::zwave_crc;
27
28#[derive(Debug, Clone)]
36pub struct ZWaveBuilder {
37 home_id: u32,
38 src: u8,
39 dst: u8,
40 routed: bool,
41 ackreq: bool,
42 lowpower: bool,
43 speedmodified: bool,
44 headertype: u8,
45 beam_control: u8,
46 seqn: u8,
47 cmd_class_val: Option<u8>,
48 cmd_val: Option<u8>,
49 cmd_data_val: Vec<u8>,
50}
51
52impl Default for ZWaveBuilder {
53 fn default() -> Self {
54 Self {
55 home_id: 0,
56 src: 1,
57 dst: 2,
58 routed: false,
59 ackreq: true,
60 lowpower: false,
61 speedmodified: false,
62 headertype: 0,
63 beam_control: 0,
64 seqn: 0,
65 cmd_class_val: None,
66 cmd_val: None,
67 cmd_data_val: Vec::new(),
68 }
69 }
70}
71
72impl ZWaveBuilder {
73 #[must_use]
75 pub fn new() -> Self {
76 Self::default()
77 }
78
79 #[must_use]
83 pub fn home_id(mut self, id: u32) -> Self {
84 self.home_id = id;
85 self
86 }
87
88 #[must_use]
90 pub fn src(mut self, src: u8) -> Self {
91 self.src = src;
92 self
93 }
94
95 #[must_use]
97 pub fn dst(mut self, dst: u8) -> Self {
98 self.dst = dst;
99 self
100 }
101
102 #[must_use]
104 pub fn routed(mut self, v: bool) -> Self {
105 self.routed = v;
106 self
107 }
108
109 #[must_use]
111 pub fn ackreq(mut self, v: bool) -> Self {
112 self.ackreq = v;
113 self
114 }
115
116 #[must_use]
118 pub fn lowpower(mut self, v: bool) -> Self {
119 self.lowpower = v;
120 self
121 }
122
123 #[must_use]
125 pub fn speedmodified(mut self, v: bool) -> Self {
126 self.speedmodified = v;
127 self
128 }
129
130 #[must_use]
132 pub fn headertype(mut self, v: u8) -> Self {
133 self.headertype = v & 0x0F;
134 self
135 }
136
137 #[must_use]
139 pub fn beam_control(mut self, v: u8) -> Self {
140 self.beam_control = v & 0x03;
141 self
142 }
143
144 #[must_use]
146 pub fn seqn(mut self, v: u8) -> Self {
147 self.seqn = v & 0x0F;
148 self
149 }
150
151 #[must_use]
153 pub fn cmd_class(mut self, cc: u8) -> Self {
154 self.cmd_class_val = Some(cc);
155 self
156 }
157
158 #[must_use]
160 pub fn cmd(mut self, c: u8) -> Self {
161 self.cmd_val = Some(c);
162 self
163 }
164
165 #[must_use]
167 pub fn cmd_data(mut self, data: Vec<u8>) -> Self {
168 self.cmd_data_val = data;
169 self
170 }
171
172 #[must_use]
174 pub fn ack(mut self) -> Self {
175 self.cmd_class_val = None;
176 self.cmd_val = None;
177 self.cmd_data_val = Vec::new();
178 self
179 }
180
181 fn build_frame_ctrl(&self) -> u8 {
185 let mut fc: u8 = self.headertype & 0x0F;
186 if self.routed {
187 fc |= 0x80;
188 }
189 if self.ackreq {
190 fc |= 0x40;
191 }
192 if self.lowpower {
193 fc |= 0x20;
194 }
195 if self.speedmodified {
196 fc |= 0x10;
197 }
198 fc
199 }
200
201 fn build_beam_seqn(&self) -> u8 {
203 ((self.beam_control & 0x03) << 5) | (self.seqn & 0x0F)
204 }
205
206 #[must_use]
212 pub fn build(&self) -> Vec<u8> {
213 let is_ack = self.cmd_class_val.is_none();
214
215 let payload_len = if is_ack {
217 0
218 } else {
219 1 + usize::from(self.cmd_val.is_some()) + self.cmd_data_val.len()
221 };
222
223 let total_len = 10 + payload_len; let mut buf = Vec::with_capacity(total_len);
225
226 buf.extend_from_slice(&self.home_id.to_be_bytes());
228
229 buf.push(self.src);
231
232 buf.push(self.build_frame_ctrl());
234
235 buf.push(self.build_beam_seqn());
237
238 buf.push(total_len as u8);
240
241 buf.push(self.dst);
243
244 if let Some(cc) = self.cmd_class_val {
246 buf.push(cc);
247 if let Some(cmd) = self.cmd_val {
248 buf.push(cmd);
249 }
250 buf.extend_from_slice(&self.cmd_data_val);
251 }
252
253 let crc = zwave_crc(&buf);
255 buf.push(crc);
256
257 buf
258 }
259}
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264 use crate::layer::zwave::{ZWAVE_MIN_HEADER_LEN, ZWaveLayer, cmd_class};
265 use crate::layer::{LayerIndex, LayerKind};
266
267 #[test]
268 fn test_build_ack_frame() {
269 let pkt = ZWaveBuilder::new()
270 .home_id(0x0161f498)
271 .src(1)
272 .dst(2)
273 .ack()
274 .build();
275 assert_eq!(pkt.len(), ZWAVE_MIN_HEADER_LEN);
276 let idx = LayerIndex::new(LayerKind::ZWave, 0, pkt.len());
278 let zw = ZWaveLayer::new(idx);
279 assert_eq!(zw.home_id(&pkt).unwrap(), 0x0161f498);
280 assert_eq!(zw.src(&pkt).unwrap(), 1);
281 assert_eq!(zw.dst(&pkt).unwrap(), 2);
282 assert!(zw.is_ack(&pkt));
283 assert!(zw.verify_crc(&pkt));
284 }
285
286 #[test]
287 fn test_build_req_frame() {
288 let pkt = ZWaveBuilder::new()
289 .home_id(0xDEADBEEF)
290 .src(3)
291 .dst(5)
292 .cmd_class(cmd_class::SWITCH_BINARY)
293 .cmd(0x01)
294 .cmd_data(vec![0xFF])
295 .build();
296
297 assert_eq!(pkt.len(), 13);
299
300 let idx = LayerIndex::new(LayerKind::ZWave, 0, pkt.len());
301 let zw = ZWaveLayer::new(idx);
302 assert!(!zw.is_ack(&pkt));
303 assert_eq!(zw.home_id(&pkt).unwrap(), 0xDEADBEEF);
304 assert_eq!(zw.src(&pkt).unwrap(), 3);
305 assert_eq!(zw.dst(&pkt).unwrap(), 5);
306 assert_eq!(zw.cmd_class(&pkt).unwrap(), cmd_class::SWITCH_BINARY);
307 assert_eq!(zw.cmd(&pkt).unwrap(), 0x01);
308 assert_eq!(zw.cmd_data(&pkt).unwrap(), &[0xFF]);
309 assert!(zw.verify_crc(&pkt));
310 }
311
312 #[test]
313 fn test_crc_verification() {
314 let pkt = ZWaveBuilder::new()
315 .home_id(0x01020304)
316 .src(10)
317 .dst(20)
318 .cmd_class(cmd_class::BASIC)
319 .cmd(0x01)
320 .build();
321
322 let idx = LayerIndex::new(LayerKind::ZWave, 0, pkt.len());
323 let zw = ZWaveLayer::new(idx);
324 assert!(zw.verify_crc(&pkt));
325
326 let mut bad = pkt.clone();
328 bad[4] ^= 0x01;
329 assert!(!zw.verify_crc(&bad));
330 }
331
332 #[test]
333 fn test_frame_ctrl_flags() {
334 let pkt = ZWaveBuilder::new()
335 .routed(true)
336 .ackreq(true)
337 .lowpower(true)
338 .speedmodified(true)
339 .headertype(0x03)
340 .ack()
341 .build();
342
343 let idx = LayerIndex::new(LayerKind::ZWave, 0, pkt.len());
344 let zw = ZWaveLayer::new(idx);
345 assert!(zw.routed(&pkt).unwrap());
346 assert!(zw.ackreq(&pkt).unwrap());
347 assert!(zw.lowpower(&pkt).unwrap());
348 assert!(zw.speedmodified(&pkt).unwrap());
349 assert_eq!(zw.headertype(&pkt).unwrap(), 0x03);
350 }
351
352 #[test]
353 fn test_beam_seqn() {
354 let pkt = ZWaveBuilder::new().beam_control(2).seqn(0x0A).ack().build();
355
356 let idx = LayerIndex::new(LayerKind::ZWave, 0, pkt.len());
357 let zw = ZWaveLayer::new(idx);
358 assert_eq!(zw.beam_control(&pkt).unwrap(), 2);
359 assert_eq!(zw.seqn(&pkt).unwrap(), 0x0A);
360 }
361
362 #[test]
363 fn test_defaults() {
364 let b = ZWaveBuilder::new();
365 let pkt = b.build();
366 assert_eq!(pkt.len(), 10);
368 let idx = LayerIndex::new(LayerKind::ZWave, 0, pkt.len());
369 let zw = ZWaveLayer::new(idx);
370 assert_eq!(zw.home_id(&pkt).unwrap(), 0);
371 assert_eq!(zw.src(&pkt).unwrap(), 1);
372 assert_eq!(zw.dst(&pkt).unwrap(), 2);
373 assert!(zw.ackreq(&pkt).unwrap());
374 assert!(!zw.routed(&pkt).unwrap());
375 assert!(zw.is_ack(&pkt));
376 }
377
378 #[test]
379 fn test_large_payload_round_trip() {
380 let data: Vec<u8> = (0..200).map(|i| (i & 0xFF) as u8).collect();
381 let pkt = ZWaveBuilder::new()
382 .home_id(0xCAFEBABE)
383 .src(10)
384 .dst(20)
385 .cmd_class(cmd_class::MANUFACTURER_PROPRIETARY)
386 .cmd(0x42)
387 .cmd_data(data.clone())
388 .build();
389
390 let idx = LayerIndex::new(LayerKind::ZWave, 0, pkt.len());
391 let zw = ZWaveLayer::new(idx);
392 assert_eq!(zw.home_id(&pkt).unwrap(), 0xCAFEBABE);
393 assert_eq!(zw.src(&pkt).unwrap(), 10);
394 assert_eq!(zw.dst(&pkt).unwrap(), 20);
395 assert_eq!(
396 zw.cmd_class(&pkt).unwrap(),
397 cmd_class::MANUFACTURER_PROPRIETARY
398 );
399 assert_eq!(zw.cmd(&pkt).unwrap(), 0x42);
400 assert_eq!(zw.cmd_data(&pkt).unwrap(), &data[..]);
401 assert!(zw.verify_crc(&pkt));
402 }
403
404 #[test]
405 fn test_length_field_correct() {
406 let ack = ZWaveBuilder::new().ack().build();
408 let idx = LayerIndex::new(LayerKind::ZWave, 0, ack.len());
409 let zw = ZWaveLayer::new(idx);
410 assert_eq!(zw.length(&ack).unwrap(), 10);
411
412 let req = ZWaveBuilder::new()
414 .cmd_class(cmd_class::BASIC)
415 .cmd(0x01)
416 .cmd_data(vec![0xAA])
417 .build();
418 let idx2 = LayerIndex::new(LayerKind::ZWave, 0, req.len());
419 let zw2 = ZWaveLayer::new(idx2);
420 assert_eq!(zw2.length(&req).unwrap(), 13);
421 assert_eq!(req.len(), 13);
422 }
423}