├── .gitattributes ├── .github └── workflows │ ├── comment-cherry-pick.yml │ ├── pull.yml │ └── update-downloads.yml ├── .gitignore ├── .jenkins ├── deploy.groovy ├── release.groovy └── staging.groovy ├── CONTRIBUTING.md ├── DISCLAIMER ├── LICENSE ├── PRIVACY_POLICY.md ├── README.md ├── SharedFiles ├── Commands.vsct └── Twinpack.png ├── THIRD_PARTY_LICENSES.txt ├── Twinpack.15 ├── Properties │ └── AssemblyInfo.cs ├── Twinpack.15.csproj └── Twinpack.15.snk ├── Twinpack.17 ├── Properties │ └── AssemblyInfo.cs ├── Twinpack.17.csproj └── Twinpack.17.snk ├── Twinpack.Net └── Twinpack.Net.csproj ├── Twinpack.NodeJs ├── package.json └── src │ ├── example.js │ └── twinpack.js ├── Twinpack.iss ├── Twinpack.sln ├── TwinpackCli.Net ├── Commands │ ├── AbstractCommand.cs │ ├── AddCommand.cs │ ├── ConfigCommand.cs │ ├── DownloadCommand.cs │ ├── ListCommand.cs │ ├── PullCommand.cs │ ├── PushCommand.cs │ ├── RemoveCommand.cs │ ├── RestoreCommand.cs │ ├── SearchCommand.cs │ ├── SetVersionCommand.cs │ └── UpdateCommand.cs ├── Program.cs ├── Properties │ └── launchSettings.json └── TwinpackCli.Net.csproj ├── TwinpackCli.Windows ├── App.config ├── Commands │ ├── AbstractCommand.cs │ ├── AddCommand.cs │ ├── ConfigCommand.cs │ ├── DownloadCommand.cs │ ├── ListCommand.cs │ ├── PullCommand.cs │ ├── PushCommand.cs │ ├── RemoveCommand.cs │ ├── RestoreCommand.cs │ ├── SearchCommand.cs │ ├── SetVersionCommand.cs │ └── UpdateCommand.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── TwinpackCli.Windows.csproj ├── TwinpackCore ├── Commands │ ├── AbstractCommand.cs │ ├── AddCommand.cs │ ├── ConfigCommand.cs │ ├── DownloadCommand.cs │ ├── ListCommand.cs │ ├── PullCommand.cs │ ├── PushCommand.cs │ ├── RemoveCommand.cs │ ├── RestoreCommand.cs │ ├── SearchCommand.cs │ ├── SetVersionCommand.cs │ └── UpdateCommand.cs ├── Configuration │ ├── Config.cs │ ├── ConfigFactory.cs │ └── ConfigPlcFactory.cs ├── Core │ ├── AutomationInterface.cs │ ├── AutomationInterface4024.cs │ ├── AutomationInterfaceHeadless.cs │ ├── CancelableTask.cs │ ├── IAutomationInterface.cs │ ├── MessageFilter.cs │ ├── PackageServerCollection.cs │ ├── SynchronizationContextAwaiter.cs │ ├── TwinpackService.cs │ └── VisualStudio.cs ├── DirectoryExtension.cs ├── Exceptions │ └── Exceptions.cs ├── LibraryReader.cs ├── Models │ ├── PackageItem.cs │ ├── Plc.cs │ ├── PlcLibrary.cs │ ├── PlcVersion.cs │ ├── Project.cs │ ├── Solution.cs │ └── SourceRepositories.cs ├── Protocol │ ├── Api.cs │ ├── Authentication.cs │ ├── IPackageServer.cs │ ├── Native │ │ ├── CachedHttpClient.cs │ │ └── TwinpackServer.cs │ ├── Nuget │ │ ├── BeckhoffServer.cs │ │ └── NugetServer.cs │ ├── PackagingServer.cs │ ├── PackagingServerFactory.cs │ └── PackagingServerRegistry.cs ├── TwinpackCore.projitems ├── TwinpackCore.shproj └── app.config ├── TwinpackRegistry ├── App.config ├── Commands │ ├── Command.cs │ ├── DumpCommand.cs │ ├── PullCommand.cs │ └── UpdateDownloads.cs ├── IconUtils.cs ├── NLog.dll.nlog ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── TwinpackRegistry.cs ├── TwinpackRegistry.csproj └── packages.config ├── TwinpackTests ├── AddPlcLibraryOptionsTest.cs ├── ConfigFactoryTest.cs ├── LibraryReaderTest.cs ├── PackageServerCollectionTest.cs ├── Properties │ └── AssemblyInfo.cs ├── SystemTest.cs ├── TwinpackServiceTest.cs ├── TwinpackTests.csproj ├── assets │ ├── Flat.libcat.xml │ ├── LibCat1_All_selected.library │ ├── LibCat4P_1_selected.library │ ├── LibCat4_All_selected.library │ ├── LibCat4_MyParent_selected.library │ ├── TestSolution │ │ ├── .Zeugwerk │ │ │ └── libraries │ │ │ │ └── TC3.1 │ │ │ │ ├── PlcLibrary1_1.2.3.3.library │ │ │ │ └── PlcLibrary1_1.2.3.4.library │ │ ├── TestLibraryProject │ │ │ ├── TestLibraryProject.tsproj │ │ │ └── _Boot │ │ │ │ └── TargetDescription.xml │ │ ├── TestProject │ │ │ ├── Plc1 │ │ │ │ ├── POUs │ │ │ │ │ └── MAIN.TcPOU │ │ │ │ ├── Plc1.plcproj │ │ │ │ ├── PlcTask.TcTTO │ │ │ │ └── _Libraries │ │ │ │ │ └── beckhoff automation gmbh │ │ │ │ │ ├── tc2_standard │ │ │ │ │ └── 3.3.3.0 │ │ │ │ │ │ └── tc2_standard.compiled-library │ │ │ │ │ ├── tc2_system │ │ │ │ │ └── 3.6.2.0 │ │ │ │ │ │ └── tc2_system.compiled-library │ │ │ │ │ └── tc3_module │ │ │ │ │ └── 3.3.23.0 │ │ │ │ │ └── tc3_module.compiled-library │ │ │ ├── TestProject.tsproj │ │ │ └── _Boot │ │ │ │ └── TargetDescription.xml │ │ ├── TestProject2 │ │ │ ├── PlcLibrary1 │ │ │ │ ├── POUs │ │ │ │ │ └── MAIN.TcPOU │ │ │ │ ├── PlcLibrary1.plcproj │ │ │ │ └── _Libraries │ │ │ │ │ └── beckhoff automation gmbh │ │ │ │ │ ├── tc2_standard │ │ │ │ │ └── 3.3.3.0 │ │ │ │ │ │ └── tc2_standard.compiled-library │ │ │ │ │ ├── tc2_system │ │ │ │ │ └── 3.6.2.0 │ │ │ │ │ │ └── tc2_system.compiled-library │ │ │ │ │ └── tc3_module │ │ │ │ │ └── 3.3.23.0 │ │ │ │ │ └── tc3_module.compiled-library │ │ │ └── TestProject2.tspproj │ │ └── TestSolution.sln │ ├── Untitled1_4024_35_minimal_infos_123.library │ ├── Untitled1_4024_35_minimal_infos_1234.library │ ├── Untitled1_4024_35_minimal_infos_123_n.library │ ├── Untitled1_4024_35_minimal_infos_123_np.library │ ├── Untitled1_4024_35_minimal_infos_123_npa.library │ ├── Untitled1_4024_35_minimal_infos_123_npad.library │ ├── Untitled1_4024_35_minimal_infos_123_rnpad.library │ ├── Untitled1_4024_53_minimal_infos_12.library │ ├── Untitled1_4024_53_minimal_infos_123.library │ ├── Untitled1_4024_53_minimal_infos_1234.library │ ├── Untitled1_4026_00_minimal_infos_123.library │ ├── Untitled1_libcat_child.library │ ├── Untitled1_libcat_child2.library │ ├── Untitled1_libcat_onlychild_selected.library │ ├── Untitled2.libcat.xml │ ├── Untitled2_libcat.library │ ├── Untitled2_libcat_1234.library │ ├── Untitled2_libcat_sub.library │ ├── Untitled2_minimal_infos_libcat_1234.library │ ├── Untitled2_minimal_infos_libcat_mine_1234.library │ ├── Untitled2_minimal_infos_libcat_mine_theirs_1234.library │ └── Untitled2_minimal_infos_libcat_mine_theirs_yours_1234.library └── mocks │ └── PackageServerMock.cs ├── TwinpackVsix.15 ├── Properties │ └── AssemblyInfo.cs ├── TwinpackVsix.15.csproj ├── packages.config └── source.extension.vsixmanifest ├── TwinpackVsix.17 ├── Properties │ └── AssemblyInfo.cs ├── TwinpackVsix.17.csproj └── source.extension.vsixmanifest ├── TwinpackVsixShared ├── Commands │ ├── CatalogCommand.cs │ ├── Command.cs │ ├── ICommand.cs │ ├── ModifyCommand.cs │ ├── PublishCommand.cs │ └── TwinpackMenuCommand.cs ├── Dialogs │ ├── CatalogWindow.xaml │ ├── CatalogWindow.xaml.cs │ ├── Converters │ │ ├── BooleanToBorderThicknessConverter.cs │ │ ├── BooleanToInverseBooleanConverter.cs │ │ ├── BooleanToVisibilityConverter.cs │ │ ├── CatalogIconUrlVisibilityConverter.cs │ │ ├── CatalogIconVisibilityConverter.cs │ │ ├── CatalogNoIconVisibilityConverter.cs │ │ ├── IntToVisibilityConverter.cs │ │ ├── NullToVisibilityConverter.cs │ │ ├── PackageVersionToBooleanConverter.cs │ │ ├── UrlToImageConverter.cs │ │ ├── VersionToStringConverter.cs │ │ └── VisibilityToInverseVisibilityConverter.cs │ ├── InputDialog.xaml │ ├── InputDialog.xaml.cs │ ├── LicenseWindow.xaml │ ├── LicenseWindow.xaml.cs │ ├── PackageVersionWindow.xaml │ ├── PackageVersionWindow.xaml.cs │ ├── PackagingServerDialog.cs │ └── PackagingServerDialog.xaml ├── IconCache.cs ├── Logger │ └── VsOutputWindowTarget.cs ├── PackagingServer.cs ├── Panels │ └── CatalogPane.cs ├── TwinpackPackage.cs ├── TwinpackVsixShared.projitems ├── TwinpackVsixShared.shproj ├── VsixManifest.cs └── app.config ├── Zeugwerk.bmp ├── Zeugwerk.ico ├── documentation ├── configuration.md ├── library_reader.md └── twinpack-server.md ├── images ├── twinpack_catalog.png ├── twinpack_contextmenu.png ├── twinpack_contextmenu2.png ├── twinpack_package_servers.png └── twinpack_publish.png └── references.json /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | *.tt text=crlf 6 | 7 | ############################################################################### 8 | # Set default behavior for command prompt diff. 9 | # 10 | # This is need for earlier builds of msysgit that does not have it on by 11 | # default for csharp files. 12 | # Note: This is only used by command line 13 | ############################################################################### 14 | #*.cs diff=csharp 15 | 16 | ############################################################################### 17 | # Set the merge driver for project and solution files 18 | # 19 | # Merging from the command prompt will add diff markers to the files if there 20 | # are conflicts (Merging from VS is not affected by the settings below, in VS 21 | # the diff markers are never inserted). Diff markers may cause the following 22 | # file extensions to fail to load in VS. An alternative would be to treat 23 | # these files as binary and thus will always conflict and require user 24 | # intervention with every merge. To do so, just uncomment the entries below 25 | ############################################################################### 26 | #*.sln merge=binary 27 | #*.csproj merge=binary 28 | #*.vbproj merge=binary 29 | #*.vcxproj merge=binary 30 | #*.vcproj merge=binary 31 | #*.dbproj merge=binary 32 | #*.fsproj merge=binary 33 | #*.lsproj merge=binary 34 | #*.wixproj merge=binary 35 | #*.modelproj merge=binary 36 | #*.sqlproj merge=binary 37 | #*.wwaproj merge=binary 38 | 39 | ############################################################################### 40 | # behavior for image files 41 | # 42 | # image files are treated as binary by default. 43 | ############################################################################### 44 | #*.jpg binary 45 | #*.png binary 46 | #*.gif binary 47 | 48 | ############################################################################### 49 | # diff behavior for common document formats 50 | # 51 | # Convert binary document formats to text before diffing them. This feature 52 | # is only available from the command line. Turn it on by uncommenting the 53 | # entries below. 54 | ############################################################################### 55 | #*.doc diff=astextplain 56 | #*.DOC diff=astextplain 57 | #*.docx diff=astextplain 58 | #*.DOCX diff=astextplain 59 | #*.dot diff=astextplain 60 | #*.DOT diff=astextplain 61 | #*.pdf diff=astextplain 62 | #*.PDF diff=astextplain 63 | #*.rtf diff=astextplain 64 | #*.RTF diff=astextplain 65 | -------------------------------------------------------------------------------- /.github/workflows/comment-cherry-pick.yml: -------------------------------------------------------------------------------- 1 | name: Cherry Pick On Comment 2 | on: 3 | issue_comment: 4 | types: [created] 5 | jobs: 6 | cherry-pick: 7 | name: Cherry Pick 8 | if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/cherry-pick') 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout the latest code 12 | uses: actions/checkout@v2 13 | with: 14 | token: ${{ secrets.GITHUB_TOKEN }} 15 | fetch-depth: 0 # otherwise, you will fail to push refs to dest repo 16 | - name: Automatic Cherry Pick 17 | uses: Zeugwerk/cherry-pick-action@v1 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | -------------------------------------------------------------------------------- /.github/workflows/pull.yml: -------------------------------------------------------------------------------- 1 | name: Pull 2 | on: 3 | schedule: 4 | - cron: "0 13 * * *" 5 | workflow_dispatch: 6 | jobs: 7 | Pull: 8 | runs-on: windows-latest 9 | steps: 10 | - run: | 11 | curl.exe -s -o twinpack-registry-latest.tar.gz https://zeugwerk.dev/Tools/twinpack-registry-latest.tar.gz 12 | tar -xf twinpack-registry-latest.tar.gz 13 | ./twinpack-registry.exe pull --username "${{ secrets.ACTIONS_ZGWK_USERNAME }}" --password "${{ secrets.ACTIONS_ZGWK_PASSWORD }}" --beckhoff-username "${{ secrets.ACTIONS_BECKHOFF_USERNAME }}" --beckhoff-password "${{ secrets.ACTIONS_BECKHOFF_PASSWORD }}" --token "${{ secrets.GH_TOKEN }}" 14 | shell: bash 15 | -------------------------------------------------------------------------------- /.github/workflows/update-downloads.yml: -------------------------------------------------------------------------------- 1 | name: Update Downloads 2 | on: 3 | schedule: 4 | - cron: "0 1 * * 0" 5 | workflow_dispatch: 6 | jobs: 7 | update_downloads: 8 | runs-on: windows-latest 9 | steps: 10 | - run: | 11 | curl.exe -s -o twinpack-registry-latest.tar.gz https://zeugwerk.dev/Tools/twinpack-registry-latest.tar.gz 12 | tar -xf twinpack-registry-latest.tar.gz 13 | ./twinpack-registry.exe update-downloads --username "${{ secrets.ACTIONS_ZGWK_USERNAME }}" --password "${{ secrets.ACTIONS_ZGWK_PASSWORD }}" --token "${{ secrets.GH_TOKEN }}" 14 | shell: bash 15 | -------------------------------------------------------------------------------- /.jenkins/deploy.groovy: -------------------------------------------------------------------------------- 1 | deploy() 2 | -------------------------------------------------------------------------------- /.jenkins/release.groovy: -------------------------------------------------------------------------------- 1 | release() 2 | -------------------------------------------------------------------------------- /.jenkins/staging.groovy: -------------------------------------------------------------------------------- 1 | staging() 2 | -------------------------------------------------------------------------------- /DISCLAIMER: -------------------------------------------------------------------------------- 1 | Twinpack is a tool developed by Zeugwerk GmbH to facilitate the distribution and management of packages within the Twincat system. 2 | Zeugwerk GmbH acknowledge that Twinpack allows users to distribute their own packages through its platform. 3 | However, Zeugwerk GmbH explicitly states that it does not assume responsibility for any packages distributed by users through Twinpack. 4 | 5 | While Zeugwerk endeavors to provide a reliable and secure platform, Twinpack operates on a user-driven model where packages are 6 | created and shared by individual users. Zeugwerk GmbH does not perform extensive verification or validation of these packages, 7 | nor does it endorse or guarantee the quality, safety, or suitability of any package available through Twinpack. 8 | 9 | Users of Twinpack must exercise their own discretion and undertake thorough evaluation and testing of any packages they 10 | choose to download, install, or use. Zeugwerk GmbH shall not be held liable for any damages, losses, or adverse consequences 11 | arising from the use of packages distributed through Twinpack, including but not limited to software malfunctions, 12 | data corruption, system failures, or any other issues that may arise. 13 | 14 | By using the Twinpack and accessing packages distributed through it, users accept and agree to assume all risks associated 15 | with the use of such packages. It is strongly recommended that users employ appropriate security measures, including but not 16 | limited to utilizing antivirus software, regularly backing up data, and ensuring compatibility with their specific system requirements. 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Twinpack 2 | 3 | The Twinpack Package Manager is a powerful and user-friendly package management tool for TwinCAT libraries. It is designed to empower the TwinCAT community by enabling sharing and distribution of libraries. It acts as a versatile platform similarly to NuGet (but with a PLC touch), allowing users to efficiently manage and deploy their custom-built modules. 4 | 5 |
6 | 7 |
8 | 9 | Twinpack currently supports the following package sources 10 | 11 | 1. [Twinpack server](https://zeugwerk.dev/Zeugwerk_Framework/Documentation/snapshot/userguide/twinpack/twinpack_quickstart.html#using-a-package): Zeugwerk hosts open source libraries for and from everyone who is interested for free. Additionally this server type supports special features for Zeugwerk customers like feature branches. 12 | 1. [Nuget Server](https://zeugwerk.dev/Zeugwerk_Framework/Documentation/snapshot/userguide/twinpack/twinpack_nuget_package.html): Everyone can host his own NuGet server and create packages to consume them from the on premises server. 13 | 1. [Beckhoff Library Repository](https://zeugwerk.dev/Zeugwerk_Framework/Documentation/snapshot/userguide/twinpack/twinpack_beckhoffrepository.html): Since TwinCAT 4026, Beckhoff provides a public repository for their libraries. Twinpack can connect this repositories and integrate them seamlessly into the IDE. 14 | 15 | The full project documentation, including a quickstart guide for **Twinpack**, is available at the following at [Project Documentation](https://zeugwerk.dev/Zeugwerk_Framework/Documentation/snapshot/userguide/twinpack/twinpack_quickstart.html) 16 | 17 | Visit the link to get detailed instructions on setting up and using the project. 18 | 19 | ## Quicklinks 20 | - [Download latest Release](https://github.com/Zeugwerk/Twinpack/releases/latest) 21 | - [Twinpack-Registry](https://github.com/Zeugwerk/Twinpack-Registry) for automatic publishing of your library on Twinpack by "pulling" them from your GitHub releases 22 | - [Registration](https://zeugwerk.dev/wp-login.php?action=register), only needed to "push" package, i.e. if you want to publish packages manually or with CI 23 | - [Contact us](mailto:info@zeugwerk.at) 24 | 25 | 26 | ## Further information 27 | 28 | 🌟 Make sure to follow this project by leaving a star or simply follow us, to always get notified if a newer version of Twinpack is released. 29 | 30 | 📺 We have also created a short introductionary video on how to install and use Twinpack 31 | - [Share TwinCAT libraries with Twinpack](https://youtu.be/xvJG9BRN610?si=RMMIPcdtMAoHkyGW) 32 | -------------------------------------------------------------------------------- /SharedFiles/Twinpack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/SharedFiles/Twinpack.png -------------------------------------------------------------------------------- /THIRD_PARTY_LICENSES.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/THIRD_PARTY_LICENSES.txt -------------------------------------------------------------------------------- /Twinpack.15/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // Allgemeine Informationen über eine Assembly werden über die folgenden 6 | // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, 7 | // die einer Assembly zugeordnet sind. 8 | [assembly: AssemblyTitle("Twinpack")] 9 | [assembly: AssemblyDescription("Package manager for TwinCAT libraries")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Zeugwerk GmbH")] 12 | [assembly: AssemblyProduct("Twinpack")] 13 | [assembly: AssemblyCopyright("Copyright © Zeugwerk GmbH 2023")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | [assembly: AssemblyKeyFile("Twinpack.15.snk")] 17 | 18 | // Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly 19 | // für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von 20 | // COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. 21 | [assembly: ComVisible(false)] 22 | 23 | // Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird 24 | [assembly: Guid("3d96f91e-afe2-4b62-bbcb-b2546aaa932e")] 25 | 26 | // Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: 27 | // 28 | // Hauptversion 29 | // Nebenversion 30 | // Buildnummer 31 | // Revision 32 | // 33 | // Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden, 34 | // indem Sie "*" wie unten gezeigt eingeben: 35 | // [assembly: AssemblyVersion("1.0.*")] 36 | //[assembly: AssemblyVersion("1.0.0.0")] 37 | //[assembly: AssemblyFileVersion("1.0.0.0")] 38 | -------------------------------------------------------------------------------- /Twinpack.15/Twinpack.15.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/Twinpack.15/Twinpack.15.snk -------------------------------------------------------------------------------- /Twinpack.17/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // Allgemeine Informationen über eine Assembly werden über die folgenden 6 | // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, 7 | // die einer Assembly zugeordnet sind. 8 | [assembly: AssemblyTitle("Twinpack.17")] 9 | [assembly: AssemblyDescription("Package manager for TwinCAT libraries")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Zeugwerk GmbH")] 12 | [assembly: AssemblyProduct("Twinpack.17")] 13 | [assembly: AssemblyCopyright("Copyright © Zeugwerk GmbH 2023")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | [assembly: AssemblyKeyFile("Twinpack.17.snk")] 17 | 18 | // Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly 19 | // für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von 20 | // COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. 21 | [assembly: ComVisible(false)] 22 | 23 | // Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird 24 | [assembly: Guid("9607f8b7-e76a-452c-87f0-e804bae4e92f")] 25 | 26 | // Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: 27 | // 28 | // Hauptversion 29 | // Nebenversion 30 | // Buildnummer 31 | // Revision 32 | // 33 | // Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden, 34 | // indem Sie "*" wie unten gezeigt eingeben: 35 | // [assembly: AssemblyVersion("1.0.*")] 36 | //[assembly: AssemblyVersion("1.0.0.0")] 37 | //[assembly: AssemblyFileVersion("1.0.0.0")] 38 | -------------------------------------------------------------------------------- /Twinpack.17/Twinpack.17.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/Twinpack.17/Twinpack.17.snk -------------------------------------------------------------------------------- /Twinpack.Net/Twinpack.Net.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Twinpack.NodeJs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "twinpack.nodejs", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "keywords": [], 9 | "author": "", 10 | "license": "ISC", 11 | "description": "" 12 | } 13 | -------------------------------------------------------------------------------- /Twinpack.NodeJs/src/example.js: -------------------------------------------------------------------------------- 1 | const Twinpack = require('./twinpack'); 2 | 3 | // Initialize the Twinpack library 4 | const twinpack = new Twinpack(); 5 | 6 | async function runExamples() { 7 | try { 8 | console.log('\nRunning Config Command...'); 9 | const configResults = await twinpack.config({ 10 | types: [], 11 | sources: [], 12 | names: [], 13 | usernames: [], 14 | passwords: [], 15 | purge: true, 16 | reset: true 17 | }); 18 | console.log('Config Results:', configResults); 19 | 20 | console.log('Running Search Command...'); 21 | const searchResults = await twinpack.search({ searchTerm: 'ZCore', take: 5 }); 22 | console.log('Search Results:', searchResults); 23 | } catch (error) { 24 | console.error('Error:', error.message); 25 | } 26 | } 27 | 28 | runExamples(); -------------------------------------------------------------------------------- /Twinpack.NodeJs/src/twinpack.js: -------------------------------------------------------------------------------- 1 | const { exec } = require('child_process'); 2 | 3 | class Twinpack { 4 | constructor(executablePath = 'dotnet ../TwinpackCli.Net/bin/Debug/net8.0/twinpack.dll') { 5 | this.executablePath = executablePath; 6 | } 7 | 8 | /** 9 | * Run a command with specified arguments and parse JSON output. 10 | * @param {string} command The command to run. 11 | * @param {Object} args An object of arguments to pass. 12 | * @returns {Promise} Parsed JSON output from the command. 13 | */ 14 | runCommand(command, args = {}) { 15 | const argsArray = []; 16 | argsArray.push(command); 17 | 18 | // Add the --json flag to get JSON output 19 | argsArray.push('--json-output'); 20 | 21 | // Convert args object to command line arguments 22 | for (const [key, value] of Object.entries(args)) { 23 | if (Array.isArray(value)) { 24 | value.forEach(val => argsArray.push(`--${key}`, val)); 25 | } else if (typeof value === 'boolean') { 26 | if (value) argsArray.push(`--${key}`); 27 | } else if (value !== null && value !== undefined) { 28 | argsArray.push(`--${key}`, value); 29 | } 30 | } 31 | 32 | const commandStr = `${this.executablePath} ${argsArray.join(' ')}`; 33 | return new Promise((resolve, reject) => { 34 | exec(commandStr, (error, stdout, stderr) => { 35 | if (error) { 36 | reject(new Error(`${stdout} ${stderr}`)); 37 | } else { 38 | try { 39 | const json = JSON.parse(stdout); 40 | resolve(json); 41 | } catch (parseError) { 42 | reject(new Error(`Failed to parse JSON output: ${parseError.message}`)); 43 | } 44 | } 45 | }); 46 | }); 47 | } 48 | 49 | /** 50 | * Search for packages with optional filters. 51 | * @param {string} searchTerm Term to filter packages by name or keyword. 52 | * @param {number} take Limit the number of results. 53 | * @returns {Promise} JSON object with search results. 54 | */ 55 | search({ searchTerm = null, take = null } = {}) { 56 | return this.runCommand('search', { 'search-term': searchTerm, take }); 57 | } 58 | 59 | /** 60 | * Configure package servers. 61 | * @param {Object} options Configuration options. 62 | * @returns {Promise} JSON object with the configuration result. 63 | */ 64 | config({ types = [], sources = [], names = [], usernames = [], passwords = [], purge = false, reset = false } = {}) { 65 | return this.runCommand('config', { 66 | type: types, 67 | source: sources, 68 | name: names, 69 | username: usernames, 70 | password: passwords, 71 | purge, 72 | reset 73 | }); 74 | } 75 | } 76 | 77 | module.exports = Twinpack; 78 | -------------------------------------------------------------------------------- /TwinpackCli.Net/Commands/AddCommand.cs: -------------------------------------------------------------------------------- 1 | using Twinpack.Core; 2 | using System.ComponentModel; 3 | using Spectre.Console.Cli; 4 | 5 | namespace Twinpack.Commands 6 | { 7 | [Description("Ensure that the configuration file is properly set up using 'twinpack.exe config' before executing this command.")] 8 | public class AddCommand : AbstractCommand 9 | { 10 | public class Settings : AbstractSettings 11 | { 12 | [CommandOption("--project ")] 13 | [Description("The name of the project where the packages should be added.")] 14 | public string ProjectName { get; set; } 15 | 16 | [CommandOption("--plc ")] 17 | [Description("The name of the PLC where the packages will be added.")] 18 | public string PlcName { get; set; } 19 | 20 | [CommandOption("--package")] 21 | [Description("Specifies the package(s) to be added.")] 22 | public string[] Packages { get; set; } 23 | 24 | [CommandOption("--version")] 25 | [Description("Defines the version(s) of the specified package(s). If omitted, Twinpack will automatically add the latest available version.")] 26 | public string[] Versions { get; set; } 27 | 28 | [CommandOption("--branch")] 29 | [Description("Specifies the branch(es) of the package(s). If not provided, Twinpack will determine the appropriate branch automatically, i.e. 'main'")] 30 | public string[] Branches { get; set; } 31 | 32 | [CommandOption("--target")] 33 | [Description("Indicates the target(s) for the package(s). If omitted, Twinpack will select the appropriate target(s) automatically, i.e. 'TC3.1'")] 34 | public string[] Targets { get; set; } 35 | 36 | [CommandOption("--configuration")] 37 | [Description("Specifies the configuration(s) for the package(s). If not specified, Twinpack will handle the configuration automatically, i.e. 'Release'")] 38 | public string[] Configurations { get; set; } 39 | 40 | [CommandOption("--add-dependencies")] 41 | [Description("If true, package dependencies will be added automatically to the PLC references.")] 42 | public bool AddDependencies { get; set; } 43 | 44 | [CommandOption("--force-download")] 45 | [Description("Forces the download of package(s) even if they are already available on the system.")] 46 | public bool ForceDownload { get; set; } 47 | 48 | [CommandOption("--headed")] 49 | [Description("Enables the use of the Beckhoff Automation Interface, which is required for installing and/or uninstalling packages on the target. In 'headless' mode, install operations have to be performed by Beckhoff's 'RepTool.exe'. Defaults to false")] 50 | public bool Headed { get; set; } 51 | } 52 | 53 | public override int Execute(CommandContext context, Settings settings) 54 | { 55 | SetUpLogger(settings); 56 | Initialize(settings.Headed); 57 | 58 | var packages = CreatePackageItems(settings.Packages, settings.Versions, settings.Branches, settings.Targets, settings.Configurations, settings.ProjectName, settings.PlcName); 59 | _twinpack.AddPackagesAsync(packages, new TwinpackService.AddPackageOptions { ForceDownload=settings.ForceDownload, IncludeDependencies= settings.AddDependencies }).GetAwaiter().GetResult(); 60 | return 0; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /TwinpackCli.Net/Commands/ListCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.ComponentModel; 5 | using Spectre.Console.Cli; 6 | using Spectre.Console; 7 | using System.Text.Json; 8 | 9 | namespace Twinpack.Commands 10 | { 11 | [Description(@"Lists all packages used in the configuration file located at ./Zeugwerk/config.json, or in the first solution found in the current directory.")] 12 | public class ListCommand : AbstractCommand 13 | { 14 | public class Settings : AbstractSettings 15 | { 16 | [CommandOption("-s|--search-term")] 17 | [Description("Optional search term to filter the listed packages by name or keyword.")] 18 | public string? SearchTerm { get; set; } = null; 19 | 20 | [CommandOption("--take")] 21 | [Description("Limits the number of results returned from the search.")] 22 | public int? Take { get; set; } 23 | 24 | [CommandOption("--plc ")] 25 | [Description("Filters the results to show only packages related to specific PLC(s). By default, all PLCs are considered.")] 26 | public string[] PlcFilter { get; set; } 27 | 28 | [CommandOption("--outdated")] 29 | [Description("Displays only the packages that are outdated compared to their latest available versions, considering the configured branch/target/configuration")] 30 | public bool Outdated { get; set; } = false; 31 | } 32 | 33 | public override int Execute(CommandContext context, Settings settings) 34 | { 35 | SetUpLogger(settings); 36 | Initialize(headed: false); 37 | 38 | // remove projects accordingly to the filter 39 | foreach (var project in _config.Projects) 40 | project.Plcs = project.Plcs.Where(x => settings.PlcFilter == null || !settings.PlcFilter.Any() || settings.PlcFilter.Contains(x.Name)).ToList(); 41 | 42 | var packages = _twinpack.RetrieveUsedPackagesAsync(settings.SearchTerm).GetAwaiter().GetResult() 43 | .Where(x => !settings.Outdated || x.IsUpdateable); 44 | 45 | if(settings.JsonOutput == true) 46 | { 47 | Console.Write(JsonSerializer.Serialize( 48 | packages.Select(x => new { 49 | Catalog = x.Catalog, 50 | Package = x.Package, 51 | PackageVersion = x.PackageVersion, 52 | Used = x.Used, 53 | Config = x.Config } 54 | ))); 55 | } 56 | else 57 | { 58 | var table = new Table(); 59 | table.AddColumns(new[] { "Project", "Plc", "Package", "Installed Version", "Latest Version", "Updatable" }); 60 | 61 | foreach (var package in packages) 62 | table.AddRow(new[] { package.ProjectName ?? "n/a", package.PlcName ?? "n/a", package.Catalog?.Name ?? "n/a", package.InstalledVersion ?? "n/a", package.UpdateVersion ?? "n/a", package.IsUpdateable.ToString() ?? "n/a" }); 63 | 64 | AnsiConsole.Write(table); 65 | } 66 | 67 | 68 | return 0; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /TwinpackCli.Net/Commands/PullCommand.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console.Cli; 2 | using System.ComponentModel; 3 | using Twinpack.Configuration; 4 | using Twinpack.Core; 5 | using Twinpack.Protocol; 6 | 7 | namespace Twinpack.Commands 8 | { 9 | [Description("Downloads packages that are references in .Zeugwerk/config.json to .Zeugwerk/libraries, you can use RepTool.exe to install them into the TwinCAT library repository.")] 10 | public class PullCommand : AbstractCommand 11 | { 12 | public class Settings : AbstractSettings 13 | { 14 | [CommandOption("-u|--username")] 15 | [Description("Username for Twinpack Server")] 16 | public string Username { get; set; } 17 | 18 | [CommandOption("-p|--password")] 19 | [Description("Password for Twinpack Server")] 20 | public string Password { get; set; } 21 | 22 | [CommandOption("-P|--provided")] 23 | [Description("Also pull packages that are provided by the package definition")] 24 | public bool Provided { get; set; } 25 | } 26 | 27 | public override int Execute(CommandContext context, Settings settings) 28 | { 29 | SetUpLogger(settings); 30 | 31 | PackagingServerRegistry.InitializeAsync(useDefaults: true, login: false).GetAwaiter().GetResult(); 32 | _twinpack = new TwinpackService(PackagingServerRegistry.Servers); 33 | 34 | var config = ConfigFactory.Load(); 35 | _twinpack.LoginAsync(settings.Username, settings.Password).GetAwaiter().GetResult(); 36 | PackagingServerRegistry.Servers.PullAsync(config, skipInternalPackages: !settings.Provided).GetAwaiter().GetResult(); 37 | return 0; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /TwinpackCli.Net/Commands/PushCommand.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console.Cli; 2 | using System.ComponentModel; 3 | using System.Linq; 4 | using Twinpack.Configuration; 5 | using Twinpack.Core; 6 | using Twinpack.Protocol; 7 | 8 | namespace Twinpack.Commands 9 | { 10 | [Description("Pushes libraries to a Twinpack Server")] 11 | public class PushCommand : AbstractCommand 12 | { 13 | public class Settings : AbstractSettings 14 | { 15 | [CommandOption("-u|--username")] 16 | [Description("Username for Twinpack Server")] 17 | public string Username { get; set; } 18 | 19 | [CommandOption("-p|--password")] 20 | [Description("Password for Twinpack Server")] 21 | public string Password { get; set; } 22 | 23 | [CommandOption("--configuration")] 24 | [Description("Package Configuration (Release, Debug, ...)")] 25 | public string Configuration { get; set; } 26 | 27 | [CommandOption("--target")] 28 | [Description("Package Target")] 29 | public string Target { get; set; } 30 | 31 | [CommandOption("--branch")] 32 | [Description("Package Branch")] 33 | public string Branch { get; set; } 34 | 35 | [CommandOption("--notes")] 36 | [Description("Optional release notes, specific to the file that is uploaded")] 37 | public string Notes { get; set; } 38 | 39 | [CommandOption("--compiled")] 40 | [Description("The package is a compiled-library")] 41 | public bool Compiled { get; set; } 42 | 43 | [CommandOption("--without-config")] 44 | [Description("Don't use a config.json file, but use the information where to find libraries from the other arguments")] 45 | public bool WithoutConfig { get; set; } 46 | 47 | [CommandOption("--library-path")] 48 | [Description("Only valid when without-config is used, path where .library files are located")] 49 | public string LibraryPath { get; set; } 50 | 51 | [CommandOption("--skip-duplicate")] 52 | [Description("If a package and version already exists, skip it and continue with the next package in the push, if any")] 53 | public bool SkipDuplicate { get; set; } 54 | } 55 | 56 | public override int Execute(CommandContext context, Settings settings) 57 | { 58 | SetUpLogger(settings); 59 | 60 | PackagingServerRegistry.InitializeAsync(useDefaults: true, login: false).GetAwaiter().GetResult(); 61 | _twinpack = new TwinpackService(PackagingServerRegistry.Servers); 62 | 63 | _twinpack.LoginAsync(settings.Username, settings.Password).GetAwaiter().GetResult(); 64 | 65 | foreach (var twinpackServer in PackagingServerRegistry.Servers.Where(x => x as TwinpackServer != null).Select(x => x as TwinpackServer)) 66 | { 67 | twinpackServer.PushAsync( 68 | settings.WithoutConfig ? 69 | ConfigPlcProjectFactory.PlcProjectsFromPath(settings.LibraryPath) : 70 | ConfigPlcProjectFactory.PlcProjectsFromConfig(settings.Compiled, settings.Target), 71 | settings.Configuration, 72 | settings.Branch, 73 | settings.Target, 74 | settings.Notes, 75 | settings.Compiled, 76 | settings.SkipDuplicate).GetAwaiter().GetResult(); 77 | } 78 | 79 | return 0; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /TwinpackCli.Net/Commands/RemoveCommand.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console.Cli; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | 5 | namespace Twinpack.Commands 6 | { 7 | [Description(@"Removes package(s) from the specified project and PLC using the sources defined in '.\sourceRepositories.json' or '%APPDATA%\Zeugwerk\Twinpack\sourceRepositories.json'.")] 8 | public class RemoveCommand : AbstractCommand 9 | { 10 | public class Settings : AbstractSettings 11 | { 12 | [CommandOption("--project")] 13 | [Description("The name of the project where the packages should be added.")] 14 | public string ProjectName { get; set; } 15 | 16 | [CommandOption("--plc ")] 17 | [Description("The name of the PLC where the packages will be added.")] 18 | public string PlcName { get; set; } 19 | 20 | [CommandOption("--package")] 21 | [Description("Specifies the package(s) to be added.")] 22 | public string[] Packages { get; set; } 23 | 24 | [CommandOption("--headed")] 25 | [Description("Enables the use of the Beckhoff Automation Interface, which is required for installing and/or uninstalling packages on the target. In 'headless' mode, install operations have to be performed by Beckhoff's 'RepTool.exe'. Defaults to false")] 26 | public bool Headed { get; set; } 27 | } 28 | 29 | public override int Execute(CommandContext context, Settings settings) 30 | { 31 | SetUpLogger(settings); 32 | 33 | Initialize(settings.Headed); 34 | 35 | var packages = CreatePackageItems(settings.Packages, settings.ProjectName, settings.PlcName); 36 | 37 | _twinpack.RemovePackagesAsync(packages, uninstall: false).GetAwaiter().GetResult(); 38 | 39 | return 0; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /TwinpackCli.Net/Commands/RestoreCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Twinpack.Core; 3 | using System.ComponentModel; 4 | using Spectre.Console.Cli; 5 | 6 | namespace Twinpack.Commands 7 | { 8 | [Description(@"Restore package(s) using the sources defined in '.\sourceRepositories.json' or '%APPDATA%\Zeugwerk\Twinpack\sourceRepositories.json'.")] 9 | public class RestoreCommand : AbstractCommand 10 | { 11 | public class Settings : AbstractSettings 12 | { 13 | [CommandOption("--include-provided-packages")] 14 | [Description("Restore packages, which are provided by the configuration (Plcs, which are also packages themselves)")] 15 | public bool IncludeProvidedPackages { get; set; } 16 | public string[] Configurations { get; set; } 17 | [CommandOption("--skip-download")] 18 | [Description("Skips the download of package(s)")] 19 | public bool SkipDownload { get; set; } 20 | [CommandOption("--skip-install")] 21 | [Description("Skips the installation of package(s)")] 22 | public bool SkipInstall { get; set; } 23 | [CommandOption("--force-download")] 24 | [Description("Forces the download of package(s) even if they are already available on the system.")] 25 | public bool ForceDownload { get; set; } 26 | [CommandOption("--headed")] 27 | [Description("Enables the use of the Beckhoff Automation Interface, which is required for installing and/or uninstalling packages on the target. In 'headless' mode, install operations have to be performed by Beckhoff's 'RepTool.exe'. Defaults to false")] 28 | public bool Headed { get; set; } 29 | } 30 | 31 | public override int Execute(CommandContext context, Settings settings) 32 | { 33 | SetUpLogger(settings); 34 | 35 | if (!settings.ForceDownload && !settings.Headed) 36 | _logger.Warn("Using headless mode, downloading packages even if they are available on the system."); 37 | 38 | Initialize(settings.Headed); 39 | 40 | _twinpack.RestorePackagesAsync( 41 | new TwinpackService.RestorePackageOptions 42 | { 43 | SkipDownload = settings.SkipDownload, 44 | IncludeProvidedPackages = settings.IncludeProvidedPackages, 45 | ForceDownload = settings.ForceDownload, 46 | IncludeDependencies = true 47 | }).GetAwaiter().GetResult(); 48 | 49 | return 0; 50 | } 51 | 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /TwinpackCli.Net/Commands/SearchCommand.cs: -------------------------------------------------------------------------------- 1 | using NLog.Fluent; 2 | using Spectre.Console; 3 | using Spectre.Console.Cli; 4 | using System; 5 | using System.ComponentModel; 6 | using System.Linq; 7 | using System.Text.Json; 8 | using System.Threading.Tasks; 9 | using Twinpack.Core; 10 | using Twinpack.Models; 11 | using Twinpack.Protocol; 12 | 13 | namespace Twinpack.Commands 14 | { 15 | [Description(@"Searches all package sources defined in the configuration file located at ./Zeugwerk/config.json, or in the first solution found in the current directory.")] 16 | public class SearchCommand : AbstractCommand 17 | { 18 | public class Settings : Twinpack.Commands.AbstractSettings 19 | { 20 | [CommandOption("-s|--search-term")] 21 | [Description("Optional search term to filter the listed packages by name or keyword.")] 22 | public string? SearchTerm { get; set; } = null; 23 | 24 | [CommandOption("--take")] 25 | [Description("Limit the number of results to return")] 26 | public int? Take { get; set; } 27 | } 28 | 29 | public override int Execute(CommandContext context, Settings settings) 30 | { 31 | SetUpLogger(settings); 32 | Initialize(headed: false, requiresConfig: false); 33 | 34 | var packages = _twinpack.RetrieveAvailablePackagesAsync(settings.SearchTerm, settings.Take).GetAwaiter().GetResult() 35 | .Where(x => x.Catalog != null) 36 | .Select(x => x.Catalog); 37 | 38 | if (settings.JsonOutput == true) 39 | { 40 | Console.Write(JsonSerializer.Serialize(packages)); 41 | } 42 | else 43 | { 44 | var table = new Table(); 45 | table.AddColumns(new[] { "Package", "Distributor" }); 46 | 47 | foreach (var package in packages) 48 | table.AddRow(new[] { package.Name, package.DistributorName }); 49 | 50 | AnsiConsole.Write(table); 51 | } 52 | 53 | 54 | return 0; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /TwinpackCli.Net/Commands/SetVersionCommand.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console.Cli; 2 | using System.ComponentModel; 3 | using static Twinpack.Core.TwinpackService; 4 | 5 | namespace Twinpack.Commands 6 | { 7 | [Description(@"Downloads package(s) using the sources defined in '.\sourceRepositories.json' or '%APPDATA%\Zeugwerk\Twinpack\sourceRepositories.json'.")] 8 | public class SetVersionCommand : AbstractCommand 9 | { 10 | public class Settings : AbstractSettings 11 | { 12 | [CommandArgument(0, "")] 13 | public string Version { get; set; } 14 | 15 | [CommandOption("--project")] 16 | [Description("Name of the project, which contains the PLC to set the version for. Defaults to null, meaning 'all projects'")] 17 | 18 | public string? ProjectName { get; set; } 19 | 20 | [CommandOption("--plc")] 21 | [Description("Name of the plc to set the version for. Defaults to null, meaning 'all plcs'")] 22 | 23 | public string? PlcName { get; set; } 24 | 25 | [CommandOption("--sync-framework-packages")] 26 | [Description("Set the version also for all other packages, which are part of the same framework")] 27 | 28 | public bool SyncFrameworkPackages { get; set; } 29 | [CommandOption("--purge-packages")] 30 | [Description("Purges the PLC from packages, which are not configured")] 31 | 32 | public bool PurgePackages { get; set; } 33 | 34 | [CommandOption("--branch")] 35 | [Description("Together with 'sync-framework-packages', the preferred branch of framework packages")] 36 | 37 | public string? PreferredFrameworkBranch { get; set; } 38 | 39 | [CommandOption("--target")] 40 | [Description("Together with 'sync-framework-packages', the preferred target of framework package")] 41 | 42 | public string? PreferredFrameworkTarget { get; set; } 43 | 44 | [CommandOption("--configuration")] 45 | [Description("Together with 'sync-framework-packages', the preferred configuration of framework packages")] 46 | 47 | public string? PreferredFrameworkConfiguration { get; set; } 48 | 49 | [CommandOption("--headed")] 50 | [Description("Enables the use of the Beckhoff Automation Interface, which is required for installing and/or uninstalling packages on the target. In 'headless' mode, install operations have to be performed by Beckhoff's 'RepTool.exe'. Defaults to false")] 51 | public bool Headed { get; set; } 52 | 53 | } 54 | public override int Execute(CommandContext context, Settings settings) 55 | { 56 | SetUpLogger(settings); 57 | Initialize(settings.Headed); 58 | 59 | _twinpack.SetPackageVersionAsync(settings.Version, 60 | new SetPackageVersionOptions 61 | { 62 | PurgePackages = settings.PurgePackages, 63 | ProjectName = settings.ProjectName, 64 | PlcName = settings.PlcName, 65 | SyncFrameworkPackages = settings.SyncFrameworkPackages, 66 | PreferredFrameworkBranch = settings.PreferredFrameworkBranch, 67 | PreferredFrameworkTarget = settings.PreferredFrameworkTarget, 68 | PreferredFrameworkConfiguration = settings.PreferredFrameworkConfiguration, 69 | } 70 | ).GetAwaiter().GetResult(); 71 | 72 | return 0; 73 | } 74 | 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /TwinpackCli.Net/Program.cs: -------------------------------------------------------------------------------- 1 | using NLog; 2 | using Twinpack.Commands; 3 | using Spectre.Console.Cli; 4 | 5 | namespace Twinpack 6 | { 7 | class Program 8 | { 9 | private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); 10 | [STAThread] 11 | #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously 12 | static int Main(string[] args) 13 | #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously 14 | { 15 | _logger.Info("sadf"); 16 | var app = new CommandApp(); 17 | app.Configure(config => 18 | { 19 | config.AddCommand("config"); 20 | config.AddCommand("search"); 21 | config.AddCommand("list"); 22 | config.AddCommand("download"); 23 | config.AddCommand("add"); 24 | config.AddCommand("remove"); 25 | config.AddCommand("restore"); 26 | config.AddCommand("update"); 27 | config.AddCommand("set-version"); 28 | config.AddCommand("pull"); 29 | config.AddCommand("push"); 30 | config.Settings.StrictParsing = true; 31 | config.Settings.ApplicationName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; 32 | config.Settings.ApplicationVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); 33 | }); 34 | 35 | try 36 | { 37 | return app.Run(args); 38 | } 39 | catch(Exception ex) 40 | { 41 | _logger.Error(ex.Message); 42 | _logger.Error(ex); 43 | return -1; 44 | } 45 | finally 46 | { 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /TwinpackCli.Net/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "TwinpackCli.Net": { 4 | "commandName": "Project", 5 | "commandLineArgs": "search -s ZCore" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /TwinpackCli.Net/TwinpackCli.Net.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | twinpack 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /TwinpackCli.Windows/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /TwinpackCli.Windows/Commands/AddCommand.cs: -------------------------------------------------------------------------------- 1 | using Twinpack.Core; 2 | using System.ComponentModel; 3 | using Spectre.Console.Cli; 4 | 5 | namespace Twinpack.Commands 6 | { 7 | [Description("Ensure that the configuration file is properly set up using 'twinpack.exe config' before executing this command.")] 8 | public class AddCommand : AbstractCommand 9 | { 10 | public class Settings : AbstractSettings 11 | { 12 | [CommandOption("--project ")] 13 | [Description("The name of the project where the packages should be added.")] 14 | public string ProjectName { get; set; } 15 | 16 | [CommandOption("--plc ")] 17 | [Description("The name of the PLC where the packages will be added.")] 18 | public string PlcName { get; set; } 19 | 20 | [CommandOption("--package")] 21 | [Description("Specifies the package(s) to be added.")] 22 | public string[] Packages { get; set; } 23 | 24 | [CommandOption("--version")] 25 | [Description("Defines the version(s) of the specified package(s). If omitted, Twinpack will automatically add the latest available version.")] 26 | public string[] Versions { get; set; } 27 | 28 | [CommandOption("--branch")] 29 | [Description("Specifies the branch(es) of the package(s). If not provided, Twinpack will determine the appropriate branch automatically, i.e. 'main'")] 30 | public string[] Branches { get; set; } 31 | 32 | [CommandOption("--target")] 33 | [Description("Indicates the target(s) for the package(s). If omitted, Twinpack will select the appropriate target(s) automatically, i.e. 'TC3.1'")] 34 | public string[] Targets { get; set; } 35 | 36 | [CommandOption("--configuration")] 37 | [Description("Specifies the configuration(s) for the package(s). If not specified, Twinpack will handle the configuration automatically, i.e. 'Release'")] 38 | public string[] Configurations { get; set; } 39 | 40 | [CommandOption("--add-dependencies")] 41 | [Description("If true, package dependencies will be added automatically to the PLC references.")] 42 | public bool AddDependencies { get; set; } 43 | 44 | [CommandOption("--force-download")] 45 | [Description("Forces the download of package(s) even if they are already available on the system.")] 46 | public bool ForceDownload { get; set; } 47 | 48 | [CommandOption("--headed")] 49 | [Description("Enables the use of the Beckhoff Automation Interface, which is required for installing and/or uninstalling packages on the target. In 'headless' mode, install operations have to be performed by Beckhoff's 'RepTool.exe'. Defaults to false")] 50 | public bool Headed { get; set; } 51 | } 52 | 53 | public override int Execute(CommandContext context, Settings settings) 54 | { 55 | SetUpLogger(settings); 56 | Initialize(settings.Headed); 57 | 58 | var packages = CreatePackageItems(settings.Packages, settings.Versions, settings.Branches, settings.Targets, settings.Configurations, settings.ProjectName, settings.PlcName); 59 | _twinpack.AddPackagesAsync(packages, new TwinpackService.AddPackageOptions { ForceDownload=settings.ForceDownload, IncludeDependencies= settings.AddDependencies }).GetAwaiter().GetResult(); 60 | return 0; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /TwinpackCli.Windows/Commands/ListCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.ComponentModel; 5 | using Spectre.Console.Cli; 6 | using Spectre.Console; 7 | using System.Text.Json; 8 | 9 | namespace Twinpack.Commands 10 | { 11 | [Description(@"Lists all packages used in the configuration file located at ./Zeugwerk/config.json, or in the first solution found in the current directory.")] 12 | public class ListCommand : AbstractCommand 13 | { 14 | public class Settings : AbstractSettings 15 | { 16 | [CommandOption("-s|--search-term")] 17 | [Description("Optional search term to filter the listed packages by name or keyword.")] 18 | public string? SearchTerm { get; set; } = null; 19 | 20 | [CommandOption("--take")] 21 | [Description("Limits the number of results returned from the search.")] 22 | public int? Take { get; set; } 23 | 24 | [CommandOption("--plc ")] 25 | [Description("Filters the results to show only packages related to specific PLC(s). By default, all PLCs are considered.")] 26 | public string[] PlcFilter { get; set; } 27 | 28 | [CommandOption("--outdated")] 29 | [Description("Displays only the packages that are outdated compared to their latest available versions, considering the configured branch/target/configuration")] 30 | public bool Outdated { get; set; } = false; 31 | } 32 | 33 | public override int Execute(CommandContext context, Settings settings) 34 | { 35 | SetUpLogger(settings); 36 | Initialize(headed: false); 37 | 38 | // remove projects accordingly to the filter 39 | foreach (var project in _config.Projects) 40 | project.Plcs = project.Plcs.Where(x => settings.PlcFilter == null || !settings.PlcFilter.Any() || settings.PlcFilter.Contains(x.Name)).ToList(); 41 | 42 | var packages = _twinpack.RetrieveUsedPackagesAsync(settings.SearchTerm).GetAwaiter().GetResult() 43 | .Where(x => !settings.Outdated || x.IsUpdateable); 44 | 45 | if(settings.JsonOutput == true) 46 | { 47 | Console.Write(JsonSerializer.Serialize( 48 | packages.Select(x => new { 49 | Catalog = x.Catalog, 50 | Package = x.Package, 51 | PackageVersion = x.PackageVersion, 52 | Used = x.Used, 53 | Config = x.Config } 54 | ))); 55 | } 56 | else 57 | { 58 | var table = new Table(); 59 | table.AddColumns(new[] { "Project", "Plc", "Package", "Installed Version", "Latest Version", "Updatable" }); 60 | 61 | foreach (var package in packages) 62 | table.AddRow(new[] { package.ProjectName ?? "n/a", package.PlcName ?? "n/a", package.Catalog?.Name ?? "n/a", package.InstalledVersion ?? "n/a", package.UpdateVersion ?? "n/a", package.IsUpdateable.ToString() ?? "n/a" }); 63 | 64 | AnsiConsole.Write(table); 65 | } 66 | 67 | 68 | return 0; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /TwinpackCli.Windows/Commands/PullCommand.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console.Cli; 2 | using System.ComponentModel; 3 | using Twinpack.Configuration; 4 | using Twinpack.Core; 5 | using Twinpack.Protocol; 6 | 7 | namespace Twinpack.Commands 8 | { 9 | [Description("Downloads packages that are references in .Zeugwerk/config.json to .Zeugwerk/libraries, you can use RepTool.exe to install them into the TwinCAT library repository.")] 10 | public class PullCommand : AbstractCommand 11 | { 12 | public class Settings : AbstractSettings 13 | { 14 | [CommandOption("-u|--username")] 15 | [Description("Username for Twinpack Server")] 16 | public string Username { get; set; } 17 | 18 | [CommandOption("-p|--password")] 19 | [Description("Password for Twinpack Server")] 20 | public string Password { get; set; } 21 | 22 | [CommandOption("-P|--provided")] 23 | [Description("Also pull packages that are provided by the package definition")] 24 | public bool Provided { get; set; } 25 | } 26 | 27 | public override int Execute(CommandContext context, Settings settings) 28 | { 29 | SetUpLogger(settings); 30 | 31 | PackagingServerRegistry.InitializeAsync(useDefaults: true, login: false).GetAwaiter().GetResult(); 32 | _twinpack = new TwinpackService(PackagingServerRegistry.Servers); 33 | 34 | var config = ConfigFactory.Load(); 35 | _twinpack.LoginAsync(settings.Username, settings.Password).GetAwaiter().GetResult(); 36 | PackagingServerRegistry.Servers.PullAsync(config, skipInternalPackages: !settings.Provided).GetAwaiter().GetResult(); 37 | return 0; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /TwinpackCli.Windows/Commands/PushCommand.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console.Cli; 2 | using System.ComponentModel; 3 | using System.Linq; 4 | using Twinpack.Configuration; 5 | using Twinpack.Core; 6 | using Twinpack.Protocol; 7 | 8 | namespace Twinpack.Commands 9 | { 10 | [Description("Pushes libraries to a Twinpack Server")] 11 | public class PushCommand : AbstractCommand 12 | { 13 | public class Settings : AbstractSettings 14 | { 15 | [CommandOption("-u|--username")] 16 | [Description("Username for Twinpack Server")] 17 | public string Username { get; set; } 18 | 19 | [CommandOption("-p|--password")] 20 | [Description("Password for Twinpack Server")] 21 | public string Password { get; set; } 22 | 23 | [CommandOption("--configuration")] 24 | [Description("Package Configuration (Release, Debug, ...)")] 25 | public string Configuration { get; set; } 26 | 27 | [CommandOption("--target")] 28 | [Description("Package Target")] 29 | public string Target { get; set; } 30 | 31 | [CommandOption("--branch")] 32 | [Description("Package Branch")] 33 | public string Branch { get; set; } 34 | 35 | [CommandOption("--notes")] 36 | [Description("Optional release notes, specific to the file that is uploaded")] 37 | public string Notes { get; set; } 38 | 39 | [CommandOption("--compiled")] 40 | [Description("The package is a compiled-library")] 41 | public bool Compiled { get; set; } 42 | 43 | [CommandOption("--without-config")] 44 | [Description("Don't use a config.json file, but use the information where to find libraries from the other arguments")] 45 | public bool WithoutConfig { get; set; } 46 | 47 | [CommandOption("--library-path")] 48 | [Description("Only valid when without-config is used, path where .library files are located")] 49 | public string LibraryPath { get; set; } 50 | 51 | [CommandOption("--skip-duplicate")] 52 | [Description("If a package and version already exists, skip it and continue with the next package in the push, if any")] 53 | public bool SkipDuplicate { get; set; } 54 | } 55 | 56 | public override int Execute(CommandContext context, Settings settings) 57 | { 58 | SetUpLogger(settings); 59 | 60 | PackagingServerRegistry.InitializeAsync(useDefaults: true, login: false).GetAwaiter().GetResult(); 61 | _twinpack = new TwinpackService(PackagingServerRegistry.Servers); 62 | 63 | _twinpack.LoginAsync(settings.Username, settings.Password).GetAwaiter().GetResult(); 64 | 65 | foreach (var twinpackServer in PackagingServerRegistry.Servers.Where(x => x as TwinpackServer != null).Select(x => x as TwinpackServer)) 66 | { 67 | twinpackServer.PushAsync( 68 | settings.WithoutConfig ? 69 | ConfigPlcProjectFactory.PlcProjectsFromPath(settings.LibraryPath) : 70 | ConfigPlcProjectFactory.PlcProjectsFromConfig(settings.Compiled, settings.Target), 71 | settings.Configuration, 72 | settings.Branch, 73 | settings.Target, 74 | settings.Notes, 75 | settings.Compiled, 76 | settings.SkipDuplicate).GetAwaiter().GetResult(); 77 | } 78 | 79 | return 0; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /TwinpackCli.Windows/Commands/RemoveCommand.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console.Cli; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | 5 | namespace Twinpack.Commands 6 | { 7 | [Description(@"Removes package(s) from the specified project and PLC using the sources defined in '.\sourceRepositories.json' or '%APPDATA%\Zeugwerk\Twinpack\sourceRepositories.json'.")] 8 | public class RemoveCommand : AbstractCommand 9 | { 10 | public class Settings : AbstractSettings 11 | { 12 | [CommandOption("--project")] 13 | [Description("The name of the project where the packages should be added.")] 14 | public string ProjectName { get; set; } 15 | 16 | [CommandOption("--plc ")] 17 | [Description("The name of the PLC where the packages will be added.")] 18 | public string PlcName { get; set; } 19 | 20 | [CommandOption("--package")] 21 | [Description("Specifies the package(s) to be added.")] 22 | public string[] Packages { get; set; } 23 | 24 | [CommandOption("--headed")] 25 | [Description("Enables the use of the Beckhoff Automation Interface, which is required for installing and/or uninstalling packages on the target. In 'headless' mode, install operations have to be performed by Beckhoff's 'RepTool.exe'. Defaults to false")] 26 | public bool Headed { get; set; } 27 | } 28 | 29 | public override int Execute(CommandContext context, Settings settings) 30 | { 31 | SetUpLogger(settings); 32 | 33 | Initialize(settings.Headed); 34 | 35 | var packages = CreatePackageItems(settings.Packages, settings.ProjectName, settings.PlcName); 36 | 37 | _twinpack.RemovePackagesAsync(packages, uninstall: false).GetAwaiter().GetResult(); 38 | 39 | return 0; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /TwinpackCli.Windows/Commands/RestoreCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Twinpack.Core; 3 | using System.ComponentModel; 4 | using Spectre.Console.Cli; 5 | 6 | namespace Twinpack.Commands 7 | { 8 | [Description(@"Restore package(s) using the sources defined in '.\sourceRepositories.json' or '%APPDATA%\Zeugwerk\Twinpack\sourceRepositories.json'.")] 9 | public class RestoreCommand : AbstractCommand 10 | { 11 | public class Settings : AbstractSettings 12 | { 13 | [CommandOption("--include-provided-packages")] 14 | [Description("Restore packages, which are provided by the configuration (Plcs, which are also packages themselves)")] 15 | public bool IncludeProvidedPackages { get; set; } 16 | public string[] Configurations { get; set; } 17 | [CommandOption("--skip-download")] 18 | [Description("Skips the download of package(s)")] 19 | public bool SkipDownload { get; set; } 20 | [CommandOption("--skip-install")] 21 | [Description("Skips the installation of package(s)")] 22 | public bool SkipInstall { get; set; } 23 | [CommandOption("--force-download")] 24 | [Description("Forces the download of package(s) even if they are already available on the system.")] 25 | public bool ForceDownload { get; set; } 26 | [CommandOption("--headed")] 27 | [Description("Enables the use of the Beckhoff Automation Interface, which is required for installing and/or uninstalling packages on the target. In 'headless' mode, install operations have to be performed by Beckhoff's 'RepTool.exe'. Defaults to false")] 28 | public bool Headed { get; set; } 29 | } 30 | 31 | public override int Execute(CommandContext context, Settings settings) 32 | { 33 | SetUpLogger(settings); 34 | 35 | if (!settings.ForceDownload && !settings.Headed) 36 | _logger.Warn("Using headless mode, downloading packages even if they are available on the system."); 37 | 38 | Initialize(settings.Headed); 39 | 40 | _twinpack.RestorePackagesAsync( 41 | new TwinpackService.RestorePackageOptions 42 | { 43 | SkipDownload = settings.SkipDownload, 44 | IncludeProvidedPackages = settings.IncludeProvidedPackages, 45 | ForceDownload = settings.ForceDownload, 46 | IncludeDependencies = true 47 | }).GetAwaiter().GetResult(); 48 | 49 | return 0; 50 | } 51 | 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /TwinpackCli.Windows/Commands/SearchCommand.cs: -------------------------------------------------------------------------------- 1 | using NLog.Fluent; 2 | using Spectre.Console; 3 | using Spectre.Console.Cli; 4 | using System; 5 | using System.ComponentModel; 6 | using System.Linq; 7 | using System.Text.Json; 8 | using System.Threading.Tasks; 9 | using Twinpack.Core; 10 | using Twinpack.Models; 11 | using Twinpack.Protocol; 12 | 13 | namespace Twinpack.Commands 14 | { 15 | [Description(@"Searches all package sources defined in the configuration file located at ./Zeugwerk/config.json, or in the first solution found in the current directory.")] 16 | public class SearchCommand : AbstractCommand 17 | { 18 | public class Settings : Twinpack.Commands.AbstractSettings 19 | { 20 | [CommandOption("-s|--search-term")] 21 | [Description("Optional search term to filter the listed packages by name or keyword.")] 22 | public string? SearchTerm { get; set; } = null; 23 | 24 | [CommandOption("--take")] 25 | [Description("Limit the number of results to return")] 26 | public int? Take { get; set; } 27 | } 28 | 29 | public override int Execute(CommandContext context, Settings settings) 30 | { 31 | SetUpLogger(settings); 32 | Initialize(headed: false, requiresConfig: false); 33 | 34 | var packages = _twinpack.RetrieveAvailablePackagesAsync(settings.SearchTerm, settings.Take).GetAwaiter().GetResult() 35 | .Where(x => x.Catalog != null) 36 | .Select(x => x.Catalog); 37 | 38 | if (settings.JsonOutput == true) 39 | { 40 | Console.Write(JsonSerializer.Serialize(packages)); 41 | } 42 | else 43 | { 44 | var table = new Table(); 45 | table.AddColumns(new[] { "Package", "Distributor" }); 46 | 47 | foreach (var package in packages) 48 | table.AddRow(new[] { package.Name, package.DistributorName }); 49 | 50 | AnsiConsole.Write(table); 51 | } 52 | 53 | 54 | return 0; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /TwinpackCli.Windows/Commands/SetVersionCommand.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console.Cli; 2 | using System.ComponentModel; 3 | using static Twinpack.Core.TwinpackService; 4 | 5 | namespace Twinpack.Commands 6 | { 7 | [Description(@"Downloads package(s) using the sources defined in '.\sourceRepositories.json' or '%APPDATA%\Zeugwerk\Twinpack\sourceRepositories.json'.")] 8 | public class SetVersionCommand : AbstractCommand 9 | { 10 | public class Settings : AbstractSettings 11 | { 12 | [CommandArgument(0, "")] 13 | public string Version { get; set; } 14 | 15 | [CommandOption("--project")] 16 | [Description("Name of the project, which contains the PLC to set the version for. Defaults to null, meaning 'all projects'")] 17 | 18 | public string? ProjectName { get; set; } 19 | 20 | [CommandOption("--plc")] 21 | [Description("Name of the plc to set the version for. Defaults to null, meaning 'all plcs'")] 22 | 23 | public string? PlcName { get; set; } 24 | 25 | [CommandOption("--sync-framework-packages")] 26 | [Description("Set the version also for all other packages, which are part of the same framework")] 27 | 28 | public bool SyncFrameworkPackages { get; set; } 29 | [CommandOption("--purge-packages")] 30 | [Description("Purges the PLC from packages, which are not configured")] 31 | 32 | public bool PurgePackages { get; set; } 33 | 34 | [CommandOption("--branch")] 35 | [Description("Together with 'sync-framework-packages', the preferred branch of framework packages")] 36 | 37 | public string? PreferredFrameworkBranch { get; set; } 38 | 39 | [CommandOption("--target")] 40 | [Description("Together with 'sync-framework-packages', the preferred target of framework package")] 41 | 42 | public string? PreferredFrameworkTarget { get; set; } 43 | 44 | [CommandOption("--configuration")] 45 | [Description("Together with 'sync-framework-packages', the preferred configuration of framework packages")] 46 | 47 | public string? PreferredFrameworkConfiguration { get; set; } 48 | 49 | [CommandOption("--headed")] 50 | [Description("Enables the use of the Beckhoff Automation Interface, which is required for installing and/or uninstalling packages on the target. In 'headless' mode, install operations have to be performed by Beckhoff's 'RepTool.exe'. Defaults to false")] 51 | public bool Headed { get; set; } 52 | 53 | } 54 | public override int Execute(CommandContext context, Settings settings) 55 | { 56 | SetUpLogger(settings); 57 | Initialize(settings.Headed); 58 | 59 | _twinpack.SetPackageVersionAsync(settings.Version, 60 | new SetPackageVersionOptions 61 | { 62 | PurgePackages = settings.PurgePackages, 63 | ProjectName = settings.ProjectName, 64 | PlcName = settings.PlcName, 65 | SyncFrameworkPackages = settings.SyncFrameworkPackages, 66 | PreferredFrameworkBranch = settings.PreferredFrameworkBranch, 67 | PreferredFrameworkTarget = settings.PreferredFrameworkTarget, 68 | PreferredFrameworkConfiguration = settings.PreferredFrameworkConfiguration, 69 | } 70 | ).GetAwaiter().GetResult(); 71 | 72 | return 0; 73 | } 74 | 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /TwinpackCli.Windows/Program.cs: -------------------------------------------------------------------------------- 1 | using NLog; 2 | using System; 3 | using Twinpack.Commands; 4 | using Spectre.Console.Cli; 5 | using NLog.Config; 6 | using System.Runtime.Remoting.Contexts; 7 | 8 | namespace Twinpack 9 | { 10 | class Program 11 | { 12 | private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); 13 | [STAThread] 14 | #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously 15 | static int Main(string[] args) 16 | #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously 17 | { 18 | var app = new CommandApp(); 19 | app.Configure(config => 20 | { 21 | config.AddCommand("config"); 22 | config.AddCommand("search"); 23 | config.AddCommand("list"); 24 | config.AddCommand("download"); 25 | config.AddCommand("add"); 26 | config.AddCommand("remove"); 27 | config.AddCommand("restore"); 28 | config.AddCommand("update"); 29 | config.AddCommand("set-version"); 30 | config.AddCommand("pull"); 31 | config.AddCommand("push"); 32 | config.Settings.StrictParsing = true; 33 | config.Settings.ApplicationName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; 34 | config.Settings.ApplicationVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); 35 | }); 36 | 37 | try 38 | { 39 | return app.Run(args); 40 | } 41 | catch(Exception ex) 42 | { 43 | _logger.Error(ex.Message); 44 | _logger.Trace(ex); 45 | return -1; 46 | } 47 | finally 48 | { 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /TwinpackCli.Windows/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TwinpackCli")] 9 | [assembly: AssemblyDescription("Package manager for TwinCAT libraries")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Zeugwerk GmbH")] 12 | [assembly: AssemblyProduct("TwinpackCli")] 13 | [assembly: AssemblyCopyright("Copyright © Zeugwerk GmbH 2023")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("0accaf6b-a62c-4051-ae4f-47461eaa4a2f")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /TwinpackCore/Commands/AddCommand.cs: -------------------------------------------------------------------------------- 1 | using Twinpack.Core; 2 | using System.ComponentModel; 3 | using Spectre.Console.Cli; 4 | 5 | namespace Twinpack.Commands 6 | { 7 | [Description("Ensure that the configuration file is properly set up using 'twinpack.exe config' before executing this command.")] 8 | public class AddCommand : AbstractCommand 9 | { 10 | public class Settings : AbstractSettings 11 | { 12 | [CommandOption("--project ")] 13 | [Description("The name of the project where the packages should be added.")] 14 | public string ProjectName { get; set; } 15 | 16 | [CommandOption("--plc ")] 17 | [Description("The name of the PLC where the packages will be added.")] 18 | public string PlcName { get; set; } 19 | 20 | [CommandOption("--package")] 21 | [Description("Specifies the package(s) to be added.")] 22 | public string[] Packages { get; set; } 23 | 24 | [CommandOption("--version")] 25 | [Description("Defines the version(s) of the specified package(s). If omitted, Twinpack will automatically add the latest available version.")] 26 | public string[] Versions { get; set; } 27 | 28 | [CommandOption("--branch")] 29 | [Description("Specifies the branch(es) of the package(s). If not provided, Twinpack will determine the appropriate branch automatically, i.e. 'main'")] 30 | public string[] Branches { get; set; } 31 | 32 | [CommandOption("--target")] 33 | [Description("Indicates the target(s) for the package(s). If omitted, Twinpack will select the appropriate target(s) automatically, i.e. 'TC3.1'")] 34 | public string[] Targets { get; set; } 35 | 36 | [CommandOption("--configuration")] 37 | [Description("Specifies the configuration(s) for the package(s). If not specified, Twinpack will handle the configuration automatically, i.e. 'Release'")] 38 | public string[] Configurations { get; set; } 39 | 40 | [CommandOption("--add-dependencies")] 41 | [Description("If true, package dependencies will be added automatically to the PLC references.")] 42 | public bool AddDependencies { get; set; } 43 | 44 | [CommandOption("--force-download")] 45 | [Description("Forces the download of package(s) even if they are already available on the system.")] 46 | public bool ForceDownload { get; set; } 47 | 48 | [CommandOption("--headed")] 49 | [Description("Enables the use of the Beckhoff Automation Interface, which is required for installing and/or uninstalling packages on the target. In 'headless' mode, install operations have to be performed by Beckhoff's 'RepTool.exe'. Defaults to false")] 50 | public bool Headed { get; set; } 51 | } 52 | 53 | public override int Execute(CommandContext context, Settings settings) 54 | { 55 | SetUpLogger(settings); 56 | Initialize(settings.Headed); 57 | 58 | var packages = CreatePackageItems(settings.Packages, settings.Versions, settings.Branches, settings.Targets, settings.Configurations, settings.ProjectName, settings.PlcName); 59 | _twinpack.AddPackagesAsync(packages, new TwinpackService.AddPackageOptions { ForceDownload=settings.ForceDownload, IncludeDependencies= settings.AddDependencies }).GetAwaiter().GetResult(); 60 | return 0; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /TwinpackCore/Commands/DownloadCommand.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console.Cli; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel; 5 | using System.Linq; 6 | using Twinpack.Core; 7 | 8 | namespace Twinpack.Commands 9 | { 10 | [Description(@"Downloads package(s) from the sources defined in %APPDATA%\Zeugwerk\Twinpack\sourceRepositories.json.")] 11 | public class DownloadCommand : AbstractCommand 12 | { 13 | public class Settings : AbstractSettings 14 | { 15 | [CommandOption("--package")] 16 | [Description("Specifies the package(s) to be downloaded. If not provided, all relevant packages will be handled.")] 17 | public string[] Packages { get; set; } 18 | [CommandOption("--version")] 19 | [Description("Specifies the version(s) of the package(s) to be downloaded. If not explicitly set, Twinpack downloads the latest version.")] 20 | public string[] Versions { get; set; } 21 | [CommandOption("--branch")] 22 | [Description("Specifies the branch(es) of the package(s). If not provided, Twinpack automatically determines the appropriate branch.")] 23 | public string[] Branches { get; set; } 24 | [CommandOption("--target")] 25 | [Description("Specifies the target(s) for the package(s). If omitted, Twinpack will automatically select the appropriate target.")] 26 | public string[] Targets { get; set; } 27 | [CommandOption("--configuration")] 28 | [Description("Specifies the configuration(s) of the package(s). If not set, Twinpack will automatically select the appropriate configuration.")] 29 | public string[] Configurations { get; set; } 30 | [CommandOption("--force-download")] 31 | [Description("Forces the download of package(s) even if they are already available on the system.")] 32 | public bool ForceDownload { get; set; } 33 | [CommandOption("--include-provided-packages")] 34 | [Description("Includes the download of packages that are provided by the configuration (e.g., PLCs that are also considered packages by being available on a configured package server).")] 35 | public bool IncludeProvidedPackages { get; set; } 36 | [CommandOption("--headed")] 37 | [Description("Enables the use of the Beckhoff Automation Interface, which is required for installing and/or uninstalling packages on the target. In 'headless' mode, install operations have to be performed by Beckhoff's 'RepTool.exe'. Defaults to false")] 38 | public bool Headed { get; set; } 39 | } 40 | public override int Execute(CommandContext context, Settings settings) 41 | { 42 | SetUpLogger(settings); 43 | 44 | if (!settings.ForceDownload && !settings.Headed) 45 | _logger.Warn("Using headless mode, downloading packages even if they are available on the system."); 46 | 47 | Initialize(settings.Headed); 48 | 49 | var packages = CreatePackageItems(settings.Packages, settings.Versions, settings.Branches, settings.Targets, settings.Configurations); 50 | 51 | if (settings.Packages == null) 52 | packages = _twinpack.RetrieveUsedPackagesAsync().GetAwaiter().GetResult().ToList(); 53 | 54 | // download packages 55 | var downloadedPackageVersions = _twinpack.DownloadPackagesAsync(packages, 56 | new TwinpackService.DownloadPackageOptions 57 | { 58 | IncludeProvidedPackages = settings.IncludeProvidedPackages, 59 | IncludeDependencies = true, 60 | ForceDownload = settings.ForceDownload, 61 | }).GetAwaiter().GetResult(); 62 | 63 | return 0; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /TwinpackCore/Commands/ListCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.ComponentModel; 5 | using Spectre.Console.Cli; 6 | using Spectre.Console; 7 | using System.Text.Json; 8 | 9 | namespace Twinpack.Commands 10 | { 11 | [Description(@"Lists all packages used in the configuration file located at ./Zeugwerk/config.json, or in the first solution found in the current directory.")] 12 | public class ListCommand : AbstractCommand 13 | { 14 | public class Settings : AbstractSettings 15 | { 16 | [CommandOption("-s|--search-term")] 17 | [Description("Optional search term to filter the listed packages by name or keyword.")] 18 | public string? SearchTerm { get; set; } = null; 19 | 20 | [CommandOption("--take")] 21 | [Description("Limits the number of results returned from the search.")] 22 | public int? Take { get; set; } 23 | 24 | [CommandOption("--plc ")] 25 | [Description("Filters the results to show only packages related to specific PLC(s). By default, all PLCs are considered.")] 26 | public string[] PlcFilter { get; set; } 27 | 28 | [CommandOption("--outdated")] 29 | [Description("Displays only the packages that are outdated compared to their latest available versions, considering the configured branch/target/configuration")] 30 | public bool Outdated { get; set; } = false; 31 | } 32 | 33 | public override int Execute(CommandContext context, Settings settings) 34 | { 35 | SetUpLogger(settings); 36 | Initialize(headed: false); 37 | 38 | // remove projects accordingly to the filter 39 | foreach (var project in _config.Projects) 40 | project.Plcs = project.Plcs.Where(x => settings.PlcFilter == null || !settings.PlcFilter.Any() || settings.PlcFilter.Contains(x.Name)).ToList(); 41 | 42 | var packages = _twinpack.RetrieveUsedPackagesAsync(settings.SearchTerm).GetAwaiter().GetResult() 43 | .Where(x => !settings.Outdated || x.IsUpdateable); 44 | 45 | if(settings.JsonOutput == true) 46 | { 47 | Console.Write(JsonSerializer.Serialize( 48 | packages.Select(x => new { 49 | Catalog = x.Catalog, 50 | Package = x.Package, 51 | PackageVersion = x.PackageVersion, 52 | Used = x.Used, 53 | Config = x.Config } 54 | ))); 55 | } 56 | else 57 | { 58 | var table = new Table(); 59 | table.AddColumns(new[] { "Project", "Plc", "Package", "Installed Version", "Latest Version", "Updatable" }); 60 | 61 | foreach (var package in packages) 62 | table.AddRow(new[] { package.ProjectName ?? "n/a", package.PlcName ?? "n/a", package.Catalog?.Name ?? "n/a", package.InstalledVersion ?? "n/a", package.UpdateVersion ?? "n/a", package.IsUpdateable.ToString() ?? "n/a" }); 63 | 64 | AnsiConsole.Write(table); 65 | } 66 | 67 | 68 | return 0; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /TwinpackCore/Commands/PullCommand.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console.Cli; 2 | using System.ComponentModel; 3 | using Twinpack.Configuration; 4 | using Twinpack.Core; 5 | using Twinpack.Protocol; 6 | 7 | namespace Twinpack.Commands 8 | { 9 | [Description("Downloads packages that are references in .Zeugwerk/config.json to .Zeugwerk/libraries, you can use RepTool.exe to install them into the TwinCAT library repository.")] 10 | public class PullCommand : AbstractCommand 11 | { 12 | public class Settings : AbstractSettings 13 | { 14 | [CommandOption("-u|--username")] 15 | [Description("Username for Twinpack Server")] 16 | public string Username { get; set; } 17 | 18 | [CommandOption("-p|--password")] 19 | [Description("Password for Twinpack Server")] 20 | public string Password { get; set; } 21 | 22 | [CommandOption("-P|--provided")] 23 | [Description("Also pull packages that are provided by the package definition")] 24 | public bool Provided { get; set; } 25 | } 26 | 27 | public override int Execute(CommandContext context, Settings settings) 28 | { 29 | SetUpLogger(settings); 30 | 31 | PackagingServerRegistry.InitializeAsync(useDefaults: true, login: false).GetAwaiter().GetResult(); 32 | _twinpack = new TwinpackService(PackagingServerRegistry.Servers); 33 | 34 | var config = ConfigFactory.Load(); 35 | _twinpack.LoginAsync(settings.Username, settings.Password).GetAwaiter().GetResult(); 36 | PackagingServerRegistry.Servers.PullAsync(config, skipInternalPackages: !settings.Provided).GetAwaiter().GetResult(); 37 | return 0; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /TwinpackCore/Commands/PushCommand.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console.Cli; 2 | using System.ComponentModel; 3 | using System.Linq; 4 | using Twinpack.Configuration; 5 | using Twinpack.Core; 6 | using Twinpack.Protocol; 7 | 8 | namespace Twinpack.Commands 9 | { 10 | [Description("Pushes libraries to a Twinpack Server")] 11 | public class PushCommand : AbstractCommand 12 | { 13 | public class Settings : AbstractSettings 14 | { 15 | [CommandOption("-u|--username")] 16 | [Description("Username for Twinpack Server")] 17 | public string Username { get; set; } 18 | 19 | [CommandOption("-p|--password")] 20 | [Description("Password for Twinpack Server")] 21 | public string Password { get; set; } 22 | 23 | [CommandOption("--configuration")] 24 | [Description("Package Configuration (Release, Debug, ...)")] 25 | public string Configuration { get; set; } 26 | 27 | [CommandOption("--target")] 28 | [Description("Package Target")] 29 | public string Target { get; set; } 30 | 31 | [CommandOption("--branch")] 32 | [Description("Package Branch")] 33 | public string Branch { get; set; } 34 | 35 | [CommandOption("--notes")] 36 | [Description("Optional release notes, specific to the file that is uploaded")] 37 | public string Notes { get; set; } 38 | 39 | [CommandOption("--compiled")] 40 | [Description("The package is a compiled-library")] 41 | public bool Compiled { get; set; } 42 | 43 | [CommandOption("--without-config")] 44 | [Description("Don't use a config.json file, but use the information where to find libraries from the other arguments")] 45 | public bool WithoutConfig { get; set; } 46 | 47 | [CommandOption("--library-path")] 48 | [Description("Only valid when without-config is used, path where .library files are located")] 49 | public string LibraryPath { get; set; } 50 | 51 | [CommandOption("--skip-duplicate")] 52 | [Description("If a package and version already exists, skip it and continue with the next package in the push, if any")] 53 | public bool SkipDuplicate { get; set; } 54 | } 55 | 56 | public override int Execute(CommandContext context, Settings settings) 57 | { 58 | SetUpLogger(settings); 59 | 60 | PackagingServerRegistry.InitializeAsync(useDefaults: true, login: false).GetAwaiter().GetResult(); 61 | _twinpack = new TwinpackService(PackagingServerRegistry.Servers); 62 | 63 | _twinpack.LoginAsync(settings.Username, settings.Password).GetAwaiter().GetResult(); 64 | 65 | foreach (var twinpackServer in PackagingServerRegistry.Servers.Where(x => x as TwinpackServer != null).Select(x => x as TwinpackServer)) 66 | { 67 | twinpackServer.PushAsync( 68 | settings.WithoutConfig ? 69 | ConfigPlcProjectFactory.PlcProjectsFromPath(settings.LibraryPath) : 70 | ConfigPlcProjectFactory.PlcProjectsFromConfig(settings.Compiled, settings.Target), 71 | settings.Configuration, 72 | settings.Branch, 73 | settings.Target, 74 | settings.Notes, 75 | settings.Compiled, 76 | settings.SkipDuplicate).GetAwaiter().GetResult(); 77 | } 78 | 79 | return 0; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /TwinpackCore/Commands/RemoveCommand.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console.Cli; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | 5 | namespace Twinpack.Commands 6 | { 7 | [Description(@"Removes package(s) from the specified project and PLC using the sources defined in %APPDATA%\Zeugwerk\Twinpack\sourceRepositories.json.")] 8 | public class RemoveCommand : AbstractCommand 9 | { 10 | public class Settings : AbstractSettings 11 | { 12 | [CommandOption("--project")] 13 | [Description("The name of the project where the packages should be added.")] 14 | public string ProjectName { get; set; } 15 | 16 | [CommandOption("--plc ")] 17 | [Description("The name of the PLC where the packages will be added.")] 18 | public string PlcName { get; set; } 19 | 20 | [CommandOption("--package")] 21 | [Description("Specifies the package(s) to be added.")] 22 | public string[] Packages { get; set; } 23 | 24 | [CommandOption("--headed")] 25 | [Description("Enables the use of the Beckhoff Automation Interface, which is required for installing and/or uninstalling packages on the target. In 'headless' mode, install operations have to be performed by Beckhoff's 'RepTool.exe'. Defaults to false")] 26 | public bool Headed { get; set; } 27 | } 28 | 29 | public override int Execute(CommandContext context, Settings settings) 30 | { 31 | SetUpLogger(settings); 32 | 33 | Initialize(settings.Headed); 34 | 35 | var packages = CreatePackageItems(settings.Packages, settings.ProjectName, settings.PlcName); 36 | 37 | _twinpack.RemovePackagesAsync(packages, uninstall: false).GetAwaiter().GetResult(); 38 | 39 | return 0; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /TwinpackCore/Commands/RestoreCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Twinpack.Core; 3 | using System.ComponentModel; 4 | using Spectre.Console.Cli; 5 | 6 | namespace Twinpack.Commands 7 | { 8 | [Description(@"Restore package(s) using the sources defined in %APPDATA%\Zeugwerk\Twinpack\sourceRepositories.json.")] 9 | public class RestoreCommand : AbstractCommand 10 | { 11 | public class Settings : AbstractSettings 12 | { 13 | [CommandOption("--include-provided-packages")] 14 | [Description("Restore packages, which are provided by the configuration (Plcs, which are also packages themselves)")] 15 | public bool IncludeProvidedPackages { get; set; } 16 | public string[] Configurations { get; set; } 17 | [CommandOption("--skip-download")] 18 | [Description("Skips the download of package(s)")] 19 | public bool SkipDownload { get; set; } 20 | [CommandOption("--skip-install")] 21 | [Description("Skips the installation of package(s)")] 22 | public bool SkipInstall { get; set; } 23 | [CommandOption("--force-download")] 24 | [Description("Forces the download of package(s) even if they are already available on the system.")] 25 | public bool ForceDownload { get; set; } 26 | [CommandOption("--headed")] 27 | [Description("Enables the use of the Beckhoff Automation Interface, which is required for installing and/or uninstalling packages on the target. In 'headless' mode, install operations have to be performed by Beckhoff's 'RepTool.exe'. Defaults to false")] 28 | public bool Headed { get; set; } 29 | } 30 | 31 | public override int Execute(CommandContext context, Settings settings) 32 | { 33 | SetUpLogger(settings); 34 | 35 | if (!settings.ForceDownload && !settings.Headed) 36 | _logger.Warn("Using headless mode, downloading packages even if they are available on the system."); 37 | 38 | Initialize(settings.Headed); 39 | 40 | _twinpack.RestorePackagesAsync( 41 | new TwinpackService.RestorePackageOptions 42 | { 43 | SkipDownload = settings.SkipDownload, 44 | IncludeProvidedPackages = settings.IncludeProvidedPackages, 45 | ForceDownload = settings.ForceDownload, 46 | IncludeDependencies = true 47 | }).GetAwaiter().GetResult(); 48 | 49 | return 0; 50 | } 51 | 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /TwinpackCore/Commands/SearchCommand.cs: -------------------------------------------------------------------------------- 1 | using NLog.Fluent; 2 | using Spectre.Console; 3 | using Spectre.Console.Cli; 4 | using System; 5 | using System.ComponentModel; 6 | using System.Linq; 7 | using System.Text.Json; 8 | using System.Threading.Tasks; 9 | using Twinpack.Core; 10 | using Twinpack.Models; 11 | using Twinpack.Protocol; 12 | 13 | namespace Twinpack.Commands 14 | { 15 | [Description(@"Searches all package sources defined in the configuration file located at ./Zeugwerk/config.json, or in the first solution found in the current directory.")] 16 | public class SearchCommand : AbstractCommand 17 | { 18 | public class Settings : Twinpack.Commands.AbstractSettings 19 | { 20 | [CommandOption("-s|--search-term")] 21 | [Description("Optional search term to filter the listed packages by name or keyword.")] 22 | public string? SearchTerm { get; set; } = null; 23 | 24 | [CommandOption("--take")] 25 | [Description("Limit the number of results to return")] 26 | public int? Take { get; set; } 27 | } 28 | 29 | public override int Execute(CommandContext context, Settings settings) 30 | { 31 | SetUpLogger(settings); 32 | Initialize(headed: false, requiresConfig: false); 33 | 34 | var packages = _twinpack.RetrieveAvailablePackagesAsync(settings.SearchTerm, settings.Take).GetAwaiter().GetResult() 35 | .Where(x => x.Catalog != null) 36 | .Select(x => x.Catalog); 37 | 38 | if (settings.JsonOutput == true) 39 | { 40 | Console.Write(JsonSerializer.Serialize(packages)); 41 | } 42 | else 43 | { 44 | var table = new Table(); 45 | table.AddColumns(new[] { "Package", "Distributor" }); 46 | 47 | foreach (var package in packages) 48 | table.AddRow(new[] { package.Name, package.DistributorName }); 49 | 50 | AnsiConsole.Write(table); 51 | } 52 | 53 | 54 | return 0; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /TwinpackCore/Commands/SetVersionCommand.cs: -------------------------------------------------------------------------------- 1 | using Spectre.Console.Cli; 2 | using System.ComponentModel; 3 | using static Twinpack.Core.TwinpackService; 4 | 5 | namespace Twinpack.Commands 6 | { 7 | [Description(@"Downloads package(s) using the sources defined in %APPDATA%\Zeugwerk\Twinpack\sourceRepositories.json.")] 8 | public class SetVersionCommand : AbstractCommand 9 | { 10 | public class Settings : AbstractSettings 11 | { 12 | [CommandArgument(0, "")] 13 | public string Version { get; set; } 14 | 15 | [CommandOption("--project")] 16 | [Description("Name of the project, which contains the PLC to set the version for. Defaults to null, meaning 'all projects'")] 17 | 18 | public string? ProjectName { get; set; } 19 | 20 | [CommandOption("--plc")] 21 | [Description("Name of the plc to set the version for. Defaults to null, meaning 'all plcs'")] 22 | 23 | public string? PlcName { get; set; } 24 | 25 | [CommandOption("--sync-framework-packages")] 26 | [Description("Set the version also for all other packages, which are part of the same framework")] 27 | 28 | public bool SyncFrameworkPackages { get; set; } 29 | [CommandOption("--purge-packages")] 30 | [Description("Purges the PLC from packages, which are not configured")] 31 | 32 | public bool PurgePackages { get; set; } 33 | 34 | [CommandOption("--branch")] 35 | [Description("Together with 'sync-framework-packages', the preferred branch of framework packages")] 36 | 37 | public string? PreferredFrameworkBranch { get; set; } 38 | 39 | [CommandOption("--target")] 40 | [Description("Together with 'sync-framework-packages', the preferred target of framework package")] 41 | 42 | public string? PreferredFrameworkTarget { get; set; } 43 | 44 | [CommandOption("--configuration")] 45 | [Description("Together with 'sync-framework-packages', the preferred configuration of framework packages")] 46 | 47 | public string? PreferredFrameworkConfiguration { get; set; } 48 | 49 | [CommandOption("--headed")] 50 | [Description("Enables the use of the Beckhoff Automation Interface, which is required for installing and/or uninstalling packages on the target. In 'headless' mode, install operations have to be performed by Beckhoff's 'RepTool.exe'. Defaults to false")] 51 | public bool Headed { get; set; } 52 | 53 | } 54 | public override int Execute(CommandContext context, Settings settings) 55 | { 56 | SetUpLogger(settings); 57 | Initialize(settings.Headed); 58 | 59 | _twinpack.SetPackageVersionAsync(settings.Version, 60 | new SetPackageVersionOptions 61 | { 62 | PurgePackages = settings.PurgePackages, 63 | ProjectName = settings.ProjectName, 64 | PlcName = settings.PlcName, 65 | SyncFrameworkPackages = settings.SyncFrameworkPackages, 66 | PreferredFrameworkBranch = settings.PreferredFrameworkBranch, 67 | PreferredFrameworkTarget = settings.PreferredFrameworkTarget, 68 | PreferredFrameworkConfiguration = settings.PreferredFrameworkConfiguration, 69 | } 70 | ).GetAwaiter().GetResult(); 71 | 72 | return 0; 73 | } 74 | 75 | 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /TwinpackCore/Core/CancelableTask.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System; 3 | using System.Threading.Tasks; 4 | 5 | public class CancelableTask 6 | { 7 | private NLog.Logger _logger; 8 | 9 | private CancellationTokenSource _cts; 10 | private Task _task; 11 | 12 | public CancelableTask(NLog.Logger logger) 13 | { 14 | _logger = logger ?? throw new ArgumentNullException(nameof(logger)); 15 | } 16 | 17 | public void Cancel() 18 | { 19 | _cts?.Cancel(); 20 | _cts?.Dispose(); 21 | _cts = null; 22 | } 23 | 24 | public async Task RunAsync(Func action, Action onFinally = null) 25 | { 26 | _cts?.Cancel(); 27 | 28 | if (_task != null) 29 | { 30 | try 31 | { 32 | await _task; 33 | } 34 | catch { } 35 | } 36 | 37 | var wasCanceled = false; 38 | try 39 | { 40 | _cts?.Dispose(); 41 | _cts = new CancellationTokenSource(); 42 | _task = action(_cts.Token); 43 | 44 | _cts.Token.ThrowIfCancellationRequested(); 45 | await _task; 46 | _cts.Token.ThrowIfCancellationRequested(); 47 | 48 | _task = null; 49 | } 50 | catch (OperationCanceledException ex) 51 | { 52 | wasCanceled = true; 53 | _logger.Trace(ex); 54 | } 55 | catch (Exception ex) 56 | { 57 | _logger.Trace(ex); 58 | _logger.Error(ex.Message); 59 | } 60 | finally 61 | { 62 | onFinally?.Invoke(); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /TwinpackCore/Core/IAutomationInterface.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Management; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using Twinpack.Configuration; 7 | using Twinpack.Models; 8 | 9 | namespace Twinpack.Core 10 | { 11 | public interface IAutomationInterface 12 | { 13 | event EventHandler ProgressedEvent; 14 | public bool IsSupported(string tcversion); 15 | public string DefaultLibraryCachePath { get; } 16 | public string TwincatPath { get; } 17 | public string LicensesPath { get; } 18 | public string BootFolderPath { get; } 19 | public string SolutionPath { get; } 20 | public Task ResolveEffectiveVersionAsync(string projectName, string plcName, string placeholderName); 21 | public Task SetPackageVersionAsync(ConfigPlcProject package, CancellationToken cancellationToken = default); 22 | public Task IsPackageInstalledAsync(PackageItem package); 23 | public bool IsPackageInstalled(PackageItem package); 24 | public Task AddPackageAsync(PackageItem package); 25 | public Task RemovePackageAsync(PackageItem package, bool uninstall = false, bool forceRemoval = false); 26 | public Task RemoveAllPackagesAsync(string projectName, string plcName); 27 | public Task InstallPackageAsync(PackageItem package, string cachePath = null); 28 | public Task UninstallPackageAsync(PackageItem package); 29 | public Task CloseAllPackageRelatedWindowsAsync(List packages); 30 | public Task SaveAllAsync(); 31 | public void SaveAsLibrary(ConfigPlcProject plc, string filePath); 32 | } 33 | } -------------------------------------------------------------------------------- /TwinpackCore/Core/MessageFilter.cs: -------------------------------------------------------------------------------- 1 | using NLog.Filters; 2 | using System; 3 | using System.Runtime.InteropServices; 4 | 5 | 6 | namespace Twinpack.Core 7 | { 8 | // Interface for an OleMessageFilter that is used to wait for COM event. Without implementing 9 | // this interface, most commands the use the Beckhoff Automation Interface will fail due to 10 | // timing issues. 11 | [ComImport(), Guid("00000016-0000-0000-C000-000000000046"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] 12 | public interface IOleMessageFilter 13 | { 14 | [PreserveSig] 15 | int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo); 16 | [PreserveSig] 17 | int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType); 18 | [PreserveSig] 19 | int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType); 20 | } 21 | 22 | // This class is suggested by Beckhoff in order to use the Automation Interface. It is used to 23 | // synchronize the Visualstudio instance that is started within the application. 24 | public class MessageFilter : IOleMessageFilter, IDisposable 25 | { 26 | IOleMessageFilter _oldFilter; 27 | public MessageFilter() 28 | { 29 | CoRegisterMessageFilter(this, out _oldFilter); 30 | } 31 | 32 | public void Dispose() 33 | { 34 | if (_oldFilter != null) 35 | { 36 | CoRegisterMessageFilter(_oldFilter, out _); 37 | _oldFilter = null; 38 | } 39 | } 40 | 41 | int IOleMessageFilter.HandleInComingCall(int dwCallType, System.IntPtr hTaskCaller, int dwTickCount, System.IntPtr lpInterfaceInfo) 42 | { 43 | return 0; 44 | } 45 | 46 | int IOleMessageFilter.RetryRejectedCall(System.IntPtr hTaskCallee, int dwTickCount, int dwRejectType) 47 | { 48 | // Thread call was refused, try again. 49 | if (dwRejectType == 2) // flag = SERVERCALL_RETRYLATER. 50 | { 51 | // retry thread call at once, if return value >=0 & // <100. 52 | return 99; 53 | } 54 | 55 | return -1; 56 | } 57 | 58 | int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee, int dwTickCount, int dwPendingType) 59 | { 60 | //return flag PENDINGMSG_WAITDEFPROCESS. 61 | return 2; 62 | } // implement IOleMessageFilter interface 63 | 64 | [DllImport("Ole32.dll")] 65 | private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /TwinpackCore/Core/SynchronizationContextAwaiter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using System.Runtime.CompilerServices; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace Twinpack.Core 11 | { 12 | public struct SynchronizationContextAwaiter : INotifyCompletion, IEquatable 13 | { 14 | private static readonly SendOrPostCallback postCallback = state => (state as Action)?.Invoke(); 15 | 16 | private readonly SynchronizationContext context; 17 | 18 | public SynchronizationContextAwaiter(SynchronizationContext context) => this.context = context; 19 | 20 | public bool IsCompleted => context == SynchronizationContext.Current; 21 | 22 | public static bool operator !=(SynchronizationContextAwaiter left, SynchronizationContextAwaiter right) => 23 | !(left == right); 24 | 25 | public static bool operator ==(SynchronizationContextAwaiter left, SynchronizationContextAwaiter right) => 26 | left.Equals(right); 27 | 28 | public override bool Equals(object obj) => 29 | obj is SynchronizationContextAwaiter awaiter && this.Equals(awaiter); 30 | 31 | public bool Equals(SynchronizationContextAwaiter other) => 32 | EqualityComparer.Default.Equals(this.context, other.context); 33 | 34 | public override int GetHashCode() => HashCode.Combine(this.context); 35 | 36 | [SuppressMessage("Performance", "CA1822:Mark members as static", Justification = "Reviewed.")] 37 | public void GetResult() {} 38 | 39 | public void OnCompleted(Action continuation) 40 | { 41 | context.Post(postCallback, continuation); 42 | } 43 | } 44 | 45 | public static class SynchronizationContextAwaiterExtensions 46 | { 47 | public static SynchronizationContextAwaiter GetAwaiter(this SynchronizationContext context) => 48 | new SynchronizationContextAwaiter(context); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /TwinpackCore/Exceptions/Exceptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Twinpack.Exceptions 4 | { 5 | public abstract class ProjectException : Exception 6 | { 7 | public string ProjectName { get; private set; } 8 | public bool RestartHint { get; private set; } 9 | 10 | public ProjectException(string message) : base(message) 11 | { 12 | ProjectName = null; 13 | RestartHint = false; 14 | } 15 | public ProjectException(string projectName, string message, bool restartHint) : base(message) 16 | { 17 | ProjectName = projectName; 18 | RestartHint = restartHint; 19 | } 20 | } 21 | 22 | public class CompileException : ProjectException 23 | { 24 | public CompileException(string message) : base(message) { } 25 | public CompileException(string projectName, string message) : base(projectName, (projectName ?? "Solution") + ": " + message, false) { } 26 | } 27 | 28 | public class AutomationInterfaceUnresponsiveException : ProjectException 29 | { 30 | public AutomationInterfaceUnresponsiveException(string message) : base(message) {} 31 | public AutomationInterfaceUnresponsiveException(string projectName, string message, bool restartHint) : base(projectName, message, restartHint) {} 32 | } 33 | 34 | public class LibraryNotFoundException : Exception 35 | { 36 | public string Reference { get; private set; } 37 | public string Version { get; private set; } 38 | public LibraryNotFoundException(string reference, string version, string message) : base(message) 39 | { 40 | Reference = reference; 41 | Version = version; 42 | } 43 | } 44 | 45 | public class ProtocolException : Exception 46 | { 47 | public ProtocolException(string message) : base(message) 48 | { 49 | } 50 | } 51 | 52 | public class LoginException : Exception 53 | { 54 | public LoginException(string message) : base($"Login failed: {message}") 55 | { 56 | } 57 | } 58 | 59 | public class ChecksumMismatchException : Exception 60 | { 61 | public ChecksumMismatchException(string message, string expected, string actual) : base($"{message}, expected={expected}, actual={actual}") 62 | { 63 | } 64 | } 65 | 66 | public class LibraryFileInvalidException : Exception 67 | { 68 | public LibraryFileInvalidException(string message) : base(message) 69 | { 70 | } 71 | } 72 | 73 | public class PackageServerTypeException : Exception 74 | { 75 | public PackageServerTypeException(string message) : base(message) 76 | { 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /TwinpackCore/Models/Plc.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Twinpack.Models 4 | { 5 | public class Plc 6 | { 7 | public Plc(string name, string filepath) 8 | { 9 | if (!File.Exists(filepath)) 10 | throw new FileNotFoundException($"PLC {filepath} could not be found in workspace"); 11 | 12 | Name = name; 13 | FilePath = Path.GetFullPath(filepath); 14 | } 15 | 16 | public string Name { get; private set; } = null; 17 | public string FilePath { get; private set; } = null; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /TwinpackCore/Models/PlcLibrary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Text.Json.Serialization; 7 | using System.Threading.Tasks; 8 | 9 | namespace Twinpack.Models 10 | { 11 | public class AddPlcLibraryOptions 12 | { 13 | public AddPlcLibraryOptions() 14 | { 15 | LibraryReference = false; 16 | Optional = false; 17 | HideWhenReferencedAsDependency = false; 18 | PublishSymbolsInContainer = false; 19 | QualifiedOnly = false; 20 | } 21 | 22 | public AddPlcLibraryOptions(AddPlcLibraryOptions options) 23 | { 24 | if (options == null) 25 | return; 26 | 27 | LibraryReference = options.LibraryReference; 28 | Optional = options.Optional; 29 | HideWhenReferencedAsDependency = options.HideWhenReferencedAsDependency; 30 | PublishSymbolsInContainer = options.PublishSymbolsInContainer; 31 | QualifiedOnly = options.QualifiedOnly; 32 | } 33 | 34 | public AddPlcLibraryOptions CopyForDependency() 35 | { 36 | return new AddPlcLibraryOptions 37 | { 38 | LibraryReference = false, 39 | Optional = false, 40 | HideWhenReferencedAsDependency = false, 41 | PublishSymbolsInContainer = false, 42 | QualifiedOnly = this.QualifiedOnly, 43 | }; 44 | } 45 | 46 | [DefaultValue(false)] 47 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] 48 | [JsonPropertyName("library-reference")] 49 | public bool LibraryReference { get; set; } 50 | 51 | [DefaultValue(false)] 52 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] 53 | [JsonPropertyName("optional")] 54 | public bool Optional { get; set; } 55 | 56 | [DefaultValue(false)] 57 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] 58 | [JsonPropertyName("hide")] 59 | public bool HideWhenReferencedAsDependency { get; set; } 60 | 61 | [DefaultValue(false)] 62 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] 63 | [JsonPropertyName("publish-all-symbols")] 64 | public bool PublishSymbolsInContainer { get; set; } 65 | 66 | [DefaultValue(false)] 67 | [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] 68 | [JsonPropertyName("qualified-only")] 69 | public bool QualifiedOnly { get; set; } 70 | } 71 | public class PlcLibrary 72 | { 73 | public string Name { get; set; } 74 | public string Version { get; set; } 75 | public string DistributorName { get; set; } 76 | public AddPlcLibraryOptions Options { get; set; } 77 | public static bool operator ==(PlcLibrary lhs, PlcLibrary rhs) 78 | { 79 | return lhs?.Name == rhs?.Name && lhs?.Name == rhs?.Name && lhs?.Version == rhs?.Version; 80 | } 81 | public static bool operator !=(PlcLibrary lhs, PlcLibrary rhs) 82 | { 83 | return !(lhs == rhs); 84 | } 85 | public override bool Equals(object o) 86 | { 87 | return this == o as PlcLibrary; 88 | } 89 | public override int GetHashCode() 90 | { 91 | return 17 + Name?.GetHashCode() ?? 0 + 23 * Version?.GetHashCode() ?? 0 + 23 * DistributorName?.GetHashCode() ?? 0; 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /TwinpackCore/Models/PlcVersion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Twinpack.Models 8 | { 9 | public class PlcVersion 10 | { 11 | public string Version { get; set; } 12 | public string VersionDisplayText { get; set; } 13 | 14 | public static bool operator ==(PlcVersion lhs, PlcVersion rhs) 15 | { 16 | return lhs?.Version == rhs?.Version && 17 | lhs?.VersionDisplayText == rhs?.VersionDisplayText; 18 | } 19 | 20 | public static bool operator !=(PlcVersion lhs, PlcVersion rhs) 21 | { 22 | return !(lhs == rhs); 23 | } 24 | 25 | public override bool Equals(object o) 26 | { 27 | return this == o as PlcVersion; 28 | } 29 | 30 | public override int GetHashCode() 31 | { 32 | return 23 * Version?.GetHashCode() + 48 * VersionDisplayText?.GetHashCode() ?? 0; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /TwinpackCore/Models/Project.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Xml.Linq; 5 | 6 | namespace Twinpack.Models 7 | { 8 | public class Project 9 | { 10 | public Project(string name, List plcs) 11 | { 12 | Name = name; 13 | Plcs = plcs; 14 | } 15 | 16 | public Project(string name, string filepath) 17 | { 18 | if (!File.Exists(filepath)) 19 | throw new FileNotFoundException($"Project {filepath} could not be found in workspace"); 20 | 21 | var directory = new FileInfo(filepath).Directory.FullName; 22 | var content = XDocument.Load(filepath); 23 | 24 | Name = name; 25 | Plcs = content?.Root?.Elements("Project")?.Elements("Plc")?.Elements("Project") 26 | .Where(x => x.Attribute("Name")?.Value != null && x.Attribute("PrjFilePath")?.Value != null) 27 | .Select(x => new Plc(x.Attribute("Name")?.Value, $@"{directory}\{x.Attribute("PrjFilePath")?.Value}")).ToList(); 28 | 29 | var xtis = content?.Root?.Elements("Project")?.Elements("Plc")?.Elements("Project") 30 | .Where(x => x.Attribute("File")?.Value != null) 31 | .Select(x => $"{directory}\\_Config\\PLC\\" + x.Attribute("File")?.Value).ToList(); 32 | 33 | foreach(var xti in xtis) 34 | { 35 | if (!File.Exists(xti)) 36 | throw new FileNotFoundException($"XTI {xti} could not be found in workspace"); 37 | 38 | content = XDocument.Load(xti); 39 | directory = new FileInfo(xti).Directory.FullName; 40 | 41 | Plcs = Plcs.Concat(content?.Root?.Elements("Project")? 42 | .Where(x => x.Attribute("Name")?.Value != null && x.Attribute("PrjFilePath")?.Value != null) 43 | .Select(x => new Plc(x.Attribute("Name")?.Value, $@"{directory}\{x.Attribute("PrjFilePath")?.Value}"))).ToList(); 44 | } 45 | } 46 | 47 | public string Name { get; private set; } = null; 48 | public List Plcs { get; private set; } = new List(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /TwinpackCore/Models/Solution.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Text.RegularExpressions; 5 | 6 | namespace Twinpack.Models 7 | { 8 | public class Solution 9 | { 10 | public Solution() 11 | { 12 | 13 | } 14 | 15 | public Solution(string name, List projects) 16 | { 17 | Name = name; 18 | Projects = projects; 19 | } 20 | 21 | public static Solution LoadFromFile(string filepath) 22 | { 23 | var solution = new Solution(); 24 | solution.Load(filepath); 25 | return solution; 26 | } 27 | 28 | public void Load(string filepath) 29 | { 30 | if (!File.Exists(filepath)) 31 | throw new FileNotFoundException($"Solution {filepath} could not be found in workspace"); 32 | 33 | var directory = new FileInfo(filepath).Directory.FullName; 34 | var content = File.ReadAllText(filepath); 35 | 36 | //Project("{DFBE7525-6864-4E62-8B2E-D530D69D9D96}") = "ZApplication", "ZApplication.tspproj", "{55567FAF-D581-431A-8E43-734906367EA7}" 37 | var projectMatches = Regex.Matches(content ?? "", $"Project\\(.*?\\)\\s*=\\s*\"(.*?)\"\\s*,\\s*\"(.*?ts[p]?proj)\"\\s*,.*"); 38 | 39 | Name = Path.GetFileNameWithoutExtension(filepath); 40 | Projects = projectMatches.Cast() 41 | .Where(x => File.Exists(directory + "\\" + x.Groups[2].Value)) 42 | .Select(x => new Project(x.Groups[1].Value, directory + "\\" + x.Groups[2].Value)) 43 | .ToList(); 44 | } 45 | 46 | public string Name { get; private set; } = null; 47 | public IEnumerable Projects { get; private set; } = new List(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /TwinpackCore/Models/SourceRepositories.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Text.Json.Serialization; 5 | 6 | namespace Twinpack.Models 7 | { 8 | public class SourceRepositories 9 | { 10 | [JsonPropertyName("repositories")] 11 | public List PackagingServers { get; set; } = new List(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /TwinpackCore/Protocol/IPackageServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Twinpack.Models; 5 | using Twinpack.Protocol.Api; 6 | using System.Threading; 7 | 8 | namespace Twinpack.Protocol 9 | { 10 | public enum ChecksumMode 11 | { 12 | IgnoreMismatch, 13 | IgnoreMismatchAndFallack, 14 | Throw 15 | } 16 | 17 | public interface IPackageServer 18 | { 19 | string ServerType { get; } 20 | string Name { get; set; } 21 | string UrlBase { get; set; } 22 | string Url { get; } 23 | string UrlRegister { get; } 24 | Task, bool>> GetCatalogAsync(string search, int page = 1, int perPage = 5, CancellationToken cancellationToken = default); 25 | Task, bool>> GetPackageVersionsAsync(PlcLibrary library, string branch = null, string configuration = null, string target = null, int page = 1, int perPage = 5, CancellationToken cancellationToken = default); 26 | Task ResolvePackageVersionAsync(PlcLibrary library, string preferredTarget = null, string preferredConfiguration = null, string preferredBranch = null, CancellationToken cancellationToken = default); 27 | Task DownloadPackageVersionAsync(PackageVersionGetResponse packageVersion, ChecksumMode checksumMode, string downloadPath = null, CancellationToken cancellationToken = default); 28 | Task GetPackageVersionAsync(PlcLibrary library, string branch, string configuration, string target, CancellationToken cancellationToken = default); 29 | Task GetPackageAsync(string distributorName, string packageName, CancellationToken cancellationToken = default); 30 | Task PutPackageAsync(PackagePatchRequest package, CancellationToken cancellationToken = default); 31 | Task PostPackageVersionAsync(PackageVersionPostRequest packageVersion, CancellationToken cancellationToken = default); 32 | Task PutPackageVersionAsync(PackageVersionPatchRequest package, CancellationToken cancellationToken = default); 33 | Task LoginAsync(string username = null, string password = null, CancellationToken cancellationToken = default); 34 | string Username { get; set; } 35 | string Password { get; set; } 36 | LoginPostResponse UserInfo { get; } 37 | bool LoggedIn { get; } 38 | bool Connected { get; } 39 | Task LogoutAsync(); 40 | void InvalidateCache(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /TwinpackCore/Protocol/Native/CachedHttpClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using System.Net.NetworkInformation; 4 | using System.Runtime.Caching; // Add a reference to System.Runtime.Caching.dll 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Twinpack.Protocol 9 | { 10 | public class CachedHttpClient : HttpClient 11 | { 12 | private readonly ObjectCache _cache; 13 | 14 | public CachedHttpClient() : base() 15 | { 16 | _cache = MemoryCache.Default; 17 | } 18 | 19 | public async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken = default, TimeSpan? cacheDuration = null) 20 | { 21 | try 22 | { 23 | if (!NetworkInterface.GetIsNetworkAvailable()) 24 | throw new HttpRequestException("No internet connection! Please check your connection."); 25 | 26 | if (request.Method != HttpMethod.Get && request.Method != HttpMethod.Head) 27 | { 28 | return (await base.SendAsync(request, cancellationToken)).EnsureSuccessStatusCode(); 29 | } 30 | 31 | cacheDuration = cacheDuration ?? TimeSpan.FromHours(12); 32 | 33 | var url = request.RequestUri.ToString(); 34 | if (_cache.Contains(url)) 35 | { 36 | return _cache.Get(url) as HttpResponseMessage; 37 | } 38 | 39 | var response = await base.SendAsync(request, cancellationToken); 40 | response.EnsureSuccessStatusCode(); 41 | 42 | var cacheItemPolicy = new CacheItemPolicy 43 | { 44 | AbsoluteExpiration = DateTimeOffset.Now.Add((TimeSpan)cacheDuration) 45 | }; 46 | _cache.Set(url, response, cacheItemPolicy); 47 | return response; 48 | } 49 | catch (TaskCanceledException ex) 50 | { 51 | if (cancellationToken.IsCancellationRequested) 52 | throw; 53 | 54 | throw new TimeoutException($"Timed out during request to {request.RequestUri.Scheme}://{request.RequestUri.Host}\nTwinpack Server is unresponsive! There might be no internet connection or the server may be temporarily down! " + 55 | "Please check your connection or try again later."); 56 | } 57 | finally 58 | { 59 | } 60 | } 61 | 62 | public void Invalidate() 63 | { 64 | try 65 | { 66 | foreach (var item in _cache) 67 | _cache.Remove(item.Key); 68 | } 69 | finally 70 | { 71 | } 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /TwinpackCore/Protocol/Nuget/BeckhoffServer.cs: -------------------------------------------------------------------------------- 1 | using NLog; 2 | using System.Threading.Tasks; 3 | using Twinpack.Models; 4 | using Twinpack.Protocol.Api; 5 | using System.Threading; 6 | using System.Linq; 7 | using NuGet.Protocol.Core.Types; 8 | using NuGet.Packaging.Core; 9 | 10 | namespace Twinpack.Protocol 11 | { 12 | class BeckhoffPackagingServerFactory : IPackagingServerFactory 13 | { 14 | public IPackageServer Create(string name, string uri) 15 | { 16 | return new BeckhoffServer(name, uri); 17 | } 18 | 19 | public string ServerType { get; } = "Beckhoff Repository"; 20 | } 21 | 22 | public class BeckhoffServer : NugetServer, IPackageServer 23 | { 24 | public const string DefaultUrlBase = "https://public.tcpkg.beckhoff-cloud.com/api/v1/feeds/stable/"; 25 | 26 | private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); 27 | public new string ServerType { get; } = "Beckhoff Repository"; 28 | protected override string SearchPrefix { get => "tags:library "; } 29 | public override string UrlRegister 30 | { 31 | get => "https://www.beckhoff.com/en-en/mybeckhoff-registration/index.aspx"; 32 | } 33 | protected override string IconUrl { get => "https://twinpack.dev/Icons/beckhoff.png"; } 34 | 35 | public BeckhoffServer(string name = "", string url = DefaultUrlBase) : base(name, url) 36 | { 37 | 38 | } 39 | 40 | public override async Task ResolvePackageVersionAsync(PlcLibrary library, string preferredTarget = null, string preferredConfiguration = null, string preferredBranch = null, CancellationToken cancellationToken = default) 41 | { 42 | if(!library.Name.StartsWith("TwinCAT.XAE.PLC.Lib.")) 43 | library.Name = "TwinCAT.XAE.PLC.Lib." + library.Name; 44 | 45 | return await base.ResolvePackageVersionAsync(library, preferredTarget, preferredConfiguration, preferredBranch, cancellationToken); 46 | } 47 | 48 | #if !NETSTANDARD2_1_OR_GREATER 49 | protected override async Task GetPackageIconAsync(PackageIdentity identity, CancellationToken cancellationToken) 50 | { 51 | // Beckhoff Packages come without Icons 52 | return null; 53 | } 54 | #endif 55 | #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously 56 | protected override async Task EvaluateTitleAsync(IPackageSearchMetadata package, CancellationToken cancellationToken) 57 | #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously 58 | { 59 | // heuristics for the actual title of the package, needed for Beckhoff, because there is no metadata, which gives the real name of the library 60 | var title = package.Identity.Id; 61 | var tags = package.Tags.Split(' '); 62 | var libraryIdx = tags.ToList().IndexOf("Library"); 63 | if (libraryIdx > 0 && tags.Length > libraryIdx + 1 && title.Contains(tags[libraryIdx + 1])) 64 | title = tags[libraryIdx + 1]; 65 | 66 | return title; 67 | } 68 | 69 | protected override int EvaluateCompiled(string tags) 70 | { 71 | return 1; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /TwinpackCore/Protocol/PackagingServer.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace Twinpack.Models 5 | { 6 | public class PackagingServer : INotifyPropertyChanged 7 | { 8 | public event PropertyChangedEventHandler PropertyChanged; 9 | 10 | public string Name { get; set; } 11 | public string Url { get; set; } 12 | public string ServerType { get; set; } 13 | 14 | bool _loggedIn; 15 | [JsonIgnore(Condition = JsonIgnoreCondition.Always)] 16 | public bool LoggedIn 17 | { 18 | get 19 | { 20 | return _loggedIn && _connected && !_connecting; 21 | } 22 | set 23 | { 24 | _loggedIn = value; 25 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(LoggedIn))); 26 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(LoggedOut))); 27 | } 28 | } 29 | 30 | [JsonIgnore(Condition = JsonIgnoreCondition.Always)] 31 | public bool LoggedOut 32 | { 33 | get 34 | { 35 | return !_loggedIn && !_connecting; 36 | } 37 | } 38 | 39 | bool _connected; 40 | [JsonIgnore(Condition = JsonIgnoreCondition.Always)] 41 | public bool Connected 42 | { 43 | get 44 | { 45 | return _connected; 46 | } 47 | set 48 | { 49 | _connected = value; 50 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Connected))); 51 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(LoggedIn))); 52 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(LoggedOut))); 53 | } 54 | } 55 | 56 | bool _connecting; 57 | [JsonIgnore(Condition = JsonIgnoreCondition.Always)] 58 | public bool Connecting 59 | { 60 | get 61 | { 62 | return _connecting; 63 | } 64 | set 65 | { 66 | _connecting = value; 67 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Connecting))); 68 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(LoggedIn))); 69 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(LoggedOut))); 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /TwinpackCore/Protocol/PackagingServerFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Twinpack.Protocol 2 | { 3 | interface IPackagingServerFactory 4 | { 5 | IPackageServer Create(string name, string uri); 6 | string ServerType { get; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /TwinpackCore/TwinpackCore.projitems: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | true 6 | 963dd6a7-e9d8-4386-92e0-8b39cb7d8e9c 7 | 8 | 9 | TwinpackShared 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /TwinpackCore/TwinpackCore.shproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 963dd6a7-e9d8-4386-92e0-8b39cb7d8e9c 5 | 14.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /TwinpackCore/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /TwinpackRegistry/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /TwinpackRegistry/Commands/Command.cs: -------------------------------------------------------------------------------- 1 | using NLog; 2 | using System; 3 | using System.Threading.Tasks; 4 | 5 | 6 | namespace Twinpack.Commands 7 | { 8 | public abstract class Command 9 | { 10 | protected static readonly Logger _logger = LogManager.GetCurrentClassLogger(); 11 | 12 | protected Protocol.TwinpackServer _twinpackServer = new Protocol.TwinpackServer(); 13 | protected Protocol.BeckhoffServer _beckhoffServer = new Protocol.BeckhoffServer(); 14 | 15 | protected async Task LoginAsync(string username, string password, string beckhoffUsername, string beckhoffPassword) 16 | { 17 | await _twinpackServer.LoginAsync(username, password); 18 | if (!_twinpackServer.Connected) 19 | throw new Exception("Login to Twinpack Repository failed!"); 20 | 21 | 22 | if (beckhoffUsername != null && beckhoffPassword != null) 23 | { 24 | await _beckhoffServer.LoginAsync(beckhoffUsername, beckhoffPassword); 25 | if (!_beckhoffServer.Connected) 26 | throw new Exception("Login to Beckhoff Repository failed!"); 27 | } 28 | } 29 | 30 | public abstract Task ExecuteAsync(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /TwinpackRegistry/Commands/DumpCommand.cs: -------------------------------------------------------------------------------- 1 | using CommandLine; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO.Compression; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace Twinpack.Commands 11 | { 12 | [Verb("dump", HelpText = "")] 13 | public class DumpCommand : Command 14 | { 15 | [Option('p', "path", Required = false, Default = "Twinpack-Registry", HelpText = "")] 16 | public string Path { get; set; } 17 | 18 | public override async Task ExecuteAsync() 19 | { 20 | _logger.Info(">>> twinpack-registry:dump"); 21 | 22 | foreach (var file in Directory.GetFiles(Path)) 23 | { 24 | try 25 | { 26 | using (var memoryStream = new MemoryStream(File.ReadAllBytes(file))) 27 | using (var zipArchive = new ZipArchive(memoryStream)) 28 | { 29 | var libraryInfo = LibraryReader.Read(File.ReadAllBytes(file), dumpFilenamePrefix: file); 30 | } 31 | } 32 | catch (Exception) { } 33 | } 34 | return 0; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /TwinpackRegistry/Commands/PullCommand.cs: -------------------------------------------------------------------------------- 1 | using CommandLine; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Twinpack.Configuration; 8 | using Twinpack.Models; 9 | using Twinpack.Protocol; 10 | 11 | namespace Twinpack.Commands 12 | { 13 | [Verb("pull", HelpText = "Downloads packages that are references in .Zeugwerk/config.json to .Zeugwerk/libraries, you can use RepTool.exe to install them into the TwinCAT library repository.")] 14 | public class PullCommand : Command 15 | { 16 | [Option('u', "username", Required = false, Default = null, HelpText = "Username for Twinpack Repository")] 17 | public string Username { get; set; } 18 | 19 | [Option('p', "password", Required = false, Default = null, HelpText = "Password for Twinpack Repository")] 20 | public string Password { get; set; } 21 | 22 | [Option("beckhoff-username", Required = false, Default = null, HelpText = "Username for Beckhoff Repository")] 23 | public string BeckhoffUsername { get; set; } 24 | 25 | [Option("beckhoff-password", Required = false, Default = null, HelpText = "Password for Beckhoff Repository")] 26 | public string BeckhoffPassword { get; set; } 27 | 28 | [Option('r', "owner", Required = false, Default = "Zeugwerk", HelpText = "")] 29 | public string RegistryOwner { get; set; } 30 | 31 | [Option('r', "name", Required = false, Default = "Twinpack-Registry", HelpText = "")] 32 | public string RegistryName { get; set; } 33 | 34 | [Option('d', "dry-run", Required = false, Default = false, HelpText = "")] 35 | public bool DryRun { get; set; } 36 | 37 | [Option('D', "dump", Required = false, Default = false, HelpText = "")] 38 | public bool Dump { get; set; } 39 | 40 | [Option('t', "token", Required = false, Default = false, HelpText = "")] 41 | public string Token { get; set; } 42 | 43 | public override async Task ExecuteAsync() 44 | { 45 | _logger.Info(">>> twinpack-registry:pull"); 46 | 47 | var registry = new TwinpackRegistry(new List { _twinpackServer, _beckhoffServer }); 48 | await LoginAsync(Username, Password, BeckhoffUsername, BeckhoffPassword); 49 | 50 | 51 | _logger.Info(new string('-', 3) + $" download"); 52 | await registry.DownloadAsync(RegistryOwner, RegistryName, token: Token); 53 | 54 | var plcs = ConfigPlcProjectFactory.PlcProjectsFromConfig(compiled: false, target: "TC3.1"); 55 | if (!DryRun && plcs.Any()) 56 | { 57 | _logger.Info(new string('-', 3) + $" push"); 58 | await _twinpackServer.PushAsync(plcs, "Release", "main", "TC3.1", null, false); 59 | } 60 | 61 | return 0; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /TwinpackRegistry/Commands/UpdateDownloads.cs: -------------------------------------------------------------------------------- 1 | using CommandLine; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Twinpack.Protocol; 8 | 9 | namespace Twinpack.Commands 10 | { 11 | [Verb("update-downloads", HelpText = "Downloads packages that are references in .Zeugwerk/config.json to .Zeugwerk/libraries, you can use RepTool.exe to install them into the TwinCAT library repository.")] 12 | public class UpdateDownloadsCommand : Command 13 | { 14 | [Option('u', "username", Required = false, Default = null, HelpText = "Username for Twinpack Server")] 15 | public string Username { get; set; } 16 | 17 | [Option('p', "password", Required = false, Default = null, HelpText = "Password for Twinpack Server")] 18 | public string Password { get; set; } 19 | [Option("beckhoff-username", Required = false, Default = null, HelpText = "Username for Beckhoff Repository")] 20 | public string BeckhoffUsername { get; set; } 21 | 22 | [Option("beckhoff-password", Required = false, Default = null, HelpText = "Password for Beckhoff Repository")] 23 | public string BeckhoffPassword { get; set; } 24 | 25 | [Option('r', "owner", Required = false, Default = "Zeugwerk", HelpText = "")] 26 | public string RegistryOwner { get; set; } 27 | 28 | [Option('r', "name", Required = false, Default = "Twinpack-Registry", HelpText = "")] 29 | public string RegistryName { get; set; } 30 | 31 | [Option('d', "dry-run", Required = false, Default = false, HelpText = "")] 32 | public bool DryRun { get; set; } 33 | 34 | [Option('t', "token", Required = false, Default = null, HelpText = "")] 35 | public string Token { get; set; } 36 | 37 | public override async Task ExecuteAsync() 38 | { 39 | _logger.Info(">>> twinpack-registry:update-downloads"); 40 | var registry = new TwinpackRegistry(new List { _twinpackServer, _beckhoffServer }); 41 | 42 | await LoginAsync(Username, Password, BeckhoffUsername, BeckhoffPassword); 43 | await registry.UpdateDownloadsAsync(RegistryOwner, RegistryName, token: Token, dryRun: DryRun); 44 | 45 | return 0; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /TwinpackRegistry/IconUtils.cs: -------------------------------------------------------------------------------- 1 | using Jdenticon.Rendering; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Twinpack 10 | { 11 | public class IconUtils 12 | { 13 | public static byte[] GenerateIdenticon(string packageName) 14 | { 15 | var size = 128; 16 | var renderer = new PngRenderer(size, size); 17 | var icon = Jdenticon.Identicon.FromValue(packageName, size); 18 | 19 | icon.Style = new Jdenticon.IdenticonStyle 20 | { 21 | Hues = new HueCollection { { 116, HueUnit.Degrees } }, 22 | BackColor = Color.Transparent, 23 | ColorLightness = Jdenticon.Range.Create(0.37f, 0.37f), 24 | GrayscaleLightness = Jdenticon.Range.Create(0.37f, 0.37f), 25 | ColorSaturation = 0.26f, 26 | GrayscaleSaturation = 0.26f 27 | }; 28 | 29 | icon.Draw(renderer); 30 | 31 | using (var stream = new MemoryStream()) 32 | { 33 | renderer.SavePng(stream); 34 | return stream.ToArray(); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /TwinpackRegistry/NLog.dll.nlog: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /TwinpackRegistry/Program.cs: -------------------------------------------------------------------------------- 1 | using CommandLine; 2 | using NLog; 3 | using System; 4 | using System.Threading.Tasks; 5 | using Twinpack.Commands; 6 | using Twinpack.Protocol; 7 | 8 | namespace Twinpack 9 | { 10 | class Program 11 | { 12 | private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); 13 | 14 | 15 | [STAThread] 16 | static int Main(string[] args) 17 | { 18 | _logger.Info("-------------------------------------------------------------------------"); 19 | _logger.Info($"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name} {System.Reflection.Assembly.GetExecutingAssembly().GetName().Version}"); 20 | _logger.Info("-------------------------------------------------------------------------"); 21 | 22 | LogManager.Setup(); 23 | var watch = System.Diagnostics.Stopwatch.StartNew(); 24 | 25 | try 26 | { 27 | return Parser.Default.ParseArguments(args) 28 | .MapResult( 29 | (PullCommand command) => ExecuteAsync(command).GetAwaiter().GetResult(), 30 | (UpdateDownloadsCommand command) => ExecuteAsync(command).GetAwaiter().GetResult(), 31 | (DumpCommand command) => ExecuteAsync(command).GetAwaiter().GetResult(), 32 | errs => 1); 33 | } 34 | catch (Exception ex) 35 | { 36 | _logger.Error(ex); 37 | 38 | _logger.Info("-------------------------------------------------------------------------"); 39 | _logger.Info("FAILED"); 40 | _logger.Info("-------------------------------------------------------------------------"); 41 | 42 | throw; 43 | } 44 | finally 45 | { 46 | PackagingServerRegistry.PurgeAsync().GetAwaiter().GetResult(); 47 | watch.Stop(); 48 | TimeSpan ts = watch.Elapsed; 49 | string elapsedTime = string.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10); 50 | 51 | _logger.Info($"Total time: {elapsedTime}"); 52 | _logger.Info($"Finished at: {DateTime.Now.ToString("dd-MM-yyyy HH:mm:ss")}"); 53 | _logger.Info("-------------------------------------------------------------------------"); 54 | } 55 | } 56 | 57 | private static async Task ExecuteAsync(T command) where T : Commands.Command 58 | { 59 | var exitCode = await command.ExecuteAsync(); 60 | 61 | if (exitCode == 0) 62 | { 63 | _logger.Info("-------------------------------------------------------------------------"); 64 | _logger.Info("SUCCESS"); 65 | _logger.Info("-------------------------------------------------------------------------"); 66 | } 67 | 68 | return exitCode; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /TwinpackRegistry/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TwinpackRegistry")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TwinpackRegistry")] 13 | [assembly: AssemblyCopyright("Copyright © 2023")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("dbbe37c9-0a4d-4eb9-b965-e03431a2fe43")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | //[assembly: AssemblyVersion("1.0.0.0")] 36 | //[assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /TwinpackRegistry/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TwinpackTests/AddPlcLibraryOptionsTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.IO.Compression; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using Twinpack.Models; 9 | using Twinpack.Protocol; 10 | 11 | namespace TwinpackTests 12 | { 13 | [TestClass] 14 | public class AddPlcLibraryOptionsTest 15 | { 16 | [TestMethod] 17 | public async Task CopyOptionForDependency() 18 | { 19 | var options = new AddPlcLibraryOptions 20 | { 21 | LibraryReference = true, 22 | Optional = true, 23 | HideWhenReferencedAsDependency = true, 24 | PublishSymbolsInContainer = true, 25 | QualifiedOnly = true, 26 | }; 27 | 28 | var dependencyOption = options.CopyForDependency(); 29 | 30 | Assert.AreEqual(false, dependencyOption.LibraryReference); 31 | Assert.AreEqual(false, dependencyOption.Optional); 32 | Assert.AreEqual(false, dependencyOption.HideWhenReferencedAsDependency); 33 | Assert.AreEqual(false, dependencyOption.PublishSymbolsInContainer); 34 | Assert.AreEqual(true, dependencyOption.QualifiedOnly); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /TwinpackTests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("TwinpackTests")] 6 | [assembly: AssemblyDescription("")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("")] 9 | [assembly: AssemblyProduct("TwinpackTests")] 10 | [assembly: AssemblyCopyright("Copyright © 2023")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | 14 | [assembly: ComVisible(false)] 15 | 16 | [assembly: Guid("c9f03787-eacd-4120-83e4-414ab7a8c695")] 17 | 18 | // [assembly: AssemblyVersion("1.0.*")] 19 | [assembly: AssemblyVersion("1.0.0.0")] 20 | [assembly: AssemblyFileVersion("1.0.0.0")] 21 | -------------------------------------------------------------------------------- /TwinpackTests/assets/Flat.libcat.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | d5875a42-817d-44f3-8af7-6c59eaddc95f 5 | 1.1.2.3 6 | MyParent1 7 | 8 | -------------------------------------------------------------------------------- /TwinpackTests/assets/LibCat1_All_selected.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/LibCat1_All_selected.library -------------------------------------------------------------------------------- /TwinpackTests/assets/LibCat4P_1_selected.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/LibCat4P_1_selected.library -------------------------------------------------------------------------------- /TwinpackTests/assets/LibCat4_All_selected.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/LibCat4_All_selected.library -------------------------------------------------------------------------------- /TwinpackTests/assets/LibCat4_MyParent_selected.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/LibCat4_MyParent_selected.library -------------------------------------------------------------------------------- /TwinpackTests/assets/TestSolution/.Zeugwerk/libraries/TC3.1/PlcLibrary1_1.2.3.3.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/TestSolution/.Zeugwerk/libraries/TC3.1/PlcLibrary1_1.2.3.3.library -------------------------------------------------------------------------------- /TwinpackTests/assets/TestSolution/.Zeugwerk/libraries/TC3.1/PlcLibrary1_1.2.3.4.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/TestSolution/.Zeugwerk/libraries/TC3.1/PlcLibrary1_1.2.3.4.library -------------------------------------------------------------------------------- /TwinpackTests/assets/TestSolution/TestLibraryProject/TestLibraryProject.tsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /TwinpackTests/assets/TestSolution/TestLibraryProject/_Boot/TargetDescription.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 3 5 | 1 6 | 4024 7 | 55 8 | 9 | 10 | -------------------------------------------------------------------------------- /TwinpackTests/assets/TestSolution/TestProject/Plc1/POUs/MAIN.TcPOU: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /TwinpackTests/assets/TestSolution/TestProject/Plc1/PlcTask.TcTTO: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 10000 6 | 20 7 | 8 | MAIN 9 | 10 | {806330ea-a83c-4cf0-97a9-d1c0f1d3a39e} 11 | {8a1a742f-00fb-4d41-9f51-2b21db55ea0a} 12 | {e4defeef-fe49-4eb7-bb9c-41a635e83f49} 13 | {bdfbe57b-0039-4be5-bc04-57f608bb9e9b} 14 | {48523fa5-f2a3-41da-a6bd-4952901793dc} 15 | 16 | 17 | -------------------------------------------------------------------------------- /TwinpackTests/assets/TestSolution/TestProject/Plc1/_Libraries/beckhoff automation gmbh/tc2_standard/3.3.3.0/tc2_standard.compiled-library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/TestSolution/TestProject/Plc1/_Libraries/beckhoff automation gmbh/tc2_standard/3.3.3.0/tc2_standard.compiled-library -------------------------------------------------------------------------------- /TwinpackTests/assets/TestSolution/TestProject/Plc1/_Libraries/beckhoff automation gmbh/tc2_system/3.6.2.0/tc2_system.compiled-library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/TestSolution/TestProject/Plc1/_Libraries/beckhoff automation gmbh/tc2_system/3.6.2.0/tc2_system.compiled-library -------------------------------------------------------------------------------- /TwinpackTests/assets/TestSolution/TestProject/Plc1/_Libraries/beckhoff automation gmbh/tc3_module/3.3.23.0/tc3_module.compiled-library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/TestSolution/TestProject/Plc1/_Libraries/beckhoff automation gmbh/tc3_module/3.3.23.0/tc3_module.compiled-library -------------------------------------------------------------------------------- /TwinpackTests/assets/TestSolution/TestProject/TestProject.tsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | PlcTask 8 | 9 | 10 | 11 | 12 | 13 | 14 | Plc1 Instance 15 | {08500001-0000-0000-F000-000000000064} 16 | 17 | 18 | 1 19 | Default 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /TwinpackTests/assets/TestSolution/TestProject/_Boot/TargetDescription.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 3 5 | 1 6 | 4024 7 | 35 8 | 9 | 10 | -------------------------------------------------------------------------------- /TwinpackTests/assets/TestSolution/TestProject2/PlcLibrary1/POUs/MAIN.TcPOU: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /TwinpackTests/assets/TestSolution/TestProject2/PlcLibrary1/_Libraries/beckhoff automation gmbh/tc2_standard/3.3.3.0/tc2_standard.compiled-library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/TestSolution/TestProject2/PlcLibrary1/_Libraries/beckhoff automation gmbh/tc2_standard/3.3.3.0/tc2_standard.compiled-library -------------------------------------------------------------------------------- /TwinpackTests/assets/TestSolution/TestProject2/PlcLibrary1/_Libraries/beckhoff automation gmbh/tc2_system/3.6.2.0/tc2_system.compiled-library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/TestSolution/TestProject2/PlcLibrary1/_Libraries/beckhoff automation gmbh/tc2_system/3.6.2.0/tc2_system.compiled-library -------------------------------------------------------------------------------- /TwinpackTests/assets/TestSolution/TestProject2/PlcLibrary1/_Libraries/beckhoff automation gmbh/tc3_module/3.3.23.0/tc3_module.compiled-library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/TestSolution/TestProject2/PlcLibrary1/_Libraries/beckhoff automation gmbh/tc3_module/3.3.23.0/tc3_module.compiled-library -------------------------------------------------------------------------------- /TwinpackTests/assets/TestSolution/TestProject2/TestProject2.tspproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled1_4024_35_minimal_infos_123.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/Untitled1_4024_35_minimal_infos_123.library -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled1_4024_35_minimal_infos_1234.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/Untitled1_4024_35_minimal_infos_1234.library -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled1_4024_35_minimal_infos_123_n.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/Untitled1_4024_35_minimal_infos_123_n.library -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled1_4024_35_minimal_infos_123_np.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/Untitled1_4024_35_minimal_infos_123_np.library -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled1_4024_35_minimal_infos_123_npa.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/Untitled1_4024_35_minimal_infos_123_npa.library -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled1_4024_35_minimal_infos_123_npad.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/Untitled1_4024_35_minimal_infos_123_npad.library -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled1_4024_35_minimal_infos_123_rnpad.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/Untitled1_4024_35_minimal_infos_123_rnpad.library -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled1_4024_53_minimal_infos_12.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/Untitled1_4024_53_minimal_infos_12.library -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled1_4024_53_minimal_infos_123.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/Untitled1_4024_53_minimal_infos_123.library -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled1_4024_53_minimal_infos_1234.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/Untitled1_4024_53_minimal_infos_1234.library -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled1_4026_00_minimal_infos_123.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/Untitled1_4026_00_minimal_infos_123.library -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled1_libcat_child.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/Untitled1_libcat_child.library -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled1_libcat_child2.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/Untitled1_libcat_child2.library -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled1_libcat_onlychild_selected.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/Untitled1_libcat_onlychild_selected.library -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled2.libcat.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | d5875a42-817d-44f3-8af7-6c59eaddc95f 5 | 1.1.2.3 6 | MyParent1 7 | 8 | 9 | 426a88ee-bb19-4918-8554-e1c9d24621f9 10 | 1.1.2.4 11 | MyParent2 12 | 13 | 14 | d5876a42-817d-44f3-8af7-6c59eaddc95f 15 | 2.1.2.3 16 | MyParent3 17 | 18 | 19 | 426a88ee-bb19-4918-8554-e1d9d24621f9 20 | 2.1.2.4 21 | MyParent4 22 | 23 | -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled2_libcat.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/Untitled2_libcat.library -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled2_libcat_1234.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/Untitled2_libcat_1234.library -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled2_libcat_sub.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/Untitled2_libcat_sub.library -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled2_minimal_infos_libcat_1234.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/Untitled2_minimal_infos_libcat_1234.library -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled2_minimal_infos_libcat_mine_1234.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/Untitled2_minimal_infos_libcat_mine_1234.library -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled2_minimal_infos_libcat_mine_theirs_1234.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/Untitled2_minimal_infos_libcat_mine_theirs_1234.library -------------------------------------------------------------------------------- /TwinpackTests/assets/Untitled2_minimal_infos_libcat_mine_theirs_yours_1234.library: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Zeugwerk/Twinpack/8a2224c24e4559735309f10f6a595ec9e9ff5a5a/TwinpackTests/assets/Untitled2_minimal_infos_libcat_mine_theirs_yours_1234.library -------------------------------------------------------------------------------- /TwinpackVsix.15/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Shell; 2 | using System.Reflection; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | 6 | // General Information about an assembly is controlled through the following 7 | // set of attributes. Change these attribute values to modify the information 8 | // associated with an assembly. 9 | [assembly: AssemblyTitle("Twinpack")] 10 | [assembly: AssemblyDescription("")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyCompany("")] 13 | [assembly: AssemblyProduct("Twinpack")] 14 | [assembly: AssemblyCopyright("")] 15 | [assembly: AssemblyTrademark("")] 16 | [assembly: AssemblyCulture("")] 17 | 18 | // Setting ComVisible to false makes the types in this assembly not visible 19 | // to COM components. If you need to access a type in this assembly from 20 | // COM, set the ComVisible attribute to true on that type. 21 | [assembly: ComVisible(false)] 22 | 23 | // replaces app.config BindingRedirection in vsix extension (app.config belongs to an exe) 24 | [assembly: ProvideBindingRedirection(AssemblyName = "System.Runtime.CompilerServices.Unsafe", 25 | NewVersion = "6.0.0.0", OldVersionLowerBound = "0.0.0.0", 26 | OldVersionUpperBound = "6.0.0.0")] 27 | 28 | // Version information for an assembly consists of the following four values: 29 | // 30 | // Major Version 31 | // Minor Version 32 | // Build Number 33 | // Revision 34 | // 35 | // You can specify all the values or you can default the Build and Revision Numbers 36 | // by using the '*' as shown below: 37 | // [assembly: AssemblyVersion("1.0.*")] 38 | //[assembly: AssemblyVersion("1.0.0.0")] 39 | //[assembly: AssemblyFileVersion("1.0.0.0")] 40 | -------------------------------------------------------------------------------- /TwinpackVsix.15/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Zeugwerk Twinpack 6 | Zeugwerk Twinpack 7 | https://www.zeugwerk.at 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /TwinpackVsix.17/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TwinpackVsix17")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TwinpackVsix17")] 13 | [assembly: AssemblyCopyright("")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // Version information for an assembly consists of the following four values: 23 | // 24 | // Major Version 25 | // Minor Version 26 | // Build Number 27 | // Revision 28 | // 29 | // You can specify all the values or you can default the Build and Revision Numbers 30 | // by using the '*' as shown below: 31 | // [assembly: AssemblyVersion("1.0.*")] 32 | //[assembly: AssemblyVersion("1.0.0.0")] 33 | //[assembly: AssemblyFileVersion("1.0.0.0")] 34 | -------------------------------------------------------------------------------- /TwinpackVsix.17/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | TwinpackVsix17 6 | Zeugwerk Twinpack 7 | 8 | 9 | 10 | amd64 11 | 12 | 13 | amd64 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /TwinpackVsixShared/Commands/Command.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Shell; 2 | using Microsoft.VisualStudio.Shell.Interop; 3 | using Microsoft.VisualStudio.Threading; 4 | using NLog; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.ComponentModel.Design; 8 | using System.Linq; 9 | using System.Resources; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | 13 | namespace Twinpack.Commands 14 | { 15 | /// 16 | /// Base Class for all menus and commands of the Zeugwerk Creator Extension 17 | /// 18 | public abstract class Command : ICommand 19 | { 20 | public static readonly Guid CommandSet = new Guid("0ee4f42e-82af-4eba-92b9-bc7691b0de49"); 21 | protected static readonly Logger _logger = LogManager.GetCurrentClassLogger(); 22 | protected readonly TwinpackPackage _package; 23 | protected MenuCommand _menuCommand; 24 | 25 | public Command(TwinpackPackage package) 26 | { 27 | _package = package; 28 | } 29 | 30 | public MenuCommand MenuItem 31 | { 32 | get { return _menuCommand; } 33 | } 34 | public void PackageReady() 35 | { 36 | // package is initialized, enable functions of the command 37 | if (_package.IsInitialized) 38 | { 39 | _menuCommand.Visible = true; 40 | _menuCommand.Enabled = true; 41 | } 42 | } 43 | public virtual void PackageReset() 44 | { 45 | _menuCommand.Visible = true; 46 | _menuCommand.Enabled = true; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /TwinpackVsixShared/Commands/ICommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Twinpack.Commands 8 | { 9 | internal interface ICommand 10 | { 11 | void PackageReady(); 12 | void PackageReset(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /TwinpackVsixShared/Commands/ModifyCommand.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Shell; 2 | using Microsoft.VisualStudio.Shell.Interop; 3 | using NLog; 4 | using System; 5 | using System.ComponentModel.Design; 6 | using System.Globalization; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | using Twinpack; 11 | using TCatSysManagerLib; 12 | using Task = System.Threading.Tasks.Task; 13 | 14 | namespace Twinpack.Commands 15 | { 16 | /// 17 | /// Command handler 18 | /// 19 | internal sealed class ModifyCommand : Command, ICommand 20 | { 21 | /// 22 | /// Command ID. 23 | /// 24 | public const int CommandId = 262; 25 | 26 | /// 27 | /// Initializes a new instance of the class. 28 | /// Adds our command handlers for menu (commands must exist in the command table file) 29 | /// 30 | /// Owner package, not null. 31 | /// Command service to add command to, not null. 32 | private ModifyCommand(TwinpackPackage package, OleMenuCommandService commandService) 33 | : base(package) 34 | { 35 | var menuCommandID = new CommandID(CommandSet, CommandId); 36 | var menuItem = new MenuCommand(this.Execute, menuCommandID) 37 | { 38 | Visible = true, 39 | Enabled = true 40 | }; 41 | commandService.AddCommand(menuItem); 42 | 43 | _menuCommand = menuItem; 44 | } 45 | 46 | /// 47 | /// Gets the instance of the command. 48 | /// 49 | public static ModifyCommand Instance 50 | { 51 | get; 52 | private set; 53 | } 54 | 55 | /// 56 | /// Initializes the singleton instance of the command. 57 | /// 58 | /// Owner package, not null. 59 | public static async Task InitializeAsync(TwinpackPackage package) 60 | { 61 | // Switch to the main thread - the call to AddCommand in BackuppanelCommand's constructor requires 62 | // the UI thread. 63 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken); 64 | 65 | OleMenuCommandService commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; 66 | Instance = new ModifyCommand(package, commandService); 67 | } 68 | 69 | /// 70 | /// Shows the tool window when the menu item is clicked. 71 | /// 72 | /// The event sender. 73 | /// The event args. 74 | #pragma warning disable VSTHRD100 // "async void"-Methoden vermeiden 75 | private async void Execute(object sender, EventArgs e) 76 | #pragma warning restore VSTHRD100 // "async void"-Methoden vermeiden 77 | { 78 | try 79 | { 80 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(_package.DisposalToken); 81 | 82 | EnvDTE.Project plc = null; 83 | if (_package.Context.Dte.ActiveSolutionProjects is Array activeSolutionProjects && activeSolutionProjects.Length > 0) 84 | plc = activeSolutionProjects.GetValue(0) as EnvDTE.Project; 85 | 86 | var publishWindow = new Dialogs.PackageVersionWindow(false, _package.Context, plc); 87 | publishWindow.ShowDialog(); 88 | } 89 | catch(Exception ex) 90 | { 91 | _logger.Trace(ex); 92 | _logger.Error(ex.Message); 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /TwinpackVsixShared/Commands/PublishCommand.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Shell; 2 | using Microsoft.VisualStudio.Shell.Interop; 3 | using NLog; 4 | using System; 5 | using System.ComponentModel.Design; 6 | using System.Globalization; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | using Twinpack; 11 | using TCatSysManagerLib; 12 | using Task = System.Threading.Tasks.Task; 13 | 14 | namespace Twinpack.Commands 15 | { 16 | /// 17 | /// Command handler 18 | /// 19 | internal sealed class PublishCommand : Command, ICommand 20 | { 21 | /// 22 | /// Command ID. 23 | /// 24 | public const int CommandId = 261; 25 | 26 | /// 27 | /// Initializes a new instance of the class. 28 | /// Adds our command handlers for menu (commands must exist in the command table file) 29 | /// 30 | /// Owner package, not null. 31 | /// Command service to add command to, not null. 32 | private PublishCommand(TwinpackPackage package, OleMenuCommandService commandService) 33 | : base(package) 34 | { 35 | var menuCommandID = new CommandID(CommandSet, CommandId); 36 | var menuItem = new MenuCommand(this.Execute, menuCommandID) 37 | { 38 | Visible = true, 39 | Enabled = true 40 | }; 41 | commandService.AddCommand(menuItem); 42 | 43 | _menuCommand = menuItem; 44 | } 45 | 46 | /// 47 | /// Gets the instance of the command. 48 | /// 49 | public static PublishCommand Instance 50 | { 51 | get; 52 | private set; 53 | } 54 | 55 | /// 56 | /// Initializes the singleton instance of the command. 57 | /// 58 | /// Owner package, not null. 59 | public static async Task InitializeAsync(TwinpackPackage package) 60 | { 61 | // Switch to the main thread - the call to AddCommand in BackuppanelCommand's constructor requires 62 | // the UI thread. 63 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken); 64 | 65 | OleMenuCommandService commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; 66 | Instance = new PublishCommand(package, commandService); 67 | } 68 | 69 | /// 70 | /// Shows the tool window when the menu item is clicked. 71 | /// 72 | /// The event sender. 73 | /// The event args. 74 | #pragma warning disable VSTHRD100 // "async void"-Methoden vermeiden 75 | private async void Execute(object sender, EventArgs e) 76 | #pragma warning restore VSTHRD100 // "async void"-Methoden vermeiden 77 | { 78 | try 79 | { 80 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(_package.DisposalToken); 81 | 82 | EnvDTE.Project plc = null; 83 | if (_package.Context.Dte.ActiveSolutionProjects is Array activeSolutionProjects && activeSolutionProjects.Length > 0) 84 | plc = activeSolutionProjects.GetValue(0) as EnvDTE.Project; 85 | 86 | var publishWindow = new Dialogs.PackageVersionWindow(true, _package.Context, plc); 87 | publishWindow.ShowDialog(); 88 | } 89 | catch(Exception ex) 90 | { 91 | _logger.Trace(ex); 92 | _logger.Error(ex.Message); 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /TwinpackVsixShared/Commands/TwinpackMenuCommand.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.Shell; 2 | using Microsoft.VisualStudio.Shell.Interop; 3 | using NLog; 4 | using System; 5 | using System.ComponentModel.Design; 6 | using System.Globalization; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | using Twinpack; 11 | using TCatSysManagerLib; 12 | using Task = System.Threading.Tasks.Task; 13 | 14 | namespace Twinpack.Commands 15 | { 16 | /// 17 | /// Command handler 18 | /// 19 | internal sealed class TwinpackMenuCommand : Command, ICommand 20 | { 21 | /// 22 | /// Command ID. 23 | /// 24 | public const int CommandId = 263; 25 | 26 | /// 27 | /// Initializes a new instance of the class. 28 | /// Adds our command handlers for menu (commands must exist in the command table file) 29 | /// 30 | /// Owner package, not null. 31 | /// Command service to add command to, not null. 32 | private TwinpackMenuCommand(TwinpackPackage package, OleMenuCommandService commandService) 33 | : base(package) 34 | { 35 | var menuCommandID = new CommandID(CommandSet, CommandId); 36 | var menuItem = new MenuCommand(this.Execute, menuCommandID) 37 | { 38 | Visible = true, 39 | Enabled = true 40 | }; 41 | commandService.AddCommand(menuItem); 42 | 43 | _menuCommand = menuItem; 44 | } 45 | 46 | /// 47 | /// Gets the instance of the command. 48 | /// 49 | public static TwinpackMenuCommand Instance 50 | { 51 | get; 52 | private set; 53 | } 54 | 55 | /// 56 | /// Initializes the singleton instance of the command. 57 | /// 58 | /// Owner package, not null. 59 | public static async Task InitializeAsync(TwinpackPackage package) 60 | { 61 | // Switch to the main thread - the call to AddCommand in BackuppanelCommand's constructor requires 62 | // the UI thread. 63 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken); 64 | 65 | OleMenuCommandService commandService = await package.GetServiceAsync(typeof(IMenuCommandService)) as OleMenuCommandService; 66 | Instance = new TwinpackMenuCommand(package, commandService); 67 | } 68 | 69 | /// 70 | /// Shows the tool window when the menu item is clicked. 71 | /// 72 | /// The event sender. 73 | /// The event args. 74 | #pragma warning disable CS1998 // Bei der asynchronen Methode fehlen "await"-Operatoren. Die Methode wird synchron ausgeführt. 75 | #pragma warning disable VSTHRD100 // "async void"-Methoden vermeiden 76 | private async void Execute(object sender, EventArgs e) 77 | #pragma warning restore VSTHRD100 // "async void"-Methoden vermeiden 78 | #pragma warning restore CS1998 // Bei der asynchronen Methode fehlen "await"-Operatoren. Die Methode wird synchron ausgeführt. 79 | { 80 | 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /TwinpackVsixShared/Dialogs/Converters/BooleanToBorderThicknessConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Data; 7 | using System.Windows; 8 | using System.Globalization; 9 | 10 | namespace Twinpack.Dialogs 11 | { 12 | public class BooleanToBorderThicknessConverter : IValueConverter 13 | { 14 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | bool boolValue = (bool)value; 17 | return boolValue ? new Thickness(0, 0, 0, 2) : new Thickness(0, 0, 0, 0); 18 | } 19 | 20 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 21 | { 22 | throw new NotImplementedException(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /TwinpackVsixShared/Dialogs/Converters/BooleanToInverseBooleanConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Data; 3 | using System.Globalization; 4 | 5 | namespace Twinpack.Dialogs 6 | { 7 | public class BooleanToInverseBooleanConverter : IValueConverter 8 | { 9 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 10 | { 11 | bool boolValue = (bool)value; 12 | return !boolValue; 13 | } 14 | 15 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 16 | { 17 | bool boolValue = (bool)value; 18 | return !boolValue; 19 | } 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /TwinpackVsixShared/Dialogs/Converters/BooleanToVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Data; 3 | using System.Windows; 4 | using System.Globalization; 5 | 6 | namespace Twinpack.Dialogs 7 | { 8 | public class BooleanToVisibilityConverter : IValueConverter 9 | { 10 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 11 | { 12 | if (value == null) 13 | return Visibility.Visible; 14 | bool boolValue = (bool)value; 15 | boolValue = (parameter != null) ? !boolValue : boolValue; 16 | return boolValue ? Visibility.Visible : Visibility.Collapsed; 17 | } 18 | 19 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 20 | { 21 | throw new NotImplementedException(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /TwinpackVsixShared/Dialogs/Converters/CatalogIconUrlVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Data; 3 | using System.Windows; 4 | using System.Globalization; 5 | using Twinpack.Protocol.Api; 6 | 7 | namespace Twinpack.Dialogs 8 | { 9 | public class CatalogIconUrlVisibilityConverter : IValueConverter 10 | { 11 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 12 | { 13 | if (value == null) 14 | return Visibility.Collapsed; 15 | 16 | if (value is CatalogItemGetResponse catalogItem) 17 | { 18 | return catalogItem.Icon == null && catalogItem.IconUrl != null ? Visibility.Visible : Visibility.Collapsed; 19 | } 20 | 21 | return Visibility.Collapsed; 22 | } 23 | 24 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 25 | { 26 | throw new NotImplementedException(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /TwinpackVsixShared/Dialogs/Converters/CatalogIconVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Data; 3 | using System.Windows; 4 | using System.Globalization; 5 | using Twinpack.Protocol.Api; 6 | 7 | namespace Twinpack.Dialogs 8 | { 9 | public class CatalogIconVisibilityConverter : IValueConverter 10 | { 11 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 12 | { 13 | if (value == null) 14 | return Visibility.Collapsed; 15 | 16 | if (value is CatalogItemGetResponse catalogItem) 17 | { 18 | return catalogItem.Icon == null ? Visibility.Collapsed : Visibility.Visible; 19 | } 20 | 21 | return Visibility.Collapsed; 22 | } 23 | 24 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 25 | { 26 | throw new NotImplementedException(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /TwinpackVsixShared/Dialogs/Converters/CatalogNoIconVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Data; 3 | using System.Windows; 4 | using System.Globalization; 5 | using Twinpack.Protocol.Api; 6 | 7 | namespace Twinpack.Dialogs 8 | { 9 | public class CatalogNoIconVisibilityConverter : IValueConverter 10 | { 11 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 12 | { 13 | if (value == null) 14 | return Visibility.Collapsed; 15 | 16 | if (value is CatalogItemGetResponse catalogItem) 17 | { 18 | return catalogItem.IconUrl == null && catalogItem.Icon == null ? Visibility.Visible : Visibility.Collapsed; 19 | } 20 | 21 | return Visibility.Collapsed; 22 | } 23 | 24 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 25 | { 26 | throw new NotImplementedException(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /TwinpackVsixShared/Dialogs/Converters/IntToVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Data; 7 | using System.Windows; 8 | using System.Globalization; 9 | 10 | namespace Twinpack.Dialogs 11 | { 12 | public class IntToVisibilityConverter : IValueConverter 13 | { 14 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | try 17 | { 18 | int threshold = 0; 19 | int.TryParse(parameter as string, out threshold); 20 | return (value as int?) > threshold ? Visibility.Visible : Visibility.Hidden; 21 | } 22 | catch (Exception) { } 23 | return Visibility.Visible; 24 | } 25 | 26 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 27 | { 28 | throw new NotImplementedException(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /TwinpackVsixShared/Dialogs/Converters/NullToVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Data; 7 | using System.Windows; 8 | using System.Globalization; 9 | 10 | namespace Twinpack.Dialogs 11 | { 12 | public class NullToVisibilityConverter : IValueConverter 13 | { 14 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | if (value == null) 17 | return Visibility.Collapsed; 18 | 19 | try 20 | { 21 | return (int)value != 0 ? Visibility.Visible : Visibility.Collapsed; 22 | } 23 | catch 24 | { 25 | 26 | } 27 | 28 | try 29 | { 30 | if(value as Protocol.Api.PackageVersionGetResponse != null) 31 | return (value as Protocol.Api.PackageVersionGetResponse).Name != null ? Visibility.Visible : Visibility.Collapsed; 32 | 33 | if (value as Protocol.Api.PackageGetResponse != null) 34 | return (value as Protocol.Api.PackageGetResponse).Name != null ? Visibility.Visible : Visibility.Collapsed; 35 | } 36 | catch 37 | { 38 | 39 | } 40 | 41 | if (string.IsNullOrEmpty(value as string)) 42 | return Visibility.Collapsed; 43 | 44 | return Visibility.Visible; 45 | } 46 | 47 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 48 | { 49 | throw new NotImplementedException(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /TwinpackVsixShared/Dialogs/Converters/PackageVersionToBooleanConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Data; 3 | using System.Windows; 4 | using System.Globalization; 5 | using Twinpack.Models; 6 | 7 | namespace Twinpack.Dialogs 8 | { 9 | public class PackageVersionToBooleanConverter : IValueConverter 10 | { 11 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 12 | { 13 | return (value as PackageVersionGetResponse)?.Name != null; 14 | } 15 | 16 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 17 | { 18 | throw new NotImplementedException(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /TwinpackVsixShared/Dialogs/Converters/UrlToImageConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Data; 3 | using System.Globalization; 4 | 5 | namespace Twinpack.Dialogs 6 | { 7 | public class UrlToImageConverter : IValueConverter 8 | { 9 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 10 | { 11 | return IconCache.Icon((string)value); 12 | } 13 | 14 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | throw new NotImplementedException(); 17 | } 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /TwinpackVsixShared/Dialogs/Converters/VersionToStringConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Data; 3 | using System.Windows; 4 | using System.Globalization; 5 | 6 | namespace Twinpack.Dialogs 7 | { 8 | public class VersionToStringConverter : IValueConverter 9 | { 10 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 11 | { 12 | if (value == null) 13 | return "Latest"; 14 | 15 | return value; 16 | } 17 | 18 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 19 | { 20 | throw new NotImplementedException(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /TwinpackVsixShared/Dialogs/Converters/VisibilityToInverseVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Windows.Data; 7 | using System.Windows; 8 | using System.Globalization; 9 | 10 | namespace Twinpack.Dialogs 11 | { 12 | public class VisibilityToInverseVisibilityConverter : IValueConverter 13 | { 14 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 15 | { 16 | if (value == null) 17 | return Visibility.Collapsed; 18 | var visibility = (Visibility)value; 19 | return visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible; 20 | } 21 | 22 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 23 | { 24 | throw new NotImplementedException(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /TwinpackVsixShared/Dialogs/InputDialog.xaml: -------------------------------------------------------------------------------- 1 |  14 | 15 | 16 |