//******************************************************************************************************
// FileBlockWriter.cs - Gbtc
//
// Copyright © 2013, Grid Protection Alliance. All Rights Reserved.
//
// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
// the NOTICE file distributed with this work for additional information regarding copyright ownership.
// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may
// not use this file except in compliance with the License. You may obtain a copy of the License at:
//
// http://www.opensource.org/licenses/MIT
//
// Unless agreed to in writing, the subject software distributed under the License is distributed on an
// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
// License for the specific language governing permissions and limitations.
//
// Code Modification History:
// ----------------------------------------------------------------------------------------------------
// 04/26/2013 - Stephen C. Wills
// Generated original version of source code.
//
//******************************************************************************************************
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Text;
using GSF;
using GSF.Diagnostics;
using GSF.IO;
using GSF.TimeSeries;
using GSF.TimeSeries.Adapters;
#if !MONO
using GSF.TimeSeries.UI.Editors;
#endif
namespace FileAdapters
{
///
/// Output adapter that receives blocks of data as measurements and writes them to a file.
///
[Description("FileBlockWriter: Receives buffer block measurements and writes them to files")]
public class FileBlockWriter : OutputAdapterBase
{
#region [ Members ]
// Fields
private string m_outputDirectory;
private FileStream m_activeFileStream;
private long m_activeFileSize;
private long m_bytesWritten;
private bool m_disposed;
#endregion
#region [ Properties ]
///
/// Gets or sets the directory to which files are written.
///
[ConnectionStringParameter, Description("Defines the directory to which files are written.")]
#if !MONO
[CustomConfigurationEditor(typeof(FolderBrowserEditor))]
#endif
public string OutputDirectory
{
get
{
return m_outputDirectory;
}
set
{
m_outputDirectory = value;
}
}
///
/// Gets the flag that determines if measurements sent to this are destined for archival.
///
public override bool OutputIsForArchive => false;
///
/// Gets flag that determines if the data output stream connects asynchronously.
///
protected override bool UseAsyncConnect => false;
#endregion
#region [ Methods ]
///
/// Initializes .
///
public override void Initialize()
{
const string errorMessage = @"{0} is missing from Settings - Example: outputDirectory=C:\Files";
Dictionary settings;
string setting;
base.Initialize();
settings = Settings;
// Required parameters
if (!settings.TryGetValue("outputDirectory", out setting))
throw new ArgumentException(string.Format(errorMessage, "outputDirectory"));
m_outputDirectory = FilePath.GetAbsolutePath(setting);
if (!Directory.Exists(m_outputDirectory))
Directory.CreateDirectory(m_outputDirectory);
}
///
/// Gets a short one-line status of this .
///
/// Maximum number of available characters for display.
/// A short one-line summary of the current status of this .
public override string GetShortStatus(int maxLength)
{
if ((object)m_activeFileStream != null)
return $"Currently writing to file {Path.GetFileName(m_activeFileStream.Name)}".CenterText(maxLength);
return $"{FilePath.GetFileList(Path.Combine(m_outputDirectory, "*")).Length} files written by {Name}".CenterText(maxLength);
}
///
/// Attempts to connect to data output stream.
///
protected override void AttemptConnection()
{
// Nothing to connect to until we start receiving file data
}
///
/// Attempts to disconnect from data output stream.
///
protected override void AttemptDisconnection()
{
if ((object)m_activeFileStream != null)
{
m_activeFileStream.Dispose();
m_activeFileStream = null;
}
}
///
/// Writes buffer blocks out to files.
///
protected override void ProcessMeasurements(IMeasurement[] measurements)
{
BufferBlockMeasurement bufferBlockMeasurement;
foreach (IMeasurement measurement in measurements)
{
bufferBlockMeasurement = measurement as BufferBlockMeasurement;
// Only buffer block measurements can be processed
if ((object)bufferBlockMeasurement != null)
ProcessBufferBlockMeasurement(bufferBlockMeasurement);
}
}
private void ProcessBufferBlockMeasurement(BufferBlockMeasurement measurement)
{
byte[] bufferBlock = measurement.Buffer;
int index = 1;
if (bufferBlock[0] != 0)
{
// Start of a new file - read file info
int fileNameByteLength = BigEndian.ToInt32(bufferBlock, 1);
string fileName = Encoding.Unicode.GetString(bufferBlock, 5, fileNameByteLength);
long fileSize = BigEndian.ToInt64(bufferBlock, 5 + fileNameByteLength);
// Notify of new file creation
OnStatusMessage(MessageLevel.Info, "Now writing to file {0}...", fileName);
// ReSharper disable once UnusedVariable > Justification: Implementation pattern closes any existing stream
// Create new file
using (FileStream activeFileStream = m_activeFileStream)
m_activeFileStream = File.Create(Path.Combine(m_outputDirectory, fileName));
m_activeFileStream.SetLength(fileSize);
m_activeFileSize = fileSize;
m_bytesWritten = 0L;
// Advance buffer pointer to file data
index = 1 + 4 + fileNameByteLength + 8;
}
if ((object)m_activeFileStream != null)
{
// Write data into the file
int bytesOfData = measurement.Length - index;
m_activeFileStream.Write(bufferBlock, index, bytesOfData);
m_bytesWritten += bytesOfData;
// Close the file when we detect that
// we've written all bytes to the file
if (m_bytesWritten >= m_activeFileSize)
{
OnStatusMessage(MessageLevel.Info, "Finished writing to file {0}.", Path.GetFileName(m_activeFileStream.Name));
m_activeFileStream.Dispose();
m_activeFileStream = null;
m_bytesWritten = 0L;
}
}
}
///
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
///
/// true to release both managed and unmanaged resources; false to release only unmanaged resources.
protected override void Dispose(bool disposing)
{
if (!m_disposed)
{
try
{
if (disposing)
{
if ((object)m_activeFileStream != null)
{
m_activeFileStream.Dispose();
m_activeFileStream = null;
}
}
}
finally
{
m_disposed = true; // Prevent duplicate dispose.
base.Dispose(disposing); // Call base class Dispose().
}
}
}
#endregion
}
}