| | |
| | | #include "Servo.h" |
| | | #include "afxdialogex.h" |
| | | #include "PortConfigurationDlg.h" |
| | | #include "NewCellTypes/GridCellCheck.h" |
| | | #include "NewCellTypes/GridCellCombo.h" |
| | | #include "NewCellTypes/GridCellNumeric.h" |
| | | #include "RecipeManager.h" |
| | | #include "ServoCommo.h" |
| | | |
| | | |
| | | |
| | | #define CHECKBOX_ALL_ID 0x1234 |
| | | |
| | | // CPortConfigurationDlg 对话框 |
| | | |
| | |
| | | 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)); |
| | | 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) |
| | | { |
| | | 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; |
| | | } |
| | | |
| | | CStringArray permissions; |
| | | permissions.Add(_T("G1")); |
| | | permissions.Add(_T("G2")); |
| | | |
| | | 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; |
| | | } |
| | | |
| | | // 设置 Panel ID 和勾选框 |
| | | 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)); |
| | | } |
| | | } |
| | | m_wndGrid.SetItemData(nRow, 0, (LPARAM)pGlass); |
| | | |
| | | // 回填 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; |
| | | } |
| | | } |
| | | } |
| | | m_pCheckBox->SetCheck(IsCheckedAll() ? BST_CHECKED : BST_UNCHECKED); |
| | | } |
| | | |
| | | void CPortConfigurationDlg::InitGrid() |
| | | { |
| | | if (m_wndGrid.GetSafeHwnd() == NULL) { |
| | | return; |
| | | } |
| | | |
| | | const int nCols = 3; |
| | | const int nFixRows = 1; |
| | | const int nRows = SLOT_MAX + 1; // 存在表头,所以 +1 |
| | | |
| | | int nColIdx = 0; |
| | | m_wndGrid.DeleteAllItems(); |
| | | m_wndGrid.SetVirtualMode(FALSE); |
| | | |
| | | // 设置样式颜色 |
| | | m_wndGrid.GetDefaultCell(TRUE, FALSE)->SetBackClr(g_nGridFixCellColor); |
| | | m_wndGrid.GetDefaultCell(FALSE, TRUE)->SetBackClr(g_nGridFixCellColor); |
| | | m_wndGrid.GetDefaultCell(FALSE, FALSE)->SetBackClr(g_nGridCellColor); |
| | | m_wndGrid.SetFixedTextColor(g_nGridFixFontColor); |
| | | |
| | | // 行列数量 |
| | | m_wndGrid.SetRowCount(nRows); |
| | | m_wndGrid.SetColumnCount(nCols); |
| | | m_wndGrid.SetFixedRowCount(nFixRows); |
| | | m_wndGrid.SetFixedColumnCount(0); |
| | | |
| | | // 设置列宽 |
| | | m_wndGrid.SetColumnWidth(nColIdx, 50); |
| | | m_wndGrid.SetItemText(0, nColIdx++, _T("Slot ID")); |
| | | m_wndGrid.SetColumnWidth(nColIdx, 60); |
| | | 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(FALSE); |
| | | m_wndGrid.SetListMode(TRUE); |
| | | m_wndGrid.EnableSelection(TRUE); |
| | | m_wndGrid.SetSingleRowSelection(TRUE); |
| | | m_wndGrid.ExpandColumnsToFit(TRUE); |
| | | m_wndGrid.ExpandLastColumn(); |
| | | |
| | | // 自动计算并设置每行高度 |
| | | CRect rcClient; |
| | | m_wndGrid.GetClientRect(&rcClient); |
| | | int nAvailableHeight = rcClient.Height(); |
| | | int nEachRowHeight = max(24, nAvailableHeight / nRows); |
| | | |
| | | for (int i = 0; i < nRows; ++i) { |
| | | m_wndGrid.SetRowHeight(i, nEachRowHeight); |
| | | } |
| | | |
| | | 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) |
| | | { |
| | | CDialogEx::DoDataExchange(pDX); |
| | | 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) |
| | | 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() |
| | | |
| | | |
| | |
| | | CDialogEx::OnInitDialog(); |
| | | |
| | | // TODO: 在此添加额外的初始化 |
| | | // 绑定自定义控件(必须与资源中的 ID 一致) |
| | | m_wndGrid.SubclassDlgItem(IDC_GRID_PANEL_RECIPE, this); |
| | | |
| | | // 设置行列数 |
| | | m_wndGrid.SetRowCount(9); // 包括表头1行 + 数据8行 |
| | | m_wndGrid.SetColumnCount(4); |
| | | |
| | | // 设置固定的表头行 |
| | | m_wndGrid.SetFixedRowCount(1); |
| | | m_wndGrid.SetFixedColumnCount(0); |
| | | |
| | | // 设置列标题 |
| | | m_wndGrid.SetItemText(0, 0, _T("Slot ID")); |
| | | m_wndGrid.SetItemText(0, 1, _T("EQ Recipe")); |
| | | m_wndGrid.SetItemText(0, 2, _T("Panel ID")); |
| | | m_wndGrid.SetItemText(0, 3, _T("✓")); |
| | | |
| | | // 设置列宽 |
| | | m_wndGrid.SetColumnWidth(0, 60); |
| | | m_wndGrid.SetColumnWidth(1, 150); |
| | | m_wndGrid.SetColumnWidth(2, 150); |
| | | m_wndGrid.SetColumnWidth(3, 40); |
| | | |
| | | // 填充数据行 |
| | | for (int i = 1; i <= 8; ++i) |
| | | { |
| | | CString str; |
| | | str.Format(_T("%d"), i); |
| | | m_wndGrid.SetItemText(i, 0, str); |
| | | // 初始化端口下拉框内容 |
| | | CString ports[] = { _T("Port 1"), _T("Port 2"), _T("Port 3"), _T("Port 4") }; |
| | | for (const auto& item : ports) { |
| | | m_comboPort.AddString(item); |
| | | } |
| | | 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(); |
| | | 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); // 默认选择第一个物料类型 |
| | | m_comboMaterialsType.EnableWindow(FALSE); |
| | | InitGrid(); |
| | | |
| | | LoadPortConfigToUI(m_pPort[0]); // 默认加载第一个端口的配置 |
| | | |
| | | // 设置对话框标题 |
| | | 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 |
| | | } |
| | | |
| | | void CPortConfigurationDlg::OnSelchangeComboPort() |
| | | { |
| | | // TODO: 在此添加控件通知处理程序代码 |
| | | int selPort = m_comboPort.GetCurSel(); |
| | | if (selPort < 0 || selPort >= 4) { |
| | | return; // 无效选择 |
| | | } |
| | | |
| | | // 加载选中端口的配置到 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]); |
| | | } |
| | | |
| | | 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 <= 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>(config.nMaterialType));*/ |
| | | |
| | | 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); |
| | | |
| | | RecipeInfo stRecipeInfo = RecipeManager::getInstance().getRecipeByPPID(config.strRecipe); |
| | | std::vector<DeviceRecipe> vecRecipeInfo = stRecipeInfo.vecDeviceList; |
| | | |
| | | 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); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | 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) return; |
| | | m_pPort[selPort]->sendCassetteCtrlCmd(CCC_PROCESS_START, nullptr, 0, 0, 0, nullptr, nullptr); |
| | | } |
| | | |
| | | void CPortConfigurationDlg::OnBnClickedButtonProcessCancel() |
| | | { |
| | | int selPort = (0 <= m_nCurSelPort && m_nCurSelPort <= 3) ? m_nCurSelPort |
| | | : m_comboPort.GetCurSel(); |
| | | if (selPort < 0 || selPort >= 4) return; |
| | | m_pPort[selPort]->sendCassetteCtrlCmd(CCC_PROCESS_CANCEL, nullptr, 0, 0, 0, nullptr, nullptr); |
| | | } |