private void ReceiveMain()
        {
            var ep = new IPEndPoint(IPAddress.Any, 0);

            bytesReceived = 0;
            int  lastID         = 0;
            long lastTimestamp  = 0;
            var  currentProfile = new ProfileFragments();

            // this callback will kill the socket when the
            // token was canceled, which is the only way to get out
            // of the blocking udpClient.Receive()
            for (; ;)
            {
                try
                {
                    token.ThrowIfCancellationRequested();

                    // next call blocks!
                    byte[] raw = receiveUdpClient.Receive(ref ep);

                    if (!isRunning)
                    {
                        continue;
                    }

                    var header = new PacketHeader(raw);
                    if (header.Magic == 0xFACD) // Data packet
                    {
                        lastReceivedPacketTime = timeBase.ElapsedMilliseconds;
                        bytesReceived         += raw.Length;

                        var p = new DataPacket(raw, timeBase.ElapsedMilliseconds);
                        goodPackets++;

                        int  newID        = p.Source;
                        long newTimestamp = p.Timestamp;

                        if (newID != lastID || newTimestamp != lastTimestamp)
                        {
                            if (currentProfile.Count > 0)
                            {
                                profileAssembler.AssembleProfiles(currentProfile);
                                ++IncompleteProfilesReceivedCount;
                                currentProfile.Clear();
                            }
                        }

                        currentProfile.Add(p);
                        if (currentProfile.Complete)
                        {
                            profileAssembler.AssembleProfiles(currentProfile);
                            ++CompleteProfilesReceivedCount;
                            currentProfile.Clear();
                        }

                        lastID        = newID;
                        lastTimestamp = newTimestamp;
                    }
                    else if (header.Magic == 0xFACE) // Non-data packets
                    {
                        switch (header.Type)
                        {
                        case ScanPacketType.Status:
                            var from = ep.Address;
                            try
                            {
                                scanHead.Status = new StatusPacket(raw, from).ScanHeadStatus;
                            }
                            catch (VersionCompatibilityException e)
                            {
                                IsVersionMismatched   = true;
                                VersionMismatchReason = e.Message;

                                // Versions are not compatible, try to send a disconnect before bailing
                                CreateIPEndPoint(from);
                                Disconnect();
                                break;
                            }

                            if (scanHead.SerialNumber == scanHead.Status.ScanHeadSerialNumber &&
                                scanHeadDataIpEndPoint == null)
                            {
                                CreateIPEndPoint(from);
                            }

                            if (scanHeadDataIpEndPoint != null)
                            {
                                lastReceivedPacketTime = timeBase.ElapsedMilliseconds;
                            }

                            break;

                        default:
                            // Unknown command
                            BadPacketsCount++;
                            break;
                        }
                    }
                    else
                    {
                        // Wrong signature for received packet: expected 0xFACE or 0xFACD
                        BadPacketsCount++;
                    }
                }
                catch (ObjectDisposedException)
                {
                    // Time to break out of receive loop
                    break;
                }
                catch (SocketException)
                {
                    // We get here if we call Close() on the UdpClient
                    // while it is in the Receive(0 call. Apparently
                    // the only way to abort a Receive call is to
                    // close the underlying Socket.
                    break;
                }
                catch (OperationCanceledException)
                {
                    // Time to break out of receive loop
                    break;
                }
                catch (Exception)
                {
                    BadPacketsCount++;
                }
            }
        }
        private void ReceiveMain()
        {
            IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0);

            bytesReceived = 0;
            // this callback will kill the socket when the
            // token was canceled, which is the only way to get out
            // of the blocking udpClient.Receive()
            var demuxerDict = new Dictionary <int, ProfileFragments>();

            for (;;)
            {
                try
                {
                    token.ThrowIfCancellationRequested();

                    // next call blocks!
                    var raw = receiveUdpClient.Receive(ref ep);

                    if (!isRunning)
                    {
                        continue; // we ignore everything
                    }

                    var from   = ep.Address;
                    var header = new PacketHeader(raw);

                    if (header.Magic == 0xFACD) // Data packet
                    {
                        lastReceivedPacketTime = timeBase.ElapsedMilliseconds;
                        goodPackets++;
                        bytesReceived += raw.Length;
                        var p = new DataPacket(raw, timeBase.ElapsedMilliseconds);
                        // handle the de-multiplexing
                        if (p.NumParts == 1) // one part-datagram
                        {
                            // hand straight over to workers
                            profileAssembler.AssembleProfiles(new ProfileFragments(p, timeBase.ElapsedMilliseconds));
                            continue;
                        }

                        // source is a composite of scan head, camera and laser, we use it to identify packets from the same head/camera/laser combo
                        var id = p.Source;
                        if (!demuxerDict.ContainsKey(id))
                        {
                            // first time we see a packet from this source
                            demuxerDict[id] = new ProfileFragments(p, timeBase.ElapsedMilliseconds);
                        }
                        else
                        {
                            if (demuxerDict[id].Timestamp == p.Timestamp)
                            {
                                // the timestamp on this packet is the same as is the dict, so it must belong to the same  profile
                                demuxerDict[id].Add(p);
                                if (demuxerDict[id].Complete)
                                {
                                    CompleteProfilesReceivedCount++;
                                    // hand it off to a processor thread, we're done with it.
                                    profileAssembler.AssembleProfiles(demuxerDict[id]);
                                    demuxerDict.Remove(id);
                                    // but also record the Id in a fixed size queue, so that stragglers and
                                    // duplicates don't create a mess
                                    // TODO: add a done queue
                                }
                            }
                            else
                            {
                                // the timestamp on the current packet is newer than on the existing
                                // set of packets in the dictionary, which means we either received out of order or
                                // a packet got lost
                                // If we got the next in sequence, we now consider the previous profile done
                                // hand it off to a processor thread, since we're done with it.
                                profileAssembler.AssembleProfiles(demuxerDict[id]);

                                demuxerDict.Remove(id);

                                evictedForNextSeq++;
                                demuxerDict[id] = new ProfileFragments(p, timeBase.ElapsedMilliseconds);
                            }

                            // here we would check for timeouts, but that is tricky since the Receive call blocks.
                            // We may need to de-couple receiving from assembling.
                        }
                    }
                    else if (header.Magic == 0xFACE) // Non-data packets
                    {
                        switch (header.Type)
                        {
                        case ScanPacketType.Status:
                            try
                            {
                                scanHead.Status = new StatusPacket(raw, from).ScanHeadStatus;
                            }
                            catch (VersionCompatibilityException e)
                            {
                                IsVersionMismatched   = true;
                                VersionMismatchReason = e.Message;

                                // Versions are not compatible, try to send a disconnect before bailing
                                CreateIPEndPoint(from);
                                Disconnect();
                                Stop();
                                break;
                            }

                            if (scanHead.SerialNumber == scanHead.Status.ScanHeadSerialNumber &&
                                scanHeadDataIpEndPoint == null)
                            {
                                CreateIPEndPoint(from);
                            }

                            if (scanHeadDataIpEndPoint != null)
                            {
                                lastReceivedPacketTime = timeBase.ElapsedMilliseconds;
                            }

                            break;

                        default:
                            // Unknown command
                            BadPacketsCount++;
                            break;
                        }
                    }
                    else
                    {
                        // Wrong signature for received packet: expected 0xFACE or 0xFACD
                        BadPacketsCount++;
                    }
                }
                catch (ObjectDisposedException)
                {
                    // Time to break out of receive loop
                    break;
                }
                catch (SocketException)
                {
                    // We get here if we call Close() on the UdpClient
                    // while it is in the Receive(0 call. Apparently
                    // the only way to abort a Receive call is to
                    // close the underlying Socket.
                    break;
                }
                catch (OperationCanceledException)
                {
                    // Time to break out of receive loop
                    break;
                }
#pragma warning disable CA1031 // Do not catch general exception types
                catch (Exception)
#pragma warning restore CA1031 // Do not catch general exception types
                {
                    BadPacketsCount++;
                }
            }
        }