def send_bcm(bcm_socket, data): """ Send raw frame to a BCM socket and handle errors. :param socket: :param data: :return: """ try: return bcm_socket.send(data) except OSError as e: base = "Couldn't send CAN BCM frame. OS Error {}: {}\n".format( e.errno, os.strerror(e.errno)) if e.errno == errno.EINVAL: raise can.CanError( base + "You are probably referring to a non-existing frame.") elif e.errno == errno.ENETDOWN: raise can.CanError(base + "The CAN interface appears to be down.") elif e.errno == errno.EBADF: raise can.CanError(base + "The CAN socket appears to be closed.") else: raise e
def send(self, msg, timeout=None): frame = _build_can_frame(msg) # Wait for write availability. write will fail below on timeout _, ready_send_sockets, _ = select.select([], [self.socket], [], timeout) if not ready_send_sockets: raise can.CanError("Timeout while sending") # all sizes & lengths are in bytes total_sent = 0 remaining = ctypes.sizeof(frame) while remaining > 0: # this might not send the entire frame # see: http://man7.org/linux/man-pages/man2/write.2.html bytes_sent = libc.write(self.socket, ctypes.byref(frame, total_sent), remaining) if bytes_sent == 0: raise can.CanError("Transmit buffer overflow") elif bytes_sent == -1: error_message = error_code_to_str(ctypes.get_errno()) raise can.CanError( "can.socketcan_ctypes failed to transmit: {}".format( error_message)) total_sent += bytes_sent remaining -= bytes_sent log.debug("Frame transmitted with %s bytes", bytes_sent)
def open(self, configuration, flags): """ Opens a CAN connection using `CanalOpen()`. :param str configuration: the configuration: "device_id; baudrate" :param int flags: the flags to be set :raises can.CanError: if any error occurred :returns: Valid handle for CANAL API functions on success """ try: # we need to convert this into bytes, since the underlying DLL cannot # handle non-ASCII configuration strings config_ascii = configuration.encode("ascii", "ignore") result = self.__m_dllBasic.CanalOpen(config_ascii, flags) except Exception as ex: # catch any errors thrown by this call and re-raise raise can.CanError( 'CanalOpen() failed, configuration: "{}", error: {}'.format( configuration, ex)) else: # any greater-than-zero return value indicates a success # (see https://grodansparadis.gitbooks.io/the-vscp-daemon/canal_interface_specification.html) # raise an error if the return code is <= 0 if result <= 0: raise can.CanError( 'CanalOpen() failed, configuration: "{}", return code: {}'. format(configuration, result)) else: return result
def recv( self, timeout: Optional[float] = None ) -> Optional[Tuple[bytes, IP_ADDRESS_INFO, float]]: """ Receive up to **max_buffer** bytes. :param timeout: the timeout in seconds after which `None` is returned if no data arrived :returns: `None` on timeout, or a 3-tuple comprised of: - received data, - the sender of the data, and - a timestamp in seconds """ # get all sockets that are ready (can be a list with a single value # being self.socket or an empty list if self.socket is not ready) try: # get all sockets that are ready (can be a list with a single value # being self.socket or an empty list if self.socket is not ready) ready_receive_sockets, _, _ = select.select([self._socket], [], [], timeout) except socket.error as exc: # something bad (not a timeout) happened (e.g. the interface went down) raise can.CanError(f"Failed to wait for IP/UDP socket: {exc}") if ready_receive_sockets: # not empty # fetch data & source address ( raw_message_data, ancillary_data, _, # flags sender_address, ) = self._socket.recvmsg(self.max_buffer, self.received_ancillary_buffer_size) # fetch timestamp; this is configured in in _create_socket() assert len( ancillary_data) == 1, "only requested a single extra field" cmsg_level, cmsg_type, cmsg_data = ancillary_data[0] assert (cmsg_level == socket.SOL_SOCKET and cmsg_type == SO_TIMESTAMPNS ), "received control message type that was not requested" # see https://man7.org/linux/man-pages/man3/timespec.3.html -> struct timespec for details seconds, nanoseconds = struct.unpack( self.received_timestamp_struct, cmsg_data) if nanoseconds >= 1e9: raise can.CanError( f"Timestamp nanoseconds field was out of range: {nanoseconds} not less than 1e9" ) timestamp = seconds + nanoseconds * 1.0e-9 return raw_message_data, sender_address, timestamp # socket wasn't readable or timeout occurred return None
def send(self, msg, timeout=None): frame = _build_can_frame(msg) if timeout: # Wait for write availability. write will fail below on timeout select.select([], [self.socket], [], timeout) bytes_sent = libc.write(self.socket, ctypes.byref(frame), ctypes.sizeof(frame)) if bytes_sent == -1: log.debug("Error sending frame :-/") raise can.CanError("can.socketcan.ctypes failed to transmit") elif bytes_sent == 0: raise can.CanError("Transmit buffer overflow") log.debug("Frame transmitted with %s bytes", bytes_sent)
def send(self, msg: can.Message, timeout: Optional[float] = None): """Transmit a message to the CAN bus. :param Message msg: A message object. :param timeout: timeout is not supported. The function won't return until message is sent or exception is raised. :raises can.CanError: if the message could not be sent """ can_id = msg.arbitration_id if msg.is_extended_id: can_id = can_id | CAN_EFF_FLAG if msg.is_remote_frame: can_id = can_id | CAN_RTR_FLAG if msg.is_error_frame: can_id = can_id | CAN_ERR_FLAG # Pad message data msg.data.extend([0x00] * (CAN_MAX_DLC - len(msg.data))) frame = GsUsbFrame() frame.can_id = can_id frame.can_dlc = msg.dlc frame.timestamp_us = int(msg.timestamp * 1000000) frame.data = list(msg.data) try: self.gs_usb.send(frame) except usb.core.USBError: raise can.CanError("The message can not be sent")
def __init__(self, channel='vcan0', receive_own_messages=False, *args, **kwargs): """ :param str channel: The can interface name with which to create this bus. An example channel would be 'vcan0'. """ self.socket = createSocket() self.channel = channel log.debug("Result of createSocket was %d", self.socket) # Add any socket options such as can frame filters if 'can_filters' in kwargs and len(kwargs['can_filters']) > 0: log.debug("Creating a filtered can bus") self.set_filters(kwargs['can_filters']) error = bindSocket(self.socket, channel) if error < 0: m = u'bindSocket failed for channel {} with error {}'.format( channel, error) raise can.CanError(m) if receive_own_messages: error1 = recv_own_msgs(self.socket) super(SocketcanCtypes_Bus, self).__init__(*args, **kwargs)
def __init__(self, channel, receive_own_messages=False, *args, **kwargs): """ :param str channel: The can interface name with which to create this bus. An example channel would be 'vcan0' or 'can0'. """ self.socket = createSocket() self.channel = channel self.channel_info = "ctypes socketcan channel '%s'" % channel log.debug("Result of createSocket was %d", self.socket) error = bindSocket(self.socket, channel) if error < 0: m = u'bindSocket failed for channel {} with error {}'.format( channel, error) raise can.CanError(m) if receive_own_messages: error1 = recv_own_msgs(self.socket) # TODO handle potential error self._is_filtered = False kwargs.update({'receive_own_messages': receive_own_messages}) super(SocketcanCtypes_Bus, self).__init__(channel=channel, *args, **kwargs)
def send(self, handle, msg): try: res = self.__m_dllBasic.CanalSend(handle, msg) return res except: log.warning('Sending error') raise can.CanError("Failed to transmit frame")
def send(self, msg, timeout=None): log.debug("We've been asked to write a message to the bus") logger_tx = log.getChild("tx") logger_tx.debug("sending: %s", msg) if timeout: # Wait for write availability _, ready_send_sockets, _ = select.select([], [self.socket], [], timeout) if not ready_send_sockets: raise can.CanError("Timeout while sending") try: bytes_sent = self.socket.send(build_can_frame(msg)) except OSError as exc: raise can.CanError("Transmit failed (%s)" % exc) if bytes_sent == 0: raise can.CanError("Transmit buffer overflow")
def send(self, msg, timeout=None): frame = _build_can_frame(msg) bytes_sent = libc.write(self.socket, ctypes.byref(frame), ctypes.sizeof(frame)) if bytes_sent == -1: logging.debug("Error sending frame :-/") raise can.CanError("can.socketcan.ctypes failed to transmit") logging.debug("Frame transmitted with %s bytes", bytes_sent)
def send(self, msg, timeout=None): log.debug("We've been asked to write a message to the bus") logger_tx = log.getChild("tx") logger_tx.debug("sending: %s", msg) if timeout: # Wait for write availability _, ready_send_sockets, _ = select.select([], [self.socket], [], timeout) if not ready_send_sockets: raise can.CanError("Timeout while sending") try: self.socket.sendall(build_can_frame(msg)) except OSError as exc: error_message = error_code_to_str(exc.errno) raise can.CanError( "can.socketcan_native failed to transmit: {}".format( error_message))
def _send_once(self, data: bytes, channel: Optional[str] = None) -> int: try: if self.channel == "" and channel: # Message must be addressed to a specific channel sent = self.socket.sendto(data, (channel,)) else: sent = self.socket.send(data) except socket.error as exc: raise can.CanError("Failed to transmit: %s" % exc) return sent
def from_buffer(cls, buf): try: length, = struct.unpack_from('B', buf) except struct.error: raise NeedMoreDataError() if len(buf) - 1 < length: raise NeedMoreDataError() text = buf[1:1 + length].decode('utf-8') return cls(can.CanError(text))
def send(self, msg, timeout=None): log.debug("We've been asked to write a message to the bus") arbitration_id = msg.arbitration_id if msg.id_type: log.debug("sending an extended id type message") arbitration_id |= 0x80000000 if msg.is_remote_frame: log.debug("requesting a remote frame") arbitration_id |= 0x40000000 if msg.is_error_frame: log.warning("Trying to send an error frame - this won't work") arbitration_id |= 0x20000000 log_tx.debug("Sending: %s", msg) if timeout: # Wait for write availability. send will fail below on timeout select.select([], [self.socket], [], timeout) try: bytes_sent = self.socket.send( build_can_frame(arbitration_id, msg.data)) except OSError as exc: raise can.CanError("Transmit failed (%s)" % exc) if bytes_sent == 0: raise can.CanError("Transmit buffer overflow")
def initialize_nodes(self): try: # For some reason, reconnect is needed... self.connect() self.network.scanner.search() # Give time to complete the search process time.sleep(0.5) self.logger.info( f"CAN Network scan result: nodes number {self.network.scanner.nodes}" ) for node_id in self.network.scanner.nodes: new_node = BaseNode402(node_id, 'LOVATO_VLB3.eds') self.network.add_node(new_node) new_node.nmt.state = "PRE-OPERATIONAL" time.sleep(0.2) new_node.rpdo.read() new_node.tpdo.read() new_node.rpdo[1].enable = True new_node.rpdo[1].start(0.05) new_node.rpdo[1]['CiA: Controlword'].bits[0] = 1 new_node.rpdo[1]['CiA: Controlword'].bits[1] = 1 new_node.rpdo[1]['CiA: Controlword'].bits[3] = 1 new_node.nmt.state = 'OPERATIONAL' new_node.rpdo[1]['CiA: Controlword'].bits[0] = 1 new_node.rpdo[1]['CiA: Controlword'].bits[1] = 1 new_node.rpdo[1]['CiA: Controlword'].bits[3] = 1 time.sleep(0.2) self.reset_faulty_node(new_node) new_node.rpdo[1][ 'CiA: Controlword'].raw = self.SWITCH_ON_DISABLED self.nodes_list.append(new_node) if len(self.nodes_list) > 0: self.logger.info( f"Node number {self.nodes_list[0].id} identified") else: self.logger.warning("No nodes found...") except can.CanError: self.connected = False self.logger.error( "Connection was successful but no active node is completing the bus," " meaning that communication is impossible. Please setup a node before" " trying to connect again") raise can.CanError( "Connection was successful but no active node is completing the bus," " meaning that communication is impossible. Please setup a node before" " trying to connect again") # Wait before setting all to ready time.sleep(0.5) self.set_network_state(self.SWITCHED_ON)
def connect(self): try: self.network = canopen.Network() self.network.connect(bitrate=self.bitrate, channel=self.interface_name, bustype=self.bustype) self.connected = True except OSError: self.connected = False self.logger.error( f"Could not connect on the {self.interface_name} interface." " A reboot will probably solve the problem") raise can.CanError( f"Could not connect on the {self.interface_name} interface." " A reboot will probably solve the problem")
def _send_once(self, data, channel=None): try: if self.channel == "" and channel: # Message must be addressed to a specific channel if HAS_NATIVE_SUPPORT: sent = self.socket.sendto(data, (channel, )) else: addr = get_addr(self.socket, channel) sent = libc.sendto(self.socket.fileno(), data, len(data), 0, addr, len(addr)) else: sent = self.socket.send(data) except socket.error as exc: raise can.CanError("Failed to transmit: %s" % exc) return sent
def __new__(cls, other, channel, *args, **kwargs): config = load_config(config={'channel': channel}) # Import the correct implementation of CyclicSendTask if config['interface'] == 'socketcan_ctypes': from can.interfaces.socketcan.socketcan_ctypes import CyclicSendTask as _ctypesCyclicSendTask cls = _ctypesCyclicSendTask elif config['interface'] == 'socketcan_native': from can.interfaces.socketcan.socketcan_native import CyclicSendTask as _nativeCyclicSendTask cls = _nativeCyclicSendTask else: raise can.CanError( "Current CAN interface doesn't support CyclicSendTask") return cls(config['channel'], *args, **kwargs)
def __init__(self, channel, bus, address, bitrate, can_filters=None, **kwargs): """ :param channel: usb device name :param bus: number of the bus that the device is connected to :param address: address of the device on the bus it is connected to :param can_filters: not supported :param bitrate: CAN network bandwidth (bits/s) """ gs_usb = GsUsb.find(bus=bus, address=address) if not gs_usb: raise can.CanError("Can not find device {}".format(channel)) self.gs_usb = gs_usb self.channel_info = channel self.gs_usb.set_bitrate(bitrate) self.gs_usb.start() super().__init__(channel=channel, can_filters=can_filters, **kwargs)
def send(self, msg, timeout=None): log.debug("We've been asked to write a message to the bus") arbitration_id = msg.arbitration_id if msg.id_type: log.debug("sending an extended id type message") arbitration_id |= 0x80000000 if msg.is_remote_frame: log.debug("requesting a remote frame") arbitration_id |= 0x40000000 if msg.is_error_frame: log.warning("Trying to send an error frame - this won't work") arbitration_id |= 0x20000000 l = log.getChild("tx") l.debug("sending: %s", msg) try: self.socket.send(build_can_frame(arbitration_id, msg.data)) except OSError: l.warning("Failed to send: %s", msg) raise can.CanError("can.socketcan.native failed to transmit")
def bindSocket(socketID, channel_name): """ Binds the given socket to the given interface. :param int socketID: The ID of the socket to be bound :param str channel_name: The interface name to find and bind. :return: The error code from the bind call. +----+----------------------------+ | 0 |protocol invalid | +----+----------------------------+ | -1 |socket creation unsuccessful| +----+----------------------------+ """ log.debug('Binding socket with id %d to channel %s', socketID, channel_name) socketID = ctypes.c_int(socketID) ifr = IFREQ() ifr.ifr_name = channel_name.encode('ascii') log.debug('calling ioctl SIOCGIFINDEX') # ifr.ifr_ifindex gets filled with that device's index ret = libc.ioctl(socketID, SIOCGIFINDEX, ctypes.byref(ifr)) if ret < 0: m = u'Failure while getting "{}" interface index.'.format(channel_name) raise can.CanError(m) log.info('ifr.ifr_ifindex: %d', ifr.ifr_ifindex) # select the CAN interface and bind the socket to it addr = SOCKADDR_CAN(AF_CAN, ifr.ifr_ifindex) error = libc.bind(socketID, ctypes.byref(addr), ctypes.sizeof(addr)) if error < 0: log.error("Couldn't bind socket") log.debug('bind returned: %d', error) return error
def _recv_internal( self, timeout: Optional[float] ) -> Tuple[Optional[Message], bool]: try: # get all sockets that are ready (can be a list with a single value # being self.socket or an empty list if self.socket is not ready) ready_receive_sockets, _, _ = select.select([self.socket], [], [], timeout) except socket.error as exc: # something bad happened (e.g. the interface went down) raise can.CanError(f"Failed to receive: {exc}") if ready_receive_sockets: # not empty get_channel = self.channel == "" msg = capture_message(self.socket, get_channel) if msg and not msg.channel and self.channel: # Default to our own channel msg.channel = self.channel return msg, self._is_filtered # socket wasn't readable or timeout occurred return None, self._is_filtered
def __new__(cls, other, channel, *args, **kwargs): config = load_config(config={'channel': channel}) # Import the correct implementation of CyclicSendTask if config['interface'] == 'socketcan_ctypes': from can.interfaces.socketcan.socketcan_ctypes import CyclicSendTask as _ctypesCyclicSendTask cls = _ctypesCyclicSendTask elif config['interface'] == 'socketcan_native': from can.interfaces.socketcan.socketcan_native import CyclicSendTask as _nativeCyclicSendTask cls = _nativeCyclicSendTask # CyclicSendTask has not been fully implemented on remote interface yet. # Waiting for issue #80 which will change the API to make it easier for # interfaces other than socketcan to implement it #elif can.rc['interface'] == 'remote': # from can.interfaces.remote import CyclicSendTask as _remoteCyclicSendTask # cls = _remoteCyclicSendTask else: raise can.CanError( "Current CAN interface doesn't support CyclicSendTask") return cls(config['channel'], *args, **kwargs)
def send(self, msg: Message, timeout: Optional[float] = None) -> None: """Transmit a message to the CAN bus. :param msg: A message object. :param timeout: Wait up to this many seconds for the transmit queue to be ready. If not given, the call may fail immediately. :raises can.CanError: if the message could not be written. """ log.debug("We've been asked to write a message to the bus") logger_tx = log.getChild("tx") logger_tx.debug("sending: %s", msg) started = time.time() # If no timeout is given, poll for availability if timeout is None: timeout = 0 time_left = timeout data = build_can_frame(msg) while time_left >= 0: # Wait for write availability ready = select.select([], [self.socket], [], time_left)[1] if not ready: # Timeout break channel = str(msg.channel) if msg.channel else None sent = self._send_once(data, channel) if sent == len(data): return # Not all data were sent, try again with remaining data data = data[sent:] time_left = timeout - (time.time() - started) raise can.CanError("Transmit buffer full")
def capture_message(sock, get_channel=False): """ Captures a message from given socket. :param socket.socket sock: The socket to read a message from. :param bool get_channel: Find out which channel the message comes from. :return: The received message, or None on failure. """ # Fetching the Arb ID, DLC and Data try: if get_channel: if HAS_NATIVE_SUPPORT: cf, addr = sock.recvfrom(CANFD_MTU) channel = addr[0] if isinstance(addr, tuple) else addr else: data = ctypes.create_string_buffer(CANFD_MTU) addr = ctypes.create_string_buffer(32) addrlen = ctypes.c_int(len(addr)) received = libc.recvfrom(sock.fileno(), data, len(data), 0, addr, ctypes.byref(addrlen)) cf = data.raw[:received] # Figure out the channel name family, ifindex = struct.unpack_from("Hi", addr.raw) assert family == AF_CAN data = struct.pack("16xi", ifindex) res = fcntl.ioctl(sock, SIOCGIFNAME, data) channel = ctypes.create_string_buffer(res).value.decode() else: cf = sock.recv(CANFD_MTU) channel = None except socket.error as exc: raise can.CanError("Error receiving: %s" % exc) can_id, can_dlc, flags, data = dissect_can_frame(cf) #log.debug('Received: can_id=%x, can_dlc=%x, data=%s', can_id, can_dlc, data) # Fetching the timestamp binary_structure = "@LL" res = fcntl.ioctl(sock, SIOCGSTAMP, struct.pack(binary_structure, 0, 0)) seconds, microseconds = struct.unpack(binary_structure, res) timestamp = seconds + microseconds * 1e-6 # EXT, RTR, ERR flags -> boolean attributes # /* special address description flags for the CAN_ID */ # #define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */ # #define CAN_RTR_FLAG 0x40000000U /* remote transmission request */ # #define CAN_ERR_FLAG 0x20000000U /* error frame */ is_extended_frame_format = bool(can_id & CAN_EFF_FLAG) is_remote_transmission_request = bool(can_id & CAN_RTR_FLAG) is_error_frame = bool(can_id & CAN_ERR_FLAG) is_fd = len(cf) == CANFD_MTU bitrate_switch = bool(flags & CANFD_BRS) error_state_indicator = bool(flags & CANFD_ESI) if is_extended_frame_format: #log.debug("CAN: Extended") # TODO does this depend on SFF or EFF? arbitration_id = can_id & 0x1FFFFFFF else: #log.debug("CAN: Standard") arbitration_id = can_id & 0x000007FF msg = Message(timestamp=timestamp, channel=channel, arbitration_id=arbitration_id, is_extended_id=is_extended_frame_format, is_remote_frame=is_remote_transmission_request, is_error_frame=is_error_frame, is_fd=is_fd, bitrate_switch=bitrate_switch, error_state_indicator=error_state_indicator, dlc=can_dlc, data=data) #log_rx.debug('Received: %s', msg) return msg
def check_status(result, function, arguments): if result < 0: raise can.CanError(error_code_to_str(ctypes.get_errno())) return result
def test_exception(self): event1 = events.RemoteException(can.CanError('This is an error')) buf = event1.encode() event2 = events.RemoteException.from_buffer(buf) self.assertEqual(str(event1.exc), str(event2.exc)) self.assertEqual(len(event2), len(buf))
def raise_error(msg): raise can.CanError('This is some error')
def _recv_internal(self, timeout): if len(self.__message_buffer) != 0: can_message = self.__message_buffer.popleft() return can_message, False try: # get all sockets that are ready (can be a list with a single value # being self.socket or an empty list if self.socket is not ready) ready_receive_sockets, _, _ = select.select([self.__socket], [], [], timeout) except socket.error as exc: # something bad happened (e.g. the interface went down) log.error(f"Failed to receive: {exc}") raise can.CanError(f"Failed to receive: {exc}") try: if not ready_receive_sockets: # socket wasn't readable or timeout occurred log.debug("Socket not ready") return None, False ascii_msg = self.__socket.recv(1024).decode( "ascii") # may contain multiple messages self.__receive_buffer += ascii_msg log.debug(f"Received Ascii Message: {ascii_msg}") buffer_view = self.__receive_buffer chars_processed_successfully = 0 while True: if len(buffer_view) == 0: break start = buffer_view.find("<") if start == -1: log.warning( f"Bad data: No opening < found => discarding entire buffer '{buffer_view}'" ) chars_processed_successfully = len(self.__receive_buffer) break end = buffer_view.find(">") if end == -1: log.warning( "Got incomplete message => waiting for more data") if len(buffer_view) > 200: log.warning( "Incomplete message exceeds 200 chars => Discarding" ) chars_processed_successfully = len( self.__receive_buffer) break chars_processed_successfully += end + 1 single_message = buffer_view[start:end + 1] parsed_can_message = convert_ascii_message_to_can_message( single_message) if parsed_can_message is None: log.warning(f"Invalid Frame: {single_message}") else: self.__message_buffer.append(parsed_can_message) buffer_view = buffer_view[end + 1:] self.__receive_buffer = self.__receive_buffer[ chars_processed_successfully + 1:] can_message = (None if len(self.__message_buffer) == 0 else self.__message_buffer.popleft()) return can_message, False except Exception as exc: log.error(f"Failed to receive: {exc} {traceback.format_exc()}") raise can.CanError( f"Failed to receive: {exc} {traceback.format_exc()}")