LAPTOP-SNT8I5JK\Boounion
2025-09-17 4ef35bf238fc6f7217e4b6de4aee37192ec503ec
SourceCode/Bond/Servo/PortConfigurationDlg.cpp
@@ -8,7 +8,10 @@
#include "NewCellTypes/GridCellCheck.h"
#include "NewCellTypes/GridCellCombo.h"
#include "NewCellTypes/GridCellNumeric.h"
#include "RecipeManager.h"
#include "ServoCommo.h"
#define CHECKBOX_ALL_ID        0x1234
// CPortConfigurationDlg 对话框
@@ -17,11 +20,111 @@
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) {
                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()
@@ -32,7 +135,7 @@
    const int nCols = 3;
    const int nFixRows = 1;
    const int nRows = 9;
   const int nRows = SLOT_MAX + 1; // 存在表头,所以 +1
    int nColIdx = 0;
    m_wndGrid.DeleteAllItems();
@@ -53,19 +156,18 @@
    // 设置列宽
    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("启用"));
    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);
@@ -82,53 +184,35 @@
        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);
        // 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));
        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)
{
    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)
    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()
@@ -139,8 +223,229 @@
   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);
    }
    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());
    }
    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;
   //}
    // 获取 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>(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);
            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);
                }
            }
        }
    }
    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);
}