Skip to content

Add Linux build support for headless deployment (issue #48)#50

Draft
chriskinal wants to merge 3 commits into
developfrom
feat/linux-build-issue-48
Draft

Add Linux build support for headless deployment (issue #48)#50
chriskinal wants to merge 3 commits into
developfrom
feat/linux-build-issue-48

Conversation

@chriskinal
Copy link
Copy Markdown

@chriskinal chriskinal commented May 18, 2026

Summary

Draft PR to validate the cross-platform refactor on CI before proposing for merge. Addresses #48 — enables building and running AOG-TaskController as a headless Linux service on x86_64 and aarch64 (Raspberry Pi 3/4/5/Zero 2 W, generic Linux SBCs), and documents the UDP protocol for AgIO / AgValonia / third-party clients.

The Windows codepath is preserved bit-for-bit:

  • WinMain, AttachConsole, hidden tool window, message pump, and lifecycle ordering are unchanged.
  • SHGetFolderPathA(CSIDL_APPDATA) is the same call the original build linked against — no new ole32 dependency.
  • localtime_s, WIN32_EXECUTABLE, AppIcon.rc, and all DLL copy/install commands still execute on Windows.
  • Existing windows_build and windows_release workflow jobs are untouched; the new Linux jobs run in parallel.

What changed

Cross-platform code

  • src/main.cpp — split entry point: WinMain (Windows) and main(argc,argv) (POSIX) with SIGINT/SIGTERM handlers and a new --can_adapter=socketcan option.
  • src/settings.cpp — portable config dir via std::filesystem. Windows: %APPDATA%\AOG-TaskController; Linux: $XDG_CONFIG_HOME/~/.config/AOG-TaskController; macOS: ~/Library/Application Support/AOG-TaskController.
  • src/logging.cpp / include/logging_utils.hpplocaltime_r on POSIX, localtime_s on Windows.
  • src/udp_connections.cppSO_REUSEADDR on both UDP sockets (needed on Linux when binding 0.0.0.0 alongside a specific-IP socket) and getifaddrs(3) interface enumeration on POSIX (the hostname resolver returns 127.0.1.1 on stock Ubuntu and never matches a LAN subnet).
  • src/app.cpp / include/settings.hpp — explicit <iostream>/<cstdint> that were arriving transitively through windows.h.

CMake

  • WIN32_EXECUTABLE, AppIcon.rc, and all four Windows DLL copy/install blocks gated behind if(WIN32).
  • CAN_DRIVER defaults per platform — Windows: PCAN+InnoMaker+TouCAN+SYS_TEC; Linux: SocketCAN; macOS: MacCANPCAN. Still overridable via -DCAN_DRIVER=.

CI / packaging

  • .github/workflows/build.yml — new linux_build job with a matrix producing both linux-x86_64 (ubuntu-latest) and linux-aarch64 (ubuntu-24.04-arm) tarballs. The aarch64 tarball is what runs on Raspberry Pi under 64-bit Raspberry Pi OS.
  • .github/workflows/release.ymllinux_release mirrors the Windows release with the same matrix; both tarballs attach to the same GitHub Release via softprops/action-gh-release@v2 (idempotent on tag_name).
  • resources/linux/ — sample settings.json and a systemd unit shipped inside the tarball.

Documentation

  • docs/LINUX_DAEMON.md — deployment guide for running the TC as a headless Linux service. Covers hardware targets, resource footprint (CPU/RAM/network), MCP2515 HAT and USB-CAN setup, the systemd unit, settings.json, and troubleshooting. Pi Zero 2 W is called out specifically.
  • docs/PROTOCOL.md — UDP wire-protocol reference for client authors. Frame format (0x8081 start + checksum), ports 8888/9999, every PGN the TC sends/receives byte-by-byte (0xC9, 0xE5, 0xF0, 0xF1, 0xF2), settings + CLI reference, ISOBUS/CAN overview, and Python + C# example clients (the latter targeting AgValonia on .NET 8+).
  • readme.md — links both new docs at the top.

Test plan

Already verified on Ubuntu 24.04 aarch64 against vcan0:

  • Build with cmake/ninja
  • --help / --version work
  • SocketCAN attach to vcan0
  • TC + TECU address claim with J1939-81 250ms post-claim delay
  • UDP bind on real LAN interface via getifaddrs
  • Synthetic AgIO subnet-detection → rebind
  • Synthetic section-control enable received
  • Synthetic 64-section state update received
  • SIGTERM triggers graceful shutdown

CI status (latest run):

  • Windows Build — pass
  • Linux Build (x86_64) — pass
  • Linux Build (aarch64) — pass
  • clang-format style — pass
  • cmake-format style — pass

Not testable from this branch alone:

  • macOS build path — code paths exist (SHGetFolderPath replaced by ~/Library/Application Support on __APPLE__; CAN_DRIVER=MacCANPCAN default), but no CI runner added and no host to compile on. Can be added later if/when needed.

🤖 Generated with Claude Code

chriskinal and others added 3 commits May 17, 2026 17:27
Refactor the platform-specific glue so AOG-TaskController can build and
run on Linux as a headless console service, while leaving the Windows
build and CI byte-compatible. Adds a Linux CI job and release tarball
job that mirror the existing Windows ones.

Cross-platform changes:
- main.cpp: split entry point into WinMain (preserved, same lifecycle
  ordering as before) and POSIX main with SIGINT/SIGTERM handlers.
  Adds --can_adapter=socketcan with --can_channel=<iface>.
- settings.cpp: portable config dir via std::filesystem. Windows still
  uses SHGetFolderPathA(CSIDL_APPDATA); Linux uses XDG_CONFIG_HOME or
  ~/.config; macOS uses ~/Library/Application Support.
- logging.cpp / logging_utils.hpp: localtime_r on POSIX, localtime_s
  retained on Windows.
- udp_connections.cpp: SO_REUSEADDR on both UDP sockets (required on
  Linux when binding 0.0.0.0 alongside a specific-IP socket). On POSIX
  enumerate interfaces via getifaddrs(3) because the hostname resolver
  returns 127.0.1.1 on stock Ubuntu and never matches a LAN subnet.
- app.cpp / settings.hpp: add explicit <iostream> / <cstdint> includes
  that were arriving transitively through windows.h.
- CMakeLists.txt: gate WIN32_EXECUTABLE, AppIcon.rc, and the four
  Windows DLL copy/install blocks behind if(WIN32). Default CAN_DRIVER
  per platform (Windows: PCAN+InnoMaker+TouCAN+SYS_TEC; Linux: SocketCAN;
  macOS: MacCANPCAN), still overridable via -DCAN_DRIVER=.

CI / packaging:
- .github/workflows/build.yml: add linux_build job (ubuntu-latest,
  Ninja) producing AOG-TaskController-linux-x86_64.tar.gz.
- .github/workflows/release.yml: add linux_release job mirroring the
  Windows release (same develop-branch safety check, prerelease regex,
  attaches tarball to the GitHub Release).
- resources/linux/: sample settings.json and a systemd unit file
  shipped inside the tarball.

Smoke-tested on Ubuntu 24.04 aarch64 against vcan0: TC and TECU claim
addresses with J1939-81 250ms delay, UDP binds cleanly, AgIO subnet
detection rebind works, section-control-enable and 64-section state
updates are received, SIGTERM triggers graceful shutdown.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitHub provides hosted ubuntu-24.04-arm runners, so we can produce a
native aarch64 binary alongside x86_64 without any cross-compile
plumbing. The aarch64 tarball is what runs on Raspberry Pi (Zero 2 W,
3, 4, 5) under a 64-bit Raspberry Pi OS install.

- build.yml linux_build job: matrix over (x86_64 on ubuntu-latest,
  aarch64 on ubuntu-24.04-arm). Two artifacts uploaded:
  'Linux Tarball (x86_64)' and 'Linux Tarball (aarch64)'.
- release.yml linux_release job: same matrix. Both tarballs attach to
  the same GitHub Release via softprops/action-gh-release@v2
  (idempotent on tag_name).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two new documents under docs/ aimed at client authors and SBC operators:

- LINUX_DAEMON.md walks through deploying AOG-TaskController as a
  headless Linux service: hardware targets (x86_64 and aarch64
  tarballs), resource footprint, MCP2515/USB CAN setup, the systemd
  unit, settings.json, and troubleshooting. Covers Raspberry Pi
  (including Pi Zero 2 W) specifically.

- PROTOCOL.md is a reference for anything writing a client against
  the TC: UDP packet framing (0x8081 start + checksum), ports
  8888/9999, every PGN the TC sends/receives byte-by-byte, the
  ISOBUS/CAN side at a glance, settings + CLI reference, and
  Python + C# example clients (the latter targeting AgValonia
  on .NET 8+).

readme.md links to both at the top so they're easy to find.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant