dmcan-sdk 1.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: dmcan_sdk
3
+ Version: 1.0.1
4
+ Summary: DM USB CAN Device SDK
5
+ Requires-Python: >=3.12
6
+ Dynamic: requires-python
7
+ Dynamic: summary
@@ -0,0 +1,10 @@
1
+
2
+
3
+ __name__ = 'dmcan'
4
+ __version__ = '1.0.1'
5
+
6
+
7
+ from dmcan import *
8
+ from dmcan.dmcan_def import *
9
+ from dmcan.dmcan_device import *
10
+ from dmcan.dmcan_context import *
@@ -0,0 +1,145 @@
1
+ import sys
2
+
3
+ from ctypes import *
4
+ from pathlib import Path
5
+ from typing import Optional, List
6
+ from dmcan.dmcan_def import dmcan_device_handle, dmcan_context
7
+ from dmcan.dmcan_device import DmCanDevice
8
+
9
+
10
+ def find_backend_dll_path():
11
+ """
12
+ 查找后端 DLL 路径
13
+
14
+ Returns:
15
+ str: DLL 的绝对路径,找不到返回 None
16
+ """
17
+ # 获取平台对应的文件名
18
+ if sys.platform == "win32":
19
+ dll_name = "libdm_device.dll"
20
+ elif sys.platform == "darwin":
21
+ dll_name = "libdm_device.dylib"
22
+ elif sys.platform == "linux":
23
+ dll_name = "libdm_device.so"
24
+ else:
25
+ return None
26
+
27
+ # 获取项目根目录(假设当前文件在 core/ 或项目根目录)
28
+ current_file = Path(__file__).resolve()
29
+
30
+ # 尝试多个可能的位置
31
+ possible_paths = [
32
+ # 相对于当前文件
33
+ current_file.parent / "dlls" / dll_name,
34
+ current_file.parent.parent / "dlls" / dll_name,
35
+ # 相对于当前工作目录
36
+ Path.cwd() / "dlls" / dll_name,
37
+ Path.cwd() / dll_name,
38
+ # 系统路径(直接使用文件名)
39
+ Path(dll_name),
40
+ ]
41
+
42
+ # 返回第一个存在的路径
43
+ for path in possible_paths:
44
+ if path.exists():
45
+ return str(path)
46
+
47
+ # 找不到,返回默认相对路径(让调用者处理错误)
48
+ return f"./dlls/{dll_name}"
49
+
50
+ class DmCanContext:
51
+
52
+ def __init__(self):
53
+
54
+ self.dll_path=find_backend_dll_path()
55
+ self.dll=CDLL(self.dll_path,winmode=0)
56
+ self._ctx=POINTER(dmcan_context)()
57
+ self._devices:List[dmcan_device_handle]=[]
58
+
59
+ self._init_funcs()
60
+
61
+ self.dll.dmcan_context_create(self._ctx)
62
+
63
+ def _init_funcs(self):
64
+
65
+ self.dll.dmcan_context_create.argtypes=[POINTER(POINTER(dmcan_context))]
66
+ self.dll.dmcan_context_create.restype=None
67
+
68
+ self.dll.dmcan_context_destroy.argtypes=[POINTER(dmcan_context)]
69
+ self.dll.dmcan_context_destroy.restype=None
70
+
71
+ self.dll.dmcan_print_version.argtypes=[POINTER(dmcan_context)]
72
+ self.dll.dmcan_print_version.restype=None
73
+
74
+ self.dll.dmcan_find_devices.argtypes=[POINTER(dmcan_context)]
75
+ self.dll.dmcan_find_devices.restype=c_int
76
+
77
+ self.dll.dmcan_find_devices_with_type.argtypes=[POINTER(dmcan_context),c_int]
78
+ self.dll.dmcan_find_devices_with_type.restype=c_int
79
+
80
+ self.dll.dmcan_show_all_devices.argtypes=[POINTER(dmcan_context)]
81
+ self.dll.dmcan_show_all_devices.restype=None
82
+
83
+ self.dll.dmcan_device_get.argtypes=[POINTER(dmcan_context),POINTER(POINTER(dmcan_device_handle)),c_int]
84
+ self.dll.dmcan_device_get.restype=c_bool
85
+
86
+ def destroy(self):
87
+ if self._ctx:
88
+ self.dll.dmcan_context_destroy(self._ctx)
89
+ self._ctx=None
90
+
91
+ def print_version(self):
92
+ if self._ctx:
93
+ self.dll.dmcan_print_version(self._ctx)
94
+
95
+ def find_devices(self, dmcan_device_type:Optional[int]=None) -> int:
96
+ if self._ctx is None:
97
+ return 0
98
+
99
+ device_cnt=0
100
+ if dmcan_device_type is None:
101
+ device_cnt= self.dll.dmcan_find_devices(self._ctx)
102
+ else:
103
+ device_cnt= self.dll.dmcan_find_devices_with_type(self._ctx, dmcan_device_type)
104
+
105
+
106
+
107
+ self._devices.clear()
108
+
109
+ for i in range(device_cnt):
110
+
111
+ dev_handle = POINTER(dmcan_device_handle)()
112
+
113
+ ret=self.dll.dmcan_device_get(self._ctx,byref(dev_handle),i)
114
+
115
+ if not ret:
116
+ continue
117
+
118
+ if not dev_handle:
119
+ continue
120
+ device=DmCanDevice(self.dll,dev_handle,i)
121
+ self._devices.append(device)
122
+
123
+ return device_cnt
124
+
125
+
126
+ def show_all_devices(self):
127
+ if self._ctx is None:
128
+ return
129
+ self.dll.dmcan_show_all_devices(self._ctx)
130
+
131
+ def get_device(self,index:int)->DmCanDevice:
132
+ return self._devices[index]
133
+
134
+
135
+ def __enter__(self):
136
+ return self
137
+
138
+ def __exit__(self,exc_type,exc_value,traceback):
139
+ for device in self._devices:
140
+ device.close()
141
+ self.destroy()
142
+
143
+ def __del__(self):
144
+ if hasattr(self, '_ctx'):
145
+ self.destroy()
@@ -0,0 +1,97 @@
1
+ import ctypes
2
+ from ctypes import *
3
+ from enum import Enum, IntEnum
4
+
5
+
6
+
7
+ class dmcan_device_type(IntEnum):
8
+ USB2CANFD = 0
9
+ USB2CANFD_DUAL=1
10
+ LinkX4C=2
11
+
12
+ class dmcan_channel_can_info(Structure):
13
+ _pack_ = 1
14
+ _fields_ = [
15
+
16
+ ("channel",c_uint8),
17
+ ("canfd",c_bool),
18
+ ("can_baudrate",c_uint32),
19
+ ("canfd_baudrate",c_uint32),
20
+ ("can_sp",c_float),
21
+ ("canfd_sp",c_float)
22
+ ]
23
+
24
+ class dmcan_channel_can_config(Structure):
25
+ _pack_ = 1
26
+ _fields_ = [
27
+ ("channel",c_uint8),
28
+ ("can_fd",c_uint8),
29
+ ("can_seg1",c_uint8),
30
+ ("can_seg2", c_uint8),
31
+ ("can_sjw", c_uint8),
32
+ ("can_prescaler", c_uint8),
33
+ ("canfd_seg1", c_uint8),
34
+ ("canfd_seg2", c_uint8),
35
+ ("canfd_sjw", c_uint8),
36
+ ("canfd_prescaler", c_uint8)
37
+ ]
38
+
39
+ class usb_rx_frame_head(Structure):
40
+ _pack_ = 1
41
+ _fields_ = [
42
+
43
+ ("can_id",c_uint32,29),
44
+ ("esi",c_uint32,1),
45
+ ("ext", c_uint32, 1),
46
+ ("rtr", c_uint32, 1),
47
+ ("timestamp", c_uint64),
48
+ ("channel",c_uint8),
49
+ ("canfd",c_uint8,1),
50
+ ("dir",c_uint8,1),
51
+ ("brs",c_uint8,1),
52
+ ("ack",c_uint8,1),
53
+ ("dlc",c_uint8,4),
54
+ ("reserved",c_uint16),
55
+ ]
56
+
57
+ def get_can_id_string(self) -> str:
58
+ """获取格式化的 CAN ID 字符串"""
59
+ if self.ext:
60
+ return f"0x{self.can_id:08X}" # 扩展帧 29位
61
+ else:
62
+ return f"0x{self.can_id:03X}" # 标准帧 11位
63
+ def get_data_length(self):
64
+ """根据 DLC 获取实际数据长度"""
65
+ dlc_map = {0:0, 1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7, 8:8,
66
+ 9:12, 10:16, 11:20, 12:24, 13:32, 14:48, 15:64}
67
+ return dlc_map.get(self.dlc, 0)
68
+
69
+ class usb_rx_frame(Structure):
70
+ _pack_ = 1
71
+ _fields_ = [
72
+ ("head",usb_rx_frame_head),
73
+ ("payload",c_uint8*64),
74
+ ]
75
+
76
+ def get_payload_bytes(self, length=None):
77
+ """获取 payload 数据字节"""
78
+ if length is None:
79
+ length = self.head.get_data_length()
80
+ length = min(length, 64)
81
+ return bytes(self.payload[:length])
82
+
83
+ def get_payload_hex(self, length=None):
84
+ """获取十六进制格式的 payload"""
85
+ data = self.get_payload_bytes(length)
86
+ return ' '.join(f'{b:02X}' for b in data)
87
+
88
+ class dmcan_context(Structure):
89
+ pass
90
+
91
+ class dmcan_device_handle(Structure):
92
+ pass
93
+
94
+
95
+ DEV_RECV_CALLBACK=CFUNCTYPE(None,POINTER(dmcan_device_handle),POINTER(usb_rx_frame))
96
+ DEV_SENT_CALLBACK=CFUNCTYPE(None,POINTER(dmcan_device_handle),POINTER(usb_rx_frame))
97
+ DEV_ERR_CALLBACK=CFUNCTYPE(None,POINTER(dmcan_device_handle),POINTER(usb_rx_frame))
@@ -0,0 +1,173 @@
1
+ import ctypes
2
+ from collections.abc import Callable
3
+ from ctypes import *
4
+ from typing import Optional, Any
5
+
6
+ from dmcan.dmcan_def import dmcan_device_handle, dmcan_channel_can_config, dmcan_channel_can_info, DEV_RECV_CALLBACK, \
7
+ DEV_SENT_CALLBACK, DEV_ERR_CALLBACK, usb_rx_frame
8
+
9
+
10
+ class DmCanDevice:
11
+
12
+
13
+ def __init__(self, dll: ctypes.CDLL,handle:POINTER(dmcan_device_handle),index:int):
14
+ self.index = index
15
+ self.dll = dll
16
+ self._handle = handle
17
+ self._recv_callback:Optional[Callable]=None
18
+ self._sent_callback:Optional[Callable]=None
19
+ self._error_callback:Optional[Callable]=None
20
+
21
+ self._recv_cb_wrapper:Optional[Callable]=None
22
+ self._sent_cb_wrapper:Optional[Callable]=None
23
+ self._error_cb_wrapper:Optional[Callable]=None
24
+
25
+ self._init_funcs()
26
+
27
+
28
+ def _init_funcs(self):
29
+
30
+ self.dll.dmcan_device_open.argtypes=[POINTER(dmcan_device_handle)]
31
+ self.dll.dmcan_device_open.restype=c_bool
32
+
33
+ self.dll.dmcan_device_close.argtypes=[POINTER(dmcan_device_handle)]
34
+ self.dll.dmcan_device_close.restype=None
35
+
36
+ self.dll.dmcan_device_get_version.argtypes = [POINTER(dmcan_device_handle),c_char_p, c_size_t]
37
+ self.dll.dmcan_device_get_version.restype = None
38
+
39
+ self.dll.dmcan_device_print_version.argtypes=[POINTER(dmcan_device_handle)]
40
+ self.dll.dmcan_device_print_version.restype=None
41
+
42
+ self.dll.dmcan_device_enable_channel.argtypes=[POINTER(dmcan_device_handle),c_uint8]
43
+ self.dll.dmcan_device_enable_channel.restype=None
44
+
45
+ self.dll.dmcan_device_disable_channel.argtypes=[POINTER(dmcan_device_handle),c_uint8]
46
+ self.dll.dmcan_device_disable_channel.restype=None
47
+
48
+ self.dll.dmcan_device_get_channel_baudrate.argtypes=[POINTER(dmcan_device_handle),c_uint8,POINTER(dmcan_channel_can_info)]
49
+ self.dll.dmcan_device_get_channel_baudrate.restype=c_bool
50
+
51
+ self.dll.dmcan_device_get_channel_baudrate_details.argtypes = [POINTER(dmcan_device_handle), c_uint8,POINTER(dmcan_channel_can_config)]
52
+ self.dll.dmcan_device_get_channel_baudrate_details.restype = c_bool
53
+
54
+ self.dll.dmcan_device_set_channel_baudrate.argtypes=[POINTER(dmcan_device_handle),c_uint8,dmcan_channel_can_info]
55
+ self.dll.dmcan_device_set_channel_baudrate.restype=c_bool
56
+
57
+ self.dll.dmcan_device_set_channel_baudrate_details.argtypes = [POINTER(dmcan_device_handle), c_uint8,dmcan_channel_can_config]
58
+ self.dll.dmcan_device_set_channel_baudrate_details.restype = c_bool
59
+
60
+ self.dll.dmcan_device_hook_recv_callback.argtypes=[POINTER(dmcan_device_handle),DEV_RECV_CALLBACK]
61
+ self.dll.dmcan_device_hook_recv_callback.restype=None
62
+
63
+ self.dll.dmcan_device_hook_sent_callback.argtypes = [POINTER(dmcan_device_handle), DEV_SENT_CALLBACK]
64
+ self.dll.dmcan_device_hook_sent_callback.restype = None
65
+
66
+ self.dll.dmcan_device_hook_err_callback.argtypes = [POINTER(dmcan_device_handle), DEV_ERR_CALLBACK]
67
+ self.dll.dmcan_device_hook_err_callback.restype = None
68
+
69
+ self.dll.dmcan_device_send_can.argtypes=[POINTER(dmcan_device_handle),c_uint8,c_uint32,c_bool,c_bool,c_bool,c_bool,c_uint8,POINTER(c_uint8)]
70
+ self.dll.dmcan_device_send_can.restype=c_bool
71
+
72
+ self.dll.dmcan_device_send_can_details.argtypes=[POINTER(dmcan_device_handle),c_uint8,c_uint32,c_uint16,c_uint32,c_int,c_uint32,c_bool,c_bool,c_bool,c_bool,c_bool,c_bool,c_uint8,POINTER(c_uint8)]
73
+ self.dll.dmcan_device_send_can_details.restype=c_bool
74
+
75
+ def open(self)->bool:
76
+ if not self._handle:
77
+ return False
78
+ return self.dll.dmcan_device_open(self._handle)
79
+
80
+ def close(self):
81
+ if not self._handle:
82
+ return None
83
+ return self.dll.dmcan_device_close(self._handle)
84
+
85
+ def get_version(self)->str:
86
+ if not self._handle:
87
+ return None
88
+ buf=create_string_buffer(256)
89
+ self.dll.dmcan_device_get_version(self._handle,buf,len(buf))
90
+ return buf.value.decode('utf-8')
91
+
92
+ def print_version(self):
93
+ if not self._handle:
94
+ return None
95
+ return self.dll.dmcan_device_print_version(self._handle)
96
+
97
+ def enable_channel(self,channel:int,enable:bool)->bool:
98
+ if not self._handle:
99
+ return False
100
+ if enable:
101
+ return self.dll.dmcan_device_enable_channel(self._handle,channel)
102
+ else:
103
+ return self.dll.dmcan_device_disable_channel(self._handle,channel)
104
+
105
+ def get_channel_baudrate(self,channel:int)->dmcan_channel_can_info:
106
+ if not self._handle:
107
+ return None
108
+
109
+ info=dmcan_channel_can_info()
110
+ if self.dll.dmcan_device_get_channel_baudrate(self._handle,channel,info):
111
+ return info
112
+ else:
113
+ return None
114
+
115
+ def set_channel_baudrate(self,channel:int,info:dmcan_channel_can_info)->bool:
116
+ if not self._handle:
117
+ return False
118
+ if info:
119
+ return self.dll.dmcan_device_set_channel_baudrate(self._handle,channel,info)
120
+ else:
121
+ return False
122
+
123
+ def send_can(self,channel:int,can_id:int,dlen:int,payload:bytes,canfd:bool=False,ext:bool=False,rtr:bool=False,brs:bool=False)->bool:
124
+ if rtr:
125
+ dlen = 0
126
+
127
+ if len(payload) < dlen:
128
+ raise ValueError(f"Payload length {len(payload)} < {dlen}")
129
+
130
+ # 转换 payload 为 c_uint8 数组
131
+ data = (c_uint8 * dlen)(*payload[:dlen])
132
+
133
+ return self.dll.dmcan_device_send_can(
134
+ self._handle, channel, can_id, canfd, ext, rtr, brs, dlen, data)
135
+
136
+ def hook_recv_callback(self, callback: Callable[[Any, usb_rx_frame], None]):
137
+
138
+ self._recv_callback = callback
139
+
140
+ @DEV_RECV_CALLBACK
141
+ def wrapper(handle_ptr, frame_ptr):
142
+ if frame_ptr:
143
+ frame = frame_ptr.contents
144
+ self._recv_callback(self, frame)
145
+
146
+ self._recv_cb_wrapper = wrapper
147
+ self.dll.dmcan_device_hook_recv_callback(self._handle, wrapper)
148
+
149
+ def hook_sent_callback(self, callback: Callable[[Any, usb_rx_frame], None]):
150
+
151
+ self._sent_callback = callback
152
+
153
+ @DEV_SENT_CALLBACK
154
+ def wrapper(handle_ptr, frame_ptr):
155
+ if frame_ptr:
156
+ frame = frame_ptr.contents
157
+ self._sent_callback(self, frame)
158
+
159
+ self._sent_cb_wrapper = wrapper
160
+ self.dll.dmcan_device_hook_sent_callback(self._handle, wrapper)
161
+
162
+ def hook_err_callback(self, callback: Callable[[Any, usb_rx_frame], None]):
163
+
164
+ self._error_callback = callback
165
+
166
+ @DEV_ERR_CALLBACK
167
+ def wrapper(handle_ptr, frame_ptr):
168
+ if frame_ptr:
169
+ frame = frame_ptr.contents
170
+ self._error_callback(self, frame)
171
+
172
+ self._error_cb_wrapper = wrapper
173
+ self.dll.dmcan_device_hook_err_callback(self._handle, wrapper)
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: dmcan_sdk
3
+ Version: 1.0.1
4
+ Summary: DM USB CAN Device SDK
5
+ Requires-Python: >=3.12
6
+ Dynamic: requires-python
7
+ Dynamic: summary
@@ -0,0 +1,9 @@
1
+ setup.py
2
+ dmcan/__init__.py
3
+ dmcan/dmcan_context.py
4
+ dmcan/dmcan_def.py
5
+ dmcan/dmcan_device.py
6
+ dmcan_sdk.egg-info/PKG-INFO
7
+ dmcan_sdk.egg-info/SOURCES.txt
8
+ dmcan_sdk.egg-info/dependency_links.txt
9
+ dmcan_sdk.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ dmcan
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,9 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name='dmcan_sdk',
5
+ version = '1.0.1',
6
+ description="DM USB CAN Device SDK",
7
+ python_requires=">=3.12",
8
+ packages=find_packages()
9
+ )