wip: _top

This commit is contained in:
Baud 2024-03-18 19:23:28 +00:00
parent 493bf7a6e8
commit d2f41821c0
12 changed files with 158 additions and 12 deletions

View File

@ -103,6 +103,8 @@ pub struct AtemSocket {
socket: Option<UdpSocket>, socket: Option<UdpSocket>,
address: SocketAddr, address: SocketAddr,
protocol_version: ProtocolVersion,
last_received_at: SystemTime, last_received_at: SystemTime,
last_received_packed_id: u16, last_received_packed_id: u16,
in_flight: Vec<InFlightPacket>, in_flight: Vec<InFlightPacket>,
@ -159,6 +161,8 @@ impl AtemSocket {
socket: None, socket: None,
address: "0.0.0.0:0".parse().unwrap(), address: "0.0.0.0:0".parse().unwrap(),
protocol_version: ProtocolVersion::V7_2,
last_received_at: SystemTime::now(), last_received_at: SystemTime::now(),
last_received_packed_id: 0, last_received_packed_id: 0,
in_flight: vec![], in_flight: vec![],
@ -532,7 +536,7 @@ impl AtemSocket {
} }
fn on_commands_received(&mut self, payload: &[u8]) { fn on_commands_received(&mut self, payload: &[u8]) {
let commands = deserialize_commands(payload); let commands = deserialize_commands(payload, &self.protocol_version);
let _ = self let _ = self
.atem_event_tx .atem_event_tx

View File

@ -8,7 +8,8 @@ pub trait DeserializedCommand: Send + Sync + Debug {
} }
pub trait CommandDeserializer: Send + Sync { pub trait CommandDeserializer: Send + Sync {
fn deserialize(&self, buffer: &[u8]) -> Arc<dyn DeserializedCommand>; fn deserialize(&self, buffer: &[u8], version: &ProtocolVersion)
-> Arc<dyn DeserializedCommand>;
} }
pub trait SerializableCommand: Send + Sync { pub trait SerializableCommand: Send + Sync {

View File

@ -1,2 +1,3 @@
pub mod product_identifier; pub mod product_identifier;
pub mod topology;
pub mod version; pub mod version;

View File

@ -2,7 +2,7 @@ use std::{ffi::CString, sync::Arc};
use crate::{ use crate::{
commands::command_base::{CommandDeserializer, DeserializedCommand}, commands::command_base::{CommandDeserializer, DeserializedCommand},
enums::Model, enums::{Model, ProtocolVersion},
}; };
pub const DESERIALIZE_PRODUCT_IDENTIFIER_RAW_NAME: &str = "_pin"; pub const DESERIALIZE_PRODUCT_IDENTIFIER_RAW_NAME: &str = "_pin";
@ -43,7 +43,11 @@ impl DeserializedCommand for ProductIdentifierCommand {
pub struct ProductIdentifierCommandDeserializer {} pub struct ProductIdentifierCommandDeserializer {}
impl CommandDeserializer for ProductIdentifierCommandDeserializer { impl CommandDeserializer for ProductIdentifierCommandDeserializer {
fn deserialize(&self, buffer: &[u8]) -> std::sync::Arc<dyn DeserializedCommand> { fn deserialize(
&self,
buffer: &[u8],
version: &ProtocolVersion,
) -> std::sync::Arc<dyn DeserializedCommand> {
let null_byte_index = buffer let null_byte_index = buffer
.iter() .iter()
.position(|byte| *byte == b'\0') .position(|byte| *byte == b'\0')

View File

@ -0,0 +1,91 @@
use std::sync::Arc;
use crate::{
commands::command_base::{CommandDeserializer, DeserializedCommand},
enums::ProtocolVersion,
};
pub const DESERIALIZE_TOPOLOGY_RAW_NAME: &str = "_top";
#[derive(Debug)]
pub struct TopologyCommand {
mix_effects: u8,
sources: u8,
auxilliaries: u8,
mix_minus_outputs: u8,
media_players: u8,
multiviewers: Option<u8>,
serial_ports: u8,
max_hyperdecks: u8,
dves: u8,
stingers: u8,
super_sources: u8,
talkback_channels: u8,
downstream_keyers: u8,
camera_control: bool,
advanced_chroma_keyers: bool,
only_configurable_outputs: bool,
}
impl DeserializedCommand for TopologyCommand {
fn raw_name(&self) -> &'static str {
todo!()
}
fn apply_to_state(&self, state: &mut crate::state::AtemState) {
todo!()
}
}
pub struct TopologyCommandDeserializer {}
impl CommandDeserializer for TopologyCommandDeserializer {
fn deserialize(
&self,
buffer: &[u8],
version: &ProtocolVersion,
) -> std::sync::Arc<dyn crate::commands::command_base::DeserializedCommand> {
let v230offset = if *version > ProtocolVersion::V8_0_1 {
1
} else {
0
};
let multiviewers = if v230offset > 0 {
Some(buffer[6])
} else {
None
};
let advanced_chroma_keyers = if buffer.len() > 20 {
buffer[21 + v230offset] == 1
} else {
false
};
let only_configurable_outputs = if buffer.len() > 20 {
buffer[22 + v230offset] == 1
} else {
false
};
Arc::new(TopologyCommand {
mix_effects: buffer[0],
sources: buffer[1],
downstream_keyers: buffer[2],
auxilliaries: buffer[3],
mix_minus_outputs: buffer[4],
media_players: buffer[5],
multiviewers,
serial_ports: buffer[6 + v230offset],
max_hyperdecks: buffer[7 + v230offset],
dves: buffer[8 + v230offset],
stingers: buffer[9 + v230offset],
super_sources: buffer[10 + v230offset],
talkback_channels: buffer[12 + v230offset],
camera_control: buffer[17 + v230offset] == 1,
advanced_chroma_keyers,
only_configurable_outputs,
})
}
}

View File

@ -26,7 +26,11 @@ impl DeserializedCommand for VersionCommand {
pub struct VersionCommandDeserializer {} pub struct VersionCommandDeserializer {}
impl CommandDeserializer for VersionCommandDeserializer { impl CommandDeserializer for VersionCommandDeserializer {
fn deserialize(&self, buffer: &[u8]) -> std::sync::Arc<dyn DeserializedCommand> { fn deserialize(
&self,
buffer: &[u8],
version: &ProtocolVersion,
) -> std::sync::Arc<dyn DeserializedCommand> {
let version = u32::from_be_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]); let version = u32::from_be_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
let version: ProtocolVersion = version.try_into().expect("Invalid protocol version"); let version: ProtocolVersion = version.try_into().expect("Invalid protocol version");

View File

@ -1,5 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use crate::enums::ProtocolVersion;
use super::command_base::{CommandDeserializer, DeserializedCommand}; use super::command_base::{CommandDeserializer, DeserializedCommand};
pub const DESERIALIZE_INIT_COMPLETE_RAW_NAME: &str = "InCm"; pub const DESERIALIZE_INIT_COMPLETE_RAW_NAME: &str = "InCm";
@ -19,7 +21,11 @@ impl DeserializedCommand for InitComplete {
pub struct InitCompleteDeserializer {} pub struct InitCompleteDeserializer {}
impl CommandDeserializer for InitCompleteDeserializer { impl CommandDeserializer for InitCompleteDeserializer {
fn deserialize(&self, _buffer: &[u8]) -> std::sync::Arc<dyn DeserializedCommand> { fn deserialize(
&self,
_buffer: &[u8],
version: &ProtocolVersion,
) -> std::sync::Arc<dyn DeserializedCommand> {
Arc::new(InitComplete {}) Arc::new(InitComplete {})
} }
} }

View File

@ -4,6 +4,7 @@ use crate::{
commands::command_base::{ commands::command_base::{
BasicWritableCommand, CommandDeserializer, DeserializedCommand, SerializableCommand, BasicWritableCommand, CommandDeserializer, DeserializedCommand, SerializableCommand,
}, },
enums::ProtocolVersion,
state::util::get_mix_effect, state::util::get_mix_effect,
}; };
@ -58,7 +59,11 @@ impl DeserializedCommand for ProgramInput {
pub struct ProgramInputDeserializer {} pub struct ProgramInputDeserializer {}
impl CommandDeserializer for ProgramInputDeserializer { impl CommandDeserializer for ProgramInputDeserializer {
fn deserialize(&self, buffer: &[u8]) -> Arc<dyn DeserializedCommand> { fn deserialize(
&self,
buffer: &[u8],
version: &ProtocolVersion,
) -> Arc<dyn DeserializedCommand> {
let mix_effect = buffer[0]; let mix_effect = buffer[0];
let source = u16::from_be_bytes([buffer[2], buffer[3]]); let source = u16::from_be_bytes([buffer[2], buffer[3]]);

View File

@ -1,5 +1,7 @@
use std::{collections::VecDeque, sync::Arc}; use std::{collections::VecDeque, sync::Arc};
use crate::enums::ProtocolVersion;
use super::{ use super::{
command_base::{CommandDeserializer, DeserializedCommand}, command_base::{CommandDeserializer, DeserializedCommand},
device_profile::{ device_profile::{
@ -14,7 +16,10 @@ use super::{
time::{TimeDeserializer, DESERIALIZE_TIME_RAW_NAME}, time::{TimeDeserializer, DESERIALIZE_TIME_RAW_NAME},
}; };
pub fn deserialize_commands(payload: &[u8]) -> VecDeque<Arc<dyn DeserializedCommand>> { pub fn deserialize_commands(
payload: &[u8],
version: &ProtocolVersion,
) -> VecDeque<Arc<dyn DeserializedCommand>> {
let mut parsed_commands = VecDeque::new(); let mut parsed_commands = VecDeque::new();
let mut head = 0; let mut head = 0;
@ -31,13 +36,14 @@ pub fn deserialize_commands(payload: &[u8]) -> VecDeque<Arc<dyn DeserializedComm
log::debug!("Received command {} with length {}", name, length); log::debug!("Received command {} with length {}", name, length);
if let Some(deserializer) = command_deserializer_from_string(name.as_str()) { if let Some(deserializer) = command_deserializer_from_string(name.as_str()) {
let deserialized_command = deserializer.deserialize(&payload[head + 8..head + length]); let deserialized_command =
deserializer.deserialize(&payload[head + 8..head + length], version);
log::debug!("Received {:?}", deserialized_command); log::debug!("Received {:?}", deserialized_command);
parsed_commands.push_back(deserialized_command); parsed_commands.push_back(deserialized_command);
} else { } else {
log::warn!("Received command {name} for which there is no deserializer."); log::warn!("Received command {name} for which there is no deserializer.");
// TODO: Remove! // TODO: Remove!
todo!("Write deserializer for {name}."); // todo!("Write deserializer for {name}.");
} }
head += length; head += length;

View File

@ -1,5 +1,7 @@
use std::{collections::HashMap, sync::Arc}; use std::{collections::HashMap, sync::Arc};
use crate::enums::ProtocolVersion;
use super::command_base::{CommandDeserializer, DeserializedCommand}; use super::command_base::{CommandDeserializer, DeserializedCommand};
pub const DESERIALIZE_TALLY_BY_SOURCE_RAW_NAME: &str = "TlSr"; pub const DESERIALIZE_TALLY_BY_SOURCE_RAW_NAME: &str = "TlSr";
@ -29,7 +31,11 @@ impl DeserializedCommand for TallyBySource {
pub struct TallyBySourceDeserializer {} pub struct TallyBySourceDeserializer {}
impl CommandDeserializer for TallyBySourceDeserializer { impl CommandDeserializer for TallyBySourceDeserializer {
fn deserialize(&self, buffer: &[u8]) -> Arc<dyn DeserializedCommand> { fn deserialize(
&self,
buffer: &[u8],
version: &ProtocolVersion,
) -> Arc<dyn DeserializedCommand> {
let source_count = u16::from_be_bytes([buffer[0], buffer[1]]) as usize; let source_count = u16::from_be_bytes([buffer[0], buffer[1]]) as usize;
log::debug!("{:?}", buffer); log::debug!("{:?}", buffer);

View File

@ -1,5 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use crate::enums::ProtocolVersion;
use super::command_base::{CommandDeserializer, DeserializedCommand}; use super::command_base::{CommandDeserializer, DeserializedCommand};
pub const DESERIALIZE_TIME_RAW_NAME: &str = "Time"; pub const DESERIALIZE_TIME_RAW_NAME: &str = "Time";
@ -33,6 +35,7 @@ impl CommandDeserializer for TimeDeserializer {
fn deserialize( fn deserialize(
&self, &self,
buffer: &[u8], buffer: &[u8],
version: &ProtocolVersion,
) -> std::sync::Arc<dyn super::command_base::DeserializedCommand> { ) -> std::sync::Arc<dyn super::command_base::DeserializedCommand> {
let info = TimeInfo { let info = TimeInfo {
hour: buffer[0], hour: buffer[0],

View File

@ -1,3 +1,5 @@
use std::fmt::Display;
#[derive(Debug, Clone, Default, PartialEq)] #[derive(Debug, Clone, Default, PartialEq)]
pub enum Model { pub enum Model {
#[default] #[default]
@ -71,7 +73,7 @@ impl From<u8> for Model {
} }
} }
#[derive(Debug, Default, Clone, Copy, PartialEq)] #[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)]
pub enum ProtocolVersion { pub enum ProtocolVersion {
#[default] #[default]
Unknown = 0, Unknown = 0,
@ -98,6 +100,19 @@ impl TryFrom<u32> for ProtocolVersion {
} }
} }
impl Display for ProtocolVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ProtocolVersion::Unknown => write!(f, "Unknown"),
ProtocolVersion::V7_2 => write!(f, "v7.2"),
ProtocolVersion::V7_5_2 => write!(f, "v7.5.2"),
ProtocolVersion::V8_0 => write!(f, "v8.0"),
ProtocolVersion::V8_0_1 => write!(f, "v8.0.1"),
ProtocolVersion::V8_1_1 => write!(f, "v8.1.1"),
}
}
}
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub enum TransitionStyle { pub enum TransitionStyle {
MIX = 0x00, MIX = 0x00,