-
Notifications
You must be signed in to change notification settings - Fork 68
feat: remove numpy #120
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
khauersp
wants to merge
8
commits into
juergenH87:master
Choose a base branch
from
khauersp:feature/remove-numpy
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
feat: remove numpy #120
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
0aa8c1f
Add claude init
RaulSMS ea76186
feat: support all four DM1 SPN conversion methods
RaulSMS 122be7c
Merge pull request #1 from RaulSMS/claude-helper
RaulSMS f71df59
Merge pull request #2 from RaulSMS/add-DM1-CMs
RaulSMS edb5a5e
feat: remove numpy
khauersp 4a3066c
test: add coverage for j1939_22 logic
khauersp 3403261
test: clean up docs and remove constant
khauersp ba43a25
feat: use constants instead of hardcoded numbers
khauersp File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| # CLAUDE.md | ||
|
|
||
| This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. | ||
|
|
||
| ## Project | ||
|
|
||
| `can-j1939` is a Python implementation of the SAE J1939 protocol stack on top of | ||
| [python-can](https://python-can.readthedocs.org/). It supports both J1939-21 and J1939-22 (J1939-FD) | ||
| data link layers, including transport protocols (BAM, CMDT / RTS-CTS), address claiming, and a | ||
| number of diagnostic messages (DM1, DM11, DM14, DM22). | ||
|
|
||
| ## Common commands | ||
|
|
||
| ```bash | ||
| # Install the package (editable for development) | ||
| pip install -e . | ||
|
|
||
| # Run the full test suite (matches CI) | ||
| pytest . --pyargs | ||
|
|
||
| # Run a single test file / test | ||
| pytest test/test_ecu.py | ||
| pytest test/test_memory_access.py::TestMemoryAccess::test_some_name -v | ||
| ``` | ||
|
|
||
| CI runs `pytest . --pyargs` on Python 3.10 across Ubuntu/macOS/Windows | ||
| (`.github/workflows/CI.yml`). | ||
|
|
||
| ## Architecture | ||
|
|
||
| The stack is layered: an **ECU** owns a **data-link layer** object and one or more | ||
| **ControllerApplications**. Background work runs on a dedicated job thread. | ||
|
|
||
| - `j1939/electronic_control_unit.py` — `ElectronicControlUnit` is the entry point. It owns the | ||
| `can.Bus`, a `MessageListener`, a job thread (`_async_job_thread`) that drives timers and | ||
| transport-protocol timeouts, and a list of subscribers. The `data_link_layer` constructor arg | ||
| (`'j1939-21'` or `'j1939-22'`) selects which DLL is instantiated. The ECU passes the DLL a | ||
| small surface of callbacks: `send_message`, `_job_thread_wakeup`, `_notify_subscribers`, | ||
| `_is_message_acceptable`. For tests, `send_message=` can be injected to bypass real CAN I/O. | ||
| - `j1939/j1939_21.py` and `j1939/j1939_22.py` — the two DLL implementations. They share the | ||
| callback signature above and implement the transport protocols (TP-BAM, TP-CMDT / RTS-CTS, | ||
| and for J1939-22 the FD multi-session variants and Multi-PG / FEFF). Changes that touch | ||
| protocol behaviour usually need parallel updates in both files. | ||
| - `j1939/controller_application.py` — `ControllerApplication` (CA) implements J1939/81 address | ||
| claiming, state machine (`NONE` → `WAITING_VETO` → `NORMAL` / `CANNOT_CLAIM`), per-CA | ||
| subscriptions, and `send_pgn` (which dispatches to the ECU's DLL). | ||
| - `j1939/name.py`, `j1939/parameter_group_number.py`, `j1939/message_id.py` — value objects for | ||
| the J1939 NAME, PGN encoding, and 29-bit CAN identifier framing. | ||
| - `j1939/diagnostic_messages.py`, `j1939/memory_access.py`, `j1939/Dm14Query.py`, | ||
| `j1939/Dm14Server.py`, `j1939/error_info.py` — diagnostic-message support (DM1/DM11/DM14/DM22), | ||
| including the DM14 memory-access client (`Dm14Query`) and server (`Dm14Server`). | ||
| - `j1939/__init__.py` is the public API surface — anything users are expected to import lives | ||
| here. | ||
|
|
||
| ### Threading model | ||
|
|
||
| All I/O and protocol timing flows through the ECU's job thread. The DLL never blocks on I/O | ||
| itself — it enqueues work and calls `_job_thread_wakeup` to nudge the thread. Callbacks | ||
| registered via `ca.subscribe(...)` or `ca.add_timer(...)` run on that job thread, so they | ||
| must not block. | ||
|
|
||
| ### Tests | ||
|
|
||
| - `test/` holds unit tests. `test_helpers/feeder.py` provides the `Feeder` fixture (registered | ||
| in `test_helpers/conftest.py`) which is the standard way to drive the stack from tests: it | ||
| replaces `ElectronicControlUnit.send_message` with a simulated bus, lets the test queue | ||
| expected RX/TX messages and PDUs in order, and asserts that the stack produces the expected | ||
| TX sequence. New protocol-level tests should follow that pattern instead of mocking | ||
| `python-can` directly. | ||
| - `test_helpers/feeder.AcceptAllCA` is a CA subclass with `message_acceptable` overridden to | ||
| accept everything — use it when a test needs to receive peer-to-peer messages without setting | ||
| up a real claim. | ||
|
|
||
| ### Examples | ||
|
|
||
| `examples/` contains runnable scripts mirroring the README quick-start (simple receive, own CA | ||
| producer, transport protocols, multi-PG, diagnostic messages). When adding a new public | ||
| feature, prefer extending an existing example over inventing a new pattern in the docs. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| """Tests for the SAE J1939-73 SPN conversion methods (CM 1, 2, 3, 4).""" | ||
| import pytest | ||
|
|
||
| from j1939.diagnostic_messages import DTC | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("cm", [1, 2, 3, 4]) | ||
| @pytest.mark.parametrize("spn", [0, 123, 456, 0x12345, 0x3FFFF]) | ||
| @pytest.mark.parametrize("fmi", [0, 1, 5, 31]) | ||
| @pytest.mark.parametrize("oc", [0, 1, 42, 127]) | ||
| def test_dtc_round_trip(cm, spn, fmi, oc): | ||
| """Encoded DTC bytes must decode back to the same SPN/FMI/OC/CM.""" | ||
| encoded = DTC(spn=spn, fmi=fmi, oc=oc, cm=cm) | ||
| decoded = DTC(dtc=encoded.dtc, cm=cm) | ||
| assert decoded.spn == spn | ||
| assert decoded.fmi == fmi | ||
| assert decoded.oc == oc | ||
| assert decoded.cm == cm | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("cm,expected_cm_bit", [(1, 1), (2, 1), (3, 1), (4, 0)]) | ||
| def test_cm_bit_on_wire(cm, expected_cm_bit): | ||
| """Only CM 4 has the CM bit cleared; CMs 1/2/3 set it.""" | ||
| d = DTC(spn=1000, fmi=5, oc=3, cm=cm) | ||
| assert ((d.dtc >> 31) & 0x01) == expected_cm_bit | ||
|
|
||
|
|
||
| def test_cm1_byte_layout_matches_reference(): | ||
| """CM 1 layout: b1=SPN[18:11], b2=SPN[10:3], b3=SPN[2:0]|FMI, b4=OC|CM.""" | ||
| spn, fmi, oc = 0x12345, 5, 3 | ||
| d = DTC(spn=spn, fmi=fmi, oc=oc, cm=1) | ||
| b1 = d.dtc & 0xFF | ||
| b2 = (d.dtc >> 8) & 0xFF | ||
| b3 = (d.dtc >> 16) & 0xFF | ||
| b4 = (d.dtc >> 24) & 0xFF | ||
| assert b1 == (spn >> 11) & 0xFF | ||
| assert b2 == (spn >> 3) & 0xFF | ||
| assert b3 == (((spn & 0x07) << 5) | (fmi & 0x1F)) | ||
| assert b4 == ((oc & 0x7F) | 0x80) | ||
|
|
||
|
|
||
| def test_cm2_byte_layout_matches_reference(): | ||
| """CM 2 layout: b1=SPN[10:3], b2=SPN[18:11], b3=SPN[2:0]|FMI, b4=OC|CM.""" | ||
| spn, fmi, oc = 0x12345, 5, 3 | ||
| d = DTC(spn=spn, fmi=fmi, oc=oc, cm=2) | ||
| b1 = d.dtc & 0xFF | ||
| b2 = (d.dtc >> 8) & 0xFF | ||
| b3 = (d.dtc >> 16) & 0xFF | ||
| b4 = (d.dtc >> 24) & 0xFF | ||
| assert b1 == (spn >> 3) & 0xFF | ||
| assert b2 == (spn >> 11) & 0xFF | ||
| assert b3 == (((spn & 0x07) << 5) | (fmi & 0x1F)) | ||
| assert b4 == ((oc & 0x7F) | 0x80) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("cm", [3, 4]) | ||
| def test_cm3_cm4_byte_layout(cm): | ||
| """CM 3/4 layout: SPN packed little-endian in b1/b2 with top 3 bits in b3.""" | ||
| spn, fmi, oc = 0x12345, 5, 3 | ||
| d = DTC(spn=spn, fmi=fmi, oc=oc, cm=cm) | ||
| b1 = d.dtc & 0xFF | ||
| b2 = (d.dtc >> 8) & 0xFF | ||
| b3 = (d.dtc >> 16) & 0xFF | ||
| b4 = (d.dtc >> 24) & 0xFF | ||
| assert b1 == spn & 0xFF | ||
| assert b2 == (spn >> 8) & 0xFF | ||
| assert b3 == ((((spn >> 16) & 0x07) << 5) | (fmi & 0x1F)) | ||
| expected_b4 = oc & 0x7F | ||
| if cm == 3: | ||
| expected_b4 |= 0x80 | ||
| assert b4 == expected_b4 | ||
|
|
||
|
|
||
| def test_invalid_cm_raises(): | ||
| with pytest.raises(ValueError): | ||
| DTC(spn=1, fmi=1, oc=0, cm=5) | ||
| with pytest.raises(ValueError): | ||
| DTC(dtc=0x12345678, cm=0) | ||
|
|
||
|
|
||
| def test_default_cm_is_4(): | ||
| """Backward compatibility: omitting `cm` produces the modern CM 4 layout.""" | ||
| d = DTC(spn=0x12345, fmi=5, oc=3) | ||
| assert d.cm == 4 | ||
| assert ((d.dtc >> 31) & 0x01) == 0 |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for not knowing the answer to these questions already, but i was wondering:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test coverage is a bit lackluster so I'll add some tests on this branch. I believe so, from a quick search it looks like, range, list and the list comprehension to make the data_list functionality was all added back in python 2, it seems they changed a bit with python 3.0 but I believe they should be widely supported. I can't actually find what all versions we support, but it should work for python 3