use std::net::SocketAddr;
use std::time::Duration;
use super::constants::IDNFLG_STATUS_REALTIME;
use super::parser::{ChunkType, ParsedChunk, ReceivedPoint, SampleFormat};
#[derive(Clone, Copy, Debug)]
pub struct ReceivedChunk<'a> {
pub source_addr: SocketAddr,
pub sequence: u16,
pub content_id: u16,
pub channel_id: u8,
pub chunk_type: ChunkType,
pub config_or_last_fragment: bool,
pub has_config: bool,
pub is_last_fragment: bool,
pub timestamp_us_u32: u32,
pub duration_us: u32,
pub format: SampleFormat,
pub points: &'a [ReceivedPoint],
}
impl<'a> ReceivedChunk<'a> {
pub(crate) fn new(source_addr: SocketAddr, chunk: &'a ParsedChunk) -> Self {
Self {
source_addr,
sequence: chunk.sequence,
content_id: chunk.content_id,
channel_id: chunk.channel_id,
chunk_type: chunk.chunk_type,
config_or_last_fragment: chunk.config_or_last_fragment,
has_config: chunk.has_config,
is_last_fragment: chunk.is_last_fragment,
timestamp_us_u32: chunk.timestamp_us_u32,
duration_us: chunk.duration_us,
format: chunk.format,
points: &chunk.points,
}
}
}
pub trait ServerBehavior: Send + 'static {
fn on_packet_received(&mut self, _raw_data: &[u8]) {}
fn on_frame_received(&mut self, _raw_data: &[u8]) {}
fn on_chunk_received(&mut self, chunk: ReceivedChunk<'_>) {
self.on_points_received(chunk.points);
}
fn on_points_received(&mut self, _points: &[ReceivedPoint]) {}
fn should_respond(&self, command: u8) -> bool;
fn get_status_byte(&self) -> u8;
fn get_ack_result_code(&self) -> u8;
fn get_simulated_latency(&self) -> Duration {
Duration::ZERO
}
fn on_client_connected(&mut self, _addr: SocketAddr) {}
fn on_client_disconnected(&mut self) {}
fn should_force_disconnect(&mut self) -> bool {
false
}
fn is_occupied(&self) -> bool {
false
}
fn is_excluded(&self) -> bool {
false
}
}
#[derive(Default)]
pub struct SimpleBehavior;
impl ServerBehavior for SimpleBehavior {
fn should_respond(&self, _command: u8) -> bool {
true
}
fn get_status_byte(&self) -> u8 {
IDNFLG_STATUS_REALTIME
}
fn get_ack_result_code(&self) -> u8 {
0x00
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Default)]
struct MinimalBehavior {
forwarded_points: usize,
}
impl ServerBehavior for MinimalBehavior {
fn should_respond(&self, _command: u8) -> bool {
true
}
fn get_status_byte(&self) -> u8 {
0
}
fn get_ack_result_code(&self) -> u8 {
0
}
fn on_points_received(&mut self, points: &[ReceivedPoint]) {
self.forwarded_points += points.len();
}
}
fn sample_chunk() -> ParsedChunk {
ParsedChunk {
sequence: 9,
content_id: 0xC003,
channel_id: 2,
chunk_type: ChunkType::Frame,
config_or_last_fragment: true,
has_config: true,
is_last_fragment: true,
timestamp_us_u32: 1234,
duration_us: 1000,
format: SampleFormat::Xyrgbi,
points: vec![
ReceivedPoint {
x: 0.0,
y: 0.0,
r: 1.0,
g: 0.0,
b: 0.0,
},
ReceivedPoint {
x: 0.5,
y: -0.5,
r: 0.0,
g: 1.0,
b: 0.0,
},
],
}
}
#[test]
fn simple_behavior_defaults() {
let mut b = SimpleBehavior;
assert!(b.should_respond(0x10));
assert_eq!(b.get_status_byte(), IDNFLG_STATUS_REALTIME);
assert_eq!(b.get_ack_result_code(), 0x00);
assert_eq!(b.get_simulated_latency(), Duration::ZERO);
assert!(!b.should_force_disconnect());
assert!(!b.is_occupied());
assert!(!b.is_excluded());
}
#[test]
fn default_on_chunk_received_forwards_to_on_points_received() {
let chunk = sample_chunk();
let addr: SocketAddr = "127.0.0.1:5000".parse().unwrap();
let received = ReceivedChunk::new(addr, &chunk);
let mut b = MinimalBehavior::default();
b.on_chunk_received(received);
assert_eq!(
b.forwarded_points, 2,
"default on_chunk_received should forward the chunk's points to on_points_received"
);
}
#[test]
fn received_chunk_mirrors_parsed_chunk_metadata() {
let chunk = sample_chunk();
let addr: SocketAddr = "127.0.0.1:5001".parse().unwrap();
let received = ReceivedChunk::new(addr, &chunk);
assert_eq!(received.source_addr, addr);
assert_eq!(received.sequence, chunk.sequence);
assert_eq!(received.content_id, chunk.content_id);
assert_eq!(received.channel_id, chunk.channel_id);
assert_eq!(received.chunk_type, chunk.chunk_type);
assert_eq!(
received.config_or_last_fragment,
chunk.config_or_last_fragment
);
assert_eq!(received.has_config, chunk.has_config);
assert_eq!(received.is_last_fragment, chunk.is_last_fragment);
assert_eq!(received.timestamp_us_u32, chunk.timestamp_us_u32);
assert_eq!(received.duration_us, chunk.duration_us);
assert_eq!(received.format, chunk.format);
assert_eq!(received.points.len(), chunk.points.len());
}
}