#[macro_use]
pub mod proto;
use crate::proto::control_api::*;
use crate::proto::streaming_api::*;
pub mod goose;
use goose::GooseEvent;
pub mod stream_info;
pub use stream_info::{InputStream, StreamId, StreamInfo, SysStream};
pub mod pulser;
use prost::Message;
use serde::Deserialize;
use serde::de::DeserializeOwned;
use std::collections::{HashMap, HashSet};
use std::io::Error;
use std::io::prelude::*;
use std::os::unix::net::UnixStream;
use std::str::FromStr;
use std::sync::mpsc;
use std::*;
pub trait App: Sized {
type Settings: DeserializeOwned;
type AppEvent: Send + 'static;
fn new(settings: Self::Settings) -> Self;
fn handle_connect(&mut self, _connection: &mut Connection<Self>) -> Result<(), Error> {
Ok(())
}
fn handle_packet(
&mut self,
_connection: &mut Connection<Self>,
_stream_id: StreamId,
_packet: Datagram,
) -> Result<(), Error> {
Ok(())
}
fn handle_new_settings(
&mut self,
_connection: &mut Connection<Self>,
_settings: Self::Settings,
) -> Result<(), Error> {
Err(Error::other("Unhandled new settings"))
}
fn handle_ptp_event(
&mut self,
_connection: &mut Connection<Self>,
_ptp_event: PtpEvent,
) -> Result<(), Error> {
Ok(())
}
fn handle_goose_packet(
&mut self,
_connection: &mut Connection<Self>,
_goose_event: GooseEvent,
) -> Result<(), Error> {
Ok(())
}
fn handle_event(
&mut self,
_connection: &mut Connection<Self>,
_event: Self::AppEvent,
) -> Result<(), Error> {
Ok(())
}
}
enum SDKMessage<AppEvent> {
Datagram(StreamId, Datagram),
SystemEvent(SystemEvent),
PtpEvent(PtpEvent),
AppEvent(AppEvent),
Goose(GooseEvent),
}
pub struct Connection<A: App> {
settings: Settings,
multiplex_sender: mpsc::Sender<SDKMessage<A::AppEvent>>,
multiplex_receiver: mpsc::Receiver<SDKMessage<A::AppEvent>>,
#[allow(dead_code)]
control_stream: UnixStream,
subscribed_streams: HashSet<StreamId>,
}
pub struct Sinusoidal<A: App> {
app: A,
connection: Connection<A>,
}
pub const SS_PRIV_DIR: &str = "/sinusoidal/priv/";
pub const SS_DATA_DIR: &str = "/sinusoidal/data/";
pub const SS_LOG_DIR: &str = "/sinusoidal/log/";
pub fn open_persistent_file(filename: &str) -> Result<fs::File, std::io::Error> {
fs::File::options()
.read(true)
.write(true)
.create(true)
.truncate(false)
.open(SS_PRIV_DIR.to_owned() + filename)
}
pub fn create_public_file(filename: &str) -> Result<fs::File, std::io::Error> {
fs::File::options()
.write(true)
.truncate(true)
.create(true)
.open(SS_DATA_DIR.to_owned() + filename)
}
pub fn create_log_file(filename: &str) -> Result<fs::File, std::io::Error> {
fs::File::options()
.append(true)
.create(true)
.open(SS_LOG_DIR.to_owned() + filename)
}
fn send<S: Write>(stream: &mut S, buf: Vec<u8>) -> Result<(), Error> {
let len = buf.len();
let len = vec![(len >> 8) as u8, len as u8];
stream.write_all(&len)?;
stream.write_all(&buf)?;
Ok(())
}
fn recv<S: Read>(stream: &mut S) -> Result<Vec<u8>, Error> {
let mut len = [0; 2];
stream.read_exact(&mut len)?;
let n = (len[0] as usize) << 8 | (len[1] as usize);
let mut buf = vec![0; n];
stream.read_exact(&mut buf)?;
Ok(buf)
}
pub struct OutStream {
pub name: String,
socket: UnixStream,
}
impl OutStream {
pub fn send(&mut self, packet: Datagram) -> Result<(), Error> {
let mut buf = Vec::new();
StreamPacketDatagram!(packet).encode(&mut buf)?;
send(&mut self.socket, buf)?;
Ok(())
}
}
#[derive(Deserialize, Debug)]
struct Settings {
name: String,
input_streams: HashMap<StreamId, StreamInfo>,
}
impl Settings {
fn new(name: String, streams: Vec<StreamInfo>) -> Result<Self, Error> {
let mut map = HashMap::new();
for stream in streams {
map.insert(
StreamId::from_str(&stream.name()).map_err(Error::other)?,
stream,
);
}
Ok(Settings {
name,
input_streams: map,
})
}
}
impl<A: App> Sinusoidal<A> {
pub fn new() -> Result<Sinusoidal<A>, Error> {
let (multiplex_sender, multiplex_receiver) = mpsc::channel();
let (control_stream, name, input_streams_s, settings_s) =
Connection::<A>::connect(multiplex_sender.clone())?;
let settings = Settings::new(name, serde_json::from_str(&input_streams_s)?)?;
let app_settings = serde_json::from_str(&settings_s)?;
let app = A::new(app_settings);
Ok(Sinusoidal {
app,
connection: Connection {
settings,
multiplex_sender,
multiplex_receiver,
control_stream,
subscribed_streams: HashSet::new(),
},
})
}
pub fn run(mut self) -> Result<(), Error> {
panic::set_hook(Box::new(|panic_info| {
println!("{panic_info}");
process::exit(1);
}));
self.app.handle_connect(&mut self.connection)?;
while let Ok(msg) = self.connection.multiplex_receiver.recv() {
match msg {
SDKMessage::Datagram(sid, d) => self.app.handle_packet(&mut self.connection, sid, d)?,
SDKMessage::SystemEvent(SystemEvent::Shutdown(_)) => return Ok(()),
SDKMessage::SystemEvent(SystemEvent::NewSettings(EventNewSettings { new_settings })) => {
let json = serde_json::from_str(&new_settings)?;
let s = serde_json::from_value(json)?;
match self.app.handle_new_settings(&mut self.connection, s) {
Ok(()) => (),
Err(err) => return Err(err),
}
}
SDKMessage::PtpEvent(ptp_event) => {
self.app.handle_ptp_event(&mut self.connection, ptp_event)?
}
SDKMessage::AppEvent(event) => self.app.handle_event(&mut self.connection, event)?,
SDKMessage::Goose(packet) => self.app.handle_goose_packet(&mut self.connection, packet)?,
}
}
panic!("Multiplex receiver failed")
}
}
impl<A: App> Connection<A> {
fn connect(
multiplex_sender: mpsc::Sender<SDKMessage<A::AppEvent>>,
) -> Result<(UnixStream, String, String, String), Error> {
let mut stream = UnixStream::connect("/sinusoidal/sock/control.sock")?;
let msg = Request {
request: Some(Req::Register(RequestRegister {})),
};
match Self::request(&mut stream, msg)? {
Resp::Register(ResponseRegister {
name,
input_streams,
settings,
}) => {
thread::spawn(move || Self::event_receiver(multiplex_sender));
Ok((stream, name, input_streams, settings))
}
r => {
panic!("Protocol violation: Got {r:?} when attempting to register")
}
}
}
fn event_receiver(sender: mpsc::Sender<SDKMessage<A::AppEvent>>) -> Result<(), Error> {
let mut event_stream = UnixStream::connect("/sinusoidal/sock/event.sock")?;
while let Ok(r) = recv(&mut event_stream) {
if let Ok(FwMsgEvent!(msg)) = WrappedSystemEvent::decode(&r[..]) {
sender.send(SDKMessage::SystemEvent(msg)).unwrap();
}
}
panic!("Event socket closed, terminating");
}
fn request(control_api: &mut UnixStream, req: Request) -> Result<Resp, Error> {
println!("Request: {req:?}");
let mut buf = Vec::new();
req.encode(&mut buf)?;
send(control_api, buf)?;
let buf = recv(control_api)?;
if let FwMsgResponse!(rsp) = Response::decode(&buf[..])? {
println!("Response: {rsp:?}");
Ok(rsp)
} else {
panic!("Bad response!");
}
}
pub fn connect_to_stream(&mut self, input_stream: &InputStream) -> Result<StreamInfo, Error> {
let stream_id = &input_stream.name;
if self.subscribed_streams.contains(stream_id) {
return match self.settings.input_streams.get(stream_id) {
Some(info) => Ok(info.clone()),
None => {
let name = stream_id.to_string();
Err(Error::other(format!("Unknown stream: {name}")))
}
};
}
match self.settings.input_streams.get(stream_id) {
Some(info) => {
let sender = self.multiplex_sender.clone();
let stream_id = stream_id.clone();
thread::spawn(move || Self::stream_receiver(stream_id, sender));
self.subscribed_streams.insert(input_stream.name.clone());
Ok(info.clone())
}
None => {
let name = stream_id.to_string();
Err(Error::other(format!("Unknown stream: {name}")))
}
}
}
fn stream_receiver(stream_id: StreamId, sender: mpsc::Sender<SDKMessage<A::AppEvent>>) -> () {
let sock_name = stream_id.to_string().replace("/", "%2f");
let sock_file = format!("/sinusoidal/sock/{sock_name}.sock");
let mut stream =
UnixStream::connect(sock_file).unwrap_or_else(|_| panic!("Failed to connect to {stream_id}"));
loop {
let r =
recv(&mut stream).unwrap_or_else(|_| panic!("Failed to receive packet on {stream_id}"));
let msg = StreamPacket::decode(&r[..]).expect("StreamPacket decode failure");
match msg {
StreamPacketDatagram!(d) => sender
.send(SDKMessage::Datagram(stream_id.clone(), d))
.unwrap(),
StreamPacketPtp!(ptp) => {
if let StreamId::Sys {
stream: SysStream::Time,
} = stream_id
{
sender.send(SDKMessage::PtpEvent(ptp)).unwrap()
}
}
StreamPacketGoose!(goose) => {
if let StreamId::Goose { go_id: _ } = stream_id {
sender
.send(SDKMessage::Goose(GooseEvent::new(goose)))
.unwrap()
}
}
_ => {} };
}
}
pub fn register_out_stream(&mut self, stream_name: &str) -> Result<OutStream, Error> {
let stream_id = StreamId::App {
app_name: self.settings.name.clone(),
stream_name: stream_name.to_string(),
};
let socket = UnixStream::connect(format!("/sinusoidal/sock/{stream_id}.out.sock"))?;
let stream = OutStream {
name: stream_name.to_string(),
socket,
};
Ok(stream)
}
pub fn get_event_sender(&mut self) -> Box<dyn Fn(A::AppEvent) + Send> {
let tx = self.multiplex_sender.clone();
Box::new(move |e| tx.send(SDKMessage::AppEvent(e)).unwrap())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ClockSynch {
NoSync,
Local,
Global,
LocalSet(u8),
}
impl ClockSynch {
pub fn decode(raw: u32) -> Result<Option<Self>, Error> {
match raw {
0 => Ok(Some(Self::NoSync)),
1 => Ok(Some(Self::Local)),
2 => Ok(Some(Self::Global)),
3 => Ok(None),
5..=254 => Ok(Some(Self::LocalSet(raw as u8))),
_ => Err(Error::new(
std::io::ErrorKind::InvalidData,
format!("invalid clock_synch value {raw}"),
)),
}
}
pub fn encode(value: Option<Self>) -> u32 {
match value {
Some(Self::NoSync) => 0,
Some(Self::Local) => 1,
Some(Self::Global) => 2,
None => 3,
Some(Self::LocalSet(level @ 5..=254)) => u32::from(level),
Some(Self::LocalSet(level)) => panic!("invalid local clock_synch payload {level}"),
}
}
}
fn clock_synch_rank(clock_synch: ClockSynch) -> u8 {
match clock_synch {
ClockSynch::NoSync => 0,
ClockSynch::Local => 1,
ClockSynch::Global => 255,
ClockSynch::LocalSet(level) => level,
}
}
pub fn merge_clock_synch(
left: Option<ClockSynch>,
right: Option<ClockSynch>,
) -> Option<ClockSynch> {
match (left, right) {
(None, other) | (other, None) => other,
(Some(left), Some(right)) => {
if clock_synch_rank(left) <= clock_synch_rank(right) {
Some(left)
} else {
Some(right)
}
}
}
}
pub fn decode_datagram_flags(raw_flags: &[u8], value_count: usize) -> Result<Vec<u16>, Error> {
match raw_flags.len() {
0 => Ok(vec![0; value_count]),
2 if raw_flags[0] == 0 && raw_flags[1] == 0 => Ok(vec![]),
2 if value_count > 0 => {
let shared_flags = u16::from_be_bytes([raw_flags[0], raw_flags[1]]);
Ok(vec![shared_flags; value_count])
}
len if len == value_count * 2 => Ok(
raw_flags
.chunks_exact(2)
.map(|chunk| u16::from_be_bytes([chunk[0], chunk[1]]))
.collect(),
),
len if len % 2 != 0 => Err(Error::new(
std::io::ErrorKind::InvalidData,
format!("invalid flags payload length {len}"),
)),
len => Err(Error::new(
std::io::ErrorKind::InvalidData,
format!("flags payload length {len} does not match value count {value_count}"),
)),
}
}
pub fn encode_datagram_flags(flags: &[u16]) -> Vec<u8> {
match flags.split_first() {
None => vec![0, 0],
Some((&first_flag, rest)) if first_flag == 0 && rest.iter().all(|&flag| flag == 0) => {
Vec::new()
}
Some((&first_flag, rest)) if rest.iter().all(|&flag| flag == first_flag) => {
first_flag.to_be_bytes().to_vec()
}
_ => flags.iter().flat_map(|flag| flag.to_be_bytes()).collect(),
}
}
pub fn merge_datagram_flags(left: &[u16], right: &[u16]) -> Result<Vec<u16>, Error> {
match (left.is_empty(), right.is_empty()) {
(true, true) => Ok(Vec::new()),
(true, false) => Ok(right.to_vec()),
(false, true) => Ok(left.to_vec()),
(false, false) if left.len() == right.len() => Ok(
left
.iter()
.zip(right.iter())
.map(|(left, right)| left | right)
.collect(),
),
(false, false) => Err(Error::new(
std::io::ErrorKind::InvalidInput,
format!(
"cannot merge flag vectors of different lengths ({} vs {})",
left.len(),
right.len()
),
)),
}
}
pub fn collapse_datagram_flags(flags: &[u16]) -> u16 {
flags.iter().copied().fold(0, |acc, flag| acc | flag)
}
impl Datagram {
pub fn decoded_clock_synch(&self) -> Result<Option<ClockSynch>, Error> {
ClockSynch::decode(self.clock_synch)
}
pub fn set_clock_synch(&mut self, clock_synch: Option<ClockSynch>) {
self.clock_synch = ClockSynch::encode(clock_synch);
}
pub fn decoded_flags(&self) -> Result<Vec<u16>, Error> {
decode_datagram_flags(&self.flags, self.values.len())
}
pub fn set_flags(&mut self, flags: &[u16]) -> Result<(), Error> {
if !flags.is_empty() && flags.len() != self.values.len() {
return Err(Error::new(
std::io::ErrorKind::InvalidInput,
format!(
"flags length {} does not match value count {}",
flags.len(),
self.values.len()
),
));
}
self.flags = encode_datagram_flags(flags);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use quickcheck::TestResult;
use std::io::Cursor;
#[quickcheck_macros::quickcheck]
fn send_recv_roundtrip(payload: Vec<u8>) -> TestResult {
if payload.len() > u16::MAX as usize {
return TestResult::discard();
}
let mut stream = Cursor::new(Vec::new());
send(&mut stream, payload.clone()).unwrap();
stream.set_position(0);
let received = recv(&mut stream).unwrap();
TestResult::from_bool(payload == received)
}
#[test]
fn clock_synch_roundtrips() {
assert_eq!(ClockSynch::decode(ClockSynch::encode(None)).unwrap(), None);
assert_eq!(
ClockSynch::decode(ClockSynch::encode(Some(ClockSynch::NoSync))).unwrap(),
Some(ClockSynch::NoSync)
);
assert_eq!(
ClockSynch::decode(ClockSynch::encode(Some(ClockSynch::Local))).unwrap(),
Some(ClockSynch::Local)
);
assert_eq!(
ClockSynch::decode(ClockSynch::encode(Some(ClockSynch::Global))).unwrap(),
Some(ClockSynch::Global)
);
assert_eq!(
ClockSynch::decode(ClockSynch::encode(Some(ClockSynch::LocalSet(5)))).unwrap(),
Some(ClockSynch::LocalSet(5))
);
assert_eq!(
ClockSynch::decode(ClockSynch::encode(Some(ClockSynch::LocalSet(254)))).unwrap(),
Some(ClockSynch::LocalSet(254))
);
}
#[test]
fn clock_synch_rejects_invalid_values() {
assert!(ClockSynch::decode(4).is_err());
assert!(ClockSynch::decode(255).is_err());
}
#[test]
fn decode_datagram_flags_handles_compact_encodings() {
assert_eq!(decode_datagram_flags(&[], 3).unwrap(), vec![0, 0, 0]);
assert_eq!(
decode_datagram_flags(&0x1234_u16.to_be_bytes(), 3).unwrap(),
vec![0x1234, 0x1234, 0x1234]
);
assert_eq!(
decode_datagram_flags(&[0x00, 0x01, 0x00, 0x02, 0x00, 0x03], 3).unwrap(),
vec![1, 2, 3]
);
}
#[test]
fn decode_datagram_flags_rejects_invalid_lengths() {
assert!(decode_datagram_flags(&[0x00], 1).is_err());
assert!(decode_datagram_flags(&[0x00, 0x01, 0x00, 0x02], 3).is_err());
assert!(decode_datagram_flags(&[0x00, 0x01], 0).is_err());
}
#[test]
fn encode_datagram_flags_compacts_zero_and_shared_values() {
assert_eq!(encode_datagram_flags(&[]), vec![0, 0]);
assert_eq!(encode_datagram_flags(&[0, 0, 0]), Vec::<u8>::new());
assert_eq!(encode_datagram_flags(&[0x1234, 0x1234]), vec![0x12, 0x34]);
assert_eq!(
encode_datagram_flags(&[1, 2, 3]),
vec![0x00, 0x01, 0x00, 0x02, 0x00, 0x03]
);
}
#[test]
fn datagram_helpers_roundtrip() {
let mut datagram = Datagram {
timestamp: 0,
sample_count: 3,
values: vec![10, 20, 30],
clock_synch: 3,
flags: Vec::new(),
gm_identity: vec![1, 2, 3],
};
datagram.set_clock_synch(Some(ClockSynch::LocalSet(9)));
datagram.set_flags(&[0x00ff, 0x00ff, 0x00ff]).unwrap();
assert_eq!(
datagram.decoded_clock_synch().unwrap(),
Some(ClockSynch::LocalSet(9))
);
assert_eq!(datagram.decoded_flags().unwrap(), vec![0x00ff; 3]);
assert_eq!(datagram.flags, vec![0x00, 0xff]);
}
#[test]
fn datagram_set_flags_requires_one_entry_per_value() {
let mut datagram = Datagram {
timestamp: 0,
sample_count: 2,
values: vec![10, 20],
clock_synch: 3,
flags: Vec::new(),
gm_identity: Vec::new(),
};
assert!(datagram.set_flags(&[0x0011]).is_err());
}
#[test]
fn datagram_set_flags_allows_no_flags_sentinel() {
let mut datagram = Datagram {
timestamp: 0,
sample_count: 2,
values: vec![10, 20],
clock_synch: 3,
flags: Vec::new(),
gm_identity: Vec::new(),
};
datagram.set_flags(&[]).unwrap();
assert_eq!(datagram.flags, vec![0, 0]);
assert_eq!(datagram.decoded_flags().unwrap(), Vec::<u16>::new());
}
#[test]
fn merge_datagram_flags_preserves_empty_as_identity() {
assert_eq!(merge_datagram_flags(&[], &[]).unwrap(), Vec::<u16>::new());
assert_eq!(merge_datagram_flags(&[], &[1, 2]).unwrap(), vec![1, 2]);
assert_eq!(merge_datagram_flags(&[1, 2], &[]).unwrap(), vec![1, 2]);
assert_eq!(merge_datagram_flags(&[1, 2], &[4, 8]).unwrap(), vec![5, 10]);
assert!(merge_datagram_flags(&[1], &[1, 2]).is_err());
}
#[test]
fn merge_clock_synch_prefers_more_constrained_value() {
assert_eq!(
merge_clock_synch(Some(ClockSynch::Global), Some(ClockSynch::Local)),
Some(ClockSynch::Local)
);
assert_eq!(
merge_clock_synch(Some(ClockSynch::LocalSet(5)), Some(ClockSynch::LocalSet(9))),
Some(ClockSynch::LocalSet(5))
);
assert_eq!(
merge_clock_synch(None, Some(ClockSynch::Global)),
Some(ClockSynch::Global)
);
}
}