class Simulator: beta = 0 def __init__(self, verbose=False): self.packetSize = 0 self.tdmaSlotSize = 0 # channels self.achannel = AcousticChannel(k = 2.0, s = 0.0, w = 0.0) self.ochannel = OpticalChannel(c = 4.3e-2, T = 298.15, \ S = OM.sensitivity, \ R = OM.shuntResistance, \ Id = OM.maxDarkCurrent, \ Il = OM.incidentCurrent, \ Ar = OM.Ar, At = OM.At, \ bw = OM.bandWidth, \ theta = OM.beamDivergence) # application parameters self.appStart = tools.INFINITY self.appInterval = tools.INFINITY self.appStop = tools.INFINITY # control self.clock = Clock() self.evMngr = EventManager() self.verbose = verbose self.firstNode = 0 # node control self.nodesUpdated = True self.numNodes = 0 self.nodesRef = {} # __ self.aneighbors = {} # __ self.oneighbors = {} # __ # statistics self.atxs = 0 self.afailedRxs = 0 self.asucceedRxs = 0 self.otxs = 0 self.ofailedRxs = 0 self.osucceedRxs = 0 def add_node(self, node): # assert issubclass(type(node), BasicNode) assert node.addr is not BROADCAST_ADDR, "Node addr is invalid (addr=0)" node.set_clock_src(self.clock) node.set_verbose(self.verbose) self.nodesRef[node.addr] = node self.nodesUpdated = False def set_tdma_slot(self, tdmaSlotSize): self.tdmaSlotSize = tdmaSlotSize def set_packet_size(self, packetSize): self.packetSize = packetSize def set_data_collection(self, appStart, appInterval, appStop=tools.INFINITY): self.appStart = appStart self.appInterval = appInterval self.appStop = appStop def get_num_nodes(self): return len(self.nodesRef.values()) def get_num_acoustic_txs(self): return self.atxs def get_num_acoustic_successes(self): return self.asucceedRxs def get_num_acoustic_failures(self): return self.afailedRxs def get_num_optical_txs(self): return self.otxs def get_num_optical_successes(self): return self.osucceedRxs def get_num_optical_failures(self): return self.ofailedRxs # necessary for broadcast def __update_nodes_info(self): if self.verbose: print("Updating nodes information") self.numNodes = len(self.nodesRef) for addr1 in self.nodesRef.keys(): # updating tdma info self.nodesRef[addr1].update_time_slot_size(self.tdmaSlotSize) self.nodesRef[addr1].update_num_time_slots(self.numNodes) # updating neighborhood references aneighbors = [] oneighbors = [] for addr2 in self.nodesRef.keys(): if addr1 is not addr2: node1 = self.nodesRef[addr1] node2 = self.nodesRef[addr2] distance = tools.distance(node1.position, node2.position) if distance <= AM.maxRange: aneighbors.append(addr2) if distance <= OM.maxRange: oneighbors.append(addr2) self.aneighbors[addr1] = aneighbors self.oneighbors[addr1] = oneighbors def create_app_msgs(self): # Method to feed the routing algorithm with application messages. for node in self.nodesRef.values(): if node.energy > 0 and node.isSink is False: node.collect_data() def print_data(self): print("Time: {0:.5f}".format(self.clock.read())) print("Number of acoustic transmissions: " + str(self.atxs)) print("Number of optical transmissions: " + str(self.otxs)) def __handle_send_event(self, event): # Check if some transmission is successful. In case of success, events # for message receptions are created. msg = event[2] isAcoustic = msg.flags & MsgFlags.ACOUSTIC destinations = [] if msg.dst == BROADCAST_ADDR: assert isAcoustic, "Optical broadcasts are not allowed" destinations = self.aneighbors[msg.src] else: destinations = [msg.dst] if isAcoustic: self.atxs += 1 else: self.otxs += 1 for dst in destinations: srcPos = self.nodesRef[msg.src].position dstPos = self.nodesRef[dst].position dist = tools.distance(srcPos, dstPos) if self.verbose: print("Message " + str(msg.src) + "->" + str(dst), end=" ") if isAcoustic: success = self.achannel.use(AM.frequency, AM.txPower, dist, \ len(msg)) if success: self.asucceedRxs += 1 propTime = self.achannel.get_propagation_time(dist) recvTime = self.clock.read() + propTime self.evMngr.insert(EG.create_recv_event(recvTime, dst, msg)) if self.verbose: print("was successfull: will arrive " + str(recvTime)) else: if self.verbose: print("failed") self.afailedRxs += 1 else: success = self.ochannel.use(OM.txPower, dist, dist, self.beta, \ len(msg)) if success: self.osucceedRxs += 1 propTime = self.ochannel.get_propagation_time(dist) recvTime = self.clock.read() + propTime self.evMngr.insert(EG.create_recv_event(recvTime, dst, msg)) if self.verbose: print("was successfull: will arrive " + str(recvTime)) else: if self.verbose: print("failed") self.ofailedRxs += 1 def start(self, stopExec): assert (stopExec > 0), "Execution time must be > 0" assert (self.tdmaSlotSize > 0), "TDMA time slots must be > 0" assert (self.packetSize > 0), "Packet size can not be <= 0" assert (len(self.nodesRef) is not 0), "Missing nodes" assert (self.appStart is not tools.INFINITY), "Missing app start time" assert (self.appInterval is not tools.INFINITY), "Missing app "+ \ "interval time" assert (self.appStop > self.appStart), "Stop time must be > start time" # if it is the first simulation start call if not self.clock.alarm_is_on(): # set alarm to start the data collection process self.clock.set_alarm(self.create_app_msgs, self.appStart, \ self.appInterval, self.appStop) # Creating a basic payload to avoid large memory usage # Removes two header size because of Packet inside Packet # (used for statistics) payloadSize = self.packetSize - (2 * Message.headerSize) basicPayload = list(0 for x in range(0, payloadSize)) for node in self.nodesRef.values(): node.basicPayload = basicPayload node1stSlot = self.clock.read() + (self.tdmaSlotSize * \ (node.addr - 1)) self.evMngr.insert(EG.create_call_event(node1stSlot, node.addr)) # Updating node information because some node was recently added if not self.nodesUpdated: self.__update_nodes_info() nodesList = [0] + list(self.nodesRef.values()) # to align with addresses numSlots = int(stopExec/self.tdmaSlotSize) print("Simulation started") while len(self.evMngr) != 0: event = self.evMngr.get_next() eTime = event[0] if eTime >= stopExec: break self.clock.force_time(eTime) # adjusting time for event ecode = event[1] naddr = event[2] if ecode is EventCode.NODE_CALL: if self.verbose: print("Node " + str(naddr) + " is executing") newEvents = nodesList[naddr].execute() for newEvent in newEvents: # msg send events are handled if newEvent[1] is EventCode.MSG_SEND: self.__handle_send_event(newEvent) else: self.evMngr.insert(newEvent) elif ecode is EventCode.MSG_RECV: msg = event[3] if self.verbose: print("Node " + str(naddr) + " is receiving a message") newEvents = nodesList[naddr].recv_msg(msg) for newEvent in newEvents: # msg send events are handled if newEvent[1] is EventCode.MSG_SEND: self.__handle_send_event(newEvent) else: self.evMngr.insert(newEvent) else: raise Exception("Unknown event code " + str(ecode)) print("Simulation finished")
class Simulator: beta = 0 def __init__(self, verbose=False): self.slotSize = 0 self.schedulingSize = 0 self.framesExecuted = 0 self.nextFrameStart = 0 # topology information self.parentOf = [] self.childOf = [] # for timeslot and bandwidth attribution self.nodeSlots = None # application parameters self.dcStart = tools.INFINITY self.dcInterval = tools.INFINITY self.dcStop = tools.INFINITY # control self.verbose = verbose self.channel = None self.clock = Clock() self.evMngr = EventManager() # node control self.nodes = [] self.sinkid = None # only one sink node in the network # transmission control self.txs = [] # composed of [tx_id, [src, msg, power], success] # statistics self.ongoingTxs = 0 self.finishedTxs = 0 self.failedRxs = 0 self.succeedRxs = 0 self.numDCollect = 0 # number of data collections def add_node(self, node): assert issubclass(type(node), Node) node.set_id(len(self.nodes)) node.set_clock_src(self.clock) node.set_verbose(self.verbose) self.nodes.append(node) if node.isSink: self.sinkid = node.id def add_nodes(self, nodes): for node in nodes: self.add_node(node) def set_slot_size(self, slotSize): self.slotSize = slotSize def set_channel_info(self, alpha, ambNoise): self.channel = RFChannel(alpha, ambNoise) # tschedule must be a list of slots for each node in the network. Slots must # be numbered from 1 to n. def set_timeslot_schedule(self, tschedule): assert len(tschedule) == len(self.nodes) self.nodeSlots = tschedule slotsUsed = set() for nodeSlots in tschedule: for slot in nodeSlots: slotsUsed.add(slot) self.frameSize = len(slotsUsed) def set_network_topology(self, relations): # relations must be a (parent, child) tuple list for each node in the # network assert len(self.nodes) is not 0, "No nodes yet" numNodes = len(self.nodes) self.parentOf = [ [] for i in range(numNodes) ] self.childOf = [ [] for i in range(numNodes) ] for parent, child in relations: self.parentOf[parent].append(child) self.childOf[child].append(parent) # removing duplicates for i in range(numNodes): self.parentOf[i] = list(set(self.parentOf[i])) self.childOf[i] = list(set(self.childOf[i])) if self.verbose: for i in range(numNodes): print("Node {} is parent of ".format(i) + \ "nodes {}".format(self.parentOf[i])) def set_data_collection_start(self, dcStart): assert (dcStart >= 0), "Start time must be >= 0" self.dcStart = dcStart def set_data_collection(self, dcStart, dcInterval, dcStop=tools.INFINITY): self.dcStart = dcStart self.dcInterval = dcInterval self.dcStop = dcStop def get_num_nodes(self): return len(self.nodes.values()) def get_ongoing_txs(self): return self.ongoingTxs def get_num_txs(self): return self.finishedTxs def get_num_rxs_successes(self): return self.succeedRxs def get_num_rxs_failures(self): return self.failedRxs def get_energy_consumption(self): energy = 0 for node in self.nodes: energy += node.get_energy() return -energy def do_data_collection(self): # Method to generate new messages for the nodes. for node in self.nodes: # if node.energy > 0 and node.isSink is False: if node.isSink is False: node.collect_data() self.numDCollect += 1 def run(self, framesToSim): assert (framesToSim > 0), "Execution time must be > 0" assert (self.slotSize > 0), "TDMA time slots must be > 0" assert (len(self.nodes) is not 0), "Missing nodes" assert (self.dcStart is not tools.INFINITY), "Missing app start time" assert (self.dcInterval is not tools.INFINITY), "Missing app "+ \ "interval time" assert (self.dcStop > self.dcStart), "Stop time must be > start time" assert (self.channel != None), "Missing transmission channel" # initialization self.ongoingTxs = 0 self.finishedTxs = 0 self.failedRxs = 0 self.succeedRxs = 0 self.clock.reset() self.evMngr.reset() # distributing topology information for nid in range(len(self.nodes)): if len(self.childOf[nid]) != 0: self.nodes[nid].set_parent(self.childOf[nid][0]) self.nodes[nid].set_children(self.parentOf[nid]) # distributing time slots self.frameTime = self.frameSize * self.slotSize for i in range(len(self.nodeSlots)): self.nodes[i].set_slot_size(self.slotSize) self.nodes[i].set_frame_time(self.frameTime) for slot in self.nodeSlots[i]: # slots are numbered from 1 to n ntime = self.clock.read() + ((slot - 1) * self.slotSize) self.evMngr.insert(EG.create_node_call_event(ntime, i)) self.nodes[i].add_slot(ntime) self.nodes[i].start_tdma_system() # setting alarm to start the data collection process # self.dcInterval = self.frameTime self.clock.set_periodic_alarm(self.do_data_collection, self.dcStart, \ self.dcInterval, self.dcStop) simTime = framesToSim * self.frameTime self.evMngr.insert(EG.create_stop_simulation_event(simTime)) print("========= Simulation started =========") while len(self.evMngr) != 0: event = self.evMngr.get_next() etime = event[0] if etime < self.clock.read(): raise Exception("Can not go to the past ({} < {})".format( self.clock.read(), etime)) self.clock.force_time(etime) # adjusting time for event if self.verbose: print("{0:.5f}: ".format(self.clock.read()), end = "") if etime >= self.nextFrameStart: self.framesExecuted += 1 self.nextFrameStart += self.frameTime ecode = event[1] einfo = event[2] if ecode is EventCode.NODE_CALL or ecode is EventCode.NODE_RESUME: # einfo := node id if ecode is EventCode.NODE_CALL: # updating for the next frame ntime = etime + self.frameTime self.evMngr.insert(EG.change_event(event, newTime=ntime)) if self.verbose: print("Node {} new slot".format(einfo)) else: if self.verbose: print("Node {} resuming exec".format(einfo)) newEvent = self.nodes[einfo].execute() if newEvent[1] is EventCode.TX_START: # node will transmit a message msg = newEvent[2] self.__handle_tx_request(einfo, msg) elif newEvent[1] is EventCode.NODE_SLEEP: if self.verbose: print("\tNode {} finishing exec".format(einfo)) continue else: raise Exception("Bad event {} from node {}".format( newEvent[1], einfo)) elif ecode is EventCode.TX_FINISH: # einfo = transmission id if self.verbose: print("TX {} is finishing".format(einfo)) self.__handle_tx_finish(einfo) elif ecode is EventCode.STOP_SIM: break else: raise Exception("Unknown event code " + str(ecode)) print("========= Simulation finished =========") def __handle_tx_request(self, src, msg): # estimating transmission ttime, tenergy = tools.estimate_transmission(msg, self.nodes[src].radio) ftime = self.clock.read() + ttime if self.verbose: print("\tNode {0:d} tx: {1:.4f} to {2:.4f}".format(src, self.clock.read(), ftime)) # adding call node event (adding 0.00001 to assure it will called after # the transmission was completed) self.evMngr.insert(EG.create_node_resume_event(ftime + 0.00001, src)) # evaluating transmissions txid = self.ongoingTxs txInfo = [src, msg, self.nodes[src].radio.txPower, ttime] self.txs.append([txid, txInfo, True]) self.ongoingTxs += 1 self.__evaluate_txs() # adding event to check if the transmission was successful self.evMngr.insert(EG.create_tx_finish_event(ftime, txid)) def __handle_tx_finish(self, txid): # find the transmission in the list and checks whether it was successful # or not. When it was, the message is delivered to the destination node. for i in range(len(self.txs)): if self.txs[i][0] == txid: tid, tinfo, tsuccess = self.txs.pop(i) # if the transmission was successful if tsuccess: msg = tinfo[1] dst = msg.dst ttime = tinfo[3] self.nodes[dst].recv_msg(msg, ttime) self.succeedRxs += 1 else: self.failedRxs += 1 self.finishedTxs += 1 self.ongoingTxs -= 1 break def __evaluate_txs(self): # check for interferences between the current transmissions nodesTxs = [] txsToCheck = [] # indices of transmissions that must be checked (the # ones that are currently been successful) # gathering information to start the evaluation i = 0 for tid, tinfo, tsuccess in self.txs: src = tinfo[0] pos = self.nodes[src].position power = tinfo[2] nodesTxs.append([src, pos, power]) if tsuccess: txsToCheck.append(i) i += 1 # checking the transmissions that have not yet failed. for i in txsToCheck: if self.verbose: print("\t- Evaluating tx {}".format(self.txs[i][0])) tinfo = self.txs[i][1] src = tinfo[0] power = tinfo[2] dst = tinfo[1].dst # from msg spos = self.nodes[src].position dpos = self.nodes[dst].position # calculating SINR dist = tools.distance(spos, dpos) sinr = power / (dist ** self.channel.alpha) # calculating interference inter = 0 for isrc, ipos, ipower in nodesTxs: if isrc != src: dist = tools.distance(ipos, dpos) if dist == 0: dist = 0.0000001 inter += ipower / (dist ** self.channel.alpha) # calculating final SINR and checking for zero division (when there # is neither interference nor noise) if (inter + self.channel.noise) == 0: sinr = float("inf") else: sinr = sinr / (self.channel.noise + inter) # sinr = 10 * math.log10(sinr) # to dB if self.verbose: print("\tSINR = {0:.2f} ({1:d}->{2:d})".format(sinr, src, dst)) # checking if the data can be decoded if sinr >= self.nodes[dst].radio.minSIR: self.txs[i][2] = True else: self.txs[i][2] = False return None