2026-07-14 17:14:55 +08:00

1309 lines
42 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "ParkingSpaceGuidePresenter.h"
#include <algorithm>
#include <chrono>
#include <cstring>
#include <exception>
#include <fstream>
#include <QDateTime>
#include <QJsonDocument>
#include <QJsonObject>
#include "DetectPresenter.h"
#include "IYUDPClient.h"
#include "ParkingStatusHttpServer.h"
#include "PathManager.h"
#include "Version.h"
#include "VrLog.h"
namespace {
constexpr int kMvsPixelTypeMono8 = 0x01080001;
constexpr int kMvsPixelTypeRGB8 = 0x02180014;
constexpr int kMvsPixelTypeBGR8 = 0x02180015;
constexpr int kMvsAcqModeContinuous = 2;
constexpr int kAirplaneProbeIntervalMs = 1000;
constexpr unsigned int kModelCaptureTimeoutMs = 1000;
QString ConfigText(const std::string& value, const QString& fallback)
{
const QString text = QString::fromStdString(value).trimmed();
return text.isEmpty() ? fallback : text;
}
QString ResultText(const QString& value, const QString& fallback)
{
const QString text = value.trimmed();
return text.isEmpty() ? fallback : text;
}
ParkingSpaceGuideInfo FallbackGuideInfo(const UdpBroadcastConfig& config)
{
ParkingSpaceGuideInfo info;
info.targetId = ConfigText(config.targetId, QStringLiteral("T001"));
info.parkId = ConfigText(config.parkId, QStringLiteral("P01"));
info.modelType = QStringLiteral("未知");
return info;
}
bool IsExceptionGuideState(int guideStateCode)
{
switch (guideStateCode) {
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
case 18:
case 19:
case 20:
case 21:
case 22:
case 23:
case 24:
return true;
default:
return false;
}
}
QJsonObject BuildGuideInfoJson(const ParkingSpaceGuideInfo& info,
const UdpBroadcastConfig& config)
{
const ParkingSpaceGuideInfo fallback = FallbackGuideInfo(config);
QJsonObject object;
object["targetId"] = ResultText(info.targetId, fallback.targetId);
object["parkId"] = ResultText(info.parkId, fallback.parkId);
object["modelType"] = ResultText(info.modelType, fallback.modelType);
object["guideStateCode"] = info.guideStateCode;
object["distanceToStop"] = info.distance;
object["bodyYawAngle"] = info.angle;
object["lateralOffset"] = info.lateralOffset;
object["aircraftSpeed"] = info.aircraftSpeed;
object["hasException"] = info.hasException || IsExceptionGuideState(info.guideStateCode);
object["guideText"] = info.guideText;
return object;
}
QJsonObject BuildDetectionResultPayload(const DetectionResult& result,
const UdpBroadcastConfig& config)
{
const bool hasResult = !result.parkingSpaceInfoList.empty();
const bool success = result.errorCode == 0 && hasResult;
const ParkingSpaceGuideInfo firstInfo = hasResult
? result.parkingSpaceInfoList.front()
: FallbackGuideInfo(config);
const QJsonObject firstResult = BuildGuideInfoJson(firstInfo, config);
QJsonObject payload;
payload["protocol"] = QStringLiteral("ParkingSpaceGuideResult");
payload["version"] = QStringLiteral("1.0");
payload["success"] = success;
payload["hasResult"] = hasResult;
payload["targetId"] = firstResult["targetId"];
payload["parkId"] = firstResult["parkId"];
payload["modelType"] = firstResult["modelType"];
payload["guideStateCode"] = firstResult["guideStateCode"];
payload["distanceToStop"] = firstResult["distanceToStop"];
payload["bodyYawAngle"] = firstResult["bodyYawAngle"];
payload["lateralOffset"] = firstResult["lateralOffset"];
payload["aircraftSpeed"] = firstResult["aircraftSpeed"];
payload["hasException"] = result.errorCode != 0 || firstResult["hasException"].toBool();
payload["guideText"] = firstResult["guideText"];
payload["message"] = result.message;
payload["errorCode"] = result.errorCode;
payload["timestamp"] = QDateTime::currentMSecsSinceEpoch();
return payload;
}
} // namespace
ParkingSpaceGuidePresenter::ParkingSpaceGuidePresenter(QObject* parent)
: QObject(parent)
{
}
ParkingSpaceGuidePresenter::~ParkingSpaceGuidePresenter()
{
DeinitApp();
}
int ParkingSpaceGuidePresenter::Init()
{
m_reconfiguring.store(false);
NotifyWorkStatus(WorkStatus::InitIng);
NotifyStatus("停车引导初始化中");
int ret = InitConfig();
if (ret != 0) {
NotifyWorkStatus(WorkStatus::Error);
return ret;
}
const ConfigResult config = m_configManager->GetConfigResult();
m_cameraList.clear();
DetectionResult initialStatus;
initialStatus.errorCode = -1;
initialStatus.message = QStringLiteral("暂无检测结果");
UpdateCurrentStatus(initialStatus);
m_detectPresenter = new DetectPresenter();
ret = InitMvsCamera(config.mvsCamera);
if (ret != 0) {
NotifyStatus("平面相机初始化失败,继续使用预留算法模式");
}
if (m_cameraList.empty()) {
const std::string cameraName = config.mvsCamera.serialNumber.empty()
? std::string("平面相机")
: config.mvsCamera.serialNumber;
m_cameraList.push_back(std::make_pair(cameraName, static_cast<void*>(m_mvsDevice)));
}
ret = InitRsLidar(config.lidarConfig);
if (ret != 0) {
NotifyStatus("雷达初始化失败,继续使用预留算法模式");
}
ret = InitLedDisplay(config.ledDisplayConfig);
if (ret != 0) {
NotifyStatus("显示屏初始化失败");
}
ret = InitUdpBroadcast(config.udpBroadcastConfig);
if (ret != 0) {
NotifyStatus("组播初始化失败");
}
ret = InitHttpServer(config.httpServerConfig);
if (ret != 0) {
NotifyStatus("HTTP状态服务初始化失败");
}
if (StatusCallback()) {
StatusCallback()->OnCameraCountChanged(1);
}
m_initialized = true;
NotifyWorkStatus(WorkStatus::Ready);
NotifyStatus("停车引导初始化完成");
return 0;
}
void ParkingSpaceGuidePresenter::DeinitApp()
{
m_initialized.store(false);
{
std::lock_guard<std::mutex> reconfigurationLock(m_reconfigurationMutex);
m_reconfiguring.store(true);
StopDetection();
CloseDevices();
delete m_detectPresenter;
m_detectPresenter = nullptr;
}
if (m_configManager) {
m_configManager->Shutdown();
delete m_configManager;
m_configManager = nullptr;
}
}
bool ParkingSpaceGuidePresenter::StartDetection(int cameraIndex)
{
std::lock_guard<std::mutex> threadLock(m_detectionThreadMutex);
if (!m_initialized || m_reconfiguring.load() || !m_detectPresenter || !m_configManager) {
NotifyStatus("系统未初始化");
return false;
}
if (!m_lidarDevice || !m_lidarDevice->IsRunning()) {
NotifyStatus("雷达未连接,无法启动实时检测");
return false;
}
if (m_detectionRunning.load()) {
NotifyStatus("停车引导实时检测已在运行");
return true;
}
if (m_detectionThread.joinable()) {
m_detectionThread.join();
}
if (cameraIndex > 0) {
m_detectIndex = cameraIndex;
} else {
m_detectIndex = m_currentCameraIndex;
}
{
std::lock_guard<std::mutex> dataLock(m_dataMutex);
m_consumedCloudSequence = m_cloudSequence;
m_lidarErrorPending = false;
m_lidarErrorMessage.clear();
}
m_trackingMode.store(false);
m_lastProbeAcceptedMs.store(0);
m_stopDetectionRequested.store(false);
m_detectionRunning.store(true);
try {
m_detectionThread = std::thread(&ParkingSpaceGuidePresenter::DetectionLoop, this);
} catch (...) {
m_detectionRunning.store(false);
m_stopDetectionRequested.store(true);
NotifyWorkStatus(WorkStatus::Error);
NotifyStatus("启动停车引导实时检测线程失败");
return false;
}
NotifyWorkStatus(WorkStatus::Working);
NotifyStatus("停车引导实时检测已开始");
return true;
}
bool ParkingSpaceGuidePresenter::TriggerDetection(int cameraIndex)
{
std::lock_guard<std::mutex> threadLock(m_detectionThreadMutex);
if (!m_initialized || m_reconfiguring.load() || !m_detectPresenter || !m_configManager) {
NotifyStatus("系统未初始化");
return false;
}
if (m_detectionRunning.load()) {
NotifyStatus("实时检测运行中,无法执行单次检测");
return false;
}
if (cameraIndex > 0) {
m_detectIndex = cameraIndex;
} else {
m_detectIndex = m_currentCameraIndex;
}
std::shared_ptr<const OwnedCloudFrame> cloudFrame;
{
std::lock_guard<std::mutex> dataLock(m_dataMutex);
cloudFrame = m_latestCloudFrame;
}
if (!cloudFrame) {
NotifyStatus("暂无可用雷达点云");
return false;
}
NotifyWorkStatus(WorkStatus::Detecting);
NotifyStatus("停车引导单次检测已开始");
bool success = false;
ParkingGuideAlgorithmState algorithmState;
try {
success = DetectOnce(cloudFrame->cloud, algorithmState);
} catch (const std::exception& e) {
NotifyStatus(std::string("停车引导单次检测异常:") + e.what());
} catch (...) {
NotifyStatus("停车引导单次检测发生未知异常");
}
NotifyWorkStatus(success ? WorkStatus::Completed : WorkStatus::Error);
NotifyStatus(success ? "停车引导单次检测完成"
: "停车引导单次检测失败");
return success;
}
bool ParkingSpaceGuidePresenter::DetectOnce(const RsCloudData& cloud,
ParkingGuideAlgorithmState& algorithmState)
{
std::lock_guard<std::mutex> executionLock(m_detectionExecutionMutex);
if (!m_detectPresenter || !m_configManager) {
return false;
}
DetectionResult result;
ParkingGuideAlgorithmControl control;
result.cameraIndex = m_detectIndex;
const int ret = m_detectPresenter->DetectParkingSpaceGuide(cloud,
m_configManager->GetAlgorithmParams(),
algorithmState,
result,
control);
result.cameraIndex = m_detectIndex;
if (ret != 0) {
result.errorCode = ret;
if (result.message.isEmpty()) {
result.message = QStringLiteral("停车引导检测失败");
}
} else {
if (result.parkingSpaceInfoList.empty() && result.message.isEmpty()) {
result.message = QStringLiteral("无停车引导结果");
}
if (control.needModelRecognition) {
const auto appendRecognitionFailure = [this, &result, &control](const QString& reason) {
const QString text = reason.trimmed().isEmpty()
? QStringLiteral("机型识别失败")
: reason.trimmed();
NotifyStatus(text.toUtf8().toStdString());
result.message = result.message.trimmed().isEmpty()
? text
: result.message + QStringLiteral("") + text;
if (control.nextState.modelRecognitionAttempts >= 2) {
for (ParkingSpaceGuideInfo& info : result.parkingSpaceInfoList) {
info.hasException = true;
info.guideStateCode = static_cast<int>(
ParkingGuideState::AircraftVerificationFailed);
info.guideText = QStringLiteral("STOP+IDFAIL");
}
}
};
try {
QImage modelImage;
QString captureError;
if (!CaptureModelRecognitionImage(modelImage, captureError)) {
appendRecognitionFailure(captureError);
} else {
result.image = modelImage;
ModelRecognitionResult modelResult;
const int modelRet = m_detectPresenter->RecognizeModel2D(
modelImage,
control.recognitionContext,
modelResult);
const double minimumConfidence =
m_configManager->GetAlgorithmParams().guideParam.confidenceThreshold;
if (modelRet == 0 && modelResult.IsVerified(minimumConfidence)) {
control.nextState.modelType = modelResult.modelType.trimmed();
control.nextState.modelConfidence = modelResult.confidence;
control.nextState.modelVerified = true;
for (ParkingSpaceGuideInfo& info : result.parkingSpaceInfoList) {
info.modelType = control.nextState.modelType;
info.confidence = control.nextState.modelConfidence;
}
} else {
const QString reason = modelResult.message.trimmed().isEmpty()
? QStringLiteral("机型识别结果未通过验证")
: modelResult.message;
appendRecognitionFailure(reason);
}
}
} catch (const std::exception& e) {
appendRecognitionFailure(
QStringLiteral("机型识别异常:%1").arg(QString::fromUtf8(e.what())));
} catch (...) {
appendRecognitionFailure(QStringLiteral("机型识别发生未知异常"));
}
}
}
if (ret == 0) {
algorithmState = control.nextState;
}
PublishDetectionResult(result);
return ret == 0;
}
std::shared_ptr<ParkingSpaceGuidePresenter::OwnedCloudFrame>
ParkingSpaceGuidePresenter::AcquireCloudFrameBuffer()
{
std::lock_guard<std::mutex> dataLock(m_dataMutex);
for (size_t i = 0; i < m_cloudFrameSlots.size(); ++i) {
const size_t slotIndex = (m_nextCloudFrameSlot + i) % m_cloudFrameSlots.size();
std::shared_ptr<OwnedCloudFrame>& slot = m_cloudFrameSlots[slotIndex];
if (!slot) {
slot = std::make_shared<OwnedCloudFrame>();
}
if (slot.use_count() == 1) {
m_nextCloudFrameSlot = (slotIndex + 1) % m_cloudFrameSlots.size();
return slot;
}
}
return std::shared_ptr<OwnedCloudFrame>();
}
bool ParkingSpaceGuidePresenter::CopyCloudFrame(const RsCloudData& cloud,
const RsFrameInfo& info,
OwnedCloudFrame& destination)
{
size_t totalPointCount = 0;
for (const auto& item : cloud) {
const SVzLaserLineData& line = item.second;
if (item.first != keResultDataType_PointXYZI ||
line.nPointCount < 0 ||
(line.nPointCount > 0 && !line.p3DPoint)) {
return false;
}
totalPointCount += static_cast<size_t>(line.nPointCount);
}
destination.points.resize(totalPointCount);
destination.cloud.clear();
destination.cloud.reserve(cloud.size());
destination.info = info;
size_t pointOffset = 0;
for (const auto& item : cloud) {
SVzLaserLineData line = item.second;
line.p2DPoint = nullptr;
if (line.nPointCount > 0) {
SVzNLPointXYZI* target = destination.points.data() + pointOffset;
std::memcpy(target,
line.p3DPoint,
sizeof(SVzNLPointXYZI) * static_cast<size_t>(line.nPointCount));
line.p3DPoint = target;
pointOffset += static_cast<size_t>(line.nPointCount);
} else {
line.p3DPoint = nullptr;
}
destination.cloud.emplace_back(item.first, line);
}
return true;
}
bool ParkingSpaceGuidePresenter::ProcessAirplanePresence(const RsCloudData& cloud)
{
AirplanePresenceResult presence;
const int ret = m_detectPresenter->DetectAirplanePresence(
cloud,
m_configManager->GetAlgorithmParams(),
presence);
if (ret != 0) {
const QString message = presence.message.trimmed().isEmpty()
? QStringLiteral("飞机存在检测失败")
: presence.message.trimmed();
PublishErrorResult(ret, message);
return false;
}
if (presence.state == AirplanePresenceState::Detected) {
NotifyStatus("检测到飞机,进入引导检测");
return true;
}
ParkingGuideState guideState = ParkingGuideState::Unknown;
QString guideText;
if (presence.state == AirplanePresenceState::GateBlocked) {
guideState = ParkingGuideState::GateBlocked;
guideText = QStringLiteral("GATEBLOCK");
} else if (presence.state == AirplanePresenceState::ViewBlocked) {
guideState = ParkingGuideState::ViewBlocked;
guideText = QStringLiteral("VIEWBLOCKED");
} else {
return false;
}
DetectionResult result;
ParkingSpaceGuideInfo info;
info.hasException = true;
info.guideStateCode = static_cast<int>(guideState);
info.guideText = guideText;
result.parkingSpaceInfoList.push_back(info);
result.message = presence.message.trimmed().isEmpty() ? guideText : presence.message.trimmed();
PublishDetectionResult(result);
return false;
}
void ParkingSpaceGuidePresenter::PublishDetectionResult(DetectionResult& result)
{
EnrichDetectionResult(result);
if (StatusCallback()) {
StatusCallback()->OnDetectionResult(result);
}
UpdateCurrentStatus(result);
if (m_ledDisplay) {
m_ledDisplay->SendResult(ConvertLedResult(result));
}
BroadcastDetectionResult(result);
}
void ParkingSpaceGuidePresenter::PublishErrorResult(int errorCode, const QString& message)
{
DetectionResult result;
result.errorCode = errorCode == 0 ? -1 : errorCode;
result.message = message;
PublishDetectionResult(result);
}
bool ParkingSpaceGuidePresenter::CaptureModelRecognitionImage(QImage& image,
QString& errorMessage)
{
image = QImage();
errorMessage.clear();
if (!m_mvsDevice || !m_mvsDevice->IsDeviceOpen() || !m_mvsDevice->IsAcquisitioning()) {
errorMessage = QStringLiteral("机型识别相机不可用,雷达引导继续");
return false;
}
int ret = m_mvsDevice->SendSoftTrigger();
if (ret != 0) {
errorMessage = QStringLiteral("机型识别相机软触发失败:%1").arg(ret);
return false;
}
MvsImageData captured;
ret = m_mvsDevice->CaptureImage(captured, kModelCaptureTimeoutMs);
std::unique_ptr<unsigned char[]> capturedData(captured.pData);
if (ret != 0 || !captured.pData) {
errorMessage = QStringLiteral("获取机型识别图像失败:%1").arg(ret);
return false;
}
if (!CacheMvsFrame(captured, image)) {
errorMessage = QStringLiteral("机型识别图像格式无效");
return false;
}
return true;
}
void ParkingSpaceGuidePresenter::DetectionLoop()
{
ParkingGuideAlgorithmState algorithmState;
while (!m_stopDetectionRequested.load()) {
std::shared_ptr<const OwnedCloudFrame> cloudFrame;
QString lidarError;
{
std::unique_lock<std::mutex> dataLock(m_dataMutex);
m_detectionCondition.wait(dataLock, [this]() {
return m_stopDetectionRequested.load() ||
m_lidarErrorPending ||
m_cloudSequence != m_consumedCloudSequence;
});
if (m_stopDetectionRequested.load()) {
break;
}
if (m_lidarErrorPending) {
lidarError = m_lidarErrorMessage;
m_lidarErrorPending = false;
} else {
cloudFrame = m_latestCloudFrame;
m_consumedCloudSequence = m_cloudSequence;
}
}
if (!lidarError.isEmpty()) {
PublishErrorResult(-1, lidarError);
m_stopDetectionRequested.store(true);
m_detectionRunning.store(false);
m_trackingMode.store(false);
NotifyWorkStatus(WorkStatus::Error);
break;
}
if (!cloudFrame) {
continue;
}
try {
if (!m_trackingMode.load()) {
if (ProcessAirplanePresence(cloudFrame->cloud)) {
m_trackingMode.store(true);
}
continue;
}
DetectOnce(cloudFrame->cloud, algorithmState);
} catch (const std::exception& e) {
PublishErrorResult(-1,
QStringLiteral("停车引导检测异常:%1")
.arg(QString::fromUtf8(e.what())));
} catch (...) {
PublishErrorResult(-1, QStringLiteral("停车引导检测发生未知异常"));
}
}
m_detectionRunning.store(false);
m_trackingMode.store(false);
}
int ParkingSpaceGuidePresenter::StopDetection()
{
std::lock_guard<std::mutex> threadLock(m_detectionThreadMutex);
const bool wasRunning = m_detectionRunning.load();
{
std::lock_guard<std::mutex> dataLock(m_dataMutex);
m_stopDetectionRequested.store(true);
}
m_detectionCondition.notify_all();
if (m_detectionThread.joinable() &&
m_detectionThread.get_id() != std::this_thread::get_id()) {
m_detectionThread.join();
}
m_detectionRunning.store(false);
m_trackingMode.store(false);
m_lastProbeAcceptedMs.store(0);
if (wasRunning) {
NotifyWorkStatus(WorkStatus::Ready);
NotifyStatus("停车引导实时检测已停止");
}
return 0;
}
bool ParkingSpaceGuidePresenter::IsDetectionRunning() const
{
return m_detectionRunning.load();
}
int ParkingSpaceGuidePresenter::LoadAndDetect(const QString& fileName)
{
Q_UNUSED(fileName);
return TriggerDetection(m_currentCameraIndex) ? 0 : -1;
}
int ParkingSpaceGuidePresenter::SaveDetectionDataToFile(const std::string& filePath)
{
std::lock_guard<std::mutex> lock(m_dataMutex);
std::ofstream file(filePath.c_str(), std::ios::out | std::ios::trunc);
if (!file.is_open()) {
return -1;
}
file << "ParkingSpaceGuide cached data\n";
file << "hasFrame=" << (m_hasFrame ? 1 : 0) << "\n";
file << "frameWidth=" << m_latestFrameInfo.width << "\n";
file << "frameHeight=" << m_latestFrameInfo.height << "\n";
file << "hasCloud=" << (m_latestCloudFrame ? 1 : 0) << "\n";
file << "cloudLines=" << (m_latestCloudFrame ? m_latestCloudFrame->cloud.size() : 0) << "\n";
return 0;
}
int ParkingSpaceGuidePresenter::GetDetectionDataCacheSize() const
{
std::lock_guard<std::mutex> lock(m_dataMutex);
return (m_hasFrame ? 1 : 0) + (m_latestCloudFrame ? 1 : 0);
}
void ParkingSpaceGuidePresenter::SetDefaultCameraIndex(int cameraIndex)
{
m_currentCameraIndex = (std::max)(1, cameraIndex);
}
int ParkingSpaceGuidePresenter::GetDetectIndex() const
{
return m_detectIndex;
}
QString ParkingSpaceGuidePresenter::GetAlgoVersion() const
{
return DetectPresenter::GetAlgoVersion();
}
ConfigManager* ParkingSpaceGuidePresenter::GetConfigManager()
{
return m_configManager;
}
std::vector<std::pair<std::string, void*>> ParkingSpaceGuidePresenter::GetCameraList() const
{
return m_cameraList;
}
void ParkingSpaceGuidePresenter::OnConfigChanged(const ConfigResult& configResult)
{
std::lock_guard<std::mutex> reconfigurationLock(m_reconfigurationMutex);
if (!m_initialized) {
return;
}
m_reconfiguring.store(true);
const bool restartDetection = m_detectionRunning.load();
const int detectIndex = m_detectIndex;
StopDetection();
NotifyStatus("配置已变更,正在重新连接设备");
InitMvsCamera(configResult.mvsCamera);
InitRsLidar(configResult.lidarConfig);
InitLedDisplay(configResult.ledDisplayConfig);
InitUdpBroadcast(configResult.udpBroadcastConfig);
InitHttpServer(configResult.httpServerConfig);
m_reconfiguring.store(false);
if (restartDetection) {
StartDetection(detectIndex);
}
}
IYParkingSpaceGuideStatus* ParkingSpaceGuidePresenter::StatusCallback() const
{
return static_cast<IYParkingSpaceGuideStatus*>(m_statusCallback);
}
int ParkingSpaceGuidePresenter::InitConfig()
{
if (!m_configManager) {
m_configManager = new ConfigManager();
}
if (!m_configManager->Initialize(PathManager::GetInstance().GetConfigFilePath().toStdString())) {
NotifyStatus("初始化配置管理器失败");
return -1;
}
ConfigResult config = m_configManager->GetConfigResult();
config.Normalize();
SystemConfig systemConfig = m_configManager->GetConfig();
systemConfig.configResult = config;
m_configManager->UpdateFullConfig(systemConfig);
return 0;
}
int ParkingSpaceGuidePresenter::InitMvsCamera(const MvsCameraConfig& config)
{
auto closeMvsDevice = [this]() {
if (!m_mvsDevice) {
return;
}
m_mvsDevice->StopAcquisition();
m_mvsDevice->UnregisterImageCallback();
m_mvsDevice->CloseDevice();
m_mvsDevice->UninitSDK();
delete m_mvsDevice;
m_mvsDevice = nullptr;
};
closeMvsDevice();
m_cameraList.clear();
{
std::lock_guard<std::mutex> lock(m_dataMutex);
m_latestFrame.clear();
m_latestFrameInfo = CameraFrameInfo();
m_hasFrame = false;
}
if (!config.enabled) {
NotifyCameraStatus(false);
NotifyStatus("平面相机未启用");
m_cameraList.push_back(std::make_pair(std::string("平面相机"), static_cast<void*>(nullptr)));
return 0;
}
if (IMvsDevice::CreateObject(&m_mvsDevice) != 0 || !m_mvsDevice) {
NotifyCameraStatus(false);
NotifyStatus("创建平面相机设备失败");
return -1;
}
int ret = m_mvsDevice->InitSDK();
if (ret != 0) {
NotifyCameraStatus(false);
NotifyStatus("平面相机SDK初始化失败" + std::to_string(ret));
closeMvsDevice();
return ret;
}
std::vector<MvsDeviceInfo> devices;
ret = m_mvsDevice->EnumerateDevices(devices);
if (ret != 0 || devices.empty()) {
NotifyCameraStatus(false);
NotifyStatus(ret != 0
? "搜索平面相机失败:" + std::to_string(ret)
: "未搜索到平面相机");
closeMvsDevice();
return ret != 0 ? ret : -1;
}
NotifyStatus("搜索到平面相机数量:" + std::to_string(devices.size()));
if (!config.serialNumber.empty()) {
ret = m_mvsDevice->OpenDevice(config.serialNumber);
} else {
unsigned int deviceIndex = static_cast<unsigned int>((std::max)(0, config.deviceIndex));
if (deviceIndex >= devices.size()) {
NotifyStatus("相机设备序号超出范围使用第0台设备");
deviceIndex = 0;
}
ret = m_mvsDevice->OpenDeviceByIndex(deviceIndex);
}
if (ret != 0) {
NotifyCameraStatus(false);
NotifyStatus("打开平面相机失败:" + std::to_string(ret));
closeMvsDevice();
return ret;
}
MvsDeviceInfo openedInfo;
std::string cameraName = config.serialNumber.empty() ? std::string("平面相机") : config.serialNumber;
if (m_mvsDevice->GetDeviceInfo(openedInfo) == 0) {
if (!openedInfo.displayName.empty()) {
cameraName = openedInfo.displayName;
} else if (!openedInfo.serialNumber.empty()) {
cameraName = openedInfo.serialNumber;
}
}
ret = m_mvsDevice->SetEnumFeature("AcquisitionMode", kMvsAcqModeContinuous);
if (ret != 0) {
NotifyStatus("设置平面相机连续采集模式失败,继续使用当前模式:" + std::to_string(ret));
}
ret = m_mvsDevice->SetEnumFeature("PixelFormat", kMvsPixelTypeMono8);
if (ret != 0) {
NotifyStatus("设置平面相机灰度格式失败,继续使用当前格式:" + std::to_string(ret));
}
ret = m_mvsDevice->SetTriggerMode(true);
if (ret != 0) {
NotifyCameraStatus(false);
NotifyStatus("启用机型识别相机触发模式失败:" + std::to_string(ret));
closeMvsDevice();
return ret;
}
ret = m_mvsDevice->SetEnumFeatureByString("TriggerSource", "Software");
if (ret != 0) {
NotifyCameraStatus(false);
NotifyStatus("设置机型识别相机软触发源失败:" + std::to_string(ret));
closeMvsDevice();
return ret;
}
ret = m_mvsDevice->StartAcquisition();
if (ret != 0) {
NotifyCameraStatus(false);
NotifyStatus("启动平面相机采集失败:" + std::to_string(ret));
closeMvsDevice();
return ret;
}
NotifyCameraStatus(true);
m_cameraList.push_back(std::make_pair(cameraName, static_cast<void*>(m_mvsDevice)));
NotifyStatus("平面相机已连接:" + cameraName);
return 0;
}
int ParkingSpaceGuidePresenter::InitRsLidar(const RsLidarConfig& config)
{
if (m_lidarDevice) {
m_lidarDevice->Stop();
m_lidarDevice->CloseDevice();
delete m_lidarDevice;
m_lidarDevice = nullptr;
}
{
std::lock_guard<std::mutex> dataLock(m_dataMutex);
m_latestCloudFrame.reset();
for (std::shared_ptr<OwnedCloudFrame>& slot : m_cloudFrameSlots) {
slot.reset();
}
m_nextCloudFrameSlot = 0;
m_cloudSequence = 0;
m_consumedCloudSequence = 0;
m_lidarErrorPending = false;
m_lidarErrorMessage.clear();
}
m_trackingMode.store(false);
m_lastProbeAcceptedMs.store(0);
m_cloudCopyErrorReported.store(false);
if (IRsLidarDevice::CreateObject(&m_lidarDevice) != 0 || !m_lidarDevice) {
NotifyLidarStatus(false);
return -1;
}
m_lidarDevice->SetPointCloudCallback([this](const RsCloudData& cloud, const RsFrameInfo& info) {
if (!m_trackingMode.load()) {
const long long nowMs = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now().time_since_epoch()).count();
const long long lastMs = m_lastProbeAcceptedMs.load();
if (lastMs > 0 && nowMs - lastMs < kAirplaneProbeIntervalMs) {
return;
}
m_lastProbeAcceptedMs.store(nowMs);
}
try {
std::shared_ptr<OwnedCloudFrame> frame = AcquireCloudFrameBuffer();
if (!frame) {
return;
}
if (!CopyCloudFrame(cloud, info, *frame)) {
if (!m_cloudCopyErrorReported.exchange(true)) {
NotifyStatus("雷达点云格式非 PointXYZI已丢弃当前帧");
}
return;
}
m_cloudCopyErrorReported.store(false);
{
std::lock_guard<std::mutex> dataLock(m_dataMutex);
m_latestCloudFrame = frame;
++m_cloudSequence;
}
m_detectionCondition.notify_one();
} catch (const std::exception& e) {
if (!m_cloudCopyErrorReported.exchange(true)) {
NotifyStatus(std::string("复制雷达点云失败:") + e.what());
}
} catch (...) {
if (!m_cloudCopyErrorReported.exchange(true)) {
NotifyStatus("复制雷达点云发生未知异常");
}
}
});
m_lidarDevice->SetExceptionCallback([this](const RsExceptionInfo& info) {
const QString message = info.message.empty()
? QStringLiteral("雷达异常")
: QStringLiteral("雷达异常:%1").arg(QString::fromStdString(info.message));
const bool fatal = info.code == 0x40 || info.code >= 0x80;
if (fatal && m_detectionRunning.load()) {
std::lock_guard<std::mutex> dataLock(m_dataMutex);
m_lidarErrorPending = true;
m_lidarErrorMessage = message;
}
if (fatal) {
m_detectionCondition.notify_all();
}
NotifyStatus(message.toUtf8().toStdString());
if (fatal) {
NotifyLidarStatus(false);
}
});
int ret = m_lidarDevice->InitDevice();
if (ret != 0) {
NotifyLidarStatus(false);
return ret;
}
ret = m_lidarDevice->OpenDevice(config);
if (ret != 0) {
NotifyLidarStatus(false);
return ret;
}
ret = m_lidarDevice->Start();
NotifyLidarStatus(ret == 0);
return ret;
}
int ParkingSpaceGuidePresenter::InitLedDisplay(const LedDisplayConfig& config)
{
if (m_ledDisplay) {
m_ledDisplay->Close();
delete m_ledDisplay;
m_ledDisplay = nullptr;
}
if (ILedDisplayDevice::CreateObject(&m_ledDisplay) != 0 || !m_ledDisplay) {
NotifyLedStatus(false);
return -1;
}
m_ledDisplay->SetStatusCallback([](ELedDisplayStatus status,
const std::string& message,
void* user) {
auto* self = static_cast<ParkingSpaceGuidePresenter*>(user);
if (!self) {
return;
}
self->NotifyLedStatus(status == ELedDisplayStatus::Connected);
if (!message.empty()) {
self->NotifyStatus(std::string("显示屏:") + message);
}
}, this);
const int ret = m_ledDisplay->Open(config);
NotifyLedStatus(config.enabled && ret == 0 && m_ledDisplay->IsConnected());
return ret;
}
int ParkingSpaceGuidePresenter::InitUdpBroadcast(const UdpBroadcastConfig& config)
{
std::lock_guard<std::mutex> lock(m_udpMutex);
if (m_udpClient) {
m_udpClient->StopUDPClient();
delete m_udpClient;
m_udpClient = nullptr;
}
m_udpBroadcastConfig = config;
if (!config.enabled) {
NotifyStatus("组播未启用");
return 0;
}
if (config.address.empty() || config.port <= 0 || config.port > 65535) {
NotifyStatus("组播配置无效");
return -1;
}
if (!IYUDPClient::CreateUDPClient(&m_udpClient) || !m_udpClient) {
NotifyStatus("创建组播客户端失败");
return -1;
}
const int ret = m_udpClient->Init(false);
if (ret != 0) {
NotifyStatus("初始化组播客户端失败:" + std::to_string(ret));
m_udpClient->StopUDPClient();
delete m_udpClient;
m_udpClient = nullptr;
return ret;
}
NotifyStatus("组播客户端已初始化");
return 0;
}
int ParkingSpaceGuidePresenter::InitHttpServer(const HttpServerConfig& config)
{
CloseHttpServer();
m_httpServer = new ParkingStatusHttpServer();
const int ret = m_httpServer->Start(
config,
[this]() {
return CurrentStatusJson();
});
if (ret != 0) {
const std::string error = m_httpServer->LastError();
delete m_httpServer;
m_httpServer = nullptr;
NotifyStatus(error.empty() ? "HTTP状态服务启动失败" : "HTTP状态服务启动失败" + error);
return ret;
}
if (config.enabled) {
NotifyStatus("HTTP状态服务已启动" + config.address + ":" + std::to_string(config.port));
} else {
NotifyStatus("HTTP状态服务未启用");
}
return 0;
}
void ParkingSpaceGuidePresenter::CloseUdpBroadcast()
{
std::lock_guard<std::mutex> lock(m_udpMutex);
if (!m_udpClient) {
return;
}
m_udpClient->StopUDPClient();
delete m_udpClient;
m_udpClient = nullptr;
}
void ParkingSpaceGuidePresenter::CloseHttpServer()
{
if (!m_httpServer) {
return;
}
m_httpServer->Stop();
delete m_httpServer;
m_httpServer = nullptr;
}
void ParkingSpaceGuidePresenter::CloseDevices()
{
CloseHttpServer();
CloseUdpBroadcast();
if (m_mvsDevice) {
m_mvsDevice->StopAcquisition();
m_mvsDevice->UnregisterImageCallback();
m_mvsDevice->CloseDevice();
m_mvsDevice->UninitSDK();
delete m_mvsDevice;
m_mvsDevice = nullptr;
}
if (m_lidarDevice) {
m_lidarDevice->Stop();
m_lidarDevice->CloseDevice();
delete m_lidarDevice;
m_lidarDevice = nullptr;
}
if (m_ledDisplay) {
m_ledDisplay->Close();
delete m_ledDisplay;
m_ledDisplay = nullptr;
}
}
void ParkingSpaceGuidePresenter::EnrichDetectionResult(DetectionResult& result) const
{
const UdpBroadcastConfig config = m_configManager
? m_configManager->GetConfigResult().udpBroadcastConfig
: m_udpBroadcastConfig;
for (ParkingSpaceGuideInfo& info : result.parkingSpaceInfoList) {
if (info.targetId.trimmed().isEmpty()) {
info.targetId = ConfigText(config.targetId, QStringLiteral("T001"));
}
if (info.parkId.trimmed().isEmpty()) {
info.parkId = ConfigText(config.parkId, QStringLiteral("P01"));
}
if (info.modelType.trimmed().isEmpty()) {
info.modelType = QStringLiteral("未知");
}
}
}
void ParkingSpaceGuidePresenter::UpdateCurrentStatus(const DetectionResult& result)
{
const UdpBroadcastConfig config = m_configManager
? m_configManager->GetConfigResult().udpBroadcastConfig
: m_udpBroadcastConfig;
const QByteArray data = QJsonDocument(BuildDetectionResultPayload(result, config))
.toJson(QJsonDocument::Compact);
std::lock_guard<std::mutex> lock(m_statusMutex);
m_currentStatusJson.assign(data.constData(), static_cast<size_t>(data.size()));
}
std::string ParkingSpaceGuidePresenter::CurrentStatusJson() const
{
std::lock_guard<std::mutex> lock(m_statusMutex);
if (!m_currentStatusJson.empty()) {
return m_currentStatusJson;
}
return "{\"success\":false,\"hasResult\":false,\"message\":\"暂无检测结果\",\"errorCode\":-1}";
}
bool ParkingSpaceGuidePresenter::BroadcastDetectionResult(const DetectionResult& result)
{
Q_UNUSED(result);
std::lock_guard<std::mutex> lock(m_udpMutex);
if (!m_udpClient || !m_udpBroadcastConfig.enabled) {
return false;
}
const std::string currentStatus = CurrentStatusJson();
const QByteArray data(currentStatus.data(), static_cast<int>(currentStatus.size()));
const int ret = m_udpClient->SendData(m_udpBroadcastConfig.port,
m_udpBroadcastConfig.address.c_str(),
data.constData(),
data.size());
if (ret != 0) {
NotifyStatus("组播发送失败:" + std::to_string(ret));
return false;
}
return true;
}
void ParkingSpaceGuidePresenter::NotifyStatus(const std::string& message)
{
LOG_INFO("%s\n", message.c_str());
if (StatusCallback()) {
StatusCallback()->OnStatusUpdate(message);
}
}
void ParkingSpaceGuidePresenter::NotifyWorkStatus(WorkStatus status)
{
if (StatusCallback()) {
StatusCallback()->OnWorkStatusChanged(status);
}
}
void ParkingSpaceGuidePresenter::NotifyCameraStatus(bool connected)
{
if (StatusCallback()) {
StatusCallback()->OnCamera1StatusChanged(connected);
}
}
void ParkingSpaceGuidePresenter::NotifyLidarStatus(bool connected)
{
if (StatusCallback()) {
StatusCallback()->OnCamera2StatusChanged(connected);
}
}
void ParkingSpaceGuidePresenter::NotifyLedStatus(bool connected)
{
if (StatusCallback()) {
StatusCallback()->OnSerialConnectionChanged(connected);
}
}
LedDisplayResult ParkingSpaceGuidePresenter::ConvertLedResult(const DetectionResult& result) const
{
LedDisplayResult output;
if (result.errorCode != 0) {
output.valid = true;
output.guideCode = 2;
return output;
}
output.valid = !result.parkingSpaceInfoList.empty();
if (!output.valid) {
return output;
}
const ParkingSpaceGuideInfo& info = result.parkingSpaceInfoList.front();
output.guideCode = (info.hasException || IsExceptionGuideState(info.guideStateCode)) ? 2 : 1;
output.distance = static_cast<float>(info.distance);
output.lateralOffset = static_cast<float>(info.lateralOffset);
output.angle = static_cast<float>(info.angle);
return output;
}
bool ParkingSpaceGuidePresenter::CacheMvsFrame(const MvsImageData& image, QImage& frame)
{
frame = QImage();
if (!image.pData || image.width == 0 || image.height == 0 || image.dataSize == 0) {
return false;
}
const int width = static_cast<int>(image.width);
const int height = static_cast<int>(image.height);
const size_t pixelCount = static_cast<size_t>(width) * static_cast<size_t>(height);
if (pixelCount == 0) {
return false;
}
QImage rgbImage;
if (image.pixelFormat == kMvsPixelTypeRGB8 && image.dataSize >= pixelCount * 3) {
QImage src(image.pData, width, height, width * 3, QImage::Format_RGB888);
rgbImage = src.copy();
} else if (image.pixelFormat == kMvsPixelTypeBGR8 && image.dataSize >= pixelCount * 3) {
QImage src(image.pData, width, height, width * 3, QImage::Format_RGB888);
rgbImage = src.rgbSwapped();
} else if (image.pixelFormat == kMvsPixelTypeMono8 && image.dataSize >= pixelCount) {
QImage src(image.pData, width, height, width, QImage::Format_Grayscale8);
rgbImage = src.convertToFormat(QImage::Format_RGB888);
} else {
return false;
}
if (rgbImage.isNull()) {
return false;
}
std::vector<unsigned char> compact(pixelCount * 3);
for (int row = 0; row < height; ++row) {
std::memcpy(compact.data() + static_cast<size_t>(row) * static_cast<size_t>(width) * 3,
rgbImage.constScanLine(row),
static_cast<size_t>(width) * 3);
}
CameraFrameInfo frameInfo;
frameInfo.width = width;
frameInfo.height = height;
frameInfo.pixelFormat = image.pixelFormat;
frameInfo.frameId = image.frameID;
frameInfo.timestamp = image.timestamp;
std::lock_guard<std::mutex> lock(m_dataMutex);
m_latestFrame.swap(compact);
m_latestFrameInfo = frameInfo;
m_hasFrame = true;
frame = rgbImage;
return true;
}