コード例 #1
0
 def update_slots(self):
     assert self.project.target == 'v5'
     if self.port.is_valid() and bool(self.port.value):
         from pros.serial.devices.vex import V5Device
         from pros.serial.ports import DirectPort
         device = V5Device(DirectPort(self.port.value))
         slot_options = [
             f'{slot}' +
             ('' if program is None else f' (Currently: {program})')
             for slot, program in device.used_slots().items()
         ]
     else:
         slot_options = [str(i) for i in range(1, 9)]
     project_name = self.advanced_options['name'].value
     if 'slot' in self.project.upload_options:
         # first, see if the project has it specified in its upload options
         selected = slot_options[self.project.upload_options['slot'] - 1]
     else:
         # otherwise, try to do a name match
         matched_slots = [
             i for i, slot in enumerate(slot_options)
             if slot.endswith(f'{project_name})')
         ]
         if len(matched_slots) > 0:
             selected = slot_options[matched_slots[0]]
         elif 'slot' in self.advanced_options:
             # or whatever the last value was
             selected = slot_options[
                 int(self.advanced_options['slot'].value[0]) - 1]
         else:
             # or just slot 1
             selected = slot_options[0]
     self.advanced_options['slot'] = parameters.OptionParameter(
         selected, slot_options)
コード例 #2
0
ファイル: v5_utils.py プロジェクト: Skyluker4/pros-cli
def cat_metadata(file_name: str, port: str, vid: int):
    """
    Print metadata for a file
    """
    from pros.serial.devices.vex import V5Device
    from pros.serial.ports import DirectPort
    port = resolve_v5_port(port, 'system')
    if not port:
        return -1

    ser = DirectPort(port)
    device = V5Device(ser)
    print(device.get_file_metadata_by_name(file_name, vid=vid))
コード例 #3
0
ファイル: v5_utils.py プロジェクト: Skyluker4/pros-cli
def rm_file(file_name: str, port: str, vid: int, erase_all: bool):
    """
    Remove a file from the flash filesystem
    """
    from pros.serial.devices.vex import V5Device
    from pros.serial.ports import DirectPort
    port = resolve_v5_port(port, 'system')
    if not port:
        return -1

    ser = DirectPort(port)
    device = V5Device(ser)
    device.erase_file(file_name, vid=vid, erase_all=erase_all)
コード例 #4
0
def write_file(file, port: str, remote_file: str, **kwargs):
    """
    Write a file to the V5.
    """
    from pros.serial.ports import DirectPort
    from pros.serial.devices.vex import V5Device
    port = resolve_v5_port(port, 'system')
    if not port:
        return -1

    ser = DirectPort(port)
    device = V5Device(ser)
    device.write_file(file=file, remote_file=remote_file or os.path.basename(file.name), **kwargs)
コード例 #5
0
ファイル: v5_utils.py プロジェクト: Skyluker4/pros-cli
def stop(port: str):
    """
    Stops a V5 program

    If FILE is unspecified or is a directory, then attempts to find the correct filename based on the PROS project
    """
    from pros.serial.devices.vex import V5Device
    from pros.serial.ports import DirectPort
    port = resolve_v5_port(port, 'system')
    if not port:
        return -1
    ser = DirectPort(port)
    device = V5Device(ser)
    device.execute_program_file('', run=False)
コード例 #6
0
def read_file(file_name: str, port: str, vid: int, source: str):
    """
    Read file on the flash filesystem to stdout
    """
    from pros.serial.devices.vex import V5Device
    from pros.serial.ports import DirectPort
    port = resolve_v5_port(port, 'system')
    if not port:
        return -1

    ser = DirectPort(port)
    device = V5Device(ser)
    device.read_file(file=click.get_binary_stream('stdout'), remote_file=file_name,
                     vid=vid, target=source)
コード例 #7
0
ファイル: v5_utils.py プロジェクト: Skyluker4/pros-cli
def ls_files(port: str, vid: int, options: int):
    """
    List files on the flash filesystem
    """
    from pros.serial.devices.vex import V5Device
    from pros.serial.ports import DirectPort
    port = resolve_v5_port(port, 'system')
    if not port:
        return -1

    ser = DirectPort(port)
    device = V5Device(ser)
    c = device.get_dir_count(vid=vid, options=options)
    for i in range(0, c):
        print(device.get_file_metadata_by_idx(i))
コード例 #8
0
ファイル: v5_utils.py プロジェクト: Skyluker4/pros-cli
def rm_file(slot: int, port: str, vid: int):
    """
    Remove a program from the flash filesystem
    """
    from pros.serial.devices.vex import V5Device
    from pros.serial.ports import DirectPort
    port = resolve_v5_port(port, 'system')
    if not port:
        return -1

    base_name = 'slot_' + str(slot)
    ser = DirectPort(port)
    device = V5Device(ser)
    device.erase_file(base_name + '.ini', vid=vid)
    device.erase_file(base_name + '.bin', vid=vid)
コード例 #9
0
ファイル: v5_utils.py プロジェクト: Skyluker4/pros-cli
def run(slot: str, port: str):
    """
    Run a V5 program
    """
    from pros.serial.devices.vex import V5Device
    from pros.serial.ports import DirectPort
    file = f'slot_{slot}.bin'
    import re
    if not re.match(r'[\w\.]{1,24}', file):
        logger(__name__).error('file must be a valid V5 filename')
        return 1
    port = resolve_v5_port(port, 'system')
    if not port:
        return -1
    ser = DirectPort(port)
    device = V5Device(ser)
    device.execute_program_file(file, run=True)
コード例 #10
0
ファイル: v5_utils.py プロジェクト: Skyluker4/pros-cli
def rm_file(file_name: str, port: str, vid: int):
    """
    Remove a file from the V5
    """
    from pros.serial.devices.vex import V5Device
    from pros.serial.ports import DirectPort
    port = resolve_v5_port(port, 'system')
    if not port:
        return -1

    ser = DirectPort(port)
    device = V5Device(ser)
    c = device.get_dir_count(vid=vid)
    if file_name != None:
        device.erase_file(file_name, vid=vid)
    else:
        print('Invalid or no filename given')
コード例 #11
0
ファイル: v5_utils.py プロジェクト: Skyluker4/pros-cli
def capture(file_name: str, port: str, force: bool = False):
    """
    Take a screen capture of the display
    """
    from pros.serial.devices.vex import V5Device
    from pros.serial.ports import DirectPort
    import png
    import os

    port = resolve_v5_port(port, 'system')
    if not port:
        return -1
    ser = DirectPort(port)
    device = V5Device(ser)
    i_data, width, height = device.capture_screen()

    if i_data is None:
        print('Failed to capture screen from connected brain.')
        return -1

    # Sanity checking and default values for filenames
    if file_name is None:
        import time
        time_s = time.strftime('%Y-%m-%d-%H%M%S')
        file_name = f'{time_s}_{width}x{height}_pros_capture.png'
    if file_name == '-':
        # Send the data to stdout to allow for piping
        print(i_data, end='')
        return

    if not file_name.endswith('.png'):
        file_name += '.png'

    if not force and os.path.exists(file_name):
        print(f'{file_name} already exists. Refusing to overwrite!')
        print(
            'Re-run this command with the --force argument to overwrite existing files.'
        )
        return -1

    with open(file_name, 'wb') as file_:
        w = png.Writer(width, height, greyscale=False)
        w.write(file_, i_data)

    print(f'Saved screen capture to {file_name}')
コード例 #12
0
ファイル: v5_utils.py プロジェクト: Skyluker4/pros-cli
def rm_all(port: str, vid: int):
    """
    Remove all user files from the V5
    """
    from pros.serial.devices.vex import V5Device
    from pros.serial.ports import DirectPort
    port = resolve_v5_port(port, 'system')
    if not port:
        return -1

    ser = DirectPort(port)
    device = V5Device(ser)
    c = device.get_dir_count(vid=vid)
    files = []
    for i in range(0, c):
        files.append(device.get_file_metadata_by_idx(i)['filename'])
    for file in files:
        device.erase_file(file, vid=vid)
コード例 #13
0
ファイル: v5_utils.py プロジェクト: Skyluker4/pros-cli
def status(port: str):
    """
    Print system information for the V5
    """
    from pros.serial.devices.vex import V5Device
    from pros.serial.ports import DirectPort
    port = resolve_v5_port(port, 'system')
    if not port:
        return -1

    ser = DirectPort(port)
    device = V5Device(ser)
    if ismachineoutput():
        print(device.status)
    else:
        print('Connected to V5 on {}'.format(port))
        print('System version:', device.status['system_version'])
        print('CPU0 F/W version:', device.status['cpu0_version'])
        print('CPU1 SDK version:', device.status['cpu1_version'])
        print('System ID: 0x{:x}'.format(device.status['system_id']))
コード例 #14
0
ファイル: upload.py プロジェクト: weini7149/pros-cli3
def upload(path: str, project: Optional[c.Project], port: str, **kwargs):
    """
    Upload a binary to a microcontroller.

    [PATH] may be a directory or file. If a directory, finds a PROS project root and uploads the binary for the correct
    target automatically. If a file, then the file is uploaded. Note that --target must be specified in this case.

    [PORT] may be any valid communication port file, such as COM1 or /dev/ttyACM0. If left blank, then a port is
    automatically detected based on the target (or as supplied by the PROS project)
    """
    import pros.serial.devices.vex as vex
    from pros.serial.ports import DirectPort
    args = []
    if path is None or os.path.isdir(path):
        if project is None:
            project_path = c.Project.find_project(path or os.getcwd())
            if project_path is None:
                raise click.UsageError(
                    'Specify a file to upload or set the cwd inside a PROS project'
                )
            project = c.Project(project_path)
        path = os.path.join(project.location, project.output)
        if project.target == 'v5' and not kwargs['name']:
            kwargs['name'] = project.name

        # apply upload_options as a template
        options = dict(**project.upload_options)
        options.update(kwargs)
        kwargs = options

        kwargs[
            'target'] = project.target  # enforce target because uploading to the wrong uC is VERY bad
        if 'program-version' in kwargs:
            kwargs['version'] = kwargs['program-version']
        if 'name' not in kwargs:
            kwargs['name'] = project.name
    if 'target' not in kwargs:
        logger(__name__).debug(
            f'Target not specified. Arguments provided: {kwargs}')
        raise click.UsageError(
            'Target not specified. specify a project (using the file argument) or target manually'
        )

    if kwargs['target'] == 'v5':
        port = resolve_v5_port(port, 'system')
    elif kwargs['target'] == 'cortex':
        port = resolve_cortex_port(port)
    else:
        logger(__name__).debug(f"Invalid target provided: {kwargs['target']}")
        logger(__name__).debug('Target should be one of ("v5" or "cortex").')
    if not port:
        return -1

    if kwargs['target'] == 'v5':
        if kwargs['name'] is None:
            kwargs['name'] = os.path.splitext(os.path.basename(path))[0]
        args.append(kwargs.pop('name').replace('@', '_'))
        kwargs['slot'] -= 1
        if kwargs['run_after'] and kwargs['run_screen']:
            kwargs['run_after'] = vex.V5Device.FTCompleteOptions.RUN_SCREEN
        elif kwargs['run_after'] and not kwargs['run_screen']:
            kwargs[
                'run_after'] = vex.V5Device.FTCompleteOptions.RUN_IMMEDIATELY
        else:
            kwargs['run_after'] = vex.V5Device.FTCompleteOptions.DONT_RUN
    elif kwargs['target'] == 'cortex':
        pass

    # print what was decided
    ui.echo('Uploading {} to {} device on {}'.format(path, kwargs['target'],
                                                     port),
            nl=False)
    if kwargs['target'] == 'v5':
        ui.echo(f' as {args[0]} to slot {kwargs["slot"] + 1}', nl=False)
    ui.echo('')

    logger(__name__).debug('Arguments: {}'.format(str(kwargs)))
    if not os.path.isfile(path) and path is not '-':
        logger(__name__).error(
            '{} is not a valid file! Make sure it exists (e.g. by building your project)'
            .format(path))
        return -1

    # Do the actual uploading!
    try:
        ser = DirectPort(port)
        device = None
        if kwargs['target'] == 'v5':
            device = vex.V5Device(ser)
        elif kwargs['target'] == 'cortex':
            device = vex.CortexDevice(ser).get_connected_device()
        with click.open_file(path, mode='rb') as pf:
            device.write_program(pf, *args, **kwargs)
    except Exception as e:
        logger(__name__).exception(e, exc_info=True)
        exit(1)

    ui.finalize('upload',
                f'Finished uploading {path} to {kwargs["target"]} on {port}')
コード例 #15
0
ファイル: terminal.py プロジェクト: BWHS-Robotics/pros-cli
def terminal(port: str, backend: str, **kwargs):
    """
    Open a terminal to a serial port

    There are two possible backends for the terminal: "share" or "solo". In "share" mode, a server/bridge is created
    so that multiple PROS processes (such as another terminal or flash command) may communicate with the device. In the
    simpler solo mode, only one PROS process may communicate with the device. The default mode is "share", but "solo"
    may be preferred when "share" doesn't perform adequately.

    Note: share backend is not yet implemented.
    """
    from pros.serial.devices.vex.v5_user_device import V5UserDevice
    from pros.serial.terminal import Terminal
    is_v5_user_joystick = False
    if port == 'default':
        project_path = c.Project.find_project(os.getcwd())
        if project_path is None:
            v5_port, is_v5_user_joystick = resolve_v5_port(None,
                                                           'user',
                                                           quiet=True)
            cortex_port = resolve_cortex_port(None, quiet=True)
            if ((v5_port is None) ^
                (cortex_port is None)) or (v5_port is not None
                                           and v5_port == cortex_port):
                port = v5_port or cortex_port
            else:
                raise click.UsageError(
                    'You must be in a PROS project directory to enable default port selecting'
                )
        else:
            project = c.Project(project_path)
            port = project.target

    if port == 'v5':
        port = None
        port, is_v5_user_joystick = resolve_v5_port(port, 'user')
    elif port == 'cortex':
        port = None
        port = resolve_cortex_port(port)
        kwargs['raw'] = True
    if not port:
        return -1

    if backend == 'share':
        raise NotImplementedError('Share backend is not yet implemented')
        # ser = SerialSharePort(port)
    elif is_v5_user_joystick:
        logger(__name__).debug("it's a v5 joystick")
        ser = V5WirelessPort(port)
    else:
        logger(__name__).debug("not a v5 joystick")
        ser = DirectPort(port)
    if kwargs.get('raw', False):
        device = devices.RawStreamDevice(ser)
    else:
        device = devices.vex.V5UserDevice(ser)
    term = Terminal(device, request_banner=kwargs.pop('request_banner', True))

    signal.signal(signal.SIGINT, term.stop)
    term.start()
    while not term.alive.is_set():
        time.sleep(0.005)
    term.join()
    logger(__name__).info('CLI Main Thread Dying')
コード例 #16
0
def upload(path: Optional[str], project: Optional[c.Project], port: str,
           **kwargs):
    """
    Upload a binary to a microcontroller.

    [PATH] may be a directory or file. If a directory, finds a PROS project root and uploads the binary for the correct
    target automatically. If a file, then the file is uploaded. Note that --target must be specified in this case.

    [PORT] may be any valid communication port file, such as COM1 or /dev/ttyACM0. If left blank, then a port is
    automatically detected based on the target (or as supplied by the PROS project)
    """
    import pros.serial.devices.vex as vex
    from pros.serial.ports import DirectPort
    if path is None or os.path.isdir(path):
        if project is None:
            project_path = c.Project.find_project(path or os.getcwd())
            if project_path is None:
                raise click.UsageError(
                    'Specify a file to upload or set the cwd inside a PROS project'
                )
            project = c.Project(project_path)
        path = os.path.join(project.location, project.output)
        if project.target == 'v5' and not kwargs['remote_name']:
            kwargs['remote_name'] = project.name

        # apply upload_options as a template
        options = dict(**project.upload_options)
        if 'slot' in options and kwargs.get('slot', None) is None:
            kwargs.pop('slot')
        elif kwargs.get('slot', None) is None:
            kwargs['slot'] = 1

        options.update(kwargs)
        kwargs = options

        kwargs[
            'target'] = project.target  # enforce target because uploading to the wrong uC is VERY bad
        if 'program-version' in kwargs:
            kwargs['version'] = kwargs['program-version']
        if 'remote_name' not in kwargs:
            kwargs['remote_name'] = project.name

    if 'target' not in kwargs or kwargs['target'] is None:
        logger(__name__).debug(
            f'Target not specified. Arguments provided: {kwargs}')
        raise click.UsageError(
            'Target not specified. specify a project (using the file argument) or target manually'
        )

    if kwargs['target'] == 'v5':
        port = resolve_v5_port(port, 'system')[0]
    elif kwargs['target'] == 'cortex':
        port = resolve_cortex_port(port)
    else:
        logger(__name__).debug(f"Invalid target provided: {kwargs['target']}")
        logger(__name__).debug('Target should be one of ("v5" or "cortex").')
    if not port:
        raise dont_send(
            click.UsageError(
                'No port provided or located. Make sure to specify --target if needed.'
            ))

    if kwargs['target'] == 'v5':
        if kwargs['remote_name'] is None:
            kwargs['remote_name'] = os.path.splitext(os.path.basename(path))[0]
        kwargs['remote_name'] = kwargs['remote_name'].replace('@', '_')
        kwargs['slot'] -= 1
        if kwargs['run_after'] and kwargs['run_screen']:
            kwargs['run_after'] = vex.V5Device.FTCompleteOptions.RUN_SCREEN
        elif kwargs['run_after'] and not kwargs['run_screen']:
            kwargs[
                'run_after'] = vex.V5Device.FTCompleteOptions.RUN_IMMEDIATELY
        else:
            kwargs['run_after'] = vex.V5Device.FTCompleteOptions.DONT_RUN
        kwargs.pop('run_screen')
    elif kwargs['target'] == 'cortex':
        pass

    logger(__name__).debug('Arguments: {}'.format(str(kwargs)))

    # Do the actual uploading!
    try:
        ser = DirectPort(port)
        device = None
        if kwargs['target'] == 'v5':
            device = vex.V5Device(ser)
        elif kwargs['target'] == 'cortex':
            device = vex.CortexDevice(ser).get_connected_device()
        if project is not None:
            device.upload_project(project, **kwargs)
        else:
            with click.open_file(path, mode='rb') as pf:
                device.write_program(pf, **kwargs)
    except Exception as e:
        logger(__name__).exception(e, exc_info=True)
        exit(1)