Skip to content

Commit 07f7871

Browse files
chiranjeevi2776nordicjm
authored andcommitted
scripts: Add traffic generator server
This is a python script that implements traffic generator server to test nRF70 series Wi-Fi chipsets. Formatted usign black with max-line-length=100 and ran flake8 linter. Signed-off-by: Chiranjeevi Srikakulapu <[email protected]>
1 parent d5d2644 commit 07f7871

File tree

1 file changed

+239
-0
lines changed

1 file changed

+239
-0
lines changed

scripts/traffic_gen_server.py

+239
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
#!/usr/bin/env python3
2+
3+
# Copyright (c) 2023 Nordic Semiconductor ASA
4+
#
5+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
6+
7+
import socket
8+
import struct
9+
import time
10+
import argparse
11+
import logging
12+
13+
# Define constants for client roles
14+
UPLINK = 1
15+
DOWNLINK = 2
16+
17+
# Define constants for traffic types
18+
TCP = 1
19+
20+
# Define constants for traffic modes
21+
CONTINUOUS = 1
22+
DELAYED = 2
23+
24+
EXTRA_TIMEOUT_S = 5
25+
26+
logging.basicConfig(level=logging.INFO)
27+
logger = logging.getLogger(__name__)
28+
29+
# Define the message structure using a Python dictionary
30+
config_data = {
31+
"role": 0,
32+
"type": 0,
33+
"mode": 0,
34+
"duration": 0,
35+
"payload_len": 0,
36+
}
37+
38+
39+
def tcp_server(ctrl_socket, config_data):
40+
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
41+
server_address = ("0.0.0.0", 7777) # Listen on all available network interfaces
42+
server_socket.bind(server_address)
43+
server_socket.listen(1)
44+
logger.debug(
45+
f"TCP server is listening on {server_address[0]}:{server_address[1]}..."
46+
)
47+
48+
client_socket, client_address = server_socket.accept()
49+
logger.debug(f"Connected to client: {client_address}")
50+
51+
packet_count = 0
52+
total_bytes_received = 0
53+
start_time = time.time()
54+
remaining_timeout = config_data["duration"] + EXTRA_TIMEOUT_S
55+
56+
try:
57+
while True:
58+
client_socket.settimeout(
59+
remaining_timeout
60+
) # Set the current remaining timeout
61+
try:
62+
data = client_socket.recv(1024)
63+
if not data:
64+
logger.debug("Received empty message. Closing the connection.")
65+
break
66+
packet_count += 1
67+
total_bytes_received += len(data)
68+
except socket.timeout:
69+
logger.warning(
70+
f"No data received for {remaining_timeout:.2f} seconds. Closing connection."
71+
)
72+
break
73+
except BlockingIOError:
74+
logger.debug("BlockingIOError caught. Setting socket to non-blocking.")
75+
client_socket.setblocking(False) # Set the socket to non-blocking
76+
break
77+
finally:
78+
elapsed_time = time.time() - start_time
79+
remaining_timeout = max(
80+
0, (config_data["duration"] + EXTRA_TIMEOUT_S) - elapsed_time
81+
)
82+
83+
finally:
84+
# Calculate statistics
85+
end_time = time.time()
86+
total_seconds = int(end_time - start_time)
87+
throughput_kbps = int(
88+
(total_bytes_received * 8) / (total_seconds * 1024)
89+
) # Kbps calculation
90+
average_jitter = 0 # Calculate jitter as needed
91+
92+
# Prepare the report data
93+
report_data = {
94+
"bytes_received": total_bytes_received,
95+
"packets_received": packet_count,
96+
"elapsed_time": total_seconds,
97+
"throughput": throughput_kbps,
98+
"average_jitter": average_jitter,
99+
}
100+
101+
# Print the report
102+
print_report(report_data)
103+
time.sleep(5)
104+
# Send the report back to the client using the 6666 server socket
105+
send_server_report(ctrl_socket, report_data)
106+
client_socket.close()
107+
server_socket.close()
108+
logger.info("TCP server is closed.")
109+
110+
111+
def send_server_report(client_socket, report_data):
112+
report_format = "!iiiii"
113+
report_packed = struct.pack(
114+
report_format,
115+
report_data["bytes_received"],
116+
report_data["packets_received"],
117+
report_data["elapsed_time"],
118+
report_data["throughput"],
119+
report_data["average_jitter"],
120+
)
121+
client_socket.sendall(report_packed)
122+
logger.debug("Report sent to client.")
123+
124+
125+
def print_report(report_data):
126+
logger.info("TCP Server Report:")
127+
logger.info(f" Bytes Received: {report_data['bytes_received']} bytes")
128+
logger.info(f" Packets Received: {report_data['packets_received']}")
129+
logger.info(f" Elapsed Time: {report_data['elapsed_time']} Seconds")
130+
logger.info(f" Throughput: {report_data['throughput']} Kbps")
131+
logger.info(
132+
f" Average Jitter: {report_data['average_jitter']}"
133+
) # Calculate and provide actual value
134+
135+
136+
def tcp_client(config_data, client_address):
137+
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
138+
server_address = (
139+
client_address[0],
140+
7777, # TODO: Get this from the client
141+
)
142+
client_socket.connect(server_address)
143+
144+
try:
145+
start_time = time.time()
146+
while time.time() - start_time < config_data["duration"]:
147+
message = (
148+
b"x" * config_data["payload_len"]
149+
) # Creating a message with specified frame length
150+
client_socket.sendall(message)
151+
time.sleep(1 / 1000) # Sending messages every 1 second
152+
logger.debug("Data transmission finished.")
153+
# Send an empty message to signal termination
154+
client_socket.sendall(b"")
155+
finally:
156+
client_socket.close()
157+
158+
159+
def process_client_config_data(ctrl_socket, client_address):
160+
global config_data
161+
162+
logger.info("Waiting for client config info\n")
163+
buffer = ctrl_socket.recv(1024)
164+
cf = buffer[:24] # Assuming sizeof(struct config_data) is 24 bytes
165+
config_data = {
166+
"role": int.from_bytes(cf[0:4], byteorder="big"),
167+
"type": int.from_bytes(cf[4:8], byteorder="big"),
168+
"mode": int.from_bytes(cf[8:12], byteorder="big"),
169+
"duration": int.from_bytes(cf[12:16], byteorder="big"),
170+
"payload_len": int.from_bytes(cf[16:20], byteorder="big"),
171+
}
172+
173+
logger.info("Client config data received")
174+
logger.info("\tClient Role: %d", config_data["role"])
175+
logger.info("\tTraffic type: %d", config_data["type"])
176+
logger.info("\tTraffic Mode: %d", config_data["mode"])
177+
logger.info("\tDuration: %d", config_data["duration"])
178+
logger.info("\tFrame len: %d", config_data["payload_len"])
179+
180+
# Implement logic for starting servers/clients and processing traffic
181+
if config_data["role"] == UPLINK:
182+
logger.debug("UPLINK:")
183+
if config_data["type"] == TCP:
184+
logger.debug("TCP SERVER STARTED")
185+
tcp_server(ctrl_socket, config_data)
186+
else:
187+
logger.info("Invalid Traffic type")
188+
elif config_data["role"] == DOWNLINK:
189+
logger.debug("DOWNLINK:")
190+
time.sleep(10)
191+
if config_data["type"] == TCP:
192+
logger.debug("TCP CLIENT STARTED")
193+
tcp_client(config_data, client_address)
194+
logger.debug("TCP CLIENT FINISHED")
195+
else:
196+
logger.error("Invalid Traffic Type")
197+
198+
def parse_args():
199+
parser = argparse.ArgumentParser(
200+
allow_abbrev=False,
201+
description="Traffic Generator server for testing nRF70 Wi-Fi chipsets"
202+
)
203+
parser.add_argument(
204+
"--control-port",
205+
type=int,
206+
default=6666,
207+
help="Port for control connection with client",
208+
)
209+
parser.add_argument("-d", "--debug", action="store_true", help="Enable debug logs")
210+
211+
return parser.parse_args()
212+
213+
214+
def main():
215+
args = parse_args() # Parse command-line arguments
216+
if args.debug:
217+
logger.setLevel(logging.DEBUG)
218+
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
219+
server_address = (
220+
"0.0.0.0",
221+
args.control_port,
222+
) # Listen on all available network interfaces
223+
server_socket.bind(server_address)
224+
server_socket.listen(1)
225+
logger.info(f"Server is listening on {server_address[0]}:{server_address[1]}...")
226+
227+
client_socket, client_address = server_socket.accept()
228+
logger.info(f"Connected to client: {client_address}")
229+
230+
try:
231+
process_client_config_data(client_socket, client_address)
232+
finally:
233+
time.sleep(2)
234+
client_socket.close()
235+
server_socket.close()
236+
logger.debug("Server is closed.")
237+
238+
if __name__ == "__main__":
239+
main()

0 commit comments

Comments
 (0)