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