Как правильно работать с ExtensionObject и Struct в milo opc ua

Я хотел бы спросить, как мне правильно работать со Struct, когда я пытаюсь прочитать какой-то объект с сервера opc ua. Я прошел через это example, и я смог прочитать данные.

Но сейчас я не знаю, как правильно их читать. Представим, что я читаю некую структуру данных, включающую два массива для значений x и y. Я пробовал сделать что-то вроде этого:

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

но я получаю исключение. Невозможно преобразовать 'java.lang.Object []' в 'java.lang.Float []'. Я могу сделать это следующим образом:

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

но я не думаю, что это могло быть правильно.

В любом случае я хотел бы добиться чего-то подобного, например, чтения json-файла с помощью Джексона. Чтобы иметь какой-то класс с тем же именем, что и члены, и с подходящими типами, и сделать что-то вроде:

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

Я мог быть совершенно неправ, поэтому мог ли кто-нибудь подсказать мне, как мне решить такую ​​проблему?


person Jakub Znamenáček    schedule 20.11.2020    source источник


Ответы (2)


Во-первых, вы не можете преобразовать массив таких объектов. Вместо этого вы можете использовать потоковый API для создания таких плавающих объектов:

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

Что касается клиента Milo, есть отличный пример чтения пользовательских типов данных в ReadWriteCustomDataTypeNodeExample.

Вы можете создать свой собственный тип, аналогичный CustomStructType, и переопределить метод декодирования для себя. В декодере также есть встроенный readFloatArray метод:

@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);
}
person István Békési    schedule 23.11.2020
comment
Большое спасибо за ответ, я постараюсь пройти через него. Это именно то, что я хотел знать. Спасибо! - person Jakub Znamenáček; 23.11.2020
comment
Привет, я просмотрел код и не совсем уверен, зачем нужны методы getTypeId и getBinaryEncodingId и как с ними обращаться. - person Jakub Znamenáček; 24.11.2020
comment
Я получаю это исключение: java.lang.ClassCastException: класс org.eclipse.milo.opcua.binaryschema.Struct не может быть преобразован в класс OpcuaCurve (org.eclipse.milo.opcua.binaryschema.Struct и OpcuaCurve находятся в безымянном модуле загрузчика 'приложение') - person Jakub Znamenáček; 24.11.2020
comment
К какому типу OPC-сервера вы подключены? Вы сначала определили OpcuaCurve на сервере? В этом примере декодер уже возвращает CustomStructType, поэтому не должно быть исключения приведения, если вы зарегистрируете кодек и напишете свой декодер, как это видно в примере. Вот как CustomStructType выглядит на примере сервера: i.imgur.com/8qgdXmE.png - person István Békési; 25.11.2020
comment
Я не реализовал сервер самостоятельно, мне нужно прочитать данные с сервера plc. Это то, что я вижу, когда использую UaExpert imgur.com/a/wUZNT1s. - person Jakub Znamenáček; 26.11.2020
comment
Я не уверен, как мне следует преодолеть эти два в примере: imgur.com/a/GK4wmoc Отдых швы должны быть ясны для меня. - person Jakub Znamenáček; 26.11.2020
comment
Возможно, этот снимок экрана от UaExpert лучше объясняет это: prnt.sc/vqgkzm - person Jakub Znamenáček; 26.11.2020
comment
Предполагается, что TYPE_ID ссылается на узел DataType, в вашем случае попробуйте что-нибудь вроде public static final ExpandedNodeId TYPE_ID = ExpandedNodeId.parse("ns=3;s=DT_PrServo_typeRuntime ... ");. Вы также можете просмотреть указанный DataType в браузере UaExpert. Этот тип данных ссылается на HasEncoding, его идентификатор узла должен быть BINARY_ENCODING_ID. i.imgur.com/KsZFD4B.png - person István Békési; 26.11.2020
comment
Это то, что я нашел в UaExpert в разделе Типы / Типы данных / Двоичный OPC: prnt.sc/vqm7c8, и это что я нахожу в разделе "Типы / типы данных / Тип базовых данных": prnt.sc/vqm8cu, поэтому я думаю, что использую второй (NamespaceIndex и идентификатор) для TYPE_ID, но я не уверен, где мне взять BINARY_ENCODING_ID. Это первый, который я нашел? - person Jakub Znamenáček; 26.11.2020
comment
На втором экране в правом нижнем углу: HasEncoding: Default Binary Дважды щелкните по нему и используйте его идентификатор узла как BINARY_ENCODING_ID. - person István Békési; 26.11.2020
comment
Давайте продолжим это обсуждение в чате. - person Jakub Znamenáček; 26.11.2020

Большое спасибо istibekesi, нам удалось заставить его работать. Вот что вам нужно сделать тем, у кого возникла бы такая же проблема:

1) Найдите TYPE_ID

  • вам нужно найти NamespaceIndex и идентификатор DataType структуры (объекта), которую вы хотите прочитать через OPC UA, используя, например, UaExpert
  • если вы не уверены, какой это тип данных, просто найдите некоторую переменную, представляющую эту структуру, и там вы увидите информацию о типе данных в правой части экрана, когда вы нажмете на нее.

2) Найдите BINARY_ENCODING_ID

  • чтобы найти это, вам нужно найти сам DataType с помощью UaExpert, некоторые из них будут в типах / DataTypes ...
  • когда вы найдете его, нажмите на него, чтобы узнать больше
  • то в правой нижней части экрана появится HasEncoding | Default Binary, и вы дважды щелкните по нему
  • таким образом вы получите NamespaceIndex и идентификатор для BINARY_ENCODING_ID

3) Следуйте этому пример

  • Чтобы иметь все необходимые вам части milo, вам необходимо включить в свои зависимости sdk-client, dictionary-reader, bsd-parser.
  • создайте класс, подобный этому:
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);
        }
    }

}
  • и зарегистрируйте декодер для клиента следующим образом:
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()
        );
    }
person Jakub Znamenáček    schedule 26.11.2020