diff --git a/requirements.txt b/requirements.txt index fdb4017..ff61e2b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,15 @@ sr-robot3==2025.0.1 april_vision==2.2.0 -opencv-python-headless >=4,<5 +opencv-python-headless >=4.8.0.76,<5 + +# Library versions are selected to provide support for Python 3.9-3.12 + +flask==2.3.3 +matplotlib==3.9.2 +networkx==3.1 +numpy==1.26.4 +pandas==2.2.3 +pillow==11.0.0 +scikit-learn==1.3.1 +scipy==1.11.2 +shapely==2.0.6 diff --git a/scripts/run_comp_match.py b/scripts/run_comp_match.py index 655cc73..9754fd2 100755 --- a/scripts/run_comp_match.py +++ b/scripts/run_comp_match.py @@ -175,8 +175,8 @@ def execute_match(arena_root: Path) -> None: # Webots is only on the PATH on Linux so we have a helper function to find it try: webots, world_file = get_webots_parameters() - except RuntimeError: - raise FileNotFoundError("Webots executable not found.") + except RuntimeError as e: + raise FileNotFoundError(e) sim_env = os.environ.copy() sim_env['ARENA_ROOT'] = str(arena_root) diff --git a/scripts/run_simulator.py b/scripts/run_simulator.py index 8b9f301..e4a6a7d 100755 --- a/scripts/run_simulator.py +++ b/scripts/run_simulator.py @@ -4,6 +4,7 @@ Largely just a shortcut to running the arena world in Webots. """ +# ruff: noqa: E501 from __future__ import annotations import sys @@ -18,11 +19,22 @@ if (Path(__file__).parent / 'simulator/VERSION').exists(): print("Running in release mode") - SIM_BASE = Path(__file__).parent + SIM_BASE = Path(__file__).parent.resolve() else: print("Running in development mode") # Assume the script is in the scripts directory - SIM_BASE = Path(__file__).parents[1] + SIM_BASE = Path(__file__).parents[1].resolve() + +POSSIBLE_WEBOTS_PATHS = [ + ("darwin", "/Applications/Webots.app/Contents/MacOS/webots"), + ("win32", "C:\\Program Files\\Webots\\msys64\\mingw64\\bin\\webotsw.exe"), + ("win32", expandvars("%LOCALAPPDATA%\\Programs\\Webots\\msys64\\mingw64\\bin\\webotsw.exe")), + # Attempt to use the start menu shortcut + ("win32", expandvars("%ProgramData%\\Microsoft\\Windows\\Start Menu\\Programs\\Cyberbotics\\Webots.lnk")), + ("win32", expandvars("%APPDATA%\\Microsoft\\Windows\\Start Menu\\Programs\\Cyberbotics\\Webots.lnk")), + ("linux", "/usr/local/bin/webots"), + ("linux", "/usr/bin/webots"), +] def get_webots_parameters() -> tuple[Path, Path]: @@ -36,45 +48,23 @@ def get_webots_parameters() -> tuple[Path, Path]: if not world_file.exists(): raise RuntimeError("World file not found.") + if not (SIM_BASE / "venv").exists(): + raise RuntimeError("Please run the setup.py script before running the simulator.") + # Check if Webots is in the PATH webots = which("webots") # Find the webots executable, if it is not in the PATH if webots is None: - if sys.platform == "darwin": - webots = "/Applications/Webots.app/Contents/MacOS/webots" - elif sys.platform == "win32": - possible_paths = [ - "C:\\Program Files\\Webots\\msys64\\mingw64\\bin\\webotsw.exe", - expandvars("%LOCALAPPDATA%\\Programs\\Webots\\msys64\\mingw64\\bin\\webotsw.exe"), - ] - for path in possible_paths: - if Path(path).exists(): - webots = path - break - else: - print("Webots executable not found.") - raise RuntimeError - elif sys.platform.startswith("linux"): - possible_paths = ["/usr/local/bin/webots", "/usr/bin/webots"] - for path in possible_paths: + for system_filter, path in POSSIBLE_WEBOTS_PATHS: + if sys.platform.startswith(system_filter): + print(f"Checking {path}") if Path(path).exists(): webots = path break - else: - print("Webots executable not found.") - raise RuntimeError - else: - print("Unsupported platform.") - raise RuntimeError - if not Path(webots).exists(): - print("Webots executable not found.") - raise RuntimeError - - if not (SIM_BASE / "venv").exists(): - print("Please run the setup.py script before running the simulator.") - raise RuntimeError + if webots is None or not Path(webots).exists(): + raise RuntimeError("Webots executable not found.") return Path(webots), world_file @@ -90,10 +80,13 @@ def main() -> None: Popen( [str(webots), str(world_file)], creationflags=DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP, + # shell=True is needed to run from shortcuts + shell=(webots.suffix == ".lnk"), ) else: Popen([str(webots), str(world_file)], start_new_session=True) - except RuntimeError: + except RuntimeError as e: + print(f"An error occurred: {e}") input("Press enter to continue...") exit(1) except Exception as e: diff --git a/scripts/setup.py b/scripts/setup.py index 4b1557e..bbf1d30 100755 --- a/scripts/setup.py +++ b/scripts/setup.py @@ -13,6 +13,7 @@ import logging import platform import shutil +import sys from pathlib import Path from subprocess import run from venv import create @@ -62,13 +63,17 @@ def populate_python_config(runtime_ini: Path, venv_python: Path) -> None: try: if (Path(__file__).parent / 'simulator/VERSION').exists(): # This is running from a release + print("Running in release mode") project_root = Path(__file__).parent requirements = project_root / "simulator/requirements.txt" else: # This is running from the repository + print("Running in development mode") project_root = Path(__file__).parents[1] requirements = project_root / "requirements.txt" + print(f"Python version: {sys.version} on {platform.platform()}") + venv_dir = project_root / "venv" logger.info(f"Creating virtual environment in {venv_dir.absolute()}") diff --git a/simulator/controllers/competition_supervisor/lighting_control.py b/simulator/controllers/competition_supervisor/lighting_control.py index 2485073..30a508c 100644 --- a/simulator/controllers/competition_supervisor/lighting_control.py +++ b/simulator/controllers/competition_supervisor/lighting_control.py @@ -178,7 +178,7 @@ def expand_lighting_fade( """Expand a fade effect into a list of steps.""" fades = [] - assert isinstance(cue.start_time, float), \ + assert isinstance(cue.start_time, (float, int)), \ "FromEnd times should be converted to absolute times" cue_start = int((cue.start_time * 1000) / self.timestep) diff --git a/simulator/modules/robot_utils.py b/simulator/modules/robot_utils.py index 04b2833..4cb02f5 100644 --- a/simulator/modules/robot_utils.py +++ b/simulator/modules/robot_utils.py @@ -2,6 +2,7 @@ from __future__ import annotations import json +import platform import subprocess import sys from pathlib import Path @@ -87,7 +88,10 @@ def print_simulation_version() -> None: except subprocess.CalledProcessError: version = 'unknown' - print(f"Running simulator version: {version}") + print( + f"Running simulator version: {version} in Python {platform.python_version()} " + f"({platform.system()}-{platform.machine()})" + ) def get_match_data() -> MatchData: