From bb13ecc602edb0247f65a1362135e4ef70a5a79f Mon Sep 17 00:00:00 2001
From: LAPTOP-SNT8I5JK\Boounion <Chenluhua@qq.com>
Date: 星期五, 06 六月 2025 13:50:15 +0800
Subject: [PATCH] Merge branch 'master' into clh

---
 SourceCode/Bond/Servo/CRobotTask.h         |    1 
 SourceCode/Bond/Servo/resource.h           |    0 
 SourceCode/Bond/Servo/CRobotCmdTestDlg.cpp |    2 
 SourceCode/Bond/Servo/CRobotTask.cpp       |   12 ++
 SourceCode/Bond/Servo/CRobotTaskDlg.h      |    4 
 SourceCode/Bond/Servo/BlButton.cpp         |    8 
 SourceCode/Bond/Servo/JobSlotGrid.h        |   14 ++
 SourceCode/Bond/Servo/CRobotTaskDlg.cpp    |   97 +++++++++++++++++--
 SourceCode/Bond/Servo/JobSlotGrid.cpp      |  111 +++++++++++++++++++++-
 9 files changed, 229 insertions(+), 20 deletions(-)

diff --git a/SourceCode/Bond/Servo/BlButton.cpp b/SourceCode/Bond/Servo/BlButton.cpp
index 5eb7694..2bddd2b 100644
--- a/SourceCode/Bond/Servo/BlButton.cpp
+++ b/SourceCode/Bond/Servo/BlButton.cpp
@@ -219,8 +219,7 @@
 		hFont = (HFONT)pFont->GetSafeHandle();
 	}
 
-
-	::SelectObject(hDC, hFont);
+	HFONT hOldFont = (HFONT)::SelectObject(hDC, hFont);
 	::SetBkMode(hDC, TRANSPARENT);
 	::SetTextColor(hDC, m_crText[state]);
 
@@ -237,6 +236,7 @@
 		}
 		DrawTextA(hDC, szText, (int)strlen(szText), &rcText, DT_VCENTER | DT_CENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
 	}
+	::SelectObject(hDC, hOldFont);
 
 
 	// 是否有小圆点
@@ -283,8 +283,8 @@
 		::Polygon(hDC, pt, 3);
 		::SelectObject(hDC, hOldBrush);
 		::SelectObject(hDC, hOldPen);
-		::DeleteObject(hBrush);
-		::DeleteObject(hPen);
+		::DeleteObject(hbrDrop);     // 正确释放小三角使用的画刷
+		::DeleteObject(hPenDrop);    // 正确释放小三角的笔
 	}
 }
 
diff --git a/SourceCode/Bond/Servo/CRobotCmdTestDlg.cpp b/SourceCode/Bond/Servo/CRobotCmdTestDlg.cpp
index ea62463..804335e 100644
--- a/SourceCode/Bond/Servo/CRobotCmdTestDlg.cpp
+++ b/SourceCode/Bond/Servo/CRobotCmdTestDlg.cpp
@@ -342,7 +342,7 @@
 			}
 
 			return 0;
-			});
+		});
 
 		CString log;
 		if (ret == 0) {
diff --git a/SourceCode/Bond/Servo/CRobotTask.cpp b/SourceCode/Bond/Servo/CRobotTask.cpp
index 3112351..a33cc40 100644
--- a/SourceCode/Bond/Servo/CRobotTask.cpp
+++ b/SourceCode/Bond/Servo/CRobotTask.cpp
@@ -174,6 +174,18 @@
 		return m_robotCmdParam.putSlotNo;
 	}
 
+	CString CRobotTask::getStateString()
+	{
+		switch (m_state) {
+		case ROBOT_TASK_STATE::Ready:     return _T("Ready");
+		case ROBOT_TASK_STATE::Running:   return _T("Running");
+		case ROBOT_TASK_STATE::Error:     return _T("Error");
+		case ROBOT_TASK_STATE::Abort:     return _T("Abort");
+		case ROBOT_TASK_STATE::Completed: return _T("Completed");
+		default:                          return _T("Unknown");
+		}
+	}
+
 	void CRobotTask::fetchOut()
 	{
 		m_timeFetchOut = CToolUnits::getUnixTimestamp();;
diff --git a/SourceCode/Bond/Servo/CRobotTask.h b/SourceCode/Bond/Servo/CRobotTask.h
index f02d1ff..2cd8654 100644
--- a/SourceCode/Bond/Servo/CRobotTask.h
+++ b/SourceCode/Bond/Servo/CRobotTask.h
@@ -30,6 +30,7 @@
 		int getSrcSlot();
 		int getTarPosition();
 		int getTarSlot();
+		CString getStateString();
 
 		// 从源地拔片
 		void fetchOut();
diff --git a/SourceCode/Bond/Servo/CRobotTaskDlg.cpp b/SourceCode/Bond/Servo/CRobotTaskDlg.cpp
index 16fd9da..b354216 100644
--- a/SourceCode/Bond/Servo/CRobotTaskDlg.cpp
+++ b/SourceCode/Bond/Servo/CRobotTaskDlg.cpp
@@ -32,6 +32,7 @@
 	ON_WM_DESTROY()
 	ON_WM_SIZE()
 	ON_WM_ACTIVATE()
+	ON_BN_CLICKED(IDC_BUTTON_ABORT_TASK, &CRobotTaskDlg::OnBnClickedAbortTask)
 END_MESSAGE_MAP()
 
 
@@ -47,15 +48,47 @@
 	GetDlgItem(IDC_LABEL_NO_TASK)->ShowWindow(m_pRobotTask == nullptr ? SW_SHOW : SW_HIDE);
 	GetDlgItem(IDC_LABEL_GET_PUT)->ShowWindow(m_pRobotTask != nullptr ? SW_SHOW : SW_HIDE);
 
+	if (m_btnAbortTask.m_hWnd) {
+		m_btnAbortTask.ShowWindow(m_pRobotTask ? SW_SHOW : SW_HIDE);
+	}
 
 	if (m_pRobotTask != nullptr) {
-		SERVO::CEquipment* pEq1, * pEq2;
-		pEq1 = theApp.m_model.getMaster().getEquipment(m_pRobotTask->getSrcPosition());
-		pEq2 = theApp.m_model.getMaster().getEquipment(m_pRobotTask->getTarPosition());
+		using namespace SERVO;
 
-		CString strText;
-		strText.Format(_T("%s --> %s"), pEq1->getName().c_str(), pEq2->getName().c_str());
-		SetDlgItemText(IDC_LABEL_GET_PUT, strText);
+		CEquipment* pSrcEq = theApp.m_model.getMaster().getEquipment(pRobotTask->getSrcPosition());
+		CEquipment* pDstEq = theApp.m_model.getMaster().getEquipment(pRobotTask->getTarPosition());
+
+		ROBOT_CMD_PARAM& param = pRobotTask->getRobotCmdParam();
+
+		auto format_time = [](time_t t) -> CString {
+			if (t == 0) {
+				return _T("-");
+			}
+			CTime time(t);
+			return time.Format(_T("%Y-%m-%d %H:%M:%S"));
+		};
+
+		CString strDetail;
+		strDetail.Format(
+			_T("浠诲姟 ID: %s\r\n婧愪綅缃�: %s (P%d)\r\n鐩爣浣嶇疆: %s (P%d)\r\n")
+			_T("婧愭Ы浣�: Slot %d\r\n鐩爣妲戒綅: Slot %d\r\n鎵嬭噦缂栧彿: Arm %d\r\n浠诲姟鐘舵��: %s\r\n")
+			_T("鍒涘缓鏃堕棿: %s\r\n鍙栫墖鏃堕棿: %s\r\n鏀剧墖鏃堕棿: %s\r\n缁撴潫鏃堕棿: %s"),
+			pRobotTask->getId().c_str(),
+			pSrcEq ? pSrcEq->getName().c_str() : _T("鏈煡"),
+			param.getPosition,
+			pDstEq ? pDstEq->getName().c_str() : _T("鏈煡"),
+			param.putPosition,
+			param.getSlotNo,
+			param.putSlotNo,
+			param.armNo,
+			pRobotTask->getStateString(),
+			format_time(pRobotTask->getCreateTime()),
+			format_time(pRobotTask->getFetchoutTime()),
+			format_time(pRobotTask->getStoredTime()),
+			format_time(pRobotTask->getFinishTime())
+		);
+
+		SetDlgItemText(IDC_LABEL_GET_PUT, strDetail);
 	}
 }
 
@@ -64,6 +97,20 @@
 	CDialogEx::OnInitDialog();
 
 	// TODO:  鍦ㄦ娣诲姞棰濆鐨勫垵濮嬪寲
+	// 鍒涘缓鈥滃仠姝换鍔♀�濇寜閽�
+	m_btnAbortTask.Create(_T("鍋滄浠诲姟"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, CRect(0, 0, 100, 30), this, IDC_BUTTON_ABORT_TASK);
+
+	// 鍔ㄦ�佹寜閽垱寤哄悗璁剧疆瀛椾綋
+	if (m_fontButton.GetSafeHandle() == nullptr) {
+		m_fontButton.CreatePointFont(110, _T("寰蒋闆呴粦")); // 鎴� "Segoe UI"
+	}
+	m_btnAbortTask.SetFont(&m_fontButton);
+
+	// 璁剧疆 LABEL 鎺т欢鐨勫瓧浣�
+	if (m_fontDetail.GetSafeHandle() == nullptr) {
+		m_fontDetail.CreatePointFont(100, _T("寰蒋闆呴粦"));
+	}
+	GetDlgItem(IDC_LABEL_GET_PUT)->SetFont(&m_fontDetail);
 
 	return TRUE;  // return TRUE unless you set the focus to a control
 				  // 寮傚父: OCX 灞炴�ч〉搴旇繑鍥� FALSE
@@ -86,6 +133,13 @@
 	CDialogEx::OnDestroy();
 
 	// TODO: 鍦ㄦ澶勬坊鍔犳秷鎭鐞嗙▼搴忎唬鐮�
+	if (m_fontButton.GetSafeHandle()) {
+		m_fontButton.DeleteObject();
+	}
+
+	if (m_fontDetail.GetSafeHandle()) {
+		m_fontDetail.DeleteObject();
+	}
 }
 
 void CRobotTaskDlg::OnSize(UINT nType, int cx, int cy)
@@ -116,9 +170,32 @@
 		(rcClient.Height() - rcItem.Height()) / 2, rcItem.Width(), rcItem.Height());
 
 	pItem = GetDlgItem(IDC_LABEL_GET_PUT);
-	pItem->GetClientRect(&rcItem);
-	pItem->MoveWindow(12,
-		12, rcItem.Width(), rcItem.Height());
+	if (pItem && pItem->m_hWnd) {
+		const int nLabelX = 12;
+		const int nLabelY = 12;
+		const int nLabelWidth = rcClient.Width() - 24;
+		const int nLabelHeight = rcClient.Height() - 24;
+
+		pItem->MoveWindow(nLabelX, nLabelY, nLabelWidth, nLabelHeight);
+	}
+
+	// 璁剧疆鈥滃仠姝换鍔♀�濇寜閽綅缃紙鍙充笅瑙掞級
+	if (m_btnAbortTask.m_hWnd != nullptr) {
+		const int nBtnWidth = 100;
+		const int nBtnHeight = 28;
+		const int nMargin = 12;
+
+		const int nPosX = rcClient.right - nBtnWidth - nMargin;
+		const int nPosY = rcClient.bottom - nBtnHeight - nMargin;
+
+		m_btnAbortTask.MoveWindow(nPosX, nPosY, nBtnWidth, nBtnHeight);
+	}
 }
 
-
+void CRobotTaskDlg::OnBnClickedAbortTask()
+{
+	if (m_pRobotTask) {
+		m_pRobotTask->abort();
+		AfxMessageBox(_T("浠诲姟宸插仠姝€��"));
+	}
+}
\ No newline at end of file
diff --git a/SourceCode/Bond/Servo/CRobotTaskDlg.h b/SourceCode/Bond/Servo/CRobotTaskDlg.h
index 8754432..9226dcb 100644
--- a/SourceCode/Bond/Servo/CRobotTaskDlg.h
+++ b/SourceCode/Bond/Servo/CRobotTaskDlg.h
@@ -21,6 +21,9 @@
 
 private:
 	SERVO::CRobotTask* m_pRobotTask;
+	CButton m_btnAbortTask;
+	CFont m_fontButton;
+	CFont m_fontDetail;
 
 
 // 瀵硅瘽妗嗘暟鎹�
@@ -38,4 +41,5 @@
 	afx_msg void OnDestroy();
 	afx_msg void OnSize(UINT nType, int cx, int cy);
 	afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);
+	afx_msg void OnBnClickedAbortTask();
 };
diff --git a/SourceCode/Bond/Servo/JobSlotGrid.cpp b/SourceCode/Bond/Servo/JobSlotGrid.cpp
index cf7df72..12a4c2f 100644
--- a/SourceCode/Bond/Servo/JobSlotGrid.cpp
+++ b/SourceCode/Bond/Servo/JobSlotGrid.cpp
@@ -10,6 +10,10 @@
 BEGIN_MESSAGE_MAP(CJobSlotGrid, CWnd)
     ON_WM_PAINT()
     ON_WM_ERASEBKGND()
+    ON_WM_MOUSEMOVE()
+    ON_WM_MOUSELEAVE()
+    ON_WM_LBUTTONDOWN()
+    ON_WM_LBUTTONUP()
 END_MESSAGE_MAP()
 
 CJobSlotGrid::CJobSlotGrid() {
@@ -48,6 +52,7 @@
     m_nRows = nRows;
     m_nCols = nCols;
     m_vSlotStatus.assign(nRows, std::vector<bool>(nCols, false));
+    m_vSlotClickable.assign(nRows, std::vector<bool>(nCols, false));
 
     // 鍒濆鍖栨枃鏈暟缁�
     m_vSlotText.assign(nRows, std::vector<CString>(nCols));
@@ -130,8 +135,88 @@
     Invalidate();
 }
 
+void CJobSlotGrid::SetSlotClickable(int nRow, int nCol, bool bClickable)
+{
+    if (nRow >= 0 && nRow < m_nRows && nCol >= 0 && nCol < m_nCols) {
+        m_vSlotClickable[nRow][nCol] = bClickable;
+    }
+}
+
+bool CJobSlotGrid::IsSlotClickable(int nRow, int nCol) const
+{
+    if (nRow >= 0 && nRow < m_nRows && nCol >= 0 && nCol < m_nCols) {
+        return m_vSlotClickable[nRow][nCol];
+    }
+    return false;
+}
+
+void CJobSlotGrid::SetSlotClickCallback(SlotClickCallback fnCallback)
+{
+    m_fnSlotClickCallback = fnCallback;
+}
+
 BOOL CJobSlotGrid::OnEraseBkgnd(CDC* pDC) {
     return TRUE;
+}
+
+void CJobSlotGrid::OnMouseMove(UINT nFlags, CPoint point)
+{
+    TRACKMOUSEEVENT tme = { sizeof(TRACKMOUSEEVENT), TME_LEAVE, m_hWnd };
+    ::TrackMouseEvent(&tme);
+
+    CRect rect;
+    GetClientRect(&rect);
+    int nCellWidth = rect.Width() / m_nCols;
+    int nCellHeight = rect.Height() / m_nRows;
+
+    int nCol = point.x / nCellWidth;
+    int nRow = point.y / nCellHeight;
+
+    if (nRow != m_ptHover.y || nCol != m_ptHover.x) {
+        m_ptHover = CPoint(nCol, nRow);
+        Invalidate();
+    }
+
+    CWnd::OnMouseMove(nFlags, point);
+}
+
+void CJobSlotGrid::OnMouseLeave()
+{
+    m_ptHover = CPoint(-1, -1);
+    Invalidate();
+
+    CWnd::OnMouseLeave();
+}
+
+void CJobSlotGrid::OnLButtonDown(UINT nFlags, CPoint point)
+{
+    m_bLButtonDown = true;
+    Invalidate();
+
+    CWnd::OnLButtonDown(nFlags, point);
+}
+
+void CJobSlotGrid::OnLButtonUp(UINT nFlags, CPoint point)
+{
+    m_bLButtonDown = false;
+    Invalidate();
+
+    // 淇濇寔鍘熸湁閫昏緫涓嶅彉
+    CRect rect;
+    GetClientRect(&rect);
+    int nCellWidth = rect.Width() / m_nCols;
+    int nCellHeight = rect.Height() / m_nRows;
+
+    int nCol = point.x / nCellWidth;
+    int nRow = point.y / nCellHeight;
+
+    if (IsSlotClickable(nRow, nCol)) {
+        if (m_fnSlotClickCallback) {
+            m_fnSlotClickCallback(nRow, nCol);
+        }
+    }
+
+    CWnd::OnLButtonUp(nFlags, point);
 }
 
 void CJobSlotGrid::OnPaint() {
@@ -152,14 +237,30 @@
         for (int j = 0; j < m_nCols; ++j) {
             CRect cellRect(j * nCellWidth, i * nCellHeight, (j + 1) * nCellWidth, (i + 1) * nCellHeight);
 
-            // 鑳屾櫙
-            CBrush* pBrush = m_vSlotStatus[i][j] ? &m_brushHasJob : &m_brushNoJob;
-            pDC->FillRect(&cellRect, pBrush);
+            // 鍒ゆ柇鐘舵�侊細鎮仠 / 鎸変笅
+            bool bIsHover = (m_ptHover.x == j && m_ptHover.y == i);
+            bool bIsClicking = bIsHover && m_bLButtonDown;
+
+            // 閫夋嫨棰滆壊
+            COLORREF fillColor;
+            if (bIsClicking) {
+                fillColor = RGB(0, 120, 215);   // 榧犳爣鎸変笅鑹�
+            }
+            else if (bIsHover) {
+                fillColor = RGB(200, 230, 255); // 鎮仠楂樹寒
+            }
+            else {
+                fillColor = m_vSlotStatus[i][j] ? m_colorHasJob : m_colorNoJob;
+            }
+
+            // 鐢昏儗鏅�
+            CBrush brush(fillColor);
+            pDC->FillRect(&cellRect, &brush);
 
             // 杈规
             pDC->DrawEdge(&cellRect, EDGE_SUNKEN, BF_RECT);
 
-            // 鏂囧瓧锛堝眳涓級
+            // 鏂囨湰
             pDC->SetBkMode(TRANSPARENT);
             pDC->SetTextColor(RGB(0, 0, 0));
             pDC->DrawText(m_vSlotText[i][j], &cellRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
@@ -167,4 +268,4 @@
     }
 
     pDC->SelectObject(pOldFont);
-}
+}
\ No newline at end of file
diff --git a/SourceCode/Bond/Servo/JobSlotGrid.h b/SourceCode/Bond/Servo/JobSlotGrid.h
index f904aea..c00dea9 100644
--- a/SourceCode/Bond/Servo/JobSlotGrid.h
+++ b/SourceCode/Bond/Servo/JobSlotGrid.h
@@ -1,6 +1,9 @@
 #pragma once
 #include <afxwin.h>
 #include <vector>
+#include <functional>
+
+using SlotClickCallback = std::function<void(int nRow, int nCol)>;
 
 class CJobSlotGrid : public CWnd
 {
@@ -16,15 +19,24 @@
     void SetSlotText(int nRow, int nCol, const CString& strText);
     void SetTextFont(const CString& strFontName, int nPointSize);
     void ClearAll();
+    void SetSlotClickable(int nRow, int nCol, bool bClickable);
+    bool IsSlotClickable(int nRow, int nCol) const;
+    void SetSlotClickCallback(SlotClickCallback fnCallback);
 
 protected:
     afx_msg void OnPaint();
     afx_msg BOOL OnEraseBkgnd(CDC* pDC);
+    afx_msg void OnMouseMove(UINT nFlags, CPoint point);
+    afx_msg void OnMouseLeave();
+    afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
+    afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
     DECLARE_MESSAGE_MAP()
 
 private:
     int m_nRows;
     int m_nCols;
+    bool m_bLButtonDown = false;             // 鼠标是否按下
+    CPoint m_ptHover{ -1, -1 };   // 当前悬停的 cell 索引(row, col)
     CFont m_fontText;
     COLORREF m_colorHasJob;
     COLORREF m_colorNoJob;
@@ -32,6 +44,8 @@
     CBrush m_brushNoJob;
     std::vector<std::vector<bool>> m_vSlotStatus;
     std::vector<std::vector<CString>> m_vSlotText;
+    std::vector<std::vector<bool>> m_vSlotClickable;
+    SlotClickCallback m_fnSlotClickCallback;
 
     void DrawGrid(CDC* pDC);
 };
\ No newline at end of file
diff --git a/SourceCode/Bond/Servo/resource.h b/SourceCode/Bond/Servo/resource.h
index 8baf4d6..1827348 100644
--- a/SourceCode/Bond/Servo/resource.h
+++ b/SourceCode/Bond/Servo/resource.h
Binary files differ

--
Gitblit v1.9.3