can_messenger 1.1.0 → 1.2.0

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: 67c064d4b8899c012642e83b9c348cf72da13eb136bc81283a2acb7116e79897
4
+ data.tar.gz: ddb8c322325800be6b03813510e1831348e724967e4f05daa9ac0c2b5b32dad3
5
5
  SHA512:
6
- metadata.gz: 61c5455b82b6b707885d0babaaea5643b3e0587f4389850dd51aa5956587ca2ea25a040136d075c5d8a01590e8ab9abf445dd1d34b77b0c8556958cefffe8bbe
7
- data.tar.gz: 0ec5d24c6612c889f472b201037b533b0ac612a7826dfbc2d45ed218f5f6600d0d8e366c0ce90dd04c28d2c421450af28832bac1984223c0da12b512e3b4ff94
6
+ metadata.gz: 3d248a72be8fb4b407fa4b79f42fc291658b39340db84c255440cd33b465c02ca7a2d6bf9274a1b52cae8b4cb3b4890d0d2562238040d2365649c1d7d7e0300e
7
+ data.tar.gz: 7749d6a7544e43db00dfec4a44a967362b449b78bb657da2f7e3afefc04956a466a9348c9414f05c2b72669858e8cecc27f8ac0b32fa2d6c8b58cbaca492e041
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
 
66
- ### Receiving CAN Messages
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:### Receiving CAN Messages
55
+
56
+ ```ruby
57
+ messenger.send_can_message(id: 0x123456, data: [0x01, 0x02, 0x03], extended_id: true)
58
+ ```
59
+
60
+ ### Listen to 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,9 +38,9 @@ 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
46
  rescue StandardError => e
@@ -111,13 +111,16 @@ module CanMessenger
111
111
  # @param id [Integer] the CAN ID
112
112
  # @param data [Array<Integer>] up to 8 bytes
113
113
  # @return [String] a 16-byte string representing a classic CAN frame
114
- def build_can_frame(id:, data:)
114
+ def build_can_frame(id:, data:, extended_id: false)
115
115
  raise ArgumentError, "CAN data cannot exceed 8 bytes" if data.size > 8
116
116
 
117
- # Apply 29-bit mask if extended ID is used
117
+ # Mask the ID to 29 bits
118
118
  can_id = id & 0x1FFFFFFF
119
119
 
120
- # Pack the ID as 4 bytes in big-endian or little-endian
120
+ # If extended_id == true, set bit 31 (CAN_EFF_FLAG)
121
+ can_id |= 0x80000000 if extended_id
122
+
123
+ # Pack the 4‐byte ID (big-endian or little-endian)
121
124
  id_bytes = @endianness == :big ? [can_id].pack("L>") : [can_id].pack("V")
122
125
 
123
126
  # 1 byte for DLC, then 3 bytes of padding
@@ -126,7 +129,7 @@ module CanMessenger
126
129
  # Up to 8 data bytes, pad with 0 if fewer
127
130
  payload = data.pack("C*").ljust(8, "\x00")
128
131
 
129
- # Total 16 bytes
132
+ # Total 16 bytes (4 for ID, 1 for DLC, 3 padding, 8 data)
130
133
  id_bytes + dlc_and_pad + payload
131
134
  end
132
135
 
@@ -166,25 +169,40 @@ module CanMessenger
166
169
  #
167
170
  # @param [String] frame
168
171
  # @return [Hash, nil]
169
- def parse_frame(frame:)
172
+ def parse_frame(frame:) # rubocop:disable Metrics/MethodLength
170
173
  return nil unless frame && frame.size >= MIN_FRAME_SIZE
171
174
 
172
- # Big-endian ID in bytes [0..3]
173
- id = unpack_frame_id(frame: frame)
175
+ raw_id = unpack_frame_id(frame: frame)
176
+
177
+ # Determine if EFF bit is set
178
+ extended = raw_id.anybits?(0x80000000)
179
+ # or raw_id.anybits?(0x80000000) if your Ruby version supports `Integer#anybits?`
180
+
181
+ # Now mask off everything except the lower 29 bits
182
+ id = raw_id & 0x1FFFFFFF
174
183
 
175
184
  # DLC is the lower 4 bits of byte 4
176
185
  data_length = frame[4].ord & 0x0F
177
186
 
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 }
187
+ # Extract data
188
+ data = if frame.size >= MIN_FRAME_SIZE + data_length
189
+ frame[MIN_FRAME_SIZE, data_length].unpack("C*")
190
+ else
191
+ []
192
+ end
193
+
194
+ { id: id, data: data, extended: extended }
181
195
  rescue StandardError => e
182
196
  @logger.error("Error parsing CAN frame: #{e}")
183
197
  nil
184
198
  end
185
199
 
186
200
  def unpack_frame_id(frame:)
187
- @endianness == :big ? frame[0..3].unpack1("L>") & 0x1FFFFFFF : frame[0..3].unpack1("V") & 0x1FFFFFFF
201
+ if @endianness == :big
202
+ frame[0..3].unpack1("L>")
203
+ else
204
+ frame[0..3].unpack1("V")
205
+ end
188
206
  end
189
207
 
190
208
  # 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.0"
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.0
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-02-28 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.