diff --git a/definitions.py b/definitions.py new file mode 100644 index 0000000..af1febd --- /dev/null +++ b/definitions.py @@ -0,0 +1,22 @@ +"""This file contains definitions that will be used throughout the entire +project +""" + +from __future__ import annotations + +from pathlib import Path +from enum import Enum, auto + +PROJECT_ROOT = Path(__file__).parent + + +class Subsystems(Enum): + """enum of the simulated subsystems.""" + + ADCS = auto() + DFGM = auto() + DUMMY = auto() + EPS = auto() + IRIS = auto() + UHF_TO_GS = auto() + UHF_TO_OBC = auto() diff --git a/docs/definitions.md b/docs/definitions.md new file mode 100644 index 0000000..e0ddf2e --- /dev/null +++ b/docs/definitions.md @@ -0,0 +1,21 @@ +# Definitions Module + +## Overview + +This module contains immutable definitions that are shared +throughout the project. Things such as the root path of the +project directory on any file system, and `enum` that +represents the different simulated subsystems. + +## Content + +### PROJECT_ROOT + +This is a `pathlib.Path` object that contains the path to +the root of the project. This value is dynamic, as it +will update between machines. + +### Subsystems + +This `enum` contains the fixed grouping of subsystems +supported by this project. diff --git a/docs/simconfig.md b/docs/simconfig.md new file mode 100644 index 0000000..8ec411f --- /dev/null +++ b/docs/simconfig.md @@ -0,0 +1,24 @@ +# Simconfig Module + +## Overview + +This is a helper library that can read the `simulated_config.ini` +configuration files. + +## Usage + +### Obtaining root config + +The following Python snippet is how the configuration +dictionary can be obtained from the configuration file +in the root. + +```python +from simconfig import get_simulated_config +from definitions import TEST_PATH, Subsystems + +config_dictionary = get_simulated_config(TEST_PATH, Subsystems.ADCS) + +config_dictionary +# {"port": "61200"} +``` diff --git a/docs/testing.md b/docs/testing.md new file mode 100644 index 0000000..df05b91 --- /dev/null +++ b/docs/testing.md @@ -0,0 +1,23 @@ +# Testing + +## Dependencies + +| Module name | version | +| --- | --- | +| Python | >=3.10 | +| pytest | | + +## Running Tests + +To run tests in this repository, navigate to the project root, and run the +following command: +```bash +pytest +``` + +## Writing Tests + +Simply add a new file with a name that starts with the word `test` in the +`test` directory. You do not need to import `pytest`, only the module +that you are trying to test. To get a quick overview of python modules, +refer to [this document](https://docs.python.org/3.10/tutorial/modules.html). diff --git a/simconfig/__init__.py b/simconfig/__init__.py new file mode 100644 index 0000000..42a870f --- /dev/null +++ b/simconfig/__init__.py @@ -0,0 +1,52 @@ +"""This library provides the tools required to read configuration from the filesystem. +Using the INI format as it has broad compatabilities. +""" + +from __future__ import annotations + +from configparser import ConfigParser +from pathlib import Path + +from definitions import Subsystems + + +def get_simulated_config( + config_file_path: Path, subsystem: Subsystems +) -> dict[str, str]: + """This function will read the configuration + file defined by config_file_path and obtain + the configuration for the defined subsystem. + """ + config = ConfigParser() + config.read(config_file_path) + + subsystem_name = subsystem.name.lower() + return dict(config[subsystem_name]) + + +def make_fresh_config(file_path: Path) -> None: + """This function creates a new subsystem_config.ini file, in the case that + a reset must occur. + + Will create configuration for all the subsystems that are defined in the + definitions.py file. + """ + config = ConfigParser() + for index, subsystem in enumerate(Subsystems): + config[subsystem.name.lower()] = _make_default_subsystem_values(index) + + with open(file_path, "w", encoding="utf-8") as configfile: + config.write(configfile) + + +def _make_default_subsystem_values(i: int) -> dict[str, str]: + """This makes the default port for the simulated subsystem. Takes a port + offset as parameter. + + NOTE: + - Left as a function for potential future extensions. + - The port is in the ephemeral range. + """ + sim_port = 61200 + i + + return {"port": str(sim_port)} diff --git a/simulated_config.ini b/simulated_config.ini new file mode 100644 index 0000000..c4a79f9 --- /dev/null +++ b/simulated_config.ini @@ -0,0 +1,21 @@ +[adcs] +port = 1803 + +[dfgm] +port = 1802 + +[dummy] +port = 1807 + +[eps] +port = 61201 + +[iris] +port = 1806 + +[uhf_to_gs] +port = 1235 + +[uhf_to_obc] +port = 1234 + diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_sim_config.py b/test/test_sim_config.py new file mode 100644 index 0000000..8393817 --- /dev/null +++ b/test/test_sim_config.py @@ -0,0 +1,66 @@ +"""Tests for the config reader helper library""" + +from __future__ import annotations + +from configparser import ConfigParser + +import definitions +from simconfig import get_simulated_config, make_fresh_config + +TEST_PATH = definitions.PROJECT_ROOT / "test.ini" + + +def test_make_default_simconfig() -> None: + """Tests if we could make a clean configuration file""" + checks = tuple(x.name.lower() for x in definitions.Subsystems) + make_fresh_config(TEST_PATH) + read_config = ConfigParser() + read_config.read(TEST_PATH) + + for index, subsystem in enumerate(checks): + config_port = read_config[subsystem]["port"] + # There is a hardcoded testing value for the port. At risk of change + assert int(config_port) == 61200 + index, f"{subsystem}" + + TEST_PATH.unlink() + + +def test_obtain_default_ports() -> None: + """Testing if we could read from the config file, and + obtain the ports""" + checks = tuple(x for x in definitions.Subsystems) + make_fresh_config(TEST_PATH) + read_config = ConfigParser() + read_config.read(TEST_PATH) + + for index, subsystem in enumerate(checks): + config_dict = get_simulated_config(TEST_PATH, subsystem) + # There is a hardcoded testing value for the port. At risk of change + assert ( + "port" in config_dict + ), f"Missing port for {subsystem.name}, a required field" + assert int(config_dict["port"]) == 61200 + index, f"{subsystem}" + + TEST_PATH.unlink() + + +def test_root_defaults() -> None: + """Tests the default value of the config file at the root""" + config_path = definitions.PROJECT_ROOT / "simulated_config.ini" + expected = { + definitions.Subsystems.ADCS: 1803, + definitions.Subsystems.UHF_TO_GS: 1235, + definitions.Subsystems.UHF_TO_OBC: 1234, + definitions.Subsystems.DFGM: 1802, + definitions.Subsystems.IRIS: 1806, + definitions.Subsystems.DUMMY: 1807, + } + + for subsys, expected_port in expected.items(): + found_port = get_simulated_config(config_path, subsys)["port"] + assert ( + found_port is not None + ), "Expected port for {subsys.name} to exist, but found None" + assert ( + int(found_port) == expected_port + ), "Expected to see {expected_port}, but got {found_port}"