Getting data (C# client version)

The TET C# Client supplied with the SDK simplifies connectivity and parsing data. Although this is a sample implementation we recommend that the reference clients are used when possible as they have been verified to work with the EyeTribe Server. If you do not want to use this a simple plain C# example is provided further down.

To get started we first import the TETCSharpClient.dll to the project.

In the constructor of our application we need to Activate with the GazeManager. The first parameter indicated the API version. We have chosen to provide an enum here that will list the versions this specific client build is compatible with. The second parameter specifies that we want data continuously pushed to our application (the alternative is pull, to manually fetch data by request). 

Classes that wish to receive gaze data should implement the IGazeListener interface. This interface contains three methods where the OnGazeUpdate is of most interest since it contains the coordinates of the estimated on-screen gaze position, size of the pupils, position relative to the sensor etc.

Note that the Eye Tracker Server must be calibrated before it outputs gaze data.

public class GazePoint : IGazeListener 
{
     public GazePoint()
     {
// Connect client GazeManager.Instance.Activate(GazeManager.ApiVersion.VERSION_1_0, GazeManager.ClientMode.Push);

// Register this class for events GazeManager.Instance.AddGazeListener(this); } public void OnGazeUpdate(GazeData gazeData) { double gX = gazeData.SmoothedCoordinates.X; double gY = gazeData.SmoothedCoordinates.Y; // Move point, do hit-testing, log coordinates etc. } }

Getting data (Plain version)

In order to fetch the data stream coming out of the Tracker Server we need to do three things,

  1. connect a tcp socket
  2. spawn a thread to parse the data
  3. create a timer that sends heartbeats to keep the connection alive.

Lets look at how we can accomplish this in C#. This is a simplified version that demonstrates the basic concepts of obtaining data from the server.

First we define three objects.

private TcpClient socket;
private Thread incomingThread;
private System.Timers.Timer timerHeartbeat;

The socket is to be connected to the “localhost” on port “6555” (default values). Using the same socket we then send a simple connect request that specifies what version of the API we will be using. In order to continuously receive data we spawn a thread that reads data from the socket. The json data received is then parsed into an object called Packet where Key-Value pairs can be parsed further.

Since we want to keep the connection with the server alive we are required to send heartbeat requests at certain intervals. To achieve this we create a timer that ticks every N milliseconds and sends a heartbeat request over the socket.

First, lets create a Connect() method that connects the socket, starts the listener thread and the heartbeat timer.

public bool Connect(string host, int port)
{
    try
    {
       socket = new TcpClient(“localhost”, 6555);
    }
    catch (Exception ex)
    {
        Console.Out.WriteLine("Error connecting: " + ex.Message);
        return false;
    }

    // Send the obligatory connect request message
    string REQ_CONNECT = "{\"values\":{\"push\":true,\"version\":1},\"category\":\"tracker\",\"request\":\"set\"}"; 
    Send(REQ_CONNECT);

    // Lauch a seperate thread to parse incoming data
    incomingThread = new Thread(ListenerLoop);
    incomingThread.Start();

    // Start a timer that sends a heartbeat every 250ms.
    // The minimum interval required by the server can be read out 
    // in the response to the initial connect request.   

    string REQ_HEATBEAT = "{\"category\":\"heartbeat\",\"request\":null}";
    timerHeartbeat = new System.Timers.Timer(250);
    timerHeartbeat.Elapsed += delegate { Send(REQ_HEATBEAT); };
    timerHeartbeat.Start();

    return true;
}

The Send(string message) method is used to send data back to the server. In this example these messages consists of the initial connect request and subsequent heartbeats.

private void Send(string message)
{
    if (socket != null && socket.Connected)
    {
        StreamWriter writer = new StreamWriter(socket.GetStream());
        writer.WriteLine(message);
        writer.Flush();
    }
}

From the Connect() method we spawn a new thread that reads data from the socket and parses the json messages into a “Packet” object. We use the Newtonsoft Json library to handle the parsing. Once a packet has been parsed we raise an event with the data.

public event EventHandler<ReceivedDataEventArgs> OnData;

private void ListenerLoop()
{
    StreamReader reader = new StreamReader(socket.GetStream());
    isRunning = true;

    while (isRunning)
    {
       string response = string.Empty;

        try
        {
            response = reader.ReadLine();

            JObject jObject = JObject.Parse(response);

            Packet p = new Packet();
            p.RawData = json;

            p.Category = (string)jObject["category"];
            p.Request = (string)jObject["request"];
            p.StatusCode = (string)jObject["statuscode"];

            JToken values = jObject.GetValue("values");

            if (values != null)
            {
/* We can further parse the Key-Value pairs from the values here. For example using a switch on the Category and/or Request to create Gaze Data or CalibrationResult objects and pass these via separate events. */ } // Raise event with the data if(OnData != null) OnData(this, new ReceivedDataEventArgs(p)); } catch (Exception ex) { Console.Out.WriteLine("Error while reading response: " + ex.Message; } } }

We use a simple container class called Packet to hold the data.

public class Packet
{
    public string time = DateTime.UtcNow.Ticks.ToString();
    public string category = string.Empty;
    public string request = string.Empty;
    public string statuscode = string.Empty;
    public string values = string.Empty;
    public string rawData = string.Empty;

    public Packet() { }
}

 The Packet is then broadcasted to event-subscribers by raising a custom event that simply contains the Packet.

public class ReceivedDataEventArgs : EventArgs
{
    private Packet packet;

    public ReceivedDataEventArgs(Packet _packet)
    {
        this.packet = _packet;
    }

    public Packet Packet
    {
        get { return packet; }
    }
}