Skip to content

Conversation

@rootschafer
Copy link

This PR adds error handling, command flags, and shutdown state filtering to Anchor.

Breaking change: Transport::receive() now returns Result that must be handled. Shutdown filtering is feature-gated and non-breaking.

Motivation

Previously, #[klipper_command] functions couldn't return errors. Users had to either use panic! (problematic across different embedded platforms) or shared state with Mutex/Atomic (adds synchronization overhead to every main loop iteration). Returning errors from commands is more portable and matches Klipper's setjmp behavior more closely.

There was also no efficient way to filter commands during shutdown to match Klipper's firmware behavior where only certain commands are allowed.

Changes

Error Handling

Commands can return Result<(), CustomError>. Errors are wrapped in a generated KlipperCommandError enum and propagate through Transport::receive():

#[derive(Debug)]
pub enum ConfigError {
    NotFound,
}

#[klipper_command]
fn get_config() -> Result<(), ConfigError> {
    if config_not_found {
        Err(ConfigError::NotFound)
    } else {
        Ok(())
    }
}

Command Flags

Commands can be marked with flags to control behavior. Currently supports HF_IN_SHUTDOWN:

use anchor::KlipperCommandFlags;

#[klipper_command(flags = KlipperCommandFlags::HF_IN_SHUTDOWN)]
fn clear_shutdown() {
    // ...
}

Shutdown State Filtering (Feature-Gated)

When shutdown-filtering is enabled, commands without the HF_IN_SHUTDOWN flag are filtered during shutdown. Requires implementing CheckShutdown on your context type. Uses local state for zero-cost checks in the happy path.

Implementation

  • Added anchor_types crate for shared KlipperCommandFlags bitflags
  • Command struct now stores flags parsed from #[klipper_command(flags = ...)]
  • Generated KlipperCommandError enum wraps all command error types
  • Generated dispatcher filters commands based on shutdown state and flags
  • CheckShutdown trait for efficient shutdown state checking (feature-gated)

Breaking Changes

Transport::receive() now returns Result that must be handled. Shutdown filtering is feature-gated and optional.

Migration

Update your main loop to handle Transport::receive() errors:

// Before
transport.receive(&mut buffer, context);

// After
if let Err(e) = transport.receive(&mut buffer, context) {
    // Handle errors
}

To use shutdown filtering, enable the feature and implement CheckShutdown on your context type. See README.md for details.

Testing

Tested with testjig:

  • Error handling with get_config command
  • Shutdown filtering with emergency stop → firmware restart flow
  • Command flags with clear_shutdown, config_reset, get_config, etc.
  • Feature-gated compilation (works with and without feature)

Documentation

Updated README.md and inline docs with examples and usage instructions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant