Skip to content

Commit a3d1e13

Browse files
christiansandberghardbyte
authored andcommitted
Add IXXAT periodic send API
1 parent 1e836bf commit a3d1e13

5 files changed

Lines changed: 120 additions & 12 deletions

File tree

can/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,6 @@ class CanError(IOError):
3434
from can.broadcastmanager import send_periodic, \
3535
CyclicSendTaskABC, \
3636
LimitedDurationCyclicSendTaskABC, \
37+
ModifiableCyclicTaskABC, \
3738
MultiRateCyclicSendTaskABC, \
3839
RestartableCyclicTaskABC

can/interfaces/ixxat/canlib.py

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
from can import CanError, BusABC
1414
from can import Message
1515
from can.interfaces.ixxat import constants, structures
16-
16+
from can.broadcastmanager import (LimitedDurationCyclicSendTaskABC,
17+
RestartableCyclicTaskABC)
1718
from can.ctypesutil import CLibrary, HANDLE, PHANDLE
1819

1920
from .constants import VCI_MAX_ERRSTRLEN
@@ -176,6 +177,22 @@ def __check_status(result, function, arguments):
176177
_canlib.map_symbol("canControlAddFilterIds", ctypes.c_long, (HANDLE, ctypes.c_int, ctypes.c_uint32, ctypes.c_uint32), __check_status)
177178
#EXTERN_C HRESULT canControlRemFilterIds (HANDLE hControl, BOOL fExtendend, UINT32 dwCode, UINT32 dwMask );
178179
_canlib.map_symbol("canControlRemFilterIds", ctypes.c_long, (HANDLE, ctypes.c_int, ctypes.c_uint32, ctypes.c_uint32), __check_status)
180+
#EXTERN_C HRESULT canSchedulerOpen (HANDLE hDevice, UINT32 dwCanNo, PHANDLE phScheduler );
181+
_canlib.map_symbol("canSchedulerOpen", ctypes.c_long, (HANDLE, ctypes.c_uint32, PHANDLE), __check_status)
182+
#EXTERN_C HRESULT canSchedulerClose (HANDLE hScheduler );
183+
_canlib.map_symbol("canSchedulerClose", ctypes.c_long, (HANDLE, ), __check_status)
184+
#EXTERN_C HRESULT canSchedulerGetCaps (HANDLE hScheduler, PCANCAPABILITIES pCaps );
185+
_canlib.map_symbol("canSchedulerGetCaps", ctypes.c_long, (HANDLE, structures.PCANCAPABILITIES), __check_status)
186+
#EXTERN_C HRESULT canSchedulerActivate ( HANDLE hScheduler, BOOL fEnable );
187+
_canlib.map_symbol("canSchedulerActivate", ctypes.c_long, (HANDLE, ctypes.c_int), __check_status)
188+
#EXTERN_C HRESULT canSchedulerAddMessage (HANDLE hScheduler, PCANCYCLICTXMSG pMessage, PUINT32 pdwIndex );
189+
_canlib.map_symbol("canSchedulerAddMessage", ctypes.c_long, (HANDLE, structures.PCANCYCLICTXMSG, ctypes.POINTER(ctypes.c_uint32)), __check_status)
190+
#EXTERN_C HRESULT canSchedulerRemMessage (HANDLE hScheduler, UINT32 dwIndex );
191+
_canlib.map_symbol("canSchedulerRemMessage", ctypes.c_long, (HANDLE, ctypes.c_uint32), __check_status)
192+
#EXTERN_C HRESULT canSchedulerStartMessage (HANDLE hScheduler, UINT32 dwIndex, UINT16 dwCount );
193+
_canlib.map_symbol("canSchedulerStartMessage", ctypes.c_long, (HANDLE, ctypes.c_uint32, ctypes.c_uint16), __check_status)
194+
#EXTERN_C HRESULT canSchedulerStopMessage (HANDLE hScheduler, UINT32 dwIndex );
195+
_canlib.map_symbol("canSchedulerStopMessage", ctypes.c_long, (HANDLE, ctypes.c_uint32), __check_status)
179196
_canlib.vciInitialize()
180197
except AttributeError:
181198
# In case _canlib == None meaning we're not on win32/no lib found
@@ -337,6 +354,11 @@ def __init__(self, channel, can_filters=None, **config):
337354
# Start the CAN controller. Messages will be forwarded to the channel
338355
_canlib.canControlStart(self._control_handle, constants.TRUE)
339356

357+
# For cyclic transmit list. Set when .send_periodic() is first called
358+
self._scheduler = None
359+
self._scheduler_resolution = None
360+
self.channel = channel
361+
340362
# Usually you get back 3 messages like "CAN initialized" ecc...
341363
# Filter them out with low timeout
342364
while True:
@@ -460,8 +482,67 @@ def send(self, msg, timeout=None):
460482
else:
461483
_canlib.canChannelPostMessage(self._channel_handle, message)
462484

485+
def send_periodic(self, msg, period, duration=None):
486+
"""Send a message using built-in cyclic transmit list functionality."""
487+
if self._scheduler is None:
488+
self._scheduler = HANDLE()
489+
_canlib.canSchedulerOpen(self._device_handle, self.channel,
490+
self._scheduler)
491+
caps = structures.CANCAPABILITIES()
492+
_canlib.canSchedulerGetCaps(self._scheduler, caps)
493+
self._scheduler_resolution = float(caps.dwClockFreq) / caps.dwCmsDivisor
494+
_canlib.canSchedulerActivate(self._scheduler, constants.TRUE)
495+
return CyclicSendTask(self._scheduler, msg, period, duration,
496+
self._scheduler_resolution)
497+
463498
def shutdown(self):
499+
if self._scheduler is not None:
500+
_canlib.canSchedulerClose(self._scheduler)
464501
_canlib.canChannelClose(self._channel_handle)
465502
_canlib.canControlStart(self._control_handle, constants.FALSE)
466503
_canlib.canControlClose(self._control_handle)
467504
_canlib.vciDeviceClose(self._device_handle)
505+
506+
507+
class CyclicSendTask(LimitedDurationCyclicSendTaskABC,
508+
RestartableCyclicTaskABC):
509+
"""A message in the cyclic transmit list."""
510+
511+
def __init__(self, scheduler, msg, period, duration, resolution):
512+
super(CyclicSendTask, self).__init__(msg, period, duration)
513+
self._scheduler = scheduler
514+
self._index = None
515+
self._count = int(duration / period) if duration else 0
516+
517+
self._msg = structures.CANCYCLICTXMSG()
518+
self._msg.wCycleTime = int(round(period * resolution))
519+
self._msg.dwMsgId = msg.arbitration_id
520+
self._msg.uMsgInfo.Bits.type = constants.CAN_MSGTYPE_DATA
521+
self._msg.uMsgInfo.Bits.ext = 1 if msg.id_type else 0
522+
self._msg.uMsgInfo.Bits.rtr = 1 if msg.is_remote_frame else 0
523+
self._msg.uMsgInfo.Bits.dlc = msg.dlc
524+
for i, b in enumerate(msg.data):
525+
self._msg.abData[i] = b
526+
self.start()
527+
528+
def start(self):
529+
"""Start transmitting message (add to list if needed)."""
530+
if self._index is None:
531+
self._index = ctypes.c_uint32()
532+
_canlib.canSchedulerAddMessage(self._scheduler,
533+
self._msg,
534+
self._index)
535+
_canlib.canSchedulerStartMessage(self._scheduler,
536+
self._index,
537+
self._count)
538+
539+
def pause(self):
540+
"""Pause transmitting message (keep it in the list)."""
541+
_canlib.canSchedulerStopMessage(self._scheduler, self._index)
542+
543+
def stop(self):
544+
"""Stop transmitting message (remove from list)."""
545+
# Remove it completely instead of just stopping it to avoid filling up
546+
# the list with permanently stopped messages
547+
_canlib.canSchedulerRemMessage(self._scheduler, self._index)
548+
self._index = None

can/interfaces/ixxat/structures.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,14 @@ class CANMSG(ctypes.Structure):
144144
("abData", ctypes.c_uint8 * 8)
145145
]
146146
PCANMSG = ctypes.POINTER(CANMSG)
147+
148+
class CANCYCLICTXMSG(ctypes.Structure):
149+
_fields_ = [
150+
("wCycleTime", ctypes.c_uint16),
151+
("bIncrMode", ctypes.c_uint8),
152+
("bByteIndex", ctypes.c_uint8),
153+
("dwMsgId", ctypes.c_uint32),
154+
("uMsgInfo", CANMSGINFO),
155+
("abData", ctypes.c_uint8 * 8)
156+
]
157+
PCANCYCLICTXMSG = ctypes.POINTER(CANCYCLICTXMSG)

doc/interfaces/ixxat.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,20 @@ Interface to `IXXAT <http://www.ixxat.com/>`__ Virtual CAN Interface V3 SDK. Wor
1111

1212
The Linux ECI SDK is currently unsupported, however on Linux some devices are supported with :doc:`socketcan`.
1313

14+
The :meth:`~can.interfaces.ixxat.IXXATBus.send_periodic` method is supported
15+
natively through the on-board cyclic transmit list.
16+
The only limitation is that when using the
17+
:meth:`~can.interfaces.ixxat.canlib.CyclicSendTask.modify_data` method, the message needs
18+
to be removed and re-added which will cause a glitch in the periodicity.
19+
1420

1521
Bus
1622
---
1723

1824
.. autoclass:: can.interfaces.ixxat.IXXATBus
1925

26+
.. autoclass:: can.interfaces.ixxat.canlib.CyclicSendTask
27+
2028

2129

2230
Configuration file

examples/cyclic.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,20 @@ def limited_periodic_send(bus):
3636
if not isinstance(task, can.LimitedDurationCyclicSendTaskABC):
3737
print("This interface doesn't seem to support a ")
3838
task.stop()
39+
return
3940

41+
time.sleep(1.5)
4042
print("stopped cyclic send")
4143

4244

43-
def test_periodic_send_with_modifying_data():
45+
def test_periodic_send_with_modifying_data(bus):
4446
print("Starting to send a message every 200ms. Initial data is ones")
4547
msg = can.Message(arbitration_id=0x0cf02200, data=[1, 1, 1, 1])
46-
task = can.send_periodic('vcan0', msg, 0.20)
48+
task = bus.send_periodic(msg, 0.20)
49+
if not isinstance(task, can.ModifiableCyclicTaskABC):
50+
print("This interface doesn't seem to support modification")
51+
task.stop()
52+
return
4753
time.sleep(2)
4854
print("Changing data of running task to begin with 99")
4955
msg.data[0] = 0x99
@@ -54,6 +60,7 @@ def test_periodic_send_with_modifying_data():
5460
print("stopped cyclic send")
5561
print("Changing data of stopped task to single ff byte")
5662
msg.data = bytearray([0xff])
63+
msg.dlc = 1
5764
task.modify_data(msg)
5865
time.sleep(1)
5966
print("starting again")
@@ -94,16 +101,14 @@ def test_periodic_send_with_modifying_data():
94101
reset_msg = can.Message(arbitration_id=0x00, data=[0, 0, 0, 0, 0, 0], extended_id=False)
95102

96103

97-
98-
for interface in {
99-
'socketcan_ctypes',
100-
'socketcan_native'
101-
}:
104+
for interface, channel in [
105+
('socketcan_ctypes', 'can0'),
106+
('socketcan_native', 'can0')
107+
#('ixxat', 0)
108+
]:
102109
print("Carrying out cyclic tests with {} interface".format(interface))
103-
can.rc['interface'] = interface
104110

105-
channel = 'vcan0'
106-
bus = can.interface.Bus(channel=channel)
111+
bus = can.interface.Bus(bustype=interface, channel=channel, bitrate=500000)
107112
bus.send(reset_msg)
108113

109114
simple_periodic_send(bus)
@@ -112,11 +117,13 @@ def test_periodic_send_with_modifying_data():
112117

113118
limited_periodic_send(bus)
114119

115-
test_periodic_send_with_modifying_data()
120+
test_periodic_send_with_modifying_data(bus)
116121

117122
#print("Carrying out multirate cyclic test for {} interface".format(interface))
118123
#can.rc['interface'] = interface
119124
#test_dual_rate_periodic_send()
120125

126+
bus.shutdown()
127+
121128

122129
time.sleep(2)

0 commit comments

Comments
 (0)