feat: rust proto implementation
This commit is contained in:
parent
1e68840042
commit
e4b508ae86
12 changed files with 381 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
**/target/
|
25
domo_proto/Cargo.lock
generated
Normal file
25
domo_proto/Cargo.lock
generated
Normal file
|
@ -0,0 +1,25 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "domo_proto"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
]
|
9
domo_proto/Cargo.toml
Normal file
9
domo_proto/Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "domo_proto"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
crc32fast = "1.3.2"
|
5
domo_proto/src/commands/mod.rs
Normal file
5
domo_proto/src/commands/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
use crate::packet::packet_data::PacketData;
|
||||
|
||||
pub mod node_management;
|
||||
|
||||
pub trait Command: Into<PacketData> + From<PacketData> {}
|
39
domo_proto/src/commands/node_management.rs
Normal file
39
domo_proto/src/commands/node_management.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
use crate::packet::identifier::Identifier;
|
||||
|
||||
pub enum NodeManagement {
|
||||
Ping,
|
||||
RegisterNode { device_id: Identifier },
|
||||
RemoveNode,
|
||||
RegisterProperty {
|
||||
property_name: String,
|
||||
data_type: u8,
|
||||
read_only: bool
|
||||
},
|
||||
RemoveProperty {
|
||||
property_name: String,
|
||||
},
|
||||
Error {
|
||||
error_code: u8,
|
||||
metadata: [u8; 0xFFFF]
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum NodeManagementError {
|
||||
net_duplicate_packet,
|
||||
net_broken_packet,
|
||||
net_invalid_packet,
|
||||
|
||||
errc_not_registered
|
||||
}
|
||||
|
||||
impl NodeManagementError {
|
||||
pub fn error_code(&self) -> u8 {
|
||||
match self {
|
||||
NodeManagementError::net_duplicate_packet => 0x00,
|
||||
NodeManagementError::net_broken_packet => 0x01,
|
||||
NodeManagementError::net_invalid_packet => 0x02,
|
||||
NodeManagementError::errc_not_registered => 0x10
|
||||
}
|
||||
}
|
||||
}
|
3
domo_proto/src/lib.rs
Normal file
3
domo_proto/src/lib.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub mod commands;
|
||||
pub mod packet;
|
||||
pub mod prelude;
|
21
domo_proto/src/main.rs
Normal file
21
domo_proto/src/main.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use domo_proto::packet::data_types::DataType;
|
||||
use domo_proto::packet::identifier::Identifier;
|
||||
use domo_proto::packet::packet_data::PacketData;
|
||||
|
||||
fn main() {
|
||||
let _ = domo_proto::packet::Packet::V1 {
|
||||
src: Identifier::from(0xAABBCCDD),
|
||||
dest: Identifier::from(0xAABBCCDD),
|
||||
packet_id: Identifier::from(0x00000001),
|
||||
reply_to: Identifier::from(0x00000000),
|
||||
command: 0x00,
|
||||
data: PacketData::default(),
|
||||
};
|
||||
|
||||
let buf = vec![
|
||||
0x00, 0x01, 0x00, 0x10, 0x00, 0xFF, 0x20, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x01, 0xF0, 0xAA, 0xBB, 0xCC, 0xDD,
|
||||
];
|
||||
|
||||
println!("{:?}", DataType::get_data(buf));
|
||||
}
|
98
domo_proto/src/packet/data_types.rs
Normal file
98
domo_proto/src/packet/data_types.rs
Normal file
|
@ -0,0 +1,98 @@
|
|||
use crate::prelude::{as_u32_be, as_u64_be};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DataType {
|
||||
// Basic types
|
||||
Nothing,
|
||||
Switch(bool),
|
||||
Slider(u8),
|
||||
Text(String),
|
||||
|
||||
// Cosmetic
|
||||
RGB(u8, u8, u8),
|
||||
|
||||
// Time & Space
|
||||
Seconds(u64),
|
||||
|
||||
// Domo Types
|
||||
NodeRef(u32),
|
||||
}
|
||||
|
||||
impl DataType {
|
||||
pub fn get_data_size(data_type: u8) -> usize {
|
||||
match data_type {
|
||||
0x01 => 1,
|
||||
0x02 => 1,
|
||||
0x03 => 256,
|
||||
0x10 => 3,
|
||||
0x90 => 8,
|
||||
0xF0 => 4,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_data(buf: Vec<u8>) -> Vec<DataType> {
|
||||
let mut data = vec![];
|
||||
let mut pieces = vec![];
|
||||
let mut byte = buf.iter().peekable();
|
||||
|
||||
while let Some(&c) = byte.next() {
|
||||
let mut piece = vec![c];
|
||||
let mut len = DataType::get_data_size(c);
|
||||
while len > 0 {
|
||||
if let Some(_) = byte.peek() {
|
||||
piece.push(*byte.next().unwrap());
|
||||
}
|
||||
len -= 1;
|
||||
}
|
||||
if piece.len() == DataType::get_data_size(c) + 1 {
|
||||
pieces.push(piece);
|
||||
} else {
|
||||
pieces.push(vec![0x00]);
|
||||
}
|
||||
}
|
||||
|
||||
for piece in pieces {
|
||||
data.push(DataType::from(piece))
|
||||
}
|
||||
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_data_type {
|
||||
($v: ident, $len: literal, $($t:tt)*) => {
|
||||
if $v.len() >= $len {
|
||||
$($t)*
|
||||
} else {
|
||||
DataType::Nothing
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for DataType {
|
||||
/// Turn a Vec<u8> into a single data type.
|
||||
/// Returns Datatype::Nothing if it's nothing or can find nothing.
|
||||
fn from(value: Vec<u8>) -> Self {
|
||||
if value.len() == 0 {
|
||||
return DataType::Nothing;
|
||||
};
|
||||
match value[0] {
|
||||
0x01 => impl_data_type!(value, 1, DataType::Switch(value[1] != 0)),
|
||||
0x02 => impl_data_type!(value, 1, DataType::Slider(value[1])),
|
||||
0x03 => impl_data_type!(
|
||||
value,
|
||||
256,
|
||||
if let Ok(s) = String::from_utf8(value[1..257].to_vec()) {
|
||||
DataType::Text(s)
|
||||
} else {
|
||||
DataType::Nothing
|
||||
}
|
||||
),
|
||||
0x10 => 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(as_u32_be(&value[1..5]))),
|
||||
_ => DataType::Nothing,
|
||||
}
|
||||
}
|
||||
}
|
45
domo_proto/src/packet/identifier.rs
Normal file
45
domo_proto/src/packet/identifier.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
use std::fmt::{Formatter, LowerHex};
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct Identifier {
|
||||
pub bytes: [u8; 4],
|
||||
}
|
||||
|
||||
impl LowerHex for Identifier {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{:x}{:x}{:x}{:x}",
|
||||
self.bytes[0], self.bytes[1], self.bytes[2], self.bytes[3]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&[u8]> for Identifier {
|
||||
fn from(value: &[u8]) -> Self {
|
||||
let mut bytes = [0_u8; 4];
|
||||
bytes[..value.len()].copy_from_slice(value);
|
||||
Identifier {
|
||||
bytes: [bytes[0], bytes[1], bytes[2], bytes[3]],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for Identifier {
|
||||
fn from(value: Vec<u8>) -> Self {
|
||||
Identifier::from(value.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for Identifier {
|
||||
fn from(value: u32) -> Self {
|
||||
Identifier {
|
||||
bytes: [
|
||||
(value >> 24) as u8,
|
||||
(value >> 16) as u8,
|
||||
(value >> 8) as u8,
|
||||
(value >> 0) as u8,
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
94
domo_proto/src/packet/mod.rs
Normal file
94
domo_proto/src/packet/mod.rs
Normal file
|
@ -0,0 +1,94 @@
|
|||
pub mod data_types;
|
||||
pub mod identifier;
|
||||
pub mod packet_data;
|
||||
|
||||
use crate::prelude;
|
||||
use identifier::Identifier;
|
||||
use packet_data::PacketData;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Packet {
|
||||
V1 {
|
||||
src: Identifier,
|
||||
dest: Identifier,
|
||||
packet_id: Identifier,
|
||||
reply_to: Identifier,
|
||||
command: u8,
|
||||
data: PacketData,
|
||||
},
|
||||
}
|
||||
|
||||
impl Packet {
|
||||
/// Build packet **without** CRC32
|
||||
pub fn build_base_packet(&self) -> Vec<u8> {
|
||||
match self {
|
||||
Packet::V1 {
|
||||
src,
|
||||
dest,
|
||||
packet_id,
|
||||
reply_to,
|
||||
command,
|
||||
data,
|
||||
} => {
|
||||
let mut buf = Vec::new();
|
||||
let data_length = data.len();
|
||||
buf.push(0x01);
|
||||
buf.extend_from_slice(&src.bytes);
|
||||
buf.extend_from_slice(&dest.bytes);
|
||||
buf.extend_from_slice(&packet_id.bytes);
|
||||
buf.extend_from_slice(&reply_to.bytes);
|
||||
buf.push(command.clone());
|
||||
buf.push((data_length >> 8) as u8);
|
||||
buf.push((data_length & 0xFF) as u8);
|
||||
buf.extend(data.get_data());
|
||||
buf
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_full_packet(&self) -> Vec<u8> {
|
||||
match self {
|
||||
Packet::V1 { .. } => {
|
||||
let mut buf = self.build_base_packet();
|
||||
buf.extend_from_slice(crc32fast::hash(&buf.as_slice()).to_be_bytes().as_ref());
|
||||
buf
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<u8>> for Packet {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
match value[0] {
|
||||
0x01 => {
|
||||
let header: Vec<u16> = value[..0x15].iter().map(|v| u16::from(*v)).collect();
|
||||
let data_length = (header[0x12] << 8) | header[0x13];
|
||||
let crc32 = prelude::as_u32_be(&value[(0x14 + data_length as usize)..]);
|
||||
let packet = Packet::V1 {
|
||||
src: Identifier::from(&value[0x01..0x05]),
|
||||
dest: Identifier::from(&value[0x05..0x09]),
|
||||
packet_id: Identifier::from(&value[0x09..0x0D]),
|
||||
reply_to: Identifier::from(&value[0x0D..0x11]),
|
||||
command: value[0x11],
|
||||
data: PacketData::new(value[0x14..(data_length as usize + 0x14)].to_owned()),
|
||||
};
|
||||
if crc32fast::hash(packet.build_base_packet().as_slice()) != crc32 {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<u8>> for Packet {
|
||||
fn into(self) -> Vec<u8> {
|
||||
self.build_full_packet()
|
||||
}
|
||||
}
|
24
domo_proto/src/packet/packet_data.rs
Normal file
24
domo_proto/src/packet/packet_data.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
#[derive(Debug)]
|
||||
pub struct PacketData {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl PacketData {
|
||||
pub fn new(data: Vec<u8>) -> PacketData {
|
||||
PacketData { data }
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.data.len()
|
||||
}
|
||||
|
||||
pub fn get_data(&self) -> Vec<u8> {
|
||||
self.data.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PacketData {
|
||||
fn default() -> Self {
|
||||
PacketData { data: vec![] }
|
||||
}
|
||||
}
|
17
domo_proto/src/prelude.rs
Normal file
17
domo_proto/src/prelude.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
pub fn as_u32_be(array: &[u8]) -> u32 {
|
||||
((array[0] as u32) << 24)
|
||||
+ ((array[1] as u32) << 16)
|
||||
+ ((array[2] as u32) << 8)
|
||||
+ ((array[3] as u32) << 0)
|
||||
}
|
||||
|
||||
pub fn as_u64_be(array: &[u8]) -> u64 {
|
||||
((array[0] as u64) << 56)
|
||||
+ ((array[1] as u64) << 48)
|
||||
+ ((array[2] as u64) << 40)
|
||||
+ ((array[3] as u64) << 32)
|
||||
+ ((array[4] as u64) << 24)
|
||||
+ ((array[5] as u64) << 16)
|
||||
+ ((array[6] as u64) << 8)
|
||||
+ ((array[7] as u64) << 0)
|
||||
}
|
Loading…
Reference in a new issue