Files
CalibratorLauncher/widgets/signalmeasurementwidget.cpp
2026-01-02 19:20:35 +09:00

1761 lines
59 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "signalmeasurementwidget.h"
#include "overlaydialogswidget.h"
#include <QDateTime>
#include <QFileDialog>
#include <QApplication>
#include <QMouseEvent>
#include <QDebug>
#include <random>
#include <cmath>
// ============== 样式常量定义 ==============
const QString MeasurementModuleBase::CARD_STYLE =
"QWidget { background: white; border-radius: 8px; }";
const QString MeasurementModuleBase::INPUT_STYLE =
"QLineEdit, QSpinBox, QDoubleSpinBox { "
"background: white; border: 2px solid #E0E0E0; border-radius: 6px; "
"padding: 8px 12px; font-size: 14px; color: #263238; }"
"QLineEdit:focus, QSpinBox:focus, QDoubleSpinBox:focus { border-color: #1976D2; }";
const QString MeasurementModuleBase::PRIMARY_BTN_STYLE =
"QPushButton { background: #1976D2; color: white; border: none; border-radius: 6px; "
"padding: 8px 16px; font-size: 13px; font-weight: 500; }"
"QPushButton:hover { background: #1565C0; }"
"QPushButton:pressed { background: #0D47A1; }"
"QPushButton:disabled { background: #BDBDBD; }";
const QString MeasurementModuleBase::SECONDARY_BTN_STYLE =
"QPushButton { background: #455A64; color: white; border: none; border-radius: 6px; "
"padding: 8px 16px; font-size: 12px; font-weight: 500; }"
"QPushButton:hover { background: #546E7A; }"
"QPushButton:pressed { background: #37474F; }";
const QString MeasurementModuleBase::VALUE_DISPLAY_STYLE =
"QLabel { background: #ECEFF1; color: #1976D2; border: 2px solid #90CAF9; "
"border-radius: 6px; padding: 10px 14px; font-size: 18px; font-weight: bold; "
"font-family: 'Consolas', 'Monaco', monospace; }";
const QString MeasurementModuleBase::LABEL_STYLE =
"QLabel { font-size: 13px; color: #455A64; font-weight: 500; }";
const QString MeasurementModuleBase::GROUPBOX_STYLE =
"QGroupBox { font-size: 14px; font-weight: bold; color: #263238; "
"border: 2px solid #E0E0E0; border-radius: 8px; margin-top: 10px; padding-top: 14px; }"
"QGroupBox::title { subcontrol-origin: margin; left: 16px; padding: 0 8px; "
"background: white; }";
// ============== MeasurementModuleBase ==============
MeasurementModuleBase::MeasurementModuleBase(const QString &title, QWidget *parent)
: QWidget(parent), m_title(title)
{
m_mainLayout = new QVBoxLayout(this);
m_mainLayout->setContentsMargins(12, 12, 12, 12);
m_mainLayout->setSpacing(10);
setStyleSheet("background: #FAFAFA;");
}
QLineEdit *MeasurementModuleBase::createStyledInput(const QString &placeholder, int width)
{
auto *input = new QLineEdit(this);
input->setPlaceholderText(placeholder);
input->setFixedWidth(width > 0 ? width : 100);
input->setFixedHeight(36);
input->setStyleSheet(INPUT_STYLE);
return input;
}
QDoubleSpinBox *MeasurementModuleBase::createStyledSpinBox(double min, double max, int decimals, int width)
{
auto *spin = new QDoubleSpinBox(this);
spin->setRange(min, max);
spin->setDecimals(decimals);
spin->setFixedWidth(width > 0 ? width : 100);
spin->setFixedHeight(36);
spin->setButtonSymbols(QAbstractSpinBox::NoButtons);
spin->setStyleSheet(
"QDoubleSpinBox { background: white; border: 2px solid #E0E0E0; border-radius: 6px; "
"padding: 6px 12px 6px 12px; font-size: 13px; color: #263238; }"
"QDoubleSpinBox:focus { border-color: #1976D2; }");
// 创建加减按钮
auto *plusBtn = new QPushButton("+", this);
plusBtn->setFixedSize(36, 36);
plusBtn->setStyleSheet(
"QPushButton { background: #F5F5F5; border: 2px solid #E0E0E0; border-radius: 6px; "
"font-size: 20px; font-weight: bold; color: #263238; }"
"QPushButton:hover { background: #E8E8E8; }"
"QPushButton:pressed { background: #D0D0D0; }");
connect(plusBtn, &QPushButton::clicked, spin, &QDoubleSpinBox::stepUp);
auto *minusBtn = new QPushButton("-", this);
minusBtn->setFixedSize(36, 36);
minusBtn->setStyleSheet(
"QPushButton { background: #F5F5F5; border: 2px solid #E0E0E0; border-radius: 6px; "
"font-size: 20px; font-weight: bold; color: #263238; }"
"QPushButton:hover { background: #E8E8E8; }"
"QPushButton:pressed { background: #D0D0D0; }");
connect(minusBtn, &QPushButton::clicked, spin, &QDoubleSpinBox::stepDown);
// 将按钮存储到SpinBox的动态属性中以便外部可以访问
spin->setProperty("plusButton", QVariant::fromValue(plusBtn));
spin->setProperty("minusButton", QVariant::fromValue(minusBtn));
return spin;
}
QComboBox *MeasurementModuleBase::createStyledComboBox(const QStringList &items, int width)
{
auto *combo = new QComboBox(this);
combo->addItems(items);
combo->setFixedWidth(width > 0 ? width : 120);
combo->setFixedHeight(36);
combo->setStyleSheet(
"QComboBox { background: white; border: 2px solid #E0E0E0; border-radius: 6px; "
"padding: 6px 12px 6px 12px; font-size: 13px; color: #263238; }"
"QComboBox:focus { border-color: #1976D2; }"
"QComboBox::drop-down { subcontrol-origin: padding; subcontrol-position: center right; "
"width: 42px; border-left: 2px solid #E0E0E0; background: #F5F5F5; "
"border-top-right-radius: 4px; border-bottom-right-radius: 4px; }"
"QComboBox::drop-down:hover { background: #E8E8E8; }"
"QComboBox::drop-down:pressed { background: #D0D0D0; }"
"QComboBox QAbstractItemView { background: white; selection-background-color: #1976D2; "
"selection-color: white; outline: none; }"
"QComboBox QAbstractItemView::item { min-height: 42px; padding: 8px 12px; font-size: 13px; }"
"QComboBox QAbstractItemView::item:hover { background: #E3F2FD; }");
return combo;
}
QPushButton *MeasurementModuleBase::createPrimaryButton(const QString &text, int width)
{
auto *btn = new QPushButton(text, this);
btn->setFixedSize(width > 0 ? width : 90, 36);
btn->setCursor(Qt::PointingHandCursor);
btn->setStyleSheet(PRIMARY_BTN_STYLE);
return btn;
}
QPushButton *MeasurementModuleBase::createSecondaryButton(const QString &text, int width)
{
auto *btn = new QPushButton(text, this);
btn->setFixedSize(width > 0 ? width : 80, 36);
btn->setCursor(Qt::PointingHandCursor);
btn->setStyleSheet(SECONDARY_BTN_STYLE);
return btn;
}
QLabel *MeasurementModuleBase::createValueDisplay(const QString &value, int width)
{
auto *label = new QLabel(value, this);
label->setFixedSize(width > 0 ? width : 140, 42);
label->setAlignment(Qt::AlignCenter);
label->setStyleSheet(VALUE_DISPLAY_STYLE);
return label;
}
QGroupBox *MeasurementModuleBase::createStyledGroupBox(const QString &title)
{
auto *group = new QGroupBox(title, this);
group->setStyleSheet(GROUPBOX_STYLE);
return group;
}
QHBoxLayout *MeasurementModuleBase::createLabeledRow(const QString &label, QWidget *widget, const QString &unit)
{
auto *row = new QHBoxLayout();
row->setSpacing(12);
auto *lbl = new QLabel(label, this);
lbl->setFixedWidth(100);
lbl->setStyleSheet(LABEL_STYLE);
lbl->setAlignment(Qt::AlignVCenter | Qt::AlignLeft);
lbl->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
row->addWidget(lbl, 0, Qt::AlignVCenter);
row->addWidget(widget, 0, Qt::AlignVCenter);
// 如果是SpinBox添加+/-按钮
if (auto *spin = qobject_cast<QDoubleSpinBox *>(widget))
{
if (spin->property("minusButton").isValid())
{
auto *minusBtn = qvariant_cast<QPushButton *>(spin->property("minusButton"));
auto *plusBtn = qvariant_cast<QPushButton *>(spin->property("plusButton"));
if (minusBtn && plusBtn)
{
row->addWidget(minusBtn, 0, Qt::AlignVCenter);
row->addWidget(plusBtn, 0, Qt::AlignVCenter);
}
}
}
if (!unit.isEmpty())
{
auto *unitLbl = new QLabel(unit, this);
unitLbl->setStyleSheet("font-size: 13px; color: #78909C;");
unitLbl->setAlignment(Qt::AlignVCenter);
unitLbl->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
row->addWidget(unitLbl, 0, Qt::AlignVCenter);
}
row->addStretch();
return row;
}
// ============== RTDMeasurementWidget ==============
RTDMeasurementWidget::RTDMeasurementWidget(QWidget *parent)
: MeasurementModuleBase("热电阻测量", parent), m_channelManager(ChannelManager::instance()), m_selectedChannel(ChannelManager::INPUT_CHANNEL_1), m_currentChannel(ChannelManager::INPUT_CHANNEL_1), m_measurementTimer(new QTimer(this)), m_isMeasuring(false), m_currentMode(FOUR_WIRE_MODE), m_currentRTDType(PT100), m_currentResistance(100.0), m_currentTemperature(0.0)
{
setupUI();
// 连接定时器
connect(m_measurementTimer, &QTimer::timeout, this, &RTDMeasurementWidget::updateMeasurement);
m_measurementTimer->setInterval(200); // 每200ms更新
}
RTDMeasurementWidget::~RTDMeasurementWidget()
{
if (m_isMeasuring)
{
m_measurementTimer->stop();
m_channelManager->releaseChannel(m_currentChannel);
}
}
void RTDMeasurementWidget::setupUI()
{
// === 通道选择区域 ===
m_channelSelectionWidget = new QWidget(this);
m_channelSelectionWidget->setStyleSheet("background: #37474F; border-radius: 6px;");
auto *channelLayout = new QHBoxLayout(m_channelSelectionWidget);
channelLayout->setContentsMargins(20, 14, 20, 14);
channelLayout->setSpacing(28);
auto *channelLabel = new QLabel("选择测量通道:", this);
channelLabel->setStyleSheet("font-weight: bold; color: #FFFFFF; font-size: 14px;");
channelLabel->setAlignment(Qt::AlignVCenter);
channelLayout->addWidget(channelLabel);
setupChannelSelection();
channelLayout->addStretch();
m_mainLayout->addWidget(m_channelSelectionWidget);
// === 测量组 ===
auto *measureGroup = createStyledGroupBox("热电阻测量");
auto *measureLayout = new QVBoxLayout(measureGroup);
measureLayout->setSpacing(12);
// 接线方式和传感器类型
auto *settingsRow = new QHBoxLayout();
settingsRow->setSpacing(20);
settingsRow->setAlignment(Qt::AlignVCenter);
m_wireTypeCombo = createStyledComboBox({"2线制", "3线制", "4线制"}, 120);
m_wireTypeCombo->setCurrentIndex(2); // 默认4线制
connect(m_wireTypeCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &RTDMeasurementWidget::onWireTypeChanged);
settingsRow->addLayout(createLabeledRow("接线方式", m_wireTypeCombo));
m_rtdTypeCombo = createStyledComboBox({"PT100", "PT1000", "CU50", "CU100"}, 120);
settingsRow->addLayout(createLabeledRow("传感器类型", m_rtdTypeCombo));
settingsRow->addStretch();
measureLayout->addLayout(settingsRow);
// 接线图提示
m_wiringDiagramLabel = new QLabel("接线图: 输入通道1 四线制 (白-红-灰-黑)", this);
m_wiringDiagramLabel->setStyleSheet("color: #1976D2; font-size: 13px; padding: 8px; "
"background: #E3F2FD; border-radius: 4px;");
measureLayout->addWidget(m_wiringDiagramLabel);
// 测量值显示
auto *displayRow = new QHBoxLayout();
displayRow->setSpacing(12);
auto *resBox = new QVBoxLayout();
auto *resLabel = new QLabel("电阻值 (Ω)", this);
resLabel->setStyleSheet(LABEL_STYLE);
resBox->addWidget(resLabel);
m_resistanceDisplay = createValueDisplay("---", 140);
resBox->addWidget(m_resistanceDisplay);
displayRow->addLayout(resBox);
auto *tempBox = new QVBoxLayout();
auto *tempLabel = new QLabel("温度 (°C)", this);
tempLabel->setStyleSheet(LABEL_STYLE);
tempBox->addWidget(tempLabel);
m_temperatureDisplay = createValueDisplay("---", 140);
tempBox->addWidget(m_temperatureDisplay);
displayRow->addLayout(tempBox);
auto *lineResBox = new QVBoxLayout();
auto *lineResLabel = new QLabel("线电阻 (Ω)", this);
lineResLabel->setStyleSheet(LABEL_STYLE);
lineResBox->addWidget(lineResLabel);
m_lineResDisplay = createValueDisplay("---", 120);
lineResBox->addWidget(m_lineResDisplay);
displayRow->addLayout(lineResBox);
displayRow->addStretch();
measureLayout->addLayout(displayRow);
// 通道状态
m_channelStatusLabel = new QLabel("通道状态: 输入通道1 - 空闲", this);
m_channelStatusLabel->setStyleSheet("color: #455A64; font-size: 13px;");
measureLayout->addWidget(m_channelStatusLabel);
// 按钮
auto *btnRow = new QHBoxLayout();
btnRow->setSpacing(12);
m_measureBtn = createPrimaryButton("开始测量", 120);
connect(m_measureBtn, &QPushButton::clicked, this, &RTDMeasurementWidget::onMeasureClicked);
btnRow->addWidget(m_measureBtn);
m_stopBtn = createSecondaryButton("停止测量", 120);
m_stopBtn->setEnabled(false);
connect(m_stopBtn, &QPushButton::clicked, this, &RTDMeasurementWidget::onStopClicked);
btnRow->addWidget(m_stopBtn);
btnRow->addStretch();
measureLayout->addLayout(btnRow);
m_mainLayout->addWidget(measureGroup);
m_mainLayout->addStretch();
// 初始化
updateConnectionModeVisibility();
}
void RTDMeasurementWidget::setupChannelSelection()
{
QList<QPair<ChannelManager::ChannelId, QString>> channels = {
{ChannelManager::INPUT_CHANNEL_1, "输入通道1"},
{ChannelManager::INPUT_CHANNEL_2, "输入通道2"}};
auto *channelLayout = qobject_cast<QHBoxLayout *>(m_channelSelectionWidget->layout());
for (const auto &channel : channels)
{
ChannelStatusWidget *widget = new ChannelStatusWidget(
channel.first, channel.second, this, ChannelStatusWidget::OVERVIEW_MODE);
widget->setFixedSize(88, 48);
widget->setCursor(Qt::PointingHandCursor);
// 安装事件过滤器处理点击
widget->installEventFilter(this);
m_channelWidgets[channel.first] = widget;
channelLayout->addWidget(widget);
}
updateChannelSelection();
}
void RTDMeasurementWidget::updateChannelSelection()
{
for (auto it = m_channelWidgets.begin(); it != m_channelWidgets.end(); ++it)
{
ChannelManager::ChannelId channelId = it.key();
ChannelStatusWidget *widget = it.value();
if (channelId == m_selectedChannel)
{
widget->setStyleSheet("background-color: #2c3e50; border: 3px solid #1976D2; border-radius: 8px;");
}
else
{
widget->setStyleSheet("background-color: #34495e; border: 2px solid #2c3e50; border-radius: 8px;");
}
widget->update();
}
}
void RTDMeasurementWidget::onChannelSelected(ChannelManager::ChannelId channelId)
{
if (m_isMeasuring)
{
OverlayDialog::information(this, "通道切换", "请先停止当前测量再切换通道。");
return;
}
m_selectedChannel = channelId;
m_currentChannel = channelId;
updateChannelSelection();
updateConnectionModeVisibility();
// 更新状态显示
QString statusText = QString("通道状态: %1 - %2")
.arg(m_channelManager->getChannelDisplayName(m_currentChannel))
.arg(m_channelManager->isChannelAvailable(m_currentChannel) ? "空闲" : "占用中");
m_channelStatusLabel->setText(statusText);
}
void RTDMeasurementWidget::updateConnectionModeVisibility()
{
// 输入通道2只支持二线制
if (m_selectedChannel == ChannelManager::INPUT_CHANNEL_2)
{
m_wireTypeCombo->setEnabled(false);
m_wireTypeCombo->setCurrentIndex(0); // 强制二线制
m_currentMode = TWO_WIRE_MODE;
m_wiringDiagramLabel->setText("接线图: 输入通道2 二线制 (白-灰)");
}
else
{
m_wireTypeCombo->setEnabled(true);
onWireTypeChanged(m_wireTypeCombo->currentIndex());
}
}
void RTDMeasurementWidget::onMeasureClicked()
{
if (!validateChannelForMode())
{
return;
}
// 确定连接类型
ChannelManager::ConnectionType connectionType;
switch (m_currentMode)
{
case TWO_WIRE_MODE:
connectionType = ChannelManager::TWO_WIRE;
break;
case THREE_WIRE_MODE:
connectionType = ChannelManager::THREE_WIRE;
break;
case FOUR_WIRE_MODE:
connectionType = ChannelManager::FOUR_WIRE;
break;
}
QString description = QString("热电阻测量(%1)").arg(m_wireTypeCombo->currentText());
if (!m_channelManager->occupyChannel(m_currentChannel,
ChannelManager::RESISTANCE_MEASUREMENT,
description, connectionType))
{
OverlayDialog::warning(this, "通道占用",
QString("无法占用%1通道可能正在被其他功能使用。")
.arg(m_channelManager->getChannelDisplayName(m_currentChannel)));
return;
}
m_isMeasuring = true;
m_measurementTimer->start();
m_measureBtn->setEnabled(false);
m_stopBtn->setEnabled(true);
m_wireTypeCombo->setEnabled(false);
// 更新通道状态显示
QString statusText = QString("通道状态: 正在测量 (%1)")
.arg(m_channelManager->getChannelDisplayName(m_currentChannel));
m_channelStatusLabel->setText(statusText);
m_channelStatusLabel->setStyleSheet("color: #1976D2; font-size: 13px; font-weight: bold;");
// 更新通道widget状态
if (m_channelWidgets.contains(m_currentChannel))
{
m_channelWidgets[m_currentChannel]->updateStatus(
ChannelManager::RESISTANCE_MEASUREMENT, "测量中");
}
}
void RTDMeasurementWidget::onStopClicked()
{
if (m_isMeasuring)
{
m_measurementTimer->stop();
m_channelManager->releaseChannel(m_currentChannel);
m_isMeasuring = false;
m_measureBtn->setEnabled(true);
m_stopBtn->setEnabled(false);
if (m_selectedChannel == ChannelManager::INPUT_CHANNEL_1)
{
m_wireTypeCombo->setEnabled(true);
}
QString statusText = QString("通道状态: %1 - 空闲")
.arg(m_channelManager->getChannelDisplayName(m_currentChannel));
m_channelStatusLabel->setText(statusText);
m_channelStatusLabel->setStyleSheet("color: #455A64; font-size: 13px;");
if (m_channelWidgets.contains(m_currentChannel))
{
m_channelWidgets[m_currentChannel]->updateStatus(ChannelManager::NONE, "");
}
}
}
void RTDMeasurementWidget::onWireTypeChanged(int index)
{
m_currentMode = static_cast<MeasurementMode>(index);
m_lineResDisplay->setVisible(index == 2); // 仅4线制显示线电阻
QString channelName = m_channelManager->getChannelDisplayName(m_currentChannel);
QString wiringText;
switch (index)
{
case 0: // 二线制
wiringText = QString("接线图: %1 二线制 (白-灰)").arg(channelName);
break;
case 1: // 三线制
wiringText = QString("接线图: %1 三线制 (白-红-灰)").arg(channelName);
break;
case 2: // 四线制
wiringText = QString("接线图: %1 四线制 (白-红-灰-黑)").arg(channelName);
break;
}
m_wiringDiagramLabel->setText(wiringText);
}
void RTDMeasurementWidget::updateMeasurement()
{
generateSimulatedData();
m_resistanceDisplay->setText(QString::number(m_currentResistance, 'f', 3));
m_temperatureDisplay->setText(QString::number(m_currentTemperature, 'f', 2));
if (m_currentMode == FOUR_WIRE_MODE)
{
// 模拟线电阻(四线制可以测量)
static std::random_device rd;
static std::mt19937 gen(rd());
std::uniform_real_distribution<> lineResNoise(0.1, 0.3);
m_lineResDisplay->setText(QString::number(lineResNoise(gen), 'f', 3));
}
emit measurementDataReady(m_currentResistance, m_currentTemperature);
}
void RTDMeasurementWidget::generateSimulatedData()
{
// 生成逼真的RTD模拟数据
static std::random_device rd;
static std::mt19937 gen(rd());
static std::uniform_real_distribution<> noise(-0.02, 0.02);
// 基础温度缓慢变化
static double baseTemp = 20.0;
static int counter = 0;
if (counter++ % 50 == 0)
{
baseTemp += noise(gen) * 10;
baseTemp = std::max(-50.0, std::min(200.0, baseTemp));
}
m_currentTemperature = baseTemp + noise(gen);
// PT100 温度转电阻计算 (IEC 60751)
double R0 = 100.0;
double A = 3.9083e-3;
double B = -5.775e-7;
double C = -4.183e-12;
double T = m_currentTemperature;
if (T >= 0)
{
m_currentResistance = R0 * (1 + A * T + B * T * T);
}
else
{
m_currentResistance = R0 * (1 + A * T + B * T * T + C * T * T * T * (T - 100));
}
// 根据接线方式添加测量噪声
double noiseLevel = 0.001;
switch (m_currentMode)
{
case TWO_WIRE_MODE:
noiseLevel = 0.005; // 二线制噪声最大
break;
case THREE_WIRE_MODE:
noiseLevel = 0.002; // 三线制中等
break;
case FOUR_WIRE_MODE:
noiseLevel = 0.001; // 四线制噪声最小
break;
}
m_currentResistance += noise(gen) * noiseLevel;
}
double RTDMeasurementWidget::convertResistanceToTemperature(double resistance)
{
// PT100简化逆运算
double R0 = 100.0;
double A = 3.9083e-3;
return (resistance - R0) / (R0 * A);
}
bool RTDMeasurementWidget::validateChannelForMode()
{
ChannelManager::ConnectionType connectionType;
switch (m_currentMode)
{
case TWO_WIRE_MODE:
connectionType = ChannelManager::TWO_WIRE;
break;
case THREE_WIRE_MODE:
connectionType = ChannelManager::THREE_WIRE;
break;
case FOUR_WIRE_MODE:
connectionType = ChannelManager::FOUR_WIRE;
break;
}
if (!m_channelManager->canChannelSupport(m_currentChannel,
ChannelManager::RESISTANCE_MEASUREMENT,
connectionType))
{
QString message;
if (m_currentChannel == ChannelManager::INPUT_CHANNEL_2 &&
(m_currentMode == THREE_WIRE_MODE || m_currentMode == FOUR_WIRE_MODE))
{
message = "输入通道2只支持二线制测量请选择二线制或切换到输入通道1。";
}
else
{
message = QString("所选通道不支持%1测量。").arg(m_wireTypeCombo->currentText());
}
OverlayDialog::warning(this, "通道限制", message);
return false;
}
return true;
}
bool RTDMeasurementWidget::eventFilter(QObject *obj, QEvent *event)
{
// 处理通道选择widget的鼠标事件
for (auto it = m_channelWidgets.begin(); it != m_channelWidgets.end(); ++it)
{
if (it.value() == obj && event->type() == QEvent::MouseButtonPress)
{
onChannelSelected(it.key());
return true;
}
}
return MeasurementModuleBase::eventFilter(obj, event);
}
// ============== ThermocoupleMeasurementWidget ==============
ThermocoupleMeasurementWidget::ThermocoupleMeasurementWidget(QWidget *parent)
: MeasurementModuleBase("热电偶测量", parent)
{
setupUI();
}
void ThermocoupleMeasurementWidget::setupUI()
{
auto *measureGroup = createStyledGroupBox("热电偶测量");
auto *measureLayout = new QVBoxLayout(measureGroup);
measureLayout->setSpacing(16);
m_tcTypeCombo = createStyledComboBox({"K型", "J型", "T型", "E型", "S型", "R型", "B型"}, 150);
measureLayout->addLayout(createLabeledRow("热电偶类型", m_tcTypeCombo));
auto *displayRow = new QHBoxLayout();
displayRow->setSpacing(16);
auto *voltBox = new QVBoxLayout();
voltBox->addWidget(new QLabel("电压 (mV)", this));
m_voltageDisplay = createValueDisplay("---", 160);
voltBox->addWidget(m_voltageDisplay);
displayRow->addLayout(voltBox);
auto *tempBox = new QVBoxLayout();
tempBox->addWidget(new QLabel("温度 (°C)", this));
m_temperatureDisplay = createValueDisplay("---", 160);
tempBox->addWidget(m_temperatureDisplay);
displayRow->addLayout(tempBox);
displayRow->addStretch();
measureLayout->addLayout(displayRow);
m_measureBtn = createPrimaryButton("开始测量", 140);
connect(m_measureBtn, &QPushButton::clicked, this, &ThermocoupleMeasurementWidget::onMeasureClicked);
measureLayout->addWidget(m_measureBtn);
m_mainLayout->addWidget(measureGroup);
auto *outputGroup = createStyledGroupBox("热电偶模拟输出");
auto *outputLayout = new QVBoxLayout(outputGroup);
outputLayout->setSpacing(16);
m_outputTempSpin = createStyledSpinBox(-200, 1300, 1, 150);
m_outputTempSpin->setValue(100.0);
outputLayout->addLayout(createLabeledRow("设定温度", m_outputTempSpin, "°C"));
auto *outDisplayRow = new QHBoxLayout();
outDisplayRow->setSpacing(16);
auto *outVoltLbl = new QLabel("输出电压 (mV):", this);
outDisplayRow->addWidget(outVoltLbl);
m_outputVoltageDisplay = createValueDisplay("---", 180);
outDisplayRow->addWidget(m_outputVoltageDisplay);
outDisplayRow->addStretch();
outputLayout->addLayout(outDisplayRow);
m_outputBtn = createPrimaryButton("启动输出", 140);
connect(m_outputBtn, &QPushButton::clicked, this, &ThermocoupleMeasurementWidget::onOutputClicked);
outputLayout->addWidget(m_outputBtn);
m_mainLayout->addWidget(outputGroup);
m_mainLayout->addStretch();
}
void ThermocoupleMeasurementWidget::onMeasureClicked()
{
m_voltageDisplay->setText("4.096");
m_temperatureDisplay->setText("100.2");
}
void ThermocoupleMeasurementWidget::onOutputClicked()
{
double temp = m_outputTempSpin->value();
// K型热电偶简化: ~40µV/°C
double voltage = temp * 0.04;
m_outputVoltageDisplay->setText(QString::number(voltage, 'f', 3));
OverlayDialog::information(this, "输出启动",
QString("已启动热电偶输出\n温度: %1°C\n电压: %2mV").arg(temp).arg(voltage, 0, 'f', 3));
}
// ============== InsulationMeasurementWidget ==============
InsulationMeasurementWidget::InsulationMeasurementWidget(QWidget *parent)
: MeasurementModuleBase("绝缘电阻测量", parent)
{
setupUI();
}
void InsulationMeasurementWidget::setupUI()
{
auto *measureGroup = createStyledGroupBox("绝缘电阻测量");
auto *measureLayout = new QVBoxLayout(measureGroup);
measureLayout->setSpacing(16);
auto *voltageRow = new QHBoxLayout();
auto *voltLbl = new QLabel("测试电压:", this);
voltLbl->setFixedWidth(100);
voltLbl->setStyleSheet(LABEL_STYLE);
voltageRow->addWidget(voltLbl);
auto *btnGroup = new QButtonGroup(this);
m_50vRadio = new QRadioButton("50V", this);
m_100vRadio = new QRadioButton("100V", this);
m_50vRadio->setChecked(true);
btnGroup->addButton(m_50vRadio);
btnGroup->addButton(m_100vRadio);
m_50vRadio->setStyleSheet("QRadioButton { font-size: 14px; spacing: 8px; }");
m_100vRadio->setStyleSheet("QRadioButton { font-size: 14px; spacing: 8px; }");
voltageRow->addWidget(m_50vRadio);
voltageRow->addWidget(m_100vRadio);
voltageRow->addStretch();
measureLayout->addLayout(voltageRow);
m_autoTestCheck = new QCheckBox("测量后自动检测绝缘电阻", this);
m_autoTestCheck->setStyleSheet("QCheckBox { font-size: 14px; color: #455A64; }");
connect(m_autoTestCheck, &QCheckBox::toggled, this, &InsulationMeasurementWidget::onAutoTestToggled);
measureLayout->addWidget(m_autoTestCheck);
auto *displayBox = new QVBoxLayout();
displayBox->addWidget(new QLabel("绝缘电阻 (MΩ)", this));
m_insulationDisplay = createValueDisplay("---", 200);
displayBox->addWidget(m_insulationDisplay);
measureLayout->addLayout(displayBox);
m_statusDisplay = new QLabel("就绪", this);
m_statusDisplay->setStyleSheet("font-size: 14px; color: #43A047; font-weight: bold;");
measureLayout->addWidget(m_statusDisplay);
m_dischargeProgress = new QProgressBar(this);
m_dischargeProgress->setRange(0, 100);
m_dischargeProgress->setValue(0);
m_dischargeProgress->setVisible(false);
m_dischargeProgress->setStyleSheet(
"QProgressBar { border: 2px solid #E0E0E0; border-radius: 6px; text-align: center; "
"background: white; height: 28px; }"
"QProgressBar::chunk { background: #43A047; border-radius: 4px; }");
measureLayout->addWidget(m_dischargeProgress);
m_measureBtn = createPrimaryButton("开始测量", 140);
connect(m_measureBtn, &QPushButton::clicked, this, &InsulationMeasurementWidget::onMeasureClicked);
measureLayout->addWidget(m_measureBtn);
m_mainLayout->addWidget(measureGroup);
m_mainLayout->addStretch();
}
void InsulationMeasurementWidget::onMeasureClicked()
{
m_statusDisplay->setText("测量中...");
m_statusDisplay->setStyleSheet("font-size: 14px; color: #1976D2; font-weight: bold;");
// 模拟测量
QTimer::singleShot(1000, this, [this]()
{
m_insulationDisplay->setText("125.6");
m_statusDisplay->setText("测量完成,放电中...");
m_dischargeProgress->setVisible(true);
// 模拟放电过程
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, [this, timer]() {
int val = m_dischargeProgress->value() + 10;
m_dischargeProgress->setValue(val);
if (val >= 100) {
timer->stop();
timer->deleteLater();
m_dischargeProgress->setVisible(false);
m_dischargeProgress->setValue(0);
m_statusDisplay->setText("就绪");
m_statusDisplay->setStyleSheet("font-size: 14px; color: #43A047; font-weight: bold;");
}
});
timer->start(200); });
}
void InsulationMeasurementWidget::onAutoTestToggled(bool enabled)
{
if (enabled)
{
OverlayDialog::information(this, "自动测试", "已启用自动绝缘电阻测试\n将在热电阻/热电偶测量后自动执行");
}
}
// ============== DCCurrentWidget ==============
DCCurrentWidget::DCCurrentWidget(QWidget *parent)
: MeasurementModuleBase("直流电流", parent)
{
setupUI();
}
void DCCurrentWidget::setupUI()
{
auto *measureGroup = createStyledGroupBox("电流测量 (0-20mA)");
auto *measureLayout = new QVBoxLayout(measureGroup);
measureLayout->setSpacing(16);
auto *displayRow = new QHBoxLayout();
displayRow->setSpacing(12);
auto *currentBox = new QVBoxLayout();
currentBox->addWidget(new QLabel("当前值 (mA)", this));
m_currentDisplay = createValueDisplay("---", 140);
currentBox->addWidget(m_currentDisplay);
displayRow->addLayout(currentBox);
auto *statsBox = new QVBoxLayout();
statsBox->setSpacing(8);
auto *maxRow = new QHBoxLayout();
maxRow->addWidget(new QLabel("最大:", this));
m_maxDisplay = new QLabel("---", this);
m_maxDisplay->setStyleSheet("font-size: 14px; color: #D32F2F; font-weight: bold;");
maxRow->addWidget(m_maxDisplay);
maxRow->addStretch();
statsBox->addLayout(maxRow);
auto *minRow = new QHBoxLayout();
minRow->addWidget(new QLabel("最小:", this));
m_minDisplay = new QLabel("---", this);
m_minDisplay->setStyleSheet("font-size: 14px; color: #1976D2; font-weight: bold;");
minRow->addWidget(m_minDisplay);
minRow->addStretch();
statsBox->addLayout(minRow);
auto *avgRow = new QHBoxLayout();
avgRow->addWidget(new QLabel("平均:", this));
m_avgDisplay = new QLabel("---", this);
m_avgDisplay->setStyleSheet("font-size: 14px; color: #43A047; font-weight: bold;");
avgRow->addWidget(m_avgDisplay);
avgRow->addStretch();
statsBox->addLayout(avgRow);
displayRow->addLayout(statsBox);
displayRow->addStretch();
measureLayout->addLayout(displayRow);
m_measureBtn = createPrimaryButton("开始测量", 140);
connect(m_measureBtn, &QPushButton::clicked, this, &DCCurrentWidget::onMeasureClicked);
measureLayout->addWidget(m_measureBtn);
m_mainLayout->addWidget(measureGroup);
auto *outputGroup = createStyledGroupBox("电流输出");
auto *outputLayout = new QVBoxLayout(outputGroup);
outputLayout->setSpacing(16);
auto *modeRow = new QHBoxLayout();
auto *modeLbl = new QLabel("输出模式:", this);
modeLbl->setFixedWidth(100);
modeLbl->setStyleSheet(LABEL_STYLE);
modeRow->addWidget(modeLbl);
auto *modeGroup = new QButtonGroup(this);
m_activeRadio = new QRadioButton("有源", this);
m_passiveRadio = new QRadioButton("无源", this);
m_activeRadio->setChecked(true);
modeGroup->addButton(m_activeRadio);
modeGroup->addButton(m_passiveRadio);
m_activeRadio->setStyleSheet("QRadioButton { font-size: 14px; }");
m_passiveRadio->setStyleSheet("QRadioButton { font-size: 14px; }");
modeRow->addWidget(m_activeRadio);
modeRow->addWidget(m_passiveRadio);
modeRow->addStretch();
outputLayout->addLayout(modeRow);
m_outputSpin = createStyledSpinBox(0, 20, 3, 150);
m_outputSpin->setValue(4.0);
outputLayout->addLayout(createLabeledRow("输出电流", m_outputSpin, "mA"));
m_outputBtn = createPrimaryButton("启动输出", 140);
connect(m_outputBtn, &QPushButton::clicked, this, &DCCurrentWidget::onOutputClicked);
outputLayout->addWidget(m_outputBtn);
m_mainLayout->addWidget(outputGroup);
m_mainLayout->addStretch();
}
void DCCurrentWidget::onMeasureClicked()
{
m_currentDisplay->setText("12.456");
m_maxDisplay->setText("12.502 mA");
m_minDisplay->setText("12.401 mA");
m_avgDisplay->setText("12.451 mA");
}
void DCCurrentWidget::onOutputClicked()
{
QString mode = m_activeRadio->isChecked() ? "有源" : "无源";
OverlayDialog::information(this, "输出启动",
QString("已启动电流输出\n模式: %1\n电流: %2mA").arg(mode).arg(m_outputSpin->value()));
}
// ============== DCVoltageWidget ==============
DCVoltageWidget::DCVoltageWidget(QWidget *parent)
: MeasurementModuleBase("直流电压", parent)
{
setupUI();
}
void DCVoltageWidget::setupUI()
{
auto *measureGroup = createStyledGroupBox("电压测量 (0-20V)");
auto *measureLayout = new QVBoxLayout(measureGroup);
measureLayout->setSpacing(16);
auto *displayRow = new QHBoxLayout();
displayRow->setSpacing(12);
auto *voltBox = new QVBoxLayout();
voltBox->addWidget(new QLabel("当前值 (V)", this));
m_voltageDisplay = createValueDisplay("---", 140);
voltBox->addWidget(m_voltageDisplay);
displayRow->addLayout(voltBox);
auto *statsBox = new QVBoxLayout();
statsBox->setSpacing(8);
auto *maxRow = new QHBoxLayout();
maxRow->addWidget(new QLabel("最大:", this));
m_maxDisplay = new QLabel("---", this);
m_maxDisplay->setStyleSheet("font-size: 14px; color: #D32F2F; font-weight: bold;");
maxRow->addWidget(m_maxDisplay);
maxRow->addStretch();
statsBox->addLayout(maxRow);
auto *minRow = new QHBoxLayout();
minRow->addWidget(new QLabel("最小:", this));
m_minDisplay = new QLabel("---", this);
m_minDisplay->setStyleSheet("font-size: 14px; color: #1976D2; font-weight: bold;");
minRow->addWidget(m_minDisplay);
minRow->addStretch();
statsBox->addLayout(minRow);
auto *avgRow = new QHBoxLayout();
avgRow->addWidget(new QLabel("平均:", this));
m_avgDisplay = new QLabel("---", this);
m_avgDisplay->setStyleSheet("font-size: 14px; color: #43A047; font-weight: bold;");
avgRow->addWidget(m_avgDisplay);
avgRow->addStretch();
statsBox->addLayout(avgRow);
displayRow->addLayout(statsBox);
displayRow->addStretch();
measureLayout->addLayout(displayRow);
m_measureBtn = createPrimaryButton("开始测量", 140);
connect(m_measureBtn, &QPushButton::clicked, this, &DCVoltageWidget::onMeasureClicked);
measureLayout->addWidget(m_measureBtn);
m_mainLayout->addWidget(measureGroup);
auto *outputGroup = createStyledGroupBox("电压输出");
auto *outputLayout = new QVBoxLayout(outputGroup);
outputLayout->setSpacing(16);
m_outputSpin = createStyledSpinBox(0, 20, 3, 150);
m_outputSpin->setValue(5.0);
outputLayout->addLayout(createLabeledRow("输出电压", m_outputSpin, "V"));
m_outputBtn = createPrimaryButton("启动输出", 140);
connect(m_outputBtn, &QPushButton::clicked, this, &DCVoltageWidget::onOutputClicked);
outputLayout->addWidget(m_outputBtn);
m_mainLayout->addWidget(outputGroup);
m_mainLayout->addStretch();
}
void DCVoltageWidget::onMeasureClicked()
{
m_voltageDisplay->setText("5.123");
m_maxDisplay->setText("5.156 V");
m_minDisplay->setText("5.098 V");
m_avgDisplay->setText("5.124 V");
}
void DCVoltageWidget::onOutputClicked()
{
OverlayDialog::information(this, "输出启动",
QString("已启动电压输出\n电压: %1V").arg(m_outputSpin->value()));
}
// ============== FrequencyWidget ==============
FrequencyWidget::FrequencyWidget(QWidget *parent)
: MeasurementModuleBase("频率信号", parent)
{
setupUI();
}
void FrequencyWidget::setupUI()
{
auto *measureGroup = createStyledGroupBox("频率测量");
auto *measureLayout = new QVBoxLayout(measureGroup);
measureLayout->setSpacing(16);
auto *displayBox = new QVBoxLayout();
displayBox->addWidget(new QLabel("频率 (Hz)", this));
m_frequencyDisplay = createValueDisplay("---", 200);
displayBox->addWidget(m_frequencyDisplay);
measureLayout->addLayout(displayBox);
m_measureBtn = createPrimaryButton("开始测量", 140);
connect(m_measureBtn, &QPushButton::clicked, this, &FrequencyWidget::onMeasureClicked);
measureLayout->addWidget(m_measureBtn);
m_mainLayout->addWidget(measureGroup);
auto *outputGroup = createStyledGroupBox("频率信号输出");
auto *outputLayout = new QVBoxLayout(outputGroup);
outputLayout->setSpacing(16);
m_waveTypeCombo = createStyledComboBox({"方波", "正弦波"}, 150);
connect(m_waveTypeCombo, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &FrequencyWidget::onWaveTypeChanged);
outputLayout->addLayout(createLabeledRow("波形类型", m_waveTypeCombo));
m_freqSpin = createStyledSpinBox(0.1, 10000, 1, 150);
m_freqSpin->setValue(1000.0);
outputLayout->addLayout(createLabeledRow("频率", m_freqSpin, "Hz"));
m_amplitudeSpin = createStyledSpinBox(0.1, 10, 2, 150);
m_amplitudeSpin->setValue(5.0);
outputLayout->addLayout(createLabeledRow("幅值", m_amplitudeSpin, "V"));
m_outputBtn = createPrimaryButton("启动输出", 140);
connect(m_outputBtn, &QPushButton::clicked, this, &FrequencyWidget::onOutputClicked);
outputLayout->addWidget(m_outputBtn);
m_mainLayout->addWidget(outputGroup);
m_mainLayout->addStretch();
}
void FrequencyWidget::onMeasureClicked()
{
m_frequencyDisplay->setText("1250.5");
}
void FrequencyWidget::onOutputClicked()
{
QString waveType = m_waveTypeCombo->currentText();
OverlayDialog::information(this, "输出启动",
QString("已启动频率输出\n波形: %1\n频率: %2Hz\n幅值: %3V")
.arg(waveType)
.arg(m_freqSpin->value())
.arg(m_amplitudeSpin->value()));
}
void FrequencyWidget::onWaveTypeChanged(int)
{
// 波形类型变化处理
}
// ============== ACVoltageWidget ==============
ACVoltageWidget::ACVoltageWidget(QWidget *parent)
: MeasurementModuleBase("220VAC测量", parent)
{
setupUI();
}
void ACVoltageWidget::setupUI()
{
auto *measureGroup = createStyledGroupBox("交流电压测量");
auto *measureLayout = new QVBoxLayout(measureGroup);
measureLayout->setSpacing(16);
auto *displayRow = new QHBoxLayout();
displayRow->setSpacing(16);
auto *voltBox = new QVBoxLayout();
voltBox->addWidget(new QLabel("电压 (V)", this));
m_voltageDisplay = createValueDisplay("---", 160);
voltBox->addWidget(m_voltageDisplay);
displayRow->addLayout(voltBox);
auto *freqBox = new QVBoxLayout();
freqBox->addWidget(new QLabel("频率 (Hz)", this));
m_frequencyDisplay = createValueDisplay("---", 160);
freqBox->addWidget(m_frequencyDisplay);
displayRow->addLayout(freqBox);
displayRow->addStretch();
measureLayout->addLayout(displayRow);
m_measureBtn = createPrimaryButton("开始测量", 140);
connect(m_measureBtn, &QPushButton::clicked, this, &ACVoltageWidget::onMeasureClicked);
measureLayout->addWidget(m_measureBtn);
m_mainLayout->addWidget(measureGroup);
m_mainLayout->addStretch();
}
void ACVoltageWidget::onMeasureClicked()
{
m_voltageDisplay->setText("220.5");
m_frequencyDisplay->setText("50.0");
}
// ============== SwitchDetectionWidget ==============
SwitchDetectionWidget::SwitchDetectionWidget(QWidget *parent)
: MeasurementModuleBase("开关量检测", parent)
{
setupUI();
}
void SwitchDetectionWidget::setupUI()
{
auto *detectGroup = createStyledGroupBox("开关量检测");
auto *detectLayout = new QVBoxLayout(detectGroup);
detectLayout->setSpacing(16);
auto *modeRow = new QHBoxLayout();
auto *modeLbl = new QLabel("检测模式:", this);
modeLbl->setFixedWidth(100);
modeLbl->setStyleSheet(LABEL_STYLE);
modeRow->addWidget(modeLbl);
auto *modeGroup = new QButtonGroup(this);
m_resistanceMode = new QRadioButton("电阻通断", this);
m_voltageMode = new QRadioButton("电压模式 (50V内)", this);
m_resistanceMode->setChecked(true);
modeGroup->addButton(m_resistanceMode);
modeGroup->addButton(m_voltageMode);
m_resistanceMode->setStyleSheet("QRadioButton { font-size: 14px; }");
m_voltageMode->setStyleSheet("QRadioButton { font-size: 14px; }");
modeRow->addWidget(m_resistanceMode);
modeRow->addWidget(m_voltageMode);
modeRow->addStretch();
detectLayout->addLayout(modeRow);
auto *displayRow = new QHBoxLayout();
displayRow->setSpacing(16);
auto *statusBox = new QVBoxLayout();
statusBox->addWidget(new QLabel("开关状态", this));
m_statusDisplay = createValueDisplay("---", 140);
statusBox->addWidget(m_statusDisplay);
displayRow->addLayout(statusBox);
auto *valueBox = new QVBoxLayout();
valueBox->addWidget(new QLabel("测量值", this));
m_valueDisplay = createValueDisplay("---", 160);
valueBox->addWidget(m_valueDisplay);
displayRow->addLayout(valueBox);
displayRow->addStretch();
detectLayout->addLayout(displayRow);
m_detectBtn = createPrimaryButton("开始检测", 140);
connect(m_detectBtn, &QPushButton::clicked, this, &SwitchDetectionWidget::onDetectClicked);
detectLayout->addWidget(m_detectBtn);
m_mainLayout->addWidget(detectGroup);
m_mainLayout->addStretch();
}
void SwitchDetectionWidget::onDetectClicked()
{
if (m_resistanceMode->isChecked())
{
m_statusDisplay->setText("闭合");
m_statusDisplay->setStyleSheet(VALUE_DISPLAY_STYLE + " color: #43A047;");
m_valueDisplay->setText("0.5 Ω");
}
else
{
m_statusDisplay->setText("断开");
m_statusDisplay->setStyleSheet(VALUE_DISPLAY_STYLE + " color: #D32F2F;");
m_valueDisplay->setText("24.5 V");
}
}
// ============== SignalAcquisitionWidget ==============
SignalAcquisitionWidget::SignalAcquisitionWidget(QWidget *parent)
: MeasurementModuleBase("信号采集", parent)
{
setupUI();
m_acquisitionTimer = new QTimer(this);
}
void SignalAcquisitionWidget::setupUI()
{
auto *configGroup = createStyledGroupBox("采集配置");
auto *configLayout = new QVBoxLayout(configGroup);
configLayout->setSpacing(16);
m_channel1Type = createStyledComboBox({"电压", "电流"}, 120);
configLayout->addLayout(createLabeledRow("通道1", m_channel1Type));
m_channel2Type = createStyledComboBox({"电压", "电流"}, 120);
configLayout->addLayout(createLabeledRow("通道2", m_channel2Type));
m_rateSpin = createStyledSpinBox(25, 1000, 0, 120);
m_rateSpin->setValue(25);
m_rateSpin->setSuffix(" ms/点");
configLayout->addLayout(createLabeledRow("采集速率", m_rateSpin));
auto *btnRow = new QHBoxLayout();
m_startBtn = createPrimaryButton("开始采集", 120);
m_stopBtn = createSecondaryButton("停止", 100);
m_exportBtn = createSecondaryButton("导出数据", 120);
m_stopBtn->setEnabled(false);
connect(m_startBtn, &QPushButton::clicked, this, &SignalAcquisitionWidget::onStartClicked);
connect(m_stopBtn, &QPushButton::clicked, this, &SignalAcquisitionWidget::onStopClicked);
connect(m_exportBtn, &QPushButton::clicked, this, &SignalAcquisitionWidget::onExportClicked);
btnRow->addWidget(m_startBtn);
btnRow->addWidget(m_stopBtn);
btnRow->addWidget(m_exportBtn);
btnRow->addStretch();
configLayout->addLayout(btnRow);
m_mainLayout->addWidget(configGroup);
auto *dataGroup = createStyledGroupBox("采集数据");
auto *dataLayout = new QVBoxLayout(dataGroup);
m_dataTable = new QTableWidget(this);
m_dataTable->setColumnCount(3);
m_dataTable->setHorizontalHeaderLabels({"时间", "通道1", "通道2"});
m_dataTable->setStyleSheet(
"QTableWidget { border: 1px solid #E0E0E0; gridline-color: #E0E0E0; background: white; }"
"QHeaderView::section { background: #ECEFF1; padding: 8px; border: none; "
"border-bottom: 2px solid #CFD8DC; font-weight: bold; }");
m_dataTable->horizontalHeader()->setStretchLastSection(true);
m_dataTable->setMinimumHeight(250);
dataLayout->addWidget(m_dataTable);
m_mainLayout->addWidget(dataGroup);
m_mainLayout->addStretch();
}
void SignalAcquisitionWidget::onStartClicked()
{
m_startBtn->setEnabled(false);
m_stopBtn->setEnabled(true);
m_dataTable->setRowCount(0);
OverlayDialog::information(this, "采集启动", "信号采集已开始");
}
void SignalAcquisitionWidget::onStopClicked()
{
m_startBtn->setEnabled(true);
m_stopBtn->setEnabled(false);
OverlayDialog::information(this, "采集停止", "信号采集已停止");
}
void SignalAcquisitionWidget::onExportClicked()
{
QString filename = QFileDialog::getSaveFileName(this, "导出数据", "", "CSV文件 (*.csv)");
if (!filename.isEmpty())
{
OverlayDialog::information(this, "导出成功", "数据已导出到: " + filename);
}
}
// ============== RampSignalWidget ==============
RampSignalWidget::RampSignalWidget(QWidget *parent)
: MeasurementModuleBase("斜波输出", parent)
{
setupUI();
}
void RampSignalWidget::setupUI()
{
auto *configGroup = createStyledGroupBox("斜波配置");
auto *configLayout = new QVBoxLayout(configGroup);
configLayout->setSpacing(16);
m_signalType = createStyledComboBox({"电压", "电流"}, 120);
configLayout->addLayout(createLabeledRow("信号类型", m_signalType));
m_startValue = createStyledSpinBox(0, 20, 3, 120);
m_startValue->setValue(0);
configLayout->addLayout(createLabeledRow("起始值", m_startValue));
m_endValue = createStyledSpinBox(0, 20, 3, 120);
m_endValue->setValue(10);
configLayout->addLayout(createLabeledRow("结束值", m_endValue));
m_rampRate = createStyledSpinBox(0.01, 10, 2, 120);
m_rampRate->setValue(0.1);
m_rampRate->setSuffix(" /s");
configLayout->addLayout(createLabeledRow("斜波速率", m_rampRate));
auto *displayBox = new QVBoxLayout();
displayBox->addWidget(new QLabel("当前输出值", this));
m_currentValueDisplay = createValueDisplay("---", 160);
displayBox->addWidget(m_currentValueDisplay);
configLayout->addLayout(displayBox);
m_rampProgress = new QProgressBar(this);
m_rampProgress->setRange(0, 100);
m_rampProgress->setValue(0);
m_rampProgress->setStyleSheet(
"QProgressBar { border: 2px solid #E0E0E0; border-radius: 6px; text-align: center; "
"background: white; height: 28px; }"
"QProgressBar::chunk { background: #1976D2; border-radius: 4px; }");
configLayout->addWidget(m_rampProgress);
auto *btnRow = new QHBoxLayout();
m_startBtn = createPrimaryButton("启动斜波", 120);
m_stopBtn = createSecondaryButton("停止", 100);
m_stopBtn->setEnabled(false);
connect(m_startBtn, &QPushButton::clicked, this, &RampSignalWidget::onStartClicked);
connect(m_stopBtn, &QPushButton::clicked, this, &RampSignalWidget::onStopClicked);
btnRow->addWidget(m_startBtn);
btnRow->addWidget(m_stopBtn);
btnRow->addStretch();
configLayout->addLayout(btnRow);
m_mainLayout->addWidget(configGroup);
m_mainLayout->addStretch();
}
void RampSignalWidget::onStartClicked()
{
m_startBtn->setEnabled(false);
m_stopBtn->setEnabled(true);
m_rampProgress->setValue(0);
m_currentValueDisplay->setText(QString::number(m_startValue->value(), 'f', 3));
OverlayDialog::information(this, "斜波启动", "斜波信号输出已启动");
}
void RampSignalWidget::onStopClicked()
{
m_startBtn->setEnabled(true);
m_stopBtn->setEnabled(false);
OverlayDialog::information(this, "斜波停止", "斜波信号输出已停止");
}
// ============== RippleDetectionWidget ==============
RippleDetectionWidget::RippleDetectionWidget(QWidget *parent)
: MeasurementModuleBase("纹波检测", parent)
{
setupUI();
}
void RippleDetectionWidget::setupUI()
{
auto *detectGroup = createStyledGroupBox("电源纹波检测 (最大50V)");
auto *detectLayout = new QVBoxLayout(detectGroup);
detectLayout->setSpacing(16);
auto *displayRow = new QHBoxLayout();
displayRow->setSpacing(16);
auto *rippleBox = new QVBoxLayout();
rippleBox->addWidget(new QLabel("纹波值 (mVpp)", this));
m_rippleDisplay = createValueDisplay("---", 160);
rippleBox->addWidget(m_rippleDisplay);
displayRow->addLayout(rippleBox);
displayRow->addStretch();
detectLayout->addLayout(displayRow);
auto *btnRow = new QHBoxLayout();
m_detectBtn = createPrimaryButton("检测纹波", 120);
m_diagnoseBtn = createPrimaryButton("AI诊断", 120);
m_diagnoseBtn->setEnabled(false);
connect(m_detectBtn, &QPushButton::clicked, this, &RippleDetectionWidget::onDetectClicked);
connect(m_diagnoseBtn, &QPushButton::clicked, this, &RippleDetectionWidget::onDiagnoseClicked);
btnRow->addWidget(m_detectBtn);
btnRow->addWidget(m_diagnoseBtn);
btnRow->addStretch();
detectLayout->addLayout(btnRow);
m_mainLayout->addWidget(detectGroup);
auto *diagGroup = createStyledGroupBox("故障诊断结果");
auto *diagLayout = new QVBoxLayout(diagGroup);
diagLayout->setSpacing(12);
auto *diagRow = new QHBoxLayout();
diagRow->addWidget(new QLabel("诊断结果:", this));
m_diagnosisDisplay = new QLabel("未诊断", this);
m_diagnosisDisplay->setStyleSheet("font-size: 15px; color: #455A64; font-weight: bold;");
diagRow->addWidget(m_diagnosisDisplay);
diagRow->addStretch();
diagLayout->addLayout(diagRow);
auto *confRow = new QHBoxLayout();
confRow->addWidget(new QLabel("置信度:", this));
m_confidenceDisplay = new QLabel("---", this);
m_confidenceDisplay->setStyleSheet("font-size: 15px; color: #1976D2; font-weight: bold;");
confRow->addWidget(m_confidenceDisplay);
confRow->addStretch();
diagLayout->addLayout(confRow);
m_mainLayout->addWidget(diagGroup);
m_mainLayout->addStretch();
}
void RippleDetectionWidget::onDetectClicked()
{
m_rippleDisplay->setText("125.6");
m_diagnoseBtn->setEnabled(true);
OverlayDialog::information(this, "检测完成", "纹波检测完成可进行AI诊断");
}
void RippleDetectionWidget::onDiagnoseClicked()
{
m_diagnosisDisplay->setText("⚠ 滤波电容老化");
m_diagnosisDisplay->setStyleSheet("font-size: 15px; color: #F57C00; font-weight: bold;");
m_confidenceDisplay->setText("92.5%");
OverlayDialog::warning(this, "诊断完成",
"AI诊断结果:\n滤波电容老化\n置信度: 92.5%\n\n建议: 更换输出滤波电容");
}
// ============== SignalMeasurementWidget ==============
SignalMeasurementWidget::SignalMeasurementWidget(QWidget *parent)
: QWidget(parent), m_titleLabel(nullptr), m_currentModuleIndex(-1)
{
setupUI();
}
void SignalMeasurementWidget::setModule(int moduleIndex)
{
onModuleSelected(moduleIndex);
}
void SignalMeasurementWidget::setupUI()
{
auto *mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(0);
// 顶部工具栏
auto *headerWidget = new QWidget(this);
headerWidget->setStyleSheet("background: #263238;");
headerWidget->setFixedHeight(48);
auto *headerLayout = new QHBoxLayout(headerWidget);
headerLayout->setContentsMargins(8, 0, 16, 0);
headerLayout->setSpacing(12);
auto *backBtn = new QPushButton("返回", this);
backBtn->setFixedHeight(34);
backBtn->setStyleSheet(
"QPushButton { background: #455A64; color: white; border: none; border-radius: 4px; "
"padding: 0 14px; font-size: 13px; }"
"QPushButton:hover { background: #546E7A; }"
"QPushButton:pressed { background: #37474F; }");
connect(backBtn, &QPushButton::clicked, this, &SignalMeasurementWidget::onBackClicked);
headerLayout->addWidget(backBtn);
m_titleLabel = new QLabel("信号测量功能", this);
m_titleLabel->setStyleSheet("color: white; font-size: 16px; font-weight: bold;");
headerLayout->addWidget(m_titleLabel, 1);
mainLayout->addWidget(headerWidget);
// 内容区域 - 添加滚动支持
setupContentArea();
auto *scrollArea = new QScrollArea(this);
scrollArea->setWidget(m_contentStack);
scrollArea->setWidgetResizable(true);
scrollArea->setFrameShape(QFrame::NoFrame);
scrollArea->setStyleSheet(
"QScrollArea { background: #FAFAFA; border: none; }"
"QScrollBar:vertical { width: 8px; background: transparent; }"
"QScrollBar::handle:vertical { background: #BDBDBD; border-radius: 4px; min-height: 30px; }"
"QScrollBar::handle:vertical:hover { background: #9E9E9E; }"
"QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical { height: 0px; }"
"QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { background: transparent; }");
mainLayout->addWidget(scrollArea, 1);
}
void SignalMeasurementWidget::setupContentArea()
{
m_contentStack = new QStackedWidget(this);
m_rtdWidget = new RTDMeasurementWidget(this);
m_tcWidget = new ThermocoupleMeasurementWidget(this);
m_insulationWidget = new InsulationMeasurementWidget(this);
m_dcCurrentWidget = new DCCurrentWidget(this);
m_dcVoltageWidget = new DCVoltageWidget(this);
m_frequencyWidget = new FrequencyWidget(this);
m_acVoltageWidget = new ACVoltageWidget(this);
m_switchWidget = new SwitchDetectionWidget(this);
m_acquisitionWidget = new SignalAcquisitionWidget(this);
m_rampWidget = new RampSignalWidget(this);
m_rippleWidget = new RippleDetectionWidget(this);
m_rtdOutputWidget = new RTDOutputWidget(this);
m_contentStack->addWidget(m_rtdWidget);
m_contentStack->addWidget(m_tcWidget);
m_contentStack->addWidget(m_insulationWidget);
m_contentStack->addWidget(m_dcCurrentWidget);
m_contentStack->addWidget(m_dcVoltageWidget);
m_contentStack->addWidget(m_frequencyWidget);
m_contentStack->addWidget(m_acVoltageWidget);
m_contentStack->addWidget(m_switchWidget);
m_contentStack->addWidget(m_acquisitionWidget);
m_contentStack->addWidget(m_rampWidget);
m_contentStack->addWidget(m_rippleWidget);
m_contentStack->addWidget(m_rtdOutputWidget);
}
void SignalMeasurementWidget::onBackClicked()
{
emit backRequested();
}
void SignalMeasurementWidget::onModuleSelected(int index)
{
QStringList moduleTitles = {
"热电阻测量", "热电偶测量", "绝缘电阻测量",
"直流电流测量", "直流电压测量", "频率信号测量",
"220VAC测量", "开关量检测", "信号采集",
"斜波信号输出", "纹波检测诊断", "PT100模拟输出"};
m_currentModuleIndex = index;
if (index >= 0 && index < moduleTitles.size())
{
m_titleLabel->setText(moduleTitles[index]);
m_contentStack->setCurrentIndex(index);
}
}
// ============== RTDOutputWidget ==============
RTDOutputWidget::RTDOutputWidget(QWidget *parent)
: MeasurementModuleBase("PT100模拟输出", parent),
m_isChannel1Outputting(false),
m_isChannel2Outputting(false)
{
setupUI();
}
RTDOutputWidget::~RTDOutputWidget()
{
}
void RTDOutputWidget::setupUI()
{
// === 通道1输出组 ===
auto *channel1Group = createStyledGroupBox("PT100 模拟输出 (输出通道1)");
auto *ch1Layout = new QVBoxLayout(channel1Group);
ch1Layout->setSpacing(16);
m_channel1TempSpin = createStyledSpinBox(-200, 850, 1, 150);
m_channel1TempSpin->setValue(25.0);
connect(m_channel1TempSpin, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
this, &RTDOutputWidget::onChannel1TempChanged);
ch1Layout->addLayout(createLabeledRow("设定温度", m_channel1TempSpin, "°C"));
auto *ch1DisplayRow = new QHBoxLayout();
ch1DisplayRow->setSpacing(16);
auto *ch1ResLbl = new QLabel("输出电阻 (Ω):", this);
ch1ResLbl->setStyleSheet(LABEL_STYLE);
ch1DisplayRow->addWidget(ch1ResLbl);
m_channel1ResDisplay = createValueDisplay("---", 180);
ch1DisplayRow->addWidget(m_channel1ResDisplay);
ch1DisplayRow->addStretch();
ch1Layout->addLayout(ch1DisplayRow);
auto *ch1BtnRow = new QHBoxLayout();
m_channel1OutputBtn = createPrimaryButton("启动输出", 120);
connect(m_channel1OutputBtn, &QPushButton::clicked, this, &RTDOutputWidget::onChannel1OutputClicked);
ch1BtnRow->addWidget(m_channel1OutputBtn);
m_channel1StopBtn = createSecondaryButton("停止输出", 120);
m_channel1StopBtn->setEnabled(false);
connect(m_channel1StopBtn, &QPushButton::clicked, this, &RTDOutputWidget::onChannel1StopClicked);
ch1BtnRow->addWidget(m_channel1StopBtn);
ch1BtnRow->addStretch();
ch1Layout->addLayout(ch1BtnRow);
m_mainLayout->addWidget(channel1Group);
// === 通道2输出组 ===
auto *channel2Group = createStyledGroupBox("PT100 模拟输出 (输出通道2)");
auto *ch2Layout = new QVBoxLayout(channel2Group);
ch2Layout->setSpacing(16);
m_channel2TempSpin = createStyledSpinBox(-200, 850, 1, 150);
m_channel2TempSpin->setValue(25.0);
connect(m_channel2TempSpin, QOverload<double>::of(&QDoubleSpinBox::valueChanged),
this, &RTDOutputWidget::onChannel2TempChanged);
ch2Layout->addLayout(createLabeledRow("设定温度", m_channel2TempSpin, "°C"));
auto *ch2DisplayRow = new QHBoxLayout();
ch2DisplayRow->setSpacing(16);
auto *ch2ResLbl = new QLabel("输出电阻 (Ω):", this);
ch2ResLbl->setStyleSheet(LABEL_STYLE);
ch2DisplayRow->addWidget(ch2ResLbl);
m_channel2ResDisplay = createValueDisplay("---", 180);
ch2DisplayRow->addWidget(m_channel2ResDisplay);
ch2DisplayRow->addStretch();
ch2Layout->addLayout(ch2DisplayRow);
auto *ch2BtnRow = new QHBoxLayout();
m_channel2OutputBtn = createPrimaryButton("启动输出", 120);
connect(m_channel2OutputBtn, &QPushButton::clicked, this, &RTDOutputWidget::onChannel2OutputClicked);
ch2BtnRow->addWidget(m_channel2OutputBtn);
m_channel2StopBtn = createSecondaryButton("停止输出", 120);
m_channel2StopBtn->setEnabled(false);
connect(m_channel2StopBtn, &QPushButton::clicked, this, &RTDOutputWidget::onChannel2StopClicked);
ch2BtnRow->addWidget(m_channel2StopBtn);
ch2BtnRow->addStretch();
ch2Layout->addLayout(ch2BtnRow);
m_mainLayout->addWidget(channel2Group);
m_mainLayout->addStretch();
// 初始化显示
onChannel1TempChanged(m_channel1TempSpin->value());
onChannel2TempChanged(m_channel2TempSpin->value());
}
void RTDOutputWidget::onChannel1OutputClicked()
{
m_isChannel1Outputting = true;
m_channel1OutputBtn->setEnabled(false);
m_channel1StopBtn->setEnabled(true);
m_channel1TempSpin->setEnabled(false);
// TODO: 实际发送输出命令到硬件
qDebug() << "通道1开始输出:" << m_channel1TempSpin->value() << "°C";
}
void RTDOutputWidget::onChannel1StopClicked()
{
m_isChannel1Outputting = false;
m_channel1OutputBtn->setEnabled(true);
m_channel1StopBtn->setEnabled(false);
m_channel1TempSpin->setEnabled(true);
// TODO: 发送停止输出命令到硬件
qDebug() << "通道1停止输出";
}
void RTDOutputWidget::onChannel2OutputClicked()
{
m_isChannel2Outputting = true;
m_channel2OutputBtn->setEnabled(false);
m_channel2StopBtn->setEnabled(true);
m_channel2TempSpin->setEnabled(false);
// TODO: 实际发送输出命令到硬件
qDebug() << "通道2开始输出:" << m_channel2TempSpin->value() << "°C";
}
void RTDOutputWidget::onChannel2StopClicked()
{
m_isChannel2Outputting = false;
m_channel2OutputBtn->setEnabled(true);
m_channel2StopBtn->setEnabled(false);
m_channel2TempSpin->setEnabled(true);
// TODO: 发送停止输出命令到硬件
qDebug() << "通道2停止输出";
}
void RTDOutputWidget::onChannel1TempChanged(double value)
{
double resistance = convertTemperatureToResistance(value);
m_channel1ResDisplay->setText(QString::number(resistance, 'f', 2));
}
void RTDOutputWidget::onChannel2TempChanged(double value)
{
double resistance = convertTemperatureToResistance(value);
m_channel2ResDisplay->setText(QString::number(resistance, 'f', 2));
}
double RTDOutputWidget::convertTemperatureToResistance(double temperature)
{
// PT100标准公式 R(T) = R0 * (1 + A*T + B*T^2)
// R0 = 100Ω, A = 3.9083e-3, B = -5.775e-7 (0-850°C)
const double R0 = 100.0;
const double A = 3.9083e-3;
const double B = -5.775e-7;
if (temperature >= 0)
{
return R0 * (1.0 + A * temperature + B * temperature * temperature);
}
else
{
// 负温度区 R(T) = R0 * (1 + A*T + B*T^2 + C*(T-100)*T^3)
const double C = -4.183e-12;
return R0 * (1.0 + A * temperature + B * temperature * temperature +
C * (temperature - 100.0) * temperature * temperature * temperature);
}
}