Compare commits

..

No commits in common. "a9b4e4e7dbefd5fe2de6cbf5573bfbfb2ca6aab5" and "6c3557c71de5f90bc9dc45cf9b2cc8d3c8b72b6f" have entirely different histories.

17 changed files with 226 additions and 391 deletions

View File

@ -2,21 +2,127 @@ cmake_minimum_required(VERSION 3.13)
set(CMAKE_POLICY_VERSION_MINIMUM 3.5)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_BUILD_TYPE Release)
set(CMAKE_BUILD_TYPE Debug)
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")
option(BUILD_SHARED_LIBS "Build using shared libraries" OFF)
if(NOT CMAKE_TOOLCHAIN_FILE)
set(CMAKE_CXX_COMPILER /usr/bin/clang++)
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(BUILD_SHARED_LIBS "Build using shared libraries" OFF)
include(cmake/dependencies.cmake)
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()
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}
@ -30,5 +136,15 @@ target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE
${clipp_SOURCE_DIR}/include
)
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE ftxui::component ftxui::screen ftxui::dom tiny-process-library xlnt sqlite3_lib z ${Boost_LIBRARIES})
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 spdlog::spdlog systemd z ${Boost_LIBRARIES})
# target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC sqlite3 ${Boost_LIBRARIES})

View File

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

124
Makefile
View File

@ -1,124 +0,0 @@
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,105 +1,83 @@
# {canscope}
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.
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.
![demo](canscope-demo.gif)
[![asciicast](https://asciinema.org/a/uliPOLa1MtVvnjvL.svg)](https://asciinema.org/a/uliPOLa1MtVvnjvL)
## 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
- Ninja
- System libraries: boost (signals2, spirit, phoenix), sqlite3, zlib
- System libraries: boost (signals2, spirit, phoenix), sqlite3, systemd, zlib
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
The rest of the dependencies are fetched automatically via CMake FetchContent (FTXUI, tiny-process-library, sqlite_modern_cpp, xlnt, fmt, nlohmann/json, spdlog).
```bash
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
cmake -B build -S . && cmake --build build -j$(nproc)
```
### Native build
Binary: `build/canscope`
### Docker
```bash
make build
./build/native/canscope -e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx
docker build -t canscope .
```
### Docker (cross-platform)
Works on Linux, macOS (?), and Windows (?). Requires only Docker and Make.
```bash
# TUI mode - local CAN interface
make docker-run ARGS='-e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx'
# 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 - 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'
# Headless mode
docker run --network=host canscope -hl -e "candump can0" -j1939 /app/thirdparty/j1939da_2018.xlsx
# 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'
# Read from stdin
candump can0 | docker run -i canscope -j1939 /app/thirdparty/j1939da_2018.xlsx -hl
```
### 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)
canscope -e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx
./build/canscope -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 stdout
./build/canscope -hl -e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx
# Headless - JSON to file
canscope -hl -e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx -of output.json
# Headless -- JSON to file
./build/canscope -hl -e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx -of output.json
# Read from stdin (pipe)
candump can0 | canscope -j1939 thirdparty/j1939da_2018.xlsx
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
# Record to SQLite database
canscope -rec -db recording.db -e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx
./build/canscope -rec -db recording.db -e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx
# Record + TUI
canscope -rec -db recording.db -tui -e "candump can0" -j1939 thirdparty/j1939da_2018.xlsx
./build/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.
@ -117,7 +95,31 @@ canscope -rec -db recording.db -tui -e "candump can0" -j1939 thirdparty/j1939da_
| `-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
- **CANopen protocol support** - CANopen decoding alongside J1939
- **Other small features and enhancements** - UI improvements, performance optimizations, additional export formats
- **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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 MiB

View File

@ -1,114 +0,0 @@
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

@ -1,27 +0,0 @@
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

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

View File

@ -1,17 +0,0 @@
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

View File

@ -1,15 +0,0 @@
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,6 +11,8 @@
#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"
@ -237,8 +239,16 @@ 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,6 +10,8 @@
#include <ftxui/screen/terminal.hpp>
#include <map>
#include <ranges>
#include <spdlog/sinks/systemd_sink.h>
#include <spdlog/spdlog.h>
#include <unordered_map>
#include <vector>
@ -22,6 +24,8 @@ 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,9 +7,15 @@
#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);
}
@ -34,6 +40,7 @@ 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();
}
}
@ -123,14 +130,15 @@ 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;
if (console_output_) {
fmt::println("Flushed batch: {} frames, {:.1f}KB -> {:.1f}KB gzip ({:.0f}% compression)",
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);
}
} catch (const sqlite::sqlite_exception &) {
log_->info("{}", msg);
if (console_output_) fmt::println("{}", msg);
} catch (const sqlite::sqlite_exception &e) {
log_->error("disk DB write failed: {}", e.what());
}
pending_.clear();
@ -149,6 +157,7 @@ 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,6 +11,7 @@
#include <vector>
#include <nlohmann/json.hpp>
#include <spdlog/spdlog.h>
struct FrameRecord {
int64_t ts_ms;
@ -42,4 +43,5 @@ private:
std::stop_source flush_stop_;
std::future<void> flush_task_;
bool console_output_ = false;
std::shared_ptr<spdlog::logger> log_;
};

BIN
thirdparty/j1939da_2018_hitachi.xlsx vendored Normal file

Binary file not shown.