# Robot Reflection System

This page explains the Robot Reflection System. This is a system that gives the robot self-awareness, meaning that it detects automatically the
connected hardware components and configures the robot accordingly.

!!! Note
    The robot reflection system will not detect hardware components if the emergency stop is pressed during boot because the power cut prevents the components from being detected.

## The boot process

When the robot is powered on, the robot reflection system is run as a systemd service. It scans the connected hardware components for which plugins are installed, and configures the robot accordingly.

After the reflection process is done, the module manager is started to launch all the applications. See the [startup process](../software/startup.md) for more details.

## Interacting with the robot reflection system
### Re-run

If you want to re-run the robot reflection system without rebooting the robot, do:

```console
pal module_manager restart --restart-reflection
```

After this the robot reflection system will be executed again, detecting the connected hardware and applying the necessary configurations. After that, the module manager will be restarted to relaunch all the applications with the new configuration.

If you just want to restart the robot reflection system without restarting the module manager, do

```console
systemctl restart robot_reflection.service
```

### Retrieve hardware of last boot

The logs of the robot reflection system can be obtained by doing

```console
journalctl -u robot_reflection.service -b
```

### Disable

If you don't want the robot reflection system to run on boot, you can disable the service by running the following command:

```console
systemctl disable robot_reflection.service
```

Or you can export the following environment variable in the `/usr/bin/init-pal-env` file:

```console
export DISABLE_ROBOT_REFLECTION=1
```

### Re-enable

If you have previously disabled the robot reflection system service, you can re-enable it by running the following command:

```console
systemctl enable robot_reflection.service
```

In case you have disabled it via the environment variable, just unset it by removing the line from your `/usr/bin/init-pal-env` file or set it to 0:

```console
export DISABLE_ROBOT_REFLECTION=0
```

### Manually run

To run the robot reflection system manually do

```console
ros2 launch robot_reflection detect_hardware.py
```

with the following optional arguments:

- `verbose`: To get a more verbose output. Default is True.
- `dry_run`: To not apply changes, only simulate. Default is False.
- `plugin`: To specify a particular plugin to run. Default is to run all available plugins.

Example:

```console
ros2 launch robot_reflection detect_hardware.py verbose:=True dry_run:=True plugin:=pal_pro_gripper
```

## How to create new plugins

For integrating new hardware components into the Robot Reflection framework, you must create a custom plugin.

A plugin must be a valid Python package containing a class that inherits from the
`HardwareReflection` base class.

The framework provides two base classes in `robot_reflection.hardware_reflection`:

- `HardwareReflection`: Base class for all hardware reflection plugins.

    - `run()`: Executes detection logic.
    - `get_robot_info()`: Returns a dictionary of the detected info.
    - `get_default_robot_info()`: Returns a dictionary of default info if hardware is not detected.
    - `detect_hardware()`: Orchestrates the run/get cycle and handles errors.

- `HardwareReflectionCmd`: An extension for plugins that rely on shell commands.

    - `run_cmd(...)`: Safely execute shell commands with timeouts.

### Implementation step-by-step

1. Create a class that implements the required methods. Below is an example of a component named `dummy_component`.

        from typing import Dict
        from robot_reflection.hardware_reflection import HardwareReflectionCmd

        KEY = 'dummy_component'

        class DummyComponentReflection(HardwareReflectionCmd):
            def __init__(self):
                 super().__init__()
                 self.result = None

            def run(self) -> None:
                 # execute logic or run shell commands
                 cmd_result = self.run_cmd('echo "Dummy component detected"')
                 self.result = cmd_result.stdout.strip()

            def get_robot_info(self) -> Dict:
                 if self.result is None:
                     raise RuntimeError('run() must be called before get_robot_info()')

                 info = {}
                 # Parse result and update dictionary
                 if self.result == 'Dummy component detected':
                     info[KEY] = True
                 else:
                     info[KEY] = False
                 return info

            def get_default_robot_info(self) -> Dict:
                 if self.result is None:
                     raise RuntimeError('run() must be called before get_robot_info()')

                 info = {}
                 # Parse result and update dictionary
                 if self.result == 'Dummy component detected':
                     info[KEY] = True
                 else:
                     info[KEY] = False
                 return info


2. Register the Plugin

     To register the plugin, add an entry point in the `setup.py` file of your package:

        entry_points={
                'robot_reflection.hardware': [
                    'dummy_component = dummy_component_reflection.detect_dummy_component:DummyComponentReflection',
                ],
        }

3. Compile and source your package, and the robot reflection system will automatically detect and use your new plugin during its execution. You can test your plugin by running the robot reflection system manually  as described above, specifying your plugin name if desired.

