BB.Diagnostic (bb v0.15.0)

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.

ConcernMechanismPurpose
DiagnosticsTelemetry eventsMonitoring dashboards
Control errorsReturn valuesProgram 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

Functions

Convenience function to publish an :error diagnostic.

Creates a new diagnostic struct.

Convenience function to publish an :ok diagnostic.

Publishes a diagnostic event via telemetry.

Convenience function to publish a :stale diagnostic.

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}
)