787 lines
32 KiB
C++
Raw Permalink Normal View History

#include "ILedDisplayDevice.h"
#include <QApplication>
#include <QButtonGroup>
#include <QComboBox>
#include <QDateTime>
#include <QDoubleSpinBox>
#include <QFormLayout>
#include <QGridLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QLabel>
#include <QLineEdit>
#include <QMainWindow>
#include <QMessageBox>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QScrollArea>
#include <QSpinBox>
#include <QTableWidget>
#include <QTextCodec>
#include <QTimer>
#include <QVBoxLayout>
#include <memory>
#include <string>
#include <vector>
namespace {
struct GuideStateItem
{
int code;
const char* name;
const char* description;
bool danger = false;
};
const std::vector<GuideStateItem> kGuideStates = {
{1, "START", "开始引导"},
{2, "CAPTURE", "目标捕获"},
{5, "CENTER", "对中引导"},
{6, "SLOW", "减速提示"},
{7, "AZIMUTH", "方位引导"},
{8, "STOP", "到位停止", true},
{10, "TOO FAR", "停止过远"},
{15, "ID FAIL", "识别失败", true},
{16, "GATE BLOCK", "入口遮挡"},
{17, "VIEW BLOCK", "视野遮挡"},
{20, "STOP", "停止状态", true},
{21, "CHOCK ON", "轮挡到位"},
{22, "ERROR", "系统错误", true},
};
QString StatusName(ELedDisplayStatus status)
{
switch (status) {
case ELedDisplayStatus::Disconnected:
return QStringLiteral("未连接");
case ELedDisplayStatus::Connected:
return QStringLiteral("已连接");
case ELedDisplayStatus::Error:
return QStringLiteral("异常");
}
return QStringLiteral("未知");
}
class LedDisplayTestWindow final : public QMainWindow
{
public:
LedDisplayTestWindow()
{
setWindowTitle(QStringLiteral("LED 显示屏协议调试"));
setMinimumSize(1000, 700);
resize(1220, 820);
ILedDisplayDevice* rawDevice = nullptr;
if (ILedDisplayDevice::CreateObject(&rawDevice) == 0 && rawDevice) {
m_device.reset(rawDevice);
m_device->SetStatusCallback(
[](ELedDisplayStatus status,
const std::string& message,
void* user) {
auto* self = static_cast<LedDisplayTestWindow*>(user);
if (self) {
self->handleDeviceStatus(
status, QString::fromUtf8(message.c_str()));
}
},
this);
}
buildUi();
applyStyle();
setConnected(false);
appendLog(QStringLiteral("LED 协议调试工具已启动"));
}
~LedDisplayTestWindow() override
{
m_demoTimer.stop();
if (m_device) {
m_device->SetStatusCallback({}, nullptr);
m_device->Close();
}
}
private:
void buildUi()
{
auto* central = new QWidget(this);
auto* rootLayout = new QVBoxLayout(central);
rootLayout->setContentsMargins(20, 16, 20, 20);
rootLayout->setSpacing(14);
auto* headerLayout = new QHBoxLayout();
auto* titleLayout = new QVBoxLayout();
auto* title = new QLabel(QStringLiteral("LED 显示屏协议调试"), central);
title->setObjectName(QStringLiteral("pageTitle"));
auto* subtitle = new QLabel(
QStringLiteral("独立连接控制器并发送停机引导显示状态"), central);
subtitle->setObjectName(QStringLiteral("pageSubtitle"));
titleLayout->addWidget(title);
titleLayout->addWidget(subtitle);
headerLayout->addLayout(titleLayout);
headerLayout->addStretch();
m_statusDot = new QLabel(central);
m_statusDot->setFixedSize(12, 12);
m_statusText = new QLabel(QStringLiteral("未连接"), central);
m_statusText->setObjectName(QStringLiteral("connectionStatus"));
headerLayout->addWidget(m_statusDot);
headerLayout->addWidget(m_statusText);
rootLayout->addLayout(headerLayout);
auto* contentLayout = new QHBoxLayout();
contentLayout->setSpacing(14);
contentLayout->addWidget(buildSettingsPanel(), 0);
contentLayout->addWidget(buildCommandPanel(), 1);
rootLayout->addLayout(contentLayout, 1);
setCentralWidget(central);
}
QWidget* buildSettingsPanel()
{
auto* scrollArea = new QScrollArea(this);
scrollArea->setObjectName(QStringLiteral("settingsScroll"));
scrollArea->setWidgetResizable(true);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setFixedWidth(340);
auto* panel = new QWidget(scrollArea);
auto* layout = new QVBoxLayout(panel);
layout->setContentsMargins(0, 0, 8, 0);
layout->setSpacing(12);
auto* connectionGroup = new QGroupBox(QStringLiteral("连接配置"), panel);
auto* connectionForm = new QFormLayout(connectionGroup);
connectionForm->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
connectionForm->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter);
m_ipEdit = new QLineEdit(QStringLiteral("192.168.1.199"), connectionGroup);
m_portSpin = makeSpinBox(1, 65535, 5005, connectionGroup);
m_timeoutSpin = makeSpinBox(100, 60000, 1000, connectionGroup);
m_timeoutSpin->setSuffix(QStringLiteral(" ms"));
m_slaveIdEdit = new QLineEdit(QStringLiteral("0xFFFE"), connectionGroup);
m_controllerTypeEdit = new QLineEdit(QStringLiteral("0xFE"), connectionGroup);
connectionForm->addRow(QStringLiteral("控制器 IP"), m_ipEdit);
connectionForm->addRow(QStringLiteral("TCP 端口"), m_portSpin);
connectionForm->addRow(QStringLiteral("响应超时"), m_timeoutSpin);
connectionForm->addRow(QStringLiteral("从机地址"), m_slaveIdEdit);
connectionForm->addRow(QStringLiteral("控制器类型"), m_controllerTypeEdit);
auto* connectionButtons = new QHBoxLayout();
m_connectButton = new QPushButton(QStringLiteral("连接"), connectionGroup);
m_connectButton->setObjectName(QStringLiteral("primaryButton"));
m_disconnectButton = new QPushButton(QStringLiteral("断开"), connectionGroup);
connectionButtons->addWidget(m_connectButton);
connectionButtons->addWidget(m_disconnectButton);
connectionForm->addRow(connectionButtons);
layout->addWidget(connectionGroup);
auto* areaGroup = new QGroupBox(QStringLiteral("显示区域与动画"), panel);
auto* areaForm = new QFormLayout(areaGroup);
areaForm->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter);
m_areaXSpin = makeSpinBox(0, 32767, 0, areaGroup);
m_areaYSpin = makeSpinBox(0, 32767, 0, areaGroup);
m_areaWidthSpin = makeSpinBox(1, 32767, 64, areaGroup);
m_areaHeightSpin = makeSpinBox(1, 32767, 32, areaGroup);
m_startAddressSpin = makeSpinBox(0, 7, 0, areaGroup);
m_dynamicTimeoutSpin = makeSpinBox(0, 65535, 10, areaGroup);
m_dynamicTimeoutSpin->setSuffix(QStringLiteral(" s"));
m_modeSpin = makeSpinBox(1, 7, 1, areaGroup);
m_speedSpin = makeSpinBox(0, 24, 2, areaGroup);
m_staySpin = makeSpinBox(0, 255, 10, areaGroup);
areaForm->addRow(QStringLiteral("区域 X"), m_areaXSpin);
areaForm->addRow(QStringLiteral("区域 Y"), m_areaYSpin);
areaForm->addRow(QStringLiteral("区域宽度"), m_areaWidthSpin);
areaForm->addRow(QStringLiteral("区域高度"), m_areaHeightSpin);
areaForm->addRow(QStringLiteral("动态区编号"), m_startAddressSpin);
areaForm->addRow(QStringLiteral("动态超时"), m_dynamicTimeoutSpin);
areaForm->addRow(QStringLiteral("显示模式"), m_modeSpin);
areaForm->addRow(QStringLiteral("动画速度"), m_speedSpin);
areaForm->addRow(QStringLiteral("停留时间"), m_staySpin);
layout->addWidget(areaGroup);
layout->addStretch();
scrollArea->setWidget(panel);
connect(m_connectButton, &QPushButton::clicked,
this, [this]() { connectDevice(); });
connect(m_disconnectButton, &QPushButton::clicked,
this, [this]() { disconnectDevice(); });
return scrollArea;
}
QWidget* buildCommandPanel()
{
auto* panel = new QWidget(this);
auto* layout = new QVBoxLayout(panel);
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(12);
auto* dataGroup = new QGroupBox(QStringLiteral("测试数据"), panel);
auto* previewGroup = new QGroupBox(QStringLiteral("Screen Preview (one full-screen area)"), panel);
auto* previewLayout = new QVBoxLayout(previewGroup);
m_screenPreview = new QLabel(QStringLiteral("NO DATA"), previewGroup);
m_screenPreview->setObjectName(QStringLiteral("screenPreview"));
m_screenPreview->setAlignment(Qt::AlignCenter);
m_screenPreview->setWordWrap(true);
m_screenPreview->setMinimumHeight(190);
previewLayout->addWidget(m_screenPreview);
layout->addWidget(previewGroup);
auto* dataGrid = new QGridLayout(dataGroup);
m_modelEdit = new QLineEdit(QStringLiteral("A320"), dataGroup);
m_distanceSpin = makeDoubleSpinBox(-1000000.0, 1000000.0, 12500.0,
QStringLiteral(" mm"), dataGroup);
m_offsetSpin = makeDoubleSpinBox(-100000.0, 100000.0, 0.0,
QStringLiteral(" mm"), dataGroup);
m_angleSpin = makeDoubleSpinBox(-360.0, 360.0, 0.0,
QStringLiteral("°"), dataGroup);
m_errorCodeSpin = makeSpinBox(-999999, 999999, 22, dataGroup);
dataGrid->addWidget(new QLabel(QStringLiteral("机型"), dataGroup), 0, 0);
dataGrid->addWidget(m_modelEdit, 0, 1);
dataGrid->addWidget(new QLabel(QStringLiteral("距离"), dataGroup), 0, 2);
dataGrid->addWidget(m_distanceSpin, 0, 3);
dataGrid->addWidget(new QLabel(QStringLiteral("横向偏移"), dataGroup), 1, 0);
dataGrid->addWidget(m_offsetSpin, 1, 1);
dataGrid->addWidget(new QLabel(QStringLiteral("角度"), dataGroup), 1, 2);
dataGrid->addWidget(m_angleSpin, 1, 3);
dataGrid->addWidget(new QLabel(QStringLiteral("错误码"), dataGroup), 2, 0);
dataGrid->addWidget(m_errorCodeSpin, 2, 1);
dataGrid->setColumnStretch(1, 1);
dataGrid->setColumnStretch(3, 1);
layout->addWidget(dataGroup);
auto* stateGroup = new QGroupBox(QStringLiteral("标准引导状态"), panel);
auto* stateGrid = new QGridLayout(stateGroup);
stateGrid->setSpacing(8);
for (int index = 0; index < static_cast<int>(kGuideStates.size()); ++index) {
const GuideStateItem& item = kGuideStates[static_cast<size_t>(index)];
auto* button = new QPushButton(
QStringLiteral("%1\n%2")
.arg(QString::fromLatin1(item.name),
QString::fromUtf8(item.description)),
stateGroup);
button->setProperty("stateButton", true);
button->setProperty("danger", item.danger);
button->setMinimumHeight(54);
m_sendControls.push_back(button);
connect(button, &QPushButton::clicked, this,
[this, code = item.code]() { sendState(code); });
stateGrid->addWidget(button, index / 4, index % 4);
}
layout->addWidget(stateGroup);
auto* customGroup = new QGroupBox(QStringLiteral("自定义与批量测试"), panel);
auto* customLayout = new QGridLayout(customGroup);
m_customTextEdit = new QLineEdit(customGroup);
m_customTextEdit->setPlaceholderText(QStringLiteral("输入任意显示文本"));
m_sendTextButton = new QPushButton(QStringLiteral("发送文本"), customGroup);
m_blankButton = new QPushButton(QStringLiteral("清屏"), customGroup);
m_invalidButton = new QPushButton(QStringLiteral("发送 NO DATA"), customGroup);
m_demoButton = new QPushButton(QStringLiteral("启动演示"), customGroup);
m_screenOnButton = new QPushButton(QStringLiteral("开屏"), customGroup);
m_screenOffButton = new QPushButton(QStringLiteral("关屏"), customGroup);
m_demoButton->setObjectName(QStringLiteral("accentButton"));
m_demoIntervalSpin = makeSpinBox(100, 60000, 1000, customGroup);
m_demoIntervalSpin->setSuffix(QStringLiteral(" ms"));
customLayout->addWidget(m_customTextEdit, 0, 0, 1, 3);
customLayout->addWidget(m_sendTextButton, 0, 3);
customLayout->addWidget(m_blankButton, 1, 0);
customLayout->addWidget(m_invalidButton, 1, 1);
customLayout->addWidget(m_demoIntervalSpin, 1, 2);
customLayout->addWidget(m_demoButton, 1, 3);
customLayout->addWidget(m_screenOnButton, 2, 0);
customLayout->addWidget(m_screenOffButton, 2, 1);
layout->addWidget(customGroup);
m_sendControls.push_back(m_sendTextButton);
m_sendControls.push_back(m_blankButton);
m_sendControls.push_back(m_invalidButton);
m_sendControls.push_back(m_screenOnButton);
m_sendControls.push_back(m_screenOffButton);
m_sendControls.push_back(m_demoButton);
connect(m_sendTextButton, &QPushButton::clicked,
this, [this]() { sendCustomText(); });
connect(m_blankButton, &QPushButton::clicked,
this, [this]() { sendBlank(); });
connect(m_invalidButton, &QPushButton::clicked,
this, [this]() { sendInvalid(); });
connect(m_screenOnButton, &QPushButton::clicked,
this, [this]() { setScreenPower(true); });
connect(m_screenOffButton, &QPushButton::clicked,
this, [this]() { setScreenPower(false); });
connect(m_demoButton, &QPushButton::clicked,
this, [this]() { toggleDemo(); });
connect(&m_demoTimer, &QTimer::timeout,
this, [this]() { sendNextDemoState(); });
auto* logGroup = new QGroupBox(QStringLiteral("协议响应日志"), panel);
auto* logLayout = new QVBoxLayout(logGroup);
m_logEdit = new QPlainTextEdit(logGroup);
m_logEdit->setReadOnly(true);
m_logEdit->setMaximumBlockCount(1000);
auto* clearLogButton = new QPushButton(QStringLiteral("清空日志"), logGroup);
clearLogButton->setFixedWidth(96);
auto* logToolbar = new QHBoxLayout();
logToolbar->addStretch();
logToolbar->addWidget(clearLogButton);
logLayout->addLayout(logToolbar);
logLayout->addWidget(m_logEdit, 1);
connect(clearLogButton, &QPushButton::clicked,
m_logEdit, &QPlainTextEdit::clear);
layout->addWidget(logGroup, 1);
return panel;
}
static QSpinBox* makeSpinBox(int minimum,
int maximum,
int value,
QWidget* parent)
{
auto* spin = new QSpinBox(parent);
spin->setRange(minimum, maximum);
spin->setValue(value);
spin->setAccelerated(true);
return spin;
}
static QDoubleSpinBox* makeDoubleSpinBox(double minimum,
double maximum,
double value,
const QString& suffix,
QWidget* parent)
{
auto* spin = new QDoubleSpinBox(parent);
spin->setRange(minimum, maximum);
spin->setDecimals(1);
spin->setValue(value);
spin->setSuffix(suffix);
spin->setAccelerated(true);
return spin;
}
void applyStyle()
{
setStyleSheet(QStringLiteral(R"(
QMainWindow, QWidget { background: #f4f6f8; color: #20252b; font-family: "Microsoft YaHei UI"; font-size: 13px; }
QLabel#pageTitle { font-size: 24px; font-weight: 700; color: #182028; }
QLabel#pageSubtitle { color: #68737d; font-size: 13px; }
QLabel#connectionStatus { font-size: 14px; font-weight: 600; }
QGroupBox { background: #ffffff; border: 1px solid #d7dde3; border-radius: 6px; margin-top: 14px; padding: 14px 12px 12px 12px; font-weight: 600; }
QGroupBox::title { subcontrol-origin: margin; left: 12px; padding: 0 5px; color: #35404a; }
QLineEdit, QSpinBox, QDoubleSpinBox, QComboBox { min-height: 32px; background: #ffffff; border: 1px solid #bfc8d0; border-radius: 4px; padding: 0 8px; selection-background-color: #2474a8; }
QLineEdit:focus, QSpinBox:focus, QDoubleSpinBox:focus { border: 1px solid #2474a8; }
QPushButton { min-height: 34px; background: #eef1f3; border: 1px solid #bfc8d0; border-radius: 4px; padding: 0 12px; font-weight: 600; }
QPushButton:hover { background: #e2e7eb; }
QPushButton:pressed { background: #d5dce2; }
QPushButton:disabled { color: #9aa3aa; background: #f1f3f4; border-color: #dce1e5; }
QPushButton#primaryButton { color: white; background: #2474a8; border-color: #2474a8; }
QPushButton#primaryButton:hover { background: #1d628f; }
QPushButton#accentButton { color: white; background: #2d7d56; border-color: #2d7d56; }
QPushButton#accentButton:hover { background: #246746; }
QPushButton[stateButton="true"] { text-align: left; padding: 5px 10px; background: #f8fafb; }
QPushButton[stateButton="true"]:hover { background: #e8f1f6; border-color: #6d9dbb; }
QPushButton[stateButton="true"][danger="true"] { background: #fff6f4; border-color: #dfafa6; color: #8c3024; }
QPushButton[stateButton="true"][danger="true"]:hover { background: #fde8e4; }
QPlainTextEdit { background: #171b1f; color: #d9e2e8; border: 1px solid #2c3339; border-radius: 4px; font-family: Consolas; font-size: 12px; padding: 8px; }
QLabel#screenPreview { background: #101820; color: #67f3a1; border: 2px solid #344452; border-radius: 8px; font-family: Consolas; font-size: 28px; font-weight: 700; padding: 18px; }
QScrollArea#settingsScroll { border: none; background: transparent; }
QScrollArea#settingsScroll > QWidget > QWidget { background: transparent; }
)"));
}
bool parseHexField(QLineEdit* edit,
const QString& fieldName,
int maximum,
int& value)
{
bool ok = false;
value = edit->text().trimmed().toInt(&ok, 0);
if (!ok || value < 0 || value > maximum) {
QMessageBox::warning(
this,
QStringLiteral("参数错误"),
QStringLiteral("%1 必须是 0 到 0x%2 之间的十进制或十六进制数。")
.arg(fieldName)
.arg(maximum, 0, 16));
edit->setFocus();
return false;
}
return true;
}
bool readConfig(LedDisplayConfig& config)
{
int slaveId = 0;
int controllerType = 0;
if (m_ipEdit->text().trimmed().isEmpty()) {
QMessageBox::warning(this, QStringLiteral("参数错误"),
QStringLiteral("请输入控制器 IP。"));
m_ipEdit->setFocus();
return false;
}
if (!parseHexField(m_slaveIdEdit, QStringLiteral("从机地址"),
0xFFFF, slaveId) ||
!parseHexField(m_controllerTypeEdit, QStringLiteral("控制器类型"),
0xFF, controllerType)) {
return false;
}
config.enabled = true;
config.ip = m_ipEdit->text().trimmed().toStdString();
config.port = m_portSpin->value();
config.timeoutMs = m_timeoutSpin->value();
config.slaveId = slaveId;
config.controllerType = controllerType;
config.startAddress = m_startAddressSpin->value();
config.areaX = m_areaXSpin->value();
config.areaY = m_areaYSpin->value();
config.areaWidth = m_areaWidthSpin->value();
config.areaHeight = m_areaHeightSpin->value();
config.dynamicTimeoutSec = m_dynamicTimeoutSpin->value();
config.displayMode = m_modeSpin->value();
config.speed = m_speedSpin->value();
config.stayTime = m_staySpin->value();
return true;
}
void connectDevice()
{
if (!m_device) {
QMessageBox::critical(this, QStringLiteral("初始化失败"),
QStringLiteral("无法创建设备对象。"));
return;
}
LedDisplayConfig config;
if (!readConfig(config)) {
return;
}
appendLog(QStringLiteral("连接 %1:%2,超时 %3 ms")
.arg(QString::fromStdString(config.ip))
.arg(config.port)
.arg(config.timeoutMs));
QApplication::setOverrideCursor(Qt::WaitCursor);
const int result = m_device->Open(config);
QApplication::restoreOverrideCursor();
const bool connected = result == 0 && m_device->IsConnected();
setConnected(connected);
if (connected) {
m_screenPoweredOn = true;
if (m_screenPreview) {
m_screenPreview->setText(m_lastPreviewText);
}
}
appendLog(QStringLiteral("OPEN return=%1,connected=%2")
.arg(result)
.arg(connected ? QStringLiteral("yes")
: QStringLiteral("no")));
}
void disconnectDevice()
{
stopDemo();
if (m_device) {
m_device->Close();
}
setConnected(false);
appendLog(QStringLiteral("连接已断开"));
}
LedDisplayResult makeResult(int stateCode) const
{
LedDisplayResult result;
result.valid = true;
result.guideCode = stateCode == 22 ? 2 : 1;
result.guideStateCode = stateCode;
result.errorCode = m_errorCodeSpin->value();
result.modelType = m_modelEdit->text().trimmed().toStdString();
result.distance = static_cast<float>(m_distanceSpin->value());
result.lateralOffset = static_cast<float>(m_offsetSpin->value());
result.angle = static_cast<float>(m_angleSpin->value());
return result;
}
void sendState(int stateCode)
{
sendResult(makeResult(stateCode),
QStringLiteral("state=%1").arg(stateCode));
}
QString previewText(const LedDisplayResult& result) const
{
if (result.blank) {
return QString();
}
if (!result.valid) {
return QStringLiteral("NO DATA");
}
if (!result.stateText.empty() && result.guideStateCode == 0) {
return QString::fromLocal8Bit(result.stateText.data(),
static_cast<int>(result.stateText.size()));
}
switch (result.guideStateCode) {
case 1: return QStringLiteral("START\n%1").arg(QString::fromStdString(result.modelType));
case 2: return QStringLiteral("CAPTURE\n%1").arg(QString::fromStdString(result.modelType));
case 5: return QStringLiteral("CENTER\n%1m\n%2")
.arg(result.distance / 1000.0, 0, 'f', 1)
.arg(QString::fromStdString(result.modelType));
case 6: return QStringLiteral("SLOW\n%1m")
.arg(result.distance / 1000.0, 0, 'f', 1);
case 7: return QStringLiteral("AZIMUTH\n%1m\nL:%2")
.arg(result.distance / 1000.0, 0, 'f', 1)
.arg(result.lateralOffset, 0, 'f', 0);
case 8:
case 20: return QStringLiteral("STOP");
case 10: return QStringLiteral("TOO FAR");
case 15: return QStringLiteral("STOP\nID FAIL");
case 16: return QStringLiteral("WAIT\nGATE BLOCK");
case 17: return QStringLiteral("WAIT\nVIEW BLOCK");
case 21: return QStringLiteral("CHOCK ON");
case 22: return QStringLiteral("ERROR\nE:%1").arg(result.errorCode);
default:
return result.stateText.empty()
? QStringLiteral("OK")
: QString::fromLocal8Bit(result.stateText.data(),
static_cast<int>(result.stateText.size()));
}
}
void sendCustomText()
{
const QString text = m_customTextEdit->text();
if (text.isEmpty()) {
QMessageBox::information(this, QStringLiteral("自定义文本"),
QStringLiteral("请先输入要显示的文本。"));
return;
}
LedDisplayResult result = makeResult(0);
QTextCodec* gbk = QTextCodec::codecForName("GBK");
const QByteArray encoded = gbk ? gbk->fromUnicode(text)
: text.toLocal8Bit();
result.stateText.assign(encoded.constData(),
static_cast<size_t>(encoded.size()));
sendResult(result, QStringLiteral("text=\"%1\"").arg(text));
}
void sendBlank()
{
LedDisplayResult result;
result.valid = true;
result.blank = true;
sendResult(result, QStringLiteral("blank"));
}
void sendInvalid()
{
sendResult(LedDisplayResult(), QStringLiteral("invalid / NO DATA"));
}
void setScreenPower(bool on)
{
if (!m_device || !m_device->IsConnected()) {
setConnected(false);
appendLog(QStringLiteral("Screen power command cancelled: device is not connected"));
return;
}
QApplication::setOverrideCursor(Qt::WaitCursor);
const int returnCode = on ? m_device->OpenScreen()
: m_device->CloseScreen();
QApplication::restoreOverrideCursor();
if (returnCode == 0) {
m_screenPoweredOn = on;
if (m_screenPreview) {
m_screenPreview->setText(on ? m_lastPreviewText
: QStringLiteral("SCREEN OFF"));
}
appendLog(on ? QStringLiteral("Screen ON accepted")
: QStringLiteral("Screen OFF accepted"));
} else {
appendLog(QStringLiteral("Screen power command failed, return=%1")
.arg(returnCode));
if (!m_device->IsConnected()) {
setConnected(false);
}
}
}
bool sendResult(const LedDisplayResult& result, const QString& description)
{
// The simulator models the physical screen as one full-screen
// dynamic area. Keep it useful even before a controller is connected.
m_lastPreviewText = previewText(result);
if (m_screenPreview && m_screenPoweredOn) {
m_screenPreview->setText(m_lastPreviewText);
}
if (!m_device || !m_device->IsConnected()) {
setConnected(false);
appendLog(QStringLiteral("发送已取消:设备未连接"));
return false;
}
appendLog(QStringLiteral("SEND %1").arg(description));
QApplication::setOverrideCursor(Qt::WaitCursor);
const int returnCode = m_device->SendResult(result);
QApplication::restoreOverrideCursor();
const bool success = returnCode == 0 && m_device->IsConnected();
appendLog(QStringLiteral("%1 return=%2,connected=%3")
.arg(success ? QStringLiteral("控制器已接收(实际显示需现场确认)")
: QStringLiteral("FAILED"))
.arg(returnCode)
.arg(success ? QStringLiteral("yes")
: QStringLiteral("no")));
if (!success) {
stopDemo();
setConnected(false);
}
return success;
}
void toggleDemo()
{
if (m_demoTimer.isActive()) {
stopDemo();
appendLog(QStringLiteral("自动演示已停止"));
return;
}
if (!m_device || !m_device->IsConnected()) {
setConnected(false);
return;
}
m_demoIndex = 0;
m_demoTimer.setInterval(m_demoIntervalSpin->value());
m_demoTimer.start();
m_demoButton->setText(QStringLiteral("停止演示"));
appendLog(QStringLiteral("自动演示已启动,间隔 %1 ms")
.arg(m_demoTimer.interval()));
sendNextDemoState();
}
void sendNextDemoState()
{
if (kGuideStates.empty()) {
return;
}
const GuideStateItem& item =
kGuideStates[static_cast<size_t>(m_demoIndex)];
if (!sendResult(makeResult(item.code),
QStringLiteral("demo state=%1").arg(item.code))) {
return;
}
m_demoIndex = (m_demoIndex + 1) % static_cast<int>(kGuideStates.size());
}
void stopDemo()
{
m_demoTimer.stop();
if (m_demoButton) {
m_demoButton->setText(QStringLiteral("启动演示"));
}
}
void handleDeviceStatus(ELedDisplayStatus status, const QString& message)
{
appendLog(QStringLiteral("STATUS %1%2")
.arg(StatusName(status),
message.isEmpty()
? QString()
: QStringLiteral(":%1").arg(message)));
}
void setConnected(bool connected)
{
m_connected = connected;
m_statusText->setText(connected ? QStringLiteral("已连接")
: QStringLiteral("未连接"));
m_statusDot->setStyleSheet(
connected
? QStringLiteral("background: #2d8a5f; border-radius: 6px;")
: QStringLiteral("background: #a6afb7; border-radius: 6px;"));
m_connectButton->setEnabled(!connected);
m_disconnectButton->setEnabled(connected);
for (QPushButton* button : m_sendControls) {
button->setEnabled(connected);
}
if (!connected) {
stopDemo();
}
}
void appendLog(const QString& message)
{
if (!m_logEdit) {
return;
}
m_logEdit->appendPlainText(
QStringLiteral("[%1] %2")
.arg(QDateTime::currentDateTime().toString(
QStringLiteral("HH:mm:ss.zzz")),
message));
}
private:
std::unique_ptr<ILedDisplayDevice> m_device;
bool m_connected = false;
QLabel* m_statusDot = nullptr;
QLabel* m_statusText = nullptr;
QLineEdit* m_ipEdit = nullptr;
QSpinBox* m_portSpin = nullptr;
QSpinBox* m_timeoutSpin = nullptr;
QLineEdit* m_slaveIdEdit = nullptr;
QLineEdit* m_controllerTypeEdit = nullptr;
QSpinBox* m_areaXSpin = nullptr;
QSpinBox* m_areaYSpin = nullptr;
QSpinBox* m_areaWidthSpin = nullptr;
QSpinBox* m_areaHeightSpin = nullptr;
QSpinBox* m_startAddressSpin = nullptr;
QSpinBox* m_dynamicTimeoutSpin = nullptr;
QSpinBox* m_modeSpin = nullptr;
QSpinBox* m_speedSpin = nullptr;
QSpinBox* m_staySpin = nullptr;
QLineEdit* m_modelEdit = nullptr;
QDoubleSpinBox* m_distanceSpin = nullptr;
QDoubleSpinBox* m_offsetSpin = nullptr;
QDoubleSpinBox* m_angleSpin = nullptr;
QSpinBox* m_errorCodeSpin = nullptr;
QLineEdit* m_customTextEdit = nullptr;
QSpinBox* m_demoIntervalSpin = nullptr;
QPushButton* m_connectButton = nullptr;
QPushButton* m_disconnectButton = nullptr;
QPushButton* m_sendTextButton = nullptr;
QPushButton* m_blankButton = nullptr;
QPushButton* m_invalidButton = nullptr;
QPushButton* m_screenOnButton = nullptr;
QPushButton* m_screenOffButton = nullptr;
QPushButton* m_demoButton = nullptr;
QPlainTextEdit* m_logEdit = nullptr;
QLabel* m_screenPreview = nullptr;
QString m_lastPreviewText = QStringLiteral("NO DATA");
bool m_screenPoweredOn = true;
std::vector<QPushButton*> m_sendControls;
QTimer m_demoTimer;
int m_demoIndex = 0;
};
} // namespace
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
QApplication::setApplicationName(QStringLiteral("LedDisplayDeviceTest"));
QApplication::setApplicationDisplayName(QStringLiteral("LED 显示屏协议调试"));
LedDisplayTestWindow window;
window.show();
return app.exec();
}