Jak poprawnie pracować z ExtensionObject i Struct w milo opc ua

Nov 20 2020

Chciałbym zapytać, jak mam poprawnie pracować ze Struct, gdy próbuję odczytać jakiś obiekt z serwera OPC UA. Przeszedłem przez ten przykład i udało mi się odczytać dane.

Ale w tej chwili nie wiem, jak je poprawnie odczytać. Wyobraźmy sobie, że czytam pewną strukturę danych, w tym dwie tablice dla wartości x i y. Próbowałem zrobić coś takiego:

 Float[] x = (Float[])struct.getMember("x").getValue()
 Float[] y = (Float[])struct.getMember("y").getValue()

ale otrzymuję wyjątek „Nie można przesłać 'java.lang.Object []' na 'java.lang.Float []'" Mogę to zrobić w ten sposób:

float[] x = new float[100];
        int i = 0;
        for(Object o: (Object[])struct.getMember("x").getValue()){
            x[i] = (Float)o;
            i++;
        }

ale nie sądzę, żeby to mogło być właściwe.

W każdym razie chciałbym osiągnąć coś podobnego, jak czytanie pliku json z jacksonem. Aby mieć jakąś klasę z takimi samymi nazwami jak „członkowie są” i z odpowiednimi typami i wykonaj coś takiego:

OpcuaReader reader = ...
MyClass myClass = reader.read(struct, MyClass.class)

Mogę się całkowicie mylić, więc czy ktoś mógłby mi podpowiedzieć, jak mam rozwiązać taki problem?

Odpowiedzi

1 IstvánBékési Nov 23 2020 at 08:19

Po pierwsze, nie możesz rzucać tablicy takich obiektów. Zamiast tego możesz użyć interfejsu API strumienia do skonstruowania pływaków w następujący sposób:

Object[] objectArray = { 1.0f, 2.0f, 3, 4, 5 };
Float floatArray[] = Arrays.stream(objectArray)
  .map(Object::toString)
  .map(Float::valueOf)
  .toArray(Float[]::new);

Jeśli chodzi o klienta Milo, istnieje doskonały przykład odczytu niestandardowych typów danych w ReadWriteCustomDataTypeNodeExample .

Możesz stworzyć własny typ podobny do CustomStructTypemetody dekodowania i nadpisać ją dla siebie. Dekoder ma również wbudowaną readFloatArraymetodę:

@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);
}
JakubZnamenáček Nov 26 2020 at 16:15

Wielkie dzięki dla istibekesi , udało nam się to uruchomić. Dla kogoś, kto miałby tutaj ten sam problem, musisz zrobić:

1) Znajdź TYPE_ID

  • musisz znaleźć NamespaceIndex i Identifier of the DataType struktury (obiektu), którą chcesz odczytać przez OPC UA, używając na przykład UaExpert
  • jeśli nie jesteś pewien, który to jest typ danych, po prostu znajdź jakąś zmienną reprezentującą tę strukturę, a po kliknięciu zobaczysz informację o typie danych po prawej stronie ekranu.

2) Znajdź BINARY_ENCODING_ID

  • aby znaleźć ten, musisz wyszukać sam DataType za pomocą UaExpert, niektóre z nich były w Typach / Typach danych ...
  • kiedy go znajdziesz, kliknij go, aby uzyskać więcej informacji
  • następnie w prawej dolnej części ekranu pojawi się "HasEncoding | Default Binary" i klikasz na niego dwukrotnie
  • w ten sposób otrzymasz NamespaceIndex i identyfikator dla BINARY_ENCODING_ID

3) Postępuj zgodnie z tym przykładem

  • Aby mieć wszystkie części milo, musisz uwzględnić w swoich zależnościach klienta sdk , słownik-czytnik , parser bsd
  • utwórz klasę podobną do tej:
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);
        }
    }

}
  • i zarejestruj dekoder do klienta w ten sposób:
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()
        );
    }