-
Notifications
You must be signed in to change notification settings - Fork 636
Add NI-XNET CANFD interface #968
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 6 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
09c4f12
Add initial NI-XNET support
jaesc 55941ad
Add NI-XNET doc
jaesc a1ac1a3
update authors information
jaesc 2e0d559
Add queue len condition to read and use BRS flag to change message type
jaesc 8fd6aa3
Add NI-XNET dependency to setup.py
jaesc ba50df2
NI-XNET added to interfaces.rst
jaesc 04f4458
nixnet files related reformatted with black
jaesc 49691d8
Update nixnet.rst
hardbyte File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,257 @@ | ||
# coding: utf-8 | ||
|
||
""" | ||
NI-XNET interface module. | ||
|
||
Implementation references: | ||
NI-XNET Hardware and Software Manual: https://www.ni.com/pdf/manuals/372840h.pdf | ||
NI-XNET Python implementation: https://github.com/ni/nixnet-python | ||
|
||
Authors: Javier Rubio Giménez <[email protected]>, Jose A. Escobar <[email protected]> | ||
""" | ||
|
||
import logging | ||
import sys | ||
import time | ||
import struct | ||
|
||
from can import CanError, BusABC, Message | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
if sys.platform == "win32": | ||
try: | ||
from nixnet import session, types, constants, errors, system, database | ||
except ImportError: | ||
logger.error("Error, NIXNET python module cannot be loaded.") | ||
raise ImportError() | ||
else: | ||
logger.error("NI-XNET interface is only available on Windows systems") | ||
raise NotImplementedError("NiXNET is not supported on not Win32 platforms") | ||
|
||
|
||
class NiXNETcanBus(BusABC): | ||
""" | ||
The CAN Bus implemented for the NI-XNET interface. | ||
|
||
""" | ||
|
||
def __init__( | ||
self, | ||
channel, | ||
can_filters=None, | ||
bitrate=None, | ||
fd=False, | ||
fd_bitrate=None, | ||
brs=False, | ||
can_termination=False, | ||
log_errors=True, | ||
**kwargs | ||
): | ||
""" | ||
:param str channel: | ||
Name of the object to open (e.g. 'CAN0') | ||
|
||
:param int bitrate: | ||
Bitrate in bits/s | ||
|
||
:param list can_filters: | ||
See :meth:`can.BusABC.set_filters`. | ||
|
||
:param bool log_errors: | ||
If True, communication errors will appear as CAN messages with | ||
``is_error_frame`` set to True and ``arbitration_id`` will identify | ||
the error (default True) | ||
|
||
:raises can.interfaces.nixnet.NiXNETError: | ||
If starting communication fails | ||
|
||
""" | ||
self._rx_queue = [] | ||
self.channel = channel | ||
self.channel_info = "NI-XNET: " + channel | ||
|
||
# Set database for the initialization | ||
if not fd: | ||
database_name = ":memory:" | ||
else: | ||
if not brs: | ||
database_name = ":can_fd:" | ||
else: | ||
database_name = ":can_fd_brs:" | ||
|
||
try: | ||
|
||
# We need two sessions for this application, one to send frames and another to receive them | ||
|
||
self.__session_send = session.FrameOutStreamSession( | ||
channel, database_name=database_name | ||
) | ||
self.__session_receive = session.FrameInStreamSession( | ||
channel, database_name=database_name | ||
) | ||
|
||
# We stop the sessions to allow reconfiguration, as by default they autostart at creation | ||
self.__session_send.stop() | ||
self.__session_receive.stop() | ||
|
||
# See page 1017 of NI-XNET Hardware and Software Manual to set custom can configuration | ||
if bitrate: | ||
self.__session_send.intf.baud_rate = bitrate | ||
self.__session_receive.intf.baud_rate = bitrate | ||
|
||
if fd_bitrate: | ||
# See page 951 of NI-XNET Hardware and Software Manual to set custom can configuration | ||
self.__session_send.intf.can_fd_baud_rate = fd_bitrate | ||
self.__session_receive.intf.can_fd_baud_rate = fd_bitrate | ||
|
||
if can_termination: | ||
self.__session_send.intf.can_term = constants.CanTerm.ON | ||
self.__session_receive.intf.can_term = constants.CanTerm.ON | ||
|
||
self.__session_receive.queue_size = 512 | ||
# Once that all the parameters have been restarted, we start the sessions | ||
self.__session_send.start() | ||
self.__session_receive.start() | ||
|
||
except errors.XnetError as err: | ||
raise NiXNETError(function="__init__", error_message=err.args[0]) from None | ||
|
||
self._is_filtered = False | ||
super(NiXNETcanBus, self).__init__( | ||
channel=channel, | ||
can_filters=can_filters, | ||
bitrate=bitrate, | ||
log_errors=log_errors, | ||
**kwargs | ||
) | ||
|
||
def _recv_internal(self, timeout): | ||
try: | ||
if len(self._rx_queue)==0: | ||
fr = self.__session_receive.frames.read(4, timeout=0) | ||
for f in fr: | ||
self._rx_queue.append(f) | ||
can_frame = self._rx_queue.pop(0) | ||
|
||
# Timestamp should be converted from raw frame format(100ns increment from(12:00 a.m. January 1 1601 Coordinated | ||
# Universal Time (UTC)) to epoch time(number of seconds from January 1, 1970 (midnight UTC/GMT)) | ||
msg = Message( | ||
timestamp=can_frame.timestamp / 10000000.0 - 11644473600, | ||
channel=self.channel, | ||
is_remote_frame=can_frame.type == constants.FrameType.CAN_REMOTE, | ||
is_error_frame=can_frame.type == constants.FrameType.CAN_BUS_ERROR, | ||
is_fd=( | ||
can_frame.type == constants.FrameType.CANFD_DATA | ||
or can_frame.type == constants.FrameType.CANFDBRS_DATA | ||
), | ||
bitrate_switch=can_frame.type == constants.FrameType.CANFDBRS_DATA, | ||
is_extended_id=can_frame.identifier.extended, | ||
# Get identifier from CanIdentifier structure | ||
arbitration_id=can_frame.identifier.identifier, | ||
dlc=len(can_frame.payload), | ||
data=can_frame.payload, | ||
) | ||
|
||
return msg, self._filters is None | ||
except Exception as e: | ||
# print('Error: ', e) | ||
return None, self._filters is None | ||
|
||
def send(self, msg, timeout=None): | ||
""" | ||
Send a message using NI-XNET. | ||
|
||
:param can.Message msg: | ||
Message to send | ||
|
||
:param float timeout: | ||
Max time to wait for the device to be ready in seconds, None if time is infinite | ||
|
||
:raises can.interfaces.nixnet.NiXNETError: | ||
If writing to transmit buffer fails. | ||
It does not wait for message to be ACKed currently. | ||
""" | ||
if timeout is None: | ||
timeout = constants.TIMEOUT_INFINITE | ||
|
||
if msg.is_remote_frame: | ||
type_message = constants.FrameType.CAN_REMOTE | ||
elif msg.is_error_frame: | ||
type_message = constants.FrameType.CAN_BUS_ERROR | ||
elif msg.is_fd: | ||
if msg.bitrate_switch: | ||
type_message = constants.FrameType.CANFDBRS_DATA | ||
else: | ||
type_message = constants.FrameType.CANFD_DATA | ||
else: | ||
type_message = constants.FrameType.CAN_DATA | ||
|
||
can_frame = types.CanFrame( | ||
types.CanIdentifier(msg.arbitration_id, msg.is_extended_id), | ||
type=type_message, | ||
payload=msg.data, | ||
) | ||
|
||
try: | ||
self.__session_send.frames.write([can_frame], timeout) | ||
except errors.XnetError as err: | ||
raise NiXNETError(function="send", error_message=err.args[0]) from None | ||
|
||
def reset(self): | ||
""" | ||
Resets network interface. Stops network interface, then resets the CAN | ||
chip to clear the CAN error counters (clear error passive state). | ||
Resetting includes clearing all entries from read and write queues. | ||
""" | ||
self.__session_send.flush() | ||
self.__session_receive.flush() | ||
|
||
self.__session_send.stop() | ||
self.__session_receive.stop() | ||
|
||
self.__session_send.start() | ||
self.__session_receive.start() | ||
|
||
def shutdown(self): | ||
"""Close object.""" | ||
self.__session_send.flush() | ||
self.__session_receive.flush() | ||
|
||
self.__session_send.stop() | ||
self.__session_receive.stop() | ||
|
||
self.__session_send.close() | ||
self.__session_receive.close() | ||
|
||
@staticmethod | ||
def _detect_available_configs(): | ||
configs = [] | ||
nixnet_system = system.System() | ||
for can_intf in nixnet_system.intf_refs_can: | ||
logger.info("Channel index %d: %s", can_intf.port_num, str(can_intf)) | ||
configs.append( | ||
{ | ||
"interface": "nixnet", | ||
"channel": str(can_intf), | ||
"can_term_available": can_intf.can_term_cap | ||
== constants.CanTermCap.YES, | ||
} | ||
) | ||
nixnet_system.close() | ||
return configs | ||
|
||
|
||
# To-Do review error management, I don't like this implementation | ||
class NiXNETError(CanError): | ||
"""Error from NI-XNET driver.""" | ||
|
||
def __init__(self, function="", error_message=""): | ||
super(NiXNETError, self).__init__() | ||
#: Function that failed | ||
self.function = function | ||
#: Arguments passed to function | ||
self.error_message = error_message | ||
|
||
def __str__(self): | ||
return "Function %s failed:\n%s" % (self.function, self.error_message) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
NI-XNET | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, and you also need to add this to interfaces.rst. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done! |
||
======= | ||
|
||
This interface adds support for NI-XNET CAN controllers by `National Instruments`_. | ||
|
||
|
||
.. warning:: | ||
|
||
NI-XNET only seems to support windows platforms. | ||
|
||
.. warning:: | ||
|
||
|
||
Bus | ||
--- | ||
|
||
.. autoclass:: can.interfaces.nican.NiXNETcanBus | ||
|
||
.. autoexception:: can.interfaces.nican.NiXNETError | ||
|
||
|
||
.. _National Instruments: http://www.ni.com/can/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remote frames don't contain any data, isn't there a DLC attribute?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, there isn't...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, no big deal. RTR is not used that much nowadays anyway.