Skip to content

Commit f66ceff

Browse files
authored
Immutable built in types and code generation (#3534)
Generate stack and application code using source generators Change built in types to readonly structs Update localized text implementation to use JSON text serializer Status code with symbolic names Immutable node ids No boxing for all built in types <= 8 bytes in Variant. Split user identity token and token handling via token handler Add migration guide for breaking changes
1 parent 029a8fb commit f66ceff

File tree

658 files changed

+167339
-718126
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

658 files changed

+167339
-718126
lines changed

.editorconfig

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,11 @@ dotnet_diagnostic.CA1724.severity =
439439
dotnet_diagnostic.CA2326.severity = none
440440
dotnet_diagnostic.CA2327.severity = none
441441

442+
dotnet_diagnostic.CA5390.severity = none
443+
dotnet_diagnostic.CA5403.severity = none
444+
dotnet_code_quality.CA5390.excluded_symbol_names = Initializers
445+
dotnet_code_quality.CA5403.excluded_symbol_names = Initializers
446+
442447
# TODO Fix as P1:
443448

444449
# CA2213: Disposable fields should be disposed
@@ -1052,18 +1057,6 @@ dotnet_diagnostic.UA_NETStandard_1.severity = none
10521057

10531058
# exclude generated code
10541059

1055-
[**/Generated/*.cs]
1056-
generated_code = true
1057-
dotnet_diagnostic.severity = none
1058-
dotnet_analyzer.severity = none
1059-
#dotnet_analyzer_diagnostic.category-roslynator.severity = none
1060-
1061-
[**/*.{Classes,DataTypes,Constants,StatusCodes,Attributes}.cs]
1062-
generated_code = true
1063-
dotnet_diagnostic.severity = none
1064-
dotnet_analyzer.severity = none
1065-
#dotnet_analyzer_diagnostic.category-roslynator.severity = none
1066-
10671060
[**/OPCBinarySchema.cs]
10681061
generated_code = true
10691062
dotnet_diagnostic.severity = none

.github/workflows/buildandtest.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
matrix:
2222
# os: [ubuntu-latest, windows-latest, macOS-latest] - disable mac os due to cost
2323
os: [ubuntu-latest, windows-latest]
24-
csproj: [Security.Certificates, Types, Core, Server, Client, Client.ComplexTypes, PubSub, Configuration, Gds]
24+
csproj: [Security.Certificates, Types, Core, Server, Client, Client.ComplexTypes, PubSub, Configuration, Gds, SourceGeneration.Stack, SourceGeneration, SourceGeneration.Core]
2525
include:
2626
- framework: 'net10.0'
2727
dotnet-version: '10.0.x'
@@ -51,11 +51,11 @@ jobs:
5151
run: ./.azurepipelines/set-version.ps1
5252

5353
- name: Build
54-
run: dotnet build ${{ env.CSPROJECT }} --force --framework ${{ matrix.framework }} --configuration ${{ matrix.configuration }} /p:CustomTestTarget=${{ matrix.customtesttarget }}
54+
run: dotnet build ${{ env.CSPROJECT }} --framework ${{ matrix.framework }} --configuration ${{ matrix.configuration }} /p:CustomTestTarget=${{ matrix.customtesttarget }}
5555

5656
- name: Test
5757
# note: /p:CollectCoverage=true is only used to disable deterministic builds
58-
run: dotnet test ${{ env.CSPROJECT }} --no-build --framework ${{ matrix.framework }} --logger trx --configuration ${{ matrix.configuration }} /p:CollectCoverage=true /p:CustomTestTarget=${{ matrix.customtesttarget }} --collect:"XPlat Code Coverage" --settings ./Tests/coverlet.runsettings.xml --results-directory ${{ env.TESTRESULTS }}
58+
run: dotnet test ${{ env.CSPROJECT }} --no-build --framework ${{ matrix.framework }} --logger trx --configuration ${{ matrix.configuration }} /p:CollectCoverage=true /p:CustomTestTarget=${{ matrix.customtesttarget }} --collect:"XPlat Code Coverage" --settings ./Tests/coverlet.runsettings.xml --results-directory ${{ env.TESTRESULTS }}
5959

6060
- name: Upload test results
6161
uses: actions/upload-artifact@v6

Applications/ConsoleReferenceClient/ClientSamples.cs

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
using System.Globalization;
3535
using System.IO;
3636
using System.Linq;
37-
using System.Reflection;
3837
using System.Text;
3938
using System.Threading;
4039
using System.Threading.Tasks;
@@ -83,19 +82,19 @@ public ClientSamples(
8382

8483
m_desiredEventFields.Add(
8584
eventIndexCounter++,
86-
[.. new QualifiedName[] { BrowseNames.Time }]);
85+
[.. new QualifiedName[] { QualifiedName.From(BrowseNames.Time) }]);
8786
m_desiredEventFields.Add(
8887
eventIndexCounter++,
89-
[.. new QualifiedName[] { BrowseNames.ActiveState }]);
88+
[.. new QualifiedName[] { QualifiedName.From(BrowseNames.ActiveState) }]);
9089
m_desiredEventFields.Add(
9190
eventIndexCounter++,
92-
[.. new QualifiedName[] { BrowseNames.Message }]);
91+
[.. new QualifiedName[] { QualifiedName.From(BrowseNames.Message) }]);
9392
m_desiredEventFields.Add(
9493
eventIndexCounter++,
95-
[.. new QualifiedName[] { BrowseNames.LimitState, BrowseNames.CurrentState }]);
94+
[.. new QualifiedName[] { QualifiedName.From(BrowseNames.LimitState), QualifiedName.From(BrowseNames.CurrentState) }]);
9695
m_desiredEventFields.Add(
9796
eventIndexCounter++,
98-
[.. new QualifiedName[] { BrowseNames.LimitState, BrowseNames.LastTransition }]);
97+
[.. new QualifiedName[] { QualifiedName.From(BrowseNames.LimitState), QualifiedName.From(BrowseNames.LastTransition) }]);
9998
}
10099

101100
/// <summary>
@@ -526,7 +525,7 @@ public async Task<bool> SubscribeToDataChangesAsync(
526525
{
527526
AttributeId = Attributes.Value,
528527
TypeDefinitionId = ObjectTypeIds.ExclusiveLevelAlarmType,
529-
BrowsePath = new QualifiedNameCollection(["EventType"])
528+
BrowsePath = [QualifiedName.From("EventType")]
530529
};
531530
var desiredEventType = new LiteralOperand
532531
{
@@ -749,7 +748,7 @@ public async Task<ReferenceDescriptionCollection> ManagedBrowseFullAddressSpaceA
749748
}
750749
}
751750

752-
var nodesToBrowse = new List<NodeId> { startingNode ?? ObjectIds.RootFolder };
751+
var nodesToBrowse = new List<NodeId> { startingNode.IsNull ? ObjectIds.RootFolder : startingNode };
753752

754753
const int kMaxReferencesPerNode = 1000;
755754

@@ -905,7 +904,7 @@ public async Task<ReferenceDescriptionCollection> BrowseFullAddressSpaceAsync(
905904
browseDescription
906905
?? new BrowseDescription
907906
{
908-
NodeId = startingNode ?? ObjectIds.RootFolder,
907+
NodeId = startingNode.IsNull ? ObjectIds.RootFolder : startingNode,
909908
BrowseDirection = BrowseDirection.Forward,
910909
ReferenceTypeId = ReferenceTypeIds.HierarchicalReferences,
911910
IncludeSubtypes = true,
@@ -914,7 +913,7 @@ public async Task<ReferenceDescriptionCollection> BrowseFullAddressSpaceAsync(
914913
};
915914
BrowseDescriptionCollection browseDescriptionCollection
916915
= CreateBrowseDescriptionCollectionFromNodeId(
917-
[.. new NodeId[] { startingNode ?? ObjectIds.RootFolder }],
916+
[.. new NodeId[] { startingNode.IsNull ? ObjectIds.RootFolder : startingNode }],
918917
browseTemplate);
919918

920919
// Browse
@@ -1167,16 +1166,9 @@ private static Task FetchReferenceIdTypesAsync(
11671166
ISession session,
11681167
CancellationToken ct = default)
11691168
{
1170-
// fetch the reference types first, otherwise browse for e.g. hierarchical
1171-
// references with subtypes won't work
1172-
const BindingFlags bindingFlags = BindingFlags.Instance |
1173-
BindingFlags.Static |
1174-
BindingFlags.Public;
11751169
NamespaceTable namespaceUris = session.NamespaceUris;
1176-
IEnumerable<ExpandedNodeId> referenceTypes = typeof(ReferenceTypeIds)
1177-
.GetFields(bindingFlags)
1178-
.Select(
1179-
field => NodeId.ToExpandedNodeId((NodeId)field.GetValue(null), namespaceUris));
1170+
IEnumerable<ExpandedNodeId> referenceTypes = ReferenceTypeIds.Identifiers
1171+
.Select(nodeId => NodeId.ToExpandedNodeId(nodeId, namespaceUris));
11801172
return session.FetchTypeTreeAsync([.. referenceTypes], ct);
11811173
}
11821174

@@ -1336,7 +1328,7 @@ public async Task SubscribeAllValuesAsync(
13361328
StartNodeId = item.NodeId,
13371329
AttributeId = Attributes.Value,
13381330
SamplingInterval = samplingInterval,
1339-
DisplayName = item.DisplayName?.Text ?? item.BrowseName?.Name ?? "unknown",
1331+
DisplayName = item.DisplayName.Text ?? item.BrowseName.Name ?? "unknown",
13401332
QueueSize = queueSize,
13411333
DiscardOldest = true,
13421334
MonitoringMode = MonitoringMode.Reporting
@@ -1505,7 +1497,7 @@ private void OnMonitoredItemEventNotification(
15051497
{
15061498
try
15071499
{
1508-
var currentTime = (DateTime)field.Value;
1500+
DateTime currentTime = field.GetDateTime();
15091501
TimeSpan timeSpan = currentTime - m_lastEventTime;
15101502
m_lastEventTime = currentTime;
15111503
m_processedEvents++;
@@ -1534,7 +1526,7 @@ private void OnMonitoredItemEventNotification(
15341526
"\tField [{Index}] \"{Name}\" = [{Value}]",
15351527
entry.Key,
15361528
fieldName,
1537-
field.Value);
1529+
field);
15381530
}
15391531
}
15401532
}
@@ -1666,8 +1658,9 @@ public async Task<IReadOnlyDictionary<string, string>> ExportNodesToNodeSet2PerN
16661658
{
16671659
string namespaceUri = session.NamespaceUris.GetString(group.Key);
16681660
// Exclude OPC Foundation companion specifications
1669-
return !string.IsNullOrEmpty(namespaceUri) &&
1670-
!namespaceUri.StartsWith("http://opcfoundation.org/UA/", StringComparison.OrdinalIgnoreCase);
1661+
return
1662+
!string.IsNullOrEmpty(namespaceUri) &&
1663+
!namespaceUri.StartsWith(Namespaces.OpcUa, StringComparison.OrdinalIgnoreCase);
16711664
})
16721665
.ToDictionary(
16731666
group => group.Key,

Applications/ConsoleReferencePublisher/Program.cs

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -212,16 +212,15 @@ private static PubSubConfigurationDataType CreatePublisherConfiguration_UdpUadp(
212212
pubSubConnection1.Address = new ExtensionObject(address);
213213

214214
// configure custom DiscoveryAddress for Discovery messages
215-
pubSubConnection1.TransportSettings = new ExtensionObject
216-
{
217-
Body = new DatagramConnectionTransportDataType
215+
pubSubConnection1.TransportSettings = new ExtensionObject(
216+
new DatagramConnectionTransportDataType
218217
{
219-
DiscoveryAddress = new ExtensionObject
220-
{
221-
Body = new NetworkAddressUrlDataType { Url = "opc.udp://224.0.2.15:4840" }
222-
}
223-
}
224-
};
218+
DiscoveryAddress = new ExtensionObject(
219+
new NetworkAddressUrlDataType
220+
{
221+
Url = "opc.udp://224.0.2.15:4840"
222+
})
223+
});
225224

226225
var writerGroup1 = new WriterGroupDataType
227226
{
@@ -615,31 +614,31 @@ private static PublishedDataSetDataType CreatePublishedDataSetSimple()
615614
new FieldMetaData
616615
{
617616
Name = "BoolToggle",
618-
DataSetFieldId = new Uuid(Guid.NewGuid()),
617+
DataSetFieldId = Uuid.NewUuid(),
619618
BuiltInType = (byte)DataTypes.Boolean,
620619
DataType = DataTypeIds.Boolean,
621620
ValueRank = ValueRanks.Scalar
622621
},
623622
new FieldMetaData
624623
{
625624
Name = "Int32",
626-
DataSetFieldId = new Uuid(Guid.NewGuid()),
625+
DataSetFieldId = Uuid.NewUuid(),
627626
BuiltInType = (byte)DataTypes.Int32,
628627
DataType = DataTypeIds.Int32,
629628
ValueRank = ValueRanks.Scalar
630629
},
631630
new FieldMetaData
632631
{
633632
Name = "Int32Fast",
634-
DataSetFieldId = new Uuid(Guid.NewGuid()),
633+
DataSetFieldId = Uuid.NewUuid(),
635634
BuiltInType = (byte)DataTypes.Int32,
636635
DataType = DataTypeIds.Int32,
637636
ValueRank = ValueRanks.Scalar
638637
},
639638
new FieldMetaData
640639
{
641640
Name = "DateTime",
642-
DataSetFieldId = new Uuid(Guid.NewGuid()),
641+
DataSetFieldId = Uuid.NewUuid(),
643642
BuiltInType = (byte)DataTypes.DateTime,
644643
DataType = DataTypeIds.DateTime,
645644
ValueRank = ValueRanks.Scalar
@@ -700,119 +699,119 @@ private static PublishedDataSetDataType CreatePublishedDataSetAllTypes()
700699
new FieldMetaData
701700
{
702701
Name = "BoolToggle",
703-
DataSetFieldId = new Uuid(Guid.NewGuid()),
702+
DataSetFieldId = Uuid.NewUuid(),
704703
BuiltInType = (byte)DataTypes.Boolean,
705704
DataType = DataTypeIds.Boolean,
706705
ValueRank = ValueRanks.Scalar
707706
},
708707
new FieldMetaData
709708
{
710709
Name = "Byte",
711-
DataSetFieldId = new Uuid(Guid.NewGuid()),
710+
DataSetFieldId = Uuid.NewUuid(),
712711
BuiltInType = (byte)DataTypes.Byte,
713712
DataType = DataTypeIds.Byte,
714713
ValueRank = ValueRanks.Scalar
715714
},
716715
new FieldMetaData
717716
{
718717
Name = "Int16",
719-
DataSetFieldId = new Uuid(Guid.NewGuid()),
718+
DataSetFieldId = Uuid.NewUuid(),
720719
BuiltInType = (byte)DataTypes.Int16,
721720
DataType = DataTypeIds.Int16,
722721
ValueRank = ValueRanks.Scalar
723722
},
724723
new FieldMetaData
725724
{
726725
Name = "Int32",
727-
DataSetFieldId = new Uuid(Guid.NewGuid()),
726+
DataSetFieldId = Uuid.NewUuid(),
728727
BuiltInType = (byte)DataTypes.Int32,
729728
DataType = DataTypeIds.Int32,
730729
ValueRank = ValueRanks.Scalar
731730
},
732731
new FieldMetaData
733732
{
734733
Name = "SByte",
735-
DataSetFieldId = new Uuid(Guid.NewGuid()),
734+
DataSetFieldId = Uuid.NewUuid(),
736735
BuiltInType = (byte)DataTypes.SByte,
737736
DataType = DataTypeIds.SByte,
738737
ValueRank = ValueRanks.Scalar
739738
},
740739
new FieldMetaData
741740
{
742741
Name = "UInt16",
743-
DataSetFieldId = new Uuid(Guid.NewGuid()),
742+
DataSetFieldId = Uuid.NewUuid(),
744743
BuiltInType = (byte)DataTypes.UInt16,
745744
DataType = DataTypeIds.UInt16,
746745
ValueRank = ValueRanks.Scalar
747746
},
748747
new FieldMetaData
749748
{
750749
Name = "UInt32",
751-
DataSetFieldId = new Uuid(Guid.NewGuid()),
750+
DataSetFieldId = Uuid.NewUuid(),
752751
BuiltInType = (byte)DataTypes.UInt32,
753752
DataType = DataTypeIds.UInt32,
754753
ValueRank = ValueRanks.Scalar
755754
},
756755
new FieldMetaData
757756
{
758757
Name = "UInt64",
759-
DataSetFieldId = new Uuid(Guid.NewGuid()),
758+
DataSetFieldId = Uuid.NewUuid(),
760759
BuiltInType = (byte)DataTypes.UInt64,
761760
DataType = DataTypeIds.UInt64,
762761
ValueRank = ValueRanks.Scalar
763762
},
764763
new FieldMetaData
765764
{
766765
Name = "Float",
767-
DataSetFieldId = new Uuid(Guid.NewGuid()),
766+
DataSetFieldId = Uuid.NewUuid(),
768767
BuiltInType = (byte)DataTypes.Float,
769768
DataType = DataTypeIds.Float,
770769
ValueRank = ValueRanks.Scalar
771770
},
772771
new FieldMetaData
773772
{
774773
Name = "Double",
775-
DataSetFieldId = new Uuid(Guid.NewGuid()),
774+
DataSetFieldId = Uuid.NewUuid(),
776775
BuiltInType = (byte)DataTypes.Double,
777776
DataType = DataTypeIds.Double,
778777
ValueRank = ValueRanks.Scalar
779778
},
780779
new FieldMetaData
781780
{
782781
Name = "String",
783-
DataSetFieldId = new Uuid(Guid.NewGuid()),
782+
DataSetFieldId = Uuid.NewUuid(),
784783
BuiltInType = (byte)DataTypes.String,
785784
DataType = DataTypeIds.String,
786785
ValueRank = ValueRanks.Scalar
787786
},
788787
new FieldMetaData
789788
{
790789
Name = "ByteString",
791-
DataSetFieldId = new Uuid(Guid.NewGuid()),
790+
DataSetFieldId = Uuid.NewUuid(),
792791
BuiltInType = (byte)DataTypes.ByteString,
793792
DataType = DataTypeIds.ByteString,
794793
ValueRank = ValueRanks.Scalar
795794
},
796795
new FieldMetaData
797796
{
798797
Name = "Guid",
799-
DataSetFieldId = new Uuid(Guid.NewGuid()),
798+
DataSetFieldId = Uuid.NewUuid(),
800799
BuiltInType = (byte)DataTypes.Guid,
801800
DataType = DataTypeIds.Guid,
802801
ValueRank = ValueRanks.Scalar
803802
},
804803
new FieldMetaData
805804
{
806805
Name = "DateTime",
807-
DataSetFieldId = new Uuid(Guid.NewGuid()),
806+
DataSetFieldId = Uuid.NewUuid(),
808807
BuiltInType = (byte)DataTypes.DateTime,
809808
DataType = DataTypeIds.DateTime,
810809
ValueRank = ValueRanks.Scalar
811810
},
812811
new FieldMetaData
813812
{
814813
Name = "UInt32Array",
815-
DataSetFieldId = new Uuid(Guid.NewGuid()),
814+
DataSetFieldId = Uuid.NewUuid(),
816815
BuiltInType = (byte)DataTypes.UInt32,
817816
DataType = DataTypeIds.UInt32,
818817
ValueRank = ValueRanks.OneDimension

Applications/ConsoleReferencePublisher/PublishedValuesWrites.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ private void LoadInitialData()
244244
WriteFieldData(
245245
"Guid",
246246
NamespaceIndexAllTypes,
247-
new DataValue(new Variant(Guid.NewGuid()), StatusCodes.Good, DateTime.UtcNow)
247+
new DataValue(new Variant(Uuid.NewUuid()), StatusCodes.Good, DateTime.UtcNow)
248248
);
249249
WriteFieldData(
250250
"DateTime",
@@ -502,7 +502,7 @@ private void IncrementValue(
502502
case BuiltInType.Guid:
503503
if (variable.ValueRank == ValueRanks.Scalar)
504504
{
505-
dataValue.Value = Guid.NewGuid();
505+
dataValue.Value = Uuid.NewUuid();
506506
valueUpdated = true;
507507
}
508508
break;

0 commit comments

Comments
 (0)