//****************************************************************************************************** // Subscribers.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: // ---------------------------------------------------------------------------------------------------- // 05/20/2011 - Mehulbhai P Thakkar // Generated original version of source code. // 05/20/2011 - Mehulbhai P Thakkar // Added commands and other logic to add or remove measurements and measurement groups. // //****************************************************************************************************** using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Data; using System.Linq; using System.Windows; using System.Windows.Input; using System.Windows.Threading; using GSF.Collections; using GSF.Data; using GSF.TimeSeries.Transport.UI.DataModels; using GSF.TimeSeries.UI; using GSF.TimeSeries.UI.Commands; using DataModelMeasurement = GSF.TimeSeries.UI.DataModels.Measurement; namespace GSF.TimeSeries.Transport.UI.ViewModels { /// /// Class to hold bindable collection and current selection information for UI. /// internal class Subscribers : PagedViewModelBase { #region [ Members ] // Fields private DataSet m_subscriberPermissionsDataSet; private ICollection m_currentAvailableMeasurementsPage; private object m_availableMeasurementsLock; private RelayCommand m_addAllowedMeasurementGroupCommand; private RelayCommand m_removeAllowedMeasurementGroupCommand; private RelayCommand m_addDeniedMeasurementGroupCommand; private RelayCommand m_removeDeniedMeasurementGroupCommand; private DispatcherTimer m_refreshTimer; private readonly SubscriberStatusQuery m_subscriberStatusQuery; private readonly object m_subscriberStatusQueryLock; private readonly List m_subscriberIDs; private Func m_rightsLookup; private SecurityMode m_securityMode; private byte[] m_remoteCertificateData; // Delegates /// /// Method signature for a function which handles MeasurementsAdded event. /// /// Source of an event. /// Event arguments. public delegate void OnMeasurementsAdded(object sender, RoutedEventArgs e); // Event /// /// Event to notify subscribers that measurements have been added to the group. /// public event OnMeasurementsAdded MeasurementsAdded; #endregion #region [ Constructors ] /// /// Creates an instance of class. /// /// Integer value to determine number of items per page. /// Boolean value to determine is user changes should be saved automatically. public Subscribers(int itemsPerPage, bool autoSave = true) : base(itemsPerPage, autoSave) { m_subscriberIDs = new List(); m_subscriberStatusQueryLock = new object(); m_subscriberStatusQuery = new SubscriberStatusQuery(); m_subscriberStatusQuery.SubscriberStatuses += m_subscriberStatusQuery_SubscriberStatuses; PropertyChanged += PropertyChangedHandler; } #endregion #region [ Properties ] /// /// Gets flag that determines if is a new record. /// public override bool IsNewRecord { get { return CurrentItem.ID == Guid.Empty; } } /// /// Gets or sets measurements with Subscribed flag set to true. /// public ICollection CurrentAvailableMeasurementsPage { get { lock (m_availableMeasurementsLock) { // Return a copy of the measurements since we can't request consumer to lock the collection return new ObservableCollection(m_currentAvailableMeasurementsPage ?? new DataModelMeasurement[0]); } } set { lock (m_availableMeasurementsLock) { m_currentAvailableMeasurementsPage = value; } UpdateEffectivePermissions(); } } /// /// Gets to add allowed measurement groups. /// public ICommand AddAllowedMeasurementGroupCommand { get { if (m_addAllowedMeasurementGroupCommand == null) m_addAllowedMeasurementGroupCommand = new RelayCommand(AddAllowedMeasurementGroup, (param) => CanSave); return m_addAllowedMeasurementGroupCommand; } } /// /// Gets to remove allowed measurement groups. /// public ICommand RemoveAllowedMeasurementGroupCommand { get { if (m_removeAllowedMeasurementGroupCommand == null) m_removeAllowedMeasurementGroupCommand = new RelayCommand(RemoveAllowedMeasurementGroup, (param) => CanSave); return m_removeAllowedMeasurementGroupCommand; } } /// /// Gets to add denied measurement groups. /// public ICommand AddDeniedMeasurementGroupCommand { get { if (m_addDeniedMeasurementGroupCommand == null) m_addDeniedMeasurementGroupCommand = new RelayCommand(AddDeniedMeasurementGroup, (param) => CanSave); return m_addDeniedMeasurementGroupCommand; } } /// /// Gets to remove denied measurement groups. /// public ICommand RemoveDeniedMeasurementGroupCommand { get { if (m_removeDeniedMeasurementGroupCommand == null) m_removeDeniedMeasurementGroupCommand = new RelayCommand(RemoveDeniedMeasurementGroup, (param) => CanSave); return m_removeDeniedMeasurementGroupCommand; } } /// /// Gets or sets the security mode used by the data Subscriber. /// public SecurityMode SecurityMode { get { return m_securityMode; } set { m_securityMode = value; OnPropertyChanged("SecurityMode"); OnPropertyChanged("TransportLayerSecuritySelected"); OnPropertyChanged("GatewaySecuritySelected"); } } /// /// Gets the flag that indicates whether the user has selected TLS as the security mode. /// public bool TransportLayerSecuritySelected { get { return m_securityMode == SecurityMode.TLS; } } /// /// Gets the flag that indicates whether the user has selected Gateway security. /// public bool GatewaySecuritySelected { get { return m_securityMode == SecurityMode.Gateway; } } /// /// Gets or sets the remote certificate file data that was imported from the subscription request file. /// public byte[] RemoteCertificateData { get { return m_remoteCertificateData; } set { m_remoteCertificateData = value; } } #endregion #region [ Methods ] private void m_subscriberStatusQuery_SubscriberStatuses(object sender, EventArgs>> e) { Dictionary> subscriberStatuses = e.Argument; lock (m_subscriberStatusQueryLock) { foreach (Subscriber subscriber in ItemsSource) { Tuple status; if (subscriberStatuses.TryGetValue(subscriber.ID, out status)) { subscriber.StatusColor = status.Item1 ? "green" : "red"; subscriber.Version = status.Item2; } } } //OnPropertyChanged("ItemsSource"); } /// /// Start refresh timer. /// public void StartTimer() { try { if (m_refreshTimer == null) { m_refreshTimer = new DispatcherTimer(); m_refreshTimer.Interval = TimeSpan.FromSeconds(10); m_refreshTimer.Tick += m_refreshTimer_Tick; } foreach (Subscriber subscriber in ItemsSource) m_subscriberIDs.Add(subscriber.ID); m_subscriberStatusQuery.RequestSubscriberStatus(m_subscriberIDs); m_refreshTimer.Start(); } catch (Exception ex) { Popup("Failed to start data refresh timer. Please reload page." + Environment.NewLine + ex.Message, "Refresh Data", MessageBoxImage.Error); } } /// /// Stop refresh timer. /// public void StopTimer() { try { if (m_refreshTimer != null) m_refreshTimer.Stop(); } finally { m_refreshTimer = null; } } private void m_refreshTimer_Tick(object sender, EventArgs e) { lock (m_subscriberStatusQueryLock) { m_subscriberStatusQuery.RequestSubscriberStatus(m_subscriberIDs); } } /// /// Gets the primary key value of the . /// /// The primary key value of the . public override Guid GetCurrentItemKey() { return CurrentItem.ID; } /// /// Gets the string based named identifier of the . /// /// The string based named identifier of the . public override string GetCurrentItemName() { return CurrentItem.Name; } /// /// Initialization to be done before the initial call to . /// public override void Initialize() { m_availableMeasurementsLock = new object(); base.Initialize(); LoadPermissionsDataSet(); } /// /// Saves the record for the associated . /// public override void Save() { base.Save(); LoadPermissionsDataSet(); } /// /// Handles . /// /// Collection of measurements to be allowed. public void AddAllowedMeasurements(ICollection measurementIDs) { int currentCount = Subscriber.GetMeasurementCount(null, CurrentItem.ID, true); int addedCount = Subscriber.AddMeasurements(null, CurrentItem.ID, measurementIDs, true); if (addedCount > 0) { DisplayStatusMessage("Measurements successfully added to allowed measurements list for subscriber"); // Log changes to event log CommonFunctions.LogEvent(string.Format("Allowed {0} new measurements in addition to the {1} already allowed for subscriber {2} [{3}] for a total of {4} now allowed. See audit log for complete details.\r\n\r\nAction taken to add the following measurements: {5}", addedCount, currentCount, CurrentItem.Name.ToNonNullNorWhiteSpace("Subscriber"), CurrentItem.Acronym, currentCount + addedCount, ShortFormattedList(measurementIDs.Select(GetMeasurementName))), 0); } else { Popup("Selected measurements already exist or no measurements were selected.", "Allow Measurements", MessageBoxImage.Information); } MeasurementsAdded?.Invoke(this, null); try { CommonFunctions.SendCommandToService("ReloadConfig"); } catch (Exception ex) { CommonFunctions.LogException(null, "", ex); } LoadPermissionsDataSet(); } /// /// Handles . /// /// Collection of measurements to be disallowed. public void RemoveAllowedMeasurements(ICollection measurementIDs) { int currentCount = Subscriber.GetMeasurementCount(null, CurrentItem.ID, true); int removedCount = Subscriber.RemoveMeasurements(null, CurrentItem.ID, measurementIDs); DisplayStatusMessage("Measurements successfully removed from allowed measurements list for subscriber"); // Log changes to event log CommonFunctions.LogEvent(string.Format("Removed {0} measurements from the {1} currently allowed for subscriber {2} [{3}] for a total of {4} now allowed. See audit log for complete details.\r\n\r\nAction taken to remove the following measurements: {5}.", removedCount, currentCount, CurrentItem.Name.ToNonNullNorWhiteSpace("Subscriber"), CurrentItem.Acronym, currentCount - removedCount, ShortFormattedList(measurementIDs.Select(GetMeasurementName))), 0); try { CommonFunctions.SendCommandToService("ReloadConfig"); } catch (Exception ex) { CommonFunctions.LogException(null, "", ex); } LoadPermissionsDataSet(); } /// /// Handles . /// /// Collection of measurements to be denied. public void AddDeniedMeasurements(ICollection measurementIDs) { int currentCount = Subscriber.GetMeasurementCount(null, CurrentItem.ID, false); int addedCount = Subscriber.AddMeasurements(null, CurrentItem.ID, measurementIDs, false); if (addedCount > 0) { DisplayStatusMessage("Measurements successfully added to denied measurements list for subscriber"); // Log changes to event log CommonFunctions.LogEvent(string.Format("Denied {0} new measurements in addition to the {1} already denied for subscriber {2} [{3}] for a total of {4} now denied. See audit log for complete details.\r\n\r\nAction taken to add the following measurements: {5}", addedCount, currentCount, CurrentItem.Name.ToNonNullNorWhiteSpace("Subscriber"), CurrentItem.Acronym, currentCount + addedCount, ShortFormattedList(measurementIDs.Select(GetMeasurementName))), 0); } else { Popup("Selected measurements already exist or no measurements were selected.", "Deny Measurements", MessageBoxImage.Information); } MeasurementsAdded?.Invoke(this, null); try { CommonFunctions.SendCommandToService("ReloadConfig"); } catch (Exception ex) { CommonFunctions.LogException(null, "", ex); } LoadPermissionsDataSet(); } /// /// Handles /// /// Collection of measurements to be removed. public void RemoveDeniedMeasurements(ICollection measurementIDs) { int currentCount = Subscriber.GetMeasurementCount(null, CurrentItem.ID, false); int removedCount = Subscriber.RemoveMeasurements(null, CurrentItem.ID, measurementIDs); DisplayStatusMessage("Measurements successfully removed from allowed measurements list for subscriber"); // Log changes to event log CommonFunctions.LogEvent(string.Format("Removed {0} measurements from the {1} currently denied for subscriber {2} [{3}] for a total of {4} now denied. See audit log for complete details.\r\n\r\nAction taken to remove the following measurements: {5}.", removedCount, currentCount, CurrentItem.Name.ToNonNullNorWhiteSpace("Subscriber"), CurrentItem.Acronym, currentCount - removedCount, ShortFormattedList(measurementIDs.Select(GetMeasurementName))), 0); try { CommonFunctions.SendCommandToService("ReloadConfig"); } catch (Exception ex) { CommonFunctions.LogException(null, "", ex); } LoadPermissionsDataSet(); } /// /// Determines if subscriber has rights to specified . /// /// signal ID to lookup. /// true if subscriber has rights to specified ; otherwise false. public bool SubscriberHasRights(Guid signalID) { return m_rightsLookup(signalID); } /// /// Handles . /// /// Collection of measurement groups to be added. private void AddAllowedMeasurementGroup(object parameter) { ObservableCollection items = (ObservableCollection)parameter; if (items != null && items.Count > 0) { List measurementGroupIDs = new List(); foreach (object item in items) measurementGroupIDs.Add(((KeyValuePair)item).Key); int currentCount = Subscriber.GetGroupCount(null, CurrentItem.ID, true); int addedCount = Subscriber.AddMeasurementGroups(null, CurrentItem.ID, measurementGroupIDs, true); DisplayStatusMessage("Measurement groups successfully added to allowed measurement groups list for subscriber"); // Log changes to event log CommonFunctions.LogEvent(string.Format("Allowed {0} new measurement groups in addition to the {1} already allowed for subscriber {2} [{3}] for a total of {4} now allowed. See audit log for complete details.\r\n\r\nAction taken to add the following measurement groups: {5}", addedCount, currentCount, CurrentItem.Name.ToNonNullNorWhiteSpace("Subscriber"), CurrentItem.Acronym, currentCount + addedCount, ShortFormattedList(measurementGroupIDs.Select(GetGroupName))), 0); CurrentItem.AllowedMeasurementGroups = Subscriber.GetAllowedMeasurementGroups(null, CurrentItem.ID); CurrentItem.AvailableMeasurementGroups = Subscriber.GetAvailableMeasurementGroups(null, CurrentItem.ID); try { CommonFunctions.SendCommandToService("ReloadConfig"); } catch (Exception ex) { CommonFunctions.LogException(null, "", ex); } LoadPermissionsDataSet(); } } /// /// Handles . /// /// Collection of measurement groups to be removed. private void RemoveAllowedMeasurementGroup(object parameter) { ObservableCollection items = (ObservableCollection)parameter; if (items != null && items.Count > 0) { List measurementGroupIDs = new List(); foreach (object item in items) measurementGroupIDs.Add(((KeyValuePair)item).Key); int currentCount = Subscriber.GetGroupCount(null, CurrentItem.ID, true); int removedCount = Subscriber.RemoveMeasurementGroups(null, CurrentItem.ID, measurementGroupIDs); DisplayStatusMessage("Measurement groups successfully removed from allowed measurement groups list for subscriber"); // Log changes to event log CommonFunctions.LogEvent(string.Format("Removed {0} measurement group from the {1} currently allowed for subscriber {2} [{3}] for a total of {4} now allowed. See audit log for complete details.\r\n\r\nAction taken to remove the following measurement groups: {5}.", removedCount, currentCount, CurrentItem.Name.ToNonNullNorWhiteSpace("Subscriber"), CurrentItem.Acronym, currentCount - removedCount, ShortFormattedList(measurementGroupIDs.Select(GetGroupName))), 0); CurrentItem.AllowedMeasurementGroups = Subscriber.GetAllowedMeasurementGroups(null, CurrentItem.ID); CurrentItem.AvailableMeasurementGroups = Subscriber.GetAvailableMeasurementGroups(null, CurrentItem.ID); try { CommonFunctions.SendCommandToService("ReloadConfig"); } catch (Exception ex) { CommonFunctions.LogException(null, "", ex); } LoadPermissionsDataSet(); } } /// /// Handles . /// /// Collection of measurement groups to be added. private void AddDeniedMeasurementGroup(object parameter) { ObservableCollection items = (ObservableCollection)parameter; if (items != null && items.Count > 0) { List measurementGroupIDs = new List(); foreach (object item in items) measurementGroupIDs.Add(((KeyValuePair)item).Key); int currentCount = Subscriber.GetGroupCount(null, CurrentItem.ID, false); int addedCount = Subscriber.AddMeasurementGroups(null, CurrentItem.ID, measurementGroupIDs, false); DisplayStatusMessage("Measurement groups successfully added to denied measurement groups list for subscriber"); // Log changes to event log CommonFunctions.LogEvent(string.Format("Denied {0} new measurement groups in addition to the {1} already denied for subscriber {2} [{3}] for a total of {4} now denied. See audit log for complete details.\r\n\r\nAction taken to add the following measurement groups: {5}", addedCount, currentCount, CurrentItem.Name.ToNonNullNorWhiteSpace("Subscriber"), CurrentItem.Acronym, currentCount + addedCount, ShortFormattedList(measurementGroupIDs.Select(GetGroupName))), 0); CurrentItem.AvailableMeasurementGroups = Subscriber.GetAvailableMeasurementGroups(null, CurrentItem.ID); CurrentItem.DeniedMeasurementGroups = Subscriber.GetDeniedMeasurementGroups(null, CurrentItem.ID); try { CommonFunctions.SendCommandToService("ReloadConfig"); } catch (Exception ex) { CommonFunctions.LogException(null, "", ex); } LoadPermissionsDataSet(); } } /// /// Handles . /// /// Collection of measurement groups to be removed. private void RemoveDeniedMeasurementGroup(object parameter) { ObservableCollection items = (ObservableCollection)parameter; if (items != null && items.Count > 0) { List measurementGroupIDs = new List(); foreach (object item in items) measurementGroupIDs.Add(((KeyValuePair)item).Key); int currentCount = Subscriber.GetGroupCount(null, CurrentItem.ID, false); int removedCount = Subscriber.RemoveMeasurementGroups(null, CurrentItem.ID, measurementGroupIDs); DisplayStatusMessage("Measurement groups successfully removed from denied measurement groups list for subscriber"); // Log changes to event log CommonFunctions.LogEvent(string.Format("Removed {0} measurement group from the {1} currently denied for subscriber {2} [{3}] for a total of {4} now denied. See audit log for complete details.\r\n\r\nAction taken to remove the following measurement groups: {5}.", removedCount, currentCount, CurrentItem.Name.ToNonNullNorWhiteSpace("Subscriber"), CurrentItem.Acronym, currentCount - removedCount, ShortFormattedList(measurementGroupIDs.Select(GetGroupName))), 0); CurrentItem.AvailableMeasurementGroups = Subscriber.GetAvailableMeasurementGroups(null, CurrentItem.ID); CurrentItem.DeniedMeasurementGroups = Subscriber.GetDeniedMeasurementGroups(null, CurrentItem.ID); try { CommonFunctions.SendCommandToService("ReloadConfig"); } catch (Exception ex) { CommonFunctions.LogException(null, "", ex); } LoadPermissionsDataSet(); } } private void PropertyChangedHandler(object sender, PropertyChangedEventArgs args) { if (args.PropertyName == "CurrentItem") { LoadPermissionsDataSet(); m_securityMode = IsNewRecord || !string.IsNullOrEmpty(CurrentItem.RemoteCertificateFile) ? SecurityMode.TLS : SecurityMode.Gateway; } } // Attempt to get measurement name from signal ID private string GetMeasurementName(Guid signalID) { DataModelMeasurement measurement = DataModelMeasurement.GetMeasurement(null, signalID); if ((object)measurement != null) return string.Format("{0} [{1}:{2}]", measurement.PointTag, measurement.HistorianAcronym, measurement.PointID); return signalID.ToString(); } // Attempt to get group name from group ID private string GetGroupName(int groupID) { return MeasurementGroup.GetMeasurementGroup(null, groupID)?.Name ?? "Group"; } // Convert enumerated list to a delimited list limiting the total enumerations private static string ShortFormattedList(IEnumerable items, int total = 10) { return items.Take(total).Select(item => item.ToString()).ToDelimitedString(", ") + (items.Count() > total ? ", ..." : ""); } // Determines the effective permissions of each of the measurements in the current page private void UpdateEffectivePermissions() { try { m_rightsLookup = !IsNewRecord ? new SubscriberRightsLookup(m_subscriberPermissionsDataSet, CurrentItem.ID).HasRightsFunc : (id => false); foreach (DataModelMeasurement measurement in CurrentAvailableMeasurementsPage) measurement.Selected = m_rightsLookup(measurement.SignalID); } catch (Exception ex) { CommonFunctions.LogException(null, "Subscriber Effective Permissions", ex); m_rightsLookup = id => false; } } // Loads the data set used to determine effective permissions private void LoadPermissionsDataSet() { Dictionary dataTableDefinitions = new Dictionary() { { "ActiveMeasurement", "ActiveMeasurements" }, { "Subscriber", "Subscribers" }, { "SubscriberMeasurement", "SubscriberMeasurements" }, { "SubscriberMeasurementGroup", "SubscriberMeasurementGroups" }, { "MeasurementGroup", "MeasurementGroups" }, { "MeasurementGroupMeasurement", "MeasurementGroupMeasurements" } }; DataTable dataTable; string queryFormat; string parameterizedQuery; m_subscriberPermissionsDataSet = new DataSet(); using (AdoDataConnection database = new AdoDataConnection(CommonFunctions.DefaultSettingsCategory)) { foreach (KeyValuePair definition in dataTableDefinitions) { queryFormat = string.Format("SELECT * FROM {0} WHERE NodeID = {{0}}", definition.Key); parameterizedQuery = database.ParameterizedQueryString(queryFormat, "nodeID"); dataTable = database.Connection.RetrieveData(database.AdapterType, parameterizedQuery, DataExtensions.DefaultTimeoutDuration, database.CurrentNodeID()); dataTable.TableName = definition.Value; dataTable.DataSet.Tables.Remove(dataTable); m_subscriberPermissionsDataSet.Tables.Add(dataTable); } } UpdateEffectivePermissions(); } #endregion } }