Sic.Framework-Nanjing-Baishi/MECF.Framework.Common/Aitex/Core/RT/Device/BaseDevice.cs

382 lines
14 KiB
C#
Raw Normal View History

2023-04-13 11:51:03 +08:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using Aitex.Core.RT.Event;
using Aitex.Core.RT.IOCore;
using Aitex.Core.RT.Log;
using Aitex.Core.RT.SCCore;
using MECF.Framework.Common.Equipment;
using MECF.Framework.Common.Event;
namespace Aitex.Core.RT.Device
{
/// <summary>
/// 设备类型的基类。
/// </summary>
/// <remarks>
/// 该类描述了系统使用的设备对象。该设备对象为系统中最小控制单元所有模组Module通过此设备对象对实际的硬件设备进行控制。
/// </remarks>
public abstract class BaseDevice : IAlarmHandler
2023-04-13 11:51:03 +08:00
{
#region Variables
/// <summary>
/// 当设备报警状态发生变化时,调用此事件。
/// </summary>
public event Action<string, AlarmEventItem> OnDeviceAlarmStateChanged;
2023-04-13 11:51:03 +08:00
/// <summary>
/// 设备线程锁。
/// </summary>
protected readonly object SyncRoot;
2023-04-13 11:51:03 +08:00
/// <summary>
/// 报警信息字典。
/// </summary>
protected readonly Dictionary<string, AlarmEventItem> DicAlarms;
2023-04-13 11:51:03 +08:00
#endregion
2023-04-13 11:51:03 +08:00
#region Constructors
2023-04-13 11:51:03 +08:00
/// <summary>
/// 创建设备对象的实例。
/// </summary>
protected BaseDevice()
{
SyncRoot = new object();
ScBasePath = ModuleName.System.ToString();
IoBasePath = ModuleName.System.ToString();
DicAlarms = new Dictionary<string, AlarmEventItem>();
IsEnabled = true;
}
2023-04-13 11:51:03 +08:00
/// <summary>
/// 创建设备对象的实例。
/// </summary>
/// <param name="module">设备所属的模组名称。</param>
/// <param name="name">设备名称。</param>
/// <param name="display">设备在用户界面的显示名称。</param>
/// <param name="id">设备编号。</param>
protected BaseDevice(string module, string name, string display, string id) : this()
{
display = (string.IsNullOrEmpty(display) ? name : display);
id = (string.IsNullOrEmpty(id) ? name : id);
Module = module;
Name = name;
Display = display;
DeviceID = id;
UniqueName = module + "." + name;
}
/// <summary>
/// 根据XML的配置描述创建设备对象的实例。
/// </summary>
/// <param name="module">设备所属的模组名称。</param>
/// <param name="node">配置文件描述本设备的Xml节点。</param>
/// <param name="ioModule"></param>
protected BaseDevice(string module, XmlElement node, string ioModule = "") : this()
{
var attrModule = node.GetAttribute("module");
Module = string.IsNullOrEmpty(attrModule) ? module : attrModule;
Unit = node.GetAttribute("unit");
Name = node.GetAttribute("id");
Display = node.GetAttribute("display");
DeviceID = node.GetAttribute("schematicId");
UniqueName = module + "." + Name;
var scBasePath = node.GetAttribute("scBasePath");
scBasePath = string.IsNullOrEmpty(scBasePath) ?
$"{Module}.{Name}"
: scBasePath.Replace("{module}", Module);
ScBasePath = scBasePath;
}
#endregion
2023-04-13 11:51:03 +08:00
#region Properties
2023-04-13 11:51:03 +08:00
/// <summary>
/// 设置或返回当前设备是否被使能。
/// </summary>
/// <remarks>
/// 默认情况下当设备被创建时自动使能但如果设备对应的Module被配置为UnInstalled则该设备
/// 会被其所属Module禁用以提高RT性能及避免产生不必要的报警信息。
/// </remarks>
public bool IsEnabled { get; set; }
2023-04-13 11:51:03 +08:00
/// <summary>
/// 设置或返回当前设备在系统中唯一的名称。
/// </summary>
public string UniqueName { get; set; }
/// <summary>
/// 设置或返回当前设备所属的模组名称。
/// </summary>
/// <remarks>
/// 关于系统中有效的模组名称,请参考<see cref="ModuleName"/>。
/// </remarks>
public string Module { get; set; }
/// <summary>
/// 设置或返回当前设备名称。
/// </summary>
public string Name { get; set; }
2023-04-13 11:51:03 +08:00
/// <summary>
/// 设置或返回当前设备向用户显示的名称。
/// </summary>
public string Display { get; set; }
/// <summary>
/// 设置或返回当前设备编号。
/// </summary>
public string DeviceID { get; set; }
/// <summary>
/// 设置或返回当前设备的计量单位。
/// </summary>
public string Unit { get; set; }
/// <summary>
/// 设置或返回当前设备配置信息位于系统配置的节点名称。
/// </summary>
public string ScBasePath { get; set; }
/// <summary>
/// 设置或返回当前设备所使用的IO Provider。
/// </summary>
public string IoBasePath { get; set; }
/// <summary>
/// 设置或返回设备是否产生报警。
/// </summary>
public bool HasAlarm =>
DicAlarms?.Values.FirstOrDefault((AlarmEventItem x) => !x.IsAcknowledged && x.Level == EventLevel.Alarm) !=
null;
#endregion
#region Methods
/// <summary>
/// 订阅报警事件。
/// </summary>
/// <param name="name">报警事件名称。</param>
/// <param name="description">报警事件描述。</param>
/// <param name="resetChecker">报警复位检查回调函数。</param>
/// <param name="level">报警等级,请参考<see cref="EventLevel"/></param>
/// <returns></returns>
protected AlarmEventItem SubscribeAlarm(string name, string description, Func<bool> resetChecker, EventLevel level = EventLevel.Alarm)
{
var ae = new AlarmEventItem(Module, name, description, resetChecker, this);
ae.Level = level;
DicAlarms[name] = ae;
EV.Subscribe(ae);
return ae;
}
/// <summary>
/// 清除当前设备的报警信息。
/// </summary>
protected void ResetAlarm()
{
foreach (var alarm in DicAlarms)
alarm.Value.Reset();
}
/// <summary>
/// 当前对象的子类实现的扫描任务。
/// </summary>
protected virtual void HandleMonitor()
{
}
/// <summary>
/// 执行当前设备扫描周期任务。
/// </summary>
public void Monitor()
{
// 如果当前设备被标记为禁用,则不要执行周期性扫描任务。
if(IsEnabled)
HandleMonitor();
}
/// <summary>
/// 通知当前设备的报警信息发生了变化。
/// </summary>
/// <param name="args"></param>
public void AlarmStateChanged(AlarmEventItem args)
{
OnDeviceAlarmStateChanged?.Invoke(UniqueName ?? "", args);
}
/// <summary>
/// 从XML配置描述中解析指定的DO对象。
/// </summary>
/// <remarks>
/// 通过该方法从XML配置中获取指定的DO名称从系统点表中查找并返回指定名称的DO实例。
/// </remarks>
/// <param name="name">XML配置节点中定义DO的属性的名称。</param>
/// <param name="node">XML配置节点。</param>
/// <param name="ioModule">IO Provider名称。</param>
/// <returns>
/// 指定名称的<see cref="DOAccessor"/>对象。
/// <br/>
/// 如果未找到指定名称的DO则返回null。
/// </returns>
public DOAccessor ParseDoNode(string name, XmlElement node, string ioModule = "")
2023-04-13 11:51:03 +08:00
{
if (!string.IsNullOrEmpty(node.GetAttribute(name).Trim()))
{
return IO.DO[string.IsNullOrEmpty(ioModule) ? node.GetAttribute(name).Trim() : (ioModule + "." + node.GetAttribute(name).Trim())];
}
return null;
}
/// <summary>
/// 从XML配置描述中解析指定的DI对象。
/// </summary>
/// <remarks>
/// 通过该方法从XML配置中获取指定的DI名称从系统点表中查找并返回指定名称的DI实例。
/// </remarks>
/// <param name="name">XML配置节点中定义DI的属性的名称。</param>
/// <param name="node">XML配置节点。</param>
/// <param name="ioModule">IO Provider名称。</param>
/// <returns>
/// 指定名称的<see cref="DIAccessor"/>对象。
/// <br/>
/// 如果未找到指定名称的DI则返回null。
/// </returns>
2023-04-13 11:51:03 +08:00
public DIAccessor ParseDiNode(string name, XmlElement node, string ioModule = "")
{
if (!string.IsNullOrEmpty(node.GetAttribute(name).Trim()))
{
return IO.DI[string.IsNullOrEmpty(ioModule) ? node.GetAttribute(name).Trim() : (ioModule + "." + node.GetAttribute(name).Trim())];
}
return null;
}
/// <summary>
/// 从XML配置描述中解析指定的AO对象。
/// </summary>
/// <remarks>
/// 通过该方法从XML配置中获取指定的AO名称从系统点表中查找并返回指定名称的AO实例。
/// </remarks>
/// <param name="name">XML配置节点中定义AO的属性的名称。</param>
/// <param name="node">XML配置节点。</param>
/// <param name="ioModule">IO Provider名称。</param>
/// <returns>
/// 指定名称的<see cref="AOAccessor"/>对象。
/// <br/>
/// 如果未找到指定名称的AO则返回null。
/// </returns>
2023-04-13 11:51:03 +08:00
public AOAccessor ParseAoNode(string name, XmlElement node, string ioModule = "")
{
if (!string.IsNullOrEmpty(node.GetAttribute(name).Trim()))
{
return IO.AO[string.IsNullOrEmpty(ioModule) ? node.GetAttribute(name).Trim() : (ioModule + "." + node.GetAttribute(name).Trim())];
}
return null;
}
/// <summary>
/// 从XML配置描述中解析指定的AI对象。
/// </summary>
/// <remarks>
/// 通过该方法从XML配置中获取指定的AI名称从系统点表中查找并返回指定名称的AI实例。
/// </remarks>
/// <param name="name">XML配置节点中定义AI的属性的名称。</param>
/// <param name="node">XML配置节点。</param>
/// <param name="ioModule">IO Provider名称。</param>
/// <returns>
/// 指定名称的<see cref="AIAccessor"/>对象。
/// <br/>
/// 如果未找到指定名称的AI则返回null。
/// </returns>
2023-04-13 11:51:03 +08:00
public AIAccessor ParseAiNode(string name, XmlElement node, string ioModule = "")
{
if (!string.IsNullOrEmpty(node.GetAttribute(name).Trim()))
{
return IO.AI[string.IsNullOrEmpty(ioModule) ? node.GetAttribute(name).Trim() : (ioModule + "." + node.GetAttribute(name).Trim())];
}
return null;
}
/// <summary>
/// 从XML配置描述中解析当前设备配置信息位于系统配置中的节点。
/// </summary>
/// <param name="name">XML配置节点中定义系统配置节点的属性的名称。</param>
/// <param name="node">XML配置节点。</param>
/// <param name="ioModule">IO Provider名称。</param>
/// <param name="defaultScPath">如果未找到name指定的系统配置节点则使用此默认系统配置节点。</param>
/// <returns>
/// <see cref="SCConfigItem"/>。
/// </returns>
public SCConfigItem ParseScNode(string name, XmlElement node, string ioModule = "", string defaultScPath = "")
2023-04-13 11:51:03 +08:00
{
SCConfigItem sCConfigItem = null;
if (!string.IsNullOrEmpty(node.GetAttribute(name).Trim()))
{
sCConfigItem = SC.GetConfigItem(node.GetAttribute(name));
}
if (sCConfigItem == null && !string.IsNullOrEmpty(defaultScPath) && SC.ContainsItem(defaultScPath))
{
sCConfigItem = SC.GetConfigItem(defaultScPath);
}
return sCConfigItem;
}
/// <summary>
/// 从XML配置描述中解析设备名称并从系统设备列表中查找该设备。
/// </summary>
/// <typeparam name="T">待查找的设备的类型。</typeparam>
/// <param name="name">XML配置节点中定义设备名称的属性的名称。</param>
/// <param name="node">XML配置节点。</param>
/// <returns>
/// 指定的设备实例。
/// <br/>
/// 如果未找到指定的设备则返回null。
/// </returns>
2023-04-13 11:51:03 +08:00
public static T ParseDeviceNode<T>(string name, XmlElement node) where T : class, IDevice
{
if (!string.IsNullOrEmpty(node.GetAttribute(name).Trim()))
{
return DEVICE.GetDevice<T>(node.GetAttribute(name));
}
LOG.Write($"{node.InnerXml},未定义{name}");
return null;
}
/// <summary>
/// 从XML配置描述中解析设备名称并从系统设备列表中查找该设备。
/// </summary>
/// <typeparam name="T">待查找的设备的类型。</typeparam>
/// <param name="module">设备所属模组。</param>
/// <param name="name">XML配置节点中定义设备名称的属性的名称。</param>
/// <param name="node">XML配置节点。</param>
/// <returns>
/// 指定的设备实例。
/// <br/>
/// 如果未找到指定的设备则返回null。
/// </returns>
public static T ParseDeviceNode<T>(string module, string name, XmlElement node) where T : class, IDevice
2023-04-13 11:51:03 +08:00
{
var attribute = node.GetAttribute(name);
2023-04-13 11:51:03 +08:00
if (!string.IsNullOrEmpty(attribute) && !string.IsNullOrEmpty(attribute.Trim()))
{
return DEVICE.GetDevice<T>(module + "." + attribute);
}
LOG.Write($"{node.InnerXml},未定义{name}");
return null;
}
#endregion
}
2023-04-13 11:51:03 +08:00
}