using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using Tango.Core;
using Tango.Logging;
namespace Tango.Transport
{
/// <summary>
/// Represents an <see cref="ITransportAdapter"/> base class.
/// </summary>
/// <seealso cref="Tango.Transport.ITransportAdapter" />
public abstract class TransportAdapterBase : ExtendedObject, ITransportAdapter
{
protected long _totalBytes;
protected static long _component_counter = 1;
private long _transferRateTotalBytes;
private Timer _transferRateTimer;
protected const int MAX_BUFFER_SIZE = 1024; //10 MB.
#region Events
/// <summary>
/// Occurs when component state changes.
/// </summary>
public event EventHandler<TransportComponentState> StateChanged;
/// <summary>
/// Occurs when new data is available.
/// </summary>
public event EventHandler<byte[]> DataAvailable;
#endregion
#region Properties
/// <summary>
/// Gets or sets the name of the transport component.
/// </summary>
public String ComponentName { get; set; } = "Not Set";
private long _totalBytesReceived;
/// <summary>
/// Gets the total bytes received.
/// </summary>
public long TotalBytesReceived
{
get { return _totalBytesReceived; }
protected set { _totalBytesReceived = value; RaisePropertyChanged(nameof(TotalBytesReceived)); }
}
private long _totalBytesSent;
/// <summary>
/// Gets the total bytes sent.
/// </summary>
public long TotalBytesSent
{
get { return _totalBytesSent; }
protected set { _totalBytesSent = value; RaisePropertyChanged(nameof(TotalBytesSent)); }
}
private long _transferRate;
/// <summary>
/// Gets the adapter current transfer rate.
/// </summary>
public long TransferRate
{
get
{
return _transferRate;
}
protected set { _transferRate = value; RaisePropertyChanged(nameof(TransferRate)); }
}
private String _address;
/// <summary>
/// Gets or sets the channel address.
/// </summary>
public String Address
{
get { return _address; }
set { _address = value; RaisePropertyChangedAuto(); }
}
/// <summary>
/// Gets the last failed state exception/reason.
/// </summary>
public Exception FailedStateException { get; private set; }
private TransportComponentState _state;
/// <summary>
/// Gets the component state.
/// </summary>
public TransportComponentState State
{
get { return _state; }
protected set
{
if (_state != value)
{
_state = value;
OnStateChanged(_state);
}
}
}
private bool _enableCompression;
/// <summary>
/// Gets or sets a value indicating whether to enable compression/decompression of data.
/// </summary>
public bool EnableCompression
{
get { return _enableCompression; }
set { _enableCompression = value; RaisePropertyChangedAuto(); }
}
#endregion
#region Virtual Methods
/// <summary>
/// Called when the adapter has failed.
/// </summary>
/// <param name="ex">The ex.</param>
protected virtual void OnFailed(Exception ex)
{
FailedStateException = ex;
LogManager.Log(ex, $"{ComponentName}: Adapter failed.");
Disconnect().Wait();
State = TransportComponentState.Failed;
}
/// <summary>
/// Called when there is new data available.
/// </summary>
/// <param name="data">The data.</param>
protected virtual void OnDataAvailable(byte[] data)
{
TotalBytesReceived += data.Length;
_totalBytes += data.Length;
AppendTransferRateBytes(data.Length);
DataAvailable?.Invoke(this, data);
}
/// <summary>
/// Called when the adapter state has changed.
/// </summary>
/// <param name="state">The state.</param>
protected virtual void OnStateChanged(TransportComponentState state)
{
StateChanged?.Invoke(this, state);
if (state == TransportComponentState.Connected)
{
_totalBytes = 0;
TransferRate = 0;
if (_transferRateTimer != null)
{
_transferRateTimer.Stop();
_transferRateTimer.Dispose();
}
_transferRateTimer = new Timer(1000);
_transferRateTimer.Elapsed += _transferRateTimer_Elapsed;
_transferRateTimer.Start();
}
else
{
if (_transferRateTimer != null)
{
_transferRateTimer.Stop();
_transferRateTimer.Dispose();
}
}
}
/// <summary>
/// Throws an exception if adapter is in a failed or disposed state.
/// </summary>
protected virtual void ThrowIfDisposed()
{
if (State == TransportComponentState.Disposed)
{
throw LogManager.Log(new ObjectDisposedException($"{ComponentName}: The adapter is in a " + State + " state."));
}
}
/// <summary>
/// Applies any additional headers if required.
/// </summary>
/// <param name="data">The data.</param>
/// <returns></returns>
protected virtual byte[] PostProcessBuffer(byte[] data)
{
byte[] postData = data;
postData = BitConverter.GetBytes(data.Length).Concat(data).ToArray();
TotalBytesSent += postData.Length;
_totalBytes += postData.Length;
AppendTransferRateBytes(postData.Length);
return postData;
}
#endregion
#region Private Methods
protected void AppendTransferRateBytes(long dataLength)
{
_transferRateTotalBytes += dataLength;
}
#endregion
#region Dispose
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public virtual void Dispose()
{
Disconnect().Wait();
State = TransportComponentState.Disposed;
}
#endregion
#region Abstract Methods
/// <summary>
/// Writes the specified data to the stream.
/// </summary>
/// <param name="data">The data.</param>
/// <param name="immidiate">Writes the data as soon as possible while ignoring any message queuing and batching.</param>
public abstract void Write(byte[] data, bool immidiate = false);
/// <summary>
/// Connects the transport component.
/// </summary>
/// <returns></returns>
public abstract Task Connect();
/// <summary>
/// Disconnects the transport component.
/// </summary>
/// <returns></returns>
public abstract Task Disconnect();
#endregion
#region Override Methods
/// <summary>
/// Returns a <see cref="System.String" /> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String" /> that represents this instance.
/// </returns>
public override string ToString()
{
return this.GetType().Name;
}
#endregion
#region Calculate Transfer Rate
private void _transferRateTimer_Elapsed(object sender, ElapsedEventArgs e)
{
TransferRate = _transferRateTotalBytes;
_transferRateTotalBytes = 0;
}
#endregion
}
}