python-can-j1939 0.1.0__py3-none-any.whl
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.
- j1939/Dm14Query.py +331 -0
- j1939/Dm14Server.py +399 -0
- j1939/__init__.py +11 -0
- j1939/controller_application.py +363 -0
- j1939/diagnostic_messages.py +448 -0
- j1939/electronic_control_unit.py +499 -0
- j1939/error_info.py +93 -0
- j1939/j1939_21.py +543 -0
- j1939/j1939_22.py +845 -0
- j1939/memory_access.py +387 -0
- j1939/message_id.py +51 -0
- j1939/name.py +268 -0
- j1939/parameter_group_number.py +136 -0
- j1939/version.py +1 -0
- python_can_j1939-0.1.0.dist-info/METADATA +325 -0
- python_can_j1939-0.1.0.dist-info/RECORD +19 -0
- python_can_j1939-0.1.0.dist-info/WHEEL +5 -0
- python_can_j1939-0.1.0.dist-info/licenses/LICENSE +22 -0
- python_can_j1939-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-can-j1939
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: SAE J1939 stack implementation (fork of can-j1939 by Juergen Heilgemeir)
|
|
5
|
+
Author: Raul Sainz-Maza, Drew Rife, Grant Allan, Koltan Hauersperger, Mahesh Sharma, Todd Snider, Victor Klueber
|
|
6
|
+
Maintainer: Raul Sainz-Maza
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/RaulSMS/python-can-j1939
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/RaulSMS/python-can-j1939/issues
|
|
10
|
+
Project-URL: Documentation, https://python-can-j1939.readthedocs.io/en/latest/
|
|
11
|
+
Keywords: CAN,SAE,J1939,J1939-FD,J1939-22
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Intended Audience :: Developers
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: python-can>=4.2.0
|
|
24
|
+
Provides-Extra: test
|
|
25
|
+
Requires-Dist: pytest>=6.2.5; extra == "test"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# SAE J1939 for Python
|
|
29
|
+
|
|
30
|
+
[](https://pypi.org/project/python-can-j1939/)
|
|
31
|
+
[](https://python-can-j1939.readthedocs.io/en/latest/)
|
|
32
|
+
|
|
33
|
+
An implementation of the CAN SAE J1939 standard for Python. This is the
|
|
34
|
+
first J1939-22 (J1939-FD) implementation!
|
|
35
|
+
|
|
36
|
+
If you experience a problem or think the stack would not behave
|
|
37
|
+
properly, do not hesitate to open a ticket or write an email.
|
|
38
|
+
Pull Requests (PR) are of course even more welcome!
|
|
39
|
+
|
|
40
|
+
The project uses the
|
|
41
|
+
[python-can](https://python-can.readthedocs.org/en/stable/) package to
|
|
42
|
+
support multiple hardware drivers. At the time of writing the supported
|
|
43
|
+
interfaces are
|
|
44
|
+
|
|
45
|
+
- CAN over Serial
|
|
46
|
+
- CAN over Serial / SLCAN
|
|
47
|
+
- CANalyst-II
|
|
48
|
+
- IXXAT Virtual CAN Interface
|
|
49
|
+
- Kvasers CANLIB
|
|
50
|
+
- NEOVI Interface
|
|
51
|
+
- NI-CAN
|
|
52
|
+
- PCAN Basic API
|
|
53
|
+
- Socketcan
|
|
54
|
+
- SYSTEC interface
|
|
55
|
+
- USB2CAN Interface
|
|
56
|
+
- Vector
|
|
57
|
+
- Virtual
|
|
58
|
+
- isCAN
|
|
59
|
+
|
|
60
|
+
## Overview
|
|
61
|
+
|
|
62
|
+
An SAE J1939 CAN Network consists of multiple Electronic Control Units
|
|
63
|
+
(ECUs). Each ECU can have one or more Controller Applications (CAs).
|
|
64
|
+
Each CA has its own (unique) Address on the bus. This address is either
|
|
65
|
+
acquired within the address claiming procedure or set to a fixed value.
|
|
66
|
+
In the latter case, the CA has to announce its address to the bus to
|
|
67
|
+
check whether it is free.
|
|
68
|
+
|
|
69
|
+
The CAN messages in a SAE J1939 network are called Protocol Data Units
|
|
70
|
+
(PDUs). This definition is not completely correct, but close enough to
|
|
71
|
+
think of PDUs as the CAN messages.
|
|
72
|
+
|
|
73
|
+
## Features
|
|
74
|
+
|
|
75
|
+
- one ElectronicControlUnit (ECU) can hold multiple
|
|
76
|
+
ControllerApplications (CA)
|
|
77
|
+
- ECU (CA) Naming according SAE J1939/81
|
|
78
|
+
- full featured address claiming procedure according SAE J1939/81
|
|
79
|
+
- full support of transport protocol (up to 1785 bytes) according SAE
|
|
80
|
+
J1939/21 for sending and receiving
|
|
81
|
+
- Connection Mode Data Transfers (CMDT)
|
|
82
|
+
- Broadcast Announce Message (BAM)
|
|
83
|
+
- support of Multi-PG according SAE J1939/22
|
|
84
|
+
- currently FEFF (Flexible Data Rate Extended Frame Format)
|
|
85
|
+
supported only
|
|
86
|
+
- full support of fd-transport protocol according SAE J1939/22
|
|
87
|
+
(J1939-FD) for sending and receiving
|
|
88
|
+
- RTS/CTS (Destination Specific) Transfer with up to 8 concurrent
|
|
89
|
+
sessions and up to 16777215 bytes of data per session
|
|
90
|
+
- Broadcast Announce Message (BAM) with up to 4 concurrent
|
|
91
|
+
sessions and up to 15300 bytes of data per session
|
|
92
|
+
- Requests (global and specific)
|
|
93
|
+
- correct timeout and deadline handling
|
|
94
|
+
- (under construction) almost complete testcoverage
|
|
95
|
+
- diagnostic messages (see
|
|
96
|
+
<https://github.com/RaulSMS/python-can-j1939/tree/master/examples/diagnostic_message.py>)
|
|
97
|
+
- support of DM1 Tool and ECU functionaliy (all four SAE J1939-73
|
|
98
|
+
SPN conversion methods: 1, 2, 3, 4)
|
|
99
|
+
- support of DM11 Tool functionaliy
|
|
100
|
+
- support of DM22 Tool functionaliy
|
|
101
|
+
|
|
102
|
+
## Installation
|
|
103
|
+
|
|
104
|
+
Requires **Python 3.10 or later** and python-can_ >= 4.2.0.
|
|
105
|
+
|
|
106
|
+
Install python-can-j1939 with pip:
|
|
107
|
+
|
|
108
|
+
pip install python-can-j1939
|
|
109
|
+
|
|
110
|
+
or do the trick with:
|
|
111
|
+
|
|
112
|
+
git clone https://github.com/RaulSMS/python-can-j1939.git
|
|
113
|
+
cd python-can-j1939
|
|
114
|
+
pip install .
|
|
115
|
+
|
|
116
|
+
## Upgrade
|
|
117
|
+
|
|
118
|
+
Upgrade an already installed python-can-j1939 package:
|
|
119
|
+
|
|
120
|
+
pip install --upgrade python-can-j1939
|
|
121
|
+
|
|
122
|
+
## Quick start
|
|
123
|
+
|
|
124
|
+
To simply receive all passing (public) messages on the bus you can
|
|
125
|
+
subscribe to the ECU object.
|
|
126
|
+
|
|
127
|
+
``` python
|
|
128
|
+
import logging
|
|
129
|
+
import time
|
|
130
|
+
import can
|
|
131
|
+
import j1939
|
|
132
|
+
|
|
133
|
+
logging.getLogger('j1939').setLevel(logging.DEBUG)
|
|
134
|
+
logging.getLogger('can').setLevel(logging.DEBUG)
|
|
135
|
+
|
|
136
|
+
def on_message(priority, pgn, sa, timestamp, data):
|
|
137
|
+
"""Receive incoming messages from the bus
|
|
138
|
+
|
|
139
|
+
:param int priority:
|
|
140
|
+
Priority of the message
|
|
141
|
+
:param int pgn:
|
|
142
|
+
Parameter Group Number of the message
|
|
143
|
+
:param int sa:
|
|
144
|
+
Source Address of the message
|
|
145
|
+
:param int timestamp:
|
|
146
|
+
Timestamp of the message
|
|
147
|
+
:param bytearray data:
|
|
148
|
+
Data of the PDU
|
|
149
|
+
"""
|
|
150
|
+
print("PGN {} length {}".format(pgn, len(data)))
|
|
151
|
+
|
|
152
|
+
def main():
|
|
153
|
+
print("Initializing")
|
|
154
|
+
|
|
155
|
+
# create the ElectronicControlUnit (one ECU can hold multiple ControllerApplications)
|
|
156
|
+
ecu = j1939.ElectronicControlUnit()
|
|
157
|
+
|
|
158
|
+
# Connect to the CAN bus
|
|
159
|
+
# Arguments are passed to python-can's can.interface.Bus() constructor
|
|
160
|
+
# (see https://python-can.readthedocs.io/en/stable/bus.html).
|
|
161
|
+
# ecu.connect(bustype='socketcan', channel='can0')
|
|
162
|
+
# ecu.connect(bustype='kvaser', channel=0, bitrate=250000)
|
|
163
|
+
ecu.connect(bustype='pcan', channel='PCAN_USBBUS1', bitrate=250000)
|
|
164
|
+
# ecu.connect(bustype='ixxat', channel=0, bitrate=250000)
|
|
165
|
+
# ecu.connect(bustype='vector', app_name='CANalyzer', channel=0, bitrate=250000)
|
|
166
|
+
# ecu.connect(bustype='nican', channel='CAN0', bitrate=250000)
|
|
167
|
+
|
|
168
|
+
# subscribe to all (global) messages on the bus
|
|
169
|
+
ecu.subscribe(on_message)
|
|
170
|
+
|
|
171
|
+
time.sleep(120)
|
|
172
|
+
|
|
173
|
+
print("Deinitializing")
|
|
174
|
+
ecu.disconnect()
|
|
175
|
+
|
|
176
|
+
if __name__ == '__main__':
|
|
177
|
+
main()
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
A more sophisticated example in which the CA class was overloaded to
|
|
181
|
+
include its own functionality:
|
|
182
|
+
|
|
183
|
+
``` python
|
|
184
|
+
import logging
|
|
185
|
+
import time
|
|
186
|
+
import can
|
|
187
|
+
import j1939
|
|
188
|
+
|
|
189
|
+
logging.getLogger('j1939').setLevel(logging.DEBUG)
|
|
190
|
+
logging.getLogger('can').setLevel(logging.DEBUG)
|
|
191
|
+
|
|
192
|
+
# compose the name descriptor for the new ca
|
|
193
|
+
name = j1939.Name(
|
|
194
|
+
arbitrary_address_capable=0,
|
|
195
|
+
industry_group=j1939.Name.IndustryGroup.Industrial,
|
|
196
|
+
vehicle_system_instance=1,
|
|
197
|
+
vehicle_system=1,
|
|
198
|
+
function=1,
|
|
199
|
+
function_instance=1,
|
|
200
|
+
ecu_instance=1,
|
|
201
|
+
manufacturer_code=666,
|
|
202
|
+
identity_number=1234567
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
# create the ControllerApplications
|
|
206
|
+
ca = j1939.ControllerApplication(name, 128)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def ca_receive(priority, pgn, source, timestamp, data):
|
|
210
|
+
"""Feed incoming message to this CA.
|
|
211
|
+
(OVERLOADED function)
|
|
212
|
+
:param int priority:
|
|
213
|
+
Priority of the message
|
|
214
|
+
:param int pgn:
|
|
215
|
+
Parameter Group Number of the message
|
|
216
|
+
:param intsa:
|
|
217
|
+
Source Address of the message
|
|
218
|
+
:param int timestamp:
|
|
219
|
+
Timestamp of the message
|
|
220
|
+
:param bytearray data:
|
|
221
|
+
Data of the PDU
|
|
222
|
+
"""
|
|
223
|
+
print("PGN {} length {}".format(pgn, len(data)))
|
|
224
|
+
|
|
225
|
+
def ca_timer_callback1(cookie):
|
|
226
|
+
"""Callback for sending messages
|
|
227
|
+
|
|
228
|
+
This callback is registered at the ECU timer event mechanism to be
|
|
229
|
+
executed every 500ms.
|
|
230
|
+
|
|
231
|
+
:param cookie:
|
|
232
|
+
A cookie registered at 'add_timer'. May be None.
|
|
233
|
+
"""
|
|
234
|
+
# wait until we have our device_address
|
|
235
|
+
if ca.state != j1939.ControllerApplication.State.NORMAL:
|
|
236
|
+
# returning true keeps the timer event active
|
|
237
|
+
return True
|
|
238
|
+
|
|
239
|
+
# create data with 8 bytes
|
|
240
|
+
data = [j1939.ControllerApplication.FieldValue.NOT_AVAILABLE_8] * 8
|
|
241
|
+
|
|
242
|
+
# sending normal broadcast message
|
|
243
|
+
ca.send_pgn(0, 0xFD, 0xED, 6, data)
|
|
244
|
+
|
|
245
|
+
# sending normal peer-to-peer message, destintion address is 0x04
|
|
246
|
+
ca.send_pgn(0, 0xE0, 0x04, 6, data)
|
|
247
|
+
|
|
248
|
+
# returning true keeps the timer event active
|
|
249
|
+
return True
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def ca_timer_callback2(cookie):
|
|
253
|
+
"""Callback for sending messages
|
|
254
|
+
|
|
255
|
+
This callback is registered at the ECU timer event mechanism to be
|
|
256
|
+
executed every 500ms.
|
|
257
|
+
|
|
258
|
+
:param cookie:
|
|
259
|
+
A cookie registered at 'add_timer'. May be None.
|
|
260
|
+
"""
|
|
261
|
+
# wait until we have our device_address
|
|
262
|
+
if ca.state != j1939.ControllerApplication.State.NORMAL:
|
|
263
|
+
# returning true keeps the timer event active
|
|
264
|
+
return True
|
|
265
|
+
|
|
266
|
+
# create data with 100 bytes
|
|
267
|
+
data = [j1939.ControllerApplication.FieldValue.NOT_AVAILABLE_8] * 100
|
|
268
|
+
|
|
269
|
+
# sending multipacket message with TP-BAM
|
|
270
|
+
ca.send_pgn(0, 0xFE, 0xF6, 6, data)
|
|
271
|
+
|
|
272
|
+
# sending multipacket message with TP-CMDT, destination address is 0x05
|
|
273
|
+
ca.send_pgn(0, 0xD0, 0x05, 6, data)
|
|
274
|
+
|
|
275
|
+
# returning true keeps the timer event active
|
|
276
|
+
return True
|
|
277
|
+
|
|
278
|
+
def main():
|
|
279
|
+
print("Initializing")
|
|
280
|
+
|
|
281
|
+
# create the ElectronicControlUnit (one ECU can hold multiple ControllerApplications)
|
|
282
|
+
ecu = j1939.ElectronicControlUnit()
|
|
283
|
+
|
|
284
|
+
# Connect to the CAN bus
|
|
285
|
+
# Arguments are passed to python-can's can.interface.Bus() constructor
|
|
286
|
+
# (see https://python-can.readthedocs.io/en/stable/bus.html).
|
|
287
|
+
# ecu.connect(bustype='socketcan', channel='can0')
|
|
288
|
+
# ecu.connect(bustype='kvaser', channel=0, bitrate=250000)
|
|
289
|
+
ecu.connect(bustype='pcan', channel='PCAN_USBBUS1', bitrate=250000)
|
|
290
|
+
# ecu.connect(bustype='ixxat', channel=0, bitrate=250000)
|
|
291
|
+
# ecu.connect(bustype='vector', app_name='CANalyzer', channel=0, bitrate=250000)
|
|
292
|
+
# ecu.connect(bustype='nican', channel='CAN0', bitrate=250000)
|
|
293
|
+
# ecu.connect('testchannel_1', bustype='virtual')
|
|
294
|
+
|
|
295
|
+
# add CA to the ECU
|
|
296
|
+
ecu.add_ca(controller_application=ca)
|
|
297
|
+
ca.subscribe(ca_receive)
|
|
298
|
+
# callback every 0.5s
|
|
299
|
+
ca.add_timer(0.500, ca_timer_callback1)
|
|
300
|
+
# callback every 5s
|
|
301
|
+
ca.add_timer(5, ca_timer_callback2)
|
|
302
|
+
# by starting the CA it starts the address claiming procedure on the bus
|
|
303
|
+
ca.start()
|
|
304
|
+
|
|
305
|
+
time.sleep(120)
|
|
306
|
+
|
|
307
|
+
print("Deinitializing")
|
|
308
|
+
ca.stop()
|
|
309
|
+
ecu.disconnect()
|
|
310
|
+
|
|
311
|
+
if __name__ == '__main__':
|
|
312
|
+
main()
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## Credits
|
|
316
|
+
|
|
317
|
+
This package is a fork of
|
|
318
|
+
[can-j1939](https://github.com/juergenH87/python-can-j1939) by Juergen
|
|
319
|
+
Heilgemeir, who greatly extended the original work and added J1939-22
|
|
320
|
+
(J1939-FD) support.
|
|
321
|
+
|
|
322
|
+
The original implementation was taken from
|
|
323
|
+
<https://github.com/benkfra/j1939> by Frank Benkert.
|
|
324
|
+
|
|
325
|
+
Thanks to all contributors for their great work!
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
j1939/Dm14Query.py,sha256=1SUGtwd23w56V0ZOxQs7KCHhff7GdMxGbPwEdc8PhcM,12226
|
|
2
|
+
j1939/Dm14Server.py,sha256=ftOtW1J6ix5cIbrtUZZcAwtkvb7Trk5jeEPQBDXvyI4,13838
|
|
3
|
+
j1939/__init__.py,sha256=Jr9uygL7C57iAgD0HHtHBn4onzAyEEJkpiQ2g6f8fv4,405
|
|
4
|
+
j1939/controller_application.py,sha256=wkDDZXqB5H6Z8ZWu-LqJhWzfCW4kX6mUBwobFeabBuI,16310
|
|
5
|
+
j1939/diagnostic_messages.py,sha256=HjzZ-dT_qMW7sLqYQXutDKBnHrPY_u2XP7J-sqmM9yo,15179
|
|
6
|
+
j1939/electronic_control_unit.py,sha256=0sYfpbf1GCwHl0U4ehw_evIE5qRL5rYxXV7yzJdG4G8,19649
|
|
7
|
+
j1939/error_info.py,sha256=oMT8QJUsPnXym3elmyHl4Hxt9ObXw1pxMTqBK1uVlGM,4154
|
|
8
|
+
j1939/j1939_21.py,sha256=Nt7yjdXuwtLxBD_KjJFLzB3yA6hCJLzZdomOQckqUHM,28526
|
|
9
|
+
j1939/j1939_22.py,sha256=yFiWa5dgfpLRd3-WD6p9VAVys-9392zsBmrLxIq6ApQ,44083
|
|
10
|
+
j1939/memory_access.py,sha256=2pHyjJsGXCUirKamPmItnhpYhrOnbUgmWdPV9tXxXyI,15304
|
|
11
|
+
j1939/message_id.py,sha256=AGU-sg74lqmqxlZFNNHjOYyz_jtASDY5kT76yb82GGU,1839
|
|
12
|
+
j1939/name.py,sha256=x-Tr6I2MUTChEf3C9BCQQe9qnvMzm5pseqRlnUr_OVI,10322
|
|
13
|
+
j1939/parameter_group_number.py,sha256=Q1U-GZuckYhQiIxAOjP1LGYDzQMla0nb232FQS5E9Jw,5323
|
|
14
|
+
j1939/version.py,sha256=Pru0BlFBASFCFo7McHdohtKkUtgMPDwbGfyUZlE2_Vw,21
|
|
15
|
+
python_can_j1939-0.1.0.dist-info/licenses/LICENSE,sha256=A5rtfN-Go8YRj8h812jydwJEPT6NIC1rLVQtHfVyOTs,1130
|
|
16
|
+
python_can_j1939-0.1.0.dist-info/METADATA,sha256=ccoR5VMWJjP3wlGCdgSZeugLPi1RaZpV8bWNMdaxWdQ,10455
|
|
17
|
+
python_can_j1939-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
18
|
+
python_can_j1939-0.1.0.dist-info/top_level.txt,sha256=DuSapIvGxz2GN_YXQAigYWEn-nqGoxEfJL12LrvTsrA,6
|
|
19
|
+
python_can_j1939-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 Frank Benkert (opensource@frank-benkert.de)
|
|
4
|
+
Copyright (c) 2021 juergenH87
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
j1939
|