can_messenger 1.1.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8a3eb889a0115b86ec0c286505b92919e55271f7c95e5423ee7216bd49587237
4
- data.tar.gz: 97806e21ddccdcd8815219de47ed0ba0f6acad5182b878db7b2b19b1aa9cde95
3
+ metadata.gz: d47c9c4b19c40337cb6b58f380e9a3f07554144a14d23ef4c2589060fd25942e
4
+ data.tar.gz: 66f58f8fab19fb73ab5124e4036313459ae032282bdd07613dc6156ef86a1379
5
5
  SHA512:
6
- metadata.gz: 61c5455b82b6b707885d0babaaea5643b3e0587f4389850dd51aa5956587ca2ea25a040136d075c5d8a01590e8ab9abf445dd1d34b77b0c8556958cefffe8bbe
7
- data.tar.gz: 0ec5d24c6612c889f472b201037b533b0ac612a7826dfbc2d45ed218f5f6600d0d8e366c0ce90dd04c28d2c421450af28832bac1984223c0da12b512e3b4ff94
6
+ metadata.gz: a55c719faf6f813ff4aef9004b4b32065297773498bf92291019002520b43c940e304fa5a0244635f42c9152ab3729cbeaa8534d809b2790878ebc9ef9a00244
7
+ data.tar.gz: '014168fa0419b78aa966c4d1d9a415c2e8e70650323df2c1a4328354099ffacf038cd52ef1c1fc371334e312a91538e985b7ec98727863739aa70c88e449d4d3'
data/README.md CHANGED
@@ -11,18 +11,6 @@
11
11
 
12
12
  ## Installation
13
13
 
14
- Ensure you have `SocketCAN` available on your system. Typically on Linux (e.g., Debian/Ubuntu), you install SocketCAN support with:
15
-
16
- ```bash
17
- sudo apt-get install net-tools iproute2
18
- ```
19
-
20
- Then bring up your CAN interface (e.g., `can0`) using `ip` commands or a tool like `can-utils` (though this gem no longer depends on `cansend`):
21
-
22
- ```bash
23
- sudo ip link set can0 up type can bitrate 500000
24
- ```
25
-
26
14
  To install `can_messenger`, add it to your application's Gemfile:
27
15
 
28
16
  ```ruby
@@ -63,13 +51,19 @@ messenger.send_can_message(id: 0x123, data: [0xDE, 0xAD, 0xBE, 0xEF])
63
51
 
64
52
  > **Note:** Under the hood, the gem now writes CAN frames to a raw socket instead of calling `cansend`. No external dependencies are required beyond raw-socket permissions.
65
53
 
54
+ If you need to send an extended CAN frame (29-bit ID), set extended_id: true. The gem then sets the Extended Frame Format (EFF) bit automatically:
55
+
56
+ ```ruby
57
+ messenger.send_can_message(id: 0x123456, data: [0x01, 0x02, 0x03], extended_id: true)
58
+ ```
59
+
66
60
  ### Receiving CAN Messages
67
61
 
68
62
  To listen for incoming messages, set up a listener:
69
63
 
70
64
  ```ruby
71
- messenger.start_listening do |message|
72
- puts "Received: ID=#{message[:id]}, Data=#{message[:data]}"
65
+ messenger.start_listening do |msg|
66
+ puts "Received ID=0x#{msg[:id].to_s(16)}, Extended=#{msg[:extended]}, Data=#{msg[:data]}"
73
67
  end
74
68
  ```
75
69
 
@@ -15,7 +15,7 @@ module CanMessenger
15
15
  # messenger.start_listening do |message|
16
16
  # puts "Received: ID=#{message[:id]}, Data=#{message[:data].map { |b| '0x%02X' % b }}"
17
17
  # end
18
- class Messenger
18
+ class Messenger # rubocop:disable Metrics/ClassLength
19
19
  FRAME_SIZE = 16
20
20
  MIN_FRAME_SIZE = 8
21
21
  TIMEOUT = [1, 0].pack("l_2")
@@ -38,11 +38,13 @@ module CanMessenger
38
38
  # @param [Integer] id The CAN ID of the message (up to 29 bits for extended IDs).
39
39
  # @param [Array<Integer>] data The data bytes of the CAN message (0 to 8 bytes).
40
40
  # @return [void]
41
- def send_can_message(id:, data:)
41
+ def send_can_message(id:, data:, extended_id: false)
42
42
  with_socket do |socket|
43
- frame = build_can_frame(id: id, data: data)
43
+ frame = build_can_frame(id: id, data: data, extended_id: extended_id)
44
44
  socket.write(frame)
45
45
  end
46
+ rescue ArgumentError
47
+ raise
46
48
  rescue StandardError => e
47
49
  @logger.error("Error sending CAN message (ID: #{id}): #{e}")
48
50
  end
@@ -63,6 +65,8 @@ module CanMessenger
63
65
  def start_listening(filter: nil, &block)
64
66
  return @logger.error("No block provided to handle messages.") unless block_given?
65
67
 
68
+ @listening = true
69
+
66
70
  with_socket do |socket|
67
71
  @logger.info("Started listening on #{@interface_name}")
68
72
  process_message(socket, filter, &block) while @listening
@@ -111,13 +115,16 @@ module CanMessenger
111
115
  # @param id [Integer] the CAN ID
112
116
  # @param data [Array<Integer>] up to 8 bytes
113
117
  # @return [String] a 16-byte string representing a classic CAN frame
114
- def build_can_frame(id:, data:)
118
+ def build_can_frame(id:, data:, extended_id: false)
115
119
  raise ArgumentError, "CAN data cannot exceed 8 bytes" if data.size > 8
116
120
 
117
- # Apply 29-bit mask if extended ID is used
121
+ # Mask the ID to 29 bits
118
122
  can_id = id & 0x1FFFFFFF
119
123
 
120
- # Pack the ID as 4 bytes in big-endian or little-endian
124
+ # If extended_id == true, set bit 31 (CAN_EFF_FLAG)
125
+ can_id |= 0x80000000 if extended_id
126
+
127
+ # Pack the 4‐byte ID (big-endian or little-endian)
121
128
  id_bytes = @endianness == :big ? [can_id].pack("L>") : [can_id].pack("V")
122
129
 
123
130
  # 1 byte for DLC, then 3 bytes of padding
@@ -126,7 +133,7 @@ module CanMessenger
126
133
  # Up to 8 data bytes, pad with 0 if fewer
127
134
  payload = data.pack("C*").ljust(8, "\x00")
128
135
 
129
- # Total 16 bytes
136
+ # Total 16 bytes (4 for ID, 1 for DLC, 3 padding, 8 data)
130
137
  id_bytes + dlc_and_pad + payload
131
138
  end
132
139
 
@@ -166,25 +173,40 @@ module CanMessenger
166
173
  #
167
174
  # @param [String] frame
168
175
  # @return [Hash, nil]
169
- def parse_frame(frame:)
176
+ def parse_frame(frame:) # rubocop:disable Metrics/MethodLength
170
177
  return nil unless frame && frame.size >= MIN_FRAME_SIZE
171
178
 
172
- # Big-endian ID in bytes [0..3]
173
- id = unpack_frame_id(frame: frame)
179
+ raw_id = unpack_frame_id(frame: frame)
180
+
181
+ # Determine if EFF bit is set
182
+ extended = raw_id.anybits?(0x80000000)
183
+ # or raw_id.anybits?(0x80000000) if your Ruby version supports `Integer#anybits?`
184
+
185
+ # Now mask off everything except the lower 29 bits
186
+ id = raw_id & 0x1FFFFFFF
174
187
 
175
188
  # DLC is the lower 4 bits of byte 4
176
189
  data_length = frame[4].ord & 0x0F
177
190
 
178
- # Data follows at byte index 8, up to data_length bytes
179
- data = (frame[MIN_FRAME_SIZE, data_length].unpack("C*") if frame.size >= MIN_FRAME_SIZE + data_length)
180
- { id: id, data: data }
191
+ # Extract data
192
+ data = if frame.size >= MIN_FRAME_SIZE + data_length
193
+ frame[MIN_FRAME_SIZE, data_length].unpack("C*")
194
+ else
195
+ []
196
+ end
197
+
198
+ { id: id, data: data, extended: extended }
181
199
  rescue StandardError => e
182
200
  @logger.error("Error parsing CAN frame: #{e}")
183
201
  nil
184
202
  end
185
203
 
186
204
  def unpack_frame_id(frame:)
187
- @endianness == :big ? frame[0..3].unpack1("L>") & 0x1FFFFFFF : frame[0..3].unpack1("V") & 0x1FFFFFFF
205
+ if @endianness == :big
206
+ frame[0..3].unpack1("L>")
207
+ else
208
+ frame[0..3].unpack1("V")
209
+ end
188
210
  end
189
211
 
190
212
  # Checks whether the given message ID matches the specified filter.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CanMessenger
4
- VERSION = "1.1.0"
4
+ VERSION = "1.2.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: can_messenger
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - fk1018
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-02-11 00:00:00.000000000 Z
11
+ date: 2025-06-05 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: CanMessenger provides an interface to send and receive messages over
14
14
  the CAN bus, useful for applications requiring CAN communication in Ruby.