209 lines
7.8 KiB
C++
Raw 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 "VrConfig.h"
#include <iostream>
#include <string>
#include <vector>
#include "ConfigXmlUtils.h"
#include "VrLog.h"
using namespace tinyxml2;
namespace {
// 读取 <AlgorithmParams> 下的子节点到 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("TireParam")) {
if (e->Attribute("diameter"))
params.tireParam.diameter = e->DoubleAttribute("diameter");
if (e->Attribute("thickness"))
params.tireParam.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* tireElement = doc.NewElement("TireParam");
tireElement->SetAttribute("diameter", params.tireParam.diameter);
tireElement->SetAttribute("thickness", params.tireParam.thickness);
algoElement->InsertEndChild(tireElement);
// 多相机平面校准参数
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()) != "TireHolePoseConfig")
{
LOG_ERR("Config file format error: root element is not TireHolePoseConfig\n");
return LOAD_CONFIG_INVALID_FORMAT;
}
// 解析摄像头列表
ConfigXmlUtils::LoadCameraList(root, configResult.cameraList);
// 解析算法参数(类比 WorkpieceHole 的 <AlgorithmParams> 结构)
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<int>(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<uint16_t>(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<uint16_t>(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("TireHolePoseConfig");
doc.InsertEndChild(root);
// 摄像头列表
ConfigXmlUtils::SaveCameraList(doc, root, configResult.cameraList);
// 算法参数(类比 WorkpieceHole 的 <AlgorithmParams> 结构)
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;
}