[Server] bug updating bidirectional HasNotifier Refererences

Questions regarding the use of the .NET SDK 2.0 for Server or Client development or integration into customer products ...

Moderator: uasdknet

Post Reply
alennartz
Jr. Member
Jr. Member
Posts: 4
Joined: 04 Apr 2016, 22:03

[Server] bug updating bidirectional HasNotifier Refererences

Post by alennartz »

I believe that there is a bug with regards to updating bidirectional references of type HasNotifier (I'm using version 2.4.1.366).

say I have object nodes o1 and o2 both of node type BaseObjectType and having the SubscribeToEvents flag set on their EventNotifier attribute as well as having the server node as their initial notifier parent.

If I use the following code from my node manager:

Code: Select all

AddReference(Server.DefaultRequestContext, o1.NodeId, ReferenceTypeIds.HasNotifier, false, o2.NodeId, true);
then in UAexpert I can see that the reference is properly created. However, if I try to send an event using ReportEvent(NodeId, GenericEvent) with o2 as the source while monitoring o1 in UaExpert I do not get the event. If I monitor o2 directly then I do get it.


If instead I use this:

Code: Select all

AddReference(Server.DefaultRequestContext, o2.NodeId, ReferenceTypeIds.HasNotifier, true, o1.NodeId, true);
Then I can monitor o1 and receive and event that was raised on o2.

Interestingly enough DeleteReference has the opposite bug. ie

Code: Select all

DeleteReference(Server.DefaultRequestContext, o2.NodeId, ReferenceTypeIds.HasNotifier, true, o1.NodeId, true);
-> the reference is deleted but the event is still reported to the o1 node when sent on o2...

Code: Select all

DeleteReference(Server.DefaultRequestContext, o1.NodeId, ReferenceTypeIds.HasNotifier, false, o2.NodeId, true);
-> the reference is deleted and the event is no longer reported to the o1 node when sent on o2.


I can currently work around the issue by using the following method for all my updates:

Code: Select all

private void UpdateNotifierReference(NodeId parent, NodeId child, bool add)
{
	if (add)
	{
		AddReference(Server.DefaultRequestContext, child, ReferenceTypeIds.HasNotifier, true, parent, true);
	}
	else
	{
		DeleteReference(Server.DefaultRequestContext, parent, ReferenceTypeIds.HasNotifier, false, child, true);
	}
}



On a somewhat related note: if I have n different paths from a node o1 to a node o2 using chains of HasNotifier references then i will get n copies of the event in UAExpert when monitoring o1 and reporting an event on o2. Is this behaviour intended? Having multiple paths is supported according to the OPC specification and my information model allows my users to configure their system in this way. However receiving multiple events does not seem like it would be the desired behaviour from an enduser's perspective. Is there anyway i can work around this behavior?

Thanks,

User avatar
Support Team
Hero Member
Hero Member
Posts: 3072
Joined: 18 Mar 2011, 15:09

Re: [Server] bug updating bidirectional HasNotifier Referere

Post by Support Team »

Hello,

To create a event hierarchy you have to call AddNotifier. If you are adding an inverse HasNotifier reference, AddNotifier is called implicity. This is the main use case, since most
We add the request to call AddNotifier also for forward references to the feature list for the next release.

Best regards
Support Team

alennartz
Jr. Member
Jr. Member
Posts: 4
Joined: 04 Apr 2016, 22:03

Re: [Server] bug updating bidirectional HasNotifier Referere

Post by alennartz »

Thanks for the response, i will play around with the AddNotifier methods as well to see how they behave.

With regards to my other point.
On a somewhat related note: if I have n different paths from a node o1 to a node o2 using chains of HasNotifier references then i will get n copies of the event in UAExpert when monitoring o1 and reporting an event on o2. Is this behaviour intended? Having multiple paths is supported according to the OPC specification and my information model allows my users to configure their system in this way. However receiving multiple events does not seem like it would be the desired behaviour from an enduser's perspective. Is there anyway i can work around this behavior?
are there any options that permit me to change this behavior and still profit from the toolkit layer of the sdk for sending events?

Thanks,

User avatar
Support Team
Hero Member
Hero Member
Posts: 3072
Joined: 18 Mar 2011, 15:09

Re: [Server] bug updating bidirectional HasNotifier Referere

Post by Support Team »

Hello,

We cannot reproduce your scenario. Please send a minimum example project to support@unifiedautomation.com to analyse the problem.

Best regards
Support Team

alennartz
Jr. Member
Jr. Member
Posts: 4
Joined: 04 Apr 2016, 22:03

Re: [Server] bug updating bidirectional HasNotifier Referere

Post by alennartz »

My apologies, by stripping down the code to the bare minimum i found out that i had misinterpreted the root cause of my problem. multiple paths do NOT cause the events to be reported multiple times.

Thanks,

alennartz
Jr. Member
Jr. Member
Posts: 4
Joined: 04 Apr 2016, 22:03

Re: [Server] bug updating bidirectional HasNotifier Referere

Post by alennartz »

I have figured out the source of the issue. it appears that if you do not assign an event ID to the events then the issue i described happens, However if you do assign one then it does not. I will be assigning event ids from now on.

I managed to reproduce the issue using the Lesson01 project from the GettingStarted solution you provide with the sdk and swapping out the node manager in there for this one:

Code: Select all

using System;
using System.Timers;
using UnifiedAutomation.UaBase;
using UnifiedAutomation.UaServer;

namespace YourCompany.GettingStarted
{
    internal class EventTestNodeManager : BaseNodeManager
    {
        private Timer _timer;

        public EventTestNodeManager(ServerManager server, params string[] namespaceUris) : base(server, namespaceUris)
        {
        }


        private NodeId rootId;
        private NodeId o1Id;
        private NodeId o2Id;
        private NodeId sharedId;


        public override void Startup()
        {
            base.Startup();
            DefaultNamespaceIndex = AddNamespaceUri("Http://sample.com");



            rootId = new NodeId("Root", DefaultNamespaceIndex);
            o1Id = new NodeId("o1", DefaultNamespaceIndex);
            o2Id = new NodeId("o2", DefaultNamespaceIndex);
            sharedId = new NodeId("oShared", DefaultNamespaceIndex);


            //root folder.
            var f1 = new CreateObjectSettings()
            {
                ParentNodeId = ObjectIds.ObjectsFolder,
                ReferenceTypeId = ReferenceTypeIds.Organizes,
                RequestedNodeId = rootId,
                BrowseName = new QualifiedName("EventRoot", DefaultNamespaceIndex),
                TypeDefinitionId = ObjectTypeIds.FolderType,
                NotifierParent = ObjectIds.Server,
            };
            
            CreateObject(Server.DefaultRequestContext, f1);

            //o1 as child of root folder
            CreateTestObject(o1Id, "Obj1");

            //o2 as child of root folder
            CreateTestObject(o2Id, "Obj2");

            //oShared as child of root
            CreateTestObject(sharedId, "ObjShared");



            //make obj_shared also a child and notifier of o1 and o2
            UpdateParentChildReference(o1Id, sharedId, true);
            UpdateNotifierReference(o1Id, sharedId, true);

            UpdateParentChildReference(o2Id, sharedId, true);
            UpdateNotifierReference(o2Id, sharedId, true);

            //remove references for root.
            UpdateOrganizesReference(rootId, sharedId, false);
            UpdateNotifierReference(rootId, sharedId, false);


            _timer = new Timer { Interval = 3000 };
            _timer.Elapsed += OnTimerElapsed;
            _timer.AutoReset = true;
            _timer.Enabled = true;
        }

        private void CreateTestObject(NodeId requestedId, string name)
        {
            var oShared = new CreateObjectSettings()
            {
                ParentNodeId = rootId,
                ReferenceTypeId = ReferenceTypeIds.Organizes,
                RequestedNodeId = requestedId,
                BrowseName = new QualifiedName(name, DefaultNamespaceIndex),
                TypeDefinitionId = ObjectTypeIds.BaseObjectType,
                EventNotifier = EventNotifiers.SubscribeToEvents,
                NotifierParent = rootId, //root is initial notifier parent.
            };

            //create object with root as notifier parent.
            CreateObject(Server.DefaultRequestContext, oShared);
        }

        private void UpdateNotifierReference(NodeId parent, NodeId child, bool add)
        {
            if (add)
            {
                AddReference(Server.DefaultRequestContext, child, ReferenceTypeIds.HasNotifier, true, parent, true);
            }
            else
            {
                DeleteReference(Server.DefaultRequestContext, parent, ReferenceTypeIds.HasNotifier, false, child, true);
            }
        }


        private void UpdateOrganizesReference(NodeId parent, NodeId child, bool add)
        {
            if (add)
            {
                AddReference(Server.DefaultRequestContext, parent, ReferenceTypeIds.Organizes, false, child, true);
            }
            else
            {
                DeleteReference(Server.DefaultRequestContext, parent, ReferenceTypeIds.Organizes, false, child, true);
            }
        }



        private void UpdateParentChildReference(NodeId parent, NodeId child, bool add)
        {
            if (add)
            {
                AddReference(Server.DefaultRequestContext, parent, ReferenceTypeIds.HasChild, false, child, true);
            }
            else
            {
                DeleteReference(Server.DefaultRequestContext, parent, ReferenceTypeIds.HasChild, false, child, true);
            }
        }


        private int m_i = 0;

        private void OnTimerElapsed(object sender, ElapsedEventArgs e)
        {

            var df = new DeviceFailureEventModel
            {
                SourceNode = sharedId,
                SourceName = "ObjShared",
                //EventId = Guid.NewGuid().ToByteArray(), //UNCOMMENTING THIS LINE OF CODE RESOLVES THE ISSUE.
                Message = $"Event {++m_i}",
            };

            var ge = df.CreateEvent(Server.FilterManager);


            ReportEvent(sharedId, ge);
        }


        public override void Shutdown()
        {
            base.Shutdown();
            _timer.Enabled = false;
        }
    }
}

from ua expert you then monitor the EventRoot folder. Events will be received 3x

The reason i was not providing an eventId is because i thought that it would be automatically assigned by the sdk if i left it null but probably that only works if you call the Initialze method on the GenericEvent object as is explained in the documentation here:
http://documentation.unified-automation ... ess05.html

It would be nice if the codepath i used above also provided an eventId value in the case where the user does not specify one.

User avatar
Support Team
Hero Member
Hero Member
Posts: 3072
Joined: 18 Mar 2011, 15:09

Re: [Server] bug updating bidirectional HasNotifier Referere

Post by Support Team »

Hello,

You need to call GenericEvent.Initialize or assign an EventId directly.
Anyway we will fix the SDK to send an event only once even if the EventId is not set.

Best regards
Support Team

Post Reply