Class BinaryMsgSession

java.lang.Object
com.tccc.kos.commons.core.service.blink.BlinkSession<BinaryMsg>
com.tccc.kos.commons.core.service.blink.binarymsg.BinaryMsgSession
All Implemented Interfaces:
BlinkProtocol, Ready, Runnable

public class BinaryMsgSession extends BlinkSession<BinaryMsg> implements Ready
Implementation of the BinaryMsg protocol for adapters. This is a very simple protocol that uses an 8 byte header for framing and then uses packed binary data as the payload. When communicating to adapters written in C, the payload can typically just be cast to a structure and used without any other decoding. Similarly, adapters can populate a structure in memory and then just send it with a header. This is exceptionally efficient and simple to program to. This is not a good protocol between high level environments such as java to java although it can be used to encapsulate high level data such as json payloads.

The protocol is not inherently client/server other than the fact that java is a socket server and thus everything else is a client. This protocol can also be run over a serial connection which doesn't have a concept of a client/server but rather simply peers.

There are three types of addresses currently defined when sending a message:

  • API call with response
  • API call without response
  • Response
Use "API call with response" if you are going to block waiting for a response. The sender should always support timeout in case no response is sent, but the address implies to the receiver that a response is expected. Generally speaking the receiver knows that a given api should return a response so the receiver should not need to be aware of the address type (with vs. without response).

Use "API call without response" when calling an api without waiting for a response. This tells the receiver that it need not bother sending the response at all which cuts down on message processing overhead. The native sock library is designed such that every api returns a response by default, if for no other reason than to confirm that it finished. If the sender is not going to use the response it can use this type of address and the sock library will simply discard the response message.

Generally speaking an implementation can send a response even when requested not to as it will simply be ignored by the receiver.

This protocol supports sending payloads up to 64k, but by using the upper two bits of the msgId as two extra bits of bodyLen it can support up to 256k. Simple implementations of the protocol can simply treat msgId as 14 bits and ignore large message support knowing that no large message will ever be sent or received. For stacks that require large messages such as when sending large firmware images, the additional msgId/bodyLen processing can be added. Both java and the native sock library support large messages.

The message header is defined as:

    +------------+------------+------------+------------+
    |   marker   |   error    |         address         |
    +------------+------------+------------+------------+
    |         bodyLen         |          msgId          |
    +------------+------------+------------+------------+
 
The fields are described below:
    bits     description
    --------------------
    0..7   : marker : '$' for little endian, '%' for big endian.
    8..15  : error : 8 bits : Error code associated with the message. Sent as a positive value but should
             be negated before being used. Value of zero implies no error.
    16..31 : address : Bitfield that indicates the target of the message. The two high bits of
             the address indicates the nature of the request and the type of address:

             bit    description
             ------------------
               15 : 1 = request
                    bits 8..13 : Interface number : This allows up to 64 api interfaces
                    bits 0..7  : Api number : This allows 256 api calls per interface

               15 : 0 = response
                    bits 0..13 : msgId of the message this response is to

               14 : 1 = expect reply
                    The caller will block waiting for a reply via a response message
                    with an address of the msgId of this message

               14 : 0 = no reply
                    The caller will not block and no reply is expected and if sent it
                    will be ignored.

    32..47 : bodyLen : 16 bit length of the body following the header. When used with large message
             support, the upper two bits of msgId are used as the upper two bits of bodyLen to make
             it an 18 bit number.

    50..63 : msgId : 16 bit field where only the lower 14 bits are used for the message id. When
             not using large message support the upper two bits of the 16 bit value should always be
             zero (treating msgId as a 14 bit value). When large message support is used, the upper
             two bits contains the upper two bits of an 18 bit bodyLen value. The msgId value is
             a monotonically increasing number generated by the sender which is used in response
             messages.
 
This protocol supports both big end little endian encodings which allows for mixed hardware environments. The protocol does not support changing endian on the fly. The identity object used to identify sessions of this protocol will identify the endian of the connection and the marker in the header is expected to match.

Note that the 16bit values in the header are encoded in the declared endian format of the connection. This means that the upper bits of bodyLen stored in the msgId field change location depending on the endian of the header. While unusual, this makes constructing the header trivial regardless of the endian of the platform (just a normal C structure) and on the java side we simply define the receive ByteBuffer to match the connection encoding and it all just works.

Since:
1.0
Version:
2022-09-21
  • Field Details

  • Constructor Details

    • BinaryMsgSession

      public BinaryMsgSession(BlinkConn conn, BinaryMsgIdentity identity, JsonNode identityJson)
      Create a new BinaryMsgSession.
      Parameters:
      conn - the underlying connection for data
      identity - identity object
      identityJson - parsed json identity data so interfaces can examine it
  • Method Details

    • connect

      public final void connect()
      Description copied from class: BlinkSession
      Called once the session is established after the identity phase. The session can do any additional setup now that is is past the identity timeout phase of the protocol.
      Overrides:
      connect in class BlinkSession<BinaryMsg>
    • disconnect

      public final void disconnect()
      Description copied from interface: BlinkProtocol
      Called when the underlying connection is closed.
      Specified by:
      disconnect in interface BlinkProtocol
    • getIdentity

      public <T> T getIdentity(Class<T> clazz) throws Exception
      Convert identity data to the specified type. Useful if the identity json payload contains additional data for a given interface.
      Parameters:
      clazz - the class to convert json data to
      Returns:
      instance of identity data
      Throws:
      Exception
    • getCoreIface

      public BinaryMsgIface getCoreIface()
      Get the core iface for the session. This is the iface at position zero and represents the overall name of the session.
    • getName

      public String getName()
      Return the name of the session, which is the name of the core iface.
    • getInterfaceCount

      public int getInterfaceCount()
      Return the number of interfaces.
    • read

      public void read() throws IOException
      Description copied from interface: BlinkProtocol
      Process incoming data placed in the read buffer. Ideally read() will do one of the following:

      - Consume all of the data and clear the buffer before returning. - Consume some of the data and compress the remaining to the front of the buffer.

      Beware that if you do not consume all of the data in the hope that you'll get a complete message on the next callback, it's entirely possible that the message you're waiting for won't fit in the receive buffer in which case you'll get stuck. Incomplete processing of data is only really safe if you're waiting for the rest of a fixed size header to be received or something like that.

      Specified by:
      read in interface BlinkProtocol
      Throws:
      IOException - throwing this will close the connection
    • getRevision

      public int getRevision()
      Return the client revision in the identity.
    • getByteOrder

      public ByteOrder getByteOrder()
      Return the byte order of the stream.
      Returns:
      the byte order of the stream
    • getMarker

      public byte getMarker()
      The marker to use in the header.
    • getDefaultTimeout

      public int getDefaultTimeout()
      Return the currently configured default timeout for receive operations.
    • setDefaultTimeout

      public void setDefaultTimeout(int timeout)
      Set the default timeout for receive operations.
      Parameters:
      timeout - the new default timeout 0 = system default
    • dispatch

      public void dispatch(BinaryMsg msg)
      Description copied from class: BlinkSession
      Dispatch a message to a handler. This will be called for every message placed in the dispatch queue by calling queue(). This will be called by a worker thread from the endpoint thread pool so it can block or be long running. Only one message is dispatched at a time. This means that if a dispatched message blocks, this method will not be called again until the blocked message finally returns. This ensures that messages are dispatched in the order they were received.
      Specified by:
      dispatch in class BlinkSession<BinaryMsg>
      Parameters:
      msg - the handler to run in the dispatch thread pool
    • getReadBuf

      public ByteBuffer getReadBuf()
      Description copied from interface: BlinkProtocol
      Return the read buffer to read channel data into.
      Specified by:
      getReadBuf in interface BlinkProtocol
    • getNodeId

      public NodeId getNodeId()
      Return the nodeId that the client is running on. This may be different from the current node when using clustering.
      Returns:
      the nodeId of the client connection
    • isPrimary

      public boolean isPrimary()
      Return true if the nodeId is the primary node.
    • isConnected

      public boolean isConnected()
      True if the client is still connected.
      Returns:
      true if connected
    • msg

      public BinaryMsg msg(int ifaceNum, int apiNum) throws IOException
      Return a new message instance that can be populated with data and sent to the client side of the session.
      Parameters:
      ifaceNum - the interface number
      apiNum - the api number
      Returns:
      the new message instance
      Throws:
      IOException
    • send

      public void send(BinaryMsg msg) throws IOException
      Send the specified message without expecting any response.
      Parameters:
      msg - the message to send
      Throws:
      IOException
    • sendAndRecv

      public BinaryMsg sendAndRecv(BinaryMsg msg, int timeout) throws IOException
      Send the specified message and wait for a response.
      Parameters:
      msg - the message to send
      timeout - timeout for receive (<= uses default)
      Returns:
      the response message
      Throws:
      TimeoutBinaryMsgException - if there is a timeout
      ErrorBinaryMsgException - if the response contains an error
      IOException - if there is a problem sending
    • sendAndRecv

      public void sendAndRecv(BinaryMsg msg, int timeout, BinaryMsgListener listener) throws IOException
      Send the specified message and send the response to the listener. Listener will be called with a null message if there is a timeout.
      Parameters:
      msg - the message to send
      timeout - timeout for receive (<=0 uses default)
      Throws:
      IOException - if there is a problem sending
    • getReady

      public ReadyIndicator getReady()
      Description copied from interface: Ready
      Fetches the associated ready indicator.
      Specified by:
      getReady in interface Ready
      Returns:
      the associated ReadyIndicator object