private List <MetadataRecord> GetMetadata() { Ticks operationTime; Ticks operationStartTime; // Load historian meta-data ShowMessage(">>> Loading source connection metadata..."); operationStartTime = DateTime.UtcNow.Ticks; List <MetadataRecord> metadata = MetadataRecord.Query(m_settings.HostAddress, m_settings.MetadataPort, m_settings.MetadataTimeout); operationTime = DateTime.UtcNow.Ticks - operationStartTime; ShowMessage("*** Metadata Load Complete ***"); ShowMessage($"Total metadata load time {operationTime.ToElapsedTimeString(3)}..."); // Parse meta-data expression ShowMessage(">>> Processing filter expression for metadata..."); operationStartTime = DateTime.UtcNow.Ticks; MeasurementKey[] inputKeys = AdapterBase.ParseInputMeasurementKeys(MetadataRecord.Metadata, false, m_settings.PointList, "MeasurementDetail"); List <ulong> pointIDList = inputKeys.Select(key => (ulong)key.ID).ToList(); List <MetadataRecord> records = new List <MetadataRecord>(); foreach (ulong pointID in pointIDList) { MetadataRecord record = metadata.FirstOrDefault(md => md.PointID == pointID); if ((object)record != null) { records.Add(record); } } operationTime = DateTime.UtcNow.Ticks - operationStartTime; ShowMessage($">>> Historian read will be for {pointIDList.Count:N0} points based on provided meta-data expression."); ShowMessage("*** Filter Expression Processing Complete ***"); ShowMessage($"Total filter expression processing time {operationTime.ToElapsedTimeString(3)}..."); return(records); }
// Internal Functions private void ReadArchive(object state) { try { double timeRange = (m_settings.EndTime - m_settings.StartTime).TotalSeconds; long receivedPoints = 0; long processedDataBlocks = 0; long duplicatePoints = 0; Ticks operationTime; Ticks operationStartTime; DataPoint point = new DataPoint(); DateTime firstTimestamp = new DateTime(0L); DateTime lastTimestamp = new DateTime(0L); using (Algorithm algorithm = new Algorithm()) { algorithm.ShowMessage = ShowUpdateMessage; algorithm.MessageInterval = m_settings.MessageInterval; algorithm.StartTime = m_settings.StartTime; algorithm.EndTime = m_settings.EndTime; algorithm.FrameRate = m_settings.FrameRate; algorithm.TimeRange = timeRange; algorithm.Log = m_log; // Load historian meta-data ShowUpdateMessage(">>> Loading source connection metadata..."); operationStartTime = DateTime.UtcNow.Ticks; algorithm.Metadata = MetadataRecord.Query(m_settings.HostAddress, m_settings.MetadataPort, m_settings.MetadataTimeout); operationTime = DateTime.UtcNow.Ticks - operationStartTime; ShowUpdateMessage("*** Metadata Load Complete ***"); ShowUpdateMessage($"Total metadata load time {operationTime.ToElapsedTimeString(3)}..."); ShowUpdateMessage(">>> Processing filter expression for metadata..."); operationStartTime = DateTime.UtcNow.Ticks; MeasurementKey[] inputKeys = AdapterBase.ParseInputMeasurementKeys(MetadataRecord.Metadata, false, textBoxPointList.Text, "MeasurementDetail"); List <ulong> pointIDList = inputKeys.Select(key => (ulong)key.ID).ToList(); operationTime = DateTime.UtcNow.Ticks - operationStartTime; // Allow algorithm to augment (or even replace) point ID list as provided by user algorithm.AugmentPointIDList(pointIDList); ShowUpdateMessage($">>> Historian read will be for {pointIDList.Count:N0} points based on provided meta-data expression and algorithm augmentation."); // Reduce metadata to filtered point list ShowUpdateMessage($">>> Reducing metadata to the {pointIDList.Count:N0} defined points..."); List <MetadataRecord> records = new List <MetadataRecord>(); foreach (ulong pointID in pointIDList) { MetadataRecord record = algorithm.Metadata.FirstOrDefault(metadata => metadata.PointID == pointID); if ((object)record != null) { records.Add(record); } } algorithm.Metadata = records; ShowUpdateMessage("*** Filter Expression Processing Complete ***"); ShowUpdateMessage($"Total filter expression processing time {operationTime.ToElapsedTimeString(3)}..."); ShowUpdateMessage(">>> Initializing algorithm..."); algorithm.Initialize(); ShowUpdateMessage(">>> Starting archive read..."); // Start historian data read operationStartTime = DateTime.UtcNow.Ticks; using (SnapDBClient historianClient = new SnapDBClient(m_settings.HostAddress, m_settings.DataPort, m_settings.InstanceName, m_settings.StartTime, m_settings.EndTime, m_settings.FrameRate, pointIDList)) { // Scan to first record if (!historianClient.ReadNext(point)) { throw new InvalidOperationException("No data for specified time range in openHistorian connection!"); } ulong currentTimestamp; receivedPoints++; while (!m_formClosing) { int timeComparison; bool readSuccess = true; // Create a new data block for current timestamp and load first/prior point Dictionary <ulong, DataPoint> dataBlock = new Dictionary <ulong, DataPoint> { [point.PointID] = point.Clone() }; currentTimestamp = point.Timestamp; // Load remaining data for current timestamp do { // Scan to next record if (!historianClient.ReadNext(point)) { readSuccess = false; break; } receivedPoints++; timeComparison = DataPoint.CompareTimestamps(point.Timestamp, currentTimestamp, m_settings.FrameRate); if (timeComparison == 0) { // Timestamps are compared based on configured frame rate - if archived data rate is // higher than configured frame rate, then data block will contain only latest values if (dataBlock.ContainsKey(point.PointID)) { duplicatePoints++; } dataBlock[point.PointID] = point.Clone(); } }while (timeComparison == 0); // Finished with data read if (!readSuccess) { ShowUpdateMessage(">>> End of data in range encountered..."); break; } if (++processedDataBlocks % m_settings.MessageInterval == 0) { ShowUpdateMessage($"{Environment.NewLine}{receivedPoints:N0} points{(duplicatePoints > 0 ? $", which included {duplicatePoints:N0} duplicates," : "")} read so far averaging {receivedPoints / (DateTime.UtcNow.Ticks - operationStartTime).ToSeconds():N0} points per second."); UpdateProgressBar((int)((1.0D - new Ticks(m_settings.EndTime.Ticks - (long)point.Timestamp).ToSeconds() / timeRange) * 100.0D)); } try { lastTimestamp = new DateTime((long)currentTimestamp); if (firstTimestamp.Ticks == 0L) { firstTimestamp = lastTimestamp; } // Analyze data block algorithm.Execute(lastTimestamp, dataBlock.Values.ToArray()); } catch (Exception ex) { ShowUpdateMessage($"ERROR: Algorithm exception: {ex.Message}"); m_log.Publish(MessageLevel.Error, "AlgorithmError", "Failed while processing data from the historian", exception: ex); } } operationTime = DateTime.UtcNow.Ticks - operationStartTime; if (m_formClosing) { ShowUpdateMessage("*** Historian Read Canceled ***"); UpdateProgressBar(0); } else { ShowUpdateMessage("*** Historian Read Complete ***"); UpdateProgressBar(100); } algorithm.Complete(); // Show some operational statistics long expectedPoints = (long)(timeRange * m_settings.FrameRate * algorithm.Metadata.Count); double dataCompleteness = Math.Round(receivedPoints / (double)expectedPoints * 100000.0D) / 1000.0D; string overallSummary = $"Total processing time {operationTime.ToElapsedTimeString(3)} at {receivedPoints / operationTime.ToSeconds():N0} points per second.{Environment.NewLine}" + $"{Environment.NewLine}" + $" Meta-data points: {algorithm.Metadata.Count}{Environment.NewLine}" + $" Time-span covered: {timeRange:N0} seconds: {Ticks.FromSeconds(timeRange).ToElapsedTimeString(2)}{Environment.NewLine}" + $" Processed timestamps: {processedDataBlocks:N0}{Environment.NewLine}" + $" Expected points: {expectedPoints:N0} @ {m_settings.FrameRate:N0} samples per second{Environment.NewLine}" + $" Received points: {receivedPoints:N0}{Environment.NewLine}" + $" Duplicate points: {duplicatePoints:N0}{Environment.NewLine}" + $" Data completeness: {dataCompleteness:N3}%{Environment.NewLine}" + $" First timestamp with data: {firstTimestamp:yyyy-MM-dd HH:mm:ss.fff}{Environment.NewLine}" + $" Last timestamp with data: {lastTimestamp:yyyy-MM-dd HH:mm:ss.fff}{Environment.NewLine}"; ShowUpdateMessage(overallSummary); } } } catch (Exception ex) { ShowUpdateMessage($"!!! Failure during historian read: {ex.Message}"); m_log.Publish(MessageLevel.Error, "HistorianDataRead", "Failed while reading data from the historian", exception: ex); } finally { SetGoButtonEnabledState(true); } }