using System; using System.Collections.Generic; using System.IO.Ports; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace KFDtool.Adapter.Protocol.Serial { public class SerialProtocol { private static NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger(); const byte SOM_EOM = 0x61; const byte SOM_EOM_PLACEHOLDER = 0x62; const byte ESC = 0x63; const byte ESC_PLACEHOLDER = 0x64; private static AutoResetEvent CancelRead = new AutoResetEvent(false); private List FrameBuffer; private bool FoundStart; private List> PacketBuffer; private static ManualResetEvent PacketReady = new ManualResetEvent(false); private SerialPort Port; public SerialProtocol(string portName) { FrameBuffer = new List(); FoundStart = false; PacketBuffer = new List>(); Port = new SerialPort(); Port.PortName = portName; Port.BaudRate = 115200; Port.Parity = Parity.None; Port.DataBits = 8; Port.StopBits = StopBits.One; Port.DataReceived += new SerialDataReceivedEventHandler(OnDataReceived); } public void Open() { Port.Open(); } public void Close() { Port.Close(); } public void Clear() { FrameBuffer.Clear(); PacketBuffer.Clear(); PacketReady.Reset(); FoundStart = false; while (Port.BytesToRead > 0) { Port.ReadByte(); } } public void Send(List data) { List frameData = new List(); frameData.Add(SOM_EOM); foreach (byte b in data) { if (b == ESC) { frameData.Add(ESC); frameData.Add(ESC_PLACEHOLDER); } else if (b == SOM_EOM) { frameData.Add(ESC); frameData.Add(SOM_EOM_PLACEHOLDER); } else { frameData.Add(b); } } frameData.Add(SOM_EOM); byte[] outData = frameData.ToArray(); Logger.Debug("out len: {0}", outData.Length); Logger.Debug("out data: {0}", BitConverter.ToString(outData)); Port.Write(outData, 0, outData.Length); } private byte[] ReadPacketFromPacketBuffer() { Logger.Trace("packet buffer before length: {0}", PacketBuffer.Count); if (PacketBuffer.Count == 0) { throw new Exception("no packet in packet buffer"); } byte[] packet = PacketBuffer[0].ToArray(); Logger.Trace("packet contents: {0}", BitConverter.ToString(packet)); PacketBuffer.RemoveAt(0); Logger.Trace("packet buffer after length: {0}", PacketBuffer.Count); return packet; } public List Read(int timeout) { // if there are no packets in the buffer, wait until there is one if (PacketBuffer.Count == 0) { if (timeout > 0) { if (!PacketReady.WaitOne(timeout)) { throw new Exception("timeout waiting for data"); } } else if (timeout == 0) { WaitHandle[] handles = new WaitHandle[] { PacketReady, CancelRead }; if (WaitHandle.WaitAny(handles) == 1) { throw new OperationCanceledException("read was canceled"); } } else { throw new ArgumentOutOfRangeException("timeout can not be negative"); } } List data = new List(); byte[] packet = ReadPacketFromPacketBuffer(); data.AddRange(packet); PacketReady.Reset(); return data; } public void Cancel() { CancelRead.Set(); } private void OnDataReceived(object sender, SerialDataReceivedEventArgs e) { SerialPort sp = sender as SerialPort; int toRead = sp.BytesToRead; Logger.Debug("in len: {0}", toRead); if (toRead == 0) { Logger.Warn("no data to read"); return; } byte[] inData = new byte[toRead]; sp.Read(inData, 0, inData.Length); Logger.Debug("in data: {0}", BitConverter.ToString(inData)); foreach (byte b in inData) { Logger.Trace("new byte: 0x{0:X2}", b); if (b == SOM_EOM) { FoundStart = true; if (FrameBuffer.Count > 0) { for (int i = 0; i < FrameBuffer.Count; i++) { if (FrameBuffer[i] == ESC) { FrameBuffer.RemoveAt(i); if (i == FrameBuffer.Count) { throw new Exception("escape character at end"); } if (FrameBuffer[i] == ESC_PLACEHOLDER) { FrameBuffer[i] = ESC; } else if (FrameBuffer[i] == SOM_EOM_PLACEHOLDER) { FrameBuffer[i] = SOM_EOM; } else { throw new Exception("invalid character after escape character"); } } } List packet = new List(); packet.AddRange(FrameBuffer); PacketBuffer.Add(packet); Logger.Debug("packet contents: {0}", BitConverter.ToString(packet.ToArray())); FrameBuffer.Clear(); Logger.Debug("packet buffer length: {0}", PacketBuffer.Count); } } else { if (FoundStart) { Logger.Trace("added 0x{0:X2}", b); FrameBuffer.Add(b); } } } if (PacketBuffer.Count > 0) { PacketReady.Set(); } } } }