obd-poller: OBD-II mode 01, the single CAN writer; recorder keeps its values
obd-poller asks 0x7DF for the nine obd2 signals, only while the bus is alive, and publishes envelopes stamped with the reply's kernel time. The recorder subscribes to yukart/vehicle/* and writes vehicle.ndjson beside the frames. Tested against a fake ECU on vcan0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
This commit is contained in:
@@ -21,6 +21,9 @@ pub enum Event {
|
||||
CanLink(bool, String),
|
||||
Gnss(f64, String),
|
||||
GnssLink(bool, String),
|
||||
/// An envelope on yukart/vehicle/<signal>: receive time, signal, payload, retained.
|
||||
Vehicle(f64, String, String, bool),
|
||||
BusLink(bool, String),
|
||||
Ctl(String, mpsc::Sender<String>),
|
||||
}
|
||||
|
||||
@@ -124,6 +127,48 @@ pub fn spawn_gnss(addr: String, tx: SyncSender<Event>, stop: Arc<AtomicBool>) {
|
||||
}).expect("spawn gnss thread");
|
||||
}
|
||||
|
||||
/// Envelopes from the broker, for the record only: raw capture never waits on this.
|
||||
pub fn spawn_bus(addr: String, tx: SyncSender<Event>, stop: Arc<AtomicBool>) {
|
||||
let Some((host, port)) = addr.rsplit_once(':').and_then(|(h, p)| Some((h.to_string(), p.parse::<u16>().ok()?))) else {
|
||||
let _ = tx.send(Event::BusLink(false, format!("bad broker address {addr}")));
|
||||
return;
|
||||
};
|
||||
thread::Builder::new().name("bus".into()).spawn(move || {
|
||||
let mut opts = rumqttc::MqttOptions::new("recorder", host, port);
|
||||
opts.set_keep_alive(Duration::from_secs(5));
|
||||
let (client, mut connection) = rumqttc::Client::new(opts, 64);
|
||||
let mut net = rumqttc::NetworkOptions::new();
|
||||
net.set_tcp_nodelay(true);
|
||||
connection.eventloop.set_network_options(net);
|
||||
let mut up = false;
|
||||
for ev in connection.iter() {
|
||||
if stop.load(Ordering::Relaxed) {
|
||||
return;
|
||||
}
|
||||
match ev {
|
||||
Ok(rumqttc::Event::Incoming(rumqttc::Packet::ConnAck(_))) => {
|
||||
up = true;
|
||||
let _ = client.try_subscribe("yukart/vehicle/+", rumqttc::QoS::AtMostOnce);
|
||||
let _ = tx.send(Event::BusLink(true, format!("{addr}: subscribed to yukart/vehicle/+")));
|
||||
}
|
||||
Ok(rumqttc::Event::Incoming(rumqttc::Packet::Publish(p))) => {
|
||||
let signal = p.topic.rsplit('/').next().unwrap_or_default().to_string();
|
||||
let payload = String::from_utf8_lossy(&p.payload).into_owned();
|
||||
// Never block the event loop on the writer; CAN frames have priority in the queue.
|
||||
let _ = tx.try_send(Event::Vehicle(clock::realtime(), signal, payload, p.retain));
|
||||
}
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
if std::mem::take(&mut up) {
|
||||
let _ = tx.send(Event::BusLink(false, format!("{addr}: {e}")));
|
||||
}
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}).expect("spawn bus thread");
|
||||
}
|
||||
|
||||
/// One command per connection, one reply line back.
|
||||
pub fn spawn_ctl(path: String, tx: SyncSender<Event>) -> std::io::Result<()> {
|
||||
let _ = std::fs::remove_file(&path);
|
||||
|
||||
Reference in New Issue
Block a user