aboutsummaryrefslogtreecommitdiff
path: root/Tools/fileman/nsh.py
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/fileman/nsh.py')
-rwxr-xr-xTools/fileman/nsh.py222
1 files changed, 222 insertions, 0 deletions
diff --git a/Tools/fileman/nsh.py b/Tools/fileman/nsh.py
new file mode 100755
index 000000000..8336b3c50
--- /dev/null
+++ b/Tools/fileman/nsh.py
@@ -0,0 +1,222 @@
+import os, sys, serial, time, struct
+
+class NSH:
+ def __init__(self):
+ self.serial = None
+
+ def flush(self):
+ self.serial.flush()
+
+ def send(self, data):
+ from collections import deque
+ data = deque(data)
+ attempt = 0
+ while data:
+ if self.serial.write(data.popleft()) == 0:
+ print("Failed to write {}".format(attempt))
+ attempt += 1
+ if attempt > 50:
+ raise RuntimeError("fail")
+ else:
+ attempt = 0
+ self.serial.flush()
+
+ def recv(self, size=1):
+ if size is not None:
+ attempt = 1
+ data = self.serial.read(size)
+ if len(data) < 1:
+ raise RuntimeError("timeout waiting for data")
+ size -= len(data)
+ if size > 0:
+ #raise RuntimeError("received less data than expected ({}/{})".format(len(data), size))
+ attempt += 1
+ if attempt > 10:
+ raise RuntimeError("Couldn't receive all the data ({} bytes left)".format(size))
+ return data
+ else:
+ return self.serial.read()
+
+ def wait_for_string(self, s, timeout, debug=False):
+ t0 = time.time()
+ buf = []
+ res = []
+ n = 0
+ if type(s) == str:
+ s = (s,)
+ maxlen = max((len(string) for string in s))
+ while True:
+ c = self.recv(size=None)
+ if debug:
+ sys.stderr.write(c)
+ buf.append(c)
+ if len(buf) > maxlen:
+ res.append(buf.pop(0))
+ n += 1
+ if n % 10000 == 0:
+ sys.stderr.write(str(n) + "\n")
+ string = "".join(buf)
+ for sz in s:
+ index = string.find(sz)
+ if index != -1:
+ res += buf[:index]
+ return "".join(res)
+ if timeout > 0.0 and time.time() - t0 > timeout:
+ raise Exception("Timeout while waiting for: {}".format(s))
+
+ def exec_cmd(self, cmd, timeout, cmd_end_str=("nsh> \x1b[K",)):
+ print("nsh> {}".format(cmd))
+ self.send(cmd + "\n")
+ self.flush()
+ self.wait_for_string(cmd + "\r\n", timeout)
+ if cmd_end_str:
+ return self.wait_for_string(cmd_end_str, timeout)
+ else:
+ return None
+
+ def identify(self):
+ #ver hw
+ #PX4FMU_V2
+ return self.exec_cmd("ver hw", 1.0).startswith("PX4FMU_V2")
+
+ def ls_dir(self, dir, timeout=1.0):
+ res = []
+ dir = dir.rstrip("/")
+ for line in self.exec_cmd("ls " + dir, timeout).splitlines()[1:]:
+ res.append(line[1:])
+ return res
+
+ def get_dir_files(self, directory="/"):
+ res = []
+ for f in self.ls_dir(directory):
+ if f[-1] != '/': #is file
+ res.append(f)
+ return f
+
+ def get_all_files(self, basedir="/", subdir="/"):
+ dir = basedir + subdir
+ res = []
+ for d in self.ls_dir(dir):
+ if d[-1] == '/':
+ res += self.get_all_files(basedir, subdir + d)
+ else:
+ res.append(subdir + d)
+ return res
+
+ def get_all_dirs(self, basedir="/", subdir="/"):
+ dir = basedir + subdir
+ res = []
+ for d in self.ls_dir(dir):
+ if d[-1] == '/':
+ res += self.get_all_files(basedir, subdir + d)
+ res.append(d)
+ return res
+
+ def file_exists(self, fn):
+ rslash = fn.rindex("/")
+ dir = fn[:rslash]
+ fn = fn[rslash+1:]
+ found = False
+ for file in self.ls_dir(dir):
+ if file == fn:
+ return True
+ return False
+
+ def download_file(self, fn):
+ res = self.exec_cmd("dumpfile " + fn, 1.0, ("\n",))
+ data = []
+ if res.startswith("OK"):
+ size = int(res.split()[1])
+ block_size = 1024 * 16
+ print("Reading file {}:".format(fn))
+ while size > 0:
+ buf = self.recv(min(size, block_size))
+ data.append(buf)
+ size -= len(buf)
+ print("Read {} bytes / {} left".format(len(buf), size))
+ sys.stdout.flush()
+ print("Successfully read file {}".format(fn))
+ else:
+ raise Exception("Error downloading file (not exists?)")
+ self.wait_for_string("nsh> \x1b[K", 2.0)
+ return "".join(data)
+
+ def random_str(self, length):
+ from random import shuffle
+ from math import ceil
+ abc = ''.join([chr(c) for c in range(ord('a'), ord('z') + 1)])
+ ABC = ''.join([chr(c) for c in range(ord('A'), ord('Z') + 1)])
+ num = '0123456789'
+ string = abc + ABC + num
+ mul = int(ceil(float(length) / len(string)))
+ res = list(string * mul)
+ shuffle(res)
+ return ''.join(res[:length])
+
+ def upload_file(self, out_fn, in_fn):
+ f = open(in_fn, 'rb')
+ filedata = f.read()
+ f.close()
+
+ szOK = "OK\r\n"
+ szDEFEND = "nsh> \x1b[K"
+
+ while True:
+ tmp_fn = '/fs/microsd/' + self.random_str(14) + '.tmp'
+ if not self.file_exists(tmp_fn):
+ break
+
+ res = self.exec_cmd("writefile {} {}".format(tmp_fn, len(filedata)), 1.0, (szDEFEND, szOK))
+ if len(res) == 0: #szOK found
+ self.flush()
+ print("Writing file {}".format(tmp_fn))
+ self.send(filedata)
+ self.wait_for_string("nsh> \x1b[K", 2.0)
+ print("Deleting {}".format(out_fn))
+ self.exec_cmd("rm {}".format(out_fn), 2.0)
+ print("Coping {} to {}".format(tmp_fn, out_fn))
+ self.exec_cmd("cp {} {}".format(tmp_fn, out_fn), 2.0)
+ print("Removing temponary file {}".format(tmp_fn))
+ self.exec_cmd("rm {}".format(tmp_fn), 2.0)
+ return True
+ else: #nsh> found
+ print("Error occured during the file upload (`{}` < `{}`)".format(out_fn, in_fn))
+ print(res)
+ return False
+
+ def wait_and_open_nsh(self):
+ print("Waiting for the device.. (Press Ctrl+C to stop)")
+ while True:
+ if os.name == 'nt':
+ port_list = ['COM32', 'COM31', 'COM30', 'COM29', 'COM28', 'COM27', 'COM26', 'COM25', 'COM24', 'COM23', 'COM22', 'COM21', 'COM20', 'COM19', 'COM18', 'COM17', 'COM16', 'COM15', 'COM14', 'COM13', 'COM12', 'COM11', 'COM10', 'COM9', 'COM8', 'COM7', 'COM6', 'COM5', 'COM4', 'COM3', 'COM2', 'COM1', 'COM0']
+ else:
+ from glob import glob
+ port_list = glob("/dev/serial/by-id/usb-3D_Robotics*") #linux
+ port_list += glob("/dev/tty.usbmodem*") #mac
+
+ for port in port_list:
+ try:
+ self.serial = serial.Serial(port, 57600, timeout=0.5)
+ except serial.SerialException:
+ #open failed, rate-limit our attempts
+ time.sleep(0.05)
+ # and loop to the next port
+ continue
+
+ # port is open, try talking to it
+ # identify the bootloader
+ if self.identify():
+ print("Found device on port {}".format(port))
+ return
+ else:
+ print("Ignoring invalid board on port {}".format(port))
+ self.close()
+ continue
+
+ def close(self):
+ if self.serial is not None:
+ self.serial.close()
+
+if __name__ == "__main__":
+ print("This is class file. It does nothing.")
+