C#监控设备状态(如PLC),并记录到本地sqlite数据库中。
本文只是一个【设备状态仿真模拟器】
新建winform应用程序DeviceStatusDemo,选择.net framework 4.5.将默认的Form1重命名为FormDeviceStatus。
添加对System.Data.SQLite.dll的引用
窗体FormDeviceStatus设计如下
一、添加日志处理类Context,Context.cs源程序:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;
using System.Threading;
namespace DeviceStatusDemo
{
/// <summary>
/// 上下文实例类
/// </summary>
public static class Context
{
/// <summary>
/// 显示普通日志
/// </summary>
public static event Action<string> EventInfoLog;
/// <summary>
/// 显示错误信息
/// </summary>
public static event Action<string, Exception> EventErrorLog;
/// <summary>
/// 显示错误日志
/// </summary>
/// <param name="strErrorMsg">错误信息</param>
/// <param name="ex">异常对象</param>
public static void ShowError(string strErrorMsg, Exception ex = null)
{
if (strErrorMsg == "" || strErrorMsg == null)
{
return;
}
EventErrorLog?.BeginInvoke(strErrorMsg, ex, null, null);
}
/// <summary>
/// 显示日志信息
/// </summary>
/// <param name="strErrorMsg"></param>
public static void ShowInfo(string strErrorMsg)
{
EventInfoLog?.BeginInvoke(strErrorMsg, null, null);
}
}
}
二、新建 设备状态数据类DeviceStatusData,同时定义设备状态枚举 DeviceStatus,读取设备状态枚举的描述信息。DeviceStatusData.cs源程序如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace DeviceStatusDemo
{
/// <summary>
/// 设备状态数据类:待料、堵料、自动、手动、故障
/// </summary>
public class DeviceStatusData
{
/// <summary>
/// 日期,格式: yyyy-MM-dd
/// </summary>
public string RecordDate { get; set; }
/// <summary>
/// 班次:白班、夜班
/// </summary>
public string RecordShift { get; set; }
/// <summary>
/// 当前设备状态【待料、堵料、自动、手动、故障】中的某一个枚举
/// </summary>
public string DeviceStatus { get; set; }
/// <summary>
/// 设备状态代码
/// </summary>
public short DeviceStatusCode { get; set; }
/// <summary>
/// 开始时间
/// </summary>
public DateTime BeginTime { get; set; }
/// <summary>
/// 结束时间
/// </summary>
public DateTime EndTime { get; set; }
/// <summary>
/// 状态保持时间(s)
/// </summary>
public double Duration { get; set; }
/// <summary>
/// 设备工位:如 密封钉A工作台、顶盖焊B工作台
/// </summary>
public string DevicePosition { get; set; }
/// <summary>
/// 记录设备状态描述信息,比如:设备状态由【自动】切换到【待料】
/// </summary>
public string DeviceStatusMessage { get; set; }
}
/// <summary>
/// 设备状态枚举
/// </summary>
public enum DeviceStatus : short
{
/// <summary>
/// 自动
/// </summary>
[Description("自动")]
Auto = 1,
/// <summary>
/// 手动
/// </summary>
[Description("手动")]
Manual = 2,
/// <summary>
/// 待料
/// </summary>
[Description("待料")]
WaitingForMaterial = 3,
/// <summary>
/// 堵料
/// </summary>
[Description("堵料")]
Blocking = 4,
/// <summary>
/// 故障
/// </summary>
[Description("故障")]
Fault = 5
}
public static class Util
{
/// <summary>
/// 根据枚举值得到枚举中文信息
/// </summary>
/// <param name="enumType">一种枚举类型</param>
/// <param name="value"></param>
/// <returns></returns>
public static string GetEnumDescription(this Type enumType, int? value)
{
//如果不是枚举类型,直接返回
if (!enumType.IsEnum)
{
return value == null ? "" : value.ToString();
}
FieldInfo[] fields = enumType.GetFields();
//不考虑枚举默认的第一个元素
for (int i = 1, count = fields.Length; i < count; i++)
{
if (Convert.ToInt32(Enum.Parse(enumType, fields[i].Name)) == value)
{
DescriptionAttribute[] enumAttributes = (DescriptionAttribute[])fields[i].GetCustomAttributes(typeof(DescriptionAttribute), false);
if (enumAttributes.Length > 0)
{
return enumAttributes[0].Description;
}
}
}
return value == null ? "" : value.ToString();
}
}
}
三、新建sqlite数据库操作类SqliteDbHelper,SqliteDbHelper.cs源程序:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SQLite;
using System.Linq;
using System.Text;
namespace DeviceStatusDemo
{
/// <summary>
/// 功能:sqlite数据库操作类
/// </summary>
public class SqliteDbHelper
{
static object ObjLock = new object();
/// <summary>
/// 连接字符串
/// </summary>
private string m_strConnectString = string.Empty;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="strDbPath">SQLite数据库文件路径</param>
public SqliteDbHelper(string strDbPath)
{
this.m_strConnectString = "Data Source=" + strDbPath + ";Pooling=true;FailIfMissing=false;Journal Mode=WAL";
}
/// <summary>
/// 创建SQLite数据库文件
/// </summary>
/// <param name="strDbPath">要创建的SQLite数据库文件路径</param>
/// <param name="strdbTableCreateString">要创建的SQLite数据库表格</param>
public static void CreateDB(string strDbPath, string strdbTableCreateString)
{
using (SQLiteConnection connection = new SQLiteConnection("Data Source=" + strDbPath))
{
connection.Open();
using (SQLiteCommand command = new SQLiteCommand(connection))
{
command.CommandText = strdbTableCreateString;
command.ExecuteNonQuery();
}
}
}
/// <summary>
/// 对SQLite数据库执行增删改操作,返回受影响的行数。
/// </summary>
/// <param name="strSql">要执行的增删改的SQL语句</param>
/// <param name="parameters">执行增删改语句所需要的参数,参数必须以它们在SQL语句中的顺序为准</param>
/// <returns>返回受影响的行数</returns>
public int ExecuteNonQuery(string strSql, SQLiteParameter[] parameters)
{
try
{
int intAffectedRows = 0;
using (SQLiteConnection connection = new SQLiteConnection(m_strConnectString))
{
connection.Open();
lock (ObjLock)
{
using (DbTransaction transaction = connection.BeginTransaction())
{
using (SQLiteCommand command = new SQLiteCommand(connection))
{
command.CommandText = strSql;
if (parameters != null)
{
command.Parameters.AddRange(parameters);
}
intAffectedRows = command.ExecuteNonQuery();
}
transaction.Commit();
}
}
}
return intAffectedRows;
}
catch (Exception ex)
{
Context.ShowError("执行sql语句异常," + ex.Message + "\r\n详细sql语句为:" + strSql, ex);
return -1;
}
}
public int ExecuteNonQueryByDict(string strSql, Dictionary<string, object> dicParams)
{
try
{
int intAffectedRows = 0;
using (SQLiteConnection connection = new SQLiteConnection(m_strConnectString))
{
connection.Open();
lock (ObjLock)
{
using (DbTransaction transaction = connection.BeginTransaction())
{
using (SQLiteCommand command = new SQLiteCommand(connection))
{
command.CommandText = strSql;
if (dicParams != null && dicParams.Count > 0)
{
command.Parameters.Clear();
foreach (string parameterName in dicParams.Keys)
{
command.Parameters.AddWithValue(parameterName, dicParams[parameterName]);
}
}
intAffectedRows = command.ExecuteNonQuery();
}
transaction.Commit();
}
}
}
return intAffectedRows;
}
catch (Exception ex)
{
Context.ShowError("执行sql语句异常," + ex.Message + "\r\n详细sql语句为:" + strSql, ex);
return -1;
}
}
/// <summary>
/// 执行一个查询语句,返回一个关联的SQLiteDataReader实例
/// </summary>
/// <param name="strSql">要执行的查询语句</param>
/// <param name="parameters">执行SQL查询语句所需要的参数,参数必须以它们在SQL语句中的顺序为准</param>
/// <returns>返回SQLiteDataReader</returns>
public SQLiteDataReader ExecuteReader(string strSql, SQLiteParameter[] parameters)
{
SQLiteConnection connection = new SQLiteConnection(m_strConnectString);
SQLiteCommand command = new SQLiteCommand(strSql, connection);
if (parameters != null)
{
command.Parameters.AddRange(parameters);
}
connection.Open();
return command.ExecuteReader(CommandBehavior.CloseConnection);
}
/// <summary>
/// 执行一个查询语句,返回一个包含查询结果的DataTable
/// </summary>
/// <param name="strSql">要执行的查询语句</param>
/// <param name="parameters">执行SQL查询语句所需要的参数,参数必须以它们在SQL语句中的顺序为准</param>
/// <returns>返回数据表</returns>
public DataTable ExecuteDataTable(string strSql, SQLiteParameter[] parameters)
{
using (SQLiteConnection connection = new SQLiteConnection(m_strConnectString))
{
using (SQLiteCommand command = new SQLiteCommand(strSql, connection))
{
if (parameters != null)
{
command.Parameters.AddRange(parameters);
}
SQLiteDataAdapter adapter = new SQLiteDataAdapter(command);
DataTable data = new DataTable();
lock (ObjLock)
{
adapter.Fill(data);
}
return data;
}
}
}
public DataTable GetDataTable(string strSql, Dictionary<string, object> dicParams)
{
using (SQLiteConnection connection = new SQLiteConnection(m_strConnectString))
{
using (SQLiteCommand command = new SQLiteCommand(strSql, connection))
{
if (dicParams != null && dicParams.Count > 0)
{
command.Parameters.Clear();
foreach (string parameterName in dicParams.Keys)
{
command.Parameters.AddWithValue(parameterName, dicParams[parameterName]);
}
}
SQLiteDataAdapter adapter = new SQLiteDataAdapter(command);
DataTable data = new DataTable();
lock (ObjLock)
{
adapter.Fill(data);
}
return data;
}
}
}
public DataSet ExcuteDataSet(string strSql)
{
DataSet ds = new DataSet("MyDataSet");
using (SQLiteConnection connection = new SQLiteConnection(m_strConnectString))
{
using (SQLiteCommand command = new SQLiteCommand(strSql, connection))
{
SQLiteDataAdapter adapter = new SQLiteDataAdapter(command);
lock (ObjLock)
{
adapter.Fill(ds);
}
return ds;
}
}
}
/// <summary>
/// 执行一个查询语句,返回查询结果的第一行第一列
/// </summary>
/// <param name="strSql">要执行的查询语句</param>
/// <returns>返回第一行第一列的值</returns>
public object ExecuteScalar(string strSql)
{
try
{
using (SQLiteConnection connection = new SQLiteConnection(m_strConnectString))
{
connection.Open();
lock (ObjLock)
{
using (SQLiteCommand command = new SQLiteCommand(strSql, connection))
{
return command.ExecuteScalar();
}
}
}
}
catch
{
throw;
}
}
public int ExecuteScalarInt(string strSql)
{
object oRes = ExecuteScalar(strSql);
return oRes == null ? 0 : Convert.ToInt32(oRes);
}
/// <summary>
/// 查询数据库中的所有数据类型信息
/// </summary>
/// <returns>返回数据表</returns>
public DataTable GetSchema()
{
using (SQLiteConnection connection = new SQLiteConnection(m_strConnectString))
{
connection.Open();
DataTable data = connection.GetSchema("TABLES");
connection.Close();
return data;
}
}
public bool DatabaseBetter()
{
try
{
lock (ObjLock)
{
using (SQLiteConnection connection = new SQLiteConnection(m_strConnectString))
{
connection.Open();
using (SQLiteCommand command = new SQLiteCommand(connection))
{
command.CommandText = "VACUUM";
command.ExecuteNonQuery();
}
}
}
return true;
}
catch (Exception ex)
{
Context.ShowError("使用[VACUUM]命令优化数据库异常 ,详情:" + ex.Message);
return false;
}
}
}
}
四、新建用于操作数据库的静态类Env,Env.cs源程序
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data;
using System.IO;
namespace DeviceStatusDemo
{
/// <summary>
/// 应用程序级别的单例类
/// </summary>
public static class Env
{
static Env()
{
//增加sqlite数据库操作对象
string strSqliteConnStr = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "deviceStatusDemo.db");
SqliteDb = new SqliteDbHelper(strSqliteConnStr);
}
/// <summary>
/// Sqlite数据库操作类对象
/// </summary>
public static SqliteDbHelper SqliteDb { get; set; }
/// <summary>
/// 创建数据库
/// </summary>
public static void EnsureDataBaseTables()
{
string strFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "deviceStatusDemo.db");
if (!File.Exists(strFile))
{
System.Data.SQLite.SQLiteConnection.CreateFile(strFile);
}
string strSql = @"
--创建设备状态记录表
CREATE TABLE IF NOT EXISTS
Hans_DeviceStatus
(
Id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
RecordDate TEXT,
RecordShift TEXT,
DeviceStatus TEXT,
DeviceStatusCode INTEGER,
BeginTime TEXT,
EndTime TEXT,
Duration TEXT,
DevicePosition TEXT,
DeviceStatusMessage TEXT
);";
SqliteDb.ExecuteNonQueryByDict(strSql, null);
}
}
}
五、关键操作设备状态改变的类DeviceStatusUtil.DeviceStatusUtil.cs源程序如下
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DeviceStatusDemo
{
/// <summary>
/// 设备状态改变处理
/// </summary>
public class DeviceStatusUtil
{
/// <summary>
/// 触发设备状态改变事件
/// 第一个参数:当前设备状态
/// 第二个参数:上次的设备状态
/// 第三个参数:班次
/// </summary>
public static event Action<short, short, string> EventDeviceStatusChanged;
/// <summary>
/// 比较设备状态是否已改变
/// </summary>
/// <param name="buffer">当前设备状态</param>
/// <param name="bufferLast">上次的设备状态</param>
/// <returns></returns>
public static bool CompareStatusChanged(short buffer, short bufferLast)
{
DateTime dtNow = DateTime.Now;
string recordShift = GetShiftNormal(dtNow);
//是否已改变
if (bufferLast != -1 && buffer != bufferLast)
{
//触发设备状态改变事件
EventDeviceStatusChanged?.Invoke(buffer, bufferLast, recordShift);
return true;
}
return false;
}
/// <summary>
/// 获得正常情况下的班次
/// 【8点~20点是白班】
/// </summary>
/// <returns></returns>
public static string GetShiftNormal(DateTime dtNow)
{
DateTime dtFrom = new DateTime(dtNow.Year, dtNow.Month, dtNow.Day, 8, 0, 0);
DateTime dtTo = new DateTime(dtNow.Year, dtNow.Month, dtNow.Day, 20, 0, 0);
//8点~20点之间为白班
if (dtNow >= dtFrom && dtNow < dtTo)
{
return "白班";
}
return "夜班";
}
/// <summary>
/// 获取上一次设备状态的记录开始时间
/// </summary>
/// <returns></returns>
public static DateTime GetLastRecordTime()
{
object objResult = Env.SqliteDb.ExecuteScalar("select BeginTime from Hans_DeviceStatus where Id in (select max(Id) from Hans_DeviceStatus)");
if (objResult == null || string.IsNullOrWhiteSpace(objResult.ToString()))
{
return DateTime.Now;
}
return DateTime.Parse(objResult.ToString());
}
/// <summary>
/// 获取最后一次的设备状态代码
/// </summary>
/// <returns></returns>
public static short GetLastDeviceStatusCode()
{
object objResult = Env.SqliteDb.ExecuteScalar("select DeviceStatusCode from Hans_DeviceStatus where Id in (select max(Id) from Hans_DeviceStatus)");
if (objResult == null || string.IsNullOrWhiteSpace(objResult.ToString()))
{
return -1;
}
return Convert.ToInt16(objResult);
}
/// <summary>
/// 保存设备状态改变到数据库
/// 【类似于工作流的A环节 送出到 B环节】
/// 先更新上一条数据的结束时间为Now,持续时间,然后增加一条开始时间为Now,结束时间、持续时间为空的记录
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static int SaveDeviceStatus(DeviceStatusData data)
{
Dictionary<string, object> dict = new Dictionary<string, object>();
dict.Add("RecordDate", data.RecordDate);
dict.Add("RecordShift", data.RecordShift);
dict.Add("DeviceStatus", data.DeviceStatus);
dict.Add("DeviceStatusCode", data.DeviceStatusCode);
dict.Add("BeginTime", data.BeginTime.ToString("yyyy-MM-dd HH:mm:ss"));
dict.Add("EndTime", data.EndTime.ToString("yyyy-MM-dd HH:mm:ss"));
dict.Add("Duration", data.Duration.ToString("N2"));
dict.Add("DevicePosition", data.DevicePosition);
dict.Add("DeviceStatusMessage", data.DeviceStatusMessage);
SQLiteParameter[] parameters = new SQLiteParameter[dict.Count];
for (int i = 0; i < dict.Count; i++)
{
KeyValuePair<string, object> keyValue = dict.ElementAt(i);
parameters[i] = new SQLiteParameter(keyValue.Key, keyValue.Value);
}
string sql = @"
update Hans_DeviceStatus set EndTime=@EndTime,Duration=@Duration,DeviceStatusMessage=@DeviceStatusMessage where Id in (select max(Id) from Hans_DeviceStatus);
insert into Hans_DeviceStatus (RecordDate,RecordShift,DeviceStatus,DeviceStatusCode,BeginTime,EndTime,Duration,DevicePosition,DeviceStatusMessage) values (@RecordDate,@RecordShift,@DeviceStatus,@DeviceStatusCode,@BeginTime,'','',@DevicePosition,'')";
int affectRow = Env.SqliteDb.ExecuteNonQuery(sql, parameters);
return affectRow;
}
}
}
六、设备操作仿真类PlcOperateUtil,PlcOperateUtil.cs源程序
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DeviceStatusDemo
{
/// <summary>
/// PLC操作类
/// 该类只是仿真,实际使用需要读写PLC的相关地址
/// </summary>
public class PlcOperateUtil
{
/// <summary>
/// 读取PLC指定地址的值,
/// 这里只是一个模拟器
/// </summary>
/// <param name="address"></param>
/// <returns></returns>
public static short ReadInt16(string address)
{
return (short)new Random().Next(1, 6);
}
}
}
七、窗体FormDeviceStatus的相关主要程序如下(忽略设计器自动生成的部分类代码):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace DeviceStatusDemo
{
public partial class FormDeviceStatus : Form
{
/// <summary>
/// 是否在运行中
/// </summary>
public bool IsRunning { get; set; }
/// <summary>
/// 上一次的设备状态
/// </summary>
static short bufferLastStatus = -1;
public FormDeviceStatus()
{
InitializeComponent();
Env.EnsureDataBaseTables();
Context.EventErrorLog += Context_EventErrorLog;
Context.EventInfoLog += Context_EventInfoLog;
}
private void Context_EventInfoLog(string message)
{
DisplayMessage(message);
}
private void Context_EventErrorLog(string message, Exception ex)
{
DisplayMessage($"{message}\n{(ex == null ? "" : ex.Message)}");
}
/// <summary>
/// 设备状态更改事件
/// </summary>
/// <param name="currentDeviceStatus">当前设备状态</param>
/// <param name="lastDeviceStatus">上次设备状态</param>
private void DeviceStatusUtil_EventDeviceStatusChanged(short currentDeviceStatus, short lastDeviceStatus, string recordShift)
{
//当前设备状态描述、上次设备状态描述
string currentStatusDesc = typeof(DeviceStatus).GetEnumDescription(currentDeviceStatus);
string lastStatusDesc = typeof(DeviceStatus).GetEnumDescription(lastDeviceStatus);
DateTime dtLastTime = DeviceStatusUtil.GetLastRecordTime();
DateTime dtNow = DateTime.Now;
DeviceStatusData data = new DeviceStatusData();
data.RecordDate = dtNow.ToString("yyyy-MM-dd");
data.RecordShift = recordShift;
data.DeviceStatus = currentStatusDesc;
data.DeviceStatusCode = currentDeviceStatus;
data.BeginTime = dtNow;//当前开始时间
data.EndTime = dtNow;//上一次的结束时间
data.Duration = dtNow.Subtract(dtLastTime).TotalSeconds;
data.DevicePosition = "XX焊接工作台";
data.DeviceStatusMessage = $"设备状态【{lastStatusDesc}】持续了【{data.Duration.ToString("N2")}】秒.设备状态已由【{lastDeviceStatus}-{lastStatusDesc}】切换到【{currentDeviceStatus}-{currentStatusDesc}】";
Context.ShowInfo(data.DeviceStatusMessage);
DeviceStatusUtil.SaveDeviceStatus(data);
}
/// <summary>
/// 启用监控设备状态
/// </summary>
private void StartMonitor()
{
//从数据库读取最后一次的设备状态代码
bufferLastStatus = DeviceStatusUtil.GetLastDeviceStatusCode();
//绑定设备状态更改事件
DeviceStatusUtil.EventDeviceStatusChanged -= DeviceStatusUtil_EventDeviceStatusChanged;
DeviceStatusUtil.EventDeviceStatusChanged += DeviceStatusUtil_EventDeviceStatusChanged;
IsRunning = true;
Task.Factory.StartNew(() =>
{
while (IsRunning)
{
try
{
#region 设备状态保存日志
//读取设备状态
short currentDeviceStatus = PlcOperateUtil.ReadInt16("DB500.10");
RefreshDeviceStatus(currentDeviceStatus);
bool isStatisticFlag = DeviceStatusUtil.CompareStatusChanged(currentDeviceStatus, bufferLastStatus);
if (isStatisticFlag)
{
if (bufferLastStatus != -1 && bufferLastStatus != currentDeviceStatus)
{
Context.ShowInfo($"设备状态已变更,由【{bufferLastStatus}】转变为【{currentDeviceStatus}】");
}
}
bufferLastStatus = currentDeviceStatus;
#endregion
}
catch (Exception ex)
{
Context.ShowError("与PLC交互出现异常," + ex.Message, null);
break;
}
finally
{
Thread.Sleep(2000);
}
}
});
}
/// <summary>
/// 停止监控
/// </summary>
public void StopMonitor()
{
IsRunning = false;
//解除绑定设备状态更改事件
DeviceStatusUtil.EventDeviceStatusChanged -= DeviceStatusUtil_EventDeviceStatusChanged;
}
/// <summary>
/// 显示富文本框的消息
/// </summary>
/// <param name="message"></param>
private void DisplayMessage(string message)
{
this.BeginInvoke(new Action(() =>
{
if (rtxtDisplay.TextLength >= 10240)
{
rtxtDisplay.Clear();
}
rtxtDisplay.AppendText($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} --> {message}\n");
rtxtDisplay.ScrollToCaret();
}));
}
/// <summary>
/// 刷新设备状态
/// </summary>
/// <param name="deviceStatusDesc"></param>
/// <param name="color"></param>
private void RefreshDeviceStatus(short currentDeviceStatus)
{
string currentStatusDesc = typeof(DeviceStatus).GetEnumDescription(currentDeviceStatus);
Color color = Color.Green;
switch (currentDeviceStatus)
{
case 1:
case 3:
color = Color.Green;
break;
case 2:
color = Color.Yellow;
break;
default:
color = Color.Red;
break;
}
this.BeginInvoke(new Action(() =>
{
lblStatus.Text = currentStatusDesc;
lblStatus.BackColor = color;
}));
}
private void FormDeviceStatus_Load(object sender, EventArgs e)
{
btnStartMonitor.Enabled = true;
btnStopMonitor.Enabled = false;
}
private void btnStartMonitor_Click(object sender, EventArgs e)
{
btnStartMonitor.Enabled = false;
btnStopMonitor.Enabled = true;
Context.ShowInfo("启动监控设备状态...");
StartMonitor();
}
private void btnStopMonitor_Click(object sender, EventArgs e)
{
btnStartMonitor.Enabled = true;
btnStopMonitor.Enabled = false;
Context.ShowInfo("停止监控设备状态...");
StopMonitor();
}
}
}
八、仿真程序运行如图: