Skip to content

Instantly share code, notes, and snippets.

@NoahAndrews
Last active September 30, 2022 18:40
Show Gist options
  • Save NoahAndrews/29f7a383d537cb3e9e612e06a38bb604 to your computer and use it in GitHub Desktop.
Save NoahAndrews/29f7a383d537cb3e9e612e06a38bb604 to your computer and use it in GitHub Desktop.

Messages

All communication over the WebSocket connection is done through Messages, which are passed back and forth between the WebSocket client and the robot using a consistent JSON format. They have three fields, namespace, type, and payload.

namespace is used to route messages on the robot/server side, and to allow clients to specify what information they are interested in (through subscription). This allows for separation of concerns.

The type field is used to specify what the message means. Types are scoped to namespaces, so you can safely reuse them between namespaces.

The payload field optionally contains arbitrary text (often escaped JSON). The libraries will handle performing the escaping for you.

Messages can be sent either as responses to a specific client, or broadcast to an entire namespace (all connections that have subscribed to the namespace will receive the message).

Using the Java library

The entirety of the Java WebSocket library lives in the org.firstinspires.ftc.robotserver.internal.webserver.websockets package. Class names referenced here are located therein.

The Java library allows you to set up namespace handlers and send broadcasts. All namespace handlers need to extend the WebSocketNamespaceHandler abstract class. There are two ways to handle messages, which can be used together within a namespace handler:

  1. You can override the onMessage() method, which will be called for every received message that is in the relevant namespace.

  2. You can override the registerMessageTypeHandlers() method, which will provide a map that you can use to register handlers for specific message types. This allows you to avoid long switch statements or else if chains in a complex namespace.

The handler callbacks will provide you with an FtcWebSocketMessage instance (the received message) and an an FtcWebSocket instance (the WebSocket connection that sent it). FtcWebSocket has a send() method that you can use to reply to the sender if necessary.

WebSocketNamespaceHandler also has onSubscribe() and onUnsubscribe() methods that you can override to respond to the coming and going of clients, should you have a need for that.

Once you've created a namespace handler, you need to register it. I recommend registering namespace handlers from a the setState method of a WebHandlers class. Make sure you use WebServer's new getWebSocketManager() method to provide the WebSocketManager instance to your handler's constructor if you will be sending broadcasts.

To send a broadcast, call broadcastToNamespace() on the WebSocketManager instance. This will let you send a message to all WebSocket connections that have subscribed to your namespace. A namespace must be registered before you can send a broadcast to it. If you don't need to receive messages from the namespace you want to broadcast to, you can call registerNamespaceAsBroadcastOnly(namespace) on the WebSocketManager instance.

Using the JavaScript library

The js library consists of two files, websocket-core.js and websocket-iframe.js. Both of them live in RobotServer/src/main/assets/js.

The js library supports the idea of having multiple pieces of UI on the screen that can remain separate from each other and subscribe to different namespaces. Doing this through iframes is especially easy. Every iframe that wants to use the WebSocket connection includes the websocket-iframe.js file. This file sets up a WEBSOCKET_LIB object that contains the WebSocketMessage constructor and a webSocketManager object, which is scoped to the iframe.

WebSocket traffic from different iframes uses the same underlying WebSocket connection. The connection is only initiated when the websocket-iframe.js file is loaded. If the connection is severed, a reconnection attempt will be made when a ping sent by frame.html succeeds.

The websocket-iframe.js file includes documentation about the correct setup sequence for using the WebSocket connection. The documentation for the webSocketManager methods lives in websocket-core.js, where the WebSocketManager constructor is.

webSocketManager has these methods: sendMessage, registerConnectionStateListeners, subscribeToNamespace, registerNamespaceHandler, and registerTypeHandler.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment