919 lines
30 KiB
C#
919 lines
30 KiB
C#
|
using Aitex.Core.RT.Event;
|
|||
|
using Aitex.Core.RT.Log;
|
|||
|
using MECF.Framework.Common.DataCenter;
|
|||
|
using MECF.Framework.Common.Utilities;
|
|||
|
using MECF.Framework.UI.Client.ClientBase;
|
|||
|
using OpenSEMI.ClientBase.Command;
|
|||
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Data;
|
|||
|
using System.Diagnostics;
|
|||
|
using System.IO;
|
|||
|
using System.Linq;
|
|||
|
using System.Text;
|
|||
|
using System.Threading.Tasks;
|
|||
|
using System.Windows;
|
|||
|
using System.Windows.Input;
|
|||
|
using System.Windows.Media.Imaging;
|
|||
|
using Sicentury.Core.Collections;
|
|||
|
|
|||
|
namespace MECF.Framework.UI.Client.CenterViews.DataLogs.Event
|
|||
|
{
|
|||
|
public class EventViewModel : BaseModel
|
|||
|
{
|
|||
|
#region Variables
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 首次查询。需要计算总页码等参数。
|
|||
|
/// </summary>
|
|||
|
private const int QUERY_FIRST_TIME = -1;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 一次性查询所有数据,EXCEL导出使用。
|
|||
|
/// </summary>
|
|||
|
private const int QUERY_FOR_EXCEL_EXPORT = 0;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 指示函数<see cref="tbLoadPort1Selection"/>是否发生了重入。如果发生重入,说明“ALL”由
|
|||
|
/// 后台程序勾选,此时不要进行任何逻辑操作,仅设置”ALL“的选择状态。
|
|||
|
/// 重入的情况发生在:
|
|||
|
/// 1. 所有选项全部被选中
|
|||
|
/// 2. 此时将“ALL”以外的选项勾掉
|
|||
|
/// 3. “ALL”自动取消选择
|
|||
|
/// 上述2会触发3,引起函数重入。
|
|||
|
/// </summary>
|
|||
|
private bool _isEventSourceFilterSelectionReentered = false;
|
|||
|
|
|||
|
private int _currentPage;
|
|||
|
private int _totalPage;
|
|||
|
private int _selectedPage;
|
|||
|
private int _selectedPaginationCapacity;
|
|||
|
private bool _isLoading;
|
|||
|
private bool _isExporting;
|
|||
|
private string _exportingMessage;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 显示查询到的事件日志列表。
|
|||
|
/// </summary>
|
|||
|
private readonly IProgress<List<EventItem>> _progShowSearchingResult;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 构造Event Source列表。
|
|||
|
/// </summary>
|
|||
|
private readonly IProgress<List<string>> _progConstructEventSourcesList;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 更新分页信息。
|
|||
|
/// <para>Tuple.Item1: 当前页号</para>
|
|||
|
/// <para>Tuple.Item2: 总页号;设置为-1时表示忽略更新总页号。</para>
|
|||
|
/// </summary>
|
|||
|
private readonly IProgress<Tuple<int, int>> _progUpdatePaginationInfo;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 更新查询状态。
|
|||
|
/// </summary>
|
|||
|
private readonly IProgress<bool> _progUpdateQueryStatus;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 更新日志导出状态。
|
|||
|
/// <para>Tuple.Item1: 导出进度,0~100</para>
|
|||
|
/// <para>Tuple.Item2: 消息</para>
|
|||
|
/// </summary>
|
|||
|
private readonly IProgress<Tuple<int, string>> _progUpdateExportingState;
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Constructors
|
|||
|
|
|||
|
public EventViewModel()
|
|||
|
{
|
|||
|
DisplayName = "Event";
|
|||
|
|
|||
|
QueryEventList = () =>
|
|||
|
{
|
|||
|
var result = new List<string>();
|
|||
|
|
|||
|
foreach (var eventName in Enum.GetNames(typeof(EventEnum)))
|
|||
|
result.Add(eventName);
|
|||
|
|
|||
|
return result;
|
|||
|
};
|
|||
|
|
|||
|
SearchedResult = new ObservableRangeCollection<SystemLogItem>();
|
|||
|
FilterEventSources = new ObservableRangeCollection<string>();
|
|||
|
SelectedFilterEventSource = new ObservableRangeCollection<string>();
|
|||
|
PaginationSource = new ObservableRangeCollection<int>();
|
|||
|
|
|||
|
SearchKeyWords = string.Empty;
|
|||
|
|
|||
|
SearchAlarmEvent = true;
|
|||
|
SearchWarningEvent = true;
|
|||
|
SearchInfoEvent = true;
|
|||
|
SearchOpeLog = false;
|
|||
|
|
|||
|
SearchPMA = false;
|
|||
|
SearchPMB = false;
|
|||
|
SearchPMC = false;
|
|||
|
SearchPMD = false;
|
|||
|
SearchTM = false;
|
|||
|
SearchLL = false;
|
|||
|
SearchSystem = false;
|
|||
|
|
|||
|
tbLoadPort1SelectionChangedCommand = new BaseCommand<object>(tbLoadPort1Selection);
|
|||
|
|
|||
|
|
|||
|
// 分页支持的每页Log数量
|
|||
|
//! 最小每页条目数使用30,以保证正好显示一页而不出现纵向滚动条。
|
|||
|
PaginationCapacity = new List<int>
|
|||
|
{
|
|||
|
30,
|
|||
|
300,
|
|||
|
3000
|
|||
|
};
|
|||
|
|
|||
|
_selectedPaginationCapacity = PaginationCapacity.Last();
|
|||
|
_totalPage = 1;
|
|||
|
_currentPage = 1;
|
|||
|
|
|||
|
NavigateCommand = new BaseCommand<string>(Navigate, (o) => true);
|
|||
|
|
|||
|
#region 显示查询到的EventLog列表
|
|||
|
|
|||
|
_progShowSearchingResult = new Progress<List<EventItem>>((queryRet =>
|
|||
|
{
|
|||
|
if (SearchedResult == null)
|
|||
|
SearchedResult = new ObservableRangeCollection<SystemLogItem>();
|
|||
|
else
|
|||
|
SearchedResult.Clear();
|
|||
|
|
|||
|
SearchedResult.AddRange(queryRet.Select(x =>
|
|||
|
new SystemLogItem()
|
|||
|
{
|
|||
|
Time = (x.OccuringTime).ToString("yyyy/MM/dd HH:mm:ss.fff"),
|
|||
|
LogType = x.Level.ToString(),
|
|||
|
Detail = x.Description,
|
|||
|
TargetChamber = x.Source,
|
|||
|
Initiator = "",
|
|||
|
Icon = new BitmapImage(new Uri(
|
|||
|
$"pack://application:,,,/MECF.Framework.UI.Core;component/Resources/SystemLog/{x.Level}.png",
|
|||
|
UriKind.Absolute))
|
|||
|
}
|
|||
|
));
|
|||
|
}));
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region 构造Event Sources列表
|
|||
|
|
|||
|
_progConstructEventSourcesList = new Progress<List<string>>((list =>
|
|||
|
{
|
|||
|
SelectedFilterEventSource.Clear();
|
|||
|
FilterEventSources.Clear();
|
|||
|
|
|||
|
SelectedFilterEventSource.Add("ALL");
|
|||
|
FilterEventSources.Add("ALL");
|
|||
|
|
|||
|
SelectedFilterEventSource.AddRange(list);
|
|||
|
FilterEventSources.AddRange(list);
|
|||
|
|
|||
|
}));
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region 更新分页信息
|
|||
|
|
|||
|
_progUpdatePaginationInfo = new Progress<Tuple<int, int>>(pageInfo =>
|
|||
|
{
|
|||
|
_currentPage = pageInfo.Item1;
|
|||
|
|
|||
|
if (pageInfo.Item2 > 0)
|
|||
|
{
|
|||
|
_totalPage = pageInfo.Item2;
|
|||
|
PaginationSource.Clear();
|
|||
|
PaginationSource.AddRange(Enumerable.Range(1, _totalPage));
|
|||
|
NotifyOfPropertyChange(nameof(PaginationSource));
|
|||
|
}
|
|||
|
|
|||
|
NotifyOfPropertyChange(nameof(PageInfo));
|
|||
|
|
|||
|
_selectedPage = _currentPage;
|
|||
|
NotifyOfPropertyChange(nameof(SelectedPage));
|
|||
|
});
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region 更新查询状态
|
|||
|
|
|||
|
_progUpdateQueryStatus = new Progress<bool>(isBusy =>
|
|||
|
{
|
|||
|
IsLoading = isBusy;
|
|||
|
});
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region 更新日志导出进度信息
|
|||
|
|
|||
|
_progUpdateExportingState = new Progress<Tuple<int, string>>(state =>
|
|||
|
{
|
|||
|
var progress = state.Item1;
|
|||
|
var message = state.Item2;
|
|||
|
|
|||
|
switch (progress)
|
|||
|
{
|
|||
|
case 0:
|
|||
|
IsExporting = true;
|
|||
|
break;
|
|||
|
|
|||
|
case 100:
|
|||
|
IsExporting = false;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
ExportingMessage = message;
|
|||
|
});
|
|||
|
|
|||
|
#endregion
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Properties
|
|||
|
|
|||
|
public bool SearchAlarmEvent { get; set; }
|
|||
|
|
|||
|
public bool SearchWarningEvent { get; set; }
|
|||
|
|
|||
|
public bool SearchInfoEvent { get; set; }
|
|||
|
|
|||
|
public bool SearchOpeLog { get; set; }
|
|||
|
|
|||
|
public bool SearchPMA { get; set; }
|
|||
|
|
|||
|
public bool SearchPMB { get; set; }
|
|||
|
|
|||
|
public bool SearchPMC { get; set; }
|
|||
|
|
|||
|
public bool SearchPMD { get; set; }
|
|||
|
|
|||
|
//public bool SearchCoolDown { get; set; }
|
|||
|
public bool SearchTM { get; set; }
|
|||
|
|
|||
|
public bool SearchLL { get; set; }
|
|||
|
|
|||
|
//public bool SearchBuf1 { get; set; }
|
|||
|
public bool SearchSystem { get; set; }
|
|||
|
|
|||
|
public string SearchKeyWords { get; set; }
|
|||
|
|
|||
|
public string FilterKeyWords { get; set; }
|
|||
|
|
|||
|
public DateTime SearchBeginTime
|
|||
|
{
|
|||
|
get => ((EventView)view).wfTimeFrom.Value;
|
|||
|
set
|
|||
|
{
|
|||
|
((EventView)view).wfTimeFrom.Value = value;
|
|||
|
NotifyOfPropertyChange(nameof(SearchBeginTime));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public DateTime SearchEndTime
|
|||
|
{
|
|||
|
get => ((EventView)view).wfTimeTo.Value;
|
|||
|
set
|
|||
|
{
|
|||
|
((EventView)view).wfTimeTo.Value = value;
|
|||
|
NotifyOfPropertyChange(nameof(SearchEndTime));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public List<string> EventList { get; set; }
|
|||
|
|
|||
|
public string SelectedEvent { get; set; }
|
|||
|
|
|||
|
public ObservableRangeCollection<SystemLogItem> SearchedResult { get; private set; }
|
|||
|
|
|||
|
public Func<List<string>> QueryEventList { get; set; }
|
|||
|
|
|||
|
public bool IsPermission => Permission == 3;
|
|||
|
|
|||
|
private EventView view;
|
|||
|
|
|||
|
public ObservableRangeCollection<string> FilterEventSources { get; set; }
|
|||
|
|
|||
|
public ObservableRangeCollection<string> SelectedFilterEventSource { get; set; }
|
|||
|
|
|||
|
public ICommand tbLoadPort1SelectionChangedCommand { get; set; }
|
|||
|
//public ICommand tbLoadPort2SelectionChangedCommand { get; set; }
|
|||
|
|
|||
|
public ICommand NavigateCommand { get; set; }
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 返回受支持的分页每页容量。
|
|||
|
/// </summary>
|
|||
|
public List<int> PaginationCapacity { get; }
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 页码列表,用于快速跳转页面下拉框数据源。
|
|||
|
/// </summary>
|
|||
|
public ObservableRangeCollection<int> PaginationSource { get; }
|
|||
|
|
|||
|
public int SelectedPage
|
|||
|
{
|
|||
|
get => _selectedPage;
|
|||
|
set
|
|||
|
{
|
|||
|
_selectedPage = value;
|
|||
|
Query(_selectedPage);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 返回选择的分页每页容量。
|
|||
|
/// </summary>
|
|||
|
public int SelectedPaginationCapacity
|
|||
|
{
|
|||
|
get => _selectedPaginationCapacity;
|
|||
|
set
|
|||
|
{
|
|||
|
_selectedPaginationCapacity = value;
|
|||
|
Query();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 页码信息。
|
|||
|
/// </summary>
|
|||
|
public string PageInfo => $"{_currentPage}/{_totalPage}";
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 返回是否正在加载数据。
|
|||
|
/// </summary>
|
|||
|
public bool IsLoading
|
|||
|
{
|
|||
|
get => _isLoading;
|
|||
|
set
|
|||
|
{
|
|||
|
_isLoading = value;
|
|||
|
NotifyOfPropertyChange(nameof(IsLoading));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 是否正在导出数据。
|
|||
|
/// </summary>
|
|||
|
public bool IsExporting
|
|||
|
{
|
|||
|
get => _isExporting;
|
|||
|
private set
|
|||
|
{
|
|||
|
_isExporting = value;
|
|||
|
NotifyOfPropertyChange(nameof(IsExporting));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 导出进度消息。
|
|||
|
/// </summary>
|
|||
|
public string ExportingMessage
|
|||
|
{
|
|||
|
get => _exportingMessage;
|
|||
|
private set
|
|||
|
{
|
|||
|
_exportingMessage = value;
|
|||
|
NotifyOfPropertyChange(nameof(ExportingMessage));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Methods
|
|||
|
|
|||
|
public void Navigate(string args)
|
|||
|
{
|
|||
|
switch (args)
|
|||
|
{
|
|||
|
case "first":
|
|||
|
if (_currentPage == 1)
|
|||
|
return;
|
|||
|
_currentPage = 1;
|
|||
|
break;
|
|||
|
|
|||
|
case "previous":
|
|||
|
if(_currentPage > 1)
|
|||
|
_currentPage--;
|
|||
|
break;
|
|||
|
|
|||
|
case "next":
|
|||
|
if(_currentPage < _totalPage)
|
|||
|
_currentPage++;
|
|||
|
break;
|
|||
|
|
|||
|
case "last":
|
|||
|
if (_currentPage == _totalPage)
|
|||
|
return;
|
|||
|
_currentPage = _totalPage;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
Query(_currentPage);
|
|||
|
}
|
|||
|
|
|||
|
protected override void OnViewLoaded(object view)
|
|||
|
{
|
|||
|
base.OnViewLoaded(view);
|
|||
|
|
|||
|
// 默认的查询时间设置为当天
|
|||
|
this.view = (EventView)view;
|
|||
|
SearchBeginTime = DateTime.Today;
|
|||
|
SearchEndTime = DateTime.Today.AddDays(1).AddTicks(-1);
|
|||
|
Preload();
|
|||
|
}
|
|||
|
|
|||
|
public void Preload()
|
|||
|
{
|
|||
|
EventList = new List<string> { "All" };
|
|||
|
|
|||
|
if (QueryEventList != null)
|
|||
|
{
|
|||
|
var evList = QueryEventList();
|
|||
|
foreach (var ev in evList)
|
|||
|
EventList.Add(ev);
|
|||
|
}
|
|||
|
|
|||
|
SelectedEvent = "All";
|
|||
|
|
|||
|
Query();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 打开YALV日志管理器。
|
|||
|
/// </summary>
|
|||
|
public void OpenDetailedLogViewer()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
// 检查YALV是否已经打开
|
|||
|
var yalv = Process.GetProcesses().FirstOrDefault(p => p.ProcessName.ToLower().Contains("yalv"));
|
|||
|
if (yalv != null)
|
|||
|
throw new InvalidOperationException("日志管理器已经打开。");
|
|||
|
|
|||
|
const string PATH_YALV = @"yalv\yalv.exe";
|
|||
|
var yalvPath = Path.Combine(Environment.CurrentDirectory, PATH_YALV);
|
|||
|
|
|||
|
// check the existence of the YALV
|
|||
|
if (File.Exists(yalvPath) == false)
|
|||
|
{
|
|||
|
throw new FileNotFoundException("日志管理器可执行文件不存在。");
|
|||
|
}
|
|||
|
|
|||
|
var logFile = Path.Combine(Environment.CurrentDirectory, $"logs\\log{DateTime.Now:yyyyMMdd}.xlog");
|
|||
|
|
|||
|
var proc = new Process()
|
|||
|
{
|
|||
|
StartInfo = new ProcessStartInfo
|
|||
|
{
|
|||
|
FileName = yalvPath,
|
|||
|
Arguments = $"\"{logFile}\""
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
proc.Start();
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
LOG.Write(ex);
|
|||
|
MessageBox.Show($"打开日志管理器失败,{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Event Source中全选和全不选逻辑实现。
|
|||
|
/// <see cref="_isEventSourceFilterSelectionReentered"/>
|
|||
|
/// </summary>
|
|||
|
/// <param name="o"></param>
|
|||
|
public void tbLoadPort1Selection(object o)
|
|||
|
{
|
|||
|
if (_isEventSourceFilterSelectionReentered)
|
|||
|
return;
|
|||
|
|
|||
|
_isEventSourceFilterSelectionReentered = true;
|
|||
|
|
|||
|
if (o is ItemSelectionData item)
|
|||
|
{
|
|||
|
if (item.SelectItem == "ALL")
|
|||
|
{
|
|||
|
//if (isAllSelectedInBackend)
|
|||
|
// return;
|
|||
|
|
|||
|
if (item.IsSelect)
|
|||
|
{
|
|||
|
SelectedFilterEventSource.Clear();
|
|||
|
SelectedFilterEventSource.AddRange(FilterEventSources);
|
|||
|
//FilterEventSources.ToList().ForEach(sp =>
|
|||
|
//{
|
|||
|
// if (!SelectedFilterEventSource.Contains(sp))
|
|||
|
// SelectedFilterEventSource.Add(sp);
|
|||
|
//});
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
SelectedFilterEventSource.Clear();
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// 判断是否需要取消或选中“ALL”
|
|||
|
var list =
|
|||
|
FilterEventSources
|
|||
|
.Except(new[] {"ALL"})
|
|||
|
.Except(SelectedFilterEventSource.Except(new [] {"ALL"}))
|
|||
|
.ToList();
|
|||
|
|
|||
|
//isAllSelectedInBackend = true;
|
|||
|
|
|||
|
if (list.Any())
|
|||
|
{
|
|||
|
if (SelectedFilterEventSource.Contains("ALL"))
|
|||
|
SelectedFilterEventSource.Remove("ALL");
|
|||
|
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
SelectedFilterEventSource.Clear();
|
|||
|
SelectedFilterEventSource.AddRange(FilterEventSources);
|
|||
|
//FilterEventSources.ToList().ForEach(sp =>
|
|||
|
//{
|
|||
|
// if (!SelectedFilterEventSource.Contains(sp))
|
|||
|
// SelectedFilterEventSource.Add(sp);
|
|||
|
//});
|
|||
|
}
|
|||
|
|
|||
|
//isAllSelectedInBackend = false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
_isEventSourceFilterSelectionReentered = false;
|
|||
|
}
|
|||
|
|
|||
|
private string GetSourceWhere()
|
|||
|
{
|
|||
|
return "";
|
|||
|
}
|
|||
|
|
|||
|
#region 数据库查询相关函数
|
|||
|
|
|||
|
#region SQL 语句构造函数
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取EventLog查询语句中select部分表达式。
|
|||
|
/// </summary>
|
|||
|
private string GetEventLogQuerySql()
|
|||
|
{
|
|||
|
return
|
|||
|
"SELECT \"event_id\", \"event_enum\", \"type\", \"occur_time\", \"level\",\"source\" , \"description\" FROM \"event_data\"" +
|
|||
|
GetWhereTimeRangeExpression();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取EventLog行数查询语句中select部分表达式。
|
|||
|
/// </summary>
|
|||
|
private string GetEventLogCountSql()
|
|||
|
{
|
|||
|
return
|
|||
|
"SELECT COUNT(1) FROM \"event_data\"" + GetWhereTimeRangeExpression(); ;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取EventLog行数查询语句中select部分表达式。
|
|||
|
/// </summary>
|
|||
|
private string GetChamberListSql()
|
|||
|
{
|
|||
|
return
|
|||
|
"SELECT DISTINCT \"source\" FROM \"event_data\"" + GetWhereTimeRangeExpression();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取WHERE条件中的“时间范围”子句。
|
|||
|
/// <para>注意:所有Select语句构造时必须包含TimeRange条件。</para>
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
private string GetWhereTimeRangeExpression()
|
|||
|
{
|
|||
|
|
|||
|
var sql = new StringBuilder(
|
|||
|
$" WHERE \"occur_time\" BETWEEN '{SearchBeginTime:yyyy/MM/dd HH:mm:ss}' AND '{SearchEndTime:yyyy/MM/dd HH:mm:ss}'");
|
|||
|
|
|||
|
return sql.ToString();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取WHERE条件中的“Log Level”子句。
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
private string GetWhereLevelExpression()
|
|||
|
{
|
|||
|
var sqlLevelConditions = new List<string>();
|
|||
|
if (SearchInfoEvent)
|
|||
|
sqlLevelConditions.Add("'Information'");
|
|||
|
|
|||
|
if (SearchWarningEvent)
|
|||
|
sqlLevelConditions.Add("'Warning'");
|
|||
|
|
|||
|
if (SearchAlarmEvent)
|
|||
|
sqlLevelConditions.Add("'Alarm'");
|
|||
|
|
|||
|
return $" AND (\"level\" in ({string.Join(",", sqlLevelConditions)}))";
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取WHERE条件中的“关键字”子句。
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
private string GetWhereQueryKeyWordsExpression()
|
|||
|
{
|
|||
|
return string.IsNullOrEmpty(SearchKeyWords)
|
|||
|
? ""
|
|||
|
: $" AND (lower(\"description\") like lower('%{SearchKeyWords}%'))";
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取WHERE条件中的“事件类型过滤条件“子句。
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
private string GetWhereFilterEventSourceExpression()
|
|||
|
{
|
|||
|
if (SelectedFilterEventSource == null || SelectedFilterEventSource.Count <= 0)
|
|||
|
throw new ArgumentOutOfRangeException(nameof(SelectedFilterEventSource),
|
|||
|
"no event source(s) in filter are selected.");
|
|||
|
|
|||
|
if (SelectedFilterEventSource.Contains("ALL"))
|
|||
|
return "";
|
|||
|
|
|||
|
return $" AND (\"source\" in ({string.Join(",", SelectedFilterEventSource.Select(x => $"'{x}'"))}))";
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取WHERE条件中的“事件描述关键字过滤条件“子句。
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
private string GetWhereFilterEventKeywordExpression()
|
|||
|
{
|
|||
|
return string.IsNullOrEmpty(FilterKeyWords)
|
|||
|
? ""
|
|||
|
: $" AND (lower(\"description\") like lower('%{FilterKeyWords}%'))";
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取SQL查询语句中的分页表达式。
|
|||
|
/// </summary>
|
|||
|
/// <param name="countPerPage"></param>
|
|||
|
/// <param name="currentPage"></param>
|
|||
|
/// <returns></returns>
|
|||
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
|||
|
private string GetPaginationExpression(int countPerPage, int currentPage)
|
|||
|
{
|
|||
|
if (countPerPage <= 0)
|
|||
|
throw new ArgumentOutOfRangeException(nameof(countPerPage),
|
|||
|
"the amount items per page can not be less than 1.");
|
|||
|
|
|||
|
if (currentPage <= 0)
|
|||
|
throw new ArgumentOutOfRangeException(nameof(currentPage),
|
|||
|
"the current page required can not be less than 1.");
|
|||
|
|
|||
|
|
|||
|
|
|||
|
return $" LIMIT {countPerPage} OFFSET {countPerPage * (currentPage - 1)}";
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 查询当前条件下共有多少行数据。
|
|||
|
/// <param name="isIncludeFilter">是否包含过滤条件。</param>
|
|||
|
/// </summary>
|
|||
|
private int QueryRowAmount(bool isIncludeFilter)
|
|||
|
{
|
|||
|
var sql = GetEventLogCountSql()
|
|||
|
+ GetWhereLevelExpression()
|
|||
|
+ GetWhereQueryKeyWordsExpression();
|
|||
|
|
|||
|
if (isIncludeFilter)
|
|||
|
sql += GetWhereFilterEventSourceExpression()
|
|||
|
+ GetWhereFilterEventKeywordExpression();
|
|||
|
|
|||
|
var dt = QueryDataClient.Instance.Service.QueryData(sql);
|
|||
|
|
|||
|
// 未查询到任何数据。
|
|||
|
if (dt == null || dt.Rows.Count <= 0 || dt.Columns.Count <= 0)
|
|||
|
return 0;
|
|||
|
|
|||
|
// 返回满足当前条件的行数。
|
|||
|
return int.Parse(dt.Rows[0][0].ToString());
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 查询Event源列表。
|
|||
|
/// <para>注意:该查询仅在首次查询时使用;语句的条件仅只用时间范围和事件类型,不包含Filter选项。</para>
|
|||
|
/// <para>当套用Filter选项查询、或分页查询时,不再重构Event源列表。</para>
|
|||
|
/// </summary>
|
|||
|
/// <returns></returns>
|
|||
|
private List<string> QueryEventSources()
|
|||
|
{
|
|||
|
var chamberList = new List<string>();
|
|||
|
|
|||
|
var sql = GetChamberListSql() + GetWhereLevelExpression();
|
|||
|
var dt = QueryDataClient.Instance.Service.QueryData(sql);
|
|||
|
|
|||
|
// 未查询到任何数据。
|
|||
|
if (dt == null || dt.Rows.Count <= 0 || dt.Columns.Count <= 0)
|
|||
|
return chamberList;
|
|||
|
|
|||
|
// 返回满足当前条件的行数。
|
|||
|
chamberList.AddRange(from DataRow row in dt.Rows select row[0].ToString());
|
|||
|
|
|||
|
return chamberList;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 查询符合条件的事件日志。
|
|||
|
/// </summary>
|
|||
|
/// <param name="page">待查询页码。0:查询所有数据而不分页;其它:查询指定的页码。</param>
|
|||
|
/// <param name="isIncludeFilter">是否包含过滤条件。</param>
|
|||
|
/// <exception cref="ArgumentOutOfRangeException">页码错误,页码必须为大于等于0的整数。</exception>
|
|||
|
/// <returns></returns>
|
|||
|
private List<EventItem> QueryEventLogs(int page, bool isIncludeFilter)
|
|||
|
{
|
|||
|
if (page < 0)
|
|||
|
throw new ArgumentOutOfRangeException(nameof(page), "the page number must be greater than 0.");
|
|||
|
|
|||
|
var sql = GetEventLogQuerySql()
|
|||
|
+ GetWhereLevelExpression()
|
|||
|
+ GetWhereQueryKeyWordsExpression();
|
|||
|
|
|||
|
if (isIncludeFilter)
|
|||
|
sql += GetWhereFilterEventSourceExpression()
|
|||
|
+ GetWhereFilterEventKeywordExpression();
|
|||
|
|
|||
|
// page == 0 表示查询所有数据,仅导出Excel时使用。
|
|||
|
if(page > 0)
|
|||
|
sql += GetPaginationExpression(SelectedPaginationCapacity, page);
|
|||
|
|
|||
|
return QueryDataClient.Instance.Service.QueryDBEvent(sql);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 查询数据。
|
|||
|
/// </summary>
|
|||
|
/// <param name="page">待查询的页号</param>
|
|||
|
/// <param name="isIncludeFilter">是否包含过滤条件。</param>
|
|||
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
|||
|
public void Query(int page = QUERY_FIRST_TIME, bool isIncludeFilter = false)
|
|||
|
{
|
|||
|
if (IsLoading)
|
|||
|
return;
|
|||
|
|
|||
|
if (SearchBeginTime > SearchEndTime)
|
|||
|
{
|
|||
|
MessageBox.Show("Time range invalid, start time should be early than end time");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
Task.Run(() =>
|
|||
|
{
|
|||
|
var searchingResult = new List<EventItem>();
|
|||
|
|
|||
|
// 查询开始
|
|||
|
_progUpdateQueryStatus.Report(true);
|
|||
|
|
|||
|
if (page <= QUERY_FIRST_TIME) // Query按钮按下,或首次加载界面
|
|||
|
{
|
|||
|
// 统计总页数
|
|||
|
var rowCount = QueryRowAmount(isIncludeFilter);
|
|||
|
|
|||
|
var totalPage = (int)Math.Ceiling(rowCount / (double)SelectedPaginationCapacity);
|
|||
|
|
|||
|
// 首次查询第一页
|
|||
|
searchingResult = QueryEventLogs(1, isIncludeFilter);
|
|||
|
|
|||
|
if (isIncludeFilter == false)
|
|||
|
{
|
|||
|
// 构造Event Sources列表,仅在首次查询时构造
|
|||
|
var eventSources = QueryEventSources();
|
|||
|
_progConstructEventSourcesList.Report(eventSources);
|
|||
|
}
|
|||
|
|
|||
|
// 更新页面信息
|
|||
|
_progUpdatePaginationInfo.Report(new Tuple<int, int>(1, totalPage));
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
else if (page == QUERY_FOR_EXCEL_EXPORT)
|
|||
|
{
|
|||
|
|
|||
|
}
|
|||
|
else if (page >= 1)
|
|||
|
{
|
|||
|
// 翻页按钮按下
|
|||
|
searchingResult = QueryEventLogs(page, isIncludeFilter);
|
|||
|
|
|||
|
// 更新页面信息
|
|||
|
_progUpdatePaginationInfo.Report(new Tuple<int, int>(page, -1));
|
|||
|
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
throw new ArgumentOutOfRangeException(nameof(page), "invalid page number.");
|
|||
|
}
|
|||
|
|
|||
|
// 显示结果
|
|||
|
_progShowSearchingResult.Report(searchingResult);
|
|||
|
|
|||
|
// 查询完毕
|
|||
|
_progUpdateQueryStatus.Report(false);
|
|||
|
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 导出
|
|||
|
/// </summary>
|
|||
|
public async void Export()
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
var dlg = new Microsoft.Win32.SaveFileDialog();
|
|||
|
dlg.DefaultExt = ".xlsx"; // Default file extension
|
|||
|
dlg.FileName = $"Operation Log_{DateTime.Now:yyyyMMdd_HHmmss}";
|
|||
|
dlg.Filter = "Excel数据表格文件(*.xlsx)|*.xlsx"; // Filter files by extension
|
|||
|
var result = dlg.ShowDialog(); // Show open file dialog box
|
|||
|
|
|||
|
if (result != true)
|
|||
|
return;
|
|||
|
|
|||
|
await Task.Run(() =>
|
|||
|
{
|
|||
|
|
|||
|
_progUpdateExportingState.Report(new Tuple<int, string>(0, "Querying data ..."));
|
|||
|
|
|||
|
// 查询数据
|
|||
|
var queryRet = QueryEventLogs(0, false);
|
|||
|
if (queryRet == null || queryRet.Count <= 0)
|
|||
|
throw new Exception("no event logs are found, please change the conditions and retry.");
|
|||
|
|
|||
|
|
|||
|
var ds = new DataSet();
|
|||
|
ds.Tables.Add(new System.Data.DataTable("系统运行日志"));
|
|||
|
ds.Tables[0].Columns.Add("Type");
|
|||
|
ds.Tables[0].Columns.Add("Time");
|
|||
|
ds.Tables[0].Columns.Add("System");
|
|||
|
ds.Tables[0].Columns.Add("Content");
|
|||
|
|
|||
|
var currRow = 0;
|
|||
|
var totalRow = queryRet.Count;
|
|||
|
foreach (var item in queryRet)
|
|||
|
{
|
|||
|
currRow++;
|
|||
|
|
|||
|
_progUpdateExportingState.Report(new Tuple<int, string>(50,
|
|||
|
$"Exporting item {currRow}/{totalRow} ..."));
|
|||
|
|
|||
|
var row = ds.Tables[0].NewRow();
|
|||
|
row[0] = item.Level;
|
|||
|
row[1] = item.OccuringTime.ToString("yyyy/MM/dd HH:mm:ss.fff");
|
|||
|
row[2] = item.Source;
|
|||
|
row[3] = item.Description;
|
|||
|
ds.Tables[0].Rows.Add(row);
|
|||
|
}
|
|||
|
|
|||
|
_progUpdateExportingState.Report(new Tuple<int, string>(60, "Writing excel file ..."));
|
|||
|
if (!ExcelHelper.ExportToExcel(dlg.FileName, ds, out var reason))
|
|||
|
throw new InvalidOperationException($"Export failed, {reason}");
|
|||
|
|
|||
|
_progUpdateExportingState.Report(new Tuple<int, string>(100, "Done!"));
|
|||
|
});
|
|||
|
|
|||
|
MessageBox.Show($"Export succeed, file save as {dlg.FileName}", "Export", MessageBoxButton.OK,
|
|||
|
MessageBoxImage.Information);
|
|||
|
}
|
|||
|
catch (Exception ex)
|
|||
|
{
|
|||
|
LOG.Write(ex);
|
|||
|
MessageBox.Show("导出系统日志发生错误", "导出失败", MessageBoxButton.OK, MessageBoxImage.Warning);
|
|||
|
}
|
|||
|
finally
|
|||
|
{
|
|||
|
_progUpdateExportingState.Report(new Tuple<int, string>(100, "Done!"));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#endregion
|
|||
|
}
|
|||
|
}
|