Initial commit
This commit is contained in:
@@ -0,0 +1,215 @@
|
||||
using Sick.SafetyScanners.DataStructures;
|
||||
|
||||
namespace Sick.SafetyScanners.DataProcessing;
|
||||
|
||||
/// <summary>
|
||||
/// Merges TCP packets that may be fragmented across multiple TCP packets
|
||||
/// Thread-safe implementation - matches C++ logic
|
||||
/// </summary>
|
||||
public sealed class TcpPacketMerger : IDisposable
|
||||
{
|
||||
private readonly List<PacketBuffer> _packetBuffers;
|
||||
private uint _targetSize;
|
||||
private bool _isComplete;
|
||||
private PacketBuffer? _deployedBuffer;
|
||||
private bool _disposed;
|
||||
private readonly object _lock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new TCP packet merger
|
||||
/// </summary>
|
||||
public TcpPacketMerger(uint targetSize = 0)
|
||||
{
|
||||
_packetBuffers = new List<PacketBuffer>();
|
||||
_targetSize = targetSize;
|
||||
_isComplete = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the packet is complete
|
||||
/// </summary>
|
||||
public bool IsComplete
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _isComplete;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the buffer is empty
|
||||
/// </summary>
|
||||
public bool IsEmpty
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _packetBuffers.Count == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target size
|
||||
/// </summary>
|
||||
public uint TargetSize
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _targetSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the expected total packet length
|
||||
/// </summary>
|
||||
public void SetTargetSize(uint expectedLength)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_targetSize = expectedLength;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a TCP packet to the merger (stores reference, doesn't copy data yet)
|
||||
/// Returns true if the packet is complete
|
||||
/// </summary>
|
||||
public bool AddTcpPacket(PacketBuffer packet)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(nameof(TcpPacketMerger));
|
||||
|
||||
if (packet == null)
|
||||
throw new ArgumentNullException(nameof(packet));
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
// If already complete, reset for new packet
|
||||
if (_isComplete)
|
||||
{
|
||||
_isComplete = false;
|
||||
_deployedBuffer = null;
|
||||
}
|
||||
|
||||
// Calculate remaining size BEFORE adding packet (matches C++ logic)
|
||||
var currentSize = GetCurrentSize();
|
||||
var remainingSize = _targetSize - currentSize;
|
||||
|
||||
// Add packet reference (don't copy data yet)
|
||||
_packetBuffers.Add(packet);
|
||||
|
||||
// Check if complete (remaining size should equal packet length)
|
||||
if (remainingSize == packet.Length)
|
||||
{
|
||||
_isComplete = true;
|
||||
DeployPacketIfComplete();
|
||||
}
|
||||
|
||||
return _isComplete;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current total size of all packets
|
||||
/// </summary>
|
||||
private uint GetCurrentSize()
|
||||
{
|
||||
uint sum = 0;
|
||||
foreach (var packet in _packetBuffers)
|
||||
{
|
||||
sum += (uint)packet.Length;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deploys packet if complete (merges all packets into one buffer)
|
||||
/// </summary>
|
||||
private void DeployPacketIfComplete()
|
||||
{
|
||||
if (!_isComplete)
|
||||
return;
|
||||
|
||||
DeployPacket();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges all packets into a single buffer
|
||||
/// </summary>
|
||||
private void DeployPacket()
|
||||
{
|
||||
var totalSize = GetCurrentSize();
|
||||
var mergedBuffer = new List<byte>((int)totalSize);
|
||||
|
||||
// Pre-allocate capacity for better performance
|
||||
foreach (var packet in _packetBuffers)
|
||||
{
|
||||
var buffer = packet.GetBuffer();
|
||||
mergedBuffer.AddRange(buffer.Span);
|
||||
}
|
||||
|
||||
_deployedBuffer = new PacketBuffer(mergedBuffer.ToArray(), mergedBuffer.Count);
|
||||
_packetBuffers.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the merged packet buffer (only when complete)
|
||||
/// </summary>
|
||||
public PacketBuffer GetDeployedBuffer()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(nameof(TcpPacketMerger));
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (!_isComplete)
|
||||
throw new InvalidOperationException("Packet is not complete yet");
|
||||
|
||||
if (_deployedBuffer == null)
|
||||
throw new InvalidOperationException("Deployed buffer is null");
|
||||
|
||||
// Reset for next packet
|
||||
_isComplete = false;
|
||||
var result = _deployedBuffer;
|
||||
_deployedBuffer = null;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the merger for a new packet
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_packetBuffers.Clear();
|
||||
_targetSize = 0;
|
||||
_isComplete = false;
|
||||
_deployedBuffer = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
_packetBuffers.Clear();
|
||||
_deployedBuffer = null;
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user