Files
EJM_Display/Pages/P00_PublicPage.cpp
2025-09-15 22:28:43 +08:00

381 lines
13 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 "P00_PublicPage.h"
#include "ui_P00_PublicPage.h"
#include <QDebug>
#include <QVBoxLayout>
#include <QMovie>
#include <type_traits>
#include <FileOperation/ConfigFiles.h>
#include <PublicFunctions/TTSManager.h>
#include <QKeyEvent>
// 1. 模板声明
template <typename T> struct PageNumber;
template <typename T> struct PageIndexs;
// 2. 宏:自动特化 PageNumber<CLASS> 的 value
#define REG_PAGE(Class, Num) \
template <> struct PageNumber<Class> { static constexpr int value = Num; }
// 3. 把现有页面一次性注册完
REG_PAGE(P01_MianPage, 1);
REG_PAGE(P02_ShieldPage, 2);
REG_PAGE(P301_PumpPage, 301);
REG_PAGE(P401_CuttingPage, 401);
REG_PAGE(P501_LoaderPage, 501);
REG_PAGE(P601_TransportPage, 601);
REG_PAGE(P07_CylinderPage, 7);
REG_PAGE(P08_AlarmPage, 8);
REG_PAGE(P09_RemotePage, 9);
REG_PAGE(P10_IntelligentPage,10);
REG_PAGE(P11_IsolationPage, 11);
REG_PAGE(P12_NetworkPage, 12);
REG_PAGE(P13_InsPage, 13);
REG_PAGE(P14_AtlasPage, 14);
REG_PAGE(P15_ParameterPage, 15);
REG_PAGE(P16_SettingPage, 16);
// 修复字符串常量类型 - 添加const
#define REG_PAGE1(Class, str) \
template <> struct PageIndexs<Class> { static constexpr const char* value = str; }
// 3. 注册页面类
REG_PAGE1(P01_MianPage, "P01");
REG_PAGE1(P02_ShieldPage, "P02");
REG_PAGE1(P301_PumpPage, "P301");
REG_PAGE1(P401_CuttingPage, "P401");
REG_PAGE1(P501_LoaderPage, "P501");
REG_PAGE1(P601_TransportPage, "P601");
REG_PAGE1(P07_CylinderPage, "P07");
REG_PAGE1(P08_AlarmPage, "P08");
REG_PAGE1(P09_RemotePage, "P09");
REG_PAGE1(P10_IntelligentPage,"P10");
REG_PAGE1(P11_IsolationPage, "P11");
REG_PAGE1(P12_NetworkPage, "P12");
REG_PAGE1(P13_InsPage, "P13");
REG_PAGE1(P14_AtlasPage, "P14");
REG_PAGE1(P15_ParameterPage, "P15");
REG_PAGE1(P16_SettingPage, "P16");
P00_PublicPage::P00_PublicPage(QWidget *parent)
: QWidget(parent)
, ui(new Ui::P00_PublicPage)
, m_opcManager(OpcUaManager::instance(this))
, m_dataCenter(DataCenter::instance(this))
, tts(nullptr)
, UIRefresh_Timer(nullptr)
, CursorScan_Timer(nullptr)
, PLC_Started(false)
{
ui->setupUi(this);
CursorInit();
WinInit();
SignalSlotInit();
// 连接服务器
tsServerAddr tmpServerAddr = ConfigFiles::ReadServerAddr();
m_opcManager->connectToServer(tmpServerAddr.Host, tmpServerAddr.UserName, tmpServerAddr.Passwd);
// 初始化TTS
tts = new QTextToSpeech(this);
tts->setLocale(QLocale("zh_CN"));
tts->setRate(0.0);
tts->setPitch(1.0);
tts->setVolume(1.0);
qDebug() << "可用语音列表:";
for (const QVoice &v : tts->availableVoices()) {
qDebug() << v.name();
}
connect(tts, &QTextToSpeech::stateChanged,
[](QTextToSpeech::State s){
if (s == QTextToSpeech::Ready)
qDebug() << "TTS ready";
});
}
P00_PublicPage::~P00_PublicPage()
{
// 清理定时器
if (UIRefresh_Timer) {
UIRefresh_Timer->stop();
delete UIRefresh_Timer;
}
if (CursorScan_Timer) {
CursorScan_Timer->stop();
delete CursorScan_Timer;
}
// 清理TTS
if (tts) {
tts->stop();
delete tts;
}
// 清理缓存的窗口
qDeleteAll(m_widgetCache);
m_widgetCache.clear();
delete ui;
}
void P00_PublicPage::CursorInit()
{
/* 自绘光标 */
m_cursorLabel = new QLabel(this);
m_cursorLabel->setPixmap(QPixmap(":/Icos/Icos/Sursor.png"));
m_cursorLabel->setFixedSize(m_cursorLabel->pixmap()->size());
m_cursorLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
m_cursorLabel->raise();
m_cursorLabel->show();
/* 光标控制器 */
gCursor = new CursorController(this);
connect(gCursor, &CursorController::cursorMoved,
this, &P00_PublicPage::onCursorMoved);
gCursor->cfg.step = 2;
gCursor->cfg.wrap = false;
gCursor->cfg.emulate = true;
setFocusPolicy(Qt::StrongFocus);
setFocus();
activateWindow();
grabKeyboard();
}
void P00_PublicPage::onCursorMoved(const QPoint &global)
{
QPoint local = mapFromGlobal(global);
m_cursorLabel->move(local - m_cursorLabel->rect().center());
}
void P00_PublicPage::WinInit()
{
this->move(0,0);
setWindowFlag(Qt::FramelessWindowHint );
setAttribute(Qt::WA_TranslucentBackground, true);
ui->But_Page_0->setChecked(false);
// 初始化主页面
Pub_MianPage = new P01_MianPage(this);
Pub_MianPage->setWindowFlags(Qt::Widget);
if (!ui->widgetContainer->layout()) {
ui->widgetContainer->setLayout(new QVBoxLayout);
}
ui->widgetContainer->layout()->addWidget(Pub_MianPage);
Pub_MianPage->show();
m_widgetCache[PageNumber<P01_MianPage>::value] = Pub_MianPage;
// 初始化定时器(作为成员变量)
UIRefresh_Timer = new QTimer(this);
connect(UIRefresh_Timer, &QTimer::timeout, this, &P00_PublicPage::UIRefreshTimeOut);
UIRefresh_Timer->setInterval(100);
UIRefresh_Timer->start();
CursorScan_Timer = new QTimer(this);
connect(CursorScan_Timer, &QTimer::timeout, this, &P00_PublicPage::CursorScanTimerOut);
CursorScan_Timer->setInterval(50);
CursorScan_Timer->start();
#if defined(Q_OS_WIN)
ui->Label_BorderImg->setStyleSheet("border-image: url(:/Imgs/Imgs/newbg_all.png);");
ui->Label_BorderImg->setMinimumSize(QSize(1024,768));
ui->Label_BorderImg->move(0,0);
#endif
}
void P00_PublicPage::SignalSlotInit()
{
m_dataCenter->initData();
connect(m_opcManager, &OpcUaManager::stateChanged,
this, &P00_PublicPage::onOpcStateChanged, Qt::QueuedConnection);
}
void P00_PublicPage::keyPressEvent(QKeyEvent *e)
{
gCursor->handleKey(e->key(), true);
}
void P00_PublicPage::onOpcStateChanged(QOpcUaClient::ClientState state)
{
switch (state) {
case QOpcUaClient::Disconnected:
ui->LED_ConnectionStatus->show();
m_dataCenter->stopCyclicRead();
PLC_Started = false;
break;
case QOpcUaClient::Connecting:
ui->LED_ConnectionStatus->show();
m_dataCenter->stopCyclicRead();
PLC_Started = false;
break;
case QOpcUaClient::Connected:
ui->LED_ConnectionStatus->hide();
m_dataCenter->startCyclicRead(20);
break;
default:
break;
}
}
void P00_PublicPage::UIRefreshTimeOut(){
// 获取当前时间
ui->Title->setText(gPageName[gPageIndexStr]);
QDateTime currentTime = QDateTime::currentDateTime();
QString currentTimeStr = currentTime.toString("yyyy-MM-dd HH:mm:ss");
ui->Lable_SystemTime->setText(currentTimeStr);
ui->LCD_SystemVal_U1->display(gOPC_NodeValue["ns=6;s=::AsGlobalPV:IN_SystemVal.Filtered30_U"].toReal());
ui->LCD_SystemVal_V1->display(gOPC_NodeValue["ns=6;s=::AsGlobalPV:IN_SystemVal.Filtered30_V"].toReal());
ui->LCD_SystemVal_W1->display(gOPC_NodeValue["ns=6;s=::AsGlobalPV:IN_SystemVal.Filtered30_W"].toReal());
// 修复:使用正确的方式获取节点值并转换为整数
uint16_t AlarmNumber = static_cast<uint16_t>(gOPC_NodeValue["ns=6;s=::AsGlobalPV:AlarmCode.Index"].toUInt());
uint8_t AlarmAllNum = AlarmNumber / 256;
uint8_t AlarmIndex = AlarmNumber % 256;
uint16_t LoopAlarm = static_cast<uint16_t>(gOPC_NodeValue["ns=6;s=::AsGlobalPV:AlarmCode.Loop"].toUInt());
uint8_t LoopAlarm_Dev = LoopAlarm / 256;
uint8_t LoopAlarm_Ind = LoopAlarm % 256;
ui->Txt_AlarmCode->setHidden(AlarmAllNum == 0);
ui->Num_MainAlarmNum->setHidden(AlarmAllNum == 0);
ui->Lable_MainAlarm->setHidden(AlarmAllNum == 0);
ui->Num_MainAlarmNum->setText(""+QString::number(AlarmAllNum) + " 条 第 " + QString::number(AlarmIndex)+" 条:");
ui->Txt_AlarmCode->setText("(" + QString("%1").arg(LoopAlarm, 4, 16, QLatin1Char('0')).toUpper() + ")");
// 修复使用数组边界常量检查代替size()方法
const int MAX_DEV = 255; // 与AlarmText定义匹配
const int MAX_IND = 255; // 与AlarmText定义匹配
if (LoopAlarm_Dev < MAX_DEV && LoopAlarm_Ind < MAX_IND) {
ui->Lable_MainAlarm->setStyleSheet("border-image: url(:/Frames/null.png);\n" + AlarmText[LoopAlarm_Dev][LoopAlarm_Ind].TextColor);
ui->Lable_MainAlarm->setText(AlarmText[LoopAlarm_Dev][LoopAlarm_Ind].AlarmText);
} else {
ui->Lable_MainAlarm->setStyleSheet("border-image: url(:/Frames/null.png);");
ui->Lable_MainAlarm->setText("无效报警");
}
}
// 静态变量初始化
bool P00_PublicPage::OldUp = false;
bool P00_PublicPage::OldDown = false;
bool P00_PublicPage::OldLeft = false;
bool P00_PublicPage::OldRight = false;
bool P00_PublicPage::OldConfirm = false;
void P00_PublicPage::CursorScanTimerOut()
{
uint32_t CursorControl = gOPC_NodeValue["ns=6;s=::AsGlobalPV:PageContol.CursorControl"].toUInt();
bool Up = getBitOf32Data(CursorControl,10);
bool Down = getBitOf32Data(CursorControl,14);
bool Left = getBitOf32Data(CursorControl,13);
bool Right = getBitOf32Data(CursorControl,15);
bool Confirm = getBitOf32Data(CursorControl,11);
bool Return = getBitOf32Data(CursorControl,9);
// 处理按键事件(带防抖)
if(Return) {
gCursor->handleKey(Qt::Key_Enter);
}
if(Up != OldUp) {
gCursor->handleKey(Qt::Key_Up, Up);
OldUp = Up;
}
if(Down != OldDown) {
gCursor->handleKey(Qt::Key_Down, Down);
OldDown = Down;
}
if(Left != OldLeft) {
gCursor->handleKey(Qt::Key_Left, Left);
OldLeft = Left;
}
if(Right != OldRight) {
gCursor->handleKey(Qt::Key_Right, Right);
OldRight = Right;
}
if(!OldConfirm && Confirm) {
gCursor->handleKey(Qt::Key_Return);
OldConfirm = Confirm;
} else if(OldConfirm && !Confirm) {
OldConfirm = false;
}
}
void P00_PublicPage::on_But_Close_clicked()
{
qApp->quit();
}
template <typename SubWidget>
inline void P00_PublicPage::replaceSingleWidget()
{
QWidget *container = ui->widgetContainer;
if (!container) return;
if (!container->layout())
container->setLayout(new QVBoxLayout(container));
constexpr uint16_t pageNum = PageNumber<SubWidget>::value;
// 1. 先把当前布局里的所有控件都移除并隐藏
QLayoutItem *child;
while ((child = container->layout()->takeAt(0)) != nullptr) {
if (child->widget()) {
child->widget()->hide();
}
delete child; // 注意:这里只是删除 layout item不会删除 widget
}
// 2. 从缓存中找目标窗口,没有就创建
QWidget *w = nullptr;
if (m_widgetCache.contains(pageNum)) {
w = m_widgetCache[pageNum];
} else {
w = new SubWidget(container);
w->setWindowFlags(Qt::Widget);
m_widgetCache[pageNum] = w;
}
// 3. 把目标窗口加入布局并显示
container->layout()->addWidget(w);
w->show();
// 4. 其他逻辑
m_opcManager->writeNodeValue("ns=6;s=::AsGlobalPV:PageContol.CurrentPage", pageNum);
QString str = QStringLiteral("当前页 %1").arg(pageNum);
// // 修复使用tts成员变量而不是未定义的ttsManager
// if (tts && tts->state() == QTextToSpeech::Ready) {
// tts->say(str);
// }
gPageIndexStr = PageIndexs<SubWidget>::value;
}
// 页面切换实现
void P00_PublicPage::on_But_Page_0_clicked(){ replaceSingleWidget<P01_MianPage>(); }
void P00_PublicPage::on_But_Page_1_clicked(){ replaceSingleWidget<P02_ShieldPage>(); }
void P00_PublicPage::on_But_Page_2_clicked(){ replaceSingleWidget<P301_PumpPage>(); }
void P00_PublicPage::on_But_Page_3_clicked(){ replaceSingleWidget<P401_CuttingPage>(); }
void P00_PublicPage::on_But_Page_4_clicked(){ replaceSingleWidget<P501_LoaderPage>(); }
void P00_PublicPage::on_But_Page_5_clicked(){ replaceSingleWidget<P601_TransportPage>(); }
void P00_PublicPage::on_But_Page_6_clicked(){ replaceSingleWidget<P07_CylinderPage>(); }
void P00_PublicPage::on_But_Page_7_clicked(){ replaceSingleWidget<P08_AlarmPage>(); }
void P00_PublicPage::on_But_Page_8_clicked(){ replaceSingleWidget<P09_RemotePage>(); }
void P00_PublicPage::on_But_Page_9_clicked(){ replaceSingleWidget<P10_IntelligentPage>(); }
void P00_PublicPage::on_But_Page_10_clicked(){ replaceSingleWidget<P11_IsolationPage>(); }
void P00_PublicPage::on_But_Page_11_clicked(){ replaceSingleWidget<P12_NetworkPage>(); }
void P00_PublicPage::on_But_Page_12_clicked(){ replaceSingleWidget<P13_InsPage>(); }
void P00_PublicPage::on_But_Page_13_clicked(){ replaceSingleWidget<P14_AtlasPage>(); }
void P00_PublicPage::on_But_Page_14_clicked(){ replaceSingleWidget<P15_ParameterPage>(); }
void P00_PublicPage::on_But_Page_15_clicked(){ replaceSingleWidget<P16_SettingPage>(); }