From 2dee3a9ae9482f3a3bd93a77f0cc7f88ac45d052 Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Sat, 8 Aug 2026 11:59:43 +0200 Subject: [PATCH] Rework updating process a bit --- Installer/Files/config.ini | 2 +- PamhagenSysCtrl/App.xaml.cs | 69 +++++++------------ PamhagenSysCtrl/Dialogs/UpdateDialog.xaml | 4 +- PamhagenSysCtrl/Dialogs/UpdateDialog.xaml.cs | 24 +++---- PamhagenSysCtrl/Helpers/Config.cs | 9 ++- PamhagenSysCtrl/Helpers/UpdateService.cs | 53 +++++++------- .../Windows/PlantSchemeWindow.xaml | 2 +- .../Windows/PlantSchemeWindow.xaml.cs | 5 +- 8 files changed, 72 insertions(+), 96 deletions(-) diff --git a/Installer/Files/config.ini b/Installer/Files/config.ini index 05280d2..41bb4c4 100644 --- a/Installer/Files/config.ini +++ b/Installer/Files/config.ini @@ -3,5 +3,5 @@ port = COM1 [update] -url = https://elwig.at/files/pamhagen-sysctrl/?format=json +url = https://elwig.at/files/pamhagen-sysctrl/latest auto = true diff --git a/PamhagenSysCtrl/App.xaml.cs b/PamhagenSysCtrl/App.xaml.cs index 5e64ed2..e0bda00 100644 --- a/PamhagenSysCtrl/App.xaml.cs +++ b/PamhagenSysCtrl/App.xaml.cs @@ -12,7 +12,6 @@ namespace PamhagenSysCtrl { public static Dispatcher? MainDispatcher; private readonly DispatcherTimer AutoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) }; - private bool IsCheckingForUpdates; public static readonly string DataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Anlagensteuerung Pamhagen"); public static readonly string ConfigPath = Path.Combine(DataPath, "config.ini"); @@ -50,69 +49,49 @@ namespace PamhagenSysCtrl { base.OnStartup(evt); - if (Config.UpdateAuto && Config.UpdateUrl != null) { - AutoUpdateTimer.Tick += async (_, _) => await CheckForUpdates(); + if (Config.UpdateUrl != null && Config.UpdateAuto) { + AutoUpdateTimer.Tick += async (_, _) => { + try { + await CheckForUpdates(); + } catch { } + }; AutoUpdateTimer.Start(); - _ = Dispatcher.BeginInvoke(async () => { + await Dispatcher.BeginInvoke(async () => { await Task.Delay(1500); - await CheckForUpdates(); + try { + await CheckForUpdates(); + } catch { } }); } } - public async Task CheckForUpdates(bool showResult = false) { - if (IsCheckingForUpdates) { - if (showResult) { - MessageBox.Show("Es wird bereits nach Updates gesucht.", "Nach Updates suchen", MessageBoxButton.OK, MessageBoxImage.Information); - } - return; - } - if (Config.UpdateUrl == null) { - if (showResult) { - MessageBox.Show("Die automatische Update-Suche ist deaktiviert.", "Nach Updates suchen", MessageBoxButton.OK, MessageBoxImage.Information); - } - return; - } + public static async Task CheckForUpdates(bool showResult = false) { + if (Config.UpdateUrl == null) return; - IsCheckingForUpdates = true; try { - using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(15)); - var latest = await UpdateService.GetLatestInstallerAsync(Config.UpdateUrl, timeout.Token); - if (latest.Version > Version) { - var dialog = new UpdateDialog(latest) { - Owner = MainWindow, + var latest = await UpdateService.GetLatestInstallerUrl(Config.UpdateUrl); + if (latest.HasValue && new Version(latest.Value.Version) > Version) { + var dialog = new UpdateDialog(latest.Value.Version, latest.Value.Url, latest.Value.Size) { + Owner = Current.MainWindow, }; if (dialog.ShowDialog() == true) { - Shutdown(); + Current.Shutdown(); } } else if (showResult) { - MessageBox.Show( - $"Die Anlagensteuerung ist auf dem aktuellen Stand. (Version {Version})", - "Nach Updates suchen", - MessageBoxButton.OK, - MessageBoxImage.Information - ); + MessageBox.Show($"Die Anlagensteuerung ist auf dem aktuellen Stand.\n(Version {Version})", "Nach Updates suchen", MessageBoxButton.OK, MessageBoxImage.Information); } } catch (OperationCanceledException) { if (showResult) { - MessageBox.Show( - "Zeitüberschreitung beim Abrufen der Update-Informationen.", - "Nach Updates suchen", - MessageBoxButton.OK, - MessageBoxImage.Error - ); + MessageBox.Show("Zeitüberschreitung beim Abrufen der Update-Informationen.", "Nach Updates suchen", MessageBoxButton.OK, MessageBoxImage.Error); + } else { + throw; } } catch (Exception exc) { if (showResult) { - MessageBox.Show( - $"Die Update-Informationen konnten nicht abgerufen werden:\n\n{exc.Message}", - "Nach Updates suchen", - MessageBoxButton.OK, - MessageBoxImage.Error - ); + MessageBox.Show($"Die Update-Informationen konnten nicht abgerufen werden:\n\n{exc.Message}", "Nach Updates suchen", MessageBoxButton.OK, MessageBoxImage.Error); + } else { + throw; } - } finally { - IsCheckingForUpdates = false; } } diff --git a/PamhagenSysCtrl/Dialogs/UpdateDialog.xaml b/PamhagenSysCtrl/Dialogs/UpdateDialog.xaml index da0ab88..0a783aa 100644 --- a/PamhagenSysCtrl/Dialogs/UpdateDialog.xaml +++ b/PamhagenSysCtrl/Dialogs/UpdateDialog.xaml @@ -4,7 +4,7 @@ ResizeMode="NoResize" ShowInTaskbar="False" Topmost="True" WindowStartupLocation="CenterOwner" Title="Update verfügbar - Anlagensteuerung Pamhagen" - Height="220" Width="460" + Height="220" Width="480" Closed="OnClosed"> @@ -18,7 +18,7 @@ Version 0.0.0 ist verfügbar. Soll das Update heruntergeladen und installiert werden? (ca. 0 MB) - Hinweis: Die Anlagensteuerung wird zur Installation geschlossen. + Hinweis: Die Anlagensteuerung wird für die Installation geschlossen. diff --git a/PamhagenSysCtrl/Dialogs/UpdateDialog.xaml.cs b/PamhagenSysCtrl/Dialogs/UpdateDialog.xaml.cs index 93e8c71..bf271fe 100644 --- a/PamhagenSysCtrl/Dialogs/UpdateDialog.xaml.cs +++ b/PamhagenSysCtrl/Dialogs/UpdateDialog.xaml.cs @@ -1,17 +1,18 @@ using PamhagenSysCtrl.Helpers; +using System.IO; using System.Windows; namespace PamhagenSysCtrl.Dialogs { public partial class UpdateDialog : Window { - private readonly UpdateInstaller Installer; + private readonly string Url; private readonly CancellationTokenSource Cancellation = new(); - public UpdateDialog(UpdateInstaller installer) { - Installer = installer; + public UpdateDialog(string version, string url, long size) { + Url = url; InitializeComponent(); - VersionText.Text = installer.Version.ToString(); - SizeText.Text = Math.Ceiling(installer.Size / 1024d / 1024d).ToString("N0"); + VersionText.Text = version; + SizeText.Text = Math.Ceiling(size / 1024d / 1024d).ToString("N0"); } private void OnClosed(object? sender, EventArgs evt) { @@ -26,20 +27,15 @@ namespace PamhagenSysCtrl.Dialogs { try { var progress = new Progress(value => DownloadProgress.Value = value * 100); - var fileName = await UpdateService.DownloadInstallerAsync(Installer, App.TempPath, progress, Cancellation.Token); + var filename = Path.Combine(App.TempPath, $"PamhagenSysCtrl-{VersionText.Text}.msi"); + await UpdateService.DownloadInstaller(Url, filename, progress, Cancellation.Token); StatusText.Text = "Installer wird gestartet..."; - UpdateService.StartInstaller(fileName); + UpdateService.StartInstaller(filename); DialogResult = true; } catch (OperationCanceledException) { // Closing the dialog cancels an active download. } catch (Exception exc) { - MessageBox.Show( - this, - $"Das Update konnte nicht installiert werden:\n\n{exc.Message}", - "Update installieren", - MessageBoxButton.OK, - MessageBoxImage.Error - ); + MessageBox.Show($"Das Update konnte nicht installiert werden:\n\n{exc.Message}", "Update installieren", MessageBoxButton.OK, MessageBoxImage.Error); Description.Visibility = Visibility.Visible; DownloadPanel.Visibility = Visibility.Collapsed; InstallButton.IsEnabled = true; diff --git a/PamhagenSysCtrl/Helpers/Config.cs b/PamhagenSysCtrl/Helpers/Config.cs index f90de4e..6771f8d 100644 --- a/PamhagenSysCtrl/Helpers/Config.cs +++ b/PamhagenSysCtrl/Helpers/Config.cs @@ -9,8 +9,8 @@ namespace PamhagenSysCtrl.Helpers { private readonly string FileName; public string PlcPort = "COM1"; - public string? UpdateUrl = "https://elwig.at/files/pamhagen-sysctrl/?format=json"; - public bool UpdateAuto = true; + public string? UpdateUrl; + public bool UpdateAuto; public Config(string filename) { FileName = filename; @@ -21,9 +21,8 @@ namespace PamhagenSysCtrl.Helpers { try { var config = new ConfigurationBuilder().AddIniFile(FileName).Build(); PlcPort = config["plc:port"] ?? "COM1"; - UpdateUrl = config["update:url"] ?? UpdateUrl; - var updateAuto = config["update:auto"]; - UpdateAuto = updateAuto == null || TrueValues.Contains(updateAuto.ToLower()); + UpdateUrl = config["update:url"]; + UpdateAuto = TrueValues.Contains(config["update:auto"]?.ToLower()); } catch (Exception exc) { MessageBox.Show($"Die Konfigurationsdatei konnte nicht gelesen werden:\n\n{exc.Message}", "Konfigurationsdatei lesen", MessageBoxButton.OK, MessageBoxImage.Error); Application.Current.Shutdown(); diff --git a/PamhagenSysCtrl/Helpers/UpdateService.cs b/PamhagenSysCtrl/Helpers/UpdateService.cs index fc8a9d7..9ad36c2 100644 --- a/PamhagenSysCtrl/Helpers/UpdateService.cs +++ b/PamhagenSysCtrl/Helpers/UpdateService.cs @@ -4,37 +4,42 @@ using System.Net.Http; using System.Text.Json.Nodes; namespace PamhagenSysCtrl.Helpers { - public sealed record UpdateInstaller(Version Version, Uri Url, long Size); - public static class UpdateService { - private static readonly HttpClient HttpClient = new() { - Timeout = Timeout.InfiniteTimeSpan, - }; + public static async Task<(string Version, string Url, long Size)?> GetLatestInstallerUrl(string feedUrl) { + try { + using var client = new HttpClient() { + Timeout = TimeSpan.FromSeconds(5), + }; + client.DefaultRequestHeaders.UserAgent.Clear(); + client.DefaultRequestHeaders.UserAgent.ParseAdd($"PamhagenSysCtrl/{App.Version} ({Environment.MachineName}, {Environment.OSVersion})"); + client.DefaultRequestHeaders.Accept.Clear(); + client.DefaultRequestHeaders.Accept.Add(new("application/json")); + using var res = await client.GetAsync(feedUrl); + if (!res.IsSuccessStatusCode) + return null; - public static async Task GetLatestInstallerAsync(string feedUrl, CancellationToken cancellationToken = default) { - using var response = await HttpClient.GetAsync(feedUrl, cancellationToken); - response.EnsureSuccessStatusCode(); - - var json = JsonNode.Parse(await response.Content.ReadAsStringAsync(cancellationToken)); - var latest = json!["data"]!.AsArray()[^1]!; - return new( - new Version((string)latest["version"]!), - new Uri((string)latest["url"]!), - (long)latest["size"]! - ); + var json = JsonNode.Parse(await res.Content.ReadAsStringAsync()); + var latest = json!["data"]!.AsArray()[^1]!; + return ((string)latest["version"]!, (string)latest["url"]!, (long)latest["size"]!); + } catch { + return null; + } } - public static async Task DownloadInstallerAsync(UpdateInstaller installer, string targetDirectory, IProgress? progress = null, CancellationToken cancellationToken = default) { - Directory.CreateDirectory(targetDirectory); - var fileName = Path.Combine(targetDirectory, $"PamhagenSysCtrl-{installer.Version}.msi"); - + public static async Task DownloadInstaller(string url, string filename, IProgress? progress = null, CancellationToken cancellationToken = default) { try { - using var response = await HttpClient.GetAsync(installer.Url, HttpCompletionOption.ResponseHeadersRead, cancellationToken); + using var client = new HttpClient() { + Timeout = TimeSpan.FromSeconds(5), + }; + client.DefaultRequestHeaders.UserAgent.Clear(); + client.DefaultRequestHeaders.UserAgent.ParseAdd($"PamhagenSysCtrl/{App.Version} ({Environment.MachineName}, {Environment.OSVersion})"); + client.DefaultRequestHeaders.Accept.Clear(); + using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, cancellationToken); response.EnsureSuccessStatusCode(); var contentLength = response.Content.Headers.ContentLength; - await using (var destination = new FileStream(fileName, FileMode.Create)) { + await using (var destination = new FileStream(filename, FileMode.Create)) { await using var source = await response.Content.ReadAsStreamAsync(cancellationToken); var buffer = new byte[81920]; long downloaded = 0; @@ -49,9 +54,8 @@ namespace PamhagenSysCtrl.Helpers { } progress?.Report(1); - return fileName; } catch { - File.Delete(fileName); + File.Delete(filename); throw; } } @@ -69,6 +73,5 @@ namespace PamhagenSysCtrl.Helpers { throw new InvalidOperationException("Der Installer konnte nicht gestartet werden."); } } - } } diff --git a/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml b/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml index d23b8da..31d4eb4 100644 --- a/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml +++ b/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml @@ -50,7 +50,7 @@ - + diff --git a/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml.cs b/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml.cs index 7ec428a..42415a3 100644 --- a/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml.cs +++ b/PamhagenSysCtrl/Windows/PlantSchemeWindow.xaml.cs @@ -42,6 +42,7 @@ namespace PamhagenSysCtrl.Windows { public PlantSchemeWindow() { InitializeComponent(); + Menu_Help_CheckForUpdates.IsEnabled = App.Config.UpdateUrl != null; Graph = new(); Graph.Draw(SchemeCanvas); FastSelectPaths = new Dictionary { @@ -118,9 +119,7 @@ namespace PamhagenSysCtrl.Windows { } private async void Menu_Help_CheckForUpdates_Click(object sender, RoutedEventArgs evt) { - if (Application.Current is App app) { - await app.CheckForUpdates(true); - } + await App.CheckForUpdates(true); } private void Menu_Help_About_Click(object sender, RoutedEventArgs evt) {