CscCommander#

class lsst.ts.salobj.CscCommander(name, index=0, enable=False, exclude=None, exclude_commands=(), fields_to_ignore=('ignored', 'value'), telemetry_fields_to_not_compare=('timestamp',), telemetry_fields_compare_digits=None)#

Bases: object

Command a CSC from the command line.

Parameters:
  • name (str) – SAL component name of CSC.

  • index (int, optional) – SAL index of CSC.

  • enable (bool, optional) – Enable the CSC (when the commander starts up)? Note: amain always supplies this argument.

  • exclude (List [str] or None, optional) – Names of telemetry or event topics to not print. If None or empty then no topics are excluded.

  • exclude_commands (List [str], optional) – Names of commands to exclude.

  • fields_to_ignore (List [str], optional) – SAL topic fields names to ignore when specifying command parameters, and when printing events and telemetry.

  • telemetry_fields_to_not_compare (List [str], optional) – Telemetry field names to ignore when checking if a telemetry topic has changed enough to print the latest sample. These fields are printed.

  • telemetry_fields_compare_digits (dict [str, int]) – Dict of telemetry field name: number of digits to round the value to before comparing to see if the value has changed. Silently ignored for non-float fields and fields that appear in telemetry_fields_to_not_compare.

domain#

DDS domain.

Type:

Domain

remote#

Remote for the CSC being commanded.

Type:

Remote

help_dict#

Dict of command_name: documentation. You should add one entry for every do_command method you define. Each documentation string should start with a list of argument names (the command name will be prepended for you) optionally followed by # and a brief description. The documentation string should a single line, if practical.

Type:

dict

Notes

Warning: use with caution. Running a commander may interfere with telescope operations! If you provide a command-line script in bin/ that runs a commander, consider picking a name that is obscure and not easily confused with the script that runs the CSC.

Make a subclass of CscCommander if you want to add extra commands or provide special handling for some commands, events or telemetry. Subclasses may provide overrides as follows:

  • To override handling of a standard command (one defined in the XML): define an async do_<command_name> method. The method receives one argument: a list of str arguments. You should provide a custom handler for, or hide, any command with array parameters, because the command parser only accepts scalars.

  • To add an additional command (one not defined in the XML):

    • Define an async do_<command_name> to handle the command. The method receives one argument: a list of str arguments.

    • Add an entry to help_dict. The key is the command name and the value is a brief (preferably only one line) help string that lists the arguments first and a brief description after. Here is an example:

      self.help_dict["sine"] = \
          "start_position amplitude  # track one cycle of a sine wave"
      
  • To override handling of an event or telemetry topic: define async method evt_<event_name>_callback or tel_<event_name>_callback, respectively. It receives one argument: the message data. This can be especially useful if the default behavior is too chatty for one or more telemetry topics.

Examples

You can do a lot with just the base class. This command-line script will control the Test CSC pretty well:

import asyncio
from lsst.ts import salobj
asyncio.run(salobj.CscCommander.amain(name="Test", index=True))

However, this does not handle array-valued command parameters. See TestCscCommander for an example that handles arrays. TestCscCommander is run as follows:

import asyncio
from lsst.ts import salobj
asyncio.run(salobj.TestCscCommander.amain(index=True))

For an example with extra commands and special telemetry handling, see RotatorCommander in the ts_rotator package.

Unit Testing

To unit test a commander:

  • Set commander.testing = True. This appends all output messages to output queue commander.output_queue, a collections.deque. The messages are also printed to stdout, as usual.

  • Call commander.run_command to run a command.

  • Check the output using the output queue.

  • Clear the output queue with commander.output_queue.clear() whenever you like, e.g. before running a command whose output you want to check.

Methods Summary

add_arguments(parser)

Add arguments to the parser created by make_from_cmd_line.

add_kwargs_from_args(args, kwargs)

Add constructor keyword arguments based on parsed arguments.

amain(*, index, **kwargs)

Construct the commander and run it.

check_arguments(args, *names)

Check that the required arguments are provided.

close()

Close the commander, prior to quitting.

do_start(args)

Allow the start command to have no arguments.

event_callback(data, name)

Generic callback for events.

evt_logMessage_callback(data)

Abbreviate the log output by omitting less-interesting fields.

evt_summaryState_callback(data)

field_is_public(name)

Return True if the specified field name is public, False otherwise.

format_data(data)

Format the public fields of an event or telemetry sample, for printing.

format_dict(data)

Format a dict for printing.

format_item(key, value)

Format one event or telemetry field for printing.

get_commands_help()

Get help for each command, as a list of strings.

get_public_data(data)

Get a dict of field_name: value for public fields of a message.

get_rounded_public_data(data[, digits])

Get a dict of field_name: value for public fields of a DDS sample with float values rounded.

get_telemetry_comparison_dict(public_dict, ...)

Get a dict of field name: rounded data, for comparing new telemetry to old.

make_from_cmd_line(index, **kwargs)

Construct a SAL-related class from command line arguments.

output(msg)

Print a message to output, appending a final newline.

print_help()

Print help.

run_command(cmd)

Run the specified command string and wait for it to finish.

run_command_topic(command_name, args)

Run a command that has an associated salobj RemoteCommand topic.

start()

Start asynchonous processes.

telemetry_callback(data, name[, digits])

Default callback for telemetry topics.

Methods Documentation

classmethod add_arguments(parser)#

Add arguments to the parser created by make_from_cmd_line.

Parameters:

parser (argparse.ArgumentParser) – The argument parser.

Return type:

None

Notes

If you override this method then you should almost certainly override add_kwargs_from_args as well.

classmethod add_kwargs_from_args(args, kwargs)#

Add constructor keyword arguments based on parsed arguments.

Parameters:
  • args (argparse.namespace) – Parsed command.

  • kwargs (dict) – Keyword argument dict for the constructor. Update this based on args. The index argument will already be present if relevant.

Return type:

None

Notes

If you override this method then you should almost certainly override add_arguments as well.

async classmethod amain(*, index, **kwargs)#

Construct the commander and run it.

Parse the command line to construct the commander, then parse and execute commands until the exit is seen.

Parameters:
  • index (int, enum.IntEnum, True, or None) –

    If the CSC is indexed, do one of the following:

    • Specify True to make index a required command-line argument that accepts any nonzero index.

    • Specify an enum.IntEnum class to make index a required command-line argument that only accepts the enum values.

    • Specify a non-zero integer to use that index. This is rare; if the CSC is indexed then the user should usually be allowed to specify the index.

    If the CSC is not indexed, specify None or 0.

  • **kwargs (dict, optional) – Additional keyword arguments for your CSC’s constructor.

Return type:

None

check_arguments(args, *names)#

Check that the required arguments are provided. and return them as a keyword argument dict with cast values.

Parameters:
  • args (List [str]) – Command arguments, as strings.

  • *names (List [str or tuple]) –

    Argument name and optional cast function. Each element is either:

    • An argument name, in which case the argument is cast to a float

    • A tuple of (name, cast function), in which case the argument is cast using the cast function. The cast function takes one str argument and returns the cast value.

Return type:

dict[str, Any]

async close()#

Close the commander, prior to quitting.

Return type:

None

async do_start(args)#

Allow the start command to have no arguments.

Parameters:

args (Sequence[str])

Return type:

None

async event_callback(data, name)#

Generic callback for events.

You may provide evt_<event_name> methods to override printing of specific events.

Parameters:
  • data (BaseMsgType)

  • name (str)

Return type:

None

async evt_logMessage_callback(data)#

Abbreviate the log output by omitting less-interesting fields.

Omit traceback unless it is non-blank. Always omit filePath, functionName, lineNumber, process, and timestamp.

Parameters:

data (BaseMsgType)

Return type:

None

async evt_summaryState_callback(data)#
Parameters:

data (BaseMsgType)

Return type:

None

field_is_public(name)#

Return True if the specified field name is public, False otherwise.

Parameters:

name (str)

Return type:

bool

format_data(data)#

Format the public fields of an event or telemetry sample, for printing.

Parameters:

data (BaseMsgType)

Return type:

str

format_dict(data)#

Format a dict for printing.

Unlike format_data, this requires a dict and formats all fields.

Parameters:

data (dict[str, Any])

Return type:

str

format_item(key, value)#

Format one event or telemetry field for printing.

Parameters:
Return type:

str

get_commands_help()#

Get help for each command, as a list of strings.

End with “Other Commands:” and any commands in help_dict that are not in command_dict.

Return type:

list[str]

get_public_data(data)#

Get a dict of field_name: value for public fields of a message.

Parameters:

data (BaseMsgType) – Message.

Return type:

dict[str, Any]

get_rounded_public_data(data, digits=2)#

Get a dict of field_name: value for public fields of a DDS sample with float values rounded.

Parameters:
  • data (BaseMsgType)

  • digits (int, default: 2)

Return type:

dict[str, Any]

get_telemetry_comparison_dict(public_dict, digits)#

Get a dict of field name: rounded data, for comparing new telemetry to old.

Parameters:
  • public_data (dict [str, Any]) – Dict of field_name: value containing public fields that are to be compared.

  • digits (int) – The default number of digits to which to round float values. May be overridden for specific fields using constructor argument telemetry_fields_compare_digits.

  • public_dict (dict[str, Any])

Return type:

dict[str, Any]

classmethod make_from_cmd_line(index, **kwargs)#

Construct a SAL-related class from command line arguments.

Parameters:
  • index (int, enum.IntEnum, True, or None) –

    If the CSC is indexed, do one of the following:

    • Specify True to make index a required command-line argument that accepts any nonzero index.

    • Specify an enum.IntEnum class to make index a required command-line argument that only accepts the enum values.

    • Specify a non-zero integer to use that index. This is rare; if the CSC is indexed then the user should usually be allowed to specify the index.

    If the CSC is not indexed, specify None or 0.

  • **kwargs (dict, optional) – Additional keyword arguments for your class’s constructor.

Returns:

instance – The constructed instance.

Return type:

cls

Notes

To add additional command-line arguments, override add_arguments and add_kwargs_from_args.

output(msg)#

Print a message to output, appending a final newline.

Please call this instead of print to support unit tests.

If self.testing is True then append str to self.output_queue instead of printing it. Use this mode for unit testing a CSC commander.

Parameters:

msg (str)

Return type:

None

print_help()#

Print help.

Return type:

None

async run_command(cmd)#

Run the specified command string and wait for it to finish.

Parameters:

cmd (str) – Command string (command name and arguments). Note: does not handle the “exit” command.

Return type:

None

async run_command_topic(command_name, args)#

Run a command that has an associated salobj RemoteCommand topic.

Parameters:
  • command_name (str) – Command name, e.g. Enable

  • args (List [str]) – String arguments for the command. There must be exactly one argument per public fields.

Return type:

None

Notes

This method works for command topics that take scalar arguments. To support command topics with more exotic arguments you must provide a do_<command> method that parses the arguments and add an entry to self.help_dict.

async start()#

Start asynchonous processes.

Return type:

None

async telemetry_callback(data, name, digits=2)#

Default callback for telemetry topics.

Print the telemetry information if it has changed enough (as specified by digits).

Parameters:
  • data (type_hints.BaseMsgType) – The message data.

  • name (str) – The name to print. Typically the basic topic name with no prefix.

  • digits (int) – The default number of digits to which to round float values before comparing if fields have changed enough to be worth printing the new data. May be overridden for specific fields using constructor argument telemetry_fields_compare_digits.

Return type:

None

Notes

  • This method cannot be directly assigned as a topic callback. Use functools.partial or similar to specify name, and, if you like, digits.

  • This method can also be used for events, if you only want The event published when the data changes significantly. One good candidate is the generic clockOffset event.