├── .github └── workflows │ └── dotnet.yml ├── .gitignore ├── CustomTypeLibrary ├── CustomTypeLibrary.csproj └── CustomVector.cs ├── LICENSE.txt ├── README.md ├── UaClient.UnitTests ├── ITestCertificateStore.cs ├── IntegrationTests │ └── IntegrationTests.cs ├── LICENSE.txt ├── TestCertificateStore.cs ├── TestException.cs ├── ThrowingTestCertificateStore.cs ├── UnitTests │ ├── ArraySegmentExtensionTests.cs │ ├── Channels │ │ ├── BinaryDecoderTests.Equivalency.cs │ │ ├── BinaryDecoderTests.cs │ │ ├── BinaryEncoderTests.Equivalency.cs │ │ ├── BinaryEncoderTests.cs │ │ ├── CommunicationObjectTests.cs │ │ ├── UaSecureConversationTests.Data.cs │ │ └── UaSecureConversationTests.cs │ ├── DataTypeIdAttributeTests.cs │ ├── DataValueExtensionTests.cs │ ├── DataValueTests.cs │ ├── DictionaryStoreTests.cs │ ├── ErrorsContainerTests.cs │ ├── ExpandedNodeIdTests.cs │ ├── ExtensionObjectTests.cs │ ├── IssuedIdentityTests.cs │ ├── LocalizedTextTests.cs │ ├── MonitoredItemAttributeTests.cs │ ├── NodeIdTests.cs │ ├── ObservableQueueTests.cs │ ├── QualifiedNameTests.cs │ ├── ServiceExtensionsTests.cs │ ├── ServiceResultTests.cs │ ├── ServiceSetTests.cs │ ├── StatusCodeTests.cs │ ├── TypeLibraryTests.cs │ ├── UaApplicationOptionsTests.cs │ ├── UserNameIdentityTests.cs │ ├── VariantTests.cs │ ├── X509IdentityTests.cs │ └── XmlEncodingIdAttributeTests.cs ├── Workstation.UaClient.UnitTests.csproj ├── appSettings.json └── coverlet.runsettings ├── UaClient ├── Collections │ ├── ErrorsContainer.cs │ └── ObservableQueue.cs ├── Internal │ └── System.Diagnostics.CodeAnalysis.cs ├── Key.snk ├── ServiceModel │ └── Ua │ │ ├── AccessLevelFlags.cs │ │ ├── AcknowledgeableCondition.cs │ │ ├── AlarmCondition.cs │ │ ├── AnonymousIdentity.cs │ │ ├── ArraySegmentExtensions.cs │ │ ├── AttributeIds.generated.cs │ │ ├── AttributeIds.tt │ │ ├── AttributeServiceSet.cs │ │ ├── BaseEvent.cs │ │ ├── BinaryEncodingIdAttribute.cs │ │ ├── ByteSequenceComparer.cs │ │ ├── Channels │ │ ├── BinaryDecoder.cs │ │ ├── BinaryEncoder.cs │ │ ├── BinaryEncodingProvider.cs │ │ ├── ClientSecureChannel.cs │ │ ├── ClientSessionChannel.cs │ │ ├── ClientTransportChannel.cs │ │ ├── CommunicationObject.cs │ │ ├── EncodingContext.cs │ │ ├── MessageTypes.cs │ │ ├── ServiceOperation.cs │ │ ├── StackProfile.cs │ │ ├── StackProfiles.cs │ │ ├── UaClientConnection.cs │ │ ├── UaSecureConversation.cs │ │ ├── UaSecureConversationProvider.cs │ │ └── UaTcpConnectionProvider.cs │ │ ├── CommunicationsState.cs │ │ ├── Condition.cs │ │ ├── DataTypeIdAttribute.cs │ │ ├── DataValue.cs │ │ ├── DataValueExtensions.cs │ │ ├── DiagnosticFlags.cs │ │ ├── DiagnosticInfo.cs │ │ ├── DirectoryStore.cs │ │ ├── DiscoveryService.cs │ │ ├── EventFieldAttribute.cs │ │ ├── EventHelper.cs │ │ ├── EventNotifierFlags.cs │ │ ├── ExpandedNodeId.cs │ │ ├── ExtensionObject.cs │ │ ├── ICertificateStore.cs │ │ ├── ICommunicationObject.cs │ │ ├── IConversation.cs │ │ ├── IConversationProvider.cs │ │ ├── IDecoder.cs │ │ ├── IEncodable.cs │ │ ├── IEncoder.cs │ │ ├── IEncodingProvider.cs │ │ ├── IOptionalFields.cs │ │ ├── IRequestChannel.cs │ │ ├── IServiceRequest.cs │ │ ├── IServiceResponse.cs │ │ ├── ISetDataErrorInfo.cs │ │ ├── ITransportConnection.cs │ │ ├── ITransportConnectionProvider.cs │ │ ├── IUserIdentity.cs │ │ ├── IssuedIdentity.cs │ │ ├── LocalizedText.cs │ │ ├── MappedEndpoint.cs │ │ ├── MethodServiceSet.cs │ │ ├── MonitoredItemAttribute.cs │ │ ├── MonitoredItemBase.cs │ │ ├── MonitoredItemCollection.cs │ │ ├── MonitoredItemServiceSet.cs │ │ ├── NodeId.cs │ │ ├── NodeIds.generated.cs │ │ ├── NodeIds.tt │ │ ├── NodeManagementServiceSet.cs │ │ ├── QualifiedName.cs │ │ ├── QueryServiceSet.cs │ │ ├── Schema │ │ ├── AttributeIds.csv │ │ ├── NodeIds.csv │ │ ├── StatusCodes.csv │ │ └── Types.xsd │ │ ├── SecurityPolicyUris.cs │ │ ├── ServiceExtensions.cs │ │ ├── ServiceResult.cs │ │ ├── ServiceResultException.cs │ │ ├── SessionServiceSet.cs │ │ ├── StatusCode.cs │ │ ├── StatusCodes.generated.cs │ │ ├── StatusCodes.tt │ │ ├── Structure.cs │ │ ├── SubscriptionAttribute.cs │ │ ├── SubscriptionBase.cs │ │ ├── SubscriptionServiceSet.cs │ │ ├── TransportConnectionOptions.cs │ │ ├── TransportProfileUris.cs │ │ ├── TypeLibrary.cs │ │ ├── Types.generated.cs │ │ ├── Types.tt │ │ ├── UaApplication.cs │ │ ├── UaApplicationBuilder.cs │ │ ├── UaApplicationOptions.cs │ │ ├── UserNameIdentity.cs │ │ ├── Variant.cs │ │ ├── VariantExtensions.cs │ │ ├── ViewServiceSet.cs │ │ ├── X509Identity.cs │ │ └── XmlEncodingIdAttribute.cs ├── Workstation.UaClient.csproj └── stylecop.json ├── _config.yml ├── opc-ua-client.sln └── robot6.jpg /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a .NET project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net 3 | 4 | name: .NET 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Setup .NET 20 | uses: actions/setup-dotnet@v3 21 | with: 22 | dotnet-version: 6.0.x 23 | - name: Restore dependencies 24 | run: dotnet restore 25 | - name: Build 26 | run: dotnet build --no-restore 27 | - name: Test 28 | run: dotnet test --no-build --verbosity normal --filter FullyQualifiedName!~Workstation.UaClient.IntegrationTests 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studo 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | *_i.c 42 | *_p.c 43 | *_i.h 44 | *.ilk 45 | *.meta 46 | *.obj 47 | *.pch 48 | *.pdb 49 | *.pgc 50 | *.pgd 51 | *.rsp 52 | *.sbr 53 | *.tlb 54 | *.tli 55 | *.tlh 56 | *.tmp 57 | *.tmp_proj 58 | *.log 59 | *.vspscc 60 | *.vssscc 61 | .builds 62 | *.pidb 63 | *.svclog 64 | *.scc 65 | 66 | # Chutzpah Test files 67 | _Chutzpah* 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | *.cachefile 76 | 77 | # Visual Studio profiler 78 | *.psess 79 | *.vsp 80 | *.vspx 81 | 82 | # TFS 2012 Local Workspace 83 | $tf/ 84 | 85 | # Guidance Automation Toolkit 86 | *.gpState 87 | 88 | # ReSharper is a .NET coding add-in 89 | _ReSharper*/ 90 | *.[Rr]e[Ss]harper 91 | *.DotSettings.user 92 | 93 | # JustCode is a .NET coding addin-in 94 | .JustCode 95 | 96 | # TeamCity is a build add-in 97 | _TeamCity* 98 | 99 | # DotCover is a Code Coverage Tool 100 | *.dotCover 101 | 102 | # NCrunch 103 | _NCrunch_* 104 | .*crunch*.local.xml 105 | 106 | # MightyMoose 107 | *.mm.* 108 | AutoTest.Net/ 109 | 110 | # Web workbench (sass) 111 | .sass-cache/ 112 | 113 | # Installshield output folder 114 | [Ee]xpress/ 115 | 116 | # DocProject is a documentation generator add-in 117 | DocProject/buildhelp/ 118 | DocProject/Help/*.HxT 119 | DocProject/Help/*.HxC 120 | DocProject/Help/*.hhc 121 | DocProject/Help/*.hhk 122 | DocProject/Help/*.hhp 123 | DocProject/Help/Html2 124 | DocProject/Help/html 125 | 126 | # Click-Once directory 127 | publish/ 128 | 129 | # Publish Web Output 130 | *.[Pp]ublish.xml 131 | *.azurePubxml 132 | # TODO: Comment the next line if you want to checkin your web deploy settings 133 | # but database connection strings (with potential passwords) will be unencrypted 134 | *.pubxml 135 | *.publishproj 136 | 137 | # NuGet Packages 138 | *.nupkg 139 | # The packages folder can be ignored because of Package Restore 140 | **/packages/* 141 | # except build/, which is used as an MSBuild target. 142 | !**/packages/build/ 143 | # Uncomment if necessary however generally it will be regenerated when needed 144 | #!**/packages/repositories.config 145 | *.nuget.props 146 | *.nuget.targets 147 | *.lock.json 148 | 149 | # Windows Azure Build Output 150 | csx/ 151 | *.build.csdef 152 | 153 | # Windows Store app package directory 154 | AppPackages/ 155 | 156 | # Others 157 | *.[Cc]ache 158 | ClientBin/ 159 | #[Ss]tyle[Cc]op.* 160 | ~$* 161 | *~ 162 | *.dbmdl 163 | *.dbproj.schemaview 164 | *.pfx 165 | *.publishsettings 166 | node_modules/ 167 | bower_components/ 168 | 169 | # RIA/Silverlight projects 170 | Generated_Code/ 171 | 172 | # Backup & report files from converting an old project file 173 | # to a newer Visual Studio version. Backup files are not needed, 174 | # because we have git ;-) 175 | _UpgradeReport_Files/ 176 | Backup*/ 177 | UpgradeLog*.XML 178 | UpgradeLog*.htm 179 | 180 | # SQL Server files 181 | *.mdf 182 | *.ldf 183 | 184 | # Business Intelligence projects 185 | *.rdl.data 186 | *.bim.layout 187 | *.bim_*.settings 188 | 189 | # Microsoft Fakes 190 | FakesAssemblies/ 191 | 192 | # Node.js Tools for Visual Studio 193 | .ntvs_analysis.dat 194 | 195 | # Visual Studio 6 build log 196 | *.plg 197 | 198 | # Visual Studio 6 workspace options file 199 | *.opt 200 | /UaClient/Workstation.UaClient.xml 201 | project.lock.json 202 | /UaClient.UnitTests/UnitTest1 - Copy.cs 203 | -------------------------------------------------------------------------------- /CustomTypeLibrary/CustomTypeLibrary.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /CustomTypeLibrary/CustomVector.cs: -------------------------------------------------------------------------------- 1 |  2 | using System; 3 | using Workstation.ServiceModel.Ua; 4 | 5 | [assembly: TypeLibrary()] 6 | namespace CustomTypeLibrary 7 | { 8 | [DataTypeId("nsu=http://www.unifiedautomation.com/DemoServer/;i=3002")] 9 | [BinaryEncodingId("nsu=http://www.unifiedautomation.com/DemoServer/;i=5054")] 10 | public class CustomVector : Structure 11 | { 12 | public double X { get; set; } 13 | public double Y { get; set; } 14 | public double Z { get; set; } 15 | public override void Encode(IEncoder encoder) 16 | { 17 | encoder.WriteDouble("X", X); 18 | encoder.WriteDouble("Y", Y); 19 | encoder.WriteDouble("Z", Z); 20 | } 21 | public override void Decode(IDecoder decoder) 22 | { 23 | X = decoder.ReadDouble("X"); 24 | Y = decoder.ReadDouble("Y"); 25 | Z = decoder.ReadDouble("Z"); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Converter Systems LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /UaClient.UnitTests/ITestCertificateStore.cs: -------------------------------------------------------------------------------- 1 | using Workstation.ServiceModel.Ua; 2 | 3 | namespace Workstation.UaClient 4 | { 5 | public interface ITestCertificateStore : ICertificateStore 6 | { 7 | byte[] ServerCertificate { get; } 8 | byte[] ClientCertificate { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /UaClient.UnitTests/LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Converter Systems LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /UaClient.UnitTests/TestException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.Serialization; 4 | using System.Text; 5 | 6 | namespace Workstation.UaClient 7 | { 8 | public sealed class TestException : Exception 9 | { 10 | public TestException() 11 | { 12 | } 13 | 14 | public TestException(string message) : base(message) 15 | { 16 | } 17 | 18 | public TestException(string message, Exception innerException) : base(message, innerException) 19 | { 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /UaClient.UnitTests/ThrowingTestCertificateStore.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | using Org.BouncyCastle.Crypto.Parameters; 3 | using Org.BouncyCastle.X509; 4 | using System; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using Workstation.ServiceModel.Ua; 8 | 9 | namespace Workstation.UaClient 10 | { 11 | public class ThrowingTestCertificateStore : ITestCertificateStore 12 | { 13 | public byte[] ServerCertificate => null; 14 | 15 | public byte[] ClientCertificate => null; 16 | 17 | public Task<(X509Certificate Certificate, RsaKeyParameters Key)> GetLocalCertificateAsync(ApplicationDescription applicationDescription, ILogger logger, CancellationToken token) 18 | => throw new NotImplementedException(); 19 | 20 | public Task ValidateRemoteCertificateAsync(X509Certificate certificate, ILogger logger, CancellationToken token) 21 | => throw new NotImplementedException(); 22 | } 23 | } -------------------------------------------------------------------------------- /UaClient.UnitTests/UnitTests/ArraySegmentExtensionTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using Workstation.ServiceModel.Ua; 7 | using Xunit; 8 | 9 | namespace Workstation.UaClient.UnitTests 10 | { 11 | public class ArraySegmentExtensionTests 12 | { 13 | [Fact] 14 | public void AsArraySegment1() 15 | { 16 | var array = new int[] { 1, 2, 3, 4, 5 }; 17 | 18 | array.AsArraySegment() 19 | .Should().BeEquivalentTo(array); 20 | } 21 | 22 | [Fact] 23 | public void AsArraySegmentWithOffset() 24 | { 25 | var array = new int[] { 1, 2, 3, 4, 5 }; 26 | 27 | array.AsArraySegment(1) 28 | .Should().BeEquivalentTo(array.Skip(1)); 29 | } 30 | 31 | [Fact] 32 | public void AsArraySegmentWithOffsetAndCount() 33 | { 34 | var array = new int[] { 1, 2, 3, 4, 5 }; 35 | 36 | array.AsArraySegment(1, 3) 37 | .Should().BeEquivalentTo(array.Skip(1).Take(3)); 38 | } 39 | 40 | [Fact] 41 | public void CreateStream() 42 | { 43 | var array = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 44 | 45 | using (var stream = ArraySegmentExtensions.CreateStream(array)) 46 | { 47 | var buffer = new byte[array.Length]; 48 | 49 | stream.Length 50 | .Should().Be(array.Length); 51 | 52 | stream.Read(buffer, 0, buffer.Length); 53 | 54 | buffer 55 | .Should().BeEquivalentTo(array); 56 | } 57 | } 58 | 59 | [Fact] 60 | public void CreateBinaryReader() 61 | { 62 | var array = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 63 | 64 | using (var reader = ArraySegmentExtensions.CreateBinaryReader(array)) 65 | { 66 | foreach (var b in array) 67 | { 68 | reader.ReadByte() 69 | .Should().Be(b); 70 | } 71 | } 72 | } 73 | 74 | [Fact] 75 | public void CreateBinaryWriter() 76 | { 77 | var array = new byte[10]; 78 | 79 | using (var writer = ArraySegmentExtensions.CreateBinaryWriter(array)) 80 | { 81 | for (byte b = 0; b < array.Length; b++) 82 | { 83 | writer.Write(b); 84 | } 85 | 86 | array 87 | .Should().BeEquivalentTo(new byte[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }); 88 | } 89 | } 90 | 91 | [Fact] 92 | public void Take() 93 | { 94 | var array = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 95 | 96 | array.AsArraySegment().Take(3) 97 | .Should().BeEquivalentTo(array.Take(3)); 98 | } 99 | 100 | [Fact] 101 | public void Skip() 102 | { 103 | var array = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 104 | 105 | array.AsArraySegment().Skip(3) 106 | .Should().BeEquivalentTo(array.Skip(3)); 107 | } 108 | 109 | [Fact] 110 | public void Slice() 111 | { 112 | var array = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 113 | 114 | array.AsArraySegment().Slice(3, 4) 115 | .Should().BeEquivalentTo(array.Skip(3).Take(4)); 116 | } 117 | 118 | [Fact] 119 | public void TakeLast() 120 | { 121 | var array = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 122 | 123 | array.AsArraySegment().TakeLast(3) 124 | .Should().BeEquivalentTo(array.TakeLast(3)); 125 | } 126 | 127 | [Fact] 128 | public void SkipLast() 129 | { 130 | var array = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 131 | 132 | array.AsArraySegment().SkipLast(3) 133 | .Should().BeEquivalentTo(array.SkipLast(3)); 134 | } 135 | 136 | [Fact] 137 | public void CopyToArraySegment() 138 | { 139 | var array = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 140 | var input = array.AsArraySegment(2, 5); 141 | var output = new byte[10].AsArraySegment(4, 4); 142 | 143 | ArraySegmentExtensions.CopyTo(input, output); 144 | 145 | output 146 | .Should().BeEquivalentTo(input.SkipLast(1)); 147 | } 148 | 149 | [Fact] 150 | public void CopyToArray() 151 | { 152 | var array = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 153 | var input = array.AsArraySegment(2, 5); 154 | var output = new byte[10]; 155 | 156 | ArraySegmentExtensions.CopyTo(input, output); 157 | 158 | output.Take(5) 159 | .Should().BeEquivalentTo(input); 160 | } 161 | 162 | [Fact] 163 | public void ToArray() 164 | { 165 | var array = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 166 | var input = array.AsArraySegment(2, 5); 167 | 168 | var output = ArraySegmentExtensions.ToArray(input); 169 | 170 | output 171 | .Should().BeEquivalentTo(input); 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /UaClient.UnitTests/UnitTests/DataTypeIdAttributeTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using Workstation.ServiceModel.Ua; 6 | using Xunit; 7 | 8 | namespace Workstation.UaClient.UnitTests 9 | { 10 | public class DataTypeIdAttributeTests 11 | { 12 | public static IEnumerable CreateData => ExpandedNodeIdTests.ParseData; 13 | 14 | [MemberData(nameof(CreateData))] 15 | [Theory] 16 | public void Create(string s, ExpandedNodeId id) 17 | { 18 | var att = new DataTypeIdAttribute(s); 19 | 20 | att.NodeId 21 | .Should().Be(id); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /UaClient.UnitTests/UnitTests/DataValueExtensionTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using Workstation.ServiceModel.Ua; 7 | using Xunit; 8 | 9 | namespace Workstation.UaClient.UnitTests 10 | { 11 | public class DataValueExtensionTests 12 | { 13 | [Fact] 14 | public void GetValueAsString() 15 | { 16 | var val = new DataValue("Hi"); 17 | 18 | val.GetValueOrDefault() 19 | .Should().Be("Hi"); 20 | val.GetValueOrDefault(-1) 21 | .Should().Be(-1); 22 | } 23 | 24 | [Fact] 25 | public void GetValueAsCustomVector() 26 | { 27 | var obj = new CustomTypeLibrary.CustomVector { X = 1.0, Y = 2.0, Z = 3.0 }; 28 | var val = new DataValue(obj); 29 | 30 | val.GetValueOrDefault() 31 | .Should().BeEquivalentTo(obj); 32 | val.GetValueOrDefault() 33 | .Should().BeEquivalentTo(obj); 34 | val.GetValueOrDefault(-1) 35 | .Should().Be(-1); 36 | } 37 | 38 | [Fact] 39 | public void GetValueAsArrayInt32() 40 | { 41 | var array = new int[] { 1, 2, 3, 4, 5 }; 42 | var val = new DataValue(array); 43 | 44 | val.GetValueOrDefault() 45 | .Should().BeEquivalentTo(array); 46 | val.GetValueOrDefault(-1) 47 | .Should().Be(-1); 48 | } 49 | 50 | [Fact] 51 | public void GetValueAsArrayCustomVector() 52 | { 53 | var array = new CustomTypeLibrary.CustomVector[] 54 | { 55 | new CustomTypeLibrary.CustomVector { X = 1.0, Y = 2.0, Z = 3.0 } 56 | }; 57 | var val = new DataValue(array); 58 | 59 | val.GetValueOrDefault() 60 | .Should().BeEquivalentTo(array); 61 | val.GetValueOrDefault() 62 | .Should().BeEquivalentTo(array); 63 | val.GetValueOrDefault(-1) 64 | .Should().Be(-1); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /UaClient.UnitTests/UnitTests/DataValueTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Workstation.ServiceModel.Ua; 8 | using Xunit; 9 | 10 | namespace Workstation.UaClient.UnitTests 11 | { 12 | public class DataValueTests 13 | { 14 | [Fact] 15 | public void CreateFromNull() 16 | { 17 | var val = new DataValue(default(object)); 18 | 19 | val.ServerPicoseconds 20 | .Should().Be(0); 21 | val.ServerTimestamp 22 | .Should().Be(default); 23 | val.SourcePicoseconds 24 | .Should().Be(0); 25 | val.SourceTimestamp 26 | .Should().Be(default); 27 | val.StatusCode 28 | .Should().Be((StatusCode)StatusCodes.Good); 29 | val.Value 30 | .Should().Be(null); 31 | val.Variant.Value 32 | .Should().Be(null); 33 | } 34 | 35 | public static IEnumerable CreateFromObjectData { get; } = new object[][] 36 | { 37 | new object[] { default }, 38 | new object[] { true }, 39 | new object[] { (sbyte)13 }, 40 | new object[] { (byte)13 }, 41 | new object[] { (short)13 }, 42 | new object[] { (ushort)13 }, 43 | new object[] { 13 }, 44 | new object[] { (uint)13 }, 45 | new object[] { (long)13 }, 46 | new object[] { (ulong)13 }, 47 | new object[] { (float)13 }, 48 | new object[] { (double)13 }, 49 | new object[] { "13" }, 50 | new object[] { new DateTime(0L) }, 51 | new object[] { Guid.NewGuid() }, 52 | new object[] { new byte[] { 0x1, 0x3} }, 53 | }; 54 | 55 | [MemberData(nameof(CreateFromObjectData))] 56 | [Theory] 57 | public void CreateFromObject(object obj) 58 | { 59 | var val = new DataValue(obj); 60 | 61 | val.ServerPicoseconds 62 | .Should().Be(0); 63 | val.ServerTimestamp 64 | .Should().Be(default); 65 | val.SourcePicoseconds 66 | .Should().Be(0); 67 | val.SourceTimestamp 68 | .Should().Be(default); 69 | val.StatusCode 70 | .Should().Be((StatusCode)StatusCodes.Good); 71 | val.Value 72 | .Should().Be(obj); 73 | val.Variant.Value 74 | .Should().Be(obj); 75 | } 76 | 77 | [MemberData(nameof(CreateFromObjectData))] 78 | [Theory] 79 | public void CreateFromVariant(object obj) 80 | { 81 | var variant = new Variant(obj); 82 | var val = new DataValue(variant); 83 | 84 | val.ServerPicoseconds 85 | .Should().Be(0); 86 | val.ServerTimestamp 87 | .Should().Be(default); 88 | val.SourcePicoseconds 89 | .Should().Be(0); 90 | val.SourceTimestamp 91 | .Should().Be(default); 92 | val.StatusCode 93 | .Should().Be((StatusCode)StatusCodes.Good); 94 | val.Value 95 | .Should().Be(obj); 96 | val.Variant.Value 97 | .Should().Be(obj); 98 | val.Variant 99 | .Should().Be(variant); 100 | } 101 | 102 | [Fact] 103 | public void CreateWithSourceTimestamp() 104 | { 105 | var ts = DateTime.Now; 106 | var val = new DataValue(1, sourceTimestamp: ts, sourcePicoseconds: 13); 107 | 108 | val.SourcePicoseconds 109 | .Should().Be(13); 110 | val.SourceTimestamp 111 | .Should().Be(ts); 112 | } 113 | 114 | [Fact] 115 | public void CreateWithServerTimestamp() 116 | { 117 | var ts = DateTime.Now; 118 | var val = new DataValue(1, serverTimestamp: ts, serverPicoseconds: 13); 119 | 120 | val.ServerPicoseconds 121 | .Should().Be(13); 122 | val.ServerTimestamp 123 | .Should().Be(ts); 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /UaClient.UnitTests/UnitTests/ErrorsContainerTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using Workstation.Collections; 7 | using Xunit; 8 | 9 | namespace Workstation.UaClient.UnitTests 10 | { 11 | public class ErrorsContainerTests 12 | { 13 | private const string TestProperty1 = "Property1"; 14 | private const string TestProperty2 = "Property2"; 15 | 16 | public static IEnumerable TestProperties { get; } = new[] 17 | { 18 | null, 19 | "", 20 | "TestProperty" 21 | } 22 | .Select(v => new object[] { v }); 23 | 24 | [MemberData(nameof(TestProperties))] 25 | [Theory] 26 | public void Create(string property) 27 | { 28 | var container = new ErrorsContainer(s => { }); 29 | 30 | container.HasErrors 31 | .Should().BeFalse(); 32 | 33 | container.GetErrors(property) 34 | .Should().BeEmpty(); 35 | } 36 | 37 | [Fact] 38 | public void CreateNull() 39 | { 40 | Action action = null; 41 | 42 | Action act = () => new ErrorsContainer(action); 43 | act.Should().Throw(); 44 | } 45 | 46 | [MemberData(nameof(TestProperties))] 47 | [Theory] 48 | public void InsertSingle(string property) 49 | { 50 | var called = 0; 51 | var container = new ErrorsContainer(_ => called++); 52 | 53 | called 54 | .Should().Be(0); 55 | 56 | container.SetErrors(property, new[] { 1 }); 57 | 58 | called 59 | .Should().Be(1); 60 | 61 | container.HasErrors 62 | .Should().BeTrue(); 63 | 64 | container.GetErrors(property) 65 | .Should().ContainSingle() 66 | .Which 67 | .Should().Be(1); 68 | 69 | called 70 | .Should().Be(1); 71 | } 72 | 73 | [MemberData(nameof(TestProperties))] 74 | [Theory] 75 | public void InsertEmpty(string property) 76 | { 77 | var called = 0; 78 | var container = new ErrorsContainer(_ => called++); 79 | 80 | container.SetErrors(property, Enumerable.Empty()); 81 | 82 | container.HasErrors 83 | .Should().BeFalse(); 84 | 85 | container.GetErrors(property) 86 | .Should().BeEmpty(); 87 | 88 | called 89 | .Should().Be(0); 90 | } 91 | 92 | [MemberData(nameof(TestProperties))] 93 | [Theory] 94 | public void InsertNull(string property) 95 | { 96 | var called = 0; 97 | var container = new ErrorsContainer(_ => called++); 98 | 99 | container.SetErrors(property, null); 100 | 101 | container.HasErrors 102 | .Should().BeFalse(); 103 | 104 | container.GetErrors(property) 105 | .Should().BeEmpty(); 106 | 107 | called 108 | .Should().Be(0); 109 | } 110 | 111 | [MemberData(nameof(TestProperties))] 112 | [Theory] 113 | public void InsertSingleAndRemove(string property) 114 | { 115 | var called = 0; 116 | var container = new ErrorsContainer(_ => called++); 117 | 118 | container.SetErrors(property, new[] { 1 }); 119 | container.SetErrors(property, null); 120 | 121 | called 122 | .Should().Be(2); 123 | 124 | container.HasErrors 125 | .Should().BeFalse(); 126 | 127 | container.GetErrors(property) 128 | .Should().BeEmpty(); 129 | } 130 | 131 | [MemberData(nameof(TestProperties))] 132 | [Theory] 133 | public void InsertSingleAndClear(string property) 134 | { 135 | var called = 0; 136 | var container = new ErrorsContainer(_ => called++); 137 | 138 | container.SetErrors(property, new[] { 1 }); 139 | container.ClearErrors(property); 140 | 141 | called 142 | .Should().Be(2); 143 | 144 | container.HasErrors 145 | .Should().BeFalse(); 146 | 147 | container.GetErrors(property) 148 | .Should().BeEmpty(); 149 | } 150 | 151 | [MemberData(nameof(TestProperties))] 152 | [Theory] 153 | public void InsertMany(string property) 154 | { 155 | var called = 0; 156 | var container = new ErrorsContainer(_ => called++); 157 | 158 | container.SetErrors(property, new[] { 1 }); 159 | container.SetErrors(property, new[] { 2, 3, 6 }); 160 | 161 | called 162 | .Should().Be(2); 163 | 164 | container.HasErrors 165 | .Should().BeTrue(); 166 | 167 | container.GetErrors(property) 168 | .Should().BeEquivalentTo(new[] { 2, 3, 6 }); 169 | } 170 | 171 | [MemberData(nameof(TestProperties))] 172 | [Theory] 173 | public void InsertSame(string property) 174 | { 175 | var called = 0; 176 | var container = new ErrorsContainer(_ => called++); 177 | 178 | container.SetErrors(property, new[] { 1, 2, 4 }); 179 | container.SetErrors(property, new[] { 1, 2, 4 }); 180 | 181 | called 182 | .Should().Be(2); 183 | 184 | container.HasErrors 185 | .Should().BeTrue(); 186 | 187 | container.GetErrors(property) 188 | .Should().BeEquivalentTo(new[] { 1, 2, 4 }); 189 | } 190 | 191 | [Fact] 192 | public void InsertForTwoProperties() 193 | { 194 | var called = 0; 195 | var container = new ErrorsContainer(_ => called++); 196 | 197 | container.SetErrors(TestProperty1, new[] { 1, 2, 4 }); 198 | container.SetErrors(TestProperty2, new[] { 5, 6 }); 199 | 200 | called 201 | .Should().Be(2); 202 | 203 | container.HasErrors 204 | .Should().BeTrue(); 205 | 206 | container.GetErrors(TestProperty1) 207 | .Should().BeEquivalentTo(new[] { 1, 2, 4 }); 208 | container.GetErrors(TestProperty2) 209 | .Should().BeEquivalentTo(new[] { 5, 6 }); 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /UaClient.UnitTests/UnitTests/ExtensionObjectTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Xml.Linq; 8 | using Workstation.ServiceModel.Ua; 9 | using Xunit; 10 | 11 | namespace Workstation.UaClient.UnitTests 12 | { 13 | public class ExtensionObjectTests 14 | { 15 | [Fact] 16 | public void CreateFromByteStringNull() 17 | { 18 | var nodeid = new ExpandedNodeId(54); 19 | var obj = new ExtensionObject(default(byte[]), nodeid); 20 | 21 | obj.Body 22 | .Should().BeNull(); 23 | obj.BodyType 24 | .Should().Be(BodyType.None); 25 | obj.TypeId 26 | .Should().BeNull(); 27 | } 28 | 29 | [Fact] 30 | public void CreateFromByteString() 31 | { 32 | var nodeid = new ExpandedNodeId(54); 33 | var body = new byte[] { 12, 13, 14 }; 34 | var obj = new ExtensionObject(body, nodeid); 35 | 36 | obj.Body 37 | .Should().BeSameAs(body); 38 | obj.BodyType 39 | .Should().Be(BodyType.ByteString); 40 | obj.TypeId 41 | .Should().BeSameAs(nodeid); 42 | } 43 | 44 | [Fact] 45 | public void CreateFromXElementNull() 46 | { 47 | var nodeid = new ExpandedNodeId(54); 48 | var obj = new ExtensionObject(default(XElement), nodeid); 49 | 50 | obj.Body 51 | .Should().BeNull(); 52 | obj.BodyType 53 | .Should().Be(BodyType.None); 54 | obj.TypeId 55 | .Should().BeNull(); 56 | } 57 | 58 | [Fact] 59 | public void CreateFromXElement() 60 | { 61 | var nodeid = new ExpandedNodeId(54); 62 | var body = XElement.Parse(@""); 63 | var obj = new ExtensionObject(body, nodeid); 64 | 65 | obj.Body 66 | .Should().BeSameAs(body); 67 | obj.BodyType 68 | .Should().Be(BodyType.XmlElement); 69 | obj.TypeId 70 | .Should().BeSameAs(nodeid); 71 | } 72 | 73 | [Fact] 74 | public void CreateFromEncodableNull() 75 | { 76 | var nodeid = new ExpandedNodeId(54); 77 | var obj = new ExtensionObject(default(IEncodable), nodeid); 78 | 79 | obj.Body 80 | .Should().BeNull(); 81 | obj.BodyType 82 | .Should().Be(BodyType.None); 83 | obj.TypeId 84 | .Should().BeNull(); 85 | } 86 | 87 | [Fact] 88 | public void CreateFromEncodable() 89 | { 90 | var nodeid = new ExpandedNodeId(54); 91 | var body = new ReadRequest(); 92 | var obj = new ExtensionObject(body, nodeid); 93 | 94 | obj.Body 95 | .Should().BeSameAs(body); 96 | obj.BodyType 97 | .Should().Be(BodyType.Encodable); 98 | obj.TypeId 99 | .Should().Be(nodeid); 100 | } 101 | 102 | [Fact] 103 | public void CreateFromEncodableWithoutTypeId() 104 | { 105 | var body = new ReadRequest(); 106 | var obj = new ExtensionObject(body); 107 | 108 | obj.Body 109 | .Should().BeSameAs(body); 110 | obj.BodyType 111 | .Should().Be(BodyType.Encodable); 112 | obj.TypeId 113 | .Should().Be(ExpandedNodeId.Parse(ObjectIds.ReadRequest_Encoding_DefaultBinary)); 114 | } 115 | 116 | private class DataTypeWithoutEncodingId : Structure 117 | { 118 | public override void Decode(IDecoder decoder) 119 | { 120 | throw new NotImplementedException(); 121 | } 122 | 123 | public override void Encode(IEncoder encoder) 124 | { 125 | throw new NotImplementedException(); 126 | } 127 | } 128 | 129 | [Fact] 130 | public void CreateFromEncodableWithoutAnyTypeId() 131 | { 132 | var body = new DataTypeWithoutEncodingId(); 133 | 134 | body 135 | .Invoking(b => new ExtensionObject(b)) 136 | .Should().Throw() 137 | .Which.StatusCode 138 | .Should().Be((StatusCode)StatusCodes.BadDataEncodingUnsupported); 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /UaClient.UnitTests/UnitTests/IssuedIdentityTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using Workstation.ServiceModel.Ua; 6 | using Xunit; 7 | 8 | namespace Workstation.UaClient.UnitTests 9 | { 10 | public class IssuedIdentityTests 11 | { 12 | [InlineData(null)] 13 | [InlineData(new byte[] { })] 14 | [InlineData(new byte[] { 0x45, 0xff})] 15 | [Theory] 16 | public void Create(byte[] tokenData) 17 | { 18 | var id = new IssuedIdentity(tokenData); 19 | 20 | id.TokenData 21 | .Should().BeSameAs(tokenData); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /UaClient.UnitTests/UnitTests/LocalizedTextTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Workstation.ServiceModel.Ua; 8 | using Xunit; 9 | 10 | namespace Workstation.UaClient.UnitTests 11 | { 12 | public class LocalizedTextTests 13 | { 14 | public static IEnumerable> LocalizedTexts { get; } = new Func[] 15 | { 16 | () => new LocalizedText("un po' di testo", "it-IT"), 17 | () => new LocalizedText("some text", "en-US"), 18 | () => new LocalizedText("etwas Text", "de-DE"), 19 | () => new LocalizedText("some text"), 20 | () => new LocalizedText("some text", null), 21 | () => new LocalizedText("", "en-US"), 22 | () => new LocalizedText(null, "en-US"), 23 | () => new LocalizedText(null), 24 | () => new LocalizedText(null, null) 25 | }; 26 | 27 | public static IEnumerable ValueEqualityData => 28 | from a in LocalizedTexts.Select((f, i) => (text: f(), index: i)) 29 | from b in LocalizedTexts.Select((f, i) => (text: f(), index: i)) 30 | select new object[] { a.text, b.text, a.index == b.index }; 31 | 32 | public static IEnumerable ReferenceEqualityData 33 | { 34 | get 35 | { 36 | var list = LocalizedTexts.Select((f, i) => (text: f(), index: i)).ToList(); 37 | return from a in list 38 | from b in list 39 | select new object[] { a.text, b.text, a.index == b.index }; 40 | } 41 | } 42 | 43 | [MemberData(nameof(ValueEqualityData))] 44 | [MemberData(nameof(ReferenceEqualityData))] 45 | [Theory] 46 | public void Equality(LocalizedText a, LocalizedText b, bool shouldBeEqual) 47 | { 48 | if (shouldBeEqual) 49 | { 50 | // Should().Be() is using Equal(object) 51 | a 52 | .Should().Be(b); 53 | a 54 | .Should().NotBe(5); 55 | 56 | // Test Equal(LocalizableText) 57 | a.Equals(b) 58 | .Should().BeTrue(); 59 | 60 | // operator 61 | (a == b) 62 | .Should().BeTrue(); 63 | (a != b) 64 | .Should().BeFalse(); 65 | 66 | a.GetHashCode() 67 | .Should().Be(b.GetHashCode()); 68 | } 69 | else 70 | { 71 | // Should().Be() is using Equal(object) 72 | a 73 | .Should().NotBe(b); 74 | a 75 | .Should().NotBe(5); 76 | 77 | // Test Equal(LocalizableText) 78 | a.Equals(b) 79 | .Should().BeFalse(); 80 | 81 | // operator 82 | (a != b) 83 | .Should().BeTrue(); 84 | (a == b) 85 | .Should().BeFalse(); 86 | 87 | // This is technically not required but the current 88 | // implementation fulfills this. If this should ever 89 | // fail it could be bad luck or the the implementation 90 | // is really broken. 91 | a.GetHashCode() 92 | .Should().NotBe(b.GetHashCode()); 93 | } 94 | } 95 | 96 | public static IEnumerable EqualityNullData => 97 | LocalizedTexts.Select(id => new[] { id() }); 98 | 99 | [MemberData(nameof(EqualityNullData))] 100 | [Theory] 101 | public void EqualityNull(LocalizedText val) 102 | { 103 | (val == null) 104 | .Should().BeFalse(); 105 | (val != null) 106 | .Should().BeTrue(); 107 | (null == val) 108 | .Should().BeFalse(); 109 | (null != val) 110 | .Should().BeTrue(); 111 | 112 | // This is using Equals(object) 113 | val.Should() 114 | .NotBeNull(); 115 | 116 | val.Equals((LocalizedText)null) 117 | .Should().BeFalse(); 118 | } 119 | 120 | [InlineData("First text")] 121 | [InlineData("Second text")] 122 | [InlineData(null)] 123 | [Theory] 124 | public void ImplicitConversion(string val) 125 | { 126 | LocalizedText lt = val; 127 | 128 | lt.Text 129 | .Should().Be(val); 130 | lt.Locale 131 | .Should().Be(""); 132 | 133 | string txt2 = lt; 134 | 135 | txt2 136 | .Should().Be(val); 137 | } 138 | 139 | [Fact] 140 | public void ImplicitConversionNull() 141 | { 142 | string txt = (LocalizedText)null; 143 | txt 144 | .Should().Be(null); 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /UaClient.UnitTests/UnitTests/MonitoredItemAttributeTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using Workstation.ServiceModel.Ua; 6 | using Xunit; 7 | 8 | namespace Workstation.UaClient.UnitTests 9 | { 10 | public class MonitoredItemAttributeTests 11 | { 12 | [Fact] 13 | public void Create() 14 | { 15 | var att = new MonitoredItemAttribute("s=Hello"); 16 | 17 | att.AttributeId 18 | .Should().Be(AttributeIds.Value); 19 | att.DataChangeTrigger 20 | .Should().Be(DataChangeTrigger.StatusValue); 21 | att.DeadbandType 22 | .Should().Be(DeadbandType.None); 23 | att.DeadbandValue 24 | .Should().Be(0.0); 25 | att.DiscardOldest 26 | .Should().BeTrue(); 27 | att.IndexRange 28 | .Should().BeNull(); 29 | att.NodeId 30 | .Should().Be("s=Hello"); 31 | att.QueueSize 32 | .Should().Be(0); 33 | att.SamplingInterval 34 | .Should().Be(-1); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /UaClient.UnitTests/UnitTests/QualifiedNameTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Workstation.ServiceModel.Ua; 8 | using Xunit; 9 | 10 | namespace Workstation.UaClient.UnitTests 11 | { 12 | public class QualifiedNameTests 13 | { 14 | [Fact] 15 | public void Constructor() 16 | { 17 | var qn = new QualifiedName("TestName", 2); 18 | 19 | qn.Name 20 | .Should().Be("TestName"); 21 | 22 | qn.NamespaceIndex 23 | .Should().Be(2); 24 | } 25 | 26 | [Fact] 27 | public void ConstructorDefaultValues() 28 | { 29 | var qn = new QualifiedName("TestName"); 30 | 31 | qn.Name 32 | .Should().Be("TestName"); 33 | 34 | qn.NamespaceIndex 35 | .Should().Be(0); 36 | } 37 | 38 | public static IEnumerable> QualifiedNames { get; } = new Func[] 39 | { 40 | () => new QualifiedName(null), 41 | () => new QualifiedName("A"), 42 | () => new QualifiedName("B"), 43 | () => new QualifiedName("A", 2), 44 | () => new QualifiedName("B", 2), 45 | }; 46 | 47 | public static IEnumerable ValueEqualityData => 48 | from a in QualifiedNames.Select((f, i) => (id: f(), index: i)) 49 | from b in QualifiedNames.Select((f, i) => (id: f(), index: i)) 50 | select new object[] { a.id, b.id, a.index == b.index }; 51 | 52 | public static IEnumerable ReferenceEqualityData 53 | { 54 | get 55 | { 56 | var list = QualifiedNames.Select((f, i) => (id: f(), index: i)).ToList(); 57 | return from a in list 58 | from b in list 59 | select new object[] { a.id, b.id, a.index == b.index }; 60 | } 61 | } 62 | 63 | [MemberData(nameof(ValueEqualityData))] 64 | [MemberData(nameof(ReferenceEqualityData))] 65 | [Theory] 66 | public void Equality(QualifiedName a, QualifiedName b, bool shouldBeEqual) 67 | { 68 | if (shouldBeEqual) 69 | { 70 | // Should().Be() is using Equal(object) 71 | a 72 | .Should().Be(b); 73 | a 74 | .Should().NotBe(5); 75 | 76 | // Test Equal(QualifiedName) 77 | a.Equals(b) 78 | .Should().BeTrue(); 79 | 80 | // operator 81 | (a == b) 82 | .Should().BeTrue(); 83 | (a != b) 84 | .Should().BeFalse(); 85 | 86 | a.GetHashCode() 87 | .Should().Be(b.GetHashCode()); 88 | } 89 | else 90 | { 91 | // Should().Be() is using Equal(object) 92 | a 93 | .Should().NotBe(b); 94 | a 95 | .Should().NotBe(5); 96 | 97 | // Test Equal(QualifiedName) 98 | a.Equals(b) 99 | .Should().BeFalse(); 100 | 101 | // operator 102 | (a != b) 103 | .Should().BeTrue(); 104 | (a == b) 105 | .Should().BeFalse(); 106 | 107 | // This is technically not required but the current 108 | // implementation fulfills this. If this should ever 109 | // fail it could be bad luck or the the implementation 110 | // is really broken. 111 | a.GetHashCode() 112 | .Should().NotBe(b.GetHashCode()); 113 | } 114 | } 115 | 116 | public static IEnumerable EqualityNullData => 117 | QualifiedNames.Select(id => new[] { id() }); 118 | 119 | [MemberData(nameof(EqualityNullData))] 120 | [Theory] 121 | public void EqualityNull(QualifiedName val) 122 | { 123 | (val == null) 124 | .Should().BeFalse(); 125 | (val != null) 126 | .Should().BeTrue(); 127 | (null == val) 128 | .Should().BeFalse(); 129 | (null != val) 130 | .Should().BeTrue(); 131 | 132 | // This is using Equals(object) 133 | val.Should() 134 | .NotBeNull(); 135 | 136 | val.Equals((QualifiedName)null) 137 | .Should().BeFalse(); 138 | } 139 | 140 | [InlineData("0:ABC", 0, "ABC")] 141 | [InlineData("ABC", 0, "ABC")] 142 | [InlineData("2:ABC", 2, "ABC")] 143 | [Theory] 144 | public void Parse(string text, ushort ns, string name) 145 | { 146 | QualifiedName.Parse(text) 147 | .Should().Be(new QualifiedName(name, ns)); 148 | } 149 | 150 | [InlineData(null)] 151 | [InlineData("c:foo")] 152 | [InlineData(":foo")] 153 | [InlineData(" :foo")] 154 | [Theory] 155 | public void NotParsable(string text) 156 | { 157 | Action act = () => QualifiedName.Parse(text); 158 | act.Should().Throw(); 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /UaClient.UnitTests/UnitTests/TypeLibraryTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Xml.Linq; 8 | using Workstation.ServiceModel.Ua; 9 | using Xunit; 10 | 11 | namespace Workstation.UaClient.UnitTests 12 | { 13 | public class TypeLibraryTests 14 | { 15 | [Fact] 16 | public void FindBinaryEncodingIdByType() 17 | { 18 | TypeLibrary.TryGetBinaryEncodingIdFromType(typeof(ReadRequest), out ExpandedNodeId nodeid) 19 | .Should().BeTrue(); 20 | nodeid 21 | .Should().Be(ExpandedNodeId.Parse(ObjectIds.ReadRequest_Encoding_DefaultBinary)); 22 | } 23 | 24 | [Fact] 25 | public void FindTypeByBinaryEncodingId() 26 | { 27 | TypeLibrary.TryGetTypeFromBinaryEncodingId(ExpandedNodeId.Parse(ObjectIds.ReadRequest_Encoding_DefaultBinary), out Type type) 28 | .Should().BeTrue(); 29 | type 30 | .Should().Be(typeof(ReadRequest)); 31 | } 32 | 33 | [Fact] 34 | public void FindTypeByDataTypeId() 35 | { 36 | TypeLibrary.TryGetTypeFromDataTypeId(ExpandedNodeId.Parse("i=1"), out Type type) 37 | .Should().BeTrue(); 38 | type 39 | .Should().Be(typeof(Boolean)); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /UaClient.UnitTests/UnitTests/UaApplicationOptionsTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using Workstation.ServiceModel.Ua; 6 | using Xunit; 7 | 8 | namespace Workstation.UaClient.UnitTests 9 | { 10 | public class UaApplicationOptionsTests 11 | { 12 | [Fact] 13 | public void ClientTransportChannelOptionsDefaults() 14 | { 15 | var lowestBufferSize = 8192u; 16 | 17 | var options = new ClientTransportChannelOptions(); 18 | 19 | options.LocalReceiveBufferSize 20 | .Should().BeGreaterOrEqualTo(lowestBufferSize); 21 | options.LocalSendBufferSize 22 | .Should().BeGreaterOrEqualTo(lowestBufferSize); 23 | } 24 | 25 | [Fact] 26 | public void ClientSecureChannelOptionsDefaults() 27 | { 28 | var shortestTimespan = TimeSpan.FromMilliseconds(100); 29 | 30 | var options = new ClientSecureChannelOptions(); 31 | 32 | TimeSpan.FromMilliseconds(options.TimeoutHint) 33 | .Should().BeGreaterOrEqualTo(shortestTimespan); 34 | 35 | options.DiagnosticsHint 36 | .Should().Be(0); 37 | } 38 | 39 | [Fact] 40 | public void ClientSessionChannelOptionsDefaults() 41 | { 42 | var shortestTimespan = TimeSpan.FromMilliseconds(100); 43 | 44 | var options = new ClientSessionChannelOptions(); 45 | 46 | TimeSpan.FromMilliseconds(options.SessionTimeout) 47 | .Should().BeGreaterOrEqualTo(shortestTimespan); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /UaClient.UnitTests/UnitTests/UserNameIdentityTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using Workstation.ServiceModel.Ua; 6 | using Xunit; 7 | 8 | namespace Workstation.UaClient.UnitTests 9 | { 10 | public class UserNameIdentityTests 11 | { 12 | [InlineData("", "")] 13 | [InlineData("UserName", "Password")] 14 | [InlineData(null, "Password")] 15 | [InlineData("UserName", null)] 16 | [Theory] 17 | public void Create(string userName, string password) 18 | { 19 | var user = new UserNameIdentity(userName, password); 20 | 21 | user.UserName 22 | .Should().Be(userName); 23 | user.Password 24 | .Should().Be(password); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /UaClient.UnitTests/UnitTests/X509IdentityTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Org.BouncyCastle.Asn1.X509; 3 | using Org.BouncyCastle.Crypto.Parameters; 4 | using Org.BouncyCastle.Math; 5 | using Org.BouncyCastle.X509; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Text; 9 | using Workstation.ServiceModel.Ua; 10 | using Xunit; 11 | 12 | namespace Workstation.UaClient.UnitTests 13 | { 14 | public class X509IdentityTests 15 | { 16 | [Fact] 17 | public void CreateNull() 18 | { 19 | var id = new X509Identity(null, null); 20 | 21 | id.Certificate 22 | .Should().BeNull(); 23 | id.PrivateKey 24 | .Should().BeNull(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /UaClient.UnitTests/UnitTests/XmlEncodingIdAttributeTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using Workstation.ServiceModel.Ua; 6 | using Xunit; 7 | 8 | namespace Workstation.UaClient.UnitTests 9 | { 10 | public class XmlEncodingIdAttributeTests 11 | { 12 | public static IEnumerable CreateData => ExpandedNodeIdTests.ParseData; 13 | 14 | [MemberData(nameof(CreateData))] 15 | [Theory] 16 | public void Create(string s, ExpandedNodeId id) 17 | { 18 | var att = new XmlEncodingIdAttribute(s); 19 | 20 | att.NodeId 21 | .Should().Be(id); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /UaClient.UnitTests/Workstation.UaClient.UnitTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0-windows 5 | Workstation.UaClient.UnitTests 6 | Workstation.UaClient 7 | 2.0.0 8 | 2.0.0.0 9 | true 10 | true 11 | latest 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | PreserveNewest 21 | 22 | 23 | 24 | 25 | 26 | runtime; build; native; contentfiles; analyzers; buildtransitive 27 | all 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | all 38 | runtime; build; native; contentfiles; analyzers; buildtransitive 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /UaClient.UnitTests/appSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "MappedEndpoints": [ 3 | { 4 | "RequestedUrl": "opc.tcp://localhost:26543", 5 | "Endpoint": { 6 | "EndpointUrl": "opc.tcp://localhost:26543", 7 | "SecurityPolicyUri": "http://opcfoundation.org/UA/SecurityPolicy#None" 8 | } 9 | }, 10 | { 11 | "RequestedUrl": "opc.tcp://localhost:48010", 12 | "Endpoint": { 13 | "EndpointUrl": "opc.tcp://localhost:48010", 14 | "SecurityPolicyUri": "http://opcfoundation.org/UA/SecurityPolicy#None" 15 | } 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /UaClient.UnitTests/coverlet.runsettings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | json,cobertura 8 | ObsoleteAttribute,GeneratedCodeAttribute 9 | **\*.generated.cs 10 | false 11 | true 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /UaClient/Collections/ErrorsContainer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace Workstation.Collections 9 | { 10 | /// 11 | /// Manages validation errors for an object, notifying when the error state changes. 12 | /// 13 | /// The type of the error object. 14 | public class ErrorsContainer 15 | { 16 | private static readonly T[] _noErrors = Array.Empty(); 17 | private readonly Action _raiseErrorsChanged; 18 | private readonly Dictionary> _validationResults; 19 | 20 | /// 21 | /// Initializes a new instance of the class. 22 | /// 23 | /// The action that invoked if when errors are added for an object./> 24 | /// event. 25 | public ErrorsContainer(Action raiseErrorsChanged) 26 | { 27 | 28 | if (raiseErrorsChanged == null) 29 | { 30 | throw new ArgumentNullException(nameof(raiseErrorsChanged)); 31 | } 32 | 33 | _raiseErrorsChanged = raiseErrorsChanged; 34 | _validationResults = new Dictionary>(); 35 | } 36 | 37 | /// 38 | /// Gets a value indicating whether the object has validation errors. 39 | /// 40 | public bool HasErrors 41 | { 42 | get 43 | { 44 | return _validationResults.Count != 0; 45 | } 46 | } 47 | 48 | /// 49 | /// Gets the validation errors for a specified property. 50 | /// 51 | /// The name of the property. 52 | /// The validation errors of type for the property. 53 | public IEnumerable GetErrors(string? propertyName) 54 | { 55 | var localPropertyName = propertyName ?? string.Empty; 56 | List? currentValidationResults = null; 57 | if (_validationResults.TryGetValue(localPropertyName, out currentValidationResults)) 58 | { 59 | return currentValidationResults; 60 | } 61 | else 62 | { 63 | return _noErrors; 64 | } 65 | } 66 | 67 | /// 68 | /// Clears the errors for a property. 69 | /// 70 | /// The name of th property for which to clear errors. 71 | /// 72 | /// container.ClearErrors("SomeProperty"); 73 | /// 74 | public void ClearErrors(string propertyName) 75 | { 76 | SetErrors(propertyName, new List()); 77 | } 78 | 79 | /// 80 | /// Sets the validation errors for the specified property. 81 | /// 82 | /// 83 | /// If a change is detected then the errors changed event is raised. 84 | /// 85 | /// The name of the property. 86 | /// The new validation errors. 87 | public void SetErrors(string propertyName, IEnumerable? newValidationResults) 88 | { 89 | var localPropertyName = propertyName ?? string.Empty; 90 | var hasCurrentValidationResults = _validationResults.ContainsKey(localPropertyName); 91 | var hasNewValidationResults = newValidationResults != null && newValidationResults.Count() > 0; 92 | 93 | if (hasCurrentValidationResults || hasNewValidationResults) 94 | { 95 | if (hasNewValidationResults) 96 | { 97 | _validationResults[localPropertyName] = new List(newValidationResults!); 98 | _raiseErrorsChanged(localPropertyName); 99 | } 100 | else 101 | { 102 | _validationResults.Remove(localPropertyName); 103 | _raiseErrorsChanged(localPropertyName); 104 | } 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /UaClient/Collections/ObservableQueue.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Collections.Generic; 5 | using System.Collections.Specialized; 6 | using System.ComponentModel; 7 | 8 | namespace Workstation.Collections 9 | { 10 | /// 11 | /// Represents a first-in, first-out collection that implements INotifyCollectionChanged. 12 | /// 13 | /// Type of element. 14 | public class ObservableQueue : Queue, INotifyCollectionChanged, INotifyPropertyChanged 15 | { 16 | private readonly int _capacity; 17 | private readonly bool _isFixedSize; 18 | 19 | /// 20 | /// Initializes a new instance of the class. 21 | /// 22 | public ObservableQueue() 23 | { 24 | } 25 | 26 | /// 27 | /// Initializes a new instance of the class. 28 | /// 29 | /// The number of elements that the queue can initially store. 30 | /// If true, older elements are discarded. 31 | public ObservableQueue(int capacity, bool isFixedSize = false) 32 | : base(capacity) 33 | { 34 | _capacity = capacity; 35 | _isFixedSize = isFixedSize; 36 | } 37 | 38 | /// 39 | /// Occurs when an item is added, removed, changed, moved, or the entire list is refreshed. 40 | /// 41 | public event NotifyCollectionChangedEventHandler? CollectionChanged; 42 | 43 | /// 44 | /// Occurs when a property value changes. 45 | /// 46 | public event PropertyChangedEventHandler? PropertyChanged; 47 | 48 | /// 49 | /// Removes all objects from the queue. 50 | /// 51 | public new void Clear() 52 | { 53 | if (Count == 0) 54 | { 55 | return; 56 | } 57 | 58 | base.Clear(); 59 | OnPropertyChanged("Count"); 60 | OnPropertyChanged("Item[]"); 61 | OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); 62 | } 63 | 64 | /// 65 | /// Removes and returns the object at the beginning of the queue. 66 | /// 67 | /// The object that is removed from the beginning of the queue. 68 | public new T Dequeue() 69 | { 70 | var item = base.Dequeue(); 71 | OnPropertyChanged("Count"); 72 | OnPropertyChanged("Item[]"); 73 | OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, 0)); 74 | return item; 75 | } 76 | 77 | /// 78 | /// Adds an object to the end of the queue. 79 | /// 80 | /// The object to add to the queue. 81 | public new void Enqueue(T item) 82 | { 83 | if (_isFixedSize && _capacity > 0) 84 | { 85 | while (Count >= _capacity) 86 | { 87 | Dequeue(); 88 | } 89 | } 90 | 91 | base.Enqueue(item); 92 | OnPropertyChanged("Count"); 93 | OnPropertyChanged("Item[]"); 94 | OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, Count - 1)); 95 | } 96 | 97 | /// 98 | /// Raises the CollectionChanged event with the provided arguments. 99 | /// 100 | /// Arguments of the event being raised. 101 | protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) 102 | { 103 | CollectionChanged?.Invoke(this, e); 104 | } 105 | 106 | /// 107 | /// Raises the PropertyChanged event with the provided arguments. 108 | /// 109 | /// Arguments of the event being raised. 110 | protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) 111 | { 112 | PropertyChanged?.Invoke(this, e); 113 | } 114 | 115 | private void OnPropertyChanged(string propertyName) 116 | { 117 | OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); 118 | } 119 | } 120 | } -------------------------------------------------------------------------------- /UaClient/Internal/System.Diagnostics.CodeAnalysis.cs: -------------------------------------------------------------------------------- 1 | // taken from https://github.com/dotnet/standard/blob/master/src/netstandard/ref/System.Diagnostics.CodeAnalysis.cs 2 | 3 | // Licensed to the .NET Foundation under one or more agreements. 4 | // The .NET Foundation licenses this file to you under the MIT license. 5 | // See the LICENSE file in the project root for more information. 6 | 7 | #if NETSTANDARD2_0 8 | namespace System.Diagnostics.CodeAnalysis 9 | { 10 | [System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.Parameter | System.AttributeTargets.Property, Inherited = false)] 11 | internal sealed partial class AllowNullAttribute : System.Attribute 12 | { 13 | public AllowNullAttribute() { } 14 | } 15 | [System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.Parameter | System.AttributeTargets.Property, Inherited = false)] 16 | internal sealed partial class DisallowNullAttribute : System.Attribute 17 | { 18 | public DisallowNullAttribute() { } 19 | } 20 | [System.AttributeUsageAttribute(System.AttributeTargets.Method, Inherited = false)] 21 | internal sealed partial class DoesNotReturnAttribute : System.Attribute 22 | { 23 | public DoesNotReturnAttribute() { } 24 | } 25 | [System.AttributeUsageAttribute(System.AttributeTargets.Parameter, Inherited = false)] 26 | internal sealed partial class DoesNotReturnIfAttribute : System.Attribute 27 | { 28 | public DoesNotReturnIfAttribute(bool parameterValue) { } 29 | public bool ParameterValue { get { throw null!; } } 30 | } 31 | [System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.Parameter | System.AttributeTargets.Property | System.AttributeTargets.ReturnValue, Inherited = false)] 32 | internal sealed partial class MaybeNullAttribute : System.Attribute 33 | { 34 | public MaybeNullAttribute() { } 35 | } 36 | [System.AttributeUsageAttribute(System.AttributeTargets.Parameter, Inherited = false)] 37 | internal sealed partial class MaybeNullWhenAttribute : System.Attribute 38 | { 39 | public MaybeNullWhenAttribute(bool returnValue) { } 40 | public bool ReturnValue { get { throw null!; } } 41 | } 42 | [System.AttributeUsageAttribute(System.AttributeTargets.Field | System.AttributeTargets.Parameter | System.AttributeTargets.Property | System.AttributeTargets.ReturnValue, Inherited = false)] 43 | internal sealed partial class NotNullAttribute : System.Attribute 44 | { 45 | public NotNullAttribute() { } 46 | } 47 | [System.AttributeUsageAttribute(System.AttributeTargets.Parameter | System.AttributeTargets.Property | System.AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] 48 | internal sealed partial class NotNullIfNotNullAttribute : System.Attribute 49 | { 50 | public NotNullIfNotNullAttribute(string parameterName) { } 51 | public string ParameterName { get { throw null!; } } 52 | } 53 | [System.AttributeUsageAttribute(System.AttributeTargets.Parameter, Inherited = false)] 54 | internal sealed partial class NotNullWhenAttribute : System.Attribute 55 | { 56 | public NotNullWhenAttribute(bool returnValue) { } 57 | public bool ReturnValue { get { throw null!; } } 58 | } 59 | } 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /UaClient/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convertersystems/opc-ua-client/10948088eecf3dc38a484e0a8fc5153cf3c2ed21/UaClient/Key.snk -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/AccessLevelFlags.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | 6 | namespace Workstation.ServiceModel.Ua 7 | { 8 | [Flags] 9 | public enum AccessLevelFlags : byte 10 | { 11 | None = 0, 12 | CurrentRead = 1, 13 | CurrentWrite = 2, 14 | HistoryRead = 4, 15 | HistoryWrite = 8, 16 | SemanticChange = 16, 17 | StatusWrite = 32, 18 | TimestampWrite = 64, 19 | } 20 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/AcknowledgeableCondition.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace Workstation.ServiceModel.Ua 8 | { 9 | /// 10 | /// Represents an acknowledgeable condition. 11 | /// 12 | public class AcknowledgeableCondition : Condition 13 | { 14 | [EventField(typeDefinitionId: ObjectTypeIds.AcknowledgeableConditionType, browsePath: "AckedState/Id")] 15 | public bool? AckedState { get; set; } 16 | 17 | [EventField(typeDefinitionId: ObjectTypeIds.AcknowledgeableConditionType, browsePath: "ConfirmedState/Id")] 18 | public bool? ConfirmedState { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/AlarmCondition.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace Workstation.ServiceModel.Ua 8 | { 9 | /// 10 | /// Represents an alarm condition. 11 | /// 12 | public class AlarmCondition : AcknowledgeableCondition 13 | { 14 | [EventField(typeDefinitionId: ObjectTypeIds.AlarmConditionType, browsePath: "ActiveState/Id")] 15 | public bool? ActiveState { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/AnonymousIdentity.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Workstation.ServiceModel.Ua 5 | { 6 | public class AnonymousIdentity : IUserIdentity 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/AttributeIds.generated.cs: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version: 15.0.0.0 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | // ------------------------------------------------------------------------------ 10 | namespace Workstation.ServiceModel.Ua 11 | { 12 | public static class AttributeIds 13 | { 14 | public const uint NodeId = 1u; 15 | public const uint NodeClass = 2u; 16 | public const uint BrowseName = 3u; 17 | public const uint DisplayName = 4u; 18 | public const uint Description = 5u; 19 | public const uint WriteMask = 6u; 20 | public const uint UserWriteMask = 7u; 21 | public const uint IsAbstract = 8u; 22 | public const uint Symmetric = 9u; 23 | public const uint InverseName = 10u; 24 | public const uint ContainsNoLoops = 11u; 25 | public const uint EventNotifier = 12u; 26 | public const uint Value = 13u; 27 | public const uint DataType = 14u; 28 | public const uint ValueRank = 15u; 29 | public const uint ArrayDimensions = 16u; 30 | public const uint AccessLevel = 17u; 31 | public const uint UserAccessLevel = 18u; 32 | public const uint MinimumSamplingInterval = 19u; 33 | public const uint Historizing = 20u; 34 | public const uint Executable = 21u; 35 | public const uint UserExecutable = 22u; 36 | public const uint DataTypeDefinition = 23u; 37 | public const uint RolePermissions = 24u; 38 | public const uint UserRolePermissions = 25u; 39 | public const uint AccessRestrictions = 26u; 40 | public const uint AccessLevelEx = 27u; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/AttributeIds.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="true" hostspecific="true" language="C#" 2 | #><#@ assembly name="System.Core" 3 | #><#@ import namespace="System.Linq" 4 | #><#@ import namespace="System.Text" 5 | #><#@ import namespace="System.Collections.Generic" 6 | #><#@ assembly name="System.Xml.dll" 7 | #><#@ import namespace="System.Xml" 8 | #><#@ import namespace="System.IO" 9 | #><#@ output extension="generated.cs" 10 | #><# 11 | var filename = this.Host.ResolvePath(@"Schema\AttributeIds.csv"); 12 | var comma = new[] { ',' }; 13 | var fields = 14 | from l in File.ReadAllLines(filename) 15 | let s = l.Split(comma, 2) 16 | select new { Name = s[0], Value = s[1] }; 17 | #>// ------------------------------------------------------------------------------ 18 | // 19 | // This code was generated by a tool. 20 | // Runtime Version: 15.0.0.0 21 | // 22 | // Changes to this file may cause incorrect behavior and will be lost if 23 | // the code is regenerated. 24 | // 25 | // ------------------------------------------------------------------------------ 26 | namespace Workstation.ServiceModel.Ua 27 | { 28 | public static class AttributeIds 29 | { 30 | <# foreach (var f in fields) { #> public const uint <#= f.Name #> = <#= f.Value #>u; 31 | <# } #> } 32 | } 33 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/AttributeServiceSet.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Workstation.ServiceModel.Ua 9 | { 10 | public static class AttributeServiceSet 11 | { 12 | /// 13 | /// Reads a list of Node attributes. 14 | /// 15 | /// A instance of . 16 | /// A . 17 | /// A representing the asynchronous operation that returns a . 18 | /// OPC UA specification Part 4: Services, 5.10.2 19 | public static async Task ReadAsync(this IRequestChannel client, ReadRequest request, CancellationToken token = default) 20 | { 21 | if (request == null) 22 | { 23 | throw new ArgumentNullException(nameof(request)); 24 | } 25 | 26 | return (ReadResponse)await client.RequestAsync(request, token).ConfigureAwait(false); 27 | } 28 | 29 | /// 30 | /// Writes a list of Node attributes. 31 | /// 32 | /// A instance of . 33 | /// A . 34 | /// A representing the asynchronous operation that returns a . 35 | /// OPC UA specification Part 4: Services, 5.10.4 36 | public static async Task WriteAsync(this IRequestChannel client, WriteRequest request, CancellationToken token = default) 37 | { 38 | if (request == null) 39 | { 40 | throw new ArgumentNullException(nameof(request)); 41 | } 42 | 43 | return (WriteResponse)await client.RequestAsync(request, token).ConfigureAwait(false); 44 | } 45 | 46 | /// 47 | /// Reads historical values or Events of one or more Nodes. 48 | /// 49 | /// A instance of . 50 | /// A . 51 | /// A representing the asynchronous operation that returns a . 52 | /// OPC UA specification Part 4: Services, 5.10.3 53 | public static async Task HistoryReadAsync(this IRequestChannel client, HistoryReadRequest request, CancellationToken token = default) 54 | { 55 | if (request == null) 56 | { 57 | throw new ArgumentNullException(nameof(request)); 58 | } 59 | 60 | return (HistoryReadResponse)await client.RequestAsync(request, token).ConfigureAwait(false); 61 | } 62 | 63 | /// 64 | /// Updates historical values or Events of one or more Nodes. 65 | /// 66 | /// A instance of . 67 | /// A . 68 | /// A representing the asynchronous operation that returns a . 69 | /// OPC UA specification Part 4: Services, 5.10.5 70 | public static async Task HistoryUpdateAsync(this IRequestChannel client, HistoryUpdateRequest request, CancellationToken token = default) 71 | { 72 | if (request == null) 73 | { 74 | throw new ArgumentNullException(nameof(request)); 75 | } 76 | 77 | return (HistoryUpdateResponse)await client.RequestAsync(request, token).ConfigureAwait(false); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/BaseEvent.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace Workstation.ServiceModel.Ua 8 | { 9 | /// 10 | /// Represents an event. 11 | /// 12 | public class BaseEvent 13 | { 14 | [EventField(typeDefinitionId: ObjectTypeIds.BaseEventType, browsePath: "EventId")] 15 | public byte[]? EventId { get; set; } 16 | 17 | [EventField(typeDefinitionId: ObjectTypeIds.BaseEventType, browsePath: "EventType")] 18 | public NodeId? EventType { get; set; } 19 | 20 | [EventField(typeDefinitionId: ObjectTypeIds.BaseEventType, browsePath: "SourceName")] 21 | public string? SourceName { get; set; } 22 | 23 | [EventField(typeDefinitionId: ObjectTypeIds.BaseEventType, browsePath: "Time")] 24 | public DateTime Time { get; set; } 25 | 26 | [EventField(typeDefinitionId: ObjectTypeIds.BaseEventType, browsePath: "Message")] 27 | public LocalizedText? Message { get; set; } 28 | 29 | [EventField(typeDefinitionId: ObjectTypeIds.BaseEventType, browsePath: "Severity")] 30 | public ushort Severity { get; set; } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/BinaryEncodingIdAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | 6 | namespace Workstation.ServiceModel.Ua 7 | { 8 | /// 9 | /// Attribute for classes of type IEncodable to indicate the binary encoding id. 10 | /// 11 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] 12 | public sealed class BinaryEncodingIdAttribute : Attribute 13 | { 14 | public BinaryEncodingIdAttribute(string s) 15 | { 16 | this.NodeId = ExpandedNodeId.Parse(s); 17 | } 18 | 19 | public ExpandedNodeId NodeId { get; } 20 | } 21 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/ByteSequenceComparer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace Workstation.ServiceModel.Ua 8 | { 9 | public sealed class ByteSequenceComparer : IEqualityComparer 10 | { 11 | public static readonly ByteSequenceComparer Instance = new ByteSequenceComparer(); 12 | 13 | private ByteSequenceComparer() 14 | { 15 | } 16 | 17 | public static bool Equals(byte[]? left, byte[]? right) 18 | { 19 | if (ReferenceEquals(left, right)) 20 | { 21 | return true; 22 | } 23 | 24 | if (left == null || right == null || left.Length != right.Length) 25 | { 26 | return false; 27 | } 28 | 29 | for (var i = 0; i < left.Length; i++) 30 | { 31 | if (left[i] != right[i]) 32 | { 33 | return false; 34 | } 35 | } 36 | 37 | return true; 38 | } 39 | 40 | public static int GetHashCode(byte[] x) 41 | { 42 | // Compute the FNV-1a hash of a sequence of bytes 43 | // See http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function 44 | int hashCode = unchecked((int)2166136261); 45 | 46 | for (int i = 0; i < x.Length; i++) 47 | { 48 | hashCode = unchecked((hashCode ^ x[i]) * 16777619); 49 | } 50 | 51 | return hashCode; 52 | } 53 | 54 | bool IEqualityComparer.Equals(byte[]? x, byte[]? y) 55 | { 56 | return Equals(x, y); 57 | } 58 | 59 | int IEqualityComparer.GetHashCode(byte[] x) 60 | { 61 | return GetHashCode(x); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/Channels/BinaryEncodingProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.IO; 5 | 6 | namespace Workstation.ServiceModel.Ua.Channels 7 | { 8 | /// 9 | /// The interface implementation 10 | /// for the OPC UA Binary DataEncoding. 11 | /// 12 | /// OPC UA specification Part 6: Mappings, 5.2.1 13 | public class BinaryEncodingProvider : IEncodingProvider 14 | { 15 | /// 16 | public IDecoder CreateDecoder(Stream stream, IEncodingContext? context, bool keepStreamOpen) 17 | { 18 | return new BinaryDecoder(stream, context, keepStreamOpen); 19 | } 20 | 21 | /// 22 | public IEncoder CreateEncoder(Stream stream, IEncodingContext? context, bool keepStreamOpen) 23 | { 24 | return new BinaryEncoder(stream, context, keepStreamOpen); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/Channels/EncodingContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Workstation.ServiceModel.Ua.Channels 6 | { 7 | public interface IEncodingContext 8 | { 9 | IReadOnlyList NamespaceUris { get; } 10 | IReadOnlyList ServerUris { get; } 11 | int MaxStringLength { get; } 12 | int MaxArrayLength { get; } 13 | int MaxByteStringLength { get; } 14 | } 15 | 16 | public class DefaultEncodingContext : IEncodingContext 17 | { 18 | public IReadOnlyList NamespaceUris => new List { "http://opcfoundation.org/UA/" }; 19 | 20 | public IReadOnlyList ServerUris => new List(); 21 | 22 | public int MaxStringLength => 65535; 23 | 24 | public int MaxArrayLength => 65535; 25 | 26 | public int MaxByteStringLength => 65535; 27 | 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/Channels/MessageTypes.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Workstation.ServiceModel.Ua 5 | { 6 | public static class MessageTypes 7 | { 8 | public const uint HELF = 'H' | 'E' << 8 | 'L' << 16 | 'F' << 24; 9 | public const uint ACKF = 'A' | 'C' << 8 | 'K' << 16 | 'F' << 24; 10 | public const uint ERRF = 'E' | 'R' << 8 | 'R' << 16 | 'F' << 24; 11 | public const uint RHEF = 'R' | 'H' << 8 | 'E' << 16 | 'F' << 24; 12 | public const uint OPNF = 'O' | 'P' << 8 | 'N' << 16 | 'F' << 24; 13 | public const uint CLOF = 'C' | 'L' << 8 | 'O' << 16 | 'F' << 24; 14 | public const uint MSGF = 'M' | 'S' << 8 | 'G' << 16 | 'F' << 24; 15 | public const uint MSGC = 'M' | 'S' << 8 | 'G' << 16 | 'C' << 24; 16 | public const uint MSGA = 'M' | 'S' << 8 | 'G' << 16 | 'A' << 24; 17 | } 18 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/Channels/ServiceOperation.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Threading.Tasks; 5 | 6 | namespace Workstation.ServiceModel.Ua.Channels 7 | { 8 | public class ServiceOperation : TaskCompletionSource 9 | { 10 | public ServiceOperation(IServiceRequest request) 11 | #if NET45 12 | : base(request) 13 | #else 14 | : base(request, TaskCreationOptions.RunContinuationsAsynchronously) 15 | #endif 16 | { 17 | } 18 | 19 | /// 20 | /// Gets the request. 21 | /// 22 | public IServiceRequest Request => (IServiceRequest)this.Task.AsyncState!; 23 | } 24 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/Channels/StackProfile.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Workstation.ServiceModel.Ua.Channels 5 | { 6 | /// 7 | /// The stack profile class. 8 | /// 9 | /// OPC UA specification Part 6: Mappings, 4 10 | public class StackProfile 11 | { 12 | /// 13 | /// The transport connection provider. 14 | /// 15 | public ITransportConnectionProvider TransportConnectionProvider { get; } 16 | 17 | /// 18 | /// The conversation provider. 19 | /// 20 | public IConversationProvider ConversationProvider { get; } 21 | 22 | /// 23 | /// The encoding provider. 24 | /// 25 | public IEncodingProvider EncodingProvider { get; } 26 | 27 | /// 28 | /// Creates a stack profile instance. 29 | /// 30 | /// The transport connection provider. 31 | /// The conversation provider. 32 | /// The encoding provider. 33 | public StackProfile(ITransportConnectionProvider transportConnectionProvider, IConversationProvider conversationProvider, IEncodingProvider encodingProvider) 34 | { 35 | TransportConnectionProvider = transportConnectionProvider; 36 | ConversationProvider = conversationProvider; 37 | EncodingProvider = encodingProvider; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/Channels/StackProfiles.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | 6 | namespace Workstation.ServiceModel.Ua.Channels 7 | { 8 | public static class StackProfiles 9 | { 10 | /// 11 | /// A stack profile for the UA-TCP UA-SC UA-Binary profile 12 | /// (http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary). 13 | /// 14 | public static StackProfile TcpUascBinary { get; } 15 | = new StackProfile( 16 | new UaTcpConnectionProvider(), 17 | new UaSecureConversationProvider(), 18 | new BinaryEncodingProvider() 19 | ); 20 | 21 | /// 22 | /// Get the for the given endpoint. 23 | /// 24 | /// 25 | /// If no ´matching stack is found, will 26 | /// be returned. 27 | /// 28 | /// The endpoint. 29 | /// A matching stack. 30 | public static StackProfile GetStackProfile(EndpointDescription remoteEndpoint) 31 | { 32 | switch (remoteEndpoint.TransportProfileUri) 33 | { 34 | case TransportProfileUris.UaTcpTransport: 35 | return TcpUascBinary; 36 | // Use TcpUascBinary as fallback, or should we throw here? 37 | default: 38 | return TcpUascBinary; 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/Channels/UaSecureConversationProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.Extensions.Logging; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Workstation.ServiceModel.Ua.Channels 9 | { 10 | /// 11 | /// The interface implementation 12 | /// for the OPC UA Secure Conversation (UASC). 13 | /// 14 | /// OPC UA specification Part 6: Mappings, 7.2 15 | public class UaSecureConversationProvider : IConversationProvider 16 | { 17 | /// 18 | public async Task CreateAsync(EndpointDescription remoteEndpoint, ApplicationDescription localDescription, TransportConnectionOptions options, ICertificateStore? certificateStore, ILogger? logger, CancellationToken token) 19 | { 20 | var conversation = new UaSecureConversation(localDescription, options, certificateStore, logger) 21 | { 22 | SecurityMode = remoteEndpoint.SecurityMode 23 | }; 24 | 25 | await conversation.SetRemoteCertificateAsync(remoteEndpoint.SecurityPolicyUri, remoteEndpoint.ServerCertificate, token).ConfigureAwait(false); 26 | 27 | return conversation; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/Channels/UaTcpConnectionProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Net.Sockets; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace Workstation.ServiceModel.Ua.Channels 10 | { 11 | /// 12 | /// The interface implementation 13 | /// for the OPC UA TCP transport protocol. 14 | /// 15 | /// OPC UA specification Part 6: Mappings, 7.2 16 | public class UaTcpConnectionProvider : ITransportConnectionProvider 17 | { 18 | private const int ConnectTimeout = 5000; 19 | 20 | /// 21 | public async Task ConnectAsync(string connectionString, CancellationToken token) 22 | { 23 | var uri = new Uri(connectionString); 24 | var client = new TcpClient 25 | { 26 | NoDelay = true 27 | }; 28 | 29 | await client.ConnectAsync(uri.Host, uri.Port).TimeoutAfter(ConnectTimeout, token).ConfigureAwait(false); 30 | 31 | // The stream will own the client and takes care on disposing/closing it 32 | return new UaClientConnection(client.GetStream(), uri); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/CommunicationsState.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Workstation.ServiceModel.Ua 5 | { 6 | /// Defines the states in which an can exist. 7 | public enum CommunicationState 8 | { 9 | /// Indicates that the communication object has been instantiated and is configurable, but not yet open or ready for use. 10 | Created, 11 | 12 | /// Indicates that the communication object is being transitioned to the opened state. 13 | Opening, 14 | 15 | /// Indicates that the communication object is now open and ready to be used. 16 | Opened, 17 | 18 | /// Indicates that the communication object is transitioning to the closed state. 19 | Closing, 20 | 21 | /// Indicates that the communication object has been closed and is no longer usable. 22 | Closed, 23 | 24 | /// Indicates that the communication object has encountered an error or fault from which it cannot recover and from which it is no longer usable. 25 | Faulted 26 | } 27 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/Condition.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace Workstation.ServiceModel.Ua 8 | { 9 | /// 10 | /// Represents a condition. 11 | /// 12 | public class Condition : BaseEvent 13 | { 14 | [EventField(typeDefinitionId: ObjectTypeIds.ConditionType, attributeId: AttributeIds.NodeId)] 15 | public NodeId? ConditionId { get; set; } 16 | 17 | [EventField(typeDefinitionId: ObjectTypeIds.ConditionType, browsePath: "ConditionName")] 18 | public string? ConditionName { get; set; } 19 | 20 | [EventField(typeDefinitionId: ObjectTypeIds.ConditionType, browsePath: "BranchId")] 21 | public NodeId? BranchId { get; set; } 22 | 23 | [EventField(typeDefinitionId: ObjectTypeIds.ConditionType, browsePath: "Retain")] 24 | public bool? Retain { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/DataTypeIdAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | 6 | namespace Workstation.ServiceModel.Ua 7 | { 8 | /// 9 | /// Attribute for classes to indicate the data type id. 10 | /// 11 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Struct, AllowMultiple = false, Inherited = true)] 12 | public sealed class DataTypeIdAttribute : Attribute 13 | { 14 | public DataTypeIdAttribute(string s) 15 | { 16 | this.NodeId = ExpandedNodeId.Parse(s); 17 | } 18 | 19 | public ExpandedNodeId NodeId { get; } 20 | } 21 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/DataValue.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | 6 | namespace Workstation.ServiceModel.Ua 7 | { 8 | [DataTypeId(DataTypeIds.DataValue)] 9 | public sealed class DataValue 10 | { 11 | public DataValue(object? value, StatusCode statusCode = default(StatusCode), DateTime sourceTimestamp = default(DateTime), ushort sourcePicoseconds = 0, DateTime serverTimestamp = default(DateTime), ushort serverPicoseconds = 0) 12 | { 13 | this.Variant = new Variant(value); 14 | this.StatusCode = statusCode; 15 | this.SourceTimestamp = sourceTimestamp; 16 | this.SourcePicoseconds = sourcePicoseconds; 17 | this.ServerTimestamp = serverTimestamp; 18 | this.ServerPicoseconds = serverPicoseconds; 19 | } 20 | 21 | public DataValue(Variant value, StatusCode statusCode = default(StatusCode), DateTime sourceTimestamp = default(DateTime), ushort sourcePicoseconds = 0, DateTime serverTimestamp = default(DateTime), ushort serverPicoseconds = 0) 22 | { 23 | this.Variant = value; 24 | this.StatusCode = statusCode; 25 | this.SourceTimestamp = sourceTimestamp; 26 | this.SourcePicoseconds = sourcePicoseconds; 27 | this.ServerTimestamp = serverTimestamp; 28 | this.ServerPicoseconds = serverPicoseconds; 29 | } 30 | 31 | public object? Value 32 | { 33 | get { return this.Variant.Value; } 34 | } 35 | 36 | public StatusCode StatusCode { get; } 37 | 38 | public DateTime SourceTimestamp { get; } 39 | 40 | public ushort SourcePicoseconds { get; } 41 | 42 | public DateTime ServerTimestamp { get; } 43 | 44 | public ushort ServerPicoseconds { get; } 45 | 46 | public Variant Variant { get; } 47 | 48 | public override string ToString() 49 | { 50 | return $"{this.Value}; status: {this.StatusCode}; ts: {this.SourceTimestamp}"; 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/DataValueExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Diagnostics.CodeAnalysis; 6 | using System.Linq; 7 | 8 | namespace Workstation.ServiceModel.Ua 9 | { 10 | public static class DataValueExtensions 11 | { 12 | /// 13 | /// Gets the value of the DataValue. 14 | /// 15 | /// The DataValue. 16 | /// The value. 17 | public static object? GetValue(this DataValue dataValue) 18 | { 19 | var value = dataValue.Value; 20 | switch (value) 21 | { 22 | case ExtensionObject obj: 23 | 24 | return obj.BodyType == BodyType.Encodable ? obj.Body : obj; 25 | 26 | case ExtensionObject[] objArray: 27 | 28 | return objArray.Select(e => e.BodyType == BodyType.Encodable ? e.Body : e).ToArray(); 29 | 30 | default: 31 | 32 | return value; 33 | } 34 | } 35 | 36 | /// 37 | /// Gets the value of the DataValue, or the default value for the type. 38 | /// 39 | /// The expected type. 40 | /// The DataValue. 41 | /// The value, if an instance of the specified Type, otherwise the Type's default value. 42 | [return: MaybeNull] 43 | public static T GetValueOrDefault(this DataValue dataValue) 44 | { 45 | var value = dataValue.Value; 46 | switch (value) 47 | { 48 | case ExtensionObject obj: 49 | // handle object, custom type 50 | var v2 = obj.BodyType == BodyType.Encodable ? obj.Body : obj; 51 | if (v2 is T t1) 52 | { 53 | return t1; 54 | } 55 | return default!; 56 | 57 | case ExtensionObject[] objArray: 58 | // handle object[], custom type[] 59 | var v3 = objArray.Select(e => e.BodyType == BodyType.Encodable ? e.Body : e); 60 | var elementType = typeof(T).GetElementType(); 61 | if (elementType == null) 62 | { 63 | return default!; 64 | } 65 | try 66 | { 67 | var v4 = typeof(Enumerable).GetMethod("Cast")!.MakeGenericMethod(elementType).Invoke(null, new object?[] { v3 }); 68 | var v5 = typeof(Enumerable).GetMethod("ToArray")!.MakeGenericMethod(elementType).Invoke(null, new object?[] { v4 }); 69 | if (v5 is T t2) 70 | { 71 | return t2; 72 | } 73 | return default!; 74 | } 75 | catch (Exception) 76 | { 77 | return default!; 78 | } 79 | 80 | default: 81 | // handle built-in type 82 | if (value is T t) 83 | { 84 | return t; 85 | } 86 | return default!; 87 | 88 | } 89 | } 90 | 91 | /// 92 | /// Gets the value of the DataValue, or the specified default value. 93 | /// 94 | /// The expected type. 95 | /// A DataValue 96 | /// A default value. 97 | /// The value, if an instance of the specified Type, otherwise the specified default value. 98 | [return: NotNullIfNotNull("defaultValue")] 99 | public static T GetValueOrDefault(this DataValue dataValue, T defaultValue) 100 | { 101 | var value = dataValue.Value; 102 | switch (value) 103 | { 104 | case ExtensionObject obj: 105 | // handle object, custom type 106 | var v2 = obj.BodyType == BodyType.Encodable ? obj.Body : obj; 107 | if (v2 is T t1) 108 | { 109 | return t1; 110 | } 111 | return defaultValue; 112 | 113 | case ExtensionObject[] objArray: 114 | // handle object[], custom type[] 115 | var v3 = objArray.Select(e => e.BodyType == BodyType.Encodable ? e.Body : e); 116 | var elementType = typeof(T).GetElementType(); 117 | if (elementType == null) 118 | { 119 | return defaultValue; 120 | } 121 | try 122 | { 123 | var v4 = typeof(Enumerable).GetMethod("Cast")!.MakeGenericMethod(elementType).Invoke(null, new object?[] { v3 }); 124 | var v5 = typeof(Enumerable).GetMethod("ToArray")!.MakeGenericMethod(elementType).Invoke(null, new object?[] { v4 }); 125 | if (v5 is T t2) 126 | { 127 | return t2; 128 | } 129 | return defaultValue; 130 | } 131 | catch (Exception) 132 | { 133 | return defaultValue; 134 | } 135 | 136 | default: 137 | // handle built-in type 138 | if (value is T t) 139 | { 140 | return t; 141 | } 142 | return defaultValue; 143 | 144 | } 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/DiagnosticFlags.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | 6 | namespace Workstation.ServiceModel.Ua 7 | { 8 | [Flags] 9 | public enum DiagnosticFlags : uint 10 | { 11 | None = 0x00000000, 12 | ServiceSymbolicId = 0x00000001, 13 | ServiceLocalizedText = 0x00000002, 14 | ServiceAdditionalInfo = 0x00000004, 15 | ServiceInnerStatusCode = 0x00000008, 16 | ServiceInnerDiagnostics = 0x00000010, 17 | OperationSymbolicId = 0x00000020, 18 | OperationLocalizedText = 0x00000040, 19 | OperationAdditionalInfo = 0x0000080, 20 | OperationInnerStatusCode = 0x00000100, 21 | OperationInnerDiagnostics = 0x0000200, 22 | } 23 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/DiagnosticInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Workstation.ServiceModel.Ua 5 | { 6 | [DataTypeId(DataTypeIds.DiagnosticInfo)] 7 | public sealed class DiagnosticInfo 8 | { 9 | public DiagnosticInfo(int namespaceUri = -1, int symbolicId = -1, int locale = -1, int localizedText = -1, string? additionalInfo = null, StatusCode innerStatusCode = default(StatusCode), DiagnosticInfo? innerDiagnosticInfo = null) 10 | { 11 | NamespaceUri = namespaceUri; 12 | SymbolicId = symbolicId; 13 | Locale = locale; 14 | LocalizedText = localizedText; 15 | AdditionalInfo = additionalInfo; 16 | InnerStatusCode = innerStatusCode; 17 | InnerDiagnosticInfo = innerDiagnosticInfo; 18 | } 19 | 20 | public int NamespaceUri { get; } 21 | 22 | public int SymbolicId { get; } 23 | 24 | public int Locale { get; } 25 | 26 | public int LocalizedText { get; } 27 | 28 | public string? AdditionalInfo { get; } 29 | 30 | public StatusCode InnerStatusCode { get; } 31 | 32 | public DiagnosticInfo? InnerDiagnosticInfo { get; } 33 | } 34 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/EventFieldAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | 6 | namespace Workstation.ServiceModel.Ua 7 | { 8 | /// 9 | /// Specifies the EventField that will be created for this property. 10 | /// 11 | [AttributeUsage(AttributeTargets.Property)] 12 | public sealed class EventFieldAttribute : Attribute 13 | { 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | /// the TypeDefinitionId. 18 | /// the browse names, separated by '/'. 19 | /// the attribute. 20 | /// the range of array indexes. 21 | public EventFieldAttribute(string? typeDefinitionId = null, string? browsePath = null, uint attributeId = AttributeIds.Value, string? indexRange = null) 22 | { 23 | this.TypeDefinitionId = typeDefinitionId; 24 | this.BrowsePath = browsePath; 25 | this.AttributeId = attributeId; 26 | this.IndexRange = indexRange; 27 | } 28 | 29 | /// 30 | /// Gets the TypeDefinitionId. 31 | /// 32 | public string? TypeDefinitionId { get; } 33 | 34 | /// 35 | /// Gets the BrowsePath. 36 | /// 37 | public string? BrowsePath { get; } 38 | 39 | /// 40 | /// Gets the attribute. 41 | /// 42 | public uint AttributeId { get; } 43 | 44 | /// 45 | /// Gets the range of array indexes. 46 | /// 47 | public string? IndexRange { get; } 48 | } 49 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/EventHelper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | 9 | namespace Workstation.ServiceModel.Ua 10 | { 11 | public static class EventHelper 12 | { 13 | private static readonly Dictionary _selectClauseCache = new Dictionary(); 14 | private static readonly Dictionary _deserializerCache = new Dictionary(); 15 | 16 | public static T Deserialize(Variant[] eventFields) 17 | where T : BaseEvent, new() 18 | { 19 | var e = Activator.CreateInstance(); 20 | if (_deserializerCache.TryGetValue(typeof(T), out var infos)) 21 | { 22 | for (int i = 0; i < eventFields.Length; i++) 23 | { 24 | infos[i].SetValue(e, eventFields[i].GetValue()); 25 | } 26 | } 27 | 28 | return e; 29 | } 30 | 31 | public static BaseEvent Deserialize(Type type, Variant[] eventFields) 32 | { 33 | var e = (BaseEvent)Activator.CreateInstance(type)!; 34 | if (_deserializerCache.TryGetValue(type, out var infos)) 35 | { 36 | for (int i = 0; i < eventFields.Length; i++) 37 | { 38 | infos[i].SetValue(e, eventFields[i].GetValue()); 39 | } 40 | } 41 | 42 | return e; 43 | } 44 | 45 | public static SimpleAttributeOperand[] GetSelectClauses() 46 | where T : BaseEvent, new() 47 | { 48 | var type = typeof(T); 49 | if (_selectClauseCache.TryGetValue(type, out var clauses)) 50 | { 51 | return clauses; 52 | } 53 | 54 | RegisterSelectClauseAndDeserializer(type); 55 | return _selectClauseCache[type]; 56 | } 57 | 58 | public static SimpleAttributeOperand[] GetSelectClauses(Type type) 59 | { 60 | if (_selectClauseCache.TryGetValue(type, out var clauses)) 61 | { 62 | return clauses; 63 | } 64 | 65 | RegisterSelectClauseAndDeserializer(type); 66 | return _selectClauseCache[type]; 67 | } 68 | 69 | private static void RegisterSelectClauseAndDeserializer(Type type) 70 | { 71 | var clauseList = new List(); 72 | var infoList = new List(); 73 | foreach (var info in type.GetRuntimeProperties()) 74 | { 75 | var efa = info.GetCustomAttribute(); 76 | if (efa == null || string.IsNullOrEmpty(efa.TypeDefinitionId)) 77 | { 78 | continue; 79 | } 80 | 81 | var clause = new SimpleAttributeOperand 82 | { 83 | TypeDefinitionId = NodeId.Parse(efa.TypeDefinitionId!), 84 | BrowsePath = !String.IsNullOrWhiteSpace(efa.BrowsePath) ? efa.BrowsePath!.Split('/').Select(s => QualifiedName.Parse(s)).ToArray() : Array.Empty(), 85 | AttributeId = efa.AttributeId, 86 | IndexRange = efa.IndexRange 87 | }; 88 | 89 | clauseList.Add(clause); 90 | infoList.Add(info); 91 | } 92 | 93 | _selectClauseCache[type] = clauseList.ToArray(); 94 | _deserializerCache[type] = infoList.ToArray(); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/EventNotifierFlags.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | 6 | namespace Workstation.ServiceModel.Ua 7 | { 8 | [Flags] 9 | public enum EventNotifierFlags : byte 10 | { 11 | None = 0, 12 | SubscribeToEvents = 1, 13 | HistoryRead = 4, 14 | HistoryWrite = 8, 15 | } 16 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/ExtensionObject.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Xml.Linq; 7 | 8 | namespace Workstation.ServiceModel.Ua 9 | { 10 | public enum BodyType 11 | { 12 | None, 13 | ByteString, 14 | XmlElement, 15 | Encodable 16 | } 17 | 18 | public sealed class ExtensionObject 19 | { 20 | public ExtensionObject(byte[]? body, ExpandedNodeId? typeId) 21 | { 22 | if (body == null) 23 | { 24 | BodyType = BodyType.None; 25 | return; 26 | } 27 | 28 | Body = body; 29 | BodyType = BodyType.ByteString; 30 | TypeId = typeId; 31 | } 32 | 33 | public ExtensionObject(XElement? body, ExpandedNodeId? typeId) 34 | { 35 | if (body == null) 36 | { 37 | BodyType = BodyType.None; 38 | return; 39 | } 40 | 41 | Body = body; 42 | BodyType = BodyType.XmlElement; 43 | TypeId = typeId; 44 | } 45 | 46 | public ExtensionObject(IEncodable? body, ExpandedNodeId? typeId) 47 | { 48 | if (body == null) 49 | { 50 | BodyType = BodyType.None; 51 | return; 52 | } 53 | 54 | Body = body; 55 | BodyType = BodyType.Encodable; 56 | TypeId = typeId; 57 | } 58 | 59 | public ExtensionObject(IEncodable? body) 60 | { 61 | if (body == null) 62 | { 63 | BodyType = BodyType.None; 64 | return; 65 | } 66 | 67 | Body = body; 68 | BodyType = BodyType.Encodable; 69 | if (!TypeLibrary.TryGetBinaryEncodingIdFromType(body.GetType(), out var binaryEncodingId)) 70 | { 71 | throw new ServiceResultException(StatusCodes.BadDataEncodingUnsupported); 72 | } 73 | TypeId = binaryEncodingId; 74 | 75 | } 76 | 77 | public object? Body { get; } 78 | 79 | public ExpandedNodeId? TypeId { get; } 80 | 81 | public BodyType BodyType { get; } 82 | } 83 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/ICertificateStore.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using Microsoft.Extensions.Logging; 8 | using Org.BouncyCastle.Crypto.Parameters; 9 | using Org.BouncyCastle.X509; 10 | 11 | namespace Workstation.ServiceModel.Ua 12 | { 13 | /// 14 | /// The certificate store interface. 15 | /// 16 | public interface ICertificateStore 17 | { 18 | /// 19 | /// Gets the local certificate and private key. 20 | /// 21 | /// The application description. 22 | /// The logger. 23 | /// The local certificate and private key. 24 | Task<(X509Certificate? Certificate, RsaKeyParameters? Key)> GetLocalCertificateAsync(ApplicationDescription applicationDescription, ILogger? logger, System.Threading.CancellationToken token); 25 | 26 | /// 27 | /// Validates the remote certificate. 28 | /// 29 | /// The remote certificate. 30 | /// The logger. 31 | /// The validator result. 32 | Task ValidateRemoteCertificateAsync(X509Certificate certificate, ILogger? logger, CancellationToken token); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/ICommunicationObject.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Workstation.ServiceModel.Ua 8 | { 9 | /// Defines the contract for the basic state machine for all communication-oriented objects in the system. 10 | public interface ICommunicationObject 11 | { 12 | /// Gets the current state of the communication-oriented object. 13 | /// The value of the of the object. 14 | CommunicationState State { get; } 15 | 16 | /// Causes a communication object to transition immediately from its current state into the closed state. 17 | /// The that notifies that the task should be canceled. 18 | /// A representing the asynchronous operation. 19 | Task AbortAsync(CancellationToken token = default); 20 | 21 | /// Causes a communication object to transition from its current state into the closed state. 22 | /// The that notifies that the task should be canceled. 23 | /// A representing the asynchronous operation. 24 | Task CloseAsync(CancellationToken token = default); 25 | 26 | /// Causes a communication object to transition from the created state into the opened state. 27 | /// The that notifies that the task should be canceled. 28 | /// A representing the asynchronous operation. 29 | Task OpenAsync(CancellationToken token = default); 30 | } 31 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/IConversation.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.IO; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace Workstation.ServiceModel.Ua 10 | { 11 | /// 12 | /// The conversation interface is used to support different 13 | /// security protocols in the secure channel implementation. 14 | /// 15 | /// OPC UA specification Part 6: Mappings, 4 16 | public interface IConversation 17 | { 18 | /// 19 | /// The channel ID. 20 | /// 21 | uint ChannelId { get; set; } 22 | 23 | /// 24 | /// The token ID. 25 | /// 26 | uint TokenId { get; set; } 27 | 28 | /// 29 | /// The remote nounce. 30 | /// 31 | byte[]? RemoteNonce { get; set; } 32 | 33 | /// 34 | /// This will replace the current local nounce, with the next local 35 | /// nounce. 36 | /// 37 | /// The next nounce. 38 | byte[]? GetNextNonce(); 39 | 40 | /// 41 | /// Encrypts the content given by the stream 42 | /// into message chunks that are propagated further by the 43 | /// delegate. 44 | /// 45 | /// The content to be encrypted. 46 | /// The message type, . 47 | /// The request handle. 48 | /// The delegate to consume the encrypted chunks. 49 | /// A cancellation token used to propagate notification that this operation should be canceled. 50 | /// A task representing the asynchronous operation. 51 | Task EncryptMessageAsync(Stream bodyStream, uint messageType, uint requestHandle, Func consume, CancellationToken token); 52 | 53 | /// 54 | /// Decrypts the content received by the 55 | /// delegate into the stream. 56 | /// 57 | /// The stream to recieve the decrypted content. 58 | /// The delgate to receive the chunks. 59 | /// A cancellation token used to propagate notification that this operation should be canceled. 60 | /// The (last) message type and request handle. 61 | Task<(uint messageType, uint requestHandle)> DecryptMessageAsync(Stream bodyStream, Func> receive, CancellationToken token); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/IConversationProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Microsoft.Extensions.Logging; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Workstation.ServiceModel.Ua 9 | { 10 | /// 11 | /// Provider interface for instances. 12 | /// 13 | public interface IConversationProvider 14 | { 15 | /// 16 | /// Creates an instance for a client 17 | /// channel. 18 | /// 19 | /// The remote endpoint. 20 | /// The local application description. 21 | /// The transport connection options. 22 | /// The certificate store. 23 | /// The logger. 24 | /// An instance. 25 | Task CreateAsync(EndpointDescription remoteEndpoint, ApplicationDescription localDescription, TransportConnectionOptions options, ICertificateStore? certificateStore, ILogger? logger, CancellationToken token); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/IDecoder.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Xml.Linq; 6 | 7 | namespace Workstation.ServiceModel.Ua 8 | { 9 | public interface IDecoder : IDisposable 10 | { 11 | void PushNamespace(string namespaceUri); 12 | 13 | void PopNamespace(); 14 | 15 | bool ReadBoolean(string? fieldName); 16 | 17 | sbyte ReadSByte(string? fieldName); 18 | 19 | byte ReadByte(string? fieldName); 20 | 21 | short ReadInt16(string? fieldName); 22 | 23 | ushort ReadUInt16(string? fieldName); 24 | 25 | int ReadInt32(string? fieldName); 26 | 27 | uint ReadUInt32(string? fieldName); 28 | 29 | long ReadInt64(string? fieldName); 30 | 31 | ulong ReadUInt64(string? fieldName); 32 | 33 | float ReadFloat(string? fieldName); 34 | 35 | double ReadDouble(string? fieldName); 36 | 37 | string? ReadString(string? fieldName); 38 | 39 | DateTime ReadDateTime(string? fieldName); 40 | 41 | Guid ReadGuid(string? fieldName); 42 | 43 | byte[]? ReadByteString(string? fieldName); 44 | 45 | XElement? ReadXElement(string? fieldName); 46 | 47 | NodeId ReadNodeId(string? fieldName); 48 | 49 | ExpandedNodeId ReadExpandedNodeId(string? fieldName); 50 | 51 | StatusCode ReadStatusCode(string? fieldName); 52 | 53 | QualifiedName ReadQualifiedName(string? fieldName); 54 | 55 | LocalizedText ReadLocalizedText(string? fieldName); 56 | 57 | ExtensionObject? ReadExtensionObject(string? fieldName); 58 | 59 | T? ReadExtensionObject(string? fieldName) 60 | where T : class, IEncodable; 61 | 62 | DataValue ReadDataValue(string? fieldName); 63 | 64 | Variant ReadVariant(string? fieldName); 65 | 66 | DiagnosticInfo ReadDiagnosticInfo(string? fieldName); 67 | 68 | T ReadEncodable(string? fieldName) 69 | where T : class, IEncodable; 70 | 71 | IServiceResponse ReadResponse(); 72 | 73 | T ReadEnumeration(string? fieldName) 74 | where T : struct, IConvertible; 75 | 76 | bool[]? ReadBooleanArray(string? fieldName); 77 | 78 | sbyte[]? ReadSByteArray(string? fieldName); 79 | 80 | byte[]? ReadByteArray(string? fieldName); 81 | 82 | short[]? ReadInt16Array(string? fieldName); 83 | 84 | ushort[]? ReadUInt16Array(string? fieldName); 85 | 86 | int[]? ReadInt32Array(string? fieldName); 87 | 88 | uint[]? ReadUInt32Array(string? fieldName); 89 | 90 | long[]? ReadInt64Array(string? fieldName); 91 | 92 | ulong[]? ReadUInt64Array(string? fieldName); 93 | 94 | float[]? ReadFloatArray(string? fieldName); 95 | 96 | double[]? ReadDoubleArray(string? fieldName); 97 | 98 | string?[]? ReadStringArray(string? fieldName); 99 | 100 | DateTime[]? ReadDateTimeArray(string? fieldName); 101 | 102 | Guid[]? ReadGuidArray(string? fieldName); 103 | 104 | byte[]?[]? ReadByteStringArray(string? fieldName); 105 | 106 | XElement?[]? ReadXElementArray(string? fieldName); 107 | 108 | NodeId[]? ReadNodeIdArray(string? fieldName); 109 | 110 | ExpandedNodeId[]? ReadExpandedNodeIdArray(string? fieldName); 111 | 112 | StatusCode[]? ReadStatusCodeArray(string? fieldName); 113 | 114 | QualifiedName[]? ReadQualifiedNameArray(string? fieldName); 115 | 116 | LocalizedText[]? ReadLocalizedTextArray(string? fieldName); 117 | 118 | ExtensionObject?[]? ReadExtensionObjectArray(string? fieldName); 119 | 120 | T?[]? ReadExtensionObjectArray(string? fieldName) 121 | where T : class, IEncodable; 122 | 123 | DataValue[]? ReadDataValueArray(string? fieldName); 124 | 125 | Variant[]? ReadVariantArray(string? fieldName); 126 | 127 | DiagnosticInfo[]? ReadDiagnosticInfoArray(string? fieldName); 128 | 129 | T[]? ReadEncodableArray(string? fieldName) 130 | where T : class, IEncodable; 131 | 132 | T[]? ReadEnumerationArray(string fieldName) 133 | where T : struct, IConvertible; 134 | } 135 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/IEncodable.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Workstation.ServiceModel.Ua 5 | { 6 | public interface IEncodable 7 | { 8 | void Encode(IEncoder encoder); 9 | 10 | void Decode(IDecoder decoder); 11 | } 12 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/IEncoder.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Xml.Linq; 6 | 7 | namespace Workstation.ServiceModel.Ua 8 | { 9 | public interface IEncoder : IDisposable 10 | { 11 | void PushNamespace(string namespaceUri); 12 | 13 | void PopNamespace(); 14 | 15 | void WriteBoolean(string? fieldName, bool value); 16 | 17 | void WriteSByte(string? fieldName, sbyte value); 18 | 19 | void WriteByte(string? fieldName, byte value); 20 | 21 | void WriteInt16(string? fieldName, short value); 22 | 23 | void WriteUInt16(string? fieldName, ushort value); 24 | 25 | void WriteInt32(string? fieldName, int value); 26 | 27 | void WriteUInt32(string? fieldName, uint value); 28 | 29 | void WriteInt64(string? fieldName, long value); 30 | 31 | void WriteUInt64(string? fieldName, ulong value); 32 | 33 | void WriteFloat(string? fieldName, float value); 34 | 35 | void WriteDouble(string? fieldName, double value); 36 | 37 | void WriteString(string? fieldName, string? value); 38 | 39 | void WriteDateTime(string? fieldName, DateTime value); 40 | 41 | void WriteGuid(string? fieldName, Guid value); 42 | 43 | void WriteByteString(string? fieldName, byte[]? value); 44 | 45 | void WriteXElement(string? fieldName, XElement? value); 46 | 47 | void WriteNodeId(string? fieldName, NodeId? value); 48 | 49 | void WriteExpandedNodeId(string? fieldName, ExpandedNodeId? value); 50 | 51 | void WriteStatusCode(string? fieldName, StatusCode value); 52 | 53 | void WriteQualifiedName(string? fieldName, QualifiedName? value); 54 | 55 | void WriteLocalizedText(string? fieldName, LocalizedText? value); 56 | 57 | void WriteExtensionObject(string? fieldName, ExtensionObject? value); 58 | 59 | void WriteExtensionObject(string? fieldName, T? value) 60 | where T : class, IEncodable; 61 | 62 | void WriteDataValue(string? fieldName, DataValue? value); 63 | 64 | void WriteVariant(string? fieldName, Variant value); 65 | 66 | void WriteDiagnosticInfo(string? fieldName, DiagnosticInfo? value); 67 | 68 | void WriteEncodable(string? fieldName, T? value) 69 | where T : class, IEncodable; 70 | 71 | void WriteRequest(IServiceRequest request); 72 | 73 | void WriteEnumeration(string? fieldName, T value) 74 | where T : struct, IConvertible; 75 | 76 | void WriteBooleanArray(string? fieldName, bool[]? values); 77 | 78 | void WriteSByteArray(string? fieldName, sbyte[]? values); 79 | 80 | void WriteByteArray(string? fieldName, byte[]? values); 81 | 82 | void WriteInt16Array(string? fieldName, short[]? values); 83 | 84 | void WriteUInt16Array(string? fieldName, ushort[]? values); 85 | 86 | void WriteInt32Array(string? fieldName, int[]? values); 87 | 88 | void WriteUInt32Array(string? fieldName, uint[]? values); 89 | 90 | void WriteInt64Array(string? fieldName, long[]? values); 91 | 92 | void WriteUInt64Array(string? fieldName, ulong[]? values); 93 | 94 | void WriteFloatArray(string? fieldName, float[]? values); 95 | 96 | void WriteDoubleArray(string? fieldName, double[]? values); 97 | 98 | void WriteStringArray(string? fieldName, string?[]? values); 99 | 100 | void WriteDateTimeArray(string? fieldName, DateTime[]? values); 101 | 102 | void WriteGuidArray(string? fieldName, Guid[]? values); 103 | 104 | void WriteByteStringArray(string? fieldName, byte[]?[]? values); 105 | 106 | void WriteXElementArray(string? fieldName, XElement?[]? values); 107 | 108 | void WriteNodeIdArray(string? fieldName, NodeId?[]? values); 109 | 110 | void WriteExpandedNodeIdArray(string? fieldName, ExpandedNodeId?[]? values); 111 | 112 | void WriteStatusCodeArray(string? fieldName, StatusCode[]? values); 113 | 114 | void WriteQualifiedNameArray(string? fieldName, QualifiedName?[]? values); 115 | 116 | void WriteLocalizedTextArray(string? fieldName, LocalizedText?[]? values); 117 | 118 | void WriteExtensionObjectArray(string? fieldName, ExtensionObject?[]? values); 119 | 120 | void WriteExtensionObjectArray(string? fieldName, T?[]? values) 121 | where T : class, IEncodable; 122 | 123 | void WriteDataValueArray(string? fieldName, DataValue?[]? values); 124 | 125 | void WriteVariantArray(string? fieldName, Variant[]? values); 126 | 127 | void WriteDiagnosticInfoArray(string? fieldName, DiagnosticInfo?[]? values); 128 | 129 | void WriteEncodableArray(string? fieldName, T?[]? values) 130 | where T : class, IEncodable; 131 | 132 | void WriteEnumerationArray(string fieldName, T[] values) 133 | where T : struct, IConvertible; 134 | } 135 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/IEncodingProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.IO; 5 | using Workstation.ServiceModel.Ua.Channels; 6 | 7 | namespace Workstation.ServiceModel.Ua 8 | { 9 | /// 10 | /// Provider interface for and 11 | /// instances. 12 | /// 13 | public interface IEncodingProvider 14 | { 15 | /// 16 | /// Creates an encoder instance. 17 | /// 18 | /// The target stream. 19 | /// The encoding context. 20 | /// Whether the stream should remain open on disposal. 21 | /// The encoder instance. 22 | IEncoder CreateEncoder(Stream stream, IEncodingContext? context, bool keepStreamOpen); 23 | 24 | /// 25 | /// Creates a decoder instance. 26 | /// 27 | /// The source stream. 28 | /// The encoding context. 29 | /// Whether the stream should remain open on disposal. 30 | /// The decoder instance. 31 | IDecoder CreateDecoder(Stream stream, IEncodingContext? context, bool keepStreamOpen); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/IOptionalFields.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Workstation.ServiceModel.Ua 5 | { 6 | /// 7 | /// This interface should be implemented by every OPC Ua data type which is 8 | /// derived from the OPC UA structure type and has one or more optional 9 | /// fields. 10 | /// 11 | /// Classes, that introduce optional fields first, should implement the 12 | /// property as a virtual property getter. 13 | /// This allows derived classes to override this property, and thus 14 | /// increase the number of optional fields. Classes should also provide 15 | /// a `protected` setter method for the 16 | /// property to enable derived classes to set the bits belonging to 17 | /// its fields/properties. 18 | /// 19 | /// OPC UA specification Part 6: Mappings, 5.2.7 20 | /// OPC UA specification Part 6: Mappings, 5.3.6 21 | /// OPC UA specification Part 6: Mappings, 5.4.7 22 | public interface IOptionalFields 23 | { 24 | /// 25 | /// Retrieves the count of optional fields, that is properities in dotnet. 26 | /// 27 | int OptionalFieldCount { get; } 28 | 29 | /// 30 | /// Retrieves the encoding mask. 31 | /// 32 | uint EncodingMask { get; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/IRequestChannel.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Workstation.ServiceModel.Ua 8 | { 9 | /// 10 | /// Provides a method to send an to a RemoteEndpoint. 11 | /// 12 | public interface IRequestChannel 13 | { 14 | /// Sends an IServiceRequest and returns the correlated IServiceResponse. 15 | /// The received in response to the request. 16 | /// The to be transmitted. 17 | /// Optional . 18 | Task RequestAsync(IServiceRequest request, CancellationToken token = default); 19 | } 20 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/IServiceRequest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Workstation.ServiceModel.Ua 5 | { 6 | public interface IServiceRequest : IEncodable 7 | { 8 | RequestHeader? RequestHeader { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/IServiceResponse.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Workstation.ServiceModel.Ua 5 | { 6 | public interface IServiceResponse : IEncodable 7 | { 8 | ResponseHeader? ResponseHeader { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/ISetDataErrorInfo.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Collections.Generic; 5 | 6 | namespace Workstation.ServiceModel.Ua 7 | { 8 | /// 9 | /// Sets the result of create, read, write, or publish service calls. 10 | /// 11 | public interface ISetDataErrorInfo 12 | { 13 | /// 14 | /// Sets the result of a create, read, write, or publish service call. 15 | /// 16 | /// The property name. 17 | /// The error messages. 18 | void SetErrors(string propertyName, IEnumerable? errors); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/ITransportConnection.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Workstation.ServiceModel.Ua 9 | { 10 | /// 11 | /// The transport connection interface is used to support different 12 | /// transport protocols in the transport channel implementation. 13 | /// 14 | /// OPC UA specification Part 6: Mappings, 4 15 | public interface ITransportConnection : IAsyncDisposable 16 | { 17 | /// 18 | /// Opens the connection. This includes the hello message handshake. 19 | /// 20 | /// 21 | /// The real network may already be opened at a previous point. The 22 | /// connection is closed with 23 | /// method. 24 | /// 25 | /// The protocol version. 26 | /// The requested transport connection options. 27 | /// A cancellation token used to propagate notification that this operation should be canceled. 28 | /// The transport connection options to be used. 29 | Task OpenAsync(uint protocolVersion, TransportConnectionOptions localOptions, CancellationToken token); 30 | 31 | /// 32 | /// Sends content from the buffer. 33 | /// 34 | /// The buffer. 35 | /// The starting offset. 36 | /// The count of bytes to be send. 37 | /// A cancellation token used to propagate notification that this operation should be canceled. 38 | /// A task representing the asynchronous operation. 39 | Task SendAsync(byte[] buffer, int offset, int count, CancellationToken token); 40 | 41 | /// 42 | /// Receive content into the buffer. 43 | /// 44 | /// The buffer. 45 | /// The starting offset. 46 | /// The count of bytes to be received. 47 | /// A cancellation token used to propagate notification that this operation should be canceled. 48 | /// A task representing the asynchronous operation. 49 | Task ReceiveAsync(byte[] buffer, int offset, int count, CancellationToken token); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/ITransportConnectionProvider.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Workstation.ServiceModel.Ua 8 | { 9 | /// 10 | /// Provider interface for instances. 11 | /// 12 | public interface ITransportConnectionProvider 13 | { 14 | /// 15 | /// Creates a transport connection. 16 | /// 17 | /// 18 | /// The implementation can already open the bare network connection, or 19 | /// defer this process to the 20 | /// method. 21 | /// 22 | /// The connection string. 23 | /// The transport connection. 24 | Task ConnectAsync(string connectionString, CancellationToken token); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/IUserIdentity.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Workstation.ServiceModel.Ua 5 | { 6 | public class IUserIdentity 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/IssuedIdentity.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Workstation.ServiceModel.Ua 5 | { 6 | public class IssuedIdentity : IUserIdentity 7 | { 8 | public IssuedIdentity(byte[] tokenData) 9 | { 10 | TokenData = tokenData; 11 | } 12 | 13 | public byte[] TokenData { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/LocalizedText.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace Workstation.ServiceModel.Ua 8 | { 9 | 10 | [DataTypeId(DataTypeIds.LocalizedText)] 11 | public sealed class LocalizedText : IEquatable 12 | { 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | /// The text in the specified locale. 17 | /// The locale. 18 | public LocalizedText(string? text, string? locale = "") 19 | { 20 | Locale = locale; 21 | Text = text; 22 | } 23 | 24 | public string? Text { get; } 25 | 26 | public string? Locale { get; } 27 | 28 | public static implicit operator LocalizedText(string? a) 29 | { 30 | return new LocalizedText(a); 31 | } 32 | 33 | public static implicit operator string?(LocalizedText? a) 34 | { 35 | return a?.Text; 36 | } 37 | 38 | public override bool Equals(object? obj) 39 | { 40 | return Equals(obj as LocalizedText); 41 | } 42 | 43 | public bool Equals(LocalizedText? other) 44 | { 45 | return other != null && 46 | Text == other.Text && 47 | Locale == other.Locale; 48 | } 49 | 50 | public override int GetHashCode() 51 | { 52 | int hashCode = 670029253; 53 | if (Text != null) hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Text); 54 | if (Locale != null) hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Locale); 55 | return hashCode; 56 | } 57 | 58 | public static bool operator ==(LocalizedText? left, LocalizedText? right) 59 | { 60 | return EqualityComparer.Default.Equals(left, right); 61 | } 62 | 63 | public static bool operator !=(LocalizedText? left, LocalizedText? right) 64 | { 65 | return !(left == right); 66 | } 67 | 68 | public override string? ToString() 69 | { 70 | return Text; 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/MappedEndpoint.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Workstation.ServiceModel.Ua 5 | { 6 | /// 7 | /// A map between a requested endpoint url and the endpoint. 8 | /// 9 | public class MappedEndpoint 10 | { 11 | public string? RequestedUrl { get; set; } 12 | 13 | public EndpointDescription? Endpoint { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/MethodServiceSet.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Workstation.ServiceModel.Ua 9 | { 10 | public static class MethodServiceSet 11 | { 12 | /// 13 | /// Calls (invokes) a list of Methods. 14 | /// 15 | /// A instance of . 16 | /// A . 17 | /// A representing the asynchronous operation that returns a . 18 | /// OPC UA specification Part 4: Services, 5.11.2 19 | public static async Task CallAsync(this IRequestChannel channel, CallRequest request, CancellationToken token = default) 20 | { 21 | if (request == null) 22 | { 23 | throw new ArgumentNullException(nameof(request)); 24 | } 25 | 26 | return (CallResponse)await channel.RequestAsync(request, token).ConfigureAwait(false); 27 | } 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/MonitoredItemAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | 6 | namespace Workstation.ServiceModel.Ua 7 | { 8 | /// 9 | /// Specifies the MonitoredItem that will be created for this property. 10 | /// 11 | [AttributeUsage(AttributeTargets.Property)] 12 | public sealed class MonitoredItemAttribute : Attribute 13 | { 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | /// the NodeId to monitor. 18 | /// the attribute to monitor. 19 | /// the range of array indexes to monitor. 20 | /// the sampling interval. 21 | /// the length of the queue used by the server to buffer values. 22 | /// a value indicating whether to discard the oldest entries in the queue when it is full. 23 | /// the properties that trigger a data change. 24 | /// the type of deadband calculation. 25 | /// the deadband value. 26 | public MonitoredItemAttribute(string nodeId, uint attributeId = AttributeIds.Value, string? indexRange = null, int samplingInterval = -1, uint queueSize = 0, bool discardOldest = true, DataChangeTrigger dataChangeTrigger = DataChangeTrigger.StatusValue, DeadbandType deadbandType = DeadbandType.None, double deadbandValue = 0.0) 27 | { 28 | NodeId = nodeId; 29 | AttributeId = attributeId; 30 | IndexRange = indexRange; 31 | SamplingInterval = samplingInterval; 32 | QueueSize = queueSize; 33 | DiscardOldest = discardOldest; 34 | DataChangeTrigger = dataChangeTrigger; 35 | DeadbandType = deadbandType; 36 | DeadbandValue = deadbandValue; 37 | } 38 | 39 | /// 40 | /// Gets the NodeId to monitor. 41 | /// 42 | public string NodeId { get; } 43 | 44 | /// 45 | /// Gets the attribute to monitor. 46 | /// 47 | public uint AttributeId { get; } 48 | 49 | /// 50 | /// Gets the range of array indexes to monitor. 51 | /// 52 | public string? IndexRange { get; } 53 | 54 | /// 55 | /// Gets the sampling interval. 56 | /// 57 | public int SamplingInterval { get; } 58 | 59 | /// 60 | /// Gets the length of the queue used by the server to buffer values. 61 | /// 62 | public uint QueueSize { get; } 63 | 64 | /// 65 | /// Gets a value indicating whether to discard the oldest entries in the queue when it is full. 66 | /// 67 | public bool DiscardOldest { get; } 68 | 69 | /// 70 | /// Gets the properties that trigger a data change. 71 | /// 72 | public DataChangeTrigger DataChangeTrigger { get; } 73 | 74 | /// 75 | /// Gets the type of deadband calculation. 76 | /// 77 | public DeadbandType DeadbandType { get; } 78 | 79 | /// 80 | /// Gets the deadband value. 81 | /// 82 | public double DeadbandValue { get; } 83 | } 84 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/MonitoredItemServiceSet.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Workstation.ServiceModel.Ua 9 | { 10 | public static class MonitoredItemServiceSet 11 | { 12 | /// 13 | /// Creates and adds one or more MonitoredItems to a Subscription. 14 | /// 15 | /// A instance of . 16 | /// A . 17 | /// A representing the asynchronous operation that returns a . 18 | /// OPC UA specification Part 4: Services, 5.12.2 19 | public static async Task CreateMonitoredItemsAsync(this IRequestChannel channel, CreateMonitoredItemsRequest request, CancellationToken token = default) 20 | { 21 | if (request == null) 22 | { 23 | throw new ArgumentNullException(nameof(request)); 24 | } 25 | 26 | return (CreateMonitoredItemsResponse)await channel.RequestAsync(request, token).ConfigureAwait(false); 27 | } 28 | 29 | /// 30 | /// Modifies MonitoredItems of a Subscription. 31 | /// 32 | /// A instance of . 33 | /// A . 34 | /// A representing the asynchronous operation that returns a . 35 | /// OPC UA specification Part 4: Services, 5.12.3 36 | public static async Task ModifyMonitoredItemsAsync(this IRequestChannel channel, ModifyMonitoredItemsRequest request, CancellationToken token = default) 37 | { 38 | if (request == null) 39 | { 40 | throw new ArgumentNullException(nameof(request)); 41 | } 42 | 43 | return (ModifyMonitoredItemsResponse)await channel.RequestAsync(request, token).ConfigureAwait(false); 44 | } 45 | 46 | /// 47 | /// Sets the monitoring mode for one or more MonitoredItems of a Subscription. 48 | /// 49 | /// A instance of . 50 | /// A . 51 | /// A representing the asynchronous operation that returns a . 52 | /// OPC UA specification Part 4: Services, 5.12.4 53 | public static async Task SetMonitoringModeAsync(this IRequestChannel channel, SetMonitoringModeRequest request, CancellationToken token = default) 54 | { 55 | if (request == null) 56 | { 57 | throw new ArgumentNullException(nameof(request)); 58 | } 59 | 60 | return (SetMonitoringModeResponse)await channel.RequestAsync(request, token).ConfigureAwait(false); 61 | } 62 | 63 | /// 64 | /// Creates and deletes triggering links for a triggering item. 65 | /// 66 | /// A instance of . 67 | /// A . 68 | /// A representing the asynchronous operation that returns a . 69 | /// OPC UA specification Part 4: Services, 5.12.5 70 | public static async Task SetTriggeringAsync(this IRequestChannel channel, SetTriggeringRequest request, CancellationToken token = default) 71 | { 72 | if (request == null) 73 | { 74 | throw new ArgumentNullException(nameof(request)); 75 | } 76 | 77 | return (SetTriggeringResponse)await channel.RequestAsync(request, token).ConfigureAwait(false); 78 | } 79 | 80 | /// 81 | /// Removes one or more MonitoredItems of a Subscription. 82 | /// 83 | /// A instance of . 84 | /// A . 85 | /// A representing the asynchronous operation that returns a . 86 | /// OPC UA specification Part 4: Services, 5.12.6 87 | public static async Task DeleteMonitoredItemsAsync(this IRequestChannel channel, DeleteMonitoredItemsRequest request, CancellationToken token = default) 88 | { 89 | if (request == null) 90 | { 91 | throw new ArgumentNullException(nameof(request)); 92 | } 93 | 94 | return (DeleteMonitoredItemsResponse)await channel.RequestAsync(request, token).ConfigureAwait(false); 95 | } 96 | 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/NodeIds.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="true" hostspecific="true" language="C#" 2 | #><#@ assembly name="System.Core" 3 | #><#@ import namespace="System.Linq" 4 | #><#@ import namespace="System.Text" 5 | #><#@ import namespace="System.Collections.Generic" 6 | #><#@ assembly name="System.Xml.dll" 7 | #><#@ import namespace="System.Xml" 8 | #><#@ import namespace="System.IO" 9 | #><#@ output extension="generated.cs" 10 | #><# 11 | var filename = this.Host.ResolvePath(@"Schema\NodeIds.csv"); 12 | var comma = new[] { ',' }; 13 | var fields = 14 | from l in File.ReadAllLines(filename) 15 | let s = l.Split(comma, 3) 16 | select new { Name = s[0], Value = s[1], Type =s[2] }; 17 | 18 | #>// ------------------------------------------------------------------------------ 19 | // 20 | // This code was generated by a tool. 21 | // Runtime Version: 15.0.0.0 22 | // 23 | // Changes to this file may cause incorrect behavior and will be lost if 24 | // the code is regenerated. 25 | // 26 | // ------------------------------------------------------------------------------ 27 | namespace Workstation.ServiceModel.Ua 28 | { 29 | public static class DataTypeIds 30 | { 31 | <# foreach (var f in fields.Where(ff=>ff.Type=="DataType")) { #> public const string <#= f.Name #> = "i=<#= f.Value #>"; 32 | <# } #> public const string HistoryBase = "i=99999"; 33 | } 34 | public static class MethodIds 35 | { 36 | <# foreach (var f in fields.Where(ff=>ff.Type=="Method")) { #> public const string <#= f.Name #> = "i=<#= f.Value #>"; 37 | <# } #> } 38 | public static class ObjectIds 39 | { 40 | <# foreach (var f in fields.Where(ff=>ff.Type=="Object")) { #> public const string <#= f.Name #> = "i=<#= f.Value #>"; 41 | <# } #> public const string HistoryBase_Encoding_DefaultBinary = "i=99998"; 42 | public const string HistoryBase_Encoding_DefaultXml = "i=99997"; 43 | } 44 | public static class ObjectTypeIds 45 | { 46 | <# foreach (var f in fields.Where(ff=>ff.Type=="ObjectType")) { #> public const string <#= f.Name #> = "i=<#= f.Value #>"; 47 | <# } #> } 48 | public static class ReferenceTypeIds 49 | { 50 | <# foreach (var f in fields.Where(ff=>ff.Type=="ReferenceType")) { #> public const string <#= f.Name #> = "i=<#= f.Value #>"; 51 | <# } #> } 52 | public static class VariableIds 53 | { 54 | <# foreach (var f in fields.Where(ff=>ff.Type=="Variable")) { #> public const string <#= f.Name #> = "i=<#= f.Value #>"; 55 | <# } #> } 56 | public static class VariableTypeIds 57 | { 58 | <# foreach (var f in fields.Where(ff=>ff.Type=="VariableType")) { #> public const string <#= f.Name #> = "i=<#= f.Value #>"; 59 | <# } #> } 60 | } 61 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/NodeManagementServiceSet.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Workstation.ServiceModel.Ua 9 | { 10 | public static class NodeManagementServiceSet 11 | { 12 | /// 13 | /// Adds one or more Nodes into the AddressSpace hierarchy. 14 | /// 15 | /// A instance of . 16 | /// A . 17 | /// A representing the asynchronous operation that returns a . 18 | /// OPC UA specification Part 4: Services, 5.7.2 19 | public static async Task AddNodesAsync(this IRequestChannel channel, AddNodesRequest request, CancellationToken token = default) 20 | { 21 | if (request == null) 22 | { 23 | throw new ArgumentNullException(nameof(request)); 24 | } 25 | 26 | return (AddNodesResponse)await channel.RequestAsync(request, token).ConfigureAwait(false); 27 | } 28 | 29 | /// 30 | /// Adds one or more References to one or more Nodes. 31 | /// 32 | /// A instance of . 33 | /// A . 34 | /// A representing the asynchronous operation that returns a . 35 | /// OPC UA specification Part 4: Services, 5.7.3 36 | public static async Task AddReferencesAsync(this IRequestChannel channel, AddReferencesRequest request, CancellationToken token = default) 37 | { 38 | if (request == null) 39 | { 40 | throw new ArgumentNullException(nameof(request)); 41 | } 42 | 43 | return (AddReferencesResponse)await channel.RequestAsync(request, token).ConfigureAwait(false); 44 | } 45 | 46 | /// 47 | /// Deletes one or more Nodes from the AddressSpace. 48 | /// 49 | /// A instance of . 50 | /// A . 51 | /// A representing the asynchronous operation that returns a . 52 | /// OPC UA specification Part 4: Services, 5.7.4 53 | public static async Task DeleteNodesAsync(this IRequestChannel channel, DeleteNodesRequest request, CancellationToken token = default) 54 | { 55 | if (request == null) 56 | { 57 | throw new ArgumentNullException(nameof(request)); 58 | } 59 | 60 | return (DeleteNodesResponse)await channel.RequestAsync(request, token).ConfigureAwait(false); 61 | } 62 | 63 | /// 64 | /// Deletes one or more References of a Node. 65 | /// 66 | /// A instance of . 67 | /// A . 68 | /// A representing the asynchronous operation that returns a . 69 | /// OPC UA specification Part 4: Services, 5.7.5 70 | public static async Task DeleteReferencesAsync(this IRequestChannel channel, DeleteReferencesRequest request, CancellationToken token = default) 71 | { 72 | if (request == null) 73 | { 74 | throw new ArgumentNullException(nameof(request)); 75 | } 76 | 77 | return (DeleteReferencesResponse)await channel.RequestAsync(request, token).ConfigureAwait(false); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/QualifiedName.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace Workstation.ServiceModel.Ua 8 | { 9 | 10 | [DataTypeId(DataTypeIds.QualifiedName)] 11 | public sealed class QualifiedName : IEquatable 12 | { 13 | /// 14 | /// Initializes a new instance of the class. 15 | /// 16 | /// the text portion of the QualifiedName. 17 | /// index that identifies the namespace that qualifies the name. 18 | public QualifiedName(string? name, ushort namespaceIndex = 0) 19 | { 20 | Name = name; 21 | NamespaceIndex = namespaceIndex; 22 | } 23 | 24 | public string? Name { get; private set; } 25 | 26 | public ushort NamespaceIndex { get; private set; } 27 | 28 | public static bool TryParse(string s, out QualifiedName qname) 29 | { 30 | try 31 | { 32 | string[] ss = s.Split(new[] { ':' }, 2); 33 | ushort ns = 0; 34 | string name = s; 35 | if (ss.Length > 1) 36 | { 37 | ns = ushort.Parse(ss[0]); 38 | name = ss[1]; 39 | } 40 | 41 | qname = new QualifiedName(name, ns); 42 | return true; 43 | } 44 | catch (Exception) 45 | { 46 | qname = new QualifiedName(string.Empty); 47 | return false; 48 | } 49 | } 50 | 51 | public static QualifiedName Parse(string s) 52 | { 53 | QualifiedName value; 54 | if (!QualifiedName.TryParse(s, out value)) 55 | { 56 | throw new ArgumentException("Unable to parse QualifiedName.", nameof(s)); 57 | } 58 | 59 | return value; 60 | } 61 | 62 | public override string ToString() 63 | { 64 | return $"{NamespaceIndex}:{Name}"; 65 | } 66 | 67 | public override bool Equals(object? obj) 68 | { 69 | return Equals(obj as QualifiedName); 70 | } 71 | 72 | public bool Equals(QualifiedName? other) 73 | { 74 | return other != null && 75 | Name == other.Name && 76 | NamespaceIndex == other.NamespaceIndex; 77 | } 78 | 79 | public override int GetHashCode() 80 | { 81 | int hashCode = 978021522; 82 | if (Name != null) hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Name); 83 | hashCode = hashCode * -1521134295 + NamespaceIndex.GetHashCode(); 84 | return hashCode; 85 | } 86 | 87 | public static bool operator ==(QualifiedName? left, QualifiedName? right) 88 | { 89 | return EqualityComparer.Default.Equals(left, right); 90 | } 91 | 92 | public static bool operator !=(QualifiedName? left, QualifiedName? right) 93 | { 94 | return !(left == right); 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/QueryServiceSet.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Workstation.ServiceModel.Ua 9 | { 10 | public static class QueryServiceSet 11 | { 12 | /// 13 | /// Issues a Query request to a View. 14 | /// 15 | /// A instance of . 16 | /// A . 17 | /// A representing the asynchronous operation that returns a . 18 | /// OPC UA specification Part 4: Services, 5.9.3 19 | public static async Task QueryFirstAsync(this IRequestChannel channel, QueryFirstRequest request, CancellationToken token = default) 20 | { 21 | if (request == null) 22 | { 23 | throw new ArgumentNullException(nameof(request)); 24 | } 25 | 26 | return (QueryFirstResponse)await channel.RequestAsync(request, token).ConfigureAwait(false); 27 | } 28 | 29 | /// 30 | /// Requests the next set of Query responses, when the information is too large to be sent in a single response. 31 | /// 32 | /// A instance of . 33 | /// A . 34 | /// A representing the asynchronous operation that returns a . 35 | /// OPC UA specification Part 4: Services, 5.9.4 36 | public static async Task QueryNextAsync(this IRequestChannel channel, QueryNextRequest request, CancellationToken token = default) 37 | { 38 | if (request == null) 39 | { 40 | throw new ArgumentNullException(nameof(request)); 41 | } 42 | 43 | return (QueryNextResponse)await channel.RequestAsync(request, token).ConfigureAwait(false); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/Schema/AttributeIds.csv: -------------------------------------------------------------------------------- 1 | NodeId,1 2 | NodeClass,2 3 | BrowseName,3 4 | DisplayName,4 5 | Description,5 6 | WriteMask,6 7 | UserWriteMask,7 8 | IsAbstract,8 9 | Symmetric,9 10 | InverseName,10 11 | ContainsNoLoops,11 12 | EventNotifier,12 13 | Value,13 14 | DataType,14 15 | ValueRank,15 16 | ArrayDimensions,16 17 | AccessLevel,17 18 | UserAccessLevel,18 19 | MinimumSamplingInterval,19 20 | Historizing,20 21 | Executable,21 22 | UserExecutable,22 23 | DataTypeDefinition,23 24 | RolePermissions,24 25 | UserRolePermissions,25 26 | AccessRestrictions,26 27 | AccessLevelEx,27 -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/SecurityPolicyUris.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Workstation.ServiceModel.Ua 5 | { 6 | public static class SecurityPolicyUris 7 | { 8 | public const string None = "http://opcfoundation.org/UA/SecurityPolicy#None"; 9 | public const string Basic128Rsa15 = "http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15"; 10 | public const string Basic256 = "http://opcfoundation.org/UA/SecurityPolicy#Basic256"; 11 | public const string Https = "http://opcfoundation.org/UA/SecurityPolicy#Https"; 12 | public const string Basic256Sha256 = "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256"; 13 | public const string Aes128_Sha256_RsaOaep = "http://opcfoundation.org/UA/SecurityPolicy#Aes128_Sha256_RsaOaep"; 14 | public const string Aes256_Sha256_RsaPss = "http://opcfoundation.org/UA/SecurityPolicy#Aes256_Sha256_RsaPss"; 15 | } 16 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/ServiceResultException.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | 6 | namespace Workstation.ServiceModel.Ua 7 | { 8 | #if NETSTANDARD 9 | [Serializable] 10 | #endif 11 | public sealed class ServiceResultException : Exception 12 | { 13 | public ServiceResultException(ServiceResult result) 14 | : base(result.ToString()) 15 | { 16 | HResult = unchecked((int)(uint)result.StatusCode); 17 | } 18 | 19 | public ServiceResultException(StatusCode statusCode) 20 | : base(StatusCodes.GetDefaultMessage(statusCode)) 21 | { 22 | HResult = unchecked((int)(uint)statusCode); 23 | } 24 | 25 | public ServiceResultException(StatusCode statusCode, string message) 26 | : base(message) 27 | { 28 | HResult = unchecked((int)(uint)statusCode); 29 | } 30 | 31 | public ServiceResultException(StatusCode statusCode, string message, Exception innerException) 32 | : base(message, innerException) 33 | { 34 | HResult = unchecked((int)(uint)statusCode); 35 | } 36 | 37 | /// 38 | /// Gets the StatusCode of the ServiceResult. 39 | /// 40 | public StatusCode StatusCode => (uint)HResult; 41 | } 42 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/SessionServiceSet.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Workstation.ServiceModel.Ua 9 | { 10 | public static class SessionServiceSet 11 | { 12 | /// 13 | /// Creates a Session. 14 | /// 15 | /// A instance of . 16 | /// A . 17 | /// A representing the asynchronous operation that returns a . 18 | /// OPC UA specification Part 4: Services, 5.6.2 19 | internal static async Task CreateSessionAsync(this IRequestChannel channel, CreateSessionRequest request, CancellationToken token = default) 20 | { 21 | if (request == null) 22 | { 23 | throw new ArgumentNullException(nameof(request)); 24 | } 25 | 26 | return (CreateSessionResponse)await channel.RequestAsync(request, token).ConfigureAwait(false); 27 | } 28 | 29 | /// 30 | /// Activates a session. 31 | /// 32 | /// A instance of . 33 | /// A . 34 | /// A representing the asynchronous operation that returns a . 35 | /// OPC UA specification Part 4: Services, 5.6.3 36 | internal static async Task ActivateSessionAsync(this IRequestChannel channel, ActivateSessionRequest request, CancellationToken token = default) 37 | { 38 | if (request == null) 39 | { 40 | throw new ArgumentNullException(nameof(request)); 41 | } 42 | 43 | return (ActivateSessionResponse)await channel.RequestAsync(request, token).ConfigureAwait(false); 44 | } 45 | 46 | /// 47 | /// Closes a session. 48 | /// 49 | /// A instance of . 50 | /// A . 51 | /// A representing the asynchronous operation that returns a . 52 | /// OPC UA specification Part 4: Services, 5.6.4 53 | internal static async Task CloseSessionAsync(this IRequestChannel channel, CloseSessionRequest request, CancellationToken token = default) 54 | { 55 | if (request == null) 56 | { 57 | throw new ArgumentNullException(nameof(request)); 58 | } 59 | 60 | return (CloseSessionResponse)await channel.RequestAsync(request, token).ConfigureAwait(false); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/StatusCode.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | 6 | namespace Workstation.ServiceModel.Ua 7 | { 8 | 9 | [DataTypeId(DataTypeIds.StatusCode)] 10 | public readonly struct StatusCode : IEquatable 11 | { 12 | private const uint _severityMask = 0xC0000000u; 13 | private const uint _severityGood = 0x00000000u; 14 | private const uint _severityUncertain = 0x40000000u; 15 | private const uint _severityBad = 0x80000000u; 16 | private const uint _subCodeMask = 0x0FFF0000u; 17 | private const uint _structureChanged = 0x00008000u; 18 | private const uint _semanticsChanged = 0x00004000u; 19 | private const uint _infoTypeMask = 0x00000C00u; 20 | private const uint _infoTypeDataValue = 0x00000400u; 21 | private const uint _infoBitsMask = 0x000003FFu; 22 | private const uint _limitBitsMask = 0x00000300u; 23 | private const uint _limitBitsNone = 0x00000000u; 24 | private const uint _limitBitsLow = 0x00000100u; 25 | private const uint _limitBitsHigh = 0x00000200u; 26 | private const uint _limitBitsConstant = 0x00000300u; 27 | private const uint _overflow = 0x0080u; 28 | 29 | public StatusCode(uint value) 30 | { 31 | Value = value; 32 | } 33 | 34 | public uint Value { get; } 35 | 36 | public static implicit operator StatusCode(uint a) 37 | { 38 | return new StatusCode(a); 39 | } 40 | 41 | public static implicit operator uint(StatusCode a) 42 | { 43 | return a.Value; 44 | } 45 | 46 | public static bool operator ==(StatusCode left, StatusCode right) 47 | { 48 | return left.Equals(right); 49 | } 50 | 51 | public static bool operator !=(StatusCode left, StatusCode right) 52 | { 53 | return !(left == right); 54 | } 55 | 56 | public static bool IsGood(StatusCode a) 57 | { 58 | return (a.Value & _severityMask) == _severityGood; 59 | } 60 | 61 | public static bool IsBad(StatusCode a) 62 | { 63 | return (a.Value & _severityMask) == _severityBad; 64 | } 65 | 66 | public static bool IsUncertain(StatusCode a) 67 | { 68 | return (a.Value & _severityMask) == _severityUncertain; 69 | } 70 | 71 | public static bool IsStructureChanged(StatusCode a) 72 | { 73 | return (a.Value & _structureChanged) == _structureChanged; 74 | } 75 | 76 | public static bool IsSemanticsChanged(StatusCode a) 77 | { 78 | return (a.Value & _semanticsChanged) == _semanticsChanged; 79 | } 80 | 81 | public static bool IsOverflow(StatusCode a) 82 | { 83 | return ((a.Value & _infoTypeMask) == _infoTypeDataValue) && ((a.Value & _overflow) == _overflow); 84 | } 85 | 86 | public override string ToString() 87 | { 88 | return $"0x{Value:X8}"; 89 | } 90 | 91 | public override bool Equals(object? obj) 92 | { 93 | return obj is StatusCode code && Equals(code); 94 | } 95 | 96 | public bool Equals(StatusCode other) 97 | { 98 | return Value == other.Value; 99 | } 100 | 101 | public override int GetHashCode() 102 | { 103 | return -1937169414 + Value.GetHashCode(); 104 | } 105 | } 106 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/StatusCodes.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="true" hostspecific="true" language="C#" 2 | #><#@ assembly name="System.Core" 3 | #><#@ import namespace="System.Linq" 4 | #><#@ import namespace="System.Text" 5 | #><#@ import namespace="System.Collections.Generic" 6 | #><#@ assembly name="System.Xml.dll" 7 | #><#@ import namespace="System.Xml" 8 | #><#@ import namespace="System.Xml" 9 | #><#@ import namespace="System.IO" 10 | #><#@ output extension="generated.cs" 11 | #><# 12 | var filename = this.Host.ResolvePath(@"Schema\StatusCodes.csv"); 13 | var comma = new[] { ',' }; 14 | var quote = new[] { '\"' }; 15 | var fields = 16 | from l in File.ReadAllLines(filename) 17 | let s = l.Split(comma, 3) 18 | select new { Name = s[0], Value = s[1], Message =s[2] }; 19 | #>// ------------------------------------------------------------------------------ 20 | // 21 | // This code was generated by a tool. 22 | // Runtime Version: 15.0.0.0 23 | // 24 | // Changes to this file may cause incorrect behavior and will be lost if 25 | // the code is regenerated. 26 | // 27 | // ------------------------------------------------------------------------------ 28 | namespace Workstation.ServiceModel.Ua 29 | { 30 | using System.Collections.Generic; 31 | 32 | public static class StatusCodes 33 | { 34 | /// The operation completed successfully. 35 | public const uint Good = 0x00000000u; 36 | <# foreach (var f in fields) { #> /// <#= f.Message.Trim(quote) #> 37 | public const uint <#= f.Name #> = <#= f.Value #>u; 38 | <# } #> public static string GetDefaultMessage(uint statusCode) 39 | { 40 | string value; 41 | if (DefaultMessages.TryGetValue(statusCode & 0xFFFF0000, out value)) 42 | { 43 | return value; 44 | } 45 | return "An unexpected error occurred."; 46 | } 47 | static Dictionary DefaultMessages = new Dictionary() 48 | { 49 | [0x00000000]="The operation completed successfully.", 50 | <# foreach (var f in fields) { #> [<#= f.Value #>]=<#= f.Message #>, 51 | <# } #> }; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/Structure.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Workstation.ServiceModel.Ua 5 | { 6 | /// 7 | /// A base implementation of a Structure. 8 | /// 9 | [DataTypeId(DataTypeIds.Structure)] 10 | public abstract class Structure : IEncodable 11 | { 12 | public abstract void Encode(IEncoder encoder); 13 | 14 | public abstract void Decode(IDecoder decoder); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/SubscriptionAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | 6 | namespace Workstation.ServiceModel.Ua 7 | { 8 | /// 9 | /// Specifies the Subscription that will be created for this viewmodel. 10 | /// 11 | [AttributeUsage(AttributeTargets.Class)] 12 | public sealed class SubscriptionAttribute : Attribute 13 | { 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | /// the endpoint url. 18 | /// the publishing interval. 19 | /// the number of PublishingIntervals before the server should return an empty Publish response. 20 | /// the number of PublishingIntervals before the server should delete the subscription. 21 | /// whether publishing is enabled. 22 | public SubscriptionAttribute(string endpointUrl, double publishingInterval = 1000f, uint keepAliveCount = 10, uint lifetimeCount = 0, bool publishingEnabled = true) 23 | { 24 | EndpointUrl = endpointUrl; 25 | PublishingInterval = publishingInterval; 26 | KeepAliveCount = keepAliveCount; 27 | LifetimeCount = lifetimeCount; 28 | PublishingEnabled = publishingEnabled; 29 | } 30 | 31 | /// 32 | /// Gets the endpoint url. 33 | /// 34 | public string EndpointUrl { get; } 35 | 36 | /// 37 | /// Gets the publishing interval. 38 | /// 39 | public double PublishingInterval { get; } 40 | 41 | /// 42 | /// Gets the number of PublishingIntervals before the server should return an empty Publish response. 43 | /// 44 | public uint KeepAliveCount { get; } 45 | 46 | /// 47 | /// Gets the number of PublishingIntervals before the server should delete the subscription. 48 | /// 49 | public uint LifetimeCount { get; } 50 | 51 | /// 52 | /// Gets a value indicating whether publishing is enabled. 53 | /// 54 | public bool PublishingEnabled { get; } 55 | } 56 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/TransportConnectionOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Workstation.ServiceModel.Ua 5 | { 6 | /// 7 | /// The transport connection options. 8 | /// 9 | public class TransportConnectionOptions 10 | { 11 | public const uint DefaultBufferSize = 64 * 1024; 12 | public const uint DefaultMaxMessageSize = 16 * 1024 * 1024; 13 | public const uint DefaultMaxChunkCount = 4 * 1024; 14 | 15 | /// 16 | /// Gets or sets the size of the receive buffer. 17 | /// 18 | public uint ReceiveBufferSize { get; set; } = DefaultBufferSize; 19 | 20 | /// 21 | /// Gets or sets the size of the send buffer. 22 | /// 23 | public uint SendBufferSize { get; set; } = DefaultBufferSize; 24 | 25 | /// 26 | /// Gets or sets the maximum total size of a message. 27 | /// 28 | public uint MaxMessageSize { get; set; } = DefaultMaxMessageSize; 29 | 30 | /// 31 | /// Gets or sets the maximum number of message chunks. 32 | /// 33 | public uint MaxChunkCount { get; set; } = DefaultMaxChunkCount; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/TransportProfileUris.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Workstation.ServiceModel.Ua 5 | { 6 | public static class TransportProfileUris 7 | { 8 | public const string UaTcpTransport = "http://opcfoundation.org/UA-Profile/Transport/uatcp-uasc-uabinary"; 9 | public const string HttpsXmlOrBinaryTransport = "http://opcfoundation.org/UA-Profile/Transport/https-uasoapxml-uabinary"; 10 | public const string HttpsXmlTransport = "http://opcfoundation.org/UA-Profile/Transport/https-uasoapxml"; 11 | public const string HttpsBinaryTransport = "http://opcfoundation.org/UA-Profile/Transport/https-uabinary"; 12 | } 13 | } -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/UaApplicationOptions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | using Workstation.ServiceModel.Ua; 8 | using Workstation.ServiceModel.Ua.Channels; 9 | 10 | namespace Workstation.ServiceModel.Ua 11 | { 12 | /// 13 | /// The UaApplication options. 14 | /// 15 | public class UaApplicationOptions : ClientSessionChannelOptions 16 | { 17 | } 18 | 19 | /// 20 | /// The options. 21 | /// 22 | public class ClientSessionChannelOptions : ClientSecureChannelOptions 23 | { 24 | /// 25 | /// Gets the requested number of milliseconds that a session may be unused before being closed by the server. 26 | /// 27 | public double SessionTimeout { get; set; } = ClientSessionChannel.DefaultSessionTimeout; 28 | } 29 | 30 | /// 31 | /// The options. 32 | /// 33 | public class ClientSecureChannelOptions : ClientTransportChannelOptions 34 | { 35 | /// 36 | /// Gets or sets the default number of milliseconds that may elapse before an operation is cancelled by the service. 37 | /// 38 | public uint TimeoutHint { get; set; } = ClientSecureChannel.DefaultTimeoutHint; 39 | 40 | /// 41 | /// Gets or sets the default diagnostics flags to be requested by the service. 42 | /// 43 | public uint DiagnosticsHint { get; set; } = ClientSecureChannel.DefaultDiagnosticsHint; 44 | } 45 | 46 | /// 47 | /// The options. 48 | /// 49 | public class ClientTransportChannelOptions 50 | { 51 | /// 52 | /// Gets or sets the size of the receive buffer. 53 | /// 54 | public uint LocalReceiveBufferSize { get; set; } = ClientTransportChannel.DefaultBufferSize; 55 | 56 | /// 57 | /// Gets or sets the size of the send buffer. 58 | /// 59 | public uint LocalSendBufferSize { get; set; } = ClientTransportChannel.DefaultBufferSize; 60 | 61 | /// 62 | /// Gets or sets the maximum total size of a message. 63 | /// 64 | public uint LocalMaxMessageSize { get; set; } = ClientTransportChannel.DefaultMaxMessageSize; 65 | 66 | /// 67 | /// Gets or sets the maximum number of message chunks. 68 | /// 69 | public uint LocalMaxChunkCount { get; set; } = ClientTransportChannel.DefaultMaxChunkCount; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/UserNameIdentity.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | namespace Workstation.ServiceModel.Ua 5 | { 6 | public class UserNameIdentity : IUserIdentity 7 | { 8 | public UserNameIdentity(string userName, string password) 9 | { 10 | this.UserName = userName; 11 | this.Password = password; 12 | } 13 | 14 | public string UserName { get; set; } 15 | 16 | public string Password { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/VariantExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Diagnostics.CodeAnalysis; 6 | using System.Linq; 7 | using System.Reflection; 8 | 9 | namespace Workstation.ServiceModel.Ua 10 | { 11 | public static class VariantExtensions 12 | { 13 | /// 14 | /// Gets the value of the Variant. 15 | /// 16 | /// The Variant. 17 | /// The value. 18 | public static object? GetValue(this Variant variant) 19 | { 20 | var value = variant.Value; 21 | switch (value) 22 | { 23 | case ExtensionObject obj: 24 | 25 | return obj.BodyType == BodyType.Encodable ? obj.Body : obj; 26 | 27 | case ExtensionObject[] objArray: 28 | 29 | return objArray.Select(e => e.BodyType == BodyType.Encodable ? e.Body : e).ToArray(); 30 | 31 | default: 32 | 33 | return value; 34 | } 35 | } 36 | 37 | /// 38 | /// Gets the value of the Variant, or the default value for the type. 39 | /// 40 | /// The expected type. 41 | /// The Variant. 42 | /// The value, if an instance of the specified Type, otherwise the Type's default value. 43 | [return: MaybeNull] 44 | public static T GetValueOrDefault(this Variant variant) 45 | { 46 | var value = variant.GetValue(); 47 | if (value != null) 48 | { 49 | if (value is T) 50 | { 51 | return (T)value; 52 | } 53 | } 54 | 55 | return default(T)!; 56 | } 57 | 58 | /// 59 | /// Gets the value of the Variant, or the specified default value. 60 | /// 61 | /// The expected type. 62 | /// A Variant 63 | /// A default value. 64 | /// The value, if an instance of the specified Type, otherwise the specified default value. 65 | [return: NotNullIfNotNull("defaultValue")] 66 | public static T GetValueOrDefault(this Variant variant, T defaultValue) 67 | { 68 | var value = variant.GetValue(); 69 | if (value != null) 70 | { 71 | if (value is T) 72 | { 73 | return (T)value; 74 | } 75 | } 76 | 77 | return defaultValue; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/ViewServiceSet.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Workstation.ServiceModel.Ua 9 | { 10 | public static class ViewServiceSet 11 | { 12 | /// 13 | /// Discovers the References of a specified Node. 14 | /// 15 | /// A instance of . 16 | /// A . 17 | /// A representing the asynchronous operation that returns a . 18 | /// OPC UA specification Part 4: Services, 5.8.2 19 | public static async Task BrowseAsync(this IRequestChannel channel, BrowseRequest request, CancellationToken token = default) 20 | { 21 | if (request == null) 22 | { 23 | throw new ArgumentNullException(nameof(request)); 24 | } 25 | 26 | return (BrowseResponse)await channel.RequestAsync(request, token).ConfigureAwait(false); 27 | } 28 | 29 | /// 30 | /// Requests the next set of Browse responses, when the information is too large to be sent in a single response. 31 | /// 32 | /// A instance of . 33 | /// A . 34 | /// A representing the asynchronous operation that returns a . 35 | /// OPC UA specification Part 4: Services, 5.8.3 36 | public static async Task BrowseNextAsync(this IRequestChannel channel, BrowseNextRequest request, CancellationToken token = default) 37 | { 38 | if (request == null) 39 | { 40 | throw new ArgumentNullException(nameof(request)); 41 | } 42 | 43 | return (BrowseNextResponse)await channel.RequestAsync(request, token).ConfigureAwait(false); 44 | } 45 | 46 | /// 47 | /// Translates one or more browse paths to NodeIds. 48 | /// 49 | /// A instance of . 50 | /// A . 51 | /// A representing the asynchronous operation that returns a . 52 | /// OPC UA specification Part 4: Services, 5.8.4 53 | public static async Task TranslateBrowsePathsToNodeIdsAsync(this IRequestChannel channel, TranslateBrowsePathsToNodeIdsRequest request, CancellationToken token = default) 54 | { 55 | if (request == null) 56 | { 57 | throw new ArgumentNullException(nameof(request)); 58 | } 59 | 60 | return (TranslateBrowsePathsToNodeIdsResponse)await channel.RequestAsync(request, token).ConfigureAwait(false); 61 | } 62 | 63 | /// 64 | /// Registers the Nodes that will be accessed repeatedly (e.g. Write, Call). 65 | /// 66 | /// A instance of . 67 | /// A . 68 | /// A representing the asynchronous operation that returns a . 69 | /// OPC UA specification Part 4: Services, 5.8.5 70 | public static async Task RegisterNodesAsync(this IRequestChannel channel, RegisterNodesRequest request, CancellationToken token = default) 71 | { 72 | if (request == null) 73 | { 74 | throw new ArgumentNullException(nameof(request)); 75 | } 76 | 77 | return (RegisterNodesResponse)await channel.RequestAsync(request, token).ConfigureAwait(false); 78 | } 79 | 80 | /// 81 | /// Unregisters NodeIds that have been obtained via the RegisterNodes service. 82 | /// 83 | /// A instance of . 84 | /// A . 85 | /// A representing the asynchronous operation that returns a . 86 | /// OPC UA specification Part 4: Services, 5.8.6 87 | public static async Task UnregisterNodesAsync(this IRequestChannel channel, UnregisterNodesRequest request, CancellationToken token = default) 88 | { 89 | if (request == null) 90 | { 91 | throw new ArgumentNullException(nameof(request)); 92 | } 93 | 94 | return (UnregisterNodesResponse)await channel.RequestAsync(request, token).ConfigureAwait(false); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/X509Identity.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using Org.BouncyCastle.Asn1.X509; 5 | using Org.BouncyCastle.Crypto; 6 | using Org.BouncyCastle.Crypto.Generators; 7 | using Org.BouncyCastle.Crypto.Operators; 8 | using Org.BouncyCastle.Crypto.Parameters; 9 | using Org.BouncyCastle.Math; 10 | using Org.BouncyCastle.OpenSsl; 11 | using Org.BouncyCastle.Pkix; 12 | using Org.BouncyCastle.Security; 13 | using Org.BouncyCastle.X509; 14 | using Org.BouncyCastle.X509.Extension; 15 | using Org.BouncyCastle.X509.Store; 16 | 17 | namespace Workstation.ServiceModel.Ua 18 | { 19 | public class X509Identity : IUserIdentity 20 | { 21 | public X509Identity(X509Certificate certificate, RsaKeyParameters privateKey) 22 | { 23 | this.Certificate = certificate; 24 | this.PrivateKey = privateKey; 25 | } 26 | 27 | public X509Certificate Certificate { get; } 28 | 29 | public RsaKeyParameters PrivateKey { get; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /UaClient/ServiceModel/Ua/XmlEncodingIdAttribute.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Converter Systems LLC. All rights reserved. 2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information. 3 | 4 | using System; 5 | 6 | namespace Workstation.ServiceModel.Ua 7 | { 8 | /// 9 | /// Attribute for classes of type IEncodable to indicate the xml encoding id. 10 | /// 11 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] 12 | public sealed class XmlEncodingIdAttribute : Attribute 13 | { 14 | public XmlEncodingIdAttribute(string s) 15 | { 16 | this.NodeId = ExpandedNodeId.Parse(s); 17 | } 18 | 19 | public ExpandedNodeId NodeId { get; } 20 | } 21 | } -------------------------------------------------------------------------------- /UaClient/Workstation.UaClient.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0;net6.0 5 | Workstation.UaClient 6 | Workstation 7 | 3.2.3 8 | Andrew Cullen 9 | Converter Systems LLC 10 | https://github.com/convertersystems/opc-ua-client 11 | 12 | A library to browse, read, write and subscribe to the live data published by the OPC UA servers on your network. 13 | opc, opc-ua, iiot 14 | https://github.com/convertersystems/opc-ua-client 15 | Copyright © 2023 Converter Systems LLC. 16 | 3.2.3.0 17 | Workstation.UaClient ($(TargetFramework)) 18 | true 19 | 3.2.3.0 20 | True 21 | Key.snk 22 | true 23 | true 24 | LICENSE.txt 25 | latest 26 | enable 27 | README.md 28 | 29 | 30 | 31 | 32 | 33 | True 34 | \ 35 | 36 | 37 | True 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | TextTemplatingFileGenerator 60 | AttributeIds.generated.cs 61 | 62 | 63 | TextTemplatingFileGenerator 64 | NodeIds.generated.cs 65 | 66 | 67 | TextTemplatingFileGenerator 68 | StatusCodes.generated.cs 69 | 70 | 71 | TextTemplatingFileGenerator 72 | Types.generated.cs 73 | 74 | 75 | 76 | 77 | 78 | True 79 | True 80 | AttributeIds.tt 81 | 82 | 83 | True 84 | True 85 | NodeIds.tt 86 | 87 | 88 | True 89 | True 90 | StatusCodes.tt 91 | 92 | 93 | True 94 | True 95 | Types.tt 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /UaClient/stylecop.json: -------------------------------------------------------------------------------- 1 | { 2 | // ACTION REQUIRED: This file was automatically added to your project, but it 3 | // will not take effect until additional steps are taken to enable it. See the 4 | // following page for additional information: 5 | // 6 | // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md 7 | 8 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", 9 | "settings": { 10 | "documentationRules": { 11 | "companyName": "Converter Systems LLC", 12 | "copyrightText": "Copyright (c) {companyName}. All rights reserved.\nLicensed under the MIT license. See LICENSE file in the project root for full license information.", 13 | "xmlHeader": false 14 | }, 15 | "orderingRules": { 16 | "elementOrder": [ 17 | "constant", 18 | "readonly" 19 | ], 20 | "usingDirectivesPlacement": "outsideNamespace", 21 | "systemUsingDirectivesFirst": true 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-hacker 2 | -------------------------------------------------------------------------------- /opc-ua-client.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29020.237 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Workstation.UaClient.UnitTests", "UaClient.UnitTests\Workstation.UaClient.UnitTests.csproj", "{8994DA00-5CE1-461F-963C-43F7CFC6864E}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A8EFACC3-6790-4CB0-8067-BE7574C94F09}" 9 | ProjectSection(SolutionItems) = preProject 10 | docs\index.html = docs\index.html 11 | LICENSE.txt = LICENSE.txt 12 | README.md = README.md 13 | robot6.jpg = robot6.jpg 14 | EndProjectSection 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Workstation.UaClient", "UaClient\Workstation.UaClient.csproj", "{04B8D717-4F15-4486-86E5-E33D8B8BF57D}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CustomTypeLibrary", "CustomTypeLibrary\CustomTypeLibrary.csproj", "{4908D255-ECC4-4846-B0A5-7799DE4524E6}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Debug|ARM = Debug|ARM 24 | Debug|x64 = Debug|x64 25 | Debug|x86 = Debug|x86 26 | Release|Any CPU = Release|Any CPU 27 | Release|ARM = Release|ARM 28 | Release|x64 = Release|x64 29 | Release|x86 = Release|x86 30 | EndGlobalSection 31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 32 | {8994DA00-5CE1-461F-963C-43F7CFC6864E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {8994DA00-5CE1-461F-963C-43F7CFC6864E}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {8994DA00-5CE1-461F-963C-43F7CFC6864E}.Debug|ARM.ActiveCfg = Debug|Any CPU 35 | {8994DA00-5CE1-461F-963C-43F7CFC6864E}.Debug|ARM.Build.0 = Debug|Any CPU 36 | {8994DA00-5CE1-461F-963C-43F7CFC6864E}.Debug|x64.ActiveCfg = Debug|Any CPU 37 | {8994DA00-5CE1-461F-963C-43F7CFC6864E}.Debug|x64.Build.0 = Debug|Any CPU 38 | {8994DA00-5CE1-461F-963C-43F7CFC6864E}.Debug|x86.ActiveCfg = Debug|Any CPU 39 | {8994DA00-5CE1-461F-963C-43F7CFC6864E}.Debug|x86.Build.0 = Debug|Any CPU 40 | {8994DA00-5CE1-461F-963C-43F7CFC6864E}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {8994DA00-5CE1-461F-963C-43F7CFC6864E}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {8994DA00-5CE1-461F-963C-43F7CFC6864E}.Release|ARM.ActiveCfg = Release|Any CPU 43 | {8994DA00-5CE1-461F-963C-43F7CFC6864E}.Release|ARM.Build.0 = Release|Any CPU 44 | {8994DA00-5CE1-461F-963C-43F7CFC6864E}.Release|x64.ActiveCfg = Release|Any CPU 45 | {8994DA00-5CE1-461F-963C-43F7CFC6864E}.Release|x64.Build.0 = Release|Any CPU 46 | {8994DA00-5CE1-461F-963C-43F7CFC6864E}.Release|x86.ActiveCfg = Release|Any CPU 47 | {8994DA00-5CE1-461F-963C-43F7CFC6864E}.Release|x86.Build.0 = Release|Any CPU 48 | {04B8D717-4F15-4486-86E5-E33D8B8BF57D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {04B8D717-4F15-4486-86E5-E33D8B8BF57D}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {04B8D717-4F15-4486-86E5-E33D8B8BF57D}.Debug|ARM.ActiveCfg = Debug|Any CPU 51 | {04B8D717-4F15-4486-86E5-E33D8B8BF57D}.Debug|ARM.Build.0 = Debug|Any CPU 52 | {04B8D717-4F15-4486-86E5-E33D8B8BF57D}.Debug|x64.ActiveCfg = Debug|Any CPU 53 | {04B8D717-4F15-4486-86E5-E33D8B8BF57D}.Debug|x64.Build.0 = Debug|Any CPU 54 | {04B8D717-4F15-4486-86E5-E33D8B8BF57D}.Debug|x86.ActiveCfg = Debug|Any CPU 55 | {04B8D717-4F15-4486-86E5-E33D8B8BF57D}.Debug|x86.Build.0 = Debug|Any CPU 56 | {04B8D717-4F15-4486-86E5-E33D8B8BF57D}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {04B8D717-4F15-4486-86E5-E33D8B8BF57D}.Release|Any CPU.Build.0 = Release|Any CPU 58 | {04B8D717-4F15-4486-86E5-E33D8B8BF57D}.Release|ARM.ActiveCfg = Release|Any CPU 59 | {04B8D717-4F15-4486-86E5-E33D8B8BF57D}.Release|ARM.Build.0 = Release|Any CPU 60 | {04B8D717-4F15-4486-86E5-E33D8B8BF57D}.Release|x64.ActiveCfg = Release|Any CPU 61 | {04B8D717-4F15-4486-86E5-E33D8B8BF57D}.Release|x64.Build.0 = Release|Any CPU 62 | {04B8D717-4F15-4486-86E5-E33D8B8BF57D}.Release|x86.ActiveCfg = Release|Any CPU 63 | {04B8D717-4F15-4486-86E5-E33D8B8BF57D}.Release|x86.Build.0 = Release|Any CPU 64 | {4908D255-ECC4-4846-B0A5-7799DE4524E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 65 | {4908D255-ECC4-4846-B0A5-7799DE4524E6}.Debug|Any CPU.Build.0 = Debug|Any CPU 66 | {4908D255-ECC4-4846-B0A5-7799DE4524E6}.Debug|ARM.ActiveCfg = Debug|Any CPU 67 | {4908D255-ECC4-4846-B0A5-7799DE4524E6}.Debug|ARM.Build.0 = Debug|Any CPU 68 | {4908D255-ECC4-4846-B0A5-7799DE4524E6}.Debug|x64.ActiveCfg = Debug|Any CPU 69 | {4908D255-ECC4-4846-B0A5-7799DE4524E6}.Debug|x64.Build.0 = Debug|Any CPU 70 | {4908D255-ECC4-4846-B0A5-7799DE4524E6}.Debug|x86.ActiveCfg = Debug|Any CPU 71 | {4908D255-ECC4-4846-B0A5-7799DE4524E6}.Debug|x86.Build.0 = Debug|Any CPU 72 | {4908D255-ECC4-4846-B0A5-7799DE4524E6}.Release|Any CPU.ActiveCfg = Release|Any CPU 73 | {4908D255-ECC4-4846-B0A5-7799DE4524E6}.Release|Any CPU.Build.0 = Release|Any CPU 74 | {4908D255-ECC4-4846-B0A5-7799DE4524E6}.Release|ARM.ActiveCfg = Release|Any CPU 75 | {4908D255-ECC4-4846-B0A5-7799DE4524E6}.Release|ARM.Build.0 = Release|Any CPU 76 | {4908D255-ECC4-4846-B0A5-7799DE4524E6}.Release|x64.ActiveCfg = Release|Any CPU 77 | {4908D255-ECC4-4846-B0A5-7799DE4524E6}.Release|x64.Build.0 = Release|Any CPU 78 | {4908D255-ECC4-4846-B0A5-7799DE4524E6}.Release|x86.ActiveCfg = Release|Any CPU 79 | {4908D255-ECC4-4846-B0A5-7799DE4524E6}.Release|x86.Build.0 = Release|Any CPU 80 | EndGlobalSection 81 | GlobalSection(SolutionProperties) = preSolution 82 | HideSolutionNode = FALSE 83 | EndGlobalSection 84 | GlobalSection(ExtensibilityGlobals) = postSolution 85 | SolutionGuid = {74A9B17D-6375-49B4-9A35-B4C99D77C538} 86 | EndGlobalSection 87 | EndGlobal 88 | -------------------------------------------------------------------------------- /robot6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/convertersystems/opc-ua-client/10948088eecf3dc38a484e0a8fc5153cf3c2ed21/robot6.jpg --------------------------------------------------------------------------------