ether_dream/
protocol.rs

1//! Types and constants that precisely match the specification.
2
3use byteorder::{ReadBytesExt, WriteBytesExt, LE};
4use std::io;
5
6pub use self::command::Command;
7
8/// Communication with the DAC happens over TCP on port 7765.
9pub const COMMUNICATION_PORT: u16 = 7765;
10
11/// The DAC sends UDP broadcast messages on port 7654.
12///
13/// This does not appeared to be documented in the protocol, but was found within the
14/// `github.com/j4cbo/j4cDAC` repository.
15pub const BROADCAST_PORT: u16 = 7654;
16
17/// A trait for writing any of the Ether Dream protocol types to little-endian bytes.
18///
19/// A blanket implementation is provided for all types that implement `byteorder::WriteBytesExt`.
20pub trait WriteBytes {
21    fn write_bytes<P: WriteToBytes>(&mut self, protocol: P) -> io::Result<()>;
22}
23
24/// A trait for reading any of the Ether Dream protocol types from little-endian bytes.
25///
26/// A blanket implementation is provided for all types that implement `byteorder::ReadBytesExt`.
27pub trait ReadBytes {
28    fn read_bytes<P: ReadFromBytes>(&mut self) -> io::Result<P>;
29}
30
31/// Protocol types that may be written to little endian bytes.
32pub trait WriteToBytes {
33    /// Write the command to bytes.
34    fn write_to_bytes<W: WriteBytesExt>(&self, writer: W) -> io::Result<()>;
35}
36
37/// Protocol types that may be read from little endian bytes.
38pub trait ReadFromBytes: Sized {
39    /// Read the command from bytes.
40    fn read_from_bytes<R: ReadBytesExt>(reader: R) -> io::Result<Self>;
41}
42
43/// Types that have a constant size when written to or read from bytes.
44pub trait SizeBytes {
45    const SIZE_BYTES: usize;
46}
47
48/// Periodically, and as part of ACK packets, the DAC sends its current playback status to the
49/// host.
50#[repr(C)]
51#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
52pub struct DacStatus {
53    /// This remains undocumented in the protocol.
54    ///
55    /// The original implementation source simply sets this to `0`.
56    pub protocol: u8,
57    /// The current state of the "light engine" state machine.
58    pub light_engine_state: u8,
59    /// The current state of the "playback" state machine.
60    pub playback_state: u8,
61    /// The currently-selected data source:
62    ///
63    /// - `0`: Network streaming (the protocol implemented in this library).
64    /// - `1`: ILDA playback from SD card.
65    /// - `2`: Internal abstract generator.
66    pub source: u8,
67    /// If the light engine is `Ready`, this will be `0`.
68    ///
69    /// Otherwise, bits will be set as follows:
70    ///
71    /// - `0`: Emergency stop occurred due to E-Stop packet or invalid command.
72    /// - `1`: Emergency stop occurred due to E-Stop input to projector.
73    /// - `2`: Emergency stop input to projector is currently active.
74    /// - `3`: Emergency stop occurred due to over-temperature condition.
75    /// - `4`: Over-temperature condition is currently active.
76    /// - `5`: Emergency stop occurred due to loss of ethernet link.
77    ///
78    /// All remaining are reserved for future use.
79    pub light_engine_flags: u16,
80    /// These flags may be non-zero during normal operation.
81    ///
82    /// Bits are defined as follows:
83    ///
84    /// - `0`: **Shutter state**. `0` is closed, `1` is open.
85    /// - `1`: **Underflow**. `1` if the last stream ended with underflow rather than a `Stop`
86    ///   command. This is reset to `0` by the `Prepare` command.
87    /// - `2`: **E-Stop**. `1` if the last stream ended because the E-Stop state was entered. Reset
88    ///   to zero by the `Prepare` command.
89    pub playback_flags: u16,
90    /// This field is undocumented within the protocol reference.
91    ///
92    /// By looking at the source code of the original implementation, this seems to represent the
93    /// state of the current `source`.
94    ///
95    /// If `source` is set to `1` for ILDA playback from SD card, the following flags are defined:
96    ///
97    /// - `0`: `ILDA_PLAYER_PLAYING`.
98    /// - `1`: `ILDA_PLAYER_REPEAT`.
99    ///
100    /// If `source` is set to `2` for the internal abstract generator, the flags are defined as
101    /// follows:
102    ///
103    /// - `0`: `ABSTRACT_PLAYING`.
104    pub source_flags: u16,
105    /// The number of points currently buffered.
106    pub buffer_fullness: u16,
107    /// If in the `Prepared` or `Playing` playback states, this is the number of points per
108    /// second for which the DAC is configured.
109    ///
110    /// If in the `Idle` playback state, this will be `0`.
111    pub point_rate: u32,
112    /// If in the `Playing` playback state, this is the number of points that the DAC has actually
113    /// emitted since it started playing.
114    ///
115    /// If in the `Prepared` or `Idle` playback states, this will be `0`.
116    pub point_count: u32,
117}
118
119/// Regardless of the data source being used, each DAC broadcasts a status/ID datagram over UDP to
120/// its local network's broadcast address once per second.
121#[repr(C)]
122#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
123pub struct DacBroadcast {
124    /// The unique hardware identifier for the DAC.
125    pub mac_address: [u8; 6],
126    /// This field is undocumented in the official protocol but seems to represent a version number
127    /// for the hardware in use by the DAC.
128    pub hw_revision: u16,
129    /// This field is undocumented in the official protocol but seems to represent the version of
130    /// the protocol implementation. As of writing this, this is hardcoded as `2` in the original
131    /// source.
132    pub sw_revision: u16,
133    /// The DAC's maximum buffer capacity for storing points that are yet to be converted to
134    /// output.
135    ///
136    /// As of writing this, this is hardcoded to `1800` in the original DAC source code.
137    pub buffer_capacity: u16,
138    /// The DAC's maximum point rate.
139    ///
140    /// As of writing this, this is hardcoded to `100_000` in the original DAC source code.
141    pub max_point_rate: u32,
142    /// The current status of the DAC.
143    pub dac_status: DacStatus,
144}
145
146/// Values are full-scale.
147///
148/// E.g. for all color channels, `65535` is full output while `0` is no output.
149#[repr(C)]
150#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
151pub struct DacPoint {
152    /// A set of bit fields. The following fields are defined:
153    ///
154    /// - `15`: Change point rate. If this bit is set and there are any values in the point
155    ///   rate change buffer a new rate is read out of the buffer and set as the current
156    ///   playback rate. If the buffer is empty, the point rate is not changed.
157    ///
158    /// All other bits are reserved for future expansion to support extra TTL outputs, etc.
159    pub control: u16,
160    /// -32768 is the start along the *x* axis (left-most point).
161    /// 32767 is the end along the *x* axis (right-most point).
162    pub x: i16,
163    /// -32768 is the start along the *y* axis (bottom-most point).
164    /// 32767 is the end along the *y* axis (top-most point).
165    pub y: i16,
166    /// `0` is no red. `65535` is full red.
167    pub r: u16,
168    /// `0` is no green. `65535` is full green.
169    pub g: u16,
170    /// `0` is no blue. `65535` is full blue.
171    pub b: u16,
172    pub i: u16,
173    pub u1: u16,
174    pub u2: u16,
175}
176
177/// A response from a DAC.
178#[repr(C)]
179#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
180pub struct DacResponse {
181    /// See the `DacResponse` associated constants for the possible values for this field.
182    pub response: u8,
183    /// In the case of ACK/NAK responses, this echoes back the command to which the response is
184    /// sent.
185    ///
186    /// Commands are always sent in order, so this field exists for sanity-checking on the
187    /// host-side.
188    pub command: u8,
189    /// The current status of the DAC.
190    pub dac_status: DacStatus,
191}
192
193impl DacStatus {
194    /// The light engine is ready.
195    pub const LIGHT_ENGINE_READY: u8 = 0;
196    /// In the case where the DAC is also used for thermal control of laser apparatus, this is the
197    /// state that is entered after power-up.
198    pub const LIGHT_ENGINE_WARMUP: u8 = 1;
199    /// Lasers are off but thermal control is still active.
200    pub const LIGHT_ENGINE_COOLDOWN: u8 = 2;
201    /// An emergency stop has been triggered, either by an E-stop input on the DAC, an E-stop
202    /// command over the network, or a fault such as over-temperature.
203    pub const LIGHT_ENGINE_EMERGENCY_STOP: u8 = 3;
204
205    /// The default state:
206    ///
207    /// - No points may be added to the buffer.
208    /// - No output is generated.
209    /// - All analog outputs are at 0v.
210    /// - The shutter is controlled by the data source.
211    pub const PLAYBACK_IDLE: u8 = 0;
212    /// The buffer will accept points.
213    ///
214    /// The output is the same as the `Idle` state
215    pub const PLAYBACK_PREPARED: u8 = 1;
216    /// Points are being sent to the output.
217    pub const PLAYBACK_PLAYING: u8 = 2;
218
219    /// Network streaming (the protocol implemented in this library).
220    pub const SOURCE_NETWORK_STREAMING: u8 = 0;
221    /// ILDA playback from SD card.
222    pub const SOURCE_ILDA_PLAYBACK_SD: u8 = 1;
223    /// Internal abstract generator.
224    pub const SOURCE_INTERNAL_ABSTRACT_GENERATOR: u8 = 2;
225}
226
227impl DacResponse {
228    /// The previous command was accepted.
229    pub const ACK: u8 = 0x61;
230    /// The write command could not be performed because there was not enough buffer space when it
231    /// was received.
232    pub const NAK_FULL: u8 = 0x46;
233    /// The command contained an invalid `command` byte or parameters.
234    pub const NAK_INVALID: u8 = 0x49;
235    /// An emergency-stop condition still exists.
236    pub const NAK_STOP_CONDITION: u8 = 0x21;
237}
238
239impl WriteToBytes for DacStatus {
240    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
241        writer.write_u8(self.protocol)?;
242        writer.write_u8(self.light_engine_state)?;
243        writer.write_u8(self.playback_state)?;
244        writer.write_u8(self.source)?;
245        writer.write_u16::<LE>(self.light_engine_flags)?;
246        writer.write_u16::<LE>(self.playback_flags)?;
247        writer.write_u16::<LE>(self.source_flags)?;
248        writer.write_u16::<LE>(self.buffer_fullness)?;
249        writer.write_u32::<LE>(self.point_rate)?;
250        writer.write_u32::<LE>(self.point_count)?;
251        Ok(())
252    }
253}
254
255impl WriteToBytes for DacBroadcast {
256    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
257        for &byte in &self.mac_address {
258            writer.write_u8(byte)?;
259        }
260        writer.write_u16::<LE>(self.hw_revision)?;
261        writer.write_u16::<LE>(self.sw_revision)?;
262        writer.write_u16::<LE>(self.buffer_capacity)?;
263        writer.write_u32::<LE>(self.max_point_rate)?;
264        writer.write_bytes(&self.dac_status)?;
265        Ok(())
266    }
267}
268
269impl WriteToBytes for DacPoint {
270    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
271        writer.write_u16::<LE>(self.control)?;
272        writer.write_i16::<LE>(self.x)?;
273        writer.write_i16::<LE>(self.y)?;
274        writer.write_u16::<LE>(self.r)?;
275        writer.write_u16::<LE>(self.g)?;
276        writer.write_u16::<LE>(self.b)?;
277        writer.write_u16::<LE>(self.i)?;
278        writer.write_u16::<LE>(self.u1)?;
279        writer.write_u16::<LE>(self.u2)?;
280        Ok(())
281    }
282}
283
284impl WriteToBytes for DacResponse {
285    fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
286        writer.write_u8(self.response)?;
287        writer.write_u8(self.command)?;
288        writer.write_bytes(&self.dac_status)?;
289        Ok(())
290    }
291}
292
293impl ReadFromBytes for DacStatus {
294    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
295        let protocol = reader.read_u8()?;
296        let light_engine_state = reader.read_u8()?;
297        let playback_state = reader.read_u8()?;
298        let source = reader.read_u8()?;
299        let light_engine_flags = reader.read_u16::<LE>()?;
300        let playback_flags = reader.read_u16::<LE>()?;
301        let source_flags = reader.read_u16::<LE>()?;
302        let buffer_fullness = reader.read_u16::<LE>()?;
303        let point_rate = reader.read_u32::<LE>()?;
304        let point_count = reader.read_u32::<LE>()?;
305        let dac_status = DacStatus {
306            protocol,
307            light_engine_state,
308            playback_state,
309            source,
310            light_engine_flags,
311            playback_flags,
312            source_flags,
313            buffer_fullness,
314            point_rate,
315            point_count,
316        };
317        Ok(dac_status)
318    }
319}
320
321impl ReadFromBytes for DacBroadcast {
322    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
323        let mac_address = [
324            reader.read_u8()?,
325            reader.read_u8()?,
326            reader.read_u8()?,
327            reader.read_u8()?,
328            reader.read_u8()?,
329            reader.read_u8()?,
330        ];
331        let hw_revision = reader.read_u16::<LE>()?;
332        let sw_revision = reader.read_u16::<LE>()?;
333        let buffer_capacity = reader.read_u16::<LE>()?;
334        let max_point_rate = reader.read_u32::<LE>()?;
335        let dac_status = reader.read_bytes::<DacStatus>()?;
336        let dac_broadcast = DacBroadcast {
337            mac_address,
338            hw_revision,
339            sw_revision,
340            buffer_capacity,
341            max_point_rate,
342            dac_status,
343        };
344        Ok(dac_broadcast)
345    }
346}
347
348impl ReadFromBytes for DacPoint {
349    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
350        let control = reader.read_u16::<LE>()?;
351        let x = reader.read_i16::<LE>()?;
352        let y = reader.read_i16::<LE>()?;
353        let r = reader.read_u16::<LE>()?;
354        let g = reader.read_u16::<LE>()?;
355        let b = reader.read_u16::<LE>()?;
356        let i = reader.read_u16::<LE>()?;
357        let u1 = reader.read_u16::<LE>()?;
358        let u2 = reader.read_u16::<LE>()?;
359        let dac_point = DacPoint {
360            control,
361            x,
362            y,
363            i,
364            r,
365            g,
366            b,
367            u1,
368            u2,
369        };
370        Ok(dac_point)
371    }
372}
373
374impl ReadFromBytes for DacResponse {
375    fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
376        let response = reader.read_u8()?;
377        let command = reader.read_u8()?;
378        let dac_status = reader.read_bytes::<DacStatus>()?;
379        let dac_response = DacResponse {
380            response,
381            command,
382            dac_status,
383        };
384        Ok(dac_response)
385    }
386}
387
388impl SizeBytes for DacStatus {
389    const SIZE_BYTES: usize = 20;
390}
391
392impl SizeBytes for DacBroadcast {
393    const SIZE_BYTES: usize = DacStatus::SIZE_BYTES + 16;
394}
395
396impl SizeBytes for DacPoint {
397    const SIZE_BYTES: usize = 18;
398}
399
400impl SizeBytes for DacResponse {
401    const SIZE_BYTES: usize = DacStatus::SIZE_BYTES + 2;
402}
403
404impl<'a, P> WriteToBytes for &'a P
405where
406    P: WriteToBytes,
407{
408    fn write_to_bytes<W: WriteBytesExt>(&self, writer: W) -> io::Result<()> {
409        (*self).write_to_bytes(writer)
410    }
411}
412
413impl<W> WriteBytes for W
414where
415    W: WriteBytesExt,
416{
417    fn write_bytes<P: WriteToBytes>(&mut self, protocol: P) -> io::Result<()> {
418        protocol.write_to_bytes(self)
419    }
420}
421
422impl<R> ReadBytes for R
423where
424    R: ReadBytesExt,
425{
426    fn read_bytes<P: ReadFromBytes>(&mut self) -> io::Result<P> {
427        P::read_from_bytes(self)
428    }
429}
430
431/// When a host first connects to the device, the device immediately sends it a status reply as if
432/// the host had sent a ping packet. The host sends to the device a series of commands. All
433/// commands receive a response from the DAC.
434pub mod command {
435    use super::{DacPoint, ReadBytes, ReadFromBytes, SizeBytes, WriteBytes, WriteToBytes};
436    use byteorder::{ReadBytesExt, WriteBytesExt, LE};
437    use std::borrow::Cow;
438    use std::{self, io};
439
440    /// Types that may be submitted as commands to the DAC.
441    pub trait Command {
442        /// The starting byte of the command.
443        const START_BYTE: u8;
444        /// A provided method for producing the start byte. Useful for trait objects.
445        fn start_byte(&self) -> u8 {
446            Self::START_BYTE
447        }
448    }
449
450    /// This command causes the playback system to enter the `Prepared` state. The DAC resets its
451    /// buffer to be empty and sets "point_count" to `0`.
452    ///
453    /// This command may only be sent if the light engine is `Ready` and the playback system is
454    /// `Idle`. If so, the DAC replies with ACK. Otherwise, it replies with NAK - Invalid.
455    #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
456    pub struct PrepareStream;
457
458    /// Causes the DAC to begin producing output.
459    ///
460    /// If the playback system was `Prepared` and there was data in the buffer, then the DAC will
461    /// reply with ACK.
462    ///
463    /// Otherwise, it replies with NAK - Invalid.
464    #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
465    pub struct Begin {
466        /// *Currently unused.*
467        pub low_water_mark: u16,
468        /// The number of points per second to be read from the buffer.
469        pub point_rate: u32,
470    }
471
472    /// Adds a new point rate (in points per second) to the point rate buffer.
473    ///
474    /// Point rate changes are read out of the buffer when a point with an appropriate flag is
475    /// played (see the `WriteData` command).
476    ///
477    /// If the DAC's playback state is not `Prepared` or `Playing`, it replies with NAK - Invalid.
478    ///
479    /// If the point rate buffer is full, it replies with NAK - Full.
480    ///
481    /// Otherwise, it replies with ACK.
482    #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
483    pub struct PointRate(pub u32);
484
485    /// Indicates to the DAC to add the following point data into its buffer.
486    ///
487    /// Point data is laid out as follows:
488    ///
489    /// - 0x64
490    /// - <the number of points>: u16
491    /// - <point data>
492    #[derive(Clone, Debug, PartialEq, Eq, Hash)]
493    pub struct Data<'a> {
494        pub points: Cow<'a, [DacPoint]>,
495    }
496
497    /// Causes the DAC to immediately stop playing and return to the `Idle` playback state.
498    ///
499    /// It is ACKed if the DAC was in the `Playing` or `Prepared` playback states.
500    ///
501    /// Otherwise it is replied to with NAK - Invalid.
502    #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
503    pub struct Stop;
504
505    /// Causes the light engine to enter the E-Stop state, regardless of its previous state.
506    ///
507    /// This command is always ACKed.
508    ///
509    /// **Note:** Any unrecognised command will also be treated as E-Stop. However, software should
510    /// not send undefined commands deliberately, since they may be defined in the future.
511    #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
512    pub struct EmergencyStop;
513
514    /// Causes the light engine to enter the E-Stop state, regardless of its previous state.
515    ///
516    /// This command is always ACKed.
517    ///
518    /// **Note:** Any unrecognised command will also be treated as E-Stop. However, software should
519    /// not send undefined commands deliberately, since they may be defined in the future.
520    #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
521    pub struct EmergencyStopAlt;
522
523    /// If the light engine was in E-Stop state due to an emergency stop command (either from a
524    /// local stop condition or over the network), this command resets it to `Ready`.
525    ///
526    /// It is ACKed if the DAC was previously in E-Stop.
527    ///
528    /// Otherwise it is replied to with a NAK - Invalid.
529    ///
530    /// If the condition that caused the emergency stop is still active (e.g. E-Stop input still
531    /// asserted, temperature still out of bounds, etc) a NAK - Stop Condition is sent.
532    #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
533    pub struct ClearEmergencyStop;
534
535    /// The DAC will reply to this with an ACK packet.
536    ///
537    /// This serves as a keep-alive for the connection when the DAC is not actively streaming.
538    #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
539    pub struct Ping;
540
541    impl Begin {
542        /// Consecutively read the fields of the **Begin** type and return a **Begin** instance.
543        ///
544        /// Note that if reading from a stream, this method assumes that the starting command byte
545        /// has already been read.
546        pub fn read_fields<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
547            let low_water_mark = reader.read_u16::<LE>()?;
548            let point_rate = reader.read_u32::<LE>()?;
549            let begin = Begin {
550                low_water_mark,
551                point_rate,
552            };
553            Ok(begin)
554        }
555    }
556
557    impl PointRate {
558        /// Consecutively read the fields of the **PointRate** type and return a **PointRate** instance.
559        ///
560        /// Note that if reading from a stream, this method assumes that the starting command byte
561        /// has already been read.
562        pub fn read_fields<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
563            let point_rate = PointRate(reader.read_u32::<LE>()?);
564            Ok(point_rate)
565        }
566    }
567
568    impl<'a> Data<'a> {
569        /// Read the `u16` representing the number of points within the **Data** from the given
570        /// `reader`.
571        ///
572        /// This method is useful for determining how many more bytes should be read from a stream.
573        ///
574        /// Note that if reading from a stream, this method assumes that the starting command byte
575        /// has already been read.
576        pub fn read_n_points<R>(mut reader: R) -> io::Result<u16>
577        where
578            R: ReadBytesExt,
579        {
580            reader.read_u16::<LE>()
581        }
582
583        /// Read and append the given number of points into the given Vec of **DacPoint**s.
584        ///
585        /// This method is useful as an alternative to **Self::read_fields** or
586        /// **Self::read_from_bytes** as it allows for re-using a buffer of points rather than
587        /// dynamically allocating a new one each time.
588        ///
589        /// Note that if reading from a stream, this method assumes that the starting command byte
590        /// and the `u16` representing the number of points have both already been read.
591        pub fn read_points<R>(
592            mut reader: R,
593            mut n_points: u16,
594            points: &mut Vec<DacPoint>,
595        ) -> io::Result<()>
596        where
597            R: ReadBytesExt,
598        {
599            while n_points > 0 {
600                let dac_point = reader.read_bytes::<DacPoint>()?;
601                points.push(dac_point);
602                n_points -= 1;
603            }
604            Ok(())
605        }
606    }
607
608    impl Data<'static> {
609        /// Consecutively read the fields of the **Data** type and return a **Data** instance.
610        ///
611        /// Note that if reading from a stream, this method assumes that the starting command byte
612        /// has already been read.
613        pub fn read_fields<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
614            let n_points = Self::read_n_points(&mut reader)?;
615            let mut data = Vec::with_capacity(n_points as _);
616            Self::read_points(reader, n_points, &mut data)?;
617            let data = Data {
618                points: Cow::Owned(data),
619            };
620            Ok(data)
621        }
622    }
623
624    impl<'a, C> Command for &'a C
625    where
626        C: Command,
627    {
628        const START_BYTE: u8 = C::START_BYTE;
629    }
630
631    impl Command for PrepareStream {
632        const START_BYTE: u8 = 0x70;
633    }
634
635    impl Command for Begin {
636        const START_BYTE: u8 = 0x62;
637    }
638
639    impl Command for PointRate {
640        const START_BYTE: u8 = 0x74;
641    }
642
643    impl<'a> Command for Data<'a> {
644        const START_BYTE: u8 = 0x64;
645    }
646
647    impl Command for Stop {
648        const START_BYTE: u8 = 0x73;
649    }
650
651    impl Command for EmergencyStop {
652        const START_BYTE: u8 = 0x00;
653    }
654
655    impl Command for EmergencyStopAlt {
656        const START_BYTE: u8 = 0xff;
657    }
658
659    impl Command for ClearEmergencyStop {
660        const START_BYTE: u8 = 0x63;
661    }
662
663    impl Command for Ping {
664        const START_BYTE: u8 = 0x3f;
665    }
666
667    impl SizeBytes for PrepareStream {
668        const SIZE_BYTES: usize = 1;
669    }
670
671    impl SizeBytes for Begin {
672        const SIZE_BYTES: usize = 7;
673    }
674
675    impl SizeBytes for PointRate {
676        const SIZE_BYTES: usize = 5;
677    }
678
679    impl SizeBytes for Stop {
680        const SIZE_BYTES: usize = 1;
681    }
682
683    impl SizeBytes for EmergencyStop {
684        const SIZE_BYTES: usize = 1;
685    }
686
687    impl SizeBytes for ClearEmergencyStop {
688        const SIZE_BYTES: usize = 1;
689    }
690
691    impl SizeBytes for Ping {
692        const SIZE_BYTES: usize = 1;
693    }
694
695    impl WriteToBytes for PrepareStream {
696        fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
697            writer.write_u8(Self::START_BYTE)?;
698            Ok(())
699        }
700    }
701
702    impl WriteToBytes for Begin {
703        fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
704            writer.write_u8(Self::START_BYTE)?;
705            writer.write_u16::<LE>(self.low_water_mark)?;
706            writer.write_u32::<LE>(self.point_rate)?;
707            Ok(())
708        }
709    }
710
711    impl WriteToBytes for PointRate {
712        fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
713            writer.write_u8(Self::START_BYTE)?;
714            writer.write_u32::<LE>(self.0)?;
715            Ok(())
716        }
717    }
718
719    impl<'a> WriteToBytes for Data<'a> {
720        fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
721            if self.points.len() > std::u16::MAX as usize {
722                let err_msg = "the number of points exceeds the maximum possible `u16` value";
723                return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
724            }
725            writer.write_u8(Self::START_BYTE)?;
726            writer.write_u16::<LE>(self.points.len() as u16)?;
727            for point in self.points.iter() {
728                writer.write_bytes(point)?;
729            }
730            Ok(())
731        }
732    }
733
734    impl WriteToBytes for Stop {
735        fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
736            writer.write_u8(Self::START_BYTE)?;
737            Ok(())
738        }
739    }
740
741    impl WriteToBytes for EmergencyStop {
742        fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
743            writer.write_u8(Self::START_BYTE)?;
744            Ok(())
745        }
746    }
747
748    impl WriteToBytes for EmergencyStopAlt {
749        fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
750            writer.write_u8(Self::START_BYTE)?;
751            Ok(())
752        }
753    }
754
755    impl WriteToBytes for ClearEmergencyStop {
756        fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
757            writer.write_u8(Self::START_BYTE)?;
758            Ok(())
759        }
760    }
761
762    impl WriteToBytes for Ping {
763        fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
764            writer.write_u8(Self::START_BYTE)?;
765            Ok(())
766        }
767    }
768
769    impl ReadFromBytes for PrepareStream {
770        fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
771            let command = reader.read_u8()?;
772            if command != Self::START_BYTE {
773                let err_msg = "invalid \"prepare stream\" command byte";
774                return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
775            }
776            Ok(PrepareStream)
777        }
778    }
779
780    impl ReadFromBytes for Begin {
781        fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
782            if reader.read_u8()? != Self::START_BYTE {
783                let err_msg = "invalid \"begin\" command byte";
784                return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
785            }
786            Self::read_fields(reader)
787        }
788    }
789
790    impl ReadFromBytes for PointRate {
791        fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
792            if reader.read_u8()? != Self::START_BYTE {
793                let err_msg = "invalid \"queue change\" command byte";
794                return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
795            }
796            Self::read_fields(reader)
797        }
798    }
799
800    impl ReadFromBytes for Data<'static> {
801        fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
802            if reader.read_u8()? != Self::START_BYTE {
803                let err_msg = "invalid \"data\" command byte";
804                return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
805            }
806            Self::read_fields(reader)
807        }
808    }
809
810    impl ReadFromBytes for Stop {
811        fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
812            let command = reader.read_u8()?;
813            if command != Self::START_BYTE {
814                let err_msg = "invalid \"stop\" command byte";
815                return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
816            }
817            Ok(Stop)
818        }
819    }
820
821    impl ReadFromBytes for EmergencyStop {
822        fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
823            let command = reader.read_u8()?;
824            if command != Self::START_BYTE && command != EmergencyStopAlt::START_BYTE {
825                let err_msg = "invalid \"emergency stop\" command byte";
826                return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
827            }
828            Ok(EmergencyStop)
829        }
830    }
831
832    impl ReadFromBytes for ClearEmergencyStop {
833        fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
834            let command = reader.read_u8()?;
835            if command != Self::START_BYTE {
836                let err_msg = "invalid \"clear emergency stop\" command byte";
837                return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
838            }
839            Ok(ClearEmergencyStop)
840        }
841    }
842
843    impl ReadFromBytes for Ping {
844        fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
845            let command = reader.read_u8()?;
846            if command != Self::START_BYTE {
847                let err_msg = "invalid \"ping\" command byte";
848                return Err(io::Error::new(io::ErrorKind::InvalidData, err_msg));
849            }
850            Ok(Ping)
851        }
852    }
853}