#include "VrConfig.h" #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* algoElement = root->FirstChildElement("AlgorithmParams"); if (algoElement) { // 2.1 解析螺杆检测参数 (ScrewParam) XMLElement* screwElement = algoElement->FirstChildElement("ScrewParam"); if (screwElement) { screwElement->QueryDoubleAttribute("rodDiameter", &configResult.algorithmParams.screwParam.rodDiameter); screwElement->QueryBoolAttribute("isHorizonScan", &configResult.algorithmParams.screwParam.isHorizonScan); LOG_INFO("ScrewParam: rodDiameter=%.1f, isHorizonScan=%d\n", configResult.algorithmParams.screwParam.rodDiameter, configResult.algorithmParams.screwParam.isHorizonScan); } // 2.2 解析离群点滤波参数 (FilterParam) XMLElement* filterElement = algoElement->FirstChildElement("FilterParam"); if (filterElement) { filterElement->QueryDoubleAttribute("continuityTh", &configResult.algorithmParams.filterParam.continuityTh); filterElement->QueryDoubleAttribute("outlierTh", &configResult.algorithmParams.filterParam.outlierTh); LOG_INFO("FilterParam: continuityTh=%.1f, outlierTh=%.1f\n", configResult.algorithmParams.filterParam.continuityTh, configResult.algorithmParams.filterParam.outlierTh); } // 2.3 解析角点检测参数 (CornerParam) XMLElement* cornerElement = algoElement->FirstChildElement("CornerParam"); if (cornerElement) { cornerElement->QueryDoubleAttribute("minEndingGap", &configResult.algorithmParams.cornerParam.minEndingGap); cornerElement->QueryDoubleAttribute("minEndingGap_z", &configResult.algorithmParams.cornerParam.minEndingGap_z); cornerElement->QueryDoubleAttribute("scale", &configResult.algorithmParams.cornerParam.scale); cornerElement->QueryDoubleAttribute("cornerTh", &configResult.algorithmParams.cornerParam.cornerTh); cornerElement->QueryDoubleAttribute("jumpCornerTh_1", &configResult.algorithmParams.cornerParam.jumpCornerTh_1); cornerElement->QueryDoubleAttribute("jumpCornerTh_2", &configResult.algorithmParams.cornerParam.jumpCornerTh_2); LOG_INFO("CornerParam: minEndingGap=%.1f, cornerTh=%.1f\n", configResult.algorithmParams.cornerParam.minEndingGap, configResult.algorithmParams.cornerParam.cornerTh); } // 2.4 解析树生长参数 (GrowParam) XMLElement* growElement = algoElement->FirstChildElement("GrowParam"); if (growElement) { growElement->QueryDoubleAttribute("yDeviation_max", &configResult.algorithmParams.growParam.yDeviation_max); growElement->QueryDoubleAttribute("zDeviation_max", &configResult.algorithmParams.growParam.zDeviation_max); growElement->QueryIntAttribute("maxLineSkipNum", &configResult.algorithmParams.growParam.maxLineSkipNum); growElement->QueryDoubleAttribute("maxSkipDistance", &configResult.algorithmParams.growParam.maxSkipDistance); growElement->QueryDoubleAttribute("minLTypeTreeLen", &configResult.algorithmParams.growParam.minLTypeTreeLen); growElement->QueryDoubleAttribute("minVTypeTreeLen", &configResult.algorithmParams.growParam.minVTypeTreeLen); LOG_INFO("GrowParam: yDeviation_max=%.1f, zDeviation_max=%.1f\n", configResult.algorithmParams.growParam.yDeviation_max, configResult.algorithmParams.growParam.zDeviation_max); } // 2.5 解析平面标定参数 (PlaneCalibParams) XMLElement* calibElement = algoElement->FirstChildElement("PlaneCalibParams"); if (calibElement) { XMLElement* cameraCalibElement = calibElement->FirstChildElement("CameraCalibParam"); while (cameraCalibElement) { VrCameraPlaneCalibParam cameraParam; cameraCalibElement->QueryIntAttribute("cameraIndex", &cameraParam.cameraIndex); if (cameraCalibElement->Attribute("cameraName")) cameraParam.cameraName = cameraCalibElement->Attribute("cameraName"); cameraCalibElement->QueryBoolAttribute("isCalibrated", &cameraParam.isCalibrated); cameraCalibElement->QueryDoubleAttribute("planeHeight", &cameraParam.planeHeight); // 解析校准矩阵(展开的属性格式) cameraCalibElement->QueryDoubleAttribute("planeCalib_00", &cameraParam.planeCalib[0]); cameraCalibElement->QueryDoubleAttribute("planeCalib_01", &cameraParam.planeCalib[1]); cameraCalibElement->QueryDoubleAttribute("planeCalib_02", &cameraParam.planeCalib[2]); cameraCalibElement->QueryDoubleAttribute("planeCalib_10", &cameraParam.planeCalib[3]); cameraCalibElement->QueryDoubleAttribute("planeCalib_11", &cameraParam.planeCalib[4]); cameraCalibElement->QueryDoubleAttribute("planeCalib_12", &cameraParam.planeCalib[5]); cameraCalibElement->QueryDoubleAttribute("planeCalib_20", &cameraParam.planeCalib[6]); cameraCalibElement->QueryDoubleAttribute("planeCalib_21", &cameraParam.planeCalib[7]); cameraCalibElement->QueryDoubleAttribute("planeCalib_22", &cameraParam.planeCalib[8]); // 解析逆矩阵(展开的属性格式) cameraCalibElement->QueryDoubleAttribute("invRMatrix_00", &cameraParam.invRMatrix[0]); cameraCalibElement->QueryDoubleAttribute("invRMatrix_01", &cameraParam.invRMatrix[1]); cameraCalibElement->QueryDoubleAttribute("invRMatrix_02", &cameraParam.invRMatrix[2]); cameraCalibElement->QueryDoubleAttribute("invRMatrix_10", &cameraParam.invRMatrix[3]); cameraCalibElement->QueryDoubleAttribute("invRMatrix_11", &cameraParam.invRMatrix[4]); cameraCalibElement->QueryDoubleAttribute("invRMatrix_12", &cameraParam.invRMatrix[5]); cameraCalibElement->QueryDoubleAttribute("invRMatrix_20", &cameraParam.invRMatrix[6]); cameraCalibElement->QueryDoubleAttribute("invRMatrix_21", &cameraParam.invRMatrix[7]); cameraCalibElement->QueryDoubleAttribute("invRMatrix_22", &cameraParam.invRMatrix[8]); configResult.algorithmParams.planeCalibParam.SetCameraCalibParam(cameraParam); LOG_INFO("PlaneCalibParam: camera %d, isCalibrated=%d, planeHeight=%.3f\n", cameraParam.cameraIndex, cameraParam.isCalibrated, cameraParam.planeHeight); cameraCalibElement = cameraCalibElement->NextSiblingElement("CameraCalibParam"); } } } // 3. 解析调试参数 XMLElement* debugElement = root->FirstChildElement("DebugParam"); 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()); } // 4. 解析串口配置 XMLElement* serialElement = root->FirstChildElement("SerialConfig"); 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); } // 5. 解析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); } 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* algoElement = doc.NewElement("AlgorithmParams"); // 2.1 保存螺杆检测参数 XMLElement* screwElement = doc.NewElement("ScrewParam"); screwElement->SetAttribute("rodDiameter", configResult.algorithmParams.screwParam.rodDiameter); screwElement->SetAttribute("isHorizonScan", configResult.algorithmParams.screwParam.isHorizonScan); algoElement->InsertEndChild(screwElement); // 2.2 保存离群点滤波参数 XMLElement* filterElement = doc.NewElement("FilterParam"); filterElement->SetAttribute("continuityTh", configResult.algorithmParams.filterParam.continuityTh); filterElement->SetAttribute("outlierTh", configResult.algorithmParams.filterParam.outlierTh); algoElement->InsertEndChild(filterElement); // 2.3 保存角点检测参数 XMLElement* cornerElement = doc.NewElement("CornerParam"); cornerElement->SetAttribute("cornerTh", configResult.algorithmParams.cornerParam.cornerTh); cornerElement->SetAttribute("scale", configResult.algorithmParams.cornerParam.scale); cornerElement->SetAttribute("minEndingGap", configResult.algorithmParams.cornerParam.minEndingGap); cornerElement->SetAttribute("minEndingGap_z", configResult.algorithmParams.cornerParam.minEndingGap_z); cornerElement->SetAttribute("jumpCornerTh_1", configResult.algorithmParams.cornerParam.jumpCornerTh_1); cornerElement->SetAttribute("jumpCornerTh_2", configResult.algorithmParams.cornerParam.jumpCornerTh_2); algoElement->InsertEndChild(cornerElement); // 2.4 保存树生长参数 XMLElement* growElement = doc.NewElement("GrowParam"); growElement->SetAttribute("maxLineSkipNum", configResult.algorithmParams.growParam.maxLineSkipNum); growElement->SetAttribute("yDeviation_max", configResult.algorithmParams.growParam.yDeviation_max); growElement->SetAttribute("maxSkipDistance", configResult.algorithmParams.growParam.maxSkipDistance); growElement->SetAttribute("zDeviation_max", configResult.algorithmParams.growParam.zDeviation_max); growElement->SetAttribute("minLTypeTreeLen", configResult.algorithmParams.growParam.minLTypeTreeLen); growElement->SetAttribute("minVTypeTreeLen", configResult.algorithmParams.growParam.minVTypeTreeLen); algoElement->InsertEndChild(growElement); // 2.5 保存平面标定参数 if (!configResult.algorithmParams.planeCalibParam.cameraCalibParams.empty()) { XMLElement* calibElement = doc.NewElement("PlaneCalibParams"); for (const auto& cameraParam : configResult.algorithmParams.planeCalibParam.cameraCalibParams) { XMLElement* cameraCalibElement = doc.NewElement("CameraCalibParam"); cameraCalibElement->SetAttribute("cameraIndex", cameraParam.cameraIndex); cameraCalibElement->SetAttribute("cameraName", cameraParam.cameraName.c_str()); cameraCalibElement->SetAttribute("isCalibrated", cameraParam.isCalibrated); cameraCalibElement->SetAttribute("planeHeight", cameraParam.planeHeight); // 保存校准矩阵(展开的属性格式) cameraCalibElement->SetAttribute("planeCalib_00", cameraParam.planeCalib[0]); cameraCalibElement->SetAttribute("planeCalib_01", cameraParam.planeCalib[1]); cameraCalibElement->SetAttribute("planeCalib_02", cameraParam.planeCalib[2]); cameraCalibElement->SetAttribute("planeCalib_10", cameraParam.planeCalib[3]); cameraCalibElement->SetAttribute("planeCalib_11", cameraParam.planeCalib[4]); cameraCalibElement->SetAttribute("planeCalib_12", cameraParam.planeCalib[5]); cameraCalibElement->SetAttribute("planeCalib_20", cameraParam.planeCalib[6]); cameraCalibElement->SetAttribute("planeCalib_21", cameraParam.planeCalib[7]); cameraCalibElement->SetAttribute("planeCalib_22", cameraParam.planeCalib[8]); // 保存逆矩阵(展开的属性格式) cameraCalibElement->SetAttribute("invRMatrix_00", cameraParam.invRMatrix[0]); cameraCalibElement->SetAttribute("invRMatrix_01", cameraParam.invRMatrix[1]); cameraCalibElement->SetAttribute("invRMatrix_02", cameraParam.invRMatrix[2]); cameraCalibElement->SetAttribute("invRMatrix_10", cameraParam.invRMatrix[3]); cameraCalibElement->SetAttribute("invRMatrix_11", cameraParam.invRMatrix[4]); cameraCalibElement->SetAttribute("invRMatrix_12", cameraParam.invRMatrix[5]); cameraCalibElement->SetAttribute("invRMatrix_20", cameraParam.invRMatrix[6]); cameraCalibElement->SetAttribute("invRMatrix_21", cameraParam.invRMatrix[7]); cameraCalibElement->SetAttribute("invRMatrix_22", cameraParam.invRMatrix[8]); calibElement->InsertEndChild(cameraCalibElement); } algoElement->InsertEndChild(calibElement); } root->InsertEndChild(algoElement); // 3. 保存调试参数 XMLElement* debugElement = doc.NewElement("DebugParam"); debugElement->SetAttribute("enableDebug", configResult.debugParam.enableDebug); debugElement->SetAttribute("savePointCloud", configResult.debugParam.savePointCloud); debugElement->SetAttribute("saveDebugImage", configResult.debugParam.saveDebugImage); debugElement->SetAttribute("printDetailLog", configResult.debugParam.printDetailLog); debugElement->SetAttribute("debugOutputPath", configResult.debugParam.debugOutputPath.c_str()); root->InsertEndChild(debugElement); // 4. 保存串口配置 XMLElement* serialElement = doc.NewElement("SerialConfig"); 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); // 5. 保存TCP端口 XMLElement* tcpElement = doc.NewElement("TCP"); tcpElement->SetAttribute("port", configResult.tcpPort); root->InsertEndChild(tcpElement); // 保存到文件 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; }