GrabBag/AppUtils/AppCommon/Inc/ScanDataCache.h
2026-11-04 10:49:40 +08:00

108 lines
3.5 KiB
C++

#ifndef SCAN_DATA_CACHE_H
#define SCAN_DATA_CACHE_H
#include <atomic>
#include <condition_variable>
#include <cstdint>
#include <deque>
#include <functional>
#include <map>
#include <mutex>
#include <thread>
#include <utility>
#include <vector>
#include "VZNL_Types.h"
/**
* Owns all laser lines produced by a scan. SDK callbacks only copy a line
* and try to enqueue it; they never wait for the consumer or the disk.
*/
class ScanDataCache
{
public:
using ScanId = std::uint64_t;
using Data = std::vector<std::pair<EVzResultDataType, SVzLaserLineData>>;
ScanDataCache();
~ScanDataCache();
ScanDataCache(const ScanDataCache&) = delete;
ScanDataCache& operator=(const ScanDataCache&) = delete;
void BeginScan(ScanId scanId);
bool TryEnqueue(ScanId scanId, int cameraIndex,
EVzResultDataType dataType,
const SVzLaserLineData& line);
void FinishCamera(ScanId scanId, int cameraIndex);
// Moves a fully received camera buffer to the caller. Returns false when
// the scan is not finished yet or the buffer has no data.
bool TryTakeCompleted(ScanId scanId, int cameraIndex, Data& data);
void Restore(ScanId scanId, int cameraIndex, Data&& data);
// Used by debug-file input, which is already a complete scan.
void ReplaceCompleted(ScanId scanId, int cameraIndex, Data&& data);
void SetLineObserver(std::function<void(ScanId, int, EVzResultDataType,
const SVzLaserLineData&)> observer);
void SetReadyNotifier(std::function<void(ScanId, int)> notifier);
bool CopyData(ScanId scanId, int cameraIndex, Data& data) const;
bool HasData(ScanId scanId, int cameraIndex) const;
bool IsCompleted(ScanId scanId, int cameraIndex) const;
bool IsIncomplete(ScanId scanId, int cameraIndex) const;
std::size_t Size(ScanId scanId, int cameraIndex) const;
void CancelScan(ScanId scanId);
void Clear();
void Drain();
void Stop();
private:
struct QueueItem {
ScanId scanId = 0;
int cameraIndex = 0;
EVzResultDataType dataType{};
SVzLaserLineData line{};
std::size_t bytes = 0;
};
struct CameraBuffer {
Data data;
std::size_t pending = 0;
bool finishRequested = false;
bool completed = false;
bool incomplete = false;
};
using CameraKey = std::pair<ScanId, int>;
void WorkerLoop();
static bool CopyLine(EVzResultDataType dataType,
const SVzLaserLineData& source,
SVzLaserLineData& destination);
static void FreeLine(EVzResultDataType dataType, SVzLaserLineData& line);
static void FreeData(Data& data);
mutable std::mutex m_stateMutex;
std::map<CameraKey, CameraBuffer> m_buffers;
std::function<void(ScanId, int, EVzResultDataType,
const SVzLaserLineData&)> m_lineObserver;
std::function<void(ScanId, int)> m_readyNotifier;
// The SDK callback only holds these locks for the queue/state update. It
// never waits for the consumer, the algorithm, or disk I/O.
std::mutex m_queueMutex;
std::condition_variable m_queueCondition;
// The scan buffer is the source of truth and must not drop valid lines at
// an arbitrary byte threshold. The queue is drained by the worker; actual
// memory availability is the only general limit.
std::deque<QueueItem> m_queue;
std::size_t m_queuedBytes = 0;
std::size_t m_inFlight = 0;
std::atomic<bool> m_running{true};
std::thread m_worker;
};
#endif