Professional OPC
Development Tools

logos

Online Forums

Technical support is provided through Support Forums below. Anybody can view them; you need to Register/Login to our site (see links in upper right corner) in order to Post questions. You do not have to be a licensed user of our product.

Please read Rules for forum posts before reporting your issue or asking a question. OPC Labs team is actively monitoring the forums, and replies as soon as possible. Various technical information can also be found in our Knowledge Base. For your convenience, we have also assembled a Frequently Asked Questions page.

Do not use the Contact page for technical issues.

parallel execution of mapping

More
03 Feb 2017 13:23 #4920 by Captain_Dash
You are right, that was my bad.

Please Log in or Create an account to join the conversation.

More
03 Feb 2017 12:57 #4919 by support
I think this is most likely by design. You are truly mapping twice, and that creates more mappings than allowed, and the error says that.

You may want to call Unmap(), or Unmap(mappings) on the mapper in your DeInit(). This would return the mapper to its initial state.

Please Log in or Create an account to join the conversation.

More
03 Feb 2017 12:49 #4918 by Captain_Dash
I found another misbehavior which maybe based on the same issue.

I extended the init method from the example code with an write instruction
    public class MapperTestClass
    {
        private readonly string browseName;
 
        private EasyUAClient client;
 
        private UAClientMapper mapper;
 
        private UANodeDescriptor descriptor;
 
        private UAApplicationElement serverInstance;
 
        private MotionAxis axis = new MotionAxis();
 
        private static readonly object mapLock = new object();
 
        public MapperTestClass(string browseName)
        {
            this.browseName = browseName;
            client = new EasyUAClient();
            mapper = new UAClientMapper(client);
        }
 
        public void Init()
        {
            descriptor = new UANodeDescriptor
            {
                BrowsePath = UABrowsePath.Parse(this.browseName, OpcUaDataRequestMock.DefaultNamespaceUri)
            };
 
            UAApplicationElementCollection serverInstances = client.DiscoverServers("Server");
            serverInstance = serverInstances[0];
 
            lock (mapLock)
            {
                mapper.Map(Axis,
                new UAMappingContext
                {
                    EndpointDescriptor = serverInstance.DiscoveryUriString,
                    NodeDescriptor = descriptor,
                    MonitoringParameters = 20
                });
 
            }
            mapper.Subscribe(true);
            mapper.Read();
 
            Axis.MetaInfo.Enable = true;
            mapper.WriteTarget(Axis.MetaInfo, false);
        }
 
        public MotionAxis Axis { get { return axis; } set { axis = value; } }
 
        public void DeInit()
        {
            Axis.MetaInfo.Enable = false;
            mapper.WriteTarget(Axis.MetaInfo, false);
        }
    }

Usage
            var mapperTestClass  = new MapperTestClass(OpcUaDataRequestMock.AxisA[0]);
            var mapperTestClass1 = new MapperTestClass(OpcUaDataRequestMock.AxisA[1]);
            var mapperTestClass2 = new MapperTestClass(OpcUaDataRequestMock.AxisA[2]);
 
            var mapperTestClass3 = new MapperTestClass(OpcUaDataRequestMock.AxisB[0]);
            var mapperTestClass4 = new MapperTestClass(OpcUaDataRequestMock.AxisB[1]);
            var mapperTestClass5 = new MapperTestClass(OpcUaDataRequestMock.AxisB[2]);
 
            List<MapperTestClass> mapperTestClasses = new List<MapperTestClass>
            {
                mapperTestClass,
                mapperTestClass1,
                mapperTestClass2,
                mapperTestClass3,
                mapperTestClass4,
                mapperTestClass5
            };
 
 
            Parallel.ForEach(mapperTestClasses,
                testClass =>
                    {
                        testClass.Init();
                    });
 
            Parallel.ForEach(mapperTestClasses,
                testClass =>
                    {
                        testClass.DeInit();
                    });
            Parallel.ForEach(mapperTestClasses,
                testClass =>
                    {
                        testClass.Init();
                    });

While the second init is running an ExecutionException is thrown:

An exception of type 'OpcLabs.BaseLib.LiveMapping.ExecutionException' occurred in OpcLabs.EasyOpcUA.dll but was not handled in user code

Additional information: More than one mapping of Value kind for a Write operation on an OPC-UA data mapping source.



Is this based on the same issue? How can we avoid this?

Btw the order will be taken soon, i was missinformed.

Best Regards

Please Log in or Create an account to join the conversation.

More
03 Feb 2017 12:03 #4917 by Captain_Dash
First of all, thank you for this really fast and great response. I am really pleased with your online support. Much respect for that.

We are using the 2016.2 release as eval version in the moment but we ordered a full version with maintainance on Tuesday, so we get the updated when it will be released and it is ok for us to wait for the 2017.1 release.

Best Regards
André

Please Log in or Create an account to join the conversation.

More
03 Feb 2017 11:42 - 03 Feb 2017 11:44 #4916 by support
I have not tried to reproduce this, but I have looked into the code and I probably know what is happening.

You have multiple instances of the mapper class, so theoretically even if the mapper was not thread-safe, they should not collide. But the mapper object as such *is* thread-safe.

The mapper, however, contains a reference to so-called "mapping provider", and by default (for efficiency), all (OPC UA) mappers share the same mapping provider. And, the mapping provider is *not* thread-safe, and that's where the error comes from. We have simply not thought well of the use case where the mapping would run in parallel. One task of the mapping provider is to keep the information collected by reflection from the attributes on the types/members - and of course, once those are collected once for a particular type, by having one provider we can reuse that information next time the same class is mapped, without having to collect it again.

It would be relatively simple to fix this on our side, but in general we try to avoid fixes to current version unless they are critical. We will certainly fix it in the upcoming version (2017.1), but that has some months before the release.

I can see following options at the moment

a) You keep using the workaround you already have. When version 2017.1 is released, you can switch to it and remove the workaround. We can give you version 2017.1 for free even if you have not purchased a maintenance, as a bounty for finding this :-)

b) I can propose a different workaround in which no locking will be needed, but instead you will create the mappers in such way that each of them will use a separate mapping provider. This will be, however, slower and require more memory as compared to the current shared-provider design. When version 2017.1 is released, you can proceed as in a).

c) We can actually fix it into the existing version if that's really important to you, but we do not prefer that. Could do it, if you insist...

I suppose you are using version 2016.2?

Let me know.

Best regards
Last edit: 03 Feb 2017 11:44 by support.
The following user(s) said Thank You: Captain_Dash

Please Log in or Create an account to join the conversation.

More
03 Feb 2017 11:12 #4915 by Captain_Dash
Hello,

I discovered a strange behavior of the mapping class and wanted to know whether it is caused by the client or the server and how to avoid it properly.

First of all we have a class which contains a class that will be mapped to a structure on the server. Of this class we have multiple instances and start parallel execution of a init method where the mapping will be done. When the map method is executed a exception will be thrown. Either this Exception is a NullReferenceException or an ArgumentException. The ArgumentException provide the following message:

An exception of type 'System.ArgumentException' occurred in OpcLabs.BaseLib.dll but was not handled in user code

Additional information: Mapping definition for a type 'XXXTYPEXXX' already exists in the abstract mapping provider.


If I am using a lock around the map method everything is working fine. But in that way it is necessary to know if any other class which uses the mapping will call the method.

I extracted this problem in a little Example:

Example class:
  public class MapperTestClass
    {
        private readonly string browseName;
 
        private EasyUAClient client;
 
        private UAClientMapper mapper;
 
        private UANodeDescriptor descriptor;
 
        private UAApplicationElement serverInstance;
 
        private MotionAxis axis = new MotionAxis();
 
        private static readonly object mapLock = new object();
 
        public MapperTestClass(string browseName)
        {
            this.browseName = browseName;
            client = new EasyUAClient();
            mapper = new UAClientMapper(client);
        }
 
        public void Init()
        {
            descriptor = new UANodeDescriptor
            {
                BrowsePath = UABrowsePath.Parse(this.browseName, OpcUaDataRequestMock.DefaultNamespaceUri)
            };
 
            UAApplicationElementCollection serverInstances = client.DiscoverServers("Server");
            serverInstance = serverInstances[0];
 
            //lock (mapLock)
            //{
                mapper.Map(Axis,
                new UAMappingContext
                {
                    EndpointDescriptor = serverInstance.DiscoveryUriString,
                    NodeDescriptor = descriptor,
                    MonitoringParameters = 20
                });
 
            //}
            mapper.Subscribe(true);
            mapper.Read();
        }
 
        public MotionAxis Axis { get { return axis; } set { axis = value; } }
    }

Usage of the class:
            var mapperTestClass  = new MapperTestClass(OpcUaDataRequestMock.AxisA[0]);
            var mapperTestClass1 = new MapperTestClass(OpcUaDataRequestMock.AxisA[1]);
            var mapperTestClass2 = new MapperTestClass(OpcUaDataRequestMock.AxisA[2]);
 
            var mapperTestClass3 = new MapperTestClass(OpcUaDataRequestMock.AxisB[0]);
            var mapperTestClass4 = new MapperTestClass(OpcUaDataRequestMock.AxisB[1]);
            var mapperTestClass5 = new MapperTestClass(OpcUaDataRequestMock.AxisB[2]);
 
            List<MapperTestClass> mapperTestClasses = new List<MapperTestClass>
            {
                mapperTestClass,
                mapperTestClass1,
                mapperTestClass2,
                mapperTestClass3,
                mapperTestClass4,
                mapperTestClass5
            };
 
 
            Parallel.ForEach(mapperTestClasses,
                testClass =>
                    {
                        testClass.Init();
                    });
 

Best Regards
André

Please Log in or Create an account to join the conversation.

Moderators: support
Time to create page: 0.058 seconds