140 lines
6.5 KiB
C#
140 lines
6.5 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// XML 信号地址定义解析/序列化器 | XML signal address definition parser/serializer
|
|
/// 负责 PlcAddrDfn.xml 的读写操作 | Responsible for reading/writing PlcAddrDfn.xml
|
|
/// </summary>
|
|
public class XmlSignalParser : IXmlSignalParser
|
|
{
|
|
/// <summary>
|
|
/// 从文件加载信号分组列表 | Load signal group list from file
|
|
/// </summary>
|
|
/// <param name="filePath">XML 文件路径 | XML file path</param>
|
|
/// <returns>信号分组列表 | Signal group list</returns>
|
|
/// <exception cref="FileNotFoundException">文件不存在 | File not found</exception>
|
|
/// <exception cref="XmlException">XML 格式错误或 Group 缺少必要属性 | XML format error or Group missing required attributes</exception>
|
|
public List<SignalGroup> 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<SignalGroup>();
|
|
|
|
// 遍历 <Config> 下所有 <Group> 子节点 | Iterate all <Group> child nodes under <Config>
|
|
var groupElements = doc.Root?.Elements("Group") ?? Enumerable.Empty<XElement>();
|
|
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<SignalEntry>();
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 按 SignalGroup 结构保存信号配置到文件 | Save signal configuration to file by SignalGroup structure
|
|
/// </summary>
|
|
/// <param name="filePath">XML 文件路径 | XML file path</param>
|
|
/// <param name="groups">信号分组列表 | Signal group list</param>
|
|
public void SaveToFile(string filePath, List<SignalGroup> 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);
|
|
}
|
|
}
|
|
}
|