Graphics.TrickVariableServer - lordmundi/wikidoctest GitHub Wiki

DOUG comm with a TRICK sim via the Variable Server (using TCL)

UPDATE: There is now capability to drive simulations from a TRICK variable server without writing any code at all using the updata simdata plugin in EDGE v2.1. You can find documentation on that here.

NOTE: To get synchronous variable server data, you need to add the following to your S_define in the "sys" object:

(automatic_last) sim_services/exec: var_server_sync( Inout EXECUTIVE * E = &sys.exec ) ;

… and also send the following variable server command:

var_sync = Yes;

DOUG has the ability to communicate with a Trick simulation via the Trick variable server. This document will explain how to create a DOUG GUI script that connects to a Trick simulation using the Trick Interface Library and receives data from a Trick simulation at a defined frequency.

Trick Variable Server Compatability Modes

There are currently two compatability modes used when communicating to a Trick variable server:

  • MODERN: Trick version 04 and newer
  • VINTAGE: Trick version 03 and older

These compatability modes determine the protocols used for connecting to the trick sim, as well as how the interface should request, receive, and parse data. The Trick Interface Library will try to determine which mode a connection is set to based on the presence of Simcom v1.1 (VINTAGE) or Simcom 1.2 (MODERN). Each simulation connection's compatability mode can be overridden by the user.

Required Connection Data

When connecting to a Trick simulation we need to know four pieces of information:

  • Simulation Host
  • Variable Server Port
  • Data Rate
  • Compatability Mode

In our sample code we will define these at the top of the routine:

set simhost          localhost
 set simport          7000
 set cycletime        0.20

Communications Callback Routines

The Trick Interface Library utilizes callbacks to allow user control when a connection has been established to a Trick simulation and when data is received from a Trick simulation. These callback routines are referred to as "Connect" and "Receive" callbacks.

The connect callback is called when a connection has been established with a Trick simulation. This routine is primarily used to make the request for data, but can also be used to handle any initialization between DOUG and the simulation. A connect callback does not take any arguments. A very simple connection callback may look like this:

proc comm_init { } {
      global client_id

      # Start the data retrieval process by calling request_data
      request_data ${client_id}
 }

This routine simply initiates a data request to the variable server. Since you may have connections to more than one simulation, or more than one connection to a simulation it is necessary to send an identifier for the connection you wish to receive data on (client ID) to the request_data routine. You will receive a client ID from the Trick Interface Library when you initially setup your connection. We will cover this later.

Receive callbacks process data received from a simulation. The Trick Interface Library passes a list of raw (unparsed) data to this routine. It is the responsibility of the user to parse this data. In this example we will use the parse routine provided with the Trick Interface Library. A simple example of a receive callback may look like this:

set comm_prefix "_COMM_"

 proc update_overlays { data } {
     global client_id get_variable_list comm_prefix

     set data_list [parse_data "${data}" ${client_id}]

     # Create variables that map to associated variable list indexes.
     # This allows us to reference values by name instead of hard-coded indexes.
     set list_size [ llength ${get_variable_list} ]
     for { set idx 0 } { ${idx} < ${list_size} } { incr idx } {
         regsub -all {\[} [lindex ${get_variable_list} ${idx}] _ variable_name
         regsub -all {\]} ${variable_name} _ variable_name
         set ${comm_prefix}${variable_name} ${idx}
     }

     # Collect the data from simulation&hellip;

     # Display simulation time (sys.exec.out.time)
     puts "Simulation Time: [lindex $data_list [set ${comm_prefix}sys.exec.out.time]]"

     # Call goes here to functions that utilize sim data&hellip;
 }

The central block of code above is for your convenience. It allows the user to reference variable indexes in the list using a variable name that closely resembles that of the original Trick variable name. This is very useful when working with larger lists because it allows the user to insert variables in any part of the variable list without having to modify any indexes when referencing data. With this code you can get data from the list in a more readable fashion:

puts "Simulation Time: [lindex $data_list [set ${comm_prefix}sys.exec.out.time]]"

Without this block of code you would have to refer to the variable by it's position in the list:

puts "Simulation Time: [lindex $data_list 0]"  

Registering A Connection

When connecting to a Trick simulation you must provide the Variable Server with a list of variables to send back to you at a specified interval. This list is referred to as the "Variable List". The following is a simple example of a variable list that request simulation time:

set get_variable_list { sys.exec.out.time } ;

You must also know the names of your connect and receive callbacks. Examples of these routines are listed above. In this example we will store the names in global variables:

set RECV_CALLBACK update_overlays
 set CNCT_CALLBACK comm_init

A client ID is returned to the user each time a connection is registered. For this example we will store it in a global variable named "client_id". For good practice we will default it to −1 (invalid connection) before we actually use it.

set client_id &minus;1

If the TRICK_HOME environment variable is defined and we have successfully imported Simcom routines (via Trick Interface Library) we can register our connection and kick off an effort to connect to the simulation. The following code does just that:

if { [info exists env(TRICK_HOME)] && ([llength [info procs ::Simcom::*]] > 0) } {
     global client_id siminfo

     # USAGE: set_siminfo HOST PORT CLIENT_TYPE RATE SEND_CALLBACK RECV_CALLBACK VARIABLE_LIST
     set client_id [set_siminfo ${simhost} ${simport} generic ${cycletime} ${CNCT_CALLBACK} ${RECV_CALLBACK} ${get_variable_list}]

     # Open Sim Connection and Receive Data
     if { [catch { open_connection ${client_id} } results] != 0 } {
         puts "ERROR running open_connection() : ${results}"
     }
 }

If we wanted to ensure that our connection is running in a particular compatability mode we could override the siminfo compatability member before issuing the call to open_connection(). The siminfo array is global, so you need to ensure that you global siminfo before using it:

global siminfo
 set siminfo(${client_id},compatability) MODERN 

Dependencies

You must source the Trick Interface Library at the top of your routine. If the TRICK_HOME environment variable is not defined the Trick Interface Library will not be able to find Simcom. In this example it is only sources if TRICK_HOME is defined in the environment:

if { [info exists env(TRICK_HOME)] } {
     if { [catch { source "$env(DOUG_HOME)/gui/interface_libraries/trick_comm_library.tcl" } results] != 0 } {
         puts "ERROR sourcing $env(DOUG_HOME)/gui/interface_libraries/trick_comm_library.tcl: ${results}"
         exit &minus;1
     }
 }

Putting It All Together

The following routine will connect to a Trick simulation (localhost, port 7000) using the MODERN Variable Server protocol and receive simulation time every 0.20 seconds:

#!/usr/bin/wish

 # Sim Connection Information
 set simhost          localhost
 set simport          7000
 set cycletime        0.20

 set DOUG_HOME $env(DOUG_HOME)

 set comm_prefix "_COMM_"

 # Trick Variable Server interface routines&hellip;
 if { [info exists env(TRICK_HOME)] } {
     if { [catch { source "$env(DOUG_HOME)/gui/interface_libraries/trick_comm_library.tcl" } results] != 0 } {
         puts "ERROR sourcing $env(DOUG_HOME)/gui/interface_libraries/trick_comm_library.tcl: ${results}"
         exit &minus;1
     }
 }

 # Trick Variable Server Settings
 set RECV_CALLBACK update_overlays
 set CNCT_CALLBACK comm_init

 set get_variable_list { sys.exec.out.time } ;

 # Trick Variable Server Data Received Callback (RECV_CALLBACK)
 proc update_overlays { data } {
     global client_id get_variable_list comm_prefix

     set data_list [parse_data "${data}" ${client_id}]

     # Create variables that map to associated variable list indexes.
     # This allows us to reference values by name instead of hard-coded indexes.
     set list_size [ llength ${get_variable_list} ]
     for { set idx 0 } { ${idx} < ${list_size} } { incr idx } {
         regsub -all {\[} [lindex ${get_variable_list} ${idx}] _ variable_name
         regsub -all {\]} ${variable_name} _ variable_name
         set ${comm_prefix}${variable_name} ${idx}
     }

     # Collect the data from simulation&hellip;

     # Display simulation time (sys.exec.out.time)
     puts "Simulation Time: [lindex $data_list [set ${comm_prefix}sys.exec.out.time]]"

     # Call goes here to functions that utilize sim data&hellip;
 }

 # Trick Variable Server Connected Callback (CNCT_CALLBACK)
 proc comm_init { } {
     global client_id

     # Start the data retrieval process by calling request_data
     request_data ${client_id}
 }

 set client_id &minus;1

 if { [info exists env(TRICK_HOME)] && ([llength [info procs ::Simcom::*]] > 0) } {
     global siminfo

     # USAGE: set_siminfo HOST PORT CLIENT_TYPE RATE SEND_CALLBACK RECV_CALLBACK VARIABLE_LIST
     set client_id [set_siminfo ${simhost} ${simport} generic ${cycletime} ${CNCT_CALLBACK} ${RECV_CALLBACK} ${get_variable_list}]

     set siminfo(${client_id},compatability) MODERN

     # Open Sim Connection and Receive Data
     if { [catch { open_connection ${client_id} } results] != 0 } {
         puts "ERROR running open_connection() : ${results}"
     }
 }