opcua-asyncio
opcua-asyncio copied to clipboard
Unable to get_child() from UaModeller xml nodeset
Hello,
Im trying to import a simple XML nodeset from UaModeller but I keep getting the following error:
Traceback (most recent call last):
File "c:\Users\SVL\OneDrive - University of Cambridge\unified-architecture\opcua-server\xml-server.py", line 25, in <module>
asyncio.run(main())
File "C:\Users\SVL\AppData\Local\Programs\Python\Python39-32\lib\asyncio\runners.py", line 44, in run
return loop.run_until_complete(main)
File "C:\Users\SVL\AppData\Local\Programs\Python\Python39-32\lib\asyncio\base_events.py", line 647, in run_until_complete
return future.result()
File "c:\Users\SVL\OneDrive - University of Cambridge\unified-architecture\opcua-server\xml-server.py", line 15, in main
knauer = await objects.get_child("1:Kanuer")
File "C:\Users\SVL\AppData\Local\Programs\Python\Python39-32\lib\site-packages\asyncua\common\node.py", line 503, in get_child
result.StatusCode.check()
File "C:\Users\SVL\AppData\Local\Programs\Python\Python39-32\lib\site-packages\asyncua\ua\uatypes.py", line 328, in check
raise UaStatusCodeError(self.value)
asyncua.ua.uaerrors._auto.BadNoMatch: "The requested operation has no match to return."(BadNoMatch)
My code is based off the example xml-server and comprises of a single object "knauer" and a variable "Pressure" which is being populated with a random number ever 100ms. Simply importing the XML without trying to link to the nodes works with the nodes appearing in UaExpert. However, when I try to link to the nodes using get_child() I get an error?
import asyncio
from asyncua import ua, Server
from asyncua.common.methods import uamethod
import random
async def main():
server = Server()
server.set_endpoint('opc.tcp://0.0.0.0:4840')
await server.init()
await server.import_xml(r'opcua-server\p4_1s.xml')
objects = server.nodes.objects
knauer = await objects.get_child("1:Kanuer")
pressure = await knauer.get_child("1:Pressure")
async with server:
while True:
await pressure.write_value(random.random())
await asyncio.sleep(0.1)
if __name__ == '__main__':
asyncio.run(main())
My model was generated using UaModeller.
<?xml version="1.0" encoding="utf-8"?>
<UANodeSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:uax="http://opcfoundation.org/UA/2008/02/Types.xsd" xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd" xmlns:s1="http://github.com/om327/unified-automation/Types.xsd" xmlns:ua="http://unifiedautomation.com/Configuration/NodeSet.xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<NamespaceUris>
<Uri>http://github.com/om327/unified-automation</Uri>
</NamespaceUris>
<Aliases>
<Alias Alias="Boolean">i=1</Alias>
<Alias Alias="Double">i=11</Alias>
<Alias Alias="String">i=12</Alias>
<Alias Alias="DateTime">i=13</Alias>
<Alias Alias="Organizes">i=35</Alias>
<Alias Alias="HasTypeDefinition">i=40</Alias>
<Alias Alias="HasProperty">i=46</Alias>
<Alias Alias="HasComponent">i=47</Alias>
<Alias Alias="IdType">i=256</Alias>
<Alias Alias="NumericRange">i=291</Alias>
</Aliases>
<Extensions>
<Extension>
<ua:ModelInfo Tool="UaModeler" Hash="oDfr+Nds+p0BjkomhYVqGA==" Version="1.6.6"/>
</Extension>
</Extensions>
<UAObject NodeId="ns=1;i=5001" BrowseName="1:Knauer">
<DisplayName>Knauer</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=58</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=6008</Reference>
<Reference ReferenceType="Organizes" IsForward="false">i=85</Reference>
</References>
</UAObject>
<UAVariable DataType="Double" NodeId="ns=1;i=6008" BrowseName="1:Pressure" AccessLevel="3">
<DisplayName>Pressure</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=5001</Reference>
</References>
<Value>
<uax:Double>0</uax:Double>
</Value>
</UAVariable>
<UAObject SymbolicName="http___github_com_om327_unified_automation" NodeId="ns=1;i=5002" BrowseName="1:http://github.com/om327/unified-automation">
<DisplayName>http://github.com/om327/unified-automation</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=11616</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">i=11715</Reference>
<Reference ReferenceType="HasProperty">ns=1;i=6001</Reference>
<Reference ReferenceType="HasProperty">ns=1;i=6002</Reference>
<Reference ReferenceType="HasProperty">ns=1;i=6003</Reference>
<Reference ReferenceType="HasProperty">ns=1;i=6004</Reference>
<Reference ReferenceType="HasProperty">ns=1;i=6005</Reference>
<Reference ReferenceType="HasProperty">ns=1;i=6006</Reference>
<Reference ReferenceType="HasProperty">ns=1;i=6007</Reference>
</References>
</UAObject>
<UAVariable DataType="Boolean" ParentNodeId="ns=1;i=5002" NodeId="ns=1;i=6001" BrowseName="IsNamespaceSubset">
<DisplayName>IsNamespaceSubset</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=5002</Reference>
</References>
<Value>
<uax:Boolean>false</uax:Boolean>
</Value>
</UAVariable>
<UAVariable DataType="DateTime" ParentNodeId="ns=1;i=5002" NodeId="ns=1;i=6002" BrowseName="NamespacePublicationDate">
<DisplayName>NamespacePublicationDate</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=5002</Reference>
</References>
<Value>
<uax:DateTime>2022-07-22T00:15:42Z</uax:DateTime>
</Value>
</UAVariable>
<UAVariable DataType="String" ParentNodeId="ns=1;i=5002" NodeId="ns=1;i=6003" BrowseName="NamespaceUri">
<DisplayName>NamespaceUri</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=5002</Reference>
</References>
<Value>
<uax:String>http://github.com/om327/unified-automation</uax:String>
</Value>
</UAVariable>
<UAVariable DataType="String" ParentNodeId="ns=1;i=5002" NodeId="ns=1;i=6004" BrowseName="NamespaceVersion">
<DisplayName>NamespaceVersion</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=5002</Reference>
</References>
<Value>
<uax:String>1.0.0</uax:String>
</Value>
</UAVariable>
<UAVariable DataType="IdType" ParentNodeId="ns=1;i=5002" ValueRank="1" NodeId="ns=1;i=6005" ArrayDimensions="0" BrowseName="StaticNodeIdTypes">
<DisplayName>StaticNodeIdTypes</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=5002</Reference>
</References>
</UAVariable>
<UAVariable DataType="NumericRange" ParentNodeId="ns=1;i=5002" ValueRank="1" NodeId="ns=1;i=6006" ArrayDimensions="0" BrowseName="StaticNumericNodeIdRange">
<DisplayName>StaticNumericNodeIdRange</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=5002</Reference>
</References>
</UAVariable>
<UAVariable DataType="String" ParentNodeId="ns=1;i=5002" NodeId="ns=1;i=6007" BrowseName="StaticStringNodeIdPattern">
<DisplayName>StaticStringNodeIdPattern</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=5002</Reference>
</References>
</UAVariable>
</UANodeSet>
interestingly there was another error when importing if the following lines were present in the XML:
<Models>
<Model ModelUri="http://github.com/om327/unified-automation" PublicationDate="2022-07-22T00:15:42Z" Version="1.0.0">
<RequiredModel ModelUri="http://opcfoundation.org/UA/" PublicationDate="2022-01-24T00:00:00Z" Version="1.05.01"/>
</Model>
</Models>
Any help would be fantastic! Thanks!
First of for such kind of problems it is always good to check your addresspace via a testclient such as UAExpert.
My therory is that your namespace index is not 1 so maybe try this:
import asyncio
from asyncua import ua, Server
from asyncua.common.methods import uamethod
import random
async def main():
server = Server()
server.set_endpoint('opc.tcp://0.0.0.0:4840')
await server.init()
await server.import_xml(r'opcua-server\p4_1s.xml')
ns = await server.get_namespace_index("http://github.com/om327/unified-automation")
objects = server.nodes.objects
knauer = await objects.get_child(f"{ns}:Kanuer")
pressure = await knauer.get_child(f"{ns}:Pressure")
async with server:
while True:
await pressure.write_value(random.random())
await asyncio.sleep(0.1)
if __name__ == '__main__':
asyncio.run(main())
We currently ship 1.04.XX nodesets in the future we will update to 1.05.XX. Maybe you can change the required nodeset some where in UAModller.
Thanks @schroeder the get_namespace() function returns a value of 2, whereas in the XML this is defined as 1:
<UAObject NodeId="ns=1;i=5001" BrowseName="1:Knauer">
Unfortunately, substituting the namespace value using your code still returns the same error?
Intrestingly in UaModeller the kanuer objectType has a namespace of 1.
Importing and running the XML without trying to bind will give me the correct server in UaExpert:
import asyncio
from asyncua import ua, Server
from asyncua.common.methods import uamethod
import random
async def main():
server = Server()
server.set_endpoint('opc.tcp://0.0.0.0:4840')
await server.init()
await server.import_xml(r'opcua-server\p4_1s.xml')
async with server:
while True:
await asyncio.sleep(0.1)
if __name__ == '__main__':
asyncio.run(main())
Is it something to do with the the XML parser or has UaModeller changed the way it outputs XML schema?
Thanks again!
First of the namespace ids in nodeset are relative. So 1 just means its using the first namespace in the nodeset xml. Othwise it would be impossible to load multiple nodesets. The namespaces idx depends on insertation order of new namespaces. Every new namespace gets the a new idx. 0 -> opc ua namespace 1 -> toolkit namespace 2 -> first registered namespace 3 -> second registerd namespace ...
Most opc ua librarys reserve namespace 1 for internal use.
Hi @schroeder- daft question, when using a tool like UaModeller how do we import multiple XML schema?
Do these need to be imported as separate XML files with different URLs or one XML file with the different URLs in the schema?
await server.init()
await server.import_xml(r'r-series.xml')
ns = await server.get_namespace_index("http://github.com/om327/unified-architecture/r-series")
await server.import_xml(r'gx271.xml')
ns2 = await server.get_namespace_index("http://github.com/om327/unified-architecture/gx271")
This doesn't seem to work which makes me think I should import just one XML?
Importing multiple xml is supported, maybe there is something else wrong. It would be easier if you could post a sample xml file so I can check what is going wrong.
Hi @schroeder- ,
Thanks for getting back to me - I have some code for some lab equipment I can send you. It was created using UaModeller - I must admit I'm a bit new to OPCUA and using XML models.
The grand plan would be to dynamically import XML models for equipment that is detected on a network - is this possible with python. I've been playing with python dictionaries for looping through the XML nodes address. This is so that for instances of there being two pieces of the same equipment they can both be loaded into the server as two separate objects but using the same XML - is this the best wat to do this, are there any examples of this type of use case?
Really appreciate the help!
It is possible to import multiple XML models. But they either should have their own namespace or at least no colliding Nodeids, which can be tricky with UaModeller. Also another approach would be to delete unused nodes. Also you could just create ObjectTypes/VariableTypes in your XML and instantiate the Objects and Variables as needed, to match the equipment.
Trivial thing, and I assume this is just a typo when giving the information: Th call stack says "Kanuer", the XML says "Knauer". You've ruled out that error source, right? (Because that kind of typo would give that exact error)