#!/usr/bin/env python3 import os import re import subprocess import time from enum import Enum import pexpect import pexpect.fdpexpect import pexpect.spawnbase import serial rootPath = os.path.dirname(os.path.abspath(__file__)) tmp_read_nonblocking = pexpect.spawnbase.SpawnBase.read_nonblocking def enhanced_read_nonblocking(self, size=1, timeout=None): return re.sub( r"(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]", "", tmp_read_nonblocking(self, size, timeout).decode(errors="ignore"), ).encode() pexpect.spawnbase.SpawnBase.read_nonblocking = enhanced_read_nonblocking class StatusCodeEnum(Enum): NORMAL = (0, "Normal") TIMEOUT_ERR = (-1, "Timeout") EOF_ERR = (-2, "EOF") CRASH_ERR = (-3, "Crash happened") BUSYLOOP_ERR = (-4, "Busy loop happened") UNKNOWN_ERR = (-5, "Unknown") @staticmethod def get_enum_msg_by_code(status_code): for status in StatusCodeEnum: if status.value[0] == status_code: return status.value[1] class connectNuttx(object): def __init__( self, board, vela_path, dev, log_path, fs, ota_version, core, sudo, ci, method, target, ): self.board = board self.path = vela_path self.dev = dev self.log_path = log_path self.fs = fs self.version = ota_version self.sudo = sudo self.ci = ci self.core = core self.method = method self.target = target self.enter = "\r" self.debug_flag = 0 self.format_str_len = 105 # get PROMPT value and rate value self.PROMPT = getConfigValue( self.path, self.board, core=self.core, flag="NSH_PROMPT_STRING" ) self.rate = getConfigValue( self.path, self.board, core=self.core, flag="UART0_BAUD" ) if not os.path.exists(self.log_path): os.makedirs(self.log_path) def setup(self): self.start_time = time.strftime("%Y%m%d_%H%M%S", time.localtime()) print("============ start at %s ============" % self.start_time) if self.target == "sim": # start nuttx for sim start.startNuttx(self, self.path, self.log_path, self.board, self.sudo) if self.target == "qemu": start.startQemu(self, self.path, self.log_path, self.board, self.sudo) if self.target in ["target", "module"]: # start minicom if self.method == "minicom": start.startMinicom( self, self.dev, self.board, self.log_path, self.core, self.rate ) else: start.startSerial( self, self.dev, self.board, self.log_path, self.core, self.rate ) def sendcontrol(self, char): char = char.lower() a = ord(char) if 97 <= a <= 122: a = a - ord("a") + 1 byte = bytes([a]) return byte d = { "@": 0, "`": 0, "[": 27, "{": 27, "\\": 28, "|": 28, "]": 29, "}": 29, "^": 30, "~": 30, "_": 31, "?": 127, } if char not in d: return b"" byte = bytes(d[char]) return byte def sendControlCmd(self, cmd, expect="ap>", timeout=10): if self.method == "minicom": self.process.sendcontrol(cmd) time.sleep(1) self.process.sendcontrol(cmd) else: byte = self.sendcontrol(cmd) time.sleep(1) self.process.send(byte) time.sleep(1) self.process.send(byte) def print_format_str(self, string, type="text"): str_prefix = "+" str_suffix = "+" if type == "head": rest_char_len = self.format_str_len - 2 - len(string) half_len = int(rest_char_len / 2) print( str_prefix + "-" * half_len + string + "-" * (rest_char_len - half_len) + str_suffix ) elif type == "tail": rest_char_len = self.format_str_len - 2 print(str_prefix + "-" * rest_char_len + str_suffix) elif type == "text": str_prefix = "| " str_suffix = " |" rest_char_len = ( self.format_str_len - len(str_prefix) - len(str_suffix) - len(string) ) print( str_prefix + string + " " * (1 if rest_char_len < 1 else rest_char_len) + str_suffix ) else: print(string) def clean_buffer(self): i = -1 while True: if ( ( self.process.before is not None and self.process.before.decode(errors="ignore") .replace("\r", "") .replace("\n", "") != "" ) or ( self.process.after is not None and self.process.after != pexpect.TIMEOUT and self.process.after.decode(errors="ignore") .replace("\r", "") .replace("\n", "") != "" ) or i == 0 ): i = self.process.expect( [re.compile(b".+"), pexpect.TIMEOUT, pexpect.EOF], timeout=0.1 ) else: while True: try: self.process.read_nonblocking( size=self.process.maxread, timeout=0.1 ) except Exception: break self.process.before = b"" self.process.after = b"" break # send command to nsh def sendCommand(self, cmd, *argc, **argv): expect = [] timeout = 10 ret = StatusCodeEnum.NORMAL.value[0] length = len(argc) if length == 0: expect.append(self.PROMPT) else: for i in argc: expect.append(i) length = len(argv) if length != 0: for key, value in argv.items(): if key == "timeout": timeout = value if self.method != "minicom": time.sleep(0.5) if self.target == "qemu": self.clean_buffer() self.process.sendline(cmd) else: self.clean_buffer() self.process.sendline(cmd) time.sleep(0.1) self.process.send("\r\n\r\n") try: for i in expect: ret = self.process.expect(i, timeout=timeout) except Exception as e: self.print_format_str(" Catch Exception ", type="head") if isinstance(e, pexpect.TIMEOUT): ret = StatusCodeEnum.TIMEOUT_ERR.value[0] elif isinstance(e, pexpect.EOF): ret = StatusCodeEnum.EOF_ERR.value[0] self.print_format_str(f"An pexpect.EOF error occurred: {str(e)}") else: ret = StatusCodeEnum.UNKNOWN_ERR.value[0] self.print_format_str(f"An unexpected error occurred: {str(e)}") self.print_format_str(" Result ", type="head") self.print_format_str(f"Command : '{cmd}'") self.print_format_str(f"Expect value: {str(expect)}") self.print_format_str(f"Timeout : {timeout}s") self.print_format_str( f"Test result : {StatusCodeEnum.get_enum_msg_by_code(ret)}" ) self.print_format_str("", type="tail") finally: self.debug(cmd, ret) if self.method != "minicom": time.sleep(0.5) return ret def switch_to_original_core(self): if self.target == "target": self.sendControlCmd("c") if self.core != "ap": self.process.sendline("cu -l /dev/tty%s\n" % self.core.upper()) self.process.expect_exact(self.PROMPT) def debug(self, cmd, ret): if self.debug_flag: print("********************* DEBUG START ********************") if cmd == "\n": cmd = r"\n" print("cmd: {}".format(cmd)) print("ret: {}".format(ret)) print("before: {}".format(self.process.before.decode(errors="ignore"))) print("after: {}".format(self.process.after.decode(errors="ignore"))) print("buffer: {}".format(self.process.buffer.decode(errors="ignore"))) print("********************** DEBUG END **********************") def cleanup(self): if self.target == "sim": self.process.sendline("poweroff") if self.target == "qemu": self.sendControlCmd("a", self.PROMPT) self.process.sendline("x") class start: def startMinicom(self, dev, board, log_path, core, rate): self.log = "{}/{}_{}.cap".format(log_path, dev[-4:], self.start_time) self.process = pexpect.spawn( r"sudo minicom -D {} -b {} -o -C {}".format(dev, rate, self.log), maxread=200000, ) self.process.expect("Welcome to minicom") self.switch_to_original_core() def startSerial(self, dev, board, log_path, core, rate): self.log = "{}/{}_{}.cap".format(log_path, dev[-4:], self.start_time) self.logFile = open(self.log, "ab+") self.ser = serial.Serial(port=dev, baudrate=int(rate)) self.process = pexpect.fdpexpect.fdspawn( self.ser, "wb", maxread=20000, logfile=self.logFile ) self.switch_to_original_core() def startNuttx(self, path, log_path, board="sim", sudo=False): os.chdir(path) self.log = "{}/{}_{}.log".format(log_path, board, self.start_time) if sudo: if board in ["sim_rpserver", "sim_rpproxy"]: os.chdir(path) self.process = pexpect.spawn( "bash", ["-c", "sudo ./rpserver/nuttx/nuttx | tee %s" % self.log] ) else: self.process = pexpect.spawn( "bash", ["-c", "sudo ./nuttx | tee %s" % self.log] ) else: self.process = pexpect.spawn("bash", ["-c", "./nuttx | tee %s" % self.log]) self.process.expect(self.PROMPT) def startQemu(self, path, log_path, board="qemu", sudo=False): os.chdir(path) self.log = "{}/{}_{}.log".format(log_path, board, self.start_time) flag1 = getConfigValue(path, board, core=None, flag="ARCH_CHIP") if flag1 == "imx6": self.process = pexpect.spawn( "bash", [ "-c", "qemu-system-arm -semihosting -M sabrelite -m 1024 -smp 4 -kernel ./nuttx -nographic | tee %s" % self.log, ], ) if flag1 == "qemu-rv": flag2 = getConfigValue(path, board, core=None, flag="ARCH_RV64") options = "" if flag2: riscv = "qemu-system-riscv64" else: riscv = "qemu-system-riscv32" fs_flag = getConfigValue(path, board, core=None, flag="DRIVERS_VIRTIO_BLK") if fs_flag: os.system("dd if=/dev/zero of=fatfs.img bs=512 count=128K") os.system("mkfs.fat fatfs.img") os.system("chmod 777 ./fatfs.img") options = ( "-drive index=0,id=userdata,if=none,format=raw,file=./fatfs.img " "-device virtio-blk-device,bus=virtio-mmio-bus.0,drive=userdata" ) self.process = pexpect.spawn( "bash", [ "-c", "%s -M virt -bios ./nuttx -nographic %s | tee %s" % (riscv, options, self.log), ], ) self.process.expect(self.PROMPT) def runCmd(cmd): p = subprocess.Popen( cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) stdout, stderr = p.communicate() recode = p.returncode if recode != 0: print("Debug: run command '%s' failed" % cmd) return stdout # find file def findFile(path, flag, board, core=None, match=True): fList = [] for root, dirs, files in os.walk(path): for name in files: if not match and flag in name: fList.append(os.path.join(root, name)) if match and name == flag and ".o" not in name: fList.append(os.path.join(root, name)) if len(fList) != 1: fList = [x for x in fList if board in x and core + "/" in x] print(fList) return fList # get CONFIG_NSH_PROMPT_STRING value def getConfigValue(path, board, core, flag): value = "" l1 = findFile(path, ".config", board, core=core) with open(l1[0], "r+") as f: lines = f.readlines() f.close() print(lines) print(flag) for line in lines: if flag + "=" in line: value = line.split("=")[1] if '"' in value: value = value.strip('"').strip() print(value) return value # read log and extract timestamp def getTimestamp(filename, str1, str2, pattern): with open(filename, "r") as f: buff = f.read() pat = re.compile(str1 + "(.*?)" + str2, re.S) result = pat.findall(buff) timestamp = re.findall(pattern, str(result)) # extract timestamp return timestamp # calculate the time difference for each time def getDif(timestamp, rateANDinterance): lst = [] for i in range(1, len(timestamp)): difts = int(timestamp[i]) - int(timestamp[i - 1]) float_num = (difts / 1000000) - rateANDinterance lst.append(float_num) return lst # read log def getLog(filename, str1, str2): l1 = [] with open(filename, "r", encoding="utf-8", errors="ignore") as f: buff = f.read() f.close() pat = re.compile(str1 + "(.*?)" + str2, re.S) result = pat.findall(buff) if "gtest" in str1: return result[0].strip("\n") else: # add line to l1 for i in result[0].strip("\n").split("\n"): l1.append(i) return l1 def rmfile(p, core, file): if p.core == core: p.sendCommand("rm -r" + file) else: p.process.sendline("cu -l /dev/tty" + core.upper()) p.sendCommand("\n", core, flag=core + ">") p.sendCommand("rm -r " + file, core, flag=core + ">") if p.core == "ap": p.sendControlCmd("c") else: p.switch_to_original_core(p)