乐于分享
好东西不私藏

构建工业自动化软件框架:WPF界面开发(三) 报警管理

构建工业自动化软件框架:WPF界面开发(三) 报警管理

前言

上篇介绍了用户管理界面部分的内容,这篇就开始说一说关于报警处理和报警界面相关的内容。报警是一台上位机软件中最重要的部分,设备出现故障时必须要准确报警,才能即使处理,确保生产正常进行。我这边这次用

功能介绍

我在框架中将报警内容分为三个部分。

1、实时报警触发和显示,

当报警触发时,在界面右上角逐行悬浮显示。可以通过右键点击来清除当前报警。

2、历史报警显示,

软件启动后发生的所有报警都会被完整记录在内存队列中,同时可以通过时间筛选,可以用于后续故障追溯与数据分析。

3、报警注册。

所有报警代码与中文释义统一在此处进行注册。

所有的报警统一在报警注册界面中进行注册,如果报警触发时没有对应的报警代码,自动注册一个模板报警,然后手动再去修改内容即可。实时报警在右上角开始逐行显示,可以通过右击清除报警。

当前我只是完成了报警触发和显示部分的内容,还没有融入到状态机里面,这个后面做流程界面的时候再一起开发。下面是已经完成的三部分内容的界面显示。

代码实现

报警实体类

首先还是报警的实体类,包含报警代码,报警内容以及报警时间,注册报警也用的同一个类,后面的时间在注册时实际就是报警注册的时间,不过我在报警注册界面是没有显示时间的,但是config文件中会有体现。

public class AlarmItem{    public string AlarmCode { getset; }    public string Content { getset; }    public DateTime Time { getset; }}

报警仓储

之前做了关于数据库的仓储模式,包含了用户,产量等操作,后面做的时候,把配置文件也做成了仓储模式,这样会方便很多。

报警仓储中,包含了两个集合,分别是当前报警和注册报警。

public ObservableCollection<AlarmItemActuralAlarmList { getprivate set; }public ObservableCollection<AlarmItemRegisteredAlarms { getprivate set; }

当前报警是实时更新的,注册报警则需要从配置文件中读取加载。

publicAlarmRepository(IConfigManagerFactory configManagerFactory, IEventAggregator eventAggregator, ILoggerFactory loggerFactory){    _alarmConfigManager = configManagerFactory.CreateConfigManager(ConfigType.json, "alarm_config""Alarm");    _eventAggregator = eventAggregator;    _logger = loggerFactory.CreateLogger("Alarms");    ActuralAlarmList = new ObservableCollection<AlarmItem>();    HistoryAlarms = new ObservableCollection<AlarmItem>();    Restore();} publicvoidRestore() {     RegisteredAlarms = new ObservableCollection<AlarmItem>(_alarmConfigManager.LoadConfig<List<AlarmItem>>()); }

同时保存注册的报警内容也是放在仓储中,直接保存即可。

publicvoidRegisterAlarm(){    _alarmConfigManager.SaveConfig(RegisteredAlarms);}

报警触发则是包含了判断是否注册,是否存在报警,以及日志记录、弹窗显示和发布报警。报警清除就是直接清空当前报警集合,同时发布清除报警事件。

publicvoidClearAlarm(string alarmCode){    ActuralAlarmList.Remove(ActuralAlarmList.FirstOrDefault(x => x.AlarmCode == alarmCode));    _eventAggregator.GetEvent<AlarmClearedEvent>().Publish();}publicvoidAlarm(string alarmCode){    AlarmItem alarmItem;    var now = DateTime.Now;    var alarmitems = RegisteredAlarms.Where(x => x.AlarmCode == alarmCode).ToList();    if (alarmitems.Count() >= 1)    {        var config = alarmitems[0];        alarmItem = new AlarmItem()        {            AlarmCode = config.AlarmCode,            Content = config.Content,            Time = now        };    }    else    {        alarmItem = new AlarmItem()        {            AlarmCode = alarmCode,            Content = "非手动注册报警,请确认代码,或者前往报警注册界面修改本内容!",            Time = now        };        RegisterAlarm();        RegisteredAlarms.Add(alarmItem);    }    if (!ActuralAlarmList.Contains(alarmItem))    {        ActuralAlarmList.Insert(0, alarmItem);        string logMsg = $"- {alarmCode} - {alarmItem.Content}";        _logger.Warn(logMsg);        Growl.Error(            new GrowlInfo            {                Message = $"{alarmCode} - {alarmItem.Content}",                ActionBeforeClose = isConfirmed =>                {                    ClearAlarm(alarmCode);                    return true                }            });        _eventAggregator.GetEvent<AlarmOccurredEvent>().Publish(alarmItem);    }}

界面显示

界面分为历史报警和报警注册两项。

历史报警主要用来显示软件开启后出现过的所有报警内容,同时包含按照时间段筛选的功能。主要的代码功能一个是订阅报警,同时把报警添加到历史报警中,还有一个就是执行筛选。

publicAlarmHistoryViewModel(IEventAggregator eventAggregator){    _eventAggregator = eventAggregator;    _eventAggregator.GetEvent<AlarmOccurredEvent>().Subscribe(x =>    {        _historyAlarmList.Add(x);        DisplayAlarmList = IsFiltered ?                       new ObservableCollection<AlarmItem>(_historyAlarmList.Where(a => a.Time > StartTime && a.Time < EndTime).ToList()) :                       _historyAlarmList;    }, ThreadOption.UIThread);    FilterCommand = new DelegateCommand(ExcuteFilter);}privatevoidExcuteFilter(){    IsFiltered = !IsFiltered;    DisplayAlarmList = IsFiltered ?                       new ObservableCollection<AlarmItem>(_historyAlarmList.Where(a => a.Time > StartTime && a.Time < EndTime).ToList()) :                       _historyAlarmList;}

报警注册主要包含了添加删除保存等操作,我还预留了导出导入功能,后续有时间再做。

publicAlarmRegisterViewModel(IAlarmRepository alarmRepository){    _alarmRepository = alarmRepository;    RegisteredAlarms = _alarmRepository.RegisteredAlarms;    AddAlarmCommand = new DelegateCommand(ExcuteAddAlarm);    DeleteAlarmCommand = new DelegateCommand(ExcuteDeleteAlarm);    SaveCommand = new DelegateCommand(ExcuteSave);    RestoreCommand = new DelegateCommand(ExcuteRestore);    ImportCommand = new DelegateCommand(ExcuteImport);    ExportCommand = new DelegateCommand(ExcuteExport);}privatevoidExcuteAddAlarm(){    RegisteredAlarms.Insert(0new AlarmItem());}privatevoidExcuteDeleteAlarm(){    RegisteredAlarms.Remove(SelectedAlarm);}privatevoidExcuteSave(){    var emptyItems = RegisteredAlarms                    .Where(x => x == null || string.IsNullOrWhiteSpace(x.AlarmCode))                    .ToList();    foreach (var item in emptyItems)    {        RegisteredAlarms.Remove(item);    }    _alarmRepository.RegisterAlarm();    Growl.Success($"保存报警注册成功!");}privatevoidExcuteRestore(){    _alarmRepository.Restore();    RegisteredAlarms = _alarmRepository.RegisteredAlarms;}

事件

报警部分实现了两个事件,一个是清除报警,一个是报警触发,当前还没有融合到状态机中,这部分后续再继续完成。

public classAlarmClearedEvent : PubSubEvent{}_eventAggregator.GetEvent<AlarmClearedEvent>().Publish();public classAlarmOccurredEvent : PubSubEvent<AlarmItem>{}_eventAggregator.GetEvent<AlarmOccurredEvent>().Publish(alarmItem);_eventAggregator.GetEvent<AlarmOccurredEvent>().Subscribe(x =>{    _historyAlarmList.Add(x);    DisplayAlarmList = IsFiltered ?                   new ObservableCollection<AlarmItem>(_historyAlarmList.Where(a => a.Time > StartTime && a.Time < EndTime).ToList()) :                   _historyAlarmList;}, ThreadOption.UIThread);

后记

以上就是报警管理部分的全部内容了,下面应该会继续完善这部分关于状态机部分的内容,同时完成剩下的界面开发。

所有的代码都存放再Github中,欢迎大家前往Star、Fork、提Issue。所有文章都发布在专栏里,有需要看前期内容和后续更新的欢迎点击订阅。

如果大家在配置或者理解上有什么疑问,或者因为网络原因无法畅快访问 GitHub,可以直接在我的微信公众号后台私信我获取全套源码离线包。

https://github.com/JeffreyXXL/Sophon

构建工业自动化软件框架
设计模式在C#开发工控软件中的应用