Class: CanMessenger::Adapter::Socketcan

Inherits:
Base
  • Object
show all
Defined in:
lib/can_messenger/adapter/socketcan.rb

Overview

Adapter implementation for Linux SocketCAN interfaces.

Constant Summary collapse

CAN_RAW =

rubocop:disable Metrics/ClassLength

1
SOL_CAN_BASE =
100
SOL_CAN_RAW =
SOL_CAN_BASE + CAN_RAW
CAN_RAW_FD_FRAMES =
5
FRAME_SIZE =
16
CANFD_FRAME_SIZE =
72
MIN_FRAME_SIZE =
8
MAX_FD_DATA =
64
MAX_STANDARD_ID =
0x7FF
SOCKADDR_CAN_ADDR_SIZE =
16
SOCKADDR_CAN_SIZE =
24
SOCKADDR_CAN_PADDING =
2
TIMEOUT =
[1, 0].pack("l_2")
ZERO_CAN_ADDR =
"\x00" * SOCKADDR_CAN_ADDR_SIZE

Instance Attribute Summary

Attributes inherited from Base

#endianness, #interface_name, #logger

Instance Method Summary collapse

Methods inherited from Base

#initialize, native_endianness

Constructor Details

This class inherits a constructor from CanMessenger::Adapter::Base

Instance Method Details

#build_can_frame(id:, data:, extended_id: false, can_fd: nil) ⇒ Object

Builds a raw CAN or CAN FD frame for SocketCAN.



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/can_messenger/adapter/socketcan.rb', line 38

def build_can_frame(id:, data:, extended_id: false, can_fd: nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity
  if can_fd
    raise ArgumentError, "CAN FD data cannot exceed #{MAX_FD_DATA} bytes" if data.size > MAX_FD_DATA
  elsif data.size > 8
    raise ArgumentError, "CAN data cannot exceed 8 bytes"
  end

  validate_can_id!(id, extended_id: extended_id)

  can_id = id
  # Set bit 31 for extended frames
  can_id |= Constants::EXTENDED_ID_FLAG if extended_id

  # Pack the ID based on endianness
  id_bytes = endianness == :big ? [can_id].pack("L>") : [can_id].pack("V")

  dlc_and_pad = [data.size, 0, 0, 0].pack("C*")

  payload = if can_fd
              data.pack("C*").ljust(MAX_FD_DATA, "\x00")
            else
              data.pack("C*").ljust(8, "\x00")
            end

  id_bytes + dlc_and_pad + payload
end

#open_socket(can_fd: nil) ⇒ Object

Creates and configures a CAN socket bound to the interface.



27
28
29
30
31
32
33
34
35
# File 'lib/can_messenger/adapter/socketcan.rb', line 27

def open_socket(can_fd: nil)
  socket = Socket.open(Socket::PF_CAN, Socket::SOCK_RAW, CAN_RAW)
  configure_socket(socket, can_fd: can_fd)
  socket
rescue StandardError => e
  close_socket(socket)
  logger.error("Error creating CAN socket on interface #{interface_name}: #{e}")
  nil
end

#parse_frame(frame:, can_fd: nil) ⇒ Object

Parses a raw CAN frame string into a hash with id, data and extended flag.



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/can_messenger/adapter/socketcan.rb', line 80

def parse_frame(frame:, can_fd: nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity
  return nil unless frame && frame.size >= MIN_FRAME_SIZE

  use_fd = can_fd.nil? ? frame.size >= CANFD_FRAME_SIZE : can_fd

  raw_id = unpack_frame_id(frame: frame)
  extended = raw_id.anybits?(Constants::EXTENDED_ID_FLAG)
  id = raw_id & Constants::MAX_EXTENDED_ID

  data_length = if use_fd
                  frame[4].ord
                else
                  frame[4].ord & 0x0F
                end

  data = if frame.size >= MIN_FRAME_SIZE + data_length
           frame[MIN_FRAME_SIZE, data_length].unpack("C*")
         else
           []
         end

  { id: id, data: data, extended: extended }
rescue StandardError => e
  logger.error("Error parsing CAN frame: #{e}")
  nil
end

#receive_message(socket:, can_fd: nil) ⇒ Object

Reads a frame from the socket and parses it into a hash.



66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/can_messenger/adapter/socketcan.rb', line 66

def receive_message(socket:, can_fd: nil)
  frame_size = can_fd ? CANFD_FRAME_SIZE : FRAME_SIZE
  frame = socket.recv(frame_size)
  return nil if frame.nil? || frame.size < MIN_FRAME_SIZE

  parse_frame(frame: frame, can_fd: can_fd)
rescue IO::WaitReadable
  nil
rescue StandardError => e
  logger.error("Error receiving CAN message on interface #{interface_name}: #{e}")
  nil
end