Skip to content

Interop

gradysim.encapsulator.interop

Wraps the running protocol and prepares it for integration with the OMNeT++, in integrated-mode. It is injected with a InteropProvider instance that provides it with the necessary tools to interact with the OMNeT++ environment.

The integration works by collecting all the consequences of the protocol's actions and returning them to the OMNeT++. Consequences are actions that the protocol wants to perform on the environment, such as sending a message or moving the node. The OMNeT++ environment then performs these actions is able to call protocol when it needs to, such as when a message is received or a timer fires.

This encapsulator is instantiated by OMNeT++, the execution flow is only transferred to this code when an envet happens in the OMNeT++ simulation that warrants a call to the protocol. No code changes are necessary to run a protocol in this mode.

Consequence = Tuple[ConsequenceType, Union[CommunicationCommand, MobilityCommand, TimerParams, TrackVariableParams]] module-attribute

A consequence is a tuple of a consequence type and a consequence payload. The payload is a different type for each consequence type

TimerParams = Tuple[str, float] module-attribute

Parameters to a timer consequence. The str is the timer and the float is the timestamp when the timer should fire

TrackVariableParams = Tuple[str, Any] module-attribute

Parameters to a track variable consequence. The str is the variable name and the Any is the variable value

ConsequenceType

Bases: int, Enum

Enum representing the different types of consequences that can be returned by the protocol. Each consequence type serializes a different interaction the protocol wants to perform with the environment.

Source code in gradysim\encapsulator\interop.py
class ConsequenceType(int, Enum):
    """
    Enum representing the different types of consequences that can be returned by the protocol. Each consequence type
    serializes a different interaction the protocol wants to perform with the environment.
    """
    COMMUNICATION = 0
    MOBILITY = 1
    TIMER = 2
    TRACK_VARIABLE = 3

InteropEncapsulator

Bases: IEncapsulator

Encapsulator implementation for the OMNeT++ environment. The encapsulator wraps the protocol and prepares it for integration with the OMNeT++ environment. It is injected with a InteropProvider instance that provides it with the necessary tools to interact with the OMNeT++ environment.

Source code in gradysim\encapsulator\interop.py
class InteropEncapsulator(IEncapsulator):
    """
    Encapsulator implementation for the OMNeT++ environment. The encapsulator wraps the protocol and prepares it for
    integration with the OMNeT++ environment. It is injected with a [InteropProvider][gradysim.encapsulator.interop.InteropProvider]
    instance that provides it with the necessary tools to interact with the OMNeT++ environment.
    """
    provider: InteropProvider

    def __init__(self):
        """
        Creates a new encapsulator instance
        """
        self.provider = InteropProvider()

    def encapsulate(self, protocol: Type[IProtocol]) -> None:
        """
        Instantiates the protocol and injects the provider into it

        Args:
            protocol: The type of protocol being encapsulated
        """
        self.protocol = protocol.instantiate(self.provider)

    def _collect_consequences(self) -> List[Consequence]:
        consequences = self.provider.consequences
        self.provider.consequences = []
        return consequences

    def set_timestamp(self, timestamp: float):
        """
        Sets the current simulation time in seconds. This method is called by the OMNeT++ environment before calling
        the protocol's methods, to make sure the protocol has the correct time.
        """
        self.provider.timestamp = timestamp

    def set_id(self, id: int):
        """
        Sets the node's unique identifier in the simulation. This method is called once by the OMNeT++ environment
        before calling any of the protocol's methods, to make sure the protocol has the correct id.
        """
        self.provider.id = id

    def initialize(self) -> List[Consequence]:
        """
        Initializes the protocol. Called by the OMNeT++ environment before the simulation starts.

        Returns:
            A list of consequences that the protocol wants to perform on the environment from this call
        """
        self.protocol.initialize()
        return self._collect_consequences()

    def handle_timer(self, timer: str) -> List[Consequence]:
        """
        Handles a timer event. Called by the OMNeT++ environment when a timer fires.

        Args:
            timer: The timer that fired

        Returns:
            A list of consequences that the protocol wants to perform on the environment from this call
        """
        self.protocol.handle_timer(timer)
        return self._collect_consequences()

    def handle_packet(self, message: str) -> List[Consequence]:
        """
        Handles a message. Called by the OMNeT++ environment when a message is received.

        Args:
            message: Message being received

        Returns:
            A list of consequences that the protocol wants to perform on the environment from this call
        """
        self.protocol.handle_packet(message)
        return self._collect_consequences()

    def handle_telemetry(self, telemetry: Telemetry) -> List[Consequence]:
        """
        Handles a telemetry event. Called by the OMNeT++ environment when a telemetry event happens.

        Args:
            telemetry: Telemetry event being received

        Returns:
            A list of consequences that the protocol wants to perform on the environment from this call
        """
        self.protocol.handle_telemetry(telemetry)
        return self._collect_consequences()

    def finish(self) -> List[Consequence]:
        """
        Finalizes the protocol. Called by the OMNeT++ environment when the simulation is over.

        Returns:
            A list of consequences that the protocol wants to perform on the environment from this call
        """
        self.protocol.finish()
        return self._collect_consequences()

__init__()

Creates a new encapsulator instance

Source code in gradysim\encapsulator\interop.py
def __init__(self):
    """
    Creates a new encapsulator instance
    """
    self.provider = InteropProvider()

encapsulate(protocol)

Instantiates the protocol and injects the provider into it

Parameters:

Name Type Description Default
protocol Type[IProtocol]

The type of protocol being encapsulated

required
Source code in gradysim\encapsulator\interop.py
def encapsulate(self, protocol: Type[IProtocol]) -> None:
    """
    Instantiates the protocol and injects the provider into it

    Args:
        protocol: The type of protocol being encapsulated
    """
    self.protocol = protocol.instantiate(self.provider)

finish()

Finalizes the protocol. Called by the OMNeT++ environment when the simulation is over.

Returns:

Type Description
List[Consequence]

A list of consequences that the protocol wants to perform on the environment from this call

Source code in gradysim\encapsulator\interop.py
def finish(self) -> List[Consequence]:
    """
    Finalizes the protocol. Called by the OMNeT++ environment when the simulation is over.

    Returns:
        A list of consequences that the protocol wants to perform on the environment from this call
    """
    self.protocol.finish()
    return self._collect_consequences()

handle_packet(message)

Handles a message. Called by the OMNeT++ environment when a message is received.

Parameters:

Name Type Description Default
message str

Message being received

required

Returns:

Type Description
List[Consequence]

A list of consequences that the protocol wants to perform on the environment from this call

Source code in gradysim\encapsulator\interop.py
def handle_packet(self, message: str) -> List[Consequence]:
    """
    Handles a message. Called by the OMNeT++ environment when a message is received.

    Args:
        message: Message being received

    Returns:
        A list of consequences that the protocol wants to perform on the environment from this call
    """
    self.protocol.handle_packet(message)
    return self._collect_consequences()

handle_telemetry(telemetry)

Handles a telemetry event. Called by the OMNeT++ environment when a telemetry event happens.

Parameters:

Name Type Description Default
telemetry Telemetry

Telemetry event being received

required

Returns:

Type Description
List[Consequence]

A list of consequences that the protocol wants to perform on the environment from this call

Source code in gradysim\encapsulator\interop.py
def handle_telemetry(self, telemetry: Telemetry) -> List[Consequence]:
    """
    Handles a telemetry event. Called by the OMNeT++ environment when a telemetry event happens.

    Args:
        telemetry: Telemetry event being received

    Returns:
        A list of consequences that the protocol wants to perform on the environment from this call
    """
    self.protocol.handle_telemetry(telemetry)
    return self._collect_consequences()

handle_timer(timer)

Handles a timer event. Called by the OMNeT++ environment when a timer fires.

Parameters:

Name Type Description Default
timer str

The timer that fired

required

Returns:

Type Description
List[Consequence]

A list of consequences that the protocol wants to perform on the environment from this call

Source code in gradysim\encapsulator\interop.py
def handle_timer(self, timer: str) -> List[Consequence]:
    """
    Handles a timer event. Called by the OMNeT++ environment when a timer fires.

    Args:
        timer: The timer that fired

    Returns:
        A list of consequences that the protocol wants to perform on the environment from this call
    """
    self.protocol.handle_timer(timer)
    return self._collect_consequences()

initialize()

Initializes the protocol. Called by the OMNeT++ environment before the simulation starts.

Returns:

Type Description
List[Consequence]

A list of consequences that the protocol wants to perform on the environment from this call

Source code in gradysim\encapsulator\interop.py
def initialize(self) -> List[Consequence]:
    """
    Initializes the protocol. Called by the OMNeT++ environment before the simulation starts.

    Returns:
        A list of consequences that the protocol wants to perform on the environment from this call
    """
    self.protocol.initialize()
    return self._collect_consequences()

set_id(id)

Sets the node's unique identifier in the simulation. This method is called once by the OMNeT++ environment before calling any of the protocol's methods, to make sure the protocol has the correct id.

Source code in gradysim\encapsulator\interop.py
def set_id(self, id: int):
    """
    Sets the node's unique identifier in the simulation. This method is called once by the OMNeT++ environment
    before calling any of the protocol's methods, to make sure the protocol has the correct id.
    """
    self.provider.id = id

set_timestamp(timestamp)

Sets the current simulation time in seconds. This method is called by the OMNeT++ environment before calling the protocol's methods, to make sure the protocol has the correct time.

Source code in gradysim\encapsulator\interop.py
def set_timestamp(self, timestamp: float):
    """
    Sets the current simulation time in seconds. This method is called by the OMNeT++ environment before calling
    the protocol's methods, to make sure the protocol has the correct time.
    """
    self.provider.timestamp = timestamp

InteropProvider

Bases: IProvider

Provider implementation for the OMNeT++ environment. The provider is injected into the protocol encapsulator and provides it with the necessary tools to interact with the environment.

Source code in gradysim\encapsulator\interop.py
class InteropProvider(IProvider):
    """
    Provider implementation for the OMNeT++ environment. The provider is injected into the protocol encapsulator and
    provides it with the necessary tools to interact with the environment.
    """
    consequences: List[Consequence]
    timestamp: int
    id: int

    def __init__(self):
        """
        Creates a new provider instance
        """
        self.consequences = []
        self.timestamp = 0
        self.id = 0
        self.tracked_variables = \
            _TrackedVariableContainer(lambda key, value: self.consequences.append((ConsequenceType.TRACK_VARIABLE,
                                                                                   (key, value))))

    def send_communication_command(self, command: CommunicationCommand) -> None:
        """
        Adds a communication command to the list of consequences

        Args:
            command: Command being issued
        """
        self.consequences.append((ConsequenceType.COMMUNICATION, command))

    def send_mobility_command(self, command: MobilityCommand) -> None:
        """
        Adds a mobility command to the list of consequences

        Args:
            command: Command being issued
        """
        self.consequences.append((ConsequenceType.MOBILITY, command))

    def schedule_timer(self, timer: str, timestamp: float) -> None:
        """
        Adds a timer consequence to the list of consequences

        Args:
            timer: The timer that will be fired
            timestamp: The timestamp in simulation seconds when the timer will fire
        """
        self.consequences.append((ConsequenceType.TIMER, (timer, timestamp)))

    def cancel_timer(self, timer: str) -> None:
        raise NotImplementedError("Canceling timers is not supported in the OMNeT++ environment. "
                                  "If you need this feature, please show your interest by opening an issue at "
                                  "the github repository.")

    def current_time(self) -> int:
        """
        Returns the current simulation time in seconds

        Returns: The current simulation time in seconds
        """
        return self.timestamp

    def get_id(self) -> int:
        """
        Returns the node's unique identifier in the simulation

        Returns:
            The node's unique identifier in the simulation
        """
        return self.id

__init__()

Creates a new provider instance

Source code in gradysim\encapsulator\interop.py
def __init__(self):
    """
    Creates a new provider instance
    """
    self.consequences = []
    self.timestamp = 0
    self.id = 0
    self.tracked_variables = \
        _TrackedVariableContainer(lambda key, value: self.consequences.append((ConsequenceType.TRACK_VARIABLE,
                                                                               (key, value))))

current_time()

Returns the current simulation time in seconds

Returns: The current simulation time in seconds

Source code in gradysim\encapsulator\interop.py
def current_time(self) -> int:
    """
    Returns the current simulation time in seconds

    Returns: The current simulation time in seconds
    """
    return self.timestamp

get_id()

Returns the node's unique identifier in the simulation

Returns:

Type Description
int

The node's unique identifier in the simulation

Source code in gradysim\encapsulator\interop.py
def get_id(self) -> int:
    """
    Returns the node's unique identifier in the simulation

    Returns:
        The node's unique identifier in the simulation
    """
    return self.id

schedule_timer(timer, timestamp)

Adds a timer consequence to the list of consequences

Parameters:

Name Type Description Default
timer str

The timer that will be fired

required
timestamp float

The timestamp in simulation seconds when the timer will fire

required
Source code in gradysim\encapsulator\interop.py
def schedule_timer(self, timer: str, timestamp: float) -> None:
    """
    Adds a timer consequence to the list of consequences

    Args:
        timer: The timer that will be fired
        timestamp: The timestamp in simulation seconds when the timer will fire
    """
    self.consequences.append((ConsequenceType.TIMER, (timer, timestamp)))

send_communication_command(command)

Adds a communication command to the list of consequences

Parameters:

Name Type Description Default
command CommunicationCommand

Command being issued

required
Source code in gradysim\encapsulator\interop.py
def send_communication_command(self, command: CommunicationCommand) -> None:
    """
    Adds a communication command to the list of consequences

    Args:
        command: Command being issued
    """
    self.consequences.append((ConsequenceType.COMMUNICATION, command))

send_mobility_command(command)

Adds a mobility command to the list of consequences

Parameters:

Name Type Description Default
command MobilityCommand

Command being issued

required
Source code in gradysim\encapsulator\interop.py
def send_mobility_command(self, command: MobilityCommand) -> None:
    """
    Adds a mobility command to the list of consequences

    Args:
        command: Command being issued
    """
    self.consequences.append((ConsequenceType.MOBILITY, command))