PageRenderTime 26ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/Alpha/Source/Libraries/Adapters/openHistorian.Adapters/RemoteOutputAdapter.cs

#
C# | 340 lines | 227 code | 38 blank | 75 comment | 12 complexity | 0f1cab08512f8ce9dc8312b5fbcd0760 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, EPL-1.0
  1. //******************************************************************************************************
  2. // HistorianInputQueue.cs - Gbtc
  3. //
  4. // Copyright © 2013, Grid Protection Alliance. All Rights Reserved.
  5. //
  6. // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
  7. // the NOTICE file distributed with this work for additional information regarding copyright ownership.
  8. // The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
  9. // not use this file except in compliance with the License. You may obtain a copy of the License at:
  10. //
  11. // http://www.opensource.org/licenses/eclipse-1.0.php
  12. //
  13. // Unless agreed to in writing, the subject software distributed under the License is distributed on an
  14. // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
  15. // License for the specific language governing permissions and limitations.
  16. //
  17. // Code Modification History:
  18. // ----------------------------------------------------------------------------------------------------
  19. // 1/5/2013 - Steven E. Chisholm
  20. // Generated original version of source code.
  21. //
  22. //******************************************************************************************************
  23. using System;
  24. using System.Collections.Generic;
  25. using System.ComponentModel;
  26. using System.Text;
  27. using GSF;
  28. using GSF.SortedTreeStore;
  29. using GSF.SortedTreeStore.Net;
  30. using GSF.TimeSeries;
  31. using GSF.TimeSeries.Adapters;
  32. using openHistorian.Collections;
  33. using GSF.SortedTreeStore.Tree;
  34. using openHistorian.Queues;
  35. namespace openHistorian.Adapters
  36. {
  37. /// <summary>
  38. /// Represents an output adapter that publishes measurements to TVA Historian for archival.
  39. /// </summary>
  40. [Description("openHistorian 2.0 (Remote): forwards measurements to a remote openHistorian for archival.")]
  41. public class RemoteOutputAdapter : OutputAdapterBase
  42. {
  43. #region [ Members ]
  44. // Constants
  45. private const int DefaultHistorianPort = 1003;
  46. private const bool DefaultOutputIsForArchive = true;
  47. private const string DefaultServer = "localhost";
  48. private const string DefaultDatabaseName = "default";
  49. // Fields
  50. private string m_databaseName;
  51. private string m_server;
  52. private int m_port;
  53. private bool m_outputIsForArchive;
  54. private SortedTreeStoreClient<HistorianKey, HistorianValue> m_client;
  55. private HistorianInputQueue m_inputQueue;
  56. private long m_measurementsPublished;
  57. private bool m_disposed;
  58. #endregion
  59. #region [ Constructors ]
  60. /// <summary>
  61. /// Initializes a new instance of the <see cref="RemoteOutputAdapter"/> class.
  62. /// </summary>
  63. public RemoteOutputAdapter()
  64. : base()
  65. {
  66. m_port = DefaultHistorianPort;
  67. m_outputIsForArchive = DefaultOutputIsForArchive;
  68. m_server = DefaultServer;
  69. m_databaseName = DefaultDatabaseName;
  70. }
  71. #endregion
  72. #region [ Properties ]
  73. /// <summary>
  74. /// Gets or sets the default database on the server to use to write data.
  75. /// </summary>
  76. [ConnectionStringParameter,
  77. Description("Defines the default database on the server to use to write data."),
  78. DefaultValue("default")]
  79. public string DatabaseName
  80. {
  81. get
  82. {
  83. return m_databaseName;
  84. }
  85. set
  86. {
  87. m_databaseName = value;
  88. }
  89. }
  90. /// <summary>
  91. /// Gets or sets the host name for the server hosting the remote historian.
  92. /// </summary>
  93. [ConnectionStringParameter,
  94. Description("Define the host name of the remote historian."),
  95. DefaultValue("localhost")]
  96. public string Server
  97. {
  98. get
  99. {
  100. return m_server;
  101. }
  102. set
  103. {
  104. m_server = value;
  105. }
  106. }
  107. /// <summary>
  108. /// Gets or sets the port on which the remote historian is listening.
  109. /// </summary>
  110. [ConnectionStringParameter,
  111. Description("Define the port on which the remote historian is listening."),
  112. DefaultValue(1003)]
  113. public int Port
  114. {
  115. get
  116. {
  117. return m_port;
  118. }
  119. set
  120. {
  121. m_port = value;
  122. }
  123. }
  124. /// <summary>
  125. /// Returns a flag that determines if measurements sent to this <see cref="RemoteOutputAdapter"/> are destined for archival.
  126. /// </summary>
  127. [ConnectionStringParameter,
  128. Description("Define a value that determines whether the measurements are destined for archival."),
  129. DefaultValue(true)]
  130. public override bool OutputIsForArchive
  131. {
  132. get
  133. {
  134. return m_outputIsForArchive;
  135. }
  136. }
  137. /// <summary>
  138. /// Gets flag that determines if this <see cref="RemoteOutputAdapter"/> uses an asynchronous connection.
  139. /// </summary>
  140. protected override bool UseAsyncConnect
  141. {
  142. get
  143. {
  144. return false;
  145. }
  146. }
  147. /// <summary>
  148. /// Returns the detailed status of the data output source.
  149. /// </summary>
  150. public override string Status
  151. {
  152. get
  153. {
  154. StringBuilder status = new StringBuilder();
  155. status.Append(base.Status);
  156. status.AppendLine();
  157. //status.Append(m_historianPublisher.Status);
  158. return status.ToString();
  159. }
  160. }
  161. #endregion
  162. #region [ Methods ]
  163. /// <summary>
  164. /// Initializes this <see cref="RemoteOutputAdapter"/>.
  165. /// </summary>
  166. /// <exception cref="ArgumentException"><b>Server</b> is missing from the <see cref="AdapterBase.Settings"/>.</exception>
  167. public override void Initialize()
  168. {
  169. base.Initialize();
  170. string errorMessage = "{0} is missing from Settings - Example: server=localhost;port=1003;payloadAware=True;conserveBandwidth=True;outputIsForArchive=True;throttleTransmission=True;samplesPerTransmission=100000";
  171. Dictionary<string, string> settings = Settings;
  172. string setting;
  173. // Validate settings.
  174. if (!settings.TryGetValue("server", out m_server))
  175. throw new ArgumentException(string.Format(errorMessage, "server"));
  176. if (settings.TryGetValue("port", out setting))
  177. m_port = int.Parse(setting);
  178. else
  179. settings.Add("port", m_port.ToString());
  180. if (settings.TryGetValue("outputisforarchive", out setting))
  181. m_outputIsForArchive = setting.ParseBoolean();
  182. }
  183. /// <summary>
  184. /// Gets a short one-line status of this <see cref="RemoteOutputAdapter"/>.
  185. /// </summary>
  186. /// <param name="maxLength">Maximum length of the status message.</param>
  187. /// <returns>Text of the status message.</returns>
  188. public override string GetShortStatus(int maxLength)
  189. {
  190. if (m_outputIsForArchive)
  191. return string.Format("Published {0} measurements for archival.", m_measurementsPublished).CenterText(maxLength);
  192. return string.Format("Published {0} measurements for processing.", m_measurementsPublished).CenterText(maxLength);
  193. }
  194. /// <summary>
  195. /// Releases the unmanaged resources used by this <see cref="RemoteOutputAdapter"/> and optionally releases the managed resources.
  196. /// </summary>
  197. /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
  198. protected override void Dispose(bool disposing)
  199. {
  200. if (!m_disposed)
  201. {
  202. try
  203. {
  204. // This will be done regardless of whether the object is finalized or disposed.
  205. if (disposing)
  206. {
  207. if (m_client != null)
  208. m_client.Dispose();
  209. }
  210. }
  211. finally
  212. {
  213. m_disposed = true; // Prevent duplicate dispose.
  214. base.Dispose(disposing); // Call base class Dispose().
  215. }
  216. }
  217. }
  218. /// <summary>
  219. /// Attempts to connect to this <see cref="RemoteOutputAdapter"/>.
  220. /// </summary>
  221. protected override void AttemptConnection()
  222. {
  223. HistorianClientOptions clientOptions = new HistorianClientOptions();
  224. clientOptions.DefaultDatabase = DatabaseName;
  225. clientOptions.ServerNameOrIp = Server;
  226. clientOptions.NetworkPort = Port;
  227. clientOptions.IsReadOnly = false;
  228. m_client = new HistorianClient(clientOptions);
  229. m_inputQueue = new HistorianInputQueue(() => m_client.GetDefaultDatabase());
  230. }
  231. /// <summary>
  232. /// Attempts to disconnect from this <see cref="RemoteOutputAdapter"/>.
  233. /// </summary>
  234. protected override void AttemptDisconnection()
  235. {
  236. if (m_client != null)
  237. m_client.Dispose();
  238. }
  239. /// <summary>
  240. /// Publishes <paramref name="measurements"/> for archival.
  241. /// </summary>
  242. /// <param name="measurements">Measurements to be archived.</param>
  243. /// <exception cref="OperationCanceledException">Acknowledgement is not received from historian for published data.</exception>
  244. protected override void ProcessMeasurements(IMeasurement[] measurements)
  245. {
  246. m_inputQueue.Enqueue(new StreamPoints(measurements));
  247. m_measurementsPublished += measurements.Length;
  248. }
  249. private class StreamPoints
  250. : TreeStream<HistorianKey, HistorianValue>
  251. {
  252. private int m_index;
  253. private readonly IMeasurement[] m_measurements;
  254. public StreamPoints(IMeasurement[] measurements)
  255. {
  256. m_index = 0;
  257. m_measurements = measurements;
  258. }
  259. public bool Read(out ulong timestamp, out ulong pointId, out ulong quality, out ulong value)
  260. {
  261. if (m_index < m_measurements.Length)
  262. {
  263. IMeasurement measurement = m_measurements[m_index];
  264. timestamp = (ulong)(long)measurement.Timestamp;
  265. pointId = measurement.Key.ID;
  266. quality = (ulong)measurement.StateFlags;
  267. value = BitMath.ConvertToUInt64((float)measurement.AdjustedValue);
  268. m_index++;
  269. return true;
  270. }
  271. timestamp = 0;
  272. pointId = 0;
  273. quality = 0;
  274. value = 0;
  275. return false;
  276. }
  277. public override bool Read(HistorianKey key, HistorianValue value)
  278. {
  279. if (m_index < m_measurements.Length)
  280. {
  281. IMeasurement measurement = m_measurements[m_index];
  282. key.Timestamp = (ulong)(long)measurement.Timestamp;
  283. key.PointID = measurement.Key.ID;
  284. key.EntryNumber = 0;
  285. value.Value1 = BitMath.ConvertToUInt64((float)measurement.AdjustedValue);
  286. value.Value2 = 0;
  287. value.Value3 = (ulong)measurement.StateFlags;
  288. m_index++;
  289. return true;
  290. }
  291. key.Clear();
  292. value.Clear();
  293. return false;
  294. }
  295. public override void Cancel()
  296. {
  297. m_index = m_measurements.Length;
  298. }
  299. }
  300. #endregion
  301. }
  302. }