//******************************************************************************************************
// OutputStreamDevicePhasor.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 - Aniket Salver
// Generated original version of source code.
// 09/16/2011 - Mehulbhai P Thakkar
// Fixed issue in Load() method.
// 09/19/2011 - Mehulbhai P Thakkar
// Added OnPropertyChanged() on all properties to reflect changes on UI.
// Fixed Load() and GetLookupList() static methods.
// 06/27/2012- Vijay Sukhavasi
// Modified Delete() to delete measurements associated with phasor
// 08/15/2012 - Aniket Salver
// Added paging and sorting technique.
//
//******************************************************************************************************
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows;
using GSF.Data;
using GSF.TimeSeries.UI;
namespace GSF.PhasorProtocols.UI.DataModels
{
///
/// Represents a record of information as defined in the database.
///
public class OutputStreamDevicePhasor : DataModelBase
{
#region [ Members ]
private Guid m_nodeID;
private int m_outputStreamDeviceID;
private int m_id;
private string m_label;
private string m_type;
private string m_phase;
private int m_scalingValue;
private int m_loadOrder;
#endregion
#region [ Properties ]
///
/// Gets or sets NodeID.
///
[Required(ErrorMessage = "OutputStreamDevicePhasor NodeID is a required field, please provide value.")]
public Guid NodeID
{
get => m_nodeID;
set
{
m_nodeID = value;
OnPropertyChanged("NodeID");
}
}
///
/// Gets or sets OutputStreamDeviceID.
///
[Required(ErrorMessage = "OutputStreamDevicePhasor OutputStreamDeviceID is a required field, please provide value.")]
public int OutputStreamDeviceID
{
get => m_outputStreamDeviceID;
set
{
m_outputStreamDeviceID = value;
OnPropertyChanged("OutputStreamDeviceID");
}
}
///
/// Gets or sets ID.
///
// Field is populated by database via auto-increment and has no screen interaction, so no validation attributes are applied
[Required(ErrorMessage = "OutputStreamDevicePhasor ID is a required field, please provide value.")]
public int ID
{
get => m_id;
set
{
m_id = value;
OnPropertyChanged("ID");
}
}
///
/// Gets or sets Label.
///
[Required(ErrorMessage = "OutputStreamDevicePhasor Label is a required field, please provide value.")]
[StringLength(200, ErrorMessage = "OutputStreamDevicePhasor Label cannot exceed 200 characters.")]
public string Label
{
get => m_label;
set
{
m_label = value;
OnPropertyChanged("Label");
}
}
///
/// Gets or sets Type.
///
[Required(ErrorMessage = "OutputStreamDevicePhasor Type is a required field, please provide value.")]
[DefaultValue("V")]
public string Type
{
get => m_type;
set
{
m_type = value;
OnPropertyChanged("Type");
}
}
///
/// Gets or sets Phase.
///
[Required(ErrorMessage = "OutputStreamDevicePhasor Phase is a required field, please provide value.")]
[DefaultValue("+")]
public string Phase
{
get => m_phase;
set
{
m_phase = value;
OnPropertyChanged("Phase");
}
}
///
/// Gets or sets ScalingValue .
///
[Required(ErrorMessage = "OutputStreamDevicePhasor ScalingValue is a required field, please provide value.")]
public int ScalingValue
{
get => m_scalingValue;
set
{
m_scalingValue = value;
OnPropertyChanged("ScalingValue");
}
}
///
/// Gets or sets LoadOrder .
///
[Required(ErrorMessage = "OutputStreamDevicePhasor LoadOrder is a required field, please provide value.")]
public int LoadOrder
{
get => m_loadOrder;
set
{
m_loadOrder = value;
OnPropertyChanged("LoadOrder");
}
}
///
/// Gets or sets PhasorType.
///
public string PhasorType { get; private set; }
///
/// Gets or sets PhaseType.
///
public string PhaseType { get; private set; }
///
/// Gets or sets when the current was created.
///
// Field is populated by trigger and has no screen interaction, so no validation attributes are applied
public DateTime CreatedOn { get; set; }
///
/// Gets or sets who the current was created by.
///
// Field is populated by trigger and has no screen interaction, so no validation attributes are applied
public string CreatedBy { get; set; }
///
/// Gets or sets when the current was updated.
///
// Field is populated by trigger and has no screen interaction, so no validation attributes are applied
public DateTime UpdatedOn { get; set; }
///
/// Gets or sets who the current was updated by.
///
// Field is populated by trigger and has no screen interaction, so no validation attributes are applied
public string UpdatedBy { get; set; }
#endregion
#region [ Static ]
// Static Methods
///
/// LoadKeys information as an style list.
///
/// to connection to database.
/// ID of the output stream device to filter data.
/// The field to sort by.
/// ASC or DESC for ascending or descending respectively.
/// Collection of .
public static IList LoadKeys(AdoDataConnection database, int outputStreamDeviceID, string sortMember, string sortDirection)
{
bool createdConnection = false;
try
{
createdConnection = CreateConnection(ref database);
IList outputStreamDevicePhasorList = new List();
string sortClause = string.Empty;
if (!string.IsNullOrEmpty(sortMember))
sortClause = $"ORDER BY {sortMember} {sortDirection}";
DataTable OutputStreamDevicePhasorTable = database.Connection.RetrieveData(database.AdapterType, $"SELECT ID FROM OutputStreamDevicePhasor WHERE OutputStreamDeviceID = {outputStreamDeviceID} {sortClause}");
foreach (DataRow row in OutputStreamDevicePhasorTable.Rows)
outputStreamDevicePhasorList.Add((row.ConvertField("ID")));
return outputStreamDevicePhasorList;
}
finally
{
if (createdConnection)
database?.Dispose();
}
}
///
/// Loads information as an style list.
///
/// to connection to database.
/// Keys of the measurements to be loaded from the database
/// Collection of .
public static ObservableCollection Load(AdoDataConnection database, IList keys)
{
bool createdConnection = false;
try
{
createdConnection = CreateConnection(ref database);
OutputStreamDevicePhasor[] outputStreamDevicePhasorList = null;
if (keys is not null && keys.Count > 0)
{
string commaSeparatedKeys = keys.Select(key => "" + key.ToString() + "").Aggregate((str1, str2) => str1 + "," + str2);
string query = database.ParameterizedQueryString("SELECT NodeID, OutputStreamDeviceID, ID, Label, Type, Phase, ScalingValue, LoadOrder " + $"FROM OutputStreamDevicePhasor WHERE ID IN ({commaSeparatedKeys})");
DataTable outputStreamDevicePhasorTable = database.Connection.RetrieveData(database.AdapterType, query);
outputStreamDevicePhasorList = new OutputStreamDevicePhasor[outputStreamDevicePhasorTable.Rows.Count];
foreach (DataRow row in outputStreamDevicePhasorTable.Rows)
{
int id = row.ConvertField("ID");
outputStreamDevicePhasorList[keys.IndexOf(id)] = new OutputStreamDevicePhasor()
{
NodeID = database.Guid(row, "NodeID"),
OutputStreamDeviceID = row.ConvertField("OutputStreamDeviceID"),
ID = id,
Label = row.Field("Label"),
Type = row.Field("Type"),
Phase = row.Field("Phase"),
ScalingValue = row.ConvertField("ScalingValue"),
LoadOrder = row.ConvertField("LoadOrder"),
PhaseType = row.Field("Phase") == "+" ? "Positive Sequence" : row.Field("Phase") == "-" ? "Negative Sequence" :
row.Field("Phase") == "0" ? "Zero Sequence" : row.Field("Phase") == "A" ? "Phase A" :
row.Field("Phase") == "B" ? "Phase B" : "Phase C",
PhasorType = row.Field("Type") == "V" ? "Voltage" : "Current"
};
}
}
return new ObservableCollection(outputStreamDevicePhasorList ?? new OutputStreamDevicePhasor[0]);
}
finally
{
if (createdConnection)
database?.Dispose();
}
}
///
/// Gets a style list of information.
///
/// to connection to database.
/// ID of the output stream device to filter data.
/// Indicates if selection on UI is optional for this collection.
/// containing ID and Label of OutputStreamDevicePhasors defined in the database.
public static Dictionary GetLookupList(AdoDataConnection database, int outputStreamDeviceID, bool isOptional = false)
{
bool createdConnection = false;
try
{
createdConnection = CreateConnection(ref database);
Dictionary OutputStreamDevicePhasorList = new();
if (isOptional)
OutputStreamDevicePhasorList.Add(0, "Select OutputStreamDevicePhasor");
string query = database.ParameterizedQueryString("SELECT ID, Label FROM OutputStreamDevicePhasor " +
"WHERE OutputStreamDeviceID = {0} ORDER BY LoadOrder", "outputStreamDeviceID");
DataTable OutputStreamDevicePhasorTable = database.Connection.RetrieveData(database.AdapterType, query, DefaultTimeout, outputStreamDeviceID);
foreach (DataRow row in OutputStreamDevicePhasorTable.Rows)
OutputStreamDevicePhasorList[row.ConvertField("ID")] = row.Field("Label");
return OutputStreamDevicePhasorList;
}
finally
{
if (createdConnection)
database?.Dispose();
}
}
///
/// Saves information to database.
///
/// to connection to database.
/// Information about .
/// String, for display use, indicating success.
public static string Save(AdoDataConnection database, OutputStreamDevicePhasor outputStreamDevicePhasor)
{
bool createdConnection = false;
try
{
createdConnection = CreateConnection(ref database);
string query;
if (outputStreamDevicePhasor.ID == 0)
{
query = database.ParameterizedQueryString("INSERT INTO OutputStreamDevicePhasor (NodeID, OutputStreamDeviceID, Label, Type, Phase, ScalingValue, " +
"LoadOrder, UpdatedBy, UpdatedOn, CreatedBy, CreatedOn) VALUES ({0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10})", "nodeID",
"outputStreamDeviceID", "label", "type", "phase", "scalingValue", "loadOrder", "updatedBy", "updatedOn", "createdBy", "createdOn");
database.Connection.ExecuteNonQuery(query, DefaultTimeout, database.CurrentNodeID(), outputStreamDevicePhasor.OutputStreamDeviceID,
outputStreamDevicePhasor.Label, outputStreamDevicePhasor.Type, outputStreamDevicePhasor.Phase, outputStreamDevicePhasor.ScalingValue,
outputStreamDevicePhasor.LoadOrder, CommonFunctions.CurrentUser, database.UtcNow, CommonFunctions.CurrentUser, database.UtcNow);
// PhasorType, PhaseType @phasorName, @phaseType, OutputStreamDevicePhasor.PhasorType, OutputStreamDevicePhasor.PhaseType
}
else
{
query = "UPDATE OutputStreamDevicePhasor SET NodeID = {0}, OutputStreamDeviceID = {1}, Label = {2}, Type = {3}, " +
"Phase = {4}, ScalingValue = {5}, LoadOrder = {6}, UpdatedBy = {7}, UpdatedOn = {8} WHERE ID = {9}";
database.ExecuteNonQuery(DefaultTimeout, query, outputStreamDevicePhasor.NodeID, outputStreamDevicePhasor.OutputStreamDeviceID,
outputStreamDevicePhasor.Label, outputStreamDevicePhasor.Type, outputStreamDevicePhasor.Phase, outputStreamDevicePhasor.ScalingValue,
outputStreamDevicePhasor.LoadOrder, CommonFunctions.CurrentUser, database.UtcNow, outputStreamDevicePhasor.ID);
//PhasorType = @typeName, PhaseType = @PhaseType" OutputStreamDevicePhasor.PhasorType, OutputStreamDevicePhasor.PhaseType,
}
return "OutputStreamDevicePhasor information saved successfully";
}
finally
{
if (createdConnection)
database?.Dispose();
}
}
///
/// Deletes specified record and its associated measurements from database.
///
/// to connection to database.
/// ID of the record to be deleted.
/// String, for display use, indicating success.
public static string Delete(AdoDataConnection database, int outputStreamDevicePhasorID)
{
bool createdConnection = false;
string nextAngleSignalReference = string.Empty;
string nextMagnitudeSignalReference = string.Empty;
string lastAffectedMeasurementsMessage = string.Empty;
try
{
createdConnection = CreateConnection(ref database);
// Setup current user context for any delete triggers
CommonFunctions.SetCurrentUserContext(database);
GetDeleteMeasurementDetails(database, outputStreamDevicePhasorID, out string angleSignalReference, out string magnitudeSignalReference, out int adapterID, out int outputStreamDeviceID);
// Delete angle/magnitude of measurements if they exist
database.Connection.ExecuteNonQuery(database.ParameterizedQueryString("DELETE FROM OutputStreamMeasurement WHERE SignalReference = {0} AND AdapterID = {1}", "signalReference", "adapterID"), DefaultTimeout, angleSignalReference, adapterID);
database.Connection.ExecuteNonQuery(database.ParameterizedQueryString("DELETE FROM OutputStreamMeasurement WHERE SignalReference = {0} AND AdapterID = {1}", "signalReference", "adapterID"), DefaultTimeout, magnitudeSignalReference, adapterID);
int presentDevicePhasorCount = Convert.ToInt32(database.Connection.ExecuteScalar(database.ParameterizedQueryString("SELECT COUNT(*) FROM OutputStreamDevicePhasor WHERE OutputStreamDeviceID = {0}", "outputStreamDeviceID"), DefaultTimeout, outputStreamDeviceID));
// Using signal reference angle/mag deleted build the next signal reference (increment by 1)
int.TryParse(Regex.Match(magnitudeSignalReference, @"\d+$").Value, out int deletedSignalReferenceIndex);
string angleSignalReferenceBase = Regex.Replace(angleSignalReference, @"\d+$", "");
string magnitudeSignalReferenceBase = Regex.Replace(magnitudeSignalReference, @"\d+$", "");
for (int i = deletedSignalReferenceIndex; i < presentDevicePhasorCount; i++)
{
// We will be modifying the measurements with signal reference index i+1 to have signal reference index i.
string previousAngleSignalReference = $"{angleSignalReferenceBase}{i}";
nextAngleSignalReference = $"{angleSignalReferenceBase}{i + 1}";
string previousMagnitudeSignalReference = $"{magnitudeSignalReferenceBase}{i}";
nextMagnitudeSignalReference = $"{magnitudeSignalReferenceBase}{i + 1}";
// For angle...
// Obtain details of measurements after the deleted measurements, then modify the signal reference (decrement by 1) and put it back
OutputStreamMeasurement outputStreamMeasurement = GetOutputMeasurementDetails(database, nextAngleSignalReference, adapterID);
outputStreamMeasurement.SignalReference = previousAngleSignalReference;
OutputStreamMeasurement.Save(database, outputStreamMeasurement);
// For magnitude...
outputStreamMeasurement = GetOutputMeasurementDetails(database, nextMagnitudeSignalReference, adapterID);
outputStreamMeasurement.SignalReference = previousMagnitudeSignalReference;
OutputStreamMeasurement.Save(database, outputStreamMeasurement);
}
database.Connection.ExecuteNonQuery(database.ParameterizedQueryString("DELETE FROM OutputStreamDevicePhasor WHERE ID = {0}", "outputStreamDevicePhasorID"), DefaultTimeout, outputStreamDevicePhasorID);
return "OutputStreamDevicePhasor deleted successfully";
}
catch (Exception ex)
{
if (!string.IsNullOrEmpty(nextMagnitudeSignalReference))
lastAffectedMeasurementsMessage = $"{Environment.NewLine}(Last affected measurements: {nextMagnitudeSignalReference} {nextAngleSignalReference})";
CommonFunctions.LogException(database, "OutputStreamDevicePhasor.Delete", ex);
MessageBoxResult dialogResult = MessageBox.Show($"Could not delete or modify measurements.{Environment.NewLine}Do you still wish to delete this Phasor?{lastAffectedMeasurementsMessage}", "", MessageBoxButton.YesNo);
if (dialogResult == MessageBoxResult.Yes)
{
database.Connection.ExecuteNonQuery(database.ParameterizedQueryString("DELETE FROM OutputStreamDevicePhasor WHERE ID = {0}", "outputStreamDevicePhasorID"), DefaultTimeout, outputStreamDevicePhasorID);
return "OutputStreamDevicePhasor deleted successfully but failed to modify all measurements ";
}
else
{
Exception exception = ex.InnerException ?? ex;
return $"Delete OutputStreamDevicePhasor was unsuccessful: {exception.Message}";
}
}
finally
{
if (createdConnection)
database?.Dispose();
}
}
private static OutputStreamMeasurement GetOutputMeasurementDetails(AdoDataConnection database, string signalReference, int adapterID)
{
bool createdConnection = false;
try
{
createdConnection = CreateConnection(ref database);
string query = database.ParameterizedQueryString("SELECT * FROM OutputStreamMeasurement WHERE SignalReference = {0} AND AdapterID = {1}", "signalReference", "adapterID");
DataRow row = database.Connection.RetrieveData(database.AdapterType, query, DefaultTimeout, signalReference, adapterID).Rows[0];
OutputStreamMeasurement outputStreamMeasurement = new()
{
NodeID = row.ConvertField("NodeID"),
AdapterID = row.Field("AdapterID"),
ID = row.Field("ID"),
HistorianID = row.Field("HistorianID"),
PointID = row.Field("PointID"),
SignalReference = row.ConvertField("SignalReference"),
CreatedOn = row.ConvertField("CreatedOn"),
CreatedBy = row.Field("CreatedBy"),
UpdatedOn = row.ConvertField("UpdatedOn"),
UpdatedBy = row.Field("UpdatedBy")
};
return outputStreamMeasurement;
}
catch (Exception ex)
{
CommonFunctions.LogException(database, "OutputStreamDevicePhasor.GetOutputMeasurementDetails", ex);
throw;
}
finally
{
if (createdConnection)
database?.Dispose();
}
}
private static void GetDeleteMeasurementDetails(AdoDataConnection database, int outputStreamDevicePhasorID, out string angleSignalReference, out string magnitudeSignalReference, out int adapterID, out int outputStreamDeviceID)
{
const string outputPhasorFormat = "SELECT Label, OutputStreamDeviceID FROM OutputStreamDevicePhasor WHERE ID = {0}";
const string outputDeviceFormat = "SELECT Acronym, AdapterID FROM OutputStreamDevice WHERE ID = {0}";
const string measurementDetailFormat = "SELECT PointTag FROM MeasurementDetail WHERE DeviceAcronym = '{0}' AND PhasorLabel = '{1}' AND SignalTypeSuffix = '{2}'";
const string outputMeasurementDetailFormat = "SELECT SignalReference FROM OutputStreamMeasurementDetail WHERE SourcePointTag = '{0}'";
bool createdConnection = false;
try
{
createdConnection = CreateConnection(ref database);
DataRow outputPhasorRecord = database.Connection.RetrieveData(database.AdapterType, string.Format(outputPhasorFormat, outputStreamDevicePhasorID)).Rows[0];
string labelName = outputPhasorRecord.Field("Label");
outputStreamDeviceID = outputPhasorRecord.ConvertField("OutputStreamDeviceID");
DataRow outputDeviceRecord = database.Connection.RetrieveData(database.AdapterType, string.Format(outputDeviceFormat, outputStreamDeviceID)).Rows[0];
string deviceName = outputDeviceRecord.Field("Acronym");
adapterID = outputDeviceRecord.ConvertField("AdapterID");
string anglePointTag = database.Connection.ExecuteScalar(string.Format(measurementDetailFormat, deviceName, labelName, "PA")).ToNonNullString();
angleSignalReference = database.Connection.ExecuteScalar(string.Format(outputMeasurementDetailFormat, anglePointTag)).ToNonNullString();
string magnitudePointTag = database.Connection.ExecuteScalar(string.Format(measurementDetailFormat, deviceName, labelName, "PM")).ToNonNullString();
magnitudeSignalReference = database.Connection.ExecuteScalar(string.Format(outputMeasurementDetailFormat, magnitudePointTag)).ToNonNullString();
}
catch (Exception ex)
{
CommonFunctions.LogException(database, "OutputStreamDevicePhasor.GetDeleteMeasurementDetails", ex);
throw;
}
finally
{
if (createdConnection)
database?.Dispose();
}
}
#endregion
}
}