chenluhua1980
2026-01-13 155cb7fe0dcb564729c6aecdb65815f3f0ed24e2
1.ECID, DVID的查询和实现;
已修改13个文件
448 ■■■■ 文件已修改
SourceCode/Bond/EAPSimulator/CHsmsActive.cpp 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/EAPSimulator/CHsmsActive.h 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/EAPSimulator/EAPSimulator.rc 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.cpp 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.h 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/EAPSimulator/Resource.h 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/CPageGlassList.cpp 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/ClientListDlg.cpp 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/HsmsPassive.cpp 224 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/HsmsPassive.h 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/Servo/Model.cpp 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/x64/Debug/DataVariableList.txt 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/x64/Debug/VariableList.txt 65 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SourceCode/Bond/EAPSimulator/CHsmsActive.cpp
@@ -395,6 +395,33 @@
    return 0;
}
int CHsmsActive::hsmsEquipmentConstantRequest(const std::vector<unsigned short>& ecids)
{
    IMessage* pMessage = nullptr;
    int nRet = HSMS_Create1Message(pMessage, m_nSessionId, 2 | REPLY, 13, ++m_nSystemByte);
    for (auto id : ecids) {
        pMessage->getBody()->addU2Item(id, "ECID");
    }
    m_pActive->sendMessage(pMessage);
    HSMS_Destroy1Message(pMessage);
    return 0;
}
int CHsmsActive::hsmsEquipmentConstantSend(const std::vector<std::pair<unsigned short, std::string>>& ecidValues)
{
    IMessage* pMessage = nullptr;
    int nRet = HSMS_Create1Message(pMessage, m_nSessionId, 2 | REPLY, 15, ++m_nSystemByte);
    ISECS2Item* pBody = pMessage->getBody();
    for (const auto& kv : ecidValues) {
        ISECS2Item* pEntry = pBody->addItem();
        pEntry->addU2Item(kv.first, "ECID");
        pEntry->addItem(kv.second.c_str(), "ECV");
    }
    m_pActive->sendMessage(pMessage);
    HSMS_Destroy1Message(pMessage);
    return 0;
}
int CHsmsActive::hsmsQueryPPIDList()
{
    IMessage* pMessage = nullptr;
SourceCode/Bond/EAPSimulator/CHsmsActive.h
@@ -89,6 +89,8 @@
    int hsmsQueryAllStatusVariables();      // S1F11
    int hsmsQueryAllDataVariables();        // S1F21
    int hsmsQueryAllCollectionEvents();     // S1F23
    int hsmsEquipmentConstantRequest(const std::vector<unsigned short>& ecids); // S2F13
    int hsmsEquipmentConstantSend(const std::vector<std::pair<unsigned short, std::string>>& ecidValues); // S2F15
    // 查询PPID List
    int hsmsQueryPPIDList();
SourceCode/Bond/EAPSimulator/EAPSimulator.rc
@@ -54,10 +54,10 @@
    "\r\n"
    "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)\r\n"
    "LANGUAGE 4, 2\r\n"
    "#include ""res\\EAPSimulator.rc2""  // 闂?Microsoft Visual C++ 缂傛牞绶惃鍕カ濠?\n"
    "#include ""l.CHS\\afxres.rc""      // 閺嶅洤鍣紒鍕\r\n"
    "#include ""res\\EAPSimulator.rc2""  // 闂?Microsoft Visual C++ 缂傚倹鐗炵欢顐︽儍閸曨喚銈繝?\n"
    "#include ""l.CHS\\afxres.rc""      // 闁哄秴娲ら崳顖滅磼閸曨亝顐?\n"
    "#if !defined(_AFXDLL)\r\n"
    "#include ""l.CHS\\afxribbon.rc""   // MFC 閸旂喕鍏橀崠鍝勬嫲閹貉冨煑閺壜ょカ濠?\n"
    "#include ""l.CHS\\afxribbon.rc""   // MFC 闁告梻鍠曢崗姗€宕犻崫鍕闁硅矇鍐ㄧ厬闁哄銈囥偒婵?\n"
    "#endif\r\n"
    "#endif\r\n"
    "\0"
@@ -92,17 +92,17 @@
    DEFPUSHBUTTON   "确定",IDOK,113,41,50,14,WS_GROUP
END
IDD_EAPSIMULATOR_DIALOG DIALOGEX 0, 0, 469, 292
IDD_EAPSIMULATOR_DIALOG DIALOGEX 0, 0, 469, 319
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
EXSTYLE WS_EX_APPWINDOW
CAPTION "EAPSimulator"
FONT 9, "MS Shell Dlg", 0, 0, 0x1
BEGIN
    DEFPUSHBUTTON   "确定",IDOK,246,271,50,14,NOT WS_VISIBLE
    PUSHBUTTON      "取消",IDCANCEL,412,271,50,14,NOT WS_VISIBLE
    EDITTEXT        IDC_EDIT_LOG,7,218,455,67,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL
    LTEXT           "日志:",IDC_STATIC,7,203,24,8
    GROUPBOX        "功能",IDC_STATIC,7,7,455,190
    DEFPUSHBUTTON   "确定",IDOK,246,298,50,14,NOT WS_VISIBLE
    PUSHBUTTON      "取消",IDCANCEL,412,298,50,14,NOT WS_VISIBLE
    EDITTEXT        IDC_EDIT_LOG,7,245,455,67,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL
    LTEXT           "日志:",IDC_STATIC,7,234,24,8
    GROUPBOX        "功能",IDC_STATIC,7,7,455,223
    LTEXT           "IP:",IDC_STATIC,24,20,11,8
    EDITTEXT        IDC_EDIT_IP,39,17,94,14,ES_AUTOHSCROLL
    LTEXT           "Port:",IDC_STATIC,141,20,17,8
@@ -299,7 +299,7 @@
        LEFTMARGIN, 7
        RIGHTMARGIN, 462
        TOPMARGIN, 7
        BOTTOMMARGIN, 285
        BOTTOMMARGIN, 312
    END
    IDD_DIALOG_TERMINAL_DISPLAY, DIALOG
@@ -442,10 +442,10 @@
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE 4, 2
#include "res\EAPSimulator.rc2"  // 非 Microsoft Visual C++ 编辑的资源
#include "l.CHS\afxres.rc"      // 标准组件
#include "res\EAPSimulator.rc2"  // 闂?Microsoft Visual C++ 缂傛牞绶惃鍕カ濠?
#include "l.CHS\afxres.rc"      // 閺嶅洤鍣紒鍕
#if !defined(_AFXDLL)
#include "l.CHS\afxribbon.rc"   // MFC 功能区和控制条资源
#include "l.CHS\afxribbon.rc"   // MFC 閸旂喕鍏橀崠鍝勬嫲閹貉冨煑閺壜ょカ濠?
#endif
#endif
SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.cpp
@@ -107,6 +107,8 @@
    ON_BN_CLICKED(IDC_BUTTON_QUERY_ALL_SVID, &CEAPSimulatorDlg::OnBnClickedButtonQueryAllSvid)
    ON_BN_CLICKED(IDC_BUTTON_QUERY_ALL_DVID, &CEAPSimulatorDlg::OnBnClickedButtonQueryAllDvid)
    ON_BN_CLICKED(IDC_BUTTON_QUERY_ALL_CEID, &CEAPSimulatorDlg::OnBnClickedButtonQueryAllCeid)
    ON_BN_CLICKED(IDC_BUTTON_QUERY_ALL_ECID, &CEAPSimulatorDlg::OnBnClickedButtonQueryAllEcid)
    ON_BN_CLICKED(IDC_BUTTON_SET_ECID, &CEAPSimulatorDlg::OnBnClickedButtonSetEcid)
    ON_BN_CLICKED(IDC_BUTTON_QUERY_CURRENT_RECIPE, &CEAPSimulatorDlg::OnBnClickedButtonQueryCurrentRecipe)
    ON_BN_CLICKED(IDC_BUTTON_PP_REQUEST, &CEAPSimulatorDlg::OnBnClickedButtonPpRequest)
END_MESSAGE_MAP()
@@ -290,6 +292,41 @@
        if (hBtn != nullptr) {
            ::SendMessage(hBtn, WM_SETFONT, (WPARAM)GetFont()->GetSafeHandle(), TRUE);
        }
    }
    // S2F13 QueryAllECID
    {
        CRect rc(14 + 2 * (140 + 5), 192, 14 + 2 * (140 + 5) + 140, 192 + 14);
        MapDialogRect(&rc);
        HWND hBtn = ::CreateWindow(_T("BUTTON"), _T("S2F13_QueryAllECID"),
            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
            rc.left, rc.top, rc.Width(), rc.Height(),
            m_hWnd, (HMENU)IDC_BUTTON_QUERY_ALL_ECID, AfxGetInstanceHandle(), nullptr);
        if (hBtn != nullptr) {
            ::SendMessage(hBtn, WM_SETFONT, (WPARAM)GetFont()->GetSafeHandle(), TRUE);
        }
    }
    // ECID edit + send (S2F15)
    {
        CRect rcEcid(14, 192, 14 + 60, 192 + 14);
        CRect rcEcv(14 + 60 + 4, 192, 14 + 60 + 4 + 60, 192 + 14);
        MapDialogRect(&rcEcid);
        MapDialogRect(&rcEcv);
        HWND hEditEcid = ::CreateWindow(_T("EDIT"), _T(""), WS_CHILD | WS_VISIBLE | WS_BORDER,
            rcEcid.left, rcEcid.top, rcEcid.Width(), rcEcid.Height(),
            m_hWnd, (HMENU)IDC_EDIT_ECID, AfxGetInstanceHandle(), nullptr);
        HWND hEditEcv = ::CreateWindow(_T("EDIT"), _T(""), WS_CHILD | WS_VISIBLE | WS_BORDER,
            rcEcv.left, rcEcv.top, rcEcv.Width(), rcEcv.Height(),
            m_hWnd, (HMENU)IDC_EDIT_ECV, AfxGetInstanceHandle(), nullptr);
        if (hEditEcid) ::SendMessage(hEditEcid, WM_SETFONT, (WPARAM)GetFont()->GetSafeHandle(), TRUE);
        if (hEditEcv) ::SendMessage(hEditEcv, WM_SETFONT, (WPARAM)GetFont()->GetSafeHandle(), TRUE);
        CRect rcBtn(14 + 60 + 4 + 60 + 4, 192, 14 + 60 + 4 + 60 + 4 + 90, 192 + 14);
        MapDialogRect(&rcBtn);
        HWND hBtn = ::CreateWindow(_T("BUTTON"), _T("S2F15_SetECID"),
            WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
            rcBtn.left, rcBtn.top, rcBtn.Width(), rcBtn.Height(),
            m_hWnd, (HMENU)IDC_BUTTON_SET_ECID, AfxGetInstanceHandle(), nullptr);
        if (hBtn) ::SendMessage(hBtn, WM_SETFONT, (WPARAM)GetFont()->GetSafeHandle(), TRUE);
    }
    // S1F3 CurrentRecipe (EQ specific) combo + button
    {
@@ -675,6 +712,28 @@
    theApp.m_model.m_pHsmsActive->hsmsQueryAllCollectionEvents();
}
void CEAPSimulatorDlg::OnBnClickedButtonQueryAllEcid()
{
    // empty list => all ECID
    std::vector<unsigned short> ecids;
    ecids.push_back(2000);
    theApp.m_model.m_pHsmsActive->hsmsEquipmentConstantRequest(ecids);
}
void CEAPSimulatorDlg::OnBnClickedButtonSetEcid()
{
    // simple demo: read ECID and value from edit boxes (reuse PPID edit)
    CString sEcid, sVal;
    GetDlgItemText(IDC_EDIT_ECID, sEcid);
    GetDlgItemText(IDC_EDIT_ECV, sVal);
    unsigned short id = static_cast<unsigned short>(_ttoi(sEcid));
    std::string val = CT2A(sVal);
    std::vector<std::pair<unsigned short, std::string>> kvs;
    if (id != 0) {
        kvs.push_back({ id, val });
        theApp.m_model.m_pHsmsActive->hsmsEquipmentConstantSend(kvs);
    }
}
void CEAPSimulatorDlg::OnBnClickedButtonQueryCurrentRecipe()
{
    CString sel;
SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.h
@@ -73,6 +73,8 @@
    afx_msg void OnBnClickedButtonQueryAllSvid();
    afx_msg void OnBnClickedButtonQueryAllDvid();
    afx_msg void OnBnClickedButtonQueryAllCeid();
    afx_msg void OnBnClickedButtonQueryAllEcid();
    afx_msg void OnBnClickedButtonSetEcid();
    afx_msg void OnBnClickedButtonQueryCurrentRecipe();
    afx_msg void OnBnClickedButtonPpRequest();
};
SourceCode/Bond/EAPSimulator/Resource.h
@@ -72,6 +72,10 @@
#define IDC_EDIT_PPID_REQ               1054
#define IDC_BUTTON_PP_REQUEST           1055
#define IDC_BUTTON_QUERY_ALL_DVID       1056
#define IDC_BUTTON_QUERY_ALL_ECID       1057
#define IDC_EDIT_ECID                   1058
#define IDC_EDIT_ECV                    1059
#define IDC_BUTTON_SET_ECID             1060
// Next default values for new objects
// 
SourceCode/Bond/Servo/CPageGlassList.cpp
@@ -1364,7 +1364,7 @@
    if (!row.pretty.empty()) {
        CFile file;
        if (file.Open(filePath, CFile::modeCreate | CFile::modeWrite)) {
            file.Write(row.pretty.c_str(), row.pretty.length());
            file.Write(row.pretty.c_str(), (UINT)row.pretty.length());
            file.Close();
            CString strSuccess;
SourceCode/Bond/Servo/ClientListDlg.cpp
@@ -132,7 +132,7 @@
    {
        const ClientInfo& client = clients[i];
        
        int nItem = m_listClients.InsertItem(i, CString(client.ip.c_str()));
        int nItem = m_listClients.InsertItem((int)i, CString(client.ip.c_str()));
        m_listClients.SetItemText(nItem, 1, CString(std::to_string(client.port).c_str()));
        m_listClients.SetItemText(nItem, 2, client.versionOk ? _T("正常") : _T("异常"));
        m_listClients.SetItemText(nItem, 3, CString(client.status.c_str()));
SourceCode/Bond/Servo/HsmsPassive.cpp
@@ -107,8 +107,6 @@
    m_listener.onEQOffLine = nullptr;
    m_listener.onEQOnLine = nullptr;
    m_listener.onCommand = nullptr;
    m_listener.onEQConstantRequest = nullptr;
    m_listener.onEQConstantSend = nullptr;
    m_pActiveAction = nullptr;
    InitializeCriticalSection(&m_criticalSection);
}
@@ -258,6 +256,9 @@
    SERVO::CReport* pReport = new SERVO::CReport(RPTID, vids);
    for (auto vid : vids) {
        SERVO::CVariable* pVariable = getVariable(vid);
        if (pVariable == nullptr) {
            pVariable = getDataVariable(vid);
        }
        if (pVariable != nullptr) {
            pReport->addVariable(pVariable);
            LOGI("<CHsmsPassive>defineReport RPTID=%d", RPTID);
@@ -557,6 +558,95 @@
    return 0;
}
int CHsmsPassive::loadEquipmentConstants(const char* pszFilepath)
{
    if (pszFilepath == NULL) return -1;
    m_strEquipmentConstantFilepath = pszFilepath;
    m_bEquipmentConstantUtf8 = false;
    m_bEquipmentConstantUtf8Bom = false;
    CFile file;
    if (!file.Open(pszFilepath, CFile::modeRead | CFile::shareDenyNone)) {
        return -1;
    }
    const ULONGLONG nLen = file.GetLength();
    if (nLen == 0) {
        return -1;
    }
    std::string buffer;
    buffer.resize(static_cast<size_t>(nLen));
    file.Read(buffer.data(), static_cast<UINT>(nLen));
    file.Close();
    if (hasUtf8Bom(buffer)) {
        m_bEquipmentConstantUtf8 = true;
        m_bEquipmentConstantUtf8Bom = true;
        buffer = buffer.substr(3);
    }
    else if (isLikelyUtf8(buffer)) {
        m_bEquipmentConstantUtf8 = true;
    }
    CStringW content = m_bEquipmentConstantUtf8 ? Utf8ToWide(buffer.c_str()) : AnsiToWide(buffer.c_str());
    if (content.IsEmpty()) return -1;
    std::wregex pattern(L"^\\d+,[^,]*,[^,]*,([^,]*),.*");
    std::vector<EquipmentConstantEntry> constants;
    CStringW strLine, strId, strName, strFormat, strRemark, strDefault;
    std::wstringstream ss(content.GetString());
    auto narrowFromW = [](const CStringW& s) -> std::string {
        int need = WideCharToMultiByte(CP_ACP, 0, s, -1, nullptr, 0, nullptr, nullptr);
        if (need <= 0) return {};
        std::string out(static_cast<size_t>(need - 1), '\0');
        WideCharToMultiByte(CP_ACP, 0, s, -1, out.data(), need, nullptr, nullptr);
        return out;
    };
    std::wstring line;
    while (std::getline(ss, line, L'\n')) {
        strLine = line.c_str();
        strLine.Trim();
        if (strLine.IsEmpty()) continue;
        if (strLine.Find(L"ECID") == 0) continue; // skip header
        if (!std::regex_match(static_cast<LPCWSTR>(strLine), pattern)) {
            continue;
        }
        int last = 0;
        int idx = strLine.Find(L",", last);
        if (idx < 0) continue;
        strId = strLine.Left(idx);
        last = idx + 1;
        idx = strLine.Find(L",", last);
        if (idx < 0) continue;
        strName = strLine.Mid(last, idx - last);
        last = idx + 1;
        idx = strLine.Find(L",", last);
        if (idx < 0) continue;
        strFormat = strLine.Mid(last, idx - last);
        last = idx + 1;
        idx = strLine.Find(L",", last);
        if (idx < 0) continue;
        strRemark = strLine.Mid(last, idx - last);
        last = idx + 1;
        strDefault = strLine.Right(strLine.GetLength() - last);
        EquipmentConstantEntry entry;
        entry.id = _wtoi(strId);
        entry.name = narrowFromW(strName);
        entry.format = narrowFromW(strFormat);
        entry.remark = narrowFromW(strRemark);
        entry.value = narrowFromW(strDefault);
        constants.push_back(entry);
    }
    if (!constants.empty()) {
        m_equipmentConstants = std::move(constants);
    }
    return 0;
}
std::vector<SERVO::CVariable*>& CHsmsPassive::getVariables()
{
    return m_variabels;
@@ -714,6 +804,9 @@
    if (auto v = getVariable(pszName)) {
        v->setValue(value);
    }
    else if (auto dv = getDataVariable(pszName)) {
        dv->setValue(value);
    }
    Unlock();
}
@@ -723,6 +816,9 @@
    if (auto v = getVariable(pszName)) {
        v->setValue(value);
    }
    else if (auto dv = getDataVariable(pszName)) {
        dv->setValue(value);
    }
    Unlock();
}
@@ -731,6 +827,9 @@
    Lock();
    if (auto v = getVariable(pszName)) {
        v->setValue(vars);
    }
    else if (auto dv = getDataVariable(pszName)) {
        dv->setValue(vars);
    }
    Unlock();
}
@@ -1006,6 +1105,9 @@
        SERVO::CReport* pReport = new SERVO::CReport(_wtoi(strId), vids);
        for (auto vid : vids) {
            SERVO::CVariable* pVariable = getVariable(vid);
            if (pVariable == nullptr) {
                pVariable = getDataVariable(vid);
            }
            if (pVariable != nullptr) {
                pReport->addVariable(pVariable);
            }
@@ -1084,6 +1186,9 @@
    SERVO::CReport* pReport = new SERVO::CReport(rptid, vids);
    for (auto vid : vids) {
        SERVO::CVariable* pVariable = getVariable((int)vid);
        if (pVariable == nullptr) {
            pVariable = getDataVariable((int)vid);
        }
        if (pVariable != nullptr) {
            pReport->addVariable(pVariable);
        }
@@ -1100,6 +1205,9 @@
            SERVO::CReport* pReport = new SERVO::CReport(rptid, vids);
            for (auto vid : vids) {
                SERVO::CVariable* pVariable = getVariable((int)vid);
                if (pVariable == nullptr) {
                    pVariable = getDataVariable((int)vid);
                }
                if (pVariable != nullptr) {
                    pReport->addVariable(pVariable);
                }
@@ -2160,6 +2268,49 @@
    return ER_NOERROR;
}
int CHsmsPassive::writeEquipmentConstantsToFile(const std::string& filepath)
{
    if (filepath.empty()) return -1;
    CFile file;
    if (!file.Open(filepath.c_str(), CFile::modeCreate | CFile::modeWrite | CFile::shareDenyNone)) {
        return -1;
    }
    const std::string headerAnsi = "ECID,EC Name,EC Format,EC Remark,Default Value\r\n";
    if (m_bEquipmentConstantUtf8) {
        if (m_bEquipmentConstantUtf8Bom) {
            const BYTE bom[3] = { 0xEF, 0xBB, 0xBF };
            file.Write(bom, 3);
        }
        CStringA header = AnsiToUtf8(headerAnsi);
        file.Write(header.GetString(), header.GetLength());
    }
    else {
        file.Write(headerAnsi.data(), (UINT)headerAnsi.size());
    }
    for (const auto& e : m_equipmentConstants) {
        std::string line;
        line.reserve(128);
        line += std::to_string(e.id);
        line.push_back(',');
        line += e.name;
        line.push_back(',');
        line += e.format;
        line.push_back(',');
        line += e.remark;
        line.push_back(',');
        line += e.value;
        line.append("\r\n");
        if (m_bEquipmentConstantUtf8) {
            CStringA out = AnsiToUtf8(line);
            file.Write(out.GetString(), out.GetLength());
        }
        else {
            file.Write(line.data(), (UINT)line.size());
        }
    }
    file.Close();
    return 0;
}
// S1F21/S1F22 - Data Variable Namelist
int CHsmsPassive::replyDataVariableNamelistRequest(IMessage* pRecv)
{
@@ -2318,32 +2469,42 @@
    }
    // 要获取的常量表表
    BOOL bCheckData = FALSE;
    std::vector<EQConstant> eqcs;
    {
        ISECS2Item* pItem = pRecv->getBody();
        int ecidSize = pItem->getSubItemSize();
        const int ecidSize = pItem ? pItem->getSubItemSize() : 0;
        for (int i = 0; i < ecidSize; i++) {
            EQConstant eqc;
            unsigned short id;
            if (pItem->getSubItemU2(i, id)) {
            EQConstant eqc{};
            unsigned short id = 0;
            if (pItem && pItem->getSubItemU2(i, id)) {
                eqc.id = id;
                eqcs.push_back(eqc);
            }
        }
    }
    // 交由上层应用来获取机器常量值
    if (m_listener.onEQConstantRequest != nullptr) {
        m_listener.onEQConstantRequest(this, eqcs);
    // 空列表表示请求全部 ECID
    if (eqcs.empty()) {
        for (const auto& e : m_equipmentConstants) {
            EQConstant eqc{};
            eqc.id = e.id;
            strcpy_s(eqc.szValue, EQCONSTANT_VALUE_MAX, e.value.c_str());
            eqcs.push_back(eqc);
    }
    } else {
        for (auto& item : eqcs) {
            auto it = std::find_if(m_equipmentConstants.begin(), m_equipmentConstants.end(),
                [&](const EquipmentConstantEntry& e) { return e.id == item.id; });
            if (it != m_equipmentConstants.end()) {
                strcpy_s(item.szValue, EQCONSTANT_VALUE_MAX, it->value.c_str());
            } else {
                item.szValue[0] = '\0'; // unknown -> empty
            }
        }
    }
    // 回复
    IMessage* pMessage = NULL;
    HSMS_Create1Message(pMessage, m_nSessionId, 1, 14, pRecv->getHeader()->systemBytes);
    HSMS_Create1Message(pMessage, m_nSessionId, 2, 14, pRecv->getHeader()->systemBytes);
    ASSERT(pMessage);
    ISECS2Item* pItem = pMessage->getBody();
    for (auto& item : eqcs) {
@@ -2367,18 +2528,17 @@
    }
    // 要设置的常量表表
    BOOL bCheckData = FALSE;
    // 要设置的常量表
    std::vector<EQConstant> eqcs;
    {
        ISECS2Item* pItem = pRecv->getBody();
        int ecidSize = pItem->getSubItemSize();
        int ecidSize = pItem ? pItem->getSubItemSize() : 0;
        for (int i = 0; i < ecidSize; i++) {
            ISECS2Item* pItemEqc = pItem->getSubItem(i);
            if (pItemEqc != nullptr) {
                EQConstant eqc;
                unsigned short eqcid;
                const char* pszValue;
            ISECS2Item* pItemEqc = pItem ? pItem->getSubItem(i) : nullptr;
            if (pItemEqc == nullptr) continue;
            EQConstant eqc{};
            unsigned short eqcid = 0;
            const char* pszValue = nullptr;
                if (pItemEqc->getSubItemU2(0, eqcid)
                    && pItemEqc->getSubItemString(1, pszValue)) {
                    eqc.id = eqcid;
@@ -2387,13 +2547,19 @@
                }
            }
        }
    // 更新内存表并落盘
    bool changed = false;
    for (auto& item : eqcs) {
        auto it = std::find_if(m_equipmentConstants.begin(), m_equipmentConstants.end(),
            [&](const EquipmentConstantEntry& e) { return e.id == item.id; });
        if (it != m_equipmentConstants.end()) {
            it->value = item.szValue;
            changed = true;
    }
    // 交由上层应用来保存和设置机器常量值
    std::vector<unsigned int> ecvs;
    if (m_listener.onEQConstantSend != nullptr) {
        m_listener.onEQConstantSend(this, eqcs);
    }
    if (changed && !m_strEquipmentConstantFilepath.empty()) {
        writeEquipmentConstantsToFile(m_strEquipmentConstantFilepath);
    }
SourceCode/Bond/Servo/HsmsPassive.h
@@ -79,7 +79,6 @@
typedef std::function<void(void* pFrom)> SECSEQOFFLINE;
typedef std::function<void(void* pFrom, std::vector<EQConstant>&)> SECSEQCONSTANTREQUEST;
typedef std::function<void(void* pFrom, const char*, std::vector<CommandParameter>&)> SECSCommand;
typedef std::function<void(void* pFrom, SYSTEMTIME& time)> DATETIMESYNC;
typedef std::function<void(void* pFrom, bool bEnable, std::vector<unsigned int>& ids)> EDEVENTREPORT;
@@ -98,8 +97,6 @@
{
    SECSEQOFFLINE                onEQOffLine;
    SECSEQOFFLINE                onEQOnLine;
    SECSEQCONSTANTREQUEST        onEQConstantRequest;
    SECSEQCONSTANTREQUEST        onEQConstantSend;
    SECSCommand                    onCommand;
    DATETIMESYNC                onDatetimeSync;
    EDEVENTREPORT                onEnableDisableEventReport;
@@ -149,6 +146,8 @@
    int loadVarialbles(const char* pszFilepath);
    // 从文件中加载CDataVariable列表
    int loadDataVarialbles(const char* pszFilepath);
    // 从文件中加载Equipment Constant列表
    int loadEquipmentConstants(const char* pszFilepath);
    // 取得CVariable列表
    std::vector<SERVO::CVariable*>& getVariables();
@@ -276,6 +275,7 @@
    std::vector<unsigned int> parseVidList(CString& strNums);
    int writeVariablesToFile(const std::string& filepath);
    int writeDataVariablesToFile(const std::string& filepath);
    int writeEquipmentConstantsToFile(const std::string& filepath);
    int writeReportsToFile(const std::string& filepath);
    int writeCollectionEventsToFile(const std::string& filepath);
@@ -307,6 +307,9 @@
    std::string m_strCollectionEventFilepath;
    bool m_bCollectionUtf8{ false };
    bool m_bCollectionUtf8Bom{ false };
    std::string m_strEquipmentConstantFilepath;
    bool m_bEquipmentConstantUtf8{ false };
    bool m_bEquipmentConstantUtf8Bom{ false };
    BOOL m_bCimWorking;
    HANDLE m_hCimWorkEvent;
    HANDLE m_hCimWorkThreadHandle;
@@ -319,6 +322,15 @@
    std::vector<SERVO::CVariable*> m_variabels;
    // CDataVariable vector
    std::vector<SERVO::CDataVariable*> m_dataVariabels;
    // Equipment constants
    struct EquipmentConstantEntry {
        unsigned int id{ 0 };
        std::string name;
        std::string format;
        std::string remark;
        std::string value;
    };
    std::vector<EquipmentConstantEntry> m_equipmentConstants;
    // CReport vector
    std::vector<SERVO::CReport*> m_reports;
SourceCode/Bond/Servo/Model.cpp
@@ -168,18 +168,6 @@
            setControlState(ControlState::OnlineRemote);
        }
    };
    listener.onEQConstantRequest = [&](void* pFrom, std::vector<EQConstant>& eqcs) -> void {
        // 在此填充常量值,目前仅是加1后返回
        for (auto& item : eqcs) {
            sprintf_s(item.szValue, EQCONSTANT_VALUE_MAX, "Test%d", item.id + 1);
        }
    };
    listener.onEQConstantSend = [&](void* pFrom, std::vector<EQConstant>& eqcs) -> void {
        // 在此保存和设置机器常量值
        for (auto& item : eqcs) {
            LOGI("onEQConstantRequest: %d, %s", item.id, item.szValue);
        }
    };
    listener.onDatetimeSync = [&](void* pFrom, SYSTEMTIME& time) -> void {
        LOGI("onDatetimeSync: %d%02d%02d%02d%02d%02d", time.wYear,
            time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond);
@@ -313,6 +301,8 @@
    m_hsmsPassive.loadVarialbles((LPTSTR)(LPCTSTR)strVarialbleFile);
    strVarialbleFile.Format(_T("%s\\DataVariableList.txt"), (LPTSTR)(LPCTSTR)m_strWorkDir);
    m_hsmsPassive.loadDataVarialbles((LPTSTR)(LPCTSTR)strVarialbleFile);
    strVarialbleFile.Format(_T("%s\\EquipmentConstantList.txt"), (LPTSTR)(LPCTSTR)m_strWorkDir);
    m_hsmsPassive.loadEquipmentConstants((LPTSTR)(LPCTSTR)strVarialbleFile);
    setControlState(m_currentControlState);
    refreshDerivedSVs();
    m_hsmsPassive.init(this, "APP", 7000);
SourceCode/Bond/x64/Debug/DataVariableList.txt
@@ -93,3 +93,6 @@
6701,Measurement_PD_Time,A20,Measurement PD: 时间
6702,Measurement_PD_Value1,A20,Measurement PD: 测量值1
6703,Measurement_PD_Value2,A20,Measurement PD: 测量值2
10200,SlotMap,U2,SlotMap(Scan)
10201,SlotMapScan,U2,SlotMap(Scan)
10202,SlotMapDownload,U2,SlotMap(Download)
SourceCode/Bond/x64/Debug/VariableList.txt
@@ -1,39 +1,36 @@
SVID,SV Name,SV Format,SV Remark
100,PortTransferState,U1,0=OutOfService
300,AccessMode,U1,1=Manual
500,Clock,A50,
600,CurrentControlState,U1,0:Offline:equipment
601,PreviousControlState,U1,
700,CurrentProcessState,U1,0:DOWN
701,PreviousProcessState,U1,
800,EFEMPPExecName,A20,
801,EQPPExecName,A20,
8100,Bonder1CurrentRecipe,A50,
8101,Bonder2CurrentRecipe,A50,
8102,VacuumBakeCurrentRecipe,A50,
8103,BakeCoolingCurrentRecipe,A50,
8104,MeasurementCurrentRecipe,A50,
8105,EFEMCurrentRecipe,A50,
100,PortTransferState,U1,0=OutOfService;1=ReadyToLoad/LoadRequest;2=Loaded;3=InUse/LoadComplete;4=ReadyToUnload/UnloadRequest;5=Empty/UnloadComplete;6=TransferBlocked
300,AccessMode,U1,0=Auto;1=Manual
500,Clock,A50,Current timestamp string
600,CurrentControlState,U1,0=OfflineEquipment;1=OfflineAttempt;2=Online;3=OfflineHost;4=OnlineLocal;5=OnlineRemote
601,PreviousControlState,U1,Previous control state (same code set as CurrentControlState)
700,CurrentProcessState,U1,0=Ready;1=Processing;2=Complete;3=Error
701,PreviousProcessState,U1,Previous process state (0=Ready;1=Processing;2=Complete;3=Error)
800,EFEMPPExecName,A20,Current PPExec name from EFEM
801,EQPPExecName,A20,Current PPExec name from equipment
8100,Bonder1CurrentRecipe,A50,Current recipe for Bonder1
8101,Bonder2CurrentRecipe,A50,Current recipe for Bonder2
8102,VacuumBakeCurrentRecipe,A50,Current recipe for VacuumBake
8103,BakeCoolingCurrentRecipe,A50,Current recipe for BakeCooling
8104,MeasurementCurrentRecipe,A50,Current recipe for Measurement
8105,EFEMCurrentRecipe,A50,Current recipe for EFEM
5001,CJobSpace,U1,CJ Space
5002,PJobSpace,U1,PJ Space
5003,PJQueued,L,PJ Queued
5004,PJStartID,A20,PJStartID
5005,PJEndID,A20,PJEndID
5006,PanelStartID,A20,PanelStartID
5007,PanelEndID,A20,PanelEndID
5008,CJStartID,A20,CJStartID
5009,CJEndID,A20,CJEndID
5010,UnloadReadyPortId,U1,Port ID
5011,LoadReadyPortId,U1,Port ID
5012,BlockedPortId,U1,Port ID
5014,VCRPanelID,A20,Panel id, comes from reader
5015,ReadyToReleasePortId,U1,Port ID
5016,LoadPortNotAssocPortId,U1,Port ID
5017,ProcessDataReportText,A50,EV_PROCESS_DATA_REPORT payload string (placeholder)
5018,SubEqpName,A20,Equipment name for EV_SubEqpStart/EV_SubEqpEnd
5019,SubEqpSlot,U1,Slot number for EV_SubEqpStart/EV_SubEqpEnd
5003,PJQueued,L,PJ queued list (IDs)
5004,PJStartID,A20,PJ start ID
5005,PJEndID,A20,PJ end ID
5006,PanelStartID,A20,Panel start ID
5007,PanelEndID,A20,Panel end ID
5008,CJStartID,A20,CJ start ID
5009,CJEndID,A20,CJ end ID
5010,UnloadReadyPortId,U1,Port ID (ReadyToUnload)
5011,LoadReadyPortId,U1,Port ID (ReadyToLoad)
5012,BlockedPortId,U1,Port ID (TransferBlocked)
5014,VCRPanelID,A20,Panel ID from reader
5015,ReadyToReleasePortId,U1,Port ID (ReadyToRelease)
5016,LoadPortNotAssocPortId,U1,Port ID (LoadPortNotAssoc)
5017,ProcessDataReportText,A50,EV_PROCESS_DATA_REPORT payload (placeholder)
5018,SubEqpName,A20,Sub equipment name for SubEqp events
5019,SubEqpSlot,U1,Slot number for SubEqp events
5020,PortStateChangePortId,U1,Port ID for PortStateChange
5021,PortState,U1,Port transfer/state code for PortStateChange
10200,SlotMap,U2,SlotMap(Scan)
10201,SlotMapScan,U2,SlotMap(Scan)
10202,SlotMapDownload,U2,SlotMap(Download)