Port complete CAN-aware risk pipeline to C++
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace cms {
|
||||
// FIFO with bounded ownership. Normal close drains; cancellation may discard.
|
||||
template<class T> class BoundedQueue {
|
||||
public:
|
||||
explicit BoundedQueue(std::size_t capacity):capacity_(capacity) {
|
||||
if(!capacity)throw std::invalid_argument("Queue capacity must be positive");
|
||||
}
|
||||
bool push(T item) {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
changed_.wait(lock,[&]{return closed_||items_.size()<capacity_;});
|
||||
if(closed_)return false;
|
||||
items_.push_back(std::move(item));changed_.notify_all();return true;
|
||||
}
|
||||
bool pushLatest(T item) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if(closed_)return false;
|
||||
if(items_.size()>=capacity_)items_.pop_front();
|
||||
items_.push_back(std::move(item));changed_.notify_all();return true;
|
||||
}
|
||||
std::optional<T> pop() {
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
changed_.wait(lock,[&]{return closed_||!items_.empty();});
|
||||
if(items_.empty())return {};
|
||||
T item=std::move(items_.front());items_.pop_front();changed_.notify_all();return item;
|
||||
}
|
||||
void close(bool discard=false) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
closed_=true;if(discard)items_.clear();changed_.notify_all();
|
||||
}
|
||||
private:
|
||||
const std::size_t capacity_;
|
||||
std::mutex mutex_;std::condition_variable changed_;
|
||||
std::deque<T> items_;bool closed_{};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include "image.hpp"
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace cms {
|
||||
struct CameraConfig {
|
||||
std::string device{"/dev/video5"};
|
||||
std::string video;
|
||||
int width{1920},height{1536},fps{30},buffers{4},timeout_ms{2000};
|
||||
};
|
||||
struct CapturedFrame {
|
||||
CaptureMetadata metadata;
|
||||
std::shared_ptr<const BgrImage> image;
|
||||
};
|
||||
// The capture thread exclusively owns read(); requestStop() is thread safe.
|
||||
class Camera {
|
||||
public:
|
||||
explicit Camera(CameraConfig config);
|
||||
~Camera();
|
||||
Camera(Camera const&)=delete;
|
||||
Camera& operator=(Camera const&)=delete;
|
||||
std::optional<CapturedFrame> read();
|
||||
void requestStop() noexcept;
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
};
|
||||
std::uint64_t monotonicNs();
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#include "camera.hpp"
|
||||
#include "frame_result.hpp"
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
|
||||
namespace cms {
|
||||
struct PipelineConfig {
|
||||
CameraConfig camera;
|
||||
std::filesystem::path engines,door_mask,door_zone_config;
|
||||
bool can_enabled{true};
|
||||
std::string can_side{"left"},active_door{"can"};
|
||||
double can_timeout_ms{1000};
|
||||
std::uint64_t max_frames{};
|
||||
std::filesystem::path results_path,pose_records_path;
|
||||
double detector_confidence{0.30};
|
||||
int max_people{3};
|
||||
double warning_relative{0.30},release_relative{0.45};
|
||||
std::size_t queue_capacity{2};
|
||||
bool door_mask_calibration_verified{false};
|
||||
};
|
||||
struct PipelineStats {
|
||||
std::uint64_t captured{},published{},driver_sequence_gaps{},discarded_error_frames{};
|
||||
double mean_capture_to_result_ms{};
|
||||
};
|
||||
// One lifecycle per instance. start() warms engines before starting capture.
|
||||
// Runs continuously until requestStop() or a worker failure.
|
||||
// latest() reads the immutable result variable without removing or acknowledging it.
|
||||
// No result queue, consumer registration or frame-count termination.
|
||||
// Stop is safe from any thread;
|
||||
// destruction closes queues, interrupts capture and joins every worker.
|
||||
class CameraPipeline {
|
||||
public:
|
||||
explicit CameraPipeline(PipelineConfig config);
|
||||
~CameraPipeline();
|
||||
CameraPipeline(CameraPipeline const&)=delete;
|
||||
CameraPipeline& operator=(CameraPipeline const&)=delete;
|
||||
void start();
|
||||
FrameResultPtr latest() const;
|
||||
bool running() const noexcept;
|
||||
void requestStop() noexcept;
|
||||
void join();
|
||||
PipelineStats stats() const;
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// can_comm.h - USBCANFD CAN/CANFD 收发封装
|
||||
//
|
||||
// 整合自 vision-anti-pinch_bak/main.cpp 的 CAN 模块, 提供:
|
||||
// can_start() - 打开设备, 初始化并启动双通道接收线程
|
||||
// can_stop() - 停止接收并关闭设备
|
||||
// can_send() - 发送一帧 CAN (8 字节)
|
||||
// canfd_send() - 发送一帧 CANFD (64 字节)
|
||||
//
|
||||
// 依赖 zcan.h (include/) 和 libusbcanfd.so + libusb-1.0.so (lib/)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace CanComm {
|
||||
|
||||
// 初始化并启动 CAN 收发(双通道, 仲裁域 500kbps / 数据域 2000kbps)。
|
||||
// 返回 true 成功; false 失败(主流程可继续仅取图)。
|
||||
bool can_start();
|
||||
|
||||
// 停止 CAN 接收线程并关闭设备。
|
||||
void can_stop();
|
||||
|
||||
// 注册报文解析回调。收到每帧 CAN/CANFD 数据帧时调用。
|
||||
// time: 时间戳(us)
|
||||
// msg_id: CAN-ID
|
||||
// data: 数据指针(回调内有效, 不要保存)
|
||||
// len: 数据长度
|
||||
// 传 nullptr 取消注册。
|
||||
using MsgCallback = void (*)(int64_t time, uint32_t msg_id, const uint8_t *data, uint32_t len);
|
||||
void set_msg_callback(MsgCallback cb);
|
||||
|
||||
// 发送一帧 CAN2.0 数据帧(8 字节)。
|
||||
// chn_idx: 通道号 0/1
|
||||
// id: CAN-ID
|
||||
// data: 8 字节数据(不足补 0, 超出截断)
|
||||
// 返回实际发送帧数(0 失败)。
|
||||
uint32_t can_send(int chn_idx, uint32_t id, const uint8_t data[8]);
|
||||
|
||||
// 发送一帧 CANFD 数据帧(64 字节, 扩展帧+加速)。
|
||||
// chn_idx: 通道号 0/1
|
||||
// id: CAN-ID
|
||||
// data: 64 字节数据(不足补 0, 超出截断)
|
||||
// 返回实际发送帧数(0 失败)。
|
||||
uint32_t canfd_send(int chn_idx, uint32_t id, const uint8_t data[64]);
|
||||
|
||||
// 是否已打开并运行
|
||||
bool is_open();
|
||||
|
||||
// 累计接收帧数(线程安全)
|
||||
int receive_count();
|
||||
|
||||
} // namespace CanComm
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include <string>
|
||||
namespace cms {
|
||||
struct CanSnapshot {
|
||||
std::array<int,4> positions{{-1,-1,-1,-1}}; // LF, RF, LR, RR
|
||||
double age_ms{-1};
|
||||
bool valid{false};
|
||||
std::string source{"hardware"},side{"left"};
|
||||
};
|
||||
class CanInput {
|
||||
public:
|
||||
explicit CanInput(bool enabled);
|
||||
~CanInput();
|
||||
CanInput(CanInput const&)=delete;
|
||||
CanInput& operator=(CanInput const&)=delete;
|
||||
CanSnapshot snapshot(std::string const& side,double timeoutMs) const;
|
||||
private:
|
||||
bool opened_{};
|
||||
};
|
||||
std::string activeDoor(CanSnapshot const& state);
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
#include <NvInfer.h>
|
||||
#include <NvInferVersion.h>
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace fangjia {
|
||||
|
||||
inline void checkCuda(cudaError_t status, char const* expression, char const* file, int line) {
|
||||
if (status != cudaSuccess) {
|
||||
throw std::runtime_error(
|
||||
std::string("CUDA failure: ") + expression + " at " + file + ":" +
|
||||
std::to_string(line) + ": " + cudaGetErrorString(status));
|
||||
}
|
||||
}
|
||||
|
||||
#define CUDA_CHECK(expr) ::fangjia::checkCuda((expr), #expr, __FILE__, __LINE__)
|
||||
|
||||
class TrtLogger final : public nvinfer1::ILogger {
|
||||
public:
|
||||
explicit TrtLogger(Severity minimum = Severity::kWARNING) : minimum_(minimum) {}
|
||||
|
||||
void log(Severity severity, char const* message) noexcept override {
|
||||
if (severity <= minimum_) {
|
||||
std::cerr << "[TensorRT] " << message << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Severity minimum_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct TrtDeleter {
|
||||
void operator()(T* object) const noexcept { delete object; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using TrtUniquePtr = std::unique_ptr<T, TrtDeleter<T>>;
|
||||
|
||||
inline std::vector<std::uint8_t> readBinary(std::filesystem::path const& path) {
|
||||
std::ifstream stream(path, std::ios::binary | std::ios::ate);
|
||||
if (!stream) {
|
||||
throw std::runtime_error("Cannot open " + path.string());
|
||||
}
|
||||
auto const size = stream.tellg();
|
||||
if(size<=0)throw std::runtime_error("Empty or unreadable engine: "+path.string());
|
||||
stream.seekg(0);
|
||||
std::vector<std::uint8_t> bytes(static_cast<std::size_t>(size));
|
||||
if(!stream.read(reinterpret_cast<char*>(bytes.data()), size))throw std::runtime_error("Truncated engine: "+path.string());
|
||||
return bytes;
|
||||
}
|
||||
|
||||
inline void writeBinary(std::filesystem::path const& path, void const* data, std::size_t size) {
|
||||
std::filesystem::create_directories(path.parent_path());
|
||||
std::ofstream stream(path, std::ios::binary);
|
||||
if (!stream) {
|
||||
throw std::runtime_error("Cannot write " + path.string());
|
||||
}
|
||||
stream.write(static_cast<char const*>(data), static_cast<std::streamsize>(size));
|
||||
}
|
||||
|
||||
inline std::size_t volume(nvinfer1::Dims const& dims) {
|
||||
std::size_t value = 1;
|
||||
for (int index = 0; index < dims.nbDims; ++index) {
|
||||
if (dims.d[index] < 0) {
|
||||
throw std::runtime_error("Unresolved dynamic tensor dimension");
|
||||
}
|
||||
value *= static_cast<std::size_t>(dims.d[index]);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
inline std::size_t dataTypeSize(nvinfer1::DataType type) {
|
||||
switch (type) {
|
||||
case nvinfer1::DataType::kFLOAT: return 4;
|
||||
case nvinfer1::DataType::kHALF: return 2;
|
||||
case nvinfer1::DataType::kINT8: return 1;
|
||||
case nvinfer1::DataType::kINT32: return 4;
|
||||
#if NV_TENSORRT_MAJOR >= 10
|
||||
case nvinfer1::DataType::kINT64: return 8;
|
||||
#endif
|
||||
case nvinfer1::DataType::kBOOL: return 1;
|
||||
default: throw std::runtime_error("Unsupported TensorRT data type");
|
||||
}
|
||||
}
|
||||
|
||||
class CudaEventTimer {
|
||||
public:
|
||||
CudaEventTimer() {
|
||||
CUDA_CHECK(cudaEventCreate(&start_));
|
||||
CUDA_CHECK(cudaEventCreate(&end_));
|
||||
}
|
||||
~CudaEventTimer() {
|
||||
cudaEventDestroy(start_);
|
||||
cudaEventDestroy(end_);
|
||||
}
|
||||
void start(cudaStream_t stream) { CUDA_CHECK(cudaEventRecord(start_, stream)); }
|
||||
float stop(cudaStream_t stream) {
|
||||
CUDA_CHECK(cudaEventRecord(end_, stream));
|
||||
CUDA_CHECK(cudaEventSynchronize(end_));
|
||||
float milliseconds = 0.0F;
|
||||
CUDA_CHECK(cudaEventElapsedTime(&milliseconds, start_, end_));
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
private:
|
||||
cudaEvent_t start_{};
|
||||
cudaEvent_t end_{};
|
||||
};
|
||||
|
||||
} // namespace fangjia
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
#include "camera_pipeline.hpp"
|
||||
namespace cms {
|
||||
PipelineConfig loadConfig(std::filesystem::path const& path);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include "pose_engine.hpp"
|
||||
#include "gpu_preprocess.hpp"
|
||||
namespace cms {
|
||||
class Detector {
|
||||
public:
|
||||
Detector(std::filesystem::path const& path, fangjia::TrtLogger& logger, float confidence, int maxPeople);
|
||||
std::vector<fangjia::Box> infer(cv::Mat const& frame,cudaStream_t stream,int filterHeight=0);
|
||||
private:
|
||||
fangjia::TrtEngine engine_;
|
||||
float confidence_;
|
||||
int maxPeople_, width_, height_;
|
||||
std::string input_,output_;
|
||||
std::unique_ptr<YoloPreprocess> preprocess_;
|
||||
std::vector<float> outputHost_;
|
||||
};
|
||||
cv::Mat maskOtherPeople(cv::Mat const& frame,std::vector<fangjia::Box> const& boxes,size_t target);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <opencv2/core.hpp>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <set>
|
||||
using Json = nlohmann::ordered_json;
|
||||
namespace cms {
|
||||
struct DoorOptions {
|
||||
std::filesystem::path constant, directory;
|
||||
std::string format{"auto"};
|
||||
std::set<std::string> labels{"door", "车门"};
|
||||
int offset{};
|
||||
bool require{};
|
||||
double warning{0.30}, release{0.45};
|
||||
};
|
||||
class DoorRisk {
|
||||
public:
|
||||
DoorRisk(DoorOptions options, cv::Size size);
|
||||
void initialize() { load(0); }
|
||||
Json evaluate(int frame, Json& record);
|
||||
cv::Mat const& mask() const { return mask_; }
|
||||
private:
|
||||
void load(int frame);
|
||||
cv::Mat rasterize(std::filesystem::path const& path) const;
|
||||
DoorOptions options_;
|
||||
cv::Size size_;
|
||||
std::string format_, status_;
|
||||
std::filesystem::path loadedPath_;
|
||||
std::map<int, std::filesystem::path> paths_;
|
||||
cv::Mat mask_, distance_;
|
||||
bool cached_{};
|
||||
};
|
||||
double rounded(double value, int digits);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "door_risk.hpp"
|
||||
#include <deque>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace cms {
|
||||
class DoorZoneAssigner {
|
||||
public:
|
||||
explicit DoorZoneAssigner(std::filesystem::path const& configuration);
|
||||
Json update(int frame, Json const& record, cv::Size size);
|
||||
private:
|
||||
std::string classify(double probability) const;
|
||||
Json features_;
|
||||
double frontMin_{},rearMax_{},minimumHeight_{};
|
||||
int minimumPoseFeatures_{},historyFrames_{},staleFrames_{};
|
||||
std::unordered_map<int,std::deque<double>> history_;
|
||||
std::unordered_map<int,int> lastSeen_;
|
||||
};
|
||||
struct DoorDecisions { Json doors; Json selected; };
|
||||
DoorDecisions buildDoorRisks(Json const& raw, Json const& assignments,
|
||||
std::string const& activeDoor, bool canValid);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
// Public downstream contract. Owns its data; no GPU, JSON or network handles.
|
||||
#include "image.hpp"
|
||||
#include "can_state.hpp"
|
||||
#include <map>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace cms {
|
||||
struct DoorMaskResult {
|
||||
std::string status;
|
||||
int annotation_index{};
|
||||
std::optional<std::string> path;
|
||||
std::string format;
|
||||
int area_px{};
|
||||
};
|
||||
struct RegionRisk {
|
||||
std::string status;
|
||||
std::optional<double> risk_score;
|
||||
std::string risk_level;
|
||||
std::string decision;
|
||||
std::string source;
|
||||
std::string door_zone;
|
||||
int track_id{};
|
||||
std::string side;
|
||||
std::optional<std::string> state;
|
||||
// Unavailable evidence has no geometric measurements, matching the reference.
|
||||
std::optional<int> region_area_px, door_overlap_px;
|
||||
std::optional<double> region_overlap_ratio, min_distance_px;
|
||||
std::optional<double> region_scale_px, distance_relative;
|
||||
};
|
||||
struct RiskResult {
|
||||
std::string risk_type;
|
||||
std::optional<std::string> door_id;
|
||||
std::string status;
|
||||
std::optional<double> risk_score;
|
||||
std::string risk_level;
|
||||
std::string decision;
|
||||
std::optional<std::string> source;
|
||||
std::optional<int> track_id;
|
||||
std::optional<std::string> side;
|
||||
DoorMaskResult door_mask;
|
||||
double warning_distance_relative{}, release_distance_relative{};
|
||||
std::vector<RegionRisk> regions;
|
||||
};
|
||||
struct PosePoint { double x{},y{},score{}; };
|
||||
struct HandState {
|
||||
std::string state, source;
|
||||
bool limb_consensus{}, distance_eligible{}, alarm_eligible{};
|
||||
};
|
||||
struct PersonResult {
|
||||
int track_id{};
|
||||
double confidence{};
|
||||
std::array<double,4> box{};
|
||||
std::array<PosePoint,133> selected{}, smoothed{}, temporal{};
|
||||
std::array<HandState,2> hands; // left, right
|
||||
};
|
||||
struct HandRegion {
|
||||
int person_id{},track_id{};
|
||||
std::string side;
|
||||
std::array<PosePoint,21> keypoints{};
|
||||
};
|
||||
struct StageTiming {
|
||||
double detector_ms{}, pose_ms{}, fusion_risk_ms{}, capture_to_result_ms{};
|
||||
};
|
||||
struct DoorAssignment {
|
||||
int track_id{}, person_index{}, pose_feature_count{}, history_frames{};
|
||||
std::string door_zone, raw_door_zone, evidence_source;
|
||||
double front_probability{}, smoothed_front_probability{}, confidence{};
|
||||
std::map<std::string,double> features, feature_frontness;
|
||||
};
|
||||
struct FrameResult {
|
||||
int frame_index{};
|
||||
std::vector<int> track_ids;
|
||||
std::vector<std::array<double,4>> boxes;
|
||||
RiskResult risk;
|
||||
RiskResult ungated_risk;
|
||||
std::map<std::string,RiskResult> door_risks;
|
||||
std::vector<DoorAssignment> door_assignments;
|
||||
std::string active_door;
|
||||
CanSnapshot can;
|
||||
CaptureMetadata capture;
|
||||
StageTiming timing;
|
||||
std::vector<PersonResult> persons;
|
||||
std::vector<HandRegion> hand_regions;
|
||||
// Both images belong to this result. The constant mask is shared across frames.
|
||||
std::shared_ptr<const BgrImage> image;
|
||||
std::shared_ptr<const BinaryMask> door_mask_pixels;
|
||||
bool door_mask_calibration_verified{};
|
||||
};
|
||||
using FrameResultPtr = std::shared_ptr<const FrameResult>;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "common.hpp"
|
||||
#include <npp.h>
|
||||
#include <opencv2/core.hpp>
|
||||
namespace cms {
|
||||
inline void nppCheck(NppStatus status) {if(status<0)throw std::runtime_error("NPP error "+std::to_string(int(status)));}
|
||||
inline NppStreamContext nppContext(cudaStream_t stream) {
|
||||
NppStreamContext c{};c.hStream=stream;CUDA_CHECK(cudaGetDevice(&c.nCudaDeviceId));cudaDeviceProp p{};CUDA_CHECK(cudaGetDeviceProperties(&p,c.nCudaDeviceId));
|
||||
c.nMultiProcessorCount=p.multiProcessorCount;c.nMaxThreadsPerMultiProcessor=p.maxThreadsPerMultiProcessor;c.nMaxThreadsPerBlock=p.maxThreadsPerBlock;c.nSharedMemPerBlock=p.sharedMemPerBlock;c.nCudaDevAttrComputeCapabilityMajor=p.major;c.nCudaDevAttrComputeCapabilityMinor=p.minor;CUDA_CHECK(cudaStreamGetFlags(stream,&c.nStreamFlags));return c;
|
||||
}
|
||||
class YoloPreprocess {
|
||||
public:
|
||||
YoloPreprocess(int width,int height):width_(width),height_(height) {CUDA_CHECK(cudaMalloc(&letter_,size_t(width)*height*3));}
|
||||
~YoloPreprocess(){cudaFree(source_);cudaFree(letter_);}
|
||||
void enqueue(cv::Mat const& frame,float* destination,cudaStream_t stream);
|
||||
private:
|
||||
int width_,height_;size_t capacity_{};unsigned char *source_{},*letter_{};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace cms {
|
||||
// Immutable when published. Stride is explicit; no borrowed camera/GPU memory.
|
||||
struct BgrImage {
|
||||
int width{}, height{}, stride{};
|
||||
std::vector<std::uint8_t> pixels;
|
||||
};
|
||||
struct BinaryMask {
|
||||
int width{}, height{}, stride{};
|
||||
std::vector<std::uint8_t> pixels;
|
||||
};
|
||||
struct CaptureMetadata {
|
||||
std::uint64_t frame_index{};
|
||||
std::uint32_t driver_sequence{};
|
||||
std::uint64_t driver_timestamp_ns{};
|
||||
std::uint64_t received_monotonic_ns{};
|
||||
std::uint64_t capture_monotonic_ns{};
|
||||
bool driver_timestamp_monotonic{};
|
||||
std::uint32_t skipped_driver_frames{};
|
||||
std::uint32_t discarded_error_frames{};
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "frame_result.hpp"
|
||||
#include <atomic>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace cms {
|
||||
// One immutable result variable. Readers neither remove nor acknowledge results.
|
||||
class LatestResult {
|
||||
public:
|
||||
void publish(FrameResultPtr result) {
|
||||
if(!result)throw std::invalid_argument("Cannot publish a null result");
|
||||
std::atomic_store_explicit(&latest_,std::move(result),std::memory_order_release);
|
||||
}
|
||||
FrameResultPtr load() const {
|
||||
return std::atomic_load_explicit(&latest_,std::memory_order_acquire);
|
||||
}
|
||||
private:
|
||||
FrameResultPtr latest_;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace fangjia {
|
||||
|
||||
struct Box {
|
||||
float x1{};
|
||||
float y1{};
|
||||
float x2{};
|
||||
float y2{};
|
||||
float score{};
|
||||
int trackId{};
|
||||
};
|
||||
|
||||
struct Keypoint {
|
||||
float x{};
|
||||
float y{};
|
||||
float score{};
|
||||
};
|
||||
|
||||
struct PersonPose {
|
||||
Box box;
|
||||
std::array<Keypoint, 133> keypoints{};
|
||||
};
|
||||
|
||||
enum class LimbState {
|
||||
Observed,
|
||||
Provisional,
|
||||
Recovered,
|
||||
Reassigned,
|
||||
Interpolated,
|
||||
PredictedShort,
|
||||
ReachableMedium,
|
||||
ReachableLong,
|
||||
};
|
||||
|
||||
inline char const* limbStateName(LimbState state) {
|
||||
switch (state) {
|
||||
case LimbState::Observed: return "observed";
|
||||
case LimbState::Provisional: return "provisional";
|
||||
case LimbState::Recovered: return "recovered";
|
||||
case LimbState::Reassigned: return "reassigned";
|
||||
case LimbState::Interpolated: return "interpolated";
|
||||
case LimbState::PredictedShort: return "predicted_short";
|
||||
case LimbState::ReachableMedium: return "reachable_medium";
|
||||
case LimbState::ReachableLong: return "reachable_long";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
struct LimbObservation {
|
||||
int trackId{};
|
||||
bool left{};
|
||||
LimbState state{LimbState::ReachableLong};
|
||||
Keypoint elbow{};
|
||||
Keypoint wrist{};
|
||||
std::array<Keypoint, 21> hand{};
|
||||
Box personBox{};
|
||||
bool handValid{};
|
||||
bool alarmEligible{};
|
||||
float handQuality{};
|
||||
};
|
||||
|
||||
// Exact pre-V2 hand evidence consumed by the Python risk stage. These points
|
||||
// come from raw/isolated fusion before temporal reconstruction.
|
||||
struct GlobalHand {
|
||||
int personId{};
|
||||
int trackId{};
|
||||
bool left{};
|
||||
std::string source;
|
||||
std::string state;
|
||||
bool distanceEligible{};
|
||||
bool alarmEligible{};
|
||||
float score{};
|
||||
float spanPx{};
|
||||
std::array<Keypoint, 21> keypoints{};
|
||||
std::vector<std::pair<int, bool>> aliases;
|
||||
};
|
||||
|
||||
struct FusionMeta {
|
||||
bool limbConsensus{}, handValid{}, distanceEligible{}, alarmEligible{};
|
||||
double handScore{};
|
||||
std::string handSource, handState;
|
||||
};
|
||||
|
||||
struct PoseFrameResult {
|
||||
std::vector<std::array<FusionMeta, 2>> metadata;
|
||||
std::vector<PersonPose> selectedPeople;
|
||||
std::vector<PersonPose> smoothedPeople;
|
||||
std::vector<PersonPose> v2People;
|
||||
std::vector<LimbObservation> limbs;
|
||||
std::vector<GlobalHand> globalHands;
|
||||
};
|
||||
|
||||
} // namespace fangjia
|
||||
@@ -0,0 +1,108 @@
|
||||
#pragma once
|
||||
|
||||
#include "pipeline_types.hpp"
|
||||
#include "trt_engine.hpp"
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
|
||||
#include <deque>
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace fangjia {
|
||||
|
||||
class BoxTracker {
|
||||
public:
|
||||
std::vector<int> update(std::vector<Box>& boxes, int frameIndex, int width, int height);
|
||||
|
||||
private:
|
||||
struct Track { Box box; int lastFrame{}; };
|
||||
int nextId_{1};
|
||||
std::unordered_map<int, Track> tracks_;
|
||||
};
|
||||
|
||||
class RtmwEngine {
|
||||
public:
|
||||
RtmwEngine(
|
||||
std::filesystem::path const& enginePath,
|
||||
TrtLogger& logger,
|
||||
int maxSourceWidth,
|
||||
int maxSourceHeight,
|
||||
int maxPeople = 3);
|
||||
~RtmwEngine();
|
||||
|
||||
RtmwEngine(RtmwEngine const&) = delete;
|
||||
RtmwEngine& operator=(RtmwEngine const&) = delete;
|
||||
|
||||
std::vector<PersonPose> infer(
|
||||
cv::Mat const& frame,
|
||||
std::vector<Box> const& boxes,
|
||||
cudaStream_t stream,
|
||||
float* preprocessMs = nullptr,
|
||||
float* inferenceMs = nullptr,
|
||||
float* postprocessMs = nullptr);
|
||||
|
||||
void warmup(cudaStream_t stream);
|
||||
|
||||
private:
|
||||
TrtEngine engine_;
|
||||
int maxSourceWidth_{};
|
||||
int maxSourceHeight_{};
|
||||
int maxPeople_{};
|
||||
std::uint8_t* sourceDevice_{};
|
||||
std::uint8_t* cropsDevice_{};
|
||||
float* networkDevice_{};
|
||||
std::vector<double> centersHost_;
|
||||
std::vector<double> scalesHost_;
|
||||
std::vector<float> simccXHost_;
|
||||
std::vector<float> simccYHost_;
|
||||
};
|
||||
|
||||
class PoseFusionTracker {
|
||||
public:
|
||||
PoseFrameResult update(
|
||||
std::vector<PersonPose> const& raw,
|
||||
std::vector<PersonPose> const& isolated,
|
||||
int frameIndex);
|
||||
|
||||
private:
|
||||
struct Vec2 { double x{}; double y{}; };
|
||||
struct SideFusion {
|
||||
std::array<Vec2, 3> limbPrevious{};
|
||||
std::array<Vec2, 3> limbVelocity{};
|
||||
std::array<Vec2, 21> handPrevious{};
|
||||
std::array<Vec2, 21> handVelocity{};
|
||||
bool hasLimbPrevious{};
|
||||
bool hasLimbVelocity{};
|
||||
bool hasHandPrevious{};
|
||||
bool hasHandVelocity{};
|
||||
std::string limbSource;
|
||||
std::string handSource;
|
||||
};
|
||||
struct Endpoint {
|
||||
bool hasWrist{};
|
||||
Vec2 wrist{};
|
||||
Vec2 velocity{};
|
||||
std::array<Vec2, 21> handShape{};
|
||||
bool hasHandShape{};
|
||||
int lastObservedFrame{-1};
|
||||
int missedFrames{};
|
||||
bool hasLastElbow{};
|
||||
Vec2 lastElbow{};
|
||||
bool hasLastDirection{};
|
||||
Vec2 lastDirection{};
|
||||
};
|
||||
struct PersonState {
|
||||
std::array<SideFusion, 2> fusion;
|
||||
std::array<Endpoint, 2> endpoints;
|
||||
std::array<std::deque<std::array<double, 2>>, 2> boneSamples;
|
||||
std::array<Vec2, 133> smoothed{};
|
||||
bool hasSmoothed{};
|
||||
int lastFrame{-1};
|
||||
};
|
||||
std::unordered_map<int, PersonState> states_;
|
||||
};
|
||||
|
||||
} // namespace fangjia
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include "door_risk.hpp"
|
||||
#include "pipeline_types.hpp"
|
||||
namespace cms {
|
||||
inline Json xy(std::vector<fangjia::PersonPose> const& people, int digits=3) {
|
||||
Json a=Json::array(); for(auto const& p:people) { Json k=Json::array();for(auto const& v:p.keypoints) k.push_back({rounded(v.x,digits),rounded(v.y,digits)});a.push_back(k); } return a;
|
||||
}
|
||||
inline Json scores(std::vector<fangjia::PersonPose> const& people) {
|
||||
Json a=Json::array();for(auto const& p:people) {Json k=Json::array();for(auto const& v:p.keypoints) k.push_back(v.score);a.push_back(k);}return a;
|
||||
}
|
||||
inline Json makeRecord(int frame,std::vector<fangjia::PersonPose> const& raw,std::vector<fangjia::PersonPose> const& isolated,fangjia::PoseFrameResult const& fused, bool diagnostics=true, bool visual=true) {
|
||||
Json r={{"frame_index",frame},{"track_ids",Json::array()},{"boxes",Json::array()},{"det_confidences",Json::array()}};
|
||||
for(auto const&p:raw) {auto const&b=p.box;r["track_ids"].push_back(b.trackId);r["boxes"].push_back({rounded(b.x1,3),rounded(b.y1,3),rounded(b.x2,3),rounded(b.y2,3)});r["det_confidences"].push_back(rounded(b.score,4));}
|
||||
if(diagnostics) {
|
||||
r["raw_keypoints"]=xy(raw,9);r["raw_scores"]=scores(raw);r["isolated_keypoints"]=xy(isolated,9);r["isolated_scores"]=scores(isolated);
|
||||
r["inference_boxes"]=Json::array();for(auto const&p:raw)r["inference_boxes"].push_back({p.box.x1,p.box.y1,p.box.x2,p.box.y2});
|
||||
}
|
||||
if(visual||diagnostics) {
|
||||
r["temporal_selected_keypoints"]=xy(fused.selectedPeople);r["temporal_keypoints"]=xy(fused.smoothedPeople);r["v2_keypoints"]=xy(fused.v2People);
|
||||
r["temporal_metadata"]=Json::array();r["v2_metadata"]=Json::array();
|
||||
for(size_t p=0;p<fused.metadata.size();++p) {
|
||||
Json m=Json::object(),v=Json::object();
|
||||
for(int s=0;s<2;++s) {auto side=s==0?"left":"right";auto const&t=fused.metadata[p][s];
|
||||
m[side]={{"limb_state",t.limbConsensus?"consensus":"inferred"},{"hand_source",t.handSource},{"hand_state",t.handState},{"distance_eligible",t.distanceEligible},{"alarm_eligible",t.alarmEligible},{"selected_hand_valid",t.handValid},{"selected_hand_score",t.handScore}};
|
||||
v[side]={{"state",fangjia::limbStateName(fused.limbs[p*2+s].state)}};
|
||||
} r["temporal_metadata"].push_back(m);r["v2_metadata"].push_back(v);
|
||||
}
|
||||
}
|
||||
r["global_hands"]=Json::array();
|
||||
for(auto const& h:fused.globalHands) {
|
||||
Json points=Json::array(),aliases=Json::array();double cx=0,cy=0;
|
||||
for(auto const& k:h.keypoints) {points.push_back({rounded(k.x,3),rounded(k.y,3)});cx+=k.x;cy+=k.y;}
|
||||
for(auto const&a:h.aliases) aliases.push_back({{"person_id",a.first},{"side",a.second?"left":"right"}});
|
||||
r["global_hands"].push_back({{"person_id",h.personId},{"side",h.left?"left":"right"},{"source",h.source},{"state",h.state},{"distance_eligible",h.distanceEligible},{"alarm_eligible",h.alarmEligible},{"score",rounded(h.score,4)},{"center",{rounded(cx/21,3),rounded(cy/21,3)}},{"span_px",rounded(h.spanPx,3)},{"keypoints",points},{"aliases",aliases}});
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include "frame_result.hpp"
|
||||
#include "door_risk.hpp"
|
||||
namespace cms {
|
||||
template<class T> inline std::optional<T> optionalField(Json const& j,char const* key) {
|
||||
auto it=j.find(key);return it==j.end()||it->is_null()?std::nullopt:std::optional<T>(it->get<T>());
|
||||
}
|
||||
inline RiskResult parseRisk(Json const& j) {
|
||||
RiskResult r;r.status=j.at("status");r.risk_score=optionalField<double>(j,"risk_score");r.risk_level=j.at("risk_level");r.decision=j.at("decision");
|
||||
r.risk_type=j.value("risk_type",std::string());r.door_id=optionalField<std::string>(j,"door_id");
|
||||
r.source=optionalField<std::string>(j,"source");r.track_id=optionalField<int>(j,"track_id");r.side=optionalField<std::string>(j,"side");
|
||||
if(j.contains("door_mask")) {auto const& d=j.at("door_mask");r.door_mask={d.at("status"),d.at("annotation_index"),optionalField<std::string>(d,"path"),d.at("format"),d.at("area_px")};}
|
||||
r.warning_distance_relative=j.value("warning_distance_relative",.30);r.release_distance_relative=j.value("release_distance_relative",.45);
|
||||
for(auto const& x:j.at("regions")) {
|
||||
RegionRisk v;v.status=x.at("status");v.risk_score=optionalField<double>(x,"risk_score");v.risk_level=x.at("risk_level");v.decision=x.at("decision");v.source=x.at("source");
|
||||
v.track_id=x.at("track_id");v.side=x.at("side");v.state=optionalField<std::string>(x,"state");v.door_zone=x.value("door_zone",std::string());
|
||||
v.region_area_px=optionalField<int>(x,"region_area_px");v.door_overlap_px=optionalField<int>(x,"door_overlap_px");
|
||||
v.region_overlap_ratio=optionalField<double>(x,"region_overlap_ratio");v.min_distance_px=optionalField<double>(x,"min_distance_px");
|
||||
v.region_scale_px=optionalField<double>(x,"region_scale_px");v.distance_relative=optionalField<double>(x,"distance_relative");r.regions.push_back(std::move(v));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
inline DoorAssignment parseAssignment(Json const& j) {
|
||||
DoorAssignment a;a.track_id=j.at("track_id");a.person_index=j.at("person_index");a.door_zone=j.at("door_zone");a.raw_door_zone=j.at("raw_door_zone");
|
||||
a.front_probability=j.at("front_probability");a.smoothed_front_probability=j.at("smoothed_front_probability");a.confidence=j.at("confidence");a.evidence_source=j.at("evidence_source");
|
||||
a.pose_feature_count=j.at("pose_feature_count");a.history_frames=j.at("history_frames");a.features=j.at("features").get<std::map<std::string,double>>();a.feature_frontness=j.at("feature_frontness").get<std::map<std::string,double>>();return a;
|
||||
}
|
||||
inline FrameResult makeFrameResult(Json const& record,Json const& raw,CaptureMetadata capture) {
|
||||
FrameResult r;r.frame_index=record.at("frame_index");r.track_ids=record.at("track_ids").get<std::vector<int>>();r.boxes=record.at("boxes").get<std::vector<std::array<double,4>>>();r.capture=capture;r.ungated_risk=parseRisk(raw);return r;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
#include "frame_result.hpp"
|
||||
#include "door_risk.hpp"
|
||||
namespace cms {
|
||||
Json serializeResult(FrameResult const& result);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include "camera_pipeline.hpp"
|
||||
#include <atomic>
|
||||
#include <csignal>
|
||||
#include <pthread.h>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
|
||||
namespace cms {
|
||||
// Construct before starting workers so all threads inherit the signal mask.
|
||||
// Stop requests run in an ordinary thread, never an asynchronous signal handler.
|
||||
class StopSignals {
|
||||
public:
|
||||
explicit StopSignals(CameraPipeline& pipeline):pipeline_(pipeline) {
|
||||
sigemptyset(&signals_);sigaddset(&signals_,SIGINT);sigaddset(&signals_,SIGTERM);
|
||||
if(pthread_sigmask(SIG_BLOCK,&signals_,&old_)!=0)throw std::runtime_error("Cannot block termination signals");
|
||||
try {
|
||||
thread_=std::thread([this]{
|
||||
while(!done_) {
|
||||
timespec timeout{0,100000000};
|
||||
int signal=sigtimedwait(&signals_,nullptr,&timeout);
|
||||
if(signal==SIGINT||signal==SIGTERM){pipeline_.requestStop();break;}
|
||||
}
|
||||
});
|
||||
}catch(...){pthread_sigmask(SIG_SETMASK,&old_,nullptr);throw;}
|
||||
}
|
||||
~StopSignals(){done_=true;if(thread_.joinable())thread_.join();pthread_sigmask(SIG_SETMASK,&old_,nullptr);}
|
||||
StopSignals(StopSignals const&)=delete;
|
||||
StopSignals& operator=(StopSignals const&)=delete;
|
||||
private:
|
||||
CameraPipeline& pipeline_;
|
||||
sigset_t signals_{},old_{};
|
||||
std::atomic<bool> done_{false};
|
||||
std::thread thread_;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace fangjia {
|
||||
|
||||
class TrtEngine {
|
||||
public:
|
||||
TrtEngine(std::filesystem::path const& enginePath, TrtLogger& logger);
|
||||
~TrtEngine();
|
||||
|
||||
TrtEngine(TrtEngine const&) = delete;
|
||||
TrtEngine& operator=(TrtEngine const&) = delete;
|
||||
|
||||
void setInputShape(std::string const& name, nvinfer1::Dims const& shape);
|
||||
void prepare();
|
||||
void bindExternal(std::string const& name, void* pointer, std::size_t bytes);
|
||||
void useOwned(std::string const& name);
|
||||
bool enqueue(cudaStream_t stream);
|
||||
|
||||
void* device(std::string const& name);
|
||||
std::size_t bytes(std::string const& name) const;
|
||||
nvinfer1::Dims shape(std::string const& name) const;
|
||||
nvinfer1::DataType dataType(std::string const& name) const;
|
||||
std::vector<std::string> inputs() const;
|
||||
std::vector<std::string> outputs() const;
|
||||
|
||||
private:
|
||||
struct Buffer {
|
||||
void* owned{};
|
||||
void* active{};
|
||||
std::size_t capacity{};
|
||||
std::size_t required{};
|
||||
bool external{};
|
||||
};
|
||||
|
||||
Buffer& buffer(std::string const& name);
|
||||
Buffer const& buffer(std::string const& name) const;
|
||||
|
||||
TrtUniquePtr<nvinfer1::IRuntime> runtime_;
|
||||
TrtUniquePtr<nvinfer1::ICudaEngine> engine_;
|
||||
TrtUniquePtr<nvinfer1::IExecutionContext> context_;
|
||||
std::filesystem::path enginePath_;
|
||||
std::unordered_map<std::string, Buffer> buffers_;
|
||||
};
|
||||
|
||||
} // namespace fangjia
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include "image.hpp"
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
namespace cms {
|
||||
// GPU NPP BT.601 UYVY -> RGB followed by a CUDA BGR channel swap.
|
||||
class UyvyConverter {
|
||||
public:
|
||||
UyvyConverter(int width,int height);
|
||||
~UyvyConverter();
|
||||
UyvyConverter(UyvyConverter const&)=delete;
|
||||
UyvyConverter& operator=(UyvyConverter const&)=delete;
|
||||
std::shared_ptr<const BgrImage> convert(void const* data,std::size_t stride);
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
#ifndef V4L2_CAPTURE_H
|
||||
#define V4L2_CAPTURE_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <stddef.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// V4L2 流式(MMAP)取图封装
|
||||
// 参考 quick_bring_up_64g.sh 中 v4l2src 的行为:
|
||||
// - 打开 /dev/videoN
|
||||
// - 协商 UYVY 像素格式与分辨率
|
||||
// - 设置相机控制(sensor_mode / trig_pin 等, 等价于 v4l2-ctl -c ...)
|
||||
// - MMAP 方式申请内核缓冲区并流式采集
|
||||
class V4l2Capture {
|
||||
public:
|
||||
// 单帧采集结果
|
||||
struct Frame {
|
||||
void *data = nullptr; // 指向 MMAP 缓冲区, 释放前有效
|
||||
size_t size = 0; // 实际帧字节数(bytesused)
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
uint32_t pixelformat = 0; // V4L2_PIX_FMT_* (如 V4L2_PIX_FMT_UYVY)
|
||||
uint32_t bytes_per_line = 0;
|
||||
uint32_t sequence = 0; // 驱动序号
|
||||
uint64_t timestamp_us = 0; // 单调时钟时间戳(us)
|
||||
int index = -1; // 内部缓冲区索引
|
||||
};
|
||||
|
||||
V4l2Capture() = default;
|
||||
~V4l2Capture();
|
||||
|
||||
V4l2Capture(const V4l2Capture &) = delete;
|
||||
V4l2Capture &operator=(const V4l2Capture &) = delete;
|
||||
|
||||
// 打开设备节点, 例如 /dev/video5
|
||||
bool open(const std::string &device);
|
||||
|
||||
// 关闭设备并释放所有资源
|
||||
void close();
|
||||
|
||||
bool isOpen() const { return fd_ >= 0; }
|
||||
|
||||
// 协商像素格式与分辨率. pixelformat 使用 V4L2_PIX_FMT_* 四字符码
|
||||
bool setFormat(uint32_t width, uint32_t height, uint32_t pixelformat);
|
||||
|
||||
// 设置帧率(通过 VIDIOC_S_PARM). fps 分子/分母均为 fps/1
|
||||
bool setFps(uint32_t fps);
|
||||
|
||||
// 设置 V4L2 控制, 等价 v4l2-ctl -c <name>=<value>. id 为 V4L2_CID_* 之四字符码
|
||||
// 例如 sensor_mode=0x009a2008, trig_pin=0x009a2084
|
||||
bool setControl(uint32_t id, int32_t value);
|
||||
|
||||
// 申请 MMAP 缓冲区, 默认 4 个
|
||||
bool initMmap(uint32_t buffer_count = 4);
|
||||
|
||||
// 开始 / 停止采集
|
||||
bool startStreaming();
|
||||
bool stopStreaming();
|
||||
|
||||
// 阻塞取一帧, 超时 timeout_ms 毫秒. 成功返回 true, 需配对调用 releaseFrame()
|
||||
bool getFrame(Frame &frame, int timeout_ms = 1000);
|
||||
|
||||
// 归还缓冲区给驱动队列, 必须在下次 getFrame 前调用
|
||||
void releaseFrame(const Frame &frame);
|
||||
|
||||
uint32_t width() const { return width_; }
|
||||
uint32_t height() const { return height_; }
|
||||
uint32_t pixelformat() const { return pixelformat_; }
|
||||
|
||||
private:
|
||||
struct MmapBuffer {
|
||||
void *start = nullptr;
|
||||
size_t length = 0;
|
||||
bool queued = false; // 是否在驱动队列中
|
||||
};
|
||||
|
||||
// 归还指定索引的缓冲区 (内部使用)
|
||||
void releaseFrameByIndex(int idx);
|
||||
|
||||
int fd_ = -1;
|
||||
uint32_t width_ = 0;
|
||||
uint32_t height_ = 0;
|
||||
uint32_t pixelformat_ = 0;
|
||||
uint32_t bytes_per_line_ = 0;
|
||||
bool streaming_ = false;
|
||||
std::vector<MmapBuffer> buffers_;
|
||||
int last_dequeued_ = -1; // 当前已出队但未归还的缓冲区索引
|
||||
};
|
||||
|
||||
#endif // V4L2_CAPTURE_H
|
||||
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "frame_result.hpp"
|
||||
#include <opencv2/core.hpp>
|
||||
|
||||
namespace cms {
|
||||
class CameraPipeline;
|
||||
// Pure drawing from the immutable result. Never re-runs inference.
|
||||
cv::Mat renderResult(FrameResult const& result);
|
||||
// Optional observer: closing the window returns without stopping the pipeline.
|
||||
void runLocalVisualization(CameraPipeline const& pipeline);
|
||||
}
|
||||
+443
@@ -0,0 +1,443 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define EXTERN_C extern "C"
|
||||
#else
|
||||
#define EXTERN_C
|
||||
#endif
|
||||
|
||||
#define ZCAN_API
|
||||
#define INVALID_HANDLE_VALUE ((HANDLE)(-1))
|
||||
|
||||
#define CMD_CAN_FILTER 0x14 // 滤波
|
||||
#define CMD_CAN_TTX 0x16 // 定时发送
|
||||
#define CMD_CAN_TTX_CTL 0x17 // 使能定时发送
|
||||
#define CMD_CAN_TRES 0x18 // CAN终端电阻
|
||||
|
||||
#define ZCAN_CMD_SET_CHNL_RECV_MERGE 0x32 // 设置合并接收 0:不合并接收;1:合并接收
|
||||
#define ZCAN_CMD_GET_CHNL_RECV_MERGE 0x33 // 获取是否开启合并接收 0:不合并接收;1:合并接收
|
||||
|
||||
#define CMD_SET_SN 0x42 // 获取SN号
|
||||
#define CMD_GET_SN 0x43 // 设置SN号
|
||||
#define CMD_CAN_TX_TIMEOUT 0x44 // 发送超时
|
||||
|
||||
#define ZCAN_CMD_GET_SEND_QUEUE_SIZE 0x100 // 获取队列大小,uint32_t
|
||||
#define ZCAN_CMD_GET_SEND_QUEUE_SPACE 0x101 // 获取队列剩余空间, uint32_t
|
||||
#define ZCAN_CMD_SET_SEND_QUEUE_CLR 0x102 // 清空发送队列,1:清空
|
||||
#define ZCAN_CMD_SET_SEND_QUEUE_EN 0x103 // 开启发送队列,1:使能
|
||||
|
||||
typedef unsigned char U8;
|
||||
typedef unsigned short U16;
|
||||
typedef unsigned int U32;
|
||||
typedef unsigned long long U64;
|
||||
|
||||
enum eZCANDataDEF
|
||||
{
|
||||
//数据类型
|
||||
ZCAN_DT_ZCAN_CAN_DATA = 1, // CAN/CANFD数据
|
||||
ZCAN_DT_ZCAN_CANFD_DATA = 2, // CAN/CANFD数据
|
||||
ZCAN_DT_ZCAN_ERROR_DATA = 3, // CAN/CANFD错误数据
|
||||
ZCAN_DT_ZCAN_LIN_DATA = 4, // LIN数据
|
||||
ZCAN_DT_ZCAN_LIN_ERROR_DATA = 5, // LIN错误数据
|
||||
};
|
||||
#pragma pack(push, 1)
|
||||
|
||||
typedef enum {
|
||||
ZCAN_TX_NORM = 0, /**< normal transmission */
|
||||
ZCAN_TX_ONCE = 1, /**< single-shot transmission */
|
||||
ZCAN_SR_NORM = 2, /**< self reception */
|
||||
ZCAN_SR_ONCE = 3, /**< single-shot transmission & self reception */
|
||||
} ZCAN_TX_MODE;
|
||||
|
||||
/** CAN filter configuration */
|
||||
typedef struct {
|
||||
U8 type; /**< 0-std_frame, 1-ext_frame */
|
||||
U8 pad[3];
|
||||
U32 sid; /**< start-id */
|
||||
U32 eid; /**< end-id */
|
||||
} ZCAN_FILTER;
|
||||
|
||||
typedef struct {
|
||||
U32 size;
|
||||
ZCAN_FILTER table[64];
|
||||
} ZCAN_FILTER_TABLE;
|
||||
|
||||
/** controller initialization */
|
||||
typedef struct {
|
||||
U32 clk; // 时钟频率
|
||||
U32 mode; /**< bit0-normal/listen_only, bit1-ISO/BOSCH */
|
||||
struct {
|
||||
U8 tseg1;
|
||||
U8 tseg2;
|
||||
U8 sjw;
|
||||
U8 smp;
|
||||
U16 brp;
|
||||
} aset;
|
||||
struct {
|
||||
U8 tseg1;
|
||||
U8 tseg2;
|
||||
U8 sjw;
|
||||
U8 smp;
|
||||
U16 brp;
|
||||
} dset;
|
||||
} ZCAN_INIT;
|
||||
|
||||
/** CAN message info */
|
||||
typedef struct {
|
||||
U32 txm : 4; /**< TX-mode, @see ZCAN_TX_MODE */
|
||||
U32 fmt : 4; /**< 0-CAN2.0, 1-CANFD */
|
||||
U32 sdf : 1; /**< 0-data_frame, 1-remote_frame */
|
||||
U32 sef : 1; /**< 0-std_frame, 1-ext_frame */
|
||||
U32 err : 1; /**< error flag */
|
||||
U32 brs : 1; /**< bit-rate switch */
|
||||
U32 est : 1; /**< error state */
|
||||
U32 tx : 1; /**< received valid, tx frame */
|
||||
U32 echo : 1; /**< tx valid, echo frame */
|
||||
U32 qsend_100us:1; /**< queue send delay unit, 1-100us, 0-ms */
|
||||
U32 qsend:1; /**< send valid, queue send frame */
|
||||
U32 pad : 15;
|
||||
} ZCAN_MSG_INF;
|
||||
|
||||
/** CAN message header */
|
||||
typedef struct {
|
||||
U32 ts; /**< timestamp */
|
||||
U32 id; /**< CAN-ID */
|
||||
ZCAN_MSG_INF inf; /**< @see ZCAN_MSG_INF */
|
||||
U16 pad; /**< send queue delay */
|
||||
U8 chn; /**< channel */
|
||||
U8 len; /**< data length */
|
||||
} ZCAN_MSG_HDR;
|
||||
|
||||
/** CAN2.0-frame */
|
||||
typedef struct {
|
||||
ZCAN_MSG_HDR hdr;
|
||||
U8 dat[8];
|
||||
} ZCAN_20_MSG;
|
||||
|
||||
/** CANFD-frame */
|
||||
typedef struct {
|
||||
ZCAN_MSG_HDR hdr;
|
||||
U8 dat[64];
|
||||
} ZCAN_FD_MSG;
|
||||
|
||||
// 错误帧
|
||||
typedef struct {
|
||||
ZCAN_MSG_HDR hdr;
|
||||
U8 dat[8];
|
||||
} ZCAN_ERR_MSG;
|
||||
|
||||
// 定时发送结构体
|
||||
typedef struct {
|
||||
U32 interval; // 定时发送周期,单位 100us
|
||||
U16 repeat; // 发送次数,0等于循环发
|
||||
U8 index; // 定时发送列表的帧索引号,也就是第几条定时发送报文
|
||||
U8 flags; // 0-此帧禁用定时发送,1-此帧使能定时发送
|
||||
ZCAN_FD_MSG msg; // CANFD帧结构体
|
||||
} ZCAN_TTX;
|
||||
|
||||
typedef struct {
|
||||
U32 size; // 实际生效的数组的长度(用来控制实际生效多少条定时发送帧结构体)
|
||||
ZCAN_TTX table[8]; // 最大设置8条
|
||||
} ZCAN_TTX_CFG;
|
||||
|
||||
// 设备信息
|
||||
typedef struct {
|
||||
U16 hwv; /**< hardware version */
|
||||
U16 fwv; /**< firmware version */
|
||||
U16 drv; /**< driver version */
|
||||
U16 api; /**< API version */
|
||||
U16 irq; /**< IRQ */
|
||||
U8 chn; /**< channels */
|
||||
U8 sn[20]; /**< serial number */
|
||||
U8 id[40]; /**< card id */
|
||||
U16 pad[4];
|
||||
} ZCAN_DEV_INF;
|
||||
|
||||
/** controller status */
|
||||
typedef struct {
|
||||
U8 IR; /**< not used(for backward compatibility) */
|
||||
U8 MOD; /**< not used */
|
||||
U8 SR; /**< not used */
|
||||
U8 ALC; /**< not used */
|
||||
U8 ECC; /**< not used */
|
||||
U8 EWL; /**< not used */
|
||||
U8 RXE; /**< RX errors */
|
||||
U8 TXE; /**< TX errors */
|
||||
U32 PAD;
|
||||
} ZCAN_STAT;
|
||||
|
||||
//lin begin
|
||||
typedef struct _VCI_LIN_INIT_CONFIG
|
||||
{
|
||||
U8 linMode; // 是否作为主机,0-从机,1-主机
|
||||
U8 chkSumMode; // 校验方式,1-经典校验 2-增强校验 3-自动(对应eZLINChkSumMode的模式)
|
||||
U16 reserved; // 保留
|
||||
U32 linBaud; // 波特率,取值1000~20000
|
||||
}ZCAN_LIN_INIT_CONFIG, *PZCAN_LIN_INIT_CONFIG;
|
||||
|
||||
typedef struct tagZCANLINData
|
||||
{
|
||||
union{
|
||||
struct {
|
||||
U8 ID : 6; // 帧ID
|
||||
U8 Parity : 2; // 帧ID校验
|
||||
}unionVal;
|
||||
U8 rawVal; // 受保护的ID原始值
|
||||
} PID; // 受保护的ID
|
||||
struct
|
||||
{
|
||||
U64 timeStamp; // 时间戳,单位微秒(us)
|
||||
U8 dataLen; // 数据长度
|
||||
U8 dir; // 传输方向,0-接收 1-发送
|
||||
U8 chkSum; // 数据校验, 部分设备不支持校验数据的获取
|
||||
U8 reserved[13]; // 保留
|
||||
U8 data[8]; // 数据
|
||||
}RxData; // 仅接收数据时有效
|
||||
U8 reserved[7]; // 保留
|
||||
}ZCANLINData;
|
||||
|
||||
typedef struct tagZCANLINErrData
|
||||
{
|
||||
U64 timeStamp; // 时间戳, 单位微秒(us)
|
||||
union {
|
||||
struct {
|
||||
U8 ID : 6; // 帧ID
|
||||
U8 Parity : 2; // 帧ID校验
|
||||
}unionVal;
|
||||
U8 rawVal; // 受保护的ID原始值
|
||||
} PID; // 受保护的ID
|
||||
U8 dataLen;
|
||||
U8 data[8];
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
U16 errStage : 4; // 错误阶段
|
||||
U16 errReason : 4; // 错误原因
|
||||
U16 reserved : 8; // 保留
|
||||
};
|
||||
U16 unionErrData;
|
||||
}errData;
|
||||
U8 dir; // 传输方向
|
||||
U8 chkSum; // 数据校验, 部分设备不支持校验数据的获取
|
||||
U8 reserved[10]; // 保留
|
||||
}ZCANLINErrData;
|
||||
|
||||
typedef struct _VCI_LIN_MSG{
|
||||
U8 chnl; // 数据通道
|
||||
U8 dataType; // 数据类型, 0-LIN数据 1-LIN错误数据
|
||||
union
|
||||
{
|
||||
ZCANLINData zcanLINData; // LIN数据
|
||||
ZCANLINErrData zcanLINErrData; // LIN错误数据
|
||||
U8 raw[46]; // RAW数据
|
||||
} data; // 实际数据, 联合体,有效成员根据 dataType 字段而定
|
||||
}ZCAN_LIN_MSG, *PZCAN_LIN_MSG;
|
||||
|
||||
enum eZLINChkSumMode
|
||||
{
|
||||
DEFAULT = 0, // 默认,启动时配置
|
||||
CLASSIC_CHKSUM, // 经典校验
|
||||
ENHANCE_CHKSUM, // 增强校验
|
||||
AUTOMATIC, // 自动,设备自动识别校验方式(仅ZCAN_SetLINSubscribe时有效)
|
||||
};
|
||||
enum
|
||||
{
|
||||
MAX_LIN_ID_COUNT = 63,
|
||||
MIN_LIN_DLC = 1,
|
||||
MAX_LIN_DLC = 8,
|
||||
AUTO_LIN_DLC = 255,
|
||||
};
|
||||
typedef struct _VCI_LIN_PUBLISH_CFG
|
||||
{
|
||||
U8 ID; // 受保护的ID(ID取值范围为0-63)
|
||||
U8 dataLen; // dataLen范围为1-8
|
||||
U8 data[8];
|
||||
U8 chkSumMode; // 校验方式,0-默认,启动时配置 1-经典校验 2-增强校验(对应eZLINChkSumMode的模式)
|
||||
U8 reserved[5]; // 保留
|
||||
}ZCAN_LIN_PUBLISH_CFG, *PZCAN_LIN_PUBLISH_CFG;
|
||||
|
||||
typedef struct _VCI_LIN_SUBSCIBE_CFG
|
||||
{
|
||||
U8 ID; // 受保护的ID(ID取值范围为0-63)
|
||||
U8 dataLen; // dataLen范围为1-8 当为255(0xff)则表示设备自动识别报文长度
|
||||
U8 chkSumMode; // 校验方式,0-默认,启动时配置 1-经典校验 2-增强校验 3-自动(对应eZLINChkSumMode的模式)
|
||||
U8 reserved[5]; // 保留
|
||||
}ZCAN_LIN_SUBSCIBE_CFG, *PZCAN_LIN_SUBSCIBE_CFG;
|
||||
|
||||
// 合并接收数据数据结构,支持CAN/CANFD/LIN/错误等不同类型数据
|
||||
typedef struct tagZCANDataObj
|
||||
{
|
||||
U8 dataType; // 数据类型, 参考eZCANDataDEF中 数据类型 部分定义
|
||||
U8 chnl; // 数据通道
|
||||
union{
|
||||
struct{
|
||||
U16 reserved : 16; // 保留
|
||||
}unionVal;
|
||||
U16 rawVal;
|
||||
}flag; // 标志信息, 暂未使用
|
||||
U8 extraData[4]; // 额外数据, 暂未使用
|
||||
union
|
||||
{
|
||||
ZCAN_20_MSG zcanCANData; // CAN数据
|
||||
ZCAN_FD_MSG zcanCANFDData; // CANFD数据
|
||||
ZCAN_ERR_MSG zcanErrData; // 错误数据
|
||||
ZCANLINData zcanLINData; // LIN数据
|
||||
ZCANLINErrData zcanLINErrData; // LIN错误数据
|
||||
U8 raw[92]; // RAW数据
|
||||
} data; // 实际数据, 联合体,有效成员根据 dataType 字段而定
|
||||
}ZCANDataObj;
|
||||
|
||||
|
||||
// UDS传输协议版本
|
||||
typedef U8 ZCAN_UDS_TRANS_VER;
|
||||
#define ZCAN_UDS_TRANS_VER_0 0 // ISO15765-2(2004版本)
|
||||
#define ZCAN_UDS_TRANS_VER_1 1 // ISO15765-2(2016版本)
|
||||
|
||||
// 帧类型
|
||||
typedef U8 ZCAN_UDS_FRAME_TYPE;
|
||||
#define ZCAN_UDS_FRAME_CAN 0 // CAN帧
|
||||
#define ZCAN_UDS_FRAME_CANFD 1 // CANFD帧
|
||||
#define ZCAN_UDS_FRAME_CANFD_BRS 2 // CANFD加速帧
|
||||
|
||||
// UDS填充模式
|
||||
#define FILL_MODE_SHORT 0 // 小于8字节填充至8字节,大于8字节时按DLC就近填充
|
||||
#define FILL_MODE_NONE 1 // 不填充
|
||||
#define FILL_MODE_MAX 2 // 填充至最大数据长度 (不建议)
|
||||
|
||||
// CAN UDS请求数据
|
||||
typedef struct _ZCAN_UDS_REQUEST
|
||||
{
|
||||
U32 req_id; // 请求事务ID,范围0~65535,本次请求的唯一标识
|
||||
U8 channel; // 设备通道索引 0~255
|
||||
ZCAN_UDS_FRAME_TYPE frame_type; // 帧类型
|
||||
U8 reserved0[2]; // 保留
|
||||
U32 src_addr; // 请求地址
|
||||
U32 dst_addr; // 响应地址
|
||||
U8 suppress_response; // 1:抑制响应
|
||||
U8 sid; // 请求服务id
|
||||
U8 reserved1[6]; // 保留
|
||||
struct {
|
||||
U32 timeout; // 响应超时时间(ms)。因PC定时器误差,建议设置不小于200ms
|
||||
U32 enhanced_timeout; // 收到消极响应错误码为0x78后的超时时间(ms)。因PC定时器误差,建议设置不小于200ms
|
||||
U8 check_any_negative_response:1; // 接收到非本次请求服务的消极响应时是否需要判定为响应错误
|
||||
U8 wait_if_suppress_response:1; // 抑制响应时是否需要等待消极响应,等待时长为响应超时时间
|
||||
U8 flag:6; // 保留
|
||||
U8 reserved0[7]; // 保留
|
||||
} session_param; // 会话层参数
|
||||
struct {
|
||||
ZCAN_UDS_TRANS_VER version; // 传输协议版本, VERSION_0, VERSION_1
|
||||
U8 max_data_len; // 单帧最大数据长度, can:8, canfd:64
|
||||
U8 local_st_min; // 本程序发送流控时用,连续帧之间的最小间隔, 0x00-0x7F(0ms~127ms), 0xF1-0xF9(100us~900us)
|
||||
U8 block_size; // 流控帧的块大小
|
||||
U8 fill_byte; // 无效字节的填充数据
|
||||
U8 ext_frame; // 0:标准帧 1:扩展帧
|
||||
U8 is_modify_ecu_st_min; // 是否忽略ECU返回流控的STmin,强制使用本程序设置的 remote_st_min
|
||||
U8 remote_st_min; // 发送多帧时用, is_ignore_ecu_st_min = 1 时有效, 0x00-0x7F(0ms~127ms), 0xF1-0xF9(100us~900us)
|
||||
U32 fc_timeout; // 接收流控超时时间(ms), 如发送首帧后需要等待回应流控帧
|
||||
U8 fill_mode; // 数据长度填充模式, FILL_MODE_SHORT, FILL_MODE_NONE, FILL_MODE_MAX
|
||||
U8 reserved0[3]; // 保留
|
||||
} trans_param; // 传输层参数
|
||||
U8 *data; // 数据数组(不包含SID)
|
||||
U32 data_len; // 数据数组的长度
|
||||
U32 reserved2; // 保留
|
||||
} ZCAN_UDS_REQUEST;
|
||||
|
||||
// UDS错误码
|
||||
typedef U8 ZCAN_UDS_ERROR;
|
||||
#define ZCAN_UDS_ERROR_OK 0 // 没错误
|
||||
#define ZCAN_UDS_ERROR_TIMEOUT 1 // 响应超时
|
||||
#define ZCAN_UDS_ERROR_TRANSPORT 2 // 发送数据失败
|
||||
#define ZCAN_UDS_ERROR_CANCEL 3 // 取消请求
|
||||
#define ZCAN_UDS_ERROR_SUPPRESS_RESPONSE 4 // 抑制响应
|
||||
#define ZCAN_UDS_ERROR_BUSY 5 // 忙碌中
|
||||
#define ZCAN_UDS_ERROR_REQ_PARAM 6 // 请求参数错误
|
||||
#define ZCAN_UDS_ERROR_OTHTER 100
|
||||
|
||||
typedef U8 ZCAN_UDS_RESPONSE_TYPE;
|
||||
#define ZCAN_UDS_RT_NEGATIVE 0 // 消极响应
|
||||
#define ZCAN_UDS_RT_POSITIVE 1 // 积极响应
|
||||
|
||||
// UDS响应数据
|
||||
typedef struct _ZCAN_UDS_RESPONSE
|
||||
{
|
||||
ZCAN_UDS_ERROR status; // 响应状态
|
||||
U8 reserved[6]; // 保留
|
||||
ZCAN_UDS_RESPONSE_TYPE type; // 响应类型
|
||||
union {
|
||||
struct {
|
||||
U8 sid; // 响应服务id
|
||||
U32 data_len; // 数据长度(不包含SID), 数据存放在接口传入的dataBuf中
|
||||
} positive;
|
||||
struct {
|
||||
U8 neg_code; // 固定为0x7F
|
||||
U8 sid; // 请求服务id
|
||||
U8 error_code; // 错误码
|
||||
} negative;
|
||||
U8 raw[8];
|
||||
};
|
||||
} ZCAN_UDS_RESPONSE;
|
||||
|
||||
// UDS控制类型
|
||||
typedef U32 ZCAN_UDS_CTRL_CODE;
|
||||
#define ZCAN_UDS_CTRL_STOP_REQ 0 // 停止UDS请求
|
||||
|
||||
// UDS控制请求
|
||||
typedef struct _ZCAN_UDS_CTRL_REQ
|
||||
{
|
||||
U32 reqID; // 请求事务ID,指明要操作哪一条请求
|
||||
ZCAN_UDS_CTRL_CODE cmd; // 控制类型
|
||||
U8 reserved[8]; // 保留
|
||||
} ZCAN_UDS_CTRL_REQ;
|
||||
|
||||
// UDS控制结果
|
||||
typedef U32 ZCAN_UDS_CTRL_RESULT;
|
||||
#define ZCAN_UDS_CTRL_RESULT_OK 0 // 成功
|
||||
#define ZCAN_UDS_CTRL_RESULT_ERR 1 // 失败
|
||||
|
||||
// UDS控制响应数据
|
||||
typedef struct _ZCAN_UDS_CTRL_RESP
|
||||
{
|
||||
ZCAN_UDS_CTRL_RESULT result; // 操作结果
|
||||
U8 reserved[8]; // 保留
|
||||
} ZCAN_UDS_CTRL_RESP;
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
//
|
||||
EXTERN_C U32 ZCAN_API VCI_OpenDevice(U32 Type, U32 Card, U32 Reserved);
|
||||
EXTERN_C U32 ZCAN_API VCI_CloseDevice(U32 Type, U32 Card);
|
||||
EXTERN_C U32 ZCAN_API VCI_InitCAN(U32 Type, U32 Card, U32 Port, ZCAN_INIT *pInit);
|
||||
EXTERN_C U32 ZCAN_API VCI_ReadBoardInfo(U32 Type, U32 Card, ZCAN_DEV_INF *pInfo);
|
||||
EXTERN_C U32 ZCAN_API VCI_ReadErrInfo(U32 Type, U32 Card, U32 Port, ZCAN_ERR_MSG *pErr);
|
||||
EXTERN_C U32 ZCAN_API VCI_ReadCANStatus(U32 Type, U32 Card, U32 Port, ZCAN_STAT *pStat);
|
||||
EXTERN_C U32 ZCAN_API VCI_GetReference(U32 Type, U32 Card, U32 Port, U32 Ref, void *pData);
|
||||
EXTERN_C U32 ZCAN_API VCI_SetReference(U32 Type, U32 Card, U32 Port, U32 Ref, void *pData);
|
||||
EXTERN_C U32 ZCAN_API VCI_GetReceiveNum(U32 Type, U32 Card, U32 Port);
|
||||
EXTERN_C U32 ZCAN_API VCI_ClearBuffer(U32 Type, U32 Card, U32 Port);
|
||||
EXTERN_C U32 ZCAN_API VCI_StartCAN(U32 Type, U32 Card, U32 Port);
|
||||
EXTERN_C U32 ZCAN_API VCI_ResetCAN(U32 Type, U32 Card, U32 Port);
|
||||
EXTERN_C U32 ZCAN_API VCI_Transmit(U32 Type, U32 Card, U32 Port, ZCAN_20_MSG *pData, U32 Count);
|
||||
EXTERN_C U32 ZCAN_API VCI_TransmitFD(U32 Type, U32 Card, U32 Port, ZCAN_FD_MSG *pData, U32 Count);
|
||||
EXTERN_C U32 ZCAN_API VCI_Receive(U32 Type, U32 Card, U32 Port, ZCAN_20_MSG *pData, U32 Count, U32 Time);
|
||||
EXTERN_C U32 ZCAN_API VCI_ReceiveFD(U32 Type, U32 Card, U32 Port, ZCAN_FD_MSG *pData, U32 Count, U32 Time);
|
||||
EXTERN_C U32 ZCAN_API VCI_Debug(U32 Debug);
|
||||
|
||||
// LIN
|
||||
EXTERN_C U32 VCI_InitLIN(U32 Type, U32 Card, U32 LinChn, PZCAN_LIN_INIT_CONFIG pLINInitConfig);
|
||||
EXTERN_C U32 VCI_StartLIN(U32 Type, U32 Card, U32 LinChn);
|
||||
EXTERN_C U32 VCI_ResetLIN(U32 Type, U32 Card, U32 LinChn);
|
||||
EXTERN_C U32 VCI_TransmitLIN(U32 Type, U32 Card, U32 LinChn, PZCAN_LIN_MSG pSend, U32 Len);
|
||||
EXTERN_C U32 VCI_GetLINReceiveNum(U32 Type, U32 Card, U32 LinChn);
|
||||
EXTERN_C U32 VCI_ClearLINBuffer(U32 Type, U32 Card, U32 LinChn);
|
||||
EXTERN_C U32 VCI_ReceiveLIN(U32 Type, U32 Card, U32 LinChn, PZCAN_LIN_MSG pReceive, U32 Len,int WaitTime);
|
||||
EXTERN_C U32 VCI_SetLINSubscribe(U32 Type, U32 Card, U32 LinChn, PZCAN_LIN_SUBSCIBE_CFG pSend, U32 nSubscribeCount);
|
||||
EXTERN_C U32 VCI_SetLINPublish(U32 Type, U32 Card, U32 LinChn, PZCAN_LIN_PUBLISH_CFG pSend, U32 nPublishCount);
|
||||
|
||||
// 合并接收
|
||||
EXTERN_C U32 VCI_TransmitData(unsigned Type, unsigned Card, unsigned Port, ZCANDataObj *pData, unsigned Count);
|
||||
EXTERN_C U32 VCI_ReceiveData(unsigned Type, unsigned Card, unsigned Port, ZCANDataObj *pData, unsigned Count, unsigned Time);
|
||||
|
||||
// UDS
|
||||
EXTERN_C U32 VCI_UDS_Request(unsigned Type, unsigned Card, const ZCAN_UDS_REQUEST *req, ZCAN_UDS_RESPONSE *resp, U8 *dataBuf, U32 dataBufSize);
|
||||
EXTERN_C U32 VCI_UDS_Control(unsigned Type, unsigned Card, const ZCAN_UDS_CTRL_REQ *ctrl, ZCAN_UDS_CTRL_RESP *resp);
|
||||
Reference in New Issue
Block a user