//******************************************************************************************************
// RemoteInputAdapter.cs - Gbtc
//
// Copyright © 2012, 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:
// ----------------------------------------------------------------------------------------------------
// 06/01/2006 - J. Ritchie Carroll
// Generated original version of source code.
// 09/15/2009 - Stephen C. Wills
// Added new header and license agreement.
// 09/17/2009 - Pinal C. Patel
// Re-wrote the adapter to utilize existing historian components.
// 11/18/2009 - Pinal C. Patel
// Removed the need for HistorianID in Settings by using adapter Name instead.
// 03/05/2010 - Pinal C. Patel
// Added status updates for when the parser is having trouble parsing data.
// 12/13/2012 - Starlynn Danyelle Gilliam
// Modified Header.
//
//******************************************************************************************************
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using GSF;
using GSF.Communication;
using GSF.Diagnostics;
using GSF.Historian;
using GSF.TimeSeries;
using GSF.TimeSeries.Adapters;
namespace HistorianAdapters
{
///
/// Represents an input adapters that listens for time-series data from a remote Historian.
///
[Description("Remote v1.0 openHistorian Listener: Listens for time-series data from a remote 1.0 openHistorian")]
public class RemoteInputAdapter : InputAdapterBase
{
#region [ Members ]
// Fields
private readonly DataListener m_historianDataListener;
private bool m_disposed;
#endregion
#region [ Constructors ]
///
/// Initializes a new instance of the class.
///
public RemoteInputAdapter()
{
m_historianDataListener = new DataListener();
}
#endregion
#region [ Properties ]
///
/// Gets or sets the host name or IP address of the openHistorian.
///
[ConnectionStringParameter,
Description("Define the host name or IP address of the openHistorian.")]
public string Server
{
get
{
return m_historianDataListener.Server;
}
set
{
m_historianDataListener.Server = value;
}
}
///
/// Gets or sets the port on which the openHistorian is broadcasting data.
///
[ConnectionStringParameter,
Description("Define the port on which the openHistorian is broadcasting data.")]
public int Port
{
get
{
return m_historianDataListener.Port;
}
set
{
m_historianDataListener.Port = value;
}
}
///
/// Gets or sets the type of connection used to connect to the openHistorian.
///
[ConnectionStringParameter,
Description("Define the type of connection used to connect to the openHistorian.")]
public TransportProtocol Protocol
{
get
{
return m_historianDataListener.Protocol;
}
set
{
m_historianDataListener.Protocol = value;
}
}
///
/// Gets or sets a value that indicates whether the listener will initiate the connection to the openHistorian.
///
[ConnectionStringParameter,
Description("Indicates whether the listener will initiate the connection to the openHistorian.")]
public bool InitiateConnection
{
get
{
return m_historianDataListener.ConnectToServer;
}
set
{
m_historianDataListener.ConnectToServer = value;
}
}
///
/// Returns the detailed status of the data input source.
///
public override string Status
{
get
{
StringBuilder status = new StringBuilder();
status.Append(base.Status);
status.AppendLine();
status.Append(m_historianDataListener.Status);
return status.ToString();
}
}
///
/// Gets flag that determines if this uses an asynchronous connection.
///
protected override bool UseAsyncConnect => true;
///
/// Gets the flag indicating if this adapter supports temporal processing.
///
public override bool SupportsTemporalProcessing => false;
#endregion
#region [ Methods ]
///
/// Initializes this .
///
/// HistorianID, Server, Port, Protocol, or InitiateConnection is missing from the .
public override void Initialize()
{
base.Initialize();
string server;
string port;
string protocol;
string outbound;
string message = "{0} is missing from Settings - Example: protocol=UDP;server=192.168.1.15;port=2004;initiateConnection=True";
Dictionary settings = Settings;
// Validate settings.
if (!settings.TryGetValue("server", out server))
throw new ArgumentException(string.Format(message, "server"));
if (!settings.TryGetValue("port", out port))
throw new ArgumentException(string.Format(message, "port"));
if (!settings.TryGetValue("protocol", out protocol))
throw new ArgumentException(string.Format(message, "protocol"));
if (!settings.TryGetValue("initiateconnection", out outbound))
throw new ArgumentException(string.Format(message, "initiateConnection"));
m_historianDataListener.ID = Name;
m_historianDataListener.InitializeData = false;
m_historianDataListener.CacheData = false;
m_historianDataListener.Server = server;
m_historianDataListener.Port = int.Parse(port);
m_historianDataListener.Protocol = (TransportProtocol)Enum.Parse(typeof(TransportProtocol), protocol, true);
m_historianDataListener.ConnectToServer = outbound.ParseBoolean();
m_historianDataListener.DataExtracted += HistorianDataListener_DataExtracted;
m_historianDataListener.SocketConnecting += HistorianDataListener_SocketConnecting;
m_historianDataListener.SocketConnected += HistorianDataListener_SocketConnected;
m_historianDataListener.SocketDisconnected += HistorianDataListener_SocketDisconnected;
m_historianDataListener.Parser.OutputTypeNotFound += HistorianDataListener_OutputTypeNotFound;
m_historianDataListener.Parser.DataDiscarded += HistorianDataListener_DataDiscarded;
m_historianDataListener.Parser.ParsingException += HistorianDataListener_ParsingException;
m_historianDataListener.Initialize();
}
///
/// Gets a short one-line status of this .
///
/// Maximum length of the status message.
/// Text of the status message.
public override string GetShortStatus(int maxLength)
{
return $"Received {m_historianDataListener.TotalBytesReceived} bytes in {m_historianDataListener.TotalPacketsReceived} packets.".CenterText(maxLength);
}
///
/// Releases the unmanaged resources used by this 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
{
// This will be done regardless of whether the object is finalized or disposed.
if (disposing)
{
// This will be done only when the object is disposed by calling Dispose().
if (m_historianDataListener != null)
{
m_historianDataListener.DataExtracted -= HistorianDataListener_DataExtracted;
m_historianDataListener.SocketConnecting -= HistorianDataListener_SocketConnecting;
m_historianDataListener.SocketConnected -= HistorianDataListener_SocketConnected;
m_historianDataListener.SocketDisconnected -= HistorianDataListener_SocketDisconnected;
m_historianDataListener.Parser.OutputTypeNotFound -= HistorianDataListener_OutputTypeNotFound;
m_historianDataListener.Parser.DataDiscarded -= HistorianDataListener_DataDiscarded;
m_historianDataListener.Parser.ParsingException -= HistorianDataListener_ParsingException;
m_historianDataListener.Dispose();
}
}
}
finally
{
m_disposed = true; // Prevent duplicate dispose.
base.Dispose(disposing); // Call base class Dispose().
}
}
}
///
/// Attempts to connect to this .
///
protected override void AttemptConnection()
{
m_historianDataListener.StartAsync();
}
///
/// Attempts to disconnect from this .
///
protected override void AttemptDisconnection()
{
m_historianDataListener.Stop();
}
private void HistorianDataListener_DataExtracted(object sender, EventArgs> e)
{
try
{
List measurements = new List();
foreach (IDataPoint dataPoint in e.Argument)
{
measurements.Add(new Measurement
{
Metadata = MeasurementKey.LookUpOrCreate(m_historianDataListener.ID, (uint)dataPoint.HistorianID).Metadata,
Value = dataPoint.Value,
Timestamp = dataPoint.Time
});
}
OnNewMeasurements(measurements);
}
catch (Exception ex)
{
OnProcessException(MessageLevel.Warning, ex);
}
}
private void HistorianDataListener_SocketConnecting(object sender, EventArgs e)
{
OnStatusMessage(MessageLevel.Info, "Attempting socket connection...");
}
private void HistorianDataListener_SocketConnected(object sender, EventArgs e)
{
OnConnected();
}
private void HistorianDataListener_SocketDisconnected(object sender, EventArgs e)
{
OnDisconnected();
}
private void HistorianDataListener_OutputTypeNotFound(object sender, EventArgs e)
{
OnStatusMessage(MessageLevel.Warning, $"Unable to parse data for packet type {e.Argument}.");
}
private void HistorianDataListener_DataDiscarded(object sender, EventArgs e)
{
OnStatusMessage(MessageLevel.Warning, $"Unable to parse data: {e.Argument.Length} bytes discarded.");
}
private void HistorianDataListener_ParsingException(object sender, EventArgs e)
{
OnProcessException(MessageLevel.Warning, e.Argument);
}
#endregion
}
}