using Sick.SafetyScanners.DataStructures;
namespace Sick.SafetyScanners.DataProcessing;
///
/// Main parser that coordinates parsing of all data blocks from UDP packets
/// Thread-safe implementation
///
public sealed class ParseData
{
private readonly ParseDataHeader _headerParser;
private readonly ParseDerivedValues _derivedValuesParser;
private readonly ParseMeasurementData _measurementDataParser;
private readonly ParseGeneralSystemState _generalSystemStateParser;
private readonly ParseIntrusionData _intrusionDataParser;
private readonly ParseApplicationData _applicationDataParser;
///
/// Creates a new ParseData instance
///
public ParseData()
{
_headerParser = new ParseDataHeader();
_derivedValuesParser = new ParseDerivedValues();
_measurementDataParser = new ParseMeasurementData();
_generalSystemStateParser = new ParseGeneralSystemState();
_intrusionDataParser = new ParseIntrusionData();
_applicationDataParser = new ParseApplicationData();
}
///
/// Parses the complete UDP sequence into UdpScanData
///
public UdpScanData ParseUdpSequence(PacketBuffer buffer)
{
if (buffer == null)
throw new ArgumentNullException(nameof(buffer));
// Parse header first (required for all other parsers)
var header = _headerParser.ParseUdpSequence(buffer);
// Validate packet size before parsing other blocks
ValidatePacketSize(buffer, header);
// Parse all data blocks in order
// 1. DerivedValues (needed for MeasurementData and IntrusionData)
var derivedValues = _derivedValuesParser.ParseUdpSequence(buffer, header);
// 2. MeasurementData (needs DerivedValues for angle calculation)
var measurementData = _measurementDataParser.ParseUdpSequence(buffer, header, derivedValues);
// 3. GeneralSystemState (independent)
var generalSystemState = _generalSystemStateParser.ParseUdpSequence(buffer, header);
// 4. IntrusionData (needs DerivedValues for number of scan points)
var intrusionData = _intrusionDataParser.ParseUdpSequence(buffer, header, derivedValues);
// 5. ApplicationData (independent)
var applicationData = _applicationDataParser.ParseUdpSequence(buffer, header);
return new UdpScanData
{
Header = header,
DerivedValues = derivedValues.IsEmpty ? null : derivedValues,
MeasurementData = measurementData.IsEmpty ? null : measurementData,
GeneralSystemState = generalSystemState.IsEmpty ? null : generalSystemState,
IntrusionData = intrusionData.IsEmpty ? null : intrusionData,
ApplicationData = applicationData.IsEmpty ? null : applicationData
};
}
///
/// Parses the complete TCP sequence (from COLA2 command response) into UdpScanData
/// Note: TCP and UDP use the same data structure format, only the transport differs
///
public UdpScanData ParseTcpSequence(PacketBuffer buffer)
{
// TCP sequence uses the same format as UDP for the data payload
return ParseUdpSequence(buffer);
}
///
/// Validates that the packet buffer contains enough data for all enabled blocks
///
private static void ValidatePacketSize(PacketBuffer buffer, DataHeader header)
{
if (header.IsEmpty)
return;
// Calculate expected minimum size
var expectedSize = (uint)(
header.DerivedValuesBlockSize +
header.MeasurementDataBlockSize +
header.GeneralSystemStateBlockSize +
header.IntrusionDataBlockSize +
header.ApplicationDataBlockSize);
var actualSize = (uint)buffer.GetBuffer().Length;
if (actualSize < expectedSize)
{
// Log warning would go here in production
// For now, we'll let individual parsers handle missing data gracefully
// by checking block offsets and sizes
}
}
}