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

382 lines
14 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
{
#region Variables
/// <summary>
/// 当设备报警状态发生变化时,调用此事件。
/// </summary>
public event Action<string, AlarmEventItem> OnDeviceAlarmStateChanged;
/// <summary>
/// 设备线程锁。
/// </summary>
protected readonly object SyncRoot;
/// <summary>
/// 报警信息字典。
/// </summary>
protected readonly Dictionary<string, AlarmEventItem> DicAlarms;
#endregion
#region Constructors
/// <summary>
/// 创建设备对象的实例。
/// </summary>
protected BaseDevice()
{
SyncRoot = new object();
ScBasePath = ModuleName.System.ToString();
IoBasePath = ModuleName.System.ToString();
DicAlarms = new Dictionary<string, AlarmEventItem>();
IsEnabled = true;
}
/// <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
#region Properties
/// <summary>
/// 设置或返回当前设备是否被使能。
/// </summary>
/// <remarks>
/// 默认情况下当设备被创建时自动使能但如果设备对应的Module被配置为UnInstalled则该设备
/// 会被其所属Module禁用以提高RT性能及避免产生不必要的报警信息。
/// </remarks>
public bool IsEnabled { get; set; }
/// <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; }
/// <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 = "")
{
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>
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>
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>
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 = "")
{
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>
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
{
var attribute = node.GetAttribute(name);
if (!string.IsNullOrEmpty(attribute) && !string.IsNullOrEmpty(attribute.Trim()))
{
return DEVICE.GetDevice<T>(module + "." + attribute);
}
LOG.Write($"{node.InnerXml},未定义{name}");
return null;
}
#endregion
}
}