//******************************************************************************************************
// InputWizardDevices.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/24/2011 - Mehulbhai P Thakkar
// Generated original version of source code.
//
//******************************************************************************************************
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Soap;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Threading;
using GSF.Communication;
using GSF.ComponentModel.DataAnnotations;
using GSF.Data;
using GSF.PhasorProtocols.BPAPDCstream;
using GSF.PhasorProtocols.IEEEC37_118;
using GSF.PhasorProtocols.UI.DataModels;
using GSF.PhasorProtocols.UI.Modal;
using GSF.PhasorProtocols.UI.UserControls;
using GSF.ServiceProcess;
using GSF.TimeSeries.UI;
using GSF.TimeSeries.UI.Commands;
using GSF.TimeSeries.UI.DataModels;
using GSF.Units.EE;
using PhasorProtocolAdapters;
using Application = System.Windows.Application;
using Cursors = System.Windows.Input.Cursors;
using Phasor = GSF.PhasorProtocols.UI.DataModels.Phasor;
namespace GSF.PhasorProtocols.UI.ViewModels
{
///
/// Bindable class to hold collection of .
///
internal class InputWizardDevices : PagedViewModelBase, IDataErrorInfo
{
#region [ Members ]
// Fields
private RelayCommand m_launchWalkthroughCommand;
private RelayCommand m_browseConnectionFileCommand;
private RelayCommand m_buildConnectionStringCommand;
private RelayCommand m_buildAlternateCommandChannelCommand;
private RelayCommand m_browseIniFileCommand;
private RelayCommand m_browseConfigurationFileCommand;
private RelayCommand m_requestConfigurationCommand;
private RelayCommand m_saveConfigurationFileCommand;
private RelayCommand m_manualConfigurationCommand;
private RelayCommand m_cancelConfigurationRequestCommand;
private bool m_stepsEnabled;
private bool m_stepOneExpanded;
private bool m_stepTwoExpanded;
private bool m_stepThreeExpanded;
private string m_connectionString;
private string m_alternateCommandChannel;
private int m_accessID;
private int[] m_deviceIDs;
private string[] m_deviceAcronyms;
private int m_protocolID;
private string m_protocolAcronym;
private bool m_connectToConcentrator;
private int? m_pdcID;
private string m_pdcAcronym;
private string m_pdcName;
private int m_pdcVendorDeviceID;
private int m_pdcFrameRate;
private int m_companyID;
private int m_historianID;
private int m_interconnectionID;
private bool m_skipDisableRealTimeData;
private bool m_requestConfigurationPopupIsOpen;
private string m_requestConfigurationPopupText;
private bool m_requestConfigurationSuccess;
private readonly ObservableCollection m_protocolList;
private string m_connectionFileName;
private string m_configurationFileName;
private string m_iniFileName;
private string m_pdcMessage;
private string m_configurationSummary;
private IConfigurationFrame m_configurationFrame;
private string m_requestConfigurationError;
private object m_requestConfigurationAttachment;
private Device m_pdcDevice;
private bool m_newDeviceConfiguration;
private readonly Dispatcher m_dispatcher;
private readonly Dictionary m_errorMessages;
#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 InputWizardDevices(int itemsPerPage, bool autoSave = true)
: base(itemsPerPage, autoSave)
{
CompanyLookupList = Company.GetLookupList(null, true);
HistorianLookupList = TimeSeries.UI.DataModels.Historian.GetLookupList(null, true, false);
InterconnectionLookupList = Interconnection.GetLookupList(null, true);
ProtocolLookupList = Protocol.GetLookupList(null);
VendorDeviceLookupList = VendorDevice.GetLookupList(null, true);
m_protocolList = Protocol.Load(null);
PdcFrameRate = 30;
StepsEnabled = true;
StepOneExpanded = true;
NewDeviceConfiguration = true;
if (CompanyLookupList.Count > 0)
CompanyID = CompanyLookupList.First().Key;
if (HistorianLookupList.Count > 1)
HistorianID = HistorianLookupList.Skip(1).First().Key;
else if (HistorianLookupList.Count > 0)
HistorianID = HistorianLookupList.First().Key;
if (InterconnectionLookupList.Count > 0)
InterconnectionID = InterconnectionLookupList.First().Key;
if (ProtocolLookupList.Count > 0)
ProtocolID = ProtocolLookupList.First().Key;
if (VendorDeviceLookupList.Count > 0)
PdcVendorDeviceID = VendorDeviceLookupList.First().Key;
m_dispatcher = Dispatcher.CurrentDispatcher;
m_errorMessages = new Dictionary();
}
#endregion
#region [ Properties ]
///
/// Gets flag that determines if is a new record.
///
public override bool IsNewRecord => CurrentItem.ID == 0; // string.IsNullOrEmpty(CurrentItem.Acronym);
///
/// Gets or sets flag that determines whether the wizard steps are enabled.
///
public bool StepsEnabled
{
get => m_stepsEnabled;
private set
{
m_stepsEnabled = value;
OnPropertyChanged(nameof(StepsEnabled));
}
}
///
/// Gets or sets flag that determines whether step 1 is expanded.
///
public bool StepOneExpanded
{
get => m_stepOneExpanded;
set
{
m_stepOneExpanded = value;
OnPropertyChanged(nameof(StepOneExpanded));
}
}
///
/// Gets or sets flag that determines whether step 2 is expanded.
///
public bool StepTwoExpanded
{
get => m_stepTwoExpanded;
set
{
m_stepTwoExpanded = value;
OnPropertyChanged(nameof(StepTwoExpanded));
}
}
///
/// Gets or sets flag that determines whether step 3 is expanded.
///
public bool StepThreeExpanded
{
get => m_stepThreeExpanded;
set
{
m_stepThreeExpanded = value;
OnPropertyChanged(nameof(StepThreeExpanded));
}
}
///
/// Gets or sets name of the connection file used for configuration.
///
public string ConnectionFileName
{
get => m_connectionFileName;
set
{
m_connectionFileName = value;
OnPropertyChanged(nameof(ConnectionFileName));
}
}
///
/// Gets or sets name of the XML configuration file used for configuration.
///
public string ConfigurationFileName
{
get => m_configurationFileName;
set
{
m_configurationFileName = value;
OnPropertyChanged(nameof(ConfigurationFileName));
}
}
///
/// Gets or sets name of the INI file used for configuration.
///
public string IniFileName
{
get => m_iniFileName;
set
{
m_iniFileName = value;
OnPropertyChanged(nameof(IniFileName));
}
}
///
/// Gets type collection of defined in the database.
///
public Dictionary CompanyLookupList { get; }
///
/// Gets type collection of defined in the database.
///
public Dictionary HistorianLookupList { get; }
///
/// Gets type collection of defined in the database.
///
public Dictionary InterconnectionLookupList { get; }
///
/// Gets type collection of defined in the database.
///
public Dictionary ProtocolLookupList { get; }
///
/// Gets type collection of defined in the database.
///
public Dictionary VendorDeviceLookupList { get; }
///
/// Gets or sets connection string to back-end service.
///
public string ConnectionString
{
get => m_connectionString;
set
{
m_connectionString = value;
OnPropertyChanged(nameof(ConnectionString));
}
}
///
/// Gets or sets command channel to communicate with back-end service.
///
public string AlternateCommandChannel
{
get => m_alternateCommandChannel;
set
{
m_alternateCommandChannel = value;
OnPropertyChanged(nameof(AlternateCommandChannel));
}
}
///
/// Gets or sets ID of the device to be configured.
///
public int AccessID
{
get => m_accessID;
set
{
m_accessID = value;
OnPropertyChanged(nameof(AccessID));
}
}
///
/// Gets or sets device IDs.
///
public int[] DeviceIDs
{
get => m_deviceIDs ?? (m_deviceIDs = Array.Empty());
set
{
m_deviceIDs = value;
OnPropertyChanged(nameof(DeviceIDs));
}
}
///
/// Gets or sets device acronyms.
///
public string[] DeviceAcronyms
{
get => m_deviceAcronyms ?? (m_deviceAcronyms = Array.Empty());
set
{
m_deviceAcronyms = value;
OnPropertyChanged(nameof(DeviceAcronyms));
}
}
///
/// Gets or sets protocol id for devices to be configured.
///
public int ProtocolID
{
get => m_protocolID;
set
{
m_protocolID = value;
OnPropertyChanged(nameof(ProtocolID));
ProtocolAcronym = ProtocolLookupList[m_protocolID];
}
}
///
/// Gets or sets protocol acronym for devices to be configured.
///
public string ProtocolAcronym
{
get => m_protocolAcronym;
set
{
m_protocolAcronym = value;
OnPropertyChanged(nameof(ProtocolAcronym));
OnPropertyChanged(nameof(ProtocolIsBpaPdcStream));
}
}
///
/// Gets a flag indicating if selected protocol is BpaPdcStream.
/// This is used to hide or display INI file selection on input wizard screen.
///
public bool ProtocolIsBpaPdcStream => string.Equals(m_protocolAcronym, "BPAPDCstream", StringComparison.OrdinalIgnoreCase);
///
/// Gets or sets boolean value indicating if connection is to concentrator.
///
public bool ConnectToConcentrator
{
get => m_connectToConcentrator;
set
{
m_connectToConcentrator = value;
OnPropertyChanged(nameof(ConnectToConcentrator));
ValidatePdcAcronym();
}
}
///
/// Gets or sets ID of the concentrator device.
///
public int? PdcID
{
get => m_pdcID;
set
{
m_pdcID = value;
OnPropertyChanged(nameof(PdcID));
}
}
///
/// Gets or sets acronym of the concentrator device.
///
public string PdcAcronym
{
get => m_pdcAcronym;
set
{
m_pdcAcronym = value.Replace(" ", "_").Replace("'", "").ToUpper();
ValidatePdcAcronym();
}
}
///
/// Gets or sets message to be displayed on the screen when PDC acronym changes.
///
public string PdcMessage
{
get => m_pdcMessage;
set
{
m_pdcMessage = value;
OnPropertyChanged(nameof(PdcMessage));
}
}
///
/// Gets or sets name of the concentrator device.
///
public string PdcName
{
get => m_pdcName;
set
{
m_pdcName = value;
OnPropertyChanged(nameof(PdcName));
}
}
///
/// Gets or sets vendor device id for this configuration.
///
public int PdcVendorDeviceID
{
get => m_pdcVendorDeviceID;
set
{
m_pdcVendorDeviceID = value;
OnPropertyChanged(nameof(PdcVendorDeviceID));
}
}
///
/// Gets or sets frame rate for this configuration.
///
public int PdcFrameRate
{
get => m_pdcFrameRate;
set
{
m_pdcFrameRate = value;
OnPropertyChanged(nameof(PdcFrameRate));
}
}
///
/// Gets or sets company ID to which devices are to be associated in this configuration.
///
public int CompanyID
{
get => m_companyID;
set
{
m_companyID = value;
OnPropertyChanged(nameof(CompanyID));
}
}
///
/// Gets or sets Historian ID to which devices are to be associated in this configuration.
///
public int HistorianID
{
get => m_historianID;
set
{
m_historianID = value;
OnPropertyChanged(nameof(HistorianID));
}
}
///
/// Gets or sets Interconnection ID to which devices are to be associated in this configuration.
///
public int InterconnectionID
{
get => m_interconnectionID;
set
{
m_interconnectionID = value;
OnPropertyChanged(nameof(InterconnectionID));
}
}
///
/// Gets or sets a boolean flag for SkipDisableRealTimeData.
///
public bool SkipDisableRealTimeData
{
get => m_skipDisableRealTimeData;
set
{
m_skipDisableRealTimeData = value;
OnPropertyChanged(nameof(SkipDisableRealTimeData));
}
}
///
/// Gets or set a boolean flag for whether the configuration frame request pop-up is open.
///
public bool RequestConfigurationPopupIsOpen
{
get => m_requestConfigurationPopupIsOpen;
set
{
m_requestConfigurationPopupIsOpen = value;
OnPropertyChanged(nameof(RequestConfigurationPopupIsOpen));
}
}
///
/// Gets or sets the text displayed on the configuration frame request pop-up.
///
public string RequestConfigurationPopupText
{
get => m_requestConfigurationPopupText;
set
{
m_requestConfigurationPopupText = value;
OnPropertyChanged(nameof(RequestConfigurationPopupText));
}
}
///
/// Gets a boolean flag that indicates whether the last configuration request operation was successful.
///
public bool RequestConfigurationSuccess
{
get => m_requestConfigurationSuccess;
set
{
m_requestConfigurationSuccess = value;
OnPropertyChanged(nameof(RequestConfigurationSuccess));
}
}
///
/// Gets to launch the wizard walk through.
///
public ICommand LaunchWalkthroughCommand => m_launchWalkthroughCommand ?? (m_launchWalkthroughCommand = new RelayCommand(LaunchWalkthrough));
///
/// Gets to browse to connection file.
///
public ICommand BrowseConnectionFileCommand => m_browseConnectionFileCommand ?? (m_browseConnectionFileCommand = new RelayCommand(BrowseConnectionFile, () => CanSave));
///
/// Gets to build connection string.
///
public ICommand BuildConnectionStringCommand => m_buildConnectionStringCommand ?? (m_buildConnectionStringCommand = new RelayCommand(BuildConnectionString, () => CanSave));
///
/// Gets to build alternate command channel.
///
public ICommand BuildAlternateCommandChannelCommand => m_buildAlternateCommandChannelCommand ?? (m_buildAlternateCommandChannelCommand = new RelayCommand(BuildAlternateCommandChannel, () => CanSave));
///
/// Gets to browse to INI file.
///
public ICommand BrowseIniFileCommand => m_browseIniFileCommand ?? (m_browseIniFileCommand = new RelayCommand(BrowseIniFile, () => CanSave));
///
/// Gets to browse to configuration file.
///
public ICommand BrowseConfigurationFileCommand => m_browseConfigurationFileCommand ?? (m_browseConfigurationFileCommand = new RelayCommand(BrowseConfigurationFile, () => CanSave));
///
/// Gets to request configuration frame from backed service.
///
public ICommand RequestConfigurationCommand => m_requestConfigurationCommand ?? (m_requestConfigurationCommand = new RelayCommand(RequestConfiguration, () => CanSave));
///
/// Gets to cancel a configuration frame request.
///
public ICommand CancelConfigurationRequestCommand => m_cancelConfigurationRequestCommand ?? (m_cancelConfigurationRequestCommand = new RelayCommand(CancelConfigurationRequest));
///
/// Gets to save current configuration to XML file.
///
public ICommand SaveConfigurationFileCommand => m_saveConfigurationFileCommand ?? (m_saveConfigurationFileCommand = new RelayCommand(SaveConfigurationFile, () => CanSave));
///
/// Gets to create or update configuration manually.
///
public ICommand ManualConfigurationCommand => m_manualConfigurationCommand ?? (m_manualConfigurationCommand = new RelayCommand(ManualConfiguration, () => CanSave));
///
/// Gets or sets summary message to be displayed on UI after parsing configuration file or frame.
///
public string ConfigurationSummary
{
get => m_configurationSummary;
set
{
m_configurationSummary = value;
OnPropertyChanged(nameof(ConfigurationSummary));
}
}
///
/// Gets boolean flag indicating if configuration can be saved or not.
///
public override bool CanSave => CommonFunctions.CurrentPrincipal.IsInRole("Administrator, Editor");
///
/// Gets or sets ID of the device in case where configuration is being updated.
///
public int CurrentDeviceRuntimeID { get; set; }
///
/// Gets a flag indicating if current device was disconnected before requesting configuration.
///
public bool DisconnectedCurrentDevice { get; private set; }
///
/// Gets or sets a flag indicating whether the wizard was launched in
/// order to set up a new device configuration or update an existing one.
///
public bool NewDeviceConfiguration
{
get => m_newDeviceConfiguration;
set
{
m_newDeviceConfiguration = value;
OnPropertyChanged(nameof(NewDeviceConfiguration));
}
}
///
/// Gets the error message for the property with the given name.
///
///
/// The error message for the property. The default is an empty string ("").
///
/// The name of the property whose error message to get.
public string this[string propertyName] => m_errorMessages.TryGetValue(propertyName, out string errorMessage) ? errorMessage : string.Empty;
///
/// Gets an error message indicating what is wrong with this object.
///
///
/// An error message indicating what is wrong with this object. The default is an empty string ("").
///
public string Error
{
get
{
IEnumerable errorMessages = m_errorMessages.Select(kvp => $"{kvp.Key}: {kvp.Value}");
return string.Join(Environment.NewLine, errorMessages);
}
}
#endregion
#region [ Methods ]
///
/// Gets the primary key value of the .
///
/// The primary key value of the .
public override string GetCurrentItemKey() => CurrentItem.Acronym;
///
/// Gets the string based named identifier of the .
///
/// The string based named identifier of the .
public override string GetCurrentItemName() => CurrentItem.Name;
///
/// Handles BrowseConnectionFileCommand.
///
private void BrowseConnectionFile()
{
OpenFileDialog fileDialog = new OpenFileDialog();
fileDialog.Multiselect = false;
fileDialog.Filter = "PMU Connection Files (*.PmuConnection)|*.PmuConnection|All Files (*.*)|*.*";
if (fileDialog.ShowDialog() != DialogResult.OK)
return;
try
{
Mouse.OverrideCursor = Cursors.Wait;
ConnectionFileName = fileDialog.FileName;
Stream fileData = fileDialog.OpenFile();
ConnectionSettings connectionSettings;
using (fileData)
{
SoapFormatter sf = new SoapFormatter();
sf.AssemblyFormat = FormatterAssemblyStyle.Simple;
sf.TypeFormat = FormatterTypeStyle.TypesWhenNeeded;
sf.Binder = Serialization.LegacyBinder;
connectionSettings = sf.Deserialize(fileData) as ConnectionSettings;
}
if (!(connectionSettings is null))
{
ConnectionString = connectionSettings.ConnectionString;
Dictionary connectionStringKeyValues = ConnectionString.ParseKeyValuePairs();
if (connectionStringKeyValues.ContainsKey("commandchannel"))
{
AlternateCommandChannel = connectionStringKeyValues["commandchannel"];
connectionStringKeyValues.Remove("commandchannel");
}
if (connectionStringKeyValues.ContainsKey("skipdisablerealtimedata"))
{
SkipDisableRealTimeData = Convert.ToBoolean(connectionStringKeyValues["skipdisablerealtimedata"]);
connectionStringKeyValues.Remove("skipdisablerealtimedata");
}
ConnectionString = $"transportprotocol={connectionSettings.TransportProtocol};{connectionStringKeyValues.JoinKeyValuePairs()}";
if (!(connectionSettings.ConnectionParameters is null))
{
switch (connectionSettings.PhasorProtocol)
{
case PhasorProtocol.BPAPDCstream:
if (connectionSettings.ConnectionParameters is ConnectionParameters bpaParameters)
ConnectionString += $"; iniFileName={bpaParameters.ConfigurationFileName}; refreshConfigFileOnChange={bpaParameters.RefreshConfigurationFileOnChange}; parseWordCountFromByte={bpaParameters.ParseWordCountFromByte}";
break;
case PhasorProtocol.FNET:
if (connectionSettings.ConnectionParameters is FNET.ConnectionParameters fnetParameters)
ConnectionString += $"; timeOffset={fnetParameters.TimeOffset}; stationName={fnetParameters.StationName}; frameRate={fnetParameters.FrameRate}; nominalFrequency={(int)fnetParameters.NominalFrequency}";
break;
case PhasorProtocol.SelFastMessage:
if (connectionSettings.ConnectionParameters is SelFastMessage.ConnectionParameters selParameters)
ConnectionString += $"; messagePeriod={selParameters.MessagePeriod}";
AccessID = 0;
break;
case PhasorProtocol.IEC61850_90_5:
if (connectionSettings.ConnectionParameters is IEC61850_90_5.ConnectionParameters iecParameters)
ConnectionString += $"; useETRConfiguration={iecParameters.UseETRConfiguration}; guessConfiguration={iecParameters.GuessConfiguration}; parseRedundantASDUs={iecParameters.ParseRedundantASDUs}; ignoreSignatureValidationFailures={iecParameters.IgnoreSignatureValidationFailures}; ignoreSampleSizeValidationFailures={iecParameters.IgnoreSampleSizeValidationFailures}";
break;
case PhasorProtocol.Macrodyne:
if (connectionSettings.ConnectionParameters is Macrodyne.ConnectionParameters macrodyneParameters)
ConnectionString += $"; protocolVersion={macrodyneParameters.ProtocolVersion}; iniFileName={macrodyneParameters.ConfigurationFileName}; refreshConfigFileOnChange={macrodyneParameters.RefreshConfigurationFileOnChange}; deviceLabel={macrodyneParameters.DeviceLabel}";
AccessID = 0;
break;
}
}
AccessID = connectionSettings.PmuID;
ProtocolAcronym = connectionSettings.PhasorProtocol.ToString();
if (ProtocolLookupList.Count > 0)
{
Protocol protocol = m_protocolList.FirstOrDefault(protocolRecord => string.Equals(protocolRecord.Acronym, connectionSettings.PhasorProtocol.ToString(), StringComparison.OrdinalIgnoreCase));
if (!(protocol is null))
ProtocolID = protocol.ID;
}
}
}
catch (Exception ex)
{
DisplayPopup(ex.Message, "Open Connection File", MessageBoxImage.Error);
}
finally
{
Mouse.OverrideCursor = null;
}
}
///
/// Handles BrowseConfigurationFileCommand.
///
private void BrowseConfigurationFile()
{
OpenFileDialog fileDialog = new OpenFileDialog();
fileDialog.Multiselect = false;
fileDialog.Filter = "XML Files (*.xml)|*.xml|All Files (*.*)|*.*";
if (fileDialog.ShowDialog() == DialogResult.OK)
{
try
{
Mouse.OverrideCursor = Cursors.Wait;
ConfigurationFileName = fileDialog.FileName;
ConfigurationSummary = string.Empty;
Stream fileData = fileDialog.OpenFile();
SoapFormatter sf = new SoapFormatter
{
AssemblyFormat = FormatterAssemblyStyle.Simple,
TypeFormat = FormatterTypeStyle.TypesWhenNeeded,
Binder = Serialization.LegacyBinder
};
// If protocol is BpaPdcStream and INI file path is provided then replace existing path with the provided one.
if (ProtocolIsBpaPdcStream && !string.IsNullOrEmpty(IniFileName))
{
try
{
string configFileDataString = new StreamReader(fileData).ReadToEnd();
string leftPart = configFileDataString.Substring(0, configFileDataString.IndexOf("", StringComparison.Ordinal));
string rightPart = configFileDataString.Substring(configFileDataString.IndexOf("", StringComparison.Ordinal));
leftPart = leftPart.Substring(0, leftPart.LastIndexOf(">", StringComparison.Ordinal) + 1);
configFileDataString = leftPart + m_iniFileName + rightPart;
Byte[] fileBytes = Encoding.UTF8.GetBytes(configFileDataString);
using (MemoryStream ms = new MemoryStream(fileBytes))
{
m_configurationFrame = sf.Deserialize(ms) as IConfigurationFrame;
}
}
catch (Exception ex)
{
throw new Exception($"Error occurred in processing INI file. {ex.Message}");
}
}
else
{
m_configurationFrame = sf.Deserialize(fileData) as IConfigurationFrame;
}
ParseConfiguration();
}
catch (Exception ex)
{
DisplayPopup(ex.Message, "Open Configuration File", MessageBoxImage.Error);
}
finally
{
Mouse.OverrideCursor = null;
}
}
}
///
/// Parses IConfigurationFrame to retrieve devices and other configuration information.
///
private void ParseConfiguration(bool displayPopup = true)
{
ObservableCollection wizardDeviceList = new ObservableCollection();
bool isConcentrator = false;
if (!(m_configurationFrame is null))
{
PdcFrameRate = m_configurationFrame.FrameRate;
for (int i = 0; i < m_configurationFrame.Cells.Count; i++)
{
IConfigurationCell cell = m_configurationFrame.Cells[i];
Device existingDevice = null;
string stationAcronym = cell.StationName?.Replace(" ", "_").Replace("'", "").ToUpper() ?? "UNDEFINED";
string stationName = CultureInfo.CurrentUICulture.TextInfo.ToTitleCase(cell.StationName?.ToLower() ?? stationAcronym);
string deviceAcronym = i < DeviceAcronyms.Length ? DeviceAcronyms[i] : stationAcronym;
int deviceID = i < DeviceIDs.Length ? DeviceIDs[i] : 0;
Guid? uniqueID = null;
decimal? longitude = null, latitude = null;
if (cell is ConfigurationCell3 configCell3)
{
uniqueID = configCell3.GlobalID;
longitude = configCell3.LongitudeM;
latitude = configCell3.LatitudeM;
}
if (deviceID > 0)
existingDevice = Device.GetDevice(null, $"WHERE ID = {deviceID}");
if (existingDevice is null)
existingDevice = Device.GetDevice(null, $"WHERE Acronym = '{deviceAcronym}'");
if (existingDevice is null && !(uniqueID is null) && uniqueID != Guid.Empty)
{
try
{
// If about to add a new device and unique ID already exists locally, then
// source device implementation of configuration 3 GlobalID is faulty. In
// this case we just create our own unique ID normally...
if (!(Device.GetDevice(null, $"WHERE UniqueID = '{uniqueID.ToString().ToLower()}'") is null))
{
try
{
CommonFunctions.LogException(null, "Non-unique GlobalID encountered", new InvalidOperationException($"GlobalID {{{uniqueID}}} in IEEE C37.118 configuration frame 3 for device \"{stationAcronym}\" [ID: {deviceID}] already exists in database. Check device configuration."));
}
finally
{
uniqueID = null;
}
}
}
catch (Exception ex)
{
CommonFunctions.LogException(null, "Failed while attempting query unique ID", ex);
}
}
Dictionary existingPhasors = null;
if (existingDevice?.ID > 0)
existingPhasors = Phasor.Load(null, Phasor.LoadKeys(null, existingDevice.ID)).ToDictionary(phasor => phasor.SourceIndex - 1, phasor => phasor);
// Make sure any updates to phase guessing is synchronized with "AddSynchrophasorDevice.cshtml" in the "PhasorWebUI" assembly
bool phaseMatchExact(string phaseLabel, string[] phaseMatches) =>
phaseMatches.Any(match => phaseLabel.Equals(match, StringComparison.Ordinal));
bool phaseEndsWith(string phaseLabel, string[] phaseMatches, bool ignoreCase) =>
phaseMatches.Any(match => phaseLabel.EndsWith(match, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal));
bool phaseStartsWith(string phaseLabel, string[] phaseMatches, bool ignoreCase) =>
phaseMatches.Any(match => phaseLabel.StartsWith(match, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal));
bool phaseContains(string phaseLabel, string[] phaseMatches, bool ignoreCase) =>
phaseMatches.Any(match => phaseLabel.IndexOf(match, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) > -1);
bool phaseMatchHighConfidence(string phaseLabel, string[] containsMatches, string[] endsWithMatches)
{
if (phaseEndsWith(phaseLabel, containsMatches, true))
return true;
if (phaseStartsWith(phaseLabel, containsMatches, true))
return true;
foreach (string match in containsMatches.Concat(endsWithMatches))
{
string[] variations = { $" {match}", $"_{match}", $"-{match}", $".{match}" };
if (phaseEndsWith(phaseLabel, variations, false))
return true;
}
foreach (string match in containsMatches)
{
string[] variations = { $" {match} ", $"_{match}_", $"-{match}-", $"-{match}_", $"_{match}-", $".{match}." };
if (phaseContains(phaseLabel, variations, false))
return true;
}
return false;
}
bool phaseMatchLowConfidence(string phaseLabel, string[] phaseMatches)
{
foreach (string match in phaseMatches)
{
string[] variations = { $" {match}", $"{match} ", $"_{match}", $"{match}_", $"_{match}_", $"-{match}", $"{match}-", $"-{match}-", $"-{match}_", $"_{match}-", $".{match}", $"{match}.", $".{match}." };
if (phaseContains(phaseLabel, variations, true))
return true;
}
return false;
}
string guessPhase(string phase, string phasorLabel)
{
if (!string.IsNullOrWhiteSpace(phase) && phase != "+" && !phase.EndsWith("?"))
return phase;
// Handle high confidence phase matches when no phase is defined or when phase is "+" - since positive sequence is often default value, it's treated with suspicion
if (string.IsNullOrWhiteSpace(phase) || phase == "+")
{
if (phaseMatchExact(phasorLabel, new[] { "V1PM", "I1PM" }) || phaseMatchHighConfidence(phasorLabel, new[] { "V1", "VP", "I1", "IP", "VSEQ1", "ISEQ1" }, new[] { "POS", "V1PM", "I1PM", "PS", "PSV", "PSI" }) || phaseEndsWith(phasorLabel, new[] { "+SV", "+SI", "+V", "+I" }, true))
return "+";
if (phaseMatchExact(phasorLabel, new[] { "V0PM", "I0PM", "VZPM", "IZPM" }) || phaseMatchHighConfidence(phasorLabel, new[] { "V0", "I0", "VSEQ0", "ISEQ0" }, new[] { "ZERO", "ZPV", "ZPI", "VSPM", "V0PM", "I0PM", "VZPM", "IZPM", "ZS", "ZSV", "ZSI" }) || phaseEndsWith(phasorLabel, new[] { "0SV", "0SI" }, true))
return "0";
if (phaseMatchExact(phasorLabel, new[] { "VAPM", "IAPM" }) || phaseMatchHighConfidence(phasorLabel, new[] { "VA", "IA" }, new[] { "APV", "API", "VAPM", "IAPM", "AV", "AI" }))
return "A";
if (phaseMatchExact(phasorLabel, new[] { "VBPM", "IBPM" }) || phaseMatchHighConfidence(phasorLabel, new[] { "VB", "IB" }, new[] { "BPV", "BPI", "VBPM", "IBPM", "BV", "BI" }))
return "B";
if (phaseMatchExact(phasorLabel, new[] { "VCPM", "ICPM" }) || phaseMatchHighConfidence(phasorLabel, new[] { "VC", "IC" }, new[] { "CPV", "CPI", "VCPM", "ICPM", "CV", "CI" }))
return "C";
if (phaseMatchExact(phasorLabel, new[] { "VNPM", "INPM" }) || phaseMatchHighConfidence(phasorLabel, new[] { "VN", "IN" }, new[] { "NEUT", "NPV", "NPI", "VNPM", "INPM", "NV", "NI" }))
return "N";
if (phaseMatchExact(phasorLabel, new[] { "V2PM", "I2PM" }) || phaseMatchHighConfidence(phasorLabel, new[] { "V2", "I2", "VSEQ2", "ISEQ2" }, new[] { "NEG", "-SV", "-SI", "V2PM", "I2PM", "NS", "NSV", "NSI" }))
return "-";
}
// Handle lower confidence phase matches only when phase is not defined
if (string.IsNullOrWhiteSpace(phase))
{
// Since positive sequence is the default and always treated with accuracy suspicion, verify it's value first
if (phaseMatchLowConfidence(phasorLabel, new[] { "V1", "VP", "I1", "IP", "POS", "V1PM", "I1PM", "PS", "PSV", "PSI", "+SV", "+SI", "+V", "+I" }))
return "+?";
if (phaseMatchLowConfidence(phasorLabel, new[] { "V0", "I0", "ZERO", "ZPV", "ZPI", "VSPM", "VZPM", "IZPM", "ZS", "ZSV", "ZSI", "0SV", "0SI" }))
return "0?";
if (phaseMatchLowConfidence(phasorLabel, new[] { "VA", "IA", "APV", "API", "VAPM", "IAPM", "AV", "AI" }))
return "A?";
if (phaseMatchLowConfidence(phasorLabel, new[] { "VB", "IB", "BPV", "BPI", "VBPM", "IBPM", "BV", "BI" }))
return "B?";
if (phaseMatchLowConfidence(phasorLabel, new[] { "VC", "IC", "CPV", "CPI", "VCPM", "ICPM", "CV", "CI" }))
return "C?";
if (phaseMatchLowConfidence(phasorLabel, new[] { "VN", "IN", "NEUT", "NPV", "NPI", "VNPM", "INPM", "NV", "NI" }))
return "N?";
if (phaseMatchLowConfidence(phasorLabel, new[] { "V2", "I2", "NEG", "-SV", "-SI", "V2PM", "I2PM", "NS", "NSV", "NSI" }))
return "-?";
// Test for contains after checks with separators
if (phaseContains(phasorLabel, new[] { "V1", "I1", "POS", "V1PM", "I1PM", "PS", "PSV", "PSI", "+SV", "+SI", "+V", "+I" }, true))
return "+?";
if (phaseContains(phasorLabel, new[] { "V0", "I0", "ZERO", "ZPV", "ZPI", "VSPM", "VZPM", "IZPM", "ZS", "ZSV", "ZSI", "0SV", "0SI" }, true))
return "0?";
if (phaseContains(phasorLabel, new[] { "VA", "IA", "APV", "API", "VAPM", "IAPM", "AV", "AI" }, true))
return "A?";
if (phaseContains(phasorLabel, new[] { "VB", "IB", "BPV", "BPI", "VBPM", "IBPM", "BV", "BI" }, true))
return "B?";
if (phaseContains(phasorLabel, new[] { "VC", "IC", "CPV", "CPI", "VCPM", "ICPM", "CV", "CI" }, true))
return "C?";
if (phaseContains(phasorLabel, new[] { "VN", "IN", "NEUT", "NPV", "NPI", "VNPM", "INPM", "NV", "NI" }, true))
return "N?";
if (phaseContains(phasorLabel, new[] { "V2", "I2", "NEG", "-SV", "-SI", "V2PM", "I2PM", "NS", "NSV", "NSI" }, true))
return "-?";
// -V and -I may match too often, so check these last
if (phaseMatchLowConfidence(phasorLabel, new[] { "-V", "-I" }) || phaseContains(phasorLabel, new[] { "-V", "-I" }, true))
return "-?";
return "+?";
}
return phase;
}
string guessBaseKV(string baseKV, string phasorLabel, string deviceLabel)
{
if (!string.IsNullOrWhiteSpace(baseKV) && int.TryParse(baseKV, out int value) && value > 0)
return baseKV;
// Check phasor label before device
foreach (string voltageLevel in s_commonVoltageLevels)
{
if (phasorLabel.IndexOf(voltageLevel, StringComparison.Ordinal) > -1)
return voltageLevel;
}
foreach (string voltageLevel in s_commonVoltageLevels)
{
if (deviceLabel.IndexOf(voltageLevel, StringComparison.Ordinal) > -1)
return voltageLevel;
}
return "0";
}
bool phasorExists(IPhasorDefinition phasor) => !(phasor is null) && (existingPhasors?.ContainsKey(phasor.Index) ?? false);
string getPhasorLabel(IPhasorDefinition phasor) => phasorExists(phasor) ? existingPhasors?[phasor.Index].Label : phasor.Label;
string getPhasorType(IPhasorDefinition phasor) => phasorExists(phasor) ? existingPhasors?[phasor.Index].Type : phasor.PhasorType == PhasorType.Current ? "I" : "V";
string getPhasorPhase(IPhasorDefinition phasor)
{
string configPhase = string.Empty;
if (phasor is PhasorDefinition3 phasor3)
{
// ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
switch (phasor3.PhasorComponent)
{
case PhasorComponent.ZeroSequence:
configPhase = "0";
break;
case PhasorComponent.PositiveSequence:
configPhase = "+";
break;
case PhasorComponent.NegativeSequence:
configPhase = "-";
break;
case PhasorComponent.PhaseA:
configPhase = "A";
break;
case PhasorComponent.PhaseB:
configPhase = "B";
break;
case PhasorComponent.PhaseC:
configPhase = "C";
break;
case PhasorComponent.ReservedPhase:
break;
default:
configPhase = string.Empty;
break;
}
}
return guessPhase(phasorExists(phasor) ? existingPhasors?[phasor.Index].Phase : configPhase, phasor.Label);
}
string getPhasorBaseKV(IPhasorDefinition phasor)
{
string configBaseKV = "0";
if (phasor is PhasorDefinition3 phasor3 && Enum.TryParse(phasor3.UserFlags.ToString(), out VoltageLevel level))
configBaseKV = level.Value().ToString();
return guessBaseKV(phasorExists(phasor) ? existingPhasors?[phasor.Index].BaseKV.ToString() : configBaseKV, phasor.Label, string.IsNullOrWhiteSpace(existingDevice?.Name) ? existingDevice?.Acronym ?? "" : existingDevice?.Name);
}
float getMagnitudeMultiplier(IPhasorDefinition phasor) =>
phasor is PhasorDefinition3 phasor3 ? phasor3.MagnitudeMultiplier : 1.0F;
float getAngleAdder(IPhasorDefinition phasor) =>
phasor is PhasorDefinition3 phasor3 ? phasor3.AngleAdder : 0.0F;
Tuple getAnalogScalarSet(IAnalogDefinition analog) =>
analog is AnalogDefinition3 analog3 ? new Tuple(analog3.Adder, analog3.Multiplier) : null;
Tuple[] getAnalogScalars(AnalogDefinitionCollection analogs) =>
analogs.Select(getAnalogScalarSet).ToArray();
string deviceIndex = m_configurationFrame.Cells.Count > 1 ? $" {i + 1:N0}" : "";
wizardDeviceList.Add(new InputWizardDevice
{
ID = deviceID,
UniqueID = existingDevice?.UniqueID ?? uniqueID,
Acronym = string.IsNullOrWhiteSpace(existingDevice?.Acronym) ? stationAcronym : existingDevice.Acronym,
Name = string.IsNullOrWhiteSpace(existingDevice?.Name) ? stationName : existingDevice.Name,
ConfigAcronym = $"Device{deviceIndex} label from config: {stationAcronym}{(string.IsNullOrWhiteSpace(cell.IDLabel) ? "" : $" ({cell.IDLabel})")}",
ConfigName = $"Device{deviceIndex} name derived from config: {stationName}",
Longitude = existingDevice?.Longitude ?? longitude ?? -98.6m,
Latitude = existingDevice?.Latitude ?? latitude ?? 37.5m,
VendorDeviceID = existingDevice?.VendorDeviceID,
AccessID = cell.IDCode,
ParentAccessID = m_configurationFrame.IDCode,
Include = true,
DigitalCount = cell.DigitalDefinitions.Count,
AnalogCount = cell.AnalogDefinitions.Count,
AddDigitals = cell.DigitalDefinitions.Count > 0,
AddAnalogs = cell.AnalogDefinitions.Count > 0,
Existing = !(existingDevice is null),
DigitalLabels = GetAnalogOrDigitalLabels(cell.DigitalDefinitions),
AnalogLabels = GetAnalogOrDigitalLabels(cell.AnalogDefinitions),
AnalogScalars = getAnalogScalars(cell.AnalogDefinitions),
PhasorList = new ObservableCollection(
(
from phasor in cell.PhasorDefinitions
select new InputWizardDevicePhasor
{
Label = getPhasorLabel(phasor),
Type = getPhasorType(phasor),
ConfigLabel = $"Phasor {phasor.Index + 1:N0} label from config: {phasor.Label}",
ConfigType = $"Phasor {phasor.Index + 1:N0} type from config: {phasor.PhasorType}",
Phase = getPhasorPhase(phasor),
BaseKVInput = getPhasorBaseKV(phasor),
Include = true,
MagnitudeMultiplier = getMagnitudeMultiplier(phasor),
AngleAdder = getAngleAdder(phasor)
}
).ToList())
});
}
isConcentrator = wizardDeviceList.Count > 1 || wizardDeviceList.Count == 1 && m_configurationFrame.IDCode != m_configurationFrame.Cells.First().IDCode;
}
m_dispatcher.BeginInvoke(new Action(() =>
{
ItemsSource = wizardDeviceList;
ConfigurationSummary = $"Current Configuration Summary: {wizardDeviceList.Count}";
if (isConcentrator)
{
ConfigurationSummary += " Devices. Please provide PDC information below.";
ConnectToConcentrator = true;
}
else
{
ConfigurationSummary += " Device";
ConnectToConcentrator = false;
}
if (displayPopup)
DisplayPopup(ConfigurationSummary, "Parsed Configuration Successfully.", MessageBoxImage.Information);
}));
}
private static List GetAnalogOrDigitalLabels(IList analogOrDigitalCollection)
{
List returnCollection = new List();
switch (analogOrDigitalCollection)
{
case DigitalDefinitionCollection digitalCollection:
{
returnCollection.AddRange(digitalCollection.Select(digital => digital is DigitalDefinition3 digital3 ? digital3.Label : digital.Label.TruncateRight(256)));
break;
}
case AnalogDefinitionCollection analogCollection:
{
returnCollection.AddRange(analogCollection.Select(analog => analog is AnalogDefinition3 analog3 ? analog3.Label : analog.Label.TruncateRight(16)));
break;
}
}
return returnCollection;
}
///
/// Saves current configuration information into XML file.
///
private void SaveConfigurationFile()
{
try
{
if (m_configurationFrame is null)
{
DisplayPopup("Current configuration is undefined and cannot be saved.", "Save Current Configuration", MessageBoxImage.Information);
}
else
{
SaveFileDialog saveDialog = new SaveFileDialog();
saveDialog.Title = "Save Current Configuration";
saveDialog.Filter = "XML Files (*.xml)|*.xml|All Files (*.*)|*.*";
saveDialog.FileName = "";
if (saveDialog.ShowDialog() == DialogResult.OK)
{
using (FileStream stream = File.Create(saveDialog.FileName))
{
SoapFormatter sf = new SoapFormatter
{
AssemblyFormat = FormatterAssemblyStyle.Simple,
TypeFormat = FormatterTypeStyle.TypesWhenNeeded
};
sf.Serialize(stream, m_configurationFrame);
DisplayPopup("Configuration file saved successfully.", "Save Current Configuration", MessageBoxImage.Information);
}
}
}
}
catch (Exception ex)
{
DisplayPopup($"ERROR: {ex.Message}", "Save Current Configuration", MessageBoxImage.Error);
}
}
///
/// Handles BroseIniFileCommand.
///
private void BrowseIniFile()
{
OpenFileDialog fileDialog = new OpenFileDialog
{
Multiselect = false,
Filter = "INI Files (*.ini)|*.ini|All Files (*.*)|*.*"
};
if (fileDialog.ShowDialog() == DialogResult.OK)
{
try
{
IniFileName = fileDialog.FileName;
}
catch (Exception ex)
{
DisplayPopup(ex.Message, "Browse to INI File", MessageBoxImage.Error);
}
}
}
///
/// Pops up another window to assist in filling out the fields on the input wizard.
///
private void LaunchWalkthrough()
{
InputWizardWalkthrough walkthrough = new InputWizardWalkthrough
{
Owner = Application.Current.MainWindow,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
DataContext = this
};
walkthrough.ShowDialog();
}
///
/// Pops up another window to build or modify connection string.
///
private void BuildConnectionString()
{
ConnectionStringBuilder csb = new ConnectionStringBuilder(ConnectionStringBuilder.ConnectionType.DeviceConnection);
if (!string.IsNullOrEmpty(ConnectionString))
csb.ConnectionString = ConnectionString;
csb.Closed += delegate
{
if (csb.DialogResult != null && csb.DialogResult.GetValueOrDefault())
ConnectionString = csb.ConnectionString;
};
csb.Owner = Application.Current.MainWindow;
csb.WindowStartupLocation = WindowStartupLocation.CenterOwner;
csb.ShowDialog();
}
///
/// Pops up another window to build or modify alternate command channel.
///
private void BuildAlternateCommandChannel()
{
ConnectionStringBuilder csb = new ConnectionStringBuilder(ConnectionStringBuilder.ConnectionType.AlternateCommandChannel);
if (!string.IsNullOrEmpty(AlternateCommandChannel))
csb.ConnectionString = AlternateCommandChannel;
csb.Closed += delegate
{
if (csb.DialogResult != null && csb.DialogResult.GetValueOrDefault())
AlternateCommandChannel = csb.ConnectionString;
};
csb.Owner = Application.Current.MainWindow;
csb.WindowStartupLocation = WindowStartupLocation.CenterOwner;
csb.ShowDialog();
}
///
/// Handles RequestConfigurationCommand.
///
private void RequestConfiguration()
{
DispatcherTimer timer = new DispatcherTimer();
StepsEnabled = false;
RequestConfigurationPopupIsOpen = true;
RequestConfigurationPopupText = $"Requesting configuration{Environment.NewLine}.";
timer.Tick += (sender, args) => RequestConfigurationPopupText += ".";
timer.Interval = TimeSpan.FromSeconds(1.0D);
timer.Start();
Thread requestConfigurationThread = new Thread(() =>
{
AdoDataConnection database = null;
WindowsServiceClient windowsServiceClient = null;
try
{
m_requestConfigurationError = string.Empty;
m_dispatcher.BeginInvoke(new Action(() => RequestConfigurationSuccess = false));
if (CurrentDeviceRuntimeID > 0)
{
CommonFunctions.SendCommandToService($"Disconnect {CurrentDeviceRuntimeID}");
DisconnectedCurrentDevice = true;
}
database = new AdoDataConnection(CommonFunctions.DefaultSettingsCategory);
m_dispatcher.BeginInvoke(new Action(() => Mouse.OverrideCursor = Cursors.Wait));
s_responseWaitHandle = new ManualResetEvent(false);
windowsServiceClient = CommonFunctions.GetWindowsServiceClient();
if (windowsServiceClient?.Helper?.RemotingClient != null && windowsServiceClient.Helper.RemotingClient.CurrentState == ClientState.Connected)
{
windowsServiceClient.Helper.RemotingClient.ConnectionTerminated += RemotingClient_ConnectionTerminated;
windowsServiceClient.Helper.ReceivedServiceResponse += Helper_ReceivedServiceResponse;
windowsServiceClient.Helper.ReceivedServiceUpdate += Helper_ReceivedServiceUpdate;
string connectionString = GenerateConnectionString();
if (!connectionString.EndsWith(";"))
connectionString += ";";
connectionString += $"accessid={AccessID};";
if (!connectionString.ToLower().Contains("phasorprotocol"))
{
Protocol protocol = m_protocolList.FirstOrDefault(p => p.ID == ProtocolID);
if (protocol != null)
connectionString += $"phasorprotocol={protocol.Acronym};";
}
s_responseWaitHandle.Reset();
m_requestConfigurationAttachment = null;
CommonFunctions.SendCommandToService($"invoke 0 requestdeviceconfiguration \"{connectionString}\"");
if (s_responseWaitHandle.WaitOne(65000))
{
switch (m_requestConfigurationAttachment)
{
case ConfigurationErrorFrame _:
m_dispatcher.BeginInvoke(new Action(() => RequestConfigurationPopupText = $"Accumulating error messages{Environment.NewLine}."));
Thread.Sleep(3000);
m_requestConfigurationError = $"Received configuration error frame.{Environment.NewLine}{m_requestConfigurationError}";
throw new ApplicationException(m_requestConfigurationError);
case IConfigurationFrame frame:
m_configurationFrame = frame;
ParseConfiguration();
m_dispatcher.BeginInvoke(new Action(() => RequestConfigurationSuccess = true));
break;
default:
throw new ApplicationException("Invalid frame received, invocation for device configuration has failed.");
}
}
else
{
throw new ApplicationException("Response timeout occurred. Waited 60 seconds for Configuration Frame to arrive.");
}
}
else
{
throw new ApplicationException("Connection timeout occurred. Tried 10 times to connect to windows service.");
}
m_dispatcher.BeginInvoke(new Action(() =>
{
Mouse.OverrideCursor = null;
StepsEnabled = true;
RequestConfigurationPopupIsOpen = false;
timer.Stop();
}));
}
catch (Exception ex)
{
m_dispatcher.BeginInvoke(new Action(() =>
{
Mouse.OverrideCursor = null;
StepsEnabled = true;
RequestConfigurationPopupIsOpen = false;
timer.Stop();
DisplayPopup($"ERROR: {ex.Message}", "Request Configuration", MessageBoxImage.Error);
}));
}
finally
{
database?.Dispose();
if (windowsServiceClient?.Helper?.RemotingClient != null)
{
windowsServiceClient.Helper.RemotingClient.ConnectionTerminated -= RemotingClient_ConnectionTerminated;
windowsServiceClient.Helper.ReceivedServiceResponse -= Helper_ReceivedServiceResponse;
windowsServiceClient.Helper.ReceivedServiceUpdate -= Helper_ReceivedServiceUpdate;
}
}
});
requestConfigurationThread.IsBackground = true;
requestConfigurationThread.Start();
}
private void CancelConfigurationRequest()
{
try
{
CommonFunctions.SendCommandToService("Invoke 0 CancelConfigurationFrameRequest");
}
catch (Exception ex)
{
DisplayPopup($"ERROR: {ex.Message}", "Cancel Configuration Request", MessageBoxImage.Error);
}
}
///
/// Handles ManualConfigurationCommand.
///
private void ManualConfiguration()
{
ConfigurationCreator cc = new ConfigurationCreator();
if (m_configurationFrame != null)
cc.ConfigurationFrame = m_configurationFrame;
cc.Closed += delegate
{
if (cc.DialogResult != null && cc.DialogResult.GetValueOrDefault())
{
m_configurationFrame = cc.ConfigurationFrame;
ParseConfiguration(false);
}
};
cc.Owner = Application.Current.MainWindow;
cc.WindowStartupLocation = WindowStartupLocation.CenterOwner;
cc.ShowDialog();
}
///
/// Handles ReceivedServiceUpdate event.
///
/// Source of the event.
/// Event arguments.
private void Helper_ReceivedServiceUpdate(object sender, EventArgs e)
{
foreach (string message in e.Argument2.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
{
if (message.StartsWith("[PHASOR!SERVICES]") && !message.Contains("*"))
m_requestConfigurationError += message.Replace("[PHASOR!SERVICES]", "") + Environment.NewLine;
}
}
///
/// Handles ReceivedServiceResponse event.
///
/// Source of the event.
/// Event arguments.
private void Helper_ReceivedServiceResponse(object sender, EventArgs e)
{
List