From 0de0a72329a77fbf20bc299c542e435a6d47256b Mon Sep 17 00:00:00 2001
From: LAPTOP-SNT8I5JK\Boounion <Chenluhua@qq.com>
Date: 星期六, 21 六月 2025 17:12:01 +0800
Subject: [PATCH] Merge branch 'liuyang' into clh

---
 SourceCode/Bond/Servo/RecipeManager.cpp        |   19 ++
 SourceCode/Bond/Servo/resource.h               |    0 
 SourceCode/Bond/Servo/RecipeDeviceBindDlg.h    |   11 +
 SourceCode/Bond/Servo/CLoadPort.h              |    1 
 SourceCode/Bond/Servo/RecipeManager.h          |    3 
 SourceCode/Bond/Servo/Model.cpp                |    2 
 SourceCode/Bond/Servo/CLoadPort.cpp            |   40 +++++
 SourceCode/Bond/Servo/PortConfigurationDlg.h   |   14 +
 SourceCode/Bond/Servo/ServoDlg.cpp             |    5 
 SourceCode/Bond/Servo/PortConfigurationDlg.cpp |  219 ++++++++++++++++++++++++++++---
 SourceCode/Bond/Servo/TopToolbar.h             |    1 
 SourceCode/Bond/Servo/Servo.rc                 |    0 
 SourceCode/Bond/Servo/TopToolbar.cpp           |    8 +
 SourceCode/Bond/Servo/ServoCommo.h             |   19 ++
 SourceCode/Bond/Servo/RecipeDeviceBindDlg.cpp  |   39 +++++
 15 files changed, 355 insertions(+), 26 deletions(-)

diff --git a/SourceCode/Bond/Servo/CLoadPort.cpp b/SourceCode/Bond/Servo/CLoadPort.cpp
index cee42a2..d2366f1 100644
--- a/SourceCode/Bond/Servo/CLoadPort.cpp
+++ b/SourceCode/Bond/Servo/CLoadPort.cpp
@@ -1140,4 +1140,44 @@
 		return 0;
 	}
 
+	int CLoadPort::testGenerateGlassListFromConfig(const SERVO::PortConfig& config)
+	{
+		char szBuffer[64];
+		for (const auto& slot : config.vecSlot) {
+			if (!slot.isEnabled) continue;
+
+			int nSlotIndex = slot.nSlotID - 1;
+			if (nSlotIndex < 0 || nSlotIndex >= SLOT_MAX) {
+				continue;
+			}
+
+			m_slot[nSlotIndex].enable();
+
+			if (!m_slot[nSlotIndex].isEnable()) continue;
+
+			CJobDataS js;
+			js.setCassetteSequenceNo(getNextCassetteSequenceNo());
+			js.setJobSequenceNo(m_slot[nSlotIndex].getNo());
+
+			sprintf_s(szBuffer, 64, "%05d%05d", js.getCassetteSequenceNo(), js.getJobSequenceNo());
+			js.setJobType(1);
+			js.setMaterialsType(config.nMaterialType);
+
+			js.setLotId(config.strLotID.c_str());
+			js.setProductId(config.strProductID.c_str());
+			js.setOperationId(config.strOperationID.c_str());
+			js.setGlass1Id(szBuffer);
+
+			CGlass* pGlass = theApp.m_model.m_glassPool.allocaGlass();
+			pGlass->addPath(m_nID, 0);
+			pGlass->processEnd(m_nID, 0);
+			pGlass->setID(szBuffer);
+			pGlass->setType(static_cast<SERVO::MaterialsType>(config.nMaterialType));
+			pGlass->setJobDataS(&js);
+
+			m_slot[nSlotIndex].setContext(pGlass);
+		}
+
+		return 0;
+	}
 }
diff --git a/SourceCode/Bond/Servo/CLoadPort.h b/SourceCode/Bond/Servo/CLoadPort.h
index 0910af3..bc91232 100644
--- a/SourceCode/Bond/Servo/CLoadPort.h
+++ b/SourceCode/Bond/Servo/CLoadPort.h
@@ -56,6 +56,7 @@
 		int getCassetteMappingState();
 		int getCassetteStatus();
 		int testGenerateGlassList(MaterialsType type);
+		int testGenerateGlassListFromConfig(const SERVO::PortConfig& config);
 
 	public:
 		static std::string& getPortTypeDescription(PortType portType, std::string& strDescription);
diff --git a/SourceCode/Bond/Servo/Model.cpp b/SourceCode/Bond/Servo/Model.cpp
index 78ba03f..064a164 100644
--- a/SourceCode/Bond/Servo/Model.cpp
+++ b/SourceCode/Bond/Servo/Model.cpp
@@ -217,7 +217,7 @@
 
 		// 安全格式化时间
 		auto format_time = [](time_t t) -> std::string {
-			if (t < 0 || t == _I64_MIN || t == _I64_MAX) { 
+			if (t <= 0 || t == _I64_MIN || t == _I64_MAX) { 
 				return "";
 			}
 
diff --git a/SourceCode/Bond/Servo/PortConfigurationDlg.cpp b/SourceCode/Bond/Servo/PortConfigurationDlg.cpp
index 79a1e11..b941707 100644
--- a/SourceCode/Bond/Servo/PortConfigurationDlg.cpp
+++ b/SourceCode/Bond/Servo/PortConfigurationDlg.cpp
@@ -8,6 +8,8 @@
 #include "NewCellTypes/GridCellCheck.h"
 #include "NewCellTypes/GridCellCombo.h"
 #include "NewCellTypes/GridCellNumeric.h"
+#include "RecipeManager.h"
+#include "ServoCommo.h"
 
 
 // CPortConfigurationDlg 瀵硅瘽妗�
@@ -17,11 +19,70 @@
 CPortConfigurationDlg::CPortConfigurationDlg(CWnd* pParent /*=nullptr*/)
 	: CDialogEx(IDD_DIALOG_PORT_CONFIGURATION, pParent)
 {
-
+	// 鍒濆鍖栨垚鍛樺彉閲�
+    m_pPort[0] = dynamic_cast<SERVO::CLoadPort*>(theApp.m_model.m_master.getEquipment(EQ_ID_LOADPORT1));
+    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));
 }
 
 CPortConfigurationDlg::~CPortConfigurationDlg()
 {
+}
+
+int CPortConfigurationDlg::GetLoadPortEqID(const std::string& strPortName)
+{
+    if (strPortName == "Port 1") return EQ_ID_LOADPORT1;
+    if (strPortName == "Port 2") return EQ_ID_LOADPORT2;
+    if (strPortName == "Port 3") return EQ_ID_LOADPORT3;
+    if (strPortName == "Port 4") return EQ_ID_LOADPORT4;
+    return -1; // 鏈煡绔彛
+}
+
+void CPortConfigurationDlg::LoadPortConfigToUI(SERVO::CLoadPort* pPort)
+{
+    if (!pPort) {
+        return;
+    }
+
+    SetDlgItemText(IDC_EDIT_LOTID, "");
+    SetDlgItemText(IDC_EDIT_PRODUCTID, "");
+    SetDlgItemText(IDC_EDIT_OPERATIONID, "");
+    m_comboMaterialsType.SetCurSel(0);
+
+    bool bJobInfoSet = false;
+    for (int i = 0; i < SLOT_MAX; ++i) {
+        SERVO::CSlot* pSlot = pPort->getSlot(i);
+        if (!pSlot) { 
+            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(""));
+        }
+		else {
+			pCheck->SetCheck(FALSE);
+			pCheck->SetText(_T(""));
+		}
+
+        // 鍥炲~ Job 淇℃伅锛堝彧鍙栫涓�涓湁鏁� Glass锛�
+        if (!bJobInfoSet && pGlass) {
+            SERVO::CJobDataS* pJS = pGlass->getJobDataS();
+            if (pJS) {
+                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);
+                bJobInfoSet = true;
+            }
+        }
+    }
 }
 
 void CPortConfigurationDlg::InitGrid()
@@ -30,9 +91,9 @@
         return;
     }
 
-    const int nCols = 3;
+    const int nCols = 2;
     const int nFixRows = 1;
-    const int nRows = 9;
+	const int nRows = SLOT_MAX + 1; // 瀛樺湪琛ㄥご锛屾墍浠� +1
 
     int nColIdx = 0;
     m_wndGrid.DeleteAllItems();
@@ -53,10 +114,6 @@
     // 璁剧疆鍒楀
     m_wndGrid.SetColumnWidth(nColIdx, 50);
     m_wndGrid.SetItemText(0, nColIdx++, _T("Slot ID"));
-    m_wndGrid.SetColumnWidth(nColIdx, 150);
-    m_wndGrid.SetItemText(0, nColIdx++, _T("EQ Recipe"));
-    m_wndGrid.SetColumnWidth(nColIdx, 150);
-    m_wndGrid.SetItemText(0, nColIdx++, _T("Panel ID"));
     m_wndGrid.SetColumnWidth(nColIdx, 60);
     m_wndGrid.SetItemText(0, nColIdx++, _T("鍚敤"));
 
@@ -98,20 +155,9 @@
         m_wndGrid.SetItemText(i, 0, strIndex);
         m_wndGrid.SetItemState(i, 0, GVIS_READONLY);
 
-        // EQ Recipe - ComboBox
-        //if (m_wndGrid.SetCellType(i, 1, RUNTIME_CLASS(CGridCellCombo))) {
-        //    CGridCellCombo* pCell = static_cast<CGridCellCombo*>(m_wndGrid.GetCell(i, 1));
-        //    pCell->SetOptions(recipeOptions);
-        //    pCell->SetStyle(CBS_DROPDOWNLIST);
-        //}
-        //m_wndGrid.SetItemText(i, 1, recipeOptions[0]);
-
-        // Panel ID - 鍙紪杈�
-        m_wndGrid.SetItemText(i, 1, _T(""));
-
         // Checkbox
-        m_wndGrid.SetCellType(i, 2, RUNTIME_CLASS(CGridCellCheck));
-        CGridCellCheck* pCheck = static_cast<CGridCellCheck*>(m_wndGrid.GetCell(i, 2));
+        m_wndGrid.SetCellType(i, 1, RUNTIME_CLASS(CGridCellCheck));
+        CGridCellCheck* pCheck = static_cast<CGridCellCheck*>(m_wndGrid.GetCell(i, 1));
         if (pCheck) {
             pCheck->SetCheck(FALSE);
         }
@@ -123,12 +169,17 @@
 
 void CPortConfigurationDlg::DoDataExchange(CDataExchange* pDX)
 {
+    CDialogEx::DoDataExchange(pDX);
     DDX_Control(pDX, IDC_GRID_PANEL_RECIPE, m_wndGrid);
-	CDialogEx::DoDataExchange(pDX);
+    DDX_Control(pDX, IDC_COMBO_PORT, m_comboPort);
+    DDX_Control(pDX, IDC_COMBO_RECIPE, m_comboRecipe);
+    DDX_Control(pDX, IDC_COMBO_MATERIALS_TYPE, m_comboMaterialsType);
 }
 
 
 BEGIN_MESSAGE_MAP(CPortConfigurationDlg, CDialogEx)
+    ON_CBN_SELCHANGE(IDC_COMBO_PORT, &CPortConfigurationDlg::OnSelchangeComboPort)
+    ON_BN_CLICKED(IDC_BUTTON_APPLY, &CPortConfigurationDlg::OnBnClickedButtonApply)
 END_MESSAGE_MAP()
 
 
@@ -139,8 +190,134 @@
 	CDialogEx::OnInitDialog();
 
 	// TODO:  鍦ㄦ娣诲姞棰濆鐨勫垵濮嬪寲
+    // 鍒濆鍖栫鍙d笅鎷夋鍐呭
+    CString ports[] = { _T("Port 1"), _T("Port 2"), _T("Port 3"), _T("Port 4") };
+    for (const auto& item : ports) {
+        m_comboPort.AddString(item);
+    }
+    m_comboPort.SetCurSel(0); // 榛樿閫夋嫨绗竴涓鍙�
+
+    // 鍒濆鍖栭厤鏂逛笅鎷夋鍐呭
+    std::vector<std::string> vecRecipe = RecipeManager::getInstance().getAllPPID();
+    for (const auto& recipe : vecRecipe) {
+        m_comboRecipe.AddString(CString(recipe.c_str()));
+    }
+    m_comboRecipe.SetCurSel(0); // 榛樿閫夋嫨绗竴涓厤鏂�
+
+    // 鍒濆鍖栫墿鏂欑被鍨嬩笅鎷夋鍐呭
+    CString materialTypes[] = { _T("G1"), _T("G2"), _T("G1+G2") };
+    for (const auto& item : materialTypes) {
+        m_comboMaterialsType.AddString(item);
+    }
+    m_comboMaterialsType.SetCurSel(0);  // 榛樿閫夋嫨绗竴涓墿鏂欑被鍨�
+
     InitGrid();
+
+	LoadPortConfigToUI(m_pPort[0]);     // 榛樿鍔犺浇绗竴涓鍙g殑閰嶇疆
+
+	// 璁剧疆瀵硅瘽妗嗘爣棰�
+	SetWindowText(_T("Port Configuration"));
 
 	return TRUE;  // return TRUE unless you set the focus to a control
 	// 寮傚父: OCX 灞炴�ч〉搴旇繑鍥� FALSE
 }
+
+void CPortConfigurationDlg::OnSelchangeComboPort()
+{
+    // TODO: 鍦ㄦ娣诲姞鎺т欢閫氱煡澶勭悊绋嬪簭浠g爜
+	int selPort = m_comboPort.GetCurSel();
+	if (selPort < 0 || selPort >= 4) {
+		return; // 鏃犳晥閫夋嫨
+	}
+
+	// 鍔犺浇閫変腑绔彛鐨勯厤缃埌 UI
+	LoadPortConfigToUI(m_pPort[selPort]);
+}
+
+void CPortConfigurationDlg::OnBnClickedButtonApply()
+{
+    // TODO: 鍦ㄦ娣诲姞鎺т欢閫氱煡澶勭悊绋嬪簭浠g爜
+    SERVO::PortConfig config;
+
+    // 鑾峰彇 Port 鍚嶇О
+    int selPort = m_comboPort.GetCurSel();
+    if (selPort != CB_ERR) {
+        CString str;
+        m_comboPort.GetLBText(selPort, str);
+        config.strPortName = CT2A(str.GetString());
+    }
+
+    // 鑾峰彇 Recipe 鍚嶇О
+    int selRecipe = m_comboRecipe.GetCurSel();
+    if (selRecipe != CB_ERR) {
+        CString str;
+        m_comboRecipe.GetLBText(selRecipe, str);
+        config.strRecipe = CT2A(str.GetString());
+    }
+
+    // 鑾峰彇 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;
+	}
+
+    // 鑾峰彇 Lot ID / Product ID / Operation ID
+    CString strText;
+    GetDlgItemText(IDC_EDIT_LOTID, strText);
+    config.strLotID = CT2A(strText.GetString());
+    if (config.strLotID.empty()) {
+		AfxMessageBox(_T("Lot ID cannot be empty!"));
+		return;
+	}
+
+    GetDlgItemText(IDC_EDIT_PRODUCTID, strText);
+    config.strProductID = CT2A(strText.GetString());
+	if (config.strProductID.empty()) {
+		AfxMessageBox(_T("Product ID cannot be empty!"));
+		return;
+	}
+
+    GetDlgItemText(IDC_EDIT_OPERATIONID, strText);
+    config.strOperationID = CT2A(strText.GetString());
+	if (config.strOperationID.empty()) {
+		AfxMessageBox(_T("Operation ID cannot be empty!"));
+		return;
+	}
+
+    // 鑾峰彇 Grid 琛ㄦ牸涓� Slot 鐘舵�侊紙绗�1~8琛岋級
+    for (int i = 1; i <= 8; ++i) {
+        SERVO::SlotConfig slot;
+        slot.nSlotID = i;
+
+        CGridCellCheck* pCheck = static_cast<CGridCellCheck*>(m_wndGrid.GetCell(i, 1));
+        if (pCheck) {
+            slot.isEnabled = pCheck->GetCheck();
+        }
+
+        config.vecSlot.push_back(slot);
+    }
+
+    int nEqID = GetLoadPortEqID(config.strPortName);
+    if (nEqID < 0) {
+        AfxMessageBox(_T("鏈煡鐨勭鍙e悕绉帮紒"));
+        return;
+    }
+
+    SERVO::CLoadPort* pPort = dynamic_cast<SERVO::CLoadPort*>(theApp.m_model.m_master.getEquipment(nEqID));
+    if (!pPort) {
+        AfxMessageBox(_T("鎵句笉鍒板搴旂殑 LoadPort 璁惧锛�"));
+        return;
+    }
+
+    // 搴旂敤閰嶇疆锛堜緥濡傛祴璇曠敓鎴愮幓鐠冿級
+    if (pPort->testGenerateGlassListFromConfig(config) < 0) {
+		AfxMessageBox(_T("Failed to generate glass list from configuration!"));
+        return;
+	}
+
+    OnOK();
+}
\ No newline at end of file
diff --git a/SourceCode/Bond/Servo/PortConfigurationDlg.h b/SourceCode/Bond/Servo/PortConfigurationDlg.h
index ac7a710..f08af7f 100644
--- a/SourceCode/Bond/Servo/PortConfigurationDlg.h
+++ b/SourceCode/Bond/Servo/PortConfigurationDlg.h
@@ -1,6 +1,7 @@
 锘�#pragma once
 #include "afxdialogex.h"
 #include "GridCtrl.h"
+#include "CLoadPort.h"
 
 // CPortConfigurationDlg 瀵硅瘽妗�
 
@@ -20,11 +21,20 @@
 protected:
 	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 鏀寔
 	virtual BOOL OnInitDialog();
+	afx_msg void OnSelchangeComboPort();
+	afx_msg void OnBnClickedButtonApply();
 	DECLARE_MESSAGE_MAP()
 
 private:
-	CGridCtrl m_wndGrid;
-
+	int GetLoadPortEqID(const std::string& strPortName);
+	void LoadPortConfigToUI(SERVO::CLoadPort* pPort);
 	void InitGrid();
 	void FillGrid();
+
+	SERVO::CLoadPort* m_pPort[4];
+
+	CGridCtrl m_wndGrid;
+	CComboBox m_comboPort;
+	CComboBox m_comboRecipe;
+	CComboBox m_comboMaterialsType;
 };
diff --git a/SourceCode/Bond/Servo/RecipeDeviceBindDlg.cpp b/SourceCode/Bond/Servo/RecipeDeviceBindDlg.cpp
index fec07da..4098135 100644
--- a/SourceCode/Bond/Servo/RecipeDeviceBindDlg.cpp
+++ b/SourceCode/Bond/Servo/RecipeDeviceBindDlg.cpp
@@ -6,6 +6,9 @@
 #include "afxdialogex.h"
 #include "RecipeDeviceBindDlg.h"
 
+#define IDC_EDIT_DEVICEID_BASE     3000
+#define IDC_EDIT_DEVICENAME_BASE   3050
+#define IDC_COMBO_RECIPEID_BASE    3100
 
 // CRecipeDeviceBindDlg 瀵硅瘽妗�
 
@@ -32,3 +35,39 @@
 
 
 // CRecipeDeviceBindDlg 娑堟伅澶勭悊绋嬪簭
+
+BOOL CRecipeDeviceBindDlg::OnInitDialog()
+{
+	CDialogEx::OnInitDialog();
+
+	// TODO:  鍦ㄦ娣诲姞棰濆鐨勫垵濮嬪寲
+    // 璁剧疆鍥哄畾澶у皬锛堜緥濡� 600x400锛�
+    SetWindowPos(nullptr, 0, 0, 600, 400, SWP_NOMOVE | SWP_NOZORDER);
+
+	// 鍒涘缓鎺т欢
+    const int totalControlWidth = 340;
+    CRect clientRect;
+    GetClientRect(&clientRect);
+    int xStart = (clientRect.Width() - totalControlWidth) / 2;
+
+    const int nRowCount = 8;
+    const int nRowHeight = 30;
+    const int yStart = 30; // 椤堕儴璧峰楂樺害
+
+    for (int i = 0; i < nRowCount; ++i)
+    {
+        int y = yStart + i * nRowHeight;
+
+        CEdit* pEditID = new CEdit();
+        pEditID->Create(WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(xStart, y, xStart + 100, y + 25), this, IDC_EDIT_DEVICEID_BASE + i);
+
+        CEdit* pEditName = new CEdit();
+        pEditName->Create(WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(xStart + 110, y, xStart + 210, y + 25), this, IDC_EDIT_DEVICENAME_BASE + i);
+
+        CComboBox* pCombo = new CComboBox();
+        pCombo->Create(WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST, CRect(xStart + 220, y, xStart + 340, y + 300), this, IDC_COMBO_RECIPEID_BASE + i);
+    }
+
+	return TRUE;  // return TRUE unless you set the focus to a control
+	// 寮傚父: OCX 灞炴�ч〉搴旇繑鍥� FALSE
+}
\ No newline at end of file
diff --git a/SourceCode/Bond/Servo/RecipeDeviceBindDlg.h b/SourceCode/Bond/Servo/RecipeDeviceBindDlg.h
index c6634bf..d65c49a 100644
--- a/SourceCode/Bond/Servo/RecipeDeviceBindDlg.h
+++ b/SourceCode/Bond/Servo/RecipeDeviceBindDlg.h
@@ -19,6 +19,15 @@
 
 protected:
 	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 鏀寔
-
+	virtual BOOL OnInitDialog();
 	DECLARE_MESSAGE_MAP()
+
+private:
+	struct DeviceWidget {
+		CEdit editDeviceID;
+		CEdit editDeviceName;
+		CComboBox comboRecipeID;
+	};
+
+	std::vector<DeviceWidget> m_vecDevices;
 };
diff --git a/SourceCode/Bond/Servo/RecipeManager.cpp b/SourceCode/Bond/Servo/RecipeManager.cpp
index 6e58418..465c1af 100644
--- a/SourceCode/Bond/Servo/RecipeManager.cpp
+++ b/SourceCode/Bond/Servo/RecipeManager.cpp
@@ -238,6 +238,25 @@
     return recipes;
 }
 
+std::vector<std::string> RecipeManager::getAllPPID() const {
+    std::vector<std::string> vecPPID;
+
+    if (!m_pDB) {
+        return vecPPID;
+    }
+
+    const std::string query = "SELECT ppid FROM recipes ORDER BY ppid;";
+    auto result = m_pDB->fetchResults(query);
+
+    for (const auto& row : result) {
+        if (!row.empty()) {
+            vecPPID.push_back(row[0]);
+        }
+    }
+
+    return vecPPID;
+}
+
 RecipeInfo RecipeManager::getRecipeByPPID(const std::string& ppid) {
     RecipeInfo info;
     auto rows = m_pDB->fetchResults("SELECT ppid, description, create_time FROM recipes WHERE ppid = '" + ppid + "';");
diff --git a/SourceCode/Bond/Servo/RecipeManager.h b/SourceCode/Bond/Servo/RecipeManager.h
index a51e14d..f13bc50 100644
--- a/SourceCode/Bond/Servo/RecipeManager.h
+++ b/SourceCode/Bond/Servo/RecipeManager.h
@@ -58,6 +58,9 @@
     // 查询所有配方
     std::vector<RecipeInfo> getAllRecipes();
 
+	// 获取所有 PPID
+    std::vector<std::string> getAllPPID() const;
+
     // 按 PPID 查询配方
     RecipeInfo getRecipeByPPID(const std::string& ppid);
 
diff --git a/SourceCode/Bond/Servo/Servo.rc b/SourceCode/Bond/Servo/Servo.rc
index 3440b75..7f88830 100644
--- a/SourceCode/Bond/Servo/Servo.rc
+++ b/SourceCode/Bond/Servo/Servo.rc
Binary files differ
diff --git a/SourceCode/Bond/Servo/ServoCommo.h b/SourceCode/Bond/Servo/ServoCommo.h
index c8b3ae1..1e63c8e 100644
--- a/SourceCode/Bond/Servo/ServoCommo.h
+++ b/SourceCode/Bond/Servo/ServoCommo.h
@@ -1,5 +1,6 @@
 #pragma once
-
+#include <string>
+#include <vector>
 
 namespace SERVO {
 #define BLOCK_BUFFER_MAX			1024
@@ -165,6 +166,22 @@
 		Error
 	};
 
+	/* Port Status */
+	struct SlotConfig {
+		int nSlotID = 0;
+		bool isEnabled = false;
+	};
+
+	struct PortConfig {
+		int nMaterialType;			   // 物料类型,1: G1, 2: G2, 3: G1+G2
+		std::string strPortName;       // 例如 "Port 1"
+		std::string strRecipe;         // 例如 "P1001"
+		std::string strLotID;
+		std::string strProductID;
+		std::string strOperationID;
+		std::vector<SlotConfig> vecSlot;
+	};
+
 	/* EQ Data changed code */
 #define EDCC_FETCHOUT_JOB				1000	/* 取片 */
 #define EDCC_STORED_JOB					1001	/* 放片 */
diff --git a/SourceCode/Bond/Servo/ServoDlg.cpp b/SourceCode/Bond/Servo/ServoDlg.cpp
index b17c293..2de8710 100644
--- a/SourceCode/Bond/Servo/ServoDlg.cpp
+++ b/SourceCode/Bond/Servo/ServoDlg.cpp
@@ -21,6 +21,7 @@
 #include "SystemLogManagerDlg.h"
 #include "UserManager.h"
 #include "SystemLogManager.h"
+#include "PortConfigurationDlg.h"
 
 
 #ifdef _DEBUG
@@ -885,6 +886,10 @@
 			m_pTopToolbar->GetBtn(IDC_BUTTON_STOP)->EnableWindow(FALSE);
 		}
 	}
+	else if (id == IDC_BUTTON_PORT_CONFIG) {
+		CPortConfigurationDlg dlg;
+		dlg.DoModal();
+	}
 	else if (id == IDC_BUTTON_ROBOT) {
 		theApp.m_model.getMaster().clearError();
 		SERVO::CEFEM* pEFEM = (SERVO::CEFEM*)theApp.m_model.getMaster().getEquipment(EQ_ID_EFEM);
diff --git a/SourceCode/Bond/Servo/TopToolbar.cpp b/SourceCode/Bond/Servo/TopToolbar.cpp
index 32c9d8a..a601129 100644
--- a/SourceCode/Bond/Servo/TopToolbar.cpp
+++ b/SourceCode/Bond/Servo/TopToolbar.cpp
@@ -30,6 +30,7 @@
 	DDX_Control(pDX, IDC_BUTTON_STOP, m_btnStop);
 	DDX_Control(pDX, IDC_BUTTON_ALARM, m_btnAlarm);
 	DDX_Control(pDX, IDC_BUTTON_SETTINGS, m_btnSettings);
+	DDX_Control(pDX, IDC_BUTTON_PORT_CONFIG, m_btnPortConfig);
 	DDX_Control(pDX, IDC_BUTTON_ROBOT, m_btnRobot);
 	DDX_Control(pDX, IDC_BUTTON_OPERATOR, m_btnOperator);
 }
@@ -55,6 +56,7 @@
 	InitBtn(m_btnAlarm, "Alarm_o_32.ico", "Alarm_gray_32.ico");
 	InitBtn(m_btnSettings, "Settings_High_32.ico", "Settings_Gray_32.ico");
 	InitBtn(m_btnRobot, "Robot_High_32.ico", "Robot_Gray_32.ico");
+	InitBtn(m_btnPortConfig, "PortConfig_High_32.ico", "PortConfig_Gray_32.ico");
 	InitBtn(m_btnOperator, "Operator_High_32.ico", "Operator_Gray_32.ico");
 	HMENU hMenu = LoadMenu(AfxGetInstanceHandle(), MAKEINTRESOURCEA(IDR_MENU_OPEATOR));
 	m_btnOperator.SetMenu(hMenu);
@@ -133,6 +135,11 @@
 	x += BTN_WIDTH;
 	x += 2;
 
+	pItem = GetDlgItem(IDC_BUTTON_PORT_CONFIG);
+	pItem->MoveWindow(x, y, BTN_WIDTH, nBthHeight);
+	x += BTN_WIDTH;
+	x += 2;
+
 	pItem = GetDlgItem(IDC_BUTTON_ROBOT);
 	pItem->MoveWindow(x, y, BTN_WIDTH, nBthHeight);
 	x += BTN_WIDTH;
@@ -186,6 +193,7 @@
 	case IDC_BUTTON_STOP:
 	case IDC_BUTTON_ALARM:
 	case IDC_BUTTON_SETTINGS:
+	case IDC_BUTTON_PORT_CONFIG:
 	case IDC_BUTTON_ROBOT:
 		GetParent()->SendMessage(ID_MSG_TOOLBAR_BTN_CLICKED, 0, LOWORD(wParam));
 		break;
diff --git a/SourceCode/Bond/Servo/TopToolbar.h b/SourceCode/Bond/Servo/TopToolbar.h
index b65ad85..e4e0408 100644
--- a/SourceCode/Bond/Servo/TopToolbar.h
+++ b/SourceCode/Bond/Servo/TopToolbar.h
@@ -34,6 +34,7 @@
 	CBlButton m_btnStop;
 	CBlButton m_btnAlarm;
 	CBlButton m_btnSettings;
+	CBlButton m_btnPortConfig;
 	CBlButton m_btnRobot;
 	CBlButton m_btnOperator;
 
diff --git a/SourceCode/Bond/Servo/resource.h b/SourceCode/Bond/Servo/resource.h
index e2b5983..e5eb250 100644
--- a/SourceCode/Bond/Servo/resource.h
+++ b/SourceCode/Bond/Servo/resource.h
Binary files differ

--
Gitblit v1.9.3