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
j1939/name.py
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
|
|
2
|
+
class Name:
|
|
3
|
+
"""The Name of one Controller Application.
|
|
4
|
+
|
|
5
|
+
The Name consists of 64 bit:
|
|
6
|
+
|
|
7
|
+
1-bit Arbitrary Address Capable
|
|
8
|
+
Indicate the capability to solve address conflicts.
|
|
9
|
+
Set to 1 if the device is Arbitrary Address Capable, set to 0 if
|
|
10
|
+
it's Single Address Capable.
|
|
11
|
+
|
|
12
|
+
3-bit Industry Group
|
|
13
|
+
One of the predefined J1939 industry groups.
|
|
14
|
+
|
|
15
|
+
4-bit Vehicle System Instance
|
|
16
|
+
Instance number of a vehicle system to distinguish two or more
|
|
17
|
+
device with the same Vehicle System number in the same J1939
|
|
18
|
+
network.
|
|
19
|
+
The first instance is assigned to the instance number 0.
|
|
20
|
+
|
|
21
|
+
7-bit Vehicle System
|
|
22
|
+
A subcomponent of a vehicle, that includes one or more J1939
|
|
23
|
+
segments and may be connected or disconnected from the vehicle.
|
|
24
|
+
A Vehicle System may be made of one or more functions. The Vehicle
|
|
25
|
+
System depends on the Industry Group definition.
|
|
26
|
+
|
|
27
|
+
1-bit Reserved
|
|
28
|
+
This field is reserved for future use by SAE.
|
|
29
|
+
|
|
30
|
+
8-bit Function
|
|
31
|
+
One of the predefined J1939 functions. The same function value
|
|
32
|
+
(upper 128 only) may mean different things for different Industry
|
|
33
|
+
Groups or Vehicle Systems.
|
|
34
|
+
|
|
35
|
+
5-bit Function Instance
|
|
36
|
+
Instance number of a function to distinguish two or more devices
|
|
37
|
+
with the same function number in the same J1939 network.
|
|
38
|
+
The first instance is assigned to the instance number 0.
|
|
39
|
+
|
|
40
|
+
3-bit ECU Instance
|
|
41
|
+
Identify the ECU instance if multiple ECUs are involved in
|
|
42
|
+
performing a single function. Normally set to 0.
|
|
43
|
+
|
|
44
|
+
11-bit Manufacturer Code
|
|
45
|
+
One of the predefined J1939 manufacturer codes.
|
|
46
|
+
|
|
47
|
+
21-bit Identity Number
|
|
48
|
+
A unique number which identifies the particular device in a
|
|
49
|
+
manufacturer specific way.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
class IndustryGroup:
|
|
53
|
+
Global = 0
|
|
54
|
+
OnHighway = 1
|
|
55
|
+
AgriculturalAndForestry = 2
|
|
56
|
+
Construction = 3
|
|
57
|
+
Marine = 4
|
|
58
|
+
Industrial = 5
|
|
59
|
+
|
|
60
|
+
def __init__(self, **kwargs):
|
|
61
|
+
"""
|
|
62
|
+
:param value:
|
|
63
|
+
64-bit value the address should be extracted from
|
|
64
|
+
|
|
65
|
+
:param bytes:
|
|
66
|
+
Array of 8 bytes containing the name object as binary representation.
|
|
67
|
+
|
|
68
|
+
:param arbitrary_address_capable:
|
|
69
|
+
1-bit Arbitrary Address Capable
|
|
70
|
+
Indicate the capability to solve address conflicts.
|
|
71
|
+
Set to 1 if the device is Arbitrary Address Capable, set to 0 if
|
|
72
|
+
it's Single Address Capable.
|
|
73
|
+
:param industry_group:
|
|
74
|
+
3-bit Industry Group
|
|
75
|
+
One of the predefined J1939 industry groups.
|
|
76
|
+
:param vehicle_system_instance:
|
|
77
|
+
4-bit Vehicle System Instance
|
|
78
|
+
Instance number of a vehicle system to distinguish two or more
|
|
79
|
+
device with the same Vehicle System number in the same J1939
|
|
80
|
+
network.
|
|
81
|
+
The first instance is assigned to the instance number 0.
|
|
82
|
+
:param vehicle_system:
|
|
83
|
+
7-bit Vehicle System
|
|
84
|
+
A subcomponent of a vehicle, that includes one or more J1939
|
|
85
|
+
segments and may be connected or disconnected from the vehicle.
|
|
86
|
+
A Vehicle System may be made of one or more functions. The Vehicle
|
|
87
|
+
System depends on the Industry Group definition.
|
|
88
|
+
:param function:
|
|
89
|
+
8-bit Function
|
|
90
|
+
One of the predefined J1939 functions. The same function value
|
|
91
|
+
(upper 128 only) may mean different things for different Industry
|
|
92
|
+
Groups or Vehicle Systems.
|
|
93
|
+
:param function_instance:
|
|
94
|
+
5-bit Function Instance
|
|
95
|
+
Instance number of a function to distinguish two or more devices
|
|
96
|
+
with the same function number in the same J1939 network.
|
|
97
|
+
The first instance is assigned to the instance number 0.
|
|
98
|
+
:param ecu_instance:
|
|
99
|
+
3-bit ECU Instance
|
|
100
|
+
Identify the ECU instance if multiple ECUs are involved in
|
|
101
|
+
performing a single function. Normally set to 0.
|
|
102
|
+
:param manufacturer_code:
|
|
103
|
+
11-bit Manufacturer Code
|
|
104
|
+
One of the predefined J1939 manufacturer codes.
|
|
105
|
+
:param identity_number:
|
|
106
|
+
21-bit Identity Number
|
|
107
|
+
A unique number which identifies the particular device in a
|
|
108
|
+
manufacturer specific way.
|
|
109
|
+
"""
|
|
110
|
+
if 'value' in kwargs:
|
|
111
|
+
self.value = kwargs['value']
|
|
112
|
+
elif 'bytes' in kwargs:
|
|
113
|
+
self.bytes = kwargs['bytes']
|
|
114
|
+
else:
|
|
115
|
+
self.arbitrary_address_capable = kwargs.get('arbitrary_address_capable', False)
|
|
116
|
+
if (self.arbitrary_address_capable < 0) or (self.arbitrary_address_capable > 1):
|
|
117
|
+
raise ValueError("Length of arbitrary address capable incorrect")
|
|
118
|
+
self.industry_group = kwargs.get('industry_group', Name.IndustryGroup.Global)
|
|
119
|
+
if (self.industry_group < 0) or (self.industry_group > ((2 ** 3) - 1)):
|
|
120
|
+
raise ValueError("Length of industry group incorrect")
|
|
121
|
+
self.vehicle_system_instance = kwargs.get('vehicle_system_instance', 0)
|
|
122
|
+
if (self.vehicle_system_instance < 0) or (self.vehicle_system_instance > ((2 ** 4) - 1)):
|
|
123
|
+
raise ValueError("Length of vehicle system instance incorrect")
|
|
124
|
+
self.vehicle_system = kwargs.get('vehicle_system', 0)
|
|
125
|
+
if (self.vehicle_system < 0) or (self.vehicle_system > ((2 ** 7) - 1)):
|
|
126
|
+
raise ValueError("Length of vehicle system incorrect")
|
|
127
|
+
self.function = kwargs.get('function', 0)
|
|
128
|
+
if (self.function < 0) or (self.function > ((2 ** 8) - 1)):
|
|
129
|
+
raise ValueError("Length of function incorrect")
|
|
130
|
+
self.function_instance = kwargs.get('function_instance', 0)
|
|
131
|
+
if (self.function_instance < 0) or (self.function_instance > ((2 ** 5) - 1)):
|
|
132
|
+
raise ValueError("Length of function instance incorrect")
|
|
133
|
+
self.ecu_instance = kwargs.get('ecu_instance', 0)
|
|
134
|
+
if (self.ecu_instance < 0) or (self.ecu_instance > ((2 ** 3) - 1)):
|
|
135
|
+
raise ValueError("Length of ecu instance incorrect")
|
|
136
|
+
self.manufacturer_code = kwargs.get('manufacturer_code', 0)
|
|
137
|
+
if (self.manufacturer_code < 0) or (self.manufacturer_code > ((2 ** 11) - 1)):
|
|
138
|
+
raise ValueError("Length of manufacturer code incorrect")
|
|
139
|
+
self.identity_number = kwargs.get('identity_number', 0)
|
|
140
|
+
if (self.identity_number < 0) or (self.identity_number > ((2 ** 21) - 1)):
|
|
141
|
+
raise ValueError("Length of identity number incorrect")
|
|
142
|
+
|
|
143
|
+
self.reserved_bit = 0
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def arbitrary_address_capable(self):
|
|
147
|
+
return self.__arbitrary_address_capable
|
|
148
|
+
|
|
149
|
+
@arbitrary_address_capable.setter
|
|
150
|
+
def arbitrary_address_capable(self, value):
|
|
151
|
+
self.__arbitrary_address_capable = value
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def industry_group(self):
|
|
155
|
+
return self.__industry_group
|
|
156
|
+
|
|
157
|
+
@industry_group.setter
|
|
158
|
+
def industry_group(self, value):
|
|
159
|
+
self.__industry_group = value
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def vehicle_system_instance(self):
|
|
163
|
+
return self.__vehicle_system_instance
|
|
164
|
+
|
|
165
|
+
@vehicle_system_instance.setter
|
|
166
|
+
def vehicle_system_instance(self, value):
|
|
167
|
+
self.__vehicle_system_instance = value
|
|
168
|
+
|
|
169
|
+
@property
|
|
170
|
+
def vehicle_system(self):
|
|
171
|
+
return self.__vehicle_system
|
|
172
|
+
|
|
173
|
+
@vehicle_system.setter
|
|
174
|
+
def vehicle_system(self, value):
|
|
175
|
+
self.__vehicle_system = value
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def reserved_bit(self):
|
|
179
|
+
return self.__reserved_bit
|
|
180
|
+
|
|
181
|
+
@reserved_bit.setter
|
|
182
|
+
def reserved_bit(self, value):
|
|
183
|
+
self.__reserved_bit = value
|
|
184
|
+
|
|
185
|
+
@property
|
|
186
|
+
def function(self):
|
|
187
|
+
return self.__function
|
|
188
|
+
|
|
189
|
+
@function.setter
|
|
190
|
+
def function(self, value):
|
|
191
|
+
self.__function = value
|
|
192
|
+
|
|
193
|
+
@property
|
|
194
|
+
def function_instance(self):
|
|
195
|
+
return self.__function_instance
|
|
196
|
+
|
|
197
|
+
@function_instance.setter
|
|
198
|
+
def function_instance(self, value):
|
|
199
|
+
self.__function_instance = value
|
|
200
|
+
|
|
201
|
+
@property
|
|
202
|
+
def ecu_instance(self):
|
|
203
|
+
return self.__ecu_instance
|
|
204
|
+
|
|
205
|
+
@ecu_instance.setter
|
|
206
|
+
def ecu_instance(self, value):
|
|
207
|
+
self.__ecu_instance = value
|
|
208
|
+
|
|
209
|
+
@property
|
|
210
|
+
def manufacturer_code(self):
|
|
211
|
+
return self.__manufacturer_code
|
|
212
|
+
|
|
213
|
+
@manufacturer_code.setter
|
|
214
|
+
def manufacturer_code(self, value):
|
|
215
|
+
self.__manufacturer_code = value
|
|
216
|
+
|
|
217
|
+
@property
|
|
218
|
+
def identity_number(self):
|
|
219
|
+
return self.__identity_number
|
|
220
|
+
|
|
221
|
+
@identity_number.setter
|
|
222
|
+
def identity_number(self, value):
|
|
223
|
+
self.__identity_number = value
|
|
224
|
+
|
|
225
|
+
@property
|
|
226
|
+
def value(self):
|
|
227
|
+
retval = self.identity_number
|
|
228
|
+
retval += (self.manufacturer_code << 21)
|
|
229
|
+
retval += (self.ecu_instance << 32)
|
|
230
|
+
retval += (self.function_instance << 35)
|
|
231
|
+
retval += (self.function << 40)
|
|
232
|
+
retval += (self.reserved_bit << 48)
|
|
233
|
+
retval += (self.vehicle_system << 49)
|
|
234
|
+
retval += (self.vehicle_system_instance << 56)
|
|
235
|
+
retval += (self.industry_group << 60)
|
|
236
|
+
retval += (self.arbitrary_address_capable << 63)
|
|
237
|
+
return retval
|
|
238
|
+
|
|
239
|
+
@value.setter
|
|
240
|
+
def value(self, value):
|
|
241
|
+
self.identity_number = value & ((2 ** 21) - 1)
|
|
242
|
+
self.manufacturer_code = (value >> 21) & ((2 ** 11) - 1)
|
|
243
|
+
self.ecu_instance = (value >> 32) & ((2 ** 3) - 1)
|
|
244
|
+
self.function_instance = (value >> 35) & ((2 ** 5) - 1)
|
|
245
|
+
self.function = (value >> 40) & ((2 ** 8) - 1)
|
|
246
|
+
self.reserved_bit = (value >> 48) & 1
|
|
247
|
+
self.vehicle_system = (value >> 49) & ((2 ** 7) - 1)
|
|
248
|
+
self.vehicle_system_instance = (value >> 56) & ((2 ** 4) - 1)
|
|
249
|
+
self.industry_group = (value >> 60) & ((2 ** 3) - 1)
|
|
250
|
+
self.arbitrary_address_capable = (value >> 63) & 1
|
|
251
|
+
|
|
252
|
+
@property
|
|
253
|
+
def bytes(self):
|
|
254
|
+
"""Get the Name object as 8 Byte Data"""
|
|
255
|
+
return [
|
|
256
|
+
((self.value >> 0) & 0xFF),
|
|
257
|
+
((self.value >> 8) & 0xFF),
|
|
258
|
+
((self.value >> 16) & 0xFF),
|
|
259
|
+
((self.value >> 24) & 0xFF),
|
|
260
|
+
((self.value >> 32) & 0xFF),
|
|
261
|
+
((self.value >> 40) & 0xFF),
|
|
262
|
+
((self.value >> 48) & 0xFF),
|
|
263
|
+
((self.value >> 56) & 0xFF)
|
|
264
|
+
]
|
|
265
|
+
|
|
266
|
+
@bytes.setter
|
|
267
|
+
def bytes(self, value):
|
|
268
|
+
self.value = int.from_bytes(value, byteorder='little', signed=False)
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import j1939
|
|
2
|
+
|
|
3
|
+
class ParameterGroupNumber:
|
|
4
|
+
"""Parameter Group Number (PGN).
|
|
5
|
+
|
|
6
|
+
The PGN are described in SAE J1939/21 and consists of four parts:
|
|
7
|
+
* 1-bit Reserved (sometimes referred to as Extended Data Page)
|
|
8
|
+
* 1-bit Data Page (DP)
|
|
9
|
+
* 8-bit PDU Format (PF)
|
|
10
|
+
* 8-bit PDU Specific (PS)
|
|
11
|
+
|
|
12
|
+
Predefined PGNs are listed in SAE J1939 and SAE J1939/71
|
|
13
|
+
|
|
14
|
+
A PF value from 0 to 239 (PDU1) indicates a destination address (DA) in PS
|
|
15
|
+
(peer-to-peer communication). A PF value from 240 to 255 (PDU2) indicates
|
|
16
|
+
a Group Extension (GE) inside the PS (broadcast message).
|
|
17
|
+
The DA 255 is called the Global Destination Address. It requires all nodes
|
|
18
|
+
to listen to and to respond, if required.
|
|
19
|
+
|
|
20
|
+
TODO: naming/wording: according the standard, a PGN in PDU1 format always
|
|
21
|
+
sets the 8 Bit PS to 0.
|
|
22
|
+
Do we have to separate this object to reflect this rule. And if we
|
|
23
|
+
have to, how to name the other PGN object?
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
class PGN:
|
|
27
|
+
FEFF_MULTI_PG = 9472 # 2500
|
|
28
|
+
FD_TP_CM = 19712 # 4D00
|
|
29
|
+
FD_TP_DT = 19968 # 4E00
|
|
30
|
+
|
|
31
|
+
REQUEST = 59904 # EA00
|
|
32
|
+
#ACKNOWLEDGEMENT = 59392
|
|
33
|
+
ADDRESSCLAIM = 60928 # EE00
|
|
34
|
+
DATATRANSFER = 60160 # EB00
|
|
35
|
+
TP_CM = 60416 # EC00
|
|
36
|
+
#COMMANDED_ADDRESS = 65240
|
|
37
|
+
#PROPRIETARY_A = 61184
|
|
38
|
+
#SOFTWARE_IDENT = 65242
|
|
39
|
+
# Diagnostic messages
|
|
40
|
+
DM01 = 65226 # FECA
|
|
41
|
+
DM02 = 65227 # FECB
|
|
42
|
+
DM03 = 65228 # FECC
|
|
43
|
+
DM04 = 65229 # FECD
|
|
44
|
+
DM05 = 65230 # FECE
|
|
45
|
+
DM06 = 65231 # FECF
|
|
46
|
+
DM07 = 58112 # E300
|
|
47
|
+
DM08 = 65232 # FED0
|
|
48
|
+
DM10 = 65234 # FED2
|
|
49
|
+
DM11 = 65235 # FED3
|
|
50
|
+
DM12 = 65236 # FED4
|
|
51
|
+
DM13 = 57088 # DF00
|
|
52
|
+
DM14 = 55552 # D900
|
|
53
|
+
DM15 = 55296 # D800
|
|
54
|
+
DM16 = 55040 # D700
|
|
55
|
+
DM17 = 54784 # D600
|
|
56
|
+
DM18 = 54272 # D400
|
|
57
|
+
DM19 = 54016 # D300
|
|
58
|
+
DM20 = 49664 # C200
|
|
59
|
+
DM21 = 49408 # C100
|
|
60
|
+
DM22 = 49920 # C300
|
|
61
|
+
DM23 = 64949 # FDB5
|
|
62
|
+
DM24 = 64950 # FDB6
|
|
63
|
+
DM25 = 64951 # FDB7
|
|
64
|
+
DM26 = 64952 # FDB8
|
|
65
|
+
DM27 = 64898 # FD82
|
|
66
|
+
DM28 = 64896 # FD80
|
|
67
|
+
DM29 = 40448 # 9E00
|
|
68
|
+
DM30 = 41984 # A400
|
|
69
|
+
DM31 = 41728 # A300
|
|
70
|
+
DM32 = 41472 # A200
|
|
71
|
+
DM33 = 41216 # A100
|
|
72
|
+
DM34 = 40960 # A000
|
|
73
|
+
DM35 = 40704 # 9F00
|
|
74
|
+
DM36 = 64868 # FD64
|
|
75
|
+
DM37 = 64867 # FD63
|
|
76
|
+
DM38 = 64866 # FD62
|
|
77
|
+
DM39 = 64865 # FD61
|
|
78
|
+
DM40 = 64864 # FD60
|
|
79
|
+
DM41 = 64863 # FD5F
|
|
80
|
+
DM42 = 64862 # FD5E
|
|
81
|
+
DM43 = 64861 # FD5D
|
|
82
|
+
DM44 = 64860 # FD5C
|
|
83
|
+
DM45 = 64859 # FD5B
|
|
84
|
+
DM46 = 64858 # FD5A
|
|
85
|
+
DM47 = 64857 # FD59
|
|
86
|
+
DM48 = 64856 # FD58
|
|
87
|
+
DM49 = 64855 # FD57
|
|
88
|
+
DM50 = 64854 # FD56
|
|
89
|
+
DM51 = 64853 # FD55
|
|
90
|
+
DM52 = 64852 # FD54
|
|
91
|
+
DM53 = 64721 # FCD1
|
|
92
|
+
DM54 = 64722 # FCD2
|
|
93
|
+
DM55 = 64723 # FCD3
|
|
94
|
+
DM56 = 64711 # FCC7
|
|
95
|
+
DM57 = 64710 # FCC6
|
|
96
|
+
|
|
97
|
+
class Address:
|
|
98
|
+
NULL = 254
|
|
99
|
+
GLOBAL = 255
|
|
100
|
+
|
|
101
|
+
def __init__(self, data_page=0, pdu_format=0, pdu_specific=0):
|
|
102
|
+
"""
|
|
103
|
+
:param data_page:
|
|
104
|
+
1-bit Data Page
|
|
105
|
+
:param pdu_format:
|
|
106
|
+
8-bit PDU Format
|
|
107
|
+
:param pdu_specific:
|
|
108
|
+
8-bit PDU Specific
|
|
109
|
+
"""
|
|
110
|
+
self.data_page = data_page & 0x01
|
|
111
|
+
self.pdu_format = pdu_format & 0xFF
|
|
112
|
+
self.pdu_specific = pdu_specific & 0xFF
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def is_pdu1_format(self):
|
|
116
|
+
"""Indicates Peer-to-Peer communication"""
|
|
117
|
+
return True if self.pdu_format>=0 and self.pdu_format<=239 else False
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def is_pdu2_format(self):
|
|
121
|
+
"""Indicates broadcast communication"""
|
|
122
|
+
return True if self.pdu_format>=240 and self.pdu_format<=255 else False
|
|
123
|
+
|
|
124
|
+
def from_message_id(self, mid):
|
|
125
|
+
"""Fills in the object from a MessageId given"""
|
|
126
|
+
if not isinstance(mid, j1939.MessageId):
|
|
127
|
+
raise ValueError("the parameter mid must be an instance of MessageId")
|
|
128
|
+
|
|
129
|
+
self.data_page = (mid.parameter_group_number >> 16) & 0x01
|
|
130
|
+
self.pdu_format = (mid.parameter_group_number >> 8) & 0xFF
|
|
131
|
+
self.pdu_specific = mid.parameter_group_number & 0xFF
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def value(self):
|
|
135
|
+
"""Returns the value of the PGN"""
|
|
136
|
+
return (self.data_page << 16) | (self.pdu_format << 8) | self.pdu_specific
|
j1939/version.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|