namespace TLVProtocal.SubLayers; internal class TlvAckLayer(bool ConvertToBigEnd, int timeoutMillionSeconds) : BaseFilter { private const byte STX = 0x02;//Send private const byte ENQ = 0x05;//Request private const byte ACK = 0x06;//Response ACK private readonly object _idLock = new(); private byte _messageID = 0; private readonly int _timeoutMillionSeconds = timeoutMillionSeconds; private readonly ConcurrentDictionary _ackReceivedEvent = []; private byte GetNextID() { lock (_idLock) _messageID = _messageID == byte.MaxValue ? (byte)0 : ++_messageID; return _messageID; } public override bool Send(Data data) { if (data is not TlvData tlv) return false; byte messageID = GetNextID(); AutoResetEvent resetEvent = new(false); for (; !this._ackReceivedEvent.TryAdd(messageID, resetEvent); messageID = GetNextID()) ; List sendBytes = []; switch (tlv.RequestID.HasValue) { case true: sendBytes.Add(ENQ); byte[] requestID = BitConverter.GetBytes(tlv.RequestID.Value); if (ConvertToBigEnd) Array.Reverse(requestID); sendBytes.AddRange(requestID); break; case false: sendBytes.Add(STX); break; } sendBytes.Add(this._messageID); sendBytes.AddRange(data.RawData); Data sendData = new([.. sendBytes], Connection, data.DateTime); try { if (!base.Send(sendData)) return false; return resetEvent.WaitOne(this._timeoutMillionSeconds); } finally { this._ackReceivedEvent.TryRemove(messageID, out _); } } public override bool Receive(Data data) { byte[] content; switch (data.RawData[0]) { case ENQ: this.AckReply(data); content = new byte[data.RawData.Length - 4]; ushort requestID; if (ConvertToBigEnd) { byte[] requestIDBytes = new byte[2]; Array.Copy(data.RawData, 1, requestIDBytes, 0, 2); Array.Reverse(requestIDBytes); requestID = BitConverter.ToUInt16(requestIDBytes, 0); } else requestID = BitConverter.ToUInt16(data.RawData, 0); Array.Copy(data.RawData, 4, content, 0, content.Length); TlvData tlvData = new(0, content, data.Connection, data.DateTime) { RequestID = requestID }; return base.Receive(tlvData); case STX: this.AckReply(data); content = new byte[data.RawData.Length - 2]; Array.Copy(data.RawData, 2, content, 0, content.Length); return base.Receive(new TlvData(0, content, data.Connection, data.DateTime)); case ACK: if (!this._ackReceivedEvent.TryGetValue(data.RawData[1], out AutoResetEvent resetEvent) || resetEvent is null) return false; resetEvent.Set(); return true; default: return false; } } private bool AckReply(Data data) { Data ackReply = new([ACK, data.RawData[1]], Connection, DateTime.Now); return base.Send(ackReply); } public override void Connected(Connection connection) { this.Connection = connection; base.Connected(connection); } private Connection Connection; }