Files
DP44/DataPRO/Modules/TestSetups/Imports/TTS/ViewModel/AnalogChannelsViewModel.cs
2026-04-17 14:55:32 -04:00

623 lines
25 KiB
C#

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
}
}