C#--运动控制--日志报警记录和操作记录--记录查询(SqlLite数据库操作)
以下是学习笔记
【分析】要记录哪些内容?有哪些分类?
操作记录:参数设置,手动控制(点了哪个控制按钮等),登录,退出。
日志信息:监控窗体的AddLog日志列表同时也写入数据库
报警信息:监控窗体的AddAlarm显示同事也写入数据库;打开不到位报警,伺服报警。
1,报警信息的界面UI
2,实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | 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数据查询类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 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】系统退出记录
1 2 3 4 5 6 7 8 9 10 | private void FrmMain_FormClosing( object sender, FormClosingEventArgs e) { //写入数据库 if (!SysLogService.AddSysLog( new SysLog( "系统退出" , "触发" , LogTye.操作记录, Program.GlobalSysAdmin.LoginName))) { NLogHelper.Error( "日志信息写入数据库出错" ); }; cts.Cancel(); } |
【4.3】 参数修改记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | /// <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】日志信息和报警信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | /// <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,报警查询界面的代码编写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | 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,效果展示
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构