Compare commits

..

4 Commits

Author SHA1 Message Date
oleg a9b4e4e7db add docker run, arm64 cross-build in docker 2026-04-12 19:01:22 +03:00
oleg 288ccd6aa5 move patches 2026-04-10 18:57:59 +03:00
oleg 0648920c34 fix readme 2026-04-10 13:42:31 +03:00
oleg b7cb570409 fix readme, demo 2026-04-10 13:30:51 +03:00
17 changed files with 393 additions and 228 deletions

View File

@ -2,127 +2,21 @@ cmake_minimum_required(VERSION 3.13)
set(CMAKE_POLICY_VERSION_MINIMUM 3.5)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_BUILD_TYPE Release)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_MESSAGE_LOG_LEVEL TRACE)
set(CMAKE_VERBOSE_MAKEFILE OFF)
set(CMAKE_CXX_COMPILER /usr/bin/clang++ CACHE INTERNAL "cxx compiler")
if(NOT CMAKE_TOOLCHAIN_FILE)
set(CMAKE_CXX_COMPILER /usr/bin/clang++)
endif()
option(BUILD_SHARED_LIBS "Build using shared libraries" OFF)
include(FetchContent)
set(FETCHCONTENT_QUIET FALSE)
FetchContent_Declare(
tpl
GIT_REPOSITORY https://gitlab.com/eidheim/tiny-process-library.git
GIT_TAG 8bbb5a211c5c9df8ee69301da9d22fb977b27dc1
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(tpl)
FetchContent_Declare(
ftxui
GIT_REPOSITORY https://github.com/ArthurSonzogni/FTXUI.git
GIT_TAG v6.0.0
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
PATCH_COMMAND git apply --check ${CMAKE_SOURCE_DIR}/thirdparty/ftxui-empty-container.patch 2>/dev/null && git apply ${CMAKE_SOURCE_DIR}/thirdparty/ftxui-empty-container.patch || true COMMAND git apply --check ${CMAKE_SOURCE_DIR}/thirdparty/ftxui-window.patch 2>/dev/null && git apply ${CMAKE_SOURCE_DIR}/thirdparty/ftxui-window.patch || true
)
FetchContent_MakeAvailable(ftxui)
FetchContent_Declare(
sqlite_modern
GIT_REPOSITORY https://github.com/SqliteModernCpp/sqlite_modern_cpp
GIT_TAG 6e3009973025e0016d5573529067714201338c80
GIT_PROGRESS TRUE
)
FetchContent_GetProperties(sqlite_modern)
if(NOT sqlite_modern_POPULATED)
FetchContent_Populate(sqlite_modern)
endif()
option(STATIC "Set to ON to build xlnt as a static library instead of a shared library" OFF)
FetchContent_Declare(
xlnt
GIT_REPOSITORY https://github.com/xlnt-community/xlnt.git
GIT_TAG e165887739147027e7fbab918280b88f9efa5ffb
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(xlnt)
# Suppress warnings in xlnt
if(TARGET xlnt)
target_compile_options(xlnt PRIVATE
-Wno-unsafe-buffer-usage-in-libc-call
-Wno-unsafe-buffer-usage
-Wno-undefined-reinterpret-cast
-Wno-extra-semi-stmt
-Wno-sign-conversion
-Wno-old-style-cast
-Wno-switch-default
-Wno-nrvo
-Wno-reserved-identifier
-Wno-unused-but-set-variable
-Wno-missing-prototypes
-Wno-character-conversion
-Wno-implicit-int-float-conversion
-Wno-float-equal
-Wno-global-constructors
-Wno-unique-object-duplication
)
endif()
set(FMT_TEST OFF CACHE BOOL "" FORCE)
set(FMT_DOC OFF CACHE BOOL "" FORCE)
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt
GIT_TAG 11.1.4
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(fmt)
set(JSON_BuildTests OFF CACHE BOOL "" FORCE)
set(JSON_Install OFF CACHE BOOL "" FORCE)
FetchContent_Declare(
json
GIT_REPOSITORY https://github.com/nlohmann/json
GIT_TAG v3.12.0
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(json)
set(SPDLOG_FMT_EXTERNAL OFF CACHE BOOL "" FORCE)
FetchContent_Declare(
spdlog
GIT_REPOSITORY https://github.com/gabime/spdlog
GIT_TAG v1.15.3
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(spdlog)
FetchContent_Declare(
clipp
GIT_REPOSITORY https://github.com/muellan/clipp.git
GIT_TAG v1.2.3
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
)
FetchContent_GetProperties(clipp)
if(NOT clipp_POPULATED)
FetchContent_Populate(clipp)
endif()
include(cmake/dependencies.cmake)
project(canscope)
file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(${CMAKE_PROJECT_NAME} ${SOURCES})
target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
@ -136,15 +30,5 @@ target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
${clipp_SOURCE_DIR}/include
)
target_link_directories(${CMAKE_PROJECT_NAME}
PRIVATE
${ftxui_BINARY_DIR}
${tpl_BINARY_DIR}
${xlnt_BINARY_DIR}
${fmt_BINARY_DIR}
${json_BINARY_DIR}
#{sqlite_modern_BINARY_DIR}
)
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE ftxui::component ftxui::screen ftxui::dom tiny-process-library xlnt sqlite3_lib z ${Boost_LIBRARIES})
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE ftxui-component ftxui-screen ftxui-dom tiny-process-library xlnt sqlite3 spdlog::spdlog systemd z ${Boost_LIBRARIES})
# target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC sqlite3 ${Boost_LIBRARIES})

124
Makefile Normal file
View File

@ -0,0 +1,124 @@
BUILD_DIR := build/native
BUILD_STATIC := build/native_static
BUILD_ARM64 := build/arm64
BUILD_ARM64_ST := build/arm64_static
JOBS := $(shell nproc)
PREFIX ?= /usr/local
INSTALL_DIR := $(DESTDIR)$(PREFIX)/canscope
DEV_IMAGE := canscope-dev
BUILD_DOCKER := build/docker
CROSS_IMAGE := canscope-cross
SYSROOT_IMAGE := canscope-sysroot
SYSROOT_CONTAINER := canscope-sysroot
DOCKER_SYSROOT := /sysroot
TOOLCHAIN := cmake/toolchain-aarch64.cmake
SSH_DIR ?= $(HOME)/.ssh
CMAKE_COMMON := -DFETCHCONTENT_UPDATES_DISCONNECTED=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
SSH_PREP := set -euo pipefail; \
rm -rf /root/.ssh; \
mkdir -p /root/.ssh; \
cp -r /host_ssh/. /root/.ssh/; \
chown -R $$(id -u):$$(id -g) /root/.ssh; \
chmod 700 /root/.ssh; \
find /root/.ssh -type f -exec chmod 600 {} \;; \
[ -f /root/.ssh/known_hosts ] && chmod 644 /root/.ssh/known_hosts || true; \
chmod 644 /root/.ssh/*.pub || true; \
KEY=$$(find /root/.ssh -maxdepth 1 -type f \
! -name "*.pub" \
! -name "known_hosts" \
! -name "authorized_keys" \
! -name "config" | head -n1); \
if [ -z "$$KEY" ]; then \
echo "No SSH private key found in ~/.ssh"; \
exit 1; \
fi; \
export GIT_SSH_COMMAND="ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new -i $$KEY";
.PHONY: build build_static install install_static docker-run \
build_arm64 build_arm64_static clean list
HOST_OS := $(shell uname -s)
HOST_ARCH := $(shell uname -m)
build: ## Build $(HOST_ARCH) (dynamic linking)
cmake -G Ninja -B $(BUILD_DIR) -S . $(CMAKE_COMMON) \
-DBUILD_SHARED_LIBS=ON \
-DCMAKE_INSTALL_RPATH='$$ORIGIN/../canscope/lib'
cmake --build $(BUILD_DIR)
build_static: ## Build $(HOST_ARCH) (static linking)
cmake -G Ninja -B $(BUILD_STATIC) -S . $(CMAKE_COMMON) \
-DCMAKE_EXE_LINKER_FLAGS="-static" \
-DBUILD_SHARED_LIBS=OFF \
-DSTATIC=ON
cmake --build $(BUILD_STATIC)
install: ## Install binary to PREFIX/bin, shared libs to PREFIX/canscope/lib
@test -f $(BUILD_DIR)/canscope || { echo "Error: run 'make build' first"; exit 1; }
install -d $(DESTDIR)$(PREFIX)/bin $(INSTALL_DIR)/lib
install -m 755 $(BUILD_DIR)/canscope $(DESTDIR)$(PREFIX)/bin/
patchelf --set-rpath '$$ORIGIN/../canscope/lib' $(DESTDIR)$(PREFIX)/bin/canscope
find $(BUILD_DIR)/_deps -name '*.so*' -type f -exec install -m 755 {} $(INSTALL_DIR)/lib/ \;
find $(BUILD_DIR)/_deps -name '*.so*' -type l -exec cp -a {} $(INSTALL_DIR)/lib/ \;
install_static: ## Install static binary to PREFIX/bin
@test -f $(BUILD_STATIC)/canscope || { echo "Error: run 'make build_static' first"; exit 1; }
install -d $(DESTDIR)$(PREFIX)/bin
install -m 755 $(BUILD_STATIC)/canscope $(DESTDIR)$(PREFIX)/bin/
docker-run: ## Build and run in Docker (works on Linux/Mac/Windows)
docker build -t $(DEV_IMAGE) -f docker/Dockerfile.dev .
docker run --rm -it \
-v $(CURDIR):/app \
-v $(SSH_DIR):/host_ssh:ro \
-v /etc/hosts:/etc/hosts:ro \
$(DEV_IMAGE) \
bash -c '$(SSH_PREP) cmake -G Ninja -B $(BUILD_DOCKER) -S . $(CMAKE_COMMON) \
&& cmake --build $(BUILD_DOCKER) \
&& ./$(BUILD_DOCKER)/canscope $(ARGS)'
build_arm64: ## Cross-compile for arm64 (dynamic linking, in Docker)
docker build -t $(CROSS_IMAGE) -f docker/Dockerfile.cross .
docker build --platform=linux/arm64 -t $(SYSROOT_IMAGE) -f docker/Dockerfile.sysroot .
-docker rm -f $(SYSROOT_CONTAINER) 2>/dev/null
docker create --name $(SYSROOT_CONTAINER) $(SYSROOT_IMAGE)
docker run --rm \
-v $(CURDIR):/app \
-v $(SSH_DIR):/host_ssh:ro \
--volumes-from $(SYSROOT_CONTAINER) \
$(CROSS_IMAGE) \
bash -c '$(SSH_PREP) cmake -G Ninja -B $(BUILD_ARM64) -S . $(CMAKE_COMMON) \
-DCMAKE_TOOLCHAIN_FILE=/app/$(TOOLCHAIN) \
-DSYSROOT=$(DOCKER_SYSROOT) \
&& cmake --build $(BUILD_ARM64)'
build_arm64_static: ## Cross-compile for arm64 (static linking, in Docker)
docker build -t $(CROSS_IMAGE) -f docker/Dockerfile.cross .
docker build --platform=linux/arm64 -t $(SYSROOT_IMAGE) -f docker/Dockerfile.sysroot .
-docker rm -f $(SYSROOT_CONTAINER) 2>/dev/null
docker create --name $(SYSROOT_CONTAINER) $(SYSROOT_IMAGE)
docker run --rm \
-v $(CURDIR):/app \
-v $(SSH_DIR):/host_ssh:ro \
--volumes-from $(SYSROOT_CONTAINER) \
$(CROSS_IMAGE) \
bash -c '$(SSH_PREP) cmake -G Ninja -B $(BUILD_ARM64_ST) -S . $(CMAKE_COMMON) \
-DCMAKE_TOOLCHAIN_FILE=/app/$(TOOLCHAIN) \
-DSYSROOT=$(DOCKER_SYSROOT) \
-DCMAKE_EXE_LINKER_FLAGS=-static \
-DBUILD_SHARED_LIBS=OFF \
-DSTATIC=ON \
&& cmake --build $(BUILD_ARM64_ST)'
clean: ## Remove all build directories
rm -rf build
## Gets comments line and parses the name of target
list: ## Show available targets
@grep -E '^[a-zA-Z0-9_-]+:.*##' $(MAKEFILE_LIST) | \
sed 's/:.* ## /\t/' | sed 's/:.*##/\t/' | \
sed 's/$$(HOST_ARCH)/$(HOST_ARCH)/g' | \
awk -F '\t' '{printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}'

134
README.md
View File

@ -1,83 +1,105 @@
# {canscope}
CAN bus sniffer and SAE J1939 protocol analyzer. Reads CAN frames from an external process (e.g. `candump`), decodes them using a J1939 Digital Annex (xlsx), and presents results in an interactive terminal UI or as JSON output.
CAN bus sniffer and SAE J1939 protocol analyzer. Reads CAN frames in `candump` format, decodes them using a J1939 Digital Annex (xlsx), and presents results in an interactive terminal UI or as JSON output.
[![asciicast](https://asciinema.org/a/uliPOLa1MtVvnjvL.svg)](https://asciinema.org/a/uliPOLa1MtVvnjvL)
![demo](canscope-demo.gif)
## Features
- **TUI mode** -- full-screen interactive terminal interface (FTXUI). Multiple display modes per CAN ID: deployed, brief, verbose, manual, little-endian
- **Headless mode** -- JSON output to stdout or file, for scripting and automation
- **Recording** -- decoded J1939 SPN values saved to SQLite database with gzip compression and batch flushing
- **J1939 decoding** -- PGN/SPN lookup, bit-level value extraction from payload
- **CAN playback** -- replay recorded CAN frames
- **Custom SPN configuration** -- per-parameter settings, parameter export
- **Real-time** -- 30 fps UI refresh
- **TUI mode** - full-screen interactive terminal interface (FTXUI). Multiple display modes per CAN ID: deployed, brief, verbose, manual, little-endian
- **Headless mode** - JSON output to stdout or file, for scripting and automation
- **Recording** - decoded J1939 SPN values saved to SQLite database with gzip compression and batch flushing
- **J1939 decoding** - PGN/SPN lookup, bit-level value extraction from payload
- **CAN playback** - replay recorded CAN frames
- **Custom SPN configuration** - per-parameter settings, parameter export
- **Real-time** - 30 fps UI refresh
## Build
**Requirements:**
- clang++ with C++23 support
- CMake >= 3.13
- System libraries: boost (signals2, spirit, phoenix), sqlite3, systemd, zlib
- Ninja
- System libraries: boost (signals2, spirit, phoenix), sqlite3, zlib
The rest of the dependencies are fetched automatically via CMake FetchContent (FTXUI, tiny-process-library, sqlite_modern_cpp, xlnt, fmt, nlohmann/json, spdlog).
Dependencies fetched automatically via CMake FetchContent:
- [FTXUI](https://github.com/ArthurSonzogni/FTXUI) - terminal UI framework
- [tiny-process-library](https://gitlab.com/eidheim/tiny-process-library) - subprocess management
- [sqlite_modern_cpp](https://github.com/SqliteModernCpp/sqlite_modern_cpp) - modern C++ SQLite wrapper
- [xlnt](https://github.com/xlnt-community/xlnt) - xlsx reading
- [fmt](https://github.com/fmtlib/fmt) - text formatting
- [nlohmann/json](https://github.com/nlohmann/json) - JSON library
- [clipp](https://github.com/muellan/clipp) - CLI argument parsing
### Available targets
```bash
cmake -B build -S . && cmake --build build -j$(nproc)
make list # Show all targets
make build # Native build (dynamic linking)
make build_static # Native build (static linking)
make install # Install to PREFIX (default /usr/local), requires patchelf
make install_static # Install static binary to PREFIX
make docker-run ARGS='...' # Build and run in Docker (cross-platform)
make build_arm64 # Cross-compile for arm64 (dynamic)
make build_arm64_static # Cross-compile for arm64 (static)
make clean # Remove all build artifacts
```
Binary: `build/canscope`
### Docker
### Native build
```bash
docker build -t canscope .
make build
./build/native/canscope -e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx
```
### Docker (cross-platform)
Works on Linux, macOS (?), and Windows (?). Requires only Docker and Make.
```bash
# TUI mode -- requires terminal and CAN interface access
docker run -it --network=host canscope -e "candump can0" -j1939 /app/thirdparty/j1939da_2018.xlsx
# TUI mode - local CAN interface
make docker-run ARGS='-e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx'
# Headless mode
docker run --network=host canscope -hl -e "candump can0" -j1939 /app/thirdparty/j1939da_2018.xlsx
# TUI mode - remote CAN interface via SSH (no data if will ask password - use public key access or sshpass utility)
make docker-run ARGS='-e "ssh user@remote candump can0" -j1939 thirdparty/j1939da_2018.xlsx'
# Read from stdin
candump can0 | docker run -i canscope -j1939 /app/thirdparty/j1939da_2018.xlsx -hl
# Headless mode - create report about collected PGNs and SPNs
make docker-run ARGS='-hl -e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx -of output.json'
```
### Cross-compile for arm64
```bash
make build_arm64 # dynamic linking
make build_arm64_static # static linking
```
Requires Docker. SSH keys from `~/.ssh` and `/etc/hosts` are forwarded into the build container for fetching private git dependencies.
## Usage
```bash
# TUI mode (default)
./build/canscope -e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx
canscope -e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx
# Headless -- JSON to stdout
./build/canscope -hl -e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx
# Headless - JSON to stdout
canscope -hl -e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx
# Headless -- JSON to file
./build/canscope -hl -e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx -of output.json
# Headless - JSON to file
canscope -hl -e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx -of output.json
# Read from stdin (pipe)
candump can0 | ./build/canscope -j1939 thirdparty/j1939da_2018.xlsx
# Headless with stdin
candump can0 | ./build/canscope -hl -j1939 thirdparty/j1939da_2018.xlsx
# Docker -- TUI with CAN interface access
docker run -it --network=host canscope -e "candump can0" -j1939 /app/thirdparty/j1939da_2018.xlsx
# Docker -- headless
docker run --network=host canscope -hl -e "candump can0" -j1939 /app/thirdparty/j1939da_2018.xlsx
# Docker -- read from stdin
candump can0 | docker run -i canscope -hl -j1939 /app/thirdparty/j1939da_2018.xlsx
candump can0 | canscope -j1939 thirdparty/j1939da_2018.xlsx
# Record to SQLite database
./build/canscope -rec -db recording.db -e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx
canscope -rec -db recording.db -e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx
# Record + TUI
./build/canscope -rec -db recording.db -tui -e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx
canscope -rec -db recording.db -tui -e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx
```
> **Note:** J1939 decoding has only been tested with the Digital Annex 2018 edition. Other editions may work but are not guaranteed.
@ -95,31 +117,7 @@ candump can0 | docker run -i canscope -hl -j1939 /app/thirdparty/j1939da_2018.xl
| `-tui` | | Show TUI alongside recording |
| `-h` | `--help` | Show help |
## Architecture
```
candump / other CAN source
| stdout
v
aggregator_task ──> shared JSON (mutex-protected)
|
diff_task (33ms)
|
"new_entry" signal
/ \
TUI headless/recorder
```
### Patterns
- Components communicate through a type-safe signal map (`signals_map_t`)
- `nlohmann::json` as universal data interchange between all layers
- Factory functions via `extern` declarations instead of header includes
- `std::jthread` / `std::stop_token` for async task management
- SIGINT gracefully stops all background tasks
## Roadmap
- **Cross-platform support** -- Windows and macOS in addition to Linux
- ~~**Docker deployment** -- pre-built image for quick setup without manual compilation~~
- **CANopen protocol support** -- CANopen decoding alongside J1939
- **CANopen protocol support** - CANopen decoding alongside J1939
- **Other small features and enhancements** - UI improvements, performance optimizations, additional export formats

BIN
canscope-demo.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

114
cmake/dependencies.cmake Normal file
View File

@ -0,0 +1,114 @@
include(FetchContent)
set(FETCHCONTENT_QUIET FALSE)
FetchContent_Declare(
tpl
GIT_REPOSITORY git@gitlab.com:eidheim/tiny-process-library.git
GIT_TAG 8bbb5a211c5c9df8ee69301da9d22fb977b27dc1
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(tpl)
FetchContent_Declare(
ftxui
GIT_REPOSITORY git@github.com:ArthurSonzogni/FTXUI.git
GIT_TAG v6.0.0
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
PATCH_COMMAND git apply --check ${CMAKE_SOURCE_DIR}/cmake/patches/ftxui-empty-container.patch 2>/dev/null && git apply ${CMAKE_SOURCE_DIR}/cmake/patches/ftxui-empty-container.patch || true COMMAND git apply --check ${CMAKE_SOURCE_DIR}/cmake/patches/ftxui-window.patch 2>/dev/null && git apply ${CMAKE_SOURCE_DIR}/cmake/patches/ftxui-window.patch || true
)
FetchContent_MakeAvailable(ftxui)
FetchContent_Declare(
sqlite_modern
GIT_REPOSITORY git@github.com:SqliteModernCpp/sqlite_modern_cpp
GIT_TAG 6e3009973025e0016d5573529067714201338c80
GIT_PROGRESS TRUE
)
FetchContent_GetProperties(sqlite_modern)
if(NOT sqlite_modern_POPULATED)
FetchContent_Populate(sqlite_modern)
endif()
FetchContent_Declare(
sqlite3
URL https://www.sqlite.org/2024/sqlite-amalgamation-3470200.zip
DOWNLOAD_EXTRACT_TIMESTAMP TRUE
)
FetchContent_MakeAvailable(sqlite3)
add_library(sqlite3_lib STATIC ${sqlite3_SOURCE_DIR}/sqlite3.c)
target_include_directories(sqlite3_lib PUBLIC ${sqlite3_SOURCE_DIR})
target_compile_definitions(sqlite3_lib PRIVATE SQLITE_THREADSAFE=1 SQLITE_OMIT_LOAD_EXTENSION)
option(STATIC "Set to ON to build xlnt as a static library instead of a shared library" OFF)
FetchContent_Declare(
xlnt
GIT_REPOSITORY git@github.com:xlnt-community/xlnt.git
GIT_TAG e165887739147027e7fbab918280b88f9efa5ffb
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(xlnt)
# Suppress warnings in xlnt
if(TARGET xlnt)
target_compile_options(xlnt PRIVATE
-Wno-unsafe-buffer-usage-in-libc-call
-Wno-unsafe-buffer-usage
-Wno-undefined-reinterpret-cast
-Wno-extra-semi-stmt
-Wno-sign-conversion
-Wno-old-style-cast
-Wno-switch-default
-Wno-nrvo
-Wno-reserved-identifier
-Wno-unused-but-set-variable
-Wno-missing-prototypes
-Wno-character-conversion
-Wno-implicit-int-float-conversion
-Wno-float-equal
-Wno-global-constructors
-Wno-unique-object-duplication
)
endif()
set(FMT_TEST OFF CACHE BOOL "" FORCE)
set(FMT_DOC OFF CACHE BOOL "" FORCE)
FetchContent_Declare(
fmt
GIT_REPOSITORY git@github.com:fmtlib/fmt
GIT_TAG 11.1.4
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(fmt)
set(JSON_BuildTests OFF CACHE BOOL "" FORCE)
set(JSON_Install OFF CACHE BOOL "" FORCE)
FetchContent_Declare(
json
GIT_REPOSITORY git@github.com:nlohmann/json
GIT_TAG v3.12.0
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(json)
FetchContent_Declare(
clipp
GIT_REPOSITORY git@github.com:muellan/clipp.git
GIT_TAG v1.2.3
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
)
FetchContent_GetProperties(clipp)
if(NOT clipp_POPULATED)
FetchContent_Populate(clipp)
endif()

View File

@ -0,0 +1,27 @@
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(SYSROOT "/sysroot" CACHE PATH "Path to aarch64 sysroot")
set(CMAKE_C_COMPILER clang)
set(CMAKE_CXX_COMPILER clang++)
set(CMAKE_C_COMPILER_TARGET aarch64-linux-gnu)
set(CMAKE_CXX_COMPILER_TARGET aarch64-linux-gnu)
# Find GCC installation inside sysroot for crt files and libgcc
file(GLOB _gcc_dirs "${SYSROOT}/usr/lib/gcc/aarch64-*/*")
list(GET _gcc_dirs 0 _gcc_dir)
get_filename_component(GCC_INSTALL_DIR "${_gcc_dir}" DIRECTORY)
set(CMAKE_C_FLAGS_INIT "--gcc-install-dir=${_gcc_dir}")
set(CMAKE_CXX_FLAGS_INIT "--gcc-install-dir=${_gcc_dir}")
set(CMAKE_LINKER_TYPE LLD)
set(CMAKE_SYSROOT ${SYSROOT})
set(CMAKE_FIND_ROOT_PATH ${SYSROOT})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

View File

@ -7,8 +7,6 @@ RUN pacman -Syu --noconfirm && \
make \
git \
boost \
sqlite \
systemd-libs \
zlib && \
pacman -Scc --noconfirm

13
docker/Dockerfile.cross Normal file
View File

@ -0,0 +1,13 @@
FROM manjarolinux/base:latest
RUN pacman -Syu --noconfirm && \
pacman -S --noconfirm \
clang \
lld \
cmake \
ninja \
git \
openssh \
&& pacman -Scc --noconfirm
WORKDIR /app

17
docker/Dockerfile.dev Normal file
View File

@ -0,0 +1,17 @@
FROM manjarolinux/base:latest
RUN pacman -Syu --noconfirm && \
pacman -S --noconfirm \
clang \
lld \
cmake \
ninja \
git \
openssh \
sshpass \
boost \
sqlite3 \
zlib && \
pacman -Scc --noconfirm
WORKDIR /app

15
docker/Dockerfile.sysroot Normal file
View File

@ -0,0 +1,15 @@
FROM --platform=linux/arm64 manjarolinux/base:latest
RUN pacman -Syu --noconfirm && \
pacman -S --noconfirm \
boost \
zlib \
gcc && \
pacman -Scc --noconfirm
RUN mkdir -p /sysroot && \
cp -a /usr /sysroot/usr && \
cp -a /lib /sysroot/lib && \
([ -d /lib64 ] && cp -a /lib64 /sysroot/lib64 || true)
VOLUME /sysroot

View File

@ -11,8 +11,6 @@
#include "process.hpp"
#include "tagsettings.hpp"
#include "json/json.hpp"
#include <spdlog/sinks/systemd_sink.h>
#include <spdlog/spdlog.h>
// For sqlite
// #include "sqlite_modern_cpp.h"
@ -239,16 +237,8 @@ CanIDUnit::CanIDUnit(const std::string &iface, const std::string &canid, const s
}
bool CanIDUnit::OnEvent(ftxui::Event event) {
static auto log = spdlog::systemd_logger_mt("canidunit", "cansniffer-hover");
log->set_level(spdlog::level::debug);
if (event.is_mouse()) {
bool prev = m_hovered_;
m_hovered_ = m_box_.Contain(event.mouse().x, event.mouse().y);
if (m_hovered_ != prev) {
log->debug("{}: hover={} mouse=({},{}) box=({},{},{},{})", m_canid_, m_hovered_, event.mouse().x, event.mouse().y,
m_box_.x_min, m_box_.x_max, m_box_.y_min, m_box_.y_max);
}
}
return ftxui::ComponentBase::OnEvent(event);

View File

@ -10,8 +10,6 @@
#include <ftxui/screen/terminal.hpp>
#include <map>
#include <ranges>
#include <spdlog/sinks/systemd_sink.h>
#include <spdlog/spdlog.h>
#include <unordered_map>
#include <vector>
@ -24,8 +22,6 @@ ftxui::Component makeMainForm(ftxui::ScreenInteractive *screen, signals_map_t &s
class Impl : public ftxui::ComponentBase {
public:
explicit Impl(ftxui::ScreenInteractive *screen, signals_map_t &smap) {
static auto logger = spdlog::systemd_logger_mt("mainform", "cansniffer");
static bool canbus_params_export_dialog_shown = false, file_dialog_shown = false,
canbus_player_dialog_shown = false, canplayer_is_ready = false;
;

View File

@ -7,15 +7,9 @@
#define FMT_HEADER_ONLY
#include <fmt/format.h>
#include <spdlog/sinks/systemd_sink.h>
Recorder::Recorder(const std::string &db_path, bool console_output)
: disk_db_path_(db_path), console_output_(console_output) {
log_ = spdlog::systemd_logger_mt("recorder", "cansniffer-rec");
log_->set_level(spdlog::level::info);
flush_task_ = std::async(std::launch::async, [this](std::stop_token st) { background_flush_task(st); }, flush_stop_.get_token());
log_->info("recorder initialized, db_path={}", db_path);
if (console_output_) fmt::println("Recording to: {}", db_path);
}
@ -40,7 +34,6 @@ void Recorder::flushAndClose() {
std::lock_guard<std::mutex> lock(batch_mtx_);
if (!pending_.empty()) {
log_->info("flushing remaining {} frames on exit", pending_.size());
compress_batch();
}
}
@ -130,15 +123,14 @@ void Recorder::compress_batch() {
disk_db << "INSERT INTO batches (ts_start, ts_end, frame_count, data) VALUES (?, ?, ?, ?);"
<< ts_start << ts_end << static_cast<int64_t>(pending_.size()) << compressed;
auto msg = fmt::format("Flushed batch: {} frames, {:.1f}KB -> {:.1f}KB gzip ({:.0f}% compression)",
pending_.size(),
static_cast<double>(json_str.size()) / 1024.0,
static_cast<double>(compressed.size()) / 1024.0,
(1.0 - static_cast<double>(compressed.size()) / static_cast<double>(json_str.size())) * 100.0);
log_->info("{}", msg);
if (console_output_) fmt::println("{}", msg);
} catch (const sqlite::sqlite_exception &e) {
log_->error("disk DB write failed: {}", e.what());
if (console_output_) {
fmt::println("Flushed batch: {} frames, {:.1f}KB -> {:.1f}KB gzip ({:.0f}% compression)",
pending_.size(),
static_cast<double>(json_str.size()) / 1024.0,
static_cast<double>(compressed.size()) / 1024.0,
(1.0 - static_cast<double>(compressed.size()) / static_cast<double>(json_str.size())) * 100.0);
}
} catch (const sqlite::sqlite_exception &) {
}
pending_.clear();
@ -157,7 +149,6 @@ void Recorder::background_flush_task(std::stop_token st) {
bool mem_trigger = get_rss_mb() >= 500;
if (time_trigger || mem_trigger) {
if (mem_trigger) log_->warn("RSS >= 500MB, forcing flush");
compress_batch();
}
}

View File

@ -11,7 +11,6 @@
#include <vector>
#include <nlohmann/json.hpp>
#include <spdlog/spdlog.h>
struct FrameRecord {
int64_t ts_ms;
@ -43,5 +42,4 @@ private:
std::stop_source flush_stop_;
std::future<void> flush_task_;
bool console_output_ = false;
std::shared_ptr<spdlog::logger> log_;
};

Binary file not shown.