WebSocket protocol
Most control protocols for Broadcast routers come from the era of RS422 serial comms. Even those routers which support control via IP tend to use proprietary protocols that are not easily compatible with web-based control applications.
The RouteMaster VR Websocket protocol was designed to use web standards to enable router control from web applications running on any modern browser. This allows web pages to easily control routers and receive status from any router controlled via the RouteMaster VR system.
RouteMaster VR Web Panels use this protocol to control routers connected to the RouteMaster VR system. RouteMaster VR can also use the same protocol to control routers that support it, or downstream RouteMaster VR systems.
Client vs Server
The ‘client’ is typically any application that wants to make routes or receive router status, and the ‘server’ is typically the RouteMaster VR system (or any other router using this protocol).
The ‘server’ typically listens for connections on TCP/IP port 81, although other ports can be configured. Servers support multiple simultaneous client connections. RouteMaster VR limits the number of simultaneous clients according to the number of installed licenses.
WebSocket protocol
Messages are exchanged between client and server using the WebSocket protocol, described here: https://en.wikipedia.org/wiki/WebSocket
Unlike HTTP, it offers a persistent full-duplex connection. RouteMaster VR V2.3.7 and later support encrypted WebSockets (wss://hostname:portnum). This can be enabled in the RouteMaster VR client configuration dialog.
Protocol URL
When controlling RouteMaster VR from other systems such as Helm, you may need to enter the URL of the connection. Like an HTTP URL, this has the format <protocol>://<host>:<port>, where the protocol is ws (or wss for secure WebSockets).
URL Query Parameters
Optional query parameters can be appended which affect the operation of the protocol.
versionSpecifies the version of protocol to be used. Version 1 is the default. If Version 2 is specified, the multipletallyCrosspointmessages sent on connection are replaced with a singlebulkCrosspointsmessage.UserID(optional) Identifies the system or user connecting to RouteMaster VR. The UserID is not authenticated, but is used for logging to determine which system issued specific commands.
Examples:
ws://localhost:81wss://192.168.0.222:84wss://webcentre.rascular.com:81?version=2wss://webcentre.rascular.com:81?version=2&UserID=MyApplication
JSON
WebSockets give a way of sending 'messages' between endpoints, but do not define the content and structure of those messages. Messages could be binary, XML, or any proprietary format. RouteMaster VR uses JSON for the message structure, which fits well with JavaScript used in browser-side applications. http://json.org/
Message Flow
The protocol does not impose any restrictions on message flow. Messages can normally be sent by both client and server at any time, and in any order. However, when a new client connects to a server, it should be sent the entire router state including source and destination names, protect and crosspoint status.
The final message after the initial state has been sent will be the connectComplete message.
No Ack/Nak
Messages do not implicitly cause ACK or NAK replies to be sent, but the success of messages forcing a change of state can be determined by the tally messages returning when an operation completes.
No Query Commands
There are no query commands to request state from RouteMaster VR. This is because the full router state is automatically sent to all clients when they connect, and clients are then automatically notified whenever the state changes.
Use Persistent Connections
Each time a WebSocket connection is established, the full router state with names, etc is sent to the client. Client software should keep the connection open, rather than opening it every time a command must be sent.
Input/Output numbering
All sources, destinations and levels are numbered starting from ZERO.
JSON Message fields
Every JSON message has a mandatory string field event which describes the message type. A data structure contains parameters and data regarding the event. Unknown incoming events will be ignored, and unknown fields in the JSON data are also ignored.
API on Postman
We're switching to Postman to design, test and document our APIs. Postman support for WebSocket APIs is improving, and you'll find the RouteMaster VR API with example messages here.
Messages from Server to Client
| Event | Function | Data | Example |
|---|---|---|---|
tallyCrosspoint |
Notifies client of crosspoint change | destination: integer sources : array of integers, one per level | { "event": "tallyCrosspoint", "data": { "sources": [0,0], "destination":0 } } |
sourceName |
Notifies client of source name change | source: integer names : array of strings for names starting at specified source. | { "event": "sourceName", "data": { "names": ["XXX A", "SRC 1", "SRC 2", "SRC 3"], "source":0 } } |
destName |
Notifies client of destination name change | destination: integer names : array of strings for names starting at specified destination. | { "event": "destName", "data": { "names":["MON", "DST 1", "DST 2", "DST 3"], "destination": 0 } } |
levelName |
Notifies client of level name change | level: integer names : array of strings for names starting at specified level. | { "event": "levelName", "data": { "names":["Video", "Audio"], "level": 0 } } |
routerData |
Basic information about the router size, etc | sources: integer dests: integer levels: integer name: string | { "event": "routerData", "data": { "sources": 64, "levels": 2, "dests": 64, "name": "DUMMY" } } |
inputGroups |
Information about input (source) groups. | groups[]: array of name: string groups, values: number[] router inputs in the group. Note that RouteMaster VR expands the values arrays, so a group defined as "1-5" will appear as an array [1,2,3,4,5]. | [ { "name": "Cameras", "values": [2,3,4,5,6,9,10] }, { "name": "Servers", "values": [21,22,23,24] } ] |
outputGroups |
Information about output (destination) groups | groups[]: array of name: string groups, values: number[] router outputs in the group. | [ { "name": "Monitors", "values": [2,3,4,5] } ] |
protectState |
Notifies client of destination protect change | dest: integer state: array of { prot: "Unlocked", "Locked", or "Protected", user: string } | { "event": "protectState", "data": { "state": [ {"prot": "Unlocked"}, {"prot":"Locked", "user":"Joe"}], "dest": 0 } } |
connectComplete |
Indicates that the initial status transfer to client has finished. | none | { "event": "connectComplete" } |
salvoNames |
The list of available Salvo Names | names: array of strings | { "event":"salvoNames", "data": { "names": ["Salvo 1", "Salvo 2", "My Salvo"] } } |
bulkCrosspoints V2 Only |
Bulk crosspoint tally sent on connection | array of tally information, as per the tallyCrosspoint message. destination: integer sources: array of integers, one per level |
{ "event":"bulkCrosspoints", "data": { ... } } |
sourceStatus |
Status and metadata information for sources | source: integer state: array of source status records starting at specified source. The data available for each source will depend on the router type and system configuration | { "event": "sourceStatus", "data": { "source": 42, "state": [{...}, {...}] } } |
destStatus |
Status and metadata information for destinations | dest: integer state: array of destination status records starting at specified destination. The data available for each destination will depend on the router type and system configuration | { "event": "destStatus", "data": { "dest": 42, "state": [{...}, {...}] } } |
tielineStatus |
Messages from Client to Server
| Event | Function | Data | Example |
|---|---|---|---|
makeCrosspoint |
Request a crosspoint change. Will cause a tallyCrosspoint message to be returned when the route is made. |
destination: integer source: integer levels: | { "event": "makeCrosspoint", "data": { "source":6, "destination":2, "levels":"L0" } } |
lockOutput |
Lock/unlock an output. A protectState message will be returned. |
destination: integer state: "Locked"/"Unlocked"/"Protected" | { "event": "lockOutput", "data": { "destination": 2, "state":"Locked" } } |
fireSalvo |
Fire a 'salvo' of multiple routes. Individual tallyCrosspoint messages will be received for each route made. |
id: name or id number of salvo to fire | { "event": "fireSalvo", "data": { "id": "RESET" } } |
freeTieline |
Force a named tieline to become free, disconnecting any previous use of the tieline. | tieline: Name of tie-line to free | { "event": "freeTieline", "data": { "tieline": "Example" } } |
Further details for sourceStatus on NDI sources
The sourceStatus message contains additional information for NDI sources.
event:sourceStatusdata: JSON object with the following fields:source: integer source numberstate: JSON Object with the following fields:ok: boolean indicating whether the source is currently valid and availableerror: string describing any error condition affecting the sourcmetadata: JSON object containing any metadata associated with the sourceIPAddress: string with IP and Port of the NDI sourceNDISource: name of the NDI SourceSourceMetadata: String with any XML metadata provided directly by the source.
JavaScript implementation of WebSocket clients
For a good overview of how to handle JSON-based WebSockets from JavaScript, see this page:
https://www.new-bamboo.co.uk/blog/2010/02/10/json-event-based-convention-websockets/
Test Server
You can try out the protocol using our online test server at wss://webcentre.rascular.com/RouteMaster VR. To easily view and test messages, just enter that URL at https://piehost.com/websocket-tester and 'connect'.