Как добавить узел с дочерними свойствами и задать им значения?

Я построил сервер OPC UA из примера OPCFoundation - https://github.com/OPCFoundation/UA-.NETStandard

Также скомпилировал свою собственную модель с помощью - https://github.com/Pro/UA-ModelCompiler/tree/eebf9988940a789a4f36cbef8c5bd6340f1b2f42

В примерах у них есть сгенерированные файлы в папках, а затем пользовательские классы для NodeManger, State и так далее.

Я успешно загрузил сгенерированный файл .uanodes с некоторыми примерами узлов в нем. Но я не могу создавать эти узлы с помощью кода со стороны сервера. При тестировании делаю это в функции - LoadPredefinedNodes (контекст ISystemContext).

Моя структура должна быть узлом под объектами под названием «Теги», в этом узле я хочу динамически добавлять узлы и устанавливать их свойства во время работы сервера.

При тестировании я использую клиент OPC UA объекта интеграции.

Структура выглядит правильно, например: Объекты => Теги => MyTestTag (Свойства: Имя, Описание, Значение)

Первые два свойства добавлены правильно из определения файла модели.

    <!-- ### Object Types ###-->

    <!-- BaseType -->
    <ObjectType SymbolicName="TAG:TagType" BaseType="ua:BaseObjectType" IsAbstract="true" SupportsEvents="true" AccessLevel="ReadWrite">
        <Description>Base type for all tags</Description>
        <Children>
            <Property SymbolicName="TAG:Name" DataType="ua:String" ValueRank="Scalar" ModellingRule="Mandatory" AccessLevel="ReadWrite">
                <Description>Name of the tag</Description>
            </Property>
            <Property SymbolicName="TAG:Description" DataType="ua:String" ValueRank="Scalar" ModellingRule="Optional" AccessLevel="ReadWrite">
                <Description>Description of the tag</Description>
            </Property>
        </Children>
    </ObjectType>

    <!-- ### Objects ###-->
    <Object SymbolicName="TAG:Tags" TypeDefinition="ua:BaseObjectType">
        <Description>Contains all instances of tags</Description>
        <References>
            <Reference IsInverse="true">
                <ReferenceType>ua:Organizes</ReferenceType>
                <TargetId>ua:ObjectsFolder</TargetId>
            </Reference>
        </References>
    </Object>

Третий - это свойство, которое я пытаюсь определить из кода на стороне сервера. Я добавил свойство Value, но не могу понять, как записать его значение.

        protected override NodeStateCollection LoadPredefinedNodes(ISystemContext context)
        {
            NodeStateCollection predefinedNodes = new NodeStateCollection();
            predefinedNodes.LoadFromBinaryResource(context, "Opc.Ua.ValmetData.Tag.Tag.PredefinedNodes.uanodes", this.GetType().GetTypeInfo().Assembly, true);

            // The defined ObjectType in the model
            NodeState tageTypeNode = predefinedNodes.Find(x => x.SymbolicName == "TagType");

            // List node to add the tag to
            NodeState tagsNode = predefinedNodes.Find(x => x.SymbolicName == "Tags");

            ushort namespaceIndex = Server.NamespaceUris.GetIndexOrAppend(Namespaces.Tag);


            TagState tagNode = new TagState(tagsNode);
            tagNode.SymbolicName = "MyTestTag";
            tagNode.ClearChangeMasks(context, true);            

            PropertyState ps = new PropertyState(tagNode);
            ps.ClearChangeMasks(context, true);
            ps.NodeId = new NodeId();
            ps.Description = "This is a description";
            ps.TypeDefinitionId = tageTypeNode.NodeId;
            ps.NumericId = 66;
            //  ps.NodeClass = NodeClass.Variable;
            ps.ReferenceTypeId = ReferenceTypeIds.HasProperty;
            ps.BrowseName = new QualifiedName("Value", NamespaceIndex);
            ps.DisplayName = ps.BrowseName.Name;
            ps.DataType = (uint)BuiltInType.Int32;
            // ps.UserAccessLevel = AccessLevels.CurrentReadOrWrite;
            // ps.AccessLevel = AccessLevels.CurrentReadOrWrite;
            ps.Value = 66;

            tagNode.AddChild(ps);

            // assign
            tagNode.Create(
                SystemContext,
                new NodeId(tagNode.SymbolicName, namespaceIndex),
                new QualifiedName(tagNode.SymbolicName, namespaceIndex),
                null,
                true);

            tagsNode.AddChild(tagNode);

            return predefinedNodes;

}

Из этого кода в узел Теги добавляется узел MyTestTag. Свойства «Имя» и «Описание» добавлены из модели. А свойство «Значение» добавляется кодом. Однако я не могу установить значение ни для одного из этих свойств.

При нажатии на свойство MyTestTag.Value в клиенте выводится ошибка «Обнуляемый объект должен иметь значение».

Подскажите пожалуйста, что я делаю не так.


person krazor    schedule 28.06.2019    source источник
comment
Не имеет отношения к вашей проблеме, но это может помочь вам в будущем: я создал PR, который также добавляет контейнер докеров в UA-ModelCompiler. Это имеет то преимущество, что вы можете просто использовать контейнер для компиляции вашей модели вместо того, чтобы сначала компилировать исполняемый файл ua-modelcompiler. github.com/OPCFoundation/UA-ModelCompiler/pull/36   -  person Stefan Profanter    schedule 28.06.2019
comment
В любом случае спасибо за комментарий. Позже проверю.   -  person krazor    schedule 28.06.2019


Ответы (2)


В примерах из OPC-UA есть биты кода для обновления переменных, поэтому я не уверен, в чем проблема. Предполагая, что я устанавливаю хорошее значение, код может выглядеть так:

variable.Value = value;
variable.Timestamp = DateTime.UtcNow;
variable.StatusCode = StatusCodes.Good;
variable.ClearChangeMasks(SystemContext, false);
person astrowalker    schedule 02.07.2019

В моем случае проблема заключалась в nodeId. я изменил

ps.NodeId = new NodeId();
// to
ps.NodeId = new NodeId(Guid.NewGuid(), NamespaceIndex);

Затем он появился в дереве узлов.

person krazor    schedule 02.07.2019