From 787ce04c9c70c50462a26b9563ce1655a425bcd0 Mon Sep 17 00:00:00 2001 From: Jerker Dahlblom Date: Thu, 11 Apr 2024 12:25:39 +0300 Subject: [PATCH 1/9] Set AskForReloadAPIList to false as default --- src/client/DCSInsight/Properties/Settings.Designer.cs | 2 +- src/client/DCSInsight/Properties/Settings.settings | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/DCSInsight/Properties/Settings.Designer.cs b/src/client/DCSInsight/Properties/Settings.Designer.cs index 142ecf1..7c18452 100644 --- a/src/client/DCSInsight/Properties/Settings.Designer.cs +++ b/src/client/DCSInsight/Properties/Settings.Designer.cs @@ -97,7 +97,7 @@ public bool ReloadAPIList { [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] + [global::System.Configuration.DefaultSettingValueAttribute("False")] public bool AskForReloadAPIList { get { return ((bool)(this["AskForReloadAPIList"])); diff --git a/src/client/DCSInsight/Properties/Settings.settings b/src/client/DCSInsight/Properties/Settings.settings index a900690..add16ac 100644 --- a/src/client/DCSInsight/Properties/Settings.settings +++ b/src/client/DCSInsight/Properties/Settings.settings @@ -21,7 +21,7 @@ False - True + False \ No newline at end of file From f1665da83b1e2fe5f4ef6d35dd1e257662b1bd7b Mon Sep 17 00:00:00 2001 From: Jerker Dahlblom Date: Thu, 11 Apr 2024 12:26:44 +0300 Subject: [PATCH 2/9] Split API and Command JSON events --- src/client/DCSInsight/Events/EventArgs.cs | 26 ++++++++- .../DCSInsight/Events/ICEventHandler.cs | 58 +++++++++++++++---- .../DCSInsight/Interfaces/Interfaces.cs | 14 ++++- 3 files changed, 82 insertions(+), 16 deletions(-) diff --git a/src/client/DCSInsight/Events/EventArgs.cs b/src/client/DCSInsight/Events/EventArgs.cs index f2d18be..db652fa 100644 --- a/src/client/DCSInsight/Events/EventArgs.cs +++ b/src/client/DCSInsight/Events/EventArgs.cs @@ -27,20 +27,40 @@ public ErrorEventArgs(string message, Exception ex) public Exception Ex { get; } } + public class CommsErrorEventArgs : EventArgs + { + public CommsErrorEventArgs(string shortMessage, Exception ex) + { + ShortMessage = shortMessage; + Ex = ex; + } + + public string ShortMessage { get; } + + public Exception Ex { get; } + } + public class ConnectionEventArgs : EventArgs { public bool IsConnected { get; init; } } - public class DataEventArgs : EventArgs + public class CommandDataEventArgs : EventArgs { - public DataEventArgs(DCSAPI? dcsApi, List? dcsapis) + public CommandDataEventArgs(DCSAPI dcsApi) { DCSApi = dcsApi; - DCSAPIS = dcsapis; } public DCSAPI? DCSApi { get; } + } + + public class APIDataEventArgs : EventArgs + { + public APIDataEventArgs(List dcsapis) + { + DCSAPIS = dcsapis; + } public List? DCSAPIS { get; } } diff --git a/src/client/DCSInsight/Events/ICEventHandler.cs b/src/client/DCSInsight/Events/ICEventHandler.cs index 92ff467..fc357fd 100644 --- a/src/client/DCSInsight/Events/ICEventHandler.cs +++ b/src/client/DCSInsight/Events/ICEventHandler.cs @@ -70,28 +70,64 @@ public static void SendConnectionStatus(bool isConnected) OnConnection?.Invoke(new ConnectionEventArgs { IsConnected = isConnected}); } /* - * + * For handling API list + */ + public delegate void APIDataEventHandler(APIDataEventArgs e); + public static event APIDataEventHandler? OnAPIData; + + public static void AttachAPIDataListener(IAPIDataListener listener) + { + OnAPIData += listener.APIDataReceived; + } + + public static void DetachAPIDataListener(IAPIDataListener listener) + { + OnAPIData -= listener.APIDataReceived; + } + + public static void SendAPIData(List dcsAPIs) + { + OnAPIData?.Invoke(new APIDataEventArgs(dcsAPIs)); + } + /* + * For handling back and forth command data */ - public delegate void DataEventHandler(DataEventArgs e); - public static event DataEventHandler? OnData; + public delegate void CommandDataEventHandler(CommandDataEventArgs e); + public static event CommandDataEventHandler? OnCommandData; - public static void AttachDataListener(IDataListener listener) + public static void AttachCommandDataListener(ICommandDataListener listener) { - OnData += listener.DataReceived; + OnCommandData += listener.CommandDataReceived; } - public static void DetachDataListener(IDataListener listener) + public static void DetachCommandDataListener(ICommandDataListener listener) { - OnData -= listener.DataReceived; + OnCommandData -= listener.CommandDataReceived; } - public static void SendData(List dcsAPIs) + public static void SendCommandData(DCSAPI dcsAPI) { - OnData?.Invoke(new DataEventArgs(null, dcsAPIs )); + OnCommandData?.Invoke(new CommandDataEventArgs(dcsAPI)); } - public static void SendData(DCSAPI dcsAPI) + /* + * + */ + public delegate void CommsErrorEventHandler(CommsErrorEventArgs e); + public static event CommsErrorEventHandler? OnCommsError; + + public static void AttachCommsErrorListener(ICommsErrorListener listener) + { + OnCommsError += listener.CommsErrorMessage; + } + + public static void DetachCommsErrorListener(ICommsErrorListener listener) + { + OnCommsError -= listener.CommsErrorMessage; + } + + public static void SendCommsErrorMessage(string shortMessage, Exception ex) { - OnData?.Invoke(new DataEventArgs (dcsAPI, null)); + OnCommsError?.Invoke(new CommsErrorEventArgs(shortMessage, ex)); } } } diff --git a/src/client/DCSInsight/Interfaces/Interfaces.cs b/src/client/DCSInsight/Interfaces/Interfaces.cs index 87d77ac..9dde96f 100644 --- a/src/client/DCSInsight/Interfaces/Interfaces.cs +++ b/src/client/DCSInsight/Interfaces/Interfaces.cs @@ -12,13 +12,23 @@ public interface IErrorListener void ErrorMessage(ErrorEventArgs args); } + public interface ICommsErrorListener + { + void CommsErrorMessage(CommsErrorEventArgs args); + } + public interface IConnectionListener { void ConnectionStatus(ConnectionEventArgs args); } - public interface IDataListener + public interface IAPIDataListener + { + void APIDataReceived(APIDataEventArgs args); + } + + public interface ICommandDataListener { - void DataReceived(DataEventArgs args); + void CommandDataReceived(CommandDataEventArgs args); } } From e8b285cdd3a332eb6e1cbc985886bd83e554ad81 Mon Sep 17 00:00:00 2001 From: Jerker Dahlblom Date: Thu, 11 Apr 2024 12:27:12 +0300 Subject: [PATCH 3/9] Set AskForReloadAPIList to false as default --- src/client/DCSInsight/App.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/DCSInsight/App.config b/src/client/DCSInsight/App.config index ca31740..39fa540 100644 --- a/src/client/DCSInsight/App.config +++ b/src/client/DCSInsight/App.config @@ -26,7 +26,7 @@ False - True + False From 6477b5a20c1fc6380a44ec9b95f82c269e7e22e9 Mon Sep 17 00:00:00 2001 From: Jerker Dahlblom Date: Thu, 11 Apr 2024 12:28:04 +0300 Subject: [PATCH 4/9] Version bump --- src/client/DCSInsight/DCSInsight.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/DCSInsight/DCSInsight.csproj b/src/client/DCSInsight/DCSInsight.csproj index 836db51..7bb2007 100644 --- a/src/client/DCSInsight/DCSInsight.csproj +++ b/src/client/DCSInsight/DCSInsight.csproj @@ -8,7 +8,7 @@ dcs-insight 1.0.0 - 1.9.3 + 1.10.0 Images\Magnifier_icon.ico DCS-Skunkworks From 09cd2556cf79a8f04b85f6d74419b54758e80adc Mon Sep 17 00:00:00 2001 From: Jerker Dahlblom Date: Thu, 11 Apr 2024 12:28:51 +0300 Subject: [PATCH 5/9] Separate API and Command handling --- .../Communication/TCPClientHandler.cs | 99 ++++++++++++------- 1 file changed, 66 insertions(+), 33 deletions(-) diff --git a/src/client/DCSInsight/Communication/TCPClientHandler.cs b/src/client/DCSInsight/Communication/TCPClientHandler.cs index bb1e04f..2471e5b 100644 --- a/src/client/DCSInsight/Communication/TCPClientHandler.cs +++ b/src/client/DCSInsight/Communication/TCPClientHandler.cs @@ -24,18 +24,14 @@ internal class TCPClientHandler : IDisposable, ICommandListener private bool _isRunning; private readonly string _host; private readonly string _port; - private bool _apiListReceived; - private int _metaDataPollCounter; - public bool LogJSON { get; set; } + public bool LogJSON { get; set; } private string _currentMessage = ""; private volatile bool _responseReceived; - private bool _requestAPIList; - public TCPClientHandler(string host, string port, bool requestAPIList) + public TCPClientHandler(string host, string port) { _host = host; _port = port; - _requestAPIList = requestAPIList; ICEventHandler.AttachCommandListener(this); } @@ -47,6 +43,39 @@ public void Dispose() GC.SuppressFinalize(this); } + public void RequestAPIList() + { + if (_tcpClient == null) return; + + try + { + if (!_tcpClient.Connected) return; + + Thread.Sleep(300); + _tcpClient.GetStream().Write(Encoding.ASCII.GetBytes("SENDAPI\n")); + Thread.Sleep(1000); + + var bytes = new byte[_tcpClient.Available]; + var bytesRead = _tcpClient.GetStream().Read(bytes); + var msg = Encoding.ASCII.GetString(bytes); + if (LogJSON) Logger.Info(msg); + HandleAPIMessage(msg); + Thread.Sleep(100); + } + catch (SocketException ex) + { + Logger.Error(ex); + return; + } + } + + public void StartListening() + { + _isRunning = true; + _clientThread = new Thread(ClientThread); + _clientThread.Start(); + } + private void ClientThread() { if (_tcpClient == null) return; @@ -78,15 +107,6 @@ private void ClientThread() if (!_tcpClient.Connected) break; - if (_requestAPIList && !_apiListReceived && _metaDataPollCounter < 1) - { - Thread.Sleep(300); - _metaDataPollCounter++; - _tcpClient.GetStream().Write(Encoding.ASCII.GetBytes("SENDAPI\n")); - Thread.Sleep(1000); - _requestAPIList = false; - } - if (_commandsQueue.Count > 0 && _responseReceived) { if (!_commandsQueue.TryDequeue(out var dcsApi)) continue; @@ -103,7 +123,7 @@ private void ClientThread() var bytesRead = _tcpClient.GetStream().Read(bytes); var msg = Encoding.ASCII.GetString(bytes); if (LogJSON) Logger.Info(msg); - HandleMessage(msg); + HandleCommandMessage(msg); Thread.Sleep(100); } catch (SocketException ex) @@ -117,24 +137,30 @@ private void ClientThread() _tcpClient = null; ICEventHandler.SendConnectionStatus(_isRunning); } - - private void HandleMessage(string str) + + private void HandleCommandMessage(string str) { try { - if (!_apiListReceived) - { - HandleAPIMessage(str); - return; - } - if (str.Contains("\"returns_data\":") && str.EndsWith("}")) // regex? { - var dcsApi = JsonConvert.DeserializeObject(_currentMessage + str); + DCSAPI? dcsApi; + try + { + dcsApi = JsonConvert.DeserializeObject(_currentMessage + str); + } + catch (Exception e) + { + _currentMessage = ""; + _responseReceived = true; + ICEventHandler.SendCommsErrorMessage("Error parsing JSON (API)", e); + return; + } + if (dcsApi == null) return; _currentMessage = ""; - ICEventHandler.SendData(dcsApi); + ICEventHandler.SendCommandData(dcsApi); _responseReceived = true; } else @@ -152,12 +178,22 @@ private void HandleAPIMessage(string str) { try { - var dcsAPIList = JsonConvert.DeserializeObject>(str); + List? dcsAPIList; + try + { + dcsAPIList = JsonConvert.DeserializeObject>(str); + } + catch (Exception e) + { + _responseReceived = true; + ICEventHandler.SendCommsErrorMessage("Error parsing JSON (API List)", e); + return; + } + if (dcsAPIList == null) return; - ICEventHandler.SendData(dcsAPIList); + ICEventHandler.SendAPIData(dcsAPIList); _responseReceived = true; - _apiListReceived = true; } catch (Exception ex) { @@ -185,9 +221,6 @@ public void Connect() _isRunning = false; _tcpClient = new TcpClient(); _tcpClient.Connect(serverEndPoint); - _isRunning = true; - _clientThread = new Thread(ClientThread); - _clientThread.Start(); } catch (Exception ex) { @@ -213,7 +246,7 @@ public void AddCommand(DCSAPI dcsApi) { _commandsQueue.Enqueue(dcsApi); } - + public void SendCommand(SendCommandEventArgs args) { try From 55dedc0db3253a17a485bea029c37461f17fe8d0 Mon Sep 17 00:00:00 2001 From: Jerker Dahlblom Date: Thu, 11 Apr 2024 12:29:06 +0300 Subject: [PATCH 6/9] Add words to dictionary --- src/client/DCSInsight/DCSInsight.sln.DotSettings | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/client/DCSInsight/DCSInsight.sln.DotSettings b/src/client/DCSInsight/DCSInsight.sln.DotSettings index 3d9e681..243bdc3 100644 --- a/src/client/DCSInsight/DCSInsight.sln.DotSettings +++ b/src/client/DCSInsight/DCSInsight.sln.DotSettings @@ -5,9 +5,11 @@ DCSAPI DCSAPIS DCSBIOS + HSL IC JSON LED + RGB TCP UI API From f7fca0d7179d75cf08c5f52b1653280c8ff4a604 Mon Sep 17 00:00:00 2001 From: Jerker Dahlblom Date: Thu, 11 Apr 2024 12:29:26 +0300 Subject: [PATCH 7/9] Add HSL color functionality --- src/client/DCSInsight/Misc/Common.cs | 32 ++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/client/DCSInsight/Misc/Common.cs b/src/client/DCSInsight/Misc/Common.cs index b7e8b37..b4bd54c 100644 --- a/src/client/DCSInsight/Misc/Common.cs +++ b/src/client/DCSInsight/Misc/Common.cs @@ -186,5 +186,37 @@ internal static bool TryFindVisualChildByName(this DependencyObject pare return false; } + + public static Color HSLToRGB(double h, double s, double l) + { + double r = 0, g = 0, b = 0; + if (s == 0) + { + r = g = b = l; // achromatic + } + else + { + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = HueToRGB(p, q, h + 1.0 / 3); + g = HueToRGB(p, q, h); + b = HueToRGB(p, q, h - 1.0 / 3); + } + + return Color.FromRgb( + (byte)(r * 255), + (byte)(g * 255), + (byte)(b * 255)); + } + + private static double HueToRGB(double p, double q, double t) + { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1.0 / 6) return p + (q - p) * 6 * t; + if (t < 1.0 / 2) return q; + if (t < 2.0 / 3) return p + (q - p) * (2.0 / 3 - t) * 6; + return p; + } } } From 3a125046620d67074b7524d1fb9aee18ff4caa55 Mon Sep 17 00:00:00 2001 From: Jerker Dahlblom Date: Thu, 11 Apr 2024 12:29:52 +0300 Subject: [PATCH 8/9] Apply changed interface name --- src/client/DCSInsight/Windows/WindowRangeTest.xaml.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/client/DCSInsight/Windows/WindowRangeTest.xaml.cs b/src/client/DCSInsight/Windows/WindowRangeTest.xaml.cs index 51f24f7..664b5c0 100644 --- a/src/client/DCSInsight/Windows/WindowRangeTest.xaml.cs +++ b/src/client/DCSInsight/Windows/WindowRangeTest.xaml.cs @@ -17,7 +17,7 @@ namespace DCSInsight.Windows /// /// Interaction logic for WindowRangeTest.xaml /// - public partial class WindowRangeTest : Window, IErrorListener, IConnectionListener, IDataListener, IDisposable + public partial class WindowRangeTest : Window, IErrorListener, IConnectionListener, ICommandDataListener, IDisposable { private readonly List _dcsAPIList; private bool _formLoaded; @@ -44,14 +44,14 @@ public WindowRangeTest(List dcsAPIList) _dcsAPIList.RemoveAll(o => o.Id == Constants.LuaConsole); ICEventHandler.AttachErrorListener(this); ICEventHandler.AttachConnectionListener(this); - ICEventHandler.AttachDataListener(this); + ICEventHandler.AttachCommandDataListener(this); } public void Dispose() { ICEventHandler.DetachErrorListener(this); ICEventHandler.DetachConnectionListener(this); - ICEventHandler.DetachDataListener(this); + ICEventHandler.DetachCommandDataListener(this); _stopRunning = true; _doLoop = false; AutoResetEvent1.Set(); @@ -136,7 +136,7 @@ private void SetCurrentTestStructure(DCSAPI dcsApi) } } - public void DataReceived(DataEventArgs args) + public void CommandDataReceived(CommandDataEventArgs args) { try { From 0f00a5bc42d2b6a00d63026dc843644ed64f471b Mon Sep 17 00:00:00 2001 From: Jerker Dahlblom Date: Thu, 11 Apr 2024 12:31:28 +0300 Subject: [PATCH 9/9] Add functionality for separate API/command msg JSON error parsing now is show in the bottom bar instead of showing a dialog. Error should be recoverable. Fixed polling not working. --- src/client/DCSInsight/MainWindow.xaml | 33 ++-- src/client/DCSInsight/MainWindow.xaml.cs | 182 ++++++++++++++---- .../UserControls/UserControlAPI.xaml.cs | 12 +- .../UserControls/UserControlAPIBase.cs | 39 +++- 4 files changed, 206 insertions(+), 60 deletions(-) diff --git a/src/client/DCSInsight/MainWindow.xaml b/src/client/DCSInsight/MainWindow.xaml index 5c9381c..d579841 100644 --- a/src/client/DCSInsight/MainWindow.xaml +++ b/src/client/DCSInsight/MainWindow.xaml @@ -15,13 +15,26 @@ + - + + + + + + + + + + + + + @@ -36,11 +49,11 @@ - + - + dcs-insight @@ -48,17 +61,9 @@ wiki - - check version - - - set dcs-bios - - - api reload setting - - - view client log + + + diff --git a/src/client/DCSInsight/MainWindow.xaml.cs b/src/client/DCSInsight/MainWindow.xaml.cs index 4306692..1069612 100644 --- a/src/client/DCSInsight/MainWindow.xaml.cs +++ b/src/client/DCSInsight/MainWindow.xaml.cs @@ -1,10 +1,12 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; +using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; @@ -22,13 +24,15 @@ using DCSInsight.Properties; using DCSInsight.Windows; using Octokit; +using System.ComponentModel.Design; +using System.Windows.Media; namespace DCSInsight { /// /// Interaction logic for MainWindow.xaml /// - public partial class MainWindow : Window, IErrorListener, IConnectionListener, IDataListener, IDisposable + public partial class MainWindow : Window, IErrorListener, IConnectionListener, ICommsErrorListener, ICommandDataListener, IAPIDataListener, IDisposable { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private List _dcsAPIList = new(); @@ -38,6 +42,11 @@ public partial class MainWindow : Window, IErrorListener, IConnectionListener, I private bool _isConnected; private bool _rangeTesting; private LuaWindow? _luaWindow; + private static readonly AutoResetEvent AutoResetEventErrorMessage = new(false); + private Thread? _errorMessageThread; + private const int ErrorMessageViewTime = 4000; + private readonly ConcurrentQueue _errorMessagesQueue = new(); + private bool _isClosing; public MainWindow() { @@ -46,16 +55,22 @@ public MainWindow() Settings.Default.Save(); ICEventHandler.AttachErrorListener(this); ICEventHandler.AttachConnectionListener(this); - ICEventHandler.AttachDataListener(this); + ICEventHandler.AttachAPIDataListener(this); + ICEventHandler.AttachCommandDataListener(this); + ICEventHandler.AttachCommsErrorListener(this); } public void Dispose() { _luaWindow?.Close(); + AutoResetEventErrorMessage.Set(); + _errorMessageThread?.Join(); ItemsControlAPI.Items.Clear(); ICEventHandler.DetachErrorListener(this); ICEventHandler.DetachConnectionListener(this); - ICEventHandler.DetachDataListener(this); + ICEventHandler.DetachAPIDataListener(this); + ICEventHandler.DetachCommandDataListener(this); + ICEventHandler.DetachCommsErrorListener(this); _tcpClientHandler?.Dispose(); GC.SuppressFinalize(this); } @@ -66,6 +81,10 @@ private void MainWindow_OnLoaded(object sender, RoutedEventArgs e) { if (_formLoaded) return; + _errorMessageThread = new Thread(ErrorMessageThread); + _errorMessageThread.Start(); + TextBlockError.Background = new SolidColorBrush(Common.HSLToRGB(348.0 / 360, 0.83, 0.70)); + TextBoxSearchLuaControls.SetBackgroundSearchBanner(TextBoxSearchAPI); ShowVersionInfo(); SetFormState(); @@ -112,8 +131,15 @@ private void Connect() { _tcpClientHandler?.Disconnect(); _isConnected = false; - _tcpClientHandler = new TCPClientHandler(TextBoxServer.Text, TextBoxPort.Text, ItemsControlAPI.Items.Count == 0 || Settings.Default.ReloadAPIList); + _tcpClientHandler = new TCPClientHandler(TextBoxServer.Text, TextBoxPort.Text); _tcpClientHandler.Connect(); + + if (Settings.Default.ReloadAPIList || ItemsControlAPI.Items.IsEmpty) + { + _tcpClientHandler.RequestAPIList(); + } + + _tcpClientHandler.StartListening(); } catch (Exception ex) { @@ -162,7 +188,7 @@ public void ConnectionStatus(ConnectionEventArgs args) } Dispatcher?.BeginInvoke((Action)(() => SetConnectionStatus(args.IsConnected))); - Dispatcher?.BeginInvoke((Action)SetFormState); + Dispatcher?.BeginInvoke((Action)SetFormState); } catch (Exception ex) { @@ -185,16 +211,12 @@ public void ErrorMessage(ErrorEventArgs args) } } - public void DataReceived(DataEventArgs args) + public void CommandDataReceived(CommandDataEventArgs args) { try { if (_rangeTesting) return; - if (args.DCSAPIS != null) - { - HandleAPIMessage(args.DCSAPIS); - } if (args.DCSApi != null) { HandleMessage(args.DCSApi); @@ -206,6 +228,23 @@ public void DataReceived(DataEventArgs args) } } + public void APIDataReceived(APIDataEventArgs args) + { + try + { + if (_rangeTesting) return; + + if (args.DCSAPIS != null) + { + HandleAPIMessage(args.DCSAPIS); + } + } + catch (Exception ex) + { + Dispatcher?.BeginInvoke((Action)(() => Common.ShowErrorMessageBox(ex))); + } + } + private void SetConnectionStatus(bool connected) { ButtonConnectDisconnect.Content = connected ? "Disconnect" : "Connect"; @@ -238,6 +277,8 @@ private void HandleAPIMessage(List dcsApis) { try { + if (!Settings.Default.ReloadAPIList && !ItemsControlAPI.Items.IsEmpty) return; + _dcsAPIList = dcsApis; //Debug.WriteLine("Count is " + _dcsAPIList.Count); Dispatcher?.BeginInvoke((Action)(() => ShowAPIs())); @@ -270,6 +311,8 @@ private void MainWindow_OnClosing(object sender, CancelEventArgs e) { try { + _isClosing = true; + AutoResetEventErrorMessage.Set(); Settings.Default.MainWindowTop = Top; Settings.Default.MainWindowLeft = Left; Settings.Default.Save(); @@ -488,7 +531,7 @@ private void ShowVersionInfo() Common.ShowErrorMessageBox(ex); } } - + private void ButtonRangeTest_OnClick(object sender, RoutedEventArgs e) { try @@ -521,14 +564,6 @@ private void TextBlockAppWiki_OnMouseDown(object sender, MouseButtonEventArgs e) private async void TextBlockCheckNewVersion_OnMouseDown(object sender, MouseButtonEventArgs e) { - try - { - await CheckForNewVersion(); - } - catch (Exception ex) - { - Common.ShowErrorMessageBox(ex); - } } private async Task CheckForNewVersion() @@ -566,32 +601,13 @@ private async Task CheckForNewVersion() } } - private void TextBlockSetDCSBIOSLocation_OnMouseDown(object sender, MouseButtonEventArgs e) - { - try - { - var settingsWindow = new SettingsWindow(); - settingsWindow.Owner = this; - settingsWindow.ShowDialog(); - if (settingsWindow.DialogResult == true) - { - Settings.Default.DCSBiosJSONLocation = settingsWindow.DcsBiosJSONLocation; - } - Settings.Default.Save(); - } - catch (Exception ex) - { - Common.ShowErrorMessageBox(ex); - } - } - private void ButtonLuaWindow_OnClick(object sender, RoutedEventArgs e) { try { _luaWindow?.Close(); _luaWindow = new LuaWindow(); - _luaWindow.Show(); + _luaWindow.Show(); _luaWindow.Left = Left + Width; } catch (Exception ex) @@ -621,7 +637,7 @@ private void TextBoxSearchAPI_OnPreviewKeyDown(object sender, KeyEventArgs e) } } - private void TextBlockAPIReload_OnMouseDown(object sender, MouseButtonEventArgs e) + private void MenuItemAPIReload_OnClick(object sender, RoutedEventArgs e) { try { @@ -635,7 +651,38 @@ private void TextBlockAPIReload_OnMouseDown(object sender, MouseButtonEventArgs } } - private void TextBlockViewLog_OnMouseDown(object sender, MouseButtonEventArgs e) + private void MenuSetDCSBIOSPath_OnClick(object sender, RoutedEventArgs e) + { + try + { + var settingsWindow = new SettingsWindow(); + settingsWindow.Owner = this; + settingsWindow.ShowDialog(); + if (settingsWindow.DialogResult == true) + { + Settings.Default.DCSBiosJSONLocation = settingsWindow.DcsBiosJSONLocation; + } + Settings.Default.Save(); + } + catch (Exception ex) + { + Common.ShowErrorMessageBox(ex); + } + } + + private async void MenuCheckNewVersion_OnClick(object sender, RoutedEventArgs e) + { + try + { + await CheckForNewVersion(); + } + catch (Exception ex) + { + Common.ShowErrorMessageBox(ex); + } + } + + private void MenuOpenLog_OnClick(object sender, RoutedEventArgs e) { try { @@ -649,5 +696,58 @@ private void TextBlockViewLog_OnMouseDown(object sender, MouseButtonEventArgs e) Common.ShowErrorMessageBox(ex); } } + + private void MenuItemExit_OnClick(object sender, RoutedEventArgs e) + { + try + { + Close(); + } + catch (Exception ex) + { + Common.ShowErrorMessageBox(ex); + } + } + + private void ErrorMessageThread() + { + try + { + while (!_isClosing) + { + if(_errorMessagesQueue.IsEmpty) AutoResetEventErrorMessage.WaitOne(); + + if (_isClosing) break; + + var result = _errorMessagesQueue.TryDequeue(out var message); + Dispatcher?.BeginInvoke(() => result ? TextBlockError.Text = message : TextBlockError.Text = ""); + Thread.Sleep(ErrorMessageViewTime); + Dispatcher?.BeginInvoke(() => TextBlockError.Text = ""); + } + } + catch (Exception ex) + { + ICEventHandler.SendErrorMessage("ErrorMessageThread() Error", ex); + } + } + + private void SetCommsErrorMessage(string errorMessage) + { + _errorMessagesQueue.Enqueue(errorMessage); + AutoResetEventErrorMessage.Set(); + } + + public void CommsErrorMessage(CommsErrorEventArgs args) + { + try + { + SetCommsErrorMessage(args.ShortMessage); + Logger.Error(args.Ex); + } + catch (Exception ex) + { + Common.ShowErrorMessageBox(ex); + } + } } } diff --git a/src/client/DCSInsight/UserControls/UserControlAPI.xaml.cs b/src/client/DCSInsight/UserControls/UserControlAPI.xaml.cs index e195256..d17aed8 100644 --- a/src/client/DCSInsight/UserControls/UserControlAPI.xaml.cs +++ b/src/client/DCSInsight/UserControls/UserControlAPI.xaml.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Windows; using System.Windows.Controls; @@ -22,7 +23,6 @@ public UserControlAPI(DCSAPI dcsAPI, bool isConnected) : base(dcsAPI, isConnecte InitializeComponent(); LabelResultBase = LabelResult; TextBoxResultBase = TextBoxResult; - } private void UserControlAPI_OnLoaded(object sender, RoutedEventArgs e) @@ -92,7 +92,14 @@ private void BuildLuaConsoleUI() StackPanelLinks.Visibility = Visibility.Visible; StackPanelConsolePolling.Visibility = Visibility.Visible; - var dockPanelParameters = Application.Current.MainWindow.FindChild("DockPanelParameters") ?? throw new Exception("Failed to find DockPanelParameters"); + if (Application.Current.MainWindow.FindChild("DockPanelParameters") == null) + { + Logger.Error("Failed to find DockPanelParameters in BuildLuaConsoleUI."); + Common.ShowMessageBox("Error building GUI. Please restart application."); + return; + } + + var dockPanelParameters = Application.Current.MainWindow.FindChild("DockPanelParameters") ?? throw new Exception("Failed to find DockPanelParameters"); dockPanelParameters.LastChildFill = true; var controlList = new List(); @@ -113,6 +120,7 @@ private void BuildLuaConsoleUI() }; textBoxLuaCode.PreviewKeyDown += TextBoxLuaCode_OnPreviewKeyDown; + textBoxLuaCode.KeyUp += TextBoxLuaCode_OnKeyUp; var brushConverter = new BrushConverter().ConvertFromString("#0000FF"); var labelConsoleWarning = new Label diff --git a/src/client/DCSInsight/UserControls/UserControlAPIBase.cs b/src/client/DCSInsight/UserControls/UserControlAPIBase.cs index 20162bf..69ceb48 100644 --- a/src/client/DCSInsight/UserControls/UserControlAPIBase.cs +++ b/src/client/DCSInsight/UserControls/UserControlAPIBase.cs @@ -8,6 +8,7 @@ using System.Windows.Input; using System.Windows.Threading; using DCSInsight.Events; +using DCSInsight.Interfaces; using DCSInsight.JSON; using DCSInsight.Misc; using NLog; @@ -17,14 +18,13 @@ namespace DCSInsight.UserControls /// /// Interaction logic for UserControlAPIBase.xaml /// - public abstract partial class UserControlAPIBase : UserControl, IDisposable, IAsyncDisposable + public abstract partial class UserControlAPIBase : UserControl, ICommsErrorListener, IDisposable, IAsyncDisposable { protected static readonly Logger Logger = LogManager.GetCurrentClassLogger(); protected readonly DCSAPI DCSAPI; protected bool IsControlLoaded; protected readonly List TextBoxParameterList = new(); protected bool IsConnected; - private readonly Timer _pollingTimer; protected bool CanSend; private bool _keepResults; protected Button? ButtonSend; @@ -32,6 +32,7 @@ public abstract partial class UserControlAPIBase : UserControl, IDisposable, IAs protected ComboBox? ComboBoxPollTimes; protected Label? LabelResultBase; protected TextBox? TextBoxResultBase; + private readonly Timer _pollingTimer; private static readonly AutoResetEvent AutoResetEventPolling = new(false); protected readonly bool IsLuaConsole; @@ -47,6 +48,7 @@ protected UserControlAPIBase(DCSAPI dcsAPI, bool isConnected) IsConnected = isConnected; _pollingTimer = new Timer(PollingTimerCallback); _pollingTimer.Change(Timeout.Infinite, 10000); + ICEventHandler.AttachCommsErrorListener(this); } public void Dispose() @@ -55,6 +57,7 @@ public void Dispose() AutoResetEventPolling.Set(); _pollingTimer?.Dispose(); AutoResetEventPolling.Dispose(); + ICEventHandler.DetachCommsErrorListener(this); GC.SuppressFinalize(this); } @@ -64,6 +67,7 @@ public async ValueTask DisposeAsync() AutoResetEventPolling.Set(); await _pollingTimer.DisposeAsync(); AutoResetEventPolling.Dispose(); + ICEventHandler.DetachCommsErrorListener(this); GC.SuppressFinalize(this); } @@ -137,6 +141,22 @@ internal void SetResult(DCSAPI dcsApi) } } + /// + /// If the command resulted in JSON parsing error this is triggered instead of the above SetResult(). + /// The next command should then be sent. + /// + /// + public void CommsErrorMessage(CommsErrorEventArgs args) + { + try + { + AutoResetEventPolling.Set(); + } + catch (Exception ex) + { + Common.ShowErrorMessageBox(ex); + } + } public void SetConnectionStatus(bool connected) { @@ -147,7 +167,8 @@ public void SetConnectionStatus(bool connected) { _pollingTimer.Change(Timeout.Infinite, 10000); } - SetFormState(); + + if(IsControlLoaded) SetFormState(); } catch (Exception ex) { @@ -314,6 +335,18 @@ protected void TextBoxLuaCode_OnPreviewKeyDown(object sender, KeyEventArgs e) } } + protected void TextBoxLuaCode_OnKeyUp(object sender, KeyEventArgs e) + { + try + { + SetFormState(); + } + catch (Exception ex) + { + Common.ShowErrorMessageBox(ex); + } + } + private void TextBoxParameter_OnKeyUp(object sender, KeyEventArgs e) { try