Communication Between Controllers

Because a controller can operate in the Captivate UI and also in a local browser, it’s quite possible for you to have multiple instances of the same controller in two different places both communicating with Captivate. This can cause problems unless the two instances of the controller know about each other and are able to communicate.

In fact, there are many reasons why one controller would want to communicate directly with another controller, so Captivate provides two methods designed to address this specific situation. For example, if you have one controller that has one UI running in Captivate and another UI designed for a remote operator, you can keep both interfaces in sync by sending messages through Captivate.

Using messageIn and messageOut

Controllers can pass messages back and forth to other controllers or to other instances of themselves by using the messageOut method in combination with the messageIn signal.

To use, call the messageOut method with three arguments:

  • from - A string identifying the source of the message (usually the controller name, but may be anything).

  • to - A string identifying the target of the message (usually the other controller name, but may be anything).

  • data - A JavaScript object of key, value pairs. This may be an arbitrarily deeply nested object, but it must be an object. Strings, arrays, and other primitives will result in an error.

Note: The "to" field doesn't actually do any message routing in the backend. When using messageOut, ALL subscribers to the messageIn event emitter will receive the same three arguments and must do their own filtering. Additionally, this means that anything connected to the Captivate API will be able to read any messages sent this way.

Example:

const my_id = '12345678';
const target_id = '87654321';
const scheduler = ServiceHandler.scheduler;
scheduler.messageIn.connect((from, to, json) => {
  // filter out unwanted messages
  if (to != my_id) return;
  console.log('messageIn received');
  console.log({
    from,
    to,
    data: JSON.parse(json),
  });
});

scheduler.messageOut(
  my_id,
  target_id,
  {
    hello: 'world',
    list: [1, 2, 3],
    nested: { level: { deep: 0 } },
  },
  (e) => console.log((e === `Sent to receiver`) ? 'success!' : 'failed to send');
);

Using Targeted Commands

Instead of messageIn and messageOut, you can also use scheduleCommand to send targeted commands between controllers.

To turn a command message into a targeted command, add a to field to your parameters object. It’s also helpful to use a sender field as you'll see below.

Note: The sender in a targeted command is not related to the sender field in our notifications. It's merely a convention our internal controllers use to identify what input sent the command. You could just as well use from for this purpose in your controllers.

Sending

Ordinarily, this command will return a large amount of data about the current project:

However, by adding a to field to the parameters Captivate will ignore the command internally and just pass the entire payload around to the other inputs (with a few modifications, see below).

If the targeted input can’t be found, Captivate will reply with an error, but whether the other input is found or not, the command message will always be ignored by Captivate and the payload will always get sent to all other controllers listening on the notification stream.

If the specified input was connected to a title in the project, this is the result:

If the input is not connected to any project title, this is the result:

Regardless of whether the input is found, the command is ignored by Captivate and the following payload will be sent through the notification system:

Receiving

To receive targeted messages, you must be listening to the notification stream. Subscribing to notifications is described elsewhere, but here's an example again.

Example:

Make sure only one instance of the controller is allowed to send API commands:

Targeted Command Payload Restrictions

The payload sent to the notification stream is assembled according to the following rules:

  • The first argument passed (command name) will populate the command field in the JSON payload.

  • All key/value pairs from the second argument, what we call parameters, will be included as-is in the notification payload.

  • The third argument, what we call variables, will be included in the notification payload as a child of the variables key.

Because of this, controllers can send and receive messages with arbitrary data in the form of key/value pairs by simply adding more fields to the original message payload or by including these values in the variables argument. Here is a more thorough example:

This will be printed to the console (assuming the target controller exists in the project):

...and this is the JSON payload that will propagate through the notification system:

Last updated