482 lines
19 KiB
C#
482 lines
19 KiB
C#
|
|
using System.ComponentModel;
|
|||
|
|
using System.ComponentModel.Composition;
|
|||
|
|
using System.Threading.Tasks;
|
|||
|
|
using DTS.Common.Events;
|
|||
|
|
using DTS.Common.Interface.TestSetups.CachedItemsList;
|
|||
|
|
using System;
|
|||
|
|
using DTS.Common.Interface.DataRecorders;
|
|||
|
|
using DTS.Common.Interface.Sensors;
|
|||
|
|
using System.Linq;
|
|||
|
|
using Prism.Events;
|
|||
|
|
using Prism.Regions;
|
|||
|
|
using Unity;
|
|||
|
|
using DTS.Common.Interactivity;
|
|||
|
|
|
|||
|
|
// ReSharper disable CheckNamespace
|
|||
|
|
// ReSharper disable MemberCanBePrivate.Global
|
|||
|
|
// ReSharper disable InconsistentNaming
|
|||
|
|
|
|||
|
|
namespace CachedItemsList
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// this class handles CachedItemsList functionality
|
|||
|
|
/// </summary>
|
|||
|
|
[PartCreationPolicy(CreationPolicy.Shared)]
|
|||
|
|
public class CachedItemsListViewModel : ICachedItemsListViewModel
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// The SensorList view
|
|||
|
|
/// </summary>
|
|||
|
|
public ICachedItemsListView View { get; set; }
|
|||
|
|
|
|||
|
|
private IEventAggregator _eventAggregator { get; }
|
|||
|
|
private IRegionManager _regionManager;
|
|||
|
|
private IUnityContainer UnityContainer { get; }
|
|||
|
|
|
|||
|
|
public InteractionRequest<Notification> NotificationRequest { get; }
|
|||
|
|
public InteractionRequest<Confirmation> ConfirmationRequest { get; }
|
|||
|
|
|
|||
|
|
/// <inheritdoc />
|
|||
|
|
/// <summary>
|
|||
|
|
/// Occurs when a property value changes.
|
|||
|
|
/// </summary>
|
|||
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|||
|
|
public void OnPropertyChanged(string propertyName)
|
|||
|
|
{
|
|||
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#region constructors and initializers
|
|||
|
|
/// <summary>
|
|||
|
|
/// Creates a new instance of the CachedItemsListViewModel
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="view"></param>
|
|||
|
|
/// <param name="regionManager">The logical placeholder defined within the application's UI (in the shell or within views) into which views are displayed.</param>
|
|||
|
|
/// <param name="eventAggregator">The EventAggregator which allows different components to publish/subscribe to events without being coupled to each other.</param>
|
|||
|
|
/// <param name="unityContainer">The unityContainer.</param>
|
|||
|
|
public CachedItemsListViewModel(ICachedItemsListView view, IRegionManager regionManager,
|
|||
|
|
IEventAggregator eventAggregator, IUnityContainer unityContainer)
|
|||
|
|
{
|
|||
|
|
View = view;
|
|||
|
|
View.DataContext = this;
|
|||
|
|
|
|||
|
|
NotificationRequest = new InteractionRequest<Notification>();
|
|||
|
|
ConfirmationRequest = new InteractionRequest<Confirmation>();
|
|||
|
|
|
|||
|
|
_eventAggregator = eventAggregator;
|
|||
|
|
UnityContainer = unityContainer;
|
|||
|
|
_regionManager = regionManager;
|
|||
|
|
_eventAggregator.GetEvent<RaiseNotification>().Subscribe(OnRaiseNotification);
|
|||
|
|
_eventAggregator.GetEvent<BusyIndicatorChangeNotification>()
|
|||
|
|
.Subscribe(OnBusyIndicatorNotification, ThreadOption.PublisherThread, true);
|
|||
|
|
}
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region Methods
|
|||
|
|
|
|||
|
|
public void Unset()
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
public void Cleanup()
|
|||
|
|
{
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public Task CleanupAsync()
|
|||
|
|
{
|
|||
|
|
return Task.CompletedTask;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void Initialize()
|
|||
|
|
{
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void Initialize(object parameter)
|
|||
|
|
{
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void Initialize(object parameter, object model)
|
|||
|
|
{
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public Task InitializeAsync()
|
|||
|
|
{
|
|||
|
|
return Task.CompletedTask;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public Task InitializeAsync(object parameter)
|
|||
|
|
{
|
|||
|
|
return Task.CompletedTask;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void Activated()
|
|||
|
|
{
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Private Event handler for RaiseNotification event.
|
|||
|
|
/// </summary>
|
|||
|
|
private void OnBusyIndicatorNotification(bool eventArg)
|
|||
|
|
{
|
|||
|
|
IsBusy = eventArg;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// Private Event handler for RaiseNotification event.
|
|||
|
|
/// </summary>
|
|||
|
|
private void OnRaiseNotification(NotificationContentEventArgs eventArgsWithTitle)
|
|||
|
|
{
|
|||
|
|
// The NotificationRequest.Raise triggers the Invoke() method of the PopupWindowAction object to show the NotificationWindow window
|
|||
|
|
// Notification object expects a NotificationContentEventArgsWithoutTitle object and a Title string.
|
|||
|
|
var eventArgsWithoutTitle = new NotificationContentEventArgs(eventArgsWithTitle.Message, "",
|
|||
|
|
eventArgsWithTitle.Image, string.Empty);
|
|||
|
|
|
|||
|
|
NotificationRequest.Raise(new Notification
|
|||
|
|
{
|
|||
|
|
Content = eventArgsWithoutTitle,
|
|||
|
|
Title = eventArgsWithTitle.Title
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public bool SetCachedItems(ISensorData[] sensors, ISensorCalibration[] sensorCalibrations,
|
|||
|
|
IDASHardware[] hardware, IDASHardware[] allDAS)
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
//var list = new List<ICachedItem>();
|
|||
|
|
|
|||
|
|
//#region SENSORS
|
|||
|
|
//var serialNumberToSensor = new Dictionary<string, ISensorData>();
|
|||
|
|
//var sensorsNeedingProcessing = new HashSet<string>();
|
|||
|
|
//foreach (var s in sensors)
|
|||
|
|
//{
|
|||
|
|
// serialNumberToSensor[s.SerialNumber] = s;
|
|||
|
|
// sensorsNeedingProcessing.Add(s.SerialNumber);
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
//#region Sensors IN DB
|
|||
|
|
|
|||
|
|
//#region analog
|
|||
|
|
|
|||
|
|
//var hr = DbOperations.SensorsAnalogGet(null, null, null, out var records);
|
|||
|
|
//if( 0 == hr && null != records && records.Any())
|
|||
|
|
//{
|
|||
|
|
// foreach( var record in records)
|
|||
|
|
// {
|
|||
|
|
// if (!sensorsNeedingProcessing.Contains(record.SerialNumber))
|
|||
|
|
// {
|
|||
|
|
// //either we didn't need to process this sensor, or we already have ...?
|
|||
|
|
// continue;
|
|||
|
|
// }
|
|||
|
|
// sensorsNeedingProcessing.Remove(record.SerialNumber);
|
|||
|
|
// var original = serialNumberToSensor[record.SerialNumber];
|
|||
|
|
// if (Math.Abs(record.LastModified.Subtract(original.LastModified).TotalSeconds) > 1)
|
|||
|
|
// {
|
|||
|
|
// list.Add(new CachedItem(record.SerialNumber, Resources.StringResources.Sensor,
|
|||
|
|
// original.LastModified, record.LastModified));
|
|||
|
|
// }
|
|||
|
|
// }
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
|
|||
|
|
//#region squib
|
|||
|
|
//hr = DbOperations.SensorsSquibGet(null, null, null, out var squibRecords);
|
|||
|
|
//if( 0 == hr && null != squibRecords && squibRecords.Any())
|
|||
|
|
//{
|
|||
|
|
// foreach( var squibRecord in squibRecords)
|
|||
|
|
// {
|
|||
|
|
// if( !sensorsNeedingProcessing.Contains(squibRecord.SerialNumber))
|
|||
|
|
// {
|
|||
|
|
// continue;
|
|||
|
|
// }
|
|||
|
|
// sensorsNeedingProcessing.Remove(squibRecord.SerialNumber);
|
|||
|
|
// var original = serialNumberToSensor[squibRecord.SerialNumber];
|
|||
|
|
// var modifiedBy = squibRecord.LastModifiedBy;
|
|||
|
|
// var lastModified = squibRecord.LastModified;
|
|||
|
|
// if( Math.Abs(lastModified.Subtract(original.LastModified).TotalSeconds) > 1)
|
|||
|
|
// {
|
|||
|
|
// list.Add(new CachedItem(squibRecord.SerialNumber, Resources.StringResources.Sensor, original.LastModified, lastModified));
|
|||
|
|
// }
|
|||
|
|
// }
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
|
|||
|
|
//#region DigitalInputs
|
|||
|
|
//hr = DbOperations.SensorsDigitalInGet(null, null, null, out var digitalIns);
|
|||
|
|
//if (0 == hr && null != digitalIns && digitalIns.Any())
|
|||
|
|
//{
|
|||
|
|
// foreach( var digitalIn in digitalIns)
|
|||
|
|
// {
|
|||
|
|
// if (!sensorsNeedingProcessing.Contains(digitalIn.SerialNumber)) { continue; }
|
|||
|
|
// sensorsNeedingProcessing.Remove(digitalIn.SerialNumber);
|
|||
|
|
// var original = serialNumberToSensor[digitalIn.SerialNumber];
|
|||
|
|
// var lastModified = digitalIn.LastModified;
|
|||
|
|
// if (Math.Abs(lastModified.Subtract(original.LastModified).TotalSeconds) > 1)
|
|||
|
|
// {
|
|||
|
|
// list.Add(new CachedItem(digitalIn.SerialNumber, Resources.StringResources.Sensor, original.LastModified, lastModified));
|
|||
|
|
// }
|
|||
|
|
// }
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
|
|||
|
|
//#region DigitalOutputs
|
|||
|
|
//hr = DbOperations.SensorsDigitalOutGet(null, null, out var digitalOutRecords);
|
|||
|
|
//if (0 == hr && null != digitalOutRecords && digitalOutRecords.Any())
|
|||
|
|
//{
|
|||
|
|
// foreach( var record in digitalOutRecords)
|
|||
|
|
// {
|
|||
|
|
// if (!sensorsNeedingProcessing.Contains(record.SerialNumber))
|
|||
|
|
// {
|
|||
|
|
// //either we didn't need to process this sensor, or we already have ...?
|
|||
|
|
// continue;
|
|||
|
|
// }
|
|||
|
|
// sensorsNeedingProcessing.Remove(record.SerialNumber);
|
|||
|
|
// if (IsTestSpecificDigitalOutput(record.SerialNumber)) { continue; }
|
|||
|
|
// var original = serialNumberToSensor[record.SerialNumber];
|
|||
|
|
// var lastModified = record.LastModified;
|
|||
|
|
// if (Math.Abs(lastModified.Subtract(original.LastModified).TotalSeconds) > 1)
|
|||
|
|
// {
|
|||
|
|
// list.Add(new CachedItem(record.SerialNumber, Resources.StringResources.Sensor,
|
|||
|
|
// original.LastModified, lastModified));
|
|||
|
|
// }
|
|||
|
|
// }
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
|
|||
|
|
//#endregion SENSORS IN DB
|
|||
|
|
|
|||
|
|
//#region Sensors NOT IN DB
|
|||
|
|
|
|||
|
|
//using (var eSensors = sensorsNeedingProcessing.GetEnumerator())
|
|||
|
|
//{
|
|||
|
|
// while (eSensors.MoveNext())
|
|||
|
|
// {
|
|||
|
|
// var original = serialNumberToSensor[eSensors.Current];
|
|||
|
|
// list.Add(new CachedItem(eSensors.Current, Resources.StringResources.Sensor, original.LastModified,
|
|||
|
|
// DateTime.MinValue));
|
|||
|
|
// }
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
//#endregion SENSORS
|
|||
|
|
|
|||
|
|
//#region Hardware
|
|||
|
|
|
|||
|
|
//var serialNumberToHardware = new Dictionary<string, IDASHardware>();
|
|||
|
|
//var hardwareNeedingProcessing = new HashSet<string>();
|
|||
|
|
//foreach (var h in hardware)
|
|||
|
|
//{
|
|||
|
|
// serialNumberToHardware[h.SerialNumber] = h;
|
|||
|
|
// hardwareNeedingProcessing.Add(h.SerialNumber);
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
//#region Hardware In DB
|
|||
|
|
|
|||
|
|
//foreach (var das in allDAS)
|
|||
|
|
//{
|
|||
|
|
// if (!hardwareNeedingProcessing.Contains(das.SerialNumber))
|
|||
|
|
// {
|
|||
|
|
// continue;
|
|||
|
|
// }
|
|||
|
|
// hardwareNeedingProcessing.Remove(das.SerialNumber);
|
|||
|
|
// var original = serialNumberToHardware[das.SerialNumber];
|
|||
|
|
// if (Math.Abs(das.LastModified.Subtract(original.LastModified).TotalSeconds) > 1)
|
|||
|
|
// {
|
|||
|
|
// list.Add(new CachedItem(das.SerialNumber, Resources.StringResources.Hardware, original.LastModified,
|
|||
|
|
// das.LastModified));
|
|||
|
|
// }
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
//#endregion Hardware In DB
|
|||
|
|
|
|||
|
|
//#region Hardware NOT IN DB
|
|||
|
|
|
|||
|
|
//using (var eHardware = hardwareNeedingProcessing.GetEnumerator())
|
|||
|
|
//{
|
|||
|
|
// while (eHardware.MoveNext())
|
|||
|
|
// {
|
|||
|
|
// var original = serialNumberToHardware[eHardware.Current];
|
|||
|
|
// list.Add(new CachedItem(original.SerialNumber, Resources.StringResources.Hardware,
|
|||
|
|
// original.LastModified, DateTime.MinValue));
|
|||
|
|
// }
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
//#endregion
|
|||
|
|
|
|||
|
|
//#endregion Hardware
|
|||
|
|
|
|||
|
|
//#region Calibrations
|
|||
|
|
//var serialNumberToLastCalibration = new Dictionary<string, ISensorCalibration>();
|
|||
|
|
//var serialNumberToLastCalibrationInDb = new Dictionary<string, ISensorCalibration>();
|
|||
|
|
//var calsNeedingProcessing = new HashSet<string>();
|
|||
|
|
|
|||
|
|
//foreach (var cal in sensorCalibrations)
|
|||
|
|
//{
|
|||
|
|
// if (!calsNeedingProcessing.Contains(cal.SerialNumber))
|
|||
|
|
// {
|
|||
|
|
// calsNeedingProcessing.Add(cal.SerialNumber);
|
|||
|
|
// serialNumberToLastCalibration[cal.SerialNumber] = cal;
|
|||
|
|
// }
|
|||
|
|
// else
|
|||
|
|
// {
|
|||
|
|
// if (serialNumberToLastCalibration[cal.SerialNumber].CalibrationDate < cal.CalibrationDate)
|
|||
|
|
// {
|
|||
|
|
// serialNumberToLastCalibration[cal.SerialNumber] = cal;
|
|||
|
|
// }
|
|||
|
|
// else if (serialNumberToLastCalibration[cal.SerialNumber].CalibrationDate == cal.CalibrationDate &&
|
|||
|
|
// serialNumberToLastCalibration[cal.SerialNumber].ModifyDate < cal.ModifyDate)
|
|||
|
|
// {
|
|||
|
|
// serialNumberToLastCalibration[cal.SerialNumber] = cal;
|
|||
|
|
// }
|
|||
|
|
// }
|
|||
|
|
//}
|
|||
|
|
|
|||
|
|
//#region CalibrationsInDb
|
|||
|
|
//hr = DbOperations.SensorCalibrationsGet(null, null, out var calRecords);
|
|||
|
|
//if( 0 == hr && null != calRecords && calRecords.Any())
|
|||
|
|
//{
|
|||
|
|
// foreach( var record in calRecords)
|
|||
|
|
// {
|
|||
|
|
// var sc = new SensorCalibration(record);
|
|||
|
|
// if (!serialNumberToLastCalibration.ContainsKey(sc.SerialNumber))
|
|||
|
|
// {
|
|||
|
|
// continue;
|
|||
|
|
// }
|
|||
|
|
// calsNeedingProcessing.Remove(sc.SerialNumber);
|
|||
|
|
// if (!serialNumberToLastCalibrationInDb.ContainsKey(sc.SerialNumber))
|
|||
|
|
// {
|
|||
|
|
// serialNumberToLastCalibrationInDb[sc.SerialNumber] = sc;
|
|||
|
|
// }
|
|||
|
|
// else if (serialNumberToLastCalibrationInDb[sc.SerialNumber].CalibrationDate <
|
|||
|
|
// sc.CalibrationDate)
|
|||
|
|
// {
|
|||
|
|
// serialNumberToLastCalibrationInDb[sc.SerialNumber] = sc;
|
|||
|
|
// }
|
|||
|
|
// else if (serialNumberToLastCalibrationInDb[sc.SerialNumber].CalibrationDate ==
|
|||
|
|
// sc.CalibrationDate &&
|
|||
|
|
// serialNumberToLastCalibrationInDb[sc.SerialNumber].ModifyDate < sc.ModifyDate)
|
|||
|
|
// {
|
|||
|
|
// serialNumberToLastCalibrationInDb[sc.SerialNumber] = sc;
|
|||
|
|
// }
|
|||
|
|
// }
|
|||
|
|
//}
|
|||
|
|
//using (var eCals = serialNumberToLastCalibrationInDb.GetEnumerator())
|
|||
|
|
//{
|
|||
|
|
// while (eCals.MoveNext())
|
|||
|
|
// {
|
|||
|
|
// var original = serialNumberToLastCalibration[eCals.Current.Key];
|
|||
|
|
// var dbVersion = eCals.Current.Value;
|
|||
|
|
// if (original.CalibrationDate < dbVersion.CalibrationDate)
|
|||
|
|
// {
|
|||
|
|
// list.Add(new CachedItem(original.SerialNumber, Resources.StringResources.SensorCal,
|
|||
|
|
// original.ModifyDate, dbVersion.ModifyDate));
|
|||
|
|
// }
|
|||
|
|
// else if (original.CalibrationDate == dbVersion.CalibrationDate)
|
|||
|
|
// {
|
|||
|
|
// if (Math.Abs(dbVersion.ModifyDate.Subtract(original.ModifyDate).TotalSeconds) > 1)
|
|||
|
|
// {
|
|||
|
|
// list.Add(new CachedItem(original.SerialNumber, Resources.StringResources.SensorCal,
|
|||
|
|
// original.ModifyDate, dbVersion.ModifyDate));
|
|||
|
|
// }
|
|||
|
|
// }
|
|||
|
|
// }
|
|||
|
|
//}
|
|||
|
|
//#endregion
|
|||
|
|
//#region Calibrations NOT IN DB
|
|||
|
|
|
|||
|
|
//using (var eCals = calsNeedingProcessing.GetEnumerator())
|
|||
|
|
//{
|
|||
|
|
// while (eCals.MoveNext())
|
|||
|
|
// {
|
|||
|
|
// var cal = serialNumberToLastCalibration[eCals.Current];
|
|||
|
|
// list.Add(new CachedItem(cal.SerialNumber, Resources.StringResources.SensorCal, cal.ModifyDate,
|
|||
|
|
// DateTime.MinValue));
|
|||
|
|
// }
|
|||
|
|
//}
|
|||
|
|
//#endregion
|
|||
|
|
//#endregion
|
|||
|
|
//CachedItems = list.ToArray();
|
|||
|
|
//OnPropertyChanged("CachedItems");
|
|||
|
|
//return list.Any();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static bool IsTestSpecificDigitalOutput(string serialNumber)
|
|||
|
|
{
|
|||
|
|
if (string.IsNullOrWhiteSpace(serialNumber))
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
return serialNumber.StartsWith(TEST_SPECIFIC_DIGITAL_OUT);
|
|||
|
|
}
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region Constants and Enums
|
|||
|
|
|
|||
|
|
private const string TEST_SPECIFIC_DIGITAL_OUT = "TSD_";
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region Properties
|
|||
|
|
|
|||
|
|
public ICachedItem[] CachedItems { get; set; } = new ICachedItem[0];
|
|||
|
|
|
|||
|
|
public bool IsDirty => false;
|
|||
|
|
|
|||
|
|
private bool _isBusy;
|
|||
|
|
|
|||
|
|
public bool IsBusy
|
|||
|
|
{
|
|||
|
|
get => _isBusy;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
_isBusy = value;
|
|||
|
|
OnPropertyChanged("IsBusy");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private bool _isMenuIncluded;
|
|||
|
|
|
|||
|
|
public bool IsMenuIncluded
|
|||
|
|
{
|
|||
|
|
get => _isMenuIncluded;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
_isMenuIncluded = value;
|
|||
|
|
OnPropertyChanged("IsMenuIncluded");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private bool _isNavigationIncluded;
|
|||
|
|
|
|||
|
|
public bool IsNavigationIncluded
|
|||
|
|
{
|
|||
|
|
get => _isNavigationIncluded;
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
_isNavigationIncluded = value;
|
|||
|
|
OnPropertyChanged("IsNavigationIncluded");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public bool HasOutofDateCachedItems => CachedItems.Any();
|
|||
|
|
|
|||
|
|
public bool HasMissingSensors
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
return CachedItems.Any() && Array.Exists(CachedItems, item => item.DBTime.Equals(DateTime.MinValue) && item.ObjectType == Resources.StringResources.Sensor);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion Properties
|
|||
|
|
|
|||
|
|
#region Commands
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
}
|
|||
|
|
}
|