Commit 005d35b4 authored by Fabian Beitler's avatar Fabian Beitler Committed by oroulet

Change file-path structure

Rewrite if statement to easier readable code
Replace Path expressions a better way?
Add absolute to get absolute path to file (I hope)
Remove one obsolete import pytest
Refactor method name to better name
Redo nested if statement a better way
Remove forgotten garbage methods
Replace os path with pathlib
Small Codestyle fixes
Add check if both or neither xml arg was passed -> not xor
Fix wrong required model handling
Differ between xmlstrin and file
Add tests for requirement check
Add two xml files for test purposes
Replace ImportError with Exception
Remove unused var
List all missing requirements
Check server models in seperate Coroutine
Add method to check requirements while xmlimport
Add namespaces Node to Shortcuts
parent 1f5ac6e5
......@@ -24,6 +24,7 @@ class Shortcuts(object):
self.variable_types = Node(server, ObjectIds.VariableTypesFolder)
self.object_types = Node(server, ObjectIds.ObjectTypesFolder)
self.namespace_array = Node(server, ObjectIds.Server_NamespaceArray)
self.namespaces = Node(server, ObjectIds.Server_Namespaces)
self.opc_binary = Node(server, ObjectIds.OPCBinarySchema_TypeSystem)
self.base_structure_type = Node(server, ObjectIds.Structure)
self.server_state = Node(server, ObjectIds.Server_ServerStatus_State)
......
......@@ -44,12 +44,46 @@ class XmlImporter:
aliases_mapped[alias] = self._to_migrated_nodeid(node_id)
return aliases_mapped
async def _get_existing_model_in_namespace(self):
server_model_list = []
server_namespaces_node = await self.server.nodes.namespaces.get_children()
for model_node in server_namespaces_node:
server_model_list.append({"ModelUri": await(await model_node.get_child("NamespaceUri")).read_value(),
"Version": await(await model_node.get_child("NamespaceVersion")).read_value(),
"PublicationDate": str(await(
await model_node.get_child("NamespacePublicationDate")).read_value())})
return server_model_list
async def _check_required_models(self, xmlpath=None, xmlstring=None):
req_models = self.parser.list_required_models(xmlpath, xmlstring)
if not req_models:
return None
server_model_list = await self._get_existing_model_in_namespace()
for model in server_model_list:
for req_model in req_models:
if model["ModelUri"] == req_model["ModelUri"] and \
model["PublicationDate"] >= req_model["PublicationDate"]:
if "Version" in model and "Version" in req_model:
if model["Version"] >= req_model["Version"]:
req_models.remove(req_model)
else:
req_models.remove(req_model)
if len(req_models):
for missing_model in server_model_list:
_logger.warning("Model is missing: ", missing_model)
raise ValueError("Server doesn't satisfy required XML-Models. Import them first!")
return None
async def import_xml(self, xmlpath=None, xmlstring=None):
"""
import xml and return added nodes
"""
if (xmlpath is None and xmlstring is None) or (xmlpath and xmlstring):
raise ValueError("Expected either xmlpath or xmlstring, not both or neither.")
_logger.info("Importing XML file %s", xmlpath)
self.parser = XMLParser()
await self._check_required_models(xmlpath, xmlstring)
await self.parser.parse(xmlpath, xmlstring)
self.namespaces = await self._map_namespaces(self.parser.get_used_namespaces())
_logger.info("namespace map: %s", self.namespaces)
......@@ -81,10 +115,13 @@ class XmlImporter:
missing.append(nd)
for ref in nd.refs:
if ref.forward:
if ref.reftype in [self.server.nodes.HasComponent.nodeid, self.server.nodes.HasProperty.nodeid, self.server.nodes.Organizes.nodeid]:
if ref.reftype in [self.server.nodes.HasComponent.nodeid,
self.server.nodes.HasProperty.nodeid,
self.server.nodes.Organizes.nodeid]:
# if a node has several links, the last one will win
if ref.target in childs:
_logger.warning("overwriting parent target, shouldbe fixed", ref.target, nd.nodeid, ref.reftype, childs[ref.target])
_logger.warning("overwriting parent target, shouldbe fixed",
ref.target, nd.nodeid, ref.reftype, childs[ref.target])
childs[ref.target] = (nd.nodeid, ref.reftype)
for nd in missing:
if nd.nodeid in childs:
......@@ -166,7 +203,7 @@ class XmlImporter:
node.RequestedNewNodeId = self._migrate_ns(obj.nodeid)
node.BrowseName = self._migrate_ns(obj.browsename)
_logger.info("Importing xml node (%s, %s) as (%s %s)", obj.browsename,
obj.nodeid, node.BrowseName, node.RequestedNewNodeId)
obj.nodeid, node.BrowseName, node.RequestedNewNodeId)
node.NodeClass = getattr(ua.NodeClass, obj.nodetype[2:])
if obj.parent and obj.parentlink:
node.ParentNodeId = self._migrate_ns(obj.parent)
......@@ -427,7 +464,8 @@ class XmlImporter:
if self.server.nodes.base_structure_type in path:
attrs.DataTypeDefinition = self._get_sdef(node, obj)
else:
_logger.warning("%s has datatypedefinition and path %s but we could not find out if this is a struct", obj, path)
_logger.warning("%s has datatypedefinition and path %s"
" but we could not find out if this is a struct", obj, path)
node.NodeAttributes = attrs
res = await self._get_server().add_nodes([node])
res[0].StatusCode.check()
......
......@@ -7,7 +7,6 @@ import base64
import logging
from pytz import utc
import xml.etree.ElementTree as ET
from .ua_utils import string_to_val
......@@ -67,6 +66,7 @@ class NodeData:
__repr__ = __str__
class Field:
def __init__(self, data):
self.datatype = data.get("DataType", "")
......@@ -74,10 +74,11 @@ class Field:
self.dname = data.get("DisplayName", "")
self.optional = bool(data.get("IsOptional", False))
self.valuerank = int(data.get("ValueRank", -1))
self.arraydim = data.get("ArrayDimensions", None) #FIXME: check type
self.arraydim = data.get("ArrayDimensions", None) # FIXME: check type
self.value = int(data.get("Value", 0))
self.desc = data.get("Description", "")
class RefStruct:
def __init__(self):
......@@ -87,6 +88,7 @@ class RefStruct:
def __str__(self):
return f"RefStruct({self.reftype, self.forward, self.target})"
__repr__ = __str__
......@@ -384,3 +386,21 @@ class XMLParser:
obj.parent, obj.parentlink = parent, parentlink
if not obj.parent:
self.logger.info("Could not find parent for node '%s'", obj.nodeid)
@staticmethod
def list_required_models(xmlpath, xmlstring):
"""
Try getting required XML Models, before parsing NodeSet
"""
if xmlpath:
tree = ET.parse(xmlpath)
else:
tree = ET.fromstring(xmlstring)
required_models = []
for child in tree.iter():
if child.tag.endswith("RequiredModel"):
# check if ModelUri X, in Version Y from time Z was already imported
required_models.append(child.attrib)
return required_models
<?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://yourorganisation.org/testenum104/Types.xsd" xmlns:ua="http://unifiedautomation.com/Configuration/NodeSet.xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<NamespaceUris>
<Uri>http://yourorganisation.org/testenum104/</Uri>
</NamespaceUris>
<Models>
<Model ModelUri="http://yourorganisation.org/testenum104/" PublicationDate="2020-08-11T04:54:44Z" Version="1.0.0">
<RequiredModel ModelUri="http://opcfoundation.org/UA/" PublicationDate="2019-09-09T00:00:00Z" Version="1.0"/>
<RequiredModel ModelUri="http://senseless-modeluri_1.trollface/UA/" PublicationDate="1992-09-09T00:00:00Z" Version="1.04.3"/>
<RequiredModel ModelUri="http://senseless-modeluri_2.the_cake_is_a_lie/UA/" PublicationDate="2011-12-20T00:00:00Z" Version="1.04.3"/>
</Model>
</Models>
<Aliases>
<Alias Alias="Boolean">i=1</Alias>
<Alias Alias="String">i=12</Alias>
<Alias Alias="DateTime">i=13</Alias>
<Alias Alias="LocalizedText">i=21</Alias>
<Alias Alias="HasModellingRule">i=37</Alias>
<Alias Alias="HasTypeDefinition">i=40</Alias>
<Alias Alias="HasSubtype">i=45</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="QRHcxoxT9xtx6MQr8YC8og==" Version="1.6.3"/>
</Extension>
</Extensions>
<UADataType NodeId="ns=1;i=3002" BrowseName="1:MyEnum">
<DisplayName>MyEnum</DisplayName>
<References>
<Reference ReferenceType="HasProperty">ns=1;i=6001</Reference>
<Reference ReferenceType="HasSubtype" IsForward="false">i=29</Reference>
</References>
<Definition Name="1:MyEnum">
<Field Name="val1" Value="0"/>
<Field Name="val2" Value="1"/>
<Field Name="val3" Value="2"/>
</Definition>
</UADataType>
<UAVariable DataType="LocalizedText" ParentNodeId="ns=1;i=3002" ValueRank="1" NodeId="ns=1;i=6001" ArrayDimensions="3" BrowseName="EnumStrings">
<DisplayName>EnumStrings</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=3002</Reference>
<Reference ReferenceType="HasModellingRule">i=78</Reference>
</References>
<Value>
<uax:ListOfLocalizedText>
<uax:LocalizedText>
<uax:Text>val1</uax:Text>
</uax:LocalizedText>
<uax:LocalizedText>
<uax:Text>val2</uax:Text>
</uax:LocalizedText>
<uax:LocalizedText>
<uax:Text>val3</uax:Text>
</uax:LocalizedText>
</uax:ListOfLocalizedText>
</Value>
</UAVariable>
<UAObject SymbolicName="http___yourorganisation_org_testenum104_" NodeId="ns=1;i=5001" BrowseName="1:http://yourorganisation.org/testenum104/">
<DisplayName>http://yourorganisation.org/testenum104/</DisplayName>
<References>
<Reference ReferenceType="HasProperty">ns=1;i=6002</Reference>
<Reference ReferenceType="HasTypeDefinition">i=11616</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">i=11715</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>
<Reference ReferenceType="HasProperty">ns=1;i=6008</Reference>
</References>
</UAObject>
<UAVariable DataType="Boolean" ParentNodeId="ns=1;i=5001" NodeId="ns=1;i=6002" BrowseName="IsNamespaceSubset">
<DisplayName>IsNamespaceSubset</DisplayName>
<References>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=5001</Reference>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
</References>
<Value>
<uax:Boolean>false</uax:Boolean>
</Value>
</UAVariable>
<UAVariable DataType="DateTime" ParentNodeId="ns=1;i=5001" NodeId="ns=1;i=6003" BrowseName="NamespacePublicationDate">
<DisplayName>NamespacePublicationDate</DisplayName>
<References>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=5001</Reference>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
</References>
<Value>
<uax:DateTime>2020-08-11T04:54:44Z</uax:DateTime>
</Value>
</UAVariable>
<UAVariable DataType="String" ParentNodeId="ns=1;i=5001" NodeId="ns=1;i=6004" BrowseName="NamespaceUri">
<DisplayName>NamespaceUri</DisplayName>
<References>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=5001</Reference>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
</References>
<Value>
<uax:String>http://yourorganisation.org/testenum104/</uax:String>
</Value>
</UAVariable>
<UAVariable DataType="String" ParentNodeId="ns=1;i=5001" NodeId="ns=1;i=6005" BrowseName="NamespaceVersion">
<DisplayName>NamespaceVersion</DisplayName>
<References>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=5001</Reference>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
</References>
<Value>
<uax:String>1.0.0</uax:String>
</Value>
</UAVariable>
<UAVariable DataType="IdType" ParentNodeId="ns=1;i=5001" ValueRank="1" NodeId="ns=1;i=6006" ArrayDimensions="0" BrowseName="StaticNodeIdTypes">
<DisplayName>StaticNodeIdTypes</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=5001</Reference>
</References>
</UAVariable>
<UAVariable DataType="NumericRange" ParentNodeId="ns=1;i=5001" ValueRank="1" NodeId="ns=1;i=6007" ArrayDimensions="0" BrowseName="StaticNumericNodeIdRange">
<DisplayName>StaticNumericNodeIdRange</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=5001</Reference>
</References>
</UAVariable>
<UAVariable DataType="String" ParentNodeId="ns=1;i=5001" NodeId="ns=1;i=6008" BrowseName="StaticStringNodeIdPattern">
<DisplayName>StaticStringNodeIdPattern</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=5001</Reference>
</References>
</UAVariable>
</UANodeSet>
<?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://yourorganisation.org/testenum104/Types.xsd" xmlns:ua="http://unifiedautomation.com/Configuration/NodeSet.xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<NamespaceUris>
<Uri>http://yourorganisation.org/testenum104/</Uri>
</NamespaceUris>
<Models>
<Model ModelUri="http://yourorganisation.org/testenum104/" PublicationDate="2020-08-11T04:54:44Z" Version="1.0.0">
<RequiredModel ModelUri="http://opcfoundation.org/UA/" PublicationDate="2020-04-13T04:54:44Z"/>
</Model>
</Models>
<Aliases>
<Alias Alias="Boolean">i=1</Alias>
<Alias Alias="String">i=12</Alias>
<Alias Alias="DateTime">i=13</Alias>
<Alias Alias="LocalizedText">i=21</Alias>
<Alias Alias="HasModellingRule">i=37</Alias>
<Alias Alias="HasTypeDefinition">i=40</Alias>
<Alias Alias="HasSubtype">i=45</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="QRHcxoxT9xtx6MQr8YC8og==" Version="1.6.3"/>
</Extension>
</Extensions>
<UADataType NodeId="ns=1;i=3002" BrowseName="1:MyEnum">
<DisplayName>MyEnum</DisplayName>
<References>
<Reference ReferenceType="HasProperty">ns=1;i=6001</Reference>
<Reference ReferenceType="HasSubtype" IsForward="false">i=29</Reference>
</References>
<Definition Name="1:MyEnum">
<Field Name="val1" Value="0"/>
<Field Name="val2" Value="1"/>
<Field Name="val3" Value="2"/>
</Definition>
</UADataType>
<UAVariable DataType="LocalizedText" ParentNodeId="ns=1;i=3002" ValueRank="1" NodeId="ns=1;i=6001" ArrayDimensions="3" BrowseName="EnumStrings">
<DisplayName>EnumStrings</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=3002</Reference>
<Reference ReferenceType="HasModellingRule">i=78</Reference>
</References>
<Value>
<uax:ListOfLocalizedText>
<uax:LocalizedText>
<uax:Text>val1</uax:Text>
</uax:LocalizedText>
<uax:LocalizedText>
<uax:Text>val2</uax:Text>
</uax:LocalizedText>
<uax:LocalizedText>
<uax:Text>val3</uax:Text>
</uax:LocalizedText>
</uax:ListOfLocalizedText>
</Value>
</UAVariable>
<UAObject SymbolicName="http___yourorganisation_org_testenum104_" NodeId="ns=1;i=5001" BrowseName="1:http://yourorganisation.org/testenum104/">
<DisplayName>http://yourorganisation.org/testenum104/</DisplayName>
<References>
<Reference ReferenceType="HasProperty">ns=1;i=6002</Reference>
<Reference ReferenceType="HasTypeDefinition">i=11616</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">i=11715</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>
<Reference ReferenceType="HasProperty">ns=1;i=6008</Reference>
</References>
</UAObject>
<UAVariable DataType="Boolean" ParentNodeId="ns=1;i=5001" NodeId="ns=1;i=6002" BrowseName="IsNamespaceSubset">
<DisplayName>IsNamespaceSubset</DisplayName>
<References>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=5001</Reference>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
</References>
<Value>
<uax:Boolean>false</uax:Boolean>
</Value>
</UAVariable>
<UAVariable DataType="DateTime" ParentNodeId="ns=1;i=5001" NodeId="ns=1;i=6003" BrowseName="NamespacePublicationDate">
<DisplayName>NamespacePublicationDate</DisplayName>
<References>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=5001</Reference>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
</References>
<Value>
<uax:DateTime>2020-08-11T04:54:44Z</uax:DateTime>
</Value>
</UAVariable>
<UAVariable DataType="String" ParentNodeId="ns=1;i=5001" NodeId="ns=1;i=6004" BrowseName="NamespaceUri">
<DisplayName>NamespaceUri</DisplayName>
<References>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=5001</Reference>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
</References>
<Value>
<uax:String>http://yourorganisation.org/testenum104/</uax:String>
</Value>
</UAVariable>
<UAVariable DataType="String" ParentNodeId="ns=1;i=5001" NodeId="ns=1;i=6005" BrowseName="NamespaceVersion">
<DisplayName>NamespaceVersion</DisplayName>
<References>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=5001</Reference>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
</References>
<Value>
<uax:String>1.0.0</uax:String>
</Value>
</UAVariable>
<UAVariable DataType="IdType" ParentNodeId="ns=1;i=5001" ValueRank="1" NodeId="ns=1;i=6006" ArrayDimensions="0" BrowseName="StaticNodeIdTypes">
<DisplayName>StaticNodeIdTypes</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=5001</Reference>
</References>
</UAVariable>
<UAVariable DataType="NumericRange" ParentNodeId="ns=1;i=5001" ValueRank="1" NodeId="ns=1;i=6007" ArrayDimensions="0" BrowseName="StaticNumericNodeIdRange">
<DisplayName>StaticNumericNodeIdRange</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=5001</Reference>
</References>
</UAVariable>
<UAVariable DataType="String" ParentNodeId="ns=1;i=5001" NodeId="ns=1;i=6008" BrowseName="StaticStringNodeIdPattern">
<DisplayName>StaticStringNodeIdPattern</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=5001</Reference>
</References>
</UAVariable>
</UANodeSet>
import os
import uuid
import pytz
import pytest
import logging
import datetime
import pathlib
import pytest
......@@ -17,8 +16,11 @@ logger.setLevel(logging.DEBUG)
pytestmark = pytest.mark.asyncio
CUSTOM_NODES_XML_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "custom_nodes.xml"))
CUSTOM_NODES_NS_XML_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), "custom_nodesns.xml"))
BASE_DIR = pathlib.Path(__file__).parent.absolute()
CUSTOM_NODES_XML_PATH = BASE_DIR / "custom_nodes.xml"
CUSTOM_NODES_NS_XML_PATH = BASE_DIR / "custom_nodesns.xml"
CUSTOM_REQ_XML_PASS_PATH = BASE_DIR / "test_requirement_pass.xml"
CUSTOM_REQ_XML_FAIL_PATH = BASE_DIR / "test_requirement_fail.xml"
@uamethod
......@@ -438,3 +440,11 @@ async def test_xml_byte(opc, tmpdir):
assert dtype == await o2.read_data_type()
assert dv.Value == (await o2.read_data_value()).Value
await opc.opc.delete_nodes([o2])
async def test_xml_required_models_fail(opc):
with pytest.raises(ValueError):
await opc.opc.import_xml(CUSTOM_REQ_XML_FAIL_PATH)
async def test_xml_required_models_pass(opc):
await opc.opc.import_xml(CUSTOM_REQ_XML_PASS_PATH)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment