/// <summary> /// Generates binary image of the <see cref="CompactMeasurement"/> and copies it into the given buffer, for <see cref="ISupportBinaryImage.BinaryLength"/> bytes. /// </summary> /// <param name="buffer">Buffer used to hold generated binary image of the source object.</param> /// <param name="startIndex">0-based starting index in the <paramref name="buffer"/> to start writing.</param> /// <returns>The number of bytes written to the <paramref name="buffer"/>.</returns> /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="startIndex"/> or <see cref="ISupportBinaryImage.BinaryLength"/> is less than 0 -or- /// <paramref name="startIndex"/> and <see cref="ISupportBinaryImage.BinaryLength"/> will exceed <paramref name="buffer"/> length. /// </exception> /// <remarks> /// <para> /// Field: Bytes: <br/> /// -------- -------<br/> /// Flags 1 <br/> /// ID 2 <br/> /// Value 4 <br/> /// [Time] 2? <br/> /// </para> /// <para> /// Constant Length = 7<br/> /// Variable Length = 0, 2, 4 or 8 (i.e., total size is 7, 9, 11 or 15) /// </para> /// </remarks> public int GenerateBinaryImage(byte[] buffer, int startIndex) { // Call to binary length property caches result of m_usingBaseTimeOffset int length = BinaryLength; buffer.ValidateParameters(startIndex, length); // Encode compact state flags CompactMeasurementStateFlags flags = StateFlags.MapToCompactFlags(); if (m_timeIndex != 0) { flags |= CompactMeasurementStateFlags.TimeIndex; } if (m_usingBaseTimeOffset) { flags |= CompactMeasurementStateFlags.BaseTimeOffset; } // Added flags to beginning of buffer buffer[startIndex++] = (byte)flags; // Encode runtime ID EndianOrder.BigEndian.CopyBytes(m_signalIndexCache.GetSignalIndex(ID), buffer, startIndex); startIndex += 2; // Encode adjusted value (accounts for adder and multipler) EndianOrder.BigEndian.CopyBytes((float)AdjustedValue, buffer, startIndex); startIndex += 4; if (m_includeTime) { if (m_usingBaseTimeOffset) { if (m_useMillisecondResolution) { // Encode 2-byte millisecond offset timestamp EndianOrder.BigEndian.CopyBytes((ushort)(Timestamp - m_baseTimeOffsets[m_timeIndex]).ToMilliseconds(), buffer, startIndex); } else { // Encode 4-byte ticks offset timestamp EndianOrder.BigEndian.CopyBytes((uint)((long)Timestamp - m_baseTimeOffsets[m_timeIndex]), buffer, startIndex); } } else { // Encode 8-byte full fidelity timestamp EndianOrder.BigEndian.CopyBytes((long)Timestamp, buffer, startIndex); } } return(length); }
/// <summary> /// Maps <see cref="CompactMeasurementStateFlags"/> to <see cref="MeasurementStateFlags"/>. /// </summary> /// <param name="stateFlags">Flags to map.</param> /// <returns><see cref="MeasurementStateFlags"/> mapped from <see cref="CompactMeasurementStateFlags"/>.</returns> public static MeasurementStateFlags MapToFullFlags(this CompactMeasurementStateFlags stateFlags) { MeasurementStateFlags mappedStateFlags = MeasurementStateFlags.Normal; if ((stateFlags & CompactMeasurementStateFlags.DataRange) > 0) { mappedStateFlags |= DataRangeMask; } if ((stateFlags & CompactMeasurementStateFlags.DataQuality) > 0) { mappedStateFlags |= DataQualityMask; } if ((stateFlags & CompactMeasurementStateFlags.TimeQuality) > 0) { mappedStateFlags |= TimeQualityMask; } if ((stateFlags & CompactMeasurementStateFlags.SystemIssue) > 0) { mappedStateFlags |= SystemIssueMask; } if ((stateFlags & CompactMeasurementStateFlags.CalculatedValue) > 0) { mappedStateFlags |= CalculatedValueMask; } if ((stateFlags & CompactMeasurementStateFlags.DiscardedValue) > 0) { mappedStateFlags |= DiscardedValueMask; } return(mappedStateFlags); }
/// <summary> /// Maps <see cref="MeasurementStateFlags"/> to <see cref="CompactMeasurementStateFlags"/>. /// </summary> /// <param name="stateFlags">Flags to map.</param> /// <returns><see cref="CompactMeasurementStateFlags"/> mapped from <see cref="MeasurementStateFlags"/>.</returns> public static CompactMeasurementStateFlags MapToCompactFlags(this MeasurementStateFlags stateFlags) { CompactMeasurementStateFlags mappedStateFlags = CompactMeasurementStateFlags.NoFlags; if ((stateFlags & DataRangeMask) > 0) { mappedStateFlags |= CompactMeasurementStateFlags.DataRange; } if ((stateFlags & DataQualityMask) > 0) { mappedStateFlags |= CompactMeasurementStateFlags.DataQuality; } if ((stateFlags & TimeQualityMask) > 0) { mappedStateFlags |= CompactMeasurementStateFlags.TimeQuality; } if ((stateFlags & SystemIssueMask) > 0) { mappedStateFlags |= CompactMeasurementStateFlags.SystemIssue; } if ((stateFlags & CalculatedValueMask) > 0) { mappedStateFlags |= CompactMeasurementStateFlags.CalculatedValue; } if ((stateFlags & DiscardedValueMask) > 0) { mappedStateFlags |= CompactMeasurementStateFlags.DiscardedValue; } return(mappedStateFlags); }
/// <summary> /// Initializes <see cref="CompactMeasurement"/> from the specified binary image. /// </summary> /// <param name="buffer">Buffer containing binary image to parse.</param> /// <param name="startIndex">0-based starting index in the <paramref name="buffer"/> to start parsing.</param> /// <param name="length">Valid number of bytes within <paramref name="buffer"/> from <paramref name="startIndex"/>.</param> /// <returns>The number of bytes used for initialization in the <paramref name="buffer"/> (i.e., the number of bytes parsed).</returns> /// <exception cref="InvalidOperationException">Not enough buffer available to deserialize measurement.</exception> /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="startIndex"/> or <paramref name="length"/> is less than 0 -or- /// <paramref name="startIndex"/> and <paramref name="length"/> will exceed <paramref name="buffer"/> length. /// </exception> public int ParseBinaryImage(byte[] buffer, int startIndex, int length) { buffer.ValidateParameters(startIndex, length); if (length < 1) { throw new InvalidOperationException("Not enough buffer available to deserialize measurement."); } // Decode flags CompactMeasurementStateFlags flags = (CompactMeasurementStateFlags)buffer[startIndex]; StateFlags = flags.MapToFullFlags(); m_timeIndex = (flags & CompactMeasurementStateFlags.TimeIndex) > 0 ? 1 : 0; m_usingBaseTimeOffset = (flags & CompactMeasurementStateFlags.BaseTimeOffset) > 0; int index = startIndex + 1; // Decode runtime ID ushort id = EndianOrder.BigEndian.ToUInt16(buffer, index); index += 2; // Restore signal identification Tuple <Guid, string, uint> tuple; if (m_signalIndexCache.Reference.TryGetValue(id, out tuple)) { ID = tuple.Item1; Key = new MeasurementKey(tuple.Item1, tuple.Item3, tuple.Item2); } else { throw new InvalidOperationException("Failed to find associated signal identification for runtime ID " + id); } // Decode value Value = EndianOrder.BigEndian.ToSingle(buffer, index); index += 4; if (m_includeTime) { if (m_usingBaseTimeOffset) { long baseTimeOffset = m_baseTimeOffsets[m_timeIndex]; if (m_useMillisecondResolution) { // Decode 2-byte millisecond offset timestamp if (baseTimeOffset > 0) { Timestamp = baseTimeOffset + EndianOrder.BigEndian.ToUInt16(buffer, index) * Ticks.PerMillisecond; } index += 2; } else { // Decode 4-byte tick offset timestamp if (baseTimeOffset > 0) { Timestamp = baseTimeOffset + EndianOrder.BigEndian.ToUInt32(buffer, index); } index += 4; } } else { // Decode 8-byte full fidelity timestamp Timestamp = EndianOrder.BigEndian.ToInt64(buffer, index); index += 8; } } return(index - startIndex); }