Skip to content

Commit ea142fd

Browse files
authored
Fix SocketCAN periodic Tasks on Python 2 (#850)
socket.error is a deprecated alias of OSError used on Python versions lower than 3.3. Also cherry pick over the relevant cyclic SocketCAN tests and enable Travis CI for SocketCAN tests on Python 2. Fixes #845
1 parent cdf1e5a commit ea142fd

File tree

3 files changed

+185
-1
lines changed

3 files changed

+185
-1
lines changed

.travis.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ jobs:
5252

5353
# Unit Testing Stage
5454

55+
# testing socketcan on Trusty & Python 2.7, since it is not available on Xenial
56+
- stage: test
57+
name: Socketcan
58+
os: linux
59+
dist: trusty
60+
python: "2.7"
61+
sudo: required
62+
env: TEST_SOCKETCAN=TRUE
5563
# testing socketcan on Trusty & Python 3.6, since it is not available on Xenial
5664
- stage: test
5765
name: Socketcan

can/interfaces/socketcan/socketcan.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ def _tx_setup(self, message):
363363
)
364364
try:
365365
self.bcm_socket.send(check_header)
366-
except OSError as e:
366+
except (socket.error, OSError) as e:
367367
if e.errno != errno.EINVAL:
368368
raise e
369369
else:

test/test_cyclic_socketcan.py

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
"""
2+
This module tests multiple message cyclic send tasks.
3+
"""
4+
import unittest
5+
6+
import time
7+
import can
8+
9+
from .config import TEST_INTERFACE_SOCKETCAN
10+
11+
12+
@unittest.skipUnless(TEST_INTERFACE_SOCKETCAN, "skip testing of socketcan")
13+
class CyclicSocketCan(unittest.TestCase):
14+
BITRATE = 500000
15+
TIMEOUT = 0.1
16+
17+
INTERFACE_1 = "socketcan"
18+
CHANNEL_1 = "vcan0"
19+
INTERFACE_2 = "socketcan"
20+
CHANNEL_2 = "vcan0"
21+
22+
PERIOD = 1.0
23+
24+
DELTA = 0.01
25+
26+
def _find_start_index(self, tx_messages, message):
27+
"""
28+
:param tx_messages:
29+
The list of messages that were passed to the periodic backend
30+
:param message:
31+
The message whose data we wish to match and align to
32+
33+
:returns: start index in the tx_messages
34+
"""
35+
start_index = -1
36+
for index, tx_message in enumerate(tx_messages):
37+
if tx_message.data == message.data:
38+
start_index = index
39+
break
40+
return start_index
41+
42+
def setUp(self):
43+
self._send_bus = can.Bus(
44+
interface=self.INTERFACE_1, channel=self.CHANNEL_1, bitrate=self.BITRATE
45+
)
46+
self._recv_bus = can.Bus(
47+
interface=self.INTERFACE_2, channel=self.CHANNEL_2, bitrate=self.BITRATE
48+
)
49+
50+
def tearDown(self):
51+
self._send_bus.shutdown()
52+
self._recv_bus.shutdown()
53+
54+
def test_cyclic_initializer_message(self):
55+
message = can.Message(
56+
arbitration_id=0x401,
57+
data=[0x11, 0x11, 0x11, 0x11, 0x11, 0x11],
58+
is_extended_id=False,
59+
)
60+
61+
task = self._send_bus.send_periodic(message, self.PERIOD)
62+
self.assertIsInstance(task, can.broadcastmanager.CyclicSendTaskABC)
63+
64+
# Take advantage of kernel's queueing mechanisms
65+
time.sleep(4 * self.PERIOD)
66+
task.stop()
67+
68+
for _ in range(4):
69+
tx_message = message
70+
rx_message = self._recv_bus.recv(self.TIMEOUT)
71+
72+
self.assertIsNotNone(rx_message)
73+
self.assertEqual(tx_message.arbitration_id, rx_message.arbitration_id)
74+
self.assertEqual(tx_message.dlc, rx_message.dlc)
75+
self.assertEqual(tx_message.data, rx_message.data)
76+
self.assertEqual(tx_message.is_extended_id, rx_message.is_extended_id)
77+
self.assertEqual(tx_message.is_remote_frame, rx_message.is_remote_frame)
78+
self.assertEqual(tx_message.is_error_frame, rx_message.is_error_frame)
79+
self.assertEqual(tx_message.is_fd, rx_message.is_fd)
80+
81+
def test_create_same_id_raises_exception(self):
82+
messages_a = can.Message(
83+
arbitration_id=0x401,
84+
data=[0x11, 0x11, 0x11, 0x11, 0x11, 0x11],
85+
is_extended_id=False,
86+
)
87+
88+
messages_b = can.Message(
89+
arbitration_id=0x401,
90+
data=[0x22, 0x22, 0x22, 0x22, 0x22, 0x22],
91+
is_extended_id=False,
92+
)
93+
94+
task_a = self._send_bus.send_periodic(messages_a, 1)
95+
self.assertIsInstance(task_a, can.broadcastmanager.CyclicSendTaskABC)
96+
97+
# The second one raises a ValueError when we attempt to create a new
98+
# Task, since it has the same arbitration ID.
99+
with self.assertRaises(ValueError):
100+
task_b = self._send_bus.send_periodic(messages_b, 1)
101+
102+
def test_modify_data_message(self):
103+
message_odd = can.Message(
104+
arbitration_id=0x401,
105+
data=[0x11, 0x11, 0x11, 0x11, 0x11, 0x11],
106+
is_extended_id=False,
107+
)
108+
message_even = can.Message(
109+
arbitration_id=0x401,
110+
data=[0x22, 0x22, 0x22, 0x22, 0x22, 0x22],
111+
is_extended_id=False,
112+
)
113+
task = self._send_bus.send_periodic(message_odd, self.PERIOD)
114+
self.assertIsInstance(task, can.broadcastmanager.ModifiableCyclicTaskABC)
115+
116+
results_odd = []
117+
results_even = []
118+
for _ in range(1 * 4):
119+
result = self._recv_bus.recv(self.PERIOD * 2)
120+
if result:
121+
results_odd.append(result)
122+
123+
task.modify_data(message_even)
124+
for _ in range(1 * 4):
125+
result = self._recv_bus.recv(self.PERIOD * 2)
126+
if result:
127+
results_even.append(result)
128+
129+
task.stop()
130+
131+
# Now go through the partitioned results and assert that they're equal
132+
for rx_index, rx_message in enumerate(results_even):
133+
tx_message = message_even
134+
135+
self.assertEqual(tx_message.arbitration_id, rx_message.arbitration_id)
136+
self.assertEqual(tx_message.dlc, rx_message.dlc)
137+
self.assertEqual(tx_message.data, rx_message.data)
138+
self.assertEqual(tx_message.is_extended_id, rx_message.is_extended_id)
139+
self.assertEqual(tx_message.is_remote_frame, rx_message.is_remote_frame)
140+
self.assertEqual(tx_message.is_error_frame, rx_message.is_error_frame)
141+
self.assertEqual(tx_message.is_fd, rx_message.is_fd)
142+
143+
if rx_index != 0:
144+
prev_rx_message = results_even[rx_index - 1]
145+
# Assert timestamps are within the expected period
146+
self.assertTrue(
147+
abs(
148+
(rx_message.timestamp - prev_rx_message.timestamp) - self.PERIOD
149+
)
150+
<= self.DELTA
151+
)
152+
153+
for rx_index, rx_message in enumerate(results_odd):
154+
tx_message = message_odd
155+
156+
self.assertEqual(tx_message.arbitration_id, rx_message.arbitration_id)
157+
self.assertEqual(tx_message.dlc, rx_message.dlc)
158+
self.assertEqual(tx_message.data, rx_message.data)
159+
self.assertEqual(tx_message.is_extended_id, rx_message.is_extended_id)
160+
self.assertEqual(tx_message.is_remote_frame, rx_message.is_remote_frame)
161+
self.assertEqual(tx_message.is_error_frame, rx_message.is_error_frame)
162+
self.assertEqual(tx_message.is_fd, rx_message.is_fd)
163+
164+
if rx_index != 0:
165+
prev_rx_message = results_odd[rx_index - 1]
166+
# Assert timestamps are within the expected period
167+
self.assertTrue(
168+
abs(
169+
(rx_message.timestamp - prev_rx_message.timestamp) - self.PERIOD
170+
)
171+
<= self.DELTA
172+
)
173+
174+
175+
if __name__ == "__main__":
176+
unittest.main()

0 commit comments

Comments
 (0)