Search documentation of bb

# BB.Diagnostic (bb v0.17.0)

Copy Markdown View Source

Diagnostic reporting for monitoring and awareness.

Diagnostics are separate from control errors - they provide observability into system health without affecting control flow. Following the ROS2 model, diagnostics use four levels that indicate component health status.

## Diagnostic Levels

- `:ok` - Component operating normally
- `:warn` - Unusual condition, but operation continues
- `:error` - Component has failed or is degraded
- `:stale` - No recent updates from component (timeout/disconnect)

## Usage

Publish diagnostics via telemetry:

    BB.Diagnostic.publish(
      component: [:robot, :arm, :elbow],
      level: :warn,
      message: "Motor temperature elevated",
      values: %{temperature: 65.2, threshold: 70.0}
    )

Subscribe to diagnostics in your application:

    :telemetry.attach(
      "my-diagnostic-handler",
      [:bb, :diagnostic],
      &MyApp.handle_diagnostic/4,
      nil
    )

## Integration with bb_liveview

The `bb_liveview` package provides a diagnostic dashboard that aggregates and displays diagnostics from all robot components. It subscribes to the `[:bb, :diagnostic]` telemetry event automatically.

## Separation from Control Errors

Diagnostics are for awareness - they inform operators about system state but don't halt operations. Control errors (`BB.Error.*`) are for control flow - they're returned from functions and affect program execution.

Concern | Mechanism | Purpose
--- | --- | ---
Diagnostics | Telemetry events | Monitoring dashboards
Control errors | Return values | Program control flow

A component may publish a `:warn` diagnostic while continuing to operate, or publish an `:error` diagnostic when it has failed. The safety system (`BB.Safety`) handles critical failures independently.

## Summary

### Types

level()
t()

### Functions

error(component, message, opts \\ [])

Convenience function to publish an `:error` diagnostic.

new(opts)

Creates a new diagnostic struct.

ok(component, message, opts \\ [])

Convenience function to publish an `:ok` diagnostic.

publish(diagnostic)

Publishes a diagnostic event via telemetry.

stale(component, message, opts \\ [])

Convenience function to publish a `:stale` diagnostic.

warn(component, message, opts \\ [])

Convenience function to publish a `:warn` diagnostic.

## Types

# level()

    @type level() :: :ok | :warn | :error | :stale

# t()

    @type t() :: %BB.Diagnostic{
      component: [atom()],
      level: level(),
      message: String.t(),
      timestamp: DateTime.t(),
      values: map()
    }

## Functions

# error(component, message, opts \\ [])

    @spec error([atom()], String.t(), keyword()) :: :ok

Convenience function to publish an `:error` diagnostic.

## Examples

    BB.Diagnostic.error([:my_robot, :sensor], "Sensor disconnected",
      values: %{last_reading: ~U[2025-01-15 10:30:00Z]}
    )

# new(opts)

    @spec new(keyword()) :: t()

Creates a new diagnostic struct.

## Options

- `:component` (required) - Path to the component, e.g. `[:robot, :arm, :elbow]`
- `:level` (required) - One of `:ok`, `:warn`, `:error`, `:stale`
- `:message` (required) - Human-readable description
- `:values` - Map of diagnostic values (default: `%{}`)
- `:timestamp` - When the diagnostic was generated (default: `DateTime.utc_now/0`)

## Examples

    BB.Diagnostic.new(
      component: [:my_robot, :gripper],
      level: :ok,
      message: "Gripper operating normally"
    )

    BB.Diagnostic.new(
      component: [:my_robot, :arm, :shoulder],
      level: :warn,
      message: "Motor temperature elevated",
      values: %{temperature: 65.2, threshold: 70.0}
    )

# ok(component, message, opts \\ [])

    @spec ok([atom()], String.t(), keyword()) :: :ok

Convenience function to publish an `:ok` diagnostic.

## Examples

    BB.Diagnostic.ok([:my_robot, :arm], "Arm calibrated successfully")

    BB.Diagnostic.ok([:my_robot, :gripper], "Gripper ready",
      values: %{grip_force: 10.5}
    )

# publish(diagnostic)

    @spec publish(t() | keyword()) :: :ok

Publishes a diagnostic event via telemetry.

This is the primary way to emit diagnostics. The event is published to `[:bb, :diagnostic]` with the diagnostic struct as metadata.

Accepts either a `BB.Diagnostic` struct or keyword options (which are passed to `new/1`).

## Examples

    # With keyword options
    BB.Diagnostic.publish(
      component: [:my_robot, :battery],
      level: :warn,
      message: "Battery low",
      values: %{percentage: 15, threshold: 20}
    )

    # With struct
    diagnostic = BB.Diagnostic.new(component: [:my_robot], level: :ok, message: "OK")
    BB.Diagnostic.publish(diagnostic)

# stale(component, message, opts \\ [])

    @spec stale([atom()], String.t(), keyword()) :: :ok

Convenience function to publish a `:stale` diagnostic.

Used when a component hasn't reported recently, indicating potential disconnect or hang.

## Examples

    BB.Diagnostic.stale([:my_robot, :camera], "No frames received",
      values: %{last_frame: ~U[2025-01-15 10:25:00Z], timeout_ms: 5000}
    )

# warn(component, message, opts \\ [])

    @spec warn([atom()], String.t(), keyword()) :: :ok

Convenience function to publish a `:warn` diagnostic.

## Examples

    BB.Diagnostic.warn([:my_robot, :motor], "Temperature elevated",
      values: %{temperature: 65.0, threshold: 70.0}
    )