C#--运动控制--日志报警记录和操作记录--记录查询(SqlLite数据库操作)
以下是学习笔记
【分析】要记录哪些内容?有哪些分类?
操作记录:参数设置,手动控制(点了哪个控制按钮等),登录,退出。
日志信息:监控窗体的AddLog日志列表同时也写入数据库
报警信息:监控窗体的AddAlarm显示同事也写入数据库;打开不到位报警,伺服报警。
1,报警信息的界面UI
2,实体类
namespace AutomaticStoreMotionModels { /// <summary> /// 日志类 /// </summary> public class SysLog { public SysLog() { } public SysLog(string logInfo,string logAlarmState,LogTye logType,string user) { this.LogTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); this.LogInfo = logInfo; this.LogAlarmState = logAlarmState; this.LogType = logType; this.User = user; } public string LogTime { get; set; } public string LogInfo { get; set; } /// <summary> /// 记录报警状态,是触发报警还是消除报警 /// </summary> public string LogAlarmState { get; set; } /// <summary> /// 日志类型(枚举) /// </summary> public LogTye LogType { get; set; } public string User { get; set; } } /// <summary> /// 日志类型 /// </summary> public enum LogTye { 日志信息, 报警信息, 操作记录 } }
实体类的类名,属性要和数据表名,字段保持一致
3,DAL数据查询类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using AutomaticStoreMotionModels; using SqlSugar; namespace AutomaticStoreMotionDal { public class SysLogService { /// <summary> /// 插入一条记录 /// </summary> /// <param name="log">日志记录对象</param> /// <returns>是否成功</returns> public static bool AddSysLog(SysLog log) { return SqlSugarHelper.SqlSugarClient.Insertable(log).ExecuteCommand() == 1; } /// <summary> /// 查询日志信息 /// </summary> /// <param name="start">开始时间</param> /// <param name="end">结束时间</param> /// <param name="logtype">日志类型</param> /// <param name="logAlarmState">报警状态</param> /// <returns></returns> public static List<SysLog> GetSysLogByCondiiton(string start, string end, string logtype, string logAlarmState) { //根据时间查询 var query = SqlSugarHelper.SqlSugarClient.Queryable<SysLog>() .Where(c => SqlFunc.Between(c.LogTime, start, end)); //日志类型 if (logtype.Length > 0) { query.Where(c => c.LogType == (LogTye)Enum.Parse(typeof(LogTye),logtype)); } //报警状态 if (logAlarmState.Length > 0) { query.Where(c => c.LogAlarmState==logAlarmState); } return query.ToList(); } } }
4,使用
【4.1】 登录成功记录
【4.2】系统退出记录
private void FrmMain_FormClosing(object sender, FormClosingEventArgs e) { //写入数据库 if (!SysLogService.AddSysLog(new SysLog("系统退出", "触发", LogTye.操作记录, Program.GlobalSysAdmin.LoginName))) { NLogHelper.Error("日志信息写入数据库出错"); }; cts.Cancel(); }
【4.3】 参数修改记录
/// <summary> /// 修改参数 /// </summary> /// <typeparam name="T">对象类型</typeparam> /// <param name="parameterType">参数类型</param> /// <param name="obj">对象</param> private void ModifyParameter<T>(string parameterType,T obj) { StringBuilder sb=new StringBuilder(); foreach (var item in this.tab_main.SelectedTab.Controls) { if (item is NumericUpDown numeric) { //获取控件的名称(控件的名称对应的是对象属性的名称) string propertyName = numeric.Name; //通过对象属性名称拿到属性的值 string value = GetObjectPropertyVaule(obj, propertyName); //如果控制的值和对象属性的值不一致,表示有修改动作 if (value.Length>0&& numeric.Value.ToString() != value) { //添加修改记录 sb.Append(numeric.Tag.ToString() + "修改为:" + numeric.Value.ToString() + "; "); SetObjectPropertyVaule(obj, propertyName, numeric.Value.ToString()); } } else if(item is ComboBox cmb) { //获取控件的名称(控件的名称对应的是对象属性的名称) string propertyName = cmb.Name; //通过对象属性名称拿到属性的值 string value = GetObjectPropertyVaule(obj, propertyName); //如果控制的值和对象属性的值不一致,表示有修改动作 if (value.Length > 0 && cmb.Text != value) { //添加修改记录 sb.Append(cmb.Tag.ToString() + "修改为:" + cmb.Text.ToString() + "; "); SetObjectPropertyVaule(obj, propertyName, cmb.Text.ToString()); } } } if (sb.ToString().Length > 0) { OperationResult result = parameterType == "基础参数" ? motionEx.SaveBasicParmetmer() : motionEx.SaveAdvancedParmetmer(); if (result.IsSuccess) { //写入数据库 if (!SysLogService.AddSysLog(new SysLog(sb.ToString(), "触发", LogTye.操作记录, Program.GlobalSysAdmin.LoginName))) { NLogHelper.Error("参数修改记录写入数据库出错"); }; MessageBox.Show(parameterType+"修改成功", "参数修改"); } else { MessageBox.Show(parameterType + "修改失败", "参数修改"); } } else { MessageBox.Show("参数未做任何修改", "参数修改"); } }
【4.4】日志信息和报警信息
/// <summary> /// 添加日志信息 /// </summary> /// <param name="index">日志等级(0:info,1:warning,2:error)</param> /// <param name="log">日志信息</param> public void AddLog(int index, string log) { if (!this.lvw_info.InvokeRequired)//如果没有跨线程访问 { ListViewItem lstItem=new ListViewItem(CurrentTime,index); lstItem.SubItems.Add(log); this.lvw_info.Items.Insert(0, lstItem);//保证最新的显示在第一条 //只保留最后100条记录 if (lvw_info.Items.Count > 100) { lvw_info.Items.RemoveAt(100); } } else//如果有线程从多线程使用 { this.lvw_info.Invoke(new Action(() => { ListViewItem lstItem = new ListViewItem(CurrentTime, index); lstItem.SubItems.Add(log); this.lvw_info.Items.Insert(0, lstItem);//保证最新的显示在第一条 //只保留最后100条记录 if (lvw_info.Items.Count > 100) { lvw_info.Items.RemoveAt(100); } })); } //写入数据库 if (!SysLogService.AddSysLog(new SysLog(log, "触发", LogTye.日志信息, Program.GlobalSysAdmin.LoginName))) { NLogHelper.Error("日志信息写入数据库出错"); }; } /// <summary> /// 【报警显示步骤2】在监控窗体添加报警委托的原型 /// </summary> /// <param name="info">报警信息</param> /// <param name="isAck">触发报警还是消除报警</param> public void AddAlarm(string info, bool isAck) { if (isAck) { //如果 if (!AlarmInfoList.Contains(info)) { AlarmInfoList.Add(info); } } else { if (AlarmInfoList.Contains(info)) { AlarmInfoList.Remove(info); } } //刷新界面 RefreshAlarm(); //写入数据库 if (!SysLogService.AddSysLog(new SysLog(info,isAck? "触发":"消除", LogTye.日志信息, Program.GlobalSysAdmin.LoginName))) { NLogHelper.Error("日志信息写入数据库出错"); }; }
5,报警查询界面的代码编写:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using AutomaticStoreMotionDal; using AutomaticStoreMotionModels; namespace AutomaticStoreMotionPro { public partial class FrmSysLog : Form { public FrmSysLog() { InitializeComponent(); //关闭自动创建列 this.dgv_data.AutoGenerateColumns = false; //AutoSizeColumnsMode要设置为None,下面设置的固定宽度才有效果 this.dgv_data.AutoSizeColumnsMode= System.Windows.Forms.DataGridViewAutoSizeColumnsMode.None; this.dgv_data.Columns[0].Width = 150; this.dgv_data.Columns[1].AutoSizeMode=DataGridViewAutoSizeColumnMode.Fill;//这一列设置自动填充显示的跨度 this.dgv_data.Columns[2].Width = 100; this.dgv_data.Columns[3].Width = 100; this.dgv_data.Columns[4].Width = 100; this.chk_logInfo.Checked = true; this.rdb_ack.Checked = true; this.dtp_start.Value = this.dtp_end.Value.AddHours(-3.0); } private void btn_query_Click(object sender, EventArgs e) { Task.Run(() => { this.Invoke(new Action(() => { QueryProcess(DateTime.Now.ToString(this.dtp_start.Text), DateTime.Now.ToString(this.dtp_end.Text), GetLogType(), GetLogAlarmState()); })); }); } private void btn_queryToay_Click(object sender, EventArgs e) { Task.Run(() => { this.Invoke(new Action(() => { QueryProcess(DateTime.Now.ToString("yyyy-MM-dd 00:00:00"), DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),GetLogType(),GetLogAlarmState()); })); }); } /// <summary> /// 获取日志类型(查询条件,可以多选) /// </summary> /// <returns></returns> private List<string> GetLogType() { List<string> result=new List<string>(); if (chk_logInfo.Checked) { result.Add(this.chk_logInfo.Text); } if (chk_alarmInfo.Checked) { result.Add(this.chk_alarmInfo.Text); } if (chk_operationInfo.Checked) { result.Add(this.chk_operationInfo.Text); } return result; } /// <summary> /// 获取报警状态(查询条件,只能单选) /// </summary> /// <returns></returns> private string GetLogAlarmState() { if (rdb_ack.Checked) { return "触发"; } else if(rdb_unAck.Checked) { return "消除"; } else { return "全部"; } } /// <summary> /// 通用的查询方法 /// </summary> /// <param name="start"></param> /// <param name="end"></param> /// <param name="logtype"></param> /// <param name="logAlarmState"></param> private void QueryProcess(string start, string end,List<string> logtype, string logAlarmState) { DateTime t1 = Convert.ToDateTime(start); DateTime t2 = Convert.ToDateTime(end); if (t1 > t2) { new FrmConfirmSingle("日志查询", "开始时间不能大于结束时间") {TopMost = true}.ShowDialog(); return; } if (logAlarmState == "全部") { logAlarmState = string.Empty; } var List=new List<SysLog>(); if (logtype.Count > 0) { foreach (var item in logtype) { List.AddRange(SysLogService.GetSysLogByCondiiton(start,end,item,logAlarmState)); } } //排序【这个排序很重要,因为是根据日志类型分组查询的,导致List是根据分组来排列显示的,要用这个排序方法来设置按照时间顺序显示】 List.Sort((p1, p2) => { if (Convert.ToDateTime(p1.LogTime) > Convert.ToDateTime(p2.LogTime)) { return 1; } else if (Convert.ToDateTime(p1.LogTime) < Convert.ToDateTime(p2.LogTime)) { return -1; } else { return 0; } }); this.dgv_data.DataSource = null; this.dgv_data.DataSource = List; } private void btn_export_Click(object sender, EventArgs e) { string filePath= ExcelHelper.ExportToExcel(this.dgv_data); if (filePath!=null) { new FrmConfirmSingle("日志导出", "日志记录导出成功") { TopMost = true }.ShowDialog(); } else { new FrmConfirmSingle("日志导出", "日志记录导出失败:") { TopMost = true }.ShowDialog(); } } } }
6,效果展示