diff --git a/client/eepprog.py b/client/eepprog.py index 2a9641f..58837d0 100755 --- a/client/eepprog.py +++ b/client/eepprog.py @@ -1,26 +1,25 @@ #!/usr/bin/env python3 import click -import serial.tools.list_ports from helpers import CliContext, Logger, EepromProgrammer -from helpers.click import CategorizedGroup -@click.group(cls=CategorizedGroup) +@click.group() @click.option('--device', '-d', default='/dev/ttyUSB0', show_default=True, metavar='DEVICE', help="Set the serial device") @click.option('--baud', '-b', default=38400, show_default=True, metavar='BAUDRATE', help="Set the baud rate of the serial device") @click.option('--verbose', '-v', is_flag=True, help='Print debug output') @click.pass_context -def eepprog(ctx: click.Context, device: str, baud: int, verbose: bool) -> None: +def eepprog(ctx: click.Context, device: str, baud: int, verbose: bool): # Create dependencies logger = Logger(verbose=verbose) eeprom_programmer = EepromProgrammer(logger=logger, device=device, baudrate=baud) + logger.debug("Creating CLI context (device: {}, bauds: {})".format(device, baud)) + # Create CLI context - logger.debug("Creating CLI context") ctx.obj = CliContext( logger=logger, eeprom_programmer=eeprom_programmer, @@ -30,9 +29,9 @@ def eepprog(ctx: click.Context, device: str, baud: int, verbose: bool) -> None: ctx.call_on_close(eeprom_programmer.close) -@eepprog.command('hello', category='Test commands', short_help='Say hello. :)') +@eepprog.command('hello', short_help='Say hello. :)') @click.pass_obj -def hello(context: CliContext) -> None: +def hello(context: CliContext): """ Say hello. :) @@ -47,36 +46,19 @@ def hello(context: CliContext) -> None: context.logger.success('yay!') -@eepprog.command('test', category='Test commands', short_help="Send INIT command to the programmer and read answer") +@eepprog.command('test', short_help="Send INIT command to the programmer and read answer") @click.pass_obj -def test(context: CliContext) -> None: +def test(context: CliContext): """ Send an 'INIT' command to the programmer and read the answer. Test command for debugging. """ - context.eeprom_programmer.test_command() - - -@eepprog.command('list-devices', category='Helper commands', short_help="List available serial ports") -@click.pass_obj -def list_devices(context: CliContext) -> None: - """ - List serial ports that are available on the system. - - Internally uses 'serial.tools.list_ports' of pySerial. - """ - context.logger.debug("Getting list of serial ports") - ports = serial.tools.list_ports.comports() - - context.logger.debug() # Print empty line - click.echo("Found {} serial ports:".format(len(ports))) - for port in ports: - click.echo('' + str(port)) - + context.eeprom_programmer.test() +# TODO command: list-devices -> serial.tools.list_ports # TODO shell: Run an interactive shell if __name__ == '__main__': - eepprog(max_content_width=120) + eepprog() diff --git a/client/helpers/EepromProgrammer.py b/client/helpers/EepromProgrammer.py index 19f55ec..d529ddc 100644 --- a/client/helpers/EepromProgrammer.py +++ b/client/helpers/EepromProgrammer.py @@ -1,5 +1,5 @@ from serial import Serial -from typing import Optional, Union +from typing import Optional from . import Logger @@ -20,49 +20,33 @@ class EepromProgrammer: self._device_file = device self._baudrate = baudrate - def open(self) -> None: + # TODO + def open(self): """ Open and setup serial port. """ assert self.serial is None, 'Serial port is already opened!' - self.logger.debug("Opening serial device '{}' with {} bauds".format(self._device_file, self._baudrate)) + self.logger.debug("Setting up serial device '{}' with baudrate {}".format(self._device_file, self._baudrate)) self.serial = Serial(self._device_file, self._baudrate) - def close(self) -> None: - """ - Closes the serial port. - """ - if self.serial is not None: - self.logger.debug("Closing serial port") - self.serial.close() + def close(self): + if self.serial is None: + self.logger.debug("Serial port is already closed") + return - def write(self, data: Union[bytes, bytearray]) -> int: - """ - Writes bytes to the serial port and writes debug log. - """ - self.logger.debug('Writing: {}'.format(str(data))) - return self.serial.write(data) + self.logger.debug("Closing serial port") + self.serial.close() # TODO - def test_command(self) -> None: + def test(self): # Open serial port if self.serial is None: self.open() - # TODO where to do this? in open() or when needed? - self.serial.timeout = 1 - # Write a test command - self.logger.info("Sending INIT command ...") - self.write(b'INIT BINARY\n') + self.logger.info("Sending 'INIT' ...") + self.serial.write(b"INIT\n") # Just read some stuff - self.logger.info("Received line: ", self.serial.readline(80)) - - # Send a READ command - self.logger.info("Sending READ command ...") - self.write(b'READ 0000:0010\n') - - while True: - self.logger.info("Received line: ", self.serial.readline(80)) + self.logger.info("Read line: ", self.serial.readline(80)) diff --git a/client/helpers/click.py b/client/helpers/click.py deleted file mode 100644 index ff38a6a..0000000 --- a/client/helpers/click.py +++ /dev/null @@ -1,69 +0,0 @@ -import click -import collections - - -class CategorizedGroup(click.Group): - """ - Click command group that categorizes subcommands in the help text by the additional 'category' attribute. - Commands that don't have a 'category' attribute are grouped under the default category "Commands" as usual. - - Example usage: - `@categorized_group.command('foobar', category='Debug commands')` - """ - default_category: str - - def __init__(self, name=None, commands=None, default_category: str = 'Commands', **attrs): - super().__init__(name, commands, **attrs) - self.commands = commands or collections.OrderedDict() - self.default_category = default_category - - def list_commands(self, ctx): - """ List commands in the order they were added to the group. """ - return self.commands - - def command(self, *args, **kwargs): - """ Extends the command decorator by setting a category attribute on the command. """ - category = kwargs.pop('category', None) - orig_decorator = super().command(*args, **kwargs) - - def decorator(f): - cmd = orig_decorator(f) - if category: - cmd.category = category - return cmd - - return decorator - - def format_commands(self, ctx, formatter): - """ - Prints commands in help messages. - This version groups them into categories and is based on the original Group.format_commands method. - """ - categorized_commands = {self.default_category: []} - - # Collect subcommands grouped by category - for subcommand in self.list_commands(ctx): - cmd = self.get_command(ctx, subcommand) - if cmd is None or cmd.hidden: - continue - - category = getattr(cmd, 'category', self.default_category) - if categorized_commands.get(category) is None: - categorized_commands[category] = [] - - categorized_commands[category].append((subcommand, cmd)) - - # Print help sections for each subcommand category - for category, subcommands in categorized_commands.items(): - if not len(subcommands): - continue - - limit = formatter.width - 6 - max(len(cmd[0]) for cmd in subcommands) - - rows = [] - for subcommand, cmd in subcommands: - help_str = cmd.get_short_help_str(limit) - rows.append((subcommand, help_str)) - - with formatter.section(category): - formatter.write_dl(rows)