#pragma once #include "fgc/Config.h" #include "fgc/ICameraSource.h" #include "fgc/IrbgrabApi.h" #include #include #include namespace fgc { // The thermal camera: an InfraTec TarisIR mini through the IRBGRAB SDK // (IrbgrabApi.h), one camera per source. // // The camera is kept grabbing from open() to close(), whatever the scan does: // a microbolometer that is stopped and restarted starts with zero frames and a // fresh NUC. A worker thread owns every SDK call - connecting, polling the // newest frame of counts, switching the calibration profile, reconnecting // when the camera goes quiet - so nothing here ever blocks the control loop. // trigger() hands over the newest frame if it is fresh enough, and otherwise // says no at once: the thermal picture of a waypoint is best effort, the // colour camera's is not. // // A camera that is missing, unlicensed or unreachable is not a startup error. // The source reports why in offline(), keeps retrying, and the scan goes on // without it. class TarisCameraSource : public ICameraSource { public: struct Options { std::string serial; // the camera's serial number int calib_profile = 0; double frame_rate_hz = 5.0; int max_frame_age_ms = 400; std::string library = "libirbgrab_x86_64-linux.so"; std::string interface; // for diagnostics only; may be empty int stall_ms = 5000; // connected but no frame this long: reconnect int retry_min_ms = 2000; // between connection attempts, doubling... int retry_max_ms = 30000;// ...up to this int calib_timeout_ms = 15000;// a profile switch takes about 3 s }; static Options fromSpec(const CameraSpec& spec); // api: the SDK, or null to load opts.library when the worker starts. // has_net_raw: may this process open a raw socket (the SDK needs one)? // Null: read the effective capabilities from /proc/self/status. explicit TarisCameraSource(Options opts, std::shared_ptr api = nullptr, std::function has_net_raw = nullptr); ~TarisCameraSource() override; void open() override; void close() override; // Only open and close delivery; the camera keeps grabbing either way. void start() override; void stop() override; bool trigger() override; void setFrameCallback(FrameCallback cb) override; int cameraCount() const override { return 1; } std::vector deviceInfo() override; std::string offline(int camera_index) override; bool grabFrame(int camera_index, Frame& out, int timeout_ms) override; // "CalibProfile" (index), "KelvinPerCount", "HousingTemperature" (degC). bool readFeature(int camera_index, const std::string& name, double& value) override; // "CalibProfile": switch, and wait until the camera has (seconds). bool setFeature(int camera_index, const std::string& name, const std::string& value) override; // Disconnect and connect again; waits until it is back or gives up. bool resetCamera(int camera_index) override; // The counts' scale the profiles are documented to have (TarisIR mini): // 0.01 K for profile 0, 0.02 K for profile 1; 0 for any other. static double expectedKelvinPerCount(int calib_profile); // Does /proc/self/status grant CAP_NET_RAW? static bool processHasNetRaw(); private: struct Impl; std::unique_ptr impl_; }; } // namespace fgc