This page shows how to read the description of a Staubli CS8 or CS9 controller and of its robots: the arms, the controller parameters, the Denavit-Hartenberg parameters and the joint ranges. These calls only read: they never change the state of the controller.

## Robots of the controller

`GetRobots()` returns one `Robot` per arm driven by the controller. The index of an arm in this array is the `robot` number that the other methods take.

**C# : ControllerRobots**
```csharp
using UnderAutomation.Staubli;
using UnderAutomation.Staubli.Soap.Data;

public class ControllerRobots
{
    static void Main()
    {
        var controller = new StaubliController();
        controller.Connect("192.168.0.254");

        /**/
        // The robots driven by the controller. The index in this array
        // is the robot number that the other methods take.
        Robot[] robots = controller.Soap.GetRobots();

        for (int i = 0; i < robots.Length; i++)
        {
            Robot robot = robots[i];
            Console.WriteLine($"Robot {i}: {robot.Arm}"); // for example TX2-60
            Console.WriteLine($"  Kinematic: {robot.Kinematic}"); // Anthropomorph6, Scara...
            Console.WriteLine($"  Mount type: {robot.MountType}"); // Floor, Ceiling, Wall
            Console.WriteLine($"  Tuning: {robot.Tuning}");
        }
        /**/

        controller.Disconnect();
    }
}
```

**Python : ControllerRobots**
```python
from underautomation.staubli.staubli_controller import StaubliController

controller = StaubliController()
controller.connect("192.168.0.254")

##
# The robots driven by the controller. The index in this list
# is the robot number that the other methods take.
robots = controller.soap.get_robots()

for i, robot in enumerate(robots):
    print(f"Robot {i}: {robot.arm}")  # for example TX2-60
    print(f"  Kinematic: {robot.kinematic.name}")  # Anthropomorph6, Scara...
    print(f"  Mount type: {robot.mount_type.name}")  # Floor, Ceiling, Wall
    print(f"  Tuning: {robot.tuning}")
##

controller.disconnect()
```

`Kinematic` gives the type of arm: `Anthropomorph6` for a 6 axis arm like the TX2 series, `Scara` for a SCARA like the TS2 series, and the other values of the `Kinematic` enumeration.

## Controller parameters

`GetControllerParameters()` returns the parameters of the controller, as key, name and value.

**C# : ControllerParameters**
```csharp
using UnderAutomation.Staubli;
using UnderAutomation.Staubli.Soap.Data;

public class ControllerParameters
{
    static void Main()
    {
        var controller = new StaubliController();
        controller.Connect("192.168.0.254");

        /**/
        Parameter[] parameters = controller.Soap.GetControllerParameters();

        foreach (Parameter parameter in parameters)
            Console.WriteLine($"{parameter.Key} | {parameter.Name} = {parameter.Value}");
        /**/

        controller.Disconnect();
    }
}
```

**Python : ControllerParameters**
```python
from underautomation.staubli.staubli_controller import StaubliController

controller = StaubliController()
controller.connect("192.168.0.254")

##
parameters = controller.soap.get_controller_parameters()

for parameter in parameters:
    print(f"{parameter.key} | {parameter.name} = {parameter.value}")
##

controller.disconnect()
```

The list and the values depend on the controller and on its version. Read the list once on your controller to find the keys you need.

## Denavit-Hartenberg parameters

`GetDhParameters(robot)` returns one `DhParameters` per joint of the arm, as the controller defines its geometry.

**C# : ControllerDhParameters**
```csharp
using UnderAutomation.Staubli;
using UnderAutomation.Staubli.Soap.Data;

public class ControllerDhParameters
{
    static void Main()
    {
        var controller = new StaubliController();
        controller.Connect("192.168.0.254");

        /**/
        // One entry per joint of the first robot
        DhParameters[] dh = controller.Soap.GetDhParameters(robot: 0);

        for (int i = 0; i < dh.Length; i++)
            Console.WriteLine($"J{i + 1}: theta={dh[i].Theta} d={dh[i].D} a={dh[i].A} alpha={dh[i].Alpha} beta={dh[i].Beta}");
        /**/

        controller.Disconnect();
    }
}
```

**Python : ControllerDhParameters**
```python
from underautomation.staubli.staubli_controller import StaubliController

controller = StaubliController()
controller.connect("192.168.0.254")

##
# One entry per joint of the first robot
dh = controller.soap.get_dh_parameters(0)

for i, joint in enumerate(dh):
    print(f"J{i + 1}: theta={joint.theta} d={joint.d} a={joint.a} alpha={joint.alpha} beta={joint.beta}")
##

controller.disconnect()
```

## Joint ranges

`GetJointRange(robot)` returns the software limits of each joint, in radians. The inverse kinematics uses them to reject a solution out of range: see [Kinematics](/staubli/documentation/soap-kinematics).

**C# : ControllerJointRange**
```csharp
using UnderAutomation.Staubli;
using UnderAutomation.Staubli.Soap.Data;

public class ControllerJointRange
{
    static void Main()
    {
        var controller = new StaubliController();
        controller.Connect("192.168.0.254");

        /**/
        // Software limits of each joint of the first robot, in radians
        JointRange range = controller.Soap.GetJointRange(robot: 0);

        for (int i = 0; i < range.Min.Length; i++)
            Console.WriteLine($"J{i + 1}: {range.Min[i]} to {range.Max[i]}");
        /**/

        controller.Disconnect();
    }
}
```

**Python : ControllerJointRange**
```python
from underautomation.staubli.staubli_controller import StaubliController

controller = StaubliController()
controller.connect("192.168.0.254")

##
# Software limits of each joint of the first robot, in radians
joint_range = controller.soap.get_joint_range(0)

for i, (low, high) in enumerate(zip(joint_range.min, joint_range.max)):
    print(f"J{i + 1}: {low} to {high}")
##

controller.disconnect()
```

## Try it in the demo application



## Reference

**Methods of SoapClientBase**
```csharp
// Get the current Cartesian position of a robot end effector
Parameter[] GetControllerParameters();

// Get Robot DH parameters
DhParameters[] GetDhParameters(int robot = 0);

// Get the range Min-Max of each joint of a robot
JointRange GetJointRange(int robot = 0);

// Get all the robots handled by this controller
Robot[] GetRobots();
```

Every method also exists in an asynchronous version, with the same name followed by `Async` and an optional `CancellationToken`.

**Members of Soap.Data.Robot**
```csharp
public class Robot {
    // Initializes a new instance of the <xref href="UnderAutomation.Staubli.Soap.Data.Robot" data-throw-if-not-resolved="false"></xref> class.
    public Robot()

    // Arm model identifier.
    public string Arm { get; set; }

    // Diameter of the third axis.
    public DiameterAxis3 DiameterAxis3 { get; set; }

    public override bool Equals(object obj)

    public override int GetHashCode()

    // Kinematic type of the robot.
    public Kinematic Kinematic { get; set; }

    // Length of the third axis.
    public LengthAxis3 LengthAxis3 { get; set; }

    // Mounting type of the robot (floor, ceiling, wall).
    public MountType MountType { get; set; }

    public override string ToString()

    // Tuning identifier.
    public string Tuning { get; set; }
}
```

**Members of Soap.Data.Kinematic**
```csharp
public enum Kinematic {
    // 6-axis anthropomorphic parallel robot.
    Anthrioparallel6 = 2

    // 5-axis anthropomorphic robot.
    Anthropomorph5 = 3

    // 6-axis anthropomorphic robot.
    Anthropomorph6 = 1

    // Eisenmann kinematic type.
    Eisenmann = 5

    // Invalid or unknown kinematic type.
    Invalid = 0

    // SCARA robot.
    Scara = 4
}
```

**Members of Soap.Data.MountType**
```csharp
public enum MountType {
    // Robot is ceiling-mounted.
    Ceiling = 2

    // Robot is floor-mounted.
    Floor = 1

    // Invalid or unknown mount type.
    Invalid = 0

    // Robot is wall-mounted.
    Wall = 3
}
```

**Members of Soap.Data.Parameter**
```csharp
public class Parameter {
    // Initializes a new instance of the <xref href="UnderAutomation.Staubli.Soap.Data.Parameter" data-throw-if-not-resolved="false"></xref> class.
    public Parameter()

    public override bool Equals(object obj)

    public override int GetHashCode()

    // Parameter key identifier.
    public string Key { get; set; }

    // Display name of the parameter.
    public string Name { get; set; }

    public override string ToString()

    // Value of the parameter.
    public string Value { get; set; }
}
```

**Members of Soap.Data.DhParameters**
```csharp
public class DhParameters {
    // Initializes a new instance of the <xref href="UnderAutomation.Staubli.Soap.Data.DhParameters" data-throw-if-not-resolved="false"></xref> class.
    public DhParameters()

    // Link length a (m).
    public double A { get; set; }

    // Link twist alpha (radians).
    public double Alpha { get; set; }

    // Joint twist beta (radians).
    public double Beta { get; set; }

    // Link offset d (m).
    public double D { get; set; }

    public override bool Equals(object obj)

    public override int GetHashCode()

    // Joint angle theta (radians).
    public double Theta { get; set; }

    public override string ToString()
}
```

**Members of Soap.Data.JointRange**
```csharp
public class JointRange {
    // Initializes a new instance of the <xref href="UnderAutomation.Staubli.Soap.Data.JointRange" data-throw-if-not-resolved="false"></xref> class.
    public JointRange()

    public override bool Equals(object obj)

    public override int GetHashCode()

    // Maximum values for each joint (radians).
    public double[] Max { get; set; }

    // Minimum values for each joint (radians).
    public double[] Min { get; set; }

    public override string ToString()
}
```