Beispiel #1
0
    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
Beispiel #2
0
    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))
Beispiel #6
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)
Beispiel #7
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)
Beispiel #8
0
        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
Beispiel #10
0
    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)
Beispiel #11
0
    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)
Beispiel #12
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 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
Beispiel #13
0
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)
Beispiel #14
0
    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
Beispiel #15
0
    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)
Beispiel #16
0
    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))
Beispiel #18
0
    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)
Beispiel #19
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 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
Beispiel #20
0
    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()
Beispiel #23
0
 def schedule(self, delay):
     evt = Event(self.env)
     evt.callbacks.append(self.observe)
     evt._ok = True
     self.env.schedule(evt, priority=2, delay=delay)
Beispiel #24
0
 def __init__(self, sock, data):
     Event.__init__(self, sock.env)
     self.sock = sock
     self.data = data
Beispiel #25
0
"""
一次等待多个事件.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 |
Beispiel #26
0
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
Beispiel #27
0
 def __init__(self, type: Enum, args: Dict[str, Any] = None):
     self.type = type
     self.args = args
     self.eProcessed = Event(SimMan.env)
Beispiel #28
0
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()
Beispiel #30
0
 def event(self):
     """
     Creates and returns a new :class:`~simpy.events.Event` object belonging to the
     current environment.
     """
     return Event(self.env)