Message.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. from classes.Utilities import random_string
  2. import random
  3. from classes.Packet import Packet
  4. import math
  5. class Message():
  6. ''' This class defines an object of a Message, which is a message send between
  7. the sender and recipient. '''
  8. __slots__ = ['conf', 'id', 'payload', 'real_sender', 'time_queued', 'time_sent', 'time_delivered', 'transit_time', 'reconstruct', 'complete_receiving', 'pkts']
  9. def __init__(self, conf, net, payload, dest, real_sender, id=None):
  10. self.conf = conf
  11. self.id = id or random_string(self.conf["misc"]["id_len"])
  12. self.payload = payload
  13. self.real_sender = real_sender
  14. self.time_queued = None # The first packet to be added in the queue (sender updates this)
  15. self.time_sent = None # The first packet to leave the client (sender updates this)
  16. self.time_delivered = None # The last packet to arrive (The recipient of msg will fill this in)
  17. self.transit_time = None
  18. self.reconstruct = set() # The IDs we need to reconstruct.
  19. # State on reception
  20. self.complete_receiving = False
  21. # Packets
  22. self.pkts = self.split_into_packets(net, dest)
  23. @classmethod
  24. def random(cls, conf, net, sender, dest):
  25. ''' This class method creates a random message, with random payload. '''
  26. size = random.randint(conf["message"]["min_msg_size"], conf["message"]["max_msg_size"])
  27. payload = random_string(size)
  28. m = cls(conf=conf, net=net, payload=payload, real_sender=sender, dest=dest)
  29. return m
  30. def split_into_packets(self, net, dest):
  31. ''' Function splits the payload of the message into the fixed size blocks
  32. and encodes them into the objects of class Packet.
  33. Keyword arguments:
  34. topology - the network topology,
  35. dest - the destination of the message.
  36. '''
  37. pkts = []
  38. pkt_size = float(self.conf["packet"]["packet_size"])
  39. # to be able to have atomic messages, we keep this if condition
  40. if pkt_size == 0:
  41. fragments = [self.payload]
  42. num_fragments = 1
  43. else:
  44. num_fragments = int(math.ceil(float(len(self.payload))/pkt_size))
  45. fragments = [self.payload[i:i + int(self.conf["packet"]["packet_size"])] for i in range(0, len(self.payload), int(self.conf["packet"]["packet_size"]))]
  46. for i, f in enumerate(fragments):
  47. rand_route = net.select_random_route()
  48. rand_route = rand_route + [dest]
  49. tmp_pkt = Packet(conf=self.conf, route=rand_route, payload=f, sender=self.real_sender, dest=dest, msg_id=self.id, type="REAL", order=i+1, num=num_fragments, message=self)
  50. pkts.append(tmp_pkt)
  51. self.reconstruct.add(tmp_pkt.id)
  52. return pkts
  53. def register_received_pkt(self, new_pkt):
  54. ''' Function registers the information about the received packets
  55. by removing the packet id from the set of expected packets. Additionaly,
  56. the function triggers updates the completeness of the message,
  57. i.e., the flag pointing whether all the expected packets were received
  58. and the times, i.e., the information when the last packet was received.
  59. Keyword arguments:
  60. new_pkt - the new packet to be registered.
  61. '''
  62. if self.complete_receiving: #Same packet may have been retransmitted.
  63. return
  64. elif new_pkt.msg_id == self.id and new_pkt.id in self.reconstruct:
  65. self.reconstruct.remove(new_pkt.id)
  66. self.update_times(new_pkt)
  67. self.update_completeness()
  68. def update_times(self, pkt):
  69. ''' Function updates the time_delivered of the message with the time of the
  70. recently received packet.
  71. Keyword arguments:
  72. pkt - the received packet.
  73. '''
  74. if self.time_delivered is None or pkt.time_delivered > self.time_delivered:
  75. self.time_delivered = pkt.time_delivered
  76. def update_completeness(self):
  77. ''' Function checks whether all the expected packets of the message
  78. were received and if the message can be reconstructed. If yes,
  79. the status of the message completeness is set to True.
  80. '''
  81. self.complete_receiving = (len(self.reconstruct) == 0)
  82. def output(self):
  83. ''' Prints all the information about the message ans its packets'''
  84. if not self.conf["debug"]["enabled"]:
  85. return
  86. self.transit_time = self.time_delivered - self.time_sent
  87. print("=====================")
  88. print("Message ID : " + str(self.id))
  89. print("Real Sender : " + str(self.pkts[0].real_sender))
  90. print("All fragments collected?: " + str(self.complete_receiving))
  91. print("Original Fragments : " + str(len(self.pkts)))
  92. print("Fragments sent : " + str(self.pkts[0].fragments))
  93. print("Fragments missing : " + str(len(self.reconstruct)))
  94. print("Time queued : " + str(self.time_queued))
  95. print("Time sent : " + str(self.time_sent))
  96. print("Time delivered : " + str(self.time_delivered))
  97. print("Transit duration : " + str(self.transit_time))
  98. # Reconstruct Payload
  99. tmp_payload = ""
  100. for i in range(self.pkts[0].fragments):
  101. for tmp_pkt in self.pkts:
  102. if tmp_pkt.order == (i-1):
  103. tmp_payload += tmp_pkt.payload
  104. print("Payload : " + str(tmp_payload))
  105. for packet in self.pkts:
  106. packet.output()
  107. print("=====================")