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);
}
}
}