use crate::error::NanonisError;
use crate::signals::SignalFrame;
use crate::tcplog::TCPLogStatus;
use byteorder::{BigEndian, ReadBytesExt};
use std::io::{Cursor, Read};
use std::net::{SocketAddr, TcpStream};
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
pub struct TCPLoggerStream {
stream: TcpStream,
buffer: Vec<u8>,
}
impl TCPLoggerStream {
pub fn new(addr: &str, stream_port: u16) -> Result<Self, NanonisError> {
let socket_addr: SocketAddr = format!("{addr}:{stream_port}")
.parse()
.map_err(|_| NanonisError::Protocol(format!("Invalid address: {addr}")))?;
let stream = TcpStream::connect(socket_addr).map_err(|e| NanonisError::Io {
source: e,
context: format!("Failed to connect to TCP stream at {}", socket_addr),
})?;
stream
.set_read_timeout(Some(Duration::from_secs(30)))
.map_err(|e| NanonisError::Io {
source: e,
context: "Setting TCP stream read timeout".to_string(),
})?;
Ok(Self {
stream,
buffer: Vec::with_capacity(1024),
})
}
pub fn spawn_background_reader(mut self) -> mpsc::Receiver<SignalFrame> {
let (sender, receiver) = mpsc::channel();
thread::spawn(move || {
while let Ok(frame) = self.read_frame() {
if sender.send(frame).is_err() {
break;
}
}
});
receiver
}
pub fn read_frame(&mut self) -> Result<SignalFrame, NanonisError> {
let header_size = 18;
self.buffer.resize(header_size, 0);
self.stream
.read_exact(&mut self.buffer[..header_size])
.map_err(|e| NanonisError::Io {
source: e,
context: "Reading TCP Logger frame header".to_string(),
})?;
let mut cursor = Cursor::new(&self.buffer[..header_size]);
let num_channels = cursor.read_u32::<BigEndian>()?;
let _oversampling = cursor.read_f32::<BigEndian>()?;
let counter = cursor.read_u64::<BigEndian>()?;
let state_val = cursor.read_u16::<BigEndian>()?;
let _state = TCPLogStatus::try_from(state_val as i32)?;
let data_size = (num_channels * 4) as usize; self.buffer.resize(data_size, 0);
self.stream
.read_exact(&mut self.buffer[..data_size])
.map_err(|e| NanonisError::Io {
source: e,
context: "Reading TCP Logger frame data".to_string(),
})?;
let mut cursor = Cursor::new(&self.buffer[..data_size]);
let mut data = Vec::with_capacity(num_channels as usize);
for _ in 0..num_channels {
data.push(cursor.read_f32::<BigEndian>()?);
}
Ok(SignalFrame { counter, data })
}
}