[chirp_devel] [PATCH 00/10] chirpc improvements
This series continues my efforts to improve the CLI. It starts with new versions of the two problematic patches from my last series. It then adds an option for listing the radio's settings. The next four patches clean up and extends the memory channel options, adding several new abilities: list all channels, clear a channel, and copy a channel. Finally, I have included a new README.chirpc file to provide some guidance on using this newly refurbished tool.
Zachary T Welch (9): Improve CLI download/upload (#2343) Add newline at end of console status (#2343) chirpc: add memory option group (#2343) Improve settings strigification (#2343) chirpc: add --list-settings (#2343) chirpc: add --list-mem (#2343) chirpc: make mem options more robust (#2343) chirpc: add --clear-mem option (#2343) chirpc: add --copy-mem option (#2343) README.chirpc: new file (#2343)
README.chirpc | 104 +++++++++++++++++++++++++++ chirp/chirp_common.py | 3 + chirp/settings.py | 10 ++- chirpc | 196 ++++++++++++++++++++++++++++++++++---------------- 4 files changed, 249 insertions(+), 64 deletions(-) create mode 100644 README.chirpc
# HG changeset patch # User Zachary T Welch zach@mandolincreekfarm.com # Fake Node ID 31b8fc9d4465045b03a54f061b3cfb7906eb4c9a
Add newline at end of console status (#2343)
When downloading/uploading on the console, a pretty status bar is printed on a single line. At the end of the transfer, a newline needs to be added, or subsequent output will appear on the same line as the status bar.
diff --git a/chirp/chirp_common.py b/chirp/chirp_common.py index 725612b..b42859a 100644 --- a/chirp/chirp_common.py +++ b/chirp/chirp_common.py @@ -659,7 +659,10 @@ def console_status(status): if not logger.is_visible(logging.WARN): return import sys + import os sys.stdout.write("\r%s" % status) + if status.cur == status.max: + sys.stdout.write(os.linesep)
class RadioPrompts:
Dan,
I just noticed that you did not commit this patch. As apparently it was not clear, I did change it per your initial feedback. The logic to add the newline has been moved out of the Status stringification routine and into console_status.
On 03/06/2015 01:15 AM, Zachary T Welch wrote:
# HG changeset patch # User Zachary T Welch zach@mandolincreekfarm.com # Fake Node ID 31b8fc9d4465045b03a54f061b3cfb7906eb4c9a
Add newline at end of console status (#2343)
When downloading/uploading on the console, a pretty status bar is printed on a single line. At the end of the transfer, a newline needs to be added, or subsequent output will appear on the same line as the status bar.
diff --git a/chirp/chirp_common.py b/chirp/chirp_common.py index 725612b..b42859a 100644 --- a/chirp/chirp_common.py +++ b/chirp/chirp_common.py @@ -659,7 +659,10 @@ def console_status(status): if not logger.is_visible(logging.WARN): return import sys
- import os sys.stdout.write("\r%s" % status)
- if status.cur == status.max:
sys.stdout.write(os.linesep)
class RadioPrompts:
From: Zach Welch zach@mandolincreekfarm.com
# HG changeset patch # User Zach Welch zach@mandolincreekfarm.com # Fake Node ID 2276e60c220a280955a96e2b30220f2cdaa132a2
Improve CLI download/upload (#2343)
This patch adds much-needed checks in the CLI, allowing an unwitting user to stumble their way toward a working set of options that permits downloading/uploading an image from/to a radio.
diff --git a/chirpc b/chirpc index db3b13f..4414fc0 100755 --- a/chirpc +++ b/chirpc @@ -17,6 +17,7 @@
import serial +import os import sys import argparse import logging @@ -185,7 +186,7 @@ if __name__ == "__main__": if options.mmap: rclass = directory.get_radio_by_image(options.mmap).__class__ else: - print "Must specify a radio model" + print "You must specify a radio model. See --list-radios." sys.exit(1) else: rclass = directory.get_radio(options.radio) @@ -195,6 +196,9 @@ if __name__ == "__main__": s = options.mmap else: s = options.radio + ".img" + if not os.path.exists(s): + LOG.error("Image file '%s' does not exist" % s) + sys.exit(1) else: LOG.info("opening %s at %i" % (options.serial, rclass.BAUD_RATE)) s = serial.Serial(port=options.serial, @@ -293,17 +297,33 @@ if __name__ == "__main__": print mem
if options.download_mmap: - # isinstance(radio, chirp_common.IcomMmapRadio) or fail_unsupported() - radio.sync_in() - radio.save_mmap(options.mmap) + if not issubclass(rclass, chirp_common.CloneModeRadio): + LOG.error("%s is not a clone mode radio" % options.radio) + sys.exit(1) + if not options.mmap: + LOG.error("You must specify the destination file name with --mmap") + sys.exit(1) + try: + radio.sync_in() + radio.save_mmap(options.mmap) + except Exception, e: + LOG.exception(e) + sys.exit(1)
if options.upload_mmap: - # isinstance(radio, chirp_common.IcomMmapRadio) or fail_unsupported() - radio.load_mmap(options.mmap) - if radio.sync_out(): - print "Clone successful" - else: - LOG.error("Clone failed") + if not issubclass(rclass, chirp_common.CloneModeRadio): + LOG.error("%s is not a clone mode radio" % options.radio) + sys.exit(1) + if not options.mmap: + LOG.error("You must specify the source file name with --mmap") + sys.exit(1) + try: + radio.load_mmap(options.mmap) + radio.sync_out() + print "Upload successful" + except Exception, e: + LOG.exception(e) + sys.exit(1)
if options.mmap and isinstance(radio, chirp_common.CloneModeRadio): radio.save_mmap(options.mmap)
# HG changeset patch # User Zachary T Welch zach@mandolincreekfarm.com # Fake Node ID 5ca4e798157e01c96a205c719eac80d048909a8e
chirpc: add memory option group (#2343)
This patch groups the memory/channel modification options into an argument group, making the --help text easier to digest. It also eliminates redundant arguments to the relevant add_argument calls, since those lines are being touched anyway.
diff --git a/chirpc b/chirpc index 4414fc0..3ca9811 100755 --- a/chirpc +++ b/chirpc @@ -77,71 +77,52 @@ if __name__ == "__main__": default=False, action="store_true", help="Request radio ID string") - parser.add_argument("--raw", dest="raw", - default=False, - action="store_true", + + memarg = parser.add_argument_group("Memory/Channel Options") + memarg.add_argument("--raw", action="store_true", help="Dump raw memory location")
- parser.add_argument("--get-mem", dest="get_mem", - default=False, - action="store_true", + memarg.add_argument("--get-mem", action="store_true", help="Get and print memory location") - parser.add_argument("--set-mem-name", dest="set_mem_name", - default=None, - help="Set memory name") - parser.add_argument("--set-mem-freq", dest="set_mem_freq", - type=float, - default=None, + memarg.add_argument("--set-mem-name", help="Set memory name") + memarg.add_argument("--set-mem-freq", type=float, help="Set memory frequency")
- parser.add_argument("--set-mem-tencon", dest="set_mem_tencon", - default=False, - action="store_true", + memarg.add_argument("--set-mem-tencon", action="store_true", help="Set tone encode enabled flag") - parser.add_argument("--set-mem-tencoff", dest="set_mem_tencoff", - default=False, - action="store_true", + memarg.add_argument("--set-mem-tencoff", action="store_true", help="Set tone decode disabled flag") - parser.add_argument("--set-mem-tsqlon", dest="set_mem_tsqlon", - default=False, - action="store_true", + memarg.add_argument("--set-mem-tsqlon", action="store_true", help="Set tone squelch enabled flag") - parser.add_argument("--set-mem-tsqloff", dest="set_mem_tsqloff", - default=False, - action="store_true", + memarg.add_argument("--set-mem-tsqloff", action="store_true", help="Set tone squelch disabled flag") - parser.add_argument("--set-mem-dtcson", dest="set_mem_dtcson", - default=False, - action="store_true", + memarg.add_argument("--set-mem-dtcson", action="store_true", help="Set DTCS enabled flag") - parser.add_argument("--set-mem-dtcsoff", dest="set_mem_dtcsoff", - default=False, - action="store_true", + memarg.add_argument("--set-mem-dtcsoff", action="store_true", help="Set DTCS disabled flag")
- parser.add_argument("--set-mem-tenc", dest="set_mem_tenc", + memarg.add_argument("--set-mem-tenc", type=float, action=ToneAction, nargs=1, help="Set memory encode tone") - parser.add_argument("--set-mem-tsql", dest="set_mem_tsql", + memarg.add_argument("--set-mem-tsql", type=float, action=ToneAction, nargs=1, help="Set memory squelch tone")
- parser.add_argument("--set-mem-dtcs", dest="set_mem_dtcs", + memarg.add_argument("--set-mem-dtcs", type=int, action=DTCSAction, nargs=1, help="Set memory DTCS code") - parser.add_argument("--set-mem-dtcspol", dest="set_mem_dtcspol", + memarg.add_argument("--set-mem-dtcspol", action=DTCSPolarityAction, nargs=1, help="Set memory DTCS polarity (NN, NR, RN, RR)")
- parser.add_argument("--set-mem-dup", dest="set_mem_dup", + memarg.add_argument("--set-mem-dup", help="Set memory duplex (+,-, or blank)") - parser.add_argument("--set-mem-offset", dest="set_mem_offset", - type=float, + memarg.add_argument("--set-mem-offset", type=float, help="Set memory duplex offset (in MHz)")
- parser.add_argument("--set-mem-mode", dest="set_mem_mode", - default=None, + memarg.add_argument("--set-mem-mode", help="Set mode (%s)" % ",".join(chirp_common.MODES)) + parser.add_argument("-r", "--radio", dest="radio", default=None, help="Radio model (see --list-radios)")
# HG changeset patch # User Zachary T Welch zach@mandolincreekfarm.com # Fake Node ID 7e14d2e242b745bbffc505f5c2bfd0801f4db966
Improve settings strigification (#2343)
This patch allows a RadioSettings object to be strigified. It also cleans up the string representation of RadioSettingGroup objects.
diff --git a/chirp/settings.py b/chirp/settings.py index a435d1f..55571a3 100644 --- a/chirp/settings.py +++ b/chirp/settings.py @@ -210,6 +210,10 @@ class RadioSettings(list): def __init__(self, *groups): list.__init__(self, groups)
+ def __str__(self): + items = [str(self[i]) for i in range(0, len(self))] + return "\n".join(items) +
class RadioSettingGroup(object): """A group of settings""" @@ -242,9 +246,9 @@ class RadioSettingGroup(object): self.__doc__ = doc
def __str__(self): - string = "{Settings Group %s:\n" % self._name - for element in self._elements.values(): - string += str(element) + "\n" + string = "group '%s': {\n" % self._name + for element in sorted(self._elements.values()): + string += "\t" + str(element) + "\n" string += "}" return string
# HG changeset patch # User Zachary T Welch zach@mandolincreekfarm.com # Fake Node ID 7b1fb53d8abc4bc94dd5b51341ff16644812ada0
chirpc: add --list-settings (#2343)
This patch allows the CLI to print out the radio's current settings.
diff --git a/chirpc b/chirpc index 3ca9811..dadf8f2 100755 --- a/chirpc +++ b/chirpc @@ -73,6 +73,9 @@ if __name__ == "__main__": default="mmap", help="Serial port (default: mmap)")
+ parser.add_argument("--list-settings", action="store_true", + help="List settings") + parser.add_argument("-i", "--id", dest="id", default=False, action="store_true", @@ -188,6 +191,10 @@ if __name__ == "__main__":
radio = rclass(s)
+ if options.list_settings: + print radio.get_settings() + sys.exit(0) + if options.raw: data = radio.get_raw_memory(int(args[0])) for i in data:
# HG changeset patch # User Zachary T Welch zach@mandolincreekfarm.com # Fake Node ID 93489cb7cabf1b7aa312d2df0466991087ecce47
chirpc: add --list-mem (#2343)
This patch allows the CLI to print out the entire list of memories. If --verbose is given, it displays empty slots too; otherwise, only non-empty memories are displayed.
diff --git a/chirpc b/chirpc index dadf8f2..a31a4ef 100755 --- a/chirpc +++ b/chirpc @@ -82,6 +82,9 @@ if __name__ == "__main__": help="Request radio ID string")
memarg = parser.add_argument_group("Memory/Channel Options") + memarg.add_argument("--list-mem", action="store_true", + help="List all memory locations") + memarg.add_argument("--raw", action="store_true", help="Dump raw memory location")
@@ -195,6 +198,16 @@ if __name__ == "__main__": print radio.get_settings() sys.exit(0)
+ if options.list_mem: + rf = radio.get_features() + start, end = rf.memory_bounds + for i in range(start, end + 1): + mem = radio.get_memory(i) + if mem.empty and not logger.is_visible(logging.INFO): + continue + print mem + sys.exit(0) + if options.raw: data = radio.get_raw_memory(int(args[0])) for i in data:
# HG changeset patch # User Zachary T Welch zach@mandolincreekfarm.com # Fake Node ID e4f8bf3b9beded071946d0edd5966d0aa6f4f44a
chirpc: make mem options more robust (#2343)
This patch makes the memory options more robust to a bad memory number argument and some other exceptions. It also fixes a bug that prevented setting an empty channel. Finally, it avoids overwriting the image file when performaning a memory query.
diff --git a/chirpc b/chirpc index a31a4ef..4e88dbd 100755 --- a/chirpc +++ b/chirpc @@ -66,6 +66,26 @@ class DTCSPolarityAction(argparse.Action): setattr(parser.values, option.dest, value)
+def parse_memory_number(radio, args): + if len(args) < 1: + LOG.error("You must provide an argument specifying the memory number.") + sys.exit(1) + + try: + memnum = int(args[0]) + except ValueError: + LOG.error("'%s' is not a valid memory number", args[0]) + sys.exit(1) + + rf = radio.get_features() + start, end = rf.memory_bounds + if memnum < start or memnum > end: + LOG.error("memory number must be between %d and %d (got %d)", + start, end, memnum) + sys.exit(1) + return memnum + + if __name__ == "__main__": parser = argparse.ArgumentParser() logger.add_version_argument(parser) @@ -209,12 +229,12 @@ if __name__ == "__main__": sys.exit(0)
if options.raw: - data = radio.get_raw_memory(int(args[0])) + memnum = parse_memory_number(radio, args) + data = radio.get_raw_memory(memnum) for i in data: if ord(i) > 0x7F: - print "Memory location %i (%i):\n%s" % (int(args[0]), - len(data), - util.hexprint(data)) + print "Memory location %i (%i):\n%s" % \ + (memnum, len(data), util.hexprint(data)) sys.exit(0) print data sys.exit(0) @@ -249,11 +269,17 @@ if __name__ == "__main__": options.set_mem_dtcs or options.set_mem_dup is not None or \ options.set_mem_mode or options.set_mem_dtcspol or\ options.set_mem_offset: + memnum = parse_memory_number(radio, args) try: - mem = radio.get_memory(int(args[0])) - except errors.InvalidMemoryLocation: + mem = radio.get_memory(memnum) + except errors.InvalidMemoryLocation, e: + LOG.exception(e) + sys.exit(1) + + if mem.empty: + LOG.info("creating new memory (#%d)", memnum) mem = chirp_common.Memory() - mem.number = int(args[0]) + mem.number = memnum
mem.name = options.set_mem_name or mem.name mem.freq = options.set_mem_freq or mem.freq @@ -284,11 +310,7 @@ if __name__ == "__main__": radio.set_memory(mem)
if options.get_mem: - try: - pos = int(args[0]) - except ValueError: - pos = args[0] - + pos = parse_memory_number(radio, args) try: mem = radio.get_memory(pos) except errors.InvalidMemoryLocation, e: @@ -296,6 +318,7 @@ if __name__ == "__main__": mem.number = pos
print mem + sys.exit(0)
if options.download_mmap: if not issubclass(rclass, chirp_common.CloneModeRadio):
# HG changeset patch # User Zachary T Welch zach@mandolincreekfarm.com # Fake Node ID 350eb045efef246b0f80657abe3678daf9170d66
chirpc: add --clear-mem option (#2343)
This patch gives the CLI the means of clearing a memory channel.
diff --git a/chirpc b/chirpc index 4e88dbd..c3b85d2 100755 --- a/chirpc +++ b/chirpc @@ -110,6 +110,9 @@ if __name__ == "__main__":
memarg.add_argument("--get-mem", action="store_true", help="Get and print memory location") + memarg.add_argument("--clear-mem", action="store_true", + help="Clear memory location") + memarg.add_argument("--set-mem-name", help="Set memory name") memarg.add_argument("--set-mem-freq", type=float, help="Set memory frequency") @@ -228,6 +231,19 @@ if __name__ == "__main__": print mem sys.exit(0)
+ if options.clear_mem: + memnum = parse_memory_number(radio, args) + try: + mem = radio.get_memory(memnum) + except errors.InvalidMemoryLocation, e: + LOG.exception(e) + sys.exit(1) + if mem.empty: + LOG.warn("memory %d is already empty, exiting", memnum) + sys.exit(0) + mem.empty = True + radio.set_memory(mem) + if options.raw: memnum = parse_memory_number(radio, args) data = radio.get_raw_memory(memnum)
# HG changeset patch # User Zachary T Welch zach@mandolincreekfarm.com # Fake Node ID 1f7f1fd0113a9a34596900231af30788dff31dd9
chirpc: add --copy-mem option (#2343)
This patch adds an option that permits copying a memory channel.
diff --git a/chirpc b/chirpc index c3b85d2..1a3fec4 100755 --- a/chirpc +++ b/chirpc @@ -110,6 +110,8 @@ if __name__ == "__main__":
memarg.add_argument("--get-mem", action="store_true", help="Get and print memory location") + memarg.add_argument("--copy-mem", action="store_true", + help="Copy memory location") memarg.add_argument("--clear-mem", action="store_true", help="Clear memory location")
@@ -231,6 +233,18 @@ if __name__ == "__main__": print mem sys.exit(0)
+ if options.copy_mem: + src = parse_memory_number(radio, args) + dst = parse_memory_number(radio, args[1:]) + try: + mem = radio.get_memory(src) + except errors.InvalidMemoryLocation, e: + LOG.exception(e) + sys.exit(1) + LOG.info("copying memory %d to %d", src, dst) + mem.number = dst + radio.set_memory(mem) + if options.clear_mem: memnum = parse_memory_number(radio, args) try:
# HG changeset patch # User Zachary T Welch zach@mandolincreekfarm.com # Fake Node ID 470eac0cbe23f827996176726d1f8833a9a19e7c
README.chirpc: new file (#2343)
This patch adds a new README.chirpc file, providing some basic recipies for accomplishing tasks using the renovated CLI.
diff --git a/README.chirpc b/README.chirpc new file mode 100644 index 0000000..a367fac --- /dev/null +++ b/README.chirpc @@ -0,0 +1,104 @@ +chirpc: CHIRP Command-line interface +==================================== + +CHIRP provides a CLI tool (chirpc) to interact with your radio and +memory image files. It has been designed to be used from programs or +scripts written in other languages, providing facilities for automating +queries and transformations. + + +WARNING: All modifications are made in-place, overwriting the original +file with new contents. Be sure to make a backup copy of any files +that you want unchanged. + + +======== +Cookbook +======== + +This section provides copy-and-paste recipies for accomplishing some +tasks using the CLI. + + +List Radios +----------- + +To see the list of supported <radio> names that can be passed to the +-r/--radio option: + + chirpc --list-radios + + +Download from Radio +------------------- + +To download a new image from your radio: + + chirpc -r <radio> --serial=<port> --mmap=<file> --download-mmap + +This will connect to the specified <radio> on <port>, saving the image +obtained from the radio into the specified <file>. + + +Upload to Radio +--------------- + +To upload an existing image to your radio: + + chirpc -r <radio> --serial=<port> --mmap=<file> --upload-mmap + +This will connect to the specified <radio> on <port>, loading the image +in the specified <file> onto the radio. + + +List Settings +------------- + +For radios that support settings, you can list the current settings +in a saved image: + + chirpc --mmap=<file> --list-settings + + +Show Memory Channels +-------------------- + +You can list all current memory channels in a saved image: + + chirpc --mmap=<file> --list-mem + +That command only lists the currently programmed channels. To see the +complete list (including empty channels), add '--verbose'. + +To view only a single channel, use the --get-mem option: + + chirpc --mmap=<file> --get-mem <channel> + + +Set a Memory Channel +-------------------- + + chirpc --mmap=<file> --set-mem-name=<name> ... <channel> + +See the --help text for a complete list of options that can be used +to configure the channel. Any settings that are not configured using +a command option will be left unchanged. + + +Clearing a Memory Channel +------------------------- + +You can clear a memory channel, discarding all settings: + + chirpc --mmap=<file> --clear-mem <channel> + + +Copying a Memory Channel +------------------------ + +You can copy a memory channel: + + chirpc --mmap=<file> --copy-mem <source_channel> <destination_channel> + +Note: The contents of <destination_channel> will be overwritten with +the contents from <source_channel>
participants (2)
-
Zach Welch
-
Zachary T Welch