diff --git a/opc-ua-stack/encoding-json/src/main/java/org/eclipse/milo/opcua/stack/core/encoding/json/OpcUaJsonEncoder.java b/opc-ua-stack/encoding-json/src/main/java/org/eclipse/milo/opcua/stack/core/encoding/json/OpcUaJsonEncoder.java index e03423340..90c4f874e 100644 --- a/opc-ua-stack/encoding-json/src/main/java/org/eclipse/milo/opcua/stack/core/encoding/json/OpcUaJsonEncoder.java +++ b/opc-ua-stack/encoding-json/src/main/java/org/eclipse/milo/opcua/stack/core/encoding/json/OpcUaJsonEncoder.java @@ -913,120 +913,174 @@ private void encodeVariantValue(@NonNull Object value) throws IOException { } private void encodeVariantBodyValue(Object value, TypeHint typeHint, int typeId) { + OpcUaDataType dataType = OpcUaDataType.fromTypeId(typeId); + if (dataType == null) { + throw new UaSerializationException( + StatusCodes.Bad_EncodingError, "unknown typeId: " + typeId); + } switch (typeHint) { case BUILTIN: { - encodeBuiltinTypeValue(null, typeId, value); + encodeBuiltinType(null, value, dataType); break; } case ENUM: { Object enumValue = ((UaEnumeratedType) value).getValue(); - encodeBuiltinTypeValue(null, typeId, enumValue); + encodeBuiltinType(null, enumValue, dataType); break; } case STRUCT: { UaStructuredType struct = (UaStructuredType) value; ExtensionObject xo = ExtensionObject.encode(encodingContext, struct); - encodeBuiltinTypeValue(null, typeId, xo); + encodeBuiltinType(null, xo, dataType); break; } case OPTION_SET: { Object optionSetValue = ((OptionSetUInteger) value).getValue(); - encodeBuiltinTypeValue(null, typeId, optionSetValue); + encodeBuiltinType(null, optionSetValue, dataType); break; } } } - private void encodeBuiltinTypeValue(String field, int typeId, Object value) - throws UaSerializationException { + @Override + public void encodeBuiltinType(String field, Object value, OpcUaDataType dataType) + throws UaSerializationException { contextPush(EncoderContext.BUILTIN); - switch (typeId) { - case 1: - encodeBoolean(field, (Boolean) value); - break; - case 2: - encodeSByte(field, (Byte) value); - break; - case 3: - encodeByte(field, (UByte) value); - break; - case 4: - encodeInt16(field, (Short) value); - break; - case 5: - encodeUInt16(field, (UShort) value); - break; - case 6: - encodeInt32(field, (Integer) value); - break; - case 7: - encodeUInt32(field, (UInteger) value); - break; - case 8: - encodeInt64(field, (Long) value); - break; - case 9: - encodeUInt64(field, (ULong) value); - break; - case 10: - encodeFloat(field, (Float) value); - break; - case 11: - encodeDouble(field, (Double) value); - break; - case 12: - encodeString(field, (String) value); - break; - case 13: - encodeDateTime(field, (DateTime) value); - break; - case 14: - encodeGuid(field, (UUID) value); - break; - case 15: - encodeByteString(field, (ByteString) value); - break; - case 16: - encodeXmlElement(field, (XmlElement) value); - break; - case 17: - encodeNodeId(field, (NodeId) value); - break; - case 18: - encodeExpandedNodeId(field, (ExpandedNodeId) value); - break; - case 19: - encodeStatusCode(field, (StatusCode) value); - break; - case 20: - encodeQualifiedName(field, (QualifiedName) value); - break; - case 21: - encodeLocalizedText(field, (LocalizedText) value); - break; - case 22: - encodeExtensionObject(field, (ExtensionObject) value); - break; - case 23: - encodeDataValue(field, (DataValue) value); - break; - case 24: - encodeVariant(field, (Variant) value); - break; - case 25: - encodeDiagnosticInfo(field, (DiagnosticInfo) value); - break; - default: - throw new UaSerializationException( - StatusCodes.Bad_EncodingError, "not a built-in type: " + value.getClass()); + switch (dataType) { + case Boolean -> encodeBoolean(field, (Boolean) value); + case SByte -> encodeSByte(field, (Byte) value); + case Byte -> encodeByte(field, (UByte) value); + case Int16 -> encodeInt16(field, (Short) value); + case UInt16 -> encodeUInt16(field, (UShort) value); + case Int32 -> encodeInt32(field, (Integer) value); + case UInt32 -> encodeUInt32(field, (UInteger) value); + case Int64 -> encodeInt64(field, (Long) value); + case UInt64 -> encodeUInt64(field, (ULong) value); + case Float -> encodeFloat(field, (Float) value); + case Double -> encodeDouble(field, (Double) value); + case String -> encodeString(field, (String) value); + case DateTime -> encodeDateTime(field, (DateTime) value); + case Guid -> encodeGuid(field, (UUID) value); + case ByteString -> encodeByteString(field, (ByteString) value); + case XmlElement -> encodeXmlElement(field, (XmlElement) value); + case NodeId -> encodeNodeId(field, (NodeId) value); + case ExpandedNodeId -> encodeExpandedNodeId(field, (ExpandedNodeId) value); + case StatusCode -> encodeStatusCode(field, (StatusCode) value); + case QualifiedName -> encodeQualifiedName(field, (QualifiedName) value); + case LocalizedText -> encodeLocalizedText(field, (LocalizedText) value); + case ExtensionObject -> encodeExtensionObject(field, (ExtensionObject) value); + case DataValue -> encodeDataValue(field, (DataValue) value); + case Variant -> encodeVariant(field, (Variant) value); + case DiagnosticInfo -> encodeDiagnosticInfo(field, (DiagnosticInfo) value); } contextPop(); } + @Override + public void encodeBuiltinTypeArray(String field, Object value, OpcUaDataType dataType) { + switch (dataType) { + case Boolean -> { + if (value instanceof boolean[] primitiveArray) { + Boolean[] boxedArray = new Boolean[primitiveArray.length]; + for (int i = 0; i < primitiveArray.length; i++) { + boxedArray[i] = primitiveArray[i]; + } + encodeBooleanArray(field, boxedArray); + } else { + encodeBooleanArray(field, (Boolean[]) value); + } + } + case Byte -> encodeByteArray(field, (UByte[]) value); + case SByte -> { + if (value instanceof byte[] primitiveArray) { + Byte[] boxedArray = new Byte[primitiveArray.length]; + for (int i = 0; i < primitiveArray.length; i++) { + boxedArray[i] = primitiveArray[i]; + } + encodeSByteArray(field, boxedArray); + } else { + encodeSByteArray(field, (Byte[]) value); + } + } + case Int16 -> { + if (value instanceof short[] primitiveArray) { + Short[] boxedArray = new Short[primitiveArray.length]; + for (int i = 0; i < primitiveArray.length; i++) { + boxedArray[i] = primitiveArray[i]; + } + encodeInt16Array(field, boxedArray); + } else { + encodeInt16Array(field, (Short[]) value); + } + } + case UInt16 -> encodeUInt16Array(null, (UShort[]) value); + case Int32 -> { + if (value instanceof int[] primitiveArray) { + Integer[] boxedArray = new Integer[primitiveArray.length]; + for (int i = 0; i < primitiveArray.length; i++) { + boxedArray[i] = primitiveArray[i]; + } + encodeInt32Array(field, boxedArray); + } else { + encodeInt32Array(field, (Integer[]) value); + } + } + case UInt32 -> encodeUInt32Array(field, (UInteger[]) value); + case Int64 -> { + if (value instanceof long[] primitiveArray) { + Long[] boxedArray = new Long[primitiveArray.length]; + for (int i = 0; i < primitiveArray.length; i++) { + boxedArray[i] = primitiveArray[i]; + } + encodeInt64Array(field, boxedArray); + } else { + encodeInt64Array(field, (Long[]) value); + } + } + case UInt64 -> encodeUInt64Array(field, (ULong[]) value); + case Float -> { + if (value instanceof float[] primitiveArray) { + Float[] boxedArray = new Float[primitiveArray.length]; + for (int i = 0; i < primitiveArray.length; i++) { + boxedArray[i] = primitiveArray[i]; + } + encodeFloatArray(field, boxedArray); + } else { + encodeFloatArray(field, (Float[]) value); + } + } + case Double -> { + if (value instanceof double[] primitiveArray) { + Double[] boxedArray = new Double[primitiveArray.length]; + for (int i = 0; i < primitiveArray.length; i++) { + boxedArray[i] = primitiveArray[i]; + } + encodeDoubleArray(field, boxedArray); + } else { + encodeDoubleArray(field, (Double[]) value); + } + } + case String -> encodeStringArray(field, (String[]) value); + case DateTime -> encodeDateTimeArray(field, (DateTime[]) value); + case Guid -> encodeGuidArray(field, (UUID[]) value); + case ByteString -> encodeByteStringArray(field, (ByteString[]) value); + case XmlElement -> encodeXmlElementArray(field, (XmlElement[]) value); + case NodeId -> encodeNodeIdArray(field, (NodeId[]) value); + case ExpandedNodeId -> encodeExpandedNodeIdArray(field, (ExpandedNodeId[]) value); + case StatusCode -> encodeStatusCodeArray(field, (StatusCode[]) value); + case QualifiedName -> encodeQualifiedNameArray(field, (QualifiedName[]) value); + case LocalizedText -> encodeLocalizedTextArray(field, (LocalizedText[]) value); + case ExtensionObject -> encodeExtensionObjectArray(field, (ExtensionObject[]) value); + case DataValue -> encodeDataValueArray(field, (DataValue[]) value); + case Variant -> encodeVariantArray(field, (Variant[]) value); + case DiagnosticInfo -> encodeDiagnosticInfoArray(field, (DiagnosticInfo[]) value); + } + } + private static Class getClass(@NonNull Object o) { if (o.getClass().isArray()) { return ArrayUtil.getType(o); @@ -1383,7 +1437,7 @@ public void encodeMatrix(String field, Matrix value) throws UaSerializationExcep jsonWriter.beginArray(); for (int i = 0; i < Array.getLength(flatArray); i++) { Object e = Array.get(flatArray, i); - encodeBuiltinTypeValue(null, dataType.getTypeId(), e); + encodeBuiltinType(null, e, dataType); } jsonWriter.endArray(); diff --git a/opc-ua-stack/encoding-xml/src/main/java/org/eclipse/milo/opcua/stack/core/encoding/xml/OpcUaXmlEncoder.java b/opc-ua-stack/encoding-xml/src/main/java/org/eclipse/milo/opcua/stack/core/encoding/xml/OpcUaXmlEncoder.java index e83f40409..621faf6f3 100644 --- a/opc-ua-stack/encoding-xml/src/main/java/org/eclipse/milo/opcua/stack/core/encoding/xml/OpcUaXmlEncoder.java +++ b/opc-ua-stack/encoding-xml/src/main/java/org/eclipse/milo/opcua/stack/core/encoding/xml/OpcUaXmlEncoder.java @@ -784,11 +784,16 @@ public void encodeVariantValue(@Nullable Object value) { } } + @Override + public void encodeBuiltinType(String field, Object value, OpcUaDataType dataType) { + encodeBuiltinTypeValue(value, dataType); + } + private void encodeBuiltinTypeValue(Object value, OpcUaDataType dataType) { switch (dataType) { case Boolean -> encodeBoolean("Boolean", (Boolean) value); - case Byte -> encodeByte("Byte", (UByte) value); case SByte -> encodeSByte("SByte", (Byte) value); + case Byte -> encodeByte("Byte", (UByte) value); case Int16 -> encodeInt16("Int16", (Short) value); case UInt16 -> encodeUInt16("UInt16", (UShort) value); case Int32 -> encodeInt32("Int32", (Integer) value); @@ -814,6 +819,11 @@ private void encodeBuiltinTypeValue(Object value, OpcUaDataType dataType) { } } + @Override + public void encodeBuiltinTypeArray(String field, Object value, OpcUaDataType dataType) { + encodeBuiltinTypeArrayValue(value, dataType); + } + private void encodeBuiltinTypeArrayValue(Object value, OpcUaDataType dataType) { switch (dataType) { case Boolean -> { diff --git a/opc-ua-stack/stack-core/src/main/java/org/eclipse/milo/opcua/stack/core/encoding/UaEncoder.java b/opc-ua-stack/stack-core/src/main/java/org/eclipse/milo/opcua/stack/core/encoding/UaEncoder.java index 3ebc8e07f..3c90ffce5 100644 --- a/opc-ua-stack/stack-core/src/main/java/org/eclipse/milo/opcua/stack/core/encoding/UaEncoder.java +++ b/opc-ua-stack/stack-core/src/main/java/org/eclipse/milo/opcua/stack/core/encoding/UaEncoder.java @@ -11,6 +11,8 @@ package org.eclipse.milo.opcua.stack.core.encoding; import java.util.UUID; + +import org.eclipse.milo.opcua.stack.core.OpcUaDataType; import org.eclipse.milo.opcua.stack.core.UaSerializationException; import org.eclipse.milo.opcua.stack.core.types.UaEnumeratedType; import org.eclipse.milo.opcua.stack.core.types.UaMessageType; @@ -172,4 +174,8 @@ void encodeStructMatrix(String field, Matrix value, NodeId dataTypeId) void encodeStructMatrix(String field, Matrix value, ExpandedNodeId dataTypeId) throws UaSerializationException; + + void encodeBuiltinType(String field, Object value, OpcUaDataType dataType); + + void encodeBuiltinTypeArray(String field, Object value, OpcUaDataType dataType); } diff --git a/opc-ua-stack/stack-core/src/main/java/org/eclipse/milo/opcua/stack/core/encoding/binary/OpcUaBinaryEncoder.java b/opc-ua-stack/stack-core/src/main/java/org/eclipse/milo/opcua/stack/core/encoding/binary/OpcUaBinaryEncoder.java index 7b0cbc2c7..4573b739a 100644 --- a/opc-ua-stack/stack-core/src/main/java/org/eclipse/milo/opcua/stack/core/encoding/binary/OpcUaBinaryEncoder.java +++ b/opc-ua-stack/stack-core/src/main/java/org/eclipse/milo/opcua/stack/core/encoding/binary/OpcUaBinaryEncoder.java @@ -671,101 +671,165 @@ public void encodeVariant(Variant variant) throws UaSerializationException { private void encodeValue( Object value, int typeId, boolean structure, boolean enumeration, boolean optionSet) { + OpcUaDataType dataType = OpcUaDataType.fromTypeId(typeId); + if (dataType == null) { + throw new UaSerializationException( + StatusCodes.Bad_EncodingError, "unknown typeId: " + typeId); + } if (structure) { UaStructuredType struct = (UaStructuredType) value; ExtensionObject extensionObject = ExtensionObject.encode(context, struct); - encodeBuiltinType(typeId, extensionObject); + encodeBuiltinType(extensionObject, dataType); } else if (enumeration) { - encodeBuiltinType(typeId, ((UaEnumeratedType) value).getValue()); + encodeBuiltinType(((UaEnumeratedType) value).getValue(), dataType); } else if (optionSet) { - encodeBuiltinType(typeId, ((OptionSetUInteger) value).getValue()); + encodeBuiltinType(((OptionSetUInteger) value).getValue(), dataType); } else { - encodeBuiltinType(typeId, value); + encodeBuiltinType(value, dataType); } } - private void encodeBuiltinType(int typeId, Object value) throws UaSerializationException { - switch (typeId) { - case 1: - encodeBoolean(null, (Boolean) value); - break; - case 2: - encodeSByte((Byte) value); - break; - case 3: - encodeByte((UByte) value); - break; - case 4: - encodeInt16((Short) value); - break; - case 5: - encodeUInt16((UShort) value); - break; - case 6: - encodeInt32((Integer) value); - break; - case 7: - encodeUInt32((UInteger) value); - break; - case 8: - encodeInt64((Long) value); - break; - case 9: - encodeUInt64((ULong) value); - break; - case 10: - encodeFloat((Float) value); - break; - case 11: - encodeDouble((Double) value); - break; - case 12: - encodeString((String) value); - break; - case 13: - encodeDateTime((DateTime) value); - break; - case 14: - encodeGuid((UUID) value); - break; - case 15: - encodeByteString((ByteString) value); - break; - case 16: - encodeXmlElement((XmlElement) value); - break; - case 17: - encodeNodeId((NodeId) value); - break; - case 18: - encodeExpandedNodeId((ExpandedNodeId) value); - break; - case 19: - encodeStatusCode((StatusCode) value); - break; - case 20: - encodeQualifiedName((QualifiedName) value); - break; - case 21: - encodeLocalizedText((LocalizedText) value); - break; - case 22: - encodeExtensionObject((ExtensionObject) value); - break; - case 23: - encodeDataValue((DataValue) value); - break; - case 24: - encodeVariant((Variant) value); - break; - case 25: - encodeDiagnosticInfo((DiagnosticInfo) value); - break; - default: - throw new UaSerializationException( - StatusCodes.Bad_EncodingError, "unknown builtin type: " + typeId); + @Override + public void encodeBuiltinType(String field, Object value, OpcUaDataType dataType) { + encodeBuiltinType(value, dataType); + } + + private void encodeBuiltinType(Object value, OpcUaDataType dataType) throws UaSerializationException { + switch (dataType) { + case Boolean -> encodeBoolean((Boolean) value); + case SByte -> encodeSByte((Byte) value); + case Byte -> encodeByte((UByte) value); + case Int16 -> encodeInt16((Short) value); + case UInt16 -> encodeUInt16((UShort) value); + case Int32 -> encodeInt32((Integer) value); + case UInt32 -> encodeUInt32((UInteger) value); + case Int64 -> encodeInt64((Long) value); + case UInt64 -> encodeUInt64((ULong) value); + case Float -> encodeFloat((Float) value); + case Double -> encodeDouble((Double) value); + case String -> encodeString((String) value); + case DateTime -> encodeDateTime((DateTime) value); + case Guid -> encodeGuid((UUID) value); + case ByteString -> encodeByteString((ByteString) value); + case XmlElement -> encodeXmlElement((XmlElement) value); + case NodeId -> encodeNodeId((NodeId) value); + case ExpandedNodeId -> encodeExpandedNodeId((ExpandedNodeId) value); + case StatusCode -> encodeStatusCode((StatusCode) value); + case QualifiedName -> encodeQualifiedName((QualifiedName) value); + case LocalizedText -> encodeLocalizedText((LocalizedText) value); + case ExtensionObject -> encodeExtensionObject((ExtensionObject) value); + case DataValue -> encodeDataValue((DataValue) value); + case Variant -> encodeVariant((Variant) value); + case DiagnosticInfo -> encodeDiagnosticInfo((DiagnosticInfo) value); + default -> throw new UaSerializationException( + StatusCodes.Bad_EncodingError, "unknown builtin type: " + dataType); + } + } + + @Override + public void encodeBuiltinTypeArray(String field, Object value, OpcUaDataType dataType) { + encodeBuiltinTypeArray(value, dataType); + } + + private void encodeBuiltinTypeArray(Object value, OpcUaDataType dataType) { + switch (dataType) { + case Boolean -> { + if (value instanceof boolean[] primitiveArray) { + Boolean[] boxedArray = new Boolean[primitiveArray.length]; + for (int i = 0; i < primitiveArray.length; i++) { + boxedArray[i] = primitiveArray[i]; + } + encodeBooleanArray(null, boxedArray); + } else { + encodeBooleanArray(null, (Boolean[]) value); + } + } + case Byte -> encodeByteArray(null, (UByte[]) value); + case SByte -> { + if (value instanceof byte[] primitiveArray) { + Byte[] boxedArray = new Byte[primitiveArray.length]; + for (int i = 0; i < primitiveArray.length; i++) { + boxedArray[i] = primitiveArray[i]; + } + encodeSByteArray(null, boxedArray); + } else { + encodeSByteArray(null, (Byte[]) value); + } + } + case Int16 -> { + if (value instanceof short[] primitiveArray) { + Short[] boxedArray = new Short[primitiveArray.length]; + for (int i = 0; i < primitiveArray.length; i++) { + boxedArray[i] = primitiveArray[i]; + } + encodeInt16Array(null, boxedArray); + } else { + encodeInt16Array(null, (Short[]) value); + } + } + case UInt16 -> encodeUInt16Array(null, (UShort[]) value); + case Int32 -> { + if (value instanceof int[] primitiveArray) { + Integer[] boxedArray = new Integer[primitiveArray.length]; + for (int i = 0; i < primitiveArray.length; i++) { + boxedArray[i] = primitiveArray[i]; + } + encodeInt32Array(null, boxedArray); + } else { + encodeInt32Array(null, (Integer[]) value); + } + } + case UInt32 -> encodeUInt32Array(null, (UInteger[]) value); + case Int64 -> { + if (value instanceof long[] primitiveArray) { + Long[] boxedArray = new Long[primitiveArray.length]; + for (int i = 0; i < primitiveArray.length; i++) { + boxedArray[i] = primitiveArray[i]; + } + encodeInt64Array(null, boxedArray); + } else { + encodeInt64Array(null, (Long[]) value); + } + } + case UInt64 -> encodeUInt64Array(null, (ULong[]) value); + case Float -> { + if (value instanceof float[] primitiveArray) { + Float[] boxedArray = new Float[primitiveArray.length]; + for (int i = 0; i < primitiveArray.length; i++) { + boxedArray[i] = primitiveArray[i]; + } + encodeFloatArray(null, boxedArray); + } else { + encodeFloatArray(null, (Float[]) value); + } + } + case Double -> { + if (value instanceof double[] primitiveArray) { + Double[] boxedArray = new Double[primitiveArray.length]; + for (int i = 0; i < primitiveArray.length; i++) { + boxedArray[i] = primitiveArray[i]; + } + encodeDoubleArray(null, boxedArray); + } else { + encodeDoubleArray(null, (Double[]) value); + } + } + case String -> encodeStringArray(null, (String[]) value); + case DateTime -> encodeDateTimeArray(null, (DateTime[]) value); + case Guid -> encodeGuidArray(null, (UUID[]) value); + case ByteString -> encodeByteStringArray(null, (ByteString[]) value); + case XmlElement -> encodeXmlElementArray(null, (XmlElement[]) value); + case NodeId -> encodeNodeIdArray(null, (NodeId[]) value); + case ExpandedNodeId -> encodeExpandedNodeIdArray(null, (ExpandedNodeId[]) value); + case StatusCode -> encodeStatusCodeArray(null, (StatusCode[]) value); + case QualifiedName -> encodeQualifiedNameArray(null, (QualifiedName[]) value); + case LocalizedText -> encodeLocalizedTextArray(null, (LocalizedText[]) value); + case ExtensionObject -> encodeExtensionObjectArray(null, (ExtensionObject[]) value); + case DataValue -> encodeDataValueArray(null, (DataValue[]) value); + case Variant -> encodeVariantArray(null, (Variant[]) value); + case DiagnosticInfo -> encodeDiagnosticInfoArray(null, (DiagnosticInfo[]) value); } }