Base TMC Command Design

Overview

The BaseTMCCommand provides a reusable class for implementing Telescope Management Controller (TMC) commands using the Template Method Pattern.

The Design defines a fixed execution workflow while allowing concrete commands to customize specific extension points through hook methods. Common infrastructure concerns such as command invocation, completion tracking, timeout handling, abort handling, and result evaluation are handled by the framework, allowing subclasses to focus only on command-specific business logic.

Design

BaseTMCCommand
        │
        ├── ConfigureCommand
        ├── AssignResourcesCommand
        ├── ScanCommand
        └── ReleaseResourcesCommand

                │
                ▼

         CommandExecutor

                │
                ▼

    CommandCompletionTracker

                │
                ▼

CommandContext / CommandRuntimeContext

Command Workflow

execute()
  │
  ▼
pre_process()
  │
  ▼
initialize()
  │
  ▼
prepare_command()
  │
  ▼
build_device_commands()
  │
  ▼
CommandExecutor.execute()
        │
        ├── invoke device command
        ├── command_invoked_callback() (optional)
        └── repeat for remaining devices
  │
  ▼
CommandCompletionTracker.wait_for_command_completion()
  │
  ▼
evaluate_result()
  │
  ▼
handle_exception() (only on exception)
  │
  ▼
cleanup()
  │
  ▼
call_update_task_status()
  │
  ▼
post_process()

Responsibilities

Component

Responsibility

BaseTMCCommand

Define the framework for command execution and provide extension points

CommandExecutor

Executes subsystem commands and invokes per-device callbacks

CommandCompletionTracker

Waits for command completion, abort, early termination, or timeout

CommandContext

Stores runtime state for a single command execution

CommandRuntimeContext

Provides shared runtime services and synchronization primitives

CommandCompletionContext

Encapsulates completion tracking configuration

Required Methods

Every concrete command must implement the following methods.

def build_device_commands(self):
    pass

def update_task_status(self, **kwargs):
    pass

Optional Hooks

The framework provides several hook methods that subclasses may override to customize behaviour.

Hook

Purpose

pre_process()

Execute logic before command initialization

prepare_command()

Validate input and prepare command payloads

command_invoked_callback()

Execute logic immediately after each device command invocation

is_state_complete()

Define additional state-based completion criteria

should_stop_waiting()

Stop waiting immediately when command-specific conditions are met

allowed_result_codes()

Define acceptable command result codes

evaluate_result()

Evaluate overall command outcome

handle_exception()

Customize exception mapping

post_process()

Execute logic after command completion

Completion Tracking

The framework creates a default CommandCompletionContext containing:

  • Completion condition

  • Command timeout

  • Completion checker

  • Abort checker

Subclasses may override create_completion_context() when additional completion behaviour is required.

Waiting continues until one of the following occurs:

  • Completion criteria are satisfied.

  • should_stop_waiting() requests early termination.

  • The command is aborted.

  • The command times out.

Command Invocation Callback

command_invoked_callback() is invoked immediately after a command has been successfully invoked on an individual device.

This hook executes once for each device command before the framework continues invoking commands on remaining devices.

Typical use cases include:

  • Registering device-specific event subscriptions.

  • Recording command identifiers.

  • Performing device-specific initialization.

  • Starting monitoring activities.

Result Evaluation

After completion tracking finishes, the framework evaluates the collected device results.

By default:

  • Only ResultCode.OK is considered successful.

  • Commands may override allowed_result_codes() to treat additional result codes as successful.

  • Commands may override evaluate_result() to implement command-specific result evaluation.

Command Execution Responsibilities

The framework automatically provides:

  • Command initialization

  • Command identifier generation

  • Adapter lookup

  • Command invocation

  • Completion tracking

  • Early wait termination

  • Timeout handling

  • Abort handling

  • Result evaluation

  • Exception mapping

  • Task status update

  • Cleanup

Best Practices

  • Keep validation and payload preparation inside prepare_command().

  • Use pre_process() only for work that must occur before initialization.

  • Use command_invoked_callback() for actions that must occur immediately after a device accepts a command.

  • Override is_state_complete() only when completion depends on subsystem state.

  • Override should_stop_waiting() when waiting should terminate before completion criteria are met.

  • Override allowed_result_codes() instead of evaluate_result() whenever only acceptable result codes differ.

  • Override evaluate_result() only when command-specific evaluation logic is required.

  • Keep business logic inside subclasses and infrastructure concerns inside the framework.