Skip to content

Commit 0262b8d

Browse files
authored
Merge pull request hardbyte#1083 from hardbyte/felixdivo-issue-1046-vector
Specific Exceptions: Adapting vector interface
2 parents 0e0c64f + 4d86f70 commit 0262b8d

File tree

5 files changed

+144
-90
lines changed

5 files changed

+144
-90
lines changed

can/interfaces/vector/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
"""
33

44
from .canlib import VectorBus, VectorChannelConfig
5-
from .exceptions import VectorError
5+
from .exceptions import VectorError, VectorOperationError, VectorInitializationError

can/interfaces/vector/canlib.py

Lines changed: 45 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@
1010
import logging
1111
import time
1212
import os
13-
from typing import List, Optional, Tuple, Sequence, Union
14-
15-
import typing
13+
from typing import List, NamedTuple, Optional, Tuple, Sequence, Union
1614

1715
try:
1816
# Try builtin Python 3 Windows API
@@ -31,22 +29,24 @@
3129

3230
# Import Modules
3331
# ==============
34-
from can import BusABC, Message
32+
from can import BusABC, Message, CanInterfaceNotImplementedError, CanInitializationError
3533
from can.util import (
3634
len2dlc,
3735
dlc2len,
3836
deprecated_args_alias,
3937
time_perfcounter_correlation,
4038
)
41-
from can.typechecking import CanFilters
39+
from can.typechecking import AutoDetectedConfig, CanFilters
40+
4241
from .exceptions import VectorError
4342

4443
# Define Module Logger
4544
# ====================
4645
LOG = logging.getLogger(__name__)
4746

48-
# Import Vector API module
49-
# ========================
47+
# Import Vector API modules
48+
# =========================
49+
from .exceptions import VectorError, VectorInitializationError, VectorOperationError
5050
from . import xldefine, xlclass
5151

5252
# Import safely Vector API module for Travis tests
@@ -131,14 +131,20 @@ def __init__(
131131
Bus timing value tseg1 (data)
132132
:param tseg2_dbr:
133133
Bus timing value tseg2 (data)
134+
135+
:raise can.CanInterfaceNotImplementedError:
136+
If the current operating system is not supported or the driver could not be loaded.
137+
:raise can.CanInitializationError:
138+
If the bus could not be set up.
139+
This may or may not be a :class:`can.interfaces.vector.VectorInitializationError`.
134140
"""
135141
if os.name != "nt" and not kwargs.get("_testing", False):
136-
raise OSError(
142+
raise CanInterfaceNotImplementedError(
137143
f'The Vector interface is only supported on Windows, but you are running "{os.name}"'
138144
)
139145

140146
if xldriver is None:
141-
raise ImportError("The Vector API has not been loaded")
147+
raise CanInterfaceNotImplementedError("The Vector API has not been loaded")
142148

143149
self.poll_interval = poll_interval
144150

@@ -176,7 +182,7 @@ def __init__(
176182
self.channels = channel_index
177183
else:
178184
# Is there any better way to raise the error?
179-
raise Exception(
185+
raise CanInitializationError(
180186
"None of the configured channels could be found on the specified hardware."
181187
)
182188

@@ -201,7 +207,7 @@ def __init__(
201207
# If hardware is unavailable, this function returns -1.
202208
# Raise an exception as if the driver
203209
# would have signalled XL_ERR_HW_NOT_PRESENT.
204-
raise VectorError(
210+
raise VectorInitializationError(
205211
xldefine.XL_Status.XL_ERR_HW_NOT_PRESENT,
206212
xldefine.XL_Status.XL_ERR_HW_NOT_PRESENT.name,
207213
"xlGetChannelIndex",
@@ -305,9 +311,9 @@ def __init__(
305311
xldriver.xlActivateChannel(
306312
self.port_handle, self.mask, xldefine.XL_BusTypes.XL_BUS_TYPE_CAN, 0
307313
)
308-
except VectorError:
314+
except VectorOperationError as error:
309315
self.shutdown()
310-
raise
316+
raise VectorInitializationError.from_generic(error) from None
311317

312318
# Calculate time offset for absolute timestamps
313319
offset = xlclass.XLuint64()
@@ -316,25 +322,25 @@ def __init__(
316322
ts, perfcounter = time_perfcounter_correlation()
317323
try:
318324
xldriver.xlGetSyncTime(self.port_handle, offset)
319-
except VectorError:
325+
except VectorInitializationError:
320326
xldriver.xlGetChannelTime(self.port_handle, self.mask, offset)
321327
current_perfcounter = time.perf_counter()
322328
now = ts + (current_perfcounter - perfcounter)
323329
self._time_offset = now - offset.value * 1e-9
324330
else:
325331
try:
326332
xldriver.xlGetSyncTime(self.port_handle, offset)
327-
except VectorError:
333+
except VectorInitializationError:
328334
xldriver.xlGetChannelTime(self.port_handle, self.mask, offset)
329335
self._time_offset = time.time() - offset.value * 1e-9
330336

331-
except VectorError:
337+
except VectorInitializationError:
332338
self._time_offset = 0.0
333339

334340
self._is_filtered = False
335341
super().__init__(channel=channel, can_filters=can_filters, **kwargs)
336342

337-
def _apply_filters(self, filters):
343+
def _apply_filters(self, filters) -> None:
338344
if filters:
339345
# Only up to one filter per ID type allowed
340346
if len(filters) == 1 or (
@@ -352,7 +358,7 @@ def _apply_filters(self, filters):
352358
if can_filter.get("extended")
353359
else xldefine.XL_AcceptanceFilter.XL_CAN_STD,
354360
)
355-
except VectorError as exc:
361+
except VectorOperationError as exc:
356362
LOG.warning("Could not set filters: %s", exc)
357363
# go to fallback
358364
else:
@@ -379,7 +385,7 @@ def _apply_filters(self, filters):
379385
0x0,
380386
xldefine.XL_AcceptanceFilter.XL_CAN_STD,
381387
)
382-
except VectorError as exc:
388+
except VectorOperationError as exc:
383389
LOG.warning("Could not reset filters: %s", exc)
384390

385391
def _recv_internal(
@@ -394,7 +400,7 @@ def _recv_internal(
394400
else:
395401
msg = self._recv_can()
396402

397-
except VectorError as exc:
403+
except VectorOperationError as exc:
398404
if exc.error_code != xldefine.XL_Status.XL_ERR_QUEUE_IS_EMPTY:
399405
raise
400406
else:
@@ -437,7 +443,7 @@ def _recv_canfd(self) -> Optional[Message]:
437443
timestamp = xl_can_rx_event.timeStamp * 1e-9
438444
channel = self.index_to_channel.get(xl_can_rx_event.chanIndex)
439445

440-
msg = Message(
446+
return Message(
441447
timestamp=timestamp + self._time_offset,
442448
arbitration_id=msg_id & 0x1FFFFFFF,
443449
is_extended_id=bool(
@@ -461,7 +467,6 @@ def _recv_canfd(self) -> Optional[Message]:
461467
dlc=dlc,
462468
data=data_struct.data[:dlc],
463469
)
464-
return msg
465470

466471
def _recv_can(self) -> Optional[Message]:
467472
xl_event = xlclass.XLevent()
@@ -478,7 +483,7 @@ def _recv_can(self) -> Optional[Message]:
478483
timestamp = xl_event.timeStamp * 1e-9
479484
channel = self.index_to_channel.get(xl_event.chanIndex)
480485

481-
msg = Message(
486+
return Message(
482487
timestamp=timestamp + self._time_offset,
483488
arbitration_id=msg_id & 0x1FFFFFFF,
484489
is_extended_id=bool(
@@ -498,7 +503,6 @@ def _recv_can(self) -> Optional[Message]:
498503
data=xl_event.tagData.msg.data[:dlc],
499504
channel=channel,
500505
)
501-
return msg
502506

503507
def handle_can_event(self, event: xlclass.XLevent) -> None:
504508
"""Handle non-message CAN events.
@@ -507,9 +511,7 @@ def handle_can_event(self, event: xlclass.XLevent) -> None:
507511
when `event.tag` is not `XL_RECEIVE_MSG`. Subclasses can implement this method.
508512
509513
:param event: XLevent that could have a `XL_CHIP_STATE`, `XL_TIMER` or `XL_SYNC_PULSE` tag.
510-
:return: None
511514
"""
512-
pass
513515

514516
def handle_canfd_event(self, event: xlclass.XLcanRxEvent) -> None:
515517
"""Handle non-message CAN FD events.
@@ -520,27 +522,25 @@ def handle_canfd_event(self, event: xlclass.XLcanRxEvent) -> None:
520522
521523
:param event: `XLcanRxEvent` that could have a `XL_CAN_EV_TAG_RX_ERROR`, `XL_CAN_EV_TAG_TX_ERROR`,
522524
`XL_TIMER` or `XL_CAN_EV_TAG_CHIP_STATE` tag.
523-
:return: None
524525
"""
525-
pass
526526

527-
def send(self, msg: Message, timeout: typing.Optional[float] = None):
527+
def send(self, msg: Message, timeout: Optional[float] = None):
528528
self._send_sequence([msg])
529529

530-
def _send_sequence(self, msgs: typing.Sequence[Message]) -> int:
530+
def _send_sequence(self, msgs: Sequence[Message]) -> int:
531531
"""Send messages and return number of successful transmissions."""
532532
if self.fd:
533533
return self._send_can_fd_msg_sequence(msgs)
534534
else:
535535
return self._send_can_msg_sequence(msgs)
536536

537-
def _get_tx_channel_mask(self, msgs: typing.Sequence[Message]) -> int:
537+
def _get_tx_channel_mask(self, msgs: Sequence[Message]) -> int:
538538
if len(msgs) == 1:
539539
return self.channel_masks.get(msgs[0].channel, self.mask)
540540
else:
541541
return self.mask
542542

543-
def _send_can_msg_sequence(self, msgs: typing.Sequence[Message]) -> int:
543+
def _send_can_msg_sequence(self, msgs: Sequence[Message]) -> int:
544544
"""Send CAN messages and return number of successful transmissions."""
545545
mask = self._get_tx_channel_mask(msgs)
546546
message_count = ctypes.c_uint(len(msgs))
@@ -571,7 +571,7 @@ def _build_xl_event(msg: Message) -> xlclass.XLevent:
571571

572572
return xl_event
573573

574-
def _send_can_fd_msg_sequence(self, msgs: typing.Sequence[Message]) -> int:
574+
def _send_can_fd_msg_sequence(self, msgs: Sequence[Message]) -> int:
575575
"""Send CAN FD messages and return number of successful transmissions."""
576576
mask = self._get_tx_channel_mask(msgs)
577577
message_count = len(msgs)
@@ -611,22 +611,22 @@ def _build_xl_can_tx_event(msg: Message) -> xlclass.XLcanTxEvent:
611611

612612
return xl_can_tx_event
613613

614-
def flush_tx_buffer(self):
614+
def flush_tx_buffer(self) -> None:
615615
xldriver.xlCanFlushTransmitQueue(self.port_handle, self.mask)
616616

617-
def shutdown(self):
617+
def shutdown(self) -> None:
618618
xldriver.xlDeactivateChannel(self.port_handle, self.mask)
619619
xldriver.xlClosePort(self.port_handle)
620620
xldriver.xlCloseDriver()
621621

622-
def reset(self):
622+
def reset(self) -> None:
623623
xldriver.xlDeactivateChannel(self.port_handle, self.mask)
624624
xldriver.xlActivateChannel(
625625
self.port_handle, self.mask, xldefine.XL_BusTypes.XL_BUS_TYPE_CAN, 0
626626
)
627627

628628
@staticmethod
629-
def _detect_available_configs():
629+
def _detect_available_configs() -> List[AutoDetectedConfig]:
630630
configs = []
631631
channel_configs = get_channel_configs()
632632
LOG.info("Found %d channels", len(channel_configs))
@@ -663,7 +663,7 @@ def _detect_available_configs():
663663
def popup_vector_hw_configuration(wait_for_finish: int = 0) -> None:
664664
"""Open vector hardware configuration window.
665665
666-
:param int wait_for_finish:
666+
:param wait_for_finish:
667667
Time to wait for user input in milliseconds.
668668
"""
669669
xldriver.xlPopupHwConfig(ctypes.c_char_p(), ctypes.c_uint(wait_for_finish))
@@ -681,9 +681,9 @@ def get_application_config(
681681
:return:
682682
Returns a tuple of the hardware type, the hardware index and the
683683
hardware channel.
684-
:raises VectorError:
685-
Raises a VectorError when the application name does not exist in
686-
Vector Hardware Configuration.
684+
685+
:raises can.interfaces.vector.VectorInitializationError:
686+
If the application name does not exist in the Vector hardware configuration.
687687
"""
688688
hw_type = ctypes.c_uint()
689689
hw_index = ctypes.c_uint()
@@ -732,6 +732,9 @@ def set_application_config(
732732
hardware type are present.
733733
:param hw_channel:
734734
The channel index of the interface.
735+
736+
:raises can.interfaces.vector.VectorInitializationError:
737+
If the application name does not exist in the Vector hardware configuration.
735738
"""
736739
xldriver.xlSetApplConfig(
737740
app_name.encode(),
@@ -757,7 +760,7 @@ def set_timer_rate(self, timer_rate_ms: int) -> None:
757760
xldriver.xlSetTimerRate(self.port_handle, timer_rate_10us)
758761

759762

760-
class VectorChannelConfig(typing.NamedTuple):
763+
class VectorChannelConfig(NamedTuple):
761764
name: str
762765
hwType: xldefine.XL_HardwareType
763766
hwIndex: int

can/interfaces/vector/exceptions.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
"""
2-
"""
1+
"""Exception/error declarations for the vector interface."""
32

4-
from can import CanError
3+
from can import CanError, CanInitializationError, CanOperationError
54

65

76
class VectorError(CanError):
@@ -14,4 +13,16 @@ def __init__(self, error_code, error_string, function):
1413
self._args = error_code, error_string, function
1514

1615
def __reduce__(self):
17-
return VectorError, self._args, {}
16+
return type(self), self._args, {}
17+
18+
19+
class VectorInitializationError(VectorError, CanInitializationError):
20+
@staticmethod
21+
def from_generic(error: VectorError) -> "VectorInitializationError":
22+
return VectorInitializationError(*error._args)
23+
24+
25+
class VectorOperationError(VectorError, CanOperationError):
26+
@staticmethod
27+
def from_generic(error: VectorError) -> "VectorOperationError":
28+
return VectorOperationError(*error._args)

0 commit comments

Comments
 (0)