Console.xaml.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.ComponentModel;
  5. using System.IO;
  6. using System.Text.RegularExpressions;
  7. using System.Threading.Tasks;
  8. using System.Timers;
  9. using System.Windows;
  10. using System.Windows.Controls;
  11. using System.Windows.Data;
  12. using System.Windows.Input;
  13. using System.Windows.Media;
  14. using System.Windows.Threading;
  15. using H.Pipes;
  16. using InABox.Logging;
  17. using InABox.Wpf;
  18. using Microsoft.Win32;
  19. namespace PRSServer
  20. {
  21. public static class ItemsControlExtensions
  22. {
  23. public static void ScrollIntoView(
  24. this ItemsControl control,
  25. object item)
  26. {
  27. var framework =
  28. control.ItemContainerGenerator.ContainerFromItem(item)
  29. as FrameworkElement;
  30. if (framework == null) return;
  31. framework.BringIntoView();
  32. }
  33. public static void ScrollIntoView(this ItemsControl control)
  34. {
  35. var count = control.Items.Count;
  36. if (count == 0) return;
  37. var item = control.Items[count - 1];
  38. control.ScrollIntoView(item);
  39. }
  40. }
  41. /// <summary>
  42. /// Interaction logic for Console.xaml
  43. /// </summary>
  44. public partial class Console : ThemableWindow
  45. {
  46. private PipeClient<string> _client;
  47. public CollectionViewSource _filtered;
  48. private readonly ObservableCollection<LogEntry> _logentries;
  49. private readonly bool _monitoronly = true;
  50. private PRSService _service;
  51. public string ServiceName { get; private set; }
  52. private readonly string description;
  53. private readonly TimeSpan regexTimeOut = TimeSpan.FromMilliseconds(100);
  54. private Timer? RefreshTimer;
  55. private Regex? searchRegex;
  56. public Console(string servicename, string description, bool monitoronly = true)
  57. {
  58. ServiceName = servicename;
  59. _monitoronly = monitoronly;
  60. InitializeComponent();
  61. _logentries = new ObservableCollection<LogEntry>();
  62. _filtered = new CollectionViewSource();
  63. _filtered.Source = _logentries;
  64. _filtered.Filter += (sender, args) =>
  65. {
  66. var logEntry = (LogEntry)args.Item;
  67. if (ShowImportant.IsChecked == true && !IsImportant(logEntry))
  68. {
  69. args.Accepted = false;
  70. return;
  71. }
  72. if (UseRegEx.IsChecked == true && searchRegex != null)
  73. args.Accepted = string.IsNullOrWhiteSpace(Search.Text)
  74. || searchRegex.IsMatch(logEntry.DateTime)
  75. || searchRegex.IsMatch(logEntry.Type)
  76. || searchRegex.IsMatch(logEntry.User)
  77. || searchRegex.IsMatch(logEntry.Message);
  78. else
  79. args.Accepted = string.IsNullOrWhiteSpace(Search.Text)
  80. || logEntry.DateTime.Contains(Search.Text)
  81. || logEntry.Type.Contains(Search.Text)
  82. || logEntry.User.Contains(Search.Text)
  83. || logEntry.Message.Contains(Search.Text);
  84. };
  85. DataContext = _filtered;
  86. Title = description;
  87. this.description = description;
  88. }
  89. private bool IsImportant(LogEntry entry)
  90. {
  91. return entry.Type == "IMPTNT";
  92. }
  93. private LogEntry ParseLogMessage(string logLine)
  94. {
  95. var datetime = logLine.Length > 32 ? logLine.Substring(0, 12) : string.Format("{0:HH:mm:ss.fff}", DateTime.Now);
  96. var type = logLine.Length > 32 ? logLine.Substring(13, 6).Trim() : "INFO";
  97. var user = logLine.Length > 32 ? logLine.Substring(20, 12) : "";
  98. var msg = logLine.Length > 32 ? logLine.Substring(33) : logLine;
  99. return new LogEntry
  100. {
  101. DateTime = datetime,
  102. Type = type,
  103. User = user,
  104. Message = msg
  105. };
  106. }
  107. private void Console_Loaded(object sender, RoutedEventArgs e)
  108. {
  109. _client = new PipeClient<string>(ServiceName, ".");
  110. _client.MessageReceived += (o, args) =>
  111. {
  112. var m = args.Message;
  113. var logEntry = ParseLogMessage(args.Message ?? "");
  114. var logType = logEntry.Type;
  115. if (logType == "INFO" || logType == "ERROR" || logType == "IMPTNT")
  116. Dispatcher.BeginInvoke((Action)(() => { _logentries.Insert(0, logEntry); }));
  117. };
  118. _client.Connected += (o, args) =>
  119. {
  120. Dispatcher.Invoke(() => LogBorder.Background = new SolidColorBrush(Colors.LightYellow));
  121. };
  122. _client.Disconnected += (o, args) =>
  123. {
  124. Dispatcher.Invoke(() => LogBorder.Background = new SolidColorBrush(Colors.WhiteSmoke));
  125. if (RefreshTimer == null)
  126. {
  127. RefreshTimer = new Timer(1000);
  128. RefreshTimer.Elapsed += RefreshTimer_Elapsed;
  129. }
  130. RefreshTimer.Start();
  131. };
  132. _client.ExceptionOccurred += (o, args) =>
  133. {
  134. };
  135. if (!_client.IsConnecting)
  136. {
  137. _client.ConnectAsync();
  138. }
  139. if (!_monitoronly)
  140. {
  141. var timer = new DispatcherTimer { Interval = new TimeSpan(0, 0, 3) };
  142. timer.Tick += (o, args) =>
  143. {
  144. timer.IsEnabled = false;
  145. _service = new PRSService(ServiceName, null);
  146. _service.Run(ServiceName);
  147. };
  148. timer.IsEnabled = true;
  149. }
  150. }
  151. private void RefreshTimer_Elapsed(object? sender, ElapsedEventArgs e)
  152. {
  153. if(!_client.IsConnected)
  154. {
  155. if (!_client.IsConnecting)
  156. {
  157. _client.ConnectAsync();
  158. }
  159. }
  160. else
  161. {
  162. RefreshTimer?.Stop();
  163. }
  164. }
  165. private void Window_Closing(object sender, CancelEventArgs e)
  166. {
  167. if (_monitoronly)
  168. {
  169. _client.DisposeAsync().AsTask().Wait();
  170. RefreshTimer?.Stop();
  171. }
  172. else
  173. _service?.Halt();
  174. }
  175. private void Search_KeyDown(object sender, KeyEventArgs e)
  176. {
  177. if (e.Key == Key.Enter && UseRegEx.IsChecked == true)
  178. {
  179. try
  180. {
  181. searchRegex = new Regex(Search.Text, RegexOptions.Compiled, regexTimeOut);
  182. }
  183. catch (ArgumentException)
  184. {
  185. searchRegex = null;
  186. }
  187. _filtered.View.Refresh();
  188. SetSearchStyleNormal();
  189. }
  190. }
  191. private void SetSearchStyleChanged()
  192. {
  193. Search.Background = Brushes.White;
  194. }
  195. private void SetSearchStyleNormal()
  196. {
  197. Search.Background = Brushes.LightYellow;
  198. }
  199. private void Search_TextChanged(object sender, TextChangedEventArgs e)
  200. {
  201. if (UseRegEx.IsChecked != true)
  202. {
  203. _filtered.View.Refresh();
  204. }
  205. else
  206. {
  207. if (string.IsNullOrWhiteSpace(Search.Text))
  208. {
  209. searchRegex = null;
  210. _filtered.View.Refresh();
  211. SetSearchStyleNormal();
  212. }
  213. else
  214. {
  215. SetSearchStyleChanged();
  216. }
  217. }
  218. }
  219. private void UseRegEx_Checked(object sender, RoutedEventArgs e)
  220. {
  221. try
  222. {
  223. searchRegex = new Regex(Search.Text, RegexOptions.Compiled, regexTimeOut);
  224. }
  225. catch (ArgumentException ex)
  226. {
  227. searchRegex = null;
  228. }
  229. _filtered.View.Refresh();
  230. }
  231. private void UseRegEx_Unchecked(object sender, RoutedEventArgs e)
  232. {
  233. searchRegex = null;
  234. _filtered.View.Refresh();
  235. SetSearchStyleNormal();
  236. }
  237. private void SetErrorMessage(string? error)
  238. {
  239. if (string.IsNullOrWhiteSpace(error))
  240. {
  241. Error.Content = "";
  242. Error.Visibility = Visibility.Collapsed;
  243. }
  244. else
  245. {
  246. Error.Content = error;
  247. Error.Visibility = Visibility.Visible;
  248. }
  249. }
  250. private void LoadLog_Click(object sender, RoutedEventArgs e)
  251. {
  252. var dialog = new OpenFileDialog();
  253. dialog.InitialDirectory = DatabaseEngine.GetPath(ServiceName);
  254. if (dialog.ShowDialog() == true)
  255. {
  256. var logEntries = new List<LogEntry>();
  257. var numberSkipped = 0;
  258. var lines = File.ReadLines(dialog.FileName);
  259. foreach (var line in lines)
  260. {
  261. var logEntry = ParseLogMessage(line ?? "");
  262. var logType = logEntry.Type;
  263. if (logType == "ERROR" || logType == "INFO" || logType == "IMPTNT")
  264. logEntries.Add(logEntry);
  265. else if (string.IsNullOrWhiteSpace(logType)) numberSkipped++;
  266. }
  267. if (numberSkipped > 0)
  268. {
  269. if (logEntries.Count == 0)
  270. SetErrorMessage("File does not contain valid log information!");
  271. else
  272. SetErrorMessage(string.Format("Skipped {0} lines that did not contain valid log information", numberSkipped));
  273. }
  274. Title = dialog.FileName;
  275. _filtered.Source = logEntries;
  276. LoadLog.Visibility = Visibility.Collapsed;
  277. CloseLog.Visibility = Visibility.Visible;
  278. }
  279. }
  280. private void CloseLog_Click(object sender, RoutedEventArgs e)
  281. {
  282. _filtered.Source = _logentries;
  283. CloseLog.Visibility = Visibility.Collapsed;
  284. LoadLog.Visibility = Visibility.Visible;
  285. Title = description;
  286. }
  287. private void ShowImportant_Checked(object sender, RoutedEventArgs e)
  288. {
  289. _filtered.View.Refresh();
  290. }
  291. private void ShowImportant_Unchecked(object sender, RoutedEventArgs e)
  292. {
  293. _filtered.View.Refresh();
  294. }
  295. }
  296. }