1212import struct
1313from collections import namedtuple
1414
15+ import errno
16+
17+ import os
18+
1519log = logging .getLogger ('can.socketcan.native' )
1620log .info ("Loading socketcan native backend" )
1721
3135from can .interfaces .socketcan .socketcan_common import * # parseCanFilters
3236from 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
114118def 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
119123def 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+
133162def _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
224260def createSocket (can_protocol = None ):
0 commit comments