mrDarker
2025-06-21 90c8673e4edaf2e68931470b4d480e02d4634fd0
1. 动态生成设备配方绑定界面的控件
2. 完善本地测试界面
已修改11个文件
286 ■■■■■ 文件已修改
SourceCode/Bond/Servo/CLoadPort.cpp 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CLoadPort.h 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/PortConfigurationDlg.cpp 145 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/PortConfigurationDlg.h 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/RecipeDeviceBindDlg.cpp 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/RecipeDeviceBindDlg.h 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/RecipeManager.cpp 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/RecipeManager.h 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Servo.rc 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ServoCommo.h 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/resource.h 补丁 | 查看 | 原始文档 | blame | 历史
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/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 对话框
@@ -24,13 +26,22 @@
{
}
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::InitGrid()
{
    if (m_wndGrid.GetSafeHwnd() == NULL) {
        return;
    }
    const int nCols = 3;
    const int nCols = 2;
    const int nFixRows = 1;
    const int nRows = 9;
@@ -53,8 +64,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);
@@ -98,20 +107,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 +121,16 @@
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_BN_CLICKED(IDC_BUTTON_APPLY, &CPortConfigurationDlg::OnBnClickedButtonApply)
END_MESSAGE_MAP()
@@ -139,8 +141,117 @@
    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();
    return TRUE;  // return TRUE unless you set the focus to a control
    // 异常: OCX 属性页应返回 FALSE
}
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
@@ -20,11 +20,16 @@
protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持
    virtual BOOL OnInitDialog();
    afx_msg void OnBnClickedButtonApply();
    DECLARE_MESSAGE_MAP()
private:
    CGridCtrl m_wndGrid;
    int GetLoadPortEqID(const std::string& strPortName);
    void InitGrid();
    void FillGrid();
    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.rc
Binary 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/resource.h
Binary files differ