From d14420e7f85b5fe7289aafe6bef0c053dede3657 Mon Sep 17 00:00:00 2001
From: chenluhua1980 <Chenluhua@qq.com>
Date: 星期五, 16 一月 2026 11:20:39 +0800
Subject: [PATCH] 1.修复以下问题: CEID 校验恒通过:ceidDefined 返回 true,PauseEvent ID 不做有效性检查。Host 若下发无效 CEID,将被接受但运行时无法触发暂停,风险难以察觉。 建议的上线前防护 / 改进(无需真机也可先改) 2.软件侧警告id上抛;

---
 SourceCode/Bond/Servo/ToolUnits.cpp       |   25 ++++
 SourceCode/Bond/Servo/Model.h             |   10 ++
 SourceCode/Bond/x64/Release/AlarmList.txt |    1 
 SourceCode/Bond/Servo/CMaster.cpp         |  107 +++++++++++++++++++++
 SourceCode/Bond/Servo/CMaster.h           |   12 ++
 SourceCode/Bond/Servo/Model.cpp           |   78 +++++++++++++++
 SourceCode/Bond/Servo/ServoDlg.cpp        |   21 ++++
 SourceCode/Bond/Servo/ToolUnits.h         |    4 
 SourceCode/Bond/Servo/Common.h            |    6 +
 SourceCode/Bond/x64/Debug/AlarmList.txt   |    1 
 10 files changed, 260 insertions(+), 5 deletions(-)

diff --git a/SourceCode/Bond/Servo/CMaster.cpp b/SourceCode/Bond/Servo/CMaster.cpp
index d9a0e67..90b4d23 100644
--- a/SourceCode/Bond/Servo/CMaster.cpp
+++ b/SourceCode/Bond/Servo/CMaster.cpp
@@ -8,6 +8,9 @@
 #include <fstream>
 #include "SerializeUtil.h"
 #include "CServoUtilsTool.h"
+#include "AlarmManager.h"
+#include "ToolUnits.h"
+#include "Model.h"
 
 
 namespace SERVO {
@@ -72,6 +75,8 @@
 		m_nContinuousWorkingPort = 0;
 		m_nContinuousWorkingSlot = 0;
 		m_pControlJob = nullptr;
+		m_bPauseAlarmRaised = false;
+		m_pModelCtx = nullptr;
 		m_nTestFlag = 0;
 		InitializeCriticalSection(&m_criticalSection);
 	}
@@ -119,6 +124,11 @@
 	void CMaster::setListener(MasterListener listener)
 	{
 		m_listener = listener;
+	}
+
+	void CMaster::setModelCtx(CModel* pModel)
+	{
+		m_pModelCtx = pModel;
 	}
 
 	CRobotTask* CMaster::getActiveRobotTask()
@@ -879,6 +889,33 @@
 						m_pActiveRobotTask->place();
 					}
 					unlock(); // 绛夊綋鍓嶄换鍔″畬鎴愭垨涓鍚庣户缁�
+					continue;
+				}
+
+				// 5.5) 鏆傚仠鐘舵�佹鏌ワ細鑻� CJ 鎴栧湪鍒� PJ 澶勪簬 Paused锛屾殏缂撹皟搴︽柊鐨勬惉閫�
+				bool pausedByEvent = false;
+				if (m_pControlJob != nullptr && m_pControlJob->state() == CJState::Paused) {
+					pausedByEvent = true;
+				}
+				for (auto pj : m_inProcesJobs) {
+					if (pj != nullptr && pj->state() == PJState::Paused) {
+						pausedByEvent = true;
+						break;
+					}
+				}
+				if (!pausedByEvent && m_bPauseAlarmRaised) {
+					if (m_pModelCtx != nullptr) {
+						m_pModelCtx->clearSoftAlarm(ALID_SOFTWARE_PAUSE_EVENT, 0, 0);
+					}
+					else {
+						AlarmManager& alarmManager = AlarmManager::getInstance();
+						alarmManager.clearAlarmByAttributes(ALID_SOFTWARE_PAUSE_EVENT, 0, 0, CToolUnits::getCurrentTimeString());
+					}
+					m_bPauseAlarmRaised = false;
+				}
+				if (pausedByEvent) {
+					LOGI("<Master>璋冨害鏆傚仠锛欳ontrolJob/ProcessJob 澶勪簬 Paused 鐘舵�侊紙鍙兘鐢� PauseEvent 瑙﹀彂锛�");
+					unlock();
 					continue;
 				}
 
@@ -2977,15 +3014,83 @@
 		return m_allowedCeids.find(ceid) != m_allowedCeids.end();
 	}
 
+	bool CMaster::raiseSoftAlarm(int alarmId,
+		const std::string& desc,
+		int level/* = -1*/,
+		int deviceId/* = 0*/,
+		int unitId/* = 0*/,
+		const char* deviceName/* = "Software"*/,
+		const char* unitName/* = "App"*/)
+	{
+		AlarmManager& alarmManager = AlarmManager::getInstance();
+		const AlarmInfo* info = alarmManager.getAlarmInfoByID(alarmId);
+
+		int severity = level;
+		if (severity < 0 && info != nullptr) {
+			severity = info->nAlarmLevel;
+		}
+		if (severity < 0) severity = 0; // fallback
+
+		std::string descText = desc;
+		if (descText.empty() && info != nullptr) {
+			descText = !info->strDescription.empty() ? info->strDescription : info->strAlarmText;
+		}
+		if (descText.empty()) {
+			descText = CToolUnits::formatString("Alarm %d", alarmId);
+		}
+
+		AlarmData alarmData;
+		alarmData.nId = alarmId;
+		alarmData.nSeverityLevel = severity;
+		alarmData.nDeviceId = deviceId;
+		alarmData.nUnitId = unitId;
+		alarmData.strDeviceName = deviceName;
+		alarmData.strUnitName = unitName;
+		alarmData.strStartTime = CToolUnits::timeToString2(CToolUnits::getTimestamp());
+		alarmData.strEndTime = "";
+		alarmData.strDescription = descText;
+
+		int nAlarmEventId = 0;
+		return alarmManager.addAlarm(alarmData, nAlarmEventId);
+	}
+
 	void CMaster::handleCollectionEvent(uint32_t ceid)
 	{
 		// 閬嶅巻褰撳墠 PJ锛屽懡涓� pauseEvents 鏃跺彲鍦ㄦ鎵╁睍鏆傚仠鍔ㄤ綔
+		bool pausedAny = false;
 		for (auto pj : m_processJobs) {
 			if (pj == nullptr) continue;
 			const auto& pauseList = pj->pauseEvents();
 			if (std::find(pauseList.begin(), pauseList.end(), ceid) != pauseList.end()) {
 				LOGW("<Master>PauseEvent hit: CEID=%u, PJ=%s, state=%d", ceid, pj->id().c_str(), (int)pj->state());
-				// TODO: 琛旀帴鍏蜂綋鏆傚仠绛栫暐锛堝鏆傚仠 PJ/CJ銆佸仠姝㈣皟搴�/鎼�侊級锛屾澶勪粎鐣欐々浣�
+				if (pj->pause()) {
+					LOGI("<Master>PJ paused by CEID=%u", ceid);
+					pausedAny = true;
+				}
+				if (m_pControlJob != nullptr && m_pControlJob->state() == CJState::Executing) {
+					if (m_pControlJob->pause()) {
+						LOGI("<Master>ControlJob paused by CEID=%u", ceid);
+						pausedAny = true;
+					}
+				}
+			}
+		}
+		if (pausedAny && m_listener.onControlJobChanged) {
+			// 閫氱煡搴旂敤灞傚埛鏂� UI/鎸夐挳鐘舵��
+			m_listener.onControlJobChanged(this);
+		}
+		if (pausedAny && !m_bPauseAlarmRaised) {
+			std::string desc = CToolUnits::formatString("<PauseEvent CEID=%u>", ceid);
+			bool raised = false;
+			if (m_pModelCtx != nullptr) {
+				raised = m_pModelCtx->raiseSoftAlarm(ALID_SOFTWARE_PAUSE_EVENT, desc);
+			}
+			else {
+				raised = raiseSoftAlarm(ALID_SOFTWARE_PAUSE_EVENT, desc);
+			}
+			if (raised) {
+				LOGI("<Master>PauseEvent soft alarm raised, CEID=%u", ceid);
+				m_bPauseAlarmRaised = true;
 			}
 		}
 	}
diff --git a/SourceCode/Bond/Servo/CMaster.h b/SourceCode/Bond/Servo/CMaster.h
index acd3065..c1f1755 100644
--- a/SourceCode/Bond/Servo/CMaster.h
+++ b/SourceCode/Bond/Servo/CMaster.h
@@ -19,6 +19,8 @@
 #include "../DAQBridge/core/Collector.h"
 #include "CJobDataS.h"
 
+class CModel;
+
 
 #define CTStep_Unknow                   0
 #define CTStep_LoadPort_Aligner         1
@@ -99,6 +101,7 @@
 
 
     public:
+        void setModelCtx(CModel* pModel);
         void setListener(MasterListener listener);
         CRobotTask* getActiveRobotTask();
         int init();
@@ -192,6 +195,13 @@
         bool ceidDefined(uint32_t ceid) const override;
         void setAllowedCeids(const std::vector<unsigned int>& ceids);
         void handleCollectionEvent(uint32_t ceid);
+        bool raiseSoftAlarm(int alarmId,
+            const std::string& desc,
+            int level = -1,
+            int deviceId = 0,
+            int unitId = 0,
+            const char* deviceName = "Software",
+            const char* unitName = "App");
 
     public:
         int getLastError();
@@ -273,9 +283,11 @@
     private:
         bool m_bEnableEventReport;
         bool m_bEnableAlarmReport;
+        bool m_bPauseAlarmRaised;
         SERVO::CControlJob* m_pControlJob;
         std::vector<SERVO::CProcessJob*> m_processJobs;
         std::string m_strStatePath;
+        CModel* m_pModelCtx;
 
         int m_nTestFlag;
         std::list<CGlass*> m_bufGlass;
diff --git a/SourceCode/Bond/Servo/Common.h b/SourceCode/Bond/Servo/Common.h
index 4969d57..b207bfd 100644
--- a/SourceCode/Bond/Servo/Common.h
+++ b/SourceCode/Bond/Servo/Common.h
@@ -20,7 +20,10 @@
 #define RX_CODE_EQ_ROBOT_TASK			1012
 #define RX_CODE_LOADPORT_STATUS_CHANGED	1014
 #define RX_CODE_CONTROL_STATE_CHANGED	1015
+#define RX_CODE_CONTROLJOB_CHANGED		1016
 
+/* 软件侧 ALID */
+#define ALID_SOFTWARE_PAUSE_EVENT		9000
 
 /* Channel Name */
 #define MC_CHANNEL1_NAME		"McChannel1"
@@ -556,4 +559,5 @@
 
 
 /* PPID名字最大长度 */
-#define PPID_NAME_MAX			80
\ No newline at end of file
+#define PPID_NAME_MAX			80
+
diff --git a/SourceCode/Bond/Servo/Model.cpp b/SourceCode/Bond/Servo/Model.cpp
index 3bc322e..61f3a80 100644
--- a/SourceCode/Bond/Servo/Model.cpp
+++ b/SourceCode/Bond/Servo/Model.cpp
@@ -37,6 +37,79 @@
 	m_hsmsPassive.setVariableValue("PJobSpace", (__int64)(m_master.isProcessJobsEmpty() ? 1 : 0));
 }
 
+void CModel::notifyControlJobChanged()
+{
+	// 1) 鍒锋柊娲剧敓 SV
+	refreshDerivedSVs();
+	// 2) 閫氱煡涓婂眰 UI锛圧X_CODE_CONTROLJOB_CHANGED锛�
+	notify(RX_CODE_CONTROLJOB_CHANGED);
+}
+
+bool CModel::raiseSoftAlarm(int alarmId,
+	const std::string& desc,
+	int level /*= -1*/,
+	int deviceId /*= 0*/,
+	int unitId /*= 0*/,
+	const char* deviceName /*= "Software"*/,
+	const char* unitName /*= "App"*/)
+{
+	AlarmManager& alarmManager = AlarmManager::getInstance();
+	const AlarmInfo* info = alarmManager.getAlarmInfoByID(alarmId);
+
+	int severity = level;
+	if (severity < 0 && info != nullptr) severity = info->nAlarmLevel;
+	if (severity < 0) severity = 0;
+
+	std::string descText = desc;
+	if (descText.empty() && info != nullptr) {
+		descText = !info->strDescription.empty() ? info->strDescription : info->strAlarmText;
+	}
+	if (descText.empty()) {
+		descText = CToolUnits::formatString("Alarm %d", alarmId);
+	}
+
+	AlarmData alarmData;
+	alarmData.nId = alarmId;
+	alarmData.nSeverityLevel = severity;
+	alarmData.nDeviceId = deviceId;
+	alarmData.nUnitId = unitId;
+	alarmData.strDeviceName = deviceName;
+	alarmData.strUnitName = unitName;
+	// 鑻ユ湭鏄惧紡鎻愪緵璁惧/鍗曞厓鍚嶇О锛屽皾璇曢�氳繃 deviceId/unitId 瑙f瀽锛坰oft alarm 榛樿鍧囦负 0锛�
+	if (alarmData.strDeviceName.empty()) {
+		alarmData.strDeviceName = alarmManager.getDeviceNameById(deviceId);
+	}
+	if (alarmData.strUnitName.empty()) {
+		alarmData.strUnitName = alarmManager.getUnitNameById(deviceId, unitId);
+	}
+	alarmData.strStartTime = CToolUnits::timeToString2(CToolUnits::getTimestamp());
+	alarmData.strEndTime = "";
+	alarmData.strDescription = descText;
+
+	int nAlarmEventId = 0;
+	bool result = alarmManager.addAlarm(alarmData, nAlarmEventId);
+	if (result) {
+		notify(RX_CODE_ALARM_SET);
+		if (m_master.isAlarmReportEnable()) {
+			m_hsmsPassive.requestAlarmReport(1, alarmId, descText.c_str());
+		}
+	}
+	return result;
+}
+
+void CModel::clearSoftAlarm(int alarmId, int deviceId, int unitId)
+{
+	AlarmManager& alarmManager = AlarmManager::getInstance();
+	alarmManager.clearAlarmByAttributes(alarmId, deviceId, unitId, CToolUnits::getCurrentTimeString());
+	notify(RX_CODE_ALARM_CLEAR);
+	if (m_master.isAlarmReportEnable()) {
+		const AlarmInfo* info = alarmManager.getAlarmInfoByID(alarmId);
+		std::string descText;
+		if (info != nullptr) descText = info->strAlarmText;
+		m_hsmsPassive.requestAlarmReport(0, alarmId, descText.c_str());
+	}
+}
+
 void CModel::setControlState(ControlState newState)
 {
 	const auto prev = m_currentControlState;
@@ -124,6 +197,9 @@
 
 	// CGlassPool
 	m_glassPool.initPool();
+
+	// 灏� Model 涓婁笅鏂囦紶閫掔粰 Master锛屼究浜� Master 瑙﹀彂杞欢绾ф姤璀︾瓑璺ㄥ眰鎿嶄綔
+	m_master.setModelCtx(this);
 
 
 	// Log
@@ -341,7 +417,7 @@
 	};
 	masterListener.onControlJobChanged = [this](void* pMaster) {
 		(void)pMaster;
-		this->refreshDerivedSVs();
+		this->notifyControlJobChanged();
 		};
 	masterListener.onEqAlive = [&](void* pMaster, SERVO::CEquipment* pEquipment, BOOL bAlive) -> void {
 		LOGI("<CModel>Equipment onAlive:%s(%s).", pEquipment->getName().c_str(),
diff --git a/SourceCode/Bond/Servo/Model.h b/SourceCode/Bond/Servo/Model.h
index 839e949..51a5427 100644
--- a/SourceCode/Bond/Servo/Model.h
+++ b/SourceCode/Bond/Servo/Model.h
@@ -4,6 +4,7 @@
 #include "CMaster.h"
 #include "CGlassPool.h"
 #include <cstdint>
+#include <string>
 
 enum class ControlState : uint8_t {
 	OfflineEquipment = 0,
@@ -33,9 +34,18 @@
 
 	ControlState getControlState() const noexcept { return m_currentControlState; }
 	void setControlState(ControlState newState);
+	bool raiseSoftAlarm(int alarmId,
+		const std::string& desc = "",
+		int level = -1,
+		int deviceId = 0,
+		int unitId = 0,
+		const char* deviceName = "Software",
+		const char* unitName = "App");
+	void clearSoftAlarm(int alarmId, int deviceId = 0, int unitId = 0);
 
 private:
 	void refreshDerivedSVs();
+	void notifyControlJobChanged();
 
 public:
 	int notify(int code);
diff --git a/SourceCode/Bond/Servo/ServoDlg.cpp b/SourceCode/Bond/Servo/ServoDlg.cpp
index cdc423f..77e626f 100644
--- a/SourceCode/Bond/Servo/ServoDlg.cpp
+++ b/SourceCode/Bond/Servo/ServoDlg.cpp
@@ -259,6 +259,27 @@
 					SetTimer(TIMER_ID_UPDATE_RUMTIME, 500, nullptr);
 				}
 			}
+			else if (RX_CODE_CONTROLJOB_CHANGED == code) {
+				auto* cj = theApp.m_model.getMaster().getControlJob();
+				CString text;
+				if (cj != nullptr) {
+					std::string st = cj->getStateText();
+					text.Format(_T("ControlJob: %S (%S)"), cj->id().c_str(), st.c_str());
+					if (cj->state() == SERVO::CJState::Paused) {
+						text += _T(" [Paused]");
+					}
+				}
+				else {
+					text = _T("ControlJob: None");
+				}
+				if (m_pMyStatusbar != nullptr) {
+					m_pMyStatusbar->setRunTimeText((LPTSTR)(LPCTSTR)text);
+					if (cj != nullptr && cj->state() == SERVO::CJState::Paused) {
+						m_pMyStatusbar->setBackgroundColor(STATUSBAR_BK_ALARM);
+						m_pMyStatusbar->setForegroundColor(RGB(0, 0, 0));
+					}
+				}
+			}
 			else if (RX_CODE_EQ_ROBOT_TASK == code) {
 				int exCode;
 				if (pAny->getIntValue("exCode", exCode)) {
diff --git a/SourceCode/Bond/Servo/ToolUnits.cpp b/SourceCode/Bond/Servo/ToolUnits.cpp
index 974121c..e3e1dca 100644
--- a/SourceCode/Bond/Servo/ToolUnits.cpp
+++ b/SourceCode/Bond/Servo/ToolUnits.cpp
@@ -7,6 +7,8 @@
 #include <ctime>
 #include <iomanip>
 #include <sstream>
+#include <vector>
+#include <cstdarg>
 
 CToolUnits::CToolUnits()
 {
@@ -614,4 +616,25 @@
 		nullptr, nullptr);
 
 	return str;
-}
\ No newline at end of file
+}
+std::string CToolUnits::formatString(const char* fmt, ...)
+{
+    if (fmt == nullptr) return std::string();
+
+    char buf[512] = {0};
+    va_list args;
+    va_start(args, fmt);
+    int n = _vsnprintf_s(buf, sizeof(buf), _TRUNCATE, fmt, args);
+    va_end(args);
+
+    if (n >= 0 && n < (int)sizeof(buf)) {
+        return std::string(buf);
+    }
+
+    // ?????????????????
+    std::vector<char> tmp((n > 0 ? n + 2 : 1024), 0);
+    va_start(args, fmt);
+    _vsnprintf_s(tmp.data(), tmp.size(), _TRUNCATE, fmt, args);
+    va_end(args);
+    return std::string(tmp.data());
+}
diff --git a/SourceCode/Bond/Servo/ToolUnits.h b/SourceCode/Bond/Servo/ToolUnits.h
index 7314ea8..0d171c7 100644
--- a/SourceCode/Bond/Servo/ToolUnits.h
+++ b/SourceCode/Bond/Servo/ToolUnits.h
@@ -3,6 +3,8 @@
 #include <chrono>
 #include <optional>
 #include <utility>
+#include <vector>
+#include <cstdarg>
 
 enum class QuickRange { Today, Last7Days, ThisMonth, ThisYear };
 using TP = std::chrono::system_clock::time_point;
@@ -62,5 +64,5 @@
 	static std::string NowStrSec();
 	static std::wstring AnsiToWString(const std::string& str);
 	static std::string WStringToAnsi(const std::wstring& wstr);
+	static std::string formatString(const char* fmt, ...);
 };
-
diff --git a/SourceCode/Bond/x64/Debug/AlarmList.txt b/SourceCode/Bond/x64/Debug/AlarmList.txt
index 69473c8..59dab8f 100644
--- a/SourceCode/Bond/x64/Debug/AlarmList.txt
+++ b/SourceCode/Bond/x64/Debug/AlarmList.txt
@@ -225,3 +225,4 @@
 
 
 
+9000,0,PauseEvent触发
diff --git a/SourceCode/Bond/x64/Release/AlarmList.txt b/SourceCode/Bond/x64/Release/AlarmList.txt
index 34ad3a9..40bca15 100644
--- a/SourceCode/Bond/x64/Release/AlarmList.txt
+++ b/SourceCode/Bond/x64/Release/AlarmList.txt
@@ -225,3 +225,4 @@
 
 
 
+9000,0,PauseEvent触发

--
Gitblit v1.9.3