Skip to content

Commit f119490

Browse files
committed
Add OS error handling and more platform agnostic bcm frame packing format
1 parent 850f67e commit f119490

1 file changed

Lines changed: 50 additions & 14 deletions

File tree

can/interfaces/socketcan/socketcan_native.py

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
import struct
1313
from collections import namedtuple
1414

15+
import errno
16+
17+
import os
18+
1519
log = logging.getLogger('can.socketcan.native')
1620
log.info("Loading socketcan native backend")
1721

@@ -31,7 +35,7 @@
3135
from can.interfaces.socketcan.socketcan_common import * # parseCanFilters
3236
from can import Message, BusABC
3337

34-
from can.broadcastmanager import CyclicSendTaskABC
38+
from can.broadcastmanager import ModifiableCyclicTaskABC, RestartableCyclicTaskABC, LimitedDurationCyclicSendTaskABC
3539

3640
# struct module defines a binary packing format:
3741
# https://docs.python.org/3/library/struct.html#struct-format-strings
@@ -70,7 +74,7 @@ def build_bcm_header(opcode, flags, count, ival1_seconds, ival1_usec, ival2_seco
7074
# struct timeval ival1, ival2; -> llll ...
7175
# canid_t can_id; -> I
7276
# __u32 nframes; -> I
73-
bcm_cmd_msg_fmt = "@IIIllllII"
77+
bcm_cmd_msg_fmt = "@3I4l2I0q"
7478

7579
return struct.pack(bcm_cmd_msg_fmt,
7680
opcode,
@@ -113,7 +117,7 @@ def split_time(value):
113117

114118
def dissect_can_frame(frame):
115119
can_id, can_dlc, data = struct.unpack(can_frame_fmt, frame)
116-
return (can_id, can_dlc, data[:can_dlc])
120+
return can_id, can_dlc, data[:can_dlc]
117121

118122

119123
def create_bcm_socket(channel):
@@ -130,6 +134,31 @@ def create_bcm_socket(channel):
130134
return s
131135

132136

137+
def send_bcm(socket, data):
138+
"""
139+
Send raw frame to a BCM socket and handle errors.
140+
141+
:param socket:
142+
:param data:
143+
:return:
144+
"""
145+
try:
146+
return socket.send(data)
147+
except OSError as e:
148+
base = "Couldn't send CAN BCM frame. OS Error {}: {}\n".format(e.errno, os.strerror(e.errno))
149+
150+
if e.errno == errno.EINVAL:
151+
raise can.CanError(
152+
base + "You are probably referring to a non-existing frame.")
153+
elif e.errno == errno.ENETDOWN:
154+
raise can.CanError(
155+
base + "The CAN interface appears to be down."
156+
)
157+
elif e.errno == errno.EBADF:
158+
raise can.CanError(base + "The CAN socket appears to be closed.")
159+
else:
160+
raise
161+
133162
def _add_flags_to_can_id(message):
134163
can_id = message.arbitration_id
135164
if message.is_extended_id:
@@ -153,7 +182,15 @@ def __init__(self, channel, *args, **kwargs):
153182
super(SocketCanBCMBase, self).__init__(*args, **kwargs)
154183

155184

156-
class CyclicSendTask(SocketCanBCMBase, CyclicSendTaskABC):
185+
class CyclicSendTask(SocketCanBCMBase, LimitedDurationCyclicSendTaskABC, ModifiableCyclicTaskABC, RestartableCyclicTaskABC):
186+
"""
187+
A socketcan cyclic send task supports:
188+
189+
- setting of a task duration
190+
- modifying the data
191+
- stopping then subsequent restarting of the task
192+
193+
"""
157194

158195
def __init__(self, channel, message, period):
159196
"""
@@ -162,17 +199,17 @@ def __init__(self, channel, message, period):
162199
:param message: The message to be sent periodically.
163200
:param period: The rate in seconds at which to send the message.
164201
"""
165-
super(CyclicSendTask, self).__init__(channel, message, period)
202+
super(CyclicSendTask, self).__init__(channel, message, period, duration=None)
166203
self._tx_setup(message)
167204
self.message = message
168205

169206
def _tx_setup(self, message):
170207
# Create a low level packed frame to pass to the kernel
171-
can_id = _add_flags_to_can_id(message)
172-
header = build_bcm_transmit_header(can_id, 0, 0.0, self.period)
173-
frame = build_can_frame(can_id, message.data)
208+
self.can_id_with_flags = _add_flags_to_can_id(message)
209+
header = build_bcm_transmit_header(self.can_id_with_flags, 0, 0.0, self.period)
210+
frame = build_can_frame(self.can_id_with_flags, message.data)
174211
log.debug("Sending BCM command")
175-
self.bcm_socket.send(header + frame)
212+
send_bcm(self.bcm_socket, header + frame)
176213

177214
def stop(self):
178215
"""Send a TX_DELETE message to cancel this task.
@@ -182,10 +219,9 @@ def stop(self):
182219
TX_DELETE is {[bcm_msg_head]} (only the header).
183220
"""
184221
log.debug("Stopping periodic task")
185-
try:
186-
self.bcm_socket.send(build_bcm_tx_delete_header(self.can_id))
187-
except:
188-
pass
222+
223+
stopframe = build_bcm_tx_delete_header(self.can_id_with_flags)
224+
send_bcm(self.bcm_socket, stopframe)
189225

190226
def modify_data(self, message):
191227
"""Update the contents of this periodically sent message.
@@ -218,7 +254,7 @@ def __init__(self, channel, message, count, initial_period, subsequent_period):
218254
subsequent_period)
219255

220256
log.info("Sending BCM TX_SETUP command")
221-
self.bcm_socket.send(header + frame)
257+
send_bcm(self.bcm_socket, header + frame)
222258

223259

224260
def createSocket(can_protocol=None):

0 commit comments

Comments
 (0)