#include "MvsDevice.h" #include "MvsSDKManager.h" #include "VrError.h" #include "VrLog.h" #include #include #include namespace { constexpr unsigned int kUsbImageNodeNum = 1; constexpr unsigned int kUsbTransferSize = 0x40000; // 256KB, reduce per-URB DMA pressure on ARM USB2 constexpr unsigned int kUsbTransferWays = 1; constexpr float kUsbOpenFrameRate = 2.0f; bool IsUsbDeviceType(unsigned int type) { return type == MV_USB_DEVICE || type == MV_VIR_USB_DEVICE; } void ApplyConservativeUsbStreamConfig(void* handle, const char* stage) { if (!handle) return; int frameEnableRet = MV_CC_SetBoolValue(handle, "AcquisitionFrameRateEnable", true); int nodeRet = MV_CC_SetImageNodeNum(handle, kUsbImageNodeNum); int sizeRet = MV_USB_SetTransferSize(handle, kUsbTransferSize); int waysRet = MV_USB_SetTransferWays(handle, kUsbTransferWays); MVCC_FLOATVALUE frameRateValue; memset(&frameRateValue, 0, sizeof(frameRateValue)); int frameGetRet = MV_CC_GetFloatValue(handle, "AcquisitionFrameRate", &frameRateValue); unsigned int readSize = 0; unsigned int readWays = 0; int getSizeRet = MV_USB_GetTransferSize(handle, &readSize); int getWaysRet = MV_USB_GetTransferWays(handle, &readWays); LOG_DEBUG("[MVS] USB stream config(%s) fps=%.1f enableRet=0x%x fpsGetRet=0x%x node=%u ret=0x%x transferSize=0x%x ret=0x%x read=0x%x readRet=0x%x transferWays=%u ret=0x%x read=%u readRet=0x%x\n", stage ? stage : "", frameRateValue.fCurValue, frameEnableRet, frameGetRet, kUsbImageNodeNum, nodeRet, kUsbTransferSize, sizeRet, readSize, getSizeRet, kUsbTransferWays, waysRet, readWays, getWaysRet); } } // 工厂 int IMvsDevice::CreateObject(IMvsDevice** ppDevice) { if (ppDevice == nullptr) { return ERR_CODE(DEV_ARG_INVAILD); } try { *ppDevice = new CMvsDevice(); return SUCCESS; } catch (...) { return ERR_CODE(DATA_ERR_MEM); } } CMvsDevice::CMvsDevice() = default; CMvsDevice::~CMvsDevice() { try { if (m_bAcquisitioning) StopAcquisition(); if (m_bDeviceOpen) CloseDevice(); if (m_bSDKInitialized) UninitSDK(); } catch (...) {} } int CMvsDevice::InitSDK() { if (m_bSDKInitialized) return SUCCESS; int ret = MvsSDKManager::GetInstance().InitSDK(); if (ret == SUCCESS) m_bSDKInitialized = true; return ret; } int CMvsDevice::UninitSDK() { if (!m_bSDKInitialized) return SUCCESS; int ret = MvsSDKManager::GetInstance().UninitSDK(); if (ret == SUCCESS) m_bSDKInitialized = false; return ret; } int CMvsDevice::GetSDKVersion(std::string& version) { unsigned int v = MV_CC_GetSDKVersion(); char buf[64] = {0}; snprintf(buf, sizeof(buf), "MVS %u.%u.%u.%u", (v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF); version = buf; return SUCCESS; } void CMvsDevice::FillDeviceInfo(const MV_CC_DEVICE_INFO& src, MvsDeviceInfo& dst) { if (src.nTLayerType == MV_GIGE_DEVICE || src.nTLayerType == MV_VIR_GIGE_DEVICE || src.nTLayerType == MV_GENTL_GIGE_DEVICE) { const auto& gi = src.SpecialInfo.stGigEInfo; dst.tlType = EMvsTLType::GigE; dst.serialNumber = reinterpret_cast(gi.chSerialNumber); dst.modelName = reinterpret_cast(gi.chModelName); dst.manufacturerName = reinterpret_cast(gi.chManufacturerName); dst.deviceVersion = reinterpret_cast(gi.chDeviceVersion); dst.userDefinedName = reinterpret_cast(gi.chUserDefinedName); // IP 拼接 unsigned int ip = gi.nCurrentIp; char ipBuf[32] = {0}; snprintf(ipBuf, sizeof(ipBuf), "%u.%u.%u.%u", (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF); dst.ipAddress = ipBuf; dst.macAddress.clear(); } else if (src.nTLayerType == MV_USB_DEVICE || src.nTLayerType == MV_VIR_USB_DEVICE) { const auto& ui = src.SpecialInfo.stUsb3VInfo; dst.tlType = EMvsTLType::USB3; dst.serialNumber = reinterpret_cast(ui.chSerialNumber); dst.modelName = reinterpret_cast(ui.chModelName); dst.manufacturerName = reinterpret_cast(ui.chManufacturerName); dst.deviceVersion = reinterpret_cast(ui.chDeviceVersion); dst.userDefinedName = reinterpret_cast(ui.chUserDefinedName); dst.ipAddress.clear(); dst.macAddress.clear(); } else { dst.tlType = EMvsTLType::Other; } dst.displayName = dst.userDefinedName.empty() ? (dst.modelName + " (" + dst.serialNumber + ")") : dst.userDefinedName; } int CMvsDevice::EnumerateDevices(std::vector& deviceList, unsigned int tLayerType) { if (!m_bSDKInitialized) { int r = InitSDK(); if (r != SUCCESS) return r; } deviceList.clear(); m_devInfoCache.clear(); unsigned int layer = tLayerType == 0 ? (MV_GIGE_DEVICE | MV_USB_DEVICE) : tLayerType; MV_CC_DEVICE_INFO_LIST stDeviceList; memset(&stDeviceList, 0, sizeof(stDeviceList)); int ret = MV_CC_EnumDevices(layer, &stDeviceList); if (ret != MV_OK) { LOG_DEBUG("MV_CC_EnumDevices fail: %x\n", ret); return ret; } for (unsigned int i = 0; i < stDeviceList.nDeviceNum; ++i) { MV_CC_DEVICE_INFO* p = stDeviceList.pDeviceInfo[i]; if (!p) continue; m_devInfoCache.push_back(*p); MvsDeviceInfo info; FillDeviceInfo(*p, info); deviceList.push_back(info); } LOG_DEBUG("MVS Enumerated %u devices\n", static_cast(deviceList.size())); return SUCCESS; } int CMvsDevice::OpenDevice(const std::string& serialNumber) { std::lock_guard lk(m_deviceMutex); if (!m_bSDKInitialized) { int r = InitSDK(); if (r != SUCCESS) return r; } // 如果缓存为空(外部直接通过 SN 打开),先枚举一次 if (m_devInfoCache.empty()) { std::vector tmp; int r = EnumerateDevices(tmp); if (r != SUCCESS) return r; } // 查找匹配的 SN MV_CC_DEVICE_INFO* pTarget = nullptr; bool targetIsUsb = false; for (auto& dev : m_devInfoCache) { const char* sn = nullptr; if (dev.nTLayerType == MV_GIGE_DEVICE || dev.nTLayerType == MV_VIR_GIGE_DEVICE || dev.nTLayerType == MV_GENTL_GIGE_DEVICE) { sn = reinterpret_cast(dev.SpecialInfo.stGigEInfo.chSerialNumber); } else if (dev.nTLayerType == MV_USB_DEVICE || dev.nTLayerType == MV_VIR_USB_DEVICE) { sn = reinterpret_cast(dev.SpecialInfo.stUsb3VInfo.chSerialNumber); } if (sn && serialNumber == sn) { pTarget = &dev; targetIsUsb = IsUsbDeviceType(dev.nTLayerType); break; } } if (!pTarget) return ERR_CODE(DEV_NOT_FIND); if (m_bDeviceOpen) CloseDevice(); int ret = MV_CC_CreateHandle(&m_handle, pTarget); if (ret != MV_OK) { LOG_DEBUG("MV_CC_CreateHandle fail: %x\n", ret); return ret; } ret = MV_CC_OpenDevice(m_handle); if (ret != MV_OK) { LOG_DEBUG("MV_CC_OpenDevice fail: %x\n", ret); MV_CC_DestroyHandle(m_handle); m_handle = nullptr; return ret; } // GigE 设备建议优化包大小 if (pTarget->nTLayerType == MV_GIGE_DEVICE) { int nPacketSize = MV_CC_GetOptimalPacketSize(m_handle); if (nPacketSize > 0) { MV_CC_SetIntValueEx(m_handle, "GevSCPSPacketSize", nPacketSize); } } // USB 设备优化带宽(降低传输负载,避免资源申请失败) if (pTarget->nTLayerType == MV_USB_DEVICE || pTarget->nTLayerType == MV_VIR_USB_DEVICE) { // 降低 USB 传输包大小,减少带宽占用 MV_CC_SetIntValueEx(m_handle, "PayloadSize", 0); // 自动计算 // 设置合理的帧率上限(避免带宽不足) MV_CC_SetBoolValue(m_handle, "AcquisitionFrameRateEnable", true); MV_CC_SetFloatValue(m_handle, "AcquisitionFrameRate", kUsbOpenFrameRate); } // 默认关闭触发模式 MV_CC_SetEnumValue(m_handle, "TriggerMode", MV_TRIGGER_MODE_OFF); m_currentDeviceIsUsb = targetIsUsb; if (m_currentDeviceIsUsb) ApplyConservativeUsbStreamConfig(m_handle, "open-final"); FillDeviceInfo(*pTarget, m_currentDeviceInfo); m_bDeviceOpen = true; return SUCCESS; } int CMvsDevice::OpenDeviceByIndex(unsigned int index) { std::vector list; int r = EnumerateDevices(list); if (r != SUCCESS) return r; if (index >= list.size()) return ERR_CODE(DEV_ID_ERR); return OpenDevice(list[index].serialNumber); } int CMvsDevice::CloseDevice() { std::lock_guard lk(m_deviceMutex); if (m_bAcquisitioning) StopAcquisition(); if (m_handle) { MV_CC_CloseDevice(m_handle); MV_CC_DestroyHandle(m_handle); m_handle = nullptr; } m_bDeviceOpen = false; m_currentDeviceIsUsb = false; return SUCCESS; } bool CMvsDevice::IsDeviceOpen() { return m_bDeviceOpen; } int CMvsDevice::GetDeviceInfo(MvsDeviceInfo& info) { if (!m_bDeviceOpen) return ERR_CODE(DEV_NO_OPEN); info = m_currentDeviceInfo; GetWidth(info.width); GetHeight(info.height); return SUCCESS; } int CMvsDevice::SetTriggerMode(bool enable) { if (!m_handle) return ERR_CODE(DEV_CTRL_ERR); return MV_CC_SetEnumValue(m_handle, "TriggerMode", enable ? MV_TRIGGER_MODE_ON : MV_TRIGGER_MODE_OFF); } int CMvsDevice::SendSoftTrigger() { if (!m_handle) return ERR_CODE(DEV_CTRL_ERR); return MV_CC_SetCommandValue(m_handle, "TriggerSoftware"); } int CMvsDevice::StartAcquisition() { if (!m_handle) return ERR_CODE(DEV_CTRL_ERR); if (m_bAcquisitioning) return SUCCESS; if (m_currentDeviceIsUsb) ApplyConservativeUsbStreamConfig(m_handle, "start"); int ret = MV_CC_StartGrabbing(m_handle); if (ret == MV_OK) { m_bAcquisitioning = true; } else { LOG_DEBUG("MV_CC_StartGrabbing fail: %x\n", ret); // 针对资源申请失败,输出详细诊断信息 if (ret == 0x80000006) // MV_E_RESOURCE { LOG_ERROR("USB resource allocation failed. Possible causes:\n"); LOG_ERROR(" 1. Insufficient USB bandwidth (try lower resolution/framerate)\n"); LOG_ERROR(" 2. USB controller overload (disconnect other USB devices)\n"); LOG_ERROR(" 3. Insufficient system memory\n"); LOG_ERROR(" 4. USB power supply insufficient\n"); // 尝试读取当前相机参数 MVCC_INTVALUE_EX stParam; memset(&stParam, 0, sizeof(stParam)); if (MV_CC_GetIntValueEx(m_handle, "Width", &stParam) == MV_OK) { LOG_ERROR(" Current Width: %lld\n", stParam.nCurValue); } memset(&stParam, 0, sizeof(stParam)); if (MV_CC_GetIntValueEx(m_handle, "Height", &stParam) == MV_OK) { LOG_ERROR(" Current Height: %lld\n", stParam.nCurValue); } MVCC_FLOATVALUE stFloatParam; memset(&stFloatParam, 0, sizeof(stFloatParam)); if (MV_CC_GetFloatValue(m_handle, "AcquisitionFrameRate", &stFloatParam) == MV_OK) { LOG_ERROR(" Current FrameRate: %.2f\n", stFloatParam.fCurValue); } } } return ret; } int CMvsDevice::StopAcquisition() { if (!m_handle) return ERR_CODE(DEV_CTRL_ERR); if (!m_bAcquisitioning) return SUCCESS; int ret = MV_CC_StopGrabbing(m_handle); if (ret == MV_OK) m_bAcquisitioning = false; else LOG_DEBUG("MV_CC_StopGrabbing fail: %x\n", ret); return ret; } bool CMvsDevice::IsAcquisitioning() { return m_bAcquisitioning; } int CMvsDevice::CaptureImage(MvsImageData& image, unsigned int timeoutMs) { if (!m_handle) return ERR_CODE(DEV_CTRL_ERR); MV_FRAME_OUT stFrame; memset(&stFrame, 0, sizeof(stFrame)); int ret = MV_CC_GetImageBuffer(m_handle, &stFrame, timeoutMs); if (ret != MV_OK) { LOG_DEBUG("MV_CC_GetImageBuffer fail: %x\n", ret); return ret; } image.width = stFrame.stFrameInfo.nExtendWidth ? stFrame.stFrameInfo.nExtendWidth : stFrame.stFrameInfo.nWidth; image.height = stFrame.stFrameInfo.nExtendHeight ? stFrame.stFrameInfo.nExtendHeight : stFrame.stFrameInfo.nHeight; image.dataSize = stFrame.stFrameInfo.nFrameLen; image.pixelFormat = static_cast(stFrame.stFrameInfo.enPixelType); image.frameID = stFrame.stFrameInfo.nFrameNum; image.timestamp = (static_cast(stFrame.stFrameInfo.nDevTimeStampHigh) << 32) | stFrame.stFrameInfo.nDevTimeStampLow; image.pData = new unsigned char[image.dataSize]; memcpy(image.pData, stFrame.pBufAddr, image.dataSize); MV_CC_FreeImageBuffer(m_handle, &stFrame); return SUCCESS; } int CMvsDevice::RegisterImageCallback(MvsImageCallback callback) { if (!m_handle) return ERR_CODE(DEV_CTRL_ERR); { std::lock_guard lk(m_callbackMutex); m_imageCallback = std::move(callback); } int ret = MV_CC_RegisterImageCallBackEx(m_handle, &CMvsDevice::OnFrameCallback, this); LOG_DEBUG("[MVS] RegisterImageCallback handle=%p ret=0x%x\n", (void*)m_handle, ret); return ret; } int CMvsDevice::UnregisterImageCallback() { if (!m_handle) return SUCCESS; // MVS 没有显式反注册,使用空回调覆盖 MV_CC_RegisterImageCallBackEx(m_handle, nullptr, nullptr); std::lock_guard lk(m_callbackMutex); m_imageCallback = nullptr; return SUCCESS; } void __stdcall CMvsDevice::OnFrameCallback(unsigned char* pData, MV_FRAME_OUT_INFO_EX* pFrameInfo, void* pUser) { if (!pData || !pFrameInfo || !pUser) return; CMvsDevice* self = static_cast(pUser); MvsImageCallback cb; { std::lock_guard lk(self->m_callbackMutex); cb = self->m_imageCallback; } if (!cb) return; MvsImageData img; img.width = pFrameInfo->nExtendWidth ? pFrameInfo->nExtendWidth : pFrameInfo->nWidth; img.height = pFrameInfo->nExtendHeight ? pFrameInfo->nExtendHeight : pFrameInfo->nHeight; img.dataSize = pFrameInfo->nFrameLen; img.pixelFormat = static_cast(pFrameInfo->enPixelType); img.frameID = pFrameInfo->nFrameNum; img.timestamp = (static_cast(pFrameInfo->nDevTimeStampHigh) << 32) | pFrameInfo->nDevTimeStampLow; // 回调期间 pData 由 SDK 持有,外部不要 delete img.pData = pData; try { cb(img); } catch (...) {} } int CMvsDevice::GetWidth(unsigned int& width) { if (!m_handle) return ERR_CODE(DEV_CTRL_ERR); MVCC_INTVALUE_EX v; memset(&v, 0, sizeof(v)); int ret = MV_CC_GetIntValueEx(m_handle, "Width", &v); if (ret == MV_OK) width = static_cast(v.nCurValue); return ret; } int CMvsDevice::GetHeight(unsigned int& height) { if (!m_handle) return ERR_CODE(DEV_CTRL_ERR); MVCC_INTVALUE_EX v; memset(&v, 0, sizeof(v)); int ret = MV_CC_GetIntValueEx(m_handle, "Height", &v); if (ret == MV_OK) height = static_cast(v.nCurValue); return ret; } int CMvsDevice::SetROI(const MvsROI& roi) { if (!m_handle) return ERR_CODE(DEV_CTRL_ERR); int r = 0; r |= MV_CC_SetIntValueEx(m_handle, "OffsetX", roi.offsetX); r |= MV_CC_SetIntValueEx(m_handle, "OffsetY", roi.offsetY); r |= MV_CC_SetIntValueEx(m_handle, "Width", roi.width); r |= MV_CC_SetIntValueEx(m_handle, "Height", roi.height); return r == 0 ? SUCCESS : ERR_CODE(DEV_CTRL_ERR); } int CMvsDevice::GetROI(MvsROI& roi) { if (!m_handle) return ERR_CODE(DEV_CTRL_ERR); MVCC_INTVALUE_EX v; memset(&v, 0, sizeof(v)); MV_CC_GetIntValueEx(m_handle, "OffsetX", &v); roi.offsetX = (int)v.nCurValue; memset(&v, 0, sizeof(v)); MV_CC_GetIntValueEx(m_handle, "OffsetY", &v); roi.offsetY = (int)v.nCurValue; memset(&v, 0, sizeof(v)); MV_CC_GetIntValueEx(m_handle, "Width", &v); roi.width = (int)v.nCurValue; memset(&v, 0, sizeof(v)); MV_CC_GetIntValueEx(m_handle, "Height", &v); roi.height = (int)v.nCurValue; return SUCCESS; } int CMvsDevice::SetExposureTime(double v) { return m_handle ? MV_CC_SetFloatValue(m_handle, "ExposureTime", (float)v) : ERR_CODE(DEV_CTRL_ERR); } int CMvsDevice::GetExposureTime(double& v) { if (!m_handle) return ERR_CODE(DEV_CTRL_ERR); MVCC_FLOATVALUE fv; memset(&fv, 0, sizeof(fv)); int r = MV_CC_GetFloatValue(m_handle, "ExposureTime", &fv); if (r == MV_OK) v = fv.fCurValue; return r; } int CMvsDevice::SetGain(double v) { return m_handle ? MV_CC_SetFloatValue(m_handle, "Gain", (float)v) : ERR_CODE(DEV_CTRL_ERR); } int CMvsDevice::GetGain(double& v) { if (!m_handle) return ERR_CODE(DEV_CTRL_ERR); MVCC_FLOATVALUE fv; memset(&fv, 0, sizeof(fv)); int r = MV_CC_GetFloatValue(m_handle, "Gain", &fv); if (r == MV_OK) v = fv.fCurValue; return r; } int CMvsDevice::SetFrameRate(double v) { if (!m_handle) return ERR_CODE(DEV_CTRL_ERR); MV_CC_SetBoolValue(m_handle, "AcquisitionFrameRateEnable", true); return MV_CC_SetFloatValue(m_handle, "AcquisitionFrameRate", (float)v); } int CMvsDevice::GetFrameRate(double& v) { if (!m_handle) return ERR_CODE(DEV_CTRL_ERR); MVCC_FLOATVALUE fv; memset(&fv, 0, sizeof(fv)); int r = MV_CC_GetFloatValue(m_handle, "AcquisitionFrameRate", &fv); if (r == MV_OK) v = fv.fCurValue; return r; } int CMvsDevice::SetIntFeature(const std::string& name, int64_t v) { return m_handle ? MV_CC_SetIntValueEx(m_handle, name.c_str(), v) : ERR_CODE(DEV_CTRL_ERR); } int CMvsDevice::GetIntFeature(const std::string& name, int64_t& v) { if (!m_handle) return ERR_CODE(DEV_CTRL_ERR); MVCC_INTVALUE_EX iv; memset(&iv, 0, sizeof(iv)); int r = MV_CC_GetIntValueEx(m_handle, name.c_str(), &iv); if (r == MV_OK) v = iv.nCurValue; return r; } int CMvsDevice::SetFloatFeature(const std::string& name, double v) { return m_handle ? MV_CC_SetFloatValue(m_handle, name.c_str(), (float)v) : ERR_CODE(DEV_CTRL_ERR); } int CMvsDevice::GetFloatFeature(const std::string& name, double& v) { if (!m_handle) return ERR_CODE(DEV_CTRL_ERR); MVCC_FLOATVALUE fv; memset(&fv, 0, sizeof(fv)); int r = MV_CC_GetFloatValue(m_handle, name.c_str(), &fv); if (r == MV_OK) v = fv.fCurValue; return r; } int CMvsDevice::SetEnumFeature(const std::string& name, int64_t v) { return m_handle ? MV_CC_SetEnumValue(m_handle, name.c_str(), (unsigned int)v) : ERR_CODE(DEV_CTRL_ERR); } int CMvsDevice::GetEnumFeature(const std::string& name, int64_t& v) { if (!m_handle) return ERR_CODE(DEV_CTRL_ERR); MVCC_ENUMVALUE ev; memset(&ev, 0, sizeof(ev)); int r = MV_CC_GetEnumValue(m_handle, name.c_str(), &ev); if (r == MV_OK) v = ev.nCurValue; return r; } int CMvsDevice::SetBoolFeature(const std::string& name, bool v) { return m_handle ? MV_CC_SetBoolValue(m_handle, name.c_str(), v) : ERR_CODE(DEV_CTRL_ERR); } int CMvsDevice::GetBoolFeature(const std::string& name, bool& v) { if (!m_handle) return ERR_CODE(DEV_CTRL_ERR); bool bv = false; int r = MV_CC_GetBoolValue(m_handle, name.c_str(), &bv); if (r == MV_OK) v = bv; return r; } int CMvsDevice::SetStringFeature(const std::string& name, const std::string& v) { return m_handle ? MV_CC_SetStringValue(m_handle, name.c_str(), v.c_str()) : ERR_CODE(DEV_CTRL_ERR); } int CMvsDevice::GetStringFeature(const std::string& name, std::string& v) { if (!m_handle) return ERR_CODE(DEV_CTRL_ERR); MVCC_STRINGVALUE sv; memset(&sv, 0, sizeof(sv)); int r = MV_CC_GetStringValue(m_handle, name.c_str(), &sv); if (r == MV_OK) v = sv.chCurValue; return r; } int CMvsDevice::ExecuteCommand(const std::string& name) { return m_handle ? MV_CC_SetCommandValue(m_handle, name.c_str()) : ERR_CODE(DEV_CTRL_ERR); }