From bc7f1c4e028e69be51079b59dae4ae5c4d43f5bb Mon Sep 17 00:00:00 2001
From: chenluhua1980 <Chenluhua@qq.com>
Date: 星期六, 31 一月 2026 21:54:56 +0800
Subject: [PATCH] 1.状态指示图,目前灰色表示掉线,绿色表示在线。增加Slot的小点表示有没有料,及加工状态 。 2.增加图示

---
 SourceCode/Bond/Servo/PortConfigurationDlg.cpp |  361 +++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 288 insertions(+), 73 deletions(-)

diff --git a/SourceCode/Bond/Servo/PortConfigurationDlg.cpp b/SourceCode/Bond/Servo/PortConfigurationDlg.cpp
index b941707..5b21fda 100644
--- a/SourceCode/Bond/Servo/PortConfigurationDlg.cpp
+++ b/SourceCode/Bond/Servo/PortConfigurationDlg.cpp
@@ -11,6 +11,7 @@
 #include "RecipeManager.h"
 #include "ServoCommo.h"
 
+#define CHECKBOX_ALL_ID        0x1234
 
 // CPortConfigurationDlg 瀵硅瘽妗�
 
@@ -24,10 +25,18 @@
     m_pPort[1] = dynamic_cast<SERVO::CLoadPort*>(theApp.m_model.m_master.getEquipment(EQ_ID_LOADPORT2));
     m_pPort[2] = dynamic_cast<SERVO::CLoadPort*>(theApp.m_model.m_master.getEquipment(EQ_ID_LOADPORT3));
     m_pPort[3] = dynamic_cast<SERVO::CLoadPort*>(theApp.m_model.m_master.getEquipment(EQ_ID_LOADPORT4));
+    m_nCurSelPort = -1;
+    m_pCheckBox = nullptr;
+    m_bCheckedAll = FALSE;
 }
 
 CPortConfigurationDlg::~CPortConfigurationDlg()
 {
+}
+
+void CPortConfigurationDlg::setCurSelPort(int sel)
+{
+    m_nCurSelPort = sel;
 }
 
 int CPortConfigurationDlg::GetLoadPortEqID(const std::string& strPortName)
@@ -45,6 +54,10 @@
         return;
     }
 
+    CStringArray permissions;
+    permissions.Add(_T("G1"));
+    permissions.Add(_T("G2"));
+
     SetDlgItemText(IDC_EDIT_LOTID, "");
     SetDlgItemText(IDC_EDIT_PRODUCTID, "");
     SetDlgItemText(IDC_EDIT_OPERATIONID, "");
@@ -57,32 +70,61 @@
             continue;
         }
 
-        SERVO::CGlass* pGlass = dynamic_cast<SERVO::CGlass*>(pSlot->getContext());
-        int nRow = i + 1;
-
         // 璁剧疆 Panel ID 鍜屽嬀閫夋
-        CGridCellCheck* pCheck = static_cast<CGridCellCheck*>(m_wndGrid.GetCell(nRow, 1));
-        if (pCheck && pGlass) {
-            pCheck->SetCheck(pSlot->isEnable() ? TRUE : FALSE);
-            pCheck->SetText(pGlass ? pGlass->getID().c_str() : _T(""));
+        SERVO::CGlass* pGlass = dynamic_cast<SERVO::CGlass*>(pSlot->getContext());
+        SERVO::CJobDataS* pJobDataS = pGlass->getJobDataS();
+        int nRow = i + 1;
+        if (pGlass != nullptr && pJobDataS != nullptr) {
+            m_wndGrid.SetItemState(nRow, 0, GVIS_READONLY);
+            m_wndGrid.SetItemText(nRow, 0, pSlot->getName().c_str());
+            m_wndGrid.SetCellType(nRow, 1, RUNTIME_CLASS(CGridCellCheck));
+            CGridCellCheck* pCheck = dynamic_cast<CGridCellCheck*>(m_wndGrid.GetCell(nRow, 1));
+            ASSERT(pCheck);
+            pCheck->SetCheck(pGlass->isScheduledForProcessing());
+            pCheck->SetText(pGlass->getID().c_str());
+
+            if (m_wndGrid.SetCellType(nRow, 2, RUNTIME_CLASS(CGridCellCombo))) {
+                CGridCellCombo* pCell = static_cast<CGridCellCombo*>(m_wndGrid.GetCell(nRow, 2));
+                pCell->SetOptions(permissions);
+                pCell->SetStyle(CBS_DROPDOWNLIST);
+
+                int nMaterialsType = pJobDataS->getMaterialsType() - 1;
+                if (nMaterialsType < 0 || nMaterialsType > 2) {
+                    nMaterialsType = 0;
+                }
+                m_wndGrid.SetItemText(nRow, 2, permissions.GetAt(nMaterialsType));
+            }
         }
-		else {
-			pCheck->SetCheck(FALSE);
-			pCheck->SetText(_T(""));
-		}
+        m_wndGrid.SetItemData(nRow, 0, (LPARAM)pGlass);
 
         // 鍥炲~ Job 淇℃伅锛堝彧鍙栫涓�涓湁鏁� Glass锛�
         if (!bJobInfoSet && pGlass) {
             SERVO::CJobDataS* pJS = pGlass->getJobDataS();
-            if (pJS) {
+            if (pJS) {        
+                int nRecipeID = pJobDataS->getMasterRecipe();
+                std::string strRecipeName = RecipeManager::getInstance().getPPIDById(nRecipeID);
                 SetDlgItemText(IDC_EDIT_LOTID, CString(pJS->getLotId().c_str()));
                 SetDlgItemText(IDC_EDIT_PRODUCTID, CString(pJS->getProductId().c_str()));
                 SetDlgItemText(IDC_EDIT_OPERATIONID, CString(pJS->getOperationId().c_str()));
                 m_comboMaterialsType.SetCurSel(pJS->getMaterialsType() - 1);
+
+                if (!strRecipeName.empty()) {
+                    CString csRecipeName(strRecipeName.c_str());
+                    int nIndex = m_comboRecipe.FindStringExact(-1, csRecipeName);
+                    if (nIndex != CB_ERR) {
+                        m_comboRecipe.SetCurSel(nIndex);
+                    }
+                    else {
+						AfxMessageBox(_T("褰撳墠閰嶆柟鍦ㄧ郴缁熶腑涓嶅瓨鍦紝璇烽噸鏂伴�夋嫨锛�"), MB_ICONWARNING);
+                        m_comboRecipe.SetCurSel(0);
+                    }
+				}
+
                 bJobInfoSet = true;
             }
         }
     }
+    m_pCheckBox->SetCheck(IsCheckedAll() ? BST_CHECKED : BST_UNCHECKED);
 }
 
 void CPortConfigurationDlg::InitGrid()
@@ -91,7 +133,7 @@
         return;
     }
 
-    const int nCols = 2;
+    const int nCols = 3;
     const int nFixRows = 1;
 	const int nRows = SLOT_MAX + 1; // 瀛樺湪琛ㄥご锛屾墍浠� +1
 
@@ -115,14 +157,17 @@
     m_wndGrid.SetColumnWidth(nColIdx, 50);
     m_wndGrid.SetItemText(0, nColIdx++, _T("Slot ID"));
     m_wndGrid.SetColumnWidth(nColIdx, 60);
-    m_wndGrid.SetItemText(0, nColIdx++, _T("鍚敤"));
+    m_wndGrid.SetItemText(0, nColIdx++, _T("Glass ID"));
+    m_wndGrid.SetColumnWidth(nColIdx, 60);
+    m_wndGrid.SetItemText(0, nColIdx++, _T("鐗╂枡绫诲瀷"));
+
 
     // 璁剧疆琛屼负鏍峰紡
     m_wndGrid.SetFixedRowSelection(FALSE);
     m_wndGrid.SetFixedColumnSelection(FALSE);
     m_wndGrid.SetEditable(TRUE);
     m_wndGrid.SetRowResize(FALSE);
-    m_wndGrid.SetColumnResize(TRUE);
+    m_wndGrid.SetColumnResize(FALSE);
     m_wndGrid.SetListMode(TRUE);
     m_wndGrid.EnableSelection(TRUE);
     m_wndGrid.SetSingleRowSelection(TRUE);
@@ -139,32 +184,16 @@
         m_wndGrid.SetRowHeight(i, nEachRowHeight);
     }
 
-    FillGrid();
-}
-
-void CPortConfigurationDlg::FillGrid()
-{
-    //CStringArray recipeOptions;
-    //recipeOptions.Add(_T("Recipe A"));
-    //recipeOptions.Add(_T("Recipe B"));
-    //recipeOptions.Add(_T("Recipe C"));
-
-    for (int i = 1; i < 9; ++i) {
-        CString strIndex;
-        strIndex.Format(_T("%d"), i);
-        m_wndGrid.SetItemText(i, 0, strIndex);
-        m_wndGrid.SetItemState(i, 0, GVIS_READONLY);
-
-        // Checkbox
-        m_wndGrid.SetCellType(i, 1, RUNTIME_CLASS(CGridCellCheck));
-        CGridCellCheck* pCheck = static_cast<CGridCellCheck*>(m_wndGrid.GetCell(i, 1));
-        if (pCheck) {
-            pCheck->SetCheck(FALSE);
-        }
-    }
-
-    m_wndGrid.Invalidate();
-    m_wndGrid.UpdateWindow();
+    CRect rect;
+    BOOL bOK = m_wndGrid.GetCellRect(0, 1, rect);
+    m_pCheckBox = new CCustomCheckBox();
+    m_pCheckBox->Create(_T("閫夋嫨鎵�鏈�"),
+        WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, // 鑷姩鍕鹃��
+        CRect(0, 0, 150, 32), &m_wndGrid, CHECKBOX_ALL_ID);
+    m_pCheckBox->SetFont(GetFont());
+    m_pCheckBox->MoveWindow(rect.left + 5, rect.top + 3, 150, rect.Height() - 6);
+    m_pCheckBox->SetBackgroundColor(g_nGridFixCellColor);
+    m_pCheckBox->SetNotifyHwnd(GetSafeHwnd());
 }
 
 void CPortConfigurationDlg::DoDataExchange(CDataExchange* pDX)
@@ -180,6 +209,10 @@
 BEGIN_MESSAGE_MAP(CPortConfigurationDlg, CDialogEx)
     ON_CBN_SELCHANGE(IDC_COMBO_PORT, &CPortConfigurationDlg::OnSelchangeComboPort)
     ON_BN_CLICKED(IDC_BUTTON_APPLY, &CPortConfigurationDlg::OnBnClickedButtonApply)
+    ON_MESSAGE(WM_CHECKBOX_STATE_CHANGED, &CPortConfigurationDlg::OnCheckAllClicked)
+    ON_WM_DESTROY()
+    ON_BN_CLICKED(IDC_BUTTON_PROCESS_START, &CPortConfigurationDlg::OnBnClickedButtonProcessStart)
+    ON_BN_CLICKED(IDC_BUTTON_PROCESS_CANCEL, &CPortConfigurationDlg::OnBnClickedButtonProcessCancel)
 END_MESSAGE_MAP()
 
 
@@ -195,7 +228,13 @@
     for (const auto& item : ports) {
         m_comboPort.AddString(item);
     }
-    m_comboPort.SetCurSel(0); // 榛樿閫夋嫨绗竴涓鍙�
+    if (0 <= m_nCurSelPort && m_nCurSelPort <= 3) {
+        m_comboPort.SetCurSel(m_nCurSelPort);
+        m_comboPort.EnableWindow(FALSE);
+    }
+    else {
+        m_comboPort.SetCurSel(0);
+    }
 
     // 鍒濆鍖栭厤鏂逛笅鎷夋鍐呭
     std::vector<std::string> vecRecipe = RecipeManager::getInstance().getAllPPID();
@@ -210,13 +249,25 @@
         m_comboMaterialsType.AddString(item);
     }
     m_comboMaterialsType.SetCurSel(0);  // 榛樿閫夋嫨绗竴涓墿鏂欑被鍨�
-
+    m_comboMaterialsType.EnableWindow(FALSE);
     InitGrid();
 
 	LoadPortConfigToUI(m_pPort[0]);     // 榛樿鍔犺浇绗竴涓鍙g殑閰嶇疆
 
 	// 璁剧疆瀵硅瘽妗嗘爣棰�
-	SetWindowText(_T("Port Configuration"));
+    BOOL bAutoPopup = 0 <= m_nCurSelPort && m_nCurSelPort <= 3;
+    if (bAutoPopup) {
+        CString strTitle;
+        strTitle.Format(_T("%s Configuration"), ports[m_nCurSelPort]);
+        SetWindowText(strTitle);
+    }
+    else {
+        SetWindowText(_T("Port Configuration"));
+    }
+
+    // Porcess Start / Process Cancel 鎸夐挳鐘舵��
+    GetDlgItem(IDC_BUTTON_PROCESS_START)->EnableWindow(FALSE);
+    GetDlgItem(IDC_BUTTON_PROCESS_CANCEL)->EnableWindow(FALSE);
 
 	return TRUE;  // return TRUE unless you set the focus to a control
 	// 寮傚父: OCX 灞炴�ч〉搴旇繑鍥� FALSE
@@ -231,6 +282,12 @@
 	}
 
 	// 鍔犺浇閫変腑绔彛鐨勯厤缃埌 UI
+    for (int i = 1; i <= 8; i++) {
+        m_wndGrid.SetItemText(i, 0, "");
+        m_wndGrid.SetItemText(i, 1, "");
+        m_wndGrid.SetCellType(i, 1, RUNTIME_CLASS(CGridCellNumeric));
+    }
+    m_wndGrid.Invalidate();
 	LoadPortConfigToUI(m_pPort[selPort]);
 }
 
@@ -254,16 +311,19 @@
         m_comboRecipe.GetLBText(selRecipe, str);
         config.strRecipe = CT2A(str.GetString());
     }
+    int nRecipeID = RecipeManager::getInstance().getIdByPPID(config.strRecipe);
+    RecipeInfo stRecipeInfo = RecipeManager::getInstance().getRecipeByPPID(config.strRecipe);
+    std::vector<DeviceRecipe> vecRecipeInfo = stRecipeInfo.vecDeviceList;
 
     // 鑾峰彇 Material Type 绱㈠紩锛堢储寮曚粠 0 寮�濮嬶紝瀵瑰簲鏋氫妇浠� 1 寮�濮嬶級
-    int selMaterial = m_comboMaterialsType.GetCurSel();
-    if (selMaterial != CB_ERR) {
-        config.nMaterialType = selMaterial + 1;
-    }
-	else {
-		AfxMessageBox(_T("Please select a material type!"));
-		return;
-	}
+    //int selMaterial = m_comboMaterialsType.GetCurSel();
+    //if (selMaterial != CB_ERR) {
+    //    config.nMaterialType = selMaterial + 1;
+    //}
+	//else {
+	//	AfxMessageBox(_T("Please select a material type!"));
+	//	return;
+	//}
 
     // 鑾峰彇 Lot ID / Product ID / Operation ID
     CString strText;
@@ -289,35 +349,190 @@
 	}
 
     // 鑾峰彇 Grid 琛ㄦ牸涓� Slot 鐘舵�侊紙绗�1~8琛岋級
-    for (int i = 1; i <= 8; ++i) {
-        SERVO::SlotConfig slot;
-        slot.nSlotID = i;
+    for (int i = 1; i <= SLOT_MAX; ++i) {
+        SERVO::CGlass* pGlass = (SERVO::CGlass*)m_wndGrid.GetItemData(i, 0);
+        int nMaterialType = m_wndGrid.GetItemText(i, 2).CompareNoCase("G1") == 0 ? 1 : 2;
+        if (pGlass != nullptr) {
+            CGridCellCheck* pCheck = dynamic_cast<CGridCellCheck*>(m_wndGrid.GetCell(i, 1));
+            CGridCellCombo* pCombo = dynamic_cast<CGridCellCombo*>(m_wndGrid.GetCell(i, 2));
+            ASSERT(pCheck && pCombo);
+            pGlass->setScheduledForProcessing(pCheck->GetCheck());
+            pGlass->setType(static_cast<SERVO::MaterialsType>(nMaterialType));
 
-        CGridCellCheck* pCheck = static_cast<CGridCellCheck*>(m_wndGrid.GetCell(i, 1));
-        if (pCheck) {
-            slot.isEnabled = pCheck->GetCheck();
+            SERVO::CJobDataS* pJobDataS = pGlass->getJobDataS();
+            pJobDataS->setLotId(config.strLotID.c_str());
+            pJobDataS->setProductId(config.strProductID.c_str());
+            pJobDataS->setOperationId(config.strOperationID.c_str());
+            pJobDataS->setMaterialsType(nMaterialType);
+            pJobDataS->setMasterRecipe(nRecipeID);
+
+            for (const auto& info : vecRecipeInfo) {
+                const std::string& name = info.strDeviceName;
+                short nRecipeID = (short)info.nRecipeID;
+
+                if (name == EQ_NAME_EFEM) {
+                    pJobDataS->setDeviceRecipeId(0, nRecipeID);
+                }
+                else if (name == EQ_NAME_BONDER1) {
+                    pJobDataS->setDeviceRecipeId(1, nRecipeID);
+                }
+                else if (name == EQ_NAME_BONDER2) {
+                    pJobDataS->setDeviceRecipeId(2, nRecipeID);
+                }
+                else if (name == EQ_NAME_BAKE_COOLING) {
+                    pJobDataS->setDeviceRecipeId(3, nRecipeID);
+                }
+                else if (name == EQ_NAME_VACUUMBAKE) {
+                    pJobDataS->setDeviceRecipeId(4, nRecipeID);
+                }
+                else if (name == EQ_NAME_MEASUREMENT) {
+                    pJobDataS->setDeviceRecipeId(5, nRecipeID);
+                }
+            }
         }
-
-        config.vecSlot.push_back(slot);
     }
 
-    int nEqID = GetLoadPortEqID(config.strPortName);
-    if (nEqID < 0) {
-        AfxMessageBox(_T("鏈煡鐨勭鍙e悕绉帮紒"));
+    GetDlgItem(IDC_BUTTON_PROCESS_START)->EnableWindow(TRUE);
+    GetDlgItem(IDC_BUTTON_PROCESS_CANCEL)->EnableWindow(TRUE);
+}
+
+void CPortConfigurationDlg::OnDestroy()
+{
+    CDialogEx::OnDestroy();
+
+    if (m_pCheckBox != nullptr) {
+        m_pCheckBox->DestroyWindow();
+        delete m_pCheckBox;
+        m_pCheckBox = nullptr;
+    }
+}
+
+LRESULT  CPortConfigurationDlg::OnCheckAllClicked(WPARAM wParam, LPARAM lParam)
+{
+    UINT ctrlID = (UINT)wParam;
+    BOOL bChecked = (BOOL)lParam;
+    for (int i = 1; i <= SLOT_MAX; ++i) {
+        CGridCellCheck* pCheck = dynamic_cast<CGridCellCheck*>(m_wndGrid.GetCell(i, 1));
+        if (pCheck != nullptr) {
+            pCheck->SetCheck(bChecked);
+        }
+    }
+
+
+    return 0;
+}
+
+BOOL CPortConfigurationDlg::IsCheckedAll()
+{
+    for (int i = 1; i <= SLOT_MAX; ++i) {
+        CGridCellCheck* pCheck = dynamic_cast<CGridCellCheck*>(m_wndGrid.GetCell(i, 1));
+        if (pCheck != nullptr) {
+            if (!pCheck->GetCheck()) return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+void CPortConfigurationDlg::OnBnClickedButtonProcessStart()
+{
+    int selPort = (0 <= m_nCurSelPort && m_nCurSelPort <= 3) ? m_nCurSelPort
+        : m_comboPort.GetCurSel();
+    if (selPort < 0 || selPort >= 4) {
+        LOGE("ProcessStart invalid port index: %d", selPort);
         return;
     }
 
-    SERVO::CLoadPort* pPort = dynamic_cast<SERVO::CLoadPort*>(theApp.m_model.m_master.getEquipment(nEqID));
-    if (!pPort) {
-        AfxMessageBox(_T("鎵句笉鍒板搴旂殑 LoadPort 璁惧锛�"));
+    SERVO::CLoadPort* pPort = m_pPort[selPort];
+    if (pPort == nullptr) {
+        LOGE("ProcessStart port pointer is null, index: %d", selPort);
         return;
     }
 
-    // 搴旂敤閰嶇疆锛堜緥濡傛祴璇曠敓鎴愮幓鐠冿級
-    if (pPort->testGenerateGlassListFromConfig(config) < 0) {
-		AfxMessageBox(_T("Failed to generate glass list from configuration!"));
-        return;
-	}
+    constexpr short cmd = CCC_PROCESS_START;
+    LOGI("ProcessStart request: port=%d, cmd=%d", selPort + 1, cmd);
+    short jobExistence[12] = { 0 };
+    short slotProcess = 0;
+    short jobCount = 0; // 0 = Process All Glass (per spec)
+    bool anyScheduled = false;
 
-    OnOK();
-}
\ No newline at end of file
+    // Prefer hardware scan map for job existence (first 16 slots).
+    const short scanMap = pPort->getScanCassetteMap();
+    if (scanMap != 0) {
+        jobExistence[0] = scanMap;
+    }
+
+    // Build existence/selected maps from current glass list (up to 192 slots).
+    const int maxSlots = 12 * 16;
+    const int totalSlots = (SLOT_MAX < maxSlots) ? SLOT_MAX : maxSlots;
+    for (int slot = 1; slot <= totalSlots; ++slot) {
+        SERVO::CGlass* pGlass = pPort->getGlassFromSlot(slot);
+        if (pGlass == nullptr) continue;
+
+        const int wordIndex = (slot - 1) / 16;
+        const int bitIndex = (slot - 1) % 16;
+        jobExistence[wordIndex] = (short)(jobExistence[wordIndex] | (1 << bitIndex));
+
+        if (slot <= 16 && pGlass->isScheduledForProcessing()) {
+            slotProcess = (short)(slotProcess | (1 << bitIndex));
+            anyScheduled = true;
+        }
+    }
+
+    // If no slot explicitly selected, default to all existing in the first word.
+    if (!anyScheduled) {
+        slotProcess = jobExistence[0];
+    }
+
+    int ret = pPort->sendCassetteCtrlCmd(cmd, jobExistence, 12, slotProcess, jobCount, nullptr,
+        [selPort](int code) -> int {
+            if (code == WOK) {
+                LOGI("ProcessStart write complete: port=%d, code=WOK", selPort + 1);
+            }
+            else {
+                LOGE("ProcessStart write failed: port=%d, code=%d", selPort + 1, code);
+            }
+            return 0;
+        });
+    if (ret != 0) {
+        LOGE("ProcessStart sendCassetteCtrlCmd immediate failure: port=%d, ret=%d", selPort + 1, ret);
+    }
+    else {
+        LOGI("ProcessStart sendCassetteCtrlCmd dispatched: port=%d", selPort + 1);
+    }
+}
+
+void CPortConfigurationDlg::OnBnClickedButtonProcessCancel()
+{
+    int selPort = (0 <= m_nCurSelPort && m_nCurSelPort <= 3) ? m_nCurSelPort
+        : m_comboPort.GetCurSel();
+    if (selPort < 0 || selPort >= 4) {
+        LOGE("ProcessCancel invalid port index: %d", selPort);
+        return;
+    }
+
+    SERVO::CLoadPort* pPort = m_pPort[selPort];
+    if (pPort == nullptr) {
+        LOGE("ProcessCancel port pointer is null, index: %d", selPort);
+        return;
+    }
+
+    constexpr short cmd = CCC_PROCESS_CANCEL;
+    LOGI("ProcessCancel request: port=%d, cmd=%d", selPort + 1, cmd);
+    int ret = pPort->sendCassetteCtrlCmd(cmd, nullptr, 0, 0, 0, nullptr,
+        [selPort](int code) -> int {
+            if (code == WOK) {
+                LOGI("ProcessCancel write complete: port=%d, code=WOK", selPort + 1);
+            }
+            else {
+                LOGE("ProcessCancel write failed: port=%d, code=%d", selPort + 1, code);
+            }
+            return 0;
+        });
+    if (ret != 0) {
+        LOGE("ProcessCancel sendCassetteCtrlCmd immediate failure: port=%d, ret=%d", selPort + 1, ret);
+    }
+    else {
+        LOGI("ProcessCancel sendCassetteCtrlCmd dispatched: port=%d", selPort + 1);
+    }
+}

--
Gitblit v1.9.3