mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
90e57cdf8f
1. add test cases and related scripts 2. add CI config files read README.md for detail
526 lines
21 KiB
Python
Executable File
526 lines
21 KiB
Python
Executable File
from __future__ import division
|
|
import time
|
|
import threading
|
|
import re
|
|
import random
|
|
import os
|
|
import binascii
|
|
|
|
from TCAction import PerformanceTCBase
|
|
from NativeLog import NativeLog
|
|
from NativeLog import HTMLGenerator
|
|
from comm import MeshPort
|
|
from Utility import Encoding
|
|
|
|
# check frequency in second
|
|
CHECK_FREQ = 0.05
|
|
# check timeout in seconds
|
|
CHECK_TIMEOUT = 30
|
|
# multicast group len
|
|
MULTICAST_GROUP_LEN = 2
|
|
|
|
|
|
LOG_PATH = os.path.join("..", "log")
|
|
|
|
def _convert_to_mesh_mac_format(value_in):
|
|
value_out = ""
|
|
match_list = re.findall("([0-9a-fA-F]+)", value_in)
|
|
try:
|
|
for i in range(6):
|
|
value_out += "%02X" % int(match_list[i], base=16)
|
|
pass
|
|
except StandardError, e:
|
|
NativeLog.add_exception_log(e)
|
|
raise e
|
|
return value_out
|
|
|
|
class SendRecvTime(threading.Thread):
|
|
def __init__(self):
|
|
threading.Thread.__init__(self)
|
|
self.setDaemon(True)
|
|
self.send_time = dict()
|
|
self.recv_time = dict()
|
|
self.send_time_lock = threading.Lock()
|
|
self.recv_time_lock = threading.Lock()
|
|
|
|
def add_send_time(self, key, timestamp):
|
|
with self.send_time_lock:
|
|
self.send_time[key] = timestamp
|
|
|
|
def add_recv_time(self, key, timestamp):
|
|
with self.recv_time_lock:
|
|
if key in self.recv_time.keys():
|
|
self.recv_time[key].append(timestamp)
|
|
else:
|
|
self.recv_time[key] = [timestamp]
|
|
|
|
def calculate(self):
|
|
# add compute delay time code here
|
|
print 'send dict len:', len(self.send_time)
|
|
print 'recv dict len:', len(self.recv_time)
|
|
recv_time_keys = self.recv_time.keys()
|
|
Max_delay_time = 0.0
|
|
Total_delay_time = 0.0
|
|
# for i in range(len(recv_time_keys)):
|
|
# key = recv_time_keys[i]
|
|
for key in recv_time_keys:
|
|
Total_delay_time_t = 0.0
|
|
if isinstance(self.recv_time[key], list):
|
|
for time1 in self.recv_time[key]:
|
|
if time1 - self.send_time[key] >= Max_delay_time:
|
|
Max_delay_time = time1 - self.send_time[key]
|
|
Total_delay_time_t += (time1 - self.send_time[key])
|
|
else:
|
|
pass
|
|
else:
|
|
if self.recv_time[key] - self.send_time[key] > Max_delay_time:
|
|
Max_delay_time = self.recv_time[key] - self.send_time[key]
|
|
Total_delay_time_t += (self.recv_time[key] - self.send_time[key])
|
|
Total_delay_time_t += (Total_delay_time_t / len(self.recv_time[key]))
|
|
Total_delay_time += Total_delay_time_t
|
|
Avg_delay_time = Total_delay_time / len(recv_time_keys)
|
|
loss_rate = (len(self.send_time.keys()) - len(self.recv_time.keys())) / len(self.send_time.keys())
|
|
return [Max_delay_time, Avg_delay_time, loss_rate]
|
|
pass
|
|
|
|
class EntitySendThread(threading.Thread):
|
|
def __init__(self, port, behavior, unicast_addr, send_delay, typ, device_mac_list, server_addr, send_recv_time):
|
|
threading.Thread.__init__(self)
|
|
self.setDaemon(True)
|
|
self.recv_data_cache = ""
|
|
self.packets_sent = 0
|
|
self.port = port
|
|
self.behavior = behavior
|
|
self.typ = typ
|
|
self.unicast_addr = unicast_addr
|
|
self.node_num = len(device_mac_list)
|
|
self.device_mac_list = list(device_mac_list)
|
|
self.server_addr = server_addr
|
|
if typ != "SERVER":
|
|
self.device_mac_list.remove(port.device_mac)
|
|
self.send_delay = send_delay
|
|
self.cache_lock = threading.Lock()
|
|
self.exit_event = threading.Event()
|
|
self.send_recv_time = send_recv_time
|
|
pass
|
|
|
|
def data_recv_callback(self, data):
|
|
with self.cache_lock:
|
|
self.recv_data_cache += data
|
|
if self.typ == "SSC":
|
|
while True:
|
|
if self.recv_data_cache is not None:
|
|
match = re.compile(".+\+MSEND1:\d+:OK", re.DOTALL)
|
|
res = match.search(self.recv_data_cache)
|
|
index = re.search("\+MSEND1:(\d+):OK", self.recv_data_cache)
|
|
if index is not None:
|
|
time1 = time.time()
|
|
index1 = int(index.group(1))
|
|
self.send_recv_time.add_send_time(index1, time1)
|
|
#print 'send index:', index1
|
|
process_index = res.group().split("MSEND1")
|
|
if len(process_index) > 1:
|
|
process_index_t = len(process_index[0]) + len("MSEND1")
|
|
self.recv_data_cache = self.recv_data_cache[process_index_t:]
|
|
else:
|
|
self.recv_data_cache = self.recv_data_cache[len(res.group()):]
|
|
else:
|
|
break
|
|
else:
|
|
break
|
|
pass
|
|
|
|
|
|
def __server_send_packet(self, dst_addr, option_list=None, group_addr=None):
|
|
ver = 0x0
|
|
flags = 0x0
|
|
proto = 0x0
|
|
index = random.randint(10000, 999999999)
|
|
if group_addr is not None:
|
|
len_t = hex(len(group_addr) * 6).split("0x")
|
|
if len(group_addr) <= 2:
|
|
option_list = "070" + len_t[1]
|
|
else:
|
|
option_list = "07" + len_t[1]
|
|
group = ""
|
|
for addr in group_addr:
|
|
group += _convert_to_mesh_mac_format(addr)
|
|
option_list += group
|
|
else:
|
|
option_list = None
|
|
if self.behavior == "broadcast":
|
|
dst_addr = "00:00:00:00:00:00"
|
|
elif self.behavior == "unicast":
|
|
if self.unicast_addr == "random":
|
|
dst_addr = random.choice(self.device_mac_list)
|
|
else:
|
|
dst_addr = self.unicast_addr
|
|
elif self.behavior == "p2p":
|
|
proto = 0x2
|
|
if self.unicast_addr == "random":
|
|
dst_addr = random.choice(self.device_mac_list)
|
|
else:
|
|
dst_addr = self.unicast_addr
|
|
packet = MeshPort.Packet(ver=ver, flags=flags, proto=proto,
|
|
dst_addr=dst_addr, src_addr=self.server_addr, option_list=option_list, data="A" * 100, index=index)
|
|
send_data = packet.dumps
|
|
try:
|
|
self.port.socket.send(send_data)
|
|
time2 = time.time()
|
|
self.send_recv_time.add_send_time(index, time2)
|
|
except StandardError, e:
|
|
NativeLog.add_exception_log(e)
|
|
return False
|
|
|
|
def __server_do_send(self):
|
|
if self.behavior == "broadcast":
|
|
if self.__server_send_packet(dst_addr="00:00:00:00:00:00", group_addr=None) is True:
|
|
self.packets_sent += self.node_num
|
|
elif self.behavior == "multicast":
|
|
random.shuffle(self.device_mac_list)
|
|
group_addr_list = self.device_mac_list[:MULTICAST_GROUP_LEN]
|
|
if self.__server_send_packet(dst_addr="01:00:5E:00:00:00", group_addr=group_addr_list) is True:
|
|
self.packets_sent += MULTICAST_GROUP_LEN
|
|
elif self.behavior == "unicast":
|
|
if self.__server_send_packet(dst_addr=random.choice(self.device_mac_list), group_addr=None) is True:
|
|
self.packets_sent += 1
|
|
elif self.behavior == "p2p":
|
|
if self.__server_send_packet(dst_addr=random.choice(self.device_mac_list), group_addr=None) is True:
|
|
self.packets_sent += 1
|
|
else:
|
|
NativeLog.add_trace_critical("unsupported behavior [%s]" % self.behavior)
|
|
self.exit()
|
|
return
|
|
|
|
def __node_send_packet(self, dst_addr, group_addr=None):
|
|
send_data = ""
|
|
ret = False
|
|
if group_addr is not None:
|
|
len_t = hex(len(group_addr) * 6).split("0x")
|
|
if len(group_addr) <= 2:
|
|
option_list = "070" + len_t[1]
|
|
else:
|
|
option_list = "07" + len_t[1]
|
|
group = ""
|
|
for addr in group_addr:
|
|
group += _convert_to_mesh_mac_format(addr)
|
|
option_list += group
|
|
dst_addr = "01:00:5E:00:00:00"
|
|
send_data = "meshsend -S -d %s -o %s -l 100\r\n" % (dst_addr, option_list)
|
|
else:
|
|
if self.behavior == "broadcast":
|
|
dst_addr = "00:00:00:00:00:00"
|
|
send_data = "meshsend -S -d %s -l 100\r\n" % dst_addr
|
|
elif self.behavior == "unicast":
|
|
if self.unicast_addr == "random":
|
|
dst_addr = random.choice(self.device_mac_list)
|
|
else:
|
|
dst_addr = self.unicast_addr
|
|
send_data = "meshsend -S -d %s -l 100\r\n" % dst_addr
|
|
elif self.behavior == "p2p":
|
|
if self.unicast_addr == "random":
|
|
dst_addr = random.choice(self.device_mac_list)
|
|
else:
|
|
dst_addr = self.unicast_addr
|
|
send_data = "meshsend -S -d %s -t 1 -l 100\r\n" % dst_addr
|
|
try:
|
|
self.port.write(send_data)
|
|
except StandardError, e:
|
|
NativeLog.add_exception_log(e)
|
|
pass
|
|
for i in range(int(CHECK_TIMEOUT / CHECK_FREQ)):
|
|
time.sleep(CHECK_FREQ)
|
|
with self.cache_lock:
|
|
if self.recv_data_cache.find("+MESHSEND:OK") != -1:
|
|
ret = True
|
|
break
|
|
elif self.recv_data_cache.find("+MESHSEND:ERROR") != -1:
|
|
break
|
|
return ret
|
|
|
|
|
|
def __node_do_send(self):
|
|
if self.behavior == "broadcast":
|
|
if self.__node_send_packet("00:00:00:00:00:00", group_addr=None) is True:
|
|
self.packets_sent += self.node_num
|
|
elif self.behavior == "multicast":
|
|
random.shuffle(self.device_mac_list)
|
|
group_addr_list = self.device_mac_list[:MULTICAST_GROUP_LEN]
|
|
if self.__node_send_packet("01:00:5E:00:00:00", group_addr_list) is True:
|
|
self.packets_sent += MULTICAST_GROUP_LEN
|
|
elif self.behavior == "unicast":
|
|
if self.__node_send_packet(random.choice(self.device_mac_list), group_addr=None) is True:
|
|
self.packets_sent += 1
|
|
elif self.behavior == "p2p":
|
|
if self.__node_send_packet(random.choice(self.device_mac_list), group_addr=None) is True:
|
|
self.packets_sent += 1
|
|
else:
|
|
NativeLog.add_trace_critical("unsupported behavior [%s]" % self.behavior)
|
|
self.exit()
|
|
return
|
|
|
|
def get_sent_packets(self):
|
|
return self.packets_sent
|
|
|
|
def exit(self):
|
|
self.exit_event.set()
|
|
pass
|
|
|
|
def run(self):
|
|
while self.exit_event.isSet() is False:
|
|
if self.typ == "SSC":
|
|
self.__node_do_send()
|
|
elif self.typ == "SERVER":
|
|
self.__server_do_send()
|
|
else:
|
|
NativeLog.add_trace_critical("type [%s] is neither SSC nor SERVER" % self.typ)
|
|
break
|
|
time.sleep(self.send_delay)
|
|
|
|
pass
|
|
|
|
|
|
class EntityRecvThread(threading.Thread):
|
|
def __init__(self, port, typ, send_recv_time):
|
|
threading.Thread.__init__(self)
|
|
self.setDaemon(True)
|
|
self.recv_data_cache = ""
|
|
self.packets_recv = 0
|
|
self.port = port
|
|
self.typ = typ
|
|
self.cache_lock = threading.Lock()
|
|
self.exit_event = threading.Event()
|
|
self.send_recv_time = send_recv_time
|
|
pass
|
|
|
|
def data_recv_callback(self, data):
|
|
# if self.typ == "SERVER":
|
|
# NativeLog.add_prompt_trace("[data_recv_callback] server recv len %d" % len(data))
|
|
with self.cache_lock:
|
|
self.recv_data_cache += data
|
|
pass
|
|
|
|
def __server_do_recv(self):
|
|
while True:
|
|
if self.recv_data_cache:
|
|
data_cache = self.recv_data_cache
|
|
data_cache_hex = binascii.hexlify(data_cache)
|
|
packet_len = int(data_cache_hex[2:6], 16)
|
|
if len(self.recv_data_cache) >= packet_len:
|
|
time3 = time.time()
|
|
data_catch_t = self.recv_data_cache[:packet_len]
|
|
packet = binascii.hexlify(data_catch_t)
|
|
index3 = int(packet[-8:], 16)
|
|
self.send_recv_time.add_recv_time(index3, time3)
|
|
self.recv_data_cache = self.recv_data_cache[packet_len:]
|
|
else:
|
|
break
|
|
#self.packets_recv += 1
|
|
else:
|
|
break
|
|
|
|
def __node_do_recv(self):
|
|
with self.cache_lock:
|
|
while True:
|
|
if self.recv_data_cache:
|
|
match = re.search("\+MESHRECV:\d+", self.recv_data_cache)
|
|
index = re.search(",(\d+),OK", self.recv_data_cache)
|
|
res = re.compile(".+,\d+,OK", re.DOTALL)
|
|
res_t = res.search(self.recv_data_cache)
|
|
if match is not None:
|
|
time4 = time.time()
|
|
if index is not None:
|
|
index4 = int(index.group(1))
|
|
self.send_recv_time.add_recv_time(index4, time4)
|
|
if len(res_t.group()) > 1:
|
|
process_index = len(res_t.group(0))
|
|
self.recv_data_cache = self.recv_data_cache[process_index:]
|
|
else:
|
|
process_index = len(res_t.group())
|
|
self.recv_data_cache = self.recv_data_cache[process_index:]
|
|
else:
|
|
break
|
|
else:
|
|
break
|
|
# self.packets_recv += 1
|
|
else:
|
|
break
|
|
pass
|
|
|
|
def get_recv_packets(self):
|
|
return self.packets_recv
|
|
|
|
def exit(self):
|
|
self.exit_event.set()
|
|
pass
|
|
|
|
def run(self):
|
|
while self.exit_event.isSet() is False:
|
|
if self.typ == "SSC":
|
|
self.__node_do_recv()
|
|
elif self.typ == "SERVER":
|
|
self.__server_do_recv()
|
|
else:
|
|
NativeLog.add_trace_critical("type [%s] is neither SSC nor SERVER" % self.typ)
|
|
break
|
|
time.sleep(CHECK_FREQ)
|
|
|
|
pass
|
|
|
|
|
|
class MeshSendRecv(PerformanceTCBase.PerformanceTCBase):
|
|
def __init__(self, name, test_env, cmd_set, timeout, log_path):
|
|
PerformanceTCBase.PerformanceTCBase.__init__(self, name, test_env, cmd_set=cmd_set,
|
|
timeout=timeout, log_path=log_path)
|
|
self.send_config = []
|
|
self.test_time = 0
|
|
self.loss_rate_standard = 0.8
|
|
# load param from excel
|
|
for i in range(1, len(cmd_set)):
|
|
if cmd_set[i][0] != "dummy" and cmd_set[i][0] != "":
|
|
cmd_string = "self." + cmd_set[i][0]
|
|
exec cmd_string
|
|
# load node send config
|
|
for i in range(1, len(cmd_set)):
|
|
for j in range(len(cmd_set[i][1])):
|
|
if cmd_set[i][1][j] != "":
|
|
cmd_string = "self.send_config.extend([" + cmd_set[i][1][j] + "])"
|
|
exec cmd_string
|
|
node_num = self.get_parameter("node_num")
|
|
self.recv_cb = dict.fromkeys(["SSC%s" % (x + 1) for x in range(int(node_num))] + ["GSOC1"])
|
|
self.recv_cb_lock = threading.Lock()
|
|
pass
|
|
|
|
def register_recv_callback(self, port_name, callback):
|
|
with self.recv_cb_lock:
|
|
if self.recv_cb[port_name] is None:
|
|
self.recv_cb[port_name] = [callback]
|
|
else:
|
|
self.recv_cb[port_name].append(callback)
|
|
pass
|
|
|
|
def process(self):
|
|
try:
|
|
test_time = self.test_time * 60
|
|
send_config = self.send_config
|
|
loss_rate_standard = self.loss_rate_standard
|
|
node_num = self.get_parameter("node_num")
|
|
pc_ip_list = self.get_parameter("pc_ip").split(".")
|
|
port = self.get_parameter("test_tcp_port1")
|
|
send_recv_time = SendRecvTime()
|
|
except StandardError:
|
|
return
|
|
#create server_addr
|
|
server_addr = ""
|
|
for i in range(len(pc_ip_list)):
|
|
if pc_ip_list[i] in ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]:
|
|
server_addr = server_addr + "0" + pc_ip_list[i]
|
|
else:
|
|
list_t = hex(int(pc_ip_list[i])).split("0x")
|
|
server_addr += list_t[1]
|
|
port_t = hex(port).split("0x")
|
|
port_t_list = list(port_t[1])
|
|
server_addr = server_addr + port_t_list[2] + port_t_list[3] + port_t_list[0] + port_t_list[1]
|
|
server_port = self.test_env.get_port_by_name("GSOC1")
|
|
if server_port is None:
|
|
return
|
|
|
|
# create thread dict
|
|
thread_dict = dict.fromkeys(["SSC%s" % (x + 1) for x in range(int(node_num))] + ["GSOC1"])
|
|
for port_name in thread_dict:
|
|
thread_dict[port_name] = dict(zip(["tx", "rx"], [None, None]))
|
|
device_mac_list = []
|
|
# init recv thread & register port for SSC
|
|
for port_name in ["SSC%s" % (x + 1) for x in range(int(node_num))]:
|
|
port = self.test_env.get_port_by_name(port_name)
|
|
thread_dict[port_name]["rx"] = EntityRecvThread(port, "SSC", send_recv_time)
|
|
self.register_recv_callback(port_name, thread_dict[port_name]["rx"].data_recv_callback)
|
|
device_mac_list.append(port.device_mac)
|
|
|
|
thread_dict["GSOC1"]["rx"] = EntityRecvThread(server_port, "SERVER", send_recv_time)
|
|
self.register_recv_callback("GSOC1", thread_dict["GSOC1"]["rx"].data_recv_callback)
|
|
|
|
# config[0]: target_name; config[1]: behavior; config[2]: destination; config[3]:send_delay;
|
|
for config in send_config:
|
|
port = self.test_env.get_port_by_name(config[0])
|
|
name = port.name
|
|
if config[2] == "GSOC1":
|
|
dst = server_addr[:2] + ":" + server_addr[2:4] + ":" + server_addr[4:6] + ":" + server_addr[6:8] + \
|
|
":" + server_addr[8:10] + ":" + server_addr[10:12]
|
|
elif config[2] == "random":
|
|
dst = "random"
|
|
else:
|
|
dst = self.test_env.get_port_by_name(config[2]).device_mac
|
|
if name != "GSOC1":
|
|
server_addr = None
|
|
if config[1] == "broadcast" or config[1] == "multicast":
|
|
dst = None
|
|
typ = "SSC" if isinstance(port, MeshPort.MeshPort) is False else "SERVER"
|
|
thread_dict[name]["tx"] = EntitySendThread(port, config[1], dst, config[3], typ, device_mac_list,
|
|
server_addr, send_recv_time)
|
|
self.register_recv_callback(name, thread_dict[name]["tx"].data_recv_callback)
|
|
pass
|
|
|
|
# start all thread
|
|
for port_name in thread_dict:
|
|
if thread_dict[port_name]["rx"] is not None:
|
|
thread_dict[port_name]["rx"].start()
|
|
if thread_dict[port_name]["tx"] is not None:
|
|
thread_dict[port_name]["tx"].start()
|
|
|
|
# wait test time
|
|
time.sleep(test_time)
|
|
# close all send thread
|
|
for port_name in thread_dict:
|
|
if thread_dict[port_name]["tx"] is not None:
|
|
thread_dict[port_name]["tx"].exit()
|
|
thread_dict[port_name]["tx"].join()
|
|
# make sure all packet received before close recv thread
|
|
time.sleep(10)
|
|
# close all recv thread
|
|
for port_name in thread_dict:
|
|
if thread_dict[port_name]["rx"] is not None:
|
|
thread_dict[port_name]["rx"].exit()
|
|
thread_dict[port_name]["rx"].join()
|
|
|
|
[max_delay_time, avg_delay_time, loss_rate] = send_recv_time.calculate()
|
|
|
|
NativeLog.add_trace_critical("[Mesh Send Recv Test] MAX Delay Time is %.3f" % max_delay_time)
|
|
NativeLog.add_trace_critical("[Mesh Send Recv Test] Avg Delay Time is %.3f" % avg_delay_time)
|
|
NativeLog.add_trace_critical("[Mesh Send Recv Test] loss rate is %.2f%%" % (loss_rate * 100))
|
|
|
|
# set succeed if loss rate higher than required
|
|
if loss_rate < loss_rate_standard:
|
|
self.set_result("Succeed")
|
|
pass
|
|
|
|
@Encoding.encode_utf8(3)
|
|
def result_check(self, port_name, data):
|
|
if port_name in self.recv_cb:
|
|
# if port_name == "GSOC1":
|
|
# NativeLog.add_prompt_trace("[result_check] recv GSOC1 data len %s" % len(data))
|
|
with self.recv_cb_lock:
|
|
callback_list = self.recv_cb[port_name]
|
|
if callback_list is not None:
|
|
for callback in callback_list:
|
|
callback(data)
|
|
|
|
# do logging
|
|
timestamp = NativeLog.generate_timestamp()
|
|
with self.sync_lock:
|
|
_formatted_data = HTMLGenerator.process_one_log_item(data, self.log_index, port_name, timestamp)
|
|
self.log_index += 1
|
|
|
|
self.append_to_log_file(_formatted_data)
|
|
|
|
NativeLog.add_all_tc_log(data, port_name, timestamp)
|
|
pass
|
|
|
|
|
|
def main():
|
|
pass
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|