#include "VrConfig.h" #include #include #include #include "ConfigXmlUtils.h" #include "VrLog.h" using namespace tinyxml2; namespace { // 读取 下的子节点到 VrAlgorithmParams void LoadAlgorithmParams(XMLElement* algoElement, VrAlgorithmParams& params) { if (!algoElement) return; // 解析角点检测参数 if (XMLElement* e = algoElement->FirstChildElement("CornerParam")) { if (e->Attribute("minEndingGap")) params.cornerParam.minEndingGap = e->DoubleAttribute("minEndingGap"); if (e->Attribute("minEndingGap_z")) params.cornerParam.minEndingGap_z = e->DoubleAttribute("minEndingGap_z"); if (e->Attribute("scale")) params.cornerParam.scale = e->DoubleAttribute("scale"); if (e->Attribute("cornerTh")) params.cornerParam.cornerTh = e->DoubleAttribute("cornerTh"); if (e->Attribute("jumpCornerTh_1")) params.cornerParam.jumpCornerTh_1 = e->DoubleAttribute("jumpCornerTh_1"); if (e->Attribute("jumpCornerTh_2")) params.cornerParam.jumpCornerTh_2 = e->DoubleAttribute("jumpCornerTh_2"); } // 解析工件参数 if (XMLElement* e = algoElement->FirstChildElement("WorkpieceParam")) { if (e->Attribute("len")) params.workpieceParam.len = e->DoubleAttribute("len"); if (e->Attribute("width")) params.workpieceParam.width = e->DoubleAttribute("width"); if (e->Attribute("thickness")) params.workpieceParam.thickness = e->DoubleAttribute("thickness"); } // 解析多相机平面校准参数 ConfigXmlUtils::LoadPlaneCalibParams(algoElement, params.planeCalibParam); } void SaveAlgorithmParams(XMLDocument& doc, XMLElement* algoElement, const VrAlgorithmParams& params) { // 角点检测参数 XMLElement* cornerElement = doc.NewElement("CornerParam"); cornerElement->SetAttribute("cornerTh", params.cornerParam.cornerTh); cornerElement->SetAttribute("scale", params.cornerParam.scale); cornerElement->SetAttribute("minEndingGap", params.cornerParam.minEndingGap); cornerElement->SetAttribute("minEndingGap_z", params.cornerParam.minEndingGap_z); cornerElement->SetAttribute("jumpCornerTh_1", params.cornerParam.jumpCornerTh_1); cornerElement->SetAttribute("jumpCornerTh_2", params.cornerParam.jumpCornerTh_2); algoElement->InsertEndChild(cornerElement); // 工件参数 XMLElement* wpElement = doc.NewElement("WorkpieceParam"); wpElement->SetAttribute("len", params.workpieceParam.len); wpElement->SetAttribute("width", params.workpieceParam.width); wpElement->SetAttribute("thickness", params.workpieceParam.thickness); algoElement->InsertEndChild(wpElement); // 多相机平面校准参数 ConfigXmlUtils::SavePlaneCalibParams(doc, algoElement, params.planeCalibParam); } } // namespace CVrConfig::CVrConfig() : m_pNotify(nullptr) { } CVrConfig::~CVrConfig() { } int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResult) { XMLDocument doc; const 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()) != "JiuruiWorkpiecePoseConfig") { LOG_ERR("Config file format error: root element is not JiuruiWorkpiecePoseConfig\n"); return LOAD_CONFIG_INVALID_FORMAT; } // 解析摄像头列表 ConfigXmlUtils::LoadCameraList(root, configResult.cameraList); // 解析算法参数(类比 WorkpieceHole 的 结构) XMLElement* algoParamsElement = root->FirstChildElement("AlgorithmParams"); LoadAlgorithmParams(algoParamsElement, configResult.algorithmParams); // 解析调试参数 ConfigXmlUtils::LoadDebugParam(root, configResult.debugParam); // 解析串口配置 ConfigXmlUtils::LoadSerialConfig(root, configResult.serialConfig); // 解析手眼标定矩阵列表(支持多相机,类比 WorkpieceHole) ConfigXmlUtils::LoadHandEyeCalibMatrixs(root, configResult.handEyeCalibMatrixList); // 补全缺失的相机矩阵槽位 int cameraCount = static_cast(configResult.cameraList.size()); if (cameraCount <= 0) cameraCount = 1; for (int camIdx = 1; camIdx <= cameraCount; ++camIdx) { configResult.EnsureHandEyeMatrix(camIdx); } // 解析网络配置 if (XMLElement* networkElement = root->FirstChildElement("NetworkConfig")) { if (networkElement->Attribute("tcpServerPort")) configResult.tcpPort = static_cast(networkElement->IntAttribute("tcpServerPort")); if (networkElement->Attribute("eulerOrder")) configResult.eulerOrder = networkElement->IntAttribute("eulerOrder"); if (networkElement->Attribute("outputEulerOrder")) configResult.outputEulerOrder = networkElement->IntAttribute("outputEulerOrder"); if (networkElement->Attribute("poseOutputOrder")) configResult.poseOutputOrder = networkElement->IntAttribute("poseOutputOrder"); if (networkElement->Attribute("byteOrder")) configResult.byteOrder = networkElement->IntAttribute("byteOrder"); } if (XMLElement* tcpElement = root->FirstChildElement("TCP")) { if (tcpElement->Attribute("port")) configResult.tcpPort = static_cast(tcpElement->IntAttribute("port")); } LOG_INFO("Config loaded successfully from: %s (cameras: %zu, handEyeMatrices: %zu)\n", filePath.c_str(), configResult.cameraList.size(), configResult.handEyeCalibMatrixList.size()); return LOAD_CONFIG_SUCCESS; } bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResult) { XMLDocument doc; XMLDeclaration* declaration = doc.NewDeclaration("xml version=\"1.0\" encoding=\"UTF-8\""); doc.InsertFirstChild(declaration); XMLElement* root = doc.NewElement("JiuruiWorkpiecePoseConfig"); doc.InsertEndChild(root); // 摄像头列表 ConfigXmlUtils::SaveCameraList(doc, root, configResult.cameraList); // 算法参数(类比 WorkpieceHole 的 结构) XMLElement* algoParamsElement = doc.NewElement("AlgorithmParams"); SaveAlgorithmParams(doc, algoParamsElement, configResult.algorithmParams); root->InsertEndChild(algoParamsElement); // 调试参数 ConfigXmlUtils::SaveDebugParam(doc, root, configResult.debugParam); // 串口配置 ConfigXmlUtils::SaveSerialConfig(doc, root, configResult.serialConfig); // 手眼标定矩阵列表(支持多相机,类比 WorkpieceHole) ConfigXmlUtils::SaveHandEyeCalibMatrixs(doc, root, configResult.handEyeCalibMatrixList); // 网络配置 XMLElement* networkElement = doc.NewElement("NetworkConfig"); networkElement->SetAttribute("tcpServerPort", configResult.tcpPort); networkElement->SetAttribute("eulerOrder", configResult.eulerOrder); networkElement->SetAttribute("outputEulerOrder", configResult.outputEulerOrder); networkElement->SetAttribute("poseOutputOrder", configResult.poseOutputOrder); networkElement->SetAttribute("byteOrder", configResult.byteOrder); root->InsertEndChild(networkElement); XMLElement* tcpElement = doc.NewElement("TCP"); tcpElement->SetAttribute("port", configResult.tcpPort); root->InsertEndChild(tcpElement); const 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; } if (m_pNotify) { m_pNotify->OnConfigChanged(configResult); } LOG_INFO("Config saved successfully to: %s\n", filePath.c_str()); return true; } void CVrConfig::SetConfigChangeNotify(IVrConfigChangeNotify* notify) { m_pNotify = notify; } bool IVrConfig::CreateInstance(IVrConfig** ppVrConfig) { *ppVrConfig = new CVrConfig(); return *ppVrConfig != nullptr; }