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
|
|
|
|
|
{
|
2023-05-09 16:01:15 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 设备类型的基类。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// 该类描述了系统使用的设备对象。该设备对象为系统中最小控制单元,所有模组(Module)通过此设备对象对实际的硬件设备进行控制。
|
|
|
|
|
/// </remarks>
|
2023-04-25 10:01:43 +08:00
|
|
|
|
public abstract class BaseDevice : IAlarmHandler
|
2023-04-13 11:51:03 +08:00
|
|
|
|
{
|
2023-05-09 16:01:15 +08:00
|
|
|
|
#region Variables
|
2023-05-08 14:59:31 +08:00
|
|
|
|
|
2023-05-09 16:01:15 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 当设备报警状态发生变化时,调用此事件。
|
|
|
|
|
/// </summary>
|
|
|
|
|
public event Action<string, AlarmEventItem> OnDeviceAlarmStateChanged;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-05-09 16:01:15 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 设备线程锁。
|
|
|
|
|
/// </summary>
|
|
|
|
|
protected readonly object SyncRoot;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-05-09 16:01:15 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 报警信息字典。
|
|
|
|
|
/// </summary>
|
|
|
|
|
protected readonly Dictionary<string, AlarmEventItem> DicAlarms;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-05-09 16:01:15 +08:00
|
|
|
|
#endregion
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-05-09 16:01:15 +08:00
|
|
|
|
#region Constructors
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-05-09 16:01:15 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 创建设备对象的实例。
|
|
|
|
|
/// </summary>
|
|
|
|
|
protected BaseDevice()
|
|
|
|
|
{
|
|
|
|
|
SyncRoot = new object();
|
2023-05-08 14:59:31 +08:00
|
|
|
|
ScBasePath = ModuleName.System.ToString();
|
2023-05-09 16:01:15 +08:00
|
|
|
|
IoBasePath = ModuleName.System.ToString();
|
|
|
|
|
DicAlarms = new Dictionary<string, AlarmEventItem>();
|
|
|
|
|
IsEnabled = true;
|
|
|
|
|
}
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-05-09 16:01:15 +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;
|
2023-04-19 15:38:04 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-05-09 16:01:15 +08:00
|
|
|
|
/// <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()
|
2023-04-19 15:38:04 +08:00
|
|
|
|
{
|
|
|
|
|
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");
|
2023-05-09 16:01:15 +08:00
|
|
|
|
scBasePath = string.IsNullOrEmpty(scBasePath) ?
|
|
|
|
|
$"{Module}.{Name}"
|
|
|
|
|
: scBasePath.Replace("{module}", Module);
|
|
|
|
|
|
2023-04-19 15:38:04 +08:00
|
|
|
|
ScBasePath = scBasePath;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-09 16:01:15 +08:00
|
|
|
|
#endregion
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-05-09 16:01:15 +08:00
|
|
|
|
#region Properties
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-05-09 16:01:15 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// 设置或返回当前设备是否被使能。
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// 默认情况下,当设备被创建时自动使能;但如果设备对应的Module被配置为UnInstalled,则该设备
|
|
|
|
|
/// 会被其所属Module禁用,以提高RT性能,及避免产生不必要的报警信息。
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public bool IsEnabled { get; set; }
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-05-09 16:01:15 +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
|
|
|
|
|
2023-05-09 16:01:15 +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;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-09 16:01:15 +08:00
|
|
|
|
/// <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;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-09 16:01:15 +08:00
|
|
|
|
/// <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;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-09 16:01:15 +08:00
|
|
|
|
/// <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;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-09 16:01:15 +08:00
|
|
|
|
/// <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;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-09 16:01:15 +08:00
|
|
|
|
/// <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;
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-09 16:01:15 +08:00
|
|
|
|
/// <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
|
|
|
|
{
|
2023-05-09 16:01:15 +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;
|
|
|
|
|
}
|
2023-05-09 16:01:15 +08:00
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
2023-04-13 11:51:03 +08:00
|
|
|
|
}
|