Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [milo-dev] how to decode "Structures with optional fields"

Structures with optional fields are encoded with a UInt32 "EncodingMask" field that indicates whether the optional fields are present in the stream or not.

Here's an example codec for a DataType that has some optional fields. Note how the encoding mask is the first field encoded/decoded, and how it's consulted during decode to determine if the field should actually be decoded or not. During encoding the field's value being null or not determines whether it's encoded or not.

If this seems like a lot of work to do for every type - yes, it is. These aren't meant to be coded by hand. Information models are meant to be fed into code generators that generate all of the types the information model defines.

public static final class Codec extends GenericDataTypeCodec<BACnetActionCommand> {
@Override
public Class<BACnetActionCommand> getType() {
return BACnetActionCommand.class;
}

@Override
public BACnetActionCommand decode(SerializationContext context, UaDecoder decoder) {
UInteger encodingMask = decoder.readUInt32("EncodingMask");
long encodingMaskValue = encodingMask.longValue();
UInteger deviceIdentifier = null;
if ((encodingMaskValue & (1L << 0)) != 0) {
deviceIdentifier = decoder.readUInt32("deviceIdentifier");
}
UInteger objectIdentifier = decoder.readUInt32("objectIdentifier");
BACnetPropertyIdentifier propertyIdentifier = decoder.readEnum("propertyIdentifier", BACnetPropertyIdentifier.class);
UInteger propertyArrayIndex = null;
if ((encodingMaskValue & (1L << 1)) != 0) {
propertyArrayIndex = decoder.readUInt32("propertyArrayIndex");
}
Variant propertyValue = decoder.readVariant("propertyValue");
UByte priority = null;
if ((encodingMaskValue & (1L << 2)) != 0) {
priority = decoder.readByte("priority");
}
UInteger postDelay = null;
if ((encodingMaskValue & (1L << 3)) != 0) {
postDelay = decoder.readUInt32("postDelay");
}
Boolean quitOnFailure = decoder.readBoolean("quitOnFailure");
Boolean writeSuccessful = decoder.readBoolean("writeSuccessful");
return new BACnetActionCommand(deviceIdentifier, objectIdentifier, propertyIdentifier, propertyArrayIndex, propertyValue, priority, postDelay, quitOnFailure, writeSuccessful);
}

@Override
public void encode(SerializationContext context, UaEncoder encoder, BACnetActionCommand value) {
long encodingMaskValue = 0L;
if (value.getDeviceIdentifier() != null) {
encodingMaskValue |= (1L << 0);
}
if (value.getPropertyArrayIndex() != null) {
encodingMaskValue |= (1L << 1);
}
if (value.getPriority() != null) {
encodingMaskValue |= (1L << 2);
}
if (value.getPostDelay() != null) {
encodingMaskValue |= (1L << 3);
}
encoder.writeUInt32("EncodingMask", Unsigned.uint(encodingMaskValue));
if (value.getDeviceIdentifier() != null) {
encoder.writeUInt32("deviceIdentifier", value.getDeviceIdentifier());
}
encoder.writeUInt32("objectIdentifier", value.getObjectIdentifier());
encoder.writeEnum("propertyIdentifier", value.getPropertyIdentifier());
if (value.getPropertyArrayIndex() != null) {
encoder.writeUInt32("propertyArrayIndex", value.getPropertyArrayIndex());
}
encoder.writeVariant("propertyValue", value.getPropertyValue());
if (value.getPriority() != null) {
encoder.writeByte("priority", value.getPriority());
}
if (value.getPostDelay() != null) {
encoder.writeUInt32("postDelay", value.getPostDelay());
}
encoder.writeBoolean("quitOnFailure", value.getQuitOnFailure());
encoder.writeBoolean("writeSuccessful", value.getWriteSuccessful());
}
}

On Wed, Jul 17, 2024 at 3:47 AM Francesco Viscomi via milo-dev <milo-dev@xxxxxxxxxxx> wrote:
Hi all,
I need to know which one is the right option to decode a structure with optional field?


Reading the page, it seems that I have to use the function 
ByteString JoiningProcessId = decoder.readByteString("JoiningProcessId");

but if I try to use it I get 

imput v0:ddd
imput v1:ExtensionObject{encoded=ByteString{bytes=[7, 0, 0, 0, 2, 0, 0, 0, 51, 52, 2, 0, 0, 0, 50, 50, 2, 0, 0, 0, 50, 50]}, encodingId=NodeId{ns=6, id=5121}}
imput v2:LocalizedText{text=yyyy, locale=it}
decode JoiningProcessIdentificationDataType

JoiningProcessId ByteString{bytes=[2, 0, 0, 0, 51, 52, 2]}
12:43:02.438 [milo-shared-thread-pool-6] ERROR o.e.m.e.m.opcua.ModeledNamespace - Uncaught Throwable invoking method handler for methodId=NodeId{ns=8, id=92}.
org.eclipse.milo.opcua.stack.core.UaSerializationException: array length exceeds max message size (length=838860800, max=2097152)
at org.eclipse.milo.opcua.stack.core.serialization.OpcUaBinaryStreamDecoder.checkArrayLength(OpcUaBinaryStreamDecoder.java:1240)
at org.eclipse.milo.opcua.stack.core.serialization.OpcUaBinaryStreamDecoder.readByteString(OpcUaBinaryStreamDecoder.java:187)
at org.eclipse.milo.opcua.stack.core.serialization.OpcUaBinaryStreamDecoder.readByteString(OpcUaBinaryStreamDecoder.java:607)
at org.eclipse.milo.opcua.stack.core.types.structured.JoiningProcessIdentificationDataType$Codec.decode(JoiningProcessIdentificationDataType.java:125)
at org.eclipse.milo.opcua.stack.core.types.structured.JoiningProcessIdentificationDataType$Codec.decode(JoiningProcessIdentificationDataType.java:1)
at org.eclipse.milo.opcua.stack.core.serialization.codecs.GenericDataTypeCodec$GenericBinaryDataTypeCodec.decode(GenericDataTypeCodec.java:47)
at org.eclipse.milo.opcua.stack.core.types.OpcUaDefaultBinaryEncoding.decode(OpcUaDefaultBinaryEncoding.java:117)
at org.eclipse.milo.opcua.stack.core.types.builtin.ExtensionObject.lambda$0(ExtensionObject.java:105)

I cannot figure out why this happen, also I cannot figure out if eclipse milo is able to read a structure with optional fields 

please help 

--
Ing. Viscomi Francesco
_______________________________________________
milo-dev mailing list
milo-dev@xxxxxxxxxxx
To unsubscribe from this list, visit https://www.eclipse.org/mailman/listinfo/milo-dev

Back to the top