diff --git a/core/Dockerfile b/core/Dockerfile index 0d13251257..e5b85a5fc0 100644 --- a/core/Dockerfile +++ b/core/Dockerfile @@ -87,6 +87,7 @@ COPY --from=downloadBinaries \ /usr/bin/machineid-cli \ /usr/bin/mavlink2rest \ /usr/bin/mavlink-routerd \ + /usr/bin/mavlink-server \ /usr/bin/mavp2p \ /usr/bin/ttyd \ /usr/bin/ diff --git a/core/services/ardupilot_manager/mavlink_proxy/MAVLinkServer.py b/core/services/ardupilot_manager/mavlink_proxy/MAVLinkServer.py new file mode 100644 index 0000000000..f3490f473f --- /dev/null +++ b/core/services/ardupilot_manager/mavlink_proxy/MAVLinkServer.py @@ -0,0 +1,69 @@ +import re +import subprocess +from typing import Optional + +from mavlink_proxy.AbstractRouter import AbstractRouter +from mavlink_proxy.Endpoint import Endpoint +from typedefs import EndpointType + + +class MAVLinkServer(AbstractRouter): + def __init__(self) -> None: + super().__init__() + + def _get_version(self) -> Optional[str]: + binary = self.binary() + assert binary is not None + for line in subprocess.check_output([binary, "--version"]).decode("utf-8").split("\n"): + regex = re.search(r"mavlink-server (?P\S+)\b", line) + if regex: + return regex.group("version") + + return None + + def assemble_command(self, master_endpoint: Endpoint) -> str: + # Convert endpoint format to mavlink-router format + def convert_endpoint(endpoint: Endpoint) -> str: + if endpoint.connection_type == EndpointType.Serial: + return f"serial:{endpoint.place}:{endpoint.argument}" + if endpoint.connection_type == EndpointType.TCPServer: + return f"tcps:{endpoint.place}:{endpoint.argument}" + if endpoint.connection_type == EndpointType.TCPClient: + return f"tcpc:{endpoint.place}:{endpoint.argument}" + if endpoint.connection_type == EndpointType.UDPServer: + return f"udps:{endpoint.place}:{endpoint.argument}" + if endpoint.connection_type == EndpointType.UDPClient: + return f"udpc:{endpoint.place}:{endpoint.argument}" + raise ValueError(f"Endpoint of type {endpoint.connection_type} not supported on MAVLink-Server.") + + endpoints = " ".join([convert_endpoint(endpoint) for endpoint in [master_endpoint, *self.endpoints()]]) + + return f"{self.binary()} {endpoints}" + + @staticmethod + def name() -> str: + return "MAVLink-Server" + + @staticmethod + def binary_name() -> str: + return "mavlink-server" + + @staticmethod + def _validate_endpoint(endpoint: Endpoint) -> None: + valid_connection_types = [ + EndpointType.UDPClient, + EndpointType.UDPServer, + EndpointType.TCPServer, + EndpointType.TCPClient, + EndpointType.Serial, + ] + if endpoint.connection_type not in valid_connection_types: + raise ValueError(f"Connection_type '{endpoint.connection_type}' not supported by {MAVLinkServer.name()}.") + + @staticmethod + def is_ok() -> bool: + try: + router = MAVLinkServer() + return router.binary() is not None and router.version() is not None + except Exception as _: + return False diff --git a/core/services/ardupilot_manager/mavlink_proxy/Manager.py b/core/services/ardupilot_manager/mavlink_proxy/Manager.py index 2d9a207f4b..185c7c6145 100644 --- a/core/services/ardupilot_manager/mavlink_proxy/Manager.py +++ b/core/services/ardupilot_manager/mavlink_proxy/Manager.py @@ -7,6 +7,7 @@ # Plugins # pylint: disable=unused-import import mavlink_proxy.MAVLinkRouter +import mavlink_proxy.MAVLinkServer import mavlink_proxy.MAVP2P import mavlink_proxy.MAVProxy from exceptions import ( diff --git a/core/tools/install-static-binaries.sh b/core/tools/install-static-binaries.sh index fc3ac37336..3900af3874 100755 --- a/core/tools/install-static-binaries.sh +++ b/core/tools/install-static-binaries.sh @@ -12,6 +12,7 @@ TOOLS=( machineid mavlink2rest mavlink_router + mavlink_server mavp2p ttyd ) diff --git a/core/tools/mavlink_server/bootstrap.sh b/core/tools/mavlink_server/bootstrap.sh new file mode 100755 index 0000000000..391e8fec4a --- /dev/null +++ b/core/tools/mavlink_server/bootstrap.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash + +# Immediately exit on errors +set -e + +VERSION="master" +PROJECT_NAME="mavlink-server" +REPOSITORY_ORG="bluerobotics" +REPOSITORY_NAME="$PROJECT_NAME" +REPOSITORY_URL="https://github.com/$REPOSITORY_ORG/$REPOSITORY_NAME" + +echo "Installing project $PROJECT_NAME version $VERSION" + +# Step 1: Prepare the download URL + +ARCH="$(uname -m)" +case "$ARCH" in + x86_64 | amd64) + BUILD_NAME="x86_64-unknown-linux-musl" + ;; + armv7l | armhf) + BUILD_NAME="armv7-unknown-linux-musleabihf" + ;; + aarch64 | arm64) + BUILD_NAME="aarch64-unknown-linux-musl" + ;; + *) + echo "Architecture: $ARCH is unsupported, please create a new issue on https://github.com/bluerobotics/BlueOS/issues" + exit 1 + ;; +esac +ARTIFACT_NAME="$PROJECT_NAME-$BUILD_NAME" +echo "For architecture $ARCH, using build $BUILD_NAME" + +REMOTE_URL="$REPOSITORY_URL/releases/download/$VERSION/$ARTIFACT_NAME" +echo "Remote URL is $REMOTE_URL" + +# Step 2: Prepare the installation path + +if [ -n "$VIRTUAL_ENV" ]; then + BIN_DIR="$VIRTUAL_ENV/bin" +else + BIN_DIR="/usr/bin" +fi +mkdir -p "$BIN_DIR" + +BINARY_PATH="$BIN_DIR/$PROJECT_NAME" +echo "Installing to $BINARY_PATH" + +# Step 3: Download and install + +wget -q "$REMOTE_URL" -O "$BINARY_PATH" +chmod +x "$BINARY_PATH" + +echo "Installed binary type: $(file "$(which "$BINARY_PATH")")" + +echo "Finished installing $PROJECT_NAME"