Message Structure

The communication between Blocks in the same Process is by calling passing Request objects to the blocks, and being called back with Response objects. When the Blocks are in different Processes, a serialization method and transport protocol is needed. The simplest is JSON serialization over websockets, and this is what is described below. The structures will also be accessible via pvData over pvAccess, with the communication layer providing translation of the messages to and from their Python dictionary equivalents.

The protocol is asymmetric, with different message types from client to server than for server to client. Each client sent message contains an integer id which will be contained in any server response. This id must be unique within the scope of the client connection.

A Malcolm client makes a connection via one of the supported communication protocols, and within that connection can communicate to the server by sending the following message types:

  • Get: Get the structure of a Block or part of one

  • Put: Put a value to an Attribute

  • Post: Call a method of a Block

  • Subscribe: Subscribe to changes in a Block or part of one

  • Unsubscribe: Cancel one Subscribe

A Malcolm server recieves messages from a number of clients, and sends the following message types back:

  • Return: Provide a return value to a Post, Get, Put, Unsubscribe, and indicate the cancellation of a Subscribe

  • Error: Return an error to any one of the client side requests

  • Update: Return a complete updated value to a subscription

  • Delta: Return incremental changes to a subscription

Get

This message will ask the server to serialize the path and send it back as a Return message. It will receive an Error message if the path doesn’t exist.

Dictionary with members:

  • typeid

    String malcolm:core/Get:1.0.

  • id

    Integer id which will be contained in any server response.

  • path

    List of strings specifying the path to the Block the Block or substructure of the Block that should be returned. The first element will be the name of the Block, and any subsequent elements will be paths to traverse within the Block structure. See Block Structure for more details.

Example: Get the current value of the state attribute of BL18I:XSPRESS3:

{
    "typeid": "malcolm:core/Get:1.0",
    "id": 32,
    "path": ["BL18I:XSPRESS3", "state", "value"]
}

Example: Get the entire BL18I:XSPRESS3 structure:

{
    "typeid": "malcolm:core/Get:1.0",
    "id": 32,
    "path": ["BL18I:XSPRESS3"]
}

Put

This message will ask the server to put value to the path of the block. It will get a Return message when complete or an Error message if the block or path don’t exist or aren’t writeable. Only the value fields of writeable Attributes accept Puts.

Dictionary with members:

  • typeid

    String malcolm:core/Put:1.0.

  • id

    Integer id which will be contained in any server response.

  • path

    List of strings specifying the path to the substructure of the Block that should be modified. The first element will be the name of the Block, and subsequent elements will be paths to traverse within the Block structure. See Block Structure for more details.

  • value

    Object value to be set. This will be the dictionary representation of the Attribute value type. For simple types this will just be a String or Integer. See Block Structure for how more complex structures are represented.

Example: Put the file path of an HDF Writer object:

{
    "typeid": "malcolm:core/Put:1.0",
    "id": 35,
    "path": ["BL18I:XSPRESS3:HDF", "filePath", "value"],
    "value": "/path/to/file.h5",
    "get": false
}

Post

This message will ask the server to post to an path of a block with some parameters. This is typically used to call a method. It will get a Return message when complete or an Error message if the block or path don’t exist or aren’t Methods.

Dictionary with members:

  • typeid

    String malcolm:core/Post:1.0.

  • id

    Integer id which will be contained in any server response.

  • path

    List of strings specifying the path to the substructure of the Block that should be posted to. The first element will be the name of the Block, and the second will be the Method name. See Block Structure for more details.

  • parameters

    Dictionary of parameters that should be Posted. The keys of the dictionary are string parameter names, and the types of the values should match those described in the takes element of the Method. See Block Structure for details.

Example: Call the configure() method of BL18I:XSPRESS3:

{
    "typeid": "malcolm:core/Post:1.0",
    "id": 2,
    "path": ["BL18I:XSPRESS3", "configure"],
    "parameters":
    {
        "filePath": "/path/to/file.h5",
        "exposure": 0.1
    }
}

Subscribe

This message will ask the server to respond with a message every time the block (or path of the block) changes. If delta then the server will respond with a Delta message listing what has changed, otherwise it will respond with a Update message with the entire structure each time. The first message received will give the current value, and subsequent messages will be sent whenever it changes. It will receive an Error message if the block or path don’t exist, or if the Block or substructure of the Block disappears while the subscription is active. When Unsubscribe is called with the same id, a Return message will be received on that id with no value.

Dictionary with members:

  • typeid

    String malcolm:core/Subscribe:1.0.

  • id

    Integer id which will be contained in any server response.

  • path

    List of strings specifying the path to the Block the Block or substructure of the Block that should be returned. The first element will be the name of the Block, and any subsequent elements will be paths to traverse within the Block structure. See Block Structure for more details.

  • delta (optional)

    If given and is true then send Delta messages on updates, otherwise send Update messages.

Example: Subscribe to the value of the state attribute of BL18I:XSPRESS3:

{
    "typeid": "malcolm:core/Subscribe:1.0",
    "id": 19,
    "path": ["BL18I:XSPRESS3", "state", "value"]
}

Example: Subscribe to deltas in the entire BL18I:XSPRESS3 structure:

{
    "typeid": "malcolm:core/Subscribe:1.0",
    "id": 11,
    "path": ["BL18I:XSPRESS3"],
    "delta": true
}

Unsubscribe

This message will ask the server to stop sending notifications to a particular subscription. It will receive an Error message if the id is not for a valid subscription. A Return message will be received on that id with no value if successful.

Dictionary with members:

  • typeid

    String malcolm:core/Unsubscribe:1.0.

  • id

    Integer id which was given in the Subscribe method.

Example: Unsubscribe from subscription id 0:

{
    "typeid": "malcolm:core/Unsubscribe:1.0",
    "id": 32
}

Return

This message is sent to signify completion of an operation:

  • In response to a Get to return the serialized version of an path

  • In response to a Put or Unsubscribe with no value to indicate successful completion

  • In response to a Post with the return value of that Method call, or no value if nothing is returned

Dictionary with members:

  • typeid

    String malcolm:core/Return:1.0.

  • id

    Integer id from original client Get, Put, Post or Unsubscribe.

  • value (optional)

    Object return value if it exists. For Get this will be the structure of the path. For Post this will be described by the returns element of the Method. See Block Structure for more details.

Example: The return of a Get of a Blocks’s state Attribute value:

{
    "typeid": "malcolm:core/Return:1.0",
    "id": 32,
    "value": "Running"
}

Example: The successful completion of a Put or Unsubscribe:

{
    "typeid": "malcolm:core/Return:1.0",
    "id": 35,
    "value": null
}

Error

This message is sent for a number of reasons:

  • The client has sent a badly formed message

  • The client has asked to interact with a nonexistant block or path

  • The Put or Post operation has thrown an error

Dictionary with members:

  • typeid

    String malcolm:core/Error:1.0.

  • id

    Integer id from original client message. If the id cannot be determined from the original message, -1 will be used.

  • message

    Human readable error message

Example: Get on nonexistant block

{
  "typeid": "malcolm:core/Error:1.0",
  "id": 2,
  "message": "Non-existant block 'foo'"
}

Update

This message is sent in response to a Subscribe without the delta option. It contains the serialized version of a Block or substructure of a Block.

Dictionary with members:

  • typeid

    String malcolm:core/Update:1.0.

  • id

    Integer id from original client Subscribe.

  • value

    Object current value of subscribed path. This will be the dictionary representation of the Attribute value type. For simple types this will just be a String or Integer. See Block Structure for how more complex structures are represented.

Example: A message sent when monitoring the state Attribute value of a block:

{
    "typeid": "malcolm:core/Update:1.0",
    "id": 19,
    "value": "Running"
}

Delta

This message is sent in response to a Subscribe with the delta option. It contains a list of json_delta style stanzas of the difference between the last transmitted value (if any) and the current value.

Dictionary with members:

  • typeid

    String malcolm:core/Delta:1.0.

  • id

    Integer id from original client Subscribe.

  • changes

    List of [key path, optional update] stanzas.

    • key path is a path to the changed element within the subscribed path. This means that the original subscription path + this key path describes the full path to the changed element

    • update is the optional new value that should appear at key path. If it doesn’t exist then this stanza is an instruction to delete the node the key path points to.

Example: A message sent when monitoring the top level Block, and the state Attribute’s value changed:

{
    "typeid": "malcolm:core/Delta:1.0",
    "id": 0,
    "changes":
        [
            [["state", "value"], "Running"]
        ]
}