Skip to content

Communication Management System

Tom Tzook edited this page Sep 13, 2017 · 8 revisions

The Communication Management System offers a complex network control to allow seperation of data that passes through a single port. In essence, it allows managing of data transfer between 2 sides with ease. In addition, it allows users to send and receive data in any way possible, be it TCP sockets, UDP, UART and more.

Examples for the communications system can be found here: Examples->communications.

The Communication Process

The Communication Management System manages communication between two different sides. Each side has an equal number of Sendable objects, any of which sends and receives data individually:

Communication system

When a Sendable is added to a communications manager, it is assigned an ID. When connection is established with a remote side, data about each Sendable is sent. If a Sendable with the same ID exists on both sides the 2 objects begin communication, if not an attempt is done to create a Sendable for communication according to the data of the remote Sendable.

Creation of Sendable objects by the manager is done using a SendableCreator object. SendableCreators receive data about a Sendable, specifically a byte indicating a type and a name, and create an object to communicate with that Sendable. Creation of objects by type has to be implemented by users.

In order to allow flexibility with the system, the actual sending and receiving of data is seperated from the management system and is done using a CommInterface. CommInterfaces serve as communication sockets. They are responsible for sending data to the other side, and receiving data from it, its implementation does not affect the operation of the manager. A CommInterface can use TCP or UDP sockets, UART ports or any other possible way to transfer data.

Sendable

The Sendable class provides abstract logic for a node in the communications system. Each Sendable object conducts communications with a remote counterpart, and data between them is organized by the manager and transfered accordingly, so there is no need to worry about unwanted data.

A Sendable object has an ID which is assigned to it by the communications manager. This ID is used to identify the object and its counterpart, making sure data is transfered currectly. So for two Sendable objects to communicate, they must have the same ID.

In addition to an ID, each Sendable object has a "type": a byte which can be used by users to dynamically create Sendable objects. When connection is established by the manager, there is an attempt to locate the remote counterpart Sendable with a matching ID. If it doesn't exist, it is possible to create it on the spot using a SendableCreator object. The type byte can be used by users to identify which Sendable object to create as the counterpart, this means each Sendable class should have a unique type.

A "name" is also available for each Sendable, it is does not play an important rule, but rather it allows users to display data about their object easily, since the name is sent to the remote counterpart.

There are several methods that require implementation when extending the Sendable class:

  • newData: this method is for receiving data from the remote counterpart. It receives a byte array with the data.
  • dataForTransmition: this method is for sending data to the remote counterpart. It returns an array of bytes or null.
  • hasChanged: this method indicates whether or not the object has data to send and so it returns a boolean. If true is returned, dataForTransmition will be called.
  • onConnection: called by the manager when connection with a remote counterpart Sendable was confirmed. If the communications manager has connected to a remote manager, but no counterpart Sendable is available, onConnection will not be called.
  • onDisconnection: called by the manager when connection with a remote counterpart Sendable was lost. This does not necessarilly means the communications was all lost, but rather it could also mean the remote Sendable object was removed by the user. If no counterpart Sendable was detected in the first placed, this will not be called.

SendableCreator

Since ID allocation can be problematic. It is a good idea to wait for a remote Sendable detection and only then create a counterpart for it. For this reason, we provided the SendableCreator interface.

When a connection between 2 managers is established, data about Sendable objects from each side is transfered. If a remote Sendable does not have a local one with the same ID, it is possible to create one locally. If a SendableCreator object exists in the manager, the method create from it will be called and both the name and the type of the remote Sendable will be passed, then by checking the value of the type, users can decide which object to create and return it. If the method returns null, nothing will happen, if it returns an object an ID matching the remote will be allocated to it and connection between it and the remote Sendable will be confirmed.

However, Sendable objects created on the run, will no be saved after the communications manager has disconnected or lost connection with the remote communications manager.

CommInterface

To allow flexibility for users, the actual transfer of data between communication managers is abstracted in the form of CommInterface. This interface is responsible for sending data from one side to the other and is free to do so at will. Users can implement a simple UDP, create UDP with data recovery, even write and read from files (not recommended though).

The interface itself has a lot of methods to be implemented and has to offer a few capabilities, such as read timeouts.

When used by the communications manager, the following steps are taken:

  • setReadTimeout is called, setting a timeout for data reading
  • While isOpened returns false, open is called to open the communications port every ~500ms
  • While isConnected returns false, the method connect is called to try connection to the remote side every ~500ms
  • When isConnected returns true connection is confirmed and data transfer starts
  • A loop starts and runs while isConnected returns true
  • write and read are called periodically to send and read data
  • The method update is also called periodically, allowing CommInterfaces to perform extra actions such as connection verification
  • When isConnected returns false, the manager loop stops, disconnect is called and again connection is attempted
  • When the manager is closed, the method close is called

For reading data the Packet class is used. It is a simple class holding a byte array and an int variable. This class should hold the reading result in the byte array and the amount of bytes read in the int variable. If no bytes were read, the length should be 0. It is necessary to manually provide the array, since the array in the class will be uninitialized.

To help users in implementing CommInterfaces, FlashLib provies few extensions to CommInterface with different implementations:

ManualConnectionVerifier

An abstract class which provides verification of connection by manually sending and reading handshakes.

It uses the update method to periodically check the time that has passed since data was sent or received. If a defined amount of time has passed since sending data, it will send an handshake to the remote (call write and pass the handshake byte array). If a defined amount of time has passed since receiving data, it will register a timeout. If a defined amount of timeouts has occurred, disconnect is called. Timeouts reset themselves over time.

This class only implements update from CommInterface.

If your protocol does not provide a connection handshake such as the one provided by TCP, you may use the handshake provided by this class. The server should call the static handshakeServer and the client should call handshakeClient. Those method will use a 3 way handshake between two side to confirm connection and will return a boolean result indicating if the connection was successful.

When extending this, remember to follow the following rules:

  • Each time connection is established, call 'resetData' to ready this class for use
  • When receiving data, call newDataRead updating it that new data from the remote was received
  • When sending data, call newDataSent updating it that new data to the remote was sent
  • When analyzing received data, check if that data is the handshake array by calling isHandshake. If it is, just do nothing.
  • If you want to use the update method too, be sure to call its update method too or it will not function properly:
super.update(millis);

StreamCommInterface

An abstract class for communication protocols using a data stream, such as a serial port. This class provides packets for data making sure data does not mix with each other. It also features a data corruption detection using CRC32, which is completely optional.

This class extends ManualConnectionVerifier and implements all but open, close, isOpened, connect, disconnect, isConnected, setReadTimeout and getReadTimeout from CommInterface. It also has 2 abstract method which require user implementation:

  • writeData: receives a byte array, a start index and a length and should write this data to the used communications stream.
  • readData: receives a byte array, reads data from the stream into it and returns the amount of bytes read. If non were read, return 0, if an error has occurred which indicates disconnection from the stream, return -1.

If enabled, the corruption detection is done by attaching a checksum to each data packet sent. When received, the checksum is extracted and compared to a locally created checksum of the same data. If they do not match, the data is considered corruption and is ignored.

SerialCommInterface

An extenstion of StreamCommInterface, this class provides implementation for connect, disconnect and isConnected. The implementation uses a manual connection handshake to perform connection.

Implementations

In addition to provided abstract logic for CommInterface, there are several full implementations:

  • TcpCommInterface: uses the TCP protocol for communications. Extends StreamCommInterface and can act as both server and client.
  • UdpCommInterface: uses the UDP protocol for communications. Extends 'ManualConnectionVerifier` and can act as both server and client.
  • RxTxCommInterface: uses the RXTXComm library and implements a serial communications interface. Extends SerialCommInterface.

Communications

The Communications class is the communications manager used in FlashLib. It performs the communication in a thread and can hold a collection of Sendable objects and one SendableCreator.

This class receives a CommInterface in its constructor which is used for the communications transfer. If this manager should be a server, meaning it should wait for a client to connect to it, then the CommInterface must be a server. Keep in mind that the manager was create to communicate with one remote manager and not several.

When connection with a remote manager is established, the preperation of Sendables is done. This is when ID are allocated and it is done sequentially - by the order of attachment to the manager. The allocation starts with a minimum ID which can be defined by users (setMinAllocationID(id)). After a disconnection/connection loss, the ID are reset for its Sendable so that they can be reallocated in the next connection, meaning that a Sendable does not keep an ID forever. In addition, if a Sendable was detached from the manager, this will mean that after another connection the ID previously allocated to it will be allocated to a different object.

If wanted, it is possible to force the allocation of an ID for a Sendable by using attachForID. If the manager is not connected and no other Sendable was forced with this ID, then no problem. But if the manager is connected, then the ID might already be allocated by the manager and this cannot be done.

When planning communication with this system, there are a few possible designs:

  • One side will attach Sendables and the other will create them with a SendableCreator.
  • Both sides attach Sendable objects with a matching order so that ID will be allocated accordingly.
  • Both sides attach Sendable objects but each side has a different minimum ID. Both sides will use SendableCreator to create counterparts.

The second design is not recommanded since it is prone to possible user errors.