use crate::{ProxyHelp, TioOpts};
use std::io::Write;
use std::net::{TcpListener, TcpStream};
use std::thread;
use twinleaf::device::Device;
use twinleaf::tio;
pub fn run_nmea_proxy(tio: TioOpts, tcp_port: u16) -> eyre::Result<()> {
use color_eyre::Help;
use eyre::WrapErr;
let proxy = tio::proxy::Interface::new(&tio.root);
let route = tio.route.clone();
let bind_addr = format!("0.0.0.0:{}", tcp_port);
let listener = TcpListener::bind(&bind_addr)
.wrap_err_with(|| format!("could not bind to {}", bind_addr))
.suggestion(format!("port {} may be in use; try -p <N>", tcp_port))?;
println!("Listening on {}", bind_addr);
for connection in listener.incoming() {
if let Ok(stream) = connection {
let device = proxy
.device_full(route.clone())
.wrap_err_with(|| format!("could not open device at {}", tio.root))
.with_proxy_help()?;
thread::spawn(move || broadcast_to_client(stream, device));
}
}
Ok(())
}
fn format_nmea_sentence(talker_id: &str, sentence_type: &str, fields: &[String]) -> String {
let mut sentence = format!("${}{}", talker_id, sentence_type);
for field in fields {
sentence.push(',');
sentence.push_str(field);
}
let checksum = sentence[1..].bytes().fold(0u8, |acc, b| acc ^ b);
format!("{}*{:02X}\r\n", sentence, checksum)
}
fn broadcast_to_client(mut stream: TcpStream, port: tio::proxy::Port) {
let mut device = Device::new(port);
let peer_addr = stream.peer_addr().unwrap();
println!("Connection from: {}", peer_addr);
loop {
let sample = match device.next() {
Ok(sample) => sample,
Err(_) => {
eprintln!("Failed to parse sample");
break;
}
};
if sample.stream.stream_id != 1 {
continue;
}
let timestamp = sample.timestamp_end();
let hours = (timestamp / 3600.0) as u32;
let minutes = ((timestamp % 3600.0) / 60.0) as u32;
let seconds = timestamp % 60.0;
let time_str = format!("{:02}{:02}{:05.2}", hours, minutes, seconds);
let mut fields = vec![time_str];
for col in &sample.columns {
let value = match col.value {
twinleaf::data::ColumnData::Int(x) => format!("{}", x),
twinleaf::data::ColumnData::UInt(x) => format!("{}", x),
twinleaf::data::ColumnData::Float(x) => format!("{:.2}", x),
twinleaf::data::ColumnData::Unknown => "?".to_string(),
};
fields.push(value);
}
let nmea = format_nmea_sentence("TL", "MAG", &fields);
if let Err(_) = write!(stream, "{}", nmea) {
break;
}
}
println!("Disconnected: {}", peer_addr);
}