using System; using System.Collections.Generic; using System.ComponentModel; 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 void LoadSC(string scPath, string scPathFallback, T valueFallback, ref T valueHolder, Action scValueChangedCallback = null) { if (scPath == null) throw new ArgumentNullException(nameof(scPath)); if (scPathFallback == null) throw new ArgumentNullException(nameof(scPathFallback)); if (valueFallback == null) throw new ArgumentNullException(nameof(valueFallback)); if (valueHolder == null) throw new ArgumentNullException(nameof(valueHolder)); // 读取系统配置 var path = scPath; var scItem = SC.GetConfigItem(path); if (scItem == null) { // 没找到配置,用默认路径找 path = scPathFallback; scItem = SC.GetConfigItem(path); } if (scItem != null) { // 找到系统配置,尝试转换并赋值到指定的字段 try { var converter = TypeDescriptor.GetConverter(typeof(T)); valueHolder = (T)converter.ConvertFromString(scItem.Value.ToString()); // 如果指定了该回调,则表明需要监测当前系统配置值 if (scValueChangedCallback != null) { SC.RegisterValueChangedCallback(path, v => { var val = (T)converter.ConvertFromString(scItem.Value.ToString()); scValueChangedCallback(val); }); } } catch (Exception) { EV.PostWarningLog(Module, $"System config {path} type mismatched, set to default value"); valueHolder = valueFallback; } } else { // 指定路径和默认路径均找不到配置,使用默认配置 EV.PostWarningLog(Module, $"System config {path} type mismatched, set to default value"); valueHolder = valueFallback; } } /// /// 订阅报警事件。 /// /// 报警事件名称。 /// 报警事件描述。 /// 报警复位检查回调函数。 /// 报警等级,请参考 /// 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 } }