/// <summary> /// Tests if the <paramref name="dataBlockPointer"/> matches the specified search criteria. /// </summary> /// <param name="dataBlockPointer"><see cref="ArchiveDataBlockPointer"/> to test.</param> /// <param name="historianID">Desired historian ID.</param> /// <param name="startTime">Desired start time.</param> /// <param name="endTime">Desired end time.</param> /// <returns><c>true</c> if the specified <paramref name="dataBlockPointer"/> is for <paramref name="historianID"/> and falls within the <paramref name="startTime"/> and <paramref name="endTime"/>; otherwise <c>false</c>.</returns> public static bool Matches(this ArchiveDataBlockPointer dataBlockPointer, int historianID, TimeTag startTime, TimeTag endTime) { // Note: The StartTime value of the pointer is ignored if m_searchStartTime = TimeTag.MinValue and // m_searchEndTime = TimeTag.MaxValue. In this case only the PointID value is compared. This // comes in handy when the first or last pointer is to be found from the list of pointers for // a point ID in addition to all the pointers for a point ID. if ((object)dataBlockPointer != null) { return(dataBlockPointer.HistorianID == historianID && (startTime.CompareTo(TimeTag.MinValue) == 0 || dataBlockPointer.StartTime.CompareTo(startTime) >= 0) && (endTime.CompareTo(TimeTag.MaxValue) == 0 || dataBlockPointer.StartTime.CompareTo(endTime) <= 0)); } return(false); }
/// <summary> /// Returns all <see cref="ArchiveDataBlock"/>s in the <see cref="ArchiveFile"/> for the specified <paramref name="historianID"/> with <see cref="ArchiveDataPoint"/>s between the specified <paramref name="startTime"/> and <paramref name="endTime"/>. /// </summary> /// <param name="historianID">Historian identifier.</param> /// <param name="startTime">Start <see cref="TimeTag"/>.</param> /// <param name="endTime">End <see cref="TimeTag"/>.</param> /// <param name="preRead">true to pre-read data to locate write cursor.</param> /// <returns>A collection of <see cref="ArchiveDataBlock"/>s.</returns> public List <ArchiveDataBlock> FindDataBlocks(int historianID, TimeTag startTime, TimeTag endTime, bool preRead = true) { if ((object)startTime == null) { startTime = TimeTag.MaxValue; } if ((object)endTime == null) { endTime = TimeTag.MaxValue; } List <ArchiveDataBlockPointer> blockPointers; lock (m_dataBlockPointers) { // Get all block pointers for given point ID over specified time range blockPointers = m_dataBlockPointers.FindAll(dataBlockPointer => dataBlockPointer.Matches(historianID, startTime, endTime)); // Look for pointer to data block on the borders of the specified range which may contain data if (!(startTime == TimeTag.MinValue || endTime == TimeTag.MaxValue)) { // There are 2 different search criteria for this: // 1) If matching data block pointers have been found, then find data block pointer before the first matching data block pointer. // or // 2) Find the last data block pointer in the time range of TimeTag.MinValue to m_searchEndTime. TimeTag searchEndTime = endTime; if (blockPointers.Count > 0) { searchEndTime = new TimeTag(blockPointers.First().StartTime.Value - 1.0D); } ArchiveDataBlockPointer borderMatch = m_dataBlockPointers.LastOrDefault(dataBlockPointer => dataBlockPointer.Matches(historianID, TimeTag.MinValue, searchEndTime)); if ((object)borderMatch != null) { blockPointers.Insert(0, borderMatch); } } } // Return list of data blocks for given block pointers return(blockPointers.Select(pointer => pointer.GetDataBlock(preRead)).ToList()); }
/// <summary> /// Initializes <see cref="VariableTableRegion"/> by parsing the specified <paramref name="buffer"/> containing a 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="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); ArchiveDataBlockPointer blockPointer; // Set parsing cursor beyond old-style visual basic array descriptor int index = startIndex + ArrayDescriptorLength; for (int i = 0; i < m_parent.m_dataBlockCount; i++) { blockPointer = new ArchiveDataBlockPointer(m_parent.m_parent, i); index += blockPointer.ParseBinaryImage(buffer, index, length - (index - startIndex)); m_parent.m_dataBlockPointers.Add(blockPointer); } return(index - startIndex); }
/// <summary> /// Compares the current <see cref="ArchiveDataBlockPointer"/> object to <paramref name="obj"/>. /// </summary> /// <param name="obj">Object against which the current <see cref="ArchiveDataBlockPointer"/> object is to be compared.</param> /// <returns> /// Negative value if the current <see cref="ArchiveDataBlockPointer"/> object is less than <paramref name="obj"/>, /// Zero if the current <see cref="ArchiveDataBlockPointer"/> object is equal to <paramref name="obj"/>, /// Positive value if the current <see cref="ArchiveDataBlockPointer"/> object is greater than <paramref name="obj"/>. /// </returns> public virtual int CompareTo(object obj) { ArchiveDataBlockPointer other = obj as ArchiveDataBlockPointer; if ((object)other == null) { return(1); } int result = m_historianID.CompareTo(other.HistorianID); if (result != 0) { return(result); } return(m_startTime.CompareTo(other.StartTime)); }
/// <summary> /// Returns an <see cref="ArchiveDataBlock"/> for writing <see cref="ArchiveDataPoint"/>s for the specified <paramref name="historianID"/>. /// </summary> /// <param name="historianID">Historian identifier for which the <see cref="ArchiveDataBlock"/> is being requested.</param> /// <param name="dataTime"><see cref="TimeTag"/> of the <see cref="ArchiveDataPoint"/> to be written to the <see cref="ArchiveDataBlock"/>.</param> /// <param name="blockIndex"><see cref="ArchiveDataBlock.Index"/> of the <see cref="ArchiveDataBlock"/> last used for writing <see cref="ArchiveDataPoint"/>s for the <paramref name="historianID"/>.</param> /// <returns><see cref="ArchiveDataBlock"/> object if available; otherwise null if all <see cref="ArchiveDataBlock"/>s have been allocated.</returns> internal ArchiveDataBlock RequestDataBlock(int historianID, TimeTag dataTime, int blockIndex) { ArchiveDataBlock dataBlock = null; ArchiveDataBlockPointer dataBlockPointer = null; if (blockIndex >= 0 && blockIndex < m_dataBlockCount) { // Valid data block index is specified, so retrieve the corresponding data block. lock (m_dataBlockPointers) { dataBlockPointer = m_dataBlockPointers[blockIndex]; } dataBlock = dataBlockPointer.DataBlock; if (!dataBlockPointer.IsAllocated && dataBlock.SlotsUsed > 0) { // Clear existing data from the data block since it is unallocated. dataBlock.Reset(); } else if (dataBlockPointer.IsAllocated && (dataBlockPointer.HistorianID != historianID || (dataBlockPointer.HistorianID == historianID && dataBlock.SlotsAvailable == 0))) { // Search for a new data block since the suggested data block cannot be used. blockIndex = -1; } } if (blockIndex < 0) { // Negative data block index is specified indicating a search must be performed for a data block. dataBlock = FindLastDataBlock(historianID); if ((object)dataBlock != null && dataBlock.SlotsAvailable == 0) { // Previously used data block is full. dataBlock = null; } if ((object)dataBlock == null) { // Look for the first unallocated data block. dataBlock = FindDataBlock(-1); if ((object)dataBlock == null) { // Extend the file for historic writes only. if (m_parent.FileType == ArchiveFileType.Historic) { Extend(); dataBlock = m_dataBlockPointers[m_dataBlockPointers.Count - 1].DataBlock; } } else { // Reset the unallocated data block if there is data in it. if (dataBlock.SlotsUsed > 0) { dataBlock.Reset(); } } } // Get the pointer to the data block so that its information can be updated if necessary. if ((object)dataBlock == null) { dataBlockPointer = null; } else { lock (m_dataBlockPointers) { dataBlockPointer = m_dataBlockPointers[dataBlock.Index]; } } } if ((object)dataBlockPointer != null && !dataBlockPointer.IsAllocated) { // Mark the data block as allocated. dataBlockPointer.HistorianID = historianID; dataBlockPointer.StartTime = dataTime; // Set the file start time if not set. if (m_fileStartTime == TimeTag.MinValue) { m_fileStartTime = dataTime; } // Persist data block information to disk. lock (m_parent.FileData) { // We'll write information about the just allocated data block to the file. m_parent.FileData.Seek(DataLength + ArrayDescriptorLength + (dataBlockPointer.Index * ArchiveDataBlockPointer.FixedLength), SeekOrigin.Begin); dataBlockPointer.CopyBinaryImageToStream(m_parent.FileData); // We'll also write the fixed part of the FAT data that resides at the end. m_parent.FileData.Seek(-m_fixedTableRegion.BinaryLength, SeekOrigin.End); // Copy generated binary image to stream m_fixedTableRegion.CopyBinaryImageToStream(m_parent.FileData); if (!m_parent.CacheWrites) { m_parent.FileData.Flush(); } } // Re-fetch the data block with updated information after allocation. dataBlock = dataBlockPointer.DataBlock; } return(dataBlock); }
/// <summary> /// Initializes <see cref="VariableTableRegion"/> by parsing the specified <paramref name="buffer"/> containing a 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="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); ArchiveDataBlockPointer blockPointer; // Set parsing cursor beyond old-style visual basic array descriptor int index = startIndex + ArrayDescriptorLength; for (int i = 0; i < m_parent.m_dataBlockCount; i++) { blockPointer = new ArchiveDataBlockPointer(m_parent.m_parent, i); index += blockPointer.ParseBinaryImage(buffer, index, length - (index - startIndex)); m_parent.m_dataBlockPointers.Add(blockPointer); } return (index - startIndex); }