canmatrix 1.2__tar.gz → 1.3.2__tar.gz
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.
- canmatrix-1.3.2/MANIFEST.in +2 -0
- canmatrix-1.3.2/PKG-INFO +135 -0
- canmatrix-1.3.2/README.md +65 -0
- canmatrix-1.3.2/pyproject.toml +92 -0
- canmatrix-1.3.2/setup.cfg +4 -0
- canmatrix-1.3.2/src/canmatrix/ArbitrationId.py +255 -0
- canmatrix-1.3.2/src/canmatrix/AutosarE2EProperties.py +36 -0
- canmatrix-1.3.2/src/canmatrix/AutosarSecOCProperties.py +46 -0
- canmatrix-1.3.2/src/canmatrix/DecodedSignal.py +61 -0
- canmatrix-1.3.2/src/canmatrix/Define.py +108 -0
- canmatrix-1.3.2/src/canmatrix/Ecu.py +83 -0
- canmatrix-1.3.2/src/canmatrix/Endpoint.py +47 -0
- canmatrix-1.3.2/src/canmatrix/FloatFactory.py +55 -0
- canmatrix-1.3.2/src/canmatrix/Frame.py +882 -0
- canmatrix-1.3.2/src/canmatrix/Pdu.py +112 -0
- canmatrix-1.3.2/src/canmatrix/Signal.py +416 -0
- canmatrix-1.3.2/src/canmatrix/SignalGroup.py +78 -0
- canmatrix-1.3.2/src/canmatrix/__init__.py +10 -0
- canmatrix-1.3.2/src/canmatrix/_canmatrix.py +793 -0
- canmatrix-1.3.2/src/canmatrix/canmatrix.py +22 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/cli/convert.py +10 -5
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/compare.py +13 -9
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/convert.py +7 -4
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/copy.py +7 -7
- canmatrix-1.3.2/src/canmatrix/exceptions.py +61 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/formats/__init__.py +12 -5
- canmatrix-1.2/src/canmatrix/formats/json.py → canmatrix-1.3.2/src/canmatrix/formats/_dict_codec.py +112 -75
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/formats/arxml.py +658 -231
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/formats/dbc.py +50 -26
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/formats/dbf.py +19 -17
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/formats/eds.py +32 -27
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/formats/fibex.py +265 -58
- canmatrix-1.3.2/src/canmatrix/formats/json.py +65 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/formats/kcd.py +16 -14
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/formats/ldf.py +8 -4
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/formats/odx.py +14 -9
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/formats/sym.py +9 -10
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/formats/xls.py +18 -17
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/formats/xls_common.py +4 -4
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/formats/xlsx.py +16 -15
- canmatrix-1.3.2/src/canmatrix/formats/yaml.py +53 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/j1939_decoder.py +13 -9
- canmatrix-1.3.2/src/canmatrix/py.typed +0 -0
- canmatrix-1.3.2/src/canmatrix/utils.py +249 -0
- canmatrix-1.3.2/src/canmatrix.egg-info/PKG-INFO +135 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix.egg-info/SOURCES.txt +33 -5
- canmatrix-1.3.2/tests/__init__.py +0 -0
- canmatrix-1.3.2/tests/createTestFdMatrix.py +52 -0
- canmatrix-1.3.2/tests/createTestMatrix.py +82 -0
- canmatrix-1.3.2/tests/test_canmatrix.py +291 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/tests/test_cli_compare.py +7 -3
- {canmatrix-1.2 → canmatrix-1.3.2}/tests/test_cli_convert.py +45 -10
- {canmatrix-1.2 → canmatrix-1.3.2}/tests/test_codec.py +2 -1
- {canmatrix-1.2 → canmatrix-1.3.2}/tests/test_copy.py +32 -27
- {canmatrix-1.2 → canmatrix-1.3.2}/tests/test_dbc.py +79 -19
- {canmatrix-1.2 → canmatrix-1.3.2}/tests/test_dbf.py +2 -1
- canmatrix-1.3.2/tests/test_formats.py +57 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/tests/test_frame_decoding.py +36 -30
- {canmatrix-1.2 → canmatrix-1.3.2}/tests/test_frame_encoding.py +5 -4
- {canmatrix-1.2 → canmatrix-1.3.2}/tests/test_j1939_decoder.py +11 -5
- {canmatrix-1.2 → canmatrix-1.3.2}/tests/test_json.py +25 -22
- canmatrix-1.3.2/tests/test_scapy.py +54 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/tests/test_sym.py +6 -4
- {canmatrix-1.2 → canmatrix-1.3.2}/tests/test_wireshark.py +13 -9
- canmatrix-1.3.2/tests/test_yaml.py +151 -0
- canmatrix-1.3.2/tests/unit/__init__.py +0 -0
- canmatrix-1.3.2/tests/unit/test_ArbitrationId.py +166 -0
- canmatrix-1.3.2/tests/unit/test_DecodedSignal.py +56 -0
- canmatrix-1.3.2/tests/unit/test_Define.py +117 -0
- canmatrix-1.3.2/tests/unit/test_Ecu.py +64 -0
- canmatrix-1.3.2/tests/unit/test_Endpoint.py +38 -0
- canmatrix-1.3.2/tests/unit/test_Frame.py +363 -0
- canmatrix-1.3.2/tests/unit/test_Pdu.py +75 -0
- canmatrix-1.3.2/tests/unit/test_Signal.py +368 -0
- canmatrix-1.3.2/tests/unit/test_SignalGroup.py +107 -0
- canmatrix-1.3.2/tests/unit/test_canmatrixIsClass.py +31 -0
- canmatrix-1.2/MANIFEST.in +0 -3
- canmatrix-1.2/PKG-INFO +0 -95
- canmatrix-1.2/README.md +0 -28
- canmatrix-1.2/setup.cfg +0 -11
- canmatrix-1.2/setup.py +0 -125
- canmatrix-1.2/src/canmatrix/__init__.py +0 -34
- canmatrix-1.2/src/canmatrix/_version.py +0 -21
- canmatrix-1.2/src/canmatrix/canmatrix.py +0 -2478
- canmatrix-1.2/src/canmatrix/formats/yaml.py +0 -134
- canmatrix-1.2/src/canmatrix/utils.py +0 -136
- canmatrix-1.2/src/canmatrix.egg-info/PKG-INFO +0 -95
- canmatrix-1.2/tests/test_canmatrix.py +0 -1144
- canmatrix-1.2/tests/test_formats.py +0 -26
- canmatrix-1.2/tests/test_scapy.py +0 -50
- canmatrix-1.2/versioneer.py +0 -1819
- {canmatrix-1.2 → canmatrix-1.3.2}/LICENSE +0 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/cancluster.py +0 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/cli/__init__.py +0 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/cli/compare.py +0 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/formats/csv.py +0 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/formats/scapy.py +0 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/formats/wireshark.py +0 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/j1939.dbc +0 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/join.py +0 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/log.py +0 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix/types.py +0 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix.egg-info/dependency_links.txt +0 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix.egg-info/entry_points.txt +0 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix.egg-info/requires.txt +0 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/src/canmatrix.egg-info/top_level.txt +0 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/tests/test.py +0 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/tests/test_arxml.py +0 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/tests/test_arxml_gw.py +0 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/tests/test_utils.py +0 -0
- {canmatrix-1.2 → canmatrix-1.3.2}/tests/test_xls.py +0 -0
canmatrix-1.3.2/PKG-INFO
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: canmatrix
|
|
3
|
+
Version: 1.3.2
|
|
4
|
+
Summary: Automotive Communication Matrix
|
|
5
|
+
Author-email: Eduard Bröcker <eduard@gmx.de>
|
|
6
|
+
Maintainer: canmatrix contributors
|
|
7
|
+
License: BSD-2-Clause
|
|
8
|
+
Project-URL: homepage, https://github.com/ebroecker/canmatrix
|
|
9
|
+
Project-URL: documentation, https://canmatrix.readthedocs.io
|
|
10
|
+
Project-URL: repository, https://github.com/ebroecker/canmatrix
|
|
11
|
+
Keywords: CAN,canbus,dbc,arxml,kcd,dbf,sym,fibex,eds,automotive
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Education
|
|
16
|
+
Classifier: Intended Audience :: Information Technology
|
|
17
|
+
Classifier: Intended Audience :: Manufacturing
|
|
18
|
+
Classifier: Intended Audience :: Telecommunications Industry
|
|
19
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
20
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
21
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
22
|
+
Classifier: Operating System :: MacOS
|
|
23
|
+
Classifier: Programming Language :: Python
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
27
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
28
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
29
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
30
|
+
Classifier: Topic :: Software Development :: Embedded Systems :: Controller Area Network (CAN)
|
|
31
|
+
Classifier: Topic :: System :: Networking
|
|
32
|
+
Classifier: Topic :: Utilities
|
|
33
|
+
Requires-Python: >=3.8
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
License-File: LICENSE
|
|
36
|
+
Requires-Dist: attrs>=19.2.0
|
|
37
|
+
Requires-Dist: click
|
|
38
|
+
Requires-Dist: importlib-metadata; python_version < "3.8"
|
|
39
|
+
Requires-Dist: typing; python_version < "3.5"
|
|
40
|
+
Provides-Extra: arxml
|
|
41
|
+
Requires-Dist: lxml; extra == "arxml"
|
|
42
|
+
Provides-Extra: csv
|
|
43
|
+
Provides-Extra: dbc
|
|
44
|
+
Provides-Extra: dbf
|
|
45
|
+
Provides-Extra: fibex
|
|
46
|
+
Requires-Dist: lxml; extra == "fibex"
|
|
47
|
+
Provides-Extra: json
|
|
48
|
+
Provides-Extra: kcd
|
|
49
|
+
Requires-Dist: lxml; extra == "kcd"
|
|
50
|
+
Provides-Extra: ldf
|
|
51
|
+
Requires-Dist: ldfparser; extra == "ldf"
|
|
52
|
+
Provides-Extra: odx
|
|
53
|
+
Requires-Dist: lxml; extra == "odx"
|
|
54
|
+
Provides-Extra: scapy
|
|
55
|
+
Provides-Extra: sym
|
|
56
|
+
Provides-Extra: test
|
|
57
|
+
Requires-Dist: pathlib2; python_version < "3.4" and extra == "test"
|
|
58
|
+
Requires-Dist: pytest; extra == "test"
|
|
59
|
+
Provides-Extra: wireshark
|
|
60
|
+
Provides-Extra: xls
|
|
61
|
+
Requires-Dist: xlrd==1.2.0; extra == "xls"
|
|
62
|
+
Requires-Dist: xlwt; extra == "xls"
|
|
63
|
+
Provides-Extra: xlsx
|
|
64
|
+
Requires-Dist: openpyxl; extra == "xlsx"
|
|
65
|
+
Provides-Extra: yaml
|
|
66
|
+
Requires-Dist: pyyaml; extra == "yaml"
|
|
67
|
+
Provides-Extra: eds
|
|
68
|
+
Requires-Dist: canopen; extra == "eds"
|
|
69
|
+
Dynamic: license-file
|
|
70
|
+
|
|
71
|
+
## Canmatrix is a python package to read and write several CAN (Controller Area Network) database formats.
|
|
72
|
+
|
|
73
|
+
[](https://pypi.org/project/canmatrix/)
|
|
74
|
+
[](https://pypi.org/project/canmatrix/)
|
|
75
|
+
[](https://coveralls.io/github/ebroecker/canmatrix?branch=development)
|
|
76
|
+
[](https://github.com/ebroecker/canmatrix/issues)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
## About
|
|
80
|
+
|
|
81
|
+
***Canmatrix*** implements a "Python Can Matrix Object" which describes the can-communication and the needed objects (Boardunits, Frames, Signals, Values, ...)
|
|
82
|
+
***Canmatrix*** also includes two **Tools** (canconvert and cancompare) for converting and comparing **CAN** databases.
|
|
83
|
+
|
|
84
|
+
- Multiple formats automotive related communication matrix file parsing.
|
|
85
|
+
- CAN message encoding and decoding.
|
|
86
|
+
|
|
87
|
+
**Supported file formats for import:**
|
|
88
|
+
|
|
89
|
+
.dbc candb / Vector
|
|
90
|
+
.dbf Busmaster (open source!)
|
|
91
|
+
.kcd kayak (open source!)
|
|
92
|
+
.arxml autosar system description
|
|
93
|
+
.yaml structured export/import (same structure as json)
|
|
94
|
+
.xls(x) excel xls-import, works with .xls-file generated by this lib
|
|
95
|
+
.sym peak pcan can description
|
|
96
|
+
.xml (fibex or CANopen eds)
|
|
97
|
+
.ldf (lin bus)
|
|
98
|
+
.odx (diagnostic file)
|
|
99
|
+
.eds
|
|
100
|
+
|
|
101
|
+
**Supported file formats for export:**
|
|
102
|
+
|
|
103
|
+
.dbc
|
|
104
|
+
.dbf
|
|
105
|
+
.kcd
|
|
106
|
+
.xls(x)
|
|
107
|
+
.json Canard (open source!)
|
|
108
|
+
.arxml (very basic implementation)
|
|
109
|
+
.yaml structured export/import (same structure as json)
|
|
110
|
+
.sym
|
|
111
|
+
.xml (fibex)
|
|
112
|
+
.lua (wireshark script)
|
|
113
|
+
.scapy
|
|
114
|
+
|
|
115
|
+
Project Homepage: https://github.com/ebroecker/canmatrix
|
|
116
|
+
|
|
117
|
+
Documentation: https://canmatrix.readthedocs.io
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
## Installation
|
|
121
|
+
|
|
122
|
+
https://canmatrix.readthedocs.io/en/latest/installation.html
|
|
123
|
+
|
|
124
|
+
[Chinese Translation / 安装中文方法解释及注意事项](https://github.com/ebroecker/canmatrix/wiki/%E5%AE%89%E8%A3%85%E4%B8%AD%E6%96%87%E6%96%B9%E6%B3%95%E8%A7%A3%E9%87%8A%E5%8F%8A%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
## Contributing
|
|
128
|
+
|
|
129
|
+
Source Code, Documentation, Examples, Report issues and Any other contributions are **extremely appreciated**.
|
|
130
|
+
|
|
131
|
+
1. Fork the Project
|
|
132
|
+
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
|
|
133
|
+
3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
|
|
134
|
+
4. Push to the Branch (`git push origin feature/AmazingFeature`)
|
|
135
|
+
5. Open a Pull Request
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
## Canmatrix is a python package to read and write several CAN (Controller Area Network) database formats.
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/canmatrix/)
|
|
4
|
+
[](https://pypi.org/project/canmatrix/)
|
|
5
|
+
[](https://coveralls.io/github/ebroecker/canmatrix?branch=development)
|
|
6
|
+
[](https://github.com/ebroecker/canmatrix/issues)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## About
|
|
10
|
+
|
|
11
|
+
***Canmatrix*** implements a "Python Can Matrix Object" which describes the can-communication and the needed objects (Boardunits, Frames, Signals, Values, ...)
|
|
12
|
+
***Canmatrix*** also includes two **Tools** (canconvert and cancompare) for converting and comparing **CAN** databases.
|
|
13
|
+
|
|
14
|
+
- Multiple formats automotive related communication matrix file parsing.
|
|
15
|
+
- CAN message encoding and decoding.
|
|
16
|
+
|
|
17
|
+
**Supported file formats for import:**
|
|
18
|
+
|
|
19
|
+
.dbc candb / Vector
|
|
20
|
+
.dbf Busmaster (open source!)
|
|
21
|
+
.kcd kayak (open source!)
|
|
22
|
+
.arxml autosar system description
|
|
23
|
+
.yaml structured export/import (same structure as json)
|
|
24
|
+
.xls(x) excel xls-import, works with .xls-file generated by this lib
|
|
25
|
+
.sym peak pcan can description
|
|
26
|
+
.xml (fibex or CANopen eds)
|
|
27
|
+
.ldf (lin bus)
|
|
28
|
+
.odx (diagnostic file)
|
|
29
|
+
.eds
|
|
30
|
+
|
|
31
|
+
**Supported file formats for export:**
|
|
32
|
+
|
|
33
|
+
.dbc
|
|
34
|
+
.dbf
|
|
35
|
+
.kcd
|
|
36
|
+
.xls(x)
|
|
37
|
+
.json Canard (open source!)
|
|
38
|
+
.arxml (very basic implementation)
|
|
39
|
+
.yaml structured export/import (same structure as json)
|
|
40
|
+
.sym
|
|
41
|
+
.xml (fibex)
|
|
42
|
+
.lua (wireshark script)
|
|
43
|
+
.scapy
|
|
44
|
+
|
|
45
|
+
Project Homepage: https://github.com/ebroecker/canmatrix
|
|
46
|
+
|
|
47
|
+
Documentation: https://canmatrix.readthedocs.io
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
|
|
52
|
+
https://canmatrix.readthedocs.io/en/latest/installation.html
|
|
53
|
+
|
|
54
|
+
[Chinese Translation / 安装中文方法解释及注意事项](https://github.com/ebroecker/canmatrix/wiki/%E5%AE%89%E8%A3%85%E4%B8%AD%E6%96%87%E6%96%B9%E6%B3%95%E8%A7%A3%E9%87%8A%E5%8F%8A%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
## Contributing
|
|
58
|
+
|
|
59
|
+
Source Code, Documentation, Examples, Report issues and Any other contributions are **extremely appreciated**.
|
|
60
|
+
|
|
61
|
+
1. Fork the Project
|
|
62
|
+
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
|
|
63
|
+
3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
|
|
64
|
+
4. Push to the Branch (`git push origin feature/AmazingFeature`)
|
|
65
|
+
5. Open a Pull Request
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools >= 40.8.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "canmatrix"
|
|
7
|
+
description = "Automotive Communication Matrix"
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
license = { text = "BSD-2-Clause" }
|
|
10
|
+
dynamic = ["version"]
|
|
11
|
+
authors = [{ name="Eduard Bröcker", email = "eduard@gmx.de" }]
|
|
12
|
+
maintainers = [{ name = "canmatrix contributors" }]
|
|
13
|
+
dependencies = [
|
|
14
|
+
"attrs>=19.2.0",
|
|
15
|
+
"click",
|
|
16
|
+
"importlib-metadata; python_version < '3.8'",
|
|
17
|
+
"typing; python_version < '3.5'",
|
|
18
|
+
]
|
|
19
|
+
requires-python = ">=3.8"
|
|
20
|
+
keywords = [
|
|
21
|
+
"CAN",
|
|
22
|
+
"canbus",
|
|
23
|
+
"dbc",
|
|
24
|
+
"arxml",
|
|
25
|
+
"kcd",
|
|
26
|
+
"dbf",
|
|
27
|
+
"sym",
|
|
28
|
+
"fibex",
|
|
29
|
+
"eds",
|
|
30
|
+
"automotive",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
classifiers = [
|
|
34
|
+
"Development Status :: 5 - Production/Stable",
|
|
35
|
+
"Environment :: Console",
|
|
36
|
+
"Intended Audience :: Developers",
|
|
37
|
+
"Intended Audience :: Education",
|
|
38
|
+
"Intended Audience :: Information Technology",
|
|
39
|
+
"Intended Audience :: Manufacturing",
|
|
40
|
+
"Intended Audience :: Telecommunications Industry",
|
|
41
|
+
"License :: OSI Approved :: BSD License",
|
|
42
|
+
"Operating System :: Microsoft :: Windows",
|
|
43
|
+
"Operating System :: POSIX :: Linux",
|
|
44
|
+
"Operating System :: MacOS",
|
|
45
|
+
"Programming Language :: Python",
|
|
46
|
+
"Programming Language :: Python :: 3.8",
|
|
47
|
+
"Programming Language :: Python :: 3.9",
|
|
48
|
+
"Programming Language :: Python :: 3.10",
|
|
49
|
+
"Programming Language :: Python :: 3.11",
|
|
50
|
+
"Programming Language :: Python :: 3.12",
|
|
51
|
+
"Programming Language :: Python :: 3.13",
|
|
52
|
+
"Topic :: Software Development :: Embedded Systems :: Controller Area Network (CAN)",
|
|
53
|
+
"Topic :: System :: Networking",
|
|
54
|
+
"Topic :: Utilities",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
[project.scripts]
|
|
58
|
+
cancompare = "canmatrix.cli.compare:cli_compare"
|
|
59
|
+
canconvert = "canmatrix.cli.convert:cli_convert"
|
|
60
|
+
|
|
61
|
+
[project.urls]
|
|
62
|
+
homepage = "https://github.com/ebroecker/canmatrix"
|
|
63
|
+
documentation = "https://canmatrix.readthedocs.io"
|
|
64
|
+
repository = "https://github.com/ebroecker/canmatrix"
|
|
65
|
+
|
|
66
|
+
[project.optional-dependencies]
|
|
67
|
+
arxml = ["lxml"]
|
|
68
|
+
csv = []
|
|
69
|
+
dbc = []
|
|
70
|
+
dbf = []
|
|
71
|
+
fibex = ["lxml"]
|
|
72
|
+
json = []
|
|
73
|
+
kcd = ["lxml"]
|
|
74
|
+
ldf = ["ldfparser"]
|
|
75
|
+
odx = ["lxml"]
|
|
76
|
+
scapy = []
|
|
77
|
+
sym = []
|
|
78
|
+
test = ["pathlib2; python_version < '3.4'", "pytest"]
|
|
79
|
+
wireshark = []
|
|
80
|
+
xls = ["xlrd==1.2.0", "xlwt"]
|
|
81
|
+
xlsx = ["openpyxl"]
|
|
82
|
+
yaml = ["pyyaml"]
|
|
83
|
+
eds = ["canopen"]
|
|
84
|
+
|
|
85
|
+
[tool.setuptools.dynamic]
|
|
86
|
+
version = {attr = "canmatrix.__version__"}
|
|
87
|
+
|
|
88
|
+
[tool.setuptools.packages.find]
|
|
89
|
+
where = ["src"]
|
|
90
|
+
|
|
91
|
+
[tool.setuptools.package-data]
|
|
92
|
+
canmatrix = ["j1939.dbc", "py.typed"]
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (c) 2013, Eduard Broecker
|
|
3
|
+
# With contributions 2025, Gabriele Omodeo Vanone
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
|
7
|
+
# modification, are permitted provided that
|
|
8
|
+
# the following conditions are met:
|
|
9
|
+
#
|
|
10
|
+
# Redistributions of source code must retain the above copyright notice,
|
|
11
|
+
# this list of conditions and the following disclaimer.
|
|
12
|
+
# Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
# this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
# and/or other materials provided with the distribution.
|
|
15
|
+
#
|
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
17
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
18
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
19
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
20
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
21
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
22
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
23
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
24
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
25
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
26
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
|
27
|
+
|
|
28
|
+
import attr
|
|
29
|
+
import warnings
|
|
30
|
+
import typing
|
|
31
|
+
from canmatrix.exceptions import ArbitrationIdOutOfRange, J1939NeedsExtendedIdentifier
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _check_range(name, value, mask): # type: (str, int, int) -> None
|
|
35
|
+
if value != value & mask:
|
|
36
|
+
raise ValueError(
|
|
37
|
+
"{name} {value} out of range (0-{maximum})".format(
|
|
38
|
+
name=name, value=value, maximum=mask)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@attr.s
|
|
43
|
+
class ArbitrationId(object):
|
|
44
|
+
standard_id_mask = ((1 << 11) - 1)
|
|
45
|
+
extended_id_mask = ((1 << 29) - 1)
|
|
46
|
+
compound_extended_mask = (1 << 31)
|
|
47
|
+
|
|
48
|
+
id = attr.ib(default=None)
|
|
49
|
+
extended = attr.ib(default=False) # type: bool
|
|
50
|
+
|
|
51
|
+
def __attrs_post_init__(self):
|
|
52
|
+
if self.extended is None:
|
|
53
|
+
# Mimicking old behaviour for now -- remove in the future
|
|
54
|
+
self.extended = True
|
|
55
|
+
warnings.warn(
|
|
56
|
+
"Please set 'extended' attribute as a boolean instead of "
|
|
57
|
+
"None when creating an instance of ArbitrationId class",
|
|
58
|
+
DeprecationWarning
|
|
59
|
+
)
|
|
60
|
+
if self.extended:
|
|
61
|
+
mask = self.extended_id_mask
|
|
62
|
+
else:
|
|
63
|
+
mask = self.standard_id_mask
|
|
64
|
+
|
|
65
|
+
if self.id != self.id & mask:
|
|
66
|
+
raise ArbitrationIdOutOfRange('ID out of range')
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def j1939_pgn(self):
|
|
70
|
+
return self.pgn
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def pgn(self):
|
|
74
|
+
if not self.extended:
|
|
75
|
+
raise J1939NeedsExtendedIdentifier
|
|
76
|
+
# PGN is bits 8-25 of the 29-Bit Extended CAN-ID
|
|
77
|
+
# Made up of PDU-S (8-15), PDU-F (16-23), Data Page (24) & Extended Data Page (25)
|
|
78
|
+
# If PDU-F >= 240 the PDU-S is interpreted as Group Extension
|
|
79
|
+
# If PDU-F < 240 the PDU-S is interpreted as a Destination Address
|
|
80
|
+
_pgn = 0
|
|
81
|
+
if self.j1939_pdu_format == 2:
|
|
82
|
+
_pgn += self.j1939_ps
|
|
83
|
+
_pgn += self.j1939_pf << 8
|
|
84
|
+
_pgn += self.j1939_dp << 16
|
|
85
|
+
_pgn += self.j1939_edp << 17
|
|
86
|
+
|
|
87
|
+
return _pgn
|
|
88
|
+
|
|
89
|
+
@pgn.setter
|
|
90
|
+
def pgn(self, value): # type: (int) -> None
|
|
91
|
+
self.extended = True
|
|
92
|
+
_pgn = value & 0x3FFFF
|
|
93
|
+
self.id &= 0xfc0000ff
|
|
94
|
+
self.id |= (_pgn << 8 & 0x3FFFF00) # default pgn is None -> mypy reports error
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def j1939_tuple(self): # type: () -> typing.Tuple[int, int, int]
|
|
98
|
+
"""Get tuple (destination, PGN, source)
|
|
99
|
+
|
|
100
|
+
:rtype: tuple"""
|
|
101
|
+
|
|
102
|
+
return self.j1939_destination, self.pgn, self.j1939_source
|
|
103
|
+
|
|
104
|
+
@property
|
|
105
|
+
def j1939_destination(self):
|
|
106
|
+
if not self.extended:
|
|
107
|
+
raise J1939NeedsExtendedIdentifier
|
|
108
|
+
if self.j1939_pdu_format == 1:
|
|
109
|
+
destination = self.j1939_ps
|
|
110
|
+
else:
|
|
111
|
+
destination = None
|
|
112
|
+
return destination
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def j1939_source(self):
|
|
116
|
+
if not self.extended:
|
|
117
|
+
raise J1939NeedsExtendedIdentifier
|
|
118
|
+
return self.id & 0xFF
|
|
119
|
+
|
|
120
|
+
@j1939_source.setter
|
|
121
|
+
def j1939_source(self, value): # type: (int) -> None
|
|
122
|
+
self.extended = True
|
|
123
|
+
self.id = (self.id & 0xffffff00) | (value & 0xff)
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def j1939_ps(self):
|
|
127
|
+
if not self.extended:
|
|
128
|
+
raise J1939NeedsExtendedIdentifier
|
|
129
|
+
return (self.id >> 8) & 0xFF
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def j1939_pf(self):
|
|
133
|
+
if not self.extended:
|
|
134
|
+
raise J1939NeedsExtendedIdentifier
|
|
135
|
+
return (self.id >> 16) & 0xFF
|
|
136
|
+
|
|
137
|
+
@property
|
|
138
|
+
def j1939_pdu_format(self):
|
|
139
|
+
return 1 if (self.j1939_pf < 240) else 2
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def j1939_dp(self):
|
|
143
|
+
if not self.extended:
|
|
144
|
+
raise J1939NeedsExtendedIdentifier
|
|
145
|
+
return (self.id >> 24) & 0x1
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def j1939_edp(self):
|
|
149
|
+
if not self.extended:
|
|
150
|
+
raise J1939NeedsExtendedIdentifier
|
|
151
|
+
return (self.id >> 25) & 0x1
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def j1939_priority(self):
|
|
155
|
+
if not self.extended:
|
|
156
|
+
raise J1939NeedsExtendedIdentifier
|
|
157
|
+
return (self.id >> 26) & 0x7
|
|
158
|
+
|
|
159
|
+
@j1939_priority.setter
|
|
160
|
+
def j1939_priority(self, value): # type: (int) -> None
|
|
161
|
+
self.extended = True
|
|
162
|
+
self.id = (self.id & 0x3ffffff) | ((value & 0x7) << 26)
|
|
163
|
+
|
|
164
|
+
@property
|
|
165
|
+
def j1939_str(self): # type: () -> str
|
|
166
|
+
return "DA:0x{da:02X} PGN:0x{pgn:04X} SA:0x{sa:02X}".format(
|
|
167
|
+
da=self.j1939_destination, pgn=self.pgn, sa=self.j1939_source)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@classmethod
|
|
171
|
+
def from_compound_integer(cls, i): # type: (typing.Any) -> ArbitrationId
|
|
172
|
+
return cls(
|
|
173
|
+
id=i & cls.extended_id_mask,
|
|
174
|
+
extended=(i & cls.compound_extended_mask) != 0,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
@classmethod
|
|
178
|
+
def from_pgn(cls, pgn): # type: (int) -> ArbitrationId
|
|
179
|
+
return cls(
|
|
180
|
+
id = (pgn << 8), extended = True
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
@classmethod
|
|
184
|
+
def from_j1939_fields(cls, pdu_format, pdu_specific, priority=0, edp=0, dp=0,
|
|
185
|
+
source_address=0):
|
|
186
|
+
# type: (int, int, int, int, int, int) -> ArbitrationId
|
|
187
|
+
"""Build an extended ArbitrationId from the individual J1939 bit fields.
|
|
188
|
+
|
|
189
|
+
:param pdu_format: PDU Format (PF), 8 bits (0-255)
|
|
190
|
+
:param pdu_specific: PDU Specific (PS), 8 bits (0-255) -- destination
|
|
191
|
+
address (PF < 240) or group extension (PF >= 240)
|
|
192
|
+
:param priority: message priority, 3 bits (0-7)
|
|
193
|
+
:param edp: Extended Data Page, 1 bit (0-1)
|
|
194
|
+
:param dp: Data Page, 1 bit (0-1)
|
|
195
|
+
:param source_address: source address (SA), 8 bits (0-255)
|
|
196
|
+
:rtype: ArbitrationId
|
|
197
|
+
"""
|
|
198
|
+
_check_range("priority", priority, 0x7)
|
|
199
|
+
_check_range("edp", edp, 0x1)
|
|
200
|
+
_check_range("dp", dp, 0x1)
|
|
201
|
+
_check_range("pdu_format", pdu_format, 0xFF)
|
|
202
|
+
_check_range("pdu_specific", pdu_specific, 0xFF)
|
|
203
|
+
_check_range("source_address", source_address, 0xFF)
|
|
204
|
+
_id = (
|
|
205
|
+
(priority << 26)
|
|
206
|
+
| (edp << 25)
|
|
207
|
+
| (dp << 24)
|
|
208
|
+
| (pdu_format << 16)
|
|
209
|
+
| (pdu_specific << 8)
|
|
210
|
+
| source_address
|
|
211
|
+
)
|
|
212
|
+
return cls(id=_id, extended=True)
|
|
213
|
+
|
|
214
|
+
@classmethod
|
|
215
|
+
def from_pgn_fields(cls, pgn, priority=0, source_address=0, destination=None):
|
|
216
|
+
# type: (int, int, int, typing.Optional[int]) -> ArbitrationId
|
|
217
|
+
"""Build an extended ArbitrationId from a PGN plus its surrounding fields.
|
|
218
|
+
|
|
219
|
+
:param pgn: Parameter Group Number, 18 bits (0-0x3FFFF)
|
|
220
|
+
:param priority: message priority, 3 bits (0-7)
|
|
221
|
+
:param source_address: source address (SA), 8 bits (0-255)
|
|
222
|
+
:param destination: destination address, 8 bits (0-255). Only valid for
|
|
223
|
+
PDU1 messages (PDU format < 240); raises ValueError otherwise.
|
|
224
|
+
:rtype: ArbitrationId
|
|
225
|
+
"""
|
|
226
|
+
_check_range("pgn", pgn, 0x3FFFF)
|
|
227
|
+
_check_range("priority", priority, 0x7)
|
|
228
|
+
_check_range("source_address", source_address, 0xFF)
|
|
229
|
+
_id = (pgn << 8) | (priority << 26) | source_address
|
|
230
|
+
if destination is not None:
|
|
231
|
+
_check_range("destination", destination, 0xFF)
|
|
232
|
+
pdu_format = (_id >> 16) & 0xFF
|
|
233
|
+
if pdu_format >= 240:
|
|
234
|
+
raise ValueError(
|
|
235
|
+
"destination is only valid for PDU1 messages "
|
|
236
|
+
"(PDU format < 240)"
|
|
237
|
+
)
|
|
238
|
+
_id |= destination << 8
|
|
239
|
+
return cls(id=_id, extended=True)
|
|
240
|
+
|
|
241
|
+
def to_compound_integer(self):
|
|
242
|
+
if self.extended:
|
|
243
|
+
return self.id | self.compound_extended_mask
|
|
244
|
+
else:
|
|
245
|
+
return self.id
|
|
246
|
+
|
|
247
|
+
def __eq__(self, other):
|
|
248
|
+
return (
|
|
249
|
+
self.id == other.id
|
|
250
|
+
and (
|
|
251
|
+
self.extended is None
|
|
252
|
+
or other.extended is None
|
|
253
|
+
or self.extended == other.extended
|
|
254
|
+
)
|
|
255
|
+
)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (c) 2013, Eduard Broecker
|
|
3
|
+
# With contributions 2025, Gabriele Omodeo Vanone
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
|
7
|
+
# modification, are permitted provided that
|
|
8
|
+
# the following conditions are met:
|
|
9
|
+
#
|
|
10
|
+
# Redistributions of source code must retain the above copyright notice,
|
|
11
|
+
# this list of conditions and the following disclaimer.
|
|
12
|
+
# Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
# this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
# and/or other materials provided with the distribution.
|
|
15
|
+
#
|
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
17
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
18
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
19
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
20
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
21
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
22
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
23
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
24
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
25
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
26
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
|
27
|
+
|
|
28
|
+
import typing
|
|
29
|
+
import attr
|
|
30
|
+
|
|
31
|
+
@attr.s(eq=False)
|
|
32
|
+
class AutosarE2EProperties(object):
|
|
33
|
+
profile = attr.ib(default=None) # type: str
|
|
34
|
+
data_id_mode = attr.ib(default=None) # type: str
|
|
35
|
+
data_ids = attr.ib(default=None) # type: list[int]
|
|
36
|
+
data_length = attr.ib(default=None) # type: int
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (c) 2013, Eduard Broecker
|
|
3
|
+
# With contributions 2025, Gabriele Omodeo Vanone
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
|
7
|
+
# modification, are permitted provided that
|
|
8
|
+
# the following conditions are met:
|
|
9
|
+
#
|
|
10
|
+
# Redistributions of source code must retain the above copyright notice,
|
|
11
|
+
# this list of conditions and the following disclaimer.
|
|
12
|
+
# Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
# this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
# and/or other materials provided with the distribution.
|
|
15
|
+
#
|
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
17
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
18
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
19
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
20
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
21
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
22
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
23
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
24
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
25
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
26
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
|
27
|
+
|
|
28
|
+
import typing
|
|
29
|
+
import attr
|
|
30
|
+
|
|
31
|
+
@attr.s(eq=False)
|
|
32
|
+
class AutosarSecOCProperties(object):
|
|
33
|
+
secured_i_pdu_name = attr.ib(default="unknown") # type: str
|
|
34
|
+
authentic_i_pdu_name = attr.ib(default="unknown") # type: str
|
|
35
|
+
auth_algorithm = attr.ib(default=None) # type: str|None
|
|
36
|
+
authentic_pdu_length = attr.ib(default=0) # type: int
|
|
37
|
+
secured_i_pdu_length = attr.ib(default=0) # type: int
|
|
38
|
+
auth_tx_length = attr.ib(default=None) # type: int|None
|
|
39
|
+
data_id = attr.ib(default=None) # type: int|None
|
|
40
|
+
freshness_value_id = attr.ib(default=None) # type: int|None
|
|
41
|
+
freshness_length = attr.ib(default=None) # type: int|None
|
|
42
|
+
freshness_tx_length = attr.ib(default=None) # type: int|None
|
|
43
|
+
use_as_cryptographic_i_pdu = attr.ib(default=False) # type: bool
|
|
44
|
+
message_link_length = attr.ib(default=None) # type: int|None
|
|
45
|
+
message_link_position = attr.ib(default=None) # type: int|None
|
|
46
|
+
key_id = attr.ib(default=None) # type: int|None
|