Commit 37f69181 authored by Olivier R-D's avatar Olivier R-D

autopep8

parent e6300bec
......@@ -3,10 +3,13 @@ import logging
from opcua import Client
from opcua import uaprotocol as ua
class SubHandler(object):
"""
Client to subscription. It will receive events from server
"""
def data_change(self, handle, node, val, attr):
print("Python: New data change event", handle, node, val, attr)
......@@ -14,8 +17,7 @@ class SubHandler(object):
print("Python: New event", handle, event)
if __name__ == "__main__":
if __name__ == "__main__":
from IPython import embed
logging.basicConfig(level=logging.WARN)
client = Client("opc.tcp://localhost:53530/OPCUA/SimulationServer/")
......@@ -33,7 +35,7 @@ if __name__ == "__main__":
myuint64 = client.get_node("ns=4;s=UInt64")
myint32 = client.get_node("ns=4;s=Int32")
myuint32 = client.get_node("ns=4;s=UInt32")
var = client.get_node(ua.NodeId("Random1", 5))
print("var is: ", var)
print("value of var is: ", var.get_value())
......@@ -42,7 +44,6 @@ if __name__ == "__main__":
myfloat.set_value(ua.Variant(1.234, ua.VariantType.Float))
print("reading float value: ", myfloat.get_value())
handler = SubHandler()
sub = client.create_subscription(500, handler)
sub.subscribe_data_change(var)
......@@ -52,7 +53,6 @@ if __name__ == "__main__":
result = device.call_method(method, ua.Variant("sin"), ua.Variant(180, ua.VariantType.Double))
print("Mehtod result is: ", result)
embed()
client.close_session()
finally:
......
......@@ -5,6 +5,7 @@ try:
from IPython import embed
except ImportError:
import code
def embed():
vars = globals()
vars.update(locals())
......@@ -15,10 +16,13 @@ except ImportError:
from opcua import Client
from opcua import uaprotocol as ua
class SubHandler(object):
"""
Client to subscription. It will receive events from server
"""
def data_change(self, handle, node, val, attr):
print("Python: New data change event", handle, node, val, attr)
......@@ -26,11 +30,10 @@ class SubHandler(object):
print("Python: New event", handle, event)
if __name__ == "__main__":
if __name__ == "__main__":
logging.basicConfig(level=logging.WARN)
#logger = logging.getLogger("KeepAlive")
#logger.setLevel(logging.DEBUG)
# logger.setLevel(logging.DEBUG)
client = Client("opc.tcp://localhost:4841/freeopcua/server/")
try:
client.connect()
......@@ -39,8 +42,8 @@ if __name__ == "__main__":
print(root.get_children())
print(root.get_browse_name())
#var = client.get_node(ua.NodeId(1002, 2))
#print(var)
#print(var.get_value())
# print(var)
# print(var.get_value())
#var.set_value(ua.Variant([23], ua.VariantType.Int64))
state = root.get_child(["0:Objects", "0:Server"])
print(state)
......@@ -52,10 +55,10 @@ if __name__ == "__main__":
handle = sub.subscribe_data_change(myvar)
time.sleep(0.1)
sub.subscribe_events()
#sub.unsubscribe(handle)
#sub.delete()
#calling a method on server
# sub.unsubscribe(handle)
# sub.delete()
# calling a method on server
res = obj.call_method("2:multiply", 3, "klk")
print("method result is: ", res)
......
......@@ -5,6 +5,7 @@ try:
from IPython import embed
except ImportError:
import code
def embed():
vars = globals()
vars.update(locals())
......@@ -16,38 +17,43 @@ from opcua import ua, uamethod, Server, Event, ObjectIds
class SubHandler(object):
"""
Client to subscription. It will receive events from server
"""
def data_change(self, handle, node, val, attr):
print("Python: New data change event", handle, node, val, attr)
def event(self, handle, event):
print("Python: New event", handle, event)
#method to be exposed through server
# method to be exposed through server
def func(parent, variant):
return [variant.Value * 2]
#method to be exposed through server
# method to be exposed through server
# uses a decorator to automatically convert to and from variants
@uamethod
def multiply(parent, x, y):
print("multiply method call with parameters: ", x, y)
return x*y
return x * y
if __name__ == "__main__":
#optional setup logging
# optional setup logging
logging.basicConfig(level=logging.WARN)
#logger = logging.getLogger("opcua.address_space")
#logger = logging.getLogger("opcua.internal_server")
#logger.setLevel(logging.DEBUG)
# logger.setLevel(logging.DEBUG)
#logger = logging.getLogger("opcua.subscription_server")
#logger.setLevel(logging.DEBUG)
# logger.setLevel(logging.DEBUG)
# now setup our server
# now setup our server
server = Server()
server.set_endpoint("opc.tcp://localhost:4841/freeopcua/server/")
server.set_server_name("FreeOpcUa Example Server")
......@@ -73,13 +79,13 @@ if __name__ == "__main__":
myevent = server.get_event_object(ObjectIds.BaseEventType)
myevent.Message.Text = "This is my event"
myevent.Severity = 300
# starting!
server.start()
print("Available loggers are: ", logging.Logger.manager.loggerDict.keys())
try:
handler = SubHandler()
#enable following if you want to subscribe to nodes on server side
# enable following if you want to subscribe to nodes on server side
sub = server.create_subscription(500, handler)
handle = sub.subscribe_data_change(myvar)
# trigger event, all subscribed clients wil receive it
......@@ -88,4 +94,3 @@ if __name__ == "__main__":
embed()
finally:
server.stop()
......@@ -15,7 +15,7 @@ from opcua.server import Server
def uamethod(func):
"""
Method decorator to automatically convert
Method decorator to automatically convert
arguments and output to and from variants
"""
def wrapper(parent, *args):
......@@ -23,10 +23,9 @@ def uamethod(func):
return to_variant(result)
return wrapper
def to_variant(*args):
uaargs = []
for arg in args:
uaargs.append(ua.Variant(arg))
return uaargs
......@@ -4,17 +4,21 @@ import pickle
from opcua import ua
class AttributeValue(object):
def __init__(self, value):
self.value = value
self.value_callback = None
self.datachange_callbacks = {}
self.value_callback = None
self.datachange_callbacks = {}
def __str__(self):
return "AttributeValue({})".format(self.value)
__repr__ = __str__
class NodeData(object):
def __init__(self, nodeid):
self.nodeid = nodeid
self.attributes = {}
......@@ -27,6 +31,7 @@ class NodeData(object):
class AttributeService(object):
def __init__(self, aspace):
self.logger = logging.getLogger(__name__)
self._aspace = aspace
......@@ -45,7 +50,9 @@ class AttributeService(object):
res.append(self._aspace.set_attribute_value(writevalue.NodeId, writevalue.AttributeId, writevalue.Value))
return res
class ViewService(object):
def __init__(self, aspace):
self.logger = logging.getLogger(__name__)
self._aspace = aspace
......@@ -132,7 +139,7 @@ class ViewService(object):
current = nodeid
target = ua.BrowsePathTarget()
target.TargetId = current
target.RemainingPathIndex = 4294967295
target.RemainingPathIndex = 4294967295
res.Targets = [target]
return res
......@@ -140,7 +147,7 @@ class ViewService(object):
with self._aspace.lock:
nodedata = self._aspace.nodes[nodeid]
for ref in nodedata.references:
#FIXME: here we should check other arguments!!
# FIXME: here we should check other arguments!!
if ref.BrowseName == el.TargetName:
return ref.NodeId
self.logger.info("element %s was not found in node %s", el, nodeid)
......@@ -148,6 +155,7 @@ class ViewService(object):
class NodeManagementService(object):
def __init__(self, aspace):
self.logger = logging.getLogger(__name__)
self._aspace = aspace
......@@ -167,19 +175,19 @@ class NodeManagementService(object):
result.StatusCode = ua.StatusCode(ua.StatusCodes.BadNodeIdExists)
return result
nodedata = NodeData(item.RequestedNewNodeId)
#add common attrs
# add common attrs
nodedata.attributes[ua.AttributeIds.NodeId] = AttributeValue(ua.DataValue(ua.Variant(item.RequestedNewNodeId, ua.VariantType.NodeId)))
nodedata.attributes[ua.AttributeIds.BrowseName] = AttributeValue(ua.DataValue(ua.Variant(item.BrowseName, ua.VariantType.QualifiedName)))
nodedata.attributes[ua.AttributeIds.NodeClass] = AttributeValue(ua.DataValue(ua.Variant(item.NodeClass, ua.VariantType.Int32)))
#add requested attrs
# add requested attrs
self._add_nodeattributes(item.NodeAttributes, nodedata)
#add parent
# add parent
if item.ParentNodeId == ua.NodeId():
#self.logger.warning("add_node: creating node %s without parent", item.RequestedNewNodeId)
#self.logger.warning("add_node: creating node %s without parent", item.RequestedNewNodeId)
pass
elif not item.ParentNodeId in self._aspace.nodes:
#self.logger.warning("add_node: while adding node %s, requested parent node %s does not exists", item.RequestedNewNodeId, item.ParentNodeId)
#self.logger.warning("add_node: while adding node %s, requested parent node %s does not exists", item.RequestedNewNodeId, item.ParentNodeId)
result.StatusCode = ua.StatusCode(ua.StatusCodes.BadParentNodeIdInvalid)
return result
else:
......@@ -192,11 +200,11 @@ class NodeManagementService(object):
desc.TypeDefinition = item.TypeDefinition
desc.IsForward = True
self._aspace.nodes[item.ParentNodeId].references.append(desc)
#now add our node to db
# now add our node to db
self._aspace.nodes[item.RequestedNewNodeId] = nodedata
#add type definition
# add type definition
if item.TypeDefinition != ua.NodeId():
addref = ua.AddReferencesItem()
addref.SourceNodeId = item.RequestedNewNodeId
......@@ -230,10 +238,10 @@ class NodeManagementService(object):
rdesc.NodeClass = addref.TargetNodeClass
bname = self._aspace.get_attribute_value(addref.TargetNodeId, ua.AttributeIds.BrowseName).Value.Value
if bname:
rdesc.BrowseName = bname
rdesc.BrowseName = bname
dname = self._aspace.get_attribute_value(addref.TargetNodeId, ua.AttributeIds.DisplayName).Value.Value
if dname:
rdesc.DisplayName = dname
rdesc.DisplayName = dname
self._aspace.nodes[addref.SourceNodeId].references.append(rdesc)
return ua.StatusCode()
......@@ -241,7 +249,7 @@ class NodeManagementService(object):
if item.SpecifiedAttributes & getattr(ua.NodeAttributesMask, name):
dv = ua.DataValue(ua.Variant(getattr(item, name), vtype))
nodedata.attributes[getattr(ua.AttributeIds, name)] = AttributeValue(dv)
def _add_nodeattributes(self, item, nodedata):
item = ua.downcast_extobject(item)
self._add_node_attr(item, nodedata, "AccessLevel", ua.VariantType.Byte)
......@@ -270,6 +278,7 @@ class NodeManagementService(object):
class MethodService(object):
def __init__(self, aspace):
self.logger = logging.getLogger(__name__)
self._aspace = aspace
......@@ -281,9 +290,9 @@ class MethodService(object):
results.append(self._call(method))
return results
def _call(self, method):
def _call(self, method):
res = ua.CallMethodResult()
if method.ObjectId not in self._aspace.nodes or method.MethodId not in self._aspace.nodes:
if method.ObjectId not in self._aspace.nodes or method.MethodId not in self._aspace.nodes:
res.StatusCode = ua.StatusCode(ua.StatusCodes.BadNodeIdInvalid)
else:
node = self._aspace.nodes[method.MethodId]
......@@ -298,19 +307,21 @@ class MethodService(object):
self.logger.exception("Error executing method call %s, an exception was raised: ", method)
res.StatusCode = ua.StatusCode(ua.StatusCodes.BadUnexpectedError)
return res
class AddressSpace(object):
"""
The address space object stores all the nodes og the OPC-UA server
and helper methods.
It is NOT threadsafe, lock the lock object before reading of modifying
a node
"""
def __init__(self):
self.logger = logging.getLogger(__name__)
self.nodes = {}
self.lock = RLock() #FIXME: should use multiple reader, one writter pattern
self.lock = RLock() # FIXME: should use multiple reader, one writter pattern
self._datachange_callback_counter = 200
self._handle_to_attribute_map = {}
......@@ -386,13 +397,3 @@ class AddressSpace(object):
with self.lock:
node = self.nodes[methodid]
node.call = callback
......@@ -12,6 +12,7 @@ import opcua.utils as utils
class BinaryClient(object):
"""
low level OPC-UA client.
implement all(well..one day) methods defined in opcua spec
......@@ -19,11 +20,12 @@ class BinaryClient(object):
in python most of the structures are defined in
uaprotocol_auto.py and uaprotocol_hand.py
"""
def __init__(self):
self.logger = logging.getLogger(__name__)
self._socket = None
self._do_stop = False
self._security_token = ua.ChannelSecurityToken()
self._security_token = ua.ChannelSecurityToken()
self._authentication_token = ua.NodeId()
self._sequence_number = 0
self._request_id = 0
......@@ -43,9 +45,9 @@ class BinaryClient(object):
self._thread.start()
def _send_request(self, request, callback=None, timeout=1000):
#HACK to make sure we can convert our request to binary before increasing request counter etc ...
# HACK to make sure we can convert our request to binary before increasing request counter etc ...
request.to_binary()
#END HACK
# END HACK
with self._lock:
request.RequestHeader = self._create_request_header(timeout)
hdr = ua.Header(ua.MessageType.SecureMessage, ua.ChunkType.Single, self._security_token.ChannelId)
......@@ -62,7 +64,7 @@ class BinaryClient(object):
return data
def _check_answer(self, data, context):
data = data.copy(50)#FIXME check max length nodeid + responseheader
data = data.copy(50) # FIXME check max length nodeid + responseheader
typeid = ua.NodeId.from_binary(data)
if typeid == ua.FourByteNodeId(ua.ObjectIds.ServiceFault_Encoding_DefaultBinary):
self.logger.warning("ServiceFault from server received %s", context)
......@@ -159,14 +161,13 @@ class BinaryClient(object):
hdr.RequestId = self._request_id
return hdr
def connect_socket(self, host, port):
"""
connect to server socket and start receiving thread
"""
self.logger.info("opening connection")
self._socket = socket.create_connection((host, port))
self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)#nodelay ncessary to avoid packing in one frame, some servers do not like it
self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) # nodelay ncessary to avoid packing in one frame, some servers do not like it
self.start()
def disconnect_socket(self):
......@@ -183,7 +184,7 @@ class BinaryClient(object):
self._callbackmap[0] = future
self._write_socket(header, hello)
return ua.Acknowledge.from_binary(future.result())
def open_secure_channel(self, params):
self.logger.info("open_secure_channel")
request = ua.OpenSecureChannelRequest()
......@@ -229,7 +230,7 @@ class BinaryClient(object):
request.DeleteSubscriptions = deletesubscriptions
data = self._send_request(request)
ua.CloseSessionResponse.from_binary(data)
#response.ResponseHeader.ServiceResult.check() #disabled, it seems we sent wrong session Id, but where is the sessionId supposed to be sent???
# response.ResponseHeader.ServiceResult.check() #disabled, it seems we sent wrong session Id, but where is the sessionId supposed to be sent???
def browse(self, parameters):
self.logger.info("browse")
......@@ -281,7 +282,7 @@ class BinaryClient(object):
seqhdr = self._create_sequence_header()
self._write_socket(hdr, symhdr, seqhdr, request)
#some servers send a response here, most do not ... so we ignore
# some servers send a response here, most do not ... so we ignore
def translate_browsepaths_to_nodeids(self, browsepaths):
self.logger.info("translate_browsepath_to_nodeid")
......@@ -315,7 +316,7 @@ class BinaryClient(object):
def publish(self, acks=None):
self.logger.info("publish")
if acks is None:
if acks is None:
acks = []
request = ua.PublishRequest()
request.Parameters.SubscriptionAcknowledgements = acks
......@@ -326,10 +327,9 @@ class BinaryClient(object):
response = ua.PublishResponse.from_binary(future.result())
try:
self._publishcallbacks[response.Parameters.SubscriptionId](response.Parameters)
except Exception as ex: #we call client code, catch everything!
except Exception as ex: # we call client code, catch everything!
self.logger.exception("Exception while calling user callback")
def create_monitored_items(self, params):
self.logger.info("create_monitored_items")
request = ua.CreateMonitoredItemsRequest()
......@@ -356,14 +356,11 @@ class BinaryClient(object):
response = ua.AddNodesResponse.from_binary(data)
response.ResponseHeader.ServiceResult.check()
return response.Results
def call(self, methodstocall):
request = ua.CallRequest()
request.Parameters.MethodsToCall = methodstocall
request.Parameters.MethodsToCall = methodstocall
data = self._send_request(request)
response = ua.CallResponse.from_binary(data)
response.ResponseHeader.ServiceResult.check()
return response.Results
......@@ -14,10 +14,13 @@ from opcua.uaprocessor import UAProcessor
logger = logging.getLogger(__name__)
class BinaryServer(Thread):
"""
Socket server forwarding request to internal server
"""
def __init__(self, internal_server, hostname, port):
Thread.__init__(self)
self.socket_server = None
......@@ -33,10 +36,10 @@ class BinaryServer(Thread):
def run(self):
logger.warning("Listening on %s:%s", self.hostname, self.port)
socketserver.TCPServer.allow_reuse_address = True #get rid of address already in used warning
socketserver.TCPServer.allow_reuse_address = True # get rid of address already in used warning
self.socket_server = ThreadingTCPServer((self.hostname, self.port), UAHandler)
#self.socket_server.daemon_threads = True # this will force a shutdown of all threads, maybe too hard
self.socket_server.internal_server = self.iserver #allow handler to acces server properties
# self.socket_server.daemon_threads = True # this will force a shutdown of all threads, maybe too hard
self.socket_server.internal_server = self.iserver # allow handler to acces server properties
with self._cond:
self._cond.notify_all()
self.socket_server.serve_forever()
......@@ -47,6 +50,7 @@ class BinaryServer(Thread):
class UAHandler(socketserver.BaseRequestHandler):
"""
The RequestHandler class for our server.
......@@ -65,5 +69,3 @@ class UAHandler(socketserver.BaseRequestHandler):
class ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
from __future__ import division #support for python2
from __future__ import division # support for python2
from threading import Thread, Condition
import logging
try:
from urllib.parse import urlparse
except ImportError: #support for python2
except ImportError: # support for python2
from urlparse import urlparse
from opcua import uaprotocol as ua
......@@ -12,14 +12,16 @@ from opcua import utils
class KeepAlive(Thread):
"""
Used by Client to keep session opened.
OPCUA defines timeout both for sessions and secure channel
"""
def __init__(self, client, timeout):
Thread.__init__(self)
self.logger = logging.getLogger(__name__)
if timeout == 0: # means no timeout bu we do not trust such servers
if timeout == 0: # means no timeout bu we do not trust such servers
timeout = 360000
self.timeout = timeout
self.client = client
......@@ -31,7 +33,7 @@ class KeepAlive(Thread):
server_state = self.client.get_node(ua.FourByteNodeId(ua.ObjectIds.Server_ServerStatus_State))
while not self._dostop:
with self._cond:
self._cond.wait(self.timeout/1000)
self._cond.wait(self.timeout / 1000)
if self._dostop:
break
self.logger.debug("renewing channel")
......@@ -48,8 +50,9 @@ class KeepAlive(Thread):
class Client(object):
"""
High level client to connect to an OPC-UA server.
High level client to connect to an OPC-UA server.
This class makes it easy to connect and browse address space.
It attemps to expose as much functionality as possible
but if you want to do to special things you will probably need
......@@ -57,6 +60,7 @@ class Client(object):
which offers a raw OPC-UA interface.
"""
def __init__(self, url):
"""
used url argument to connect to server.
......@@ -65,8 +69,8 @@ class Client(object):
"""
self.logger = logging.getLogger(__name__)
self.server_url = urlparse(url)
self.name = "Pure Python Client"
self.description = self.name
self.name = "Pure Python Client"
self.description = self.name
self.application_uri = "urn:freeopcua:client"
self.product_uri = "urn:freeopcua.github.no:client"
self.security_policy_uri = "http://opcfoundation.org/UA/SecurityPolicy#None"
......@@ -124,7 +128,7 @@ class Client(object):
Send OPC-UA hello to server
"""
ack = self.bclient.send_hello(self.server_url.geturl())
#FIXME check ack
# FIXME check ack
def open_secure_channel(self, renew=False):
"""
......@@ -161,14 +165,14 @@ class Client(object):
params = ua.CreateSessionParameters()
params.ClientNonce = utils.create_nonce()
params.ClientCertificate = b''
params.ClientDescription = desc
params.ClientDescription = desc
params.EndpointUrl = self.server_url.geturl()
params.SessionName = self.description + " Session" + str(self._session_counter)
params.RequestedSessionTimeout = 3600000
params.MaxResponseMessageSize = 0 #means not max size
params.MaxResponseMessageSize = 0 # means not max size
response = self.bclient.create_session(params)
self.session_timeout = response.RevisedSessionTimeout
self.keepalive = KeepAlive(self, min(self.session_timeout, self.secure_channel_timeout) * 0.7) #0.7 is from spec
self.keepalive = KeepAlive(self, min(self.session_timeout, self.secure_channel_timeout) * 0.7) # 0.7 is from spec
self.keepalive.start()
return response
......@@ -224,8 +228,3 @@ class Client(object):
def get_namespace_index(self, uri):
uries = self.get_namespace_array()
return uries.index(uri)
......@@ -9,6 +9,7 @@ import uuid
class Event(object):
"""
Create an event based on an event type. Per default is BaseEventType used.
arguments are:
......@@ -16,6 +17,7 @@ class Event(object):
source: The emiting source for the node, either an objectId, NodeId or a Node
etype: The event type, either an objectId, a NodeId or a Node object
"""
def __init__(self, isession, etype=ObjectIds.BaseEventType, source=ObjectIds.Server):
self.isession = isession
......@@ -25,16 +27,16 @@ class Event(object):
self.node = Node(self.isession, etype)
else:
self.node = Node(self.isession, ua.NodeId(etype))
self.set_members_from_node(self.node)
if isinstance(source, Node):
self.SourceNode = source.NodeId
self.SourceNode = source.NodeId
elif isinstance(etype, ua.NodeId):
self.SourceNode = source.NodeId
self.SourceNode = source.NodeId
else:
self.SourceNode = ua.NodeId(source)
#set some default values for attributes from BaseEventType, thus that all event must have
# set some default values for attributes from BaseEventType, thus that all event must have
self.EventId = uuid.uuid4().bytes
self.EventType = self.node.nodeid
self.LocaleTime = datetime.now()
......@@ -44,7 +46,7 @@ class Event(object):
self.Severity = ua.Variant(1, ua.VariantType.UInt16)
self.SourceName = "Server"
#og set some node attributed we also are expected to have
# og set some node attributed we also are expected to have
self.BrowseName = self.node.get_browse_name()
self.DisplayName = self.node.get_display_name()
self.NodeId = self.node.nodeid
......@@ -63,5 +65,3 @@ class Event(object):
for desc in references:
node = Node(self.isession, desc.NodeId)
setattr(self, desc.BrowseName.Name, node.get_value())
......@@ -10,7 +10,7 @@ from threading import Thread
from enum import Enum
import functools
try:
#we prefer to use bundles asyncio version, otherwise fallback to trollius
# we prefer to use bundles asyncio version, otherwise fallback to trollius
import asyncio
except ImportError:
import trollius as asyncio
......@@ -30,6 +30,7 @@ from opcua import standard_address_space
class ThreadLoop(Thread):
def __init__(self):
Thread.__init__(self)
self.logger = logging.getLogger(__name__)
......@@ -69,7 +70,9 @@ class SessionState(Enum):
Activated = 1
Closed = 2
class InternalServer(object):
def __init__(self):
self.logger = logging.getLogger(__name__)
self.endpoints = []
......@@ -81,13 +84,13 @@ class InternalServer(object):
self.method_service = MethodService(self.aspace)
self.node_mgt_service = NodeManagementService(self.aspace)
standard_address_space.fill_address_space(self.node_mgt_service)
#standard_address_space.fill_address_space_from_disk(self.aspace)
# standard_address_space.fill_address_space_from_disk(self.aspace)
self.loop = ThreadLoop()
self.subcsription_service = SubscriptionService(self.loop, self.aspace)
# create a session to use on server side
self.isession = InternalSession(self, self.aspace, self.subcsription_service, "Internal")
self.isession = InternalSession(self, self.aspace, self.subcsription_service, "Internal")
self._timer = None
self.current_time_node = Node(self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_CurrentTime))
uries = ["http://opcfoundation.org/UA/"]
......@@ -100,7 +103,7 @@ class InternalServer(object):
def dump_address_space(self, path):
self.aspace.dump(path)
def start(self):
def start(self):
self.logger.info("starting internal server")
self.loop.start()
Node(self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_State)).set_value(0)
......@@ -124,15 +127,17 @@ class InternalServer(object):
def get_endpoints(self, params=None):
self.logger.info("get endpoint")
#FIXME check params
# FIXME check params
return self.endpoints[:]
def create_session(self, name):
return InternalSession(self, self.aspace, self.subcsription_service, name)
class InternalSession(object):
_counter = 10
_auth_counter = 1000
def __init__(self, internal_server, aspace, submgr, name):
self.logger = logging.getLogger(__name__)
self.iserver = internal_server
......@@ -144,14 +149,14 @@ class InternalSession(object):
InternalSession._counter += 1
self.authentication_token = ua.NodeId(self._auth_counter)
InternalSession._auth_counter += 1
self.nonce = utils.create_nonce()
self.nonce = utils.create_nonce()
self.subscriptions = []
self.logger.warning("Created internal session %s", self.name)
self._lock = Lock()
def __str__(self):
return "InternalSession(name:{}, id:{}, auth_token:{})".format(self.name, self.session_id, self.authentication_token)
def get_endpoints(self, params=None):
return self.iserver.get_endpoints(params)
......@@ -160,7 +165,7 @@ class InternalSession(object):
result = ua.CreateSessionResult()
result.SessionId = self.session_id
result.AuthenticationToken = self.authentication_token
result.AuthenticationToken = self.authentication_token
result.RevisedSessionTimeout = params.RequestedSessionTimeout
result.MaxRequestMessageSize = 65536
result.ServerNonce = self.nonce
......@@ -230,10 +235,8 @@ class InternalSession(object):
def delete_monitored_items(self, params):
return self.subscription_service.delete_monitored_items(params)
def publish(self, acks=None):
if acks is None:
acks = []
return self.subscription_service.publish(acks)
"""
High level node object, to access node attribute
High level node object, to access node attribute
and browse address space
"""
import opcua.uaprotocol as ua
class Node(object):
"""
High level node object, to access node attribute,
browse and populate address space
"""
def __init__(self, server, nodeid):
self.server = server
self.nodeid = None
......@@ -21,6 +24,7 @@ class Node(object):
self.nodeid = ua.NodeId(nodeid, 0)
else:
raise Exception("argument to node must be a NodeId object or a string defining a nodeid found {} of type {}".format(nodeid, type(nodeid)))
def __eq__(self, other):
if isinstance(other, Node) and self.nodeid == other.nodeid:
return True
......@@ -61,7 +65,7 @@ class Node(object):
def get_value(self):
"""
Get value of a node. Only variables(properties) have values.
Get value of a node. Only variables(properties) have values.
An exception will be generated for other node types.
"""
result = self.get_attribute(ua.AttributeIds.Value)
......@@ -69,11 +73,11 @@ class Node(object):
def set_value(self, value, varianttype=None):
"""
Set value of a node. Only variables(properties) have values.
Set value of a node. Only variables(properties) have values.
An exception will be generated for other node types.
"""
variant = None
if type(value) == ua.Variant:
if isinstance(value, ua.Variant):
variant = value
else:
variant = ua.Variant(value, varianttype)
......@@ -148,7 +152,7 @@ class Node(object):
desc.IncludeSubtypes = includesubtypes
desc.NodeClassMask = nodeclassmask
desc.ResultMask = ua.BrowseResultMask.All
desc.NodeId = self.nodeid
params = ua.BrowseParameters()
params.NodesToBrowse.append(desc)
......@@ -172,7 +176,7 @@ class Node(object):
el.ReferenceTypeId = ua.TwoByteNodeId(ua.ObjectIds.HierarchicalReferences)
el.IsInverse = False
el.IncludeSubtypes = True
if type(item) == ua.QualifiedName:
if isinstance(item, ua.QualifiedName):
el.TargetName = item
else:
el.TargetName = ua.QualifiedName.from_string(item)
......@@ -183,7 +187,7 @@ class Node(object):
result = self.server.translate_browsepaths_to_nodeids([bpath])
result = result[0]
result.StatusCode.check()
#FIXME: seems this method may return several nodes
# FIXME: seems this method may return several nodes
return Node(self.server, result.Targets[0].TargetId)
def add_folder(self, *args):
......@@ -194,11 +198,11 @@ class Node(object):
"""
nodeid, qname = self._parse_add_args(*args)
return self._add_folder(nodeid, qname)
def _add_folder(self, nodeid, qname):
node = ua.AddNodesItem()
node.RequestedNewNodeId = nodeid
node.BrowseName = qname
node.RequestedNewNodeId = nodeid
node.BrowseName = qname
node.NodeClass = ua.NodeClass.Object
node.ParentNodeId = self.nodeid
node.ReferenceTypeId = ua.NodeId.from_string("i=35")
......@@ -211,7 +215,7 @@ class Node(object):
results = self.server.add_nodes([node])
results[0].StatusCode.check()
return Node(self.server, nodeid)
def add_object(self, *args):
"""
create a child node object
......@@ -220,13 +224,13 @@ class Node(object):
"""
nodeid, qname = self._parse_add_args(*args)
return self._add_object(nodeid, qname)
def _add_object(self, nodeid, qname):
node = ua.AddNodesItem()
node.RequestedNewNodeId = nodeid
node.BrowseName = qname
node.RequestedNewNodeId = nodeid
node.BrowseName = qname
node.NodeClass = ua.NodeClass.Object
node.ParentNodeId = self.nodeid
node.ParentNodeId = self.nodeid
node.ReferenceTypeId = ua.NodeId.from_string("i=35")
node.TypeDefinition = ua.NodeId(ua.ObjectIds.BaseObjectType)
attrs = ua.ObjectAttributes()
......@@ -247,7 +251,7 @@ class Node(object):
nodeid, qname = self._parse_add_args(*args[:2])
val = self._to_variant(*args[2:])
return self._add_variable(nodeid, qname, val, isproperty=True)
def add_variable(self, *args):
"""
create a child node variable
......@@ -266,10 +270,10 @@ class Node(object):
def _add_variable(self, nodeid, qname, val, isproperty=False):
node = ua.AddNodesItem()
node.RequestedNewNodeId = nodeid
node.BrowseName = qname
node.RequestedNewNodeId = nodeid
node.BrowseName = qname
node.NodeClass = ua.NodeClass.Variable
node.ParentNodeId = self.nodeid
node.ParentNodeId = self.nodeid
if isproperty:
node.ReferenceTypeId = ua.NodeId(ua.ObjectIds.HasProperty)
node.TypeDefinition = ua.NodeId(ua.ObjectIds.PropertyType)
......@@ -281,7 +285,7 @@ class Node(object):
attrs.DisplayName = ua.LocalizedText(qname.Name)
attrs.DataType = self._guess_uatype(val)
attrs.Value = val
attrs.ValueRank = 0
attrs.ValueRank = 0
attrs.WriteMask = 0
attrs.UserWriteMask = 0
attrs.Historizing = 0
......@@ -302,17 +306,17 @@ class Node(object):
nodeid, qname = self._parse_add_args(*args[:2])
callback = args[2]
if len(args) > 3:
inputs = args[3]
inputs = args[3]
if len(args) > 4:
outputs = args[4]
outputs = args[4]
return self._add_method(nodeid, qname, callback, inputs, outputs)
def _add_method(self, nodeid, qname, callback, inputs, outputs):
node = ua.AddNodesItem()
node.RequestedNewNodeId = nodeid
node.BrowseName = qname
node.RequestedNewNodeId = nodeid
node.BrowseName = qname
node.NodeClass = ua.NodeClass.Method
node.ParentNodeId = self.nodeid
node.ParentNodeId = self.nodeid
node.ReferenceTypeId = ua.NodeId.from_string("i=47")
#node.TypeDefinition = ua.NodeId(ua.ObjectIds.BaseObjectType)
attrs = ua.MethodAttributes()
......@@ -332,19 +336,18 @@ class Node(object):
method.add_property(qname.NamespaceIndex, "OutputArguments", [self._vtype_to_argument(vtype) for vtype in outputs])
self.server.add_method_callback(method.nodeid, callback)
return method
def call_method(self, methodid, *args):
"""
Call an OPC-UA method. methodid is browse name of child method or the
nodeid of method as a NodeId object
Call an OPC-UA method. methodid is browse name of child method or the
nodeid of method as a NodeId object
arguments are variants or python object convertible to variants.
which may be of different types
returns a list of variants which are output of the method
returns a list of variants which are output of the method
"""
if type(methodid) is str:
if isinstance(methodid, str):
methodid = self.get_child(methodid).nodeid
elif type(methodid) is Node:
elif isinstance(methodid, Node):
methodid = methodid.nodeid
arguments = []
......@@ -391,21 +394,20 @@ class Node(object):
return ua.NodeId(getattr(ua.ObjectIds, variant.VariantType.name))
def _parse_add_args(self, *args):
if type(args[0]) is ua.NodeId:
if isinstance(args[0], ua.NodeId):
return args[0], args[1]
elif type(args[0]) is str:
elif isinstance(args[0], str):
return ua.NodeId.from_string(args[0]), ua.QualifiedName.from_string(args[1])
elif type(args[0]) is int:
elif isinstance(args[0], int):
return generate_nodeid(args[0]), ua.QualifiedName(args[1], args[0])
else:
raise TypeError("Add methods takes a nodeid and a qualifiedname as argument, received %s" % args)
__nodeid_counter = 2000
def generate_nodeid(idx):
global __nodeid_counter
__nodeid_counter += 1
return ua.NodeId(__nodeid_counter, idx)
......@@ -16,6 +16,7 @@ from opcua import Node, Subscription, ObjectIds, Event
class Server(object):
def __init__(self):
self.logger = logging.getLogger(__name__)
self.endpoint = "opc.tcp://localhost:4841/freeopcua/server/"
......@@ -26,7 +27,7 @@ class Server(object):
self.iserver = InternalServer()
self.bserver = None
#setup some expected values
# setup some expected values
self.register_namespace(self.server_uri)
sa_node = self.get_node(ua.NodeId(ua.ObjectIds.Server_ServerArray))
sa_node.set_value([self.server_uri])
......@@ -38,7 +39,7 @@ class Server(object):
return self.iserver.get_endpoints()
def _setup_server_nodes(self):
#to be called just before starting server since it needs all parameters to be setup
# to be called just before starting server since it needs all parameters to be setup
self._set_endpoints()
def _set_endpoints(self):
......@@ -48,9 +49,9 @@ class Server(object):
appdesc = ua.ApplicationDescription()
appdesc.ApplicationName = ua.LocalizedText(self.name)
appdesc.ApplicationUri = self.server_uri
appdesc.ApplicationUri = self.server_uri
appdesc.ApplicationType = ua.ApplicationType.Server
appdesc.ProductUri = self.product_uri
appdesc.ProductUri = self.product_uri
appdesc.DiscoveryUrls.append(self.endpoint.geturl())
edp = ua.EndpointDescription()
......@@ -116,7 +117,7 @@ class Server(object):
uries = ns_node.get_value()
uries.append(uri)
ns_node.set_value(uries)
return (len(uries)-1)
return (len(uries) - 1)
def get_namespace_index(self, uri):
uries = self.get_namespace_array()
......@@ -124,8 +125,3 @@ class Server(object):
def get_event_object(self, etype=ObjectIds.BaseEventType, source=ObjectIds.Server):
return Event(self.iserver.isession, etype, source)
......@@ -23,9 +23,8 @@ def fill_address_space(nodeservice):
create_standard_address_space_Part11(nodeservice)
create_standard_address_space_Part13(nodeservice)
def fill_address_space_from_disk(aspace):
dirname = os.path.dirname(opcua.__file__)
path = os.path.join(dirname, "binary_address_space.pickle")
aspace.load(path)
......@@ -12,14 +12,16 @@ from opcua import ObjectIds
from opcua import AttributeIds
from opcua import Event
class EventResult():
def __str__(self):
return "EventResult({})".format([str(k) + ":" + str(v) for k, v in self.__dict__.items()])
__repr__ = __str__
class SubscriptionItemData():
def __init__(self):
self.node = None
self.client_handle = None
......@@ -27,18 +29,20 @@ class SubscriptionItemData():
self.attribute = None
self.mfilter = None
class Subscription(object):
def __init__(self, server, params, handler):
self.logger = logging.getLogger(__name__)
self.server = server
self._client_handle = 200
self._handler = handler
self.parameters = params #move to data class
self.parameters = params # move to data class
self._monitoreditems_map = {}
self._lock = RLock()
self.subscription_id = None
response = self.server.create_subscription(params, self.publish_callback)
self.subscription_id = response.SubscriptionId #move to data class
self.subscription_id = response.SubscriptionId # move to data class
self.server.publish()
self.server.publish()
......@@ -63,7 +67,7 @@ class Subscription(object):
self._call_status(statuschange)
else:
self.logger.warn("Notification type not supported yet for notification %s", notif)
ack = ua.SubscriptionAcknowledgement()
ack.SubscriptionId = self.subscription_id
ack.SequenceNumber = publishresult.NotificationMessage.SequenceNumber
......@@ -133,7 +137,7 @@ class Subscription(object):
rv = ua.ReadValueId()
rv.NodeId = node.nodeid
rv.AttributeId = attr
#rv.IndexRange //We leave it null, then the entire array is returned
# rv.IndexRange //We leave it null, then the entire array is returned
mparams = ua.MonitoringParameters()
self._client_handle += 1
mparams.ClientHandle = self._client_handle
......@@ -143,7 +147,7 @@ class Subscription(object):
if mfilter:
mparams.Filter = mfilter
mir = ua.MonitoredItemCreateRequest()
mir = ua.MonitoredItemCreateRequest()
mir.ItemToMonitor = rv
mir.MonitoringMode = ua.MonitoringMode.Reporting
mir.RequestedParameters = mparams
......@@ -156,7 +160,7 @@ class Subscription(object):
results = self.server.create_monitored_items(params)
result = results[0]
result.StatusCode.check()
data = SubscriptionItemData()
data.client_handle = mparams.ClientHandle
data.node = node
......@@ -176,6 +180,3 @@ class Subscription(object):
params.MonitoredItemIds = [handle]
results = self.server.delete_monitored_items(params)
results[0].check()
......@@ -8,7 +8,9 @@ import logging
from opcua import ua
class SubscriptionService(object):
def __init__(self, loop, aspace):
self.logger = logging.getLogger(__name__)
self.loop = loop
......@@ -90,7 +92,7 @@ class SubscriptionService(object):
def republish(self, params):
with self._lock:
if not params.SubscriptionId in self.subscriptions:
#what should I do?
# what should I do?
return ua.NotificationMessage()
return self.subscriptions[params.SubscriptionId].republish(params.RetransmitSequenceNumber)
......@@ -100,17 +102,18 @@ class SubscriptionService(object):
sub.trigger_event(event)
class MonitoredItemData(object):
def __init__(self):
self.client_handle = None
self.callback_handle = None
self.monitored_item_id = None
self.parameters = None
self.mode = None
class InternalSubscription(object):
def __init__(self, manager, data, addressspace, callback):
self.logger = logging.getLogger(__name__)
self.aspace = addressspace
......@@ -128,18 +131,18 @@ class InternalSubscription(object):
self._triggered_statuschanges = []
self._notification_seq = 1
self._not_acknowledged_results = {}
self._startup = True
self._startup = True
self._keep_alive_count = 0
self._publish_cycles_count = 0
self._stopev = False
def __str__(self):
return "Subscription(id:{})".format(self.data.SubscriptionId)
def start(self):
self.logger.debug("starting subscription %s", self.data.SubscriptionId)
self._subscription_loop()
def stop(self):
self.logger.debug("stopping subscription %s", self.data.SubscriptionId)
self._stopev = True
......@@ -151,7 +154,7 @@ class InternalSubscription(object):
def _subscription_loop(self):
#self.logger.debug("%s loop", self)
if not self._stopev:
self.manager.loop.call_later(self.data.RevisedPublishingInterval/1000.0, self._sub_loop)
self.manager.loop.call_later(self.data.RevisedPublishingInterval / 1000.0, self._sub_loop)
def _sub_loop(self):
self.publish_results()
......@@ -167,14 +170,14 @@ class InternalSubscription(object):
self._keep_alive_count += 1
return False
def publish_results(self):
def publish_results(self):
if self._publish_cycles_count > self.data.RevisedLifetimeCount:
self.logger.warn("Subscription %s has expired, publish cycle count(%s) > lifetime count (%s)", self, self._publish_cycles_count, self.data.RevisedLifetimeCount)
#FIXME this will never be send since we do not have publish request anyway
self.trigger_statuschange(ua.StatusCode(ua.StatusCodes.BadTimeout))
# FIXME this will never be send since we do not have publish request anyway
self.trigger_statuschange(ua.StatusCode(ua.StatusCodes.BadTimeout))
self._stopev = True
with self._lock:
if self.has_published_results(): #FIXME: should we pop a publish request here? or we do not care?
if self.has_published_results(): # FIXME: should we pop a publish request here? or we do not care?
self._publish_cycles_count += 1
result = self._pop_publish_result()
self.callback(result)
......@@ -185,7 +188,7 @@ class InternalSubscription(object):
if self._triggered_datachanges:
notif = ua.DataChangeNotification()
notif.MonitoredItems = self._triggered_datachanges[:]
self._triggered_datachanges = []
self._triggered_datachanges = []
self.logger.debug("sending datachanges nontification with %s events", len(notif.MonitoredItems))
result.NotificationMessage.NotificationData.append(notif)
if self._triggered_events:
......@@ -205,7 +208,7 @@ class InternalSubscription(object):
result.AvailableSequenceNumbers = list(self._not_acknowledged_results.keys())
self._not_acknowledged_results[result.NotificationMessage.SequenceNumber] = result
return result
def trigger_statuschange(self, code):
self._triggered_statuschanges.append(code)
......@@ -248,11 +251,11 @@ class InternalSubscription(object):
result = ua.MonitoredItemCreateResult()
if mdata.monitored_item_id == params.MonitoredItemId:
result.RevisedSamplingInterval = self.data.RevisedPublishingInterval
result.RevisedQueueSize = ua.downcast_extobject(params.RequestedParameters.QueueSize) #FIXME check and use value
result.RevisedQueueSize = ua.downcast_extobject(params.RequestedParameters.QueueSize) # FIXME check and use value
result.FilterResult = params.RequestedParameters.Filter
mdata.parameters = result
return result
#FIXME modify event subscriptions
# FIXME modify event subscriptions
result = ua.MonitoredItemCreateResult()
result.StatusCode(ua.StatusCodes.BadMonitoredItemIdInvalid)
return result
......@@ -261,7 +264,7 @@ class InternalSubscription(object):
with self._lock:
result = ua.MonitoredItemCreateResult()
result.RevisedSamplingInterval = self.data.RevisedPublishingInterval
result.RevisedQueueSize = params.RequestedParameters.QueueSize #FIXME check and use value
result.RevisedQueueSize = params.RequestedParameters.QueueSize # FIXME check and use value
result.FilterResult = ua.downcast_extobject(params.RequestedParameters.Filter)
self._monitored_item_counter += 1
result.MonitoredItemId = self._monitored_item_counter
......@@ -271,7 +274,7 @@ class InternalSubscription(object):
mdata.parameters = result
mdata.mode = params.MonitoringMode
mdata.client_handle = params.RequestedParameters.ClientHandle
mdata.monitored_item_id = result.MonitoredItemId
mdata.monitored_item_id = result.MonitoredItemId
self._monitored_items[result.MonitoredItemId] = mdata
......@@ -284,7 +287,7 @@ class InternalSubscription(object):
self.logger.debug("adding callback return status %s and handle %s", result.StatusCode, handle)
mdata.callback_handle = handle
self._monitored_datachange[handle] = result.MonitoredItemId
#force data change event generation
# force data change event generation
self.trigger_datachange(handle, params.ItemToMonitor.NodeId, params.ItemToMonitor.AttributeId)
return result
......@@ -312,7 +315,6 @@ class InternalSubscription(object):
self._monitored_items.pop(mid)
return ua.StatusCode()
def datachange_callback(self, handle, value):
self.logger.info("subscription %s: datachange callback called with handle '%s' and value '%s'", self, handle, value.Value)
event = ua.MonitoredItemNotification()
......@@ -354,9 +356,3 @@ class InternalSubscription(object):
except AttributeError:
fields.append(ua.Variant())
return fields
......@@ -6,13 +6,17 @@ from datetime import datetime
from opcua import ua
from opcua import utils
class PublishRequestData(object):
def __init__(self):
self.requesthdr = None
self.algohdr = None
self.seqhdr = None
class UAProcessor(object):
def __init__(self, internal_server, socket, name):
self.logger = logging.getLogger(__name__)
self.iserver = internal_server
......@@ -26,7 +30,7 @@ class UAProcessor(object):
self._seq_number = 1
def loop(self):
#first we want a hello message
# first we want a hello message
while True:
header = ua.Header.from_stream(self.socket)
body = self._receive_body(header.body_size)
......@@ -59,8 +63,8 @@ class UAProcessor(object):
seqhdr.SequenceNumber = self._seq_number
self._seq_number += 1
hdr = ua.Header(msgtype, ua.ChunkType.Single, self.channel.SecurityToken.ChannelId)
if type(algohdr) is ua.SymmetricAlgorithmHeader:
algohdr.TokenId = self.channel.SecurityToken.TokenId
if isinstance(algohdr, ua.SymmetricAlgorithmHeader):
algohdr.TokenId = self.channel.SecurityToken.TokenId
self._write_socket(hdr, algohdr, seqhdr, response)
def _write_socket(self, hdr, *args):
......@@ -89,7 +93,7 @@ class UAProcessor(object):
request = ua.OpenSecureChannelRequest.from_binary(body)
channel = self._open_secure_channel(request.Parameters)
#send response
# send response
response = ua.OpenSecureChannelResponse()
response.Parameters = channel
self.send_response(request.RequestHeader.RequestHandle, algohdr, seqhdr, response, ua.MessageType.SecureOpen)
......@@ -123,16 +127,16 @@ class UAProcessor(object):
else:
self.logger.warning("Unsupported message type: %s", header.MessageType)
return True
def process_message(self, algohdr, seqhdr, body):
typeid = ua.NodeId.from_binary(body)
requesthdr = ua.RequestHeader.from_binary(body)
if typeid == ua.NodeId(ua.ObjectIds.CreateSessionRequest_Encoding_DefaultBinary):
self.logger.info("Create session request")
params = ua.CreateSessionParameters.from_binary(body)
self.session = self.iserver.create_session(self.name)#create the session on server
sessiondata = self.session.create_session(params)#get a session creation result to send back
self.session = self.iserver.create_session(self.name) # create the session on server
sessiondata = self.session.create_session(params) # get a session creation result to send back
response = ua.CreateSessionResponse()
response.Parameters = sessiondata
......@@ -141,7 +145,7 @@ class UAProcessor(object):
elif typeid == ua.NodeId(ua.ObjectIds.CloseSessionRequest_Encoding_DefaultBinary):
self.logger.info("Close session request")
deletesubs = ua.unpack_uatype('Boolean', body)
self.session.close_session(deletesubs)
response = ua.CloseSessionResponse()
......@@ -149,11 +153,11 @@ class UAProcessor(object):
elif typeid == ua.NodeId(ua.ObjectIds.ActivateSessionRequest_Encoding_DefaultBinary):
self.logger.info("Activate session request")
params = ua.ActivateSessionParameters.from_binary(body)
params = ua.ActivateSessionParameters.from_binary(body)
if not self.session:
#result = ua.ActivateSessionResult()
#result.Results.append(ua.StatusCode(ua.StatusCodes.BadSessionIdInvalid))
# result.Results.append(ua.StatusCode(ua.StatusCodes.BadSessionIdInvalid))
response = ua.ServiceFault()
response.ResponseHeader.ServiceResult = ua.StatusCode(ua.StatusCodes.BadSessionIdInvalid)
self.send_response(requesthdr.RequestHandle, algohdr, seqhdr, response)
......@@ -166,8 +170,8 @@ class UAProcessor(object):
elif typeid == ua.NodeId(ua.ObjectIds.ReadRequest_Encoding_DefaultBinary):
self.logger.info("Read request")
params = ua.ReadParameters.from_binary(body)
params = ua.ReadParameters.from_binary(body)
results = self.session.read(params)
response = ua.ReadResponse()
......@@ -176,8 +180,8 @@ class UAProcessor(object):
elif typeid == ua.NodeId(ua.ObjectIds.WriteRequest_Encoding_DefaultBinary):
self.logger.info("Write request")
params = ua.WriteParameters.from_binary(body)
params = ua.WriteParameters.from_binary(body)
results = self.session.write(params)
response = ua.WriteResponse()
......@@ -186,8 +190,8 @@ class UAProcessor(object):
elif typeid == ua.NodeId(ua.ObjectIds.BrowseRequest_Encoding_DefaultBinary):
self.logger.info("Browse request")
params = ua.BrowseParameters.from_binary(body)
params = ua.BrowseParameters.from_binary(body)
results = self.session.browse(params)
response = ua.BrowseResponse()
......@@ -196,8 +200,8 @@ class UAProcessor(object):
elif typeid == ua.NodeId(ua.ObjectIds.GetEndpointsRequest_Encoding_DefaultBinary):
self.logger.info("get endpoints request")
params = ua.GetEndpointsParameters.from_binary(body)
params = ua.GetEndpointsParameters.from_binary(body)
endpoints = self.iserver.get_endpoints(params)
response = ua.GetEndpointsResponse()
......@@ -207,8 +211,8 @@ class UAProcessor(object):
elif typeid == ua.NodeId(ua.ObjectIds.TranslateBrowsePathsToNodeIdsRequest_Encoding_DefaultBinary):
self.logger.info("translate browsepaths to nodeids request")
params = ua.TranslateBrowsePathsToNodeIdsParameters.from_binary(body)
params = ua.TranslateBrowsePathsToNodeIdsParameters.from_binary(body)
paths = self.session.translate_browsepaths_to_nodeids(params.BrowsePaths)
response = ua.TranslateBrowsePathsToNodeIdsResponse()
......@@ -218,8 +222,8 @@ class UAProcessor(object):
elif typeid == ua.NodeId(ua.ObjectIds.AddNodesRequest_Encoding_DefaultBinary):
self.logger.info("add nodes request")
params = ua.AddNodesParameters.from_binary(body)
params = ua.AddNodesParameters.from_binary(body)
results = self.session.add_nodes(params.NodesToAdd)
response = ua.AddNodesResponse()
......@@ -229,8 +233,8 @@ class UAProcessor(object):
elif typeid == ua.NodeId(ua.ObjectIds.CreateSubscriptionRequest_Encoding_DefaultBinary):
self.logger.info("create subscription request")
params = ua.CreateSubscriptionParameters.from_binary(body)
params = ua.CreateSubscriptionParameters.from_binary(body)
result = self.session.create_subscription(params, self.forward_publish_response)
response = ua.CreateSubscriptionResponse()
......@@ -240,8 +244,8 @@ class UAProcessor(object):
elif typeid == ua.NodeId(ua.ObjectIds.DeleteSubscriptionsRequest_Encoding_DefaultBinary):
self.logger.info("delete subscriptions request")
params = ua.DeleteSubscriptionsParameters.from_binary(body)
params = ua.DeleteSubscriptionsParameters.from_binary(body)
results = self.session.delete_subscriptions(params.SubscriptionIds)
response = ua.DeleteSubscriptionsResponse()
......@@ -261,7 +265,7 @@ class UAProcessor(object):
elif typeid == ua.NodeId(ua.ObjectIds.ModifyMonitoredItemsRequest_Encoding_DefaultBinary):
self.logger.info("modify monitored items request")
params = ua.ModifyMonitoredItemsParameters.from_binary(body)
params = ua.ModifyMonitoredItemsParameters.from_binary(body)
results = self.session.modify_monitored_items(params)
response = ua.ModifyMonitoredItemsResponse()
......@@ -271,8 +275,8 @@ class UAProcessor(object):
elif typeid == ua.NodeId(ua.ObjectIds.DeleteMonitoredItemsRequest_Encoding_DefaultBinary):
self.logger.info("delete monitored items request")
params = ua.DeleteMonitoredItemsParameters.from_binary(body)
params = ua.DeleteMonitoredItemsParameters.from_binary(body)
results = self.session.delete_monitored_items(params)
response = ua.DeleteMonitoredItemsResponse()
......@@ -285,21 +289,21 @@ class UAProcessor(object):
if not self.session:
return False
params = ua.PublishParameters.from_binary(body)
params = ua.PublishParameters.from_binary(body)
data = PublishRequestData()
data.requesthdr = requesthdr
data.seqhdr = seqhdr
data.algohdr = algohdr
with self._datalock:
self._publishdata_queue.append(data) # will be used to send publish answers from server
self._publishdata_queue.append(data) # will be used to send publish answers from server
self.session.publish(params.SubscriptionAcknowledgements)
elif typeid == ua.NodeId(ua.ObjectIds.RepublishRequest_Encoding_DefaultBinary):
self.logger.info("re-publish request")
params = ua.RepublishParameters.from_binary(body)
params = ua.RepublishParameters.from_binary(body)
msg = self.session.republish(params)
response = ua.RepublishResponse()
......@@ -313,12 +317,12 @@ class UAProcessor(object):
self.send_response(requesthdr.RequestHandle, algohdr, seqhdr, response)
self.channel = None
return False
elif typeid == ua.NodeId(ua.ObjectIds.CallRequest_Encoding_DefaultBinary):
self.logger.info("call request")
params = ua.CallParameters.from_binary(body)
params = ua.CallParameters.from_binary(body)
results = self.session.call(params.MethodsToCall)
response = ua.CallResponse()
......@@ -338,13 +342,11 @@ class UAProcessor(object):
self.logger.info("open secure channel")
if params.RequestType == ua.SecurityTokenRequestType.Issue:
self.channel = ua.OpenSecureChannelResult()
self.channel.SecurityToken.TokenId = 13 #random value
self.channel.SecurityToken.TokenId = 13 # random value
self.channel.SecurityToken.ChannelId = self.iserver.get_new_channel_id()
self.channel.SecurityToken.RevisedLifetime = params.RequestedLifetime
self.channel.SecurityToken.RevisedLifetime = params.RequestedLifetime
self.channel.SecurityToken.TokenId += 1
self.channel.SecurityToken.CreatedAt = datetime.now()
self.channel.SecurityToken.RevisedLifetime = params.RequestedLifetime
self.channel.ServerNonce = utils.create_nonce()
return self.channel
......@@ -5,7 +5,6 @@ from opcua.uaprotocol_auto import *
from opcua.uaprotocol_hand import *
# FIXME: this is really crappy, should thing about a better implementation
# maybe never inherit extensionobject and parse only body....
def downcast_extobject(item):
......@@ -15,5 +14,3 @@ def downcast_extobject(item):
classname = objectidname.split("_")[0]
cmd = "{}.from_binary(utils.Buffer(item.to_binary()))".format(classname)
return eval(cmd)
import io
import struct
import struct
import logging
import opcua.uaprotocol_auto as auto
import opcua.uatypes as uatypes
import opcua.uatypes as uatypes
import opcua.utils as utils
from opcua.object_ids import ObjectIds
from opcua.attribute_ids import AttributeIds
......@@ -14,14 +14,16 @@ logger = logging.getLogger('opcua.uaprotocol')
class SocketClosedException(Exception):
pass
def get_bytes_from_sock(sock, size):
data = utils.recv_all(sock, size)
if len(data) < size: #socket has closed!
if len(data) < size: # socket has closed!
raise SocketClosedException("Server socket has closed")
return io.BytesIO(data)
class Hello(uatypes.FrozenClass):
def __init__(self):
self.ProtocolVersion = 0
self.ReceiveBufferSize = 65536
......@@ -53,9 +55,8 @@ class Hello(uatypes.FrozenClass):
return hello
class MessageType(object):
Invalid = b"INV" #FIXME: check value
Invalid = b"INV" # FIXME: check value
Hello = b"HEL"
Acknowledge = b"ACK"
Error = b"ERR"
......@@ -63,15 +64,16 @@ class MessageType(object):
SecureClose = b"CLO"
SecureMessage = b"MSG"
class ChunkType(object):
Invalid = b"0" #FIXME check
Invalid = b"0" # FIXME check
Single = b"F"
Intermediate = b"C"
Final = b"A"
class Header(uatypes.FrozenClass):
def __init__(self, msgType=None, chunkType=None, channelid=0):
self.MessageType = msgType
self.ChunkType = chunkType
......@@ -113,6 +115,7 @@ class Header(uatypes.FrozenClass):
class ErrorMessage(uatypes.FrozenClass):
def __init__(self):
self.Error = uatypes.StatusCode()
self.Reason = ""
......@@ -137,12 +140,13 @@ class ErrorMessage(uatypes.FrozenClass):
class Acknowledge(uatypes.FrozenClass):
def __init__(self):
self.ProtocolVersion = 0
self.ReceiveBufferSize = 65536
self.SendBufferSize = 65536
self.MaxMessageSize = 0 #No limits
self.MaxChunkCount = 0 #No limits
self.MaxMessageSize = 0 # No limits
self.MaxChunkCount = 0 # No limits
self._freeze()
def to_binary(self):
......@@ -154,8 +158,6 @@ class Acknowledge(uatypes.FrozenClass):
b.append(struct.pack("<I", self.MaxChunkCount))
return b"".join(b)
@staticmethod
def from_binary(data):
ack = Acknowledge()
......@@ -166,7 +168,9 @@ class Acknowledge(uatypes.FrozenClass):
ack.MaxChunkCount = struct.unpack("<I", data.read(4))[0]
return ack
class AsymmetricAlgorithmHeader(uatypes.FrozenClass):
def __init__(self):
self.SecurityPolicyURI = "http://opcfoundation.org/UA/SecurityPolicy#None"
self.SenderCertificate = b""
......@@ -194,6 +198,7 @@ class AsymmetricAlgorithmHeader(uatypes.FrozenClass):
class SymmetricAlgorithmHeader(uatypes.FrozenClass):
def __init__(self):
self.TokenId = 0
self._freeze()
......@@ -213,6 +218,7 @@ class SymmetricAlgorithmHeader(uatypes.FrozenClass):
class SequenceHeader(uatypes.FrozenClass):
def __init__(self):
self.SequenceNumber = None
self.RequestId = None
......@@ -235,54 +241,64 @@ class SequenceHeader(uatypes.FrozenClass):
return "{}(SequenceNumber:{}, RequestId:{} )".format(self.__class__.__name__, self.SequenceNumber, self.RequestId)
__repr__ = __str__
###FIXES for missing switchfield in NodeAttributes classes
# FIXES for missing switchfield in NodeAttributes classes
ana = auto.NodeAttributesMask
class ObjectAttributes(auto.ObjectAttributes):
def __init__(self):
auto.ObjectAttributes.__init__(self)
self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.EventNotifier
class ObjectTypeAttributes(auto.ObjectTypeAttributes):
def __init__(self):
auto.ObjectTypeAttributes.__init__(self)
self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.IsAbstract
class VariableAttributes(auto.VariableAttributes):
def __init__(self):
auto.VariableAttributes.__init__(self)
self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.Value | ana.DataType | ana.ValueRank | ana.ArrayDimensions | ana.AccessLevel | ana.UserAccessLevel | ana.MinimumSamplingInterval |ana.Historizing
self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.Value | ana.DataType | ana.ValueRank | ana.ArrayDimensions | ana.AccessLevel | ana.UserAccessLevel | ana.MinimumSamplingInterval | ana.Historizing
class VariableTypeAttributes(auto.VariableTypeAttributes):
def __init__(self):
auto.VariableTypeAttributes.__init__(self)
self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.Value | ana.DataType | ana.ValueRank | ana.ArrayDimensions | ana.IsAbstract
class MethodAttributes(auto.MethodAttributes):
def __init__(self):
auto.MethodAttributes.__init__(self)
self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.Executable | ana.UserExecutable
self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.Executable | ana.UserExecutable
class ReferenceTypeAttributes(auto.ReferenceTypeAttributes):
def __init__(self):
auto.ReferenceTypeAttributes.__init__(self)
self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.IsAbstract | ana.Symmetric | ana.InverseName
self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.IsAbstract | ana.Symmetric | ana.InverseName
class DataTypeAttributes(auto.DataTypeAttributes):
def __init__(self):
auto.DataTypeAttributes.__init__(self)
self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.IsAbstract
self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.IsAbstract
class ViewAttributes(auto.ViewAttributes):
def __init__(self):
auto.ViewAttributes.__init__(self)
self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.ContainsNoLoops | ana.EventNotifier
self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.ContainsNoLoops | ana.EventNotifier
ObjectIdsInv = {v: k for k, v in ObjectIds.__dict__.items()}
AttributeIdsInv = {v: k for k, v in AttributeIds.__dict__.items()}
......@@ -10,22 +10,25 @@ if sys.version_info.major > 2:
unicode = str
import uuid
import struct
import struct
import opcua.status_code as status_code
from opcua.object_ids import ObjectIds
logger = logging.getLogger('opcua.uaprotocol')
#types that will packed and unpacked directly using struct (string, bytes and datetime are handles as special cases
# types that will packed and unpacked directly using struct (string, bytes and datetime are handles as special cases
UaTypes = ("Boolean", "SByte", "Byte", "Int8", "UInt8", "Int16", "UInt16", "Int32", "UInt32", "Int64", "UInt64", "Float", "Double")
EPOCH_AS_FILETIME = 116444736000000000 # January 1, 1970 as MS file time
HUNDREDS_OF_NANOSECONDS = 10000000
class UTC(tzinfo):
"""UTC"""
def utcoffset(self, dt):
return timedelta(0)
......@@ -36,26 +39,26 @@ class UTC(tzinfo):
return timedelta(0)
#methods copied from David Buxton <david@gasmark6.com> sample code
# methods copied from David Buxton <david@gasmark6.com> sample code
def datetime_to_win_epoch(dt):
if (dt.tzinfo is None) or (dt.tzinfo.utcoffset(dt) is None):
dt = dt.replace(tzinfo=UTC())
ft = EPOCH_AS_FILETIME + (timegm(dt.timetuple()) * HUNDREDS_OF_NANOSECONDS)
return ft + (dt.microsecond * 10)
def win_epoch_to_datetime(epch):
(s, ns100) = divmod(epch - EPOCH_AS_FILETIME, HUNDREDS_OF_NANOSECONDS)
try:
# TDA, this generates exceptions on systems where RTC is really off
dt = datetime.utcfromtimestamp(s)
except:
logger.debug("Exception occurred during conversion within 'win_epoch_to_datetime'." )
logger.debug("Exception occurred during conversion within 'win_epoch_to_datetime'.")
return datetime.now()
dt = dt.replace(microsecond=(ns100 // 10))
return dt
def uatype_to_fmt(uatype):
if uatype == "Char":
return "B"
......@@ -90,6 +93,7 @@ def uatype_to_fmt(uatype):
else:
raise Exception("Error unknown uatype: " + uatype)
def pack_uatype_array(uatype, value):
if value is None:
return struct.pack("<i", -1)
......@@ -99,6 +103,7 @@ def pack_uatype_array(uatype, value):
b.append(pack_uatype(uatype, val))
return b"".join(b)
def pack_uatype(uatype, value):
if uatype == "Null":
return b''
......@@ -115,6 +120,7 @@ def pack_uatype(uatype, value):
else:
return value.to_binary()
def unpack_uatype(uatype, data):
if uatype == "String":
return unpack_string(data)
......@@ -132,6 +138,7 @@ def unpack_uatype(uatype, data):
tmp = eval(code)
return tmp
def unpack_uatype_array(uatype, data):
length = struct.unpack('<i', data.read(4))[0]
if length == -1:
......@@ -142,43 +149,50 @@ def unpack_uatype_array(uatype, data):
result.append(unpack_uatype(uatype, data))
return result
def pack_string(string):
length = len(string)
if length == 0:
return struct.pack("<i", -1)
if not type(string) is bytes:
return struct.pack("<i", -1)
if not isinstance(string, bytes):
string = string.encode()
return struct.pack("<i", length) + string
pack_bytes = pack_string
def unpack_bytes(data):
length = struct.unpack("<i", data.read(4))[0]
if length == -1:
return b''
return data.read(length)
def unpack_string(data):
b = unpack_bytes(data)
if sys.version_info.major < 3:
return str(b)
return b.decode("utf-8")
def test_bit(data, offset):
mask = 1 << offset
return data & mask
def set_bit(data, offset):
mask = 1 << offset
return data | mask
class FrozenClass(object):
"""
make it impossible to add members to a class.
This is a hack since I found out that most bugs are due to misspelling a variable in protocol
"""
__isfrozen = False
def __setattr__(self, key, value):
if self.__isfrozen and not hasattr(self, key):
raise TypeError("Error adding member '{}' to class '{}', class is frozen, members are {}".format(key, self.__class__.__name__, self.__dict__.keys()))
......@@ -189,11 +203,12 @@ class FrozenClass(object):
class Guid(object):
def __init__(self):
self.uuid = uuid.uuid4()
def to_binary(self):
return self.uuid.bytes
return self.uuid.bytes
@staticmethod
def from_binary(data):
......@@ -204,7 +219,9 @@ class Guid(object):
def __eq__(self, other):
return isinstance(other, Guid) and self.uuid == other.uuid
class StatusCode(object):
def __init__(self, value=0):
self.value = value
self.name, self.doc = status_code.get_name_and_doc(value)
......@@ -212,7 +229,7 @@ class StatusCode(object):
def to_binary(self):
return struct.pack("<I", self.value)
@staticmethod
@staticmethod
def from_binary(data):
val = struct.unpack("<I", data.read(4))[0]
sc = StatusCode(val)
......@@ -226,6 +243,7 @@ class StatusCode(object):
return 'StatusCode({})'.format(self.name)
__repr__ = __str__
class NodeIdType(object):
TwoByte = 0
FourByte = 1
......@@ -236,6 +254,7 @@ class NodeIdType(object):
class NodeId(object):
def __init__(self, identifier=None, namespaceidx=0, nodeidtype=None):
self.Identifier = identifier
self.NamespaceIndex = namespaceidx
......@@ -247,17 +266,17 @@ class NodeId(object):
self.NodeIdType = NodeIdType.TwoByte
return
if self.NodeIdType is None:
if type(self.Identifier) == int:
if isinstance(self.Identifier, int):
self.NodeIdType = NodeIdType.Numeric
elif type(self.Identifier) == str:
elif isinstance(self.Identifier, str):
self.NodeIdType = NodeIdType.String
elif type(self.Identifier) == bytes:
elif isinstance(self.Identifier, bytes):
self.NodeIdType = NodeIdType.ByteString
else:
raise Exception("NodeId: Could not guess type of NodeId, set NodeIdType")
def __key(self):
if self.NodeIdType in (NodeIdType.TwoByte, NodeIdType.FourByte, NodeIdType.Numeric):#twobyte, fourbyte and numeric may represent the same node
if self.NodeIdType in (NodeIdType.TwoByte, NodeIdType.FourByte, NodeIdType.Numeric): # twobyte, fourbyte and numeric may represent the same node
return self.NamespaceIndex, self.Identifier
else:
return self.NodeIdType, self.NamespaceIndex, self.Identifier
......@@ -307,7 +326,6 @@ class NodeId(object):
nodeid.ServerIndex = srv
return nodeid
def to_string(self):
string = ""
if self.NamespaceIndex != 0:
......@@ -318,9 +336,9 @@ class NodeId(object):
elif self.NodeIdType == NodeIdType.String:
ntype = "s"
elif self.NodeIdType == NodeIdType.TwoByte:
ntype = "i"
ntype = "i"
elif self.NodeIdType == NodeIdType.FourByte:
ntype = "i"
ntype = "i"
elif self.NodeIdType == NodeIdType.Guid:
ntype = "g"
elif self.NodeIdType == NodeIdType.ByteString:
......@@ -330,7 +348,7 @@ class NodeId(object):
string = "srv=" + str(self.ServerIndex) + string
if self.NamespaceUri:
string += "nsu={}".format(self.NamespaceUri)
return string
return string
def __str__(self):
return "NodeId({})".format(self.to_string())
......@@ -384,39 +402,52 @@ class NodeId(object):
return nid
class TwoByteNodeId(NodeId):
def __init__(self, identifier):
NodeId.__init__(self, identifier, 0, NodeIdType.TwoByte)
class FourByteNodeId(NodeId):
def __init__(self, identifier, namespace=0):
NodeId.__init__(self, identifier, namespace, NodeIdType.FourByte)
class NumericNodeId(NodeId):
def __init__(self, identifier, namespace=0):
NodeId.__init__(self, identifier, namespace, NodeIdType.Numeric)
class ByteStringNodeId(NodeId):
def __init__(self, identifier, namespace=0):
NodeId.__init__(self, identifier, namespace, NodeIdType.ByteString)
class GuidNodeId(NodeId):
def __init__(self, identifier, namespace=0):
NodeId.__init__(self, identifier, namespace, NodeIdType.Guid)
class StringNodeId(NodeId):
def __init__(self, identifier, namespace=0):
NodeId.__init__(self, identifier, namespace, NodeIdType.String)
ExpandedNodeId = NodeId
class QualifiedName(object):
'''
A string qualified with a namespace index.
'''
def __init__(self, name="", namespaceidx=0):
self.NamespaceIndex = namespaceidx
self.Name = name
......@@ -432,14 +463,14 @@ class QualifiedName(object):
idx = 0
name = string
return QualifiedName(name, int(idx))
def to_binary(self):
packet = []
fmt = '<H'
packet.append(struct.pack(fmt, self.NamespaceIndex))
packet.append(pack_string(self.Name))
return b''.join(packet)
@staticmethod
def from_binary(data):
obj = QualifiedName()
......@@ -447,38 +478,43 @@ class QualifiedName(object):
obj.NamespaceIndex = struct.unpack(fmt, data.read(2))[0]
obj.Name = unpack_string(data)
return obj
def __eq__(self, bname):
return isinstance(bname, QualifiedName) and self.Name == bname.Name and self.NamespaceIndex == bname.NamespaceIndex
def __str__(self):
return 'QualifiedName({}:{})'.format(self.NamespaceIndex, self.Name)
__repr__ = __str__
class LocalizedText(FrozenClass):
'''
A string qualified with a namespace index.
'''
def __init__(self, text=""):
self.Encoding = 0
self.Text = text.encode()
if self.Text: self.Encoding |= (1 << 1)
if self.Text:
self.Encoding |= (1 << 1)
self.Locale = b''
self._freeze()
def to_binary(self):
packet = []
if self.Locale: self.Encoding |= (1 << 0)
if self.Text: self.Encoding |= (1 << 1)
if self.Locale:
self.Encoding |= (1 << 0)
if self.Text:
self.Encoding |= (1 << 1)
packet.append(pack_uatype('UInt8', self.Encoding))
if self.Locale:
if self.Locale:
packet.append(pack_uatype('CharArray', self.Locale))
if self.Text:
if self.Text:
packet.append(pack_uatype('CharArray', self.Text))
return b''.join(packet)
@staticmethod
def from_binary(data):
obj = LocalizedText()
......@@ -488,11 +524,11 @@ class LocalizedText(FrozenClass):
if obj.Encoding & (1 << 1):
obj.Text = unpack_uatype('CharArray', data)
return obj
def __str__(self):
return 'LocalizedText(' + 'Encoding:' + str(self.Encoding) + ', ' + \
'Locale:' + str(self.Locale) + ', ' + \
'Text:' + str(self.Text) + ')'
'Locale:' + str(self.Locale) + ', ' + \
'Text:' + str(self.Text) + ')'
__repr__ = __str__
def __eq__(self, other):
......@@ -502,23 +538,26 @@ class LocalizedText(FrozenClass):
class ExtensionObject(FrozenClass):
'''
'''
def __init__(self):
self.TypeId = NodeId()
self.Encoding = 0
self.Body = b''
self._freeze()
def to_binary(self):
packet = []
if self.Body: self.Encoding |= (1 << 0)
if self.Body:
self.Encoding |= (1 << 0)
packet.append(self.TypeId.to_binary())
packet.append(pack_uatype('UInt8', self.Encoding))
if self.Body:
if self.Body:
packet.append(pack_uatype('ByteString', self.Body))
return b''.join(packet)
@staticmethod
def from_binary(data):
obj = ExtensionObject()
......@@ -538,13 +577,14 @@ class ExtensionObject(FrozenClass):
def __str__(self):
return 'ExtensionObject(' + 'TypeId:' + str(self.TypeId) + ', ' + \
'Encoding:' + str(self.Encoding) + ', ' + \
'Body:' + str(self.Body) + ')'
'Encoding:' + str(self.Encoding) + ', ' + \
'Body:' + str(self.Body) + ')'
__repr__ = __str__
class VariantType(Enum):
'''
The possible types of a variant.
'''
......@@ -575,13 +615,16 @@ class VariantType(Enum):
Variant = 24
DiagnosticInfo = 25
class Variant(object):
"""
Create an OPC-UA Variant object.
if no argument a Null Variant is created.
if not variant type is given, attemps to guess type from python type
if a variant is given as value, the new objects becomes a copy of the argument
"""
def __init__(self, value=None, varianttype=None):
self.Encoding = 0
self.Value = value
......@@ -605,17 +648,17 @@ class Variant(object):
def _guess_type(self, val):
if val is None:
return VariantType.Null
elif type(val) == float:
elif isinstance(val, float):
return VariantType.Double
elif type(val) == int:
elif isinstance(val, int):
return VariantType.Int64
elif type(val) in (str, unicode):
return VariantType.String
elif type(val) == bytes:
elif isinstance(val, bytes):
return VariantType.ByteString
elif type(val) == datetime:
elif isinstance(val, datetime):
return VariantType.DateTime
elif type(val) == bool:
elif isinstance(val, bool):
# TDA, added this because it was missing and causes exceptions when 'bool' type is used
return VariantType.Boolean
else:
......@@ -656,44 +699,52 @@ class Variant(object):
class DataValue(object):
'''
A value with an associated timestamp, and quality.
Automatically generated from xml , copied and modified here to fix errors in xml spec
'''
def __init__(self, variant=None):
self.Encoding = 0
if not type(variant) is Variant:
if not isinstance(variant, Variant):
variant = Variant(variant)
self.Value = variant
self.StatusCode = StatusCode()
self.SourceTimestamp = datetime.now()#DateTime()
self.SourceTimestamp = datetime.now() # DateTime()
self.SourcePicoseconds = 0
self.ServerTimestamp = datetime.now()#DateTime()
self.ServerTimestamp = datetime.now() # DateTime()
self.ServerPicoseconds = 0
def to_binary(self):
packet = []
if self.Value: self.Encoding |= (1 << 0)
if self.StatusCode: self.Encoding |= (1 << 1)
if self.SourceTimestamp: self.Encoding |= (1 << 2)
if self.ServerTimestamp: self.Encoding |= (1 << 3)
if self.SourcePicoseconds: self.Encoding |= (1 << 4)
if self.ServerPicoseconds: self.Encoding |= (1 << 5)
if self.Value:
self.Encoding |= (1 << 0)
if self.StatusCode:
self.Encoding |= (1 << 1)
if self.SourceTimestamp:
self.Encoding |= (1 << 2)
if self.ServerTimestamp:
self.Encoding |= (1 << 3)
if self.SourcePicoseconds:
self.Encoding |= (1 << 4)
if self.ServerPicoseconds:
self.Encoding |= (1 << 5)
packet.append(pack_uatype('UInt8', self.Encoding))
if self.Value:
if self.Value:
packet.append(self.Value.to_binary())
if self.StatusCode:
if self.StatusCode:
packet.append(self.StatusCode.to_binary())
if self.SourceTimestamp:
packet.append(pack_uatype('DateTime', self.SourceTimestamp))#self.SourceTimestamp.to_binary())
if self.ServerTimestamp:
packet.append(pack_uatype('DateTime', self.ServerTimestamp))#self.ServerTimestamp.to_binary())
if self.SourcePicoseconds:
if self.SourceTimestamp:
packet.append(pack_uatype('DateTime', self.SourceTimestamp)) # self.SourceTimestamp.to_binary())
if self.ServerTimestamp:
packet.append(pack_uatype('DateTime', self.ServerTimestamp)) # self.ServerTimestamp.to_binary())
if self.SourcePicoseconds:
packet.append(pack_uatype('UInt16', self.SourcePicoseconds))
if self.ServerPicoseconds:
if self.ServerPicoseconds:
packet.append(pack_uatype('UInt16', self.ServerPicoseconds))
return b''.join(packet)
@staticmethod
def from_binary(data):
obj = DataValue()
......@@ -703,27 +754,22 @@ class DataValue(object):
if obj.Encoding & (1 << 1):
obj.StatusCode = StatusCode.from_binary(data)
if obj.Encoding & (1 << 2):
obj.SourceTimestamp = unpack_uatype('DateTime', data)#DateTime.from_binary(data)
obj.SourceTimestamp = unpack_uatype('DateTime', data) # DateTime.from_binary(data)
if obj.Encoding & (1 << 3):
obj.ServerTimestamp = unpack_uatype('DateTime', data)#DateTime.from_binary(data)
obj.ServerTimestamp = unpack_uatype('DateTime', data) # DateTime.from_binary(data)
if obj.Encoding & (1 << 4):
obj.SourcePicoseconds = unpack_uatype('UInt16', data)
if obj.Encoding & (1 << 5):
obj.ServerPicoseconds = unpack_uatype('UInt16', data)
return obj
def __str__(self):
return 'DataValue(' + 'Encoding:' + str(self.Encoding) + ', ' + \
'Value:' + str(self.Value) + ', ' + \
'StatusCode:' + str(self.StatusCode) + ', ' + \
'SourceTimestamp:' + str(self.SourceTimestamp) + ', ' + \
'ServerTimestamp:' + str(self.ServerTimestamp) + ', ' + \
'SourcePicoseconds:' + str(self.SourcePicoseconds) + ', ' + \
'ServerPicoseconds:' + str(self.ServerPicoseconds) + ')'
__repr__ = __str__
'Value:' + str(self.Value) + ', ' + \
'StatusCode:' + str(self.StatusCode) + ', ' + \
'SourceTimestamp:' + str(self.SourceTimestamp) + ', ' + \
'ServerTimestamp:' + str(self.ServerTimestamp) + ', ' + \
'SourcePicoseconds:' + str(self.SourcePicoseconds) + ', ' + \
'ServerPicoseconds:' + str(self.ServerPicoseconds) + ')'
__repr__ = __str__
......@@ -3,10 +3,12 @@ import uuid
class Buffer(object):
"""
alternative to io.BytesIO making debug easier
and added a few conveniance methods
"""
def __init__(self, data):
self.logger = logging.getLogger(__name__)
self.data = data
......@@ -45,8 +47,6 @@ class Buffer(object):
return self.data[:size]
def recv_all(socket, size):
"""
Receive up to size bytes from socket
......@@ -60,6 +60,6 @@ def recv_all(socket, size):
size -= len(chunk)
return data
def create_nonce():
return uuid.uuid4().bytes + uuid.uuid4().bytes #seems we need at least 32 bytes not 16 as python gives us...
def create_nonce():
return uuid.uuid4().bytes + uuid.uuid4().bytes # seems we need at least 32 bytes not 16 as python gives us...
......@@ -24,20 +24,26 @@ from opcua import AttributeIds
port_num1 = 48510
port_num2 = 48530
class SubHandler():
'''
Dummy subscription client
'''
def data_change(self, handle, node, val, attr):
pass
pass
def event(self, handle, event):
pass
pass
class MySubHandler():
'''
More advanced subscription client using Future, so we can wait for events in tests
More advanced subscription client using Future, so we can wait for events in tests
'''
def __init__(self):
self.future = Future()
......@@ -52,8 +58,9 @@ class MySubHandler():
class Unit(unittest.TestCase):
'''
Simple unit test that do not need to setup a server or a client
Simple unit test that do not need to setup a server or a client
'''
def test_guid(self):
......@@ -62,37 +69,37 @@ class Unit(unittest.TestCase):
def test_nodeid(self):
nid = ua.NodeId()
self.assertEqual(nid.NodeIdType, ua.NodeIdType.TwoByte)
self.assertEqual(nid.NodeIdType, ua.NodeIdType.TwoByte)
nid = ua.NodeId(446, 3, ua.NodeIdType.FourByte)
self.assertEqual(nid.NodeIdType, ua.NodeIdType.FourByte)
self.assertEqual(nid.NodeIdType, ua.NodeIdType.FourByte)
d = nid.to_binary()
new_nid = nid.from_binary(io.BytesIO(d))
self.assertEqual(new_nid, nid)
self.assertEqual(new_nid.NodeIdType, ua.NodeIdType.FourByte)
self.assertEqual(new_nid.Identifier, 446)
self.assertEqual(new_nid.NamespaceIndex, 3)
self.assertEqual(new_nid, nid)
self.assertEqual(new_nid.NodeIdType, ua.NodeIdType.FourByte)
self.assertEqual(new_nid.Identifier, 446)
self.assertEqual(new_nid.NamespaceIndex, 3)
tb = ua.TwoByteNodeId(53)
fb = ua.FourByteNodeId(53)
n = ua.NumericNodeId(53)
n1 = ua.NumericNodeId(53, 0)
s = ua.StringNodeId(53, 0)#should we raise an exception???
s = ua.StringNodeId(53, 0) # should we raise an exception???
s1 = ua.StringNodeId("53", 0)
bs = ua.ByteStringNodeId(b"53", 0)
gid = ua.Guid()
g = ua.ByteStringNodeId(gid, 0)
self.assertEqual(tb, fb)
self.assertEqual(tb, n)
self.assertEqual(tb, n1)
self.assertEqual(n1, fb)
self.assertNotEqual(n1, s)
self.assertNotEqual(s, bs)
self.assertNotEqual(s, g)
self.assertEqual(tb, fb)
self.assertEqual(tb, n)
self.assertEqual(tb, n1)
self.assertEqual(n1, fb)
self.assertNotEqual(n1, s)
self.assertNotEqual(s, bs)
self.assertNotEqual(s, g)
def test_nodeid_string(self):
nid0 = ua.NodeId(45)
self.assertEqual(nid0, ua.NodeId.from_string("i=45"))
self.assertEqual(nid0, ua.NodeId.from_string("ns=0;i=45"))
self.assertEqual(nid0, ua.NodeId.from_string("i=45"))
self.assertEqual(nid0, ua.NodeId.from_string("ns=0;i=45"))
nid = ua.NodeId(45, 10)
self.assertEqual(nid, ua.NodeId.from_string("i=45; ns=10"))
self.assertNotEqual(nid, ua.NodeId.from_string("i=45; ns=11"))
......@@ -102,7 +109,7 @@ class Unit(unittest.TestCase):
def test_expandednodeid(self):
nid = ua.ExpandedNodeId()
self.assertEqual(nid.NodeIdType, ua.NodeIdType.TwoByte)
self.assertEqual(nid.NodeIdType, ua.NodeIdType.TwoByte)
nid2 = ua.ExpandedNodeId.from_binary(ua.utils.Buffer(nid.to_binary()))
self.assertEqual(nid, nid2)
......@@ -111,7 +118,7 @@ class Unit(unittest.TestCase):
obj2 = ua.ExtensionObject.from_binary(ua.utils.Buffer(obj.to_binary()))
self.assertEqual(obj2.TypeId, obj2.TypeId)
self.assertEqual(obj2.Body, obj2.Body)
def test_datetime(self):
now = datetime.now()
epch = ua.datetime_to_win_epoch(now)
......@@ -126,11 +133,11 @@ class Unit(unittest.TestCase):
def test_equal_nodeid(self):
nid1 = ua.NodeId(999, 2)
nid2 = ua.NodeId(999, 2)
self.assertTrue(nid1==nid2)
self.assertTrue(id(nid1)!=id(nid2))
self.assertTrue(nid1 == nid2)
self.assertTrue(id(nid1) != id(nid2))
def test_zero_nodeid(self):
self.assertEqual(ua.NodeId(), ua.NodeId(0,0))
self.assertEqual(ua.NodeId(), ua.NodeId(0, 0))
self.assertEqual(ua.NodeId(), ua.NodeId.from_string('ns=0;i=0;'))
def test_string_nodeid(self):
......@@ -150,25 +157,24 @@ class Unit(unittest.TestCase):
self.assertEqual(nid.NamespaceIndex, 2)
self.assertEqual(nid.Identifier, 'PLC1.Manufacturer')
def test_strrepr_nodeid(self):
nid = ua.NodeId.from_string('ns=2;s=PLC1.Manufacturer;')
self.assertEqual(nid.to_string(), 'ns=2;s=PLC1.Manufacturer')
#self.assertEqual(repr(nid), 'ns=2;s=PLC1.Manufacturer;')
def test_qualified_name(self):
qn = ua.QualifiedName('qname', 2)
self.assertEqual(qn.NamespaceIndex, 2)
self.assertEqual(qn.Name, 'qname')
self.assertEqual(qn.to_string(), '2:qname')
def test_datavalue(self):
dv = ua.DataValue(123)
self.assertEqual(dv.Value, ua.Variant(123))
self.assertEqual(type(dv.Value), ua.Variant)
dv = ua.DataValue('abc')
self.assertEqual(dv.Value, ua.Variant('abc'))
now = datetime.now()
now = datetime.now()
dv.source_timestamp = now
def test_variant(self):
......@@ -182,13 +188,13 @@ class Unit(unittest.TestCase):
v2 = ua.Variant.from_binary(ua.utils.Buffer(v.to_binary()))
self.assertEqual(v.Value, v2.Value)
self.assertEqual(v.VariantType, v2.VariantType)
#commonity method:
# commonity method:
self.assertEqual(v, ua.Variant(v))
def test_variant_array(self):
v = ua.Variant([1,2,3,4,5])
v = ua.Variant([1, 2, 3, 4, 5])
self.assertEqual(v.Value[1], 2)
#self.assertEqual(v.VarianType, ua.VariantType.Int64) # we do not care, we should aonly test for sutff that matter
# self.assertEqual(v.VarianType, ua.VariantType.Int64) # we do not care, we should aonly test for sutff that matter
v2 = ua.Variant.from_binary(ua.utils.Buffer(v.to_binary()))
self.assertEqual(v.Value, v2.Value)
self.assertEqual(v.VariantType, v2.VariantType)
......@@ -210,10 +216,12 @@ class Unit(unittest.TestCase):
t4 = ua.LocalizedText.from_binary(ua.utils.Buffer(t1.to_binary()))
self.assertEqual(t1, t4)
class CommonTests(object):
'''
Tests that will be run twice. Once on server side and once on
client side since we have been carefull to have the exact
Tests that will be run twice. Once on server side and once on
client side since we have been carefull to have the exact
same api on server and client side
'''
......@@ -221,13 +229,13 @@ class CommonTests(object):
root = self.opc.get_root_node()
self.assertEqual(ua.QualifiedName('Root', 0), root.get_browse_name())
self.assertEqual(ua.LocalizedText('Root'), root.get_display_name())
nid = ua.NodeId(84, 0)
nid = ua.NodeId(84, 0)
self.assertEqual(nid, root.nodeid)
def test_objects(self):
objects = self.opc.get_objects_node()
self.assertEqual(ua.QualifiedName('Objects', 0), objects.get_browse_name())
nid = ua.NodeId(85, 0)
nid = ua.NodeId(85, 0)
self.assertEqual(nid, objects.nodeid)
def test_browse(self):
......@@ -265,7 +273,7 @@ class CommonTests(object):
time.sleep(0.1)
sub.unsubscribe(handle)
sub.delete()
def test_subscribe_events(self):
sub = self.opc.create_subscription(100, sclt)
handle = sub.subscribe_events()
......@@ -278,9 +286,9 @@ class CommonTests(object):
#cond = msclt.setup()
sub = self.opc.create_subscription(100, msclt)
handle = sub.subscribe_events()
ev = Event(self.srv.iserver.isession)
msg = b"this is my msg "
msg = b"this is my msg "
ev.Message.Text = msg
tid = datetime.now()
ev.Time = tid
......@@ -288,23 +296,22 @@ class CommonTests(object):
#ev.source_name = "our server node"
ev.Severity = 500
ev.trigger()
clthandle, ev = msclt.future.result()
#with cond:
#ret = cond.wait(50000)
#if sys.version_info.major>2: self.assertEqual(ret, True) # we went into timeout waiting for subcsription callback
#else: pass # python2
self.assertIsNot(ev, None)# we did not receive event
# with cond:
#ret = cond.wait(50000)
# if sys.version_info.major>2: self.assertEqual(ret, True) # we went into timeout waiting for subcsription callback
# else: pass # python2
self.assertIsNot(ev, None) # we did not receive event
self.assertEqual(ev.Message.Text, msg)
#self.assertEqual(msclt.ev.Time, tid)
self.assertEqual(ev.Severity, 500)
self.assertEqual(ev.SourceNode, self.opc.get_server_node().nodeid)
#time.sleep(0.1)
# time.sleep(0.1)
sub.unsubscribe(handle)
sub.delete()
def test_non_existing_path(self):
root = self.opc.get_root_node()
with self.assertRaises(Exception):
......@@ -334,29 +341,29 @@ class CommonTests(object):
objects = self.opc.get_objects_node()
v1 = objects.add_variable(4, "test_datetime", now)
tid = v1.get_value()
self.assertEqual(now, tid)
self.assertEqual(now, tid)
def test_add_numeric_variable(self):
objects = self.opc.get_objects_node()
v = objects.add_variable('ns=3;i=888;', '3:numericnodefromstring', 99)
nid = ua.NodeId(888, 3)
qn = ua.QualifiedName('numericnodefromstring', 3)
qn = ua.QualifiedName('numericnodefromstring', 3)
self.assertEqual(nid, v.nodeid)
self.assertEqual(qn, v.get_browse_name())
def test_add_string_variable(self):
objects = self.opc.get_objects_node()
v = objects.add_variable('ns=3;s=stringid;', '3:stringnodefromstring', [68])
nid = ua.NodeId('stringid', 3)
qn = ua.QualifiedName('stringnodefromstring', 3)
nid = ua.NodeId('stringid', 3)
qn = ua.QualifiedName('stringnodefromstring', 3)
self.assertEqual(nid, v.nodeid)
self.assertEqual(qn, v.get_browse_name())
def test_add_string_array_variable(self):
objects = self.opc.get_objects_node()
v = objects.add_variable('ns=3;s=stringarrayid;', '9:stringarray', ['l', 'b'])
nid = ua.NodeId('stringarrayid', 3)
qn = ua.QualifiedName('stringarray', 9)
nid = ua.NodeId('stringarrayid', 3)
qn = ua.QualifiedName('stringarray', 9)
self.assertEqual(nid, v.nodeid)
self.assertEqual(qn, v.get_browse_name())
val = v.get_value()
......@@ -430,10 +437,9 @@ class CommonTests(object):
with self.assertRaises(Exception):
bad.set_value(89)
with self.assertRaises(Exception):
bad.add_object(0, "myobj" )
bad.add_object(0, "myobj")
with self.assertRaises(Exception):
bad.get_child(0, "myobj" )
bad.get_child(0, "myobj")
def test_array_value(self):
o = self.opc.get_objects_node()
......@@ -446,13 +452,13 @@ class CommonTests(object):
v = o.add_variable(3, 'VariableArrayValue', [1, 2, 3])
v.set_value([1])
val = v.get_value()
self.assertEqual([1], val)
self.assertEqual([1], val)
def test_subscription_data_change(self):
'''
test subscriptions. This is far too complicated for
a unittest but, setting up subscriptions requires a lot
of code, so when we first set it up, it is best
of code, so when we first set it up, it is best
to test as many things as possible
'''
msclt = MySubHandler()
......@@ -467,34 +473,34 @@ class CommonTests(object):
handle1 = sub.subscribe_data_change(v1)
# Now check we get the start value
clthandle, node, val, attr = msclt.future.result()
#with cond:
#ret = cond.wait(0.5)
#if sys.version_info.major>2: self.assertEqual(ret, True) # we went into timeout waiting for subcsription callback
#else: pass # XXX
clthandle, node, val, attr = msclt.future.result()
# with cond:
#ret = cond.wait(0.5)
# if sys.version_info.major>2: self.assertEqual(ret, True) # we went into timeout waiting for subcsription callback
# else: pass # XXX
self.assertEqual(val, startv1)
self.assertEqual(node, v1)
msclt.reset()#reset future object
msclt.reset() # reset future object
# modify v1 and check we get value
# modify v1 and check we get value
v1.set_value([5])
clthandle, node, val, attr = msclt.future.result()
#with cond:
#ret = cond.wait(0.5)
#if sys.version_info.major>2: self.assertEqual(ret, True) # we went into timeout waiting for subcsription callback
#else: pass # XXX
clthandle, node, val, attr = msclt.future.result()
# with cond:
#ret = cond.wait(0.5)
# if sys.version_info.major>2: self.assertEqual(ret, True) # we went into timeout waiting for subcsription callback
# else: pass # XXX
self.assertEqual(node, v1)
self.assertEqual(val, [5])
with self.assertRaises(Exception):
sub.unsubscribe(999)# non existing handle
sub.unsubscribe(999) # non existing handle
sub.unsubscribe(handle1)
with self.assertRaises(Exception):
sub.unsubscribe(handle1) # second try should fail
sub.unsubscribe(handle1) # second try should fail
sub.delete()
with self.assertRaises(Exception):
sub.unsubscribe(handle1) # sub does not exist anymore
sub.unsubscribe(handle1) # sub does not exist anymore
def test_subscribe_server_time(self):
msclt = MySubHandler()
......@@ -503,10 +509,10 @@ class CommonTests(object):
sub = self.opc.create_subscription(200, msclt)
handle = sub.subscribe_data_change(server_time_node)
clthandle, node, val, attr = msclt.future.result()
clthandle, node, val, attr = msclt.future.result()
self.assertEqual(node, server_time_node)
delta = datetime.now() - val
delta = datetime.now() - val
self.assertTrue(delta < timedelta(seconds=1))
sub.unsubscribe(handle)
......@@ -514,11 +520,11 @@ class CommonTests(object):
def test_use_namespace(self):
idx = self.opc.get_namespace_index("urn:freeopcua:python:server")
self.assertEqual(idx, 1)
self.assertEqual(idx, 1)
root = self.opc.get_root_node()
myvar = root.add_variable(idx, 'var_in_custom_namespace', [5])
myid = myvar.nodeid
self.assertEqual(idx, myid.NamespaceIndex)
self.assertEqual(idx, myid.NamespaceIndex)
def test_method(self):
o = self.opc.get_objects_node()
......@@ -526,10 +532,10 @@ class CommonTests(object):
result = o.call_method("2:ServerMethod", 2.1)
self.assertEqual(result, 4.2)
with self.assertRaises(Exception):
#FIXME: we should raise a more precise exception
# FIXME: we should raise a more precise exception
result = o.call_method("2:ServerMethod", 2.1, 89, 9)
with self.assertRaises(Exception):
result = o.call_method(ua.NodeId(999), 2.1) #non existing method
result = o.call_method(ua.NodeId(999), 2.1) # non existing method
def test_method_array(self):
o = self.opc.get_objects_node()
......@@ -558,9 +564,6 @@ class CommonTests(object):
self.assertTrue(endpoints[0].EndpointUrl.startswith("opc.tcp://localhost"))
def add_server_methods(srv):
@uamethod
def func(parent, value):
......@@ -569,7 +572,6 @@ def add_server_methods(srv):
o = srv.get_objects_node()
v = o.add_method(ua.NodeId("ServerMethod", 2), ua.QualifiedName('ServerMethod', 2), func, [ua.VariantType.Int64], [ua.VariantType.Int64])
@uamethod
def func2(parent, methodname, value):
return math.sin(value)
......@@ -585,18 +587,18 @@ def add_server_methods(srv):
v = o.add_method(ua.NodeId("ServerMethodArray2", 2), ua.QualifiedName('ServerMethodArray2', 2), func3, [ua.VariantType.Int64], [ua.VariantType.Int64])
class TestClient(unittest.TestCase, CommonTests):
'''
Run common tests on client side
Of course we need a server so we start a server in another
Of course we need a server so we start a server in another
process using python Process module
Tests that can only be run on client side must be defined here
'''
@classmethod
def setUpClass(self):
# start our own server
self.srv = Server()
self.srv = Server()
self.srv.set_endpoint('opc.tcp://localhost:%d' % port_num1)
add_server_methods(self.srv)
self.srv.start()
......@@ -614,13 +616,13 @@ class TestClient(unittest.TestCase, CommonTests):
def test_service_fault(self):
request = ua.ReadRequest()
request.TypeId = ua.FourByteNodeId(999) # bad type!
request.TypeId = ua.FourByteNodeId(999) # bad type!
with self.assertRaises(Exception):
self.clt.bclient._send_request(request)
class TestServer(unittest.TestCase, CommonTests):
'''
Run common tests on server side
Tests that can only be run on server side must be defined here
......@@ -631,18 +633,17 @@ class TestServer(unittest.TestCase, CommonTests):
self.srv.set_endpoint('opc.tcp://localhost:%d' % port_num2)
add_server_methods(self.srv)
self.srv.start()
self.opc = self.srv
self.opc = self.srv
@classmethod
def tearDownClass(self):
self.srv.stop()
def test_register_namespace(self):
uri = 'http://mycustom.Namespace.com'
idx1 = self.opc.register_namespace(uri)
idx2 = self.opc.get_namespace_index(uri)
self.assertEqual(idx1, idx2)
self.assertEqual(idx1, idx2)
def test_register_use_namespace(self):
uri = 'http://my_very_custom.Namespace.com'
......@@ -650,7 +651,7 @@ class TestServer(unittest.TestCase, CommonTests):
root = self.opc.get_root_node()
myvar = root.add_variable(idx, 'var_in_custom_namespace', [5])
myid = myvar.nodeid
self.assertEqual(idx, myid.NamespaceIndex)
self.assertEqual(idx, myid.NamespaceIndex)
def test_server_method(self):
def func(parent, variant):
......@@ -662,10 +663,8 @@ class TestServer(unittest.TestCase, CommonTests):
self.assertEqual(result, 4.2)
if __name__ == '__main__':
logging.basicConfig(level=logging.WARN)
sclt = SubHandler()
unittest.main(verbosity=3)
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