#include "Hd_CameraModule_HIK_PCIE.h" using namespace std; #include #include #include #include #include #include enum statusEnum { CONNECTED, RECONNECTING, DISCONNECTED }; enum triggerModeEnum { SOFTWARE, HARDWARE, EMPTY }; std::atomic cameraStatus(DISCONNECTED); std::atomic 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 queuepic; QMutex* n_mutex = new QMutex(); class callbackCollectTimeoutTimerClass : public QThread { public: void run() { runFlag.store(true); counter = 0; while (runFlag.load() && counter < interval.load()) { QThread::msleep(ontTimeSleepInterval); counter += ontTimeSleepInterval; } if (runFlag.load() && counter >= interval.load()) timeoutAction(); } void timeoutAction() { QMutexLocker locker(n_mutex); if (queuepic.size() > 0) { queuepic.clear(); qDebug() << QString::fromStdString(Username) << "上次回调后未取图,已因超时清除缓存,interval" << interval; std::cout << Username << "上次回调后未取图,已因超时清除缓存,interval" << interval << std::endl; } } void setInterval(int msec) { interval.store(msec); } void stop() { runFlag.store(false); } private: int counter = 0; std::atomic runFlag = false; std::atomic interval = 1500; int ontTimeSleepInterval = 10; }; callbackCollectTimeoutTimerClass callbackCollectTimeoutTimer; 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((LPCSTR)name); if (Username == userName) { return i; } } } if (MV_OK != nRet) { return -1; } return -1; } bool connectDevice() { int cameraIndex = SearchDevice(); if (cameraIndex == -1) { //未匹配到相机 qDebug() << "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); } int nRet = MV_CC_CreateHandleByGenTL(&handle, m_stDevList.pDeviceInfo[cameraIndex]); if (MV_OK != nRet) { handle = NULL; lastError = QString("MV_CC_CreateHandleByGenTL failed, nRet=0x%1").arg(QString::number(nRet, 16)); return false; } 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() << "exposureValue:" << exposureValue; MV_CC_SetEnumValue(handle, "ExposureAuto", MV_EXPOSURE_AUTO_MODE_OFF); MV_CC_SetFloatValue(handle, "ExposureTime", exposureValue); } void SetGain(float GainValue) { qDebug() << "GainValue:" << GainValue; MV_CC_SetEnumValue(handle, "GainAuto", 0); MV_CC_SetFloatValue(handle, "Gain", GainValue); } void __stdcall ReconnectDevice(unsigned int nMsgType, void* pUser) { qDebug() << " MV camera disconnects!"; if (nMsgType == MV_EXCEPTION_DEV_DISCONNECT && cameraStatus.load() == CONNECTED) { // 防止重复启动重连线程 bool expected = false; if (!reconnecting.compare_exchange_strong(expected, true)) { qDebug() << "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() << "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) callbackCollectTimeoutTimer.stop(); 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); } callbackCollectTimeoutTimer.start(); queuepic.push_back(srcImage); pData0 = { 0 }; double time_End = (double)clock(); qDebug() << __FUNCTION__ << " line:" << __LINE__ << " checkImg getImg,Time:" << (time_End - time_Start) << "ms" << "--CameraName:" << QString::fromStdString(Username) << " imgIndex:" << imgIndex << "nFrameNum : " << frameNum; } //关闭设备 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() { } Hd_CameraModule_HIK_hard_PCIE::~Hd_CameraModule_HIK_hard_PCIE() { runFlag = false; callbackCollectTimeoutTimer.stop(); CloseDevice(); if (n_mutex) { delete n_mutex; n_mutex = nullptr; } qDebug() << " delete success!"; } //从类中读数据到实例对象 bool Hd_CameraModule_HIK_hard_PCIE::readData(std::vector& mat, QByteArray& data) { if (data == "getStatus") { data = lastError.toUtf8(); return true; } else if (data == "clearCache") { queuepic.clear(); return true; } if (queuepic.isEmpty()) { return false; } callbackCollectTimeoutTimer.stop(); QMutexLocker locker(n_mutex); cv::Mat img = queuepic.dequeue(); mat.push_back(img); qDebug() << __FUNCTION__ << " line:" << __LINE__ << " checkImg readData--CameraName:" << QString::fromStdString(Username) << "--imgIndex : " << imgIndex; return true; } //实例对象把数据写入到类;加载模型后会运行此函数 bool Hd_CameraModule_HIK_hard_PCIE::writeData(std::vector& 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() << " 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() << " 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() << " 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() << " ev:" << ev; } return true; } //相机连接状态:正常连接或断开 bool Hd_CameraModule_HIK_hard_PCIE::checkStatus() { //status = moduleStatus; return status; } Hd_CameraModule_HIK_hard_PCIE* create() { qDebug() << "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() << "Hd_CameraModule_HIK destory success!"; }