• 设备状态监控之观察者模式


    有很长一段时间没有写博客了,因为生活中遭遇了一些不愉快的事情,让我无法静下心来写一篇技术博客,反而更想表达一些生活的感悟,呵呵,眼看着2019就快要结束了,圈子里也看到了一些大佬们的年终总结的帖子,我想我也该写一篇关于我的2019,但是我并不想写在技术论坛里,所以,这里还是纯粹的谈论学习,那么,回到今天的主题内容,关于设备状态监控的。

    我接到的需求就是在一个窗体里,实时显示设备的状态,每一种状态用对应的图标显示,其实核心的需求就是这么简单。那么,拿到这样的需求,脑海里首先想到的就是使用观察者模式来实现,为什么是观察者模式呢?简单的回答的话,那就是经验,经验告诉我观察者模式很契合上述需求的实现,专业一点的话,那么需要我们先了解何为观察者模式?

    何为观察者模式?

    观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己

    举个例子:微信公众号,相信大家都关注过,这个关注就是订阅的过程,那么,这个公众号有新的文章发布的时候,你就会收到这个公众号推送的消息,这个推送就是发布的过程,这就是观察者模式最经典的应用。

    回到我们的设备监控,每一个图标控件就是观察者,设备状态发生改变的时候,通知我们的观察者,观察者根据设备状态再来改变图标显示,这就达到我们的目的了。那么,已经有了整体的思路,我们就开始编码吧!

    观察者模式核心代码

    /// <summary>
        /// 设备监控接口
        /// </summary>
        public interface IDevicesMonitor
        {
            /// <summary>
            /// 添加观察者
            /// </summary>
            /// <param name="observer">观察者</param>
            void RegisterObserver(IObserver observer);
    
            /// <summary>
            /// 移除观察者
            /// </summary>
            /// <param name="observer">观察者</param>
            void RemoveObserver(IObserver observer);
    
            /// <summary>
            /// 通知观察者
            /// </summary>
            void NotifyObserver();
        }
    /// <summary>
        /// 设备观察者接口
        /// </summary>
        public interface IObserver
        {
            /// <summary>
            /// 更新设备状态
            /// </summary>
            /// <param name="deviceStatus"></param>
            void Update(DeviceInfo deviceInfo);
        }

    观察者模式,实现上述两个接口,就完成了,且看代码:设备监控类中,使用了设计模式中的单例模式

    /// <summary>
        /// 设备监控类
        /// </summary>
        public class DevicesMonitor : IDevicesMonitor
        {
            /// <summary>
            /// 保存所有的观察者
            /// </summary>
            private ConcurrentBag<IObserver> _observers;
    
            /// <summary>
            /// 设备信息
            /// </summary>
            public DeviceInfo DeviceInfo { private get; set; }
    
            /// <summary>
            /// 单例模式
            /// </summary>
            private static DevicesMonitor singleton = new DevicesMonitor();
            private static object lockObj = new object();
    
            /// <summary>
            /// 监控类 单例
            /// </summary>
            /// <returns></returns>
            public static DevicesMonitor GetSingleton()
            {
                if (singleton == null)
                {
                    lock (lockObj)
                    {
                        if (singleton == null)
                        {
                            singleton = new DevicesMonitor();
                        }
                    }
                }
                return singleton;
            }
    
            private DevicesMonitor()
            {
                _observers = new ConcurrentBag<IObserver>();
            }
    
            public void NotifyObserver()
            {
                foreach (var observer in _observers)
                {
                    observer.Update(DeviceInfo);
                }
            }
    
            /// <summary>
            /// 添加观察者
            /// </summary>
            /// <param name="observer"></param>
            public void RegisterObserver(IObserver observer)
            {
                _observers.Add(observer);
            }
    
            /// <summary>
            /// 移除观察者
            /// </summary>
            /// <param name="observer"></param>
            public void RemoveObserver(IObserver observer)
            {
                _observers.TryTake(out observer);
            }
    
            /// <summary>
            /// 设置设备状态
            /// </summary>
            /// <param name="deviceInfo"></param>
            public void SetStatus(DeviceInfo deviceInfo)
            {
                this.DeviceInfo = deviceInfo;
                NotifyObserver();
            }
        }

    观察者,使用自定义控件,继承Panel和IOberver接口

    public partial class ucDevice : Panel, IObserver
        {
            public ucDevice(DeviceInfo deviceInfo)
            {
                InitializeComponent();
                this.DeviceInfo = deviceInfo;
                InitDeviceStatus(deviceInfo);
                _status = deviceInfo.Status;
            }
    
            int _xPos;
            int _yPos;
            bool _moveFlag;
    
            enumDeviceStatus _status = enumDeviceStatus.UnKnow;
    
            /// <summary>
            /// 删除电子地图中的设备的时候 触发的事件
            /// </summary>
            public event Action<ucDevice> OnDeleteDevice;
    
            /// <summary>
            /// 设备状态发生改变的时候 触发的事件
            /// </summary>
            public event Action<DeviceInfo, enumDeviceStatus> OnStatusChanged;
    
            public DeviceInfo DeviceInfo { get; set; }
    
    
            public void Update(DeviceInfo deviceInfo)
            {
                if (this.DeviceInfo.Id != deviceInfo.Id) return;
                if (_status != deviceInfo.Status)
                {
                    this.DeviceInfo.Status = deviceInfo.Status;
                    InitDeviceStatus(this.DeviceInfo);
                    if (OnStatusChanged != null)
                    {
                        OnStatusChanged(this.DeviceInfo, _status);
                    }
                    _status = deviceInfo.Status;
                }
            }
    
            private void InitDeviceStatus(DeviceInfo deviceInfo)
            {
                StringBuilder stringBuilder = new StringBuilder();
                if (!string.IsNullOrEmpty(deviceInfo.DeviceName))
                {
                    this.lblMsg.Text = deviceInfo.DeviceName;
                    stringBuilder.Append("设备名称:").Append(deviceInfo.DeviceName).Append(Environment.NewLine);
                }
                if (!string.IsNullOrEmpty(deviceInfo.IpAddr))
                { stringBuilder.Append("IP地址:").Append(deviceInfo.IpAddr).Append(Environment.NewLine); }
                if (!string.IsNullOrEmpty(deviceInfo.Mac))
                { stringBuilder.Append("Mac地址:").Append(deviceInfo.Mac).Append(Environment.NewLine); }
    
    
                this.lblMsg.ForeColor = Color.Red;
                this.toolTip1.ToolTipTitle = "Warning";
                this.toolTip1.ToolTipIcon = ToolTipIcon.Warning;
                switch (deviceInfo.Status)
                {
                    case enumDeviceStatus.Normal:
                        this.lblMsg.ForeColor = Color.Blue;
                        this.toolTip1.ToolTipTitle = "Normal";
                        this.toolTip1.ToolTipIcon = ToolTipIcon.Info;
                        stringBuilder.Append("设备状态:正常").Append(Environment.NewLine);
                        break;
                    case enumDeviceStatus.Warning:
                        stringBuilder.Append("设备状态:线路1和线路2都离线").Append(Environment.NewLine);
                        break;
                    case enumDeviceStatus.Warning_1:
                        stringBuilder.Append("设备状态:线路1离线").Append(Environment.NewLine);
                        break;
                    case enumDeviceStatus.Warning_2:
                        stringBuilder.Append("设备状态:线路2离线").Append(Environment.NewLine);
                        break;
                }
                
                this.toolTip1.SetToolTip(picDevice, stringBuilder.ToString());
    
                string fileName = deviceInfo.DeviceType.ToString() + "_" + deviceInfo.Status.ToString();
                string imagePath = EnvironmentInfo.ImagesPath.Find(x => x.Contains(fileName.ToLower()));
    
                if (File.Exists(imagePath))
                {
                    this.picDevice.Image = Image.FromFile(imagePath);
    
                }
                else
                {
                    this.picDevice.Image = Image.FromFile(EnvironmentInfo.ImagesPath.Find(x => x.Contains(enumDeviceStatus.UnKnow.ToString().ToLower())));
                }
            }
    
            private void UcDeviceContainter_MouseMove(object sender, MouseEventArgs e)
            {
                if (_moveFlag)
                {
                    this.Left += Convert.ToInt16(e.X - _xPos);//设置x坐标.
                    this.Top += Convert.ToInt16(e.Y - _yPos);//设置y坐标.
                }
            }
    
            private void UcDeviceContainter_MouseDown(object sender, MouseEventArgs e)
            {
                _moveFlag = true;//已经按下.
                _xPos = e.X;//当前x坐标.
                _yPos = e.Y;//当前y坐标.
            }
    
            private void UcDeviceContainter_MouseUp(object sender, MouseEventArgs e)
            {
                _moveFlag = false;
            }
    
            private void ToolStripMenuItemDelete_Click(object sender, EventArgs e)
            {
                if (OnDeleteDevice != null)
                {
                    OnDeleteDevice(this);
                }
            }
        }

    最后就是通过对设备状态的监控来完成设备状态的刷新,这里只是一个模拟的效果,直接从数据库中获取设备的状态,然后来刷新设备状态,具体的项目,根据自己的业务来决定

    public class ClientProxy
        {
            private readonly string _connString = "";
    
            /// <summary>
            /// 后台监控线程
            /// </summary>
            BackgroundWorker _backgroundWorker = new BackgroundWorker();
    
            /// <summary>
            /// 休眠时间 单位:秒
            /// </summary>
            public int SleepTimes { private get; set; } = 10;
    
            public ClientProxy(string connString)
            {
                _connString = connString;
    
                InitData();
                EnvironmentInfo.InitProxy();//必须先初始化
                DoWork();
            }
    
            /// <summary>
            /// 初始化数据
            /// </summary>
            /// <returns></returns>
            public List<DeviceInfo> InitData()
            {
                try
                {
                    List<DeviceInfo> deviceInfos = new List<DeviceInfo>();
                    string sql = "select * from deviceinfo";
                    DataTable dt = MySqlHelper.ExecuteDataset(_connString, sql).Tables[0];//从数据库中获取数据
                    foreach (DataRow dr in dt.Rows)
                    {
                        enumDeviceType deviceType = enumDeviceType.Router;
                        switch (dr["DeviceType"].ToString())
                        {
                            case "0":
                                deviceType = enumDeviceType.Server;
                                break;
                            case "1":
                                deviceType = enumDeviceType.Router;
                                break;
                            case "2":
                                deviceType = enumDeviceType.ATM;
                                break;
                        }
                        deviceInfos.Add(new DeviceInfo()//转换成 所需要的实体列表
                        {
                            Id = int.Parse(dr["Id"].ToString()),
                            DeviceName = dr["DeviceName"].ToString(),
                            IpAddr = dr["IpAddr"].ToString(),
                            ParentId = int.Parse(dr["ParentId"].ToString()),
                            Status = enumDeviceStatus.Normal,
                            DeviceType = deviceType
                        });
                    }
                    return deviceInfos;
                }
                catch (Exception)
                {
                    throw;
                }
            }
    
            /// <summary>
            /// 开始监控设备状态
            /// </summary>
            private void DoWork()
            {
                _backgroundWorker.WorkerReportsProgress = true;
                _backgroundWorker.DoWork += _backgroundWorker_DoWork;
                _backgroundWorker.ProgressChanged += _backgroundWorker_ProgressChanged;
                if (!_backgroundWorker.IsBusy)
                {
                    _backgroundWorker.RunWorkerAsync();
                }
            }
    
            private void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
            {
                while (true)
                {
                    string sql = "select * from deviceinfo d,monitor m where d.Id=m.DeviceId";
                    DataTable dt = MySqlHelper.ExecuteDataset(_connString, sql).Tables[0];
                    foreach (DataRow dr in dt.Rows)
                    {
                        enumDeviceType deviceType = enumDeviceType.Router;
                        switch (dr["DeviceType"].ToString())
                        {
                            case "0":
                                deviceType = enumDeviceType.Server;
                                break;
                            case "1":
                                deviceType = enumDeviceType.Router;
                                break;
                            case "2":
                                deviceType = enumDeviceType.ATM;
                                break;
                        }
                        DeviceInfo deviceInfo = new DeviceInfo()
                        {
                            Id = int.Parse(dr["Id"].ToString()),
                            DeviceType = deviceType
                        };
    
                        string netWorkStr = dr["Network1"].ToString() + dr["Network2"].ToString();
                        switch (netWorkStr)
                        {
                            case "00":
                                deviceInfo.Status = enumDeviceStatus.Warning;
                                break;
                            case "01":
                                deviceInfo.Status = enumDeviceStatus.Warning_1;
                                break;
                            case "10":
                                deviceInfo.Status = enumDeviceStatus.Warning_2;
                                break;
                            case "11":
                                deviceInfo.Status = enumDeviceStatus.Normal;
                                break;
                            default:
                                deviceInfo.Status = enumDeviceStatus.Normal;
                                break;
                        }
    
                        _backgroundWorker.ReportProgress(1, deviceInfo);
    
                    }
    
                    System.Threading.Thread.Sleep(SleepTimes * 1000);
                }
            }
    
            private void _backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
            {
                if (e.ProgressPercentage == 1)
                {
                    DeviceInfo deviceInfo = e.UserState as DeviceInfo;
                    if (deviceInfo != null)
                    {
                        DevicesMonitor.GetSingleton().SetStatus(deviceInfo);
                    }
                }
            }
        }

    脚本:

    CREATE TABLE `deviceinfo` (
      `Id` int(11) NOT NULL AUTO_INCREMENT,
      `IpAddr` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL,
      `Mac` varchar(17) COLLATE utf8_unicode_ci DEFAULT NULL,
      `DeviceName` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
      `ParentId` int(11) DEFAULT NULL,
      `DeviceType` int(11) NOT NULL,
      PRIMARY KEY (`Id`),
      KEY `index_IpAddr` (`IpAddr`) USING BTREE
    ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
    
    CREATE TABLE `monitor` (
      `Id` int(11) NOT NULL AUTO_INCREMENT,
      `DeviceId` int(11) DEFAULT NULL,
      `Network1` int(11) DEFAULT NULL,
      `Network2` int(11) DEFAULT NULL,
      PRIMARY KEY (`Id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

    源代码:https://e.coding.net/dwBurning/DeviceMonitor.githttps://e.coding.net/dwBurning/DeviceMonitor.githttps://e.coding.net/dwBurning/DeviceMonitor.githttps://e.coding.net/dwBurning/DeviceMonitor.git

    最后的效果,上个图:

  • 相关阅读:
    基于Redis主从复制读写分离架构的Session共享(Windows Server)
    第3章 线性表
    第2章 算法
    python中统计计数的几种方法和Counter的介绍
    Linux关于文件的权限笔记
    线程同步与互斥(线程安全)
    Python csv模块的使用
    Python 源码分析:queue 队列模块
    Java锁,真的有这么复杂吗?
    1537 学生干部虚基类
  • 原文地址:https://www.cnblogs.com/dwBurning/p/DeviceMonitorObserver.html
Copyright © 2020-2023  润新知