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…
# 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…
}
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 −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 −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…
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 −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…
# 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…
}
# 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 −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}"
}
}