Commit 3db3b0f6 authored by ORD's avatar ORD

Merge pull request #77 from huazh/master

performance enhancement 2
parents b98292a3 7a814487
......@@ -11,6 +11,14 @@ import opcua.uaprotocol as ua
import opcua.utils as utils
class CachedRequest(object):
def __init__(self, binary):
self.binary = binary
def to_binary(self):
return self.binary
class UASocketClient(object):
"""
handle socket connection and send ua messages
......@@ -44,11 +52,15 @@ class UASocketClient(object):
send request to server.
timeout is the timeout written in ua header
"""
# HACK to make sure we can convert our request to binary before increasing request counter etc ...
request.to_binary()
# END HACK
with self._lock:
request.RequestHeader = self._create_request_header(timeout)
try:
cachedreq = CachedRequest(request.to_binary())
except:
# reset reqeust handle if any error
# see self._create_request_header
self._request_handle -= 1
raise
hdr = ua.Header(ua.MessageType.SecureMessage, ua.ChunkType.Single, self._security_token.ChannelId)
symhdr = self._create_sym_algo_header()
seqhdr = self._create_sequence_header()
......@@ -56,7 +68,7 @@ class UASocketClient(object):
if callback:
future.add_done_callback(callback)
self._callbackmap[seqhdr.RequestId] = future
self._write_socket(hdr, symhdr, seqhdr, request)
self._write_socket(hdr, symhdr, seqhdr, cachedreq)
if not callback:
data = future.result(self.timeout)
self.check_answer(data, " in response to " + request.__class__.__name__)
......
This diff is collapsed.
This diff is collapsed.
......@@ -3,6 +3,7 @@ import logging
import opcua.uaprotocol_auto as auto
import opcua.uatypes as uatypes
from opcua.uatypes import uatype_UInt32
import opcua.utils as utils
from opcua.object_ids import ObjectIds
from opcua.attribute_ids import AttributeIds
......@@ -35,22 +36,22 @@ class Hello(uatypes.FrozenClass):
def to_binary(self):
b = []
b.append(struct.pack("<I", self.ProtocolVersion))
b.append(struct.pack("<I", self.ReceiveBufferSize))
b.append(struct.pack("<I", self.SendBufferSize))
b.append(struct.pack("<I", self.MaxMessageSize))
b.append(struct.pack("<I", self.MaxChunkCount))
b.append(uatype_UInt32.pack(self.ProtocolVersion))
b.append(uatype_UInt32.pack(self.ReceiveBufferSize))
b.append(uatype_UInt32.pack(self.SendBufferSize))
b.append(uatype_UInt32.pack(self.MaxMessageSize))
b.append(uatype_UInt32.pack(self.MaxChunkCount))
b.append(uatypes.pack_string(self.EndpointUrl))
return b"".join(b)
@staticmethod
def from_binary(data):
hello = Hello()
hello.ProtocolVersion = struct.unpack("<I", data.read(4))[0]
hello.ReceiveBufferSize = struct.unpack("<I", data.read(4))[0]
hello.SendBufferSize = struct.unpack("<I", data.read(4))[0]
hello.MaxMessageSize = struct.unpack("<I", data.read(4))[0]
hello.MaxChunkCount = struct.unpack("<I", data.read(4))[0]
hello.ProtocolVersion = uatype_UInt32.unpack(data.read(4))[0]
hello.ReceiveBufferSize = uatype_UInt32.unpack(data.read(4))[0]
hello.SendBufferSize = uatype_UInt32.unpack(data.read(4))[0]
hello.MaxMessageSize = uatype_UInt32.unpack(data.read(4))[0]
hello.MaxChunkCount = uatype_UInt32.unpack(data.read(4))[0]
hello.EndpointUrl = uatypes.unpack_string(data)
return hello
......@@ -87,26 +88,23 @@ class Header(uatypes.FrozenClass):
def to_binary(self):
b = []
b.append(struct.pack("<3s", self.MessageType))
b.append(struct.pack("<s", self.ChunkType))
b.append(struct.pack("<3ss", self.MessageType, self.ChunkType))
size = self.body_size + 8
if self.MessageType in (MessageType.SecureOpen, MessageType.SecureClose, MessageType.SecureMessage):
size += 4
b.append(struct.pack("<I", size))
b.append(uatype_UInt32.pack(size))
if self.MessageType in (MessageType.SecureOpen, MessageType.SecureClose, MessageType.SecureMessage):
b.append(struct.pack("<I", self.ChannelId))
b.append(uatype_UInt32.pack(self.ChannelId))
return b"".join(b)
@staticmethod
def from_string(data):
hdr = Header()
hdr.MessageType = struct.unpack("<3s", data.read(3))[0]
hdr.ChunkType = struct.unpack("<c", data.read(1))[0]
hdr.packet_size = struct.unpack("<I", data.read(4))[0]
hdr.MessageType, hdr.ChunkType, hdr.packet_size = struct.unpack("<3scI", data.read(8))
hdr.body_size = hdr.packet_size - 8
if hdr.MessageType in (MessageType.SecureOpen, MessageType.SecureClose, MessageType.SecureMessage):
hdr.body_size -= 4
hdr.ChannelId = struct.unpack("<I", data.read(4))[0]
hdr.ChannelId = uatype_UInt32.unpack(data.read(4))[0]
return hdr
def __str__(self):
......@@ -150,22 +148,19 @@ class Acknowledge(uatypes.FrozenClass):
self._freeze()
def to_binary(self):
b = []
b.append(struct.pack("<I", self.ProtocolVersion))
b.append(struct.pack("<I", self.ReceiveBufferSize))
b.append(struct.pack("<I", self.SendBufferSize))
b.append(struct.pack("<I", self.MaxMessageSize))
b.append(struct.pack("<I", self.MaxChunkCount))
return b"".join(b)
return struct.pack(
"<5I",
self.ProtocolVersion,
self.ReceiveBufferSize,
self.SendBufferSize,
self.MaxMessageSize,
self.MaxChunkCount)
@staticmethod
def from_binary(data):
ack = Acknowledge()
ack.ProtocolVersion = struct.unpack("<I", data.read(4))[0]
ack.ReceiveBufferSize = struct.unpack("<I", data.read(4))[0]
ack.SendBufferSize = struct.unpack("<I", data.read(4))[0]
ack.MaxMessageSize = struct.unpack("<I", data.read(4))[0]
ack.MaxChunkCount = struct.unpack("<I", data.read(4))[0]
ack.ProtocolVersion, ack.ReceiveBufferSize, ack.SendBufferSize, ack.MaxMessageSize, ack.MaxChunkCount \
= struct.unpack("<5I", data.read(20))
return ack
......@@ -206,11 +201,11 @@ class SymmetricAlgorithmHeader(uatypes.FrozenClass):
@staticmethod
def from_binary(data):
obj = SymmetricAlgorithmHeader()
obj.TokenId = struct.unpack("<I", data.read(4))[0]
obj.TokenId = uatype_UInt32.unpack(data.read(4))[0]
return obj
def to_binary(self):
return struct.pack("<I", self.TokenId)
return uatype_UInt32.pack(self.TokenId)
def __str__(self):
return "{}(TokenId:{} )".format(self.__class__.__name__, self.TokenId)
......@@ -227,14 +222,14 @@ class SequenceHeader(uatypes.FrozenClass):
@staticmethod
def from_binary(data):
obj = SequenceHeader()
obj.SequenceNumber = struct.unpack("<I", data.read(4))[0]
obj.RequestId = struct.unpack("<I", data.read(4))[0]
obj.SequenceNumber = uatype_UInt32.unpack(data.read(4))[0]
obj.RequestId = uatype_UInt32.unpack(data.read(4))[0]
return obj
def to_binary(self):
b = []
b.append(struct.pack("<I", self.SequenceNumber))
b.append(struct.pack("<I", self.RequestId))
b.append(uatype_UInt32.pack(self.SequenceNumber))
b.append(uatype_UInt32.pack(self.RequestId))
return b"".join(b)
def __str__(self):
......
This diff is collapsed.
......@@ -26,25 +26,29 @@ class Buffer(object):
"""
def __init__(self, data):
self.logger = logging.getLogger(__name__)
self.data = data
# self.logger = logging.getLogger(__name__)
self._data = data
self.rsize = 0
def __str__(self):
return "Buffer(size:{}, data:{})".format(len(self.data), self.data)
return "Buffer(size:{}, data:{})".format(len(self), self.data)
__repr__ = __str__
def __len__(self):
return len(self.data)
return len(self._data) - self.rsize
def read(self, size):
"""
read and pop number of bytes for buffer
"""
if size > len(self.data):
rsize = self.rsize
nrsize = rsize + size
mydata = self._data
if nrsize > len(mydata):
raise Exception("Not enough data left in buffer, request for {}, we have {}".format(size, self))
#self.logger.debug("Request for %s bytes, from %s", size, self)
data = self.data[:size]
self.data = self.data[size:]
data = mydata[rsize:nrsize]
self.rsize = nrsize
#self.logger.debug("Returning: %s ", data)
return data
......@@ -53,17 +57,27 @@ class Buffer(object):
return a copy, optionnaly only copy 'size' bytes
"""
if size is None:
return Buffer(self.data)
return Buffer(self._data[self.rsize:])
else:
return Buffer(self.data[:size])
return Buffer(self._data[self.rsize:self.rsize + size])
def test_read(self, size):
"""
read 'size' bytes from buffer, without removing them from buffer
"""
if size > len(self.data):
if self.rsize + size > len(self._data):
raise Exception("Not enough data left in buffer, request for {}, we have {}".format(size, self))
return self.data[:size]
return self._data[self.rsize:self.rsize + size]
def get_data(self):
return self._data[self.rsize:]
def set_data(self, v):
self._data = v
self.rsize = 0
data = property(get_data, set_data)
class SocketClosedException(Exception):
pass
......
......@@ -6,7 +6,7 @@ from copy import copy
import xml.etree.ElementTree as ET
from IPython import embed
# from IPython import embed
NeedOverride = []
NeedConstructor = []#["RelativePathElement", "ReadValueId", "OpenSecureChannelParameters", "UserIdentityToken", "RequestHeader", "ResponseHeader", "ReadParameters", "UserIdentityToken", "BrowseDescription", "ReferenceDescription", "CreateSubscriptionParameters", "PublishResult", "NotificationMessage", "SetPublishingModeParameters"]
......
......@@ -91,8 +91,15 @@ class CodeGenerator(object):
self.write(":ivar {}:".format(field.name))
self.write(":vartype {}: {}".format(field.name, field.uatype))
self.write("'''")
self.write("def __init__(self):")
self.write("def __init__(self, binary=None):")
self.iidx += 1
self.write("if binary is not None:")
self.iidx += 1
self.write("self._binary_init(binary)")
self.write("self._freeze()")
self.write("return")
self.iidx -= 1
# hack extension object stuff
extobj_hack = False
......@@ -151,15 +158,15 @@ class CodeGenerator(object):
self.write("if self.{}: ".format(field.name))
self.iidx += 1
if field.length:
self.write("{}.append(struct.pack('<i', len(self.{})))".format(listname, field.name))
self.write("{}.append(uatype_Int32.pack(len(self.{})))".format(listname, field.name))
self.write("for fieldname in self.{}:".format(field.name))
fname = "fieldname"
self.iidx += 1
if field.is_native_type():
self.write("{}.append(pack_uatype('{}', {}))".format(listname, field.uatype, fname))
self.write_pack_uatype(listname, fname, field.uatype)
elif field.uatype in self.model.enum_list:
uatype = self.model.get_enum(field.uatype).uatype
self.write("{}.append(pack_uatype('{}', {}))".format(listname, uatype, fname))
self.write_pack_uatype(listname, fname, uatype)
elif field.uatype in ("ExtensionObject"):
self.write("{}.append(extensionobject_to_binary({}))".format(listname, fname))
else:
......@@ -179,8 +186,13 @@ class CodeGenerator(object):
self.write("@staticmethod")
self.write("def from_binary(data):")
self.iidx += 1
self.write("return {}(data)".format(obj.name))
self.iidx -= 1
self.write("")
self.write("def _binary_init(self, data):")
self.iidx += 1
iidx = self.iidx
self.write("obj = {}()".format(obj.name))
for idx, field in enumerate(obj.fields):
self.iidx = iidx
# if field.name == "Body" and idx <= (len(obj.fields)-1):
......@@ -190,37 +202,53 @@ class CodeGenerator(object):
bit = obj.bits[field.switchfield]
if field.switchvalue:
mask = '0b' + '0' * (8 - bit.length) + '1' * bit.length
self.write("val = obj.{} & {}".format(bit.container, mask))
self.write("val = self.{} & {}".format(bit.container, mask))
self.write("if val == {}:".format(bit.idx))
else:
self.write("if obj.{} & (1 << {}):".format(bit.container, bit.idx))
self.write("if self.{} & (1 << {}):".format(bit.container, bit.idx))
self.iidx += 1
array = False
if field.is_native_type():
if field.length:
self.write("obj.{} = unpack_uatype_array('{}', data)".format(field.name, field.uatype))
self.write("self.{} = unpack_uatype_array('{}', data)".format(field.name, field.uatype))
else:
self.write("obj.{} = unpack_uatype('{}', data)".format(field.name, field.uatype))
self.write_unpack_uatype(field.name, field.uatype)
elif field.uatype in self.model.enum_list:
uatype = self.model.get_enum(field.uatype).uatype
self.write("obj.{} = unpack_uatype('{}', data)".format(field.name, uatype))
self.write_unpack_uatype(field.name, uatype)
else:
if field.uatype in ("ExtensionObject"):
frombinary = "extensionobject_from_binary(data)"
else:
frombinary = "{}.from_binary(data)".format(field.uatype)
if field.length:
self.write("length = struct.unpack('<i', data.read(4))[0]")
self.write("length = uatype_Int32.unpack(data.read(4))[0]")
self.write("array = []")
self.write("if length != -1:")
self.iidx += 1
self.write("for _ in range(0, length):")
self.iidx += 1
self.write("obj.{}.append({})".format(field.name, frombinary))
self.write("array.append({})".format(frombinary))
self.iidx -= 2
self.write("self.{} = array".format(field.name))
else:
self.write("obj.{} = {}".format(field.name, frombinary))
self.write("self.{} = {}".format(field.name, frombinary))
if field.switchfield:
self.iidx -= 1
self.write("else:")
self.iidx += 1
if extobj_hack and field.name == "Encoding":
self.write("self.Encoding = 1")
elif field.uatype == obj.name: # help!!! selv referencing class
self.write("self.{} = None".format(field.name))
elif not obj.name in ("ExtensionObject") and field.name == "TypeId": # and ( obj.name.endswith("Request") or obj.name.endswith("Response")):
self.write("self.TypeId = FourByteNodeId(ObjectIds.{}_Encoding_DefaultBinary)".format(obj.name))
else:
self.write("self.{} = {}".format(field.name, "[]" if field.length else self.get_default_value(field)))
if len(obj.fields) == 0:
self.write("pass")
self.iidx = 2
self.write("return obj")
#__str__
self.iidx = 1
......@@ -236,6 +264,51 @@ class CodeGenerator(object):
self.iix = 0
def write_unpack_uatype(self, name, uatype):
if uatype in ("Int8", "UInt8", "Sbyte", "Byte", "Char", "Boolean"):
size = 1
elif uatype in ("Int16", "UInt16"):
size = 2
elif uatype in ("Int32", "UInt32", "Float"):
size = 4
elif uatype in ("Int64", "UInt64", "Double"):
size = 8
elif uatype == "String":
self.write("self.{} = unpack_string(data)".format(name))
return
elif uatype in ("CharArray", "ByteString"):
self.write("self.{} = unpack_bytes(data)".format(name))
return
elif uatype == "DateTime":
self.write("self.{} = unpack_datetime(data)".format(name))
return
else:
self.write("self.{} = unpack_uatype('{}', data)".format(name, uatype))
return
self.write("self.{} = uatype_{}.unpack(data.read({}))[0]".format(name, uatype, size))
def write_pack_uatype(self, listname, name, uatype):
if uatype in (
"Int8", "UInt8", "Sbyte", "Byte", "Char", "Boolean",
"Int16", "UInt16",
"Int32", "UInt32", "Float",
"Int64", "UInt64", "Double"
):
self.write("{}.append(uatype_{}.pack({}))".format(listname, uatype, name))
return
elif uatype == "String":
self.write("{}.append(pack_string({}))".format(listname, name))
return
elif uatype in ("CharArray", "ByteString"):
self.write("{}.append(pack_bytes({}))".format(listname, name))
return
elif uatype == "DateTime":
self.write("{}.append(pack_datetime({}))".format(listname, name))
return
else:
self.write("{}.append(pack_uatype('{}', {}))".format(listname, uatype, name))
return
def get_default_value(self, field):
if field.uatype in self.model.enum_list:
return 0
......
......@@ -5,13 +5,13 @@ def status_codes():
inputfile = open("StatusCodes_add.csv")
additional = {}
for line in inputfile:
name, val, doc = line.split(",", maxsplit=2)
name, val, doc = line.split(",", 2)
additional[int(val, 0)] = (name, val, doc)
inputfile = open("StatusCodes.csv")
result = []
for line in inputfile:
name, val, doc = line.split(",", maxsplit=2)
name, val, doc = line.split(",", 2)
result.append((name, val, doc))
additional.pop(int(val, 0), None)
add = [ additional[k] for k in sorted(additional.keys()) ]
......@@ -25,23 +25,28 @@ if __name__ == "__main__":
#outputfile.write("from enum import Enum\n")
outputfile.write("\n")
outputfile.write("class StatusCodes:\n")
outputfile.write("class StatusCodes(object):\n")
for name, val, doc in codes:
doc = doc.strip()
outputfile.write(" {} = {}\n".format(name, val))
outputfile.write("\n")
outputfile.write("\n")
outputfile.write("def get_name_and_doc(val):\n")
kword = "if"
outputfile.write("code_to_name_doc = {\n")
for name, val, doc in codes:
doc = doc.strip()
doc = doc.replace("'", '"')
outputfile.write(" {} val == {}:\n".format(kword, val))
outputfile.write(" return '{}', '{}'\n".format(name, doc))
kword = "elif"
outputfile.write(" else:\n".format(val))
outputfile.write(" return 'UnknownUaError', 'Unknown StatusCode value: {}'.format(val)\n")
outputfile.write(" {}: ('{}', '{}'),\n".format(val, name, doc))
outputfile.write("}\n")
outputfile.write("\n")
outputfile.write("\n")
outputfile.write("""def get_name_and_doc(val):
if val in code_to_name_doc:
return code_to_name_doc[val]
else:
return 'UnknownUaError', 'Unknown StatusCode value: {}'.format(val)
""")
'''
outputfile.write("class StatusCode(Enum):\n")
outputfile.write(" Good = 0\n")
......
......@@ -5,9 +5,9 @@ def extensionobject_from_binary(data):
Returns an object, or None if TypeId is zero
"""
TypeId = NodeId.from_binary(data)
Encoding = unpack_uatype('UInt8', data)
Encoding = ord(data.read(1))
if Encoding & (1 << 0):
Body = unpack_uatype('ByteString', data)
Body = unpack_bytes(data)
if TypeId.Identifier == 0:
return None
klass = ExtensionClasses[TypeId.Identifier]
......@@ -29,7 +29,7 @@ def extensionobject_to_binary(obj):
Body = obj.to_binary()
packet = []
packet.append(TypeId.to_binary())
packet.append(pack_uatype('UInt8', Encoding))
packet.append(uatype_UInt8.pack(Encoding))
if Body:
packet.append(pack_uatype('ByteString', Body))
packet.append(pack_bytes(Body))
return b''.join(packet)
......@@ -225,7 +225,7 @@ class Unit(unittest.TestCase):
dv = ua.DataValue('abc')
self.assertEqual(dv.Value, ua.Variant('abc'))
now = datetime.now()
dv.source_timestamp = now
dv.SourceTimestamp = now
def test_variant(self):
dv = ua.Variant(True, ua.VariantType.Boolean)
......
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