use std::net::{SocketAddr, UdpSocket};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::{self, JoinHandle};
use std::time::Duration;
use rill_core::queues::{CommandEnum, SensorCommand};
use rill_core_actor::{ActorRef, ActorSystem};
use rill_osc::osc::{self, OscMessage, OscPacket};
use crate::engine::{ControlEvent, Module};
use crate::sensor::Sensor;
pub struct OscSensor {
id: String,
thread: Option<JoinHandle<()>>,
running: Arc<AtomicBool>,
events: Option<ActorRef<ControlEvent>>,
bind_addr: SocketAddr,
}
impl OscSensor {
pub fn new(id: impl Into<String>, bind_addr: SocketAddr) -> Self {
Self {
id: id.into(),
thread: None,
running: Arc::new(AtomicBool::new(true)),
events: None,
bind_addr,
}
}
pub fn start(
id: impl Into<String>,
bind_addr: SocketAddr,
events: ActorRef<ControlEvent>,
) -> Self {
let mut sensor = Self::new(id, bind_addr);
sensor.attach(events);
sensor.start();
sensor
}
}
impl Module for OscSensor {
fn id(&self) -> &str {
&self.id
}
fn stop(&mut self) {
self.running.store(false, Ordering::Release);
if let Some(handle) = self.thread.take() {
let _ = handle.join();
}
}
}
impl Sensor for OscSensor {
fn attach(&mut self, events: ActorRef<ControlEvent>) {
self.events = Some(events);
}
fn start(&mut self) {
let events = self
.events
.take()
.expect("OscSensor: attach() must be called before start()");
let bind_addr = self.bind_addr;
let r = self.running.clone();
let mid = self.id.clone();
self.thread = Some(thread::spawn(move || {
let socket = match UdpSocket::bind(bind_addr) {
Ok(s) => s,
Err(e) => {
log::error!("OscSensor '{mid}': failed to bind {bind_addr}: {e}");
return;
}
};
let _ = socket.set_nonblocking(true);
let mut buf = [0u8; 65536];
while r.load(Ordering::Acquire) {
match socket.recv_from(&mut buf) {
Ok((n, _src)) => match osc::decode(&buf[..n]) {
Ok(packet) => {
for event in osc_packet_to_events(&packet) {
events.send(event);
}
}
Err(e) => {
log::warn!("OscSensor '{mid}': decode error: {e}");
}
},
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
thread::sleep(Duration::from_millis(1));
}
Err(e) => {
log::warn!("OscSensor '{mid}': recv error: {e}");
thread::sleep(Duration::from_millis(10));
}
}
}
}));
}
}
impl Drop for OscSensor {
fn drop(&mut self) {
self.stop();
}
}
pub fn spawn_osc_sensor(
id: &str,
bind_addr: SocketAddr,
system: &ActorSystem,
servo_ref: ActorRef<CommandEnum>,
) -> ActorRef<CommandEnum> {
let enabled = Arc::new(AtomicBool::new(true));
let sr = servo_ref.clone();
let oid = id.to_string();
let actor_ref = system.spawn_detached(
&format!("osc_{id}"),
{
let e2 = enabled.clone();
move || {
Box::new(move |msg: CommandEnum| {
if let CommandEnum::Sensor(SensorCommand::SetEnabled { enabled: en, .. }) = msg
{
e2.store(en, Ordering::Release);
}
})
}
},
10,
);
thread::spawn(move || {
let socket = match UdpSocket::bind(bind_addr) {
Ok(s) => s,
Err(e) => {
log::error!("osc sensor '{oid}' failed to bind {bind_addr}: {e}");
return;
}
};
let _ = socket.set_nonblocking(true);
let mut buf = [0u8; 65536];
loop {
thread::sleep(Duration::from_millis(5));
if !enabled.load(Ordering::Acquire) {
continue;
}
match socket.recv_from(&mut buf) {
Ok((n, _src)) => match osc::decode(&buf[..n]) {
Ok(packet) => {
for event in osc_packet_to_events(&packet) {
sr.send(CommandEnum::Control(event));
}
}
Err(e) => {
log::warn!("osc sensor '{oid}' decode error: {e}");
}
},
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
Err(e) => {
log::warn!("osc sensor '{oid}' recv error: {e}");
thread::sleep(Duration::from_millis(50));
}
}
}
});
actor_ref
}
fn osc_packet_to_events(packet: &OscPacket) -> Vec<ControlEvent> {
let mut events = Vec::new();
match packet {
OscPacket::Message(msg) => {
events.push(osc_message_to_event(msg));
}
OscPacket::Bundle(bundle) => {
for inner in &bundle.packets {
events.extend(osc_packet_to_events(inner));
}
}
}
events
}
fn osc_message_to_event(msg: &OscMessage) -> ControlEvent {
let args: Vec<f32> = msg
.args
.iter()
.filter_map(|a| match a {
rill_osc::osc::OscType::Float(f) => Some(*f),
rill_osc::osc::OscType::Int(i) => Some(*i as f32),
_ => None,
})
.collect();
ControlEvent::Osc {
address: msg.addr.clone(),
args,
}
}
pub fn parse_osc(msg: &OscMessage) -> ControlEvent {
osc_message_to_event(msg)
}