以下是學習筆記
【分析】要記錄哪些內容?有哪些分類?
操作記錄:參數設置,手動控制(點了哪個控制按鈕等),登錄,退出。
日志信息:監控窗體的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,效果展示