can_messenger 1.4.0 → 2.1.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 +4 -4
- data/CHANGELOG.md +181 -0
- data/LICENSE.txt +21 -0
- data/README.md +74 -7
- data/lib/can_messenger/adapter/base.rb +62 -0
- data/lib/can_messenger/adapter/socketcan.rb +125 -0
- data/lib/can_messenger/dbc.rb +40 -25
- data/lib/can_messenger/messenger.rb +15 -129
- data/lib/can_messenger/version.rb +1 -1
- metadata +8 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b79b0febdca3de5f068c142413eef6f959461518e3928cd9beed52b14ee8ceef
|
|
4
|
+
data.tar.gz: aca5cf8fde09f75b6f0d7a81ab1bca9e6a59fc2d3d811a72321eb6b9d6b88ba9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 864bd3e56da612dee5e02bd6e575eff32adab311c3090e2a874704ee7fb6f84895146294a83362be40faef5477e3951c69203f6ecd4ad094a79067d4a5928d1e
|
|
7
|
+
data.tar.gz: c1bc154e2b42b3373aadd0e27572d66221d3859fb5272f0594fc5d1adb1a4e1382de1e04e02e7e2171bf28b6f2a1cfc33500b4c4af0f0acd3edf35ea3b87c1f4
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
## [Unreleased]
|
|
2
|
+
|
|
3
|
+
## [2.1.0] - 2026-02-23
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Promote the first stable docs snapshot version to `2.1.0`.
|
|
8
|
+
- Tighten DBC signal bounds validation and report exact out-of-range bit positions.
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Fix DBC big-endian (`@0`) bit mapping for multi-byte signals.
|
|
13
|
+
- Reject unsigned DBC signal values that exceed the field's bit-length range instead of silently wrapping.
|
|
14
|
+
- Align `Messenger` RBS signatures with runtime behavior (`adapter:` support and correct private method surface).
|
|
15
|
+
- Update DBC specs to cover the corrected big-endian behavior and unsigned overflow errors.
|
|
16
|
+
|
|
17
|
+
## [2.0.0] - 2026-02-02
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- **Breaking:** Require Ruby 4.0.1 or higher.
|
|
22
|
+
- Update CI to run on Ruby 4.0.1.
|
|
23
|
+
- Upgrade RuboCop to support Ruby 4.0 and refresh linting.
|
|
24
|
+
- Minor style cleanups in DBC parsing and message listener block forwarding.
|
|
25
|
+
- Extract SocketCAN logic into a dedicated adapter and add a base adapter interface.
|
|
26
|
+
- Allow injecting a custom adapter into `Messenger` for alternate transports or testing.
|
|
27
|
+
- **Breaking:** Default CAN ID endianness is now native (`:native`) instead of `:big`.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- Close SocketCAN sockets when bind/setsockopt fails to avoid leaks.
|
|
32
|
+
|
|
33
|
+
## [1.4.0] - 2025-07-25
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- `send_dbc_message` helper for encoding and sending messages defined in DBC files.
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
|
|
41
|
+
- `send_can_message` now only accepts raw frame parameters.
|
|
42
|
+
- DBC parsing code split into helper methods for clarity.
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
|
|
46
|
+
- Correct encoding of negative signal values using two's-complement.
|
|
47
|
+
|
|
48
|
+
## [1.3.0] - 2025-06-27
|
|
49
|
+
|
|
50
|
+
### Added
|
|
51
|
+
|
|
52
|
+
- Optional **CAN FD** support for sending and receiving up to 64-byte frames.
|
|
53
|
+
|
|
54
|
+
### Changed
|
|
55
|
+
|
|
56
|
+
- Updated APIs to accept a `can_fd:` flag on initialization and message methods.
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
|
|
60
|
+
- (Nothing since last release.)
|
|
61
|
+
|
|
62
|
+
## [1.2.1] - 2025-06-05
|
|
63
|
+
|
|
64
|
+
### Changed
|
|
65
|
+
|
|
66
|
+
- `send_can_message` now raises `ArgumentError` when data length exceeds eight bytes.
|
|
67
|
+
- Updated RBS `initialize` signature to include the `endianness` argument.
|
|
68
|
+
- Fixed formatting in README around extended CAN frames.
|
|
69
|
+
- Clarified spec helper comment.
|
|
70
|
+
|
|
71
|
+
### Fixed
|
|
72
|
+
|
|
73
|
+
- Addressed a listener restart bug allowing `start_listening` to be called again.
|
|
74
|
+
|
|
75
|
+
## [1.2.0] - 2025-02-28
|
|
76
|
+
|
|
77
|
+
### Added
|
|
78
|
+
|
|
79
|
+
- **Explicit extended CAN ID support**.
|
|
80
|
+
- Added an `extended_id: false` parameter to `send_can_message`, which, if set to `true`, sets the Extended Frame Format bit (bit 31) in the CAN ID.
|
|
81
|
+
- Updated `parse_frame` to detect and report `extended: true` when the EFF bit is set in incoming frames.
|
|
82
|
+
- Added corresponding tests for sending and receiving extended CAN frames.
|
|
83
|
+
|
|
84
|
+
### Changed
|
|
85
|
+
|
|
86
|
+
- _No breaking changes_, but internal refactoring around how CAN IDs are packed and unpacked.
|
|
87
|
+
- Removed the masking of bit 31 in `unpack_frame_id`, ensuring extended frames are no longer silently treated as standard frames.
|
|
88
|
+
|
|
89
|
+
### Fixed
|
|
90
|
+
|
|
91
|
+
- (Nothing since last release.)
|
|
92
|
+
|
|
93
|
+
## [1.1.0] - 2025-02-10
|
|
94
|
+
|
|
95
|
+
### Changed
|
|
96
|
+
|
|
97
|
+
- **Removed dependency on `cansend`**. We now write CAN frames directly via raw sockets.
|
|
98
|
+
- Internal refactoring to support raw-socket–based sending without changing the public API.
|
|
99
|
+
|
|
100
|
+
### Fixed
|
|
101
|
+
|
|
102
|
+
## [1.0.3] - 2025-02-09
|
|
103
|
+
|
|
104
|
+
- Revert release.yml
|
|
105
|
+
|
|
106
|
+
### Fixed
|
|
107
|
+
|
|
108
|
+
## [1.0.2] - 2025-02-09
|
|
109
|
+
|
|
110
|
+
- Bugfix release.yml
|
|
111
|
+
|
|
112
|
+
## [1.0.1] - 2025-02-09
|
|
113
|
+
|
|
114
|
+
### Changed
|
|
115
|
+
|
|
116
|
+
- Updated the README to include an **Important Considerations** section that outlines environment requirements, API changes (keyword arguments and block requirement), threading and socket management notes, and logging behavior.
|
|
117
|
+
- Made minor documentation clarifications and tweaks to help users avoid common pitfalls.
|
|
118
|
+
|
|
119
|
+
## [1.0.0] - 2025-02-09
|
|
120
|
+
|
|
121
|
+
### Changed
|
|
122
|
+
|
|
123
|
+
- **Breaking Change:** Updated the Messenger API to require keyword arguments (e.g., `interface_name:`) for initialization. Existing code using the old API will need to be updated.
|
|
124
|
+
- **Breaking Change:** Updated the send_can_message Method to require keyword arguments (e.g., `id:,data:`) to send messages to the can bus. Existing code using the old API will need to be updated.
|
|
125
|
+
- Refactored `start_listening`.
|
|
126
|
+
- Enhanced error handling throughout the gem with more detailed logging.
|
|
127
|
+
- Updated type signatures (RBS) and documentation to match the new API.
|
|
128
|
+
- Refactored tests to reflect the new API and improved error handling.
|
|
129
|
+
|
|
130
|
+
## [0.2.3] - 2025-02-01
|
|
131
|
+
|
|
132
|
+
### Changed
|
|
133
|
+
|
|
134
|
+
- Updated the internal listening loop in `start_listening` to continue iterating on nil (timeout) responses instead of breaking out, improving reliability.
|
|
135
|
+
- Suppressed log output during tests by injecting a silent logger.
|
|
136
|
+
- Updated the test suite to better handle long-running listening loops and error conditions.
|
|
137
|
+
|
|
138
|
+
## [0.2.2] - 2024-12-06
|
|
139
|
+
|
|
140
|
+
### Changed
|
|
141
|
+
|
|
142
|
+
- Updated README.md to reflect modern debian package install command.
|
|
143
|
+
|
|
144
|
+
## [0.2.1] - 2024-12-06
|
|
145
|
+
|
|
146
|
+
### Changed
|
|
147
|
+
|
|
148
|
+
- Updated `start_listening` RBS signature to include the `filter` parameter, ensuring type definitions match the implementation.
|
|
149
|
+
|
|
150
|
+
## [0.2.0] - 2024-12-05
|
|
151
|
+
|
|
152
|
+
### Added
|
|
153
|
+
|
|
154
|
+
- Filtering support for `start_listening` via a `filter` parameter:
|
|
155
|
+
- Single CAN ID.
|
|
156
|
+
- Range of CAN IDs.
|
|
157
|
+
- Array of CAN IDs.
|
|
158
|
+
|
|
159
|
+
### Changed
|
|
160
|
+
|
|
161
|
+
- Refactored `start_listening` to support optional filtering of incoming CAN messages.
|
|
162
|
+
- Documentation updates for `start_listening` in README.
|
|
163
|
+
|
|
164
|
+
## [0.1.0] - 2024-11-10
|
|
165
|
+
|
|
166
|
+
- Initial release
|
|
167
|
+
[Unreleased]: https://github.com/fk1018/can_messenger/compare/v2.1.0...HEAD
|
|
168
|
+
[2.1.0]: https://github.com/fk1018/can_messenger/compare/v2.0.0...v2.1.0
|
|
169
|
+
[2.0.0]: https://github.com/fk1018/can_messenger/compare/v1.3.0...v2.0.0
|
|
170
|
+
[1.3.0]: https://github.com/fk1018/can_messenger/compare/v1.2.1...v1.3.0
|
|
171
|
+
[1.2.1]: https://github.com/fk1018/can_messenger/compare/v1.2.0...v1.2.1
|
|
172
|
+
[1.2.0]: https://github.com/fk1018/can_messenger/compare/v1.1.0...v1.2.0
|
|
173
|
+
[1.1.0]: https://github.com/fk1018/can_messenger/compare/v1.0.3...v1.1.0
|
|
174
|
+
[1.0.3]: https://github.com/fk1018/can_messenger/compare/v1.0.1...v1.0.3
|
|
175
|
+
[1.0.1]: https://github.com/fk1018/can_messenger/compare/v1.0.0...v1.0.1
|
|
176
|
+
[1.0.0]: https://github.com/fk1018/can_messenger/compare/v0.2.3...v1.0.0
|
|
177
|
+
[0.2.3]: https://github.com/fk1018/can_messenger/compare/v0.2.2...v0.2.3
|
|
178
|
+
[0.2.2]: https://github.com/fk1018/can_messenger/compare/v0.2.1...v0.2.2
|
|
179
|
+
[0.2.1]: https://github.com/fk1018/can_messenger/compare/v0.2.0...v0.2.1
|
|
180
|
+
[0.2.0]: https://github.com/fk1018/can_messenger/compare/v0.1.0...v0.2.0
|
|
181
|
+
[0.1.0]: https://github.com/fk1018/can_messenger/releases/tag/v0.1.0
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 fk1018
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
CHANGED
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
|
|
13
13
|
## Requirements
|
|
14
14
|
|
|
15
|
-
- Ruby
|
|
15
|
+
- Ruby 4.0.1 or higher.
|
|
16
|
+
- Docker (optional, for containerized development without installing Ruby locally).
|
|
16
17
|
|
|
17
18
|
## Installation
|
|
18
19
|
|
|
@@ -135,17 +136,34 @@ To stop listening, use:
|
|
|
135
136
|
messenger.stop_listening
|
|
136
137
|
```
|
|
137
138
|
|
|
139
|
+
### Adapters
|
|
140
|
+
|
|
141
|
+
`CanMessenger::Messenger` delegates low-level CAN bus operations to an adapter. By default it uses the
|
|
142
|
+
SocketCAN adapter which communicates with Linux CAN interfaces using raw sockets:
|
|
143
|
+
|
|
144
|
+
```ruby
|
|
145
|
+
messenger = CanMessenger::Messenger.new(interface_name: "can0")
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
You can provide a custom adapter via the `adapter:` option:
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
my_adapter = MyCustomAdapter.new(interface_name: "can0", logger: Logger.new($stdout))
|
|
152
|
+
messenger = CanMessenger::Messenger.new(interface_name: "can0", adapter: my_adapter)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
To build your own adapter, subclass `CanMessenger::Adapter::Base` and implement the required methods
|
|
156
|
+
`open_socket`, `build_can_frame`, `receive_message`, and `parse_frame`.
|
|
157
|
+
|
|
138
158
|
## Important Considerations
|
|
139
159
|
|
|
140
160
|
Before using `can_messenger`, please note the following:
|
|
141
161
|
|
|
142
162
|
- **Environment Requirements:**
|
|
143
|
-
|
|
144
163
|
- **SocketCAN** must be available on your Linux system.
|
|
145
164
|
- **Permissions:** Working with raw sockets may require elevated privileges or membership in a specific group to open and bind to CAN interfaces without running as root.
|
|
146
165
|
|
|
147
166
|
- **API Changes (v1.0.0 and later):**
|
|
148
|
-
|
|
149
167
|
- **Keyword Arguments:** The Messenger API now requires keyword arguments. For example, when initializing the Messenger:
|
|
150
168
|
|
|
151
169
|
```ruby
|
|
@@ -169,16 +187,14 @@ Before using `can_messenger`, please note the following:
|
|
|
169
187
|
```
|
|
170
188
|
|
|
171
189
|
- **Threading & Socket Management:**
|
|
172
|
-
|
|
173
190
|
- **Blocking Behavior:** The gem uses blocking socket calls and continuously listens for messages. Manage the listener’s lifecycle appropriately, especially in multi-threaded environments. Always call `stop_listening` to gracefully shut down the listener.
|
|
174
191
|
- **Resource Cleanup:** The socket is automatically closed when the listening loop terminates. Stop the listener to avoid resource leaks.
|
|
175
192
|
|
|
176
193
|
- **Logging:**
|
|
177
|
-
|
|
178
194
|
- **Default Logger:** If no logger is provided, logs go to standard output. Provide a custom logger if you want more control.
|
|
179
195
|
|
|
180
196
|
- **CAN Frame Format Assumptions:**
|
|
181
|
-
- By default, the gem uses **
|
|
197
|
+
- By default, the gem uses **native endianness** for CAN IDs (little-endian on most x86/ARM systems). **Changed in v2.0.0:** this default was previously `:big`. You can override this by passing `endianness: :big` or `endianness: :little`.
|
|
182
198
|
- The gem expects a standard CAN frame layout (16 bytes total, with the first 4 for the ID, followed by 1 byte for DLC, 3 bytes of padding, and up to 8 bytes of data). **CAN FD** frames (up to 64 bytes) are supported when enabled.
|
|
183
199
|
|
|
184
200
|
## Features
|
|
@@ -191,7 +207,58 @@ Before using `can_messenger`, please note the following:
|
|
|
191
207
|
|
|
192
208
|
## Development
|
|
193
209
|
|
|
194
|
-
|
|
210
|
+
### Docker-first workflow (no local Ruby required)
|
|
211
|
+
|
|
212
|
+
Build the development image:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
docker compose build app
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Run RuboCop:
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
docker compose run --rm lint
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Run the test suite:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
docker compose run --rm test
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Build the gem:
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
docker compose run --rm build
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Install docs dependencies (Docusaurus):
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
docker compose run --rm docs npm ci
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Build the docs site:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
docker compose run --rm docs npm run build
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Preview docs locally:
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
docker compose run --rm --service-ports docs npm run start
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Local Ruby workflow
|
|
255
|
+
|
|
256
|
+
If you already have Ruby installed locally, you can still use:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
bin/setup
|
|
260
|
+
bundle exec rake test:rspec
|
|
261
|
+
```
|
|
195
262
|
|
|
196
263
|
## Contributing
|
|
197
264
|
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CanMessenger
|
|
4
|
+
module Adapter
|
|
5
|
+
# Base adapter defines the interface for CAN bus adapters.
|
|
6
|
+
# Concrete adapters must implement all of the methods defined here.
|
|
7
|
+
class Base
|
|
8
|
+
attr_reader :interface_name, :logger, :endianness
|
|
9
|
+
|
|
10
|
+
def self.native_endianness
|
|
11
|
+
native = pack_uint(1, "I")
|
|
12
|
+
return :little if native == pack_uint(1, "V")
|
|
13
|
+
return :big if native == pack_uint(1, "N")
|
|
14
|
+
|
|
15
|
+
# Fallback for unusual platforms.
|
|
16
|
+
native.bytes.first == 1 ? :little : :big
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.pack_uint(value, template)
|
|
20
|
+
[value].pack(template)
|
|
21
|
+
end
|
|
22
|
+
private_class_method :pack_uint
|
|
23
|
+
|
|
24
|
+
def initialize(interface_name:, logger:, endianness: :native)
|
|
25
|
+
@interface_name = interface_name
|
|
26
|
+
@logger = logger
|
|
27
|
+
normalized_endianness = normalize_endianness(endianness)
|
|
28
|
+
@endianness = normalized_endianness == :native ? self.class.native_endianness : normalized_endianness
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Open a socket for the underlying interface.
|
|
32
|
+
# @return [Object] adapter-specific socket
|
|
33
|
+
def open_socket(can_fd: false)
|
|
34
|
+
raise NotImplementedError, "open_socket must be implemented in subclasses"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Build a frame ready to be written to the socket.
|
|
38
|
+
def build_can_frame(id:, data:, extended_id: false, can_fd: false)
|
|
39
|
+
raise NotImplementedError, "build_can_frame must be implemented in subclasses"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Receive and parse a frame from the socket.
|
|
43
|
+
def receive_message(socket:, can_fd: false)
|
|
44
|
+
raise NotImplementedError, "receive_message must be implemented in subclasses"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Parse a raw frame string into a message hash.
|
|
48
|
+
def parse_frame(frame:, can_fd: false)
|
|
49
|
+
raise NotImplementedError, "parse_frame must be implemented in subclasses"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def normalize_endianness(endianness)
|
|
55
|
+
normalized = endianness.is_a?(String) ? endianness.strip.downcase.to_sym : endianness
|
|
56
|
+
return normalized if %i[native little big].include?(normalized)
|
|
57
|
+
|
|
58
|
+
raise ArgumentError, "endianness must be :native, :little, or :big"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "socket"
|
|
4
|
+
require_relative "base"
|
|
5
|
+
|
|
6
|
+
module CanMessenger
|
|
7
|
+
module Adapter
|
|
8
|
+
# Adapter implementation for Linux SocketCAN interfaces.
|
|
9
|
+
class Socketcan < Base
|
|
10
|
+
FRAME_SIZE = 16
|
|
11
|
+
CANFD_FRAME_SIZE = 72
|
|
12
|
+
MIN_FRAME_SIZE = 8
|
|
13
|
+
MAX_FD_DATA = 64
|
|
14
|
+
TIMEOUT = [1, 0].pack("l_2")
|
|
15
|
+
|
|
16
|
+
# Creates and configures a CAN socket bound to the interface.
|
|
17
|
+
def open_socket(can_fd: false)
|
|
18
|
+
socket = Socket.open(Socket::PF_CAN, Socket::SOCK_RAW, Socket::CAN_RAW)
|
|
19
|
+
configure_socket(socket, can_fd: can_fd)
|
|
20
|
+
socket
|
|
21
|
+
rescue StandardError => e
|
|
22
|
+
close_socket(socket)
|
|
23
|
+
logger.error("Error creating CAN socket on interface #{interface_name}: #{e}")
|
|
24
|
+
nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Builds a raw CAN or CAN FD frame for SocketCAN.
|
|
28
|
+
def build_can_frame(id:, data:, extended_id: false, can_fd: false) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity
|
|
29
|
+
if can_fd
|
|
30
|
+
raise ArgumentError, "CAN FD data cannot exceed #{MAX_FD_DATA} bytes" if data.size > MAX_FD_DATA
|
|
31
|
+
elsif data.size > 8
|
|
32
|
+
raise ArgumentError, "CAN data cannot exceed 8 bytes"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Mask the ID to 29 bits
|
|
36
|
+
can_id = id & 0x1FFFFFFF
|
|
37
|
+
# Set bit 31 for extended frames
|
|
38
|
+
can_id |= 0x80000000 if extended_id
|
|
39
|
+
|
|
40
|
+
# Pack the ID based on endianness
|
|
41
|
+
id_bytes = endianness == :big ? [can_id].pack("L>") : [can_id].pack("V")
|
|
42
|
+
|
|
43
|
+
dlc_and_pad = [data.size, 0, 0, 0].pack("C*")
|
|
44
|
+
|
|
45
|
+
payload = if can_fd
|
|
46
|
+
data.pack("C*").ljust(MAX_FD_DATA, "\x00")
|
|
47
|
+
else
|
|
48
|
+
data.pack("C*").ljust(8, "\x00")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
id_bytes + dlc_and_pad + payload
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Reads a frame from the socket and parses it into a hash.
|
|
55
|
+
def receive_message(socket:, can_fd: false)
|
|
56
|
+
frame_size = can_fd ? CANFD_FRAME_SIZE : FRAME_SIZE
|
|
57
|
+
frame = socket.recv(frame_size)
|
|
58
|
+
return nil if frame.nil? || frame.size < MIN_FRAME_SIZE
|
|
59
|
+
|
|
60
|
+
parse_frame(frame: frame, can_fd: can_fd)
|
|
61
|
+
rescue IO::WaitReadable
|
|
62
|
+
nil
|
|
63
|
+
rescue StandardError => e
|
|
64
|
+
logger.error("Error receiving CAN message on interface #{interface_name}: #{e}")
|
|
65
|
+
nil
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Parses a raw CAN frame string into a hash with id, data and extended flag.
|
|
69
|
+
def parse_frame(frame:, can_fd: nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity
|
|
70
|
+
return nil unless frame && frame.size >= MIN_FRAME_SIZE
|
|
71
|
+
|
|
72
|
+
use_fd = can_fd.nil? ? frame.size >= CANFD_FRAME_SIZE : can_fd
|
|
73
|
+
|
|
74
|
+
raw_id = unpack_frame_id(frame: frame)
|
|
75
|
+
extended = raw_id.anybits?(0x80000000)
|
|
76
|
+
id = raw_id & 0x1FFFFFFF
|
|
77
|
+
|
|
78
|
+
data_length = if use_fd
|
|
79
|
+
frame[4].ord
|
|
80
|
+
else
|
|
81
|
+
frame[4].ord & 0x0F
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
data = if frame.size >= MIN_FRAME_SIZE + data_length
|
|
85
|
+
frame[MIN_FRAME_SIZE, data_length].unpack("C*")
|
|
86
|
+
else
|
|
87
|
+
[]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
{ id: id, data: data, extended: extended }
|
|
91
|
+
rescue StandardError => e
|
|
92
|
+
logger.error("Error parsing CAN frame: #{e}")
|
|
93
|
+
nil
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def unpack_frame_id(frame:)
|
|
99
|
+
if endianness == :big
|
|
100
|
+
frame[0..3].unpack1("L>")
|
|
101
|
+
else
|
|
102
|
+
frame[0..3].unpack1("V")
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def configure_socket(socket, can_fd:)
|
|
107
|
+
socket.bind(Socket.pack_sockaddr_can(interface_name))
|
|
108
|
+
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, TIMEOUT)
|
|
109
|
+
return unless can_fd && Socket.const_defined?(:CAN_RAW_FD_FRAMES)
|
|
110
|
+
|
|
111
|
+
socket.setsockopt(Socket.const_defined?(:SOL_CAN_RAW) ? Socket::SOL_CAN_RAW : Socket::CAN_RAW,
|
|
112
|
+
Socket::CAN_RAW_FD_FRAMES, 1)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def close_socket(socket)
|
|
116
|
+
return unless socket
|
|
117
|
+
return if socket.closed?
|
|
118
|
+
|
|
119
|
+
socket.close
|
|
120
|
+
rescue StandardError
|
|
121
|
+
# Ignore close errors so we can report the original failure.
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
data/lib/can_messenger/dbc.rb
CHANGED
|
@@ -85,7 +85,7 @@ module CanMessenger
|
|
|
85
85
|
# @param [Message] _current The current message being processed (unused but kept for API consistency)
|
|
86
86
|
# @return [Signal, nil] A Signal object if the line matches, nil otherwise
|
|
87
87
|
def parse_signal_line(line, _current) # rubocop:disable Metrics/MethodLength
|
|
88
|
-
return unless (m = line.match(/^SG_\s+(\w+)\s*:\s*(\d+)\|(\d+)@(\d)([+-])\s*\(([^,]+),([
|
|
88
|
+
return unless (m = line.match(/^SG_\s+(\w+)\s*:\s*(\d+)\|(\d+)@(\d)([+-])\s*\(([^,]+),([^)]+)\)/))
|
|
89
89
|
|
|
90
90
|
sig_name = m[1]
|
|
91
91
|
start_bit = m[2].to_i
|
|
@@ -283,15 +283,15 @@ module CanMessenger
|
|
|
283
283
|
# @return [void]
|
|
284
284
|
# @raise [ArgumentError] If signal bits exceed message boundaries or start_bit is negative
|
|
285
285
|
def validate_signal_bounds(message_size_bytes)
|
|
286
|
-
max_bit = start_bit + length - 1
|
|
287
|
-
max_allowed_bit = (message_size_bytes * 8) - 1
|
|
288
|
-
|
|
289
286
|
raise ArgumentError, "Signal #{name}: start_bit (#{start_bit}) cannot be negative" if start_bit.negative?
|
|
287
|
+
raise ArgumentError, "Signal #{name}: length (#{length}) must be positive" if length <= 0
|
|
290
288
|
|
|
291
|
-
|
|
289
|
+
max_allowed_bit = max_allowed_bit_for(message_size_bytes)
|
|
290
|
+
invalid_position = invalid_bit_position(max_allowed_bit)
|
|
291
|
+
return unless invalid_position
|
|
292
292
|
|
|
293
293
|
raise ArgumentError,
|
|
294
|
-
"Signal #{name}:
|
|
294
|
+
"Signal #{name}: bit position #{invalid_position} exceeds message size " \
|
|
295
295
|
"(#{message_size_bytes} bytes = #{max_allowed_bit + 1} bits)"
|
|
296
296
|
end
|
|
297
297
|
|
|
@@ -328,9 +328,15 @@ module CanMessenger
|
|
|
328
328
|
# @return [void]
|
|
329
329
|
# @raise [ArgumentError] If an unsigned value is negative
|
|
330
330
|
def validate_unsigned_value(raw)
|
|
331
|
-
return unless sign == :unsigned
|
|
331
|
+
return unless sign == :unsigned
|
|
332
|
+
|
|
333
|
+
raise ArgumentError, "Unsigned value cannot be negative: #{raw}" if raw.negative?
|
|
332
334
|
|
|
333
|
-
|
|
335
|
+
max_val = (1 << length) - 1
|
|
336
|
+
return if raw <= max_val
|
|
337
|
+
|
|
338
|
+
raise ArgumentError,
|
|
339
|
+
"Unsigned value #{raw} out of range [0..#{max_val}] for #{length}-bit field"
|
|
334
340
|
end
|
|
335
341
|
|
|
336
342
|
# Validates signed values to ensure they fit in the signal's bit range.
|
|
@@ -396,19 +402,12 @@ module CanMessenger
|
|
|
396
402
|
if endianness == :little
|
|
397
403
|
start_bit + bit_offset
|
|
398
404
|
else
|
|
399
|
-
#
|
|
400
|
-
#
|
|
401
|
-
#
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
# position (aligned to the nearest multiple of 8). Adding (7 - (start_bit % 8))
|
|
406
|
-
# adjusts this base position to point to the MSB of the starting byte.
|
|
407
|
-
#
|
|
408
|
-
# Finally, we subtract the bit offset to account for the signal's length and
|
|
409
|
-
# position within the message.
|
|
410
|
-
base = ((start_bit / 8) * 8) + (7 - (start_bit % 8))
|
|
411
|
-
base - bit_offset
|
|
405
|
+
# DBC big-endian (`@0`) start_bit is in sawtooth numbering and points at the
|
|
406
|
+
# signal MSB. Convert to sequential network numbering, move toward the LSB,
|
|
407
|
+
# then convert back to the little-endian bit index used by byte writes.
|
|
408
|
+
network_start = sawtooth_to_network_bitnum(start_bit)
|
|
409
|
+
network_bit = network_start + (length - 1 - bit_offset)
|
|
410
|
+
sawtooth_to_network_bitnum(network_bit)
|
|
412
411
|
end
|
|
413
412
|
end
|
|
414
413
|
|
|
@@ -506,11 +505,27 @@ module CanMessenger
|
|
|
506
505
|
# @param [Integer] value The unsigned integer value to potentially convert
|
|
507
506
|
# @return [Integer] The final signed or unsigned value
|
|
508
507
|
def convert_to_signed_if_needed(value)
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
508
|
+
return value unless sign == :signed && length.positive?
|
|
509
|
+
|
|
510
|
+
msb_set = (value >> (length - 1)).allbits?(1)
|
|
511
|
+
msb_set ? value - (1 << length) : value
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
def sawtooth_to_network_bitnum(bitnum)
|
|
515
|
+
(8 * (bitnum / 8)) + (7 - (bitnum % 8))
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
def invalid_bit_position(max_allowed_bit)
|
|
519
|
+
length.times do |i|
|
|
520
|
+
bit_pos = calculate_bit_position(i)
|
|
521
|
+
return bit_pos if bit_pos.negative? || bit_pos > max_allowed_bit
|
|
513
522
|
end
|
|
523
|
+
|
|
524
|
+
nil
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
def max_allowed_bit_for(message_size_bytes)
|
|
528
|
+
(message_size_bytes * 8) - 1
|
|
514
529
|
end
|
|
515
530
|
end
|
|
516
531
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "socket"
|
|
4
3
|
require "logger"
|
|
4
|
+
require_relative "adapter/socketcan"
|
|
5
5
|
|
|
6
6
|
module CanMessenger
|
|
7
7
|
# Messenger
|
|
@@ -15,25 +15,24 @@ 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
|
|
19
|
-
FRAME_SIZE = 16
|
|
20
|
-
CANFD_FRAME_SIZE = 72
|
|
21
|
-
MIN_FRAME_SIZE = 8
|
|
22
|
-
MAX_FD_DATA = 64
|
|
23
|
-
TIMEOUT = [1, 0].pack("l_2")
|
|
24
|
-
|
|
18
|
+
class Messenger
|
|
25
19
|
# Initializes a new Messenger instance.
|
|
26
20
|
#
|
|
27
21
|
# @param [String] interface_name The CAN interface to use (e.g., 'can0').
|
|
28
22
|
# @param [Logger, nil] logger Optional logger for error handling and debug information.
|
|
29
|
-
# @param [Symbol] endianness The endianness of the CAN ID (default: :
|
|
23
|
+
# @param [Symbol] endianness The endianness of the CAN ID (default: :native) can be :big, :little, or :native.
|
|
30
24
|
# @return [void]
|
|
31
|
-
def initialize(interface_name:, logger: nil, endianness: :
|
|
25
|
+
def initialize(interface_name:, logger: nil, endianness: :native, can_fd: false, adapter: Adapter::Socketcan)
|
|
32
26
|
@interface_name = interface_name
|
|
33
27
|
@logger = logger || Logger.new($stdout)
|
|
34
28
|
@listening = true # Control flag for listening loop
|
|
35
|
-
@endianness = endianness # :big or :
|
|
29
|
+
@endianness = endianness # :big, :little, or :native
|
|
36
30
|
@can_fd = can_fd
|
|
31
|
+
@adapter = if adapter.is_a?(Class)
|
|
32
|
+
adapter.new(interface_name: interface_name, logger: @logger, endianness: endianness)
|
|
33
|
+
else
|
|
34
|
+
adapter
|
|
35
|
+
end
|
|
37
36
|
end
|
|
38
37
|
|
|
39
38
|
# Sends a CAN message by writing directly to a raw CAN socket
|
|
@@ -47,7 +46,7 @@ module CanMessenger
|
|
|
47
46
|
use_fd = can_fd.nil? ? @can_fd : can_fd
|
|
48
47
|
|
|
49
48
|
with_socket(can_fd: use_fd) do |socket|
|
|
50
|
-
frame = build_can_frame(id: id, data: data, extended_id: extended_id, can_fd: use_fd)
|
|
49
|
+
frame = @adapter.build_can_frame(id: id, data: data, extended_id: extended_id, can_fd: use_fd)
|
|
51
50
|
socket.write(frame)
|
|
52
51
|
end
|
|
53
52
|
rescue ArgumentError
|
|
@@ -86,7 +85,7 @@ module CanMessenger
|
|
|
86
85
|
# - `:id` [Integer] the CAN message ID
|
|
87
86
|
# - `:data` [Array<Integer>] the message data bytes
|
|
88
87
|
# @return [void]
|
|
89
|
-
def start_listening(filter: nil, can_fd: nil, dbc: nil, &
|
|
88
|
+
def start_listening(filter: nil, can_fd: nil, dbc: nil, &)
|
|
90
89
|
return @logger.error("No block provided to handle messages.") unless block_given?
|
|
91
90
|
|
|
92
91
|
@listening = true
|
|
@@ -95,7 +94,7 @@ module CanMessenger
|
|
|
95
94
|
|
|
96
95
|
with_socket(can_fd: use_fd) do |socket|
|
|
97
96
|
@logger.info("Started listening on #{@interface_name}")
|
|
98
|
-
process_message(socket, filter, use_fd, dbc, &
|
|
97
|
+
process_message(socket, filter, use_fd, dbc, &) while @listening
|
|
99
98
|
end
|
|
100
99
|
end
|
|
101
100
|
|
|
@@ -115,7 +114,7 @@ module CanMessenger
|
|
|
115
114
|
# @yield [socket] An open CAN socket.
|
|
116
115
|
# @return [void]
|
|
117
116
|
def with_socket(can_fd: @can_fd)
|
|
118
|
-
socket =
|
|
117
|
+
socket = @adapter.open_socket(can_fd: can_fd)
|
|
119
118
|
return @logger.error("Failed to open socket, cannot continue operation.") if socket.nil?
|
|
120
119
|
|
|
121
120
|
yield socket
|
|
@@ -123,57 +122,6 @@ module CanMessenger
|
|
|
123
122
|
socket&.close
|
|
124
123
|
end
|
|
125
124
|
|
|
126
|
-
# Creates and configures a CAN socket bound to @interface_name.
|
|
127
|
-
#
|
|
128
|
-
# @return [Socket, nil] The configured CAN socket, or nil if the socket cannot be opened.
|
|
129
|
-
def open_can_socket(can_fd: @can_fd) # rubocop:disable Metrics/MethodLength
|
|
130
|
-
socket = Socket.open(Socket::PF_CAN, Socket::SOCK_RAW, Socket::CAN_RAW)
|
|
131
|
-
socket.bind(Socket.pack_sockaddr_can(@interface_name))
|
|
132
|
-
socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, TIMEOUT)
|
|
133
|
-
if can_fd && Socket.const_defined?(:CAN_RAW_FD_FRAMES)
|
|
134
|
-
socket.setsockopt(Socket.const_defined?(:SOL_CAN_RAW) ? Socket::SOL_CAN_RAW : Socket::CAN_RAW,
|
|
135
|
-
Socket::CAN_RAW_FD_FRAMES, 1)
|
|
136
|
-
end
|
|
137
|
-
socket
|
|
138
|
-
rescue StandardError => e
|
|
139
|
-
@logger.error("Error creating CAN socket on interface #{@interface_name}: #{e}")
|
|
140
|
-
nil
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
# Builds a raw CAN or CAN FD frame for SocketCAN.
|
|
144
|
-
#
|
|
145
|
-
# @param id [Integer] the CAN ID
|
|
146
|
-
# @param data [Array<Integer>] data bytes (up to 8 for classic, 64 for CAN FD)
|
|
147
|
-
# @param can_fd [Boolean] whether to build a CAN FD frame
|
|
148
|
-
# @return [String] the packed CAN frame
|
|
149
|
-
def build_can_frame(id:, data:, extended_id: false, can_fd: false) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity
|
|
150
|
-
if can_fd
|
|
151
|
-
raise ArgumentError, "CAN FD data cannot exceed #{MAX_FD_DATA} bytes" if data.size > MAX_FD_DATA
|
|
152
|
-
elsif data.size > 8
|
|
153
|
-
raise ArgumentError, "CAN data cannot exceed 8 bytes"
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
# Mask the ID to 29 bits
|
|
157
|
-
can_id = id & 0x1FFFFFFF
|
|
158
|
-
|
|
159
|
-
# If extended_id == true, set bit 31 (CAN_EFF_FLAG)
|
|
160
|
-
can_id |= 0x80000000 if extended_id
|
|
161
|
-
|
|
162
|
-
# Pack the 4‐byte ID (big-endian or little-endian)
|
|
163
|
-
id_bytes = @endianness == :big ? [can_id].pack("L>") : [can_id].pack("V")
|
|
164
|
-
|
|
165
|
-
# 1 byte for DLC/length, then 3 bytes for flags/reserved
|
|
166
|
-
dlc_and_pad = [data.size, 0, 0, 0].pack("C*")
|
|
167
|
-
|
|
168
|
-
payload = if can_fd
|
|
169
|
-
data.pack("C*").ljust(MAX_FD_DATA, "\x00")
|
|
170
|
-
else
|
|
171
|
-
data.pack("C*").ljust(8, "\x00")
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
id_bytes + dlc_and_pad + payload
|
|
175
|
-
end
|
|
176
|
-
|
|
177
125
|
# Processes a single CAN message from `socket`. Applies filter, yields to block if it matches.
|
|
178
126
|
#
|
|
179
127
|
# @param socket [Socket] The CAN socket.
|
|
@@ -181,7 +129,7 @@ module CanMessenger
|
|
|
181
129
|
# @yield [message] Yields the message if it passes filtering.
|
|
182
130
|
# @return [void]
|
|
183
131
|
def process_message(socket, filter, can_fd, dbc, &block)
|
|
184
|
-
message = receive_message(socket: socket, can_fd: can_fd)
|
|
132
|
+
message = @adapter.receive_message(socket: socket, can_fd: can_fd)
|
|
185
133
|
return if message.nil?
|
|
186
134
|
return if filter && !matches_filter?(message_id: message[:id], filter: filter)
|
|
187
135
|
|
|
@@ -195,68 +143,6 @@ module CanMessenger
|
|
|
195
143
|
@logger.error("Unexpected error in listening loop: #{e.message}")
|
|
196
144
|
end
|
|
197
145
|
|
|
198
|
-
# Reads a frame from the socket and parses it into { id:, data: }, or nil if none is received.
|
|
199
|
-
#
|
|
200
|
-
# @param socket [Socket]
|
|
201
|
-
# @return [Hash, nil]
|
|
202
|
-
def receive_message(socket:, can_fd: false)
|
|
203
|
-
frame_size = can_fd ? CANFD_FRAME_SIZE : FRAME_SIZE
|
|
204
|
-
frame = socket.recv(frame_size)
|
|
205
|
-
return nil if frame.nil? || frame.size < MIN_FRAME_SIZE
|
|
206
|
-
|
|
207
|
-
parse_frame(frame: frame, can_fd: can_fd)
|
|
208
|
-
rescue IO::WaitReadable
|
|
209
|
-
nil
|
|
210
|
-
rescue StandardError => e
|
|
211
|
-
@logger.error("Error receiving CAN message on interface #{@interface_name}: #{e}")
|
|
212
|
-
nil
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
# Parses a raw CAN frame into { id: Integer, data: Array<Integer> }, or nil on error.
|
|
216
|
-
#
|
|
217
|
-
# @param [String] frame
|
|
218
|
-
# @return [Hash, nil]
|
|
219
|
-
def parse_frame(frame:, can_fd: nil) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity
|
|
220
|
-
return nil unless frame && frame.size >= MIN_FRAME_SIZE
|
|
221
|
-
|
|
222
|
-
use_fd = can_fd.nil? ? frame.size >= CANFD_FRAME_SIZE : can_fd
|
|
223
|
-
|
|
224
|
-
raw_id = unpack_frame_id(frame: frame)
|
|
225
|
-
|
|
226
|
-
# Determine if EFF bit is set
|
|
227
|
-
extended = raw_id.anybits?(0x80000000)
|
|
228
|
-
# or raw_id.anybits?(0x80000000) if your Ruby version supports `Integer#anybits?`
|
|
229
|
-
|
|
230
|
-
# Now mask off everything except the lower 29 bits
|
|
231
|
-
id = raw_id & 0x1FFFFFFF
|
|
232
|
-
|
|
233
|
-
data_length = if use_fd
|
|
234
|
-
frame[4].ord
|
|
235
|
-
else
|
|
236
|
-
frame[4].ord & 0x0F
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
# Extract data
|
|
240
|
-
data = if frame.size >= MIN_FRAME_SIZE + data_length
|
|
241
|
-
frame[MIN_FRAME_SIZE, data_length].unpack("C*")
|
|
242
|
-
else
|
|
243
|
-
[]
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
{ id: id, data: data, extended: extended }
|
|
247
|
-
rescue StandardError => e
|
|
248
|
-
@logger.error("Error parsing CAN frame: #{e}")
|
|
249
|
-
nil
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
def unpack_frame_id(frame:)
|
|
253
|
-
if @endianness == :big
|
|
254
|
-
frame[0..3].unpack1("L>")
|
|
255
|
-
else
|
|
256
|
-
frame[0..3].unpack1("V")
|
|
257
|
-
end
|
|
258
|
-
end
|
|
259
|
-
|
|
260
146
|
# Checks whether the given message ID matches the specified filter.
|
|
261
147
|
#
|
|
262
148
|
# @param message_id [Integer] The ID of the incoming CAN message.
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: can_messenger
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 2.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- fk1018
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies: []
|
|
13
12
|
description: CanMessenger provides an interface to send and receive messages over
|
|
14
13
|
the CAN bus, useful for applications requiring CAN communication in Ruby.
|
|
@@ -18,8 +17,12 @@ executables: []
|
|
|
18
17
|
extensions: []
|
|
19
18
|
extra_rdoc_files: []
|
|
20
19
|
files:
|
|
20
|
+
- CHANGELOG.md
|
|
21
|
+
- LICENSE.txt
|
|
21
22
|
- README.md
|
|
22
23
|
- lib/can_messenger.rb
|
|
24
|
+
- lib/can_messenger/adapter/base.rb
|
|
25
|
+
- lib/can_messenger/adapter/socketcan.rb
|
|
23
26
|
- lib/can_messenger/dbc.rb
|
|
24
27
|
- lib/can_messenger/messenger.rb
|
|
25
28
|
- lib/can_messenger/version.rb
|
|
@@ -32,7 +35,6 @@ metadata:
|
|
|
32
35
|
source_code_uri: https://github.com/fk1018/can_messenger
|
|
33
36
|
changelog_uri: https://github.com/fk1018/can_messenger/blob/main/CHANGELOG.md
|
|
34
37
|
rubygems_mfa_required: 'true'
|
|
35
|
-
post_install_message:
|
|
36
38
|
rdoc_options: []
|
|
37
39
|
require_paths:
|
|
38
40
|
- lib
|
|
@@ -40,15 +42,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
40
42
|
requirements:
|
|
41
43
|
- - ">="
|
|
42
44
|
- !ruby/object:Gem::Version
|
|
43
|
-
version:
|
|
45
|
+
version: 4.0.1
|
|
44
46
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
45
47
|
requirements:
|
|
46
48
|
- - ">="
|
|
47
49
|
- !ruby/object:Gem::Version
|
|
48
50
|
version: '0'
|
|
49
51
|
requirements: []
|
|
50
|
-
rubygems_version:
|
|
51
|
-
signing_key:
|
|
52
|
+
rubygems_version: 4.0.3
|
|
52
53
|
specification_version: 4
|
|
53
54
|
summary: A simple Ruby wrapper to read and write CAN bus messages.
|
|
54
55
|
test_files: []
|