## 🌍 What's a Variable in This Chapter?

In this chapter, when we say "variable," we're talking about **global variables** in the UR controller. These can be **program variables** or **installation variables**.

In our library, regardless of the protocol used to handle variables, they are represented by the `GlobalVariable` class, which contains both the **name** and **value** of the variable.  
This class extends `GlobalVariableValue`, which only holds the value. Simple, right? 🚀

---

## 📡 Reading Variables with Dashboard

The **Dashboard Server Protocol** lets you query the robot for a variable's value.

The function that makes this magic happen is `GetVariable`. It returns a `GlobalVariableValue` object containing the **name** and **value** of the variable. The type? It's automatically estimated from the value! 🧠✨

**C# : DashboardVariable**
```csharp
﻿using UnderAutomation.UniversalRobots;
using UnderAutomation.UniversalRobots.Common;

class DashboardVariable
{
  static void Main(string[] args)
  {
    var robot = new UR();

    // Dashboard is enabled by default
    robot.Connect("192.168.0.1");

    /**/
    // Read variable called "myVar"
    var request = robot.Dashboard.GetVariable("myVar");

    if (request.Succeed)
    {
      GlobalVariable variable = request.Value;
      // variable.Name
      // variable.Type
      // variable.Value
      // variable.ToMatrix();
      // variable.ToFloat();
      // variable.To...();
    }
    /**/
  }
}
```

---

## 🔄 Reading Variables with Primary Interface

The **Primary Interface** is a bit more dynamic, it **raises an event** whenever a variable's value **changes** or when the **variable list** is updated.  
This means the library always keeps track of the **latest received values** for each variable, so you can access their **name, type, and value** at any time. 🚦

⚠ **Heads-up!** If your application connects **while a program is running**, you'll only get the list of variables **when a change occurs**.  
That means the list might initially be empty, because the Primary Interface only reacts to updates! ⏳

💡 **Good to know:** The library adapts to different **controller firmware versions**.  
Since **version 3.2**, and later **5.9**, the way variables are decoded has changed. But don't worry, it's completely **seamless** for developers! 🎩✨

Two events help you stay updated:

- 📝 `ListUpdated` → Triggered when the **variable list changes**.
- 🔔 `ValuesUpdated` → Triggered when **a variable's value changes**.

The **Primary Interface** has a `GlobalVariables` property, which gives you **access to the list of variables** and the associated events.

**C# : PrimaryInterfaceVariable**
```csharp
using UnderAutomation.UniversalRobots;
using UnderAutomation.UniversalRobots.Common;
using UnderAutomation.UniversalRobots.PrimaryInterface;

class PrimaryInterfaceVariable
{
  static void Main(string[] args)
  {
    var robot = new UR();

    // Primary Interface is enabled by default
    robot.Connect("192.168.0.1");

    /**/
    // Be notified when the list of variables changes
    robot.PrimaryInterface.GlobalVariables.ListUpdated += GlobalVariables_ListUpdated;

    // Be notified when at least one variable changes value
    robot.PrimaryInterface.GlobalVariables.ValuesUpdated += GlobalVariables_ValuesUpdated;

    // Get the list of all variables
    GlobalVariable[] variables = robot.PrimaryInterface.GlobalVariables.GetAll();

    if (variables.Length == 0) return;

    // Get first variable
    var variable = variables[0];

    // Get variable name
    string name = variable.Name;

    // Get variable generic value
    object value = variable.Value;

    // Get its value according to its type
    switch (variable.Type)
    {
      case GlobalVariableTypes.None:
        // Variable is empty or doesn't exist
        break;
      case GlobalVariableTypes.String:
        string stringValue = variable.ToString();
        break;
      case GlobalVariableTypes.List:
        GlobalVariableValue[] listValue = variable.ToList();
        break;
      case GlobalVariableTypes.Pose:
        Pose poseValue = variable.ToPose();
        break;
      case GlobalVariableTypes.Bool:
        bool boolValue = variable.ToBool();
        break;
      case GlobalVariableTypes.Int:
        int intValue = variable.ToInt();
        break;
      case GlobalVariableTypes.Float:
        float floatValue = variable.ToFloat();
        break;
      case GlobalVariableTypes.Matrix:
        System.Array matrixValue = variable.ToMatrix();
        break;
    }
    /**/
  }

  private static void GlobalVariables_ValuesUpdated(object sender, GlobalVariablesEventArgs e)
  {
    // Get new list of variable values
    GlobalVariable[] variables = e.Variables;
  }

  private static void GlobalVariables_ListUpdated(object sender, GlobalVariablesEventArgs e)
  {
    // Get new list of variable values
    GlobalVariable[] variables = e.Variables;
  }
}
```

---

## ✍️ Writing Variables

Unfortunately, **writing a variable while a program is running is not possible**. 😕  
This is because none of the protocols exposed by UR controllers allow it.

That said, you _can_ [send a script](/universal-robots/documentation/remote-send-script) like this:

```ur
global myVar = 12
```

### 🛠️ The Hidden Trick 🤫

However, be warned, this will **stop the currently running program**! ❌  
So, if you don't have an active program and just need to set an **installation variable**, this method works perfectly.  
For example, you could **initialize an environment variable** before starting your program.

Here's a little undocumented trick straight out of nowhere:  
To **ensure** the new installation variable value is actually **saved**, you need to set `_hidden_verificationVariable = 0`.  
Why? Because this is how **Polyscope gets notified** of the change! 🧙‍♂️✨

This is an example of how to set the environment variable `i_var_1 = 6`:

```csharp
_robot.PrimaryInterface.Script.Send(@"
def SetInstallationVariable():
    global _hidden_verificationVariable=0
    global i_var_1=6
end");
```

### 🚫 No Global Variable Writing in a Secondary Program

Another limitation to keep in mind: **writing to global variables is not allowed within a secondary program**.  
So, if you need to modify variables dynamically, you might need to rethink your approach! 🤔

### 🔄 Best Practices for Exchanging Values with Your Program

Variables are super handy for sending **data from the robot to your software**.  
As mentioned earlier, when using the **Primary Interface**, you need to be **connected right from the start** of program execution to track variables.

But what if you need **real-time communication** during execution? 🤨  
For **high-frequency read/write operations**, **registers** are the way to go! 🚀

- Registers are designed for **general-purpose usage**.
- They can be **read and written at high speed** using **RTDE** and/or **Primary Interface**.

So, if you're looking for **seamless live data exchange**, **use registers instead of standard variables!** 🏎️💨

---

## 🖥️ Try It Out with the Windows Example!

Want to see this in action? A special **WinForms** page lets you test variable reading live! 🎮  
👉 [Download it here](/universal-robots/download) and give it a spin!

![](/universal-robots/WinformsScreenshots/Program.jpg)

---

## 📚 Available Classes

Here are the key classes you'll be working with:

**Members of Common.GlobalVariable**
```csharp
public class GlobalVariable : GlobalVariableValue {
    public GlobalVariable()

    // Variable name
    public string Name { get; }

    // Last time the variable was sampled
    public TimeSpan Time { get; }
}
```
**Members of Common.GlobalVariableValue**
```csharp
public class GlobalVariableValue {
    public GlobalVariableValue()

    // Estimate variable value from its string representation
    public static GlobalVariableValue Parse(string message)

    // Returns variable value if type is Bool. Il type is Float or Int, it returns True if value is not 0. Else, it returns false
    public bool ToBool()

    // Returns variable value if type is Float. Il type is int, it casts it to float. If Type is bool, it returns 1 or 0. Else it returns NaN
    public float ToFloat()

    // Returns variable value if type is Int. Il type is Float, it tries to cast it to int. If Type is bool, it returns 1 or 0. Else it returns 0
    public int ToInt()

    // Returns an array of GlobalVariableValue if Type is List. Else, null is returned
    public GlobalVariableValue[] ToList()

    // Return variable value GlobalVariable[,] if variable is a matrix. First dimension is row index and second dimension is column index.
    // Use GetLength(0) to get row number and GetLength(1) to get column count
    public Array ToMatrix()

    // Returns a Pose if Type is Pose. Else, null is returned
    public Pose ToPose()

    // Returns a string that describes the variable, regardless of its type
    public override string ToString()

    // Type of a variable
    public GlobalVariableTypes Type { get; }

    // Value of the variable
    public object Value { get; }
}
```
**Members of Common.GlobalVariableTypes**
```csharp
public enum GlobalVariableTypes {
    // Variable value is bool
    Bool = 4

    // Variable value is float
    Float = 6

    // Variable value is int
    Int = 5

    // Variable value is an array : GlobalVariableValue[]
    List = 2

    // Variable value is a matrix
    Matrix = 7

    // Variable value is null, the value has not been assigned yet
    None = 0

    // Variable value is a UnderAutomation.UniversalRobots.Pose
    Pose = 3

    // Variable value is a System.String
    String = 1
}
```
**Members of PrimaryInterface.GlobalVariables**
```csharp
public class GlobalVariables {
    // Indicates which decoder is used used to read variables according to firmware version
    public GlobalVariablesFirmwareVersion FirmwareVersion { get; }

    // Returns a list of all variables declared in the robot
    public GlobalVariable[] GetAll()

    // Get a variable by its name. Null is returned if the variable doesn't exist
    public GlobalVariable GetByName(string name)

    // Event raised whan the variable list changed. For example, after a program starts
    public event EventHandler<GlobalVariablesEventArgs> ListUpdated

    // Return a string where each line contains type, name and variable value
    public override string ToString()

    // Event raised at 10Hz when variable values are updated
    public event EventHandler<GlobalVariablesEventArgs> ValuesUpdated
}
```

---

Now you're all set to handle variables like a pro! 🚀 Happy coding! 🎉