using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml; using System.Xml.Linq; using XP.Hardware.PLC.Abstractions; using XP.Hardware.PLC.Models; namespace XP.Hardware.PLC.Helpers { /// /// XML 信号地址定义解析/序列化器 | XML signal address definition parser/serializer /// 负责 PlcAddrDfn.xml 的读写操作 | Responsible for reading/writing PlcAddrDfn.xml /// public class XmlSignalParser : IXmlSignalParser { /// /// 从文件加载信号分组列表 | Load signal group list from file /// /// XML 文件路径 | XML file path /// 信号分组列表 | Signal group list /// 文件不存在 | File not found /// XML 格式错误或 Group 缺少必要属性 | XML format error or Group missing required attributes public List LoadFromFile(string filePath) { // 文件不存在时抛出异常 | Throw exception if file does not exist if (!File.Exists(filePath)) { throw new FileNotFoundException($"XML 配置文件不存在 | XML config file not found: {filePath}", filePath); } XDocument doc; try { doc = XDocument.Load(filePath, LoadOptions.SetLineInfo); } catch (XmlException ex) { throw new XmlException($"XML 格式错误 | XML format error: {ex.Message}", ex); } var groups = new List(); // 遍历 下所有 子节点 | Iterate all child nodes under var groupElements = doc.Root?.Elements("Group") ?? Enumerable.Empty(); foreach (var groupElement in groupElements) { // 验证 Group 必须包含 ID 属性 | Validate Group must have ID attribute var idAttr = groupElement.Attribute("ID"); if (idAttr == null) { var lineInfo = (IXmlLineInfo)groupElement; throw new XmlException( $"Group 节点缺少 ID 属性 | Group node missing ID attribute. " + $"位置 | Position: Line {lineInfo.LineNumber}"); } // 验证 Group 必须包含 DBNumber 属性 | Validate Group must have DBNumber attribute var dbNumberAttr = groupElement.Attribute("DBNumber"); if (dbNumberAttr == null) { var lineInfo = (IXmlLineInfo)groupElement; throw new XmlException( $"Group 节点缺少 DBNumber 属性 | Group node missing DBNumber attribute. " + $"Group ID: {idAttr.Value}, " + $"位置 | Position: Line {lineInfo.LineNumber}"); } // 解析 DBNumber 为整数 | Parse DBNumber as integer if (!int.TryParse(dbNumberAttr.Value, out int dbNumber)) { var lineInfo = (IXmlLineInfo)groupElement; throw new XmlException( $"Group 节点 DBNumber 属性值无效(非整数)| Group node DBNumber attribute value is invalid (not an integer). " + $"Group ID: {idAttr.Value}, DBNumber: {dbNumberAttr.Value}, " + $"位置 | Position: Line {lineInfo.LineNumber}"); } string groupId = idAttr.Value; // 解析该 Group 下所有 Signal 子节点 | Parse all Signal child nodes under this Group var signals = new System.Collections.ObjectModel.ObservableCollection(); foreach (var signalElement in groupElement.Elements("Signal")) { var signal = new SignalEntry { // 使用 Name 属性作为信号名称 | Use Name attribute as signal name Name = signalElement.Attribute("Name")?.Value ?? string.Empty, Type = signalElement.Attribute("Type")?.Value ?? string.Empty, StartAddr = int.TryParse(signalElement.Attribute("StartAddr")?.Value, out var addr) ? addr : 0, IndexOrLength = (signalElement.Attribute("IndexOrLength")?.Value ?? string.Empty).Trim(), Remark = signalElement.Attribute("Remark")?.Value ?? string.Empty, // 将 Group 的 ID 和 DBNumber 赋值到 SignalEntry | Assign Group's ID and DBNumber to SignalEntry GroupId = groupId, DBNumber = dbNumber }; signals.Add(signal); } groups.Add(new SignalGroup { GroupId = groupId, DBNumber = dbNumber, Signals = signals }); } return groups; } /// /// 按 SignalGroup 结构保存信号配置到文件 | Save signal configuration to file by SignalGroup structure /// /// XML 文件路径 | XML file path /// 信号分组列表 | Signal group list public void SaveToFile(string filePath, List groups) { var doc = new XDocument( new XDeclaration("1.0", "utf-8", "yes"), new XElement("Config", groups.Select(group => new XElement("Group", new XAttribute("ID", group.GroupId ?? string.Empty), new XAttribute("DBNumber", group.DBNumber), group.Signals.Select(signal => new XElement("Signal", new XAttribute("Name", signal.Name ?? string.Empty), new XAttribute("Type", signal.Type ?? string.Empty), new XAttribute("StartAddr", signal.StartAddr.ToString()), new XAttribute("IndexOrLength", signal.IndexOrLength ?? string.Empty), new XAttribute("Remark", signal.Remark ?? string.Empty) )) )) ) ); doc.Save(filePath); } } }