chore: Various cleanups
This commit is contained in:
parent
2bbf2c5c6b
commit
90b2cfd984
|
@ -4,7 +4,7 @@ use std::{
|
|||
sync::Arc,
|
||||
};
|
||||
|
||||
use tokio::sync::{mpsc::error::TryRecvError, Semaphore};
|
||||
use tokio::{select, sync::Semaphore};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::{
|
||||
|
@ -61,33 +61,32 @@ impl Atem {
|
|||
let mut state = AtemState::default();
|
||||
|
||||
while !cancel.is_cancelled() {
|
||||
match atem_event_rx.try_recv() {
|
||||
Ok(event) => match event {
|
||||
AtemEvent::Error(_) => todo!(),
|
||||
AtemEvent::Info(_) => todo!(),
|
||||
AtemEvent::Debug(_) => todo!(),
|
||||
AtemEvent::Connected => {
|
||||
log::info!("Atem connected");
|
||||
}
|
||||
AtemEvent::Disconnected => todo!(),
|
||||
AtemEvent::ReceivedCommands(commands) => {
|
||||
self.mutate_state(&mut state, &mut status, commands).await
|
||||
}
|
||||
AtemEvent::AckedCommand(tracking_id) => {
|
||||
log::debug!("Received tracking Id {tracking_id}");
|
||||
if let Some(semaphore) =
|
||||
self.waiting_semaphores.read().await.get(&tracking_id)
|
||||
{
|
||||
semaphore.add_permits(1);
|
||||
} else {
|
||||
log::warn!("Received tracking Id {tracking_id} with no-one waiting for it to be resolved.")
|
||||
select! {
|
||||
_ = cancel.cancelled() => {},
|
||||
message = atem_event_rx.recv() => match message {
|
||||
Some(event) => match event {
|
||||
AtemEvent::Connected => {
|
||||
log::info!("Atem connected");
|
||||
}
|
||||
AtemEvent::Disconnected => todo!(),
|
||||
AtemEvent::ReceivedCommands(commands) => {
|
||||
self.mutate_state(&mut state, &mut status, commands).await
|
||||
}
|
||||
AtemEvent::AckedCommand(tracking_id) => {
|
||||
log::debug!("Received tracking Id {tracking_id}");
|
||||
if let Some(semaphore) =
|
||||
self.waiting_semaphores.read().await.get(&tracking_id)
|
||||
{
|
||||
semaphore.add_permits(1);
|
||||
} else {
|
||||
log::warn!("Received tracking Id {tracking_id} with no-one waiting for it to be resolved.")
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
log::info!("ATEM event channel has closed, exiting event loop.");
|
||||
cancel.cancel();
|
||||
}
|
||||
},
|
||||
Err(TryRecvError::Empty) => {}
|
||||
Err(TryRecvError::Disconnected) => {
|
||||
log::info!("ATEM event channel has closed, exiting event loop.");
|
||||
cancel.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,8 @@ use std::{
|
|||
|
||||
use tokio::{
|
||||
net::UdpSocket,
|
||||
select,
|
||||
sync::{Barrier, Mutex},
|
||||
task::yield_now,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -55,9 +55,6 @@ pub struct TrackingIdsCallback {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub enum AtemEvent {
|
||||
Error(String),
|
||||
Info(String),
|
||||
Debug(String),
|
||||
Connected,
|
||||
Disconnected,
|
||||
ReceivedCommands(VecDeque<Arc<dyn DeserializedCommand>>),
|
||||
|
@ -179,60 +176,65 @@ impl AtemSocket {
|
|||
cancel: tokio_util::sync::CancellationToken,
|
||||
) {
|
||||
while !cancel.is_cancelled() {
|
||||
if let Ok(msg) = atem_message_rx.try_recv() {
|
||||
match msg {
|
||||
AtemSocketMessage::Connect {
|
||||
address,
|
||||
result_callback,
|
||||
} => {
|
||||
{
|
||||
let mut connected_callbacks = self.connected_callbacks.lock().await;
|
||||
connected_callbacks.push(result_callback);
|
||||
}
|
||||
if self.connect(address).await.is_err() {
|
||||
let mut connected_callbacks = self.connected_callbacks.lock().await;
|
||||
for callback in connected_callbacks.drain(0..) {
|
||||
let _ = callback.send(false);
|
||||
select! {
|
||||
_ = cancel.cancelled() => {},
|
||||
message = atem_message_rx.recv() => {
|
||||
match message {
|
||||
Some(AtemSocketMessage::Connect {
|
||||
address,
|
||||
result_callback,
|
||||
}) => {
|
||||
{
|
||||
let mut connected_callbacks = self.connected_callbacks.lock().await;
|
||||
connected_callbacks.push(result_callback);
|
||||
}
|
||||
if self.connect(address).await.is_err() {
|
||||
let mut connected_callbacks = self.connected_callbacks.lock().await;
|
||||
for callback in connected_callbacks.drain(0..) {
|
||||
let _ = callback.send(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
AtemSocketMessage::Disconnect => self.disconnect(),
|
||||
AtemSocketMessage::SendCommands {
|
||||
commands,
|
||||
tracking_ids_callback,
|
||||
} => {
|
||||
let barrier = Arc::new(Barrier::new(2));
|
||||
tracking_ids_callback
|
||||
.send(TrackingIdsCallback {
|
||||
tracking_ids: self.send_commands(commands).await,
|
||||
barrier: barrier.clone(),
|
||||
})
|
||||
.ok();
|
||||
Some(AtemSocketMessage::Disconnect) => self.disconnect(),
|
||||
Some(AtemSocketMessage::SendCommands {
|
||||
commands,
|
||||
tracking_ids_callback,
|
||||
}) => {
|
||||
let barrier = Arc::new(Barrier::new(2));
|
||||
tracking_ids_callback
|
||||
.send(TrackingIdsCallback {
|
||||
tracking_ids: self.send_commands(commands).await,
|
||||
barrier: barrier.clone(),
|
||||
})
|
||||
.ok();
|
||||
|
||||
// Let's play the game "Synchronisation Shenanigans"!
|
||||
// So, we are sending tracking Ids to the sender of this message, the sender will then wait
|
||||
// for each of these tracking Ids to be ACK'd by the ATEM. However, the sender will need to
|
||||
// do ✨ some form of shenanigans ✨ in order to be ready to receive tracking Ids. So we send
|
||||
// them a barrier as part of the callback so that they can tell us that they are ready for
|
||||
// us to continue with ATEM communication, at which point we may immediately inform them of a
|
||||
// received tracking Id matching one included in this callback.
|
||||
//
|
||||
// Now, if we were being 🚩 Real Proper Software Developers 🚩 we'd probably expect the receiver
|
||||
// of the callback to do clever things so that if a tracking Id is received immediately, they
|
||||
// then wait for something that wants that tracking Id on their side, rather than blocking this
|
||||
// task so that the caller can do ✨ shenanigans ✨. However, that sounds far too clever and too
|
||||
// much like 🚩 Real Actual Work 🚩 so instead we've chosen to do this and hope that whichever
|
||||
// actor we're waiting on doesn't take _too_ long to do ✨ shenanigans ✨ before signalling that
|
||||
// they are ready. If they do, I suggest finding whoever wrote that code and bonking them 🔨.
|
||||
barrier.wait().await;
|
||||
// Let's play the game "Synchronisation Shenanigans"!
|
||||
// So, we are sending tracking Ids to the sender of this message, the sender will then wait
|
||||
// for each of these tracking Ids to be ACK'd by the ATEM. However, the sender will need to
|
||||
// do ✨ some form of shenanigans ✨ in order to be ready to receive tracking Ids. So we send
|
||||
// them a barrier as part of the callback so that they can tell us that they are ready for
|
||||
// us to continue with ATEM communication, at which point we may immediately inform them of a
|
||||
// received tracking Id matching one included in this callback.
|
||||
//
|
||||
// Now, if we were being 🚩 Real Proper Software Developers 🚩 we'd probably expect the receiver
|
||||
// of the callback to do clever things so that if a tracking Id is received immediately, they
|
||||
// then wait for something that wants that tracking Id on their side, rather than blocking this
|
||||
// task so that the caller can do ✨ shenanigans ✨. However, that sounds far too clever and too
|
||||
// much like 🚩 Real Actual Work 🚩 so instead we've chosen to do this and hope that whichever
|
||||
// actor we're waiting on doesn't take _too_ long to do ✨ shenanigans ✨ before signalling that
|
||||
// they are ready. If they do, I suggest finding whoever wrote that code and bonking them 🔨.
|
||||
barrier.wait().await;
|
||||
},
|
||||
None => {
|
||||
log::info!("ATEM message channel has closed, exiting event loop.");
|
||||
cancel.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
yield_now().await;
|
||||
}
|
||||
|
||||
self.tick().await;
|
||||
};
|
||||
}
|
||||
|
||||
self.tick().await;
|
||||
}
|
||||
|
||||
pub async fn connect(&mut self, address: SocketAddr) -> Result<(), io::Error> {
|
||||
|
@ -316,7 +318,7 @@ impl AtemSocket {
|
|||
|
||||
async fn restart_connection(&mut self) {
|
||||
self.disconnect();
|
||||
self.connect(self.address.clone()).await.ok();
|
||||
self.connect(self.address).await.ok();
|
||||
}
|
||||
|
||||
async fn tick(&mut self) {
|
||||
|
@ -470,7 +472,7 @@ impl AtemSocket {
|
|||
let flag: u8 = PacketFlag::AckReply.into();
|
||||
let opcode = u16::from(flag) << 11;
|
||||
let mut buffer: [u8; ACK_PACKET_LENGTH as _] = [0; 12];
|
||||
buffer[0..2].copy_from_slice(&u16::to_be_bytes(opcode as u16 | ACK_PACKET_LENGTH));
|
||||
buffer[0..2].copy_from_slice(&u16::to_be_bytes(opcode | ACK_PACKET_LENGTH));
|
||||
buffer[2..4].copy_from_slice(&u16::to_be_bytes(self.session_id));
|
||||
buffer[4..6].copy_from_slice(&u16::to_be_bytes(packet_id));
|
||||
self.send_packet(&buffer).await;
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::{enums::ProtocolVersion, state::AtemState};
|
|||
|
||||
pub trait DeserializedCommand: Send + Sync + Debug {
|
||||
fn raw_name(&self) -> &'static str;
|
||||
fn apply_to_state(&self, state: &mut AtemState) -> bool;
|
||||
fn apply_to_state(&self, state: &mut AtemState);
|
||||
}
|
||||
|
||||
pub trait CommandDeserializer: Send + Sync {
|
||||
|
|
|
@ -16,8 +16,8 @@ impl DeserializedCommand for VersionCommand {
|
|||
DESERIALIZE_VERSION_RAW_NAME
|
||||
}
|
||||
|
||||
fn apply_to_state(&self, state: &mut crate::state::AtemState) -> bool {
|
||||
todo!("Apply to state: Version")
|
||||
fn apply_to_state(&self, state: &mut crate::state::AtemState) {
|
||||
state.info.api_version = self.version;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ impl DeserializedCommand for InitComplete {
|
|||
DESERIALIZE_INIT_COMPLETE_RAW_NAME
|
||||
}
|
||||
|
||||
fn apply_to_state(&self, state: &mut crate::state::AtemState) -> bool {
|
||||
fn apply_to_state(&self, state: &mut crate::state::AtemState) {
|
||||
todo!("Apply to state: Init Complete")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ impl DeserializedCommand for ProgramInput {
|
|||
DESERIALIZE_PROGRAM_INPUT_RAW_NAME
|
||||
}
|
||||
|
||||
fn apply_to_state(&self, state: &mut crate::state::AtemState) -> bool {
|
||||
fn apply_to_state(&self, state: &mut crate::state::AtemState) {
|
||||
todo!("Apply to state: Program Input")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ impl DeserializedCommand for TallyBySource {
|
|||
DESERIALIZE_TALLY_BY_SOURCE_RAW_NAME
|
||||
}
|
||||
|
||||
fn apply_to_state(&self, state: &mut crate::state::AtemState) -> bool {
|
||||
fn apply_to_state(&self, state: &mut crate::state::AtemState) {
|
||||
todo!("Apply to state: Tally By Source")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,9 +23,7 @@ impl DeserializedCommand for Time {
|
|||
DESERIALIZE_TIME_RAW_NAME
|
||||
}
|
||||
|
||||
fn apply_to_state(&self, state: &mut crate::state::AtemState) -> bool {
|
||||
false
|
||||
}
|
||||
fn apply_to_state(&self, state: &mut crate::state::AtemState) {}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
|
@ -21,7 +21,7 @@ pub enum Model {
|
|||
MiniExtremeISO = 0x11,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||
pub enum ProtocolVersion {
|
||||
#[default]
|
||||
Unknown = 0,
|
||||
|
|
|
@ -16,16 +16,16 @@ pub mod video;
|
|||
|
||||
#[derive(Default, Clone, PartialEq)]
|
||||
pub struct AtemState {
|
||||
info: info::DeviceInfo,
|
||||
video: video::AtemVideoState,
|
||||
audio: Option<audio::AtemClassicAudioState>,
|
||||
fairlight: Option<fairlight::AtemFairlightAudioState>,
|
||||
media: media::MediaState,
|
||||
inputs: HashMap<u64, input::InputChannel>,
|
||||
pub info: info::DeviceInfo,
|
||||
pub video: video::AtemVideoState,
|
||||
pub audio: Option<audio::AtemClassicAudioState>,
|
||||
pub fairlight: Option<fairlight::AtemFairlightAudioState>,
|
||||
pub media: media::MediaState,
|
||||
pub inputs: HashMap<u64, input::InputChannel>,
|
||||
// macro is a rust keyword
|
||||
atem_macro: atem_macro::MacroState,
|
||||
settings: settings::SettingsState,
|
||||
recording: Option<recording::RecordingState>,
|
||||
streaming: Option<streaming::StreamingState>,
|
||||
color_generators: HashMap<u64, color::ColorGeneratorState>,
|
||||
pub atem_macro: atem_macro::MacroState,
|
||||
pub settings: settings::SettingsState,
|
||||
pub recording: Option<recording::RecordingState>,
|
||||
pub streaming: Option<streaming::StreamingState>,
|
||||
pub color_generators: HashMap<u64, color::ColorGeneratorState>,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue