#pragma once #include "fgc/ICameraSource.h" #include #include namespace fgc { // Several camera sources behind one, so the scheduler and the pipeline keep // seeing a single camera array: the colour cameras through Vimba and the // thermal one through its own SDK take their pictures at the same waypoints // and intervals without either knowing about the other. // // The first child leads. Its cameras come first in the index order (camera 0 // is its first camera, which the keep rule reads), and its trigger() decides // whether a picture was taken: only once it has delivered are the others // asked, so a retry while the leader waits never yields a second thermal // frame for the same waypoint. The others are best effort - a follower with // no fresh frame costs that picture its thermal image, nothing more. // // Every frame of one trigger carries the leader's timestamp, so the pair is // RGB/.jxl and TIR/.jxl with the same in both CamEvents. // // Indices are global: child k's camera i is offset(k) + i. Calls about one // camera go to the child that has it. // // The offsets come from `counts`, how many cameras each child is configured // with, not from cameraCount(): VimbaCameraSource counts what it has opened, // which is 0 before open(), and a thermal frame numbered from that landed as // camera 0 - the colour camera's label and file (seen on tower 111). Without // counts, each child's cameraCount() at construction is taken. class CompositeCameraSource : public ICameraSource { public: explicit CompositeCameraSource(std::vector> children, std::vector counts = {}); void open() override; void close() override; void start() override; void stop() override; bool trigger() override; bool setProfile(int camera_index, const CameraProfile& profile) override; void setFrameCallback(FrameCallback cb) override; uint64_t frameSeq() override; std::vector framesSince(uint64_t after) override; bool readFeature(int camera_index, const std::string& name, double& value) override; int cameraCount() const override; std::vector deviceInfo() override; std::string offline(int camera_index) override; bool grabFrame(int camera_index, Frame& out, int timeout_ms) override; bool setFeature(int camera_index, const std::string& name, const std::string& value) override; bool featureRange(int camera_index, const std::string& name, double& min, double& max) override; bool restoreProfile(int camera_index) override; bool resetCamera(int camera_index) override; size_t childCount() const { return children_.size(); } ICameraSource& child(size_t k) { return *children_[k]; } private: // The child holding a global index and its index there; nullptr if none. ICameraSource* route(int camera_index, int& local) const; std::vector> children_; std::vector counts_; // cameras each child is configured with std::vector offsets_; // global index of each child's camera 0 FrameCallback callback_; bool in_trigger_ = false; long long shot_ts_ = 0; // the leader's frame time, during a trigger }; } // namespace fgc