#pragma once #include "can_types.hpp" #include #include namespace cms { // One lifecycle per instance; only one owner per device index in a process. // start() must run after StopSignals so the receiver inherits its signal mask. // Missing hardware is reported by snapshot(), and does not stop inference. class CanBus { public: explicit CanBus(CanConfig config); ~CanBus(); CanBus(CanBus const&)=delete; CanBus& operator=(CanBus const&)=delete; void start(); void requestStop() noexcept; void join() noexcept; CanSnapshot snapshot() const; // Thread-safe on-demand data-frame send; invalid frames throw, SDK failure // returns false. Never generates periodic or inference-triggered messages. bool send(CanFrame const& frame); // Send one standard 8-byte 0x1F6 frame on CH1. Both nibbles of // data[0] contain 0 for "clear", "watch" and "unavailable", 1 for every // other level: 0x00 or 0x11. Bytes 1-7 are zero. Returns {false, CH1 result}. std::array sendRisk(std::string_view riskLevel); // Nonblocking live-inference path. Attempts the initial binary risk once, // then only encoded 0/1 changes. Latest pending state wins while busy; // returning to the in-flight state cancels a redundant pending transition. // True means accepted (including unchanged), not delivered. No periodic // retransmission or retry of a failed state without a new binary change. bool queueRisk(std::string_view riskLevel); private: struct Impl; std::unique_ptr impl_; }; }