8000 Specific Exceptions: Adapting usb2can interface (+ simplify + partial… · projectgus/python-can@ead8c70 · GitHub
[go: up one dir, main page]

Skip to content

Commit ead8c70

Browse files
authored
Specific Exceptions: Adapting usb2can interface (+ simplify + partially type) (hardbyte#1091)
* Simplify + partially type usb2can; use new exceptions
1 parent bb1706b commit ead8c70

File tree

3 files changed

+87
-110
lines changed

3 files changed

+87
-110
lines changed

can/interfaces/usb2can/serial_selector.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"""
33

44
import logging
5+
from typing import List
56

67
try:
78
import win32com.client
@@ -10,7 +11,7 @@
1011
raise
1112

1213

13-
def WMIDateStringToDate(dtmDate):
14+
def WMIDateStringToDate(dtmDate) -> str:
1415
if dtmDate[4] == 0:
1516
strDateTime = dtmDate[5] + "/"
1617
else:
@@ -39,14 +40,12 @@ def WMIDateStringToDate(dtmDate):
3940
return strDateTime
4041

4142

42-
def find_serial_devices(serial_matcher="ED"):
43+
def find_serial_devices(serial_matcher: str = "ED") -> List[str]:
4344
"""
4445
Finds a list of USB devices where the serial number (partially) matches the given string.
4546
46-
:param str serial_matcher (optional):
47+
:param serial_matcher:
4748
only device IDs starting with this string are returned
48-
49-
:rtype: List[str]
5049
"""
5150
objWMIService = win32com.client.Dispatch("WbemScripting.SWbemLocator")
5251
objSWbemServices = objWMIService.ConnectServer(".", "root\\cimv2")

can/interfac ED4F es/usb2can/usb2canInterface.py

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
"""
2-
This interface is for Windows only, otherwise use socketCAN.
2+
This interface is for Windows only, otherwise use SocketCAN.
33
"""
44

55
import logging
66
from ctypes import byref
7-
8-
from can import BusABC, Message, CanError
9-
from .usb2canabstractionlayer import *
7+
from typing import Optional
8+
9+
from can import BusABC, Message, CanInitializationError, CanOperationError
10+
from .usb2canabstractionlayer import Usb2CanAbstractionLayer, CanalMsg, CanalError
11+
from .usb2canabstractionlayer import (
12+
flags_t,
13+
IS_ERROR_FRAME,
14+
IS_REMOTE_FRAME,
15+
IS_ID_TYPE,
16+
)
1017
from .serial_selector import find_serial_devices
1118

1219
# Set up logging
@@ -90,7 +97,7 @@ def __init__(
9097
flags=0x00000008,
9198
*args,
9299
bitrate=500000,
93-
**kwargs
100+
**kwargs,
94101
):
95102

96103
self.can = Usb2CanAbstractionLayer(dll)
@@ -102,15 +109,15 @@ def __init__(
102109
if not device_id:
103110
devices = find_serial_devices()
104111
if not devices:
105-
raise CanError("could not automatically find any device")
112+
raise CanInitializationError("could not automatically find any device")
106113
device_id = devices[0]
107114

108115
# convert to kb/s and cap: max rate is 1000 kb/s
109116
baudrate = min(int(bitrate // 1000), 1000)
110117

111-
self.channel_info = "USB2CAN device {}".format(device_id)
118+
self.channel_info = f"USB2CAN device {device_id}"
112119

113-
connector = "{}; {}".format(device_id, baudrate)
120+
connector = f"{device_id}; {baudrate}"
114121
self.handle = self.can.open(connector, flags_t)
115122

116123
super().__init__(
@@ -126,7 +133,7 @@ def send(self, msg, timeout=None):
126133
status = self.can.send(self.handle, byref(tx))
127134

128135
if status != CanalError.SUCCESS:
129-
raise CanError("could not send message: status == {}".format(status))
136+
raise CanOperationError("could not send message", error_code=status)
130137

131138
def _recv_internal(self, timeout):
132139

@@ -148,37 +155,36 @@ def _recv_internal(self, timeout):
148155
):
149156
rx = None
150157
else:
151-
log.error("Canal Error %s", status)
152-
rx = None
158+
raise CanOperationError("could not receive message", error_code=status)
153159

154160
return rx, False
155161

156162
def shutdown(self):
157163
"""
158164
Shuts down connection to the device safely.
159165
160-
:raise cam.CanError: is closing the connection did not work
166+
:raise cam.CanOperationError: is closing the connection did not work
161167
"""
162168
status = self.can.close(self.handle)
163169

164170
if status != CanalError.SUCCESS:
165-
raise CanError("could not shut down bus: status == {}".format(status))
171+
raise CanOperationError("could not shut down bus", error_code=status)
166172

167173
@staticmethod
168174
def _detect_available_configs():
169175
return Usb2canBus.detect_available_configs()
170176

171177
@staticmethod
172-
def detect_available_configs(serial_matcher=None):
178+
def detect_available_configs(serial_matcher: Optional[str] = None):
173179
"""
174-
Uses the Windows Management Instrumentation to identify serial devices.
180+
Uses the *Windows Management Instrumentation* to identify serial devices.
175181
176-
:param str serial_matcher (optional):
182+
:param serial_matcher:
177183
search string for automatic detection of the device serial
178184
"""
179-
if serial_matcher:
180-
channels = find_serial_devices(serial_matcher)
181-
else:
185+
if serial_matcher is None:
182186
channels = find_serial_devices()
187+
else:
188+
channels = find_serial_devices(serial_matcher)
183189

184190
return [{"interface": "usb2can", "channel": c} for c in channels]

can/interfaces/usb2can/usb2canabstractionlayer.py

Lines changed: 58 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
"""
55

66
from ctypes import *
7-
from struct import *
8-
from enum import Enum
7+
from enum import IntEnum
98
import logging
9+
from contextlib import contextmanager
1010

1111
import can
1212

@@ -25,7 +25,7 @@
2525
IS_ID_TYPE = 1
2626

2727

28-
class CanalError(Enum):
28+
class CanalError(IntEnum):
2929
SUCCESS = 0
3030
BAUDRATE = 1
3131
BUS_OFF = 2
@@ -102,6 +102,14 @@ class CanalMsg(Structure):
102102
]
103103

104104

105+
@contextmanager
106+
def error_check(error_message: str) -> None:
107+
try:
108+
yield
109+
except Exception as error:
110+
raise can.CanOperationError(error_message) from error
111+
112+
105113
class Usb2CanAbstractionLayer:
106114
"""A low level wrapper around the usb2can library.
107115
@@ -112,21 +120,26 @@ def __init__(self, dll="usb2can.dll"):
112120
"""
113121
:type dll: str or path-like
114122
:param dll (optional): the path to the usb2can DLL to load
115-
:raises OSError: if the DLL could not be loaded
123+
124+
:raises can.CanInterfaceNotImplementedError: if the DLL could not be loaded
116125
"""
117-
self.__m_dllBasic = windll.LoadLibrary(dll)
126+
try:
127+
self.__m_dllBasic = windll.LoadLibrary(dll)
128+
if self.__m_dllBasic is None:
129+
raise Exception("__m_dllBasic is None")
118130

119-
if self.__m_dllBasic is None:
120-
log.warning("DLL failed to load at path: {}".format(dll))
131+
except Exception as error:
132+
message = f"DLL failed to load at path: {dll}"
133+
raise can.CanInterfaceNotImplementedError(message) from error
121134

122-
def open(self, configuration, flags):
135+
def open(self, configuration: str, flags: int):
123136
"""
124137
Opens a CAN connection using `CanalOpen()`.
125138
126-
:param str configuration: the configuration: "device_id; baudrate"
127-
:param int flags: the flags to be set
139+
:param configuration: the configuration: "device_id; baudrate"
140+
:param flags: the flags to be set
128141
129-
:raises can.CanError: if any error occurred
142+
:raises can.CanInitializationError: if any error occurred
130143
:returns: Valid handle for CANAL API functions on success
131144
"""
132145
try:
@@ -136,100 +149,59 @@ def open(self, configuration, flags):
136149
result = self.__m_dllBasic.CanalOpen(config_ascii, flags)
137150
except Exception as ex:
138151
# catch any errors thrown by this call and re-raise
139-
raise can.CanError(
140-
'CanalOpen() failed, configuration: "{}", error: {}'.format(
141-
configuration, ex
142-
)
152+
raise can.CanInitializationError(
153+
f'CanalOpen() failed, configuration: "{configuration}", error: {ex}'
143154
)
144155
else:
145156
# any greater-than-zero return value indicates a success
146157
# (see https://grodansparadis.gitbooks.io/the-vscp-daemon/canal_interface_specification.html)
147158
# raise an error if the return code is <= 0
148159
if result <= 0:
149-
raise can.CanError(
150-
'CanalOpen() failed, configuration: "{}", return code: {}'.format(
151-
configuration, result
152-
)
160+
raise can.CanInitializationError(
161+
f'CanalOpen() failed, configuration: "{configuration}"',
162+
error_code=result,
153163
)
154164
else:
155165
return result
156166

157-
def close(self, handle):
158-
try:
159-
res = self.__m_dllBasic.CanalClose(handle)
160-
return CanalError(res)
161-
except:
162-
log.warning("Failed to close")
163-
raise
167+
def close(self, handle) -> CanalError:
168+
with error_check("Failed to close"):
169+
return CanalError(self.__m_dllBasic.CanalClose(handle))
164170

165-
def send(self, handle, msg):
166-
try:
167-
res = self.__m_dllBasic.CanalSend(handle, msg)
168-
return CanalError(res)
169-
except:
170-
log.warning("Sending error")
171-
raise can.CanError("Failed to transmit frame")
171+
def send(self, handle, msg) -> CanalError:
172+
with error_check("Failed to transmit frame"):
173+
return CanalError(self.__m_dllBasic.CanalSend(handle, msg))
172174

173-
def receive(self, handle, msg):
174-
try:
175-
res = self.__m_dllBasic.CanalReceive(handle, msg)
176-
return CanalError(res)
177-
except:
178-
log.warning("Receive error")
179-
raise
175+
def receive(self, handle, msg) -> CanalError:
176+
with error_check("Receive error"):
177+
return CanalError(self.__m_dllBasic.CanalReceive(handle, msg))
180178

181-
def blocking_send(self, handle, msg, timeout):
182-
try:
183-
res = self.__m_dllBasic.CanalBlockingSend(handle, msg, timeout)
184-
return CanalError(res)
185-
except:
186-
log.warning("Blocking send error")
187-
raise
179+
def blocking_send(self, handle, msg, timeout) -> CanalError:
180+
with error_check("Blocking send error"):
181+
return CanalError(self.__m_dllBasic.CanalBlockingSend(handle, msg, timeout))
188182

189-
def blocking_receive(self, handle, msg, timeout):
190-
try:
191-
res = self.__m_dllBasic.CanalBlockingReceive(handle, msg, timeout)
192-
return CanalError(res)
193-
except:
194-
log.warning("Blocking Receive Failed")
195-
raise
183+
def blocking_receive(self, handle, msg, timeout) -> CanalError:
184+
with error_check("Blocking Receive Failed"):
185+
return CanalError(
186+
self.__m_dllBasic.CanalBlockingReceive(handle, msg, timeout)
187+
)
196188

197-
def get_status(self, handle, status):
198-
try:
199-
res = self.__m_dllBasic.CanalGetStatus(handle, status)
200-
return CanalError(res)
201-
except:
202-
log.warning("Get status failed")
203-
raise
189+
def get_status(self, handle, status) -> CanalError:
190+
with error_check("Get status failed"):
191+
return CanalError(self.__m_dllBasic.CanalGetStatus(handle, status))
204192

205-
def get_statistics(self, handle, statistics):
206-
try:
207-
res = self.__m_dllBasic.CanalGetStatistics(handle, statistics)
208-
return CanalError(res)
209-
except:
210-
log.warning("Get Statistics failed")
211-
raise
193+
def get_statistics(self, handle, statistics) -> CanalError:
194+
with error_check("Get Statistics failed"):
195+
return CanalError(self.__m_dllBasic.CanalGetStatistics(handle, statistics))
212196

213197
def get_version(self):
214-
try:
215-
res = self.__m_dllBasic.CanalGetVersion()
216-
return res
217-
except:
218-
log.warning("Failed to get version info")
219-
raise
198+
with error_check("Failed to get version info"):
199+
return self.__m_dllBasic.CanalGetVersion()
220200

221201
def get_library_version(self):
222-
try:
223-
res = self.__m_dllBasic.CanalGetDllVersion()
224-
return res
225-
except:
226-
log.warning("Failed to get DLL version")
227-
raise
202+
with error_check("Failed to get DLL version"):
203+
return self.__m_dllBasic.CanalGetDllVersion()
228204

229205
def get_vendor_string(self):
230-
try:
231-
res = self.__m_dllBasic.CanalGetVendorString()
232-
return res
233-
except:
234-
log.warning("Failed to get vendor string")
235-
raise
206+
with error_check("Failed to get vendor string"):
207+
return self.__m_dllBasic.CanalGetVendorString()

0 commit comments

Comments
 (0)
0