From 50f3078f4efb3718e921b76567bd33d6c3640a44 Mon Sep 17 00:00:00 2001
From: chenluhua1980 <Chenluhua@qq.com>
Date: 星期三, 21 一月 2026 22:20:31 +0800
Subject: [PATCH] 1.新增“读即已读”逻辑:CServoDlg::AckAlarm(int alarmId)记录已读并刷新角标;弹窗在显示任一告警时自动调用,导航上一条/下一条都会标记已读,工具条数量会同步更新。

---
 SourceCode/Bond/Servo/ServoDlg.cpp |  158 +++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 148 insertions(+), 10 deletions(-)

diff --git a/SourceCode/Bond/Servo/ServoDlg.cpp b/SourceCode/Bond/Servo/ServoDlg.cpp
index 7c6641f..7ab2e44 100644
--- a/SourceCode/Bond/Servo/ServoDlg.cpp
+++ b/SourceCode/Bond/Servo/ServoDlg.cpp
@@ -31,6 +31,7 @@
 #include "InputDialog.h"
 #include "ClientListDlg.h"
 #include "CControlJobManagerDlg.h"
+#include "AlarmManager.h"
 #include "CUserManager2.h"
 #include "CUserManager2Dlg.h"
 #include "CUserXLogDlg.h"
@@ -110,6 +111,8 @@
 	m_pTopToolbar = nullptr;
 	m_pMyStatusbar = nullptr;
 	m_pRobotTaskDlg = nullptr;
+	m_pTab = nullptr;
+	m_pAlarmPopupDlg = nullptr;
 }
 
 void CServoDlg::DoDataExchange(CDataExchange* pDX)
@@ -137,6 +140,10 @@
 	ON_UPDATE_COMMAND_UI(ID_MENU_FILE_SECSTEST, &CServoDlg::OnUpdateMenuFileSecsTest)
 	ON_COMMAND(ID_MENU_PROJECT_VARIABLE_LIST, &CServoDlg::OnMenuProjectVarialbleList)
 	ON_UPDATE_COMMAND_UI(ID_MENU_PROJECT_VARIABLE_LIST, &CServoDlg::OnUpdateMenuProjectVarialbleList)
+	ON_COMMAND(ID_MENU_TEST_ALARM_ON, &CServoDlg::OnMenuTestAlarmOn)
+	ON_UPDATE_COMMAND_UI(ID_MENU_TEST_ALARM_ON, &CServoDlg::OnUpdateMenuTestAlarmOn)
+	ON_COMMAND(ID_MENU_TEST_ALARM_OFF, &CServoDlg::OnMenuTestAlarmOff)
+	ON_UPDATE_COMMAND_UI(ID_MENU_TEST_ALARM_OFF, &CServoDlg::OnUpdateMenuTestAlarmOff)
 	ON_COMMAND(ID_MENU_TEST_MESSAGE_SET, &CServoDlg::OnMenuTestMessageSet)
 	ON_UPDATE_COMMAND_UI(ID_MENU_TEST_MESSAGE_SET, &CServoDlg::OnUpdateMenuTestMessageSet)
 	ON_COMMAND(ID_MENU_TEST_MESSAGE_CLEAR, &CServoDlg::OnMenuTestMessageClear)
@@ -264,7 +271,7 @@
 				CString text;
 				if (cj != nullptr) {
 					std::string st = cj->getStateText();
-					text.Format(_T("ControlJob: %S (%S)"), cj->id().c_str(), st.c_str());
+					text.Format(_T("ControlJob: %s (%s)"), cj->id().c_str(), st.c_str());
 					if (cj->state() == SERVO::CJState::Paused) {
 						text += _T(" [Paused]");
 					}
@@ -273,7 +280,7 @@
 					text = _T("ControlJob: None");
 				}
 				if (m_pMyStatusbar != nullptr) {
-					m_pMyStatusbar->setRunTimeText((LPTSTR)(LPCTSTR)text);
+					m_pMyStatusbar->setJobText((LPTSTR)(LPCTSTR)text);
 					if (cj != nullptr && cj->state() == SERVO::CJState::Paused) {
 						m_pMyStatusbar->setBackgroundColor(STATUSBAR_BK_ALARM);
 						m_pMyStatusbar->setForegroundColor(RGB(0, 0, 0));
@@ -326,6 +333,9 @@
 					//dlg.DoModal();
 				}
 			}
+			else if (RX_CODE_ALARM_SET == code || RX_CODE_ALARM_CLEAR == code) {
+				RefreshAlarmBadge();
+			}
 
 			if (RX_CODE_PASSIVE_STATUS_CHANGED == code) {
 				int state = 0;
@@ -357,6 +367,83 @@
 
 		theApp.m_model.getObservable()->observeOn(pRxWindows->mainThread())
 			->subscribe(m_pObserver);
+	}
+}
+
+void CServoDlg::RefreshAlarmBadge()
+{
+	if (m_pTopToolbar == nullptr) return;
+	auto activeAlarms = AlarmManager::getInstance().getActiveAlarms();
+
+	// 缁存姢鏈鍒楄〃锛氬綋鍓嶆椿璺冧笖鏈湪宸茶闆嗗悎涓殑鎶ヨ
+	std::unordered_set<int> activeIds;
+	m_unreadAlarms.clear();
+	for (const auto& alarm : activeAlarms) {
+		activeIds.insert(alarm.nId);
+		if (m_ackAlarms.find(alarm.nId) == m_ackAlarms.end()) {
+			m_unreadAlarms.push_back(alarm);
+		}
+	}
+	// 绉婚櫎宸茶闆嗗悎涓凡涓嶅啀娲昏穬鐨勫憡璀�
+	for (auto it = m_ackAlarms.begin(); it != m_ackAlarms.end(); ) {
+		if (activeIds.find(*it) == activeIds.end()) {
+			it = m_ackAlarms.erase(it);
+		}
+		else {
+			++it;
+		}
+	}
+
+	int count = static_cast<int>(m_unreadAlarms.size());
+
+	auto* pBtn = dynamic_cast<CBlButton*>(m_pTopToolbar->GetBtn(IDC_BUTTON_ALARM));
+	if (pBtn != nullptr) {
+		if (count <= 0) {
+			pBtn->SetBadgeNumber(0);
+			pBtn->ShowDotBadge(FALSE, RGB(255, 0, 0));
+			pBtn->StopFlash();
+		}
+		else if (count <= 9) {
+			pBtn->ShowDotBadge(FALSE, RGB(255, 0, 0));
+			pBtn->SetBadgeNumber(count);
+			if (!pBtn->IsFlash()) pBtn->Flash(600);
+		}
+		else {
+			pBtn->SetBadgeNumber(0);
+			pBtn->ShowDotBadge(TRUE, RGB(255, 0, 0));
+			if (!pBtn->IsFlash()) pBtn->Flash(600);
+		}
+		pBtn->EnableWindow(TRUE);
+	}
+}
+
+void CServoDlg::AckAlarm(int alarmId)
+{
+	m_ackAlarms.insert(alarmId);
+	RefreshAlarmBadge();
+}
+
+void CServoDlg::RaiseTestAlarm()
+{
+	theApp.m_model.raiseSoftAlarm(ALID_SOFTWARE_TEST_ALARM, "Test Alarm (Ctrl+Alt+T)");
+}
+
+void CServoDlg::ClearTestAlarm()
+{
+	theApp.m_model.clearSoftAlarm(ALID_SOFTWARE_TEST_ALARM);
+}
+
+void CServoDlg::MarkAlarmsRead()
+{
+	auto* pBtn = dynamic_cast<CBlButton*>(m_pTopToolbar ? m_pTopToolbar->GetBtn(IDC_BUTTON_ALARM) : nullptr);
+	for (const auto& alarm : m_unreadAlarms) {
+		m_ackAlarms.insert(alarm.nId);
+	}
+	m_unreadAlarms.clear();
+	if (pBtn != nullptr) {
+		pBtn->SetBadgeNumber(0);
+		pBtn->ShowDotBadge(FALSE, RGB(255, 0, 0));
+		pBtn->StopFlash();
 	}
 }
 
@@ -466,7 +553,7 @@
 		(unsigned long long)(GetTickCount64() - boot_pages_begin),
 		(unsigned long long)(GetTickCount64() - boot_ui_begin));
 
-	CHmTab* m_pTab = CHmTab::Hook(GetDlgItem(IDC_TAB1)->m_hWnd);
+	m_pTab = CHmTab::Hook(GetDlgItem(IDC_TAB1)->m_hWnd);
 	m_pTab->SetPaddingLeft(20);
 	m_pTab->SetItemMarginLeft(18);
 	m_pTab->AddItem("鐘舵�佸浘", FALSE);
@@ -505,6 +592,7 @@
 	m_pMyStatusbar = new CMyStatusbar();
 	m_pMyStatusbar->Create(IDD_STATUSBAR, this);
 	m_pMyStatusbar->ShowWindow(SW_SHOW);
+	m_pMyStatusbar->setJobText("ControlJob: None");
 	LOGI("[BOOT][UI] statusbar created, elapsed=%llu ms", (unsigned long long)(GetTickCount64() - boot_ui_begin));
 
 
@@ -531,13 +619,20 @@
 		}
 		LOGI("[BOOT][UI] before master.init, elapsed=%llu ms", (unsigned long long)(GetTickCount64() - boot_ui_begin));
 
-	// 鐩稿綋浜庡欢鏃惰皟鐢╩aster鐨勫垵濮嬪寲
-	const ULONGLONG boot_master_begin = GetTickCount64();
-	theApp.m_model.m_master.init();
-	LOGI("[BOOT][UI] m_master.init finished, cost=%llu ms (since OnInit start %llu ms)",
-		(unsigned long long)(GetTickCount64() - boot_master_begin),
-		(unsigned long long)(GetTickCount64() - boot_ui_begin));
-	theApp.m_model.loadPortParams();
+		// 鐩稿綋浜庡欢鏃惰皟鐢╩aster鐨勫垵濮嬪寲
+		const ULONGLONG boot_master_begin = GetTickCount64();
+		theApp.m_model.m_master.init();
+		LOGI("[BOOT][UI] m_master.init finished, cost=%llu ms (since OnInit start %llu ms)",
+			(unsigned long long)(GetTickCount64() - boot_master_begin),
+			(unsigned long long)(GetTickCount64() - boot_ui_begin));
+		theApp.m_model.loadPortParams();
+	}
+
+	// 杩樺師鐘舵�佹爮杩愯鏃堕棿鏄剧ず锛堥伩鍏嶄竴鐩村仠鐣欏湪鈥滄鍦ㄥ姞杞藉巻鍙茬紦瀛�...鈥濓級
+	if (m_pMyStatusbar != nullptr) {
+		CString strText;
+		GetRuntimeFormatText(strText, "");
+		m_pMyStatusbar->setRunTimeText((LPTSTR)(LPCTSTR)strText);
 	}
 
 
@@ -547,6 +642,8 @@
 
 	// 鏇存柊鐧诲綍鐘舵��
 	UpdateLoginStatus();
+	// 鍒濆鍖栨姤璀﹁鏍�
+	RefreshAlarmBadge();
 	//SystemLogManager::getInstance.log(SystemLogManager::LogType::Info, _T("BondEq鍚姩..."));
 	//SystemLogManager::getInstance.
 
@@ -736,6 +833,26 @@
 	pCmdUI->Enable(TRUE);
 }
 
+void CServoDlg::OnMenuTestAlarmOn()
+{
+	RaiseTestAlarm();
+}
+
+void CServoDlg::OnUpdateMenuTestAlarmOn(CCmdUI* pCmdUI)
+{
+	pCmdUI->Enable(TRUE);
+}
+
+void CServoDlg::OnMenuTestAlarmOff()
+{
+	ClearTestAlarm();
+}
+
+void CServoDlg::OnUpdateMenuTestAlarmOff(CCmdUI* pCmdUI)
+{
+	pCmdUI->Enable(TRUE);
+}
+
 void CServoDlg::OnMenuTestMessageSet()
 {
 	SERVO::CEquipment* pEquipment = m_pPanelMaster->GetActiveEquipment();
@@ -848,6 +965,12 @@
 		m_pMyStatusbar->DestroyWindow();
 		delete m_pMyStatusbar;
 		m_pMyStatusbar = nullptr;
+	}
+
+	if (m_pAlarmPopupDlg != nullptr) {
+		m_pAlarmPopupDlg->DestroyWindow();
+		delete m_pAlarmPopupDlg;
+		m_pAlarmPopupDlg = nullptr;
 	}
 
 	if (m_pRobotTaskDlg != nullptr) {
@@ -1256,6 +1379,16 @@
 		dlg.SetEFEM(pEFEM);
 		dlg.DoModal();
 	}
+	else if (id == IDC_BUTTON_ALARM) {
+		if (m_pAlarmPopupDlg == nullptr) {
+			m_pAlarmPopupDlg = new CAlarmPopupDlg();
+			m_pAlarmPopupDlg->Create(IDD_DIALOG_POPUP_ALARM, this);
+			m_pAlarmPopupDlg->CenterWindow();
+		}
+		m_pAlarmPopupDlg->RefreshContent();
+		m_pAlarmPopupDlg->ShowWindow(SW_SHOW);
+		MarkAlarmsRead();
+	}
 	else if (id == IDC_BUTTON_SETTINGS) {
 		SERVO::CEquipment* pEq = theApp.m_model.m_master.getEquipment(EQ_ID_EFEM);
 		((SERVO::CEFEM*)pEq)->printDebugRobotState();
@@ -1367,6 +1500,11 @@
 	return 0;
 }
 
+BOOL CServoDlg::PreTranslateMessage(MSG* pMsg)
+{
+	return CDialogEx::PreTranslateMessage(pMsg);
+}
+
 CString& CServoDlg::GetRuntimeFormatText(CString& strText, const char* pszSuffix)
 {
 	ULONGLONG ullRunTime = (ULONGLONG)(theApp.m_model.getMaster().getRunTime() * 0.001);

--
Gitblit v1.9.3