Commit d59f63b7 authored by Christian Bergmiller's avatar Christian Bergmiller

[ADD] refactor server

parent 654c7665
import asyncio
from opcua import ua, Server
from opcua.common.methods import uamethod
@uamethod
def func(parent, value):
return value * 2
async def task(loop):
# setup our server
server = Server()
await server.init()
server.set_endpoint('opc.tcp://0.0.0.0:4840/freeopcua/server/')
server.set_endpoint('opc.tcp://127.0.0.1:8080/freeopcua/server/') #4840
# setup our own namespace, not really necessary but should as spec
uri = 'http://examples.freeopcua.github.io'
idx = await server.register_namespace(uri)
# get Objects node, this is where we should put our nodes
objects = server.get_objects_node()
# populating our address space
myobj = await objects.add_object(idx, 'MyObject')
myvar = await myobj.add_variable(idx, 'MyVariable', 6.7)
await myvar.set_writable() # Set MyVariable to be writable by clients
await objects.add_method(
ua.NodeId("ServerMethod", 2), ua.QualifiedName('ServerMethod', 2),
func, [ua.VariantType.Int64], [ua.VariantType.Int64]
)
# starting!
async with server:
count = 0
......
......@@ -7,19 +7,18 @@ from opcua.common.node import Node
logger = logging.getLogger(__name__)
def copy_node(parent, node, nodeid=None, recursive=True):
async def copy_node(parent, node, nodeid=None, recursive=True):
"""
Copy a node or node tree as child of parent node
"""
rdesc = _rdesc_from_node(parent, node)
rdesc = await _rdesc_from_node(parent, node)
if nodeid is None:
nodeid = ua.NodeId(namespaceidx=node.nodeid.NamespaceIndex)
added_nodeids = _copy_node(parent.server, parent.nodeid, rdesc, nodeid, recursive)
return [Node(parent.server, nid) for nid in added_nodeids]
def _copy_node(server, parent_nodeid, rdesc, nodeid, recursive):
async def _copy_node(server, parent_nodeid, rdesc, nodeid, recursive):
addnode = ua.AddNodesItem()
addnode.RequestedNewNodeId = nodeid
addnode.BrowseName = rdesc.BrowseName
......@@ -27,18 +26,13 @@ def _copy_node(server, parent_nodeid, rdesc, nodeid, recursive):
addnode.ReferenceTypeId = rdesc.ReferenceTypeId
addnode.TypeDefinition = rdesc.TypeDefinition
addnode.NodeClass = rdesc.NodeClass
node_to_copy = Node(server, rdesc.NodeId)
attrObj = getattr(ua, rdesc.NodeClass.name + "Attributes")
_read_and_copy_attrs(node_to_copy, attrObj(), addnode)
res = server.add_nodes([addnode])[0]
attr_obj = getattr(ua, rdesc.NodeClass.name + "Attributes")
await _read_and_copy_attrs(node_to_copy, attr_obj(), addnode)
res = await server.add_nodes([addnode])[0]
added_nodes = [res.AddedNodeId]
if recursive:
descs = node_to_copy.get_children_descriptions()
descs = await node_to_copy.get_children_descriptions()
for desc in descs:
nodes = _copy_node(server, res.AddedNodeId, desc, nodeid=ua.NodeId(namespaceidx=desc.NodeId.NamespaceIndex), recursive=True)
added_nodes.extend(nodes)
......@@ -46,30 +40,33 @@ def _copy_node(server, parent_nodeid, rdesc, nodeid, recursive):
return added_nodes
def _rdesc_from_node(parent, node):
results = node.get_attributes([ua.AttributeIds.NodeClass, ua.AttributeIds.BrowseName, ua.AttributeIds.DisplayName])
async def _rdesc_from_node(parent, node):
results = await node.get_attributes([
ua.AttributeIds.NodeClass, ua.AttributeIds.BrowseName, ua.AttributeIds.DisplayName,
])
nclass, qname, dname = [res.Value.Value for res in results]
rdesc = ua.ReferenceDescription()
rdesc.NodeId = node.nodeid
rdesc.BrowseName = qname
rdesc.DisplayName = dname
rdesc.NodeClass = nclass
if parent.get_type_definition() == ua.NodeId(ua.ObjectIds.FolderType):
if await parent.get_type_definition() == ua.NodeId(ua.ObjectIds.FolderType):
rdesc.ReferenceTypeId = ua.NodeId(ua.ObjectIds.Organizes)
else:
rdesc.ReferenceTypeId = ua.NodeId(ua.ObjectIds.HasComponent)
typedef = node.get_type_definition()
typedef = await node.get_type_definition()
if typedef:
rdesc.TypeDefinition = typedef
return rdesc
def _read_and_copy_attrs(node_type, struct, addnode):
names = [name for name in struct.__dict__.keys() if not name.startswith("_") and name not in ("BodyLength", "TypeId", "SpecifiedAttributes", "Encoding", "IsAbstract", "EventNotifier")]
async def _read_and_copy_attrs(node_type, struct, addnode):
names = [name for name in struct.__dict__.keys() if not name.startswith("_") and name not in (
"BodyLength", "TypeId", "SpecifiedAttributes", "Encoding", "IsAbstract", "EventNotifier",
)]
attrs = [getattr(ua.AttributeIds, name) for name in names]
for name in names:
results = node_type.get_attributes(attrs)
results = await node_type.get_attributes(attrs)
for idx, name in enumerate(names):
if results[idx].StatusCode.is_good():
if name == "Value":
......
......@@ -14,14 +14,14 @@ from opcua.common.copy_node import _rdesc_from_node, _read_and_copy_attrs
logger = logging.getLogger(__name__)
def instantiate(parent, node_type, nodeid=None, bname=None, dname=None, idx=0):
async def instantiate(parent, node_type, nodeid=None, bname=None, dname=None, idx=0):
"""
instantiate a node type under a parent node.
nodeid and browse name of new node can be specified, or just namespace index
If they exists children of the node type, such as components, variables and
properties are also instantiated
"""
rdesc = _rdesc_from_node(parent, node_type)
rdesc = await _rdesc_from_node(parent, node_type)
rdesc.TypeDefinition = node_type.nodeid
if nodeid is None:
......@@ -31,11 +31,14 @@ def instantiate(parent, node_type, nodeid=None, bname=None, dname=None, idx=0):
elif isinstance(bname, str):
bname = ua.QualifiedName.from_string(bname)
nodeids = _instantiate_node(parent.server, Node(parent.server, rdesc.NodeId), parent.nodeid, rdesc, nodeid, bname, dname=dname, toplevel=True)
nodeids = await _instantiate_node(
parent.server,
Node(parent.server, rdesc.NodeId), parent.nodeid, rdesc, nodeid, bname, dname=dname, toplevel=True
)
return [Node(parent.server, nid) for nid in nodeids]
def _instantiate_node(server, node_type, parentid, rdesc, nodeid, bname, dname=None, recursive=True, toplevel=False):
async def _instantiate_node(server, node_type, parentid, rdesc, nodeid, bname, dname=None, recursive=True, toplevel=False):
"""
instantiate a node type under parent
"""
......@@ -48,38 +51,36 @@ def _instantiate_node(server, node_type, parentid, rdesc, nodeid, bname, dname=N
if rdesc.NodeClass in (ua.NodeClass.Object, ua.NodeClass.ObjectType):
addnode.NodeClass = ua.NodeClass.Object
_read_and_copy_attrs(node_type, ua.ObjectAttributes(), addnode)
await _read_and_copy_attrs(node_type, ua.ObjectAttributes(), addnode)
elif rdesc.NodeClass in (ua.NodeClass.Variable, ua.NodeClass.VariableType):
addnode.NodeClass = ua.NodeClass.Variable
_read_and_copy_attrs(node_type, ua.VariableAttributes(), addnode)
await _read_and_copy_attrs(node_type, ua.VariableAttributes(), addnode)
elif rdesc.NodeClass in (ua.NodeClass.Method,):
addnode.NodeClass = ua.NodeClass.Method
_read_and_copy_attrs(node_type, ua.MethodAttributes(), addnode)
await _read_and_copy_attrs(node_type, ua.MethodAttributes(), addnode)
elif rdesc.NodeClass in (ua.NodeClass.DataType,):
addnode.NodeClass = ua.NodeClass.DataType
_read_and_copy_attrs(node_type, ua.DataTypeAttributes(), addnode)
await _read_and_copy_attrs(node_type, ua.DataTypeAttributes(), addnode)
else:
logger.error("Instantiate: Node class not supported: %s", rdesc.NodeClass)
raise RuntimeError("Instantiate: Node class not supported")
return
if dname is not None:
addnode.NodeAttributes.DisplayName = dname
res = server.add_nodes([addnode])[0]
res = (await server.add_nodes([addnode]))[0]
added_nodes = [res.AddedNodeId]
if recursive:
parents = ua_utils.get_node_supertypes(node_type, includeitself=True)
parents = await ua_utils.get_node_supertypes(node_type, includeitself=True)
node = Node(server, res.AddedNodeId)
for parent in parents:
descs = parent.get_children_descriptions(includesubtypes=False)
descs = await parent.get_children_descriptions(includesubtypes=False)
for c_rdesc in descs:
# skip items that already exists, prefer the 'lowest' one in object hierarchy
if not ua_utils.is_child_present(node, c_rdesc.BrowseName):
if not await ua_utils.is_child_present(node, c_rdesc.BrowseName):
c_node_type = Node(server, c_rdesc.NodeId)
refs = c_node_type.get_referenced_nodes(refs=ua.ObjectIds.HasModellingRule)
refs = await c_node_type.get_referenced_nodes(refs=ua.ObjectIds.HasModellingRule)
# exclude nodes without ModellingRule at top-level
if toplevel and len(refs) == 0:
continue
......@@ -87,13 +88,18 @@ def _instantiate_node(server, node_type, parentid, rdesc, nodeid, bname, dname=N
if len(refs) == 1 and refs[0].nodeid == ua.NodeId(ua.ObjectIds.ModellingRule_Optional):
logger.info("Will not instantiate optional node %s as part of %s", c_rdesc.BrowseName, addnode.BrowseName)
continue
# if root node being instantiated has a String NodeId, create the children with a String NodeId
if res.AddedNodeId.NodeIdType is ua.NodeIdType.String:
inst_nodeid = res.AddedNodeId.Identifier + "." + c_rdesc.BrowseName.Name
nodeids = _instantiate_node(server, c_node_type, res.AddedNodeId, c_rdesc, nodeid=ua.NodeId(identifier=inst_nodeid, namespaceidx=res.AddedNodeId.NamespaceIndex), bname=c_rdesc.BrowseName)
nodeids = await _instantiate_node(
server, c_node_type, res.AddedNodeId, c_rdesc,
nodeid=ua.NodeId(identifier=inst_nodeid, namespaceidx=res.AddedNodeId.NamespaceIndex),
bname=c_rdesc.BrowseName
)
else:
nodeids = _instantiate_node(server, c_node_type, res.AddedNodeId, c_rdesc, nodeid=ua.NodeId(namespaceidx=res.AddedNodeId.NamespaceIndex), bname=c_rdesc.BrowseName)
nodeids = await _instantiate_node(
server, c_node_type, res.AddedNodeId, c_rdesc,
nodeid=ua.NodeId(namespaceidx=res.AddedNodeId.NamespaceIndex), bname=c_rdesc.BrowseName
)
added_nodes.extend(nodeids)
return added_nodes
"""
High level functions to create nodes
"""
import logging
from opcua import ua
from opcua.common import node
from opcua.common.instantiate import instantiate
_logger = logging.getLogger(__name__)
__all__ = [
'create_folder', 'create_object', 'create_property', 'create_variable', 'create_variable_type',
'create_reference_type', 'create_object_type', 'create_method', 'create_data_type', 'delete_nodes'
]
def _parse_nodeid_qname(*args):
try:
......@@ -28,7 +35,10 @@ def _parse_nodeid_qname(*args):
except ua.UaError:
raise
except Exception as ex:
raise TypeError("This method takes either a namespace index and a string as argument or a nodeid and a qualifiedname. Received arguments {0} and got exception {1}".format(args, ex))
raise TypeError(
"This method takes either a namespace index and a string as argument or a nodeid and a qualifiedname. Received arguments {0} and got exception {1}".format(
args, ex)
)
async def create_folder(parent, nodeid, bname):
......@@ -55,8 +65,8 @@ async def create_object(parent, nodeid, bname, objecttype=None):
if objecttype is not None:
objecttype = node.Node(parent.server, objecttype)
dname = ua.LocalizedText(bname)
nodes = instantiate(parent, objecttype, nodeid, bname=qname, dname=dname)[0]
return nodes
nodes = await instantiate(parent, objecttype, nodeid, bname=qname, dname=dname)
return nodes[0]
else:
return node.Node(
parent.server,
......@@ -64,7 +74,7 @@ async def create_object(parent, nodeid, bname, objecttype=None):
)
def create_property(parent, nodeid, bname, val, varianttype=None, datatype=None):
async def create_property(parent, nodeid, bname, val, varianttype=None, datatype=None):
"""
create a child node property
args are nodeid, browsename, value, [variant type]
......@@ -76,7 +86,10 @@ def create_property(parent, nodeid, bname, val, varianttype=None, datatype=None)
datatype = ua.NodeId(datatype, 0)
if datatype and not isinstance(datatype, ua.NodeId):
raise RuntimeError("datatype argument must be a nodeid or an int refering to a nodeid")
return node.Node(parent.server, _create_variable(parent.server, parent.nodeid, nodeid, qname, var, datatype=datatype, isproperty=True))
return node.Node(
parent.server,
await _create_variable(parent.server, parent.nodeid, nodeid, qname, var, datatype=datatype, isproperty=True)
)
async def create_variable(parent, nodeid, bname, val, varianttype=None, datatype=None):
......@@ -108,21 +121,25 @@ async def create_variable_type(parent, nodeid, bname, datatype):
if datatype and isinstance(datatype, int):
datatype = ua.NodeId(datatype, 0)
if datatype and not isinstance(datatype, ua.NodeId):
raise RuntimeError("Data type argument must be a nodeid or an int refering to a nodeid, received: {}".format(datatype))
raise RuntimeError(
"Data type argument must be a nodeid or an int refering to a nodeid, received: {}".format(datatype))
return node.Node(
parent.server,
await _create_variable_type(parent.server, parent.nodeid, nodeid, qname, datatype)
)
def create_reference_type(parent, nodeid, bname, symmetric=True, inversename=None):
async def create_reference_type(parent, nodeid, bname, symmetric=True, inversename=None):
"""
Create a new reference type
args are nodeid and browsename
or idx and name
"""
nodeid, qname = _parse_nodeid_qname(nodeid, bname)
return node.Node(parent.server, _create_reference_type(parent.server, parent.nodeid, nodeid, qname, symmetric, inversename))
return node.Node(
parent.server,
await _create_reference_type(parent.server, parent.nodeid, nodeid, qname, symmetric, inversename)
)
async def create_object_type(parent, nodeid, bname):
......@@ -144,6 +161,7 @@ async def create_method(parent, *args):
if argument types is specified, child nodes advertising what arguments the method uses and returns will be created
a callback is a method accepting the nodeid of the parent as first argument and variants after. returns a list of variants
"""
_logger.info('create_method %r', parent)
nodeid, qname = _parse_nodeid_qname(*args[:2])
callback = args[2]
if len(args) > 3:
......@@ -183,7 +201,7 @@ async def _create_object(server, parentnodeid, nodeid, qname, objecttype):
return results[0].AddedNodeId
def _create_reference_type(server, parentnodeid, nodeid, qname, symmetric, inversename):
async def _create_reference_type(server, parentnodeid, nodeid, qname, symmetric, inversename):
addnode = ua.AddNodesItem()
addnode.RequestedNewNodeId = nodeid
addnode.BrowseName = qname
......@@ -199,7 +217,7 @@ def _create_reference_type(server, parentnodeid, nodeid, qname, symmetric, inver
attrs.UserWriteMask = 0
addnode.NodeAttributes = attrs
results = server.add_nodes([addnode])
results = await server.add_nodes([addnode])
results[0].StatusCode.check()
return results[0].AddedNodeId
......@@ -268,7 +286,7 @@ async def _create_variable_type(server, parentnodeid, nodeid, qname, datatype, v
addnode.NodeClass = ua.NodeClass.VariableType
addnode.ParentNodeId = parentnodeid
addnode.ReferenceTypeId = ua.NodeId(ua.ObjectIds.HasSubtype)
#addnode.TypeDefinition = ua.NodeId(ua.ObjectIds.BaseDataVariableType)
# addnode.TypeDefinition = ua.NodeId(ua.ObjectIds.BaseDataVariableType)
attrs = ua.VariableTypeAttributes()
attrs.Description = ua.LocalizedText(qname.Name)
attrs.DisplayName = ua.LocalizedText(qname.Name)
......@@ -280,7 +298,7 @@ async def _create_variable_type(server, parentnodeid, nodeid, qname, datatype, v
attrs.ValueRank = ua.ValueRank.OneDimension
else:
attrs.ValueRank = ua.ValueRank.Scalar
#attrs.ArrayDimensions = None
# attrs.ArrayDimensions = None
attrs.WriteMask = 0
attrs.UserWriteMask = 0
addnode.NodeAttributes = attrs
......@@ -303,7 +321,7 @@ async def create_data_type(parent, nodeid, bname, description=None):
addnode.NodeClass = ua.NodeClass.DataType
addnode.ParentNodeId = parent.nodeid
addnode.ReferenceTypeId = ua.NodeId(ua.ObjectIds.HasSubtype)
#addnode.TypeDefinition = ua.NodeId(ua.ObjectIds.BaseDataVariableType) # No type definition for types
# addnode.TypeDefinition = ua.NodeId(ua.ObjectIds.BaseDataVariableType) # No type definition for types
attrs = ua.DataTypeAttributes()
if description is None:
attrs.Description = ua.LocalizedText(qname.Name)
......@@ -326,7 +344,7 @@ async def _create_method(parent, nodeid, qname, callback, inputs, outputs):
addnode.NodeClass = ua.NodeClass.Method
addnode.ParentNodeId = parent.nodeid
addnode.ReferenceTypeId = ua.NodeId(ua.ObjectIds.HasComponent)
#node.TypeDefinition = ua.NodeId(ua.ObjectIds.BaseObjectType)
# node.TypeDefinition = ua.NodeId(ua.ObjectIds.BaseObjectType)
attrs = ua.MethodAttributes()
attrs.Description = ua.LocalizedText(qname.Name)
attrs.DisplayName = ua.LocalizedText(qname.Name)
......@@ -339,19 +357,23 @@ async def _create_method(parent, nodeid, qname, callback, inputs, outputs):
results[0].StatusCode.check()
method = node.Node(parent.server, results[0].AddedNodeId)
if inputs:
create_property(method,
ua.NodeId(namespaceidx=method.nodeid.NamespaceIndex),
ua.QualifiedName("InputArguments", 0),
[_vtype_to_argument(vtype) for vtype in inputs],
varianttype=ua.VariantType.ExtensionObject,
datatype=ua.ObjectIds.Argument)
await create_property(
method,
ua.NodeId(namespaceidx=method.nodeid.NamespaceIndex),
ua.QualifiedName("InputArguments", 0),
[_vtype_to_argument(vtype) for vtype in inputs],
varianttype=ua.VariantType.ExtensionObject,
datatype=ua.ObjectIds.Argument
)
if outputs:
create_property(method,
ua.NodeId(namespaceidx=method.nodeid.NamespaceIndex),
ua.QualifiedName("OutputArguments", 0),
[_vtype_to_argument(vtype) for vtype in outputs],
varianttype=ua.VariantType.ExtensionObject,
datatype=ua.ObjectIds.Argument)
await create_property(
method,
ua.NodeId(namespaceidx=method.nodeid.NamespaceIndex),
ua.QualifiedName("OutputArguments", 0),
[_vtype_to_argument(vtype) for vtype in outputs],
varianttype=ua.VariantType.ExtensionObject,
datatype=ua.ObjectIds.Argument
)
if hasattr(parent.server, "add_method_callback"):
await parent.server.add_method_callback(method.nodeid, callback)
return results[0].AddedNodeId
......@@ -407,5 +429,3 @@ def _add_childs(nodes):
for mynode in nodes[:]:
results += mynode.get_children()
return results
......@@ -573,7 +573,7 @@ class Node:
results = await opcua.common.manage_nodes.delete_nodes(self.server, [self], recursive, delete_references)
_check_results(results)
def _fill_delete_reference_item(self, rdesc, bidirectional = False):
def _fill_delete_reference_item(self, rdesc, bidirectional=False):
ditem = ua.DeleteReferencesItem()
ditem.SourceNodeId = self.nodeid
ditem.TargetNodeId = rdesc.NodeId
......@@ -619,7 +619,7 @@ class Node:
async def _add_modelling_rule(self, parent, mandatory=True):
if mandatory is not None and await parent.get_node_class() == ua.NodeClass.ObjectType:
rule=ua.ObjectIds.ModellingRule_Mandatory if mandatory else ua.ObjectIds.ModellingRule_Optional
rule = ua.ObjectIds.ModellingRule_Mandatory if mandatory else ua.ObjectIds.ModellingRule_Optional
await self.add_reference(rule, ua.ObjectIds.HasModellingRule, True, False)
return self
......@@ -630,9 +630,9 @@ class Node:
if await parent.get_node_class() != ua.NodeClass.ObjectType:
return ua.StatusCode(ua.StatusCodes.BadTypeMismatch)
# remove all existing modelling rule
rules = self.get_references(ua.ObjectIds.HasModellingRule)
rules = await self.get_references(ua.ObjectIds.HasModellingRule)
await self.server.delete_references(list(map(self._fill_delete_reference_item, rules)))
self._add_modelling_rule(parent, mandatory)
await self._add_modelling_rule(parent, mandatory)
return ua.StatusCode()
async def add_folder(self, nodeid, bname):
......
import asyncio
class UASocketProtocol(asyncio.Protocol):
"""
Handle socket connection and send ua messages.
Timeout is the timeout used while waiting for an ua answer from server.
"""
def __init__(self, timeout=1, security_policy=ua.SecurityPolicy()):
self.logger = logging.getLogger(__name__ + ".UASocketProtocol")
self.loop = asyncio.get_event_loop()
self.transport = None
self.receive_buffer = asyncio.Queue()
self.is_receiving = False
self.timeout = timeout
self.authentication_token = ua.NodeId()
self._request_id = 0
self._request_handle = 0
self._callbackmap = {}
self._connection = SecureConnection(security_policy)
self._leftover_chunk = None
def connection_made(self, transport: asyncio.Transport):
self.transport = transport
def connection_lost(self, exc):
self.logger.info("Socket has closed connection")
self.transport = None
def data_received(self, data: bytes):
self.receive_buffer.put_nowait(data)
if not self.is_receiving:
self.is_receiving = True
self.loop.create_task(self._receive())
async def read(self, size: int):
"""Receive up to size bytes from socket."""
data = b''
self.logger.debug('read %s bytes from socket', size)
while size > 0:
self.logger.debug('data is now %s, waiting for %s bytes', len(data), size)
# ToDo: abort on timeout, socket close
# raise SocketClosedException("Server socket has closed")
if self._leftover_chunk:
self.logger.debug('leftover bytes %s', len(self._leftover_chunk))
# use leftover chunk first
chunk = self._leftover_chunk
self._leftover_chunk = None
else:
chunk = await self.receive_buffer.get()
self.logger.debug('got chunk %s needed_length is %s', len(chunk), size)
if len(chunk) <= size:
_chunk = chunk
else:
# chunk is too big
_chunk = chunk[:size]
self._leftover_chunk = chunk[size:]
data += _chunk
size -= len(_chunk)
return data
......@@ -192,7 +192,6 @@ async def is_child_present(node, browsename):
for child_desc in child_descs:
if child_desc.BrowseName == browsename:
return True
return False
......
......@@ -19,6 +19,7 @@ class OPCUAProtocol(asyncio.Protocol):
FIXME: find another solution
"""
def __init__(self, iserver=None, policies=None, clients=None):
self.loop = asyncio.get_event_loop()
self.peer_name = None
self.transport = None
self.processor = None
......@@ -54,15 +55,15 @@ class OPCUAProtocol(asyncio.Protocol):
if self.data:
data = self.data + data
self.data = b''
self._process_data(data)
self.loop.create_task(self._process_data(data))
def _process_data(self, data):
async def _process_data(self, data):
buf = ua.utils.Buffer(data)
while True:
try:
backup_buf = buf.copy()
try:
hdr = uabin.header_from_binary(buf)
hdr = await uabin.header_from_binary(buf)
except ua.utils.NotEnoughData:
logger.info('We did not receive enough data from client, waiting for more')
self.data = backup_buf.read(len(backup_buf))
......
......@@ -281,13 +281,12 @@ class Server:
self.iserver.stop()
raise exp
def stop(self):
async def stop(self):
"""
Stop server
"""
for client in self._discovery_clients.values():
client.disconnect()
self.bserver.stop()
await asyncio.wait([client.disconnect() for client in self._discovery_clients.values()])
await self.bserver.stop()
self.iserver.stop()
def get_root_node(self):
......
......@@ -505,8 +505,7 @@ def struct_from_binary(objtype, data):
def header_to_binary(hdr):
b = []
b.append(struct.pack("<3ss", hdr.MessageType, hdr.ChunkType))
b = [struct.pack("<3ss", hdr.MessageType, hdr.ChunkType)]
size = hdr.body_size + 8
if hdr.MessageType in (ua.MessageType.SecureOpen, ua.MessageType.SecureClose, ua.MessageType.SecureMessage):
size += 4
......
[pytest]
log_cli=False
log_print=True
log_level=INFO
......@@ -34,6 +34,7 @@ async def client():
async def server():
# start our own server
srv = Server()
await srv.init()
srv.set_endpoint(f'opc.tcp://127.0.0.1:{port_num1}')
await add_server_methods(srv)
await srv.start()
......
......@@ -60,9 +60,9 @@ async def add_server_methods(srv):
base_otype = srv.get_node(ua.ObjectIds.BaseObjectType)
custom_otype = await base_otype.add_object_type(2, 'ObjectWithMethodsType')
await custom_otype.add_method(2, 'ServerMethodDefault', func4)
await custom_otype.add_method(2, 'ServerMethodMandatory', func4).set_modelling_rule(True)
await custom_otype.add_method(2, 'ServerMethodOptional', func4).set_modelling_rule(False)
await custom_otype.add_method(2, 'ServerMethodNone', func4).set_modelling_rule(None)
await (await custom_otype.add_method(2, 'ServerMethodMandatory', func4)).set_modelling_rule(True)
await (await custom_otype.add_method(2, 'ServerMethodOptional', func4)).set_modelling_rule(False)
await (await custom_otype.add_method(2, 'ServerMethodNone', func4)).set_modelling_rule(None)
await o.add_object(2, 'ObjectWithMethods', custom_otype)
@uamethod
......
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