| | |
| | | #include "RecipeManager.h" |
| | | #include "ServoCommo.h" |
| | | |
| | | |
| | | |
| | | #define CHECKBOX_ALL_ID 0x1234 |
| | | |
| | | // CPortConfigurationDlg 对话框 |
| | |
| | | return; |
| | | } |
| | | |
| | | CStringArray permissions; |
| | | permissions.Add(_T("G1")); |
| | | permissions.Add(_T("G2")); |
| | | |
| | | SetDlgItemText(IDC_EDIT_LOTID, ""); |
| | | SetDlgItemText(IDC_EDIT_PRODUCTID, ""); |
| | | SetDlgItemText(IDC_EDIT_OPERATIONID, ""); |
| | |
| | | continue; |
| | | } |
| | | |
| | | |
| | | // 设置 Panel ID 和勾选框 |
| | | SERVO::CGlass* pGlass = dynamic_cast<SERVO::CGlass*>(pSlot->getContext()); |
| | | SERVO::CJobDataS* pJobDataS = pGlass->getJobDataS(); |
| | | int nRow = i + 1; |
| | | if (pGlass != nullptr) { |
| | | 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)); |
| | |
| | | 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) { |
| | | 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; |
| | | } |
| | | } |
| | |
| | | return; |
| | | } |
| | | |
| | | const int nCols = 2; |
| | | const int nCols = 3; |
| | | const int nFixRows = 1; |
| | | const int nRows = SLOT_MAX + 1; // 存在表头,所以 +1 |
| | | |
| | |
| | | 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.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); |
| | |
| | | m_comboMaterialsType.AddString(item); |
| | | } |
| | | m_comboMaterialsType.SetCurSel(0); // 默认选择第一个物料类型 |
| | | |
| | | m_comboMaterialsType.EnableWindow(FALSE); |
| | | InitGrid(); |
| | | |
| | | LoadPortConfigToUI(m_pPort[0]); // 默认加载第一个端口的配置 |
| | |
| | | else { |
| | | SetWindowText(_T("Port Configuration")); |
| | | } |
| | | |
| | | |
| | | // Porcess Start / Process Cancel 按钮状态 |
| | | GetDlgItem(IDC_BUTTON_PROCESS_START)->EnableWindow(FALSE); |
| | |
| | | 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; |
| | |
| | | // 获取 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)); |
| | | ASSERT(pCheck); |
| | | 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)); |
| | | pGlass->setType(static_cast<SERVO::MaterialsType>(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(config.nMaterialType); |
| | | 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); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | { |
| | | 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); |
| | | if (selPort < 0 || selPort >= 4) { |
| | | LOGE("ProcessStart invalid port index: %d", selPort); |
| | | return; |
| | | } |
| | | |
| | | SERVO::CLoadPort* pPort = m_pPort[selPort]; |
| | | if (pPort == nullptr) { |
| | | LOGE("ProcessStart port pointer is null, index: %d", selPort); |
| | | 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; |
| | | |
| | | // 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) return; |
| | | m_pPort[selPort]->sendCassetteCtrlCmd(CCC_PROCESS_CANCEL, nullptr, 0, 0, 0, nullptr, nullptr); |
| | | 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); |
| | | } |
| | | } |