#include "VrConfig.h" #include #include #include #include #include "VrLog.h" using namespace tinyxml2; CVrConfig::CVrConfig() : m_pNotify(nullptr) { // 构造函数 } CVrConfig::~CVrConfig() { // 析构函数 } int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResult) { // 使用tinyxml2库加载XML文件 XMLDocument doc; XMLError err = doc.LoadFile(filePath.c_str()); if (err != XML_SUCCESS) { LOG_ERR("Failed to open config file: %s\n", filePath.c_str()); return LOAD_CONFIG_FILE_NOT_FOUND; } // 获取根元素 XMLElement* root = doc.RootElement(); if (!root || std::string(root->Name()) != "ScrewPositionConfig") { LOG_ERR("Config file format error: root element is not ScrewPositionConfig\n"); return LOAD_CONFIG_INVALID_FORMAT; } // 1. 解析相机设备列表 XMLElement* camerasElement = root->FirstChildElement("Cameras"); if (camerasElement) { configResult.cameraList.clear(); XMLElement* cameraElement = camerasElement->FirstChildElement("Camera"); while (cameraElement) { DeviceInfo camera; if (cameraElement->Attribute("name")) camera.name = cameraElement->Attribute("name"); if (cameraElement->Attribute("ip")) camera.ip = cameraElement->Attribute("ip"); cameraElement->QueryIntAttribute("index", &camera.index); configResult.cameraList.push_back(camera); LOG_INFO("Camera: name=%s, ip=%s, index=%d\n", camera.name.c_str(), camera.ip.c_str(), camera.index); cameraElement = cameraElement->NextSiblingElement("Camera"); } } // 2. 解析调试参数 XMLElement* debugElement = root->FirstChildElement("Debug"); if (debugElement) { debugElement->QueryBoolAttribute("enableDebug", &configResult.debugParam.enableDebug); debugElement->QueryBoolAttribute("saveDebugImage", &configResult.debugParam.saveDebugImage); debugElement->QueryBoolAttribute("savePointCloud", &configResult.debugParam.savePointCloud); debugElement->QueryBoolAttribute("printDetailLog", &configResult.debugParam.printDetailLog); if (debugElement->Attribute("debugOutputPath")) configResult.debugParam.debugOutputPath = debugElement->Attribute("debugOutputPath"); LOG_INFO("Debug config: enableDebug=%d, saveDebugImage=%d, savePointCloud=%d, debugOutputPath=%s\n", configResult.debugParam.enableDebug, configResult.debugParam.saveDebugImage, configResult.debugParam.savePointCloud, configResult.debugParam.debugOutputPath.c_str()); } // 3. 解析串口配置 XMLElement* serialElement = root->FirstChildElement("Serial"); if (serialElement) { if (serialElement->Attribute("portName")) configResult.serialConfig.portName = serialElement->Attribute("portName"); serialElement->QueryIntAttribute("baudRate", &configResult.serialConfig.baudRate); serialElement->QueryIntAttribute("dataBits", &configResult.serialConfig.dataBits); serialElement->QueryIntAttribute("stopBits", &configResult.serialConfig.stopBits); serialElement->QueryIntAttribute("parity", &configResult.serialConfig.parity); serialElement->QueryIntAttribute("flowControl", &configResult.serialConfig.flowControl); serialElement->QueryBoolAttribute("enabled", &configResult.serialConfig.enabled); LOG_INFO("Serial config: port=%s, baudRate=%d, enabled=%d\n", configResult.serialConfig.portName.c_str(), configResult.serialConfig.baudRate, configResult.serialConfig.enabled); } // 4. 解析TCP端口 XMLElement* tcpElement = root->FirstChildElement("TCP"); if (tcpElement) { int port = 5020; tcpElement->QueryIntAttribute("port", &port); configResult.tcpPort = static_cast(port); LOG_INFO("TCP port: %d\n", configResult.tcpPort); } // 5. 解析螺杆检测参数 XMLElement* screwElement = root->FirstChildElement("ScrewParam"); if (screwElement) { XMLElement* elem = screwElement->FirstChildElement("RodDiameter"); if (elem) elem->QueryDoubleText(&configResult.algorithmParams.screwParam.rodDiameter); elem = screwElement->FirstChildElement("IsHorizonScan"); if (elem) { const char* text = elem->GetText(); if (text) configResult.algorithmParams.screwParam.isHorizonScan = (std::string(text) == "true"); } LOG_INFO("ScrewParam: rodDiameter=%.1f, isHorizonScan=%d\n", configResult.algorithmParams.screwParam.rodDiameter, configResult.algorithmParams.screwParam.isHorizonScan); } // 6. 解析角点检测参数 XMLElement* cornerElement = root->FirstChildElement("CornerParam"); if (cornerElement) { XMLElement* elem = cornerElement->FirstChildElement("MinEndingGap"); if (elem) elem->QueryDoubleText(&configResult.algorithmParams.cornerParam.minEndingGap); elem = cornerElement->FirstChildElement("MinEndingGapZ"); if (elem) elem->QueryDoubleText(&configResult.algorithmParams.cornerParam.minEndingGap_z); elem = cornerElement->FirstChildElement("Scale"); if (elem) elem->QueryDoubleText(&configResult.algorithmParams.cornerParam.scale); elem = cornerElement->FirstChildElement("CornerTh"); if (elem) elem->QueryDoubleText(&configResult.algorithmParams.cornerParam.cornerTh); elem = cornerElement->FirstChildElement("JumpCornerTh1"); if (elem) elem->QueryDoubleText(&configResult.algorithmParams.cornerParam.jumpCornerTh_1); elem = cornerElement->FirstChildElement("JumpCornerTh2"); if (elem) elem->QueryDoubleText(&configResult.algorithmParams.cornerParam.jumpCornerTh_2); LOG_INFO("CornerParam: cornerTh=%.1f, scale=%.1f\n", configResult.algorithmParams.cornerParam.cornerTh, configResult.algorithmParams.cornerParam.scale); } // 7. 解析离群点滤波参数 XMLElement* filterElement = root->FirstChildElement("FilterParam"); if (filterElement) { XMLElement* elem = filterElement->FirstChildElement("MeanK"); if (elem) elem->QueryDoubleText(&configResult.algorithmParams.filterParam.continuityTh); elem = filterElement->FirstChildElement("StddevMul"); if (elem) elem->QueryDoubleText(&configResult.algorithmParams.filterParam.outlierTh); LOG_INFO("FilterParam: continuityTh=%.1f, outlierTh=%.1f\n", configResult.algorithmParams.filterParam.continuityTh, configResult.algorithmParams.filterParam.outlierTh); } // 8. 解析区域生长参数 XMLElement* growElement = root->FirstChildElement("GrowParam"); if (growElement) { XMLElement* elem = growElement->FirstChildElement("YDeviationMax"); if (elem) elem->QueryDoubleText(&configResult.algorithmParams.growParam.yDeviation_max); elem = growElement->FirstChildElement("ZDeviationMax"); if (elem) elem->QueryDoubleText(&configResult.algorithmParams.growParam.zDeviation_max); elem = growElement->FirstChildElement("MaxLineSkipNum"); if (elem) elem->QueryIntText(&configResult.algorithmParams.growParam.maxLineSkipNum); elem = growElement->FirstChildElement("MaxSkipDistance"); if (elem) elem->QueryDoubleText(&configResult.algorithmParams.growParam.maxSkipDistance); elem = growElement->FirstChildElement("MinLTypeTreeLen"); if (elem) elem->QueryDoubleText(&configResult.algorithmParams.growParam.minLTypeTreeLen); elem = growElement->FirstChildElement("MinVTypeTreeLen"); if (elem) elem->QueryDoubleText(&configResult.algorithmParams.growParam.minVTypeTreeLen); LOG_INFO("GrowParam: yDeviation_max=%.1f, zDeviation_max=%.1f\n", configResult.algorithmParams.growParam.yDeviation_max, configResult.algorithmParams.growParam.zDeviation_max); } // 9. 解析平面校准参数 XMLElement* calibElement = root->FirstChildElement("CalibrationConfig"); while (calibElement) { int cameraId = 1; calibElement->QueryIntAttribute("id", &cameraId); VrCameraPlaneCalibParam calibParam; calibParam.cameraIndex = cameraId; XMLElement* elem = calibElement->FirstChildElement("PlaneHeight"); if (elem) elem->QueryDoubleText(&calibParam.planeHeight); elem = calibElement->FirstChildElement("IsCalibrated"); if (elem) { const char* text = elem->GetText(); if (text) calibParam.isCalibrated = (std::string(text) == "true"); } // 解析 PlaneCalib 矩阵 (9个元素) elem = calibElement->FirstChildElement("PlaneCalib"); if (elem) { const char* text = elem->GetText(); if (text) { std::istringstream iss(text); for (int i = 0; i < 9 && iss; i++) { iss >> calibParam.planeCalib[i]; } } } // 解析 InvRMatrix 矩阵 (9个元素) elem = calibElement->FirstChildElement("InvRMatrix"); if (elem) { const char* text = elem->GetText(); if (text) { std::istringstream iss(text); for (int i = 0; i < 9 && iss; i++) { iss >> calibParam.invRMatrix[i]; } } } configResult.algorithmParams.planeCalibParam.SetCameraCalibParam(calibParam); LOG_INFO("CalibrationConfig: cameraId=%d, planeHeight=%.3f, isCalibrated=%d\n", cameraId, calibParam.planeHeight, calibParam.isCalibrated); calibElement = calibElement->NextSiblingElement("CalibrationConfig"); } LOG_INFO("Config loaded successfully from: %s\n", filePath.c_str()); return LOAD_CONFIG_SUCCESS; } bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResult) { XMLDocument doc; // 创建 XML 声明 XMLDeclaration* declaration = doc.NewDeclaration(); doc.InsertFirstChild(declaration); // 创建根元素 XMLElement* root = doc.NewElement("ScrewPositionConfig"); doc.InsertEndChild(root); // 1. 保存相机设备列表 if (!configResult.cameraList.empty()) { XMLElement* camerasElement = doc.NewElement("Cameras"); for (const auto& camera : configResult.cameraList) { XMLElement* cameraElement = doc.NewElement("Camera"); cameraElement->SetAttribute("name", camera.name.c_str()); cameraElement->SetAttribute("ip", camera.ip.c_str()); cameraElement->SetAttribute("index", camera.index); camerasElement->InsertEndChild(cameraElement); } root->InsertEndChild(camerasElement); } // 2. 保存调试参数 XMLElement* debugElement = doc.NewElement("Debug"); debugElement->SetAttribute("enableDebug", configResult.debugParam.enableDebug); debugElement->SetAttribute("saveDebugImage", configResult.debugParam.saveDebugImage); debugElement->SetAttribute("savePointCloud", configResult.debugParam.savePointCloud); debugElement->SetAttribute("printDetailLog", configResult.debugParam.printDetailLog); debugElement->SetAttribute("debugOutputPath", configResult.debugParam.debugOutputPath.c_str()); root->InsertEndChild(debugElement); // 3. 保存串口配置 XMLElement* serialElement = doc.NewElement("Serial"); serialElement->SetAttribute("portName", configResult.serialConfig.portName.c_str()); serialElement->SetAttribute("baudRate", configResult.serialConfig.baudRate); serialElement->SetAttribute("dataBits", configResult.serialConfig.dataBits); serialElement->SetAttribute("stopBits", configResult.serialConfig.stopBits); serialElement->SetAttribute("parity", configResult.serialConfig.parity); serialElement->SetAttribute("flowControl", configResult.serialConfig.flowControl); serialElement->SetAttribute("enabled", configResult.serialConfig.enabled); root->InsertEndChild(serialElement); // 4. 保存TCP端口 XMLElement* tcpElement = doc.NewElement("TCP"); tcpElement->SetAttribute("port", configResult.tcpPort); root->InsertEndChild(tcpElement); // 5. 保存螺杆检测参数 XMLElement* screwElement = doc.NewElement("ScrewParam"); XMLElement* elem = doc.NewElement("RodDiameter"); elem->SetText(configResult.algorithmParams.screwParam.rodDiameter); screwElement->InsertEndChild(elem); elem = doc.NewElement("IsHorizonScan"); elem->SetText(configResult.algorithmParams.screwParam.isHorizonScan ? "true" : "false"); screwElement->InsertEndChild(elem); root->InsertEndChild(screwElement); // 6. 保存角点检测参数 XMLElement* cornerElement = doc.NewElement("CornerParam"); elem = doc.NewElement("MinEndingGap"); elem->SetText(configResult.algorithmParams.cornerParam.minEndingGap); cornerElement->InsertEndChild(elem); elem = doc.NewElement("MinEndingGapZ"); elem->SetText(configResult.algorithmParams.cornerParam.minEndingGap_z); cornerElement->InsertEndChild(elem); elem = doc.NewElement("Scale"); elem->SetText(configResult.algorithmParams.cornerParam.scale); cornerElement->InsertEndChild(elem); elem = doc.NewElement("CornerTh"); elem->SetText(configResult.algorithmParams.cornerParam.cornerTh); cornerElement->InsertEndChild(elem); elem = doc.NewElement("JumpCornerTh1"); elem->SetText(configResult.algorithmParams.cornerParam.jumpCornerTh_1); cornerElement->InsertEndChild(elem); elem = doc.NewElement("JumpCornerTh2"); elem->SetText(configResult.algorithmParams.cornerParam.jumpCornerTh_2); cornerElement->InsertEndChild(elem); root->InsertEndChild(cornerElement); // 7. 保存离群点滤波参数 XMLElement* filterElement = doc.NewElement("FilterParam"); elem = doc.NewElement("MeanK"); elem->SetText(configResult.algorithmParams.filterParam.continuityTh); filterElement->InsertEndChild(elem); elem = doc.NewElement("StddevMul"); elem->SetText(configResult.algorithmParams.filterParam.outlierTh); filterElement->InsertEndChild(elem); root->InsertEndChild(filterElement); // 8. 保存区域生长参数 XMLElement* growElement = doc.NewElement("GrowParam"); elem = doc.NewElement("YDeviationMax"); elem->SetText(configResult.algorithmParams.growParam.yDeviation_max); growElement->InsertEndChild(elem); elem = doc.NewElement("ZDeviationMax"); elem->SetText(configResult.algorithmParams.growParam.zDeviation_max); growElement->InsertEndChild(elem); elem = doc.NewElement("MaxLineSkipNum"); elem->SetText(configResult.algorithmParams.growParam.maxLineSkipNum); growElement->InsertEndChild(elem); elem = doc.NewElement("MaxSkipDistance"); elem->SetText(configResult.algorithmParams.growParam.maxSkipDistance); growElement->InsertEndChild(elem); elem = doc.NewElement("MinLTypeTreeLen"); elem->SetText(configResult.algorithmParams.growParam.minLTypeTreeLen); growElement->InsertEndChild(elem); elem = doc.NewElement("MinVTypeTreeLen"); elem->SetText(configResult.algorithmParams.growParam.minVTypeTreeLen); growElement->InsertEndChild(elem); root->InsertEndChild(growElement); // 9. 保存平面校准参数 for (const auto& calibParam : configResult.algorithmParams.planeCalibParam.cameraCalibParams) { XMLElement* calibElement = doc.NewElement("CalibrationConfig"); calibElement->SetAttribute("id", calibParam.cameraIndex); elem = doc.NewElement("PlaneHeight"); elem->SetText(calibParam.planeHeight); calibElement->InsertEndChild(elem); elem = doc.NewElement("IsCalibrated"); elem->SetText(calibParam.isCalibrated ? "true" : "false"); calibElement->InsertEndChild(elem); // 保存 PlaneCalib 矩阵 std::ostringstream ossPlane; for (int i = 0; i < 9; i++) { if (i > 0) ossPlane << " "; ossPlane << calibParam.planeCalib[i]; } elem = doc.NewElement("PlaneCalib"); elem->SetText(ossPlane.str().c_str()); calibElement->InsertEndChild(elem); // 保存 InvRMatrix 矩阵 std::ostringstream ossInv; for (int i = 0; i < 9; i++) { if (i > 0) ossInv << " "; ossInv << calibParam.invRMatrix[i]; } elem = doc.NewElement("InvRMatrix"); elem->SetText(ossInv.str().c_str()); calibElement->InsertEndChild(elem); root->InsertEndChild(calibElement); } // 保存到文件 XMLError err = doc.SaveFile(filePath.c_str()); if (err != XML_SUCCESS) { LOG_ERR("Failed to save config file: %s\n", filePath.c_str()); return false; } LOG_INFO("Config saved successfully to: %s\n", filePath.c_str()); return true; } // 设置配置改变通知回调 void CVrConfig::SetConfigChangeNotify(IVrConfigChangeNotify* notify) { m_pNotify = notify; } /** * @brief 创建实例 * @return 实例 */ bool IVrConfig::CreateInstance(IVrConfig** ppVrConfig) { *ppVrConfig = new CVrConfig() ; return *ppVrConfig != nullptr; }