Skip to content

Commit

Permalink
Merge branch 'main' into kristatraboulay/426-update-physics-engine-node
Browse files Browse the repository at this point in the history
  • Loading branch information
kristatraboulay authored Nov 2, 2024
2 parents e6e8a96 + 70ad254 commit 5e12947
Show file tree
Hide file tree
Showing 11 changed files with 72 additions and 48 deletions.
4 changes: 3 additions & 1 deletion .devcontainer/base-dev/base-dev.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -374,4 +374,6 @@ ENV DEBIAN_FRONTEND=
# install dev python3 dependencies
RUN pip3 install \
# for juypter notebooks
ipykernel
ipykernel \
# to generate ompl python bindings
pybind11
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ class FluidGenerator:
"""This class provides functionality to generate velocity vectors representing fluid movements.
Attributes:
`generator` (VectorGenerator): The vector generator used to generate 3D fluid velocities.
`generator` (VectorGenerator): The vector generator used to generate 2D fluid velocities.
`velocity` (NDArray): The most recently generated fluid velocity vector, expressed in
meters per second (m/s). It is expected to be a 3D vector.
"""

def __init__(self, generator: VectorGenerator):
self.__generator = generator
self.__velocity = np.array(self.__generator.next())
assert self.__velocity.shape == (3,)
assert self.__velocity.shape == (2,)

def next(self) -> NDArray:
"""Generates the next velocity vector for the fluid simulation.
Expand Down
38 changes: 19 additions & 19 deletions src/boat_simulator/tests/unit/nodes/physics_engine/test_fluids.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ class TestFluidGenerator:
@pytest.mark.parametrize(
"vector",
[
(np.array([1, 0, 1])),
(np.array([0, 1, 0])),
(np.array([1, 0, 0])),
(np.array([1, 0])),
(np.array([0, 1])),
(np.array([1, 0])),
],
)
def test_velocity_constant(self, vector):
Expand All @@ -26,10 +26,10 @@ def test_velocity_constant(self, vector):
@pytest.mark.parametrize(
"mean, cov",
[
(np.array([1, 2, 0]), np.array([[2, 1, 1], [1, 2, 2], [3, 1, 2]])),
(np.array([4, 5, 3]), np.array([[3, 1, 2], [1, 3, 2], [1, 2, 2]])),
(np.array([100, 50, 20]), np.array([[10, 5, 5], [5, 10, 1], [6, 2, 5]])),
(np.array([120, 130, 40]), np.array([[10, 5, 0], [5, 10, 2], [1, 3, 5]])),
(np.array([1, 2]), np.array([[2, 1], [1, 2]])),
(np.array([4, 5]), np.array([[3, 1], [1, 3]])),
(np.array([100, 50]), np.array([[10, 5], [5, 10]])),
(np.array([120, 130]), np.array([[10, 5], [5, 10]])),
],
)
def test_velocity_random(self, mean, cov):
Expand All @@ -42,12 +42,12 @@ def test_velocity_random(self, mean, cov):
@pytest.mark.parametrize(
"vector",
[
(np.array([1, 0, 1])),
(np.array([0, 1, 0])),
(np.array([-1, 0, 1])),
(np.array([0, -1, 0])),
(np.array([1, 1, 1])),
(np.array([-1, -1, -1])),
(np.array([1, 0])),
(np.array([0, 1])),
(np.array([-1, 0])),
(np.array([0, -1])),
(np.array([1, 1])),
(np.array([-1, -1])),
],
)
def test_speed(self, vector):
Expand All @@ -60,12 +60,12 @@ def test_speed(self, vector):
@pytest.mark.parametrize(
"vector, expected_direction",
[
(np.array([1, 0, 1]), 0),
(np.array([0, 1, -3]), 90),
(np.array([-1, 0, -1]), -180),
(np.array([0, -1, 0]), -90),
(np.array([1, 1, 4]), 45),
(np.array([-1, -1, 6]), -135),
(np.array([1, 0]), 0),
(np.array([0, 1]), 90),
(np.array([-1, 0]), -180),
(np.array([0, -1]), -90),
(np.array([1, 1]), 45),
(np.array([-1, -1]), -135),
],
)
def test_direction(self, vector, expected_direction):
Expand Down
4 changes: 2 additions & 2 deletions src/global_launch/config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ specified within an array: one for the `x` component, and one for the `y` compon

- _Description_: The mean value for the wind generated, expressed in kilometers per hour (km/h), for the multivariate
Gaussian generator.
- _Datatype_: `double` array, length 3
- _Datatype_: `double` array, length 2
- _Range_: `(0.0, MAX_DOUBLE)`

**`wind_generation.mvgaussian_params.cov`**
Expand All @@ -276,7 +276,7 @@ since ROS parameters do not support native 2D array types.

- _Description_: The mean value for the current generated, expressed in kilometers per hour (km/h), for the multivariate
Gaussian generator.
- _Datatype_: `double` array, length 3
- _Datatype_: `double` array, length 2
- _Range_: `(0.0, MAX_DOUBLE)`

**`current_generation.mvgaussian_params.cov`**
Expand Down
8 changes: 4 additions & 4 deletions src/global_launch/config/globals.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@ physics_engine_node:
value: [1.0, 0.0]
wind_generation:
mvgaussian_params:
mean: [5.0, 5.0, 0.0]
cov: "[[25.0, 10.0, 5.0], [10.0, 15.0, 2.0], [5.0, 2.0, 20.0]]"
mean: [5.0, 5.0]
cov: "[[25.0, 10.0], [10.0, 15.0]]"
current_generation:
mvgaussian_params:
mean: [1.0, 0.5, 0.0]
cov: "[[0.5, 0.1, 0.05], [0.1, 0.3, 0.02], [0.05, 0.02, 0.2]]"
mean: [1.0, 0.5]
cov: "[[0.5, 0.1], [0.1, 0.3]]"
data_collection_node:
ros__parameters:
file_name: 'ros_data_collection'
Expand Down
2 changes: 2 additions & 0 deletions src/local_pathfinding/local_pathfinding/local_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ def __init__(
HelperLatLon(latitude=waypoint.latitude, longitude=waypoint.longitude)
for waypoint in global_path.waypoints
]
else:
self.global_path = global_path

if filtered_wind_sensor: # TODO: remove when mock can be run
self.wind_speed = filtered_wind_sensor.speed.speed
Expand Down
5 changes: 3 additions & 2 deletions src/local_pathfinding/local_pathfinding/ompl_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@
won't work). The C++ API is documented on the OMPL website:
https://ompl.kavrakilab.org/api_overview.html.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, List, Tuple, Type

from custom_interfaces.msg import HelperLatLon
from ompl import base as ob
from ompl import geometric as og
from ompl import util as ou
from rclpy.impl.rcutils_logger import RcutilsLogger

import local_pathfinding.coord_systems as cs
from custom_interfaces.msg import HelperLatLon
from local_pathfinding.objectives import get_sailing_objective

if TYPE_CHECKING:
Expand All @@ -39,7 +40,7 @@ def __init__(self, local_path_state: LocalPathState, logger: RcutilsLogger):

self.reference_latlon = (
local_path_state.global_path[-1]
if local_path_state and len(local_path_state.global_path) > 0
if local_path_state.global_path and len(local_path_state.global_path) > 0
else HelperLatLon(latitude=0.0, longitude=0.0)
)

Expand Down
9 changes: 8 additions & 1 deletion src/local_pathfinding/test/test_ompl_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,14 @@


def test_OMPLPathState():
state = ompl_path.OMPLPathState(local_path_state=None, logger=RcutilsLogger())
local_path_state = LocalPathState(
gps=GPS(),
ais_ships=AISShips(),
global_path=Path(),
filtered_wind_sensor=WindSensor(),
planner="rrtstar",
)
state = ompl_path.OMPLPathState(local_path_state, logger=RcutilsLogger())
assert state.state_domain == (-1, 1), "incorrect value for attribute state_domain"
assert state.state_range == (-1, 1), "incorrect value for attribute start_state"
assert state.start_state == pytest.approx(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,8 @@ TEST_F(TestRemoteTransceiver, rockblockWebServerExample)
curl = curl_easy_init();

if (curl != nullptr) {
curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:8100/?data=B&imei=300434065264590&username=myuser");
curl_easy_setopt(
curl, CURLOPT_URL, "http://localhost:8100/?data=thisistestdata&ec=B&imei=300434065264590&username=myuser");

curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");

Expand Down
11 changes: 8 additions & 3 deletions src/network_systems/scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,13 @@ This web server is a virtual implementation of the Rockblock Web Servers used to
[here](https://docs.groundcontrol.com/iot/rockblock/web-services/sending-mt-message).

This virtual server follows this specification with one notable difference, it will return the specific error code that
is contained in the `data` section of the request sent to it.
is contained in the `ec` section of the request sent to it.

So a request to this url: <http://localhost:8100/?data=B&imei=300434065264590&username=myuser> will return
`FAILED,11, No RockBLOCK with this IMEI found on your account` since `data=B` and B is hex for 11 which is the
So a request to this url: <http://localhost:8100/?data=thisistestdata&ec=B&imei=300434065264590&username=myuser> will
return `FAILED,11, No RockBLOCK with this IMEI found on your account` since `ec=B` and B is hex for 11 which is the
corresponding error code as mentioned above.

Continuing on, the data parameter now implements the functionality for any form of data to be handled by the server.
This data will be written to `TEMP_FILE_PATH` so that any tests can properly verify that the rockblock web server will
have received the correct data. So as above, if all goes well, `thisistestdata` should be written to the file located
at `TEMP_FILE_PATH`.
32 changes: 19 additions & 13 deletions src/network_systems/scripts/rockblock_web_server.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import http.server
import logging
import os
import random
import signal
import socketserver
Expand All @@ -24,29 +25,32 @@
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)

TEMP_FILE_PATH = "/tmp/remote_transceiver/downstream_test.log"


# Custom HTTP request handler
class MyHandler(http.server.BaseHTTPRequestHandler):

def do_POST(self):
logger.debug("Received POST request.")
# Extract data parameter from the request
data = self.get_data()
data, error_code = self.get_data()
logger.debug(f"Extracted data: {data}")

# Check if data is not empty
if data:
try:
# Decode the hex data
error_code = int(data, 16)
logger.debug(f"Decoded error code: {error_code}")
except ValueError:
# If decoding fails, return error code 99
logger.error("Failed to decode data. Using error code 99.")
error_code = 99
else:
# Default error code if data is empty
logger.warning("No data found. Using error code 99.")
os.makedirs(os.path.dirname(TEMP_FILE_PATH), exist_ok=True)
with open(TEMP_FILE_PATH, "w") as f:
f.write(data)
logger.info(f"Data written to {TEMP_FILE_PATH}")
logger.info(f"Data written is: {data}")
except Exception as e:
logger.error(f"Failed to write data to file: {e}")

try:
error_code = int(error_code, 16) if error_code else 99
except ValueError:
logger.error("Failed to decode ec parameter. Using error code 99.")
error_code = 99

# Handle error codes and special case for error_code 0
Expand Down Expand Up @@ -75,7 +79,9 @@ def get_data(self):
"""Extracts the data parameter from the URL query string (e.g., ?data=48656C6C6F)"""
query = parse.urlsplit(self.path).query
params = dict(parse.parse_qsl(query))
return params.get("data", "")
data = params.get("data", "")
error_code = params.get("ec", "")
return data, error_code


# Set up the server
Expand Down

0 comments on commit 5e12947

Please sign in to comment.