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

862 lines
38 KiB
C#

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