256 lines
11 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;
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()) != "ScrewPositionConfig")
{
LOG_ERR("Config file format error: root element is not ScrewPositionConfig\n");
return LOAD_CONFIG_INVALID_FORMAT;
}
ConfigXmlUtils::LoadCameraList(root, configResult.cameraList);
XMLElement* algoElement = root->FirstChildElement("AlgorithmParams");
if (algoElement)
{
XMLElement* screwElement = algoElement->FirstChildElement("ScrewParam");
if (screwElement)
{
if (screwElement->Attribute("rodDiameter"))
configResult.algorithmParams.screwParam.rodDiameter = screwElement->DoubleAttribute("rodDiameter");
if (screwElement->Attribute("isHorizonScan"))
configResult.algorithmParams.screwParam.isHorizonScan = screwElement->BoolAttribute("isHorizonScan");
}
XMLElement* filterElement = algoElement->FirstChildElement("FilterParam");
if (filterElement)
{
if (filterElement->Attribute("continuityTh"))
configResult.algorithmParams.filterParam.continuityTh = filterElement->DoubleAttribute("continuityTh");
if (filterElement->Attribute("outlierTh"))
configResult.algorithmParams.filterParam.outlierTh = filterElement->DoubleAttribute("outlierTh");
}
XMLElement* cornerElement = algoElement->FirstChildElement("CornerParam");
if (cornerElement)
{
if (cornerElement->Attribute("minEndingGap"))
configResult.algorithmParams.cornerParam.minEndingGap = cornerElement->DoubleAttribute("minEndingGap");
if (cornerElement->Attribute("minEndingGap_z"))
configResult.algorithmParams.cornerParam.minEndingGap_z = cornerElement->DoubleAttribute("minEndingGap_z");
if (cornerElement->Attribute("scale"))
configResult.algorithmParams.cornerParam.scale = cornerElement->DoubleAttribute("scale");
if (cornerElement->Attribute("cornerTh"))
configResult.algorithmParams.cornerParam.cornerTh = cornerElement->DoubleAttribute("cornerTh");
if (cornerElement->Attribute("jumpCornerTh_1"))
configResult.algorithmParams.cornerParam.jumpCornerTh_1 = cornerElement->DoubleAttribute("jumpCornerTh_1");
if (cornerElement->Attribute("jumpCornerTh_2"))
configResult.algorithmParams.cornerParam.jumpCornerTh_2 = cornerElement->DoubleAttribute("jumpCornerTh_2");
}
XMLElement* growElement = algoElement->FirstChildElement("GrowParam");
if (growElement)
{
if (growElement->Attribute("yDeviation_max"))
configResult.algorithmParams.growParam.yDeviation_max = growElement->DoubleAttribute("yDeviation_max");
if (growElement->Attribute("zDeviation_max"))
configResult.algorithmParams.growParam.zDeviation_max = growElement->DoubleAttribute("zDeviation_max");
if (growElement->Attribute("maxLineSkipNum"))
configResult.algorithmParams.growParam.maxLineSkipNum = growElement->IntAttribute("maxLineSkipNum");
if (growElement->Attribute("maxSkipDistance"))
configResult.algorithmParams.growParam.maxSkipDistance = growElement->DoubleAttribute("maxSkipDistance");
if (growElement->Attribute("minLTypeTreeLen"))
configResult.algorithmParams.growParam.minLTypeTreeLen = growElement->DoubleAttribute("minLTypeTreeLen");
if (growElement->Attribute("minVTypeTreeLen"))
configResult.algorithmParams.growParam.minVTypeTreeLen = growElement->DoubleAttribute("minVTypeTreeLen");
}
ConfigXmlUtils::LoadPlaneCalibParams(algoElement, configResult.algorithmParams.planeCalibParam);
}
ConfigXmlUtils::LoadDebugParam(root, configResult.debugParam);
ConfigXmlUtils::LoadSerialConfig(root, configResult.serialConfig);
// 兼容旧配置:若存在已废弃的 eulerOrder/dirVectorInvert/longAxisDir
// 则一次性迁移为每台相机手眼矩阵的 eulerOrder/rotX/rotY/rotZ。
int legacyEulerOrder = 11;
bool hasLegacyEulerOrder = false;
double legacyRotX = 0.0;
double legacyRotY = 0.0;
double legacyRotZ = 0.0;
bool hasLegacyRotation = false;
XMLElement* networkElement = root->FirstChildElement("NetworkConfig");
if (networkElement)
{
if (networkElement->Attribute("tcpServerPort"))
configResult.tcpPort = static_cast<uint16_t>(networkElement->IntAttribute("tcpServerPort"));
if (networkElement->Attribute("poseOutputOrder"))
configResult.poseOutputOrder = networkElement->IntAttribute("poseOutputOrder");
if (networkElement->Attribute("byteOrder"))
configResult.byteOrder = networkElement->IntAttribute("byteOrder");
if (networkElement->Attribute("eulerOrder")) {
legacyEulerOrder = networkElement->IntAttribute("eulerOrder");
hasLegacyEulerOrder = true;
}
// 旧字段方向向量调整XY/XZ/YZ 反向),映射为绕对应轴 180°
if (networkElement->Attribute("dirVectorInvert")) {
const int dirInvert = networkElement->IntAttribute("dirVectorInvert");
switch (dirInvert) {
case 1: legacyRotZ = 180.0; hasLegacyRotation = true; break; // XY 反向 ≡ Rz(180°)
case 2: legacyRotY = 180.0; hasLegacyRotation = true; break; // XZ 反向 ≡ Ry(180°)
case 3: legacyRotX = 180.0; hasLegacyRotation = true; break; // YZ 反向 ≡ Rx(180°)
default: break;
}
}
// 旧字段长轴对应轴Y 轴时相当于绕 Z 旋转 -90°
if (networkElement->Attribute("longAxisDir")) {
const int longAxisDir = networkElement->IntAttribute("longAxisDir");
if (longAxisDir == 1) {
legacyRotZ += -90.0;
hasLegacyRotation = true;
}
}
}
XMLElement* tcpElement = root->FirstChildElement("TCP");
if (tcpElement && tcpElement->Attribute("port"))
{
configResult.tcpPort = static_cast<uint16_t>(tcpElement->IntAttribute("port"));
}
ConfigXmlUtils::LoadHandEyeCalibMatrixs(root,
configResult.handEyeCalibMatrixList,
legacyEulerOrder);
if (hasLegacyEulerOrder || hasLegacyRotation) {
for (auto& calibMatrix : configResult.handEyeCalibMatrixList) {
if (hasLegacyEulerOrder) {
calibMatrix.eulerOrder = legacyEulerOrder;
}
if (hasLegacyRotation) {
calibMatrix.rotX = legacyRotX;
calibMatrix.rotY = legacyRotY;
calibMatrix.rotZ = legacyRotZ;
}
}
}
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;
XMLDeclaration* declaration = doc.NewDeclaration("xml version=\"1.0\" encoding=\"UTF-8\"");
doc.InsertFirstChild(declaration);
XMLElement* root = doc.NewElement("ScrewPositionConfig");
doc.InsertEndChild(root);
ConfigXmlUtils::SaveCameraList(doc, root, configResult.cameraList);
XMLElement* algoElement = doc.NewElement("AlgorithmParams");
root->InsertEndChild(algoElement);
XMLElement* screwElement = doc.NewElement("ScrewParam");
screwElement->SetAttribute("rodDiameter", configResult.algorithmParams.screwParam.rodDiameter);
screwElement->SetAttribute("isHorizonScan", configResult.algorithmParams.screwParam.isHorizonScan);
algoElement->InsertEndChild(screwElement);
XMLElement* filterElement = doc.NewElement("FilterParam");
filterElement->SetAttribute("continuityTh", configResult.algorithmParams.filterParam.continuityTh);
filterElement->SetAttribute("outlierTh", configResult.algorithmParams.filterParam.outlierTh);
algoElement->InsertEndChild(filterElement);
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);
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);
ConfigXmlUtils::SavePlaneCalibParams(doc, algoElement, configResult.algorithmParams.planeCalibParam);
ConfigXmlUtils::SaveDebugParam(doc, root, configResult.debugParam);
ConfigXmlUtils::SaveSerialConfig(doc, root, configResult.serialConfig);
ConfigXmlUtils::SaveHandEyeCalibMatrixs(doc, root, configResult.handEyeCalibMatrixList);
XMLElement* networkElement = doc.NewElement("NetworkConfig");
networkElement->SetAttribute("tcpServerPort", configResult.tcpPort);
networkElement->SetAttribute("poseOutputOrder", configResult.poseOutputOrder);
networkElement->SetAttribute("byteOrder", configResult.byteOrder);
root->InsertEndChild(networkElement);
// Keep the legacy TCP node for backward compatibility.
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;
}