chenluhua1980
2026-01-13 1cc8012fac8508796e50653fb26bcc003bc02b9d
SourceCode/Bond/Servo/HsmsPassive.cpp
@@ -14,6 +14,47 @@
#include <regex>
#include <sstream>
// ---- Encoding helpers ----
static bool hasUtf8Bom(const std::string& s)
{
   return s.size() >= 3 &&
      static_cast<unsigned char>(s[0]) == 0xEF &&
      static_cast<unsigned char>(s[1]) == 0xBB &&
      static_cast<unsigned char>(s[2]) == 0xBF;
}
static bool isLikelyUtf8(const std::string& s)
{
   // Simple heuristic: try to convert; if success without errors, treat as UTF-8.
   int wlen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), (int)s.size(), nullptr, 0);
   return wlen > 0;
}
static CStringW Utf8ToWide(const char* psz)
{
   if (psz == nullptr) return L"";
   int wlen = MultiByteToWideChar(CP_UTF8, 0, psz, -1, nullptr, 0);
   if (wlen <= 0) return L"";
   CStringW ws;
   LPWSTR buf = ws.GetBufferSetLength(wlen - 1);
   MultiByteToWideChar(CP_UTF8, 0, psz, -1, buf, wlen);
   ws.ReleaseBuffer();
   return ws;
}
static CStringW AnsiToWide(const char* psz)
{
   if (psz == nullptr) return L"";
   int wlen = MultiByteToWideChar(CP_ACP, 0, psz, -1, nullptr, 0);
   if (wlen <= 0) return L"";
   CStringW ws;
   LPWSTR buf = ws.GetBufferSetLength(wlen - 1);
   MultiByteToWideChar(CP_ACP, 0, psz, -1, buf, wlen);
   ws.ReleaseBuffer();
   return ws;
}
// ---- End helpers ----
// ControlState values (keep in sync with Model::ControlState / VariableList.txt)
static constexpr uint8_t kControlStateOnlineRemote = 5;
@@ -424,9 +465,106 @@
   return 0;
}
int CHsmsPassive::loadDataVarialbles(const char* pszFilepath)
{
   if (pszFilepath == NULL) {
      return -1;
   }
   m_strDataVariableFilepath = pszFilepath;
   m_bDataVariableUtf8 = false;
   m_bDataVariableUtf8Bom = 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_bDataVariableUtf8 = true;
      m_bDataVariableUtf8Bom = true;
      buffer = buffer.substr(3);
   }
   else if (isLikelyUtf8(buffer)) {
      m_bDataVariableUtf8 = true;
   }
   CStringW content = m_bDataVariableUtf8 ? Utf8ToWide(buffer.c_str()) : AnsiToWide(buffer.c_str());
   // Regex: DVID,DV Name,DV Format,DV Remark
   std::wregex pattern(L"^\\d+,[^,]*,[^,]*,.*");
   std::vector<SERVO::CDataVariable*> dataVars;
   int index;
   CStringW strLine, 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;
      }
      index = strLine.Find(L",", 0);
      if (index < 0) continue;
      strId = strLine.Left(index);
      strLine = strLine.Right(strLine.GetLength() - index - 1);
      index = strLine.Find(L",", 0);
      if (index < 0) continue;
      strName = strLine.Left(index);
      strLine = strLine.Right(strLine.GetLength() - index - 1);
      index = strLine.Find(L",", 0);
      if (index < 0) continue;
      strFormat = strLine.Left(index);
      strRemark = strLine.Right(strLine.GetLength() - index - 1);
      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::CDataVariable* pVarialble = new SERVO::CDataVariable(
         sId.c_str(),
         sName.c_str(),
         sFormat.c_str(),
         sRemark.c_str());
      dataVars.push_back(pVarialble);
   }
   if (!dataVars.empty()) {
      clearAllDataVariabel();
      for (auto item : dataVars) {
         m_dataVariabels.push_back(item);
      }
   }
   return 0;
}
std::vector<SERVO::CVariable*>& CHsmsPassive::getVariables()
{
   return m_variabels;
}
std::vector<SERVO::CDataVariable*>& CHsmsPassive::getDataVariables()
{
   return m_dataVariabels;
}
unsigned int CHsmsPassive::getMaxVariableId() const
@@ -458,7 +596,30 @@
         return item;
      }
   }
   // try numeric id string
   if (pszName != nullptr && *pszName) {
      const int id = atoi(pszName);
      if (id > 0) {
         return getVariable(id);
      }
   }
   return nullptr;
}
SERVO::CDataVariable* CHsmsPassive::getDataVariable(int dvid)
{
   for (auto item : m_dataVariabels) {
      if (item->getVarialbleId() == (unsigned int)dvid) return item;
   }
   return nullptr;
}
SERVO::CDataVariable* CHsmsPassive::getDataVariable(const char* pszName)
{
   for (auto item : m_dataVariabels) {
      if (item->getName().compare(pszName) == 0) return item;
   }
   return nullptr;
}
@@ -483,6 +644,14 @@
      delete item;
   }
   m_variabels.clear();
}
void CHsmsPassive::clearAllDataVariabel()
{
   for (auto item : m_dataVariabels) {
      delete item;
   }
   m_dataVariabels.clear();
}
CStringA WideToUtf8(const CStringW& ws)
@@ -675,6 +844,53 @@
   }
   file.Close();
   return 0;
}
int CHsmsPassive::writeDataVariablesToFile(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;
   }
   const std::string headerAnsi = "DVID,DV Name,DV Format,DV Remark\r\n";
   if (m_bDataVariableUtf8) {
      if (m_bDataVariableUtf8Bom) {
         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_dataVariabels) {
      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_bDataVariableUtf8) {
         CStringA outLine = AnsiToUtf8(lineAnsi);
         file.Write(outLine.GetString(), outLine.GetLength());
      }
      else {
         file.Write(lineAnsi.data(), (UINT)lineAnsi.size());
      }
   }
   file.Close();
   return 0;
}
@@ -1324,6 +1540,9 @@
      else if (nStream == 1 && pHeader->function == 11) {
         replyStatusVariableNamelistRequest(pMessage);
      }
      else if (nStream == 1 && pHeader->function == 21) {
         replyDataVariableNamelistRequest(pMessage);
      }
      else if (nStream == 1 && pHeader->function == 23) {
         replyCollectionEventNamelistRequest(pMessage);
      }
@@ -1941,6 +2160,66 @@
   return ER_NOERROR;
}
// S1F21/S1F22 - Data Variable Namelist
int CHsmsPassive::replyDataVariableNamelistRequest(IMessage* pRecv)
{
   if (m_pPassive == NULL || STATE::SELECTED != m_pPassive->getState()) {
      return ER_NOTSELECT;
   }
   std::vector<unsigned short> reqIds;
   ISECS2Item* pBody = pRecv->getBody();
   if (pBody != nullptr && pBody->getType() == SITYPE::L) {
      const int sz = pBody->getSubItemSize();
      for (int i = 0; i < sz; ++i) {
         unsigned short id = 0;
         if (pBody->getSubItemU2(i, id)) {
            reqIds.push_back(id);
         }
      }
   }
   std::vector<unsigned short> dvids;
   std::set<unsigned short> requested(reqIds.begin(), reqIds.end());
   Lock();
   if (reqIds.empty()) {
      for (auto v : m_dataVariabels) {
         if (v) dvids.push_back(static_cast<unsigned short>(v->getVarialbleId()));
      }
   }
   else {
      for (auto id : requested) dvids.push_back(id);
   }
   Unlock();
   IMessage* pMessage = NULL;
   HSMS_Create1Message(pMessage, m_nSessionId, 1, 22, pRecv->getHeader()->systemBytes);
   ASSERT(pMessage);
   ISECS2Item* pList = pMessage->getBody(); // L[n] of {DVID, DVNAME, UNITS}
   for (auto id : dvids) {
      ISECS2Item* pEntry = pList->addItem();
      pEntry->addU2Item(id, "DVID");
      SERVO::CDataVariable* v = getDataVariable((int)id);
      if (v != nullptr) {
         pEntry->addItem(v->getName().c_str(), "DVNAME");
         pEntry->addItem(v->getRemark().c_str(), "UNITS");
      }
      else {
         pEntry->addItem("", "DVNAME");
         pEntry->addItem("", "UNITS");
      }
   }
   m_pPassive->sendMessage(pMessage);
   LOGI("<HSMS>[SEND]sessionId:%d, sType:%d systemBytes:%d",
      pMessage->getHeader()->sessionId, pMessage->getHeader()->sType, pMessage->getHeader()->systemBytes);
   LogSecsMessageBrief("<HSMS>[SEND]", pMessage);
   HSMS_Destroy1Message(pMessage);
   return ER_NOERROR;
}
// S1F23
int CHsmsPassive::replyCollectionEventNamelistRequest(IMessage* pRecv)
{