The Prophecy framework provides the following interchangeable classes which implement the ICommunication interface:
  • Prophecy.Communication.TcpCommunication
  • Prophecy.Communication.UdpCommunication
  • Prophecy.Communication.SerialCommunication
  • Prophecy.Communication.UsbHidCommunication

Because the classes implement the same interface they can be seamlessly swapped which is helpful when a hardware/software manufacturer provides multiple communication port types.

Quick Start Initialization Examples

Delimited String Protocol
The following example demonstrates opening a TCP connection using connection monitoring for an ASCII carriage return delimited protocol.

ICommunication _comm;
// ...
_comm = new TcpCommunication(_hostName, _port);
_comm.Logger = new Prophecy.Logger.TraceLogger(Prophecy.Logger.LoggerVerbosity.Normal);
_comm.Delimiter = "\r"; // change the delimiter since it defaults to "\r\n".
_comm.CurrentEncoding = System.Text.ASCIIEncoding.ASCII;  // this is unnecessary since it defaults to ASCII
_comm.ReceivedDelimitedString += new EventHandler<ReceivedDelimitedStringEventArgs>(_comm_ReceivedDelimitedString);

// Set up connection monitor.
_comm.ConnectionMonitorTimeout = 60000;
_comm.ConnectionMonitorTestRequest = "VER\r";
_comm.ConnectionEstablished += new EventHandler<EventArgs>(_comm_ConnectionEstablished);
_comm.ConnectionLost += new EventHandler<EventArgs>(_comm_ConnectionLost);
_comm.StartConnectionMonitor();


Binary Protocol
The following example demonstrates opening a Serial connection using connection monitoring for a binary protocol which does not use an end of message delimiter.

ICommunication _comm;
// ...
_comm = new SerialCommunication(_serialPortName, 19200, System.IO.Ports.Parity.None, 8, System.IO.Ports.StopBits.One);
_comm.Logger = new Prophecy.Logger.TraceLogger(Prophecy.Logger.LoggerVerbosity.Normal);
_comm.Delimiter = null; // set to null to disable the ReceivedDelimitedString
_comm.CurrentEncoding = null; // do not decode strings
_comm.ReceivedBytes += new EventHandler<ReceivedBytesEventArgs>(_serial_DataReceived);
_comm.ReadBufferEnabled = true; // If the ReadBufferEnabled property is true, you may process the ReadBuffer property in the ReceivedBytes event.
                                // This is optional... you may choose to create your own receive buffer if you like.

// Set up connection monitor.
_comm.ConnectionMonitorTimeout = 60000;
_comm.ConnectionMonitorTestRequest = new byte[] { 0x00, 0x00, 0x00 };
_comm.ConnectionEstablished += new EventHandler<EventArgs>(_serial_ConnectionEstablished);
_comm.ConnectionLost += new EventHandler<EventArgs>(_serial_ConnectionLost);
_comm.ReadBufferOverflow += _serial_ReadBufferOverflow;
_comm.StartConnectionMonitor();

Connection Monitoring

Connection monitoring detects when a connection is lost and automatically attempts to reconnect. If no data has been received within a specified amount of time the connection monitor can send a query to the host which should cause a response. If there is no response then the connection is assumed to have been lost. This works for serial, usb and tcp connections. TCP connection drops are also detected. The UdpCommunication class does not support connection monitoring since UDP is a connection-less protocol.

To enable connection monitoring:

1. Set the ConnectionMonitorTimeout property to the number of milliseconds of no data received before the test request/heartbeat is sent to request a response. If there is no test request then you must set this to int.MaxValue.

2. Optionally assign a query command to be sent as a query to request data from the host, or use the ConnectionMonitorTest event for more complicated queries. The query command should contain a complete command including any header and footer data.
  • ConnectionMonitorTestRequest: Assign this for ASCII, UTF-8, or other non-binary based protocols.
  • ConnectionMonitorTestBytes: Assign this for binary protocols.
  • ConnectionMonitorTest : For more request/heartbeat control you can subscribe to this event instead of setting ConnectionMonitorTestRequest or ConnectionMonitorTestBytes.

3. Optionally subscribe to the ConnectionEstablished event. This event should contain any initialization commands or requests needed to retrieve the initial state from the host.

4. Optionally subscribe to the ConnectionLost event. This event will occur when the framework determines that the connection has been lost either due to no data being received or by a tcp connection drop.

5. Optionally subscribe to the ConnectionAttemptFailed event. This event occurs when a connection was attempted but failed.

6. Call StartConnectionMonitor() or StartConnectionMonitorSync() to start monitoring. StartConnectionMonitor() is an asynchronous call and will attempt to open the connection on the background thread, while StartConnectionMonitorSync() will block the thread for only the 1st connection attempt.

If not using connection monitoring, you may call Open to open the connection. You may also subscribe to the ConnectionAttemptFailed event to detect when and why a connection attempt failed.

Text Encoding/Decoding

The CurrentEncoding property should be set according to the type of protocol used. This property is used to decode incoming bytes and encode outgoing string data to bytes. The default is System.Text.Encoding.ASCII which is what most hardware devices use.

Completely Text Based Protocols
The default uses ASCII but in some cases you may be to chose UTF-8, etc.

Binary Protocols
You must set CurrentEncoding property to null to avoid triggering the text based receivied data events (ReceivedDelimitedString and ReceivedString).

Sending Data

There are several methods provided to queue text and binary data to be sent and you optionally can specify a minimum amount of time to wait to send the data after the prior send has occurred.

Queue a byte array to be sent:

ICommunication _comm = new TcpCommunication(_hostName, _port);
// ...
byte[] b = new byte[]{ 0, 1, 2, 3 };
_comm.Send(b); // send the data as soon as all previously queued items have been sent.
_comm.Send(b, 1000); // send the data 1 second after the previously queued item has been sent.


Queue a string to be sent:

_comm.Send("test\r"); // send the data as soon as all previously queued items have been sent.
_comm.Send("test\r", 1000); // send the data 1 second after the previously queued item has been sent.


The queueing system provides a way to throttle outgoing data by optionally specifying the amount of time that must pass between the previously sent data and the next data.

Receiving Data

There are 3 events which can be used to process incoming data. You would usually only use one of them based on the type of protocol.

ReceivedDelimitedString
This event occurs when using a text based protocol (such as ASCII) which uses a consistent end of line marker and a full message has been received. A delimited string is detected by using the value in the Delimiter property. If the Delimiter property is null this event will not occur. The EventArgs contain the delimited string. The IncludeDelimiterInRawResponse property indicates if the delimited string will include the delimiter.

ReceivedString
This event occurs when using a text based protocol (such as ASCII) and incoming data has been decoded to a string based on the Encoding property. The received string will be in the EventArgs. This event is rarely used since most protocols use a delimited string or are binary.

ReceivedBytes
This event occurs whenever bytes are received. This is useful for binary protocols (non-ASCII/UTF8, etc) or when there is no incoming message delimiter. The received bytes will be in the EventArgs. If the ReadBufferEnabled property is true, you may also process the ReadBuffer property.

Simulating Received Data

The following methods in the communication classes can be used to simulated incoming data. This is helpful when testing.

Simulating incoming string data:

ICommunication _comm = new TcpCommunication(_hostName, _port);
// ...
_comm.SimulateReceivedData("abcd\r");


Simulating incoming byte data:

ICommunication _comm = new TcpCommunication(_hostName, _port);
// ...
byte[] b = new byte[]{ 0, 1, 2, 3 };
_comm.SimulateReceivedData(b);

Internal Buffer

An internal buffer can be used to cache incoming data until enough data has been received to process. Although each of the received data events contains the data that was just received, the data is not guaranteed to contain a complete message except for the case of the ReceivedDelimitedString which always contains a full message.

The buffer is not enabled by default and must be enabled to use. To enable the buffer, set the ReadBufferEnabled property to true. Data will be added to the buffer whenever it is received and it is the developer's responsibility to process the the buffer data to prevent it from overflowing. The ReadBufferOverflow occurs when the ReadBufferEnabled property is true and the buffer size limit has been exceeded. The overflowed data will be lost.

The buffer can be accessed by the ReadBuffer property which returns a BufferReader object. The BufferReader object is used to read data from the buffer and is generally accessed from within the ReceivedBytes or ReceivedString events. In these events you would usually check the buffer length, use the IndexOf() method to search for delimiters, or use the indexer to peek at the data to determine if an entire message has been received. If there is enough data for a complete message you would process the message and loop until there are no more complete messages.

BufferReader Members:

indexer[]
Gets the byte at the specified index in the buffer. The byte will not be removed from the buffer.

Length
Gets the number of bytes in the buffer.

Clear
Clear the buffer.

IndexOf()
Reports the index of the position of the first occurrence of the specified byte sequence or string in the buffer.

ReadByte
Read and remove the first bytes in the buffer.

ReadBytes()
Read and remove the specified number of bytes in the buffer.

ReadString()
Read and remove the specified number of characters in the buffer.

ReadChars()
Read and remove the specified number of characters in the buffer.

Monitoring Data Transmission

The CommunicationStarted and CommunicationEnd events indicate mark when data is being sent or received. The CommunicationStarted event indicates when transmission has begun. The CommunicationEnd event occurs when transmission has ended but will always be at least 1/2 second after CommunicationStarted. This prevents rapid firing of the event.

The TotalSentByteCount and TotalReceivedByteCount properties can be used to keep track of data quantities. The ResetByteCountTotals() method resets the totals to zero.

Disposing

When done using a communications class, the class should be disposed using one of the following methods.

Dispose()
Waits for the send queue to send all pending data and then releases all resources used by the object.

Dispose(int timeoutMilliseconds)
Waits a maximum of the specified number of milliseconds for the send queue to send all pending data and releases all resources used by the object.

Logging

The framework using an ILogger interface for logging and includes a StubLogger and TraceLogger classes for logging. By default the communication classes use the StubLogger which does not log anything. The Logger property may be set to an instance of a different ILogger at any time. The TraceLogger is an example logging class which logs to System.Diagnostics.Trace.

Last edited Sep 18, 2011 at 6:38 AM by JohnHughes, version 22

Comments

No comments yet.