已修改14个文件
460 ■■■■■ 文件已修改
SourceCode/Bond/Servo/CControlJob.cpp 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CControlJob.h 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CControlJobDlg.cpp 143 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CControlJobDlg.h 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CGlass.cpp 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CGlass.h 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CLoadPort.cpp 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CMaster.cpp 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CMaster.h 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ProcessJob.cpp 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ProcessJob.h 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Servo.rc 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ServoDlg.cpp 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/resource.h 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CControlJob.cpp
@@ -301,4 +301,36 @@
        return true;
    }
    std::string CControlJob::getStateText()
    {
        switch (m_state)
        {
        case SERVO::CJState::NoState:
            return "NoState";
            break;
        case SERVO::CJState::Queued:
            return "Queued";
            break;
        case SERVO::CJState::Executing:
            return "Executing";
            break;
        case SERVO::CJState::Paused:
            return "Paused";
            break;
        case SERVO::CJState::Completed:
            return "Completed";
            break;
        case SERVO::CJState::Aborted:
            return "Aborted";
            break;
        case SERVO::CJState::Failed:
            return "Failed";
            break;
        default:
            break;
        }
        return "";
    }
}
SourceCode/Bond/Servo/CControlJob.h
@@ -45,6 +45,7 @@
        CJState            state()  const noexcept { return m_state; }
        uint8_t            priority() const noexcept { return m_priority; }
        void               setPriority(uint8_t p) noexcept { m_priority = p; }
        std::string getStateText();
        // —— PJ 列表维护(去重)—— //
        bool addPJ(const std::string& pjId);                // 已存在则不重复添加
@@ -54,6 +55,7 @@
        const std::vector<std::string>& pjIds() const noexcept { return m_pjIds; }
        bool setPJs(const std::vector<CProcessJob*>& pjs);
        void clearPJs() { m_pjIds.clear(); }
        const std::vector<CProcessJob*>& getPjs() { return m_pjs; };
        // —— 校验 —— //
        struct ValidationIssue { uint32_t code; std::string text; };
SourceCode/Bond/Servo/CControlJobDlg.cpp
@@ -14,7 +14,7 @@
CControlJobDlg::CControlJobDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_DIALOG_CONTROL_JOB, pParent)
{
    m_pControlJob = nullptr;
}
CControlJobDlg::~CControlJobDlg()
@@ -29,40 +29,141 @@
BEGIN_MESSAGE_MAP(CControlJobDlg, CDialogEx)
    ON_WM_SIZE()
END_MESSAGE_MAP()
void CControlJobDlg::SetControlJob(SERVO::CControlJob* pControlJob)
{
    m_pControlJob = pControlJob;
}
// CControlJobDlg 消息处理程序
BOOL CControlJobDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    // label字体
    LOGFONT lf{};
    GetFont()->GetLogFont(&lf);
    lf.lfHeight = -20;
    lf.lfWeight = FW_BOLD;
    _tcscpy_s(lf.lfFaceName, _T("Arial"));
    m_fontNoJob.CreateFontIndirect(&lf);
    GetDlgItem(IDC_LABEL_NO_JOB)->SetFont(&m_fontNoJob);
    // 列表控件
    HIMAGELIST imageList = ImageList_Create(24, 24, ILC_COLOR24, 1, 1);
    ListView_SetImageList(m_listCtrl.GetSafeHwnd(), imageList, LVSIL_SMALL);
    // m_list 已经是对话框上的 CExpandableListCtrl 成员(拖控件改类)
    m_listCtrl.ModifyStyle(0, LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS);
    m_listCtrl.InsertColumn(0, _T("名称"), LVCFMT_LEFT, 260);
    m_listCtrl.InsertColumn(1, _T("状态"), LVCFMT_LEFT, 120);
    m_listCtrl.InsertColumn(2, _T("描述"), LVCFMT_LEFT, 260);
    m_listCtrl.InsertColumn(0, _T("ID"), LVCFMT_LEFT, 180);
    m_listCtrl.InsertColumn(1, _T("类型"), LVCFMT_LEFT, 120);
    m_listCtrl.InsertColumn(2, _T("状态"), LVCFMT_LEFT, 120);
    m_listCtrl.InsertColumn(3, _T("配方"), LVCFMT_LEFT, 120);
    m_listCtrl.InsertColumn(4, _T("Port / Carrier / Slot"), LVCFMT_LEFT, 180);
    m_listCtrl.InsertColumn(5, _T("描述"), LVCFMT_LEFT, 220);
    auto* root1 = m_listCtrl.InsertRoot({ _T("EFEM"), _T("Ready"), _T("Front End Module") });
    m_listCtrl.InsertChild(root1, { _T("Slot #1"), _T("OK"), _T("150mm wafer") });
    m_listCtrl.InsertChild(root1, { _T("Slot #2"), _T("Empty"), _T("") });
    auto* root2 = m_listCtrl.InsertRoot({ _T("Bonder"), _T("Run"), _T("G1+G2 Process") });
    auto* ch21 = m_listCtrl.InsertChild(root2, { _T("Job A"), _T("Proc"), _T("Step 1") });
    m_listCtrl.InsertChild(ch21, { _T("SubStep A1"), _T("Done"), _T("Align") });
    m_listCtrl.InsertChild(ch21, { _T("SubStep A2"), _T("Run"),  _T("Bond") });
    // 初始让顶层展开
    root1->expanded = true;
    root2->expanded = true;
    m_listCtrl.RebuildVisible();
    // 控件状态
    Resize();
    ShowGroup1(m_pControlJob == nullptr);
    ShowGroup2(m_pControlJob != nullptr);
    LoadData();
    return TRUE;  // return TRUE unless you set the focus to a control
                  // 异常: OCX 属性页应返回 FALSE
}
void CControlJobDlg::OnSize(UINT nType, int cx, int cy)
{
    CDialogEx::OnSize(nType, cx, cy);
    if (GetDlgItem(IDC_LIST1) == nullptr) return;
    Resize();
}
void CControlJobDlg::Resize()
{
    CRect rcClient, rcItem;
    CWnd* pItem;
    GetClientRect(rcClient);
    // 关闭按钮
    int y = rcClient.bottom - 12;
    pItem = GetDlgItem(IDCANCEL);
    pItem->GetClientRect(&rcItem);
    pItem->MoveWindow(rcClient.right - 12 - rcItem.Width(),
        y - rcItem.Height(),
        rcItem.Width(), rcItem.Height());
    y -= rcItem.Height();
    y -= 12;
    // 线
    pItem = GetDlgItem(IDC_LINE1);
    pItem->MoveWindow(12, y, rcClient.Width() - 24, 2);
    y -= 2;
    // Label
    pItem = GetDlgItem(IDC_LABEL_NO_JOB);
    pItem->GetClientRect(&rcItem);
    pItem->MoveWindow((rcClient.Width() - rcItem.Width()) / 2,
        (y - rcItem.Height()) / 2,
        rcItem.Width(), rcItem.Height());
    // ListCtrl
    pItem = GetDlgItem(IDC_LIST1);
    pItem->MoveWindow(12, 12, rcClient.Width() - 24, y - 12);
}
void CControlJobDlg::ShowGroup1(BOOL bShow)
{
    GetDlgItem(IDC_LABEL_NO_JOB)->ShowWindow(bShow ? SW_SHOW : SW_HIDE);
    GetDlgItem(IDC_LINE1)->ShowWindow(bShow ? SW_SHOW : SW_HIDE);
}
void CControlJobDlg::ShowGroup2(BOOL bShow)
{
    GetDlgItem(IDC_LIST1)->ShowWindow(bShow ? SW_SHOW : SW_HIDE);
}
void CControlJobDlg::LoadData()
{
    m_listCtrl.DeleteAllItems();
    if (m_pControlJob != nullptr) {
        auto* root1 = m_listCtrl.InsertRoot({ m_pControlJob->id().c_str(), _T("ControlJob"),
            m_pControlJob->getStateText().c_str(), _T("") });
        auto pjs = m_pControlJob->getPjs();
        for (auto pj : pjs) {
            auto* root2 = m_listCtrl.InsertChild(root1, {pj->id().c_str(),  _T("ProcessJob"),
                pj->getStateText().c_str(), pj->recipeSpec().c_str(), _T(""), _T(""), _T("") });
            auto cs = pj->carriers();
            for (auto c : cs) {
                for (auto g : c.contexts) {
                    SERVO::CGlass* pGlass = (SERVO::CGlass*)g;
                    if (pGlass != nullptr) {
                        int port, slot;
                        pGlass->getOrginPort(port, slot);
                        std::string carrier = c.carrierId + " / Port" + std::to_string(port + 1) + " / Slot" + std::to_string(slot + 1);
                        m_listCtrl.InsertChild(root2, { pGlass->getID().c_str(), _T("Glass"),
                            pGlass->getStateText().c_str(), _T(""), carrier.c_str(), _T("") });
                    }
                    else {
                        m_listCtrl.InsertChild(root2, { "Null", _T("Glass"), _T(""), _T(""), c.carrierId.c_str(), _T("") });
                    }
                }
            }
            root2->expanded = true;
        }
        root1->expanded = true;
        m_listCtrl.RebuildVisible();
    }
}
SourceCode/Bond/Servo/CControlJobDlg.h
@@ -1,5 +1,6 @@
#pragma once
#include "CExpandableListCtrl.h"
#include "CControlJob.h"
// CControlJobDlg 对话框
@@ -11,6 +12,19 @@
public:
    CControlJobDlg(CWnd* pParent = nullptr);   // 标准构造函数
    virtual ~CControlJobDlg();
public:
    void SetControlJob(SERVO::CControlJob* pControlJob);
private:
    void Resize();
    void ShowGroup1(BOOL bShow);
    void ShowGroup2(BOOL bShow);
    void LoadData();
private:
    SERVO::CControlJob* m_pControlJob;
    CFont m_fontNoJob;
protected:
    CExpandableListCtrl m_listCtrl;
@@ -27,4 +41,5 @@
    DECLARE_MESSAGE_MAP()
public:
    virtual BOOL OnInitDialog();
    afx_msg void OnSize(UINT nType, int cx, int cy);
};
SourceCode/Bond/Servo/CGlass.cpp
@@ -11,6 +11,7 @@
        m_nOriginPort = 0;
        m_nOriginSlot = 0;
        m_bScheduledForProcessing = FALSE;
        m_pProcessJob = nullptr;
    }
    CGlass::~CGlass()
@@ -89,6 +90,16 @@
    void CGlass::setScheduledForProcessing(BOOL bProcessing)
    {
        m_bScheduledForProcessing = bProcessing;
    }
    CProcessJob* CGlass::getProcessJob()
    {
        return m_pProcessJob;
    }
    void CGlass::setProcessJob(CProcessJob* pProcessJob)
    {
        m_pProcessJob = pProcessJob;
    }
    CPath* CGlass::getPath()
@@ -246,4 +257,116 @@
        return pPath->getInspResult();
    }
    std::string CGlass::getStateText()
    {
        switch (m_state)
        {
        case SERVO::GlsState::NoState:
            return "NoState";
            break;
        case SERVO::GlsState::Queued:
            return "Queued";
            break;
        case SERVO::GlsState::InProcess:
            return "InProcess";
            break;
        case SERVO::GlsState::Paused:
            return "Queued";
            break;
        case SERVO::GlsState::Completed:
            return "Queued";
            break;
        case SERVO::GlsState::Aborted:
            return "Aborted";
            break;
        case SERVO::GlsState::Failed:
            return "Failed";
            break;
        default:
            break;
        }
        return "";
    }
    bool CGlass::queue() {
        if (m_state != GlsState::NoState) return false;
        markQueued();
        return true;
    }
    bool CGlass::start() {
        if (m_state != GlsState::Queued && m_state != GlsState::Paused)
            return false;
        if (!m_tStart.has_value()) markStart();
        m_state = GlsState::InProcess;
        return true;
    }
    bool CGlass::pause() {
        if (m_state != GlsState::InProcess) return false;
        m_state = GlsState::Paused;
        return true;
    }
    bool CGlass::resume() {
        if (m_state != GlsState::Paused) return false;
        m_state = GlsState::InProcess;
        return true;
    }
    bool CGlass::complete() {
        if (m_state != GlsState::InProcess && m_state != GlsState::Paused) return false;
        m_state = GlsState::Completed;
        markEnd();
        return true;
    }
    bool CGlass::abort() {
        if (m_state == GlsState::Completed || m_state == GlsState::Aborted || m_state == GlsState::Failed)
            return false;
        m_state = GlsState::Aborted;
        markEnd();
        return true;
    }
    bool CGlass::fail(std::string reason)
    {
        m_failReason = trimCopy(reason);
        clampString(m_failReason, 128);
        m_state = GlsState::Failed;
        markEnd();
        return true;
    }
    std::string CGlass::trimCopy(std::string s)
    {
        auto notspace = [](int ch) { return !std::isspace(ch); };
        s.erase(s.begin(), std::find_if(s.begin(), s.end(), notspace));
        s.erase(std::find_if(s.rbegin(), s.rend(), notspace).base(), s.end());
        return s;
    }
    void CGlass::clampString(std::string& s, size_t maxLen)
    {
        if (s.size() > maxLen) s.resize(maxLen);
    }
    // —— 时间戳 & 工具 ——
    void CGlass::markQueued()
    {
        m_state = GlsState::Queued;
        m_tQueued = std::chrono::system_clock::now();
    }
    void CGlass::markStart()
    {
        m_tStart = std::chrono::system_clock::now();
    }
    void CGlass::markEnd()
    {
        m_tEnd = std::chrono::system_clock::now();
    }
}
SourceCode/Bond/Servo/CGlass.h
@@ -7,9 +7,21 @@
#include "CJobDataC.h"
#include "CJobDataS.h"
#include "ServoCommo.h"
#include "ProcessJob.h"
namespace SERVO {
    /// PJ 生命周期(贴近 E40 常见状态)
    enum class GlsState : uint8_t {
        NoState = 0,
        Queued,
        InProcess,
        Paused,
        Completed,
        Aborted,
        Failed
    };
    class CGlass : public CContext
    {
    public:
@@ -28,6 +40,8 @@
        void getOrginPort(int& port, int& slot);
        BOOL isScheduledForProcessing();
        void setScheduledForProcessing(BOOL bProcessing);
        CProcessJob* getProcessJob();
        void setProcessJob(CProcessJob* pProcessJob);
        CPath* getPathWithEq(unsigned int nEqId, unsigned int nUnit);
        CPath* getPath();
        void addPath(unsigned int nEqId, unsigned int nUnit);
@@ -44,6 +58,37 @@
        int setInspResult(unsigned int nEqId, unsigned int nUnit, InspResult result);
        InspResult getInspResult(unsigned int nEqId, unsigned int nUnit);
    public:
        // 新增状态
        GlsState state() const noexcept { return m_state; }
        std::string getStateText();
        GlsState m_state{ GlsState::NoState };
        static void clampString(std::string& s, size_t maxLen);
        static std::string trimCopy(std::string s);
        std::string m_failReason;
        // —— 状态机(带守卫)——
        bool queue();           // NoState -> Queued
        bool start();           // Queued -> InProcess
        bool pause();           // InProcess -> Paused
        bool resume();          // Paused -> InProcess
        bool complete();        // InProcess -> Completed
        bool abort();           // Any (未终态) -> Aborted
        bool fail(std::string reason); // 任意态 -> Failed(记录失败原因)
        // 时间戳
        std::optional<std::chrono::system_clock::time_point> m_tQueued;
        std::optional<std::chrono::system_clock::time_point> m_tStart;
        std::optional<std::chrono::system_clock::time_point> m_tEnd;
        // 时间戳(可用于报表/追溯)
        std::optional<std::chrono::system_clock::time_point> tQueued() const { return m_tQueued; }
        std::optional<std::chrono::system_clock::time_point> tStart()  const { return m_tStart; }
        std::optional<std::chrono::system_clock::time_point> tEnd()    const { return m_tEnd; }
        void markQueued();
        void markStart();
        void markEnd();
    private:
        MaterialsType m_type;
        std::string m_strID;
@@ -54,6 +99,7 @@
        int m_nOriginPort;
        int m_nOriginSlot;
        BOOL m_bScheduledForProcessing;            /* 是否将加工处理 */
        CProcessJob* m_pProcessJob;
    };
}
SourceCode/Bond/Servo/CLoadPort.cpp
@@ -385,7 +385,7 @@
                CStep* pStep = getStepWithName(STEP_EQ_PORT2_INUSE);
                CPortStatusReport portStatusReport;
                portStatusReport.setPortStatus(PORT_INUSE);
                portStatusReport.setJobExistenceSlot(0xf);
                portStatusReport.setJobExistenceSlot(0xff );
                portStatusReport.setCassetteId("CID1004");
                int nRet = portStatusReport.serialize(szBuffer, 64);
                decodePortStatusReport(pStep, szBuffer, 64);
SourceCode/Bond/Servo/CMaster.cpp
@@ -1143,6 +1143,25 @@
        };
        listener.onPortStatusChanged = [&](void* pEquipment, short status, __int64 data) {
            LOGE("<Master-%s>onPortStatusChanged。status=%d, data=%lld", ((CEquipment*)pEquipment)->getName().c_str(), status);
            if (status == PORT_INUSE && m_pControlJob != nullptr) {
                CLoadPort* pPort = (CLoadPort*)pEquipment;
                auto pjs = m_pControlJob->getPjs();
                for (auto pj : pjs) {
                    auto carrier = pj->getCarrier(pPort->getCassetteId());
                    if (carrier != nullptr) {
                        for (auto slot : carrier->slots) {
                            CGlass* pGlass = pPort->getGlassFromSlot(slot);
                            carrier->contexts.push_back((void*)pGlass);
                            if (pGlass != nullptr) {
                                pGlass->setProcessJob(pj);
                            }
                        }
                    }
                }
            }
            if (m_listener.onLoadPortStatusChanged != nullptr) {
                m_listener.onLoadPortStatusChanged(this, (CEquipment*)pEquipment, status, data);
            }
@@ -1868,7 +1887,7 @@
        m_processJobs = temp;
        this->saveState();
        return m_processJobs.size();
        return (int)m_processJobs.size();
    }
    std::vector<CProcessJob*>& CMaster::getProcessJobs()
@@ -1927,6 +1946,11 @@
        return 0;
    }
    CControlJob* CMaster::getControlJob()
    {
        return m_pControlJob;
    }
    CLoadPort* CMaster::getPortWithCarrierId(const std::string& carrierId) const
@@ -2047,6 +2071,19 @@
            m_processJobs.push_back(pProcessJob);
        }
        // 找到CProcessJob指针加入列表中
        std::vector<CProcessJob*> tempPjs;
        auto ids = m_pControlJob->pjIds();
        for (auto id : ids) {
            auto pj = getProcessJob(id);
            if (pj != nullptr) {
                tempPjs.push_back(pj);
            }
        }
        m_pControlJob->setPJs(tempPjs);
        // 如果版本升级,可在这里判断 version 来加载新字段
SourceCode/Bond/Servo/CMaster.h
@@ -110,6 +110,7 @@
        std::vector<CProcessJob*>& getProcessJobs();
        CProcessJob* getProcessJob(const std::string& id);
        int setControlJob(CControlJob& controlJob);
        CControlJob* getControlJob();
        CLoadPort* getPortWithCarrierId(const std::string& carrierId) const;
        bool saveState() const;
        bool loadState(const std::string& path);
SourceCode/Bond/Servo/ProcessJob.cpp
@@ -381,4 +381,53 @@
        return true;
    }
    std::string CProcessJob::getStateText()
    {
        switch (m_state)
        {
        case SERVO::PJState::NoState:
            return "NoState";
            break;
        case SERVO::PJState::Queued:
            return "Queued";
            break;
        case SERVO::PJState::SettingUp:
            return "SettingUp";
            break;
        case SERVO::PJState::InProcess:
            return "InProcess";
            break;
        case SERVO::PJState::Paused:
            return "Queued";
            break;
        case SERVO::PJState::Aborting:
            return "Aborting";
            break;
        case SERVO::PJState::Completed:
            return "Queued";
            break;
        case SERVO::PJState::Aborted:
            return "Aborted";
            break;
        case SERVO::PJState::Failed:
            return "Failed";
            break;
        default:
            break;
        }
        return "";
    }
    CarrierSlotInfo* CProcessJob::getCarrier(std::string& strId)
    {
        for (auto& item : m_carriers) {
            if (item.carrierId.compare(strId) == 0) {
                return &item;
            }
        }
        return nullptr;
    }
}
SourceCode/Bond/Servo/ProcessJob.h
@@ -49,8 +49,9 @@
    }
     */
    struct CarrierSlotInfo {
        std::string carrierId;              // CARRIERID
        std::vector<uint8_t> slots;        // SLOTID[]
        std::string carrierId;          // CARRIERID
        std::vector<uint8_t> slots;     // SLOTID[]
        std::vector<void*> contexts;    // Glass
    };
    /// 简单资源视图接口:供 Validate() 查询(由设备端实现者在外部提供)
@@ -94,6 +95,7 @@
        StartPolicy startPolicy() const noexcept { return m_startPolicy; }
        RecipeMethod recipeMethod() const noexcept { return m_recipeMethod; }
        const std::string& recipeSpec() const noexcept { return m_recipeSpec; } // PPID 或 Spec
        std::string getStateText();
        // 绑定父 CJ
        void setParentCjId(std::string cjId);
@@ -153,6 +155,7 @@
        // 访问器
        const std::vector<CarrierSlotInfo>& carriers() const noexcept { return m_carriers; }
        CarrierSlotInfo* getCarrier(std::string& strId);
        // 判定是否“按载具/卡位”方式
        bool usesCarrierSlots() const noexcept { return !m_carriers.empty(); }
SourceCode/Bond/Servo/Servo.rc
Binary files differ
SourceCode/Bond/Servo/ServoDlg.cpp
@@ -981,6 +981,7 @@
    }
    else if (id == IDC_BUTTON_JOBS) {
        CControlJobDlg dlg;
        dlg.SetControlJob(theApp.m_model.m_master.getControlJob());
        dlg.DoModal();
    }
    else if (id == IDC_BUTTON_PORT_CONFIG) {
SourceCode/Bond/Servo/resource.h
Binary files differ