diff --git a/atem-connection-rs/src/atem.rs b/atem-connection-rs/src/atem.rs index 1d9aeb5..a3bf592 100644 --- a/atem-connection-rs/src/atem.rs +++ b/atem-connection-rs/src/atem.rs @@ -11,7 +11,7 @@ use crate::{ atem_lib::atem_socket::{AtemEvent, AtemSocketCommand, AtemSocketMessage, TrackingId}, commands::{ command_base::{BasicWritableCommand, DeserializedCommand}, - device_profile::DESERIALIZE_VERSION_RAW_NAME, + device_profile::version::DESERIALIZE_VERSION_RAW_NAME, init_complete::DESERIALIZE_INIT_COMPLETE_RAW_NAME, time::DESERIALIZE_TIME_RAW_NAME, }, diff --git a/atem-connection-rs/src/commands/device_profile.rs b/atem-connection-rs/src/commands/device_profile.rs index 910c1c9..bc5afef 100644 --- a/atem-connection-rs/src/commands/device_profile.rs +++ b/atem-connection-rs/src/commands/device_profile.rs @@ -1,34 +1,2 @@ -use std::sync::Arc; - -use crate::enums::ProtocolVersion; - -use super::command_base::{CommandDeserializer, DeserializedCommand}; - -pub const DESERIALIZE_VERSION_RAW_NAME: &str = "_ver"; - -#[derive(Debug)] -pub struct VersionCommand { - pub version: ProtocolVersion, -} - -impl DeserializedCommand for VersionCommand { - fn raw_name(&self) -> &'static str { - DESERIALIZE_VERSION_RAW_NAME - } - - fn apply_to_state(&self, state: &mut crate::state::AtemState) { - state.info.api_version = self.version; - } -} - -#[derive(Default)] -pub struct VersionCommandDeserializer {} - -impl CommandDeserializer for VersionCommandDeserializer { - fn deserialize(&self, buffer: &[u8]) -> std::sync::Arc { - let version = u32::from_be_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]); - let version: ProtocolVersion = version.try_into().expect("Invalid protocol version"); - - Arc::new(VersionCommand { version }) - } -} +pub mod product_identifier; +pub mod version; diff --git a/atem-connection-rs/src/commands/device_profile/product_identifier.rs b/atem-connection-rs/src/commands/device_profile/product_identifier.rs new file mode 100644 index 0000000..17f1de6 --- /dev/null +++ b/atem-connection-rs/src/commands/device_profile/product_identifier.rs @@ -0,0 +1,64 @@ +use std::{ffi::CString, sync::Arc}; + +use crate::{ + commands::command_base::{CommandDeserializer, DeserializedCommand}, + enums::Model, +}; + +pub const DESERIALIZE_PRODUCT_IDENTIFIER_RAW_NAME: &str = "_pin"; + +#[derive(Debug)] +pub struct ProductIdentifierCommand { + pub product_identifier: String, + pub model: Model, +} + +impl DeserializedCommand for ProductIdentifierCommand { + fn raw_name(&self) -> &'static str { + DESERIALIZE_PRODUCT_IDENTIFIER_RAW_NAME + } + + fn apply_to_state(&self, state: &mut crate::state::AtemState) { + state.info.product_identifier = Some(self.product_identifier.clone()); + state.info.model = self.model.clone(); + + match state.info.model { + Model::TwoME + | Model::TwoME4K + | Model::TwoMEBS4K + | Model::Constellation + | Model::Constellation8K + | Model::ConstellationHD4ME + | Model::Constellation4K4ME => { + state.info.power = vec![false, false]; + } + _ => { + state.info.power = vec![false]; + } + } + } +} + +#[derive(Default)] +pub struct ProductIdentifierCommandDeserializer {} + +impl CommandDeserializer for ProductIdentifierCommandDeserializer { + fn deserialize(&self, buffer: &[u8]) -> std::sync::Arc { + let null_byte_index = buffer + .iter() + .position(|byte| *byte == b'\0') + .expect("No null byte"); + let product_identifier = + CString::from_vec_with_nul(buffer[..(null_byte_index + 1)].to_vec()) + .expect("Malformed string"); + let model = buffer[40]; + + Arc::new(ProductIdentifierCommand { + product_identifier: product_identifier + .to_str() + .expect("Invalid rust string") + .to_string(), + model: model.into(), + }) + } +} diff --git a/atem-connection-rs/src/commands/device_profile/version.rs b/atem-connection-rs/src/commands/device_profile/version.rs new file mode 100644 index 0000000..d6dfbaa --- /dev/null +++ b/atem-connection-rs/src/commands/device_profile/version.rs @@ -0,0 +1,35 @@ +use std::sync::Arc; + +use crate::{ + commands::command_base::{CommandDeserializer, DeserializedCommand}, + enums::ProtocolVersion, +}; + +pub const DESERIALIZE_VERSION_RAW_NAME: &str = "_ver"; + +#[derive(Debug)] +pub struct VersionCommand { + pub version: ProtocolVersion, +} + +impl DeserializedCommand for VersionCommand { + fn raw_name(&self) -> &'static str { + DESERIALIZE_VERSION_RAW_NAME + } + + fn apply_to_state(&self, state: &mut crate::state::AtemState) { + state.info.api_version = self.version; + } +} + +#[derive(Default)] +pub struct VersionCommandDeserializer {} + +impl CommandDeserializer for VersionCommandDeserializer { + fn deserialize(&self, buffer: &[u8]) -> std::sync::Arc { + let version = u32::from_be_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]); + let version: ProtocolVersion = version.try_into().expect("Invalid protocol version"); + + Arc::new(VersionCommand { version }) + } +} diff --git a/atem-connection-rs/src/commands/parse_commands.rs b/atem-connection-rs/src/commands/parse_commands.rs index 222c256..f09ea3f 100644 --- a/atem-connection-rs/src/commands/parse_commands.rs +++ b/atem-connection-rs/src/commands/parse_commands.rs @@ -2,7 +2,12 @@ use std::{collections::VecDeque, sync::Arc}; use super::{ command_base::{CommandDeserializer, DeserializedCommand}, - device_profile::{VersionCommandDeserializer, DESERIALIZE_VERSION_RAW_NAME}, + device_profile::{ + product_identifier::{ + ProductIdentifierCommandDeserializer, DESERIALIZE_PRODUCT_IDENTIFIER_RAW_NAME, + }, + version::{VersionCommandDeserializer, DESERIALIZE_VERSION_RAW_NAME}, + }, init_complete::{InitCompleteDeserializer, DESERIALIZE_INIT_COMPLETE_RAW_NAME}, mix_effects::program_input::{ProgramInputDeserializer, DESERIALIZE_PROGRAM_INPUT_RAW_NAME}, tally_by_source::{TallyBySourceDeserializer, DESERIALIZE_TALLY_BY_SOURCE_RAW_NAME}, @@ -29,6 +34,10 @@ pub fn deserialize_commands(payload: &[u8]) -> VecDeque Option Some(Box::::default()), DESERIALIZE_TALLY_BY_SOURCE_RAW_NAME => Some(Box::::default()), DESERIALIZE_TIME_RAW_NAME => Some(Box::::default()), + DESERIALIZE_PRODUCT_IDENTIFIER_RAW_NAME => { + Some(Box::::default()) + } _ => None, } } diff --git a/atem-connection-rs/src/enums/mod.rs b/atem-connection-rs/src/enums/mod.rs index e17d3ae..9fc3290 100644 --- a/atem-connection-rs/src/enums/mod.rs +++ b/atem-connection-rs/src/enums/mod.rs @@ -1,4 +1,4 @@ -#[derive(Clone, Default, PartialEq)] +#[derive(Debug, Clone, Default, PartialEq)] pub enum Model { #[default] Unknown = 0x00, @@ -19,6 +19,56 @@ pub enum Model { MiniProISO = 0x0f, MiniExtreme = 0x10, MiniExtremeISO = 0x11, + ConstellationHD1ME = 0x12, + ConstellationHD2ME = 0x13, + ConstellationHD4ME = 0x14, + SDI = 0x15, + SDIProISO = 0x16, + SDIExtremeISO = 0x17, + // 0x18 ?? + // 0x19 ?? + TelevisionStudioHD8 = 0x1a, + TelevisionStudioHD8ISO = 0x1b, + // 0x1c ?? + // 0x1d ?? + Constellation4K4ME = 0x1e, + // 0x1f ?? + TelevisionStudio4K8 = 0x20, +} + +impl From for Model { + fn from(value: u8) -> Self { + match value { + 0x01 => Model::TVS, + 0x02 => Model::OneME, + 0x03 => Model::TwoME, + 0x04 => Model::PS4K, + 0x05 => Model::OneME4K, + 0x06 => Model::TwoME4K, + 0x07 => Model::TwoMEBS4K, + 0x08 => Model::TVSHD, + 0x09 => Model::TVSProHD, + 0x0a => Model::TVSPro4K, + 0x0b => Model::Constellation, + 0x0c => Model::Constellation8K, + 0x0d => Model::Mini, + 0x0e => Model::MiniPro, + 0x0f => Model::MiniProISO, + 0x10 => Model::MiniExtreme, + 0x11 => Model::MiniExtremeISO, + 0x12 => Model::ConstellationHD1ME, + 0x13 => Model::ConstellationHD2ME, + 0x14 => Model::ConstellationHD4ME, + 0x15 => Model::SDI, + 0x16 => Model::SDIProISO, + 0x17 => Model::SDIExtremeISO, + 0x1a => Model::TelevisionStudioHD8, + 0x1b => Model::TelevisionStudioHD8ISO, + 0x1e => Model::Constellation4K4ME, + 0x20 => Model::TelevisionStudio4K8, + _ => Model::Unknown, + } + } } #[derive(Debug, Default, Clone, Copy, PartialEq)]