def __init__(self, channel, statusline, headers=None, data=None): Event.__init__(self, channel.env) self.channel = channel self.statusline = statusline self.headers = headers if headers is not None else {} self.write = types.MethodType(Write, self) self._write_ev = self.env.event() if data is not None: self.headers['Content-Length'] = str(len(data)) self.write(data) else: self.headers['Transfer-Encoding'] = 'chunked' try: channel._send_queue.append(self) except: if (hasattr(channel, '_send_queue') and channel._send_queue is not None): raise raise channel._writer_process.value if channel._out_message_ev is not None: channel._out_message_ev.succeed(self) channel._out_message_ev = None
def _write_outgoing(self): """Pulls data from :attr:`outgoing` and pushes it to the peer.""" while True: if len(self.outgoing) == 0: if self._outgoing_error is None: self._outgoing_avail = Event(self.env) yield self._outgoing_avail data = self.outgoing[:self.max_segment_size] frame = Frame(self, data) yield self.env.timeout(self._latency()) if self._peer._frame_transmission: # Inform peer of the frame and wait for the acknowledgement. self._peer._frame_transmission.succeed(frame) sent = yield frame else: # Peer has closed the connection sent = 0 self._outgoing_error = errno.EPIPE if not frame.data: # The close frame has been sent. break self.outgoing = self.outgoing[sent:] if self._writer: self._try_write()
def _writer(self): env = self.env try: while True: if not self._out_queue: self._send_ev = Event(self.env) yield self._send_ev yield self.socket.write(self._out_queue.pop(0)) except BaseException as e: self._handle_error(self.writer, e)
def read(self): if self._read_ev is not None: raise RuntimeError('Already reading') event = self._read_ev = Event(self.env) if self._read_buf: self._read_data(Event(self.env).succeed(b'')) else: self.socket.read(self.blocksize).callbacks.append(self._read_data) return event
def recv(self): if self._in_queue is None: raise self.reader.value # Enqueue reads if there are no pending incoming messages. if not self._in_queue: if self._recv_ev is not None: raise RuntimeError('Concurrent receive attempt') self._recv_ev = Event(self.env) return self._recv_ev return Event(self.env).succeed(self._in_queue.pop(0))
def run(self, until=None): """Executes :meth:`step()` until the given criterion *until* is met. - If it is ``None`` (which is the default), this method will return when there are no further events to be processed. - If it is an :class:`~simpy.events.Event`, the method will continue stepping until this event has been triggered and will return its value. Raises a :exc:`RuntimeError` if there are no further events to be processed and the *until* event was not triggered. - If it is a number, the method will continue stepping until the environment's time reaches *until*. """ if until is not None: if not isinstance(until, Event): # Assume that *until* is a number if it is not None and # not an event. Create a Timeout(until) in this case. at = float(until) if at <= self.now: raise ValueError('until(=%s) should be > the current ' 'simulation time.' % at) # Schedule the event with before all regular timeouts. until = Event(self) until.ok = True until._value = None self.schedule(until, URGENT, at - self.now) elif until.callbacks is None: # Until event has already been processed. return until.value until.callbacks.append(StopSimulation.callback) try: while True: self.step() except StopSimulation as exc: return exc.args[0] # == until.value except EmptySchedule: if until is not None: # @ FIXED ASSERTION ERROR try: not until.triggered except: pass raise RuntimeError('No scheduled events left but "until" ' 'event was not triggered: %s' % until)
def event(self) -> Event: """Return a new :class:`~simpy.events.Event` instance. Yielding this event suspends a process until another process triggers the event. """ return Event(self)
def write(self, packet): if self._write_ev is not None: raise RuntimeError('Already writing') if self.encode is not None: packet = self.encode(packet) if len(packet) > self.max_packet_size: raise ValueError('Packet too large. Allowed %d bytes but ' 'got %d bytes' % (self.max_packet_size, len(packet))) self._write_ev = Event(self.env) self._write_buf = Header.pack(len(packet)) + packet self.socket.write(self._write_buf).callbacks.append(self._write_data) return self._write_ev
def __init__(self, message, data=b'', all=False): Event.__init__(self, message.env) try: if message._write_ev.triggered: raise RuntimeError('Concurrent write attempt') except AttributeError: if message._write_ev is False: raise RuntimeError('Message has already been sent') elif message._write_ev is not None: # Unknown error occured. raise raise message.channel._writer_process.value self.data = data self.all = all if data else True message._write_ev.succeed(self)
def __init__(self, message, all=False): Event.__init__(self, message.env) try: if message._read_ev.triggered: raise RuntimeError('Concurrent read attempt') except AttributeError: if message._read_ev is False: # All content has been read. self.succeed(b'') return elif message._read_ev is not None: # Unknown error occured. raise raise message.channel._reader_process.value self.all = all message._read_ev.succeed(self)
def run(self, until=None): """Executes :meth:`step()` until the given criterion *until* is met. - If it is ``None`` (which is the default) this method will return if there are no further events to be processed. - If it is an :class:`~simpy.events.Event` the method will continue stepping until this event has been triggered and will return its value. - If it can be converted to a number the method will continue stepping until the environment's time reaches *until*. """ if until is None: until = Event(self) elif not isinstance(until, Event): at = float(until) if at <= self.now: raise ValueError('until(=%s) should be > the current ' 'simulation time.' % at) # Schedule the event with before all regular timeouts. until = Event(self) until.ok = True until._value = None self.schedule(until, URGENT, at - self.now) until.callbacks.append(_stop_simulate) try: while True: self.step() except EmptySchedule: pass if not until.triggered: return None if not until.ok: raise until.value return until.value
class Message: """ A class used for the exchange of arbitrary messages between components. A :class:`Message` can be used to simulate both asynchronous and synchronous function calls. Attributes: type(Enum): An enumeration object that defines the message type args(Dict[str, Any]): A dictionary containing the message's arguments eProcessed(Event): A SimPy event that is triggered when :meth:`setProcessed` is called. This is useful for simulating synchronous function calls and also allows for return values (an example is provided in :meth:`setProcessed`). """ def __init__(self, type: Enum, args: Dict[str, Any] = None): self.type = type self.args = args self.eProcessed = Event(SimMan.env) def setProcessed(self, returnValue: Any = None): """ Makes the :attr:`eProcessed` event succeed. Args: returnValue: If specified, will be used as the `value` of the :attr:`eProcessed` event. Examples: If `returnValue` is specified, SimPy processes can use Signals for simulating synchronous function calls with return values like this: :: signal = Signal(myType, {"key", value}) gate.output.send(signal) value = yield signal.eProcessed # value now contains the returnValue that setProcessed() was called with """ self.eProcessed.succeed(returnValue) def __repr__(self): return "Message(type: '{}', args: {})".format(self.type.name, self.args)
def run(self, until: Optional[Union[SimTime, Event]] = None) -> Optional[Any]: """Executes :meth:`step()` until the given criterion *until* is met. - If it is ``None`` (which is the default), this method will return when there are no further events to be processed. - If it is an :class:`~simpy.events.Event`, the method will continue stepping until this event has been triggered and will return its value. Raises a :exc:`RuntimeError` if there are no further events to be processed and the *until* event was not triggered. - If it is a number, the method will continue stepping until the environment's time reaches *until*. """ if until is not None: if not isinstance(until, Event): # Assume that *until* is a number if it is not None and # not an event. Create a Timeout(until) in this case. at: SimTime if isinstance(until, int): at = until else: at = float(until) if at <= self.now: raise ValueError( f'until(={at}) must be > the current simulation time.') # Schedule the event before all regular timeouts. until = Event(self) until._ok = True until._value = None self.schedule(until, URGENT, at - self.now) elif until.callbacks is None: # Until event has already been processed. return until.value until.callbacks.append(StopSimulation.callback) try: while True: self.step() except StopSimulation as exc: return exc.args[0] # == until.value except EmptySchedule: if until is not None: assert not until.triggered raise RuntimeError( f'No scheduled events left but "until" event was not ' f'triggered: {until}') return None
def networkInHandler(self, cmd): if isinstance(cmd, Message): if cmd.type is StackMessageTypes.RECEIVE: logger.debug("%s: Entering receive mode.", self) # start receiving self._receiveCmd = cmd # set _receiving and a timeout event self._receiving = True self._receiveTimeout = SimMan.timeout(cmd.args["duration"]) self._receiveTimeout.callbacks.append( self._receiveTimeoutCallback) elif isinstance(cmd, Packet): payload = cmd packet = Packet( SimpleMacHeader(self.addr, payload.header.destMAC, flag=0), payload) self._packetQueue.append(packet) self._packetAddedEvent.succeed() self._packetAddedEvent = Event(SimMan.env)
def __init__(self, channel): Event.__init__(self, channel.env) self.channel = channel # FIXME This sould be removed. self.respond = types.MethodType(OutResponse, channel) self.read = types.MethodType(Read, self) self._read_ev = self.env.event() try: channel._recv_queue.append(self) except: if (hasattr(channel, '_recv_queue') and channel._recv_queue is not None): raise raise channel._reader_process.value if channel._in_message_ev is not None: channel._in_message_ev.succeed(self) channel._in_message_ev = None
def __init__(self, env, arrival_time, eid): self.eid = eid # start_event = env.timeout(arrival_time) start_event = env.timeout(0) xor1_yes = Event(env) xor1_no = Event(env) end_act_A = env.process( act_A(env, start_event, dur_A(self), self.eid)) end_act_B = env.process( act_B(env, end_act_A, dur_B(self), self.eid)) end_act_B.callbacks.append(xor1(xor1_yes, xor1_no)) end_act_C = env.process(act_C(env, xor1_yes, dur_C(self), self.eid)) end_act_D = env.process( act_D(env, end_act_C, dur_D(self), self.eid)) end_act_E = env.process(act_E(env, xor1_no, dur_E(self), self.eid)) end_act_F = env.process( act_F(env, AnyOf(env, [end_act_D, end_act_E]), dur_F(self), self.eid)) env.process(act_G(env, end_act_F, dur_G(self), self.eid))
def __init__(self, name: str, device: Device, frequencyBandSpec: FrequencyBandSpec, addr: bytes): """ Args: name: The layer's name device: The device that operates the SimpleMac layer addr: The 6-byte-long MAC address to be assigned to this MAC layer """ super(SimpleMac, self).__init__(name, owner=device) self._addPort("phy") self._addPort("network") self.addr = addr self._packetQueue = deque(maxlen=100) # allow 100 packets to be queued self._packetAddedEvent = Event(SimMan.env) self._mcs = BpskMcs(frequencyBandSpec) self._transmissionPower = 0.0 # dBm self._receiving = False self._receiveCmd = None self._receiveTimeout = None logger.debug("Initialization completed, MAC address: %s", self.addr, sender=self)
def _read_incoming(self): """Pushes remote data frames into :attr:`incoming`.""" self._frame_transmission = Event(self.env) while True: if self._reader: self._try_read() if self._incoming_error is not None: break # Wait until there is room for incoming data. if len(self.incoming) >= self.max_buffer_size: self._incoming_avail = Event(self.env) yield self._incoming_avail continue frame = yield self._frame_transmission self._frame_transmission = Event(self.env) if frame is None: # A local close will end the frame transmission with None. continue if frame.data: read = min(self.max_buffer_size - len(self.incoming), len(frame.data)) self.incoming += frame.data[:read] # Simulate transmission of the frame acknowledgement. yield self.env.timeout(self._latency()) frame.succeed(read) else: # An empty frame has been received, this means the remote side # has closed the connection. self._incoming_error = errno.ECONNRESET self.env._unregister(self) frame.succeed() self._frame_transmission = None
def __init__(self, message, id, content): Event.__init__(self, message.env) self.message = message self.id = id self.content = content
class Message(object): # TODO Rename class and module (channel)? def __init__(self, env, socket, codec=None, message_limit=1024): self.env = env self.socket = socket if codec is None: codec = JSON() self.codec = codec self.message_limit = message_limit self._message_id = count() self._in_queue = [] self._out_queue = [] self._in_messages = {} """Maps incoming message objects to ids.""" self._out_messages = {} """Maps outgoing message ids to objects.""" self._send_ev = None self._recv_ev = None self.reader = Process(self.env, self._reader()) self.writer = Process(self.env, self._writer()) def _reader(self): try: buffer = b'' while True: data = yield self.socket.read() msg_type, msg_id, content = self.codec.decode(data) if msg_type == REQUEST: message = InMessage(self, msg_id, content) message.callbacks.append(self._reply) if len(self._in_messages) >= self.message_limit: # Close the connection if the maximum number of # incoming messages is reached. self.close() raise MessageOverflowError( 'Incoming message limit of %d has ' 'been exceeded' % self.message_limit) self._in_messages[message] = msg_id if self._recv_ev is not None: self._recv_ev.succeed(message) self._recv_ev = None else: self._in_queue.append(message) elif msg_type == SUCCESS: self._out_messages.pop(msg_id).succeed(content) elif msg_type == FAILURE: self._out_messages.pop(msg_id).fail( RemoteException(self, content)) else: raise RuntimeError('Invalid message type %d' % msg_type) except BaseException as e: self._handle_error(self.reader, e) def _writer(self): env = self.env try: while True: if not self._out_queue: self._send_ev = Event(self.env) yield self._send_ev yield self.socket.write(self._out_queue.pop(0)) except BaseException as e: self._handle_error(self.writer, e) def _handle_error(self, process, err): # FIXME Should I really ignore errors? if isinstance(err, socket.error) and err.errno in UNCRITICAL_ERRORS: uncritical = True else: uncritical = False process.defused = uncritical if self._send_ev is not None: # FIXME Is this safe? Is it impossible, that socket.write has been # triggered but not yet been processed? self._send_ev.defused = uncritical self._send_ev.fail(err) if self._out_messages is not None: for msg_id, event in self._out_messages.items(): event.defused = uncritical event.fail(err) if self._recv_ev is not None: self._recv_ev.defused = uncritical self._recv_ev.fail(err) self._in_messages = None self._out_messages = None self._in_queue = None self._out_queue = None self._recv_ev = None self._send_ev = None raise err def _reply(self, event): try: message_id = self._in_messages.pop(event) except AttributeError: if self._in_messages is not None: raise # Channel has been closed. Ignore the event. event.defused = True return if event.ok: failure = None try: self._out_queue.append( self.codec.encode((SUCCESS, message_id, event._value))) except BaseException as e: failure = e else: failure = event._value if failure is not None: # Failure is handled on the remote side. event.defused = True # FIXME Ugly hack for python < 3.3 if hasattr(failure, '__traceback__'): stacktrace = traceback.format_exception( failure.__class__, failure, failure.__traceback__) else: stacktrace = traceback.format_exception_only( failure.__class__, failure) self._out_queue.append( self.codec.encode((FAILURE, message_id, ''.join(stacktrace)))) if self._send_ev is not None: self._send_ev.succeed() self._send_ev = None def send(self, content): if self._out_queue is None: raise self.writer.value if len(self._out_messages) >= self.message_limit: raise MessageOverflowError('Outgoing message limit of %d has been ' 'exceeded' % self.message_limit) message_id = next(self._message_id) data = self.codec.encode((REQUEST, message_id, content)) message = OutMessage(self, message_id, content) self._out_queue.append(data) self._out_messages[message_id] = message # Wake the writer process. if self._send_ev is not None: self._send_ev.succeed() self._send_ev = None return message def recv(self): if self._in_queue is None: raise self.reader.value # Enqueue reads if there are no pending incoming messages. if not self._in_queue: if self._recv_ev is not None: raise RuntimeError('Concurrent receive attempt') self._recv_ev = Event(self.env) return self._recv_ev return Event(self.env).succeed(self._in_queue.pop(0)) def close(self): self.socket.close()
def schedule(self, delay): evt = Event(self.env) evt.callbacks.append(self.observe) evt._ok = True self.env.schedule(evt, priority=2, delay=delay)
def __init__(self, sock, data): Event.__init__(self, sock.env) self.sock = sock self.data = data
""" 一次等待多个事件.py: 有时候,你想同时等待多个事件。例如,您可能需要等待资源,但不是无限次的。或者你可能要等到所有的一系列事件发生。 因此SimPy中提供了AnyOf与AllOf事件,都是一个Condition事件。 两者都以事件列表作为参数,如果它们中的至少一个或全部被触发,则被触发。 """ import simpy from simpy.events import AnyOf, AllOf, Event env = simpy.Environment() events = [Event(env) for i in range(3)] a = AnyOf(env, events) # Triggers if at least one of "events" is triggered. b = AllOf(env, events) # Triggers if all each of "events" is triggered. # ''' def test_condition(env): t1, t2 = env.timeout(1, value='spam'), env.timeout(2, value='eggs') ret = yield t1 | t2 assert ret == {t1: 'spam'} t1, t2 = env.timeout(1, value='spam'), env.timeout(2, value='eggs') ret = yield t1 & t2 assert ret == {t1: 'spam', t2: 'eggs'} # You can also concatenate & and |
class TCPSocket(BaseSocket): def __init__(self, env, max_buffer_size=4096, max_segment_size=None): self.env = env self.max_buffer_size = max_buffer_size if max_segment_size: self.max_segment_size = max_segment_size else: self.max_segment_size = max_buffer_size self.incoming = None self.outgoing = None self.backlog = None self._address = ('0.0.0.0', 0) self._peer = None self._peer_address = None self._latency = None self._incoming_error = errno.ENOTCONN self._incoming_avail = None self._incoming_reader = None self._outgoing_error = errno.EPIPE self._outgoing_avail = None self._outgoing_writer = None self._frame_transmission = None self._reader = None self._writer = None self.read = types.MethodType(Read, self) self.write = types.MethodType(Write, self) @classmethod def server(cls, env, address, backlog=5): socket = cls(env) socket.bind(address) socket.listen(backlog) return socket @classmethod def connection(cls, env, address): socket = cls(env) socket.connect(address) return socket def _link(self, address, peer, peer_address, latency): self._peer = peer self._address = address self._peer_address = peer_address self._latency = latency self._incoming_reader = self.env.process(self._read_incoming()) self._outgoing_writer = self.env.process(self._write_outgoing()) self._incoming_error = None self._outgoing_error = None self.incoming = b'' self.outgoing = b'' @property def address(self): if self._address is None: raise socket_error(errno.EBADF) return self._address @property def peer_address(self): if self._address is None: raise socket_error(errno.EBADF) if self._peer_address is None: raise socket_error(errno.ENOTCONN) return self._peer_address def bind(self, address): if self._address is None: raise socket_error(errno.EBADF) if self._incoming_error != errno.ENOTCONN: raise socket_error(platform.invalid_argument) self._address = self.env._get_address(address) def listen(self, backlog): if self._address is None: raise socket_error(errno.EBADF) if self._incoming_error != errno.ENOTCONN: raise socket_error(platform.invalid_argument) self.backlog = Store(self.env, capacity=backlog) self.env._register(self) def accept(self): if self.backlog is None: raise socket_error(platform.invalid_argument) return self.backlog.get() def connect(self, address): if self._address is None: raise socket_error(errno.EBADF) if self._peer is not None: # Already connected, do nothing. return if self.backlog is not None: raise socket_error(errno.EISCONN) self.env._establish_connection(self, address) def _try_read(self): if self._incoming_error is not None: self._reader.fail(socket_error(self._incoming_error)) self._reader = None return if not self.incoming: return data = self.incoming[:self._reader.amount] self.incoming = self.incoming[self._reader.amount:] self._reader.succeed(data) self._reader = None if self._incoming_avail: self._incoming_avail.succeed() self._incoming_avail = None def _try_write(self): if self._outgoing_error is not None: self._writer.fail(socket_error(self._outgoing_error)) self._writer = None return available = self.max_buffer_size - len(self.outgoing) if not available: return self.outgoing += self._writer.data[:available] self._writer.succeed(min(len(self._writer.data), available)) self._writer = None if self._outgoing_avail: # Notify the reader process about the new data. self._outgoing_avail.succeed() self._outgoing_avail = None def _read_incoming(self): """Pushes remote data frames into :attr:`incoming`.""" self._frame_transmission = Event(self.env) while True: if self._reader: self._try_read() if self._incoming_error is not None: break # Wait until there is room for incoming data. if len(self.incoming) >= self.max_buffer_size: self._incoming_avail = Event(self.env) yield self._incoming_avail continue frame = yield self._frame_transmission self._frame_transmission = Event(self.env) if frame is None: # A local close will end the frame transmission with None. continue if frame.data: read = min(self.max_buffer_size - len(self.incoming), len(frame.data)) self.incoming += frame.data[:read] # Simulate transmission of the frame acknowledgement. yield self.env.timeout(self._latency()) frame.succeed(read) else: # An empty frame has been received, this means the remote side # has closed the connection. self._incoming_error = errno.ECONNRESET self.env._unregister(self) frame.succeed() self._frame_transmission = None def _write_outgoing(self): """Pulls data from :attr:`outgoing` and pushes it to the peer.""" while True: if len(self.outgoing) == 0: if self._outgoing_error is None: self._outgoing_avail = Event(self.env) yield self._outgoing_avail data = self.outgoing[:self.max_segment_size] frame = Frame(self, data) yield self.env.timeout(self._latency()) if self._peer._frame_transmission: # Inform peer of the frame and wait for the acknowledgement. self._peer._frame_transmission.succeed(frame) sent = yield frame else: # Peer has closed the connection sent = 0 self._outgoing_error = errno.EPIPE if not frame.data: # The close frame has been sent. break self.outgoing = self.outgoing[sent:] if self._writer: self._try_write() def close(self): """Closes the socket, all further operations will raise ``EBADF``.""" if self._address is None: return self._incoming_error = errno.EBADF self._outgoing_error = errno.EBADF if self._incoming_avail is not None: self._incoming_avail.succeed() if self._outgoing_avail is not None: self._outgoing_avail.succeed() if self._frame_transmission: # Wake the reader if it is currently waiting for a frame. if not self._frame_transmission.triggered: self._frame_transmission.succeed() self._frame_transmission = None if self.backlog is not None: for accepter in self.backlog.get_queue: if not accepter.triggered: accepter.fail(socket_error(errno.EBADF)) self.env._unregister(self) self._address = None self._peer_address = None
def __init__(self, type: Enum, args: Dict[str, Any] = None): self.type = type self.args = args self.eProcessed = Event(SimMan.env)
class SimpleMac(Module): """ A MAC layer implementation of the contention-free protocol described as follows: * Every SimpleMac has a unique 6-byte-long MAC address. * The MAC layer with address ``0`` is considered to belong to the RRM. * Time slots are grouped into frames. * Every second frame is reserved for the RRM and has a fixed length (number of time slots). * The RRM uses those frames to send a short *announcement* containing a destination MAC address and the frame length (number of time slots **n**) of the following frame. By doing so it allows the specified device to use the frequency band for the next frame. *Announcements* are packets with a :class:`~gymwipe.networking.messages.SimpleMacHeader` having the following attributes: :attr:`~gymwipe.networking.messages.SimpleMacHeader.sourceMAC`: The RRM MAC address :attr:`~gymwipe.networking.messages.SimpleMacHeader.destMAC`: The MAC address of the device that may transmit next :attr:`~gymwipe.networking.messages.SimpleMacHeader.flag`: ``1`` (flag for allowing a device to transmit) The packet's :attr:`~gymwipe.networking.messages.Packet.payload` is the number **n** mentioned above (wrapped inside a :class:`~gymwipe.networking.messages.Transmittable`) * Every other packet sent has a :class:`~gymwipe.networking.messages.SimpleMacHeader` with :attr:`~gymwipe.networking.messages.SimpleMacHeader.flag` ``0``. The `networkIn` gate accepts objects of the following types: * :class:`~gymwipe.networking.messages.Message` Types: * :attr:`~gymwipe.networking.messages.StackMessageTypes.RECEIVE` Listen for packets sent to this device. :class:`~gymwipe.networking.messages.Message` args: :duration: The time in seconds to listen for When a packet destinated to this device is received, the :class:`~gymwipe.networking.messages.Message.eProcessed` event of the :class:`~gymwipe.networking.messages.Message` will be triggered providing the packet as the value. If the time given by `duration` has passed and no packet was received, it will be triggered with ``None``. * :attr:`~gymwipe.networking.messages.Packet` Send a given packet (with a :attr:`~gymwipe.networking.messages.SimpleNetworkHeader`) to the MAC address defined in the header. The `phyIn` gate accepts objects of the following types: * :attr:`~gymwipe.networking.messages.Packet` A packet received by the physical layer """ @GateListener.setup def __init__(self, name: str, device: Device, frequencyBandSpec: FrequencyBandSpec, addr: bytes): """ Args: name: The layer's name device: The device that operates the SimpleMac layer addr: The 6-byte-long MAC address to be assigned to this MAC layer """ super(SimpleMac, self).__init__(name, owner=device) self._addPort("phy") self._addPort("network") self.addr = addr self._packetQueue = deque(maxlen=100) # allow 100 packets to be queued self._packetAddedEvent = Event(SimMan.env) self._mcs = BpskMcs(frequencyBandSpec) self._transmissionPower = 0.0 # dBm self._receiving = False self._receiveCmd = None self._receiveTimeout = None logger.debug("Initialization completed, MAC address: %s", self.addr, sender=self) rrmAddr = bytes(6) """bytes: The 6 bytes long RRM MAC address""" _macCounter = 0 @classmethod def newMacAddress(cls) -> bytes: """ A method for generating unique 6-byte-long MAC addresses (currently counting upwards starting at 1) """ cls._macCounter += 1 addr = bytearray(6) addr[5] = cls._macCounter return bytes(addr) @GateListener("phyIn", Packet) def phyInHandler(self, packet): header = packet.header if not isinstance(header, SimpleMacHeader): raise ValueError( "Can only deal with header of type SimpleMacHeader. Got %s.", type(header), sender=self) if header.destMAC == self.addr: # packet for us if header.sourceMAC == self.rrmAddr: # RRM sent the packet logger.debug("Received a packet from RRM: %s", packet, sender=self) if header.flag == 1: # we may transmit timeSlots = packet.payload.value timeTotal = timeSlots * TIME_SLOT_LENGTH stopTime = SimMan.now + timeTotal def timeLeft(): return stopTime - SimMan.now logger.info("Got permission to transmit for %d time slots", timeSlots, sender=self) timeoutEvent = SimMan.timeout(timeTotal) queuedPackets = True while not timeoutEvent.processed: if len(self._packetQueue) == 0: queuedPackets = False logger.debug( "Packet queue empty, nothing to transmit. Time left: %s s", timeLeft(), sender=self) yield self._packetAddedEvent | timeoutEvent if not timeoutEvent.processed: # new packet was added for sending logger.debug( "Packet queue was refilled. Time left: %s s", timeLeft(), sender=self) queuedPackets = True if queuedPackets: if not timeLeft() > self._packetQueue[ 0].transmissionTime(self._mcs.dataRate): logger.info( "Next packet is too large to be transmitted. Idling. Time left: %s s", timeLeft(), sender=self) yield timeoutEvent else: # enough time left to transmit the next packet # TODO This has to be done before adding the # packet to the queue! packet = self._packetQueue.popleft() message = Message( StackMessageTypes.SEND, { "packet": packet, "power": self._transmissionPower, "mcs": self._mcs }) self.gates["phyOut"].send( message) # make the PHY send the packet logger.debug( "Transmitting packet. Time left: %s", timeLeft(), sender=self) logger.debug("Packet: %s", packet, sender=self) yield message.eProcessed # wait until the transmission has completed else: # packet from any other device if self._receiving: logger.info("Received Packet.", sender=self) logger.debug("Packet: %s", packet.payload, sender=self) # return the packet's payload to the network layer self._receiveCmd.setProcessed(packet.payload) self._stopReceiving() else: logger.debug( "Received Packet from Phy, but not in receiving mode. Packet ignored.", sender=self) elif header.destMAC == self.rrmAddr: # packet from RRM to all devices pass @GateListener("networkIn", (Message, Packet)) def networkInHandler(self, cmd): if isinstance(cmd, Message): if cmd.type is StackMessageTypes.RECEIVE: logger.debug("%s: Entering receive mode.", self) # start receiving self._receiveCmd = cmd # set _receiving and a timeout event self._receiving = True self._receiveTimeout = SimMan.timeout(cmd.args["duration"]) self._receiveTimeout.callbacks.append( self._receiveTimeoutCallback) elif isinstance(cmd, Packet): payload = cmd packet = Packet( SimpleMacHeader(self.addr, payload.header.destMAC, flag=0), payload) self._packetQueue.append(packet) self._packetAddedEvent.succeed() self._packetAddedEvent = Event(SimMan.env) def _receiveTimeoutCallback(self, event: Event): if event is self._receiveTimeout: # the current receive message has timed out logger.debug("%s: Receive timed out.", self) self._receiveCmd.setProcessed() self._stopReceiving() def _stopReceiving(self): logger.debug("%s: Stopping to receive.", self) self._receiveCmd = None self._receiving = False self._receiveTimeout = None
class Packet(object): # TODO blocksize should always be max_packet_size. # TODO Never read more data than necessary. # TODO Read and write should return packet events. def __init__(self, socket, max_packet_size=16384, blocksize=4096, encode=None, decode=None): self.env = socket.env self.socket = socket self.max_packet_size = max_packet_size self.blocksize = blocksize self.encode = encode self.decode = decode self._read_ev = None self._read_buf = b'' self._read_size = None self._write_ev = None self._write_buf = b'' def _wrap(self, event): if event.ok: event._value = Packet(event._value, self.max_packet_size, self.blocksize, self.encode, self.decode) def accept(self): event = self.socket.accept() event.callbacks.append(self._wrap) return event def bind(self, address): return self.socket.bind(address) def listen(self, backlog=5): return self.socket.listen(backlog) def connect(self, address): return self.socket.connect(address) @property def address(self): return self.socket.address @property def peer_address(self): return self.socket.address def read(self): if self._read_ev is not None: raise RuntimeError('Already reading') event = self._read_ev = Event(self.env) if self._read_buf: self._read_data(Event(self.env).succeed(b'')) else: self.socket.read(self.blocksize).callbacks.append(self._read_data) return event def _read_data(self, event): if not event.ok: event.defused = True self._read_ev.fail(event.value) self._read_ev = None return self._read_buf += event.value if self._read_size is None and len(self._read_buf) >= Header.size: self._read_size = Header.unpack_from(self._read_buf)[0] if self._read_size > self.max_packet_size: raise ValueError('Packet too large. Allowed %d bytes but ' 'got %d bytes' % (self.max_packet_size, self._read_size)) self._read_size += Header.size if (self._read_size is not None and len(self._read_buf) >= self._read_size): packet = self._read_buf[Header.size:self._read_size] if self.decode is None: self._read_ev.succeed(packet) else: # TODO Handle errors. self._read_ev.succeed(self.decode(packet)) self._read_buf = self._read_buf[self._read_size:] self._read_size = None self._read_ev = None return self.socket.read(self.blocksize).callbacks.append(self._read_data) def write(self, packet): if self._write_ev is not None: raise RuntimeError('Already writing') if self.encode is not None: packet = self.encode(packet) if len(packet) > self.max_packet_size: raise ValueError('Packet too large. Allowed %d bytes but ' 'got %d bytes' % (self.max_packet_size, len(packet))) self._write_ev = Event(self.env) self._write_buf = Header.pack(len(packet)) + packet self.socket.write(self._write_buf).callbacks.append(self._write_data) return self._write_ev def _write_data(self, event): if not event.ok: event.defused = True self._write_ev.fail(event.value) self._write_ev = None return self._write_buf = self._write_buf[event.value:] if not self._write_buf: self._write_ev.succeed() self._write_ev = None else: self.socket.write(self._write_buf).callbacks.append( self._write_data) def close(self): self.socket.close()
def event(self): """ Creates and returns a new :class:`~simpy.events.Event` object belonging to the current environment. """ return Event(self.env)