From 5bee02ebc61333e7125289c69e89ad316b5bde90 Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 12:51:35 -0400 Subject: [PATCH 01/25] Added pyright and black settings to pyproject.toml --- pyproject.toml | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..6d77b65 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,64 @@ +[tool.pyright] + +# Strict checking +strictListInference = true +strictDictionaryInference = true +strictSetInference = true + +reportPropertyTypeMismatch = true # Setter types must match getter types +reportFunctionMemberAccess = true + +# Reporting missing type annotations +reportMissingParameterType = "error" # All parameters must be typed +reportMissingTypeArgument = "error" + +reportUntypedFunctionDecorator = "error" +reportUntypedClassDecorator = "error" +reportUntypedBaseClass = "error" +reportUntypedNamedTuple = "error" + +reportUnknownMemberType = "error" +reportUnknownVariableType = "error" +reportUnknownArgumentType = "error" +reportUnknownParamaterType = "error" + +# Report bad ignores +reportUnnecessaryTypeIgnoreComment = "error" + +# Best practices +reportSelfClsParameterName = "error" +reportConstantRedefinition = "error" +reportUninitializedInstanceVariable = "error" +reportImportCycles = "warning" +reportDeprecated = "error" +reportInvalidTypeVarUse = "warning" + +reportUnnecessaryIsInstance = "warning" +reportUnnecessaryCast = "warning" +reportUnnecessaryComparison = "warning" +reportUnnecessaryContains = "warning" +reportAssertAlwaysTrue = "warning" +reportImplicitStringConcatenation = "warning" +reportMatchNotExhaustive = "warning" + +reportUnusedImport = "warning" +reportUnusedClass = "warning" +reportUnusedFunction = "warning" +reportUnusedVariable = "warning" +reportUnusedCallResult = "warning" +reportUnusedExpression = "warning" +reportDuplicateImport = "warning" + +reportPrivateUsage = "warning" +reportPrivateImportUsage = "warning" +reportShadowedImports = "error" + +reportIncompatibleMethodOverride = "warning" +reportIncompatibleVariableOverride = "warning" +reportInconsistentConstructor = "warning" +reportOverlappingOverload = "warning" +reportMissingSuperCall = "warning" + + +[tool.black] +line-length = 120 From 7d272a41474468c448010d9afdce6973793236c4 Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 12:55:03 -0400 Subject: [PATCH 02/25] Updated requirements.txt to include latest version of a few modules. --- requirements.txt | Bin 514 -> 704 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/requirements.txt b/requirements.txt index 27d11ddc3004e7113dc07d77d190922c8dab56d0..1a1a589ad015654026d33e31dccc75c27d7902db 100644 GIT binary patch delta 205 zcmZo-IlwwWK{bgXhar(6nIW5jmw^jN=P+b~dA1C;3>FM}42D2#JW)DyVqKeTE<+_l z0TAmlq%u@6lmN**hGL-ld=Nd^kWpAX56Dgdii2dzfF{_2^;=B*t?Ql$)Q|yGUI3I# z21^-$jR#p^2)4Wc?35CQG@v0x47osqGJ$k4SgkQol_^N&WJShe&LW^pI?%L|$=!_l JlTR>O0RV#nDC__L delta 56 zcmX@W+Qc$JVWL+6uPuWugBgP!g8>LnoY_9PfKg@gBgUM`2~28WNpqlt5fB?rZe_9t E0F*TiNB{r; From b59055e3df17f40b0980d4b91f387b12b830857f Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 13:10:55 -0400 Subject: [PATCH 03/25] Solved type errors in test_config --- tests/test_config.py | 92 ++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index 32e60c8..259527e 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -10,7 +10,7 @@ # Fixtures @pytest.fixture() -def def_radio_params(): +def def_radio_params() -> dict[str, str | int | bool]: return { "modulation": "lora", "frequency": 433_050_000, @@ -26,17 +26,17 @@ def def_radio_params(): @pytest.fixture() -def callsigns(): +def callsigns() -> dict[str, str]: return {"VA3TEST": "Some Body", "VA3INI": "linguini1"} @pytest.fixture() -def config(def_radio_params, callsigns): +def config(def_radio_params: dict[str, str | int | bool], callsigns: dict[str, str]): return {"radio_parameters": def_radio_params, "approved_callsigns": callsigns} # Test radio parameters -def test_radio_params_default(def_radio_params): +def test_radio_params_default(def_radio_params: dict[str, str | int | bool]): """Tests that the RadioParameters object default constructor initializes all values to the correct defaults.""" params = RadioParameters() assert params.modulation.value == def_radio_params.get("modulation") @@ -47,7 +47,7 @@ def test_radio_params_default(def_radio_params): assert params.preamble_len == def_radio_params.get("preamble_len") assert params.cyclic_redundancy == def_radio_params.get("cyclic_redundancy") assert params.iqi == def_radio_params.get("iqi") - assert params.sync_word == def_radio_params.get("sync_word")[2:] + assert params.sync_word == def_radio_params.get("sync_word")[2:] # type: ignore def test_radio_params_partial_default(): @@ -61,7 +61,7 @@ def test_radio_params_partial_default(): assert params.coding_rate == CodingRates.FOUR_FIFTHS -def test_radio_params_default_json(def_radio_params): +def test_radio_params_default_json(def_radio_params: dict[str, str | int | bool]): """ Tests that the RadioParameters object's from_json method initializes all values to the correct defaults when given no radio_params JSON object. @@ -75,7 +75,7 @@ def test_radio_params_default_json(def_radio_params): assert params.preamble_len == def_radio_params.get("preamble_len") assert params.cyclic_redundancy == def_radio_params.get("cyclic_redundancy") assert params.iqi == def_radio_params.get("iqi") - assert params.sync_word == def_radio_params.get("sync_word")[2:] + assert params.sync_word == def_radio_params.get("sync_word")[2:] # type: ignore def test_radio_params_partial_defaults_json(): @@ -101,19 +101,19 @@ def test_radio_params_invalid_arguments(): """ with pytest.raises(ValueError): - RadioParameters(frequency=42) + _ = RadioParameters(frequency=42) with pytest.raises(ValueError): - RadioParameters(power=18) + _ = RadioParameters(power=18) with pytest.raises(ValueError): - RadioParameters(spread_factor=6) + _ = RadioParameters(spread_factor=6) with pytest.raises(ValueError): - RadioParameters(preamble_len=65536) + _ = RadioParameters(preamble_len=65536) with pytest.raises(ValueError): - RadioParameters(sync_word="0x101") + _ = RadioParameters(sync_word="0x101") def test_radio_params_invalid_arguments_json(): @@ -123,19 +123,19 @@ def test_radio_params_invalid_arguments_json(): """ with pytest.raises(ValueError): - RadioParameters.from_json({"frequency": 42}) + _ = RadioParameters.from_json({"frequency": 42}) with pytest.raises(ValueError): - RadioParameters.from_json({"power": 18}) + _ = RadioParameters.from_json({"power": 18}) with pytest.raises(ValueError): - RadioParameters.from_json({"spread_factor": 6}) + _ = RadioParameters.from_json({"spread_factor": 6}) with pytest.raises(ValueError): - RadioParameters.from_json({"preamble_len": 65536}) + _ = RadioParameters.from_json({"preamble_len": 65536}) with pytest.raises(ValueError): - RadioParameters.from_json({"sync_word": "0x101"}) + _ = RadioParameters.from_json({"sync_word": "0x101"}) def test_radio_params_range_edges(): @@ -144,19 +144,19 @@ def test_radio_params_range_edges(): does not raise any exceptions. """ - RadioParameters(power=-3) - RadioParameters(power=16) + _ = RadioParameters(power=-3) + _ = RadioParameters(power=16) - RadioParameters(sync_word="0x0") - RadioParameters(sync_word="0x100") + _ = RadioParameters(sync_word="0x0") + _ = RadioParameters(sync_word="0x100") - RadioParameters(preamble_len=0) - RadioParameters(preamble_len=65_535) + _ = RadioParameters(preamble_len=0) + _ = RadioParameters(preamble_len=65_535) - RadioParameters(frequency=433_050_000) - RadioParameters(frequency=434_790_000) - RadioParameters(frequency=863_000_000) - RadioParameters(frequency=870_000_000) + _ = RadioParameters(frequency=433_050_000) + _ = RadioParameters(frequency=434_790_000) + _ = RadioParameters(frequency=863_000_000) + _ = RadioParameters(frequency=870_000_000) def test_radio_params_outside_range_edges(): @@ -166,43 +166,43 @@ def test_radio_params_outside_range_edges(): """ with pytest.raises(ValueError): - RadioParameters(power=-4) + _ = RadioParameters(power=-4) assert RadioParameters(power=-3).power == -3 with pytest.raises(ValueError): - RadioParameters(power=17) + _ = RadioParameters(power=17) assert RadioParameters(power=16).power == 16 with pytest.raises(ValueError): - RadioParameters(sync_word="0x101") + _ = RadioParameters(sync_word="0x101") assert RadioParameters(sync_word="0x100").sync_word == "100" with pytest.raises(ValueError): - RadioParameters(preamble_len=-1) + _ = RadioParameters(preamble_len=-1) assert RadioParameters(preamble_len=0).preamble_len == 0 with pytest.raises(ValueError): - RadioParameters(preamble_len=65_536) + _ = RadioParameters(preamble_len=65_536) assert RadioParameters(preamble_len=65_535).preamble_len == 65_535 with pytest.raises(ValueError): - RadioParameters(frequency=433_049_999) + _ = RadioParameters(frequency=433_049_999) assert RadioParameters(frequency=433_050_000).frequency == 433_050_000 with pytest.raises(ValueError): - RadioParameters(frequency=434_790_001) + _ = RadioParameters(frequency=434_790_001) assert RadioParameters(frequency=434_790_000).frequency == 434_790_000 with pytest.raises(ValueError): - RadioParameters(frequency=862_999_999) + _ = RadioParameters(frequency=862_999_999) assert RadioParameters(frequency=863_000_000).frequency == 863_000_000 with pytest.raises(ValueError): - RadioParameters(frequency=870_000_001) + _ = RadioParameters(frequency=870_000_001) assert RadioParameters(frequency=870_000_000).frequency == 870_000_000 -def test_config_defaults(def_radio_params, callsigns): +def test_config_defaults(def_radio_params: dict[str, str | int | bool], callsigns: dict[str, str]): """Tests that initializing an empty Config object results in the correct default values.""" config = Config(approved_callsigns=callsigns) @@ -215,11 +215,11 @@ def test_config_defaults(def_radio_params, callsigns): assert config.radio_parameters.preamble_len == def_radio_params.get("preamble_len") assert config.radio_parameters.cyclic_redundancy == def_radio_params.get("cyclic_redundancy") assert config.radio_parameters.iqi == def_radio_params.get("iqi") - assert config.radio_parameters.sync_word == def_radio_params.get("sync_word")[2:] + assert config.radio_parameters.sync_word == def_radio_params.get("sync_word")[2:] # type: ignore assert config.approved_callsigns == callsigns -def test_config_defaults_json(def_radio_params, callsigns): +def test_config_defaults_json(def_radio_params: dict[str, str | int | bool], callsigns: dict[str, str]): """Tests that initializing a Config object from an empty JSON object results in the correct default values.""" config = Config.from_json({"approved_callsigns": callsigns}) @@ -232,7 +232,7 @@ def test_config_defaults_json(def_radio_params, callsigns): assert config.radio_parameters.preamble_len == def_radio_params.get("preamble_len") assert config.radio_parameters.cyclic_redundancy == def_radio_params.get("cyclic_redundancy") assert config.radio_parameters.iqi == def_radio_params.get("iqi") - assert config.radio_parameters.sync_word == def_radio_params.get("sync_word")[2:] + assert config.radio_parameters.sync_word == def_radio_params.get("sync_word")[2:] # type: ignore assert config.approved_callsigns == callsigns @@ -240,17 +240,17 @@ def test_no_callsigns(): """Tests that a Config object initialized with no callsigns raises a ValueError.""" with pytest.raises(ValueError): - Config() + _ = Config() def test_no_callsigns_json(): """Tests that Config object initialized with a JSON object containing no approved callsigns raises a ValueError.""" with pytest.raises(ValueError): - Config.from_json(dict()) + _ = Config.from_json(dict()) -def test_config_from_json(config): +def test_config_from_json(config: dict[str, dict[str, str | int | bool]]): """Test that initializing a Config object from a valid JSON config results in the correct values being set.""" cfg = Config.from_json(config) @@ -264,11 +264,11 @@ def test_config_from_json(config): assert cfg.radio_parameters.preamble_len == rparams.get("preamble_len") assert cfg.radio_parameters.cyclic_redundancy == rparams.get("cyclic_redundancy") assert cfg.radio_parameters.iqi == rparams.get("iqi") - assert cfg.radio_parameters.sync_word == rparams.get("sync_word")[2:] + assert cfg.radio_parameters.sync_word == rparams.get("sync_word")[2:] # type: ignore assert cfg.approved_callsigns == config["approved_callsigns"] -def test_load_config(config): +def test_load_config(config: dict[str, dict[str, str | int | bool]]): """Test that loading a Config object from a valid JSON config file results in the correct values being set.""" # Setup @@ -285,7 +285,7 @@ def test_load_config(config): assert cfg.radio_parameters.preamble_len == rparams.get("preamble_len") assert cfg.radio_parameters.cyclic_redundancy == rparams.get("cyclic_redundancy") assert cfg.radio_parameters.iqi == rparams.get("iqi") - assert cfg.radio_parameters.sync_word == rparams.get("sync_word")[2:] + assert cfg.radio_parameters.sync_word == rparams.get("sync_word")[2:] # type: ignore assert cfg.approved_callsigns == config["approved_callsigns"] # Teardown From 964c15b6db5ac2262a14ae959b0aeb560c023256 Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 13:12:19 -0400 Subject: [PATCH 04/25] Solved type_errors in test_websocket_config --- tests/test_websocket_commands.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_websocket_commands.py b/tests/test_websocket_commands.py index 8d671f0..cd8aba1 100644 --- a/tests/test_websocket_commands.py +++ b/tests/test_websocket_commands.py @@ -31,7 +31,7 @@ def test_unknown_command_raises_error() -> None: parameters = cmd.split_command_string(command) with pytest.raises(cmd.WebsocketCommandNotFound): - cmd.parse(parameters, cmd.WebsocketCommand) + _ = cmd.parse(parameters, cmd.WebsocketCommand) def test_wrong_enum_raises_error() -> None: @@ -41,7 +41,7 @@ def test_wrong_enum_raises_error() -> None: parameters = cmd.split_command_string(command) with pytest.raises(cmd.WebsocketCommandNotFound): - cmd.parse(parameters, cmd.WebsocketCommand.REPLAY.value) + _ = cmd.parse(parameters, cmd.WebsocketCommand.REPLAY.value) def test_command_not_found_error_message() -> None: @@ -51,7 +51,7 @@ def test_command_not_found_error_message() -> None: parameters = cmd.split_command_string(command) try: - cmd.parse(parameters, cmd.WebsocketCommand) + _ = cmd.parse(parameters, cmd.WebsocketCommand) except cmd.WebsocketCommandNotFound as error: assert error.command == "halt" assert error.message == "The websocket command 'halt' does not exist." From 2ec43aa2ec4c901dc5ca4881e393a362a69759d6 Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 14:34:34 -0400 Subject: [PATCH 05/25] Solved typing issues in json_data --- modules/telemetry/json_packets.py | 11 ++++---- modules/telemetry/replay.py | 2 +- modules/telemetry/superblock.py | 5 ++-- modules/telemetry/telemetry.py | 47 +++++++++++++++++-------------- modules/websocket/websocket.py | 23 +++++++-------- pyproject.toml | 1 - 6 files changed, 48 insertions(+), 41 deletions(-) diff --git a/modules/telemetry/json_packets.py b/modules/telemetry/json_packets.py index b1512d6..3517df3 100644 --- a/modules/telemetry/json_packets.py +++ b/modules/telemetry/json_packets.py @@ -2,6 +2,7 @@ __author__ = "Matteo Golin" # Imports +from io import BufferedReader import struct from dataclasses import dataclass, field from enum import IntEnum @@ -144,7 +145,7 @@ class ReplayData: state: ReplayState = ReplayState.DNE speed: float = 1.0 last_played_speed: float = 1.0 - mission_files_list: list = field(default_factory=list) + mission_files_list: list[Path] = field(default_factory=list) mission_list: list[MissionEntry] = field(default_factory=list) def __post_init__(self) -> None: @@ -176,7 +177,7 @@ def update_mission_list(self, missions_dir: Path = Path.cwd().joinpath(MISSIONS_ mission_time = 0 # Reads last telemetry block of each flight to get final mission time for flight in mission_sb.flights: - file.seek(flight.first_block * 512) + _ = file.seek(flight.first_block * 512) mission_time += get_last_mission_time(file, flight.num_blocks) # Output mission to mission list @@ -213,7 +214,7 @@ class ParsingException(Exception): pass -def get_last_mission_time(file, num_blocks) -> int: +def get_last_mission_time(file: BufferedReader, num_blocks: int) -> int: """Obtains last recorded telemetry mission time from a flight""" # If flight is empty, return @@ -225,7 +226,7 @@ def get_last_mission_time(file, num_blocks) -> int: while count <= ((num_blocks * 512) - 4): try: - block_header = file.read(4) + block_header: bytes = file.read(4) block_class, _, block_length = parse_sd_block_header(block_header) block_data = file.read(block_length - 4) except SDBlockException: @@ -234,7 +235,7 @@ def get_last_mission_time(file, num_blocks) -> int: count += block_length if count > (num_blocks * 512): raise ParsingException( - f"Read block of length {block_length} would read {count} bytes " f"from {num_blocks * 512} byte flight" + f"Read block of length {block_length} would read {count} bytes from {num_blocks * 512} byte flight" ) # Do not unnecessarily parse blocks unless close to end of flight diff --git a/modules/telemetry/replay.py b/modules/telemetry/replay.py index b3411ed..64dddcc 100644 --- a/modules/telemetry/replay.py +++ b/modules/telemetry/replay.py @@ -20,7 +20,7 @@ logger = logging.getLogger(__name__) -def parse_sd_block_header(header_bytes: bytes): +def parse_sd_block_header(header_bytes: bytes) -> tuple[int, int, int]: """ Parses a sd block header string into its information components and returns them in a tuple. diff --git a/modules/telemetry/superblock.py b/modules/telemetry/superblock.py index 24704f8..5a52951 100644 --- a/modules/telemetry/superblock.py +++ b/modules/telemetry/superblock.py @@ -4,6 +4,7 @@ import datetime import sys import datetime as dt +from typing import Self class Flight: @@ -14,7 +15,7 @@ def __init__(self, first_block: int, num_blocks: int, timestamp: int): self.timestamp: int = timestamp @classmethod - def from_bytes(cls, block): + def from_bytes(cls, block: bytes) -> Self: parts = struct.unpack(" Self: """Generates the Superblock data object from bytes""" if len(block) != 512: raise ValueError("Invalid Superblock") diff --git a/modules/telemetry/telemetry.py b/modules/telemetry/telemetry.py index 1c523c7..f45fe18 100644 --- a/modules/telemetry/telemetry.py +++ b/modules/telemetry/telemetry.py @@ -16,7 +16,7 @@ from signal import signal, SIGTERM from struct import unpack from time import time -from typing import Any +from typing import Any, TypeAlias import modules.telemetry.json_packets as jsp import modules.websocket.commands as wsc @@ -28,8 +28,9 @@ from modules.misc.config import Config # Types -BlockHeader = tuple[int, bool, int, int, int] -PacketHeader = tuple[str, int, int, int, int] +JSON: TypeAlias = dict[str, Any] +BlockHeader: TypeAlias = tuple[int, bool, int, int, int] +PacketHeader: TypeAlias = tuple[str, int, int, int, int] # Constants ORG: str = "CUInSpace" @@ -96,27 +97,27 @@ def __init__(self): class Telemetry(Process): def __init__( self, - serial_status: Queue, - radio_payloads: Queue, - rn2483_radio_input: Queue, - radio_signal_report: Queue, - telemetry_json_output: Queue, - telemetry_ws_commands: Queue, + serial_status: Queue[str], + radio_payloads: Queue[Any], + rn2483_radio_input: Queue[str], + radio_signal_report: Queue[str], + telemetry_json_output: Queue[JSON], + telemetry_ws_commands: Queue[list[str]], config: Config, ): super().__init__() self.config = config - self.radio_payloads = radio_payloads - self.telemetry_json_output = telemetry_json_output - self.telemetry_ws_commands = telemetry_ws_commands - self.rn2483_radio_input = rn2483_radio_input - self.radio_signal_report = radio_signal_report - self.serial_status = serial_status + self.radio_payloads: Queue[str] = radio_payloads + self.telemetry_json_output: Queue[JSON] = telemetry_json_output + self.telemetry_ws_commands: Queue[list[str]] = telemetry_ws_commands + self.rn2483_radio_input: Queue[str] = rn2483_radio_input + self.radio_signal_report: Queue[str] = radio_signal_report + self.serial_status: Queue[str] = serial_status # Telemetry Data holds a dict of the latest copy of received data blocks stored under the subtype name as a key. self.status: jsp.StatusData = jsp.StatusData() - self.telemetry: dict = {} + self.telemetry: dict[str, str] = {} # Mission System self.missions_dir = Path.cwd().joinpath("missions") @@ -130,8 +131,8 @@ def __init__( # Replay System self.replay = None - self.replay_input = Queue() - self.replay_output = Queue() + self.replay_input: Queue[str] = Queue() + self.replay_output: Queue[tuple[int, int, str]] = Queue() # Handle program closing to ensure no orphan processes signal(SIGTERM, shutdown_sequence) # type:ignore @@ -178,7 +179,7 @@ def update_websocket(self) -> None: """Updates the websocket with the latest packet using the JSON output process.""" self.telemetry_json_output.put(self.generate_websocket_response()) - def generate_websocket_response(self) -> dict[str, Any]: + def generate_websocket_response(self) -> JSON: """Returns the dictionary containing the JSON data for the websocket client.""" return {"version": VERSION, "org": ORG, "status": dict(self.status), "telemetry": self.telemetry} @@ -187,7 +188,7 @@ def reset_data(self) -> None: self.status = jsp.StatusData() self.telemetry = {} - def parse_serial_status(self, command: str, data: str): + def parse_serial_status(self, command: str, data: str) -> None: """Parses the serial managers status output""" match command: case "serial_ports": @@ -204,6 +205,8 @@ def parse_serial_status(self, command: str, data: str): self.status.mission.state = jsp.MissionState.DNE case _: self.status.mission.state = jsp.MissionState.LIVE + case _: + return None def execute_command(self, command: wsc.Enum, parameters: list[str]) -> None: """Executes the passed websocket command.""" @@ -242,6 +245,8 @@ def execute_command(self, command: wsc.Enum, parameters: list[str]) -> None: logger.error(e.message) except ReplayPlaybackError as e: logger.error(e.message) + case _: + raise NotImplementedError(f"Command {command} not implemented.") self.update_websocket() @@ -291,7 +296,7 @@ def play_mission(self, mission_name: str | None) -> None: raise ReplayPlaybackError mission_file = mission_path(mission_name, self.missions_dir) - if mission_name is not None and mission_file not in self.status.replay.mission_files_list: + if mission_file not in self.status.replay.mission_files_list: raise MissionNotFoundError(mission_name) if self.replay is None: diff --git a/modules/websocket/websocket.py b/modules/websocket/websocket.py index 7e4dbdc..6c3d272 100644 --- a/modules/websocket/websocket.py +++ b/modules/websocket/websocket.py @@ -6,10 +6,11 @@ # Authors: # Thomas Selwyn (Devil) +from __future__ import annotations import json from multiprocessing import Queue, Process from abc import ABC -from typing import Optional +from typing import Optional, Any import logging import tornado.gen @@ -19,7 +20,7 @@ import tornado.websocket # Constants -WS_COMMANDS_QUEUE: Queue +ws_commands_queue: Queue[Any] # Logger logger = logging.getLogger(__name__) @@ -29,12 +30,12 @@ class WebSocketHandler(Process): """Handles starting the websocket server process.""" - def __init__(self, telemetry_json_output: Queue, ws_commands: Queue): + def __init__(self, telemetry_json_output: Queue[Any], ws_commands: Queue[Any]): super().__init__() - global WS_COMMANDS_QUEUE + global ws_commands_queue - self.telemetry_json_output = telemetry_json_output - WS_COMMANDS_QUEUE = ws_commands + self.telemetry_json_output: Queue[Any] = telemetry_json_output + ws_commands_queue = ws_commands # Default to test mode # ws_commands_queue.put("serial rn2483_radio connect test") @@ -51,7 +52,7 @@ def start_websocket_server(self) -> None: websocket_ping_timeout=30, ) - wss.listen(33845) + _ = wss.listen(33845) io_loop = tornado.ioloop.IOLoop.current() periodic_callback = tornado.ioloop.PeriodicCallback( @@ -75,9 +76,9 @@ class TornadoWSServer(tornado.websocket.WebSocketHandler, ABC): """The server which handles websocket connections.""" - clients: set = set() + clients: set[TornadoWSServer] = set() last_msg_send: str = "" - global WS_COMMANDS_QUEUE + global ws_commands_queue def open(self) -> None: TornadoWSServer.clients.add(self) @@ -90,8 +91,8 @@ def on_close(self) -> None: @staticmethod def on_message(message: str) -> None: - global WS_COMMANDS_QUEUE - WS_COMMANDS_QUEUE.put(message) + global ws_commands_queue + ws_commands_queue.put(message) def check_origin(self, _) -> bool: """Authenticates clients from any host origin (_ parameter).""" diff --git a/pyproject.toml b/pyproject.toml index 6d77b65..4683b64 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,7 +53,6 @@ reportPrivateUsage = "warning" reportPrivateImportUsage = "warning" reportShadowedImports = "error" -reportIncompatibleMethodOverride = "warning" reportIncompatibleVariableOverride = "warning" reportInconsistentConstructor = "warning" reportOverlappingOverload = "warning" From 48675e932e37b16c5f2f657d876b47476011d9f7 Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 14:38:13 -0400 Subject: [PATCH 06/25] Fixed outstanding telemetry.py errors --- modules/telemetry/telemetry.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/telemetry/telemetry.py b/modules/telemetry/telemetry.py index f45fe18..64095e9 100644 --- a/modules/telemetry/telemetry.py +++ b/modules/telemetry/telemetry.py @@ -338,7 +338,7 @@ def start_recording(self, mission_name: str | None = None) -> None: # Create SuperBlock in file flight = Flight(first_block=1, num_blocks=0, timestamp=recording_epoch) self.mission_recording_sb.flights = [flight] - self.mission_recording_file.write(self.mission_recording_sb.to_bytes()) + _ = self.mission_recording_file.write(self.mission_recording_sb.to_bytes()) self.mission_recording_file.flush() # Status update @@ -383,19 +383,19 @@ def recording_write_bytes(self, num_bytes: int, spacer: bool = False) -> None: # Update Superblock with new block count self.mission_recording_sb.flights[0].num_blocks += int(math.ceil(num_bytes / 512)) - self.mission_recording_file.seek(0) - self.mission_recording_file.write(self.mission_recording_sb.to_bytes()) + _ = self.mission_recording_file.seek(0) + _ = self.mission_recording_file.write(self.mission_recording_sb.to_bytes()) # Dump entire buffer to file blocks = self.mission_recording_buffer[:num_bytes] self.mission_recording_buffer = self.mission_recording_buffer[num_bytes:] - self.mission_recording_file.seek(0, 2) - self.mission_recording_file.write(blocks) + _ = self.mission_recording_file.seek(0, 2) + _ = self.mission_recording_file.write(blocks) # If less than 512 bytes, or a spacer is requested then write a spacer if num_bytes < 512 or spacer: spacer_block = LoggingMetadataSpacerBlock(512 - (num_bytes % 512)) - self.mission_recording_file.write(spacer_block.to_bytes()) + _ = self.mission_recording_file.write(spacer_block.to_bytes()) def parse_rn2483_payload(self, block_type: int, block_subtype: int, contents: str) -> None: """ From 32668ca9913be53c6dfe50c26b016acdef89c6ec Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 14:41:01 -0400 Subject: [PATCH 07/25] Typeignored approx in conversion tests. --- tests/test_conversions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_conversions.py b/tests/test_conversions.py index c6889b1..549574e 100644 --- a/tests/test_conversions.py +++ b/tests/test_conversions.py @@ -9,14 +9,14 @@ # Tests def test_celsius_to_fahrenheit() -> None: """Test that Celsius is properly converted to Fahrenheit.""" - assert conv.celsius_to_fahrenheit(12.0) == pytest.approx(53.6) + assert conv.celsius_to_fahrenheit(12.0) == pytest.approx(53.6) # type: ignore def test_metres_to_feet() -> None: """Test that metres are properly converted to feet.""" - assert conv.metres_to_feet(5.0) == pytest.approx(16.4) + assert conv.metres_to_feet(5.0) == pytest.approx(16.4) # type: ignore def test_pascals_to_psi() -> None: """Test that metres are properly converted to feet.""" - assert conv.pascals_to_psi(87181) == pytest.approx(12.64) + assert conv.pascals_to_psi(87181) == pytest.approx(12.64) # type: ignore From 533077534047d600db25a982e28627b9565e3706 Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 14:57:49 -0400 Subject: [PATCH 08/25] Resolved type errors in sd_block.py --- modules/telemetry/sd_block.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/modules/telemetry/sd_block.py b/modules/telemetry/sd_block.py index 939359f..fd53a91 100644 --- a/modules/telemetry/sd_block.py +++ b/modules/telemetry/sd_block.py @@ -5,7 +5,7 @@ from __future__ import annotations import struct from abc import ABC, abstractmethod -from typing import Self +from typing import Self, Any from modules.telemetry.data_block import DataBlock import modules.telemetry.block as blk @@ -26,9 +26,10 @@ class SDBlockUnknownException(blk.BlockUnknownException): class SDBlock(ABC): """Defines the interface for all SDBlock subtypes.""" - def __init__(self, sd_subtype: blk.SDBlockSubtype, subtype) -> None: + def __init__(self, sd_subtype: blk.SDBlockSubtype, subtype: Any) -> None: + super().__init__() self.sd_subtype: blk.SDBlockSubtype = sd_subtype - self.subtype = subtype + self.subtype: Any = subtype def __len__(self) -> int: """Length of the block in bytes.""" @@ -120,13 +121,13 @@ def __str__(self): class TelemetryDataBlock(SDBlock): """Responsible for initializing data blocks.""" - def __init__(self, subtype: blk.DataBlockSubtype, data): + def __init__(self, subtype: blk.DataBlockSubtype, data: DataBlock): super().__init__(blk.SDBlockSubtype.TELEMETRY_DATA, subtype) self.data = data def __len__(self) -> int: """Length of the telemetry data in bytes. Four bytes for the block header, then the block contents itself.""" - return 4 + self.data.length + return 4 + len(self.data) @classmethod def from_payload(cls, block_subtype: int, payload: bytes) -> Self: @@ -142,7 +143,7 @@ def __str__(self): # Diagnostic Data class DiagnosticDataBlock(SDBlock): - def __init__(self, subtype) -> None: + def __init__(self, subtype: blk.DiagnosticDataBlockSubtype) -> None: super().__init__(blk.SDBlockSubtype.DIAGNOSTIC_DATA, subtype) @classmethod @@ -157,12 +158,10 @@ def from_payload(cls, block_type: blk.DiagnosticDataBlockSubtype, payload: bytes return DiagnosticDataOutgoingRadioPacketBlock.from_payload(payload) case blk.DiagnosticDataBlockSubtype.INCOMING_RADIO_PACKET: return DiagnosticDataIncomingRadioPacketBlock.from_payload(payload) - case _: - raise NotImplementedError(f"from_payload not implemented for {diagnostic_subtype}.") class DiagnosticDataRadioPacketBlock(DiagnosticDataBlock): - def __init__(self, subtype: blk.DiagnosticDataBlockSubtype, mission_time: int, packet): + def __init__(self, subtype: blk.DiagnosticDataBlockSubtype, mission_time: int, packet: bytes): super().__init__(subtype) self.mission_time: int = mission_time self.packet = packet @@ -200,7 +199,7 @@ def __str__(self): class DiagnosticDataOutgoingRadioPacketBlock(DiagnosticDataRadioPacketBlock): - def __init__(self, mission_time: int, packet): + def __init__(self, mission_time: int, packet: bytes): super().__init__(blk.DiagnosticDataBlockSubtype.OUTGOING_RADIO_PACKET, mission_time, packet) @classmethod @@ -213,7 +212,7 @@ def __str__(self): class DiagnosticDataIncomingRadioPacketBlock(DiagnosticDataRadioPacketBlock): - def __init__(self, mission_time: int, packet): + def __init__(self, mission_time: int, packet: bytes): super().__init__(blk.DiagnosticDataBlockSubtype.INCOMING_RADIO_PACKET, mission_time, packet) @classmethod From 5a323e387bd26f13cf53078bb3f65f97708da317 Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 14:58:34 -0400 Subject: [PATCH 09/25] Resolved type conflicts in superblock. --- modules/telemetry/superblock.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/telemetry/superblock.py b/modules/telemetry/superblock.py index 5a52951..d2d4126 100644 --- a/modules/telemetry/superblock.py +++ b/modules/telemetry/superblock.py @@ -1,7 +1,6 @@ #! /usr/bin/env python3 import os import struct -import datetime import sys import datetime as dt from typing import Self @@ -64,7 +63,7 @@ def from_bytes(cls, block: bytes) -> Self: partition_length = struct.unpack(" 512: - f.seek(512 * 2048) + _ = f.seek(512 * 2048) sb = SuperBlock.from_bytes(f.read(512)) sb.output(True) From cb8f8b5664b595d060eae43d844427b29b8936d7 Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 15:03:30 -0400 Subject: [PATCH 10/25] Solved type errors in replay.py --- modules/telemetry/replay.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/modules/telemetry/replay.py b/modules/telemetry/replay.py index 64dddcc..52ed13f 100644 --- a/modules/telemetry/replay.py +++ b/modules/telemetry/replay.py @@ -39,10 +39,18 @@ def parse_sd_block_header(header_bytes: bytes) -> tuple[int, int, int]: class TelemetryReplay: - def __init__(self, replay_payloads: Queue, replay_input: Queue, replay_speed: int, replay_path: Path): + def __init__( + self, + replay_payloads: Queue[tuple[int, int, str]], + replay_input: Queue[str], + replay_speed: int, + replay_path: Path, + ): + super().__init__() + # Replay buffers (Input and output) - self.replay_payloads = replay_payloads - self.replay_input = replay_input + self.replay_payloads: Queue[tuple[int, int, str]] = replay_payloads + self.replay_input: Queue[str] = replay_input # Misc replay self.replay_path = replay_path @@ -57,7 +65,7 @@ def __init__(self, replay_payloads: Queue, replay_input: Queue, replay_speed: in mission_sb = SuperBlock.from_bytes(file.read(512)) for flight in mission_sb.flights: - file.seek(flight.first_block * 512) + _ = file.seek(flight.first_block * 512) self.run(file, flight.num_blocks) def run(self, file: BinaryIO, num_blocks: int): @@ -69,13 +77,15 @@ def run(self, file: BinaryIO, num_blocks: int): if not self.replay_input.empty(): self.parse_input_command(self.replay_input.get()) - def parse_input_command(self, data: str): - split = data.split(" ") - match split[0]: + def parse_input_command(self, data: str) -> None: + cmd_list = data.split(" ") + match cmd_list[0]: case "speed": - self.speed = float(split[1]) + self.speed = float(cmd_list[1]) # Reset loop time so resuming playback doesn't skip the time it was paused self.last_loop_time = int(time() * 1000) + case _: + raise NotImplementedError(f"Replay command of {cmd_list} invalid.") def read_next_sd_block(self, file: BinaryIO, num_blocks: int): """Reads the next stored block and outputs it""" From df05cc845c592e602d2f10cf374a4a7d5ef9996f Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 15:05:30 -0400 Subject: [PATCH 11/25] Resolved type issues in websocket.py --- modules/websocket/websocket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/websocket/websocket.py b/modules/websocket/websocket.py index 6c3d272..5f785d1 100644 --- a/modules/websocket/websocket.py +++ b/modules/websocket/websocket.py @@ -106,4 +106,4 @@ def send_message(cls, message: str | None) -> None: cls.last_msg_send = message for client in cls.clients: - client.write_message(message) + _ = client.write_message(message) From dc5304e5fc13b6063b82ca3316e956de9511e626 Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 15:11:58 -0400 Subject: [PATCH 12/25] Resolved type errors in datablock.py --- modules/telemetry/data_block.py | 45 ++++++++++++--------------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/modules/telemetry/data_block.py b/modules/telemetry/data_block.py index f0f1911..d24219e 100644 --- a/modules/telemetry/data_block.py +++ b/modules/telemetry/data_block.py @@ -26,6 +26,7 @@ class DataBlock(ABC): """Interface for all telemetry data blocks.""" def __init__(self, subtype: DataBlockSubtype, mission_time: int): + super().__init__() self.mission_time: int = mission_time self.subtype: DataBlockSubtype = subtype @@ -476,7 +477,7 @@ def to_payload(self) -> bytes: ) @staticmethod - def coord_to_str(coord, ew=False): + def coord_to_str(coord: int, ew: bool = False): direction = coord >= 0 coord = abs(coord) degrees = coord // 600000 @@ -529,6 +530,7 @@ class GNSSSatInfo: GLONASS_SV_OFFSET: int = 65 def __init__(self, sat_type: GNSSSatType, elevation: int, snr: int, identifier: int, azimuth: int): + super().__init__() self.sat_type: GNSSSatType = sat_type self.elevation: int = elevation self.snr: int = snr @@ -604,9 +606,9 @@ def from_payload(cls, payload: bytes): parts = struct.unpack("> 14) & 0x3 num_samples = (len(payload) - (6 + padding)) // ((resolution.bits // 8) * 3) - samples = list() + samples: list[tuple[float, float, float]] = list() sensitivity = (2 ** (resolution.bits - 1)) // accel_range.acceleration for i in range(num_samples): if resolution == KX134Resolution.RES_8_BIT: @@ -862,8 +862,6 @@ def samples_per_sec(self): return 8 case self.SR_100: return 100 - case _: - return 0 def __str__(self): return f"{self.samples_per_sec} Hz" @@ -888,8 +886,6 @@ def acceleration(self): return 8 case self.ACCEL_16G: return 16 - case _: - raise NotImplementedError() @property def sensitivity(self): @@ -918,8 +914,6 @@ def angular_velocity(self): return 1000 case self.AV_2000DPS: return 2000 - case _: - return 0 @property def sensitivity(self): @@ -957,8 +951,6 @@ def bandwidth(self) -> float: return 218.1 case self.BW_420_HZ: return 420.0 - case _: - return 0 def __str__(self): return f"{self.bandwidth}Hz" @@ -992,8 +984,6 @@ def bandwidth(self): return 184 case self.BW_250_HZ: return 250 - case _: - return 0 def __str__(self): return f"{self.bandwidth}Hz" @@ -1010,8 +1000,6 @@ def bits(self): return 14 case MPU9250MagResolution.RES_16_BIT: return 16 - case _: - return 0 @property def sensitivity(self): @@ -1020,8 +1008,6 @@ def sensitivity(self): return 1 / 0.6 case MPU9250MagResolution.RES_16_BIT: return 1 / 0.15 - case _: - raise NotImplementedError(f"Resolution of type {type(self)} invalid.") def __str__(self): return f"{self.bits} bits per sample" @@ -1043,6 +1029,7 @@ def __init__( mag_ovf: int, mag_res: MPU9250MagResolution, ): + super().__init__() self.accel_x: float = accel_x self.accel_y: float = accel_y self.accel_z: float = accel_z @@ -1157,38 +1144,38 @@ def from_payload(cls, payload: bytes) -> Self: mag_sample_rate = MPU9250MagSR((parts[1] >> 8) & 0x1) except ValueError as error: raise DataBlockException( - f"Invalid MPU9250 magnetometer sample " f"rate: {(parts[1] >> 8) & 0x1}" + f"Invalid MPU9250 magnetometer sample rate: {(parts[1] >> 8) & 0x1}" ) from error try: accel_fsr = MPU9250AccelFSR((parts[1] >> 9) & 0x3) except ValueError as error: raise DataBlockException( - f"Invalid MPU9250 accelerometer full scale " f"range: {(parts[1] >> 9) & 0x3}" + f"Invalid MPU9250 accelerometer full scale range: {(parts[1] >> 9) & 0x3}" ) from error try: gyro_fsr = MPU9250GyroFSR((parts[1] >> 11) & 0x3) except ValueError as error: raise DataBlockException( - f"Invalid MPU9250 gyroscope full scale " f"range: {(parts[1] >> 11) & 0x3}" + f"Invalid MPU9250 gyroscope full scale range: {(parts[1] >> 11) & 0x3}" ) from error try: accel_bw = MPU9250AccelBW((parts[1] >> 13) & 0x7) except ValueError as error: raise DataBlockException( - f"Invalid MPU9250 accelerometer bandwidth: " f"{(parts[1] >> 13) & 0x7}" + f"Invalid MPU9250 accelerometer bandwidth: {(parts[1] >> 13) & 0x7}" ) from error try: gyro_bw = MPU9250GyroBW((parts[1] >> 16) & 0x7) except ValueError as error: - raise DataBlockException(f"Invalid MPU9250 gyroscope bandwidth: " f"{(parts[1] >> 16) & 0x7}") from error + raise DataBlockException(f"Invalid MPU9250 gyroscope bandwidth: {(parts[1] >> 16) & 0x7}") from error num_samples = (len(payload) - 8) // 21 - samples = list() + samples: list[MPU9250Sample] = list() for i in range(num_samples): sample_start = 8 + (i * 21) sample = MPU9250Sample.from_bytes( From f0fd64656c642b9778ce2b460fc459c9e346c6c0 Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 15:23:26 -0400 Subject: [PATCH 13/25] Resolved type errors in control_block.py --- modules/telemetry/control_block.py | 76 ++++++++++++------------------ 1 file changed, 30 insertions(+), 46 deletions(-) diff --git a/modules/telemetry/control_block.py b/modules/telemetry/control_block.py index 1acc756..6c81b62 100644 --- a/modules/telemetry/control_block.py +++ b/modules/telemetry/control_block.py @@ -1,6 +1,12 @@ +# Defines the control block types from abc import ABC, abstractmethod +from typing import Self +import logging from modules.telemetry.block import ControlBlockSubtype, BlockException, BlockUnknownException +# Define logger +logger = logging.getLogger(__name__) + class ControlDataBlockException(BlockException): pass @@ -11,78 +17,56 @@ class ControlBlockUnknownException(BlockUnknownException): class ControlBlock(ABC): - - @property - @abstractmethod - def length(self): - """ Length of block """ - - @property - @abstractmethod - def subtype(self): - """ Subtype of block """ + """Represents the base interface for a Control Block.""" @abstractmethod def to_payload(self): - """ Marshal block to a bytes object """ - - @staticmethod - @abstractmethod - def type_desc(): - """ String description of block type """ + """Marshal block to a bytes object""" @classmethod - def parse_block(cls, block_subtype, payload): - """ Unmarshal a bytes object to appropriate block class """ + def parse_block(cls, block_subtype: ControlBlockSubtype, payload: bytes) -> Self: + """Unmarshal a bytes object to appropriate block class""" match block_subtype: case ControlBlockSubtype.SIGNAL_REPORT: - print("") + logger.debug("Control block of type {block_subtype} received.") return SignalReportControlBlock.from_payload(payload) case ControlBlockSubtype.COMMAND_ACKNOWLEDGEMENT: - print("") + logger.debug("Control block of type {block_subtype} received.") case ControlBlockSubtype.COMMAND_NONCE_REQUEST: - print("") + logger.debug("Control block of type {block_subtype} received.") case ControlBlockSubtype.COMMAND_NONCE: - print("") + logger.debug("Control block of type {block_subtype} received.") case ControlBlockSubtype.BEACON: - print("") + logger.debug("Control block of type {block_subtype} received.") case ControlBlockSubtype.BEACON_RESPONSE: - print("") + logger.debug("Control block of type {block_subtype} received.") + case ControlBlockSubtype.RESERVED: + logger.debug("Control block of type {block_subtype} received.") raise ControlBlockUnknownException(f"Unknown control block subtype: {block_subtype}") - def __str__(self): - return "" - - def __iter__(self): - yield "" - class SignalReportControlBlock(ControlBlock): - def __init__(self): - self.mission_time = None - self.snr = 0 - self.tx_power = 0 + """Represents a control block requesting signal report.""" - @staticmethod - def type_desc(): - return "Signal Report" + def __init__(self): # TODO accept parameters for creating one of these + super().__init__() + self.mission_time: int = 0 + self.snr: int = 0 + self.tx_power: int = 0 - def length(self): + def __len__(self) -> int: return 16 - def to_payload(self): - return "" - - def subtype(self): - return "" + def to_payload(self) -> bytes: + return bytes() # TODO implement this method @classmethod - def from_payload(cls, payload): - return "" + def from_payload(cls, _: bytes) -> Self: + return cls() def __str__(self): - return f"{self.type_desc()} -> time: {self.mission_time}, snr: {self.snr}, power: {self.tx_power}" + return f"{self.__class__.__name__} -> time: {self.mission_time}, snr: {self.snr}, power: {self.tx_power}" def __iter__(self): yield "mission_time", self.mission_time From efd27dc92ede80c9b5f9a30fa10dc042ab33d48d Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 15:28:44 -0400 Subject: [PATCH 14/25] Resolved type errors in serial_rn2483_radio.py --- modules/serial/serial_rn2483_radio.py | 55 ++++++++++++++++----------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/modules/serial/serial_rn2483_radio.py b/modules/serial/serial_rn2483_radio.py index 1d9f9a2..704d42c 100644 --- a/modules/serial/serial_rn2483_radio.py +++ b/modules/serial/serial_rn2483_radio.py @@ -48,23 +48,32 @@ class SerialRN2483Radio(Process): def __init__( self, - serial_status: Queue, - radio_signal_report: Queue, - rn2483_radio_input: Queue, - rn2483_radio_payloads: Queue, + serial_status: Queue[str], + radio_signal_report: Queue[str], + rn2483_radio_input: Queue[str], + rn2483_radio_payloads: Queue[str], serial_port: str, settings: RadioParameters, ): Process.__init__(self) - self.serial_status = serial_status - self.radio_signal_report = radio_signal_report - self.rn2483_radio_input = rn2483_radio_input - self.rn2483_radio_payloads = rn2483_radio_payloads + self.serial_status: Queue[str] = serial_status + self.radio_signal_report: Queue[str] = radio_signal_report + self.rn2483_radio_input: Queue[str] = rn2483_radio_input + self.rn2483_radio_payloads: Queue[str] = rn2483_radio_payloads self.serial_port = serial_port self.settings = settings + self.serial = Serial( + port=self.serial_port, + timeout=1, + baudrate=57600, + bytesize=EIGHTBITS, + parity=PARITY_NONE, + stopbits=1, + rtscts=False, + ) self.run() def run(self): @@ -93,7 +102,7 @@ def run(self): while True: while not self.rn2483_radio_input.empty(): - self.write_to_rn2483_radio(self.rn2483_radio_input.get()) + _ = self.write_to_rn2483_radio(self.rn2483_radio_input.get()) self.set_rx_mode() # FUTURE TO DO LIMIT TO ONLY AFTER THE ENTIRE BATCH IS DONE. # AFTER SENDING A COMMAND TO RADIO RECALL SET_RX_MODE() @@ -113,22 +122,22 @@ def _read_ser(self) -> str: def init_gpio(self) -> None: """Set all GPIO pins to input mode, thereby putting them in a state of high impedance.""" - self.write_to_rn2483_radio("sys set pinmode GPIO0 digout") - self.write_to_rn2483_radio("sys set pinmode GPIO1 digout") - self.write_to_rn2483_radio("sys set pinmode GPIO2 digout") - self.write_to_rn2483_radio("sys set pindig GPIO0 1") - self.write_to_rn2483_radio("sys set pindig GPIO1 1") - self.write_to_rn2483_radio("sys set pindig GPIO2 0") + _ = self.write_to_rn2483_radio("sys set pinmode GPIO0 digout") + _ = self.write_to_rn2483_radio("sys set pinmode GPIO1 digout") + _ = self.write_to_rn2483_radio("sys set pinmode GPIO2 digout") + _ = self.write_to_rn2483_radio("sys set pindig GPIO0 1") + _ = self.write_to_rn2483_radio("sys set pindig GPIO1 1") + _ = self.write_to_rn2483_radio("sys set pindig GPIO2 0") for i in range(0, 14): - self.write_to_rn2483_radio(f"sys set pinmode GPIO{i} digin") + _ = self.write_to_rn2483_radio(f"sys set pinmode GPIO{i} digin") logger.info("Successfully set GPIO.") def reset(self) -> bool: """Performs a software reset on the RN2483 radio.""" - self.write_to_rn2483_radio("sys reset") + _ = self.write_to_rn2483_radio("sys reset") ret = self._read_ser() # Confirm from the rn2483 radio that the reset was a success if "RN2483" in ret: @@ -144,7 +153,7 @@ def init_rn2483_radio(self): """ # Restart the radio module - self.reset() + _ = self.reset() logger.info("Resetting radio...") # Initialize GPIO pins @@ -181,7 +190,7 @@ def write_to_rn2483_radio(self, command_string: str) -> Optional[bool]: data += "\r\n" # Must include carriage return for valid commands (see DS40001784B pg XX) self.serial.flush() # Flush the serial port - self.serial.write(data.encode("utf-8")) # Encode command_string as bytes and then transmit over serial port + _ = self.serial.write(data.encode("utf-8")) # Encode command_string as bytes and then transmit over serial port # Wait for response on the serial line. Return if 'ok' received # Sys reset gives us info about the board which we want to process differently from other commands if command_string not in ["sys reset", "radio get snr", "radio get rssi"]: @@ -210,8 +219,8 @@ def wait_for_ok(self) -> bool: def set_rx_mode(self) -> None: """Set the RN2483 radio so that it constantly listens for transmissions.""" - self.write_to_rn2483_radio("radio set wdt 0") # Turn off watch dog timer - self.write_to_rn2483_radio("mac pause") # This command must be passed before any reception can occur + _ = self.write_to_rn2483_radio("radio set wdt 0") # Turn off watch dog timer + _ = self.write_to_rn2483_radio("mac pause") # This command must be passed before any reception can occur if not self.write_to_rn2483_radio("radio rx 0"): # Command radio to go into continuous reception mode logger.error("Failure putting radio into rx mode.") @@ -229,13 +238,13 @@ def check_for_transmissions(self) -> None: logger.debug(f"Cleaned serial message: {message}") self.rn2483_radio_payloads.put(message) # Put serial message in data queue for telemetry - def _tx(self, data) -> None: + def _tx(self, data: str) -> None: """ Transmit data, a method used for debugging. ROCKET DOES NOT RESPOND TO TRANSMISSIONS AT THIS TIME. """ - self.write_to_rn2483_radio("mac pause") # Command that must be called before each transmission and receive + _ = self.write_to_rn2483_radio("mac pause") # Command that must be called before each transmission and receive if not self.write_to_rn2483_radio(f"radio tx {data}"): # Is the data we wish to transmit valid? logger.error("Invalid transmission message.") From bc6a6c2d9bfffe88a352b3701d971a13d32b2c49 Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 16:04:55 -0400 Subject: [PATCH 15/25] Solved type errors in serial_rn2483_emulator.py --- modules/serial/serial_rn2483_emulator.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/serial/serial_rn2483_emulator.py b/modules/serial/serial_rn2483_emulator.py index 3171298..1de0bad 100644 --- a/modules/serial/serial_rn2483_emulator.py +++ b/modules/serial/serial_rn2483_emulator.py @@ -12,19 +12,19 @@ class SerialRN2483Emulator(Process): - def __init__(self, serial_status: Queue, radio_signal_report: Queue, rn2483_radio_payloads: Queue): + def __init__(self, serial_status: Queue[str], radio_signal_report: Queue[str], rn2483_radio_payloads: Queue[str]): super().__init__() - self.serial_status = serial_status + self.serial_status: Queue[str] = serial_status - self.rn2483_radio_payloads = rn2483_radio_payloads - self.radio_signal_report = radio_signal_report + self.rn2483_radio_payloads: Queue[str] = rn2483_radio_payloads + self.radio_signal_report: Queue[str] = radio_signal_report # Emulation Variables - self.altitude = 0 - self.temp = 22 - self.going_up = True - self.startup_time = datetime.now() + self.altitude: float = 0 + self.temp: float = 22 + self.going_up: bool = True + self.startup_time: datetime = datetime.now() self.run() From fd255d10fc64bed210fb42a02274761c3b102d48 Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 16:15:14 -0400 Subject: [PATCH 16/25] Resolved type errors in serial_manager.py --- modules/serial/serial_manager.py | 85 +++++++++++++++++--------------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/modules/serial/serial_manager.py b/modules/serial/serial_manager.py index aaa4939..7c7a528 100644 --- a/modules/serial/serial_manager.py +++ b/modules/serial/serial_manager.py @@ -26,24 +26,31 @@ def shutdown_sequence(): class SerialManager(Process): - def __init__(self, serial_status: Queue, serial_ws_commands: Queue, radio_signal_report: Queue, - rn2483_radio_input: Queue, rn2483_radio_payloads: Queue, config: Config): + def __init__( + self, + serial_status: Queue[str], + serial_ws_commands: Queue[list[str]], + radio_signal_report: Queue[str], + rn2483_radio_input: Queue[str], + rn2483_radio_payloads: Queue[str], + config: Config, + ): super().__init__() - self.serial_status = serial_status - self.serial_ports = [] - self.serial_ws_commands = serial_ws_commands + self.serial_status: Queue[str] = serial_status + self.serial_ports: list[str] = [] + self.serial_ws_commands: Queue[list[str]] = serial_ws_commands - self.radio_signal_report = radio_signal_report + self.radio_signal_report: Queue[str] = radio_signal_report - self.rn2483_radio_input = rn2483_radio_input - self.rn2483_radio_payloads = rn2483_radio_payloads - self.rn2483_radio = None + self.rn2483_radio_input: Queue[str] = rn2483_radio_input + self.rn2483_radio_payloads: Queue[str] = rn2483_radio_payloads + self.rn2483_radio: Process | None = None self.config = config # Immediately find serial ports - self.update_serial_ports() + self.serial_ports = self.update_serial_ports(self.serial_status) # Handle program closing to ensure no orphan processes signal(SIGTERM, shutdown_sequence) # type:ignore @@ -56,21 +63,21 @@ def run(self): ws_cmd = self.serial_ws_commands.get() self.parse_ws_command(ws_cmd) - def parse_ws_command(self, ws_cmd): - """ Parses the serial websocket commands """ + def parse_ws_command(self, ws_cmd: list[str]): + """Parses the serial websocket commands""" try: match ws_cmd[0]: case "rn2483_radio": self.parse_rn2483_radio_ws(ws_cmd[1:]) case "update": - self.update_serial_ports() + self.serial_ports = self.update_serial_ports(self.serial_status) case _: logger.error("Serial: Invalid device type.") except IndexError: logger.error("Serial: Error parsing ws command") - def parse_rn2483_radio_ws(self, ws_cmd): - """ Parses the websocket commands relating to the rn2483_radio """ + def parse_rn2483_radio_ws(self, ws_cmd: list[str]) -> None: + """Parses the websocket commands relating to the RN2483_radio""" radio_ws_cmd = ws_cmd[0] if radio_ws_cmd == "connect" and self.rn2483_radio is None: @@ -86,16 +93,14 @@ def parse_rn2483_radio_ws(self, ws_cmd): proposed_serial_port, self.config.radio_parameters, ), - daemon=True) + daemon=True, + ) else: self.rn2483_radio = Process( target=SerialRN2483Emulator, - args=( - self.serial_status, - self.radio_signal_report, - self.rn2483_radio_payloads - ), - daemon=True) + args=(self.serial_status, self.radio_signal_report, self.rn2483_radio_payloads), + daemon=True, + ) self.rn2483_radio.start() elif radio_ws_cmd == "connect": logger.info("Already connected.") @@ -108,25 +113,26 @@ def parse_rn2483_radio_ws(self, ws_cmd): elif radio_ws_cmd == "disconnect": logger.warning("Serial: RN2483 Radio already disconnected.") - def update_serial_ports(self) -> list[str]: - """ Finds and updates serial ports on device + @staticmethod + def update_serial_ports(serial_status: Queue[str]) -> list[str]: + """Finds and updates serial ports on device - :raises EnvironmentError: - On unsupported or unknown platforms - :returns: - A list of the serial ports available on the system + :raises EnvironmentError: + On unsupported or unknown platforms + :returns: + A list of the serial ports available on the system """ - com_ports = [""] + com_ports: list[str] = [""] - if sys.platform.startswith('win'): - com_ports = ['COM%s' % (i + 1) for i in range(256)] - elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'): + if sys.platform.startswith("win"): + com_ports = ["COM%s" % (i + 1) for i in range(256)] + elif sys.platform.startswith("linux") or sys.platform.startswith("cygwin"): # '/dev/tty[A-Za-z]*' - com_ports = glob.glob('/dev/ttyUSB*') - elif sys.platform.startswith('darwin'): - com_ports = glob.glob('/dev/tty.*') + com_ports = glob.glob("/dev/ttyUSB*") + elif sys.platform.startswith("darwin"): + com_ports = glob.glob("/dev/tty.*") - tested_com_ports = [] + tested_com_ports: list[str] = [] # Checks ports if they are potential COM ports for test_port in com_ports: @@ -137,9 +143,6 @@ def update_serial_ports(self) -> list[str]: except (OSError, SerialException): pass - tested_com_ports = tested_com_ports + ["test"] - - self.serial_ports = tested_com_ports - self.serial_status.put(f"serial_ports {self.serial_ports}") - + tested_com_ports.append("test") + serial_status.put(f"serial_ports {tested_com_ports}") return tested_com_ports From 6e17f0f4ad8749a1e167595101f9d10b28f74996 Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 16:17:24 -0400 Subject: [PATCH 17/25] Resolved type errors in config.py --- modules/misc/config.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/misc/config.py b/modules/misc/config.py index afac341..30597b5 100644 --- a/modules/misc/config.py +++ b/modules/misc/config.py @@ -69,8 +69,7 @@ class RadioParameters: def __post_init__(self): if self.frequency not in range(*LF_RANGE) and self.frequency not in range(*HF_RANGE): raise ValueError( - f"Frequency '{self.frequency}' not in low frequency range {LF_RANGE} " - f"or high frequency range {HF_RANGE}" + f"Frequency '{self.frequency}' not in low frequency range {LF_RANGE} or high frequency range {HF_RANGE}" ) if self.power not in range(*POWER_RANGE): @@ -127,7 +126,7 @@ class Config: approved_callsigns: dict[str, str] = field(default_factory=dict) def __post_init__(self): - if self.approved_callsigns is None or len(self.approved_callsigns) == 0: + if len(self.approved_callsigns) == 0: raise ValueError("You must provide at least one approved callsign.") @classmethod From 4b1e8be996237b78a7c9051a9be6a2d1dc6e07b0 Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 16:18:19 -0400 Subject: [PATCH 18/25] Resolved type errors in cli.py --- modules/misc/cli.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/misc/cli.py b/modules/misc/cli.py index bb52e8e..3dcaf24 100644 --- a/modules/misc/cli.py +++ b/modules/misc/cli.py @@ -11,25 +11,25 @@ # Custom types def file_path(path: str): - """Verifies that the argument is a valid filepath.""" + """Verifies that the argument is a valid file path.""" if os.path.isfile(path) or os.access(os.path.dirname(path), os.W_OK): return path else: - raise FileNotFoundError(f"{path} is not a valid filepath.") + raise FileNotFoundError(f"{path} is not a valid file path.") # Arguments parser = argparse.ArgumentParser(description=DESC) -parser.add_argument( +_ = parser.add_argument( "-l", help="Selects the logging level for messages in the console.", choices=["debug", "info", "warning", "error", "critical"], - default="info" + default="info", ) -parser.add_argument( +_ = parser.add_argument( "-o", help="Output file for logging messages. Logs to console by default.", type=file_path, From bb4a3fa62a0c006ce0faa869c34ba378a77498d7 Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 16:22:20 -0400 Subject: [PATCH 19/25] Resolved type errors in main. --- main.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/main.py b/main.py index f939f38..903edad 100644 --- a/main.py +++ b/main.py @@ -8,6 +8,7 @@ from multiprocessing import Process, Queue from re import sub import logging +from typing import TypeAlias, Any from modules.misc.config import load_config from modules.misc.messages import print_cu_rocket @@ -16,6 +17,7 @@ from modules.websocket.websocket import WebSocketHandler from modules.misc.cli import parser +JSON: TypeAlias = dict[str, Any] VERSION: str = "0.5.0-DEV" STR_TO_LOGGING_MODE: dict[str, int] = { "debug": logging.DEBUG, @@ -35,7 +37,7 @@ class ShutdownException(Exception): # Set up logging -log_handlers: list = [logging.StreamHandler()] # Always print to stdout +log_handlers: list[logging.Handler] = [logging.StreamHandler()] # Always print to stdout if args.get("o") is not None: log_handlers.append(logging.FileHandler(args.get("o", "logfile.log"))) @@ -48,15 +50,15 @@ class ShutdownException(Exception): def main(): # Set up queues - serial_status = Queue() - ws_commands = Queue() - serial_ws_commands = Queue() - telemetry_ws_commands = Queue() + serial_status: Queue[str] = Queue() + ws_commands: Queue[str] = Queue() + serial_ws_commands: Queue[list[str]] = Queue() + telemetry_ws_commands: Queue[list[str]] = Queue() - radio_signal_report = Queue() - rn2483_radio_input = Queue() - rn2483_radio_payloads = Queue() - telemetry_json_output = Queue() + radio_signal_report: Queue[str] = Queue() + rn2483_radio_input: Queue[str] = Queue() + rn2483_radio_payloads: Queue[str] = Queue() + telemetry_json_output: Queue[JSON] = Queue() # Print display screen print_cu_rocket("No Name (Gas Propelled Launching Device)", VERSION) @@ -123,7 +125,7 @@ def main(): exit(0) -def parse_ws_command(ws_cmd: str, serial_commands: Queue, telemetry_commands: Queue) -> None: +def parse_ws_command(ws_cmd: str, serial_commands: Queue[list[str]], telemetry_commands: Queue[list[str]]) -> None: """Parses a websocket command and places it on the correct process queue (telemetry or serial).""" # Remove special characters From 6d97bc518a787a2954f51b68431390a4b8e6727b Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 16:23:00 -0400 Subject: [PATCH 20/25] Resolved warnings in block.py --- modules/telemetry/block.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/telemetry/block.py b/modules/telemetry/block.py index af17c69..436f97d 100644 --- a/modules/telemetry/block.py +++ b/modules/telemetry/block.py @@ -25,8 +25,6 @@ def __str__(self): return "ROCKET" case DeviceAddress.MULTICAST: return "MULTICAST" - case _: - return "UNKNOWN" class RadioBlockType(IntEnum): From 8c1bd485749eef47bfd3567af0976937b460d362 Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 16:32:55 -0400 Subject: [PATCH 21/25] Adde project description to pyproject.toml --- pyproject.toml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 4683b64..e8d92ec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,22 @@ +[project] +name = "Telemetry Server" +description = "The CUInSpace ground station telemetry server for parsing and distributing rocket telemetry." +license = {text = "MIT"} +authors = [ + {name = "Matteo Golin", email = "matteo.golin@gmail.com"}, + {name = "Thomas Selwyn"}, + {name = "Samuel Dewan"}, + {name = "Arsalan Syed"} +] +requires-python = ">=3.11" +dynamic = ["dependencies"] + +[project.urls] +Homepage = "https://github.com/CarletonURocketry/ground-station" + +[tool.setuptools.dynamic] +dependencies = {file = ["requirements.txt"]} + [tool.pyright] # Strict checking From 855366de34e5f12b3f11ce0b5e6c16929b6887e3 Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 17:21:17 -0400 Subject: [PATCH 22/25] Due to a bug in the multprocessing library (to which I am seeking help from CPython devs), the Queue method and the multiprocessing.queues.Queue type are not subscriptable and cause a runtime error. See issue 99509 in the Cpython repo. This was supposed to be fixed in a PR but I guess never was. In the meantime, I have solved the error by using queue.Queue as the type, which is subscriptable and integrates/fools pyright. --- main.py | 22 ++++++++++++---------- modules/serial/serial_manager.py | 3 ++- modules/serial/serial_rn2483_emulator.py | 3 ++- modules/serial/serial_rn2483_radio.py | 3 ++- modules/telemetry/replay.py | 2 +- modules/telemetry/telemetry.py | 10 ++++++---- 6 files changed, 25 insertions(+), 18 deletions(-) diff --git a/main.py b/main.py index 903edad..efba3d7 100644 --- a/main.py +++ b/main.py @@ -5,7 +5,9 @@ # Thomas Selwyn (Devil) # Matteo Golin (linguini1) -from multiprocessing import Process, Queue +import multiprocessing as mp +from multiprocessing import Process +from queue import Queue from re import sub import logging from typing import TypeAlias, Any @@ -50,15 +52,15 @@ class ShutdownException(Exception): def main(): # Set up queues - serial_status: Queue[str] = Queue() - ws_commands: Queue[str] = Queue() - serial_ws_commands: Queue[list[str]] = Queue() - telemetry_ws_commands: Queue[list[str]] = Queue() - - radio_signal_report: Queue[str] = Queue() - rn2483_radio_input: Queue[str] = Queue() - rn2483_radio_payloads: Queue[str] = Queue() - telemetry_json_output: Queue[JSON] = Queue() + serial_status: Queue[str] = mp.Queue() # type: ignore + ws_commands: Queue[str] = mp.Queue() # type: ignore + serial_ws_commands: Queue[list[str]] = mp.Queue() # type: ignore + telemetry_ws_commands: Queue[list[str]] = mp.Queue() # type: ignore + + radio_signal_report: Queue[str] = mp.Queue() # type: ignore + rn2483_radio_input: Queue[str] = mp.Queue() # type: ignore + rn2483_radio_payloads: Queue[str] = mp.Queue() # type: ignore + telemetry_json_output: Queue[JSON] = mp.Queue() # type: ignore # Print display screen print_cu_rocket("No Name (Gas Propelled Launching Device)", VERSION) diff --git a/modules/serial/serial_manager.py b/modules/serial/serial_manager.py index 7c7a528..27b96cf 100644 --- a/modules/serial/serial_manager.py +++ b/modules/serial/serial_manager.py @@ -7,7 +7,8 @@ import glob import sys import logging -from multiprocessing import Process, Queue, active_children +from queue import Queue +from multiprocessing import Process, active_children from serial import Serial, SerialException from modules.misc.config import Config from modules.serial.serial_rn2483_radio import SerialRN2483Radio diff --git a/modules/serial/serial_rn2483_emulator.py b/modules/serial/serial_rn2483_emulator.py index 1de0bad..4623628 100644 --- a/modules/serial/serial_rn2483_emulator.py +++ b/modules/serial/serial_rn2483_emulator.py @@ -7,7 +7,8 @@ import random import struct import time -from multiprocessing import Process, Queue +from queue import Queue +from multiprocessing import Process from datetime import datetime diff --git a/modules/serial/serial_rn2483_radio.py b/modules/serial/serial_rn2483_radio.py index 704d42c..5bb6133 100644 --- a/modules/serial/serial_rn2483_radio.py +++ b/modules/serial/serial_rn2483_radio.py @@ -10,7 +10,8 @@ # Imports import time import logging -from multiprocessing import Queue, Process +from queue import Queue +from multiprocessing import Process from typing import Optional from serial import Serial, SerialException, EIGHTBITS, PARITY_NONE diff --git a/modules/telemetry/replay.py b/modules/telemetry/replay.py index 52ed13f..f6dca2f 100644 --- a/modules/telemetry/replay.py +++ b/modules/telemetry/replay.py @@ -7,7 +7,7 @@ import logging import struct from time import time, sleep -from multiprocessing import Queue +from queue import Queue from pathlib import Path from typing import BinaryIO diff --git a/modules/telemetry/telemetry.py b/modules/telemetry/telemetry.py index 64095e9..7f45afc 100644 --- a/modules/telemetry/telemetry.py +++ b/modules/telemetry/telemetry.py @@ -9,7 +9,9 @@ import logging import math from ast import literal_eval -from multiprocessing import Queue, Process, active_children +from queue import Queue +import multiprocessing as mp +from multiprocessing import Process, active_children from pathlib import Path # Imports @@ -131,8 +133,8 @@ def __init__( # Replay System self.replay = None - self.replay_input: Queue[str] = Queue() - self.replay_output: Queue[tuple[int, int, str]] = Queue() + self.replay_input: Queue[str] = mp.Queue() # type:ignore + self.replay_output: Queue[tuple[int, int, str]] = mp.Queue() # type:ignore # Handle program closing to ensure no orphan processes signal(SIGTERM, shutdown_sequence) # type:ignore @@ -284,7 +286,7 @@ def stop_replay(self) -> None: self.reset_data() # Empty replay output - self.replay_output = Queue() + self.replay_output: Queue[tuple[int, int, str]] = mp.Queue() # type: ignore def play_mission(self, mission_name: str | None) -> None: """Plays the desired mission recording.""" From 5d74d71c2ff97827c080d834f28db5eaf246ad5c Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 17:23:18 -0400 Subject: [PATCH 23/25] Updated formatting. --- modules/misc/converter.py | 7 ++++--- modules/telemetry/data_block.py | 12 +++--------- modules/websocket/websocket.py | 3 --- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/modules/misc/converter.py b/modules/misc/converter.py index dd56482..ca74b83 100644 --- a/modules/misc/converter.py +++ b/modules/misc/converter.py @@ -2,19 +2,20 @@ # Thomas Selwyn (Devil) # Matteo Golin (linguini1) + def celsius_to_fahrenheit(celsius: float) -> float: - """ Returns the passed Celsius value in Fahrenheit as a float. """ + """Returns the passed Celsius value in Fahrenheit as a float.""" return round((celsius * 1.8) + 32, 1) def metres_to_feet(metres: float) -> float: - """ Returns the passed metres value in feet as a float. """ + """Returns the passed metres value in feet as a float.""" return round(metres * 3.28, 1) def pascals_to_psi(pascals: int) -> float: - """ Returns the passed pascals value in psi as a float. """ + """Returns the passed pascals value in psi as a float.""" return round(pascals / 6895, 2) diff --git a/modules/telemetry/data_block.py b/modules/telemetry/data_block.py index d24219e..9cac5de 100644 --- a/modules/telemetry/data_block.py +++ b/modules/telemetry/data_block.py @@ -1143,9 +1143,7 @@ def from_payload(cls, payload: bytes) -> Self: try: mag_sample_rate = MPU9250MagSR((parts[1] >> 8) & 0x1) except ValueError as error: - raise DataBlockException( - f"Invalid MPU9250 magnetometer sample rate: {(parts[1] >> 8) & 0x1}" - ) from error + raise DataBlockException(f"Invalid MPU9250 magnetometer sample rate: {(parts[1] >> 8) & 0x1}") from error try: accel_fsr = MPU9250AccelFSR((parts[1] >> 9) & 0x3) @@ -1157,16 +1155,12 @@ def from_payload(cls, payload: bytes) -> Self: try: gyro_fsr = MPU9250GyroFSR((parts[1] >> 11) & 0x3) except ValueError as error: - raise DataBlockException( - f"Invalid MPU9250 gyroscope full scale range: {(parts[1] >> 11) & 0x3}" - ) from error + raise DataBlockException(f"Invalid MPU9250 gyroscope full scale range: {(parts[1] >> 11) & 0x3}") from error try: accel_bw = MPU9250AccelBW((parts[1] >> 13) & 0x7) except ValueError as error: - raise DataBlockException( - f"Invalid MPU9250 accelerometer bandwidth: {(parts[1] >> 13) & 0x7}" - ) from error + raise DataBlockException(f"Invalid MPU9250 accelerometer bandwidth: {(parts[1] >> 13) & 0x7}") from error try: gyro_bw = MPU9250GyroBW((parts[1] >> 16) & 0x7) diff --git a/modules/websocket/websocket.py b/modules/websocket/websocket.py index 5f785d1..26fa711 100644 --- a/modules/websocket/websocket.py +++ b/modules/websocket/websocket.py @@ -43,7 +43,6 @@ def __init__(self, telemetry_json_output: Queue[Any], ws_commands: Queue[Any]): self.start_websocket_server() def start_websocket_server(self) -> None: - """Starts up the websocket server.""" wss = tornado.web.Application( @@ -63,7 +62,6 @@ def start_websocket_server(self) -> None: io_loop.start() def check_for_messages(self) -> Optional[str]: - """Returns any JSON data that may be on the telemetry JSON output queue.""" json_data = None @@ -100,7 +98,6 @@ def check_origin(self, _) -> bool: @classmethod def send_message(cls, message: str | None) -> None: - if message is None or message == "null": return From b7babc6aaff9d3ba3cb7f156ee2909f69d7ab44b Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 17:25:01 -0400 Subject: [PATCH 24/25] Updated .gitignore to ignore .idea folder and removed it from repo. --- .gitignore | 2 +- .idea/.gitignore | 3 --- .idea/.name | 1 - .idea/codeStyles/codeStyleConfig.xml | 5 ---- .idea/ground-station.iml | 10 ------- .idea/inspectionProfiles/Project_Default.xml | 15 ----------- .../inspectionProfiles/profiles_settings.xml | 6 ----- .idea/jsonSchemas.xml | 26 ------------------- .idea/misc.xml | 7 ----- .idea/modules.xml | 8 ------ .idea/vcs.xml | 7 ----- 11 files changed, 1 insertion(+), 89 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/.name delete mode 100644 .idea/codeStyles/codeStyleConfig.xml delete mode 100644 .idea/ground-station.iml delete mode 100644 .idea/inspectionProfiles/Project_Default.xml delete mode 100644 .idea/inspectionProfiles/profiles_settings.xml delete mode 100644 .idea/jsonSchemas.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/vcs.xml diff --git a/.gitignore b/.gitignore index 8c26eaa..6efa713 100644 --- a/.gitignore +++ b/.gitignore @@ -135,4 +135,4 @@ dmypy.json /missions/ # Misc -/.idea/ \ No newline at end of file +.idea/ diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d3352..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index a5205a1..0000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -telemetry.py \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index a55e7a1..0000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/ground-station.iml b/.idea/ground-station.iml deleted file mode 100644 index e345c49..0000000 --- a/.idea/ground-station.iml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index ea4a809..0000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 105ce2d..0000000 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/jsonSchemas.xml b/.idea/jsonSchemas.xml deleted file mode 100644 index 5bf76f8..0000000 --- a/.idea/jsonSchemas.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index fd958f2..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 848e7f1..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 8306744..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file From efac40a91a7d44b09844df722e7346f338eea419 Mon Sep 17 00:00:00 2001 From: linguini1 Date: Thu, 15 Jun 2023 19:30:15 -0400 Subject: [PATCH 25/25] Removed serial definition from init method. --- modules/serial/serial_rn2483_radio.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/modules/serial/serial_rn2483_radio.py b/modules/serial/serial_rn2483_radio.py index 5bb6133..a4ecdac 100644 --- a/modules/serial/serial_rn2483_radio.py +++ b/modules/serial/serial_rn2483_radio.py @@ -65,16 +65,8 @@ def __init__( self.serial_port = serial_port self.settings = settings + self.serial: Serial - self.serial = Serial( - port=self.serial_port, - timeout=1, - baudrate=57600, - bytesize=EIGHTBITS, - parity=PARITY_NONE, - stopbits=1, - rtscts=False, - ) self.run() def run(self):