Skip to content

Commit 6866b75

Browse files
committed
Add missing files
1 parent f9c7d0f commit 6866b75

File tree

2 files changed

+231
-0
lines changed

2 files changed

+231
-0
lines changed

viz-udp-app-lib/src/common.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
use std::net::{Ipv4Addr, SocketAddr, ToSocketAddrs, UdpSocket};
2+
pub const HANDSHAKE: [u8; 4] = [0x43, 0x41, 0x57, 0x0];
3+
4+
pub const PROGRAM_NAME: &str = "caw_viz_udp_app";
5+
6+
fn wait_for_handshake(socket: &UdpSocket) -> anyhow::Result<SocketAddr> {
7+
let mut buf = [0; 8];
8+
loop {
9+
let (size, client_addr) = socket.recv_from(&mut buf)?;
10+
if size == HANDSHAKE.len() && buf[0..HANDSHAKE.len()] == HANDSHAKE {
11+
return Ok(client_addr);
12+
}
13+
}
14+
}
15+
16+
pub struct VizUdpServer {
17+
socket: UdpSocket,
18+
pub buf: Vec<u8>,
19+
client_running: bool,
20+
}
21+
22+
impl VizUdpServer {
23+
pub fn new<F: FnOnce(SocketAddr) -> anyhow::Result<()>>(
24+
start_client: F,
25+
) -> anyhow::Result<Self> {
26+
let socket = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, 0))?;
27+
log::info!("Viz udp server address: {:?}", socket.local_addr());
28+
start_client(socket.local_addr()?)?;
29+
let client_address = wait_for_handshake(&socket)?;
30+
log::info!("Viz udp client address: {:?}", client_address);
31+
socket.connect(client_address)?;
32+
socket.set_nonblocking(true)?;
33+
Ok(Self {
34+
socket,
35+
buf: Vec::new(),
36+
client_running: true,
37+
})
38+
}
39+
40+
/// Returns `true` iff it succeeded in sending the data and `false` if the client has
41+
/// disconnected.
42+
pub fn send_buf(&mut self) -> anyhow::Result<bool> {
43+
if !self.client_running {
44+
// If the client window is closed then there is nothing to do. Possibly we could
45+
// restart the client at this point in the future?
46+
return Ok(false);
47+
}
48+
match self.socket.send(&self.buf) {
49+
Ok(bytes_sent) => {
50+
if bytes_sent != self.buf.len() {
51+
log::error!(
52+
"Failed to send all data to viz udp client. Tried to send {} bytes. Actually sent {} bytes.",
53+
self.buf.len(),
54+
bytes_sent
55+
);
56+
}
57+
}
58+
Err(e) => match e.kind() {
59+
std::io::ErrorKind::ConnectionRefused => {
60+
self.client_running = false;
61+
log::error!("Viz udp client has closed!");
62+
}
63+
_ => anyhow::bail!(e),
64+
},
65+
}
66+
Ok(true)
67+
}
68+
}
69+
70+
pub struct VizUdpClient {
71+
socket: UdpSocket,
72+
pub buf: Vec<u8>,
73+
}
74+
75+
impl VizUdpClient {
76+
pub fn new<A: ToSocketAddrs>(server_address: A) -> anyhow::Result<Self> {
77+
let socket = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, 0))?;
78+
socket.connect(server_address)?;
79+
assert_eq!(socket.send(&HANDSHAKE)?, HANDSHAKE.len());
80+
socket.set_nonblocking(true)?;
81+
Ok(Self {
82+
socket,
83+
buf: vec![0; 32768],
84+
})
85+
}
86+
87+
/// Returns `Some(buf)` containing a buffer of bytes received, or `None` if no bytes are
88+
/// currently available.
89+
pub fn recv(&mut self) -> anyhow::Result<Option<&[u8]>> {
90+
match self.socket.recv(&mut self.buf) {
91+
Ok(size) => Ok(Some(&self.buf[0..size])),
92+
Err(e) => match e.kind() {
93+
std::io::ErrorKind::WouldBlock => Ok(None),
94+
_ => anyhow::bail!(e),
95+
},
96+
}
97+
}
98+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
use crate::common::{PROGRAM_NAME, VizUdpClient, VizUdpServer};
2+
3+
use std::{
4+
net::{SocketAddr, ToSocketAddrs},
5+
process::{Child, Command},
6+
};
7+
8+
// Based on the max number of bytes that can somewhat reliably be sent over UDP (508) divided by 4
9+
// as we'll be sending f32's. That gives us 127, but since we're actually sending pairs of f32's,
10+
// reduce this to an even number.
11+
const MAX_SAMPLES_TO_SEND: usize = 126;
12+
13+
pub struct Config {
14+
pub width: u32,
15+
pub height: u32,
16+
pub scale: f32,
17+
pub max_num_samples: usize,
18+
pub line_width: u32,
19+
pub alpha_scale: u8,
20+
pub red: u8,
21+
pub green: u8,
22+
pub blue: u8,
23+
}
24+
25+
impl Default for Config {
26+
fn default() -> Self {
27+
Self {
28+
width: 640,
29+
height: 480,
30+
scale: 500.0,
31+
max_num_samples: 5000,
32+
line_width: 1,
33+
alpha_scale: 255,
34+
red: 0,
35+
green: 255,
36+
blue: 0,
37+
}
38+
}
39+
}
40+
41+
fn samples_to_ne_bytes(samples: &[f32], out: &mut Vec<u8>) {
42+
out.clear();
43+
for sample in samples {
44+
out.extend_from_slice(&sample.to_ne_bytes());
45+
}
46+
}
47+
48+
fn samples_from_ne_bytes(bytes: &[u8], out: &mut Vec<f32>) {
49+
if bytes.len() % 4 != 0 {
50+
log::error!(
51+
"Received a buffer of bytes that is not a multiple of 4 bytes in length (length is {}).",
52+
bytes.len()
53+
);
54+
}
55+
out.clear();
56+
let mut buf = [0; 4];
57+
for w in bytes.chunks_exact(4) {
58+
buf.copy_from_slice(w);
59+
out.push(f32::from_ne_bytes(buf));
60+
}
61+
}
62+
63+
fn start_client_app(
64+
server_addr: SocketAddr,
65+
program_name: &str,
66+
config: &Config,
67+
) -> anyhow::Result<Child> {
68+
let mut command = Command::new(program_name);
69+
command.args([
70+
format!("--server={}", server_addr.to_string()),
71+
"oscilloscope".to_string(),
72+
format!("--width={}", config.width),
73+
format!("--height={}", config.height),
74+
format!("--scale={}", config.scale),
75+
format!("--max-num-samples={}", config.max_num_samples),
76+
format!("--line-width={}", config.line_width),
77+
format!("--alpha-scale={}", config.alpha_scale),
78+
format!("--red={}", config.red),
79+
format!("--green={}", config.green),
80+
format!("--blue={}", config.blue),
81+
]);
82+
Ok(command.spawn()?)
83+
}
84+
85+
pub struct Server(VizUdpServer);
86+
87+
impl Server {
88+
pub fn new(config: Config) -> anyhow::Result<Self> {
89+
Ok(Self(VizUdpServer::new(|server_addr| {
90+
let _client_process =
91+
start_client_app(server_addr, PROGRAM_NAME, &config)?;
92+
Ok(())
93+
})?))
94+
}
95+
96+
pub fn send_samples(&mut self, samples: &[f32]) -> anyhow::Result<()> {
97+
for samples_chunk in samples.chunks(MAX_SAMPLES_TO_SEND) {
98+
samples_to_ne_bytes(samples_chunk, &mut self.0.buf);
99+
if !self.0.send_buf()? {
100+
break;
101+
}
102+
}
103+
Ok(())
104+
}
105+
}
106+
107+
pub struct Client {
108+
raw: VizUdpClient,
109+
buf: Vec<f32>,
110+
}
111+
112+
impl Client {
113+
pub fn new<A: ToSocketAddrs>(server_address: A) -> anyhow::Result<Self> {
114+
Ok(Self {
115+
raw: VizUdpClient::new(server_address)?,
116+
buf: Vec::new(),
117+
})
118+
}
119+
120+
pub fn recv_sample(&mut self) -> anyhow::Result<bool> {
121+
Ok(match self.raw.recv()? {
122+
Some(buf_raw) => {
123+
samples_from_ne_bytes(buf_raw, &mut self.buf);
124+
true
125+
}
126+
None => false,
127+
})
128+
}
129+
130+
pub fn pairs(&self) -> impl Iterator<Item = (f32, f32)> {
131+
self.buf.chunks_exact(2).map(|c| (c[0], c[1]))
132+
}
133+
}

0 commit comments

Comments
 (0)