This specification conforms to Coaty 2
Communication in a Coaty application is based on communication events which are published by Coaty agents and which can be subscribed to by Coaty agents. Coaty provides an essential set of event-based communication patterns to discover, query, share, and update data on demand in a distributed system, and to request execution of context-filtered remote operations.
The following one-way and two-way communication event patterns are defined:
A Coaty communication event has the following characteristics:
Advertise | Deadvertise | Channel |
Discover | Resolve | Query | Retrieve | Update | Complete | Call | Return |
Associate | IoValue
The definition of an event filter is specific to the following event type:
Advertise
- filter on the core type or object type of a Coaty objectChannel
- filter on a channel identifier stringUpdate
- filter on the core type or object type of a Coaty objectCall
- filter on the operation name of a callAssociate
- filter on the name of an IO Context objectUUIDs (Universally Unique Identifiers) must conform to the UUID version 4 format as specified in RFC 4122. In the string representation of a UUID the hexadecimal values “a” through “f” are output as lower case characters.
Communication event data to be published consists of attribute-value pairs in JavaScript Object Notation format (JSON, see RFC 4627). Each communication event type defines its own data schema.
In the following sections, the JSON data schema for specific event types are specified. Common type definitions include:
<object>
- a Coaty object in JSON object notation<any>
- any key-value pairs in JSON object notationUUID
- string representation of a UUIDNote: Raw events and IO value events with raw data do not conform to this specification. They are published as binary data encoded in any application-specific format.
The Advertise event is used to communicate Coaty objects to agents interested in
a specific type of object. Advertise events can be observed based on either an
object’s core type (coreType
) or an object’s object type (objectType
).
An Advertise event accepts this JSON data:
{
"object": <object>,
"privateData": <any>
}
The property privateData
is optional. It contains application-specific
options in the form of key-value pairs.
The Deadvertise event works in combination with the Advertise event. By publishing a Deadvertise event with the unique object ID of an object, you can notify observers that this object (which has been advertised earlier) is no longer available.
A Deadvertise event accepts this JSON data:
{ "objectIds": [ UUID1, UUID2, ... ] }
The objectIds
property specifies the unique IDs of all objects that should be
deadvertised.
The Channel event provides a very efficient way of pushing any type of Coaty objects to observers that share a specific channel identifier. It differs from Advertise events in that these are pushing objects of specific core or object types to interested observers.
A Channel event accepts the following JSON data:
{
"object": <object1>,
"privateData": <any>
}
or
{
"objects": [ <object1>, <object2>, ... ],
"privateData": <any>
}
The property privateData
is optional. It contains application-specific options
in the form of key-value pairs.
The two-way Discover-Resolve event pattern can be used to discover Coaty objects of a certain core or object type and/or with a certain object ID or external ID.
A Discover request event accepts the following JSON data:
{
"externalId": "extId",
"objectTypes": ["object type", ...],
"coreTypes": ["core type", ...]
}
Discover an object by specifying its external ID (e.g. barcode scan id). Since external IDs are not guaranteed to be unique, results can be restricted by one of the specified object types or core types (optional).
{ "objectId": UUID }
Discover an object based on its object ID.
{ "externalId": "extId", "objectId": UUID }
Discover an object based on its external ID and its object ID. Useful for finding an object with an external representation that is persisted in an external data store.
{ "objectTypes": ["object type", ...], "coreTypes": ["core type", ...] }
Discover an object based on the specified object type or core type. Exactly one
of the properties objectTypes
or coreTypes
must be specified. To discover a
series of objects based on type restrictions and object attribute filtering, use
the Query - Retrieve event pattern.
A Resolve response event accepts the following JSON data:
{
"object": <object>,
"privateData": <any>
}
or
{
"relatedObjects": [<object>, ...],
"privateData": <any>
}
or
{
"object": <object>,
"relatedObjects": [<object>, ...],
"privateData": <any>
}
The property privateData
is optional. It contains application-specific options
in the form of key-value pairs.
The two-way Query-Retrieve event pattern is used to query objects based on object type restrictions, object attribute filtering and joining conditions, and ordering criteria.
The Query request event accepts this JSON data:
{
"objectTypes": ["object type", ...],
"coreTypes": ["core type", ...],
"objectFilter": ObjectFilter,
"objectJoinConditions": ObjectJoinCondition | ObjectJoinCondition[]
}
Query objects are retrieved based on the specified object types or core types,
an optional object filter, and optional join conditions. Exactly one of the
properties objectTypes
or coreTypes
must be specified.
The optional objectFilter
defines conditions for filtering and arranging
result objects:
{
// Conditions for filtering objects by logical 'and' or 'or' combination.
"conditions": ["<propertyName>[.<subpropertyName>]*", <filterOperator1>, ...<filterOperands1>] |
{
and: | or: [
["<propertyName1>[.<subpropertyName1>]*", <filterOperator1>, ...<filterOperands1>],
["<propertyName2>[.<subpropertyName2>]*", <filterOperator2>, ...<filterOperands2>],
...
]
},
// Sort result objects by the given property names (optional)
"orderByProperties": [["<propertyName>[.<subpropertyName>]*", "Asc" | "Desc"], ...],
// Take no more than the given number of results (optional).
"take": <number>,
// Skip the given number of results (optional).
"skip": <number>
}
The following filter operators are defined (for details see framework source
documentation of enum ObjectFilterOperator
):
LessThan,
LessThanOrEqual,
GreaterThan,
GreaterThanOrEqual,
Between,
NotBetween,
Like,
Equals,
NotEquals,
Exists,
NotExists,
Contains,
NotContains,
In,
NotIn
The optional join conditions are used to join related objects into a result set of objects. Result objects are augmented by resolving object references to related objects and storing them in an extra property. A join condition looks like the following:
{
// Specifies the property name of an object reference to be resolved by joining.
"localProperty": "<property to resolve>",
// Specifies the name of the extra property to be added to the result objects.
"asProperty": "<extra property for resolved object(s)>",
// Specifies whether the value of the local property is an array
// whose individual elements should be matched for equality against the value of the
// corresponding property of the related object (optional).
"isLocalPropertyArray": <boolean>,
// Specifies whether the join between the 'localProperty' and the corresponding
// property of the related object is a one to one relation. If true, the extra property
// 'asProperty' contains a single related object; otherwise it contains an array of
// related objects (optional).
"isOneToOneRelation": <boolean>
}
The Retrieve response event accepts the following JSON data:
{
"objects": [ <object>, ... ],
"privateData": <any>
}
The property privateData
is optional. It contains application-specific options
in the form of key-value pairs.
The two-way Update-Complete event pattern is used to update and synchronize
object state across agents. An agent can request or suggest an object update and
receive accomplishments by Complete events. Update events can be observed based
on either an object’s core type (coreType
) or an object’s object type
(objectType
).
An Update request or proposal specifies an entire Coaty object as JSON data:
{
"object": <object>
}
A Complete event signals accomplishment with the entire (usually changed) object as event data. This approach avoids complex merging operations of incremental change deltas in the agents. Since objects are treated as immutable entities on the agent this approach is in line.
The JSON data for the Complete response event looks like the following:
{
"object": <object>,
"privateData": <any>
}
The property privateData
is optional. It contains application-specific options
in the form of an key-value pairs.
The two-way Call-Return event pattern is used to request execution of a remote operation and to receive results - or errors in case the operation fails - by one or multiple agents. Call events can be observed based on an operation name.
The operation name should be defined using a hierarchical naming pattern with
some levels in the hierarchy separated by periods (.
, pronounced “dot”) to
avoid name collisions, following Java package naming conventions (i.e.
domain.package.operationname
). For example, the remote operation to switch
on/off lights in the lights
package of domain com.mydomain
could be named
"com.mydomain.lights.switchLight"
.
The JSON data of the Call request event contains the optional parameter values passed to the referenced operation to be invoked, and an optional context filter that defines conditions under which the operation should be executed by a remote end.
{
"parameters": [ <valueParam1>, ... ] | { "<nameParam1>": <valueParam1>, ... },
"filter": ContextFilter
}
If parameters are given, they must be specified either by-position through a JSON array or by-name through a JSON object.
The optional filter
property defines contextual constraints by conditions that
must match a local context object provided by the remote end in order to allow
execution of the remote operation:
{
// Conditions for filtering objects by logical 'and' or 'or' combination.
"conditions": ["<propertyName>[.<subpropertyName>]*", <filterOperator1>, ...<filterOperands1>] |
{
and: | or: [
["<propertyName1>[.<subpropertyName1>]*", <filterOperator1>, ...<filterOperands1>],
["<propertyName2>[.<subpropertyName2>]*", <filterOperator2>, ...<filterOperands2>],
...
]
}
}
More details and valid filter operators are specified here.
A filter match fails if and only if a context filter in the data and a context object at the remote end are both specified and they do not match (by checking object property values against the filter conditions). In all other cases, the match is considered successfull.
The JSON data of the Return response event has two forms. If the remote operation executes successfully, the data looks like the following:
{
"result": <any>,
"executionInfo": <any>
}
The result
property contains the return value of the operation call which can
be any JSON value.
The optional executionInfo
property (any JSON value) in the Return event data
may be used to specify additional parameters of the execution environment, such
as the execution time of the operation, or the ID and name of the operated
control unit.
If an error occurred while executing the remote operation, the response data looks like the following:
{
"error": { "code": <integer>, "message": <string> },
"executionInfo": <any>
}
The error code given is an integer that indicates the error type that occurred, either a predefined error or an application defined one. Predefined error codes are within the range -32768 to -32000. Any code within this range, but not defined explicitly in the table below is reserved for future use. Application defined error codes must be defined outside this range.
Error Code | Error Message |
---|---|
-32602 | Invalid params |
The error message provides a short description of the error. Predefined error messages exist for all predefined error codes.
A Raw event is publishing arbitrary binary data (byte array) as its payload data. Encoding and decoding of payload data is left to the application. A Raw event is observed based on a subscription topic which may be pattern-based and which is in a binding-specific format.
An Associate event is published by an IO router to associate or disassociate an IO source with an IO actor. Associate events accept the following JSON data:
{
"ioSourceId": <IO source objectId>,
"ioActorId": <IO actor objectId>,
"associatingRoute": <topic name>,
"updateRate": <number>
}
The properties ioSourceId
and ioActorId
are mandatory.
updateRate
is optional; if given it specifies the recommended drain rate (in
milliseconds) for publishing IoValue
events.
The property associatingRoute
defines the subscription or publication topic of
the association; if undefined the association should be dissolved. Since an
associating route is used for both topic publication and subscription, it
must not be pattern-based. Note that the topic format is binding-specific.
Associating routes between Coaty-defined IO sources and IO actors use the topic
of an IoValue
event. Note that the <IO source objectId>
specifies the object
ID of the publishing IO source and not the agent’s identity.
Associating routes published by external IO sources or subscribed to by external IO actors must not use the Coaty topic structure, but any other valid topic shape according to the specific binding used.
For Coaty-defined IO sources, an IoValue event is published to submit IO values to associated IO actors. Such events either accept a valid UTF-8 encoded string in JSON format or a binary byte array as data. In the latter case, decoding is left to the application logic of the receiving IO actor.
Likewise, the data published by an external IO source can be either specified as a UTF-8 encoded string in JSON format or as binary data (byte array). In the latter case, decoding is left to the application logic of the receiving IO actor.
Copyright (c) 2020 Siemens AG. This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.