diff --git a/pyogp/lib/base/message_template.py b/pyogp/lib/base/message_template.py index cdf8b46..f523178 100644 --- a/pyogp/lib/base/message_template.py +++ b/pyogp/lib/base/message_template.py @@ -47,7 +47,8 @@ class MsgData(): self.blocks = {} def add_block(self, block): - self.blocks[block.get_name()] = block + self.blocks[block.get_name()] = [] + self.blocks[block.get_name()].append(block) def get_blocks(self): return self.blocks @@ -64,7 +65,7 @@ class MsgBlockData(): serialized and sent. """ def __init__(self, name): self.name = name - self.total_size = 0 + self.size = 0 self.variables = {} def get_name(self): @@ -87,7 +88,10 @@ class MsgVariableData(): serialized and sent """ def __init__(self, name, tp): self.name = name - self.total_size = 0 + #data_size holds info whether or not the variable is of type + #MVT_VARIABLE + self.data_size = 0 + self.size = -1 self.lltype = tp self.data = None @@ -101,7 +105,7 @@ class MsgVariableData(): def get_data(self): return self.data - def get_total_size(self): + def get_size(self): return self.total_size def get_type(self): diff --git a/pyogp/lib/base/message_template_builder.py b/pyogp/lib/base/message_template_builder.py index f0cee99..b2d1f9e 100644 --- a/pyogp/lib/base/message_template_builder.py +++ b/pyogp/lib/base/message_template_builder.py @@ -1,6 +1,12 @@ +#standard libs +import struct + +#pyogp libs from pyogp.lib.base.message_template import MsgData, MsgBlockData, \ MsgVariableData -from pyogp.lib.base.message_types import MsgType +import pyogp.lib.base.message_types +#from pyogp.lib.base.message_types import MsgType, MsgBlockType, MsgFrequency +from pyogp.lib.base.data_packer import DataPacker class MessageTemplateBuilder(): """ This class builds messages at its high level, that is, keeping @@ -17,6 +23,8 @@ class MessageTemplateBuilder(): self.cur_msg_name = '' self.cur_block_name = '' + self.packer = DataPacker() + def get_current_message(self): return self.current_msg @@ -31,13 +39,86 @@ class MessageTemplateBuilder(): #and the blocks variables #serializer should adapt the interface of MsgData (whatever it is), #and implement ISerializer - pass - def build_block(self): - pass - - def build_variable(self): - pass + #doesn't build in the header flags, sequence number, or data offset + msg_buffer = '' + bytes = 0 + + if self.current_template == None: + #error + return None + + #don't need to pack the frequency and message number. The template + #stores it because it doesn't change per template. + pack_freq_num = self.current_template.get_message_hex_num() + msg_buffer += pack_freq_num + bytes += len(pack_freq_num) + + #add some offset? + + for block in self.current_template: + packed_block, block_size = build_block(block, self.current_msg) + msg_buffer += packed_block + bytes += block_size + + return msg_buffer, bytes + + def build_block(self, template_block, message_data): + block_buffer = '' + bytes = 0 + + block_list = message_data.get_block(template_block.get_name()) + block_count = len(block_list) + + message_block = block_list[0] + + #multiple block type means there is a static number of these blocks + #that make up this message, with the number stored in the template + #don't need to add it to the buffer, because the message handlers that + #receieve this know how many to read automatically + if template_block.get_type() == MsgBlockType.MBT_MULTIPLE: + if template_block.get_block_number() != message_block.get_block_number(): + raise Exception('Block ' + template_block.get_name() + ' is type MBT_MULTIPLE \ + but only has data stored for ' + str(block_count) + ' out of its ' + \ + template_block.get_block_number() + ' blocks') + + #variable means the block variables can repeat, so we have to + #mark how many blocks there are of this type that repeat, stored in + #the data + if template_block.get_type() == MsgBlockType.MBT_VARIABLE: + block_buffer += struct.pack('>B', message_block.get_block_number()) + bytes += 1 + + for block in block_list: + for variable in message_block.get_variables(): + var_size = variable.get_size() + + if var_size == -1: + raise Exception('Variable ' + variable.get_name() + ' in block ' + \ + message_block.get_name() + ' of message ' + message_data.get_name() + \ + ' wasn"t set prior to buildMessage call') + + data_size = variable.get_data_size() + #variable's data_size represents whether or not it is of the type + #MVT_VARIABLE. If it is positive, it is + if data_size > 0: + if data_size == 1: + block_buffer += struct.pack('>B', var_size) + elif data_size == 2: + block_buffer += struct.pack('>H', var_size) + elif data_size == 4: + block_buffer += struct.pack('>I', var_size) + else: + raise Exception('Attempting to build variable with unknown size \ + of ' + str(var_size)) + + bytes += data_size + + #make sure there IS data to pack + if variable.get_data() != None and var_size > 0: + data = self.packer.pack_data(variable.get_data()) + block_buffer += data + bytes += len(data) def new_message(self, message_name): """ Creates a new packet where data can be added to it. Note, the variables @@ -55,7 +136,8 @@ class MessageTemplateBuilder(): def next_block(self, block_name): self.current_block = self.current_template.get_block(block_name) - #error check + #if it doesn't exist, create a new block (may be a VARIABLE or MULTIPLE type + #block self.cur_block_name = block_name @@ -63,34 +145,17 @@ class MessageTemplateBuilder(): var_data = MsgVariableData(variable.get_name(), variable.get_type()) self.current_block.add_variable(var_data) - def add_data(self, var_name, data, data_size): - self.check_size(var_name, data_size) - self.current_block.add_data(var_name, data, data_size) + def add_data(self, var_name, data, data_type): + """ the data type is passed in to make sure that the programmer is aware of + what type (and therefore size) of the data that is being passed in. """ + self.__check_size(var_name, data_type) + self.current_block.add_data(var_name, data, data_type) - def check_size(self, var_name, data, data_size): + def __check_size(self, var_name, data, data_type): block = self.template_list[cur_msg_name].get_block(self.cur_block_name) + data_size = MsgType.sizeof(data_type) size = block.get_variable(var_name).get_size() if size != data_size: #warning #for now, exception raise Exception('Variable size isn"t the same as the variable size') - - def add_bool(self, var_name, bool_data): - self.add_data(var_name, bool_data, MsgType.sizeof(MsgType.MVT_BOOL)) - -"""class IMessageSerializer(): - implements ISerializer - adapts MessageData - - #goes through MessageData and builds a byte string that can be sent over - #UDP or tcp - serialize (pack_message, build_message) - - #goes through each block of the message data - pack_block - - #goes through each block of the message variables, creating a byte-code - #string to return - pack_variable""" - - diff --git a/pyogp/lib/base/message_types.py b/pyogp/lib/base/message_types.py index 00771fa..051fb9c 100644 --- a/pyogp/lib/base/message_types.py +++ b/pyogp/lib/base/message_types.py @@ -4,19 +4,29 @@ class MsgBlockType(): MBT_VARIABLE = range(3) #pack flags +#= '\x80' +#= '\x80' +#= '\x40' +#= '\x20' +#= '\x10' +#= '\x00' class PackFlags(): - LL_ZERO_CODE_FLAG = '\x80' - LL_RELIABLE_FLAG = '\x40' - LL_RESENT_FLAG = '\x20' - LL_ACK_FLAG = '\x10' - LL_NONE = '\x00' + LL_ZERO_CODE_FLAG, \ + LL_RELIABLE_FLAG, \ + LL_RESENT_FLAG, \ + LL_ACK_FLAG, \ + LL_NONE = range(5) #frequency for messages +#= '\xFF\xFF\xFF' +#= '\xFF\xFF' +#= '\xFF' +#= '' class MsgFrequency(): - FIXED_FREQUENCY_MESSAGE = '\xFF\xFF\xFF' - LOW_FREQUENCY_MESSAGE = '\xFF\xFF' - MEDIUM_FREQUENCY_MESSAGE = '\xFF' - HIGH_FREQUENCY_MESSAGE = '' + FIXED_FREQUENCY_MESSAGE, \ + LOW_FREQUENCY_MESSAGE, \ + MEDIUM_FREQUENCY_MESSAGE, \ + HIGH_FREQUENCY_MESSAGE = range(4) class MsgTrust(): LL_TRUSTED, \ @@ -61,45 +71,45 @@ class MsgType(): MVT_IP_ADDR, \ MVT_IP_PORT = range(20) - #TODO should this be changed? Less switch-style and more object-style? - def sizeof(var): - if var == MsgType.MVT_FIXED: - return -1 - elif var == MsgType.MVT_VARIABLE: - return -1 - elif var == MsgType.MVT_U8: - return 1 - elif var == MsgType.MVT_U16: - return 2 - elif var == MsgType.MVT_U32: - return 4 - elif var == MsgType.MVT_U64: - return 8 - elif var == MsgType.MVT_S8: - return 1 - elif var == MsgType.MVT_S16: - return 2 - elif var == MsgType.MVT_S32: - return 4 - elif var == MsgType.MVT_S64: - return 8 - elif var == MsgType.MVT_F32: - return 4 - elif var == MsgType.MVT_F64: - return 8 - elif var == MsgType.MVT_LLVector3: - return 12 - elif var == MsgType.MVT_LLVector3d: - return 24 - elif var == MsgType.MVT_LLVector4: - return 16 - elif var == MsgType.MVT_LLQuaternion: - return 12 - elif var == MsgType.MVT_LLUUID: - return 16 - elif var == MsgType.MVT_BOOL: - return 1 - elif var == MsgType.MVT_IP_ADDR: - return 4 - elif var == MsgType.MVT_IP_PORT: - return 2 +#TODO should this be changed? Less switch-style and more object-style? +def sizeof(var): + if var == MsgType.MVT_FIXED: + return -1 + elif var == MsgType.MVT_VARIABLE: + return -1 + elif var == MsgType.MVT_U8: + return 1 + elif var == MsgType.MVT_U16: + return 2 + elif var == MsgType.MVT_U32: + return 4 + elif var == MsgType.MVT_U64: + return 8 + elif var == MsgType.MVT_S8: + return 1 + elif var == MsgType.MVT_S16: + return 2 + elif var == MsgType.MVT_S32: + return 4 + elif var == MsgType.MVT_S64: + return 8 + elif var == MsgType.MVT_F32: + return 4 + elif var == MsgType.MVT_F64: + return 8 + elif var == MsgType.MVT_LLVector3: + return 12 + elif var == MsgType.MVT_LLVector3d: + return 24 + elif var == MsgType.MVT_LLVector4: + return 16 + elif var == MsgType.MVT_LLQuaternion: + return 12 + elif var == MsgType.MVT_LLUUID: + return 16 + elif var == MsgType.MVT_BOOL: + return 1 + elif var == MsgType.MVT_IP_ADDR: + return 4 + elif var == MsgType.MVT_IP_PORT: + return 2