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 packetBuffer; private bool inPacket; private SerialPort serialPort; /// /// 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. /// /// A byte buffer which contains the packet. public delegate void PacketReceivedEventHandler(List packetBuffer); /// /// An event which is called when a full packet is received over the serial port. /// Refer to for more information. /// public event PacketReceivedEventHandler PacketReceived; /// /// 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. /// /// The serial port on which to communicate with the Thunderbolt. public ThunderboltSerialPort(SerialPort serialPort) { packetBuffer = new List(); inPacket = false; this.serialPort = serialPort; serialPort.DataReceived += DataReceived; } /// /// Begins processing serial data. /// 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]<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; } } } } } }