RMI (Remote Motion Interface) is a TCP-based protocol that lets you send TP-equivalent motion instructions and administrative commands to a Fanuc controller in real time.

## Robot requirements

- **Option R912** (Remote Motion Interface) must be loaded on the controller.
- The bootstrap port is **16001** (TCP).
- Before calling `Initialize()`, the teach pendant must be **disabled** and the controller must be in **AUTO mode**.
- Do not leave the RMI_MOVE TP program selected on the teach pendant before calling `Initialize()`.

## How it works

1. **Connect** to the controller on the bootstrap port (16001). The controller assigns a working port for the session.
2. Call **`Initialize()`** to create the `RMI_MOVE` TP program and start it.
3. Send motion or non-motion instructions via **`SendTpInstruction()`**. Each call returns an `RmiInstructionResponse` you can track.
4. The client manages the 8-slot controller buffer automatically. Instructions beyond that limit are held locally and sent as soon as a slot is free.
5. When done, call **`Abort()`** or **`Disconnect()`**. Always end your session with one of those; otherwise the controller keeps RMI_MOVE selected and other TP programs cannot run.

## Quick example

**C# : Rmi**
```csharp
using UnderAutomation.Fanuc;
using UnderAutomation.Fanuc.Common;
using UnderAutomation.Fanuc.Rmi.Data;
using UnderAutomation.Fanuc.Rmi.TpInstructions;

public class Rmi
{
    static void Main()
    {
        FanucRobot robot = new FanucRobot();
        ConnectionParameters parameters = new ConnectionParameters("192.168.0.1");
        parameters.Rmi.Enable = true;
        robot.Connect(parameters);

        /**/
        // Initialize the RMI_MOVE program on the controller.
        // TP must be disabled and the controller must be in AUTO mode.
        robot.Rmi.Initialize();

        // Linear motion at 100 mm/s to a Cartesian target (tool 1, frame 0)
        var instr = new LinearMotionTpInstruction
        {
            SpeedType = RmiLinearSpeedType.MmSec,
            Speed = 100,
            TermType = RmiTerminationType.Fine,
            Target = new CartesianPositionWithUserFrame(500, 200, 300, 0, 90, 0, tool: 1, frame: 0)
        };
        RmiInstructionResponse r = robot.Rmi.SendTpInstruction(instr);

        // Wait for the controller to confirm the motion completed
        r.WaitForCompletion();

        if (r.Status == RmiInstructionStatus.Error)
            System.Console.WriteLine("Error: " + r.ErrorText);

        // Abort when done - always end the session with Abort() or Disconnect()
        robot.Rmi.Abort();

        robot.Disconnect();
        /**/
    }
}
```

**Python : Rmi**
```python
from underautomation.fanuc.fanuc_robot import FanucRobot
from underautomation.fanuc.connection_parameters import ConnectionParameters
from underautomation.fanuc.rmi.tp_instructions.linear_motion_tp_instruction import LinearMotionTpInstruction
from underautomation.fanuc.rmi.data.rmi_linear_speed_type import RmiLinearSpeedType
from underautomation.fanuc.rmi.data.rmi_termination_type import RmiTerminationType
from underautomation.fanuc.rmi.data.rmi_instruction_status import RmiInstructionStatus
from underautomation.fanuc.common.cartesian_position_with_user_frame import CartesianPositionWithUserFrame

robot = FanucRobot()
parameters = ConnectionParameters("192.168.0.1")
parameters.rmi.enable = True
robot.connect(parameters)

##
# Initialize the RMI_MOVE program on the controller.
# TP must be disabled and the controller must be in AUTO mode.
robot.rmi.initialize()

# Linear motion at 100 mm/s to a Cartesian target (tool 1, frame 0)
instr = LinearMotionTpInstruction()
instr.speed_type = RmiLinearSpeedType.MmSec
instr.speed = 100
instr.term_type = RmiTerminationType.Fine
instr.target = CartesianPositionWithUserFrame(500, 200, 300, 0, 90, 0, 1, 0)

r = robot.rmi.send_tp_instruction(instr)

# Wait for the controller to confirm the motion completed
r.wait_for_completion()

if r.status == RmiInstructionStatus.Error:
    print("Error:", r.error_text)

# Abort when done - always end the session with abort() or disconnect()
robot.rmi.abort()
##

robot.disconnect()
```

## Connection

**C# : RmiConnection**
```csharp
using UnderAutomation.Fanuc;

public class RmiConnection
{
    static void Main()
    {
        FanucRobot robot = new FanucRobot();

        /**/
        // Enable RMI and connect. The bootstrap port is 16001.
        // The controller assigns a working port automatically.
        ConnectionParameters parameters = new ConnectionParameters("192.168.0.1");
        parameters.Rmi.Enable = true;
        robot.Connect(parameters);

        System.Console.WriteLine("Connected: " + robot.Rmi.Connected);
        System.Console.WriteLine("Protocol version: " + robot.Rmi.MajorVersion + "." + robot.Rmi.MinorVersion);
        System.Console.WriteLine("Working port: " + robot.Rmi.WorkingPort);

        // Subscribe to events before sending instructions
        robot.Rmi.SystemFaultReceived += seqId =>
            System.Console.WriteLine("System fault on sequence " + seqId);

        robot.Rmi.ConnectionTerminated += () =>
            System.Console.WriteLine("Controller closed the RMI session");

        robot.Rmi.RecordedCartesianPositionReceived += pos =>
            System.Console.WriteLine("Recorded position " + pos.PositionId);

        robot.Disconnect();
        /**/
    }
}
```

**Python : RmiConnection**
```python
from underautomation.fanuc.fanuc_robot import FanucRobot
from underautomation.fanuc.connection_parameters import ConnectionParameters

robot = FanucRobot()

##
# Enable RMI and connect. The bootstrap port is 16001.
# The controller assigns a working port automatically.
parameters = ConnectionParameters("192.168.0.1")
parameters.rmi.enable = True
robot.connect(parameters)

print("Connected:", robot.rmi.connected)
print("Protocol version:", robot.rmi.major_version, ".", robot.rmi.minor_version)
print("Working port:", robot.rmi.working_port)

# Subscribe to events before sending instructions
def on_fault(seq_id):
    print("System fault on sequence", seq_id)
robot.rmi.system_fault_received += on_fault

def on_terminated():
    print("Controller closed the RMI session")
robot.rmi.connection_terminated += on_terminated

def on_pos_recorded(pos):
    print("Recorded position", pos.position_id)
robot.rmi.recorded_cartesian_position_received += on_pos_recorded

robot.disconnect()
##
```

## Initialize and status

Call `Initialize()` after connecting. Check the controller state with `GetStatus()` first if needed.

**C# : RmiInit**
```csharp
using UnderAutomation.Fanuc;
using UnderAutomation.Fanuc.Common;
using UnderAutomation.Fanuc.Rmi.Data;

public class RmiInit
{
    static void Main()
    {
        FanucRobot robot = new FanucRobot();
        ConnectionParameters parameters = new ConnectionParameters("192.168.0.1");
        parameters.Rmi.Enable = true;
        robot.Connect(parameters);

        /**/
        // Check controller state before initializing
        RmiControllerStatusResponse status = robot.Rmi.GetStatus();
        System.Console.WriteLine("Servo ready: " + status.ServoReady);
        System.Console.WriteLine("TP enabled: " + status.TPEnabled);
        System.Console.WriteLine("RMI running: " + status.RmiMotionStatus);

        // Initialize: creates and starts the RMI_MOVE TP program.
        // Will throw if TP is enabled or servos are off.
        robot.Rmi.Initialize();

        // For multi-group controllers, specify a group mask (bit N = group N+1)
        // robot.Rmi.Initialize(groupMask: 0b00000011);  // groups 1 and 2

        // Enable real-time singularity avoidance (requires MajorVersion >= 6, R792 option)
        // robot.Rmi.Initialize(rtsa: true);

        // Set palletizing motion mode (requires MajorVersion >= 7)
        // robot.Rmi.Initialize(pltzMode: RmiPltzMode.ZeroDown);

        System.Console.WriteLine("RMI initialized");

        // The controller checks sequence IDs by default.
        // AutoSetNextSequenceId() resynchronizes the counter if needed.
        robot.Rmi.AutoSetNextSequenceId();

        robot.Rmi.Abort();
        robot.Disconnect();
        /**/
    }
}
```

**Python : RmiInit**
```python
from underautomation.fanuc.fanuc_robot import FanucRobot
from underautomation.fanuc.connection_parameters import ConnectionParameters
from underautomation.fanuc.rmi.data.rmi_pltz_mode import RmiPltzMode

robot = FanucRobot()
parameters = ConnectionParameters("192.168.0.1")
parameters.rmi.enable = True
robot.connect(parameters)

##
# Check controller state before initializing
status = robot.rmi.get_status()
print("Servo ready:", status.servo_ready)
print("TP enabled: ", status.tp_enabled)
print("RMI running:", status.rmi_motion_status)

# Initialize: creates and starts the RMI_MOVE TP program.
# Will throw if TP is enabled or servos are off.
robot.rmi.initialize()

# For multi-group controllers (requires MajorVersion >= 2)
# robot.rmi.initialize(group_mask=0b00000011)  # groups 1 and 2

# Enable real-time singularity avoidance (requires MajorVersion >= 6, R792 option)
# robot.rmi.initialize(rtsa=True)

# Set palletizing motion mode (requires MajorVersion >= 7)
# robot.rmi.initialize(pltz_mode=RmiPltzMode.ZeroDown)

print("RMI initialized")

# Resynchronize the sequence ID counter if needed
robot.rmi.auto_set_next_sequence_id()

robot.rmi.abort()
##

robot.disconnect()
```

## Instruction pipeline

`SendTpInstruction()` returns an `RmiInstructionResponse` immediately. The instruction goes through several states:

| Status | Meaning |
|--------|---------|
| `LocalQueued` | Held in the client buffer, not yet sent (controller buffer full). |
| `ControllerQueued` | Sent to the controller, waiting its turn. |
| `Executing` | The robot is currently executing this instruction. |
| `Completed` | Done without error. |
| `Error` | Failed. Check `ErrorId` and `ErrorText`. |

Call `WaitForCompletion()` to block until the instruction reaches a terminal state.

## Troubleshooting

### "Connection refused by controller"

`Connect()` throws a `ConnectException` with this message. It is not a network problem: the controller answered on port 16001, then refused the RMI session. The inner exception is an `RmiException`, and its `ErrorId` gives the error code of the controller.

Check:

- Option R912 is loaded on the controller.
- No alarm is active on the teach pendant. Reset the alarms and try again.
- No other RMI session is open. A session that was not ended with `Abort()` or `Disconnect()` keeps `RMI_MOVE` selected. Abort the programs on the teach pendant (`FCTN`, `ABORT (ALL)`), then connect again.

## Try it in the demo application

Everything on this page can be tried without writing code, in the **RMI (R912)** page of the [demo application](/fanuc/documentation/demo-app).

![RMI (R912) page of the Fanuc SDK demo application](/fanuc/WinformsScreenshots/Rmi.jpg)

Its C# source is [RmiControl.cs](https://github.com/underautomation/Fanuc.NET/blob/main/UnderAutomation.Fanuc.Showcase.Forms/Components/RmiControl.cs).

## Next steps

- [Motion commands](/fanuc/documentation/rmi-motion): linear, joint, circular, spline motions and non-motion instructions.
- [Frames, I/O & status](/fanuc/documentation/rmi-frames-io): frame management, I/O, position reading, registers.

## API reference

**Members of Rmi.RmiClient**
```csharp
public class RmiClient : RmiClientBase, IDisposable {
    // Creates a new instance of the RMI client.
    public RmiClient()

    // Connect to the FANUC controller using the RMI protocol.
    public void Connect(string ip, int port = 16001, int readTimeoutMs = 2000)
}
```

**Members of Rmi.Internal.RmiClientBase**
```csharp
public abstract class RmiClientBase : IDisposable {
    // Creates a new instance of the RMI client.
    public RmiClientBase()

    // Abort the running motion program. Note that a Reset() will be called automatically if the controller is in the HOLD state.
    public void Abort()

    // Calls internally <xref href="UnderAutomation.Fanuc.Rmi.Internal.RmiClientBase.GetStatus" data-throw-if-not-resolved="false"></xref> and set <xref href="UnderAutomation.Fanuc.Rmi.Internal.RmiClientBase.LastSequenceId" data-throw-if-not-resolved="false"></xref> only if $RMI_CFG.$Chk_seqID = FALSE. It also set <xref href="UnderAutomation.Fanuc.Rmi.Internal.RmiClientBase.CheckSequenceId" data-throw-if-not-resolved="false"></xref> to $RMI_CFG.$Chk_seqID.
    public RmiControllerStatusResponse AutoSetNextSequenceId()

    // Indicates whether the controller checks for consecutive sequence IDs in motion instructions ($RMI_CFG.$Chk_seqID). Modified by <xref href="UnderAutomation.Fanuc.Rmi.Internal.RmiClientBase.AutoSetNextSequenceId" data-throw-if-not-resolved="false"></xref>.
    public bool CheckSequenceId { get; set; }

    // Removes all instructions with a terminal status (<xref href="UnderAutomation.Fanuc.Rmi.Data.RmiInstructionStatus.Completed" data-throw-if-not-resolved="false"></xref>
    // or <xref href="UnderAutomation.Fanuc.Rmi.Data.RmiInstructionStatus.Error" data-throw-if-not-resolved="false"></xref>) from the tracked instruction list.
    // Instructions that are still pending or in progress are not affected.
    public void ClearCompletedInstructions()

    // Cancels and removes all instructions that are still in the local client buffer
    // (<xref href="UnderAutomation.Fanuc.Rmi.Data.RmiInstructionStatus.LocalQueued" data-throw-if-not-resolved="false"></xref>). These instructions have not been sent
    // to the controller yet. Each cancelled instruction is marked with an error so that any
    // thread blocked on <xref href="UnderAutomation.Fanuc.Rmi.Data.RmiInstructionResponse.WaitForCompletion(System.Int32)" data-throw-if-not-resolved="false"></xref> is unblocked.
    // Instructions already sent to the controller are not affected.
    public void ClearLocalQueuedInstructions()

    // Connect to the controller: perform the bootstrap handshake on the given port,
    // obtain the working port, then switch to it for all subsequent commands.
    protected void ConnectInternal(string host, int port, int readTimeoutMs = 2000)

    // Indicates that the client is currently connected to the controller working port.
    public bool Connected { get; }

    // Fired when the controller closes the session (e.g. communication idle timeout).
    // The client is automatically disconnected after this event fires.
    public event Action ConnectionTerminated

    // Resume a paused motion program.
    public void Continue()

    // Disconnect from the controller by sending the disconnect command on the working port.
    // Safe to call even when already disconnected.
    public void Disconnect()

    // Disconnect from the controller and release resources.
    public void Dispose()

    // Get extended controller status including drive power state and speed clamp.
    public RmiExtendedControllerStatusResponse GetExtendedStatus()

    // Get the current controller and RMI motion status.
    public RmiControllerStatusResponse GetStatus()

    // Get the current UFRAME and UTOOL numbers.
    public RmiUFrameUToolNumbersResponse GetUFrameUTool(byte? group = null)

    // Initialize RMI and start the motion program. Must be called before sending any motion instructions.
    // It also Resets <xref href="UnderAutomation.Fanuc.Rmi.Internal.RmiClientBase.LastSequenceId" data-throw-if-not-resolved="false"></xref> and empty the instruction buffer <xref href="UnderAutomation.Fanuc.Rmi.Internal.RmiClientBase.Instructions" data-throw-if-not-resolved="false"></xref>
    public void Initialize(byte? groupMask = null, bool? rtsa = null, RmiPltzMode? pltzMode = null)

    // All instructions submitted since the last <xref href="UnderAutomation.Fanuc.Rmi.Internal.RmiClientBase.Initialize(System.Nullable%7bSystem.Byte%7d%2cSystem.Nullable%7bSystem.Boolean%7d%2cSystem.Nullable%7bUnderAutomation.Fanuc.Rmi.Data.RmiPltzMode%7d)" data-throw-if-not-resolved="false"></xref> or explicit clear,
    // in submission order. Includes instructions in all states: <xref href="UnderAutomation.Fanuc.Rmi.Data.RmiInstructionStatus.LocalQueued" data-throw-if-not-resolved="false"></xref>,
    // <xref href="UnderAutomation.Fanuc.Rmi.Data.RmiInstructionStatus.ControllerQueued" data-throw-if-not-resolved="false"></xref>, <xref href="UnderAutomation.Fanuc.Rmi.Data.RmiInstructionStatus.Executing" data-throw-if-not-resolved="false"></xref>,
    // <xref href="UnderAutomation.Fanuc.Rmi.Data.RmiInstructionStatus.Completed" data-throw-if-not-resolved="false"></xref> and <xref href="UnderAutomation.Fanuc.Rmi.Data.RmiInstructionStatus.Error" data-throw-if-not-resolved="false"></xref>.
    // Returns a snapshot array; the array is not updated after it is returned.
    public RmiInstructionResponse[] Instructions { get; }

    // Indicates that the controller has entered the HOLD state and will not accept new TP instructions
    // until <xref href="UnderAutomation.Fanuc.Rmi.Internal.RmiClientBase.Reset" data-throw-if-not-resolved="false"></xref> is called.
    // 
    // <p>
    // The HOLD state is entered in two situations:
    // </p>
    // <ul><li>
    // An invalid sequence ID was detected (error RMIT-029, error code 2556957). RMI checks that sequence IDs are
    // consecutive. If a gap is found, RMI rejects the instruction and enters HOLD. The controller continues
    // executing the TP instructions already queued but blocks all new ones. Use <xref href="UnderAutomation.Fanuc.Rmi.Internal.RmiClientBase.AutoSetNextSequenceId" data-throw-if-not-resolved="false"></xref>
    // to recover the correct sequence ID, then call <xref href="UnderAutomation.Fanuc.Rmi.Internal.RmiClientBase.Reset" data-throw-if-not-resolved="false"></xref> before resuming.
    // </li><li>
    // An invalid motion instruction was received (error RMIT-024, error code 2556952), for example a motion
    // option that is not loaded on the controller. RMI returns an error for that instruction, puts the
    // controller in HOLD, and continues executing any instructions already in the TP program queue. Call
    // <xref href="UnderAutomation.Fanuc.Rmi.Internal.RmiClientBase.Reset" data-throw-if-not-resolved="false"></xref> once the problem is corrected, then resume sending instructions.
    // </li></ul>
    // <p>
    // All instructions sent while in the HOLD state are ignored by the controller and returned with an error code.
    // This flag is cleared automatically when <xref href="UnderAutomation.Fanuc.Rmi.Internal.RmiClientBase.Reset" data-throw-if-not-resolved="false"></xref> succeeds.
    // </p>
    public bool IsInHoldState { get; }

    // Sequence ID used for the last instruction sent to the controller. Reset to 0 by <xref href="UnderAutomation.Fanuc.Rmi.Internal.RmiClientBase.Initialize(System.Nullable%7bSystem.Byte%7d%2cSystem.Nullable%7bSystem.Boolean%7d%2cSystem.Nullable%7bUnderAutomation.Fanuc.Rmi.Data.RmiPltzMode%7d)" data-throw-if-not-resolved="false"></xref>.. Modified by <xref href="UnderAutomation.Fanuc.Rmi.Internal.RmiClientBase.AutoSetNextSequenceId" data-throw-if-not-resolved="false"></xref>.
    public int LastSequenceId { get; }

    // Controller protocol major version reported during the connection handshake.
    public short MajorVersion { get; }

    // Controller protocol minor version reported during the connection handshake.
    public short MinorVersion { get; }

    // Pause the running motion program.
    public void Pause()

    // Read current Cartesian TCP position.
    public RmiCartesianPositionResponse ReadCartesianPosition(byte? group = null)

    // Read a digital input port value.
    public RmiDigitalInputValueResponse ReadDIN(short portNumber)

    // Read the most recent controller error text. Up to 5 consecutive errors can be requested.
    public RmiControllerErrorTextResponse ReadError(byte? count = null)

    // Read a generic IO port (DI, DO, AI, AO, GO, RO, FLAG, RI, UI, UO).
    public RmiIoPortValueResponse ReadIOPort(RmiIoPortType portType, int portNumber)

    // Read current joint angles.
    public RmiJointAnglesSampleResponse ReadJointAngles(byte? group = null)

    // Read a numeric register
    public RmiNumericRegisterValueResponse ReadNumericRegister(int number)

    // Read a position register
    public RmiPositionRegisterDataResponse ReadPositionRegister(short number, byte? group = null)

    // Read the current TCP speed in mm/s.
    public RmiTcpSpeedResponse ReadTcpSpeed()

    // RMI connection parameters used during Connect().
    public int ReadTimeoutMs { get; }

    // Read the UFRAME at the given index.
    public RmiIndexedFrameResponse ReadUFrame(byte number, byte? group = null)

    // Read the UTOOL at the given index.
    public RmiIndexedFrameResponse ReadUTool(byte number, byte? group = null)

    // Read a system variable by name (name must include the leading <code>$</code> character).
    public RmiVariableValueResponse ReadVariable(string name)

    // Fired when the controller sends a Cartesian position via the RMI Position Record menu.
    public event Action<RmiRecordedCartesianPosition> RecordedCartesianPositionReceived

    // Fired when the controller sends a joint position via the RMI Position Record menu.
    public event Action<RmiRecordedJointPosition> RecordedJointPositionReceived

    // Reset controller errors and exit the HOLD state.
    public void Reset()

    // Sends the instruction to the controller, which queues it.
    // Returns an <xref href="UnderAutomation.Fanuc.Rmi.Data.RmiInstructionResponse" data-throw-if-not-resolved="false"></xref> that tracks execution.
    public RmiInstructionResponse SendTpInstruction(RmiInstructionBase instruction)

    // Set the program speed override (1–100 %).
    public void SetOverride(byte value)

    // Define payload compensation parameters for a payload schedule.
    public void SetPayloadCompensation(byte scheduleNumber, float massKg, float cgXm, float cgYm, float cgZm, float inertiaXkgm2, float inertiaYkgm2, float inertiaZkgm2, byte? group = null)

    // Define payload compensation parameters for a payload schedule.
    public void SetPayloadCompensation(RmiSetPayloadCompensationParameters p)

    // Immediately apply a payload schedule to the active group (command, not an instruction).
    public void SetPayloadSchedule(byte scheduleNumber, byte? group = null)

    // Define payload mass, center of gravity, and optionally inertia for a payload schedule.
    public void SetPayloadValue(byte scheduleNumber, float massKg, float cgXm, float cgYm, float cgZm, float? inertiaXkgm2 = null, float? inertiaYkgm2 = null, float? inertiaZkgm2 = null, byte? group = null)

    // Define payload mass, center of gravity, and optionally inertia for a payload schedule.
    public void SetPayloadValue(RmiSetPayloadParameters p)

    // Set the current UFRAME and UTOOL numbers.
    public void SetUFrameUTool(byte uframe, byte utool, byte? group = null)

    // Fired when the controller reports a system fault on a given sequence.
    // The argument is the SequenceID of the faulted instruction (0 when unknown).
    public event Action<int> SystemFaultReceived

    // Fired when the controller sends a response that the SDK does not know.
    public event Action<RmiResponseBase> UnknownPacketReceived

    // Working port returned by the controller; all commands use this port after connection.
    public int WorkingPort { get; }

    // Write a digital output port value.
    public void WriteDOUT(short portNumber, RmiOnOff value)

    // Write a generic IO port (AO, GO, DO, RO, FLAG).
    public void WriteIOPort(RmiIoPortType portType, int portNumber, double value)

    // Write a float value to a numeric register
    public void WriteNumericRegisterAsDouble(int number, double value)

    // Write an integer value to a numeric register
    public void WriteNumericRegisterAsInteger(int number, int value)

    // Write a Cartesian position register
    public void WritePositionRegisterCartesian(short number, CartesianPositionWithUserFrame target, byte? group = null)

    // Write the UFRAME at the given index.
    public void WriteUFrame(byte number, XYZWPRPosition position, byte? group = null)

    // Write the UTOOL at the given index.
    public void WriteUTool(byte number, XYZWPRPosition position, byte? group = null)

    // Write a float value to a system variable (name must include the leading <code>$</code>).
    public void WriteVariableAsDouble(string name, double value)

    // Write an integer value to a system variable (name must include the leading <code>$</code>).
    public void WriteVariableAsInteger(string name, int value)
}
```

**Members of Rmi.Data.RmiInstructionResponse**
```csharp
public class RmiInstructionResponse : RmiResponseBase {
    public RmiInstructionResponse()

    public override bool Equals(object obj)

    public override int GetHashCode()

    // Sent instruction
    public RmiInstructionBase Instruction { get; }

    // Sequence identifier assigned to this instruction. 0 until the instruction has been dispatched to the controller.
    public int SequenceId { get; }

    // Current execution state of the instruction.
    public RmiInstructionStatus Status { get; }

    // Fired each time <xref href="UnderAutomation.Fanuc.Rmi.Data.RmiInstructionResponse.Status" data-throw-if-not-resolved="false"></xref> changes. The argument is the new status value.
    // This event may be raised from a background thread.
    public event Action<RmiInstructionStatus> StatusChanged

    public override string ToString()

    // Blocks the calling thread until the instruction reaches a terminal state
    // (<xref href="UnderAutomation.Fanuc.Rmi.Data.RmiInstructionStatus.Completed" data-throw-if-not-resolved="false"></xref> or <xref href="UnderAutomation.Fanuc.Rmi.Data.RmiInstructionStatus.Error" data-throw-if-not-resolved="false"></xref>),
    // or until <code class="paramref">timeoutMs</code> milliseconds have elapsed.
    // Pass -1 (or omit) to wait indefinitely.
    public bool WaitForCompletion(int timeoutMs = -1)
}
```

**Members of Rmi.Data.RmiControllerStatusResponse**
```csharp
public class RmiControllerStatusResponse : RmiResponseBase {
    public RmiControllerStatusResponse()

    // Indicates the value of $RMI_CFG.$Chk_seqID, which is the configuration value that determines whether the controller checks valid incremented sequence IDs on incoming instructions.
    public bool CheckSequenceId { get; }

    public override bool Equals(object obj)

    public override int GetHashCode()

    // The next valid sequence ID. This key is only valid if the system variable $RMI_CFG.$Chk_seqID = TRUE
    public int? NextSequenceId { get; set; }

    // Number of user frames available in the robot controller
    public byte NumberUFrame { get; set; }

    // Number of user tools available in the robot controller
    public byte NumberUTool { get; set; }

    // RMI_MOVE program status
    public TaskStatus ProgramStatus { get; set; }

    // The Remote Motion Interface is running
    public bool RmiMotionStatus { get; set; }

    // The robot controller is ready for motion
    public bool ServoReady { get; set; }

    // Single step mode
    public bool SingleStepMode { get; set; }

    // The current speed override setting (1–100).
    public byte SpeedOverride { get; set; }

    // Teach Pendant Enabled (Switch on position ON) The Remote Motion interface only works when the teach pendant is disabled
    public bool TPEnabled { get; set; }

    public override string ToString()
}
```