Skip to content

Detailed Design — autoware_redundancy_switcher_interface_plugins#

1. Purpose and Background#

This document covers SimpleSwitcherAdapter, the desktop mock adapter in this package.

In production, ECU leader election is performed by dedicated switching hardware (redundancy_switcher) and communicated via UDS sockets using RedundancySwitcherAdapter. On a development machine or in CI, that hardware and its protocol are unavailable. SimpleSwitcherAdapter provides a ROS-topic-based substitute that implements the same SwitcherSignals contract without any hardware dependency.

What it replaces: RedundancySwitcherAdapter (UDS-connected hardware switcher).

What it does not simulate: Leader election algorithms, VCU handling, or any transport-level protocol. State transitions are driven exclusively by explicit ROS topic/service requests.


2. System Overview#

graph TB
  subgraph "ECU A (Main)"
    IFA["redundancy_switcher_interface\n(is_main_ecu=true)"]
    ADPA["SimpleSwitcherAdapter"]
  end

  subgraph "ECU B (Sub)"
    IFB["redundancy_switcher_interface\n(is_main_ecu=false)"]
    ADPB["SimpleSwitcherAdapter"]
  end

  subgraph "simple_switcher_node\n(single shared instance)"
    SSN["SimpleSwitcherNode\n(state machine)"]
  end

  subgraph "Operator / Test Script"
    OP["ros2 topic pub / service call"]
  end

  ADPA -- request topics --> SSN
  ADPB -- request topics --> SSN
  SSN -- status/main_ecu --> ADPA
  SSN -- status/sub_ecu --> ADPB
  OP -- manual service --> SSN
  OP -- request topics --> SSN

  IFA <--> ADPA
  IFB <--> ADPB

simple_switcher_node is a single node shared by both ECU interface nodes. Each SimpleSwitcherAdapter subscribes to the per-ECU status topics and publishes to the shared request topics.


3. File Structure#

src/
  switcher_adapter.hpp / .cpp              — SimpleSwitcherAdapter (this document)
  simple_switcher_node.hpp / .cpp          — Mock switcher node (ROS component)
  redundancy_switcher_adapter.hpp / .cpp   — RedundancySwitcherAdapter (UDS / redundancy_switcher)
  non_redundant_adapter.hpp / .cpp         — NonRedundantSwitcherAdapter (single-ECU)
  uds_types.hpp                            — ElectionRequest / ElectionStatus protocol types
  uds_receiver.hpp / uds_sender.hpp        — UDS DGRAM socket transport (header-only templates)
config/
  default.param.yaml                       — SimpleSwitcherAdapter plugin class name
  simple_switcher_node.param.yaml          — ECU IDs, publish period
  redundancy_switcher.main.param.yaml      — RedundancySwitcherAdapter, Main ECU
  redundancy_switcher.sub.param.yaml       — RedundancySwitcherAdapter, Sub ECU
  non_redundant.param.yaml                 — NonRedundantSwitcherAdapter
launch/
  simple_switcher_node.launch.xml          — Launch simple_switcher_node
docs/
  DESIGN.md                                — This document (SimpleSwitcherAdapter)

4. Topic and Service Interface#

4.1 Requests → simple_switcher_node#

Topic / Service Type Sender Effect
/system/simple_switcher/request/reset std_msgs/Empty SimpleSwitcherAdapter (via ResetCommand) or operator Reset all interruption flags; active=Main
/system/simple_switcher/request/self_interruption/main_ecu std_msgs/Empty SimpleSwitcherAdapter (via SelfInterruptionCommand, is_main_ecu=true) or operator Set main_interrupted=true
/system/simple_switcher/request/self_interruption/sub_ecu std_msgs/Empty SimpleSwitcherAdapter (via SelfInterruptionCommand, is_main_ecu=false) or operator Set sub_interrupted=true
/system/simple_switcher/input/manual_active_control_unit std_srvs/SetBool Operator (DomainID1) Manual override: true=Main, false=Sub (sets main_interrupted=true)

The manual service is not bridged in the sub-domain bridge configuration, as it is intended for operator use from DomainID1 only.

4.2 Status ← simple_switcher_node#

Topic Type Subscriber Content
/system/simple_switcher/status/active_control_unit ActiveControlUnit SimpleSwitcherAdapter Active ECU IDs
/system/simple_switcher/status/switcher_signals/main_ecu std_msgs/UInt8 SimpleSwitcherAdapter (is_main_ecu=true) Encoded SwitcherSignals for Main ECU
/system/simple_switcher/status/switcher_signals/sub_ecu std_msgs/UInt8 SimpleSwitcherAdapter (is_main_ecu=false) Encoded SwitcherSignals for Sub ECU
/system/simple_switcher/status/switcher_annotation/main_ecu std_msgs/String SimpleSwitcherAdapter (is_main_ecu=true) Human-readable state annotation
/system/simple_switcher/status/switcher_annotation/sub_ecu std_msgs/String SimpleSwitcherAdapter (is_main_ecu=false) Human-readable state annotation

switcher_signals bit encoding:

Bit Signal
bit0 is_stable
bit1 is_self_interrupted
bit2 is_faulted

5. simple_switcher_node State Machine#

5.1 State Variables#

Variable Type Initial value
main_interrupted_ bool false
sub_interrupted_ bool false
active_ids_ vector\<uint8_t> [0] (Main ECU)

5.2 Derived Signals (per ECU)#

Signal Condition
faulted main_interrupted && sub_interrupted
self_interrupted (main_interrupted \|\| sub_interrupted) && !faulted
stable !self_interrupted && !faulted

Main ECU and Sub ECU receive per-ECU signals that reflect which side is interrupted.

5.3 State Transitions#

stateDiagram-v2
  direction LR

  state "stable\nactive=[0]" as S
  state "main_interrupted\nactive=[]" as MI
  state "sub_interrupted\nactive=[]" as SI
  state "both_interrupted\n(faulted)\nactive=[]" as BI
  state "manual: sub\nmain_interrupted\nactive=[1]" as MS

  [*] --> S : startup

  S --> MI : self_interruption/main_ecu
  S --> SI : self_interruption/sub_ecu
  S --> MS : manual service (false)

  MI --> BI : self_interruption/sub_ecu
  MI --> S : reset
  MI --> MS : manual service (false)

  SI --> BI : self_interruption/main_ecu
  SI --> S : reset
  SI --> MS : manual service (false)

  BI --> S : reset
  BI --> MS : manual service (false)

  MS --> S : manual service (true) or reset
  MS --> BI : self_interruption/sub_ecu

5.4 Event Details#

Event main_interrupted_ sub_interrupted_ active_ids_ annotation
self_interruption/main_ecu true unchanged [] or [sub] if sub healthy "self_interrupted by=main ..."
self_interruption/sub_ecu unchanged true [] or [main] if main healthy "self_interrupted by=sub ..."
reset false false [main] "stable active=main (reset)"
manual service (true) false false [main] "manual override active=main"
manual service (false) true false [sub] "manual override active=sub main_fault=true"

Manual service with data=false (Sub ECU) sets main_interrupted=true. This is intentional: forcing Sub active implies Main has been declared faulted by the operator.


6. SimpleSwitcherAdapter#

The adapter bridges the interface framework and simple_switcher_node.

6.1 Inbound (status → EventGateway)#

Received topic Submitted event
status/active_control_unit SetActiveControlUnitEvent
status/switcher_signals/{main,sub}_ecu + cached annotation SetSwitcherSignalsEvent
status/switcher_annotation/{main,sub}_ecu cached in latest_annotation_ (used on next signals message)

The adapter subscribes to the per-ECU topics determined by is_main_ecu.

6.2 Autoware Ready State#

In SimpleSwitcherAdapter, the autoware_ready state is not used. Unlike RedundancySwitcherAdapter (which embeds autoware_ready in periodic ElectionRequest heartbeats), the mock switcher's simplified state machine does not need this information. UpdateAutowareReadyCommand from the interface is ignored, and no autoware_ready status topic is published by simple_switcher_node.

6.3 Outbound (CommandBus → request topics)#

Received command Published topic
ResetCommand /system/simple_switcher/request/reset
SelfInterruptionCommand (is_main_ecu=true) /system/simple_switcher/request/self_interruption/main_ecu
SelfInterruptionCommand (is_main_ecu=false) /system/simple_switcher/request/self_interruption/sub_ecu
All other commands ignored

6.4 Annotation Handling#

The annotation string and the signals value arrive on separate topics. The adapter caches the latest annotation under annotation_mutex_ and attaches it to the next SetSwitcherSignalsEvent. This means annotation and signals are slightly decoupled in time, but since both are published in sequence by simple_switcher_node::publish_status(), the lag is negligible in practice.


7. Connection Diagram (full topic graph)#

graph LR
  subgraph simple_switcher_node
    PUB_ACU["/system/simple_switcher/status/active_control_unit"]
    PUB_SIG_M["/system/simple_switcher/status/switcher_signals/main_ecu"]
    PUB_SIG_S["/system/simple_switcher/status/switcher_signals/sub_ecu"]
    PUB_ANN_M["/system/simple_switcher/status/switcher_annotation/main_ecu"]
    PUB_ANN_S["/system/simple_switcher/status/switcher_annotation/sub_ecu"]
    SUB_RST["/system/simple_switcher/request/reset"]
    SUB_INT_M["/system/simple_switcher/request/self_interruption/main_ecu"]
    SUB_INT_S["/system/simple_switcher/request/self_interruption/sub_ecu"]
    SVC_MAN["/system/simple_switcher/input/manual_active_control_unit"]
  end

  subgraph "SimpleSwitcherAdapter (Main)"
    ADPM_SIG["sub: switcher_signals/main_ecu"]
    ADPM_ANN["sub: switcher_annotation/main_ecu"]
    ADPM_ACU["sub: active_control_unit"]
    ADPM_RST["pub: request/reset"]
    ADPM_INT["pub: request/self_interruption/main_ecu"]
  end

  subgraph "SimpleSwitcherAdapter (Sub)"
    ADPS_SIG["sub: switcher_signals/sub_ecu"]
    ADPS_ANN["sub: switcher_annotation/sub_ecu"]
    ADPS_ACU["sub: active_control_unit"]
    ADPS_RST["pub: request/reset"]
    ADPS_INT["pub: request/self_interruption/sub_ecu"]
  end

  PUB_SIG_M --> ADPM_SIG
  PUB_ANN_M --> ADPM_ANN
  PUB_ACU --> ADPM_ACU
  PUB_ACU --> ADPS_ACU
  PUB_SIG_S --> ADPS_SIG
  PUB_ANN_S --> ADPS_ANN

  ADPM_RST --> SUB_RST
  ADPM_INT --> SUB_INT_M
  ADPS_RST --> SUB_RST
  ADPS_INT --> SUB_INT_S

8. Configuration Parameters#

simple_switcher_node (config/simple_switcher_node.param.yaml)#

Parameter Default Description
main_ecu_id 0 ECU ID for Main ECU
sub_ecu_id 1 ECU ID for Sub ECU
publish_period_ms 200 Periodic status publish interval (ms)

SimpleSwitcherAdapter (config/default.param.yaml)#

Parameter Value Description
switcher_plugin "autoware::redundancy_switcher::SimpleSwitcherAdapter" pluginlib class name

9. Limitations (Mock Scope)#

The following are intentionally not implemented, as this is a desktop mock:

  • Leader election algorithm
  • Hardware failure detection (all faults are manually injected)
  • VCU (Vehicle Control Unit) handling — only ECUs (IDs 0 and 1) are supported
  • DDS/UDS transport
  • Distributed consistency between physically separate machines (all nodes run on the same host)