This commit is contained in:
2026-04-17 14:55:32 -04:00
commit bc3ac1d4c9
18017 changed files with 4371742 additions and 0 deletions

View File

@@ -0,0 +1,622 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using DTS.Common.Events;
using DTS.Common.Events.TTSImport;
using DTS.Common.Interface.DataRecorders;
using DTS.Common.Interface.TestSetups.Imports.TTS;
using DTS.Common.Interface.TestSetups.Imports.TTS.ReadFile;
using DTS.Common.Utilities.Logging;
using TTSImport.Resources;
using Application = System.Windows.Application;
using DASChannel = TTSImport.Model.DASChannel;
using MessageBox = System.Windows.MessageBox;
using DTS.Common.DAS.Concepts;
using DTS.Common.Enums;
using TTSImport.Model;
using Prism.Events;
using Unity;
using DTS.Common.Interactivity;
using Prism.Regions;
using Prism.Commands;
// ReSharper disable CheckNamespace
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable InconsistentNaming
namespace TTSImport
{
/// <summary>
/// this class handles Level Trigger edit/create functionality
/// </summary>
[PartCreationPolicy(CreationPolicy.Shared)]
public class AnalogChannelsViewModel : IAnalogChannelsViewModel
{
/// <summary>
/// The Hardware Scan view
/// </summary>
public IAnalogChannelsView 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 TechnologyDomainEditViewModel.
/// </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 AnalogChannelsViewModel(IAnalogChannelsView 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);
_eventAggregator.GetEvent<AssignedChannelsChangedEvent>().Subscribe(OnAssignedChannelsChangedEvent,
ThreadOption.PublisherThread, true);
_eventAggregator.GetEvent<TTSImportHardwareScanFinishedEvent>()
.Subscribe(OnHardwareScanComplete, ThreadOption.PublisherThread, true);
_eventAggregator.GetEvent<EIDMappingEvent>()
.Subscribe(OnEIDComplete, ThreadOption.PublisherThread, true);
}
#endregion
#region Methods
private void OnEIDComplete(IDictionary<string, string> sensorIdToChannelId)
{
var channelIdToSensorId = new Dictionary<string, string>();
using (var e = sensorIdToChannelId.GetEnumerator())
{
while (e.MoveNext())
{
channelIdToSensorId[e.Current.Value] = e.Current.Key;
}
}
_hardwareChannelIdToSensorId = channelIdToSensorId;
}
public string Validate()
{
var bEmptyChannelCodes = false;
foreach (var channel in DASChannels)
{
if (null == channel.Channel)
{
continue;
}
if (string.IsNullOrWhiteSpace(channel.ToyotaCode))
{
bEmptyChannelCodes = true;
}
}
return bEmptyChannelCodes ? StringResources.EmptyChannelCodeWarning : string.Empty;
}
private void OnHardwareScanComplete(List<IDASHardware> hardware)
{
_hardware = hardware;
}
private void OnAssignedChannelsChangedEvent(ITTSSetup setup)
{
if (!Application.Current.Dispatcher.CheckAccess())
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
OnAssignedChannelsChangedEvent(setup);
}));
return;
}
_setup = setup;
if (null == _hardware || null == _setup) { return; }
var channels = new ObservableCollection<DASChannel>();
var remainingChannels = new ObservableCollection<ITTSChannelRecord>();
var channelIdToDASChannel = new Dictionary<string, DASChannel>();
foreach (var das in _hardware)
{
if (das.IsSLICEEthernetController) { continue; }
var ichannels = das.GetIHardwareChannels();
foreach (var ch in ichannels)
{
if (!ch.IsAnalog) { continue; }
var newChannel = new DASChannel(ch);
if (_hardwareChannelIdToSensorId.ContainsKey(ch.GetId()))
{
newChannel.EID = _hardwareChannelIdToSensorId[ch.GetId()];
}
channels.Add(newChannel);
channelIdToDASChannel[newChannel.HardwareChannel.GetId()] = newChannel;
}
}
foreach (var channelRecord in _setup.Channels)
{
if (channelRecord.IsEmptyRecord) { continue; }
if (channelRecord.IsDigitalInput) { continue; }
if (channelRecord.IsDigitalOutput) { continue; }
if (channelRecord.IsSquib) { continue; }
if (!channelRecord.IsChannelCodeValid) { continue; }
if (channelRecord.ChannelCode == TTSChannelRecord.NONE) { continue; }
if (null != channelRecord.HardwareChannel)
{
var key = channelRecord.HardwareChannel.GetId();
if (channelIdToDASChannel.ContainsKey(key))
{
channelIdToDASChannel[key].SetITTSChannelRecord(channelRecord);
}
}
else
{
remainingChannels.Add(channelRecord);
}
}
DASChannels = channels;
RemainingChannels = remainingChannels;
OnPropertyChanged("DASChannels");
OnPropertyChanged("RemainingChannels");
}
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
});
}
#endregion
#region Properties
private ITTSSetup _setup;
private IList<IDASHardware> _hardware;
private IDictionary<string, string> _hardwareChannelIdToSensorId = new Dictionary<string, string>();
public bool IsDirty { get; private set; }
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 AssignEnabled { get; set; }
public bool RemoveEnabled { get; set; }
public bool EnableOrDisableEnabled { get; set; }
public ObservableCollection<DASChannel> DASChannels { get; set; } = new ObservableCollection<DASChannel>();
public ObservableCollection<ITTSChannelRecord> RemainingChannels { get; set; } =
new ObservableCollection<ITTSChannelRecord>();
private ITTSChannelRecord _selectedRemainingChannel;
public ITTSChannelRecord SelectedRemainingChannel
{
get => _selectedRemainingChannel;
set
{
_selectedRemainingChannel = value;
if (null == _selectedRemainingChannel || null == SelectedDASChannel) return;
AssignEnabled = true;
OnPropertyChanged("AssignEnabled");
}
}
private DASChannel _selectedDASChannel;
public DASChannel SelectedDASChannel
{
get => _selectedDASChannel;
set
{
_selectedDASChannel = value;
DetermineRemoveEnableStatus();
}
}
public void DetermineRemoveEnableStatus()
{
if (_selectedDASChannel?.Channel != null)
{
if (!string.IsNullOrWhiteSpace(_selectedDASChannel.EID) &&
_selectedDASChannel.EID == _selectedDASChannel.Channel.SensorEID)
{
//can only be replaced, can't be removed
RemoveEnabled = false;
}
RemoveEnabled = true;
}
else
{
RemoveEnabled = false;
}
OnPropertyChanged("RemoveEnabled");
AssignEnabled = null != _selectedDASChannel && null != _selectedRemainingChannel;
OnPropertyChanged("AssignEnabled");
EnableOrDisableEnabled = null != _selectedDASChannel && null != _selectedDASChannel.Channel;
OnPropertyChanged("EnableOrDisableEnabled");
OnPropertyChanged("EnableOrDisableText");
}
public string EnableOrDisableText
{
get
{
if (SelectedDASChannel?.Channel == null)
{
return StringResources.Analog_Enable;
}
return SelectedDASChannel.Channel.Disabled
? StringResources.Analog_Enable
: StringResources.Analog_Disable;
}
}
#endregion Properties
#region Commands
#region assign
/// <summary>
/// Assign a channel code to a channel
/// </summary>
private DelegateCommand _assignCommand;
public DelegateCommand AssignCommand => _assignCommand ?? (_assignCommand = new DelegateCommand(Assign));
private void Assign()
{
if (SelectedRemainingChannel == null) { return; }
var error = string.Empty;
if (!VoltageIsValid(SelectedRemainingChannel, SelectedDASChannel, out error))
{
var window = Application.Current.MainWindow;
if (null == window) { return; }
MessageBox.Show(window, error, StringResources.AssignSensorExcitationError, MessageBoxButton.OK);
return;
}
_eventAggregator.GetEvent<TTSImportTestSetupChangedEvent>().Publish(_setup);
//BEFORE we go any further, check the state of sensor ids
//if the channel has a sensor id AND there's a sensor on the channel with the same id
//then prompt on replacing the id
//IF the channel has a sensor id and there's no sensor on the channel BUT the new sensor has a different id
//then prompt on replacing the id
var bReplacingID = false;
if (!string.IsNullOrWhiteSpace(SelectedDASChannel.EID))
{
if (null != SelectedRemainingChannel && SelectedRemainingChannel.SensorEID != SelectedDASChannel.EID)
{
bReplacingID = true;
}
//if existing channel has this ID, we will need to clear out the old id and assign a new one...
if (null != SelectedDASChannel.Channel &&
SelectedDASChannel.Channel.SensorEID == SelectedDASChannel.EID)
{
bReplacingID = true;
}
}
else if (_setup.RequireEIDFound && !string.IsNullOrWhiteSpace(SelectedRemainingChannel.SensorEID))
{
var window = Application.Current.MainWindow;
if (null == window) { return; }
var msg = string.Format(StringResources.AllowEIDToBlankChannelChallenge, SelectedRemainingChannel.SensorSerialNumber);
msg = msg.Replace("\\r\\n", "\r\n");
var dialogResult = MessageBox.Show(window, msg, StringResources.UserFeedbackRequired, MessageBoxButton.OKCancel);
APILogger.Log(msg, $"User pressed {dialogResult.ToString()}");
if (dialogResult == MessageBoxResult.OK)
{
SelectedRemainingChannel.SensorEID = string.Empty;
AssignWork();
}
return;
}
if (bReplacingID)
{
var window = Application.Current.MainWindow;
if (null == window) { return; }
var dialogResult = MessageBox.Show(window, StringResources.AssignSensorPrompt, StringResources.UserFeedbackRequired, MessageBoxButton.YesNo);
APILogger.Log(StringResources.AssignSensorPrompt, $"User pressed {dialogResult.ToString()}");
if (dialogResult == MessageBoxResult.Yes)
{
AssignWork();
}
return;
}
AssignWork();
}
/// <summary>
/// returns True if the sensor's voltage is supported by the hardware channel, or an error message and False if not
/// </summary>
/// <param name="selectedRemainingChannel"></param>
/// <param name="selectedDASChannel"></param>
/// <param name="error"></param>
/// <returns></returns>
private bool VoltageIsValid(ITTSChannelRecord selectedRemainingChannel, DASChannel selectedDASChannel, out string error)
{
error = string.Empty;
var voltageEnum = ExcitationVoltageOptions.ExcitationVoltageOption.Undefined;
try
{
voltageEnum = Test.Module.Channel.Sensor.GetExcitationVoltageEnumFromMagnitude(selectedRemainingChannel.SensorExcitationVolts);
}
catch { } //GetExcitationVoltageEnumFromMagnitude will throw an exception if an invalid voltage is passed to it
if (selectedDASChannel.HardwareChannel.IsSupportedExcitation(voltageEnum)) return true;
error = string.Format(StringResources.InvalidExcitationAssignment, selectedRemainingChannel.SensorExcitationVolts);
return false;
}
/// <summary>
/// removes the channel as a selected channel for any level triggers
/// removes the channel as a possible channel for any level triggers
/// </summary>
/// <param name="channel"></param>
private void RemoveFromLevelTriggers(ITTSChannelRecord channel)
{
if (null != _setup)
{
foreach (var lt in _setup.LevelTriggers)
{
lt.Remove(channel);
}
}
}
/// <summary>
/// adds the channel as a possible channel to any level triggers
/// </summary>
/// <param name="channel"></param>
private void AddToLevelTriggers(ITTSChannelRecord channel)
{
if (null != _setup)
{
foreach (var lt in _setup.LevelTriggers)
{
lt.Add(channel);
}
}
}
/// <summary>
/// assigns a channel record to a physical channel
/// </summary>
private void AssignWork()
{
if (null == SelectedDASChannel || null == SelectedRemainingChannel) { return; }
var excitation =
Test.Module.Channel.Sensor.GetExcitationVoltageEnumFromMagnitude(SelectedRemainingChannel
.SensorExcitationVolts);
if (!SelectedDASChannel.HardwareChannel.IsSupportedExcitation(excitation))
{
var window = Application.Current.MainWindow;
if (null == window) { return; }
var msg = string.Format(StringResources.ExcitationNotSupportedByChannel, excitation.ToString());
var result = MessageBox.Show(window, msg, StringResources.Warning, MessageBoxButton.OK);
APILogger.Log(msg, $"user pressed {result.ToString()}");
return;
}
if (null != SelectedDASChannel.Channel)
{
if (!string.IsNullOrWhiteSpace(SelectedDASChannel.EID))
{
SelectedDASChannel.Channel.SensorEID = "";
}
RemainingChannels.Add(SelectedDASChannel.Channel);
RemoveFromLevelTriggers(SelectedDASChannel.Channel);
}
if (!string.IsNullOrWhiteSpace(SelectedDASChannel.EID))
{
SelectedRemainingChannel.SensorEID = SelectedDASChannel.EID;
}
SelectedDASChannel.SetITTSChannelRecord(SelectedRemainingChannel);
AddToLevelTriggers(SelectedDASChannel.Channel);
var channel = SelectedRemainingChannel;
var index = RemainingChannels.IndexOf(channel);
SelectedRemainingChannel = null;
RemainingChannels.Remove(channel);
CollectionViewSource.GetDefaultView(DASChannels)?.Refresh();
if (index < RemainingChannels.Count)
{
SelectedRemainingChannel = RemainingChannels[index];
OnPropertyChanged("SelectedRemainingChannel");
}
else if (RemainingChannels.Count > 0)
{
SelectedRemainingChannel = RemainingChannels[index - 1];
OnPropertyChanged("SelectedRemainingChannel");
}
index = DASChannels.IndexOf(SelectedDASChannel);
for (var i = index; i < DASChannels.Count; i++)
{
var dasChannel = DASChannels[i];
if (null != dasChannel.Channel) { continue; }
SelectedDASChannel = dasChannel;
OnPropertyChanged("SelectedDASChannel");
return;
}
//didn't find a match, start from the beginning?
for (var i = 0; i < index; i++)
{
var dasChannel = DASChannels[i];
if (null != dasChannel.Channel)
{
continue;
}
SelectedDASChannel = dasChannel;
OnPropertyChanged("SelectedDASChannel");
return;
}
//if we get here there's no new channel to go to, change remove/enable/disable button status
OnPropertyChanged("SelectedDASChannel");
DetermineRemoveEnableStatus();
}
#endregion
#region remove
private DelegateCommand _removeCommand;
public DelegateCommand RemoveCommand => _removeCommand ?? (_removeCommand = new DelegateCommand(Remove));
/// <summary>
/// remove a hardware channel assignment (does not remove the channel from the test setup though?)
/// </summary>
private void Remove()
{
if (null == SelectedDASChannel || null == SelectedDASChannel.Channel) { return; }
_eventAggregator.GetEvent<TTSImportTestSetupChangedEvent>().Publish(_setup);
RemainingChannels.Add(SelectedDASChannel.Channel);
SelectedDASChannel.SetITTSChannelRecord(null);
CollectionViewSource.GetDefaultView(DASChannels)?.Refresh();
var index = DASChannels.IndexOf(SelectedDASChannel);
for (var i = index; i < DASChannels.Count; i++)
{
if (null == DASChannels[i].Channel) { continue; }
SelectedDASChannel = DASChannels[i];
OnPropertyChanged("SelectedDASChannel");
return;
}
for (var i = 0; i < index; i++)
{
if (null == DASChannels[i].Channel) { continue; }
SelectedDASChannel = DASChannels[i];
OnPropertyChanged("SelectedDASChannel");
return;
}
//if we get here there's no new channel to go to, but we need to set the remove/enable/disable button status
OnPropertyChanged("SelectedDASChannel");
DetermineRemoveEnableStatus();
}
#endregion
#region enableordisable
private DelegateCommand _enableOrDisableCommand;
public DelegateCommand EnableOrDisableCommand =>
_enableOrDisableCommand ?? (_enableOrDisableCommand = new DelegateCommand(EnableOrDisable));
/// <summary>
/// enables or disables a channel in the test.
/// </summary>
private void EnableOrDisable()
{
if (SelectedDASChannel?.Channel == null) { return; }
SelectedDASChannel.Channel.Disabled = !SelectedDASChannel.Channel.Disabled;
SelectedDASChannel.Disabled = SelectedDASChannel.Channel.Disabled;
if (SelectedDASChannel.Channel.Disabled)
{
RemoveFromLevelTriggers(SelectedDASChannel.Channel);
}
else
{
AddToLevelTriggers(SelectedDASChannel.Channel);
}
OnPropertyChanged("EnableOrDisableText");
_eventAggregator.GetEvent<TTSImportTestSetupChangedEvent>().Publish(_setup);
}
#endregion
#endregion
}
}

View File

@@ -0,0 +1,481 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using DTS.Common.Events;
using DTS.Common.Events.TTSImport;
using DTS.Common.Interface.DataRecorders;
using DTS.Common.Interface.TestSetups.Imports.TTS.DIChannels;
using DTS.Common.Interface.TestSetups.Imports.TTS.ReadFile;
using Prism.Events;
using Unity;
using DTS.Common.Interactivity;
using Prism.Regions;
using Prism.Commands;
using TTSImport.Model;
using TTSImport.Resources;
// ReSharper disable CheckNamespace
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable InconsistentNaming
namespace TTSImport
{
/// <summary>
/// this class handles Level Trigger edit/create functionality
/// </summary>
[PartCreationPolicy(CreationPolicy.Shared)]
public class DigitalInputChannelsViewModel : IDigitalInputChannelsViewModel
{
/// <summary>
/// The Hardware Scan view
/// </summary>
public IDigitalInputChannelsView View { get; set; }
private IEventAggregator _eventAggregator { get; }
private IRegionManager _regionManager;
private IUnityContainer UnityContainer { get; }
public InteractionRequest<Notification> NotificationRequest { get; }
public InteractionRequest<Confirmation> ConfirmationRequest { get; }
/// <summary>
/// Creates a new instance of the TechnologyDomainEditViewModel.
/// </summary>
/// <param name="view">The IDigitalInputChannelsView.</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 DigitalInputChannelsViewModel(IDigitalInputChannelsView 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);
_eventAggregator.GetEvent<AssignedChannelsChangedEvent>().Subscribe(OnAssignedChannelsChangedEvent,
ThreadOption.PublisherThread, true);
_eventAggregator.GetEvent<TTSImportHardwareScanFinishedEvent>()
.Subscribe(OnHardwareScanComplete, ThreadOption.PublisherThread, true);
}
#region Methods
private void OnHardwareScanComplete(List<IDASHardware> hardware)
{
_hardware = hardware;
}
private void OnAssignedChannelsChangedEvent(ITTSSetup setup)
{
if (!Application.Current.Dispatcher.CheckAccess())
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
OnAssignedChannelsChangedEvent(setup);
}));
return;
}
_setup = setup;
if (null == _hardware || null == _setup) { return; }
var channels = new ObservableCollection<DASChannel>();
var remainingChannels = new ObservableCollection<ITTSChannelRecord>();
var channelIdToChannelRecord = new Dictionary<string, DASChannel>();
foreach (var das in _hardware)
{
var ichannels = das.GetIHardwareChannels();
foreach (var ch in ichannels)
{
if (!ch.IsDigitalIn) { continue; }
var newChannel = new DASChannel(ch);
channels.Add(newChannel);
channelIdToChannelRecord[ch.GetId()] = newChannel;
}
}
foreach (var channelRecord in _setup.Channels)
{
if (channelRecord.IsEmptyRecord)
{
continue;
}
if (!channelRecord.IsDigitalInput)
{
continue;
}
if (!channelRecord.IsChannelCodeValid)
{
continue;
}
if (channelRecord.ChannelCode == TTSChannelRecord.NONE)
{
continue;
}
if (null != channelRecord.HardwareChannel &&
channelIdToChannelRecord.ContainsKey(channelRecord.HardwareChannel.GetId()))
{
channelIdToChannelRecord[channelRecord.HardwareChannel.GetId()].SetITTSChannelRecord(channelRecord);
}
else
{
remainingChannels.Add(channelRecord);
}
}
DASChannels = channels;
RemainingChannels = remainingChannels;
OnPropertyChanged("DASChannels");
OnPropertyChanged("RemainingChannels");
}
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
});
}
#endregion
#region Properties
private ITTSSetup _setup;
private IList<IDASHardware> _hardware;
public bool IsDirty { get; private set; }
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 AssignEnabled { get; set; }
public bool RemoveEnabled { get; set; }
public bool EnableOrDisableEnabled { get; set; }
public ObservableCollection<DASChannel> DASChannels { get; set; } = new ObservableCollection<DASChannel>();
public ObservableCollection<ITTSChannelRecord> RemainingChannels { get; set; } =
new ObservableCollection<ITTSChannelRecord>();
private ITTSChannelRecord _selectedRemainingChannel;
public ITTSChannelRecord SelectedRemainingChannel
{
get => _selectedRemainingChannel;
set
{
_selectedRemainingChannel = value;
if (null == _selectedRemainingChannel || null == SelectedDASChannel) return;
AssignEnabled = true;
OnPropertyChanged("AssignEnabled");
}
}
private DASChannel _selectedDASChannel;
public DASChannel SelectedDASChannel
{
get => _selectedDASChannel;
set
{
_selectedDASChannel = value;
DetermineRemoveEnableStatus();
}
}
private void DetermineRemoveEnableStatus()
{
if (_selectedDASChannel?.Channel != null)
{
if (!string.IsNullOrWhiteSpace(_selectedDASChannel.EID) &&
_selectedDASChannel.EID == _selectedDASChannel.Channel.SensorEID)
{
//can only be replaced, can't be removed
RemoveEnabled = false;
}
RemoveEnabled = true;
}
else
{
RemoveEnabled = false;
}
OnPropertyChanged("RemoveEnabled");
AssignEnabled = null != _selectedDASChannel && null != _selectedRemainingChannel;
OnPropertyChanged("AssignEnabled");
EnableOrDisableEnabled = _selectedDASChannel?.Channel != null;
OnPropertyChanged("EnableOrDisableEnabled");
OnPropertyChanged("EnableOrDisableText");
}
public string EnableOrDisableText
{
get
{
if (SelectedDASChannel?.Channel == null)
{
return StringResources.Analog_Enable;
}
return SelectedDASChannel.Channel.Disabled
? StringResources.Analog_Enable
: StringResources.Analog_Disable;
}
}
#endregion Properties
#region Commands
#region assign
/// <summary>
/// Assign a channel code to a channel
/// </summary>
private DelegateCommand _assignCommand;
public DelegateCommand AssignCommand => _assignCommand ?? (_assignCommand = new DelegateCommand(Assign));
private void Assign()
{
if (SelectedRemainingChannel == null) { return; }
_eventAggregator.GetEvent<TTSImportTestSetupChangedEvent>().Publish(_setup);
//BEFORE we go any further, check the state of sensor ids
//if the channel has a sensor id AND there's a sensor on the channel with the same id
//then prompt on replacing the id
//IF the channel has a sensor id and there's no sensor on the channel BUT the new sensor has a different id
//then prompt on replacing the id
var bReplacingID = false;
if (!string.IsNullOrWhiteSpace(SelectedDASChannel.EID))
{
if (null != SelectedRemainingChannel && SelectedRemainingChannel.SensorEID != SelectedDASChannel.EID)
{
bReplacingID = true;
}
//if existing channel has this ID, we will need to clear out the old id and assign a new one...
if (null != SelectedDASChannel.Channel && SelectedDASChannel.Channel.SensorEID == SelectedDASChannel.EID)
{
bReplacingID = true;
}
}
if (bReplacingID)
{
Task.Run(() =>
{
var dialogResult = MessageBox.Show(StringResources.AssignSensorPrompt, StringResources.UserFeedbackRequired, MessageBoxButton.YesNo);
if (dialogResult == MessageBoxResult.Yes)
{
Application.Current.Dispatcher.BeginInvoke(new Action(AssignWork));
}
});
return;
}
AssignWork();
}
/// <summary>
/// assigns a channel record to a physical channel
/// </summary>
private void AssignWork()
{
if (null != SelectedDASChannel.Channel)
{
if (!string.IsNullOrWhiteSpace(SelectedDASChannel.EID))
{
SelectedDASChannel.Channel.SensorEID = "";
}
RemainingChannels.Add(SelectedDASChannel.Channel);
}
if (!string.IsNullOrWhiteSpace(SelectedDASChannel.EID))
{
SelectedRemainingChannel.SensorEID = SelectedDASChannel.EID;
}
SelectedRemainingChannel.DigitalInputMode = DTS.Common.Enums.DigitalInputModes.CCNO;
SelectedDASChannel.SetITTSChannelRecord(SelectedRemainingChannel);
var channel = SelectedRemainingChannel;
var index = RemainingChannels.IndexOf(channel);
SelectedRemainingChannel = null;
RemainingChannels.Remove(channel);
CollectionViewSource.GetDefaultView(DASChannels)?.Refresh();
if (index < RemainingChannels.Count)
{
SelectedRemainingChannel = RemainingChannels[index];
OnPropertyChanged("SelectedRemainingChannel");
}
else if (RemainingChannels.Count > 0)
{
SelectedRemainingChannel = RemainingChannels[index - 1];
OnPropertyChanged("SelectedRemainingChannel");
}
index = DASChannels.IndexOf(SelectedDASChannel);
for (var i = index; i < DASChannels.Count; i++)
{
var dasChannel = DASChannels[i];
if (null != dasChannel.Channel) { continue; }
SelectedDASChannel = dasChannel;
OnPropertyChanged("SelectedDASChannel");
return;
}
//didn't find a match, start from the beginning?
for (var i = 0; i < index; i++)
{
var dasChannel = DASChannels[i];
if (null != dasChannel.Channel)
{
continue;
}
SelectedDASChannel = dasChannel;
OnPropertyChanged("SelectedDASChannel");
return;
}
//if we get here there's no new channel to go to, but we need to set the remove/enable/disable button status
OnPropertyChanged("SelectedDASChannel");
DetermineRemoveEnableStatus();
}
#endregion
#region remove
private DelegateCommand _removeCommand;
public DelegateCommand RemoveCommand => _removeCommand ?? (_removeCommand = new DelegateCommand(Remove));
/// <summary>
/// remove a hardware channel assignment (does not remove the channel from the test setup though?)
/// </summary>
private void Remove()
{
if (null == SelectedDASChannel || null == SelectedDASChannel.Channel) { return; }
_eventAggregator.GetEvent<TTSImportTestSetupChangedEvent>().Publish(_setup);
RemainingChannels.Add(SelectedDASChannel.Channel);
SelectedDASChannel.SetITTSChannelRecord(null);
CollectionViewSource.GetDefaultView(DASChannels)?.Refresh();
var index = DASChannels.IndexOf(SelectedDASChannel);
for (var i = index; i < DASChannels.Count; i++)
{
if (null == DASChannels[i].Channel) { continue; }
SelectedDASChannel = DASChannels[i];
OnPropertyChanged("SelectedDASChannel");
return;
}
for (var i = 0; i < index; i++)
{
if (null == DASChannels[i].Channel) { continue; }
SelectedDASChannel = DASChannels[i];
OnPropertyChanged("SelectedDASChannel");
return;
}
//if we get here there's no new channel to go to, but we need to set the remove/enable/disable button status
OnPropertyChanged("SelectedDASChannel");
DetermineRemoveEnableStatus();
}
#endregion
#region enableordisable
private DelegateCommand _enableOrDisableCommand;
public DelegateCommand EnableOrDisableCommand =>
_enableOrDisableCommand ?? (_enableOrDisableCommand = new DelegateCommand(EnableOrDisable));
/// <summary>
/// enables or disables a channel in the test.
/// </summary>
private void EnableOrDisable()
{
if (SelectedDASChannel?.Channel == null) { return; }
SelectedDASChannel.Channel.Disabled = !SelectedDASChannel.Channel.Disabled;
SelectedDASChannel.Disabled = SelectedDASChannel.Channel.Disabled;
OnPropertyChanged("EnableOrDisableText");
_eventAggregator.GetEvent<TTSImportTestSetupChangedEvent>().Publish(_setup);
}
#endregion
#endregion
/// <inheritdoc />
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -0,0 +1,302 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using DTS.Common.Events;
using DTS.Common.Events.TTSImport;
using DTS.Common.Interface.DataRecorders;
using DTS.Common.Interface.TestSetups.Imports.TTS.DOChannels;
using DTS.Common.Interface.TestSetups.Imports.TTS.ReadFile;
using Prism.Events;
using Unity;
using DTS.Common.Interactivity;
using Prism.Regions;
using Prism.Commands;
using TTSImport.Model;
using TTSImport.Resources;
// ReSharper disable CheckNamespace
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable InconsistentNaming
namespace TTSImport
{
/// <summary>
/// this class handles Level Trigger edit/create functionality
/// </summary>
[PartCreationPolicy(CreationPolicy.Shared)]
public class DigitalOutputChannelsViewModel : IDigitalOutputChannelsViewModel
{
/// <summary>
/// The Hardware Scan view
/// </summary>
public IDigitalOutputChannelsView View { get; set; }
private IEventAggregator _eventAggregator { get; }
private IRegionManager _regionManager;
private IUnityContainer UnityContainer { get; }
public InteractionRequest<Notification> NotificationRequest { get; }
public InteractionRequest<Confirmation> ConfirmationRequest { get; }
/// <summary>
/// Creates a new instance of the TechnologyDomainEditViewModel.
/// </summary>
/// <param name="view">The IDigitalOutputChannelsView.</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 DigitalOutputChannelsViewModel(IDigitalOutputChannelsView 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);
_eventAggregator.GetEvent<AssignedChannelsChangedEvent>().Subscribe(OnAssignedChannelsChangedEvent,
ThreadOption.PublisherThread, true);
_eventAggregator.GetEvent<TTSImportHardwareScanFinishedEvent>()
.Subscribe(OnHardwareScanComplete, ThreadOption.PublisherThread, true);
_eventAggregator.GetEvent<EIDMappingEvent>()
.Subscribe(OnEIDComplete, ThreadOption.PublisherThread, true);
}
#region Methods
private void OnEIDComplete(IDictionary<string, string> sensorIdToChannelId)
{
var channelIdToSensorId = new Dictionary<string, string>();
using (var e = sensorIdToChannelId.GetEnumerator())
{
while (e.MoveNext())
{
channelIdToSensorId[e.Current.Value] = e.Current.Key;
}
}
_hardwareChannelIdToSensorId = channelIdToSensorId;
}
private void OnHardwareScanComplete(List<IDASHardware> hardware)
{
_hardware = hardware;
}
private void OnAssignedChannelsChangedEvent(ITTSSetup setup)
{
if (!Application.Current.Dispatcher.CheckAccess())
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
OnAssignedChannelsChangedEvent(setup);
}));
return;
}
_setup = setup;
if (null == _hardware || null == _setup) { return; }
var channels = new ObservableCollection<DASChannel>();
var channelIdToChannelRecord = new Dictionary<string, ITTSChannelRecord>();
foreach (var ch in _setup.Channels)
{
if (!ch.IsDigitalOutput) { continue; }
if (null == ch.HardwareChannel) { continue; }
if (ch.ChannelCode == TTSChannelRecord.NONE) { continue; }
channelIdToChannelRecord[ch.HardwareChannel.GetId()] = ch;
}
var channelIdToDASChannel = new Dictionary<string, DASChannel>();
foreach (var das in _hardware)
{
var ichannels = das.GetIHardwareChannels();
foreach (var ch in ichannels)
{
if (!ch.IsDigitalOut) { continue; }
var newChannel = new DASChannel(ch, _setup);
if (_hardwareChannelIdToSensorId.ContainsKey(ch.GetId()))
{
newChannel.EID = _hardwareChannelIdToSensorId[ch.GetId()];
}
channels.Add(newChannel);
channelIdToDASChannel[newChannel.HardwareChannel.GetId()] = newChannel;
if (channelIdToChannelRecord.ContainsKey(ch.GetId()))
{
newChannel.SetITTSChannelRecord(channelIdToChannelRecord[ch.GetId()]);
newChannel.Channel.ChannelCode = $"Digital Out {ch.ToString()}";
newChannel.Channel.SensorSerialNumber = newChannel.Channel.ChannelCode;
}
}
}
using (var enumChannels = channelIdToDASChannel.GetEnumerator())
{
while (enumChannels.MoveNext())
{
if (null == enumChannels.Current.Value.Channel)
{
//CREATE new channel
var ch = new TTSChannelRecord();
ch.ChannelCode = $"Digital Out {enumChannels.Current.Value.HardwareChannel?.ToString()}";
ch.IsChannelCodeValid = true;
ch.IsDigitalOutput = true;
ch.SensorEU = "V";
ch.HardwareChannel = enumChannels.Current.Value.HardwareChannel;
//ch.SensorSerialNumber = ch.ChannelCode;
enumChannels.Current.Value.SetITTSChannelRecord(ch);
}
}
}
DASChannels = channels;
OnPropertyChanged("DASChannels");
CollectionViewSource.GetDefaultView(DASChannels)?.Refresh();
}
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
});
}
#endregion
#region Properties
private ITTSSetup _setup;
private IList<IDASHardware> _hardware;
private IDictionary<string, string> _hardwareChannelIdToSensorId = new Dictionary<string, string>();
public bool IsDirty { get; private set; }
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 ObservableCollection<DASChannel> DASChannels { get; set; } = new ObservableCollection<DASChannel>();
private DASChannel _selectedDASChannel;
public DASChannel SelectedDASChannel
{
get => _selectedDASChannel;
set => _selectedDASChannel = value;
}
public string EnableOrDisableText
{
get
{
if (SelectedDASChannel?.Channel == null)
{
return StringResources.Analog_Enable;
}
return SelectedDASChannel.Channel.Disabled
? StringResources.Analog_Enable
: StringResources.Analog_Disable;
}
}
#endregion Properties
#region Commands
#endregion
/// <inheritdoc />
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -0,0 +1,861 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using DTS.Common.Events;
using DTS.Common.Interface.TestSetups.Imports.TTS;
using DTS.Common.Interface.TestSetups.Imports.TTS.ReadFile;
using Prism.Events;
using Unity;
using DTS.Common.Interactivity;
using Prism.Regions;
using Prism.Commands;
using TTSImport.Model;
using TTSImport.Resources;
using Application = System.Windows.Application;
namespace TTSImport
{
/// <summary>
/// this class handles Level Trigger edit/create functionality
/// </summary>
[PartCreationPolicy(CreationPolicy.Shared)]
public class EditFileViewModel : IEditFileViewModel
{
/// <summary>
/// The Hardware Scan view
/// </summary>
public IEditFileView View { get; set; }
private const string DOUBLEUPTO15 = "0.###############"; //Write up to 15 decimal places to .csv
private const string STRINGWRITEFORMAT_CHANNELRANGE = DOUBLEUPTO15;
private const string STRINGWRITEFORMAT_SENSITIVITY = DOUBLEUPTO15;
private const string STRINGWRITEFORMAT_CAPACITY = DOUBLEUPTO15;
private IEventAggregator _eventAggregator { get; }
private IRegionManager _regionManager;
private IUnityContainer UnityContainer { get; }
public InteractionRequest<Notification> NotificationRequest { get; }
public InteractionRequest<Confirmation> ConfirmationRequest { get; }
///<summary>
///Occurs when a property value changes.
///</summary>
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
switch (propertyName)
{
case "TestName":
ChangeValidationIsNeeded = true;
break;
}
}
#region constructors and initializers
/// <summary>
/// Creates a new instance of the TechnologyDomainEditViewModel.
/// </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 EditFileViewModel(EditFileView 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);
_eventAggregator.GetEvent<TTSImportReadFileStatusEvent>().Subscribe(OnReadFileFinished, ThreadOption.PublisherThread, true);
}
#endregion
#region Methods
/// <summary>
/// filters the available sensors from the db by the given text
/// we keep two lists, allchannels and SystemSensors
/// SystemSensors only holds those that aren't in use and are
/// available
/// </summary>
/// <param name="text"></param>
public void Search(string text)
{
SystemSensors.Clear();
//build a list of sensors already used to exclude those sensors
var hash = new HashSet<string>();
foreach (var ch in RequiredChannels)
{
hash.Add(ch.SensorSerialNumber);
}
if (string.IsNullOrEmpty(text))
{
//nothing to sort
foreach (var s in _allChannels)
{
if (!hash.Contains(s.SensorSerialNumber))
{
SystemSensors.Add(s);
}
}
}
else
{
text = text.ToLower();
foreach (var s in _allChannels)
{
if (s.SensorSerialNumber.ToLower().Contains(text))
{
if (!hash.Contains(s.SensorSerialNumber))
{
SystemSensors.Add(s);
}
}
}
}
}
private void OnReadFileFinished(ReadFileStatusArg statusArg)
{
if (!Application.Current.Dispatcher.CheckAccess())
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
OnReadFileFinished(statusArg);
}));
return;
}
if (!statusArg.Status) return;
_setup = statusArg.TTSSetup;
}
public void InitializeView()
{
if (_setup == null) return;
//turn off Change Validation and remove old channels/sensors, if any
ChangeValidationIsNeeded = false;
RequiredChannels.Clear();
SystemSensors.Clear();
//will turn Change Validation back on
TestName = _setup.TestId;
SaveFileEnabled = false;
var requiredChannels = new ObservableCollection<ITTSChannelRecord>();
_allChannels.Clear();
foreach (var channelRecord in _setup.Channels)
{
if (channelRecord.IsDigitalOutput) { continue; }
if (channelRecord.ChannelCode != TTSChannelRecord.NONE)
{
//isn't a "reserved sensor"
var requiredChannel = channelRecord.Copy();
requiredChannel.Parent = this;
requiredChannels.Add(requiredChannel);
}
}
foreach (var sensor in DTS.SensorDB.SensorsCollection.SensorsList.GetAllSensors(false))
{
if (sensor.IsDigitalOutput()) { continue; }
if (sensor.IsTestSpecificSquib) { continue; }
if (sensor.IsTestSpecificDigitalIn) { continue; }
_allChannels.Add(new TTSChannelRecord(sensor) { Parent = this });
}
RequiredChannels = requiredChannels;
Search(_searchText);
ValidateChannelCodes();
ValidateJCodes();
_originalHash = GenerateHash();
NumChannelsAndSensors = string.Format(StringResources.NumChannelsAndSensors, RequiredChannels.Count, SystemSensors.Count);
SaveFileEnabled = false;
}
private string GenerateHash()
{
var bytes = new List<byte>();
foreach (var rc in RequiredChannels)
{
bytes.AddRange(rc.GetBytes());
}
foreach (var rs in SystemSensors)
{
bytes.AddRange(rs.GetBytes());
}
var sha = new SHA256Managed();
var hash = sha.ComputeHash(bytes.ToArray());
return BitConverter.ToString(hash).Replace("-", string.Empty);
}
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
});
}
#endregion
#region Properties
/// <summary>
/// this is the list of all channels, this is a superset while SystemSensors is the subset
/// this contains some records we don't want
/// </summary>
private readonly List<ITTSChannelRecord> _allChannels = new List<ITTSChannelRecord>();
/// <summary>
/// this is the current text the user is search for in available channels/sensors
/// </summary>
private readonly string _searchText = "";
private ITTSSetup _setup;
public bool ChangeValidationIsNeeded { get; set; }
public bool IsDirty { get; private set; }
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"); }
}
private bool _isTestNameValid;
public bool IsTestNameValid { get => _isTestNameValid; set { _isTestNameValid = value; OnPropertyChanged("IsTestNameValid"); } }
private string _testName = string.Empty;
public string TestName
{
get => _testName;
set
{
_testName = value;
IsTestNameValid = !string.IsNullOrWhiteSpace(value);
OnPropertyChanged("TestName");
}
}
private DelegateCommand<string> _testNameLostFocus;
public DelegateCommand<string> TestNameLostFocus => _testNameLostFocus ?? (_testNameLostFocus = new DelegateCommand<string>(TestNameLostFocusMethod));
public void TestNameLostFocusMethod(string code)
{
if (!ChangeValidationIsNeeded) return;
ValidateChange();
}
private bool _saveFileEnabled;
public bool SaveFileEnabled
{
get => _saveFileEnabled;
set
{
_saveFileEnabled = value;
OnPropertyChanged("SaveFileEnabled");
EnableOrDisableButtons();
}
}
private string _numChannelsAndSensors = string.Empty;
public string NumChannelsAndSensors
{
get => _numChannelsAndSensors;
set { _numChannelsAndSensors = value; OnPropertyChanged("NumChannelsAndSensors"); }
}
private bool _replaceEnabled;
public bool ReplaceEnabled
{
get => _replaceEnabled;
set { _replaceEnabled = value; OnPropertyChanged("ReplaceEnabled"); }
}
private bool _addCodeEnabled;
public bool AddCodeEnabled
{
get => _addCodeEnabled;
set { _addCodeEnabled = value; OnPropertyChanged("AddCodeEnabled"); }
}
private bool _deleteCodeEnabled;
public bool DeleteCodeEnabled
{
get => _deleteCodeEnabled;
set { _deleteCodeEnabled = value; OnPropertyChanged("DeleteCodeEnabled"); }
}
/// <summary>
/// stores a hash representing the state of channels at the time they were read in
/// this is used to determine if any changes of significance were made in edit file
/// </summary>
private string _originalHash;
private ObservableCollection<ITTSChannelRecord> _requiredChannels = new ObservableCollection<ITTSChannelRecord>();
public ObservableCollection<ITTSChannelRecord> RequiredChannels
{
get => _requiredChannels;
set
{ _requiredChannels = value; OnPropertyChanged("RequiredChannels"); }
}
private ObservableCollection<ITTSChannelRecord> _systemSensors = new ObservableCollection<ITTSChannelRecord>();
public ObservableCollection<ITTSChannelRecord> SystemSensors
{
get => _systemSensors;
set
{ _systemSensors = value; OnPropertyChanged("SystemSensors"); }
}
private ITTSChannelRecord _selectedRequiredChannel;
public ITTSChannelRecord SelectedRequiredChannel
{
get => _selectedRequiredChannel;
set
{
_selectedRequiredChannel = value; OnPropertyChanged("SelectedRequiredChannel");
EnableOrDisableButtons();
}
}
private ITTSChannelRecord _selectedSystemSensor;
public ITTSChannelRecord SelectedSystemSensor
{
get => _selectedSystemSensor;
set
{
_selectedSystemSensor = value;
OnPropertyChanged("SelectedSystemSensor");
EnableOrDisableButtons();
}
}
private void EnableOrDisableButtons()
{
if (null == _selectedRequiredChannel || null == _selectedSystemSensor) { ReplaceEnabled = false; }
else if (_selectedRequiredChannel.IsDigitalInput)
{
ReplaceEnabled = _selectedSystemSensor.IsDigitalInput;
}
else if (_selectedRequiredChannel.IsDigitalOutput)
{
ReplaceEnabled = _selectedSystemSensor.IsDigitalOutput;
}
else if (_selectedRequiredChannel.IsSquib)
{
ReplaceEnabled = _selectedSystemSensor.IsSquib;
}
else
{
ReplaceEnabled = !(_selectedSystemSensor.IsSquib
|| _selectedSystemSensor.IsDigitalInput ||
_selectedSystemSensor.IsDigitalOutput);
}
AddCodeEnabled = _selectedSystemSensor != null;
DeleteCodeEnabled = _selectedRequiredChannel != null;
}
#endregion Properties
#region Commands
/// <summary>
/// Browse to import file
/// </summary>
private DelegateCommand _saveFileClicked;
public DelegateCommand SaveFileClicked => _saveFileClicked ?? (_saveFileClicked = new DelegateCommand(SaveFileMethod));
public void SaveFileMethod()
{
using (var sfd = new System.Windows.Forms.SaveFileDialog())
{
var fullFilePath = Path.GetFullPath(_setup.OriginalImportFile);
sfd.Filter = @"TTS import (*.csv)|*.csv";
sfd.RestoreDirectory = true;
sfd.FilterIndex = 0;
var fi = new FileInfo(fullFilePath);
sfd.FileName = fi.Name;
if (fi.DirectoryName != null) sfd.InitialDirectory = Path.GetFullPath(fi.DirectoryName);
var result = sfd.ShowDialog();
if (result != System.Windows.Forms.DialogResult.OK) return;
fullFilePath = sfd.FileName;
var csv = new StringBuilder();
var newRow = _setup.Line1;
csv.Append(newRow + Environment.NewLine);
newRow = _setup.Line2;
_setup.TestId = TestName;
if (_setup.Line2 != null)
{
var fields = _setup.Line2.Split(',');
var newLine2 = fields.Where((t, index) => index > 0).Aggregate(_setup.TestId, (current, t) => current + "," + t);
newRow = newLine2;
}
csv.Append(newRow + Environment.NewLine);
newRow = _setup.Line3;
csv.Append(newRow + Environment.NewLine);
newRow = _setup.Line4;
csv.Append(newRow + Environment.NewLine);
//Order the required channels first
var channelList = RequiredChannels.ToList();
_setup.Channels = channelList.ToArray();
foreach (var channel in _setup.Channels)
{
var channelNumber = channel.ChannelNumber.ToString();
var channelCode = channel.ChannelCode;
var channelRange = channel.IsSquib ? channel.SquibFireDelayMs.ToString(STRINGWRITEFORMAT_CHANNELRANGE) : channel.ChannelRange.ToString(STRINGWRITEFORMAT_CHANNELRANGE); //Write up to 15 decimal places to .csv
var channelFilter = channel.IsSquib ? channel.SquibFireDurationMs.ToString(STRINGWRITEFORMAT_CHANNELRANGE) : channel.ChannelFilterHz.ToString(System.Globalization.CultureInfo.InvariantCulture);
if (channel.ChannelCode == TTSChannelRecord.NONE)
{
//Blank the following fields from reserved sensors
channelNumber = "";
channelCode = "";
channelRange = "";
channelFilter = "";
}
var sensorSensitivity = channel.SensorSensitivity.ToString(STRINGWRITEFORMAT_SENSITIVITY); //Write up to 15 decimal places to .csv
var sensorExcitationVolts = channel.SensorExcitationVolts.ToString("0.#");
var sensorCapacity = channel.SensorCapacity.ToString(STRINGWRITEFORMAT_CAPACITY); //Write up to 15 decimal places to .csv
var sensorPolarity = channel.SensorPolarity ? "+" : "-";
//Only FullBridge and HalfBridge contain a space
string channelType;
switch (channel.ChannelType)
{
case DTS.Common.Enums.TTS.ToyotaBridgeType.FullBridge:
channelType = "Full Bridge";
break;
case DTS.Common.Enums.TTS.ToyotaBridgeType.HalfBridge:
channelType = "Half Bridge";
break;
default:
channelType = channel.ChannelType.ToString();
break;
}
var proportionalToExcitation = channel.ProportionalToExcitation ? "mv/V/EU" : "";
var bridgeResistance = channel.BridgeResistance.ToString("0.#");
var initialOffsetVoltage = channel.InitialOffsetVoltage.ToString("0.#");
var initialOffsetVoltageTolerance = channel.InitialOffsetVoltageTolerance.ToString("0.#");
var removeOffset = channel.RemoveOffset ? "1" : "0";
var zeroMethod = ((int)channel.ZeroMethod).ToString();
var initialEUInMv = double.IsNaN(channel.InitialEUInMV) ? "" : channel.InitialEUInMV.ToString("0.##");
var initialEUInEU = double.IsNaN(channel.InitialEUInEU) ? "" : channel.InitialEUInEU.ToString("0.##");
var iRTraccExponent = double.IsNaN(channel.IRTraccExponent) ? "" : channel.IRTraccExponent.ToString("0.##");
var polynomialConstant = double.IsNaN(channel.PolynomialConstant) ? "" : channel.PolynomialConstant.ToString("0.##");
var polynomialCoefficientC = double.IsNaN(channel.PolynomialCoefficientC) ? "" : channel.PolynomialCoefficientC.ToString("0.##");
var polynomialCoefficentB = double.IsNaN(channel.PolynomialCoefficentB) ? "" : channel.PolynomialCoefficentB.ToString("0.##");
var polynomialCoefficientA = double.IsNaN(channel.PolynomialCoefficientA) ? "" : channel.PolynomialCoefficientA.ToString("0.##");
var polynomialCoefficientAlpha = double.IsNaN(channel.PolynomialCoefficientAlpha) ? "" : channel.PolynomialCoefficientAlpha.ToString("0.##");
var diagnosticsMode = channel.DiagnosticsMode ? TTSChannelRecord.DIAGNOSTICSMODE : "";
if (channel.IsDigitalInput)
{
channelRange = string.Empty;
channelFilter = string.Empty;
}
if (channel.IsDigitalOutput)
{
//14708 Squib appears to be unassigned when using edit file during TTS import
//I changed this to be consistent with the reader, which only reads the first 3
//fields for digital outputs
newRow = $"{channelNumber},{channelCode},{channel.JCodeOrDescription}{Environment.NewLine}";
}
if (channel.IsDigitalInput || channel.IsSquib)
{
//14708 Squib appears to be unassigned when using edit file during TTS import
//don't wipe out EID or serial number, these are valid fields for these sensors
//isocode and description should be valid too? as well as channel description?
//note that the import reads up to the serial number, so no point it writing more
newRow =
$"{channelNumber},{channelCode},{channel.JCodeOrDescription},{channelRange},{channelFilter},{channel.SensorEID},{channel.SensorSerialNumber}{Environment.NewLine}";
}
else
{
newRow = $"{channelNumber},{channelCode},{channel.JCodeOrDescription},{channelRange},{channelFilter},{channel.SensorEID},{channel.SensorSerialNumber},{sensorSensitivity},{sensorExcitationVolts},{sensorCapacity},{channel.SensorEU}," +
$"{sensorPolarity},{channelType},{channel.Description},{proportionalToExcitation},{bridgeResistance},{initialOffsetVoltage},{initialOffsetVoltageTolerance},{removeOffset},{zeroMethod},{channel.CableMultiplier.ToString(System.Globalization.CultureInfo.InvariantCulture)}," +
$"{initialEUInMv},{initialEUInEU},{iRTraccExponent},{polynomialConstant},{polynomialCoefficientC},{polynomialCoefficentB},{polynomialCoefficientA},{polynomialCoefficientAlpha},{channel.ISOCode},{channel.ISODescription},{channel.ISOPolarity},{diagnosticsMode}{Environment.NewLine}";
}
csv.Append(newRow);
}
File.WriteAllText(fullFilePath, csv.ToString(), Encoding.GetEncoding("Shift-JIS"));
_setup.OriginalImportFile = fullFilePath;
}
SaveFileEnabled = false;
//Enable future nav steps since changes have been saved
_eventAggregator.GetEvent<TTSImportSavedChangesStatusEvent>().Publish(true);
_originalHash = GenerateHash();
}
#region Replace
/// <summary>
/// Swap the assignments of a Required Channel and a Reserved Sensor
/// </summary>
private DelegateCommand _replaceCommand;
public DelegateCommand ReplaceCommand => _replaceCommand ?? (_replaceCommand = new DelegateCommand(Replace));
private void Replace()
{
//11574 replace button removed JCode and range and filter from an "add code" channel in edit file
//we preserve the original channel settings when doing replace
//Initialize a Reserved Sensor that's about to become a Required Channel
SelectedSystemSensor.ChannelNumber = SelectedRequiredChannel.ChannelNumber;
SelectedSystemSensor.ChannelCode = SelectedRequiredChannel.ChannelCode;
SelectedRequiredChannel.ChannelCode = TTSChannelRecord.NONE;
SelectedSystemSensor.JCodeOrDescription = SelectedRequiredChannel.JCodeOrDescription;
SelectedSystemSensor.ChannelRange = SelectedRequiredChannel.ChannelRange;
SelectedSystemSensor.ChannelFilterHz = SelectedRequiredChannel.ChannelFilterHz;
// Save off the channels that are about to be removed from their old lists
var savedSelectedSystemSensor = SelectedSystemSensor;
var savedSelectedRequiredChannel = SelectedRequiredChannel;
//Save the indices of the channels that are about to be removed from their old lists
var selectedRequiredChannelIndex = RequiredChannels.IndexOf(SelectedRequiredChannel);
var selectedSystemSensorIndex = SystemSensors.IndexOf(SelectedSystemSensor);
//Remove the channels from their old lists
RequiredChannels.Remove(SelectedRequiredChannel);
SystemSensors.Remove(SelectedSystemSensor);
//Insert the saved channels into their new lists
RequiredChannels.Insert(selectedRequiredChannelIndex, savedSelectedSystemSensor);
SystemSensors.Insert(selectedSystemSensorIndex, savedSelectedRequiredChannel);
//Re-select the same rows in the tables
SelectedRequiredChannel = RequiredChannels[selectedRequiredChannelIndex];
SelectedSystemSensor = SystemSensors[selectedSystemSensorIndex];
UpdateAndValidate();
}
#endregion Replace
#region Add Code
private DelegateCommand _addCodeCommand;
public DelegateCommand AddCodeCommand => _addCodeCommand ?? (_addCodeCommand = new DelegateCommand(AddCode));
/// <summary>
/// remove a hardware channel assignment(does not remove the channel from the test setup though?)
/// </summary>
private void AddCode()
{
AddSystemSensor();
UpdateAndValidate();
}
private void AddSystemSensor()
{
var maxChannelNumber = RequiredChannels.Select(requiredChannel => requiredChannel.ChannelNumber).Concat(new[] { 0 }).Max();
SelectedSystemSensor.ChannelNumber = maxChannelNumber + 1;
SelectedSystemSensor.ChannelCode = string.Empty;
SelectedSystemSensor.JCodeOrDescription = string.Empty;
SelectedSystemSensor.ChannelRange = 0;
SelectedSystemSensor.ChannelFilterHz = -1;
RequiredChannels.Add(SelectedSystemSensor);
var selectedSystemSensorIndex = SystemSensors.IndexOf(SelectedSystemSensor);
SystemSensors.Remove(SelectedSystemSensor);
if (SystemSensors.Count > 0)
{
//If removing from the end of the list, set the previous record to Selected, otherwise set next record to Selected
SelectedSystemSensor = selectedSystemSensorIndex == SystemSensors.Count ? SystemSensors[selectedSystemSensorIndex - 1] : SystemSensors[selectedSystemSensorIndex];
}
}
#endregion Add Code
private DelegateCommand _addSquibCommand;
public DelegateCommand AddSquibCommand =>
_addSquibCommand ?? (_addSquibCommand = new DelegateCommand(AddSquib));
private void AddSquib()
{
var maxChannelNumber = RequiredChannels.Select(requiredChannel => requiredChannel.ChannelNumber).Concat(new[] { 0 }).Max();
var channelRecord = new TTSChannelRecord();
channelRecord.ChannelNumber = ++maxChannelNumber;
channelRecord.IsSquib = true;
int maxSquibNumber = 0;
foreach (var rc in RequiredChannels)
{
if (!rc.IsSquib) continue;
var channelCode = rc.ChannelCode.Replace("TF", "").Replace("SQ", "");
if (int.TryParse(channelCode, out var temp))
{
maxSquibNumber = Math.Max(temp, maxSquibNumber);
}
}
maxSquibNumber++;
channelRecord.ChannelCode = $"SQ{maxSquibNumber}";
channelRecord.JCodeOrDescription = string.Empty;
channelRecord.LimitDuration = true;
channelRecord.ChannelRange = (int)channelRecord.SquibFireDelayMs;
channelRecord.SquibFireDurationMs = _setup.DefaultSquibFireDurationMs;
channelRecord.ChannelFilterHz = (int)channelRecord.SquibFireDurationMs; //-1;
channelRecord.Parent = this;
RequiredChannels.Add(channelRecord);
Validate();
}
private DelegateCommand _addDICommand;
public DelegateCommand ADDDICommand =>
_addDICommand ?? (_addDICommand = new DelegateCommand(AddDI));
private void AddDI()
{
var maxChannelNumber = RequiredChannels.Select(requiredChannel => requiredChannel.ChannelNumber).Concat(new[] { 0 }).Max();
var channelRecord = new TTSChannelRecord();
channelRecord.ChannelNumber = ++maxChannelNumber;
channelRecord.IsDigitalInput = true;
int maxDINumber = 0;
foreach (var rc in RequiredChannels)
{
if (!rc.IsDigitalInput) continue;
var channelCode = rc.ChannelCode.Replace("DI", "");
if (int.TryParse(channelCode, out var temp))
{
maxDINumber = Math.Max(temp, maxDINumber);
}
}
maxDINumber++;
channelRecord.ChannelCode = $"DI{maxDINumber}";
channelRecord.JCodeOrDescription = string.Empty;
channelRecord.ChannelFilterHz = -1;
channelRecord.Parent = this;
channelRecord.IsChannelCodeValid = true;
RequiredChannels.Add(channelRecord);
}
#region Delete Code
private DelegateCommand _deleteCodeCommand;
public DelegateCommand DeleteCodeCommand =>
_deleteCodeCommand ?? (_deleteCodeCommand = new DelegateCommand(DeleteCode));
/// <summary>
/// enables or disables a channel in the test.
/// </summary>
private void DeleteCode()
{
DeleteRequiredChannel();
UpdateAndValidate();
}
private void DeleteRequiredChannel()
{
if (null == SelectedRequiredChannel) { return; }
SelectedRequiredChannel.ChannelCode = TTSChannelRecord.NONE; //So that when it's saved, these fields will be set to empty strings, but if it's re-Added, force user to enter a value.
SelectedRequiredChannel.JCodeOrDescription = string.Empty; //In case it's re-Added, force user to enter a value.
SelectedRequiredChannel.ChannelRange = 0; //In case it's re-Added, force user to enter a value.
SelectedRequiredChannel.ChannelFilterHz = -1; //In case it's re-Added, force user to choose a filter or "None".
SelectedRequiredChannel.HardwareChannel = null;
SystemSensors.Add(SelectedRequiredChannel);
var selectedRequiredChannelIndex = RequiredChannels.IndexOf(SelectedRequiredChannel);
RequiredChannels.Remove(SelectedRequiredChannel);
if (RequiredChannels.Count > 0)
{
//Ensure the channels don't have any gaps
foreach (var channel in RequiredChannels)
{
channel.ChannelNumber = RequiredChannels.IndexOf(channel) + 1;
}
//If removing from the end of the list, set the previous record to Selected, otherwise set next record to Selected
SelectedRequiredChannel = selectedRequiredChannelIndex == RequiredChannels.Count ? RequiredChannels[selectedRequiredChannelIndex - 1] : RequiredChannels[selectedRequiredChannelIndex];
}
}
#endregion Delete Code
private void UpdateAndValidate()
{
NumChannelsAndSensors = string.Format(StringResources.NumChannelsAndSensors, RequiredChannels.Count, SystemSensors.Count);
EnableOrDisableButtons();
ValidateChange();
CollectionViewSource.GetDefaultView(RequiredChannels)?.Refresh();
}
#endregion Commands
/// <summary>
/// Returns True if all fields are valid, False if not
/// </summary>
/// <returns></returns>
public bool Validate()
{
var duplicates = RequiredChannels.GroupBy(x => x.ChannelCode).Any(g => g.Count() > 1);
return !duplicates && IsTestNameValid && RequiredChannels.All(x => x.IsChannelCodeValid /*&& x.IsJCodeValid*/ &&
(x.IsRangeValid || x.RangeVisible != System.Windows.Visibility.Visible) &&
(x.IsFilterValid || x.FilterVisible != System.Windows.Visibility.Visible));
}
/// <summary>
/// returns true if there are any changes that have not been saved yet
/// </summary>
/// <returns></returns>
private bool HasUnsavedChanges()
{
var hash = GenerateHash();
return hash != _originalHash;
}
/// <summary>
/// If all fields are valid after a change, enables the Save File button and returns True.
/// If not all fields are valid after a change, disables the Save File button and returns False.
/// Disables all future nav steps (they will be enabled when the Save File button is clicked).
/// </summary>
/// <returns></returns>
public bool ValidateChange(ITTSChannelRecord record = null)
{
//Enable the Save File button if everything is valid
ValidateChannelCodes();
ValidateJCodes();
bool bValid = Validate();
SaveFileEnabled = bValid && HasUnsavedChanges();
//Prevent calls to Validate if losing focus on an unchanged control, for example
ChangeValidationIsNeeded = false;
//Disable future nav steps since changes have been made but not saved, or are just or are just invalid
_eventAggregator.GetEvent<TTSImportSavedChangesStatusEvent>().Publish(!SaveFileEnabled);
return SaveFileEnabled;
}
private void ValidateChannelCodes()
{
var channelCodeToChannel = new Dictionary<string, ITTSChannelRecord>();
foreach (var channel in RequiredChannels)
{
if (string.IsNullOrWhiteSpace(channel.ChannelCode))
{
channel.IsChannelCodeValid = false;
}
else if (channel.ChannelCode == TTSChannelRecord.NONE)
{
channel.IsChannelCodeValid = false;
}
else if (channelCodeToChannel.ContainsKey(channel.ChannelCode))
{
channelCodeToChannel[channel.ChannelCode].IsChannelCodeValid = false;
channel.IsChannelCodeValid = false;
}
else
{
channelCodeToChannel[channel.ChannelCode] = channel;
channel.IsChannelCodeValid = true;
}
}
}
private void ValidateJCodes()
{
var channelJCodeToChannel = new Dictionary<string, ITTSChannelRecord>();
foreach (var channel in RequiredChannels)
{
if (string.IsNullOrWhiteSpace(channel.JCodeOrDescription))
{
channel.IsJCodeValid = false;
}
//else if (channelJCodeToChannel.ContainsKey(channel.JCodeOrDescription))
//{
// channelJCodeToChannel[channel.JCodeOrDescription].IsJCodeValid = false;
// channel.IsJCodeValid = false;
//}
else
{
channelJCodeToChannel[channel.JCodeOrDescription] = channel;
channel.IsJCodeValid = true;
}
}
}
}
}

View File

@@ -0,0 +1,613 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using DTS.Common.Base;
using DTS.Common.Classes;
using DTS.Common.Events;
using DTS.Common.Events.TTSImport;
using DTS.Common.Interface;
using DTS.Common.Interface.DataRecorders;
using DTS.Common.Interface.TestSetups.Imports.TTS.HardwareScan;
using DTS.Common.Interface.TestSetups.Imports.TTS.ReadFile;
using DTS.Common.Utilities.Logging;
using Prism.Events;
using Unity;
using DTS.Common.Interactivity;
using Prism.Regions;
using Prism.Commands;
using TTSImport.Model;
// ReSharper disable CheckNamespace
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable InconsistentNaming
namespace TTSImport
{
/// <summary>
/// this class handles Hardware Scan functionality
/// </summary>
[PartCreationPolicy(CreationPolicy.Shared)]
public class HardwareScanViewModel : IHardwareScanViewModel
{
/// <summary>
/// The Status and Progress bars
/// </summary>
public IStatusAndProgressBarView StatusAndProgressBarView { get; private set; }
/// <summary>
/// The Hardware Scan view
/// </summary>
public IHardwareScanView View { get; set; }
private IEventAggregator _eventAggregator { get; set; }
private IRegionManager _regionManager;
private IUnityContainer UnityContainer { get; set; }
public InteractionRequest<Notification> NotificationRequest { get; private set; }
public InteractionRequest<Confirmation> ConfirmationRequest { get; private set; }
/// <summary>
/// Creates a new instance of the TechnologyDomainEditViewModel.
/// </summary>
/// <param name="hardwareScanView">The Hardware Scan 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 HardwareScanViewModel(IHardwareScanView hardwareScanView, IRegionManager regionManager,
IEventAggregator eventAggregator, IUnityContainer unityContainer)
{
View = hardwareScanView;
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);
_eventAggregator.GetEvent<TTSImportReadFileStatusEvent>().Subscribe(OnReadFileFinished, ThreadOption.PublisherThread, true);
_eventAggregator.GetEvent<TTSImportHardwareScanFinishedEvent>().Subscribe(OnHardwareScanFinished, ThreadOption.PublisherThread, true);
_eventAggregator.GetEvent<AssignedChannelsChangedEvent>().Subscribe(OnAssignedChannelsChangedEvent, ThreadOption.PublisherThread, true);
}
#region Methods
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()
{
StatusAndProgressBarView = GetStatusAndProgressBarView(this);
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
});
}
private readonly StatusAndProgressBarEventArgs statusAndProgressBarEventArgs = new StatusAndProgressBarEventArgs();
/// <summary>
/// Sets the text, background color, progress value, and/or progress visibility
/// </summary>
/// <param name="status"></param>
public void SetStatus(string status)
{
switch (status)
{
case "Waiting": //change this to string resources but use the same one in DataPRO and here???
statusAndProgressBarEventArgs.StatusColor =
DTS.Common.BrushesAndColors.Brush_ApplicationStatus_Waiting.Color;
statusAndProgressBarEventArgs.ProgressBarVisibility = Visibility.Collapsed;
break;
case "Working":
statusAndProgressBarEventArgs.StatusColor =
DTS.Common.BrushesAndColors.Brush_ApplicationStatus_Busy.Color;
statusAndProgressBarEventArgs.ProgressValue = 0;
statusAndProgressBarEventArgs.ProgressBarVisibility = Visibility.Visible;
break;
case "Failed":
statusAndProgressBarEventArgs.StatusColor =
DTS.Common.BrushesAndColors.Brush_ApplicationStatus_Failed.Color;
statusAndProgressBarEventArgs.ProgressBarVisibility = Visibility.Collapsed;
break;
case "Done":
statusAndProgressBarEventArgs.StatusColor =
DTS.Common.BrushesAndColors.Brush_ApplicationStatus_Complete.Color;
statusAndProgressBarEventArgs.ProgressBarVisibility = Visibility.Collapsed;
break;
}
statusAndProgressBarEventArgs.StatusText = status;
statusAndProgressBarEventArgs.Requester = this;
_eventAggregator.GetEvent<StatusAndProgressBarEvent>().Publish(statusAndProgressBarEventArgs);
}
/// <summary>
/// Sets the progress value on the Status and Progress bar
/// </summary>
/// <param name="progress"></param>
public void SetProgress(double progress)
{
statusAndProgressBarEventArgs.ProgressValue = (int)progress;
statusAndProgressBarEventArgs.Requester = this;
_eventAggregator.GetEvent<StatusAndProgressBarEvent>().Publish(statusAndProgressBarEventArgs);
}
private IStatusAndProgressBarView GetStatusAndProgressBarView(IBaseViewModel parent)
{
var view = UnityContainer.Resolve<IStatusAndProgressBarView>();
var viewModel = UnityContainer.Resolve<IStatusAndProgressBarViewModel>();
view.DataContext = viewModel;
viewModel.Initialize(parent);
return view;
}
private void OnReadFileFinished(ReadFileStatusArg statusArg)
{
if (statusArg.Status)
{
_setup = statusArg.TTSSetup;
}
}
public void HardwareScan()
{
SetStatus(Resources.StringResources
.ImportTestSetup_PossibleStatus_Working); //use string resource (same both here and where passed)
var data = new WorkFunctionThreadData();
ThreadPool.QueueUserWorkItem(HardwareScanWorkThread, data);
}
void HardwareScanWorkThread(object obj)
{
ITTSSetup temp = null;
//Blank out the tables before re-scanning
HardwareRecords[0].Update(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
DasSummaryList = new List<IDASHardware>();
ChannelSummaryList = new List<ChannelSummary>();
_eventAggregator.GetEvent<TTSImportHardwareScanRunEvent>().Publish(temp); //temp not needed?
}
private void UpdateUnitCount(IDASHardware hardwareRecord, ref uint sps, ref uint spd, ref uint spt, ref uint ecm,
ref uint g5, ref uint rack)
{
if (hardwareRecord.SerialNumber.Length < 3)
{
return;
}
var firstThree = hardwareRecord.SerialNumber.Substring(0, 3).ToLower();
switch (firstThree)
{
case "spt": spt++; break;
case "spd": spd++; break;
case "sps": sps++; break;
case "spe": ecm++; break;
case "slt": spt++; break;
case "sld": spd++; break;
case "sle": ecm++; break;
case "sls": sps++; break;
case "sg5": g5++; break;
}
var firstTwo = hardwareRecord.SerialNumber.Substring(0, 2).ToLower();
switch (firstTwo)
{
case "dr": rack++; break;
case "lr": rack++; break;
case "5m": g5++; break;
}
}
/// <summary>
/// returns IP addresses for which SLICE2 modules have been found without a corresponding ECM/SLE
/// </summary>
/// <param name="hardwareRecords"></param>
/// <returns></returns>
private static IEnumerable<string> GetMissingECMIPAddresses(List<IDASHardware> hardwareRecords)
{
var foundECMIPAddresses = new HashSet<string>();
var missingECMIPAddresses = new List<string>();
foreach (var hardwareRecord in hardwareRecords)
{
if (!hardwareRecord.IsSLICEEthernetController) continue;
var ipAddress = hardwareRecord.Connection;
var index = ipAddress.IndexOf(':');
if (index > 0)
{
ipAddress = ipAddress.Substring(0, index);
}
foundECMIPAddresses.Add(ipAddress);
}
foreach (var hardwareRecord in hardwareRecords)
{
if (hardwareRecord.SerialNumber.Length < 3) { continue; }
var ipAddress = hardwareRecord.Connection.ToLower();
var index = ipAddress.IndexOf(':');
if (index > 0)
{
ipAddress = ipAddress.Substring(0, index);
}
if (foundECMIPAddresses.Contains(ipAddress)) { continue; }
if (missingECMIPAddresses.Contains(ipAddress)) { continue; }
var firstThree = hardwareRecord.SerialNumber.Substring(0, 3).ToLower();
switch (firstThree)
{
case "spt":
case "spd":
case "sps":
case "slt":
case "sld":
case "sls":
if (ipAddress.Contains("usb") || ipAddress.Length < 6) { continue; }
missingECMIPAddresses.Add(ipAddress);
break;
}
}
return missingECMIPAddresses;
}
/// <summary>
/// Fill in the table of DAS that are in the database and connected
/// </summary>
/// <param name="hardwareRecords"></param>
private void OnHardwareScanFinished(List<IDASHardware> hardwareRecords)
{
//Create mapping from IP address to ECM/SDB/Rack
var connectionToParent =
hardwareRecords.Where(hardwareRecord => hardwareRecord.IsSLICEEthernetController ||
hardwareRecord.IsTDASRack()).ToDictionary(hardwareRecord => hardwareRecord.Connection, hardwareRecord => hardwareRecord.SerialNumber);
var moduleRecords = new List<IDASHardware>();
uint analog = 0;
uint din = 0;
uint dout = 0;
uint squib = 0;
uint sps = 0;
uint spd = 0;
uint spt = 0;
uint ecm = 0;
uint g5 = 0;
uint rack = 0;
var missingECMIPS = GetMissingECMIPAddresses(hardwareRecords);
if (missingECMIPS.Any())
{
var prompt = $"{Resources.StringResources.MissingECMSWarning}\r\n";
prompt += string.Join("\r\n", missingECMIPS.ToArray());
var mreLocal = new ManualResetEvent(false);
APILogger.Log("MessageBox", prompt);
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
var window = Application.Current.MainWindow;
if (null == window)
{
return;
}
MessageBox.Show(window, prompt, Resources.StringResources.Warning,
MessageBoxButton.OK, MessageBoxImage.Warning);
mreLocal.Set();
}));
mreLocal.WaitOne();
}
foreach (var hardwareRecord in hardwareRecords)
{
//We don't want to list bridges, but we do want to list modules in a TDAS Rack, and DAS connected via ECM, SDB, and USB.
//Modules in a TDAS Rack have IsModule() == false.
//DAS connected via ECM or SDB have a hardwareRecord.Connection that's in the connectionToParent dictionary above.
//DAS connected via USB have a hardwareRecord.Connection == "USB".
if (hardwareRecord.IsModule() &&
!connectionToParent.ContainsKey(hardwareRecord.Connection) &&
hardwareRecord.Connection != "USB") continue;
UpdateUnitCount(hardwareRecord, ref sps, ref spd, ref spt, ref ecm, ref g5, ref rack);
//We want to display TDAS Racks, ECM, and SDBs in a separate row for their battery and
//voltage status but also as a parent of their connected DAS.
if (!hardwareRecord.IsSLICEEthernetController && !hardwareRecord.IsTDASRack())
{
//Display the DAS with its associated parent (TDAS Rack/ECM/SDB)
var parentDAS = connectionToParent.ContainsKey(hardwareRecord.Connection)
? connectionToParent[hardwareRecord.Connection]
: hardwareRecord.ParentDAS;
if (!string.IsNullOrWhiteSpace(parentDAS))
{
//DAS connected via USB have a blank ParentDAS
//When the hardwareRecord.ToString() override is implemented for all DAS types, the following
//can be modified to use it. Currently, only Slice DAS connected via ECM return "<parent>:<das>".
hardwareRecord.SerialNumberFamily = "[" + parentDAS + ":" + hardwareRecord.SerialNumber + "]";
}
else
{
hardwareRecord.SerialNumberFamily = hardwareRecord.SerialNumber;
}
UpdateChannelCount(hardwareRecord, ref analog, ref squib, ref din, ref dout);
}
else
{
hardwareRecord.SerialNumberFamily = hardwareRecord.SerialNumber;
if (hardwareRecord.IsTDASRack())
{
UpdateChannelCount(hardwareRecord, ref analog, ref squib, ref din, ref dout);
}
}
moduleRecords.Add(hardwareRecord);
}
HardwareRecords[0].Update(analog, squib, din, dout, ecm, sps, spt, spd, g5, rack);
DasSummaryList = moduleRecords;
SetStatus(hardwareRecords.Any()
? Resources.StringResources.ImportTestSetup_PossibleStatus_Done
: Resources.StringResources.ImportTestSetup_PossibleStatus_Failed);
}
/// <summary>
/// counts the number of analog/squib/digitalin/digitalout on given hardware and updates count
/// </summary>
/// <param name="hardwareRecord"></param>
/// <param name="analog"></param>
/// <param name="squib"></param>
/// <param name="din"></param>
/// <param name="dout"></param>
private static void UpdateChannelCount(IDASHardware hardwareRecord, ref uint analog, ref uint squib, ref uint din,
ref uint dout)
{
var channels = hardwareRecord.GetIHardwareChannels();
for (var i = 0; i < channels.Length; i++)
{
var ch = channels[i];
if (ch.IsAnalog)
{
analog++;
}
else if (ch.IsDigitalIn)
{
din++;
}
else if (ch.IsDigitalOut)
{
dout++;
}
else if (ch.IsSquib)
{
squib++;
i++;//skip the next squib
}
}
}
/// <summary>
/// Sets a global dictionary to be used by SetChannelSummaryList
/// </summary>
private void OnAssignedChannelsChangedEvent(ITTSSetup setup)
{
SetChannelSummaryList(_setup.Channels);
}
/// <summary>
/// Fill in the table of channels that were found in the Read File step
/// </summary>
/// <param name="channelRecords"></param>
public void SetChannelSummaryList(ITTSChannelRecord[] channelRecords)
{
const int Requested = 0;
const int Assigned = 1;
const int Unassigned = 2;
var tomChannels = new[] { 0, 0, 0 };
var digitalInChannels = new[] { 0, 0, 0 };
var analogChannels = new[] { 0, 0, 0 };
foreach (var channelRecord in channelRecords)
{
if (string.Equals(channelRecord.ChannelCode, TTSChannelRecord.NONE, StringComparison.CurrentCultureIgnoreCase)) continue;
if (channelRecord.IsSquib)
{
tomChannels[Requested] += 1;
if (channelRecord.HardwareChannel != null && channelRecord.HardwareChannel.IsSquib)
{
tomChannels[Assigned] += 1;
}
else
{
tomChannels[Unassigned] += 1;
}
}
else if (channelRecord.IsDigitalInput)
{
digitalInChannels[Requested] += 1;
if (channelRecord.HardwareChannel != null && channelRecord.HardwareChannel.IsDigitalIn)
{
digitalInChannels[Assigned] += 1;
}
else
{
digitalInChannels[Unassigned] += 1;
}
}
else if (!channelRecord.IsDigitalOutput)
{
//Must be analog
analogChannels[Requested] += 1;
if (channelRecord.HardwareChannel != null && channelRecord.HardwareChannel.IsAnalog)
{
analogChannels[Assigned] += 1;
}
else
{
analogChannels[Unassigned] += 1;
}
}
}
var temp = new List<ChannelSummary>();
var channel = new ChannelSummary
{
ChannelType = Resources.StringResources.Analog,
Requested = analogChannels[Requested],
Assigned = analogChannels[Assigned],
Unassigned = analogChannels[Unassigned],
};
temp.Add(channel);
channel = new ChannelSummary
{
ChannelType = Resources.StringResources.TOM,
Requested = tomChannels[Requested],
Assigned = tomChannels[Assigned],
Unassigned = tomChannels[Unassigned],
};
temp.Add(channel);
channel = new ChannelSummary
{
ChannelType = Resources.StringResources.DigitalIn,
Requested = digitalInChannels[Requested],
Assigned = digitalInChannels[Assigned],
Unassigned = digitalInChannels[Unassigned],
};
temp.Add(channel);
ChannelSummaryList = temp;
}
#endregion
#region Properties
private ITTSSetup _setup;
public bool IsDirty { get; private set; }
private bool _isBusy = false;
public bool IsBusy
{
get => _isBusy;
set
{
_isBusy = value;
OnPropertyChanged("IsBusy");
}
}
private bool _isMenuIncluded = false;
public bool IsMenuIncluded
{
get => _isMenuIncluded;
set
{
_isMenuIncluded = value;
OnPropertyChanged("IsMenuIncluded");
}
}
private bool _isNavigationIncluded = false;
public bool IsNavigationIncluded
{
get => _isNavigationIncluded;
set
{
_isNavigationIncluded = value;
OnPropertyChanged("IsNavigationIncluded");
}
}
private List<IDASHardware> _dasSummaryList = new List<IDASHardware>();
public List<IDASHardware> DasSummaryList
{
get => _dasSummaryList;
set
{
_dasSummaryList = value;
OnPropertyChanged("DasSummaryList");
}
}
private readonly IHardwareSummaryRecord[] _hardwareRecords = { new HardwareSummaryRecord() };
public IHardwareSummaryRecord[] HardwareRecords => _hardwareRecords;
private List<ChannelSummary> _channelSummaryList = new List<ChannelSummary>();
public List<ChannelSummary> ChannelSummaryList
{
get => _channelSummaryList;
set
{
_channelSummaryList = value;
OnPropertyChanged("ChannelSummaryList");
}
}
#endregion Properties
#region Commands
#endregion
///<summary>
///Occurs when a property value changes.
///</summary>
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -0,0 +1,376 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Data;
using DTS.Common.DAS.Concepts;
using DTS.Common.Enums;
using DTS.Common.Events;
using DTS.Common.Events.TTSImport;
using DTS.Common.Interface;
using DTS.Common.Interface.DataRecorders;
using DTS.Common.Interface.TestSetups.Imports.TTS.LevelTrigger;
using DTS.Common.Interface.TestSetups.Imports.TTS.ReadFile;
using Prism.Events;
using Unity;
using DTS.Common.Interactivity;
using Prism.Regions;
using Prism.Commands;
using TTSImport.Model;
using TTSImport.Resources;
// ReSharper disable CheckNamespace
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable InconsistentNaming
namespace TTSImport
{
/// <summary>
/// this class handles Level Trigger edit/create functionality
/// </summary>
[PartCreationPolicy(CreationPolicy.Shared)]
public class LevelTriggerViewModel : ILevelTriggerViewModel
{
/// <summary>
/// The Hardware Scan view
/// </summary>
public ILevelTriggerView View { get; set; }
private IEventAggregator _eventAggregator { get; }
private IRegionManager _regionManager;
private IUnityContainer UnityContainer { get; }
public InteractionRequest<Notification> NotificationRequest { get; }
public InteractionRequest<Confirmation> ConfirmationRequest { get; }
/// <summary>
/// Creates a new instance of the TechnologyDomainEditViewModel.
/// </summary>
/// <param name="levelTriggerView">The Level Trigger 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 LevelTriggerViewModel(ILevelTriggerView levelTriggerView, IRegionManager regionManager,
IEventAggregator eventAggregator, IUnityContainer unityContainer)
{
View = levelTriggerView;
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);
_eventAggregator.GetEvent<TTSImportReadFileStatusEvent>().Subscribe(OnReadFileFinished, ThreadOption.PublisherThread, true);
_eventAggregator.GetEvent<EIDMappingEvent>().Subscribe(OnEIDMapping, ThreadOption.PublisherThread, true);
_eventAggregator.GetEvent<TTSImportHardwareScanFinishedEvent>().Subscribe(OnHardwareScanFinished, ThreadOption.PublisherThread, true);
_eventAggregator.GetEvent<TTSImportSavedChangesStatusEvent>().Subscribe(OnImportSavedChanges, ThreadOption.PublisherThread, true);
}
#region Methods
private void OnImportSavedChanges(bool bSaved)
{
if (bSaved)
{
UpdateLevelTriggers();
}
}
private void OnHardwareScanFinished(List<IDASHardware> hardware)
{
_hardware = hardware;
}
private void OnReadFileFinished(ReadFileStatusArg statusArg)
{
if (statusArg.Status)
{
_setup = statusArg.TTSSetup;
}
}
private IDictionary<string, string> _sensorIdToChannelId = new Dictionary<string, string>();
private void OnEIDMapping(IDictionary<string, string> sensorIdToChannelId)
{
_sensorIdToChannelId = sensorIdToChannelId;
UpdateLevelTriggers();
}
private void UpdateLevelTriggers()
{
if (null == _setup)
{
return;
}
if (null == _hardware || !_hardware.Any())
{
return;
}
var channelIdToDASChannel = new Dictionary<string, DASChannel>();
var channelIdToIChannel = new Dictionary<string, IHardwareChannel>();
var availableSquibChannels = new Queue<IHardwareChannel>();
var availableDigitalInputChannels = new Queue<IHardwareChannel>();
foreach (var h in _hardware)
{
var channels = h.GetIHardwareChannels();
foreach (var ch in channels)
{
//Create a DASChannel so that voltage can be validated below
channelIdToDASChannel[ch.GetId()] = new DASChannel(ch);
channelIdToIChannel[ch.GetId()] = ch;
if (ch.IsDigitalIn)
{
availableDigitalInputChannels.Enqueue(ch);
}
else if (ch.IsSquib)
{
availableSquibChannels.Enqueue(ch);
}
}
}
//handle pre-assigned channels if any
//these are channels which we know the sensor should be assigned to a given hardware channel ahead of time
//we do this before the EID check so that EID will override it
if (_setup.PreAssignedSensorIdAndHwId.Any())
{
var remainingPreassigned = new List<Tuple<string, string>>();
foreach (var tuple in _setup.PreAssignedSensorIdAndHwId)
{
var sensorId = tuple.Item1;
var hwId = tuple.Item2;
if (!channelIdToIChannel.ContainsKey(hwId))
{
remainingPreassigned.Add(tuple);
continue;
}
var matches = from ch in _setup.Channels where ch.SensorSerialNumber == sensorId select ch;
if (matches.Any())
{
var ittsChannel = matches.First();
//do not make the assignment if the user has already assigned the channel to a different hardware channel
//this would happen if the user scanned, didn't find the hardware, went to analog channels, assigned the sensor
//then finally went to hardware scan and then DID find the hardware. in this case just ignore the preassignment
if (null == ittsChannel.HardwareChannel)
{
var hardwareChannel = channelIdToIChannel[hwId];
var excitation = Test.Module.Channel.Sensor.GetExcitationVoltageEnumFromMagnitude(ittsChannel.SensorExcitationVolts);
//if we have a hardware assignment, but the excitation is no longer valid, then don't make the assignment
if (hardwareChannel.IsAnalog && !hardwareChannel.IsSupportedExcitation(excitation))
{
continue;
}
ittsChannel.HardwareChannel = hardwareChannel;
}
}
}
//if there are any remaining channels, these are hardware assignments that we didn't find hardware for, and we still need
//to look for assignment
_setup.PreAssignedSensorIdAndHwId = remainingPreassigned.ToArray();
}
foreach (var channel in _setup.Channels)
{
if (null != channel.HardwareChannel &&
!channelIdToIChannel.ContainsKey(channel.HardwareChannel.GetId()))
{
//hardware no longer present, unassign
channel.HardwareChannel = null;
}
if (string.IsNullOrWhiteSpace(channel.SensorEID))
{
//11245 TOM and DI channels in TTS imports without IDs should have sensors assigned automatically
if (channel.IsSquib)
{
if (availableSquibChannels.Any())
{
var first = availableSquibChannels.Dequeue();
channel.HardwareChannel = first;
//first channel voltage/initiation, second current, etc
var second = availableSquibChannels.Dequeue();
}
}
else if (channel.IsDigitalInput)
{
if (availableDigitalInputChannels.Any())
{
var first = availableDigitalInputChannels.Dequeue();
channel.HardwareChannel = first;
}
}
continue;
}
if (!_sensorIdToChannelId.ContainsKey(channel.SensorEID) ||
!channelIdToDASChannel.ContainsKey(_sensorIdToChannelId[channel.SensorEID]) ||
!VoltageIsValid(channel, channelIdToDASChannel[_sensorIdToChannelId[channel.SensorEID]])
//the below condition I think was missing a not, it appears to be looking for channels that don't have a valid channel code
//but DO have a valid match for sensor id ... this is probably a reserve channel where the sensor was still found that it's looking for
//15643 Sensors with EID do not remain assigned after import of XML produced from TTS import
//changed it again ... this is inhibiting sensors from being assigned, additionally tested it with a sensor without a valid channel code but
//a valid eid, and it worked fine
|| (_sensorIdToChannelId.ContainsKey(channel.SensorEID) && !channel.IsChannelCodeValid)
)
{
//unassign hardware if one is assigned, sensor id wasn't found or the sensor has an invalid excitation voltage for this hardware
//or the channel does have a jcode
channel.HardwareChannel = null;
continue;
}
var channelId = _sensorIdToChannelId[channel.SensorEID];
if (!channelIdToIChannel.ContainsKey(channelId)) continue;
channel.HardwareChannel = channelIdToIChannel[channelId];
}
foreach (var lt in _setup.LevelTriggers)
{
lt.Refresh();
}
OnPropertyChanged("LevelTriggers");
_eventAggregator.GetEvent<AssignedChannelsChangedEvent>().Publish(_setup);
CollectionViewSource.GetDefaultView(LevelTriggers)?.Refresh();
}
/// <summary>
/// returns True if the sensor's voltage is supported by the hardware channel, False if not
/// </summary>
/// <param name="selectedRemainingChannel"></param>
/// <param name="selectedDASChannel"></param>
/// <returns></returns>
private bool VoltageIsValid(ITTSChannelRecord selectedRemainingChannel, DASChannel selectedDASChannel)
{
var voltageEnum = ExcitationVoltageOptions.ExcitationVoltageOption.Undefined;
try
{
voltageEnum = Test.Module.Channel.Sensor.GetExcitationVoltageEnumFromMagnitude(selectedRemainingChannel.SensorExcitationVolts);
}
catch { } //GetExcitationVoltageEnumFromMagnitude will throw an exception if an invalid voltage is passed to it
return selectedDASChannel.HardwareChannel.IsSupportedExcitation(voltageEnum);
}
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
});
}
#endregion
#region Properties
private IList<IDASHardware> _hardware;
private ITTSSetup _setup;
public ILevelTrigger[] LevelTriggers => _setup?.LevelTriggers;
public bool IsDirty { get; private set; }
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");
}
}
#endregion Properties
#region Commands
#endregion
///<summary>
///Occurs when a property value changes.
///</summary>
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,751 @@
using System;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Threading.Tasks;
using System.Windows;
using DTS.Common.Events;
using DTS.Common.Interface;
using DTS.Common.Interface.TestSetups.Imports.TTS.ReadFile;
using Prism.Events;
using Unity;
using DTS.Common.Interactivity;
using Prism.Regions;
using Prism.Commands;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using DTS.Common;
using DTS.Common.Interface.DataRecorders;
using DTS.Common.Base;
using DTS.Common.Classes;
using DTS.Common.Enums;
using DTS.Common.Events.TTSImport;
using TTSImport.Model;
using TTSImport.Resources;
// ReSharper disable CheckNamespace
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable InconsistentNaming
namespace TTSImport
{
/// <summary>
/// this class handles Summary functionality
/// </summary>
[PartCreationPolicy(CreationPolicy.Shared)]
public class SummaryViewModel : ISummaryViewModel
{
/// <summary>
/// The Status and Progress bars
/// </summary>
public IStatusAndProgressBarView StatusAndProgressBarView { get; private set; }
/// <summary>
/// The Summary view
/// </summary>
public ISummaryView View { get; set; }
private IEventAggregator _eventAggregator { get; set; }
private IRegionManager _regionManager;
private IUnityContainer UnityContainer { get; set; }
public InteractionRequest<Notification> NotificationRequest { get; private set; }
public InteractionRequest<Confirmation> ConfirmationRequest { get; private set; }
/// <summary>
/// Creates a new instance of the TechnologyDomainEditViewModel.
/// </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 SummaryViewModel(ISummaryView 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);
_eventAggregator.GetEvent<TTSImportReadFileStatusEvent>().Subscribe(OnReadFileFinished, ThreadOption.PublisherThread, true);
_eventAggregator.GetEvent<TTSImportTestSetupChangedEvent>().Subscribe(OnTestSetupChanged, ThreadOption.PublisherThread, true);
}
#region Methods
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()
{
StatusAndProgressBarView = GetStatusAndProgressBarView(this);
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 string GetTestId()
{
return null == View ? "" : View.GetTestId();
}
private void OnReadFileFinished(ReadFileStatusArg statusArg)
{
TestSetupImported = false;
if (!statusArg.Status) return;
_setup = statusArg.TTSSetup;
View?.SetTestName(_setup.TestId);
OnPropertyChanged(PropertyNames.ImportFileName.ToString());
OnPropertyChanged(PropertyNames.TestSetupName.ToString());
OnPropertyChanged(PropertyNames.SampleRate.ToString());
OnPropertyChanged(PropertyNames.RecordingMode.ToString());
OnPropertyChanged(PropertyNames.TestLength.ToString());
OnPropertyChanged(PropertyNames.PreTrigger.ToString());
OnPropertyChanged(PropertyNames.PostTrigger.ToString());
OnPropertyChanged(PropertyNames.ROIStart.ToString());
OnPropertyChanged(PropertyNames.ROIEnd.ToString());
OnPropertyChanged(PropertyNames.AAF_TDAS.ToString());
OnPropertyChanged(PropertyNames.AAF_SLICE.ToString());
}
private void OnTestSetupChanged(ITTSSetup setup)
{
TestSetupImported = false;
}
public void UpdateUI()
{
SampleRate = _setup.SampleRate;
SetAvailableRecordingModes();
RecordingMode = _setup.Mode;
SetChannelList();
TestSetupComplete = !SummaryChannelList.Exists(channelType => (channelType.Unassigned != "0") && (channelType.Unassigned != Resources.StringResources.Table_NA));
if (!TestSetupComplete || Array.Exists(_setup.Channels, channel => channel.IsModified) || Array.Exists(_setup.LevelTriggers, levelTrigger => levelTrigger.IsModified))
{
TestSetupImported = false;
}
}
private List<double> _availableSampleRates = new List<double>();
public List<double> AvailableSampleRates
{
get => _availableSampleRates;
set
{
_availableSampleRates = value;
OnPropertyChanged("AvailableSampleRates");
}
}
public void SetAvailableSampleRates(int[] values)
{
AvailableSampleRates.Clear();
var minSampleRate = (double)values.Min();
var maxSampleRate = (double)values.Max();
foreach (var channel in _setup.Channels)
{
if (channel.HardwareChannel == null) continue;
minSampleRate = Math.Max(channel.HardwareChannel.GetParentDAS().GetMinSampleRateDouble(), minSampleRate);
maxSampleRate = Math.Min(channel.HardwareChannel.GetParentDAS().GetMaxSampleRateDouble(), maxSampleRate);
}
foreach (var sampleRate in values)
{
if (sampleRate >= minSampleRate && sampleRate <= maxSampleRate)
{
AvailableSampleRates.Add(sampleRate);
}
}
}
public string AAF_TDAS => string.Format(StringResources.AAF_TDAS, !_aafExceptions.ContainsKey(SampleRate) ? (SampleRate / 5) : _aafExceptions[SampleRate][0]);
public string AAF_SLICE => string.Format(StringResources.AAF_SLICE, !_aafExceptions.ContainsKey(SampleRate) ? (SampleRate / 5) : _aafExceptions[SampleRate][1]);
private Dictionary<double, List<double>> _aafExceptions = new Dictionary<double, List<double>>();
public void SetAAFExceptions(Dictionary<double, List<double>> values)
{
_aafExceptions.Clear();
_aafExceptions = values;
}
public List<RecordingModes> AvailableRecordingModes { get; } = new List<RecordingModes>();
private void SetAvailableRecordingModes()
{
AvailableRecordingModes.Clear();
AvailableRecordingModes.Add(RecordingModes.CircularBuffer);
AvailableRecordingModes.Add(RecordingModes.Recorder);
if (_setup.AllowAdvancedRecordingModes)
{
AvailableRecordingModes.Add(RecordingModes.HybridRecorder);
}
}
public void SetSerializedTestIdValues(string[] values)
{
View?.UpdateTestIds(values);
}
/// <summary>
/// Fill in the table of channels, including those added after the file was read
/// </summary>
public void SetChannelList()
{
const int Assigned = 0;
const int Unassigned = 1;
var analogChannels = new[] { 0, 0 };
var tomChannels = new[] { 0, 0 };
var digitalInChannels = new[] { 0, 0 };
var levelTriggers = new[] { 0, 0 };
foreach (var channelRecord in _setup.Channels)
{
if (string.Equals(channelRecord.ChannelCode, TTSChannelRecord.NONE, StringComparison.CurrentCultureIgnoreCase) || channelRecord.Disabled) continue;
if (channelRecord.IsSquib)
{
if (channelRecord.HardwareChannel != null && channelRecord.HardwareChannel.IsSquib)
{
tomChannels[Assigned] += 1;
}
else
{
tomChannels[Unassigned] += 1;
}
}
else if (channelRecord.IsDigitalInput)
{
if (channelRecord.HardwareChannel != null && channelRecord.HardwareChannel.IsDigitalIn)
{
digitalInChannels[Assigned] += 1;
}
else
{
digitalInChannels[Unassigned] += 1;
}
}
else if (!channelRecord.IsDigitalOutput)
{
if (channelRecord.HardwareChannel != null && channelRecord.HardwareChannel.IsAnalog)
{
analogChannels[Assigned] += 1;
}
else
{
analogChannels[Unassigned] += 1;
}
}
}
foreach (var levelTrigger in _setup.LevelTriggers)
{
if (!string.IsNullOrWhiteSpace(levelTrigger.Code))
{
levelTriggers[Assigned] += 1;
}
}
var temp = new List<SummaryChannel>();
var channel = new SummaryChannel
{
ChannelType = "Analog", //internationalize these
Assigned = analogChannels[Assigned],
Unassigned = analogChannels[Unassigned].ToString()
};
temp.Add(channel);
channel = new SummaryChannel
{
ChannelType = "TOM",
Assigned = tomChannels[Assigned],
Unassigned = tomChannels[Unassigned].ToString()
};
temp.Add(channel);
channel = new SummaryChannel
{
ChannelType = "Digital In",
Assigned = digitalInChannels[Assigned],
Unassigned = digitalInChannels[Unassigned].ToString()
};
temp.Add(channel);
channel = new SummaryChannel
{
ChannelType = "Level Trigger",
Assigned = levelTriggers[Assigned],
Unassigned = Resources.StringResources.Table_NA
};
temp.Add(channel);
SummaryChannelList = temp;
}
private readonly StatusAndProgressBarEventArgs statusAndProgressBarEventArgs = new StatusAndProgressBarEventArgs();
/// <summary>
/// Sets the text, background color, progress value, and/or progress visibility
/// </summary>
/// <param name="status"></param>
/// <param name="error"></param>
public void SetStatus(string status, string error = default(string)) //default(string) is null
{
switch (status)
{
case "Waiting": //change this to string resources but use the same one in DataPRO and here???
statusAndProgressBarEventArgs.StatusColor = BrushesAndColors.Brush_ApplicationStatus_Waiting.Color;
statusAndProgressBarEventArgs.ProgressBarVisibility = Visibility.Collapsed;
break;
case "Working":
statusAndProgressBarEventArgs.StatusColor = BrushesAndColors.Brush_ApplicationStatus_Busy.Color;
statusAndProgressBarEventArgs.ProgressValue = 0;
statusAndProgressBarEventArgs.ProgressBarVisibility = Visibility.Visible;
break;
case "Failed":
TestSetupImported = false;
statusAndProgressBarEventArgs.StatusColor = BrushesAndColors.Brush_ApplicationStatus_Failed.Color;
statusAndProgressBarEventArgs.ProgressBarVisibility = Visibility.Collapsed;
break;
case "Done":
TestSetupImported = true;
foreach (var ch in _setup.Channels)
{
ch.IsModified = false;
}
foreach (var lt in _setup.LevelTriggers)
{
lt.IsModified = false;
}
statusAndProgressBarEventArgs.StatusColor = BrushesAndColors.Brush_ApplicationStatus_Complete.Color;
statusAndProgressBarEventArgs.ProgressBarVisibility = Visibility.Collapsed;
break;
}
if (!string.IsNullOrEmpty(error))
{
statusAndProgressBarEventArgs.StatusText = status + " - " + error;
}
else
{
statusAndProgressBarEventArgs.StatusText = status;
}
statusAndProgressBarEventArgs.Requester = this;
_eventAggregator.GetEvent<StatusAndProgressBarEvent>().Publish(statusAndProgressBarEventArgs);
}
/// <summary>
/// Sets the progress value on the Status and Progress bar
/// </summary>
/// <param name="progress"></param>
public void SetProgress(double progress)
{
statusAndProgressBarEventArgs.ProgressValue = (int)progress;
statusAndProgressBarEventArgs.Requester = this;
_eventAggregator.GetEvent<StatusAndProgressBarEvent>().Publish(statusAndProgressBarEventArgs);
}
private IStatusAndProgressBarView GetStatusAndProgressBarView(IBaseViewModel parent)
{
var view = UnityContainer.Resolve<IStatusAndProgressBarView>();
var viewModel = UnityContainer.Resolve<IStatusAndProgressBarViewModel>();
view.DataContext = viewModel;
viewModel.Initialize(parent);
return view;
}
#endregion
#region Properties
private ITTSSetup _setup;
public bool IsDirty { get; private set; }
private bool _isBusy = false;
public bool IsBusy
{
get => _isBusy;
set
{
_isBusy = value;
OnPropertyChanged("IsBusy");
}
}
private bool _isMenuIncluded = false;
public bool IsMenuIncluded
{
get => _isMenuIncluded;
set
{
_isMenuIncluded = value;
OnPropertyChanged("IsMenuIncluded");
}
}
private bool _isNavigationIncluded = false;
public bool IsNavigationIncluded
{
get => _isNavigationIncluded;
set
{
_isNavigationIncluded = value;
OnPropertyChanged("IsNavigationIncluded");
}
}
private List<SummaryChannel> _summaryChannelList = new List<SummaryChannel>();
public List<SummaryChannel> SummaryChannelList
{
get => _summaryChannelList;
set
{
_summaryChannelList = value;
OnPropertyChanged("SummaryChannelList");
}
}
private bool _testSetupComplete;
public bool TestSetupComplete
{
get => _testSetupComplete;
set
{
_testSetupComplete = value;
OnPropertyChanged("ImportEnabled");
}
}
private bool _testSetupImported;
public bool TestSetupImported
{
get => _testSetupImported;
set
{
_testSetupImported = value;
OnPropertyChanged("RunTestVisible");
}
}
public bool ImportEnabled => TestSetupComplete & IsROIValid;
public Visibility RunTestVisible => TestSetupImported ? Visibility.Visible : Visibility.Hidden;
public Visibility SummaryPreTriggerVisibility => RecordingMode == RecordingModes.CircularBuffer ? Visibility.Visible : Visibility.Collapsed;
public string PostTriggerOrTestLength => RecordingMode == RecordingModes.CircularBuffer ? StringResources.PostTrigger : StringResources.TestLength;
private bool _isROIStartValid = true;
public bool IsROIStartValid
{
get => _isROIStartValid;
set
{
_isROIStartValid = value;
OnPropertyChanged("IsROIStartValid");
}
}
private bool _isROIEndValid = true;
public bool IsROIEndValid
{
get => _isROIEndValid;
set
{
_isROIEndValid = value;
OnPropertyChanged("IsROIEndValid");
}
}
private void CalculateROIValid()
{
if (null == _setup)
{
IsROIEndValid = true;
IsROIStartValid = true;
return;
}
if (_setup.ROIEnd < _setup.ROIStart)
{
IsROIStartValid = false;
IsROIEndValid = false;
return;
}
if (RecordingMode == RecordingModes.Recorder)
{
IsROIStartValid = true;
IsROIEndValid = !(_setup.ROIEnd - _setup.ROIStart > _setup.PostTrigger);
}
else
{
IsROIEndValid = !(_setup.ROIEnd > _setup.PostTrigger);
IsROIStartValid = !(Math.Abs(_setup.ROIStart) > _setup.PreTrigger);
}
}
public bool IsROIValid
{
get
{
CalculateROIValid();
return IsROIStartValid && IsROIEndValid;
}
}
//=> RecordingMode != RecordingModes.Recorder || !(_setup.ROIEnd - _setup.ROIStart > _setup.PostTrigger);
#endregion Properties
#region Commands
/// <summary>
/// browse to a file to import, should be xml, maybe needs a few other criteria
/// </summary>
private DelegateCommand _importCommand;
public DelegateCommand ImportClicked => _importCommand ?? (_importCommand = new DelegateCommand(ImportMethod));
private void ImportMethod()
{
_eventAggregator.GetEvent<TTSImportSummaryImportEvent>().Publish(_setup);
}
/// <summary>
/// Run the test since the Run Test button was clicked
/// </summary>
private DelegateCommand _runTestCommand;
public DelegateCommand RunTestClicked =>
_runTestCommand ?? (_runTestCommand = new DelegateCommand(RunTestMethod));
private void RunTestMethod()
{
SetStatus(Resources.StringResources.ImportTestSetup_PossibleStatus_Working); //use string resource (same both here and where passed)
ThreadPool.QueueUserWorkItem(RunTestWorkThread, null);
}
void RunTestWorkThread(object obj)
{
_eventAggregator.GetEvent<TTSImportSummaryRunTestEvent>().Publish(_setup);
}
#endregion
#region properties
/// <summary>
/// all properties that are exposed
/// </summary>
public enum PropertyNames
{
ImportFileName,
TestSetupName,
SampleRate,
RecordingMode,
TestLength,
PreTrigger,
PostTrigger,
ROIStart,
ROIEnd,
AAF_TDAS,
AAF_SLICE,
AllowAdvancedRecordingModes,
AllowTSRAIRRecordingModes
}
/// <summary>
/// full path to import file
/// </summary>
public string ImportFileName => _setup?.Filename ?? "";
public string TestSetupName
{
get => _setup?.TestId ?? "";
set
{
TestSetupImported = false;
_setup.TestId = value;
View?.SetTestName(_setup.TestId);
}
}
public double SampleRate
{
get => _setup?.SampleRate ?? 10000;
set
{
if (_setup.SampleRate != value)
{
TestSetupImported = false;
_setup.SampleRate = value;
}
OnPropertyChanged(PropertyNames.SampleRate.ToString());
OnPropertyChanged(PropertyNames.AAF_TDAS.ToString());
OnPropertyChanged(PropertyNames.AAF_SLICE.ToString());
}
}
private void OnPropertyChanged(PropertyNames sampleRate)
{
throw new NotImplementedException();
}
public RecordingModes RecordingMode
{
get => _setup?.Mode ?? RecordingModes.CircularBuffer;
set
{
if (_setup.Mode != value)
{
TestSetupImported = false;
switch (value)
{
case RecordingModes.CircularBuffer:
case RecordingModes.Recorder:
case RecordingModes.HybridRecorder:
_setup.Mode = value;
break;
default:
_setup.Mode = RecordingModes.CircularBuffer;
break;
}
}
OnPropertyChanged("RecordingMode");
OnPropertyChanged("SummaryPreTriggerVisibility");
OnPropertyChanged("PostTriggerOrTestLength");
OnPropertyChanged("IsROIValid");
OnPropertyChanged("ImportEnabled");
}
}
public string PreTrigger
{
get => _setup?.PreTrigger.ToString("0.00") ?? "";
set
{
if (!double.TryParse(value, out double preTrigger)) return;
//if (preTrigger * -1 > _setup.ROIStart) ROIStart = (preTrigger * -1).ToString("0.00");
//if (preTrigger * -1 > _setup.ROIEnd) ROIEnd = (preTrigger * -1).ToString("0.00");
TestSetupImported = false;
_setup.PreTrigger = preTrigger;
OnPropertyChanged("PreTrigger");
OnPropertyChanged("IsROIValid");
OnPropertyChanged("ImportEnabled");
}
}
public string PostTrigger
{
get => _setup?.PostTrigger.ToString("0.00") ?? "";
set
{
if (!double.TryParse(value, out double postTrigger)) return;
//if (postTrigger < _setup.ROIStart) ROIStart = postTrigger.ToString("0.00");
//if (postTrigger < _setup.ROIEnd) ROIEnd = postTrigger.ToString("0.00");
TestSetupImported = false;
_setup.PostTrigger = postTrigger;
OnPropertyChanged("PostTrigger");
OnPropertyChanged("IsROIValid");
OnPropertyChanged("ImportEnabled");
}
}
public string ROIStart
{
get => _setup?.ROIStart.ToString("0.00") ?? "";
set
{
if (!double.TryParse(value, out double roiStart)) return;
if (roiStart < _setup.PreTrigger * -1 || roiStart > _setup.PostTrigger || roiStart > _setup.ROIEnd) return;
TestSetupImported = false;
_setup.ROIStart = roiStart;
OnPropertyChanged("ROIStart");
OnPropertyChanged("IsROIValid");
OnPropertyChanged("ImportEnabled");
}
}
public string ROIEnd
{
get => _setup?.ROIEnd.ToString("0.00") ?? "";
set
{
if (!double.TryParse(value, out double roiEnd)) return;
if (roiEnd < _setup.PreTrigger * -1 || roiEnd > _setup.PostTrigger || roiEnd < _setup.ROIStart) return;
TestSetupImported = false;
_setup.ROIEnd = roiEnd;
OnPropertyChanged("ROIEnd");
OnPropertyChanged("IsROIValid");
OnPropertyChanged("ImportEnabled");
}
}
#endregion
///<summary>
///Occurs when a property value changes.
///</summary>
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -0,0 +1,491 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;
using DTS.Common.Events;
using DTS.Common.Events.TTSImport;
using DTS.Common.Interface.DataRecorders;
using DTS.Common.Interface.TestSetups.Imports.TTS;
using DTS.Common.Interface.TestSetups.Imports.TTS.ReadFile;
using Prism.Events;
using Unity;
using DTS.Common.Interactivity;
using Prism.Regions;
using Prism.Commands;
using TTSImport.Model;
using TTSImport.Resources;
using DTS.DASLib.Service;
// ReSharper disable CheckNamespace
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable InconsistentNaming
namespace TTSImport
{
/// <summary>
/// this class handles Level Trigger edit/create functionality
/// </summary>
[PartCreationPolicy(CreationPolicy.Shared)]
public class TOMChannelsViewModel : ITOMChannelsViewModel
{
/// <summary>
/// The Hardware Scan view
/// </summary>
public ITOMChannelsView View { get; set; }
private IEventAggregator _eventAggregator { get; }
private IRegionManager _regionManager;
private IUnityContainer UnityContainer { get; }
public InteractionRequest<Notification> NotificationRequest { get; }
public InteractionRequest<Confirmation> ConfirmationRequest { get; }
/// <summary>
/// Creates a new instance of the TechnologyDomainEditViewModel.
/// </summary>
/// <param name="view">The ITOMChannelsView.</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 TOMChannelsViewModel(ITOMChannelsView 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);
_eventAggregator.GetEvent<AssignedChannelsChangedEvent>().Subscribe(OnAssignedChannelsChangedEvent,
ThreadOption.PublisherThread, true);
_eventAggregator.GetEvent<TTSImportHardwareScanFinishedEvent>()
.Subscribe(OnHardwareScanComplete, ThreadOption.PublisherThread, true);
_eventAggregator.GetEvent<TTSImportReadFileStatusEvent>().Subscribe(OnReadFileFinished, ThreadOption.PublisherThread, true);
}
#region Methods
private void OnReadFileFinished(ReadFileStatusArg statusArg)
{
_setup = statusArg.TTSSetup;
_hardware = null;
}
private void OnHardwareScanComplete(List<IDASHardware> hardware)
{
_hardware = hardware;
}
private void OnAssignedChannelsChangedEvent(ITTSSetup setup)
{
if (!Application.Current.Dispatcher.CheckAccess())
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
OnAssignedChannelsChangedEvent(setup);
}));
return;
}
_setup = setup;
if (null == _hardware || null == _setup) { return; }
var channels = new ObservableCollection<Model.DASChannel>();
var remainingChannels = new ObservableCollection<ITTSChannelRecord>();
var channelIdToChannel = new Dictionary<string, Model.DASChannel>();
foreach (var das in _hardware)
{
var ichannels = das.GetIHardwareChannels();
for (var i = 0; i < ichannels.Length; i += 2)
{
var ch = ichannels[i];
if (!ch.IsSquib) { continue; }
var newChannel = new Model.DASChannel(ch);
channels.Add(newChannel);
channelIdToChannel[ch.GetId()] = newChannel;
}
}
foreach (var channelRecord in _setup.Channels)
{
if (channelRecord.IsEmptyRecord)
{
continue;
}
if (!channelRecord.IsSquib)
{
continue;
}
if (!channelRecord.IsChannelCodeValid)
{
continue;
}
if (channelRecord.ChannelCode == TTSChannelRecord.NONE)
{
continue;
}
if (null != channelRecord.HardwareChannel)
{
if (channelIdToChannel.ContainsKey(channelRecord.HardwareChannel.GetId()))
{
channelIdToChannel[channelRecord.HardwareChannel.GetId()].SetITTSChannelRecord(channelRecord);
}
}
else
{
remainingChannels.Add(channelRecord);
}
}
DASChannels = channels;
RemainingChannels = remainingChannels;
OnPropertyChanged("DASChannels");
OnPropertyChanged("RemainingChannels");
}
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
});
}
#endregion
#region Properties
private ITTSSetup _setup;
private IList<IDASHardware> _hardware;
public bool IsDirty { get; private set; }
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 AssignEnabled { get; set; }
public bool RemoveEnabled { get; set; }
public bool EnableOrDisableEnabled { get; set; }
public ObservableCollection<Model.DASChannel> DASChannels { get; set; } = new ObservableCollection<Model.DASChannel>();
public ObservableCollection<ITTSChannelRecord> RemainingChannels { get; set; } =
new ObservableCollection<ITTSChannelRecord>();
private ITTSChannelRecord _selectedRemainingChannel;
public ITTSChannelRecord SelectedRemainingChannel
{
get => _selectedRemainingChannel;
set
{
_selectedRemainingChannel = value;
if (null == _selectedRemainingChannel || null == SelectedDASChannel) return;
AssignEnabled = true;
OnPropertyChanged("AssignEnabled");
}
}
private void DetermineRemoveEnableStatus()
{
if (_selectedDASChannel?.Channel != null)
{
if (!string.IsNullOrWhiteSpace(_selectedDASChannel.EID) &&
_selectedDASChannel.EID == _selectedDASChannel.Channel.SensorEID)
{
//can only be replaced, can't be removed
RemoveEnabled = false;
}
RemoveEnabled = true;
}
else
{
RemoveEnabled = false;
}
OnPropertyChanged("RemoveEnabled");
AssignEnabled = null != _selectedDASChannel && null != _selectedRemainingChannel;
OnPropertyChanged("AssignEnabled");
EnableOrDisableEnabled = _selectedDASChannel?.Channel != null;
OnPropertyChanged("EnableOrDisableEnabled");
OnPropertyChanged("EnableOrDisableText");
}
private Model.DASChannel _selectedDASChannel;
public Model.DASChannel SelectedDASChannel
{
get => _selectedDASChannel;
set
{
_selectedDASChannel = value;
DetermineRemoveEnableStatus();
}
}
public string EnableOrDisableText
{
get
{
if (SelectedDASChannel?.Channel == null)
{
return StringResources.Analog_Enable;
}
return SelectedDASChannel.Channel.Disabled
? StringResources.Analog_Enable
: StringResources.Analog_Disable;
}
}
#endregion Properties
#region Commands
#region assign
/// <summary>
/// Assign a channel code to a channel
/// </summary>
private DelegateCommand _assignCommand;
public DelegateCommand AssignCommand => _assignCommand ?? (_assignCommand = new DelegateCommand(Assign));
private void Assign()
{
if (SelectedRemainingChannel == null) { return; }
_eventAggregator.GetEvent<TTSImportTestSetupChangedEvent>().Publish(_setup);
//BEFORE we go any further, check the state of sensor ids
//if the channel has a sensor id AND there's a sensor on the channel with the same id
//then prompt on replacing the id
//IF the channel has a sensor id and there's no sensor on the channel BUT the new sensor has a different id
//then prompt on replacing the id
var bReplacingID = false;
if (!string.IsNullOrWhiteSpace(SelectedDASChannel.EID))
{
if (null != SelectedRemainingChannel && SelectedRemainingChannel.SensorEID != SelectedDASChannel.EID)
{
bReplacingID = true;
}
//if existing channel has this ID, we will need to clear out the old id and assign a new one...
if (null != SelectedDASChannel.Channel && SelectedDASChannel.Channel.SensorEID == SelectedDASChannel.EID)
{
bReplacingID = true;
}
}
if (bReplacingID)
{
Task.Run(() =>
{
var dialogResult = MessageBox.Show(StringResources.AssignSensorPrompt, StringResources.UserFeedbackRequired, MessageBoxButton.YesNo);
if (dialogResult == MessageBoxResult.Yes)
{
Application.Current.Dispatcher.BeginInvoke(new Action(AssignWork));
}
});
return;
}
AssignWork();
}
/// <summary>
/// assigns a channel record to a physical channel
/// </summary>
private void AssignWork()
{
if (null != SelectedDASChannel.Channel)
{
if (!string.IsNullOrWhiteSpace(SelectedDASChannel.EID))
{
SelectedDASChannel.Channel.SensorEID = "";
}
RemainingChannels.Add(SelectedDASChannel.Channel);
}
if (!string.IsNullOrWhiteSpace(SelectedDASChannel.EID))
{
SelectedRemainingChannel.SensorEID = SelectedDASChannel.EID;
}
SelectedDASChannel.SetITTSChannelRecord(SelectedRemainingChannel);
var channel = SelectedRemainingChannel;
var index = RemainingChannels.IndexOf(channel);
SelectedRemainingChannel = null;
RemainingChannels.Remove(channel);
if (index < RemainingChannels.Count)
{
SelectedRemainingChannel = RemainingChannels[index];
OnPropertyChanged("SelectedRemainingChannel");
}
else if (RemainingChannels.Count > 0)
{
SelectedRemainingChannel = RemainingChannels[index - 1];
OnPropertyChanged("SelectedRemainingChannel");
}
CollectionViewSource.GetDefaultView(DASChannels)?.Refresh();
index = DASChannels.IndexOf(SelectedDASChannel);
for (var i = index; i < DASChannels.Count; i++)
{
var dasChannel = DASChannels[i];
if (null != dasChannel.Channel) { continue; }
SelectedDASChannel = dasChannel;
OnPropertyChanged("SelectedDASChannel");
return;
}
//didn't find a match, start from the beginning?
for (var i = 0; i < index; i++)
{
var dasChannel = DASChannels[i];
if (null != dasChannel.Channel)
{
continue;
}
SelectedDASChannel = dasChannel;
OnPropertyChanged("SelectedDASChannel");
return;
}
//if we get here there's no new channel to go to, but we need to set the remove/enable/disable button status
OnPropertyChanged("SelectedDASChannel");
DetermineRemoveEnableStatus();
}
#endregion
#region remove
private DelegateCommand _removeCommand;
public DelegateCommand RemoveCommand => _removeCommand ?? (_removeCommand = new DelegateCommand(Remove));
/// <summary>
/// remove a hardware channel assignment (does not remove the channel from the test setup though?)
/// </summary>
private void Remove()
{
if (null == SelectedDASChannel || null == SelectedDASChannel.Channel) { return; }
_eventAggregator.GetEvent<TTSImportTestSetupChangedEvent>().Publish(_setup);
RemainingChannels.Add(SelectedDASChannel.Channel);
SelectedDASChannel.SetITTSChannelRecord(null);
CollectionViewSource.GetDefaultView(DASChannels)?.Refresh();
var index = DASChannels.IndexOf(SelectedDASChannel);
for (var i = index; i < DASChannels.Count; i++)
{
if (null == DASChannels[i].Channel) { continue; }
SelectedDASChannel = DASChannels[i];
OnPropertyChanged("SelectedDASChannel");
return;
}
for (var i = 0; i < index; i++)
{
if (null == DASChannels[i].Channel) { continue; }
SelectedDASChannel = DASChannels[i];
OnPropertyChanged("SelectedDASChannel");
return;
}
//if we get here there's no new channel to go to, but we need to set the remove/enable/disable button status
OnPropertyChanged("SelectedDASChannel");
DetermineRemoveEnableStatus();
}
#endregion
#region enableordisable
private DelegateCommand _enableOrDisableCommand;
public DelegateCommand EnableOrDisableCommand =>
_enableOrDisableCommand ?? (_enableOrDisableCommand = new DelegateCommand(EnableOrDisable));
/// <summary>
/// enables or disables a channel in the test.
/// </summary>
private void EnableOrDisable()
{
if (SelectedDASChannel?.Channel == null) { return; }
SelectedDASChannel.Channel.Disabled = !SelectedDASChannel.Channel.Disabled;
SelectedDASChannel.Disabled = SelectedDASChannel.Channel.Disabled;
OnPropertyChanged("EnableOrDisableText");
_eventAggregator.GetEvent<TTSImportTestSetupChangedEvent>().Publish(_setup);
}
#endregion
#endregion
/// <inheritdoc />
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}