def __init__(self,x,y,node_id): # unique identifier self.id = node_id # physical position self.x = x self.y = y # transmission radius self.radius = 2 # access to the network self.network_interface = MultipleAccess(node_id) # pointers self.parent_id = None self.child_ids = [] # state element self.state = Node.WAIT_TO_BE_ANNEXED # a timer self.timer = 0 # a timeout value for listening for responses to the grow broadcast. self.grow_timeout = 750 # timesteps # a timeout value for listening for a data response from a child. self.child_response_timeout = 99999999999 #timesteps # save the id of thie child node from whom we're currently expecting a data response self.selected_child = None # data received from child nodes self.received_data = '' # sample id self.sample_id = None # is this the data sink node? self.i_am_the_data_sink = False # screen positions self.screen_position = ((self.x + 1)*80,(self.y + 2)*50) self.parent_screen_position = None # a file pointer to log data self.output_file = None
class Node: # protocol messages: ANNEX_FREE_NODES = "If you have no parent node yet, then I am your parent node." # broadcast ACK_OF_PARENT = "I am your child node." # unicast GROW_COMMAND = "Annex any free nodes." # unicast DATA_TO_PARENT = "DATA" # unicast: "DATA,233,44,234,12,456" # states WAIT_TO_BE_ANNEXED = 1 WAIT_FOR_GROW_COMMAND = 2 GROW = 3 SEND_GROW_COMMANDS = 4 DO_NOTHING = 5 def __init__(self,x,y,node_id): # unique identifier self.id = node_id # physical position self.x = x self.y = y # transmission radius self.radius = 2 # access to the network self.network_interface = MultipleAccess(node_id) # pointers self.parent_id = None self.child_ids = [] # state element self.state = Node.WAIT_TO_BE_ANNEXED # a timer self.timer = 0 # a timeout value for listening for responses to the grow broadcast. self.grow_timeout = 750 # timesteps # a timeout value for listening for a data response from a child. self.child_response_timeout = 99999999999 #timesteps # save the id of thie child node from whom we're currently expecting a data response self.selected_child = None # data received from child nodes self.received_data = '' # sample id self.sample_id = None # is this the data sink node? self.i_am_the_data_sink = False # screen positions self.screen_position = ((self.x + 1)*80,(self.y + 2)*50) self.parent_screen_position = None # a file pointer to log data self.output_file = None def set_as_sink(self): # set this node to be the data sink node. self.i_am_the_data_sink = True self.sample_id = 1 self.grow_enter() def set_output_file(self,output_file): # set the file pointer self.output_file = output_file def ids_sent(self): # there's an extra comma in the ids string. return 'ids_sent_' + str(self.received_data.count(',') + 1) def log_event(self,event): if self.output_file: with open(self.output_file,'a') as fp: fp.write(self.id + '\t' + event + '\n') def set_parent_id(self,parent_id): self.parent_id = parent_id [x,y] = map(int, parent_id.split('_')) self.parent_screen_position = ((x + 1)*80,(y + 2)*50) def connect_to_the_medium(self,medium): # this is one part of the bidirectional pointer between the node and the medium. self.network_interface.connect_to_the_medium(medium) def save_data(self,payload): # save the node ids recieved from a child node. data = payload.replace('DATA','') self.received_data += data def send_data_to_parent(self): data = 'DATA' + self.received_data + ',' + str(self.id) message = {'sender_id':self.id,'receiver_id':[self.parent_id],'payload':data,'mode':'unicast'} self.network_interface.send_message(message) self.log_event(self.ids_sent()) def wait_to_be_annexed_do(self,message): # listen for the broadcast from a leaf node. if message and message['payload'] == Node.ANNEX_FREE_NODES and message['sample_id'] != self.sample_id: self.wait_to_be_annexed_exit(message['sender_id'],message['sample_id']) def wait_to_be_annexed_exit(self,parent_id,sample_id): # remeber what sampling this is to prevent double sampling self.sample_id = sample_id # become the broadcasting node's child. self.set_parent_id(parent_id) # send an acknowledgement to the broadcasting node. message = {'sender_id':self.id,'receiver_id':[self.parent_id],'payload':Node.ACK_OF_PARENT,'mode':'unicast'} self.network_interface.send_message(message) self.log_event('ack_of_parent') # state transition self.state = Node.WAIT_FOR_GROW_COMMAND def wait_for_grow_command_do(self,message): if message and message['sender_id'] == self.parent_id and message['payload'] == Node.GROW_COMMAND: self.grow_enter() def grow_enter(self): self.state = Node.GROW # broadcast to free nodes message = {'sender_id':self.id,'receiver_id':[],'payload':Node.ANNEX_FREE_NODES,'mode':'broadcast','sample_id':self.sample_id} self.network_interface.send_message(message) self.log_event('broadcast') # limit the window of time to listen for responses. self.timer = self.grow_timeout def grow_do(self,message): # listen for responses to the broadcast, establishing that the senders are this node's children. if message and message['payload'] == Node.ACK_OF_PARENT and message['mode'] == 'unicast': if message['sender_id'] not in self.child_ids: self.child_ids.append(message['sender_id']) # keep counting down self.timer -= 1 if self.timer == 0: # exit state self.grow_exit() def grow_exit(self): if self.child_ids: self.send_grow_commands_enter() else: self.send_data_to_parent() self.state = Node.WAIT_TO_BE_ANNEXED self.parent_id = None def send_grow_commands_enter(self): # send a command to each of the child nodes instructing them to annex free nodes. self.state = Node.SEND_GROW_COMMANDS # the ids should be in random order already. if self.child_ids: self.selected_child = self.child_ids[-1] message = {'sender_id':self.id,'receiver_id':[self.selected_child],'payload':Node.GROW_COMMAND,'mode':'unicast'} self.network_interface.send_message(message) self.log_event('grow_command') self.timer = self.child_response_timeout else: self.send_grow_commands_exit() def send_grow_commands_do(self,message=None): # listen for the data response from each child node if message and message['payload'].startswith(Node.DATA_TO_PARENT) and message['sender_id'] == self.selected_child: self.save_data(message['payload']) self.child_ids.remove(self.selected_child) self.send_grow_commands_exit() self.timer -= 1 if self.timer == 0: # exit state self.send_grow_commands_exit() def send_grow_commands_exit(self): # are there more child nodes from whom we need data? if self.child_ids: # if so, contact them self.send_grow_commands_enter() else: if self.i_am_the_data_sink: self.state = Node.DO_NOTHING else: # otherwise, send the accumulated data to the parent node. self.send_data_to_parent() # reset self.timer = 0 self.selected_child = None self.parent_id = None self.received_data = '' self.state = Node.WAIT_TO_BE_ANNEXED def handle_stray_ack_of_parenthood(self,message): # # if message and message['payload'] == Node.ACK_OF_PARENT and message['mode'] == 'unicast': if self.child_id: self.child_ids.insert(0,message['sender_id']) def update(self): # listen to the network message = self.network_interface.receive_message() # an ad-hoc fix to a systemic issue. #self.handle_stray_ack_of_parenthood(message) # state machine if self.state == Node.WAIT_TO_BE_ANNEXED: self.wait_to_be_annexed_do(message) elif self.state == Node.WAIT_FOR_GROW_COMMAND: self.wait_for_grow_command_do(message) elif self.state == Node.GROW: self.grow_do(message) elif self.state == Node.SEND_GROW_COMMANDS: self.send_grow_commands_do(message) else: # do nothing because the sink has the data. return True # update the multiple access machine self.network_interface.update() def render(self,screen): # Draw the node. The color depends on the state. if self.state == Node.WAIT_TO_BE_ANNEXED: color = BLUE elif self.state == Node.WAIT_FOR_GROW_COMMAND: color = YELLOW elif self.state == Node.GROW: color = GREEN elif self.state == Node.SEND_GROW_COMMANDS: color = RED else: color = WHITE pygame.draw.circle(screen, color, self.screen_position, 10, 0)