feat:WorkpieceRotor
-增加了logo -修改了软件名称
@ -194,6 +194,7 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
|
||||
if (debugElement)
|
||||
{
|
||||
debugElement->QueryBoolAttribute("enableDebug", &configResult.debugParam.enableDebug);
|
||||
debugElement->QueryBoolAttribute("crash", &configResult.debugParam.crash);
|
||||
debugElement->QueryBoolAttribute("saveDebugImage", &configResult.debugParam.saveDebugImage);
|
||||
debugElement->QueryBoolAttribute("savePointCloud", &configResult.debugParam.savePointCloud);
|
||||
debugElement->QueryBoolAttribute("printDetailLog", &configResult.debugParam.printDetailLog);
|
||||
@ -385,6 +386,7 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
|
||||
// 3. 保存调试参数
|
||||
XMLElement* debugElement = doc.NewElement("DebugParam");
|
||||
debugElement->SetAttribute("enableDebug", configResult.debugParam.enableDebug);
|
||||
debugElement->SetAttribute("crash", configResult.debugParam.crash);
|
||||
debugElement->SetAttribute("savePointCloud", configResult.debugParam.savePointCloud);
|
||||
debugElement->SetAttribute("saveDebugImage", configResult.debugParam.saveDebugImage);
|
||||
debugElement->SetAttribute("printDetailLog", configResult.debugParam.printDetailLog);
|
||||
|
||||
@ -1,407 +1,200 @@
|
||||
#include "VrBeltTearingConfig.h"
|
||||
#include "IVrBeltTearingConfig.h"
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include "VrLog.h"
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
#include <QString>
|
||||
#include <QTextCodec>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QXmlStreamWriter>
|
||||
#include "tinyxml2.h"
|
||||
|
||||
VrBeltTearingConfig::VrBeltTearingConfig()
|
||||
: m_notify(nullptr)
|
||||
using namespace tinyxml2;
|
||||
|
||||
namespace
|
||||
{
|
||||
const char* attr(const XMLElement* node, const char* name, const char* fallback = "")
|
||||
{
|
||||
const char* value = node ? node->Attribute(name) : nullptr;
|
||||
return value ? value : fallback;
|
||||
}
|
||||
}
|
||||
|
||||
VrBeltTearingConfig::~VrBeltTearingConfig()
|
||||
VrBeltTearingConfig::VrBeltTearingConfig() : m_notify(nullptr) {}
|
||||
VrBeltTearingConfig::~VrBeltTearingConfig() = default;
|
||||
|
||||
bool IVrBeltTearingConfig::CreateInstance(IVrBeltTearingConfig** output)
|
||||
{
|
||||
}
|
||||
// Also add the static CreateInstance method to the interface class
|
||||
bool IVrBeltTearingConfig::CreateInstance(IVrBeltTearingConfig** ppVrConfig)
|
||||
{
|
||||
if (!ppVrConfig) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*ppVrConfig = new VrBeltTearingConfig();
|
||||
if (!output) return false;
|
||||
*output = new VrBeltTearingConfig();
|
||||
return true;
|
||||
}
|
||||
|
||||
BeltTearingConfigResult VrBeltTearingConfig::LoadConfig(const std::string& filePath)
|
||||
{
|
||||
BeltTearingConfigResult 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;
|
||||
XMLDocument document;
|
||||
if (document.LoadFile(filePath.c_str()) != XML_SUCCESS) return result;
|
||||
const XMLElement* root = document.FirstChildElement("BeltTearingConfig");
|
||||
if (!root) return BeltTearingConfigResult();
|
||||
|
||||
const XMLElement* servers = root->FirstChildElement("ClientServers");
|
||||
for (const XMLElement* node = servers ? servers->FirstChildElement("Server") : nullptr; node; node = node->NextSiblingElement("Server"))
|
||||
{
|
||||
ServerInfo server;
|
||||
server.name = attr(node, "name");
|
||||
server.ip = attr(node, "ip");
|
||||
server.port = node->IntAttribute("port", 5900);
|
||||
if (server.port <= 0 || server.port > 65535) server.port = 5900;
|
||||
result.servers.push_back(server);
|
||||
}
|
||||
|
||||
// 使用QXmlStreamReader解析XML内容
|
||||
QXmlStreamReader xml(&file);
|
||||
|
||||
// 读取到根元素
|
||||
if (xml.readNextStartElement()) {
|
||||
if (xml.name() != "BeltTearingConfig") {
|
||||
xml.raiseError(QObject::tr("Not a BeltTearingConfig file"));
|
||||
}
|
||||
} else {
|
||||
xml.raiseError(QObject::tr("Failed to read root element"));
|
||||
|
||||
const XMLElement* algorithm = root->FirstChildElement("AlgorithmParams");
|
||||
const XMLElement* tearing = algorithm ? algorithm->FirstChildElement("BeltTearingParam") : nullptr;
|
||||
if (tearing)
|
||||
{
|
||||
BeltTearingParam& params = result.algorithmParams.beltTearingParam;
|
||||
params.scanXScale = tearing->DoubleAttribute("ScanXScale", params.scanXScale);
|
||||
params.scanYScale = tearing->DoubleAttribute("ScanYScale", params.scanYScale);
|
||||
params.differnceBinTh = tearing->DoubleAttribute("DiffernceBinTh", params.differnceBinTh);
|
||||
params.tearingMinLen = tearing->DoubleAttribute("TearingMinLen", params.tearingMinLen);
|
||||
params.tearingMinWidthY = tearing->DoubleAttribute("TearingMinWidthY", params.tearingMinWidthY);
|
||||
params.tearingMinDepthZ = tearing->DoubleAttribute("TearingMinDepthZ", params.tearingMinDepthZ);
|
||||
params.tearingMinGap = tearing->DoubleAttribute("TearingMinGap", params.tearingMinGap);
|
||||
params.sameGapTh = tearing->DoubleAttribute("SameGapTh", params.sameGapTh);
|
||||
params.gapChkWin = tearing->IntAttribute("GapChkWin", params.gapChkWin);
|
||||
}
|
||||
|
||||
// 解析XML内容
|
||||
while (!xml.atEnd() && !xml.hasError()) {
|
||||
xml.readNext();
|
||||
|
||||
// 解析服务器配置
|
||||
if (xml.isStartElement() && xml.name() == "ClientServers") {
|
||||
while (xml.readNextStartElement()) {
|
||||
if (xml.name() == "Server") {
|
||||
ServerInfo 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 > 65535) {
|
||||
server.port = 5900;
|
||||
}
|
||||
result.servers.push_back(server);
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 解析算法参数
|
||||
else if (xml.isStartElement() && xml.name() == "AlgorithmParams") {
|
||||
while (xml.readNextStartElement()) {
|
||||
// 皮带撕裂参数
|
||||
if (xml.name() == "BeltTearingParam") {
|
||||
// 读取简化的SDK兼容参数
|
||||
result.algorithmParams.beltTearingParam.scanXScale =
|
||||
xml.attributes().value("ScanXScale").toDouble();
|
||||
result.algorithmParams.beltTearingParam.scanYScale =
|
||||
xml.attributes().value("ScanYScale").toDouble();
|
||||
result.algorithmParams.beltTearingParam.differnceBinTh =
|
||||
xml.attributes().value("DiffernceBinTh").toDouble();
|
||||
result.algorithmParams.beltTearingParam.tearingMinLen =
|
||||
xml.attributes().value("TearingMinLen").toDouble();
|
||||
result.algorithmParams.beltTearingParam.tearingMinWidthY =
|
||||
xml.attributes().value("TearingMinWidthY").toDouble();
|
||||
result.algorithmParams.beltTearingParam.tearingMinDepthZ =
|
||||
xml.attributes().value("TearingMinDepthZ").toDouble();
|
||||
result.algorithmParams.beltTearingParam.tearingMinGap =
|
||||
xml.attributes().value("TearingMinGap").toDouble();
|
||||
result.algorithmParams.beltTearingParam.sameGapTh =
|
||||
xml.attributes().value("SameGapTh").toDouble();
|
||||
result.algorithmParams.beltTearingParam.gapChkWin =
|
||||
xml.attributes().value("GapChkWin").toInt();
|
||||
|
||||
// 设置默认值(如果参数不存在)
|
||||
if (result.algorithmParams.beltTearingParam.scanXScale == 0.0) {
|
||||
result.algorithmParams.beltTearingParam.scanXScale = 1.0;
|
||||
}
|
||||
if (result.algorithmParams.beltTearingParam.scanYScale == 0.0) {
|
||||
result.algorithmParams.beltTearingParam.scanYScale = 1.0;
|
||||
}
|
||||
if (result.algorithmParams.beltTearingParam.differnceBinTh == 0.0) {
|
||||
result.algorithmParams.beltTearingParam.differnceBinTh = 1.0;
|
||||
}
|
||||
if (result.algorithmParams.beltTearingParam.tearingMinLen == 0.0) {
|
||||
result.algorithmParams.beltTearingParam.tearingMinLen = 5.0;
|
||||
}
|
||||
if (result.algorithmParams.beltTearingParam.tearingMinGap == 0.0) {
|
||||
result.algorithmParams.beltTearingParam.tearingMinGap = 2.0;
|
||||
}
|
||||
if (result.algorithmParams.beltTearingParam.sameGapTh == 0.0) {
|
||||
result.algorithmParams.beltTearingParam.sameGapTh = 2.0;
|
||||
}
|
||||
if (result.algorithmParams.beltTearingParam.gapChkWin == 0) {
|
||||
result.algorithmParams.beltTearingParam.gapChkWin = 5;
|
||||
}
|
||||
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
// 监控参数
|
||||
else if (xml.name() == "MonitoringParam") {
|
||||
result.algorithmParams.monitoringParam.checkInterval =
|
||||
xml.attributes().value("CheckInterval").toInt();
|
||||
result.algorithmParams.monitoringParam.alertThreshold =
|
||||
xml.attributes().value("AlertThreshold").toDouble();
|
||||
|
||||
// 向后兼容:忽略不再使用的参数
|
||||
// maxHistorySize 已移除,不再读取
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
// 跳过不再使用的图像处理参数
|
||||
else if (xml.name() == "ImageProcessingParam") {
|
||||
// 向后兼容:跳过图像处理参数,不再使用
|
||||
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() == "QueueProcessParam") {
|
||||
int maxQueueSize = xml.attributes().value("maxQueueSize").toInt();
|
||||
int generationInterval = xml.attributes().value("generationInterval").toInt();
|
||||
int imageLineCount = xml.attributes().value("imageLineCount").toInt();
|
||||
|
||||
// 设置默认值(如果参数不存在或为0)
|
||||
result.queueProcessParam.maxQueueSize = (maxQueueSize > 0) ? maxQueueSize : 300;
|
||||
result.queueProcessParam.generationInterval = (generationInterval > 0) ? generationInterval : 100;
|
||||
result.queueProcessParam.imageLineCount = (imageLineCount > 0) ? imageLineCount : 300;
|
||||
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
|
||||
// 解析TCP点云发送参数。抽样仅用于发送,不影响算法和成像队列。
|
||||
else if (xml.isStartElement() && xml.name() == "PointCloudSendParam") {
|
||||
const QString enabled = xml.attributes().value("enabled").toString().toLower();
|
||||
const int lineStep = xml.attributes().value("lineStep").toInt();
|
||||
const int pointStep = xml.attributes().value("pointStep").toInt();
|
||||
|
||||
result.pointCloudSendParam.enabled = enabled.isEmpty() || enabled == "true" || enabled == "1";
|
||||
result.pointCloudSendParam.lineStep = (lineStep > 0) ? lineStep : 1;
|
||||
result.pointCloudSendParam.pointStep = (pointStep > 0) ? pointStep : 1;
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
|
||||
// 解析项目类型
|
||||
else if (xml.isStartElement() && xml.name() == "ProjectType") {
|
||||
QString typeStr = xml.attributes().value("type").toString();
|
||||
if (typeStr == "BeltMonitoring") {
|
||||
result.projectType = BeltTearingProjectType::BeltMonitoring;
|
||||
} else {
|
||||
result.projectType = BeltTearingProjectType::BeltTearing;
|
||||
}
|
||||
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 if (xml.name() == "ModbusTCPProtocol") {
|
||||
// 解析ModbusTCP协议类型,默认为简化协议
|
||||
QString protocolStr = xml.attributes().value("type").toString().toLower();
|
||||
if (protocolStr == "standard") {
|
||||
result.modbusTCPProtocol = ModbusTCPProtocolType::Standard;
|
||||
} else {
|
||||
result.modbusTCPProtocol = ModbusTCPProtocolType::Simplified; // 默认简化协议
|
||||
}
|
||||
xml.skipCurrentElement();
|
||||
} else {
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 解析相机配置
|
||||
else if (xml.isStartElement() && xml.name() == "Cameras") {
|
||||
while (xml.readNextStartElement()) {
|
||||
if (xml.name() == "Camera") {
|
||||
CameraParam camera;
|
||||
camera.name = xml.attributes().value("name").toString().toStdString();
|
||||
camera.cameraIP = xml.attributes().value("ip").toString().toStdString();
|
||||
result.cameras.push_back(camera);
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 解析串口配置(用于 Modbus RTU)
|
||||
else if (xml.isStartElement() && xml.name() == "SerialPort") {
|
||||
QString portName = xml.attributes().value("portName").toString();
|
||||
int baudRate = xml.attributes().value("baudRate").toInt();
|
||||
QString parity = xml.attributes().value("parity").toString();
|
||||
int dataBits = xml.attributes().value("dataBits").toInt();
|
||||
int stopBits = xml.attributes().value("stopBits").toInt();
|
||||
|
||||
// 如果配置文件中有串口配置,则使用配置值
|
||||
if (!portName.isEmpty()) {
|
||||
result.serialPortParam.portName = portName.toStdString();
|
||||
}
|
||||
if (baudRate > 0) {
|
||||
result.serialPortParam.baudRate = baudRate;
|
||||
}
|
||||
if (!parity.isEmpty() && parity.length() > 0) {
|
||||
result.serialPortParam.parity = parity.at(0).toLatin1();
|
||||
}
|
||||
if (dataBits > 0) {
|
||||
result.serialPortParam.dataBits = dataBits;
|
||||
}
|
||||
if (stopBits > 0) {
|
||||
result.serialPortParam.stopBits = stopBits;
|
||||
}
|
||||
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
const XMLElement* monitoring = algorithm ? algorithm->FirstChildElement("MonitoringParam") : nullptr;
|
||||
if (monitoring)
|
||||
{
|
||||
result.algorithmParams.monitoringParam.checkInterval = monitoring->IntAttribute("CheckInterval", result.algorithmParams.monitoringParam.checkInterval);
|
||||
result.algorithmParams.monitoringParam.alertThreshold = monitoring->DoubleAttribute("AlertThreshold", result.algorithmParams.monitoringParam.alertThreshold);
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
// 检查解析错误
|
||||
if (xml.hasError()) {
|
||||
LOG_ERROR("XML parsing error: %s\n", xml.errorString().toStdString().c_str());
|
||||
return BeltTearingConfigResult(); // 返回空结果
|
||||
|
||||
const XMLElement* debug = root->FirstChildElement("DebugParam");
|
||||
if (debug)
|
||||
{
|
||||
result.debugParam.enableDebug = debug->BoolAttribute("enableDebug", result.debugParam.enableDebug);
|
||||
result.debugParam.saveDebugImage = debug->BoolAttribute("saveDebugImage", result.debugParam.saveDebugImage);
|
||||
result.debugParam.printDetailLog = debug->BoolAttribute("printDetailLog", result.debugParam.printDetailLog);
|
||||
result.debugParam.debugOutputPath = attr(debug, "debugOutputPath");
|
||||
}
|
||||
const XMLElement* queue = root->FirstChildElement("QueueProcessParam");
|
||||
if (queue)
|
||||
{
|
||||
result.queueProcessParam.maxQueueSize = queue->IntAttribute("maxQueueSize", result.queueProcessParam.maxQueueSize);
|
||||
result.queueProcessParam.generationInterval = queue->IntAttribute("generationInterval", result.queueProcessParam.generationInterval);
|
||||
result.queueProcessParam.imageLineCount = queue->IntAttribute("imageLineCount", result.queueProcessParam.imageLineCount);
|
||||
}
|
||||
const XMLElement* pointCloud = root->FirstChildElement("PointCloudSendParam");
|
||||
if (pointCloud)
|
||||
{
|
||||
result.pointCloudSendParam.enabled = pointCloud->BoolAttribute("enabled", result.pointCloudSendParam.enabled);
|
||||
result.pointCloudSendParam.lineStep = pointCloud->IntAttribute("lineStep", result.pointCloudSendParam.lineStep);
|
||||
result.pointCloudSendParam.pointStep = pointCloud->IntAttribute("pointStep", result.pointCloudSendParam.pointStep);
|
||||
}
|
||||
const XMLElement* project = root->FirstChildElement("ProjectType");
|
||||
if (project && std::string(attr(project, "type")) == "BeltMonitoring") result.projectType = BeltTearingProjectType::BeltMonitoring;
|
||||
|
||||
const XMLElement* local = root->FirstChildElement("LocalServerConfig");
|
||||
if (local)
|
||||
{
|
||||
const XMLElement* serverPort = local->FirstChildElement("ServerPort");
|
||||
const XMLElement* tcpPort = local->FirstChildElement("TcpPort");
|
||||
const XMLElement* protocol = local->FirstChildElement("ModbusTCPProtocol");
|
||||
if (serverPort) result.serverPort = serverPort->IntAttribute("port", result.serverPort);
|
||||
if (tcpPort) result.tcpPort = tcpPort->IntAttribute("port", result.tcpPort);
|
||||
if (protocol && std::string(attr(protocol, "type")) == "standard") result.modbusTCPProtocol = ModbusTCPProtocolType::Standard;
|
||||
}
|
||||
const XMLElement* cameras = root->FirstChildElement("Cameras");
|
||||
for (const XMLElement* node = cameras ? cameras->FirstChildElement("Camera") : nullptr; node; node = node->NextSiblingElement("Camera"))
|
||||
{
|
||||
CameraParam camera;
|
||||
camera.name = attr(node, "name");
|
||||
camera.cameraIP = attr(node, "ip");
|
||||
result.cameras.push_back(camera);
|
||||
}
|
||||
const XMLElement* serial = root->FirstChildElement("SerialPort");
|
||||
if (serial)
|
||||
{
|
||||
result.serialPortParam.portName = attr(serial, "portName", result.serialPortParam.portName.c_str());
|
||||
result.serialPortParam.baudRate = serial->IntAttribute("baudRate", result.serialPortParam.baudRate);
|
||||
const char* parity = serial->Attribute("parity");
|
||||
if (parity && parity[0]) result.serialPortParam.parity = parity[0];
|
||||
result.serialPortParam.dataBits = serial->IntAttribute("dataBits", result.serialPortParam.dataBits);
|
||||
result.serialPortParam.stopBits = serial->IntAttribute("stopBits", result.serialPortParam.stopBits);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool VrBeltTearingConfig::SaveConfig(const std::string& filePath, BeltTearingConfigResult& configResult)
|
||||
bool VrBeltTearingConfig::SaveConfig(const std::string& filePath, BeltTearingConfigResult& config)
|
||||
{
|
||||
// 使用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"); // Explicitly set UTF-8 encoding to handle Chinese characters correctly
|
||||
xml.writeStartDocument();
|
||||
// 设置编码为UTF-8以正确处理中文
|
||||
xml.writeStartElement("BeltTearingConfig");
|
||||
|
||||
// 保存服务器配置
|
||||
if (!configResult.servers.empty()) {
|
||||
xml.writeStartElement("ClientServers");
|
||||
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
|
||||
XMLDocument document;
|
||||
document.InsertEndChild(document.NewDeclaration("xml version=\"1.0\" encoding=\"UTF-8\""));
|
||||
XMLElement* root = document.NewElement("BeltTearingConfig");
|
||||
document.InsertEndChild(root);
|
||||
if (!config.servers.empty())
|
||||
{
|
||||
XMLElement* servers = document.NewElement("ClientServers");
|
||||
root->InsertEndChild(servers);
|
||||
for (const ServerInfo& server : config.servers)
|
||||
{
|
||||
XMLElement* node = document.NewElement("Server");
|
||||
node->SetAttribute("name", server.name.c_str());
|
||||
node->SetAttribute("ip", server.ip.c_str());
|
||||
node->SetAttribute("port", server.port);
|
||||
servers->InsertEndChild(node);
|
||||
}
|
||||
xml.writeEndElement(); // ClientServers
|
||||
}
|
||||
|
||||
// 保存算法参数
|
||||
xml.writeStartElement("AlgorithmParams");
|
||||
|
||||
// 皮带撕裂参数 - 简化版本
|
||||
xml.writeStartElement("BeltTearingParam");
|
||||
xml.writeAttribute("ScanXScale", QString::number(configResult.algorithmParams.beltTearingParam.scanXScale));
|
||||
xml.writeAttribute("ScanYScale", QString::number(configResult.algorithmParams.beltTearingParam.scanYScale));
|
||||
xml.writeAttribute("DiffernceBinTh", QString::number(configResult.algorithmParams.beltTearingParam.differnceBinTh));
|
||||
xml.writeAttribute("TearingMinLen", QString::number(configResult.algorithmParams.beltTearingParam.tearingMinLen));
|
||||
xml.writeAttribute("TearingMinWidthY", QString::number(configResult.algorithmParams.beltTearingParam.tearingMinWidthY));
|
||||
xml.writeAttribute("TearingMinDepthZ", QString::number(configResult.algorithmParams.beltTearingParam.tearingMinDepthZ));
|
||||
xml.writeAttribute("TearingMinGap", QString::number(configResult.algorithmParams.beltTearingParam.tearingMinGap));
|
||||
xml.writeAttribute("SameGapTh", QString::number(configResult.algorithmParams.beltTearingParam.sameGapTh));
|
||||
xml.writeAttribute("GapChkWin", QString::number(configResult.algorithmParams.beltTearingParam.gapChkWin));
|
||||
xml.writeEndElement(); // BeltTearingParam
|
||||
|
||||
// 监控参数
|
||||
xml.writeStartElement("MonitoringParam");
|
||||
xml.writeAttribute("CheckInterval", QString::number(configResult.algorithmParams.monitoringParam.checkInterval));
|
||||
xml.writeAttribute("AlertThreshold", QString::number(configResult.algorithmParams.monitoringParam.alertThreshold));
|
||||
xml.writeEndElement(); // MonitoringParam
|
||||
|
||||
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("QueueProcessParam");
|
||||
xml.writeAttribute("maxQueueSize", QString::number(configResult.queueProcessParam.maxQueueSize));
|
||||
xml.writeAttribute("generationInterval", QString::number(configResult.queueProcessParam.generationInterval));
|
||||
xml.writeAttribute("imageLineCount", QString::number(configResult.queueProcessParam.imageLineCount));
|
||||
xml.writeEndElement(); // QueueProcessParam
|
||||
|
||||
// 保存TCP点云发送参数
|
||||
xml.writeStartElement("PointCloudSendParam");
|
||||
xml.writeAttribute("enabled", configResult.pointCloudSendParam.enabled ? "true" : "false");
|
||||
xml.writeAttribute("lineStep", QString::number(configResult.pointCloudSendParam.lineStep));
|
||||
xml.writeAttribute("pointStep", QString::number(configResult.pointCloudSendParam.pointStep));
|
||||
xml.writeEndElement(); // PointCloudSendParam
|
||||
|
||||
// 保存项目类型
|
||||
xml.writeStartElement("ProjectType");
|
||||
QString typeStr = (configResult.projectType == BeltTearingProjectType::BeltMonitoring) ? "BeltMonitoring" : "BeltTearing";
|
||||
xml.writeAttribute("type", typeStr);
|
||||
xml.writeEndElement(); // ProjectType
|
||||
|
||||
// 保存服务端配置
|
||||
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
|
||||
// 保存ModbusTCP协议类型
|
||||
xml.writeStartElement("ModbusTCPProtocol");
|
||||
QString protocolTypeStr = (configResult.modbusTCPProtocol == ModbusTCPProtocolType::Standard) ? "standard" : "simplified";
|
||||
xml.writeAttribute("type", protocolTypeStr);
|
||||
xml.writeEndElement(); // ModbusTCPProtocol
|
||||
xml.writeEndElement(); // LocalServerConfig
|
||||
|
||||
// 保存相机配置
|
||||
xml.writeStartElement("Cameras");
|
||||
for (const auto& camera : configResult.cameras) {
|
||||
xml.writeStartElement("Camera");
|
||||
xml.writeAttribute("name", QString::fromStdString(camera.name));
|
||||
xml.writeAttribute("ip", QString::fromStdString(camera.cameraIP));
|
||||
xml.writeEndElement(); // Camera
|
||||
XMLElement* algorithm = document.NewElement("AlgorithmParams");
|
||||
root->InsertEndChild(algorithm);
|
||||
XMLElement* tearing = document.NewElement("BeltTearingParam");
|
||||
algorithm->InsertEndChild(tearing);
|
||||
const BeltTearingParam& params = config.algorithmParams.beltTearingParam;
|
||||
tearing->SetAttribute("ScanXScale", params.scanXScale);
|
||||
tearing->SetAttribute("ScanYScale", params.scanYScale);
|
||||
tearing->SetAttribute("DiffernceBinTh", params.differnceBinTh);
|
||||
tearing->SetAttribute("TearingMinLen", params.tearingMinLen);
|
||||
tearing->SetAttribute("TearingMinWidthY", params.tearingMinWidthY);
|
||||
tearing->SetAttribute("TearingMinDepthZ", params.tearingMinDepthZ);
|
||||
tearing->SetAttribute("TearingMinGap", params.tearingMinGap);
|
||||
tearing->SetAttribute("SameGapTh", params.sameGapTh);
|
||||
tearing->SetAttribute("GapChkWin", params.gapChkWin);
|
||||
XMLElement* monitoring = document.NewElement("MonitoringParam");
|
||||
algorithm->InsertEndChild(monitoring);
|
||||
monitoring->SetAttribute("CheckInterval", config.algorithmParams.monitoringParam.checkInterval);
|
||||
monitoring->SetAttribute("AlertThreshold", config.algorithmParams.monitoringParam.alertThreshold);
|
||||
XMLElement* debug = document.NewElement("DebugParam");
|
||||
root->InsertEndChild(debug);
|
||||
debug->SetAttribute("enableDebug", config.debugParam.enableDebug);
|
||||
debug->SetAttribute("saveDebugImage", config.debugParam.saveDebugImage);
|
||||
debug->SetAttribute("printDetailLog", config.debugParam.printDetailLog);
|
||||
debug->SetAttribute("debugOutputPath", config.debugParam.debugOutputPath.c_str());
|
||||
XMLElement* queue = document.NewElement("QueueProcessParam");
|
||||
root->InsertEndChild(queue);
|
||||
queue->SetAttribute("maxQueueSize", config.queueProcessParam.maxQueueSize);
|
||||
queue->SetAttribute("generationInterval", config.queueProcessParam.generationInterval);
|
||||
queue->SetAttribute("imageLineCount", config.queueProcessParam.imageLineCount);
|
||||
XMLElement* pointCloud = document.NewElement("PointCloudSendParam");
|
||||
root->InsertEndChild(pointCloud);
|
||||
pointCloud->SetAttribute("enabled", config.pointCloudSendParam.enabled ? "true" : "false");
|
||||
pointCloud->SetAttribute("lineStep", config.pointCloudSendParam.lineStep);
|
||||
pointCloud->SetAttribute("pointStep", config.pointCloudSendParam.pointStep);
|
||||
XMLElement* project = document.NewElement("ProjectType");
|
||||
root->InsertEndChild(project);
|
||||
project->SetAttribute("type", config.projectType == BeltTearingProjectType::BeltMonitoring ? "BeltMonitoring" : "BeltTearing");
|
||||
XMLElement* local = document.NewElement("LocalServerConfig");
|
||||
root->InsertEndChild(local);
|
||||
XMLElement* serverPort = document.NewElement("ServerPort"); serverPort->SetAttribute("port", config.serverPort); local->InsertEndChild(serverPort);
|
||||
XMLElement* tcpPort = document.NewElement("TcpPort"); tcpPort->SetAttribute("port", config.tcpPort); local->InsertEndChild(tcpPort);
|
||||
XMLElement* protocol = document.NewElement("ModbusTCPProtocol"); protocol->SetAttribute("type", config.modbusTCPProtocol == ModbusTCPProtocolType::Standard ? "standard" : "simplified"); local->InsertEndChild(protocol);
|
||||
XMLElement* cameras = document.NewElement("Cameras"); root->InsertEndChild(cameras);
|
||||
for (const CameraParam& camera : config.cameras)
|
||||
{
|
||||
XMLElement* node = document.NewElement("Camera"); node->SetAttribute("name", camera.name.c_str()); node->SetAttribute("ip", camera.cameraIP.c_str()); cameras->InsertEndChild(node);
|
||||
}
|
||||
xml.writeEndElement(); // Cameras
|
||||
|
||||
// 保存串口配置(用于 Modbus RTU)
|
||||
xml.writeStartElement("SerialPort");
|
||||
xml.writeAttribute("portName", QString::fromStdString(configResult.serialPortParam.portName));
|
||||
xml.writeAttribute("baudRate", QString::number(configResult.serialPortParam.baudRate));
|
||||
xml.writeAttribute("parity", QString(QChar(configResult.serialPortParam.parity)));
|
||||
xml.writeAttribute("dataBits", QString::number(configResult.serialPortParam.dataBits));
|
||||
xml.writeAttribute("stopBits", QString::number(configResult.serialPortParam.stopBits));
|
||||
xml.writeEndElement(); // SerialPort
|
||||
|
||||
xml.writeEndElement(); // BeltTearingConfig
|
||||
xml.writeEndDocument();
|
||||
|
||||
file.close();
|
||||
|
||||
// 通知配置改变
|
||||
if (m_notify) {
|
||||
m_notify->OnConfigChanged(configResult);
|
||||
}
|
||||
|
||||
XMLElement* serial = document.NewElement("SerialPort");
|
||||
root->InsertEndChild(serial);
|
||||
serial->SetAttribute("portName", config.serialPortParam.portName.c_str());
|
||||
serial->SetAttribute("baudRate", config.serialPortParam.baudRate);
|
||||
serial->SetAttribute("parity", std::string(1, config.serialPortParam.parity).c_str());
|
||||
serial->SetAttribute("dataBits", config.serialPortParam.dataBits);
|
||||
serial->SetAttribute("stopBits", config.serialPortParam.stopBits);
|
||||
if (document.SaveFile(filePath.c_str()) != XML_SUCCESS) return false;
|
||||
if (m_notify) m_notify->OnConfigChanged(config);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -389,6 +389,10 @@ ConfigResult CVrConfig::LoadConfig(const std::string& filePath)
|
||||
result.debugParam.saveDebugImage = debugParamElement->BoolAttribute("saveDebugImage");
|
||||
if (debugParamElement->Attribute("printDetailLog"))
|
||||
result.debugParam.printDetailLog = debugParamElement->BoolAttribute("printDetailLog");
|
||||
if (debugParamElement->Attribute("crash"))
|
||||
{
|
||||
result.debugParam.crash = debugParamElement->BoolAttribute("crash");
|
||||
}
|
||||
if (debugParamElement->Attribute("debugOutputPath"))
|
||||
result.debugParam.debugOutputPath = debugParamElement->Attribute("debugOutputPath");
|
||||
}
|
||||
@ -740,6 +744,7 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
|
||||
debugParamElement->SetAttribute("savePointCloud", configResult.debugParam.savePointCloud);
|
||||
debugParamElement->SetAttribute("saveDebugImage", configResult.debugParam.saveDebugImage);
|
||||
debugParamElement->SetAttribute("printDetailLog", configResult.debugParam.printDetailLog);
|
||||
debugParamElement->SetAttribute("crash", configResult.debugParam.crash);
|
||||
debugParamElement->SetAttribute("debugOutputPath", configResult.debugParam.debugOutputPath.c_str());
|
||||
root->InsertEndChild(debugParamElement);
|
||||
|
||||
|
||||
@ -247,6 +247,10 @@ ConfigResult CVrConfig::LoadConfig(const std::string& filePath)
|
||||
XMLElement* debugParamElement = root->FirstChildElement("DebugParam");
|
||||
if (debugParamElement)
|
||||
{
|
||||
if (debugParamElement->Attribute("crash"))
|
||||
{
|
||||
result.debugParam.crash = debugParamElement->BoolAttribute("crash");
|
||||
}
|
||||
if (debugParamElement->Attribute("enableDebug"))
|
||||
result.debugParam.enableDebug = debugParamElement->BoolAttribute("enableDebug");
|
||||
if (debugParamElement->Attribute("savePointCloud"))
|
||||
@ -404,6 +408,7 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
|
||||
// 添加调试参数(在AlgorithmParams外面)
|
||||
XMLElement* debugParamElement = doc.NewElement("DebugParam");
|
||||
debugParamElement->SetAttribute("enableDebug", configResult.debugParam.enableDebug);
|
||||
debugParamElement->SetAttribute("crash", configResult.debugParam.crash);
|
||||
debugParamElement->SetAttribute("savePointCloud", configResult.debugParam.savePointCloud);
|
||||
debugParamElement->SetAttribute("saveDebugImage", configResult.debugParam.saveDebugImage);
|
||||
debugParamElement->SetAttribute("printDetailLog", configResult.debugParam.printDetailLog);
|
||||
|
||||
@ -710,6 +710,7 @@ void LoadDebugParam(XMLElement* root, VrDebugParam& param)
|
||||
}
|
||||
|
||||
param.enableDebug = element->BoolAttribute("enableDebug", param.enableDebug);
|
||||
param.crash = element->BoolAttribute("crash", param.crash);
|
||||
param.savePointCloud = element->BoolAttribute("savePointCloud", param.savePointCloud);
|
||||
param.saveDebugImage = element->BoolAttribute("saveDebugImage", param.saveDebugImage);
|
||||
param.printDetailLog = element->BoolAttribute("printDetailLog", param.printDetailLog);
|
||||
@ -720,6 +721,7 @@ void SaveDebugParam(XMLDocument& doc, XMLElement* root, const VrDebugParam& para
|
||||
{
|
||||
XMLElement* element = doc.NewElement("DebugParam");
|
||||
element->SetAttribute("enableDebug", param.enableDebug);
|
||||
element->SetAttribute("crash", param.crash);
|
||||
element->SetAttribute("savePointCloud", param.savePointCloud);
|
||||
element->SetAttribute("saveDebugImage", param.saveDebugImage);
|
||||
element->SetAttribute("printDetailLog", param.printDetailLog);
|
||||
|
||||
@ -281,6 +281,10 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
|
||||
XMLElement* debugParamElement = root->FirstChildElement("DebugParam");
|
||||
if (debugParamElement)
|
||||
{
|
||||
if (debugParamElement->Attribute("crash"))
|
||||
{
|
||||
configResult.debugParam.crash = debugParamElement->BoolAttribute("crash");
|
||||
}
|
||||
if (debugParamElement->Attribute("enableDebug"))
|
||||
configResult.debugParam.enableDebug = debugParamElement->BoolAttribute("enableDebug");
|
||||
if (debugParamElement->Attribute("savePointCloud"))
|
||||
@ -443,6 +447,7 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
|
||||
// 添加调试参数(在AlgorithmParams外面)
|
||||
XMLElement* debugParamElement = doc.NewElement("DebugParam");
|
||||
debugParamElement->SetAttribute("enableDebug", configResult.debugParam.enableDebug);
|
||||
debugParamElement->SetAttribute("crash", configResult.debugParam.crash);
|
||||
debugParamElement->SetAttribute("savePointCloud", configResult.debugParam.savePointCloud);
|
||||
debugParamElement->SetAttribute("saveDebugImage", configResult.debugParam.saveDebugImage);
|
||||
debugParamElement->SetAttribute("printDetailLog", configResult.debugParam.printDetailLog);
|
||||
|
||||
@ -214,6 +214,10 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
|
||||
XMLElement* debugParamElement = root->FirstChildElement("DebugParam");
|
||||
if (debugParamElement)
|
||||
{
|
||||
if (debugParamElement->Attribute("crash"))
|
||||
{
|
||||
configResult.debugParam.crash = debugParamElement->BoolAttribute("crash");
|
||||
}
|
||||
if (debugParamElement->Attribute("enableDebug"))
|
||||
configResult.debugParam.enableDebug = debugParamElement->BoolAttribute("enableDebug");
|
||||
if (debugParamElement->Attribute("savePointCloud"))
|
||||
@ -425,6 +429,7 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
|
||||
// 添加调试参数(在AlgorithmParams外面)
|
||||
XMLElement* debugParamElement = doc.NewElement("DebugParam");
|
||||
debugParamElement->SetAttribute("enableDebug", configResult.debugParam.enableDebug);
|
||||
debugParamElement->SetAttribute("crash", configResult.debugParam.crash);
|
||||
debugParamElement->SetAttribute("savePointCloud", configResult.debugParam.savePointCloud);
|
||||
debugParamElement->SetAttribute("saveDebugImage", configResult.debugParam.saveDebugImage);
|
||||
debugParamElement->SetAttribute("printDetailLog", configResult.debugParam.printDetailLog);
|
||||
|
||||
@ -327,6 +327,7 @@ struct VrAlgorithmParams
|
||||
VrFullImageCaptureParam fullImageCaptureParam; // 在线全图二维采集参数
|
||||
VrBinParam binParam; // 料框检测/默认料框参数
|
||||
VrRotorCoreParam rotorCoreParam; // 转子钢芯算法参数
|
||||
std::vector<std::pair<int, VrToolParam>> toolParamsByCamera;
|
||||
std::vector<VrPointCloudFilterParam> pointCloudFilterParams;
|
||||
|
||||
// 显式赋值构造函数,确保正确的深拷贝
|
||||
@ -341,6 +342,7 @@ struct VrAlgorithmParams
|
||||
fullImageCaptureParam = other.fullImageCaptureParam;
|
||||
binParam = other.binParam;
|
||||
rotorCoreParam = other.rotorCoreParam;
|
||||
toolParamsByCamera = other.toolParamsByCamera;
|
||||
pointCloudFilterParams = other.pointCloudFilterParams;
|
||||
}
|
||||
return *this;
|
||||
@ -357,6 +359,7 @@ struct VrAlgorithmParams
|
||||
, fullImageCaptureParam(other.fullImageCaptureParam)
|
||||
, binParam(other.binParam)
|
||||
, rotorCoreParam(other.rotorCoreParam)
|
||||
, toolParamsByCamera(other.toolParamsByCamera)
|
||||
, pointCloudFilterParams(other.pointCloudFilterParams)
|
||||
{
|
||||
}
|
||||
@ -370,6 +373,7 @@ struct VrAlgorithmParams
|
||||
*/
|
||||
struct ConfigResult
|
||||
{
|
||||
std::vector<std::pair<int, VrHandEyeCalibMatrix>> handEyeCalibMatricesByCamera;
|
||||
std::vector<DeviceInfo> cameraList;
|
||||
std::vector<DeviceInfo> deviceList;
|
||||
VrAlgorithmParams algorithmParams; // 算法参数
|
||||
@ -381,6 +385,7 @@ struct ConfigResult
|
||||
// 显式赋值构造函数,确保正确的深拷贝
|
||||
ConfigResult& operator=(const ConfigResult& other) {
|
||||
if (this != &other) {
|
||||
handEyeCalibMatricesByCamera = other.handEyeCalibMatricesByCamera;
|
||||
cameraList = other.cameraList;
|
||||
deviceList = other.deviceList;
|
||||
algorithmParams = other.algorithmParams;
|
||||
@ -394,7 +399,8 @@ struct ConfigResult
|
||||
|
||||
// 显式复制构造函数
|
||||
ConfigResult(const ConfigResult& other)
|
||||
: cameraList(other.cameraList)
|
||||
: handEyeCalibMatricesByCamera(other.handEyeCalibMatricesByCamera)
|
||||
, cameraList(other.cameraList)
|
||||
, deviceList(other.deviceList)
|
||||
, algorithmParams(other.algorithmParams)
|
||||
, handEyeCalibMatrix(other.handEyeCalibMatrix)
|
||||
|
||||
@ -55,6 +55,13 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
|
||||
return LOAD_CONFIG_INVALID_FORMAT;
|
||||
}
|
||||
|
||||
// 重读时清除按相机索引保存的数据,避免旧条目改变相机编号或覆盖新配置。
|
||||
configResult.cameraList.clear();
|
||||
configResult.algorithmParams.toolParamsByCamera.clear();
|
||||
configResult.handEyeCalibMatricesByCamera.clear();
|
||||
configResult.algorithmParams.toolParam = VrToolParam();
|
||||
configResult.handEyeCalibMatrix = VrHandEyeCalibMatrix();
|
||||
|
||||
// 解析摄像头列表
|
||||
XMLElement* camerasElement = root->FirstChildElement("Cameras");
|
||||
if (camerasElement)
|
||||
@ -138,6 +145,26 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
|
||||
if (toolParamElement->Attribute("offsetZ"))
|
||||
configResult.algorithmParams.toolParam.offsetZ = toolParamElement->DoubleAttribute("offsetZ");
|
||||
}
|
||||
XMLElement* toolParamsElement = algoParamsElement->FirstChildElement("ToolParamsByCamera");
|
||||
if (toolParamsElement)
|
||||
{
|
||||
for (XMLElement* item = toolParamsElement->FirstChildElement("ToolParam"); item; item = item->NextSiblingElement("ToolParam"))
|
||||
{
|
||||
const int cameraIndex = item->IntAttribute("cameraIndex", 0);
|
||||
VrToolParam value = cameraIndex == 1 ? configResult.algorithmParams.toolParam : VrToolParam();
|
||||
value.eulerOrder = item->IntAttribute("eulerOrder", value.eulerOrder);
|
||||
value.rotX = item->DoubleAttribute("rotX", value.rotX);
|
||||
value.rotY = item->DoubleAttribute("rotY", value.rotY);
|
||||
value.rotZ = item->DoubleAttribute("rotZ", value.rotZ);
|
||||
value.offsetX = item->DoubleAttribute("offsetX", value.offsetX);
|
||||
value.offsetY = item->DoubleAttribute("offsetY", value.offsetY);
|
||||
value.offsetZ = item->DoubleAttribute("offsetZ", value.offsetZ);
|
||||
if (cameraIndex > 0)
|
||||
{
|
||||
configResult.algorithmParams.toolParamsByCamera.push_back(std::make_pair(cameraIndex, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 类别映射独立于有序的曝光/增益/阈值参数列表。
|
||||
XMLElement* modelParamElement = algoParamsElement->FirstChildElement("WorkpieceClassMapping");
|
||||
@ -398,6 +425,10 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
|
||||
XMLElement* debugParamElement = root->FirstChildElement("DebugParam");
|
||||
if (debugParamElement)
|
||||
{
|
||||
if (debugParamElement->Attribute("crash"))
|
||||
{
|
||||
configResult.debugParam.crash = debugParamElement->BoolAttribute("crash");
|
||||
}
|
||||
if (debugParamElement->Attribute("enableDebug"))
|
||||
configResult.debugParam.enableDebug = debugParamElement->BoolAttribute("enableDebug");
|
||||
if (debugParamElement->Attribute("savePointCloud"))
|
||||
@ -449,6 +480,32 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
|
||||
else
|
||||
configResult.handEyeCalibMatrix.eulerOrder = 11; // 默认外旋ZYX
|
||||
}
|
||||
XMLElement* handEyeMatricesElement = root->FirstChildElement("HandEyeCalibMatrices");
|
||||
if (handEyeMatricesElement)
|
||||
{
|
||||
for (XMLElement* item = handEyeMatricesElement->FirstChildElement("HandEyeCalibMatrix");
|
||||
item;
|
||||
item = item->NextSiblingElement("HandEyeCalibMatrix"))
|
||||
{
|
||||
const int cameraIndex = item->IntAttribute("cameraIndex", 0);
|
||||
if (cameraIndex <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
VrHandEyeCalibMatrix value;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
char attrName[32];
|
||||
sprintf(attrName, "m%d", i);
|
||||
if (item->Attribute(attrName))
|
||||
{
|
||||
value.matrix[i] = item->DoubleAttribute(attrName);
|
||||
}
|
||||
}
|
||||
value.eulerOrder = item->IntAttribute("eulerOrder", 11);
|
||||
configResult.handEyeCalibMatricesByCamera.push_back(std::make_pair(cameraIndex, value));
|
||||
}
|
||||
}
|
||||
|
||||
// 解析 TCP/IP 文本服务端端口和 Modbus 寄存器配置
|
||||
XMLElement* tcpServerConfigElement = root->FirstChildElement("TcpServerConfig");
|
||||
@ -553,6 +610,24 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
|
||||
toolParamElement->SetAttribute("offsetY", configResult.algorithmParams.toolParam.offsetY);
|
||||
toolParamElement->SetAttribute("offsetZ", configResult.algorithmParams.toolParam.offsetZ);
|
||||
algoParamsElement->InsertEndChild(toolParamElement);
|
||||
if (!configResult.algorithmParams.toolParamsByCamera.empty())
|
||||
{
|
||||
XMLElement* list = doc.NewElement("ToolParamsByCamera");
|
||||
for (const auto& entry : configResult.algorithmParams.toolParamsByCamera)
|
||||
{
|
||||
XMLElement* item = doc.NewElement("ToolParam");
|
||||
item->SetAttribute("cameraIndex", entry.first);
|
||||
item->SetAttribute("eulerOrder", entry.second.eulerOrder);
|
||||
item->SetAttribute("rotX", entry.second.rotX);
|
||||
item->SetAttribute("rotY", entry.second.rotY);
|
||||
item->SetAttribute("rotZ", entry.second.rotZ);
|
||||
item->SetAttribute("offsetX", entry.second.offsetX);
|
||||
item->SetAttribute("offsetY", entry.second.offsetY);
|
||||
item->SetAttribute("offsetZ", entry.second.offsetZ);
|
||||
list->InsertEndChild(item);
|
||||
}
|
||||
algoParamsElement->InsertEndChild(list);
|
||||
}
|
||||
|
||||
const auto& modelParams = configResult.algorithmParams.modelParams;
|
||||
const VrWorkpieceModelParam& legacyModelParam = configResult.algorithmParams.modelParam;
|
||||
@ -689,6 +764,7 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
|
||||
// 添加调试参数(在AlgorithmParams外面)
|
||||
XMLElement* debugParamElement = doc.NewElement("DebugParam");
|
||||
debugParamElement->SetAttribute("enableDebug", configResult.debugParam.enableDebug);
|
||||
debugParamElement->SetAttribute("crash", configResult.debugParam.crash);
|
||||
debugParamElement->SetAttribute("savePointCloud", configResult.debugParam.savePointCloud);
|
||||
debugParamElement->SetAttribute("saveDebugImage", configResult.debugParam.saveDebugImage);
|
||||
debugParamElement->SetAttribute("printDetailLog", configResult.debugParam.printDetailLog);
|
||||
@ -718,6 +794,24 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
|
||||
// 保存旋转顺序
|
||||
handEyeElement->SetAttribute("eulerOrder", configResult.handEyeCalibMatrix.eulerOrder);
|
||||
root->InsertEndChild(handEyeElement);
|
||||
if (!configResult.handEyeCalibMatricesByCamera.empty())
|
||||
{
|
||||
XMLElement* handEyeMatricesElement = doc.NewElement("HandEyeCalibMatrices");
|
||||
for (const auto& entry : configResult.handEyeCalibMatricesByCamera)
|
||||
{
|
||||
XMLElement* item = doc.NewElement("HandEyeCalibMatrix");
|
||||
item->SetAttribute("cameraIndex", entry.first);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
char attrName[32];
|
||||
sprintf(attrName, "m%d", i);
|
||||
item->SetAttribute(attrName, entry.second.matrix[i]);
|
||||
}
|
||||
item->SetAttribute("eulerOrder", entry.second.eulerOrder);
|
||||
handEyeMatricesElement->InsertEndChild(item);
|
||||
}
|
||||
root->InsertEndChild(handEyeMatricesElement);
|
||||
}
|
||||
|
||||
// 添加 TCP/IP 文本服务端端口和 Modbus 寄存器配置
|
||||
XMLElement* tcpServerConfigElement = doc.NewElement("TcpServerConfig");
|
||||
|
||||
@ -336,16 +336,15 @@ void LogRotorAlgorithmInput(
|
||||
algorithmParams.toolParam.offsetZ);
|
||||
|
||||
LOG_INFO("[Rotor Debug] planeCalib: planeHeight=%.6f\n", groundCalibPara.planeHeight);
|
||||
LOG_INFO("[Rotor Debug] planeCalib=[%.9f %.9f %.9f; %.9f %.9f %.9f; %.9f %.9f %.9f]\n",
|
||||
LOG_INFO("[Rotor Debug] planeCalib=[%.6f %.6f %.6f; %.6f %.6f %.6f; %.6f %.6f %.6f]\n",
|
||||
groundCalibPara.planeCalib[0], groundCalibPara.planeCalib[1], groundCalibPara.planeCalib[2],
|
||||
groundCalibPara.planeCalib[3], groundCalibPara.planeCalib[4], groundCalibPara.planeCalib[5],
|
||||
groundCalibPara.planeCalib[6], groundCalibPara.planeCalib[7], groundCalibPara.planeCalib[8]);
|
||||
LOG_INFO("[Rotor Debug] invRMatrix=[%.9f %.9f %.9f; %.9f %.9f %.9f; %.9f %.9f %.9f]\n",
|
||||
LOG_INFO("[Rotor Debug] invRMatrix=[%.6f %.6f %.6f; %.6f %.6f %.6f; %.6f %.6f %.6f]\n",
|
||||
groundCalibPara.invRMatrix[0], groundCalibPara.invRMatrix[1], groundCalibPara.invRMatrix[2],
|
||||
groundCalibPara.invRMatrix[3], groundCalibPara.invRMatrix[4], groundCalibPara.invRMatrix[5],
|
||||
groundCalibPara.invRMatrix[6], groundCalibPara.invRMatrix[7], groundCalibPara.invRMatrix[8]);
|
||||
LOG_INFO("[Rotor Debug] handEye=[%.9f %.9f %.9f %.9f; %.9f %.9f %.9f %.9f; "
|
||||
"%.9f %.9f %.9f %.9f; %.9f %.9f %.9f %.9f]\n",
|
||||
LOG_INFO("[Rotor Debug] handEye=[%.6f %.6f %.6f %.6f; %.6f %.6f %.6f %.6f %.6f %.6f %.6f]\n",
|
||||
clibMatrix[0], clibMatrix[1], clibMatrix[2], clibMatrix[3],
|
||||
clibMatrix[4], clibMatrix[5], clibMatrix[6], clibMatrix[7],
|
||||
clibMatrix[8], clibMatrix[9], clibMatrix[10], clibMatrix[11],
|
||||
@ -714,15 +713,19 @@ int DetectPresenter::DetectStatorWorkpiecePosition(
|
||||
#ifdef _OUTPUT_DEBUG_DATA
|
||||
std::vector<std::vector<SVzNL3DPosition>> debugScanLines;
|
||||
#endif
|
||||
size_t totalPoints = 0;
|
||||
for (const auto& line : algorithmData) {
|
||||
totalPoints += line.size();
|
||||
if (debugParam.enableDebug && debugParam.printDetailLog)
|
||||
{
|
||||
size_t totalPoints = 0;
|
||||
for (const auto& line : algorithmData)
|
||||
{
|
||||
totalPoints += line.size();
|
||||
}
|
||||
LOG_INFO("[Rotor] entering wd_HRM_RotorCorePositioning: lines=%zu points=%zu objROIs=%zu "
|
||||
"spacerLength=%.3f spacerWidth=%.3f radius=%.3f height=%.3f useGroundCalibNormal=%d\n",
|
||||
algorithmData.size(), totalPoints, objROIs.size(),
|
||||
spacerParam.dWidth, spacerParam.dHeight,
|
||||
workpieceParam.radius, workpieceParam.height, algorithmParams.rotorCoreParam.useGroundCalibNormal);
|
||||
}
|
||||
LOG_INFO("[Rotor] entering wd_HRM_RotorCorePositioning: lines=%zu points=%zu objROIs=%zu "
|
||||
"spacerLength=%.3f spacerWidth=%.3f radius=%.3f height=%.3f useGroundCalibNormal=%d\n",
|
||||
algorithmData.size(), totalPoints, objROIs.size(),
|
||||
spacerParam.dWidth, spacerParam.dHeight,
|
||||
workpieceParam.radius, workpieceParam.height, algorithmParams.rotorCoreParam.useGroundCalibNormal);
|
||||
|
||||
#if 1
|
||||
try {
|
||||
@ -756,9 +759,9 @@ int DetectPresenter::DetectStatorWorkpiecePosition(
|
||||
}
|
||||
#endif
|
||||
|
||||
if (debugParam.enableDebug && debugParam.printDetailLog) {
|
||||
LogRawRotorOutputs(workpiecePositions);
|
||||
}
|
||||
// if (debugParam.enableDebug && debugParam.printDetailLog) {
|
||||
// LogRawRotorOutputs(workpiecePositions);
|
||||
// }
|
||||
if (algorithmResult != SUCCESS) {
|
||||
detectionResult.errorCode = algorithmResult;
|
||||
return algorithmResult;
|
||||
@ -843,8 +846,11 @@ int DetectPresenter::DetectStatorWorkpiecePosition(
|
||||
position.hasLeftImagePoint = true;
|
||||
position.leftImageX = workpiece.imgCenter.x;
|
||||
position.leftImageY = workpiece.imgCenter.y;
|
||||
LOG_INFO("Rotor output[%zu] algorithm 2D point: left2D=(%.3f,%.3f)\n",
|
||||
index, position.leftImageX, position.leftImageY);
|
||||
if (debugParam.enableDebug && debugParam.printDetailLog)
|
||||
{
|
||||
LOG_INFO("Rotor output[%zu] algorithm 2D point: left2D=(%.3f,%.3f)\n",
|
||||
index, position.leftImageX, position.leftImageY);
|
||||
}
|
||||
}
|
||||
else if (workpiece.workpieceType == kSpacerWorkpieceType)
|
||||
{
|
||||
@ -935,9 +941,9 @@ int DetectPresenter::DetectStatorWorkpiecePosition(
|
||||
position.yaw = robotRpy.yaw;
|
||||
|
||||
if (debugParam.enableDebug && debugParam.printDetailLog) {
|
||||
LOG_INFO("[Rotor Debug] finalOutput[%02zu]: id=%d -> %d, type=%d, value=%.3f, "
|
||||
"centerRobot=(%.3f,%.3f,%.3f), targetXYZ=(%.3f,%.3f,%.3f), "
|
||||
"RPY=(%.3f,%.3f,%.3f)\n",
|
||||
LOG_INFO("[Rotor Debug] final[%02zu]: id=%d->%d,type=%d,value=%.3f, "
|
||||
"robot=(%.3f,%.3f,%.3f),offset=(%.3f,%.3f,%.3f), "
|
||||
"rpy=(%.3f,%.3f,%.3f)\n",
|
||||
index,
|
||||
workpiece.id,
|
||||
position.sourceDetectionIndex,
|
||||
|
||||
@ -45,6 +45,39 @@ const uint16_t kWorkStatusError = 2;
|
||||
const int kModbusRegsPerPoint = 14; // workpieceType + reserved + x/y/z/r/p/y
|
||||
const int kSpacerWorkpieceType = 9;
|
||||
|
||||
bool resolveCameraParameters(const ConfigResult& config, int cameraIndex, VrToolParam& toolParam, VrHandEyeCalibMatrix& handEyeMatrix)
|
||||
{
|
||||
if (cameraIndex < 1 || cameraIndex > std::max(1, static_cast<int>(config.cameraList.size())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// 旧单份配置只属于相机 1,其他相机必须具备独立的工具参数和手眼矩阵。
|
||||
bool hasToolParam = cameraIndex == 1;
|
||||
bool hasHandEyeMatrix = cameraIndex == 1;
|
||||
if (cameraIndex == 1)
|
||||
{
|
||||
toolParam = config.algorithmParams.toolParam;
|
||||
handEyeMatrix = config.handEyeCalibMatrix;
|
||||
}
|
||||
for (const auto& item : config.algorithmParams.toolParamsByCamera)
|
||||
{
|
||||
if (item.first == cameraIndex)
|
||||
{
|
||||
toolParam = item.second;
|
||||
hasToolParam = true;
|
||||
}
|
||||
}
|
||||
for (const auto& item : config.handEyeCalibMatricesByCamera)
|
||||
{
|
||||
if (item.first == cameraIndex)
|
||||
{
|
||||
handEyeMatrix = item.second;
|
||||
hasHandEyeMatrix = true;
|
||||
}
|
||||
}
|
||||
return hasToolParam && hasHandEyeMatrix;
|
||||
}
|
||||
|
||||
struct ModbusCoordinateData
|
||||
{
|
||||
uint16_t workpieceType = 0;
|
||||
@ -647,6 +680,15 @@ int StatorWorkpiecePositionPresenter::StartWorkpieceDetection(int cameraIndex, b
|
||||
if (resolvedCameraIndex <= 0) {
|
||||
resolvedCameraIndex = 1;
|
||||
}
|
||||
VrToolParam toolParam;
|
||||
VrHandEyeCalibMatrix handEyeMatrix;
|
||||
if (!resolveCameraParameters(configResult, resolvedCameraIndex, toolParam, handEyeMatrix))
|
||||
{
|
||||
OnStatusUpdate(QString("相机 %1 的工具参数或手眼标定配置缺失,请先配置并保存。")
|
||||
.arg(resolvedCameraIndex).toStdString());
|
||||
FinishDetectionCycle();
|
||||
return ERR_CODE(DEV_ARG_INVAILD);
|
||||
}
|
||||
m_detectionCameraIndex.store(resolvedCameraIndex);
|
||||
m_autoStopHandled.store(false);
|
||||
|
||||
@ -748,6 +790,20 @@ int StatorWorkpiecePositionPresenter::LoadDebugImageAndDataAndDetect(
|
||||
return ERR_CODE(DEV_ARG_INVAILD);
|
||||
}
|
||||
|
||||
int cameraIndex = GetDetectIndex();
|
||||
if (cameraIndex <= 0)
|
||||
{
|
||||
cameraIndex = 1;
|
||||
}
|
||||
VrToolParam toolParam;
|
||||
VrHandEyeCalibMatrix handEyeMatrix;
|
||||
if (!resolveCameraParameters(configResult, cameraIndex, toolParam, handEyeMatrix))
|
||||
{
|
||||
OnStatusUpdate(QString("相机 %1 的工具参数或手眼标定配置缺失,请先配置并保存。")
|
||||
.arg(cameraIndex).toStdString());
|
||||
return ERR_CODE(DEV_ARG_INVAILD);
|
||||
}
|
||||
|
||||
if (!detectBin) {
|
||||
QImage sourceImage(QString::fromStdString(imageFilePath));
|
||||
if (sourceImage.isNull()) {
|
||||
@ -799,10 +855,6 @@ int StatorWorkpiecePositionPresenter::LoadDebugImageAndDataAndDetect(
|
||||
.arg(rgbImage.height()).toStdString());
|
||||
}
|
||||
|
||||
int cameraIndex = GetDetectIndex();
|
||||
if (cameraIndex <= 0) {
|
||||
cameraIndex = 1;
|
||||
}
|
||||
m_detectionCameraIndex.store(cameraIndex);
|
||||
m_debugDataSaveContext = CreateDebugDataSaveContext(
|
||||
m_pConfigManager->GetConfigResult().debugParam, cameraIndex);
|
||||
@ -1359,82 +1411,27 @@ int StatorWorkpiecePositionPresenter::InitApp()
|
||||
// 初始化算法参数(实现BasePresenter纯虚函数)
|
||||
int StatorWorkpiecePositionPresenter::InitAlgoParams()
|
||||
{
|
||||
LOG_DEBUG("initializing algorithm parameters\n");
|
||||
QString exePath = QCoreApplication::applicationFilePath();
|
||||
|
||||
// 从 ConfigManager 获取配置结果(包含手眼标定矩阵)
|
||||
ConfigResult configResult = m_pConfigManager->GetConfigResult();
|
||||
|
||||
// 从 config.xml 加载手眼标定矩阵
|
||||
SetDebugParam(configResult.debugParam);
|
||||
|
||||
CalibMatrix calibMatrix;
|
||||
memcpy(calibMatrix.clibMatrix, configResult.handEyeCalibMatrix.matrix, sizeof(double) * 16);
|
||||
const int cameraCount = std::max(1, static_cast<int>(configResult.cameraList.size()));
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_clibMatrixMutex);
|
||||
m_clibMatrixList.clear();
|
||||
m_clibMatrixList.push_back(calibMatrix);
|
||||
}
|
||||
|
||||
LOG_INFO("Loaded hand-eye calibration matrix from config.xml:\n");
|
||||
QString clibMatrixStr;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
clibMatrixStr.clear();
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
clibMatrixStr += QString::asprintf("%8.4f ", calibMatrix.clibMatrix[i * 4 + j]);
|
||||
for (int cameraIndex = 1; cameraIndex <= cameraCount; ++cameraIndex)
|
||||
{
|
||||
VrToolParam toolParam;
|
||||
VrHandEyeCalibMatrix handEyeMatrix;
|
||||
resolveCameraParameters(configResult, cameraIndex, toolParam, handEyeMatrix);
|
||||
CalibMatrix calibMatrix;
|
||||
memcpy(calibMatrix.clibMatrix, handEyeMatrix.matrix, sizeof(calibMatrix.clibMatrix));
|
||||
m_clibMatrixList.push_back(calibMatrix);
|
||||
}
|
||||
LOG_INFO(" %s\n", clibMatrixStr.toStdString().c_str());
|
||||
}
|
||||
|
||||
LOG_INFO("Total loaded %d hand-eye calibration matrices\n", 1);
|
||||
|
||||
const VrAlgorithmParams& xmlParams = configResult.algorithmParams;
|
||||
|
||||
LOG_INFO("Loaded XML params - Tool: eulerOrder=%d, rot=(%.3f,%.3f,%.3f), "
|
||||
"offset=(%.3f,%.3f,%.3f)\n",
|
||||
xmlParams.toolParam.eulerOrder,
|
||||
xmlParams.toolParam.rotX, xmlParams.toolParam.rotY, xmlParams.toolParam.rotZ,
|
||||
xmlParams.toolParam.offsetX, xmlParams.toolParam.offsetY, xmlParams.toolParam.offsetZ);
|
||||
LOG_INFO("Loaded 2D model params - minScore=%.3f, "
|
||||
"classToWorkpieceType={0:%d,1:%d,2:%d}; "
|
||||
"model package is resolved from the fixed platform directory\n",
|
||||
xmlParams.modelParam.minimumScore,
|
||||
xmlParams.modelParam.class0WorkpieceType,
|
||||
xmlParams.modelParam.class1WorkpieceType,
|
||||
xmlParams.modelParam.class2WorkpieceType);
|
||||
|
||||
LOG_INFO("Algorithm parameters initialized successfully\n");
|
||||
|
||||
// 循环打印所有相机的调平参数(添加安全检查)
|
||||
LOG_INFO("Loading plane calibration parameters for all cameras:\n");
|
||||
|
||||
if (!xmlParams.planeCalibParam.cameraCalibParams.empty()) {
|
||||
for (const auto& cameraParam : xmlParams.planeCalibParam.cameraCalibParams) {
|
||||
try {
|
||||
LOG_INFO("Camera %d (%s) calibration parameters:\n",
|
||||
cameraParam.cameraIndex, cameraParam.cameraName.c_str());
|
||||
LOG_INFO(" Is calibrated: %s\n", cameraParam.isCalibrated ? "YES" : "NO");
|
||||
LOG_INFO(" Plane height: %.3f\n", cameraParam.planeHeight);
|
||||
LOG_INFO(" Plane calibration matrix:\n");
|
||||
LOG_INFO(" [%.3f, %.3f, %.3f]\n", cameraParam.planeCalib[0], cameraParam.planeCalib[1], cameraParam.planeCalib[2]);
|
||||
LOG_INFO(" [%.3f, %.3f, %.3f]\n", cameraParam.planeCalib[3], cameraParam.planeCalib[4], cameraParam.planeCalib[5]);
|
||||
LOG_INFO(" [%.3f, %.3f, %.3f]\n", cameraParam.planeCalib[6], cameraParam.planeCalib[7], cameraParam.planeCalib[8]);
|
||||
LOG_INFO(" Inverse rotation matrix:\n");
|
||||
LOG_INFO(" [%.3f, %.3f, %.3f]\n", cameraParam.invRMatrix[0], cameraParam.invRMatrix[1], cameraParam.invRMatrix[2]);
|
||||
LOG_INFO(" [%.3f, %.3f, %.3f]\n", cameraParam.invRMatrix[3], cameraParam.invRMatrix[4], cameraParam.invRMatrix[5]);
|
||||
LOG_INFO(" [%.3f, %.3f, %.3f]\n", cameraParam.invRMatrix[6], cameraParam.invRMatrix[7], cameraParam.invRMatrix[8]);
|
||||
LOG_INFO(" --------------------------------\n");
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR("Exception while printing camera calibration parameters: %s\n", e.what());
|
||||
} catch (...) {
|
||||
LOG_ERROR("Unknown exception while printing camera calibration parameters\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING("No plane calibration parameters found in configuration\n");
|
||||
}
|
||||
|
||||
|
||||
LOG_INFO("Algorithm parameters loaded: cameras=%d\n", cameraCount);
|
||||
m_initializationComplete.store(true);
|
||||
return SUCCESS;
|
||||
}
|
||||
@ -1510,8 +1507,6 @@ int StatorWorkpiecePositionPresenter::ProcessAlgoDetection(std::vector<std::pair
|
||||
}
|
||||
ScopedAlgorithmProcessing processingGuard(m_algorithmProcessing);
|
||||
|
||||
LOG_INFO("[Algo Thread] Start real detection task using algorithm\n");
|
||||
|
||||
// 检查检测处理器是否已初始化
|
||||
if (!m_pDetectPresenter) {
|
||||
LOG_ERROR("DetectPresenter is null, cannot proceed with detection\n");
|
||||
@ -1526,25 +1521,24 @@ int StatorWorkpiecePositionPresenter::ProcessAlgoDetection(std::vector<std::pair
|
||||
// 本次处理使用配置快照;工件尺寸及公共转子参数使用触发时固定的副本。
|
||||
const ConfigResult configResult = m_pConfigManager->GetConfigResult();
|
||||
VrAlgorithmParams algorithmParams = configResult.algorithmParams;
|
||||
// 同一配置快照、同一相机编号解析整套参数,禁止回退到另一相机。
|
||||
VrHandEyeCalibMatrix handEyeMatrix;
|
||||
if (!resolveCameraParameters(configResult, detectionCameraIndex, algorithmParams.toolParam, handEyeMatrix))
|
||||
{
|
||||
LOG_ERROR("Missing tool or hand-eye parameters for camera %d\n", detectionCameraIndex);
|
||||
OnStatusUpdate(QString("相机 %1 的工具参数或手眼标定配置缺失。").arg(detectionCameraIndex).toStdString());
|
||||
return ERR_CODE(DEV_ARG_INVAILD);
|
||||
}
|
||||
// 工件类别映射属于公共配置,不能随二维曝光/增益/阈值列表切换。
|
||||
{
|
||||
std::lock_guard<std::mutex> sizeLock(m_workpieceSizeMutex);
|
||||
algorithmParams.rotorCoreParam = m_detectionRotorCoreParam;
|
||||
LOG_INFO("[Rotor] algorithm workpiece=%d radius=%.6f height=%.6f\n",
|
||||
m_detectionWorkpieceIndex, algorithmParams.rotorCoreParam.radius, algorithmParams.rotorCoreParam.height);
|
||||
}
|
||||
CalibMatrix currentClibMatrix;
|
||||
std::memcpy(currentClibMatrix.clibMatrix,
|
||||
configResult.handEyeCalibMatrix.matrix,
|
||||
handEyeMatrix.matrix,
|
||||
sizeof(currentClibMatrix.clibMatrix));
|
||||
|
||||
// 打印手眼标定矩阵
|
||||
LOG_INFO("[Algo Thread] Hand-Eye Calibration Matrix (Camera %d):\n", detectionCameraIndex);
|
||||
LOG_INFO(" [%.4f, %.4f, %.4f, %.4f]\n", currentClibMatrix.clibMatrix[0], currentClibMatrix.clibMatrix[1], currentClibMatrix.clibMatrix[2], currentClibMatrix.clibMatrix[3]);
|
||||
LOG_INFO(" [%.4f, %.4f, %.4f, %.4f]\n", currentClibMatrix.clibMatrix[4], currentClibMatrix.clibMatrix[5], currentClibMatrix.clibMatrix[6], currentClibMatrix.clibMatrix[7]);
|
||||
LOG_INFO(" [%.4f, %.4f, %.4f, %.4f]\n", currentClibMatrix.clibMatrix[8], currentClibMatrix.clibMatrix[9], currentClibMatrix.clibMatrix[10], currentClibMatrix.clibMatrix[11]);
|
||||
LOG_INFO(" [%.4f, %.4f, %.4f, %.4f]\n", currentClibMatrix.clibMatrix[12], currentClibMatrix.clibMatrix[13], currentClibMatrix.clibMatrix[14], currentClibMatrix.clibMatrix[15]);
|
||||
|
||||
const VrDebugParam debugParam = configResult.debugParam;
|
||||
|
||||
// 与参数页采用相同的相机配置查找规则,关闭时直接旁路整个过滤模块。
|
||||
@ -1561,11 +1555,14 @@ int StatorWorkpiecePositionPresenter::ProcessAlgoDetection(std::vector<std::pair
|
||||
}
|
||||
}
|
||||
const bool filterEnabled = cloudFilter && cloudFilter->enabled;
|
||||
LOG_INFO("[Rotor Cloud] raw input: camera=%d lines=%zu firstFrame=%llu lastFrame=%llu filterEnabled=%d\n",
|
||||
detectionCameraIndex, detectionDataCache.size(),
|
||||
detectionDataCache.empty() ? 0ULL : detectionDataCache.front().second.llFrameIdx,
|
||||
detectionDataCache.empty() ? 0ULL : detectionDataCache.back().second.llFrameIdx,
|
||||
filterEnabled ? 1 : 0);
|
||||
if (debugParam.enableDebug && debugParam.printDetailLog)
|
||||
{
|
||||
LOG_INFO("[Rotor Cloud] raw input: camera=%d lines=%zu firstFrame=%llu lastFrame=%llu filterEnabled=%d\n",
|
||||
detectionCameraIndex, detectionDataCache.size(),
|
||||
detectionDataCache.empty() ? 0ULL : detectionDataCache.front().second.llFrameIdx,
|
||||
detectionDataCache.empty() ? 0ULL : detectionDataCache.back().second.llFrameIdx,
|
||||
filterEnabled ? 1 : 0);
|
||||
}
|
||||
if (filterEnabled)
|
||||
{
|
||||
const int filterResult = m_pDetectPresenter->FilterPointCloud(detectionCameraIndex, detectionDataCache, *cloudFilter);
|
||||
@ -1581,12 +1578,10 @@ int StatorWorkpiecePositionPresenter::ProcessAlgoDetection(std::vector<std::pair
|
||||
OnStatusUpdate("扫描线数:" + std::to_string(detectionDataCache.size()) + ",正在算法检测...");
|
||||
|
||||
// 获取旋转顺序配置
|
||||
int eulerOrder = configResult.handEyeCalibMatrix.eulerOrder;
|
||||
LOG_INFO("[Algo Thread] Using euler order: %d\n", eulerOrder);
|
||||
int eulerOrder = handEyeMatrix.eulerOrder;
|
||||
|
||||
// 获取方向向量反向配置
|
||||
int dirVectorInvert = configResult.plcRobotServerConfig.dirVectorInvert;
|
||||
LOG_INFO("[Algo Thread] Using dir vector invert: %d\n", dirVectorInvert);
|
||||
|
||||
m_pendingDetectionTask.store(DETECTION_TASK_WORKPIECE);
|
||||
StatorWorkpiecePositionDetectionResult detectionResult;
|
||||
@ -1753,18 +1748,13 @@ int StatorWorkpiecePositionPresenter::ProcessAlgoDetection(std::vector<std::pair
|
||||
SaveDebug2DFinalImage(m_debugDataSaveContext.filePrefix, debugParam, detectionResult.image,
|
||||
!offlineImageBuffer.empty());
|
||||
}
|
||||
// 根据项目类型选择处理方式
|
||||
if (GetStatusCallback<IYStatorWorkpiecePositionStatus>()) {
|
||||
QString err = QString("错误:%1").arg(nRet);
|
||||
if (auto pStatus = GetStatusCallback<IYStatorWorkpiecePositionStatus>()) pStatus->OnStatusUpdate(QString("检测%1").arg(SUCCESS == nRet ? "成功": err).toStdString());
|
||||
}
|
||||
|
||||
LOG_INFO("[Algo Thread] rotor core positioning detected %zu workpieces time : %.2f ms\n",
|
||||
detectionResult.positions.size(), oTimeUtils.GetElapsedTimeInMilliSec());
|
||||
LOG_INFO("[Rotor] camera=%d result=%d workpieces=%zu time=%.2f ms\n",
|
||||
detectionCameraIndex, nRet, detectionResult.positions.size(), oTimeUtils.GetElapsedTimeInMilliSec());
|
||||
|
||||
if (nRet != SUCCESS) {
|
||||
m_pendingDetectionTask.store(DETECTION_TASK_WORKPIECE);
|
||||
LOG_ERROR("[Algo Thread] Detection failed, error: %d\n", nRet);
|
||||
OnStatusUpdate(QString("相机 %1 检测失败,错误码:%2").arg(detectionCameraIndex).arg(nRet).toStdString());
|
||||
detectionResult.cameraIndex = detectionCameraIndex;
|
||||
detectionResult.errorCode = nRet;
|
||||
if (auto pStatus = GetStatusCallback<IYStatorWorkpiecePositionStatus>()) {
|
||||
@ -1796,7 +1786,7 @@ int StatorWorkpiecePositionPresenter::ProcessAlgoDetection(std::vector<std::pair
|
||||
}
|
||||
|
||||
// 更新状态
|
||||
QString statusMsg = QString("检测完成,发现%1个工件").arg(detectionResult.positions.size());
|
||||
QString statusMsg = QString("相机 %1 检测完成,发现%2个工件").arg(detectionCameraIndex).arg(detectionResult.positions.size());
|
||||
if (auto pStatus = GetStatusCallback<IYStatorWorkpiecePositionStatus>()) pStatus->OnStatusUpdate(statusMsg.toStdString());
|
||||
|
||||
// 发送检测结果给TCP客户端
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
// 版本字符串
|
||||
#define STATORWORKPIECEPOSITION_APP_NAME "转子钢芯定位"
|
||||
#define STATORWORKPIECEPOSITION_APP_KEY "WorkpieceRotorApp"
|
||||
#define STATORWORKPIECEPOSITION_VERSION_STRING "1.0.7"
|
||||
#define STATORWORKPIECEPOSITION_VERSION_STRING "1.2.0"
|
||||
#define STATORWORKPIECEPOSITION_BUILD_STRING "1"
|
||||
#define STATORWORKPIECEPOSITION_FULL_VERSION_STRING "V" STATORWORKPIECEPOSITION_VERSION_STRING "_" STATORWORKPIECEPOSITION_BUILD_STRING
|
||||
|
||||
|
||||
@ -1,5 +1,22 @@
|
||||
# 1.2.0
|
||||
## build_1 20261008
|
||||
1. 增加了logo
|
||||
2. 修改了软件名称
|
||||
|
||||
# 1.1.2
|
||||
## build_1 20261007
|
||||
1. 更新了算法:会检测出下一层的工件
|
||||
2. 更新了模型
|
||||
|
||||
# 1.1.1
|
||||
## build_1 20261006
|
||||
1. 更新了模型
|
||||
|
||||
# 1.1.0
|
||||
## build_1 20261003
|
||||
1. 双相机方案:为了适配更换工具
|
||||
|
||||
# 1.0.7
|
||||
## build_1
|
||||
## build_1 20260930
|
||||
1. 更新算法:优化隔板检测
|
||||
|
||||
|
||||
@ -72,8 +72,7 @@ SOURCES += \
|
||||
main.cpp \
|
||||
mainwindow.cpp \
|
||||
dialogalgoarg.cpp \
|
||||
workpiecesizeactions.cpp \
|
||||
resultitem.cpp
|
||||
workpiecesizeactions.cpp
|
||||
|
||||
# 头文件
|
||||
HEADERS += \
|
||||
@ -89,15 +88,13 @@ HEADERS += \
|
||||
dialogalgoarg.h \
|
||||
rotornetworkconfigwidget.h \
|
||||
workpiecesizeactions.h \
|
||||
resultitem.h \
|
||||
Version.h
|
||||
|
||||
# UI文件
|
||||
FORMS += \
|
||||
mainwindow.ui \
|
||||
dialogalgoarg.ui \
|
||||
workpiecesizeactions.ui \
|
||||
resultitem.ui
|
||||
workpiecesizeactions.ui
|
||||
|
||||
# 资源文件
|
||||
RESOURCES += \
|
||||
|
||||
@ -25,8 +25,6 @@
|
||||
namespace
|
||||
{
|
||||
|
||||
const int kHandEyeConfigIndex = 1;
|
||||
|
||||
bool isIdentityMatrix(const double matrix[16])
|
||||
{
|
||||
for (int index = 0; index < 16; ++index)
|
||||
@ -121,10 +119,20 @@ void DialogAlgoarg::LoadConfigToUI()
|
||||
LoadBinDetectionParamsToUI(configData.algorithmParams.binParam);
|
||||
LoadRotorCoreParamsToUI(configData.algorithmParams.rotorCoreParam);
|
||||
if (m_toolExtrinsicWidget) {
|
||||
m_toolExtrinsicWidget->setData(
|
||||
toolParam.eulerOrder,
|
||||
toolParam.rotX, toolParam.rotY, toolParam.rotZ,
|
||||
toolParam.offsetX, toolParam.offsetY, toolParam.offsetZ);
|
||||
const int count = std::max(1, static_cast<int>(configData.cameraList.size()));
|
||||
for (int cameraIndex = 1; cameraIndex <= count; ++cameraIndex)
|
||||
{
|
||||
VrToolParam value = cameraIndex == 1 ? toolParam : VrToolParam();
|
||||
for (const auto& item : configData.algorithmParams.toolParamsByCamera)
|
||||
{
|
||||
if (item.first == cameraIndex)
|
||||
{
|
||||
value = item.second;
|
||||
}
|
||||
}
|
||||
m_toolExtrinsicWidget->setData(cameraIndex, value.eulerOrder, value.rotX, value.rotY,
|
||||
value.rotZ, value.offsetX, value.offsetY, value.offsetZ);
|
||||
}
|
||||
}
|
||||
|
||||
// 加载网络配置(JSON TCP 端口 + Modbus 寄存器地址)
|
||||
@ -176,7 +184,22 @@ void DialogAlgoarg::InitToolExtrinsicTab()
|
||||
}
|
||||
|
||||
m_toolExtrinsicWidget = ui->toolExtrinsicWidget;
|
||||
m_toolExtrinsicWidget->setCameraSelectorVisible(false);
|
||||
m_toolExtrinsicWidget->setCameraSelectorVisible(true);
|
||||
QVector<ToolExtrinsicCameraInfo> cameras;
|
||||
const ConfigResult config = m_pConfigManager ? m_pConfigManager->GetConfigResult() : ConfigResult();
|
||||
const int cameraCount = std::max(1, static_cast<int>(config.cameraList.size()));
|
||||
for (int cameraIndex = 1; cameraIndex <= cameraCount; ++cameraIndex)
|
||||
{
|
||||
ToolExtrinsicCameraInfo camera;
|
||||
camera.cameraIndex = cameraIndex;
|
||||
camera.displayName = QStringLiteral("相机 %1").arg(cameraIndex);
|
||||
if (cameraIndex <= static_cast<int>(config.cameraList.size()) && !config.cameraList[cameraIndex - 1].name.empty())
|
||||
{
|
||||
camera.displayName = QString::fromStdString(config.cameraList[cameraIndex - 1].name);
|
||||
}
|
||||
cameras.append(camera);
|
||||
}
|
||||
m_toolExtrinsicWidget->setCameraList(cameras);
|
||||
m_toolExtrinsicWidget->setTargetOffsetVisible(true);
|
||||
m_toolExtrinsicWidget->setAxialAngleRangeVisible(false);
|
||||
}
|
||||
@ -195,12 +218,21 @@ void DialogAlgoarg::InitHandEyeCalibTab()
|
||||
PathManager::GetInstance().GetAppConfigDirectory());
|
||||
|
||||
QVector<HandEyeCalibCameraInfo> cameraList;
|
||||
HandEyeCalibCameraInfo cameraInfo;
|
||||
cameraInfo.cameraIndex = kHandEyeConfigIndex;
|
||||
cameraInfo.displayName = QStringLiteral("手眼标定");
|
||||
cameraList.append(cameraInfo);
|
||||
const ConfigResult cameraConfig = m_pConfigManager ? m_pConfigManager->GetConfigResult() : ConfigResult();
|
||||
const int cameraCount = std::max(1, static_cast<int>(cameraConfig.cameraList.size()));
|
||||
for (int cameraIndex = 1; cameraIndex <= cameraCount; ++cameraIndex)
|
||||
{
|
||||
HandEyeCalibCameraInfo info;
|
||||
info.cameraIndex = cameraIndex;
|
||||
info.displayName = QStringLiteral("相机 %1").arg(cameraIndex);
|
||||
if (cameraIndex <= static_cast<int>(cameraConfig.cameraList.size()) && !cameraConfig.cameraList[cameraIndex - 1].name.empty())
|
||||
{
|
||||
info.displayName = QString::fromStdString(cameraConfig.cameraList[cameraIndex - 1].name);
|
||||
}
|
||||
cameraList.append(info);
|
||||
}
|
||||
m_handEyeCalibWidget->setCameraList(cameraList);
|
||||
m_handEyeCalibWidget->setCalibType(kHandEyeConfigIndex, 0);
|
||||
m_handEyeCalibWidget->setCalibType(1, 0);
|
||||
|
||||
connect(m_handEyeCalibWidget, &HandEyeCalibWidget::calibMatrixLoaded,
|
||||
this, &DialogAlgoarg::onCalibMatrixLoaded);
|
||||
@ -349,10 +381,23 @@ bool DialogAlgoarg::SaveConfigFromUI()
|
||||
StyledMessageBox::warning(this, "错误", "工具参数输入有误,请检查!");
|
||||
return false;
|
||||
}
|
||||
m_toolExtrinsicWidget->getData(
|
||||
toolParam.eulerOrder,
|
||||
toolParam.rotX, toolParam.rotY, toolParam.rotZ,
|
||||
toolParam.offsetX, toolParam.offsetY, toolParam.offsetZ);
|
||||
systemConfig.configResult.algorithmParams.toolParamsByCamera.clear();
|
||||
const int cameraCount = std::max(1, static_cast<int>(systemConfig.configResult.cameraList.size()));
|
||||
for (int cameraIndex = 1; cameraIndex <= cameraCount; ++cameraIndex)
|
||||
{
|
||||
VrToolParam value;
|
||||
if (!m_toolExtrinsicWidget->getData(cameraIndex, value.eulerOrder, value.rotX, value.rotY,
|
||||
value.rotZ, value.offsetX, value.offsetY, value.offsetZ))
|
||||
{
|
||||
StyledMessageBox::warning(this, tr("错误"), tr("相机 %1 的工具参数输入有误,请检查。").arg(cameraIndex));
|
||||
return false;
|
||||
}
|
||||
systemConfig.configResult.algorithmParams.toolParamsByCamera.push_back(std::make_pair(cameraIndex, value));
|
||||
if (cameraIndex == 1)
|
||||
{
|
||||
toolParam = value;
|
||||
}
|
||||
}
|
||||
|
||||
// 保存网络配置(JSON TCP 端口 + Modbus 寄存器地址)
|
||||
if (!SavePlcRobotServerConfigFromUI(systemConfig.configResult.plcRobotServerConfig)) {
|
||||
@ -360,11 +405,32 @@ bool DialogAlgoarg::SaveConfigFromUI()
|
||||
return false;
|
||||
}
|
||||
|
||||
// 保存手眼标定矩阵到配置
|
||||
if (!SaveCalibMatrixToConfig(systemConfig.configResult.handEyeCalibMatrix)) {
|
||||
StyledMessageBox::warning(this, "错误", "手眼标定矩阵输入有误,请检查!");
|
||||
return false;
|
||||
// 工具参数与手眼矩阵按同一相机编号成对保存,旧单份字段仅镜像相机 1。
|
||||
std::vector<std::pair<int, VrHandEyeCalibMatrix>> matrices;
|
||||
for (int cameraIndex = 1; cameraIndex <= cameraCount; ++cameraIndex)
|
||||
{
|
||||
VrHandEyeCalibMatrix matrix = cameraIndex == 1
|
||||
? systemConfig.configResult.handEyeCalibMatrix : VrHandEyeCalibMatrix();
|
||||
for (const auto& item : systemConfig.configResult.handEyeCalibMatricesByCamera)
|
||||
{
|
||||
if (item.first == cameraIndex)
|
||||
{
|
||||
matrix = item.second;
|
||||
}
|
||||
}
|
||||
bool calibrated = false;
|
||||
if (!m_handEyeCalibWidget || !m_handEyeCalibWidget->getCalibData(cameraIndex, matrix.matrix, calibrated))
|
||||
{
|
||||
StyledMessageBox::warning(this, tr("错误"), tr("相机 %1 的手眼标定矩阵输入有误,请检查。").arg(cameraIndex));
|
||||
return false;
|
||||
}
|
||||
matrices.push_back(std::make_pair(cameraIndex, matrix));
|
||||
if (cameraIndex == 1)
|
||||
{
|
||||
systemConfig.configResult.handEyeCalibMatrix = matrix;
|
||||
}
|
||||
}
|
||||
systemConfig.configResult.handEyeCalibMatricesByCamera = matrices;
|
||||
|
||||
// 更新并保存配置到文件
|
||||
if (!m_pConfigManager->UpdateFullConfig(systemConfig)) {
|
||||
@ -828,23 +894,21 @@ void DialogAlgoarg::LoadHandEyeCalibConfig()
|
||||
}
|
||||
|
||||
const ConfigResult configData = m_pConfigManager->GetConfigResult();
|
||||
const VrHandEyeCalibMatrix& calibMatrix = configData.handEyeCalibMatrix;
|
||||
m_handEyeCalibWidget->setCalibData(
|
||||
kHandEyeConfigIndex,
|
||||
calibMatrix.matrix,
|
||||
!isIdentityMatrix(calibMatrix.matrix));
|
||||
}
|
||||
|
||||
bool DialogAlgoarg::SaveCalibMatrixToConfig(VrHandEyeCalibMatrix& calibMatrix)
|
||||
{
|
||||
if (!m_handEyeCalibWidget)
|
||||
const int count = std::max(1, static_cast<int>(configData.cameraList.size()));
|
||||
for (int cameraIndex = 1; cameraIndex <= count; ++cameraIndex)
|
||||
{
|
||||
return false;
|
||||
const VrHandEyeCalibMatrix defaultMatrix;
|
||||
const VrHandEyeCalibMatrix* calibMatrix = cameraIndex == 1 ? &configData.handEyeCalibMatrix : &defaultMatrix;
|
||||
for (const auto& item : configData.handEyeCalibMatricesByCamera)
|
||||
{
|
||||
if (item.first == cameraIndex)
|
||||
{
|
||||
calibMatrix = &item.second;
|
||||
}
|
||||
}
|
||||
m_handEyeCalibWidget->setCalibData(cameraIndex, calibMatrix->matrix,
|
||||
!isIdentityMatrix(calibMatrix->matrix));
|
||||
}
|
||||
|
||||
bool isCalibrated = false;
|
||||
return m_handEyeCalibWidget->getCalibData(
|
||||
kHandEyeConfigIndex, calibMatrix.matrix, isCalibrated);
|
||||
}
|
||||
|
||||
void DialogAlgoarg::LoadPlcRobotServerConfigToUI(const VrPlcRobotServerConfig& config)
|
||||
|
||||
@ -64,7 +64,6 @@ private:
|
||||
bool SavePlcRobotServerConfigFromUI(VrPlcRobotServerConfig& config);
|
||||
|
||||
void LoadHandEyeCalibConfig();
|
||||
bool SaveCalibMatrixToConfig(VrHandEyeCalibMatrix& calibMatrix);
|
||||
|
||||
private:
|
||||
Ui::DialogAlgoarg *ui;
|
||||
|
||||
@ -459,6 +459,13 @@ QDoubleSpinBox { color: rgb(221, 225, 233); background-color: rgb(47, 48, 52); b
|
||||
<property name="font"><font><pointsize>16</pointsize></font></property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QGroupBox, QLabel { color: rgb(221, 225, 233); }
|
||||
QGroupBox#groupBox_modelDetectionRegion { border: 1px solid rgb(100, 100, 100); border-radius: 4px; margin-top: 20px; padding-top: 12px; spacing: 10px; }
|
||||
QGroupBox#groupBox_modelDetectionRegion::title { subcontrol-origin: margin; subcontrol-position: top left; left: 10px; padding: 0px 8px; }
|
||||
QGroupBox#groupBox_modelDetectionRegion::indicator { width: 28px; height: 28px; border: 2px solid rgb(180, 190, 205); border-radius: 3px; background-color: rgb(47, 48, 52); }
|
||||
QGroupBox#groupBox_modelDetectionRegion::indicator:unchecked { image: none; }
|
||||
QGroupBox#groupBox_modelDetectionRegion::indicator:checked { background-color: rgb(40, 120, 180); border-color: rgb(130, 190, 240); image: url(:/common/resource/checkbox_check.png); }
|
||||
QGroupBox#groupBox_modelDetectionRegion::indicator:hover { border-color: rgb(230, 240, 255); }
|
||||
QGroupBox#groupBox_modelDetectionRegion::indicator:disabled { background-color: rgb(60, 65, 75); border-color: rgb(110, 120, 135); }
|
||||
QSpinBox { color: rgb(221, 225, 233); background-color: rgb(47, 48, 52); border: 1px solid rgb(100, 100, 100); padding: 4px; }</string>
|
||||
</property>
|
||||
<property name="title"><string>2D采图区域(左目全图像素)</string></property>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#include "mainwindow.h"
|
||||
#include "mainwindow.h"
|
||||
#include "ui_mainwindow.h"
|
||||
#include "resultitem.h"
|
||||
#include "XyzRpyItem.h"
|
||||
#include "ResultListLayoutHelper.h"
|
||||
#include <QStandardItemModel>
|
||||
#include <QDebug>
|
||||
#include <QPainter>
|
||||
@ -15,6 +16,7 @@
|
||||
#include <QStringListModel>
|
||||
#include <QScreen>
|
||||
#include <QApplication>
|
||||
#include <QStatusBar>
|
||||
#include "VrLog.h"
|
||||
#include "VrDateUtils.h"
|
||||
#include <QIcon>
|
||||
@ -43,6 +45,41 @@
|
||||
#include "Version.h"
|
||||
#include "IVrUtils.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// 将算法返回的位置编号转换为结果卡片上的中文名称。
|
||||
QString workpiecePositionText(int workpieceType)
|
||||
{
|
||||
switch (workpieceType)
|
||||
{
|
||||
case 0:
|
||||
return QStringLiteral("中央");
|
||||
case 1:
|
||||
return QStringLiteral("上边");
|
||||
case 2:
|
||||
return QStringLiteral("下边");
|
||||
case 3:
|
||||
return QStringLiteral("左边");
|
||||
case 4:
|
||||
return QStringLiteral("右边");
|
||||
case 5:
|
||||
return QStringLiteral("左上角");
|
||||
case 6:
|
||||
return QStringLiteral("右上角");
|
||||
case 7:
|
||||
return QStringLiteral("左下角");
|
||||
case 8:
|
||||
return QStringLiteral("右下角");
|
||||
case 9:
|
||||
return QStringLiteral("隔板");
|
||||
default:
|
||||
return QStringLiteral("未知");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent)
|
||||
: QMainWindow(parent)
|
||||
, ui(new Ui::MainWindow)
|
||||
@ -115,7 +152,6 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
// 初始化右键菜单
|
||||
setupContextMenu();
|
||||
|
||||
updateStatusLog(tr("设备开始初始化..."));
|
||||
|
||||
// 初始化模块
|
||||
Init();
|
||||
@ -155,9 +191,7 @@ bool MainWindow::eventFilter(QObject* obj, QEvent* event)
|
||||
if (obj == m_versionLabel && event->type() == QEvent::MouseButtonPress) {
|
||||
const QString algoVersion = m_presenter ? m_presenter->GetAlgoVersion() : QString();
|
||||
AboutDialog dlg(STATORWORKPIECEPOSITION_APP_NAME,
|
||||
m_softwareVersionText,
|
||||
algoVersion,
|
||||
this);
|
||||
m_softwareVersionText, algoVersion, this);
|
||||
dlg.exec();
|
||||
return true;
|
||||
}
|
||||
@ -255,13 +289,16 @@ void MainWindow::addDetectionResult(const StatorWorkpiecePositionDetectionResult
|
||||
|
||||
// 显示每个工件的中心点坐标
|
||||
for (size_t i = 0; i < result.positions.size(); i++) {
|
||||
ResultItem* resultWidget = new ResultItem();
|
||||
XyzRpyItem* resultWidget = new XyzRpyItem();
|
||||
resultWidget->setAutoFillBackground(true);
|
||||
|
||||
// 设置中心点数据
|
||||
const WorkpiecePosition& workpiece = result.positions[i];
|
||||
resultWidget->setResultData(
|
||||
result.positions[i].workpieceId,
|
||||
result.positions[i],
|
||||
workpiece.workpieceId,
|
||||
QStringLiteral("工件"),
|
||||
workpiecePositionText(workpiece.workpieceType),
|
||||
static_cast<const PositionData<double>&>(workpiece),
|
||||
i == 0);
|
||||
|
||||
// 创建QListWidgetItem
|
||||
@ -335,9 +372,7 @@ void MainWindow::OnCameraCountChanged(int cameraCount)
|
||||
// 根据相机个数调整页面布局
|
||||
adjustLayoutForCameraCount(cameraCount);
|
||||
|
||||
updateStatusLog(cameraCount < 2
|
||||
? tr("系统使用单相机模式")
|
||||
: tr("系统使用双相机模式"));
|
||||
updateStatusLog(cameraCount < 2 ? tr("单相机模式") : tr("双相机模式"));
|
||||
}, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
@ -370,6 +405,7 @@ void MainWindow::OnWorkStatusChanged(WorkStatus status)
|
||||
|
||||
void MainWindow::updateWorkStatusLabel(WorkStatus status)
|
||||
{
|
||||
const bool statusChanged = m_workStatus != status;
|
||||
// 如果状态变为Working,清空检测日志(表示开始新的检测)
|
||||
if (status == WorkStatus::Working && m_workStatus != WorkStatus::Working)
|
||||
{
|
||||
@ -387,7 +423,10 @@ void MainWindow::updateWorkStatusLabel(WorkStatus status)
|
||||
ui->label_work->setText(statusText);
|
||||
|
||||
statusText = "【工作状态】" + statusText;
|
||||
updateStatusLog(statusText);
|
||||
if (statusChanged)
|
||||
{
|
||||
updateStatusLog(statusText);
|
||||
}
|
||||
|
||||
// 根据不同状态设置不同的样式和按钮启用状态
|
||||
switch (status) {
|
||||
@ -429,17 +468,15 @@ void MainWindow::updateDetectionResultDisplay(const StatorWorkpiecePositionDetec
|
||||
// 根据相机索引决定显示在哪个图像视图上
|
||||
if (!result.image.isNull()) {
|
||||
if (result.cameraIndex == 2) {
|
||||
// 第二个相机的结果显示在detect_image_2上
|
||||
updateStatusLog(tr("相机2检测图像更新"));
|
||||
// 第二个相机的结果显示在detect_image_2
|
||||
displayImageInSecondView(result.image);
|
||||
} else {
|
||||
// 第一个相机或默认显示在detect_image上
|
||||
updateStatusLog(tr("相机1检测图像更新"));
|
||||
// 第一个相机或默认显示在detect_image
|
||||
displayImage(result.image);
|
||||
}
|
||||
}
|
||||
|
||||
// 2D模型检测完成后会先推送图像结果,此时没有3D位姿,不刷新结果列表。
|
||||
// 2D模型检测完成后会先推送图像结果,此时没有3D位姿,不刷新结果列表
|
||||
if (!result.positions.empty()) {
|
||||
addDetectionResult(result);
|
||||
} else if (result.errorCode != 0) {
|
||||
@ -465,7 +502,7 @@ void MainWindow::on_btn_start_clicked()
|
||||
updateWorkStatusLabel(WorkStatus::Working);
|
||||
updateStatusLog(tr("开始工件检测,正在准备二维采集..."));
|
||||
|
||||
// 取图、二维推理、保存调试图和启动扫描均在后台执行,UI 只处理排队的通知。
|
||||
// 取图、二维推理、保存调试图和启动扫描均在后台执行,UI 只处理排队的通知
|
||||
try
|
||||
{
|
||||
m_startDetectionThread = std::thread([this]()
|
||||
@ -492,7 +529,7 @@ void MainWindow::on_btn_start_clicked()
|
||||
|
||||
QMetaObject::invokeMethod(this, [this, errorMessage]()
|
||||
{
|
||||
// 耗时操作已经结束,回收后才允许下一次按钮启动复用线程对象。
|
||||
// 耗时操作已经结束,回收后才允许下一次按钮启动复用线程对象
|
||||
m_startDetectionThread.join();
|
||||
m_startDetectionPending = false;
|
||||
if (!errorMessage.isEmpty())
|
||||
@ -520,12 +557,12 @@ void MainWindow::on_btn_stop_clicked()
|
||||
{
|
||||
if (m_startDetectionPending)
|
||||
{
|
||||
updateStatusLog(tr("正在准备检测,请等待启动完成"));
|
||||
updateStatusLog(tr("正在准备检测,请等待启动完成。"));
|
||||
return;
|
||||
}
|
||||
// 检查Presenter是否已初始化
|
||||
if (!m_presenter) {
|
||||
updateStatusLog(tr("系统未初始化,请等待初始化完成"));
|
||||
updateStatusLog(tr("系统未初始化,请等待初始化完成。"));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -537,13 +574,13 @@ void MainWindow::on_btn_camera_clicked()
|
||||
{
|
||||
// 检查是否有其他按钮已被选中
|
||||
if (m_selectedButton != nullptr && m_selectedButton != ui->btn_camera) {
|
||||
updateStatusLog(tr("请先关闭当前打开的配置窗口"));
|
||||
updateStatusLog(tr("请先关闭当前打开的配置窗口。"));
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查Presenter是否已初始化
|
||||
if (!m_presenter) {
|
||||
updateStatusLog(tr("系统未初始化,请等待初始化完成"));
|
||||
updateStatusLog(tr("系统未初始化,请等待初始化完成。"));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -570,13 +607,13 @@ void MainWindow::on_btn_algo_config_clicked()
|
||||
{
|
||||
// 检查是否有其他按钮已被选中
|
||||
if (m_selectedButton != nullptr && m_selectedButton != ui->btn_algo_config) {
|
||||
updateStatusLog(tr("请先关闭当前打开的配置窗口"));
|
||||
updateStatusLog(tr("请先关闭当前打开的配置窗口。"));
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查Presenter是否已初始化
|
||||
if (!m_presenter) {
|
||||
updateStatusLog(tr("系统未初始化,请等待初始化完成"));
|
||||
updateStatusLog(tr("系统未初始化,请等待初始化完成。"));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -635,13 +672,13 @@ void MainWindow::on_btn_camera_levelling_clicked()
|
||||
{
|
||||
// 检查是否有其他按钮已被选中
|
||||
if (m_selectedButton != nullptr && m_selectedButton != ui->btn_camera_levelling) {
|
||||
updateStatusLog(tr("请先关闭当前打开的配置窗口"));
|
||||
updateStatusLog(tr("请先关闭当前打开的配置窗口。"));
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查Presenter是否已初始化
|
||||
if (!m_presenter) {
|
||||
updateStatusLog(tr("系统未初始化,请等待初始化完成"));
|
||||
updateStatusLog(tr("系统未初始化,请等待初始化完成。"));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -685,7 +722,7 @@ void MainWindow::on_btn_test_clicked()
|
||||
}
|
||||
// 检查是否有其他按钮已被选中
|
||||
if (m_selectedButton != nullptr && m_selectedButton != ui->btn_test) {
|
||||
updateStatusLog(tr("请先关闭当前打开的配置窗口"));
|
||||
updateStatusLog(tr("请先关闭当前打开的配置窗口。"));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -949,41 +986,15 @@ void MainWindow::setupSingleCameraLayout()
|
||||
// 获取可用高度(已排除任务栏和20px边距)
|
||||
int availableHeight = getAvailableHeight();
|
||||
|
||||
// 单相机模式下的原始布局
|
||||
// detect_image: 占据左侧大部分空间,动态计算高度
|
||||
int detectImageTop = 140;
|
||||
int detectImageHeight = availableHeight - detectImageTop;
|
||||
ui->detect_image->setGeometry(20, detectImageTop, 1311, detectImageHeight);
|
||||
|
||||
// detect_image_2: 隐藏
|
||||
ui->detect_image_2->setVisible(false);
|
||||
|
||||
// 设备状态框架: 位于右上方,恢复原始尺寸
|
||||
ui->frame_dev->setGeometry(1344, 140, 556, 64);
|
||||
|
||||
// 检测结果列表: 位于右侧中间
|
||||
ui->detect_result_list->setGeometry(1344, 216, 556, 622);
|
||||
|
||||
// 单相机模式下恢复原有的垂直滚动设置
|
||||
ui->detect_result_list->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
||||
ui->detect_result_list->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
ui->detect_result_list->setFlow(QListView::TopToBottom); // 从上到下排列
|
||||
ui->detect_result_list->setWrapping(false); // 不换行,每行一个
|
||||
|
||||
// 单相机模式:设置gridSize,每行显示1个目标
|
||||
int gridWidth = 550;
|
||||
int gridHeight = 65;
|
||||
ui->detect_result_list->setGridSize(QSize(gridWidth, gridHeight));
|
||||
ResultListLayoutHelper::setupSingleCameraMode(
|
||||
ui->detect_result_list,
|
||||
ui->detect_image, ui->detect_image_2,
|
||||
ui->frame_dev, ui->detect_log,
|
||||
availableHeight);
|
||||
|
||||
// 恢复设备状态widget的原始布局
|
||||
m_deviceStatusWidget->setCameraCount(1);
|
||||
|
||||
// 日志: 位于右下方,动态计算高度
|
||||
int detectLogTop = 850;
|
||||
int detectLogHeight = availableHeight - detectLogTop;
|
||||
ui->detect_log->setGeometry(1344, detectLogTop, 556, detectLogHeight);
|
||||
|
||||
updateStatusLog(tr("页面布局已调整为单相机模式"));
|
||||
}
|
||||
|
||||
// 设置多相机模式布局
|
||||
@ -992,39 +1003,14 @@ void MainWindow::setupMultiCameraLayout()
|
||||
// 获取可用高度(已排除任务栏和20px边距)
|
||||
int availableHeight = getAvailableHeight();
|
||||
|
||||
// 多相机模式下的布局调整
|
||||
// detect_image: 缩小到左侧一半空间,动态计算高度
|
||||
int detectImageTop = 140;
|
||||
int detectImageHeight = availableHeight - detectImageTop;
|
||||
ui->detect_image->setGeometry(10, detectImageTop, 940, detectImageHeight);
|
||||
|
||||
// detect_image_2: 显示在右侧一半空间,动态计算高度
|
||||
ui->detect_image_2->setVisible(true);
|
||||
ui->detect_image_2->setGeometry(970, detectImageTop, 940, detectImageHeight);
|
||||
|
||||
// 设备状态框架: 移动到下方左侧,调整为更高的尺寸以适应纵向排布
|
||||
ui->frame_dev->setGeometry(10, 806, 180, 200);
|
||||
|
||||
// 检测结果列表: 移动到下方中间
|
||||
ui->detect_result_list->setGeometry(196, 806, 1380, 200);
|
||||
|
||||
// 多相机模式:设置gridSize,每行显示1个目标,水平排列
|
||||
ui->detect_result_list->setFlow(QListView::LeftToRight); // 水平排列
|
||||
ui->detect_result_list->setWrapping(false); // 不换行
|
||||
ui->detect_result_list->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
ui->detect_result_list->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
int gridWidth = 540;
|
||||
int gridHeight = 65;
|
||||
ui->detect_result_list->setGridSize(QSize(gridWidth, gridHeight));
|
||||
ResultListLayoutHelper::setupMultiCameraMode(
|
||||
ui->detect_result_list,
|
||||
ui->detect_image, ui->detect_image_2,
|
||||
ui->frame_dev, ui->detect_log,
|
||||
availableHeight);
|
||||
|
||||
m_deviceStatusWidget->setCameraCount(2);
|
||||
|
||||
// 日志: 移动到下方右侧,动态计算高度
|
||||
int detectLogTop = 806;
|
||||
int detectLogHeight = availableHeight - detectLogTop;
|
||||
ui->detect_log->setGeometry(1582, detectLogTop, 328, detectLogHeight);
|
||||
|
||||
updateStatusLog(tr("页面布局已调整为多相机模式"));
|
||||
}
|
||||
|
||||
// 处理相机点击事件
|
||||
@ -1081,7 +1067,7 @@ void MainWindow::showContextMenu(const QPoint& pos, QGraphicsView* view)
|
||||
{
|
||||
// 检查是否有检测数据可以保存
|
||||
if (!m_presenter) {
|
||||
updateStatusLog(tr("系统未初始化,无法保存数据"));
|
||||
updateStatusLog(tr("系统未初始化,无法保存数据。"));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1090,12 +1076,12 @@ void MainWindow::showContextMenu(const QPoint& pos, QGraphicsView* view)
|
||||
|
||||
// 检查是否是上次检测的相机
|
||||
if (cameraIndex != m_presenter->GetDetectIndex()) {
|
||||
updateStatusLog(tr("当前视图不是上次检测的相机,无法保存数据"));
|
||||
updateStatusLog(tr("当前视图不是上次检测的相机,无法保存数据。"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_presenter->GetDetectionDataCacheSize() == 0) {
|
||||
updateStatusLog(tr("没有检测数据可以保存"));
|
||||
updateStatusLog(tr("没有检测数据可以保存。"));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1107,14 +1093,14 @@ void MainWindow::showContextMenu(const QPoint& pos, QGraphicsView* view)
|
||||
void MainWindow::onSaveDetectionData()
|
||||
{
|
||||
if (!m_presenter) {
|
||||
updateStatusLog(tr("系统未初始化,无法保存数据"));
|
||||
updateStatusLog(tr("系统未初始化,无法保存数据。"));
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigManager* config = m_presenter->GetConfigManager();
|
||||
if (!config)
|
||||
{
|
||||
updateStatusLog(tr("配置模块未初始化,无法保存数据"));
|
||||
updateStatusLog(tr("配置模块未初始化,无法保存数据。"));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1138,8 +1124,6 @@ void MainWindow::onSaveDetectionData()
|
||||
// 保存数据
|
||||
if (saveDetectionDataToFile(fullPath, m_presenter->GetDetectIndex())) {
|
||||
updateStatusLog(tr("检测数据和2D图像已保存,基础文件:%1").arg(fileName.c_str()));
|
||||
} else {
|
||||
updateStatusLog(tr("保存检测数据失败"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1151,7 +1135,6 @@ bool MainWindow::saveDetectionDataToFile(const QString& filePath, int cameraInde
|
||||
int result = m_presenter->SaveDetectionDataToFile(filePath.toStdString());
|
||||
|
||||
if (result == 0) {
|
||||
updateStatusLog(tr("检测数据保存成功"));
|
||||
return true;
|
||||
} else {
|
||||
updateStatusLog(tr("保存数据失败,错误码:%1").arg(result));
|
||||
@ -1237,4 +1220,3 @@ void MainWindow::setupCPUSerialDisplay()
|
||||
|
||||
LOG_INFO("CPU Serial Number: %s\n", cpuSerial.toStdString().c_str());
|
||||
}
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>转子钢芯定位系统</string>
|
||||
<string>定子转子3D无序抓取</string>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
@ -55,29 +55,6 @@ color: rgb(239, 241, 245);</string>
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>40</x>
|
||||
<y>14</y>
|
||||
<width>431</width>
|
||||
<height>91</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>36</pointsize>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: rgb(239, 241, 245);
|
||||
background-color: rgba(255, 255, 255, 0);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>转子钢芯定位系统</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QListWidget" name="detect_result_list">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
@ -97,7 +74,7 @@ background-color: rgba(255, 255, 255, 0);</string>
|
||||
<widget class="QPushButton" name="btn_test">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>1218</x>
|
||||
<x>1305</x>
|
||||
<y>21</y>
|
||||
<width>220</width>
|
||||
<height>80</height>
|
||||
@ -120,7 +97,7 @@ border: none;</string>
|
||||
<widget class="QPushButton" name="btn_algo_config">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>728</x>
|
||||
<x>835</x>
|
||||
<y>21</y>
|
||||
<width>220</width>
|
||||
<height>80</height>
|
||||
@ -143,7 +120,7 @@ border: none;</string>
|
||||
<widget class="QPushButton" name="btn_camera_levelling">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>973</x>
|
||||
<x>1070</x>
|
||||
<y>21</y>
|
||||
<width>220</width>
|
||||
<height>80</height>
|
||||
@ -166,7 +143,7 @@ border: none;</string>
|
||||
<widget class="QPushButton" name="btn_camera">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>483</x>
|
||||
<x>600</x>
|
||||
<y>21</y>
|
||||
<width>220</width>
|
||||
<height>80</height>
|
||||
@ -204,9 +181,9 @@ border: none;</string>
|
||||
<widget class="QLabel" name="label_work">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>1450</x>
|
||||
<x>1570</x>
|
||||
<y>20</y>
|
||||
<width>311</width>
|
||||
<width>191</width>
|
||||
<height>81</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -262,8 +239,8 @@ background-color: rgba(255, 255, 255, 0);</string>
|
||||
<widget class="QPushButton" name="btn_start">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>1525</x>
|
||||
<y>21</y>
|
||||
<x>1550</x>
|
||||
<y>22</y>
|
||||
<width>80</width>
|
||||
<height>80</height>
|
||||
</rect>
|
||||
@ -284,7 +261,7 @@ background-color: rgba(255, 255, 255, 0);</string>
|
||||
<widget class="QPushButton" name="btn_stop">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>1645</x>
|
||||
<x>1650</x>
|
||||
<y>21</y>
|
||||
<width>80</width>
|
||||
<height>80</height>
|
||||
@ -303,6 +280,64 @@ background-color: rgba(255, 255, 255, 0);</string>
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QToolButton" name="title_logo">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>120</width>
|
||||
<height>120</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: transparent;
|
||||
border: none;
|
||||
padding: 0px;
|
||||
margin: 0px;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/resource/hrm.png</normaloff>:/resource/hrm.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>150</width>
|
||||
<height>80</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonIconOnly</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>120</x>
|
||||
<y>21</y>
|
||||
<width>461</width>
|
||||
<height>80</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>36</pointsize>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: rgb(239, 241, 245);
|
||||
background-color: rgba(255, 255, 255, 0);</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>定子转子3D无序抓取</string>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QFrame" name="frame_dev">
|
||||
<property name="geometry">
|
||||
@ -340,7 +375,6 @@ background-color: rgba(255, 255, 255, 0);</string>
|
||||
<zorder>groupBox</zorder>
|
||||
<zorder>detect_image</zorder>
|
||||
<zorder>detect_log</zorder>
|
||||
<zorder>label</zorder>
|
||||
<zorder>detect_result_list</zorder>
|
||||
<zorder>btn_test</zorder>
|
||||
<zorder>btn_algo_config</zorder>
|
||||
@ -383,7 +417,6 @@ background-color: rgba(255, 255, 255, 0);</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ImageView</class>
|
||||
@ -391,5 +424,8 @@ background-color: rgba(255, 255, 255, 0);</string>
|
||||
<header>ImageView.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
BIN
App/StatorWorkpiecePosition/WorkpieceRotorApp/resource/hrm.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
@ -1,11 +1,7 @@
|
||||
<RCC>
|
||||
<!-- 应用特定资源 -->
|
||||
<qresource prefix="/">
|
||||
<file>resource/logo.png</file>
|
||||
<file>resource/logo.ico</file>
|
||||
<file>resource/hrm.png</file>
|
||||
</qresource>
|
||||
|
||||
<!-- 公用资源(从 UICommon 引入) -->
|
||||
<!-- 注意:公用资源通过 UICommon 库自动引入,无需在此重复定义 -->
|
||||
<!-- 使用方式::/common/resource/xxx.png -->
|
||||
</RCC>
|
||||
|
||||
@ -1,74 +0,0 @@
|
||||
#include "resultitem.h"
|
||||
#include "ui_resultitem.h"
|
||||
|
||||
namespace {
|
||||
|
||||
QString WorkpiecePositionText(int workpieceType)
|
||||
{
|
||||
switch (workpieceType) {
|
||||
case 0: return QStringLiteral("中央");
|
||||
case 1: return QStringLiteral("上边");
|
||||
case 2: return QStringLiteral("下边");
|
||||
case 3: return QStringLiteral("左边");
|
||||
case 4: return QStringLiteral("右边");
|
||||
case 5: return QStringLiteral("左上角");
|
||||
case 6: return QStringLiteral("右上角");
|
||||
case 7: return QStringLiteral("左下角");
|
||||
case 8: return QStringLiteral("右下角");
|
||||
case 9: return QStringLiteral("隔板");
|
||||
default: return QStringLiteral("未知");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ResultItem::ResultItem(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
, ui(new Ui::ResultItem)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
// 强制应用样式表
|
||||
this->setAttribute(Qt::WA_StyledBackground, true);
|
||||
|
||||
// 初始化时设置样式
|
||||
setItemStyle();
|
||||
}
|
||||
|
||||
ResultItem::~ResultItem()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void ResultItem::setResultData(
|
||||
int workpieceIndex,
|
||||
const WorkpiecePosition& position,
|
||||
bool isCurrentPickup)
|
||||
{
|
||||
ui->result_id->setStyleSheet(isCurrentPickup
|
||||
? QStringLiteral("color: rgb(255, 0, 0); background-color: transparent;")
|
||||
: QStringLiteral("color: rgb(239, 241, 245); background-color: transparent;"));
|
||||
ui->result_id->setText(QString("工件 %1 位置:%2")
|
||||
.arg(workpieceIndex)
|
||||
.arg(WorkpiecePositionText(position.workpieceType)));
|
||||
|
||||
ui->result_xyz_rpy->setText(QString(
|
||||
"X:%1 Y:%2 Z:%3 R:%4 P:%5 Y:%6")
|
||||
.arg(position.x, 0, 'f', 3)
|
||||
.arg(position.y, 0, 'f', 3)
|
||||
.arg(position.z, 0, 'f', 3)
|
||||
.arg(position.roll, 0, 'f', 3)
|
||||
.arg(position.pitch, 0, 'f', 3)
|
||||
.arg(position.yaw, 0, 'f', 3));
|
||||
}
|
||||
|
||||
void ResultItem::setItemStyle()
|
||||
{
|
||||
// 设置边框作为分隔线
|
||||
this->setStyleSheet(
|
||||
"ResultItem { "
|
||||
" border: 1px solid #191A1C; "
|
||||
" border-radius: 0px; "
|
||||
"} "
|
||||
);
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
#ifndef RESULTITEM_H
|
||||
#define RESULTITEM_H
|
||||
|
||||
#include <QWidget>
|
||||
#include "IYStatorWorkpiecePositionStatus.h"
|
||||
|
||||
namespace Ui {
|
||||
class ResultItem;
|
||||
}
|
||||
|
||||
class ResultItem : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ResultItem(QWidget *parent = nullptr);
|
||||
~ResultItem();
|
||||
|
||||
// 设置检测结果数据
|
||||
void setResultData(int workpieceIndex, const WorkpiecePosition& position, bool isCurrentPickup);
|
||||
|
||||
// 设置样式
|
||||
void setItemStyle();
|
||||
|
||||
private:
|
||||
Ui::ResultItem *ui;
|
||||
};
|
||||
|
||||
#endif // RESULTITEM_H
|
||||
@ -1,130 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ResultItem</class>
|
||||
<widget class="QWidget" name="ResultItem">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>520</width>
|
||||
<height>60</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>520</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">background-color: rgb(37, 38, 42);
|
||||
</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_top">
|
||||
<property name="spacing">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="iconWidget" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>18</width>
|
||||
<height>18</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>18</width>
|
||||
<height>18</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">image: url(:/common/resource/result_icon.png);
|
||||
background-color: rgba(255, 255, 255, 0);</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="result_id">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>12</pointsize>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: rgb(239, 241, 245);
|
||||
background-color: transparent;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>工件 1</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_bottom">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="result_xyz_rpy">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Consolas</family>
|
||||
<pointsize>11</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color: rgb(200, 200, 200);
|
||||
background-color: transparent;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>X:0.000 Y:0.000 Z:0.000 R:0.000 P:0.000 Y:0.000</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
5010
App/StatorWorkpiecePosition/海瑞马/海瑞马.ai
Normal file
BIN
App/StatorWorkpiecePosition/海瑞马/海瑞马_画板 1 副本 2.jpg
Normal file
|
After Width: | Height: | Size: 238 KiB |
BIN
App/StatorWorkpiecePosition/海瑞马/海瑞马_画板 1 副本 2.png
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
App/StatorWorkpiecePosition/海瑞马/海瑞马_画板 1 副本 3.jpg
Normal file
|
After Width: | Height: | Size: 182 KiB |
BIN
App/StatorWorkpiecePosition/海瑞马/海瑞马_画板 1 副本 3.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
App/StatorWorkpiecePosition/海瑞马/海瑞马_画板 1 副本 4.jpg
Normal file
|
After Width: | Height: | Size: 249 KiB |
BIN
App/StatorWorkpiecePosition/海瑞马/海瑞马_画板 1 副本 4.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
App/StatorWorkpiecePosition/海瑞马/海瑞马_画板 1 副本 5.jpg
Normal file
|
After Width: | Height: | Size: 159 KiB |
BIN
App/StatorWorkpiecePosition/海瑞马/海瑞马_画板 1 副本 5.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
App/StatorWorkpiecePosition/海瑞马/海瑞马_画板 1 副本 6.jpg
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
App/StatorWorkpiecePosition/海瑞马/海瑞马_画板 1 副本 6.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
App/StatorWorkpiecePosition/海瑞马/海瑞马_画板 1 副本 7.jpg
Normal file
|
After Width: | Height: | Size: 160 KiB |
BIN
App/StatorWorkpiecePosition/海瑞马/海瑞马_画板 1 副本 7.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
App/StatorWorkpiecePosition/海瑞马/海瑞马_画板 1 副本.jpg
Normal file
|
After Width: | Height: | Size: 192 KiB |
BIN
App/StatorWorkpiecePosition/海瑞马/海瑞马_画板 1 副本.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
App/StatorWorkpiecePosition/海瑞马/海瑞马_画板 1.jpg
Normal file
|
After Width: | Height: | Size: 260 KiB |
BIN
App/StatorWorkpiecePosition/海瑞马/海瑞马_画板 1.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
@ -243,6 +243,10 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
|
||||
XMLElement* debugParamElement = root->FirstChildElement("DebugParam");
|
||||
if (debugParamElement)
|
||||
{
|
||||
if (debugParamElement->Attribute("crash"))
|
||||
{
|
||||
configResult.debugParam.crash = debugParamElement->BoolAttribute("crash");
|
||||
}
|
||||
if (debugParamElement->Attribute("enableDebug"))
|
||||
configResult.debugParam.enableDebug = debugParamElement->BoolAttribute("enableDebug");
|
||||
if (debugParamElement->Attribute("savePointCloud"))
|
||||
@ -423,6 +427,7 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
|
||||
// 添加调试参数
|
||||
XMLElement* debugParamElement = doc.NewElement("DebugParam");
|
||||
debugParamElement->SetAttribute("enableDebug", configResult.debugParam.enableDebug);
|
||||
debugParamElement->SetAttribute("crash", configResult.debugParam.crash);
|
||||
debugParamElement->SetAttribute("savePointCloud", configResult.debugParam.savePointCloud);
|
||||
debugParamElement->SetAttribute("saveDebugImage", configResult.debugParam.saveDebugImage);
|
||||
debugParamElement->SetAttribute("printDetailLog", configResult.debugParam.printDetailLog);
|
||||
|
||||
@ -258,6 +258,11 @@ WheelMeasureConfigResult VrWheelMeasureConfig::LoadConfig(const std::string& fil
|
||||
// 解析调试参数
|
||||
else if (xml.isStartElement() && xml.name() == "DebugParam") {
|
||||
result.debugParam.enableDebug = xml.attributes().value("enableDebug").toInt();
|
||||
if (xml.attributes().hasAttribute("crash"))
|
||||
{
|
||||
const QString crashValue = xml.attributes().value("crash").toString();
|
||||
result.debugParam.crash = crashValue == "1" || crashValue.compare("true", Qt::CaseInsensitive) == 0;
|
||||
}
|
||||
result.debugParam.savePointCloud = xml.attributes().value("savePointCloud").toInt();
|
||||
result.debugParam.saveDebugImage = xml.attributes().value("saveDebugImage").toInt();
|
||||
result.debugParam.printDetailLog = xml.attributes().value("printDetailLog").toInt();
|
||||
@ -475,6 +480,7 @@ bool VrWheelMeasureConfig::SaveConfig(const std::string& filePath, WheelMeasureC
|
||||
// 保存调试参数
|
||||
xml.writeStartElement("DebugParam");
|
||||
xml.writeAttribute("enableDebug", QString::number(configResult.debugParam.enableDebug));
|
||||
xml.writeAttribute("crash", QString::number(configResult.debugParam.crash));
|
||||
xml.writeAttribute("savePointCloud", QString::number(configResult.debugParam.savePointCloud));
|
||||
xml.writeAttribute("saveDebugImage", QString::number(configResult.debugParam.saveDebugImage));
|
||||
xml.writeAttribute("printDetailLog", QString::number(configResult.debugParam.printDetailLog));
|
||||
|
||||
@ -296,6 +296,10 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
|
||||
XMLElement* debugParamElement = root->FirstChildElement("DebugParam");
|
||||
if (debugParamElement)
|
||||
{
|
||||
if (debugParamElement->Attribute("crash"))
|
||||
{
|
||||
configResult.debugParam.crash = debugParamElement->BoolAttribute("crash");
|
||||
}
|
||||
if (debugParamElement->Attribute("enableDebug"))
|
||||
configResult.debugParam.enableDebug = debugParamElement->BoolAttribute("enableDebug");
|
||||
if (debugParamElement->Attribute("savePointCloud"))
|
||||
@ -470,6 +474,7 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
|
||||
// 添加调试参数(在AlgorithmParams外面)
|
||||
XMLElement* debugParamElement = doc.NewElement("DebugParam");
|
||||
debugParamElement->SetAttribute("enableDebug", configResult.debugParam.enableDebug);
|
||||
debugParamElement->SetAttribute("crash", configResult.debugParam.crash);
|
||||
debugParamElement->SetAttribute("savePointCloud", configResult.debugParam.savePointCloud);
|
||||
debugParamElement->SetAttribute("saveDebugImage", configResult.debugParam.saveDebugImage);
|
||||
debugParamElement->SetAttribute("printDetailLog", configResult.debugParam.printDetailLog);
|
||||
|
||||
@ -82,6 +82,7 @@ int CVrConfig::LoadConfig(const std::string& filePath, ConfigResult& configResul
|
||||
if (debugElement)
|
||||
{
|
||||
debugElement->QueryBoolAttribute("enableDebug", &configResult.debugParam.enableDebug);
|
||||
debugElement->QueryBoolAttribute("crash", &configResult.debugParam.crash);
|
||||
debugElement->QueryBoolAttribute("saveDebugImage", &configResult.debugParam.saveDebugImage);
|
||||
debugElement->QueryBoolAttribute("savePointCloud", &configResult.debugParam.savePointCloud);
|
||||
debugElement->QueryBoolAttribute("printDetailLog", &configResult.debugParam.printDetailLog);
|
||||
@ -157,6 +158,7 @@ bool CVrConfig::SaveConfig(const std::string& filePath, ConfigResult& configResu
|
||||
// 3. 保存调试参数
|
||||
XMLElement* debugElement = doc.NewElement("Debug");
|
||||
debugElement->SetAttribute("enableDebug", configResult.debugParam.enableDebug);
|
||||
debugElement->SetAttribute("crash", configResult.debugParam.crash);
|
||||
debugElement->SetAttribute("saveDebugImage", configResult.debugParam.saveDebugImage);
|
||||
debugElement->SetAttribute("savePointCloud", configResult.debugParam.savePointCloud);
|
||||
debugElement->SetAttribute("printDetailLog", configResult.debugParam.printDetailLog);
|
||||
|
||||
@ -7,7 +7,7 @@ target_platform: "windows-x64"
|
||||
model_family: "yolo8"
|
||||
output_contract: "task_specific"
|
||||
runtime:
|
||||
backend_library: "../../lib/yolo_backend_onnx_cpu.dll"
|
||||
backend_library: "../lib/yolo_backend_onnx_cpu.dll"
|
||||
input:
|
||||
width: 1280
|
||||
height: 1280
|
||||
|
||||