|
|
@@ -0,0 +1,88 @@ |
|
|
|
using System.Collections.Generic; |
|
|
|
using System.IO.Ports; |
|
|
|
|
|
|
|
namespace ThunderboltTimeSync { |
|
|
|
public class ThunderboltSerialPort { |
|
|
|
private static readonly byte CHAR_DLE = 0x10; |
|
|
|
private static readonly byte CHAR_ETX = 0x03; |
|
|
|
|
|
|
|
private List<byte> packetBuffer; |
|
|
|
private bool inPacket; |
|
|
|
|
|
|
|
private SerialPort serialPort; |
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// A delegate which is called when a full packet is received over the serial port. |
|
|
|
/// The byte buffer passed in contains the initial [DLE], and final [DLE][ETX]. The buffer is completely unparsed and remains stuffed. |
|
|
|
/// </summary> |
|
|
|
/// <param name="packetBuffer">A byte buffer which contains the packet.</param> |
|
|
|
public delegate void PacketReceivedEventHandler(List<byte> packetBuffer); |
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// An event which is called when a full packet is received over the serial port. |
|
|
|
/// Refer to <see cref="PacketReceivedEventHandler"/> for more information. |
|
|
|
/// </summary> |
|
|
|
public event PacketReceivedEventHandler PacketReceived; |
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// Creates an instance of the ThunderboltSerialPort class, which processes serial data from a Thunderbolt and |
|
|
|
/// The serial port passed into the function must not be opened, or have a delegate already attached to the DataReceived event. |
|
|
|
/// </summary> |
|
|
|
/// <param name="serialPort">The serial port on which to communicate with the Thunderbolt.</param> |
|
|
|
public ThunderboltSerialPort(SerialPort serialPort) { |
|
|
|
packetBuffer = new List<byte>(); |
|
|
|
inPacket = false; |
|
|
|
|
|
|
|
this.serialPort = serialPort; |
|
|
|
serialPort.DataReceived += DataReceived; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary> |
|
|
|
/// Begins processing serial data. |
|
|
|
/// </summary> |
|
|
|
public void Open() { |
|
|
|
serialPort.Open(); |
|
|
|
} |
|
|
|
|
|
|
|
private void DataReceived(object sender, SerialDataReceivedEventArgs e) { |
|
|
|
int possibleCurrentByte; |
|
|
|
|
|
|
|
while ((possibleCurrentByte = serialPort.ReadByte()) != -1) { |
|
|
|
// Once we're sure the byte that was read wasn't -1 (which signifies the end of the read), we're safe to cast to a byte |
|
|
|
byte currentByte = (byte) possibleCurrentByte; |
|
|
|
|
|
|
|
if (inPacket) { |
|
|
|
packetBuffer.Add(currentByte); |
|
|
|
|
|
|
|
// Check buffer length to ensure we've reached a plausible end of packet. |
|
|
|
// 5 bytes is [DLE]<id><1 byte of data>[DLE][ETX] |
|
|
|
if (currentByte == CHAR_ETX && packetBuffer.Count >= 5) { |
|
|
|
int numberOfPrecedingDLEs = 0; |
|
|
|
|
|
|
|
// Count number of DLEs, excluding the first two bytes (initial DLE and id) |
|
|
|
for (int i = 2; i < packetBuffer.Count; ++i) { |
|
|
|
if (packetBuffer[i] == CHAR_DLE) { |
|
|
|
++numberOfPrecedingDLEs; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Odd number of DLEs means the ETX does in fact signify the end of the packet |
|
|
|
if (numberOfPrecedingDLEs % 2 == 1) { |
|
|
|
PacketReceived?.Invoke(packetBuffer); |
|
|
|
|
|
|
|
packetBuffer.Clear(); |
|
|
|
inPacket = false; |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
// A DLE received when not currently in a packet signifies the beginning of a packet |
|
|
|
if (currentByte == CHAR_DLE) { |
|
|
|
packetBuffer.Add(currentByte); |
|
|
|
|
|
|
|
inPacket = true; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |