using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Timers; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using System.Windows.Threading; using H.Pipes; using InABox.Logging; using InABox.Wpf; using Microsoft.Win32; namespace PRSServer { public static class ItemsControlExtensions { public static void ScrollIntoView( this ItemsControl control, object item) { var framework = control.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement; if (framework == null) return; framework.BringIntoView(); } public static void ScrollIntoView(this ItemsControl control) { var count = control.Items.Count; if (count == 0) return; var item = control.Items[count - 1]; control.ScrollIntoView(item); } } /// /// Interaction logic for Console.xaml /// public partial class Console : ThemableWindow { private PipeClient _client; public CollectionViewSource _filtered; private readonly ObservableCollection _logentries; private readonly bool _monitoronly = true; private PRSService _service; public string ServiceName { get; private set; } private readonly string description; private readonly TimeSpan regexTimeOut = TimeSpan.FromMilliseconds(100); private Timer? RefreshTimer; private Regex? searchRegex; public Console(string servicename, string description, bool monitoronly = true) { ServiceName = servicename; _monitoronly = monitoronly; InitializeComponent(); _logentries = new ObservableCollection(); _filtered = new CollectionViewSource(); _filtered.Source = _logentries; _filtered.Filter += (sender, args) => { var logEntry = (LogEntry)args.Item; if (ShowImportant.IsChecked == true && !IsImportant(logEntry)) { args.Accepted = false; return; } if (UseRegEx.IsChecked == true && searchRegex != null) args.Accepted = string.IsNullOrWhiteSpace(Search.Text) || searchRegex.IsMatch(logEntry.DateTime) || searchRegex.IsMatch(logEntry.Type) || searchRegex.IsMatch(logEntry.User) || searchRegex.IsMatch(logEntry.Message); else args.Accepted = string.IsNullOrWhiteSpace(Search.Text) || logEntry.DateTime.Contains(Search.Text) || logEntry.Type.Contains(Search.Text) || logEntry.User.Contains(Search.Text) || logEntry.Message.Contains(Search.Text); }; DataContext = _filtered; Title = description; this.description = description; } private bool IsImportant(LogEntry entry) { return entry.Type == "IMPTNT"; } private LogEntry ParseLogMessage(string logLine) { var datetime = logLine.Length > 32 ? logLine.Substring(0, 12) : string.Format("{0:HH:mm:ss.fff}", DateTime.Now); var type = logLine.Length > 32 ? logLine.Substring(13, 6).Trim() : "INFO"; var user = logLine.Length > 32 ? logLine.Substring(20, 12) : ""; var msg = logLine.Length > 32 ? logLine.Substring(33) : logLine; return new LogEntry { DateTime = datetime, Type = type, User = user, Message = msg }; } private void Console_Loaded(object sender, RoutedEventArgs e) { _client = new PipeClient(ServiceName, "."); _client.MessageReceived += (o, args) => { var m = args.Message; var logEntry = ParseLogMessage(args.Message ?? ""); var logType = logEntry.Type; if (logType == "INFO" || logType == "ERROR" || logType == "IMPTNT") Dispatcher.BeginInvoke((Action)(() => { _logentries.Insert(0, logEntry); })); }; _client.Connected += (o, args) => { Dispatcher.Invoke(() => LogBorder.Background = new SolidColorBrush(Colors.LightYellow)); }; _client.Disconnected += (o, args) => { Dispatcher.Invoke(() => LogBorder.Background = new SolidColorBrush(Colors.WhiteSmoke)); if (RefreshTimer == null) { RefreshTimer = new Timer(1000); RefreshTimer.Elapsed += RefreshTimer_Elapsed; } RefreshTimer.Start(); }; _client.ExceptionOccurred += (o, args) => { }; if (!_client.IsConnecting) { _client.ConnectAsync(); } if (!_monitoronly) { var timer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 3) }; timer.Tick += (o, args) => { timer.IsEnabled = false; _service = new PRSService(ServiceName, null); _service.Run(ServiceName); }; timer.IsEnabled = true; } } private void RefreshTimer_Elapsed(object? sender, ElapsedEventArgs e) { if(!_client.IsConnected) { if (!_client.IsConnecting) { _client.ConnectAsync(); } } else { RefreshTimer?.Stop(); } } private void Window_Closing(object sender, CancelEventArgs e) { if (_monitoronly) { _client.DisposeAsync().AsTask().Wait(); RefreshTimer?.Stop(); } else _service?.Halt(); } private void Search_KeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Enter && UseRegEx.IsChecked == true) { try { searchRegex = new Regex(Search.Text, RegexOptions.Compiled, regexTimeOut); } catch (ArgumentException) { searchRegex = null; } _filtered.View.Refresh(); SetSearchStyleNormal(); } } private void SetSearchStyleChanged() { Search.Background = Brushes.White; } private void SetSearchStyleNormal() { Search.Background = Brushes.LightYellow; } private void Search_TextChanged(object sender, TextChangedEventArgs e) { if (UseRegEx.IsChecked != true) { _filtered.View.Refresh(); } else { if (string.IsNullOrWhiteSpace(Search.Text)) { searchRegex = null; _filtered.View.Refresh(); SetSearchStyleNormal(); } else { SetSearchStyleChanged(); } } } private void UseRegEx_Checked(object sender, RoutedEventArgs e) { try { searchRegex = new Regex(Search.Text, RegexOptions.Compiled, regexTimeOut); } catch (ArgumentException ex) { searchRegex = null; } _filtered.View.Refresh(); } private void UseRegEx_Unchecked(object sender, RoutedEventArgs e) { searchRegex = null; _filtered.View.Refresh(); SetSearchStyleNormal(); } private void SetErrorMessage(string? error) { if (string.IsNullOrWhiteSpace(error)) { Error.Content = ""; Error.Visibility = Visibility.Collapsed; } else { Error.Content = error; Error.Visibility = Visibility.Visible; } } private void LoadLog_Click(object sender, RoutedEventArgs e) { var dialog = new OpenFileDialog(); dialog.InitialDirectory = DatabaseEngine.GetPath(ServiceName); if (dialog.ShowDialog() == true) { var logEntries = new List(); var numberSkipped = 0; var lines = File.ReadLines(dialog.FileName); foreach (var line in lines) { var logEntry = ParseLogMessage(line ?? ""); var logType = logEntry.Type; if (logType == "ERROR" || logType == "INFO" || logType == "IMPTNT") logEntries.Add(logEntry); else if (string.IsNullOrWhiteSpace(logType)) numberSkipped++; } if (numberSkipped > 0) { if (logEntries.Count == 0) SetErrorMessage("File does not contain valid log information!"); else SetErrorMessage(string.Format("Skipped {0} lines that did not contain valid log information", numberSkipped)); } Title = dialog.FileName; _filtered.Source = logEntries; LoadLog.Visibility = Visibility.Collapsed; CloseLog.Visibility = Visibility.Visible; } } private void CloseLog_Click(object sender, RoutedEventArgs e) { _filtered.Source = _logentries; CloseLog.Visibility = Visibility.Collapsed; LoadLog.Visibility = Visibility.Visible; Title = description; } private void ShowImportant_Checked(object sender, RoutedEventArgs e) { _filtered.View.Refresh(); } private void ShowImportant_Unchecked(object sender, RoutedEventArgs e) { _filtered.View.Refresh(); } } }