RMI lets you send TP-equivalent motion instructions to the robot. Each call to `SendTpInstruction()` returns an `RmiInstructionResponse` that tracks the instruction through its execution lifecycle.

## Linear motion

Move the robot in a straight line to a Cartesian target:

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

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

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

        // Incremental linear motion (delta from current position)
        var inc = new LinearRelativeTpInstruction
        {
            SpeedType = RmiLinearSpeedType.MmSec,
            Speed = 50,
            TermType = RmiTerminationType.Fine,
            Target = new CartesianPositionWithUserFrame(0, 0, -50, 0, 0, 0, 1, 0)
        };
        robot.Rmi.SendTpInstruction(inc).WaitForCompletion();

        // Linear motion with joint-angle target representation
        var jrep = new LinearMotionJRepTpInstruction
        {
            SpeedType = RmiLinearSpeedType.MmSec,
            Speed = 80,
            TermType = RmiTerminationType.Fine,
            Joints = new JointsPosition(10, -20, 30, 0, 60, 0)
        };
        robot.Rmi.SendTpInstruction(jrep).WaitForCompletion();

        // CNT blending: chain motions without stopping
        var p1 = new LinearMotionTpInstruction
        {
            SpeedType = RmiLinearSpeedType.MmSec,
            Speed = 200,
            TermType = RmiTerminationType.Cnt,
            TermValue = 100,
            Target = new CartesianPositionWithUserFrame(600, 0, 400, 0, 90, 0, 1, 0)
        };
        var p2 = new LinearMotionTpInstruction
        {
            SpeedType = RmiLinearSpeedType.MmSec,
            Speed = 200,
            TermType = RmiTerminationType.Fine,  // last motion must be Fine (or NoBlend = true)
            Target = new CartesianPositionWithUserFrame(700, 0, 350, 0, 90, 0, 1, 0)
        };
        robot.Rmi.SendTpInstruction(p1);
        robot.Rmi.SendTpInstruction(p2).WaitForCompletion();
        /**/

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

**Python : RmiMotionLinear**
```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.tp_instructions.linear_relative_tp_instruction import LinearRelativeTpInstruction
from underautomation.fanuc.rmi.tp_instructions.linear_motion_j_rep_tp_instruction import LinearMotionJRepTpInstruction
from underautomation.fanuc.rmi.data.rmi_linear_speed_type import RmiLinearSpeedType
from underautomation.fanuc.rmi.data.rmi_termination_type import RmiTerminationType
from underautomation.fanuc.common.cartesian_position_with_user_frame import CartesianPositionWithUserFrame
from underautomation.fanuc.common.joints_position import JointsPosition

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

##
# Linear motion to a Cartesian target (tool 1, frame 0)
move = LinearMotionTpInstruction()
move.speed_type = RmiLinearSpeedType.MmSec
move.speed = 100
move.term_type = RmiTerminationType.Fine
move.target = CartesianPositionWithUserFrame(500, 200, 300, 0, 90, 0, 1, 0)
robot.rmi.send_tp_instruction(move).wait_for_completion()

# Incremental linear motion (delta from current position)
inc = LinearRelativeTpInstruction()
inc.speed_type = RmiLinearSpeedType.MmSec
inc.speed = 50
inc.term_type = RmiTerminationType.Fine
inc.target = CartesianPositionWithUserFrame(0, 0, -50, 0, 0, 0, 1, 0)
robot.rmi.send_tp_instruction(inc).wait_for_completion()

# Linear motion with joint-angle target representation
jrep = LinearMotionJRepTpInstruction()
jrep.speed_type = RmiLinearSpeedType.MmSec
jrep.speed = 80
jrep.term_type = RmiTerminationType.Fine
jrep.joints = JointsPosition(10, -20, 30, 0, 60, 0)
robot.rmi.send_tp_instruction(jrep).wait_for_completion()

# CNT blending: chain motions without stopping
p1 = LinearMotionTpInstruction()
p1.speed_type = RmiLinearSpeedType.MmSec
p1.speed = 200
p1.term_type = RmiTerminationType.Cnt
p1.term_value = 100
p1.target = CartesianPositionWithUserFrame(600, 0, 400, 0, 90, 0, 1, 0)

p2 = LinearMotionTpInstruction()
p2.speed_type = RmiLinearSpeedType.MmSec
p2.speed = 200
p2.term_type = RmiTerminationType.Fine  # last motion must be Fine (or no_blend = True)
p2.target = CartesianPositionWithUserFrame(700, 0, 350, 0, 90, 0, 1, 0)

robot.rmi.send_tp_instruction(p1)
robot.rmi.send_tp_instruction(p2).wait_for_completion()
##

robot.rmi.abort()
robot.disconnect()
```

### Speed types for linear motion

| `RmiLinearSpeedType` | Description |
|----------------------|-------------|
| `MmSec` | Millimeters per second. |
| `InchMin` | Inches per minute (0.1 in/min units). |
| `Time` | Duration in 0.1-second steps. |
| `MSec` | Duration in milliseconds. |

### Termination types

| `RmiTerminationType` | Description |
|----------------------|-------------|
| `Fine` | Stop precisely at the target. |
| `Cnt` | Continuous blending with the next motion (value 1-100). The last motion sent must be `Fine` unless `NoBlend = true`. |
| `Cr` | Corner region (requires Advanced Constant Path option). |

## Joint motion

Move using joint interpolation (Cartesian target or joint-angle target):

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

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

        /**/
        // Joint motion with Cartesian target, percent speed
        var cart = new JointMotionTpInstruction
        {
            SpeedType = RmiJointSpeedType.Percent,
            Speed = 10,
            TermType = RmiTerminationType.Fine,
            Target = new CartesianPositionWithUserFrame(500, 200, 300, 0, 90, 0, tool: 1, frame: 0)
        };
        robot.Rmi.SendTpInstruction(cart).WaitForCompletion();

        // Joint motion with joint-angle target representation
        var jrep = new JointMotionJRepTpInstruction
        {
            SpeedType = RmiJointSpeedType.Percent,
            Speed = 5,
            TermType = RmiTerminationType.Fine,
            Joints = new JointsPosition(10, -20, 30, 0, 60, 0)
        };
        robot.Rmi.SendTpInstruction(jrep).WaitForCompletion();

        // Incremental joint motion (joint-angle delta)
        var relJrep = new JointRelativeJRepTpInstruction
        {
            SpeedType = RmiJointSpeedType.Percent,
            Speed = 5,
            TermType = RmiTerminationType.Fine,
            Joints = new JointsPosition(0, 0, 0, 0, 5, 0)   // rotate J5 by 5 degrees
        };
        robot.Rmi.SendTpInstruction(relJrep).WaitForCompletion();
        /**/

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

**Python : RmiMotionJoint**
```python
from underautomation.fanuc.fanuc_robot import FanucRobot
from underautomation.fanuc.connection_parameters import ConnectionParameters
from underautomation.fanuc.rmi.tp_instructions.joint_motion_tp_instruction import JointMotionTpInstruction
from underautomation.fanuc.rmi.tp_instructions.joint_motion_j_rep_tp_instruction import JointMotionJRepTpInstruction
from underautomation.fanuc.rmi.tp_instructions.joint_relative_j_rep_tp_instruction import JointRelativeJRepTpInstruction
from underautomation.fanuc.rmi.data.rmi_joint_speed_type import RmiJointSpeedType
from underautomation.fanuc.rmi.data.rmi_termination_type import RmiTerminationType
from underautomation.fanuc.common.cartesian_position_with_user_frame import CartesianPositionWithUserFrame
from underautomation.fanuc.common.joints_position import JointsPosition

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

##
# Joint motion with Cartesian target, percent speed
cart = JointMotionTpInstruction()
cart.speed_type = RmiJointSpeedType.Percent
cart.speed = 10
cart.term_type = RmiTerminationType.Fine
cart.target = CartesianPositionWithUserFrame(500, 200, 300, 0, 90, 0, 1, 0)
robot.rmi.send_tp_instruction(cart).wait_for_completion()

# Joint motion with joint-angle target representation
jrep = JointMotionJRepTpInstruction()
jrep.speed_type = RmiJointSpeedType.Percent
jrep.speed = 5
jrep.term_type = RmiTerminationType.Fine
jrep.joints = JointsPosition(10, -20, 30, 0, 60, 0)
robot.rmi.send_tp_instruction(jrep).wait_for_completion()

# Incremental joint motion: rotate J5 by 5 degrees
rel = JointRelativeJRepTpInstruction()
rel.speed_type = RmiJointSpeedType.Percent
rel.speed = 5
rel.term_type = RmiTerminationType.Fine
rel.joints = JointsPosition(0, 0, 0, 0, 5, 0)
robot.rmi.send_tp_instruction(rel).wait_for_completion()
##

robot.rmi.abort()
robot.disconnect()
```

### Speed types for joint motion

| `RmiJointSpeedType` | Description |
|---------------------|-------------|
| `Percent` | Percentage of maximum joint speed (1-100). |
| `Time` | Duration in 0.1-second steps. |
| `MSec` | Duration in milliseconds. |

## Circular motion

Circular motion requires a **via point** and a **destination**:

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

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

        /**/
        // Circular motion: define a via-point and a destination
        var arc = new CircularMotionTpInstruction
        {
            SpeedType = RmiLinearSpeedType.MmSec,
            Speed = 80,
            TermType = RmiTerminationType.Fine,
            Via = new CartesianPositionWithUserFrame(600, 100, 350, 0, 90, 0, tool: 1, frame: 0),
            Target = new CartesianPositionWithUserFrame(700, 0, 300, 0, 90, 0, tool: 1, frame: 0)
        };
        robot.Rmi.SendTpInstruction(arc).WaitForCompletion();

        // Incremental circular motion (via and target are deltas)
        var arcRel = new CircularRelativeTpInstruction
        {
            SpeedType = RmiLinearSpeedType.MmSec,
            Speed = 60,
            TermType = RmiTerminationType.Fine,
            Via = new CartesianPositionWithUserFrame(50, 50, 0, 0, 0, 0, 1, 0),
            Target = new CartesianPositionWithUserFrame(100, 0, 0, 0, 0, 0, 1, 0)
        };
        robot.Rmi.SendTpInstruction(arcRel).WaitForCompletion();
        /**/

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

**Python : RmiMotionCircular**
```python
from underautomation.fanuc.fanuc_robot import FanucRobot
from underautomation.fanuc.connection_parameters import ConnectionParameters
from underautomation.fanuc.rmi.tp_instructions.circular_motion_tp_instruction import CircularMotionTpInstruction
from underautomation.fanuc.rmi.tp_instructions.circular_relative_tp_instruction import CircularRelativeTpInstruction
from underautomation.fanuc.rmi.data.rmi_linear_speed_type import RmiLinearSpeedType
from underautomation.fanuc.rmi.data.rmi_termination_type import RmiTerminationType
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)
robot.rmi.initialize()

##
# Circular motion: define a via-point and a destination
arc = CircularMotionTpInstruction()
arc.speed_type = RmiLinearSpeedType.MmSec
arc.speed = 80
arc.term_type = RmiTerminationType.Fine
arc.via = CartesianPositionWithUserFrame(600, 100, 350, 0, 90, 0, 1, 0)
arc.target = CartesianPositionWithUserFrame(700, 0, 300, 0, 90, 0, 1, 0)
robot.rmi.send_tp_instruction(arc).wait_for_completion()

# Incremental circular motion (via and target are deltas)
arc_rel = CircularRelativeTpInstruction()
arc_rel.speed_type = RmiLinearSpeedType.MmSec
arc_rel.speed = 60
arc_rel.term_type = RmiTerminationType.Fine
arc_rel.via = CartesianPositionWithUserFrame(50, 50, 0, 0, 0, 0, 1, 0)
arc_rel.target = CartesianPositionWithUserFrame(100, 0, 0, 0, 0, 0, 1, 0)
robot.rmi.send_tp_instruction(arc_rel).wait_for_completion()
##

robot.rmi.abort()
robot.disconnect()
```

## Spline motion

Spline motion requires firmware **MajorVersion >= 7** (V9.40P/54 or later). The controller needs at least one more spline instruction after the first to start executing the segment.

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

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

        /**/
        // Spline motion requires MajorVersion >= 7 (firmware V9.40P/54 or later).
        // The controller will not execute the first spline segment until it receives
        // a second instruction (motion or non-motion) after the last spline point.

        robot.Rmi.SendTpInstruction(new SplineMotionTpInstruction
        {
            SpeedType = RmiLinearSpeedType.MmSec,
            Speed = 200,
            TermType = RmiTerminationType.Cnt,
            TermValue = 100,
            Target = new CartesianPositionWithUserFrame(500, 100, 300, 0, 90, 0, 1, 0)
        });
        robot.Rmi.SendTpInstruction(new SplineMotionTpInstruction
        {
            SpeedType = RmiLinearSpeedType.MmSec,
            Speed = 200,
            TermType = RmiTerminationType.Cnt,
            TermValue = 100,
            Target = new CartesianPositionWithUserFrame(550, 200, 320, 0, 90, 0, 1, 0)
        });
        robot.Rmi.SendTpInstruction(new SplineMotionTpInstruction
        {
            SpeedType = RmiLinearSpeedType.MmSec,
            Speed = 200,
            TermType = RmiTerminationType.Fine,
            Target = new CartesianPositionWithUserFrame(600, 100, 300, 0, 90, 0, 1, 0)
        });

        // Send a non-spline instruction to flush the spline buffer and trigger execution
        robot.Rmi.SendTpInstruction(new WaitTimeTpInstruction { Seconds = 0.1 })
             .WaitForCompletion();

        // Spline with joint-angle representation
        robot.Rmi.SendTpInstruction(new SplineMotionJRepTpInstruction
        {
            SpeedType = RmiJointSpeedType.Percent,
            Speed = 50,
            TermType = RmiTerminationType.Cnt,
            TermValue = 100,
            Joints = new JointsPosition(10, -20, 30, 0, 60, 0)
        });
        robot.Rmi.SendTpInstruction(new SplineMotionJRepTpInstruction
        {
            SpeedType = RmiJointSpeedType.Percent,
            Speed = 50,
            TermType = RmiTerminationType.Fine,
            Joints = new JointsPosition(15, -15, 35, 5, 55, 5)
        });
        robot.Rmi.SendTpInstruction(new WaitTimeTpInstruction { Seconds = 0.1 })
             .WaitForCompletion();
        /**/

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

**Python : RmiMotionSpline**
```python
from underautomation.fanuc.fanuc_robot import FanucRobot
from underautomation.fanuc.connection_parameters import ConnectionParameters
from underautomation.fanuc.rmi.tp_instructions.spline_motion_tp_instruction import SplineMotionTpInstruction
from underautomation.fanuc.rmi.tp_instructions.spline_motion_j_rep_tp_instruction import SplineMotionJRepTpInstruction
from underautomation.fanuc.rmi.tp_instructions.wait_time_tp_instruction import WaitTimeTpInstruction
from underautomation.fanuc.rmi.data.rmi_linear_speed_type import RmiLinearSpeedType
from underautomation.fanuc.rmi.data.rmi_joint_speed_type import RmiJointSpeedType
from underautomation.fanuc.rmi.data.rmi_termination_type import RmiTerminationType
from underautomation.fanuc.common.cartesian_position_with_user_frame import CartesianPositionWithUserFrame
from underautomation.fanuc.common.joints_position import JointsPosition

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

##
# Spline motion requires MajorVersion >= 7 (firmware V9.40P/54 or later).
# The controller will not execute the first spline segment until it receives
# a second instruction after the last spline point.

s1 = SplineMotionTpInstruction()
s1.speed_type = RmiLinearSpeedType.MmSec
s1.speed = 200
s1.term_type = RmiTerminationType.Cnt
s1.term_value = 100
s1.target = CartesianPositionWithUserFrame(500, 100, 300, 0, 90, 0, 1, 0)
robot.rmi.send_tp_instruction(s1)

s2 = SplineMotionTpInstruction()
s2.speed_type = RmiLinearSpeedType.MmSec
s2.speed = 200
s2.term_type = RmiTerminationType.Cnt
s2.term_value = 100
s2.target = CartesianPositionWithUserFrame(550, 200, 320, 0, 90, 0, 1, 0)
robot.rmi.send_tp_instruction(s2)

s3 = SplineMotionTpInstruction()
s3.speed_type = RmiLinearSpeedType.MmSec
s3.speed = 200
s3.term_type = RmiTerminationType.Fine
s3.target = CartesianPositionWithUserFrame(600, 100, 300, 0, 90, 0, 1, 0)
robot.rmi.send_tp_instruction(s3)

# Non-spline instruction flushes the spline buffer
flush = WaitTimeTpInstruction()
flush.seconds = 0.1
robot.rmi.send_tp_instruction(flush).wait_for_completion()
##

robot.rmi.abort()
robot.disconnect()
```

## Relative (incremental) motions

All motion types have incremental variants. The position is a delta from the current robot position:

| Absolute | Incremental |
|----------|-------------|
| `LinearMotionTpInstruction` | `LinearRelativeTpInstruction` |
| `JointMotionTpInstruction` | `JointRelativeTpInstruction` |
| `CircularMotionTpInstruction` | `CircularRelativeTpInstruction` |
| `LinearMotionJRepTpInstruction` | `LinearRelativeJRepTpInstruction` |
| `JointMotionJRepTpInstruction` | `JointRelativeJRepTpInstruction` |

## Motion options

Most instruction classes expose optional motion modifiers:

| Property | Type | Description |
|----------|------|-------------|
| `Acc` | `byte?` | Acceleration override (20-100 %). |
| `OffsetPrNumber` | `short?` | Offset position register number. |
| `ToolOffsetPrNumber` | `short?` | Tool offset PR number (MajorVersion >= 4). |
| `VisionPrNumber` | `short?` | Vision offset register number. |
| `WristJoint` | `bool` | Enable wrist-joint mode (linear and circular only). |
| `Mrot` | `bool` | MROT option - requires WristJoint and R640 option. |
| `NoBlend` | `bool` | Allow CNT motion to execute without waiting for the next instruction (MajorVersion >= 5). |
| `Alim` | `int?` | Acceleration limit in mm/s² (MajorVersion >= 5, R921 option). |
| `AlimReg` | `short?` | Register-based acceleration limit (MajorVersion >= 5, R921 option). |
| `LcbType` | `string` | Local Condition Block type: `"TB"`, `"TA"`, or `"DB"`. |
| `LcbValue` | `short?` | LCB value (ms for TA/TB, 0.01 mm for DB). |
| `PortType` | `RmiPortType?` | Output port type (`DOUT` or `ROUT`) triggered by LCB. |
| `PortNumber` | `short?` | LCB output port number. |
| `PortValue` | `RmiOnOff?` | LCB output port value (ON or OFF). |

## Non-motion instructions

Insert wait conditions, frame changes, and program calls between motions:

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

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

        /**/
        // Wait for digital input DI[1] to turn ON before continuing
        robot.Rmi.SendTpInstruction(new WaitDinTpInstruction
        {
            PortNumber = 1,
            Value = RmiOnOff.ON
        });

        // Wait 0.5 seconds
        robot.Rmi.SendTpInstruction(new WaitTimeTpInstruction { Seconds = 0.5 });

        // Switch to UFRAME 2 for the next motions
        robot.Rmi.SendTpInstruction(new SetUFrameTpInstruction { FrameNumber = 2 });

        // Switch to UTOOL 3
        robot.Rmi.SendTpInstruction(new SetUToolTpInstruction { ToolNumber = 3 });

        // Activate payload schedule 1
        robot.Rmi.SendTpInstruction(new SetPayloadTpInstruction { ScheduleNumber = 1 });

        // Call a TP program (requires MajorVersion >= 4)
        robot.Rmi.SendTpInstruction(new CallProgramTpInstruction { ProgramName = "GRIPPER_OPEN" })
             .WaitForCompletion();
        /**/

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

**Python : RmiMotionNonMotion**
```python
from underautomation.fanuc.fanuc_robot import FanucRobot
from underautomation.fanuc.connection_parameters import ConnectionParameters
from underautomation.fanuc.rmi.data.rmi_on_off import RmiOnOff
from underautomation.fanuc.rmi.tp_instructions.wait_din_tp_instruction import WaitDinTpInstruction
from underautomation.fanuc.rmi.tp_instructions.wait_time_tp_instruction import WaitTimeTpInstruction
from underautomation.fanuc.rmi.tp_instructions.set_u_frame_tp_instruction import SetUFrameTpInstruction
from underautomation.fanuc.rmi.tp_instructions.set_u_tool_tp_instruction import SetUToolTpInstruction
from underautomation.fanuc.rmi.tp_instructions.set_payload_tp_instruction import SetPayloadTpInstruction
from underautomation.fanuc.rmi.tp_instructions.call_program_tp_instruction import CallProgramTpInstruction

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

##
# Wait for digital input DI[1] to turn ON before continuing
wait_din = WaitDinTpInstruction()
wait_din.port_number = 1
wait_din.value = RmiOnOff.ON
robot.rmi.send_tp_instruction(wait_din)

# Wait 0.5 seconds
wait_time = WaitTimeTpInstruction()
wait_time.seconds = 0.5
robot.rmi.send_tp_instruction(wait_time)

# Switch to UFRAME 2 for the next motions
uframe = SetUFrameTpInstruction()
uframe.frame_number = 2
robot.rmi.send_tp_instruction(uframe)

# Switch to UTOOL 3
utool = SetUToolTpInstruction()
utool.tool_number = 3
robot.rmi.send_tp_instruction(utool)

# Activate payload schedule 1
payload = SetPayloadTpInstruction()
payload.schedule_number = 1
robot.rmi.send_tp_instruction(payload)

# Call a TP program (requires MajorVersion >= 4)
call = CallProgramTpInstruction()
call.program_name = "GRIPPER_OPEN"
robot.rmi.send_tp_instruction(call).wait_for_completion()
##

robot.rmi.abort()
robot.disconnect()
```

## Tracking instruction status

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

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

        /**/
        // Send several instructions. Each returns immediately.
        for (int i = 0; i < 5; i++)
        {
            var instr = new LinearMotionTpInstruction
            {
                SpeedType = RmiLinearSpeedType.MmSec,
                Speed = 100,
                TermType = RmiTerminationType.Fine,
                Target = new CartesianPositionWithUserFrame(500 + i * 20, 200, 300, 0, 90, 0, 1, 0)
            };
            RmiInstructionResponse r = robot.Rmi.SendTpInstruction(instr);

            // Subscribe to status changes
            r.StatusChanged += status =>
                Console.WriteLine($"  Seq {r.SequenceId}: {status}");
        }

        // Wait for all instructions to complete
        foreach (RmiInstructionResponse r in robot.Rmi.Instructions)
            r.WaitForCompletion();

        // Print final results
        foreach (RmiInstructionResponse r in robot.Rmi.Instructions)
        {
            if (r.Status == RmiInstructionStatus.Error)
                Console.WriteLine($"  Seq {r.SequenceId} failed: {r.ErrorText}");
            else
                Console.WriteLine($"  Seq {r.SequenceId} completed OK");
        }

        // Remove completed instructions from the tracking list
        robot.Rmi.ClearCompletedInstructions();
        /**/

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

**Python : RmiMotionTracking**
```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)
robot.rmi.initialize()

##
# Send several instructions. Each returns immediately.
for i in range(5):
    instr = LinearMotionTpInstruction()
    instr.speed_type = RmiLinearSpeedType.MmSec
    instr.speed = 100
    instr.term_type = RmiTerminationType.Fine
    instr.target = CartesianPositionWithUserFrame(500 + i * 20, 200, 300, 0, 90, 0, 1, 0)
    r = robot.rmi.send_tp_instruction(instr)

    def on_change(status, seq=r.sequence_id):
        print(f"  Seq {seq}: {status}")
    r.status_changed += on_change

# Wait for all instructions to complete
for r in robot.rmi.instructions:
    r.wait_for_completion()

# Print final results
for r in robot.rmi.instructions:
    if r.status == RmiInstructionStatus.Error:
        print(f"  Seq {r.sequence_id} failed: {r.error_text}")
    else:
        print(f"  Seq {r.sequence_id} completed OK")

# Remove completed instructions from the tracking list
robot.rmi.clear_completed_instructions()
##

robot.rmi.abort()
robot.disconnect()
```

## Complete example

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

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

        // Check status
        RmiControllerStatusResponse status = robot.Rmi.GetStatus();
        if (status.TPEnabled)
        {
            Console.WriteLine("Turn off the teach pendant before proceeding.");
            return;
        }
        if (!status.ServoReady)
        {
            Console.WriteLine("Servos are not ready.");
            return;
        }

        // Initialize RMI_MOVE
        robot.Rmi.Initialize();

        // Set speed override
        robot.Rmi.SetOverride(80);

        // Move to a start position
        robot.Rmi.SendTpInstruction(new JointMotionJRepTpInstruction
        {
            SpeedType = RmiJointSpeedType.Percent,
            Speed = 10,
            TermType = RmiTerminationType.Fine,
            Joints = new JointsPosition(0, 0, 0, 0, 0, 0)
        }).WaitForCompletion();

        // Approach
        robot.Rmi.SendTpInstruction(new LinearMotionTpInstruction
        {
            SpeedType = RmiLinearSpeedType.MmSec,
            Speed = 200,
            TermType = RmiTerminationType.Cnt,
            TermValue = 50,
            Target = new CartesianPositionWithUserFrame(500, 200, 350, 0, 90, 0, 1, 0)
        });

        // Descend to pick position
        robot.Rmi.SendTpInstruction(new LinearMotionTpInstruction
        {
            SpeedType = RmiLinearSpeedType.MmSec,
            Speed = 50,
            TermType = RmiTerminationType.Fine,
            Target = new CartesianPositionWithUserFrame(500, 200, 300, 0, 90, 0, 1, 0)
        }).WaitForCompletion();

        // Activate gripper
        robot.Rmi.SendTpInstruction(new CallProgramTpInstruction { ProgramName = "GRIP_ON" })
             .WaitForCompletion();

        // Retract
        robot.Rmi.SendTpInstruction(new LinearMotionTpInstruction
        {
            SpeedType = RmiLinearSpeedType.MmSec,
            Speed = 100,
            TermType = RmiTerminationType.Fine,
            Target = new CartesianPositionWithUserFrame(500, 200, 400, 0, 90, 0, 1, 0)
        }).WaitForCompletion();

        // Check HOLD state
        if (robot.Rmi.IsInHoldState)
        {
            Console.WriteLine("Controller in HOLD. Calling Reset...");
            robot.Rmi.Reset();
        }

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

**Python : RmiMotion**
```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.tp_instructions.joint_motion_tp_instruction import JointMotionTpInstruction
from underautomation.fanuc.rmi.tp_instructions.joint_motion_j_rep_tp_instruction import JointMotionJRepTpInstruction
from underautomation.fanuc.rmi.tp_instructions.wait_din_tp_instruction import WaitDinTpInstruction
from underautomation.fanuc.rmi.tp_instructions.wait_time_tp_instruction import WaitTimeTpInstruction
from underautomation.fanuc.rmi.tp_instructions.set_payload_tp_instruction import SetPayloadTpInstruction
from underautomation.fanuc.rmi.tp_instructions.call_program_tp_instruction import CallProgramTpInstruction
from underautomation.fanuc.rmi.data.rmi_linear_speed_type import RmiLinearSpeedType
from underautomation.fanuc.rmi.data.rmi_joint_speed_type import RmiJointSpeedType
from underautomation.fanuc.rmi.data.rmi_termination_type import RmiTerminationType
from underautomation.fanuc.rmi.data.rmi_on_off import RmiOnOff
from underautomation.fanuc.common.cartesian_position_with_user_frame import CartesianPositionWithUserFrame
from underautomation.fanuc.common.joints_position import JointsPosition

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

# Check status before initializing
status = robot.rmi.get_status()
if status.tp_enabled:
    print("Turn off the teach pendant before proceeding.")
    robot.disconnect()
    exit()

robot.rmi.initialize()
robot.rmi.set_override(80)

# Move to home using joint motion with joint-angle target
home = JointMotionJRepTpInstruction()
home.speed_type = RmiJointSpeedType.Percent
home.speed = 10
home.term_type = RmiTerminationType.Fine
home.joints = JointsPosition(0, 0, 0, 0, 0, 0)
robot.rmi.send_tp_instruction(home).wait_for_completion()

# Approach
approach = LinearMotionTpInstruction()
approach.speed_type = RmiLinearSpeedType.MmSec
approach.speed = 200
approach.term_type = RmiTerminationType.Cnt
approach.term_value = 50
approach.target = CartesianPositionWithUserFrame(500, 200, 350, 0, 90, 0, 1, 0)
robot.rmi.send_tp_instruction(approach)

# Descend to pick position
pick = LinearMotionTpInstruction()
pick.speed_type = RmiLinearSpeedType.MmSec
pick.speed = 50
pick.term_type = RmiTerminationType.Fine
pick.target = CartesianPositionWithUserFrame(500, 200, 300, 0, 90, 0, 1, 0)
robot.rmi.send_tp_instruction(pick).wait_for_completion()

# Activate gripper
grip = CallProgramTpInstruction()
grip.program_name = "GRIP_ON"
robot.rmi.send_tp_instruction(grip).wait_for_completion()

# Retract
retract = LinearMotionTpInstruction()
retract.speed_type = RmiLinearSpeedType.MmSec
retract.speed = 100
retract.term_type = RmiTerminationType.Fine
retract.target = CartesianPositionWithUserFrame(500, 200, 400, 0, 90, 0, 1, 0)
robot.rmi.send_tp_instruction(retract).wait_for_completion()

# Handle HOLD state
if robot.rmi.is_in_hold_state:
    print("Controller in HOLD. Calling reset...")
    robot.rmi.reset()

robot.rmi.abort()
robot.disconnect()
```

## API reference

**Members of Rmi.TpInstructions.LinearMotionTpInstruction**
```csharp
public class LinearMotionTpInstruction : CartesianMotionTpInstructionBase {
    public LinearMotionTpInstruction()

    // Acceleration limit value. <code>null</code> uses the controller default. Requires MajorVersion &gt;= 5 and the R921 option.
    public int? Alim { get; set; }

    // Acceleration limit register number. <code>null</code> disables register-based limit. Requires MajorVersion &gt;= 5 and the R921 option.
    public short? AlimReg { get; set; }

    // When <code>true</code>, enables coordinated motion (MROT).
    public bool Mrot { get; set; }

    // When <code>true</code>, disables blending with the next instruction. Requires MajorVersion &gt;= 5.
    public bool NoBlend { get; set; }

    // Speed unit (mm/s, inch/min, or time).
    public RmiLinearSpeedType SpeedType { get; set; }

    // When <code>true</code>, enables wrist-joint mode for this motion.
    public bool WristJoint { get; set; }
}
```

**Members of Rmi.TpInstructions.JointMotionTpInstruction**
```csharp
public class JointMotionTpInstruction : CartesianMotionTpInstructionBase {
    public JointMotionTpInstruction()

    // When <code>true</code>, enables coordinated motion (MROT).
    public bool Mrot { get; set; }

    // When <code>true</code>, disables blending with the next instruction.
    public bool NoBlend { get; set; }

    // Speed unit (percent override or time).
    public RmiJointSpeedType SpeedType { get; set; }
}
```

**Members of Rmi.TpInstructions.JointMotionJRepTpInstruction**
```csharp
public class JointMotionJRepTpInstruction : JRepMotionTpInstructionBase {
    public JointMotionJRepTpInstruction()

    // When <code>true</code>, enables coordinated motion (MROT).
    public bool Mrot { get; set; }

    // When <code>true</code>, disables blending with the next instruction.
    public bool NoBlend { get; set; }

    // Speed unit (percent override or time).
    public RmiJointSpeedType SpeedType { get; set; }
}
```

**Members of Rmi.TpInstructions.CircularMotionTpInstruction**
```csharp
public class CircularMotionTpInstruction : CartesianMotionTpInstructionBase {
    public CircularMotionTpInstruction()

    // When <code>true</code>, enables coordinated motion (MROT).
    public bool Mrot { get; set; }

    // When <code>true</code>, disables blending with the next instruction.
    public bool NoBlend { get; set; }

    // Speed unit (mm/s, inch/min, or time).
    public RmiLinearSpeedType SpeedType { get; set; }

    // Via-point Cartesian position that defines the arc.
    public CartesianPositionWithUserFrame Via { get; set; }

    // When <code>true</code>, enables wrist-joint mode for this motion.
    public bool WristJoint { get; set; }
}
```

**Members of Rmi.TpInstructions.SplineMotionTpInstruction**
```csharp
public class SplineMotionTpInstruction : CartesianMotionTpInstructionBase {
    public SplineMotionTpInstruction()

    // Speed unit (mm/s, inch/min, or time).
    public RmiLinearSpeedType SpeedType { get; set; }
}
```

**Members of Rmi.TpInstructions.FullMotionTpInstructionBase**
```csharp
public abstract class FullMotionTpInstructionBase : MotionTpInstructionBase {
    protected FullMotionTpInstructionBase()

    // Optional acceleration override (1-100 %). <code>null</code> uses the controller default.
    public byte? Acc { get; set; }

    // Lock-and-continue (LCB) condition type string. <code>null</code> disables LCB.
    public string LcbType { get; set; }

    // Lock-and-continue (LCB) condition value. Required when <xref href="UnderAutomation.Fanuc.Rmi.TpInstructions.FullMotionTpInstructionBase.LcbType" data-throw-if-not-resolved="false"></xref> is set.
    public short? LcbValue { get; set; }

    // Offset position register number. <code>null</code> disables offset.
    public short? OffsetPrNumber { get; set; }

    // Digital output port number. Required when <xref href="UnderAutomation.Fanuc.Rmi.TpInstructions.FullMotionTpInstructionBase.PortType" data-throw-if-not-resolved="false"></xref> is set.
    public short? PortNumber { get; set; }

    // Digital output port type to trigger at the end of this motion. <code>null</code> disables output.
    public RmiPortType? PortType { get; set; }

    // Digital output port value. Required when <xref href="UnderAutomation.Fanuc.Rmi.TpInstructions.FullMotionTpInstructionBase.PortType" data-throw-if-not-resolved="false"></xref> is set.
    public RmiOnOff? PortValue { get; set; }

    // Tool-offset position register number. <code>null</code> disables tool offset. Requires MajorVersion &gt;= 4.
    public short? ToolOffsetPrNumber { get; set; }

    // Vision offset position register number. <code>null</code> disables vision offset.
    public short? VisionPrNumber { get; set; }
}
```

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