This commit is contained in:
@@ -3,5 +3,5 @@
|
|||||||
port = COM1
|
port = COM1
|
||||||
|
|
||||||
[update]
|
[update]
|
||||||
url = https://elwig.at/files/pamhagen-sysctrl/?format=json
|
url = https://elwig.at/files/pamhagen-sysctrl/latest
|
||||||
auto = true
|
auto = true
|
||||||
|
|||||||
+25
-46
@@ -12,7 +12,6 @@ namespace PamhagenSysCtrl {
|
|||||||
public static Dispatcher? MainDispatcher;
|
public static Dispatcher? MainDispatcher;
|
||||||
|
|
||||||
private readonly DispatcherTimer AutoUpdateTimer = new() { Interval = TimeSpan.FromHours(1) };
|
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 DataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "Anlagensteuerung Pamhagen");
|
||||||
public static readonly string ConfigPath = Path.Combine(DataPath, "config.ini");
|
public static readonly string ConfigPath = Path.Combine(DataPath, "config.ini");
|
||||||
@@ -50,69 +49,49 @@ namespace PamhagenSysCtrl {
|
|||||||
|
|
||||||
base.OnStartup(evt);
|
base.OnStartup(evt);
|
||||||
|
|
||||||
if (Config.UpdateAuto && Config.UpdateUrl != null) {
|
if (Config.UpdateUrl != null && Config.UpdateAuto) {
|
||||||
AutoUpdateTimer.Tick += async (_, _) => await CheckForUpdates();
|
AutoUpdateTimer.Tick += async (_, _) => {
|
||||||
AutoUpdateTimer.Start();
|
try {
|
||||||
_ = Dispatcher.BeginInvoke(async () => {
|
|
||||||
await Task.Delay(1500);
|
|
||||||
await CheckForUpdates();
|
await CheckForUpdates();
|
||||||
|
} catch { }
|
||||||
|
};
|
||||||
|
AutoUpdateTimer.Start();
|
||||||
|
await Dispatcher.BeginInvoke(async () => {
|
||||||
|
await Task.Delay(1500);
|
||||||
|
try {
|
||||||
|
await CheckForUpdates();
|
||||||
|
} catch { }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CheckForUpdates(bool showResult = false) {
|
public static async Task CheckForUpdates(bool showResult = false) {
|
||||||
if (IsCheckingForUpdates) {
|
if (Config.UpdateUrl == null) return;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
IsCheckingForUpdates = true;
|
|
||||||
try {
|
try {
|
||||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(15));
|
var latest = await UpdateService.GetLatestInstallerUrl(Config.UpdateUrl);
|
||||||
var latest = await UpdateService.GetLatestInstallerAsync(Config.UpdateUrl, timeout.Token);
|
if (latest.HasValue && new Version(latest.Value.Version) > Version) {
|
||||||
if (latest.Version > Version) {
|
var dialog = new UpdateDialog(latest.Value.Version, latest.Value.Url, latest.Value.Size) {
|
||||||
var dialog = new UpdateDialog(latest) {
|
Owner = Current.MainWindow,
|
||||||
Owner = MainWindow,
|
|
||||||
};
|
};
|
||||||
if (dialog.ShowDialog() == true) {
|
if (dialog.ShowDialog() == true) {
|
||||||
Shutdown();
|
Current.Shutdown();
|
||||||
}
|
}
|
||||||
} else if (showResult) {
|
} else if (showResult) {
|
||||||
MessageBox.Show(
|
MessageBox.Show($"Die Anlagensteuerung ist auf dem aktuellen Stand.\n(Version {Version})", "Nach Updates suchen", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
$"Die Anlagensteuerung ist auf dem aktuellen Stand. (Version {Version})",
|
|
||||||
"Nach Updates suchen",
|
|
||||||
MessageBoxButton.OK,
|
|
||||||
MessageBoxImage.Information
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} catch (OperationCanceledException) {
|
} catch (OperationCanceledException) {
|
||||||
if (showResult) {
|
if (showResult) {
|
||||||
MessageBox.Show(
|
MessageBox.Show("Zeitüberschreitung beim Abrufen der Update-Informationen.", "Nach Updates suchen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
"Zeitüberschreitung beim Abrufen der Update-Informationen.",
|
} else {
|
||||||
"Nach Updates suchen",
|
throw;
|
||||||
MessageBoxButton.OK,
|
|
||||||
MessageBoxImage.Error
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} catch (Exception exc) {
|
} catch (Exception exc) {
|
||||||
if (showResult) {
|
if (showResult) {
|
||||||
MessageBox.Show(
|
MessageBox.Show($"Die Update-Informationen konnten nicht abgerufen werden:\n\n{exc.Message}", "Nach Updates suchen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
$"Die Update-Informationen konnten nicht abgerufen werden:\n\n{exc.Message}",
|
} else {
|
||||||
"Nach Updates suchen",
|
throw;
|
||||||
MessageBoxButton.OK,
|
|
||||||
MessageBoxImage.Error
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
IsCheckingForUpdates = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
ResizeMode="NoResize" ShowInTaskbar="False" Topmost="True"
|
ResizeMode="NoResize" ShowInTaskbar="False" Topmost="True"
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
Title="Update verfügbar - Anlagensteuerung Pamhagen"
|
Title="Update verfügbar - Anlagensteuerung Pamhagen"
|
||||||
Height="220" Width="460"
|
Height="220" Width="480"
|
||||||
Closed="OnClosed">
|
Closed="OnClosed">
|
||||||
<Grid Margin="20">
|
<Grid Margin="20">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
Version <Run x:Name="VersionText" FontWeight="Bold">0.0.0</Run> ist verfügbar.<LineBreak/>
|
Version <Run x:Name="VersionText" FontWeight="Bold">0.0.0</Run> ist verfügbar.<LineBreak/>
|
||||||
Soll das Update heruntergeladen und installiert werden?<LineBreak/>
|
Soll das Update heruntergeladen und installiert werden?<LineBreak/>
|
||||||
(ca. <Run x:Name="SizeText">0</Run> MB)<LineBreak/><LineBreak/>
|
(ca. <Run x:Name="SizeText">0</Run> MB)<LineBreak/><LineBreak/>
|
||||||
<Run FontWeight="Bold">Hinweis:</Run> Die Anlagensteuerung wird zur Installation geschlossen.
|
<Run FontWeight="Bold">Hinweis:</Run> Die Anlagensteuerung wird für die Installation geschlossen.
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
|
|
||||||
<StackPanel x:Name="DownloadPanel" Grid.Row="0" VerticalAlignment="Center" Visibility="Collapsed">
|
<StackPanel x:Name="DownloadPanel" Grid.Row="0" VerticalAlignment="Center" Visibility="Collapsed">
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
using PamhagenSysCtrl.Helpers;
|
using PamhagenSysCtrl.Helpers;
|
||||||
|
using System.IO;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
|
||||||
namespace PamhagenSysCtrl.Dialogs {
|
namespace PamhagenSysCtrl.Dialogs {
|
||||||
public partial class UpdateDialog : Window {
|
public partial class UpdateDialog : Window {
|
||||||
|
|
||||||
private readonly UpdateInstaller Installer;
|
private readonly string Url;
|
||||||
private readonly CancellationTokenSource Cancellation = new();
|
private readonly CancellationTokenSource Cancellation = new();
|
||||||
|
|
||||||
public UpdateDialog(UpdateInstaller installer) {
|
public UpdateDialog(string version, string url, long size) {
|
||||||
Installer = installer;
|
Url = url;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
VersionText.Text = installer.Version.ToString();
|
VersionText.Text = version;
|
||||||
SizeText.Text = Math.Ceiling(installer.Size / 1024d / 1024d).ToString("N0");
|
SizeText.Text = Math.Ceiling(size / 1024d / 1024d).ToString("N0");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnClosed(object? sender, EventArgs evt) {
|
private void OnClosed(object? sender, EventArgs evt) {
|
||||||
@@ -26,20 +27,15 @@ namespace PamhagenSysCtrl.Dialogs {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
var progress = new Progress<double>(value => DownloadProgress.Value = value * 100);
|
var progress = new Progress<double>(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...";
|
StatusText.Text = "Installer wird gestartet...";
|
||||||
UpdateService.StartInstaller(fileName);
|
UpdateService.StartInstaller(filename);
|
||||||
DialogResult = true;
|
DialogResult = true;
|
||||||
} catch (OperationCanceledException) {
|
} catch (OperationCanceledException) {
|
||||||
// Closing the dialog cancels an active download.
|
// Closing the dialog cancels an active download.
|
||||||
} catch (Exception exc) {
|
} catch (Exception exc) {
|
||||||
MessageBox.Show(
|
MessageBox.Show($"Das Update konnte nicht installiert werden:\n\n{exc.Message}", "Update installieren", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
this,
|
|
||||||
$"Das Update konnte nicht installiert werden:\n\n{exc.Message}",
|
|
||||||
"Update installieren",
|
|
||||||
MessageBoxButton.OK,
|
|
||||||
MessageBoxImage.Error
|
|
||||||
);
|
|
||||||
Description.Visibility = Visibility.Visible;
|
Description.Visibility = Visibility.Visible;
|
||||||
DownloadPanel.Visibility = Visibility.Collapsed;
|
DownloadPanel.Visibility = Visibility.Collapsed;
|
||||||
InstallButton.IsEnabled = true;
|
InstallButton.IsEnabled = true;
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
private readonly string FileName;
|
private readonly string FileName;
|
||||||
|
|
||||||
public string PlcPort = "COM1";
|
public string PlcPort = "COM1";
|
||||||
public string? UpdateUrl = "https://elwig.at/files/pamhagen-sysctrl/?format=json";
|
public string? UpdateUrl;
|
||||||
public bool UpdateAuto = true;
|
public bool UpdateAuto;
|
||||||
|
|
||||||
public Config(string filename) {
|
public Config(string filename) {
|
||||||
FileName = filename;
|
FileName = filename;
|
||||||
@@ -21,9 +21,8 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
try {
|
try {
|
||||||
var config = new ConfigurationBuilder().AddIniFile(FileName).Build();
|
var config = new ConfigurationBuilder().AddIniFile(FileName).Build();
|
||||||
PlcPort = config["plc:port"] ?? "COM1";
|
PlcPort = config["plc:port"] ?? "COM1";
|
||||||
UpdateUrl = config["update:url"] ?? UpdateUrl;
|
UpdateUrl = config["update:url"];
|
||||||
var updateAuto = config["update:auto"];
|
UpdateAuto = TrueValues.Contains(config["update:auto"]?.ToLower());
|
||||||
UpdateAuto = updateAuto == null || TrueValues.Contains(updateAuto.ToLower());
|
|
||||||
} catch (Exception exc) {
|
} catch (Exception exc) {
|
||||||
MessageBox.Show($"Die Konfigurationsdatei konnte nicht gelesen werden:\n\n{exc.Message}", "Konfigurationsdatei lesen", MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBox.Show($"Die Konfigurationsdatei konnte nicht gelesen werden:\n\n{exc.Message}", "Konfigurationsdatei lesen", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
Application.Current.Shutdown();
|
Application.Current.Shutdown();
|
||||||
|
|||||||
@@ -4,37 +4,42 @@ using System.Net.Http;
|
|||||||
using System.Text.Json.Nodes;
|
using System.Text.Json.Nodes;
|
||||||
|
|
||||||
namespace PamhagenSysCtrl.Helpers {
|
namespace PamhagenSysCtrl.Helpers {
|
||||||
public sealed record UpdateInstaller(Version Version, Uri Url, long Size);
|
|
||||||
|
|
||||||
public static class UpdateService {
|
public static class UpdateService {
|
||||||
|
|
||||||
private static readonly HttpClient HttpClient = new() {
|
public static async Task<(string Version, string Url, long Size)?> GetLatestInstallerUrl(string feedUrl) {
|
||||||
Timeout = Timeout.InfiniteTimeSpan,
|
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<UpdateInstaller> GetLatestInstallerAsync(string feedUrl, CancellationToken cancellationToken = default) {
|
var json = JsonNode.Parse(await res.Content.ReadAsStringAsync());
|
||||||
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]!;
|
var latest = json!["data"]!.AsArray()[^1]!;
|
||||||
return new(
|
return ((string)latest["version"]!, (string)latest["url"]!, (long)latest["size"]!);
|
||||||
new Version((string)latest["version"]!),
|
} catch {
|
||||||
new Uri((string)latest["url"]!),
|
return null;
|
||||||
(long)latest["size"]!
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<string> DownloadInstallerAsync(UpdateInstaller installer, string targetDirectory, IProgress<double>? progress = null, CancellationToken cancellationToken = default) {
|
public static async Task DownloadInstaller(string url, string filename, IProgress<double>? progress = null, CancellationToken cancellationToken = default) {
|
||||||
Directory.CreateDirectory(targetDirectory);
|
|
||||||
var fileName = Path.Combine(targetDirectory, $"PamhagenSysCtrl-{installer.Version}.msi");
|
|
||||||
|
|
||||||
try {
|
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();
|
response.EnsureSuccessStatusCode();
|
||||||
var contentLength = response.Content.Headers.ContentLength;
|
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);
|
await using var source = await response.Content.ReadAsStreamAsync(cancellationToken);
|
||||||
var buffer = new byte[81920];
|
var buffer = new byte[81920];
|
||||||
long downloaded = 0;
|
long downloaded = 0;
|
||||||
@@ -49,9 +54,8 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
progress?.Report(1);
|
progress?.Report(1);
|
||||||
return fileName;
|
|
||||||
} catch {
|
} catch {
|
||||||
File.Delete(fileName);
|
File.Delete(filename);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,6 +73,5 @@ namespace PamhagenSysCtrl.Helpers {
|
|||||||
throw new InvalidOperationException("Der Installer konnte nicht gestartet werden.");
|
throw new InvalidOperationException("Der Installer konnte nicht gestartet werden.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""/>
|
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""/>
|
||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem Header="Nach Updates suchen..." Click="Menu_Help_CheckForUpdates_Click">
|
<MenuItem x:Name="Menu_Help_CheckForUpdates" Header="Nach Updates suchen..." Click="Menu_Help_CheckForUpdates_Click">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""/>
|
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="16" Text=""/>
|
||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ namespace PamhagenSysCtrl.Windows {
|
|||||||
|
|
||||||
public PlantSchemeWindow() {
|
public PlantSchemeWindow() {
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
Menu_Help_CheckForUpdates.IsEnabled = App.Config.UpdateUrl != null;
|
||||||
Graph = new();
|
Graph = new();
|
||||||
Graph.Draw(SchemeCanvas);
|
Graph.Draw(SchemeCanvas);
|
||||||
FastSelectPaths = new Dictionary<Button, (ISource, Pump, ISink)> {
|
FastSelectPaths = new Dictionary<Button, (ISource, Pump, ISink)> {
|
||||||
@@ -118,9 +119,7 @@ namespace PamhagenSysCtrl.Windows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async void Menu_Help_CheckForUpdates_Click(object sender, RoutedEventArgs evt) {
|
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) {
|
private void Menu_Help_About_Click(object sender, RoutedEventArgs evt) {
|
||||||
|
|||||||
Reference in New Issue
Block a user