Live Communication with Vizard
BSK can link with Vizard live in three ways:
liveStream
: The BSK sim runs in lockstep with Vizard. This supports user input handling and performs standard optical navigation if cameras are configured.broadcastStream
: The BSK sim broadcasts read-only messages in a publish/subscribe pattern, where many Vizards may subscribe simultaneously.noDisplay
: The BSK sim performs optical navigation headlessly through Vizard, only sending messages to Vizard when an image is requested. This is a more efficient OpNav mode, but shows no graphical display in Vizard.
Caution
Legacy opNavMode
has been deprecated. For opNavMode = 1
, use liveStream
flag. For opNavMode = 2
, use noDisplay
flag.
liveStream
Mode
During liveStream
mode, BSK sends a message containing simulation state information to Vizard at every
timestep. BSK then waits for an ‘OK’ response before proceeding. In this way, both BSK and Vizard operate
in lockstep with each other. This is the best method for visualizing a scenario live on your local machine.
To enable livestreaming through vizSupport
use the liveStream
flag:
viz = vizSupport.enableUnityVisualization(scSim, dynTaskName, scObject
, liveStream=True
)
Or, to bypass the vizSupport
module:
viz = vizInterface.VizInterface()
viz.liveStream = True
When the liveStream
flag is set to True, BSK will also automatically obtain user inputs from Vizard
at each timestep, as detailed in the accompanying section.
Note
BSK can only take advantage of 2-way communication during livestream mode, so liveStream
must be
set to True.
Attaching a “DirectComm” Vizard Instance
Once BSK is configured to livestream, an instance of Vizard can be attached by copying the port address into the “Socket Address” field. Make sure that the “DirectComm” option is selected.
The communication protocol and port number can be changed using:
viz.reqComProtocol = "xxx";
viz.reqComAddress = "xxx";
viz.reqPortNumber = "XXXX";
BSK assembles the full liveStream
address as:
viz.reqComProtocol + "://" + viz.reqComAddress + ":" + viz.reqPortNumber
Variable |
Type |
Default |
Description |
---|---|---|---|
|
string |
|
Transport protocol to use for the 2-way connection. See all protocol options here. |
|
string |
|
Network address to use for the connection. This can be either a host name or an IP address. |
|
string |
|
Port number to use for the connection. |
The default 2-way address is therefore tcp://localhost:5556
.
Creating VizEventDialog
Panels
VizEventDialog
panels are displayed in Vizard, and contain choices for a user to select. These panels
can be configured to have a prescribed duration and several options for a user to select. The following
list shows all VizEventDialog
structure variables.
Variable |
Type |
Units |
Required |
Description |
---|---|---|---|---|
|
string |
Yes |
Name of Vizard event handler to be returned with |
|
|
string |
Yes |
Contains the information or choice that should be posed to the user. |
|
|
string[] |
No |
Array of display strings, one entry for each user choice that will be shown. If this is empty, the dialog is assumed to be informational only. Strings must be unique (cannot display the same option multiple times in a panel). |
|
|
double |
nanoseconds |
No |
Determines when to close a panel. Default is 0, which leaves the panel on display until closed by user. |
|
bool |
No |
If true and |
|
|
int |
No |
-1 to not show a confirmation panel, 0 to use viz default, 1 to require a user confirmation of their selection. |
|
|
int |
No |
-1 to continue to show panel , 0 to use viz default, 1 to hide panel after user makes a selection, 2 to destroy panel after user makes a selection |
|
|
string |
No |
Select format for dialog box: “WARNING”, “CAUTION”, or none to use viz default format |
Here is an example an VizEventDialog
panel creation:
powerModePanel = vizInterface.VizEventDialog()
powerModePanel.eventHandlerID = "Power Mode Panel"
powerModePanel.displayString = "Set system power mode:"
powerModePanel.userOptions.append("Nominal")
powerModePanel.userOptions.append("Low-Power")
powerModePanel.useConfirmationPanel = True
viz.vizEventDialogs.append(powerModePanel)
See scenarioBasicOrbitStream for a Basilisk example script that uses Vizard event panels.
Note
The list viz.vizEventDialogs
sends current panel requests to Vizard as part of the
VizMessage, then clears itself before the next timestep. If information in a panel needs to
be modified, the same VizEventDialog
instance (with the same eventHandlerID
) can be
modified and re-appended to viz.vizEventDialogs
. This will cause the panel to re-open
if minimized, with updated information. If the panel list needs to be manually cleared, this
can be done using viz.vizEventDialogs.clear()
.
Handling User Input
Responses from panels can be used as inputs back to BSK. The key is that the responses must be read from the VizUserInputMsgPayload message at the desired rate.
From Python, this can be achieved by calling scSim.ExecuteSimulation()
at the desired input
reception rate so that responses can be parsed and used to influence the simulation state.
The required structure resembles the following:
currentTime = 0
inputTimeStep = macros.sec2nano(5) # Read inputs every 5 seconds
...
scSim.InitializeSimulation()
for i in range(int(totalDuration/inputTimeStep)):
currentTime += inputTimeStep
scSim.ConfigureStopTime(currentTime)
scSim.ExecuteSimulation()
userInputs = viz.userInputMsg.read()
keyInputs = userInputs.keyboardInput
eventInputs = userInputs.vizEventReplies
# Parse "keyInputs" and "eventInputs", modify sim state
The 2-way communication output message, viz.userInputMsg
, is an instance
of VizUserInputMsgPayload. This message fills like a queue: Vizard collects all inputs
that were recorded over the last scSim.ExecuteSimulation
call, and hands them all over together.
Caution
Setting a low input frequency (here, represented by inputTimeStep
) could lead to build-up of
responses, that could logically conflict with one another.
This behavior could also be built into a BSK module, in which case the above code structure would not be needed. However, this module would have to hard-code the mappings for different Vizard response types and their associated BSK actions.
Keyboard Parsing
There are two types of replies that Vizard can send in return. The keyboardInput
field of the
message contains a string of keyboard characters that have been pressed since the last timestep.
Keys will only be recorded if pre-specified. In the example below, listeners are configured
for the keys ‘a’, ‘b’, ‘c’, and ‘d’:
viz.settings.keyboardLiveInput = "abcd"
Caution
Note that Vizard has certain keys pre-programmed as hot-keys for menus and scene actions. If a hot-key is selected as a duplicate listener, Vizard will display a warning, and dual-actions may occur.
To parse keyInputs
, search the string for characters of interest:
if 'a' in keyInputs:
# 'a' key action
if 'b' in keyInputs:
# 'b' key action
...
Panel Response Parsing
Vizard can also return VizEventReply
structures, which contain information about selections
made within VizEventDialog
panels. The following list shows all VizEventReply
structure variables.
Variable |
Type |
Description |
---|---|---|
|
string |
Name provided when setting up the VizEventDialog object |
|
string |
Option selected by user |
|
bool |
Was the panel closed and destroyed? |
To parse eventInputs
, loop over the list:
for response in eventInputs:
eventID = response.eventHandlerID
eventOption = response.reply
if eventID == "Power Mode Panel":
if eventOption == "Low-Power":
# change mode
elif eventID == ...
See the scenario scenarioBasicOrbitStream for an implemented 2-way communication example.
broadcastStream
Mode
In addition to livestreaming, BSK can broadcast its read-only messages to a secondary port
using the broadcastStream
flag. This follows a publish/subscribe architecture, which means many
subscriber Vizards can hook up to a single BSK instance.
To enable broadcasting use the broadcastStream
flag:
viz = vizSupport.enableUnityVisualization(scSim, dynTaskName, scObject
, broadcastStream=True
)
Note
BSK blindly broadcasts to a port, regardless of whether there are subscribers present or not. This means
that unlike liveStream
mode, BSK will not wait for a Vizard connection to begin executing.
Vizard can also run both liveStream
and broadcastStream
modes simultaneously:
viz = vizSupport.enableUnityVisualization(scSim, dynTaskName, scObject
, liveStream=True
, broadcastStream=True
)
This configuration behaves the same as a normal livestream (BSK waits for connection to start), after which subscriber Vizards can connect. Subscriber Vizards cannot connect before the publisher Vizard.
Broadcast Synchronization
Using liveStream
and broadcastStream
modes simultaneously can be useful for teaching/training
environments, where an instructor is running BSK locally and interacting with panels in a publisher Vizard,
but broadcasting to separate subscriber Vizards run by students/trainees.
The trainer has the option to force certain visual settings to the broadcast:
Variable |
---|
|
|
|
|
|
See BSK Scripting Settings for types and descriptions of these variables.
This option is ON by default, but can be disabled within the menu of the trainer Vizard. Additionally, panel states (instantiation, selection, deletion) will be synchronized with the subscriber Vizard. Choices selected on panels are shown briefly in the subscriber Vizard before closure.
When disabled, the subscriber Vizard will be briefly notified that they have increased autonomy with visual settings. However, no interactions with panels will have any effect on the BSK sim.
Note
Subscriber Vizards are able to move the time slider at the bottom of the window, however panels will not replay.
Attaching a “Receive Only” Vizard Instance
Once BSK is configured to broadcast, an instance of Vizard can be attached by copying the port address into the “Socket Address” box. Make sure that the “Receive Only” option is selected.
The communication protocol and port number can be changed from Python using:
viz.pubComProtocol = "xxx";
viz.pubComAddress = "xxx";
viz.pubPortNumber = "XXXX";
BSK assembles the full broadcastStream
address as:
viz.pubComProtocol + "://" + viz.pubComAddress + ":" + viz.pubPortNumber
Variable |
Type |
Default |
Description |
---|---|---|---|
|
string |
|
Transport protocol to use for the 2-way connection. See all protocol options here. |
|
string |
|
Network address to use for the connection. This can be either a host name or an IP address. |
|
string |
|
Port number to use for the connection. |
The default broadcast address is therefore tcp://localhost:5570
.
Note
Although the default TCP ports seemed to be available across our development machines, specific firewall restrictions or other applications may restrict port use. If the port experiences trouble binding, a ZMQ error will be thrown to the terminal and a different port number should be chosen. (Error #19 if port is in use, Error #49 if address:port does not exist.)
In order to broadcast temporarily over a network, a dynamic IP address can be used. With both devices on the same network, set the broadcast address as:
viz.pubComAddress = "XX.XXX.XXX.XX"
Caution
Some networks may block broadcasting. If the broadcast has trouble connecting, check whether the
two devices can ping each other across the network. The terminal command is ping XX.XXX.XXX.XX
for Mac, or telnet XX.XXX.XXX.XX
for Windows and Linux.
If you’re looking to broadcast for longer durations (i.e. more than a few hours), you may need to set up a static IP address. IP addresses are typically assigned dynamically, meaning that they can change unpredictably throughout the day. Setting up a static IP address ensures that the broadcast can be found at the same address consistently.
noDisplay
Mode
Vizard can operate headless with a high-performance optical navigation mode, in which it only
updates and renders the scene when an image is requested from BSK. This mode does not show a display
to the user, which means that it cannot be used in conjunction with liveStream
or
broadcastStream
modes.
To use the noDisplay
flag:
viz = vizSupport.enableUnityVisualization(scSim, dynTaskName, scObject
, noDisplay=True
)