dcc_rs/packets/
service_mode.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! This module provides types and serialisers for each "service mode"
6//! packet type defined by the NMRA standard.
7//!
8//! <https://www.nmra.org/sites/default/files/standards/sandrp/pdf/S-9.2.3_2012_07.pdf>
9
10use super::{Error, Result, SerialiseBuffer};
11
12#[derive(Copy, Clone)]
13#[allow(missing_docs)]
14pub enum Operation {
15    Verify,
16    Write,
17}
18
19/// "A packet sequence sent to guarantee the contents of the page register"
20pub struct PagePreset;
21
22impl PagePreset {
23    /// Serialise the Instruction packet into the provided bufffer. Returns the
24    /// number of bits written or an `Error::TooLong` if the buffer has
25    /// insufficient capacity
26    pub fn serialise(&self, buf: &mut SerialiseBuffer) -> Result<usize> {
27        super::serialise(&[0b01111101, 0b00000001, 0b01111100], buf)
28    }
29}
30
31/// Instruction types supported by the `Instruction` packet:
32/// * `VerifyByte`: decoder compares its recorded CV value against the provided
33/// data byte and responds with an acknowledgement if they match
34/// * `WriteCvByte`: decoder writes the provided data byte into the specified
35/// CV slot and may respond with an acknowledgement on successful write
36/// * `VerifyCvBit`: Compare the given bit with the bit in the specified
37/// position within the CV and repond with an acknowledgement if they match
38/// * `WriteCvBit`: Write the given bit into the specified position within the
39/// specified CV. Decoder may respond with an acknowledgement on success
40#[derive(Copy, Clone)]
41#[allow(missing_docs)]
42pub enum InstructionType {
43    WriteCvBit { offset: u8, value: bool },
44    VerifyCvBit { offset: u8, value: bool },
45    WriteCvByte { value: u8 },
46    VerifyCvByte { value: u8 },
47}
48
49/// The `Instruction` service-mode packet instructs the decoder to write or
50/// verify the specified 10-bit CV address against the provided data byte
51pub struct Instruction {
52    typ: InstructionType,
53    cv_address: u16,
54}
55
56impl Instruction {
57    /// Create a builder for the Instruction packet
58    pub fn builder() -> InstructionBuilder {
59        InstructionBuilder::default()
60    }
61
62    /// Serialise the Instruction packet into the provided bufffer. Returns the
63    /// number of bits written or an `Error::TooLong` if the buffer has
64    /// insufficient capacity
65    pub fn serialise(&self, buf: &mut SerialiseBuffer) -> Result<usize> {
66        // write the first two bits of CV address into this byte now and fill
67        // in the packet type later
68        let mut type_and_start_of_address = 0x70;
69        type_and_start_of_address |= (self.cv_address >> 8) as u8;
70
71        // Pull out the lower 8 bits of the CV address
72        let rest_of_address = (self.cv_address & 0x00ff) as u8;
73
74        // Calculate the "data" byte: in "byte" modes this is simply the
75        // provided data byte; in "bit" modes this is a combination of offset
76        // and value
77        #[allow(clippy::unusual_byte_groupings)]
78        let data = match self.typ {
79            InstructionType::WriteCvBit { offset, value } => {
80                type_and_start_of_address |= 0b0000_10_00;
81                // padding - 1=write - data - offset
82                let mut data = 0b111_1_0000;
83                data |= offset;
84                data |= (value as u8) << 3;
85                data
86            }
87            InstructionType::VerifyCvBit { offset, value } => {
88                type_and_start_of_address |= 0b0000_10_00;
89                // padding - 0=verify - data - offset
90                #[allow(clippy::unusual_byte_groupings)]
91                let mut data = 0b111_0_0000;
92                data |= offset;
93                data |= (value as u8) << 3;
94                data
95            }
96            InstructionType::WriteCvByte { value } => {
97                type_and_start_of_address |= 0b0000_11_00;
98                value
99            }
100            InstructionType::VerifyCvByte { value } => {
101                type_and_start_of_address |= 0b0000_01_00;
102                value
103            }
104        };
105
106        super::serialise(
107            &[
108                type_and_start_of_address,
109                rest_of_address,
110                data,
111                type_and_start_of_address ^ rest_of_address ^ data,
112            ],
113            buf,
114        )
115    }
116}
117
118/// Builder struct for Instruction packets. Ensures that only valid Instructions
119/// are created
120#[derive(Default)]
121pub struct InstructionBuilder {
122    cv_address: Option<u16>,
123    typ: Option<InstructionType>,
124}
125
126impl InstructionBuilder {
127    /// Set the address. Returns `Error::InvalidAddress` if the provided CV
128    /// addresss does not fit into 10 bits.
129    ///
130    /// From the standard: "The configuration variable being addressed is the
131    /// provided 10-bit address plus 1", so this method subtracts 1 from the
132    /// supplied CV number in order to determine its address.
133    pub fn cv_address(&mut self, cv_address: u16) -> Result<&mut Self> {
134        if 0 < cv_address && cv_address < 0x0400 {
135            self.cv_address = Some(cv_address - 1);
136            Ok(self)
137        } else {
138            Err(Error::InvalidAddress)
139        }
140    }
141
142    /// Create a `WriteCvByte` packet with the provided byte value
143    pub fn write_byte(&mut self, value: u8) -> &mut Self {
144        self.typ = Some(InstructionType::WriteCvByte { value });
145        self
146    }
147
148    /// Create a `VerifyCvByte` packet with the provided byte value
149    pub fn verify_byte(&mut self, value: u8) -> &mut Self {
150        self.typ = Some(InstructionType::VerifyCvByte { value });
151        self
152    }
153
154    /// Create a `WriteCvBit` packet with the provided bit offset and value
155    pub fn write_bit(&mut self, offset: u8, value: bool) -> Result<&mut Self> {
156        if offset < 0x08 {
157            self.typ = Some(InstructionType::WriteCvBit { offset, value });
158            Ok(self)
159        } else {
160            Err(Error::InvalidOffset)
161        }
162    }
163
164    /// Create a `VerifyCvBit` packet with the provided bit offset and value
165    pub fn verify_bit(&mut self, offset: u8, value: bool) -> Result<&mut Self> {
166        if offset < 0x08 {
167            self.typ = Some(InstructionType::VerifyCvBit { offset, value });
168            Ok(self)
169        } else {
170            Err(Error::InvalidOffset)
171        }
172    }
173
174    /// Validate that all fields are present and return an Instruction packet
175    pub fn build(&mut self) -> Result<Instruction> {
176        Ok(Instruction {
177            typ: self.typ.ok_or(Error::MissingField)?,
178            cv_address: self.cv_address.ok_or(Error::MissingField)?,
179        })
180    }
181}
182
183/// `AddressOnly` instructs the decoder to set its short-mode address to the
184/// provided value and to clear its extended addressing and consist CVs
185#[allow(missing_docs)]
186pub enum AddressOnly {
187    Write { address: u8 },
188    Verify { address: u8 },
189}
190
191impl AddressOnly {
192    /// Create a packet instructing the decoder to write the specified address
193    /// into CV1. The decoder may respond with an acknowledgement on success.
194    pub fn write(address: u8) -> Result<AddressOnly> {
195        if address < 0x7f {
196            Ok(AddressOnly::Write { address })
197        } else {
198            Err(Error::InvalidAddress)
199        }
200    }
201
202    /// Create a packet instructing the decoder to verify the address stored in
203    /// CV1. The decoder must respond with an acknowledgement if they match.
204    pub fn verify(address: u8) -> Result<AddressOnly> {
205        if address < 0x7f {
206            Ok(AddressOnly::Verify { address })
207        } else {
208            Err(Error::InvalidAddress)
209        }
210    }
211
212    /// Serialise the Instruction packet into the provided bufffer. Returns the
213    /// number of bits written or an `Error::TooLong` if the buffer has
214    /// insufficient capacity
215    pub fn serialise(&self, buf: &mut SerialiseBuffer) -> Result<usize> {
216        let mut instr = 0b0111_0000;
217        let address = match self {
218            AddressOnly::Write { address } => {
219                instr |= 0b0000_1000;
220                *address
221            }
222            AddressOnly::Verify { address } => *address,
223        };
224        super::serialise(&[instr, address, instr ^ address], buf)
225    }
226}
227
228/// The `PhysicalRegister` operation instructs the decoder to update or verify
229/// the value stored in each of the eight "physical registers". These correspond
230/// to various CV slots depending on whether it is a locomotove or an accessory
231/// decoder.
232pub struct PhysicalRegister {
233    operation: Operation,
234    register: u8,
235    value: u8,
236}
237
238impl PhysicalRegister {
239    /// Address (CV 1)
240    pub const ADDRESS: u8 = 1;
241    /// Start Voltage (CV 2)
242    pub const START_VOLTAGE: u8 = 2;
243    /// Acceleration (CV 3)
244    pub const ACCELERATION: u8 = 3;
245    /// Deceleration (CV 4)
246    pub const DECELERATION: u8 = 4;
247    /// Basic configuration register (CV 29)
248    pub const BASIC_CONFIGURATION_REGISTER: u8 = 5;
249    /// Reserved for page register
250    pub const RESERVED_FOR_PAGE_REGISTER: u8 = 6;
251    /// Version number (CV 7)
252    pub const VERSION_NUMBER: u8 = 7;
253    /// Manufacturer ID (CV 8)
254    pub const MANUFACTURER_ID: u8 = 8;
255
256    /// Builder for `PhysicalRegister`
257    pub fn builder() -> PhysicalRegisterBuilder {
258        PhysicalRegisterBuilder::default()
259    }
260
261    /// Serialise the PhysicalRegister packet into the provided bufffer. Returns
262    /// the number of bits written or an `Error::TooLong` if the buffer has
263    /// insufficient capacity
264    pub fn serialise(&self, buf: &mut SerialiseBuffer) -> Result<usize> {
265        let mut instr = 0b0111_0000;
266
267        if let Operation::Write = self.operation {
268            instr |= 0b0000_1000;
269        }
270
271        instr |= self.register;
272
273        super::serialise(&[instr, self.value, instr ^ self.value], buf)
274    }
275}
276
277/// Builder struct for the `PhysicalRegister` packet
278#[derive(Default)]
279pub struct PhysicalRegisterBuilder {
280    operation: Option<Operation>,
281    register: Option<u8>,
282    value: Option<u8>,
283}
284
285impl PhysicalRegisterBuilder {
286    /// Sets the `Operation` (verify/write mode) to be performed on the register
287    pub fn operation(&mut self, operation: Operation) -> &mut Self {
288        self.operation = Some(operation);
289        self
290    }
291
292    /// Sets the register address. Valid registers are numbered 1-8,
293    /// corresponding to raw addresses 0-7. Returns `Error::InvalidAddress` for
294    /// values outside this range
295    pub fn register(&mut self, register: u8) -> Result<&mut Self> {
296        if 1 < register && register <= 8 {
297            self.register = Some(register - 1);
298            Ok(self)
299        } else {
300            Err(Error::InvalidAddress)
301        }
302    }
303
304    /// The 8-bit value to use for the operation
305    pub fn value(&mut self, value: u8) -> &mut Self {
306        self.value = Some(value);
307        self
308    }
309
310    /// Build a `PhysicalRegister` packet, returning `Error::MissingField` if
311    /// any of the required fields are missing
312    pub fn build(&mut self) -> Result<PhysicalRegister> {
313        Ok(PhysicalRegister {
314            operation: self.operation.ok_or(Error::MissingField)?,
315            register: self.register.ok_or(Error::MissingField)?,
316            value: self.value.ok_or(Error::MissingField)?,
317        })
318    }
319}
320
321/// Reset decoder to factory-default condition
322pub struct FactoryReset;
323
324impl FactoryReset {
325    /// Serialise the PhysicalRegister packet into the provided bufffer. Returns
326    /// the number of bits written or an `Error::TooLong` if the buffer has
327    /// insufficient capacity
328    pub fn serialise(&self, buf: &mut SerialiseBuffer) -> Result<usize> {
329        super::serialise(&[0b01111111, 0b00001000, 0b01110111], buf)
330    }
331}
332
333/// Query an older decoder to verify its address
334pub struct AddressQuery {
335    address: u8,
336}
337
338impl AddressQuery {
339    /// Create an `AddressQuery` packet for the given address
340    pub fn address(address: u8) -> AddressQuery {
341        AddressQuery { address }
342    }
343
344    /// Serialise the PhysicalRegister packet into the provided bufffer. Returns
345    /// the number of bits written or an `Error::TooLong` if the buffer has
346    /// insufficient capacity
347    pub fn serialise(&self, buf: &mut SerialiseBuffer) -> Result<usize> {
348        let instr = 0b11111001;
349        super::serialise(&[self.address, instr, self.address ^ instr], buf)
350    }
351}
352
353/// Instruct any decoder not matching the given address to ignore any subsequent
354/// service-mode packets
355pub struct DecoderLock {
356    address: u8,
357}
358
359impl DecoderLock {
360    /// Builder for DecoderLock packet
361    pub fn builder() -> DecoderLockBuilder {
362        DecoderLockBuilder::default()
363    }
364
365    /// Serialise the PhysicalRegister packet into the provided bufffer. Returns
366    /// the number of bits written or an `Error::TooLong` if the buffer has
367    /// insufficient capacity
368    pub fn serialise(&self, buf: &mut SerialiseBuffer) -> Result<usize> {
369        let instr = 0b11111001;
370        super::serialise(&[0, instr, self.address, self.address ^ instr], buf)
371    }
372}
373
374/// Builder for DecoderLock packet
375#[derive(Default)]
376pub struct DecoderLockBuilder {
377    address: Option<u8>,
378}
379
380impl DecoderLockBuilder {
381    /// Set the address of the DecoderLock packet. Only short-mode (i.e. 7-bit)
382    /// addresses are supported. Returns an `Error::InvalidAddress` if the
383    /// supplied address does not fit into 7 bits.
384    pub fn address(&mut self, address: u8) -> Result<&mut Self> {
385        if address < 0x7f {
386            self.address = Some(address);
387            Ok(self)
388        } else {
389            Err(Error::InvalidAddress)
390        }
391    }
392
393    /// Build the DecoderLock packet. Returns `Error::MissingField` if the
394    /// address has not been set
395    pub fn build(&mut self) -> Result<DecoderLock> {
396        Ok(DecoderLock {
397            address: self.address.ok_or(Error::MissingField)?,
398        })
399    }
400}
401
402#[cfg(test)]
403mod test {
404    use super::*;
405    use crate::packets::test::print_chunks;
406    use bitvec::prelude::*;
407
408    #[test]
409    fn serialise_instruction_packet_write_byte() {
410        // [    preamble    ] S      WWAA SAAA A AAA A_S_DD DD_DD DD_S_E EEE_E EEES
411        // 1111 1111 1111 111_0 0111 1100 0001 0_111 1_0_10 10_10 10_0_1 111_1 0011
412        let pkt = Instruction::builder()
413            .cv_address(48)
414            .unwrap()
415            .write_byte(0xaa)
416            .build()
417            .unwrap();
418
419        let mut buf = SerialiseBuffer::default();
420        let len = pkt.serialise(&mut buf).unwrap();
421        assert_eq!(len, 52);
422
423        #[allow(clippy::unusual_byte_groupings)]
424        let expected_arr = &[
425            0b1111_1111_u8, // PPPP PPPP
426            0b1111_111_0,   // PPPP PPPS
427            0b0111_11_00,   // 0111 WWAA
428            0b0001_0_111,   // SAAA AAAA
429            0b1_0_10_10_10, // ASDD DDDD
430            0b10_0_1_111_1, // DDSE EEEE
431            0b001_1_0000,   // EEES ----
432        ]
433        .view_bits::<Msb0>()[..len];
434        let mut expected = SerialiseBuffer::default();
435        expected[..52].copy_from_bitslice(expected_arr);
436
437        println!("Got:");
438        print_chunks(&buf, 52);
439        println!("Expected:");
440        print_chunks(&expected, 52);
441        assert_eq!(buf[..len], expected[..52]);
442    }
443
444    #[test]
445    fn serialise_instruction_packet_verify_bit() {
446        // [    preamble    ] S      WWAA SAAA A AAA A_S_DD DK_BO OO_S_E EEE_E EEES
447        // 1111 1111 1111 111_0 0111 1001 0001 0_100 1_0_11 10_11 01_0_1 011_1 0111
448        let pkt = Instruction::builder()
449            .cv_address(298)
450            .unwrap()
451            .verify_bit(5, true)
452            .unwrap()
453            .build()
454            .unwrap();
455
456        let mut buf = SerialiseBuffer::default();
457        let len = pkt.serialise(&mut buf).unwrap();
458        assert_eq!(len, 52);
459
460        #[allow(clippy::unusual_byte_groupings)]
461        let expected_arr = &[
462            0b1111_1111_u8, // PPPP PPPP
463            0b1111_111_0,   // PPPP PPPS
464            0b0111_10_01,   // 0111 WWAA
465            0b0001_0_100,   // SAAA AAAA
466            0b1_0_11_10_11, // ASDD DKBO
467            0b01_0_1_011_1, // OOSE EEEE
468            0b101_1_0000,   // EEES ----
469        ]
470        .view_bits::<Msb0>()[..len];
471        let mut expected = SerialiseBuffer::default();
472        expected[..52].copy_from_bitslice(expected_arr);
473
474        println!("Got:");
475        print_chunks(&buf, 52);
476        println!("Expected:");
477        print_chunks(&expected, 52);
478        assert_eq!(buf[..len], expected[..52]);
479    }
480
481    #[test]
482    fn serialise_address_only_packet() {
483        // [    preamble    ] S 0111 C000 S 0DDD DDDD S EEEE EEEE S
484        // 1111 1111 1111 111_0 0111 1000 0 0011 1011 0 0100 0011 1
485        let pkt = AddressOnly::write(59).unwrap();
486
487        let mut buf = SerialiseBuffer::default();
488        let len = pkt.serialise(&mut buf).unwrap();
489        assert_eq!(len, 43);
490
491        #[allow(clippy::unusual_byte_groupings)]
492        let expected_arr = &[
493            0b1111_1111_u8, // PPPP PPPP
494            0b1111_111_0,   // PPPP PPPS
495            0b0111_1000,    // 0111 C000
496            0b0_0011_101,   // S0DD DDDD
497            0b1_0_01_0000,  // DSEE EEEE
498            0b11_1_0_0000,  // EES- ----
499        ]
500        .view_bits::<Msb0>()[..len];
501        let mut expected = SerialiseBuffer::default();
502        expected[..43].copy_from_bitslice(expected_arr);
503
504        println!("Got:");
505        print_chunks(&buf, 43);
506        println!("Expected:");
507        print_chunks(&expected, 43);
508        assert_eq!(buf[..len], expected[..43]);
509    }
510
511    #[test]
512    fn serialise_physical_register_packet() {
513        // [    preamble    ] S 0111 CRRR S DDDD DDDD S EEEE EEEE S
514        // 1111 1111 1111 111_0 0111 1101 0 1010 1010 0 1101 0111 1
515        let pkt = PhysicalRegister::builder()
516            .operation(Operation::Write)
517            .register(6)
518            .unwrap()
519            .value(0xaa)
520            .build()
521            .unwrap();
522
523        let mut buf = SerialiseBuffer::default();
524        let len = pkt.serialise(&mut buf).unwrap();
525        assert_eq!(len, 43);
526
527        #[allow(clippy::unusual_byte_groupings)]
528        let expected_arr = &[
529            0b1111_1111_u8, // PPPP PPPP
530            0b1111_111_0,   // PPPP PPPS
531            0b0111_1_101,   // 0111 CRRR
532            0b0_1010_101,   // SDDD DDDD
533            0b0_0_11_0101,  // DSEE EEEE
534            0b11_1_0_0000,  // EES- ----
535        ]
536        .view_bits::<Msb0>()[..len];
537        let mut expected = SerialiseBuffer::default();
538        expected[..43].copy_from_bitslice(expected_arr);
539
540        println!("Got:");
541        print_chunks(&buf, 43);
542        println!("Expected:");
543        print_chunks(&expected, 43);
544        assert_eq!(buf[..len], expected[..43]);
545    }
546}