The SDK talks to the robot controller over Robot Web Services (RWS), the HTTP interface that ABB controllers expose on the network. Nothing has to be installed on the robot.

Two classes can open a connection:

- `AbbController` : the main entry point. It holds the connection parameters and gives access to every protocol.
- `RwsClient` : a standalone RWS client, when you only need RWS and prefer a smaller object.

## Quick connection

Pass an IP address and you are connected. The default parameters match an OmniCore controller with its factory user account.

**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();
```

Do not know the address yet? [Discover the controllers of the network](/abb/documentation/discover-controllers) instead of typing one.

## Full connection parameters

`ConnectionParameters` gives access to every option. Use it when the controller is not on its default port, uses HTTPS, or runs RobotWare 6.

**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 `PingBeforeConnect` is `true` (default), the SDK sends an ICMP ping before the first HTTP request. An unreachable robot then fails in a few milliseconds instead of waiting for the HTTP timeout. Set it to `false` when ICMP is blocked on your network.

The default user account of an ABB controller is `Default User` with the password `robotics`. Change it if your controller uses a dedicated account. The account must have the User Authorization System (UAS) grants for what you want to do: reading is always allowed, writing needs the matching grant.

## IRC5 or OmniCore

One API covers the two generations of controllers. Only the `Version` property changes.

| Controller | RobotWare     | RWS version | `RwsVersion` value |
| ---------- | ------------- | ----------- | ------------------ |
| IRC5       | 6 and earlier | RWS 1.0     | `Irc5_V1_0`        |
| OmniCore   | 7 and later   | RWS 2.0     | `OmniCore_V2_0`    |

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

public class ConnectVersion
{
    static void Main()
    {
        /**/
        // IRC5 controller, RobotWare 6 : RWS 1.0
        ConnectionParameters irc5 = new ConnectionParameters("192.168.0.1");
        irc5.Rws.Version = RwsVersion.Irc5_V1_0;

        // OmniCore controller, RobotWare 7 : RWS 2.0
        ConnectionParameters omniCore = new ConnectionParameters("192.168.0.2");
        omniCore.Rws.Version = RwsVersion.OmniCore_V2_0;

        // UseHttps is independent of the version. Set it to match how this
        // particular controller is configured on the network, not its generation.
        omniCore.Rws.UseHttps = true;

        AbbController robot = new AbbController();
        robot.Connect(omniCore);

        // The same code then works on both controllers
        Console.WriteLine(robot.Rws.System.GetInfo().Version);
        /**/
    }
}
```

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

##
# IRC5 controller, RobotWare 6 : RWS 1.0
irc5 = ConnectionParameters("192.168.0.1")
irc5.rws.version = RwsVersion.Irc5_V1_0

# OmniCore controller, RobotWare 7 : RWS 2.0
omni_core = ConnectionParameters("192.168.0.2")
omni_core.rws.version = RwsVersion.OmniCore_V2_0

# use_https is independent of the version. Set it to match how this
# particular controller is configured on the network, not its generation.
omni_core.rws.use_https = True

robot = AbbController()
robot.connect(omni_core)

# The same code then works on both controllers
print(robot.rws.system.get_info().version)
##
```

`OmniCore_V2_0` is the default. If you connect to an IRC5 without setting the version, the first request fails with a 404 status code.

`UseHttps` is a separate setting from `Version`. Both generations can be configured for HTTP or for HTTPS, this depends on how the controller itself is set up, not on which RWS version it speaks. Check your controller's own network configuration and set `UseHttps` to match. When the controller answers on HTTPS with a self-signed certificate, the SDK accepts it, you do not have to install anything in the certificate store.

The details of both versions are described in [IRC5 or OmniCore: which RWS version](/abb/documentation/irc5-vs-omnicore).

## Standalone RWS client

`RwsClient` connects without `AbbController`. The services are then directly on the client, `client.Controller` instead of `robot.Rws.Controller`.

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

public class ConnectStandalone
{
    static void Main()
    {
        /**/
        // RwsClient talks to the controller without going through AbbController
        RwsClient client = new RwsClient();
        client.Connect("192.168.0.1", "Default User", "robotics", 0, 10000, false, RwsVersion.OmniCore_V2_0);

        Console.WriteLine(client.Controller.GetIdentity().Name);

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

**Python : ConnectStandalone**
```python
from underautomation.abb.rws.rws_client import RwsClient
from underautomation.abb.rws.rws_version import RwsVersion

##
# RwsClient talks to the controller without going through AbbController
client = RwsClient()
client.connect("192.168.0.1", "Default User", "robotics", 0, 10000, False, RwsVersion.OmniCore_V2_0)

print(client.controller.get_identity().name)

client.disconnect()
##
```

## Synchronous and asynchronous

Every service method exists twice: a synchronous version, and an asynchronous one with the same name followed by `Async` and an optional `CancellationToken`.

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

public class ConnectAsync
{
    static async Task Main()
    {
        AbbController robot = new AbbController();
        robot.Connect("192.168.0.1");

        /**/
        // Every service method has an asynchronous twin, with a cancellation token
        var identity = await robot.Rws.Controller.GetIdentityAsync();
        var tasks = await robot.Rws.Rapid.GetTasksAsync();

        using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
        {
            var state = await robot.Rws.Panel.GetControllerStateAsync(cts.Token);
            Console.WriteLine(state);
        }
        /**/

        Console.WriteLine($"{identity.Name} runs {tasks.Length} RAPID tasks");
    }
}
```

The asynchronous methods are not available on .NET Framework 3.5 and 4.0, which have no `async` / `await`. Everything else in the SDK works on those versions.

## Errors

Every RWS failure is reported as an `RwsException`. It carries the HTTP status code and, when the controller sends one, the ABB error code and message.

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

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

        /**/
        try
        {
            robot.Rws.Panel.SetSpeedRatio(50);
        }
        catch (RwsException ex)
        {
            // StatusCode is the HTTP status returned by the controller
            // 403 usually means that another client holds the mastership
            Console.WriteLine($"RWS error {ex.StatusCode} : {ex.RwsErrorMessage}");
            Console.WriteLine($"ABB error code : {ex.RwsErrorCode}");
            Console.WriteLine($"Raw response : {ex.ResponseBody}");
        }
        /**/
    }
}
```

**Python : ConnectErrors**
```python
from underautomation.abb.abb_controller import AbbController
from UnderAutomation.ABB.Rws import RwsException

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

##
try:
    robot.rws.panel.set_speed_ratio(50)
except RwsException as ex:
    # The exception comes from the .NET runtime, so its members keep their original names
    # StatusCode is the HTTP status returned by the controller
    # 403 usually means that another client holds the mastership
    print(f"RWS error {ex.StatusCode} : {ex.RwsErrorMessage}")
    print(f"ABB error code : {ex.RwsErrorCode}")
    print(f"Raw response : {ex.ResponseBody}")
##
```

Common status codes:

| Status | Meaning                                                                                                           |
| ------ | ----------------------------------------------------------------------------------------------------------------- |
| 400    | The controller refused the value, for example a speed ratio out of range                                          |
| 403    | Another client holds the [mastership](/abb/documentation/rws-mastership), or the user account lacks the UAS grant |
| 404    | The resource does not exist on this controller, often a wrong `RwsVersion`                                        |
| 500    | The controller could not run the operation in its current state                                                   |

## Disconnect

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

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

        /**/
        // Always disconnect : the controller keeps a limited number of sessions open
        robot.Disconnect();

        Console.WriteLine(robot.Enabled); // False
        /**/
    }
}
```

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

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

##
# Always disconnect : the controller keeps a limited number of sessions open
robot.disconnect()

print(robot.enabled)  # False
##
```

A controller accepts a limited number of simultaneous sessions, around 70 on OmniCore. An application that connects in a loop without disconnecting exhausts them, and every following request answers 503.

## Try it in the demo application

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

![Connection page of the ABB SDK demo application](/abb/WinformsScreenshots/Connect.jpg)

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

## API reference

**Members of ConnectionParameters**
```csharp
public class ConnectionParameters {
    // Instantiate new connection parameters with default values
    public ConnectionParameters()

    // Instantiate new connection parameters with a specified address
    public ConnectionParameters(string address)

    // Address of the robot controller (IP or host name), default value is 127.0.0.1
    public string Address { get; set; }

    public override bool Equals(object obj)

    public override int GetHashCode()

    // Send a ping command before initializing any connections
    public bool PingBeforeConnect { get; set; }

    // RWS2 (Robot Web Services 2) connection parameters
    public RwsConnectParameters Rws { get; set; }

    public override string ToString()
}
```

**Members of Rws.RwsConnectParameters**
```csharp
public class RwsConnectParameters : RwsConnectParametersBase {
    public RwsConnectParameters()

    // Default password for Digest Authentication
    public const string DEFAULT_PASSWORD = "robotics"

    // Default RWS port (80 for HTTP, 443 for HTTPS)
    public const int DEFAULT_PORT = 80

    // Default timeout in milliseconds
    public const int DEFAULT_TIMEOUT = 10000

    // Default username for Digest Authentication
    public const string DEFAULT_USERNAME = "Default User"

    // Enable or disable the RWS client connection
    public bool Enable { get; set; }
}
```

**Members of Rws.Internal.RwsConnectParametersBase**
```csharp
public abstract class RwsConnectParametersBase {
    protected RwsConnectParametersBase()

    // Password for Digest Authentication (Default is "robotics")
    public string Password { get; set; }

    // RWS service port (if set to 0, the SDK will use 80 for HTTP, 443 for HTTPS)
    public int Port { get; set; }

    // HTTP request timeout in milliseconds (default: 1000ms)
    public int Timeout { get; set; }

    // Whether to use HTTPS instead of HTTP (default: false)
    public bool UseHttps { get; set; }

    // Username for Digest Authentication (Default is "Default User")
    public string Username { get; set; }

    // RWS protocol version to use. If not specified, <xref href="UnderAutomation.ABB.Rws.RwsVersion.OmniCore_V2_0" data-throw-if-not-resolved="false"></xref> (RWS 2.0) is used.
    // 
    // <p>RWS 2.0 is available in RobotWare &gt;= 7, which ships the new OmniCore controller generation.
    // For older RobotWare versions running on IRC5 controllers, use <xref href="UnderAutomation.ABB.Rws.RwsVersion.Irc5_V1_0" data-throw-if-not-resolved="false"></xref> (RWS 1.0).</p>
    public RwsVersion Version { get; set; }
}
```

**Members of Rws.RwsVersion**
```csharp
public enum RwsVersion {
    // RWS 1.0, exposed by IRC5 controllers running RobotWare 6 and earlier.
    Irc5_V1_0 = 10

    // RWS 2.0, exposed by OmniCore controllers running RobotWare 7 and later.
    // This is the default when no version is specified.
    OmniCore_V2_0 = 20
}
```

**Members of Rws.RwsException**
```csharp
public class RwsException : Exception, ISerializable {
    // Creates a new RWS exception with a message
    public RwsException(string message)

    // Creates a new RWS exception with a message and inner exception
    public RwsException(string message, Exception innerException)

    // Creates a new RWS exception with a message, status code and response body
    public RwsException(string message, int statusCode, string responseBody)

    // Creates a new RWS exception with a message and the raw response body
    public RwsException(string message, string responseBody)

    // Creates a new RWS exception that explains the failure of another one, keeping its diagnostics
    public RwsException(string message, RwsException innerException)

    // HTTP reason phrase returned by the server (e.g. "Forbidden", "Method Not Allowed"), if available
    public string ReasonPhrase { get; }

    // Raw response body from the server, if available
    public string ResponseBody { get; }

    // ABB internal error code extracted from the RWS error payload (e.g. "-1073445865"), if present
    public string RwsErrorCode { get; }

    // Human readable error text extracted from the RWS error payload, if present
    public string RwsErrorMessage { get; }

    // HTTP status code returned by the server
    public int? StatusCode { get; }
}
```