Example #1
0
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")
Example #2
0
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