90e57cdf8f
1. add test cases and related scripts 2. add CI config files read README.md for detail
378 lines
12 KiB
Python
Executable file
378 lines
12 KiB
Python
Executable file
import socket
|
|
import time
|
|
import os
|
|
import threading
|
|
|
|
from NativeLog import NativeLog
|
|
|
|
|
|
RETRANSMIT_COUNT = 5
|
|
RETRANSMIT_TIMEOUT = 0.5
|
|
ABORT_TIMEOUT = 120
|
|
BEACON_SEND_RATE = 30
|
|
|
|
|
|
VALUE_NAME = {"type": 0x00,
|
|
"session id": 0x01,
|
|
"result code": 0x02,
|
|
"ap ssid": 0x03,
|
|
"ap password": 0x04,
|
|
"start SC time": 0x05,
|
|
"recv UDP time": 0x06,
|
|
"SP model": 0x07,
|
|
"SP mac": 0x08,
|
|
"ET version": 0x09,
|
|
"ap bssid": 0x0A,
|
|
"ssid hidden": 0x0B,
|
|
"ap encryption": 0x0C,
|
|
}
|
|
|
|
TYPE_VAL = {"Init new test": 0x00,
|
|
"test request": 0x01,
|
|
"result": 0x02,
|
|
"query phone": 0x03,
|
|
"ACK": 0x80,
|
|
"phone report": 0x81,
|
|
"Not support": 0xFF,
|
|
"invalid session": 0xFE,
|
|
}
|
|
|
|
RESULT_CODE_VAL = {"OK": 0x80,
|
|
"JAP fail": 0x81, # SP join AP fail, should disqualify this result
|
|
"recv UDP fail": 0x82, # SP did not receive UDP sent by target
|
|
}
|
|
|
|
AP_ENCRYPTION_VAL = {"OPEN": 0x00,
|
|
"WEP": 0x01,
|
|
"WPA": 0x02,
|
|
}
|
|
|
|
AP_PROPERTY = ("ssid", "password", "bssid", "is_hidden", "encryption", "ht")
|
|
PHONE_PROPERTY = ("ip", "mac", "model")
|
|
|
|
|
|
SERIAL_PORT_NUM = 3
|
|
LOG_FILE_PREFIX = "SC_IOT"
|
|
LOG_FOLDER = os.path.join("AT_LOG", "TEMP")
|
|
LOG_FILE_NAME = os.path.join(LOG_FOLDER, "%s_%s.log" % (LOG_FILE_PREFIX, time.strftime("%d%H%M%S", time.localtime())))
|
|
|
|
|
|
REQUEST_LOCK = threading.Lock()
|
|
HANDLER_LOCK = threading.Lock()
|
|
|
|
|
|
def sync_request_list(func):
|
|
def handle_args(*args, **kwargs):
|
|
with REQUEST_LOCK:
|
|
ret = func(*args, **kwargs)
|
|
return ret
|
|
return handle_args
|
|
|
|
|
|
def sync_handler_list(func):
|
|
def handle_args(*args, **kwargs):
|
|
with HANDLER_LOCK:
|
|
ret = func(*args, **kwargs)
|
|
return ret
|
|
return handle_args
|
|
|
|
|
|
def _process_one_tlv_pair(data):
|
|
typ = ord(data[0])
|
|
length = ord(data[1])
|
|
value = data[2:2+length]
|
|
processed_data = data[2+length:]
|
|
return (typ, value), processed_data
|
|
pass
|
|
|
|
|
|
def bytes_to_msg(data):
|
|
data_to_process = data
|
|
msg = []
|
|
while True:
|
|
one_pair, data_to_process = _process_one_tlv_pair(data_to_process)
|
|
msg.append(one_pair)
|
|
if len(data_to_process) == 0:
|
|
break
|
|
return msg
|
|
pass
|
|
|
|
|
|
def msg_to_bytes(msg):
|
|
byte_str = ""
|
|
for pair in msg:
|
|
byte_str += chr(pair[0])
|
|
if isinstance(pair[1], list) is True:
|
|
byte_str += chr(len(pair[1]))
|
|
byte_str.join([chr(m) for m in pair[1]])
|
|
elif isinstance(pair[1], str) is True:
|
|
byte_str += chr(len(pair[1]))
|
|
byte_str += pair[1]
|
|
elif isinstance(pair[1], int) is True:
|
|
byte_str += chr(1)
|
|
byte_str += chr(pair[1])
|
|
else:
|
|
raise TypeError("msg content only support list and string type")
|
|
return byte_str
|
|
|
|
|
|
def get_value_from_msg(type_list, msg):
|
|
if isinstance(type_list, str) is True:
|
|
type_list = [type_list]
|
|
ret = [""] * len(type_list)
|
|
for pair in msg:
|
|
for i in range(len(type_list)):
|
|
if pair[0] == VALUE_NAME[type_list[i]]:
|
|
ret[i] = pair[1]
|
|
if "" not in ret:
|
|
# all type value found
|
|
break
|
|
else:
|
|
NativeLog.add_prompt_trace("missing required type in msg")
|
|
return ret
|
|
|
|
|
|
def bytes_to_time(bytes_in):
|
|
if len(bytes_in) != 4:
|
|
return 0
|
|
t = float(ord(bytes_in[0])*256*256*256 + ord(bytes_in[1])*256*256
|
|
+ ord(bytes_in[2])*256 + ord(bytes_in[2]))/1000
|
|
return t
|
|
pass
|
|
|
|
|
|
def mac_to_bytes(mac):
|
|
tmp = mac.split(':')
|
|
return "".join([chr(int(m[:2], base=16)) for m in tmp])
|
|
pass
|
|
|
|
|
|
def bytes_to_mac(bytes_in):
|
|
mac = "".join(["%x:" % ord(m) for m in bytes_in] )
|
|
return mac[:-1]
|
|
|
|
|
|
class RetransmitHandler(threading.Thread):
|
|
def __init__(self, udp_server):
|
|
threading.Thread.__init__(self)
|
|
self.setDaemon(True)
|
|
self.udp_server = udp_server
|
|
self.exit_event = threading.Event()
|
|
pass
|
|
|
|
@sync_request_list
|
|
def find_required_retransmit_msg(self):
|
|
time_now = time.time()
|
|
aborted_sessions = []
|
|
retransmit_msg = []
|
|
msgs = filter(lambda x: time_now - x[4] >= RETRANSMIT_TIMEOUT, self.udp_server.unconfirmed_request)
|
|
for msg in msgs:
|
|
if msg[3] == 0:
|
|
aborted_sessions.append(msg[0])
|
|
self.udp_server.unconfirmed_request.remove(msg)
|
|
else:
|
|
msg[3] -= 1
|
|
msg[4] = time_now
|
|
retransmit_msg.append(msg)
|
|
pass
|
|
return aborted_sessions, retransmit_msg
|
|
pass
|
|
|
|
def run(self):
|
|
while True:
|
|
self.exit_event.wait(0.1)
|
|
if self.exit_event.isSet() is True:
|
|
break
|
|
aborted_sessions, retransmit_msg = self.find_required_retransmit_msg()
|
|
for msg in retransmit_msg:
|
|
self.udp_server.udp_socket.sendto(msg[1], msg[2])
|
|
for session_id in aborted_sessions:
|
|
self.udp_server.session_aborted(session_id)
|
|
|
|
def exit(self):
|
|
self.exit_event.set()
|
|
pass
|
|
|
|
|
|
class SendBeacon(threading.Thread):
|
|
def __init__(self, sock, udp_port):
|
|
threading.Thread.__init__(self)
|
|
self.setDaemon(True)
|
|
self.udp_sock = sock
|
|
self.udp_port = udp_port
|
|
self.exit_event = threading.Event()
|
|
|
|
def run(self):
|
|
while True:
|
|
msg = [[VALUE_NAME["type"], TYPE_VAL["query phone"]]]
|
|
data = msg_to_bytes(msg)
|
|
self.udp_sock.sendto(data, ("<broadcast>", self.udp_port))
|
|
for i in range(BEACON_SEND_RATE):
|
|
self.exit_event.wait(1)
|
|
if self.exit_event.isSet() is True:
|
|
return
|
|
pass
|
|
|
|
def exit(self):
|
|
self.exit_event.set()
|
|
pass
|
|
|
|
|
|
class UDPServer(threading.Thread):
|
|
def __init__(self, pc_ip, udp_port, update_phone_handler):
|
|
threading.Thread.__init__(self)
|
|
self.setDaemon(True)
|
|
sock = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
|
|
sock.bind((pc_ip, udp_port))
|
|
sock.settimeout(1)
|
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
|
self.udp_socket = sock
|
|
self.unconfirmed_request = []
|
|
self.test_handler_list = []
|
|
self.pc_ip = pc_ip
|
|
self.udp_port = udp_port
|
|
self.update_phone_handler = update_phone_handler
|
|
self.retransmit_thread = RetransmitHandler(self)
|
|
self.beacon_thread = SendBeacon(self.udp_socket, self.udp_port)
|
|
self.retransmit_thread.start()
|
|
self.beacon_thread.start()
|
|
self.exit_event = threading.Event()
|
|
pass
|
|
|
|
@sync_handler_list
|
|
def register_test_handler(self, session_id, test_handler):
|
|
tmp = filter(lambda x: x[0] == session_id, self.test_handler_list)
|
|
if len(tmp) > 0:
|
|
NativeLog.add_prompt_trace("handler with same session id exist")
|
|
else:
|
|
self.test_handler_list.append([session_id, test_handler])
|
|
pass
|
|
|
|
@sync_handler_list
|
|
def deregister_test_handler(self, session_id):
|
|
tmp = filter(lambda x: x[0] == session_id, self.test_handler_list)
|
|
if len(tmp) > 1:
|
|
NativeLog.add_prompt_trace("deregister test handler fail")
|
|
elif len(tmp) == 1:
|
|
self.test_handler_list.remove(tmp[0])
|
|
pass
|
|
|
|
@sync_handler_list
|
|
def get_test_handler(self, session_id):
|
|
ret = None
|
|
tmp = filter(lambda x: x[0] == session_id, self.test_handler_list)
|
|
if len(tmp) != 1:
|
|
NativeLog.add_prompt_trace("failed to get test handler, "
|
|
"%d handler found, session id %s" % (len(tmp), session_id))
|
|
elif len(tmp) == 1:
|
|
ret = tmp[0][1]
|
|
return ret
|
|
pass
|
|
|
|
def session_aborted(self, session_id):
|
|
test_handler = self.get_test_handler(session_id)
|
|
if test_handler is not None:
|
|
test_handler.abort_handler()
|
|
pass
|
|
|
|
def confirm_request(self, session_id, msg, address):
|
|
test_handler = self.get_test_handler(session_id)
|
|
if test_handler is not None:
|
|
test_handler.res_receiver(msg, address)
|
|
self.remove_pending_request(session_id)
|
|
pass
|
|
|
|
def receive_request(self, msg, address):
|
|
result = get_value_from_msg(["type", "session id"], msg)
|
|
msg_type = ord(result[0])
|
|
session_id = result[1]
|
|
if msg_type != TYPE_VAL["result"]:
|
|
self.send_response([[VALUE_NAME["type"], TYPE_VAL["Not support"]]], address)
|
|
else:
|
|
test_handler = self.get_test_handler(session_id)
|
|
if test_handler is None:
|
|
self.send_response([[VALUE_NAME["type"], TYPE_VAL["invalid session"]],
|
|
[VALUE_NAME["session id"], session_id]],
|
|
address)
|
|
pass
|
|
else:
|
|
test_handler.req_receiver(msg, address)
|
|
pass
|
|
|
|
@sync_request_list
|
|
def add_request_to_queue(self, dest_addr, session_id, data):
|
|
tmp = filter(lambda x: x[0] == session_id, self.unconfirmed_request)
|
|
if len(tmp) != 0:
|
|
NativeLog.add_prompt_trace("One pending request belong to same session id %s" % session_id)
|
|
pass
|
|
else:
|
|
self.unconfirmed_request.append([session_id, data,
|
|
dest_addr, RETRANSMIT_COUNT-1, time.time()])
|
|
|
|
def send_request(self, dest_addr, session_id, msg):
|
|
data = msg_to_bytes(msg)
|
|
self.add_request_to_queue(dest_addr, session_id, data)
|
|
self.udp_socket.sendto(data, dest_addr)
|
|
pass
|
|
|
|
def send_response(self, msg, address):
|
|
self.udp_socket.sendto(msg_to_bytes(msg), address)
|
|
|
|
@sync_request_list
|
|
def remove_pending_request(self, session_id):
|
|
tmp = filter(lambda x: x[0] == session_id, self.unconfirmed_request)
|
|
if len(tmp) > 0:
|
|
self.unconfirmed_request.remove(tmp[0])
|
|
pass
|
|
pass
|
|
|
|
def handle_response(self, msg, address):
|
|
result = get_value_from_msg(["type", "session id"], msg)
|
|
msg_type = ord(result[0])
|
|
session_id = result[1]
|
|
if msg_type == TYPE_VAL["ACK"]:
|
|
self.confirm_request(session_id, msg, address)
|
|
elif msg_type == TYPE_VAL["phone report"]:
|
|
# add new available phone
|
|
tmp = get_value_from_msg(["SP model", "SP mac"], msg)
|
|
phone = dict(zip(PHONE_PROPERTY, [address[0], bytes_to_mac(tmp[1]), tmp[0]]))
|
|
self.update_phone_handler(phone)
|
|
pass
|
|
elif msg_type == TYPE_VAL["Not support"] or msg_type == TYPE_VAL["invalid session"]:
|
|
self.session_aborted(session_id)
|
|
pass
|
|
|
|
def run(self):
|
|
while self.exit_event.isSet() is False:
|
|
try:
|
|
data, address = self.udp_socket.recvfrom(65535)
|
|
except socket.error, e:
|
|
continue
|
|
|
|
if address[0] == self.pc_ip:
|
|
continue
|
|
|
|
msg = bytes_to_msg(data)
|
|
msg_type = get_value_from_msg(["type"], msg)[0]
|
|
|
|
if msg_type is None:
|
|
NativeLog.add_prompt_trace("invalid incoming msg: %s" % "".join(["0x%X, " % m for m in data]))
|
|
else:
|
|
msg_type = ord(msg_type)
|
|
# check if request or reply
|
|
if (msg_type & 0x80) != 0:
|
|
self.handle_response(msg, address)
|
|
else:
|
|
self.receive_request(msg, address)
|
|
pass
|
|
|
|
self.retransmit_thread.exit()
|
|
self.beacon_thread.exit()
|
|
self.retransmit_thread.join()
|
|
self.beacon_thread.join()
|
|
pass
|
|
|
|
def exit(self):
|
|
self.exit_event.set()
|
|
pass
|