Skip to content

Commit 42f9003

Browse files
committed
Add tests
1 parent 53ab916 commit 42f9003

8 files changed

Lines changed: 624003 additions & 1 deletion

File tree

canalystii/device.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def __init__(
2929
)
3030
)
3131
if not devices:
32-
raise ValueError("No Canalyst-II device was found")
32+
raise ValueError("No Canalyst-II USB device found")
3333
if len(devices) <= device_index:
3434
raise ValueError(
3535
f"Can't open device_index {device_index}, only {len(devices)} devices found."

tests/CAN_Spammer.ino

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// Spam messages out CAN0 as fast as the Due will allow!
2+
//
3+
// Use together with the can_spammer_test.py script to verify peak Canalyst-II
4+
// receive performance.
5+
//
6+
// NOTE: Requires due_can library. Library v2.0.1 sometimes sends CAN frames out
7+
// of order (or drops them, possibly?), so will give incorrect results. The
8+
// master branch library version doesn't have this issue.
9+
//
10+
#include "variant.h"
11+
#include <due_can.h>
12+
13+
// Leave defined if you use native port, comment if using programming port
14+
//#define Serial SerialUSB
15+
16+
void setup()
17+
{
18+
Serial.begin(115200);
19+
Can0.begin(CAN_BPS_1000K);
20+
Serial.println("Starting...");
21+
}
22+
23+
void loop()
24+
{
25+
CAN_FRAME outgoing;
26+
outgoing.id = 0x400;
27+
outgoing.length = 8;
28+
outgoing.data.value = 0xDEADBEEF00000000ULL;
29+
30+
while(1) {
31+
// Sending will fail unless a device on the bus ACKs the frame
32+
if(Can0.sendFrame(outgoing)) {
33+
outgoing.data.value++;
34+
}
35+
36+
// Every second, print an estimate of the sending rate
37+
static uint32_t last_seconds;
38+
uint32_t seconds = millis() / 1000;
39+
if (seconds != last_seconds) {
40+
uint32_t counter = outgoing.data.value; // Log the lower word only, Arduino doesn't format 64-bit integers
41+
static uint32_t last_counter;
42+
Serial.print(seconds);
43+
Serial.print("s ");
44+
Serial.print((uint32_t)counter);
45+
Serial.print(" msgs ");
46+
Serial.print(((uint32_t)counter - last_counter) / (seconds - last_seconds));
47+
Serial.println("msg/s");
48+
last_counter = counter;
49+
last_seconds = seconds;
50+
}
51+
}
52+
}

tests/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Tests
2+
3+
Some basic automated tests for Canalyst-II Behaviour.
4+
5+
## loopback_test.py
6+
7+
Expects channel 0 & 1 to be joined together (low to low and high to high), with termination set correctly.
8+
9+
This is a pytest module, run as `pytest loopback_test.py`
10+
11+
## can_spammer_test.py
12+
13+
Expects an Arduino Due running the "CAN_Spammer.ino" sketch to be attached to either Channel 0, Chanel 1, or both.
14+
15+
Tests throughput receiving messages from the spammer. Prints throughput rate for messages received, and any unexpected messages that may indicate dropped frames.
16+
17+
This is an ordinary script, doesn't print any pass/fail result just a throughput rate and an error count.
18+
19+
Note that when the firmware clears the RX buffer it may still keep one message somehow (firmware bug?), so 1 error per channel is possible on any run.
20+

tests/can_spammer_test.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/usr/bin/env python
2+
import canalystii
3+
import struct
4+
import time
5+
6+
CHANNELS = (0, 1) # Which channels to test on?
7+
TEST_SECS = 10 # How many seconds to sample for?
8+
SLEEP_BETWEEN = 0.05 # How long to sleep between samples?
9+
10+
11+
def main():
12+
print("Connecting to Canalyst-II device...")
13+
dev = canalystii.CanalystDevice(bitrate=1000000)
14+
15+
print(f"Testing for {TEST_SECS} seconds...")
16+
17+
deadline = time.time() + TEST_SECS
18+
last = [None, None]
19+
messages = [0, 0]
20+
errors = [0, 0]
21+
22+
dev.clear_rx_buffer(0)
23+
dev.clear_rx_buffer(1)
24+
25+
while time.time() < deadline:
26+
for ch in CHANNELS:
27+
for msg in dev.receive(ch):
28+
if last[ch] is None:
29+
last[ch] = read_counter(msg)
30+
else:
31+
messages[ch] += 1
32+
(last[ch], result) = check_id(msg, last[ch])
33+
if not result:
34+
errors[ch] += 1
35+
time.sleep(0.05)
36+
37+
for ch in CHANNELS:
38+
print(
39+
f"Channel {ch}: Received {messages[ch]} messages ({messages[ch]/TEST_SECS}/sec), {errors[ch]} errors"
40+
)
41+
42+
43+
def check_id(msg, last_counter):
44+
msg_count = read_counter(msg)
45+
result = True
46+
if msg_count < last_counter:
47+
print(f"Backwards {msg_count:#x} < prev {last_counter:#x}")
48+
result = False
49+
elif msg_count != last_counter + 1:
50+
print(f"Unexpected {msg_count:#x} expected {last_counter + 1:#x}")
51+
result = False
52+
return (msg_count, result)
53+
54+
55+
def read_counter(msg):
56+
return struct.unpack("<Q", bytes(msg.data))[0]
57+
58+
59+
if __name__ == "__main__":
60+
main()

0 commit comments

Comments
 (0)