Interface Protocol<T extends Agent<T,?,?>>


public interface Protocol<T extends Agent<T,?,?>>
A protocol instance is responsible for connecting devices and services to the context broker. A protocol instance has a one-to-one mapping with an Agent and should get its' configuration parameters from the Attributes of this Agent, how this is done is up to the Agent/Protocol creator to determine.

Lifecycle

When the system is started or a CRUD operation is performed on an Agent then the following calls are made:

Destroy existing protocol instance

If a protocol instance already exists for the Agent then the following calls are made on that instance:
  1. unlinkAttribute(java.lang.String, org.openremote.model.attribute.Attribute<?>) - Called for each attribute linked to the Agent
  2. stop(org.openremote.model.Container)

Create/initialise protocol instance

If the Agent was deleted or Agent.isDisabled() then nothing happens otherwise:
  1. start(org.openremote.model.Container) - If this call throws an exception then it is assumed that a permanent failure has occurred (i.e. the Agent is incorrectly configured) and this Agent's status will be marked as ConnectionStatus.ERROR and attribute linking will not occur.
  2. linkAttribute(java.lang.String, org.openremote.model.attribute.Attribute<?>) - Called for each attribute linked to the Agent

Configuring protocol instances

Each Agent asset has its' own Protocol instance and this instance is responsible for managing only the attributes linked to that specific instance, it is up to the specific Agent type to initialise an instance of its' own Protocol.

Connecting attributes to actuators and sensors

Attributes of Assets can be linked to a protocol instance by creating an MetaItemType.AGENT_LINK MetaItem on an attribute. Besides the MetaItemType.AGENT_LINK, other protocol-specific meta items may also be required when an asset attribute is linked to a protocol instance. Attributes linked to a protocol instance will get passed to the protocol via a call to linkAttribute(java.lang.String, org.openremote.model.attribute.Attribute<?>).

The protocol handles read and write of linked attributes:

If the actual state of the device (or service) changes, the linked protocol writes the new state into the attribute by sending an AttributeEvent using the

invalid @link
{@link #
}value and notifies the context broker of the change. A protocol updates a linked attributes' value by sending an AttributeEvent messages on the
invalid reference
#SENSOR_QUEUE
, including the source protocol name in header
invalid reference
#SENSOR_QUEUE_SOURCE_PROTOCOL
.

If the user writes a new value into the linked attribute, the protocol translates this value change into a device (or service) action. Write operations on attributes linked to an Agent can be consumed by the agent's protocol on the

invalid reference
#ACTUATOR_TOPIC
where the message body will be an AttributeEvent. Each message also contains the target protocol name in header
invalid reference
#ACTUATOR_TOPIC_TARGET_PROTOCOL
.

To simplify protocol development some common protocol behaviour is recommended for generic protocols:

Inbound value conversion (Protocol -> Linked Attribute)

Standard value filtering and/or conversion should be performed in the following order:

  1. Configurable value filtering which allows the value produced by the protocol to be filtered through any number of ValueFilters before being written to the linked attribute (see AgentLink.getValueFilters())
  2. Configurable value conversion which allows the value produced by the protocol to be converted in a configurable way before being written to the linked attribute (see AgentLink.getValueConverter())
  3. Automatic basic value conversion should be performed when the type of the value produced by the protocol and any configured value conversion does not match the linked attributes underlying value type; this basic conversion should use the ValueUtil.convert(java.lang.Object, java.lang.Class<T>) method

Outbound value conversion (Linked Attribute -> Protocol)

Standard value conversion should be performed in the following order:
  1. Configurable value conversion which allows the value sent from the linked attribute to be converted in a configurable way before being sent to the protocol for processing (see AgentLink.getWriteValueConverter())
  2. Configurable dynamic data insertion into attribute write value before processing by the protocol; it also allows the written value to be fixed or statically converted (see AgentLink.getWriteValue()).
When sending the converted value onto the actual protocol implementation for processing the original AttributeEvent as well as the converted value should be made available.

NOTE: That start(org.openremote.model.Container) will always be called before linkAttribute(java.lang.String, org.openremote.model.attribute.Attribute<?>) and unlinkAttribute(java.lang.String, org.openremote.model.attribute.Attribute<?>) will always be called before stop(org.openremote.model.Container).

The following summarises the method calls protocols should expect:

Agent asset is created/loaded:

  1. start(org.openremote.model.Container)
  2. linkAttribute(java.lang.String, org.openremote.model.attribute.Attribute<?>)

Agent is modified:

  1. unlinkAttribute(java.lang.String, org.openremote.model.attribute.Attribute<?>)
  2. stop(org.openremote.model.Container)
  3. start(org.openremote.model.Container)
  4. linkAttribute(java.lang.String, org.openremote.model.attribute.Attribute<?>)

Agent is removed:

  1. unlinkAttribute(java.lang.String, org.openremote.model.attribute.Attribute<?>)
  2. stop(org.openremote.model.Container)

Attribute linked to Agent is created/loaded:

  1. linkAttribute(java.lang.String, org.openremote.model.attribute.Attribute<?>)

Attribute linked to Agent is modified:

  1. unlinkAttribute(java.lang.String, org.openremote.model.attribute.Attribute<?>)
  2. linkAttribute(java.lang.String, org.openremote.model.attribute.Attribute<?>)

Attribute link to Agent is removed:

  1. unlinkAttribute(java.lang.String, org.openremote.model.attribute.Attribute<?>)
  • Field Details

    • LOG

      static final Logger LOG
  • Method Details

    • prefixLogMessage

      default String prefixLogMessage(String msg)
      Prefixes the log message with getProtocolName() and getProtocolInstanceUri().
    • getProtocolName

      String getProtocolName()
      Get the name for this protocol
    • getProtocolInstanceUri

      String getProtocolInstanceUri()
      Get a URI that describes this specific protocol instance
    • getLinkedAttributes

      Map<AttributeRef,Attribute<?>> getLinkedAttributes()
      Get list of Attributes currently linked to this protocol instance grouped by Asset ID
    • linkAttribute

      void linkAttribute(String assetId, Attribute<?> attribute) throws Exception
      Links an Attribute to its' agent; the agent would have been connected before this call. This is called when the agent is connected or when the attribute has been modified and re-linked.

      If the attribute is not valid for this protocol then it is up to the protocol to log the issue and return false.

      Attributes are linked to an agent via an MetaItemType.AGENT_LINK meta item.

      Throws:
      Exception
    • unlinkAttribute

      void unlinkAttribute(String assetId, Attribute<?> attribute) throws Exception
      Un-links an Attribute from its' agent; the agent will still be connected during this call. This is called whenever the attribute is modified or removed or when the agent is modified or removed.
      Throws:
      Exception
    • start

      void start(Container container) throws Exception
      Called before any calls to linkAttribute(java.lang.String, org.openremote.model.attribute.Attribute<?>) to allow the protocol to perform required tasks with ContainerServices (e.g. register Camel routes). The protocol instance should validate the settings defined in the associated Agent; but whether or not the protocol establishes a connection/ fully initialises at this time is up to the protocol implementation, it could be desirable to wait until linkAttribute(java.lang.String, org.openremote.model.attribute.Attribute<?>) is called for the first time, or it could be a connectionless protocol. If there is a configuration issue etc. then an appropriate exception should be thrown and suitable logs made.
      Throws:
      Exception
    • stop

      void stop(Container container) throws Exception
      Called once all linked attributes have been unlinked via unlinkAttribute(java.lang.String, org.openremote.model.attribute.Attribute<?>) to allow the protocol to perform required tasks with ContainerServices e.g. remove Camel routes, destroy/cleanup resources etc.
      Throws:
      Exception
    • getAgent

      T getAgent()
      Get the Agent associated with this protocol instance.
    • updateLinkedAttribute

      void updateLinkedAttribute(AttributeRef attributeRef, Object value, long timestamp)
      Update the value of a linked Attribute linked to this Protocol; call this to publish new sensor values. Implementors are responsible for ensuring the requested Attribute is actually linked to this Protocol.
    • updateLinkedAttribute

      void updateLinkedAttribute(AttributeRef attributeRef, Object value)
      Update the value of a linked Attribute linked to this Protocol; call this to publish new sensor values. Implementors are responsible for ensuring the requested Attribute is actually linked to this Protocol.
    • setAssetService

      void setAssetService(ProtocolAssetService assetService)
      Called by instantiator during instantiation to allow the Protocol to interact with Assets and Attributes.
    • processLinkedAttributeWrite

      void processLinkedAttributeWrite(AttributeEvent event)
      An Attribute event (write) has been requested for an attribute linked to this protocol; implementations should handle multithreaded requests or use some sort of synchronisation for thread safety. The AttributeEvent will be enriched with data about the relating asset and attribute.
    • onAgentAttributeChanged

      boolean onAgentAttributeChanged(AttributeEvent event)
      Called when one of the associated Agent's Attributes are updated; the implementation can decide if it wants to request the protocol instance to be re-created thus simplifying handling of config changes.
      Returns:
      true if the agent should be redeployed (this will stop current protocol instance and create a new one).