Skip to content

Commit 37e0181

Browse files
committed
rpc_wrappers: zperf_upload: added
Add a wrapper for the `zperf_upload` RPC. Signed-off-by: Jordan Yates <jordan@embeint.com>
1 parent 9d9c20d commit 37e0181

File tree

3 files changed

+223
-0
lines changed

3 files changed

+223
-0
lines changed

src/infuse_iot/generated/rpc_definitions.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,18 @@ class rpc_struct_infuse_state(VLACompatLittleEndianStruct):
161161
_pack_ = 1
162162

163163

164+
class rpc_struct_sockaddr(VLACompatLittleEndianStruct):
165+
"""`struct sockaddr_in` or `struct sockaddr_in6` compatible address"""
166+
167+
_fields_ = [
168+
("sin_family", ctypes.c_uint8),
169+
("sin_port", ctypes.c_uint16),
170+
("sin_addr", 16 * ctypes.c_uint8),
171+
("scope_id", ctypes.c_uint8),
172+
]
173+
_pack_ = 1
174+
175+
164176
class rpc_enum_bt_le_addr_type(enum.IntEnum):
165177
"""Bluetooth LE address type"""
166178

@@ -194,6 +206,16 @@ class rpc_enum_data_logger(enum.IntEnum):
194206
FLASH_REMOVABLE = 2
195207

196208

209+
class rpc_enum_zperf_data_source(enum.IntEnum):
210+
"""Source for zperf data upload"""
211+
212+
CONSTANT = 0
213+
RANDOM = 1
214+
FLASH_ONBOARD = 2
215+
FLASH_REMOVABLE = 3
216+
ENCRYPT = 128
217+
218+
197219
class reboot:
198220
"""Reboot the device after a delay"""
199221

@@ -634,6 +656,40 @@ class response(VLACompatLittleEndianStruct):
634656
_pack_ = 1
635657

636658

659+
class zperf_upload:
660+
"""Network upload bandwidth testing using zperf/iperf"""
661+
662+
HELP = "Network upload bandwidth testing using zperf/iperf"
663+
DESCRIPTION = "Network upload bandwidth testing using zperf/iperf"
664+
COMMAND_ID = 31
665+
666+
class request(VLACompatLittleEndianStruct):
667+
_fields_ = [
668+
("peer_address", rpc_struct_sockaddr),
669+
("sock_type", ctypes.c_uint8),
670+
("data_source", ctypes.c_uint8),
671+
("duration_ms", ctypes.c_uint32),
672+
("rate_kbps", ctypes.c_uint32),
673+
("packet_size", ctypes.c_uint16),
674+
]
675+
_pack_ = 1
676+
677+
class response(VLACompatLittleEndianStruct):
678+
_fields_ = [
679+
("nb_packets_sent", ctypes.c_uint32),
680+
("nb_packets_rcvd", ctypes.c_uint32),
681+
("nb_packets_lost", ctypes.c_uint32),
682+
("nb_packets_outorder", ctypes.c_uint32),
683+
("total_len", ctypes.c_uint64),
684+
("time_in_us", ctypes.c_uint64),
685+
("jitter_in_us", ctypes.c_uint32),
686+
("client_time_in_us", ctypes.c_uint64),
687+
("packet_size", ctypes.c_uint32),
688+
("nb_packets_errors", ctypes.c_uint32),
689+
]
690+
_pack_ = 1
691+
692+
637693
class file_write_basic:
638694
"""Write a file to the device"""
639695

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
#!/usr/bin/env python3
2+
3+
import ctypes
4+
import ipaddress
5+
import socket
6+
7+
import tabulate
8+
9+
import infuse_iot.generated.rpc_definitions as defs
10+
from infuse_iot.commands import InfuseRpcCommand
11+
from infuse_iot.zephyr.errno import errno
12+
from infuse_iot.zephyr.net import AddressFamily, SockType
13+
14+
15+
class zperf_upload(InfuseRpcCommand, defs.zperf_upload):
16+
@classmethod
17+
def add_parser(cls, parser):
18+
parser.add_argument("--address", "-a", type=str, required=True, help="Peer IP address")
19+
parser.add_argument("--port", "-p", type=int, default=5001, help="Peer port")
20+
socket_group = parser.add_mutually_exclusive_group(required=True)
21+
socket_group.add_argument(
22+
"--tcp",
23+
dest="sock_type",
24+
action="store_const",
25+
const=SockType.SOCK_STREAM,
26+
help="TCP protocol",
27+
)
28+
socket_group.add_argument(
29+
"--udp",
30+
dest="sock_type",
31+
action="store_const",
32+
const=SockType.SOCK_DGRAM,
33+
help="UDP protocol",
34+
)
35+
source_group = parser.add_mutually_exclusive_group()
36+
source_group.add_argument(
37+
"--constant",
38+
dest="data_source",
39+
action="store_const",
40+
const=defs.rpc_enum_zperf_data_source.CONSTANT,
41+
default=defs.rpc_enum_zperf_data_source.CONSTANT,
42+
help="Constant data payload ('z')",
43+
)
44+
source_group.add_argument(
45+
"--random",
46+
dest="data_source",
47+
action="store_const",
48+
const=defs.rpc_enum_zperf_data_source.RANDOM,
49+
help="Random data payload",
50+
)
51+
source_group.add_argument(
52+
"--onboard",
53+
dest="data_source",
54+
action="store_const",
55+
const=defs.rpc_enum_zperf_data_source.FLASH_ONBOARD,
56+
help="Read from onboard flash logger",
57+
)
58+
source_group.add_argument(
59+
"--removable",
60+
dest="data_source",
61+
action="store_const",
62+
const=defs.rpc_enum_zperf_data_source.FLASH_REMOVABLE,
63+
help="Read from removable flash logger",
64+
)
65+
parser.add_argument("--encrypt", action="store_true", help="Encrypt payloads before transmission")
66+
parser.add_argument("--duration", type=int, default=5000, help="Duration to run test over in milliseconds")
67+
parser.add_argument("--rate-kbps", type=int, default=0, help="Desired upload rate in kbps")
68+
parser.add_argument("--payload-size", type=int, default=512, help="Payload size")
69+
70+
def __init__(self, args):
71+
self.peer_addr = ipaddress.ip_address(args.address)
72+
self.peer_port = args.port
73+
self.sock_type = args.sock_type
74+
self.data_source = args.data_source
75+
if args.encrypt:
76+
self.data_source |= defs.rpc_enum_zperf_data_source.ENCRYPT
77+
self.duration = args.duration
78+
self.rate = args.rate_kbps
79+
self.packet_size = args.payload_size
80+
if self.sock_type == SockType.SOCK_DGRAM:
81+
# Add the UDP client header size to the requested payload size
82+
self.packet_size += 40
83+
84+
def request_struct(self):
85+
peer_family = (
86+
AddressFamily.AF_INET if isinstance(self.peer_addr, ipaddress.IPv4Address) else AddressFamily.AF_INET6
87+
)
88+
addr_bytes = (16 * ctypes.c_uint8)(*self.peer_addr.packed)
89+
peer = defs.rpc_struct_sockaddr(
90+
sin_family=peer_family,
91+
sin_port=socket.htons(self.peer_port),
92+
sin_addr=addr_bytes,
93+
scope_id=0,
94+
)
95+
96+
return self.request(
97+
peer_address=peer,
98+
sock_type=self.sock_type,
99+
data_source=self.data_source,
100+
duration_ms=self.duration,
101+
rate_kbps=self.rate,
102+
packet_size=self.packet_size,
103+
)
104+
105+
def request_json(self):
106+
return {}
107+
108+
def handle_response(self, return_code, response):
109+
if return_code != 0:
110+
print(f"Failed to run zperf ({errno.strerror(-return_code)})")
111+
return
112+
113+
throughput_bps = 8 * response.total_len / (response.client_time_in_us / 1000)
114+
print(f"Average Throughput: {throughput_bps / 1000:.3f} kbps")
115+
if self.sock_type == SockType.SOCK_DGRAM:
116+
recv = 100 * response.nb_packets_rcvd / response.nb_packets_sent
117+
loss = 100 * response.nb_packets_lost / response.nb_packets_sent
118+
print(f" Packet Recv: {recv:6.2f}%")
119+
print(f" Packet Loss: {loss:6.2f}%")
120+
results = []
121+
for field_name, _ in response._fields_:
122+
results.append([field_name, getattr(response, field_name)])
123+
print(tabulate.tabulate(results))

src/infuse_iot/zephyr/net.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#!/usr/bin/env python3
2+
3+
import enum
4+
5+
6+
class IPProtocol(enum.IntEnum):
7+
IPPROTO_IP = 0
8+
IPPROTO_ICMP = 1
9+
IPPROTO_IGMP = 2
10+
IPPROTO_ETH_P_ALL = 3
11+
IPPROTO_IPIP = 4
12+
IPPROTO_TCP = 6
13+
IPPROTO_UDP = 17
14+
IPPROTO_IPV6 = 41
15+
IPPROTO_ICMPV6 = 58
16+
IPPROTO_RAW = 255
17+
18+
19+
class ProtocolFamily(enum.IntEnum):
20+
PF_UNSPEC = 0
21+
PF_INET = 1
22+
PF_INET6 = 2
23+
PF_PACKET = 3
24+
PF_CAN = 4
25+
PF_NET_MGMT = 5
26+
PF_LOCAL = 6
27+
PF_UNIX = PF_LOCAL
28+
29+
30+
class AddressFamily(enum.IntEnum):
31+
AF_UNSPEC = ProtocolFamily.PF_UNSPEC
32+
AF_INET = ProtocolFamily.PF_INET
33+
AF_INET6 = ProtocolFamily.PF_INET6
34+
AF_PACKET = ProtocolFamily.PF_PACKET
35+
AF_CAN = ProtocolFamily.PF_CAN
36+
AF_NET_MGMT = ProtocolFamily.PF_NET_MGMT
37+
AF_LOCAL = ProtocolFamily.PF_LOCAL
38+
AF_UNIX = ProtocolFamily.PF_UNIX
39+
40+
41+
class SockType(enum.IntEnum):
42+
SOCK_STREAM = 1
43+
SOCK_DGRAM = 2
44+
SOCK_RAW = 3

0 commit comments

Comments
 (0)