RMI lets you send TP-equivalent motion instructions to the robot. Each instruction has a unique `sequenceId` and is queued for execution in order.

## Linear motion

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

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

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

        /**/
        Frame target = new Frame { X = 500, Y = 200, Z = 300, W = 0, P = 90, R = 0 };
        MotionConfiguration config = new MotionConfiguration { UToolNumber = 1, UFrameNumber = 0 };

        robot.Rmi.LinearMotion(
            sequenceId: 1,
            config: config,
            position: target,
            speedType: SpeedType.MmSec,
            speed: 100,
            termType: TerminationType.Fine,
            termValue: 0,
            acc: null, offsetPr: null, visionPr: null, wristJoint: false, mrot: false,
            lcbType: null, lcbValue: null, portType: null, portNumber: null, portValue: null
        );
        /**/
    }
}
```

**Python : RmiMotionLinear**
```python
from underautomation.fanuc.fanuc_robot import FanucRobot

robot = FanucRobot()
robot.connect("192.168.0.1")
robot.rmi.initialize()

##
target = {"x": 500, "y": 200, "z": 300, "w": 0, "p": 90, "r": 0}
config = {"utool_number": 1, "uframe_number": 0}

robot.rmi.linear_motion(
    sequence_id=1,
    config=config,
    position=target,
    speed_type="MmSec",
    speed=100,
    term_type="Fine",
    term_value=0
)
##
```

### Speed types

| `SpeedType` | Description |
|-------------|-------------|
| `MmSec` | Millimeters per second |
| `InchMin` | Inches per minute |
| `Time` | Time in seconds |
| `Percent` | Percentage of maximum speed |

### Termination types

| `TerminationType` | Description |
|-------------------|-------------|
| `Fine` | Stop precisely at the target |
| `Cnt` | Continuous (fly-by, value = CNT percentage) |
| `Cr` | Corner Region |

## Joint motion

Move using joint interpolation:

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

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

        /**/
        Frame target = new Frame { X = 500, Y = 200, Z = 300, W = 0, P = 90, R = 0 };
        MotionConfiguration config = new MotionConfiguration { UToolNumber = 1, UFrameNumber = 0 };

        // Joint motion with Cartesian target
        robot.Rmi.JointMotion(
            sequenceId: 2,
            config: config,
            position: target,
            speedType: SpeedType.Percent,
            speed: 50,
            termType: TerminationType.Cnt,
            termValue: 100,
            acc: null, offsetPr: null, visionPr: null, mrot: false,
            lcbType: null, lcbValue: null, portType: null, portNumber: null, portValue: null
        );

        // Joint motion with joint angles
        JointAngles joints = new JointAngles { J1 = 0, J2 = 0, J3 = 0, J4 = 0, J5 = -90, J6 = 0 };
        robot.Rmi.JointMotionJRep(
            sequenceId: 3,
            joints: joints,
            speedType: SpeedType.Percent,
            speed: 50,
            termType: TerminationType.Cnt,
            termValue: 100,
            acc: null, offsetPr: null, visionPr: null, mrot: false,
            lcbType: null, lcbValue: null, portType: null, portNumber: null, portValue: null
        );
        /**/
    }
}
```

**Python : RmiMotionJoint**
```python
from underautomation.fanuc.fanuc_robot import FanucRobot

robot = FanucRobot()
robot.connect("192.168.0.1")
robot.rmi.initialize()

##
target = {"x": 500, "y": 200, "z": 300, "w": 0, "p": 90, "r": 0}
config = {"utool_number": 1, "uframe_number": 0}

# Joint motion with Cartesian target
robot.rmi.joint_motion(
    sequence_id=2,
    config=config,
    position=target,
    speed_type="Percent",
    speed=50,
    term_type="Cnt",
    term_value=100
)

# Joint motion with joint angles
joints = {"j1": 0, "j2": 0, "j3": 0, "j4": 0, "j5": -90, "j6": 0}
robot.rmi.joint_motion_jrep(
    sequence_id=3,
    joint_angles=joints,
    speed_type="Percent",
    speed=50,
    term_type="Cnt",
    term_value=100
)
##
```

### Joint motion with joint representation

Specify joint angles directly instead of a Cartesian frame.

## Circular motion

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

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

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

        /**/
        MotionConfiguration config = new MotionConfiguration { UToolNumber = 1, UFrameNumber = 0 };

        Frame via = new Frame { X = 450, Y = 250, Z = 350, W = 0, P = 90, R = 0 };
        MotionConfiguration viaConfig = new MotionConfiguration { UToolNumber = 1, UFrameNumber = 0 };
        Frame dest = new Frame { X = 500, Y = 300, Z = 300, W = 0, P = 90, R = 0 };

        robot.Rmi.CircularMotion(
            sequenceId: 4,
            config: config,
            position: dest,
            viaConfig: viaConfig,
            viaPosition: via,
            speedType: SpeedType.MmSec,
            speed: 50,
            termType: TerminationType.Fine,
            termValue: 0,
            acc: null, offsetPr: null, visionPr: null, wristJoint: false, mrot: false,
            lcbType: null, lcbValue: null, portType: null, portNumber: null, portValue: null
        );
        /**/
    }
}
```

**Python : RmiMotionCircular**
```python
from underautomation.fanuc.fanuc_robot import FanucRobot

robot = FanucRobot()
robot.connect("192.168.0.1")
robot.rmi.initialize()

##
config = {"utool_number": 1, "uframe_number": 0}

via = {"x": 450, "y": 250, "z": 350, "w": 0, "p": 90, "r": 0}
via_config = {"utool_number": 1, "uframe_number": 0}
dest = {"x": 500, "y": 300, "z": 300, "w": 0, "p": 90, "r": 0}

robot.rmi.circular_motion(
    sequence_id=4,
    config=config,
    position=dest,
    via_config=via_config,
    via_position=via,
    speed_type="MmSec",
    speed=50,
    term_type="Fine",
    term_value=0
)
##
```

## Relative motions

All motion types have relative (incremental) variants.

## Wait and instruction packets

Insert wait conditions between motions:

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

public class RmiMotionWait
{
    static void Main()
    {
        FanucRobot robot = new FanucRobot();
        robot.Connect("192.168.0.1");
        robot.Rmi.Initialize();

        /**/
        // Wait for digital input DI[5] = ON
        robot.Rmi.WaitDin(sequenceId: 10, portNumber: 5, value: OnOff.ON);

        // Wait for a duration (seconds)
        robot.Rmi.WaitTime(sequenceId: 11, seconds: 2.0);

        // Set frame/tool mid-program
        robot.Rmi.SetUFrameInstruction(sequenceId: 12, frameNumber: 2);
        robot.Rmi.SetUToolInstruction(sequenceId: 13, toolNumber: 3);

        // Set payload schedule
        robot.Rmi.SetPayload(sequenceId: 14, scheduleNumber: 1);
        /**/
    }
}
```

**Python : RmiMotionWait**
```python
from underautomation.fanuc.fanuc_robot import FanucRobot

robot = FanucRobot()
robot.connect("192.168.0.1")
robot.rmi.initialize()

##
# Wait for digital input DI[5] = ON
robot.rmi.wait_din(sequence_id=10, port_number=5, value=True)

# Wait for a duration (seconds)
robot.rmi.wait_time(sequence_id=11, seconds=2.0)

# Set frame/tool mid-program
robot.rmi.set_uframe_instruction(sequence_id=12, frame_number=2)
robot.rmi.set_utool_instruction(sequence_id=13, tool_number=3)

# Set payload schedule
robot.rmi.set_payload(sequence_id=14, schedule_number=1)
##
```

## Complete example

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

public class RmiMotion
{
    public 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();

        /**/
        Frame target = new Frame { X = 500, Y = 200, Z = 300, W = 0, P = 90, R = 0 };
        MotionConfiguration config = new MotionConfiguration { UToolNumber = 1, UFrameNumber = 0 };

        // Linear motion
        robot.Rmi.LinearMotion(
            sequenceId: 1,
            config: config,
            position: target,
            speedType: SpeedType.MmSec,
            speed: 100,
            termType: TerminationType.Fine,
            termValue: 0,
            acc: null, offsetPr: null, visionPr: null, wristJoint: false, mrot: false,
            lcbType: null, lcbValue: null, portType: null, portNumber: null, portValue: null
        );

        // Joint motion
        robot.Rmi.JointMotion(
            sequenceId: 2,
            config: config,
            position: target,
            speedType: SpeedType.Percent,
            speed: 50,
            termType: TerminationType.Cnt,
            termValue: 100,
            acc: null, offsetPr: null, visionPr: null, mrot: false,
            lcbType: null, lcbValue: null, portType: null, portNumber: null, portValue: null
        );

        // Joint motion with joint angles
        JointAngles joints = new JointAngles { J1 = 0, J2 = 0, J3 = 0, J4 = 0, J5 = -90, J6 = 0 };
        robot.Rmi.JointMotionJRep(
            sequenceId: 3,
            joints: joints,
            speedType: SpeedType.Percent,
            speed: 50,
            termType: TerminationType.Cnt,
            termValue: 100,
            acc: null, offsetPr: null, visionPr: null, mrot: false,
            lcbType: null, lcbValue: null, portType: null, portNumber: null, portValue: null
        );

        // Circular motion (via point + destination)
        Frame via = new Frame { X = 450, Y = 250, Z = 350, W = 0, P = 90, R = 0 };
        MotionConfiguration viaConfig = new MotionConfiguration { UToolNumber = 1, UFrameNumber = 0 };
        robot.Rmi.CircularMotion(
            sequenceId: 4,
            config: config,
            position: target,
            viaConfig: viaConfig,
            viaPosition: via,
            speedType: SpeedType.MmSec,
            speed: 50,
            termType: TerminationType.Fine,
            termValue: 0,
            acc: null, offsetPr: null, visionPr: null, wristJoint: false, mrot: false,
            lcbType: null, lcbValue: null, portType: null, portNumber: null, portValue: null
        );

        // Wait for digital input
        robot.Rmi.WaitDin(sequenceId: 10, portNumber: 5, value: OnOff.ON);

        // Wait for a duration
        robot.Rmi.WaitTime(sequenceId: 11, seconds: 2.0);

        // Set payload schedule
        robot.Rmi.SetPayload(sequenceId: 12, scheduleNumber: 1);
        /**/
    }
}
```

**Python : RmiMotion**
```python
from underautomation.fanuc.fanuc_robot import FanucRobot
from underautomation.fanuc.connection_parameters import ConnectionParameters
from underautomation.fanuc.rmi.data.speed_type import SpeedType
from underautomation.fanuc.rmi.data.termination_type import TerminationType
from underautomation.fanuc.rmi.data.on_off import OnOff
from underautomation.fanuc.rmi.data.frame import Frame
from underautomation.fanuc.rmi.data.motion_configuration import MotionConfiguration
from underautomation.fanuc.rmi.data.joint_angles import JointAngles

# Create a robot instance
robot = FanucRobot()

# Configure connection parameters
parameters = ConnectionParameters("192.168.0.1")
parameters.rmi.enable = True

# Connect to the robot
robot.connect(parameters)
robot.rmi.initialize()

##
target = Frame()
target.x = 500
target.y = 200
target.z = 300
target.w = 0
target.p = 90
target.r = 0

config = MotionConfiguration()
config.u_tool_number = 1
config.u_frame_number = 0

# Linear motion
robot.rmi.linear_motion(
    sequence_id=1,
    config=config,
    position=target,
    speed_type=SpeedType.MM_SEC,
    speed=100,
    term_type=TerminationType.FINE,
    term_value=0
)

# Joint motion
robot.rmi.joint_motion(
    sequence_id=2,
    config=config,
    position=target,
    speed_type=SpeedType.PERCENT,
    speed=50,
    term_type=TerminationType.CNT,
    term_value=100
)

# Joint motion with joint angles
joints = JointAngles()
joints.j1 = 0
joints.j2 = 0
joints.j3 = 0
joints.j4 = 0
joints.j5 = -90
joints.j6 = 0

robot.rmi.joint_motion_j_rep(
    sequence_id=3,
    joint_angles=joints,
    speed_type=SpeedType.PERCENT,
    speed=50,
    term_type=TerminationType.CNT,
    term_value=100
)

# Wait for digital input
robot.rmi.wait_din(sequence_id=10, port_number=5, value=OnOff.ON)

# Wait for a duration
robot.rmi.wait_time(sequence_id=11, seconds=2.0)

# Set payload schedule
robot.rmi.set_payload(sequence_id=12, schedule_number=1)
##
```

## API reference

**Members of Rmi.Data.Frame**
```csharp
public class Frame {
    public Frame()

    public override bool Equals(object obj)

    // Extended axis 1 (optional).
    public double Ext1 { get; set; }

    // Extended axis 2 (optional).
    public double Ext2 { get; set; }

    // Extended axis 3 (optional).
    public double Ext3 { get; set; }

    public override int GetHashCode()

    // P (Pitch) in degrees.
    public double P { get; set; }

    // R (Roll) in degrees.
    public double R { get; set; }

    public override string ToString()

    // W (Yaw) in degrees.
    public double W { get; set; }

    // X coordinate in millimeters.
    public double X { get; set; }

    // Y coordinate in millimeters.
    public double Y { get; set; }

    // Z coordinate in millimeters.
    public double Z { get; set; }
}
```

**Members of Rmi.Data.MotionConfiguration**
```csharp
public class MotionConfiguration {
    public MotionConfiguration()

    public override bool Equals(object obj)

    // FLIP bit (0/1).
    public byte Flip { get; set; }

    // FRONT bit (0/1).
    public byte Front { get; set; }

    public override int GetHashCode()

    // LEFT bit (0/1).
    public byte Left { get; set; }

    public override string ToString()

    // Turn 4.
    public byte Turn4 { get; set; }

    // Turn 5.
    public byte Turn5 { get; set; }

    // Turn 6.
    public byte Turn6 { get; set; }

    // Active UFRAME number.
    public byte UFrameNumber { get; set; }

    // Active UTOOL number.
    public byte UToolNumber { get; set; }

    // UP bit (0/1).
    public byte Up { get; set; }
}
```

**Members of Rmi.Data.JointAngles**
```csharp
public class JointAngles {
    public JointAngles()

    public override bool Equals(object obj)

    public override int GetHashCode()

    // Joint 1 angle.
    public double J1 { get; set; }

    // Joint 2 angle.
    public double J2 { get; set; }

    // Joint 3 angle.
    public double J3 { get; set; }

    // Joint 4 angle.
    public double J4 { get; set; }

    // Joint 5 angle.
    public double J5 { get; set; }

    // Joint 6 angle.
    public double J6 { get; set; }

    // Joint 7 angle (optional extended axis).
    public double J7 { get; set; }

    // Joint 8 angle (optional extended axis).
    public double J8 { get; set; }

    // Joint 9 angle (optional extended axis).
    public double J9 { get; set; }

    public override string ToString()
}
```

**Members of Rmi.Data.SpeedType**
```csharp
public enum SpeedType {
    // Linear speed in inches per minute.
    InchMin = 1

    // Linear speed in millimeters per second.
    MmSec = 0

    // Joint speed as percentage of maximum.
    Percent = 3

    // Duration-based speed (0.1 seconds unit for Linear/Circular, milliseconds for Joint).
    Time = 2
}
```

**Members of Rmi.Data.TerminationType**
```csharp
public enum TerminationType {
    // Continuous termination; blend motions (1-100).
    Cnt = 1

    // Constant path mode (requires option).
    Cr = 2

    // FINE termination; precise stop.
    Fine = 0
}
```

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

    public override bool Equals(object obj)

    public override int GetHashCode()

    // Echoed SequenceID of the instruction that completed.
    public int SequenceId { get; set; }

    public override string ToString()
}
```