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.
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
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
, optionalupdate
] 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 elementupdate
is the optional new value that should appear atkey 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"]
]
}