From 4e679b55778eb593768e1d8cb2d2c4295d1bb9ea Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Mon, 4 Dec 2023 23:20:23 +0200 Subject: [PATCH 01/27] refactor: Using system-under-test naming for clarity --- .../AutomationInterfaceTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs index b74cd00..2b04594 100644 --- a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs +++ b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs @@ -9,15 +9,15 @@ public class AutomationInterfaceTests : IDisposable [StaFact] public void ProgId_ShouldNotBeNullOrEmpty() { - var ai = new AutomationInterface(); - ai.ProgId.Should().NotBeNullOrEmpty(); + var sut = new AutomationInterface(); + sut.ProgId.Should().NotBeNullOrEmpty(); } [StaFact] public void ProgId_ShouldBeValid() { - var ai = new AutomationInterface(); - AutomationInterfaceConstants.SupportedProgIds.Should().Contain(ai.ProgId); + var sut = new AutomationInterface(); + AutomationInterfaceConstants.SupportedProgIds.Should().Contain(sut.ProgId); } protected virtual void Dispose(bool disposing) From 816f7b146ee8a6bb2ca27802a61dd77674c31a6c Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Mon, 4 Dec 2023 23:34:33 +0200 Subject: [PATCH 02/27] chore: relocated `TestTwincatProject` --- .../TestTwincatProject/TestTwincatProject.sln | 118 ++++++------- .../TestPlcProject/POUs/MAIN.TcPOU | 24 +-- .../TestPlcProject/PlcTask.TcTTO | 32 ++-- .../TestPlcProject/TestPlcProject.plcproj | 158 +++++++++--------- .../TestTwincatProject.tsproj | 52 +++--- 5 files changed, 192 insertions(+), 192 deletions(-) rename TestTwincatProject.sln => test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject.sln (96%) rename {src => test/TwinGet.AutomationInterface.Test/TestTwincatProject}/TestTwincatProject/TestPlcProject/POUs/MAIN.TcPOU (96%) rename {src => test/TwinGet.AutomationInterface.Test/TestTwincatProject}/TestTwincatProject/TestPlcProject/PlcTask.TcTTO (97%) rename {src => test/TwinGet.AutomationInterface.Test/TestTwincatProject}/TestTwincatProject/TestPlcProject/TestPlcProject.plcproj (98%) rename {src => test/TwinGet.AutomationInterface.Test/TestTwincatProject}/TestTwincatProject/TestTwincatProject.tsproj (97%) diff --git a/TestTwincatProject.sln b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject.sln similarity index 96% rename from TestTwincatProject.sln rename to test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject.sln index 3c76343..08180b7 100644 --- a/TestTwincatProject.sln +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject.sln @@ -1,59 +1,59 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# TcXaeShell Solution File, Format Version 11.00 -VisualStudioVersion = 15.0.33403.129 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{B1E792BE-AA5F-4E3C-8C82-674BF9C0715B}") = "TestTwincatProject", "src\TestTwincatProject\TestTwincatProject.tsproj", "{AF0AA87D-6A50-4129-B38E-8931819C4FEB}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|TwinCAT CE7 (ARMV7) = Debug|TwinCAT CE7 (ARMV7) - Debug|TwinCAT OS (ARMT2) = Debug|TwinCAT OS (ARMT2) - Debug|TwinCAT RT (x64) = Debug|TwinCAT RT (x64) - Debug|TwinCAT RT (x86) = Debug|TwinCAT RT (x86) - Release|TwinCAT CE7 (ARMV7) = Release|TwinCAT CE7 (ARMV7) - Release|TwinCAT OS (ARMT2) = Release|TwinCAT OS (ARMT2) - Release|TwinCAT RT (x64) = Release|TwinCAT RT (x64) - Release|TwinCAT RT (x86) = Release|TwinCAT RT (x86) - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT CE7 (ARMV7).ActiveCfg = Debug|TwinCAT CE7 (ARMV7) - {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT CE7 (ARMV7).Build.0 = Debug|TwinCAT CE7 (ARMV7) - {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT OS (ARMT2).ActiveCfg = Debug|TwinCAT OS (ARMT2) - {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT OS (ARMT2).Build.0 = Debug|TwinCAT OS (ARMT2) - {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT RT (x64).ActiveCfg = Debug|TwinCAT RT (x64) - {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT RT (x64).Build.0 = Debug|TwinCAT RT (x64) - {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT RT (x86).ActiveCfg = Debug|TwinCAT RT (x86) - {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT RT (x86).Build.0 = Debug|TwinCAT RT (x86) - {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT CE7 (ARMV7).ActiveCfg = Release|TwinCAT CE7 (ARMV7) - {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT CE7 (ARMV7).Build.0 = Release|TwinCAT CE7 (ARMV7) - {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT OS (ARMT2).ActiveCfg = Release|TwinCAT OS (ARMT2) - {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT OS (ARMT2).Build.0 = Release|TwinCAT OS (ARMT2) - {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT RT (x64).ActiveCfg = Release|TwinCAT RT (x64) - {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT RT (x64).Build.0 = Release|TwinCAT RT (x64) - {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT RT (x86).ActiveCfg = Release|TwinCAT RT (x86) - {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT RT (x86).Build.0 = Release|TwinCAT RT (x86) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT CE7 (ARMV7).ActiveCfg = Debug|TwinCAT CE7 (ARMV7) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT CE7 (ARMV7).Build.0 = Debug|TwinCAT CE7 (ARMV7) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT OS (ARMT2).ActiveCfg = Debug|TwinCAT OS (ARMT2) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT OS (ARMT2).Build.0 = Debug|TwinCAT OS (ARMT2) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT RT (x64).ActiveCfg = Debug|TwinCAT RT (x64) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT RT (x64).Build.0 = Debug|TwinCAT RT (x64) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT RT (x86).ActiveCfg = Debug|TwinCAT RT (x86) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT RT (x86).Build.0 = Debug|TwinCAT RT (x86) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT CE7 (ARMV7).ActiveCfg = Release|TwinCAT CE7 (ARMV7) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT CE7 (ARMV7).Build.0 = Release|TwinCAT CE7 (ARMV7) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT OS (ARMT2).ActiveCfg = Release|TwinCAT OS (ARMT2) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT OS (ARMT2).Build.0 = Release|TwinCAT OS (ARMT2) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT RT (x64).ActiveCfg = Release|TwinCAT RT (x64) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT RT (x64).Build.0 = Release|TwinCAT RT (x64) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT RT (x86).ActiveCfg = Release|TwinCAT RT (x86) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT RT (x86).Build.0 = Release|TwinCAT RT (x86) - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {DD06B855-CD6D-4014-BB8B-6B16769CEF3C} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# TcXaeShell Solution File, Format Version 11.00 +VisualStudioVersion = 15.0.33403.129 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{B1E792BE-AA5F-4E3C-8C82-674BF9C0715B}") = "TestTwincatProject", "TestTwincatProject\TestTwincatProject.tsproj", "{AF0AA87D-6A50-4129-B38E-8931819C4FEB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|TwinCAT CE7 (ARMV7) = Debug|TwinCAT CE7 (ARMV7) + Debug|TwinCAT OS (ARMT2) = Debug|TwinCAT OS (ARMT2) + Debug|TwinCAT RT (x64) = Debug|TwinCAT RT (x64) + Debug|TwinCAT RT (x86) = Debug|TwinCAT RT (x86) + Release|TwinCAT CE7 (ARMV7) = Release|TwinCAT CE7 (ARMV7) + Release|TwinCAT OS (ARMT2) = Release|TwinCAT OS (ARMT2) + Release|TwinCAT RT (x64) = Release|TwinCAT RT (x64) + Release|TwinCAT RT (x86) = Release|TwinCAT RT (x86) + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT CE7 (ARMV7).ActiveCfg = Debug|TwinCAT CE7 (ARMV7) + {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT CE7 (ARMV7).Build.0 = Debug|TwinCAT CE7 (ARMV7) + {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT OS (ARMT2).ActiveCfg = Debug|TwinCAT OS (ARMT2) + {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT OS (ARMT2).Build.0 = Debug|TwinCAT OS (ARMT2) + {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT RT (x64).ActiveCfg = Debug|TwinCAT RT (x64) + {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT RT (x64).Build.0 = Debug|TwinCAT RT (x64) + {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT RT (x86).ActiveCfg = Debug|TwinCAT RT (x86) + {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT RT (x86).Build.0 = Debug|TwinCAT RT (x86) + {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT CE7 (ARMV7).ActiveCfg = Release|TwinCAT CE7 (ARMV7) + {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT CE7 (ARMV7).Build.0 = Release|TwinCAT CE7 (ARMV7) + {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT OS (ARMT2).ActiveCfg = Release|TwinCAT OS (ARMT2) + {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT OS (ARMT2).Build.0 = Release|TwinCAT OS (ARMT2) + {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT RT (x64).ActiveCfg = Release|TwinCAT RT (x64) + {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT RT (x64).Build.0 = Release|TwinCAT RT (x64) + {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT RT (x86).ActiveCfg = Release|TwinCAT RT (x86) + {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT RT (x86).Build.0 = Release|TwinCAT RT (x86) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT CE7 (ARMV7).ActiveCfg = Debug|TwinCAT CE7 (ARMV7) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT CE7 (ARMV7).Build.0 = Debug|TwinCAT CE7 (ARMV7) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT OS (ARMT2).ActiveCfg = Debug|TwinCAT OS (ARMT2) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT OS (ARMT2).Build.0 = Debug|TwinCAT OS (ARMT2) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT RT (x64).ActiveCfg = Debug|TwinCAT RT (x64) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT RT (x64).Build.0 = Debug|TwinCAT RT (x64) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT RT (x86).ActiveCfg = Debug|TwinCAT RT (x86) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT RT (x86).Build.0 = Debug|TwinCAT RT (x86) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT CE7 (ARMV7).ActiveCfg = Release|TwinCAT CE7 (ARMV7) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT CE7 (ARMV7).Build.0 = Release|TwinCAT CE7 (ARMV7) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT OS (ARMT2).ActiveCfg = Release|TwinCAT OS (ARMT2) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT OS (ARMT2).Build.0 = Release|TwinCAT OS (ARMT2) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT RT (x64).ActiveCfg = Release|TwinCAT RT (x64) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT RT (x64).Build.0 = Release|TwinCAT RT (x64) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT RT (x86).ActiveCfg = Release|TwinCAT RT (x86) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT RT (x86).Build.0 = Release|TwinCAT RT (x86) + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DD06B855-CD6D-4014-BB8B-6B16769CEF3C} + EndGlobalSection +EndGlobal diff --git a/src/TestTwincatProject/TestPlcProject/POUs/MAIN.TcPOU b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject/TestPlcProject/POUs/MAIN.TcPOU similarity index 96% rename from src/TestTwincatProject/TestPlcProject/POUs/MAIN.TcPOU rename to test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject/TestPlcProject/POUs/MAIN.TcPOU index c6a0fef..44ab5e3 100644 --- a/src/TestTwincatProject/TestPlcProject/POUs/MAIN.TcPOU +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject/TestPlcProject/POUs/MAIN.TcPOU @@ -1,13 +1,13 @@ - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/src/TestTwincatProject/TestPlcProject/PlcTask.TcTTO b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject/TestPlcProject/PlcTask.TcTTO similarity index 97% rename from src/TestTwincatProject/TestPlcProject/PlcTask.TcTTO rename to test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject/TestPlcProject/PlcTask.TcTTO index 8c429f6..3ea2f60 100644 --- a/src/TestTwincatProject/TestPlcProject/PlcTask.TcTTO +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject/TestPlcProject/PlcTask.TcTTO @@ -1,17 +1,17 @@ - - - - - 10000 - 20 - - MAIN - - {9b12431b-7edd-43f8-a75b-b3f79cd7ddd6} - {a9cd95d0-9096-4b24-9ae8-c7b937ab36a3} - {44089897-9a22-4959-a4ec-4082ae02338e} - {48a3e150-e00b-4347-8cbd-2bd9dc83abd8} - {65ee9bd8-68af-4b07-b37f-085b06fa76a0} - - + + + + + 10000 + 20 + + MAIN + + {9b12431b-7edd-43f8-a75b-b3f79cd7ddd6} + {a9cd95d0-9096-4b24-9ae8-c7b937ab36a3} + {44089897-9a22-4959-a4ec-4082ae02338e} + {48a3e150-e00b-4347-8cbd-2bd9dc83abd8} + {65ee9bd8-68af-4b07-b37f-085b06fa76a0} + + \ No newline at end of file diff --git a/src/TestTwincatProject/TestPlcProject/TestPlcProject.plcproj b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject/TestPlcProject/TestPlcProject.plcproj similarity index 98% rename from src/TestTwincatProject/TestPlcProject/TestPlcProject.plcproj rename to test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject/TestPlcProject/TestPlcProject.plcproj index 1f08924..dcbfdce 100644 --- a/src/TestTwincatProject/TestPlcProject/TestPlcProject.plcproj +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject/TestPlcProject/TestPlcProject.plcproj @@ -1,80 +1,80 @@ - - - - 1.0.0.0 - 2.0 - {f423d9f4-0ef7-4885-8ec0-9a403a78ec70} - True - true - true - false - TestPlcProject - 3.1.4024.0 - {da8cb013-01e2-4e76-aad8-c63cb197c3b9} - {65a0cb05-45a0-424f-9370-5321fe656f5f} - {d1e585ee-3f7a-4fb2-acd6-080b80c26dc3} - {e3297877-f97b-44dc-8428-54f3df37edf6} - {74d54b3b-a48f-4f65-8df7-855cc9cd89e2} - {bd174e45-2719-4cbf-b50e-b235af23264e} - TwinGet - false - TwinGet.TestPlcProject - 0.1.0 - - - - Code - - - Code - - - - - - - - - - - Tc2_Standard, * (Beckhoff Automation GmbH) - Tc2_Standard - - - Tc2_System, * (Beckhoff Automation GmbH) - Tc2_System - - - Tc3_Module, * (Beckhoff Automation GmbH) - Tc3_Module - - - - - - - - "<ProjectRoot>" - - {40450F57-0AA3-4216-96F3-5444ECB29763} - - "{40450F57-0AA3-4216-96F3-5444ECB29763}" - - - ActiveVisuProfile - IR0whWr8bwfwBwAAiD2qpQAAAABVAgAA37x72QAAAAABAAAAAAAAAAEaUwB5AHMAdABlAG0ALgBTAHQAcgBpAG4AZwACTHsAZgA5ADUAYgBiADQAMgA2AC0ANQA1ADIANAAtADQAYgA0ADUALQA5ADQAMAAwAC0AZgBiADAAZgAyAGUANwA3AGUANQAxAGIAfQADCE4AYQBtAGUABDBUAHcAaQBuAEMAQQBUACAAMwAuADEAIABCAHUAaQBsAGQAIAA0ADAAMgA0AC4ANwAFFlAAcgBvAGYAaQBsAGUARABhAHQAYQAGTHsAMQA2AGUANQA1AGIANgAwAC0ANwAwADQAMwAtADQAYQA2ADMALQBiADYANQBiAC0ANgAxADQANwAxADMAOAA3ADgAZAA0ADIAfQAHEkwAaQBiAHIAYQByAGkAZQBzAAhMewAzAGIAZgBkADUANAA1ADkALQBiADAANwBmAC0ANABkADYAZQAtAGEAZQAxAGEALQBhADgAMwAzADUANgBhADUANQAxADQAMgB9AAlMewA5AGMAOQA1ADgAOQA2ADgALQAyAGMAOAA1AC0ANAAxAGIAYgAtADgAOAA3ADEALQA4ADkANQBmAGYAMQBmAGUAZABlADEAYQB9AAoOVgBlAHIAcwBpAG8AbgALBmkAbgB0AAwKVQBzAGEAZwBlAA0KVABpAHQAbABlAA4aVgBpAHMAdQBFAGwAZQBtAE0AZQB0AGUAcgAPDkMAbwBtAHAAYQBuAHkAEAxTAHkAcwB0AGUAbQARElYAaQBzAHUARQBsAGUAbQBzABIwVgBpAHMAdQBFAGwAZQBtAHMAUwBwAGUAYwBpAGEAbABDAG8AbgB0AHIAbwBsAHMAEyhWAGkAcwB1AEUAbABlAG0AcwBXAGkAbgBDAG8AbgB0AHIAbwBsAHMAFCRWAGkAcwB1AEUAbABlAG0AVABlAHgAdABFAGQAaQB0AG8AcgAVIlYAaQBzAHUATgBhAHQAaQB2AGUAQwBvAG4AdAByAG8AbAAWFHYAaQBzAHUAaQBuAHAAdQB0AHMAFwxzAHkAcwB0AGUAbQAYGFYAaQBzAHUARQBsAGUAbQBCAGEAcwBlABkmRABlAHYAUABsAGEAYwBlAGgAbwBsAGQAZQByAHMAVQBzAGUAZAAaCGIAbwBvAGwAGyJQAGwAdQBnAGkAbgBDAG8AbgBzAHQAcgBhAGkAbgB0AHMAHEx7ADQAMwBkADUAMgBiAGMAZQAtADkANAAyAGMALQA0ADQAZAA3AC0AOQBlADkANAAtADEAYgBmAGQAZgAzADEAMABlADYAMwBjAH0AHRxBAHQATABlAGEAcwB0AFYAZQByAHMAaQBvAG4AHhRQAGwAdQBnAGkAbgBHAHUAaQBkAB8WUwB5AHMAdABlAG0ALgBHAHUAaQBkACBIYQBmAGMAZAA1ADQANAA2AC0ANAA5ADEANAAtADQAZgBlADcALQBiAGIANwA4AC0AOQBiAGYAZgBlAGIANwAwAGYAZAAxADcAIRRVAHAAZABhAHQAZQBJAG4AZgBvACJMewBiADAAMwAzADYANgBhADgALQBiADUAYwAwAC0ANABiADkAYQAtAGEAMAAwAGUALQBlAGIAOAA2ADAAMQAxADEAMAA0AGMAMwB9ACMOVQBwAGQAYQB0AGUAcwAkTHsAMQA4ADYAOABmAGYAYwA5AC0AZQA0AGYAYwAtADQANQAzADIALQBhAGMAMAA2AC0AMQBlADMAOQBiAGIANQA1ADcAYgA2ADkAfQAlTHsAYQA1AGIAZAA0ADgAYwAzAC0AMABkADEANwAtADQAMQBiADUALQBiADEANgA0AC0ANQBmAGMANgBhAGQAMgBiADkANgBiADcAfQAmFk8AYgBqAGUAYwB0AHMAVAB5AHAAZQAnVFUAcABkAGEAdABlAEwAYQBuAGcAdQBhAGcAZQBNAG8AZABlAGwARgBvAHIAQwBvAG4AdgBlAHIAdABpAGIAbABlAEwAaQBiAHIAYQByAGkAZQBzACgQTABpAGIAVABpAHQAbABlACkUTABpAGIAQwBvAG0AcABhAG4AeQAqHlUAcABkAGEAdABlAFAAcgBvAHYAaQBkAGUAcgBzACs4UwB5AHMAdABlAG0ALgBDAG8AbABsAGUAYwB0AGkAbwBuAHMALgBIAGEAcwBoAHQAYQBiAGwAZQAsEnYAaQBzAHUAZQBsAGUAbQBzAC1INgBjAGIAMQBjAGQAZQAxAC0AZAA1AGQAYwAtADQAYQAzAGIALQA5ADAANQA0AC0AMgAxAGYAYQA3ADUANgBhADMAZgBhADQALihJAG4AdABlAHIAZgBhAGMAZQBWAGUAcgBzAGkAbwBuAEkAbgBmAG8AL0x7AGMANgAxADEAZQA0ADAAMAAtADcAZgBiADkALQA0AGMAMwA1AC0AYgA5AGEAYwAtADQAZQAzADEANABiADUAOQA5ADYANAAzAH0AMBhNAGEAagBvAHIAVgBlAHIAcwBpAG8AbgAxGE0AaQBuAG8AcgBWAGUAcgBzAGkAbwBuADIMTABlAGcAYQBjAHkAMzBMAGEAbgBnAHUAYQBnAGUATQBvAGQAZQBsAFYAZQByAHMAaQBvAG4ASQBuAGYAbwA0MEwAbwBhAGQATABpAGIAcgBhAHIAaQBlAHMASQBuAHQAbwBQAHIAbwBqAGUAYwB0ADUaQwBvAG0AcABhAHQAaQBiAGkAbABpAHQAeQDQAAIaA9ADAS0E0AUGGgfQBwgaAUUHCQjQAAkaBEUKCwQDAAAABQAAAA0AAAAAAAAA0AwLrQIAAADQDQEtDtAPAS0Q0AAJGgRFCgsEAwAAAAUAAAANAAAAKAAAANAMC60BAAAA0A0BLRHQDwEtENAACRoERQoLBAMAAAAFAAAADQAAAAAAAADQDAutAgAAANANAS0S0A8BLRDQAAkaBEUKCwQDAAAABQAAAA0AAAAUAAAA0AwLrQIAAADQDQEtE9APAS0Q0AAJGgRFCgsEAwAAAAUAAAANAAAAAAAAANAMC60CAAAA0A0BLRTQDwEtENAACRoERQoLBAMAAAAFAAAADQAAAAAAAADQDAutAgAAANANAS0V0A8BLRDQAAkaBEUKCwQDAAAABQAAAA0AAAAAAAAA0AwLrQIAAADQDQEtFtAPAS0X0AAJGgRFCgsEAwAAAAUAAAANAAAAKAAAANAMC60EAAAA0A0BLRjQDwEtENAZGq0BRRscAdAAHBoCRR0LBAMAAAAFAAAADQAAAAAAAADQHh8tINAhIhoCRSMkAtAAJRoFRQoLBAMAAAADAAAAAAAAAAoAAADQJgutAAAAANADAS0n0CgBLRHQKQEtENAAJRoFRQoLBAMAAAADAAAAAAAAAAoAAADQJgutAQAAANADAS0n0CgBLRHQKQEtEJoqKwFFAAEC0AABLSzQAAEtF9AAHy0t0C4vGgPQMAutAQAAANAxC60XAAAA0DIarQDQMy8aA9AwC60CAAAA0DELrQMAAADQMhqtANA0Gq0A0DUarQA= - - - - - - - - System.Collections.Hashtable - {54dd0eac-a6d8-46f2-8c27-2f43c7e49861} - System.String - - - - + + + + 1.0.0.0 + 2.0 + {f423d9f4-0ef7-4885-8ec0-9a403a78ec70} + True + true + true + false + TestPlcProject + 3.1.4024.0 + {da8cb013-01e2-4e76-aad8-c63cb197c3b9} + {65a0cb05-45a0-424f-9370-5321fe656f5f} + {d1e585ee-3f7a-4fb2-acd6-080b80c26dc3} + {e3297877-f97b-44dc-8428-54f3df37edf6} + {74d54b3b-a48f-4f65-8df7-855cc9cd89e2} + {bd174e45-2719-4cbf-b50e-b235af23264e} + TwinGet + false + TwinGet.TestPlcProject + 0.1.0 + + + + Code + + + Code + + + + + + + + + + + Tc2_Standard, * (Beckhoff Automation GmbH) + Tc2_Standard + + + Tc2_System, * (Beckhoff Automation GmbH) + Tc2_System + + + Tc3_Module, * (Beckhoff Automation GmbH) + Tc3_Module + + + + + + + + "<ProjectRoot>" + + {40450F57-0AA3-4216-96F3-5444ECB29763} + + "{40450F57-0AA3-4216-96F3-5444ECB29763}" + + + ActiveVisuProfile + IR0whWr8bwfwBwAAiD2qpQAAAABVAgAA37x72QAAAAABAAAAAAAAAAEaUwB5AHMAdABlAG0ALgBTAHQAcgBpAG4AZwACTHsAZgA5ADUAYgBiADQAMgA2AC0ANQA1ADIANAAtADQAYgA0ADUALQA5ADQAMAAwAC0AZgBiADAAZgAyAGUANwA3AGUANQAxAGIAfQADCE4AYQBtAGUABDBUAHcAaQBuAEMAQQBUACAAMwAuADEAIABCAHUAaQBsAGQAIAA0ADAAMgA0AC4ANwAFFlAAcgBvAGYAaQBsAGUARABhAHQAYQAGTHsAMQA2AGUANQA1AGIANgAwAC0ANwAwADQAMwAtADQAYQA2ADMALQBiADYANQBiAC0ANgAxADQANwAxADMAOAA3ADgAZAA0ADIAfQAHEkwAaQBiAHIAYQByAGkAZQBzAAhMewAzAGIAZgBkADUANAA1ADkALQBiADAANwBmAC0ANABkADYAZQAtAGEAZQAxAGEALQBhADgAMwAzADUANgBhADUANQAxADQAMgB9AAlMewA5AGMAOQA1ADgAOQA2ADgALQAyAGMAOAA1AC0ANAAxAGIAYgAtADgAOAA3ADEALQA4ADkANQBmAGYAMQBmAGUAZABlADEAYQB9AAoOVgBlAHIAcwBpAG8AbgALBmkAbgB0AAwKVQBzAGEAZwBlAA0KVABpAHQAbABlAA4aVgBpAHMAdQBFAGwAZQBtAE0AZQB0AGUAcgAPDkMAbwBtAHAAYQBuAHkAEAxTAHkAcwB0AGUAbQARElYAaQBzAHUARQBsAGUAbQBzABIwVgBpAHMAdQBFAGwAZQBtAHMAUwBwAGUAYwBpAGEAbABDAG8AbgB0AHIAbwBsAHMAEyhWAGkAcwB1AEUAbABlAG0AcwBXAGkAbgBDAG8AbgB0AHIAbwBsAHMAFCRWAGkAcwB1AEUAbABlAG0AVABlAHgAdABFAGQAaQB0AG8AcgAVIlYAaQBzAHUATgBhAHQAaQB2AGUAQwBvAG4AdAByAG8AbAAWFHYAaQBzAHUAaQBuAHAAdQB0AHMAFwxzAHkAcwB0AGUAbQAYGFYAaQBzAHUARQBsAGUAbQBCAGEAcwBlABkmRABlAHYAUABsAGEAYwBlAGgAbwBsAGQAZQByAHMAVQBzAGUAZAAaCGIAbwBvAGwAGyJQAGwAdQBnAGkAbgBDAG8AbgBzAHQAcgBhAGkAbgB0AHMAHEx7ADQAMwBkADUAMgBiAGMAZQAtADkANAAyAGMALQA0ADQAZAA3AC0AOQBlADkANAAtADEAYgBmAGQAZgAzADEAMABlADYAMwBjAH0AHRxBAHQATABlAGEAcwB0AFYAZQByAHMAaQBvAG4AHhRQAGwAdQBnAGkAbgBHAHUAaQBkAB8WUwB5AHMAdABlAG0ALgBHAHUAaQBkACBIYQBmAGMAZAA1ADQANAA2AC0ANAA5ADEANAAtADQAZgBlADcALQBiAGIANwA4AC0AOQBiAGYAZgBlAGIANwAwAGYAZAAxADcAIRRVAHAAZABhAHQAZQBJAG4AZgBvACJMewBiADAAMwAzADYANgBhADgALQBiADUAYwAwAC0ANABiADkAYQAtAGEAMAAwAGUALQBlAGIAOAA2ADAAMQAxADEAMAA0AGMAMwB9ACMOVQBwAGQAYQB0AGUAcwAkTHsAMQA4ADYAOABmAGYAYwA5AC0AZQA0AGYAYwAtADQANQAzADIALQBhAGMAMAA2AC0AMQBlADMAOQBiAGIANQA1ADcAYgA2ADkAfQAlTHsAYQA1AGIAZAA0ADgAYwAzAC0AMABkADEANwAtADQAMQBiADUALQBiADEANgA0AC0ANQBmAGMANgBhAGQAMgBiADkANgBiADcAfQAmFk8AYgBqAGUAYwB0AHMAVAB5AHAAZQAnVFUAcABkAGEAdABlAEwAYQBuAGcAdQBhAGcAZQBNAG8AZABlAGwARgBvAHIAQwBvAG4AdgBlAHIAdABpAGIAbABlAEwAaQBiAHIAYQByAGkAZQBzACgQTABpAGIAVABpAHQAbABlACkUTABpAGIAQwBvAG0AcABhAG4AeQAqHlUAcABkAGEAdABlAFAAcgBvAHYAaQBkAGUAcgBzACs4UwB5AHMAdABlAG0ALgBDAG8AbABsAGUAYwB0AGkAbwBuAHMALgBIAGEAcwBoAHQAYQBiAGwAZQAsEnYAaQBzAHUAZQBsAGUAbQBzAC1INgBjAGIAMQBjAGQAZQAxAC0AZAA1AGQAYwAtADQAYQAzAGIALQA5ADAANQA0AC0AMgAxAGYAYQA3ADUANgBhADMAZgBhADQALihJAG4AdABlAHIAZgBhAGMAZQBWAGUAcgBzAGkAbwBuAEkAbgBmAG8AL0x7AGMANgAxADEAZQA0ADAAMAAtADcAZgBiADkALQA0AGMAMwA1AC0AYgA5AGEAYwAtADQAZQAzADEANABiADUAOQA5ADYANAAzAH0AMBhNAGEAagBvAHIAVgBlAHIAcwBpAG8AbgAxGE0AaQBuAG8AcgBWAGUAcgBzAGkAbwBuADIMTABlAGcAYQBjAHkAMzBMAGEAbgBnAHUAYQBnAGUATQBvAGQAZQBsAFYAZQByAHMAaQBvAG4ASQBuAGYAbwA0MEwAbwBhAGQATABpAGIAcgBhAHIAaQBlAHMASQBuAHQAbwBQAHIAbwBqAGUAYwB0ADUaQwBvAG0AcABhAHQAaQBiAGkAbABpAHQAeQDQAAIaA9ADAS0E0AUGGgfQBwgaAUUHCQjQAAkaBEUKCwQDAAAABQAAAA0AAAAAAAAA0AwLrQIAAADQDQEtDtAPAS0Q0AAJGgRFCgsEAwAAAAUAAAANAAAAKAAAANAMC60BAAAA0A0BLRHQDwEtENAACRoERQoLBAMAAAAFAAAADQAAAAAAAADQDAutAgAAANANAS0S0A8BLRDQAAkaBEUKCwQDAAAABQAAAA0AAAAUAAAA0AwLrQIAAADQDQEtE9APAS0Q0AAJGgRFCgsEAwAAAAUAAAANAAAAAAAAANAMC60CAAAA0A0BLRTQDwEtENAACRoERQoLBAMAAAAFAAAADQAAAAAAAADQDAutAgAAANANAS0V0A8BLRDQAAkaBEUKCwQDAAAABQAAAA0AAAAAAAAA0AwLrQIAAADQDQEtFtAPAS0X0AAJGgRFCgsEAwAAAAUAAAANAAAAKAAAANAMC60EAAAA0A0BLRjQDwEtENAZGq0BRRscAdAAHBoCRR0LBAMAAAAFAAAADQAAAAAAAADQHh8tINAhIhoCRSMkAtAAJRoFRQoLBAMAAAADAAAAAAAAAAoAAADQJgutAAAAANADAS0n0CgBLRHQKQEtENAAJRoFRQoLBAMAAAADAAAAAAAAAAoAAADQJgutAQAAANADAS0n0CgBLRHQKQEtEJoqKwFFAAEC0AABLSzQAAEtF9AAHy0t0C4vGgPQMAutAQAAANAxC60XAAAA0DIarQDQMy8aA9AwC60CAAAA0DELrQMAAADQMhqtANA0Gq0A0DUarQA= + + + + + + + + System.Collections.Hashtable + {54dd0eac-a6d8-46f2-8c27-2f43c7e49861} + System.String + + + + \ No newline at end of file diff --git a/src/TestTwincatProject/TestTwincatProject.tsproj b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject/TestTwincatProject.tsproj similarity index 97% rename from src/TestTwincatProject/TestTwincatProject.tsproj rename to test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject/TestTwincatProject.tsproj index 6d0a80a..e209fc5 100644 --- a/src/TestTwincatProject/TestTwincatProject.tsproj +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject/TestTwincatProject.tsproj @@ -1,26 +1,26 @@ - - - - - - - PlcTask - - - - - - - TestPlcProject Instance - {08500001-0000-0000-F000-000000000064} - - - 1 - Default - - - - - - - + + + + + + + PlcTask + + + + + + + TestPlcProject Instance + {08500001-0000-0000-F000-000000000064} + + + 1 + Default + + + + + + + From e54ea97a74dd3647f8cc47b40889704c15a53b9d Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Tue, 5 Dec 2023 01:12:04 +0200 Subject: [PATCH 03/27] feat: Added `LoadSolution` to `AutomationInterface` - Build copy `TestTwincatProject` to binary - Added `DteInstanceIsNullException` --- .../AutomationInterface.cs | 37 ++++++++++++++++++- .../CouldNotCreateTwincatDteException.cs | 25 +++++++++++++ ...atDte.cs => DteInstanceIsNullException.cs} | 8 ++-- .../Properties/launchSettings.json | 8 ++++ .../TwinGet.AutomationInterface.csproj | 4 -- .../AutomationInterfaceTests.cs | 13 +++++++ .../Properties/launchSettings.json | 8 ++++ .../TestTwincatSolutionConstants.cs | 16 ++++++++ .../TwinGet.AutomationInterface.Test.csproj | 4 ++ 9 files changed, 113 insertions(+), 10 deletions(-) create mode 100644 src/TwinGet.AutomationInterface/Exceptions/CouldNotCreateTwincatDteException.cs rename src/TwinGet.AutomationInterface/Exceptions/{CouldNotCreateTwinCatDte.cs => DteInstanceIsNullException.cs} (52%) create mode 100644 src/TwinGet.AutomationInterface/Properties/launchSettings.json create mode 100644 test/TwinGet.AutomationInterface.Test/Properties/launchSettings.json create mode 100644 test/TwinGet.AutomationInterface.Test/TestTwincatSolutionConstants.cs diff --git a/src/TwinGet.AutomationInterface/AutomationInterface.cs b/src/TwinGet.AutomationInterface/AutomationInterface.cs index 59f0648..cd4d9a5 100644 --- a/src/TwinGet.AutomationInterface/AutomationInterface.cs +++ b/src/TwinGet.AutomationInterface/AutomationInterface.cs @@ -2,6 +2,7 @@ using System.Runtime.InteropServices; using System.Runtime.Versioning; +using EnvDTE80; using TwinGet.AutomationInterface.ComMessageFilter; using TwinGet.AutomationInterface.Exceptions; using TwinGet.AutomationInterface.Utils; @@ -11,16 +12,20 @@ namespace TwinGet.AutomationInterface [SupportedOSPlatform("windows")] public class AutomationInterface : IDisposable { - public string ProgId { get => _progId; } private string _progId; + private EnvDTE.Solution? _solution; + private EnvDTE.SolutionBuild? _solutionBuild; private readonly EnvDTE80.DTE2? _dte; private bool _disposedValue; + public string ProgId { get => _progId; } + public bool IsSolutionOpen { get => _solution?.IsOpen ?? false; } + public string LoadedSolutionFile { get => _solution?.FileName ?? string.Empty; } public AutomationInterface() { MessageFilter.Register(); _dte = TryInitializeDte(out _progId); - if (_dte is null) { throw new CouldNotCreateTwinCatDte("Is TwinCAT installed in this system?"); } + if (_dte is null) { throw new CouldNotCreateTwincatDteException("Is TwinCAT installed in this system?"); } } protected virtual void Dispose(bool disposing) @@ -96,5 +101,33 @@ private void CleanUp() return null; } + private void ThrowIfDteIsNull() + { + if (_dte is null) { throw new DteInstanceIsNullException($"No {nameof(DTE2)} instance available."); } + } + + private void ThrowIfInvalidSolutionPath(string solutionPath) + { + if (string.IsNullOrEmpty(solutionPath)) { throw new ArgumentException("Solution path cannot be null or empty."); } + if (!Path.Exists(solutionPath)) { throw new ArgumentException($"Provided solution path \"{solutionPath.ToString()}\" does not exists."); } + } + + public void LoadSolution(string filePath) + { + ThrowIfInvalidSolutionPath(filePath); + ThrowIfDteIsNull(); + + filePath = Path.GetFullPath(filePath); +#pragma warning disable CS8602 // Dereference of a possibly null reference. + _solution = _dte.Solution; +#pragma warning restore CS8602 // Dereference of a possibly null reference. + _solutionBuild = _dte.Solution.SolutionBuild; + _solution.Open(filePath); + } + + public static void SaveProjectAsLibrary(string solutionPath, string outFile) + { + + } } } diff --git a/src/TwinGet.AutomationInterface/Exceptions/CouldNotCreateTwincatDteException.cs b/src/TwinGet.AutomationInterface/Exceptions/CouldNotCreateTwincatDteException.cs new file mode 100644 index 0000000..7727a9f --- /dev/null +++ b/src/TwinGet.AutomationInterface/Exceptions/CouldNotCreateTwincatDteException.cs @@ -0,0 +1,25 @@ +// This file is licensed to you under MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TwinGet.AutomationInterface.Exceptions +{ + public class CouldNotCreateTwincatDteException : Exception + { + public CouldNotCreateTwincatDteException() + { + } + + public CouldNotCreateTwincatDteException(string messsage) : base(messsage) + { + } + + public CouldNotCreateTwincatDteException(string message, Exception inner) : base(message, inner) + { + } + } +} diff --git a/src/TwinGet.AutomationInterface/Exceptions/CouldNotCreateTwinCatDte.cs b/src/TwinGet.AutomationInterface/Exceptions/DteInstanceIsNullException.cs similarity index 52% rename from src/TwinGet.AutomationInterface/Exceptions/CouldNotCreateTwinCatDte.cs rename to src/TwinGet.AutomationInterface/Exceptions/DteInstanceIsNullException.cs index 7ee08ea..2921b87 100644 --- a/src/TwinGet.AutomationInterface/Exceptions/CouldNotCreateTwinCatDte.cs +++ b/src/TwinGet.AutomationInterface/Exceptions/DteInstanceIsNullException.cs @@ -8,17 +8,17 @@ namespace TwinGet.AutomationInterface.Exceptions { - public class CouldNotCreateTwinCatDte : Exception + public class DteInstanceIsNullException : Exception { - public CouldNotCreateTwinCatDte() + public DteInstanceIsNullException() { } - public CouldNotCreateTwinCatDte(string messsage) : base(messsage) + public DteInstanceIsNullException(string message) : base(message) { } - public CouldNotCreateTwinCatDte(string message, Exception inner) : base(message, inner) + public DteInstanceIsNullException(string message, Exception inner) : base(message, inner) { } } diff --git a/src/TwinGet.AutomationInterface/Properties/launchSettings.json b/src/TwinGet.AutomationInterface/Properties/launchSettings.json new file mode 100644 index 0000000..b22616c --- /dev/null +++ b/src/TwinGet.AutomationInterface/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "TwinGet.AutomationInterface": { + "commandName": "Project", + "nativeDebugging": true + } + } +} \ No newline at end of file diff --git a/src/TwinGet.AutomationInterface/TwinGet.AutomationInterface.csproj b/src/TwinGet.AutomationInterface/TwinGet.AutomationInterface.csproj index 3558051..f35edb8 100644 --- a/src/TwinGet.AutomationInterface/TwinGet.AutomationInterface.csproj +++ b/src/TwinGet.AutomationInterface/TwinGet.AutomationInterface.csproj @@ -24,8 +24,4 @@ - - - - diff --git a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs index 2b04594..3826052 100644 --- a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs +++ b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs @@ -10,6 +10,7 @@ public class AutomationInterfaceTests : IDisposable public void ProgId_ShouldNotBeNullOrEmpty() { var sut = new AutomationInterface(); + sut.ProgId.Should().NotBeNullOrEmpty(); } @@ -17,9 +18,21 @@ public void ProgId_ShouldNotBeNullOrEmpty() public void ProgId_ShouldBeValid() { var sut = new AutomationInterface(); + AutomationInterfaceConstants.SupportedProgIds.Should().Contain(sut.ProgId); } + + [StaFact] + public void LoadSolution_WithValidPath_ShouldLoadSuccessfully() + { + var sut = new AutomationInterface(); + sut.LoadSolution(TestTwincatSolutionConstants.s_testTwincatSolution); + + sut.LoadedSolutionFile.Should().Be(TestTwincatSolutionConstants.s_testTwincatSolution); + sut.IsSolutionOpen.Should().BeTrue(); + } + protected virtual void Dispose(bool disposing) { if (!_disposedValue) diff --git a/test/TwinGet.AutomationInterface.Test/Properties/launchSettings.json b/test/TwinGet.AutomationInterface.Test/Properties/launchSettings.json new file mode 100644 index 0000000..3fbd7e6 --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "TwinGet.AutomationInterface.Test": { + "commandName": "Project", + "nativeDebugging": true + } + } +} \ No newline at end of file diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatSolutionConstants.cs b/test/TwinGet.AutomationInterface.Test/TestTwincatSolutionConstants.cs new file mode 100644 index 0000000..aa91ea8 --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatSolutionConstants.cs @@ -0,0 +1,16 @@ +// This file is licensed to you under MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TwinGet.AutomationInterface.Test +{ + internal static class TestTwincatSolutionConstants + { + public static readonly string s_testTwincatSolutionRootFolder = Path.Join(@$"{AppDomain.CurrentDomain.BaseDirectory}", @"TestTwincatProject"); + public static readonly string s_testTwincatSolution = Path.Join(@$"{AppDomain.CurrentDomain.BaseDirectory}", @"TestTwincatProject\TestTwincatProject.sln"); + } +} diff --git a/test/TwinGet.AutomationInterface.Test/TwinGet.AutomationInterface.Test.csproj b/test/TwinGet.AutomationInterface.Test/TwinGet.AutomationInterface.Test.csproj index f21f212..37ed932 100644 --- a/test/TwinGet.AutomationInterface.Test/TwinGet.AutomationInterface.Test.csproj +++ b/test/TwinGet.AutomationInterface.Test/TwinGet.AutomationInterface.Test.csproj @@ -27,4 +27,8 @@ + + + + From 1beed13c6e0f40c14c3a8bd4f6633f42296ea8f7 Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Wed, 6 Dec 2023 21:41:55 +0200 Subject: [PATCH 04/27] feat: Added `TwinGet.Utils` Added `Directory.CopyDirectory()` --- TwinGet.sln | 16 +++- .../AutomationInterfaceConstants.cs | 6 -- .../CouldNotCreateTwincatDteException.cs | 6 -- .../Exceptions/DteInstanceIsNullException.cs | 6 -- src/TwinGet.Utils/IO/Directory.cs | 55 ++++++++++++++ src/TwinGet.Utils/TwinGet.Utils.csproj | 9 +++ .../AutomationInterfaceTests.cs | 5 ++ .../{Usings.cs => GlobalUsings.cs} | 8 +- .../TestTwincatSolutionConstants.cs | 6 -- .../xunit.runner.json | 5 ++ test/TwinGet.Utils.Test/GlobalUsings.cs | 4 + .../IOTests/DirectoryComparer.cs | 23 ++++++ .../IOTests/DirectoryTests.cs | 73 +++++++++++++++++++ .../TwinGet.Utils.Test.csproj | 30 ++++++++ test/TwinGet.Utils.Test/xunit.runner.json | 5 ++ 15 files changed, 228 insertions(+), 29 deletions(-) create mode 100644 src/TwinGet.Utils/IO/Directory.cs create mode 100644 src/TwinGet.Utils/TwinGet.Utils.csproj rename test/TwinGet.AutomationInterface.Test/{Usings.cs => GlobalUsings.cs} (96%) create mode 100644 test/TwinGet.AutomationInterface.Test/xunit.runner.json create mode 100644 test/TwinGet.Utils.Test/GlobalUsings.cs create mode 100644 test/TwinGet.Utils.Test/IOTests/DirectoryComparer.cs create mode 100644 test/TwinGet.Utils.Test/IOTests/DirectoryTests.cs create mode 100644 test/TwinGet.Utils.Test/TwinGet.Utils.Test.csproj create mode 100644 test/TwinGet.Utils.Test/xunit.runner.json diff --git a/TwinGet.sln b/TwinGet.sln index c885c25..6e53d81 100644 --- a/TwinGet.sln +++ b/TwinGet.sln @@ -13,7 +13,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TwinGet.AutomationInterface EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{BDB6B966-4B82-4F51-9487-91CD520BC5AE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TwinGet.AutomationInterface.Test", "test\TwinGet.AutomationInterface.Test\TwinGet.AutomationInterface.Test.csproj", "{0A952D6F-9D6D-45B0-8535-E10ED8029B6B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TwinGet.AutomationInterface.Test", "test\TwinGet.AutomationInterface.Test\TwinGet.AutomationInterface.Test.csproj", "{0A952D6F-9D6D-45B0-8535-E10ED8029B6B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TwinGet.Utils", "src\TwinGet.Utils\TwinGet.Utils.csproj", "{C29610F6-4B71-49AD-B57F-784467C1CF1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TwinGet.Utils.Test", "test\TwinGet.Utils.Test\TwinGet.Utils.Test.csproj", "{1D55596F-6184-4DA6-9DFE-BDB9B358EA4B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -37,6 +41,14 @@ Global {0A952D6F-9D6D-45B0-8535-E10ED8029B6B}.Debug|Any CPU.Build.0 = Debug|Any CPU {0A952D6F-9D6D-45B0-8535-E10ED8029B6B}.Release|Any CPU.ActiveCfg = Release|Any CPU {0A952D6F-9D6D-45B0-8535-E10ED8029B6B}.Release|Any CPU.Build.0 = Release|Any CPU + {C29610F6-4B71-49AD-B57F-784467C1CF1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C29610F6-4B71-49AD-B57F-784467C1CF1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C29610F6-4B71-49AD-B57F-784467C1CF1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C29610F6-4B71-49AD-B57F-784467C1CF1F}.Release|Any CPU.Build.0 = Release|Any CPU + {1D55596F-6184-4DA6-9DFE-BDB9B358EA4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1D55596F-6184-4DA6-9DFE-BDB9B358EA4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1D55596F-6184-4DA6-9DFE-BDB9B358EA4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1D55596F-6184-4DA6-9DFE-BDB9B358EA4B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -46,6 +58,8 @@ Global {9B7F1AC0-7368-4504-8B3F-D9C518B85653} = {46CA5D70-7812-4809-8B40-31392DE18AA1} {3FD0F366-6A8B-4CF8-A11E-D62D8A687EC7} = {46CA5D70-7812-4809-8B40-31392DE18AA1} {0A952D6F-9D6D-45B0-8535-E10ED8029B6B} = {BDB6B966-4B82-4F51-9487-91CD520BC5AE} + {C29610F6-4B71-49AD-B57F-784467C1CF1F} = {46CA5D70-7812-4809-8B40-31392DE18AA1} + {1D55596F-6184-4DA6-9DFE-BDB9B358EA4B} = {BDB6B966-4B82-4F51-9487-91CD520BC5AE} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {42610935-EABE-41BA-A9A9-57FB7A657F65} diff --git a/src/TwinGet.AutomationInterface/AutomationInterfaceConstants.cs b/src/TwinGet.AutomationInterface/AutomationInterfaceConstants.cs index 0bf5b77..d984e6b 100644 --- a/src/TwinGet.AutomationInterface/AutomationInterfaceConstants.cs +++ b/src/TwinGet.AutomationInterface/AutomationInterfaceConstants.cs @@ -1,11 +1,5 @@ // This file is licensed to you under MIT license. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace TwinGet.AutomationInterface { public static class AutomationInterfaceConstants diff --git a/src/TwinGet.AutomationInterface/Exceptions/CouldNotCreateTwincatDteException.cs b/src/TwinGet.AutomationInterface/Exceptions/CouldNotCreateTwincatDteException.cs index 7727a9f..6a03b04 100644 --- a/src/TwinGet.AutomationInterface/Exceptions/CouldNotCreateTwincatDteException.cs +++ b/src/TwinGet.AutomationInterface/Exceptions/CouldNotCreateTwincatDteException.cs @@ -1,11 +1,5 @@ // This file is licensed to you under MIT license. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace TwinGet.AutomationInterface.Exceptions { public class CouldNotCreateTwincatDteException : Exception diff --git a/src/TwinGet.AutomationInterface/Exceptions/DteInstanceIsNullException.cs b/src/TwinGet.AutomationInterface/Exceptions/DteInstanceIsNullException.cs index 2921b87..a5e9671 100644 --- a/src/TwinGet.AutomationInterface/Exceptions/DteInstanceIsNullException.cs +++ b/src/TwinGet.AutomationInterface/Exceptions/DteInstanceIsNullException.cs @@ -1,11 +1,5 @@ // This file is licensed to you under MIT license. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace TwinGet.AutomationInterface.Exceptions { public class DteInstanceIsNullException : Exception diff --git a/src/TwinGet.Utils/IO/Directory.cs b/src/TwinGet.Utils/IO/Directory.cs new file mode 100644 index 0000000..b0b1b0f --- /dev/null +++ b/src/TwinGet.Utils/IO/Directory.cs @@ -0,0 +1,55 @@ +// This file is licensed to you under MIT license. + +namespace TwinGet.Utils.IO +{ + public static class Directory + { + /// + /// Method for copying a directory content. + /// + /// Source directory. + /// Destination directory. + /// If true, copy the contents of all subdirectories. If false, copy only the contents of the current directory + /// + public static async Task CopyDirectory(string sourceDir, string destinationDir, bool recursive = true) + { + // Get information about the source directory + var dir = new DirectoryInfo(sourceDir); + + // Check if the source directory exists + if (!dir.Exists) + throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}"); + + // Create the destination directory + System.IO.Directory.CreateDirectory(destinationDir); + + + // Get the files in the source directory and copy to the destination directory + foreach (string filename in System.IO.Directory.EnumerateFiles(sourceDir)) + { + using (FileStream sourceStream = File.Open(filename, FileMode.Open)) + { + using (FileStream destinationStream = File.Create(Path.Combine(destinationDir, Path.GetFileName(filename)))) + { + await sourceStream.CopyToAsync(destinationStream); + } + } + } + + // Cache directories before we start copying + DirectoryInfo[] dirs = dir.GetDirectories(); + + // If recursive and copying subdirectories, recursively call this method + if (recursive) + { + foreach (DirectoryInfo subDir in dirs) + { + string newDestinationDir = Path.Combine(destinationDir, subDir.Name); + await CopyDirectory(subDir.FullName, newDestinationDir, true); + } + } + + return; + } + } +} diff --git a/src/TwinGet.Utils/TwinGet.Utils.csproj b/src/TwinGet.Utils/TwinGet.Utils.csproj new file mode 100644 index 0000000..cfadb03 --- /dev/null +++ b/src/TwinGet.Utils/TwinGet.Utils.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs index 3826052..c2fad8b 100644 --- a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs +++ b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs @@ -6,6 +6,11 @@ public class AutomationInterfaceTests : IDisposable { private bool _disposedValue; + public AutomationInterfaceTests() + { + + } + [StaFact] public void ProgId_ShouldNotBeNullOrEmpty() { diff --git a/test/TwinGet.AutomationInterface.Test/Usings.cs b/test/TwinGet.AutomationInterface.Test/GlobalUsings.cs similarity index 96% rename from test/TwinGet.AutomationInterface.Test/Usings.cs rename to test/TwinGet.AutomationInterface.Test/GlobalUsings.cs index e88ee46..b346bd7 100644 --- a/test/TwinGet.AutomationInterface.Test/Usings.cs +++ b/test/TwinGet.AutomationInterface.Test/GlobalUsings.cs @@ -1,4 +1,4 @@ -// This file is licensed to you under MIT license. - -global using FluentAssertions; -global using Xunit; +// This file is licensed to you under MIT license. + +global using FluentAssertions; +global using Xunit; diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatSolutionConstants.cs b/test/TwinGet.AutomationInterface.Test/TestTwincatSolutionConstants.cs index aa91ea8..97b5127 100644 --- a/test/TwinGet.AutomationInterface.Test/TestTwincatSolutionConstants.cs +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatSolutionConstants.cs @@ -1,11 +1,5 @@ // This file is licensed to you under MIT license. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace TwinGet.AutomationInterface.Test { internal static class TestTwincatSolutionConstants diff --git a/test/TwinGet.AutomationInterface.Test/xunit.runner.json b/test/TwinGet.AutomationInterface.Test/xunit.runner.json new file mode 100644 index 0000000..31fb263 --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/xunit.runner.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "parallelizeAssembly": true, + "parallelizeTestCollections": true +} diff --git a/test/TwinGet.Utils.Test/GlobalUsings.cs b/test/TwinGet.Utils.Test/GlobalUsings.cs new file mode 100644 index 0000000..b346bd7 --- /dev/null +++ b/test/TwinGet.Utils.Test/GlobalUsings.cs @@ -0,0 +1,4 @@ +// This file is licensed to you under MIT license. + +global using FluentAssertions; +global using Xunit; diff --git a/test/TwinGet.Utils.Test/IOTests/DirectoryComparer.cs b/test/TwinGet.Utils.Test/IOTests/DirectoryComparer.cs new file mode 100644 index 0000000..a3db68d --- /dev/null +++ b/test/TwinGet.Utils.Test/IOTests/DirectoryComparer.cs @@ -0,0 +1,23 @@ +// This file is licensed to you under MIT license. + +namespace TwinGet.Utils.Test.IOTests +{ + internal static class DirectoryComparer + { + public static bool Equals(string path1, string path2) + { + DirectoryInfo dir1 = new(path1); + DirectoryInfo dir2 = new(path2); + + IEnumerable dir1SubDir = Directory.GetDirectories(dir1.FullName, "*", SearchOption.AllDirectories).Select(d => Path.GetRelativePath(dir1.FullName, d)); + IEnumerable dir2SubDir = Directory.GetDirectories(dir2.FullName, "*", SearchOption.AllDirectories).Select(d => Path.GetRelativePath(dir2.FullName, d)); + bool sameDirectoryStructure = dir1SubDir.SequenceEqual(dir2SubDir); + + List dir1Files = dir1.GetFiles("*.*", SearchOption.AllDirectories).Select(f => Path.GetRelativePath(dir1.FullName, f.FullName)).ToList(); + List dir2Files = dir2.GetFiles("*.*", SearchOption.AllDirectories).Select(f => Path.GetRelativePath(dir2.FullName, f.FullName)).ToList(); + bool sameFileStructure = dir1Files.SequenceEqual(dir2Files); + + return sameDirectoryStructure && sameFileStructure; + } + } +} diff --git a/test/TwinGet.Utils.Test/IOTests/DirectoryTests.cs b/test/TwinGet.Utils.Test/IOTests/DirectoryTests.cs new file mode 100644 index 0000000..8ac917b --- /dev/null +++ b/test/TwinGet.Utils.Test/IOTests/DirectoryTests.cs @@ -0,0 +1,73 @@ +// This file is licensed to you under MIT license. + +namespace TwinGet.Utils.Test.IOTests +{ + public class DirectoryTests + { + private enum PathType + { + Directory = 0, File = 1 + } + + private static readonly Dictionary s_testDirContent = new() + { + {@"folder1", PathType.Directory}, + {@"folder1/file1.txt", PathType.File}, + {@"folder2", PathType.Directory}, + {@"folder2/file1.txt", PathType.File}, + {@"folder2/folder1", PathType.Directory}, + {@"folder2/folder1/folder1", PathType.Directory}, + {@"folder2/folder1/folder1/file1.txt", PathType.File}, + {@"folder2/folder1/folder1/file2.txt", PathType.File}, + {@"file1.txt", PathType.File}, + {@"file2.txt", PathType.File}, + }; + + /// + /// Initialize a test directory and return the root path. + /// + /// The root path of the test directory. + private static string InitializeTestDirectory(Dictionary directoryStructure) + { + string testDirectory = Path.Join(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(testDirectory); + + static void createItem(PathType type, string path) + { + switch (type) + { + case PathType.Directory: + Directory.CreateDirectory(path); break; + case PathType.File: + File.Create(path)?.Close(); break; + } + } + + foreach (KeyValuePair content in directoryStructure) + { + string fullPath = Path.Join(testDirectory, content.Key); + createItem(content.Value, fullPath); + } + + return testDirectory; + } + + [Fact] + public async void CopyDirectory_Recursively() + { + // Arrange + string sourceDirectory = InitializeTestDirectory(s_testDirContent); + string destinationDirectory = Path.Join(Path.GetTempPath(), Guid.NewGuid().ToString()); + + // Act + await IO.Directory.CopyDirectory(sourceDirectory, destinationDirectory); + + // Assert + DirectoryComparer.Equals(destinationDirectory, sourceDirectory).Should().BeTrue(); + + // Cleanup + Directory.Delete(destinationDirectory, true); + Directory.Delete(sourceDirectory, true); + } + } +} diff --git a/test/TwinGet.Utils.Test/TwinGet.Utils.Test.csproj b/test/TwinGet.Utils.Test/TwinGet.Utils.Test.csproj new file mode 100644 index 0000000..a38a2ad --- /dev/null +++ b/test/TwinGet.Utils.Test/TwinGet.Utils.Test.csproj @@ -0,0 +1,30 @@ + + + + net7.0 + enable + enable + + false + true + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/test/TwinGet.Utils.Test/xunit.runner.json b/test/TwinGet.Utils.Test/xunit.runner.json new file mode 100644 index 0000000..31fb263 --- /dev/null +++ b/test/TwinGet.Utils.Test/xunit.runner.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "parallelizeAssembly": true, + "parallelizeTestCollections": true +} From 0212bcb2fc2efe2947ac38501f1aa39eccf02abd Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Wed, 6 Dec 2023 22:17:10 +0200 Subject: [PATCH 05/27] test: Added more test setup to `AutomationInterfaceTests` During test setup, copy `TestTwincatProject` to a new temporary test directory. This helps isolate the test subjects between tests. --- .../AutomationInterface.cs | 6 ++-- .../AutomationInterfaceTests.cs | 31 +++++++++++++++++-- .../TestTwincatSolutionConstants.cs | 3 +- .../TwinGet.AutomationInterface.Test.csproj | 1 + .../IOTests/DirectoryTests.cs | 4 +-- 5 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/TwinGet.AutomationInterface/AutomationInterface.cs b/src/TwinGet.AutomationInterface/AutomationInterface.cs index cd4d9a5..fc27944 100644 --- a/src/TwinGet.AutomationInterface/AutomationInterface.cs +++ b/src/TwinGet.AutomationInterface/AutomationInterface.cs @@ -106,10 +106,10 @@ private void ThrowIfDteIsNull() if (_dte is null) { throw new DteInstanceIsNullException($"No {nameof(DTE2)} instance available."); } } - private void ThrowIfInvalidSolutionPath(string solutionPath) + private static void ThrowIfInvalidSolutionPath(string solutionPath) { if (string.IsNullOrEmpty(solutionPath)) { throw new ArgumentException("Solution path cannot be null or empty."); } - if (!Path.Exists(solutionPath)) { throw new ArgumentException($"Provided solution path \"{solutionPath.ToString()}\" does not exists."); } + if (!Path.Exists(solutionPath)) { throw new ArgumentException($"Provided solution path \"{solutionPath}\" does not exists."); } } public void LoadSolution(string filePath) @@ -125,7 +125,7 @@ public void LoadSolution(string filePath) _solution.Open(filePath); } - public static void SaveProjectAsLibrary(string solutionPath, string outFile) + public static void SaveProjectAsLibrary(string outFile, string solutionPath = "") { } diff --git a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs index c2fad8b..fb5ef5d 100644 --- a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs +++ b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs @@ -5,10 +5,36 @@ namespace TwinGet.AutomationInterface.Test public class AutomationInterfaceTests : IDisposable { private bool _disposedValue; + private readonly string _testDirectory; + private readonly string _testTwincatSolution; + + private static string ProvisionTestDirectory() + { + string testDirectory = Path.Join(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(testDirectory); + + return testDirectory; + } + + private void TearDownTestDirectory() + { + if (Directory.Exists(_testDirectory)) { Directory.Delete(_testDirectory, true); } + } + + private static void CopyTestTwincatProject(string destination) + { + TwinGet.Utils.IO.Directory.CopyDirectory( + TestTwincatSolutionConstants.s_testTwincatProject, + destination).Wait(); + } public AutomationInterfaceTests() { + _testDirectory = ProvisionTestDirectory(); + + CopyTestTwincatProject(_testDirectory); + _testTwincatSolution = Directory.GetFiles(_testDirectory, "*.sln", SearchOption.AllDirectories).First(); } [StaFact] @@ -32,9 +58,9 @@ public void ProgId_ShouldBeValid() public void LoadSolution_WithValidPath_ShouldLoadSuccessfully() { var sut = new AutomationInterface(); - sut.LoadSolution(TestTwincatSolutionConstants.s_testTwincatSolution); + sut.LoadSolution(_testTwincatSolution); - sut.LoadedSolutionFile.Should().Be(TestTwincatSolutionConstants.s_testTwincatSolution); + sut.LoadedSolutionFile.Should().Be(_testTwincatSolution); sut.IsSolutionOpen.Should().BeTrue(); } @@ -47,6 +73,7 @@ protected virtual void Dispose(bool disposing) ; } + TearDownTestDirectory(); GC.Collect(); GC.WaitForPendingFinalizers(); _disposedValue = true; diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatSolutionConstants.cs b/test/TwinGet.AutomationInterface.Test/TestTwincatSolutionConstants.cs index 97b5127..478bb29 100644 --- a/test/TwinGet.AutomationInterface.Test/TestTwincatSolutionConstants.cs +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatSolutionConstants.cs @@ -4,7 +4,6 @@ namespace TwinGet.AutomationInterface.Test { internal static class TestTwincatSolutionConstants { - public static readonly string s_testTwincatSolutionRootFolder = Path.Join(@$"{AppDomain.CurrentDomain.BaseDirectory}", @"TestTwincatProject"); - public static readonly string s_testTwincatSolution = Path.Join(@$"{AppDomain.CurrentDomain.BaseDirectory}", @"TestTwincatProject\TestTwincatProject.sln"); + internal static readonly string s_testTwincatProject = Path.Join(@$"{AppDomain.CurrentDomain.BaseDirectory}", @"TestTwincatProject"); } } diff --git a/test/TwinGet.AutomationInterface.Test/TwinGet.AutomationInterface.Test.csproj b/test/TwinGet.AutomationInterface.Test/TwinGet.AutomationInterface.Test.csproj index 37ed932..84fca33 100644 --- a/test/TwinGet.AutomationInterface.Test/TwinGet.AutomationInterface.Test.csproj +++ b/test/TwinGet.AutomationInterface.Test/TwinGet.AutomationInterface.Test.csproj @@ -25,6 +25,7 @@ + diff --git a/test/TwinGet.Utils.Test/IOTests/DirectoryTests.cs b/test/TwinGet.Utils.Test/IOTests/DirectoryTests.cs index 8ac917b..f6b5aa5 100644 --- a/test/TwinGet.Utils.Test/IOTests/DirectoryTests.cs +++ b/test/TwinGet.Utils.Test/IOTests/DirectoryTests.cs @@ -27,7 +27,7 @@ private enum PathType /// Initialize a test directory and return the root path. /// /// The root path of the test directory. - private static string InitializeTestDirectory(Dictionary directoryStructure) + private static string ProvisionTestDirectory(Dictionary directoryStructure) { string testDirectory = Path.Join(Path.GetTempPath(), Guid.NewGuid().ToString()); Directory.CreateDirectory(testDirectory); @@ -56,7 +56,7 @@ static void createItem(PathType type, string path) public async void CopyDirectory_Recursively() { // Arrange - string sourceDirectory = InitializeTestDirectory(s_testDirContent); + string sourceDirectory = ProvisionTestDirectory(s_testDirContent); string destinationDirectory = Path.Join(Path.GetTempPath(), Guid.NewGuid().ToString()); // Act From af86521735c0f26c2ef845c95f7d305aa98177fe Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Wed, 6 Dec 2023 22:33:24 +0200 Subject: [PATCH 06/27] refactor: Adjusted exception type for `ThrowIfInvalidSolutionPath` `FileNotFoundException` is a more appropriate exception to throw. --- .../AutomationInterface.cs | 2 +- .../AutomationInterfaceTests.cs | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/TwinGet.AutomationInterface/AutomationInterface.cs b/src/TwinGet.AutomationInterface/AutomationInterface.cs index fc27944..bc2edf6 100644 --- a/src/TwinGet.AutomationInterface/AutomationInterface.cs +++ b/src/TwinGet.AutomationInterface/AutomationInterface.cs @@ -109,7 +109,7 @@ private void ThrowIfDteIsNull() private static void ThrowIfInvalidSolutionPath(string solutionPath) { if (string.IsNullOrEmpty(solutionPath)) { throw new ArgumentException("Solution path cannot be null or empty."); } - if (!Path.Exists(solutionPath)) { throw new ArgumentException($"Provided solution path \"{solutionPath}\" does not exists."); } + if (!Path.Exists(solutionPath)) { throw new FileNotFoundException($"Provided solution path \"{solutionPath}\" does not exists."); } } public void LoadSolution(string filePath) diff --git a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs index fb5ef5d..085d7e3 100644 --- a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs +++ b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs @@ -37,6 +37,12 @@ public AutomationInterfaceTests() _testTwincatSolution = Directory.GetFiles(_testDirectory, "*.sln", SearchOption.AllDirectories).First(); } + [Fact] + public void PathExists_ShouldSupportRelativePath() + { + Path.Exists(@"TestTwincatProject\TestTwincatProject.sln").Should().BeTrue(); + } + [StaFact] public void ProgId_ShouldNotBeNullOrEmpty() { @@ -64,6 +70,29 @@ public void LoadSolution_WithValidPath_ShouldLoadSuccessfully() sut.IsSolutionOpen.Should().BeTrue(); } + [StaFact] + public void LoadSolution_WithInvalidPath_ShouldThrow() + { + var sut = new AutomationInterface(); + + string invalidSolution = $"{Guid.NewGuid()}.sln"; + Action loadSolution = () => sut.LoadSolution(invalidSolution); + + loadSolution.Should().Throw(); + sut.IsSolutionOpen.Should().BeFalse(); + } + + [StaFact] + public void LoadSolution_WithEmptyPath_ShouldThrow() + { + var sut = new AutomationInterface(); + + Action loadSolution = () => sut.LoadSolution(string.Empty); + + loadSolution.Should().Throw(); + sut.IsSolutionOpen.Should().BeFalse(); + } + protected virtual void Dispose(bool disposing) { if (!_disposedValue) From bfea60378208bf0240af89bf76a994c0a25d5331 Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Fri, 8 Dec 2023 22:27:10 +0200 Subject: [PATCH 07/27] chore: Added configuration parameter to build script --- build.ps1 | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/build.ps1 b/build.ps1 index 3266c6f..2d09354 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,3 +1,12 @@ +[CmdletBinding()] +param ( + [Parameter()] + [string] + [ValidateSet("Release", "Debug")] + $Configuration = "Debug" +) + + $vsInstallationPath = .\tools\vswhere.exe -latest -property installationPath $msBuildPath = Join-Path -Path $vsInstallationPath -ChildPath 'MSBuild\Current\Bin\MSBuild.exe' @@ -9,4 +18,4 @@ $null = Get-Command nuget -ErrorAction Stop $solution = Join-Path -Path $PSScriptRoot -ChildPath 'TwinGet.sln' dotnet restore $solution -& $msBuildPath $solution -p:Configuration=Release +& $msBuildPath $solution -p:Configuration=$Configuration From 2351be6008f170b03dc15e30179b51abc185966d Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Fri, 8 Dec 2023 23:48:45 +0200 Subject: [PATCH 08/27] test: Refactored `AutomationInterfaceTests` Added `TestProject` class for cleaner test setup and tear-down. --- .../AutomationInterfaceTests.cs | 49 ++++--------------- .../TestProject.cs | 41 ++++++++++++++++ ...ants.cs => TestTwincatProjectConstants.cs} | 2 +- 3 files changed, 51 insertions(+), 41 deletions(-) create mode 100644 test/TwinGet.AutomationInterface.Test/TestProject.cs rename test/TwinGet.AutomationInterface.Test/{TestTwincatSolutionConstants.cs => TestTwincatProjectConstants.cs} (82%) diff --git a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs index 085d7e3..4969f64 100644 --- a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs +++ b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs @@ -5,38 +5,6 @@ namespace TwinGet.AutomationInterface.Test public class AutomationInterfaceTests : IDisposable { private bool _disposedValue; - private readonly string _testDirectory; - private readonly string _testTwincatSolution; - - private static string ProvisionTestDirectory() - { - string testDirectory = Path.Join(Path.GetTempPath(), Guid.NewGuid().ToString()); - Directory.CreateDirectory(testDirectory); - - return testDirectory; - } - - private void TearDownTestDirectory() - { - if (Directory.Exists(_testDirectory)) { Directory.Delete(_testDirectory, true); } - } - - private static void CopyTestTwincatProject(string destination) - { - TwinGet.Utils.IO.Directory.CopyDirectory( - TestTwincatSolutionConstants.s_testTwincatProject, - destination).Wait(); - } - - public AutomationInterfaceTests() - { - _testDirectory = ProvisionTestDirectory(); - - CopyTestTwincatProject(_testDirectory); - - _testTwincatSolution = Directory.GetFiles(_testDirectory, "*.sln", SearchOption.AllDirectories).First(); - } - [Fact] public void PathExists_ShouldSupportRelativePath() { @@ -46,7 +14,7 @@ public void PathExists_ShouldSupportRelativePath() [StaFact] public void ProgId_ShouldNotBeNullOrEmpty() { - var sut = new AutomationInterface(); + using AutomationInterface sut = new(); sut.ProgId.Should().NotBeNullOrEmpty(); } @@ -54,7 +22,7 @@ public void ProgId_ShouldNotBeNullOrEmpty() [StaFact] public void ProgId_ShouldBeValid() { - var sut = new AutomationInterface(); + using AutomationInterface sut = new(); AutomationInterfaceConstants.SupportedProgIds.Should().Contain(sut.ProgId); } @@ -63,17 +31,19 @@ public void ProgId_ShouldBeValid() [StaFact] public void LoadSolution_WithValidPath_ShouldLoadSuccessfully() { - var sut = new AutomationInterface(); - sut.LoadSolution(_testTwincatSolution); + using AutomationInterface sut = new(); + using TestProject project = new(); + + sut.LoadSolution(project.SolutionPath); - sut.LoadedSolutionFile.Should().Be(_testTwincatSolution); + sut.LoadedSolutionFile.Should().Be(project.SolutionPath); sut.IsSolutionOpen.Should().BeTrue(); } [StaFact] public void LoadSolution_WithInvalidPath_ShouldThrow() { - var sut = new AutomationInterface(); + using AutomationInterface sut = new(); string invalidSolution = $"{Guid.NewGuid()}.sln"; Action loadSolution = () => sut.LoadSolution(invalidSolution); @@ -85,7 +55,7 @@ public void LoadSolution_WithInvalidPath_ShouldThrow() [StaFact] public void LoadSolution_WithEmptyPath_ShouldThrow() { - var sut = new AutomationInterface(); + using AutomationInterface sut = new(); Action loadSolution = () => sut.LoadSolution(string.Empty); @@ -102,7 +72,6 @@ protected virtual void Dispose(bool disposing) ; } - TearDownTestDirectory(); GC.Collect(); GC.WaitForPendingFinalizers(); _disposedValue = true; diff --git a/test/TwinGet.AutomationInterface.Test/TestProject.cs b/test/TwinGet.AutomationInterface.Test/TestProject.cs new file mode 100644 index 0000000..25779f6 --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/TestProject.cs @@ -0,0 +1,41 @@ +// This file is licensed to you under MIT license. + +namespace TwinGet.AutomationInterface.Test +{ + internal class TestProject : IDisposable + { + private bool _disposedValue; + public string Path { get; private set; } + public string SolutionPath { get; private set; } + + public TestProject() + { + Path = System.IO.Path.Join(System.IO.Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(Path); + + TwinGet.Utils.IO.Directory.CopyDirectory( + TestTwincatProjectConstants.s_testTwincatProject, + Path).Wait(); + + SolutionPath = Directory.GetFiles(Path, "*.sln", SearchOption.AllDirectories).First(); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) { } + + if (Directory.Exists(Path)) { Directory.Delete(Path, true); } + _disposedValue = true; + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatSolutionConstants.cs b/test/TwinGet.AutomationInterface.Test/TestTwincatProjectConstants.cs similarity index 82% rename from test/TwinGet.AutomationInterface.Test/TestTwincatSolutionConstants.cs rename to test/TwinGet.AutomationInterface.Test/TestTwincatProjectConstants.cs index 478bb29..3274caa 100644 --- a/test/TwinGet.AutomationInterface.Test/TestTwincatSolutionConstants.cs +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProjectConstants.cs @@ -2,7 +2,7 @@ namespace TwinGet.AutomationInterface.Test { - internal static class TestTwincatSolutionConstants + internal static class TestTwincatProjectConstants { internal static readonly string s_testTwincatProject = Path.Join(@$"{AppDomain.CurrentDomain.BaseDirectory}", @"TestTwincatProject"); } From 8a5c44a9a466b40a877b07a2033f1489c7d6a3d0 Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Sat, 9 Dec 2023 00:43:27 +0200 Subject: [PATCH 09/27] feat: Added `TwincatProject` as a wrapper class for a TwinCAT project Also added more test TwinCAT projects. --- .../AutomationInterface.cs | 31 ++++++-- .../AutomationInterfaceConstants.cs | 4 +- .../Exceptions/NotATwincatProject.cs | 19 +++++ .../TwinGet.AutomationInterface.csproj | 4 + .../TwincatProject.cs | 22 ++++++ .../Utils/TwincatUtils.cs | 14 ++++ .../TestNonTwincatProject1/App.config | 6 ++ .../TestNonTwincatProject1/Program.cs | 15 ++++ .../Properties/AssemblyInfo.cs | 36 +++++++++ .../TestNonTwincatProject1.csproj | 53 ++++++++++++++ .../TestTwincatProject/TestTwincatProject.sln | 72 +++++++++++++++++- .../TestPlcProject/POUs/MAIN.TcPOU | 0 .../TestPlcProject/PlcTask.TcTTO | 0 .../TestPlcProject/TestPlcProject.plcproj | 0 .../TestTwincatProject1.tsproj} | 14 +++- .../TestTwincatProject2.tsproj | 4 + .../TestTwincatProject3.tspproj | 4 + .../TestTwincatProject3.tspproj.bak | 4 + .../TwinGet.AutomationInterface.Test.csproj | 3 +- .../TwincatProjectTests.cs | 73 +++++++++++++++++++ 20 files changed, 364 insertions(+), 14 deletions(-) create mode 100644 src/TwinGet.AutomationInterface/Exceptions/NotATwincatProject.cs create mode 100644 src/TwinGet.AutomationInterface/TwincatProject.cs create mode 100644 src/TwinGet.AutomationInterface/Utils/TwincatUtils.cs create mode 100644 test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestNonTwincatProject1/App.config create mode 100644 test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestNonTwincatProject1/Program.cs create mode 100644 test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestNonTwincatProject1/Properties/AssemblyInfo.cs create mode 100644 test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestNonTwincatProject1/TestNonTwincatProject1.csproj rename test/TwinGet.AutomationInterface.Test/TestTwincatProject/{TestTwincatProject => TestTwincatProject1}/TestPlcProject/POUs/MAIN.TcPOU (100%) rename test/TwinGet.AutomationInterface.Test/TestTwincatProject/{TestTwincatProject => TestTwincatProject1}/TestPlcProject/PlcTask.TcTTO (100%) rename test/TwinGet.AutomationInterface.Test/TestTwincatProject/{TestTwincatProject => TestTwincatProject1}/TestPlcProject/TestPlcProject.plcproj (100%) rename test/TwinGet.AutomationInterface.Test/TestTwincatProject/{TestTwincatProject/TestTwincatProject.tsproj => TestTwincatProject1/TestTwincatProject1.tsproj} (73%) create mode 100644 test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestTwincatProject2.tsproj create mode 100644 test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject3/TestTwincatProject3.tspproj create mode 100644 test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject3/TestTwincatProject3.tspproj.bak create mode 100644 test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs diff --git a/src/TwinGet.AutomationInterface/AutomationInterface.cs b/src/TwinGet.AutomationInterface/AutomationInterface.cs index bc2edf6..5fab69c 100644 --- a/src/TwinGet.AutomationInterface/AutomationInterface.cs +++ b/src/TwinGet.AutomationInterface/AutomationInterface.cs @@ -3,6 +3,7 @@ using System.Runtime.InteropServices; using System.Runtime.Versioning; using EnvDTE80; +using TCatSysManagerLib; using TwinGet.AutomationInterface.ComMessageFilter; using TwinGet.AutomationInterface.Exceptions; using TwinGet.AutomationInterface.Utils; @@ -15,6 +16,7 @@ public class AutomationInterface : IDisposable private string _progId; private EnvDTE.Solution? _solution; private EnvDTE.SolutionBuild? _solutionBuild; + private ITcSysManager15 _systemManager; private readonly EnvDTE80.DTE2? _dte; private bool _disposedValue; public string ProgId { get => _progId; } @@ -28,14 +30,19 @@ public AutomationInterface() if (_dte is null) { throw new CouldNotCreateTwincatDteException("Is TwinCAT installed in this system?"); } } + internal AutomationInterface(ref EnvDTE80.DTE2? dte) + { + MessageFilter.Register(); + _dte = TryInitializeDte(out _progId); + dte = _dte; + if (_dte is null) { throw new CouldNotCreateTwincatDteException("Is TwinCAT installed in this system?"); } + } + protected virtual void Dispose(bool disposing) { if (!_disposedValue) { - if (disposing) - { - ; - } + if (disposing) { } CleanUp(); _disposedValue = true; @@ -106,10 +113,20 @@ private void ThrowIfDteIsNull() if (_dte is null) { throw new DteInstanceIsNullException($"No {nameof(DTE2)} instance available."); } } + private static void ThrowNullOrEmptySolutionPath() + { + throw new ArgumentException("Solution path cannot be null or empty."); + } + + private static void ThrowSolutionPathNotFound(string solutionPath) + { + throw new FileNotFoundException($"Provided solution path \"{solutionPath}\" does not exists."); + } + private static void ThrowIfInvalidSolutionPath(string solutionPath) { - if (string.IsNullOrEmpty(solutionPath)) { throw new ArgumentException("Solution path cannot be null or empty."); } - if (!Path.Exists(solutionPath)) { throw new FileNotFoundException($"Provided solution path \"{solutionPath}\" does not exists."); } + if (string.IsNullOrEmpty(solutionPath)) { ThrowNullOrEmptySolutionPath(); } + if (!Path.Exists(solutionPath)) { ThrowSolutionPathNotFound(solutionPath); } } public void LoadSolution(string filePath) @@ -126,7 +143,7 @@ public void LoadSolution(string filePath) } public static void SaveProjectAsLibrary(string outFile, string solutionPath = "") - { + { } } diff --git a/src/TwinGet.AutomationInterface/AutomationInterfaceConstants.cs b/src/TwinGet.AutomationInterface/AutomationInterfaceConstants.cs index d984e6b..c5cb41f 100644 --- a/src/TwinGet.AutomationInterface/AutomationInterfaceConstants.cs +++ b/src/TwinGet.AutomationInterface/AutomationInterfaceConstants.cs @@ -10,6 +10,8 @@ public static class AutomationInterfaceConstants "VisualStudio.DTE.14.0", "VisualStudio.DTE.15.0" }; - + public const string TwincatXaeProjectKind = "{B1E792BE-AA5F-4E3C-8C82-674BF9C0715B}"; + public const string TwincatPlcProjectKind = TwincatXaeProjectKind; + public const int ProjectItemStartingIndex = 1; } } diff --git a/src/TwinGet.AutomationInterface/Exceptions/NotATwincatProject.cs b/src/TwinGet.AutomationInterface/Exceptions/NotATwincatProject.cs new file mode 100644 index 0000000..0ceecd2 --- /dev/null +++ b/src/TwinGet.AutomationInterface/Exceptions/NotATwincatProject.cs @@ -0,0 +1,19 @@ +// This file is licensed to you under MIT license. + +namespace TwinGet.AutomationInterface.Exceptions +{ + public class NotATwincatProject : Exception + { + public NotATwincatProject() + { + } + + public NotATwincatProject(string message) : base(message) + { + } + + public NotATwincatProject(string message, Exception inner) : base(message, inner) + { + } + } +} diff --git a/src/TwinGet.AutomationInterface/TwinGet.AutomationInterface.csproj b/src/TwinGet.AutomationInterface/TwinGet.AutomationInterface.csproj index f35edb8..02af73d 100644 --- a/src/TwinGet.AutomationInterface/TwinGet.AutomationInterface.csproj +++ b/src/TwinGet.AutomationInterface/TwinGet.AutomationInterface.csproj @@ -24,4 +24,8 @@ + + + + diff --git a/src/TwinGet.AutomationInterface/TwincatProject.cs b/src/TwinGet.AutomationInterface/TwincatProject.cs new file mode 100644 index 0000000..f3704e0 --- /dev/null +++ b/src/TwinGet.AutomationInterface/TwincatProject.cs @@ -0,0 +1,22 @@ +// This file is licensed to you under MIT license. + +using TwinGet.AutomationInterface.Exceptions; +using TwinGet.AutomationInterface.Utils; + +namespace TwinGet.AutomationInterface +{ + internal class TwincatProject + { + private EnvDTE.Project _project; + + public TwincatProject(EnvDTE.Project project) + { + if (!TwincatUtils.IsTwincatProject(project)) + { + throw new NotATwincatProject($"The provided {project.Name} is not a TwinCAT project."); + } + + _project = project; + } + } +} diff --git a/src/TwinGet.AutomationInterface/Utils/TwincatUtils.cs b/src/TwinGet.AutomationInterface/Utils/TwincatUtils.cs new file mode 100644 index 0000000..c28ab26 --- /dev/null +++ b/src/TwinGet.AutomationInterface/Utils/TwincatUtils.cs @@ -0,0 +1,14 @@ +// This file is licensed to you under MIT license. + +namespace TwinGet.AutomationInterface.Utils +{ + internal static class TwincatUtils + { + public static bool IsTwincatProject(EnvDTE.Project project) + { + bool isTwincatXaeProject = project.Kind == AutomationInterfaceConstants.TwincatXaeProjectKind; + bool isTwincatPlcProject = project.Kind == AutomationInterfaceConstants.TwincatPlcProjectKind; + return isTwincatXaeProject || isTwincatPlcProject; + } + } +} diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestNonTwincatProject1/App.config b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestNonTwincatProject1/App.config new file mode 100644 index 0000000..731f6de --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestNonTwincatProject1/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestNonTwincatProject1/Program.cs b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestNonTwincatProject1/Program.cs new file mode 100644 index 0000000..40e635e --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestNonTwincatProject1/Program.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace TestNonTwincatProject1 +{ + class Program + { + static void Main(string[] args) + { + } + } +} diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestNonTwincatProject1/Properties/AssemblyInfo.cs b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestNonTwincatProject1/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..264892d --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestNonTwincatProject1/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("TestNonTwincatProject1")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("TestNonTwincatProject1")] +[assembly: AssemblyCopyright("Copyright © 2023")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7a6348ef-db45-4990-bea5-70fa6567a01b")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestNonTwincatProject1/TestNonTwincatProject1.csproj b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestNonTwincatProject1/TestNonTwincatProject1.csproj new file mode 100644 index 0000000..73fa428 --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestNonTwincatProject1/TestNonTwincatProject1.csproj @@ -0,0 +1,53 @@ + + + + + Debug + AnyCPU + {7A6348EF-DB45-4990-BEA5-70FA6567A01B} + Exe + TestNonTwincatProject1 + TestNonTwincatProject1 + v4.6.1 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject.sln b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject.sln index 08180b7..ae58a33 100644 --- a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject.sln +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject.sln @@ -1,22 +1,31 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# TcXaeShell Solution File, Format Version 11.00 +# Visual Studio 15 VisualStudioVersion = 15.0.33403.129 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{B1E792BE-AA5F-4E3C-8C82-674BF9C0715B}") = "TestTwincatProject", "TestTwincatProject\TestTwincatProject.tsproj", "{AF0AA87D-6A50-4129-B38E-8931819C4FEB}" +Project("{B1E792BE-AA5F-4E3C-8C82-674BF9C0715B}") = "TestTwincatProject1", "TestTwincatProject1\TestTwincatProject1.tsproj", "{AF0AA87D-6A50-4129-B38E-8931819C4FEB}" +EndProject +Project("{B1E792BE-AA5F-4E3C-8C82-674BF9C0715B}") = "TestTwincatProject2", "TestTwincatProject2\TestTwincatProject2.tsproj", "{CD779436-E0BF-4C3D-9866-879794D3B6A4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestNonTwincatProject1", "TestNonTwincatProject1\TestNonTwincatProject1.csproj", "{7A6348EF-DB45-4990-BEA5-70FA6567A01B}" +EndProject +Project("{DFBE7525-6864-4E62-8B2E-D530D69D9D96}") = "TestTwincatProject3", "TestTwincatProject3\TestTwincatProject3.tspproj", "{6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU Debug|TwinCAT CE7 (ARMV7) = Debug|TwinCAT CE7 (ARMV7) Debug|TwinCAT OS (ARMT2) = Debug|TwinCAT OS (ARMT2) Debug|TwinCAT RT (x64) = Debug|TwinCAT RT (x64) Debug|TwinCAT RT (x86) = Debug|TwinCAT RT (x86) + Release|Any CPU = Release|Any CPU Release|TwinCAT CE7 (ARMV7) = Release|TwinCAT CE7 (ARMV7) Release|TwinCAT OS (ARMT2) = Release|TwinCAT OS (ARMT2) Release|TwinCAT RT (x64) = Release|TwinCAT RT (x64) Release|TwinCAT RT (x86) = Release|TwinCAT RT (x86) EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|Any CPU.ActiveCfg = Debug|TwinCAT RT (x86) {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT CE7 (ARMV7).ActiveCfg = Debug|TwinCAT CE7 (ARMV7) {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT CE7 (ARMV7).Build.0 = Debug|TwinCAT CE7 (ARMV7) {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT OS (ARMT2).ActiveCfg = Debug|TwinCAT OS (ARMT2) @@ -25,6 +34,7 @@ Global {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT RT (x64).Build.0 = Debug|TwinCAT RT (x64) {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT RT (x86).ActiveCfg = Debug|TwinCAT RT (x86) {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Debug|TwinCAT RT (x86).Build.0 = Debug|TwinCAT RT (x86) + {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|Any CPU.ActiveCfg = Release|TwinCAT RT (x86) {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT CE7 (ARMV7).ActiveCfg = Release|TwinCAT CE7 (ARMV7) {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT CE7 (ARMV7).Build.0 = Release|TwinCAT CE7 (ARMV7) {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT OS (ARMT2).ActiveCfg = Release|TwinCAT OS (ARMT2) @@ -33,6 +43,25 @@ Global {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT RT (x64).Build.0 = Release|TwinCAT RT (x64) {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT RT (x86).ActiveCfg = Release|TwinCAT RT (x86) {AF0AA87D-6A50-4129-B38E-8931819C4FEB}.Release|TwinCAT RT (x86).Build.0 = Release|TwinCAT RT (x86) + {CD779436-E0BF-4C3D-9866-879794D3B6A4}.Debug|Any CPU.ActiveCfg = Debug|TwinCAT RT (x86) + {CD779436-E0BF-4C3D-9866-879794D3B6A4}.Debug|TwinCAT CE7 (ARMV7).ActiveCfg = Debug|TwinCAT CE7 (ARMV7) + {CD779436-E0BF-4C3D-9866-879794D3B6A4}.Debug|TwinCAT CE7 (ARMV7).Build.0 = Debug|TwinCAT CE7 (ARMV7) + {CD779436-E0BF-4C3D-9866-879794D3B6A4}.Debug|TwinCAT OS (ARMT2).ActiveCfg = Debug|TwinCAT OS (ARMT2) + {CD779436-E0BF-4C3D-9866-879794D3B6A4}.Debug|TwinCAT OS (ARMT2).Build.0 = Debug|TwinCAT OS (ARMT2) + {CD779436-E0BF-4C3D-9866-879794D3B6A4}.Debug|TwinCAT RT (x64).ActiveCfg = Debug|TwinCAT RT (x64) + {CD779436-E0BF-4C3D-9866-879794D3B6A4}.Debug|TwinCAT RT (x64).Build.0 = Debug|TwinCAT RT (x64) + {CD779436-E0BF-4C3D-9866-879794D3B6A4}.Debug|TwinCAT RT (x86).ActiveCfg = Debug|TwinCAT RT (x86) + {CD779436-E0BF-4C3D-9866-879794D3B6A4}.Debug|TwinCAT RT (x86).Build.0 = Debug|TwinCAT RT (x86) + {CD779436-E0BF-4C3D-9866-879794D3B6A4}.Release|Any CPU.ActiveCfg = Release|TwinCAT RT (x86) + {CD779436-E0BF-4C3D-9866-879794D3B6A4}.Release|TwinCAT CE7 (ARMV7).ActiveCfg = Release|TwinCAT CE7 (ARMV7) + {CD779436-E0BF-4C3D-9866-879794D3B6A4}.Release|TwinCAT CE7 (ARMV7).Build.0 = Release|TwinCAT CE7 (ARMV7) + {CD779436-E0BF-4C3D-9866-879794D3B6A4}.Release|TwinCAT OS (ARMT2).ActiveCfg = Release|TwinCAT OS (ARMT2) + {CD779436-E0BF-4C3D-9866-879794D3B6A4}.Release|TwinCAT OS (ARMT2).Build.0 = Release|TwinCAT OS (ARMT2) + {CD779436-E0BF-4C3D-9866-879794D3B6A4}.Release|TwinCAT RT (x64).ActiveCfg = Release|TwinCAT RT (x64) + {CD779436-E0BF-4C3D-9866-879794D3B6A4}.Release|TwinCAT RT (x64).Build.0 = Release|TwinCAT RT (x64) + {CD779436-E0BF-4C3D-9866-879794D3B6A4}.Release|TwinCAT RT (x86).ActiveCfg = Release|TwinCAT RT (x86) + {CD779436-E0BF-4C3D-9866-879794D3B6A4}.Release|TwinCAT RT (x86).Build.0 = Release|TwinCAT RT (x86) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|Any CPU.ActiveCfg = Debug|TwinCAT RT (x86) {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT CE7 (ARMV7).ActiveCfg = Debug|TwinCAT CE7 (ARMV7) {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT CE7 (ARMV7).Build.0 = Debug|TwinCAT CE7 (ARMV7) {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT OS (ARMT2).ActiveCfg = Debug|TwinCAT OS (ARMT2) @@ -41,6 +70,7 @@ Global {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT RT (x64).Build.0 = Debug|TwinCAT RT (x64) {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT RT (x86).ActiveCfg = Debug|TwinCAT RT (x86) {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT RT (x86).Build.0 = Debug|TwinCAT RT (x86) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|Any CPU.ActiveCfg = Release|TwinCAT RT (x86) {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT CE7 (ARMV7).ActiveCfg = Release|TwinCAT CE7 (ARMV7) {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT CE7 (ARMV7).Build.0 = Release|TwinCAT CE7 (ARMV7) {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT OS (ARMT2).ActiveCfg = Release|TwinCAT OS (ARMT2) @@ -49,6 +79,44 @@ Global {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT RT (x64).Build.0 = Release|TwinCAT RT (x64) {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT RT (x86).ActiveCfg = Release|TwinCAT RT (x86) {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT RT (x86).Build.0 = Release|TwinCAT RT (x86) + {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Debug|TwinCAT CE7 (ARMV7).ActiveCfg = Debug|Any CPU + {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Debug|TwinCAT CE7 (ARMV7).Build.0 = Debug|Any CPU + {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Debug|TwinCAT OS (ARMT2).ActiveCfg = Debug|Any CPU + {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Debug|TwinCAT OS (ARMT2).Build.0 = Debug|Any CPU + {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Debug|TwinCAT RT (x64).ActiveCfg = Debug|Any CPU + {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Debug|TwinCAT RT (x64).Build.0 = Debug|Any CPU + {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Debug|TwinCAT RT (x86).ActiveCfg = Debug|Any CPU + {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Debug|TwinCAT RT (x86).Build.0 = Debug|Any CPU + {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Release|Any CPU.Build.0 = Release|Any CPU + {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Release|TwinCAT CE7 (ARMV7).ActiveCfg = Release|Any CPU + {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Release|TwinCAT CE7 (ARMV7).Build.0 = Release|Any CPU + {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Release|TwinCAT OS (ARMT2).ActiveCfg = Release|Any CPU + {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Release|TwinCAT OS (ARMT2).Build.0 = Release|Any CPU + {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Release|TwinCAT RT (x64).ActiveCfg = Release|Any CPU + {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Release|TwinCAT RT (x64).Build.0 = Release|Any CPU + {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Release|TwinCAT RT (x86).ActiveCfg = Release|Any CPU + {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Release|TwinCAT RT (x86).Build.0 = Release|Any CPU + {6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}.Debug|Any CPU.ActiveCfg = Debug|TwinCAT OS (ARMT2) + {6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}.Debug|TwinCAT CE7 (ARMV7).ActiveCfg = Debug|TwinCAT CE7 (ARMV7) + {6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}.Debug|TwinCAT CE7 (ARMV7).Build.0 = Debug|TwinCAT CE7 (ARMV7) + {6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}.Debug|TwinCAT OS (ARMT2).ActiveCfg = Debug|TwinCAT OS (ARMT2) + {6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}.Debug|TwinCAT OS (ARMT2).Build.0 = Debug|TwinCAT OS (ARMT2) + {6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}.Debug|TwinCAT RT (x64).ActiveCfg = Debug|TwinCAT RT (x64) + {6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}.Debug|TwinCAT RT (x64).Build.0 = Debug|TwinCAT RT (x64) + {6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}.Debug|TwinCAT RT (x86).ActiveCfg = Debug|TwinCAT RT (x86) + {6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}.Debug|TwinCAT RT (x86).Build.0 = Debug|TwinCAT RT (x86) + {6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}.Release|Any CPU.ActiveCfg = Release|TwinCAT OS (ARMT2) + {6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}.Release|TwinCAT CE7 (ARMV7).ActiveCfg = Release|TwinCAT CE7 (ARMV7) + {6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}.Release|TwinCAT CE7 (ARMV7).Build.0 = Release|TwinCAT CE7 (ARMV7) + {6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}.Release|TwinCAT OS (ARMT2).ActiveCfg = Release|TwinCAT OS (ARMT2) + {6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}.Release|TwinCAT OS (ARMT2).Build.0 = Release|TwinCAT OS (ARMT2) + {6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}.Release|TwinCAT RT (x64).ActiveCfg = Release|TwinCAT RT (x64) + {6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}.Release|TwinCAT RT (x64).Build.0 = Release|TwinCAT RT (x64) + {6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}.Release|TwinCAT RT (x86).ActiveCfg = Release|TwinCAT RT (x86) + {6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}.Release|TwinCAT RT (x86).Build.0 = Release|TwinCAT RT (x86) EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject/TestPlcProject/POUs/MAIN.TcPOU b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject/POUs/MAIN.TcPOU similarity index 100% rename from test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject/TestPlcProject/POUs/MAIN.TcPOU rename to test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject/POUs/MAIN.TcPOU diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject/TestPlcProject/PlcTask.TcTTO b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject/PlcTask.TcTTO similarity index 100% rename from test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject/TestPlcProject/PlcTask.TcTTO rename to test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject/PlcTask.TcTTO diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject/TestPlcProject/TestPlcProject.plcproj b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject/TestPlcProject.plcproj similarity index 100% rename from test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject/TestPlcProject/TestPlcProject.plcproj rename to test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject/TestPlcProject.plcproj diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject/TestTwincatProject.tsproj b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestTwincatProject1.tsproj similarity index 73% rename from test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject/TestTwincatProject.tsproj rename to test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestTwincatProject1.tsproj index e209fc5..32d435c 100644 --- a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject/TestTwincatProject.tsproj +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestTwincatProject1.tsproj @@ -10,15 +10,23 @@ - + TestPlcProject Instance {08500001-0000-0000-F000-000000000064} - 1 - Default + 0 + PlcTask + + #x02010030 + + 20 + 10000000 + + + diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestTwincatProject2.tsproj b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestTwincatProject2.tsproj new file mode 100644 index 0000000..91d52a6 --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestTwincatProject2.tsproj @@ -0,0 +1,4 @@ + + + + diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject3/TestTwincatProject3.tspproj b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject3/TestTwincatProject3.tspproj new file mode 100644 index 0000000..d7de1b7 --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject3/TestTwincatProject3.tspproj @@ -0,0 +1,4 @@ + + + + diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject3/TestTwincatProject3.tspproj.bak b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject3/TestTwincatProject3.tspproj.bak new file mode 100644 index 0000000..91d52a6 --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject3/TestTwincatProject3.tspproj.bak @@ -0,0 +1,4 @@ + + + + diff --git a/test/TwinGet.AutomationInterface.Test/TwinGet.AutomationInterface.Test.csproj b/test/TwinGet.AutomationInterface.Test/TwinGet.AutomationInterface.Test.csproj index 84fca33..307614a 100644 --- a/test/TwinGet.AutomationInterface.Test/TwinGet.AutomationInterface.Test.csproj +++ b/test/TwinGet.AutomationInterface.Test/TwinGet.AutomationInterface.Test.csproj @@ -6,6 +6,7 @@ enable false + $(DefaultItemExcludes);TestTwincatProject\TestNonTwincatProject1\** @@ -29,7 +30,7 @@ - + diff --git a/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs b/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs new file mode 100644 index 0000000..c931d8a --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs @@ -0,0 +1,73 @@ +// This file is licensed to you under MIT license. + +using TwinGet.AutomationInterface.Exceptions; + +namespace TwinGet.AutomationInterface.Test +{ + public class TwincatProjectTests + { + private const string _nonTwincatProjectNamePrefix = "TestNonTwincatProject"; + private const string _twincatProjectNamePrefix = "TestTwincatProject"; + + private EnvDTE.Project? GetFirstTwincatProject(EnvDTE.Projects projects) + { + for (int i = 1; i <= projects.Count; i++) + { + EnvDTE.Project tmp = projects.Item(i); + + if (tmp.Name.Contains(_twincatProjectNamePrefix)) + { + return tmp; + } + } + + return null; + } + + private EnvDTE.Project? GetFirstNonTwincatProject(EnvDTE.Projects projects) + { + for (int i = 1; i <= projects.Count; i++) + { + EnvDTE.Project tmp = projects.Item(i); + + if (tmp.Name.Contains(_nonTwincatProjectNamePrefix)) + { + return tmp; + } + } + + return null; + } + + [StaFact] + public void Construct_WithTwincatProject_ShouldSucceed() + { + // Arrange + EnvDTE80.DTE2? dte = null; + using TestProject testProject = new(); + using AutomationInterface ai = new(ref dte); + ai.LoadSolution(testProject.SolutionPath); + EnvDTE.Project? project = GetFirstTwincatProject(dte.Solution.Projects); + + // "Assert" no exception + TwincatProject? tcProject = new(project); + } + + [StaFact] + public void Construct_WithNonTwincatProject_ShouldThrow() + { + // Arrange + EnvDTE80.DTE2? dte = null; + using TestProject testProject = new(); + using AutomationInterface ai = new(ref dte); + ai.LoadSolution(testProject.SolutionPath); + EnvDTE.Project? project = GetFirstNonTwincatProject(dte.Solution.Projects); + + // Act + Action constructTwincatProject = () => { TwincatProject? tcProject = new(project); }; + + // Assert + constructTwincatProject.Should().Throw(); + } + } +} From c72247953aad183cb3c03ce25e943848eb292f30 Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Sat, 9 Dec 2023 21:43:33 +0200 Subject: [PATCH 10/27] feat: Added file-sharing option to `CopyDirectory()` This way when creating a source stream it can share access with other threads. --- src/TwinGet.Utils/IO/Directory.cs | 7 ++++--- test/TwinGet.AutomationInterface.Test/TestProject.cs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/TwinGet.Utils/IO/Directory.cs b/src/TwinGet.Utils/IO/Directory.cs index b0b1b0f..0bb2ec3 100644 --- a/src/TwinGet.Utils/IO/Directory.cs +++ b/src/TwinGet.Utils/IO/Directory.cs @@ -9,9 +9,10 @@ public static class Directory /// /// Source directory. /// Destination directory. + /// A > value specifying the type of access other threads have to the file. /// If true, copy the contents of all subdirectories. If false, copy only the contents of the current directory /// - public static async Task CopyDirectory(string sourceDir, string destinationDir, bool recursive = true) + public static async Task CopyDirectory(string sourceDir, string destinationDir, FileShare share = FileShare.None, bool recursive = true) { // Get information about the source directory var dir = new DirectoryInfo(sourceDir); @@ -27,7 +28,7 @@ public static async Task CopyDirectory(string sourceDir, string destinationDir, // Get the files in the source directory and copy to the destination directory foreach (string filename in System.IO.Directory.EnumerateFiles(sourceDir)) { - using (FileStream sourceStream = File.Open(filename, FileMode.Open)) + using (FileStream sourceStream = File.Open(filename, FileMode.Open, FileAccess.Read, share)) { using (FileStream destinationStream = File.Create(Path.Combine(destinationDir, Path.GetFileName(filename)))) { @@ -45,7 +46,7 @@ public static async Task CopyDirectory(string sourceDir, string destinationDir, foreach (DirectoryInfo subDir in dirs) { string newDestinationDir = Path.Combine(destinationDir, subDir.Name); - await CopyDirectory(subDir.FullName, newDestinationDir, true); + await CopyDirectory(subDir.FullName, newDestinationDir, FileShare.Read, true); } } diff --git a/test/TwinGet.AutomationInterface.Test/TestProject.cs b/test/TwinGet.AutomationInterface.Test/TestProject.cs index 25779f6..aa88d53 100644 --- a/test/TwinGet.AutomationInterface.Test/TestProject.cs +++ b/test/TwinGet.AutomationInterface.Test/TestProject.cs @@ -15,7 +15,7 @@ public TestProject() TwinGet.Utils.IO.Directory.CopyDirectory( TestTwincatProjectConstants.s_testTwincatProject, - Path).Wait(); + Path, FileShare.Read).Wait(); SolutionPath = Directory.GetFiles(Path, "*.sln", SearchOption.AllDirectories).First(); } From 46b45fc19d53031ac0bd072de89edcc2b74a2311 Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Sun, 10 Dec 2023 01:22:53 +0200 Subject: [PATCH 11/27] feat: Added `ProjectFilesDeserialization` With classes for deserialize TwinCAT xml project files, including `.tsproj`, `.tspproj`, and `.plcproj` --- .../CouldNotCreateTwincatDteException.cs | 1 + .../Exceptions/DteInstanceIsNullException.cs | 1 + .../Exceptions/InvalidProjectFileFormat.cs | 27 +++ .../Exceptions/NotATwincatProject.cs | 1 + .../ProjectFilesDeserialization/PlcProject.cs | 113 +++++++++++ .../TcSmProject.cs | 178 ++++++++++++++++++ 6 files changed, 321 insertions(+) create mode 100644 src/TwinGet.AutomationInterface/Exceptions/InvalidProjectFileFormat.cs create mode 100644 src/TwinGet.AutomationInterface/ProjectFilesDeserialization/PlcProject.cs create mode 100644 src/TwinGet.AutomationInterface/ProjectFilesDeserialization/TcSmProject.cs diff --git a/src/TwinGet.AutomationInterface/Exceptions/CouldNotCreateTwincatDteException.cs b/src/TwinGet.AutomationInterface/Exceptions/CouldNotCreateTwincatDteException.cs index 6a03b04..f844712 100644 --- a/src/TwinGet.AutomationInterface/Exceptions/CouldNotCreateTwincatDteException.cs +++ b/src/TwinGet.AutomationInterface/Exceptions/CouldNotCreateTwincatDteException.cs @@ -2,6 +2,7 @@ namespace TwinGet.AutomationInterface.Exceptions { + [Serializable] public class CouldNotCreateTwincatDteException : Exception { public CouldNotCreateTwincatDteException() diff --git a/src/TwinGet.AutomationInterface/Exceptions/DteInstanceIsNullException.cs b/src/TwinGet.AutomationInterface/Exceptions/DteInstanceIsNullException.cs index a5e9671..d1db253 100644 --- a/src/TwinGet.AutomationInterface/Exceptions/DteInstanceIsNullException.cs +++ b/src/TwinGet.AutomationInterface/Exceptions/DteInstanceIsNullException.cs @@ -2,6 +2,7 @@ namespace TwinGet.AutomationInterface.Exceptions { + [Serializable] public class DteInstanceIsNullException : Exception { public DteInstanceIsNullException() diff --git a/src/TwinGet.AutomationInterface/Exceptions/InvalidProjectFileFormat.cs b/src/TwinGet.AutomationInterface/Exceptions/InvalidProjectFileFormat.cs new file mode 100644 index 0000000..0a61bad --- /dev/null +++ b/src/TwinGet.AutomationInterface/Exceptions/InvalidProjectFileFormat.cs @@ -0,0 +1,27 @@ +// This file is licensed to you under MIT license. + +namespace TwinGet.AutomationInterface.Exceptions +{ + [Serializable] + public class InvalidProjectFileFormat : FormatException + { + public string Path { get; } + + public InvalidProjectFileFormat() + { + } + + public InvalidProjectFileFormat(string message) : base(message) + { + } + + public InvalidProjectFileFormat(string message, Exception inner) : base(message, inner) + { + } + + public InvalidProjectFileFormat(string message, string filePath) : this(message) + { + Path = filePath; + } + } +} diff --git a/src/TwinGet.AutomationInterface/Exceptions/NotATwincatProject.cs b/src/TwinGet.AutomationInterface/Exceptions/NotATwincatProject.cs index 0ceecd2..8cef6cb 100644 --- a/src/TwinGet.AutomationInterface/Exceptions/NotATwincatProject.cs +++ b/src/TwinGet.AutomationInterface/Exceptions/NotATwincatProject.cs @@ -2,6 +2,7 @@ namespace TwinGet.AutomationInterface.Exceptions { + [Serializable] public class NotATwincatProject : Exception { public NotATwincatProject() diff --git a/src/TwinGet.AutomationInterface/ProjectFilesDeserialization/PlcProject.cs b/src/TwinGet.AutomationInterface/ProjectFilesDeserialization/PlcProject.cs new file mode 100644 index 0000000..24ee041 --- /dev/null +++ b/src/TwinGet.AutomationInterface/ProjectFilesDeserialization/PlcProject.cs @@ -0,0 +1,113 @@ +// This file is licensed to you under MIT license. + +using System.Xml; +using System.Xml.Serialization; + +namespace TwinGet.AutomationInterface.ProjectFilesDeserialization +{ + [XmlRoot("Project", Namespace = "http://schemas.microsoft.com/developer/msbuild/2003")] + public class PlcProject + { + [XmlAttribute("DefaultTargets")] + public string DefaultTargets { get; set; } + + [XmlElement("PropertyGroup")] + public PropertyGroup PropertyGroup { get; set; } + } + + public class PropertyGroup + { + [XmlElement("FileVersion")] + public string FileVersion { get; set; } + + [XmlElement("SchemaVersion")] + public string SchemaVersion { get; set; } + + [XmlElement("ProjectGuid")] + public string ProjectGuid { get; set; } + + [XmlElement("SubObjectsSortedByName")] + public string SubObjectsSortedByName { get; set; } + + [XmlElement("DownloadApplicationInfo")] + public string DownloadApplicationInfo { get; set; } + + [XmlElement("WriteProductVersion")] + public string WriteProductVersion { get; set; } + + [XmlElement("GenerateTpy")] + public string GenerateTpy { get; set; } + + [XmlElement("Name")] + public string Name { get; set; } + + [XmlElement("ProgramVersion")] + public string ProgramVersion { get; set; } + + [XmlElement("Application")] + public string Application { get; set; } + + [XmlElement("TypeSystem")] + public string TypeSystem { get; set; } + + [XmlElement("Implicit_Task_Info")] + public string Implicit_Task_Info { get; set; } + + [XmlElement("Implicit_KindOfTask")] + public string Implicit_KindOfTask { get; set; } + + [XmlElement("Implicit_Jitter_Distribution")] + public string Implicit_Jitter_Distribution { get; set; } + + [XmlElement("LibraryReferences")] + public string LibraryReferences { get; set; } + + [XmlElement("Released")] + public string Released { get; set; } + + [XmlElement("Title")] + public string Title { get; set; } + + [XmlElement("Company")] + public string Company { get; set; } + + [XmlElement("ProjectVersion")] + public string ProjectVersion { get; set; } + + [XmlElement("LibraryCategories")] + public LibraryCategories LibraryCategories { get; set; } + + [XmlElement("SelectedLibraryCategories")] + public SelectedLibraryCategories SelectedLibraryCategories { get; set; } + } + + public class LibraryCategories + { + [XmlElement("LibraryCategory")] + public LibraryCategory LibraryCategory { get; set; } + } + + public class LibraryCategory + { + [XmlElement("Id")] + public string Id { get; set; } + + [XmlElement("Version")] + public string Version { get; set; } + + [XmlElement("DefaultName")] + public string DefaultName { get; set; } + } + + public class SelectedLibraryCategories + { + [XmlElement("Id")] + public string Id { get; set; } + } + + public class TypeList + { + [XmlElement("Type")] + public List Types { get; set; } + } +} diff --git a/src/TwinGet.AutomationInterface/ProjectFilesDeserialization/TcSmProject.cs b/src/TwinGet.AutomationInterface/ProjectFilesDeserialization/TcSmProject.cs new file mode 100644 index 0000000..bd280ad --- /dev/null +++ b/src/TwinGet.AutomationInterface/ProjectFilesDeserialization/TcSmProject.cs @@ -0,0 +1,178 @@ +// This file is licensed to you under MIT license. + +using System.Xml.Serialization; + +namespace TwinGet.AutomationInterface.ProjectFilesDeserialization +{ + [XmlRoot("TcSmProject")] + public class TcSmProject + { + [XmlAttribute("TcSmVersion")] + public string TcSmVersion { get; set; } + + [XmlAttribute("TcVersion")] + public string TcVersion { get; set; } + + [XmlElement("Project")] + public Project Project { get; set; } + } + + public class Project + { + [XmlAttribute("ProjectGUID")] + public string ProjectGUID { get; set; } + + [XmlAttribute("ShowHideConfigurations")] + public string ShowHideConfigurations { get; set; } + + [XmlElement("System")] + public SystemElement System { get; set; } + + [XmlElement("Plc")] + public Plc Plc { get; set; } + } + + public class SystemElement + { + [XmlElement("Tasks")] + public Tasks Tasks { get; set; } + } + + public class Tasks + { + [XmlElement("Task")] + public Task Task { get; set; } + } + + public class Task + { + [XmlAttribute("Id")] + public int Id { get; set; } + + [XmlAttribute("Priority")] + public int Priority { get; set; } + + [XmlAttribute("CycleTime")] + public int CycleTime { get; set; } + + [XmlAttribute("AmsPort")] + public int AmsPort { get; set; } + + [XmlAttribute("AdtTasks")] + public bool AdtTasks { get; set; } + + [XmlElement("Name")] + public string Name { get; set; } + } + + public class Plc + { + [XmlElement("Project")] + public List Projects { get; set; } + } + + public class ProjectElement + { + [XmlAttribute("GUID")] + public string GUID { get; set; } + + [XmlAttribute("Name")] + public string Name { get; set; } + + [XmlAttribute("PrjFilePath")] + public string PrjFilePath { get; set; } + + [XmlAttribute("TmcFilePath")] + public string TmcFilePath { get; set; } + + [XmlAttribute("ReloadTmc")] + public bool ReloadTmc { get; set; } + + [XmlAttribute("AmsPort")] + public int AmsPort { get; set; } + + [XmlAttribute("FileArchiveSettings")] + public string FileArchiveSettings { get; set; } + + [XmlAttribute("SymbolicMapping")] + public bool SymbolicMapping { get; set; } + + [XmlElement("Instance")] + public Instance Instance { get; set; } + } + + public class Instance + { + [XmlAttribute("Id")] + public string Id { get; set; } + + [XmlAttribute("TcSmClass")] + public string TcSmClass { get; set; } + + [XmlAttribute("KeepUnrestoredLinks")] + public int KeepUnrestoredLinks { get; set; } + + [XmlAttribute("TmcPath")] + public string TmcPath { get; set; } + + [XmlAttribute("TmcHash")] + public string TmcHash { get; set; } + + [XmlElement("Name")] + public string Name { get; set; } + + [XmlElement("CLSID")] + public string CLSID { get; set; } + + [XmlElement("Contexts")] + public Contexts Contexts { get; set; } + + [XmlElement("TaskPouOids")] + public TaskPouOids TaskPouOids { get; set; } + } + + public class Contexts + { + [XmlElement("Context")] + public Context Context { get; set; } + } + + public class Context + { + [XmlElement("Id")] + public int Id { get; set; } + + [XmlElement("Name")] + public string Name { get; set; } + + [XmlElement("ManualConfig")] + public ManualConfig ManualConfig { get; set; } + + [XmlElement("Priority")] + public int Priority { get; set; } + + [XmlElement("CycleTime")] + public int CycleTime { get; set; } + } + + public class ManualConfig + { + [XmlElement("OTCID")] + public string OTCID { get; set; } + } + + public class TaskPouOids + { + [XmlElement("TaskPouOid")] + public TaskPouOid TaskPouOid { get; set; } + } + + public class TaskPouOid + { + [XmlAttribute("Prio")] + public int Prio { get; set; } + + [XmlAttribute("OTCID")] + public string OTCID { get; set; } + } +} From 2712457ecf18ef60dc84a5cf850cc17ba91aa27d Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Mon, 11 Dec 2023 00:24:45 +0200 Subject: [PATCH 12/27] feat: Added `TwincatProject` and `PlcProject` Acting as wrapper classes for TwinCAT project and a PLC project, respectively. --- .../AutomationInterface.cs | 19 +++- .../Exceptions/CouldNotGetSystemManager.cs | 20 ++++ .../Exceptions/CouldNotLookUpPlcProject.cs | 26 ++++++ .../Exceptions/InvalidProjectFileFormat.cs | 2 +- .../Exceptions/NotAPlcProject.cs | 20 ++++ src/TwinGet.AutomationInterface/PlcProject.cs | 59 ++++++++++++ .../TwinGet.AutomationInterface.csproj | 2 +- .../TwincatProject.cs | 91 ++++++++++++++++++- .../Utils/DteExtensions.cs | 12 +++ .../Utils/TwincatUtils.cs | 16 +++- .../TestProject.cs | 7 +- .../TestTwincatProject/README.md | 10 ++ .../TestTwincatProject/TestTwincatProject.sln | 57 ++++++++---- .../POUs/MAIN.TcPOU | 0 .../PlcTask.TcTTO | 0 .../TestPlcProject1.plcproj} | 14 ++- .../TestPlcProject2/TestPlcProject2.plcproj | 68 ++++++++++++++ .../TestTwincatProject1.tsproj | 18 +++- .../TestTwincatProject2.tsproj | 4 +- .../TestTwincatProject/TwinGet.libcat.xml | 8 ++ .../TestTwincatProjectConstants.cs | 2 + .../TwinGet.AutomationInterface.Test.csproj | 3 +- .../TwincatProjectTests.cs | 20 ++-- 23 files changed, 432 insertions(+), 46 deletions(-) create mode 100644 src/TwinGet.AutomationInterface/Exceptions/CouldNotGetSystemManager.cs create mode 100644 src/TwinGet.AutomationInterface/Exceptions/CouldNotLookUpPlcProject.cs create mode 100644 src/TwinGet.AutomationInterface/Exceptions/NotAPlcProject.cs create mode 100644 src/TwinGet.AutomationInterface/PlcProject.cs create mode 100644 test/TwinGet.AutomationInterface.Test/TestTwincatProject/README.md rename test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/{TestPlcProject => TestPlcProject1}/POUs/MAIN.TcPOU (100%) rename test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/{TestPlcProject => TestPlcProject1}/PlcTask.TcTTO (100%) rename test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/{TestPlcProject/TestPlcProject.plcproj => TestPlcProject1/TestPlcProject1.plcproj} (93%) create mode 100644 test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject2/TestPlcProject2.plcproj create mode 100644 test/TwinGet.AutomationInterface.Test/TestTwincatProject/TwinGet.libcat.xml diff --git a/src/TwinGet.AutomationInterface/AutomationInterface.cs b/src/TwinGet.AutomationInterface/AutomationInterface.cs index 5fab69c..71011b1 100644 --- a/src/TwinGet.AutomationInterface/AutomationInterface.cs +++ b/src/TwinGet.AutomationInterface/AutomationInterface.cs @@ -3,10 +3,10 @@ using System.Runtime.InteropServices; using System.Runtime.Versioning; using EnvDTE80; -using TCatSysManagerLib; using TwinGet.AutomationInterface.ComMessageFilter; using TwinGet.AutomationInterface.Exceptions; using TwinGet.AutomationInterface.Utils; +using static TwinGet.AutomationInterface.AutomationInterfaceConstants; namespace TwinGet.AutomationInterface { @@ -16,12 +16,14 @@ public class AutomationInterface : IDisposable private string _progId; private EnvDTE.Solution? _solution; private EnvDTE.SolutionBuild? _solutionBuild; - private ITcSysManager15 _systemManager; private readonly EnvDTE80.DTE2? _dte; private bool _disposedValue; + private readonly List _twincatProjects = new(); + public string ProgId { get => _progId; } public bool IsSolutionOpen { get => _solution?.IsOpen ?? false; } public string LoadedSolutionFile { get => _solution?.FileName ?? string.Empty; } + public IReadOnlyList TwincatProjects { get => _twincatProjects; } public AutomationInterface() { @@ -140,10 +142,21 @@ public void LoadSolution(string filePath) #pragma warning restore CS8602 // Dereference of a possibly null reference. _solutionBuild = _dte.Solution.SolutionBuild; _solution.Open(filePath); + + // Get TwinCAT projects + for (int i = ProjectItemStartingIndex; i <= _dte.Solution.Projects.Count; i++) + { + EnvDTE.Project currentItem = _dte.Solution.Projects.Item(i); + + if (currentItem.IsTwincatProject()) + { + _twincatProjects.Add(new TwincatProject(currentItem)); + } + } } public static void SaveProjectAsLibrary(string outFile, string solutionPath = "") - { + { } } diff --git a/src/TwinGet.AutomationInterface/Exceptions/CouldNotGetSystemManager.cs b/src/TwinGet.AutomationInterface/Exceptions/CouldNotGetSystemManager.cs new file mode 100644 index 0000000..70574f4 --- /dev/null +++ b/src/TwinGet.AutomationInterface/Exceptions/CouldNotGetSystemManager.cs @@ -0,0 +1,20 @@ +// This file is licensed to you under MIT license. + +namespace TwinGet.AutomationInterface.Exceptions +{ + [Serializable] + public class CouldNotGetSystemManager : Exception + { + public CouldNotGetSystemManager() + { + } + + public CouldNotGetSystemManager(string message) : base(message) + { + } + + public CouldNotGetSystemManager(string message, Exception inner) : base(message, inner) + { + } + } +} diff --git a/src/TwinGet.AutomationInterface/Exceptions/CouldNotLookUpPlcProject.cs b/src/TwinGet.AutomationInterface/Exceptions/CouldNotLookUpPlcProject.cs new file mode 100644 index 0000000..a44b6cc --- /dev/null +++ b/src/TwinGet.AutomationInterface/Exceptions/CouldNotLookUpPlcProject.cs @@ -0,0 +1,26 @@ +// This file is licensed to you under MIT license. + +namespace TwinGet.AutomationInterface.Exceptions +{ + [Serializable] + public class CouldNotLookUpPlcProject : Exception + { + public string? Name { get; } + public CouldNotLookUpPlcProject() + { + } + + public CouldNotLookUpPlcProject(string message) : base(message) + { + } + + public CouldNotLookUpPlcProject(string message, Exception inner) : base(message, inner) + { + } + + public CouldNotLookUpPlcProject(string message, string name) : this(message) + { + Name = name; + } + } +} diff --git a/src/TwinGet.AutomationInterface/Exceptions/InvalidProjectFileFormat.cs b/src/TwinGet.AutomationInterface/Exceptions/InvalidProjectFileFormat.cs index 0a61bad..7dbd0e0 100644 --- a/src/TwinGet.AutomationInterface/Exceptions/InvalidProjectFileFormat.cs +++ b/src/TwinGet.AutomationInterface/Exceptions/InvalidProjectFileFormat.cs @@ -5,7 +5,7 @@ namespace TwinGet.AutomationInterface.Exceptions [Serializable] public class InvalidProjectFileFormat : FormatException { - public string Path { get; } + public string? Path { get; } public InvalidProjectFileFormat() { diff --git a/src/TwinGet.AutomationInterface/Exceptions/NotAPlcProject.cs b/src/TwinGet.AutomationInterface/Exceptions/NotAPlcProject.cs new file mode 100644 index 0000000..afbcdbb --- /dev/null +++ b/src/TwinGet.AutomationInterface/Exceptions/NotAPlcProject.cs @@ -0,0 +1,20 @@ +// This file is licensed to you under MIT license. + +namespace TwinGet.AutomationInterface.Exceptions +{ + [Serializable] + public class NotAPlcProject : Exception + { + public NotAPlcProject() + { + } + + public NotAPlcProject(string message) : base(message) + { + } + + public NotAPlcProject(string message, Exception inner) : base(message, inner) + { + } + } +} diff --git a/src/TwinGet.AutomationInterface/PlcProject.cs b/src/TwinGet.AutomationInterface/PlcProject.cs new file mode 100644 index 0000000..8ba6a72 --- /dev/null +++ b/src/TwinGet.AutomationInterface/PlcProject.cs @@ -0,0 +1,59 @@ +// This file is licensed to you under MIT license. + +using System.Xml.Serialization; +using TCatSysManagerLib; +using TwinGet.AutomationInterface.Exceptions; + +namespace TwinGet.AutomationInterface +{ + public class PlcProject : ITcPlcIECProject3 + { + private readonly ITcPlcIECProject3 _plcProject; + private readonly ProjectFilesDeserialization.PlcProject _plcProjectFile; + + public string Name { get => _plcProjectFile.PropertyGroup.Name; } + public string Path { get; } + + + public PlcProject(ITcSmTreeItem treeItem, string path) : this(path) + { + try + { + _plcProject = (ITcPlcIECProject3)treeItem; + } + catch + { + throw new NotAPlcProject($"The provided tree item {treeItem.Name} is not a PLC project."); + } + } + + internal PlcProject(ITcPlcIECProject3 plcProject, string path) : this(path) + { + _plcProject = plcProject; + } + +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + protected PlcProject(string path) +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + { + ArgumentException.ThrowIfNullOrEmpty(path, nameof(path)); + + string xmlContent = File.ReadAllText(path); + XmlSerializer serializer = new(typeof(ProjectFilesDeserialization.PlcProject)); + + using (StringReader reader = new(xmlContent)) + { + ProjectFilesDeserialization.PlcProject plcProjectFile = serializer.Deserialize(reader) as ProjectFilesDeserialization.PlcProject ?? throw new InvalidProjectFileFormat("Could not deserialize the provided PLC project file.", path); + _plcProjectFile = plcProjectFile; + } + + Path = path; + } + + public void PlcOpenExport(string bstrFile, string bstrSelection) => _plcProject.PlcOpenExport(bstrFile, bstrSelection); + public void PlcOpenImport(string bstrFile, int options = 0, string bstrSelection = "", bool bSubTree = true) => _plcProject.PlcOpenImport(bstrFile, options, bstrSelection, bSubTree); + public void SaveAsLibrary(string bstrLibraryPath, bool binstall = false) => _plcProject.SaveAsLibrary(bstrLibraryPath, binstall); + public bool CheckAllObjects() => _plcProject.CheckAllObjects(); + public void RunStaticAnalysis(bool bCheckAll = true) => _plcProject.RunStaticAnalysis(bCheckAll); + } +} diff --git a/src/TwinGet.AutomationInterface/TwinGet.AutomationInterface.csproj b/src/TwinGet.AutomationInterface/TwinGet.AutomationInterface.csproj index 02af73d..7dc6f5e 100644 --- a/src/TwinGet.AutomationInterface/TwinGet.AutomationInterface.csproj +++ b/src/TwinGet.AutomationInterface/TwinGet.AutomationInterface.csproj @@ -25,7 +25,7 @@ - + diff --git a/src/TwinGet.AutomationInterface/TwincatProject.cs b/src/TwinGet.AutomationInterface/TwincatProject.cs index f3704e0..a47d190 100644 --- a/src/TwinGet.AutomationInterface/TwincatProject.cs +++ b/src/TwinGet.AutomationInterface/TwincatProject.cs @@ -1,22 +1,107 @@ // This file is licensed to you under MIT license. +using System.Xml.Serialization; +using EnvDTE; using TwinGet.AutomationInterface.Exceptions; +using TwinGet.AutomationInterface.ProjectFilesDeserialization; using TwinGet.AutomationInterface.Utils; +using ITcSysManagerAlias = TCatSysManagerLib.ITcSysManager15; namespace TwinGet.AutomationInterface { - internal class TwincatProject + public class TwincatProject : EnvDTE.Project { - private EnvDTE.Project _project; + private readonly EnvDTE.Project _project; + private readonly ITcSysManagerAlias _systemManager; + private readonly List _plcProjects; + public IReadOnlyList PlcProjects { get => _plcProjects; } public TwincatProject(EnvDTE.Project project) { - if (!TwincatUtils.IsTwincatProject(project)) + if (!project.IsTwincatProject()) { throw new NotATwincatProject($"The provided {project.Name} is not a TwinCAT project."); } _project = project; + _systemManager = (ITcSysManagerAlias)project.Object; + + if (_systemManager is null) + { + throw new CouldNotGetSystemManager($"Failed to get the system manager object from project {_project.Name}"); + } + + _plcProjects = new(TryGetPlcProjects(_systemManager, _project.FullName)); } + + private static IReadOnlyList TryGetPlcProjects(ITcSysManagerAlias systemManager, string projectPath) + { + List plcProjects = new(); + + ArgumentException.ThrowIfNullOrEmpty(projectPath, nameof(projectPath)); + + string xmlContent = File.ReadAllText(projectPath); + XmlSerializer serializer = new(typeof(TcSmProject)); + + using (StringReader reader = new(xmlContent)) + { + TcSmProject tcSmProject = serializer.Deserialize(reader) as TcSmProject ?? throw new InvalidProjectFileFormat("The format of TwinCAT project file is invalid.", projectPath); + string? rootDir = Path.GetDirectoryName(projectPath); + + if (tcSmProject.Project.Plc?.Projects is not null) + { +#pragma warning disable CS8604 // Possible null reference argument. + plcProjects.AddRange(from ProjectElement project in tcSmProject.Project.Plc.Projects + let plcProject = new PlcProject(systemManager.LookUpPlcProject(project.Name), Path.Join(rootDir, project.PrjFilePath)) + where plcProject is not null + select plcProject); +#pragma warning restore CS8604 // Possible null reference argument. + } + } + + return plcProjects; + } + + public void SaveAs(string NewFileName) => _project.SaveAs(NewFileName); + public void Save(string FileName = "") => _project.Save(FileName); + public void Delete() => _project.Delete(); + + public string Name { get => _project.Name; set => _project.Name = value; } + + public string FileName => _project.FileName; + + public bool IsDirty { get => _project.IsDirty; set => _project.IsDirty = value; } + + public Projects Collection => _project.Collection; + + public DTE DTE => _project.DTE; + + public string Kind => _project.Kind; + + public ProjectItems ProjectItems => _project.ProjectItems; + + public Properties Properties => _project.Properties; + + public string UniqueName => _project.UniqueName; + + public object Object => _project.Object; + + public object get_Extender(string ExtenderName) => _project.Extender[ExtenderName]; + + public object ExtenderNames => _project.ExtenderNames; + + public string ExtenderCATID => _project.ExtenderCATID; + + public string FullName => _project.FullName; + + public bool Saved { get => _project.Saved; set => _project.Saved = value; } + + public ConfigurationManager ConfigurationManager => _project.ConfigurationManager; + + public Globals Globals => _project.Globals; + + public ProjectItem ParentProjectItem => _project.ParentProjectItem; + + public CodeModel CodeModel => _project.CodeModel; } } diff --git a/src/TwinGet.AutomationInterface/Utils/DteExtensions.cs b/src/TwinGet.AutomationInterface/Utils/DteExtensions.cs index 36c40ed..64ddffa 100644 --- a/src/TwinGet.AutomationInterface/Utils/DteExtensions.cs +++ b/src/TwinGet.AutomationInterface/Utils/DteExtensions.cs @@ -4,6 +4,18 @@ namespace TwinGet.AutomationInterface.Utils { internal static class DteExtensions { + /// + /// Verify whether the current is a TwinCAT project. + /// + /// + /// true if the is a TwinCAT project, and false otherwise. + public static bool IsTwincatProject(this EnvDTE.Project project) + { + bool isTwincatXaeProject = project.Kind == AutomationInterfaceConstants.TwincatXaeProjectKind; + bool isTwincatPlcProject = project.Kind == AutomationInterfaceConstants.TwincatPlcProjectKind; + return isTwincatXaeProject || isTwincatPlcProject; + } + /// /// Check if TwinCAT is integrated with the given DTE instance. /// diff --git a/src/TwinGet.AutomationInterface/Utils/TwincatUtils.cs b/src/TwinGet.AutomationInterface/Utils/TwincatUtils.cs index c28ab26..f171bd4 100644 --- a/src/TwinGet.AutomationInterface/Utils/TwincatUtils.cs +++ b/src/TwinGet.AutomationInterface/Utils/TwincatUtils.cs @@ -1,14 +1,22 @@ // This file is licensed to you under MIT license. +using TCatSysManagerLib; +using TwinGet.AutomationInterface.Exceptions; +using ITcSmTreeItemAlias = TCatSysManagerLib.ITcSmTreeItem9; +using ITcSysManagerAlias = TCatSysManagerLib.ITcSysManager15; + namespace TwinGet.AutomationInterface.Utils { internal static class TwincatUtils { - public static bool IsTwincatProject(EnvDTE.Project project) + public static ITcPlcIECProject3? LookUpPlcProject(this ITcSysManagerAlias systemManager, string name) { - bool isTwincatXaeProject = project.Kind == AutomationInterfaceConstants.TwincatXaeProjectKind; - bool isTwincatPlcProject = project.Kind == AutomationInterfaceConstants.TwincatPlcProjectKind; - return isTwincatXaeProject || isTwincatPlcProject; + ArgumentException.ThrowIfNullOrEmpty(name, nameof(name)); + + var treeItem = (ITcSmTreeItemAlias)systemManager.LookupTreeItem($"TIPC^{name}^{name} Project"); + var plcProject = (ITcPlcIECProject3)treeItem ?? throw new CouldNotLookUpPlcProject($"Could not look up any PLC project with the provided name", name); + + return plcProject; } } } diff --git a/test/TwinGet.AutomationInterface.Test/TestProject.cs b/test/TwinGet.AutomationInterface.Test/TestProject.cs index aa88d53..8687883 100644 --- a/test/TwinGet.AutomationInterface.Test/TestProject.cs +++ b/test/TwinGet.AutomationInterface.Test/TestProject.cs @@ -1,12 +1,15 @@ // This file is licensed to you under MIT license. +using Microsoft.Build.Construction; + namespace TwinGet.AutomationInterface.Test { internal class TestProject : IDisposable { private bool _disposedValue; public string Path { get; private set; } - public string SolutionPath { get; private set; } + public string SolutionPath { get; } + public SolutionFile SolutionFile { get; } public TestProject() { @@ -18,6 +21,8 @@ public TestProject() Path, FileShare.Read).Wait(); SolutionPath = Directory.GetFiles(Path, "*.sln", SearchOption.AllDirectories).First(); + + SolutionFile = SolutionFile.Parse(SolutionPath); } protected virtual void Dispose(bool disposing) diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/README.md b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/README.md new file mode 100644 index 0000000..2ddc21e --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/README.md @@ -0,0 +1,10 @@ +# TestTwincatProject + +This is a test TwinCAT project used solely for the purpose of testing. The solution includes, + +* TestTwincatProject1, which is a TwinCAT XAE project +* TestTwincatProject2, which is a TwinCAT XAE project +* TestTwincatProject3, which is a TwinCAT PLC project (aka [standalone PLC project](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/4278074763.html&id=)) +* TestNonTwincatProject1, which is a console application project + +By having a test solution with a wide variety of projects, it helps our software be more robust. diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject.sln b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject.sln index ae58a33..198b8af 100644 --- a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject.sln +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject.sln @@ -61,24 +61,6 @@ Global {CD779436-E0BF-4C3D-9866-879794D3B6A4}.Release|TwinCAT RT (x64).Build.0 = Release|TwinCAT RT (x64) {CD779436-E0BF-4C3D-9866-879794D3B6A4}.Release|TwinCAT RT (x86).ActiveCfg = Release|TwinCAT RT (x86) {CD779436-E0BF-4C3D-9866-879794D3B6A4}.Release|TwinCAT RT (x86).Build.0 = Release|TwinCAT RT (x86) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|Any CPU.ActiveCfg = Debug|TwinCAT RT (x86) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT CE7 (ARMV7).ActiveCfg = Debug|TwinCAT CE7 (ARMV7) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT CE7 (ARMV7).Build.0 = Debug|TwinCAT CE7 (ARMV7) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT OS (ARMT2).ActiveCfg = Debug|TwinCAT OS (ARMT2) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT OS (ARMT2).Build.0 = Debug|TwinCAT OS (ARMT2) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT RT (x64).ActiveCfg = Debug|TwinCAT RT (x64) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT RT (x64).Build.0 = Debug|TwinCAT RT (x64) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT RT (x86).ActiveCfg = Debug|TwinCAT RT (x86) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT RT (x86).Build.0 = Debug|TwinCAT RT (x86) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|Any CPU.ActiveCfg = Release|TwinCAT RT (x86) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT CE7 (ARMV7).ActiveCfg = Release|TwinCAT CE7 (ARMV7) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT CE7 (ARMV7).Build.0 = Release|TwinCAT CE7 (ARMV7) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT OS (ARMT2).ActiveCfg = Release|TwinCAT OS (ARMT2) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT OS (ARMT2).Build.0 = Release|TwinCAT OS (ARMT2) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT RT (x64).ActiveCfg = Release|TwinCAT RT (x64) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT RT (x64).Build.0 = Release|TwinCAT RT (x64) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT RT (x86).ActiveCfg = Release|TwinCAT RT (x86) - {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT RT (x86).Build.0 = Release|TwinCAT RT (x86) {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Debug|Any CPU.Build.0 = Debug|Any CPU {7A6348EF-DB45-4990-BEA5-70FA6567A01B}.Debug|TwinCAT CE7 (ARMV7).ActiveCfg = Debug|Any CPU @@ -117,6 +99,45 @@ Global {6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}.Release|TwinCAT RT (x64).Build.0 = Release|TwinCAT RT (x64) {6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}.Release|TwinCAT RT (x86).ActiveCfg = Release|TwinCAT RT (x86) {6B5DCAB8-2334-4AAE-A500-4E3139EB5C7E}.Release|TwinCAT RT (x86).Build.0 = Release|TwinCAT RT (x86) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|Any CPU.ActiveCfg = Debug|TwinCAT RT (x86) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|Any CPU.Build.0 = Debug|TwinCAT RT (x86) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT CE7 (ARMV7).ActiveCfg = Debug|TwinCAT CE7 (ARMV7) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT CE7 (ARMV7).Build.0 = Debug|TwinCAT CE7 (ARMV7) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT OS (ARMT2).ActiveCfg = Debug|TwinCAT OS (ARMT2) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT OS (ARMT2).Build.0 = Debug|TwinCAT OS (ARMT2) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT RT (x64).ActiveCfg = Debug|TwinCAT RT (x64) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT RT (x64).Build.0 = Debug|TwinCAT RT (x64) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT RT (x86).ActiveCfg = Debug|TwinCAT RT (x86) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Debug|TwinCAT RT (x86).Build.0 = Debug|TwinCAT RT (x86) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|Any CPU.ActiveCfg = Release|TwinCAT RT (x86) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT CE7 (ARMV7).ActiveCfg = Release|TwinCAT CE7 (ARMV7) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT CE7 (ARMV7).Build.0 = Release|TwinCAT CE7 (ARMV7) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT OS (ARMT2).ActiveCfg = Release|TwinCAT OS (ARMT2) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT OS (ARMT2).Build.0 = Release|TwinCAT OS (ARMT2) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT RT (x64).ActiveCfg = Release|TwinCAT RT (x64) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT RT (x64).Build.0 = Release|TwinCAT RT (x64) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT RT (x86).ActiveCfg = Release|TwinCAT RT (x86) + {F423D9F4-0EF7-4885-8EC0-9A403A78EC70}.Release|TwinCAT RT (x86).Build.0 = Release|TwinCAT RT (x86) + {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Debug|Any CPU.ActiveCfg = Debug|TwinCAT RT (x64) + {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Debug|Any CPU.Build.0 = Debug|TwinCAT RT (x64) + {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Debug|TwinCAT CE7 (ARMV7).ActiveCfg = Debug|TwinCAT CE7 (ARMV7) + {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Debug|TwinCAT CE7 (ARMV7).Build.0 = Debug|TwinCAT CE7 (ARMV7) + {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Debug|TwinCAT OS (ARMT2).ActiveCfg = Debug|TwinCAT OS (ARMT2) + {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Debug|TwinCAT OS (ARMT2).Build.0 = Debug|TwinCAT OS (ARMT2) + {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Debug|TwinCAT RT (x64).ActiveCfg = Debug|TwinCAT RT (x64) + {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Debug|TwinCAT RT (x64).Build.0 = Debug|TwinCAT RT (x64) + {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Debug|TwinCAT RT (x86).ActiveCfg = Debug|TwinCAT RT (x86) + {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Debug|TwinCAT RT (x86).Build.0 = Debug|TwinCAT RT (x86) + {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Release|Any CPU.ActiveCfg = Release|TwinCAT RT (x64) + {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Release|Any CPU.Build.0 = Release|TwinCAT RT (x64) + {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Release|TwinCAT CE7 (ARMV7).ActiveCfg = Release|TwinCAT CE7 (ARMV7) + {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Release|TwinCAT CE7 (ARMV7).Build.0 = Release|TwinCAT CE7 (ARMV7) + {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Release|TwinCAT OS (ARMT2).ActiveCfg = Release|TwinCAT OS (ARMT2) + {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Release|TwinCAT OS (ARMT2).Build.0 = Release|TwinCAT OS (ARMT2) + {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Release|TwinCAT RT (x64).ActiveCfg = Release|TwinCAT RT (x64) + {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Release|TwinCAT RT (x64).Build.0 = Release|TwinCAT RT (x64) + {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Release|TwinCAT RT (x86).ActiveCfg = Release|TwinCAT RT (x86) + {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Release|TwinCAT RT (x86).Build.0 = Release|TwinCAT RT (x86) EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject/POUs/MAIN.TcPOU b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject1/POUs/MAIN.TcPOU similarity index 100% rename from test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject/POUs/MAIN.TcPOU rename to test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject1/POUs/MAIN.TcPOU diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject/PlcTask.TcTTO b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject1/PlcTask.TcTTO similarity index 100% rename from test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject/PlcTask.TcTTO rename to test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject1/PlcTask.TcTTO diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject/TestPlcProject.plcproj b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject1/TestPlcProject1.plcproj similarity index 93% rename from test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject/TestPlcProject.plcproj rename to test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject1/TestPlcProject1.plcproj index dcbfdce..51d08cd 100644 --- a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject/TestPlcProject.plcproj +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject1/TestPlcProject1.plcproj @@ -8,7 +8,7 @@ true true false - TestPlcProject + TestPlcProject1 3.1.4024.0 {da8cb013-01e2-4e76-aad8-c63cb197c3b9} {65a0cb05-45a0-424f-9370-5321fe656f5f} @@ -18,8 +18,18 @@ {bd174e45-2719-4cbf-b50e-b235af23264e} TwinGet false - TwinGet.TestPlcProject + TwinGet.TestTwincatProject1.TestPlcProject1 0.1.0 + + + {736e1fb9-7997-4a5d-a19a-cfcf55bcd1a4} + 1.0.0.0 + TwinGet + + + + {736e1fb9-7997-4a5d-a19a-cfcf55bcd1a4} + diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject2/TestPlcProject2.plcproj b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject2/TestPlcProject2.plcproj new file mode 100644 index 0000000..c4c4f92 --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestPlcProject2/TestPlcProject2.plcproj @@ -0,0 +1,68 @@ + + + + 1.0.0.0 + 2.0 + {a356eeb8-aafc-4761-a0dd-0b8c83ca855d} + True + true + true + false + TestPlcProject2 + 3.1.4024.0 + {ba45da6d-5cc1-4426-ba59-4238044fc4d7} + {9880b7b2-d5fd-495e-9796-381b090657e5} + {7516323b-4322-4c1f-8f67-f6fd4a8f6518} + {7b8f798a-c0e9-4bdf-a300-ebbf0c84ed9e} + {74c1b4aa-e6c6-482a-8260-7f4b18208c70} + {389ec1a5-314f-43d4-8cfe-0e5333a74c49} + TwinGet + false + TwinGet.TestTwincatProject2.TestPlcProject2 + 0.1.0 + + + {736e1fb9-7997-4a5d-a19a-cfcf55bcd1a4} + 1.0.0.0 + TwinGet + + + + {736e1fb9-7997-4a5d-a19a-cfcf55bcd1a4} + + + + + + + + "<ProjectRoot>" + + {40450F57-0AA3-4216-96F3-5444ECB29763} + + "{40450F57-0AA3-4216-96F3-5444ECB29763}" + + + ActiveVisuProfile + IR0whWr8bwfwBwAAiD2qpQAAAABVAgAA37x72QAAAAABAAAAAAAAAAEaUwB5AHMAdABlAG0ALgBTAHQAcgBpAG4AZwACTHsAZgA5ADUAYgBiADQAMgA2AC0ANQA1ADIANAAtADQAYgA0ADUALQA5ADQAMAAwAC0AZgBiADAAZgAyAGUANwA3AGUANQAxAGIAfQADCE4AYQBtAGUABDBUAHcAaQBuAEMAQQBUACAAMwAuADEAIABCAHUAaQBsAGQAIAA0ADAAMgA0AC4ANwAFFlAAcgBvAGYAaQBsAGUARABhAHQAYQAGTHsAMQA2AGUANQA1AGIANgAwAC0ANwAwADQAMwAtADQAYQA2ADMALQBiADYANQBiAC0ANgAxADQANwAxADMAOAA3ADgAZAA0ADIAfQAHEkwAaQBiAHIAYQByAGkAZQBzAAhMewAzAGIAZgBkADUANAA1ADkALQBiADAANwBmAC0ANABkADYAZQAtAGEAZQAxAGEALQBhADgAMwAzADUANgBhADUANQAxADQAMgB9AAlMewA5AGMAOQA1ADgAOQA2ADgALQAyAGMAOAA1AC0ANAAxAGIAYgAtADgAOAA3ADEALQA4ADkANQBmAGYAMQBmAGUAZABlADEAYQB9AAoOVgBlAHIAcwBpAG8AbgALBmkAbgB0AAwKVQBzAGEAZwBlAA0KVABpAHQAbABlAA4aVgBpAHMAdQBFAGwAZQBtAE0AZQB0AGUAcgAPDkMAbwBtAHAAYQBuAHkAEAxTAHkAcwB0AGUAbQARElYAaQBzAHUARQBsAGUAbQBzABIwVgBpAHMAdQBFAGwAZQBtAHMAUwBwAGUAYwBpAGEAbABDAG8AbgB0AHIAbwBsAHMAEyhWAGkAcwB1AEUAbABlAG0AcwBXAGkAbgBDAG8AbgB0AHIAbwBsAHMAFCRWAGkAcwB1AEUAbABlAG0AVABlAHgAdABFAGQAaQB0AG8AcgAVIlYAaQBzAHUATgBhAHQAaQB2AGUAQwBvAG4AdAByAG8AbAAWFHYAaQBzAHUAaQBuAHAAdQB0AHMAFwxzAHkAcwB0AGUAbQAYGFYAaQBzAHUARQBsAGUAbQBCAGEAcwBlABkmRABlAHYAUABsAGEAYwBlAGgAbwBsAGQAZQByAHMAVQBzAGUAZAAaCGIAbwBvAGwAGyJQAGwAdQBnAGkAbgBDAG8AbgBzAHQAcgBhAGkAbgB0AHMAHEx7ADQAMwBkADUAMgBiAGMAZQAtADkANAAyAGMALQA0ADQAZAA3AC0AOQBlADkANAAtADEAYgBmAGQAZgAzADEAMABlADYAMwBjAH0AHRxBAHQATABlAGEAcwB0AFYAZQByAHMAaQBvAG4AHhRQAGwAdQBnAGkAbgBHAHUAaQBkAB8WUwB5AHMAdABlAG0ALgBHAHUAaQBkACBIYQBmAGMAZAA1ADQANAA2AC0ANAA5ADEANAAtADQAZgBlADcALQBiAGIANwA4AC0AOQBiAGYAZgBlAGIANwAwAGYAZAAxADcAIRRVAHAAZABhAHQAZQBJAG4AZgBvACJMewBiADAAMwAzADYANgBhADgALQBiADUAYwAwAC0ANABiADkAYQAtAGEAMAAwAGUALQBlAGIAOAA2ADAAMQAxADEAMAA0AGMAMwB9ACMOVQBwAGQAYQB0AGUAcwAkTHsAMQA4ADYAOABmAGYAYwA5AC0AZQA0AGYAYwAtADQANQAzADIALQBhAGMAMAA2AC0AMQBlADMAOQBiAGIANQA1ADcAYgA2ADkAfQAlTHsAYQA1AGIAZAA0ADgAYwAzAC0AMABkADEANwAtADQAMQBiADUALQBiADEANgA0AC0ANQBmAGMANgBhAGQAMgBiADkANgBiADcAfQAmFk8AYgBqAGUAYwB0AHMAVAB5AHAAZQAnVFUAcABkAGEAdABlAEwAYQBuAGcAdQBhAGcAZQBNAG8AZABlAGwARgBvAHIAQwBvAG4AdgBlAHIAdABpAGIAbABlAEwAaQBiAHIAYQByAGkAZQBzACgQTABpAGIAVABpAHQAbABlACkUTABpAGIAQwBvAG0AcABhAG4AeQAqHlUAcABkAGEAdABlAFAAcgBvAHYAaQBkAGUAcgBzACs4UwB5AHMAdABlAG0ALgBDAG8AbABsAGUAYwB0AGkAbwBuAHMALgBIAGEAcwBoAHQAYQBiAGwAZQAsEnYAaQBzAHUAZQBsAGUAbQBzAC1INgBjAGIAMQBjAGQAZQAxAC0AZAA1AGQAYwAtADQAYQAzAGIALQA5ADAANQA0AC0AMgAxAGYAYQA3ADUANgBhADMAZgBhADQALihJAG4AdABlAHIAZgBhAGMAZQBWAGUAcgBzAGkAbwBuAEkAbgBmAG8AL0x7AGMANgAxADEAZQA0ADAAMAAtADcAZgBiADkALQA0AGMAMwA1AC0AYgA5AGEAYwAtADQAZQAzADEANABiADUAOQA5ADYANAAzAH0AMBhNAGEAagBvAHIAVgBlAHIAcwBpAG8AbgAxGE0AaQBuAG8AcgBWAGUAcgBzAGkAbwBuADIMTABlAGcAYQBjAHkAMzBMAGEAbgBnAHUAYQBnAGUATQBvAGQAZQBsAFYAZQByAHMAaQBvAG4ASQBuAGYAbwA0MEwAbwBhAGQATABpAGIAcgBhAHIAaQBlAHMASQBuAHQAbwBQAHIAbwBqAGUAYwB0ADUaQwBvAG0AcABhAHQAaQBiAGkAbABpAHQAeQDQAAIaA9ADAS0E0AUGGgfQBwgaAUUHCQjQAAkaBEUKCwQDAAAABQAAAA0AAAAAAAAA0AwLrQIAAADQDQEtDtAPAS0Q0AAJGgRFCgsEAwAAAAUAAAANAAAAKAAAANAMC60BAAAA0A0BLRHQDwEtENAACRoERQoLBAMAAAAFAAAADQAAAAAAAADQDAutAgAAANANAS0S0A8BLRDQAAkaBEUKCwQDAAAABQAAAA0AAAAUAAAA0AwLrQIAAADQDQEtE9APAS0Q0AAJGgRFCgsEAwAAAAUAAAANAAAAAAAAANAMC60CAAAA0A0BLRTQDwEtENAACRoERQoLBAMAAAAFAAAADQAAAAAAAADQDAutAgAAANANAS0V0A8BLRDQAAkaBEUKCwQDAAAABQAAAA0AAAAAAAAA0AwLrQIAAADQDQEtFtAPAS0X0AAJGgRFCgsEAwAAAAUAAAANAAAAKAAAANAMC60EAAAA0A0BLRjQDwEtENAZGq0BRRscAdAAHBoCRR0LBAMAAAAFAAAADQAAAAAAAADQHh8tINAhIhoCRSMkAtAAJRoFRQoLBAMAAAADAAAAAAAAAAoAAADQJgutAAAAANADAS0n0CgBLRHQKQEtENAAJRoFRQoLBAMAAAADAAAAAAAAAAoAAADQJgutAQAAANADAS0n0CgBLRHQKQEtEJoqKwFFAAEC0AABLSzQAAEtF9AAHy0t0C4vGgPQMAutAQAAANAxC60XAAAA0DIarQDQMy8aA9AwC60CAAAA0DELrQMAAADQMhqtANA0Gq0A0DUarQA= + + + {192FAD59-8248-4824-A8DE-9177C94C195A} + + "{192FAD59-8248-4824-A8DE-9177C94C195A}" + + + + + + + + + System.Collections.Hashtable + {54dd0eac-a6d8-46f2-8c27-2f43c7e49861} + System.String + + + + + \ No newline at end of file diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestTwincatProject1.tsproj b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestTwincatProject1.tsproj index 32d435c..abc5872 100644 --- a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestTwincatProject1.tsproj +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestTwincatProject1.tsproj @@ -1,6 +1,6 @@ - + @@ -9,9 +9,9 @@ - + - TestPlcProject Instance + TestPlcProject1 Instance {08500001-0000-0000-F000-000000000064} @@ -29,6 +29,18 @@ + + + TestPlcProject2 Instance + {08500001-0000-0000-F000-000000000064} + + + 1 + Default + + + + diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestTwincatProject2.tsproj b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestTwincatProject2.tsproj index 91d52a6..2e55acc 100644 --- a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestTwincatProject2.tsproj +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestTwincatProject2.tsproj @@ -1,4 +1,4 @@ - - + + diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TwinGet.libcat.xml b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TwinGet.libcat.xml new file mode 100644 index 0000000..fbd22d9 --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TwinGet.libcat.xml @@ -0,0 +1,8 @@ + + + + 736e1fb9-7997-4a5d-a19a-cfcf55bcd1a4 + 1.0.0.0 + TwinGet + + \ No newline at end of file diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProjectConstants.cs b/test/TwinGet.AutomationInterface.Test/TestTwincatProjectConstants.cs index 3274caa..f4a3515 100644 --- a/test/TwinGet.AutomationInterface.Test/TestTwincatProjectConstants.cs +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProjectConstants.cs @@ -5,5 +5,7 @@ namespace TwinGet.AutomationInterface.Test internal static class TestTwincatProjectConstants { internal static readonly string s_testTwincatProject = Path.Join(@$"{AppDomain.CurrentDomain.BaseDirectory}", @"TestTwincatProject"); + internal const string NonTwincatProjectNamePrefix = "TestNonTwincatProject"; + internal const string TwincatProjectNamePrefix = "TestTwincatProject"; } } diff --git a/test/TwinGet.AutomationInterface.Test/TwinGet.AutomationInterface.Test.csproj b/test/TwinGet.AutomationInterface.Test/TwinGet.AutomationInterface.Test.csproj index 307614a..d07a8f4 100644 --- a/test/TwinGet.AutomationInterface.Test/TwinGet.AutomationInterface.Test.csproj +++ b/test/TwinGet.AutomationInterface.Test/TwinGet.AutomationInterface.Test.csproj @@ -1,7 +1,7 @@ - net7.0-windows + net8.0-windows enable enable @@ -11,6 +11,7 @@ + diff --git a/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs b/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs index c931d8a..779781b 100644 --- a/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs +++ b/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs @@ -1,21 +1,19 @@ // This file is licensed to you under MIT license. using TwinGet.AutomationInterface.Exceptions; +using TwinGet.AutomationInterface.Utils; namespace TwinGet.AutomationInterface.Test { public class TwincatProjectTests { - private const string _nonTwincatProjectNamePrefix = "TestNonTwincatProject"; - private const string _twincatProjectNamePrefix = "TestTwincatProject"; - - private EnvDTE.Project? GetFirstTwincatProject(EnvDTE.Projects projects) + private static EnvDTE.Project? GetFirstTwincatProject(EnvDTE.Projects projects) { for (int i = 1; i <= projects.Count; i++) { EnvDTE.Project tmp = projects.Item(i); - if (tmp.Name.Contains(_twincatProjectNamePrefix)) + if (tmp.IsTwincatProject()) { return tmp; } @@ -24,13 +22,13 @@ public class TwincatProjectTests return null; } - private EnvDTE.Project? GetFirstNonTwincatProject(EnvDTE.Projects projects) + private static EnvDTE.Project? GetFirstNonTwincatProject(EnvDTE.Projects projects) { for (int i = 1; i <= projects.Count; i++) { EnvDTE.Project tmp = projects.Item(i); - if (tmp.Name.Contains(_nonTwincatProjectNamePrefix)) + if (!tmp.IsTwincatProject()) { return tmp; } @@ -47,10 +45,14 @@ public void Construct_WithTwincatProject_ShouldSucceed() using TestProject testProject = new(); using AutomationInterface ai = new(ref dte); ai.LoadSolution(testProject.SolutionPath); +#pragma warning disable CS8602 // Dereference of a possibly null reference. EnvDTE.Project? project = GetFirstTwincatProject(dte.Solution.Projects); +#pragma warning restore CS8602 // Dereference of a possibly null reference. // "Assert" no exception +#pragma warning disable CS8604 // Possible null reference argument. TwincatProject? tcProject = new(project); +#pragma warning restore CS8604 // Possible null reference argument. } [StaFact] @@ -61,10 +63,14 @@ public void Construct_WithNonTwincatProject_ShouldThrow() using TestProject testProject = new(); using AutomationInterface ai = new(ref dte); ai.LoadSolution(testProject.SolutionPath); +#pragma warning disable CS8602 // Dereference of a possibly null reference. EnvDTE.Project? project = GetFirstNonTwincatProject(dte.Solution.Projects); +#pragma warning restore CS8602 // Dereference of a possibly null reference. // Act +#pragma warning disable CS8604 // Possible null reference argument. Action constructTwincatProject = () => { TwincatProject? tcProject = new(project); }; +#pragma warning restore CS8604 // Possible null reference argument. // Assert constructTwincatProject.Should().Throw(); From 54c002b090efae8da05c4b47d16b3a52ca96dd2f Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Mon, 11 Dec 2023 21:12:05 +0200 Subject: [PATCH 13/27] test: Added tests for `ProjectFileDeserialization` --- src/TwinGet.AutomationInterface/PlcProject.cs | 6 +- .../PlcProject.cs | 2 +- .../TcSmProject.cs | 2 +- .../TwincatProject.cs | 2 +- .../PlcProjectTests.cs | 77 +++++++++++++ .../TcSmProjectTests.cs | 107 ++++++++++++++++++ .../TwincatProjectTests.cs | 6 + 7 files changed, 196 insertions(+), 6 deletions(-) rename src/TwinGet.AutomationInterface/{ProjectFilesDeserialization => ProjectFileDeserialization}/PlcProject.cs (97%) rename src/TwinGet.AutomationInterface/{ProjectFilesDeserialization => ProjectFileDeserialization}/TcSmProject.cs (98%) create mode 100644 test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/PlcProjectTests.cs create mode 100644 test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/TcSmProjectTests.cs diff --git a/src/TwinGet.AutomationInterface/PlcProject.cs b/src/TwinGet.AutomationInterface/PlcProject.cs index 8ba6a72..9f7b32c 100644 --- a/src/TwinGet.AutomationInterface/PlcProject.cs +++ b/src/TwinGet.AutomationInterface/PlcProject.cs @@ -9,7 +9,7 @@ namespace TwinGet.AutomationInterface public class PlcProject : ITcPlcIECProject3 { private readonly ITcPlcIECProject3 _plcProject; - private readonly ProjectFilesDeserialization.PlcProject _plcProjectFile; + private readonly ProjectFileDeserialization.PlcProject _plcProjectFile; public string Name { get => _plcProjectFile.PropertyGroup.Name; } public string Path { get; } @@ -39,11 +39,11 @@ protected PlcProject(string path) ArgumentException.ThrowIfNullOrEmpty(path, nameof(path)); string xmlContent = File.ReadAllText(path); - XmlSerializer serializer = new(typeof(ProjectFilesDeserialization.PlcProject)); + XmlSerializer serializer = new(typeof(ProjectFileDeserialization.PlcProject)); using (StringReader reader = new(xmlContent)) { - ProjectFilesDeserialization.PlcProject plcProjectFile = serializer.Deserialize(reader) as ProjectFilesDeserialization.PlcProject ?? throw new InvalidProjectFileFormat("Could not deserialize the provided PLC project file.", path); + ProjectFileDeserialization.PlcProject plcProjectFile = serializer.Deserialize(reader) as ProjectFileDeserialization.PlcProject ?? throw new InvalidProjectFileFormat("Could not deserialize the provided PLC project file.", path); _plcProjectFile = plcProjectFile; } diff --git a/src/TwinGet.AutomationInterface/ProjectFilesDeserialization/PlcProject.cs b/src/TwinGet.AutomationInterface/ProjectFileDeserialization/PlcProject.cs similarity index 97% rename from src/TwinGet.AutomationInterface/ProjectFilesDeserialization/PlcProject.cs rename to src/TwinGet.AutomationInterface/ProjectFileDeserialization/PlcProject.cs index 24ee041..0419457 100644 --- a/src/TwinGet.AutomationInterface/ProjectFilesDeserialization/PlcProject.cs +++ b/src/TwinGet.AutomationInterface/ProjectFileDeserialization/PlcProject.cs @@ -3,7 +3,7 @@ using System.Xml; using System.Xml.Serialization; -namespace TwinGet.AutomationInterface.ProjectFilesDeserialization +namespace TwinGet.AutomationInterface.ProjectFileDeserialization { [XmlRoot("Project", Namespace = "http://schemas.microsoft.com/developer/msbuild/2003")] public class PlcProject diff --git a/src/TwinGet.AutomationInterface/ProjectFilesDeserialization/TcSmProject.cs b/src/TwinGet.AutomationInterface/ProjectFileDeserialization/TcSmProject.cs similarity index 98% rename from src/TwinGet.AutomationInterface/ProjectFilesDeserialization/TcSmProject.cs rename to src/TwinGet.AutomationInterface/ProjectFileDeserialization/TcSmProject.cs index bd280ad..88d284f 100644 --- a/src/TwinGet.AutomationInterface/ProjectFilesDeserialization/TcSmProject.cs +++ b/src/TwinGet.AutomationInterface/ProjectFileDeserialization/TcSmProject.cs @@ -2,7 +2,7 @@ using System.Xml.Serialization; -namespace TwinGet.AutomationInterface.ProjectFilesDeserialization +namespace TwinGet.AutomationInterface.ProjectFileDeserialization { [XmlRoot("TcSmProject")] public class TcSmProject diff --git a/src/TwinGet.AutomationInterface/TwincatProject.cs b/src/TwinGet.AutomationInterface/TwincatProject.cs index a47d190..3d1e471 100644 --- a/src/TwinGet.AutomationInterface/TwincatProject.cs +++ b/src/TwinGet.AutomationInterface/TwincatProject.cs @@ -3,7 +3,7 @@ using System.Xml.Serialization; using EnvDTE; using TwinGet.AutomationInterface.Exceptions; -using TwinGet.AutomationInterface.ProjectFilesDeserialization; +using TwinGet.AutomationInterface.ProjectFileDeserialization; using TwinGet.AutomationInterface.Utils; using ITcSysManagerAlias = TCatSysManagerLib.ITcSysManager15; diff --git a/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/PlcProjectTests.cs b/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/PlcProjectTests.cs new file mode 100644 index 0000000..afadcb0 --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/PlcProjectTests.cs @@ -0,0 +1,77 @@ +// This file is licensed to you under MIT license. + +using System.Xml.Serialization; +using TwinGet.AutomationInterface.ProjectFileDeserialization; + +namespace TwinGet.AutomationInterface.Test.ProjectFileDeserialization +{ + public class PlcProjectTests + { + private class TestData + { + public static IEnumerable ValidXmlContents() + { + yield return new object[] + { + """ + + + + 1.0.0.0 + 2.0 + {f423d9f4-0ef7-4885-8ec0-9a403a78ec70} + True + true + true + false + TestPlcProject1 + 3.1.4024.0 + {da8cb013-01e2-4e76-aad8-c63cb197c3b9} + {65a0cb05-45a0-424f-9370-5321fe656f5f} + {d1e585ee-3f7a-4fb2-acd6-080b80c26dc3} + {e3297877-f97b-44dc-8428-54f3df37edf6} + {74d54b3b-a48f-4f65-8df7-855cc9cd89e2} + {bd174e45-2719-4cbf-b50e-b235af23264e} + TwinGet + false + TwinGet.TestTwincatProject1.TestPlcProject1 + 0.1.0 + + + {736e1fb9-7997-4a5d-a19a-cfcf55bcd1a4} + 1.0.0.0 + TwinGet + + + + {736e1fb9-7997-4a5d-a19a-cfcf55bcd1a4} + + + + """, + "TestPlcProject1", + "0.1.0" + }; + } + } + + [Theory] + [MemberData(nameof(TestData.ValidXmlContents), MemberType = typeof(TestData))] + public void Deserialize_FromValidXmlContent_ShouldSucceed(string xmlContent, string projectName, string projectVersion) + { + XmlSerializer serializer = new(typeof(TwinGet.AutomationInterface.ProjectFileDeserialization.PlcProject)); + + using (StringReader reader = new(xmlContent)) + { + TwinGet.AutomationInterface.ProjectFileDeserialization.PlcProject? project = serializer.Deserialize(reader) as TwinGet.AutomationInterface.ProjectFileDeserialization.PlcProject; + + project.Should().NotBeNull(); + +#pragma warning disable CS8602 // Dereference of a possibly null reference. + project.PropertyGroup.Name.Should().Be(projectName); +#pragma warning restore CS8602 // Dereference of a possibly null reference. + project.PropertyGroup.ProjectVersion.Should().Be(projectVersion); + } + } + } +} diff --git a/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/TcSmProjectTests.cs b/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/TcSmProjectTests.cs new file mode 100644 index 0000000..938ba46 --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/TcSmProjectTests.cs @@ -0,0 +1,107 @@ +// This file is licensed to you under MIT license. + +using System.Xml.Serialization; +using TwinGet.AutomationInterface.ProjectFileDeserialization; + +namespace TwinGet.AutomationInterface.Test.ProjectFileDeserialization +{ + public class TcSmProjectTests + { + private class TestData + { + public static IEnumerable ValidXmlContents() + { + yield return new object[] { + """ + + + + + + + PlcTask + + + + + + + TestPlcProject1 Instance + {08500001-0000-0000-F000-000000000064} + + + 0 + PlcTask + + #x02010030 + + 20 + 10000000 + + + + + + + + + + TestPlcProject2 Instance + {08500001-0000-0000-F000-000000000064} + + + 1 + Default + + + + + + + + """, + "3.1.4024.53", + new List(){ "TestPlcProject1", "TestPlcProject2" } }; + + yield return new object[] { + """ + + + + + """, + "3.1.4024.53", + new List() }; + } + } + + [Theory] + [MemberData(nameof(TestData.ValidXmlContents), MemberType = typeof(TestData))] + public void Deserialize_FromValidXmlContent_ShouldSucceed(string xmlContent, string tcVersion, List plcProjectNames) + { + XmlSerializer serializer = new(typeof(TcSmProject)); + + using (StringReader reader = new(xmlContent)) + { + TcSmProject? project = serializer.Deserialize(reader) as TcSmProject; + + project.Should().NotBeNull(); + +#pragma warning disable CS8602 // Dereference of a possibly null reference. + project.TcSmVersion.Should().NotBeNullOrEmpty(); +#pragma warning restore CS8602 // Dereference of a possibly null reference. + project.TcVersion.Should().Be(tcVersion); + if (plcProjectNames.Count > 0) + { + project.Project.Plc.Projects.Count.Should().Be(plcProjectNames.Count); + project.Project.Plc.Projects.Select(x => x.Name).Should().BeEquivalentTo(plcProjectNames); + } + else + { + project.Project.Plc.Should().BeNull(); + } + + } + } + } +} diff --git a/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs b/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs index 779781b..5248a68 100644 --- a/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs +++ b/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs @@ -75,5 +75,11 @@ public void Construct_WithNonTwincatProject_ShouldThrow() // Assert constructTwincatProject.Should().Throw(); } + + [StaFact] + public void PlcProjects_ShouldBeExpected() + { + + } } } From d5fbf7c184814132849ab95e33c8c5d41ae10fc9 Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Mon, 11 Dec 2023 22:30:14 +0200 Subject: [PATCH 14/27] refactor: Renamed files under `ProjectFileDeserialization` - To avoid two classes having the same name `PlcProject` - Added more tests to `TwincatProjectTests` - Rearranged and added new files to `TestUtils` folder --- .../AutomationInterfaceConstants.cs | 7 +++ src/TwinGet.AutomationInterface/PlcProject.cs | 6 +- .../{PlcProject.cs => PlcProjectData.cs} | 2 +- .../{TcSmProject.cs => TcSmProjectData.cs} | 2 +- .../TwincatProject.cs | 4 +- .../AutomationInterfaceTests.cs | 2 + .../PlcProjectTests.cs | 4 +- .../TcSmProjectTests.cs | 4 +- .../TestProject.cs | 46 --------------- .../TestUtils/TestPlcProject.cs | 29 ++++++++++ .../TestUtils/TestProject.cs | 56 +++++++++++++++++++ .../TestUtils/TestTwincatProject.cs | 39 +++++++++++++ .../TwincatProjectTests.cs | 30 +++++++++- .../xunit.runner.json | 3 +- test/TwinGet.Utils.Test/xunit.runner.json | 3 +- 15 files changed, 176 insertions(+), 61 deletions(-) rename src/TwinGet.AutomationInterface/ProjectFileDeserialization/{PlcProject.cs => PlcProjectData.cs} (98%) rename src/TwinGet.AutomationInterface/ProjectFileDeserialization/{TcSmProject.cs => TcSmProjectData.cs} (99%) delete mode 100644 test/TwinGet.AutomationInterface.Test/TestProject.cs create mode 100644 test/TwinGet.AutomationInterface.Test/TestUtils/TestPlcProject.cs create mode 100644 test/TwinGet.AutomationInterface.Test/TestUtils/TestProject.cs create mode 100644 test/TwinGet.AutomationInterface.Test/TestUtils/TestTwincatProject.cs diff --git a/src/TwinGet.AutomationInterface/AutomationInterfaceConstants.cs b/src/TwinGet.AutomationInterface/AutomationInterfaceConstants.cs index c5cb41f..7c39202 100644 --- a/src/TwinGet.AutomationInterface/AutomationInterfaceConstants.cs +++ b/src/TwinGet.AutomationInterface/AutomationInterfaceConstants.cs @@ -13,5 +13,12 @@ public static class AutomationInterfaceConstants public const string TwincatXaeProjectKind = "{B1E792BE-AA5F-4E3C-8C82-674BF9C0715B}"; public const string TwincatPlcProjectKind = TwincatXaeProjectKind; public const int ProjectItemStartingIndex = 1; + public const string TwincatXaeProjectExtension = ".tsproj"; + public const string TwincatPlcProjectExtension = ".tspproj"; + public static readonly IReadOnlyList TwincatProjectExtensions = new List + { + TwincatXaeProjectExtension, + TwincatPlcProjectExtension + }; } } diff --git a/src/TwinGet.AutomationInterface/PlcProject.cs b/src/TwinGet.AutomationInterface/PlcProject.cs index 9f7b32c..9c6316a 100644 --- a/src/TwinGet.AutomationInterface/PlcProject.cs +++ b/src/TwinGet.AutomationInterface/PlcProject.cs @@ -9,7 +9,7 @@ namespace TwinGet.AutomationInterface public class PlcProject : ITcPlcIECProject3 { private readonly ITcPlcIECProject3 _plcProject; - private readonly ProjectFileDeserialization.PlcProject _plcProjectFile; + private readonly ProjectFileDeserialization.PlcProjectData _plcProjectFile; public string Name { get => _plcProjectFile.PropertyGroup.Name; } public string Path { get; } @@ -39,11 +39,11 @@ protected PlcProject(string path) ArgumentException.ThrowIfNullOrEmpty(path, nameof(path)); string xmlContent = File.ReadAllText(path); - XmlSerializer serializer = new(typeof(ProjectFileDeserialization.PlcProject)); + XmlSerializer serializer = new(typeof(ProjectFileDeserialization.PlcProjectData)); using (StringReader reader = new(xmlContent)) { - ProjectFileDeserialization.PlcProject plcProjectFile = serializer.Deserialize(reader) as ProjectFileDeserialization.PlcProject ?? throw new InvalidProjectFileFormat("Could not deserialize the provided PLC project file.", path); + ProjectFileDeserialization.PlcProjectData plcProjectFile = serializer.Deserialize(reader) as ProjectFileDeserialization.PlcProjectData ?? throw new InvalidProjectFileFormat("Could not deserialize the provided PLC project file.", path); _plcProjectFile = plcProjectFile; } diff --git a/src/TwinGet.AutomationInterface/ProjectFileDeserialization/PlcProject.cs b/src/TwinGet.AutomationInterface/ProjectFileDeserialization/PlcProjectData.cs similarity index 98% rename from src/TwinGet.AutomationInterface/ProjectFileDeserialization/PlcProject.cs rename to src/TwinGet.AutomationInterface/ProjectFileDeserialization/PlcProjectData.cs index 0419457..1019ec1 100644 --- a/src/TwinGet.AutomationInterface/ProjectFileDeserialization/PlcProject.cs +++ b/src/TwinGet.AutomationInterface/ProjectFileDeserialization/PlcProjectData.cs @@ -6,7 +6,7 @@ namespace TwinGet.AutomationInterface.ProjectFileDeserialization { [XmlRoot("Project", Namespace = "http://schemas.microsoft.com/developer/msbuild/2003")] - public class PlcProject + public class PlcProjectData { [XmlAttribute("DefaultTargets")] public string DefaultTargets { get; set; } diff --git a/src/TwinGet.AutomationInterface/ProjectFileDeserialization/TcSmProject.cs b/src/TwinGet.AutomationInterface/ProjectFileDeserialization/TcSmProjectData.cs similarity index 99% rename from src/TwinGet.AutomationInterface/ProjectFileDeserialization/TcSmProject.cs rename to src/TwinGet.AutomationInterface/ProjectFileDeserialization/TcSmProjectData.cs index 88d284f..35cbea0 100644 --- a/src/TwinGet.AutomationInterface/ProjectFileDeserialization/TcSmProject.cs +++ b/src/TwinGet.AutomationInterface/ProjectFileDeserialization/TcSmProjectData.cs @@ -5,7 +5,7 @@ namespace TwinGet.AutomationInterface.ProjectFileDeserialization { [XmlRoot("TcSmProject")] - public class TcSmProject + public class TcSmProjectData { [XmlAttribute("TcSmVersion")] public string TcSmVersion { get; set; } diff --git a/src/TwinGet.AutomationInterface/TwincatProject.cs b/src/TwinGet.AutomationInterface/TwincatProject.cs index 3d1e471..d162d80 100644 --- a/src/TwinGet.AutomationInterface/TwincatProject.cs +++ b/src/TwinGet.AutomationInterface/TwincatProject.cs @@ -41,11 +41,11 @@ private static IReadOnlyList TryGetPlcProjects(ITcSysManagerAlias sy ArgumentException.ThrowIfNullOrEmpty(projectPath, nameof(projectPath)); string xmlContent = File.ReadAllText(projectPath); - XmlSerializer serializer = new(typeof(TcSmProject)); + XmlSerializer serializer = new(typeof(TcSmProjectData)); using (StringReader reader = new(xmlContent)) { - TcSmProject tcSmProject = serializer.Deserialize(reader) as TcSmProject ?? throw new InvalidProjectFileFormat("The format of TwinCAT project file is invalid.", projectPath); + TcSmProjectData tcSmProject = serializer.Deserialize(reader) as TcSmProjectData ?? throw new InvalidProjectFileFormat("The format of TwinCAT project file is invalid.", projectPath); string? rootDir = Path.GetDirectoryName(projectPath); if (tcSmProject.Project.Plc?.Projects is not null) diff --git a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs index 4969f64..a981867 100644 --- a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs +++ b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs @@ -1,5 +1,7 @@ // This file is licensed to you under MIT license. +using TwinGet.AutomationInterface.Test.TestUtils; + namespace TwinGet.AutomationInterface.Test { public class AutomationInterfaceTests : IDisposable diff --git a/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/PlcProjectTests.cs b/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/PlcProjectTests.cs index afadcb0..7bc30b9 100644 --- a/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/PlcProjectTests.cs +++ b/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/PlcProjectTests.cs @@ -59,11 +59,11 @@ public static IEnumerable ValidXmlContents() [MemberData(nameof(TestData.ValidXmlContents), MemberType = typeof(TestData))] public void Deserialize_FromValidXmlContent_ShouldSucceed(string xmlContent, string projectName, string projectVersion) { - XmlSerializer serializer = new(typeof(TwinGet.AutomationInterface.ProjectFileDeserialization.PlcProject)); + XmlSerializer serializer = new(typeof(PlcProjectData)); using (StringReader reader = new(xmlContent)) { - TwinGet.AutomationInterface.ProjectFileDeserialization.PlcProject? project = serializer.Deserialize(reader) as TwinGet.AutomationInterface.ProjectFileDeserialization.PlcProject; + PlcProjectData? project = serializer.Deserialize(reader) as PlcProjectData; project.Should().NotBeNull(); diff --git a/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/TcSmProjectTests.cs b/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/TcSmProjectTests.cs index 938ba46..4c87f82 100644 --- a/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/TcSmProjectTests.cs +++ b/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/TcSmProjectTests.cs @@ -79,11 +79,11 @@ public static IEnumerable ValidXmlContents() [MemberData(nameof(TestData.ValidXmlContents), MemberType = typeof(TestData))] public void Deserialize_FromValidXmlContent_ShouldSucceed(string xmlContent, string tcVersion, List plcProjectNames) { - XmlSerializer serializer = new(typeof(TcSmProject)); + XmlSerializer serializer = new(typeof(TcSmProjectData)); using (StringReader reader = new(xmlContent)) { - TcSmProject? project = serializer.Deserialize(reader) as TcSmProject; + TcSmProjectData? project = serializer.Deserialize(reader) as TcSmProjectData; project.Should().NotBeNull(); diff --git a/test/TwinGet.AutomationInterface.Test/TestProject.cs b/test/TwinGet.AutomationInterface.Test/TestProject.cs deleted file mode 100644 index 8687883..0000000 --- a/test/TwinGet.AutomationInterface.Test/TestProject.cs +++ /dev/null @@ -1,46 +0,0 @@ -// This file is licensed to you under MIT license. - -using Microsoft.Build.Construction; - -namespace TwinGet.AutomationInterface.Test -{ - internal class TestProject : IDisposable - { - private bool _disposedValue; - public string Path { get; private set; } - public string SolutionPath { get; } - public SolutionFile SolutionFile { get; } - - public TestProject() - { - Path = System.IO.Path.Join(System.IO.Path.GetTempPath(), Guid.NewGuid().ToString()); - Directory.CreateDirectory(Path); - - TwinGet.Utils.IO.Directory.CopyDirectory( - TestTwincatProjectConstants.s_testTwincatProject, - Path, FileShare.Read).Wait(); - - SolutionPath = Directory.GetFiles(Path, "*.sln", SearchOption.AllDirectories).First(); - - SolutionFile = SolutionFile.Parse(SolutionPath); - } - - protected virtual void Dispose(bool disposing) - { - if (!_disposedValue) - { - if (disposing) { } - - if (Directory.Exists(Path)) { Directory.Delete(Path, true); } - _disposedValue = true; - } - } - - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - } -} diff --git a/test/TwinGet.AutomationInterface.Test/TestUtils/TestPlcProject.cs b/test/TwinGet.AutomationInterface.Test/TestUtils/TestPlcProject.cs new file mode 100644 index 0000000..c05b5e1 --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/TestUtils/TestPlcProject.cs @@ -0,0 +1,29 @@ +// This file is licensed to you under MIT license. + +using System.Xml.Serialization; +using TwinGet.AutomationInterface.Exceptions; +using TwinGet.AutomationInterface.ProjectFileDeserialization; + +namespace TwinGet.AutomationInterface.Test.TestUtils +{ + internal class TestPlcProject + { + public string Name { get; } + public string AbsolutePath { get; } + + public TestPlcProject(string path) + { + AbsolutePath = Path.GetFullPath(path); + + string xmlContent = File.ReadAllText(AbsolutePath); + XmlSerializer serializer = new(typeof(PlcProjectData)); + + using (StringReader reader = new(xmlContent)) + { + var plcProjectData = serializer.Deserialize(reader) as PlcProjectData; + + Name = plcProjectData?.PropertyGroup.Name ?? throw new InvalidProjectFileFormat("Failed to get the PLC project name.", path); + } + } + } +} diff --git a/test/TwinGet.AutomationInterface.Test/TestUtils/TestProject.cs b/test/TwinGet.AutomationInterface.Test/TestUtils/TestProject.cs new file mode 100644 index 0000000..8881603 --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/TestUtils/TestProject.cs @@ -0,0 +1,56 @@ +// This file is licensed to you under MIT license. + +using System.IO; +using Microsoft.Build.Construction; +using static TwinGet.AutomationInterface.AutomationInterfaceConstants; + +namespace TwinGet.AutomationInterface.Test.TestUtils +{ + internal class TestProject : IDisposable + { + private bool _disposedValue; + private readonly List _twincatProjects = []; + public string RootPath { get; private set; } + public string SolutionPath { get; } + public SolutionFile SolutionFile { get; } + public IReadOnlyList TwincatProjects { get => _twincatProjects; } + + public TestProject() + { + RootPath = System.IO.Path.Join(System.IO.Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(RootPath); + + TwinGet.Utils.IO.Directory.CopyDirectory( + TestTwincatProjectConstants.s_testTwincatProject, + RootPath, FileShare.Read).Wait(); + + SolutionPath = Directory.GetFiles(RootPath, "*.sln", SearchOption.AllDirectories).First(); + + SolutionFile = SolutionFile.Parse(SolutionPath); + + static bool isTwincatProject(string absolutePath) => TwincatProjectExtensions.Contains(System.IO.Path.GetExtension(absolutePath)); + + _twincatProjects = new(SolutionFile.ProjectsInOrder + .Where(x => isTwincatProject(x.AbsolutePath)) + .Select(x => new TestTwincatProject(x.ProjectName, x.AbsolutePath))); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) { } + + if (Directory.Exists(RootPath)) { Directory.Delete(RootPath, true); } + _disposedValue = true; + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/test/TwinGet.AutomationInterface.Test/TestUtils/TestTwincatProject.cs b/test/TwinGet.AutomationInterface.Test/TestUtils/TestTwincatProject.cs new file mode 100644 index 0000000..7549844 --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/TestUtils/TestTwincatProject.cs @@ -0,0 +1,39 @@ +// This file is licensed to you under MIT license. + +using System.Xml.Serialization; +using TwinGet.AutomationInterface.Exceptions; +using TwinGet.AutomationInterface.ProjectFileDeserialization; + +namespace TwinGet.AutomationInterface.Test.TestUtils +{ + internal class TestTwincatProject + { + private List _plcProjects = []; + public string Name { get; } + public string AbsolutePath { get; } + public IReadOnlyList PlcProjects { get => _plcProjects; } + + public TestTwincatProject(string name, string absolutePath) + { + Name = name; + AbsolutePath = absolutePath; + + string xmlContent = File.ReadAllText(absolutePath); + XmlSerializer serializer = new(typeof(TcSmProjectData)); + string? rootDir = Path.GetDirectoryName(absolutePath); + + using (StringReader reader = new(xmlContent)) + { + TcSmProjectData tcSmProject = serializer.Deserialize(reader) as TcSmProjectData ?? throw new InvalidProjectFileFormat("The format of TwinCAT project file is invalid.", absolutePath); + + if (tcSmProject.Project.Plc?.Projects is not null) + { + _plcProjects.AddRange(from ProjectElement project in tcSmProject.Project.Plc.Projects + let plcProject = new TestPlcProject(Path.Join(rootDir, project.PrjFilePath)) + where plcProject is not null + select plcProject); + } + } + } + } +} diff --git a/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs b/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs index 5248a68..544bb37 100644 --- a/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs +++ b/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs @@ -1,7 +1,9 @@ // This file is licensed to you under MIT license. using TwinGet.AutomationInterface.Exceptions; +using TwinGet.AutomationInterface.Test.TestUtils; using TwinGet.AutomationInterface.Utils; +using static TwinGet.AutomationInterface.AutomationInterfaceConstants; namespace TwinGet.AutomationInterface.Test { @@ -9,7 +11,7 @@ public class TwincatProjectTests { private static EnvDTE.Project? GetFirstTwincatProject(EnvDTE.Projects projects) { - for (int i = 1; i <= projects.Count; i++) + for (int i = ProjectItemStartingIndex; i <= projects.Count; i++) { EnvDTE.Project tmp = projects.Item(i); @@ -24,7 +26,7 @@ public class TwincatProjectTests private static EnvDTE.Project? GetFirstNonTwincatProject(EnvDTE.Projects projects) { - for (int i = 1; i <= projects.Count; i++) + for (int i = ProjectItemStartingIndex; i <= projects.Count; i++) { EnvDTE.Project tmp = projects.Item(i); @@ -79,7 +81,31 @@ public void Construct_WithNonTwincatProject_ShouldThrow() [StaFact] public void PlcProjects_ShouldBeExpected() { + // Arrange + EnvDTE80.DTE2? dte = null; + using TestProject testProject = new(); + using AutomationInterface ai = new(ref dte); + ai.LoadSolution(testProject.SolutionPath); + // Act, construct TwincatProjects + List twincatProjects = []; + for (int i = ProjectItemStartingIndex; i <= dte.Solution.Projects.Count; i++) + { + EnvDTE.Project currentProject = dte.Solution.Projects.Item(i); + if (currentProject.IsTwincatProject()) + twincatProjects.Add(new TwincatProject(currentProject)); + } + + // Assert + foreach (TwincatProject twincatProject in twincatProjects) + { + TestTwincatProject? testTwincatProject = testProject.TwincatProjects.Where(x => x.Name == twincatProject.Name).FirstOrDefault(); + + IEnumerable expectedPlcProjectNames = testTwincatProject.PlcProjects.Select(x => x.Name); + IEnumerable actualPlcProjectNames = twincatProject.PlcProjects.Select(x => x.Name); + + expectedPlcProjectNames.Should().BeEquivalentTo(actualPlcProjectNames); + } } } } diff --git a/test/TwinGet.AutomationInterface.Test/xunit.runner.json b/test/TwinGet.AutomationInterface.Test/xunit.runner.json index 31fb263..083cab6 100644 --- a/test/TwinGet.AutomationInterface.Test/xunit.runner.json +++ b/test/TwinGet.AutomationInterface.Test/xunit.runner.json @@ -1,5 +1,6 @@ { "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", "parallelizeAssembly": true, - "parallelizeTestCollections": true + "parallelizeTestCollections": true, + "maxParallelThreads": -1 } diff --git a/test/TwinGet.Utils.Test/xunit.runner.json b/test/TwinGet.Utils.Test/xunit.runner.json index 31fb263..083cab6 100644 --- a/test/TwinGet.Utils.Test/xunit.runner.json +++ b/test/TwinGet.Utils.Test/xunit.runner.json @@ -1,5 +1,6 @@ { "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", "parallelizeAssembly": true, - "parallelizeTestCollections": true + "parallelizeTestCollections": true, + "maxParallelThreads": -1 } From aad601782c1b04d2671950c91241cc698f6c658e Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Mon, 11 Dec 2023 23:23:10 +0200 Subject: [PATCH 15/27] test: Renamed some tests to match the name changes in source code --- .../{PlcProjectTests.cs => PlcProjectDataTests.cs} | 2 +- .../{TcSmProjectTests.cs => TcSmProjectDataTests.cs} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/{PlcProjectTests.cs => PlcProjectDataTests.cs} (99%) rename test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/{TcSmProjectTests.cs => TcSmProjectDataTests.cs} (99%) diff --git a/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/PlcProjectTests.cs b/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/PlcProjectDataTests.cs similarity index 99% rename from test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/PlcProjectTests.cs rename to test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/PlcProjectDataTests.cs index 7bc30b9..9c8bc7b 100644 --- a/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/PlcProjectTests.cs +++ b/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/PlcProjectDataTests.cs @@ -5,7 +5,7 @@ namespace TwinGet.AutomationInterface.Test.ProjectFileDeserialization { - public class PlcProjectTests + public class PlcProjectDataTests { private class TestData { diff --git a/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/TcSmProjectTests.cs b/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/TcSmProjectDataTests.cs similarity index 99% rename from test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/TcSmProjectTests.cs rename to test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/TcSmProjectDataTests.cs index 4c87f82..6c6820c 100644 --- a/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/TcSmProjectTests.cs +++ b/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/TcSmProjectDataTests.cs @@ -5,7 +5,7 @@ namespace TwinGet.AutomationInterface.Test.ProjectFileDeserialization { - public class TcSmProjectTests + public class TcSmProjectDataTests { private class TestData { From 6a62d425c8afdedb4cffd9cf7a2a41d5ea635e4f Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Mon, 11 Dec 2023 23:46:19 +0200 Subject: [PATCH 16/27] refactor: Extracted class `TwincatDteProvider` This will make tests more lightweight as we don't need to use the whole `AutomationInterface` class everytime we setup. --- .../AutomationInterface.cs | 100 +++--------------- .../AutomationInterfaceConstants.cs | 4 +- .../TwincatDteProvider.cs | 98 +++++++++++++++++ .../AutomationInterfaceConstantsTests.cs | 6 -- .../AutomationInterfaceTests.cs | 5 +- .../TestUtils/TestProject.cs | 1 - .../TwincatProjectTests.cs | 50 ++++++--- 7 files changed, 152 insertions(+), 112 deletions(-) create mode 100644 src/TwinGet.AutomationInterface/TwincatDteProvider.cs diff --git a/src/TwinGet.AutomationInterface/AutomationInterface.cs b/src/TwinGet.AutomationInterface/AutomationInterface.cs index ae6f797..73fa944 100644 --- a/src/TwinGet.AutomationInterface/AutomationInterface.cs +++ b/src/TwinGet.AutomationInterface/AutomationInterface.cs @@ -1,8 +1,6 @@ // This file is licensed to you under MIT license. -using System.Runtime.InteropServices; using System.Runtime.Versioning; -using EnvDTE80; using TwinGet.AutomationInterface.ComMessageFilter; using TwinGet.AutomationInterface.Exceptions; using TwinGet.AutomationInterface.Utils; @@ -13,14 +11,14 @@ namespace TwinGet.AutomationInterface [SupportedOSPlatform("windows")] public class AutomationInterface : IDisposable { - private string _progId; + private bool _disposedValue; + private readonly TwincatDteProvider _dteProvider; + private EnvDTE80.DTE2 _dte { get => _dteProvider.Dte; } private EnvDTE.Solution? _solution; private EnvDTE.SolutionBuild? _solutionBuild; - private readonly EnvDTE80.DTE2? _dte; - private bool _disposedValue; private readonly List _twincatProjects = new(); - public string ProgId { get => _progId; } + public string ProgId { get => _dteProvider.ProgId; } public bool IsSolutionOpen { get => _solution?.IsOpen ?? false; } public string LoadedSolutionFile { get => _solution?.FileName ?? string.Empty; } public IReadOnlyList TwincatProjects { get => _twincatProjects; } @@ -28,16 +26,7 @@ public class AutomationInterface : IDisposable public AutomationInterface() { MessageFilter.Register(); - _dte = TryInitializeDte(out _progId); - ThrowIfFailToInitializeDte(_dte); - } - - internal AutomationInterface(ref EnvDTE80.DTE2? dte) - { - MessageFilter.Register(); - _dte = TryInitializeDte(out _progId); - dte = _dte; - if (_dte is null) { throw new CouldNotCreateTwincatDteException("Is TwinCAT installed in this system?"); } + _dteProvider = new(this, true); } protected virtual void Dispose(bool disposing) @@ -46,7 +35,11 @@ protected virtual void Dispose(bool disposing) { if (disposing) { } - CleanUp(); + /// We only dispose the that we created. + if (_dteProvider.Owner == this) + { + _dteProvider.Dispose(); + } _disposedValue = true; } } @@ -62,88 +55,25 @@ public void Dispose() GC.SuppressFinalize(this); } - private void CleanUp() - { - _progId = string.Empty; - - if (_dte is not null) - { - _dte.Quit(); - Marshal.ReleaseComObject(_dte); - MessageFilter.Revoke(); - } - } - - private void ThrowIfFailToInitializeDte(EnvDTE80.DTE2? dte) - { - if (dte is null) - { - throw new CouldNotCreateTwincatDteException($"Failed to create a DTE instance due to missing TwinCAT XAE or TwinCAT-intergrated Visual Studio installation. TwinCAT can be downloaded from: {AutomationInterfaceConstants.TwincatXaeDownloadUrl}"); - } - } - - /// - /// Try to create a Visual Studio (or TwinCAT XAE) DTE instance. - /// - /// The ProgId of the created instance if successful, empty string if not. - /// The created DTE instance if successful, null if not. - private static EnvDTE80.DTE2? TryInitializeDte(out string progId) - { - foreach (string p in AutomationInterfaceConstants.SupportedProgIds) - { - Type? t = Type.GetTypeFromProgID(p); - - if (t is null) { continue; } - - EnvDTE80.DTE2? dte; - try - { - dte = (EnvDTE80.DTE2?)Activator.CreateInstance(t); - if (dte is null) { continue; } - } - catch { continue; } - - if (dte.IsTwinCatIntegrated()) - { - progId = p; - return dte; - } - } - - progId = string.Empty; - return null; - } - private void ThrowIfDteIsNull() { - if (_dte is null) { throw new DteInstanceIsNullException($"No {nameof(DTE2)} instance available."); } - } - - private static void ThrowNullOrEmptySolutionPath() - { - throw new ArgumentException("Solution path cannot be null or empty."); + if (_dte is null) { throw new DteInstanceIsNullException($"No {nameof(EnvDTE80.DTE2)} instance available."); } } private static void ThrowSolutionPathNotFound(string solutionPath) { - throw new FileNotFoundException($"Provided solution path \"{solutionPath}\" does not exists."); - } - - private static void ThrowIfInvalidSolutionPath(string solutionPath) - { - if (string.IsNullOrEmpty(solutionPath)) { ThrowNullOrEmptySolutionPath(); } - if (!Path.Exists(solutionPath)) { ThrowSolutionPathNotFound(solutionPath); } + throw new FileNotFoundException($"Provided solution path does not exists.", solutionPath); } public void LoadSolution(string filePath) { - ThrowIfInvalidSolutionPath(filePath); + ArgumentException.ThrowIfNullOrEmpty(filePath, nameof(filePath)); + if (!Path.Exists(filePath)) { ThrowSolutionPathNotFound(filePath); } + ThrowIfDteIsNull(); filePath = Path.GetFullPath(filePath); -#pragma warning disable CS8602 // Dereference of a possibly null reference. _solution = _dte.Solution; -#pragma warning restore CS8602 // Dereference of a possibly null reference. _solutionBuild = _dte.Solution.SolutionBuild; _solution.Open(filePath); diff --git a/src/TwinGet.AutomationInterface/AutomationInterfaceConstants.cs b/src/TwinGet.AutomationInterface/AutomationInterfaceConstants.cs index e573938..7617b44 100644 --- a/src/TwinGet.AutomationInterface/AutomationInterfaceConstants.cs +++ b/src/TwinGet.AutomationInterface/AutomationInterfaceConstants.cs @@ -10,8 +10,8 @@ public static class AutomationInterfaceConstants "VisualStudio.DTE.14.0", "VisualStudio.DTE.15.0" }; - - public static string TwincatXaeDownloadUrl = @"https://www.beckhoff.com/en-en/support/download-finder/search-result/?search=eXtended%20Automation%20Engineering%20%28XAE%29"; + + public static string TwincatXaeDownloadUrl = @"https://www.beckhoff.com/en-en/support/download-finder/search-result/?search=eXtended%20Automation%20Engineering%20%28XAE%29"; public const string TwincatXaeProjectKind = "{B1E792BE-AA5F-4E3C-8C82-674BF9C0715B}"; public const string TwincatPlcProjectKind = TwincatXaeProjectKind; public const int ProjectItemStartingIndex = 1; diff --git a/src/TwinGet.AutomationInterface/TwincatDteProvider.cs b/src/TwinGet.AutomationInterface/TwincatDteProvider.cs new file mode 100644 index 0000000..dfa8e75 --- /dev/null +++ b/src/TwinGet.AutomationInterface/TwincatDteProvider.cs @@ -0,0 +1,98 @@ +// This file is licensed to you under MIT license. + +using System.Runtime.InteropServices; +using TwinGet.AutomationInterface.ComMessageFilter; +using TwinGet.AutomationInterface.Exceptions; +using TwinGet.AutomationInterface.Utils; + +namespace TwinGet.AutomationInterface +{ + public class TwincatDteProvider : IDisposable + { + private bool _disposedValue; + private readonly bool _startedMsgFiltering = false; + + public object Owner { get; } + public string ProgId { get; } + public EnvDTE80.DTE2 Dte { get; } + + /// + /// Construct the wrapper class for a TwinCAT DTE instance. + /// + /// The object that created this object. + /// Start message filtering if this is true. Default value is true. + /// Throw this exception when it fails to create a TwinCAT DTE instance. + public TwincatDteProvider(object owner, bool startMsgFiltering = true) + { + ArgumentNullException.ThrowIfNull(owner, nameof(owner)); + Owner = owner; + + foreach (string p in AutomationInterfaceConstants.SupportedProgIds) + { + Type? t = Type.GetTypeFromProgID(p); + + if (t is null) { continue; } + + EnvDTE80.DTE2? dte; + try + { + dte = (EnvDTE80.DTE2?)Activator.CreateInstance(t); + if (dte is null) { continue; } + } + catch { continue; } + + if (dte.IsTwinCatIntegrated()) + { + ProgId = p; + Dte = dte; + break; + } + } + + if (Dte is null || string.IsNullOrEmpty(ProgId)) + { + throw new CouldNotCreateTwincatDteException($"Failed to create a DTE instance due to missing TwinCAT XAE or TwinCAT-intergrated Visual Studio installation. TwinCAT can be downloaded from: {AutomationInterfaceConstants.TwincatXaeDownloadUrl}"); + } + + if (startMsgFiltering) + { + _startedMsgFiltering = true; + MessageFilter.Register(); + } + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) { } + + if (Dte is not null) + { + Dte.Quit(); + Marshal.ReleaseComObject(Dte); + + if (_startedMsgFiltering) + { + MessageFilter.Revoke(); + } + } + + _disposedValue = true; + } + } + + ~TwincatDteProvider() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: false); + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} diff --git a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceConstantsTests.cs b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceConstantsTests.cs index f66effd..a94c6e7 100644 --- a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceConstantsTests.cs +++ b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceConstantsTests.cs @@ -1,11 +1,5 @@ // This file is licensed to you under MIT license. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace TwinGet.AutomationInterface.Test { public class AutomationInterfaceConstantsTests diff --git a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs index a981867..c2a8eb9 100644 --- a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs +++ b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs @@ -69,10 +69,7 @@ protected virtual void Dispose(bool disposing) { if (!_disposedValue) { - if (disposing) - { - ; - } + if (disposing) { } GC.Collect(); GC.WaitForPendingFinalizers(); diff --git a/test/TwinGet.AutomationInterface.Test/TestUtils/TestProject.cs b/test/TwinGet.AutomationInterface.Test/TestUtils/TestProject.cs index 8881603..1e99e95 100644 --- a/test/TwinGet.AutomationInterface.Test/TestUtils/TestProject.cs +++ b/test/TwinGet.AutomationInterface.Test/TestUtils/TestProject.cs @@ -1,6 +1,5 @@ // This file is licensed to you under MIT license. -using System.IO; using Microsoft.Build.Construction; using static TwinGet.AutomationInterface.AutomationInterfaceConstants; diff --git a/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs b/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs index 544bb37..241c506 100644 --- a/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs +++ b/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs @@ -7,8 +7,10 @@ namespace TwinGet.AutomationInterface.Test { - public class TwincatProjectTests + public class TwincatProjectTests : IDisposable { + private bool _disposedValue; + private static EnvDTE.Project? GetFirstTwincatProject(EnvDTE.Projects projects) { for (int i = ProjectItemStartingIndex; i <= projects.Count; i++) @@ -43,12 +45,11 @@ public class TwincatProjectTests public void Construct_WithTwincatProject_ShouldSucceed() { // Arrange - EnvDTE80.DTE2? dte = null; using TestProject testProject = new(); - using AutomationInterface ai = new(ref dte); - ai.LoadSolution(testProject.SolutionPath); + using TwincatDteProvider dteProvider = new(this); + dteProvider.Dte.Solution.Open(testProject.SolutionPath); #pragma warning disable CS8602 // Dereference of a possibly null reference. - EnvDTE.Project? project = GetFirstTwincatProject(dte.Solution.Projects); + EnvDTE.Project? project = GetFirstTwincatProject(dteProvider.Dte.Solution.Projects); #pragma warning restore CS8602 // Dereference of a possibly null reference. // "Assert" no exception @@ -61,12 +62,11 @@ public void Construct_WithTwincatProject_ShouldSucceed() public void Construct_WithNonTwincatProject_ShouldThrow() { // Arrange - EnvDTE80.DTE2? dte = null; using TestProject testProject = new(); - using AutomationInterface ai = new(ref dte); - ai.LoadSolution(testProject.SolutionPath); + using TwincatDteProvider dteProvider = new(this); + dteProvider.Dte.Solution.Open(testProject.SolutionPath); #pragma warning disable CS8602 // Dereference of a possibly null reference. - EnvDTE.Project? project = GetFirstNonTwincatProject(dte.Solution.Projects); + EnvDTE.Project? project = GetFirstNonTwincatProject(dteProvider.Dte.Solution.Projects); #pragma warning restore CS8602 // Dereference of a possibly null reference. // Act @@ -82,16 +82,15 @@ public void Construct_WithNonTwincatProject_ShouldThrow() public void PlcProjects_ShouldBeExpected() { // Arrange - EnvDTE80.DTE2? dte = null; using TestProject testProject = new(); - using AutomationInterface ai = new(ref dte); - ai.LoadSolution(testProject.SolutionPath); + using TwincatDteProvider dteProvider = new(this); + dteProvider.Dte.Solution.Open(testProject.SolutionPath); // Act, construct TwincatProjects List twincatProjects = []; - for (int i = ProjectItemStartingIndex; i <= dte.Solution.Projects.Count; i++) + for (int i = ProjectItemStartingIndex; i <= dteProvider.Dte.Solution.Projects.Count; i++) { - EnvDTE.Project currentProject = dte.Solution.Projects.Item(i); + EnvDTE.Project currentProject = dteProvider.Dte.Solution.Projects.Item(i); if (currentProject.IsTwincatProject()) twincatProjects.Add(new TwincatProject(currentProject)); } @@ -107,5 +106,28 @@ public void PlcProjects_ShouldBeExpected() expectedPlcProjectNames.Should().BeEquivalentTo(actualPlcProjectNames); } } + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) { } + + GC.Collect(); + GC.WaitForPendingFinalizers(); + _disposedValue = true; + } + } + + ~TwincatProjectTests() + { + Dispose(disposing: false); + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } } } From 5517ff73489ead77485e0305557d2055d648e2c2 Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Tue, 12 Dec 2023 01:00:45 +0200 Subject: [PATCH 17/27] test: Rearranged order of using for `TestProject` Added usage guide regarding `TestProject` class --- .../AutomationInterfaceTests.cs | 7 +++++-- .../TcSmProjectDataTests.cs | 2 +- test/TwinGet.AutomationInterface.Test/README.md | 5 +++++ .../TestTwincatProject1/TestTwincatProject1.tsproj | 2 +- .../TestUtils/TestProject.cs | 14 +++++++++++--- .../TestUtils/TestTwincatProject.cs | 2 +- .../TwincatProjectTests.cs | 6 +----- 7 files changed, 25 insertions(+), 13 deletions(-) create mode 100644 test/TwinGet.AutomationInterface.Test/README.md diff --git a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs index c2a8eb9..ea3d7ba 100644 --- a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs +++ b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs @@ -1,12 +1,15 @@ // This file is licensed to you under MIT license. using TwinGet.AutomationInterface.Test.TestUtils; +using Xunit.Abstractions; namespace TwinGet.AutomationInterface.Test { - public class AutomationInterfaceTests : IDisposable + public class AutomationInterfaceTests(ITestOutputHelper output) : IDisposable { + private readonly ITestOutputHelper _output = output; private bool _disposedValue; + [Fact] public void PathExists_ShouldSupportRelativePath() { @@ -33,8 +36,8 @@ public void ProgId_ShouldBeValid() [StaFact] public void LoadSolution_WithValidPath_ShouldLoadSuccessfully() { - using AutomationInterface sut = new(); using TestProject project = new(); + using AutomationInterface sut = new(); sut.LoadSolution(project.SolutionPath); diff --git a/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/TcSmProjectDataTests.cs b/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/TcSmProjectDataTests.cs index 6c6820c..1ce75bb 100644 --- a/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/TcSmProjectDataTests.cs +++ b/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/TcSmProjectDataTests.cs @@ -25,7 +25,7 @@ public static IEnumerable ValidXmlContents() - + TestPlcProject1 Instance {08500001-0000-0000-F000-000000000064} diff --git a/test/TwinGet.AutomationInterface.Test/README.md b/test/TwinGet.AutomationInterface.Test/README.md new file mode 100644 index 0000000..15a0b03 --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/README.md @@ -0,0 +1,5 @@ +# TwinGet.AutomationInterface.Test + +When using [`TestProject`](\TestUtils\TestProject.cs), always make sure to use it before, for example, `AutomationInterface`, so that `AutomationInterface` dispose before `TestProject` does. This way, the solution is closed and `TestProject` can remove all the temporary files and folders. + +This also apply to any other instances that access resources created by `TestProject`. They all need to stop prior to `TestProject` disposal, otherwise it won't be able to fully clean up temporary resources it created. diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestTwincatProject1.tsproj b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestTwincatProject1.tsproj index abc5872..aa6293e 100644 --- a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestTwincatProject1.tsproj +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject1/TestTwincatProject1.tsproj @@ -10,7 +10,7 @@ - + TestPlcProject1 Instance {08500001-0000-0000-F000-000000000064} diff --git a/test/TwinGet.AutomationInterface.Test/TestUtils/TestProject.cs b/test/TwinGet.AutomationInterface.Test/TestUtils/TestProject.cs index 1e99e95..26a3d88 100644 --- a/test/TwinGet.AutomationInterface.Test/TestUtils/TestProject.cs +++ b/test/TwinGet.AutomationInterface.Test/TestUtils/TestProject.cs @@ -5,6 +5,10 @@ namespace TwinGet.AutomationInterface.Test.TestUtils { + /// + /// A class that setup a test project in a temporary path. + /// Any other instances that access resources created from must be closed prior to its disposal, otherwise will not be able to fully clean up the temporary resources. + /// internal class TestProject : IDisposable { private bool _disposedValue; @@ -16,7 +20,7 @@ internal class TestProject : IDisposable public TestProject() { - RootPath = System.IO.Path.Join(System.IO.Path.GetTempPath(), Guid.NewGuid().ToString()); + RootPath = Path.Join(Path.GetTempPath(), Guid.NewGuid().ToString()); Directory.CreateDirectory(RootPath); TwinGet.Utils.IO.Directory.CopyDirectory( @@ -27,7 +31,7 @@ public TestProject() SolutionFile = SolutionFile.Parse(SolutionPath); - static bool isTwincatProject(string absolutePath) => TwincatProjectExtensions.Contains(System.IO.Path.GetExtension(absolutePath)); + static bool isTwincatProject(string absolutePath) => TwincatProjectExtensions.Contains(Path.GetExtension(absolutePath)); _twincatProjects = new(SolutionFile.ProjectsInOrder .Where(x => isTwincatProject(x.AbsolutePath)) @@ -40,7 +44,11 @@ protected virtual void Dispose(bool disposing) { if (disposing) { } - if (Directory.Exists(RootPath)) { Directory.Delete(RootPath, true); } + if (Directory.Exists(RootPath)) + { + Directory.Delete(RootPath, true); + } + _disposedValue = true; } } diff --git a/test/TwinGet.AutomationInterface.Test/TestUtils/TestTwincatProject.cs b/test/TwinGet.AutomationInterface.Test/TestUtils/TestTwincatProject.cs index 7549844..877d8fe 100644 --- a/test/TwinGet.AutomationInterface.Test/TestUtils/TestTwincatProject.cs +++ b/test/TwinGet.AutomationInterface.Test/TestUtils/TestTwincatProject.cs @@ -8,7 +8,7 @@ namespace TwinGet.AutomationInterface.Test.TestUtils { internal class TestTwincatProject { - private List _plcProjects = []; + private readonly List _plcProjects = []; public string Name { get; } public string AbsolutePath { get; } public IReadOnlyList PlcProjects { get => _plcProjects; } diff --git a/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs b/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs index 241c506..c6468d0 100644 --- a/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs +++ b/test/TwinGet.AutomationInterface.Test/TwincatProjectTests.cs @@ -48,9 +48,7 @@ public void Construct_WithTwincatProject_ShouldSucceed() using TestProject testProject = new(); using TwincatDteProvider dteProvider = new(this); dteProvider.Dte.Solution.Open(testProject.SolutionPath); -#pragma warning disable CS8602 // Dereference of a possibly null reference. EnvDTE.Project? project = GetFirstTwincatProject(dteProvider.Dte.Solution.Projects); -#pragma warning restore CS8602 // Dereference of a possibly null reference. // "Assert" no exception #pragma warning disable CS8604 // Possible null reference argument. @@ -65,9 +63,7 @@ public void Construct_WithNonTwincatProject_ShouldThrow() using TestProject testProject = new(); using TwincatDteProvider dteProvider = new(this); dteProvider.Dte.Solution.Open(testProject.SolutionPath); -#pragma warning disable CS8602 // Dereference of a possibly null reference. EnvDTE.Project? project = GetFirstNonTwincatProject(dteProvider.Dte.Solution.Projects); -#pragma warning restore CS8602 // Dereference of a possibly null reference. // Act #pragma warning disable CS8604 // Possible null reference argument. @@ -100,7 +96,7 @@ public void PlcProjects_ShouldBeExpected() { TestTwincatProject? testTwincatProject = testProject.TwincatProjects.Where(x => x.Name == twincatProject.Name).FirstOrDefault(); - IEnumerable expectedPlcProjectNames = testTwincatProject.PlcProjects.Select(x => x.Name); + IEnumerable? expectedPlcProjectNames = testTwincatProject?.PlcProjects.Select(x => x.Name); IEnumerable actualPlcProjectNames = twincatProject.PlcProjects.Select(x => x.Name); expectedPlcProjectNames.Should().BeEquivalentTo(actualPlcProjectNames); From 5c5b46ea6ba41b1fd22c4332a9102b704cb38b94 Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Tue, 12 Dec 2023 01:06:36 +0200 Subject: [PATCH 18/27] ci: Added explicit configuration to build step --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 86c903d..3ef1885 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -7,5 +7,5 @@ jobs: runs-on: [self-hosted, Windows, x64, TwinCAT] steps: - uses: actions/checkout@v3 - - run: .\build.ps1 + - run: .\build.ps1 -Configuration Release - run: dotnet test --configuration Release --no-build .\TwinGet.sln From 2d56af757adabf21c58d4b10d6339c2d58e7ec5e Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Tue, 12 Dec 2023 20:26:17 +0200 Subject: [PATCH 19/27] refactor: Extracted `TryCleanUpDteProvider` method Added null check in case the cleanup is done simultaneously in different processes --- .../AutomationInterface.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/TwinGet.AutomationInterface/AutomationInterface.cs b/src/TwinGet.AutomationInterface/AutomationInterface.cs index 73fa944..ff06b1d 100644 --- a/src/TwinGet.AutomationInterface/AutomationInterface.cs +++ b/src/TwinGet.AutomationInterface/AutomationInterface.cs @@ -29,17 +29,24 @@ public AutomationInterface() _dteProvider = new(this, true); } + protected void TryCleanUpDteProvider() + { + if (_dteProvider is null) { return; } + + /// We only dispose the that we created. + if (_dteProvider.Owner == this) + { + _dteProvider.Dispose(); + } + } + protected virtual void Dispose(bool disposing) { if (!_disposedValue) { if (disposing) { } - /// We only dispose the that we created. - if (_dteProvider.Owner == this) - { - _dteProvider.Dispose(); - } + TryCleanUpDteProvider(); _disposedValue = true; } } From 8473ef1d16d956c6009994186d0e2656f06a140d Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Tue, 12 Dec 2023 20:28:43 +0200 Subject: [PATCH 20/27] ci: Added logger for dotnet test --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 3ef1885..ba5109d 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -8,4 +8,4 @@ jobs: steps: - uses: actions/checkout@v3 - run: .\build.ps1 -Configuration Release - - run: dotnet test --configuration Release --no-build .\TwinGet.sln + - run: dotnet test --configuration Release --no-build --logger "console;verbosity=detailed" .\TwinGet.sln From efe446df61f0fa9792f08228d6d12c6d14c68ee5 Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Tue, 12 Dec 2023 21:27:41 +0200 Subject: [PATCH 21/27] ci: Rearranged workflow and added `whoami` --- .github/workflows/CI.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ba5109d..788e0c4 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -6,6 +6,12 @@ jobs: name: Build project runs-on: [self-hosted, Windows, x64, TwinCAT] steps: - - uses: actions/checkout@v3 - - run: .\build.ps1 -Configuration Release - - run: dotnet test --configuration Release --no-build --logger "console;verbosity=detailed" .\TwinGet.sln + - name: Checkout source + uses: actions/checkout@v3 + + - name: Build and run tests + shell: pwsh + run: | + whoami + .\build.ps1 -Configuration Release + dotnet test --configuration Release --no-build --logger "console;verbosity=detailed" .\TwinGet.sln From 7f51c1cba8e602f0369c309dfa8424f4e58defa6 Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Tue, 12 Dec 2023 22:50:12 +0200 Subject: [PATCH 22/27] ci: Separated build and test steps --- .github/workflows/CI.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 788e0c4..1899f2b 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -9,9 +9,12 @@ jobs: - name: Checkout source uses: actions/checkout@v3 - - name: Build and run tests + - name: Build shell: pwsh run: | - whoami .\build.ps1 -Configuration Release + + - name: Run tests + shell: pwsh + run: | dotnet test --configuration Release --no-build --logger "console;verbosity=detailed" .\TwinGet.sln From 42f1bc3fba089e8a3b6aefbe4e1852f70d606b7a Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Tue, 12 Dec 2023 23:51:44 +0200 Subject: [PATCH 23/27] feat: Added more properties to `PlcProject` - Added more test data to `PlcProjectDataTests` regarding unmanaged library project - Added test to `AutomationInterfaceTests` to save all managed library projects (as libraries) - Added `TestPlcProject3` to `TestTwincatProject2` which is an unmanaged project --- .../AutomationInterface.cs | 2 +- .../AutomationInterfaceConstants.cs | 3 +- src/TwinGet.AutomationInterface/PlcProject.cs | 16 +++- .../PlcProjectData.cs | 12 +-- .../AutomationInterfaceTests.cs | 25 ++++++ .../PlcProjectDataTests.cs | 41 ++++++++- .../TestTwincatProject/TestTwincatProject.sln | 20 +++++ .../TestPlcProject3/POUs/MAIN.TcPOU | 13 +++ .../TestPlcProject3/PlcTask.TcTTO | 17 ++++ .../TestPlcProject3/TestPlcProject3.plcproj | 89 +++++++++++++++++++ .../TestTwincatProject2.tsproj | 24 ++++- 11 files changed, 249 insertions(+), 13 deletions(-) create mode 100644 test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestPlcProject3/POUs/MAIN.TcPOU create mode 100644 test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestPlcProject3/PlcTask.TcTTO create mode 100644 test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestPlcProject3/TestPlcProject3.plcproj diff --git a/src/TwinGet.AutomationInterface/AutomationInterface.cs b/src/TwinGet.AutomationInterface/AutomationInterface.cs index ff06b1d..98265f6 100644 --- a/src/TwinGet.AutomationInterface/AutomationInterface.cs +++ b/src/TwinGet.AutomationInterface/AutomationInterface.cs @@ -96,7 +96,7 @@ public void LoadSolution(string filePath) } } - public static void SaveProjectAsLibrary(string outFile, string solutionPath = "") + public static void SaveProjectAsLibrary(string plcProjectName, string outFile, string solutionPath = "") { } diff --git a/src/TwinGet.AutomationInterface/AutomationInterfaceConstants.cs b/src/TwinGet.AutomationInterface/AutomationInterfaceConstants.cs index 7617b44..961ea0a 100644 --- a/src/TwinGet.AutomationInterface/AutomationInterfaceConstants.cs +++ b/src/TwinGet.AutomationInterface/AutomationInterfaceConstants.cs @@ -11,7 +11,7 @@ public static class AutomationInterfaceConstants "VisualStudio.DTE.15.0" }; - public static string TwincatXaeDownloadUrl = @"https://www.beckhoff.com/en-en/support/download-finder/search-result/?search=eXtended%20Automation%20Engineering%20%28XAE%29"; + public const string TwincatXaeDownloadUrl = @"https://www.beckhoff.com/en-en/support/download-finder/search-result/?search=eXtended%20Automation%20Engineering%20%28XAE%29"; public const string TwincatXaeProjectKind = "{B1E792BE-AA5F-4E3C-8C82-674BF9C0715B}"; public const string TwincatPlcProjectKind = TwincatXaeProjectKind; public const int ProjectItemStartingIndex = 1; @@ -22,5 +22,6 @@ public static class AutomationInterfaceConstants TwincatXaeProjectExtension, TwincatPlcProjectExtension }; + public const string TwincatPlcLibraryExtension = ".library"; } } diff --git a/src/TwinGet.AutomationInterface/PlcProject.cs b/src/TwinGet.AutomationInterface/PlcProject.cs index 9c6316a..61b6b01 100644 --- a/src/TwinGet.AutomationInterface/PlcProject.cs +++ b/src/TwinGet.AutomationInterface/PlcProject.cs @@ -12,10 +12,20 @@ public class PlcProject : ITcPlcIECProject3 private readonly ProjectFileDeserialization.PlcProjectData _plcProjectFile; public string Name { get => _plcProjectFile.PropertyGroup.Name; } - public string Path { get; } + public string? Company { get => _plcProjectFile.PropertyGroup.Company; } + public string? Title { get => _plcProjectFile.PropertyGroup.Title; } + public string? ProjectVersion { get => _plcProjectFile.PropertyGroup.ProjectVersion; } + public bool IsManagedLibrary + { + get + { + return !string.IsNullOrEmpty(Company) && !string.IsNullOrEmpty(Title) && !string.IsNullOrEmpty(ProjectVersion); + } + } + public string FilePath { get; } - public PlcProject(ITcSmTreeItem treeItem, string path) : this(path) + public PlcProject(ITcSmTreeItem treeItem, string filePath) : this(filePath) { try { @@ -47,7 +57,7 @@ protected PlcProject(string path) _plcProjectFile = plcProjectFile; } - Path = path; + FilePath = path; } public void PlcOpenExport(string bstrFile, string bstrSelection) => _plcProject.PlcOpenExport(bstrFile, bstrSelection); diff --git a/src/TwinGet.AutomationInterface/ProjectFileDeserialization/PlcProjectData.cs b/src/TwinGet.AutomationInterface/ProjectFileDeserialization/PlcProjectData.cs index 1019ec1..66e0cc1 100644 --- a/src/TwinGet.AutomationInterface/ProjectFileDeserialization/PlcProjectData.cs +++ b/src/TwinGet.AutomationInterface/ProjectFileDeserialization/PlcProjectData.cs @@ -63,22 +63,22 @@ public class PropertyGroup public string LibraryReferences { get; set; } [XmlElement("Released")] - public string Released { get; set; } + public string? Released { get; set; } [XmlElement("Title")] - public string Title { get; set; } + public string? Title { get; set; } [XmlElement("Company")] - public string Company { get; set; } + public string? Company { get; set; } [XmlElement("ProjectVersion")] - public string ProjectVersion { get; set; } + public string? ProjectVersion { get; set; } [XmlElement("LibraryCategories")] - public LibraryCategories LibraryCategories { get; set; } + public LibraryCategories? LibraryCategories { get; set; } [XmlElement("SelectedLibraryCategories")] - public SelectedLibraryCategories SelectedLibraryCategories { get; set; } + public SelectedLibraryCategories? SelectedLibraryCategories { get; set; } } public class LibraryCategories diff --git a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs index ea3d7ba..d2b489b 100644 --- a/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs +++ b/test/TwinGet.AutomationInterface.Test/AutomationInterfaceTests.cs @@ -68,6 +68,31 @@ public void LoadSolution_WithEmptyPath_ShouldThrow() sut.IsSolutionOpen.Should().BeFalse(); } + [StaFact] + public void SavePlcProjectsAsLibraries_ShouldSucceed() + { + using TestProject testProject = new(); + _output.WriteLine(testProject.RootPath); + using AutomationInterface sut = new(); + + sut.LoadSolution(testProject.SolutionPath); + + foreach (TwincatProject twincatProject in sut.TwincatProjects) + { + foreach (PlcProject plcProject in twincatProject.PlcProjects) + { + if (plcProject.IsManagedLibrary) + { + string libraryFile = Path.Join(Path.GetDirectoryName(plcProject.FilePath), $"{plcProject.Title}.library"); + + _output.WriteLine(libraryFile); + plcProject.SaveAsLibrary(libraryFile); + File.Exists(libraryFile).Should().BeTrue(); + } + } + } + } + protected virtual void Dispose(bool disposing) { if (!_disposedValue) diff --git a/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/PlcProjectDataTests.cs b/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/PlcProjectDataTests.cs index 9c8bc7b..f529efe 100644 --- a/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/PlcProjectDataTests.cs +++ b/test/TwinGet.AutomationInterface.Test/ProjectFileDeserialization/PlcProjectDataTests.cs @@ -11,6 +11,8 @@ private class TestData { public static IEnumerable ValidXmlContents() { + + // Managed library, meaning has Company, Title, and ProjectVersion information. yield return new object[] { """ @@ -50,14 +52,50 @@ public static IEnumerable ValidXmlContents() """, "TestPlcProject1", + "TwinGet.TestTwincatProject1.TestPlcProject1", "0.1.0" }; + + // Unmanaged library. + yield return new object[] + { + """ + + + + 1.0.0.0 + 2.0 + {f423d9f4-0ef7-4885-8ec0-9a403a78ec70} + True + true + true + false + TestPlcProject1 + 3.1.4024.0 + {da8cb013-01e2-4e76-aad8-c63cb197c3b9} + {65a0cb05-45a0-424f-9370-5321fe656f5f} + {d1e585ee-3f7a-4fb2-acd6-080b80c26dc3} + {e3297877-f97b-44dc-8428-54f3df37edf6} + {74d54b3b-a48f-4f65-8df7-855cc9cd89e2} + {bd174e45-2719-4cbf-b50e-b235af23264e} + TwinGet + false + + {736e1fb9-7997-4a5d-a19a-cfcf55bcd1a4} + + + + """, + "TestPlcProject1", + null, + null, + }; } } [Theory] [MemberData(nameof(TestData.ValidXmlContents), MemberType = typeof(TestData))] - public void Deserialize_FromValidXmlContent_ShouldSucceed(string xmlContent, string projectName, string projectVersion) + public void Deserialize_FromValidXmlContent_ShouldSucceed(string xmlContent, string? projectName, string projectTitle, string projectVersion) { XmlSerializer serializer = new(typeof(PlcProjectData)); @@ -70,6 +108,7 @@ public void Deserialize_FromValidXmlContent_ShouldSucceed(string xmlContent, str #pragma warning disable CS8602 // Dereference of a possibly null reference. project.PropertyGroup.Name.Should().Be(projectName); #pragma warning restore CS8602 // Dereference of a possibly null reference. + project.PropertyGroup.Title.Should().Be(projectTitle); project.PropertyGroup.ProjectVersion.Should().Be(projectVersion); } } diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject.sln b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject.sln index 198b8af..d8d04c9 100644 --- a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject.sln +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject.sln @@ -138,6 +138,26 @@ Global {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Release|TwinCAT RT (x64).Build.0 = Release|TwinCAT RT (x64) {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Release|TwinCAT RT (x86).ActiveCfg = Release|TwinCAT RT (x86) {A356EEB8-AAFC-4761-A0DD-0B8C83CA855D}.Release|TwinCAT RT (x86).Build.0 = Release|TwinCAT RT (x86) + {15A1AB25-0A54-4917-9522-8422C161DFF9}.Debug|Any CPU.ActiveCfg = Debug|TwinCAT RT (x64) + {15A1AB25-0A54-4917-9522-8422C161DFF9}.Debug|Any CPU.Build.0 = Debug|TwinCAT RT (x64) + {15A1AB25-0A54-4917-9522-8422C161DFF9}.Debug|TwinCAT CE7 (ARMV7).ActiveCfg = Debug|TwinCAT CE7 (ARMV7) + {15A1AB25-0A54-4917-9522-8422C161DFF9}.Debug|TwinCAT CE7 (ARMV7).Build.0 = Debug|TwinCAT CE7 (ARMV7) + {15A1AB25-0A54-4917-9522-8422C161DFF9}.Debug|TwinCAT OS (ARMT2).ActiveCfg = Debug|TwinCAT OS (ARMT2) + {15A1AB25-0A54-4917-9522-8422C161DFF9}.Debug|TwinCAT OS (ARMT2).Build.0 = Debug|TwinCAT OS (ARMT2) + {15A1AB25-0A54-4917-9522-8422C161DFF9}.Debug|TwinCAT RT (x64).ActiveCfg = Debug|TwinCAT RT (x64) + {15A1AB25-0A54-4917-9522-8422C161DFF9}.Debug|TwinCAT RT (x64).Build.0 = Debug|TwinCAT RT (x64) + {15A1AB25-0A54-4917-9522-8422C161DFF9}.Debug|TwinCAT RT (x86).ActiveCfg = Debug|TwinCAT RT (x86) + {15A1AB25-0A54-4917-9522-8422C161DFF9}.Debug|TwinCAT RT (x86).Build.0 = Debug|TwinCAT RT (x86) + {15A1AB25-0A54-4917-9522-8422C161DFF9}.Release|Any CPU.ActiveCfg = Release|TwinCAT RT (x64) + {15A1AB25-0A54-4917-9522-8422C161DFF9}.Release|Any CPU.Build.0 = Release|TwinCAT RT (x64) + {15A1AB25-0A54-4917-9522-8422C161DFF9}.Release|TwinCAT CE7 (ARMV7).ActiveCfg = Release|TwinCAT CE7 (ARMV7) + {15A1AB25-0A54-4917-9522-8422C161DFF9}.Release|TwinCAT CE7 (ARMV7).Build.0 = Release|TwinCAT CE7 (ARMV7) + {15A1AB25-0A54-4917-9522-8422C161DFF9}.Release|TwinCAT OS (ARMT2).ActiveCfg = Release|TwinCAT OS (ARMT2) + {15A1AB25-0A54-4917-9522-8422C161DFF9}.Release|TwinCAT OS (ARMT2).Build.0 = Release|TwinCAT OS (ARMT2) + {15A1AB25-0A54-4917-9522-8422C161DFF9}.Release|TwinCAT RT (x64).ActiveCfg = Release|TwinCAT RT (x64) + {15A1AB25-0A54-4917-9522-8422C161DFF9}.Release|TwinCAT RT (x64).Build.0 = Release|TwinCAT RT (x64) + {15A1AB25-0A54-4917-9522-8422C161DFF9}.Release|TwinCAT RT (x86).ActiveCfg = Release|TwinCAT RT (x86) + {15A1AB25-0A54-4917-9522-8422C161DFF9}.Release|TwinCAT RT (x86).Build.0 = Release|TwinCAT RT (x86) EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestPlcProject3/POUs/MAIN.TcPOU b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestPlcProject3/POUs/MAIN.TcPOU new file mode 100644 index 0000000..586a62d --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestPlcProject3/POUs/MAIN.TcPOU @@ -0,0 +1,13 @@ + + + + + + + + + + \ No newline at end of file diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestPlcProject3/PlcTask.TcTTO b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestPlcProject3/PlcTask.TcTTO new file mode 100644 index 0000000..89e127c --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestPlcProject3/PlcTask.TcTTO @@ -0,0 +1,17 @@ + + + + + 10000 + 20 + + MAIN + + {8a77b9be-bde3-42e4-b69d-0591a75a2794} + {376578a1-41e7-423e-80c8-fecb9e6eb344} + {137273e1-1983-4d3d-9f9b-6187964edfd6} + {3e48b9c8-c9f8-4fba-a209-ed4882b85946} + {2e499157-7a13-4b21-9690-f1864ec231fd} + + + \ No newline at end of file diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestPlcProject3/TestPlcProject3.plcproj b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestPlcProject3/TestPlcProject3.plcproj new file mode 100644 index 0000000..515998c --- /dev/null +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestPlcProject3/TestPlcProject3.plcproj @@ -0,0 +1,89 @@ + + + + 1.0.0.0 + 2.0 + {15a1ab25-0a54-4917-9522-8422c161dff9} + True + true + true + false + TestPlcProject3 + 3.1.4024.0 + {cf42460d-1fc4-4504-b921-c8efba3c1197} + {4a418a35-738c-4472-9b57-04df3693c8c5} + {36c8f7c7-af50-43f6-81f8-cc2d6acd0658} + {ef4af66c-1aa4-43eb-86c9-51f0dab3de81} + {e9c1c23d-6c51-427f-8a59-f7a7dfa28d83} + {85c766fa-d8cb-4c47-ada9-d9d6b6116042} + false + + + + + + + + + + Code + + + Code + + + + + + + + + + + Tc2_Standard, * (Beckhoff Automation GmbH) + Tc2_Standard + + + Tc2_System, * (Beckhoff Automation GmbH) + Tc2_System + + + Tc3_Module, * (Beckhoff Automation GmbH) + Tc3_Module + + + + + + + + "<ProjectRoot>" + + {40450F57-0AA3-4216-96F3-5444ECB29763} + + "{40450F57-0AA3-4216-96F3-5444ECB29763}" + + + ActiveVisuProfile + IR0whWr8bwfwBwAAiD2qpQAAAABVAgAA37x72QAAAAABAAAAAAAAAAEaUwB5AHMAdABlAG0ALgBTAHQAcgBpAG4AZwACTHsAZgA5ADUAYgBiADQAMgA2AC0ANQA1ADIANAAtADQAYgA0ADUALQA5ADQAMAAwAC0AZgBiADAAZgAyAGUANwA3AGUANQAxAGIAfQADCE4AYQBtAGUABDBUAHcAaQBuAEMAQQBUACAAMwAuADEAIABCAHUAaQBsAGQAIAA0ADAAMgA0AC4ANwAFFlAAcgBvAGYAaQBsAGUARABhAHQAYQAGTHsAMQA2AGUANQA1AGIANgAwAC0ANwAwADQAMwAtADQAYQA2ADMALQBiADYANQBiAC0ANgAxADQANwAxADMAOAA3ADgAZAA0ADIAfQAHEkwAaQBiAHIAYQByAGkAZQBzAAhMewAzAGIAZgBkADUANAA1ADkALQBiADAANwBmAC0ANABkADYAZQAtAGEAZQAxAGEALQBhADgAMwAzADUANgBhADUANQAxADQAMgB9AAlMewA5AGMAOQA1ADgAOQA2ADgALQAyAGMAOAA1AC0ANAAxAGIAYgAtADgAOAA3ADEALQA4ADkANQBmAGYAMQBmAGUAZABlADEAYQB9AAoOVgBlAHIAcwBpAG8AbgALBmkAbgB0AAwKVQBzAGEAZwBlAA0KVABpAHQAbABlAA4aVgBpAHMAdQBFAGwAZQBtAE0AZQB0AGUAcgAPDkMAbwBtAHAAYQBuAHkAEAxTAHkAcwB0AGUAbQARElYAaQBzAHUARQBsAGUAbQBzABIwVgBpAHMAdQBFAGwAZQBtAHMAUwBwAGUAYwBpAGEAbABDAG8AbgB0AHIAbwBsAHMAEyhWAGkAcwB1AEUAbABlAG0AcwBXAGkAbgBDAG8AbgB0AHIAbwBsAHMAFCRWAGkAcwB1AEUAbABlAG0AVABlAHgAdABFAGQAaQB0AG8AcgAVIlYAaQBzAHUATgBhAHQAaQB2AGUAQwBvAG4AdAByAG8AbAAWFHYAaQBzAHUAaQBuAHAAdQB0AHMAFwxzAHkAcwB0AGUAbQAYGFYAaQBzAHUARQBsAGUAbQBCAGEAcwBlABkmRABlAHYAUABsAGEAYwBlAGgAbwBsAGQAZQByAHMAVQBzAGUAZAAaCGIAbwBvAGwAGyJQAGwAdQBnAGkAbgBDAG8AbgBzAHQAcgBhAGkAbgB0AHMAHEx7ADQAMwBkADUAMgBiAGMAZQAtADkANAAyAGMALQA0ADQAZAA3AC0AOQBlADkANAAtADEAYgBmAGQAZgAzADEAMABlADYAMwBjAH0AHRxBAHQATABlAGEAcwB0AFYAZQByAHMAaQBvAG4AHhRQAGwAdQBnAGkAbgBHAHUAaQBkAB8WUwB5AHMAdABlAG0ALgBHAHUAaQBkACBIYQBmAGMAZAA1ADQANAA2AC0ANAA5ADEANAAtADQAZgBlADcALQBiAGIANwA4AC0AOQBiAGYAZgBlAGIANwAwAGYAZAAxADcAIRRVAHAAZABhAHQAZQBJAG4AZgBvACJMewBiADAAMwAzADYANgBhADgALQBiADUAYwAwAC0ANABiADkAYQAtAGEAMAAwAGUALQBlAGIAOAA2ADAAMQAxADEAMAA0AGMAMwB9ACMOVQBwAGQAYQB0AGUAcwAkTHsAMQA4ADYAOABmAGYAYwA5AC0AZQA0AGYAYwAtADQANQAzADIALQBhAGMAMAA2AC0AMQBlADMAOQBiAGIANQA1ADcAYgA2ADkAfQAlTHsAYQA1AGIAZAA0ADgAYwAzAC0AMABkADEANwAtADQAMQBiADUALQBiADEANgA0AC0ANQBmAGMANgBhAGQAMgBiADkANgBiADcAfQAmFk8AYgBqAGUAYwB0AHMAVAB5AHAAZQAnVFUAcABkAGEAdABlAEwAYQBuAGcAdQBhAGcAZQBNAG8AZABlAGwARgBvAHIAQwBvAG4AdgBlAHIAdABpAGIAbABlAEwAaQBiAHIAYQByAGkAZQBzACgQTABpAGIAVABpAHQAbABlACkUTABpAGIAQwBvAG0AcABhAG4AeQAqHlUAcABkAGEAdABlAFAAcgBvAHYAaQBkAGUAcgBzACs4UwB5AHMAdABlAG0ALgBDAG8AbABsAGUAYwB0AGkAbwBuAHMALgBIAGEAcwBoAHQAYQBiAGwAZQAsEnYAaQBzAHUAZQBsAGUAbQBzAC1INgBjAGIAMQBjAGQAZQAxAC0AZAA1AGQAYwAtADQAYQAzAGIALQA5ADAANQA0AC0AMgAxAGYAYQA3ADUANgBhADMAZgBhADQALihJAG4AdABlAHIAZgBhAGMAZQBWAGUAcgBzAGkAbwBuAEkAbgBmAG8AL0x7AGMANgAxADEAZQA0ADAAMAAtADcAZgBiADkALQA0AGMAMwA1AC0AYgA5AGEAYwAtADQAZQAzADEANABiADUAOQA5ADYANAAzAH0AMBhNAGEAagBvAHIAVgBlAHIAcwBpAG8AbgAxGE0AaQBuAG8AcgBWAGUAcgBzAGkAbwBuADIMTABlAGcAYQBjAHkAMzBMAGEAbgBnAHUAYQBnAGUATQBvAGQAZQBsAFYAZQByAHMAaQBvAG4ASQBuAGYAbwA0MEwAbwBhAGQATABpAGIAcgBhAHIAaQBlAHMASQBuAHQAbwBQAHIAbwBqAGUAYwB0ADUaQwBvAG0AcABhAHQAaQBiAGkAbABpAHQAeQDQAAIaA9ADAS0E0AUGGgfQBwgaAUUHCQjQAAkaBEUKCwQDAAAABQAAAA0AAAAAAAAA0AwLrQIAAADQDQEtDtAPAS0Q0AAJGgRFCgsEAwAAAAUAAAANAAAAKAAAANAMC60BAAAA0A0BLRHQDwEtENAACRoERQoLBAMAAAAFAAAADQAAAAAAAADQDAutAgAAANANAS0S0A8BLRDQAAkaBEUKCwQDAAAABQAAAA0AAAAUAAAA0AwLrQIAAADQDQEtE9APAS0Q0AAJGgRFCgsEAwAAAAUAAAANAAAAAAAAANAMC60CAAAA0A0BLRTQDwEtENAACRoERQoLBAMAAAAFAAAADQAAAAAAAADQDAutAgAAANANAS0V0A8BLRDQAAkaBEUKCwQDAAAABQAAAA0AAAAAAAAA0AwLrQIAAADQDQEtFtAPAS0X0AAJGgRFCgsEAwAAAAUAAAANAAAAKAAAANAMC60EAAAA0A0BLRjQDwEtENAZGq0BRRscAdAAHBoCRR0LBAMAAAAFAAAADQAAAAAAAADQHh8tINAhIhoCRSMkAtAAJRoFRQoLBAMAAAADAAAAAAAAAAoAAADQJgutAAAAANADAS0n0CgBLRHQKQEtENAAJRoFRQoLBAMAAAADAAAAAAAAAAoAAADQJgutAQAAANADAS0n0CgBLRHQKQEtEJoqKwFFAAEC0AABLSzQAAEtF9AAHy0t0C4vGgPQMAutAQAAANAxC60XAAAA0DIarQDQMy8aA9AwC60CAAAA0DELrQMAAADQMhqtANA0Gq0A0DUarQA= + + + {192FAD59-8248-4824-A8DE-9177C94C195A} + + "{192FAD59-8248-4824-A8DE-9177C94C195A}" + + + + + + + + + System.Collections.Hashtable + {54dd0eac-a6d8-46f2-8c27-2f43c7e49861} + System.String + + + + + \ No newline at end of file diff --git a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestTwincatProject2.tsproj b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestTwincatProject2.tsproj index 2e55acc..2eae21b 100644 --- a/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestTwincatProject2.tsproj +++ b/test/TwinGet.AutomationInterface.Test/TestTwincatProject/TestTwincatProject2/TestTwincatProject2.tsproj @@ -1,4 +1,26 @@ - + + + + + PlcTask + + + + + + + TestPlcProject3 Instance + {08500001-0000-0000-F000-000000000064} + + + 1 + Default + + + + + + From 9812197ee0d721d2e8d293b39fa305947c3492db Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Wed, 13 Dec 2023 00:05:50 +0200 Subject: [PATCH 24/27] docs: Updated information regarding self-hosted runner configuration --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ffce138..3434b51 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,8 @@ This project CI pipeline uses self-hosted runner. Following are the required sof * [Visual Studio 2022](https://visualstudio.microsoft.com/vs/) * [GitHub runner](https://github.com/actions/runner/releases) * A service account for running GitHub runner as a service -* The service account must be added to the group "Distributed COM Users", for example, using the following command, +* The service account must be added to the "Administrators" group, for example, using the following command, ```powershell - Add-LocalGroupMember -Group "Distributed COM Users" -Member + Add-LocalGroupMember -Group "Administrators" -Member ``` From a14a354b790a6ddf0539741397baa4df99deb26c Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Wed, 13 Dec 2023 20:33:46 +0200 Subject: [PATCH 25/27] refactor: Changed return type for `TryCleanUpDteProvider` --- src/TwinGet.AutomationInterface/AutomationInterface.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/TwinGet.AutomationInterface/AutomationInterface.cs b/src/TwinGet.AutomationInterface/AutomationInterface.cs index 98265f6..fc62f24 100644 --- a/src/TwinGet.AutomationInterface/AutomationInterface.cs +++ b/src/TwinGet.AutomationInterface/AutomationInterface.cs @@ -29,15 +29,18 @@ public AutomationInterface() _dteProvider = new(this, true); } - protected void TryCleanUpDteProvider() + protected bool TryCleanUpDteProvider() { - if (_dteProvider is null) { return; } + if (_dteProvider is null) { return false; } /// We only dispose the that we created. if (_dteProvider.Owner == this) { _dteProvider.Dispose(); + return true; } + + return false; } protected virtual void Dispose(bool disposing) From cee99d60599595a138ac08ac958ee1549b2b0eec Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Wed, 13 Dec 2023 20:51:39 +0200 Subject: [PATCH 26/27] ci: Refactored build script and added `common.ps1` --- build.ps1 | 9 +++++---- common.ps1 | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 common.ps1 diff --git a/build.ps1 b/build.ps1 index 2d09354..0c56c39 100644 --- a/build.ps1 +++ b/build.ps1 @@ -6,15 +6,16 @@ param ( $Configuration = "Debug" ) +. "$PSScriptRoot\common.ps1" -$vsInstallationPath = .\tools\vswhere.exe -latest -property installationPath +$msBuildPath = Resolve-MsBuildPath -ErrorAction Stop -$msBuildPath = Join-Path -Path $vsInstallationPath -ChildPath 'MSBuild\Current\Bin\MSBuild.exe' +if (!$msBuildPath) { + throw "Could not resolve MSBuild path." +} $null = Test-Path $msBuildPath -ErrorAction Stop -$null = Get-Command nuget -ErrorAction Stop - $solution = Join-Path -Path $PSScriptRoot -ChildPath 'TwinGet.sln' dotnet restore $solution diff --git a/common.ps1 b/common.ps1 new file mode 100644 index 0000000..8aee47f --- /dev/null +++ b/common.ps1 @@ -0,0 +1,15 @@ +$VsWherePath = "$PSScriptRoot\tools\vswhere.exe" +$VsInstallationPath = & $VsWherePath -latest -property installationPath + +function Resolve-MsBuildPath { + [CmdletBinding()] + param ( + ) + + if (!$VsInstallationPath) { + Write-Error "Could not resolve Visual Studio installation path." + return $null + } + + return Join-Path -Path $VsInstallationPath -ChildPath 'MSBuild\Current\Bin\MSBuild.exe' +} From de23c1a49b783ae81c96014beaf9761813165252 Mon Sep 17 00:00:00 2001 From: Giang Nguyen Date: Sat, 17 Feb 2024 20:55:32 +0200 Subject: [PATCH 27/27] test: Refactored PLC project initialization in `TestTwincatProject` --- .../TestUtils/TestTwincatProject.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/test/TwinGet.AutomationInterface.Test/TestUtils/TestTwincatProject.cs b/test/TwinGet.AutomationInterface.Test/TestUtils/TestTwincatProject.cs index 877d8fe..d9e3b8a 100644 --- a/test/TwinGet.AutomationInterface.Test/TestUtils/TestTwincatProject.cs +++ b/test/TwinGet.AutomationInterface.Test/TestUtils/TestTwincatProject.cs @@ -24,15 +24,12 @@ public TestTwincatProject(string name, string absolutePath) using (StringReader reader = new(xmlContent)) { - TcSmProjectData tcSmProject = serializer.Deserialize(reader) as TcSmProjectData ?? throw new InvalidProjectFileFormat("The format of TwinCAT project file is invalid.", absolutePath); - - if (tcSmProject.Project.Plc?.Projects is not null) - { - _plcProjects.AddRange(from ProjectElement project in tcSmProject.Project.Plc.Projects - let plcProject = new TestPlcProject(Path.Join(rootDir, project.PrjFilePath)) - where plcProject is not null - select plcProject); - } + TcSmProjectData tcSmProject = serializer.Deserialize(reader) as TcSmProjectData ?? throw new InvalidProjectFileFormat("The format of TwinCAT project file is invalid.", absolutePath); + + var plcProjects = tcSmProject.Project.Plc?.Projects? + .Select(x => new TestPlcProject(Path.Join(rootDir, x.PrjFilePath))); + + _plcProjects = new(plcProjects ?? []); } } }