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; } } 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); 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 ""; } 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) { DDX_Control(pDX, IDC_GRID_PANEL_RECIPE, m_wndGrid); CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_GRID_PANEL_RECIPE, m_wndGrid); 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: 在此添加额外的初始化 // 初始化端口下拉框内容 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]); // 默认加载第一个端口的配置 // 设置对话框标题 SetWindowText(_T("Port Configuration")); return TRUE; // return TRUE unless you set the focus to a control // 异常: OCX 属性页应返回 FALSE } void CPortConfigurationDlg::OnSelchangeComboPort() { // TODO: 在此添加控件通知处理程序代码 int selPort = m_comboPort.GetCurSel(); if (selPort < 0 || selPort >= 4) { return; // 无效选择 } // 加载选中端口的配置到 UI LoadPortConfigToUI(m_pPort[selPort]); } void CPortConfigurationDlg::OnBnClickedButtonApply() { // TODO: 在此添加控件通知处理程序代码 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("未知的端口名称!")); 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(); } 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; }; 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 } 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; }; 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 + "';"); 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); SourceCode/Bond/Servo/Servo.rcBinary files differ
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 /* 放片 */ 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); 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; 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; SourceCode/Bond/Servo/resource.hBinary files differ