OVMS3-idf/components/test/TestCaseScript/MeshStress/MeshSendRecv.py

526 lines
21 KiB
Python
Raw Normal View History

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()