fwt_software/include/fgc/CompositeCameraSource.h

80 lines
3.7 KiB
C++

#pragma once
#include "fgc/ICameraSource.h"
#include <memory>
#include <vector>
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 keeps its own time, when it reached the host: the thermal frame
// is the newest the camera had, so its time is up to max_frame_age_ms off the
// colour one, and that is what the file name and the CamEvent say.
//
// A follower that is briefly without a fresh frame (frameReady() false: the
// thermal camera's NUC) holds the picture back for up to `follower_wait_ms`,
// so the pair stays whole; past that the leader goes on alone.
//
// 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<std::unique_ptr<ICameraSource>> children,
std::vector<int> counts = {}, int follower_wait_ms = 1000);
void open() override;
void close() override;
void start() override;
void stop() override;
bool trigger() override;
void moving() override;
bool frameReady() override;
bool setProfile(int camera_index, const CameraProfile& profile) override;
void setFrameCallback(FrameCallback cb) override;
uint64_t frameSeq() override;
std::vector<FrameStats> framesSince(uint64_t after) override;
bool readFeature(int camera_index, const std::string& name, double& value) override;
int cameraCount() const override;
std::vector<CameraDeviceInfo> 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<std::unique_ptr<ICameraSource>> children_;
std::vector<int> counts_; // cameras each child is configured with
std::vector<int> offsets_; // global index of each child's camera 0
FrameCallback callback_;
int follower_wait_ms_;
long long waiting_since_ = 0; // steady ms a picture began waiting for a follower
};
} // namespace fgc