From 155cb7fe0dcb564729c6aecdb65815f3f0ed24e2 Mon Sep 17 00:00:00 2001
From: chenluhua1980 <Chenluhua@qq.com>
Date: 星期二, 13 一月 2026 11:28:26 +0800
Subject: [PATCH] 1.ECID, DVID的查询和实现;

---
 SourceCode/Bond/Servo/HsmsPassive.h              |   18 +
 SourceCode/Bond/EAPSimulator/CHsmsActive.h       |    2 
 SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.h   |    2 
 SourceCode/Bond/Servo/HsmsPassive.cpp            |  234 +++++++++++++++++++++++++----
 SourceCode/Bond/EAPSimulator/EAPSimulator.rc     |   26 +-
 SourceCode/Bond/Servo/Model.cpp                  |   14 -
 SourceCode/Bond/x64/Debug/VariableList.txt       |   65 +++----
 SourceCode/Bond/Servo/CPageGlassList.cpp         |    2 
 SourceCode/Bond/EAPSimulator/CHsmsActive.cpp     |   27 +++
 SourceCode/Bond/EAPSimulator/Resource.h          |    4 
 SourceCode/Bond/x64/Debug/DataVariableList.txt   |    3 
 SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.cpp |   59 +++++++
 SourceCode/Bond/Servo/ClientListDlg.cpp          |    2 
 13 files changed, 360 insertions(+), 98 deletions(-)

diff --git a/SourceCode/Bond/EAPSimulator/CHsmsActive.cpp b/SourceCode/Bond/EAPSimulator/CHsmsActive.cpp
index 6bd6013..a2539da 100644
--- a/SourceCode/Bond/EAPSimulator/CHsmsActive.cpp
+++ b/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;
diff --git a/SourceCode/Bond/EAPSimulator/CHsmsActive.h b/SourceCode/Bond/EAPSimulator/CHsmsActive.h
index 24d1016..9e8eb07 100644
--- a/SourceCode/Bond/EAPSimulator/CHsmsActive.h
+++ b/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();
diff --git a/SourceCode/Bond/EAPSimulator/EAPSimulator.rc b/SourceCode/Bond/EAPSimulator/EAPSimulator.rc
index fb5266e..92e768e 100644
--- a/SourceCode/Bond/EAPSimulator/EAPSimulator.rc
+++ b/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
 
diff --git a/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.cpp b/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.cpp
index 5ec0665..a52341f 100644
--- a/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.cpp
+++ b/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;
diff --git a/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.h b/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.h
index 2865c6b..d1e5537 100644
--- a/SourceCode/Bond/EAPSimulator/EAPSimulatorDlg.h
+++ b/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();
 };
diff --git a/SourceCode/Bond/EAPSimulator/Resource.h b/SourceCode/Bond/EAPSimulator/Resource.h
index dee05ce..5c4789f 100644
--- a/SourceCode/Bond/EAPSimulator/Resource.h
+++ b/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
 // 
diff --git a/SourceCode/Bond/Servo/CPageGlassList.cpp b/SourceCode/Bond/Servo/CPageGlassList.cpp
index 68e2470..801a337 100644
--- a/SourceCode/Bond/Servo/CPageGlassList.cpp
+++ b/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;
diff --git a/SourceCode/Bond/Servo/ClientListDlg.cpp b/SourceCode/Bond/Servo/ClientListDlg.cpp
index 500062e..1e8cd71 100644
--- a/SourceCode/Bond/Servo/ClientListDlg.cpp
+++ b/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("姝e父") : _T("寮傚父"));
 		m_listClients.SetItemText(nItem, 3, CString(client.status.c_str()));
diff --git a/SourceCode/Bond/Servo/HsmsPassive.cpp b/SourceCode/Bond/Servo/HsmsPassive.cpp
index 91f7254..df2aaeb 100644
--- a/SourceCode/Bond/Servo/HsmsPassive.cpp
+++ b/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,33 +2528,38 @@
 	}
 
 
-	// 瑕佽缃殑甯搁噺琛ㄨ〃
-	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;
-				if (pItemEqc->getSubItemU2(0, eqcid)
-					&& pItemEqc->getSubItemString(1, pszValue)) {
-					eqc.id = eqcid;
-					strcpy_s(eqc.szValue, EQCONSTANT_VALUE_MAX, pszValue);
-					eqcs.push_back(eqc);
-				}
+			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;
+				strcpy_s(eqc.szValue, EQCONSTANT_VALUE_MAX, pszValue);
+				eqcs.push_back(eqc);
 			}
 		}
 	}
 
-
-	// 浜ょ敱涓婂眰搴旂敤鏉ヤ繚瀛樺拰璁剧疆鏈哄櫒甯搁噺鍊�
-	std::vector<unsigned int> ecvs;
-	if (m_listener.onEQConstantSend != nullptr) {
-		m_listener.onEQConstantSend(this, eqcs);
+	// 鏇存柊鍐呭瓨琛ㄥ苟钀界洏
+	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;
+		}
+	}
+	if (changed && !m_strEquipmentConstantFilepath.empty()) {
+		writeEquipmentConstantsToFile(m_strEquipmentConstantFilepath);
 	}
 
 
diff --git a/SourceCode/Bond/Servo/HsmsPassive.h b/SourceCode/Bond/Servo/HsmsPassive.h
index 938a4ec..24815d7 100644
--- a/SourceCode/Bond/Servo/HsmsPassive.h
+++ b/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;
diff --git a/SourceCode/Bond/Servo/Model.cpp b/SourceCode/Bond/Servo/Model.cpp
index 0c1c457..f571def 100644
--- a/SourceCode/Bond/Servo/Model.cpp
+++ b/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);
diff --git a/SourceCode/Bond/x64/Debug/DataVariableList.txt b/SourceCode/Bond/x64/Debug/DataVariableList.txt
index 0e29924..a874466 100644
--- a/SourceCode/Bond/x64/Debug/DataVariableList.txt
+++ b/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)
diff --git a/SourceCode/Bond/x64/Debug/VariableList.txt b/SourceCode/Bond/x64/Debug/VariableList.txt
index 6ceaf95..71f7fbc 100644
--- a/SourceCode/Bond/x64/Debug/VariableList.txt
+++ b/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)

--
Gitblit v1.9.3