有很长一段时间没有写博客了,因为生活中遭遇了一些不愉快的事情,让我无法静下心来写一篇技术博客,反而更想表达一些生活的感悟,呵呵,眼看着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
最后的效果,上个图: