Skip to main content

cc_talk_host/commands/device/
device_commands.rs

1#![allow(dead_code)]
2
3use core::time::Duration;
4
5use cc_talk_core::cc_talk::{
6    BillRouteCode, BillRoutingError, BillValidatorPollResult, BillValidatorPollResultError,
7    BitMask, BitMaskError, ChangerDevice, ChangerError, ChangerFlags, ChangerPollResult,
8    CoinAcceptorPollResult, CurrencyToken, CurrencyTokenError, EscrowFaultCode, EscrowLevelStatus,
9    EscrowOperatingStatus, EscrowServiceStatus, Fault, FaultCode, FirmwareStorageType, Header,
10    HopperDispenseStatus, HopperDispenseValueStatus, HopperFlag, HopperStatus, LampControl,
11    PowerOption, RequestOptionFlags, SorterPath, StackerCycleError, TeachModeStatus,
12    parse_changer_flags_heapless,
13};
14
15use crate::commands::command::{Command, ParseResponseError};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum PollingUnit {
19    Special = 0,
20    Ms = 1,
21    X10Ms = 2,
22    Seconds = 3,
23    Minutes = 4,
24    Hours = 5,
25    Days = 6,
26    Weeks = 7,
27    Months = 8,
28    Years = 9,
29}
30#[derive(Debug)]
31pub struct PollingPriority {
32    pub unit: PollingUnit,
33    pub value: u8,
34}
35
36impl PollingPriority {
37    pub fn as_duration(&self) -> Option<Duration> {
38        let value = self.value as u64;
39
40        let duration = match self.unit {
41            PollingUnit::Special => {
42                return None;
43            }
44            PollingUnit::Ms => Duration::from_millis(value),
45            PollingUnit::X10Ms => Duration::from_millis(value * 10),
46            PollingUnit::Seconds => Duration::from_secs(value),
47            PollingUnit::Minutes => Duration::from_secs(value * 60),
48            PollingUnit::Hours => Duration::from_secs(value * 3600),
49            PollingUnit::Days => Duration::from_secs(value * 86400),
50            PollingUnit::Weeks => Duration::from_secs(value * 604800),
51            PollingUnit::Months => Duration::from_secs(value * 2629746), // ~30.44 days
52            PollingUnit::Years => Duration::from_secs(value * 31556952), // ~365.25 days
53        };
54        Some(duration)
55    }
56}
57
58#[derive(Debug)]
59pub struct RequestPollingPriorityCommand;
60impl Command for RequestPollingPriorityCommand {
61    type Response = PollingPriority;
62
63    fn header(&self) -> Header {
64        Header::RequestPollingPriority
65    }
66
67    fn data(&self) -> &[u8] {
68        &[]
69    }
70
71    fn parse_response(
72        &self,
73        response_payload: &[u8],
74    ) -> Result<Self::Response, ParseResponseError> {
75        match response_payload.len() {
76            2 => {
77                let unit = match response_payload[0] {
78                    0 => PollingUnit::Special,
79                    1 => PollingUnit::Ms,
80                    2 => PollingUnit::X10Ms,
81                    3 => PollingUnit::Seconds,
82                    4 => PollingUnit::Minutes,
83                    5 => PollingUnit::Hours,
84                    6 => PollingUnit::Days,
85                    7 => PollingUnit::Weeks,
86                    8 => PollingUnit::Months,
87                    9 => PollingUnit::Years,
88                    _ => return Err(ParseResponseError::ParseError("Invalid polling unit")),
89                };
90                Ok(PollingPriority {
91                    unit,
92                    value: response_payload[1],
93                })
94            }
95            _ => Err(ParseResponseError::DataLengthMismatch(
96                2,
97                response_payload.len(),
98            )),
99        }
100    }
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104pub enum CoinAcceptorStatus {
105    Ok = 0,
106    CoinReturnMechanismActivated = 1,
107    CoinOnString = 2,
108}
109
110#[derive(Debug)]
111pub struct RequestStatusCommand;
112impl Command for RequestStatusCommand {
113    type Response = CoinAcceptorStatus;
114
115    fn header(&self) -> Header {
116        Header::RequestStatus
117    }
118
119    fn data(&self) -> &[u8] {
120        &[]
121    }
122
123    fn parse_response(
124        &self,
125        response_payload: &[u8],
126    ) -> Result<Self::Response, ParseResponseError> {
127        match response_payload.len() {
128            1 => match response_payload[0] {
129                0 => Ok(CoinAcceptorStatus::Ok),
130                1 => Ok(CoinAcceptorStatus::CoinReturnMechanismActivated),
131                2 => Ok(CoinAcceptorStatus::CoinOnString),
132                _ => Err(ParseResponseError::ParseError("Invalid status")),
133            },
134            _ => Err(ParseResponseError::DataLengthMismatch(
135                1,
136                response_payload.len(),
137            )),
138        }
139    }
140}
141
142#[derive(Debug)]
143pub struct RequestVariableSetCommand;
144impl Command for RequestVariableSetCommand {
145    type Response = ();
146
147    fn header(&self) -> Header {
148        Header::RequestVariableSet
149    }
150
151    fn data(&self) -> &[u8] {
152        &[]
153    }
154
155    /// Device specific
156    fn parse_response(&self, _: &[u8]) -> Result<Self::Response, ParseResponseError> {
157        Ok(())
158    }
159}
160
161#[derive(Debug)]
162pub struct RequestDatabaseVersionCommand;
163impl Command for RequestDatabaseVersionCommand {
164    type Response = u8;
165
166    fn header(&self) -> Header {
167        Header::RequestDatabaseVersion
168    }
169
170    fn data(&self) -> &[u8] {
171        &[]
172    }
173
174    fn parse_response(
175        &self,
176        response_payload: &[u8],
177    ) -> Result<Self::Response, ParseResponseError> {
178        match response_payload.len() {
179            1 => Ok(response_payload[0]),
180            _ => Err(ParseResponseError::DataLengthMismatch(
181                1,
182                response_payload.len(),
183            )),
184        }
185    }
186}
187
188#[derive(Debug, Clone, Copy, PartialEq, Eq)]
189pub struct TestSolenoidsCommand {
190    buffer: u8, // maybe this should be an array of u8?
191}
192impl TestSolenoidsCommand {
193    /// Creates a new TestSolenoidsCommand with the given bitmask.
194    pub fn new(bitmask: u8) -> Self {
195        TestSolenoidsCommand { buffer: bitmask }
196    }
197}
198impl Command for TestSolenoidsCommand {
199    type Response = ();
200
201    fn header(&self) -> Header {
202        Header::TestSolenoids
203    }
204
205    fn data(&self) -> &[u8] {
206        core::slice::from_ref(&self.buffer)
207    }
208
209    /// Replies with ack
210    fn parse_response(&self, _: &[u8]) -> Result<Self::Response, ParseResponseError> {
211        Ok(())
212    }
213}
214
215#[derive(Debug, Clone, Copy, PartialEq, Eq)]
216pub struct OperateMotorsCommand {
217    buffer: u8,
218}
219impl OperateMotorsCommand {
220    /// Creates a new OperateMotorsCommand with the given bitmask.
221    pub fn new(bitmask: u8) -> Self {
222        OperateMotorsCommand { buffer: bitmask }
223    }
224}
225impl Command for OperateMotorsCommand {
226    type Response = ();
227
228    fn header(&self) -> Header {
229        Header::OperateMotors
230    }
231
232    fn data(&self) -> &[u8] {
233        core::slice::from_ref(&self.buffer)
234    }
235
236    /// Replies with ack
237    fn parse_response(&self, _: &[u8]) -> Result<Self::Response, ParseResponseError> {
238        Ok(())
239    }
240}
241
242#[derive(Debug, Clone, Copy, PartialEq, Eq)]
243pub struct TestOutputLinesCommand {
244    buffer: u8, // Maybe this should be an array of u8?
245}
246impl TestOutputLinesCommand {
247    /// Creates a new TestOutputLinesCommand with the given bitmask.
248    pub fn new(bitmask: u8) -> Self {
249        TestOutputLinesCommand { buffer: bitmask }
250    }
251}
252impl Command for TestOutputLinesCommand {
253    type Response = ();
254
255    fn header(&self) -> Header {
256        Header::TestOutputLines
257    }
258
259    fn data(&self) -> &[u8] {
260        core::slice::from_ref(&self.buffer)
261    }
262
263    /// Replies with ack
264    fn parse_response(&self, _: &[u8]) -> Result<Self::Response, ParseResponseError> {
265        Ok(())
266    }
267}
268
269#[derive(Debug, Clone, Copy, PartialEq, Eq)]
270pub struct ReadInputLinesCommand;
271impl Command for ReadInputLinesCommand {
272    type Response = ();
273
274    fn header(&self) -> Header {
275        Header::ReadInputLines
276    }
277
278    fn data(&self) -> &[u8] {
279        &[]
280    }
281
282    /// We can't really make assumptions here, its device specific.
283    fn parse_response(&self, _: &[u8]) -> Result<Self::Response, ParseResponseError> {
284        Ok(())
285    }
286}
287
288#[derive(Debug, Clone, Copy, PartialEq, Eq)]
289pub struct ReadOptoStatesCommand;
290impl Command for ReadOptoStatesCommand {
291    type Response = u8; // Assuming the response is a single byte representing the opto states.
292
293    fn header(&self) -> Header {
294        Header::ReadOptoStates
295    }
296
297    fn data(&self) -> &[u8] {
298        &[]
299    }
300
301    /// We can't really make assumptions here, its device specific.
302    fn parse_response(&self, payload: &[u8]) -> Result<Self::Response, ParseResponseError> {
303        match payload.len() {
304            1 => Ok(payload[0]),
305            2..=usize::MAX => {
306                crate::log::warning!(
307                    "expected size of 1, but got {} instead. Maybe some information got lost.",
308                    payload.len()
309                );
310                Ok(payload[0]) // Assuming the first byte is the opto states.)
311            }
312            _ => Err(ParseResponseError::DataLengthMismatch(1, payload.len())),
313        }
314    }
315}
316
317#[derive(Debug, Clone, Copy, PartialEq, Eq)]
318pub struct LatchOutputLinesCommand {
319    buffer: u8,
320}
321impl Command for LatchOutputLinesCommand {
322    type Response = ();
323
324    fn header(&self) -> Header {
325        Header::LatchOutputLines
326    }
327
328    fn data(&self) -> &[u8] {
329        core::slice::from_ref(&self.buffer)
330    }
331
332    /// Replies with ack
333    fn parse_response(&self, _: &[u8]) -> Result<Self::Response, ParseResponseError> {
334        Ok(())
335    }
336}
337
338#[derive(Debug)]
339pub struct PerformSelfCheckCommand;
340impl Command for PerformSelfCheckCommand {
341    type Response = Fault;
342
343    fn header(&self) -> Header {
344        Header::PerformSelfCheck
345    }
346
347    fn data(&self) -> &[u8] {
348        &[]
349    }
350
351    /// Replies with ack
352    fn parse_response(&self, payload: &[u8]) -> Result<Self::Response, ParseResponseError> {
353        match payload.len() {
354            1 => {
355                let fault_code = FaultCode::try_from(payload[0])
356                    .map_err(|_| ParseResponseError::ParseError("Invalid fault code"))?;
357
358                Ok(Fault::new(fault_code))
359            }
360            2 => {
361                let fault_code = FaultCode::try_from(payload[0])
362                    .map_err(|_| ParseResponseError::ParseError("Invalid fault code"))?;
363                let fault_info = payload[1];
364
365                Ok(Fault::with_info(fault_code, fault_info))
366            }
367            _ => Err(ParseResponseError::DataLengthMismatch(0, payload.len())),
368        }
369    }
370}
371
372#[derive(Debug, Eq, PartialEq)]
373#[cfg_attr(feature = "defmt", derive(defmt::Format))]
374pub struct ModifyInhibitStatusCommand<const N: usize> {
375    buffer: [u8; N],
376}
377impl<const N: usize> ModifyInhibitStatusCommand<N> {
378    pub fn build(mask: BitMask<N>) -> Result<Self, BitMaskError> {
379        Ok(ModifyInhibitStatusCommand {
380            buffer: mask.to_le_bytes::<N>()?,
381        })
382    }
383}
384impl<const N: usize> Command for ModifyInhibitStatusCommand<N> {
385    type Response = ();
386
387    fn header(&self) -> Header {
388        Header::ModifyInhibitStatus
389    }
390
391    fn data(&self) -> &[u8] {
392        &self.buffer
393    }
394
395    /// Replies with ack
396    fn parse_response(&self, payload: &[u8]) -> Result<Self::Response, ParseResponseError> {
397        if payload.is_empty() {
398            Ok(())
399        } else {
400            Err(ParseResponseError::DataLengthMismatch(0, payload.len()))
401        }
402    }
403}
404
405#[derive(Debug)]
406pub struct RequestInhibitStatusCommand<const N: usize>;
407impl<const N: usize> Command for RequestInhibitStatusCommand<N> {
408    type Response = [u8; N];
409
410    fn header(&self) -> Header {
411        Header::RequestInhibitStatus
412    }
413
414    fn data(&self) -> &[u8] {
415        &[]
416    }
417
418    fn parse_response(
419        &self,
420        response_payload: &[u8],
421    ) -> Result<Self::Response, ParseResponseError> {
422        match response_payload.len() {
423            len if len == N => Ok(response_payload.try_into().unwrap()),
424            len if len > N => {
425                crate::log::info!("unexpected response length: expected {}, got {}", N, len);
426                Ok(response_payload[0..len].try_into().unwrap())
427            }
428            _ => Err(ParseResponseError::DataLengthMismatch(
429                4,
430                response_payload.len(),
431            )),
432        }
433    }
434}
435
436#[derive(Debug, Default)]
437pub struct ReadBufferedCreditOrErrorCodeCommand {
438    last_event_counter: u8,
439}
440impl ReadBufferedCreditOrErrorCodeCommand {
441    pub fn new(last_event_counter: u8) -> Self {
442        ReadBufferedCreditOrErrorCodeCommand { last_event_counter }
443    }
444}
445impl Command for ReadBufferedCreditOrErrorCodeCommand {
446    type Response = CoinAcceptorPollResult;
447
448    fn header(&self) -> Header {
449        Header::ReadBufferedCreditOrErrorCodes
450    }
451
452    fn data(&self) -> &[u8] {
453        &[]
454    }
455
456    fn parse_response(&self, payload: &[u8]) -> Result<Self::Response, ParseResponseError> {
457        if payload.is_empty() {
458            return Err(ParseResponseError::DataLengthMismatch(1, payload.len()));
459        }
460        CoinAcceptorPollResult::try_from((payload, self.last_event_counter))
461            .map_err(|_| ParseResponseError::ParseError("Invalid coin acceptor poll result"))
462    }
463}
464
465#[derive(Debug, Eq, PartialEq)]
466#[cfg_attr(feature = "defmt", derive(defmt::Format))]
467pub struct ModifyMasterInhibitStatusCommand<const N: usize> {
468    buffer: [u8; N],
469}
470impl<const N: usize> ModifyMasterInhibitStatusCommand<N> {
471    pub fn build(mask: BitMask<N>) -> Result<Self, BitMaskError> {
472        Ok(ModifyMasterInhibitStatusCommand {
473            buffer: mask.to_le_bytes::<N>()?,
474        })
475    }
476}
477impl<const N: usize> Command for ModifyMasterInhibitStatusCommand<N> {
478    type Response = ();
479
480    fn header(&self) -> Header {
481        Header::ModifyMasterInhibitStatus
482    }
483
484    fn data(&self) -> &[u8] {
485        &self.buffer
486    }
487
488    fn parse_response(&self, payload: &[u8]) -> Result<Self::Response, ParseResponseError> {
489        if payload.is_empty() {
490            Ok(())
491        } else {
492            Err(ParseResponseError::DataLengthMismatch(0, payload.len()))
493        }
494    }
495}
496
497#[derive(Debug)]
498pub struct RequestMasterInhibitStatusCommand<const N: usize>;
499impl<const N: usize> Command for RequestMasterInhibitStatusCommand<N> {
500    type Response = [u8; N];
501
502    fn header(&self) -> Header {
503        Header::RequestMasterInhibitStatus
504    }
505
506    fn data(&self) -> &[u8] {
507        &[]
508    }
509
510    fn parse_response(
511        &self,
512        response_payload: &[u8],
513    ) -> Result<Self::Response, ParseResponseError> {
514        match response_payload.len() {
515            len if len == N => Ok(response_payload
516                .try_into()
517                .map_err(|_| ParseResponseError::ParseError("unable to map to slice"))?),
518            len if len > N => {
519                crate::log::info!("unexpected response length: expected {}, got {}", N, len);
520                Ok(response_payload[0..len]
521                    .try_into()
522                    .map_err(|_| ParseResponseError::ParseError("unable to map to slice"))?)
523            }
524            _ => Err(ParseResponseError::DataLengthMismatch(
525                4,
526                response_payload.len(),
527            )),
528        }
529    }
530}
531
532#[derive(Debug)]
533pub struct RequestInsertionCounterCommand;
534impl Command for RequestInsertionCounterCommand {
535    type Response = u32;
536
537    fn header(&self) -> Header {
538        Header::RequestInsertionCounter
539    }
540
541    fn data(&self) -> &[u8] {
542        &[]
543    }
544
545    fn parse_response(
546        &self,
547        response_payload: &[u8],
548    ) -> Result<Self::Response, ParseResponseError> {
549        match response_payload.len() {
550            3 => Ok(u32::from_le_bytes([
551                response_payload[0],
552                response_payload[1],
553                response_payload[2],
554                0u8,
555            ])),
556            _ => Err(ParseResponseError::DataLengthMismatch(
557                3,
558                response_payload.len(),
559            )),
560        }
561    }
562}
563
564#[derive(Debug)]
565pub struct RequestCreditCounterCommand;
566impl Command for RequestCreditCounterCommand {
567    type Response = u32;
568
569    fn header(&self) -> Header {
570        Header::RequestAcceptCounter
571    }
572
573    fn data(&self) -> &[u8] {
574        &[]
575    }
576
577    fn parse_response(
578        &self,
579        response_payload: &[u8],
580    ) -> Result<Self::Response, ParseResponseError> {
581        match response_payload.len() {
582            3 => Ok(u32::from_le_bytes([
583                response_payload[0],
584                response_payload[1],
585                response_payload[2],
586                0u8,
587            ])),
588            _ => Err(ParseResponseError::DataLengthMismatch(
589                3,
590                response_payload.len(),
591            )),
592        }
593    }
594}
595
596// TODO: Implement this once encryption is supported
597#[derive(Debug)]
598pub struct ModifyEncryptedInhibitAndOverrideRegistersCommand;
599
600#[derive(Debug)]
601pub struct ModifySorterOverrideStatusCommand {
602    buffer: u8,
603}
604impl ModifySorterOverrideStatusCommand {
605    pub fn build(bitmask: BitMask<1>) -> Result<Self, BitMaskError> {
606        Ok(ModifySorterOverrideStatusCommand {
607            buffer: bitmask.to_le_bytes::<1>()?[0],
608        })
609    }
610}
611impl Command for ModifySorterOverrideStatusCommand {
612    type Response = ();
613
614    fn header(&self) -> Header {
615        Header::ModifySorterOverrideStatus
616    }
617
618    fn data(&self) -> &[u8] {
619        core::slice::from_ref(&self.buffer)
620    }
621
622    fn parse_response(&self, payload: &[u8]) -> Result<Self::Response, ParseResponseError> {
623        if payload.is_empty() {
624            Ok(())
625        } else {
626            Err(ParseResponseError::DataLengthMismatch(0, payload.len()))
627        }
628    }
629}
630
631#[derive(Debug)]
632pub struct RequestSorterOverrideStatusCommand;
633impl Command for RequestSorterOverrideStatusCommand {
634    type Response = BitMask<1>;
635
636    fn header(&self) -> Header {
637        Header::RequestSorterOverrideStatus
638    }
639
640    fn data(&self) -> &[u8] {
641        &[]
642    }
643
644    fn parse_response(
645        &self,
646        response_payload: &[u8],
647    ) -> Result<Self::Response, ParseResponseError> {
648        match response_payload.len() {
649            1 => BitMask::<1>::from_le_bytes(response_payload, 8).map_err(|_| {
650                ParseResponseError::ParseError("Invalid sorter override status bitmask")
651            }),
652            _ => Err(ParseResponseError::DataLengthMismatch(
653                1,
654                response_payload.len(),
655            )),
656        }
657    }
658}
659
660#[derive(Debug)]
661pub struct EnterNewPinNumberCommand {
662    pub pin: [u8; 4],
663}
664impl Command for EnterNewPinNumberCommand {
665    type Response = ();
666
667    fn header(&self) -> Header {
668        Header::EnterNewPinNumber
669    }
670
671    fn data(&self) -> &[u8] {
672        &self.pin
673    }
674
675    fn parse_response(
676        &self,
677        response_payload: &[u8],
678    ) -> Result<Self::Response, ParseResponseError> {
679        match response_payload.len() {
680            0 => Ok(()), // No data expected in response
681            _ => Err(ParseResponseError::DataLengthMismatch(
682                0,
683                response_payload.len(),
684            )),
685        }
686    }
687}
688
689#[derive(Debug)]
690pub struct EnterPinNumberCommand {
691    pub pin: [u8; 4],
692}
693impl Command for EnterPinNumberCommand {
694    type Response = ();
695
696    fn header(&self) -> Header {
697        Header::EnterPinNumber
698    }
699
700    fn data(&self) -> &[u8] {
701        &self.pin
702    }
703
704    fn parse_response(
705        &self,
706        response_payload: &[u8],
707    ) -> Result<Self::Response, ParseResponseError> {
708        match response_payload.len() {
709            0 => Ok(()), // No data expected in response
710            _ => Err(ParseResponseError::DataLengthMismatch(
711                0,
712                response_payload.len(),
713            )),
714        }
715    }
716}
717
718#[derive(Debug)]
719pub struct RequestpayoutHighLowStatusCommand;
720impl Command for RequestpayoutHighLowStatusCommand {
721    type Response = (u8, HopperStatus);
722
723    fn header(&self) -> Header {
724        Header::RequestPayoutStatus
725    }
726
727    fn data(&self) -> &[u8] {
728        &[]
729    }
730
731    fn parse_response(&self, payload: &[u8]) -> Result<Self::Response, ParseResponseError> {
732        match payload.len() {
733            1 => Ok((0, HopperStatus::from(payload[0]))),
734            2 => Ok((payload[0], HopperStatus::from(payload[1]))),
735            _ => Err(ParseResponseError::DataLengthMismatch(1, payload.len())),
736        }
737    }
738}
739
740/// The size `N` should be retrieved from [Header::DataStorageAvailability]
741#[derive(Debug)]
742pub struct ReadDataBlockCommand<const N: usize> {
743    pub block_number: u8,
744}
745impl<const N: usize> Command for ReadDataBlockCommand<N> {
746    type Response = [u8; N];
747
748    fn header(&self) -> Header {
749        Header::ReadDataBlock
750    }
751
752    fn data(&self) -> &[u8] {
753        core::slice::from_ref(&self.block_number)
754    }
755
756    fn parse_response(
757        &self,
758        response_payload: &[u8],
759    ) -> Result<Self::Response, ParseResponseError> {
760        match response_payload.len() {
761            len if len == N => Ok(response_payload.try_into().unwrap()),
762            len if len > N => {
763                crate::log::info!("unexpected response length: expected {}, got {}", N, len);
764                Ok(response_payload[0..N].try_into().unwrap())
765            }
766            _ => Err(ParseResponseError::DataLengthMismatch(
767                N,
768                response_payload.len(),
769            )),
770        }
771    }
772}
773
774/// The size `N` should be retrieved from [Header::DataStorageAvailability]
775#[derive(Debug)]
776pub struct WriteDataBlockCommand<const N: usize> {
777    data: heapless::Vec<u8, 256>,
778}
779impl<const N: usize> WriteDataBlockCommand<N> {
780    pub fn new(block_number: u8, buffer: &[u8]) -> Result<Self, ()> {
781        if buffer.len() > N {
782            return Err(());
783        }
784
785        let mut data = heapless::Vec::new();
786        data.push(block_number).map_err(|_| ())?;
787        data.extend_from_slice(buffer).map_err(|_| ())?;
788
789        Ok(WriteDataBlockCommand { data })
790    }
791}
792impl<const N: usize> Command for WriteDataBlockCommand<N> {
793    type Response = ();
794
795    fn header(&self) -> Header {
796        Header::WriteDataBlock
797    }
798
799    fn data(&self) -> &[u8] {
800        self.data.as_slice()
801    }
802
803    fn parse_response(
804        &self,
805        response_payload: &[u8],
806    ) -> Result<Self::Response, ParseResponseError> {
807        if response_payload.is_empty() {
808            Ok(())
809        } else {
810            Err(ParseResponseError::DataLengthMismatch(
811                0,
812                response_payload.len(),
813            ))
814        }
815    }
816}
817
818#[derive(Debug)]
819pub struct RequestOptionFlagsCommand;
820impl Command for RequestOptionFlagsCommand {
821    type Response = RequestOptionFlags;
822
823    fn header(&self) -> Header {
824        Header::RequestOptionFlags
825    }
826
827    fn data(&self) -> &[u8] {
828        &[]
829    }
830
831    // Returns the option flags, you then have to convert them to the specific device type.
832    fn parse_response(
833        &self,
834        response_payload: &[u8],
835    ) -> Result<Self::Response, ParseResponseError> {
836        match response_payload.len() {
837            1 => Ok(RequestOptionFlags::new(response_payload[0])),
838            _ => Err(ParseResponseError::DataLengthMismatch(
839                1,
840                response_payload.len(),
841            )),
842        }
843    }
844}
845
846#[derive(Debug)]
847pub struct RequestCoinPositionCommand {
848    buffer: [u8; 1],
849}
850impl RequestCoinPositionCommand {
851    pub fn new(coin_position: u8) -> Self {
852        RequestCoinPositionCommand {
853            buffer: [coin_position],
854        }
855    }
856}
857impl Command for RequestCoinPositionCommand {
858    type Response = (u8, u8);
859
860    fn header(&self) -> Header {
861        Header::RequestCoinPosition
862    }
863
864    fn data(&self) -> &[u8] {
865        &self.buffer
866    }
867
868    fn parse_response(
869        &self,
870        response_payload: &[u8],
871    ) -> Result<Self::Response, ParseResponseError> {
872        match response_payload.len() {
873            2 => Ok((response_payload[0], response_payload[1])),
874            _ => Err(ParseResponseError::DataLengthMismatch(
875                2,
876                response_payload.len(),
877            )),
878        }
879    }
880}
881
882#[derive(Debug)]
883pub struct PowerManagementControlCommand {
884    buffer: [u8; 1],
885}
886impl PowerManagementControlCommand {
887    pub fn new(power_option: PowerOption) -> Self {
888        PowerManagementControlCommand {
889            buffer: [power_option as u8],
890        }
891    }
892}
893impl Command for PowerManagementControlCommand {
894    type Response = ();
895
896    fn header(&self) -> Header {
897        Header::PowerManagementControl
898    }
899
900    fn data(&self) -> &[u8] {
901        &self.buffer
902    }
903
904    fn parse_response(
905        &self,
906        response_payload: &[u8],
907    ) -> Result<Self::Response, ParseResponseError> {
908        if response_payload.is_empty() {
909            Ok(())
910        } else {
911            Err(ParseResponseError::DataLengthMismatch(
912                0,
913                response_payload.len(),
914            ))
915        }
916    }
917}
918
919#[derive(Debug)]
920pub struct ModifySorterPathCommand {
921    buffer: [u8; 2],
922}
923impl ModifySorterPathCommand {
924    pub fn new(coin_position: u8, sorter: u8) -> Self {
925        ModifySorterPathCommand {
926            buffer: [coin_position, sorter],
927        }
928    }
929}
930impl Command for ModifySorterPathCommand {
931    type Response = ();
932
933    fn header(&self) -> Header {
934        Header::ModifySorterPaths
935    }
936
937    fn data(&self) -> &[u8] {
938        &self.buffer
939    }
940
941    fn parse_response(
942        &self,
943        response_payload: &[u8],
944    ) -> Result<Self::Response, ParseResponseError> {
945        if response_payload.is_empty() {
946            Ok(())
947        } else {
948            Err(ParseResponseError::DataLengthMismatch(
949                0,
950                response_payload.len(),
951            ))
952        }
953    }
954}
955
956#[derive(Debug)]
957pub struct RequestSorterPathCommand {
958    buffer: [u8; 1],
959}
960impl RequestSorterPathCommand {
961    pub fn new(coin_position: u8) -> Self {
962        RequestSorterPathCommand {
963            buffer: [coin_position],
964        }
965    }
966}
967impl Command for RequestSorterPathCommand {
968    type Response = SorterPath;
969
970    fn header(&self) -> Header {
971        Header::RequestSorterPaths
972    }
973
974    fn data(&self) -> &[u8] {
975        &self.buffer
976    }
977
978    fn parse_response(
979        &self,
980        response_payload: &[u8],
981    ) -> Result<Self::Response, ParseResponseError> {
982        match response_payload.len() {
983            1 => Ok(SorterPath::from(response_payload[0])),
984            2..=usize::MAX => {
985                crate::log::info!(
986                    "multipath coin are not yet supported, got {} bytes",
987                    response_payload.len()
988                );
989                Ok(SorterPath::from(response_payload[0]))
990            }
991            _ => Err(ParseResponseError::DataLengthMismatch(
992                1,
993                response_payload.len(),
994            )),
995        }
996    }
997}
998
999#[derive(Debug)]
1000pub struct ModifyPayoutAbsoluteCountCommand {
1001    buffer: [u8; 3],
1002    has_hopper_number: bool,
1003}
1004impl ModifyPayoutAbsoluteCountCommand {
1005    pub fn new(count: u32) -> Self {
1006        ModifyPayoutAbsoluteCountCommand {
1007            buffer: [(count & 0xFF) as u8, ((count >> 8) & 0xFF) as u8, 0u8],
1008            has_hopper_number: false,
1009        }
1010    }
1011
1012    pub fn new_with_hopper(hopper_number: u8, count: u32) -> Self {
1013        ModifyPayoutAbsoluteCountCommand {
1014            buffer: [
1015                hopper_number,
1016                (count & 0xFF) as u8,
1017                ((count >> 8) & 0xFF) as u8,
1018            ],
1019            has_hopper_number: true,
1020        }
1021    }
1022}
1023impl Command for ModifyPayoutAbsoluteCountCommand {
1024    type Response = ();
1025
1026    fn header(&self) -> Header {
1027        Header::ModifyPayoutAbsoluteCount
1028    }
1029
1030    fn data(&self) -> &[u8] {
1031        if self.has_hopper_number {
1032            &self.buffer[..]
1033        } else {
1034            &self.buffer[..2]
1035        }
1036    }
1037
1038    fn parse_response(
1039        &self,
1040        response_payload: &[u8],
1041    ) -> Result<Self::Response, ParseResponseError> {
1042        if response_payload.is_empty() {
1043            Ok(())
1044        } else {
1045            Err(ParseResponseError::DataLengthMismatch(
1046                0,
1047                response_payload.len(),
1048            ))
1049        }
1050    }
1051}
1052
1053#[derive(Debug)]
1054pub struct RequestPayoutAbsoluteCountCommand {
1055    buffer: [u8; 1],
1056    has_hopper_number: bool,
1057}
1058impl RequestPayoutAbsoluteCountCommand {
1059    pub fn new() -> Self {
1060        RequestPayoutAbsoluteCountCommand {
1061            buffer: [0u8],
1062            has_hopper_number: false,
1063        }
1064    }
1065
1066    pub fn new_with_hopper(hopper_number: u8) -> Self {
1067        RequestPayoutAbsoluteCountCommand {
1068            buffer: [hopper_number],
1069            has_hopper_number: true,
1070        }
1071    }
1072}
1073impl Default for RequestPayoutAbsoluteCountCommand {
1074    fn default() -> Self {
1075        Self::new()
1076    }
1077}
1078impl Command for RequestPayoutAbsoluteCountCommand {
1079    type Response = u16;
1080
1081    fn header(&self) -> Header {
1082        Header::RequestPayoutAbsoluteCount
1083    }
1084
1085    fn data(&self) -> &[u8] {
1086        if self.has_hopper_number {
1087            &self.buffer[..]
1088        } else {
1089            &[]
1090        }
1091    }
1092
1093    fn parse_response(
1094        &self,
1095        response_payload: &[u8],
1096    ) -> Result<Self::Response, ParseResponseError> {
1097        match response_payload.len() {
1098            2 => Ok(u16::from_le_bytes([
1099                response_payload[0],
1100                response_payload[1],
1101            ])),
1102            _ => Err(ParseResponseError::DataLengthMismatch(
1103                2,
1104                response_payload.len(),
1105            )),
1106        }
1107    }
1108}
1109
1110// TODO: Implement this
1111#[derive(Debug)]
1112pub struct MeterControlCommand;
1113
1114// TODO: Implement this
1115#[derive(Debug)]
1116pub struct DisplayControlCommand;
1117
1118#[derive(Debug)]
1119pub struct TeachModeControlCommand {
1120    buffer: [u8; 2],
1121    has_orientation: bool,
1122}
1123impl TeachModeControlCommand {
1124    pub fn new(position: u8) -> Self {
1125        TeachModeControlCommand {
1126            buffer: [position, 0u8],
1127            has_orientation: false,
1128        }
1129    }
1130
1131    pub fn new_with_orientation(position: u8, orientation: u8) -> Self {
1132        TeachModeControlCommand {
1133            buffer: [position, orientation],
1134            has_orientation: true,
1135        }
1136    }
1137}
1138impl Command for TeachModeControlCommand {
1139    type Response = ();
1140
1141    fn header(&self) -> Header {
1142        Header::TeachModeControl
1143    }
1144
1145    fn data(&self) -> &[u8] {
1146        if self.has_orientation {
1147            &self.buffer[..]
1148        } else {
1149            &self.buffer[..1]
1150        }
1151    }
1152
1153    fn parse_response(
1154        &self,
1155        response_payload: &[u8],
1156    ) -> Result<Self::Response, ParseResponseError> {
1157        if response_payload.is_empty() {
1158            Ok(())
1159        } else {
1160            Err(ParseResponseError::DataLengthMismatch(
1161                0,
1162                response_payload.len(),
1163            ))
1164        }
1165    }
1166}
1167
1168#[derive(Debug)]
1169pub struct RequestTeachModeStatusCommand {
1170    buffer: [u8; 1],
1171}
1172impl RequestTeachModeStatusCommand {
1173    pub fn new(abort: bool) -> Self {
1174        RequestTeachModeStatusCommand {
1175            buffer: [if abort { 1 } else { 0 }],
1176        }
1177    }
1178}
1179impl Command for RequestTeachModeStatusCommand {
1180    type Response = (u8, TeachModeStatus);
1181
1182    fn header(&self) -> Header {
1183        Header::RequestTeachStatus
1184    }
1185
1186    fn data(&self) -> &[u8] {
1187        &self.buffer
1188    }
1189
1190    // Returns (number of coins, TeachModeStatus)
1191    fn parse_response(
1192        &self,
1193        response_payload: &[u8],
1194    ) -> Result<Self::Response, ParseResponseError> {
1195        match response_payload.len() {
1196            2 => Ok((
1197                response_payload[0],
1198                TeachModeStatus::from(response_payload[1]),
1199            )),
1200            _ => Err(ParseResponseError::DataLengthMismatch(
1201                2,
1202                response_payload.len(),
1203            )),
1204        }
1205    }
1206}
1207
1208#[derive(Debug)]
1209pub struct ConfigurationToEepromCommand;
1210impl Command for ConfigurationToEepromCommand {
1211    type Response = ();
1212
1213    fn header(&self) -> Header {
1214        Header::ConfigurationToEEPROM
1215    }
1216
1217    fn data(&self) -> &[u8] {
1218        &[]
1219    }
1220
1221    /// Replies with ack
1222    fn parse_response(&self, payload: &[u8]) -> Result<Self::Response, ParseResponseError> {
1223        match payload.len() {
1224            0 => Ok(()),
1225            _ => Err(ParseResponseError::DataLengthMismatch(0, payload.len())),
1226        }
1227    }
1228}
1229
1230#[derive(Debug)]
1231pub struct CountersToEepromCommand;
1232impl Command for CountersToEepromCommand {
1233    type Response = ();
1234
1235    fn header(&self) -> Header {
1236        Header::CountersToEEPROM
1237    }
1238
1239    fn data(&self) -> &[u8] {
1240        &[]
1241    }
1242
1243    fn parse_response(&self, payload: &[u8]) -> Result<Self::Response, ParseResponseError> {
1244        match payload.len() {
1245            0 => Ok(()),
1246            _ => Err(ParseResponseError::DataLengthMismatch(0, payload.len())),
1247        }
1248    }
1249}
1250
1251#[derive(Debug)]
1252pub struct RequestRejectCounterCommand;
1253impl Command for RequestRejectCounterCommand {
1254    type Response = u32;
1255
1256    fn header(&self) -> Header {
1257        Header::RequestRejectCounter
1258    }
1259
1260    fn data(&self) -> &[u8] {
1261        &[]
1262    }
1263
1264    fn parse_response(
1265        &self,
1266        response_payload: &[u8],
1267    ) -> Result<Self::Response, ParseResponseError> {
1268        match response_payload.len() {
1269            3 => Ok(u32::from_le_bytes([
1270                response_payload[0],
1271                response_payload[1],
1272                response_payload[2],
1273                0u8,
1274            ])),
1275            _ => Err(ParseResponseError::DataLengthMismatch(
1276                3,
1277                response_payload.len(),
1278            )),
1279        }
1280    }
1281}
1282
1283#[derive(Debug)]
1284pub struct RequestFraudCounterCommand;
1285impl Command for RequestFraudCounterCommand {
1286    type Response = u32;
1287
1288    fn header(&self) -> Header {
1289        Header::RequestFraudCounter
1290    }
1291
1292    fn data(&self) -> &[u8] {
1293        &[]
1294    }
1295
1296    fn parse_response(
1297        &self,
1298        response_payload: &[u8],
1299    ) -> Result<Self::Response, ParseResponseError> {
1300        match response_payload.len() {
1301            3 => Ok(u32::from_le_bytes([
1302                response_payload[0],
1303                response_payload[1],
1304                response_payload[2],
1305                0u8,
1306            ])),
1307            _ => Err(ParseResponseError::DataLengthMismatch(
1308                3,
1309                response_payload.len(),
1310            )),
1311        }
1312    }
1313}
1314
1315// TODO: Implement this
1316#[derive(Debug)]
1317pub struct KeypadControlCommand;
1318
1319#[derive(Debug)]
1320pub struct ModifyDefaultSorterPathCommand {
1321    buffer: [u8; 1],
1322}
1323impl ModifyDefaultSorterPathCommand {
1324    pub fn new(sorter: u8) -> Self {
1325        ModifyDefaultSorterPathCommand { buffer: [sorter] }
1326    }
1327}
1328impl Command for ModifyDefaultSorterPathCommand {
1329    type Response = ();
1330
1331    fn header(&self) -> Header {
1332        Header::ModifyDefaultSorterPath
1333    }
1334
1335    fn data(&self) -> &[u8] {
1336        &self.buffer
1337    }
1338
1339    fn parse_response(
1340        &self,
1341        response_payload: &[u8],
1342    ) -> Result<Self::Response, ParseResponseError> {
1343        if response_payload.is_empty() {
1344            Ok(())
1345        } else {
1346            Err(ParseResponseError::DataLengthMismatch(
1347                0,
1348                response_payload.len(),
1349            ))
1350        }
1351    }
1352}
1353
1354#[derive(Debug)]
1355pub struct RequestDefaultSorterPathCommand;
1356impl Command for RequestDefaultSorterPathCommand {
1357    type Response = SorterPath;
1358
1359    fn header(&self) -> Header {
1360        Header::RequestDefaultSorterPath
1361    }
1362
1363    fn data(&self) -> &[u8] {
1364        &[]
1365    }
1366
1367    fn parse_response(
1368        &self,
1369        response_payload: &[u8],
1370    ) -> Result<Self::Response, ParseResponseError> {
1371        match response_payload.len() {
1372            1 => Ok(SorterPath::from(response_payload[0])),
1373            _ => Err(ParseResponseError::DataLengthMismatch(
1374                1,
1375                response_payload.len(),
1376            )),
1377        }
1378    }
1379}
1380
1381#[derive(Debug)]
1382pub struct ModifyPayoutCapacityCommand {
1383    buffer: [u8; 3],
1384    has_hopper_number: bool,
1385}
1386impl ModifyPayoutCapacityCommand {
1387    pub fn new(capacity: u16) -> Self {
1388        ModifyPayoutCapacityCommand {
1389            buffer: [(capacity & 0xFF) as u8, ((capacity >> 8) & 0xFF) as u8, 0u8],
1390            has_hopper_number: false,
1391        }
1392    }
1393
1394    pub fn new_with_hopper(hopper_number: u8, capacity: u16) -> Self {
1395        ModifyPayoutCapacityCommand {
1396            buffer: [
1397                hopper_number,
1398                (capacity & 0xFF) as u8,
1399                ((capacity >> 8) & 0xFF) as u8,
1400            ],
1401            has_hopper_number: true,
1402        }
1403    }
1404}
1405impl Command for ModifyPayoutCapacityCommand {
1406    type Response = ();
1407
1408    fn header(&self) -> Header {
1409        Header::ModifyPayoutCapacity
1410    }
1411
1412    fn data(&self) -> &[u8] {
1413        if self.has_hopper_number {
1414            &self.buffer[..]
1415        } else {
1416            &self.buffer[..2]
1417        }
1418    }
1419
1420    fn parse_response(
1421        &self,
1422        response_payload: &[u8],
1423    ) -> Result<Self::Response, ParseResponseError> {
1424        if response_payload.is_empty() {
1425            Ok(())
1426        } else {
1427            Err(ParseResponseError::DataLengthMismatch(
1428                0,
1429                response_payload.len(),
1430            ))
1431        }
1432    }
1433}
1434
1435#[derive(Debug)]
1436pub struct RequestPayoutCapacityCommand {
1437    buffer: [u8; 1],
1438    has_hopper_number: bool,
1439}
1440impl RequestPayoutCapacityCommand {
1441    pub fn new() -> Self {
1442        RequestPayoutCapacityCommand {
1443            buffer: [0u8],
1444            has_hopper_number: false,
1445        }
1446    }
1447
1448    pub fn new_with_hopper(hopper_number: u8) -> Self {
1449        RequestPayoutCapacityCommand {
1450            buffer: [hopper_number],
1451            has_hopper_number: true,
1452        }
1453    }
1454}
1455impl Default for RequestPayoutCapacityCommand {
1456    fn default() -> Self {
1457        Self::new()
1458    }
1459}
1460impl Command for RequestPayoutCapacityCommand {
1461    type Response = u16;
1462
1463    fn header(&self) -> Header {
1464        Header::RequestPayoutCapacity
1465    }
1466
1467    fn data(&self) -> &[u8] {
1468        if self.has_hopper_number {
1469            &self.buffer[..]
1470        } else {
1471            &[]
1472        }
1473    }
1474
1475    fn parse_response(
1476        &self,
1477        response_payload: &[u8],
1478    ) -> Result<Self::Response, ParseResponseError> {
1479        match response_payload.len() {
1480            2 => Ok(u16::from_le_bytes([
1481                response_payload[0],
1482                response_payload[1],
1483            ])),
1484            _ => Err(ParseResponseError::DataLengthMismatch(
1485                2,
1486                response_payload.len(),
1487            )),
1488        }
1489    }
1490}
1491
1492#[derive(Debug)]
1493pub struct ModifyCoinIdCommand {
1494    buffer: [u8; 7],
1495}
1496impl ModifyCoinIdCommand {
1497    pub fn new(coin_position: u8, coin_id: &[u8; 6]) -> Self {
1498        ModifyCoinIdCommand {
1499            buffer: [
1500                coin_position,
1501                coin_id[0],
1502                coin_id[1],
1503                coin_id[2],
1504                coin_id[3],
1505                coin_id[4],
1506                coin_id[5],
1507            ],
1508        }
1509    }
1510}
1511impl Command for ModifyCoinIdCommand {
1512    type Response = ();
1513
1514    fn header(&self) -> Header {
1515        Header::ModifyCoinId
1516    }
1517
1518    fn data(&self) -> &[u8] {
1519        &self.buffer
1520    }
1521
1522    fn parse_response(
1523        &self,
1524        response_payload: &[u8],
1525    ) -> Result<Self::Response, ParseResponseError> {
1526        if response_payload.is_empty() {
1527            Ok(())
1528        } else {
1529            Err(ParseResponseError::DataLengthMismatch(
1530                0,
1531                response_payload.len(),
1532            ))
1533        }
1534    }
1535}
1536
1537#[derive(Debug)]
1538pub struct RequestCoinIdCommand {
1539    buffer: [u8; 1],
1540}
1541impl RequestCoinIdCommand {
1542    pub fn new(coin_position: u8) -> Self {
1543        RequestCoinIdCommand {
1544            buffer: [coin_position],
1545        }
1546    }
1547}
1548impl Command for RequestCoinIdCommand {
1549    type Response = CurrencyToken;
1550
1551    fn header(&self) -> Header {
1552        Header::RequestCoinId
1553    }
1554
1555    fn data(&self) -> &[u8] {
1556        &self.buffer
1557    }
1558
1559    fn parse_response(
1560        &self,
1561        response_payload: &[u8],
1562    ) -> Result<Self::Response, ParseResponseError> {
1563        match response_payload.len() {
1564            6 => {
1565                let payload_str = core::str::from_utf8(&response_payload[0..6])
1566                    .map_err(|_| ParseResponseError::ParseError("Invalid UTF-8 in coin ID"))?;
1567
1568                CurrencyToken::build(payload_str)
1569                    .map_err(|_| ParseResponseError::ParseError("Invalid coin ID format"))
1570            }
1571            _ => Err(ParseResponseError::DataLengthMismatch(
1572                6,
1573                response_payload.len(),
1574            )),
1575        }
1576    }
1577}
1578
1579#[derive(Debug)]
1580pub struct UploadWindowDataCommand {
1581    buffer: [u8; 3],
1582    size: u8,
1583}
1584impl UploadWindowDataCommand {
1585    pub fn program_coin(position: u8) -> Self {
1586        UploadWindowDataCommand {
1587            buffer: [0u8, position, 0u8],
1588            size: 2,
1589        }
1590    }
1591
1592    pub fn modify_credit_code(position: u8, credit_code: u8) -> Self {
1593        UploadWindowDataCommand {
1594            buffer: [1u8, position, credit_code],
1595            size: 3,
1596        }
1597    }
1598
1599    pub fn delete_coin(position: u8) -> Self {
1600        UploadWindowDataCommand {
1601            buffer: [2u8, position, 0],
1602            size: 2,
1603        }
1604    }
1605
1606    pub fn program_token(position: u8, data: u8) -> Self {
1607        UploadWindowDataCommand {
1608            buffer: [3u8, position, data],
1609            size: 3,
1610        }
1611    }
1612
1613    pub fn delete_token(position: u8) -> Self {
1614        UploadWindowDataCommand {
1615            buffer: [4, position, 0],
1616            size: 2,
1617        }
1618    }
1619}
1620impl Command for UploadWindowDataCommand {
1621    type Response = ();
1622
1623    fn header(&self) -> Header {
1624        Header::UploadWindowData
1625    }
1626
1627    fn data(&self) -> &[u8] {
1628        &self.buffer[..self.size as usize]
1629    }
1630
1631    fn parse_response(
1632        &self,
1633        response_payload: &[u8],
1634    ) -> Result<Self::Response, ParseResponseError> {
1635        if response_payload.is_empty() {
1636            Ok(())
1637        } else {
1638            Err(ParseResponseError::DataLengthMismatch(
1639                0,
1640                response_payload.len(),
1641            ))
1642        }
1643    }
1644}
1645
1646/// This command is device specific, no validation/parsing is provided.
1647#[derive(Debug)]
1648pub struct DownloadCalibrationDataCommand;
1649impl Command for DownloadCalibrationDataCommand {
1650    type Response = ();
1651
1652    fn header(&self) -> Header {
1653        Header::DownloadCalibrationInfo
1654    }
1655
1656    fn data(&self) -> &[u8] {
1657        &[]
1658    }
1659
1660    fn parse_response(&self, _: &[u8]) -> Result<Self::Response, ParseResponseError> {
1661        Ok(())
1662    }
1663}
1664
1665#[derive(Debug)]
1666pub struct ModifySecuritySettingCommand {
1667    buffer: [u8; 2],
1668}
1669impl ModifySecuritySettingCommand {
1670    pub fn new(position: u8, security_setting: u8) -> Self {
1671        // TODO: use an enum for security_setting
1672        ModifySecuritySettingCommand {
1673            buffer: [position, security_setting],
1674        }
1675    }
1676}
1677impl Command for ModifySecuritySettingCommand {
1678    type Response = ();
1679
1680    fn header(&self) -> Header {
1681        Header::ModifySecuritySetting
1682    }
1683
1684    fn data(&self) -> &[u8] {
1685        &self.buffer
1686    }
1687
1688    fn parse_response(
1689        &self,
1690        response_payload: &[u8],
1691    ) -> Result<Self::Response, ParseResponseError> {
1692        match response_payload.len() {
1693            0 => Ok(()),
1694            _ => Err(ParseResponseError::DataLengthMismatch(
1695                0,
1696                response_payload.len(),
1697            )),
1698        }
1699    }
1700}
1701
1702#[derive(Debug)]
1703pub struct RequestSecuritySettingCommand {
1704    buffer: [u8; 1],
1705}
1706impl RequestSecuritySettingCommand {
1707    pub fn new(position: u8) -> Self {
1708        RequestSecuritySettingCommand { buffer: [position] }
1709    }
1710}
1711impl Command for RequestSecuritySettingCommand {
1712    type Response = u8;
1713
1714    fn header(&self) -> Header {
1715        Header::RequestSecuritySetting
1716    }
1717
1718    fn data(&self) -> &[u8] {
1719        &self.buffer
1720    }
1721
1722    fn parse_response(
1723        &self,
1724        response_payload: &[u8],
1725    ) -> Result<Self::Response, ParseResponseError> {
1726        match response_payload.len() {
1727            1 => Ok(response_payload[0]),
1728            _ => Err(ParseResponseError::DataLengthMismatch(
1729                1,
1730                response_payload.len(),
1731            )),
1732        }
1733    }
1734}
1735
1736#[derive(Debug)]
1737pub struct ModifyBankSelectCommand {
1738    buffer: [u8; 1],
1739}
1740impl ModifyBankSelectCommand {
1741    pub fn new(bank: u8) -> Self {
1742        ModifyBankSelectCommand { buffer: [bank] }
1743    }
1744}
1745impl Command for ModifyBankSelectCommand {
1746    type Response = ();
1747
1748    fn header(&self) -> Header {
1749        Header::ModifyBankSelect
1750    }
1751
1752    fn data(&self) -> &[u8] {
1753        &self.buffer
1754    }
1755
1756    fn parse_response(
1757        &self,
1758        response_payload: &[u8],
1759    ) -> Result<Self::Response, ParseResponseError> {
1760        match response_payload.len() {
1761            0 => Ok(()),
1762            _ => Err(ParseResponseError::DataLengthMismatch(
1763                0,
1764                response_payload.len(),
1765            )),
1766        }
1767    }
1768}
1769
1770#[derive(Debug)]
1771pub struct RequestBankSelectCommand;
1772impl Command for RequestBankSelectCommand {
1773    type Response = u8;
1774
1775    fn header(&self) -> Header {
1776        Header::RequestBankSelect
1777    }
1778
1779    fn data(&self) -> &[u8] {
1780        &[]
1781    }
1782
1783    fn parse_response(
1784        &self,
1785        response_payload: &[u8],
1786    ) -> Result<Self::Response, ParseResponseError> {
1787        match response_payload.len() {
1788            1 => Ok(response_payload[0]),
1789            _ => Err(ParseResponseError::DataLengthMismatch(
1790                1,
1791                response_payload.len(),
1792            )),
1793        }
1794    }
1795}
1796
1797// TODO: Implement this
1798#[derive(Debug)]
1799pub struct HandheldFunctionCommand;
1800
1801#[derive(Debug)]
1802pub struct RequestAlarmCounterCommand;
1803impl Command for RequestAlarmCounterCommand {
1804    type Response = u8;
1805
1806    fn header(&self) -> Header {
1807        Header::RequestAlarmCounter
1808    }
1809
1810    fn data(&self) -> &[u8] {
1811        &[]
1812    }
1813
1814    fn parse_response(
1815        &self,
1816        response_payload: &[u8],
1817    ) -> Result<Self::Response, ParseResponseError> {
1818        match response_payload.len() {
1819            1 => Ok(response_payload[0]),
1820            _ => Err(ParseResponseError::DataLengthMismatch(
1821                1,
1822                response_payload.len(),
1823            )),
1824        }
1825    }
1826}
1827
1828#[derive(Debug)]
1829pub struct ModifyPayoutFloatCommand {
1830    buffer: [u8; 3],
1831    has_hopper_number: bool,
1832}
1833impl ModifyPayoutFloatCommand {
1834    pub fn new(number_of_coins: u16) -> Self {
1835        ModifyPayoutFloatCommand {
1836            buffer: [
1837                (number_of_coins & 0xFF) as u8,
1838                ((number_of_coins >> 8) & 0xFF) as u8,
1839                0u8,
1840            ],
1841            has_hopper_number: false,
1842        }
1843    }
1844
1845    pub fn new_with_hopper(hopper_number: u8, number_of_coins: u16) -> Self {
1846        ModifyPayoutFloatCommand {
1847            buffer: [
1848                hopper_number,
1849                (number_of_coins & 0xFF) as u8,
1850                ((number_of_coins >> 8) & 0xFF) as u8,
1851            ],
1852            has_hopper_number: true,
1853        }
1854    }
1855}
1856impl Command for ModifyPayoutFloatCommand {
1857    type Response = ();
1858
1859    fn header(&self) -> Header {
1860        Header::ModifyPayoutFloat
1861    }
1862
1863    fn data(&self) -> &[u8] {
1864        if self.has_hopper_number {
1865            &self.buffer[..]
1866        } else {
1867            &self.buffer[..2]
1868        }
1869    }
1870
1871    fn parse_response(
1872        &self,
1873        response_payload: &[u8],
1874    ) -> Result<Self::Response, ParseResponseError> {
1875        match response_payload.len() {
1876            0 => Ok(()),
1877            _ => Err(ParseResponseError::DataLengthMismatch(
1878                0,
1879                response_payload.len(),
1880            )),
1881        }
1882    }
1883}
1884
1885#[derive(Debug)]
1886pub struct RequestPayoutFloatCommand {
1887    buffer: [u8; 1],
1888    has_hopper_number: bool,
1889}
1890impl RequestPayoutFloatCommand {
1891    pub fn new() -> Self {
1892        RequestPayoutFloatCommand {
1893            buffer: [0u8],
1894            has_hopper_number: false,
1895        }
1896    }
1897
1898    pub fn new_with_hopper(hopper_number: u8) -> Self {
1899        RequestPayoutFloatCommand {
1900            buffer: [hopper_number],
1901            has_hopper_number: true,
1902        }
1903    }
1904}
1905
1906impl Default for RequestPayoutFloatCommand {
1907    fn default() -> Self {
1908        Self::new()
1909    }
1910}
1911impl Command for RequestPayoutFloatCommand {
1912    type Response = u16;
1913
1914    fn header(&self) -> Header {
1915        Header::RequestPayoutFloat
1916    }
1917
1918    fn data(&self) -> &[u8] {
1919        if self.has_hopper_number {
1920            &self.buffer[..]
1921        } else {
1922            &[]
1923        }
1924    }
1925
1926    fn parse_response(
1927        &self,
1928        response_payload: &[u8],
1929    ) -> Result<Self::Response, ParseResponseError> {
1930        match response_payload.len() {
1931            2 => Ok(u16::from_le_bytes([
1932                response_payload[0],
1933                response_payload[1],
1934            ])),
1935            _ => Err(ParseResponseError::DataLengthMismatch(
1936                2,
1937                response_payload.len(),
1938            )),
1939        }
1940    }
1941}
1942
1943#[derive(Debug)]
1944pub struct RequestThermistorReadingCommand;
1945impl Command for RequestThermistorReadingCommand {
1946    type Response = u8;
1947
1948    fn header(&self) -> Header {
1949        Header::RequestThermistorReading
1950    }
1951
1952    fn data(&self) -> &[u8] {
1953        &[]
1954    }
1955
1956    fn parse_response(
1957        &self,
1958        response_payload: &[u8],
1959    ) -> Result<Self::Response, ParseResponseError> {
1960        match response_payload.len() {
1961            1 => Ok(response_payload[0]),
1962            _ => Err(ParseResponseError::DataLengthMismatch(
1963                1,
1964                response_payload.len(),
1965            )),
1966        }
1967    }
1968}
1969
1970#[derive(Debug)]
1971pub struct EmergencyStopCommand;
1972impl Command for EmergencyStopCommand {
1973    type Response = u8;
1974
1975    fn header(&self) -> Header {
1976        Header::EmergencyStop
1977    }
1978
1979    fn data(&self) -> &[u8] {
1980        &[]
1981    }
1982
1983    fn parse_response(
1984        &self,
1985        response_payload: &[u8],
1986    ) -> Result<Self::Response, ParseResponseError> {
1987        match response_payload.len() {
1988            1 => Ok(response_payload[0]),
1989            _ => Err(ParseResponseError::DataLengthMismatch(
1990                0,
1991                response_payload.len(),
1992            )),
1993        }
1994    }
1995}
1996
1997#[derive(Debug)]
1998pub struct RequestHopperCoinCommand;
1999impl Command for RequestHopperCoinCommand {
2000    type Response = CurrencyToken;
2001
2002    fn header(&self) -> Header {
2003        Header::RequestHopperCoin
2004    }
2005
2006    fn data(&self) -> &[u8] {
2007        &[]
2008    }
2009
2010    fn parse_response(
2011        &self,
2012        response_payload: &[u8],
2013    ) -> Result<Self::Response, ParseResponseError> {
2014        let coin_string = core::str::from_utf8(response_payload)
2015            .map_err(|_| ParseResponseError::ParseError("Invalid UTF-8 in coin string"))?;
2016
2017        CurrencyToken::build(coin_string).map_err(|err| match err {
2018            CurrencyTokenError::InvalidFormat => {
2019                ParseResponseError::ParseError("invalid coin string format")
2020            }
2021            CurrencyTokenError::ValueStringTooSmall => ParseResponseError::BufferTooSmall,
2022            CurrencyTokenError::CoinNotSupportedByDevice => {
2023                ParseResponseError::ParseError("not supported by device")
2024            }
2025        })
2026    }
2027}
2028
2029#[derive(Debug)]
2030pub struct RequestHopperDispenseCountCommand;
2031impl Command for RequestHopperDispenseCountCommand {
2032    type Response = u32;
2033
2034    fn header(&self) -> Header {
2035        Header::RequestHopperDispenseCount
2036    }
2037
2038    fn data(&self) -> &[u8] {
2039        &[]
2040    }
2041
2042    fn parse_response(
2043        &self,
2044        response_payload: &[u8],
2045    ) -> Result<Self::Response, ParseResponseError> {
2046        match response_payload.len() {
2047            3 => Ok(u32::from_le_bytes([
2048                response_payload[0],
2049                response_payload[1],
2050                response_payload[2],
2051                0,
2052            ])),
2053            _ => Err(ParseResponseError::DataLengthMismatch(
2054                3,
2055                response_payload.len(),
2056            )),
2057        }
2058    }
2059}
2060
2061#[derive(Debug)]
2062pub struct DispenseHopperCoinsCommand {
2063    buffer: [u8; 32],
2064    length: u8,
2065}
2066impl DispenseHopperCoinsCommand {
2067    pub fn new(coins: u8) -> Self {
2068        let buffer = [coins; 32];
2069        DispenseHopperCoinsCommand { buffer, length: 1 }
2070    }
2071
2072    pub fn new_with_data(coins: u8, additional_data: &[u8]) -> Self {
2073        const MAX_BUFFER_SIZE: usize = 32;
2074
2075        let mut buffer = [coins; MAX_BUFFER_SIZE];
2076
2077        let data_to_copy = additional_data.len().min(MAX_BUFFER_SIZE);
2078        buffer[..data_to_copy].copy_from_slice(&additional_data[..data_to_copy]);
2079
2080        let command_length = (1 + additional_data.len()).min(MAX_BUFFER_SIZE) as u8;
2081
2082        DispenseHopperCoinsCommand {
2083            buffer,
2084            length: command_length,
2085        }
2086    }
2087}
2088impl Command for DispenseHopperCoinsCommand {
2089    type Response = Option<u8>;
2090
2091    fn header(&self) -> Header {
2092        Header::DispenseHopperCoins
2093    }
2094
2095    fn data(&self) -> &[u8] {
2096        &self.buffer[..self.length as usize]
2097    }
2098
2099    fn parse_response(
2100        &self,
2101        response_payload: &[u8],
2102    ) -> Result<Self::Response, ParseResponseError> {
2103        if response_payload.len() == 1 {
2104            Ok(Some(response_payload[0]))
2105        } else {
2106            Ok(None)
2107        }
2108    }
2109}
2110
2111#[derive(Debug)]
2112pub struct RequestHopperStatusCommand;
2113impl Command for RequestHopperStatusCommand {
2114    type Response = HopperDispenseStatus;
2115
2116    fn header(&self) -> Header {
2117        Header::RequestHopperStatus
2118    }
2119
2120    fn data(&self) -> &[u8] {
2121        &[]
2122    }
2123
2124    fn parse_response(
2125        &self,
2126        response_payload: &[u8],
2127    ) -> Result<Self::Response, ParseResponseError> {
2128        match response_payload.len() {
2129            4 => Ok(HopperDispenseStatus::from([
2130                response_payload[0],
2131                response_payload[1],
2132                response_payload[2],
2133                response_payload[3],
2134            ])),
2135            _ => Err(ParseResponseError::DataLengthMismatch(
2136                4,
2137                response_payload.len(),
2138            )),
2139        }
2140    }
2141}
2142
2143#[derive(Debug)]
2144pub struct ModifyVariableSetCommand<const N: usize> {
2145    buffer: [u8; N],
2146}
2147impl<const N: usize> ModifyVariableSetCommand<N> {
2148    pub fn new(buffer: [u8; N]) -> Self {
2149        ModifyVariableSetCommand { buffer }
2150    }
2151}
2152impl<const N: usize> Command for ModifyVariableSetCommand<N> {
2153    type Response = ();
2154
2155    fn header(&self) -> Header {
2156        Header::ModifyVariableSet
2157    }
2158
2159    fn data(&self) -> &[u8] {
2160        &self.buffer
2161    }
2162
2163    fn parse_response(
2164        &self,
2165        response_payload: &[u8],
2166    ) -> Result<Self::Response, ParseResponseError> {
2167        if response_payload.is_empty() {
2168            Ok(())
2169        } else {
2170            Err(ParseResponseError::DataLengthMismatch(
2171                0,
2172                response_payload.len(),
2173            ))
2174        }
2175    }
2176}
2177
2178#[derive(Debug)]
2179pub struct EnableHopperCommand {
2180    buffer: [u8; 1],
2181}
2182impl EnableHopperCommand {
2183    pub fn new(enable: bool) -> Self {
2184        EnableHopperCommand {
2185            buffer: [if enable { 0xA5 } else { 0 }],
2186        }
2187    }
2188}
2189impl Command for EnableHopperCommand {
2190    type Response = ();
2191
2192    fn header(&self) -> Header {
2193        Header::EnableHopper
2194    }
2195
2196    fn data(&self) -> &[u8] {
2197        &self.buffer
2198    }
2199
2200    fn parse_response(
2201        &self,
2202        response_payload: &[u8],
2203    ) -> Result<Self::Response, ParseResponseError> {
2204        if response_payload.is_empty() {
2205            Ok(())
2206        } else {
2207            Err(ParseResponseError::DataLengthMismatch(
2208                0,
2209                response_payload.len(),
2210            ))
2211        }
2212    }
2213}
2214
2215#[derive(Debug)]
2216pub struct TestHopperCommand;
2217impl Command for TestHopperCommand {
2218    type Response = heapless::Vec<HopperFlag, 21>;
2219
2220    fn header(&self) -> Header {
2221        Header::TestHopper
2222    }
2223
2224    fn data(&self) -> &[u8] {
2225        &[]
2226    }
2227
2228    fn parse_response(
2229        &self,
2230        response_payload: &[u8],
2231    ) -> Result<Self::Response, ParseResponseError> {
2232        match response_payload.len() {
2233            0..=3 => Ok(HopperFlag::parse_hopper_flags_heapless(response_payload)),
2234            _ => Err(ParseResponseError::DataLengthMismatch(
2235                3,
2236                response_payload.len(),
2237            )),
2238        }
2239    }
2240}
2241
2242#[derive(Debug)]
2243pub struct PumpRngCommand<const N: usize> {
2244    buffer: [u8; N],
2245}
2246impl<const N: usize> PumpRngCommand<N> {
2247    pub fn new(buffer: [u8; N]) -> Self {
2248        PumpRngCommand { buffer }
2249    }
2250}
2251impl<const N: usize> Command for PumpRngCommand<N> {
2252    type Response = ();
2253
2254    fn header(&self) -> Header {
2255        Header::PumpRNG
2256    }
2257
2258    fn data(&self) -> &[u8] {
2259        &self.buffer
2260    }
2261
2262    fn parse_response(
2263        &self,
2264        response_payload: &[u8],
2265    ) -> Result<Self::Response, ParseResponseError> {
2266        match response_payload.len() {
2267            0 => Ok(()),
2268            _ => Err(ParseResponseError::DataLengthMismatch(
2269                0,
2270                response_payload.len(),
2271            )),
2272        }
2273    }
2274}
2275
2276#[derive(Debug)]
2277pub struct RequestCipherKeyCommand;
2278impl Command for RequestCipherKeyCommand {
2279    type Response = ();
2280
2281    fn header(&self) -> Header {
2282        Header::RequestCipherKey
2283    }
2284
2285    fn data(&self) -> &[u8] {
2286        &[]
2287    }
2288
2289    /// Device specific command, no validation/parsing is provided.
2290    fn parse_response(&self, _: &[u8]) -> Result<Self::Response, ParseResponseError> {
2291        Ok(())
2292    }
2293}
2294
2295#[derive(Debug, Default)]
2296pub struct ReadBufferedBillEventsCommand {
2297    last_event_counter: u8,
2298}
2299impl ReadBufferedBillEventsCommand {
2300    pub fn new(last_event_counter: u8) -> Self {
2301        ReadBufferedBillEventsCommand { last_event_counter }
2302    }
2303}
2304impl Command for ReadBufferedBillEventsCommand {
2305    type Response = BillValidatorPollResult;
2306
2307    fn header(&self) -> Header {
2308        Header::ReadBufferedBillEvents
2309    }
2310
2311    fn data(&self) -> &[u8] {
2312        &[]
2313    }
2314
2315    fn parse_response(
2316        &self,
2317        response_payload: &[u8],
2318    ) -> Result<Self::Response, ParseResponseError> {
2319        BillValidatorPollResult::try_from((response_payload, self.last_event_counter)).map_err(
2320            |error| match error {
2321                BillValidatorPollResultError::NotEnoughEvents => {
2322                    ParseResponseError::ParseError("unexpected number of events (too few)")
2323                }
2324                BillValidatorPollResultError::TooManyEvents => {
2325                    ParseResponseError::ParseError("unexpected number of events (too many)")
2326                }
2327                BillValidatorPollResultError::InvalidPayload => {
2328                    ParseResponseError::DataLengthMismatch(1357911, response_payload.len())
2329                }
2330            },
2331        )
2332    }
2333}
2334
2335#[derive(Debug)]
2336pub struct ModifyBillIdCommand {
2337    buffer: [u8; 8],
2338}
2339impl ModifyBillIdCommand {
2340    pub fn new(bill_type: u8, bill_string: &[u8; 7]) -> Self {
2341        ModifyBillIdCommand {
2342            buffer: [
2343                bill_type,
2344                bill_string[0],
2345                bill_string[1],
2346                bill_string[2],
2347                bill_string[3],
2348                bill_string[4],
2349                bill_string[5],
2350                bill_string[6],
2351            ],
2352        }
2353    }
2354}
2355impl Command for ModifyBillIdCommand {
2356    type Response = ();
2357
2358    fn header(&self) -> Header {
2359        Header::ModifyBillId
2360    }
2361
2362    fn data(&self) -> &[u8] {
2363        &self.buffer
2364    }
2365
2366    fn parse_response(
2367        &self,
2368        response_payload: &[u8],
2369    ) -> Result<Self::Response, ParseResponseError> {
2370        match response_payload.len() {
2371            0 => Ok(()),
2372            _ => Err(ParseResponseError::DataLengthMismatch(
2373                0,
2374                response_payload.len(),
2375            )),
2376        }
2377    }
2378}
2379
2380#[derive(Debug)]
2381pub struct RequestBillIdCommand {
2382    buffer: [u8; 1],
2383}
2384impl RequestBillIdCommand {
2385    pub fn new(bill_type: u8) -> Self {
2386        RequestBillIdCommand {
2387            buffer: [bill_type],
2388        }
2389    }
2390}
2391impl Command for RequestBillIdCommand {
2392    type Response = CurrencyToken;
2393
2394    fn header(&self) -> Header {
2395        Header::RequestBillId
2396    }
2397
2398    fn data(&self) -> &[u8] {
2399        &self.buffer
2400    }
2401
2402    fn parse_response(
2403        &self,
2404        response_payload: &[u8],
2405    ) -> Result<Self::Response, ParseResponseError> {
2406        match response_payload.len() {
2407            7 => {
2408                let payload_str = core::str::from_utf8(&response_payload[0..7])
2409                    .map_err(|_| ParseResponseError::ParseError("Invalid UTF-8 in bill ID"))?;
2410
2411                CurrencyToken::build(payload_str)
2412                    .map_err(|_| ParseResponseError::ParseError("Invalid bill ID format"))
2413            }
2414            _ => Err(ParseResponseError::DataLengthMismatch(
2415                7,
2416                response_payload.len(),
2417            )),
2418        }
2419    }
2420}
2421
2422// TODO: Implement this, however the scaling factor is hardcoded for now
2423#[derive(Debug)]
2424pub struct RequestCountryScalingFactorCommand;
2425
2426#[derive(Debug)]
2427pub struct RequestBillPositionCommand {
2428    buffer: [u8; 2],
2429}
2430impl RequestBillPositionCommand {
2431    pub fn new(country_code: &str) -> Self {
2432        RequestBillPositionCommand {
2433            buffer: [country_code.as_bytes()[0], country_code.as_bytes()[1]],
2434        }
2435    }
2436}
2437impl Command for RequestBillPositionCommand {
2438    type Response = ();
2439
2440    fn header(&self) -> Header {
2441        Header::RequestBillPosition
2442    }
2443
2444    fn data(&self) -> &[u8] {
2445        &self.buffer
2446    }
2447
2448    fn parse_response(
2449        &self,
2450        response_payload: &[u8],
2451    ) -> Result<Self::Response, ParseResponseError> {
2452        match response_payload.len() {
2453            1..=255 => Ok(()),
2454            _ => Err(ParseResponseError::DataLengthMismatch(
2455                1,
2456                response_payload.len(),
2457            )),
2458        }
2459    }
2460}
2461
2462#[derive(Debug)]
2463pub struct RouteBillCommand {
2464    buffer: [u8; 1],
2465}
2466impl RouteBillCommand {
2467    pub fn new(command: BillRouteCode) -> Self {
2468        RouteBillCommand {
2469            buffer: [command as u8],
2470        }
2471    }
2472}
2473impl Command for RouteBillCommand {
2474    type Response = Option<BillRoutingError>;
2475
2476    fn header(&self) -> Header {
2477        Header::RouteBill
2478    }
2479
2480    fn data(&self) -> &[u8] {
2481        &self.buffer
2482    }
2483
2484    fn parse_response(
2485        &self,
2486        response_payload: &[u8],
2487    ) -> Result<Self::Response, ParseResponseError> {
2488        match response_payload.len() {
2489            0 => Ok(None),
2490            1 => match BillRoutingError::try_from(response_payload[0]) {
2491                Ok(error) => Ok(Some(error)),
2492                Err(_) => Ok(None),
2493            },
2494            _ => Err(ParseResponseError::DataLengthMismatch(
2495                0,
2496                response_payload.len(),
2497            )),
2498        }
2499    }
2500}
2501
2502#[derive(Debug)]
2503pub struct ModifyBillOperatingModeCommand {
2504    buffer: [u8; 1],
2505}
2506impl ModifyBillOperatingModeCommand {
2507    pub fn new(use_stacker: bool, use_escrow: bool) -> Self {
2508        let mut mask = 0u8;
2509        if use_stacker {
2510            mask += 1;
2511        }
2512
2513        if use_escrow {
2514            mask += 2;
2515        }
2516
2517        ModifyBillOperatingModeCommand { buffer: [mask] }
2518    }
2519}
2520impl Command for ModifyBillOperatingModeCommand {
2521    type Response = ();
2522
2523    fn header(&self) -> Header {
2524        Header::ModifyBillOperatingMode
2525    }
2526
2527    fn data(&self) -> &[u8] {
2528        &self.buffer
2529    }
2530
2531    fn parse_response(
2532        &self,
2533        response_payload: &[u8],
2534    ) -> Result<Self::Response, ParseResponseError> {
2535        if response_payload.is_empty() {
2536            Ok(())
2537        } else {
2538            Err(ParseResponseError::DataLengthMismatch(
2539                0,
2540                response_payload.len(),
2541            ))
2542        }
2543    }
2544}
2545
2546#[derive(Debug)]
2547pub struct RequestBillOperatingModeCommand;
2548impl Command for RequestBillOperatingModeCommand {
2549    type Response = (bool, bool); // (use_stacker, use_escrow)
2550
2551    fn header(&self) -> Header {
2552        Header::RequestBillOperatingMode
2553    }
2554
2555    fn data(&self) -> &[u8] {
2556        &[]
2557    }
2558
2559    fn parse_response(
2560        &self,
2561        response_payload: &[u8],
2562    ) -> Result<Self::Response, ParseResponseError> {
2563        match response_payload.len() {
2564            1 => Ok((
2565                response_payload[0] & 0x01 != 0,
2566                response_payload[0] & 0x02 != 0,
2567            )),
2568            _ => Err(ParseResponseError::DataLengthMismatch(
2569                1,
2570                response_payload.len(),
2571            )),
2572        }
2573    }
2574}
2575
2576#[derive(Debug)]
2577pub struct TestLampsCommand {
2578    buffer: [u8; 2],
2579}
2580impl TestLampsCommand {
2581    pub fn new(lamp: u8, command: LampControl) -> Self {
2582        TestLampsCommand {
2583            buffer: [lamp, command.into()],
2584        }
2585    }
2586}
2587impl Command for TestLampsCommand {
2588    type Response = ();
2589
2590    fn header(&self) -> Header {
2591        Header::TestLamps
2592    }
2593
2594    fn data(&self) -> &[u8] {
2595        &self.buffer
2596    }
2597
2598    fn parse_response(
2599        &self,
2600        response_payload: &[u8],
2601    ) -> Result<Self::Response, ParseResponseError> {
2602        match response_payload.len() {
2603            0 => Ok(()),
2604            _ => Err(ParseResponseError::DataLengthMismatch(
2605                0,
2606                response_payload.len(),
2607            )),
2608        }
2609    }
2610}
2611
2612#[derive(Debug)]
2613pub struct RequestIndividualAcceptCounterCommand {
2614    buffer: [u8; 1],
2615}
2616impl RequestIndividualAcceptCounterCommand {
2617    pub fn new(bill_or_coin_type: u8) -> Self {
2618        RequestIndividualAcceptCounterCommand {
2619            buffer: [bill_or_coin_type],
2620        }
2621    }
2622}
2623impl Command for RequestIndividualAcceptCounterCommand {
2624    type Response = u32;
2625
2626    fn header(&self) -> Header {
2627        Header::RequestIndividualAcceptCounter
2628    }
2629
2630    fn data(&self) -> &[u8] {
2631        &self.buffer
2632    }
2633
2634    fn parse_response(
2635        &self,
2636        response_payload: &[u8],
2637    ) -> Result<Self::Response, ParseResponseError> {
2638        match response_payload.len() {
2639            3 => Ok(u32::from_le_bytes([
2640                response_payload[0],
2641                response_payload[1],
2642                response_payload[2],
2643                0u8,
2644            ])),
2645            _ => Err(ParseResponseError::DataLengthMismatch(
2646                3,
2647                response_payload.len(),
2648            )),
2649        }
2650    }
2651}
2652
2653#[derive(Debug)]
2654pub struct ReadOptoVoltagesCommand;
2655impl Command for ReadOptoVoltagesCommand {
2656    type Response = ();
2657
2658    fn header(&self) -> Header {
2659        Header::ReadOptoVoltages
2660    }
2661
2662    fn data(&self) -> &[u8] {
2663        &[]
2664    }
2665
2666    // Device specific, look at your device manual
2667    fn parse_response(
2668        &self,
2669        response_payload: &[u8],
2670    ) -> Result<Self::Response, ParseResponseError> {
2671        match response_payload.len() {
2672            1..=2 => Ok(()),
2673            _ => Err(ParseResponseError::DataLengthMismatch(
2674                1,
2675                response_payload.len(),
2676            )),
2677        }
2678    }
2679}
2680
2681#[derive(Debug)]
2682pub struct PerformStackerCycleCommand;
2683impl Command for PerformStackerCycleCommand {
2684    type Response = Option<StackerCycleError>;
2685
2686    fn header(&self) -> Header {
2687        Header::PerformStackerCycle
2688    }
2689
2690    fn data(&self) -> &[u8] {
2691        &[]
2692    }
2693
2694    // Device specific, no validation/parsing is provided.
2695    fn parse_response(
2696        &self,
2697        response_payload: &[u8],
2698    ) -> Result<Self::Response, ParseResponseError> {
2699        match response_payload.len() {
2700            1 => StackerCycleError::try_from(response_payload[0])
2701                .map(Some)
2702                .map_err(|_| ParseResponseError::ParseError("Invalid stacker cycle error")),
2703            _ => Ok(None),
2704        }
2705    }
2706}
2707
2708#[derive(Debug)]
2709pub struct OperateBiDirectionalMotorsCommand {
2710    buffer: [u8; 3],
2711}
2712impl OperateBiDirectionalMotorsCommand {
2713    pub fn new(motors: u8, directions: u8, speed: u8) -> Self {
2714        OperateBiDirectionalMotorsCommand {
2715            buffer: [motors, directions, speed],
2716        }
2717    }
2718}
2719impl Command for OperateBiDirectionalMotorsCommand {
2720    type Response = ();
2721
2722    fn header(&self) -> Header {
2723        Header::OperateBiDirectionalMotors
2724    }
2725
2726    fn data(&self) -> &[u8] {
2727        &self.buffer
2728    }
2729
2730    fn parse_response(
2731        &self,
2732        response_payload: &[u8],
2733    ) -> Result<Self::Response, ParseResponseError> {
2734        match response_payload.len() {
2735            0 => Ok(()),
2736            _ => Err(ParseResponseError::DataLengthMismatch(
2737                0,
2738                response_payload.len(),
2739            )),
2740        }
2741    }
2742}
2743
2744#[derive(Debug)]
2745pub struct RequestCurrencyRevisionCommand {
2746    buffer: [u8; 2],
2747    has_country_code: bool,
2748}
2749impl RequestCurrencyRevisionCommand {
2750    pub fn new() -> Self {
2751        RequestCurrencyRevisionCommand {
2752            buffer: [0u8, 0u8],
2753            has_country_code: false,
2754        }
2755    }
2756
2757    pub fn build_with_country(country_code: &str) -> Result<Self, ()> {
2758        let bytes = country_code.as_bytes();
2759        if bytes.len() != 2 {
2760            return Err(());
2761        }
2762
2763        Ok(RequestCurrencyRevisionCommand {
2764            buffer: [bytes[0], bytes[1]],
2765            has_country_code: true,
2766        })
2767    }
2768}
2769
2770impl Default for RequestCurrencyRevisionCommand {
2771    fn default() -> Self {
2772        Self::new()
2773    }
2774}
2775impl Command for RequestCurrencyRevisionCommand {
2776    type Response = ();
2777
2778    fn header(&self) -> Header {
2779        Header::RequestCurrencyRevision
2780    }
2781
2782    fn data(&self) -> &[u8] {
2783        if self.has_country_code {
2784            &self.buffer[..]
2785        } else {
2786            &[]
2787        }
2788    }
2789
2790    // Returns ascii string
2791    fn parse_response(&self, _: &[u8]) -> Result<Self::Response, ParseResponseError> {
2792        Ok(())
2793    }
2794}
2795
2796#[derive(Debug)]
2797pub struct UploadBillTablesCommand {
2798    buffer: [u8; 130], // block + line + 128 data
2799    data_len: u8,
2800}
2801impl UploadBillTablesCommand {
2802    pub fn new(block: u8, line: u8, data: &[u8]) -> Result<Self, ()> {
2803        const MAX_PAYLOAD_SIZE: usize = 128;
2804        const COMMAND_BUFFER_SIZE: usize = 130;
2805
2806        if data.len() > MAX_PAYLOAD_SIZE {
2807            return Err(());
2808        }
2809
2810        let mut buffer = [0u8; COMMAND_BUFFER_SIZE];
2811        buffer[0] = block;
2812        buffer[1] = line;
2813
2814        let payload_start = 2;
2815        buffer[payload_start..payload_start + data.len()].copy_from_slice(data);
2816
2817        Ok(UploadBillTablesCommand {
2818            buffer,
2819            data_len: (2 + data.len()) as u8, // header + payload length
2820        })
2821    }
2822}
2823impl Command for UploadBillTablesCommand {
2824    type Response = ();
2825
2826    fn header(&self) -> Header {
2827        Header::UploadBillTables
2828    }
2829
2830    fn data(&self) -> &[u8] {
2831        &self.buffer[..self.data_len as usize]
2832    }
2833
2834    fn parse_response(
2835        &self,
2836        response_payload: &[u8],
2837    ) -> Result<Self::Response, ParseResponseError> {
2838        if response_payload.is_empty() {
2839            Ok(())
2840        } else {
2841            Err(ParseResponseError::DataLengthMismatch(
2842                0,
2843                response_payload.len(),
2844            ))
2845        }
2846    }
2847}
2848
2849#[derive(Debug)]
2850pub struct BeginBillTableUpgradeCommand;
2851impl Command for BeginBillTableUpgradeCommand {
2852    type Response = ();
2853
2854    fn header(&self) -> Header {
2855        Header::BeginBillTableUpgrade
2856    }
2857
2858    fn data(&self) -> &[u8] {
2859        &[]
2860    }
2861
2862    fn parse_response(
2863        &self,
2864        response_payload: &[u8],
2865    ) -> Result<Self::Response, ParseResponseError> {
2866        match response_payload.len() {
2867            0 => Ok(()),
2868            _ => Err(ParseResponseError::DataLengthMismatch(
2869                0,
2870                response_payload.len(),
2871            )),
2872        }
2873    }
2874}
2875
2876#[derive(Debug)]
2877pub struct FinishBillTableUpgradeCommand;
2878impl Command for FinishBillTableUpgradeCommand {
2879    type Response = ();
2880
2881    fn header(&self) -> Header {
2882        Header::FinishBillTableUpgrade
2883    }
2884
2885    fn data(&self) -> &[u8] {
2886        &[]
2887    }
2888
2889    fn parse_response(
2890        &self,
2891        response_payload: &[u8],
2892    ) -> Result<Self::Response, ParseResponseError> {
2893        match response_payload.len() {
2894            0 => Ok(()),
2895            _ => Err(ParseResponseError::DataLengthMismatch(
2896                0,
2897                response_payload.len(),
2898            )),
2899        }
2900    }
2901}
2902
2903#[derive(Debug)]
2904pub struct RequestFirmwareUpgradeCapability {
2905    buffer: [u8; 1],
2906    has_module_identifier: bool,
2907}
2908impl RequestFirmwareUpgradeCapability {
2909    pub fn new() -> Self {
2910        RequestFirmwareUpgradeCapability {
2911            buffer: [0],
2912            has_module_identifier: false,
2913        }
2914    }
2915
2916    pub fn new_with_module_identifier(module_identifier: u8) -> Self {
2917        RequestFirmwareUpgradeCapability {
2918            buffer: [module_identifier],
2919            has_module_identifier: true,
2920        }
2921    }
2922}
2923
2924impl Default for RequestFirmwareUpgradeCapability {
2925    fn default() -> Self {
2926        Self::new()
2927    }
2928}
2929impl Command for RequestFirmwareUpgradeCapability {
2930    type Response = FirmwareStorageType;
2931
2932    fn header(&self) -> Header {
2933        Header::RequestFirmwareUpgradeCapability
2934    }
2935
2936    fn data(&self) -> &[u8] {
2937        if self.has_module_identifier {
2938            &self.buffer[..]
2939        } else {
2940            &[]
2941        }
2942    }
2943
2944    fn parse_response(
2945        &self,
2946        response_payload: &[u8],
2947    ) -> Result<Self::Response, ParseResponseError> {
2948        match response_payload.len() {
2949            1 => FirmwareStorageType::try_from(response_payload[0])
2950                .map_err(|_| ParseResponseError::ParseError("Invalid firmware storage type")),
2951            _ => Err(ParseResponseError::DataLengthMismatch(
2952                0,
2953                response_payload.len(),
2954            )),
2955        }
2956    }
2957}
2958
2959#[derive(Debug)]
2960pub struct UploadFirmwareCommand {
2961    buffer: [u8; 130], // block + line + 128 data
2962    data_len: u8,
2963}
2964impl UploadFirmwareCommand {
2965    pub fn new(block: u8, line: u8, data: &[u8]) -> Result<Self, ()> {
2966        const MAX_PAYLOAD_SIZE: usize = 128;
2967        const COMMAND_BUFFER_SIZE: usize = 130;
2968
2969        if data.len() > MAX_PAYLOAD_SIZE {
2970            return Err(());
2971        }
2972
2973        let mut buffer = [0u8; COMMAND_BUFFER_SIZE];
2974        buffer[0] = block;
2975        buffer[1] = line;
2976
2977        let payload_start = 2;
2978        buffer[payload_start..payload_start + data.len()].copy_from_slice(data);
2979
2980        Ok(UploadFirmwareCommand {
2981            buffer,
2982            data_len: (2 + data.len()) as u8, // header + payload length
2983        })
2984    }
2985}
2986impl Command for UploadFirmwareCommand {
2987    type Response = ();
2988
2989    fn header(&self) -> Header {
2990        Header::UploadBillTables
2991    }
2992
2993    fn data(&self) -> &[u8] {
2994        &self.buffer[..self.data_len as usize]
2995    }
2996
2997    fn parse_response(
2998        &self,
2999        response_payload: &[u8],
3000    ) -> Result<Self::Response, ParseResponseError> {
3001        if response_payload.is_empty() {
3002            Ok(())
3003        } else {
3004            Err(ParseResponseError::DataLengthMismatch(
3005                0,
3006                response_payload.len(),
3007            ))
3008        }
3009    }
3010}
3011
3012#[derive(Debug)]
3013pub struct BeginFirmwareUpgradeCommand {
3014    buffer: [u8; 1],
3015    has_module_identifier: bool,
3016}
3017impl BeginFirmwareUpgradeCommand {
3018    pub fn new() -> Self {
3019        BeginFirmwareUpgradeCommand {
3020            buffer: [0],
3021            has_module_identifier: false,
3022        }
3023    }
3024
3025    pub fn new_with_module_identifier(module_identifier: u8) -> Self {
3026        BeginFirmwareUpgradeCommand {
3027            buffer: [module_identifier],
3028            has_module_identifier: true,
3029        }
3030    }
3031}
3032
3033impl Default for BeginFirmwareUpgradeCommand {
3034    fn default() -> Self {
3035        Self::new()
3036    }
3037}
3038impl Command for BeginFirmwareUpgradeCommand {
3039    type Response = ();
3040
3041    fn header(&self) -> Header {
3042        Header::BeginFirmwareUpgrade
3043    }
3044
3045    fn data(&self) -> &[u8] {
3046        if self.has_module_identifier {
3047            &self.buffer[..]
3048        } else {
3049            &[]
3050        }
3051    }
3052
3053    fn parse_response(&self, payload: &[u8]) -> Result<Self::Response, ParseResponseError> {
3054        match payload.len() {
3055            0 => Ok(()),
3056            _ => Err(ParseResponseError::DataLengthMismatch(0, payload.len())),
3057        }
3058    }
3059}
3060
3061#[derive(Debug)]
3062pub struct FinishFirmwareUpgradeCommand;
3063impl Command for FinishFirmwareUpgradeCommand {
3064    type Response = ();
3065
3066    fn header(&self) -> Header {
3067        Header::FinishFirmwareUpgrade
3068    }
3069
3070    fn data(&self) -> &[u8] {
3071        &[]
3072    }
3073
3074    fn parse_response(
3075        &self,
3076        response_payload: &[u8],
3077    ) -> Result<Self::Response, ParseResponseError> {
3078        match response_payload.len() {
3079            0 => Ok(()),
3080            _ => Err(ParseResponseError::DataLengthMismatch(
3081                0,
3082                response_payload.len(),
3083            )),
3084        }
3085    }
3086}
3087
3088#[derive(Debug)]
3089pub struct SetAcceptLimitCommand {
3090    buffer: [u8; 1],
3091}
3092impl SetAcceptLimitCommand {
3093    pub fn new(limit: u8) -> Self {
3094        SetAcceptLimitCommand { buffer: [limit] }
3095    }
3096}
3097impl Command for SetAcceptLimitCommand {
3098    type Response = ();
3099
3100    fn header(&self) -> Header {
3101        Header::SetAcceptLimit
3102    }
3103
3104    fn data(&self) -> &[u8] {
3105        &self.buffer
3106    }
3107
3108    fn parse_response(
3109        &self,
3110        response_payload: &[u8],
3111    ) -> Result<Self::Response, ParseResponseError> {
3112        match response_payload.len() {
3113            0 => Ok(()),
3114            _ => Err(ParseResponseError::DataLengthMismatch(
3115                0,
3116                response_payload.len(),
3117            )),
3118        }
3119    }
3120}
3121
3122#[derive(Debug)]
3123pub struct DispenseHopperValueCommand {
3124    buffer: [u8; 10],
3125}
3126impl DispenseHopperValueCommand {
3127    pub fn new(coin_value: u16) -> Self {
3128        DispenseHopperValueCommand {
3129            buffer: [
3130                0,
3131                0,
3132                0,
3133                0,
3134                0,
3135                0,
3136                0,
3137                0,
3138                // Value
3139                (coin_value & 0xFF) as u8,
3140                ((coin_value >> 8) & 0xFF) as u8,
3141            ],
3142        }
3143    }
3144
3145    pub fn new_with_security_code(security_code: [u8; 8], coin_value: u16) -> Self {
3146        DispenseHopperValueCommand {
3147            buffer: [
3148                security_code[0],
3149                security_code[1],
3150                security_code[2],
3151                security_code[3],
3152                security_code[4],
3153                security_code[5],
3154                security_code[6],
3155                security_code[7],
3156                // Value
3157                (coin_value & 0xFF) as u8,
3158                ((coin_value >> 8) & 0xFF) as u8,
3159            ],
3160        }
3161    }
3162}
3163impl Command for DispenseHopperValueCommand {
3164    type Response = Option<u8>;
3165
3166    fn header(&self) -> Header {
3167        Header::DispenseHopperValue
3168    }
3169
3170    fn data(&self) -> &[u8] {
3171        &self.buffer
3172    }
3173
3174    fn parse_response(
3175        &self,
3176        response_payload: &[u8],
3177    ) -> Result<Self::Response, ParseResponseError> {
3178        match response_payload.len() {
3179            0 => Ok(None),
3180            1 => Ok(Some(response_payload[0])),
3181            _ => Err(ParseResponseError::DataLengthMismatch(
3182                0,
3183                response_payload.len(),
3184            )),
3185        }
3186    }
3187}
3188
3189#[derive(Debug)]
3190pub struct RequestHopperPollingValueCommand;
3191impl Command for RequestHopperPollingValueCommand {
3192    type Response = HopperDispenseValueStatus;
3193
3194    fn header(&self) -> Header {
3195        Header::RequestHopperPollingValue
3196    }
3197
3198    fn data(&self) -> &[u8] {
3199        &[]
3200    }
3201
3202    fn parse_response(
3203        &self,
3204        response_payload: &[u8],
3205    ) -> Result<Self::Response, ParseResponseError> {
3206        match response_payload.len() {
3207            7 => Ok(HopperDispenseValueStatus::from([
3208                response_payload[0],
3209                response_payload[1],
3210                response_payload[2],
3211                response_payload[3],
3212                response_payload[4],
3213                response_payload[5],
3214                response_payload[6],
3215            ])),
3216            _ => Err(ParseResponseError::DataLengthMismatch(
3217                7,
3218                response_payload.len(),
3219            )),
3220        }
3221    }
3222}
3223
3224#[derive(Debug)]
3225pub struct EmergencyStopValueCommand;
3226impl Command for EmergencyStopValueCommand {
3227    type Response = u16;
3228
3229    fn header(&self) -> Header {
3230        Header::EmergencyStopValue
3231    }
3232
3233    fn data(&self) -> &[u8] {
3234        &[]
3235    }
3236
3237    fn parse_response(
3238        &self,
3239        response_payload: &[u8],
3240    ) -> Result<Self::Response, ParseResponseError> {
3241        match response_payload.len() {
3242            2 => Ok(u16::from_le_bytes([
3243                response_payload[0],
3244                response_payload[1],
3245            ])),
3246            _ => Err(ParseResponseError::DataLengthMismatch(
3247                2,
3248                response_payload.len(),
3249            )),
3250        }
3251    }
3252}
3253
3254#[derive(Debug)]
3255pub struct RequestHopperCoinValueCommand {
3256    buffer: [u8; 1],
3257}
3258impl RequestHopperCoinValueCommand {
3259    pub fn new(coin_type: u8) -> Self {
3260        RequestHopperCoinValueCommand {
3261            buffer: [coin_type],
3262        }
3263    }
3264}
3265impl Command for RequestHopperCoinValueCommand {
3266    type Response = (CurrencyToken, u16); // Currency token, coin value
3267
3268    fn header(&self) -> Header {
3269        Header::RequestHopperCoinValue
3270    }
3271
3272    fn data(&self) -> &[u8] {
3273        &self.buffer
3274    }
3275
3276    fn parse_response(
3277        &self,
3278        response_payload: &[u8],
3279    ) -> Result<Self::Response, ParseResponseError> {
3280        match response_payload.len() {
3281            8 => {
3282                let coin_str = core::str::from_utf8(&response_payload[0..=6])
3283                    .map_err(|_| ParseResponseError::ParseError("Invalid UTF-8 in coin string"))?;
3284                let token = CurrencyToken::build(coin_str).map_err(|err| match err {
3285                    CurrencyTokenError::InvalidFormat => {
3286                        ParseResponseError::ParseError("invalid coin string format")
3287                    }
3288                    CurrencyTokenError::ValueStringTooSmall => ParseResponseError::BufferTooSmall,
3289                    CurrencyTokenError::CoinNotSupportedByDevice => {
3290                        ParseResponseError::ParseError("not supported by device")
3291                    }
3292                })?;
3293                let value = u16::from_le_bytes([response_payload[6], response_payload[7]]);
3294                Ok((token, value))
3295            }
3296            _ => Err(ParseResponseError::DataLengthMismatch(
3297                8,
3298                response_payload.len(),
3299            )),
3300        }
3301    }
3302}
3303
3304#[derive(Debug)]
3305pub struct RequestIndexedHopperDispenseCountCommand {
3306    buffer: [u8; 1],
3307}
3308impl RequestIndexedHopperDispenseCountCommand {
3309    pub fn new(coin_type: u8) -> Self {
3310        RequestIndexedHopperDispenseCountCommand {
3311            buffer: [coin_type],
3312        }
3313    }
3314}
3315impl Command for RequestIndexedHopperDispenseCountCommand {
3316    type Response = u32; // Dispense count
3317
3318    fn header(&self) -> Header {
3319        Header::RequestIndexedHopperDispenseCount
3320    }
3321
3322    fn data(&self) -> &[u8] {
3323        &self.buffer
3324    }
3325
3326    fn parse_response(
3327        &self,
3328        response_payload: &[u8],
3329    ) -> Result<Self::Response, ParseResponseError> {
3330        match response_payload.len() {
3331            3 => Ok(u32::from_le_bytes([
3332                response_payload[0],
3333                response_payload[1],
3334                response_payload[2],
3335                0,
3336            ])),
3337            _ => Err(ParseResponseError::DataLengthMismatch(
3338                3,
3339                response_payload.len(),
3340            )),
3341        }
3342    }
3343}
3344
3345#[derive(Debug)]
3346pub struct ReadBarcodeDataCommand;
3347impl Command for ReadBarcodeDataCommand {
3348    type Response = ();
3349
3350    fn header(&self) -> Header {
3351        Header::ReadBarCodeData
3352    }
3353
3354    fn data(&self) -> &[u8] {
3355        &[]
3356    }
3357
3358    /// ASCII or empty
3359    fn parse_response(&self, _: &[u8]) -> Result<Self::Response, ParseResponseError> {
3360        Ok(())
3361    }
3362}
3363
3364#[derive(Debug)]
3365pub struct RequestMoneyInCommand;
3366impl Command for RequestMoneyInCommand {
3367    type Response = u32;
3368
3369    fn header(&self) -> Header {
3370        Header::RequestMoneyIn
3371    }
3372
3373    fn data(&self) -> &[u8] {
3374        &[]
3375    }
3376
3377    fn parse_response(
3378        &self,
3379        response_payload: &[u8],
3380    ) -> Result<Self::Response, ParseResponseError> {
3381        match response_payload.len() {
3382            4 => Ok(u32::from_le_bytes([
3383                response_payload[0],
3384                response_payload[1],
3385                response_payload[2],
3386                response_payload[3],
3387            ])),
3388            _ => Err(ParseResponseError::DataLengthMismatch(
3389                4,
3390                response_payload.len(),
3391            )),
3392        }
3393    }
3394}
3395
3396#[derive(Debug)]
3397pub struct RequestMoneyOutCommand;
3398impl Command for RequestMoneyOutCommand {
3399    type Response = u32;
3400
3401    fn header(&self) -> Header {
3402        Header::RequestMoneyOut
3403    }
3404
3405    fn data(&self) -> &[u8] {
3406        &[]
3407    }
3408
3409    fn parse_response(
3410        &self,
3411        response_payload: &[u8],
3412    ) -> Result<Self::Response, ParseResponseError> {
3413        match response_payload.len() {
3414            4 => Ok(u32::from_le_bytes([
3415                response_payload[0],
3416                response_payload[1],
3417                response_payload[2],
3418                response_payload[3],
3419            ])),
3420            _ => Err(ParseResponseError::DataLengthMismatch(
3421                4,
3422                response_payload.len(),
3423            )),
3424        }
3425    }
3426}
3427
3428#[derive(Debug)]
3429pub struct ClearMoneyCountersCommand;
3430impl Command for ClearMoneyCountersCommand {
3431    type Response = ();
3432
3433    fn header(&self) -> Header {
3434        Header::ClearMoneyCounters
3435    }
3436
3437    fn data(&self) -> &[u8] {
3438        &[]
3439    }
3440
3441    fn parse_response(
3442        &self,
3443        response_payload: &[u8],
3444    ) -> Result<Self::Response, ParseResponseError> {
3445        if response_payload.is_empty() {
3446            Ok(())
3447        } else {
3448            Err(ParseResponseError::DataLengthMismatch(
3449                0,
3450                response_payload.len(),
3451            ))
3452        }
3453    }
3454}
3455
3456#[derive(Debug)]
3457pub struct PayMoneyOutCommand {
3458    buffer: [u8; 4],
3459}
3460impl PayMoneyOutCommand {
3461    pub fn new(amount: u32) -> Self {
3462        PayMoneyOutCommand {
3463            buffer: amount.to_le_bytes(),
3464        }
3465    }
3466}
3467impl Command for PayMoneyOutCommand {
3468    type Response = ();
3469
3470    fn header(&self) -> Header {
3471        Header::PayMoneyOut
3472    }
3473
3474    fn data(&self) -> &[u8] {
3475        &self.buffer
3476    }
3477
3478    fn parse_response(
3479        &self,
3480        response_payload: &[u8],
3481    ) -> Result<Self::Response, ParseResponseError> {
3482        if response_payload.is_empty() {
3483            Ok(())
3484        } else {
3485            Err(ParseResponseError::DataLengthMismatch(
3486                0,
3487                response_payload.len(),
3488            ))
3489        }
3490    }
3491}
3492
3493#[derive(Debug)]
3494pub struct VerifyMoneyOutCommand;
3495impl Command for VerifyMoneyOutCommand {
3496    type Response = ChangerPollResult;
3497
3498    fn header(&self) -> Header {
3499        Header::VerifyMoneyOut
3500    }
3501
3502    fn data(&self) -> &[u8] {
3503        &[]
3504    }
3505
3506    fn parse_response(
3507        &self,
3508        response_payload: &[u8],
3509    ) -> Result<Self::Response, ParseResponseError> {
3510        match response_payload.len() {
3511            9 => ChangerPollResult::try_from(response_payload)
3512                .map_err(|_| ParseResponseError::ParseError("Invalid ChangerPollResult format")),
3513            _ => Err(ParseResponseError::DataLengthMismatch(
3514                9,
3515                response_payload.len(),
3516            )),
3517        }
3518    }
3519}
3520
3521#[derive(Debug)]
3522pub struct RequestActivityRegisterCommand;
3523impl Command for RequestActivityRegisterCommand {
3524    type Response = heapless::Vec<ChangerFlags, 13>;
3525
3526    fn header(&self) -> Header {
3527        Header::RequestActivityRegister
3528    }
3529
3530    fn data(&self) -> &[u8] {
3531        &[]
3532    }
3533
3534    fn parse_response(
3535        &self,
3536        response_payload: &[u8],
3537    ) -> Result<Self::Response, ParseResponseError> {
3538        match response_payload.len() {
3539            2 => Ok(parse_changer_flags_heapless(response_payload)),
3540            _ => Err(ParseResponseError::DataLengthMismatch(
3541                2,
3542                response_payload.len(),
3543            )),
3544        }
3545    }
3546}
3547
3548#[derive(Debug)]
3549pub struct RequestErrorStatusCommand;
3550impl Command for RequestErrorStatusCommand {
3551    type Response = (ChangerDevice, ChangerError);
3552
3553    fn header(&self) -> Header {
3554        Header::RequestErrorStatus
3555    }
3556
3557    fn data(&self) -> &[u8] {
3558        &[]
3559    }
3560
3561    fn parse_response(
3562        &self,
3563        response_payload: &[u8],
3564    ) -> Result<Self::Response, ParseResponseError> {
3565        match response_payload.len() {
3566            2 => Ok((
3567                ChangerDevice::from(response_payload[0]),
3568                ChangerError::from(response_payload[1]),
3569            )),
3570            _ => Err(ParseResponseError::DataLengthMismatch(
3571                2,
3572                response_payload.len(),
3573            )),
3574        }
3575    }
3576}
3577
3578#[derive(Debug)]
3579pub struct PurgeHopperCommand {
3580    buffer: [u8; 2],
3581}
3582impl PurgeHopperCommand {
3583    // TODO: WH hopper can use the purge hopper command too, from what i experimented they use data
3584    // [0], but maybe [0,0]/[255,0] works too
3585    pub fn new(hopper_number: u8, count: u8) -> Self {
3586        PurgeHopperCommand {
3587            buffer: [hopper_number, count],
3588        }
3589    }
3590}
3591impl Command for PurgeHopperCommand {
3592    type Response = ();
3593
3594    fn header(&self) -> Header {
3595        Header::PurgeHopper
3596    }
3597
3598    fn data(&self) -> &[u8] {
3599        &self.buffer
3600    }
3601
3602    fn parse_response(
3603        &self,
3604        response_payload: &[u8],
3605    ) -> Result<Self::Response, ParseResponseError> {
3606        match response_payload.len() {
3607            0 => Ok(()),
3608            _ => Err(ParseResponseError::DataLengthMismatch(
3609                0,
3610                response_payload.len(),
3611            )),
3612        }
3613    }
3614}
3615
3616#[derive(Debug)]
3617pub struct ModifyHopperBalanceCommand {
3618    buffer: [u8; 3],
3619}
3620impl ModifyHopperBalanceCommand {
3621    pub fn new(hopper_number: u8, balance: u16) -> Self {
3622        ModifyHopperBalanceCommand {
3623            buffer: [
3624                hopper_number,
3625                (balance & 0xFF) as u8,
3626                ((balance >> 8) & 0xFF) as u8,
3627            ],
3628        }
3629    }
3630}
3631impl Command for ModifyHopperBalanceCommand {
3632    type Response = ();
3633
3634    fn header(&self) -> Header {
3635        Header::ModifyHopperBalance
3636    }
3637
3638    fn data(&self) -> &[u8] {
3639        &self.buffer
3640    }
3641
3642    fn parse_response(
3643        &self,
3644        response_payload: &[u8],
3645    ) -> Result<Self::Response, ParseResponseError> {
3646        match response_payload.len() {
3647            0 => Ok(()),
3648            _ => Err(ParseResponseError::DataLengthMismatch(
3649                0,
3650                response_payload.len(),
3651            )),
3652        }
3653    }
3654}
3655
3656#[derive(Debug)]
3657pub struct RequestHopperBalanceCommand {
3658    buffer: [u8; 1],
3659}
3660impl RequestHopperBalanceCommand {
3661    pub fn new(hopper_number: u8) -> Self {
3662        RequestHopperBalanceCommand {
3663            buffer: [hopper_number],
3664        }
3665    }
3666}
3667impl Command for RequestHopperBalanceCommand {
3668    type Response = (CurrencyToken, u16); // Currency token, balance
3669
3670    fn header(&self) -> Header {
3671        Header::RequestHopperBalance
3672    }
3673
3674    fn data(&self) -> &[u8] {
3675        &self.buffer
3676    }
3677
3678    fn parse_response(
3679        &self,
3680        response_payload: &[u8],
3681    ) -> Result<Self::Response, ParseResponseError> {
3682        match response_payload.len() {
3683            8 => {
3684                let coin_str = core::str::from_utf8(&response_payload[0..6])
3685                    .map_err(|_| ParseResponseError::ParseError("Invalid UTF-8 in coin string"))?;
3686                let token = CurrencyToken::build(coin_str).map_err(|err| match err {
3687                    CurrencyTokenError::InvalidFormat => {
3688                        ParseResponseError::ParseError("invalid coin string format")
3689                    }
3690                    CurrencyTokenError::ValueStringTooSmall => ParseResponseError::BufferTooSmall,
3691                    CurrencyTokenError::CoinNotSupportedByDevice => {
3692                        ParseResponseError::ParseError("not supported by device")
3693                    }
3694                })?;
3695                let count = u16::from_le_bytes([response_payload[6], response_payload[7]]);
3696
3697                Ok((token, count))
3698            }
3699            _ => Err(ParseResponseError::DataLengthMismatch(
3700                8,
3701                response_payload.len(),
3702            )),
3703        }
3704    }
3705}
3706
3707#[derive(Debug)]
3708pub struct ModifyCashBoxValueCommand {
3709    buffer: [u8; 4],
3710}
3711impl ModifyCashBoxValueCommand {
3712    pub fn new(value: u32) -> Self {
3713        ModifyCashBoxValueCommand {
3714            buffer: value.to_le_bytes(),
3715        }
3716    }
3717}
3718impl Command for ModifyCashBoxValueCommand {
3719    type Response = ();
3720
3721    fn header(&self) -> Header {
3722        Header::ModifyCashBoxValue
3723    }
3724
3725    fn data(&self) -> &[u8] {
3726        &self.buffer
3727    }
3728
3729    fn parse_response(
3730        &self,
3731        response_payload: &[u8],
3732    ) -> Result<Self::Response, ParseResponseError> {
3733        if response_payload.is_empty() {
3734            Ok(())
3735        } else {
3736            Err(ParseResponseError::DataLengthMismatch(
3737                0,
3738                response_payload.len(),
3739            ))
3740        }
3741    }
3742}
3743
3744#[derive(Debug)]
3745pub struct RequestCashBoxValueCommand;
3746impl Command for RequestCashBoxValueCommand {
3747    type Response = u32;
3748
3749    fn header(&self) -> Header {
3750        Header::RequestCashBoxValue
3751    }
3752
3753    fn data(&self) -> &[u8] {
3754        &[]
3755    }
3756
3757    fn parse_response(
3758        &self,
3759        response_payload: &[u8],
3760    ) -> Result<Self::Response, ParseResponseError> {
3761        match response_payload.len() {
3762            4 => Ok(u32::from_le_bytes([
3763                response_payload[0],
3764                response_payload[1],
3765                response_payload[2],
3766                response_payload[3],
3767            ])),
3768            _ => Err(ParseResponseError::DataLengthMismatch(
3769                4,
3770                response_payload.len(),
3771            )),
3772        }
3773    }
3774}
3775
3776#[derive(Debug)]
3777pub struct ModifyRtcCommand {
3778    buffer: [u8; 4],
3779}
3780impl ModifyRtcCommand {
3781    pub fn new(unix_epoch_seconds: u32) -> Self {
3782        ModifyRtcCommand {
3783            buffer: unix_epoch_seconds.to_le_bytes(),
3784        }
3785    }
3786}
3787impl Command for ModifyRtcCommand {
3788    type Response = ();
3789
3790    fn header(&self) -> Header {
3791        Header::ModifyRealTimeClock
3792    }
3793
3794    fn data(&self) -> &[u8] {
3795        &self.buffer
3796    }
3797
3798    fn parse_response(
3799        &self,
3800        response_payload: &[u8],
3801    ) -> Result<Self::Response, ParseResponseError> {
3802        match response_payload.len() {
3803            0 => Ok(()),
3804            _ => Err(ParseResponseError::DataLengthMismatch(
3805                0,
3806                response_payload.len(),
3807            )),
3808        }
3809    }
3810}
3811
3812#[derive(Debug)]
3813pub struct RequestRtcCommand;
3814impl Command for RequestRtcCommand {
3815    type Response = u32; // Unix epoch seconds
3816
3817    fn header(&self) -> Header {
3818        Header::RequestRealTimeClock
3819    }
3820
3821    fn data(&self) -> &[u8] {
3822        &[]
3823    }
3824
3825    fn parse_response(
3826        &self,
3827        response_payload: &[u8],
3828    ) -> Result<Self::Response, ParseResponseError> {
3829        match response_payload.len() {
3830            4 => Ok(u32::from_le_bytes([
3831                response_payload[0],
3832                response_payload[1],
3833                response_payload[2],
3834                response_payload[3],
3835            ])),
3836            _ => Err(ParseResponseError::DataLengthMismatch(
3837                4,
3838                response_payload.len(),
3839            )),
3840        }
3841    }
3842}
3843
3844// TODO: implement when encryption is supported
3845#[derive(Debug)]
3846pub struct ReadEncryptedEventsCommand;
3847#[derive(Debug)]
3848pub struct RequestEncryptedHopperStatusCommand;
3849#[derive(Debug)]
3850pub struct RequestEncryptedMonetaryIdCommand;
3851
3852#[repr(u8)]
3853#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3854#[cfg_attr(feature = "defmt", derive(defmt::Format))]
3855pub enum DivertMode {
3856    AcceptCoins = 0,
3857    ReturnCoins = 1,
3858}
3859#[derive(Debug)]
3860pub struct OperateEscrowCommand {
3861    buffer: [u8; 1],
3862}
3863impl OperateEscrowCommand {
3864    pub fn new(divert_mode: DivertMode) -> Self {
3865        OperateEscrowCommand {
3866            buffer: [divert_mode as u8],
3867        }
3868    }
3869}
3870impl Command for OperateEscrowCommand {
3871    type Response = ();
3872
3873    fn header(&self) -> Header {
3874        Header::OperateEscrow
3875    }
3876
3877    fn data(&self) -> &[u8] {
3878        &self.buffer
3879    }
3880
3881    fn parse_response(
3882        &self,
3883        response_payload: &[u8],
3884    ) -> Result<Self::Response, ParseResponseError> {
3885        // No response expected, just an empty payload
3886        match response_payload.len() {
3887            0 => Ok(()),
3888            _ => Err(ParseResponseError::DataLengthMismatch(
3889                0,
3890                response_payload.len(),
3891            )),
3892        }
3893    }
3894}
3895
3896#[derive(Debug)]
3897pub struct RequestEscrowStatusCommand;
3898impl Command for RequestEscrowStatusCommand {
3899    type Response = (EscrowOperatingStatus, EscrowLevelStatus, EscrowFaultCode);
3900
3901    fn header(&self) -> Header {
3902        Header::RequestEscrowStatus
3903    }
3904
3905    fn data(&self) -> &[u8] {
3906        &[]
3907    }
3908
3909    fn parse_response(
3910        &self,
3911        response_payload: &[u8],
3912    ) -> Result<Self::Response, ParseResponseError> {
3913        match response_payload.len() {
3914            3 => {
3915                let operating_status = EscrowOperatingStatus::try_from(response_payload[0])
3916                    .map_err(|_| ParseResponseError::ParseError("Invalid EscrowOperatingStatus"))?;
3917                let level_status = EscrowLevelStatus::try_from(response_payload[1])
3918                    .map_err(|_| ParseResponseError::ParseError("Invalid EscrowLevelStatus"))?;
3919                let fault_code = EscrowFaultCode::from(response_payload[2]);
3920
3921                Ok((operating_status, level_status, fault_code))
3922            }
3923            _ => Err(ParseResponseError::DataLengthMismatch(
3924                3,
3925                response_payload.len(),
3926            )),
3927        }
3928    }
3929}
3930
3931#[derive(Debug)]
3932pub struct RequestServiceStatusCommand {
3933    buffer: [u8; 1],
3934}
3935impl RequestServiceStatusCommand {
3936    pub fn new_report() -> Self {
3937        RequestServiceStatusCommand { buffer: [0] }
3938    }
3939
3940    pub fn new_clear_report() -> Self {
3941        RequestServiceStatusCommand { buffer: [1] }
3942    }
3943}
3944impl Command for RequestServiceStatusCommand {
3945    type Response = Option<EscrowServiceStatus>;
3946
3947    fn header(&self) -> Header {
3948        Header::RequestServiceStatus
3949    }
3950
3951    fn data(&self) -> &[u8] {
3952        &self.buffer
3953    }
3954
3955    fn parse_response(
3956        &self,
3957        response_payload: &[u8],
3958    ) -> Result<Self::Response, ParseResponseError> {
3959        match response_payload.len() {
3960            0 => Ok(None),
3961            1 => {
3962                let status = EscrowServiceStatus::try_from(response_payload[0])
3963                    .map_err(|_| ParseResponseError::ParseError("Invalid EscrowServiceStatus"))?;
3964
3965                Ok(Some(status))
3966            }
3967            _ => Err(ParseResponseError::DataLengthMismatch(
3968                1,
3969                response_payload.len(),
3970            )),
3971        }
3972    }
3973}
3974
3975#[derive(Debug)]
3976pub struct ClearCommsStatusVariablesCommand;
3977impl Command for ClearCommsStatusVariablesCommand {
3978    type Response = ();
3979
3980    fn header(&self) -> Header {
3981        Header::ClearCommsStatusVariable
3982    }
3983
3984    fn data(&self) -> &[u8] {
3985        &[]
3986    }
3987
3988    fn parse_response(
3989        &self,
3990        response_payload: &[u8],
3991    ) -> Result<Self::Response, ParseResponseError> {
3992        match response_payload.len() {
3993            0 => Ok(()),
3994            _ => Err(ParseResponseError::DataLengthMismatch(
3995                0,
3996                response_payload.len(),
3997            )),
3998        }
3999    }
4000}
4001
4002#[derive(Debug)]
4003pub struct RequestCommsStatusVariablesCommand;
4004impl Command for RequestCommsStatusVariablesCommand {
4005    type Response = (u8, u8, u8);
4006
4007    fn header(&self) -> Header {
4008        Header::RequestCommsStatusVariables
4009    }
4010
4011    fn data(&self) -> &[u8] {
4012        &[]
4013    }
4014
4015    fn parse_response(
4016        &self,
4017        response_payload: &[u8],
4018    ) -> Result<Self::Response, ParseResponseError> {
4019        match response_payload.len() {
4020            3 => Ok((
4021                response_payload[0],
4022                response_payload[1],
4023                response_payload[2],
4024            )),
4025            _ => Err(ParseResponseError::DataLengthMismatch(
4026                3,
4027                response_payload.len(),
4028            )),
4029        }
4030    }
4031}