diff --git a/Extension.md b/Extension.md index 0a77e9c..1062972 100644 --- a/Extension.md +++ b/Extension.md @@ -1,14 +1,9 @@ - ![Icon](https://raw.githubusercontent.com/swittlich/OneWare.CC-Toolchain/refs/heads/main/Icon.png) + ![Icon](https://raw.githubusercontent.com/one-ware/OneWare.CC-Toolchain/refs/heads/main/Icon.png) -Toolchain for CologneChip-FPGAs. This is not an official plugin. +Extension for GateMate™ FPGAs. Helps you get started with GateMate™ faster. ### Get Started 1. Install this Plugin -2. Download toolchain from [CologneChip](https://colognechip.com/mygatemate/) (it's free, but you will need an account). -3. Unzip the toolchain -4. Set Path to toolchain in OneWareStudio Extras -> Settings -> Tools -> CologneChip -5. Set the Toolchain in OneWareStudio to "CologneChip" -6. (optional) Add '*.ccf' files to your project fpgaproj-File - -More Information on [GitHub](https://github.com/swittlich/OneWare.CC-Toolchain/blob/main/README.md) \ No newline at end of file +2. Follow the steps to install the hardware support and toolchain +3. Use the newly provided template to get started in seconds \ No newline at end of file diff --git a/README.md b/README.md index 0e5230d..a28d903 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,4 @@ -[![Test](https://github.com/swittlich/OneWare.CC-Toolchain/actions/workflows/test.yml/badge.svg)](https://github.com/swittlich/OneWare.CC-Toolchain/actions/workflows/test.yml) -[![Publish](https://github.com/swittlich/OneWare.CC-Toolchain/actions/workflows/publish.yml/badge.svg)](https://github.com/swittlich/OneWare.CC-Toolchain/actions/workflows/publish.yml) +[![Test](https://github.com/one-ware/OneWare.CC-Toolchain/actions/workflows/test.yml/badge.svg)](https://github.com/one-ware/OneWare.CC-Toolchain/actions/workflows/test.yml) +[![Publish](https://github.com/one-ware/OneWare.CC-Toolchain/actions/workflows/publish.yml/badge.svg)](https://github.com/one-ware/OneWare.CC-Toolchain/actions/workflows/publish.yml) -# OneWare.CologneChip - -An integration of the CologneChip toolchain for OneWare. -The toolchain for the GateMate FPGA boards. For already provided boards have a look at the [GateMate repository](https://github.com/swittlich/OneWare.GateMate). - -- [CologneChip](https://colognechip.com/) -- [Toolchain](https://www.colognechip.com/docs/ug1002-toolchain-install-latest.pdf) -- [OneWare](https://one-ware.com) - -![image](https://raw.githubusercontent.com/swittlich/OneWare.CC-Toolchain/refs/heads/main/Icon.png) - -# Installation Guide - -1. Install this Plugin -2. Download toolchain from [CologneChip](https://colognechip.com/mygatemate/) (it's free, but you will need an account). -3. Unzip the toolchain -4. Set Path to toolchain in OneWareStudio Extras -> Settings -> Tools -> CologneChip -5. Set the Toolchain in OneWareStudio to "CologneChip" -6. (optional) Add '*.ccf' files to your project fpgaproj-File - -# Special thanks - -Thanks to Dr. Michael Gude from CologneChips AG for allowing us to integrate his pictures and logos for this tool chain. - -# Known errors and workarounds - -## Yosys error code != 0 -The synthesis can run, but it can happen that the error code of the synthesis is not equal to 0. The normal behaviour of the program is then not to continue working with the P_R tool. In this case, however, it may be desirable to ignore the error code. - -Can be set via Extras -> Setting -> Tools -> CologneChip -> ‘ignore an exit code not equal to 0 after synthesis’ - -## Ignore UI for HardwarePin Mapping - -Currently, OneWare studio cannot always find all signals in and out of the Top Level Entity (see also this [issue on GitHub](https://github.com/one-ware/OneWare/issues/18)). -If this is the case, then the pin mapping cannot be done via the UI, which means that no CCF file can be generated or an existing mapping in an existing ccf file is overwritten with nothing or commented out. To prevent this, the ignoreGUI option has been introduced. -This can be set as follows: -Extras -> Setting -> Tools -> CologneChip -> ‘Ignore UI for HardwarePin Mapping’ - -## OlimexEVB "fails to open device" on Windows - -```txt -empty -fails to open device -JTAG init failed with: std::exception -``` -Open On-Chip Debugger (OpenOCD) is used for DirtyJTag. This is not installed by default under windows with the openFPGALoader. - -It would be possible to install this tool on your system (untested). -[Here](https://openocd.org/pages/getting-openocd.html) is the link to the tool and [here](https://gnutoolchains.com/arm-eabi/openocd/) to the Windows binaries. -Please note that these are 3rd party binaries. - -Or the use of WSL (somewhat tested). For more information have a look at the [WSL guide](https://raw.githubusercontent.com/swittlich/OneWare.CC-Toolchain/refs/heads/main/docs/WSL.md). -Just create a WSL Instance and follow [this MS guide](https://learn.microsoft.com/en-us/windows/wsl/connect-usb) to attach your board to a VM. -Select the "useWSL" setting from the CologneChipLoader settings. \ No newline at end of file +# GateMate™ Quick Start Extension \ No newline at end of file diff --git a/oneware-extension.json b/oneware-extension.json index 79e9f99..c8a90f5 100644 --- a/oneware-extension.json +++ b/oneware-extension.json @@ -1,99 +1,37 @@ { - "category": "Toolchains", + "category": "Tools", "type": "Plugin", - "name": "CologneChip Extension", + "name": "GateMate™ Quick-Start", "id": "OneWare.CologneChip", - "description": "CologneChip-Toolchain for OneWare", + "description": "Quick-Start extension to setup Toolchain and Hardware Support for GateMate™ by CologneChip", "license": "MIT", - "iconUrl": "https://raw.githubusercontent.com/swittlich/OneWare.CC-Toolchain/main/Icon.png", + "urlLaunchIds": "colognechip", + "iconUrl": "https://raw.githubusercontent.com/one-ware/OneWare.CC-Toolchain/main/Icon.png", "tabs": [ { "title": "Readme", - "contentUrl": "https://raw.githubusercontent.com/swittlich/OneWare.CC-Toolchain/main/Extension.md" + "contentUrl": "https://raw.githubusercontent.com/one-ware/OneWare.CC-Toolchain/main/Extension.md" }, { "title": "License", - "contentUrl": "https://raw.githubusercontent.com/swittlich/OneWare.CC-Toolchain/main/License.md" + "contentUrl": "https://raw.githubusercontent.com/one-ware/OneWare.CC-Toolchain/main/License.md" }, { "title": "Release Notes", - "contentUrl": "https://raw.githubusercontent.com/swittlich/OneWare.CC-Toolchain/main/Release.md" + "contentUrl": "https://raw.githubusercontent.com/one-ware/OneWare.CC-Toolchain/main/Release.md" } ], "links": [ { "name": "Github", - "url": "https://github.com/swittlich/OneWare.CC-Toolchain" + "url": "https://github.com/one-ware/OneWare.CC-Toolchain" } ], - "sourceUrl": "https://github.com/swittlich/OneWare.CC-Toolchain/releases/download", + "sourceUrl": "https://github.com/one-ware/OneWare.CC-Toolchain/releases/download", "versions": [ { - "version": "0.1", - "targets": [ - { - "target": "all" - } - ] - }, - { - "version": "0.2", - "targets": [ - { - "target": "all" - } - ] - }, - { - "version": "0.3", - "targets": [ - { - "target": "all" - } - ] - }, - { - "version": "0.4", - "targets": [ - { - "target": "all" - } - ] - }, - { - "version": "0.5", - "targets": [ - { - "target": "all" - } - ] - }, - { - "version": "0.6", - "targets": [ - { - "target": "all" - } - ] - }, - { - "version": "0.7.1", - "targets": [ - { - "target": "all" - } - ] - }, - { - "version": "1.0.0", - "targets": [ - { - "target": "all" - } - ] - }, - { - "version": "1.0.1", + "version": "1.0", + "minStudioVersion": "1.0.19", "targets": [ { "target": "all" diff --git a/src/OneWare.CologneChip/OneWare.CologneChip.csproj b/src/OneWare.CologneChip/OneWare.CologneChip.csproj index 44306ed..61c32c7 100644 --- a/src/OneWare.CologneChip/OneWare.CologneChip.csproj +++ b/src/OneWare.CologneChip/OneWare.CologneChip.csproj @@ -1,7 +1,7 @@  - 1.0.1 + 1.0 net10.0 enable enable @@ -9,15 +9,11 @@ true false - - - - - - - + + + diff --git a/src/OneWare.CologneChip/OneWareCologneChipModule.cs b/src/OneWare.CologneChip/OneWareCologneChipModule.cs index 080cff5..c5850c3 100644 --- a/src/OneWare.CologneChip/OneWareCologneChipModule.cs +++ b/src/OneWare.CologneChip/OneWareCologneChipModule.cs @@ -1,217 +1,55 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Layout; -using Avalonia.Markup.Xaml.Styling; -using CommunityToolkit.Mvvm.Input; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Threading; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using OneWare.CologneChip.Helpers; -using OneWare.CologneChip.Services; +using OneWare.CologneChip.Templates; using OneWare.CologneChip.ViewModels; using OneWare.CologneChip.Views; -using OneWare.Essentials.Helpers; -using OneWare.Essentials.Models; using OneWare.Essentials.Services; +using OneWare.UniversalFpgaProjectSystem.Helpers; using OneWare.UniversalFpgaProjectSystem.Models; using OneWare.UniversalFpgaProjectSystem.Services; namespace OneWare.CologneChip; -// ReSharper disable once UnusedType.Global public class OneWareCologneChipModule : OneWareModuleBase { public override void RegisterServices(IServiceCollection containerRegistry) { - containerRegistry.AddSingleton(); - containerRegistry.AddSingleton(); - containerRegistry.AddSingleton(); - containerRegistry.AddSingleton(); - containerRegistry.AddSingleton(); + } - + public override void Initialize(IServiceProvider containerProvider) { - var settingsService = containerProvider.Resolve(); - var cologneChipService = containerProvider.Resolve(); var fpgaService = containerProvider.Resolve(); + fpgaService.RegisterTemplate(); - var defaultCologneChipPath = "./"; - - var resourceInclude = new ResourceInclude(new Uri("avares://OneWare.CologneChip/Styles/Icons.axaml")) - {Source = new Uri("avares://OneWare.CologneChip/Styles/Icons.axaml")}; - Application.Current?.Resources.MergedDictionaries.Add(resourceInclude); - - containerProvider.Resolve().RegisterFileIcon("VsImageLib2019.SettingsFile16X", ".ccf"); + var applicationStateService = containerProvider.Resolve(); - containerProvider.Resolve().RegisterConstructContextMenu((x, l) => + applicationStateService.RegisterUrlLaunchAction("colognechip", x => { - if (x is [IProjectFile { Extension: ".ccf" } ccf]) + if (x is "/setup") { - if (ccf.Root is UniversalFpgaProjectRoot universalFpgaProjectRoot) + _ = Dispatcher.UIThread.InvokeAsync(async () => { - if (CologneChipSettingsHelper.GetConstraintFile(universalFpgaProjectRoot) == ccf.RelativePath) { - l.Add(new MenuItemModel("ccf") + var windowService = containerProvider.Resolve(); + var packageService = containerProvider.Resolve(); + var httpService = containerProvider.Resolve(); + var dataContext = new CologneChipSetupViewModel(httpService, packageService, windowService); + + try + { + await windowService.ShowDialogAsync(new CologneChipSetupView() { - Header = "Unset as Projects Constraint File", - Command = new AsyncRelayCommand(() => CologneChipSettingsHelper.UpdateProjectProperties(ccf)), + DataContext = dataContext, }); } - else + finally { - l.Add(new MenuItemModel("ccf") - { - Header = "Set as Projects Constraint File", - Command = new AsyncRelayCommand(() => CologneChipSettingsHelper.UpdateProjectProperties(ccf)), - }); + dataContext.Dispose(); } - } + }); } }); - - fpgaService.RegisterProjectEntryModification(x => - { - if (x.Root is not UniversalFpgaProjectRoot universalFpgaProjectRoot) return; - - if (x is IProjectFile file && CologneChipSettingsHelper.GetConstraintFile(universalFpgaProjectRoot) == - file.RelativePath) - { - x.Icon?.AddOverlay("CologneChip", "ForkAwesome.Check"); - } - else - { - x.Icon?.RemoveOverlay("CologneChip"); - } - }); - - containerProvider.Resolve().RegisterUiExtension("UniversalFpgaToolBar_DownloaderConfigurationExtension", new OneWareUiExtension(x => - { - if (x is not UniversalFpgaProjectRoot cm) return null; - return new CologneChipLoaderWindowExtensionView() - { - DataContext = containerProvider.Resolve((typeof(UniversalFpgaProjectRoot), cm)) - }; - })); - - - containerProvider.Resolve().RegisterToolchain(); - containerProvider.Resolve().RegisterLoader(); - - containerProvider.Resolve().Projects.CollectionChanged += CologneChipSettingsHelper.OnCollectionChanged; - containerProvider.Resolve().RegisterPackage(CologneChipConstantService.CologneChipPackage); - - settingsService.RegisterSetting("Tools", "CologneChip", CologneChipConstantService.CcPathSetting, - new FolderPathSetting("CologneChip Toolchain Path", defaultCologneChipPath, null, null, IsCologneChipPathValid)); - - settingsService.RegisterSetting("Tools", "CologneChip", CologneChipConstantService.ToolChainSettingsKey, - new ComboBoxSetting("Place & Route", CologneChipConstantService.ToolChainDefault, CologneChipConstantService.Toolchains.Cast().ToArray())); - - settingsService.RegisterSetting("Tools", "CologneChip", CologneChipConstantService.OpenFpgaLoaderSourceSettingsKey, - new ComboBoxSetting("openFPGALoader Source", CologneChipConstantService.OpenFpgaLoaderSourceDefault, CologneChipConstantService.BinarySources.Cast().ToArray())); - - settingsService.RegisterSetting("Tools", "CologneChip", CologneChipConstantService.YosysSourceSettingsKey, - new ComboBoxSetting("Yosys Source", CologneChipConstantService.OpenFpgaLoaderSourceDefault, CologneChipConstantService.BinarySources.Cast().ToArray())); - - settingsService.GetSettingObservable(CologneChipConstantService.CcPathSetting).Subscribe(x => - { - if (string.IsNullOrEmpty(x)) return; - - if (!IsCologneChipPathValid(x)) - { - containerProvider.Resolve().Warning("CologneChip Toolchain path invalid", null, false); - return; - } - - ContainerLocator.Container.Resolve().SetPath("CC_p_r", Path.Combine(x, "bin/p_r")); - - }); - - var projectSettingsService = containerProvider.Resolve(); - projectSettingsService.AddProjectSetting(new ProjectSettingBuilder() - .WithSetting(new ComboBoxSetting("Place & Route", CologneChipConstantService.ProjectOverrideValue, - CologneChipConstantService.ToolchainsProject.Cast().ToArray())) - .WithCategory("CologneChip") - .WithKey(CologneChipConstantService.ToolChainSettingsKey) - .Build()); - - projectSettingsService.AddProjectSetting(new ProjectSettingBuilder() - .WithSetting(new ComboBoxSetting("openFPGALoader Source", - CologneChipConstantService.ProjectOverrideValue, - CologneChipConstantService.BinarySourcesProject.Cast().ToArray())) - .WithCategory("CologneChip") - .WithKey(CologneChipConstantService.OpenFpgaLoaderSourceSettingsKey) - .Build()); - - projectSettingsService.AddProjectSetting(new ProjectSettingBuilder() - .WithSetting(new ComboBoxSetting("Yosys Source", - CologneChipConstantService.ProjectOverrideValue, - CologneChipConstantService.BinarySourcesProject.Cast().ToArray())) - .WithCategory("CologneChip") - .WithKey(CologneChipConstantService.YosysSourceSettingsKey) - .Build()); - - containerProvider.Resolve().RegisterSetting("Tools", "CologneChip", - CologneChipConstantService.CologneChipSettingsIgnoreGuiKey, new CheckBoxSetting("Ignore UI for HardwarePin Mapping", false)); - - containerProvider.Resolve().RegisterSetting("Tools", "CologneChip", - CologneChipConstantService.CologneChipSettingsIgnoreSynthExitCode, new CheckBoxSetting("Ignore an exit code not equal to 0 after the synthesis", false)); - - containerProvider.Resolve().RegisterSetting("Tools", "CologneChip", - CologneChipConstantService.AutoDownloadBinariesKey, new CheckBoxSetting("Auto Download Binaries", true)); - - containerProvider.Resolve().RegisterUiExtension("UniversalFpgaToolBar_CompileMenuExtension", - new OneWareUiExtension( - x => - { - if (x is not UniversalFpgaProjectRoot { Toolchain: "cologneChip" } root) return null; - - var name = root.Properties["Fpga"]?.ToString(); - var fpgaPackage = fpgaService.FpgaPackages.FirstOrDefault(obj => obj.Name == name); - var fpga = fpgaPackage?.LoadFpga(); - - return new StackPanel() - { - Orientation = Orientation.Vertical, - Children = - { - new MenuItem() - { - Header = "Run Synthesis", - Command = new AsyncRelayCommand(async () => - { - await cologneChipService.SynthAsync(root, new FpgaModel(fpga!)); - }, () => fpga != null) - }, - new MenuItem() - { - Header = "Run Place and Route", - Command = new AsyncRelayCommand(async () => - { - await cologneChipService.PrAsync(root, new FpgaModel(fpga!)); - }, () => fpga != null) - }, - new MenuItem() - { - Header = "Run Packing", - Command = new AsyncRelayCommand(async () => - { - await cologneChipService.PackAsync(root, new FpgaModel(fpga!)); - }, () => fpga != null) - }, - } - }; - })); - - } - - private static bool IsCologneChipPathValid(string path) - { - if (!Directory.Exists(path)) return false; - - if (!File.Exists(Path.Combine(path, "VERSION"))) return false; - if (!Directory.Exists(Path.Combine(path, "bin", "yosys"))) return false; - if (!Directory.Exists(Path.Combine(path, "bin", "p_r"))) return false; - if (!Directory.Exists(Path.Combine(path, "bin", "openFPGALoader"))) return false; - - return true; } } \ No newline at end of file diff --git a/src/OneWare.CologneChip/Services/CcCompileStrategyBase.cs b/src/OneWare.CologneChip/Services/CcCompileStrategyBase.cs index 509ca80..4399dfb 100644 --- a/src/OneWare.CologneChip/Services/CcCompileStrategyBase.cs +++ b/src/OneWare.CologneChip/Services/CcCompileStrategyBase.cs @@ -1,7 +1,6 @@ using Avalonia.Styling; using Microsoft.Extensions.Logging; using OneWare.Essentials.Models; -using OneWare.GhdlExtension.Services; namespace OneWare.CologneChip.Services; @@ -215,8 +214,9 @@ protected bool MaybeIgnoreSynthExitCode(bool success) if (project.TopEntity == null) throw new Exception($"Could not find matching operation for object type: {project.GetType().Name}"); + /* await ContainerLocator.Container.Resolve() - .SynthAsync(Path.Combine(project.FullPath, project.TopEntity), "verilog", "build"); + .SynthAsync(Path.Combine(project.FullPath, project.TopEntity), "verilog", "build");*/ return $"{topName}.v"; } diff --git a/src/OneWare.CologneChip/Templates/VerilogBlinkSimulationCcTemplate.cs b/src/OneWare.CologneChip/Templates/VerilogBlinkSimulationCcTemplate.cs new file mode 100644 index 0000000..4b0c943 --- /dev/null +++ b/src/OneWare.CologneChip/Templates/VerilogBlinkSimulationCcTemplate.cs @@ -0,0 +1,41 @@ +using Microsoft.Extensions.Logging; +using OneWare.Essentials.Services; +using OneWare.UniversalFpgaProjectSystem.Helpers; +using OneWare.UniversalFpgaProjectSystem.Models; +using OneWare.UniversalFpgaProjectSystem.Services; + +namespace OneWare.CologneChip.Templates; + +public class VerilogBlinkSimulationCcTemplate(ILogger logger, IMainDockService mainDockService) : IFpgaProjectTemplate +{ + public const string TemplateName = "Verilog Blink with Simulation (Cologne Chip)"; + + public void FillTemplate(UniversalFpgaProjectRoot root) + { + var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Assets", "Templates", "BlinkSimulationVerilog"); + + try + { + var name = root.Header.Replace(" ", ""); + TemplateHelper.CopyDirectoryAndReplaceString(path, root.FullPath, ("%PROJECTNAME%", name)); + var file = root.AddFile(name + ".v"); + + root.TopEntity = name; + + var file2 = root.AddFile(name + "_tb.v"); + + root.AddTestBench(file2.RelativePath); + + root.Board = "GateMate FPGA Evaluation Board"; + + _ = mainDockService.OpenFileAsync(file.FullPath); + _ = mainDockService.OpenFileAsync(file2.FullPath); + } + catch (Exception e) + { + logger.Error(e.Message, e); + } + } + + public string Name => TemplateName; +} \ No newline at end of file diff --git a/src/OneWare.CologneChip/ViewModels/CologneChipSetupViewModel.cs b/src/OneWare.CologneChip/ViewModels/CologneChipSetupViewModel.cs new file mode 100644 index 0000000..c4f1167 --- /dev/null +++ b/src/OneWare.CologneChip/ViewModels/CologneChipSetupViewModel.cs @@ -0,0 +1,266 @@ +using System.Collections.ObjectModel; +using System.Text.Json; +using System.Windows.Input; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using DynamicData.Binding; +using OneWare.CologneChip.Templates; +using OneWare.Essentials.Controls; +using OneWare.Essentials.Enums; +using OneWare.Essentials.Models; +using OneWare.Essentials.PackageManager; +using OneWare.Essentials.PackageManager.Compatibility; +using OneWare.Essentials.Services; +using OneWare.Essentials.ViewModels; +using OneWare.OssCadSuiteIntegration.Helpers; +using OneWare.UniversalFpgaProjectSystem; +using OneWare.UniversalFpgaProjectSystem.ViewModels; +using OneWare.UniversalFpgaProjectSystem.Views; + +namespace OneWare.CologneChip.ViewModels; + +public sealed class CologneChipSetupViewModel : FlexibleWindowViewModelBase, IDisposable +{ + private readonly IHttpService _httpService; + private readonly IPackageService _packageService; + private readonly IWindowService _windowService; + private readonly JsonSerializerOptions _serializerOptions = new() + { + PropertyNameCaseInsensitive = true, + AllowTrailingCommas = true, + WriteIndented = true + }; + + private const string GateMateDataUrl = + "https://raw.githubusercontent.com/swittlich/OneWare.GateMate/main/oneware-package.json"; + + private CancellationTokenSource? _installCts; + + public CologneChipSetupViewModel(IHttpService httpService, IPackageService packageService, IWindowService windowService) + { + _httpService = httpService; + _packageService = packageService; + _windowService = windowService; + CancelCommand = new RelayCommand(Cancel); + InstallCommand = new AsyncRelayCommand(InstallAsync); + AttachedToVisualTreeCommand = new AsyncRelayCommand(AttachedToVisualTreeAsync); + _packageService.PackagesUpdated += PackagesUpdated; + } + + public string Header { get; } = "Welcome to Cologne Chip"; + public string Description { get; } = "Get started with One Ware Studio and the Cologne Chip plugin by installing the required packages."; + public ObservableCollection RequiredPackages { get; } = []; + + public ICommand AttachedToVisualTreeCommand { get; } + public ICommand CancelCommand { get; } + public ICommand InstallCommand { get; } + + public bool IsLoading + { + get; + set => SetProperty(ref field, value); + } = true; + public bool LoadingFailed + { + get; + set => SetProperty(ref field, value); + } + public bool IsInstalling + { + get; + set => SetProperty(ref field, value); + } + + public bool PackagesAlreadyInstalled + { + get; + set => SetProperty(ref field, value); + } + public bool CanInstall => !IsLoading && !LoadingFailed; + + public override bool OnWindowClosing(FlexibleWindow window) + { + _installCts?.Cancel(); + return base.OnWindowClosing(window); + } + + public void Dispose() + { + _packageService.PackagesUpdated -= PackagesUpdated; + _installCts?.Dispose(); + } + + private void PackagesUpdated(object? sender, EventArgs e) + { + _ = InitializeAsync(); + } + private async Task InitializeAsync() + { + IsLoading = true; + + try + { + var downloadManifest = await _httpService.DownloadTextAsync(GateMateDataUrl); + var package = JsonSerializer.Deserialize(downloadManifest!, _serializerOptions); + if (package == null) + throw new Exception(); + + List requiredPackages = [package, OssCadSuiteHelper.OssCadPackage]; + foreach (var pk in requiredPackages) + { + PackageViewModel vm = new(pk, _packageService.Packages[pk.Id!]); + await vm.InitializeAsync(_packageService); + RequiredPackages.Add(vm); + } + PackagesAlreadyInstalled = RequiredPackages.All(x => x.State.Status is PackageStatus.Installed); + } + catch (Exception ex) + { + LoadingFailed = true; + } + finally + { + IsLoading = false; + OnPropertyChanged(nameof(CanInstall)); + } + } + + private async Task AttachedToVisualTreeAsync() + { + // timeout, wait 10s to display an error + for (int i = 0; i < 10; i++) + { + if (!IsLoading || LoadingFailed) + return; + + await Task.Delay(1000); + } + IsLoading = false; + LoadingFailed = true; + } + + private void Cancel(object? parameter) + { + if (parameter is not Control control) + return; + + Window? wd = TopLevel.GetTopLevel(control) as Window; + wd?.Close(); + } + private async Task InstallAsync(object? parameter) + { + if (parameter is not Control control) + return; + + if (IsInstalling) + return; + + _installCts = new CancellationTokenSource(); + IsInstalling = true; + + bool success = true; + bool cancellationRequested = false; + + try + { + foreach (var package in RequiredPackages) + { + _installCts.Token.ThrowIfCancellationRequested(); + var result = await package.InstallAsync(_packageService, _installCts.Token); + + success = result.Status is PackageInstallResultReason.AlreadyInstalled + or PackageInstallResultReason.Installed; + + if (!success) + { + cancellationRequested = _installCts.Token.IsCancellationRequested; + break; + } + } + } + catch (OperationCanceledException) + { + cancellationRequested = true; + success = false; + } + catch + { + success = false; + } + finally + { + IsInstalling = false; + _installCts?.Dispose(); + _installCts = null; + } + + if (!success && !cancellationRequested) + { + await ContainerLocator.Container.Resolve().ShowMessageAsync("Installation failed", + "Please try again later or check for OneWare Studio updates", MessageBoxIcon.Error); + } + + Window? wd = TopLevel.GetTopLevel(control) as Window; + wd?.Close(); + + // open the fpga template dialog with the cologne chip template as default + var dataContext = ContainerLocator.Container.Resolve(); + dataContext.SettingsCollection.SettingModels + .OfType() + .First(x => string.Equals(x.Title, "template", StringComparison.OrdinalIgnoreCase)).Value + = VerilogBlinkSimulationCcTemplate.TemplateName; + await _windowService.ShowDialogAsync(new UniversalFpgaProjectCreatorView + { + DataContext = dataContext + }); + } +} + +public sealed class PackageViewModel : ObservableObject +{ + public PackageViewModel(Package package, IPackageState packageState) + { + Package = package; + State = packageState; + } + public Package Package { get; } + public IPackageState State { get; } + + public IImage? Image + { + get; + private set => SetProperty(ref field, value); + } + + public int Progress + { + get; + set => SetProperty(ref field, value); + } + + public async Task InitializeAsync(IPackageService packageService) + { + Image = await packageService.DownloadPackageIconAsync(Package); + } + + public async Task InstallAsync(IPackageService packageService, CancellationToken cancellationToken) + { + IDisposable? subscription = null; + + try + { + subscription = State + .WhenValueChanged(x => x.Progress) + .Subscribe(x => Progress = (int)(x * 100)); + + return await packageService.InstallAsync(Package, null, false, false, cancellationToken); + } + finally + { + subscription?.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/OneWare.CologneChip/Views/CologneChipSetupView.axaml b/src/OneWare.CologneChip/Views/CologneChipSetupView.axaml new file mode 100644 index 0000000..181bd17 --- /dev/null +++ b/src/OneWare.CologneChip/Views/CologneChipSetupView.axaml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/OneWare.CologneChip/Views/CologneChipSetupView.axaml.cs b/src/OneWare.CologneChip/Views/CologneChipSetupView.axaml.cs new file mode 100644 index 0000000..768cf26 --- /dev/null +++ b/src/OneWare.CologneChip/Views/CologneChipSetupView.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using OneWare.Essentials.Controls; + +namespace OneWare.CologneChip.Views; + +public partial class CologneChipSetupView : FlexibleWindow +{ + public CologneChipSetupView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/tests/OneWare.CologneChip.UnitTests/OneWare.CologneChip.UnitTests.csproj b/tests/OneWare.CologneChip.UnitTests/OneWare.CologneChip.UnitTests.csproj index 4a49bfe..8ba6235 100644 --- a/tests/OneWare.CologneChip.UnitTests/OneWare.CologneChip.UnitTests.csproj +++ b/tests/OneWare.CologneChip.UnitTests/OneWare.CologneChip.UnitTests.csproj @@ -16,6 +16,7 @@ +