Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ardupilot_manager: use new numeric --serialN ports instead of confusing (and deprecated) --uartX #3082

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
class="v-card v-sheet theme--light mt-5"
>
<v-combobox
v-for="port in Object.keys(port_map)"
v-for="port in range(1, 9)"
:key="port"
v-model="ports[port]"
:items="available_ports"
:label="`${port_map[port]}`"
:label="`Serial ${port}`"
:rules="[isValidEndpoint]"
:loading="current_serial_ports.isEmpty()"
outlined
Expand Down Expand Up @@ -65,6 +65,7 @@
</template>

<script lang="ts">
import { range } from 'lodash'
import Vue from 'vue'

import * as AutopilotManager from '@/components/autopilot/AutopilotManagerUpdater'
Expand All @@ -86,19 +87,6 @@ export default Vue.extend({
},
data() {
return {
// Portmap is used to deal with ardupilots weird mapping situation
// Note that the mapping is not linear
port_map: {
// A (Serial 0) is reserved
C: 'Serial 1',
D: 'Serial 2',
B: 'Serial 3',
E: 'Serial 4',
F: 'Serial 5',
G: 'Serial 6',
H: 'Serial 7',
I: 'Serial 8',
},
ports: {} as Dictionary<string | undefined>,
fetch_autopilot_serial_config_task: new OneMoreTime({ delay: 10000, disposeWith: this }),
fetch_serial_task: new OneMoreTime({ delay: 10000, disposeWith: this }),
Expand Down Expand Up @@ -144,6 +132,7 @@ export default Vue.extend({
}
}
},
range,
async saveAndRestart(): Promise<void> {
await back_axios({
method: 'put',
Expand Down
29 changes: 25 additions & 4 deletions core/services/ardupilot_manager/autopilot_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,10 @@ def get_serials(self) -> List[Serial]:
logger.error(e)
return serials

def get_serial_cmdline(self) -> str:
return " ".join([f"-{entry.port} {entry.endpoint}" for entry in self.get_serials()])
def get_serial_cmdline(self, supports_new_serial_mapping: bool) -> str:
if supports_new_serial_mapping:
return " ".join([f"--serial{entry.port} {entry.endpoint}" for entry in self.get_serials()])
return " ".join([f"-{entry.port_as_letter} {entry.endpoint}" for entry in self.get_serials()])

def get_default_params_cmdline(self, platform: Platform) -> str:
# check if file exists and return it's path as --defaults parameter
Expand All @@ -202,6 +204,17 @@ def get_default_params_cmdline(self, platform: Platform) -> str:
return f"--defaults {default_params_path}"
return ""

def check_supports_new_serial_mapping(self, firmware: pathlib.Path) -> bool:
"""
check if the firmware supports --serialN instead of --uartX by checking the output of --help
"""
try:
output = subprocess.check_output([firmware, "--help"], encoding="utf-8")
return "--serial" in output
except Exception as e:
logger.warning(f"Failed to check if firmware supports new serial mapping: {e}")
return False

async def start_linux_board(self, board: LinuxFlightController) -> None:
self._current_board = board
if not self.firmware_manager.is_firmware_installed(self._current_board):
Expand Down Expand Up @@ -248,12 +261,20 @@ async def start_linux_board(self, board: LinuxFlightController) -> None:
#
# The first column comes from https://ardupilot.org/dev/docs/sitl-serial-mapping.html

supports_new_serial_mapping = self.check_supports_new_serial_mapping(firmware_path)

master_endpoint_str = (
f" --serial0 udp:{master_endpoint.place}:{master_endpoint.argument}"
if supports_new_serial_mapping
else f" -A udp:{master_endpoint.place}:{master_endpoint.argument}"
)

command_line = (
f"{firmware_path}"
f" -A udp:{master_endpoint.place}:{master_endpoint.argument}"
f"{master_endpoint_str}"
f" --log-directory {self.settings.firmware_folder}/logs/"
f" --storage-directory {self.settings.firmware_folder}/storage/"
f" {self.get_serial_cmdline()}"
f" {self.get_serial_cmdline(supports_new_serial_mapping)}"
f" {self.get_default_params_cmdline(board.platform)}"
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ class NavigatorPi5(Navigator):

def get_serials(self) -> List[Serial]:
return [
Serial(port="C", endpoint="/dev/ttyAMA0"),
Serial(port="B", endpoint="/dev/ttyAMA2"),
Serial(port="E", endpoint="/dev/ttyAMA3"),
Serial(port="F", endpoint="/dev/ttyAMA4"),
Serial(port=1, endpoint="/dev/ttyAMA0"),
Serial(port=3, endpoint="/dev/ttyAMA2"),
Serial(port=4, endpoint="/dev/ttyAMA3"),
Serial(port=5, endpoint="/dev/ttyAMA4"),
]

def detect(self) -> bool:
Expand All @@ -76,17 +76,17 @@ def get_serials(self) -> List[Serial]:
match release:
case "Bullseye":
return [
Serial(port="C", endpoint="/dev/ttyS0"),
Serial(port="B", endpoint="/dev/ttyAMA1"),
Serial(port="E", endpoint="/dev/ttyAMA2"),
Serial(port="F", endpoint="/dev/ttyAMA3"),
Serial(port="1", endpoint="/dev/ttyS0"),
Serial(port="3", endpoint="/dev/ttyAMA1"),
Serial(port="4", endpoint="/dev/ttyAMA2"),
Serial(port="5", endpoint="/dev/ttyAMA3"),
]
case "Bookworm":
return [
Serial(port="C", endpoint="/dev/ttyS0"),
Serial(port="B", endpoint="/dev/ttyAMA3"),
Serial(port="E", endpoint="/dev/ttyAMA4"),
Serial(port="F", endpoint="/dev/ttyAMA5"),
Serial(port="1", endpoint="/dev/ttyS0"),
Serial(port="3", endpoint="/dev/ttyAMA3"),
Serial(port="4", endpoint="/dev/ttyAMA4"),
Serial(port="5", endpoint="/dev/ttyAMA5"),
]
raise RuntimeError("Unknown release, unable to map ports")

Expand Down
26 changes: 19 additions & 7 deletions core/services/ardupilot_manager/typedefs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from enum import Enum, auto
from pathlib import Path
from platform import machine
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Optional, Union

from pydantic import BaseModel, validator

Expand Down Expand Up @@ -177,15 +177,22 @@ class Serial(BaseModel):
--serial5 /dev/ttyAMA3
"""

port: str
port: int
endpoint: str

@validator("port")
@classmethod
def valid_letter(cls: Any, value: str) -> str:
if value in "BCDEFGH" and len(value) == 1:
return value
raise ValueError(f"Invalid serial port: {value}. These must be between B and H. A is reserved.")
def valid_letter(cls: Any, value: Union[str, int]) -> int:
letters = ["A", "C", "D", "B", "E", "F", "G", "H", "I", "J"]
if isinstance(value, str) and value in letters and len(value) == 1:
return letters.index(value)
try:
port = int(value)
if port in range(1, 10):
return port
except (ValueError, TypeError):
pass
raise ValueError(f"Invalid serial port: {value}. These must be between B(1) and J(9). A(0) is reserved.")

@validator("endpoint")
@classmethod
Expand All @@ -202,4 +209,9 @@ def valid_endpoint(cls: Any, value: str) -> str:
raise ValueError(f"Invalid endpoint configuration: {value}")

def __hash__(self) -> int: # make hashable BaseModel subclass
return hash(self.port + self.endpoint)
return hash(str(self.port) + self.endpoint)

@property
def port_as_letter(self) -> str:
letters = ["A", "C", "D", "B", "E", "F", "G", "H", "I", "J"]
return letters[self.port]
Loading