216 lines
5.5 KiB
C#
216 lines
5.5 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
|