Пример #1
0
def make_dir(devs, dst_dir, dry_run, recursed):
    """Creates a directory. Produces information in case of dry run.
    Isues error where necessary.
    """
    parent = os.path.split(dst_dir.rstrip(
        '/'))[0]  # Check for nonexistent parent
    parent_files = auto(devs, listdir_stat,
                        parent) if parent else True  # Relative dir
    if dry_run:
        if recursed:  # Assume success: parent not actually created yet
            qprint("Creating directory {}".format(dst_dir))
        elif parent_files is None:
            qprint("Unable to create {}".format(dst_dir))
        return True
    if not mkdir(devs, dst_dir):
        eprint("Unable to create {}".format(dst_dir))
        return False
    return True
Пример #2
0
def do_rsync(self, line):
    """rsync [-m|--mirror] [-n|--dry-run] [SRC_DIR [DST_DIR]]

       Synchronize destination directory tree to source directory tree.
    """
    db = self.boards.default
    args = self.line_to_args(line)
    sd = args.src_dst_dir
    if len(sd) > 2:
        eprint("*** More than one destination directory given")
        return
    src_dir = sd[0] if len(sd) > 0 else db.get_config('host_dir', '~/iot49')
    dst_dir = sd[1] if len(sd) > 1 else db.get_config('remote_dir', '/flash')
    src_dir = resolve_path(self.cur_dir, src_dir)
    dst_dir = resolve_path(self.cur_dir, dst_dir)
    if len(sd) < 2:
        qprint("synchronizing {} --> {}".format(src_dir, dst_dir))
    rsync(self.boards, src_dir, dst_dir,
          mirror=not args.mirror, dry_run=args.dry_run, recursed=True)
Пример #3
0
 def __init__(self, port, baudrate):
     try:
         # wait for port to come online
         for wait in range(3):
             if os.path.exists(port): break
             qprint("Waiting for port '{}' to come online".format(port))
             time.sleep(1)
         # try to connect
         for attempt in range(5):
             try:
                 self._serial = Serial(port, baudrate, inter_byte_timeout=1)
                 break
             except IOError:
                 qprint("Waiting for serial connection at '{}'".format(port))
             time.sleep(1)
         # send Control-C to put MicroPython in known state
         for attempt in range(20):
             try:
                 self._serial.write(b'\x03')
                 break
             except SerialException:
                 time.sleep(0.5)
                 qprint("Trying to talk to the MicroPython interpreter")
         self._port = port
     except AttributeError:
         raise ConnectionError("Failed connecting to board at '{}'".format(port))
     except KeyboardInterrupt:
         self._serial = None
Пример #4
0
 def _esp_flasher(self, version, **kwargs):
     """Flash firmware"""
     # flash command
     cmd = "esptool.py --port {} --baud {} {} {}".format(
         kwargs['port'],
         kwargs['baudrate'],
         kwargs['flash_options'],
         ' '.join(["0x{:x} {}".format(addr, file)
                   for addr, file in self.spec['partitions']])
     )
     try:
         with TemporaryDirectory() as dir:
             os.chdir(dir)
             # download firmware
             for p in self.spec['partitions']:
                 url = self.url + self.board + '/' + version + '/' + p[1]
                 qprint("download", url)
                 urlretrieve(url, p[1])
             # flash
             qprint("flashing ...", cmd)
             self._esptool(cmd)
     except PermissionError:
         # Windows throws error
         pass
Пример #5
0
 def erase_flash(self, port):
     qprint("erasing flash ...")
     cmd = "esptool.py --port {} erase_flash".format(port)
     self._esptool(cmd)
Пример #6
0
 def _board_characteristics(self):
     """Get device id and other updates"""
     # get unique board id
     self._id = self.remote_eval(get_unique_id, 'BOARD HAS NO ID')
     qprint("Connected to '{}' (id={}) ...".format(self.name, self.id),
            end='',
            flush=True)
     # check buffer
     self._has_buffer = self.remote_eval(test_buffer)
     qprint(" has_buffer={}".format(self._has_buffer), end='', flush=True)
     if self._serial.is_circuit_python:
         qprint()
     else:
         # get root dirs
         qprint("{} dirs=".format(self._has_buffer), end='', flush=True)
         self._root_dirs = [
             '/{}/'.format(dir) for dir in self.remote_eval(listroot)
         ]
         qprint(self._root_dirs, end='', flush=True)
         if not self.get_config('mac'):
             qprint(" mac=", end='', flush=True)
             self.set_config('mac', self.remote_eval(get_mac_address))
             qprint(self.get_config('mac'), end='', flush=True)
         # sync time
         now = time.localtime(time.time())
         qprint(" sync time ...")
         self.remote(set_time, now.tm_year, now.tm_mon, now.tm_mday,
                     now.tm_hour, now.tm_min, now.tm_sec)
     qprint()
Пример #7
0
    def __init__(self, port=None, baudrate=115200):
        self.is_circuitpy = False
        try:
            # check which ports are available
            if not port:
                for p in comports():
                    if p.vid == ADAFRUIT_VID:
                        port = p.device
                        self.is_circuitpy = True
                        break
                    elif p.vid == ESP32_VID:
                        port = p.device
                        break
                    elif p.vid:
                        qprint(f"Unknown board {p} with vid '{p.vid}' skipped")

            # did we find a valid board?
            if not port:
                eprint("No board found")
                sys.exit(1)

            # wait for port to come online
            for wait in range(3):
                if os.path.exists(port): break
                qprint("Waiting for port '{}' to come online".format(port))
                time.sleep(1)
            # try to connect
            for attempt in range(5):
                try:
                    self._serial = Serial(port,
                                          baudrate,
                                          parity='N',
                                          inter_byte_timeout=1)
                    break
                except IOError as e:
                    qprint(
                        "Waiting for serial connection at '{}'".format(port))
                    qprint(e)
                time.sleep(1)
            # send Control-C to put MicroPython in known state
            for attempt in range(20):
                try:
                    self._serial.write(b'\x03')
                    break
                except SerialException:
                    time.sleep(0.5)
                    qprint("Trying to talk to the MicroPython interpreter")
            self._port = port
            qprint(f"SerialConnection to {port} established")
        except AttributeError:
            raise ConnectionError(
                "Failed connecting to board at '{}'".format(port))
        except KeyboardInterrupt:
            self._serial = None
Пример #8
0
 def __exit__(self, exc_type, exc_val, exc_tb):
     if self._modified:
         qprint("updating '{}'".format(self._config_file))
         self.save()
Пример #9
0
 def do_EOF(self, line):
     """Type Control-D to quit."""
     qprint("Bye")
     return True
Пример #10
0
def rsync(devs, src_dir, dst_dir, mirror, dry_run, recursed):
    """Synchronizes 2 directory trees."""

    # This test is a hack to avoid errors when accessing /flash. When the
    # cache synchronisation issue is solved it should be removed
    if not isinstance(src_dir, str) or not len(src_dir):
        return

    # check that source is a directory
    sstat = auto(devs, get_stat, src_dir)
    if not is_dir(sstat):
        eprint("*** Source {} is not a directory".format(src_dir))
        return

    # create destination directory if it does not exist
    sstat = auto(devs, get_stat, dst_dir)
    if not file_exists(sstat):
        qprint("Create {} on remote".format(dst_dir))
        if not dry_run:
            if recursed and not make_dir(devs, dst_dir, dry_run, recursed):
                eprint("*** Unable to create directory", dst_dir)
    elif not is_dir(sstat):
        eprint("*** Destination {} is not a directory".format(src_dir))
        return

    # get list of src & dst files and stats
    qprint("   checking {}".format(dst_dir))
    d_src = file_dir(devs, src_dir)
    d_dst = file_dir(devs, dst_dir)

    # determine what needs to be copied or deleted
    set_dst = set(d_dst.keys())
    set_src = set(d_src.keys())
    to_add = set_src - set_dst  # Files to copy to dest
    to_del = set_dst - set_src  # To delete from dest
    to_upd = set_dst.intersection(set_src)  # In both: may need updating

    if False:
        eprint("rsync {} -> {}".format(src_dir, dst_dir))
        eprint("  sources", set_src)
        eprint("  dest   ", set_dst)
        eprint("  add    ", to_add)
        eprint("  delete ", to_del)
        eprint("  update ", to_upd)

    # add ...
    for f in to_add:
        src = os.path.join(src_dir, f)
        dst = os.path.join(dst_dir, f)
        qprint("Adding {}".format(dst))
        if is_dir(d_src[f]):
            if recursed:
                rsync(devs, src, dst, mirror, dry_run, recursed)
        else:
            if not dry_run:
                if not cp(devs, src, dst):
                    eprint("*** Unable to add {} --> {}".format(src, dst))

    # delete ...
    for f in to_del:
        if not mirror:
            break
        dst = os.path.join(dst_dir, f)
        qprint("Removing {}".format(dst))
        if not dry_run:
            res = rm(devs, dst, recursive=True, force=True)
            if not res:
                eprint("Cannot remove {}", dst)

    # update ...
    for f in to_upd:
        src = os.path.join(src_dir, f)
        dst = os.path.join(dst_dir, f)
        if is_dir(d_src[f]):
            if is_dir(d_dst[f]):
                # src and dst are directories
                if recursed:
                    rsync(devs, src, dst, mirror, dry_run, recursed)
            else:
                msg = "Source '{}' is a directory and destination " \
                      "'{}' is a file. Ignoring"
                eprint(msg.format(src, dst))
        else:
            if is_dir(d_dst[f]):
                msg = "Source '{}' is a file and destination " \
                      "'{}' is a directory. Ignoring"
                eprint(msg.format(src, dst))
            else:
                if False:
                    eprint("BEB src {} > dst {} delta={}".format(
                        stat_mtime(d_src[f]), stat_mtime(d_dst[f]),
                        stat_mtime(d_src[f]) - stat_mtime(d_dst[f])))
                if stat_size(d_src[f]) != stat_size(d_dst[f]) or \
                   stat_mtime(d_src[f]) > stat_mtime(d_dst[f]):
                    msg = "Copying {} (newer than {})"
                    qprint(msg.format(src, dst))
                    if not dry_run:
                        if not cp(devs, src, dst):
                            eprint(
                                "*** Unable to update {} --> {}".format(src, dst))
                else:
                    dprint(f, "NO update src time:", stat_mtime(d_src[f]), "dst time", stat_mtime(
                        d_dst[f]), "delta", stat_mtime(d_src[f]) - stat_mtime(d_dst[f]))
Пример #11
0
def main():
    """The main program."""

    if sys.version_info.major < 3:
        v = sys.version_info
        eprint("Shell49 requires Python 3.6 (not {}.{}.{})".format(
            v.major, v.minor, v.micro))
        return

    default_config = os.getenv('SHELL49_CONFIG_FILE') or '~/.shell49_rc.py'
    default_editor = os.getenv('SHELL49_EDITOR') or os.getenv(
        'VISUAL') or os.getenv('EDITOR') or 'vi'
    default_nocolor = 'win32' in sys.platform
    default_debug = False
    default_quiet = False

    parser = argparse.ArgumentParser(
        prog="shell49",
        usage="%(prog)s [options] [cmd]",
        description="Remote Shell for MicroPython boards.",
        epilog=("""
Environment variables:
  SHELL49_CONFIG_FILE   configuration file (Default: '{}')
  SHELL49_EDITOR        editor (Default: {})
""".format(default_config, default_editor)),
        formatter_class=argparse.RawTextHelpFormatter)
    parser.add_argument(
        "-c",
        "--config",
        dest="config",
        help="Set path of the configuration file (default: '%s')" %
        default_config,
        default=default_config)
    parser.add_argument("-e",
                        "--editor",
                        dest="editor",
                        help="Set the editor to use (default: '%s')" %
                        default_editor,
                        default=default_editor)
    parser.add_argument("-d",
                        "--debug",
                        dest="debug",
                        action="store_true",
                        help="Enable debug features (default %s)" %
                        default_debug,
                        default=default_debug)
    parser.add_argument("-n",
                        "--nocolor",
                        dest="nocolor",
                        action="store_true",
                        help="Turn off colorized output (default: %s)" %
                        default_nocolor,
                        default=default_nocolor)
    parser.add_argument("--quiet",
                        dest="quiet",
                        action="store_true",
                        help="Turn off some output (default: %s)" %
                        default_quiet,
                        default=False)
    parser.add_argument(
        "-a",
        "--no_auto_connect",
        dest="auto_connect",
        action="store_false",
        help="Do not automatically connect to board connected to serial port",
        default=True)
    parser.add_argument('-V',
                        '--version',
                        dest='version',
                        action='store_true',
                        help='Report the version and exit.',
                        default=False)
    parser.add_argument("-f",
                        "--file",
                        dest="filename",
                        help="File of commands to process (non-interactive).")
    parser.add_argument("cmd",
                        nargs=argparse.REMAINDER,
                        help="Optional command to execute and quit.")
    args = parser.parse_args(sys.argv[1:])

    debug(args.debug)
    quiet(args.quiet or args.cmd or args.filename)
    if args.nocolor: nocolor()

    dprint("config = %s" % args.config)
    dprint("editor = %s" % args.editor)
    dprint("debug = %s" % args.debug)
    dprint("quiet = %s" % args.quiet)
    dprint("nocolor = %s" % args.nocolor)
    dprint("auto_connect = %s" % args.auto_connect)
    dprint("version = %s" % __version__)
    dprint("cmd = [%s]" % ', '.join(args.cmd))

    if args.version:
        print(__version__)
        return

    cmd_line = ' '.join(args.cmd)
    if not args.filename and cmd_line == '':
        oprint(
            "Welcome to shell49 version {}. Type 'help' for information; Control-D to exit."
            .format(__version__))

    args.config = os.path.expanduser(args.config)
    args.config = os.path.normpath(args.config)

    with Config(args.config) as config:
        boards = ActiveBoards(config)

        # connect to board ...
        try:
            if args.auto_connect:
                boards.connect_serial(config.get('default', 'port'))
        except (ConnectionError, BoardError) as err:
            eprint(err)
        except KeyboardInterrupt:
            pass

        # start command shell
        attach_commands()
        if args.filename:
            with open(args.filename) as cmd_file:
                shell = Shell(boards, args.editor, stdin=cmd_file)
                shell.cmdloop('')
        else:
            if boards.num_boards() == 0:
                eprint(
                    "No MicroPython boards connected - use the connect command to add one."
                )
            shell = Shell(boards, args.editor)
            try:
                shell.cmdloop(cmd_line)
            except KeyboardInterrupt:
                qprint("Bye")
    print(printing.NO_COLOR)