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 ------------ .. code-block:: text BaseTMCCommand │ ├── ConfigureCommand ├── AssignResourcesCommand ├── ScanCommand └── ReleaseResourcesCommand │ ▼ CommandExecutor │ ▼ CommandCompletionTracker │ ▼ CommandContext / CommandRuntimeContext Command Workflow ----------------- .. code-block:: text 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. .. code-block:: python 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.