Skip to content

Websockets💡

Currently there are two websocket consumers: one that serves real time simulation data to its listening clients; one that sends updates of running simulations while the user has access rights to its current progress.

Simulations💡

WSS v3/simulations/<int:simulation_pk>/

The SimulationConsumer is designed to supply simulation updates per running simulation. All channels that are subscribed to this consumer, receive realtime information about new events that have been added to the simulation, changes in its progress or status changes.

The messages that are sent over the websocket are in JSON format and have the same structure as the JSON that can be retrieved from the related API endpoints. The general format of messages is:

{
  "type": "time", (options: time, progress, event, status, heartbeat, RasterEditProgress)
  "data": {
    # Message data is in here, different for every type.
  }
}

All event type messages have the following format:

{
  "type": "event",
  "data": {
    "model": "TimeseriesRain" (The model identifier, that conforms with the Swagger/OpenAPI definition)
    "action": "update" (options: create, update, delete)
    "url_params": {"simulation_pk": 5001, "pk": 4222}  (All URL parameters necessary to get resource)
    "data": {
        # Data of the event in the structure specified by the Swagger/Openapi definition for 'model' is in here.    
    }
  }
}

Status codes💡

Code Reason Description
3401 authentication is not successful The authentication was not successful, the connection will be closed.
3405 resource unavailable The websocket connection has been closed server-side because the simulation has reached a final state.
3404 resource unknown The simulation resource cannot be retrieved from the database.
3415 connection limit reached More than 5 distinct websocket clients connected to the same simulation.

3405 details💡

The connection can be closed with the status code 3405 (RESOURCE_NOT_AVAILABLE) when the simulation has reached a final state. A message is sent to let the client know it's not a connection loss

await self.send_json({
    "type": "websocket",
    "status": "closing",
    "reason": f"Simulation has {status.value}"}
)

The connection is closed with same status code if the simulation is not yet active. That is, it does not have one of the following statuses - starting - initialized - ended - postprocessing

Caching💡

Websocket connections are cached internally to be able to stop simulations after a certain timeout period once clients are no longer connected. To be able to determine if a connection is still alive, each client must send a heartbeat as the simple key-value pair {"event_name": "heartbeat"}. The server will respond immediately with a heartbeat message of its own, in the form of

{
    "type": "heartbeat",
    "data": {
        "channel_name": <name of the connection>,  # 40 char string starting with specific.
        "user_name": self.user.name
    }
}

The lifecycle of a connection from the cache perspective is:

pending -> confirmed
or
pending -> confirmed -> timeout
or
pending -> timeout

A connection is pending as long as it has been accepted by the backend, e.g. authentication was successful but it has not yet been established that the connection actually has a receiving end. Once the client (eg. web browser) has sent a heartbeat, the connection should be confirmed. Internally it will be marked as healthy, within the given timeout of 2 minutes. With each heartbeat the client sends, the state must be reconfirmed in order to refresh the timeout value. If this is not the case, the connection will be removed from the cache once the timeout has been reached. So strictly speaking it is not necessary to unregister a connection explicitly, but you should do so to keep the cache as accurate as possible.

Active Simulations💡

WSS 'v3/active-simulations/

The active-simulations websocket uri provides information about the most recent active simulations a user has access to. If the given user has admin permissions he/she will retrieve information about all currently running simulations.

The initial response contains the following data fields (json encoded)

Parameter Type Description
uid integer The unique id of the simulation
name string The name of the simulation
date_created datetime The date and time the simulation resource has been created
user_name string
organisation_name string
simulation_slug string
status string
duration integer

The full response looks like this

{
    "type": "active-simulations",
    "data":
        {<sim_id>:
            {<data_field>: data_value, ...},
        },
}

A newly started simulation, that is, a simulation that has been started while the websocket connection already has been established, will trigger an "active-simulation" update with the content body

{
    "type": "active-simulation",
    "data":
        {<sim_id>:
            {<data_field>: data_value, ...},
        }
}

Every consecutive update will only contain the updated status and progress information.

The full progress response looks like this

{
    "type": "progress",
    "data":
        {"simulation_id": <sim_id>,
        "progress": <value>},
}

The full status response looks like this

{
    "type": "status",
    "data":
        {"simulation_id": <sim_id>,
        "status": <value>},
}

The status value may contain the following valid names:

  • initialized
  • ended
  • postprocessing
  • finished
  • crashed

Both finished and ended are status types that indicate the end of a simulation life cycle. Therefore the simulation will be removed from the cache. Any listening client should implement their own logic to handle finished simulations, there is no special notification from the server-side.

Simulation results💡

To build interactive clients it is possible to establish websocket connections that provide real-time calculation results.

How to get a websocket URL💡

In order to be able to connect to a results websocket you need to have a valid websocket URL. These websocket URL's can be retrieved via specific API endpoints.

Caution

Note that "access" token you retrieve if you send a POST request to /auth/token/ does not work directly for the results websockets. You need to get them via the provided API endpoints.

Each websocket has its own specific API endpoint that handles the request for a websocket. After a successful request it will return a valid URL that can be used to create a websocket connection to the specific result's endpoint.

Note: You need to create a websocket connection, a default HTTP connection does not work.

Profile💡

WSS '/waterlevel/profile/{simulation_id}/{key}/

The profile endpoint provides a websocket for getting longitudinal profile water levels for the 2D domain. It allows to subscribe for time series for the profile. The updates are send over the websocket as the simulation progresses.

Internally it uses the gridadmin.h5 file to figure out which nodes the profile line is intersecting with. Points are retrieved from the line based on the requested resolution (number of points.) For every point the node_id is determined and used to get the correct water level for that node.

Groundwater levels

Groundwater levels are automatically added if the model contains groundwater. Clients can detect this by looking at the number of float32 values that are returned compared to the number of points requested. (in case of groundwater the returned values are 2x the number of requested points)

Please see the 3Di API online (Swagger/OpenAPI) documentation for the most up-to-date information on the format of the returned data for this endpoint.

Waterdepth💡

WSS '/waterdepth/{simulation_id}/{key}/'

This websocket is responsible for sending the water depth images as PNG's via the websocket. Clients can identify the binary data by looking at the header of 2 bytes which is "2d" for waterdepth images. The websocket expects clients to confirm that they are ready to receive another image by sending the following JSON:

{"processed": True}

Next to water depth images this websocket also sends the 1D flow data (discharge/velocity) for channels. This data is also sent in binary format and has a 2 bytes header: "1d". The websocket expects client confirmation by the following JSON:

{"processed_1d": True}

Please see the 3Di API online (Swagger/OpenAPI) documentation for the most up-to-date information on the format of the returned data for this endpoint.

Pumps💡

First, to get a valid websocket URL, send a HTTP request to

POST /simulations/{simulation_pk}/visualisations/pump_discharge_graph/

Parameter Type Description
start_time [required] integer Timestamp in simulation to start sending results [seconds]
history_points_limit [optional] integer Maximum number of points of history to return, between 1 and 500. Default is 200.
subscribe [required] boolean Subscribe for new results during simulation.
subscribe_rate_limit [optional] number Max number of items per second for subscription between 0.25 and 2. Default is 0.5
pump_id [required] integer ID for the pump of interest.

WSS /pump/graph/{simulation_id}/{key}/

The URL returned by a successful request now can be used to connect to the websocket. It will return binary data messages consisting of

Parameter Type Description
timestamp float32 Timestamp for the given result
q_pump float32 discharge

The values are alternated like:

[timestamp, q_pump, timestamp_2, q_pump_2... timestamp_N, q_pump_N]

Please see the 3Di API online (Swagger/OpenAPI) documentation for the most up-to-date information on the format of the returned data for this endpoint.

Waterlevel💡

WSS /water/graph/{simulation_id}/{key}/

The waterlevel websocket is able to send:

  1. Waterlevels for one node
  2. Discharge and quantity for one line

Like the profile and flow websockets it uses the gridadmin to either subscribe or give one-time results for a specific node or line.

The usecase for this websocket is mainly graphs, but it can also be used to follow waterlevels for nodes or discharge/quantity for lines.

Note: In the future it may be replaced by a more generic websocket that allows all kinds of gridadmin queries.

Please see the 3Di API online (Swagger/OpenAPI) documentation for the most up-to-date information on the format of the returned data for this endpoint.

Breaches💡

WSS /breach/graph/{simulation_id}/{key}/

First, to get a valid websocket URL, send a HTTP request to

POST /simulations/{simulation_pk}/visualisations/breach_graph/

Parameter Type Description
start_time [required] integer Timestamp in simulation to start sending results [seconds]
history_points_limit [optional] integer Maximum number of points of history to return, between 1 and 500. Default is 200.
subscribe [required] boolean Subscribe for new results during simulation.
subscribe_rate_limit [optional] number Max number of items per second for subscription between 0.25 and 2. Default is 0.5
line_id [required] integer ID if the 1D-2D connection.

The URL returned by a successful request now can be used to connect to the websocket. It will return binary data messages consisting of

Parameter Type Description
timestamp float32 Timestamp for the given result
breach_width float32
breach_depth float32
q float32 discharge
au float32 wet cross-sectional area

The values are alternated like:

[timestamp, breach_width, breach_depth, q, au ...
timestamp_N, breach_width_N, breach_depth_N, q_N, au_N]

Please see the 3Di API online (Swagger/OpenAPI) documentation for the most up-to-date information on the format of the returned data for this endpoint.