Skip to content
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
40 changes: 40 additions & 0 deletions .github/ci/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# CI stub — validates msg/srv definitions as a minimal adnav_interfaces package.
# See .github/workflows/ci.yml for how this is assembled and built.
cmake_minimum_required(VERSION 3.8)
project(adnav_interfaces)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()

find_package(ament_cmake REQUIRED)
find_package(rosidl_default_generators REQUIRED)
find_package(std_msgs REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(builtin_interfaces REQUIRED)

rosidl_generate_interfaces(${PROJECT_NAME}
# Messages
msg/ANPPHeader.msg
msg/DeviceInformationPacket.msg
msg/FilterStatus.msg
msg/GNSSFixStatus.msg
msg/LLH.msg
msg/NED.msg
msg/PacketPeriod.msg
msg/RPH.msg
msg/RawAcknowledge.msg
msg/RawStatusPacket.msg
msg/RequestPacket.msg
msg/SerialInterface.msg
msg/SystemStatus.msg
# Services
srv/Ntrip.srv
srv/PacketPeriods.srv
srv/PacketTimerPeriod.srv
srv/RequestPackets.srv
DEPENDENCIES std_msgs geometry_msgs builtin_interfaces
)

ament_export_dependencies(rosidl_default_runtime)
ament_package()
25 changes: 25 additions & 0 deletions .github/ci/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>adnav_interfaces</name>
<version>0.0.1</version>
<description>
Minimal stub package used by CI to validate the msg/srv definitions in the
an-ros-common submodule. Not intended for installation or release.
</description>
<maintainer email="support@advancednavigation.com.au">Advanced Navigation</maintainer>
<license>MIT</license>

<buildtool_depend>ament_cmake</buildtool_depend>
<buildtool_depend>rosidl_default_generators</buildtool_depend>

<depend>std_msgs</depend>
<depend>geometry_msgs</depend>
<depend>builtin_interfaces</depend>

<member_of_group>rosidl_interface_packages</member_of_group>

<export>
<build_type>ament_cmake</build_type>
</export>
</package>
74 changes: 74 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
name: CI

on:
push:
branches: ["**"]
pull_request:

jobs:
# ---------------------------------------------------------------------------
# Job 1: Compile all C / C++ sources standalone (no ROS 2 required).
# Uses the CMakeLists.txt at the repo root which builds a static library from
# the six source files.
# ---------------------------------------------------------------------------
build-cpp:
name: Build C/C++ (standalone)
runs-on: ubuntu-22.04

steps:
- uses: actions/checkout@v6

- name: Install build tools
run: |
sudo apt-get update -q
sudo apt-get install -y --no-install-recommends build-essential cmake

- name: Configure (CMake)
run: cmake -B build -DCMAKE_BUILD_TYPE=Release

- name: Build
run: cmake --build build --parallel $(nproc)

# ---------------------------------------------------------------------------
# Job 2: Validate the ROS 2 .msg / .srv definitions.
# Assembles a minimal 'adnav_interfaces' colcon package, copies the msg/srv
# files into it, and runs a full rosidl code generation build to verify
# syntax and cross-references.
# ---------------------------------------------------------------------------
validate-msgs:
name: Validate ROS 2 msgs/srvs (Humble)
runs-on: ubuntu-22.04
container:
image: ros:humble-ros-base

steps:
- uses: actions/checkout@v6

- name: Install build dependencies
run: |
apt-get update -q
apt-get install -y --no-install-recommends \
python3-colcon-common-extensions \
ros-humble-rosidl-default-generators \
ros-humble-std-msgs \
ros-humble-geometry-msgs

- name: Assemble validation workspace
run: |
PKGDIR=/tmp/ws/src/adnav_interfaces
mkdir -p "${PKGDIR}/msg" "${PKGDIR}/srv"

# Copy interface definitions from this checkout.
cp msg/*.msg "${PKGDIR}/msg/"
cp srv/*.srv "${PKGDIR}/srv/"

# Copy the CI stub package manifest and build file.
cp .github/ci/package.xml "${PKGDIR}/package.xml"
cp .github/ci/CMakeLists.txt "${PKGDIR}/CMakeLists.txt"

- name: Build interfaces (validates msg/srv syntax)
shell: bash
run: |
source /opt/ros/humble/setup.bash
cd /tmp/ws
colcon build --event-handlers console_direct+
41 changes: 41 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Standalone CMakeLists.txt for the an-ros-common library.
#
# This file exists so the shared C/C++ sources can be compiled and verified
# independently of any ROS 2 workspace (e.g. in CI). It is NOT used when
# building as part of a colcon workspace — colcon discovers packages via
# package.xml, which is intentionally absent here.
cmake_minimum_required(VERSION 3.14)
project(an_ros_common C CXX)

# --- Standards -----------------------------------------------------------------
if(NOT CMAKE_C_STANDARD)
set(CMAKE_C_STANDARD 99)
endif()
set(CMAKE_C_STANDARD_REQUIRED ON)

if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# --- Warnings ------------------------------------------------------------------
if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
add_compile_options(-Wall -Wextra)
endif()

# --- Dependencies --------------------------------------------------------------
# std::thread / std::atomic require the pthreads library on Linux.
find_package(Threads REQUIRED)

# --- Library -------------------------------------------------------------------
add_library(an_ros_common STATIC
src/an_packet_protocol.c
src/ins_packets.c
src/rs232.c
src/adnav_comms.cpp
src/adnav_utils.cpp
src/adnav_ntrip.cpp
)

target_include_directories(an_ros_common PUBLIC include)
target_link_libraries(an_ros_common PUBLIC Threads::Threads)
11 changes: 11 additions & 0 deletions src/adnav_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,17 @@ namespace utils {
// Stream in interface number and name.
ss << "\n\t" << std::setw(2) << std::right << i << " | IF Name: " << ptr_entry->ifa_name << "\t";

// Per getifaddrs(3) the ifa_addr field may be NULL for
// interfaces without an address assigned (common for some
// virtual / unconfigured interfaces, easy to hit inside a
// container's network namespace). Dereferencing it caused
// a SIGSEGV in TCP-server / UDP modes.
if (ptr_entry->ifa_addr == nullptr) {
ss << "(no address)";
i++;
continue;
}

// Check that the interface is IPv4
sa_family_t address_family = ptr_entry->ifa_addr->sa_family;
if (address_family == AF_INET) {
Expand Down
28 changes: 24 additions & 4 deletions src/rs232.c
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ int comSetRts(int index, int state)
#include <termios.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>

#if !defined(__USE_SVID)
#define __USE_SVID // For strdup
Expand Down Expand Up @@ -608,10 +609,29 @@ int comWrite(int index, const unsigned char *buffer, size_t len)
return 0;
if (comDevices[index].handle <= 0)
return 0;
int res = write(comDevices[index].handle, buffer, len);
if (res < 0)
res = 0;
return res;
/* Loop to handle short writes from the kernel TX buffer (rare on serial
* but cheap to be safe). */
size_t total = 0;
while (total < len)
{
ssize_t res = write(comDevices[index].handle,
(const char *)buffer + total, len - total);
if (res < 0)
{
if (errno == EINTR)
continue;
return (int)total;
}
if (res == 0)
break;
total += (size_t)res;
}
/* Block until the bytes have been transmitted on the wire, not just
* queued in the kernel TX buffer. Without this, the caller can issue
* back-to-back configuration packets faster than the device can apply
* them, and AN devices have been observed to NACK the second write. */
tcdrain(comDevices[index].handle);
return (int)total;
}

int comRead(int index, unsigned char *buffer, size_t len)
Expand Down