Compare commits
No commits in common. "d17d5341ceb48d7e4f0b7fe8260c947f30edc298" and "cedb5bfe811a490688b0798d240b4ccede646857" have entirely different histories.
d17d5341ce
...
cedb5bfe81
|
|
@ -1,26 +1,25 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import click
|
import click
|
||||||
import serial.tools.list_ports
|
|
||||||
|
|
||||||
from helpers import CliContext, Logger, EepromProgrammer
|
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,
|
@click.option('--device', '-d', default='/dev/ttyUSB0', show_default=True,
|
||||||
metavar='DEVICE', help="Set the serial device")
|
metavar='DEVICE', help="Set the serial device")
|
||||||
@click.option('--baud', '-b', default=38400, show_default=True,
|
@click.option('--baud', '-b', default=38400, show_default=True,
|
||||||
metavar='BAUDRATE', help="Set the baud rate of the serial device")
|
metavar='BAUDRATE', help="Set the baud rate of the serial device")
|
||||||
@click.option('--verbose', '-v', is_flag=True, help='Print debug output')
|
@click.option('--verbose', '-v', is_flag=True, help='Print debug output')
|
||||||
@click.pass_context
|
@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
|
# Create dependencies
|
||||||
logger = Logger(verbose=verbose)
|
logger = Logger(verbose=verbose)
|
||||||
eeprom_programmer = EepromProgrammer(logger=logger, device=device, baudrate=baud)
|
eeprom_programmer = EepromProgrammer(logger=logger, device=device, baudrate=baud)
|
||||||
|
|
||||||
|
logger.debug("Creating CLI context (device: {}, bauds: {})".format(device, baud))
|
||||||
|
|
||||||
# Create CLI context
|
# Create CLI context
|
||||||
logger.debug("Creating CLI context")
|
|
||||||
ctx.obj = CliContext(
|
ctx.obj = CliContext(
|
||||||
logger=logger,
|
logger=logger,
|
||||||
eeprom_programmer=eeprom_programmer,
|
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)
|
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
|
@click.pass_obj
|
||||||
def hello(context: CliContext) -> None:
|
def hello(context: CliContext):
|
||||||
"""
|
"""
|
||||||
Say hello. :)
|
Say hello. :)
|
||||||
|
|
||||||
|
|
@ -47,36 +46,19 @@ def hello(context: CliContext) -> None:
|
||||||
context.logger.success('yay!')
|
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
|
@click.pass_obj
|
||||||
def test(context: CliContext) -> None:
|
def test(context: CliContext):
|
||||||
"""
|
"""
|
||||||
Send an 'INIT' command to the programmer and read the answer.
|
Send an 'INIT' command to the programmer and read the answer.
|
||||||
|
|
||||||
Test command for debugging.
|
Test command for debugging.
|
||||||
"""
|
"""
|
||||||
context.eeprom_programmer.test_command()
|
context.eeprom_programmer.test()
|
||||||
|
|
||||||
|
|
||||||
@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))
|
|
||||||
|
|
||||||
|
|
||||||
|
# TODO command: list-devices -> serial.tools.list_ports
|
||||||
# TODO shell: Run an interactive shell
|
# TODO shell: Run an interactive shell
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
eepprog(max_content_width=120)
|
eepprog()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from serial import Serial
|
from serial import Serial
|
||||||
from typing import Optional, Union
|
from typing import Optional
|
||||||
|
|
||||||
from . import Logger
|
from . import Logger
|
||||||
|
|
||||||
|
|
@ -20,49 +20,33 @@ class EepromProgrammer:
|
||||||
self._device_file = device
|
self._device_file = device
|
||||||
self._baudrate = baudrate
|
self._baudrate = baudrate
|
||||||
|
|
||||||
def open(self) -> None:
|
# TODO
|
||||||
|
def open(self):
|
||||||
"""
|
"""
|
||||||
Open and setup serial port.
|
Open and setup serial port.
|
||||||
"""
|
"""
|
||||||
assert self.serial is None, 'Serial port is already opened!'
|
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)
|
self.serial = Serial(self._device_file, self._baudrate)
|
||||||
|
|
||||||
def close(self) -> None:
|
def close(self):
|
||||||
"""
|
if self.serial is None:
|
||||||
Closes the serial port.
|
self.logger.debug("Serial port is already closed")
|
||||||
"""
|
return
|
||||||
if self.serial is not None:
|
|
||||||
self.logger.debug("Closing serial port")
|
|
||||||
self.serial.close()
|
|
||||||
|
|
||||||
def write(self, data: Union[bytes, bytearray]) -> int:
|
self.logger.debug("Closing serial port")
|
||||||
"""
|
self.serial.close()
|
||||||
Writes bytes to the serial port and writes debug log.
|
|
||||||
"""
|
|
||||||
self.logger.debug('Writing: {}'.format(str(data)))
|
|
||||||
return self.serial.write(data)
|
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
def test_command(self) -> None:
|
def test(self):
|
||||||
# Open serial port
|
# Open serial port
|
||||||
if self.serial is None:
|
if self.serial is None:
|
||||||
self.open()
|
self.open()
|
||||||
|
|
||||||
# TODO where to do this? in open() or when needed?
|
|
||||||
self.serial.timeout = 1
|
|
||||||
|
|
||||||
# Write a test command
|
# Write a test command
|
||||||
self.logger.info("Sending INIT command ...")
|
self.logger.info("Sending 'INIT' ...")
|
||||||
self.write(b'INIT BINARY\n')
|
self.serial.write(b"INIT\n")
|
||||||
|
|
||||||
# Just read some stuff
|
# Just read some stuff
|
||||||
self.logger.info("Received line: ", self.serial.readline(80))
|
self.logger.info("Read 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))
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
Loading…
Reference in New Issue