Setting up a Communication Stream

Communications in qdex use common communication protocols and  associated parameters, often TCP/IP. In this way, all that is required to send and receive data is the IP address of the computer or device. Communications in qdex require two key blocks:

1) A client block to specify the communication protocol and reference address of the stream.

2) A simulation block to send and receive data over that stream. Inside the simulation, the signal and probe blocks are used to send/receive different types of data.

Client Block

The client block is used to define the communication stream. It requires a name and a reference address to work. The URI must specify the protocol, the address, and the port number. You can also write scripts that are triggered when one of the following events occur: 

  1. onConnected - occurs when connection to stream is made..
  2. onDisconnected - occurs when disconnection from stream is made.
  3. onReceive - occurs when data is received from stream.

Note that the client block maintains a persistent connection, which means that it automatically reconnects whenever the connection is lost. To stop this persistent connection the simulation that is referencing the client block must be stopped.

Example

This example uses a client block, that when connected and disconnected, updates the text of a button and notifies you when data is received.

<client name="myClient" uri="tcpip://130.212.47.129:18000">
  <!--Updating button text.-->
  <onConnected>
    myButton.Text = "Disconnect"
  </onConnected>
  <!--Updating button text.-->
  <onDisconnected>
    myButton.Text = "Connect"
  </onDisconnected>
  <!--Notifying user when data is recieved.-->
  <onReceive>
    notify(" Data Received")
  </onReceive>
</client>

Simulation Block

The simulation is used to send and receive data over a stream. A connection with the client will not be established until the simulation has been started and connection will be maitained until simulation stops.

<!--Simulation-->
<simulation name="mySim" period="0.01">
  <solver>
    <series>
      <!--Recieving data, outputting to plot-->
      <signal ref ="mySection.myClient"/>
      <probe ref="mySection.myPlot" />
    </series>
  </solver>
  <!--Sending value from qdex slider-->
  <solver>
   <series>
     <signal ref ="mySection.mySlider"/>
     <probe ref ="mySection.myClient"/>
    </series>
  </solver>
</simulation>

Receiving Data

Once a connection has been established using a signal to the stream, there are two main ways that the received data can be used: it can be sent directly to a qdex element (e.g. plot) using the probe tag, or it can be sent to a Lua variable for calculation or manipulation using the system tag. If receiving many different signals that need to be manipulated in various ways, the demux tag can be used to separate the received data for processing.

Probing to a Plot

Probing received data to a timePlot is the simplest way to use the data, and can be set up using very little code. Take a look at the examples below.

One set of data

If receiving a single data set from the stream, it can be displayed by probing to the plot directly after using the signal tag to reference the stream.

<client name="streamName" uri="tcpip://192.168.0.101:18000" />

<timePlot name="myPlot" />

<simulation period="0.01" name="thisSim">
  <solver name="solver">    
    <series>    
      <signal ref="mySection.streamName" />
      <probe ref="mySection.myPlot" />
    </series>
  </solver>
</simulation>

If receiving two or more sets of data from the stream, they can all be displayed on a single plot by setting the series attribute in the timePlot declaration to the number of series being received. 

<timePlot name="myPlot" series="2" />

The width attribute of the signal must also be set to the number of series being received. 

<signal ref="mySection.streamName" width="2" />


Two sets of data, separate plots

If receiving two or more sets of data from the stream, they can be displayed on separate plots by demultiplexing the received signal and probing to two separate plots inside of a stack. 

<client name="streamName" uri="tcpip://192.168.0.101:18000" />

<timePlot name="myPlot" />
<timePlot name="myOtherPlot" />

<simulation period="0.01" name="thisSim">
  <solver name="solver">    
    <series>    
      <signal ref="mySection.streamName" width="2" />
       <demux outputs="2" />
       <stack>
         <probe ref="mySection.myPlot" />
         <probe ref="mySection.myOtherPlot" />
       </stack>
    </series>
  </solver>
</simulation>

Lua Variable

You can also use the received data to update a Lua variable. The Lua variable can then be manipulated with math, or used to update an animation. Assigning the received data to a Lua variable requires the use of a custom system. 

<client name="myStream" uri="tcpip://192.168.0.101:18000" width="2" />

<script>
  local thisVar, thatVar;
</script>

<simulation period="0.1" name="sim">
  <solver name="solver">
    <series>
      <signal ref="mySection.myStream" width="2" />
      <system name="myCustomSystem">
        <input name="myReceivedData" width="2" />
        <onOutputs>
          thisVar = myReceivedData[1]
          thatVar = myReceivedData[2]
        </onOutputs>
      </system>
    </series>
  </solver>
</simulation>

The data that is received from the stream is fed into the input of the custom system as a vector. The values of the vector are assigned to the lua variables thisVar and thatVar, and are updated each period of the simulation. The simulation onUpdate method could be used to call a function that manipulates the variables.

Receiving Multiple Data Sets

Demultiplexing can be used to separate a number of data sets and manipulate them differently. In the below example, the received signal has a width of 10, and the demux tag is used to separate the received data into three "channels" in a stack:

  • Data sets 1 and 2 are assigned to the first channel, which is a probe that outputs to a plot in a stack
  • Data sets 3-5 are assigned to the second channel, which is a probe that outputs to a different plot in that same stack
  • Data sets 6-9 are assigned to the third channel, and act as the first input to the custom system
  • Data set 10 is assigned to the third channel, and acts as the second input to the custom system
<simulation name="thisSimulation" period="0.01">
  <solver name="mySolver">
    <series>
      <signal ref="mySection.streamName" width="10" />
      <demux outputs="2 3 4 1" />
      <stack>
        <probe ref="mySection.stackName.firstPlot" />
        <probe ref="mySection.stackName.secondPlot" />
        <system>
          <input name="inputOne" width="4" />
          <input name="inputTwo" width="1" />
          <onOutputs>
            local myVector = inputOne
            local myVariable = inputTwo[1]
          </onOutputs>
        </system>
      </stack>
    </series>
  </solver>
</simulation>

This method of demultiplexing the received data allows for the sending/receiving of more complicated stream data, where many different parameters can all be sent to/from qdex using a single stream.

Sending Data

In addition to receiving data over a TCP/IP stream, data can be sent back to your model to control parameters that manipulate the hardware. This is done by using the probe tag, which sends the specified inputs back over the stream. Data can be input to the probe directly from a qdex element (button, slider, etc) using a signal tag, or the value of a Lua variable can be sent using a system tag. 

Signalling from an App Control Element

The values of a button, toggle or slider can be sent directly over the stream by using the signal and probe tags. In this case, the signal tag is used to listen to the slider/button value, while the probe tag is used to send the data back. Sliders send their current value (a number between 0-100 in the example below), while buttons/toggles send either a 0 (not pressed) or a 1 (pressed). 

<client name="myStream" uri="tcpip://172.16.0.62:18000" />

<slider name="sliderName" min="0" max="100" value="50" />

<simulation name="sim" period="0.01">
  <solver name="solver2">
    <series>
      <signal ref="mySection.sliderName"/>
      <probe ref="mySection.myStream" />
    </series>
  </solver>
</simulation>

Sending Lua Values

A custom system can be used to send Lua variables over the stream back to your model. A single point of data can be sent, or multiple values can be combined into a vector.

Single Data Point

If sending a single point of data, it can be directly referenced by name in the onOutputs method of the custom system. In the example below, the variable myVariable is manipulated elsewhere in the code (not shown) before being sent over the stream. . 

<client name="myStream" uri="tcpip://172.16.0.62:18000" />

<simulation name="sim" period="0.01">
  <solver name="solver2">
    <series>
      <system name="mySystem">
        <output name="myOutput" width="1" />
        <onOutputs>
          return myVariable
        </onOutputs>
      </system>
      <probe ref="mySection.myStream" />
    </series>
  </solver>
</simulation>

Vector of Data

The system block can be used to send a Lua vector over the stream. The vector can contain different types of data, including the value of other Lua variables, or the value of an app control element like a toggle or slider.

In the below example, the value of the toggle switch is assigned to the first index of a vector, and the value of a slider is mapped to the second index. Both values are sent over the stream using the custom system. 

<client name="myStream" uri="tcpip://172.16.0.62:18000" />

<script>
  local valueVector = vector(2);
</script>

<toggle name="toggleName">
  <onValueChanged>
    valueVector[1] = toggleName.Value;
  </onValueChanged>
</toggle>

<slider name="mySlider" min="0" max="15" value="0">
  <onValueChanged>
    valueVector[2] = value;
  </onValueChanged>
</slider>

<simulation name="sim" period="0.01">
  <solver name="solver2">
    <series>
      <system name="mySystem">
        <output name="myOutput" width="2" />
        <onOutputs>
          return valueVector
        </onOutputs>
      </system>
      <probe ref="mySection.myStream" />
    </series>
  </solver>
</simulation>