Como trabalhar corretamente com ExtensionObject e Struct no milo opc ua
Gostaria de perguntar como devo trabalhar corretamente com Struct quando estou tentando ler algum objeto do opc ua server. Passei por este exemplo e consegui ler os dados.
Mas agora não sei como lê-los corretamente . Vamos imaginar que estou lendo alguma estrutura de dados, incluindo duas matrizes para os valores xey. Tentei fazer algo assim:
Float[] x = (Float[])struct.getMember("x").getValue()
Float[] y = (Float[])struct.getMember("y").getValue()
mas recebo a exceção "Não é possível lançar 'java.lang.Object []' em 'java.lang.Float []'" Posso fazer isso desta forma:
float[] x = new float[100];
int i = 0;
for(Object o: (Object[])struct.getMember("x").getValue()){
x[i] = (Float)o;
i++;
}
mas não acho que isso possa estar certo.
De qualquer forma, gostaria de conseguir algo semelhante, como ler o arquivo json com o jackson. Para ter alguma classe com a mesma nomenclatura asi os "membros são e com tipos adequados e fazem algo como:
OpcuaReader reader = ...
MyClass myClass = reader.read(struct, MyClass.class)
Posso estar totalmente errado, então alguém poderia me sugerir como devo resolver esse problema?
Respostas
Em primeiro lugar, você não pode lançar um array de objetos assim. Em vez disso, você pode usar a API de fluxo para construir flutuadores como este:
Object[] objectArray = { 1.0f, 2.0f, 3, 4, 5 };
Float floatArray[] = Arrays.stream(objectArray)
.map(Object::toString)
.map(Float::valueOf)
.toArray(Float[]::new);
Sobre o cliente Milo, há um ótimo exemplo de leitura de tipos de dados personalizados em ReadWriteCustomDataTypeNodeExample .
Você pode criar seu próprio tipo semelhante CustomStructTypee substituir o método de decodificação para você. O decodificador também possui um readFloatArraymétodo integrado :
@Override
public CustomStructType decode(
SerializationContext context,
UaDecoder decoder) throws UaSerializationException {
String foo = decoder.readString("Foo");
UInteger bar = decoder.readUInt32("Bar");
boolean baz = decoder.readBoolean("Baz");
Float[] floatArray = decoder.readFloatArray("floatArray");
return new CustomStructType(foo, bar, baz);
}
Muito obrigado ao istibekesi , conseguimos fazê-lo funcionar. Para alguém que teria o mesmo problema, aqui está o que você precisa fazer:
1) Encontre o TYPE_ID
- você precisa encontrar o NamespaceIndex e o identificador do DataType da estrutura (objeto) que deseja ler no OPC UA usando, por exemplo, UaExpert
- se você não tiver certeza de qual DataType é, apenas encontre alguma variável representando esta estrutura e lá você verá a informação do DataType no lado direito da tela ao clicar nele.
2) Encontre o BINARY_ENCODING_ID
- para encontrar este, você precisa pesquisar o próprio DataType usando UaExpert, pois haverá alguns em Tipos / DataTypes ...
- quando você encontrar clique nele para mais informações
- então na parte inferior direita da tela haverá "HasEncoding | Default Binary" e você clica duas vezes
- desta forma você receberá o NamespaceIndex e o Identificador para BINARY_ENCODING_ID
3) Siga este exemplo
- Para ter todas as partes do milo que você precisa, você precisará incluir o cliente sdk , o leitor de dicionário , o analisador bsd em suas dependências
- crie uma classe semelhante a esta:
public class OpcuaCurve implements UaStructure {
public static final ExpandedNodeId TYPE_ID = ExpandedNodeId.parse("ns=3;s=DT_\"PrServo_typeRuntimeDriveDiagnosticsProcessValuesTrends\".\"hmiTrend\"");
public static final ExpandedNodeId BINARY_ENCODING_ID = ExpandedNodeId.parse("ns=3;s=TE_\"PrServo_typeRuntimeDriveDiagnosticsProcessValuesTrends\".\"hmiTrend\"");
private final Float[] torque;
private final Float[] speed;
public OpcuaCurve() {
this(null, null);
}
public OpcuaCurve(Float[] torque, Float[] speed) {
this.torque = torque;
this.speed = speed;
}
public Float[] getSpeed() {
return speed;
}
public Float[] getTorque() {
return torque;
}
@Override
public ExpandedNodeId getTypeId() {
return TYPE_ID;
}
@Override
public ExpandedNodeId getBinaryEncodingId() {
return BINARY_ENCODING_ID;
}
@Override
public ExpandedNodeId getXmlEncodingId() {
// XML encoding not supported
return ExpandedNodeId.NULL_VALUE;
}
public static class Codec extends GenericDataTypeCodec<OpcuaCurve> {
@Override
public Class<OpcuaCurve> getType() {
return OpcuaCurve.class;
}
@Override
public OpcuaCurve decode(
SerializationContext context,
UaDecoder decoder) throws UaSerializationException {
Float[] torqueArray = decoder.readFloatArray("motorTorque");
Float[] speedArray = decoder.readFloatArray("motorSpeed");
return new OpcuaCurve(torqueArray,speedArray);
}
@Override
public void encode(
SerializationContext context,
UaEncoder encoder, OpcuaCurve value) throws UaSerializationException {
encoder.writeFloatArray("motorTorque", value.torque);
encoder.writeFloatArray("motorTorque", value.speed);
}
}
}
- e registrar o decodificador para o cliente assim:
private void registerCustomCodec(OpcUaClient client) {
NodeId binaryEncodingId = OpcuaCurve.BINARY_ENCODING_ID
.local(client.getNamespaceTable())
.orElseThrow(() -> new IllegalStateException("namespace not found"));
// Register codec with the client DataTypeManager instance
client.getDataTypeManager().registerCodec(
binaryEncodingId,
new OpcuaCurve.Codec().asBinaryCodec()
);
}