using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Windows.Forms; using System.Xml; using System.Xml.Linq; public static class SymbolParser { public static List ParseTypeList(string filePath) { XmlDocument doc = new XmlDocument(); doc.Load(filePath); // 1. 动态获取命名空间 string namespaceUri = doc.DocumentElement?.NamespaceURI ?? ""; // 2. 创建命名空间管理器 XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable); nsmgr.AddNamespace("ns", namespaceUri); var typeList = doc.SelectNodes("//ns:TypeList/*", nsmgr); if (typeList == null) { throw new Exception("无法找到 TypeList 节点,请检查XML结构或命名空间。"); } List types = new List(); // 解析类型定义 foreach (XmlNode typeNode in typeList) { var type= ParseTypeDefinition(typeNode, nsmgr); if(type!=null) { types.Add(type); } } return types; } private static TypeBase ParseTypeDefinition(XmlNode node, XmlNamespaceManager nsmgr) { string typeName = GetAttribute(node, "name"); if (typeName == null) return null; switch (node.Name) { case "TypeSimple": var typeSimple = new TypeSimple() { Name = GetAttribute(node, "name"), Size = int.Parse(GetAttribute(node, "size")), SwapSize = int.Parse(GetAttribute(node, "swapsize")), TypeClass = GetAttribute(node, "typeclass"), IecName = GetAttribute(node, "iecname"), BitOffset = int.Parse(string.IsNullOrEmpty(GetAttribute(node, "bitoffset")) ? "0" : GetAttribute(node, "bitoffset")) }; return typeSimple; case "TypeArray": var typeArray = new TypeArray() { Name = GetAttribute(node, "name"), Size = int.Parse(GetAttribute(node, "size")), NativeSize = int.Parse(GetAttribute(node, "nativesize")), TypeClass = GetAttribute(node, "typeclass"), IecName = GetAttribute(node, "iecname"), BaseType = GetAttribute(node, "basetype") }; var dimensions = GetArrayDimensions(node, nsmgr); typeArray.Rank = dimensions.Count; if (typeArray.Rank > 0) { typeArray.MinRange = new int[typeArray.Rank]; typeArray.MaxRange = new int[typeArray.Rank]; for (int i = 0; i < typeArray.Rank; i++) { typeArray.MinRange[i] = dimensions[i].Min; typeArray.MaxRange[i] = dimensions[i].Max; } } return typeArray; case "TypeUserDef": var typeUserDef = new TypeUserDef() { Name = GetAttribute(node, "name"), Size = int.Parse(GetAttribute(node, "size")), NativeSize = int.Parse(GetAttribute(node, "nativesize")), TypeClass = GetAttribute(node, "typeclass"), PouClass = GetAttribute(node, "pouclass"), IecName = GetAttribute(node, "iecname"), }; typeUserDef.UserDefElements = GetUserDefElement(node, nsmgr); return typeUserDef; default: return null; } } public static List<(int Min, int Max)> GetArrayDimensions(XmlNode arrayNode, XmlNamespaceManager nsmgr) { var result = new List<(int Min, int Max)>(); XmlNodeList dimNodes = arrayNode.SelectNodes("ns:ArrayDim", nsmgr); if (dimNodes == null) return result; foreach (XmlNode dim in dimNodes) { if (int.TryParse(dim.Attributes["minrange"]?.Value, out int min) && int.TryParse(dim.Attributes["maxrange"]?.Value, out int max)) { result.Add((min, max)); } } return result; } public static List GetUserDefElement(XmlNode arrayNode, XmlNamespaceManager nsmgr) { var result = new List(); XmlNodeList dimNodes = arrayNode.SelectNodes("ns:UserDefElement", nsmgr); if (dimNodes == null) return result; foreach (XmlNode dim in dimNodes) { var element = new UserDefElement { IecName = dim.Attributes["iecname"]?.Value, Type = dim.Attributes["type"].Value, //ByteOffset = uint.Parse(dim.Attributes["byteoffset"]?.Value), VarType = dim.Attributes["vartype"]?.Value, Access = dim.Attributes["access"]?.Value }; //if(element.Type!=GetAttribute(arrayNode, "name"))//如果一个类型的name和子类型的type相同,则不能添加进去,否则在添加Node时会出现一直递归,堆栈溢出 result.Add(element); } return result; } public static XmlVariable ParseNode(string filePath, List types) { XmlDocument doc = new XmlDocument(); doc.Load(filePath); // 1. 动态获取命名空间 string namespaceUri = doc.DocumentElement?.NamespaceURI ?? ""; // 2. 创建命名空间管理器 XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable); //nsmgr.AddNamespace("ns", "http://www.3s-software.com/schemas/Symbolconfiguration.xsd"); nsmgr.AddNamespace("ns", namespaceUri); // 使用命名空间前缀进行查询 XmlNode rootNode = doc.SelectSingleNode("//ns:NodeList/ns:Node", nsmgr); if (rootNode == null) { throw new Exception("无法找到 NodeList/Node 节点,请检查XML结构或命名空间。"); } //var varNode= ParseNode(rootNode); var varNode= ParseAllElementNode(rootNode, nsmgr, types); return varNode; } private static XmlVariable ParseAllElementNode(XmlNode xmlNode, XmlNamespaceManager nsmgr, List types) { var varNode = GetNodeValue(xmlNode, nsmgr); if (!varNode.IsLeaf) { foreach (XmlNode xmlChild in xmlNode.ChildNodes) { if (xmlChild.Name == "Node") { string strXmlChildNodeType = GetAttribute(xmlChild, "type"); if (string.IsNullOrEmpty(strXmlChildNodeType))//不是具体的数据,有子Node varNode.Children.Add(ParseAllElementNode(xmlChild, nsmgr, types)); else { var xmlChildNodeType = types.First(t => t.Name == strXmlChildNodeType);//找到具体是之前搜索到的具体哪个类型 if (xmlChildNodeType is TypeSimple)//简单数据类型,直接添加 { var subNode = GetNodeValue(xmlChild, nsmgr); varNode.Children.Add(subNode); } else if (xmlChildNodeType is TypeArray typeArray)//数组类型 { var subNode = GetNodeValue(xmlChild, nsmgr); subNode.IsLeaf = false; varNode.Children.Add(subNode);//添加数组名本身,然后再添加其下面的元素 int count = 1; for (int i = 0; i < typeArray.Rank; i++) { count *= (typeArray.MaxRange[i] + 1 - typeArray.MinRange[i]); } int[] cIndex = new int[typeArray.Rank]; for (int i = 0; i < count; i++) { string name = IndicesToString(GetNDElement(typeArray.Rank, typeArray.MinRange, typeArray.MaxRange, i)); subNode.Children.Add(ParseArrayAndUserDefElement(name, typeArray.BaseType, types)); } } else if (xmlChildNodeType is TypeUserDef typeUserDef)//结构体类型 { var subNode = GetNodeValue(xmlChild, nsmgr); subNode.IsLeaf = false; varNode.Children.Add(subNode);//添加结构体名本身,然后再添加其下面的元素 foreach (var userDefElement in typeUserDef.UserDefElements) { subNode.Children.Add(ParseArrayAndUserDefElement(userDefElement.IecName, userDefElement.Type, types)); } } } } } } return varNode; } private static int[] GetNDElement(int N, int[] MinRange, int[] MaxRange, int linearIndex) { // 1. 计算每个维度的长度 int[] lengths = new int[N]; for (int i = 0; i < N; i++) { lengths[i] = MaxRange[i] - MinRange[i] + 1; } int totalElements = 1; for (int i = 0; i < N; i++) { totalElements *= lengths[i]; } if (linearIndex < 0 || linearIndex >= totalElements) { throw new IndexOutOfRangeException("线性索引超出数组范围"); } // 2. 计算每个维度的步长(Stride) int[] strides = new int[N]; strides[N - 1] = 1; for (int i = N - 2; i >= 0; i--) { strides[i] = strides[i + 1] * lengths[i + 1]; } // 3. 还原每个维度的索引(加上偏移 MinRange[i]) int[] result = new int[N]; for (int i = 0; i < N; i++) { int index = (linearIndex / strides[i]) % lengths[i]; result[i] = MinRange[i] + index; } return result; } private static string IndicesToString(int[] indices) { if (indices == null || indices.Length == 0) return "[]"; return "[" + string.Join(",", indices) + "]"; } private static XmlVariable ParseArrayAndUserDefElement(string name, string baseType, List types) { var type = types.First(t => t.Name == baseType);//找到数组的基本类型是哪种类型(也即素组的元素是什么类型) if (type is TypeSimple typeSimple)//简单数据类型,直接添加 { var subNode = new XmlVariable() { Name = name, Type = typeSimple.Name, IsLeaf = true, }; return subNode; } else if (type is TypeArray typeArray) { var subNode = new XmlVariable(); subNode.Name = name; int count = 1; for (int i = 0; i < typeArray.Rank; i++) { count *= (typeArray.MaxRange[i] + 1 - typeArray.MinRange[i]); } int[] cIndex = new int[typeArray.Rank]; for (int i = 0; i < count; i++) { string n = IndicesToString(GetNDElement(typeArray.Rank, typeArray.MinRange, typeArray.MaxRange, i)); subNode.Children.Add(ParseArrayAndUserDefElement(n, typeArray.BaseType, types)); } return subNode;//添加数组名本身 } else if (type is TypeUserDef typeUserDef)//结构体类型 { var subNode = new XmlVariable(); subNode.Name = name; foreach (var userDefElement in typeUserDef.UserDefElements) { subNode.Children.Add(ParseArrayAndUserDefElement(userDefElement.IecName, userDefElement.Type, types)); } return subNode; } return null; } static XmlVariable GetNodeValue(XmlNode xmlNode, XmlNamespaceManager nsmgr) { var node = new XmlVariable { Name = GetAttribute(xmlNode, "name"), Type = GetAttribute(xmlNode, "type"), Access = GetAttribute(xmlNode, "access"), Comment= xmlNode.SelectSingleNode("ns:Comment", nsmgr)?.InnerText.Trim() ?? null, IsLeaf = xmlNode.SelectNodes("ns:Node", nsmgr).Count == 0 }; return node; } private static string GetAttribute(XmlNode node, string attrName) { XmlAttribute attr = node.Attributes?[attrName]; return attr?.Value ?? string.Empty; } }