C#监控设备状态(如PLC),并记录到本地sqlite数据库中。
本文只是一个【设备状态仿真模拟器】
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.IO;
using System.Threading;
namespace DeviceStatusDemo
{
///
/// 上下文实例类
///
public static class Context
{
///
/// 显示普通日志
///
public static event Action EventInfoLog;
///
/// 显示错误信息
///
public static event Action EventErrorLog;
///
/// 显示错误日志
///
/// 错误信息
/// 异常对象
public static void ShowError(string strErrorMsg, Exception ex = null)
{
if (strErrorMsg == "" || strErrorMsg == null)
{
return;
}
EventErrorLog?.BeginInvoke(strErrorMsg, ex, null, null);
}
///
/// 显示日志信息
///
///
public static void ShowInfo(string strErrorMsg)
{
EventInfoLog?.BeginInvoke(strErrorMsg, null, null);
}
}
}
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
{
///
/// 设备状态数据类:待料、堵料、自动、手动、故障
///
public class DeviceStatusData
{
///
/// 日期,格式: yyyy-MM-dd
///
public string RecordDate { get; set; }
///
/// 班次:白班、夜班
///
public string RecordShift { get; set; }
///
/// 当前设备状态【待料、堵料、自动、手动、故障】中的某一个枚举
///
public string DeviceStatus { get; set; }
///
/// 设备状态代码
///
public short DeviceStatusCode { get; set; }
///
/// 开始时间
///
public DateTime BeginTime { get; set; }
///
/// 结束时间
///
public DateTime EndTime { get; set; }
///
/// 状态保持时间(s)
///
public double Duration { get; set; }
///
/// 设备工位:如 密封钉A工作台、顶盖焊B工作台
///
public string DevicePosition { get; set; }
///
/// 记录设备状态描述信息,比如:设备状态由【自动】切换到【待料】
///
public string DeviceStatusMessage { get; set; }
}
///
/// 设备状态枚举
///
public enum DeviceStatus : short
{
///
/// 自动
///
[Description("自动")]
Auto = 1,
///
/// 手动
///
[Description("手动")]
Manual = 2,
///
/// 待料
///
[Description("待料")]
WaitingForMaterial = 3,
///
/// 堵料
///
[Description("堵料")]
Blocking = 4,
///
/// 故障
///
[Description("故障")]
Fault = 5
}
public static class Util
{
///
/// 根据枚举值得到枚举中文信息
///
/// 一种枚举类型
///
///
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();
}
}
}
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
{
///
/// 功能:sqlite数据库操作类
///
public class SqliteDbHelper
{
static object ObjLock = new object();
///
/// 连接字符串
///
private string m_strConnectString = string.Empty;
///
/// 构造函数
///
/// SQLite数据库文件路径
public SqliteDbHelper(string strDbPath)
{
this.m_strConnectString = "Data Source=" + strDbPath + ";Pooling=true;FailIfMissing=false;Journal Mode=WAL";
}
///
/// 创建SQLite数据库文件
///
/// 要创建的SQLite数据库文件路径
/// 要创建的SQLite数据库表格
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();
}
}
}
///
/// 对SQLite数据库执行增删改操作,返回受影响的行数。
///
/// 要执行的增删改的SQL语句
/// 执行增删改语句所需要的参数,参数必须以它们在SQL语句中的顺序为准
/// 返回受影响的行数
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 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;
}
}
///
/// 执行一个查询语句,返回一个关联的SQLiteDataReader实例
///
/// 要执行的查询语句
/// 执行SQL查询语句所需要的参数,参数必须以它们在SQL语句中的顺序为准
/// 返回SQLiteDataReader
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);
}
///
/// 执行一个查询语句,返回一个包含查询结果的DataTable
///
/// 要执行的查询语句
/// 执行SQL查询语句所需要的参数,参数必须以它们在SQL语句中的顺序为准
/// 返回数据表
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 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;
}
}
}
///
/// 执行一个查询语句,返回查询结果的第一行第一列
///
/// 要执行的查询语句
/// 返回第一行第一列的值
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);
}
///
/// 查询数据库中的所有数据类型信息
///
/// 返回数据表
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;
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data;
using System.IO;
namespace DeviceStatusDemo
{
///
/// 应用程序级别的单例类
///
public static class Env
{
static Env()
{
//增加sqlite数据库操作对象
string strSqliteConnStr = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "deviceStatusDemo.db");
SqliteDb = new SqliteDbHelper(strSqliteConnStr);
}
///
/// Sqlite数据库操作类对象
///
public static SqliteDbHelper SqliteDb { get; set; }
///
/// 创建数据库
///
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);
}
}
}
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DeviceStatusDemo
{
///
/// 设备状态改变处理
///
public class DeviceStatusUtil
{
///
/// 触发设备状态改变事件
/// 第一个参数:当前设备状态
/// 第二个参数:上次的设备状态
/// 第三个参数:班次
///
public static event Action EventDeviceStatusChanged;
///
/// 比较设备状态是否已改变
///
/// 当前设备状态
/// 上次的设备状态
///
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;
}
///
/// 获得正常情况下的班次
/// 【8点~20点是白班】
///
///
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 "夜班";
}
///
/// 获取上一次设备状态的记录开始时间
///
///
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());
}
///
/// 获取最后一次的设备状态代码
///
///
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);
}
///
/// 保存设备状态改变到数据库
/// 【类似于工作流的A环节 送出到 B环节】
/// 先更新上一条数据的结束时间为Now,持续时间,然后增加一条开始时间为Now,结束时间、持续时间为空的记录
///
///
///
public static int SaveDeviceStatus(DeviceStatusData data)
{
Dictionary dict = new Dictionary();
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 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;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DeviceStatusDemo
{
///
/// PLC操作类
/// 该类只是仿真,实际使用需要读写PLC的相关地址
///
public class PlcOperateUtil
{
///
/// 读取PLC指定地址的值,
/// 这里只是一个模拟器
///
///
///
public static short ReadInt16(string address)
{
return (short)new Random().Next(1, 6);
}
}
}
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
{
///
/// 是否在运行中
///
public bool IsRunning { get; set; }
///
/// 上一次的设备状态
///
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)}");
}
///
/// 设备状态更改事件
///
/// 当前设备状态
/// 上次设备状态
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);
}
///
/// 启用监控设备状态
///
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);
}
}
});
}
///
/// 停止监控
///
public void StopMonitor()
{
IsRunning = false;
//解除绑定设备状态更改事件
DeviceStatusUtil.EventDeviceStatusChanged -= DeviceStatusUtil_EventDeviceStatusChanged;
}
///
/// 显示富文本框的消息
///
///
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();
}));
}
///
/// 刷新设备状态
///
///
///
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();
}
}
}