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

autopep8

parent e6300bec
...@@ -3,10 +3,13 @@ import logging ...@@ -3,10 +3,13 @@ import logging
from opcua import Client from opcua import Client
from opcua import uaprotocol as ua from opcua import uaprotocol as ua
class SubHandler(object): class SubHandler(object):
""" """
Client to subscription. It will receive events from server Client to subscription. It will receive events from server
""" """
def data_change(self, handle, node, val, attr): def data_change(self, handle, node, val, attr):
print("Python: New data change event", handle, node, val, attr) print("Python: New data change event", handle, node, val, attr)
...@@ -14,8 +17,7 @@ class SubHandler(object): ...@@ -14,8 +17,7 @@ class SubHandler(object):
print("Python: New event", handle, event) print("Python: New event", handle, event)
if __name__ == "__main__":
if __name__ == "__main__":
from IPython import embed from IPython import embed
logging.basicConfig(level=logging.WARN) logging.basicConfig(level=logging.WARN)
client = Client("opc.tcp://localhost:53530/OPCUA/SimulationServer/") client = Client("opc.tcp://localhost:53530/OPCUA/SimulationServer/")
...@@ -33,7 +35,7 @@ if __name__ == "__main__": ...@@ -33,7 +35,7 @@ if __name__ == "__main__":
myuint64 = client.get_node("ns=4;s=UInt64") myuint64 = client.get_node("ns=4;s=UInt64")
myint32 = client.get_node("ns=4;s=Int32") myint32 = client.get_node("ns=4;s=Int32")
myuint32 = client.get_node("ns=4;s=UInt32") myuint32 = client.get_node("ns=4;s=UInt32")
var = client.get_node(ua.NodeId("Random1", 5)) var = client.get_node(ua.NodeId("Random1", 5))
print("var is: ", var) print("var is: ", var)
print("value of var is: ", var.get_value()) print("value of var is: ", var.get_value())
...@@ -42,7 +44,6 @@ if __name__ == "__main__": ...@@ -42,7 +44,6 @@ if __name__ == "__main__":
myfloat.set_value(ua.Variant(1.234, ua.VariantType.Float)) myfloat.set_value(ua.Variant(1.234, ua.VariantType.Float))
print("reading float value: ", myfloat.get_value()) print("reading float value: ", myfloat.get_value())
handler = SubHandler() handler = SubHandler()
sub = client.create_subscription(500, handler) sub = client.create_subscription(500, handler)
sub.subscribe_data_change(var) sub.subscribe_data_change(var)
...@@ -52,7 +53,6 @@ if __name__ == "__main__": ...@@ -52,7 +53,6 @@ if __name__ == "__main__":
result = device.call_method(method, ua.Variant("sin"), ua.Variant(180, ua.VariantType.Double)) result = device.call_method(method, ua.Variant("sin"), ua.Variant(180, ua.VariantType.Double))
print("Mehtod result is: ", result) print("Mehtod result is: ", result)
embed() embed()
client.close_session() client.close_session()
finally: finally:
......
...@@ -5,6 +5,7 @@ try: ...@@ -5,6 +5,7 @@ try:
from IPython import embed from IPython import embed
except ImportError: except ImportError:
import code import code
def embed(): def embed():
vars = globals() vars = globals()
vars.update(locals()) vars.update(locals())
...@@ -15,10 +16,13 @@ except ImportError: ...@@ -15,10 +16,13 @@ except ImportError:
from opcua import Client from opcua import Client
from opcua import uaprotocol as ua from opcua import uaprotocol as ua
class SubHandler(object): class SubHandler(object):
""" """
Client to subscription. It will receive events from server Client to subscription. It will receive events from server
""" """
def data_change(self, handle, node, val, attr): def data_change(self, handle, node, val, attr):
print("Python: New data change event", handle, node, val, attr) print("Python: New data change event", handle, node, val, attr)
...@@ -26,11 +30,10 @@ class SubHandler(object): ...@@ -26,11 +30,10 @@ class SubHandler(object):
print("Python: New event", handle, event) print("Python: New event", handle, event)
if __name__ == "__main__":
if __name__ == "__main__":
logging.basicConfig(level=logging.WARN) logging.basicConfig(level=logging.WARN)
#logger = logging.getLogger("KeepAlive") #logger = logging.getLogger("KeepAlive")
#logger.setLevel(logging.DEBUG) # logger.setLevel(logging.DEBUG)
client = Client("opc.tcp://localhost:4841/freeopcua/server/") client = Client("opc.tcp://localhost:4841/freeopcua/server/")
try: try:
client.connect() client.connect()
...@@ -39,8 +42,8 @@ if __name__ == "__main__": ...@@ -39,8 +42,8 @@ if __name__ == "__main__":
print(root.get_children()) print(root.get_children())
print(root.get_browse_name()) print(root.get_browse_name())
#var = client.get_node(ua.NodeId(1002, 2)) #var = client.get_node(ua.NodeId(1002, 2))
#print(var) # print(var)
#print(var.get_value()) # print(var.get_value())
#var.set_value(ua.Variant([23], ua.VariantType.Int64)) #var.set_value(ua.Variant([23], ua.VariantType.Int64))
state = root.get_child(["0:Objects", "0:Server"]) state = root.get_child(["0:Objects", "0:Server"])
print(state) print(state)
...@@ -52,10 +55,10 @@ if __name__ == "__main__": ...@@ -52,10 +55,10 @@ if __name__ == "__main__":
handle = sub.subscribe_data_change(myvar) handle = sub.subscribe_data_change(myvar)
time.sleep(0.1) time.sleep(0.1)
sub.subscribe_events() sub.subscribe_events()
#sub.unsubscribe(handle) # sub.unsubscribe(handle)
#sub.delete() # sub.delete()
#calling a method on server # calling a method on server
res = obj.call_method("2:multiply", 3, "klk") res = obj.call_method("2:multiply", 3, "klk")
print("method result is: ", res) print("method result is: ", res)
......
...@@ -5,6 +5,7 @@ try: ...@@ -5,6 +5,7 @@ try:
from IPython import embed from IPython import embed
except ImportError: except ImportError:
import code import code
def embed(): def embed():
vars = globals() vars = globals()
vars.update(locals()) vars.update(locals())
...@@ -16,38 +17,43 @@ from opcua import ua, uamethod, Server, Event, ObjectIds ...@@ -16,38 +17,43 @@ from opcua import ua, uamethod, Server, Event, ObjectIds
class SubHandler(object): class SubHandler(object):
""" """
Client to subscription. It will receive events from server Client to subscription. It will receive events from server
""" """
def data_change(self, handle, node, val, attr): def data_change(self, handle, node, val, attr):
print("Python: New data change event", handle, node, val, attr) print("Python: New data change event", handle, node, val, attr)
def event(self, handle, event): def event(self, handle, event):
print("Python: New event", handle, event) print("Python: New event", handle, event)
#method to be exposed through server # method to be exposed through server
def func(parent, variant): def func(parent, variant):
return [variant.Value * 2] 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 # uses a decorator to automatically convert to and from variants
@uamethod @uamethod
def multiply(parent, x, y): def multiply(parent, x, y):
print("multiply method call with parameters: ", x, y) print("multiply method call with parameters: ", x, y)
return x*y return x * y
if __name__ == "__main__": if __name__ == "__main__":
#optional setup logging # optional setup logging
logging.basicConfig(level=logging.WARN) logging.basicConfig(level=logging.WARN)
#logger = logging.getLogger("opcua.address_space") #logger = logging.getLogger("opcua.address_space")
#logger = logging.getLogger("opcua.internal_server") #logger = logging.getLogger("opcua.internal_server")
#logger.setLevel(logging.DEBUG) # logger.setLevel(logging.DEBUG)
#logger = logging.getLogger("opcua.subscription_server") #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 = Server()
server.set_endpoint("opc.tcp://localhost:4841/freeopcua/server/") server.set_endpoint("opc.tcp://localhost:4841/freeopcua/server/")
server.set_server_name("FreeOpcUa Example Server") server.set_server_name("FreeOpcUa Example Server")
...@@ -73,13 +79,13 @@ if __name__ == "__main__": ...@@ -73,13 +79,13 @@ if __name__ == "__main__":
myevent = server.get_event_object(ObjectIds.BaseEventType) myevent = server.get_event_object(ObjectIds.BaseEventType)
myevent.Message.Text = "This is my event" myevent.Message.Text = "This is my event"
myevent.Severity = 300 myevent.Severity = 300
# starting! # starting!
server.start() server.start()
print("Available loggers are: ", logging.Logger.manager.loggerDict.keys()) print("Available loggers are: ", logging.Logger.manager.loggerDict.keys())
try: try:
handler = SubHandler() 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) sub = server.create_subscription(500, handler)
handle = sub.subscribe_data_change(myvar) handle = sub.subscribe_data_change(myvar)
# trigger event, all subscribed clients wil receive it # trigger event, all subscribed clients wil receive it
...@@ -88,4 +94,3 @@ if __name__ == "__main__": ...@@ -88,4 +94,3 @@ if __name__ == "__main__":
embed() embed()
finally: finally:
server.stop() server.stop()
...@@ -15,7 +15,7 @@ from opcua.server import Server ...@@ -15,7 +15,7 @@ from opcua.server import Server
def uamethod(func): def uamethod(func):
""" """
Method decorator to automatically convert Method decorator to automatically convert
arguments and output to and from variants arguments and output to and from variants
""" """
def wrapper(parent, *args): def wrapper(parent, *args):
...@@ -23,10 +23,9 @@ def uamethod(func): ...@@ -23,10 +23,9 @@ def uamethod(func):
return to_variant(result) return to_variant(result)
return wrapper return wrapper
def to_variant(*args): def to_variant(*args):
uaargs = [] uaargs = []
for arg in args: for arg in args:
uaargs.append(ua.Variant(arg)) uaargs.append(ua.Variant(arg))
return uaargs return uaargs
...@@ -4,17 +4,21 @@ import pickle ...@@ -4,17 +4,21 @@ import pickle
from opcua import ua from opcua import ua
class AttributeValue(object): class AttributeValue(object):
def __init__(self, value): def __init__(self, value):
self.value = value self.value = value
self.value_callback = None self.value_callback = None
self.datachange_callbacks = {} self.datachange_callbacks = {}
def __str__(self): def __str__(self):
return "AttributeValue({})".format(self.value) return "AttributeValue({})".format(self.value)
__repr__ = __str__ __repr__ = __str__
class NodeData(object): class NodeData(object):
def __init__(self, nodeid): def __init__(self, nodeid):
self.nodeid = nodeid self.nodeid = nodeid
self.attributes = {} self.attributes = {}
...@@ -27,6 +31,7 @@ class NodeData(object): ...@@ -27,6 +31,7 @@ class NodeData(object):
class AttributeService(object): class AttributeService(object):
def __init__(self, aspace): def __init__(self, aspace):
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self._aspace = aspace self._aspace = aspace
...@@ -45,7 +50,9 @@ class AttributeService(object): ...@@ -45,7 +50,9 @@ class AttributeService(object):
res.append(self._aspace.set_attribute_value(writevalue.NodeId, writevalue.AttributeId, writevalue.Value)) res.append(self._aspace.set_attribute_value(writevalue.NodeId, writevalue.AttributeId, writevalue.Value))
return res return res
class ViewService(object): class ViewService(object):
def __init__(self, aspace): def __init__(self, aspace):
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self._aspace = aspace self._aspace = aspace
...@@ -132,7 +139,7 @@ class ViewService(object): ...@@ -132,7 +139,7 @@ class ViewService(object):
current = nodeid current = nodeid
target = ua.BrowsePathTarget() target = ua.BrowsePathTarget()
target.TargetId = current target.TargetId = current
target.RemainingPathIndex = 4294967295 target.RemainingPathIndex = 4294967295
res.Targets = [target] res.Targets = [target]
return res return res
...@@ -140,7 +147,7 @@ class ViewService(object): ...@@ -140,7 +147,7 @@ class ViewService(object):
with self._aspace.lock: with self._aspace.lock:
nodedata = self._aspace.nodes[nodeid] nodedata = self._aspace.nodes[nodeid]
for ref in nodedata.references: for ref in nodedata.references:
#FIXME: here we should check other arguments!! # FIXME: here we should check other arguments!!
if ref.BrowseName == el.TargetName: if ref.BrowseName == el.TargetName:
return ref.NodeId return ref.NodeId
self.logger.info("element %s was not found in node %s", el, nodeid) self.logger.info("element %s was not found in node %s", el, nodeid)
...@@ -148,6 +155,7 @@ class ViewService(object): ...@@ -148,6 +155,7 @@ class ViewService(object):
class NodeManagementService(object): class NodeManagementService(object):
def __init__(self, aspace): def __init__(self, aspace):
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self._aspace = aspace self._aspace = aspace
...@@ -167,19 +175,19 @@ class NodeManagementService(object): ...@@ -167,19 +175,19 @@ class NodeManagementService(object):
result.StatusCode = ua.StatusCode(ua.StatusCodes.BadNodeIdExists) result.StatusCode = ua.StatusCode(ua.StatusCodes.BadNodeIdExists)
return result return result
nodedata = NodeData(item.RequestedNewNodeId) 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.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.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))) 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) self._add_nodeattributes(item.NodeAttributes, nodedata)
#add parent # add parent
if item.ParentNodeId == ua.NodeId(): 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 pass
elif not item.ParentNodeId in self._aspace.nodes: 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) result.StatusCode = ua.StatusCode(ua.StatusCodes.BadParentNodeIdInvalid)
return result return result
else: else:
...@@ -192,11 +200,11 @@ class NodeManagementService(object): ...@@ -192,11 +200,11 @@ class NodeManagementService(object):
desc.TypeDefinition = item.TypeDefinition desc.TypeDefinition = item.TypeDefinition
desc.IsForward = True desc.IsForward = True
self._aspace.nodes[item.ParentNodeId].references.append(desc) 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 self._aspace.nodes[item.RequestedNewNodeId] = nodedata
#add type definition # add type definition
if item.TypeDefinition != ua.NodeId(): if item.TypeDefinition != ua.NodeId():
addref = ua.AddReferencesItem() addref = ua.AddReferencesItem()
addref.SourceNodeId = item.RequestedNewNodeId addref.SourceNodeId = item.RequestedNewNodeId
...@@ -230,10 +238,10 @@ class NodeManagementService(object): ...@@ -230,10 +238,10 @@ class NodeManagementService(object):
rdesc.NodeClass = addref.TargetNodeClass rdesc.NodeClass = addref.TargetNodeClass
bname = self._aspace.get_attribute_value(addref.TargetNodeId, ua.AttributeIds.BrowseName).Value.Value bname = self._aspace.get_attribute_value(addref.TargetNodeId, ua.AttributeIds.BrowseName).Value.Value
if bname: if bname:
rdesc.BrowseName = bname rdesc.BrowseName = bname
dname = self._aspace.get_attribute_value(addref.TargetNodeId, ua.AttributeIds.DisplayName).Value.Value dname = self._aspace.get_attribute_value(addref.TargetNodeId, ua.AttributeIds.DisplayName).Value.Value
if dname: if dname:
rdesc.DisplayName = dname rdesc.DisplayName = dname
self._aspace.nodes[addref.SourceNodeId].references.append(rdesc) self._aspace.nodes[addref.SourceNodeId].references.append(rdesc)
return ua.StatusCode() return ua.StatusCode()
...@@ -241,7 +249,7 @@ class NodeManagementService(object): ...@@ -241,7 +249,7 @@ class NodeManagementService(object):
if item.SpecifiedAttributes & getattr(ua.NodeAttributesMask, name): if item.SpecifiedAttributes & getattr(ua.NodeAttributesMask, name):
dv = ua.DataValue(ua.Variant(getattr(item, name), vtype)) dv = ua.DataValue(ua.Variant(getattr(item, name), vtype))
nodedata.attributes[getattr(ua.AttributeIds, name)] = AttributeValue(dv) nodedata.attributes[getattr(ua.AttributeIds, name)] = AttributeValue(dv)
def _add_nodeattributes(self, item, nodedata): def _add_nodeattributes(self, item, nodedata):
item = ua.downcast_extobject(item) item = ua.downcast_extobject(item)
self._add_node_attr(item, nodedata, "AccessLevel", ua.VariantType.Byte) self._add_node_attr(item, nodedata, "AccessLevel", ua.VariantType.Byte)
...@@ -270,6 +278,7 @@ class NodeManagementService(object): ...@@ -270,6 +278,7 @@ class NodeManagementService(object):
class MethodService(object): class MethodService(object):
def __init__(self, aspace): def __init__(self, aspace):
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self._aspace = aspace self._aspace = aspace
...@@ -281,9 +290,9 @@ class MethodService(object): ...@@ -281,9 +290,9 @@ class MethodService(object):
results.append(self._call(method)) results.append(self._call(method))
return results return results
def _call(self, method): def _call(self, method):
res = ua.CallMethodResult() 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) res.StatusCode = ua.StatusCode(ua.StatusCodes.BadNodeIdInvalid)
else: else:
node = self._aspace.nodes[method.MethodId] node = self._aspace.nodes[method.MethodId]
...@@ -298,19 +307,21 @@ class MethodService(object): ...@@ -298,19 +307,21 @@ class MethodService(object):
self.logger.exception("Error executing method call %s, an exception was raised: ", method) self.logger.exception("Error executing method call %s, an exception was raised: ", method)
res.StatusCode = ua.StatusCode(ua.StatusCodes.BadUnexpectedError) res.StatusCode = ua.StatusCode(ua.StatusCodes.BadUnexpectedError)
return res return res
class AddressSpace(object): class AddressSpace(object):
""" """
The address space object stores all the nodes og the OPC-UA server The address space object stores all the nodes og the OPC-UA server
and helper methods. and helper methods.
It is NOT threadsafe, lock the lock object before reading of modifying It is NOT threadsafe, lock the lock object before reading of modifying
a node a node
""" """
def __init__(self): def __init__(self):
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.nodes = {} 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._datachange_callback_counter = 200
self._handle_to_attribute_map = {} self._handle_to_attribute_map = {}
...@@ -386,13 +397,3 @@ class AddressSpace(object): ...@@ -386,13 +397,3 @@ class AddressSpace(object):
with self.lock: with self.lock:
node = self.nodes[methodid] node = self.nodes[methodid]
node.call = callback node.call = callback
...@@ -12,6 +12,7 @@ import opcua.utils as utils ...@@ -12,6 +12,7 @@ import opcua.utils as utils
class BinaryClient(object): class BinaryClient(object):
""" """
low level OPC-UA client. low level OPC-UA client.
implement all(well..one day) methods defined in opcua spec implement all(well..one day) methods defined in opcua spec
...@@ -19,11 +20,12 @@ class BinaryClient(object): ...@@ -19,11 +20,12 @@ class BinaryClient(object):
in python most of the structures are defined in in python most of the structures are defined in
uaprotocol_auto.py and uaprotocol_hand.py uaprotocol_auto.py and uaprotocol_hand.py
""" """
def __init__(self): def __init__(self):
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self._socket = None self._socket = None
self._do_stop = False self._do_stop = False
self._security_token = ua.ChannelSecurityToken() self._security_token = ua.ChannelSecurityToken()
self._authentication_token = ua.NodeId() self._authentication_token = ua.NodeId()
self._sequence_number = 0 self._sequence_number = 0
self._request_id = 0 self._request_id = 0
...@@ -43,9 +45,9 @@ class BinaryClient(object): ...@@ -43,9 +45,9 @@ class BinaryClient(object):
self._thread.start() self._thread.start()
def _send_request(self, request, callback=None, timeout=1000): 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() request.to_binary()
#END HACK # END HACK
with self._lock: with self._lock:
request.RequestHeader = self._create_request_header(timeout) request.RequestHeader = self._create_request_header(timeout)
hdr = ua.Header(ua.MessageType.SecureMessage, ua.ChunkType.Single, self._security_token.ChannelId) hdr = ua.Header(ua.MessageType.SecureMessage, ua.ChunkType.Single, self._security_token.ChannelId)
...@@ -62,7 +64,7 @@ class BinaryClient(object): ...@@ -62,7 +64,7 @@ class BinaryClient(object):
return data return data
def _check_answer(self, data, context): 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) typeid = ua.NodeId.from_binary(data)
if typeid == ua.FourByteNodeId(ua.ObjectIds.ServiceFault_Encoding_DefaultBinary): if typeid == ua.FourByteNodeId(ua.ObjectIds.ServiceFault_Encoding_DefaultBinary):
self.logger.warning("ServiceFault from server received %s", context) self.logger.warning("ServiceFault from server received %s", context)
...@@ -159,14 +161,13 @@ class BinaryClient(object): ...@@ -159,14 +161,13 @@ class BinaryClient(object):
hdr.RequestId = self._request_id hdr.RequestId = self._request_id
return hdr return hdr
def connect_socket(self, host, port): def connect_socket(self, host, port):
""" """
connect to server socket and start receiving thread connect to server socket and start receiving thread
""" """
self.logger.info("opening connection") self.logger.info("opening connection")
self._socket = socket.create_connection((host, port)) 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() self.start()
def disconnect_socket(self): def disconnect_socket(self):
...@@ -183,7 +184,7 @@ class BinaryClient(object): ...@@ -183,7 +184,7 @@ class BinaryClient(object):
self._callbackmap[0] = future self._callbackmap[0] = future
self._write_socket(header, hello) self._write_socket(header, hello)
return ua.Acknowledge.from_binary(future.result()) return ua.Acknowledge.from_binary(future.result())
def open_secure_channel(self, params): def open_secure_channel(self, params):
self.logger.info("open_secure_channel") self.logger.info("open_secure_channel")
request = ua.OpenSecureChannelRequest() request = ua.OpenSecureChannelRequest()
...@@ -229,7 +230,7 @@ class BinaryClient(object): ...@@ -229,7 +230,7 @@ class BinaryClient(object):
request.DeleteSubscriptions = deletesubscriptions request.DeleteSubscriptions = deletesubscriptions
data = self._send_request(request) data = self._send_request(request)
ua.CloseSessionResponse.from_binary(data) 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): def browse(self, parameters):
self.logger.info("browse") self.logger.info("browse")
...@@ -281,7 +282,7 @@ class BinaryClient(object): ...@@ -281,7 +282,7 @@ class BinaryClient(object):
seqhdr = self._create_sequence_header() seqhdr = self._create_sequence_header()
self._write_socket(hdr, symhdr, seqhdr, request) 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): def translate_browsepaths_to_nodeids(self, browsepaths):
self.logger.info("translate_browsepath_to_nodeid") self.logger.info("translate_browsepath_to_nodeid")
...@@ -315,7 +316,7 @@ class BinaryClient(object): ...@@ -315,7 +316,7 @@ class BinaryClient(object):
def publish(self, acks=None): def publish(self, acks=None):
self.logger.info("publish") self.logger.info("publish")
if acks is None: if acks is None:
acks = [] acks = []
request = ua.PublishRequest() request = ua.PublishRequest()
request.Parameters.SubscriptionAcknowledgements = acks request.Parameters.SubscriptionAcknowledgements = acks
...@@ -326,10 +327,9 @@ class BinaryClient(object): ...@@ -326,10 +327,9 @@ class BinaryClient(object):
response = ua.PublishResponse.from_binary(future.result()) response = ua.PublishResponse.from_binary(future.result())
try: try:
self._publishcallbacks[response.Parameters.SubscriptionId](response.Parameters) 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") self.logger.exception("Exception while calling user callback")
def create_monitored_items(self, params): def create_monitored_items(self, params):
self.logger.info("create_monitored_items") self.logger.info("create_monitored_items")
request = ua.CreateMonitoredItemsRequest() request = ua.CreateMonitoredItemsRequest()
...@@ -356,14 +356,11 @@ class BinaryClient(object): ...@@ -356,14 +356,11 @@ class BinaryClient(object):
response = ua.AddNodesResponse.from_binary(data) response = ua.AddNodesResponse.from_binary(data)
response.ResponseHeader.ServiceResult.check() response.ResponseHeader.ServiceResult.check()
return response.Results return response.Results
def call(self, methodstocall): def call(self, methodstocall):
request = ua.CallRequest() request = ua.CallRequest()
request.Parameters.MethodsToCall = methodstocall request.Parameters.MethodsToCall = methodstocall
data = self._send_request(request) data = self._send_request(request)
response = ua.CallResponse.from_binary(data) response = ua.CallResponse.from_binary(data)
response.ResponseHeader.ServiceResult.check() response.ResponseHeader.ServiceResult.check()
return response.Results return response.Results
...@@ -14,10 +14,13 @@ from opcua.uaprocessor import UAProcessor ...@@ -14,10 +14,13 @@ from opcua.uaprocessor import UAProcessor
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class BinaryServer(Thread): class BinaryServer(Thread):
""" """
Socket server forwarding request to internal server Socket server forwarding request to internal server
""" """
def __init__(self, internal_server, hostname, port): def __init__(self, internal_server, hostname, port):
Thread.__init__(self) Thread.__init__(self)
self.socket_server = None self.socket_server = None
...@@ -33,10 +36,10 @@ class BinaryServer(Thread): ...@@ -33,10 +36,10 @@ class BinaryServer(Thread):
def run(self): def run(self):
logger.warning("Listening on %s:%s", self.hostname, self.port) 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 = 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.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.internal_server = self.iserver # allow handler to acces server properties
with self._cond: with self._cond:
self._cond.notify_all() self._cond.notify_all()
self.socket_server.serve_forever() self.socket_server.serve_forever()
...@@ -47,6 +50,7 @@ class BinaryServer(Thread): ...@@ -47,6 +50,7 @@ class BinaryServer(Thread):
class UAHandler(socketserver.BaseRequestHandler): class UAHandler(socketserver.BaseRequestHandler):
""" """
The RequestHandler class for our server. The RequestHandler class for our server.
...@@ -65,5 +69,3 @@ class UAHandler(socketserver.BaseRequestHandler): ...@@ -65,5 +69,3 @@ class UAHandler(socketserver.BaseRequestHandler):
class ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): class ThreadingTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass pass
from __future__ import division #support for python2 from __future__ import division # support for python2
from threading import Thread, Condition from threading import Thread, Condition
import logging import logging
try: try:
from urllib.parse import urlparse from urllib.parse import urlparse
except ImportError: #support for python2 except ImportError: # support for python2
from urlparse import urlparse from urlparse import urlparse
from opcua import uaprotocol as ua from opcua import uaprotocol as ua
...@@ -12,14 +12,16 @@ from opcua import utils ...@@ -12,14 +12,16 @@ from opcua import utils
class KeepAlive(Thread): class KeepAlive(Thread):
""" """
Used by Client to keep session opened. Used by Client to keep session opened.
OPCUA defines timeout both for sessions and secure channel OPCUA defines timeout both for sessions and secure channel
""" """
def __init__(self, client, timeout): def __init__(self, client, timeout):
Thread.__init__(self) Thread.__init__(self)
self.logger = logging.getLogger(__name__) 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 timeout = 360000
self.timeout = timeout self.timeout = timeout
self.client = client self.client = client
...@@ -31,7 +33,7 @@ class KeepAlive(Thread): ...@@ -31,7 +33,7 @@ class KeepAlive(Thread):
server_state = self.client.get_node(ua.FourByteNodeId(ua.ObjectIds.Server_ServerStatus_State)) server_state = self.client.get_node(ua.FourByteNodeId(ua.ObjectIds.Server_ServerStatus_State))
while not self._dostop: while not self._dostop:
with self._cond: with self._cond:
self._cond.wait(self.timeout/1000) self._cond.wait(self.timeout / 1000)
if self._dostop: if self._dostop:
break break
self.logger.debug("renewing channel") self.logger.debug("renewing channel")
...@@ -48,8 +50,9 @@ class KeepAlive(Thread): ...@@ -48,8 +50,9 @@ class KeepAlive(Thread):
class Client(object): 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. This class makes it easy to connect and browse address space.
It attemps to expose as much functionality as possible It attemps to expose as much functionality as possible
but if you want to do to special things you will probably need but if you want to do to special things you will probably need
...@@ -57,6 +60,7 @@ class Client(object): ...@@ -57,6 +60,7 @@ class Client(object):
which offers a raw OPC-UA interface. which offers a raw OPC-UA interface.
""" """
def __init__(self, url): def __init__(self, url):
""" """
used url argument to connect to server. used url argument to connect to server.
...@@ -65,8 +69,8 @@ class Client(object): ...@@ -65,8 +69,8 @@ class Client(object):
""" """
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.server_url = urlparse(url) self.server_url = urlparse(url)
self.name = "Pure Python Client" self.name = "Pure Python Client"
self.description = self.name self.description = self.name
self.application_uri = "urn:freeopcua:client" self.application_uri = "urn:freeopcua:client"
self.product_uri = "urn:freeopcua.github.no:client" self.product_uri = "urn:freeopcua.github.no:client"
self.security_policy_uri = "http://opcfoundation.org/UA/SecurityPolicy#None" self.security_policy_uri = "http://opcfoundation.org/UA/SecurityPolicy#None"
...@@ -124,7 +128,7 @@ class Client(object): ...@@ -124,7 +128,7 @@ class Client(object):
Send OPC-UA hello to server Send OPC-UA hello to server
""" """
ack = self.bclient.send_hello(self.server_url.geturl()) ack = self.bclient.send_hello(self.server_url.geturl())
#FIXME check ack # FIXME check ack
def open_secure_channel(self, renew=False): def open_secure_channel(self, renew=False):
""" """
...@@ -161,14 +165,14 @@ class Client(object): ...@@ -161,14 +165,14 @@ class Client(object):
params = ua.CreateSessionParameters() params = ua.CreateSessionParameters()
params.ClientNonce = utils.create_nonce() params.ClientNonce = utils.create_nonce()
params.ClientCertificate = b'' params.ClientCertificate = b''
params.ClientDescription = desc params.ClientDescription = desc
params.EndpointUrl = self.server_url.geturl() params.EndpointUrl = self.server_url.geturl()
params.SessionName = self.description + " Session" + str(self._session_counter) params.SessionName = self.description + " Session" + str(self._session_counter)
params.RequestedSessionTimeout = 3600000 params.RequestedSessionTimeout = 3600000
params.MaxResponseMessageSize = 0 #means not max size params.MaxResponseMessageSize = 0 # means not max size
response = self.bclient.create_session(params) response = self.bclient.create_session(params)
self.session_timeout = response.RevisedSessionTimeout 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() self.keepalive.start()
return response return response
...@@ -224,8 +228,3 @@ class Client(object): ...@@ -224,8 +228,3 @@ class Client(object):
def get_namespace_index(self, uri): def get_namespace_index(self, uri):
uries = self.get_namespace_array() uries = self.get_namespace_array()
return uries.index(uri) return uries.index(uri)
...@@ -9,6 +9,7 @@ import uuid ...@@ -9,6 +9,7 @@ import uuid
class Event(object): class Event(object):
""" """
Create an event based on an event type. Per default is BaseEventType used. Create an event based on an event type. Per default is BaseEventType used.
arguments are: arguments are:
...@@ -16,6 +17,7 @@ class Event(object): ...@@ -16,6 +17,7 @@ class Event(object):
source: The emiting source for the node, either an objectId, NodeId or a Node 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 etype: The event type, either an objectId, a NodeId or a Node object
""" """
def __init__(self, isession, etype=ObjectIds.BaseEventType, source=ObjectIds.Server): def __init__(self, isession, etype=ObjectIds.BaseEventType, source=ObjectIds.Server):
self.isession = isession self.isession = isession
...@@ -25,16 +27,16 @@ class Event(object): ...@@ -25,16 +27,16 @@ class Event(object):
self.node = Node(self.isession, etype) self.node = Node(self.isession, etype)
else: else:
self.node = Node(self.isession, ua.NodeId(etype)) self.node = Node(self.isession, ua.NodeId(etype))
self.set_members_from_node(self.node) self.set_members_from_node(self.node)
if isinstance(source, Node): if isinstance(source, Node):
self.SourceNode = source.NodeId self.SourceNode = source.NodeId
elif isinstance(etype, ua.NodeId): elif isinstance(etype, ua.NodeId):
self.SourceNode = source.NodeId self.SourceNode = source.NodeId
else: else:
self.SourceNode = ua.NodeId(source) 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.EventId = uuid.uuid4().bytes
self.EventType = self.node.nodeid self.EventType = self.node.nodeid
self.LocaleTime = datetime.now() self.LocaleTime = datetime.now()
...@@ -44,7 +46,7 @@ class Event(object): ...@@ -44,7 +46,7 @@ class Event(object):
self.Severity = ua.Variant(1, ua.VariantType.UInt16) self.Severity = ua.Variant(1, ua.VariantType.UInt16)
self.SourceName = "Server" 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.BrowseName = self.node.get_browse_name()
self.DisplayName = self.node.get_display_name() self.DisplayName = self.node.get_display_name()
self.NodeId = self.node.nodeid self.NodeId = self.node.nodeid
...@@ -63,5 +65,3 @@ class Event(object): ...@@ -63,5 +65,3 @@ class Event(object):
for desc in references: for desc in references:
node = Node(self.isession, desc.NodeId) node = Node(self.isession, desc.NodeId)
setattr(self, desc.BrowseName.Name, node.get_value()) setattr(self, desc.BrowseName.Name, node.get_value())
...@@ -10,7 +10,7 @@ from threading import Thread ...@@ -10,7 +10,7 @@ from threading import Thread
from enum import Enum from enum import Enum
import functools import functools
try: 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 import asyncio
except ImportError: except ImportError:
import trollius as asyncio import trollius as asyncio
...@@ -30,6 +30,7 @@ from opcua import standard_address_space ...@@ -30,6 +30,7 @@ from opcua import standard_address_space
class ThreadLoop(Thread): class ThreadLoop(Thread):
def __init__(self): def __init__(self):
Thread.__init__(self) Thread.__init__(self)
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
...@@ -69,7 +70,9 @@ class SessionState(Enum): ...@@ -69,7 +70,9 @@ class SessionState(Enum):
Activated = 1 Activated = 1
Closed = 2 Closed = 2
class InternalServer(object): class InternalServer(object):
def __init__(self): def __init__(self):
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.endpoints = [] self.endpoints = []
...@@ -81,13 +84,13 @@ class InternalServer(object): ...@@ -81,13 +84,13 @@ class InternalServer(object):
self.method_service = MethodService(self.aspace) self.method_service = MethodService(self.aspace)
self.node_mgt_service = NodeManagementService(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(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.loop = ThreadLoop()
self.subcsription_service = SubscriptionService(self.loop, self.aspace) self.subcsription_service = SubscriptionService(self.loop, self.aspace)
# create a session to use on server side # 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._timer = None
self.current_time_node = Node(self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_CurrentTime)) self.current_time_node = Node(self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_CurrentTime))
uries = ["http://opcfoundation.org/UA/"] uries = ["http://opcfoundation.org/UA/"]
...@@ -100,7 +103,7 @@ class InternalServer(object): ...@@ -100,7 +103,7 @@ class InternalServer(object):
def dump_address_space(self, path): def dump_address_space(self, path):
self.aspace.dump(path) self.aspace.dump(path)
def start(self): def start(self):
self.logger.info("starting internal server") self.logger.info("starting internal server")
self.loop.start() self.loop.start()
Node(self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_State)).set_value(0) Node(self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_State)).set_value(0)
...@@ -124,15 +127,17 @@ class InternalServer(object): ...@@ -124,15 +127,17 @@ class InternalServer(object):
def get_endpoints(self, params=None): def get_endpoints(self, params=None):
self.logger.info("get endpoint") self.logger.info("get endpoint")
#FIXME check params # FIXME check params
return self.endpoints[:] return self.endpoints[:]
def create_session(self, name): def create_session(self, name):
return InternalSession(self, self.aspace, self.subcsription_service, name) return InternalSession(self, self.aspace, self.subcsription_service, name)
class InternalSession(object): class InternalSession(object):
_counter = 10 _counter = 10
_auth_counter = 1000 _auth_counter = 1000
def __init__(self, internal_server, aspace, submgr, name): def __init__(self, internal_server, aspace, submgr, name):
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.iserver = internal_server self.iserver = internal_server
...@@ -144,14 +149,14 @@ class InternalSession(object): ...@@ -144,14 +149,14 @@ class InternalSession(object):
InternalSession._counter += 1 InternalSession._counter += 1
self.authentication_token = ua.NodeId(self._auth_counter) self.authentication_token = ua.NodeId(self._auth_counter)
InternalSession._auth_counter += 1 InternalSession._auth_counter += 1
self.nonce = utils.create_nonce() self.nonce = utils.create_nonce()
self.subscriptions = [] self.subscriptions = []
self.logger.warning("Created internal session %s", self.name) self.logger.warning("Created internal session %s", self.name)
self._lock = Lock() self._lock = Lock()
def __str__(self): def __str__(self):
return "InternalSession(name:{}, id:{}, auth_token:{})".format(self.name, self.session_id, self.authentication_token) return "InternalSession(name:{}, id:{}, auth_token:{})".format(self.name, self.session_id, self.authentication_token)
def get_endpoints(self, params=None): def get_endpoints(self, params=None):
return self.iserver.get_endpoints(params) return self.iserver.get_endpoints(params)
...@@ -160,7 +165,7 @@ class InternalSession(object): ...@@ -160,7 +165,7 @@ class InternalSession(object):
result = ua.CreateSessionResult() result = ua.CreateSessionResult()
result.SessionId = self.session_id result.SessionId = self.session_id
result.AuthenticationToken = self.authentication_token result.AuthenticationToken = self.authentication_token
result.RevisedSessionTimeout = params.RequestedSessionTimeout result.RevisedSessionTimeout = params.RequestedSessionTimeout
result.MaxRequestMessageSize = 65536 result.MaxRequestMessageSize = 65536
result.ServerNonce = self.nonce result.ServerNonce = self.nonce
...@@ -230,10 +235,8 @@ class InternalSession(object): ...@@ -230,10 +235,8 @@ class InternalSession(object):
def delete_monitored_items(self, params): def delete_monitored_items(self, params):
return self.subscription_service.delete_monitored_items(params) return self.subscription_service.delete_monitored_items(params)
def publish(self, acks=None): def publish(self, acks=None):
if acks is None: if acks is None:
acks = [] acks = []
return self.subscription_service.publish(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 and browse address space
""" """
import opcua.uaprotocol as ua import opcua.uaprotocol as ua
class Node(object): class Node(object):
""" """
High level node object, to access node attribute, High level node object, to access node attribute,
browse and populate address space browse and populate address space
""" """
def __init__(self, server, nodeid): def __init__(self, server, nodeid):
self.server = server self.server = server
self.nodeid = None self.nodeid = None
...@@ -21,6 +24,7 @@ class Node(object): ...@@ -21,6 +24,7 @@ class Node(object):
self.nodeid = ua.NodeId(nodeid, 0) self.nodeid = ua.NodeId(nodeid, 0)
else: else:
raise Exception("argument to node must be a NodeId object or a string defining a nodeid found {} of type {}".format(nodeid, type(nodeid))) 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): def __eq__(self, other):
if isinstance(other, Node) and self.nodeid == other.nodeid: if isinstance(other, Node) and self.nodeid == other.nodeid:
return True return True
...@@ -61,7 +65,7 @@ class Node(object): ...@@ -61,7 +65,7 @@ class Node(object):
def get_value(self): 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. An exception will be generated for other node types.
""" """
result = self.get_attribute(ua.AttributeIds.Value) result = self.get_attribute(ua.AttributeIds.Value)
...@@ -69,11 +73,11 @@ class Node(object): ...@@ -69,11 +73,11 @@ class Node(object):
def set_value(self, value, varianttype=None): 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. An exception will be generated for other node types.
""" """
variant = None variant = None
if type(value) == ua.Variant: if isinstance(value, ua.Variant):
variant = value variant = value
else: else:
variant = ua.Variant(value, varianttype) variant = ua.Variant(value, varianttype)
...@@ -148,7 +152,7 @@ class Node(object): ...@@ -148,7 +152,7 @@ class Node(object):
desc.IncludeSubtypes = includesubtypes desc.IncludeSubtypes = includesubtypes
desc.NodeClassMask = nodeclassmask desc.NodeClassMask = nodeclassmask
desc.ResultMask = ua.BrowseResultMask.All desc.ResultMask = ua.BrowseResultMask.All
desc.NodeId = self.nodeid desc.NodeId = self.nodeid
params = ua.BrowseParameters() params = ua.BrowseParameters()
params.NodesToBrowse.append(desc) params.NodesToBrowse.append(desc)
...@@ -172,7 +176,7 @@ class Node(object): ...@@ -172,7 +176,7 @@ class Node(object):
el.ReferenceTypeId = ua.TwoByteNodeId(ua.ObjectIds.HierarchicalReferences) el.ReferenceTypeId = ua.TwoByteNodeId(ua.ObjectIds.HierarchicalReferences)
el.IsInverse = False el.IsInverse = False
el.IncludeSubtypes = True el.IncludeSubtypes = True
if type(item) == ua.QualifiedName: if isinstance(item, ua.QualifiedName):
el.TargetName = item el.TargetName = item
else: else:
el.TargetName = ua.QualifiedName.from_string(item) el.TargetName = ua.QualifiedName.from_string(item)
...@@ -183,7 +187,7 @@ class Node(object): ...@@ -183,7 +187,7 @@ class Node(object):
result = self.server.translate_browsepaths_to_nodeids([bpath]) result = self.server.translate_browsepaths_to_nodeids([bpath])
result = result[0] result = result[0]
result.StatusCode.check() 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) return Node(self.server, result.Targets[0].TargetId)
def add_folder(self, *args): def add_folder(self, *args):
...@@ -194,11 +198,11 @@ class Node(object): ...@@ -194,11 +198,11 @@ class Node(object):
""" """
nodeid, qname = self._parse_add_args(*args) nodeid, qname = self._parse_add_args(*args)
return self._add_folder(nodeid, qname) return self._add_folder(nodeid, qname)
def _add_folder(self, nodeid, qname): def _add_folder(self, nodeid, qname):
node = ua.AddNodesItem() node = ua.AddNodesItem()
node.RequestedNewNodeId = nodeid node.RequestedNewNodeId = nodeid
node.BrowseName = qname node.BrowseName = qname
node.NodeClass = ua.NodeClass.Object node.NodeClass = ua.NodeClass.Object
node.ParentNodeId = self.nodeid node.ParentNodeId = self.nodeid
node.ReferenceTypeId = ua.NodeId.from_string("i=35") node.ReferenceTypeId = ua.NodeId.from_string("i=35")
...@@ -211,7 +215,7 @@ class Node(object): ...@@ -211,7 +215,7 @@ class Node(object):
results = self.server.add_nodes([node]) results = self.server.add_nodes([node])
results[0].StatusCode.check() results[0].StatusCode.check()
return Node(self.server, nodeid) return Node(self.server, nodeid)
def add_object(self, *args): def add_object(self, *args):
""" """
create a child node object create a child node object
...@@ -220,13 +224,13 @@ class Node(object): ...@@ -220,13 +224,13 @@ class Node(object):
""" """
nodeid, qname = self._parse_add_args(*args) nodeid, qname = self._parse_add_args(*args)
return self._add_object(nodeid, qname) return self._add_object(nodeid, qname)
def _add_object(self, nodeid, qname): def _add_object(self, nodeid, qname):
node = ua.AddNodesItem() node = ua.AddNodesItem()
node.RequestedNewNodeId = nodeid node.RequestedNewNodeId = nodeid
node.BrowseName = qname node.BrowseName = qname
node.NodeClass = ua.NodeClass.Object node.NodeClass = ua.NodeClass.Object
node.ParentNodeId = self.nodeid node.ParentNodeId = self.nodeid
node.ReferenceTypeId = ua.NodeId.from_string("i=35") node.ReferenceTypeId = ua.NodeId.from_string("i=35")
node.TypeDefinition = ua.NodeId(ua.ObjectIds.BaseObjectType) node.TypeDefinition = ua.NodeId(ua.ObjectIds.BaseObjectType)
attrs = ua.ObjectAttributes() attrs = ua.ObjectAttributes()
...@@ -247,7 +251,7 @@ class Node(object): ...@@ -247,7 +251,7 @@ class Node(object):
nodeid, qname = self._parse_add_args(*args[:2]) nodeid, qname = self._parse_add_args(*args[:2])
val = self._to_variant(*args[2:]) val = self._to_variant(*args[2:])
return self._add_variable(nodeid, qname, val, isproperty=True) return self._add_variable(nodeid, qname, val, isproperty=True)
def add_variable(self, *args): def add_variable(self, *args):
""" """
create a child node variable create a child node variable
...@@ -266,10 +270,10 @@ class Node(object): ...@@ -266,10 +270,10 @@ class Node(object):
def _add_variable(self, nodeid, qname, val, isproperty=False): def _add_variable(self, nodeid, qname, val, isproperty=False):
node = ua.AddNodesItem() node = ua.AddNodesItem()
node.RequestedNewNodeId = nodeid node.RequestedNewNodeId = nodeid
node.BrowseName = qname node.BrowseName = qname
node.NodeClass = ua.NodeClass.Variable node.NodeClass = ua.NodeClass.Variable
node.ParentNodeId = self.nodeid node.ParentNodeId = self.nodeid
if isproperty: if isproperty:
node.ReferenceTypeId = ua.NodeId(ua.ObjectIds.HasProperty) node.ReferenceTypeId = ua.NodeId(ua.ObjectIds.HasProperty)
node.TypeDefinition = ua.NodeId(ua.ObjectIds.PropertyType) node.TypeDefinition = ua.NodeId(ua.ObjectIds.PropertyType)
...@@ -281,7 +285,7 @@ class Node(object): ...@@ -281,7 +285,7 @@ class Node(object):
attrs.DisplayName = ua.LocalizedText(qname.Name) attrs.DisplayName = ua.LocalizedText(qname.Name)
attrs.DataType = self._guess_uatype(val) attrs.DataType = self._guess_uatype(val)
attrs.Value = val attrs.Value = val
attrs.ValueRank = 0 attrs.ValueRank = 0
attrs.WriteMask = 0 attrs.WriteMask = 0
attrs.UserWriteMask = 0 attrs.UserWriteMask = 0
attrs.Historizing = 0 attrs.Historizing = 0
...@@ -302,17 +306,17 @@ class Node(object): ...@@ -302,17 +306,17 @@ class Node(object):
nodeid, qname = self._parse_add_args(*args[:2]) nodeid, qname = self._parse_add_args(*args[:2])
callback = args[2] callback = args[2]
if len(args) > 3: if len(args) > 3:
inputs = args[3] inputs = args[3]
if len(args) > 4: if len(args) > 4:
outputs = args[4] outputs = args[4]
return self._add_method(nodeid, qname, callback, inputs, outputs) return self._add_method(nodeid, qname, callback, inputs, outputs)
def _add_method(self, nodeid, qname, callback, inputs, outputs): def _add_method(self, nodeid, qname, callback, inputs, outputs):
node = ua.AddNodesItem() node = ua.AddNodesItem()
node.RequestedNewNodeId = nodeid node.RequestedNewNodeId = nodeid
node.BrowseName = qname node.BrowseName = qname
node.NodeClass = ua.NodeClass.Method node.NodeClass = ua.NodeClass.Method
node.ParentNodeId = self.nodeid node.ParentNodeId = self.nodeid
node.ReferenceTypeId = ua.NodeId.from_string("i=47") node.ReferenceTypeId = ua.NodeId.from_string("i=47")
#node.TypeDefinition = ua.NodeId(ua.ObjectIds.BaseObjectType) #node.TypeDefinition = ua.NodeId(ua.ObjectIds.BaseObjectType)
attrs = ua.MethodAttributes() attrs = ua.MethodAttributes()
...@@ -332,19 +336,18 @@ class Node(object): ...@@ -332,19 +336,18 @@ class Node(object):
method.add_property(qname.NamespaceIndex, "OutputArguments", [self._vtype_to_argument(vtype) for vtype in outputs]) method.add_property(qname.NamespaceIndex, "OutputArguments", [self._vtype_to_argument(vtype) for vtype in outputs])
self.server.add_method_callback(method.nodeid, callback) self.server.add_method_callback(method.nodeid, callback)
return method return method
def call_method(self, methodid, *args): def call_method(self, methodid, *args):
""" """
Call an OPC-UA method. methodid is browse name of child method or the Call an OPC-UA method. methodid is browse name of child method or the
nodeid of method as a NodeId object nodeid of method as a NodeId object
arguments are variants or python object convertible to variants. arguments are variants or python object convertible to variants.
which may be of different types 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 methodid = self.get_child(methodid).nodeid
elif type(methodid) is Node: elif isinstance(methodid, Node):
methodid = methodid.nodeid methodid = methodid.nodeid
arguments = [] arguments = []
...@@ -391,21 +394,20 @@ class Node(object): ...@@ -391,21 +394,20 @@ class Node(object):
return ua.NodeId(getattr(ua.ObjectIds, variant.VariantType.name)) return ua.NodeId(getattr(ua.ObjectIds, variant.VariantType.name))
def _parse_add_args(self, *args): def _parse_add_args(self, *args):
if type(args[0]) is ua.NodeId: if isinstance(args[0], ua.NodeId):
return args[0], args[1] 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]) 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]) return generate_nodeid(args[0]), ua.QualifiedName(args[1], args[0])
else: else:
raise TypeError("Add methods takes a nodeid and a qualifiedname as argument, received %s" % args) raise TypeError("Add methods takes a nodeid and a qualifiedname as argument, received %s" % args)
__nodeid_counter = 2000 __nodeid_counter = 2000
def generate_nodeid(idx): def generate_nodeid(idx):
global __nodeid_counter global __nodeid_counter
__nodeid_counter += 1 __nodeid_counter += 1
return ua.NodeId(__nodeid_counter, idx) return ua.NodeId(__nodeid_counter, idx)
...@@ -16,6 +16,7 @@ from opcua import Node, Subscription, ObjectIds, Event ...@@ -16,6 +16,7 @@ from opcua import Node, Subscription, ObjectIds, Event
class Server(object): class Server(object):
def __init__(self): def __init__(self):
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.endpoint = "opc.tcp://localhost:4841/freeopcua/server/" self.endpoint = "opc.tcp://localhost:4841/freeopcua/server/"
...@@ -26,7 +27,7 @@ class Server(object): ...@@ -26,7 +27,7 @@ class Server(object):
self.iserver = InternalServer() self.iserver = InternalServer()
self.bserver = None self.bserver = None
#setup some expected values # setup some expected values
self.register_namespace(self.server_uri) self.register_namespace(self.server_uri)
sa_node = self.get_node(ua.NodeId(ua.ObjectIds.Server_ServerArray)) sa_node = self.get_node(ua.NodeId(ua.ObjectIds.Server_ServerArray))
sa_node.set_value([self.server_uri]) sa_node.set_value([self.server_uri])
...@@ -38,7 +39,7 @@ class Server(object): ...@@ -38,7 +39,7 @@ class Server(object):
return self.iserver.get_endpoints() return self.iserver.get_endpoints()
def _setup_server_nodes(self): 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() self._set_endpoints()
def _set_endpoints(self): def _set_endpoints(self):
...@@ -48,9 +49,9 @@ class Server(object): ...@@ -48,9 +49,9 @@ class Server(object):
appdesc = ua.ApplicationDescription() appdesc = ua.ApplicationDescription()
appdesc.ApplicationName = ua.LocalizedText(self.name) appdesc.ApplicationName = ua.LocalizedText(self.name)
appdesc.ApplicationUri = self.server_uri appdesc.ApplicationUri = self.server_uri
appdesc.ApplicationType = ua.ApplicationType.Server appdesc.ApplicationType = ua.ApplicationType.Server
appdesc.ProductUri = self.product_uri appdesc.ProductUri = self.product_uri
appdesc.DiscoveryUrls.append(self.endpoint.geturl()) appdesc.DiscoveryUrls.append(self.endpoint.geturl())
edp = ua.EndpointDescription() edp = ua.EndpointDescription()
...@@ -116,7 +117,7 @@ class Server(object): ...@@ -116,7 +117,7 @@ class Server(object):
uries = ns_node.get_value() uries = ns_node.get_value()
uries.append(uri) uries.append(uri)
ns_node.set_value(uries) ns_node.set_value(uries)
return (len(uries)-1) return (len(uries) - 1)
def get_namespace_index(self, uri): def get_namespace_index(self, uri):
uries = self.get_namespace_array() uries = self.get_namespace_array()
...@@ -124,8 +125,3 @@ class Server(object): ...@@ -124,8 +125,3 @@ class Server(object):
def get_event_object(self, etype=ObjectIds.BaseEventType, source=ObjectIds.Server): def get_event_object(self, etype=ObjectIds.BaseEventType, source=ObjectIds.Server):
return Event(self.iserver.isession, etype, source) return Event(self.iserver.isession, etype, source)
...@@ -23,9 +23,8 @@ def fill_address_space(nodeservice): ...@@ -23,9 +23,8 @@ def fill_address_space(nodeservice):
create_standard_address_space_Part11(nodeservice) create_standard_address_space_Part11(nodeservice)
create_standard_address_space_Part13(nodeservice) create_standard_address_space_Part13(nodeservice)
def fill_address_space_from_disk(aspace): def fill_address_space_from_disk(aspace):
dirname = os.path.dirname(opcua.__file__) dirname = os.path.dirname(opcua.__file__)
path = os.path.join(dirname, "binary_address_space.pickle") path = os.path.join(dirname, "binary_address_space.pickle")
aspace.load(path) aspace.load(path)
...@@ -12,14 +12,16 @@ from opcua import ObjectIds ...@@ -12,14 +12,16 @@ from opcua import ObjectIds
from opcua import AttributeIds from opcua import AttributeIds
from opcua import Event from opcua import Event
class EventResult(): class EventResult():
def __str__(self): def __str__(self):
return "EventResult({})".format([str(k) + ":" + str(v) for k, v in self.__dict__.items()]) return "EventResult({})".format([str(k) + ":" + str(v) for k, v in self.__dict__.items()])
__repr__ = __str__ __repr__ = __str__
class SubscriptionItemData(): class SubscriptionItemData():
def __init__(self): def __init__(self):
self.node = None self.node = None
self.client_handle = None self.client_handle = None
...@@ -27,18 +29,20 @@ class SubscriptionItemData(): ...@@ -27,18 +29,20 @@ class SubscriptionItemData():
self.attribute = None self.attribute = None
self.mfilter = None self.mfilter = None
class Subscription(object): class Subscription(object):
def __init__(self, server, params, handler): def __init__(self, server, params, handler):
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.server = server self.server = server
self._client_handle = 200 self._client_handle = 200
self._handler = handler self._handler = handler
self.parameters = params #move to data class self.parameters = params # move to data class
self._monitoreditems_map = {} self._monitoreditems_map = {}
self._lock = RLock() self._lock = RLock()
self.subscription_id = None self.subscription_id = None
response = self.server.create_subscription(params, self.publish_callback) 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()
self.server.publish() self.server.publish()
...@@ -63,7 +67,7 @@ class Subscription(object): ...@@ -63,7 +67,7 @@ class Subscription(object):
self._call_status(statuschange) self._call_status(statuschange)
else: else:
self.logger.warn("Notification type not supported yet for notification %s", notif) self.logger.warn("Notification type not supported yet for notification %s", notif)
ack = ua.SubscriptionAcknowledgement() ack = ua.SubscriptionAcknowledgement()
ack.SubscriptionId = self.subscription_id ack.SubscriptionId = self.subscription_id
ack.SequenceNumber = publishresult.NotificationMessage.SequenceNumber ack.SequenceNumber = publishresult.NotificationMessage.SequenceNumber
...@@ -133,7 +137,7 @@ class Subscription(object): ...@@ -133,7 +137,7 @@ class Subscription(object):
rv = ua.ReadValueId() rv = ua.ReadValueId()
rv.NodeId = node.nodeid rv.NodeId = node.nodeid
rv.AttributeId = attr 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() mparams = ua.MonitoringParameters()
self._client_handle += 1 self._client_handle += 1
mparams.ClientHandle = self._client_handle mparams.ClientHandle = self._client_handle
...@@ -143,7 +147,7 @@ class Subscription(object): ...@@ -143,7 +147,7 @@ class Subscription(object):
if mfilter: if mfilter:
mparams.Filter = mfilter mparams.Filter = mfilter
mir = ua.MonitoredItemCreateRequest() mir = ua.MonitoredItemCreateRequest()
mir.ItemToMonitor = rv mir.ItemToMonitor = rv
mir.MonitoringMode = ua.MonitoringMode.Reporting mir.MonitoringMode = ua.MonitoringMode.Reporting
mir.RequestedParameters = mparams mir.RequestedParameters = mparams
...@@ -156,7 +160,7 @@ class Subscription(object): ...@@ -156,7 +160,7 @@ class Subscription(object):
results = self.server.create_monitored_items(params) results = self.server.create_monitored_items(params)
result = results[0] result = results[0]
result.StatusCode.check() result.StatusCode.check()
data = SubscriptionItemData() data = SubscriptionItemData()
data.client_handle = mparams.ClientHandle data.client_handle = mparams.ClientHandle
data.node = node data.node = node
...@@ -176,6 +180,3 @@ class Subscription(object): ...@@ -176,6 +180,3 @@ class Subscription(object):
params.MonitoredItemIds = [handle] params.MonitoredItemIds = [handle]
results = self.server.delete_monitored_items(params) results = self.server.delete_monitored_items(params)
results[0].check() results[0].check()
...@@ -8,7 +8,9 @@ import logging ...@@ -8,7 +8,9 @@ import logging
from opcua import ua from opcua import ua
class SubscriptionService(object): class SubscriptionService(object):
def __init__(self, loop, aspace): def __init__(self, loop, aspace):
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.loop = loop self.loop = loop
...@@ -90,7 +92,7 @@ class SubscriptionService(object): ...@@ -90,7 +92,7 @@ class SubscriptionService(object):
def republish(self, params): def republish(self, params):
with self._lock: with self._lock:
if not params.SubscriptionId in self.subscriptions: if not params.SubscriptionId in self.subscriptions:
#what should I do? # what should I do?
return ua.NotificationMessage() return ua.NotificationMessage()
return self.subscriptions[params.SubscriptionId].republish(params.RetransmitSequenceNumber) return self.subscriptions[params.SubscriptionId].republish(params.RetransmitSequenceNumber)
...@@ -100,17 +102,18 @@ class SubscriptionService(object): ...@@ -100,17 +102,18 @@ class SubscriptionService(object):
sub.trigger_event(event) sub.trigger_event(event)
class MonitoredItemData(object): class MonitoredItemData(object):
def __init__(self): def __init__(self):
self.client_handle = None self.client_handle = None
self.callback_handle = None self.callback_handle = None
self.monitored_item_id = None self.monitored_item_id = None
self.parameters = None self.parameters = None
self.mode = None self.mode = None
class InternalSubscription(object): class InternalSubscription(object):
def __init__(self, manager, data, addressspace, callback): def __init__(self, manager, data, addressspace, callback):
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.aspace = addressspace self.aspace = addressspace
...@@ -128,18 +131,18 @@ class InternalSubscription(object): ...@@ -128,18 +131,18 @@ class InternalSubscription(object):
self._triggered_statuschanges = [] self._triggered_statuschanges = []
self._notification_seq = 1 self._notification_seq = 1
self._not_acknowledged_results = {} self._not_acknowledged_results = {}
self._startup = True self._startup = True
self._keep_alive_count = 0 self._keep_alive_count = 0
self._publish_cycles_count = 0 self._publish_cycles_count = 0
self._stopev = False self._stopev = False
def __str__(self): def __str__(self):
return "Subscription(id:{})".format(self.data.SubscriptionId) return "Subscription(id:{})".format(self.data.SubscriptionId)
def start(self): def start(self):
self.logger.debug("starting subscription %s", self.data.SubscriptionId) self.logger.debug("starting subscription %s", self.data.SubscriptionId)
self._subscription_loop() self._subscription_loop()
def stop(self): def stop(self):
self.logger.debug("stopping subscription %s", self.data.SubscriptionId) self.logger.debug("stopping subscription %s", self.data.SubscriptionId)
self._stopev = True self._stopev = True
...@@ -151,7 +154,7 @@ class InternalSubscription(object): ...@@ -151,7 +154,7 @@ class InternalSubscription(object):
def _subscription_loop(self): def _subscription_loop(self):
#self.logger.debug("%s loop", self) #self.logger.debug("%s loop", self)
if not self._stopev: 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): def _sub_loop(self):
self.publish_results() self.publish_results()
...@@ -167,14 +170,14 @@ class InternalSubscription(object): ...@@ -167,14 +170,14 @@ class InternalSubscription(object):
self._keep_alive_count += 1 self._keep_alive_count += 1
return False return False
def publish_results(self): def publish_results(self):
if self._publish_cycles_count > self.data.RevisedLifetimeCount: 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) 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 # FIXME this will never be send since we do not have publish request anyway
self.trigger_statuschange(ua.StatusCode(ua.StatusCodes.BadTimeout)) self.trigger_statuschange(ua.StatusCode(ua.StatusCodes.BadTimeout))
self._stopev = True self._stopev = True
with self._lock: 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 self._publish_cycles_count += 1
result = self._pop_publish_result() result = self._pop_publish_result()
self.callback(result) self.callback(result)
...@@ -185,7 +188,7 @@ class InternalSubscription(object): ...@@ -185,7 +188,7 @@ class InternalSubscription(object):
if self._triggered_datachanges: if self._triggered_datachanges:
notif = ua.DataChangeNotification() notif = ua.DataChangeNotification()
notif.MonitoredItems = self._triggered_datachanges[:] notif.MonitoredItems = self._triggered_datachanges[:]
self._triggered_datachanges = [] self._triggered_datachanges = []
self.logger.debug("sending datachanges nontification with %s events", len(notif.MonitoredItems)) self.logger.debug("sending datachanges nontification with %s events", len(notif.MonitoredItems))
result.NotificationMessage.NotificationData.append(notif) result.NotificationMessage.NotificationData.append(notif)
if self._triggered_events: if self._triggered_events:
...@@ -205,7 +208,7 @@ class InternalSubscription(object): ...@@ -205,7 +208,7 @@ class InternalSubscription(object):
result.AvailableSequenceNumbers = list(self._not_acknowledged_results.keys()) result.AvailableSequenceNumbers = list(self._not_acknowledged_results.keys())
self._not_acknowledged_results[result.NotificationMessage.SequenceNumber] = result self._not_acknowledged_results[result.NotificationMessage.SequenceNumber] = result
return result return result
def trigger_statuschange(self, code): def trigger_statuschange(self, code):
self._triggered_statuschanges.append(code) self._triggered_statuschanges.append(code)
...@@ -248,11 +251,11 @@ class InternalSubscription(object): ...@@ -248,11 +251,11 @@ class InternalSubscription(object):
result = ua.MonitoredItemCreateResult() result = ua.MonitoredItemCreateResult()
if mdata.monitored_item_id == params.MonitoredItemId: if mdata.monitored_item_id == params.MonitoredItemId:
result.RevisedSamplingInterval = self.data.RevisedPublishingInterval 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 result.FilterResult = params.RequestedParameters.Filter
mdata.parameters = result mdata.parameters = result
return result return result
#FIXME modify event subscriptions # FIXME modify event subscriptions
result = ua.MonitoredItemCreateResult() result = ua.MonitoredItemCreateResult()
result.StatusCode(ua.StatusCodes.BadMonitoredItemIdInvalid) result.StatusCode(ua.StatusCodes.BadMonitoredItemIdInvalid)
return result return result
...@@ -261,7 +264,7 @@ class InternalSubscription(object): ...@@ -261,7 +264,7 @@ class InternalSubscription(object):
with self._lock: with self._lock:
result = ua.MonitoredItemCreateResult() result = ua.MonitoredItemCreateResult()
result.RevisedSamplingInterval = self.data.RevisedPublishingInterval 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) result.FilterResult = ua.downcast_extobject(params.RequestedParameters.Filter)
self._monitored_item_counter += 1 self._monitored_item_counter += 1
result.MonitoredItemId = self._monitored_item_counter result.MonitoredItemId = self._monitored_item_counter
...@@ -271,7 +274,7 @@ class InternalSubscription(object): ...@@ -271,7 +274,7 @@ class InternalSubscription(object):
mdata.parameters = result mdata.parameters = result
mdata.mode = params.MonitoringMode mdata.mode = params.MonitoringMode
mdata.client_handle = params.RequestedParameters.ClientHandle mdata.client_handle = params.RequestedParameters.ClientHandle
mdata.monitored_item_id = result.MonitoredItemId mdata.monitored_item_id = result.MonitoredItemId
self._monitored_items[result.MonitoredItemId] = mdata self._monitored_items[result.MonitoredItemId] = mdata
...@@ -284,7 +287,7 @@ class InternalSubscription(object): ...@@ -284,7 +287,7 @@ class InternalSubscription(object):
self.logger.debug("adding callback return status %s and handle %s", result.StatusCode, handle) self.logger.debug("adding callback return status %s and handle %s", result.StatusCode, handle)
mdata.callback_handle = handle mdata.callback_handle = handle
self._monitored_datachange[handle] = result.MonitoredItemId 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) self.trigger_datachange(handle, params.ItemToMonitor.NodeId, params.ItemToMonitor.AttributeId)
return result return result
...@@ -312,7 +315,6 @@ class InternalSubscription(object): ...@@ -312,7 +315,6 @@ class InternalSubscription(object):
self._monitored_items.pop(mid) self._monitored_items.pop(mid)
return ua.StatusCode() return ua.StatusCode()
def datachange_callback(self, handle, value): def datachange_callback(self, handle, value):
self.logger.info("subscription %s: datachange callback called with handle '%s' and value '%s'", self, handle, value.Value) self.logger.info("subscription %s: datachange callback called with handle '%s' and value '%s'", self, handle, value.Value)
event = ua.MonitoredItemNotification() event = ua.MonitoredItemNotification()
...@@ -354,9 +356,3 @@ class InternalSubscription(object): ...@@ -354,9 +356,3 @@ class InternalSubscription(object):
except AttributeError: except AttributeError:
fields.append(ua.Variant()) fields.append(ua.Variant())
return fields return fields
This diff is collapsed.
...@@ -5,7 +5,6 @@ from opcua.uaprotocol_auto import * ...@@ -5,7 +5,6 @@ from opcua.uaprotocol_auto import *
from opcua.uaprotocol_hand import * from opcua.uaprotocol_hand import *
# FIXME: this is really crappy, should thing about a better implementation # FIXME: this is really crappy, should thing about a better implementation
# maybe never inherit extensionobject and parse only body.... # maybe never inherit extensionobject and parse only body....
def downcast_extobject(item): def downcast_extobject(item):
...@@ -15,5 +14,3 @@ def downcast_extobject(item): ...@@ -15,5 +14,3 @@ def downcast_extobject(item):
classname = objectidname.split("_")[0] classname = objectidname.split("_")[0]
cmd = "{}.from_binary(utils.Buffer(item.to_binary()))".format(classname) cmd = "{}.from_binary(utils.Buffer(item.to_binary()))".format(classname)
return eval(cmd) return eval(cmd)
import io import io
import struct import struct
import logging import logging
import opcua.uaprotocol_auto as auto import opcua.uaprotocol_auto as auto
import opcua.uatypes as uatypes import opcua.uatypes as uatypes
import opcua.utils as utils import opcua.utils as utils
from opcua.object_ids import ObjectIds from opcua.object_ids import ObjectIds
from opcua.attribute_ids import AttributeIds from opcua.attribute_ids import AttributeIds
...@@ -14,14 +14,16 @@ logger = logging.getLogger('opcua.uaprotocol') ...@@ -14,14 +14,16 @@ logger = logging.getLogger('opcua.uaprotocol')
class SocketClosedException(Exception): class SocketClosedException(Exception):
pass pass
def get_bytes_from_sock(sock, size): def get_bytes_from_sock(sock, size):
data = utils.recv_all(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") raise SocketClosedException("Server socket has closed")
return io.BytesIO(data) return io.BytesIO(data)
class Hello(uatypes.FrozenClass): class Hello(uatypes.FrozenClass):
def __init__(self): def __init__(self):
self.ProtocolVersion = 0 self.ProtocolVersion = 0
self.ReceiveBufferSize = 65536 self.ReceiveBufferSize = 65536
...@@ -53,9 +55,8 @@ class Hello(uatypes.FrozenClass): ...@@ -53,9 +55,8 @@ class Hello(uatypes.FrozenClass):
return hello return hello
class MessageType(object): class MessageType(object):
Invalid = b"INV" #FIXME: check value Invalid = b"INV" # FIXME: check value
Hello = b"HEL" Hello = b"HEL"
Acknowledge = b"ACK" Acknowledge = b"ACK"
Error = b"ERR" Error = b"ERR"
...@@ -63,15 +64,16 @@ class MessageType(object): ...@@ -63,15 +64,16 @@ class MessageType(object):
SecureClose = b"CLO" SecureClose = b"CLO"
SecureMessage = b"MSG" SecureMessage = b"MSG"
class ChunkType(object): class ChunkType(object):
Invalid = b"0" #FIXME check Invalid = b"0" # FIXME check
Single = b"F" Single = b"F"
Intermediate = b"C" Intermediate = b"C"
Final = b"A" Final = b"A"
class Header(uatypes.FrozenClass): class Header(uatypes.FrozenClass):
def __init__(self, msgType=None, chunkType=None, channelid=0): def __init__(self, msgType=None, chunkType=None, channelid=0):
self.MessageType = msgType self.MessageType = msgType
self.ChunkType = chunkType self.ChunkType = chunkType
...@@ -113,6 +115,7 @@ class Header(uatypes.FrozenClass): ...@@ -113,6 +115,7 @@ class Header(uatypes.FrozenClass):
class ErrorMessage(uatypes.FrozenClass): class ErrorMessage(uatypes.FrozenClass):
def __init__(self): def __init__(self):
self.Error = uatypes.StatusCode() self.Error = uatypes.StatusCode()
self.Reason = "" self.Reason = ""
...@@ -137,12 +140,13 @@ class ErrorMessage(uatypes.FrozenClass): ...@@ -137,12 +140,13 @@ class ErrorMessage(uatypes.FrozenClass):
class Acknowledge(uatypes.FrozenClass): class Acknowledge(uatypes.FrozenClass):
def __init__(self): def __init__(self):
self.ProtocolVersion = 0 self.ProtocolVersion = 0
self.ReceiveBufferSize = 65536 self.ReceiveBufferSize = 65536
self.SendBufferSize = 65536 self.SendBufferSize = 65536
self.MaxMessageSize = 0 #No limits self.MaxMessageSize = 0 # No limits
self.MaxChunkCount = 0 #No limits self.MaxChunkCount = 0 # No limits
self._freeze() self._freeze()
def to_binary(self): def to_binary(self):
...@@ -154,8 +158,6 @@ class Acknowledge(uatypes.FrozenClass): ...@@ -154,8 +158,6 @@ class Acknowledge(uatypes.FrozenClass):
b.append(struct.pack("<I", self.MaxChunkCount)) b.append(struct.pack("<I", self.MaxChunkCount))
return b"".join(b) return b"".join(b)
@staticmethod @staticmethod
def from_binary(data): def from_binary(data):
ack = Acknowledge() ack = Acknowledge()
...@@ -166,7 +168,9 @@ class Acknowledge(uatypes.FrozenClass): ...@@ -166,7 +168,9 @@ class Acknowledge(uatypes.FrozenClass):
ack.MaxChunkCount = struct.unpack("<I", data.read(4))[0] ack.MaxChunkCount = struct.unpack("<I", data.read(4))[0]
return ack return ack
class AsymmetricAlgorithmHeader(uatypes.FrozenClass): class AsymmetricAlgorithmHeader(uatypes.FrozenClass):
def __init__(self): def __init__(self):
self.SecurityPolicyURI = "http://opcfoundation.org/UA/SecurityPolicy#None" self.SecurityPolicyURI = "http://opcfoundation.org/UA/SecurityPolicy#None"
self.SenderCertificate = b"" self.SenderCertificate = b""
...@@ -194,6 +198,7 @@ class AsymmetricAlgorithmHeader(uatypes.FrozenClass): ...@@ -194,6 +198,7 @@ class AsymmetricAlgorithmHeader(uatypes.FrozenClass):
class SymmetricAlgorithmHeader(uatypes.FrozenClass): class SymmetricAlgorithmHeader(uatypes.FrozenClass):
def __init__(self): def __init__(self):
self.TokenId = 0 self.TokenId = 0
self._freeze() self._freeze()
...@@ -213,6 +218,7 @@ class SymmetricAlgorithmHeader(uatypes.FrozenClass): ...@@ -213,6 +218,7 @@ class SymmetricAlgorithmHeader(uatypes.FrozenClass):
class SequenceHeader(uatypes.FrozenClass): class SequenceHeader(uatypes.FrozenClass):
def __init__(self): def __init__(self):
self.SequenceNumber = None self.SequenceNumber = None
self.RequestId = None self.RequestId = None
...@@ -235,54 +241,64 @@ class SequenceHeader(uatypes.FrozenClass): ...@@ -235,54 +241,64 @@ class SequenceHeader(uatypes.FrozenClass):
return "{}(SequenceNumber:{}, RequestId:{} )".format(self.__class__.__name__, self.SequenceNumber, self.RequestId) return "{}(SequenceNumber:{}, RequestId:{} )".format(self.__class__.__name__, self.SequenceNumber, self.RequestId)
__repr__ = __str__ __repr__ = __str__
###FIXES for missing switchfield in NodeAttributes classes # FIXES for missing switchfield in NodeAttributes classes
ana = auto.NodeAttributesMask ana = auto.NodeAttributesMask
class ObjectAttributes(auto.ObjectAttributes): class ObjectAttributes(auto.ObjectAttributes):
def __init__(self): def __init__(self):
auto.ObjectAttributes.__init__(self) auto.ObjectAttributes.__init__(self)
self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.EventNotifier self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.EventNotifier
class ObjectTypeAttributes(auto.ObjectTypeAttributes): class ObjectTypeAttributes(auto.ObjectTypeAttributes):
def __init__(self): def __init__(self):
auto.ObjectTypeAttributes.__init__(self) auto.ObjectTypeAttributes.__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 VariableAttributes(auto.VariableAttributes): class VariableAttributes(auto.VariableAttributes):
def __init__(self): def __init__(self):
auto.VariableAttributes.__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): class VariableTypeAttributes(auto.VariableTypeAttributes):
def __init__(self): def __init__(self):
auto.VariableTypeAttributes.__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 self.SpecifiedAttributes = ana.DisplayName | ana.Description | ana.WriteMask | ana.UserWriteMask | ana.Value | ana.DataType | ana.ValueRank | ana.ArrayDimensions | ana.IsAbstract
class MethodAttributes(auto.MethodAttributes): class MethodAttributes(auto.MethodAttributes):
def __init__(self): def __init__(self):
auto.MethodAttributes.__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): class ReferenceTypeAttributes(auto.ReferenceTypeAttributes):
def __init__(self): def __init__(self):
auto.ReferenceTypeAttributes.__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): class DataTypeAttributes(auto.DataTypeAttributes):
def __init__(self): def __init__(self):
auto.DataTypeAttributes.__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): class ViewAttributes(auto.ViewAttributes):
def __init__(self): def __init__(self):
auto.ViewAttributes.__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()} ObjectIdsInv = {v: k for k, v in ObjectIds.__dict__.items()}
AttributeIdsInv = {v: k for k, v in AttributeIds.__dict__.items()} AttributeIdsInv = {v: k for k, v in AttributeIds.__dict__.items()}
This diff is collapsed.
...@@ -3,10 +3,12 @@ import uuid ...@@ -3,10 +3,12 @@ import uuid
class Buffer(object): class Buffer(object):
""" """
alternative to io.BytesIO making debug easier alternative to io.BytesIO making debug easier
and added a few conveniance methods and added a few conveniance methods
""" """
def __init__(self, data): def __init__(self, data):
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.data = data self.data = data
...@@ -45,8 +47,6 @@ class Buffer(object): ...@@ -45,8 +47,6 @@ class Buffer(object):
return self.data[:size] return self.data[:size]
def recv_all(socket, size): def recv_all(socket, size):
""" """
Receive up to size bytes from socket Receive up to size bytes from socket
...@@ -60,6 +60,6 @@ def recv_all(socket, size): ...@@ -60,6 +60,6 @@ def recv_all(socket, size):
size -= len(chunk) size -= len(chunk)
return data 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...
This diff is collapsed.
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