1#!/usr/bin/python3
2#
3# Copyright (c) 2016 Intel Corporation
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#    http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import random
18import select
19import socket
20import sys
21import time
22
23if not hasattr(socket, 'if_nametoindex'):
24    print("Please Python3, this is incompatible with Python 2")
25    sys.exit(1)
26
27if len(sys.argv) != 3:
28    print("Usage: %s interface echo-server-ipv6-address" % sys.argv[0])
29    sys.exit(1)
30
31def log(msg):
32    print("%s: %s" % (time.asctime(), msg))
33
34iface = sys.argv[1]
35addr = sys.argv[2]
36scope_id = socket.if_nametoindex(iface)
37
38log("Connecting to [%s]:4242 (through interface %s, scope_id %d)" % (
39    addr, iface, scope_id
40))
41
42sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_TCP)
43sock.connect((addr, 4242, 0, scope_id))
44
45# Use non-blocking I/O so we can use select() to check for timeouts
46sock.setblocking(False)
47
48log("Connection established. Will begin sending data")
49
50delta_avg = 0
51delta_avg_len = 0
52timeouts = 0
53tx_errors = 0
54rx_errors = 0
55for current_n in range(0, 2**32, 10):
56    # Wait up to 1 second between each packet transmitted
57    time.sleep(random.random())
58
59    log("Will try sending %d and read it back" % current_n)
60
61    while True:
62        _, wlist, _ = select.select([], [sock], [], 1)
63        if wlist:
64            break
65
66        timeouts += 1
67        log("Timeout while sending data to Zephyr, will try again")
68
69    time_before_send = time.time()
70    current_n_bytes = bytes(str(current_n), "ascii")
71    sent_bytes = sock.send(current_n_bytes)
72    if sent_bytes != len(current_n_bytes):
73        tx_errors +=1
74        log("Were not able to transmit %d (%d bytes), will try again" % (
75            current_n, len(current_n_bytes)
76        ))
77        continue
78
79    while True:
80        rlist, _, _ = select.select([sock], [], [], 1)
81        if rlist:
82            break
83
84        timeouts += 1
85        log("Timeout while reading data from Zephyr, will try again")
86
87    rcvd_bytes = sock.recv(len(current_n_bytes))
88    if len(rcvd_bytes) != len(current_n_bytes):
89        rx_errors += 1
90        log("Got back %d bytes instead of %d. Will send again" % (
91            len(rcvd_bytes), len(current_n_bytes)
92        ))
93        continue
94
95    if rcvd_bytes != current_n_bytes:
96        rx_errors += 1
97        log("Got back '%s' but sent '%s'. Will try again" % (
98            rcvd_bytes, current_n_bytes
99        ))
100        continue
101
102    delta = time.time() - time_before_send
103    log("Got %d back in %f seconds" % (current_n, delta))
104
105    delta_avg += delta
106    delta_avg_len += 1
107    if delta_avg_len > 10:
108        log("Roundtrip takes %f seconds on average" % (delta_avg / 10))
109
110        delta_avg = 0
111        delta_avg_len = 0
112
113        if timeouts > 0 or rx_errors > 0 or tx_errors > 0:
114            log("So far: %d timeouts, %d rx errors, %d tx errors" % (
115                timeouts, rx_errors, tx_errors
116            ))
117        else:
118            log("So far, so good: no timeouts or errors")
119