427 lines
19 KiB
C++
427 lines
19 KiB
C++
#include "VrWheelMeasureConfig.h"
|
|
#include "IVrWheelMeasureConfig.h"
|
|
#include <algorithm>
|
|
#include <sstream>
|
|
#include "VrLog.h"
|
|
#include <QFile>
|
|
#include <QTextStream>
|
|
#include <QString>
|
|
#include <QTextCodec>
|
|
#include <QXmlStreamReader>
|
|
#include <QXmlStreamWriter>
|
|
|
|
VrWheelMeasureConfig::VrWheelMeasureConfig()
|
|
: m_notify(nullptr)
|
|
{
|
|
}
|
|
|
|
VrWheelMeasureConfig::~VrWheelMeasureConfig()
|
|
{
|
|
}
|
|
|
|
// 静态工厂方法
|
|
bool IVrWheelMeasureConfig::CreateInstance(IVrWheelMeasureConfig** ppVrConfig)
|
|
{
|
|
if (!ppVrConfig) {
|
|
return false;
|
|
}
|
|
|
|
*ppVrConfig = new VrWheelMeasureConfig();
|
|
return true;
|
|
}
|
|
|
|
WheelMeasureConfigResult VrWheelMeasureConfig::LoadConfig(const std::string& filePath)
|
|
{
|
|
WheelMeasureConfigResult result;
|
|
|
|
// 使用QString处理可能包含中文的路径
|
|
QString qFilePath = QString::fromStdString(filePath);
|
|
QFile file(qFilePath);
|
|
|
|
// 检查文件是否存在并可读
|
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
LOG_DEBUG("Failed to open file: %s\n", filePath.c_str());
|
|
return result;
|
|
}
|
|
|
|
// 使用QXmlStreamReader解析XML内容
|
|
QXmlStreamReader xml(&file);
|
|
|
|
// 读取到根元素
|
|
if (xml.readNextStartElement()) {
|
|
if (xml.name() != "WheelMeasureConfig") {
|
|
xml.raiseError(QObject::tr("Not a WheelMeasureConfig file"));
|
|
}
|
|
} else {
|
|
xml.raiseError(QObject::tr("Failed to read root element"));
|
|
}
|
|
|
|
// 解析XML内容
|
|
while (!xml.atEnd() && !xml.hasError()) {
|
|
xml.readNext();
|
|
|
|
// 解析相机配置
|
|
if (xml.isStartElement() && xml.name() == "Cameras") {
|
|
while (xml.readNextStartElement()) {
|
|
if (xml.name() == "Camera") {
|
|
WheelCameraParam camera;
|
|
camera.cameraIndex = xml.attributes().value("index").toInt();
|
|
camera.name = xml.attributes().value("name").toString().toStdString();
|
|
camera.cameraIP = xml.attributes().value("ip").toString().toStdString();
|
|
camera.enabled = xml.attributes().value("enabled").toInt() != 0;
|
|
result.cameras.push_back(camera);
|
|
xml.skipCurrentElement();
|
|
}
|
|
}
|
|
}
|
|
|
|
// 解析相机调平参数
|
|
else if (xml.isStartElement() && xml.name() == "PlaneCalibParams") {
|
|
while (xml.readNextStartElement()) {
|
|
if (xml.name() == "CameraCalib") {
|
|
WheelCameraPlaneCalibParam calibParam;
|
|
calibParam.cameraIndex = xml.attributes().value("index").toInt();
|
|
calibParam.cameraName = xml.attributes().value("name").toString().toStdString();
|
|
calibParam.planeHeight = xml.attributes().value("planeHeight").toDouble();
|
|
calibParam.isCalibrated = xml.attributes().value("isCalibrated").toInt() != 0;
|
|
|
|
// 读取planeCalib矩阵
|
|
QString planeCalibStr = xml.attributes().value("planeCalib").toString();
|
|
QStringList planeCalibList = planeCalibStr.split(",");
|
|
for (int i = 0; i < 9 && i < planeCalibList.size(); ++i) {
|
|
calibParam.planeCalib[i] = planeCalibList[i].toDouble();
|
|
}
|
|
|
|
// 读取invRMatrix矩阵
|
|
QString invRMatrixStr = xml.attributes().value("invRMatrix").toString();
|
|
QStringList invRMatrixList = invRMatrixStr.split(",");
|
|
for (int i = 0; i < 9 && i < invRMatrixList.size(); ++i) {
|
|
calibParam.invRMatrix[i] = invRMatrixList[i].toDouble();
|
|
}
|
|
|
|
result.planeCalibParams.push_back(calibParam);
|
|
xml.skipCurrentElement();
|
|
}
|
|
}
|
|
}
|
|
|
|
// 解析算法参数
|
|
else if (xml.isStartElement() && xml.name() == "AlgorithmParams") {
|
|
while (xml.readNextStartElement()) {
|
|
// 角点参数
|
|
if (xml.name() == "CornerParam") {
|
|
result.algorithmParams.cornerParam.minEndingGap =
|
|
xml.attributes().value("minEndingGap").toDouble();
|
|
result.algorithmParams.cornerParam.minEndingGap_z =
|
|
xml.attributes().value("minEndingGap_z").toDouble();
|
|
result.algorithmParams.cornerParam.scale =
|
|
xml.attributes().value("scale").toDouble();
|
|
result.algorithmParams.cornerParam.cornerTh =
|
|
xml.attributes().value("cornerTh").toDouble();
|
|
result.algorithmParams.cornerParam.jumpCornerTh_1 =
|
|
xml.attributes().value("jumpCornerTh_1").toDouble();
|
|
result.algorithmParams.cornerParam.jumpCornerTh_2 =
|
|
xml.attributes().value("jumpCornerTh_2").toDouble();
|
|
|
|
// 设置默认值
|
|
if (result.algorithmParams.cornerParam.minEndingGap == 0.0) {
|
|
result.algorithmParams.cornerParam.minEndingGap = 3.0;
|
|
}
|
|
if (result.algorithmParams.cornerParam.minEndingGap_z == 0.0) {
|
|
result.algorithmParams.cornerParam.minEndingGap_z = 5.0;
|
|
}
|
|
if (result.algorithmParams.cornerParam.scale == 0.0) {
|
|
result.algorithmParams.cornerParam.scale = 10.0;
|
|
}
|
|
if (result.algorithmParams.cornerParam.cornerTh == 0.0) {
|
|
result.algorithmParams.cornerParam.cornerTh = 130.0;
|
|
}
|
|
if (result.algorithmParams.cornerParam.jumpCornerTh_1 == 0.0) {
|
|
result.algorithmParams.cornerParam.jumpCornerTh_1 = 5.0;
|
|
}
|
|
if (result.algorithmParams.cornerParam.jumpCornerTh_2 == 0.0) {
|
|
result.algorithmParams.cornerParam.jumpCornerTh_2 = 2.0;
|
|
}
|
|
|
|
xml.skipCurrentElement();
|
|
}
|
|
// 线段参数
|
|
else if (xml.name() == "LineSegParam") {
|
|
result.algorithmParams.lineSegParam.segGapTh_y =
|
|
xml.attributes().value("segGapTh_y").toDouble();
|
|
result.algorithmParams.lineSegParam.segGapTh_z =
|
|
xml.attributes().value("segGapTh_z").toDouble();
|
|
result.algorithmParams.lineSegParam.maxDist =
|
|
xml.attributes().value("maxDist").toDouble();
|
|
|
|
// 设置默认值
|
|
if (result.algorithmParams.lineSegParam.segGapTh_y == 0.0) {
|
|
result.algorithmParams.lineSegParam.segGapTh_y = 5.0;
|
|
}
|
|
if (result.algorithmParams.lineSegParam.segGapTh_z == 0.0) {
|
|
result.algorithmParams.lineSegParam.segGapTh_z = 10.0;
|
|
}
|
|
if (result.algorithmParams.lineSegParam.maxDist == 0.0) {
|
|
result.algorithmParams.lineSegParam.maxDist = 50.0;
|
|
}
|
|
|
|
xml.skipCurrentElement();
|
|
}
|
|
// 离群点过滤参数
|
|
else if (xml.name() == "OutlierFilterParam") {
|
|
result.algorithmParams.filterParam.continuityTh =
|
|
xml.attributes().value("continuityTh").toDouble();
|
|
result.algorithmParams.filterParam.outlierTh =
|
|
xml.attributes().value("outlierTh").toDouble();
|
|
|
|
// 设置默认值
|
|
if (result.algorithmParams.filterParam.continuityTh == 0.0) {
|
|
result.algorithmParams.filterParam.continuityTh = 5.0;
|
|
}
|
|
if (result.algorithmParams.filterParam.outlierTh == 0.0) {
|
|
result.algorithmParams.filterParam.outlierTh = 3.0;
|
|
}
|
|
|
|
xml.skipCurrentElement();
|
|
}
|
|
// 树生长参数
|
|
else if (xml.name() == "TreeGrowParam") {
|
|
result.algorithmParams.growParam.yDeviation_max =
|
|
xml.attributes().value("yDeviation_max").toDouble();
|
|
result.algorithmParams.growParam.zDeviation_max =
|
|
xml.attributes().value("zDeviation_max").toDouble();
|
|
result.algorithmParams.growParam.maxLineSkipNum =
|
|
xml.attributes().value("maxLineSkipNum").toInt();
|
|
result.algorithmParams.growParam.maxSkipDistance =
|
|
xml.attributes().value("maxSkipDistance").toDouble();
|
|
result.algorithmParams.growParam.minLTypeTreeLen =
|
|
xml.attributes().value("minLTypeTreeLen").toDouble();
|
|
result.algorithmParams.growParam.minVTypeTreeLen =
|
|
xml.attributes().value("minVTypeTreeLen").toDouble();
|
|
|
|
// 设置默认值
|
|
if (result.algorithmParams.growParam.yDeviation_max == 0.0) {
|
|
result.algorithmParams.growParam.yDeviation_max = 20.0;
|
|
}
|
|
if (result.algorithmParams.growParam.zDeviation_max == 0.0) {
|
|
result.algorithmParams.growParam.zDeviation_max = 30.0;
|
|
}
|
|
if (result.algorithmParams.growParam.maxLineSkipNum == 0) {
|
|
result.algorithmParams.growParam.maxLineSkipNum = 5;
|
|
}
|
|
if (result.algorithmParams.growParam.minLTypeTreeLen == 0.0) {
|
|
result.algorithmParams.growParam.minLTypeTreeLen = 10.0;
|
|
}
|
|
if (result.algorithmParams.growParam.minVTypeTreeLen == 0.0) {
|
|
result.algorithmParams.growParam.minVTypeTreeLen = 10.0;
|
|
}
|
|
|
|
xml.skipCurrentElement();
|
|
}
|
|
else {
|
|
xml.skipCurrentElement();
|
|
}
|
|
}
|
|
}
|
|
|
|
// 解析调试参数
|
|
else if (xml.isStartElement() && xml.name() == "DebugParam") {
|
|
result.debugParam.enableDebug = xml.attributes().value("enableDebug").toInt();
|
|
result.debugParam.saveDebugImage = xml.attributes().value("saveDebugImage").toInt();
|
|
result.debugParam.printDetailLog = xml.attributes().value("printDetailLog").toInt();
|
|
result.debugParam.debugOutputPath = xml.attributes().value("debugOutputPath").toString().toStdString();
|
|
xml.skipCurrentElement();
|
|
}
|
|
|
|
// 解析服务端配置
|
|
else if (xml.isStartElement() && xml.name() == "LocalServerConfig") {
|
|
while (xml.readNextStartElement()) {
|
|
if (xml.name() == "ServerPort") {
|
|
result.serverPort = xml.attributes().value("port").toInt();
|
|
xml.skipCurrentElement();
|
|
} else if (xml.name() == "TcpPort") {
|
|
result.tcpPort = xml.attributes().value("port").toInt();
|
|
if (result.tcpPort == 0) {
|
|
result.tcpPort = 5800; // 默认值
|
|
}
|
|
xml.skipCurrentElement();
|
|
} else {
|
|
xml.skipCurrentElement();
|
|
}
|
|
}
|
|
}
|
|
|
|
// 解析服务器列表
|
|
else if (xml.isStartElement() && xml.name() == "Servers") {
|
|
while (xml.readNextStartElement()) {
|
|
if (xml.name() == "Server") {
|
|
WheelServerInfo server;
|
|
server.name = xml.attributes().value("name").toString().toStdString();
|
|
server.ip = xml.attributes().value("ip").toString().toStdString();
|
|
server.port = xml.attributes().value("port").toInt();
|
|
if (server.port == 0) {
|
|
server.port = 5800; // 默认端口
|
|
}
|
|
result.servers.push_back(server);
|
|
xml.skipCurrentElement();
|
|
} else {
|
|
xml.skipCurrentElement();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
file.close();
|
|
|
|
// 检查解析错误
|
|
if (xml.hasError()) {
|
|
LOG_ERROR("XML parsing error: %s\n", xml.errorString().toStdString().c_str());
|
|
return WheelMeasureConfigResult(); // 返回空结果
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool VrWheelMeasureConfig::SaveConfig(const std::string& filePath, WheelMeasureConfigResult& configResult)
|
|
{
|
|
// 使用QString处理可能包含中文的路径
|
|
QString qFilePath = QString::fromStdString(filePath);
|
|
QFile file(qFilePath);
|
|
|
|
// 打开文件进行写入
|
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
|
LOG_DEBUG("Failed to open file for writing: %s\n", filePath.c_str());
|
|
return false;
|
|
}
|
|
|
|
// 使用QXmlStreamWriter写入XML内容
|
|
QXmlStreamWriter xml(&file);
|
|
xml.setAutoFormatting(true);
|
|
xml.setCodec("UTF-8");
|
|
xml.writeStartDocument();
|
|
xml.writeStartElement("WheelMeasureConfig");
|
|
|
|
// 保存相机配置
|
|
xml.writeStartElement("Cameras");
|
|
for (const auto& camera : configResult.cameras) {
|
|
xml.writeStartElement("Camera");
|
|
xml.writeAttribute("index", QString::number(camera.cameraIndex));
|
|
xml.writeAttribute("name", QString::fromStdString(camera.name));
|
|
xml.writeAttribute("ip", QString::fromStdString(camera.cameraIP));
|
|
xml.writeAttribute("enabled", QString::number(camera.enabled ? 1 : 0));
|
|
xml.writeEndElement(); // Camera
|
|
}
|
|
xml.writeEndElement(); // Cameras
|
|
|
|
// 保存相机调平参数
|
|
xml.writeStartElement("PlaneCalibParams");
|
|
for (const auto& calibParam : configResult.planeCalibParams) {
|
|
xml.writeStartElement("CameraCalib");
|
|
xml.writeAttribute("index", QString::number(calibParam.cameraIndex));
|
|
xml.writeAttribute("name", QString::fromStdString(calibParam.cameraName));
|
|
xml.writeAttribute("planeHeight", QString::number(calibParam.planeHeight, 'f', 6));
|
|
xml.writeAttribute("isCalibrated", QString::number(calibParam.isCalibrated ? 1 : 0));
|
|
|
|
// 保存planeCalib矩阵
|
|
QStringList planeCalibList;
|
|
for (int i = 0; i < 9; ++i) {
|
|
planeCalibList.append(QString::number(calibParam.planeCalib[i], 'f', 8));
|
|
}
|
|
xml.writeAttribute("planeCalib", planeCalibList.join(","));
|
|
|
|
// 保存invRMatrix矩阵
|
|
QStringList invRMatrixList;
|
|
for (int i = 0; i < 9; ++i) {
|
|
invRMatrixList.append(QString::number(calibParam.invRMatrix[i], 'f', 8));
|
|
}
|
|
xml.writeAttribute("invRMatrix", invRMatrixList.join(","));
|
|
|
|
xml.writeEndElement(); // CameraCalib
|
|
}
|
|
xml.writeEndElement(); // PlaneCalibParams
|
|
|
|
// 保存算法参数
|
|
xml.writeStartElement("AlgorithmParams");
|
|
|
|
// 角点参数
|
|
xml.writeStartElement("CornerParam");
|
|
xml.writeAttribute("minEndingGap", QString::number(configResult.algorithmParams.cornerParam.minEndingGap));
|
|
xml.writeAttribute("minEndingGap_z", QString::number(configResult.algorithmParams.cornerParam.minEndingGap_z));
|
|
xml.writeAttribute("scale", QString::number(configResult.algorithmParams.cornerParam.scale));
|
|
xml.writeAttribute("cornerTh", QString::number(configResult.algorithmParams.cornerParam.cornerTh));
|
|
xml.writeAttribute("jumpCornerTh_1", QString::number(configResult.algorithmParams.cornerParam.jumpCornerTh_1));
|
|
xml.writeAttribute("jumpCornerTh_2", QString::number(configResult.algorithmParams.cornerParam.jumpCornerTh_2));
|
|
xml.writeEndElement(); // CornerParam
|
|
|
|
// 线段参数
|
|
xml.writeStartElement("LineSegParam");
|
|
xml.writeAttribute("segGapTh_y", QString::number(configResult.algorithmParams.lineSegParam.segGapTh_y));
|
|
xml.writeAttribute("segGapTh_z", QString::number(configResult.algorithmParams.lineSegParam.segGapTh_z));
|
|
xml.writeAttribute("maxDist", QString::number(configResult.algorithmParams.lineSegParam.maxDist));
|
|
xml.writeEndElement(); // LineSegParam
|
|
|
|
// 离群点过滤参数
|
|
xml.writeStartElement("OutlierFilterParam");
|
|
xml.writeAttribute("continuityTh", QString::number(configResult.algorithmParams.filterParam.continuityTh));
|
|
xml.writeAttribute("outlierTh", QString::number(configResult.algorithmParams.filterParam.outlierTh));
|
|
xml.writeEndElement(); // OutlierFilterParam
|
|
|
|
// 树生长参数
|
|
xml.writeStartElement("TreeGrowParam");
|
|
xml.writeAttribute("yDeviation_max", QString::number(configResult.algorithmParams.growParam.yDeviation_max));
|
|
xml.writeAttribute("zDeviation_max", QString::number(configResult.algorithmParams.growParam.zDeviation_max));
|
|
xml.writeAttribute("maxLineSkipNum", QString::number(configResult.algorithmParams.growParam.maxLineSkipNum));
|
|
xml.writeAttribute("maxSkipDistance", QString::number(configResult.algorithmParams.growParam.maxSkipDistance));
|
|
xml.writeAttribute("minLTypeTreeLen", QString::number(configResult.algorithmParams.growParam.minLTypeTreeLen));
|
|
xml.writeAttribute("minVTypeTreeLen", QString::number(configResult.algorithmParams.growParam.minVTypeTreeLen));
|
|
xml.writeEndElement(); // TreeGrowParam
|
|
|
|
xml.writeEndElement(); // AlgorithmParams
|
|
|
|
// 保存调试参数
|
|
xml.writeStartElement("DebugParam");
|
|
xml.writeAttribute("enableDebug", QString::number(configResult.debugParam.enableDebug));
|
|
xml.writeAttribute("saveDebugImage", QString::number(configResult.debugParam.saveDebugImage));
|
|
xml.writeAttribute("printDetailLog", QString::number(configResult.debugParam.printDetailLog));
|
|
xml.writeAttribute("debugOutputPath", QString::fromStdString(configResult.debugParam.debugOutputPath));
|
|
xml.writeEndElement(); // DebugParam
|
|
|
|
// 保存服务端配置
|
|
xml.writeStartElement("LocalServerConfig");
|
|
xml.writeStartElement("ServerPort");
|
|
xml.writeAttribute("port", QString::number(configResult.serverPort));
|
|
xml.writeEndElement(); // ServerPort
|
|
xml.writeStartElement("TcpPort");
|
|
xml.writeAttribute("port", QString::number(configResult.tcpPort));
|
|
xml.writeEndElement(); // TcpPort
|
|
xml.writeEndElement(); // LocalServerConfig
|
|
|
|
// 保存服务器列表
|
|
xml.writeStartElement("Servers");
|
|
for (const auto& server : configResult.servers) {
|
|
xml.writeStartElement("Server");
|
|
xml.writeAttribute("name", QString::fromStdString(server.name));
|
|
xml.writeAttribute("ip", QString::fromStdString(server.ip));
|
|
xml.writeAttribute("port", QString::number(server.port));
|
|
xml.writeEndElement(); // Server
|
|
}
|
|
xml.writeEndElement(); // Servers
|
|
|
|
xml.writeEndElement(); // WheelMeasureConfig
|
|
xml.writeEndDocument();
|
|
|
|
file.close();
|
|
|
|
// 通知配置改变
|
|
if (m_notify) {
|
|
m_notify->OnConfigChanged(configResult);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void VrWheelMeasureConfig::SetConfigChangeNotify(IVrWheelMeasureConfigChangeNotify* notify)
|
|
{
|
|
m_notify = notify;
|
|
}
|