EXE 변환 후 동작하여도 작동 작동 되도록 수정

This commit is contained in:
3minbe 2025-02-20 08:50:34 +09:00
parent be43f81125
commit 48dbd3e25a
77 changed files with 2338 additions and 19770 deletions

View File

@ -535,9 +535,24 @@ class MainView(QtWidgets.QMainWindow):
channel_info = self.settings.get("channel_info", {}).get(os.path.basename(file_path), "CH0") channel_info = self.settings.get("channel_info", {}).get(os.path.basename(file_path), "CH0")
try: try:
shutil.copy(file_path, dbc_output_dir) # DBC 파일 복사 shutil.copy(file_path, dbc_output_dir) # DBC 파일 복사
subprocess.run(["python", "DBC_Converter_RX.py", file_path, rx_output_dir, json.dumps({os.path.basename(file_path): channel_info})], check=True) if hasattr(sys, '_MEIPASS'):
subprocess.run(["python", "DBC_Converter_TX.py", file_path, tx_output_dir, json.dumps({os.path.basename(file_path): channel_info})], check=True) script_path = os.path.join(sys._MEIPASS, "DBC_Converter_RX.py")
subprocess.run(["python", "DBC_Converter_Common.py", file_path, common_output_dir, json.dumps({os.path.basename(file_path): channel_info})], check=True) else:
script_path = "DBC_Converter_RX.py"
subprocess.run(["python", script_path, file_path, rx_output_dir, json.dumps({os.path.basename(file_path): channel_info})], check=True)
if hasattr(sys, '_MEIPASS'):
script_path = os.path.join(sys._MEIPASS, "DBC_Converter_TX.py")
else:
script_path = "DBC_Converter_TX.py"
subprocess.run(["python", script_path, file_path, tx_output_dir, json.dumps({os.path.basename(file_path): channel_info})], check=True)
if hasattr(sys, '_MEIPASS'):
script_path = os.path.join(sys._MEIPASS, "DBC_Converter_Common.py")
else:
script_path = "DBC_Converter_Common.py"
subprocess.run(["python", script_path, file_path, common_output_dir, json.dumps({os.path.basename(file_path): channel_info})], check=True)
self.updateAlertText(f"변환 성공", [file_path]) self.updateAlertText(f"변환 성공", [file_path])
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
self.updateAlertText(f"변환 실패: {e}", [file_path]) self.updateAlertText(f"변환 실패: {e}", [file_path])

View File

@ -1,10 +1,11 @@
# -*- mode: python ; coding: utf-8 -*- # -*- mode: python ; coding: utf-8 -*-
added_files = [("./img/*", './img'), added_files = [("./img/*", './img'),
("./icon/*", '.icon'), ("./icon/*", './icon'),
("./icon.ico", '.')] ("./icon.ico", '.'),
("./*.py", '.')]
a = Analysis(['DBC_Converter.py'], a = Analysis(['DBC_Converter.py', 'DBC_Converter_Data_Parsing.py', 'DBC_Converter_Common.py', 'DBC_Converter_TX.py', 'DBC_Converter_RX.py'],
pathex=[], pathex=[],
binaries=[], binaries=[],
datas=added_files, datas=added_files,
@ -20,7 +21,6 @@ a = Analysis(['DBC_Converter.py'],
'PyQt5.QtWebEngineCore', 'PyQt5.QtWebEngineCore',
'PyQt5.QtWebChannel', 'PyQt5.QtWebChannel',
'PyQt5.QtWebSockets', 'PyQt5.QtWebSockets',
'cantools',
], ],
hookspath=[], hookspath=[],
hooksconfig={}, hooksconfig={},

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -54,31 +54,34 @@
('pyiboot01_bootstrap', ('pyiboot01_bootstrap',
'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\loader\\pyiboot01_bootstrap.py', 'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\loader\\pyiboot01_bootstrap.py',
'PYSOURCE'), 'PYSOURCE'),
('pyi_rth_pkgutil',
'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pkgutil.py',
'PYSOURCE'),
('pyi_rth_multiprocessing',
'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_multiprocessing.py',
'PYSOURCE'),
('pyi_rth_inspect', ('pyi_rth_inspect',
'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py', 'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py',
'PYSOURCE'), 'PYSOURCE'),
('pyi_rth_pkgres',
'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pkgres.py',
'PYSOURCE'),
('pyi_rth_setuptools',
'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_setuptools.py',
'PYSOURCE'),
('pyi_rth_pyqt5', ('pyi_rth_pyqt5',
'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pyqt5.py', 'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pyqt5.py',
'PYSOURCE'), 'PYSOURCE'),
('pyi_rth_pkgutil',
'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pkgutil.py',
'PYSOURCE'),
('DBC_Converter', ('DBC_Converter',
'C:\\Users\\MSI\\Documents\\WorkSpace\\git\\DBC_Converter\\DBC_Converter.py', 'C:\\Users\\MSI\\Documents\\WorkSpace\\git\\DBC_Converter\\DBC_Converter.py',
'PYSOURCE'),
('DBC_Converter_Data_Parsing',
'C:\\Users\\MSI\\Documents\\WorkSpace\\git\\DBC_Converter\\DBC_Converter_Data_Parsing.py',
'PYSOURCE'),
('DBC_Converter_Common',
'C:\\Users\\MSI\\Documents\\WorkSpace\\git\\DBC_Converter\\DBC_Converter_Common.py',
'PYSOURCE'),
('DBC_Converter_TX',
'C:\\Users\\MSI\\Documents\\WorkSpace\\git\\DBC_Converter\\DBC_Converter_TX.py',
'PYSOURCE'),
('DBC_Converter_RX',
'C:\\Users\\MSI\\Documents\\WorkSpace\\git\\DBC_Converter\\DBC_Converter_RX.py',
'PYSOURCE')], 'PYSOURCE')],
[], [],
False, False,
False, False,
1736326899, 1740008983,
[('runw.exe', [('runw.exe',
'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\bootloader\\Windows-64bit-intel\\runw.exe', 'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\bootloader\\Windows-64bit-intel\\runw.exe',
'EXECUTABLE')], 'EXECUTABLE')],

View File

@ -31,26 +31,29 @@
('pyiboot01_bootstrap', ('pyiboot01_bootstrap',
'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\loader\\pyiboot01_bootstrap.py', 'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\loader\\pyiboot01_bootstrap.py',
'PYSOURCE'), 'PYSOURCE'),
('pyi_rth_pkgutil',
'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pkgutil.py',
'PYSOURCE'),
('pyi_rth_multiprocessing',
'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_multiprocessing.py',
'PYSOURCE'),
('pyi_rth_inspect', ('pyi_rth_inspect',
'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py', 'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py',
'PYSOURCE'), 'PYSOURCE'),
('pyi_rth_pkgres',
'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pkgres.py',
'PYSOURCE'),
('pyi_rth_setuptools',
'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_setuptools.py',
'PYSOURCE'),
('pyi_rth_pyqt5', ('pyi_rth_pyqt5',
'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pyqt5.py', 'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pyqt5.py',
'PYSOURCE'), 'PYSOURCE'),
('pyi_rth_pkgutil',
'C:\\Users\\MSI\\AppData\\Local\\Programs\\Python\\Python313\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pkgutil.py',
'PYSOURCE'),
('DBC_Converter', ('DBC_Converter',
'C:\\Users\\MSI\\Documents\\WorkSpace\\git\\DBC_Converter\\DBC_Converter.py', 'C:\\Users\\MSI\\Documents\\WorkSpace\\git\\DBC_Converter\\DBC_Converter.py',
'PYSOURCE'),
('DBC_Converter_Data_Parsing',
'C:\\Users\\MSI\\Documents\\WorkSpace\\git\\DBC_Converter\\DBC_Converter_Data_Parsing.py',
'PYSOURCE'),
('DBC_Converter_Common',
'C:\\Users\\MSI\\Documents\\WorkSpace\\git\\DBC_Converter\\DBC_Converter_Common.py',
'PYSOURCE'),
('DBC_Converter_TX',
'C:\\Users\\MSI\\Documents\\WorkSpace\\git\\DBC_Converter\\DBC_Converter_TX.py',
'PYSOURCE'),
('DBC_Converter_RX',
'C:\\Users\\MSI\\Documents\\WorkSpace\\git\\DBC_Converter\\DBC_Converter_RX.py',
'PYSOURCE')], 'PYSOURCE')],
'python313.dll', 'python313.dll',
True, True,

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -14,50 +14,14 @@ Types if import:
IMPORTANT: Do NOT post this list to the issue-tracker. Use it as a basis for IMPORTANT: Do NOT post this list to the issue-tracker. Use it as a basis for
tracking down the missing module yourself. Thanks! tracking down the missing module yourself. Thanks!
missing module named 'collections.abc' - imported by tracemalloc (top-level), traceback (top-level), inspect (top-level), logging (top-level), typing (top-level), selectors (top-level), cantools.tester (top-level), typing_extensions (top-level), configparser (top-level), can.exceptions (conditional), asyncio.base_events (top-level), http.client (top-level), asyncio.coroutines (top-level), sqlite3.dbapi2 (top-level), pkg_resources (top-level), setuptools (top-level), setuptools._vendor.jaraco.functools (top-level), setuptools._vendor.more_itertools.more (top-level), setuptools._vendor.more_itertools.recipes (top-level), setuptools._reqs (top-level), setuptools.discovery (top-level), setuptools.dist (top-level), setuptools._distutils.dist (top-level), setuptools.config.setupcfg (top-level), setuptools.config.expand (top-level), setuptools.config.pyprojecttoml (top-level), setuptools.config._apply_pyprojecttoml (top-level), tomllib._parser (top-level), setuptools._vendor.tomli._parser (top-level), setuptools.command.egg_info (top-level), setuptools.glob (top-level), setuptools.command._requirestxt (top-level), setuptools.command.bdist_wheel (top-level), wheel.cli.convert (top-level), wheel.cli.tags (top-level), setuptools._vendor.platformdirs.windows (conditional), diskcache.persistent (top-level), cantools.typechecking (top-level), cantools.database.utils (top-level), xml.etree.ElementTree (top-level), cantools.database.can.formats.sym (conditional), _pyrepl.types (top-level), _pyrepl.readline (top-level) missing module named pyimod02_importers - imported by C:\Users\MSI\AppData\Local\Programs\Python\Python313\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgutil.py (delayed)
missing module named grp - imported by shutil (delayed, optional), tarfile (optional), pathlib._local (optional), subprocess (delayed, conditional, optional), setuptools._distutils.archive_util (optional), setuptools._vendor.backports.tarfile (optional) missing module named 'collections.abc' - imported by traceback (top-level), inspect (top-level), logging (top-level), typing (top-level), selectors (top-level), tracemalloc (top-level)
missing module named pwd - imported by posixpath (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib._local (optional), subprocess (delayed, conditional, optional), setuptools._distutils.util (delayed, conditional, optional), setuptools._distutils.archive_util (optional), netrc (delayed, conditional), getpass (delayed, optional), setuptools._vendor.backports.tarfile (optional), http.server (delayed, optional) missing module named posix - imported by posixpath (optional), shutil (conditional), importlib._bootstrap_external (conditional), os (conditional, optional)
missing module named posix - imported by posixpath (optional), shutil (conditional), importlib._bootstrap_external (conditional), os (conditional, optional), _pyrepl.unix_console (delayed, optional)
missing module named resource - imported by posix (top-level) missing module named resource - imported by posix (top-level)
missing module named _frozen_importlib_external - imported by importlib._bootstrap (delayed), importlib (optional), importlib.abc (optional), zipimport (top-level) missing module named grp - imported by shutil (delayed, optional), tarfile (optional), pathlib._local (optional), subprocess (delayed, conditional, optional)
missing module named pwd - imported by posixpath (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib._local (optional), subprocess (delayed, conditional, optional)
excluded module named _frozen_importlib - imported by importlib (optional), importlib.abc (optional), zipimport (top-level) excluded module named _frozen_importlib - imported by importlib (optional), importlib.abc (optional), zipimport (top-level)
missing module named vms_lib - imported by platform (delayed, optional) missing module named _frozen_importlib_external - imported by importlib._bootstrap (delayed), importlib (optional), importlib.abc (optional), zipimport (top-level)
missing module named 'java.lang' - imported by platform (delayed, optional)
missing module named java - imported by platform (delayed)
missing module named asyncio.DefaultEventLoopPolicy - imported by asyncio (delayed, conditional), asyncio.events (delayed, conditional)
missing module named _posixshmem - imported by multiprocessing.resource_tracker (conditional), multiprocessing.shared_memory (conditional)
missing module named multiprocessing.set_start_method - imported by multiprocessing (top-level), multiprocessing.spawn (top-level)
missing module named multiprocessing.get_start_method - imported by multiprocessing (top-level), multiprocessing.spawn (top-level)
missing module named _posixsubprocess - imported by subprocess (conditional), multiprocessing.util (delayed)
missing module named multiprocessing.get_context - imported by multiprocessing (top-level), multiprocessing.pool (top-level), multiprocessing.managers (top-level), multiprocessing.sharedctypes (top-level)
missing module named multiprocessing.TimeoutError - imported by multiprocessing (top-level), multiprocessing.pool (top-level)
missing module named _scproxy - imported by urllib.request (conditional)
missing module named termios - imported by getpass (optional), tty (top-level), _pyrepl.pager (delayed, optional), _pyrepl.unix_console (top-level), _pyrepl.fancy_termios (top-level), _pyrepl.unix_eventqueue (top-level)
missing module named multiprocessing.BufferTooShort - imported by multiprocessing (top-level), multiprocessing.connection (top-level)
missing module named multiprocessing.AuthenticationError - imported by multiprocessing (top-level), multiprocessing.connection (top-level)
missing module named usercustomize - imported by site (delayed, optional)
missing module named sitecustomize - imported by site (delayed, optional)
missing module named _curses - imported by curses (top-level), curses.has_key (top-level), _pyrepl.curses (optional)
missing module named fcntl - imported by subprocess (optional), _pyrepl.unix_console (top-level)
missing module named readline - imported by cmd (delayed, conditional, optional), code (delayed, conditional, optional), rlcompleter (optional), pdb (delayed, optional), site (delayed, optional), sqlite3.__main__ (delayed, conditional, optional)
missing module named '_typeshed.importlib' - imported by pkg_resources (conditional)
missing module named _typeshed - imported by setuptools.glob (conditional), setuptools.compat.py311 (conditional), pkg_resources (conditional)
missing module named jnius - imported by setuptools._vendor.platformdirs.android (delayed, conditional, optional)
missing module named android - imported by setuptools._vendor.platformdirs.android (delayed, conditional, optional)
missing module named importlib_resources - imported by setuptools._vendor.jaraco.text (optional)
missing module named jaraco.text.yield_lines - imported by setuptools._vendor.jaraco.text (top-level), setuptools._entry_points (top-level), setuptools.command._requirestxt (top-level)
missing module named _manylinux - imported by packaging._manylinux (delayed, optional), setuptools._vendor.packaging._manylinux (delayed, optional), wheel.vendored.packaging._manylinux (delayed, optional)
missing module named trove_classifiers - imported by setuptools.config._validate_pyproject.formats (optional)
missing module named pyimod02_importers - imported by C:\Users\MSI\AppData\Local\Programs\Python\Python313\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgutil.py (delayed), C:\Users\MSI\AppData\Local\Programs\Python\Python313\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgres.py (delayed)
missing module named cantools.database.Database - imported by cantools.database (conditional), cantools.database.utils (conditional)
missing module named cantools.database.Signal - imported by cantools.database (conditional), cantools.typechecking (conditional)
missing module named cantools.database.Message - imported by cantools.database (conditional), cantools.typechecking (conditional)
missing module named 'django.core' - imported by diskcache.djangocache (optional)
missing module named django - imported by diskcache.djangocache (top-level)
missing module named 'asammdf.mdf' - imported by can.io.mf4 (optional)
missing module named 'asammdf.blocks' - imported by can.io.mf4 (optional)
missing module named numpy - imported by can.io.mf4 (optional)
missing module named asammdf - imported by can.io.mf4 (optional)
missing module named win32event - imported by can.broadcastmanager (delayed)
missing module named pywintypes - imported by can.broadcastmanager (delayed)
missing module named _suggestions - imported by traceback (delayed, optional) missing module named _suggestions - imported by traceback (delayed, optional)
missing module named _posixsubprocess - imported by subprocess (conditional)
missing module named fcntl - imported by subprocess (optional)

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,635 @@
"""
MainView 클래스는 PyQt5를 사용하여 DBC 파일을 C 파일로 변환하는 GUI 애플리케이션을 구현합니다.
메서드:
__init__(self):
MainView 클래스의 초기화 메서드입니다. UI 설정 초기값을 설정합니다.
loadSettings(self):
설정 파일을 로드하고, 기본 저장 경로 마지막 열린 디렉토리를 설정합니다.
saveSettings(self):
현재 설정을 설정 파일에 저장합니다.
setupUI(self):
UI를 설정하고 메뉴, 툴바, 상태바, 파일 리스트 기타 위젯을 추가합니다.
selectSavePath(self):
저장 경로를 선택하는 파일 다이얼로그를 엽니다.
openSavePath(self):
현재 설정된 저장 경로를 엽니다.
openCalculator(self):
계산기를 엽니다.
centerWindow(self):
창을 화면 중앙에 위치시킵니다.
centerDialog(self, dialog):
다이얼로그를 부모 창의 중앙에 위치시킵니다.
FilesOpen(self):
파일 열기 다이얼로그를 열고 선택된 파일을 파일 리스트에 추가합니다.
deleteSelectedFiles(self):
선택된 파일을 파일 리스트에서 삭제합니다.
deleteAllFiles(self):
파일 리스트의 모든 파일을 삭제합니다.
removeChannelInfo(self, file_path):
설정 파일에서 특정 파일의 채널 정보를 제거합니다.
clearAlerts(self):
알림창의 내용을 지웁니다.
showWarning(self, title, message):
경고 메시지 박스를 표시합니다.
showDuplicateFilesWarning(self, duplicate_files, new_files, new_files_names):
중복 파일 경고 메시지 박스를 표시합니다.
sortTreeView(self, col, reverse):
파일 리스트를 특정 열을 기준으로 정렬합니다.
selectAllFiles(self):
파일 리스트의 모든 파일을 선택합니다.
invertSelection(self):
파일 리스트의 선택을 반전시킵니다.
setWindowSize(self, size):
창의 크기를 설정합니다.
openSettings(self):
설정 다이얼로그를 엽니다.
openAbout(self):
프로그램 정보 다이얼로그를 엽니다.
populateTreeView(self, file_paths):
파일 리스트에 파일을 추가하고 채널 정보를 업데이트합니다.
updateAlertText(self, message, file_list):
알림창에 메시지를 추가합니다.
convertFiles(self):
파일을 변환하고 변환 상태를 업데이트합니다.
onHeaderClicked(self, logicalIndex):
파일 리스트의 헤더를 클릭했을 정렬을 수행합니다.
onItemDoubleClicked(self, item, column):
파일 리스트의 항목을 더블클릭했을 파일을 열거나 경로를 엽니다.
openChannelDialog(self, item):
채널 선택 다이얼로그를 엽니다.
applyChannelSelection(self, item, combo, dialog):
선택된 채널을 파일 리스트에 적용하고 다이얼로그를 닫습니다.
updateChannelInfo(self, directory, filename, channel):
설정 파일에 채널 정보를 업데이트합니다.
setupCloseEvent(self):
프로그램 종료 이벤트를 설정합니다.
onClose(self, event):
프로그램 종료 설정을 초기화하고 저장합니다.
"""
import os
import sys
from PyQt5 import QtWidgets, QtGui, QtCore
from datetime import datetime
import json
import subprocess
import shutil # 파일 복사를 위해 shutil 모듈 추가
class MainView(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
if hasattr(sys, '_MEIPASS'):
base_path = sys._MEIPASS # 실행 파일 내에서 파일 경로 설정
else:
base_path = os.path.abspath(".") # 실행 파일이 아닌 경우 현재 디렉토리로 설정
icon_path = os.path.join(base_path, "icon/icon.png") # 아이콘 경로 설정
self.setWindowIcon(QtGui.QIcon(icon_path)) # 프로그램 아이콘 설정
self.version = "1.0.0" # 프로그램 버전 설정
self.default_save_path = os.path.join(os.path.expanduser("~"), "Desktop") # 바탕화면 경로 설정
self.file_paths = [] # 파일 경로 저장 리스트
self.channel_info = {} # 채널 정보 초기화
self.channel_options = ["CH0", "CH1", "CH2", "CH3", "CH4", "CH5"] # 채널 옵션 설정
self.loadSettings() # 설정 로드
self.setupUI(base_path) # UI 설정
self.centerWindow() # 창을 화면 중앙에 위치
self.sortTreeView(0, True) # 기본 파일명 오름차순 정렬
self.setupCloseEvent() # 프로그램 종료 이벤트 설정
self.tree.itemSelectionChanged.connect(self.onFileSelectionChanged)
def loadSettings(self):
self.settings_file = "settings.json"
if os.path.exists(self.settings_file):
with open(self.settings_file, "r", encoding="utf-8") as file:
self.settings = json.load(file) # 설정 파일 로드
self.default_save_path = self.settings.get("default_save_path", os.path.join(os.path.expanduser("~"), "Desktop"))
self.last_opened_dir = self.settings.get("last_opened_dir", os.path.expanduser("~"))
# JSON 파일 내의 file_paths와 channel_info 정보 지우기
self.settings["file_paths"] = []
self.settings["channel_info"] = {}
self.saveSettings()
else:
self.settings = {"theme": "light"} # 기본 설정
self.default_save_path = os.path.join(os.path.expanduser("~"), "Desktop") # 바탕화면 경로 설정
self.last_opened_dir = os.path.expanduser("~")
def saveSettings(self):
self.settings["default_save_path"] = self.default_save_path
self.settings["file_paths"] = self.file_paths
self.settings["last_opened_dir"] = self.last_opened_dir
with open(self.settings_file, "w", encoding="utf-8") as file:
json.dump(self.settings, file, ensure_ascii=False, indent=4) # 설정 파일 저장
def setupUI(self, base_path):
self.setWindowTitle("DBC Converter")
self.setGeometry(100, 100, 1250, 600) # 창 크기 조정
# 메뉴바 추가
self.menu_bar = self.menuBar()
file_menu = self.menu_bar.addMenu("파일")
edit_menu = self.menu_bar.addMenu("편집")
view_menu = self.menu_bar.addMenu("보기")
tools_menu = self.menu_bar.addMenu("도구")
# 파일 메뉴 항목 추가
add_file_action = QtWidgets.QAction("파일 추가", self)
add_file_action.setShortcut('Ctrl+O')
add_file_action.triggered.connect(self.FilesOpen)
file_menu.addAction(add_file_action)
delete_selected_action = QtWidgets.QAction("선택 파일 삭제", self)
delete_selected_action.setShortcut('Delete')
delete_selected_action.triggered.connect(self.deleteSelectedFiles)
file_menu.addAction(delete_selected_action)
delete_all_action = QtWidgets.QAction("모든 파일 삭제", self)
delete_all_action.setShortcut('Ctrl+Shift+Delete')
delete_all_action.triggered.connect(self.deleteAllFiles)
file_menu.addAction(delete_all_action)
# 편집 메뉴 항목 추가
select_all_action = QtWidgets.QAction("모두 선택", self)
select_all_action.setShortcut('Ctrl+A')
select_all_action.triggered.connect(self.selectAllFiles)
edit_menu.addAction(select_all_action)
invert_selection_action = QtWidgets.QAction("선택 반전", self)
invert_selection_action.setShortcut('Ctrl+I')
invert_selection_action.triggered.connect(self.invertSelection)
edit_menu.addAction(invert_selection_action)
# 보기 메뉴 항목 추가
size_menu = view_menu.addMenu("창크기")
size_menu.addAction("기본", lambda: self.setWindowSize("default")).setShortcut('Ctrl+0')
size_menu.addAction("작게", lambda: self.setWindowSize("small")).setShortcut('Ctrl+1')
size_menu.addAction("보통", lambda: self.setWindowSize("medium")).setShortcut('Ctrl+2')
size_menu.addAction("크게", lambda: self.setWindowSize("large")).setShortcut('Ctrl+3')
size_menu.addAction("자동", lambda: self.setWindowSize("auto")).setShortcut('Ctrl+4')
sort_menu = view_menu.addMenu("정렬")
sort_menu.addAction("파일명 오름차순", lambda: self.sortTreeView(0, False)).setShortcut('Ctrl+Shift+N')
sort_menu.addAction("파일명 내림차순", lambda: self.sortTreeView(0, True)).setShortcut('Ctrl+Shift+M')
sort_menu.addAction("파일경로 오름차순", lambda: self.sortTreeView(1, False)).setShortcut('Ctrl+Shift+P')
sort_menu.addAction("파일경로 내림차순", lambda: self.sortTreeView(1, True)).setShortcut('Ctrl+Shift+Q')
sort_menu.addAction("파일크기 오름차순", lambda: self.sortTreeView(3, False)).setShortcut('Ctrl+Shift+S')
sort_menu.addAction("파일크기 내림차순", lambda: self.sortTreeView(3, True)).setShortcut('Ctrl+Shift+T')
# 도구 메뉴 항목 추가
settings_action = QtWidgets.QAction("설정", self)
settings_action.setShortcut('Ctrl+P')
settings_action.triggered.connect(self.openSettings)
tools_menu.addAction(settings_action)
about_action = QtWidgets.QAction("프로그램 정보", self)
about_action.setShortcut('Ctrl+I')
about_action.triggered.connect(self.openAbout)
tools_menu.addAction(about_action)
tools_menu.addSeparator() # 구분선 추가
calculator_action = QtWidgets.QAction("계산기", self)
calculator_action.setShortcut('Ctrl+Shift+C')
calculator_action.triggered.connect(self.openCalculator)
tools_menu.addAction(calculator_action)
# 툴바 추가
self.toolbar = self.addToolBar("Main Toolbar")
add_file_action_img_path = os.path.join(base_path, "img/add_file.png") # 파일 추가 아이콘 경로 설정
add_file_action = QtWidgets.QAction(QtGui.QIcon(add_file_action_img_path), "파일 추가\n(Ctrl+O)", self)
add_file_action.triggered.connect(self.FilesOpen)
self.toolbar.addAction(add_file_action)
delete_file_action_img_path = os.path.join(base_path, "img/delete_file.png") # 파일 삭제 아이콘 경로 설정
delete_file_action = QtWidgets.QAction(QtGui.QIcon(delete_file_action_img_path), "선택 파일 삭제\n(Delete)", self)
delete_file_action.triggered.connect(self.deleteSelectedFiles)
self.toolbar.addAction(delete_file_action)
delete_all_action_img_path = os.path.join(base_path, "img/delete_all.png") # 모든 파일 삭제 아이콘 경로 설정
delete_all_action = QtWidgets.QAction(QtGui.QIcon(delete_all_action_img_path), "모든 파일 삭제\n(Ctrl+Shift+Delete)", self)
delete_all_action.triggered.connect(self.deleteAllFiles)
self.toolbar.addAction(delete_all_action)
delete_description_action_img_path = os.path.join(base_path, "img/delete_description.png") # 메시지 창 내용 삭제 아이콘 경로 설정
delete_description_action = QtWidgets.QAction(QtGui.QIcon(delete_description_action_img_path), "메시지 창 내용 삭제\n(Ctrl+Shift+D)", self)
delete_description_action.triggered.connect(self.clearAlerts)
self.toolbar.addAction(delete_description_action)
convert_action_img_path = os.path.join(base_path, "img/convert.png") # 변환 아이콘 경로 설정
convert_action = QtWidgets.QAction(QtGui.QIcon(convert_action_img_path), "변환\n(Ctrl+R)", self)
convert_action.triggered.connect(self.convertFiles)
self.toolbar.addAction(convert_action)
# 상태바 추가
self.status_bar = self.statusBar()
self.status_bar.showMessage("준비 완료")
self.progress_bar = QtWidgets.QProgressBar()
self.progress_bar.setMaximumWidth(200)
self.status_bar.addPermanentWidget(self.progress_bar)
self.progress_bar.setVisible(False)
# 메인 위젯 설정
main_widget = QtWidgets.QWidget()
self.setCentralWidget(main_widget)
main_layout = QtWidgets.QHBoxLayout(main_widget)
# 파일 리스트 설정
file_list_widget = QtWidgets.QWidget()
file_list_layout = QtWidgets.QVBoxLayout(file_list_widget)
self.tree = QtWidgets.QTreeWidget()
self.tree.setColumnCount(4)
self.tree.setHeaderLabels(['파일명', '파일경로', '채널', '파일크기'])
self.tree.setColumnWidth(0, 400) # 파일명 너비 설정
self.tree.setColumnWidth(1, 450) # 파일경로 너비 설정
self.tree.setColumnWidth(2, 50) # 채널 너비 설정
self.tree.setColumnWidth(3, 50) # 파일크기 너비 설정
self.tree.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.tree.header().sectionClicked.connect(self.onHeaderClicked)
self.tree.itemDoubleClicked.connect(self.onItemDoubleClicked)
# 헤더 라벨 가운데 정렬
header = self.tree.header()
for i in range(self.tree.columnCount()):
header.setDefaultAlignment(QtCore.Qt.AlignCenter)
file_list_layout.addWidget(self.tree)
# 저장 경로 설정 위젯 추가
path_layout = QtWidgets.QHBoxLayout()
self.path_label = QtWidgets.QLabel("저장 경로:")
self.path_edit = QtWidgets.QLineEdit(self.default_save_path)
self.path_edit.setReadOnly(True) # 읽기 전용으로 설정
self.path_button = QtWidgets.QPushButton("경로 선택")
self.path_button.clicked.connect(self.selectSavePath)
self.open_path_button = QtWidgets.QPushButton("경로 열기")
self.open_path_button.clicked.connect(self.openSavePath)
path_layout.addWidget(self.path_label)
path_layout.addWidget(self.path_edit)
path_layout.addWidget(self.path_button)
path_layout.addWidget(self.open_path_button)
file_list_layout.addLayout(path_layout)
# 알림창 설정
self.alert_text = QtWidgets.QTextEdit()
self.alert_text.setReadOnly(True)
self.alert_text.setAcceptRichText(True)
file_list_layout.addWidget(self.alert_text)
main_layout.addWidget(file_list_widget)
# 설정 창 추가
self.settings_panel = self.createSettingsPanel()
self.settings_panel.setFixedWidth(200) # 너비를 200으로 설정
main_layout.addWidget(self.settings_panel, 0)
def createSettingsPanel(self):
settings_panel = QtWidgets.QWidget()
settings_layout = QtWidgets.QVBoxLayout(settings_panel)
settings_label = QtWidgets.QLabel("설정")
settings_label.setAlignment(QtCore.Qt.AlignCenter)
settings_label.setStyleSheet("font-weight: bold; font-size: 14px;") # 글자를 굵게 하고 크기를 16px로 설정
settings_layout.addWidget(settings_label)
# 설정 라벨과 채널 선택 라벨 사이에 작은 간격 추가
settings_layout.addSpacing(20) # 10 픽셀 간격 추가
# 채널 선택 드롭다운 메뉴 추가
channel_label = QtWidgets.QLabel("# 채널 선택")
channel_label.setAlignment(QtCore.Qt.AlignLeft)
settings_layout.addWidget(channel_label)
self.channel_combo = QtWidgets.QComboBox()
self.channel_combo.addItems(self.channel_options)
self.channel_combo.setEnabled(False) # 초기에는 비활성화
self.channel_combo.currentIndexChanged.connect(self.onChannelChanged)
settings_layout.addWidget(self.channel_combo)
# 상단 정렬을 위해 빈 공간 추가
settings_layout.addStretch()
return settings_panel
def onFileSelectionChanged(self):
selected_items = self.tree.selectedItems()
if selected_items:
self.channel_combo.setEnabled(True)
current_channel = selected_items[0].text(2)
self.channel_combo.setCurrentText(current_channel)
else:
self.channel_combo.setEnabled(False)
def onChannelChanged(self):
selected_items = self.tree.selectedItems()
if selected_items:
new_channel = self.channel_combo.currentText()
for item in selected_items:
item.setText(2, new_channel)
self.updateChannelInfo(item.text(1), item.text(0), new_channel)
self.updateAlertText(f"채널 변경", [f"{item.text(0)}의 채널이 {new_channel}(으)로 변경되었습니다." for item in selected_items])
def selectSavePath(self):
selected_path = QtWidgets.QFileDialog.getExistingDirectory(self, "저장 경로 선택", self.default_save_path)
if selected_path:
self.default_save_path = selected_path
self.path_edit.setText(selected_path)
def openSavePath(self):
if os.path.exists(self.default_save_path):
os.startfile(self.default_save_path)
else:
self.showWarning("경고", "경로가 존재하지 않습니다")
def openCalculator(self):
subprocess.Popen('calc.exe')
def centerWindow(self):
frame_geometry = self.frameGeometry()
screen_center = QtWidgets.QDesktopWidget().availableGeometry().center()
frame_geometry.moveCenter(screen_center)
self.move(frame_geometry.topLeft())
def centerDialog(self, dialog):
dialog_geometry = dialog.frameGeometry()
dialog_geometry.moveCenter(self.frameGeometry().center())
dialog.move(dialog_geometry.topLeft())
def FilesOpen(self):
file_paths, _ = QtWidgets.QFileDialog.getOpenFileNames(
self, "파일 열기", self.last_opened_dir, "DBC 파일 (*.dbc);;모든 파일 (*.*)"
)
if file_paths:
self.last_opened_dir = os.path.dirname(file_paths[0])
self.file_paths = file_paths # 새로운 경로로 덮어쓰기
existing_files = [self.tree.topLevelItem(i).text(0) for i in range(self.tree.topLevelItemCount())]
duplicate_files = [os.path.split(file_path)[1] for file_path in file_paths if os.path.split(file_path)[1] in existing_files]
new_files = [file_path for file_path in file_paths if os.path.split(file_path)[1] not in existing_files]
if duplicate_files:
self.showDuplicateFilesWarning(duplicate_files, new_files, [os.path.split(file_path)[1] for file_path in new_files])
self.updateAlertText(f"중복 파일 경고", duplicate_files)
if new_files:
self.populateTreeView(new_files)
self.updateAlertText(f"파일 추가 완료", [os.path.split(file_path)[1] for file_path in new_files])
self.saveSettings()
def deleteSelectedFiles(self):
selected_items = self.tree.selectedItems()
if not selected_items:
self.showWarning("경고", "선택된 파일이 없습니다")
self.updateAlertText("파일 삭제 실패", ["선택된 파일이 없습니다"])
return
deleted_files = [item.text(0) for item in selected_items]
for item in selected_items:
index = self.tree.indexOfTopLevelItem(item)
file_path = os.path.join(item.text(1), item.text(0))
self.removeChannelInfo(file_path) # 채널 정보 제거
self.tree.takeTopLevelItem(index)
self.file_paths = [self.tree.topLevelItem(i).text(1) + '/' + self.tree.topLevelItem(i).text(0) for i in range(self.tree.topLevelItemCount())]
self.updateAlertText(f"파일 삭제 완료", deleted_files)
self.saveSettings()
def deleteAllFiles(self):
if self.tree.topLevelItemCount() == 0:
self.showWarning("경고", "삭제할 파일이 없습니다")
self.updateAlertText("모든 파일 삭제 실패", ["삭제할 파일이 없습니다"])
return
deleted_files = [self.tree.topLevelItem(i).text(0) for i in range(self.tree.topLevelItemCount())]
for i in range(self.tree.topLevelItemCount()):
item = self.tree.topLevelItem(i)
file_path = os.path.join(item.text(1), item.text(0))
self.removeChannelInfo(file_path) # 채널 정보 제거
self.tree.clear()
self.file_paths = []
self.updateAlertText(f"모든 파일 삭제 완료", deleted_files)
self.saveSettings()
def removeChannelInfo(self, file_path):
if "channel_info" in self.settings and file_path in self.settings["channel_info"]:
del self.settings["channel_info"][file_path]
self.saveSettings()
print(f"[INFO] Removed channel info for {file_path}")
def clearAlerts(self):
self.alert_text.clear()
def showWarning(self, title, message):
warning_box = QtWidgets.QMessageBox(self)
warning_box.setIcon(QtWidgets.QMessageBox.Warning)
warning_box.setWindowTitle(title)
warning_box.setText(message)
warning_box.exec_()
def showDuplicateFilesWarning(self, duplicate_files, new_files, new_files_names):
duplicate_files_str = "\n".join(duplicate_files)
new_files_str = "\n".join(new_files_names)
if new_files:
self.showWarning("중복 파일 경고", f"**중복 파일이 존재합니다**\n\n{duplicate_files_str}\n\n\n**중복 파일을 제외한 신규 파일을 추가합니다**\n\n{new_files_str}")
else:
self.showWarning("중복 파일 경고", f"**중복 파일이 존재합니다**\n\n{duplicate_files_str}")
def sortTreeView(self, col, reverse):
items = []
for i in range(self.tree.topLevelItemCount()):
item = self.tree.takeTopLevelItem(0)
items.append(item)
if col == 3: # 파일크기 정렬
items.sort(key=lambda x: float(x.text(col).replace(" KB", "")), reverse=reverse)
else:
items.sort(key=lambda x: x.text(col), reverse=reverse)
for item in items:
self.tree.addTopLevelItem(item)
def selectAllFiles(self):
for i in range(self.tree.topLevelItemCount()):
self.tree.topLevelItem(i).setSelected(True)
def invertSelection(self):
for i in range(self.tree.topLevelItemCount()):
item = self.tree.topLevelItem(i)
item.setSelected(not item.isSelected())
def setWindowSize(self, size):
if size == "small":
self.resize(600, 400)
elif size == "medium":
self.resize(800, 600)
elif size == "large":
self.resize(1200, 1000)
elif size == "default":
self.resize(1250, 600)
elif size == "auto":
self.adjustSize()
def openSettings(self):
settings_window = QtWidgets.QDialog(self)
settings_window.setWindowTitle("설정")
settings_window.setGeometry(100, 100, 400, 300)
layout = QtWidgets.QVBoxLayout(settings_window)
label = QtWidgets.QLabel("설정 창입니다.")
label.setAlignment(QtCore.Qt.AlignCenter)
layout.addWidget(label)
self.centerDialog(settings_window)
settings_window.exec_()
def openAbout(self):
about_window = QtWidgets.QDialog(self)
about_window.setWindowTitle("프로그램 정보")
about_window.setGeometry(100, 100, 400, 300)
layout = QtWidgets.QVBoxLayout(about_window)
label1 = QtWidgets.QLabel("이 프로그램은 DBC 파일을 C 파일로 변환합니다.")
label1.setAlignment(QtCore.Qt.AlignCenter)
label3 = QtWidgets.QLabel(f"버전: {self.version}")
label3.setAlignment(QtCore.Qt.AlignCenter)
layout.addWidget(label1)
layout.addWidget(label3)
self.centerDialog(about_window)
about_window.exec_()
def populateTreeView(self, file_paths):
def format_file_size(size_in_bytes):
size_in_kb = size_in_bytes / 1024
return f"{size_in_kb:.2f} KB"
for file_path in file_paths:
directory, file = os.path.split(file_path)
fsize = format_file_size(os.path.getsize(file_path))
item = QtWidgets.QTreeWidgetItem([file, directory, self.channel_options[0], fsize])
self.tree.addTopLevelItem(item)
self.updateChannelInfo(directory, file, self.channel_options[0]) # 기본 채널을 0으로 설정
self.file_paths = [self.tree.topLevelItem(i).text(1) + '/' + self.tree.topLevelItem(i).text(0) for i in range(self.tree.topLevelItemCount())]
self.saveSettings()
def updateAlertText(self, message, file_list):
current_time = QtCore.QDateTime.currentDateTime().toString("yy-MM-dd-ddd HH:mm:ss")
self.alert_text.append(f"[{current_time}] {message}")
for file in file_list:
self.alert_text.append(f" - {file}")
self.alert_text.append("") # 한 줄 띄우기
def convertFiles(self):
if not self.file_paths:
self.showWarning("경고", "변환할 파일이 없습니다")
self.updateAlertText("변환 실패", ["변환할 파일이 없습니다"])
return
self.status_bar.showMessage("변환 중...")
self.progress_bar.setVisible(True)
self.progress_bar.setValue(0)
total_files = len(self.file_paths)
timestamp = datetime.now().strftime("%y-%m-%d-%a-%H-%M-%S") # 날짜 및 시간 형식 변경
base_output_dir = os.path.join(self.default_save_path, "DBC 변환", timestamp)
for index, file_path in enumerate(self.file_paths):
file_name = os.path.splitext(os.path.basename(file_path))[0]
rx_output_dir = os.path.join(base_output_dir, file_name, "RX")
tx_output_dir = os.path.join(base_output_dir, file_name, "TX")
common_output_dir = os.path.join(base_output_dir, file_name, "Common")
dbc_output_dir = os.path.join(base_output_dir, "#DBC") # DBC 파일 저장 경로 설정
os.makedirs(rx_output_dir, exist_ok=True)
os.makedirs(tx_output_dir, exist_ok=True)
os.makedirs(common_output_dir, exist_ok=True)
os.makedirs(dbc_output_dir, exist_ok=True) # DBC 파일 저장 경로 생성
channel_info = self.settings.get("channel_info", {}).get(os.path.basename(file_path), "CH0")
try:
shutil.copy(file_path, dbc_output_dir) # DBC 파일 복사
if hasattr(sys, '_MEIPASS'):
script_path = os.path.join(sys._MEIPASS, "DBC_Converter_RX.py")
else:
script_path = "DBC_Converter_RX.py"
subprocess.run(["python", script_path, file_path, rx_output_dir, json.dumps({os.path.basename(file_path): channel_info})], check=True)
if hasattr(sys, '_MEIPASS'):
script_path = os.path.join(sys._MEIPASS, "DBC_Converter_TX.py")
else:
script_path = "DBC_Converter_TX.py"
subprocess.run(["python", script_path, file_path, tx_output_dir, json.dumps({os.path.basename(file_path): channel_info})], check=True)
if hasattr(sys, '_MEIPASS'):
script_path = os.path.join(sys._MEIPASS, "DBC_Converter_Common.py")
else:
script_path = "DBC_Converter_Common.py"
subprocess.run(["python", script_path, file_path, common_output_dir, json.dumps({os.path.basename(file_path): channel_info})], check=True)
self.updateAlertText(f"변환 성공", [file_path])
except subprocess.CalledProcessError as e:
self.updateAlertText(f"변환 실패: {e}", [file_path])
self.progress_bar.setValue(int((index + 1) / total_files * 100))
self.status_bar.showMessage("변환 완료")
self.progress_bar.setVisible(False)
def onHeaderClicked(self, logicalIndex):
header = self.tree.headerItem()
if header.text(logicalIndex).endswith(""):
self.sortTreeView(logicalIndex, True)
header.setText(logicalIndex, header.text(logicalIndex).replace("", ""))
else:
self.sortTreeView(logicalIndex, False)
header.setText(logicalIndex, header.text(logicalIndex).replace("", ""))
def onItemDoubleClicked(self, item, column):
if column == 0: # 파일명을 더블클릭한 경우
file_path = os.path.join(item.text(1), item.text(0))
os.startfile(file_path)
elif column == 1: # 경로를 더블클릭한 경우
os.startfile(item.text(1))
elif column == 2: # 채널을 더블클릭한 경우
self.openChannelDialog(item)
def openChannelDialog(self, item):
channel_dialog = QtWidgets.QDialog(self)
channel_dialog.setWindowTitle("채널 선택")
channel_dialog.setGeometry(100, 100, 200, 100)
layout = QtWidgets.QVBoxLayout(channel_dialog)
channel_combo = QtWidgets.QComboBox()
channel_combo.addItems(self.channel_options)
current_channel = item.text(2)
channel_combo.setCurrentText(current_channel) # 현재 채널을 콤보박스에 설정
layout.addWidget(channel_combo)
button_layout = QtWidgets.QHBoxLayout()
apply_button = QtWidgets.QPushButton("확인")
apply_button.clicked.connect(lambda: self.applyChannelSelection(item, channel_combo, channel_dialog))
button_layout.addWidget(apply_button)
cancel_button = QtWidgets.QPushButton("취소")
cancel_button.clicked.connect(channel_dialog.close)
button_layout.addWidget(cancel_button)
layout.addLayout(button_layout)
self.centerDialog(channel_dialog)
channel_dialog.exec_()
def applyChannelSelection(self, item, combo, dialog):
item.setText(2, combo.currentText())
self.updateChannelInfo(item.text(1), item.text(0), combo.currentText()) # 채널 정보 업데이트
dialog.close()
def updateChannelInfo(self, directory, filename, channel):
file_path = os.path.join(directory, filename)
file_name = os.path.basename(file_path)
if "channel_info" not in self.settings:
self.settings["channel_info"] = {}
self.settings["channel_info"][file_name] = channel
self.saveSettings()
print(f"[INFO] Updated channel info for {file_name} to {channel}")
def setupCloseEvent(self):
self.closeEvent = self.onClose # 종료 이벤트 핸들러 설정
def onClose(self, event):
self.file_paths = [] # 파일 경로 초기화
self.settings["file_paths"] = [] # 설정 파일 경로 초기화
self.settings["channel_info"] = {} # 채널 정보 초기화
self.saveSettings() # 설정 저장
event.accept() # 종료 이벤트 수락
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
main_view = MainView()
main_view.show()
sys.exit(app.exec_())

View File

@ -0,0 +1,77 @@
import os
import sys
from DBC_Converter_Data_Parsing import load_dbc_file
def generate_structs(signals, output_file):
if not signals:
print("[WARNING] No signals to generate structs for.")
return
# Ensure the output directory exists
output_dir = os.path.dirname(output_file)
if not os.path.exists(output_dir):
os.makedirs(output_dir)
with open(output_file, 'w') as f:
f.write("#ifndef GENERATED_STRUCTS_H\n")
f.write("#define GENERATED_STRUCTS_H\n\n")
f.write("#include <stdint.h>\n\n")
tx_structs = {}
rx_structs = {}
for message_name, message_info in signals.items():
hex_id = message_info["ID"]
if message_info["TX ECU name"] == "VCU":
struct_name = f"{message_name}_{hex_id}"
tx_structs[struct_name] = []
for signal in message_info["Signals"]:
if signal["Length"] <= 32:
tx_structs[struct_name].append(f" uint32_t {signal['Signal name']} : {signal['Length']};")
else:
tx_structs[struct_name].append(f" float {signal['Signal name']};")
for signal in message_info["Signals"]:
if signal["RX ECU name"] == "VCU":
struct_name = f"{message_name}_{hex_id}"
if struct_name not in rx_structs:
rx_structs[struct_name] = []
if signal["Length"] <= 32:
rx_structs[struct_name].append(f" uint32_t {signal['Signal name']} : {signal['Length']};")
else:
rx_structs[struct_name].append(f" float {signal['Signal name']};")
f.write("typedef struct {\n")
f.write(" typedef struct {\n")
for struct_name, fields in tx_structs.items():
f.write(f" typedef struct {{\n")
for field in fields:
f.write(f" {field}\n")
f.write(f" }} {struct_name};\n\n")
f.write(" } TX;\n\n")
f.write(" typedef struct {\n")
for struct_name, fields in rx_structs.items():
f.write(f" typedef struct {{\n")
for field in fields:
f.write(f" {field}\n")
f.write(f" }} {struct_name};\n\n")
f.write(" } RX;\n")
f.write("} VCU;\n\n")
f.write("#endif // GENERATED_STRUCTS_H\n")
print(f"[INFO] Structs written to {output_file}")
if __name__ == "__main__":
dbc_file_path = sys.argv[1]
output_dir = sys.argv[2]
output_structs_file = f"{output_dir}/generated_structs.h"
signals = load_dbc_file(dbc_file_path)
if signals is None:
print(f"[ERROR] Failed to load DBC file: {dbc_file_path}")
sys.exit(1)
generate_structs(signals, output_structs_file)

View File

@ -0,0 +1,73 @@
import re, sys
def load_dbc_file(file_path):
try:
with open(file_path, 'r') as file:
content = file.read()
# print(f"[INFO] Successfully loaded DBC file: {file_path}")
# 메시지와 시그널을 추출하는 정규 표현식
message_pattern = re.compile(r'BO_\s+(\d+)\s+(\w+)\s*:\s*(\d+)\s+(\w+)')
signal_pattern = re.compile(r'SG_\s+(\w+)\s*:\s*(\d+)\|(\d+)@(\d+)([+-])\s*\(([^,]+),\s*([^)]+)\)\s*\[[^\]]+\]\s*\"[^\"]*\"\s+(\w+)')
# 메시지와 시그널 매칭
messages = {}
current_message = None
for line in content.splitlines():
line = line.strip()
if not line:
current_message = None
continue
message_match = message_pattern.match(line)
if message_match:
decimal_id = int(message_match.group(1))
hex_id = f"0x{decimal_id:03X}"
message_name = message_match.group(2)
dlc = int(message_match.group(3))
tx_ecu_name = message_match.group(4)
current_message = message_name
messages[current_message] = {
"ID": hex_id,
"DLC": dlc,
"TX ECU name": tx_ecu_name,
"Signals": []
}
elif current_message:
signal_match = signal_pattern.match(line)
if signal_match:
signal_name = signal_match.group(1)
msb = int(signal_match.group(2))
length = int(signal_match.group(3))
byte_order = int(signal_match.group(4))
sign = signal_match.group(5)
factor = float(signal_match.group(6))
offset = float(signal_match.group(7))
rx_ecu_name = signal_match.group(8)
messages[current_message]["Signals"].append({
"Signal name": signal_name,
"msb": msb,
"Length": length,
"Byte order": byte_order,
"Sign": sign,
"Factor": factor,
"Offset": offset,
"RX ECU name": rx_ecu_name
})
return messages
except Exception as e:
print(f"[ERROR] Failed to parse DBC file: {e}")
return None
if __name__ == "__main__":
file_path = sys.argv[1]
messages = load_dbc_file(file_path)
if messages:
for message_name, message_info in messages.items():
print(f"[INFO] Message: {message_name}, Info: {message_info['ID']}, {message_info['DLC']}, {message_info['TX ECU name']}")
for signal in message_info['Signals']:
print(f" └ Signal: {signal}")

View File

@ -0,0 +1,207 @@
import sys
from DBC_Converter_Data_Parsing import load_dbc_file
#============================== Generate Globals ==============================#
def generate_globals(signals, C_file, header_file):
if not signals:
print("[WARNING] No signals to generate globals for.")
return
with open(C_file, 'w') as f:
f.write("#include <generated_RX_globals.h>\n")
for message_name, message_info in signals.items():
for signal in message_info["Signals"]:
if signal["Length"] > 32:
f.write(f"float GV_{signal['Signal name']} = 0.0f;\n")
else:
f.write(f"uint32_t GV_{signal['Signal name']} = 0;\n")
with open(header_file, 'w') as f:
f.write("#ifndef GENERATED_GLOBALS_H\n")
f.write("#define GENERATED_GLOBALS_H\n\n")
f.write("#include <stdint.h>\n\n")
for message_name, message_info in signals.items():
for signal in message_info["Signals"]:
if signal["Length"] > 32:
f.write(f"extern float GV_{signal['Signal name']};\n")
else:
f.write(f"extern uint32_t GV_{signal['Signal name']};\n")
f.write("\n#endif // GENERATED_GLOBALS_H\n")
print(f"[INFO] Globals and extern declarations written to {C_file} and {header_file}")
#============================== Generate VCU RX Function ==============================#
def generate_vcu_rx_function_with_factors(signals, output_file):
if not signals:
print("[ERROR] No signals to generate VCU RX functions for.")
return
# Initialize the header of the C file
c_file_content = """
#include "can.h"
// Declare Factors and Offsets for signals
"""
# Collect unique Factors and Offsets
factors_offsets = {}
for message_name, message_info in signals.items():
for signal in message_info["Signals"]:
factor_name = f"Factor_{str(signal['Factor']).replace('.', '_')}"
offset_name = f"Offset_m_{abs(signal['Offset']):.0f}"
if signal['Factor'] != 1:
factors_offsets[factor_name] = signal['Factor']
if signal['Offset'] != 0:
factors_offsets[offset_name] = signal['Offset']
# Add Factor and Offset variable declarations
for name, value in factors_offsets.items():
c_file_content += f"const float {name} = {value};\n"
# Add a newline for separation
c_file_content += "\n"
# Iterate through all messages in the signals
for message_name, message_info in signals.items():
# Define the temporary struct
temp_struct_name = f"{message_name}_temp"
# Check if any signal in the message has RX ECU name as VCU
has_vcu_signal = any(signal["RX ECU name"] == "VCU" for signal in message_info["Signals"])
if not has_vcu_signal:
continue # Skip this message if no signal has RX ECU name as VCU
c_file_content += f"""
void Receive_{message_name}_{message_info['ID']}(void)
{{
struct {{
"""
# Add temporary variables to the struct
for signal in message_info["Signals"]:
if signal["RX ECU name"] != "VCU":
continue # RX ECU name이 VCU가 아닌 경우 건너뜁니다.
if signal["Sign"] == "-":
signal_type = "signed int"
elif signal["Sign"] == "+":
signal_type = "unsigned int"
c_file_content += f" {signal_type} {signal['Signal name']}_temp : {signal['Length']};\n"
c_file_content += f" }} {temp_struct_name};\n\n"
# Add temp assignments
for signal in message_info["Signals"]:
if signal["RX ECU name"] != "VCU":
continue # RX ECU name이 VCU가 아닌 경우 건너뜁니다.
start_byte = signal["msb"] // 8
start_bit = signal["msb"] % 8
signal_length = signal["Length"]
if signal["Byte order"] == 0: # Motorola (Big Endian)
lsb = signal["msb"] + 8*(signal_length // 8) - (signal_length % 8 - 1)
if signal_length > 8:
# Handle multi-byte signals
shift_expr = f"(CAN_ch[0].rx.buf[{start_byte}] << shift{7 - start_bit})"
multi_byte_expr = []
for i in range((signal_length + 7) // 8):
byte_shift = (7 - start_bit) + (i * 8)
if i == 0:
multi_byte_expr.append(f"(CAN_ch[0].rx.buf[{start_byte + i}] << shift{7 - start_bit})")
else:
multi_byte_expr.append(f"(CAN_ch[0].rx.buf[{start_byte + i}] >> shift{byte_shift})")
shift_expr = " | ".join(multi_byte_expr)
else :
shift_expr = f"(CAN_ch[0].rx.buf[{start_byte}] >> shift{lsb % 8})"
else: # Intel (Little Endian)
shift_expr = f"(CAN_ch[0].rx.buf[{start_byte}] >> shift{start_bit})"
if signal_length > 8:
# Handle multi-byte signals
multi_byte_expr = []
for i in range((signal_length + 7) // 8):
byte_shift = i * 8
if i == 0:
multi_byte_expr.append(f"(CAN_ch[0].rx.buf[{start_byte + i}] >> shift{start_bit})")
else:
multi_byte_expr.append(f"(CAN_ch[0].rx.buf[{start_byte + i}] << shift{byte_shift})")
shift_expr = " | ".join(multi_byte_expr)
c_file_content += f" {temp_struct_name}.{signal['Signal name']}_temp = ({shift_expr}) & _{signal_length}bit;\n"
c_file_content += "\n"
# Assign to final ECU variables
for signal in message_info["Signals"]:
if signal["RX ECU name"] != "VCU":
continue # RX ECU name이 VCU가 아닌 경우 건너뜁니다.
factor_name = f"Factor_{str(signal['Factor']).replace('.', '_')}"
offset_name = f"Offset_m_{abs(signal['Offset']):.0f}"
factor = f" * {factor_name}" if signal['Factor'] != 1 else ""
offset = f" + {offset_name}" if signal['Offset'] != 0 else ""
temp_var = f"{temp_struct_name}.{signal['Signal name']}_temp"
c_file_content += f" VCU.RX.{message_name}_{message_info['ID']}.{signal['Signal name']} = ({temp_var}{factor}){offset};\n"
c_file_content += "}\n"
# Write the generated code to a single C file
with open(output_file, "w") as c_file:
c_file.write(c_file_content)
print(f"Generated RX function C file with Factors and Offsets: {output_file}")
#============================== Generate Input Function ==============================#
def generate_input_functions(signals, output_file):
if not signals:
print("[WARNING] No signals to generate Input functions for.")
return
with open(output_file, 'w') as f:
for message_name, message_info in signals.items():
hex_id = message_info["ID"]
function_name = f"void Input_Data_Set_{message_name}_CH0_{hex_id}(void)"
f.write(f"{function_name}\n{{\n")
for signal in message_info["Signals"]:
f.write(f" GV_{signal['Signal name']} = VCU.RX.CH0_RX_{message_name}_{hex_id}.{signal['Signal name']};\n")
f.write("}\n\n")
print(f"[INFO] Input functions written to {output_file}")
#============================== Generate Initialization ==============================#
def generate_initialization(signals, output_file):
if not signals:
print("[WARNING] No signals to generate initialization for.")
return
with open(output_file, 'w') as f:
f.write("void VCU_Data_Init(void)\n{\n")
for message_name, message_info in signals.items():
hex_id = message_info["ID"]
struct_prefix = f"VCU.RX.CH0_RX_{message_name}_{hex_id}"
for signal in message_info["Signals"]:
if signal["Offset"] != 0.0:
if signal["Length"] <= 32:
f.write(f" {struct_prefix}.{signal['Signal name']} = {signal['Offset']};\n")
else:
f.write(f" {struct_prefix}.{signal['Signal name']} = {signal['Offset']}f;\n")
else:
if signal["Length"] <= 32:
f.write(f" {struct_prefix}.{signal['Signal name']} = 0;\n")
else:
f.write(f" {struct_prefix}.{signal['Signal name']} = 0.0f;\n")
f.write("\n")
f.write("}\n")
print(f"[INFO] Initialization function written to {output_file}")
#============================== Main ==============================#
if __name__ == "__main__":
dbc_file_path = sys.argv[1]
output_dir = sys.argv[2]
output_globals_C_file = f"{output_dir}/generated_RX_globals.c"
output_globals_header_file = f"{output_dir}/generated_RX_globals.h"
output_c_file = f"{output_dir}/generated_RX_receive.c"
output_input_file = f"{output_dir}/generated_RX_input.c"
output_initialization_file = f"{output_dir}/generated_RX_init.c"
signals = load_dbc_file(dbc_file_path)
generate_globals(signals, output_globals_C_file, output_globals_header_file)
generate_vcu_rx_function_with_factors(signals, output_c_file)
generate_input_functions(signals, output_input_file)
generate_initialization(signals, output_initialization_file)

View File

@ -0,0 +1,192 @@
import sys
from DBC_Converter_Data_Parsing import load_dbc_file
#============================== Generate TX Globals ==============================#
def generate_tx_globals(signals, output_file, header_file):
if not signals:
print("[WARNING] No signals to generate TX globals for.")
return
with open(output_file, 'w') as f:
for message_name, message_info in signals.items():
for signal in message_info["Signals"]:
if signal["Length"] > 32:
f.write(f"float GV_{signal['Signal name']} = 0.0f;\n")
else:
f.write(f"uint32_t GV_{signal['Signal name']} = 0;\n")
with open(header_file, 'w') as f:
f.write("#ifndef GENERATED_TX_GLOBALS_H\n")
f.write("#define GENERATED_TX_GLOBALS_H\n\n")
f.write("#include <stdint.h>\n\n")
for message_name, message_info in signals.items():
for signal in message_info["Signals"]:
if signal["Length"] > 32:
f.write(f"extern float GV_{signal['Signal name']};\n")
else:
f.write(f"extern uint32_t GV_{signal['Signal name']};\n")
f.write("\n#endif // GENERATED_TX_GLOBALS_H\n")
print(f"[INFO] TX globals written to {output_file} and {header_file}")
#============================== Generate TX Functions ==============================#
def generate_tx_functions(signals, output_file):
if not signals:
print("[WARNING] No signals to generate TX functions for.")
return
with open(output_file, 'w') as f:
for message_name, message_info in signals.items():
hex_id = message_info["ID"]
function_name = f"void Output_Data_Set_{message_name}_CH0_{hex_id}(void)"
f.write(f"{function_name}\n{{\n")
for signal in message_info["Signals"]:
factor_str = f"/ {signal['Factor']}" if signal["Factor"] != 1.0 else ""
offset_str = f"- {signal['Offset']}" if signal["Offset"] != 0.0 else ""
mask = f"_{signal['Length']}bit"
f.write(
f" VCU.TX.CH0_{message_name}_{hex_id}.{signal['Signal name']} = "
f"(int)((GV_{signal['Signal name']} {offset_str}) {factor_str}) & {mask};\n"
)
f.write("}\n\n")
print(f"[INFO] TX functions written to {output_file}")
#============================== Generate TX Initialization ==============================#
def generate_tx_initialization(signals, output_file):
if not signals:
print("[WARNING] No TX signals found for initialization generation.")
return
with open(output_file, 'w') as f:
f.write("void Initialize_TX_Signals(void)\n{\n")
for message_name, message_info in signals.items():
hex_id = message_info["ID"]
f.write(f" // {message_name} ({hex_id})\n")
for signal in message_info["Signals"]:
f.write(f" VCU.TX.CH0_{message_name}_{hex_id}.{signal['Signal name']} = 0;\n")
f.write("\n")
f.write("}\n")
print(f"[INFO] TX initialization function written to {output_file}")
#============================== Generate TX Enum ==============================#
def generate_tx_enum(signals, output_file, cycle):
if not signals:
print("[WARNING] No TX messages found for enum generation.")
return
with open(output_file, 'w') as f:
f.write("typedef enum {\n")
for idx, message_name in enumerate(signals.keys()):
enum_name = f"VCU_CH0_TX_{message_name}_{cycle}"
f.write(f" {enum_name} = {idx},\n")
f.write(" NUMBER_OF_VCU_CH0_TX_MESSAGE,\n")
f.write("} VCU_CH0_TX;\n")
print(f"[INFO] TX enum written to {output_file}")
#============================== Generate TX C File ==============================#
def generate_vcu_can_transmit_single_c_file(signals, output_file):
if not signals:
print("[ERROR] No signals to generate VCU CAN transmit functions for.")
return
# Initialize the header of the C file
c_file_content = """
#include "can.h"
"""
# Iterate through all messages in the signals
for message_name, message_info in signals.items():
# Check if any signal in the message has RX ECU name as VCU
has_vcu_signal = any(signal["RX ECU name"] != "VCU" for signal in message_info["Signals"])
if not has_vcu_signal:
continue # Skip this message if no signal has RX ECU name as VCU
# Add the function definition for the message
c_file_content += f"""
void Transmit_{message_name}_CH0_{message_info['ID']}(void)
{{
"""
# Iterate through signals and generate bit-packing logic
buffer_assignments = [""] * message_info["DLC"]
for signal in message_info["Signals"]:
start_byte = signal["msb"] // 8
start_bit = signal["msb"] % 8
signal_length = signal["Length"]
signal_name = f"VCU.TX.CH0_{message_name}_{message_info['ID']}.{signal['Signal name']}"
# Handle 8-bit chunks
while signal_length > 0:
bits_in_byte = min(8 - start_bit, signal_length)
shift_amount = (signal["Length"] - signal_length)
if signal["Byte order"] == 0: # Motorola (Big Endian)
if bits_in_byte == 8:
shift_expr = f"({signal_name} >> shift{shift_amount})"
else:
shift_expr = f"({signal_name} << shift{start_bit})" if start_bit > 0 else f"({signal_name} >> shift{shift_amount})"
else: # Intel (Little Endian)
shift_expr = f"({signal_name} >> shift{shift_amount}) << shift{start_bit}" if start_bit > 0 else f"({signal_name} >> shift{shift_amount})"
buffer_index = start_byte
# Ensure buffer_index is within the range of buffer_assignments
if buffer_index >= len(buffer_assignments):
print(f"[ERROR] Buffer index {buffer_index} out of range for message {message_name}")
break
# Add to the buffer assignments
if buffer_assignments[buffer_index]:
buffer_assignments[buffer_index] += f"\n | {shift_expr}"
else:
buffer_assignments[buffer_index] = f"{shift_expr}"
# Update pointers
signal_length -= bits_in_byte
start_byte += 1
start_bit = 0
# Write buffer assignments to the function
for i, assignment in enumerate(buffer_assignments):
if assignment:
c_file_content += f" CAN_ch[0].tx.buf[{i}] = ({assignment}) & _8bit;\n"
# Add the send function call
c_file_content += f"""
can_send_config(CAN_INST_0, g_messageObjectConf_VCU_0ch_TX[VCU_CH0_TX_{message_name}_10ms]);
}}
"""
# Write the generated code to a single C file
with open(output_file, "w") as c_file:
c_file.write(c_file_content)
print(f"Generated C file with all VCU messages: {output_file}")
#============================== Main ==============================#
if __name__ == "__main__":
dbc_file_path = sys.argv[1]
output_dir = sys.argv[2]
output_structs_file = f"{output_dir}/generated_TX_structs.h"
output_globals_file = f"{output_dir}/generated_TX_globals.c"
output_globals_header = f"{output_dir}/generated_TX_globals.h"
output_tx_functions_file = f"{output_dir}/generated_TX_output.c"
output_tx_initialization = f"{output_dir}/generated_TX_init.c"
output_enum_file = f"{output_dir}/generated_TX_enum.h"
output_c_file = f"{output_dir}/generated_TX_transmit.c" # Replace with your desired output file name
cycle_time = "10ms"
signals = load_dbc_file(dbc_file_path)
if signals is None:
print(f"[ERROR] Failed to load DBC file: {dbc_file_path}")
sys.exit(1)
generate_tx_globals(signals, output_globals_file, output_globals_header)
generate_tx_functions(signals, output_tx_functions_file)
generate_tx_initialization(signals, output_tx_initialization)
generate_tx_enum(signals, output_enum_file, cycle_time)
generate_vcu_can_transmit_single_c_file(signals, output_c_file)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015-2019 Erik Moqvist
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,509 +0,0 @@
Metadata-Version: 2.1
Name: cantools
Version: 40.0.0
Summary: CAN BUS tools.
Author-email: Erik Moqvist <erik.moqvist@gmail.com>
License: MIT
Project-URL: homepage, https://github.com/cantools/cantools
Project-URL: documentation, https://cantools.readthedocs.io/
Project-URL: repository, https://github.com/cantools/cantools
Keywords: can,can bus,arxml,dbc,kcd,automotive
Classifier: License :: OSI Approved :: MIT License
Requires-Python: >=3.9
Description-Content-Type: text/x-rst
License-File: LICENSE
Requires-Dist: bitstruct>=8.16.1
Requires-Dist: python-can>=3.3.4
Requires-Dist: textparser>=0.21.1
Requires-Dist: diskcache
Requires-Dist: argparse_addons
Requires-Dist: crccheck
Provides-Extra: dev
Requires-Dist: mypy; extra == "dev"
Requires-Dist: pipx; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: tox; extra == "dev"
Provides-Extra: plot
Requires-Dist: matplotlib; extra == "plot"
Provides-Extra: windows-all
Requires-Dist: windows-curses; (platform_system == "Windows" and platform_python_implementation == "CPython") and extra == "windows-all"
|github-actions| |coverage|
About
=====
CAN BUS tools in Python 3.
- `DBC`_, `KCD`_, SYM, ARXML 3&4 and CDD file parsing.
- CAN message encoding and decoding.
- Simple and extended signal multiplexing.
- Diagnostic DID encoding and decoding.
- ``candump`` output decoder.
- Node `tester`_.
- `C` source code generator.
- CAN bus monitor.
- Graphical plots of signals.
Project homepage: https://github.com/cantools/cantools
Documentation: https://cantools.readthedocs.io
Installation
============
.. code-block:: bash
python3 -m pip install cantools
Example usage
=============
Scripting
---------
The example starts by parsing a `small DBC-file`_ and printing its
messages and signals.
.. code-block:: python
>>> import cantools
>>> from pprint import pprint
>>> db = cantools.database.load_file('tests/files/dbc/motohawk.dbc')
>>> db.messages
[message('ExampleMessage', 0x1f0, False, 8, 'Example message used as template in MotoHawk models.')]
>>> example_message = db.get_message_by_name('ExampleMessage')
>>> pprint(example_message.signals)
[signal('Enable', 7, 1, 'big_endian', False, 1.0, 0, 0.0, 0.0, '-', False, None, {0: 'Disabled', 1: 'Enabled'}, None),
signal('AverageRadius', 6, 6, 'big_endian', False, 0.1, 0, 0.0, 5.0, 'm', False, None, None, ''),
signal('Temperature', 0, 12, 'big_endian', True, 0.01, 250, 229.53, 270.47, 'degK', False, None, None, None)]
The example continues `encoding`_ a message and sending it on a CAN
bus using the `python-can`_ package.
.. code-block:: python
>>> import can
>>> can_bus = can.interface.Bus('vcan0', bustype='socketcan')
>>> data = example_message.encode({'Temperature': 250.1, 'AverageRadius': 3.2, 'Enable': 1})
>>> message = can.Message(arbitration_id=example_message.frame_id, data=data)
>>> can_bus.send(message)
Alternatively, a message can be encoded using the `encode_message()`_
method on the database object.
The last part of the example receives and `decodes`_ a CAN message.
.. code-block:: python
>>> message = can_bus.recv()
>>> db.decode_message(message.arbitration_id, message.data)
{'AverageRadius': 3.2, 'Enable': 'Enabled', 'Temperature': 250.09}
See `examples`_ for additional examples.
Command line tool
-----------------
The decode subcommand
^^^^^^^^^^^^^^^^^^^^^
Decode CAN frames captured with the Linux program ``candump``.
.. code-block:: text
$ candump vcan0 | python3 -m cantools decode tests/files/dbc/motohawk.dbc
vcan0 1F0 [8] 80 4A 0F 00 00 00 00 00 ::
ExampleMessage(
Enable: 'Enabled' -,
AverageRadius: 0.0 m,
Temperature: 255.92 degK
)
vcan0 1F0 [8] 80 4A 0F 00 00 00 00 00 ::
ExampleMessage(
Enable: 'Enabled' -,
AverageRadius: 0.0 m,
Temperature: 255.92 degK
)
vcan0 1F0 [8] 80 4A 0F 00 00 00 00 00 ::
ExampleMessage(
Enable: 'Enabled' -,
AverageRadius: 0.0 m,
Temperature: 255.92 degK
)
Alternatively, the decoded message can be printed on a single line:
.. code-block:: text
$ candump vcan0 | python3 -m cantools decode --single-line tests/files/dbc/motohawk.dbc
vcan0 1F0 [8] 80 4A 0F 00 00 00 00 00 :: ExampleMessage(Enable: 'Enabled' -, AverageRadius: 0.0 m, Temperature: 255.92 degK)
vcan0 1F0 [8] 80 4A 0F 00 00 00 00 00 :: ExampleMessage(Enable: 'Enabled' -, AverageRadius: 0.0 m, Temperature: 255.92 degK)
vcan0 1F0 [8] 80 4A 0F 00 00 00 00 00 :: ExampleMessage(Enable: 'Enabled' -, AverageRadius: 0.0 m, Temperature: 255.92 degK)
The plot subcommand
^^^^^^^^^^^^^^^^^^^
The plot subcommand is similar to the decode subcommand but messages are visualized using `matplotlib`_ instead of being printed to stdout.
.. code-block:: bash
$ candump -l vcan0
$ cat candump-2021-01-04_180521.log
(1609779922.655421) vcan0 00000343#B204B9049C049C04
(1609779922.655735) vcan0 0000024A#120527052E051905
(1609779923.657524) vcan0 00000343#C404C404CB04C404
(1609779923.658086) vcan0 0000024A#8B058B058B059205
(1609779924.659912) vcan0 00000343#5C04790479045504
(1609779924.660471) vcan0 0000024A#44064B0659064406
(1609779925.662277) vcan0 00000343#15040704F203F203
(1609779925.662837) vcan0 0000024A#8B069906A706A706
(1609779926.664191) vcan0 00000343#BC03B503A703BC03
(1609779926.664751) vcan0 0000024A#A006A706C406C406
$ cat candump-2021-01-04_180521.log | python3 -m cantools plot tests/files/dbc/abs.dbc
.. image:: https://github.com/cantools/cantools/raw/master/docs/plot-1.png
If you don't want to show all signals you can select the desired signals with command line arguments.
A ``*`` can stand for any number of any character, a ``?`` for exactly one arbitrary character.
Signals separated by a ``-`` are displayed in separate subplots.
Optionally a format can be specified after a signal, separated by a colon.
.. code-block:: bash
$ cat candump-2021-01-04_180521.log | python3 -m cantools plot tests/files/dbc/abs.dbc '*33.*fl:-<' '*33.*fr:->' - '*33.*rl:-<' '*33.*rr:->'
.. image:: https://github.com/cantools/cantools/raw/master/docs/plot-2-subplots.png
Signals with a different range of values can be displayed in the same subplot on different vertical axes by separating them with a comma.
.. code-block:: bash
$ cat candump-2021-01-04_180521.log | cantools plot --auto-color tests/files/dbc/abs.dbc -- \
--ylabel 'Bremse 33' '*_33.*fl*:-<' '*_33.*fr*:>' '*_33.*rl*:3' '*_33.*rr*:4' , \
--ylabel 'Bremse 2' '*_2.*fl*:-<' '*_2.*fr*:>' '*_2.*rl*:3' '*_2.*rr*:4'
.. image:: https://github.com/cantools/cantools/raw/master/docs/plot-2-axes.png
Matplotlib comes with different preinstalled styles that you can use:
.. code-block:: bash
$ cat candump-2021-01-04_180521.log | cantools plot tests/files/dbc/abs.dbc --style seaborn
.. image:: https://github.com/cantools/cantools/raw/master/docs/plot-seaborn.png
You can try all available styles with
.. code-block:: bash
$ cantools plot --list-styles . | sed -n '/^- /s/^- //p' | while IFS= read -r style; do
cat candump-2021-01-04_180521.log | cantools plot tests/files/dbc/abs.dbc --style "$style" --title "--style '$style'"
done
For more information see
.. code-block:: bash
$ python3 -m cantools plot --help
Note that by default matplotlib is not installed with cantools. But it can be by specifying an extra
at installation:
.. code-block:: bash
$ python3 -m pip install cantools[plot]
The dump subcommand
^^^^^^^^^^^^^^^^^^^
Dump given database in a human readable format:
.. code-block:: text
$ python3 -m cantools dump tests/files/dbc/motohawk.dbc
================================= Messages =================================
------------------------------------------------------------------------
Name: ExampleMessage
Id: 0x1f0
Length: 8 bytes
Cycle time: - ms
Senders: PCM1
Layout:
Bit
7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+
0 |<-x|<---------------------x|<--|
+---+---+---+---+---+---+---+---+
| +-- AverageRadius
+-- Enable
+---+---+---+---+---+---+---+---+
1 |-------------------------------|
+---+---+---+---+---+---+---+---+
2 |----------x| | | | | |
B +---+---+---+---+---+---+---+---+
y +-- Temperature
t +---+---+---+---+---+---+---+---+
e 3 | | | | | | | | |
+---+---+---+---+---+---+---+---+
4 | | | | | | | | |
+---+---+---+---+---+---+---+---+
5 | | | | | | | | |
+---+---+---+---+---+---+---+---+
6 | | | | | | | | |
+---+---+---+---+---+---+---+---+
7 | | | | | | | | |
+---+---+---+---+---+---+---+---+
Signal tree:
-- {root}
+-- Enable
+-- AverageRadius
+-- Temperature
Signal choices:
Enable
0 Disabled
1 Enabled
------------------------------------------------------------------------
The list subcommand
^^^^^^^^^^^^^^^^^^^
Print all information of a given database in a human readable
format. This is very similar to the "dump" subcommand, but the output
is less pretty, slightly more comprehensive and easier to parse by
shell scripts:
.. code-block:: bash
$ python3 -m cantools list -a tests/files/dbc/motohawk.dbc
ExampleMessage:
Comment[None]: Example message used as template in MotoHawk models.
Frame ID: 0x1f0 (496)
Size: 8 bytes
Is extended frame: False
Signals:
Enable:
Type: Integer
Start bit: 7
Length: 1 bits
Unit: -
Is signed: False
Named values:
0: Disabled
The generate C source subcommand
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Generate `C` source code from given database.
The generated code contains:
- Message `structs`_.
- Message `pack`_ and `unpack`_ functions.
- Signal `encode`_ and `decode`_ functions.
- Frame id, length, type, cycle time and signal choices `defines`_.
Known limitations:
- The maximum signal size is 64 bits, which in practice is never
exceeded.
Below is an example of how to generate C source code from a
database. The database is ``tests/files/dbc/motohawk.dbc``.
.. code-block:: text
$ python3 -m cantools generate_c_source tests/files/dbc/motohawk.dbc
Successfully generated motohawk.h and motohawk.c.
See `motohawk.h`_ and `motohawk.c`_ for the contents of the generated
files.
In this example we use ``--use-float`` so floating point numbers in the generated
code are single precision (``float``) instead of double precision (``double``).
.. code-block:: text
$ python3 -m cantools generate_c_source --use-float tests/files/dbc/motohawk.dbc
Successfully generated motohawk.h and motohawk.c.
In the next example we use ``--database-name`` to set a custom
namespace for all generated types, defines and functions. The output
file names are also changed by this option.
.. code-block:: text
$ python3 -m cantools generate_c_source --database-name my_database_name tests/files/dbc/motohawk.dbc
Successfully generated my_database_name.h and my_database_name.c.
See `my_database_name.h`_ and `my_database_name.c`_ for the contents
of the generated files.
In the next example we use ``--no-floating-point-numbers`` to generate
code without floating point types, i.e. ``float`` and ``double``.
.. code-block:: text
$ python3 -m cantools generate_c_source --no-floating-point-numbers tests/files/dbc/motohawk.dbc
Successfully generated motohawk.h and motohawk.c.
See `motohawk_no_floating_point_numbers.h`_ and
`motohawk_no_floating_point_numbers.c`_ for the contents of the
generated files.
In the last example ``--node`` is used to generate
message pack functions only for messages sent by the specified node and unpack
functions only for messages with its signal receivers belonging to that node.
.. code-block:: text
$ cantools generate_c_source tests/files/dbc/motohawk.dbc --node PCM1
Successfully generated motohawk.h and motohawk.c.
See `motohawk_sender_node.h`_ and
`motohawk_sender_node.c`_ for the contents of the
generated files.
Other C code generators:
- http://www.coderdbc.com
- https://github.com/howerj/dbcc
- https://github.com/lonkamikaze/hsk-libs/blob/master/scripts/dbc2c.awk
- https://sourceforge.net/projects/comframe/
The monitor subcommand
^^^^^^^^^^^^^^^^^^^^^^
Monitor CAN bus traffic in a text based user interface.
.. code-block:: text
$ python3 -m cantools monitor tests/files/dbc/motohawk.dbc
.. image:: https://github.com/cantools/cantools/raw/master/docs/monitor.png
The menu at the bottom of the monitor shows the available commands.
- Quit: Quit the monitor. Ctrl-C can be used as well.
- Filter: Only display messages matching given regular
expression. Press <Enter> to return to the menu from the filter
input line.
- Play/Pause: Toggle between playing and paused (or running and freezed).
- Reset: Reset the monitor to its initial state.
Contributing
============
#. Fork the repository.
#. Install prerequisites.
.. code-block:: text
python3 -m pip install -e .[dev]
#. Implement the new feature or bug fix.
#. Implement test case(s) to ensure that future changes do not break
legacy.
#. Run the linters
.. code-block:: text
ruff check src
mypy src
#. Run the tests.
.. code-block:: text
tox -e py
#. Create a pull request.
.. |github-actions| image:: https://github.com/cantools/cantools/actions/workflows/pythonpackage.yml/badge.svg?branch=master
:target: https://github.com/cantools/cantools/actions/workflows/pythonpackage.yml
:alt: Github Actions workflow status
.. |coverage| image:: https://coveralls.io/repos/github/cantools/cantools/badge.svg?branch=master
:target: https://coveralls.io/github/cantoolscantools?branch=master
:alt: Test coverage reports on Coveralls.io
.. _small DBC-file: https://github.com/cantools/cantools/blob/master/tests/files/dbc/motohawk.dbc
.. _motohawk.dbc: https://github.com/cantools/cantools/blob/master/tests/files/dbc/motohawk.dbc
.. _python-can: https://python-can.readthedocs.io/en/master/
.. _DBC: http://www.socialledge.com/sjsu/index.php?title=DBC_Format
.. _KCD: https://github.com/julietkilo/kcd
.. _tester: http://cantools.readthedocs.io/en/latest/#cantools.tester.Tester
.. _encoding: http://cantools.readthedocs.io/en/latest/#cantools.database.can.Message.encode
.. _encode_message(): http://cantools.readthedocs.io/en/latest/#cantools.database.can.Database.encode_message
.. _decodes: http://cantools.readthedocs.io/en/latest/#cantools.database.can.Database.decode_message
.. _examples: https://github.com/cantools/cantools/blob/master/examples
.. _structs: https://github.com/cantools/cantools/blob/master/tests/files/c_source/motohawk.h#L58
.. _pack: https://github.com/cantools/cantools/blob/master/tests/files/c_source/motohawk.h#L88
.. _unpack: https://github.com/cantools/cantools/blob/master/tests/files/c_source/motohawk.h#L102
.. _encode: https://github.com/cantools/cantools/blob/master/tests/files/c_source/motohawk.h#L116
.. _decode: https://github.com/cantools/cantools/blob/master/tests/files/c_source/motohawk.h#L125
.. _defines: https://github.com/cantools/cantools/blob/master/tests/files/c_source/motohawk.h#L42
.. _motohawk.h: https://github.com/cantools/cantools/blob/master/tests/files/c_source/motohawk.h
.. _motohawk.c: https://github.com/cantools/cantools/blob/master/tests/files/c_source/motohawk.c
.. _my_database_name.h: https://github.com/cantools/cantools/blob/master/tests/files/c_source/my_database_name.h
.. _my_database_name.c: https://github.com/cantools/cantools/blob/master/tests/files/c_source/my_database_name.c
.. _motohawk_no_floating_point_numbers.h: https://github.com/cantools/cantools/blob/master/tests/files/c_source/motohawk_no_floating_point_numbers.h
.. _motohawk_no_floating_point_numbers.c: https://github.com/cantools/cantools/blob/master/tests/files/c_source/motohawk_no_floating_point_numbers.c
.. _motohawk_sender_node.h: https://github.com/cantools/cantools/blob/master/tests/files/c_source/motohawk_sender_node.h
.. _motohawk_sender_node.c: https://github.com/cantools/cantools/blob/master/tests/files/c_source/motohawk_sender_node.c
.. _matplotlib: https://matplotlib.org/

View File

@ -1,132 +0,0 @@
../../Scripts/cantools.exe,sha256=Sb1EuGixzTWJ0RcO3RGGevfvMH5KLodtAH0Lg-EIiF4,108417
cantools-40.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
cantools-40.0.0.dist-info/LICENSE,sha256=7T2TtOL_oh75b3QyWjeJu-6qMOQpJXmIW0hddxJ1L-k,1085
cantools-40.0.0.dist-info/METADATA,sha256=eaorpFjqlfQ_y2BQEUWOkVRxVEyGn7NS0ihugoG98tc,16805
cantools-40.0.0.dist-info/RECORD,,
cantools-40.0.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
cantools-40.0.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
cantools-40.0.0.dist-info/entry_points.txt,sha256=p5cKpHKEUgX0e8zmetd96BEVi4WNq1wn_dIciA2Dx50,53
cantools-40.0.0.dist-info/top_level.txt,sha256=Tl_rJpBXgVrhgmBrKozbrcQrKnOIX89JqFF7aF_4EbA,9
cantools/__init__.py,sha256=dShD_0LbGClNr5kVJq87hHForHdbzRWimfeHFF-MFW0,3199
cantools/__main__.py,sha256=V5nadWmxSTd8D7vRUsqjlHBswGCFUy3EX7yGNSsqBuM,71
cantools/__pycache__/__init__.cpython-313.pyc,,
cantools/__pycache__/__main__.cpython-313.pyc,,
cantools/__pycache__/errors.cpython-313.pyc,,
cantools/__pycache__/j1939.cpython-313.pyc,,
cantools/__pycache__/logreader.cpython-313.pyc,,
cantools/__pycache__/tester.cpython-313.pyc,,
cantools/__pycache__/typechecking.cpython-313.pyc,,
cantools/autosar/__init__.py,sha256=KwuJfKbaKOyBXmCbj_b3l3JSyCd6B-9jm7vBInPGLuU,125
cantools/autosar/__pycache__/__init__.cpython-313.pyc,,
cantools/autosar/__pycache__/end_to_end.cpython-313.pyc,,
cantools/autosar/__pycache__/secoc.cpython-313.pyc,,
cantools/autosar/__pycache__/snakeauth.cpython-313.pyc,,
cantools/autosar/end_to_end.py,sha256=Wgoi_YbKGW_NDZjFrgRafR-JYi0v7zDiu0ulUiX0nMM,5679
cantools/autosar/secoc.py,sha256=zaSISt45oFBIbFe2cj7x6sac54nrLg0Tk2dv1RKtuYA,3771
cantools/autosar/snakeauth.py,sha256=Fv-IjzXdBF2M7rIpEB7So-i238e_MyuhGnYYQe0bDVg,1204
cantools/database/__init__.py,sha256=MjqwEd8a0qwA3N5xtwvad9Su38UrpdqTqI7fyEJTNHI,13980
cantools/database/__pycache__/__init__.cpython-313.pyc,,
cantools/database/__pycache__/conversion.cpython-313.pyc,,
cantools/database/__pycache__/errors.cpython-313.pyc,,
cantools/database/__pycache__/namedsignalvalue.cpython-313.pyc,,
cantools/database/__pycache__/utils.cpython-313.pyc,,
cantools/database/can/__init__.py,sha256=1qeDi4SCYQ350zImodvQ52GKRRo3fRMoLxQ8pkVOnvI,157
cantools/database/can/__pycache__/__init__.cpython-313.pyc,,
cantools/database/can/__pycache__/attribute.cpython-313.pyc,,
cantools/database/can/__pycache__/attribute_definition.cpython-313.pyc,,
cantools/database/can/__pycache__/bus.cpython-313.pyc,,
cantools/database/can/__pycache__/c_source.cpython-313.pyc,,
cantools/database/can/__pycache__/database.cpython-313.pyc,,
cantools/database/can/__pycache__/environment_variable.cpython-313.pyc,,
cantools/database/can/__pycache__/internal_database.cpython-313.pyc,,
cantools/database/can/__pycache__/message.cpython-313.pyc,,
cantools/database/can/__pycache__/node.cpython-313.pyc,,
cantools/database/can/__pycache__/signal.cpython-313.pyc,,
cantools/database/can/__pycache__/signal_group.cpython-313.pyc,,
cantools/database/can/attribute.py,sha256=VAKQU3jGcxv4gLByEd7UDkiOPkKdmyBurM2RBQ8EUts,775
cantools/database/can/attribute_definition.py,sha256=jh5OAR2wBOBeOA8cWKQt84FtVGMhGw0Tyh8a3tnNnvw,2190
cantools/database/can/bus.py,sha256=2E6AS0JeXycYH9MkRPIGrvi9cVLa8lUasJYbYFfHYbQ,2496
cantools/database/can/c_source.py,sha256=a2vHyAM9OBDcL13WMcvAiKzFuYCnm3UONN-vfezU2Ts,59837
cantools/database/can/database.py,sha256=JQPgn-se8857z9QkE9yEnEinMRpOlhcDydwpqkqfAIM,21416
cantools/database/can/environment_variable.py,sha256=lzr7ipETkSe-4MBJgiDHghZJNXmiQUzUyZH2c5bGM7I,3342
cantools/database/can/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
cantools/database/can/formats/__pycache__/__init__.cpython-313.pyc,,
cantools/database/can/formats/__pycache__/dbc.cpython-313.pyc,,
cantools/database/can/formats/__pycache__/dbc_specifics.cpython-313.pyc,,
cantools/database/can/formats/__pycache__/kcd.cpython-313.pyc,,
cantools/database/can/formats/__pycache__/sym.cpython-313.pyc,,
cantools/database/can/formats/__pycache__/utils.cpython-313.pyc,,
cantools/database/can/formats/arxml/__init__.py,sha256=QIrXBHtivZk38P6VPwdOFYobByLABe-W7zw6rvwJUKc,2658
cantools/database/can/formats/arxml/__pycache__/__init__.cpython-313.pyc,,
cantools/database/can/formats/arxml/__pycache__/bus_specifics.cpython-313.pyc,,
cantools/database/can/formats/arxml/__pycache__/database_specifics.cpython-313.pyc,,
cantools/database/can/formats/arxml/__pycache__/ecu_extract_loader.cpython-313.pyc,,
cantools/database/can/formats/arxml/__pycache__/end_to_end_properties.cpython-313.pyc,,
cantools/database/can/formats/arxml/__pycache__/message_specifics.cpython-313.pyc,,
cantools/database/can/formats/arxml/__pycache__/node_specifics.cpython-313.pyc,,
cantools/database/can/formats/arxml/__pycache__/secoc_properties.cpython-313.pyc,,
cantools/database/can/formats/arxml/__pycache__/system_loader.cpython-313.pyc,,
cantools/database/can/formats/arxml/__pycache__/utils.cpython-313.pyc,,
cantools/database/can/formats/arxml/bus_specifics.py,sha256=RsfMykp-KeyBvwqQCRxqU6Ll9Yj-spAYeBn-E3AC9Og,146
cantools/database/can/formats/arxml/database_specifics.py,sha256=pqhu-npNpduae3LRwxbjE51TkVNYTAvCE_n6qnKNswM,455
cantools/database/can/formats/arxml/ecu_extract_loader.py,sha256=1EpMAkpn1-H2Df1Yd19YyDa2_3-_Zr17hj6aoy_YPX4,12913
cantools/database/can/formats/arxml/end_to_end_properties.py,sha256=uXcO3HvMyIn7jJ20ulkd6COl6VTg9a2zkUcjcxVUq_g,1427
cantools/database/can/formats/arxml/message_specifics.py,sha256=zpRxcsD7Nxx5RjYQNHHHKLVmmVKag_RPz2H2dGdoXHU,2087
cantools/database/can/formats/arxml/node_specifics.py,sha256=ctWaS90gfXVqM-14_6Y1o39uRhWYpliROtO9udwXP-c,224
cantools/database/can/formats/arxml/secoc_properties.py,sha256=tQkA3L2h_2IR1I-Cq3DIVbiebwNpOH5MxAfgz4kalqc,3282
cantools/database/can/formats/arxml/system_loader.py,sha256=5hmobxU_-L_RO-1Egp7TYlZJUfE1UEEVrJ2eKqzBS4w,97943
cantools/database/can/formats/arxml/utils.py,sha256=8KPdCXqiabNY6pkJVu0i4NNDGlW4dDb4EaYD7GngCmA,2624
cantools/database/can/formats/dbc.py,sha256=9UmBZLaGdUaRCDB95l8ebDPOl_dNGrN5UXPh7fTJN8Q,67680
cantools/database/can/formats/dbc_specifics.py,sha256=d_ucv7Bu2zuHx_SLUIFN9NtZJcT7qXtjd188stNqcM8,2453
cantools/database/can/formats/kcd.py,sha256=Dwu4n6QMaG5ftz-8mCLogvmnfDZcnz6al3AZVhaVliw,15247
cantools/database/can/formats/sym.py,sha256=puVKeHI6GLqId9T32ynjnYAieDThjGDyFrCqhiuPwD0,35269
cantools/database/can/formats/utils.py,sha256=1w1z3N18cEpD-EDCAWA9d3zKMQZx8WPM049arLDoPBU,355
cantools/database/can/internal_database.py,sha256=JR-y8sItyu09HP8qahW_l3woFto55VSVYOZ3KSAnh60,826
cantools/database/can/message.py,sha256=FFLeeFwxEb0beP8kmh_LnCPdHPPpbxvjXw5kxg-KMNg,49085
cantools/database/can/node.py,sha256=Gj2TDAxgDqlyPE1zmfLMr9kGAdu0g_UMhY81pMgeIDo,2980
cantools/database/can/signal.py,sha256=XWinJCBPfeS1H7BcaKhlVe75cFUZVSDGwc0Wbh8TE6k,10792
cantools/database/can/signal_group.py,sha256=CkbHppcrCP6ESvu8x4qIbULIyb4s2jNc2GtCZOLl7eY,1293
cantools/database/conversion.py,sha256=2CoykOApx8UtfXlk4pAXDCTUetohb2fqDPWsdlH_9Sc,9842
cantools/database/diagnostics/__init__.py,sha256=vqZ2N40Vpr37GlcIwnONuVFRenVh5Scmfc40KTReFJE,75
cantools/database/diagnostics/__pycache__/__init__.cpython-313.pyc,,
cantools/database/diagnostics/__pycache__/data.cpython-313.pyc,,
cantools/database/diagnostics/__pycache__/database.cpython-313.pyc,,
cantools/database/diagnostics/__pycache__/did.cpython-313.pyc,,
cantools/database/diagnostics/__pycache__/internal_database.cpython-313.pyc,,
cantools/database/diagnostics/data.py,sha256=AAF2fSwS_24j-_pk1bAyQm29StqXfAuxPtilxUPbOt8,5195
cantools/database/diagnostics/database.py,sha256=3MCSskJpDirWRnNK3tryWbKxq-O6vIAAxCrNJkn5IZA,3170
cantools/database/diagnostics/did.py,sha256=WO1kkqqWtG_ygtH9Lk3lvuAsm06DVz36RC6tiS9y59E,3352
cantools/database/diagnostics/formats/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
cantools/database/diagnostics/formats/__pycache__/__init__.cpython-313.pyc,,
cantools/database/diagnostics/formats/__pycache__/cdd.cpython-313.pyc,,
cantools/database/diagnostics/formats/cdd.py,sha256=OGgHeL4hg_gbWG8orEbdi88kCMS8vRCorn1gX5BJPew,6754
cantools/database/diagnostics/internal_database.py,sha256=E90ThC42YoB0okiQ3zOjMSMISAdbel6UKkE_5cv0KqM,161
cantools/database/errors.py,sha256=F-y-Oagw39fYnLWTV-Vu-ysNPVFH4QWd4KnE8L5i4jY,179
cantools/database/namedsignalvalue.py,sha256=2-McjdG9--gq3sv4ctbc54TOlqThAZorqFaF1VY3_g8,1593
cantools/database/utils.py,sha256=xrN4g8CsGAL4V8zAcVWzdPlXQ0f0cdqU61vhkeEQKhQ,15980
cantools/errors.py,sha256=0AdPszq7kkayF5OtHciuT3B1FQRHxM8C1mcKgJVyfwE,89
cantools/j1939.py,sha256=_3uWp8p9uf3GdZDYHtMBCPeOSkqDw3bWIVGCFekaRBs,4484
cantools/logreader.py,sha256=_MgDs-MoPX_W0HX3TJ5N5mwZWqsekREvc2f5LxOAja4,14963
cantools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
cantools/subparsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
cantools/subparsers/__pycache__/__init__.cpython-313.pyc,,
cantools/subparsers/__pycache__/__utils__.cpython-313.pyc,,
cantools/subparsers/__pycache__/convert.cpython-313.pyc,,
cantools/subparsers/__pycache__/decode.cpython-313.pyc,,
cantools/subparsers/__pycache__/generate_c_source.cpython-313.pyc,,
cantools/subparsers/__pycache__/list.cpython-313.pyc,,
cantools/subparsers/__pycache__/monitor.cpython-313.pyc,,
cantools/subparsers/__pycache__/plot.cpython-313.pyc,,
cantools/subparsers/__utils__.py,sha256=7hV150FTaiPegd_WDMyJPC9pl5XsbzN0XTyBCmvwdxE,8780
cantools/subparsers/convert.py,sha256=ajxpdvl3DLxwBfoSx_XaIA_V2dLjzfv-4ySuSyKxigQ,1493
cantools/subparsers/decode.py,sha256=vuxOBS3E0XcMFogr9OMNLqnvD1qecjOiUooGGXfmIKI,2947
cantools/subparsers/dump/__init__.py,sha256=fJTBEqjq29K9Wt39gP6_ZgnXbG6p7SJScmlJvR8qvgU,5816
cantools/subparsers/dump/__pycache__/__init__.cpython-313.pyc,,
cantools/subparsers/dump/__pycache__/formatting.cpython-313.pyc,,
cantools/subparsers/dump/formatting.py,sha256=ljROsdSrsxgJtckZi0SvhRmd__L31A4SS3eoJ6BJofQ,11841
cantools/subparsers/generate_c_source.py,sha256=ifB44lGD_HnVaDvunz0caS6zrJ8LcnUHVbN5Aj9fXvw,4091
cantools/subparsers/list.py,sha256=SDnjwC1_RKm02jqwVEuqofCfNZl_rNJwUE3xYYHxVng,14391
cantools/subparsers/monitor.py,sha256=at_ggsrV0bZqL6On_3C-4D3J1yTX0w_4XxvUNAe478M,20071
cantools/subparsers/plot.py,sha256=OpNzyEN5cynBBPDAJgbu_Kzr6-n7--iORvlpuqaT7ow,34633
cantools/tester.py,sha256=1Vll_TKqzApfZ-nXBesqancawrIZ3ub5MksYTPPqaRQ,15370
cantools/typechecking.py,sha256=U1B-6NV-IGx9vtozvOJZ2BMREv-BdWbpcKWrH9LHa2o,1957

View File

@ -1,5 +0,0 @@
Wheel-Version: 1.0
Generator: setuptools (75.6.0)
Root-Is-Purelib: true
Tag: py3-none-any

View File

@ -1,2 +0,0 @@
[console_scripts]
cantools = cantools.__init__:_main

View File

@ -1 +0,0 @@
cantools

View File

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

View File

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,165 +0,0 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

View File

@ -1,218 +0,0 @@
Metadata-Version: 2.1
Name: python-can
Version: 4.5.0
Summary: Controller Area Network interface module for Python
Author: python-can contributors
License: LGPL v3
Project-URL: homepage, https://github.com/hardbyte/python-can
Project-URL: documentation, https://python-can.readthedocs.io
Project-URL: repository, https://github.com/hardbyte/python-can
Project-URL: changelog, https://github.com/hardbyte/python-can/blob/develop/CHANGELOG.md
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Education
Classifier: Intended Audience :: Information Technology
Classifier: Intended Audience :: Manufacturing
Classifier: Intended Audience :: Telecommunications Industry
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)
Classifier: Natural Language :: English
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Embedded Systems
Classifier: Topic :: Software Development :: Embedded Systems :: Controller Area Network (CAN)
Classifier: Topic :: System :: Hardware :: Hardware Drivers
Classifier: Topic :: System :: Logging
Classifier: Topic :: System :: Monitoring
Classifier: Topic :: System :: Networking
Classifier: Topic :: Utilities
Requires-Python: >=3.8
Description-Content-Type: text/x-rst
License-File: LICENSE.txt
Requires-Dist: wrapt~=1.10
Requires-Dist: packaging>=23.1
Requires-Dist: typing_extensions>=3.10.0.0
Requires-Dist: msgpack~=1.1.0; platform_system != "Windows"
Provides-Extra: lint
Requires-Dist: pylint==3.2.*; extra == "lint"
Requires-Dist: ruff==0.7.0; extra == "lint"
Requires-Dist: black==24.10.*; extra == "lint"
Requires-Dist: mypy==1.12.*; extra == "lint"
Provides-Extra: pywin32
Requires-Dist: pywin32>=305; extra == "pywin32"
Provides-Extra: seeedstudio
Requires-Dist: pyserial>=3.0; extra == "seeedstudio"
Provides-Extra: serial
Requires-Dist: pyserial~=3.0; extra == "serial"
Provides-Extra: neovi
Requires-Dist: filelock; extra == "neovi"
Requires-Dist: python-ics>=2.12; extra == "neovi"
Provides-Extra: canalystii
Requires-Dist: canalystii>=0.1.0; extra == "canalystii"
Provides-Extra: cantact
Requires-Dist: cantact>=0.0.7; extra == "cantact"
Provides-Extra: cvector
Requires-Dist: python-can-cvector; extra == "cvector"
Provides-Extra: gs-usb
Requires-Dist: gs_usb>=0.2.1; extra == "gs-usb"
Provides-Extra: nixnet
Requires-Dist: nixnet>=0.3.2; extra == "nixnet"
Provides-Extra: pcan
Requires-Dist: uptime~=3.0.1; extra == "pcan"
Provides-Extra: remote
Requires-Dist: python-can-remote; extra == "remote"
Provides-Extra: sontheim
Requires-Dist: python-can-sontheim>=0.1.2; extra == "sontheim"
Provides-Extra: canine
Requires-Dist: python-can-canine>=0.2.2; extra == "canine"
Provides-Extra: zlgcan
Requires-Dist: zlgcan-driver-py; extra == "zlgcan"
Provides-Extra: viewer
Requires-Dist: windows-curses; (platform_system == "Windows" and platform_python_implementation == "CPython") and extra == "viewer"
Provides-Extra: mf4
Requires-Dist: asammdf>=6.0.0; extra == "mf4"
python-can
==========
|pypi| |conda| |python_implementation| |downloads| |downloads_monthly|
|docs| |github-actions| |coverage| |mergify| |formatter|
.. |pypi| image:: https://img.shields.io/pypi/v/python-can.svg
:target: https://pypi.python.org/pypi/python-can/
:alt: Latest Version on PyPi
.. |conda| image:: https://img.shields.io/conda/v/conda-forge/python-can
:target: https://github.com/conda-forge/python-can-feedstock
:alt: Latest Version on conda-forge
.. |python_implementation| image:: https://img.shields.io/pypi/implementation/python-can
:target: https://pypi.python.org/pypi/python-can/
:alt: Supported Python implementations
.. |downloads| image:: https://static.pepy.tech/badge/python-can
:target: https://pepy.tech/project/python-can
:alt: Downloads on PePy
.. |downloads_monthly| image:: https://static.pepy.tech/badge/python-can/month
:target: https://pepy.tech/project/python-can
:alt: Monthly downloads on PePy
.. |formatter| image:: https://img.shields.io/badge/code%20style-black-000000.svg
:target: https://github.com/python/black
:alt: This project uses the black formatter.
.. |docs| image:: https://readthedocs.org/projects/python-can/badge/?version=stable
:target: https://python-can.readthedocs.io/en/stable/
:alt: Documentation
.. |github-actions| image:: https://github.com/hardbyte/python-can/actions/workflows/ci.yml/badge.svg
:target: https://github.com/hardbyte/python-can/actions/workflows/ci.yml
:alt: Github Actions workflow status
.. |coverage| image:: https://coveralls.io/repos/github/hardbyte/python-can/badge.svg?branch=develop
:target: https://coveralls.io/github/hardbyte/python-can?branch=develop
:alt: Test coverage reports on Coveralls.io
.. |mergify| image:: https://img.shields.io/endpoint.svg?url=https://api.mergify.com/v1/badges/hardbyte/python-can&style=flat
:target: https://mergify.io
:alt: Mergify Status
The **C**\ ontroller **A**\ rea **N**\ etwork is a bus standard designed
to allow microcontrollers and devices to communicate with each other. It
has priority based bus arbitration and reliable deterministic
communication. It is used in cars, trucks, boats, wheelchairs and more.
The ``can`` package provides controller area network support for
Python developers; providing common abstractions to
different hardware devices, and a suite of utilities for sending and receiving
messages on a can bus.
The library currently supports CPython as well as PyPy and runs on Mac, Linux and Windows.
============================== ===========
Library Version Python
------------------------------ -----------
2.x 2.6+, 3.4+
3.x 2.7+, 3.5+
4.0+ 3.7+
4.3+ 3.8+
============================== ===========
Features
--------
- common abstractions for CAN communication
- support for many different backends (see the `docs <https://python-can.readthedocs.io/en/stable/interfaces.html>`__)
- receiving, sending, and periodically sending messages
- normal and extended arbitration IDs
- `CAN FD <https://en.wikipedia.org/wiki/CAN_FD>`__ support
- many different loggers and readers supporting playback: ASC (CANalyzer format), BLF (Binary Logging Format by Vector), MF4 (Measurement Data Format v4 by ASAM), TRC, CSV, SQLite, and Canutils log
- efficient in-kernel or in-hardware filtering of messages on supported interfaces
- bus configuration reading from a file or from environment variables
- command line tools for working with CAN buses (see the `docs <https://python-can.readthedocs.io/en/stable/scripts.html>`__)
- more
Example usage
-------------
``pip install python-can``
.. code:: python
# import the library
import can
# create a bus instance using 'with' statement,
# this will cause bus.shutdown() to be called on the block exit;
# many other interfaces are supported as well (see documentation)
with can.Bus(interface='socketcan',
channel='vcan0',
receive_own_messages=True) as bus:
# send a message
message = can.Message(arbitration_id=123, is_extended_id=True,
data=[0x11, 0x22, 0x33])
bus.send(message, timeout=0.2)
# iterate over received messages
for msg in bus:
print(f"{msg.arbitration_id:X}: {msg.data}")
# or use an asynchronous notifier
notifier = can.Notifier(bus, [can.Logger("recorded.log"), can.Printer()])
You can find more information in the documentation, online at
`python-can.readthedocs.org <https://python-can.readthedocs.org/en/stable/>`__.
Discussion
----------
If you run into bugs, you can file them in our
`issue tracker <https://github.com/hardbyte/python-can/issues>`__ on GitHub.
`Stackoverflow <https://stackoverflow.com/questions/tagged/can+python>`__ has several
questions and answers tagged with ``python+can``.
Wherever we interact, we strive to follow the
`Python Community Code of Conduct <https://www.python.org/psf/codeofconduct/>`__.
Contributing
------------
See `doc/development.rst <doc/development.rst>`__ for getting started.

View File

@ -1,190 +0,0 @@
../../Scripts/can_logconvert.exe,sha256=4OLlyHsi4OUi_soiBmhtaUC_seuJ1e3Alu-yqLLLnqE,108412
../../Scripts/can_logger.exe,sha256=ybhXzwGvkCnwJcWCHzKa6qSxHVTi80X9sPsNeiC1PAI,108408
../../Scripts/can_player.exe,sha256=kSsqUd_DlQ6S15yAgSvUc7a_eI4ZCUAzS1gM3z-V-UA,108408
../../Scripts/can_viewer.exe,sha256=sUX1h5GPslijbiSwAAuDSpomM2Zt9DvxXaXvLygv0pE,108408
can/__init__.py,sha256=R0FbX-rk4vfp1reupjJcYGAYhu1OO_0Rsarymtx-H1A,2957
can/__pycache__/__init__.cpython-313.pyc,,
can/__pycache__/_entry_points.cpython-313.pyc,,
can/__pycache__/bit_timing.cpython-313.pyc,,
can/__pycache__/broadcastmanager.cpython-313.pyc,,
can/__pycache__/bus.cpython-313.pyc,,
can/__pycache__/ctypesutil.cpython-313.pyc,,
can/__pycache__/exceptions.cpython-313.pyc,,
can/__pycache__/interface.cpython-313.pyc,,
can/__pycache__/listener.cpython-313.pyc,,
can/__pycache__/logconvert.cpython-313.pyc,,
can/__pycache__/logger.cpython-313.pyc,,
can/__pycache__/message.cpython-313.pyc,,
can/__pycache__/notifier.cpython-313.pyc,,
can/__pycache__/player.cpython-313.pyc,,
can/__pycache__/thread_safe_bus.cpython-313.pyc,,
can/__pycache__/typechecking.cpython-313.pyc,,
can/__pycache__/util.cpython-313.pyc,,
can/__pycache__/viewer.cpython-313.pyc,,
can/_entry_points.py,sha256=h3NcPDarBMlHFggGVlcHYSY80nutagzkbd7zvkBVVq0,883
can/bit_timing.py,sha256=pWjJw0Vd52XBJ7qsdgDg4ZRYpXhi-vfwaK1T22wMtZ0,42518
can/broadcastmanager.py,sha256=F-9NF_twn7xdwd0wE0F7XuHExoPzgojbDlrOFGwVkYI,13234
can/bus.py,sha256=UyhNzcAw0y1MY2Gj1OjSVF2Bt5z3isXIvBVllnmiUSc,19552
can/ctypesutil.py,sha256=6mUNOW2tbzA92e8lR7gZ3j_Lin0sBT5zs6ivnMA-Vyw,2463
can/exceptions.py,sha256=b-iOjhfOEGGJ3XRokdUfMQ5u7mCs3CW2nvqrKnDmnq0,4097
can/interface.py,sha256=rp_DEk34gcz5oifBZ631p0mULVFLNcnGCBMMd-7JxPc,6911
can/interfaces/__init__.py,sha256=oUH47iDy8dD5PwsZTf5_TEcZaijOeidtmRHXQofjyA0,2190
can/interfaces/__pycache__/__init__.cpython-313.pyc,,
can/interfaces/__pycache__/canalystii.cpython-313.pyc,,
can/interfaces/__pycache__/cantact.cpython-313.pyc,,
can/interfaces/__pycache__/gs_usb.cpython-313.pyc,,
can/interfaces/__pycache__/iscan.cpython-313.pyc,,
can/interfaces/__pycache__/nican.cpython-313.pyc,,
can/interfaces/__pycache__/nixnet.cpython-313.pyc,,
can/interfaces/__pycache__/robotell.cpython-313.pyc,,
can/interfaces/__pycache__/slcan.cpython-313.pyc,,
can/interfaces/__pycache__/virtual.cpython-313.pyc,,
can/interfaces/canalystii.py,sha256=GedKF34FG7zuXvtNPqEDkBlZjEi0K9fjYB8X6HbBtW0,8065
can/interfaces/cantact.py,sha256=b1HIeTnXkL3REzmSWJSw3XbavqTMJTZvYVsn3GrEb-g,5972
can/interfaces/etas/__init__.py,sha256=yzYQ_j00VX4if_WzGsM6EdDTJbrfuqhVwoF0X0fFfh0,12093
can/interfaces/etas/__pycache__/__init__.cpython-313.pyc,,
can/interfaces/etas/__pycache__/boa.cpython-313.pyc,,
can/interfaces/etas/boa.py,sha256=d0P66wr-vC5HxTg4_lGFdoasIfyUdo6GrnVL7HPJxOs,20644
can/interfaces/gs_usb.py,sha256=c4AIipb_yYsNsY-zew2OSSzzFaXZ5QxvoRQ0LlkThHQ,6112
can/interfaces/ics_neovi/__init__.py,sha256=ZrIW0OnnhdUSSotJ2NQpTPGsNyXLVMFQh33Nm4oQYNE,219
can/interfaces/ics_neovi/__pycache__/__init__.cpython-313.pyc,,
can/interfaces/ics_neovi/__pycache__/neovi_bus.cpython-313.pyc,,
can/interfaces/ics_neovi/neovi_bus.py,sha256=bKIb8HhvijjCaAr8zjWOun6YLawxJDe64HR-4-_5n5s,17873
can/interfaces/iscan.py,sha256=5rah_TybSfibuVX8AxcOR3CFbA7Py_-4yU7Kk1gS644,6348
can/interfaces/ixxat/__init__.py,sha256=xRymShxicj--2CzuAFkTKUEHZKRfX1KFsniWGAWl_SQ,508
can/interfaces/ixxat/__pycache__/__init__.cpython-313.pyc,,
can/interfaces/ixxat/__pycache__/canlib.cpython-313.pyc,,
can/interfaces/ixxat/__pycache__/canlib_vcinpl.cpython-313.pyc,,
can/interfaces/ixxat/__pycache__/canlib_vcinpl2.cpython-313.pyc,,
can/interfaces/ixxat/__pycache__/constants.cpython-313.pyc,,
can/interfaces/ixxat/__pycache__/exceptions.cpython-313.pyc,,
can/interfaces/ixxat/__pycache__/structures.cpython-313.pyc,,
can/interfaces/ixxat/canlib.py,sha256=6mhDGO1_uOV3IVedVujS0jXe6DGNqSsXt4gmFmZdl5U,5844
can/interfaces/ixxat/canlib_vcinpl.py,sha256=SbpTlW1GEY5ESMq1kbpuF1cy-nkzpM6CGvyg3G9ZkDQ,38085
can/interfaces/ixxat/canlib_vcinpl2.py,sha256=c99Se3NBQH3jJPJwiB_xdo2KiR2ESKYp17quI5QnMR0,39652
can/interfaces/ixxat/constants.py,sha256=N-YrqhKmezwBnygBI4BQk-9_BQyyLVo3CFg_SZmKIdg,9102
can/interfaces/ixxat/exceptions.py,sha256=DLtBDPG522pcNbaxemwUwKNHmozgYQGfSCfXDOqefpQ,978
can/interfaces/ixxat/structures.py,sha256=WjA2pgenCAmclQqfVlRShL7nrnQl62PjEkt5oN0PSYM,10540
can/interfaces/kvaser/__init__.py,sha256=v8tcm4xo73lCZ6z2hfMUv73jb5bW7Y3OB6XrNm37-z0,218
can/interfaces/kvaser/__pycache__/__init__.cpython-313.pyc,,
can/interfaces/kvaser/__pycache__/canlib.cpython-313.pyc,,
can/interfaces/kvaser/__pycache__/constants.cpython-313.pyc,,
can/interfaces/kvaser/__pycache__/structures.cpython-313.pyc,,
can/interfaces/kvaser/canlib.py,sha256=csAl9rMb0KiXztpnF-21pLu-QWG74tekF782er8MCvo,28087
can/interfaces/kvaser/constants.py,sha256=_iFCp9ppS8X0byvk_lWm3YnSWaZXU-xYupetDoy1FnI,6965
can/interfaces/kvaser/structures.py,sha256=0rdDBApNDl_wpG8MDJ0xW3XU4bGn-XRoHuyOL_VYYq0,1971
can/interfaces/neousys/__init__.py,sha256=fVs-zoPY6J44kvYa8yH-GI-uzBH6j9yDxcC7ErW-GAM,134
can/interfaces/neousys/__pycache__/__init__.cpython-313.pyc,,
can/interfaces/neousys/__pycache__/neousys.cpython-313.pyc,,
can/interfaces/neousys/neousys.py,sha256=nOSLeSLQVpF_COQ-L3eFM4Jk9OGAXxZgTkYMlTT8Cwc,7693
can/interfaces/nican.py,sha256=1bsV3h1pc_UxvGU6Kv4KgTE00nWzwuwK3DMaK73WaUU,11552
can/interfaces/nixnet.py,sha256=fMtFQT7fvhqzCeqOc-v4zrXNG8nOqSU4G9yZx57h7pw,13179
can/interfaces/pcan/__init__.py,sha256=D0sfeL2tk2xVKLtxkAUvJjXddwP3y0n4vC1PDoa8i-Y,137
can/interfaces/pcan/__pycache__/__init__.cpython-313.pyc,,
can/interfaces/pcan/__pycache__/basic.cpython-313.pyc,,
can/interfaces/pcan/__pycache__/pcan.cpython-313.pyc,,
can/interfaces/pcan/basic.py,sha256=Iv_pNzkHWw9Qq8JZ95I1o5dnrktaPbtrrQOQrJL53sQ,41286
can/interfaces/pcan/pcan.py,sha256=JRTw6lxanH7WU4ZJze7SwM4gBOomh-qWfTjc_yDX56g,28445
can/interfaces/robotell.py,sha256=OlLqvnuWKDu7cnRCP0n36WnQAmTLJzZ97eHhBdIzD_0,15756
can/interfaces/seeedstudio/__init__.py,sha256=hRTBWKIPxob_nPLZq4quPDu6YwZO_AZmZpthjBROjkE,119
can/interfaces/seeedstudio/__pycache__/__init__.cpython-313.pyc,,
can/interfaces/seeedstudio/__pycache__/seeedstudio.cpython-313.pyc,,
can/interfaces/seeedstudio/seeedstudio.py,sha256=Pl8LHeghz-Jk8ukdS1CkrSCZKW0sL9UmKYFMlC-hgKc,9610
can/interfaces/serial/__init__.py,sha256=rpPrM68xhR0D6eIol1oyEvyw__CBCtBUAiZnfgL3k60,114
can/interfaces/serial/__pycache__/__init__.cpython-313.pyc,,
can/interfaces/serial/__pycache__/serial_can.cpython-313.pyc,,
can/interfaces/serial/serial_can.py,sha256=dyQexSnUC0DrHvlw_lkHsFKLioiVo5pRFLUilu38aqI,7384
can/interfaces/slcan.py,sha256=fGJgOdoMgGetSL2toYAn-TbGYpS4GeXzdpFfjO-7Lt0,11976
can/interfaces/socketcan/__init__.py,sha256=VV3-1FlheR-Km27B2r7nTtlEIli07aYGN188bcrUfK0,286
can/interfaces/socketcan/__pycache__/__init__.cpython-313.pyc,,
can/interfaces/socketcan/__pycache__/constants.cpython-313.pyc,,
can/interfaces/socketcan/__pycache__/socketcan.cpython-313.pyc,,
can/interfaces/socketcan/__pycache__/utils.cpython-313.pyc,,
can/interfaces/socketcan/constants.py,sha256=7DfAcYCokY9np63SyH39W7iP9wAUktxyH_1PKN3Wu2s,1130
can/interfaces/socketcan/socketcan.py,sha256=jQ8XHHCrDSDbWa8Zka5U3dOivYkk2-DJE3OJeI3mraA,32431
can/interfaces/socketcan/utils.py,sha256=I7tyAM_GqjQbvf36XIyruUdOkhm3NdKf7gqba7KoFFw,2833
can/interfaces/socketcand/__init__.py,sha256=4rUdnZlPmSmdfIpF8rvLxjHZ5w-xfLTiHL5XMCyg4c0,275
can/interfaces/socketcand/__pycache__/__init__.cpython-313.pyc,,
can/interfaces/socketcand/__pycache__/socketcand.cpython-313.pyc,,
can/interfaces/socketcand/socketcand.py,sha256=EFGWConsRpnPp1mViqw-GcA1XZU4UwgwRhPwXEa5Jaw,12912
can/interfaces/systec/__init__.py,sha256=omi-SgqBEAOKne1SrmNLj1p1BMPBCHbJOhQ9QOKlgms,160
can/interfaces/systec/__pycache__/__init__.cpython-313.pyc,,
can/interfaces/systec/__pycache__/constants.cpython-313.pyc,,
can/interfaces/systec/__pycache__/exceptions.cpython-313.pyc,,
can/interfaces/systec/__pycache__/structures.cpython-313.pyc,,
can/interfaces/systec/__pycache__/ucan.cpython-313.pyc,,
can/interfaces/systec/__pycache__/ucanbus.cpython-313.pyc,,
can/interfaces/systec/constants.py,sha256=1AupgsozCjoN8ENyx4B2LNNYtAj9YPEUeZgS9rJmBhg,22700
can/interfaces/systec/exceptions.py,sha256=jG-XitcteyN-db4AMWlIUNeSih0Lp6TXaQN-DBGyKYc,4839
can/interfaces/systec/structures.py,sha256=gj1doSJuOBIAsjGI8i2vVub7Zv5Ot-26PSGY7jeQAAk,12986
can/interfaces/systec/ucan.py,sha256=eFfhItZWkYb0ZnI_dQNJBDMLjorV_c3GZCggzP0Xmo8,46738
can/interfaces/systec/ucanbus.py,sha256=ysQDywiVMkgUhVGAFPAzCzb5VzekoXWtbJJ93whyr7w,11354
can/interfaces/udp_multicast/__init__.py,sha256=aGLFDeB2Mr0qjFHGpvvWiod-gwr8r6HWgc6HQJc326Y,157
can/interfaces/udp_multicast/__pycache__/__init__.cpython-313.pyc,,
can/interfaces/udp_multicast/__pycache__/bus.cpython-313.pyc,,
can/interfaces/udp_multicast/__pycache__/utils.cpython-313.pyc,,
can/interfaces/udp_multicast/bus.py,sha256=pSXCf4CvRFPaE1daQltg94eS7RxUainHG_y-8gQRSiM,17199
can/interfaces/udp_multicast/utils.py,sha256=s9rnXmk-MtMk-m2Iu-jn4qKcd6tAvMAykDPgPLr-LXc,2161
can/interfaces/usb2can/__init__.py,sha256=5Nz3w5APrNDpVmRNTjHuSJzeq9QfesdoJVNkgHtAzyI,253
can/interfaces/usb2can/__pycache__/__init__.cpython-313.pyc,,
can/interfaces/usb2can/__pycache__/serial_selector.cpython-313.pyc,,
can/interfaces/usb2can/__pycache__/usb2canInterface.cpython-313.pyc,,
can/interfaces/usb2can/__pycache__/usb2canabstractionlayer.cpython-313.pyc,,
can/interfaces/usb2can/serial_selector.py,sha256=FqVnUsPsfcX2o9CecXCEO14v_cK29AVNGUyqQLx8drU,1669
can/interfaces/usb2can/usb2canInterface.py,sha256=UFBttq2yefCMhA850DQaVQysDCKPRB6AAi24hr6A2wA,6648
can/interfaces/usb2can/usb2canabstractionlayer.py,sha256=tx1xnRvFIJ2A0P_GvRLfaxWamkAPHE83asm5KHgx6xQ,6100
can/interfaces/vector/__init__.py,sha256=c1CPg3ra6Dx33w9-w57FORMgKpvS5QCTlNEPbqtvhMs,564
can/interfaces/vector/__pycache__/__init__.cpython-313.pyc,,
can/interfaces/vector/__pycache__/canlib.cpython-313.pyc,,
can/interfaces/vector/__pycache__/exceptions.cpython-313.pyc,,
can/interfaces/vector/__pycache__/xlclass.cpython-313.pyc,,
can/interfaces/vector/__pycache__/xldefine.cpython-313.pyc,,
can/interfaces/vector/__pycache__/xldriver.cpython-313.pyc,,
can/interfaces/vector/canlib.py,sha256=AnAHpc1Jk9cngKaa-RQ_rdlcxAcIj_FfBFYA0nnFLU0,49407
can/interfaces/vector/exceptions.py,sha256=_4ThvHUUuSymWTnrGaiikoFevpxXlDl4j_86LvtuWEU,926
can/interfaces/vector/xlclass.py,sha256=vPoptlI7c8fAXmsFNya7MoH7E03OA8ZK2kCLWEhOxZk,8461
can/interfaces/vector/xldefine.py,sha256=XgAgUQ20vxn0bgI9FmLpBtJocQSB0GEN99zblc_j-K8,9572
can/interfaces/vector/xldriver.py,sha256=dhwldW1QtwpO9mk_OgPCbEyPR969wp7R6wsXuGExOuQ,9893
can/interfaces/virtual.py,sha256=ifFu38lt6ivI66_1mcculOntQbb4TafQHZxIIReMqjQ,7229
can/io/__init__.py,sha256=w60ODfi9bPYF6ckFloPuixKweSusDbOii5X8r1BqBTg,1243
can/io/__pycache__/__init__.cpython-313.pyc,,
can/io/__pycache__/asc.cpython-313.pyc,,
can/io/__pycache__/blf.cpython-313.pyc,,
can/io/__pycache__/canutils.cpython-313.pyc,,
can/io/__pycache__/csv.cpython-313.pyc,,
can/io/__pycache__/generic.cpython-313.pyc,,
can/io/__pycache__/logger.cpython-313.pyc,,
can/io/__pycache__/mf4.cpython-313.pyc,,
can/io/__pycache__/player.cpython-313.pyc,,
can/io/__pycache__/printer.cpython-313.pyc,,
can/io/__pycache__/sqlite.cpython-313.pyc,,
can/io/__pycache__/trc.cpython-313.pyc,,
can/io/asc.py,sha256=b4cQDbpznyllzNKd3dEYj-_FywOS9IdbUyYc1rN1Om4,17475
can/io/blf.py,sha256=jL3y6PHMTjBVUsTTKaRWVG_n-7GRkyJRkKDBHibvI0Q,21933
can/io/canutils.py,sha256=mKs-CxnlcMhfA8qYKMc7v1t6m2TyQulh8roKuRBQUDw,6313
can/io/csv.py,sha256=VhP0i8SmpAQpJgcTWGhUfoR3O7pZeBaEIIcmBjSRdUQ,4384
can/io/generic.py,sha256=3_YecJlNXMlSGKhrSjJGVHExd-IZGCrp0-DTvJrfpdw,3738
can/io/logger.py,sha256=jSe_YBIE3Et3lALP0n9ebB_3mV_c4NwQ5nl0FCd3nxA,13648
can/io/mf4.py,sha256=uwAWxixN9XrAiS9gzM1BUDXfTzMnSuHYW-G8-9C9SYw,20574
can/io/player.py,sha256=mrWV2fOb2hkFGg3XeiJFTH_u18-gqkZi-wAB4l7LY_E,6067
can/io/printer.py,sha256=RaztAPdrDzBuA0st8xaduB5m83jeXbQstjJ38eeaDxA,1810
can/io/sqlite.py,sha256=mgExkCe3dg7EIjlWaHwdP7zfgbPuYPHyoVYlS8xNzPY,9124
can/io/trc.py,sha256=vNXrjstRTY-AY7mUaKeVDr7Al35XSTFrvKcB3TsAn-Q,16142
can/listener.py,sha256=u4OhBB85cfGOExSpJh4_woAcTxabyzvbnJDn2fdZx5Q,5533
can/logconvert.py,sha256=r-mUzbaz0bouSzSV2rOBCuaRy0sI288N68QNHSOF_bQ,1541
can/logger.py,sha256=Bh3iTj37cTYrJgkG8ik6w2wHSMfZAsvxQULk-o5Deu8,10422
can/message.py,sha256=L8X6hbL5rRyPz3gIRKc0imanfjuuUAL1NPUUkZJETaE,12061
can/notifier.py,sha256=NTtbGeOg6NoiL57de8JLGT5Qd-rK-q3_sAUOKjtWKGw,6609
can/player.py,sha256=wTtnlbq--fNbTCDs4xwPvQte0Lf7LYoa32A0VmHkB18,2966
can/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
can/thread_safe_bus.py,sha256=mA2ZUS11AWlmfDG62H9LycYItkX3BBsACYH7GrHdZFg,3113
can/typechecking.py,sha256=w_-IWbaYcIIaZ2z2HxIJcu8ZTHDlbuQK-qY5T_ylhmo,2031
can/util.py,sha256=gVHFdDdGyT_dHKB-ox3ixQYhRRItfeOoh3Z9_nh_0QY,16394
can/viewer.py,sha256=kIuEiTSoBiPOnE7nclc2O98qJaqFSYqRclSPjAdN8BE,22300
python_can-4.5.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
python_can-4.5.0.dist-info/LICENSE.txt,sha256=2n6rt7r999OuXp8iOqW9we7ORaxWncIbOwN1ILRGR2g,7651
python_can-4.5.0.dist-info/METADATA,sha256=0Ndae35X7mV4kUP88bSjWklqXyPhQRzxwXVXwNOZMFU,8884
python_can-4.5.0.dist-info/RECORD,,
python_can-4.5.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
python_can-4.5.0.dist-info/entry_points.txt,sha256=k0xFGWlRv838DScY-7B604jlqkQ2SEbPSNGYoCTmTJI,142
python_can-4.5.0.dist-info/top_level.txt,sha256=D3m3layW1dpiXBpZ6ESNxVgsfJEBWPE9VTefMMmI1HY,4

View File

@ -1,5 +0,0 @@
Wheel-Version: 1.0
Generator: setuptools (75.6.0)
Root-Is-Purelib: true
Tag: py3-none-any

View File

@ -1,5 +0,0 @@
[console_scripts]
can_logconvert = can.logconvert:main
can_logger = can.logger:main
can_player = can.player:main
can_viewer = can.viewer:main

View File

@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,129 +0,0 @@
Metadata-Version: 2.1
Name: importlib_metadata
Version: 8.0.0
Summary: Read metadata from Python packages
Author-email: "Jason R. Coombs" <jaraco@jaraco.com>
Project-URL: Source, https://github.com/python/importlib_metadata
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Requires-Python: >=3.8
Description-Content-Type: text/x-rst
License-File: LICENSE
Requires-Dist: zipp >=0.5
Requires-Dist: typing-extensions >=3.6.4 ; python_version < "3.8"
Provides-Extra: doc
Requires-Dist: sphinx >=3.5 ; extra == 'doc'
Requires-Dist: jaraco.packaging >=9.3 ; extra == 'doc'
Requires-Dist: rst.linker >=1.9 ; extra == 'doc'
Requires-Dist: furo ; extra == 'doc'
Requires-Dist: sphinx-lint ; extra == 'doc'
Requires-Dist: jaraco.tidelift >=1.4 ; extra == 'doc'
Provides-Extra: perf
Requires-Dist: ipython ; extra == 'perf'
Provides-Extra: test
Requires-Dist: pytest !=8.1.*,>=6 ; extra == 'test'
Requires-Dist: pytest-checkdocs >=2.4 ; extra == 'test'
Requires-Dist: pytest-cov ; extra == 'test'
Requires-Dist: pytest-mypy ; extra == 'test'
Requires-Dist: pytest-enabler >=2.2 ; extra == 'test'
Requires-Dist: pytest-ruff >=0.2.1 ; extra == 'test'
Requires-Dist: packaging ; extra == 'test'
Requires-Dist: pyfakefs ; extra == 'test'
Requires-Dist: flufl.flake8 ; extra == 'test'
Requires-Dist: pytest-perf >=0.9.2 ; extra == 'test'
Requires-Dist: jaraco.test >=5.4 ; extra == 'test'
Requires-Dist: importlib-resources >=1.3 ; (python_version < "3.9") and extra == 'test'
.. image:: https://img.shields.io/pypi/v/importlib_metadata.svg
:target: https://pypi.org/project/importlib_metadata
.. image:: https://img.shields.io/pypi/pyversions/importlib_metadata.svg
.. image:: https://github.com/python/importlib_metadata/actions/workflows/main.yml/badge.svg
:target: https://github.com/python/importlib_metadata/actions?query=workflow%3A%22tests%22
:alt: tests
.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json
:target: https://github.com/astral-sh/ruff
:alt: Ruff
.. image:: https://readthedocs.org/projects/importlib-metadata/badge/?version=latest
:target: https://importlib-metadata.readthedocs.io/en/latest/?badge=latest
.. image:: https://img.shields.io/badge/skeleton-2024-informational
:target: https://blog.jaraco.com/skeleton
.. image:: https://tidelift.com/badges/package/pypi/importlib-metadata
:target: https://tidelift.com/subscription/pkg/pypi-importlib-metadata?utm_source=pypi-importlib-metadata&utm_medium=readme
Library to access the metadata for a Python package.
This package supplies third-party access to the functionality of
`importlib.metadata <https://docs.python.org/3/library/importlib.metadata.html>`_
including improvements added to subsequent Python versions.
Compatibility
=============
New features are introduced in this third-party library and later merged
into CPython. The following table indicates which versions of this library
were contributed to different versions in the standard library:
.. list-table::
:header-rows: 1
* - importlib_metadata
- stdlib
* - 7.0
- 3.13
* - 6.5
- 3.12
* - 4.13
- 3.11
* - 4.6
- 3.10
* - 1.4
- 3.8
Usage
=====
See the `online documentation <https://importlib-metadata.readthedocs.io/>`_
for usage details.
`Finder authors
<https://docs.python.org/3/reference/import.html#finders-and-loaders>`_ can
also add support for custom package installers. See the above documentation
for details.
Caveats
=======
This project primarily supports third-party packages installed by PyPA
tools (or other conforming packages). It does not support:
- Packages in the stdlib.
- Packages installed without metadata.
Project details
===============
* Project home: https://github.com/python/importlib_metadata
* Report bugs at: https://github.com/python/importlib_metadata/issues
* Code hosting: https://github.com/python/importlib_metadata
* Documentation: https://importlib-metadata.readthedocs.io/
For Enterprise
==============
Available as part of the Tidelift Subscription.
This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use.
`Learn more <https://tidelift.com/subscription/pkg/pypi-importlib-metadata?utm_source=pypi-importlib-metadata&utm_medium=referral&utm_campaign=github>`_.

View File

@ -1,32 +0,0 @@
importlib_metadata-8.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
importlib_metadata-8.0.0.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
importlib_metadata-8.0.0.dist-info/METADATA,sha256=anuQ7_7h4J1bSEzfcjIBakPi2cyVQ7y7jklLHsBeH1k,4648
importlib_metadata-8.0.0.dist-info/RECORD,,
importlib_metadata-8.0.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
importlib_metadata-8.0.0.dist-info/WHEEL,sha256=mguMlWGMX-VHnMpKOjjQidIo1ssRlCFu4a4mBpz1s2M,91
importlib_metadata-8.0.0.dist-info/top_level.txt,sha256=CO3fD9yylANiXkrMo4qHLV_mqXL2sC5JFKgt1yWAT-A,19
importlib_metadata/__init__.py,sha256=tZNB-23h8Bixi9uCrQqj9Yf0aeC--Josdy3IZRIQeB0,33798
importlib_metadata/__pycache__/__init__.cpython-312.pyc,,
importlib_metadata/__pycache__/_adapters.cpython-312.pyc,,
importlib_metadata/__pycache__/_collections.cpython-312.pyc,,
importlib_metadata/__pycache__/_compat.cpython-312.pyc,,
importlib_metadata/__pycache__/_functools.cpython-312.pyc,,
importlib_metadata/__pycache__/_itertools.cpython-312.pyc,,
importlib_metadata/__pycache__/_meta.cpython-312.pyc,,
importlib_metadata/__pycache__/_text.cpython-312.pyc,,
importlib_metadata/__pycache__/diagnose.cpython-312.pyc,,
importlib_metadata/_adapters.py,sha256=rIhWTwBvYA1bV7i-5FfVX38qEXDTXFeS5cb5xJtP3ks,2317
importlib_metadata/_collections.py,sha256=CJ0OTCHIjWA0ZIVS4voORAsn2R4R2cQBEtPsZEJpASY,743
importlib_metadata/_compat.py,sha256=73QKrN9KNoaZzhbX5yPCCZa-FaALwXe8TPlDR72JgBU,1314
importlib_metadata/_functools.py,sha256=PsY2-4rrKX4RVeRC1oGp1lB1pmC9eKN88_f-bD9uOoA,2895
importlib_metadata/_itertools.py,sha256=cvr_2v8BRbxcIl5x5ldfqdHjhI8Yi8s8yk50G_nm6jQ,2068
importlib_metadata/_meta.py,sha256=nxZ7C8GVlcBFAKWyVOn_dn7ot_twBcbm1NmvjIetBHI,1801
importlib_metadata/_text.py,sha256=HCsFksZpJLeTP3NEk_ngrAeXVRRtTrtyh9eOABoRP4A,2166
importlib_metadata/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
importlib_metadata/compat/__pycache__/__init__.cpython-312.pyc,,
importlib_metadata/compat/__pycache__/py311.cpython-312.pyc,,
importlib_metadata/compat/__pycache__/py39.cpython-312.pyc,,
importlib_metadata/compat/py311.py,sha256=uqm-K-uohyj1042TH4a9Er_I5o7667DvulcD-gC_fSA,608
importlib_metadata/compat/py39.py,sha256=cPkMv6-0ilK-0Jw_Tkn0xYbOKJZc4WJKQHow0c2T44w,1102
importlib_metadata/diagnose.py,sha256=nkSRMiowlmkhLYhKhvCg9glmt_11Cox-EmLzEbqYTa8,379
importlib_metadata/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0

View File

@ -1,5 +0,0 @@
Wheel-Version: 1.0
Generator: setuptools (70.1.1)
Root-Is-Purelib: true
Tag: py3-none-any

View File

@ -1,2 +0,0 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac tellus et risus vulputate vehicula. Donec lobortis risus a elit. Etiam tempor. Ut ullamcorper, ligula eu tempor congue, eros est euismod turpis, id tincidunt sapien risus a quam. Maecenas fermentum consequat mi. Donec fermentum. Pellentesque malesuada nulla a mi. Duis sapien sem, aliquet nec, commodo eget, consequat quis, neque. Aliquam faucibus, elit ut dictum aliquet, felis nisl adipiscing sapien, sed malesuada diam lacus eget erat. Cras mollis scelerisque nunc. Nullam arcu. Aliquam consequat. Curabitur augue lorem, dapibus quis, laoreet et, pretium ac, nisi. Aenean magna nisl, mollis quis, molestie eu, feugiat in, orci. In hac habitasse platea dictumst.

Binary file not shown.

View File

@ -1 +0,0 @@
pip

View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2012 Daniel Holth <dholth@fastmail.fm> and contributors
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,66 +0,0 @@
Metadata-Version: 2.3
Name: wheel
Version: 0.45.1
Summary: A built-package format for Python
Keywords: wheel,packaging
Author-email: Daniel Holth <dholth@fastmail.fm>
Maintainer-email: Alex Grönholm <alex.gronholm@nextday.fi>
Requires-Python: >=3.8
Description-Content-Type: text/x-rst
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Topic :: System :: Archiving :: Packaging
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Dist: pytest >= 6.0.0 ; extra == "test"
Requires-Dist: setuptools >= 65 ; extra == "test"
Project-URL: Changelog, https://wheel.readthedocs.io/en/stable/news.html
Project-URL: Documentation, https://wheel.readthedocs.io/
Project-URL: Issue Tracker, https://github.com/pypa/wheel/issues
Project-URL: Source, https://github.com/pypa/wheel
Provides-Extra: test
wheel
=====
This is a command line tool for manipulating Python wheel files, as defined in
`PEP 427`_. It contains the following functionality:
* Convert ``.egg`` archives into ``.whl``
* Unpack wheel archives
* Repack wheel archives
* Add or remove tags in existing wheel archives
.. _PEP 427: https://www.python.org/dev/peps/pep-0427/
Historical note
---------------
This project used to contain the implementation of the setuptools_ ``bdist_wheel``
command, but as of setuptools v70.1, it no longer needs ``wheel`` installed for that to
work. Thus, you should install this **only** if you intend to use the ``wheel`` command
line tool!
.. _setuptools: https://pypi.org/project/setuptools/
Documentation
-------------
The documentation_ can be found on Read The Docs.
.. _documentation: https://wheel.readthedocs.io/
Code of Conduct
---------------
Everyone interacting in the wheel project's codebases, issue trackers, chat
rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_.
.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md

View File

@ -1,68 +0,0 @@
../../Scripts/wheel.exe,sha256=-G3ngeOxk4bN2s9A8LHzAP3dH0v2YTzKotxQTcY-36M,108407
wheel-0.45.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
wheel-0.45.1.dist-info/LICENSE.txt,sha256=MMI2GGeRCPPo6h0qZYx8pBe9_IkcmO8aifpP8MmChlQ,1107
wheel-0.45.1.dist-info/METADATA,sha256=mKz84H7m7jsxJyzeIcTVORiTb0NPMV39KvOIYhGgmjA,2313
wheel-0.45.1.dist-info/RECORD,,
wheel-0.45.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
wheel-0.45.1.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
wheel-0.45.1.dist-info/entry_points.txt,sha256=rTY1BbkPHhkGMm4Q3F0pIzJBzW2kMxoG1oriffvGdA0,104
wheel/__init__.py,sha256=mrxMnvdXACur_LWegbUfh5g5ysWZrd63UJn890wvGNk,59
wheel/__main__.py,sha256=NkMUnuTCGcOkgY0IBLgBCVC_BGGcWORx2K8jYGS12UE,455
wheel/__pycache__/__init__.cpython-313.pyc,,
wheel/__pycache__/__main__.cpython-313.pyc,,
wheel/__pycache__/_bdist_wheel.cpython-313.pyc,,
wheel/__pycache__/_setuptools_logging.cpython-313.pyc,,
wheel/__pycache__/bdist_wheel.cpython-313.pyc,,
wheel/__pycache__/macosx_libfile.cpython-313.pyc,,
wheel/__pycache__/metadata.cpython-313.pyc,,
wheel/__pycache__/util.cpython-313.pyc,,
wheel/__pycache__/wheelfile.cpython-313.pyc,,
wheel/_bdist_wheel.py,sha256=UghCQjSH_pVfcZh6oRjzSw_TQhcf3anSx1OkiLSL82M,21694
wheel/_setuptools_logging.py,sha256=-5KC-lne0ilOUWIDfOkqapUWGMFZhuKYDIavIZiB5kM,781
wheel/bdist_wheel.py,sha256=tpf9WufiSO1RuEMg5oPhIfSG8DMziCZ_4muCKF69Cqo,1107
wheel/cli/__init__.py,sha256=Npq6_jKi03dhIcRnmbuFhwviVJxwO0tYEnEhWMv9cJo,4402
wheel/cli/__pycache__/__init__.cpython-313.pyc,,
wheel/cli/__pycache__/convert.cpython-313.pyc,,
wheel/cli/__pycache__/pack.cpython-313.pyc,,
wheel/cli/__pycache__/tags.cpython-313.pyc,,
wheel/cli/__pycache__/unpack.cpython-313.pyc,,
wheel/cli/convert.py,sha256=Bi0ntEXb9nTllCxWeTRQ4j-nPs3szWSEKipG_GgnMkQ,12634
wheel/cli/pack.py,sha256=CAFcHdBVulvsHYJlndKVO7KMI9JqBTZz5ii0PKxxCOs,3103
wheel/cli/tags.py,sha256=lHw-LaWrkS5Jy_qWcw-6pSjeNM6yAjDnqKI3E5JTTCU,4760
wheel/cli/unpack.py,sha256=Y_J7ynxPSoFFTT7H0fMgbBlVErwyDGcObgme5MBuz58,1021
wheel/macosx_libfile.py,sha256=k1x7CE3LPtOVGqj6NXQ1nTGYVPaeRrhVzUG_KPq3zDs,16572
wheel/metadata.py,sha256=JC4p7jlQZu2bUTAQ2fevkqLjg_X6gnNyRhLn6OUO1tc,6171
wheel/util.py,sha256=aL7aibHwYUgfc8WlolL5tXdkV4DatbJxZHb1kwHFJAU,423
wheel/vendored/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
wheel/vendored/__pycache__/__init__.cpython-313.pyc,,
wheel/vendored/packaging/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197
wheel/vendored/packaging/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174
wheel/vendored/packaging/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344
wheel/vendored/packaging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
wheel/vendored/packaging/__pycache__/__init__.cpython-313.pyc,,
wheel/vendored/packaging/__pycache__/_elffile.cpython-313.pyc,,
wheel/vendored/packaging/__pycache__/_manylinux.cpython-313.pyc,,
wheel/vendored/packaging/__pycache__/_musllinux.cpython-313.pyc,,
wheel/vendored/packaging/__pycache__/_parser.cpython-313.pyc,,
wheel/vendored/packaging/__pycache__/_structures.cpython-313.pyc,,
wheel/vendored/packaging/__pycache__/_tokenizer.cpython-313.pyc,,
wheel/vendored/packaging/__pycache__/markers.cpython-313.pyc,,
wheel/vendored/packaging/__pycache__/requirements.cpython-313.pyc,,
wheel/vendored/packaging/__pycache__/specifiers.cpython-313.pyc,,
wheel/vendored/packaging/__pycache__/tags.cpython-313.pyc,,
wheel/vendored/packaging/__pycache__/utils.cpython-313.pyc,,
wheel/vendored/packaging/__pycache__/version.cpython-313.pyc,,
wheel/vendored/packaging/_elffile.py,sha256=hbmK8OD6Z7fY6hwinHEUcD1by7czkGiNYu7ShnFEk2k,3266
wheel/vendored/packaging/_manylinux.py,sha256=P7sdR5_7XBY09LVYYPhHmydMJIIwPXWsh4olk74Uuj4,9588
wheel/vendored/packaging/_musllinux.py,sha256=z1s8To2hQ0vpn_d-O2i5qxGwEK8WmGlLt3d_26V7NeY,2674
wheel/vendored/packaging/_parser.py,sha256=4tT4emSl2qTaU7VTQE1Xa9o1jMPCsBezsYBxyNMUN-s,10347
wheel/vendored/packaging/_structures.py,sha256=q3eVNmbWJGG_S0Dit_S3Ao8qQqz_5PYTXFAKBZe5yr4,1431
wheel/vendored/packaging/_tokenizer.py,sha256=alCtbwXhOFAmFGZ6BQ-wCTSFoRAJ2z-ysIf7__MTJ_k,5292
wheel/vendored/packaging/markers.py,sha256=_TSPI1BhJYO7Bp9AzTmHQxIqHEVXaTjmDh9G-w8qzPA,8232
wheel/vendored/packaging/requirements.py,sha256=dgoBeVprPu2YE6Q8nGfwOPTjATHbRa_ZGLyXhFEln6Q,2933
wheel/vendored/packaging/specifiers.py,sha256=IWSt0SrLSP72heWhAC8UL0eGvas7XIQHjqiViVfmPKE,39778
wheel/vendored/packaging/tags.py,sha256=fedHXiOHkBxNZTXotXv8uXPmMFU9ae-TKBujgYHigcA,18950
wheel/vendored/packaging/utils.py,sha256=XgdmP3yx9-wQEFjO7OvMj9RjEf5JlR5HFFR69v7SQ9E,5268
wheel/vendored/packaging/version.py,sha256=PFJaYZDxBgyxkfYhH3SQw4qfE9ICCWrTmitvq14y3bs,16234
wheel/vendored/vendor.txt,sha256=Z2ENjB1i5prfez8CdM1Sdr3c6Zxv2rRRolMpLmBncAE,16
wheel/wheelfile.py,sha256=USCttNlJwafxt51YYFFKG7jnxz8dfhbyqAZL6jMTA9s,8411

View File

@ -1,4 +0,0 @@
Wheel-Version: 1.0
Generator: flit 3.10.1
Root-Is-Purelib: true
Tag: py3-none-any

View File

@ -1,6 +0,0 @@
[console_scripts]
wheel=wheel.cli:main
[distutils.commands]
bdist_wheel=wheel.bdist_wheel:bdist_wheel

View File

@ -2,6 +2,6 @@
"theme": "light", "theme": "light",
"default_save_path": "C:/Users/MSI/Desktop", "default_save_path": "C:/Users/MSI/Desktop",
"file_paths": [], "file_paths": [],
"last_opened_dir": "C:/Users/MSI/Desktop/python/motorola_tx", "last_opened_dir": "C:/Users/MSI/SynologyDrive/3min_be/한자연/!과제/초안전/#Debug/DBC",
"channel_info": {} "channel_info": {}
} }