This page shows how to use the ABB SDK from Matlab, to read and control an IRC5 or an OmniCore controller from a Matlab script. Matlab loads the .NET DLL of the SDK with its .NET interface: nothing else is installed, on the PC or on the controller.

## Requirements

| Item            | Supported                                                                                                        |
| --------------- | ---------------------------------------------------------------------------------------------------------------- |
| Windows         | Matlab with the .NET Framework 4.6.2 or later (the default on Windows 10 and 11). Load the `net48` DLL           |
| Linux and macOS | Matlab R2024b or later with the .NET runtime, selected by `dotnetenv("core")`. Not tested by UnderAutomation yet |
| Controllers     | IRC5 with RobotWare 6 and OmniCore with RobotWare 7, real or virtual in RobotStudio                              |

Matlab documents its .NET interface in [Call .NET from MATLAB](https://www.mathworks.com/help/matlab/using-net-libraries-in-matlab.html).

## Load the SDK

1. Download [UnderAutomation.ABB.zip](https://github.com/underautomation/ABB.NET/releases/latest/download/UnderAutomation.ABB.zip) from the latest release of [ABB.NET](https://github.com/underautomation/ABB.NET/releases/latest).
2. On Windows, unblock the zip before you extract it: right click, `Properties`, check `Unblock`.
3. Copy its `net48` folder next to your script.
4. Load the DLL with `NET.addAssembly`, then import the namespaces you use.

**Matlab : MatlabLoadAssembly**
```matlab
%%
% The net48 folder of UnderAutomation.ABB.zip, copied next to this script
dll = fullfile(pwd, 'net48', 'UnderAutomation.ABB.dll');

if ~NET.isNETSupported
    error('No supported .NET Framework found on this computer');
end

% Load the SDK once per Matlab session
NET.addAssembly(dll);

% Import the namespaces you use
import UnderAutomation.ABB.*
import UnderAutomation.ABB.Rws.*
import UnderAutomation.ABB.Rws.Data.*
%%
```

`NET.addAssembly` needs an absolute path: `fullfile(pwd, ...)` builds one that works on every operating system. Matlab cannot unload a .NET assembly: restart Matlab to load a new version of the DLL.

On Linux and macOS, Matlab R2024b and later can use the .NET runtime instead of the .NET Framework. Set the environment variable `DOTNET_ROOT` to the folder of the .NET runtime, call `dotnetenv("core")` before `NET.addAssembly`, and load the DLL of the `net8.0` folder. See [dotnetenv](https://www.mathworks.com/help/matlab/ref/dotnetenv.html) in the Matlab documentation.

## First program

**C# : ConnectQuick**
```csharp
using UnderAutomation.ABB;

public class ConnectQuick
{
    static void Main()
    {
        /**/
        // Connect to an OmniCore controller with the default RWS parameters
        AbbController robot = new AbbController();
        robot.Connect("192.168.0.1");

        // Every RWS service is reachable from robot.Rws
        Console.WriteLine(robot.Rws.Controller.GetIdentity().Name);
        /**/

        robot.Disconnect();
    }
}
```

**Python : ConnectQuick**
```python
from underautomation.abb.abb_controller import AbbController

##
# Connect to an OmniCore controller with the default RWS parameters
robot = AbbController()
robot.connect("192.168.0.1")

# Every RWS service is reachable from robot.rws
print(robot.rws.controller.get_identity().name)
##

robot.disconnect()
```

**Matlab : ConnectQuick**
```matlab
NET.addAssembly(fullfile(pwd, 'net48', 'UnderAutomation.ABB.dll'));
import UnderAutomation.ABB.*

%%
% Connect to an OmniCore controller with the default RWS parameters
robot = AbbController();
robot.Connect('192.168.0.1');

% Every RWS service is reachable from robot.Rws
identity = robot.Rws.Controller.GetIdentity();
disp(char(identity.Name));
%%

robot.Disconnect();
```

The default parameters target an OmniCore controller. For an IRC5 with RobotWare 6, or for another user account, use `ConnectionParameters`:

**C# : Connect**
```csharp
using UnderAutomation.ABB;
using UnderAutomation.ABB.Rws;

public class Connect
{
    static void Main()
    {
        /**/
        ConnectionParameters parameters = new ConnectionParameters("192.168.0.1");

        // Ping the controller first, so an unreachable robot fails immediately
        parameters.PingBeforeConnect = true;

        parameters.Rws.Enable = true;
        parameters.Rws.Username = "Default User";
        parameters.Rws.Password = "robotics";
        parameters.Rws.UseHttps = false;
        parameters.Rws.Port = 0; // 0 means 80 for HTTP and 443 for HTTPS
        parameters.Rws.Timeout = 10000;
        parameters.Rws.Version = RwsVersion.OmniCore_V2_0;

        AbbController robot = new AbbController();
        robot.Connect(parameters);
        /**/

        robot.Disconnect();
    }
}
```

**Python : Connect**
```python
from underautomation.abb.abb_controller import AbbController
from underautomation.abb.connection_parameters import ConnectionParameters
from underautomation.abb.rws.rws_version import RwsVersion

##
parameters = ConnectionParameters("192.168.0.1")

# Ping the controller first, so an unreachable robot fails immediately
parameters.ping_before_connect = True

parameters.rws.enable = True
parameters.rws.username = "Default User"
parameters.rws.password = "robotics"
parameters.rws.use_https = False
parameters.rws.port = 0  # 0 means 80 for HTTP and 443 for HTTPS
parameters.rws.timeout = 10000
parameters.rws.version = RwsVersion.OmniCore_V2_0

robot = AbbController()
robot.connect(parameters)
##

robot.disconnect()
```

**Matlab : Connect**
```matlab
NET.addAssembly(fullfile(pwd, 'net48', 'UnderAutomation.ABB.dll'));
import UnderAutomation.ABB.*
import UnderAutomation.ABB.Rws.*

%%
parameters = ConnectionParameters('192.168.0.1');

% Ping the controller first, so an unreachable robot fails immediately
parameters.PingBeforeConnect = true;

parameters.Rws.Enable = true;
parameters.Rws.Username = 'Default User';
parameters.Rws.Password = 'robotics';
parameters.Rws.UseHttps = false;
parameters.Rws.Port = 0; % 0 means 80 for HTTP and 443 for HTTPS
parameters.Rws.Timeout = 10000;

% IRC5 with RobotWare 6: RwsVersion.Irc5_V1_0
parameters.Rws.Version = RwsVersion.OmniCore_V2_0;

robot = AbbController();
robot.Connect(parameters);
%%

robot.Disconnect();
```

When the controller answers on HTTPS with a certificate it signed itself, the SDK accepts it and enables TLS 1.2. Nothing has to be set in Matlab. See [Connect to your robot](/abb/documentation/connect).

The SDK runs for 30 days without a key. After that, register your license once per Matlab session:

**C# : License**
```csharp
using UnderAutomation.ABB;
using UnderAutomation.ABB.License;

public class License
{
    static void Main()
    {
        /**/
        // Register the license once, before the first connection
        AbbController.RegisterLicense("YourCompanyName", "YOUR_LICENSE_KEY");

        LicenseInfo info = AbbController.LicenseInfo;

        // Number of trial days remaining, null when the product is licensed
        int? evaluationDaysLeft = info.EvaluationDaysLeft;

        bool licenseValid = info.State == LicenseState.Licensed;

        // A readable description of the current state
        Console.WriteLine(info);
        /**/

        /**/
        // Check the license once at startup, rather than catching the exception
        // on every connection
        if (!AbbController.LicenseInfo.IsLicensed)
        {
            Console.WriteLine(AbbController.LicenseInfo);
            return;
        }
        /**/

        /**/
        // Without a key the library runs in its 30 day trial period.
        // Connect throws an InvalidLicenseException once the trial has expired.
        try
        {
            AbbController robot = new AbbController();
            robot.Connect("192.168.0.1");
        }
        catch (InvalidLicenseException ex)
        {
            Console.WriteLine(ex.Message);
            Console.WriteLine(ex.LicenseInfo.State);
        }
        /**/
    }
}
```

**Python : License**
```python
from underautomation.abb.abb_controller import AbbController
from underautomation.abb.license.license_state import LicenseState
from UnderAutomation.ABB.License import InvalidLicenseException

##
# Register the license once, before the first connection
AbbController.register_license("YourCompanyName", "YOUR_LICENSE_KEY")

robot = AbbController()
info = robot.license_info

# Number of trial days remaining, None when the product is licensed
evaluation_days_left = info.evaluation_days_left

license_valid = info.state == LicenseState.Licensed

# A readable description of the current state
print(info)
##

##
# Check the license once at startup, rather than catching the exception
# on every connection
if not info.is_licensed:
    print(info)
    raise SystemExit(0)
##

##
# Without a key the library runs in its 30 day trial period.
# connect raises an InvalidLicenseException once the trial has expired.
try:
    robot = AbbController()
    robot.connect("192.168.0.1")
except InvalidLicenseException as ex:
    # The exception comes from the .NET runtime, so its members keep their original names
    print(ex.Message)
    print(ex.LicenseInfo.State)
##
```

**Matlab : License**
```matlab
NET.addAssembly(fullfile(pwd, 'net48', 'UnderAutomation.ABB.dll'));
import UnderAutomation.ABB.*

%%
% Register the license once, before the first connection
AbbController.RegisterLicense('YourCompanyName', 'YOUR_LICENSE_KEY');

info = AbbController.LicenseInfo;

% A readable description of the current state
disp(char(info.ToString()));

% Check the license once at startup
if ~info.IsLicensed
    error(char(info.ToString()));
end
%%
```

## From .NET to Matlab

The names of the classes, methods and properties are the .NET names of the other pages. The C# samples of the documentation translate line by line, with these rules:

| .NET                                             | Matlab                                                                  |
| ------------------------------------------------ | ----------------------------------------------------------------------- |
| `new AbbController()`                            | `AbbController()`, after `import UnderAutomation.ABB.*`                 |
| `"192.168.0.1"`                                  | `'192.168.0.1'`, a char vector                                          |
| `GetJointTarget("ROB_1", alwaysRead: true)`      | `GetJointTarget('ROB_1', true)`: no named arguments                     |
| An optional argument, like `tool`                | Pass every argument. `[]` passes `null`                                 |
| A `float` argument, like a signal value          | `single(1)`                                                             |
| An `int` argument, like a timeout                | `int32(2000)`                                                           |
| A nullable value (`float?`, `bool?`)             | An object with `HasValue` and `Value`, or `GetValueOrDefault()`         |
| `tasks[0]`                                       | `tasks(1)`: Matlab indexes .NET arrays from 1                           |
| `tasks.Length`                                   | `tasks.Length`                                                          |
| An enumeration value, like `task.ExecutionState` | `char(task.ExecutionState)` gives its name                              |
| `MastershipDomain.Rapid`                         | `MastershipDomain.Rapid`, after `import UnderAutomation.ABB.Rws.Data.*` |
| `try { } finally { }`                            | `try ... catch`, then the cleanup in both branches                      |

## Examples

### Discover the controllers

**C# : Discover**
```csharp
using UnderAutomation.ABB;
using UnderAutomation.ABB.Discovery;

public class Discover
{
    static void Main()
    {
        /**/
        // Looks for two seconds, no license and no connection needed
        DiscoveredController[] found = AbbController.Discover();

        foreach (var controller in found)
        {
            Console.WriteLine($"{controller.SystemName} at {controller.Address}:{controller.Port}");
            Console.WriteLine($"RobotWare {controller.RobotWareVersion}, {(controller.UseHttps ? "HTTPS" : "HTTP")}");
        }
        /**/
    }
}
```

**Python : Discover**
```python
from underautomation.abb.abb_controller import AbbController

##
# Looks for two seconds, no license and no connection needed
found = AbbController.discover()

for controller in found:
    print(f"{controller.system_name} at {controller.address}:{controller.port}")
    print(f"RobotWare {controller.robot_ware_version}, {'HTTPS' if controller.use_https else 'HTTP'}")
##
```

**Matlab : Discover**
```matlab
NET.addAssembly(fullfile(pwd, 'net48', 'UnderAutomation.ABB.dll'));
import UnderAutomation.ABB.*

%%
% Looks for two seconds, no license and no connection needed
found = AbbController.Discover(int32(2000));

% .NET arrays are indexed from 1 in Matlab
for i = 1:found.Length
    controller = found(i);
    fprintf('%s at %s:%d, RobotWare %s\n', char(controller.SystemName), ...
        char(controller.Address), controller.Port, char(controller.RobotWareVersion));
end
%%
```

### Read the position

**C# : PositionRead**
```csharp
using UnderAutomation.ABB;
using UnderAutomation.ABB.Common;
using UnderAutomation.ABB.Rws.Data;

public class PositionRead
{
    static void Main()
    {
        AbbController robot = new AbbController();
        robot.Connect("192.168.0.1");

        /**/
        // Where the tool is, in millimetres, with the axis configuration and the external axes
        RobTarget target = robot.Rws.MotionSystem.GetRobTarget("ROB_1");
        Console.WriteLine($"X={target.X} Y={target.Y} Z={target.Z}");
        Console.WriteLine($"orientation {target.Orientation}");
        Console.WriteLine($"configuration {target.Configuration}");
        Console.WriteLine($"external axes {target.ExternalAxes}");

        // The same reading in another frame, with a given tool and work object
        RobTarget inWorld = robot.Rws.MotionSystem.GetRobTarget("ROB_1", CoordinateSystem.World,
                                                                tool: "tGripper", workObject: "wobj0");
        Console.WriteLine(inWorld);
        /**/

        /**/
        // The joint values, robot axes in degrees
        JointTarget joints = robot.Rws.MotionSystem.GetJointTarget("ROB_1");
        Console.WriteLine($"axis 1 = {joints.RobotAxes.Axis1} deg");
        Console.WriteLine($"axis 2 = {joints.RobotAxes.Axis2} deg");

        // An external axis the system does not define comes back as ExternalJoints.NotInUse
        if (joints.ExternalAxes.AxisA != ExternalJoints.NotInUse)
        {
            Console.WriteLine($"external axis A = {joints.ExternalAxes.AxisA}");
        }

        // alwaysRead asks the controller to measure again instead of answering with the value it holds
        JointTarget measured = robot.Rws.MotionSystem.GetJointTarget("ROB_1", alwaysRead: true);
        Console.WriteLine(measured);
        /**/

        /**/
        // Cartesian position without the external axes. ExternalAxes is null here.
        RobTarget cartesian = robot.Rws.MotionSystem.GetCartesianPosition("ROB_1");
        Console.WriteLine(cartesian);

        // The raw values of the measurement system of the unit
        RobotJoints physical = robot.Rws.MotionSystem.GetPhysicalJoints("ROB_1");
        Console.WriteLine(physical);
        /**/

        /**/
        // The same two positions seen from a RAPID task instead of a mechanical unit
        RobTarget taskTarget = robot.Rws.Rapid.GetRobTarget("T_ROB1");
        JointTarget taskJoints = robot.Rws.Rapid.GetJointTarget("T_ROB1");

        // Which external joints of the task carry a real value
        RapidExternalJointStates states = robot.Rws.Rapid.GetExternalJointStates("T_ROB1");
        Console.WriteLine($"external joint 1 : {states.Joint1}");

        // The units the task can move
        foreach (RapidMechanicalUnitItem unit in robot.Rws.Rapid.GetMechanicalUnits("T_ROB1"))
        {
            Console.WriteLine($"{unit.Name} : {unit.Type}, {unit.Mode}");
        }
        /**/

        robot.Disconnect();
    }
}
```

**Python : PositionRead**
```python
from underautomation.abb.abb_controller import AbbController
from underautomation.abb.common.external_joints import ExternalJoints
from underautomation.abb.rws.data.coordinate_system import CoordinateSystem

robot = AbbController()
robot.connect("192.168.0.1")

##
# Where the tool is, in millimetres, with the axis configuration and the external axes
target = robot.rws.motion_system.get_rob_target("ROB_1")
print(f"X={target.x} Y={target.y} Z={target.z}")
print(f"orientation {target.orientation}")
print(f"configuration {target.configuration}")
print(f"external axes {target.external_axes}")

# The same reading in another frame, with a given tool and work object
in_world = robot.rws.motion_system.get_rob_target("ROB_1", CoordinateSystem.World,
                                                  tool="tGripper", workObject="wobj0")
print(in_world)
##

##
# The joint values, robot axes in degrees
joints = robot.rws.motion_system.get_joint_target("ROB_1")
print(f"axis 1 = {joints.robot_axes.axis1} deg")
print(f"axis 2 = {joints.robot_axes.axis2} deg")

# An external axis the system does not define comes back as ExternalJoints.NotInUse
if joints.external_axes.axis_a != ExternalJoints.NotInUse:
    print(f"external axis A = {joints.external_axes.axis_a}")

# alwaysRead asks the controller to measure again instead of answering with the value it holds
measured = robot.rws.motion_system.get_joint_target("ROB_1", alwaysRead=True)
print(measured)
##

##
# Cartesian position without the external axes. external_axes is None here.
cartesian = robot.rws.motion_system.get_cartesian_position("ROB_1")
print(cartesian)

# The raw values of the measurement system of the unit
physical = robot.rws.motion_system.get_physical_joints("ROB_1")
print(physical)
##

##
# The same two positions seen from a RAPID task instead of a mechanical unit
task_target = robot.rws.rapid.get_rob_target("T_ROB1")
task_joints = robot.rws.rapid.get_joint_target("T_ROB1")

# Which external joints of the task carry a real value
states = robot.rws.rapid.get_external_joint_states("T_ROB1")
print(f"external joint 1 : {states.joint1}")

# The units the task can move
for unit in robot.rws.rapid.get_mechanical_units("T_ROB1"):
    print(f"{unit.name} : {unit.type}, {unit.mode}")
##

robot.disconnect()
```

**Matlab : PositionRead**
```matlab
NET.addAssembly(fullfile(pwd, 'net48', 'UnderAutomation.ABB.dll'));
import UnderAutomation.ABB.*
import UnderAutomation.ABB.Rws.Data.*

robot = AbbController();
robot.Connect('192.168.0.1');

%%
% Tool position in millimeters, in the base frame, with the active tool and work object.
% Pass every argument: [] passes null for the tool and the work object.
target = robot.Rws.MotionSystem.GetRobTarget('ROB_1', CoordinateSystem.Base, [], []);
fprintf('X=%.1f Y=%.1f Z=%.1f\n', target.X, target.Y, target.Z);

% Orientation as a quaternion
q = target.Orientation;
fprintf('q = [%.4f, %.4f, %.4f, %.4f]\n', q.Q1, q.Q2, q.Q3, q.Q4);

% Joint values of the robot axes, in degrees
joints = robot.Rws.MotionSystem.GetJointTarget('ROB_1', false);
robotAxes = joints.RobotAxes;
j = [robotAxes.Axis1, robotAxes.Axis2, robotAxes.Axis3, robotAxes.Axis4, robotAxes.Axis5, robotAxes.Axis6];
%%

robot.Disconnect();
```

### Read and write I/O signals

**C# : IoReadSignal**
```csharp
using UnderAutomation.ABB;
using UnderAutomation.ABB.Rws.Data;

public class IoReadSignal
{
    static void Main()
    {
        AbbController robot = new AbbController();
        robot.Connect("192.168.0.1");

        /**/
        // A signal is identified by its network, its device and its name
        IoSignalItem signal = robot.Rws.Io.GetSignal("Local", "Board10", "DO_Gripper");

        Console.WriteLine(signal.Path);          // Local/Board10/DO_Gripper
        Console.WriteLine(signal.Type);          // DigitalOutput
        Console.WriteLine(signal.LogicalValue);  // 1
        Console.WriteLine(signal.LogicalState);  // NotSimulated
        Console.WriteLine(signal.PhysicalValue); // 1
        Console.WriteLine(signal.PhysicalState); // Valid
        /**/

        /**/
        // Every signal of the controller, in one call
        IoSignalItem[] signals = robot.Rws.Io.GetSignals();

        foreach (IoSignalItem item in signals)
        {
            Console.WriteLine($"{item.Path} = {item.LogicalValue} ({item.Type})");
        }
        /**/

        robot.Disconnect();
    }
}
```

**Python : IoReadSignal**
```python
from underautomation.abb.abb_controller import AbbController

robot = AbbController()
robot.connect("192.168.0.1")

##
# A signal is identified by its network, its device and its name
signal = robot.rws.io.get_signal("Local", "Board10", "DO_Gripper")

print(signal.path)            # Local/Board10/DO_Gripper
print(signal.type)            # DigitalOutput
print(signal.logical_value)   # 1
print(signal.logical_state)   # NotSimulated
print(signal.physical_value)  # 1
print(signal.physical_state)  # Valid
##

##
# Every signal of the controller, in one call
signals = robot.rws.io.get_signals()

for item in signals:
    print(f"{item.path} = {item.logical_value} ({item.type})")
##

robot.disconnect()
```

**Matlab : IoReadSignal**
```matlab
NET.addAssembly(fullfile(pwd, 'net48', 'UnderAutomation.ABB.dll'));
import UnderAutomation.ABB.*

robot = AbbController();
robot.Connect('192.168.0.1');

%%
% A signal is identified by its network, its device and its name
signal = robot.Rws.Io.GetSignal('Local', 'Board10', 'DO_Gripper');

% LogicalValue is a .NET Nullable: HasValue is false when the controller gave no value
if signal.LogicalValue.HasValue
    fprintf('%s = %g (%s)\n', char(signal.Path), signal.LogicalValue.Value, char(signal.Type));
end

% Every signal of the controller, in one call
signals = robot.Rws.Io.GetSignals();
for i = 1:signals.Length
    fprintf('%s = %g\n', char(signals(i).Path), signals(i).LogicalValue.GetValueOrDefault());
end
%%

robot.Disconnect();
```

**C# : IoWriteSignal**
```csharp
using UnderAutomation.ABB;

public class IoWriteSignal
{
    static void Main()
    {
        AbbController robot = new AbbController();
        robot.Connect("192.168.0.1");

        /**/
        // A digital signal takes 0 or 1
        robot.Rws.Io.SetSignalValue("Local", "Board10", "DO_Gripper", 1);

        // An analog or a group signal takes any value inside its range
        robot.Rws.Io.SetSignalValue("Local", "Board10", "AO_Speed", 12.5f);

        // The last argument writes the change in the event log of the controller
        robot.Rws.Io.SetSignalValue("Local", "Board10", "DO_Gripper", 0, true);

        // The controller applies the value 500 ms later, and answers immediately
        robot.Rws.Io.SetSignalValueDelayed("Local", "Board10", "DO_Gripper", 1, 500);
        /**/

        robot.Disconnect();
    }
}
```

**Python : IoWriteSignal**
```python
from underautomation.abb.abb_controller import AbbController

robot = AbbController()
robot.connect("192.168.0.1")

##
# A digital signal takes 0 or 1
robot.rws.io.set_signal_value("Local", "Board10", "DO_Gripper", 1)

# An analog or a group signal takes any value inside its range
robot.rws.io.set_signal_value("Local", "Board10", "AO_Speed", 12.5)

# The last argument writes the change in the event log of the controller
robot.rws.io.set_signal_value("Local", "Board10", "DO_Gripper", 0, True)

# The controller applies the value 500 ms later, and answers immediately
robot.rws.io.set_signal_value_delayed("Local", "Board10", "DO_Gripper", 1, 500)
##

robot.disconnect()
```

**Matlab : IoWriteSignal**
```matlab
NET.addAssembly(fullfile(pwd, 'net48', 'UnderAutomation.ABB.dll'));
import UnderAutomation.ABB.*

robot = AbbController();
robot.Connect('192.168.0.1');

%%
% The value is a .NET float: convert it with single().
% The last argument writes the change in the event log of the controller.

% A digital signal takes 0 or 1
robot.Rws.Io.SetSignalValue('Local', 'Board10', 'DO_Gripper', single(1), false);

% An analog or a group signal takes any value inside its range
robot.Rws.Io.SetSignalValue('Local', 'Board10', 'AO_Speed', single(12.5), false);
%%

robot.Disconnect();
```

### Read and write RAPID variables

**C# : RapidReadValue**
```csharp
using System.Globalization;
using UnderAutomation.ABB;
using UnderAutomation.ABB.Rws.Data;

public class RapidReadValue
{
    static void Main()
    {
        AbbController robot = new AbbController();
        robot.Connect("192.168.0.1");

        /**/
        // A value always comes back as the text RAPID writes it with. Reading needs no mastership.
        RapidSymbolValue value = robot.Rws.Rapid.GetSymbolValue("RAPID/T_ROB1/user/reg1");
        Console.WriteLine(value.Value);                  // "42"
        Console.WriteLine(value.DeclarationPosition);    // where the declaration sits in the module

        // num and dnum: parse with the invariant culture, RAPID uses a dot as decimal separator
        double number = double.Parse(robot.Rws.Rapid.GetSymbolValue("RAPID/T_ROB1/user/reg1").Value,
                                     CultureInfo.InvariantCulture);

        // bool: the controller writes TRUE or FALSE
        bool flag = robot.Rws.Rapid.GetSymbolValue("RAPID/T_ROB1/MainModule/myFlag")
                                   .Value.Trim().Equals("TRUE", StringComparison.OrdinalIgnoreCase);

        // string: the value carries the RAPID quotes, remove them
        string text = robot.Rws.Rapid.GetSymbolValue("RAPID/T_ROB1/MainModule/myText").Value.Trim('"');

        Console.WriteLine(number + " " + flag + " " + text);
        /**/

        robot.Disconnect();
    }
}
```

**Python : RapidReadValue**
```python
from underautomation.abb.abb_controller import AbbController

robot = AbbController()
robot.connect("192.168.0.1")

##
# A value always comes back as the text RAPID writes it with. Reading needs no mastership.
value = robot.rws.rapid.get_symbol_value("RAPID/T_ROB1/user/reg1")
print(value.value)                 # "42"
print(value.declaration_position)  # where the declaration sits in the module

# num and dnum: float() reads the dot RAPID uses as decimal separator
number = float(robot.rws.rapid.get_symbol_value("RAPID/T_ROB1/user/reg1").value)

# bool: the controller writes TRUE or FALSE
flag = robot.rws.rapid.get_symbol_value("RAPID/T_ROB1/MainModule/myFlag").value.strip().upper() == "TRUE"

# string: the value carries the RAPID quotes, remove them
text = robot.rws.rapid.get_symbol_value("RAPID/T_ROB1/MainModule/myText").value.strip('"')

print(number, flag, text)
##

robot.disconnect()
```

**Matlab : RapidReadValue**
```matlab
NET.addAssembly(fullfile(pwd, 'net48', 'UnderAutomation.ABB.dll'));
import UnderAutomation.ABB.*

robot = AbbController();
robot.Connect('192.168.0.1');

%%
% A value always comes back as the text RAPID writes it with. Reading needs no mastership.
value = robot.Rws.Rapid.GetSymbolValue('RAPID/T_ROB1/user/reg1');

% num and dnum: RAPID uses a dot as decimal separator, like Matlab
number = str2double(char(value.Value));

% bool: the controller writes TRUE or FALSE
flag = strcmpi(strtrim(char(robot.Rws.Rapid.GetSymbolValue('RAPID/T_ROB1/MainModule/myFlag').Value)), 'TRUE');

% string: the value carries the RAPID quotes, remove them
message = strip(char(robot.Rws.Rapid.GetSymbolValue('RAPID/T_ROB1/MainModule/myText').Value), '"');
%%

robot.Disconnect();
```

Writing needs the RAPID mastership. See [Mastership](/abb/documentation/rws-mastership).

**C# : RapidWriteValue**
```csharp
using System.Globalization;
using UnderAutomation.ABB;
using UnderAutomation.ABB.Rws.Data;

public class RapidWriteValue
{
    static void Main()
    {
        AbbController robot = new AbbController();
        robot.Connect("192.168.0.1");

        /**/
        // Writing needs the RAPID mastership. Take it, write, give it back.
        robot.Rws.Mastership.Request(MastershipDomain.Rapid);
        try
        {
            // num: format with the invariant culture, "1,5" is refused, "1.5" is accepted
            double speed = 1.5;
            robot.Rws.Rapid.SetSymbolValue("RAPID/T_ROB1/user/reg1",
                                           speed.ToString(CultureInfo.InvariantCulture));

            // bool: TRUE or FALSE, in capitals
            robot.Rws.Rapid.SetSymbolValue("RAPID/T_ROB1/MainModule/myFlag", "TRUE");

            // string: the RAPID quotes are part of the value
            robot.Rws.Rapid.SetSymbolValue("RAPID/T_ROB1/MainModule/myText", "\"hello\"");
        }
        finally
        {
            robot.Rws.Mastership.Release(MastershipDomain.Rapid);
        }
        /**/

        robot.Disconnect();
    }
}
```

**Python : RapidWriteValue**
```python
from underautomation.abb.abb_controller import AbbController
from underautomation.abb.rws.data.mastership_domain import MastershipDomain

robot = AbbController()
robot.connect("192.168.0.1")

##
# Writing needs the RAPID mastership. Take it, write, give it back.
robot.rws.mastership.request(MastershipDomain.Rapid)
try:
    # num: RAPID wants a dot as decimal separator, which is what repr of a float gives
    speed = 1.5
    robot.rws.rapid.set_symbol_value("RAPID/T_ROB1/user/reg1", str(speed))

    # bool: TRUE or FALSE, in capitals
    robot.rws.rapid.set_symbol_value("RAPID/T_ROB1/MainModule/myFlag", "TRUE")

    # string: the RAPID quotes are part of the value
    robot.rws.rapid.set_symbol_value("RAPID/T_ROB1/MainModule/myText", '"hello"')
finally:
    robot.rws.mastership.release(MastershipDomain.Rapid)
##

robot.disconnect()
```

**Matlab : RapidWriteValue**
```matlab
NET.addAssembly(fullfile(pwd, 'net48', 'UnderAutomation.ABB.dll'));
import UnderAutomation.ABB.*
import UnderAutomation.ABB.Rws.Data.*

robot = AbbController();
robot.Connect('192.168.0.1');

%%
% Writing needs the RAPID mastership. Take it, write, give it back.
robot.Rws.Mastership.Request(MastershipDomain.Rapid);
try
    % num: sprintf always writes a dot as decimal separator
    robot.Rws.Rapid.SetSymbolValue('RAPID/T_ROB1/user/reg1', sprintf('%g', 1.5));

    % bool: TRUE or FALSE, in capitals
    robot.Rws.Rapid.SetSymbolValue('RAPID/T_ROB1/MainModule/myFlag', 'TRUE');

    % string: the RAPID quotes are part of the value
    robot.Rws.Rapid.SetSymbolValue('RAPID/T_ROB1/MainModule/myText', '"hello"');
catch ex
    robot.Rws.Mastership.Release(MastershipDomain.Rapid);
    rethrow(ex);
end
robot.Rws.Mastership.Release(MastershipDomain.Rapid);
%%

robot.Disconnect();
```

### RAPID tasks

**C# : RapidTaskList**
```csharp
using UnderAutomation.ABB;
using UnderAutomation.ABB.Rws.Data;

public class RapidTaskList
{
    static void Main()
    {
        AbbController robot = new AbbController();
        robot.Connect("192.168.0.1");

        /**/
        // Every RAPID task of the controller
        foreach (RapidTaskItem task in robot.Rws.Rapid.GetTasks())
        {
            Console.WriteLine(task.Name);                 // T_ROB1
            Console.WriteLine(task.Type);                 // Normal, Static or SemiStatic
            Console.WriteLine(task.TaskState);            // Linked when the program is ready to run
            Console.WriteLine(task.ExecutionState);       // Started, Stopped, Ready
            Console.WriteLine(task.Active);               // null when the controller did not report it
            Console.WriteLine(task.MotionTask);           // true for the task that drives the robot
        }

        // Everything the controller knows about one task
        RapidTaskInfo info = robot.Rws.Rapid.GetTask("T_ROB1");
        Console.WriteLine(info.ExecutionLevel);           // Normal, Trap, User, None
        Console.WriteLine(info.ExecutionCycle);           // Forever, Once, OnceDone
        Console.WriteLine(info.ExecutionMode);            // Continuous, StepIn, StepOver, ...
        Console.WriteLine(info.ProductionEntryPoint);
        Console.WriteLine(info.Trust);
        /**/

        robot.Disconnect();
    }
}
```

**Python : RapidTaskList**
```python
from underautomation.abb.abb_controller import AbbController

robot = AbbController()
robot.connect("192.168.0.1")

##
# Every RAPID task of the controller
for task in robot.rws.rapid.get_tasks():
    print(task.name)             # T_ROB1
    print(task.type)             # Normal, Static or SemiStatic
    print(task.task_state)       # Linked when the program is ready to run
    print(task.execution_state)  # Started, Stopped, Ready
    print(task.active)           # None when the controller did not report it
    print(task.motion_task)      # True for the task that drives the robot

# Everything the controller knows about one task
info = robot.rws.rapid.get_task("T_ROB1")
print(info.execution_level)   # Normal, Trap, User, None
print(info.execution_cycle)   # Forever, Once, OnceDone
print(info.execution_mode)    # Continuous, StepIn, StepOver, ...
print(info.production_entry_point)
print(info.trust)
##

robot.disconnect()
```

**Matlab : RapidTaskList**
```matlab
NET.addAssembly(fullfile(pwd, 'net48', 'UnderAutomation.ABB.dll'));
import UnderAutomation.ABB.*

robot = AbbController();
robot.Connect('192.168.0.1');

%%
tasks = robot.Rws.Rapid.GetTasks();

for i = 1:tasks.Length
    task = tasks(i);

    % char() gives the name of an enumeration value: Started, Stopped, Ready...
    fprintf('%s: %s, %s\n', char(task.Name), char(task.Type), char(task.ExecutionState));
end
%%

robot.Disconnect();
```

## Known limits

- Use the synchronous methods. The asynchronous methods (`...Async`) return a .NET `Task`, which Matlab cannot await.
- Matlab has no named arguments: pass all the arguments of a .NET method, in order.
- An exception of the SDK is a `NET.NetException` in Matlab. Its `ExceptionObject` property holds the .NET exception, with its `Message`. For an `RwsException`, `ExceptionObject.StatusCode` is the HTTP status of the answer.
- Matlab keeps the DLL loaded until it closes.

See also the Matlab pages on the limits of [.NET arrays](https://www.mathworks.com/help/matlab/matlab_external/limitations-to-support-of-net-arrays.html), [.NET methods](https://www.mathworks.com/help/matlab/matlab_external/limitations-to-support-of-net-methods.html) and [.NET enumerations](https://www.mathworks.com/help/matlab/matlab_external/limitations-to-net-enumerations.html).

## What to read next

- [Connect to your robot](/abb/documentation/connect): connection parameters, IRC5 and OmniCore, errors.
- [Test with a RobotStudio virtual controller](/abb/documentation/virtual-controller): work without a real robot.
- [Robot Web Services overview](/abb/documentation/rws): the services of the SDK, one page per topic.
- [Licensing](/abb/documentation/license): the 30 day trial and the license key.