//****************************************************************************************************** // InputStatusMonitorUserControl.xaml.cs - Gbtc // // Copyright © 2010, 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: // ---------------------------------------------------------------------------------------------------- // 08/15/2011 - Mehulbhai P Thakkar // Generated original version of source code. // //****************************************************************************************************** using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Threading; using GSF.Data; using GSF.PhasorProtocols.UI.DataModels; using GSF.PhasorProtocols.UI.ViewModels; using GSF.TimeSeries; using GSF.TimeSeries.Adapters; using GSF.TimeSeries.Transport; using GSF.TimeSeries.UI; using GSF.TimeSeries.UI.UserControls; using Microsoft.Research.DynamicDataDisplay; using Microsoft.Research.DynamicDataDisplay.Charts; using Microsoft.Research.DynamicDataDisplay.DataSources; using Microsoft.Win32; using Measurement = GSF.TimeSeries.UI.DataModels.Measurement; #pragma warning disable 612,618 namespace GSF.PhasorProtocols.UI.UserControls { /// /// Interaction logic for InputStatusMonitorUserControl.xaml /// public partial class InputStatusMonitorUserControl { #region [ Members ] // Fields private RealTimeStreams m_dataContext; private List m_lineColors; // Synchronized subscription fields private bool m_restartConnectionCycle; private DataSubscriber m_subscriber; private bool m_subscribedSynchronized; private DispatcherTimer m_refreshTimer; private string m_selectedSignalIDs; private int m_processingSynchronizedMeasurements; private int[] m_xAxisDataCollection; // Source data for the binding collection. private EnumerableDataSource m_xAxisBindingCollection; // Values plotted on X-Axis. private ConcurrentQueue m_timeStampList; private ConcurrentDictionary> m_yAxisDataCollection; // Source data for the binding collection. Format is . private ConcurrentDictionary> m_yAxisBindingCollection; // Values plotted on Y-Axis. private ConcurrentDictionary m_lineGraphCollection; // List of graphs plotted on the chart. private ConcurrentDictionary m_selectedMeasurements; // Measurements user have selected to plot. private ObservableCollection m_displayedMeasurement; private int m_numberOfDataPointsToPlot = 30; private int m_framesPerSecond = 30; // Sample rate of the data from subscription API/Data Resolution private double m_leadTime = 1.0; private double m_lagTime = 3.0; private bool m_useLocalClockAsRealtime = true; private bool m_ignoreBadTimestamps; private int m_chartRefreshInterval = 66; private int m_statisticsDataRefreshInterval = 10; private int m_measurementsDataRefreshInterval = 10; private double m_frequencyRangeMin = 59.95; private double m_frequencyRangeMax = 60.05; private bool m_displayFrequencyYAxis; private bool m_displayPhaseAngleYAxis; private bool m_displayVoltageYAxis; private bool m_displayCurrentYAxis; private bool m_displayXAxis; private bool m_displayLegend; private long m_refreshRate = Ticks.FromMilliseconds(500); // This is used to refresh real-time values below chart. private long m_lastRefreshTime; private bool m_historicalPlayback; private bool m_waitingForData; private bool m_forceIPv4 = true; #endregion #region [ Constructors ] /// /// Creates an instance of . /// public InputStatusMonitorUserControl() { InitializeComponent(); Initialize(); this.Loaded += InputStatusMonitorUserControl_Loaded; this.Unloaded += InputStatusMonitorUserControl_Unloaded; this.KeyUp += InputStatusMonitorUserControl_KeyUp; } #endregion #region [ Methods ] private void InputStatusMonitorUserControl_KeyUp(object sender, KeyEventArgs e) { if (e.Key == Key.Escape && PopupSettings.IsOpen) PopupSettings.IsOpen = false; } private void InputStatusMonitorUserControl_Unloaded(object sender, RoutedEventArgs e) { m_restartConnectionCycle = false; m_dataContext.RestartConnectionCycle = false; Unsubscribe(); m_dataContext.UnsubscribeUnsynchronizedData(); IsolatedStorageManager.WriteToIsolatedStorage("InputMonitoringPoints", m_selectedSignalIDs); } /// /// Handles loaded event. /// /// Source of the event. /// Event arguments. private void InputStatusMonitorUserControl_Loaded(object sender, RoutedEventArgs e) { m_historicalPlayback = false; m_waitingForData = false; ModeMessage.Text = "Real-time"; ChartPlotterDynamic.Background = new SolidColorBrush(Color.FromArgb(255, 255, 255, 255)); InitializeUserControl(); } private void CheckBox_Checked(object sender, RoutedEventArgs e) { RealTimeMeasurement measurement = (RealTimeMeasurement)((CheckBox)sender).DataContext; m_selectedMeasurements.TryAdd(measurement.SignalID, measurement); RefreshSelectedMeasurements(); AddToDisplayedMeasurements(measurement); } private void CheckBox_Unchecked(object sender, RoutedEventArgs e) { RealTimeMeasurement measurement = (RealTimeMeasurement)((CheckBox)sender).DataContext; m_selectedMeasurements.TryRemove(measurement.SignalID, out measurement); RemoveLineGraph(measurement); RefreshSelectedMeasurements(); RemoveFromDisplayedMeasurements(measurement); } private void ButtonSave_Click(object sender, RoutedEventArgs e) { try { SaveFileDialog saveDialog = new SaveFileDialog(); saveDialog.Title = "Save Current Display Settings"; saveDialog.Filter = "Input Status Monitoring Display Settings (*.ismsettings)|*.ismsettings|All Files (*.*)|*.*"; bool? result = saveDialog.ShowDialog(Window.GetWindow(this)); if (result.GetValueOrDefault()) { using (StreamWriter writer = new StreamWriter(saveDialog.FileName)) writer.Write(m_selectedSignalIDs); } } catch (Exception) { // TODO: Turn this into popup error //System.Diagnostics.Debug.WriteLine(ex.ToString()); } } private void ButtonLoad_Click(object sender, RoutedEventArgs e) { try { OpenFileDialog openDialog = new OpenFileDialog(); openDialog.Multiselect = false; openDialog.Filter = "Input Status Monitoring Display Settings (*.ismsettings)|*.ismsettings|All Files (*.*)|*.*"; bool? result = openDialog.ShowDialog(Window.GetWindow(this)); if (result.GetValueOrDefault()) { using (StreamReader reader = new StreamReader(openDialog.OpenFile())) { string selection = reader.ReadLine().ToNonNullString(); string[] signalIDs = selection.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); AutoSelectMeasurements(signalIDs); } } } catch (Exception) { // TODO: Turn this into popup error //System.Diagnostics.Debug.WriteLine(ex.ToString()); } } private void AddToDisplayedMeasurements(RealTimeMeasurement measurement) { bool addMeasurement = true; foreach (RealTimeMeasurement m in m_displayedMeasurement) { if (m.SignalID == measurement.SignalID) { addMeasurement = false; break; } } if (addMeasurement) m_displayedMeasurement.Add(measurement); } private void RemoveFromDisplayedMeasurements(RealTimeMeasurement measurement) { bool removeMeasurement = false; foreach (RealTimeMeasurement m in m_displayedMeasurement) { if (m.SignalID == measurement.SignalID) { removeMeasurement = true; break; } } if (removeMeasurement) m_displayedMeasurement.Remove(measurement); } private void Initialize() { RetrieveSettingsFromIsolatedStorage(false); m_timeStampList = new ConcurrentQueue(); m_yAxisDataCollection = new ConcurrentDictionary>(); m_yAxisBindingCollection = new ConcurrentDictionary>(); m_lineGraphCollection = new ConcurrentDictionary(); m_selectedMeasurements = new ConcurrentDictionary(); m_displayedMeasurement = new ObservableCollection(); m_displayedMeasurement.CollectionChanged += m_displayedMeasurement_CollectionChanged; m_restartConnectionCycle = true; m_xAxisDataCollection = new int[m_numberOfDataPointsToPlot]; m_refreshRate = Ticks.FromMilliseconds(m_chartRefreshInterval); TextBlockMeasurementRefreshInterval.Text = m_measurementsDataRefreshInterval.ToString(); TextBlockStatisticsRefreshInterval.Text = m_statisticsDataRefreshInterval.ToString(); TextBoxProcessInterval.Text = "33"; } private void m_displayedMeasurement_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.Action == NotifyCollectionChangedAction.Remove) { foreach (object item in e.OldItems) ((RealTimeMeasurement)item).Selected = false; } } private void InitializeUserControl() { m_dataContext = new RealTimeStreams(1, m_measurementsDataRefreshInterval); this.DataContext = m_dataContext; //ListBoxCurrentValues.ItemsSource = m_displayedMeasurement; DataGridCurrentValues.ItemsSource = m_displayedMeasurement; // Initialize Chart Properties. InitializeColors(); InitializeChart(); m_selectedSignalIDs = IsolatedStorageManager.ReadFromIsolatedStorage("InputMonitoringPoints").ToString(); string[] signalIDs = m_selectedSignalIDs.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); AutoSelectMeasurements(signalIDs); PopulateSettings(); foreach (RealTimeStream stream in m_dataContext.ItemsSource) { if (stream.ID > 0) { GetStatistics(stream.Acronym); break; } foreach (RealTimeDevice device in stream.DeviceList) { if (device.ID > 0) { GetStatistics(device.Acronym); break; } } } } private void InitializeColors() { m_lineColors = new List(); m_lineColors.Add(Colors.DarkGoldenrod); m_lineColors.Add(Colors.Blue); m_lineColors.Add(Colors.Green); m_lineColors.Add(Colors.Red); m_lineColors.Add(Colors.Purple); m_lineColors.Add(Colors.Brown); m_lineColors.Add(Colors.Magenta); m_lineColors.Add(Colors.Black); m_lineColors.Add(Colors.DarkCyan); m_lineColors.Add(Colors.Coral); } private void InitializeChart() { ((HorizontalAxis)ChartPlotterDynamic.MainHorizontalAxis).LabelProvider.LabelStringFormat = ""; //Remove legend on the right. Panel legendParent = (Panel)ChartPlotterDynamic.Legend.ContentGrid.Parent; if (legendParent != null) legendParent.Children.Remove(ChartPlotterDynamic.Legend.ContentGrid); ChartPlotterDynamic.NewLegendVisible = m_displayLegend; ChartPlotterDynamic.MainVerticalAxisVisibility = FrequencyAxisTitle.Visibility = m_displayFrequencyYAxis ? Visibility.Visible : Visibility.Collapsed; ChartPlotterDynamic.MainHorizontalAxisVisibility = m_displayXAxis ? Visibility.Visible : Visibility.Collapsed; PhaseAngleYAxis.Visibility = PhaseAngleAxisTitle.Visibility = m_displayPhaseAngleYAxis ? Visibility.Visible : Visibility.Collapsed; VoltageYAxis.Visibility = VoltageAxisTitle.Visibility = m_displayVoltageYAxis ? Visibility.Visible : Visibility.Collapsed; CurrentYAxis.Visibility = CurrentAxisTitle.Visibility = m_displayCurrentYAxis ? Visibility.Visible : Visibility.Collapsed; // This check was added as there was an error reported where it was infinity. if (m_frequencyRangeMin.IsInfinite() || m_frequencyRangeMin.IsNaN()) m_frequencyRangeMin = 59.95; if (m_frequencyRangeMax.IsInfinite() || m_frequencyRangeMax.IsNaN()) m_frequencyRangeMax = 60.05; // DataRect.Create didn't like it when the min > max. if (m_frequencyRangeMin > m_frequencyRangeMax) { double temp = m_frequencyRangeMin; m_frequencyRangeMin = m_frequencyRangeMax; m_frequencyRangeMax = temp; } ChartPlotterDynamic.Visible = DataRect.Create(0, m_frequencyRangeMin, m_numberOfDataPointsToPlot, m_frequencyRangeMax); PhaseAnglePlotter.Visible = DataRect.Create(0, -180, m_numberOfDataPointsToPlot, 180); TextBlockLeft.Visibility = TextBlockRight.Visibility = ChartPlotterDynamic.MainHorizontalAxisVisibility; for (int i = 0; i < m_numberOfDataPointsToPlot; i++) m_xAxisDataCollection[i] = i; m_xAxisBindingCollection = new EnumerableDataSource(m_xAxisDataCollection); m_xAxisBindingCollection.SetXMapping(x => x); } private void RetrieveSettingsFromIsolatedStorage(bool reloadSelectedMeasurements = true) { // Retrieve values from IsolatedStorage. int.TryParse(IsolatedStorageManager.ReadFromIsolatedStorage("NumberOfDataPointsToPlot").ToString(), out m_numberOfDataPointsToPlot); int.TryParse(IsolatedStorageManager.ReadFromIsolatedStorage("DataResolution").ToString(), out m_framesPerSecond); int.TryParse(IsolatedStorageManager.ReadFromIsolatedStorage("ChartRefreshInterval").ToString(), out m_chartRefreshInterval); int.TryParse(IsolatedStorageManager.ReadFromIsolatedStorage("StatisticsDataRefreshInterval").ToString(), out m_statisticsDataRefreshInterval); int.TryParse(IsolatedStorageManager.ReadFromIsolatedStorage("MeasurementsDataRefreshInterval").ToString(), out m_measurementsDataRefreshInterval); double.TryParse(IsolatedStorageManager.ReadFromIsolatedStorage("LagTime").ToString(), out m_lagTime); double.TryParse(IsolatedStorageManager.ReadFromIsolatedStorage("LeadTime").ToString(), out m_leadTime); double.TryParse(IsolatedStorageManager.ReadFromIsolatedStorage("FrequencyRangeMin").ToString(), out m_frequencyRangeMin); double.TryParse(IsolatedStorageManager.ReadFromIsolatedStorage("FrequencyRangeMax").ToString(), out m_frequencyRangeMax); m_forceIPv4 = IsolatedStorageManager.ReadFromIsolatedStorage("ForceIPv4").ToString().ParseBoolean(); m_displayXAxis = IsolatedStorageManager.ReadFromIsolatedStorage("DisplayXAxis").ToString().ParseBoolean(); m_displayLegend = IsolatedStorageManager.ReadFromIsolatedStorage("DisplayLegend").ToString().ParseBoolean(); m_displayFrequencyYAxis = IsolatedStorageManager.ReadFromIsolatedStorage("DisplayFrequencyYAxis").ToString().ParseBoolean(); m_displayPhaseAngleYAxis = IsolatedStorageManager.ReadFromIsolatedStorage("DisplayPhaseAngleYAxis").ToString().ParseBoolean(); m_displayCurrentYAxis = IsolatedStorageManager.ReadFromIsolatedStorage("DisplayCurrentYAxis").ToString().ParseBoolean(); m_displayVoltageYAxis = IsolatedStorageManager.ReadFromIsolatedStorage("DisplayVoltageYAxis").ToString().ParseBoolean(); m_useLocalClockAsRealtime = IsolatedStorageManager.ReadFromIsolatedStorage("UseLocalClockAsRealtime").ToString().ParseBoolean(); m_ignoreBadTimestamps = IsolatedStorageManager.ReadFromIsolatedStorage("IgnoreBadTimestamps").ToString().ParseBoolean(); ValidateSettingsAfterRead(); if (reloadSelectedMeasurements) { m_selectedSignalIDs = IsolatedStorageManager.ReadFromIsolatedStorage("InputMonitoringPoints").ToString(); string[] signalIDs = m_selectedSignalIDs.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); AutoSelectMeasurements(signalIDs); } } /// /// Makes sure the user doesn't enter values that will crash the system. /// private void ValidateSettingsAfterRead() { m_numberOfDataPointsToPlot = Math.Abs(m_numberOfDataPointsToPlot); //m_framesPerSecond = Math.Abs(m_framesPerSecond); //m_lagTime = Math.Abs(m_lagTime); //m_leadTime = Math.Abs(m_leadTime); //m_chartRefreshInterval = Math.Abs(m_chartRefreshInterval); //m_statisticsDataRefershInterval = Math.Abs(m_statisticsDataRefershInterval); //m_measurementsDataRefreshInterval = Math.Abs(m_measurementsDataRefreshInterval); m_frequencyRangeMin = Math.Abs(m_frequencyRangeMin); m_frequencyRangeMax = Math.Abs(m_frequencyRangeMax); if (m_frequencyRangeMin > m_frequencyRangeMax) { double temp = m_frequencyRangeMin; m_frequencyRangeMin = m_frequencyRangeMax; m_frequencyRangeMax = temp; } } private void AutoSelectMeasurements(string[] signalIDs) { if (signalIDs.Any()) { foreach (RealTimeStream stream in m_dataContext.ItemsSource) { stream.Expanded = false; foreach (RealTimeDevice device in stream.DeviceList) { device.Expanded = false; foreach (RealTimeMeasurement measurement in device.MeasurementList) { if (signalIDs.Contains(measurement.SignalID.ToString())) { measurement.Selected = true; device.Expanded = true; stream.Expanded = true; m_dataContext.Expanded = true; m_selectedMeasurements.TryAdd(measurement.SignalID, measurement); AddToDisplayedMeasurements(measurement); } else { measurement.Selected = false; RemoveFromDisplayedMeasurements(measurement); RealTimeMeasurement tempMeasurement; m_selectedMeasurements.TryRemove(measurement.SignalID, out tempMeasurement); RemoveLineGraph(measurement); } } } } if (m_selectedMeasurements.Count > 0) Subscribe(); } } private void RefreshSelectedMeasurements() { StringBuilder sb = new StringBuilder(); foreach (KeyValuePair measurement in m_selectedMeasurements) { sb.Append(measurement.Value.SignalID); sb.Append(";"); } m_selectedSignalIDs = sb.ToString(); if (m_selectedSignalIDs.Length > 0) m_selectedSignalIDs = m_selectedSignalIDs.Substring(0, m_selectedSignalIDs.Length - 1); // once user has changed selection, resubscribe with new values. Subscribe(); } private void StartRefreshTimer() { if (m_refreshTimer == null) { double chartRefreshInterval = (m_chartRefreshInterval >= 0) ? m_chartRefreshInterval : 66; m_refreshTimer = new DispatcherTimer(); m_refreshTimer.Interval = TimeSpan.FromMilliseconds(chartRefreshInterval); m_refreshTimer.Tick += m_refreshTimer_Tick; m_refreshTimer.Start(); } } private void m_refreshTimer_Tick(object sender, EventArgs e) { foreach (KeyValuePair> keyValuePair in m_yAxisBindingCollection) { try { lock (keyValuePair.Value) keyValuePair.Value.RaiseDataChanged(); } catch { } } if (m_timeStampList.Count > 0) { TextBlockLeft.Text = m_timeStampList.First(); TextBlockRight.Text = m_timeStampList.Last(); } } private void StopRefreshTimer() { try { if (m_refreshTimer != null) { m_refreshTimer.Stop(); m_refreshTimer.Tick -= m_refreshTimer_Tick; } } finally { m_refreshTimer = null; } } private void RemoveLineGraph(RealTimeMeasurement measurement) { LineGraph lineGraphToBeRemoved; EnumerableDataSource bindingCollectionToBeRemoved; ConcurrentQueue dataCollectionToBeRemoved; measurement.Foreground = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0)); m_yAxisBindingCollection.TryRemove(measurement.SignalID, out bindingCollectionToBeRemoved); m_yAxisDataCollection.TryRemove(measurement.SignalID, out dataCollectionToBeRemoved); if (m_lineGraphCollection.TryRemove(measurement.SignalID, out lineGraphToBeRemoved)) { if (measurement.SignalAcronym == "FREQ") ChartPlotterDynamic.Children.Remove((IPlotterElement)lineGraphToBeRemoved); else if (measurement.SignalAcronym == "IPHA" || measurement.SignalAcronym == "VPHA") PhaseAnglePlotter.Children.Remove((IPlotterElement)lineGraphToBeRemoved); else if (measurement.SignalAcronym == "VPHM") VoltagePlotter.Children.Remove((IPlotterElement)lineGraphToBeRemoved); else if (measurement.SignalAcronym == "IPHM") CurrentPlotter.Children.Remove((IPlotterElement)lineGraphToBeRemoved); } } private void ButtonGetStatistics_Click(object sender, RoutedEventArgs e) { GetStatistics(((Button)sender).Tag.ToString()); } private void GetStatistics(string acronym) { Device device = Device.GetDevice(null, "WHERE Acronym = '" + acronym + "'"); if (device != null) { TextBlockDevice.Text = device.Acronym; m_dataContext.GetStatistics(device); //ListBoxStatistics.ItemsSource = m_dataContext.StatisticMeasurements; DataGridStatistics.ItemsSource = m_dataContext.StatisticMeasurements; } } private void ButtonEdit_Click(object sender, RoutedEventArgs e) { Device device = Device.GetDevice(null, "WHERE Acronym = '" + ((Button)sender).Tag + "'"); CommonFunctions.LoadUserControl("Manage Device Configuration", typeof(DeviceUserControl), device); } private void ButtonMeasurement_Click(object sender, RoutedEventArgs e) { DependencyObject obj = sender as DependencyObject; TreeViewItem item; RealTimeMeasurement realTimeMeasurement; Measurement measurement; PhasorMeasurementUserControl phasorMeasurementUserControl; while ((object)obj != null && obj.GetType() != typeof(TreeViewItem)) obj = VisualTreeHelper.GetParent(obj); item = (TreeViewItem)obj; realTimeMeasurement = (RealTimeMeasurement)item?.DataContext; measurement = Measurement.GetMeasurement(null, $"WHERE SignalReference = '{realTimeMeasurement?.SignalReference}'"); if ((object)measurement != null && measurement.DeviceID.HasValue) { phasorMeasurementUserControl = CommonFunctions.LoadUserControl("Manage Measurements for " + measurement.DeviceAcronym, typeof(PhasorMeasurementUserControl), (int)measurement.DeviceID) as PhasorMeasurementUserControl; if (phasorMeasurementUserControl != null) ((PhasorMeasurements)phasorMeasurementUserControl.DataContext).CurrentItem = measurement; } } private void m_subscriber_ConnectionTerminated(object sender, EventArgs e) { m_subscribedSynchronized = false; Unsubscribe(); if (m_restartConnectionCycle) InitializeSubscription(); } private void m_subscriber_NewMeasurements(object sender, EventArgs> e) { if (0 == Interlocked.Exchange(ref m_processingSynchronizedMeasurements, 1)) { try { if (m_historicalPlayback && m_waitingForData) { m_waitingForData = false; Dispatcher.BeginInvoke(new Action(delegate { ModeMessage.Text = "Historical"; })); } bool refreshMeasurementValueBelowChart = false; if (DateTime.UtcNow.Ticks - m_lastRefreshTime > m_refreshRate) { m_lastRefreshTime = DateTime.UtcNow.Ticks; refreshMeasurementValueBelowChart = true; } foreach (IMeasurement newMeasurement in e.Argument) { m_timeStampList.Enqueue(newMeasurement.Timestamp.ToString("HH:mm:ss.fff")); if (m_timeStampList.Count > m_numberOfDataPointsToPlot) { string oldValue; m_timeStampList.TryDequeue(out oldValue); } double tempValue = newMeasurement.AdjustedValue; Guid tempSignalID = newMeasurement.ID; if (!double.IsNaN(tempValue) && !double.IsInfinity(tempValue)) // Process data only if it is not NaN or infinity. { ConcurrentQueue tempValueCollection; if (m_yAxisDataCollection.TryGetValue(tempSignalID, out tempValueCollection)) // If value collection already exists, then just replace oldest value with newest. { double oldValue; if (tempValueCollection.TryDequeue(out oldValue)) tempValueCollection.Enqueue(tempValue); } else // It is probably a new measurement user wants to subscribe to. { RealTimeMeasurement measurement; // Check if user has selected this measurement. Because user may have unselected this but request may not have been processed completely. if (m_selectedMeasurements.TryGetValue(tempSignalID, out measurement)) { tempValueCollection = new ConcurrentQueue(); for (int i = 0; i < m_numberOfDataPointsToPlot; i++) tempValueCollection.Enqueue(tempValue); m_yAxisDataCollection.TryAdd(tempSignalID, tempValueCollection); EnumerableDataSource tempDataSource = new EnumerableDataSource(tempValueCollection); m_yAxisBindingCollection.TryAdd(tempSignalID, tempDataSource); tempDataSource.SetYMapping(y => y); ChartPlotterDynamic.Dispatcher.Invoke(DispatcherPriority.Normal, (Action)delegate { int colorIndex; Math.DivRem(m_yAxisBindingCollection.Count, 10, out colorIndex); LineGraph lineGraph = null; if (measurement.SignalAcronym == "FREQ") lineGraph = ChartPlotterDynamic.AddLineGraph(new CompositeDataSource(m_xAxisBindingCollection, tempDataSource), m_lineColors[colorIndex], 1, measurement.SignalReference); else if (measurement.SignalAcronym == "IPHA" || measurement.SignalAcronym == "VPHA") lineGraph = PhaseAnglePlotter.AddLineGraph(new CompositeDataSource(m_xAxisBindingCollection, tempDataSource), m_lineColors[colorIndex], 1, measurement.SignalReference); else if (measurement.SignalAcronym == "VPHM") lineGraph = VoltagePlotter.AddLineGraph(new CompositeDataSource(m_xAxisBindingCollection, tempDataSource), m_lineColors[colorIndex], 1, measurement.SignalReference); else if (measurement.SignalAcronym == "IPHM") lineGraph = CurrentPlotter.AddLineGraph(new CompositeDataSource(m_xAxisBindingCollection, tempDataSource), m_lineColors[colorIndex], 1, measurement.SignalReference); if ((object)lineGraph != null) { m_lineGraphCollection.TryAdd(tempSignalID, lineGraph); measurement.Foreground = (SolidColorBrush)lineGraph.LinePen.Brush; } }); } } if (refreshMeasurementValueBelowChart) { lock (m_selectedMeasurements) { RealTimeMeasurement measurement; if (m_selectedMeasurements.TryGetValue(tempSignalID, out measurement)) { measurement.Value = tempValue.ToString("0.###"); measurement.LongTimeTag = newMeasurement.Timestamp.ToString("yyyy-MM-dd HH:mm:ss.fff"); measurement.TimeTag = newMeasurement.Timestamp.ToString("HH:mm:ss.fff"); measurement.Quality = newMeasurement.ValueQualityIsGood() ? "GOOD" : "UNKNOWN"; } } } } } } finally { Interlocked.Exchange(ref m_processingSynchronizedMeasurements, 0); } } } private void m_subscriber_ConnectionEstablished(object sender, EventArgs e) { m_subscribedSynchronized = true; Subscribe(); } private void m_subscriber_ProcessException(object sender, EventArgs e) { } private void m_subscriber_StatusMessage(object sender, EventArgs e) { } private void m_subscriber_ProcessingComplete(object sender, EventArgs e) { Dispatcher.BeginInvoke(new Action(() => ButtonReturnToRealtime_Click(null, null))); } private void InitializeSubscription() { try { using (AdoDataConnection database = new AdoDataConnection(CommonFunctions.DefaultSettingsCategory)) { m_subscriber = new DataSubscriber(); m_subscriber.StatusMessage += m_subscriber_StatusMessage; m_subscriber.ProcessException += m_subscriber_ProcessException; m_subscriber.ConnectionEstablished += m_subscriber_ConnectionEstablished; m_subscriber.NewMeasurements += m_subscriber_NewMeasurements; m_subscriber.ConnectionTerminated += m_subscriber_ConnectionTerminated; m_subscriber.ProcessingComplete += m_subscriber_ProcessingComplete; m_subscriber.ConnectionString = database.DataPublisherConnectionString(); m_subscriber.Initialize(); m_subscriber.Start(); } } catch { // TODO: show error in popup window //Popup("Failed to initialize subscription." + Environment.NewLine + ex.Message, "Failed to Subscribe", MessageBoxImage.Error); } } private void DisposeSubscription() { if (m_subscriber != null) { m_subscriber.StatusMessage -= m_subscriber_StatusMessage; m_subscriber.ProcessException -= m_subscriber_ProcessException; m_subscriber.ConnectionEstablished -= m_subscriber_ConnectionEstablished; m_subscriber.NewMeasurements -= m_subscriber_NewMeasurements; m_subscriber.ConnectionTerminated -= m_subscriber_ConnectionTerminated; m_subscriber.ProcessingComplete -= m_subscriber_ProcessingComplete; m_subscriber.Stop(); m_subscriber.Dispose(); m_subscriber = null; } } private void Subscribe() { if (m_selectedMeasurements.Count == 0) { Unsubscribe(); } else { if (m_subscriber == null) { InitializeSubscription(); } else { if (m_subscribedSynchronized && !string.IsNullOrEmpty(m_selectedSignalIDs)) { m_subscriber.Unsubscribe(); if (m_historicalPlayback) { Dispatcher.BeginInvoke(new Action(delegate { string startTime = TextBoxStartTime.Text; string stopTime = TextBoxStopTime.Text; int processingInterval = int.Parse(TextBoxProcessInterval.Text); if (DateTime.Compare(AdapterBase.ParseTimeTag(startTime), AdapterBase.ParseTimeTag(stopTime)) < 0) { ModeMessage.Text = "Initializing historical playback..."; //m_synchronizedSubscriber.SynchronizedSubscribe(true, m_framesPerSecond, m_lagTime, m_leadTime, m_selectedSignalIDs, null, m_useLocalClockAsRealtime, m_ignoreBadTimestamps, startTime: TextBoxStartTime.Text, stopTime: TextBoxStopTime.Text, processingInterval: (int)SliderProcessInterval.Value); m_subscriber.UnsynchronizedSubscribe(true, false, m_selectedSignalIDs, null, true, m_lagTime, m_leadTime, m_useLocalClockAsRealtime, startTime, stopTime, null, processingInterval); m_waitingForData = true; } else { MessageBox.Show("Start time must precede end time"); } })); } else { //m_synchronizedSubscriber.SynchronizedSubscribe(true, m_framesPerSecond, m_lagTime, m_leadTime, m_selectedSignalIDs, null, m_useLocalClockAsRealtime, m_ignoreBadTimestamps); m_subscriber.UnsynchronizedSubscribe(true, false, m_selectedSignalIDs, null, true, m_lagTime, m_leadTime, m_useLocalClockAsRealtime); } } ChartPlotterDynamic.Dispatcher.BeginInvoke((Action)StartRefreshTimer); } } } private void Unsubscribe() { try { if (m_subscriber != null) { m_subscriber.Unsubscribe(); DisposeSubscription(); } } catch { m_subscriber = null; } StopRefreshTimer(); } private void ButtonHelp_Click(object sender, RoutedEventArgs e) { PanAndZoomViewer viewer = new PanAndZoomViewer(new BitmapImage(new Uri(@"/GSF.PhasorProtocols.UI;component/Images/" + ((Button)sender).Tag, UriKind.Relative)), "Help Me Choose"); viewer.Owner = Window.GetWindow(this); viewer.WindowStartupLocation = WindowStartupLocation.CenterOwner; viewer.Topmost = true; viewer.ShowDialog(); } private void ButtonRestoreSettings_Click(object sender, RoutedEventArgs e) { m_restartConnectionCycle = false; m_dataContext.RestartConnectionCycle = false; Unsubscribe(); m_dataContext.UnsubscribeUnsynchronizedData(); IsolatedStorageManager.InitializeStorageForInputStatusMonitor(true); RetrieveSettingsFromIsolatedStorage(); PopulateSettings(); PopupSettings.IsOpen = false; CommonFunctions.LoadUserControl(CommonFunctions.GetHeaderText("Graph Real-time Measurements"), typeof(InputStatusMonitorUserControl)); } private void ButtonSaveSettings_Click(object sender, RoutedEventArgs e) { ValidateSettingsBeforeWrite(); IsolatedStorageManager.WriteToIsolatedStorage("InputMonitoringPoints", TextBoxLastSelectedMeasurements.Text); IsolatedStorageManager.WriteToIsolatedStorage("ForceIPv4", CheckBoxForceIPv4.IsChecked.GetValueOrDefault()); IsolatedStorageManager.WriteToIsolatedStorage("NumberOfDataPointsToPlot", TextBoxNumberOFDataPointsToPlot.Text); IsolatedStorageManager.WriteToIsolatedStorage("DataResolution", TextBoxDataResolution.Text); IsolatedStorageManager.WriteToIsolatedStorage("LagTime", TextBoxLagTime.Text); IsolatedStorageManager.WriteToIsolatedStorage("LeadTime", TextBoxLeadTime.Text); IsolatedStorageManager.WriteToIsolatedStorage("UseLocalClockAsRealtime", CheckBoxUseLocalClockAsRealTime.IsChecked.GetValueOrDefault()); IsolatedStorageManager.WriteToIsolatedStorage("IgnoreBadTimestamps", CheckBoxIgnoreBadTimestamps.IsChecked.GetValueOrDefault()); IsolatedStorageManager.WriteToIsolatedStorage("ChartRefreshInterval", TextBoxChartRefreshInterval.Text); IsolatedStorageManager.WriteToIsolatedStorage("StatisticsDataRefreshInterval", TextBoxStatisticDataRefreshInterval.Text); IsolatedStorageManager.WriteToIsolatedStorage("MeasurementsDataRefreshInterval", TextBoxMeasurementDataRefreshInterval.Text); IsolatedStorageManager.WriteToIsolatedStorage("DisplayXAxis", CheckBoxDisplayXAxis.IsChecked.GetValueOrDefault()); IsolatedStorageManager.WriteToIsolatedStorage("DisplayFrequencyYAxis", CheckBoxDisplayFrequencyYAxis.IsChecked.GetValueOrDefault()); IsolatedStorageManager.WriteToIsolatedStorage("DisplayPhaseAngleYAxis", CheckBoxDisplayPhaseAngleYAxis.IsChecked.GetValueOrDefault()); IsolatedStorageManager.WriteToIsolatedStorage("DisplayVoltageYAxis", CheckBoxDisplayVoltageMagnitudeYAxis.IsChecked.GetValueOrDefault()); IsolatedStorageManager.WriteToIsolatedStorage("DisplayCurrentYAxis", CheckBoxDisplayCurrentMagnitudeYAxis.IsChecked.GetValueOrDefault()); IsolatedStorageManager.WriteToIsolatedStorage("FrequencyRangeMin", TextBoxFrequencyRangeMin.Text); IsolatedStorageManager.WriteToIsolatedStorage("FrequencyRangeMax", TextBoxFrequencyRangeMax.Text); IsolatedStorageManager.WriteToIsolatedStorage("DisplayLegend", CheckBoxDisplayLegend.IsChecked.GetValueOrDefault()); RetrieveSettingsFromIsolatedStorage(); PopulateSettings(); PopupSettings.IsOpen = false; CommonFunctions.LoadUserControl(CommonFunctions.GetHeaderText("Graph Real-time Measurements"), typeof(InputStatusMonitorUserControl)); } private void ValidateSettingsBeforeWrite() { double frequencyRangeMin, frequencyRangeMax; double.TryParse(TextBoxFrequencyRangeMin.Text, out frequencyRangeMin); double.TryParse(TextBoxFrequencyRangeMax.Text, out frequencyRangeMax); if (frequencyRangeMin > frequencyRangeMax) { string temp = TextBoxFrequencyRangeMin.Text; TextBoxFrequencyRangeMin.Text = TextBoxFrequencyRangeMax.Text; TextBoxFrequencyRangeMax.Text = temp; } } private void PopulateSettings() { // Populate settings popup control. TextBoxLastSelectedMeasurements.Text = m_selectedSignalIDs; TextBoxNumberOFDataPointsToPlot.Text = m_numberOfDataPointsToPlot.ToString(); TextBoxDataResolution.Text = m_framesPerSecond.ToString(); TextBoxChartRefreshInterval.Text = m_chartRefreshInterval.ToString(); TextBoxStatisticDataRefreshInterval.Text = m_statisticsDataRefreshInterval.ToString(); TextBoxMeasurementDataRefreshInterval.Text = m_measurementsDataRefreshInterval.ToString(); TextBoxLagTime.Text = m_lagTime.ToString(); TextBoxLeadTime.Text = m_leadTime.ToString(); TextBoxFrequencyRangeMin.Text = m_frequencyRangeMin.ToString(); TextBoxFrequencyRangeMax.Text = m_frequencyRangeMax.ToString(); CheckBoxDisplayXAxis.IsChecked = m_displayXAxis; CheckBoxDisplayLegend.IsChecked = m_displayLegend; CheckBoxDisplayFrequencyYAxis.IsChecked = m_displayFrequencyYAxis; CheckBoxDisplayPhaseAngleYAxis.IsChecked = m_displayPhaseAngleYAxis; CheckBoxDisplayCurrentMagnitudeYAxis.IsChecked = m_displayCurrentYAxis; CheckBoxDisplayVoltageMagnitudeYAxis.IsChecked = m_displayVoltageYAxis; CheckBoxUseLocalClockAsRealTime.IsChecked = m_useLocalClockAsRealtime; CheckBoxIgnoreBadTimestamps.IsChecked = m_ignoreBadTimestamps; CheckBoxForceIPv4.IsChecked = m_forceIPv4; } private void ButtonManageSettings_Click(object sender, RoutedEventArgs e) { TextBoxLastSelectedMeasurements.Text = m_selectedSignalIDs; PopupSettings.Placement = PlacementMode.Center; PopupSettings.IsOpen = true; } private void ButtonCancel_Click(object sender, RoutedEventArgs e) { PopupSettings.IsOpen = false; } private void Expander_Collapsed(object sender, RoutedEventArgs e) { if (ExpanderHistoricalPlayback.IsVisible) DataGridStatistics.Height = 170; else DataGridStatistics.Height = 190; } private void Expander_Expanded(object sender, RoutedEventArgs e) { DataGridStatistics.Height = 110; } private void ButtonStartPlayback_Click(object sender, RoutedEventArgs e) { ModeMessage.Text = "Initializing historical playback..."; ChartPlotterDynamic.Background = new SolidColorBrush(Color.FromArgb(255, 225, 225, 225)); m_timeStampList = new ConcurrentQueue(); m_historicalPlayback = true; m_waitingForData = false; Subscribe(); } private void ButtonReturnToRealtime_Click(object sender, RoutedEventArgs e) { ModeMessage.Text = "Real-time"; ChartPlotterDynamic.Background = new SolidColorBrush(Color.FromArgb(255, 255, 255, 255)); m_timeStampList = new ConcurrentQueue(); m_historicalPlayback = false; m_waitingForData = false; Subscribe(); } private void TextBoxProcessInterval_TextChanged(object sender, TextChangedEventArgs e) { // Allow dynamic updates to processing interval during operation if (m_historicalPlayback && !m_waitingForData && m_subscriber != null && m_subscriber.IsConnected) { int processingInterval; if (int.TryParse(TextBoxProcessInterval.Text, out processingInterval)) m_subscriber.ProcessingInterval = processingInterval; } } private void ButtonRemove_Click(object sender, RoutedEventArgs e) { try { ((RealTimeMeasurement)((Button)sender).DataContext).Selected = false; } catch { } } private void ButtonStatusBitReference_Click(object sender, RoutedEventArgs e)//svk_7/23/12 { ShowuserStatusDoc.Placement = PlacementMode.Center; ShowuserStatusDoc.IsOpen = true; } private void ButtonCancelShowuserStatusDoc_Click(object sender, RoutedEventArgs e) { ShowuserStatusDoc.IsOpen = false; } #endregion } }