1use heapless::Vec;
13
14use crate::ids::Vcid;
15use super::clcw::Clcw;
16
17const MAX_ACTIONS: usize = 4;
19
20#[derive(Debug, Copy, Clone, Eq, PartialEq)]
22pub enum FarmState {
23 Open,
25 Wait,
27 Lockout,
29}
30
31#[derive(Debug, Copy, Clone, Eq, PartialEq)]
33pub enum ControlCommand {
34 Unlock,
36 SetVr(u8),
38}
39
40#[derive(Debug, Clone)]
42pub enum FarmEvent<'a> {
43 AdFrame {
45 seq: u8,
47 buffer_available: bool,
49 fdu: &'a [u8],
51 },
52
53 BdFrame {
55 fdu: &'a [u8],
57 },
58
59 BcFrame {
61 command: ControlCommand,
63 },
64
65 InvalidFrame,
67
68 BufferRelease,
70
71 ClcwReport,
73}
74
75#[derive(Debug, Clone)]
77pub enum FarmAction<'a> {
78 Accept {
80 fdu: &'a [u8],
82 },
83
84 Discard,
86
87 ClcwReady,
89}
90
91#[derive(Debug)]
93pub struct FarmActions<'a> {
94 inner: Vec<FarmAction<'a>, MAX_ACTIONS>,
95}
96
97impl<'a> FarmActions<'a> {
98 pub fn new() -> Self {
100 Self { inner: Vec::new() }
101 }
102
103 fn push(&mut self, action: FarmAction<'a>) {
104 let _ = self.inner.push(action);
105 }
106
107 fn clear(&mut self) {
108 self.inner.clear();
109 }
110
111 pub fn iter(&self) -> impl Iterator<Item = &FarmAction<'a>> {
113 self.inner.iter()
114 }
115
116 pub fn is_empty(&self) -> bool {
118 self.inner.is_empty()
119 }
120
121 pub fn len(&self) -> usize {
123 self.inner.len()
124 }
125}
126
127impl Default for FarmActions<'_> {
128 fn default() -> Self {
129 Self::new()
130 }
131}
132
133impl<'b, 'a> IntoIterator for &'b FarmActions<'a> {
134 type Item = &'b FarmAction<'a>;
135 type IntoIter = core::slice::Iter<'b, FarmAction<'a>>;
136
137 fn into_iter(self) -> Self::IntoIter {
138 self.inner.iter()
139 }
140}
141
142#[derive(Debug, Clone)]
144#[derive(bon::Builder)]
145pub struct FarmConfig {
146 pub vcid: Vcid,
148 pub window_width: u8,
150}
151
152pub struct FarmMachine {
157 state: FarmState,
158 vr: u8,
160 lockout_flag: bool,
161 wait_flag: bool,
162 retransmit_flag: bool,
163 farm_b_counter: u8,
165 pw: u8,
167 nw: u8,
169 vcid: Vcid,
171}
172
173impl FarmMachine {
174 pub fn new(config: FarmConfig) -> Self {
176 let pw = config.window_width / 2;
177 let nw = config.window_width / 2;
178 Self {
179 state: FarmState::Open,
180 vr: 0,
181 lockout_flag: false,
182 wait_flag: false,
183 retransmit_flag: false,
184 farm_b_counter: 0,
185 pw,
186 nw,
187 vcid: config.vcid,
188 }
189 }
190
191 pub fn state(&self) -> FarmState {
193 self.state
194 }
195
196 pub fn vr(&self) -> u8 {
198 self.vr
199 }
200
201 pub fn clcw(&self) -> Clcw {
203 let mut clcw = Clcw::new();
204 clcw.set_cop_in_effect(1);
205 clcw.set_vcid(self.vcid);
206 clcw.set_lockout(self.lockout_flag);
207 clcw.set_wait(self.wait_flag);
208 clcw.set_retransmit(self.retransmit_flag);
209 clcw.set_farm_b_counter(self.farm_b_counter);
210 clcw.set_report_value(self.vr);
211 clcw
212 }
213
214 pub fn handle<'a>(
216 &mut self,
217 event: FarmEvent<'a>,
218 actions: &mut FarmActions<'a>,
219 ) {
220 actions.clear();
221
222 match event {
223 FarmEvent::AdFrame {
224 seq,
225 buffer_available,
226 fdu,
227 } => {
228 self.handle_ad_frame(seq, buffer_available, fdu, actions);
229 }
230 FarmEvent::BdFrame { fdu } => {
231 self.handle_bd_frame(fdu, actions);
232 }
233 FarmEvent::BcFrame { command } => {
234 self.handle_bc_frame(command, actions);
235 }
236 FarmEvent::InvalidFrame => {
237 actions.push(FarmAction::Discard);
239 }
240 FarmEvent::BufferRelease => {
241 self.handle_buffer_release(actions);
242 }
243 FarmEvent::ClcwReport => {
244 actions.push(FarmAction::ClcwReady);
246 }
247 }
248 }
249
250 fn handle_ad_frame<'a>(
252 &mut self,
253 seq: u8,
254 buffer_available: bool,
255 fdu: &'a [u8],
256 actions: &mut FarmActions<'a>,
257 ) {
258 let zone = self.classify_seq(seq);
259
260 match self.state {
261 FarmState::Open => match zone {
262 SeqZone::Expected => {
263 if buffer_available {
264 self.vr = self.vr.wrapping_add(1);
266 self.retransmit_flag = false;
267 actions.push(FarmAction::Accept { fdu });
268 } else {
269 self.retransmit_flag = true;
271 self.wait_flag = true;
272 self.state = FarmState::Wait;
273 actions.push(FarmAction::Discard);
274 }
275 }
276 SeqZone::PositiveWindow => {
277 self.retransmit_flag = true;
279 actions.push(FarmAction::Discard);
280 }
281 SeqZone::NegativeWindow => {
282 actions.push(FarmAction::Discard);
284 }
285 SeqZone::Lockout => {
286 self.lockout_flag = true;
288 self.state = FarmState::Lockout;
289 actions.push(FarmAction::Discard);
290 }
291 },
292 FarmState::Wait => match zone {
293 SeqZone::Expected => {
294 actions.push(FarmAction::Discard);
296 }
297 SeqZone::PositiveWindow => {
298 actions.push(FarmAction::Discard);
300 }
301 SeqZone::NegativeWindow => {
302 actions.push(FarmAction::Discard);
304 }
305 SeqZone::Lockout => {
306 self.lockout_flag = true;
308 self.state = FarmState::Lockout;
309 actions.push(FarmAction::Discard);
310 }
311 },
312 FarmState::Lockout => {
313 actions.push(FarmAction::Discard);
315 }
316 }
317 }
318
319 fn handle_bd_frame<'a>(
321 &mut self,
322 fdu: &'a [u8],
323 actions: &mut FarmActions<'a>,
324 ) {
325 self.farm_b_counter = (self.farm_b_counter + 1) & 0x03;
327 actions.push(FarmAction::Accept { fdu });
328 }
329
330 fn handle_bc_frame(&mut self, command: ControlCommand, _actions: &mut FarmActions<'_>) {
332 match command {
333 ControlCommand::Unlock => {
334 self.farm_b_counter = (self.farm_b_counter + 1) & 0x03;
336 self.retransmit_flag = false;
337 match self.state {
338 FarmState::Open => {
339 }
341 FarmState::Wait => {
342 self.wait_flag = false;
343 self.state = FarmState::Open;
344 }
345 FarmState::Lockout => {
346 self.wait_flag = false;
347 self.lockout_flag = false;
348 self.state = FarmState::Open;
349 }
350 }
351 }
352 ControlCommand::SetVr(new_vr) => {
353 self.farm_b_counter = (self.farm_b_counter + 1) & 0x03;
355 self.retransmit_flag = false;
356 match self.state {
357 FarmState::Open => {
358 self.vr = new_vr;
359 }
360 FarmState::Wait => {
361 self.vr = new_vr;
362 self.wait_flag = false;
363 self.state = FarmState::Open;
364 }
365 FarmState::Lockout => {
366 }
369 }
370 }
371 }
372 }
373
374 fn handle_buffer_release(&mut self, actions: &mut FarmActions<'_>) {
376 match self.state {
377 FarmState::Open => {
378 }
380 FarmState::Wait => {
381 self.wait_flag = false;
383 self.state = FarmState::Open;
384 }
385 FarmState::Lockout => {
386 self.wait_flag = false;
388 }
389 }
390 let _ = actions;
391 }
392
393 fn classify_seq(&self, seq: u8) -> SeqZone {
397 if seq == self.vr {
398 return SeqZone::Expected;
399 }
400
401 let pos_dist = seq.wrapping_sub(self.vr);
403 let neg_dist = self.vr.wrapping_sub(seq);
405
406 if pos_dist > 0 && pos_dist < self.pw {
407 SeqZone::PositiveWindow
408 } else if neg_dist > 0 && neg_dist <= self.nw {
409 SeqZone::NegativeWindow
410 } else {
411 SeqZone::Lockout
412 }
413 }
414}
415
416#[derive(Debug, Copy, Clone, Eq, PartialEq)]
418enum SeqZone {
419 Expected,
421 PositiveWindow,
423 NegativeWindow,
425 Lockout,
427}
428
429#[cfg(test)]
430mod tests {
431 use super::*;
432 use crate::ids::Vcid;
433
434 fn make_farm() -> FarmMachine {
435 FarmMachine::new(
436 FarmConfig::builder()
437 .vcid(Vcid::new(0))
438 .window_width(10)
439 .build(),
440 )
441 }
442
443 #[test]
444 fn accept_in_sequence_frame() {
445 let mut farm = make_farm();
446 let fdu = [1, 2, 3];
447 let mut actions = FarmActions::new();
448
449 farm.handle(
450 FarmEvent::AdFrame {
451 seq: 0,
452 buffer_available: true,
453 fdu: &fdu,
454 },
455 &mut actions,
456 );
457
458 assert_eq!(actions.len(), 1);
459 assert!(matches!(
460 actions.iter().next().unwrap(),
461 FarmAction::Accept { .. }
462 ));
463 assert_eq!(farm.vr(), 1);
464 assert!(!farm.clcw().retransmit());
465 }
466
467 #[test]
468 fn no_buffer_transitions_to_wait() {
469 let mut farm = make_farm();
470 let mut actions = FarmActions::new();
471
472 farm.handle(
473 FarmEvent::AdFrame {
474 seq: 0,
475 buffer_available: false,
476 fdu: &[],
477 },
478 &mut actions,
479 );
480
481 assert_eq!(farm.state(), FarmState::Wait);
482 assert!(farm.clcw().wait());
483 assert!(farm.clcw().retransmit());
484 }
485
486 #[test]
487 fn out_of_window_causes_lockout() {
488 let mut farm = make_farm();
489 let mut actions = FarmActions::new();
490
491 farm.handle(
493 FarmEvent::AdFrame {
494 seq: 200,
495 buffer_available: true,
496 fdu: &[],
497 },
498 &mut actions,
499 );
500
501 assert_eq!(farm.state(), FarmState::Lockout);
502 assert!(farm.clcw().lockout());
503 }
504
505 #[test]
506 fn unlock_resets_from_lockout() {
507 let mut farm = make_farm();
508 let mut actions = FarmActions::new();
509
510 farm.handle(
512 FarmEvent::AdFrame {
513 seq: 200,
514 buffer_available: true,
515 fdu: &[],
516 },
517 &mut actions,
518 );
519 assert_eq!(farm.state(), FarmState::Lockout);
520
521 farm.handle(
523 FarmEvent::BcFrame {
524 command: ControlCommand::Unlock,
525 },
526 &mut actions,
527 );
528
529 assert_eq!(farm.state(), FarmState::Open);
530 assert!(!farm.clcw().lockout());
531 }
532
533 #[test]
534 fn set_vr_updates_sequence() {
535 let mut farm = make_farm();
536 let mut actions = FarmActions::new();
537
538 farm.handle(
539 FarmEvent::BcFrame {
540 command: ControlCommand::SetVr(42),
541 },
542 &mut actions,
543 );
544
545 assert_eq!(farm.vr(), 42);
546 assert_eq!(farm.clcw().report_value(), 42);
547 }
548
549 #[test]
550 fn set_vr_ignored_in_lockout() {
551 let mut farm = make_farm();
552 let mut actions = FarmActions::new();
553
554 farm.handle(
556 FarmEvent::AdFrame {
557 seq: 200,
558 buffer_available: true,
559 fdu: &[],
560 },
561 &mut actions,
562 );
563
564 farm.handle(
566 FarmEvent::BcFrame {
567 command: ControlCommand::SetVr(42),
568 },
569 &mut actions,
570 );
571
572 assert_eq!(farm.state(), FarmState::Lockout);
573 assert_eq!(farm.vr(), 0); }
575
576 #[test]
577 fn bd_frame_accepted_in_all_states() {
578 let mut farm = make_farm();
579 let mut actions = FarmActions::new();
580
581 farm.handle(
583 FarmEvent::AdFrame {
584 seq: 200,
585 buffer_available: true,
586 fdu: &[],
587 },
588 &mut actions,
589 );
590
591 farm.handle(
593 FarmEvent::BdFrame { fdu: &[0xAA] },
594 &mut actions,
595 );
596
597 assert!(matches!(
598 actions.iter().next().unwrap(),
599 FarmAction::Accept { .. }
600 ));
601 }
602
603 #[test]
604 fn farm_b_counter_increments_on_bd_bc() {
605 let mut farm = make_farm();
606 let mut actions = FarmActions::new();
607
608 farm.handle(
609 FarmEvent::BdFrame { fdu: &[] },
610 &mut actions,
611 );
612 assert_eq!(farm.clcw().farm_b_counter(), 1);
613
614 farm.handle(
615 FarmEvent::BcFrame {
616 command: ControlCommand::Unlock,
617 },
618 &mut actions,
619 );
620 assert_eq!(farm.clcw().farm_b_counter(), 2);
621 }
622
623 #[test]
624 fn buffer_release_transitions_wait_to_open() {
625 let mut farm = make_farm();
626 let mut actions = FarmActions::new();
627
628 farm.handle(
630 FarmEvent::AdFrame {
631 seq: 0,
632 buffer_available: false,
633 fdu: &[],
634 },
635 &mut actions,
636 );
637 assert_eq!(farm.state(), FarmState::Wait);
638
639 farm.handle(FarmEvent::BufferRelease, &mut actions);
641
642 assert_eq!(farm.state(), FarmState::Open);
643 assert!(!farm.clcw().wait());
644 }
645
646 #[test]
647 fn positive_window_sets_retransmit() {
648 let mut farm = make_farm();
649 let mut actions = FarmActions::new();
650
651 farm.handle(
653 FarmEvent::AdFrame {
654 seq: 3,
655 buffer_available: true,
656 fdu: &[],
657 },
658 &mut actions,
659 );
660
661 assert_eq!(farm.state(), FarmState::Open);
662 assert!(farm.clcw().retransmit());
663 assert!(matches!(
664 actions.iter().next().unwrap(),
665 FarmAction::Discard
666 ));
667 }
668
669 #[test]
670 fn negative_window_silently_discards() {
671 let mut farm = make_farm();
672 let mut actions = FarmActions::new();
673
674 for i in 0..3u8 {
676 farm.handle(
677 FarmEvent::AdFrame {
678 seq: i,
679 buffer_available: true,
680 fdu: &[],
681 },
682 &mut actions,
683 );
684 }
685 assert_eq!(farm.vr(), 3);
686
687 farm.handle(
689 FarmEvent::AdFrame {
690 seq: 1,
691 buffer_available: true,
692 fdu: &[],
693 },
694 &mut actions,
695 );
696
697 assert_eq!(farm.state(), FarmState::Open);
698 assert!(!farm.clcw().retransmit());
699 assert!(matches!(
700 actions.iter().next().unwrap(),
701 FarmAction::Discard
702 ));
703 }
704
705 #[test]
706 fn clcw_report_action() {
707 let mut farm = make_farm();
708 let mut actions = FarmActions::new();
709
710 farm.handle(FarmEvent::ClcwReport, &mut actions);
711
712 assert!(matches!(
713 actions.iter().next().unwrap(),
714 FarmAction::ClcwReady
715 ));
716 }
717
718 #[test]
719 fn wrapping_sequence_acceptance() {
720 let mut farm = FarmMachine::new(
721 FarmConfig::builder()
722 .vcid(Vcid::new(0))
723 .window_width(10)
724 .build(),
725 );
726 let fdu_buf = [254u8, 255, 0, 1];
727 let mut actions = FarmActions::new();
728
729 farm.handle(
731 FarmEvent::BcFrame {
732 command: ControlCommand::SetVr(254),
733 },
734 &mut actions,
735 );
736
737 for (i, &expected_seq) in fdu_buf.iter().enumerate() {
739 farm.handle(
740 FarmEvent::AdFrame {
741 seq: expected_seq,
742 buffer_available: true,
743 fdu: &fdu_buf[i..i + 1],
744 },
745 &mut actions,
746 );
747 assert!(
748 matches!(
749 actions.iter().next().unwrap(),
750 FarmAction::Accept { .. }
751 ),
752 "should accept seq {expected_seq}"
753 );
754 }
755 assert_eq!(farm.vr(), 2);
756 }
757}