Operator Side

The operator side can be customized for vehicle data visualization, custom controller setups, and more. There are three ways to communicate with OCP on the operator side:

  • Web view: Using the Oden JavaScript SDK via WebSocket. See Webview for general web view documentation.

  • Custom plugin: From another Oden plugin. Contact your representative for plugin SDK information.

  • Player-side TCP: Using a TCP connection. See Player side TCP.

For an overview of how these configurations affect latency measurement, gamepad input, and data flow, see Latency Measurement.

The sections below describe the web view JavaScript API for communicating with OCP.

Receiving OCP data

OCP data is send from the message ocp_vehicle_user_data.

const odenClient = getOrCreateOdenLayoutClient();

const printOcpData = (data) => {
    console.log("Ocp data:", data);
};

odenClient.registerUserMessageCallback("ocp_vehicle_user_data", printOcpData);

// Unregister callback
odenClient.unregisterUserMessageCallback("ocp_vehicle_user_data", printOnlineVehicles);

The incoming data is of the following format:

{
  "vehicle_feedback": {
    <string>: {
      "ping_latency_ms": u64,
      "message_latency_roundtrip": Option<u64>,
      "ms_since_last_vehicle_data": u64,
      "missing_cameras": Option<Vec<String>>,
      "sender_fault": Vec<String>,
      "receiver_fault": Vec<String>,
      "vehicle_data": {
        "pos": {
          "lat": f64,
          "lon": f64
        },
        "user_data": serde_json::Value
      },
      "last_remote_input": {
        "buttons": {
          "a": bool,
          "b": bool,
          "x": bool,
          "y": bool,
          "left_bumper": bool,
          "right_bumper": bool,
          "back": bool,
          "start": bool,
          "guide": bool,
          "left_thumb": bool,
          "right_thumb": bool,
          "dpad_up": bool,
          "dpad_right": bool,
          "dpad_down": bool,
          "dpad_left": bool
        },
        "axes": {
          "left_x": f32,
          "left_y": f32,
          "right_x": f32,
          "right_y": f32,
          "left_trigger": f32,
          "right_trigger": f32
        }
      },
      "ack_time": u64,
      "ack_time_mac": u32
    }
  },
  "last_input": {
    "buttons": {
      "a": bool,
      "b": bool,
      "x": bool,
      "y": bool,
      "left_bumper": bool,
      "right_bumper": bool,
      "back": bool,
      "start": bool,
      "guide": bool,
      "left_thumb": bool,
      "right_thumb": bool,
      "dpad_up": bool,
      "dpad_right": bool,
      "dpad_down": bool,
      "dpad_left": bool
    },
    "axes": {
      "left_x": f32,
      "left_y": f32,
      "right_x": f32,
      "right_y": f32,
      "left_trigger": f32,
      "right_trigger": f32
    }
  }
}

Comments:

  • vehicle_feedback: A hashmap of each vehicle connected to this operator station with its respective feedback data.

  • last_input: Last gamepad controller data sent to the vehicle.

  • ping_latency_ms: Ping latency to the vehicle, this is measured from operator to vehicle and back.

  • message_latency_roundtrip: Vehicle reports this latency, which is measured from vehicle to operator and back. This value will become stale if connection to vehicle is lost.

  • ms_since_last_vehicle_data: Milliseconds since last time vehicle_data was updated.

  • missing_cameras: Vector of cameras that has lost its input, None if all cameras works. Cameras that has disabled the drop detector will not show up in the list.

  • sender_fault: Vector of fault on the operator, empty if no fault.

  • receiver_fault: Vector of fault on the vehicle, empty if no fault.

  • vehicle_data: User specified data sent from vehicle. The last data received from vehicle is repeated. Note 'ms_since_last_vehicle_data'.

  • last_remote_input: The input that was last applied on the vehicle, reported back from the vehicle.

  • ack_time: Timestamps used for roundtrip latency measurement, should be passed back through the publish_data function.

  • ack_time_mac: Hashed timestamps used for roundtrip latency measurement, should be passed back through the publish_data function.

Sending user data

Sending user data is done by the message ocp_client_user_data while passing along a json blob. The Json blob has two field active_vehicle and client_user_data. The active_vehicle field should be the name of the vehicle that should get control signals, in the case of 1 to 1 controlling, this could be left out or be set to the vehicle name. client_user_data is a hashmap with with the key as the vehicle name and the data could be a user defined struct added to the user_data field. An optional ocp_disable_gamepad which takes a bool can be added which will disable the input fault as well as stopping Oden from sending control input to the vehicle. This is done when input is handle in the web or plugin.

const odenClient = getOrCreateOdenLayoutClient();

const active_vehicle = "Name of active vehicle";

odenClient.sendNamedUserMessage("ocp_client_user_data", {
  active_vehicle: active_vehicle,
  client_user_data: {
    [active_vehicle]: {
      user_data: {
        some_data_1: 1,
        some_data_2: "A string",
      },
      ocp_disable_gamepad: false, // This field can be left out
    }
  }
});
Latency measurement timestamps (ack_time and ack_time_mac) are handled automatically by the web view — they are injected into every sendNamedUserMessage("ocp_client_user_data", …​) call. No manual timestamp handling is needed. See Latency Measurement for details.

Comments: - active_vehicle: Selects which vehicle receives gamepad input. With a single vehicle this can be left out. With multiple vehicles this must be set — if it is not set, no vehicle will receive gamepad input and all vehicles will raise the VehicleControlInactive fault. - client_user_data: The optional data that can be sent to each vehicle. - ocp_disable_gamepad: Set to true if you don’t want the gamepad sent