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 } } }