#include "P00_PublicPage.h" #include "ui_P00_PublicPage.h" #include #include #include #include #include #include #include #include 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::smoothScrollTo(int targetX) { QScrollBar* hScroll = ui->scrollArea->horizontalScrollBar(); uint16_t MaxValue = ui->scrollArea->horizontalScrollBar()->maximum(); uint16_t Current = MaxValue / 15 * targetX; if (!hScroll) return; // 创建属性动画,作用于滚动条的value属性 QPropertyAnimation* animation = new QPropertyAnimation(hScroll, "value", this); animation->setDuration(600); // 动画持续时间,单位:毫秒 animation->setEasingCurve(QEasingCurve::OutQuart); // 动画曲线,使滚动更自然 animation->setStartValue(hScroll->value()); // 起始值为当前位置 animation->setEndValue(Current); // 目标位置 // 启动动画 animation->start(QAbstractAnimation::DeleteWhenStopped); // 动画结束后自动删除 } 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() { ui->Label_VoiceWidget->resize(960,0); ui->Label_VoiceWidget->move(32,70+576/2); ui->Label_VoiceWidget->hide(); // 初始化定时器(作为成员变量) 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(gOPC_NodeValue["ns=6;s=::AsGlobalPV:AlarmCode.Index"].toUInt()); uint8_t AlarmAllNum = AlarmNumber / 256; uint8_t AlarmIndex = AlarmNumber % 256; uint16_t LoopAlarm = static_cast(gOPC_NodeValue["ns=6;s=::AsGlobalPV:AlarmCode.Loop"].toUInt()); uint8_t LoopAlarm_Dev = LoopAlarm / 256; uint8_t LoopAlarm_Ind = LoopAlarm % 256; uint32_t Bools_4 = gOPC_NodeValue["ns=6;s=::AsGlobalPV:PageBOOLS.B4"].toUInt(); 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("无效报警"); } QStringList VoiceList; VoiceList <<"油泵电机即将启动\n工作人员请注意!!!" <<"截割电机即将启动\n工作人员请注意!!!" <<"收集电机即将启动\n工作人员请注意!!!" <<"运输电机即将正转\n工作人员请注意!!!" <<"运输电机即将反转\n工作人员请注意!!!" <<"备用电机即将启动\n工作人员请注意!!!" <<"左履带正在前进\n工作人员请注意!!!" <<"左履带正在后退\n工作人员请注意!!!" <<"右履带正在前进\n工作人员请注意!!!" <<"右履带正在后退\n工作人员请注意!!!" <<"双履带正在前进\n工作人员请注意!!!" <<"双履带正在后退\n工作人员请注意!!!" <<"双履带顺时针转向\n工作人员请注意!!!" <<"双履带逆时针转向\n工作人员请注意!!!" <<"摇臂正在上升\n工作人员请注意!!!" <<"摇臂正在下降\n工作人员请注意!!!" <<"掏槽正在伸出\n工作人员请注意!!!" <<"掏槽正在缩回\n工作人员请注意!!!"; bool isVoice = false; for (uint8_t i=0;i<32 && !isVoice;i++) { if(getBitOf32Data(Bools_4, i,false)){ VoiceWin(VoiceList[i],true); isVoice = true; } } if(!isVoice) VoiceWin("",false); // qDebug()<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(); } // 页面切换实现 void P00_PublicPage::on_But_Page_0_clicked(){ ChangePage( 0, 1, "P01");} void P00_PublicPage::on_But_Page_1_clicked(){ ChangePage( 1, 2, "P02");} void P00_PublicPage::on_But_Page_2_clicked(){ ChangePage( 2, 301, "P301");} void P00_PublicPage::on_But_Page_3_clicked(){ ChangePage( 3, 401, "P401");} void P00_PublicPage::on_But_Page_4_clicked(){ ChangePage( 4, 501, "P501");} void P00_PublicPage::on_But_Page_5_clicked(){ ChangePage( 5, 601, "P601");} void P00_PublicPage::on_But_Page_6_clicked(){ ChangePage( 6, 7, "P07");} void P00_PublicPage::on_But_Page_7_clicked(){ ChangePage( 7, 8, "P08");} void P00_PublicPage::on_But_Page_8_clicked(){ ChangePage( 8, 9, "P09");} void P00_PublicPage::on_But_Page_9_clicked(){ ChangePage( 9, 10, "P10");} void P00_PublicPage::on_But_Page_10_clicked(){ ChangePage(10, 11, "P11");} void P00_PublicPage::on_But_Page_11_clicked(){ ChangePage(11, 12, "P12.00");} void P00_PublicPage::on_But_Page_12_clicked(){ ChangePage(12, 13, "P13");} void P00_PublicPage::on_But_Page_13_clicked(){ ChangePage(13, 14, "P14");} void P00_PublicPage::on_But_Page_14_clicked(){ ChangePage(14, 15, "P15");} void P00_PublicPage::on_But_Page_15_clicked(){ ChangePage(15, 16, "P16");} void P00_PublicPage::ChangePage(uint16_t PageIndex,uint16_t PageNum,QString PageName){ smoothScrollTo(PageIndex); m_opcManager->writeNodeValue("ns=6;s=::AsGlobalPV:PageContol.CurrentPage", PageNum,5,100); gPageIndexStr = PageName; } /* 1. 读写属性 */ QRect P00_PublicPage::voiceGeometry() const { return ui->Label_VoiceWidget->geometry(); } void P00_PublicPage::setVoiceGeometry(const QRect &g) { ui->Label_VoiceWidget->setGeometry(g); } /* 2. 一条动画同时驱动 geometry */ void P00_PublicPage::startGeometryAnim(const QRect &start, const QRect &end, int dur) { auto *anim = new QPropertyAnimation(this, "voiceGeometry"); anim->setDuration(dur); anim->setStartValue(start); anim->setEndValue(end); anim->setEasingCurve(QEasingCurve::OutQuad); /* 空组保活,动画结束自动销毁 */ auto *group = new QParallelAnimationGroup(this); group->addAnimation(anim); connect(group, &QAbstractAnimation::finished, [this]{ if (voiceGeometry().height() == 0) ui->Label_VoiceWidget->hide(); }); group->start(QAbstractAnimation::DeleteWhenStopped); } /* 3. 唯一入口:VoiceWin */ void P00_PublicPage::VoiceWin(const QString &Info, bool shouldShow) { ui->Label_VoiceWidget->setText(Info); if (shouldShow == m_realShow) return; m_realShow = shouldShow; const int w = 960; const int baseY = 70 + 576 / 2; // 中心线 const int x = (1024 - w) / 2; QRect start = ui->Label_VoiceWidget->geometry(); QRect end; if (shouldShow) { // 展开 ui->Label_VoiceWidget->show(); int h = 380; int y = baseY - h / 2; end = QRect(x, y, w, h); startGeometryAnim(start, end, 500); // 250 ms 展开 } else { // 收起 int h = 0; int y = baseY - h / 2; end = QRect(x, y, w, h); startGeometryAnim(start, end, 100); // 80 ms 快速收起 } }