You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

88 line
3.0 KiB

  1. using System.Collections.Generic;
  2. using System.IO.Ports;
  3. namespace ThunderboltTimeSync {
  4. public class ThunderboltSerialPort {
  5. private static readonly byte CHAR_DLE = 0x10;
  6. private static readonly byte CHAR_ETX = 0x03;
  7. private List<byte> packetBuffer;
  8. private bool inPacket;
  9. private SerialPort serialPort;
  10. /// <summary>
  11. /// A delegate which is called when a full packet is received over the serial port.
  12. /// The byte buffer passed in contains the initial [DLE], and final [DLE][ETX]. The buffer is completely unparsed and remains stuffed.
  13. /// </summary>
  14. /// <param name="packetBuffer">A byte buffer which contains the packet.</param>
  15. public delegate void PacketReceivedEventHandler(List<byte> packetBuffer);
  16. /// <summary>
  17. /// An event which is called when a full packet is received over the serial port.
  18. /// Refer to <see cref="PacketReceivedEventHandler"/> for more information.
  19. /// </summary>
  20. public event PacketReceivedEventHandler PacketReceived;
  21. /// <summary>
  22. /// Creates an instance of the ThunderboltSerialPort class, which processes serial data from a Thunderbolt and
  23. /// The serial port passed into the function must not be opened, or have a delegate already attached to the DataReceived event.
  24. /// </summary>
  25. /// <param name="serialPort">The serial port on which to communicate with the Thunderbolt.</param>
  26. public ThunderboltSerialPort(SerialPort serialPort) {
  27. packetBuffer = new List<byte>();
  28. inPacket = false;
  29. this.serialPort = serialPort;
  30. serialPort.DataReceived += DataReceived;
  31. }
  32. /// <summary>
  33. /// Begins processing serial data.
  34. /// </summary>
  35. public void Open() {
  36. serialPort.Open();
  37. }
  38. private void DataReceived(object sender, SerialDataReceivedEventArgs e) {
  39. int possibleCurrentByte;
  40. while ((possibleCurrentByte = serialPort.ReadByte()) != -1) {
  41. // 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
  42. byte currentByte = (byte) possibleCurrentByte;
  43. if (inPacket) {
  44. packetBuffer.Add(currentByte);
  45. // Check buffer length to ensure we've reached a plausible end of packet.
  46. // 5 bytes is [DLE]<id><1 byte of data>[DLE][ETX]
  47. if (currentByte == CHAR_ETX && packetBuffer.Count >= 5) {
  48. int numberOfPrecedingDLEs = 0;
  49. // Count number of DLEs, excluding the first two bytes (initial DLE and id)
  50. for (int i = 2; i < packetBuffer.Count; ++i) {
  51. if (packetBuffer[i] == CHAR_DLE) {
  52. ++numberOfPrecedingDLEs;
  53. }
  54. }
  55. // Odd number of DLEs means the ETX does in fact signify the end of the packet
  56. if (numberOfPrecedingDLEs % 2 == 1) {
  57. PacketReceived?.Invoke(packetBuffer);
  58. packetBuffer.Clear();
  59. inPacket = false;
  60. }
  61. }
  62. } else {
  63. // A DLE received when not currently in a packet signifies the beginning of a packet
  64. if (currentByte == CHAR_DLE) {
  65. packetBuffer.Add(currentByte);
  66. inPacket = true;
  67. }
  68. }
  69. }
  70. }
  71. }
  72. }