From 2a21061d88d5533065dc57cfae0b1f2c1952e06f Mon Sep 17 00:00:00 2001
From: LAPTOP-SNT8I5JK\Boounion <Chenluhua@qq.com>
Date: 星期五, 22 八月 2025 16:01:32 +0800
Subject: [PATCH] 1.PorcessJob和Glass关系绑定; 2.对话框显示ProcessJob、Glass等数据;

---
 SourceCode/Bond/Servo/CControlJobDlg.cpp |   30 +++++--
 SourceCode/Bond/Servo/ProcessJob.h       |    6 +
 SourceCode/Bond/Servo/ProcessJob.cpp     |   11 ++
 SourceCode/Bond/Servo/CGlass.cpp         |  123 ++++++++++++++++++++++++++++++
 SourceCode/Bond/Servo/CGlass.h           |   46 +++++++++++
 SourceCode/Bond/Servo/Servo.rc           |    0 
 SourceCode/Bond/Servo/CMaster.cpp        |   21 +++++
 SourceCode/Bond/Servo/CLoadPort.cpp      |    2 
 8 files changed, 227 insertions(+), 12 deletions(-)

diff --git a/SourceCode/Bond/Servo/CControlJobDlg.cpp b/SourceCode/Bond/Servo/CControlJobDlg.cpp
index a372529..7365c3d 100644
--- a/SourceCode/Bond/Servo/CControlJobDlg.cpp
+++ b/SourceCode/Bond/Servo/CControlJobDlg.cpp
@@ -58,10 +58,12 @@
     HIMAGELIST imageList = ImageList_Create(24, 24, ILC_COLOR24, 1, 1);
     ListView_SetImageList(m_listCtrl.GetSafeHwnd(), imageList, LVSIL_SMALL);
     m_listCtrl.ModifyStyle(0, LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS);
-    m_listCtrl.InsertColumn(0, _T("鍚嶇О"), LVCFMT_LEFT, 180);
-    m_listCtrl.InsertColumn(1, _T("鐘舵��"), LVCFMT_LEFT, 120);
-    m_listCtrl.InsertColumn(2, _T("鎻忚堪"), LVCFMT_LEFT, 260);
-    m_listCtrl.InsertColumn(3, _T("閰嶆柟"), LVCFMT_LEFT, 180);
+    m_listCtrl.InsertColumn(0, _T("ID"), LVCFMT_LEFT, 180);
+    m_listCtrl.InsertColumn(1, _T("绫诲瀷"), LVCFMT_LEFT, 120);
+    m_listCtrl.InsertColumn(2, _T("鐘舵��"), LVCFMT_LEFT, 120);
+    m_listCtrl.InsertColumn(3, _T("閰嶆柟"), LVCFMT_LEFT, 120);
+    m_listCtrl.InsertColumn(4, _T("Port / Carrier / Slot"), LVCFMT_LEFT, 180);
+    m_listCtrl.InsertColumn(5, _T("鎻忚堪"), LVCFMT_LEFT, 220);
 
 
     // 鎺т欢鐘舵��
@@ -135,15 +137,27 @@
     m_listCtrl.DeleteAllItems();
 
     if (m_pControlJob != nullptr) {
-        auto* root1 = m_listCtrl.InsertRoot({ m_pControlJob->id().c_str(),
+        auto* root1 = m_listCtrl.InsertRoot({ m_pControlJob->id().c_str(), _T("ControlJob"), 
             m_pControlJob->getStateText().c_str(), _T("") });
         auto pjs = m_pControlJob->getPjs();
         for (auto pj : pjs) {
-            auto* root2 = m_listCtrl.InsertChild(root1, {pj->id().c_str(),
-                pj->getStateText().c_str(), _T(""), pj->recipeSpec().c_str()});
+            auto* root2 = m_listCtrl.InsertChild(root1, {pj->id().c_str(),  _T("ProcessJob"),
+                pj->getStateText().c_str(), pj->recipeSpec().c_str(), _T(""), _T(""), _T("") });
             auto cs = pj->carriers();
             for (auto c : cs) {
-                m_listCtrl.InsertChild(root2, {c.carrierId.c_str(), _T(""), _T("") });
+                for (auto g : c.contexts) {
+                    SERVO::CGlass* pGlass = (SERVO::CGlass*)g;
+                    if (pGlass != nullptr) {
+                        int port, slot;
+                        pGlass->getOrginPort(port, slot);
+                        std::string carrier = c.carrierId + " / Port" + std::to_string(port + 1) + " / Slot" + std::to_string(slot + 1);
+                        m_listCtrl.InsertChild(root2, { pGlass->getID().c_str(), _T("Glass"),
+                            pGlass->getStateText().c_str(), _T(""), carrier.c_str(), _T("") });
+                    }
+                    else {
+                        m_listCtrl.InsertChild(root2, { "Null", _T("Glass"), _T(""), _T(""), c.carrierId.c_str(), _T("") });
+                    }
+                }
             }
             root2->expanded = true;
         }
diff --git a/SourceCode/Bond/Servo/CGlass.cpp b/SourceCode/Bond/Servo/CGlass.cpp
index 6c1b30f..974859c 100644
--- a/SourceCode/Bond/Servo/CGlass.cpp
+++ b/SourceCode/Bond/Servo/CGlass.cpp
@@ -11,6 +11,7 @@
 		m_nOriginPort = 0;
 		m_nOriginSlot = 0;
 		m_bScheduledForProcessing = FALSE;
+		m_pProcessJob = nullptr;
 	}
 
 	CGlass::~CGlass()
@@ -89,6 +90,16 @@
 	void CGlass::setScheduledForProcessing(BOOL bProcessing)
 	{
 		m_bScheduledForProcessing = bProcessing;
+	}
+
+	CProcessJob* CGlass::getProcessJob()
+	{
+		return m_pProcessJob;
+	}
+
+	void CGlass::setProcessJob(CProcessJob* pProcessJob)
+	{
+		m_pProcessJob = pProcessJob;
 	}
 
 	CPath* CGlass::getPath()
@@ -246,4 +257,116 @@
 
 		return pPath->getInspResult();
 	}
+
+	std::string CGlass::getStateText()
+	{
+		switch (m_state)
+		{
+		case SERVO::GlsState::NoState:
+			return "NoState";
+			break;
+		case SERVO::GlsState::Queued:
+			return "Queued";
+			break;
+		case SERVO::GlsState::InProcess:
+			return "InProcess";
+			break;
+		case SERVO::GlsState::Paused:
+			return "Queued";
+			break;
+		case SERVO::GlsState::Completed:
+			return "Queued";
+			break;
+		case SERVO::GlsState::Aborted:
+			return "Aborted";
+			break;
+		case SERVO::GlsState::Failed:
+			return "Failed";
+			break;
+		default:
+			break;
+		}
+
+		return "";
+	}
+
+	bool CGlass::queue() {
+		if (m_state != GlsState::NoState) return false;
+		markQueued();
+		return true;
+	}
+
+	bool CGlass::start() {
+		if (m_state != GlsState::Queued && m_state != GlsState::Paused)
+			return false;
+		if (!m_tStart.has_value()) markStart();
+		m_state = GlsState::InProcess;
+		return true;
+	}
+
+	bool CGlass::pause() {
+		if (m_state != GlsState::InProcess) return false;
+		m_state = GlsState::Paused;
+		return true;
+	}
+
+	bool CGlass::resume() {
+		if (m_state != GlsState::Paused) return false;
+		m_state = GlsState::InProcess;
+		return true;
+	}
+
+	bool CGlass::complete() {
+		if (m_state != GlsState::InProcess && m_state != GlsState::Paused) return false;
+		m_state = GlsState::Completed;
+		markEnd();
+		return true;
+	}
+
+	bool CGlass::abort() {
+		if (m_state == GlsState::Completed || m_state == GlsState::Aborted || m_state == GlsState::Failed)
+			return false;
+		m_state = GlsState::Aborted;
+		markEnd();
+		return true;
+	}
+
+	bool CGlass::fail(std::string reason)
+	{
+		m_failReason = trimCopy(reason);
+		clampString(m_failReason, 128);
+		m_state = GlsState::Failed;
+		markEnd();
+		return true;
+	}
+
+	std::string CGlass::trimCopy(std::string s)
+	{
+		auto notspace = [](int ch) { return !std::isspace(ch); };
+		s.erase(s.begin(), std::find_if(s.begin(), s.end(), notspace));
+		s.erase(std::find_if(s.rbegin(), s.rend(), notspace).base(), s.end());
+		return s;
+	}
+
+	void CGlass::clampString(std::string& s, size_t maxLen)
+	{
+		if (s.size() > maxLen) s.resize(maxLen);
+	}
+
+	// —— 时间戳 & 工具 —— 
+	void CGlass::markQueued() 
+	{
+		m_state = GlsState::Queued;
+		m_tQueued = std::chrono::system_clock::now();
+	}
+
+	void CGlass::markStart()
+	{
+		m_tStart = std::chrono::system_clock::now();
+	}
+
+	void CGlass::markEnd()
+	{
+		m_tEnd = std::chrono::system_clock::now();
+	}
 }
diff --git a/SourceCode/Bond/Servo/CGlass.h b/SourceCode/Bond/Servo/CGlass.h
index 844822b..21aaf34 100644
--- a/SourceCode/Bond/Servo/CGlass.h
+++ b/SourceCode/Bond/Servo/CGlass.h
@@ -7,9 +7,21 @@
 #include "CJobDataC.h"
 #include "CJobDataS.h"
 #include "ServoCommo.h"
+#include "ProcessJob.h"
 
 
 namespace SERVO {
+	/// PJ 生命周期(贴近 E40 常见状态)
+	enum class GlsState : uint8_t {
+		NoState = 0,
+		Queued,
+		InProcess,
+		Paused,
+		Completed,
+		Aborted,
+		Failed
+	};
+
 	class CGlass : public CContext
 	{
 	public:
@@ -28,6 +40,8 @@
 		void getOrginPort(int& port, int& slot);
 		BOOL isScheduledForProcessing();
 		void setScheduledForProcessing(BOOL bProcessing);
+		CProcessJob* getProcessJob();
+		void setProcessJob(CProcessJob* pProcessJob);
 		CPath* getPathWithEq(unsigned int nEqId, unsigned int nUnit);
 		CPath* getPath();
 		void addPath(unsigned int nEqId, unsigned int nUnit);
@@ -44,6 +58,37 @@
 		int setInspResult(unsigned int nEqId, unsigned int nUnit, InspResult result);
 		InspResult getInspResult(unsigned int nEqId, unsigned int nUnit);
 
+	public:
+		// 新增状态
+		GlsState state() const noexcept { return m_state; }
+		std::string getStateText();
+		GlsState m_state{ GlsState::NoState };
+		static void clampString(std::string& s, size_t maxLen);
+		static std::string trimCopy(std::string s);
+		std::string m_failReason;
+
+		// —— 状态机(带守卫)——
+		bool queue();           // NoState -> Queued
+		bool start();           // Queued -> InProcess
+		bool pause();           // InProcess -> Paused
+		bool resume();          // Paused -> InProcess
+		bool complete();        // InProcess -> Completed
+		bool abort();           // Any (未终态) -> Aborted
+		bool fail(std::string reason); // 任意态 -> Failed(记录失败原因)
+
+		// 时间戳
+		std::optional<std::chrono::system_clock::time_point> m_tQueued;
+		std::optional<std::chrono::system_clock::time_point> m_tStart;
+		std::optional<std::chrono::system_clock::time_point> m_tEnd;
+
+		// 时间戳(可用于报表/追溯)
+		std::optional<std::chrono::system_clock::time_point> tQueued() const { return m_tQueued; }
+		std::optional<std::chrono::system_clock::time_point> tStart()  const { return m_tStart; }
+		std::optional<std::chrono::system_clock::time_point> tEnd()    const { return m_tEnd; }
+		void markQueued();
+		void markStart();
+		void markEnd();
+
 	private:
 		MaterialsType m_type;
 		std::string m_strID;
@@ -54,6 +99,7 @@
 		int m_nOriginPort;
 		int m_nOriginSlot;
 		BOOL m_bScheduledForProcessing;			/* 是否将加工处理 */
+		CProcessJob* m_pProcessJob;
 	};
 }
 
diff --git a/SourceCode/Bond/Servo/CLoadPort.cpp b/SourceCode/Bond/Servo/CLoadPort.cpp
index 28ce403..7cc6006 100644
--- a/SourceCode/Bond/Servo/CLoadPort.cpp
+++ b/SourceCode/Bond/Servo/CLoadPort.cpp
@@ -385,7 +385,7 @@
 				CStep* pStep = getStepWithName(STEP_EQ_PORT2_INUSE);
 				CPortStatusReport portStatusReport;
 				portStatusReport.setPortStatus(PORT_INUSE);
-				portStatusReport.setJobExistenceSlot(0xf);
+				portStatusReport.setJobExistenceSlot(0xff );
 				portStatusReport.setCassetteId("CID1004");
 				int nRet = portStatusReport.serialize(szBuffer, 64);
 				decodePortStatusReport(pStep, szBuffer, 64);
diff --git a/SourceCode/Bond/Servo/CMaster.cpp b/SourceCode/Bond/Servo/CMaster.cpp
index 1f38e9f..e01b180 100644
--- a/SourceCode/Bond/Servo/CMaster.cpp
+++ b/SourceCode/Bond/Servo/CMaster.cpp
@@ -1143,6 +1143,25 @@
 		};
 		listener.onPortStatusChanged = [&](void* pEquipment, short status, __int64 data) {
 			LOGE("<Master-%s>onPortStatusChanged。status=%d, data=%lld", ((CEquipment*)pEquipment)->getName().c_str(), status);
+			if (status == PORT_INUSE && m_pControlJob != nullptr) {
+				CLoadPort* pPort = (CLoadPort*)pEquipment;
+				auto pjs = m_pControlJob->getPjs();
+				for (auto pj : pjs) {
+					auto carrier = pj->getCarrier(pPort->getCassetteId());
+					if (carrier != nullptr) {
+						for (auto slot : carrier->slots) {
+							CGlass* pGlass = pPort->getGlassFromSlot(slot);
+							carrier->contexts.push_back((void*)pGlass);
+							if (pGlass != nullptr) {
+								pGlass->setProcessJob(pj);
+							}
+						}
+					}
+				}
+
+
+			}
+			
 			if (m_listener.onLoadPortStatusChanged != nullptr) {
 				m_listener.onLoadPortStatusChanged(this, (CEquipment*)pEquipment, status, data);
 			}
@@ -1868,7 +1887,7 @@
 		m_processJobs = temp;
 		this->saveState();
 
-		return m_processJobs.size();
+		return (int)m_processJobs.size();
 	}
 
 	std::vector<CProcessJob*>& CMaster::getProcessJobs()
diff --git a/SourceCode/Bond/Servo/ProcessJob.cpp b/SourceCode/Bond/Servo/ProcessJob.cpp
index 801d9d2..b672e27 100644
--- a/SourceCode/Bond/Servo/ProcessJob.cpp
+++ b/SourceCode/Bond/Servo/ProcessJob.cpp
@@ -419,4 +419,15 @@
 
         return "";
     }
+
+    CarrierSlotInfo* CProcessJob::getCarrier(std::string& strId)
+    {
+        for (auto& item : m_carriers) {
+            if (item.carrierId.compare(strId) == 0) {
+                return &item;
+            }
+        }
+
+        return nullptr;
+    }
 }
diff --git a/SourceCode/Bond/Servo/ProcessJob.h b/SourceCode/Bond/Servo/ProcessJob.h
index 7c78b26..1300b46 100644
--- a/SourceCode/Bond/Servo/ProcessJob.h
+++ b/SourceCode/Bond/Servo/ProcessJob.h
@@ -49,8 +49,9 @@
     }
      */
     struct CarrierSlotInfo {
-        std::string carrierId;              // CARRIERID
-        std::vector<uint8_t> slots;        // SLOTID[]
+        std::string carrierId;          // CARRIERID
+        std::vector<uint8_t> slots;     // SLOTID[]
+        std::vector<void*> contexts;    // Glass
     };
 
     /// 简单资源视图接口:供 Validate() 查询(由设备端实现者在外部提供)
@@ -154,6 +155,7 @@
 
         // 访问器
         const std::vector<CarrierSlotInfo>& carriers() const noexcept { return m_carriers; }
+        CarrierSlotInfo* getCarrier(std::string& strId);
 
         // 判定是否“按载具/卡位”方式
         bool usesCarrierSlots() const noexcept { return !m_carriers.empty(); }
diff --git a/SourceCode/Bond/Servo/Servo.rc b/SourceCode/Bond/Servo/Servo.rc
index 228d83e..ac1bebb 100644
--- a/SourceCode/Bond/Servo/Servo.rc
+++ b/SourceCode/Bond/Servo/Servo.rc
Binary files differ

--
Gitblit v1.9.3