412 lines
14 KiB
C++
412 lines
14 KiB
C++
#include "Hd_CameraModule_HIK_PCIE.h"
|
|
using namespace std;
|
|
#include <QTextCodec>
|
|
#include <thread>
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <mutex>
|
|
enum statusEnum {
|
|
CONNECTED,
|
|
RECONNECTING,
|
|
DISCONNECTED
|
|
};
|
|
enum triggerModeEnum {
|
|
SOFTWARE,
|
|
HARDWARE,
|
|
EMPTY
|
|
};
|
|
std::atomic<statusEnum> cameraStatus(DISCONNECTED);
|
|
std::atomic<bool> reconnecting(false);
|
|
std::mutex handleMutex;
|
|
triggerModeEnum triggerMode = HARDWARE;
|
|
int getImageTimeOut = 3000;
|
|
bool runFlag = true;
|
|
//总取图数
|
|
int imgIndex = 0;
|
|
MV_GENTL_DEV_INFO_LIST m_stDevList; // ch:设备信息列表结构体变量,用来存储设备列表
|
|
MV_GENTL_IF_INFO_LIST m_stIFList;
|
|
string Username = "";
|
|
int cameraIndex = -1;
|
|
QString lastError;
|
|
QQueue<cv::Mat> queuepic;
|
|
QMutex* n_mutex = new QMutex();
|
|
|
|
void* handle = NULL;
|
|
void CloseDevice();
|
|
|
|
//0106 断线重连
|
|
void __stdcall ReconnectDevice(unsigned int nMsgType, void* pUser);
|
|
void __stdcall ImageCallBackEx(unsigned char* pData0, MV_FRAME_OUT_INFO_EX* pFrameInfo0, void* pUser0);
|
|
//搜索所有设备
|
|
int SearchDevice(){
|
|
|
|
if (m_stIFList.nInterfaceNum == 0) {
|
|
const char* chCtiPath = "MvFGProducerXoF.cti";//.cti文件路径
|
|
int nRet = 0;
|
|
nRet = MV_CC_EnumInterfacesByGenTL(&m_stIFList, chCtiPath);
|
|
if (nRet != MV_OK)
|
|
{
|
|
qDebug() << "EnumInterfacsByGenTL fail";
|
|
return -1;
|
|
}
|
|
|
|
}
|
|
if (m_stIFList.nInterfaceNum == 0)
|
|
{
|
|
qDebug() << "No Device";
|
|
return -1;
|
|
}
|
|
int nRet = 0;
|
|
// ch:枚举子网内所有设备 | en:Enumerate all devices within subnet
|
|
for (int p = 0; p < m_stIFList.nInterfaceNum; p++)
|
|
{
|
|
memset(&m_stDevList, 0, sizeof(MV_CC_DEVICE_INFO_LIST));
|
|
nRet = MV_CC_EnumDevicesByGenTL(m_stIFList.pIFInfo[p], &m_stDevList);
|
|
for (int i = 0; i < m_stDevList.nDeviceNum; i++)
|
|
{
|
|
//SN
|
|
unsigned char* name = m_stDevList.pDeviceInfo[i]->chSerialNumber;
|
|
string userName = static_cast<string>((LPCSTR)name);
|
|
if (Username == userName)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
if (MV_OK != nRet)
|
|
{
|
|
return -1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool connectDevice()
|
|
{
|
|
int cameraIndex = SearchDevice();
|
|
if (cameraIndex == -1) {
|
|
//未匹配到相机
|
|
qDebug() << "[Waring] " << "Hd_CameraModule_HIK not match --CameraName:" << QString::fromStdString(Username);
|
|
lastError = "找不到相机" + QString::fromStdString(Username);
|
|
return false;
|
|
}
|
|
if (handle != NULL)
|
|
{
|
|
MV_CC_StopGrabbing(handle);
|
|
handle = NULL;
|
|
//MV_CC_CloseDevice(handle);
|
|
//MV_CC_DestroyHandle(handle);
|
|
}
|
|
|
|
//打开相机
|
|
MV_CC_CreateHandleByGenTL(&handle, m_stDevList.pDeviceInfo[cameraIndex]);
|
|
int nRet = MV_CC_OpenDevice(handle);
|
|
// //加载用户集,相机设置应当UserSet1
|
|
nRet = MV_CC_SetEnumValueByString(handle, "UserSetSelector", "UserSet1");
|
|
if (MV_OK != nRet)
|
|
{
|
|
qDebug() << "Set UserSetSelector fail! nRet " << nRet;
|
|
}
|
|
// ch:注册抓图回调 | en:Register image callback
|
|
MV_CC_SetIntValueEx(handle, "GevHeartbeatTimeout", 3000);//心跳日志
|
|
|
|
nRet = MV_CC_RegisterImageCallBackEx(handle, ImageCallBackEx, handle);//注册回调
|
|
if (MV_OK != nRet)
|
|
return false;
|
|
nRet = MV_CC_RegisterExceptionCallBack(handle, ReconnectDevice, NULL);//断线重连
|
|
if (MV_OK != nRet)
|
|
return false;
|
|
MV_CC_StartGrabbing(handle);
|
|
cameraStatus.store(CONNECTED);
|
|
return true;
|
|
}
|
|
void SetExposureTime(float exposureValue)
|
|
{
|
|
qDebug() << "[INFO] " << "exposureValue:" << exposureValue;
|
|
MV_CC_SetEnumValue(handle, "ExposureAuto", MV_EXPOSURE_AUTO_MODE_OFF);
|
|
MV_CC_SetFloatValue(handle, "ExposureTime", exposureValue);
|
|
}
|
|
|
|
void SetGain(float GainValue)
|
|
{
|
|
qDebug() << "[INFO] " << "GainValue:" << GainValue;
|
|
MV_CC_SetEnumValue(handle, "GainAuto", 0);
|
|
MV_CC_SetFloatValue(handle, "Gain", GainValue);
|
|
}
|
|
|
|
void __stdcall ReconnectDevice(unsigned int nMsgType, void* pUser) {
|
|
|
|
qDebug() << "[Error] " << " MV camera disconnects!";
|
|
|
|
if (nMsgType == MV_EXCEPTION_DEV_DISCONNECT && cameraStatus.load() == CONNECTED) {
|
|
|
|
// 防止重复启动重连线程
|
|
bool expected = false;
|
|
if (!reconnecting.compare_exchange_strong(expected, true)) {
|
|
qDebug() << "[Warning] Reconnect already in progress, skipping duplicate request";
|
|
return;
|
|
}
|
|
|
|
cameraStatus.store(RECONNECTING);
|
|
// 启动后台线程负责重连,避免在回调线程中长时间阻塞
|
|
std::thread([]() {
|
|
qDebug() << QString::fromStdString(Username) << " start reconnect thread";
|
|
|
|
// 确保设备资源关闭
|
|
CloseDevice();
|
|
int times = 0;
|
|
while (runFlag)
|
|
{
|
|
qDebug() << QString::fromStdString(Username) << " reconnect attempt:" << times++;
|
|
if (connectDevice())
|
|
{
|
|
qWarning() << QString::fromStdString(Username) << " reconnect success!";
|
|
cameraStatus.store(CONNECTED);
|
|
reconnecting.store(false);
|
|
return;
|
|
}
|
|
|
|
qDebug() << QString::fromStdString(Username) << " reconnect failed, waiting 1 seconds before retry...";
|
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
}
|
|
|
|
// 如果runFlag为false,线程退出
|
|
qDebug() << "[Info] reconnect thread stopped (runFlag=false)";
|
|
cameraStatus.store(DISCONNECTED);
|
|
reconnecting.store(false);
|
|
}).detach();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//回调函数;
|
|
//经测试,相机像素格式Mono8、RGB8是可以正常转换的,YUV422还需要优化;RGB8格式比YUV422帧率低;BGR格式会导致异色问题
|
|
void __stdcall ImageCallBackEx(unsigned char* pData0, MV_FRAME_OUT_INFO_EX* pFrameInfo0, void* pUser0)
|
|
{
|
|
Q_UNUSED(pUser0)
|
|
int frameNum = 0;
|
|
double time_Start = (double)clock();
|
|
QMutexLocker locker(n_mutex);
|
|
cv::Mat srcImage = cv::Mat();
|
|
//qDebug() << "[info]" << __FUNCTION__ << " line:" << __LINE__ << "--- start";
|
|
if (pFrameInfo0)
|
|
{
|
|
//获取的是单通道灰度图
|
|
if (pFrameInfo0->enPixelType == PixelType_Gvsp_Mono8)//获取当前采集到的图像格式
|
|
{
|
|
srcImage = cv::Mat(pFrameInfo0->nHeight, pFrameInfo0->nWidth, CV_8UC1, pData0);
|
|
}
|
|
//获取的是RGB8图
|
|
else if (pFrameInfo0->enPixelType == PixelType_Gvsp_BGR8_Packed)
|
|
{
|
|
srcImage = cv::Mat(pFrameInfo0->nHeight, pFrameInfo0->nWidth, CV_8UC3, pData0);
|
|
|
|
//cvtColor(srcImage, srcImage, COLOR_RGB2BGR);//导致异色
|
|
}
|
|
else if (IsColor(pFrameInfo0->enPixelType)) //其它格式彩色图
|
|
{
|
|
MvGvspPixelType enDstPixelType = PixelType_Gvsp_Undefined;
|
|
enDstPixelType = PixelType_Gvsp_BGR8_Packed;
|
|
unsigned char* pConvertData = NULL;
|
|
pConvertData = (unsigned char*)malloc(pFrameInfo0->nWidth * pFrameInfo0->nHeight * 3);
|
|
if (NULL == pConvertData)
|
|
{
|
|
printf("malloc pConvertData fail!\n");
|
|
}
|
|
else
|
|
{
|
|
unsigned int nConvertDataSize = 0;
|
|
nConvertDataSize = pFrameInfo0->nWidth * pFrameInfo0->nHeight * 3;
|
|
|
|
// ch:像素格式转换 | en:Convert pixel format
|
|
MV_CC_PIXEL_CONVERT_PARAM stConvertParam = { 0 };
|
|
|
|
frameNum = pFrameInfo0->nFrameNum; //ch:帧号 | en:frame number
|
|
stConvertParam.nWidth = pFrameInfo0->nWidth; //ch:图像宽 | en:image width
|
|
stConvertParam.nHeight = pFrameInfo0->nHeight; //ch:图像高 | en:image height
|
|
stConvertParam.pSrcData = pData0; //ch:输入数据缓存 | en:input data buffer
|
|
stConvertParam.nSrcDataLen = pFrameInfo0->nFrameLen; //ch:输入数据大小 | en:input data size
|
|
stConvertParam.enSrcPixelType = pFrameInfo0->enPixelType; //ch:输入像素格式 | en:input pixel format
|
|
stConvertParam.enDstPixelType = enDstPixelType; //ch:输出像素格式 | en:output pixel format
|
|
stConvertParam.pDstBuffer = pConvertData; //ch:输出数据缓存 | en:output data buffer
|
|
stConvertParam.nDstBufferSize = nConvertDataSize; //ch:输出缓存大小 | en:output buffer size
|
|
int nRet = MV_CC_ConvertPixelType(handle, &stConvertParam);
|
|
|
|
Mat mysrcImage = cv::Mat(pFrameInfo0->nHeight, pFrameInfo0->nWidth, CV_8UC3, stConvertParam.pDstBuffer);
|
|
mysrcImage.copyTo(srcImage);
|
|
|
|
free(pConvertData);
|
|
pConvertData = NULL;
|
|
stConvertParam = { 0 };
|
|
}
|
|
}
|
|
}
|
|
if (srcImage.empty())
|
|
{
|
|
srcImage = cv::Mat(5, 5, CV_8UC1).setTo(0);
|
|
}
|
|
|
|
queuepic.push_back(srcImage);
|
|
pData0 = { 0 };
|
|
double time_End = (double)clock();
|
|
qDebug() << "[INFO] " << __FUNCTION__ << " line:" << __LINE__ << " checkImg getImg,Time:" << (time_End - time_Start) << "ms"
|
|
<< "--CameraName:" << QString::fromStdString(Username) << " imgIndex:" << imgIndex << "nFrameNum : " << frameNum;
|
|
}
|
|
|
|
Hd_CameraModule_HIK_hard_PCIE::Hd_CameraModule_HIK_hard_PCIE()
|
|
{
|
|
//m_thread = new MyThread;
|
|
//m_mutex = new QMutex();
|
|
}
|
|
//关闭设备
|
|
void CloseDevice()
|
|
{
|
|
if (handle == nullptr)
|
|
return;
|
|
unsigned int nRet = MV_CC_StopGrabbing(handle);
|
|
if (nRet == 0) {
|
|
//MV_CC_CloseDevice(handle);
|
|
//MV_CC_DestroyHandle(handle);
|
|
}
|
|
handle = NULL;
|
|
//nRet = MV_CC_CloseDevice(handle);
|
|
//nRet = MV_CC_DestroyHandle(handle);
|
|
}
|
|
Hd_CameraModule_HIK_hard_PCIE::~Hd_CameraModule_HIK_hard_PCIE()
|
|
{
|
|
CloseDevice();
|
|
if (n_mutex)
|
|
{
|
|
delete n_mutex;
|
|
n_mutex = nullptr;
|
|
}
|
|
|
|
qDebug() << "[INFO] " << " delete success!";
|
|
}
|
|
//从类中读数据到实例对象
|
|
bool Hd_CameraModule_HIK_hard_PCIE::readData(std::vector<cv::Mat>& mat, QByteArray& data)
|
|
{
|
|
if (data == "getStatus") {
|
|
data = lastError.toUtf8();
|
|
return true;
|
|
}
|
|
else if (data == "clearCache") {
|
|
queuepic.clear();
|
|
return true;
|
|
}
|
|
if (queuepic.isEmpty())
|
|
{
|
|
return false;
|
|
}
|
|
QMutexLocker locker(n_mutex);
|
|
cv::Mat img = queuepic.dequeue();
|
|
mat.push_back(img);
|
|
|
|
qDebug() << "[INFO] " << __FUNCTION__ << " line:" << __LINE__ << " checkImg readData--CameraName:" << QString::fromStdString(Username) << "--imgIndex : " << imgIndex;
|
|
return true;
|
|
}
|
|
|
|
//实例对象把数据写入到类;加载模型后会运行此函数
|
|
bool Hd_CameraModule_HIK_hard_PCIE::writeData(std::vector<cv::Mat>& mat, QByteArray& data)
|
|
{
|
|
Q_UNUSED(mat);
|
|
if (data.contains("deviceList")) {
|
|
auto result = QByteArrayToMV_GENTL_IF_INFO_LIST(data.mid(10));
|
|
if (result.nInterfaceNum != 0) {
|
|
m_stIFList = result;
|
|
return true;
|
|
}
|
|
}
|
|
if (data == "getStatus") {
|
|
data = lastError.toUtf8();
|
|
return true;
|
|
}
|
|
if (data == "clearCache") {
|
|
QMutexLocker locker(n_mutex);
|
|
queuepic.clear();
|
|
return true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Hd_CameraModule_HIK_hard_PCIE::initParas(QByteArray& initData)
|
|
{
|
|
QByteArrayList valueList = initData.split('#');
|
|
if (valueList.size() < 3) {
|
|
qDebug() << "[Error] " << " Hd_CameraModule_HIK initParas fail--params error!参数数量不满足";
|
|
lastError = "参数数量不满足";
|
|
return false;
|
|
}
|
|
Username = valueList[0];
|
|
|
|
auto triggerModeStr = valueList[1];
|
|
if (triggerModeStr == "HARDWARE")
|
|
triggerMode = HARDWARE;
|
|
else if (triggerModeStr == "SOFTWARE")
|
|
triggerMode = SOFTWARE;
|
|
else if (triggerModeStr == "EMPTY")
|
|
triggerMode = EMPTY;
|
|
else {
|
|
qDebug() << "[Error] " << " Hd_CameraModule_HIK initParas fail--params error!触发模式参数错误";
|
|
lastError = "触发模式参数错误";
|
|
return false;
|
|
}
|
|
|
|
getImageTimeOut = valueList[2].toInt();
|
|
|
|
imgIndex = 0;
|
|
return connectDevice();
|
|
}
|
|
|
|
bool Hd_CameraModule_HIK_hard_PCIE::run()
|
|
{
|
|
qDebug() << "[INFO] " << " run start" << QString::fromStdString(Username);
|
|
if (triggerMode == SOFTWARE)
|
|
MV_CC_SetCommandValue(handle, "TriggerSoftware");
|
|
try
|
|
{
|
|
int sleepMs = 10;
|
|
int cnt = 0;
|
|
int timeoutCnt = getImageTimeOut / sleepMs;
|
|
while (true)
|
|
{
|
|
QMutexLocker locker(n_mutex);
|
|
if (queuepic.size() > 0)return true;
|
|
if (cnt++ > timeoutCnt) {
|
|
queuepic.push_back(cv::Mat(200, 200, CV_8UC1, cv::Scalar(0))); //返回空图
|
|
qDebug() << QString::fromStdString(Username) << "timeout";
|
|
return true;
|
|
}
|
|
QThread::msleep(sleepMs);
|
|
}
|
|
}
|
|
catch (QString ev)
|
|
{
|
|
qDebug() << "[Error] " << " ev:" << ev;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
//相机连接状态:正常连接或断开
|
|
bool Hd_CameraModule_HIK_hard_PCIE::checkStatus()
|
|
{
|
|
//status = moduleStatus;
|
|
return status;
|
|
}
|
|
|
|
Hd_CameraModule_HIK_hard_PCIE* create()
|
|
{
|
|
qDebug() << "[INFO] " << "Hd_CameraModule_HIK create success!";
|
|
return new Hd_CameraModule_HIK_hard_PCIE;
|
|
}
|
|
|
|
void destory(Hd_CameraModule_HIK_hard_PCIE* ptr)
|
|
{
|
|
if (ptr)
|
|
{
|
|
delete ptr;
|
|
ptr = nullptr;
|
|
}
|
|
qDebug() << "[INFO] " << "Hd_CameraModule_HIK destory success!";
|
|
} |