/// <summary> /// Queues a collection of measurements for processing. /// </summary> /// <param name="measurements">Collection of measurements to queue for processing.</param> /// <remarks> /// Measurements are filtered against the defined <see cref="InputMeasurementKeys"/> so we override method /// so that dynamic updates to keys will be synchronized with filtering to prevent interference. /// </remarks> public override void QueueMeasurementsForProcessing(IEnumerable <IMeasurement> measurements) { if ((object)measurements == null) { return; } if (!m_startTimeSent && measurements.Any()) { m_startTimeSent = true; IMeasurement measurement = measurements.FirstOrDefault(m => m != null); Ticks timestamp = 0; if ((object)measurement != null) { timestamp = measurement.Timestamp; } m_parent.SendDataStartTime(m_clientID, timestamp); } if (m_isNaNFiltered) { measurements = measurements.Where(measurement => !double.IsNaN(measurement.Value)); } // Order measurements by signal type for better compression for non-TSSC compression modes if (m_usePayloadCompression && !m_compressionModes.HasFlag(CompressionModes.TSSC)) { base.QueueMeasurementsForProcessing(measurements.OrderBy(m => m.GetSignalType(DataSource))); } else { base.QueueMeasurementsForProcessing(measurements); } }
private void ProcessMeasurements(IEnumerable <IMeasurement> measurements) { if (m_usePayloadCompression && m_compressionModes.HasFlag(CompressionModes.TSSC)) { ProcessTSSCMeasurements(measurements); return; } // Includes data packet flags and measurement count const int PacketHeaderSize = DataPublisher.ClientResponseHeaderSize + 5; List <IBinaryMeasurement> packet; int packetSize; bool usePayloadCompression; bool useCompactMeasurementFormat; BufferBlockMeasurement bufferBlockMeasurement; byte[] bufferBlock; ushort bufferBlockSignalIndex; IBinaryMeasurement binaryMeasurement; int binaryLength; try { if (!Enabled) { return; } packet = new List <IBinaryMeasurement>(); packetSize = PacketHeaderSize; usePayloadCompression = m_usePayloadCompression; useCompactMeasurementFormat = m_useCompactMeasurementFormat || usePayloadCompression; foreach (IMeasurement measurement in measurements) { bufferBlockMeasurement = measurement as BufferBlockMeasurement; if ((object)bufferBlockMeasurement != null) { // Still sending buffer block measurements to client; we are expecting // confirmations which will indicate whether retransmission is necessary, // so we will restart the retransmission timer m_bufferBlockRetransmissionTimer.Stop(); // Handle buffer block measurements as a special case - this can be any kind of data, // measurement subscriber will need to know how to interpret buffer bufferBlock = new byte[6 + bufferBlockMeasurement.Length]; // Prepend sequence number BigEndian.CopyBytes(m_bufferBlockSequenceNumber, bufferBlock, 0); m_bufferBlockSequenceNumber++; // Copy signal index into buffer bufferBlockSignalIndex = m_signalIndexCache.GetSignalIndex(bufferBlockMeasurement.Key); BigEndian.CopyBytes(bufferBlockSignalIndex, bufferBlock, 4); // Append measurement data and send Buffer.BlockCopy(bufferBlockMeasurement.Buffer, 0, bufferBlock, 6, bufferBlockMeasurement.Length); m_parent.SendClientResponse(m_clientID, ServerResponse.BufferBlock, ServerCommand.Subscribe, bufferBlock); lock (m_bufferBlockCacheLock) { // Cache buffer block for retransmission m_bufferBlockCache.Add(bufferBlock); } // Start the retransmission timer in case we never receive a confirmation m_bufferBlockRetransmissionTimer.Start(); } else { // Serialize the current measurement. if (useCompactMeasurementFormat) { binaryMeasurement = new CompactMeasurement(measurement, m_signalIndexCache, m_includeTime, m_baseTimeOffsets, m_timeIndex, m_useMillisecondResolution); } else { binaryMeasurement = new SerializableMeasurement(measurement, m_parent.GetClientEncoding(m_clientID)); } // Determine the size of the measurement in bytes. binaryLength = binaryMeasurement.BinaryLength; // If the current measurement will not fit in the packet based on the max // packet size, process the current packet and start a new packet. if (packetSize + binaryLength > DataPublisher.MaxPacketSize) { ProcessBinaryMeasurements(packet, useCompactMeasurementFormat, usePayloadCompression); packet.Clear(); packetSize = PacketHeaderSize; } // Add the current measurement to the packet. packet.Add(binaryMeasurement); packetSize += binaryLength; } } // Process the remaining measurements. if (packet.Count > 0) { ProcessBinaryMeasurements(packet, useCompactMeasurementFormat, usePayloadCompression); } IncrementProcessedMeasurements(measurements.Count()); // Update latency statistics m_parent.UpdateLatencyStatistics(measurements.Select(m => (long)(m_lastPublishTime - m.Timestamp))); } catch (Exception ex) { string message = $"Error processing measurements: {ex.Message}"; OnProcessException(MessageLevel.Info, new InvalidOperationException(message, ex)); } }
/// <summary> /// Queues a collection of measurements for processing. /// </summary> /// <param name="measurements">Collection of measurements to queue for processing.</param> /// <remarks> /// Measurements are filtered against the defined <see cref="InputMeasurementKeys"/> so we override method /// so that dynamic updates to keys will be synchronized with filtering to prevent interference. /// </remarks> public override void QueueMeasurementsForProcessing(IEnumerable <IMeasurement> measurements) { if ((object)measurements == null) { return; } if (!m_startTimeSent && measurements.Any()) { m_startTimeSent = true; IMeasurement measurement = measurements.FirstOrDefault(m => (object)m != null); Ticks timestamp = 0; if ((object)measurement != null) { timestamp = measurement.Timestamp; } m_parent.SendDataStartTime(m_clientID, timestamp); } if (m_isNaNFiltered) { measurements = measurements.Where(measurement => !double.IsNaN(measurement.Value)); } if (!measurements.Any() || !Enabled) { return; } if (TrackLatestMeasurements) { double publishInterval; // Keep track of latest measurements base.QueueMeasurementsForProcessing(measurements); publishInterval = (m_publishInterval > 0) ? m_publishInterval : LagTime; if (DateTime.UtcNow.Ticks > m_lastPublishTime + Ticks.FromSeconds(publishInterval)) { List <IMeasurement> currentMeasurements = new List <IMeasurement>(); Measurement newMeasurement; // Create a new set of measurements that represent the latest known values setting value to NaN if it is old foreach (TemporalMeasurement measurement in LatestMeasurements) { newMeasurement = new Measurement { Key = measurement.Key, Value = measurement.GetValue(RealTime), Adder = measurement.Adder, Multiplier = measurement.Multiplier, Timestamp = measurement.Timestamp, StateFlags = measurement.StateFlags }; currentMeasurements.Add(newMeasurement); } // Order measurements by signal type for better compression when enabled if (m_usePayloadCompression) { ProcessMeasurements(currentMeasurements.OrderBy(measurement => measurement.GetSignalType(DataSource))); } else { ProcessMeasurements(currentMeasurements); } } } else { // Order measurements by signal type for better compression for non-TSSC compression modes if (m_usePayloadCompression && !m_compressionModes.HasFlag(CompressionModes.TSSC)) { ProcessMeasurements(measurements.OrderBy(measurement => measurement.GetSignalType(DataSource))); } else { ProcessMeasurements(measurements); } } }