Class: CanMessenger::Messenger

Inherits:
Object
  • Object
show all
Defined in:
lib/can_messenger/messenger.rb

Overview

Messenger

This class provides an interface to send and receive CAN bus messages. It supports sending messages with specific CAN IDs and listening for incoming messages.

Examples:

messenger = CanMessenger::Messenger.new(interface_name: 'can0')
messenger.send_can_message(id: 0x123, data: [0xDE, 0xAD, 0xBE, 0xEF])
messenger.start_listening do |message|
  puts "Received: ID=#{message[:id]}, Data=#{message[:data].map { |b| '0x%02X' % b }}"
end

Instance Method Summary collapse

Constructor Details

#initialize(interface_name:, logger: nil, endianness: :native, can_fd: false, adapter: Adapter::Socketcan) ⇒ void

Initializes a new Messenger instance.

Parameters:

  • interface_name (String)

    The CAN interface to use (e.g., β€˜can0’).

  • logger (Logger, nil) (defaults to: nil)

    Optional logger for error handling and debug information.

  • endianness (Symbol) (defaults to: :native)

    The endianness of the CAN ID (default: :native) can be :big, :little, or :native.



26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/can_messenger/messenger.rb', line 26

def initialize(interface_name:, logger: nil, endianness: :native, can_fd: false, adapter: Adapter::Socketcan)
  @interface_name = interface_name
  @logger = logger || Logger.new($stdout)
  @listening = true # Control flag for listening loop
  @endianness    = endianness # :big, :little, or :native
  @can_fd        = can_fd
  @adapter = if adapter.is_a?(Class)
               adapter.new(interface_name: interface_name, logger: @logger, endianness: endianness)
             else
               adapter
             end
end

Instance Method Details

#send_can_message(id:, data:, extended_id: false, can_fd: nil) ⇒ void

This method returns an undefined value.

Sends a CAN message by writing directly to a raw CAN socket

Parameters:

  • id (Integer)

    The CAN ID of the message (up to 29 bits for extended IDs).

  • data (Array<Integer>)

    The data bytes of the CAN message (0 to 8 bytes).



44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/can_messenger/messenger.rb', line 44

def send_can_message(id:, data:, extended_id: false, can_fd: nil)
  raise ArgumentError, "id and data are required" if id.nil? || data.nil?

  use_fd = can_fd.nil? ? @can_fd : can_fd

  with_socket(can_fd: use_fd) do |socket|
    frame = @adapter.build_can_frame(id: id, data: data, extended_id: extended_id, can_fd: use_fd)
    socket.write(frame)
  end
rescue ArgumentError
  raise
rescue StandardError => e
  @logger.error("Error sending CAN message (ID: #{id}): #{e}")
end

#send_dbc_message(message_name:, signals:, dbc: @dbc, extended_id: false, can_fd: nil) ⇒ void

This method returns an undefined value.

Encodes and sends a CAN message using a DBC definition

Parameters:

  • message_name (String)

    The message name to encode

  • signals (Hash)

    Values for each signal in the message

  • dbc (CanMessenger::DBC) (defaults to: @dbc)

    The DBC instance used for encoding (defaults to @dbc)



65
66
67
68
69
70
71
72
73
74
# File 'lib/can_messenger/messenger.rb', line 65

def send_dbc_message(message_name:, signals:, dbc: @dbc, extended_id: false, can_fd: nil)
  raise ArgumentError, "dbc is required" if dbc.nil?

  encoded = normalized_dbc_message(dbc.encode_can(message_name, signals), extended_id: extended_id)
  send_can_message(id: encoded[:id], data: encoded[:data], extended_id: encoded[:extended_id], can_fd: can_fd)
rescue ArgumentError
  raise
rescue StandardError => e
  @logger.error("Error sending DBC message #{message_name}: #{e}")
end

#start_listening(filter: nil, can_fd: nil, dbc: nil) {|message| ... } ⇒ void

This method returns an undefined value.

Continuously listens for CAN messages on the specified interface.

This method listens for incoming CAN messages and applies an optional filter. The filter can be a specific CAN ID, a range of IDs, or an array of IDs. Only messages that match the filter are yielded to the provided block.

Parameters:

  • filter (Integer, Range, Array<Integer>, nil) (defaults to: nil)

    Optional filter for CAN IDs. Pass a single ID (e.g., 0x123), a range (e.g., 0x100..0x200), or an array of IDs. If no filter is provided, all messages are processed.

Yields:

  • (message)

    Yields each received CAN message as a hash with keys:

    • β€˜:id` [Integer] the CAN message ID

    • β€˜:data` [Array<Integer>] the message data bytes



89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/can_messenger/messenger.rb', line 89

def start_listening(filter: nil, can_fd: nil, dbc: nil, &)
  return @logger.error("No block provided to handle messages.") unless block_given?

  @listening = true
  validate_filter!(filter)

  use_fd = can_fd.nil? ? @can_fd : can_fd

  with_socket(can_fd: use_fd) do |socket|
    @logger.info("Started listening on #{@interface_name}")
    process_message(socket, filter, use_fd, dbc, &) while @listening
  end
end

#stop_listeningvoid

This method returns an undefined value.

Stops the listening loop by setting @listening to false.

This method can be called from an external thread or signal handler.



107
108
109
110
# File 'lib/can_messenger/messenger.rb', line 107

def stop_listening
  @listening = false
  @logger.info("Stopped listening on #{@interface_name}")
end