Commit 9b946e22 authored by oroulet's avatar oroulet Committed by oroulet

client and server seems to work

parent 7d735413
...@@ -168,7 +168,8 @@ class UASocketProtocol(asyncio.Protocol): ...@@ -168,7 +168,8 @@ class UASocketProtocol(asyncio.Protocol):
try: try:
self._callbackmap[request_id].set_result(body) self._callbackmap[request_id].set_result(body)
except KeyError: except KeyError:
raise ua.UaError(f"No request found for request id: {request_id}, pending are {self._callbackmap.keys()}") #raise ua.UaError(f"No request found for request id: {request_id}, pending are {self._callbackmap.keys()}, body was {body}")
pass
except asyncio.InvalidStateError: except asyncio.InvalidStateError:
if not self.closed: if not self.closed:
self.logger.warning("Future for request id %s is already done", request_id) self.logger.warning("Future for request id %s is already done", request_id)
......
...@@ -23,7 +23,7 @@ async def instantiate(parent, node_type, nodeid=None, bname=None, dname=None, id ...@@ -23,7 +23,7 @@ async def instantiate(parent, node_type, nodeid=None, bname=None, dname=None, id
rdesc.TypeDefinition = node_type.nodeid rdesc.TypeDefinition = node_type.nodeid
if nodeid is None: if nodeid is None:
nodeid = ua.NodeId(namespaceidx=idx) # will trigger automatic node generation in namespace idx nodeid = ua.NodeId(NamespaceIndex=idx) # will trigger automatic node generation in namespace idx
if bname is None: if bname is None:
bname = rdesc.BrowseName bname = rdesc.BrowseName
elif isinstance(bname, str): elif isinstance(bname, str):
...@@ -112,7 +112,7 @@ async def _instantiate_node(server, ...@@ -112,7 +112,7 @@ async def _instantiate_node(server,
c_node_type, c_node_type,
res.AddedNodeId, res.AddedNodeId,
c_rdesc, c_rdesc,
nodeid=ua.NodeId(identifier=inst_nodeid, namespaceidx=res.AddedNodeId.NamespaceIndex), nodeid=ua.NodeId(Identifier=inst_nodeid, NamespaceIndex=res.AddedNodeId.NamespaceIndex),
bname=c_rdesc.BrowseName, bname=c_rdesc.BrowseName,
instantiate_optional=instantiate_optional instantiate_optional=instantiate_optional
) )
...@@ -122,7 +122,7 @@ async def _instantiate_node(server, ...@@ -122,7 +122,7 @@ async def _instantiate_node(server,
c_node_type, c_node_type,
res.AddedNodeId, res.AddedNodeId,
c_rdesc, c_rdesc,
nodeid=ua.NodeId(namespaceidx=res.AddedNodeId.NamespaceIndex), nodeid=ua.NodeId(NamespaceIndex=res.AddedNodeId.NamespaceIndex),
bname=c_rdesc.BrowseName, bname=c_rdesc.BrowseName,
instantiate_optional=instantiate_optional instantiate_optional=instantiate_optional
) )
......
...@@ -398,7 +398,7 @@ async def _create_method(parent, nodeid, qname, callback, inputs, outputs): ...@@ -398,7 +398,7 @@ async def _create_method(parent, nodeid, qname, callback, inputs, outputs):
if inputs: if inputs:
await create_property( await create_property(
method, method,
ua.NodeId(namespaceidx=method.nodeid.NamespaceIndex), ua.NodeId(NamespaceIndex=method.nodeid.NamespaceIndex),
ua.QualifiedName("InputArguments", 0), ua.QualifiedName("InputArguments", 0),
[_vtype_to_argument(vtype) for vtype in inputs], [_vtype_to_argument(vtype) for vtype in inputs],
varianttype=ua.VariantType.ExtensionObject, varianttype=ua.VariantType.ExtensionObject,
...@@ -407,7 +407,7 @@ async def _create_method(parent, nodeid, qname, callback, inputs, outputs): ...@@ -407,7 +407,7 @@ async def _create_method(parent, nodeid, qname, callback, inputs, outputs):
if outputs: if outputs:
await create_property( await create_property(
method, method,
ua.NodeId(namespaceidx=method.nodeid.NamespaceIndex), ua.NodeId(NamespaceIndex=method.nodeid.NamespaceIndex),
ua.QualifiedName("OutputArguments", 0), ua.QualifiedName("OutputArguments", 0),
[_vtype_to_argument(vtype) for vtype in outputs], [_vtype_to_argument(vtype) for vtype in outputs],
varianttype=ua.VariantType.ExtensionObject, varianttype=ua.VariantType.ExtensionObject,
......
...@@ -5,13 +5,11 @@ format is the one from opc-ua specification ...@@ -5,13 +5,11 @@ format is the one from opc-ua specification
import logging import logging
import uuid import uuid
from typing import Union, Dict from typing import Union, Dict
from copy import copy
from asyncua import ua from asyncua import ua
from .xmlparser import XMLParser, ua_type_to_python from .xmlparser import XMLParser, ua_type_to_python
from ..ua.uaerrors import UaError from ..ua.uaerrors import UaError
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
...@@ -53,15 +51,11 @@ class XmlImporter: ...@@ -53,15 +51,11 @@ class XmlImporter:
server_model_list = [] server_model_list = []
server_namespaces_node = await self.server.nodes.namespaces.get_children() server_namespaces_node = await self.server.nodes.namespaces.get_children()
for model_node in server_namespaces_node: for model_node in server_namespaces_node:
server_model_list.append( server_model_list.append({
{ "ModelUri": await (await model_node.get_child("NamespaceUri")).read_value(),
"ModelUri": await (await model_node.get_child("NamespaceUri")).read_value(), "Version": await (await model_node.get_child("NamespaceVersion")).read_value(),
"Version": await (await model_node.get_child("NamespaceVersion")).read_value(), "PublicationDate": (await (await model_node.get_child("NamespacePublicationDate")).read_value()).strftime("%Y-%m-%dT%H:%M:%SZ"),
"PublicationDate": ( })
await (await model_node.get_child("NamespacePublicationDate")).read_value()
).strftime("%Y-%m-%dT%H:%M:%SZ"),
}
)
return server_model_list return server_model_list
async def _check_required_models(self, xmlpath=None, xmlstring=None): async def _check_required_models(self, xmlpath=None, xmlstring=None):
...@@ -71,10 +65,7 @@ class XmlImporter: ...@@ -71,10 +65,7 @@ class XmlImporter:
server_model_list = await self._get_existing_model_in_namespace() server_model_list = await self._get_existing_model_in_namespace()
for model in server_model_list: for model in server_model_list:
for req_model in req_models: for req_model in req_models:
if ( if (model["ModelUri"] == req_model["ModelUri"] and model["PublicationDate"] >= req_model["PublicationDate"]):
model["ModelUri"] == req_model["ModelUri"]
and model["PublicationDate"] >= req_model["PublicationDate"]
):
if "Version" in model and "Version" in req_model: if "Version" in model and "Version" in req_model:
if model["Version"] >= req_model["Version"]: if model["Version"] >= req_model["Version"]:
req_models.remove(req_model) req_models.remove(req_model)
...@@ -123,15 +114,14 @@ class XmlImporter: ...@@ -123,15 +114,14 @@ class XmlImporter:
if missing_nodes: if missing_nodes:
_logger.warning(f"The following references exist, but the Nodes are missing: {missing_nodes}") _logger.warning(f"The following references exist, but the Nodes are missing: {missing_nodes}")
if len(self.refs): if len(self.refs):
_logger.warning("The following references could not be imported and are probably broken: %s", self.refs,) _logger.warning(
"The following references could not be imported and are probably broken: %s",
self.refs,
)
return nodes return nodes
async def _add_missing_reverse_references(self, new_nodes): async def _add_missing_reverse_references(self, new_nodes):
__unidirectional_types = {ua.ObjectIds.GuardVariableType, ua.ObjectIds.HasGuard, __unidirectional_types = {ua.ObjectIds.GuardVariableType, ua.ObjectIds.HasGuard, ua.ObjectIds.TransitionVariableType, ua.ObjectIds.StateMachineType, ua.ObjectIds.StateVariableType, ua.ObjectIds.TwoStateVariableType, ua.ObjectIds.StateType, ua.ObjectIds.TransitionType, ua.ObjectIds.FiniteTransitionVariableType, ua.ObjectIds.HasInterface}
ua.ObjectIds.TransitionVariableType, ua.ObjectIds.StateMachineType,
ua.ObjectIds.StateVariableType, ua.ObjectIds.TwoStateVariableType,
ua.ObjectIds.StateType, ua.ObjectIds.TransitionType,
ua.ObjectIds.FiniteTransitionVariableType, ua.ObjectIds.HasInterface}
dangling_refs_to_missing_nodes = set() dangling_refs_to_missing_nodes = set()
for new_node_id in new_nodes: for new_node_id in new_nodes:
new_n = self.server.get_node(new_node_id) new_n = self.server.get_node(new_node_id)
...@@ -163,9 +153,9 @@ class XmlImporter: ...@@ -163,9 +153,9 @@ class XmlImporter:
for ref in nd.refs: for ref in nd.refs:
if ref.forward: if ref.forward:
if ref.reftype in [ if ref.reftype in [
self.server.nodes.HasComponent.nodeid, self.server.nodes.HasComponent.nodeid,
self.server.nodes.HasProperty.nodeid, self.server.nodes.HasProperty.nodeid,
self.server.nodes.Organizes.nodeid, self.server.nodes.Organizes.nodeid,
]: ]:
# if a node has several links, the last one will win # if a node has several links, the last one will win
if ref.target in childs: if ref.target in childs:
...@@ -237,7 +227,7 @@ class XmlImporter: ...@@ -237,7 +227,7 @@ class XmlImporter:
field.datatype = self._to_migrated_nodeid(field.datatype) field.datatype = self._to_migrated_nodeid(field.datatype)
return new_nodes return new_nodes
def _migrate_ns(self, nodeid: ua.NodeId) -> ua.NodeId: def _migrate_ns(self, obj: Union[ua.NodeId, ua.QualifiedName]) -> Union[ua.NodeId, ua.QualifiedName]:
""" """
Check if the index of nodeid or browsename given in the xml model file Check if the index of nodeid or browsename given in the xml model file
must be converted to a already existing namespace id based on the files must be converted to a already existing namespace id based on the files
...@@ -245,10 +235,13 @@ class XmlImporter: ...@@ -245,10 +235,13 @@ class XmlImporter:
:returns: NodeId (str) :returns: NodeId (str)
""" """
if nodeid.NamespaceIndex in self.namespaces: if isinstance(obj, ua.NodeId):
nodeid = copy(nodeid) if obj.NamespaceIndex in self.namespaces:
nodeid.NamespaceIndex = self.namespaces[nodeid.NamespaceIndex] obj = ua.NodeId(Identifier=obj.Identifier, NamespaceIndex=self.namespaces[obj.NamespaceIndex], NodeIdType=obj.NodeIdType)
return nodeid if isinstance(obj, ua.QualifiedName):
if obj.NamespaceIndex in self.namespaces:
obj = ua.QualifiedName(Name=obj.Name, NamespaceIndex=self.namespaces[obj.NamespaceIndex])
return obj
def _get_add_node_item(self, obj): def _get_add_node_item(self, obj):
node = ua.AddNodesItem() node = ua.AddNodesItem()
...@@ -322,9 +315,7 @@ class XmlImporter: ...@@ -322,9 +315,7 @@ class XmlImporter:
attrs.DisplayName = ua.LocalizedText(obj.displayname) attrs.DisplayName = ua.LocalizedText(obj.displayname)
attrs.DataType = obj.datatype attrs.DataType = obj.datatype
if obj.value is not None: if obj.value is not None:
attrs.Value = self._add_variable_value( attrs.Value = self._add_variable_value(obj, )
obj,
)
if obj.rank: if obj.rank:
attrs.ValueRank = obj.rank attrs.ValueRank = obj.rank
if obj.accesslevel: if obj.accesslevel:
...@@ -417,20 +408,13 @@ class XmlImporter: ...@@ -417,20 +408,13 @@ class XmlImporter:
values.append(extobj) values.append(extobj)
return ua.Variant(values, ua.VariantType.ExtensionObject) return ua.Variant(values, ua.VariantType.ExtensionObject)
elif obj.valuetype == "ListOfGuid": elif obj.valuetype == "ListOfGuid":
return ua.Variant( return ua.Variant([uuid.UUID(guid) for guid in obj.value], getattr(ua.VariantType, obj.valuetype[6:]))
[uuid.UUID(guid) for guid in obj.value], getattr(ua.VariantType, obj.valuetype[6:])
)
elif obj.valuetype.startswith("ListOf"): elif obj.valuetype.startswith("ListOf"):
vtype = obj.valuetype[6:] vtype = obj.valuetype[6:]
if hasattr(ua.ua_binary.Primitives, vtype): if hasattr(ua.ua_binary.Primitives, vtype):
return ua.Variant(obj.value, getattr(ua.VariantType, vtype)) return ua.Variant(obj.value, getattr(ua.VariantType, vtype))
elif vtype == "LocalizedText": elif vtype == "LocalizedText":
return ua.Variant( return ua.Variant([getattr(ua, vtype)(Text=item["Text"], Locale=item["Locale"]) for item in obj.value])
[
getattr(ua, vtype)(text=item["Text"], locale=item["Locale"])
for item in obj.value
]
)
else: else:
return ua.Variant([getattr(ua, vtype)(v) for v in obj.value]) return ua.Variant([getattr(ua, vtype)(v) for v in obj.value])
elif obj.valuetype == "ExtensionObject": elif obj.valuetype == "ExtensionObject":
...@@ -570,11 +554,11 @@ class XmlImporter: ...@@ -570,11 +554,11 @@ class XmlImporter:
f = ua.EnumField() f = ua.EnumField()
f.Name = field.name f.Name = field.name
if field.dname: if field.dname:
f.DisplayName = ua.LocalizedText(text=field.dname) f.DisplayName = ua.LocalizedText(Text=field.dname)
else: else:
f.DisplayName = ua.LocalizedText(text=field.name) f.DisplayName = ua.LocalizedText(Text=field.name)
f.Value = field.value f.Value = field.value
f.Description = ua.LocalizedText(text=field.desc) f.Description = ua.LocalizedText(Text=field.desc)
edef.Fields.append(f) edef.Fields.append(f)
return edef return edef
...@@ -599,7 +583,7 @@ class XmlImporter: ...@@ -599,7 +583,7 @@ class XmlImporter:
if f.IsOptional: if f.IsOptional:
optional = True optional = True
f.ArrayDimensions = field.arraydim f.ArrayDimensions = field.arraydim
f.Description = ua.LocalizedText(text=field.desc) f.Description = ua.LocalizedText(Text=field.desc)
sdef.Fields.append(f) sdef.Fields.append(f)
if optional: if optional:
sdef.StructureType = ua.StructureType.StructureWithOptionalFields sdef.StructureType = ua.StructureType.StructureWithOptionalFields
...@@ -621,11 +605,7 @@ class XmlImporter: ...@@ -621,11 +605,7 @@ class XmlImporter:
all_node_ids = [data.nodeid for data in ndatas] all_node_ids = [data.nodeid for data in ndatas]
while ndatas: while ndatas:
for ndata in ndatas[:]: for ndata in ndatas[:]:
if ( if (ndata.nodeid.NamespaceIndex not in self.namespaces.values() or ndata.parent is None or ndata.parent not in all_node_ids):
ndata.nodeid.NamespaceIndex not in self.namespaces.values()
or ndata.parent is None
or ndata.parent not in all_node_ids
):
sorted_ndatas.append(ndata) sorted_ndatas.append(ndata)
sorted_nodes_ids.append(ndata.nodeid) sorted_nodes_ids.append(ndata.nodeid)
ndatas.remove(ndata) ndatas.remove(ndata)
......
...@@ -350,12 +350,13 @@ def nodeid_to_binary(nodeid): ...@@ -350,12 +350,13 @@ def nodeid_to_binary(nodeid):
def nodeid_from_binary(data): def nodeid_from_binary(data):
encoding = ord(data.read(1)) encoding = ord(data.read(1))
nidtype = ua.NodeIdType(encoding & 0b00111111) nidtype = ua.NodeIdType(encoding & 0b00111111)
expanded = False uri = None
server_idx = None
if nidtype == ua.NodeIdType.TwoByte: if nidtype == ua.NodeIdType.TwoByte:
identifier = ord(data.read(1)) identifier = ord(data.read(1))
return ua.TwoByteNodeId(identifier) nidx = 0
if nidtype == ua.NodeIdType.FourByte: elif nidtype == ua.NodeIdType.FourByte:
nidx, identifier = struct.unpack("<BH", data.read(3)) nidx, identifier = struct.unpack("<BH", data.read(3))
elif nidtype == ua.NodeIdType.Numeric: elif nidtype == ua.NodeIdType.Numeric:
nidx, identifier = struct.unpack("<HI", data.read(6)) nidx, identifier = struct.unpack("<HI", data.read(6))
...@@ -373,12 +374,10 @@ def nodeid_from_binary(data): ...@@ -373,12 +374,10 @@ def nodeid_from_binary(data):
if test_bit(encoding, 7): if test_bit(encoding, 7):
uri = Primitives.String.unpack(data) uri = Primitives.String.unpack(data)
expanded = True
if test_bit(encoding, 6): if test_bit(encoding, 6):
server_idx = Primitives.UInt32.unpack(data) server_idx = Primitives.UInt32.unpack(data)
expanded = True
if expanded: if uri is not None or server_idx is not None:
return ua.ExpandedNodeId(identifier, nidx, nidtype, uri, server_idx) return ua.ExpandedNodeId(identifier, nidx, nidtype, uri, server_idx)
return ua.NodeId(identifier, nidx, nidtype) return ua.NodeId(identifier, nidx, nidtype)
...@@ -449,7 +448,7 @@ def extensionobject_from_binary(data): ...@@ -449,7 +448,7 @@ def extensionobject_from_binary(data):
body = data.copy(length) body = data.copy(length)
data.skip(length) data.skip(length)
if typeid.Identifier == 0: if typeid.Identifier == 0:
return None return ua.ExtensionObject()
elif typeid in ua.extension_objects_by_typeid: elif typeid in ua.extension_objects_by_typeid:
cls = ua.extension_objects_by_typeid[typeid] cls = ua.extension_objects_by_typeid[typeid]
if body is None: if body is None:
......
This diff is collapsed.
...@@ -11,16 +11,16 @@ OPC_TCP_SCHEME = 'opc.tcp' ...@@ -11,16 +11,16 @@ OPC_TCP_SCHEME = 'opc.tcp'
@dataclass @dataclass
class Hello(uatypes.FrozenClass): class Hello(uatypes.FrozenClass):
ProtocolVersion: uatypes.UInt32 = 0 ProtocolVersion: uatypes.UInt32 = 0
# the following values couldbe set to 0 (meaning no limits) # the following values couldbe set to 0 (meaning no limits)
# unfortunaltely many servers do not support it # unfortunaltely many servers do not support it
# even newer version of prosys are broken # even newer version of prosys are broken
# so we set then to a high value known to work most places # so we set then to a high value known to work most places
ReceiveBufferSize: uatypes.UInt32 = 2**31 - 1 ReceiveBufferSize: uatypes.UInt32 = 2**31 - 1
SendBufferSize: uatypes.UInt32 = 2**31 - 1 SendBufferSize: uatypes.UInt32 = 2**31 - 1
MaxMessageSize: uatypes.UInt32 = 2**31 - 1 MaxMessageSize: uatypes.UInt32 = 2**31 - 1
MaxChunkCount: uatypes.UInt32 = 2**31 - 1 MaxChunkCount: uatypes.UInt32 = 2**31 - 1
EndpointUrl: uatypes.String = "" EndpointUrl: uatypes.String = ""
@dataclass @dataclass
...@@ -49,6 +49,7 @@ class Header(uatypes.FrozenClass): ...@@ -49,6 +49,7 @@ class Header(uatypes.FrozenClass):
ChannelId: int = 0 ChannelId: int = 0
body_size = 0 body_size = 0
packet_size = 0 packet_size = 0
header_size = 8
def add_size(self, size): def add_size(self, size):
self.body_size += size self.body_size += size
......
...@@ -263,7 +263,7 @@ class StatusCode: ...@@ -263,7 +263,7 @@ class StatusCode:
:vartype doc: string :vartype doc: string
""" """
value: UInt32 = 0 value: UInt32 = status_codes.StatusCodes.Good
def __post_init__(self): def __post_init__(self):
if isinstance(self.value, str): if isinstance(self.value, str):
...@@ -385,7 +385,6 @@ class NodeId: ...@@ -385,7 +385,6 @@ class NodeId:
def __lt__(self, other): def __lt__(self, other):
if not isinstance(other, NodeId): if not isinstance(other, NodeId):
raise AttributeError("Can only compare to NodeId") raise AttributeError("Can only compare to NodeId")
print("COMPARE", self, other)
print (self.NodeIdType, self.NamespaceIndex, self.Identifier, other.NodeIdType, other.NamespaceIndex, other.Identifier) print (self.NodeIdType, self.NamespaceIndex, self.Identifier, other.NodeIdType, other.NamespaceIndex, other.Identifier)
return (self.NodeIdType, self.NamespaceIndex, self.Identifier) < (other.NodeIdType, other.NamespaceIndex, other.Identifier) return (self.NodeIdType, self.NamespaceIndex, self.Identifier) < (other.NodeIdType, other.NamespaceIndex, other.Identifier)
...@@ -643,7 +642,7 @@ class ExtensionObject: ...@@ -643,7 +642,7 @@ class ExtensionObject:
TypeId: NodeId = NodeId() TypeId: NodeId = NodeId()
Encoding: Byte = field(default=0, repr=False, init=False) Encoding: Byte = field(default=0, repr=False, init=False)
Body: Optional[ByteString] = b"" Body: Optional[ByteString] = None
def __bool__(self): def __bool__(self):
return self.Body is not None return self.Body is not None
...@@ -910,7 +909,7 @@ class DataValue: ...@@ -910,7 +909,7 @@ class DataValue:
Encoding: Byte = field(default=0, repr=False, init=False) Encoding: Byte = field(default=0, repr=False, init=False)
Value: Optional[Variant] = None Value: Optional[Variant] = None
StatusCode: Optional[StatusCode] = None # field(default_factory=StatusCode) StatusCode_: Optional[StatusCode] = field(default_factory=StatusCode)
SourceTimestamp: Optional[DateTime] = None SourceTimestamp: Optional[DateTime] = None
SourcePicoseconds: Optional[UInt16] = None SourcePicoseconds: Optional[UInt16] = None
ServerTimestamp: Optional[DateTime] = None ServerTimestamp: Optional[DateTime] = None
...@@ -922,6 +921,14 @@ class DataValue: ...@@ -922,6 +921,14 @@ class DataValue:
if not isinstance(self.Value, Variant): if not isinstance(self.Value, Variant):
self.Value = Variant(self.Value) self.Value = Variant(self.Value)
@property
def StatusCode(self):
return self.StatusCode_
@StatusCode.setter
def StatusCode(self, val):
self.StatusCode_ = val
def datatype_to_varianttype(int_type): def datatype_to_varianttype(int_type):
""" """
......
...@@ -7,9 +7,6 @@ from asyncua import ua, Server ...@@ -7,9 +7,6 @@ from asyncua import ua, Server
from asyncua.common.methods import uamethod from asyncua.common.methods import uamethod
logging.basicConfig(level=logging.INFO)
_logger = logging.getLogger('asyncua')
@uamethod @uamethod
def func(parent, value): def func(parent, value):
...@@ -17,6 +14,7 @@ def func(parent, value): ...@@ -17,6 +14,7 @@ def func(parent, value):
async def main(): async def main():
_logger = logging.getLogger('asyncua')
# setup our server # setup our server
server = Server() server = Server()
await server.init() await server.init()
...@@ -43,4 +41,7 @@ async def main(): ...@@ -43,4 +41,7 @@ async def main():
if __name__ == '__main__': if __name__ == '__main__':
asyncio.run(main())
logging.basicConfig(level=logging.DEBUG)
asyncio.run(main(), debug=True)
...@@ -80,7 +80,7 @@ class CodeGenerator: ...@@ -80,7 +80,7 @@ class CodeGenerator:
self.write('') self.write('')
self.write('from datetime import datetime') self.write('from datetime import datetime')
self.write('from enum import IntEnum') self.write('from enum import IntEnum')
self.write('from typing import Union, List') self.write('from typing import Union, List, Optional')
self.write('from dataclasses import dataclass, field') self.write('from dataclasses import dataclass, field')
self.write('') self.write('')
# self.write('from asyncua.ua.uaerrors import UaError') # self.write('from asyncua.ua.uaerrors import UaError')
...@@ -137,14 +137,21 @@ class CodeGenerator: ...@@ -137,14 +137,21 @@ class CodeGenerator:
if "BodyLength" in [f.name for f in obj.fields]: if "BodyLength" in [f.name for f in obj.fields]:
extobj_hack = True extobj_hack = True
hack_names = []
for field in obj.fields: for field in obj.fields:
# FIXME; flag optional those that are optional
if field.length: if field.length:
typestring = f"List[{field.uatype}]" typestring = f"List[{field.uatype}]"
elif field.switchfield is not None:
typestring = f"Optional[{field.uatype}]"
else: else:
typestring = field.uatype typestring = field.uatype
if field.name == field.uatype: # help!!! selv referencing class if field.name == field.uatype:
# variable name and type name are the same. Dataclass do not like it
print("SELF REFENCING", obj, field) print("SELF REFENCING", obj, field)
hack_names.append(field.name)
fieldname = field.name + "_" fieldname = field.name + "_"
else: else:
fieldname = field.name fieldname = field.name
...@@ -153,8 +160,8 @@ class CodeGenerator: ...@@ -153,8 +160,8 @@ class CodeGenerator:
val = 0 if not extobj_hack else 1 val = 0 if not extobj_hack else 1
self.write(f"{field.name}: Byte = field(default={val}, repr=False, init=False)") self.write(f"{field.name}: Byte = field(default={val}, repr=False, init=False)")
elif field.uatype == obj.name: # help!!! selv referencing class elif field.uatype == obj.name: # help!!! selv referencing class
pass #FIXME: handle better
#FIXME: handle self.write(f"{fieldname}: Optional[ExtensionObject] = None")
elif obj.name not in ("ExtensionObject",) and \ elif obj.name not in ("ExtensionObject",) and \
field.name == "TypeId": # and ( obj.name.endswith("Request") or obj.name.endswith("Response")): field.name == "TypeId": # and ( obj.name.endswith("Request") or obj.name.endswith("Response")):
self.write(f"TypeId: NodeId = FourByteNodeId(ObjectIds.{obj.name}_Encoding_DefaultBinary)") self.write(f"TypeId: NodeId = FourByteNodeId(ObjectIds.{obj.name}_Encoding_DefaultBinary)")
...@@ -175,6 +182,17 @@ class CodeGenerator: ...@@ -175,6 +182,17 @@ class CodeGenerator:
if switch_written: if switch_written:
self.write("}") self.write("}")
if hack_names:
self.write("")
for name in hack_names:
self.write("@property")
self.write(f"def {name}(self):")
self.write(f" return self.{name}_")
self.write("")
self.write(f"@{name}.setter")
self.write(f"def {name}(self, val):")
self.write(f" self.{name}_ = val")
self.iidx = 0 self.iidx = 0
def write_unpack_enum(self, name, enum): def write_unpack_enum(self, name, enum):
......
...@@ -441,6 +441,13 @@ def test_null_string(): ...@@ -441,6 +441,13 @@ def test_null_string():
assert v.Value == v2.Value assert v.Value == v2.Value
def test_empty_extension_object():
obj = ua.ExtensionObject()
obj2 = extensionobject_from_binary(ua.utils.Buffer(extensionobject_to_binary(obj)))
assert type(obj) == type(obj2)
assert obj == obj2
def test_extension_object(): def test_extension_object():
obj = ua.UserNameIdentityToken() obj = ua.UserNameIdentityToken()
obj.UserName = "admin" obj.UserName = "admin"
...@@ -732,6 +739,8 @@ def test_bin_data_type_def(): ...@@ -732,6 +739,8 @@ def test_bin_data_type_def():
dta = ua.DataTypeAttributes() dta = ua.DataTypeAttributes()
dta.DisplayName = ua.LocalizedText("titi") dta.DisplayName = ua.LocalizedText("titi")
ad.NodeAttributes = dta ad.NodeAttributes = dta
from IPython import embed
embed()
data = struct_to_binary(ad) data = struct_to_binary(ad)
ad2 = struct_from_binary(ua.AddNodesItem, ua.utils.Buffer(data)) ad2 = struct_from_binary(ua.AddNodesItem, ua.utils.Buffer(data))
...@@ -746,3 +755,24 @@ def test_bin_datattributes(): ...@@ -746,3 +755,24 @@ def test_bin_datattributes():
data = struct_to_binary(dta) data = struct_to_binary(dta)
dta2 = struct_from_binary(ua.DataTypeAttributes, ua.utils.Buffer(data)) dta2 = struct_from_binary(ua.DataTypeAttributes, ua.utils.Buffer(data))
assert dta.DisplayName == dta2.DisplayName assert dta.DisplayName == dta2.DisplayName
def test_browse():
data = b'\x01\x00\x12\x02\xe0S2\xb3\x8f\n\xd7\x01\x04\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x03\x00\x00\x00\x00#\x01@U\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00Objects\x02\x07\x00\x00\x00Objects\x01\x00\x00\x00@=\x00\x00\x00\x00\x00#\x01@V\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00Types\x02\x05\x00\x00\x00Types\x01\x00\x00\x00@=\x00\x00\x00\x00\x00#\x01@W\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00Views\x02\x05\x00\x00\x00Views\x01\x00\x00\x00@=\x00\x00\x00\x00\xff\xff\xff\xff'
#data = b'\x01\x00\x12\x020)E\x11"\n\xd7\x01\x04\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x03\x00\x00\x00\x00#\x01@U\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00Objects\x02\x07\x00\x00\x00Objects\x01\x00\x00\x00@=\x00\x00\x00\x00\x00#\x01@V\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00Types\x02\x05\x00\x00\x00Types\x01\x00\x00\x00@=\x00\x00\x00\x00\x00#\x01@W\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00Views\x02\x05\x00\x00\x00Views\x01\x00\x00\x00@=\x00\x00\x00\x00\xff\xff\xff\xff'
res = struct_from_binary(ua.BrowseResponse, ua.utils.Buffer(data))
def test_bname():
qn = ua.QualifiedName("TOTO", 2)
d = struct_to_binary(qn)
qn2 = struct_from_binary(ua.QualifiedName, ua.utils.Buffer(d))
assert qn == qn2
def test_expandedNodeId():
d = b"\x40\x55\x00\x00\x00\x00"
nid = nodeid_from_binary(ua.utils.Buffer(d))
assert isinstance(nid, ua.ExpandedNodeId)
assert nid.ServerIndex == 0
assert nid.Identifier == 85
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