diff --git a/docs/_static/node_nic_link_component_diagram.png b/docs/_static/node_nic_link_component_diagram.png new file mode 100644 index 00000000..00a3d939 Binary files /dev/null and b/docs/_static/node_nic_link_component_diagram.png differ diff --git a/docs/index.rst b/docs/index.rst index c0e7a007..a75dd8e5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -36,7 +36,6 @@ Head over to the :ref:`getting-started` page to install and setup PrimAITE! .. toctree:: :maxdepth: 8 :caption: Contents: - :hidden: source/getting_started source/about diff --git a/docs/source/simulation.rst b/docs/source/simulation.rst index 1620f6ba..0af6c89f 100644 --- a/docs/source/simulation.rst +++ b/docs/source/simulation.rst @@ -2,9 +2,18 @@ © Crown-owned copyright 2023, Defence Science and Technology Laboratory UK -Simulation Strucutre -==================== -The simulation is made up of many smaller components which are related to each other in a tree-like structure. At the top level, there is an object called the ``SimulationController`` _(doesn't exist yet)_, which has a physical network and a software controller for managing software and users. +Simulation +========== -Each node of the simulation 'tree' has responsibility for creating, deleting, and updating its direct descendants. +.. TODO:: Add spiel here about what the simulation is. + + +Contents +######## + +.. toctree:: + :maxdepth: 8 + + simulation_structure + simulation_components/network/physical_layer diff --git a/docs/source/simulation_components/network/physical_layer.rst b/docs/source/simulation_components/network/physical_layer.rst new file mode 100644 index 00000000..2d942847 --- /dev/null +++ b/docs/source/simulation_components/network/physical_layer.rst @@ -0,0 +1,75 @@ +.. only:: comment + + © Crown-owned copyright 2023, Defence Science and Technology Laboratory UK + +Physical Layer +============== + +The physical layer components are mode of a ``NIC`` (Network Interface Card) and a ``Link``. These components allow +modelling of layer 1 (physical layer) in the OSI model. + +NIC +### +The ``NIC`` class is a realistic model of a Network Interface Card. The ``NIC`` acts as the interface between the +``Node`` and the ``Link``. + +NICs have the following attributes: + +- **ip_address:** The IPv4 address assigned to the NIC. +- **subnet_mask:** The subnet mask assigned to the NIC. +- **gateway:** The default gateway IP address for forwarding network traffic to other networks. +- **mac_address:** The MAC address of the NIC. Defaults to a randomly set MAC address. +- **speed:** The speed of the NIC in Mbps (default is 100 Mbps). +- **mtu:** The Maximum Transmission Unit (MTU) of the NIC in Bytes, representing the largest data packet size it can handle without fragmentation (default is 1500 B). +- **wake_on_lan:** Indicates if the NIC supports Wake-on-LAN functionality. +- **dns_servers:** List of IP addresses of DNS servers used for name resolution. +- **connected_link:** The link to which the NIC is connected. +- **enabled:** Indicates whether the NIC is enabled. + +**Basic Example** + +.. code-block:: python + + nic1 = NIC( + ip_address="192.168.1.100", + subnet_mask="255.255.255.0", + gateway="192.168.1.1" + ) + +Link +#### + +The ``Link`` class represents a physical link between two network endpoints. + +Links have the following attributes: + +- **endpoint_a:** The first NIC connected to the Link. +- **endpoint_b:** The second NIC connected to the Link. +- **bandwidth:** The bandwidth of the Link in Mbps (default is 100 Mbps). +- **current_load:** The current load on the link in Mbps. + +**Basic Example** + +.. code-block:: python + + nic1 = NIC( + ip_address="192.168.1.100", + subnet_mask="255.255.255.0", + gateway="192.168.1.1" + ) + nic1 = NIC( + ip_address="192.168.1.101", + subnet_mask="255.255.255.0", + gateway="192.168.1.1" + ) + + link = Link( + endpoint_a=nic1, + endpoint_b=nic2, + bandwidth=1000 + ) + +Link, NIC, Node Interface +######################### + +.. image:: ../../../_static/node_nic_link_component_diagram.png diff --git a/docs/source/simulation_structure.rst b/docs/source/simulation_structure.rst new file mode 100644 index 00000000..65373a72 --- /dev/null +++ b/docs/source/simulation_structure.rst @@ -0,0 +1,13 @@ +.. only:: comment + + © Crown-owned copyright 2023, Defence Science and Technology Laboratory UK + + +Simulation Structure +==================== + +The simulation is made up of many smaller components which are related to each other in a tree-like structure. At the +top level, there is an object called the ``SimulationController`` _(doesn't exist yet)_, which has a physical network +and a software controller for managing software and users. + +Each node of the simulation 'tree' has responsibility for creating, deleting, and updating its direct descendants. diff --git a/src/primaite/simulator/core.py b/src/primaite/simulator/core.py index 5b9bea1f..c3130116 100644 --- a/src/primaite/simulator/core.py +++ b/src/primaite/simulator/core.py @@ -37,7 +37,7 @@ class SimComponent(BaseModel): possible_actions = self._possible_actions() if action[0] in possible_actions: # take the first element off the action list and pass the remaining arguments to the corresponding action - # funciton + # function possible_actions[action.pop(0)](action) else: raise ValueError(f"{self.__class__.__name__} received invalid action {action}") @@ -45,7 +45,7 @@ class SimComponent(BaseModel): def _possible_actions(self) -> Dict[str, Callable[[List[str]], None]]: return {} - def apply_timestep(self) -> None: + def apply_timestep(self, timestep: int) -> None: """ Apply a timestep evolution to this component. @@ -53,3 +53,11 @@ class SimComponent(BaseModel): sending data. """ pass + + def reset_component_for_episode(self): + """ + Reset this component to its original state for a new episode. + + Override this method with anything that needs to happen within the component for it to be reset. + """ + pass diff --git a/src/primaite/simulator/network/physical_layer.py b/src/primaite/simulator/network/physical_layer.py index 6d268b59..2bc5c2b8 100644 --- a/src/primaite/simulator/network/physical_layer.py +++ b/src/primaite/simulator/network/physical_layer.py @@ -43,76 +43,6 @@ def generate_mac_address(oui: Optional[str] = None) -> str: return ":".join(f"{b:02x}" for b in mac) -class Link(SimComponent): - """ - Represents a network link between two network interface cards (NICs). - - :param endpoint_a: The first NIC connected to the Link. - :type endpoint_a: NIC - :param endpoint_b: The second NIC connected to the Link. - :type endpoint_b: NIC - :param bandwidth: The bandwidth of the Link in Mbps (default is 100 Mbps). - :type bandwidth: int - """ - - endpoint_a: NIC - endpoint_b: NIC - bandwidth: int = 100 - current_load: int = 0 - - def model_post_init(self, __context: Any) -> None: - """ - Ensure that endpoint_a and endpoint_b are not the same :class:`~primaite.simulator.network.physical_layer.NIC`. - - :raises ValueError: If endpoint_a and endpoint_b are the same NIC. - """ - if self.endpoint_a == self.endpoint_b: - msg = "endpoint_a and endpoint_b cannot be the same NIC" - _LOGGER.error(msg) - raise ValueError(msg) - self.endpoint_a.connect_link(self) - self.endpoint_b.connect_link(self) - - def send_frame(self, sender_nic: NIC, frame): - """ - Send a network frame from one NIC to another connected NIC. - - :param sender_nic: The NIC sending the frame. - :type sender_nic: NIC - :param frame: The network frame to be sent. - :type frame: Frame - """ - pass - - def receive_frame(self, sender_nic: NIC, frame): - """ - Receive a network frame from a connected NIC. - - :param sender_nic: The NIC sending the frame. - :type sender_nic: NIC - :param frame: The network frame being received. - :type frame: Frame - """ - pass - - def describe_state(self) -> Dict: - """ - Get the current state of the Libk as a dict. - - :return: A dict containing the current state of the Link. - """ - pass - - def apply_action(self, action: str): - """ - Apply an action to the Link. - - :param action: The action to be applied. - :type action: str - """ - pass - - class NIC(SimComponent): """ Models a Network Interface Card (NIC) in a computer or network device. @@ -121,13 +51,11 @@ class NIC(SimComponent): :param subnet_mask: The subnet mask assigned to the NIC. :param gateway: The default gateway IP address for forwarding network traffic to other networks. :param mac_address: The MAC address of the NIC. Defaults to a randomly set MAC address. - :param speed: The speed of the NIC in Mbps. + :param speed: The speed of the NIC in Mbps (default is 100 Mbps). :param mtu: The Maximum Transmission Unit (MTU) of the NIC in Bytes, representing the largest data packet size it - can handle without fragmentation. + can handle without fragmentation (default is 1500 B). :param wake_on_lan: Indicates if the NIC supports Wake-on-LAN functionality. :param dns_servers: List of IP addresses of DNS servers used for name resolution. - :param connected_link: The link to which the NIC is connected (default is None). - :param enabled: Indicates whether the NIC is enabled. """ ip_address: Union[str, IPv4Address] @@ -204,7 +132,11 @@ class NIC(SimComponent): def disconnect_link(self): """Disconnect the NIC from the connected :class:`~primaite.simulator.network.physical_layer.Link`.""" - pass + if self.connected_link.endpoint_a == self: + self.connected_link.endpoint_a = None + if self.connected_link.endpoint_b == self: + self.connected_link.endpoint_b = None + self.connected_link = None def add_dns_server(self, ip_address: IPv4Address): """ @@ -260,3 +192,77 @@ class NIC(SimComponent): :type action: str """ pass + + +class Link(SimComponent): + """ + Represents a network link between two network interface cards (NICs). + + :param endpoint_a: The first NIC connected to the Link. + :type endpoint_a: NIC + :param endpoint_b: The second NIC connected to the Link. + :type endpoint_b: NIC + :param bandwidth: The bandwidth of the Link in Mbps (default is 100 Mbps). + :type bandwidth: int + """ + + endpoint_a: NIC + "The first NIC connected to the Link." + endpoint_b: NIC + "The second NIC connected to the Link." + bandwidth: int = 100 + "The bandwidth of the Link in Mbps (default is 100 Mbps)." + current_load: int = 0 + "The current load on the link in Mbps." + + def model_post_init(self, __context: Any) -> None: + """ + Ensure that endpoint_a and endpoint_b are not the same :class:`~primaite.simulator.network.physical_layer.NIC`. + + :raises ValueError: If endpoint_a and endpoint_b are the same NIC. + """ + if self.endpoint_a == self.endpoint_b: + msg = "endpoint_a and endpoint_b cannot be the same NIC" + _LOGGER.error(msg) + raise ValueError(msg) + self.endpoint_a.connect_link(self) + self.endpoint_b.connect_link(self) + + def send_frame(self, sender_nic: NIC, frame): + """ + Send a network frame from one NIC to another connected NIC. + + :param sender_nic: The NIC sending the frame. + :type sender_nic: NIC + :param frame: The network frame to be sent. + :type frame: Frame + """ + pass + + def receive_frame(self, sender_nic: NIC, frame): + """ + Receive a network frame from a connected NIC. + + :param sender_nic: The NIC sending the frame. + :type sender_nic: NIC + :param frame: The network frame being received. + :type frame: Frame + """ + pass + + def describe_state(self) -> Dict: + """ + Get the current state of the Libk as a dict. + + :return: A dict containing the current state of the Link. + """ + pass + + def apply_action(self, action: str): + """ + Apply an action to the Link. + + :param action: The action to be applied. + :type action: str + """ + pass