diff --git a/domo_proto/src/commands/mod.rs b/domo_proto/src/commands/mod.rs index 167daaf..a82cdfe 100644 --- a/domo_proto/src/commands/mod.rs +++ b/domo_proto/src/commands/mod.rs @@ -1,8 +1,15 @@ use crate::packet::packet_data::PacketData; +/// NM = Node Management +/// This is the abstraction for managing nodes. pub mod node_management; +/// PC = Property Control +/// This is the abstraction for controling properties on a node. pub mod property_control; +/// RDT = Raw Data Transmission +/// This is the abstraction for sending raw data over domo. +pub mod raw_data_transmission; impl_into_enum_variant!(PacketData::NodeManagement, node_management::NodeManagementCommand); impl_into_enum_variant!(PacketData::PropertyControl, property_control::PropertyControlCommand); -impl_into_enum_variant!(PacketData::Raw, Vec); +impl_into_enum_variant!(PacketData::Unknown, Vec); diff --git a/domo_proto/src/commands/raw_data_transmission/mod.rs b/domo_proto/src/commands/raw_data_transmission/mod.rs new file mode 100644 index 0000000..f58afe0 --- /dev/null +++ b/domo_proto/src/commands/raw_data_transmission/mod.rs @@ -0,0 +1,78 @@ +use std::io; + +use crate::{ + identifier::Identifier, + packet::{packet_data::PacketData, raw_packet::RawPacket, Packet, ToPacket}, + prelude::as_u32_be, +}; + +pub mod vec; + +#[derive(Debug, Clone)] +pub enum RawDataTransmission { + SetupTransmission { + /// The total size of the data being sent. + size: u64, + /// This string is the mime type of the data being sent. + /// By default this should be `application/octet-stream`. + /// The maximum length of this string is 128 bytes since the mime rfc states the recommended max is 127 bytes. + mime_type: String, + }, + Data { + /// The number of the segment in the sequence of segments being sent. + sequence_number: u32, + /// Segment size in bytes + size: u32, + /// A segment of the data being sent + data: Vec, + }, +} + +impl ToPacket for RawDataTransmission { + fn to_packet( + self, + src: Identifier, + dest: Identifier, + packet_id: Identifier, + reply_to: Identifier, + ) -> Packet { + Packet { + src, + dest, + packet_id, + reply_to, + command: 0x20, + data: PacketData::RawDataTransmission(self.clone()), + } + } +} + +impl TryFrom for RawDataTransmission { + type Error = io::Error; + fn try_from(raw_packet: RawPacket) -> io::Result { + let build_invalid_input_err = |r: &str| Err(io::Error::new(io::ErrorKind::InvalidInput, r)); + + let mut data = raw_packet.data; + match raw_packet.command { + 0x20 => { + let size = u64::from_be_bytes([ + data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], + ]); + let mime_type = String::from_utf8(data[8..].to_vec()).map_err(|_| { + io::Error::new(io::ErrorKind::InvalidInput, "Invalid mime type string") + })?; + Ok(RawDataTransmission::SetupTransmission { size, mime_type }) + } + 0x21 => { + let sequence_number = as_u32_be(&mut data); + let size = as_u32_be(&mut data); + Ok(RawDataTransmission::Data { + sequence_number, + size, + data, + }) + } + _ => build_invalid_input_err("Invalid command"), + } + } +} diff --git a/domo_proto/src/commands/raw_data_transmission/vec.rs b/domo_proto/src/commands/raw_data_transmission/vec.rs new file mode 100644 index 0000000..9b1dacf --- /dev/null +++ b/domo_proto/src/commands/raw_data_transmission/vec.rs @@ -0,0 +1,21 @@ +use super::RawDataTransmission; + +impl Into> for RawDataTransmission { + fn into(self) -> Vec { + match self { + RawDataTransmission::SetupTransmission { size, mime_type } => { + let mut v = vec![]; + v.extend(size.to_be_bytes().to_vec()); + v.extend(mime_type.into_bytes()); + v + }, + RawDataTransmission::Data { sequence_number, size, data } => { + let mut v = vec![]; + v.extend(sequence_number.to_be_bytes().to_vec()); + v.extend(size.to_be_bytes().to_vec()); + v.extend(data); + v + } + } + } +} \ No newline at end of file diff --git a/domo_proto/src/data_types/mod.rs b/domo_proto/src/data_types/mod.rs index 4b4d8c9..09bde6a 100644 --- a/domo_proto/src/data_types/mod.rs +++ b/domo_proto/src/data_types/mod.rs @@ -11,39 +11,30 @@ pub enum DataType { Array(Vec), // Basic types - /// Basic Type: Switch (`0x10`] - Switch(bool), + /// Basic Type: Boolean (`0x10`] + Boolean(bool), /// Basic Type: Slider (`0x11`) - Slider(u8), + Number(u64), /// Basic Type: Text (`0x12`) Text(String), + /// Basic Type: Identifier (`0x13`) + Identifier(Identifier), - // Cosmetic - /// Cosmetic Type: RGB (`0x20`) + // Color + /// Color Type: RGB (`0x20`) RGB(u8, u8, u8), - - // Time & Space - /// Time & Space Type: Seconds (`0x90`) - Seconds(u64), - - // Domo Types - /// Domo type: Node Ref (`0xF0`) - NodeRef(Identifier), } impl DataType { pub fn get_data_size(data_type: u8) -> usize { match data_type { 0x00 => 0, - 0x01 => { - 2 - }, + 0x01 => 2, 0x10 => 1, - 0x11 => 1, + 0x11 => 8, 0x12 => 256, + 0x13 => 4, 0x20 => 3, - 0x90 => 8, - 0xF0 => 4, _ => 0, } } diff --git a/domo_proto/src/data_types/vec.rs b/domo_proto/src/data_types/vec.rs index e6d8ebd..dad217e 100644 --- a/domo_proto/src/data_types/vec.rs +++ b/domo_proto/src/data_types/vec.rs @@ -1,6 +1,6 @@ use std::io; use crate::data_types::{DataType, get_data_types}; -use crate::identifier::Identifier; +use crate::identifier::{Identifier, self}; use crate::prelude::as_u64_be; impl Into> for DataType { @@ -14,24 +14,23 @@ impl Into> for DataType { } res }, - DataType::Switch(b) => vec![0x10, b as u8], - DataType::Slider(v) => vec![0x11, v], + DataType::Boolean(b) => vec![0x10, b as u8], + DataType::Number(v) => { + let mut bytes = vec![0x11]; + bytes.extend(v.to_be_bytes()); + bytes + }, DataType::Text(s) => { let mut bytes = vec![0x12]; bytes.extend(s.into_bytes()); bytes }, + DataType::Identifier(identifier) => { + let mut bytes = vec![0x13]; + bytes.extend(identifier.bytes); + bytes + }, DataType::RGB(r, g, b) => vec![0x20, r, g, b], - DataType::Seconds(s) => { - let mut bytes = vec![0x90]; - bytes.extend_from_slice(s.to_be_bytes().as_ref()); - bytes - }, - DataType::NodeRef(id) => { - let mut bytes = vec![0xF0]; - bytes.extend_from_slice(id.bytes.as_ref()); - bytes - }, } } } @@ -61,8 +60,11 @@ impl TryFrom> for DataType { if value.len() - 1 < size as usize { return Err(io::Error::new(io::ErrorKind::InvalidData, "Array specified data length != array data length")) } Ok(DataType::Array(get_data_types(value[3..(size as usize)].to_vec()))) }, - 0x10 => impl_data_type!(value, 1, DataType::Switch(value[1] != 0)), - 0x11 => impl_data_type!(value, 1, DataType::Slider(value[1])), + 0x10 => impl_data_type!(value, 1, DataType::Boolean(value[1] != 0)), + 0x11 => impl_data_type!(value, 8, DataType::Number(u64::from_be_bytes([ + value[1], value[2], value[3], value[4], + value[5], value[6], value[7], value[8] + ]))), 0x12 => impl_data_type!( value, 256, @@ -72,9 +74,8 @@ impl TryFrom> for DataType { DataType::Nothing } ), + 0x13 => impl_data_type!(value, 4, DataType::Identifier(Identifier::from(&value[1..5]))), 0x20 => impl_data_type!(value, 3, DataType::RGB(value[1], value[2], value[3])), - 0x90 => impl_data_type!(value, 8, DataType::Seconds(as_u64_be(&value[1..9]))), - 0xF0 => impl_data_type!(value, 4, DataType::NodeRef(Identifier::from(&value[1..5]))), _ => Err(io::Error::new(io::ErrorKind::InvalidData, "Could not match data type header to a data type.")), } } diff --git a/domo_proto/src/lib.rs b/domo_proto/src/lib.rs index 04c7026..edc5e16 100644 --- a/domo_proto/src/lib.rs +++ b/domo_proto/src/lib.rs @@ -1,6 +1,6 @@ //! Welcome to the Domo protocol rust implementation. //! -//! This implementation is up-to-date with DomoProto v1. +//! This implementation is up-to-date with Domo protocol v1. //! //! Most code that should/is actually used (usually) has documentation with it. @@ -27,7 +27,6 @@ mod tests { use crate::packet; use crate::data_types::{DataType, get_data_types}; use crate::identifier::Identifier; - use crate::packet::Packet; use crate::packet::packet_data::PacketData; #[test] @@ -37,12 +36,12 @@ mod tests { get_data_types(vec![0x00, 0x10, 0x00]), vec![ DataType::Nothing, - DataType::Switch(false), + DataType::Boolean(false), ]); // test DataType -> Vec assert_eq!( - Into::>::into(DataType::Switch(true)), + Into::>::into(DataType::Boolean(true)), vec![0x10, 0x01] ) } diff --git a/domo_proto/src/packet/mod.rs b/domo_proto/src/packet/mod.rs index 33088f0..060dc4b 100644 --- a/domo_proto/src/packet/mod.rs +++ b/domo_proto/src/packet/mod.rs @@ -17,7 +17,7 @@ pub const FULL_PACKET_SIZE: usize = 65559; pub const BASE_PACKET_SIZE: usize = 65555; /// The abstraction for all DomoProto packets. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Packet { pub dest: Identifier, pub src: Identifier, @@ -31,6 +31,20 @@ pub trait ToPacket { fn to_packet(self, src: Identifier, dest: Identifier, packet_id: Identifier, reply_to: Identifier) -> Packet; } +/// Default packet is a ping from 0x0 to 0xFFFFFFFF +impl Default for Packet { + fn default() -> Self { + Self { + dest: Identifier::from(0xFFFFFFFF), + src: Identifier::from(0), + command: 0x00, + packet_id: Identifier::random(), + reply_to: Identifier::default(), + data: PacketData::Unknown(vec![]) + } + } +} + /// Secretly calls RawPacket::try_from().into() impl TryFrom> for Packet { type Error = io::Error; diff --git a/domo_proto/src/packet/packet_data.rs b/domo_proto/src/packet/packet_data.rs index 0daa807..f595088 100644 --- a/domo_proto/src/packet/packet_data.rs +++ b/domo_proto/src/packet/packet_data.rs @@ -1,6 +1,7 @@ use std::io; use crate::commands::property_control::PropertyControlCommand; use crate::commands::node_management::NodeManagementCommand; +use crate::commands::raw_data_transmission::RawDataTransmission; use crate::packet::raw_packet::RawPacket; /// Abstraction used for interpreting the data in a packet. @@ -8,13 +9,14 @@ use crate::packet::raw_packet::RawPacket; pub enum PacketData { NodeManagement(NodeManagementCommand), PropertyControl(PropertyControlCommand), - Raw(Vec) + RawDataTransmission(RawDataTransmission), + Unknown(Vec) } /// Returns an empty `PacketData::Raw` impl Default for PacketData { fn default() -> Self { - PacketData::Raw(vec![]) + PacketData::Unknown(vec![]) } } @@ -23,7 +25,8 @@ impl PacketData { match self { PacketData::NodeManagement(v) => v.into(), PacketData::PropertyControl(v) => v.into(), - PacketData::Raw(v) => v + PacketData::RawDataTransmission(v) => v.into(), + PacketData::Unknown(v) => v } } } @@ -36,6 +39,7 @@ impl TryFrom for PacketData { } { 0x0 => Ok(PacketData::NodeManagement(NodeManagementCommand::try_from(raw_packet)?)), 0x1 => Ok(PacketData::PropertyControl(PropertyControlCommand::try_from(raw_packet)?)), + 0xF => Ok(PacketData::RawDataTransmission(RawDataTransmission::try_from(raw_packet)?)), _ => Err(io::Error::new(io::ErrorKind::InvalidInput, "command group is unsupported")) } } diff --git a/domo_proto/src/packet/raw_packet/packet.rs b/domo_proto/src/packet/raw_packet/packet.rs index 0f25832..67875a0 100644 --- a/domo_proto/src/packet/raw_packet/packet.rs +++ b/domo_proto/src/packet/raw_packet/packet.rs @@ -44,7 +44,7 @@ impl Into for RawPacket { println!("{}", e); e }) - .unwrap_or(PacketData::Raw(self.data)), + .unwrap_or(PacketData::Unknown(self.data)), } } } \ No newline at end of file