-
Notifications
You must be signed in to change notification settings - Fork 0
Protocol
The core concept is that JSON objects are used to send commands, and receive responses and event notifications. Each packet carries a single JSON object. Most commands are used to get or set a property value.
Commands start with a command object, followed by a sequence object.
{
_command_ :
{
_command parameters_
},
"Sequence" : 0
}
For example, to query the version number and get basic information about the Workbench controller, send this message to get the current value of the System property:
{
"GetCommand": {
"System": {
}
},
"Sequence": 1
}
Workbench will respond with:
{
"GetResponse": {
"System": {
"Version": "Streamware Workbench 2.3.19",
"Talkers": 8,
"Listeners": 8
}
},
"Sequence": 1,
"Status": "OK"
}
Workbench fills out the System property object, and returns a response with the same sequence number as the command. The Status object indicates the result.
Workbench Remote uses the JUCE InterprocessConnection class to send and receive data over a socket. The actual data sent over the socket consists of a four byte magic number, followed by a four byte little-endian message length count in bytes, followed by the JSON string.
For example, to transmit this 45 byte JSON string:
{
"GetCommand": {
"System": {
}
},
"Sequence": 1
}
the following bytes are sent over the socket:
574f524b Magic number 'WORK'
2d000000 Byte count (32 bits, endian swapped)
7b22476574436f6d {"GetCom
6d616e64223a207b mand": {
2253797374656d22 "System"
3a207b7d7d2c2022 : {}}, "
53657175656e6365 Sequence
223a20317d ": 1}
Strings are sent without a zero terminator.
The following is a list of commands that Workbench Remote can send to the Workbench Controller, and the responses for each command.
GetCommand is a command that requests a property or set of properties from the Workbench Controller. Here is an example GetCommand:
{
"GetCommand": {
"Foo": {
}
},
"Sequence": 0
}
The first line, '"GetCommand": {', is the command object. It is interpreted by the Controller as the command being sent across.
The second line, '"Foo": {', is the message contained within the command. It describes the property or set of properties being requested.
The fifth line, '"Sequence": 0', is the sequence number of the command. The Controller will use this as the sequence number for its response to this command.
The second line is the one part of the GetCommand structure that will vary.
Here is an example response to a GetCommand:
{
"GetResponse": {
"System": {
"Version": "Streamware Workbench 2.3.23.16",
"Talkers": 2,
"Listeners": 2
}
},
"Sequence": 3,
"Status": "OK"
}
All responses to GetCommands have three values in common.
First, the GetCommand object is replaced with a GetResponse object.
Second, the sequence number is the same as the sequence number sent along with the original GetCommand message.
Third, the Status string contains a status message. Anything other than a status message of "OK" is an error message that details what went wrong; typically it is due to an invalid GetCommand message being sent.
Below are all the currently implemented properties that can be requested, what sort of responses will be received, and examples of how to structure each of them.
Sending a GetCommand asking for the System property will lead to a response containing several important bits of information about the Workbench Controller, such as the version number and the number of talker and listener streams.
Here's a properly structured System GetCommand:
{
"GetCommand": {
"System": {
}
},
"Sequence": 0
}
When sending a System GetCommand, the System object is empty, as the Workbench Controller responds with its full system information; it doesn't support requesting specific aspects of its system information.
A response to a System GetCommand is structured like this:
{
"GetResponse": {
"System": {
"Version": "Streamware Workbench 2.3.35",
"Talkers": 2,
"Listeners": 2,
"BroadRReachSupported": true
}
},
"Sequence": 3,
"Status": "OK"
}
The structure is similar to the System GetCommand, but there are several new and modified fields.
The System object now contains several name/value pairs, detailed below, along with their variable types.
| Name | Value | Variable Types |
|---|---|---|
| Version | Version number for the Workbench Controller | String |
| Talkers | Number of Talker Streams | Number |
| Listeners | Number of Listener Streams | Number |
| BroadRReachSupported | Whether BroadR-Reach is supported | Boolean |
Sending a Getcommand asking for the Talkers property will lead to a response containing detailed information about the talker streams.
Here's a properly structured Talkers GetCommand:
{
"GetCommand": {
"Talkers": [
{
"Index": 0
},
{
"Index": 1
}
]
},
"Sequence": 1
}
When sending a Talkers GetCommand, the Talkers object should contain an array of JSON name/value pairs, with the name labeled "Index", and the value being the talker stream index number. In the example above, this command would request the first and second talker stream's information.
A response to a Talkers GetCommand is structured like this:
{
"GetResponse": {
"Talkers": [
{
"Index": 0,
"Name": "Talker Stream 1",
"StreamID": "0",
"DestinationAddress": "91-e0-f0-00-fe-00",
"Subtype": 2,
"ChannelCount": 8,
"Active": false,
"FaultInjection": {
"CorruptPackets": {
"Enabled": false,
"Percent": "0"
},
"DuplicatePackets": {
"Enabled": false,
"Percent": "0"
},
"TimestampJitter": {
"Enabled": false,
"Percent": "0"
},
"TimestampJump": {
"Enabled": false,
"Percent": "0",
"Amount": "0"
}
}
}
]
},
"Sequence": 1,
"Status": "OK"
}
The structure is similar to the Talkers GetCommand, but now the Talkers object is filled with an array of detailed information about the requested talker streams.
The Talkers object now contains both name/value pairs, and several objects within it as well.
First, here are the name/value pairs, along with their variable types:
| Name | Value | Variable Types |
|---|---|---|
| Index | Index of the Talker Stream | Number |
| Name | Name of the Talker Stream | String |
| DestinationAddress | The Talker Stream's current destination address | String |
| Subtype | The Talker Stream's current subtype | Number |
| ChannelCount | The Talker Stream's current channel count | Number |
| Active | Whether the Talker Stream is active | Boolean |
Next, we have the FaultInjection Object, which contains several objects detailing the current fault injection settings:
| Name | Value |
|---|---|
| CorruptPackets | What percentage of packets are corrupted |
| DuplicatePackets | What percentage of packets are duplicated |
| TimestampJitter | How often timestamp jitter is inserted |
| TimestampJump | How often a timestamp jump occurs, and how frequently |
The name/value pairs within those objects, along with their variable types, are as follows:
| Name | Value | Variable Types |
|---|---|---|
| Enabled | Whether fault injection for this type of fault is enabled | Boolean |
| Percent | How frequently this type of fault injection occurs | String |
| Amount | How large a timestamp jump will occur | String |
The third name/value pair, Amount, only applies to the TimestampJump object.
Sending a Getcommand asking for the Listeners property will lead to a response containing detailed information about the listener streams.
The formatting for the Listeners GetCommand is nearly identical to that of the Talkers GetCommand. Here's a properly structured example:
{
"GetCommand": {
"Listeners": [
{
"Index": 0
},
{
"Index": 1
}
]
},
"Sequence": 2
}
As you can see, the only change is the name for the object inside the GetCommand.
The response, however, is different from that of a Talkers GetResponse. Here's how a Listeners GetResponse is structured:
{
"GetResponse": {
"Listeners": [
{
"Index": 0,
"Name": "Listener Stream 1",
"StreamID": "91e0f000fe000080",
"DestinationAddress": "91-e0-f0-00-fe-80",
"Subtype": 2,
"ChannelCount": 8,
"Active": false
}
]
},
"Sequence": 2,
"Status": "OK"
}
The Listeners object contains a set of name/value pairs detailing the requested listener stream's information. Here's what each name/value pair refers to, and their variable type:
| Name | Value | Variable Type |
|---|---|---|
| Index | Index of the Listener Stream | Number |
| Name | Name of the Listener Stream | String |
| StreamID | The Listener Stream's Stream ID | String |
| DestinationAddress | The Listener Stream's current destination address | String |
| Subtype | The Listener Stream's current subtype | Number |
| ChannelCount | The Listener Stream's current channel count | Number |
| Active | Whether the Listener Stream is active | Boolean |
Sending a Getcommand asking for the LinkState property will lead to a response containing detailed information about the link state.
The formatting for the LinkState GetCommand is nearly identical to that of the other GetCommands. Here's a properly structured example:
{
"GetCommand": {
"LinkState": {
}
},
"Sequence": 13
}
The response for a LinkState GetCommand is different from how the previous responses have been structured. Here's how a LinkState GetResponse is structured:
{
"GetResponse": {
"LinkState": {
"ConnectState": false,
"DuplexState": "full duplex",
"TransmitSpeed": 0,
"ReceiveSpeed": 0,
"AutoNegotiation": false,
"EthernetMode": "BroadR-Reach",
"BroadRReachMode": "Master"
}
},
"Sequence": 13,
"Status": "OK"
}
The LinkState object contains a set of name/value pairs detailing the linkstate's information. Here's what each name/value pair refers to, and their variable type:
| Name | Value | Variable Type |
|---|---|---|
| ConnectState | Whether the link is connected | Boolean |
| DuplexState | The current duplex state | String |
| TransmitSpeed | The transmit speed | Number |
| ReceiveSpeed | The receive speed | Number |
| AutoNegotiation | Whether it is set to auto negotiate | Boolean |
| EthernetMode | The ethernet mode | String |
| BroadRReachMode | The BroadR-Reach mode | String |
Sending a Getcommand asking for the WorkbenchSettings property will lead to a response containing detailed information about settings for Workbench.
The formatting for the WorkbenchSettings GetCommand is nearly identical to that of the other GetCommands. Here's a properly structured example:
{
"GetCommand": {
"WorkbenchSettings": {
}
},
"Sequence": 13
}
The response for a WorkbenchSettings GetCommand is different from how the previous responses have been structured. Here's how a WorkbenchSettings GetResponse is structured:
{
"GetResponse": {
"WorkbenchSettings": {
"StaticPTPRole": "Grandmaster",
"PTPSendFollowupTLV": true,
"PTPSendAnnounce": false,
"PTPSendsignalingFlag": false,
"PTPDelayRequest": {
"Enabled": true,
"Milliseconds": 1000
},
"TalkerPresentationOffsetMsec": 0,
"ListenerPresentationOffsetMsec": 0,
"SpdifLocked": false,
"EthernetMode": "BroadR-Reach",
"BroadRReachMode": "Master"
},
"Sequence": 13,
"Status": "OK"
}
The WorkbenchSettings object contains a set of name/value pairs detailing the settings tab of Workbench. Here's what each name/value pair refers to, and their variable type:
| Name | Value | Variable Type |
|---|---|---|
| StaticPTPRole | What the PTP role is set to | String |
| PTPSendFollowupTLV | Whether Workbench is sending Followup TLV's | Boolean |
| PTPSendAnnounce | Whether Workbench is sending announce messages | Boolean |
| PTPSendsignalingFlag | Whether Workbench is sending signaling messages | Boolean |
| PTPDelayRequest | Object containing DelayRequest information | Object |
| TalkerPresentationOffsetMsec | The talker presentation time offset | Number |
| ListenerPresentationOffsetMsec | The talker presentation time offset | Number |
| SpdifLocked | Whether Spdif is locked | Boolean |
| EthernetMode | The ethernet mode | String |
| BroadRReachMode | The BroadR-Reach mode | String |
Inside the Workbench object is another object, labeled PTPDelayRequest. It contains two name/value pairs, detailed below:
| Name | Value | Variable Type |
|---|---|---|
| Enabled | Whether Workbench is sending delay requests | Boolean |
| Milliseconds | How frequently Workbench is sending delay requests | Number |
The second field, Milliseconds, will not be present if Enabled is set to false.
SetCommand is a command that tells the Workbench Controller to change the value of a property. Here is an example SetCommand:
{
"SetCommand": {
"Talkers": [
{
"Index": 0,
"StreamID": "0"
}
]
},
"Sequence": 0
}
There are several implemented SetCommands, documented below:
A Talker/Listener SetCommand needs to specify if it is setting a property for a talker or listener stream, and which talker or listener steam it wants to set the property for. You can set multiple properties for multiple streams in the same SetCommand; however, you cannot set the properties for both talker and listener streams in the same SetCommand.
Here's an example Talker SetCommand, setting some properties for 2 talker streams:
{
"SetCommand": {
"Talkers": [
{
"Index": 0,
"StreamID": "0",
"ChannelCount": 5
},
{
"Index": 1,
"Subtype: 2
}
]
},
"Sequence": 24
}
For both talker and listener streams, here are the properties you can set, and their variable type:
| Name | Value | Variable Type |
|---|---|---|
| DestinationAddress | The stream's current destination address | String |
| Subtype | The stream's current subtype | Number |
| ChannelCount | The stream's current channel count | Number |
| Active | Whether the stream is active | Boolean |
| StreamID | The stream's Stream ID | String |
| AutoStart | Whether the stream should start upon start-up | Boolean |
A response to a Talker/Listener SetCommand is structured like this:
{
"SetResponse": {
"Talkers": [
{
"Index": 0
}
]
},
"Sequence": 29,
"Status": "OK"
}
The SetResponse will contain the indexes of the talker or listener streams that were set, along with a status message. If the status message is anything other than "OK", it is an error message, typically meaning an invalid value was sent for a property, such as an out of range subtype.
A WorkbenchSettings SetCommand lets you modify a variety of settings for Workbench. Here's an example of a WorkbenchSettings SetCommand:
{
"SetCommand": {
"WorkbenchSettings": {
"StaticPTPRole": "Follower"
}
},
"Sequence": 53
}
Here are the properties that can be set via a WorkbenchSettings SetCommand:
| Name | Value | Variable Type |
|---|---|---|
| StaticPTPRole | What the PTP role is set to | String |
| PTPSendFollowupTLV | Whether Workbench is sending Followup TLV's | Boolean |
| PTPSendAnnounce | Whether Workbench is sending announce messages | Boolean |
| PTPSendsignalingFlag | Whether Workbench is sending signaling messages | Boolean |
| TalkerPresentationOffsetMsec | The talker presentation time offset | Number |
| ListenerPresentationOffsetMsec | The talker presentation time offset | Number |
| SpdifLocked | Whether Spdif is locked | Boolean |
| EthernetMode | The ethernet mode | String |
| BroadRReachMode | The BroadR-Reach mode | String |
There are two properties that require being inside a PTPDelayRequest object within the WorkbenchSettings object:
| Name | Value | Variable Type |
|---|---|---|
| Enabled | Whether Workbench is sending delay requests | Boolean |
| Milliseconds | How frequently Workbench is sending delay requests | Number |
Here's an example WorkbenchSettings SetCommand, setting the values within the PTPDelayRequest object:
{
"SetCommand": {
"WorkbenchSettings": {
"PTPDelayRequest": {
"Enabled": true,
"Milliseconds": 2000
}
}
},
"Sequence": 56
}
A response to a WorkbenchSettings SetCommand is structured like this:
{
"GetResponse": {
"WorkbenchSettings": {
}
},
"Sequence": 204,
"Status": "OK"
}
The SetResponse will contain a sequence number and a status message. The sequence number should match the sequence number of the SetCommand it is responding to, and the status message should say OK.If the status message is anything other than "OK", it is an error message, typically meaning an invalid value was sent for a property.
There are times when the Workbench Controller will send unsolicited messages to the Workbench Remote.
Currently there is only one message type that is sent unsolicited, which is documented below.
A PropertyChanged message is sent from the Workbench Controller to the Workbench Remote whenever a stream's property is changed directly on the Workbench Controller.
It uses its own set of sequence numbers, and therefore bypasses the usual error checking of sequence numbers matching previously sent commands.
Here is an example of a PropertyChanged message:
{
"PropertyChanged": {
"Talkers": [
{
"Index": 0,
"StreamID": "2"
}
]
},
"Sequence": 19
}
These messages will only contain a single changed property value. In the above example, the StreamID for the first talker stream was changed on the Workbench Controller.
The Workbench Remote updates these properties accordingly as they come in; this way, you do not have to request the talker or listener stream's information again after it has been changed locally on the Workbench Controller; the Workbench Remote will have the new values as soon as you make the change on the Workbench Controller.
The exception to this is when the link state has changed. When that happens, Workbench will send a fully detailed link state object, containing everything that changed. It will look almost identical to the LinkState object structure listed up above, but will have the property PropertyChanged instead of GetResponse.