Skip to content

TierOne-Software/cansocket

Repository files navigation

TierOne CANSocket

A modern C++17 header-only library for CAN (Controller Area Network) and CAN FD communication on Linux systems. Provides command-line utilities cansend and candump.

Features

  • Header-only: No separate compilation required
  • Type-safe CAN IDs: Separate types for Standard (11-bit) and Extended (29-bit) IDs with compile-time validation
  • Dual Protocol Support: Works with both classic CAN and CAN FD frames
  • Modern C++: Uses C++17 features for better performance and safety
  • Thread Safety: Multiple threading policies for concurrent access patterns
  • Zero-Contention Design: Per-thread socket instances for maximum performance
  • Lock-Free Operations: High-frequency producer-consumer scenarios
  • Builder Pattern: Fluent API for convenient frame construction
  • Convenience Methods: Simple sendFrame() and sendFDFrame() methods for quick messaging
  • High-Performance Batch Operations: Efficient writeBatch() and readBatch() for bulk operations
  • Memory Pool: Pre-allocated frame pools for zero-allocation performance
  • Hardware Timestamps: Support for hardware timestamping when available
  • Flexible Filtering: Easy-to-use filtering API for both single and multiple filters
  • Non-blocking I/O: Support for both blocking and non-blocking socket operations
  • Comprehensive Testing: Full test suite with virtual CAN interface support
  • Enhanced Tools: Modern replacements for canutils (cansend, candump) with advanced features

Command Line Tools

This library includes enhanced versions of popular CAN utilities with advanced features:

cansend - Enhanced CAN Frame Sender

  • Backward Compatible: Works with standard canutils syntax
  • Advanced Features: Batch sending, templates, timeout control, threading models
  • Performance: Rate limiting, continuous sending, statistics
  • Example: cansend can0 123#DEADBEEF --repeat 10 --interval 100

candump - Enhanced CAN Frame Monitor

  • Advanced Filtering: Regex patterns, data ranges, rate limiting, change detection
  • Multiple Formats: JSON, CSV, binary, pretty-print output
  • Real-time Analysis: Live statistics, bandwidth monitoring
  • Example: candump can0 --filter 100-200 --only-changes --format json

See tools/README.md for detailed documentation and examples.

Requirements

  • C++17 compatible compiler (GCC 7+, Clang 5+)
  • Linux with SocketCAN support
  • CMake 3.16+ (for building tests)
  • Conan 2.0+ (for dependency management)

Dependencies

  • fmt - String formatting library
  • Catch2 - Testing framework (tests only)

Quick Start

Installation

This is a header-only library. Simply include cansocket.hpp in your project:

#include "cansocket.hpp"

Setting up a Virtual CAN Interface (for testing)

# Load the vcan kernel module
sudo modprobe vcan

# Create a virtual CAN interface
sudo ip link add dev vcan0 type vcan
sudo ip link set up vcan0

Examples

CAN 2.0A (Standard Frame) - 11-bit ID

CAN 2.0A uses standard frames with 11-bit identifiers (0x000 to 0x7FF).

#include "cansocket.hpp"
#include <iostream>

int main() {
    try {
        // Create a CAN socket for classic CAN
        CAN::Socket socket("vcan0", CAN::ProtocolMode::ClassicCAN);
        
        // Create a standard ID (11-bit)
        auto id = CAN::StandardID(0x123);
        
        // Prepare data
        std::array<uint8_t, 8> data = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
        
        // Create and send a CAN 2.0A frame
        CAN::Frame frame(id, 8, data);
        socket.write(frame);
        
        std::cout << "Sent CAN 2.0A frame with ID: 0x" << std::hex << id.justSFF() << std::endl;
        
        // Read frames
        auto result = socket.read();
        if (auto* frame_opt = std::get_if<std::optional<std::unique_ptr<CAN::Frame>>>(&result)) {
            if (frame_opt->has_value()) {
                auto& received_frame = **frame_opt;
                std::cout << "Received frame with ID: 0x" << std::hex << received_frame.getCANID() << std::endl;
            }
        }
        
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}

CAN 2.0B (Extended Frame) - 29-bit ID

CAN 2.0B uses extended frames with 29-bit identifiers (0x00000000 to 0x1FFFFFFF).

#include "cansocket.hpp"
#include <iostream>

int main() {
    try {
        // Create a CAN socket for classic CAN
        CAN::Socket socket("vcan0", CAN::ProtocolMode::ClassicCAN);
        
        // Create an extended ID (29-bit)
        auto id = CAN::ExtendedID(0x1ABCDEF);
        
        // Prepare data
        std::array<uint8_t, 8> data = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22};
        
        // Create and send a CAN 2.0B frame
        CAN::Frame frame(id, 8, data);
        socket.write(frame);
        
        std::cout << "Sent CAN 2.0B frame with ID: 0x" << std::hex << id.justEFF() << std::endl;
        
        // Set a filter for extended frames
        socket.setFilter(id, CAN::EFF_MASK | CAN::EFF_FLAG);
        
        // Read frames with timeout
        auto frames = socket.read(std::chrono::milliseconds(1000));
        if (auto* can_frames = std::get_if<std::vector<CAN::Frame>>(&frames)) {
            for (const auto& frame : *can_frames) {
                std::cout << "Received extended frame with ID: 0x" << std::hex << frame.getCANID() << std::endl;
            }
        }
        
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}

CAN FD (Flexible Data-Rate)

CAN FD supports larger payloads (up to 64 bytes) and higher bit rates.

#include "cansocket.hpp"
#include <iostream>
#include <vector>

int main() {
    try {
        // Create a CAN FD socket
        CAN::Socket socket("vcan0", CAN::ProtocolMode::CanFD);
        
        // Create an extended ID for CAN FD
        auto id = CAN::ExtendedID(0x18DAF110);
        
        // Prepare larger data payload (up to 64 bytes)
        std::vector<uint8_t> data;
        for (int i = 0; i < 32; i++) {
            data.push_back(static_cast<uint8_t>(i));
        }
        
        // Create a CAN FD frame with Bit Rate Switch (BRS) enabled
        CAN::FD::Frame frame(id, data, CAN::FD::FDF_FLAG | CAN::FD::BRS_FLAG);
        
        // Verify BRS flag is set
        std::cout << "BRS enabled: " << (frame.hasBRS() ? "Yes" : "No") << std::endl;
        
        // Send the frame
        socket.write(frame);
        
        std::cout << "Sent CAN FD frame with ID: 0x" << std::hex << id.justEFF() 
                  << ", length: " << static_cast<int>(frame.len) << " bytes" << std::endl;
        
        // Read CAN FD frames
        auto result = socket.read();
        if (auto* frame_opt = std::get_if<std::optional<std::unique_ptr<CAN::FD::Frame>>>(&result)) {
            if (frame_opt->has_value()) {
                auto& received_frame = **frame_opt;
                std::cout << "Received CAN FD frame with ID: 0x" << std::hex << received_frame.getCANID()
                         << ", length: " << static_cast<int>(received_frame.len) << " bytes" << std::endl;
                std::cout << "BRS: " << (received_frame.hasBRS() ? "Yes" : "No") 
                         << ", ESI: " << (received_frame.hasESI() ? "Yes" : "No") << std::endl;
            }
        }
        
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}

Builder Pattern

The library provides a fluent builder API for convenient frame construction:

#include "cansocket.hpp"

int main() {
    try {
        CAN::Socket socket("vcan0");
        
        // Build a classic CAN frame using the builder pattern
        auto frame = CAN::FrameBuilder()
            .setID(CAN::StandardID(0x123))
            .setDLC(8)
            .setData({0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08})
            .build();
        
        socket.write(frame);
        
        // Build a CAN FD frame with automatic length detection
        auto fd_frame = CAN::FD::FrameBuilder()
            .setID(CAN::ExtendedID(0x18DAF110))
            .setData({0xAA, 0xBB, 0xCC, 0xDD})  // Length automatically set to 4
            .enableBRS()  // Enable Bit Rate Switch
            .buildAuto(); // Automatically calculate length
        
        socket.write(fd_frame);
        
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}

Convenience Methods

Simple one-line methods for quick messaging:

#include "cansocket.hpp"

int main() {
    try {
        CAN::Socket socket("vcan0");
        
        // Send a classic CAN frame with one line
        socket.sendFrame(0x123, {0x01, 0x02, 0x03, 0x04});
        
        // Send to an extended ID
        socket.sendFrame(CAN::ExtendedID(0x18DAF110), {0xAA, 0xBB});
        
        // Send a CAN FD frame (requires CAN FD socket)
        CAN::Socket fd_socket("vcan0", CAN::ProtocolMode::CanFD);
        fd_socket.sendFDFrame(0x456, {0x01, 0x02, 0x03, 0x04, 0x05, 0x06});
        
        // Send with BRS enabled
        fd_socket.sendFDFrame(CAN::ExtendedID(0x18DAF110), 
                             {0xAA, 0xBB, 0xCC, 0xDD}, 
                             CAN::FD::FDF_FLAG | CAN::FD::BRS_FLAG);
        
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}

High-Performance Batch Operations

For high-throughput applications, use batch operations to reduce system call overhead:

#include "cansocket.hpp"
#include <vector>

int main() {
    try {
        CAN::Socket socket("vcan0");
        
        // Prepare multiple frames for batch sending
        std::vector<CAN::Frame> frames;
        std::array<uint8_t, 8> data = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
        
        for (int i = 0; i < 100; ++i) {
            frames.emplace_back(0x100 + i, 8, data);
        }
        
        // Send all frames in one batch operation
        ssize_t bytes_sent = socket.writeBatch(frames);
        std::cout << "Sent " << bytes_sent << " bytes in batch" << std::endl;
        
        // Batch read with custom context
        std::vector<CAN::Frame> received_frames;
        CAN::BatchContext ctx;
        ctx.batch_size = 50;  // Read up to 50 frames at once
        ctx.timeout = std::chrono::milliseconds(100);
        
        size_t frames_received = socket.readBatch(received_frames, ctx);
        std::cout << "Received " << frames_received << " frames" << std::endl;
        
        // Process received frames
        for (const auto& frame : received_frames) {
            std::cout << "Frame ID: 0x" << std::hex << frame.getCANID() 
                     << ", DLC: " << static_cast<int>(frame.dlc) << std::endl;
        }
        
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}

Memory Pool for Zero-Allocation Performance

The library includes a memory pool for high-performance scenarios:

#include "cansocket.hpp"

int main() {
    try {
        CAN::Socket socket("vcan0");
        
        // Get the global pool manager
        auto& pool_manager = CAN::Socket::getPoolManager();
        
        // Check pool statistics
        auto [free_frames, free_fd_frames] = pool_manager.getPoolStats();
        std::cout << "Available CAN frames: " << free_frames << std::endl;
        std::cout << "Available CAN FD frames: " << free_fd_frames << std::endl;
        
        // Allocate frame from pool (zero allocation)
        CAN::Frame* frame = pool_manager.allocateFrame();
        if (frame) {
            // Use the frame...
            frame->id = 0x123;
            frame->dlc = 8;
            // ... set data ...
            
            // Return to pool when done
            pool_manager.deallocateFrame(frame);
        }
        
        // The library automatically uses the pool for internal operations
        // when possible, providing zero-allocation performance
        
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }
    
    return 0;
}

Performance Features

CANSocket is designed for high-performance applications with several optimization features:

Zero-Allocation Design

  • Memory Pools: Pre-allocated frame pools eliminate runtime allocations
  • Static Thread-Local Storage: Batch operations use static buffers for zero-copy performance
  • Pool Statistics: Monitor pool utilization for optimal sizing

Batch Operations

  • High Throughput: writeBatch() and readBatch() minimize system call overhead
  • Configurable Batching: Customize batch sizes and timeouts for your use case
  • Automatic Fallback: Graceful degradation to single-frame operations when needed

Efficient APIs

  • Builder Pattern: Construct frames with minimal overhead
  • Convenience Methods: One-line sendFrame() calls for simple cases
  • Type Safety: Compile-time validation prevents runtime errors

Thread Safety

CANSocket provides multiple threading models to handle different concurrent access patterns efficiently. The original Socket class remains unchanged for backward compatibility, while new ThreadSafeSocket<Policy> templates provide thread-safe wrappers.

Threading Policies

NoLocking Policy - Zero Overhead

For single-threaded applications or when external synchronization is handled by the user:

#include "cansocket.hpp"

// Zero overhead - no thread safety mechanisms
CAN::ThreadSafeSocket<CAN::NoLocking> socket("can0");

// Direct delegation to original Socket class
socket.sendFrame(0x123, {0x01, 0x02, 0x03, 0x04});

SharedMutex Policy - Read-Heavy Workloads

For applications with multiple readers and fewer writers (monitoring, logging):

#include "cansocket.hpp"
#include <thread>
#include <vector>

// Multiple readers can access simultaneously
CAN::ThreadSafeSocket<CAN::SharedMutexPolicy> socket("can0");

// Start multiple reader threads
std::vector<std::thread> readers;
for (int i = 0; i < 4; ++i) {
    readers.emplace_back([&socket]() {
        while (true) {
            auto result = socket.read();
            // Process frames...
        }
    });
}

// Single writer thread
std::thread writer([&socket]() {
    for (int i = 0; i < 100; ++i) {
        socket.sendFrame(0x200 + i, {0x11, 0x22, 0x33, 0x44});
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
});

// Join threads...

PerThread Policy - Zero Contention

For high-frequency parallel operations where each thread gets its own socket:

#include "cansocket.hpp"
#include <thread>
#include <vector>

// Each thread gets its own socket to the same interface
CAN::ThreadSafeSocket<CAN::PerThreadPolicy> socket("can0");

// Multiple threads can write concurrently with zero contention
std::vector<std::thread> workers;
for (int thread_id = 0; thread_id < 8; ++thread_id) {
    workers.emplace_back([&socket, thread_id]() {
        for (int i = 0; i < 1000; ++i) {
            uint32_t frame_id = 0x300 + (thread_id * 100) + i;
            socket.sendFrame(frame_id, {
                static_cast<uint8_t>(thread_id), 
                static_cast<uint8_t>(i),
                0xAA, 0xBB
            });
        }
    });
}

// Each thread operates on its own socket - no locking needed!
for (auto& worker : workers) {
    worker.join();
}

std::cout << "Active thread sockets: " << socket.getThreadCount() << std::endl;
socket.closeAll(); // Clean up all thread sockets

LockFree Policy - Producer-Consumer Pattern

For high-frequency scenarios with multiple producers and a dedicated I/O thread:

#include "cansocket.hpp"
#include <thread>
#include <atomic>

// Uses lock-free queues with dedicated I/O thread
CAN::ThreadSafeSocket<CAN::LockFreePolicy> socket("can0");

std::atomic<bool> stop{false};

// Multiple producer threads
std::vector<std::thread> producers;
for (int producer_id = 0; producer_id < 4; ++producer_id) {
    producers.emplace_back([&socket, &stop, producer_id]() {
        while (!stop.load()) {
            CAN::Frame frame(0x400 + producer_id, 4, {
                static_cast<uint8_t>(producer_id),
                0x01, 0x02, 0x03
            });
            
            // Non-blocking push to lock-free queue
            if (!socket.write(frame)) {
                // Queue full - handle backpressure
                std::this_thread::sleep_for(std::chrono::microseconds(1));
            }
        }
    });
}

// Consumer thread
std::thread consumer([&socket, &stop]() {
    while (!stop.load()) {
        // Non-blocking read from lock-free buffer
        auto frame = socket.read();
        if (frame.has_value()) {
            std::cout << "Consumed frame ID: 0x" << std::hex 
                     << frame->getCANID() << std::dec << std::endl;
        }
    }
});

// Run for 1 second
std::this_thread::sleep_for(std::chrono::seconds(1));
stop.store(true);

// Clean up
for (auto& producer : producers) {
    producer.join();
}
consumer.join();

Factory Functions

Create thread-safe sockets using convenient factory functions:

#include "cansocket.hpp"

// Create specific socket types
auto no_lock_socket = CAN::createNoLockingSocket("can0");
auto shared_mutex_socket = CAN::createSharedMutexSocket("can0");
auto per_thread_socket = CAN::createPerThreadSocket("can0");
auto lock_free_socket = CAN::createLockFreeSocket("can0");

// Use them with their specific APIs
no_lock_socket->sendFrame(0x501, {0x01});
shared_mutex_socket->sendFrame(0x502, {0x02});
per_thread_socket->sendFrame(0x503, {0x03});
lock_free_socket->write(CAN::Frame(0x504, 1, {0x04, 0, 0, 0, 0, 0, 0, 0}));

Performance Characteristics

Threading Policy Contention Latency Throughput Memory Usage Use Case
NoLocking None Baseline Baseline Low Single-threaded, external sync
SharedMutex Low-Medium Low Medium-High Low Read-heavy monitoring
PerThread None Very Low Very High Medium High-frequency parallel ops
LockFree None Very Low Very High Medium Producer-consumer patterns

Lock-Free Data Structures

CANSocket includes high-performance lock-free data structures for advanced scenarios:

SPSC Ring Buffer (Single Producer, Single Consumer)

#include "cansocket.hpp"

// Create a lock-free ring buffer
CAN::SPSCRingBuffer<CAN::Frame, 1024> buffer;

// Producer thread
std::thread producer([&buffer]() {
    CAN::Frame frame(0x123, 8, {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08});
    while (buffer.push(frame)) {
        // Frame pushed successfully
    }
    // Buffer full
});

// Consumer thread
std::thread consumer([&buffer]() {
    CAN::Frame frame;
    while (buffer.pop(frame)) {
        // Process frame
    }
    // Buffer empty
});

MPSC Queue (Multiple Producer, Single Consumer)

#include "cansocket.hpp"

// Create a lock-free MPSC queue
CAN::MPSCQueue<CAN::Frame, 2048> queue;

// Multiple producer threads
std::vector<std::thread> producers;
for (int i = 0; i < 4; ++i) {
    producers.emplace_back([&queue, i]() {
        CAN::Frame frame(0x100 + i, 4, {static_cast<uint8_t>(i), 0x01, 0x02, 0x03});
        queue.push(frame);
    });
}

// Single consumer thread
std::thread consumer([&queue]() {
    CAN::Frame frame;
    while (queue.pop(frame)) {
        // Process frame from any producer
    }
});

Choosing the Right Threading Model

  • Single-threaded applications: Use NoLocking for zero overhead
  • Monitoring/logging systems: Use SharedMutex for multiple readers
  • High-frequency trading: Use PerThread for zero contention
  • Data acquisition: Use LockFree for producer-consumer patterns
  • Real-time systems: Use PerThread or LockFree for deterministic performance

Thread Safety Example Program

See examples/thread_safety_demo.cpp for a comprehensive demonstration of all threading policies with performance comparisons.

Advanced Features

Multiple Filters

// Set multiple filters for different ID ranges
std::vector<std::pair<CAN::StandardID, uint32_t>> filters = {
    {CAN::StandardID(0x100), 0x700},  // Accept IDs 0x100-0x1FF
    {CAN::StandardID(0x200), 0x7F0},  // Accept IDs 0x200-0x20F
};
socket.setFilters(filters);

Non-blocking I/O

// Enable non-blocking mode
socket.setNonBlocking();

// Read without blocking
auto result = socket.read();
if (auto* frame_opt = std::get_if<std::optional<std::unique_ptr<CAN::Frame>>>(&result)) {
    if (frame_opt->has_value()) {
        // Frame received
        auto& frame = **frame_opt;
        // Process frame...
    } else {
        // No frame available
        std::cout << "No frame available" << std::endl;
    }
}

Hardware Timestamps

// Read with hardware timestamp
auto result = socket.readMsg();
if (auto* frame_opt = std::get_if<std::optional<std::unique_ptr<CAN::Frame>>>(&result)) {
    if (frame_opt->has_value()) {
        auto& frame = **frame_opt;
        if (frame.hw_timestamp) {
            std::cout << "Hardware timestamp: " << frame.hw_timestamp->tv_sec 
                     << "." << frame.hw_timestamp->tv_nsec << std::endl;
        }
    }
}

Building and Testing

Prerequisites

# Install system dependencies
sudo apt-get install build-essential clang libc++-dev libc++abi-dev ninja-build python3-pip

# Install Conan
pip3 install conan
conan profile detect --force

Build

# Clone the repository
git clone <repository-url>
cd cansocket

# Run the build script (includes tests)
./build.sh

The build script will:

  1. Install dependencies using Conan
  2. Configure and build using CMake/Ninja
  3. Run tests with virtual CAN interface support

Manual Build

# Create Conan profile
cat > .conan-profile-debug << EOF
[settings]
os=Linux
arch=x86_64
compiler=clang
compiler.version=14
compiler.libcxx=libc++
compiler.cppstd=17
build_type=Debug
EOF

# Install dependencies
conan install . --build=missing --profile=.conan-profile-debug --output-folder=build

# Configure and build
cmake -B build -S . -G Ninja -DCMAKE_BUILD_TYPE=Debug
cmake --build build

Running Tests

Important: Tests require virtual CAN interfaces to be set up. Use the provided script:

# Set up virtual CAN interfaces for testing
sudo ./setup_test_env.sh

This script will:

  1. Load the vcan kernel module
  2. Create virtual CAN interfaces (vcan0, vcan1)
  3. Configure them for testing

After setting up the test environment:

# Run all tests
ctest --test-dir build -V

# Run specific tests
./build/tests/cansocket_tests "[can]"      # CAN-specific tests
./build/tests/cansocket_tests "[canfd]"    # CAN FD-specific tests

API Reference

Core Classes

  • CAN::Socket - Main socket class supporting both CAN and CAN FD
  • CAN::ThreadSafeSocket<Policy> - Thread-safe socket wrapper with configurable policies
  • CAN::StandardID - 11-bit CAN identifier (CAN 2.0A)
  • CAN::ExtendedID - 29-bit CAN identifier (CAN 2.0B)
  • CAN::Frame - Classic CAN frame (up to 8 bytes)
  • CAN::FD::Frame - CAN FD frame (up to 64 bytes)

Threading Policy Classes

  • CAN::NoLocking - Zero overhead, no thread safety
  • CAN::SharedMutexPolicy - Multiple readers, single writer using shared_mutex
  • CAN::PerThreadPolicy - Each thread gets its own socket instance
  • CAN::LockFreePolicy - Lock-free queues with dedicated I/O thread

Lock-Free Data Structures

  • CAN::SPSCRingBuffer<T, Size> - Single producer, single consumer ring buffer
  • CAN::MPSCQueue<T, Size> - Multiple producer, single consumer queue

Builder Classes

  • CAN::FrameBuilder - Fluent API for building classic CAN frames
  • CAN::FD::FrameBuilder - Fluent API for building CAN FD frames

Memory Management

  • CAN::FramePool<T> - Template class for memory pools
  • CAN::PoolManager - Manages frame allocation pools
  • CAN::BatchContext - Configuration for batch operations

Key Methods

Socket Operations

  • write(frame) - Send a single frame
  • writeBatch(frames) - Send multiple frames efficiently
  • read() - Read a single frame
  • readBatch(frames, context) - Read multiple frames efficiently
  • sendFrame(id, data) - Convenience method for quick sending
  • sendFDFrame(id, data, flags) - Convenience method for CAN FD

Thread Safety Operations

  • createNoLockingSocket(args...) - Create zero-overhead thread-safe socket
  • createSharedMutexSocket(args...) - Create shared-mutex based socket
  • createPerThreadSocket(args...) - Create per-thread socket
  • createLockFreeSocket(args...) - Create lock-free socket
  • getThreadCount() - Get number of active thread sockets (PerThread policy)
  • closeAll() - Close all thread sockets (PerThread policy)

Builder Methods

  • setID(id) - Set frame identifier
  • setDLC(dlc) / setLength(len) - Set data length
  • setData(data) - Set frame data
  • enableBRS() / enableESI() - Enable CAN FD flags
  • build() - Create frame with specified parameters
  • buildAuto() - Create frame with automatic length detection

Memory Pool Methods

  • getPoolManager() - Get global pool manager
  • allocateFrame() / allocateFDFrame() - Get frame from pool
  • deallocateFrame() / deallocateFDFrame() - Return frame to pool
  • getPoolStats() - Get pool utilization statistics

Protocol Modes

  • CAN::ProtocolMode::ClassicCAN - Standard CAN 2.0A/2.0B mode
  • CAN::ProtocolMode::CanFD - CAN FD mode with extended capabilities

Exception Types

  • CAN::SocketException - Socket-related errors
  • CAN::FrameException - Frame validation errors
  • CAN::FilterException - Filter configuration errors
  • CAN::IDException - CAN ID validation errors (exceeding bit limits)

License

This project is licensed under the Apache 2.0 License - see the LICENSE-2.0.txt file for details.

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Run the tests
  5. Submit a pull request

Troubleshooting

Virtual CAN Interface Issues

If you encounter issues with virtual CAN interfaces:

# Check if vcan module is loaded
lsmod | grep vcan

# Load the module if not present
sudo modprobe vcan

# Check interface status
ip link show vcan0

Permission Issues

Some operations may require root privileges:

# Run with sudo if needed
sudo ./build.sh

# Or set up permissions for your user
sudo usermod -a -G dialout $USER

Performance Tuning

For maximum performance:

# Increase socket buffer sizes (if needed)
echo 'net.core.rmem_max = 16777216' | sudo tee -a /etc/sysctl.conf
echo 'net.core.wmem_max = 16777216' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

# For high-frequency applications, consider:
# - Using batch operations for bulk transfers
# - Pre-sizing memory pools based on your traffic patterns
# - Setting appropriate BatchContext parameters
# - Using non-blocking I/O with proper error handling
# - Choosing the right threading policy for your use case
// Example: Optimize for high-frequency trading application
CAN::BatchContext high_perf_ctx;
high_perf_ctx.batch_size = 1000;  // Large batches
high_perf_ctx.timeout = std::chrono::microseconds(10);  // Low latency
high_perf_ctx.use_zero_copy = true;  // Enable zero-copy optimization

socket.readBatch(frames, high_perf_ctx);

Threading Performance Tuning

For multi-threaded applications:

// High-frequency parallel operations - use PerThread policy
CAN::ThreadSafeSocket<CAN::PerThreadPolicy> socket("can0");
// Each thread gets its own socket - zero contention

// Producer-consumer pattern - use LockFree policy  
CAN::ThreadSafeSocket<CAN::LockFreePolicy> socket("can0");
// Lock-free queues with dedicated I/O thread

// Monitor application performance
auto per_thread_socket = CAN::createPerThreadSocket("can0");
std::cout << "Active threads: " << per_thread_socket->getThreadCount() << std::endl;

Example Programs

  • examples/thread_safety_demo.cpp - Comprehensive threading demonstration
  • examples/basic_sender.cpp - Simple CAN frame sending
  • examples/basic_receiver.cpp - Simple CAN frame receiving
  • examples/canfd_example.cpp - CAN FD functionality
  • examples/performance_test.cpp - Performance testing and benchmarks

About

library for CAN (Controller Area Network) and CAN FD communication on Linux systems

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published