From bdf0dc4cb183d3fec40e1d51b3bb94b7e53d0a55 Mon Sep 17 00:00:00 2001
From: chenluhua1980 <Chenluhua@qq.com>
Date: 星期日, 04 一月 2026 10:45:07 +0800
Subject: [PATCH] 1.修复报文字段差异风险: S1F3:项目用 getSubItemU4(0) 取 SVID;日志里有 U2 的情况,可能导致解析失败后回退成 SV=0。 S6F11:项目发送 DATAID(U4) + CEID(U4);日志样例里第一个字段是 U2,可能存在类型/字段定义不一致。

---
 SourceCode/Bond/Servo/HsmsPassive.cpp |  763 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 716 insertions(+), 47 deletions(-)

diff --git a/SourceCode/Bond/Servo/HsmsPassive.cpp b/SourceCode/Bond/Servo/HsmsPassive.cpp
index 3304fa9..ae7ef22 100644
--- a/SourceCode/Bond/Servo/HsmsPassive.cpp
+++ b/SourceCode/Bond/Servo/HsmsPassive.cpp
@@ -8,6 +8,8 @@
 #include <time.h>  
 #include <stdlib.h>  
 #include <string.h>  
+#include <algorithm>
+#include <set>
 #include <regex>
 
 
@@ -228,40 +230,133 @@
 
 int CHsmsPassive::loadVarialbles(const char* pszFilepath)
 {
-	CStdioFile file;
-	if (!file.Open(pszFilepath, CFile::modeRead)) {
+	m_strVariableFilepath = pszFilepath;
+	m_bVariableUtf8 = false;
+	m_bVariableUtf8Bom = false;
+	// 鍏堣鍘熷瀛楄妭锛屽悗缁啀鎸� UTF-8/BOM 鎴栨湰鍦扮紪鐮佽浆鎹�
+	CFile file;
+	if (!file.Open(pszFilepath, CFile::modeRead | CFile::shareDenyNone)) {
 		return -1;
 	}
 
-	std::regex pattern("^\\d+,.*");  // 鍖归厤浠ユ暟瀛�+閫楀彿寮�澶寸殑瀛楃涓�
+	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();
+
+	const unsigned char* bytes = reinterpret_cast<const unsigned char*>(buffer.data());
+	size_t offset = 0;
+	CStringW content;
+
+	// UTF-8 BOM
+	if (nLen >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) {
+		offset = 3;
+		m_bVariableUtf8 = true;
+		m_bVariableUtf8Bom = true;
+	}
+
+	// UTF-16 LE BOM
+	if (nLen >= 2 && bytes[0] == 0xFF && bytes[1] == 0xFE) {
+		const wchar_t* wdata = reinterpret_cast<const wchar_t*>(buffer.data() + 2);
+		const size_t wlen = (nLen - 2) / sizeof(wchar_t);
+		content.SetString(wdata, static_cast<int>(wlen));
+	}
+	// UTF-16 BE BOM
+	else if (nLen >= 2 && bytes[0] == 0xFE && bytes[1] == 0xFF) {
+		const size_t wlen = (nLen - 2) / sizeof(wchar_t);
+		std::wstring temp;
+		temp.reserve(wlen);
+		for (size_t i = 0; i < wlen; ++i) {
+			wchar_t ch = static_cast<wchar_t>(bytes[2 + i * 2] << 8 | bytes[2 + i * 2 + 1]);
+			temp.push_back(ch);
+		}
+		content = temp.c_str();
+	}
+	// 灏濊瘯 UTF-8锛堝惈鏃� BOM锛�
+	else {
+		auto tryUtf8 = [&](size_t off) -> bool {
+			int need = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, buffer.data() + off,
+				static_cast<int>(buffer.size() - off), nullptr, 0);
+			if (need <= 0) return false;
+			std::wstring temp;
+			temp.resize(need);
+			MultiByteToWideChar(CP_UTF8, 0, buffer.data() + off,
+				static_cast<int>(buffer.size() - off), temp.data(), need);
+			content = temp.c_str();
+			m_bVariableUtf8 = true;
+			return true;
+		};
+
+		if (!tryUtf8(offset)) {
+			// 鍥為��鍒版湰鍦颁唬鐮侀〉
+			int need = MultiByteToWideChar(CP_ACP, 0, buffer.data(), static_cast<int>(buffer.size()), nullptr, 0);
+			if (need > 0) {
+				std::wstring temp;
+				temp.resize(need);
+				MultiByteToWideChar(CP_ACP, 0, buffer.data(), static_cast<int>(buffer.size()), temp.data(), need);
+				content = temp.c_str();
+			}
+		}
+	}
+
+	if (content.IsEmpty()) {
+		return -1;
+	}
+
+	std::wregex pattern(L"^\\d+,.+");  // 鍖归厤浠ユ暟瀛�+閫楀彿寮�澶寸殑瀛楃涓�
 	std::vector<SERVO::CVariable*> variables;
 	int index, last;
-	CString strLine;
-	CString strId, strName, strFormat, strRemark;
-	while (file.ReadString(strLine)) {
-		if (!std::regex_match((LPTSTR)(LPCTSTR)strLine, pattern)) {
+	CStringW strLine;
+	CStringW strId, strName, strFormat, strRemark;
+	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 (!std::regex_match(static_cast<LPCWSTR>(strLine), pattern)) {
 			continue;
 		}
 
 		last = 0;
-		index = strLine.Find(",", last);
+		index = strLine.Find(L",", last);
 		if (index < 0) continue;
 		strId = strLine.Left(index);
 		last = index + 1;
 
-		index = strLine.Find(",", last);
+		index = strLine.Find(L",", last);
 		if (index < 0) continue;
 		strName = strLine.Mid(last, index - last);
 		last = index + 1;
 
-		index = strLine.Find(",", last);
+		index = strLine.Find(L",", last);
 		if (index < 0) continue;
 		strFormat = strLine.Mid(last, index - last);
 		strRemark = strLine.Right(strLine.GetLength() - index - 1);
-		strRemark.Replace(_T("\\r\\n"), _T("\r\n"));
+		strRemark.Replace(L"\\r\\n", L"\r\n");
+
+		std::string sId = narrowFromW(strId);
+		std::string sName = narrowFromW(strName);
+		std::string sFormat = narrowFromW(strFormat);
+		std::string sRemark = narrowFromW(strRemark);
 
 		SERVO::CVariable* pVarialble = new SERVO::CVariable(
-			(LPTSTR)(LPCTSTR)strId, (LPTSTR)(LPCTSTR)strName, (LPTSTR)(LPCTSTR)strFormat, (LPTSTR)(LPCTSTR)strRemark);
+			sId.c_str(),
+			sName.c_str(),
+			sFormat.c_str(),
+			sRemark.c_str());
 		variables.push_back(pVarialble);
 	}
 
@@ -272,14 +367,23 @@
 		}
 	}
 
-
-	file.Close();
 	return 0;
 }
 
 std::vector<SERVO::CVariable*>& CHsmsPassive::getVariables()
 {
 	return m_variabels;
+}
+
+unsigned int CHsmsPassive::getMaxVariableId() const
+{
+	unsigned int maxId = 0;
+	for (auto item : m_variabels) {
+		if (item && item->getVarialbleId() > maxId) {
+			maxId = item->getVarialbleId();
+		}
+	}
+	return maxId;
 }
 
 SERVO::CVariable* CHsmsPassive::getVariable(int variableId)
@@ -312,6 +416,59 @@
 	m_variabels.clear();
 }
 
+CStringA WideToUtf8(const CStringW& ws)
+{
+	int need = WideCharToMultiByte(CP_UTF8, 0, ws, -1, nullptr, 0, nullptr, nullptr);
+	if (need <= 0) return "";
+	CStringA out;
+	LPSTR buf = out.GetBufferSetLength(need - 1);
+	WideCharToMultiByte(CP_UTF8, 0, ws, -1, buf, need, nullptr, nullptr);
+	out.ReleaseBuffer();
+	return out;
+}
+
+CStringA WideToAnsi(const CStringW& ws)
+{
+	int need = WideCharToMultiByte(CP_ACP, 0, ws, -1, nullptr, 0, nullptr, nullptr);
+	if (need <= 0) return "";
+	CStringA out;
+	LPSTR buf = out.GetBufferSetLength(need - 1);
+	WideCharToMultiByte(CP_ACP, 0, ws, -1, buf, need, nullptr, nullptr);
+	out.ReleaseBuffer();
+	return out;
+}
+
+static CStringA AnsiToUtf8(const std::string& s)
+{
+	int wlen = MultiByteToWideChar(CP_ACP, 0, s.c_str(), -1, nullptr, 0);
+	if (wlen <= 0) return "";
+	CStringW ws;
+	LPWSTR wbuf = ws.GetBufferSetLength(wlen - 1);
+	MultiByteToWideChar(CP_ACP, 0, s.c_str(), -1, wbuf, wlen);
+	ws.ReleaseBuffer();
+	return WideToUtf8(ws);
+}
+
+int CHsmsPassive::deleteVariable(int variableId)
+{
+	Lock();
+	auto it = std::find_if(m_variabels.begin(), m_variabels.end(), [=](SERVO::CVariable* v) {
+		return v != nullptr && v->getVarialbleId() == variableId;
+		});
+	if (it == m_variabels.end()) {
+		Unlock();
+		return -1;
+	}
+	delete *it;
+	m_variabels.erase(it);
+	auto filepath = m_strVariableFilepath;
+	Unlock();
+
+	if (filepath.empty()) return -2;
+
+	return writeVariablesToFile(filepath);
+}
+
 void CHsmsPassive::setVariableValue(const char* pszName, __int64 value)
 {
 	auto v = getVariable(pszName);
@@ -336,32 +493,221 @@
 	}
 }
 
+static bool isValidFormat(const std::string& fmt)
+{
+	static const std::set<std::string> allow = { "U1","U2","I2","A20","A50","L" };
+	return allow.count(fmt) > 0;
+}
+
+int CHsmsPassive::addVariable(const char* pszName, const char* pszFormat, const char* pszRemark, int& outId)
+{
+	if (pszName == nullptr || pszFormat == nullptr) return -1;
+	std::string fmt = pszFormat;
+	std::transform(fmt.begin(), fmt.end(), fmt.begin(), ::toupper);
+	if (!isValidFormat(fmt)) return -2;
+
+	Lock();
+	int maxId = 0;
+	for (auto v : m_variabels) {
+		if (v != nullptr && v->getVarialbleId() > maxId) {
+			maxId = v->getVarialbleId();
+		}
+	}
+	outId = maxId + 1;
+
+	SERVO::CVariable* pNew = new SERVO::CVariable(std::to_string(outId).c_str(), pszName, fmt.c_str(), pszRemark ? pszRemark : "");
+	m_variabels.push_back(pNew);
+	auto filepath = m_strVariableFilepath;
+	Unlock();
+
+	if (filepath.empty()) return -3;
+	return writeVariablesToFile(filepath);
+}
+
+int CHsmsPassive::updateVariable(int variableId, const char* pszName, const char* pszFormat, const char* pszRemark)
+{
+	if (pszName == nullptr || pszFormat == nullptr) return -1;
+	std::string fmt = pszFormat;
+	std::transform(fmt.begin(), fmt.end(), fmt.begin(), ::toupper);
+	if (!isValidFormat(fmt)) return -2;
+
+	Lock();
+	auto it = std::find_if(m_variabels.begin(), m_variabels.end(), [=](SERVO::CVariable* v) {
+		return v != nullptr && v->getVarialbleId() == variableId;
+		});
+	if (it == m_variabels.end()) {
+		Unlock();
+		return -4;
+	}
+	(*it)->setName(pszName);
+	(*it)->setFormat(fmt.c_str());
+	(*it)->setRemark(pszRemark ? pszRemark : "");
+	auto filepath = m_strVariableFilepath;
+	Unlock();
+
+	if (filepath.empty()) return -3;
+	return writeVariablesToFile(filepath);
+}
+
+int CHsmsPassive::writeVariablesToFile(const std::string& filepath)
+{
+	if (filepath.empty()) return -3;
+
+	CFile file;
+	if (!file.Open(filepath.c_str(), CFile::modeCreate | CFile::modeWrite | CFile::shareDenyNone)) {
+		return -3;
+	}
+
+	// header
+	const std::string headerAnsi = "SVID,SV Name,SV Format,SV Remark\r\n";
+	if (m_bVariableUtf8) {
+		if (m_bVariableUtf8Bom) {
+			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 (auto v : m_variabels) {
+		if (v == nullptr) continue;
+		std::string lineAnsi;
+		lineAnsi.reserve(256);
+		lineAnsi += std::to_string(v->getVarialbleId());
+		lineAnsi.push_back(',');
+		lineAnsi += v->getName();
+		lineAnsi.push_back(',');
+		lineAnsi += SERVO::CVariable::formatToString(v->getFormat());
+		lineAnsi.push_back(',');
+		lineAnsi += v->getRemark();
+		lineAnsi.append("\r\n");
+
+		if (m_bVariableUtf8) {
+			CStringA outLine = AnsiToUtf8(lineAnsi);
+			file.Write(outLine.GetString(), outLine.GetLength());
+		}
+		else {
+			file.Write(lineAnsi.data(), (UINT)lineAnsi.size());
+		}
+	}
+	file.Close();
+
+	return 0;
+}
+
 int CHsmsPassive::loadReports(const char* pszFilepath)
 {
-	CStdioFile file;
-	if (!file.Open(pszFilepath, CFile::modeRead)) {
+	m_strReportFilepath = pszFilepath;
+	m_bReportUtf8 = false;
+	m_bReportUtf8Bom = false;
+	// 鍏煎 UTF-8/BOM 涓庢湰鍦扮紪鐮佽鍙�
+	CFile file;
+	if (!file.Open(pszFilepath, CFile::modeRead | CFile::shareDenyNone)) {
 		return -1;
 	}
 
-	std::regex pattern("^\\d+,\\(\\d+(,\\d+)*\\).*");  // 鍖归厤浠ユ暟瀛�+閫楀彿寮�澶寸殑瀛楃涓�
+	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();
+
+	const unsigned char* bytes = reinterpret_cast<const unsigned char*>(buffer.data());
+	size_t offset = 0;
+	CStringW content;
+
+	// UTF-8 BOM
+	if (nLen >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) {
+		offset = 3;
+		m_bReportUtf8 = true;
+		m_bReportUtf8Bom = true;
+	}
+
+	// UTF-16 LE BOM
+	if (nLen >= 2 && bytes[0] == 0xFF && bytes[1] == 0xFE) {
+		const wchar_t* wdata = reinterpret_cast<const wchar_t*>(buffer.data() + 2);
+		const size_t wlen = (nLen - 2) / sizeof(wchar_t);
+		content.SetString(wdata, static_cast<int>(wlen));
+	}
+	// UTF-16 BE BOM
+	else if (nLen >= 2 && bytes[0] == 0xFE && bytes[1] == 0xFF) {
+		const size_t wlen = (nLen - 2) / sizeof(wchar_t);
+		std::wstring temp;
+		temp.reserve(wlen);
+		for (size_t i = 0; i < wlen; ++i) {
+			wchar_t ch = static_cast<wchar_t>(bytes[2 + i * 2] << 8 | bytes[2 + i * 2 + 1]);
+			temp.push_back(ch);
+		}
+		content = temp.c_str();
+	}
+	else {
+		auto tryUtf8 = [&](size_t off) -> bool {
+			int need = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, buffer.data() + off,
+				static_cast<int>(buffer.size() - off), nullptr, 0);
+			if (need <= 0) return false;
+			std::wstring temp;
+			temp.resize(need);
+			MultiByteToWideChar(CP_UTF8, 0, buffer.data() + off,
+				static_cast<int>(buffer.size() - off), temp.data(), need);
+			content = temp.c_str();
+			m_bReportUtf8 = true;
+			return true;
+		};
+
+		if (!tryUtf8(offset)) {
+			int need = MultiByteToWideChar(CP_ACP, 0, buffer.data(), static_cast<int>(buffer.size()), nullptr, 0);
+			if (need > 0) {
+				std::wstring temp;
+				temp.resize(need);
+				MultiByteToWideChar(CP_ACP, 0, buffer.data(), static_cast<int>(buffer.size()), temp.data(), need);
+				content = temp.c_str();
+			}
+		}
+	}
+
+	if (content.IsEmpty()) {
+		return -1;
+	}
+
+	std::wregex pattern(L"^\\d+,\\(\\d+(,\\d+)*\\).*");  // 鍖归厤浠ユ暟瀛�+閫楀彿寮�澶寸殑瀛楃涓�
 	std::vector<SERVO::CReport*> reports;
 	int index;
-	CString strLine, strVariable;
-	CString strId;
-	while (file.ReadString(strLine)) {
-		if (!std::regex_match((LPTSTR)(LPCTSTR)strLine, pattern)) {
+	CStringW strLine, strVariable;
+	CStringW strId;
+	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 (!std::regex_match(static_cast<LPCWSTR>(strLine), pattern)) {
 			continue;
 		}
 
-		index = strLine.Find(",", 0);
+		index = strLine.Find(L",", 0);
 		if (index < 0) continue;
 		strId = strLine.Left(index);
 		strVariable = strLine.Right(strLine.GetLength() - index - 1);
 		strVariable.Delete(0);
 		strVariable.Delete(strVariable.GetLength() - 1);
-		auto vids = parseVidList(strVariable);
+		CString strVariableA(narrowFromW(strVariable).c_str());
+		auto vids = parseVidList(strVariableA);
 
-		SERVO::CReport* pReport = new SERVO::CReport(atoi((LPTSTR)(LPCTSTR)strId), vids);
+		SERVO::CReport* pReport = new SERVO::CReport(_wtoi(strId), vids);
 		for (auto vid : vids) {
 			SERVO::CVariable* pVariable = getVariable(vid);
 			if (pVariable != nullptr) {
@@ -380,13 +726,23 @@
 	}
 	
 
-	file.Close();
 	return 0;
 }
 
 std::vector<SERVO::CReport*>& CHsmsPassive::getReports()
 {
 	return m_reports;
+}
+
+unsigned int CHsmsPassive::getMaxReportId() const
+{
+	unsigned int maxId = 0;
+	for (auto item : m_reports) {
+		if (item && item->getReportId() > maxId) {
+			maxId = item->getReportId();
+		}
+	}
+	return maxId;
 }
 
 SERVO::CReport* CHsmsPassive::getReport(int rptid)
@@ -413,6 +769,49 @@
 	return false;
 }
 
+int CHsmsPassive::deleteReport(int rptid)
+{
+	if (!removeReport(rptid)) {
+		return -1;
+	}
+	return writeReportsToFile(m_strReportFilepath);
+}
+
+int CHsmsPassive::addReport(int rptid, const std::vector<unsigned int>& vids)
+{
+	if (getReport(rptid) != nullptr) {
+		return -1;
+	}
+	SERVO::CReport* pReport = new SERVO::CReport(rptid, vids);
+	for (auto vid : vids) {
+		SERVO::CVariable* pVariable = getVariable((int)vid);
+		if (pVariable != nullptr) {
+			pReport->addVariable(pVariable);
+		}
+	}
+	m_reports.push_back(pReport);
+	return writeReportsToFile(m_strReportFilepath);
+}
+
+int CHsmsPassive::updateReport(int rptid, const std::vector<unsigned int>& vids)
+{
+	for (auto iter = m_reports.begin(); iter != m_reports.end(); ++iter) {
+		if ((*iter)->getReportId() == rptid) {
+			delete (*iter);
+			SERVO::CReport* pReport = new SERVO::CReport(rptid, vids);
+			for (auto vid : vids) {
+				SERVO::CVariable* pVariable = getVariable((int)vid);
+				if (pVariable != nullptr) {
+					pReport->addVariable(pVariable);
+				}
+			}
+			*iter = pReport;
+			return writeReportsToFile(m_strReportFilepath);
+		}
+	}
+	return -1;
+}
+
 void CHsmsPassive::clearAllReport()
 {
 	for (auto item : m_reports) {
@@ -421,44 +820,183 @@
 	m_reports.clear();
 }
 
-int CHsmsPassive::loadCollectionEvents(const char* pszFilepath)
+int CHsmsPassive::writeReportsToFile(const std::string& filepath)
 {
-	CStdioFile file;
-	if (!file.Open(pszFilepath, CFile::modeRead)) {
+	if (filepath.empty()) return -1;
+
+	CFile file;
+	if (!file.Open(filepath.c_str(), CFile::modeCreate | CFile::modeWrite)) {
 		return -1;
 	}
 
-	std::regex pattern("^\\d+,[^,]*,[^,]*,\\(\\d+(,\\d+)*\\).*");  // 鍖归厤浠ユ暟瀛�+閫楀彿寮�澶寸殑瀛楃涓�
+	if (m_bReportUtf8 && m_bReportUtf8Bom) {
+		const BYTE bom[3] = { 0xEF, 0xBB, 0xBF };
+		file.Write(bom, 3);
+	}
+
+	// header
+	const std::string headerAnsi = "RPTID,(VID1,VID2,...)\r\n";	
+	if (m_bReportUtf8) {
+		CStringA header = AnsiToUtf8(headerAnsi);
+		file.Write(header.GetString(), header.GetLength());
+	}
+	else {
+		file.Write(headerAnsi.data(), (UINT)headerAnsi.size());
+	}
+
+	for (auto rpt : m_reports) {
+		if (rpt == nullptr) continue;
+		std::string line;
+		line.reserve(64);
+		line += std::to_string(rpt->getReportId());
+		line += ",(";
+
+		const auto& vids = rpt->getVids();
+		for (size_t i = 0; i < vids.size(); ++i) {
+			line += std::to_string(vids[i]);
+			if (i + 1 < vids.size()) {
+				line.push_back(',');
+			}
+		}
+		line += ")\r\n";
+
+		if (m_bReportUtf8) {
+			CStringA out = AnsiToUtf8(line);
+			file.Write(out.GetString(), out.GetLength());
+		}
+		else {
+			file.Write(line.data(), (UINT)line.size());
+		}
+	}
+
+	file.Close();
+	return 0;
+}
+
+int CHsmsPassive::loadCollectionEvents(const char* pszFilepath)
+{
+	m_strCollectionEventFilepath = pszFilepath;
+	m_bCollectionUtf8 = false;
+	m_bCollectionUtf8Bom = 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();
+
+	const unsigned char* bytes = reinterpret_cast<const unsigned char*>(buffer.data());
+	size_t offset = 0;
+	CStringW content;
+
+	// UTF-8 BOM
+	if (nLen >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) {
+		offset = 3;
+		m_bCollectionUtf8 = true;
+		m_bCollectionUtf8Bom = true;
+	}
+
+	// UTF-16 LE BOM
+	if (nLen >= 2 && bytes[0] == 0xFF && bytes[1] == 0xFE) {
+		const wchar_t* wdata = reinterpret_cast<const wchar_t*>(buffer.data() + 2);
+		const size_t wlen = (nLen - 2) / sizeof(wchar_t);
+		content.SetString(wdata, static_cast<int>(wlen));
+	}
+	// UTF-16 BE BOM
+	else if (nLen >= 2 && bytes[0] == 0xFE && bytes[1] == 0xFF) {
+		const size_t wlen = (nLen - 2) / sizeof(wchar_t);
+		std::wstring temp;
+		temp.reserve(wlen);
+		for (size_t i = 0; i < wlen; ++i) {
+			wchar_t ch = static_cast<wchar_t>(bytes[2 + i * 2] << 8 | bytes[2 + i * 2 + 1]);
+			temp.push_back(ch);
+		}
+		content = temp.c_str();
+	}
+	else {
+		auto tryUtf8 = [&](size_t off) -> bool {
+			int need = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, buffer.data() + off,
+				static_cast<int>(buffer.size() - off), nullptr, 0);
+			if (need <= 0) return false;
+			std::wstring temp;
+			temp.resize(need);
+			MultiByteToWideChar(CP_UTF8, 0, buffer.data() + off,
+				static_cast<int>(buffer.size() - off), temp.data(), need);
+			content = temp.c_str();
+			m_bCollectionUtf8 = true;
+			return true;
+		};
+
+		if (!tryUtf8(offset)) {
+			int need = MultiByteToWideChar(CP_ACP, 0, buffer.data(), static_cast<int>(buffer.size()), nullptr, 0);
+			if (need > 0) {
+				std::wstring temp;
+				temp.resize(need);
+				MultiByteToWideChar(CP_ACP, 0, buffer.data(), static_cast<int>(buffer.size()), temp.data(), need);
+				content = temp.c_str();
+			}
+		}
+	}
+
+	if (content.IsEmpty()) {
+		return -1;
+	}
+
+	std::wregex pattern(L"^\\d+,[^,]*,[^,]*,\\(\\d+(,\\d+)*\\).*");  // 鍖归厤浠ユ暟瀛�+閫楀彿寮�澶寸殑瀛楃涓�
 	std::vector<SERVO::CCollectionEvent*> events;
 	int index, last;
-	CString strLine, strRPTIDs;
-	CString strId, strName, strDescription;
-	while (file.ReadString(strLine)) {
-		if (!std::regex_match((LPTSTR)(LPCTSTR)strLine, pattern)) {
+	CStringW strLine, strRPTIDs;
+	CStringW strId, strName, strDescription;
+	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 (!std::regex_match(static_cast<LPCWSTR>(strLine), pattern)) {
 			continue;
 		}
 
 		last = 0;
-		index = strLine.Find(",", last);
+		index = strLine.Find(L",", last);
 		if (index < 0) continue;
 		strId = strLine.Left(index);
 		last = index + 1;
 
-		index = strLine.Find(",", last);
+		index = strLine.Find(L",", last);
 		if (index < 0) continue;
 		strName = strLine.Mid(last, index - last);
 		last = index + 1;
 
-		index = strLine.Find(",", last);
+		index = strLine.Find(L",", last);
 		if (index < 0) continue;
 		strDescription = strLine.Mid(last, index - last);
 		strRPTIDs = strLine.Right(strLine.GetLength() - index - 1);
 		strRPTIDs.Delete(0);
 		strRPTIDs.Delete(strRPTIDs.GetLength() - 1);
-		auto prtids = parseVidList(strRPTIDs);
+		CString strRPTIDsA(narrowFromW(strRPTIDs).c_str());
+		auto prtids = parseVidList(strRPTIDsA);
+
+		std::string sName = narrowFromW(strName);
+		std::string sDesc = narrowFromW(strDescription);
 
 		SERVO::CCollectionEvent* pEvent = new SERVO::CCollectionEvent(
-			atoi(strId), (LPTSTR)(LPCTSTR)strName, (LPTSTR)(LPCTSTR)strDescription, prtids);
+			_wtoi(strId), sName.c_str(), sDesc.c_str(), prtids);
 		for (auto rptid : prtids) {
 			SERVO::CReport* pReport = getReport(rptid);
 			if (pReport != nullptr) {
@@ -474,15 +1012,70 @@
 			m_collectionEvents.push_back(item);
 		}
 	}
-
-
-	file.Close();
 	return 0;
 }
 
 std::vector<SERVO::CCollectionEvent*>& CHsmsPassive::getCollectionEvents()
 {
 	return m_collectionEvents;
+}
+
+unsigned int CHsmsPassive::getMaxCollectionEventId() const
+{
+	unsigned int maxId = 0;
+	for (auto item : m_collectionEvents) {
+		if (item && item->getEventId() > maxId) {
+			maxId = item->getEventId();
+		}
+	}
+	return maxId;
+}
+
+int CHsmsPassive::deleteCollectionEvent(unsigned short CEID)
+{
+	for (auto iter = m_collectionEvents.begin(); iter != m_collectionEvents.end(); ++iter) {
+		if ((*iter)->getEventId() == CEID) {
+			delete (*iter);
+			m_collectionEvents.erase(iter);
+			return writeCollectionEventsToFile(m_strCollectionEventFilepath);
+		}
+	}
+	return -1;
+}
+
+int CHsmsPassive::addCollectionEvent(unsigned int CEID, const char* name, const char* desc, const std::vector<unsigned int>& rptids)
+{
+	if (getEvent((unsigned short)CEID) != nullptr) {
+		return -1;
+	}
+	auto* pEvent = new SERVO::CCollectionEvent(CEID, name, desc, const_cast<std::vector<unsigned int>&>(rptids));
+	for (auto rptid : rptids) {
+		SERVO::CReport* pReport = getReport((int)rptid);
+		if (pReport != nullptr) {
+			pEvent->addReport(pReport);
+		}
+	}
+	m_collectionEvents.push_back(pEvent);
+	return writeCollectionEventsToFile(m_strCollectionEventFilepath);
+}
+
+int CHsmsPassive::updateCollectionEvent(unsigned int CEID, const char* name, const char* desc, const std::vector<unsigned int>& rptids)
+{
+	for (auto iter = m_collectionEvents.begin(); iter != m_collectionEvents.end(); ++iter) {
+		if ((*iter)->getEventId() == CEID) {
+			delete (*iter);
+			auto* pEvent = new SERVO::CCollectionEvent(CEID, name, desc, const_cast<std::vector<unsigned int>&>(rptids));
+			for (auto rptid : rptids) {
+				SERVO::CReport* pReport = getReport((int)rptid);
+				if (pReport != nullptr) {
+					pEvent->addReport(pReport);
+				}
+			}
+			*iter = pEvent;
+			return writeCollectionEventsToFile(m_strCollectionEventFilepath);
+		}
+	}
+	return -1;
 }
 
 void CHsmsPassive::clearAllCollectionEvent()
@@ -524,6 +1117,62 @@
 	}
 
 	return result;
+}
+
+int CHsmsPassive::writeCollectionEventsToFile(const std::string& filepath)
+{
+	if (filepath.empty()) return -1;
+
+	CFile file;
+	if (!file.Open(filepath.c_str(), CFile::modeCreate | CFile::modeWrite)) {
+		return -1;
+	}
+
+	if (m_bCollectionUtf8 && m_bCollectionUtf8Bom) {
+		const BYTE bom[3] = { 0xEF, 0xBB, 0xBF };
+		file.Write(bom, 3);
+	}
+
+	const std::string headerAnsi = "CEID,CE Name,Descriptions,Attached RPTID\r\n";
+	if (m_bCollectionUtf8) {
+		CStringA header = AnsiToUtf8(headerAnsi);
+		file.Write(header.GetString(), header.GetLength());
+	}
+	else {
+		file.Write(headerAnsi.data(), (UINT)headerAnsi.size());
+	}
+
+	for (auto ev : m_collectionEvents) {
+		if (ev == nullptr) continue;
+		std::string line;
+		line.reserve(128);
+		line += std::to_string(ev->getEventId());
+		line.push_back(',');
+		line += ev->getName();
+		line.push_back(',');
+		line += ev->getDescription();
+		line.push_back(',');
+		line.push_back('(');
+		auto rptIds = ev->getReportIds();
+		for (size_t i = 0; i < rptIds.size(); ++i) {
+			line += std::to_string(rptIds[i]);
+			if (i + 1 < rptIds.size()) {
+				line.push_back(',');
+			}
+		}
+		line += ")\r\n";
+
+		if (m_bCollectionUtf8) {
+			CStringA out = AnsiToUtf8(line);
+			file.Write(out.GetString(), out.GetLength());
+		}
+		else {
+			file.Write(line.data(), (UINT)line.size());
+		}
+	}
+
+	file.Close();
+	return 0;
 }
 
 int CHsmsPassive::init(CModel* pModel, const char* pszName, unsigned int port)
@@ -968,18 +1617,18 @@
 	ASSERT(pMessage);
 
 	unsigned char SVU1 = 0;
-	unsigned int SVID = 0;
+	unsigned short SVID = 0;
 	ISECS2Item* pBody = pRecv->getBody();
 	if (pBody == nullptr || pBody->getType() != SITYPE::L) {
 		pMessage->getBody()->addU1Item(SVU1, "SV");
 		goto MYREPLY;
 	}
-	if (!pBody->getSubItemU4(0, SVID)) {
+	if (!pBody->getSubItemU2(0, SVID)) {
 		pMessage->getBody()->addU1Item(SVU1, "SV");
 		goto MYREPLY;
 	}
 
-	SERVO::CVariable* pVariable = getVariable(SVID);
+	SERVO::CVariable* pVariable = getVariable((int)SVID);
 	if (pVariable == nullptr) {
 		pMessage->getBody()->addU1Item(SVU1, "SV");
 		goto MYREPLY;
@@ -1810,7 +2459,7 @@
 }
 
 // S6F11
-static unsigned int DATAID = 1;
+static unsigned short DATAID = 0;
 int CHsmsPassive::requestEventReportSend(unsigned int CEID)
 {
 	SERVO::CCollectionEvent* pEvent = getEvent(CEID);
@@ -1830,7 +2479,8 @@
 	HSMS_Create1Message(pMessage, m_nSessionId, 6 | REPLY, 11, ++m_nSystemByte);
 	ASSERT(pMessage);
 	ISECS2Item* pItem = pMessage->getBody();
-	pItem->addU4Item(++DATAID, "DATAID");
+	// pItem->addU2Item(++DATAID, "DATAID");		// 鏍规嵁鍒殑鏃ュ織鏄剧ずDATAID鎭掍负0锛屾墍浠ユ垜浠厛鐓т娇鐢�0
+	pItem->addU2Item(0, "DATAID");
 	pItem->addU4Item(CEID, "CEID");
 	ISECS2Item* pItemList1 = pItem->addItem();
 	ISECS2Item* pItemList2 = pItemList1->addItem();
@@ -1874,6 +2524,21 @@
 	return requestEventReportSend("CarrierID_Readed");
 }
 
+int CHsmsPassive::requestEventReportSend_CheckSlotMap()
+{
+	return requestEventReportSend("CheckSlotMap");
+}
+
+int CHsmsPassive::requestEventReportSend_SlotMapVerificationOK()
+{
+	return requestEventReportSend("SlotMapVerificationOK");
+}
+
+int CHsmsPassive::requestEventReportSend_SlotMapVerificationNG()
+{
+	return requestEventReportSend("SlotMapVerificationNG");
+}
+
 int CHsmsPassive::requestEventReportSend_Port_Unload_Ready()
 {
 	return requestEventReportSend("Port_Unload_Ready");
@@ -1882,6 +2547,11 @@
 int CHsmsPassive::requestEventReportSend_Port_Load_Ready()
 {
 	return requestEventReportSend("Port_Load_Ready");
+}
+
+int CHsmsPassive::requestEventReportSend_Port_Ready_To_Release()
+{
+	return requestEventReportSend("Port_Ready_To_Release");
 }
 
 int CHsmsPassive::requestEventReportSend_Port_Blocked()
@@ -1928,6 +2598,5 @@
 {
 	return requestEventReportSend("OCR_PanelID_Read_OK");
 }
-
 
 

--
Gitblit v1.9.3