Skip to content

Commit 3f660af

Browse files
Copilotromanett
andauthored
Fix BaseVariableState to return correct BuiltInType for Byte arrays vs ByteString (#3384)
* Initial plan * Fix ByteString vs Byte array distinction in WrappedValue Modified BaseVariableState.WrappedValue to use DataType and ValueRank when creating Variant to correctly distinguish between byte[] (Byte array) and ByteString. Added tests to verify the fix. Co-authored-by: romanett <[email protected]> * Address code review feedback - Remove redundant null check for NodeId (NodeId.IsNull handles null) - Update comment in test to be more specific about ByteString testing Co-authored-by: romanett <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: romanett <[email protected]>
1 parent 226dd78 commit 3f660af

File tree

2 files changed

+93
-1
lines changed

2 files changed

+93
-1
lines changed

Stack/Opc.Ua.Types/State/BaseVariableState.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,21 @@ public object Value
556556
[DataMember(Name = "Value", Order = 0, IsRequired = false, EmitDefaultValue = false)]
557557
public Variant WrappedValue
558558
{
559-
get => new(m_value);
559+
get
560+
{
561+
// If we have a valid DataType and ValueRank, use them to construct the TypeInfo
562+
// This is necessary to distinguish between byte[] (Byte array) and ByteString
563+
if (m_value != null && !NodeId.IsNull(m_dataType))
564+
{
565+
BuiltInType builtInType = TypeInfo.GetBuiltInType(m_dataType);
566+
if (builtInType != BuiltInType.Null)
567+
{
568+
TypeInfo typeInfo = TypeInfo.Create(builtInType, m_valueRank);
569+
return new Variant(m_value, typeInfo);
570+
}
571+
}
572+
return new Variant(m_value);
573+
}
560574
set => Value = ExtractValueFromVariant(null, value.Value, false);
561575
}
562576

Tests/Opc.Ua.Core.Tests/Stack/State/BaseDataVariableTypeStateSerializationTest.cs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,5 +125,83 @@ public void ValueRankPersistBaseVariableState(
125125

126126
Assert.AreEqual(valueRank, loadedVariable.ValueRank);
127127
}
128+
129+
/// <summary>
130+
/// Test that byte[] array is correctly returned as Byte array type, not ByteString.
131+
/// </summary>
132+
[Test]
133+
public void ByteArrayWrappedValueReturnsCorrectBuiltInType()
134+
{
135+
ITelemetryContext telemetry = NUnitTelemetryContext.Create();
136+
137+
// Create a BaseDataVariableState with byte[] value
138+
var variableState = new BaseDataVariableState<byte[]>(null);
139+
var serviceMessageContext = new ServiceMessageContext(telemetry);
140+
var systemContext = new SystemContext(telemetry)
141+
{
142+
NamespaceUris = serviceMessageContext.NamespaceUris
143+
};
144+
145+
variableState.Create(
146+
systemContext,
147+
new NodeId(1000),
148+
new QualifiedName("ByteArrayVariable"),
149+
new LocalizedText("ByteArrayVariable"),
150+
true);
151+
152+
// Set DataType to Byte and ValueRank to OneDimension (array)
153+
variableState.DataType = DataTypeIds.Byte;
154+
variableState.ValueRank = ValueRanks.OneDimension;
155+
156+
// Set a byte array value
157+
byte[] testValue = new byte[] { 1, 2, 3, 4, 5 };
158+
variableState.Value = testValue;
159+
160+
// Get the WrappedValue and verify it's a Byte array, not ByteString
161+
Variant wrappedValue = variableState.WrappedValue;
162+
163+
Assert.AreEqual(BuiltInType.Byte, wrappedValue.TypeInfo.BuiltInType, "BuiltInType should be Byte");
164+
Assert.AreEqual(ValueRanks.OneDimension, wrappedValue.TypeInfo.ValueRank, "ValueRank should be OneDimension");
165+
Assert.AreEqual(testValue, wrappedValue.Value, "Value should match the original byte array");
166+
}
167+
168+
/// <summary>
169+
/// Test that ByteString is still correctly returned as ByteString type.
170+
/// </summary>
171+
[Test]
172+
public void ByteStringWrappedValueReturnsCorrectBuiltInType()
173+
{
174+
ITelemetryContext telemetry = NUnitTelemetryContext.Create();
175+
176+
// Create a BaseDataVariableState for ByteString testing
177+
var variableState = new BaseDataVariableState<byte[]>(null);
178+
var serviceMessageContext = new ServiceMessageContext(telemetry);
179+
var systemContext = new SystemContext(telemetry)
180+
{
181+
NamespaceUris = serviceMessageContext.NamespaceUris
182+
};
183+
184+
variableState.Create(
185+
systemContext,
186+
new NodeId(1001),
187+
new QualifiedName("ByteStringVariable"),
188+
new LocalizedText("ByteStringVariable"),
189+
true);
190+
191+
// Set DataType to ByteString and ValueRank to Scalar
192+
variableState.DataType = DataTypeIds.ByteString;
193+
variableState.ValueRank = ValueRanks.Scalar;
194+
195+
// Set a byte array value (which represents a ByteString)
196+
byte[] testValue = new byte[] { 1, 2, 3, 4, 5 };
197+
variableState.Value = testValue;
198+
199+
// Get the WrappedValue and verify it's a ByteString
200+
Variant wrappedValue = variableState.WrappedValue;
201+
202+
Assert.AreEqual(BuiltInType.ByteString, wrappedValue.TypeInfo.BuiltInType, "BuiltInType should be ByteString");
203+
Assert.AreEqual(ValueRanks.Scalar, wrappedValue.TypeInfo.ValueRank, "ValueRank should be Scalar");
204+
Assert.AreEqual(testValue, wrappedValue.Value, "Value should match the original byte array");
205+
}
128206
}
129207
}

0 commit comments

Comments
 (0)