calccrc.hpp 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310
  1. /****************************************************************************
  2. ** CopyRight(c) 2023, XXXX
  3. ** All rights reserved
  4. **
  5. ** 文件名称: calccrc.hpp
  6. ** 摘要: SCD CRC 计算算法
  7. **
  8. ** 当前版本: 1.0.0.0
  9. ** 作者:
  10. **
  11. ** 完成日期: 2023-3-18
  12. **
  13. ** 历史修改记录: 
  14. ** 作者 描述 修改时间
  15. ****************************************************************************/
  16. #pragma once
  17. #include "pugixml.hpp"
  18. #include "sformat.h"
  19. #include <map>
  20. #include <string>
  21. #include <vector>
  22. #include <iostream>
  23. #include <fstream>
  24. #include <sstream>
  25. #include <iomanip>
  26. #include <chrono>
  27. #include <ctime>
  28. using namespace std;
  29. using namespace pugi;
  30. class CCalCRC
  31. {
  32. public:
  33. enum eType {
  34. eGoose = 0,
  35. eSV
  36. };
  37. /**
  38. * @brief 计算IED CRC
  39. * @param pszSCDFile SCD文件路径
  40. * @return IEDCRC Map
  41. */
  42. static map<string, string> getIedCrc(const char* pszSCDFile)
  43. {
  44. map<string, string> mapCRC;
  45. xml_document m_scddoc;
  46. xml_parse_result parse_xml = m_scddoc.load_file(pszSCDFile, parse_full);
  47. if (parse_xml.status != status_ok)
  48. {
  49. //打开失败
  50. //cout << "打开SCD失败!" << endl;
  51. return mapCRC;
  52. }
  53. //SCD根节点
  54. xml_node root_scd = m_scddoc.document_element();
  55. vector<xml_node> vecIED = getChildElements(root_scd, "IED");
  56. for (xml_node node_ied : vecIED)
  57. {
  58. xml_document ccddoc;
  59. xml_node xml_pre_node = ccddoc.prepend_child(pugi::node_declaration);
  60. xml_pre_node.append_attribute("version") = "1.0";
  61. xml_pre_node.append_attribute("encoding") = "UTF-8";
  62. xml_node root_ccd = ccddoc.append_child("IED");
  63. //CCD中IED原始,设置name属性
  64. string iedName = node_ied.attribute("name").as_string();
  65. root_ccd.append_attribute("configVersion") = node_ied.attribute("configVersion").as_string();
  66. root_ccd.append_attribute("desc") = node_ied.attribute("desc").as_string();
  67. root_ccd.append_attribute("manufacturer") = node_ied.attribute("manufacturer").as_string();
  68. root_ccd.append_attribute("name") = node_ied.attribute("name").as_string();
  69. root_ccd.append_attribute("type") = node_ied.attribute("type").as_string();
  70. bool bIsEmpty = createPubAndSubNode(root_ccd, root_scd, node_ied);
  71. if (bIsEmpty)
  72. continue;
  73. //删除私有属性
  74. cutNodeDesc(root_ccd);
  75. //按格式保存临时文件
  76. string strTempFile = string(pszSCDFile) + ".ccdtmp.txt";
  77. ccddoc.save_file(strTempFile.data());
  78. string serial;
  79. //剔除冗余空字符
  80. ifstream in(strTempFile);
  81. string strline;
  82. while (getline(in, strline)) {
  83. trim(strline);
  84. strline = remove_spaces_except_in_quotes(strline);
  85. serial += strline;
  86. }
  87. in.close();
  88. remove(strTempFile.data());
  89. int crc = 0xffffffff;
  90. crc = crcbyTable(crc, serial);
  91. string crcValue = dec2hex(crc);
  92. for (size_t i = crcValue.size(); i < 8; i++)
  93. crcValue.insert(0, "0");
  94. //输出IED CRC
  95. //cout << iedName << " : " << crcValue << endl;
  96. mapCRC[iedName] = crcValue;
  97. }
  98. return mapCRC;
  99. }
  100. /**
  101. * @brief 计算单IED CRC
  102. * @param pszSCDFile SCD文件路径
  103. * @param pszIEDName IED名称
  104. * @return CRC值
  105. */
  106. static string getIedCrc(const char* pszSCDFile, const char* pszIEDName)
  107. {
  108. xml_document m_scddoc;
  109. xml_parse_result parse_xml = m_scddoc.load_file(pszSCDFile, parse_full);
  110. if (parse_xml.status != status_ok)
  111. {
  112. //打开失败
  113. //cout << "打开SCD失败!" << endl;
  114. return "";
  115. }
  116. //SCD根节点
  117. xml_node root_scd = m_scddoc.document_element();
  118. xpath_node_set set_IED = root_scd.select_nodes(SFormat("/SCL/IED[@name='{0}']", pszIEDName).data());
  119. //GOOSE或SV控制块存在时,则认为发布内容存在
  120. if (set_IED.empty())
  121. return "";
  122. xml_node node_ied = set_IED[0].node();
  123. xml_document ccddoc;
  124. xml_node xml_pre_node = ccddoc.prepend_child(pugi::node_declaration);
  125. xml_pre_node.append_attribute("version") = "1.0";
  126. xml_pre_node.append_attribute("encoding") = "UTF-8";
  127. xml_node root_ccd = ccddoc.append_child("IED");
  128. //CCD中IED原始,设置name属性
  129. root_ccd.append_attribute("configVersion") = node_ied.attribute("configVersion").as_string();
  130. root_ccd.append_attribute("desc") = node_ied.attribute("desc").as_string();
  131. root_ccd.append_attribute("manufacturer") = node_ied.attribute("manufacturer").as_string();
  132. root_ccd.append_attribute("name") = node_ied.attribute("name").as_string();
  133. root_ccd.append_attribute("type") = node_ied.attribute("type").as_string();
  134. bool bIsEmpty = createPubAndSubNode(root_ccd, root_scd, node_ied);
  135. if (bIsEmpty)
  136. return "";
  137. //删除私有属性
  138. cutNodeDesc(root_ccd);
  139. //按格式保存临时文件
  140. string strTempFile = string(pszSCDFile) + ".ccdtmp.txt";
  141. ccddoc.save_file(strTempFile.data());
  142. string serial;
  143. //剔除冗余空字符
  144. ifstream in(strTempFile);
  145. string strline;
  146. while (getline(in, strline)) {
  147. trim(strline);
  148. strline = remove_spaces_except_in_quotes(strline);
  149. serial += strline;
  150. }
  151. in.close();
  152. remove(strTempFile.data());
  153. int crc = 0xffffffff;
  154. crc = crcbyTable(crc, serial);
  155. string crcValue = dec2hex(crc);
  156. for (size_t i = crcValue.size(); i < 8; i++)
  157. crcValue.insert(0, "0");
  158. return crcValue;
  159. }
  160. /**
  161. * @brief 导出CCD
  162. * @param pszSCDFile SCD文件路径
  163. * @param pszIEDName IED名称
  164. * @param pszExportDir 导出CCD目录
  165. * @return CRC值
  166. */
  167. static void exportIedCCD(const char* pszSCDFile, const char* pszIEDName, const char* pszExportDir)
  168. {
  169. xml_document m_scddoc;
  170. xml_parse_result parse_xml = m_scddoc.load_file(pszSCDFile, parse_full);
  171. if (parse_xml.status != status_ok)
  172. {
  173. //打开失败
  174. //cout << "打开SCD失败!" << endl;
  175. return;
  176. }
  177. //SCD根节点
  178. xml_node root_scd = m_scddoc.document_element();
  179. xpath_node_set set_IED = root_scd.select_nodes(SFormat("/SCL/IED[@name='{0}']", pszIEDName).data());
  180. //GOOSE或SV控制块存在时,则认为发布内容存在
  181. if (set_IED.empty())
  182. return;
  183. xml_node node_ied = set_IED[0].node();
  184. xml_document ccddoc;
  185. xml_node xml_pre_node = ccddoc.prepend_child(pugi::node_declaration);
  186. xml_pre_node.append_attribute("version") = "1.0";
  187. xml_pre_node.append_attribute("encoding") = "UTF-8";
  188. xml_node root_ccd = ccddoc.append_child("IED");
  189. //CCD中IED原始,设置name属性
  190. string iedName = node_ied.attribute("name").as_string();
  191. root_ccd.append_attribute("configVersion") = node_ied.attribute("configVersion").as_string();
  192. root_ccd.append_attribute("desc") = node_ied.attribute("desc").as_string();
  193. root_ccd.append_attribute("manufacturer") = node_ied.attribute("manufacturer").as_string();
  194. root_ccd.append_attribute("name") = node_ied.attribute("name").as_string();
  195. root_ccd.append_attribute("type") = node_ied.attribute("type").as_string();
  196. bool bIsEmpty = createPubAndSubNode(root_ccd, root_scd, node_ied);
  197. if (bIsEmpty)
  198. return;
  199. xml_document ccddoctmp;
  200. xml_node xml_pre_nodetmp = ccddoctmp.prepend_child(pugi::node_declaration);
  201. xml_pre_nodetmp.append_attribute("version") = "1.0";
  202. xml_pre_nodetmp.append_attribute("encoding") = "UTF-8";
  203. xml_node root_ccdtmp = ccddoctmp.append_copy(root_ccd);
  204. //删除私有属性
  205. cutNodeDesc(root_ccdtmp);
  206. //按格式保存临时文件
  207. string strTempFile = string(pszSCDFile) + ".ccdtmp.txt";
  208. ccddoctmp.save_file(strTempFile.data());
  209. string serial;
  210. //剔除冗余空字符
  211. ifstream in(strTempFile);
  212. string strline;
  213. while (getline(in, strline)) {
  214. trim(strline);
  215. strline = remove_spaces_except_in_quotes(strline);
  216. serial += strline;
  217. }
  218. in.close();
  219. remove(strTempFile.data());
  220. int crc = 0xffffffff;
  221. crc = crcbyTable(crc, serial);
  222. string crcValue = dec2hex(crc);
  223. for (size_t i = crcValue.size(); i < 8; i++)
  224. crcValue.insert(0, "0");
  225. xml_node node_crc = root_ccd.append_child("CRC");
  226. node_crc.append_attribute("id") = crcValue.data();
  227. //获取当前时间
  228. std::time_t t = std::time(nullptr);
  229. char buffer[100];
  230. std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", std::localtime(&t));
  231. string currenttime(buffer);
  232. node_crc.append_attribute("timestamp") = currenttime.data();
  233. //std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
  234. //std::time_t now_time = std::chrono::system_clock::to_time_t(now);
  235. //std::tm* now_tm = std::localtime(&now_time);
  236. //std::stringstream ss;
  237. //ss << std::put_time(now_tm, "%Y-%m-%d %H:%M:%S");
  238. //node_crc.append_attribute("timestamp") = ss.str().data();
  239. string strExportFile = pszExportDir;
  240. strExportFile = strExportFile + "/" + iedName + ".ccd";
  241. ccddoc.save_file(strExportFile.data());
  242. }
  243. /**
  244. * @brief 计算SCD CRC
  245. * @param mapCRC 各个IED的CRC值列表
  246. * @return CRC值
  247. */
  248. static string getScdCRC(map<string, string> mapCRC)
  249. {
  250. string SCDBuffer;
  251. for (auto it : mapCRC)
  252. SCDBuffer += it.second;
  253. int crc = 0xffffffff;
  254. crc = crcbyTable(crc, SCDBuffer);
  255. string crcValue = dec2hex(crc);
  256. for (size_t i = crcValue.size(); i < 8; i++)
  257. crcValue.insert(0, "0");
  258. return crcValue;
  259. }
  260. /**
  261. * @brief 计算字符串的CRC值
  262. * @param crc 初始值
  263. * @param data 字符串
  264. * @return CRC值
  265. */
  266. static unsigned int crcbyTable(unsigned int crc, string data) {
  267. unsigned int table[256] = {
  268. 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
  269. 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
  270. 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
  271. 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
  272. 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
  273. 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
  274. 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
  275. 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
  276. 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
  277. 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
  278. 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
  279. 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
  280. 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
  281. 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
  282. 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
  283. 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
  284. 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
  285. 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
  286. 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
  287. 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
  288. 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
  289. 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
  290. 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
  291. 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
  292. 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
  293. 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
  294. 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
  295. 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
  296. 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
  297. 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
  298. 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
  299. 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d };
  300. size_t length = data.length();
  301. unsigned char ch;
  302. for (int i = 0; i < length; i++) {
  303. ch = data.at(i);
  304. crc = (crc >> 8) ^ table[(crc ^ ch) & 0xff];
  305. }
  306. crc = crc ^ 0xffffffff;
  307. return crc;
  308. }
  309. //将int转成16进制字符串
  310. static string dec2hex(int i)
  311. {
  312. stringstream ioss; //定义字符串流
  313. string s_temp; //存放转化后字符
  314. ioss << setiosflags(ios::uppercase) << hex << i; //以十六制(大写)形式输出
  315. ioss >> s_temp;
  316. return s_temp;
  317. }
  318. private:
  319. /**
  320. * @brief 创建 PubAndSubNode
  321. * @param root_ccd CCD根节点
  322. * @param root_scd SCD根节点
  323. * @param node_IED IED根节点
  324. * @return 是否存在内容
  325. */
  326. static bool createPubAndSubNode(xml_node root_ccd, xml_node root_scd, xml_node node_ied)
  327. {
  328. xml_node nodeGoosePub = root_ccd.append_child("GOOSEPUB");
  329. xml_node nodeGooseSub = root_ccd.append_child("GOOSESUB");
  330. xml_node nodeSvPub = root_ccd.append_child("SVPUB");
  331. xml_node nodeSvSub = root_ccd.append_child("SVSUB");
  332. bool emptyflag = createPublishNode(nodeGoosePub, nodeSvPub, root_scd, node_ied);
  333. emptyflag &= createSubscriptionNode(nodeGooseSub, nodeSvSub, root_scd, node_ied);
  334. //内容为空时删除
  335. if (nodeGoosePub.first_child().empty())
  336. root_ccd.remove_child(nodeGoosePub);
  337. if (nodeSvPub.first_child().empty())
  338. root_ccd.remove_child(nodeSvPub);
  339. if (nodeGooseSub.first_child().empty())
  340. root_ccd.remove_child(nodeGooseSub);
  341. if (nodeSvSub.first_child().empty())
  342. root_ccd.remove_child(nodeSvSub);
  343. return emptyflag;
  344. }
  345. /**
  346. * @brief 创建 GOOSEPUB和SVPUB
  347. * @param nodeGoosePub GOOSEPUB节点
  348. * @param nodeSvPub SVPUB节点
  349. * @param node_scd SCD根节点
  350. * @param node_ied IED根节点
  351. * @return 是否存在内容
  352. */
  353. static bool createPublishNode(xml_node nodeGoosePub, xml_node nodeSvPub, xml_node node_scd, xml_node node_ied)
  354. {
  355. bool emptyFlag = true;
  356. string iedName = node_ied.attribute("name").as_string();
  357. vector<xml_node> vecAcceptPoint = getChildElements(node_ied, "AccessPoint");
  358. //变量IED下访问的
  359. for (xml_node node_AcceptPoint : vecAcceptPoint)
  360. {
  361. xpath_node_set setControl_goose = node_AcceptPoint.select_nodes("./Server/LDevice/LN0/GSEControl");
  362. xpath_node_set setControl_sample = node_AcceptPoint.select_nodes("./Server/LDevice/LN0/SampledValueControl");
  363. //GOOSE或SV控制块存在时,则认为发布内容存在
  364. if (!setControl_goose.empty() || !setControl_sample.empty())
  365. emptyFlag = false;
  366. addControlForPub(nodeGoosePub, setControl_goose, eGoose, node_scd);
  367. addControlForPub(nodeSvPub, setControl_sample, eSV, node_scd);
  368. }
  369. return emptyFlag;
  370. }
  371. /**
  372. * @brief 创建 GOOSEPUB/SVPUB
  373. * @param nodePub_CCD GOOSEPUB或SVPUB节点
  374. * @param setControl GSE/SMV访问点
  375. * @param pubType GSE/SMV
  376. * @param node_scd SCD根节点
  377. * @return void
  378. */
  379. static void addControlForPub(xml_node nodePub_CCD, xpath_node_set setControl, eType pubType, xml_node node_scd)
  380. {
  381. string refnameAttr, instAttr, nameAttr, datSetAttr, iedName, apName;
  382. for (int jj = 0; jj < setControl.size(); jj++)
  383. {
  384. xml_node node_Control = setControl[jj].node();
  385. xml_node node_LN0 = node_Control.parent();
  386. xml_node node_LDvice = node_LN0.parent();
  387. xml_node node_Server = node_LDvice.parent();
  388. xml_node node_AccessPoint = node_Server.parent();
  389. xml_node node_IED = node_AccessPoint.parent();
  390. string nameAttr = node_Control.attribute("name").as_string();
  391. string datSetAttr = node_Control.attribute("datSet").as_string();
  392. string instAttr = node_LDvice.attribute("inst").as_string();
  393. string apName = node_AccessPoint.attribute("name").as_string();
  394. string iedName = node_IED.attribute("name").as_string();
  395. string strCBName;
  396. //计算控制块refernece
  397. if (pubType == eGoose)
  398. {
  399. strCBName = "GOCBref";
  400. refnameAttr = iedName + instAttr + "/LLN0$GO$" + nameAttr;
  401. }
  402. else if (pubType == eSV)
  403. {
  404. strCBName = "SMVCBref";
  405. refnameAttr = iedName + instAttr + "/LLN0$MS$" + nameAttr;
  406. }
  407. xml_node nodeCBref = nodePub_CCD.append_child(strCBName.data());
  408. nodeCBref.append_attribute("name") = refnameAttr.data();
  409. //添加GSEControl/SampledValueControl
  410. insertNodeSorted(nodeCBref, node_Control);
  411. //添加ConnectedAP
  412. addConnectedAPForPub(node_scd, nodeCBref, iedName, apName, nameAttr, instAttr, pubType);
  413. //添加数据集
  414. addFCDAForPub(node_scd, node_LN0, nodeCBref, datSetAttr, pubType);
  415. }
  416. }
  417. /**
  418. * @brief 创建 GOOSEPUB/SVPUB的ConnectedAP
  419. * @param node_scd scd根节点
  420. * @param nodeCBref GOCBref或SMVCBref
  421. * @param iedName apName nameAttrldinst 索引属性
  422. * @param pubType GOOSE/SV
  423. */
  424. static void addConnectedAPForPub(xml_node node_scd, xml_node nodeCBref, string iedName, string apName, string nameAttr, string ldinst, eType pubType)
  425. {
  426. string pathCAP;
  427. if (pubType == eGoose)
  428. {
  429. pathCAP = SFormat("/SCL/Communication/SubNetwork/ConnectedAP[@iedName='{0}' and @apName='{1}']/GSE[@cbName='{2}' and @ldInst='{3}']",
  430. iedName, apName, nameAttr, ldinst);
  431. }
  432. else if (pubType == eSV)
  433. {
  434. pathCAP = SFormat("/SCL/Communication/SubNetwork/ConnectedAP[@iedName='{0}' and @apName='{1}']/SMV[@cbName='{2}' and @ldInst='{3}']",
  435. iedName, apName, nameAttr, ldinst);
  436. }
  437. xpath_node_set setConnectedAP = node_scd.select_nodes(pathCAP.data());
  438. if (!setConnectedAP.empty())
  439. {
  440. //添加ConnectedAP
  441. xml_node node_ConnectedAP = setConnectedAP[0].parent();
  442. insertNodeSorted(nodeCBref, node_ConnectedAP);
  443. xml_node ConnectedAP = nodeCBref.child("ConnectedAP");
  444. ConnectedAP.remove_attribute("desc");
  445. //删除ConnectedAP中其他的控制块
  446. xpath_node_set GSE_SMVSet;
  447. if (pubType == eGoose)
  448. GSE_SMVSet = ConnectedAP.select_nodes(SFormat("GSE[@cbName!='{0}' or @ldInst!='{1}']", nameAttr, ldinst).data());
  449. else if (pubType == eSV)
  450. GSE_SMVSet = ConnectedAP.select_nodes(SFormat("SMV[@cbName!='{0}' or @ldInst!='{1}']", nameAttr, ldinst).data());
  451. for (int i = 0; i < GSE_SMVSet.size(); i++)
  452. removeChildNode(GSE_SMVSet[i].node());
  453. xpath_node_set AddressSet = ConnectedAP.select_nodes("./Address");
  454. for (int i = 0; i < AddressSet.size(); i++)
  455. removeChildNode(AddressSet[i].node());
  456. }
  457. }
  458. /**
  459. * @brief GOOSEPUB/SVPUB的dataset和FCDA
  460. * @param node_scd scd根节点
  461. * @param node_LN0 数据集归属LN0
  462. * @param nodeCBref GOCBref或SMVCBref
  463. * @param datSetAttr 数据集名称
  464. * @param pubType GOOSE/SV
  465. */
  466. static void addFCDAForPub(xml_node node_scd, xml_node node_LN0, xml_node nodeCBref, string datSetAttr, eType pubType)
  467. {
  468. string pathDataSet = SFormat("./DataSet[@name='{0}']", datSetAttr);
  469. xpath_node_set setDataSet = node_LN0.select_nodes(pathDataSet.data());
  470. if (setDataSet.empty())
  471. return;
  472. xml_node nodeDataTypeTemplate = node_scd.child("DataTypeTemplates");
  473. xml_node node_LDvice = node_LN0.parent();
  474. xml_node node_DataSet = setDataSet[0].node();
  475. vector<xml_node> vec_FCDA = getChildElements(node_DataSet, "FCDA");
  476. xml_node nodeDataSet = nodeCBref.append_child("DataSet");
  477. insertNodeAttr(node_DataSet, nodeDataSet);
  478. string prefix, lnInst, lnClass, doname, daName;
  479. for (xml_node node_FCDA : vec_FCDA)
  480. {
  481. prefix = node_FCDA.attribute("prefix").as_string();
  482. lnInst = node_FCDA.attribute("lnInst").as_string();
  483. lnClass = node_FCDA.attribute("lnClass").as_string();
  484. doname = node_FCDA.attribute("doName").as_string();
  485. daName = node_FCDA.attribute("daName").as_string();
  486. xml_node nodeFCDA = nodeDataSet.append_child("FCDA");
  487. vector<string> donamelist = stringSplit(doname, '.');
  488. if (donamelist.size() == 0)
  489. continue;
  490. //FCDA属性
  491. map<string, string> mapAttrs = getNodeAttributes(node_FCDA);
  492. if (daName != "")
  493. mapAttrs["bType"] = calc_bType(daName, doname, prefix, lnInst, lnClass, node_LDvice, nodeDataTypeTemplate);
  494. string pathDOI = SFormat("./LN0[not(@prefix) or @prefix='{0}'][@inst='{1}'][@lnClass='{2}']/DOI[@name='{3}']|./LN[not(@prefix) or @prefix='{0}'][@inst='{1}'][@lnClass='{2}']/DOI[@name='{3}']",
  495. prefix, lnInst, lnClass, donamelist.at(0));
  496. if (!prefix.empty())
  497. replace_all(pathDOI, "not(@prefix) or ", "");
  498. if ("" == lnInst)
  499. replace_all(pathDOI, "[@lnInst='']", "");
  500. if (daName.empty()) //正常SV
  501. {
  502. xpath_node_set set_DOI = node_LDvice.select_nodes(pathDOI.data());
  503. if (set_DOI.size() > 0) {
  504. mapAttrs["desc"] = set_DOI[0].node().attribute("desc").as_string();
  505. insertNodeSorted(nodeFCDA, set_DOI[0].node());
  506. }
  507. }
  508. else//正常GOOSE
  509. {
  510. xpath_node_set set_DOI = node_LDvice.select_nodes(pathDOI.data());
  511. if (set_DOI.size() > 0) {
  512. xml_node node_DOI = set_DOI[0].node();
  513. mapAttrs["desc"] = node_DOI.attribute("desc").as_string();
  514. //考虑doname中有.的情况
  515. string pathDAI = string(".");
  516. for (int i = 1; i < donamelist.size(); i++)
  517. {
  518. string xpath = SFormat("/SDI[@name='{0}']", donamelist.at(i));
  519. pathDAI += xpath;
  520. }
  521. vector<string> danamelist = stringSplit(daName, '.');
  522. for (int i = 0; i < danamelist.size(); i++)
  523. {
  524. if (i == danamelist.size() - 1)
  525. pathDAI += SFormat("/DAI[@name='{0}']", danamelist.at(i));
  526. else
  527. pathDAI += SFormat("/SDI[@name='{0}']", danamelist.at(i));
  528. }
  529. xpath_node_set set_DAI = node_DOI.select_nodes(pathDAI.data());
  530. if (set_DAI.size() > 0)
  531. {
  532. xml_node node_DAI = set_DAI[0].node();
  533. string daidesc = node_DAI.attribute("desc").as_string();
  534. if (!daidesc.empty())
  535. mapAttrs["desc"] = daidesc;
  536. insertNodeSorted(nodeFCDA, node_DAI);
  537. }
  538. }
  539. }
  540. for (auto& it : mapAttrs)
  541. {
  542. if (it.first.data() == "dU" || it.first.data() == "desc")
  543. continue;
  544. nodeFCDA.append_attribute(it.first.data()) = it.second.data();
  545. }
  546. }
  547. }
  548. /**
  549. * @brief 创建 GOOSESUB和SVSUB
  550. * @param nodeGooseSub GOOSESUB节点
  551. * @param nodeSvSub SVSUB节点
  552. * @param node_scd SCD根节点
  553. * @param node_ied IED根节点
  554. * @return 是否存在内容
  555. */
  556. static bool createSubscriptionNode(xml_node nodeGooseSub, xml_node nodeSvSub, xml_node node_scd, xml_node node_ied)
  557. {
  558. xml_node nodeDataTypeTemplate = node_scd.child("DataTypeTemplates");
  559. //遍历虚回路
  560. string path_ExtRef = "./AccessPoint/Server/LDevice/*[self::LN0 or self::LN]/Inputs/ExtRef[@intAddr!='']";
  561. xpath_node_set set_ExtRef = node_ied.select_nodes(path_ExtRef.data());
  562. if (set_ExtRef.empty())
  563. return true;
  564. multimap<string, xml_node> mapInputs;
  565. //记录已添加控制块标识
  566. vector<string> GOCBrefName_GOOSE, GOCBrefName_SV;
  567. string refnameAttr;
  568. for (int d = 0; d < set_ExtRef.size(); d++)
  569. {
  570. xml_node node_ExtRef = set_ExtRef[d].node();
  571. string key = string(node_ExtRef.attribute("prefix").as_string()) +
  572. node_ExtRef.attribute("ldInst").as_string() +
  573. node_ExtRef.attribute("iedName").as_string() +
  574. node_ExtRef.attribute("lnClass").as_string() +
  575. node_ExtRef.attribute("lnInst").as_string() +
  576. node_ExtRef.attribute("doName").as_string() +
  577. node_ExtRef.attribute("daName").as_string();
  578. mapInputs.insert(make_pair(key, node_ExtRef));
  579. }
  580. //遍历关联记录
  581. for (int d = 0; d < set_ExtRef.size(); d++)
  582. {
  583. xml_node node_ExtRef = set_ExtRef[d].node();
  584. string iedNameAttr = node_ExtRef.attribute("iedName").as_string();
  585. string ldInstAttr = node_ExtRef.attribute("ldInst").as_string();
  586. string prefixAttr = node_ExtRef.attribute("prefix").as_string();
  587. string lnInstAttr = node_ExtRef.attribute("lnInst").as_string();
  588. string lnClassAttr = node_ExtRef.attribute("lnClass").as_string();
  589. string doNameAttr = node_ExtRef.attribute("doName").as_string();
  590. string daNameAttr = node_ExtRef.attribute("daName").as_string();
  591. string key = iedNameAttr + ldInstAttr;
  592. //查找关联的IED
  593. string path_PubLDevice = SFormat("/SCL/IED[@name='{0}']/AccessPoint/Server/LDevice[@inst='{1}']", iedNameAttr, ldInstAttr);
  594. xpath_node_set set_PubLDevice = node_scd.select_nodes(path_PubLDevice.data());
  595. if (set_PubLDevice.empty())
  596. continue;
  597. //查找该关联IED下的FCDA信息
  598. xml_node node_PubLDevice = set_PubLDevice[0].node();
  599. string path_dataset;
  600. path_dataset = SFormat("../LDevice/LN0/DataSet/FCDA[@ldInst='{0}'][@prefix='{1}'][@lnClass='{2}'][@lnInst='{3}'][@doName='{4}'][@daName='{5}']",
  601. ldInstAttr, prefixAttr, lnClassAttr, lnInstAttr, doNameAttr, daNameAttr);
  602. if ("" == prefixAttr)
  603. replace_all(path_dataset, "[@prefix='']", "[not(@prefix) or @prefix='']");
  604. if ("" == lnInstAttr)
  605. replace_all(path_dataset, "[@lnInst='']", "[not(@lnInst) or @lnInst='']");
  606. if ("" == daNameAttr)
  607. replace_all(path_dataset, "[@daName='']", "");
  608. xpath_node_set set_Pubfcda = node_PubLDevice.select_nodes(path_dataset.data());
  609. //添加control
  610. addControlForSub(set_Pubfcda, nodeGooseSub, node_ied, eGoose, node_PubLDevice, GOCBrefName_GOOSE, mapInputs);
  611. addControlForSub(set_Pubfcda, nodeSvSub, node_ied, eSV, node_PubLDevice, GOCBrefName_SV, mapInputs);
  612. }
  613. return false;
  614. }
  615. /**
  616. * @brief 创建 GOOSESUB/SVSUB
  617. * @param set_Pubfcda 发布端数据集成员列表
  618. * @param nodeSub GOOSESUB或SVSUB
  619. * @param node_SubIED 订阅IED,即需要导出的IED
  620. * @param pubType GOOSE/SV
  621. * @param node_PubLDevice 发布端逻辑设备
  622. * @param GOCBrefName 已添加控制块标识
  623. * @param mapInputs ExtRef记录Map
  624. */
  625. static void addControlForSub(xpath_node_set set_Pubfcda, xml_node nodeSub, xml_node node_SubIED,
  626. eType pubType, xml_node node_PubLDevice, vector<string>& GOCBrefName, const multimap<string, xml_node>& mapInputs)
  627. {
  628. if (set_Pubfcda.empty())
  629. return;
  630. //根据该记录的数据块的名称,查找对应的控制块
  631. vector<xml_node> set_PubControlALL;
  632. vector<string> datasetNameAttrList;
  633. vector<string> datasetList;
  634. string strCtrlName = pubType == eGoose ? "GSEControl" : "SampledValueControl";
  635. //解析控制块信息
  636. for (int d = 0; d < set_Pubfcda.size(); d++)
  637. {
  638. xml_node node_Pubfcda = set_Pubfcda[d].node();
  639. string datasetNameAttr = node_Pubfcda.parent().attribute("name").as_string();
  640. if (count(datasetList.begin(), datasetList.end(), datasetNameAttr))
  641. continue;
  642. datasetList.push_back(datasetNameAttr);
  643. string pathControl = SFormat("./LN0/{0}[@datSet='{1}']", strCtrlName, datasetNameAttr);
  644. xpath_node_set set_PubControl = node_PubLDevice.select_nodes(pathControl.data());
  645. if (set_PubControl.empty())
  646. {
  647. pathControl = SFormat("../LDevice/{0}", pathControl);
  648. set_PubControl = node_PubLDevice.select_nodes(pathControl.data());
  649. }
  650. if (set_PubControl.empty())
  651. continue;
  652. xml_node node_PubControl = set_PubControl[0].node();
  653. if (!count(set_PubControlALL.begin(), set_PubControlALL.end(), node_PubControl))
  654. {
  655. set_PubControlALL.push_back(node_PubControl);
  656. datasetNameAttrList.push_back(datasetNameAttr);
  657. }
  658. }
  659. if (set_PubControlALL.empty())
  660. return;
  661. for (int i = 0; i < set_PubControlALL.size(); i++)
  662. {
  663. xml_node node_PubControl = set_PubControlALL.at(i);
  664. string datasetNameAttr = datasetNameAttrList.at(i);
  665. xml_node node_LN0 = node_PubControl.parent();
  666. node_PubLDevice = node_LN0.parent();
  667. xml_node node_Server = node_PubLDevice.parent();
  668. xml_node node_AccessPoint = node_Server.parent();
  669. xml_node node_IED = node_AccessPoint.parent();
  670. xml_node nodeRoot = node_IED.parent();
  671. string nameAttr = node_PubControl.attribute("name").as_string();
  672. string ldInstAttr = node_PubLDevice.attribute("inst").as_string();
  673. string iedNameAttr = node_IED.attribute("name").as_string();
  674. string refnameAttr = "";
  675. if (pubType == eSV)
  676. refnameAttr = iedNameAttr + ldInstAttr + "/LLN0$MS$" + nameAttr;
  677. else
  678. refnameAttr = iedNameAttr + ldInstAttr + "/LLN0$GO$" + nameAttr;
  679. //添加过则不再添加
  680. if (count(GOCBrefName.begin(), GOCBrefName.end(), refnameAttr))
  681. return;
  682. GOCBrefName.push_back(refnameAttr);
  683. //添加节点
  684. string strBrefName = pubType == eGoose ? "GOCBref" : "SMVCBref";
  685. xml_node nodeCBref = nodeSub.append_child(strBrefName.data());
  686. nodeCBref.append_attribute("name") = refnameAttr.data();
  687. insertNodeSorted(nodeCBref, node_PubControl);
  688. //去除Private属性
  689. xml_node GSEControl = nodeCBref.child(strCtrlName.data());
  690. xml_node privatenode = nodeCBref.child("Private");
  691. if (!privatenode.empty())
  692. GSEControl.remove_child(privatenode);
  693. //添加ConnectedAP
  694. addConnectedAPForSub(nodeRoot, nodeCBref, iedNameAttr, nameAttr, ldInstAttr, pubType);
  695. //添加DataSet和对应的每一条FCDA记录
  696. addFCDAForSub(nodeRoot, node_PubLDevice, node_SubIED, nodeCBref, datasetNameAttr, mapInputs, pubType);
  697. }
  698. }
  699. /**
  700. * @brief 创建 GOOSESUB/SVSUB的ConnectedAP
  701. * @param node_scd scd根节点
  702. * @param nodeCBref GOCBref或SMVCBref
  703. * @param iedName apName nameAttrldinst 索引属性
  704. * @param pubType GOOSE/SV
  705. */
  706. static void addConnectedAPForSub(xml_node node_scd, xml_node nodeCBref, string iedName,
  707. string cbName, string ldInstAttr, eType pubType)
  708. {
  709. //与发布相比,仅保留GSE/SMV子元素
  710. string strCtrlName = pubType == eGoose ? "GSE" : "SMV";
  711. string path_PubGS = SFormat("/SCL/Communication/SubNetwork/ConnectedAP[@iedName='{0}']/{1}[@cbName='{2}' and @ldInst='{3}']",
  712. iedName, strCtrlName, cbName, ldInstAttr);
  713. xpath_node_set set_PubGS = node_scd.select_nodes(path_PubGS.data());
  714. if (!set_PubGS.empty())
  715. {
  716. xml_node PubConnectedAP = nodeCBref.append_child("ConnectedAP");
  717. xml_node node_PubConnectedCtl = set_PubGS[0].node();
  718. insertNodeAttr(set_PubGS[0].node().parent(), PubConnectedAP);
  719. insertNodeSorted(PubConnectedAP, node_PubConnectedCtl);
  720. }
  721. }
  722. /**
  723. * @brief GOOSESUB/SVSUB的FCDA
  724. * @param node_scd scd根节点
  725. * @param node_PubLDevice 发布的LDevice节点
  726. * @param node_SubIED 订阅的IED节点
  727. * @param nodeCBref GOCBref或SMVCBref
  728. * @param datasetNameAttr 数据集名称
  729. * @param mapInputs ExtRef记录Map
  730. * @param pubType GOOSE/SV
  731. */
  732. static void addFCDAForSub(xml_node node_scd, xml_node node_PubLDevice, xml_node node_SubIED, xml_node nodeCBref, string datasetNameAttr,
  733. const multimap<string, xml_node>& hashInputs, eType pubType)
  734. {
  735. //添加DataSet
  736. string path_fcda = SFormat("./LN0/DataSet[@name='{0}']", datasetNameAttr);
  737. xpath_node_set node_PubDataSets = node_PubLDevice.select_nodes(path_fcda.data());
  738. if (node_PubDataSets.empty())
  739. return;
  740. xml_node node_PubDataSet = node_PubDataSets[0].node();
  741. xml_node nodeDataSet = nodeCBref.append_child("DataSet");
  742. insertNodeAttr(node_PubDataSet, nodeDataSet);
  743. //记录IED信息
  744. xml_node nodeDataTypeTemplate = node_scd.child("DataTypeTemplates");
  745. xml_node node_Server = node_PubLDevice.parent();
  746. xml_node node_AccessPoint = node_Server.parent();
  747. xml_node node_IED = node_AccessPoint.parent();
  748. string iedNameAttr = node_IED.attribute("name").as_string();
  749. //添加每一条FCDA
  750. vector<xml_node> vec_PubFCDA = getChildElements(node_PubDataSet, "FCDA");
  751. for (xml_node node_PubFCDA : vec_PubFCDA)
  752. {
  753. string prefix = node_PubFCDA.attribute("prefix").as_string();
  754. string ldInst = node_PubFCDA.attribute("ldInst").as_string();
  755. string lnInst = node_PubFCDA.attribute("lnInst").as_string();
  756. string lnClass = node_PubFCDA.attribute("lnClass").as_string();
  757. string doname = node_PubFCDA.attribute("doName").as_string();
  758. string daName = node_PubFCDA.attribute("daName").as_string();
  759. xml_node nodeFCDA = nodeDataSet.append_child("FCDA");
  760. //计算FCDA的bType
  761. string stype;
  762. map<string, string> mapAttrs = getNodeAttributes(node_PubFCDA);
  763. if (!daName.empty())
  764. mapAttrs["bType"] = calc_bType(daName, doname, prefix, lnInst, lnClass, node_PubLDevice, nodeDataTypeTemplate);
  765. vector<string> donamelist = stringSplit(doname, '.');
  766. if (!donamelist.empty())
  767. {
  768. string pathDOI = SFormat("./LN0[not(@prefix) or @prefix='{0}'][@inst='{1}'][@lnClass='{2}']/DOI[@name='{3}']|./LN[not(@prefix) or @prefix='{0}'][@inst='{1}'][@lnClass='{2}']/DOI[@name='{3}']",
  769. prefix, lnInst, lnClass, donamelist.at(0));
  770. if (!prefix.empty())
  771. replace_all(pathDOI, "not(@prefix) or ", "");
  772. if ("" == lnInst)
  773. replace_all(pathDOI, "[@lnInst='']", "");
  774. xpath_node_set set_DOI = node_PubLDevice.select_nodes(pathDOI.data());
  775. if (set_DOI.size() > 0) {
  776. xml_node node_DOI = set_DOI[0].node();
  777. mapAttrs["desc"] = node_DOI.attribute("desc").as_string();
  778. string pathDAI = string(".");
  779. for (int i = 1; i < donamelist.size(); i++)
  780. {
  781. string xpath = SFormat("/SDI[@name='{0}']", donamelist.at(i));
  782. pathDAI += xpath;
  783. }
  784. vector<string> danamelist = stringSplit(daName, '.');
  785. for (int i = 0; i < danamelist.size(); i++)
  786. {
  787. if (i == danamelist.size() - 1)
  788. pathDAI += SFormat("/DAI[@name='{0}']", danamelist.at(i));
  789. else
  790. pathDAI += SFormat("/SDI[@name='{0}']", danamelist.at(i));
  791. }
  792. xpath_node_set set_DAI = node_DOI.select_nodes(pathDAI.data());
  793. if (set_DAI.size() > 0)
  794. {
  795. xml_node node_DAI = set_DAI[0].node();
  796. string daidesc = node_DAI.attribute("desc").as_string();
  797. if (!daidesc.empty())
  798. mapAttrs["desc"] = daidesc;
  799. }
  800. }
  801. }
  802. for (auto& it : mapAttrs)
  803. {
  804. if (it.first.data() == "dU" || it.first.data() == "desc")
  805. continue;
  806. nodeFCDA.append_attribute(it.first.data()) = it.second.data();
  807. }
  808. //每一条FCDA记录在订阅装置中
  809. string mapKey = prefix + ldInst + iedNameAttr + lnClass + lnInst + doname + daName;
  810. auto node_hash_ExtRefSet = hashInputs.equal_range(mapKey);
  811. for (auto k = node_hash_ExtRefSet.first; k != node_hash_ExtRefSet.second; k++)
  812. {
  813. xml_node node_hash_ExtRef = k->second;
  814. string intAddrAttr = node_hash_ExtRef.attribute("intAddr").as_string();
  815. vector<string> lstTemp = stringSplit(intAddrAttr, '.');
  816. if (lstTemp.size() < 2)
  817. {
  818. if (!nodeFCDA.child("intAddr").empty())
  819. {
  820. xml_node node_intAddr = nodeFCDA.append_child("intAddr");
  821. node_intAddr.append_attribute("desc") = "";
  822. node_intAddr.append_attribute("name") = "";
  823. }
  824. continue;
  825. }
  826. string strLD_LN = lstTemp.at(0);
  827. if (strLD_LN.find(':') != string::npos)
  828. {
  829. strLD_LN = stringSplit(strLD_LN, ':').at(1);
  830. }
  831. string LDevice_name = stringSplit(strLD_LN, '/').at(0);
  832. string LN_prefix_lnClass_inst = stringSplit(strLD_LN, '/').at(1);;
  833. string DOI_name = lstTemp.at(1);
  834. vector<string> daList;
  835. for (int j = 2; j < lstTemp.size(); j++)
  836. daList.push_back(lstTemp.at(j));
  837. xml_node node_intAddr = nodeFCDA.append_child("intAddr");
  838. string pathLDevice = SFormat("./AccessPoint/Server/LDevice[@inst='{0}']", LDevice_name);
  839. xpath_node_set set_LDevice = node_SubIED.select_nodes(pathLDevice.data());
  840. if (set_LDevice.empty())
  841. continue;
  842. vector<xml_node> vec_LN = getChildElements(set_LDevice[0].node(), "LN");
  843. xml_node vec_LN0 = set_LDevice[0].node().child("LN0");
  844. vec_LN.push_back(vec_LN0);
  845. //ln0合并
  846. for (xml_node node_LN : vec_LN)
  847. {
  848. string LN_pll = string(node_LN.attribute("prefix").as_string()) +
  849. node_LN.attribute("lnClass").as_string() +
  850. node_LN.attribute("inst").as_string();
  851. if (LN_pll == LN_prefix_lnClass_inst)
  852. {
  853. string pathDOI = SFormat("./DOI[@name='{0}']", DOI_name);
  854. xpath_node_set set_DOI = node_LN.select_nodes(pathDOI.data());
  855. if (set_DOI.size() > 0)
  856. {
  857. xml_node node_DOI = set_DOI[0].node();
  858. if (daList.empty()) //SV
  859. {
  860. node_intAddr.append_attribute("desc") = node_DOI.attribute("desc").as_string();
  861. node_intAddr.append_attribute("name") = intAddrAttr.data();
  862. insertNodeSorted(node_intAddr, set_DOI[0].node());
  863. }
  864. else //GOOSE
  865. {
  866. string pathDAI = string(".");
  867. for (int i = 0; i < daList.size(); i++)
  868. {
  869. string xpath = SFormat("/SDI[@name='{0}']", daList.at(i));
  870. if (i == daList.size() - 1)
  871. xpath = SFormat("/DAI[@name='{0}']", daList.at(i));
  872. pathDAI += xpath;
  873. }
  874. xpath_node_set set_DAI = node_DOI.select_nodes(pathDAI.data());
  875. if (set_DAI.size() > 0)
  876. {
  877. xml_node node_DAI = set_DAI[0].node();
  878. string strDaiDesc = node_DAI.attribute("desc").as_string();
  879. if(!strDaiDesc.empty())
  880. node_intAddr.append_attribute("desc") = strDaiDesc.data();
  881. else
  882. node_intAddr.append_attribute("desc") = node_DOI.attribute("desc").as_string();
  883. node_intAddr.append_attribute("name") = intAddrAttr.data();
  884. insertNodeSorted(node_intAddr, node_DAI);
  885. }
  886. else
  887. {
  888. node_intAddr.append_attribute("desc") = node_DOI.attribute("desc").as_string();
  889. node_intAddr.append_attribute("name") = intAddrAttr.data();
  890. }
  891. }
  892. }
  893. }
  894. }
  895. }
  896. if (hashInputs.count(mapKey) == 0)
  897. {
  898. xml_node node_intAddr = nodeFCDA.append_child("intAddr");
  899. node_intAddr.append_attribute("desc") = "";
  900. node_intAddr.append_attribute("name") = "NULL";
  901. continue;
  902. }
  903. }
  904. }
  905. //计算FCDA节点的 bType 属性
  906. static string calc_bType(string daName, string doname, string prefix, string lnInst, string lnClass,
  907. xml_node nodeLDevice, xml_node nodeDataTypeTemplate)
  908. {
  909. string bType = "BOOLEAN";
  910. string typeAttr = "";
  911. /*if (daName == "q") {
  912. bType = "Quality";
  913. }
  914. else if (daName == "t") {
  915. bType = "Timestamp";
  916. }
  917. else if (endWith(daName, ".f")) {
  918. bType = "FLOAT32";
  919. }
  920. else */ {
  921. if (!doname.empty()) {
  922. string lnType = "";
  923. string pathLN;
  924. pathLN = SFormat("./LN0[not(@prefix) or @prefix='{0}'][@inst='{1}'][@lnClass='{2}']|./LN[not(@prefix) or @prefix='{0}'][@inst='{1}'][@lnClass='{2}']",
  925. prefix, lnInst, lnClass);
  926. if (!prefix.empty())
  927. replace_all(pathLN, "not(@prefix) or ", "");
  928. if ("" == lnInst)
  929. replace_all(pathLN, "[@lnInst='']", "");
  930. xpath_node_set set_LN = nodeLDevice.select_nodes(pathLN.data());
  931. if (set_LN.size() > 0) {
  932. xml_node nodeLN = set_LN[0].node();
  933. lnType = nodeLN.attribute("lnType").as_string();
  934. if (doname.find('.') == string::npos) {
  935. string pathDTTDO = SFormat("./LNodeType[@id='{0}']/DO[@name='{1}']", lnType, doname);
  936. xpath_node_set setDTTDO = nodeDataTypeTemplate.select_nodes(pathDTTDO.data());
  937. if (setDTTDO.size() > 0) {
  938. typeAttr = setDTTDO[0].node().attribute("type").as_string();
  939. }
  940. }
  941. else {
  942. vector<string> DOList = stringSplit(doname, '.');
  943. string pathDTTDO = SFormat("./LNodeType[@id='{0}']/DO[@name='{1}']", lnType, DOList.at(0));
  944. xpath_node_set setDTTDO = nodeDataTypeTemplate.select_nodes(pathDTTDO.data());
  945. if (setDTTDO.size() > 0) {
  946. typeAttr = setDTTDO[0].node().attribute("type").as_string();
  947. for (int kk = 1; kk < DOList.size(); kk++) {
  948. string pathDTTSDO = SFormat("./DOType[@id='{0}']/SDO[@name='{1}']", typeAttr, DOList.at(kk));
  949. xpath_node_set setDTTSDO = nodeDataTypeTemplate.select_nodes(pathDTTSDO.data());
  950. if (setDTTSDO.empty()) {
  951. break;
  952. }
  953. typeAttr = setDTTSDO[0].node().attribute("type").as_string();
  954. }
  955. }
  956. }
  957. if (daName.find('.') == string::npos) {
  958. string pathDTTDA = SFormat("./DOType[@id='{0}']/DA[@name='{1}']", typeAttr, daName);
  959. xpath_node_set setDTTDA = nodeDataTypeTemplate.select_nodes(pathDTTDA.data());
  960. if (setDTTDA.size() > 0) {
  961. bType = setDTTDA[0].node().attribute("bType").as_string();
  962. }
  963. }
  964. else {
  965. vector<string> DAList = stringSplit(daName, '.');
  966. string pathDTTDA = SFormat("./DOType[@id='{0}']/DA[@name='{1}']", typeAttr, DAList.at(0));
  967. xpath_node_set setDTTDA = nodeDataTypeTemplate.select_nodes(pathDTTDA.data());
  968. if (setDTTDA.size() > 0) {
  969. typeAttr = setDTTDA[0].node().attribute("type").as_string();
  970. for (int kk = 1; kk < DAList.size(); kk++) {
  971. string pathDTTBDA = SFormat("./DAType[@id='{0}']/BDA[@name='{1}']", typeAttr, DAList.at(kk));
  972. xpath_node_set setDTTBDA = nodeDataTypeTemplate.select_nodes(pathDTTBDA.data());
  973. if (setDTTBDA.empty()) {
  974. break;
  975. }
  976. if (kk == DAList.size() - 1) {
  977. bType = setDTTBDA[0].node().attribute("bType").as_string();
  978. }
  979. else {
  980. typeAttr = setDTTBDA[0].node().attribute("type").as_string();
  981. }
  982. }
  983. }
  984. }
  985. }
  986. }
  987. }
  988. return bType;
  989. }
  990. static void cutNodeDesc(xml_node node_ied)
  991. {
  992. if (node_ied.empty())
  993. return;
  994. string strIedName = node_ied.attribute("name").as_string();
  995. node_ied.remove_attributes();
  996. node_ied.append_attribute("name") = strIedName.data();
  997. xpath_node_set pubfcdanodeset = node_ied.select_nodes("./GOOSEPUB/GOCBref/DataSet/FCDA|./SVPUB/SMVCBref/DataSet/FCDA");
  998. xpath_node_set subfcdanodeset = node_ied.select_nodes("./GOOSESUB/GOCBref/DataSet/FCDA|./SVSUB/SMVCBref/DataSet/FCDA");
  999. for (int i = 0; i < pubfcdanodeset.size(); i++)
  1000. pubfcdanodeset[i].node().remove_attribute("desc");
  1001. for (int i = 0; i < subfcdanodeset.size(); i++)
  1002. {
  1003. xml_node node_fcda = subfcdanodeset[i].node();
  1004. string strBtype = node_fcda.attribute("bType").as_string();
  1005. subfcdanodeset[i].node().remove_attributes();
  1006. if (!strBtype.empty())
  1007. node_fcda.append_attribute("bType") = strBtype.data();
  1008. xpath_node_set intaddrset = node_fcda.select_nodes("./intAddr");
  1009. for (int j = 0; j < intaddrset.size(); j++)
  1010. intaddrset[j].node().remove_attribute("desc");
  1011. }
  1012. }
  1013. ///后续函数为部分基础处理函数,可忽略
  1014. private:
  1015. //按顺序插入节点
  1016. static void insertNodeSorted(xml_node parent, xml_node current)
  1017. {
  1018. if (parent.empty() || current.empty()) {
  1019. return;
  1020. }
  1021. if (node_element != current.type()) {
  1022. return;
  1023. }
  1024. //跳过dU
  1025. string nameAttr = current.attribute("name").as_string();
  1026. if (nameAttr == "dU") {
  1027. return;
  1028. }
  1029. xml_node nodeNew = parent.append_child(current.name());
  1030. insertNodeAttr(current, nodeNew);
  1031. string textCurr = current.text().as_string();
  1032. if (!textCurr.empty()) {
  1033. nodeNew.text().set(textCurr.data());
  1034. }
  1035. xml_node nodeChild = current.first_child();
  1036. xml_node nodeChildL = current.last_child();
  1037. while (!nodeChild.empty()) {
  1038. if (nodeChild.type() == pugi::node_element) {
  1039. insertNodeSorted(nodeNew, nodeChild);
  1040. }
  1041. if (nodeChild == nodeChildL) {
  1042. break;
  1043. }
  1044. else {
  1045. nodeChild = nodeChild.next_sibling();
  1046. }
  1047. }
  1048. }
  1049. //插入节点属性
  1050. static void insertNodeAttr(xml_node current, xml_node nodeNew)
  1051. {
  1052. map<string, string> mapAttrs = getNodeAttributes(current);
  1053. //map 会根据key值自动排序
  1054. for (auto& attr : mapAttrs)
  1055. {
  1056. if (attr.first == "desc" || attr.first == "dU") {
  1057. continue;
  1058. }
  1059. nodeNew.append_attribute(attr.first.data()) = attr.second.data();
  1060. }
  1061. }
  1062. //获取子元素列表
  1063. static vector<xml_node> getChildElements(xml_node node, string childName)
  1064. {
  1065. int i = 0;
  1066. vector<xml_node> vector;
  1067. for (pugi::xml_node_iterator it2 = node.children().begin();
  1068. it2 != node.children().end(); it2++)
  1069. {
  1070. xml_node n = *it2;
  1071. if (n.type() != pugi::node_element)
  1072. continue;
  1073. if (n.name() == childName)
  1074. {
  1075. vector.push_back(n);
  1076. }
  1077. i++;
  1078. }
  1079. return vector;
  1080. }
  1081. static bool removeChildNode(xml_node node)
  1082. {
  1083. xml_node pa_node = node.parent();
  1084. if (pa_node.empty())
  1085. return false;
  1086. return pa_node.remove_child(node);
  1087. }
  1088. //获取所有属性
  1089. static map<string, string> getNodeAttributes(xml_node node)
  1090. {
  1091. map<string, string> attrMap;
  1092. for (xml_attribute_iterator it1 = node.attributes().begin();
  1093. it1 != node.attributes().end(); ++it1)
  1094. {
  1095. xml_attribute attr = *it1;
  1096. attrMap[attr.name()] = attr.value();
  1097. }
  1098. return attrMap;
  1099. }
  1100. static vector<string> stringSplit(string str, char split)
  1101. {
  1102. vector<string> res;
  1103. istringstream iss(str); // 输入流
  1104. string token; // 接收缓冲区
  1105. while (getline(iss, token, split)) // 以split为分隔符
  1106. res.push_back(token);
  1107. return res;
  1108. }
  1109. static string& replace_all(string& src, const string& old_value, const string& new_value) {
  1110. // 每次重新定位起始位置,防止上轮替换后的字符串形成新的old_value
  1111. for (string::size_type pos(0); pos != string::npos; pos += new_value.length()) {
  1112. if ((pos = src.find(old_value, pos)) != string::npos) {
  1113. src.replace(pos, old_value.length(), new_value);
  1114. }
  1115. else break;
  1116. }
  1117. return src;
  1118. }
  1119. static bool endWith(const string& str, const string& tail) {
  1120. return str.compare(str.size() - tail.size(), tail.size(), tail) == 0;
  1121. }
  1122. static bool startWith(const string& str, const string& head) {
  1123. return str.compare(0, head.size(), head) == 0;
  1124. }
  1125. static string trim(string& text) {
  1126. if (!text.empty())
  1127. {
  1128. text.erase(0, text.find_first_not_of(" \n\r\t"));
  1129. text.erase(text.find_last_not_of(" \n\r\t") + 1);
  1130. }
  1131. return text;
  1132. }
  1133. static std::string remove_spaces_except_in_quotes(const std::string& input)
  1134. {
  1135. std::string output;
  1136. bool quote_flag = false; // 是否在引号内的标志
  1137. bool attr_flag = false; // 是否在<>内的标志
  1138. for (char c : input) // 遍历每个字符
  1139. {
  1140. if (c == '"') // 如果是引号
  1141. {
  1142. quote_flag = !quote_flag; // 切换引号标志
  1143. }
  1144. if (c == '<')
  1145. {
  1146. attr_flag = true;
  1147. }
  1148. if (c == '>')
  1149. {
  1150. attr_flag = false; // 切换引号标志
  1151. }
  1152. if (c != ' ' || quote_flag || !attr_flag) // 如果不是空格或者在引号内
  1153. {
  1154. output += c; // 保留字符
  1155. }
  1156. }
  1157. return output;
  1158. }
  1159. };