Skip to content

Commit

Permalink
⚡ Add V5 upload compression (#48)
Browse files Browse the repository at this point in the history
#### Summary:
Adds a new option for uploading binaries to the V5:

```
$ prosv5 u --compress-bin
```

The default value is true (and can be switched off with `--no-compress-bin`). When compression is enabled, .bin files to be uploaded are first compressed with gzip, which is supported natively on the V5.

(there is a side change included with this PR that adds a missing letter to the name of an internally used class)

#### Motivation:
One of a series of PRs aimed at improving speed and reliability of wireless uploading.

##### References (optional):
<!-- If this PR is related to an issue or task, reference it here (e.g. closes #1) -->
Together with #46, this blocks #47 

#### Test Plan:
- [x]  run prosv5 u (targeting a v5). bin/output.compressed.bin should be created, and it should have uploaded successfully
- [x] run prosv5 u --no-compress-bin (targeting a v5). only the normal bin/output.bin should be uploaded
- [x] run prosv5 u (targeting a cortex). nothing out of the ordinary should happen. Tested both with explicit `prosv5 u --compress-bin` and `prosv5 u --no-compress-bin`
- [x] Test compression checkbox in interactive upload modal

#### Commits
* Add binary compression before uploading on V5

* Remove unused import

* don't compress ini files when uploading

* �� address review concerns

* add interactive UI option for compression
  • Loading branch information
HotelCalifornia authored Feb 12, 2019
1 parent eefbb45 commit fcd08bc
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 15 deletions.
5 changes: 4 additions & 1 deletion pros/cli/upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ def upload_cli():
cls=PROSOption, group='V5 Options', help='Open "run program" screen after uploading, instead of executing'
' program. This option may help with controller connectivity '
'reliability and prevent robots from running off tables.')
@click.option('--compress-bin/--no-compress-bin', 'compress_bin', cls=PROSOption, group='V5 Options', default=True,
help='Compress the program binary before uploading.')
@default_options
def upload(path: Optional[str], project: Optional[c.Project], port: str, **kwargs):
"""
Expand Down Expand Up @@ -94,7 +96,8 @@ def upload(path: Optional[str], project: Optional[c.Project], port: str, **kwarg
pass

# print what was decided
ui.echo('Uploading {} to {} device on {}'.format(path, kwargs['target'], port), nl=False)
compressed_label = ' (compressed) ' if kwargs['compress_bin'] else ' '
ui.echo(f"Uploading {path}{compressed_label}to {kwargs['target']} device on {port}", nl=False)
if kwargs['target'] == 'v5':
ui.echo(f' as {args[0]} to slot {kwargs["slot"] + 1}', nl=False)
ui.echo('')
Expand Down
8 changes: 4 additions & 4 deletions pros/common/ui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def progressbar(iterable: Iterable = None, length: int = None, label: str = None
fill_char: str = '#', empty_char: str = '-', bar_template: str = '%(label)s [%(bar)s] %(info)s',
info_sep: str = ' ', width: int = 36):
if ismachineoutput():
return _MachineOutputProgessBar(**locals())
return _MachineOutputProgressBar(**locals())
else:
return click.progressbar(**locals())

Expand Down Expand Up @@ -118,18 +118,18 @@ def finalize(method: str, data: Union[str, Dict, object, List[Union[str, Dict, o
echo(human_readable)


class _MachineOutputProgessBar(_click_ProgressBar):
class _MachineOutputProgressBar(_click_ProgressBar):
def __init__(self, *args, **kwargs):
global _current_notify_value
kwargs['file'] = open(os.devnull, 'w')
self.notify_value = kwargs.pop('notify_value', _current_notify_value)
super(_MachineOutputProgessBar, self).__init__(*args, **kwargs)
super(_MachineOutputProgressBar, self).__init__(*args, **kwargs)

def __del__(self):
self.file.close()

def render_progress(self):
super(_MachineOutputProgessBar, self).render_progress()
super(_MachineOutputProgressBar, self).render_progress()
obj = {'text': self.label, 'pct': self.pct}
if self.show_eta and self.eta_known and not self.finished:
obj['eta'] = self.eta
Expand Down
10 changes: 7 additions & 3 deletions pros/conductor/project/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ def apply_template(self, template: LocalTemplate, force_system: bool = False, fo
remove_empty_directories: bool = False):
"""
Applies a template to a project
:param remove_empty_directories:
:param template:
:param force_system:
:param force_user:
Expand Down Expand Up @@ -234,12 +235,15 @@ def make_scan_build(self, build_args: Tuple[str], cdb_file: Optional[Union[str,
build_args = [*build_args, f'BINDIR={td_path}']

def libscanbuild_capture(args: argparse.Namespace) -> Tuple[int, Iterable[Compilation]]:
"""
Implementation of compilation database generation.
:param args: the parsed and validated command line arguments
:return: the exit status of build process.
"""
from libscanbuild.intercept import setup_environment, run_build, exec_trace_files, parse_exec_trace, \
compilations
from libear import temporary_directory
""" Implementation of compilation database generation.
:param args: the parsed and validated command line arguments
:return: the exit status of build process. """

with temporary_directory(prefix='intercept-') as tmp_dir:
# run the build command
Expand Down
36 changes: 29 additions & 7 deletions pros/serial/devices/vex/v5_device.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import time

import gzip
import io
import re
import struct
import typing
Expand All @@ -9,6 +9,8 @@
from io import BytesIO, StringIO
from typing import *

from semantic_version import Spec

from pros.common import *
from pros.serial import bytes_to_str, decode_bytes_to_str
from pros.serial.ports import BasePort, list_all_comports
Expand Down Expand Up @@ -116,7 +118,7 @@ def generate_ini_file(self, remote_name: str = None, slot: int = 0, ini: ConfigP

def write_program(self, file: typing.BinaryIO, remote_name: str = None, ini: ConfigParser = None, slot: int = 0,
file_len: int = -1, run_after: FTCompleteOptions = FTCompleteOptions.DONT_RUN,
target: str = 'flash', quirk: int = 0, **kwargs):
target: str = 'flash', quirk: int = 0, compress_bin: bool = True, **kwargs):
remote_base = f'slot_{slot + 1}'
if target == 'ddr':
self.write_file(file, f'{remote_base}.bin', file_len=file_len, type='bin',
Expand All @@ -135,7 +137,8 @@ def write_program(self, file: typing.BinaryIO, remote_name: str = None, ini: Con

if (quirk & 0xff) == 1:
# WRITE BIN FILE
self.write_file(file, f'{remote_base}.bin', file_len=file_len, type='bin', run_after=run_after, **kwargs)
self.write_file(file, f'{remote_base}.bin', file_len=file_len, type='bin', run_after=run_after,
compress=compress_bin, **kwargs)
with BytesIO(ini_file.encode(encoding='ascii')) as ini_bin:
# WRITE INI FILE
self.write_file(ini_bin, f'{remote_base}.ini', type='ini', **kwargs)
Expand All @@ -146,7 +149,8 @@ def write_program(self, file: typing.BinaryIO, remote_name: str = None, ini: Con
# WRITE INI FILE
self.write_file(ini_bin, f'{remote_base}.ini', type='ini', **kwargs)
# WRITE BIN FILE
self.write_file(file, f'{remote_base}.bin', file_len=file_len, type='bin', run_after=run_after, **kwargs)
self.write_file(file, f'{remote_base}.bin', file_len=file_len, type='bin', run_after=run_after,
compress=compress_bin, **kwargs)
else:
raise ValueError(f'Unknown quirk option: {quirk}')

Expand All @@ -171,10 +175,25 @@ def read_file(self, file: typing.IO[bytes], remote_file: str, vid: int_str = 'us
self.ft_complete()

def write_file(self, file: typing.BinaryIO, remote_file: str, file_len: int = -1,
run_after: FTCompleteOptions = FTCompleteOptions.DONT_RUN, **kwargs):
run_after: FTCompleteOptions = FTCompleteOptions.DONT_RUN, compress: bool = False, **kwargs):
if file_len < 0:
file_len = file.seek(0, 2)
file.seek(0, 0)
if compress and self.status['system_version'] in Spec('>=1.0.5'):
buf = io.BytesIO()
with ui.progressbar(length=file_len, label='Compressing binary') as progress:
with gzip.GzipFile(fileobj=buf, mode='wb') as f:
while True:
data = file.read(16 * 1024)
if not data:
break
f.write(data)
progress.update(len(data))
file = buf
# recompute file length
file_len = file.seek(0, 2)
file.seek(0, 0)

crc32 = self.VEX_CRC32.compute(file.read(file_len))
file.seek(-file_len, 2)
addr = kwargs.get('addr', 0x03800000)
Expand All @@ -198,9 +217,12 @@ def write_file(self, file: typing.BinaryIO, remote_file: str, file_len: int = -1
progress.update(packet_size)
logger(__name__).debug('Completed {} of {} bytes'.format(i + packet_size, file_len))
logger(__name__).debug('Data transfer complete, sending ft complete')
if compress and self.status['system_version'] in Spec('>=1.0.5'):
logger(__name__).info('Closing gzip file')
file.close()
self.ft_complete(options=run_after)

def capture_screen(self) -> Tuple[List[int], int, int]:
def capture_screen(self) -> Tuple[List[List[int]], int, int]:
self.sc_init()
width, height = 512, 272
file_size = width * height * 4 # ARGB
Expand Down
5 changes: 5 additions & 0 deletions pros/serial/interactive/UploadProjectModal.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ def project_changed(self, new_project: ExistingProjectParameter):
),
'description': parameters.Parameter(
self.project.upload_options.get('description', 'Created with PROS')
),
'compress_bin': parameters.BooleanParameter(
self.project.upload_options.get('compress_bin', True)
)
}
else:
Expand All @@ -93,6 +96,7 @@ def confirm(self, *args, **kwargs):
kwargs['name'] = self.advanced_options['name'].value
kwargs['slot'] = self.advanced_options['slot'].value
kwargs['description'] = self.advanced_options['description'].value
kwargs['compress_bin'] = self.advanced_options['compress_bin'].value
self.exit()
get_current_context().invoke(upload, **kwargs)

Expand All @@ -118,5 +122,6 @@ def build(self) -> Generator[components.Component, None, None]:
components.InputBox('Program Name', self.advanced_options['name']),
components.InputBox('Slot', self.advanced_options['slot']),
components.InputBox('Description', self.advanced_options['description']),
components.Checkbox('Compress Binary', self.advanced_options['compress_bin']),
title='Advanced V5 Options',
collapsed=self.advanced_options_collapsed)

0 comments on commit fcd08bc

Please sign in to comment.