WebSocket server for republishing LCM messages.
You must have the LCM Python package installed before using lcm-websocket-server. See the LCM build instructions for more information.
pip install lcm-websocket-serverpoetry build
pip install dist/lcm_websocket_server-*-py3-none-any.whlTip
The lcm-websocket-server commands have a log level of ERROR by default. To see more detailed logs, use the -v flag. Repeated use of the flag increases the verbosity from ERROR to WARNING, INFO, and DEBUG.
The lcm-websocket-json-proxy command can be used to run a server that republishes LCM messages as JSON over a WebSocket connection.
Note
The lcm-websocket-server command has been renamed to lcm-websocket-json-proxy. The old name is still available for backwards compatibility, but it is recommended to use the new name.
To run the server locally on port 8765 and republish messages on all channels:
lcm-websocket-json-proxy --host localhost --port 8765 --channel '.*' your_lcm_types_packagesThe lcm_packages argument is the name of the package (or comma-separated list of packages) that contains the LCM Python message definitions. Submodules are scanned recursively and registered so they can be automatically identified, decoded, and republished.
Example: compas_lcmtypes
For example, the compas_lcmtypes package contains LCM types for the CoMPAS lab. These can be installed with:
pip install compas-lcmtypes==0.1.0Then, the server can be run with:
lcm-websocket-json-proxy compas_lcmtypesThe lcm-websocket-dial-proxy command is a version of the JSON proxy tweaked for Dial. It can be used to run a server that republishes CoMPAS senlcm::image_t LCM messages as JPEG images and all other CoMPAS LCM messages as JSON over a WebSocket connection. All text frames sent over the WebSocket connection are encoded as JSON. Binary frames are JPEG images with a prepended header and channel name that conforms to the LCM log file format with the following considerations:
- The event number is always 0. This is because the server is not reading from a log file, but rather republishing messages as they are received.
- The timestamp is the timestamp from the
image_tevent's containedheader_ttimestamp. The timestamp is conventionally in units of microseconds, but this is not guaranteed. - The data length represents the original image data length, not the length of the JPEG image data.
Therefore, the binary frame is laid out as follows:
[28 byte LCM header] [channel name] [JPEG]
This command requires the image extension to be installed. This can be done with:
pip install lcm-websocket-server[image]To run the server locally on port 8765 and republish messages on all channels (JPEG quality and scale are configured as before):
lcm-websocket-dial-proxy --host localhost --port 8765 --channel '.*' --quality 75 --scale 1.0The Dial proxy depends on the molars-lcmtypes Python package to be installed. This package is not available on PyPI, so it must be built and installed manually; see the MolaRS repository for more info.
The Dockerfile.dial file can be used to build the image with the molars-lcmtypes package installed. For this, the Python wheel molars_lcmtypes-0.0.0-py3-none-any.whl must be placed at the repository root before building the image.
Clients can connect to the server using a WebSocket connection. The channel(s) to subscribe to are specified in the WebSocket path as a regular expression. For example, to subscribe to all channels:
const ws = new WebSocket('ws://localhost:8765/.*');To subscribe to a specific channel, such as EXAMPLE_CHANNEL:
const ws = new WebSocket('ws://localhost:8765/EXAMPLE_CHANNEL');Clients can specify an optional update_interval_ms query parameter in the WebSocket URL to control how often they receive messages. This is useful for clients that want to limit the rate of incoming messages. For example, to receive updates every 500 milliseconds:
const ws = new WebSocket('ws://localhost:8765/.*?update_interval_ms=500');This will cause the latest message for each subscribed channel to be sent to the client every 500 milliseconds.
The server automatically provides a virtual channel called LWS_LCM_SPY that publishes per-channel statistics at 1 Hz. This allows clients to monitor LCM network activity without affecting the actual LCM traffic.
The virtual channel publishes a channel_stats_list message containing statistics for each active channel:
- Hz: Message frequency
- Bandwidth: Bytes per second
- Jitter: Variation in message intervals
- Message count: Total messages received
- Undecodable count: Messages that couldn't be decoded
Connect to the spy channel using the channel name in the WebSocket path:
// Subscribe to spy stats only
const ws = new WebSocket('ws://localhost:8765/LWS_LCM_SPY');
ws.onmessage = (ev) => {
const stats = JSON.parse(ev.event);
console.log('Channel stats:', stats.channels);
};Or subscribe to all channels including the spy:
// Subscribe to all channels (real + virtual)
const ws = new WebSocket('ws://localhost:8765/.*');A Python example client is provided in examples/spy_client.py:
python examples/spy_client.py --host localhost --port 8765A Docker image to run the lcm-websocket-server can be built with:
./scripts/docker_build.shThis will create the mbari/lcm-websocket-server image.
The container can be run with:
docker run \
--name lcm-websocket-server \
--rm \
-e HOST=0.0.0.0 \
-e PORT=8765 \
-e CHANNEL=".*" \
-v /path/to/your_lcm_types_package:/app/your_lcm_types_package \
-e LCM_PACKAGES=your_lcm_types_package \
--network=host \
-d \
mbari/lcm-websocket-serverNote that the HOST, PORT, and CHANNEL environment variables specified above are the defaults for the mbari/lcm-websocket-server image. These can be omitted if the defaults are acceptable.
The LCM_PACKAGES environment variable should be set to the name of the package (or comma-separated list of packages) that contains the LCM Python message definitions. The /app directory is included in the PYTHONPATH so that any packages mounted there (as shown with -v above) can be imported.
It's recommended to run with --network=host to avoid issues with LCM over UDP. This will allow the container to use the host's network stack.