FreeOpcUa / python-opcua

LGPL Pure Python OPC-UA Client and Server
http://freeopcua.github.io/
GNU Lesser General Public License v3.0
1.34k stars 660 forks source link

ModellingRule_ExposesItsArray doesn't seem to work #1110

Open ruimmaraujo opened 4 years ago

ruimmaraujo commented 4 years ago

I am trying to deploy the modeling rule ExposesItsArray but it seems it is not working by exposing the variable of the array are exposed as a individual Variable in the AddressSpace

By checking the document https://readthedocs.org/projects/python-opcua/downloads/pdf/stable/ it seems that ModellingRule_ExposesItsArray is 83 and I have tried the following line but seems it is not working: dev.add_variable(idx, "sensor1", [6.7, 7.9]).set_modelling_rule(83)

How can enable this rule?

AndreasHeine commented 4 years ago
    def set_modelling_rule(self, mandatory):
        """
        Add a modelling rule reference to Node.
        When creating a new object type, its variable and child nodes will not
        be instanciated if they do not have modelling rule
        if mandatory is None, the modelling rule is removed
        """
        # remove all existing modelling rule
        rules = self.get_references(ua.ObjectIds.HasModellingRule)
        self.server.delete_references(list(map(self._fill_delete_reference_item, rules)))
        # add new modelling rule as requested
        if mandatory is not None:
            rule = ua.ObjectIds.ModellingRule_Mandatory if mandatory else ua.ObjectIds.ModellingRule_Optional
            self.add_reference(rule, ua.ObjectIds.HasModellingRule, True, False)

can be: set_modelling_rule(None) set_modelling_rule(False) set_modelling_rule(True)

guess you need to "add_reference()" instead!

AndreasHeine commented 4 years ago
    def add_reference(self, target, reftype, forward=True, bidirectional=True):
        """
        Add reference to node
        """

        aitem = ua.AddReferencesItem()
        aitem.SourceNodeId = self.nodeid
        aitem.TargetNodeId = _to_nodeid(target)
        aitem.ReferenceTypeId = _to_nodeid(reftype)
        aitem.IsForward = forward

        params = [aitem]

        if bidirectional:
            aitem2 = ua.AddReferencesItem()
            aitem2.SourceNodeId = aitem.TargetNodeId
            aitem2.TargetNodeId = aitem.SourceNodeId
            aitem2.ReferenceTypeId = aitem.ReferenceTypeId
            aitem2.IsForward = not forward
            params.append(aitem2)

        results = self.server.add_references(params)
        for r in results:
            r.check()

maybe: dev.add_reference(ua.ObjectIds.ModellingRule_ExposesItsArray, ua.ObjectIds.HasModellingRule, True, False)

ruimmaraujo commented 4 years ago

thank you @AndreasHeine for the feedback. I haven't considered the add reference. I gave it a try but seems without success

image

In the image below the sensor values of the array are include in the sensor1 variable but as expected as individual nodes :S

image

AndreasHeine commented 4 years ago

https://reference.opcfoundation.org/v104/Core/docs/Part3/6.4.4/#6.4.4.5#6.4.4.5.4

6.4.4.5.4 ExposesItsArray ToC

The ExposesItsArray ModellingRule exposes a special semantic on VariableTypes having a single- or multidimensional array as the data type. It indicates that each value of the array will also be exposed as a Variable in the AddressSpace.

The ExposesItsArray ModellingRule can only be applied on InstanceDeclarations of NodeClass Variable that are part of a VariableType having a single- or multidimensional array as its data type.

The Variable A having this ModellingRule shall be referenced by a forward hierarchical Reference from a VariableType B. B shall have a ValueRank value that is equal to or larger than zero. A should have a data type that reflects at least parts of the data that is managed in the array of B. Each instance of B shall reference one instance of A for each of its array elements. The used Reference shall be of the same type as the hierarchical Reference that connects B with A or a subtype of it. If there are more than one forward hierarchical References between A and B, then all instances based on B shall be referenced with all those References.

Figure 21 gives an example. A is an instance of Type_A having two entries in its value array. Therefore it references two instances of the same type as the InstanceDeclaration ArrayExpose. The BrowseNames of those instances are not defined by the ModellingRule. In general, it is not possible to get a Variable representing a specific entry in the array (e.g. the second). Clients will typically either get the array or access the Variables directly, so there is no need to provide that information.

ruimmaraujo commented 4 years ago

@AndreasHeine thank you once again for the feedback. I actually had a look at that document.

and even this one https://readthedocs.org/projects/python-opcua/downloads/pdf/stable/ but it is not very clear how it is implemented :/

image image

I initially thought by set a modelling rule 83 as an argument it would expose the array.. .but no clue how to activate this...

oroulet commented 4 years ago

This is for sure not implement. I am also not sure we should implement that kind of strange things. The protocol and code is complicated enough with the features that make sense

ruimmaraujo commented 4 years ago

Hi @oroulet thank for clearfying. It is actually useful for the client to read as a node in a position of the array e.g. sensor1[0] ,sensor [1] ... instead all packed in one variable. I will see how this could be implemented ... again thank you for feedback 👍

oroulet commented 4 years ago

There is nothing bad or difficult in making every sensor available individually. Then also as an array. It does not need to be part of protocol

oroulet commented 4 years ago

But contributions are welcome anyway

AndreasHeine commented 4 years ago

it's one of those convenience functions ;)

AndreasHeine commented 4 years ago

do you know a stack which has implemented this functionality just to test what happens if we add the reference in our server variable?

ruimmaraujo commented 4 years ago

Unified Automation has implemented this in their stack. In the image bekow is an example of how it woukd be done. All variables of the array has its own node. image002

zerox1212 commented 4 years ago

I don't understand this feature. What is the problem with just getting the entire array and dealing with the sensor you want? Are you trying to reduce communication overhead or something? Or is it just for convenience that you don't want to deal with entire array (in which case why did they design it as an array in the first place?)

Seems like this just programmers solving a problem that doesn't exist lol.

AndreasHeine commented 4 years ago

@zerox1212 some parts of spec. make sense if you have a companion spec. in mind (especialy modeling rules and naming rules) so you could auto generate a number of variables with just a modelimg rule reference!

device internaly its an array for some reason so you simply write it in once but as a interface it is in some cases better to seperate them to maybe read only a few of them

edit: its not one of the features most devs need, thats for sure 😜

AndreasHeine commented 4 years ago

@oroulet maybe we can implement a method in node.py similar to "set_modelling_rule" for "expose its array" which simply add_variables for each array-element

edit: updating them if the parent got written would be something for @JoeyFaulkner callback topic 😉

ruimmaraujo commented 4 years ago

@AndreasHeine thanks for the feedback. I would be glad to contribute to this. in the automation world it is quite used the ExposesItsArray

I did some experiments to see how it would be possible something like can work. In no shape or form I am saying we should :)

image

image

AndreasHeine commented 4 years ago

"for i in testarray:" would be faster and more pythonic, range method generates a list but you already have one 😉

edit: in python there is only the for each like loop

oroulet commented 4 years ago

My issue is that if we do it inside opcua we need to register somewhere that these are the same values and that updating one exposed node must also update that value inside array... This is some code that make things more complicated and slower

AndreasHeine commented 4 years ago

updating one exposed node must also update that value inside array...

i try a quick prove of concept with an serverside subscription!

edit: just like a optional utility not a core feature!

AndreasHeine commented 4 years ago

read or write only works but not together... without callbacks not realy practical to be honest -_-°