Skip to content

Commit

Permalink
feat: fix concurrency issues with the automation interface that occur…
Browse files Browse the repository at this point in the history
… especially if the library manager is open during a package update
  • Loading branch information
iadonkey committed Nov 11, 2023
1 parent bf17626 commit db163dd
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 27 deletions.
55 changes: 48 additions & 7 deletions TwinpackShared/TwinpackUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ namespace Twinpack
{
public class TwinpackUtils
{
private static readonly Guid _libraryManagerGuid = Guid.Parse("e1825adc-a79c-4e8e-8793-08d62d84be5b");
public static string DefaultLibraryCachePath { get { return $@"{Directory.GetCurrentDirectory()}\.Zeugwerk\libraries"; } }

public static string LicensesPath = @"C:\TwinCAT\3.1\CustomConfig\Licenses";
Expand Down Expand Up @@ -74,6 +75,48 @@ public static Project ActivePlc(DTE2 dte)
return null;
}

public static void CloseAllPackageRelatedWindows(DTE2 dte, PackageVersionGetResponse packageVersion)
{
Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread();

// Close all windows that have been opened with a library manager
foreach (Window window in dte.Windows)
{
try
{
var obj = (window as dynamic).Object;
if (obj == null)
continue;

if (obj.EditorViewFactoryGuid == _libraryManagerGuid)
{
window.Close(vsSaveChanges.vsSaveChangesNo);
continue;
}

if (!(obj.FileName as string).StartsWith("["))
continue;

if (Regex.Match(obj.FileName, $@"\[{packageVersion.Name}.*?\({packageVersion.DistributorName}\)\].*", RegexOptions.IgnoreCase).Success)
{
window.Close(vsSaveChanges.vsSaveChangesNo);
continue;
}

foreach (var dependency in packageVersion.Dependencies)
{
if (Regex.Match(obj.FileName, $@"\[{dependency.Name}.*?\({dependency.DistributorName}\)\].*", RegexOptions.IgnoreCase).Success)
{
window.Close(vsSaveChanges.vsSaveChangesNo);
break;
}
}

}
catch { }
}
}

public static int BuildErrorCount(DTE2 dte)
{
int errorCount = 0;
Expand Down Expand Up @@ -166,14 +209,14 @@ public static async Task<List<PackageVersionGetResponse>> DownloadPackageVersion
if (!referenceFound || forceDownload)
{
_logger.Info($"Downloading {packageVersion.Title} (version: {packageVersion.Version}, distributor: {packageVersion.DistributorName})");

downloadedPackageVersions.Add(await server.GetPackageVersionAsync((int)packageVersion.PackageVersionId,
includeBinary: true, cachePath: cachePath));
var pk = await server.GetPackageVersionAsync((int)packageVersion.PackageVersionId, includeBinary: true, cachePath: cachePath);
downloadedPackageVersions.Add(pk);
}

foreach (var dependency in packageVersion?.Dependencies ?? new List<PackageVersionGetResponse>())
{
downloadedPackageVersions.AddRange(await DownloadPackageVersionAndDependenciesAsync(libManager, dependency, server, forceDownload, cachePath));
var pk = await DownloadPackageVersionAndDependenciesAsync(libManager, dependency, server, forceDownload, cachePath);
downloadedPackageVersions.AddRange(pk);
}

return downloadedPackageVersions;
Expand All @@ -184,8 +227,7 @@ public static async Task InstallPackageVersionsAsync(ITcPlcLibraryManager libMan
foreach(var packageVersion in packageVersions)
{
var suffix = packageVersion.Compiled == 1 ? "compiled-library" : "library";

await Task.Run( () => { libManager.InstallLibrary("System", $@"{cachePath ?? DefaultLibraryCachePath}\{packageVersion.Target}\{packageVersion.Name}_{packageVersion.Version}.{suffix}", bOverwrite: true); });
await Task.Run(() => { libManager.InstallLibrary("System", $@"{cachePath ?? DefaultLibraryCachePath}\{packageVersion.Target}\{packageVersion.Name}_{packageVersion.Version}.{suffix}", bOverwrite: true); });
}
}

Expand Down Expand Up @@ -250,7 +292,6 @@ public static void RemoveReference(ITcPlcLibraryManager libManager, string place

public static void AddReference(ITcPlcLibraryManager libManager, string placeholderName, string libraryName, string version, string distributorName, bool addAsPlaceholder = true)
{

distributorName = distributorName ?? GuessDistributorName(libManager, libraryName, version);
RemoveReference(libManager, placeholderName, libraryName, version, distributorName);

Expand Down
1 change: 1 addition & 0 deletions TwinpackVsixShared/Dialogs/CatalogWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@

<GridSplitter Grid.Column="1" Grid.RowSpan="5" Width="5" HorizontalAlignment="Stretch"></GridSplitter>
<ScrollViewer Grid.RowSpan="6" x:Name="PackageVersionView" Visibility="{Binding PackageVersion, Converter={StaticResource NullToVisibilityConverter}}" Margin="5,0,0,0"
IsEnabled="{Binding IsPackageVersionPanelEnabled, ElementName=This}"
Grid.Column="2" ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Visible">
<Grid>
<Grid.RowDefinitions>
Expand Down
70 changes: 50 additions & 20 deletions TwinpackVsixShared/Dialogs/CatalogWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using EnvDTE;
using Microsoft.VisualStudio.Threading;
using TCatSysManagerLib;

Expand All @@ -29,6 +29,7 @@ public partial class CatalogWindow : UserControl, INotifyPropertyChanged
private List<Models.CatalogItem> _availablePackages = new List<Models.CatalogItem>();
private List<Models.CatalogItem> _installedPackages = new List<Models.CatalogItem>();
private SemaphoreSlim _semaphorePackages = new SemaphoreSlim(1, 1);
private SemaphoreSlim _semaphoreAction = new SemaphoreSlim(1, 1);
private List<Models.PackageVersionGetResponse> _packageVersions;

private Models.CatalogItem _item = null;
Expand Down Expand Up @@ -468,6 +469,7 @@ private async void Dialog_Loaded(object sender, RoutedEventArgs e)
finally
{
IsCatalogLoading = false;
IsPackageVersionPanelEnabled = _plcConfig != null;
IsUpdateAvailable = _twinpackServer.IsClientUpdateAvailable == true;
btnLogin.Text = _twinpackServer.LoggedIn ? "Logout" : "Login";
btnRegister.Visibility = _twinpackServer.LoggedIn ? Visibility.Collapsed : Visibility.Visible;
Expand Down Expand Up @@ -545,12 +547,17 @@ public async void EditPackageButton_Click(object sender, RoutedEventArgs e)
_logger.Trace(ex);
_logger.Error(ex.Message);
}
finally
{
IsPackageVersionPanelEnabled = _plcConfig != null;
}
}

public async void UninstallPackageButton_Click(object sender, RoutedEventArgs e)
{
try
{
_semaphoreAction.Wait();
IsPackageVersionPanelEnabled = false;
await Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
await _context?.Logger?.ActivateAsync(clear: true);
Expand Down Expand Up @@ -583,13 +590,15 @@ public async void UninstallPackageButton_Click(object sender, RoutedEventArgs e)
finally
{
IsPackageVersionPanelEnabled = true;
_semaphoreAction.Release();
}
}

public async void AddPackageButton_Click(object sender, RoutedEventArgs e)
{
try
{
_semaphoreAction.Wait();
IsPackageVersionPanelEnabled = false;
await Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
await _context?.Logger?.ActivateAsync(clear: true);
Expand Down Expand Up @@ -620,6 +629,7 @@ public async void AddPackageButton_Click(object sender, RoutedEventArgs e)
finally
{
IsPackageVersionPanelEnabled = true;
_semaphoreAction.Release();
}
}

Expand All @@ -633,6 +643,7 @@ public async void RestoreAllPackageButton_Click(object sender, RoutedEventArgs e
IEnumerable<Models.CatalogItem> items = new List<Models.CatalogItem>();
try
{
_semaphoreAction.Wait();
IsRestoreAllEnabled = false;
IsPackageVersionPanelEnabled = false;

Expand Down Expand Up @@ -690,6 +701,7 @@ public async void RestoreAllPackageButton_Click(object sender, RoutedEventArgs e
{
IsPackageVersionPanelEnabled = true;
IsRestoreAllEnabled = true;
_semaphoreAction.Release();
}
}

Expand All @@ -698,6 +710,8 @@ public async void UpdateAllPackageButton_Click(object sender, RoutedEventArgs e)
IEnumerable<Models.CatalogItem> items = new List<Models.CatalogItem>();
try
{
_semaphoreAction.Wait();

IsPackageVersionPanelEnabled = false;
IsUpdateAllEnabled = false;

Expand Down Expand Up @@ -754,6 +768,7 @@ public async void UpdateAllPackageButton_Click(object sender, RoutedEventArgs e)
{
IsPackageVersionPanelEnabled = true;
IsUpdateAllEnabled = true;
_semaphoreAction.Release();
}
}

Expand Down Expand Up @@ -831,8 +846,6 @@ public async Task UninstallPackageAsync()
{
await Microsoft.VisualStudio.Shell.ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

IsPackageVersionPanelEnabled = false;

if (PackageVersion.PackageVersionId == null)
throw new Exception("No packages is selected that could be uninstalled!");

Expand All @@ -841,6 +854,7 @@ public async Task UninstallPackageAsync()
var libManager = sysManager.LookupTreeItem(plc.PathName + "^References") as ITcPlcLibraryManager;

_context.Dte.ExecuteCommand("File.SaveAll");
TwinpackUtils.CloseAllPackageRelatedWindows(_context.Dte, PackageVersion);
TwinpackUtils.RemoveReference(libManager, Package.Title, Package.Title, PackageVersion.Version, _package.DistributorName);
_context.Dte.ExecuteCommand("File.SaveAll");

Expand Down Expand Up @@ -922,9 +936,13 @@ public async Task AddOrUpdatePackageAsync(Models.PackageVersionGetResponse packa
}
}

_logger.Info($"Installing package {packageVersion.Name} ...");

var downloadPackageVersion = await TwinpackUtils.DownloadPackageVersionAndDependenciesAsync(libManager, packageVersion, _twinpackServer, forceDownload: ForcePackageVersionDownload, cachePath: cachePath);

// Close Library Manager and all windows that are related to the library. These windows cause race conditions
TwinpackUtils.CloseAllPackageRelatedWindows(_context.Dte, packageVersion);
TwinpackUtils.RemoveReference(libManager, packageVersion.Title, packageVersion.Title, packageVersion.Version, packageVersion.DistributorName);

_logger.Info($"Installing package {packageVersion.Name} ...");
await TwinpackUtils.InstallPackageVersionsAsync(libManager, downloadPackageVersion, cachePath: cachePath);

await Task.Run(() =>
Expand Down Expand Up @@ -1021,7 +1039,7 @@ public void ShowLicenseTmcButton_Click(object sender, RoutedEventArgs e)
}
public void RegisterButton_Click(object sender, RoutedEventArgs e)
{
Process.Start(_twinpackServer.RegisterUrl);
System.Diagnostics.Process.Start(_twinpackServer.RegisterUrl);
}

public async void LoginButton_Click(object sender, RoutedEventArgs e)
Expand Down Expand Up @@ -1202,7 +1220,6 @@ private async Task LoadInstalledPackagesAsync()
}

IsNewReference = PackageVersion.PackageVersionId == null || !_installedPackages.Any(x => x.PackageId == PackageVersion.PackageId);
IsPackageVersionPanelEnabled = _plcConfig != null;
InstalledPackagesCount = _installedPackages.Count();
UpdateablePackagesCount = _installedPackages.Where(x => x.IsUpdateable).Count();
}
Expand Down Expand Up @@ -1393,6 +1410,7 @@ public async void ReloadButton_Click(object sender, RoutedEventArgs e)
}
finally
{
IsPackageVersionPanelEnabled = _plcConfig != null;
IsCatalogLoading = false;
}
}
Expand Down Expand Up @@ -1422,7 +1440,7 @@ public async void CreateConfig_Click(object sender, RoutedEventArgs e)
$"in {config.FilePath} for your TwinCAT solution, do you want to " +
$"review and/or edit it?", "Configuration", MessageBoxButton.YesNo))
{
Process process = new Process
var process = new System.Diagnostics.Process
{
StartInfo = new ProcessStartInfo
{
Expand All @@ -1445,33 +1463,45 @@ public async void CreateConfig_Click(object sender, RoutedEventArgs e)
_logger.Trace(ex);
_logger.Error(ex.Message);
}
finally
{
IsPackageVersionPanelEnabled = _plcConfig != null;
}
}

public void ShowProjectUrl_Click(object sender, RoutedEventArgs e)
{
Process.Start(PackageVersion.ProjectUrl);
System.Diagnostics.Process.Start(PackageVersion.ProjectUrl);
}

public void UpdateAvailableButton_Click(object sender, RoutedEventArgs e)
{
Process.Start(_twinpackServer?.UserInfo?.UpdateUrl);
System.Diagnostics.Process.Start(_twinpackServer?.UserInfo?.UpdateUrl);
}

public async void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
var text = ((TextBox)sender).Text;

if (IsBrowsingAvailablePackages)
try
{
_searchText = text;
await Task.Delay(250);
var text = ((TextBox)sender).Text;

if (IsBrowsingAvailablePackages)
{
_searchText = text;
await Task.Delay(250);

if(_searchText == text)
await LoadAvailablePackagesAsync(text);
if (_searchText == text)
await LoadAvailablePackagesAsync(text);
}
else
{
UpdateCatalog();
}
}
else
catch (Exception ex)
{
UpdateCatalog();
_logger.Trace(ex);
_logger.Error(ex.Message);
}
}
}
Expand Down

0 comments on commit db163dd

Please sign in to comment.