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,142 @@
<base:BaseView x:Class="DTS.Viewer.TestModification.TestModificationView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:base="clr-namespace:DTS.Common.Base;assembly=DTS.Common"
xmlns:toolkit="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:converters="clr-namespace:DTS.Common.Converters;assembly=DTS.Common"
xmlns:validators="clr-namespace:DTS.Common.Validators;assembly=DTS.Common"
xmlns:strings="clr-namespace:DTS.Viewer.TestModification" x:Name="testViewModification">
<base:BaseView.Resources>
<ResourceDictionary>
<ControlTemplate x:Key="ValidationTemplate">
<DockPanel>
<TextBlock Foreground="Red" FontSize="14" DockPanel.Dock="Right" ToolTip="{Binding [0].ErrorContent}" Margin="1,0,0,0" Text="⚠"/>
<AdornedElementPlaceholder/>
</DockPanel>
</ControlTemplate>
<!-- ReSharper disable once Xaml.RedundantResource -->
<converters:BooleanToOpacityConverter x:Key="BooleanToOpacityConverter" />
<!-- ReSharper disable once Xaml.RedundantResource -->
<converters:EnumBooleanConverter x:Key="EnumBooleanConverter" />
<converters:InverseBooleanToOpacityConverter x:Key="InverseBooleanToOpacityConverter" />
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<converters:InverseBooleanConverter x:Key="InverseBooleanConverter"/>
<converters:BooleanToColorConverter x:Key="InvertedBooleanToAttentionBackgroundColorConverter" Background="True" Inverted="True" AttentionBrush="True" />
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Resources/TestModificationResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</base:BaseView.Resources>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Top">
<Label Content="{strings:TranslateExtension PleaseLockHeader}" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
Visibility="{Binding Path=TestModificationVisability, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter=false}" />
<Grid Visibility="{Binding Path=TestModificationVisability, Converter={StaticResource BooleanToVisibilityConverter}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- DESCRIPTION -->
<Label Grid.Row="0" Grid.Column="0" Content="{strings:TranslateExtension Description}" AutomationProperties.AutomationId="LabelDescription" />
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=Model.Description}" x:Name="tbDescription" AutomationProperties.AutomationId="TextBoxDescription" IsEnabled="{Binding Path=Model.EnableDescriptionControl}" Background="{Binding Path=Model.IsModifiedDescription, Converter={StaticResource InvertedBooleanToAttentionBackgroundColorConverter}}"/>
<!-- FILTER -->
<Label Grid.Row="1" Grid.Column="0" Content="{strings:TranslateExtension Filter}" AutomationProperties.AutomationId="LabelFilter" Background="{Binding Path=Model.IsModifiedFilter, Converter={StaticResource InvertedBooleanToAttentionBackgroundColorConverter}}" />
<ComboBox Grid.Row="1" Grid.Column="1" DisplayMemberPath="FilterName" ItemsSource="{Binding ElementName=testViewModification, Path=AvailableCFC}" SelectedItem="{Binding Path=Model.SelectedFilter}" AutomationProperties.AutomationId="ComboBoxFilter" IsEnabled="{Binding Path=Model.EnableFilterControl}" MinWidth="200" Grid.ColumnSpan="2"/>
<!-- DATAFLAG -->
<Label Grid.Row="2" Grid.Column="0" Content="{strings:TranslateExtension DataFlag}" AutomationProperties.AutomationId="LabelDataFlag" Background="{Binding Path=Model.IsModifiedDataFlag, Converter={StaticResource InvertedBooleanToAttentionBackgroundColorConverter}}"/>
<ComboBox Grid.Row="2" Grid.Column="1" ItemsSource="{Binding Source={StaticResource DataFlagProvider}}" SelectedItem="{Binding Path=Model.SelectedDataFlag}" AutomationProperties.AutomationId="ComboBoxDataFlag" IsEnabled="{Binding Path=Model.IsDataFlagEnabled}"/>
<!-- T0 -->
<Label Grid.Row="3" Grid.Column="0" Content="{strings:TranslateExtension ShiftT0ms}" AutomationProperties.AutomationId="TextBlockT0" />
<toolkit:DoubleUpDown Grid.Row="3" Grid.Column="1" FormatString="N3" Value="{Binding Path=Model.T0}" Background="{Binding Path=Model.IsModifiedT0, Converter={StaticResource InvertedBooleanToAttentionBackgroundColorConverter}}" AutomationProperties.AutomationId="DoubleUpDownT0" IsEnabled="{Binding Path=Model.IsT0Enabled}"/>
<ComboBox Grid.Row="3" Grid.Column="2" ItemsSource="{Binding Source={StaticResource T0ModeProvider}}" SelectedItem="{Binding Path=Model.T0Mode}" Width="80" IsEnabled="{Binding Path=Model.IsT0ModeTestOnly, Converter={StaticResource InverseBooleanConverter}}" AutomationProperties.AutomationId="ComboBoxT0Mode" />
<!-- EU MULTIPLIER -->
<Label Grid.Row="4" Grid.Column="0" Content="{strings:TranslateExtension EUMultiplier}" AutomationProperties.AutomationId="LabelEuMultiplier" />
<toolkit:DoubleUpDown Grid.Row="4" Grid.Column="1" FormatString="N8" Width="{Binding ActualWidth, ElementName=tbDescription}" Value="{Binding Path=Model.EuMultiplier}" Background="{Binding Path=Model.IsModifiedEuMultiplier, Converter={StaticResource InvertedBooleanToAttentionBackgroundColorConverter}}" AutomationProperties.AutomationId="DoubleUpDownEuMultiplier" IsEnabled="{Binding Path=Model.EnableEUMultiplierControl}"/>
<!-- EU OFFSET -->
<Label Grid.Row="5" Grid.Column="0" Content="{strings:TranslateExtension EUOffset}" AutomationProperties.AutomationId="LabelEuOffset" />
<toolkit:DoubleUpDown Grid.Row="5" Grid.Column="1" FormatString="N8" HorizontalAlignment="Stretch" Value="{Binding Path=Model.EuOffset}" AutomationProperties.AutomationId="DoubleUpDownEuOffset" IsEnabled="{Binding Path=Model.EnableEUOffsetControl}" Background="{Binding Path=Model.IsModifiedEuOffset, Converter={StaticResource InvertedBooleanToAttentionBackgroundColorConverter}}" />
<!-- SENSITIVITY -->
<Label Grid.Row="6" Grid.Column="0" Content="{strings:TranslateExtension Sensitivity}" AutomationProperties.AutomationId="LabelSensitivity" />
<toolkit:DoubleUpDown Grid.Row="6" Grid.Column="1" FormatString="N12" Value="{Binding Path=Model.Sensitivity}" AutomationProperties.AutomationId="DoubleUpDownSensitivity" IsEnabled="{Binding Path=Model.EnableSensitivityControl}" Background="{Binding Path=Model.IsModifiedSensitivity, Converter={StaticResource InvertedBooleanToAttentionBackgroundColorConverter}}" />
<!-- LINEFIT -->
<Label Grid.Row="7" Grid.Column="0" Content="{strings:TranslateExtension LineFit}" AutomationProperties.AutomationId="TextBlockLineFit" />
<Label Grid.Row="8" Grid.Column="0" Content="{strings:TranslateExtension T1ms}" AutomationProperties.AutomationId="TextBlockT1" />
<toolkit:DoubleUpDown Grid.Row="8" Grid.Column="1" FormatString="N3" Value="{Binding Path=Model.T1}" AutomationProperties.AutomationId="DoubleUpDownT1" IsEnabled="{Binding Path=Model.EnableLineFitControl}" Background="{Binding Path=Model.IsModifiedLineFit, Converter={StaticResource InvertedBooleanToAttentionBackgroundColorConverter}}"/>
<Label Grid.Row="9" Grid.Column="0" Content="{strings:TranslateExtension T2ms}" AutomationProperties.AutomationId="TextBlockT2" />
<toolkit:DoubleUpDown Grid.Row="9" Grid.Column="1" FormatString="N3" Value="{Binding Path=Model.T2}" AutomationProperties.AutomationId="DoubleUpDownT2" IsEnabled="{Binding Path=Model.EnableLineFitControl}" Background="{Binding Path=Model.IsModifiedLineFit, Converter={StaticResource InvertedBooleanToAttentionBackgroundColorConverter}}"/>
<Grid Grid.Row="10" Grid.Column="0" Grid.ColumnSpan="3">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Rectangle Grid.Row="0" Grid.ColumnSpan="2" Fill="Black" Height="1" Margin="5"/>
<Button Grid.Row="1" Grid.Column="0" Content="{strings:TranslateExtension Undo}" Command="{Binding Path=UndoCommand}" IsEnabled="{Binding Path=Model.IsModified}" AutomationProperties.AutomationId="ButtonUndoWrite"/>
<Button Grid.Row="1" Grid.Column="1" Content="{strings:TranslateExtension WriteFiles}" Command="{Binding Path=WriteCommand}" IsEnabled="{Binding Path=Model.IsModified}" AutomationProperties.AutomationId="ButtonWrite" />
<Button Grid.Row="2" Grid.Column="0" Content="{strings:TranslateExtension Preview}" Command="{Binding Path=PreviewCommand}" IsEnabled="{Binding Path=Model.IsModifiedLineFit}" AutomationProperties.AutomationId="ButtonPreview" Visibility="Collapsed"/>
<Button Grid.Row="2" Grid.Column="1" Content="{strings:TranslateExtension UndoAll}" Command="{Binding Path=UndoAllCommand}" IsEnabled="{Binding Path=IsBackedUp}" AutomationProperties.AutomationId="ButtonUndoAll"/>
</Grid>
<GroupBox Header="{strings:TranslateExtension SensorCalibration}" Margin="5" HorizontalAlignment="Left" AutomationProperties.AutomationId="SensorCalibrationBx"
Visibility="{Binding Path=Model.ShowSensorCal, Converter={StaticResource BooleanToVisibilityConverter}}" Grid.Row="11"
Grid.ColumnSpan="3">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Sensitivity -->
<TextBlock Grid.Row="0" Grid.Column="0" Text="{strings:TranslateExtension Sensitivity}" IsEnabled="{Binding Path=Model.NonLinear, Converter={StaticResource InverseBooleanConverter}}"/>
<TextBox Grid.Row="0" Grid.Column="1" Width="88" Validation.ErrorTemplate="{StaticResource ValidationTemplate}">
<Binding Path="Model.CalSensitivity" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True" ValidatesOnNotifyDataErrors="True" ValidatesOnExceptions="True">
<Binding.ValidationRules>
<validators:SensitivityValidator x:Name="NewSensitivityValidator" />
</Binding.ValidationRules>
</Binding>
</TextBox>
<!-- Proportional to Excitation -->
<TextBlock Grid.Row="1" Grid.Column="0" Text="{strings:TranslateExtension ProportionalToExcitation}" />
<CheckBox Grid.Row="1" Grid.Column="1" IsEnabled="False" IsChecked="{Binding Path=Model.ProportionalToExcitation, Mode=OneWay}"/>
<!-- Cal date -->
<TextBlock Grid.Row="2" Grid.Column="0" Text="{strings:TranslateExtension CalDate}" />
<TextBlock Grid.Row="2" Grid.Column="1" Width="120" Text="{Binding Model.CalDate}"/>
<!-- Modify Date -->
<TextBlock Grid.Row="3" Grid.Column="0" Text="{strings:TranslateExtension ModifyDate}" />
<TextBlock Grid.Row="3" Grid.Column="1" Width="120" Text="{Binding Model.ModifyDate}"/>
<!-- Commit Button -->
<Button Content="{strings:TranslateExtension UpdateDatabase}" Margin="10" Padding="10, 5" AutomationProperties.AutomationId="btnUpdateDatabase"
Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" Command="{Binding Path=Model.UpdateDatabaseCommand}"/>
</Grid>
</GroupBox>
</Grid>
</Grid>
</base:BaseView>

View File

@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 1.3
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">1.3</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized .NET Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the .NET Framework object]
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,205 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="CalDate" xml:space="preserve">
<value>Cal date</value>
</data>
<data name="DataFlag" xml:space="preserve">
<value>Data Flag:</value>
</data>
<data name="Description" xml:space="preserve">
<value>Description:</value>
</data>
<data name="EUMultiplier" xml:space="preserve">
<value>EU Multiplier:</value>
</data>
<data name="EUOffset" xml:space="preserve">
<value>EU Offset:</value>
</data>
<data name="FailedToModifySensitivity" xml:space="preserve">
<value>Failed to modify sensitivity: </value>
</data>
<data name="Filter" xml:space="preserve">
<value>Filter:</value>
</data>
<data name="LineFit" xml:space="preserve">
<value>Line Fit:</value>
</data>
<data name="ModifyDate" xml:space="preserve">
<value>Modify date</value>
</data>
<data name="NonLinear" xml:space="preserve">
<value>Non-linear</value>
</data>
<data name="PleaseLockHeader" xml:space="preserve">
<value>To enable, please lock a single channel</value>
<comment>Instructions asking user to lock a channel in order to see modification settings</comment>
</data>
<data name="Preview" xml:space="preserve">
<value>Preview</value>
</data>
<data name="ProportionalToExcitation" xml:space="preserve">
<value>Proportional to excitation</value>
</data>
<data name="Sensitivity" xml:space="preserve">
<value>Sensitivity:</value>
</data>
<data name="SensorCalibration" xml:space="preserve">
<value>Sensor calibration (most recent in db)</value>
</data>
<data name="ShiftT0ms" xml:space="preserve">
<value>Shift T₀ (ms):</value>
</data>
<data name="T0MustBeInDataset" xml:space="preserve">
<value>Modification can not be made, T0 must be in the dataset.</value>
<comment>Prompt given when the user clicks on write but T0 is modified and not in the dataset</comment>
</data>
<data name="T1ms" xml:space="preserve">
<value>T₁ (ms):</value>
</data>
<data name="T2ms" xml:space="preserve">
<value>T₂ (ms):</value>
</data>
<data name="Undo" xml:space="preserve">
<value>Cancel</value>
<comment>Button for undoing change(s) made in UI before committing to disk</comment>
</data>
<data name="UndoAll" xml:space="preserve">
<value>Restore All</value>
<comment>Button for reverting Test Viewer settings to the backup on disk</comment>
</data>
<data name="UndoAllPrompt" xml:space="preserve">
<value>This will revert your saved test modifications to the backup on file. Continue?</value>
</data>
<data name="UndoPrompt" xml:space="preserve">
<value>This will undo any change(s) to this channel made before saving. Continue?</value>
<comment>Prompt given to user before undoing UI changes</comment>
</data>
<data name="UpdateDatabase" xml:space="preserve">
<value>Update database</value>
</data>
<data name="WriteFiles" xml:space="preserve">
<value>Write</value>
<comment>Button for writing Test Modifications to disk</comment>
</data>
<data name="WriteFilesPrompt" xml:space="preserve">
<value>Are you sure you want to write these changes to disk?</value>
<comment>Prompt given to user before writing changes to disk</comment>
</data>
</root>

View File

@@ -0,0 +1,166 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{5EE7C61F-E9FE-479B-BE1F-78A142341C3B}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>DTS.Viewer.TestModification</RootNamespace>
<AssemblyName>DTS.Viewer.TestModification</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SccProjectName>
</SccProjectName>
<SccLocalPath>
</SccLocalPath>
<SccAuxPath>
</SccAuxPath>
<SccProvider>
</SccProvider>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Xaml.Behaviors">
<HintPath>..\..\..\Common\DTS.Common\lib\PrismLibrary\Microsoft.Xaml.Behaviors.dll</HintPath>
</Reference>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="Prism">
<HintPath>..\..\..\Common\DTS.Common\lib\PrismLibrary\Prism.dll</HintPath>
</Reference>
<Reference Include="Prism.Unity.Wpf">
<HintPath>..\..\..\Common\DTS.Common\lib\PrismLibrary\Prism.Unity.Wpf.dll</HintPath>
</Reference>
<Reference Include="Prism.Wpf">
<HintPath>..\..\..\Common\DTS.Common\lib\PrismLibrary\Prism.Wpf.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Core" />
<Reference Include="System.Xaml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="Unity.Abstractions">
<HintPath>..\..\..\Common\DTS.Common\lib\PrismLibrary\Unity.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Unity.Container">
<HintPath>..\..\..\Common\DTS.Common\lib\PrismLibrary\Unity.Container.dll</HintPath>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="Xceed.Wpf.Toolkit">
<HintPath>..\..\..\Common\DTS.Common\lib\Xceed.Wpf.Toolkit\Xceed.Wpf.Toolkit.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Model\Enums.cs" />
<Compile Include="Model\TestModelManipulation.cs" />
<Compile Include="Model\TestModificationModel.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Resources\StringResources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>StringResources.resx</DependentUpon>
</Compile>
<Compile Include="Resources\TranslateExtension.cs" />
<Compile Include="TestModificationModule.cs" />
<Compile Include="ViewModel\TestModificationViewModel.cs" />
<Compile Include="View\TestModificationView.xaml.cs">
<DependentUpon>TestModificationView.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Page Include="Resources\TestModificationResources.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="View\TestModificationView.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Common\DTS.Common.Core\DTS.Common.Core.csproj">
<Project>{fab1f470-1574-4301-b56e-d3364aa93679}</Project>
<Name>DTS.Common.Core</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Common\DTS.Common.DAS.Concepts\DTS.Common.DAS.Concepts.csproj">
<Project>{03d8c736-36eb-4cd1-a6d9-130452b23239}</Project>
<Name>DTS.Common.DAS.Concepts</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Common\DTS.Common.Serialization\DTS.Common.Serialization.csproj">
<Project>{b7d50b14-fa61-4fe4-bff7-b257902b55b0}</Project>
<Name>DTS.Common.Serialization</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Common\DTS.Common.SettingsDB\DTS.Common.Settings.csproj">
<Project>{61017104-D8EE-41D1-B9CA-DAD863FF78B2}</Project>
<Name>DTS.Common.Settings</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Common\DTS.Common.Utilities\DTS.Common.Utilities.csproj">
<Project>{03eace47-ea59-44ac-b49d-956e4dc4d618}</Project>
<Name>DTS.Common.Utilities</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Common\DTS.Common\DTS.Common.csproj">
<Project>{114edc77-f3b5-4576-a91b-40818d503b55}</Project>
<Name>DTS.Common</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\DataPRO\SensorDB\SensorDB.csproj">
<Project>{444ef10c-046e-47ad-a9a5-17318d488723}</Project>
<Name>SensorDB</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\StringResources.ja.resx" />
<EmbeddedResource Include="Resources\StringResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>StringResources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,297 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace DTS.Viewer.TestModification.Resources {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class StringResources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal StringResources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DTS.Viewer.TestModification.Resources.StringResources", typeof(StringResources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Cal date.
/// </summary>
internal static string CalDate {
get {
return ResourceManager.GetString("CalDate", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Data Flag:.
/// </summary>
internal static string DataFlag {
get {
return ResourceManager.GetString("DataFlag", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Description:.
/// </summary>
internal static string Description {
get {
return ResourceManager.GetString("Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to EU Multiplier:.
/// </summary>
internal static string EUMultiplier {
get {
return ResourceManager.GetString("EUMultiplier", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to EU Offset:.
/// </summary>
internal static string EUOffset {
get {
return ResourceManager.GetString("EUOffset", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Failed to modify sensitivity: .
/// </summary>
internal static string FailedToModifySensitivity {
get {
return ResourceManager.GetString("FailedToModifySensitivity", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Filter:.
/// </summary>
internal static string Filter {
get {
return ResourceManager.GetString("Filter", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Line Fit:.
/// </summary>
internal static string LineFit {
get {
return ResourceManager.GetString("LineFit", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Modify date.
/// </summary>
internal static string ModifyDate {
get {
return ResourceManager.GetString("ModifyDate", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Non-linear.
/// </summary>
internal static string NonLinear {
get {
return ResourceManager.GetString("NonLinear", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to To enable, please lock a single channel.
/// </summary>
internal static string PleaseLockHeader {
get {
return ResourceManager.GetString("PleaseLockHeader", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Preview.
/// </summary>
internal static string Preview {
get {
return ResourceManager.GetString("Preview", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Proportional to excitation.
/// </summary>
internal static string ProportionalToExcitation {
get {
return ResourceManager.GetString("ProportionalToExcitation", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Sensitivity:.
/// </summary>
internal static string Sensitivity {
get {
return ResourceManager.GetString("Sensitivity", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Sensor calibration (most recent in db).
/// </summary>
internal static string SensorCalibration {
get {
return ResourceManager.GetString("SensorCalibration", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Shift T₀ (ms):.
/// </summary>
internal static string ShiftT0ms {
get {
return ResourceManager.GetString("ShiftT0ms", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Modification can not be made, T0 must be in the dataset..
/// </summary>
internal static string T0MustBeInDataset {
get {
return ResourceManager.GetString("T0MustBeInDataset", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to T₁ (ms):.
/// </summary>
internal static string T1ms {
get {
return ResourceManager.GetString("T1ms", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to T₂ (ms):.
/// </summary>
internal static string T2ms {
get {
return ResourceManager.GetString("T2ms", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cancel.
/// </summary>
internal static string Undo {
get {
return ResourceManager.GetString("Undo", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Restore All.
/// </summary>
internal static string UndoAll {
get {
return ResourceManager.GetString("UndoAll", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to This will revert your saved test modifications to the backup on file. Continue?.
/// </summary>
internal static string UndoAllPrompt {
get {
return ResourceManager.GetString("UndoAllPrompt", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to This will undo any change(s) to this channel made before saving. Continue?.
/// </summary>
internal static string UndoPrompt {
get {
return ResourceManager.GetString("UndoPrompt", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Update database.
/// </summary>
internal static string UpdateDatabase {
get {
return ResourceManager.GetString("UpdateDatabase", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Write.
/// </summary>
internal static string WriteFiles {
get {
return ResourceManager.GetString("WriteFiles", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Are you sure you want to write these changes to disk?.
/// </summary>
internal static string WriteFilesPrompt {
get {
return ResourceManager.GetString("WriteFilesPrompt", resourceCulture);
}
}
}
}

View File

@@ -0,0 +1,140 @@
using System;
using System.ComponentModel.Composition;
using System.Windows.Media.Imaging;
using DTS.Common;
using DTS.Common.Interface;
using DTS.Viewer.TestModification;
using Prism.Ioc;
using Prism.Modularity;
using Unity;
// ReSharper disable RedundantAttributeUsageProperty
// ReSharper disable UnusedParameter.Local
[assembly: TestModificationModuleName()]
[assembly: TestModificationModuleImageAttribute()]
namespace DTS.Viewer.TestModification
{
[Export(typeof(IModule))]
[Module(ModuleName = "TestModification")]
public class TestModificationModule : IModule
{
/// <summary>
/// Injected unity container
/// </summary>
private readonly IUnityContainer _unityContainer;
/// <summary>
/// Initializes a new instance of the <see cref="TestModificationModule"/> class.
/// </summary>
/// <param name="unityContainer">Obtained reference of the unity container by using dependency injection.</param>
public TestModificationModule(IUnityContainer unityContainer)
{
_unityContainer = unityContainer;
}
public void Initialize()
{
// Register View & View-Model with Unity dependency injection container as a singleton.
_unityContainer.RegisterType<ITestModificationView, TestModificationView>();
_unityContainer.RegisterType<ITestModificationViewModel, TestModificationViewModel>();
}
public void OnInitialized(IContainerProvider containerProvider)
{
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
Initialize();
}
}
/// <summary>
/// Attribute class contains assembly name
/// </summary>
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
public class TestModificationModuleNameAttribute : TextAttribute
{
public TestModificationModuleNameAttribute() : this(null) { }
public TestModificationModuleNameAttribute(string s)
{
AssemblyName = AssemblyNames.TestModification.ToString();
}
public override string AssemblyName { get; }
public override Type GetAttributeType()
{
return typeof(TextAttribute);
}
public override string GetAssemblyName()
{
return AssemblyName;
}
}
/// <summary>
/// Attribute class contains assembly image and name - used on the Main screen to display available components
/// </summary>
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
public class TestModificationModuleImageAttribute : ImageAttribute
{
private BitmapImage _img;
public TestModificationModuleImageAttribute() : this(null) { }
public override BitmapImage AssemblyImage
{
get { _img = AssemblyInfo.GetImage(AssemblyNames.TestModification.ToString()); return _img; }
}
public TestModificationModuleImageAttribute(string s)
{
_img = AssemblyInfo.GetImage(AssemblyNames.TestModification.ToString());
}
public override Type GetAttributeType()
{
return typeof(ImageAttribute);
}
public override BitmapImage GetAssemblyImage()
{
return AssemblyImage;
}
private string _name;
public override string AssemblyName
{
get { _name = AssemblyNames.TestModification.ToString(); return _name; }
}
public override string GetAssemblyName()
{
return AssemblyName;
}
private string _group;
public override string AssemblyGroup
{
get { _group = eAssemblyGroups.Viewer.ToString(); return _group; }
}
public override string GetAssemblyGroup()
{
return AssemblyGroup;
}
private eAssemblyRegion _region;
public override eAssemblyRegion AssemblyRegion
{
get { _region = eAssemblyRegion.TestModificationRegion; return _region; }
}
public override eAssemblyRegion GetAssemblyRegion()
{
return AssemblyRegion;
}
}
}

View File

@@ -0,0 +1,583 @@
using DTS.Common;
using DTS.Common.Classes.Sensors;
using DTS.Common.Enums.Sensors;
using DTS.Common.Interface;
using DTS.Common.Interface.Sensors;
using DTS.Common.Interface.Sensors.SoftwareFilters;
using DTS.SensorDB;
using Prism.Commands;
using System;
using System.ComponentModel;
// ReSharper disable InconsistentNaming
// ReSharper disable RedundantDefaultMemberInitializer
// ReSharper disable CheckNamespace
// ReSharper disable UnassignedGetOnlyAutoProperty
namespace DTS.Viewer.ChartOptions.Model
{
public class TestModificationModel : ITestModificationModel
{
#region Properties
/// <summary>
/// the calibration date for cal on channel (or empty if not applicable)
/// </summary>
public string CalDate
{
get
{
if (null == Cal) { return string.Empty; }
return Cal.CalibrationDate.ToShortDateString();
}
}
/// <summary>
/// the modify date for cal on channel (or empty if not applicable)
/// </summary>
public string ModifyDate
{
get
{
if (null == Cal) { return string.Empty; }
return $"{Cal.ModifyDate.ToShortDateString()} {Cal.ModifyDate.ToShortTimeString()}";
}
}
/// <summary>
/// sensitivity in calibration record for latest cal for sensor on channel
/// </summary>
public double CalSensitivity
{
get
{
if (null == Cal) { return double.NaN; }
if (null == Cal.Records || null == Cal.Records.Records || 0 == Cal.Records.Records.Length)
{
return double.NaN;
}
return Cal.Records.Records[0].Sensitivity;
}
set
{
if (null == Cal) { return; }
if (null == Cal.Records || null == Cal.Records.Records || 0 == Cal.Records.Records.Length)
{
return;
}
Cal.Records.Records[0].Sensitivity = value;
}
}
private void RebindCalProperties()
{
OnPropertyChanged("CalSensitivity");
OnPropertyChanged("NonLinear");
OnPropertyChanged("ProportionalToExcitation");
OnPropertyChanged("ShowSensorCal");
OnPropertyChanged("CalDate");
OnPropertyChanged("ModifyDate");
}
/// <summary>
/// whether latest calibration for channel is proportional to excitation or not
/// </summary>
public bool ProportionalToExcitation
{
get
{
if (null == Cal) { return false; }
return Cal.IsProportional;
}
}
/// <summary>
/// whether you should show sensor cal or not
/// </summary>
public bool ShowSensorCal
{
get
{
return null != Cal && null != Sensor && !NonLinear;
}
}
/// <summary>
/// whether calibration is non linear or not
/// </summary>
public bool NonLinear
{
get
{
if (null == Cal) { return false; }
return Cal.NonLinear;
}
}
private ISensorCalDbRecord _cal;
/// <summary>
/// the latest calibration for channel
/// </summary>
public ISensorCalDbRecord Cal
{
get => _cal;
set
{
_cal = value;
RebindCalProperties();
}
}
private ISensorDbRecord _sensor;
/// <summary>
/// sensor corresponding to channel
/// </summary>
public ISensorDbRecord Sensor
{
get => _sensor;
set
{
_sensor = value;
RebindCalProperties();
}
}
private ITestChannel _selectedChannel;
public ITestChannel SelectedChannel
{
get => _selectedChannel;
set
{
if (value != _selectedChannel)
{
_selectedChannel = value;
OnPropertyChanged("SelectedChannel");
}
}
}
public bool IsModifiedDescription => !Description.Equals(SelectedChannel?.ChannelDescriptionString);
private string _description = "";
public string Description
{
get => _description;
set
{
if (value != _description)
{
_description = value;
OnPropertyChanged("Description");
OnPropertyChanged("IsModifiedDescription");
}
}
}
public bool IsModifiedEuMultiplier => !EuMultiplier.Equals(SelectedChannel?.Multiplier);
private double _euMultiplier = 0D;
public double EuMultiplier
{
get => _euMultiplier;
set
{
if (value != _euMultiplier)
{
_euMultiplier = value;
OnPropertyChanged("EuMultiplier");
OnPropertyChanged("IsModifiedEuMultiplier");
}
}
}
public bool IsModifiedEuOffset => !EuOffset.Equals(SelectedChannel?.UserOffsetEu);
private double _euOffset = 0D;
public double EuOffset
{
get => _euOffset;
set
{
if (value != _euOffset)
{
_euOffset = value;
OnPropertyChanged("EuOffset");
OnPropertyChanged("IsModifiedEuOffset");
}
}
}
public bool IsModifiedT0 => !T0.Equals(0D);
private double _t0 = 0D;
public double T0
{
get => _t0;
set
{
if (value != _t0)
{
_t0 = value;
OnPropertyChanged("T0");
OnPropertyChanged("IsModifiedT0");
}
}
}
private bool _isModified;
/// <summary>
/// indicates whether any values have changed from default for the selected channel
/// </summary>
public bool IsModified
{
get => _isModified;
private set
{
if (value != _isModified)
{
_isModified = value;
OnPropertyChanged("IsModified");
}
}
}
private bool _isModifiedLineFit;
public bool IsModifiedLineFit
{
get
{
_isModifiedLineFit = !T1.Equals(0D) || !T2.Equals(0D);
return _isModifiedLineFit;
}
set
{
if (value != _isModifiedLineFit)
{
_isModifiedLineFit = value;
OnPropertyChanged("IsModifiedLineFit");
}
}
}
/// <summary>
/// Line fit start point (ms)
/// </summary>
private double _t1 = 0D;
public double T1
{
get => _t1;
set
{
if (!_t1.Equals(value))
{
_t1 = value;
OnPropertyChanged("T1");
OnPropertyChanged("IsModifiedLineFit");
}
}
}
/// <summary>
/// Line fit end point (ms)
/// </summary>
private double _t2 = 0D;
public double T2
{
get => _t2;
set
{
if (!_t2.Equals(value))
{
_t2 = value;
OnPropertyChanged("T2");
OnPropertyChanged("IsModifiedLineFit");
}
}
}
public bool IsModifiedSensitivity => !Sensitivity.Equals(SelectedChannel?.Sensitivity);
private double _sensitivity = 0D;
public double Sensitivity
{
get => _sensitivity;
set
{
if (!_sensitivity.Equals(value))
{
_sensitivity = value;
OnPropertyChanged("Sensitivity");
OnPropertyChanged("IsModifiedSensitivity");
}
}
}
//FB 13120 updated the IsModifiedFilterto use filter class
public bool IsModifiedFilter
{
get
{
var sf = FilterClass.GetFilterClassFromString(SelectedChannel?.SoftwareFilter);
return !SelectedFilter.Equals(sf);
}
}
//FB 13120 selected filter
private IFilterClass _selectedFilter = new FilterClass(FilterClassType.Unfiltered);
public IFilterClass SelectedFilter
{
get => _selectedFilter;
set
{
if (!_selectedFilter.Equals(value))
{
_selectedFilter = value;
OnPropertyChanged("SelectedFilter");
OnPropertyChanged("IsModifiedFilter");
}
}
}
/// <summary>
/// T0 Mode indicates whether T0 changes will be applied to DAS or test
/// </summary>
private T0Mode _t0Mode = T0Mode.Test;
public T0Mode T0Mode
{
get => _t0Mode;
set
{
var newValue = !IsT0ModeTestOnly ? value : T0Mode.Test;
if (newValue != _t0Mode)
{
_t0Mode = newValue;
OnPropertyChanged("T0Mode");
}
}
}
private bool _isT0ModeTestOnly = false;
public bool IsT0ModeTestOnly
{
get => _isT0ModeTestOnly;
internal set
{
if (value != _isT0ModeTestOnly)
{
_isT0ModeTestOnly = value;
OnPropertyChanged("IsT0ModeTestOnly");
}
}
}
private bool _enableSensitivityControl = true;
public bool EnableSensitivityControl
{
get => _enableSensitivityControl;
internal set
{
if (value != _enableSensitivityControl)
{
_enableSensitivityControl = value;
OnPropertyChanged("EnableSensitivityControl");
}
}
}
private bool _enableLineFitControl = true;
public bool EnableLineFitControl
{
get => _enableLineFitControl;
internal set
{
if (value != _enableLineFitControl)
{
_enableLineFitControl = value;
OnPropertyChanged("EnableLineFitControl");
}
}
}
private bool _enableEUOffsetControl = true;
public bool EnableEUOffsetControl
{
get => _enableEUOffsetControl;
internal set
{
if (value != _enableEUOffsetControl)
{
_enableEUOffsetControl = value;
OnPropertyChanged("EnableEUOffsetControl");
}
}
}
private bool _enableEUMultiplierControl = true;
public bool EnableEUMultiplierControl
{
get => _enableEUMultiplierControl;
internal set
{
if (value != _enableEUMultiplierControl)
{
_enableEUMultiplierControl = value;
OnPropertyChanged("EnableEUMultiplierControl");
}
}
}
private bool _enableFilterControl = true;
public bool EnableFilterControl
{
get => _enableFilterControl;
internal set
{
if (value != _enableFilterControl)
{
_enableFilterControl = value;
OnPropertyChanged("EnableFilterControl");
}
}
}
private bool _enableDescriptionControl = true;
public bool EnableDescriptionControl
{
get => _enableDescriptionControl;
internal set
{
if (value != _enableDescriptionControl)
{
_enableDescriptionControl = value;
OnPropertyChanged("EnableDescriptionControl");
}
}
}
private bool _IsDataFlagEnabled = true;
public bool IsDataFlagEnabled
{
get => _IsDataFlagEnabled;
internal set
{
if (value != _IsDataFlagEnabled)
{
_IsDataFlagEnabled = value;
OnPropertyChanged("IsDataFlagEnabled");
}
}
}
private bool _IsT0Enabled = true;
public bool IsT0Enabled
{
get => _IsT0Enabled;
internal set
{
if (value != _IsT0Enabled)
{
_IsT0Enabled = value;
OnPropertyChanged("IsT0Enabled");
}
}
}
public bool IsModifiedDataFlag => null != SelectedChannel && !SelectedDataFlag.Equals((DataFlag)SelectedChannel.DataFlag);
private DataFlag _dataFlag = DataFlag.None;
public DataFlag SelectedDataFlag
{
get => _dataFlag;
set
{
if (value != _dataFlag)
{
_dataFlag = value;
OnPropertyChanged("SelectedDataFlag");
OnPropertyChanged("IsModifiedDataFlag");
}
}
}
private ITestModificationViewModel _parent;
public ITestModificationViewModel Parent
{
get => _parent;
set
{
if (value != _parent)
{
_parent = value;
OnPropertyChanged("Parent");
}
}
}
///<summary>
///Occurs when a property value changes.
///</summary>
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
if (propertyName != "IsModified" && propertyName != "LineFitModified" && propertyName != "SelectedDataFlag")
{
IsModified = (IsModifiedDescription || IsModifiedEuMultiplier
|| IsModifiedEuOffset || IsModifiedFilter
|| IsModifiedLineFit || IsModifiedSensitivity || IsModifiedT0)
&& SelectedChannel != null; //can't modify nothing
}
if (propertyName == "IsT0ModeTestOnly")
{
T0Mode = _t0Mode; //if we've changed the global setting, run a check of the current mode
}
Parent?.PublishChanges();
}
public bool IsSaved { get; }
#endregion Properties
private DelegateCommand _UpdateDatabaseCommand;
public DelegateCommand UpdateDatabaseCommand => _UpdateDatabaseCommand ?? (_UpdateDatabaseCommand = new DelegateCommand(UpdateDatabaseMethod));
private void UpdateDatabaseMethod()
{
Parent.UpdateDatabaseMethod();
}
#region Functions
/// <summary>
/// returns true if T0 is valid
/// right now this just means T0 falls between the start and end of the current channel's dataset
/// </summary>
/// <returns></returns>
public bool ValidateT0()
{
var valid = true;
if (null == SelectedChannel) { return true; }
if (null == SelectedChannel.ParentModule) { return true; }
var startRecordSampleNumber = Convert.ToDecimal(SelectedChannel.ParentModule.StartRecordSampleNumber);
var triggerSampleNumber = Convert.ToDecimal(SelectedChannel.ParentModule.TriggerSampleNumbers[0]);
var totalSamples = Convert.ToDecimal(SelectedChannel.ParentModule.NumberOfSamples);
var sampleRate = Convert.ToDecimal(SelectedChannel.ParentModule.SampleRateHz);
//note that T0 is always in ms, so we just deal in ms here (sampleRate is hz[sps])
var startTime = -1000 * (triggerSampleNumber - startRecordSampleNumber) / sampleRate;
var endTime = 1000 * (totalSamples - triggerSampleNumber + startRecordSampleNumber) / sampleRate;
var t0 = Convert.ToDecimal(T0);
if (t0 < startTime || t0 > endTime) { return false; }
return valid;
}
#endregion Functions
}
}

View File

@@ -0,0 +1,461 @@
using System;
using System.ComponentModel;
using DTS.Common;
using DTS.Common.Classes.Sensors;
using DTS.Common.Enums.Sensors;
using DTS.Common.Interface;
using DTS.Common.Interface.Sensors.SoftwareFilters;
using DTS.SensorDB;
// ReSharper disable InconsistentNaming
// ReSharper disable RedundantDefaultMemberInitializer
// ReSharper disable CheckNamespace
// ReSharper disable UnassignedGetOnlyAutoProperty
namespace DTS.Viewer.ChartOptions.Model
{
public class TestModificationModel : ITestModificationModel
{
#region Properties
private ITestChannel _selectedChannel;
public ITestChannel SelectedChannel
{
get => _selectedChannel;
set
{
if (value != _selectedChannel)
{
_selectedChannel = value;
OnPropertyChanged("SelectedChannel");
}
}
}
public bool IsModifiedDescription => !Description.Equals(SelectedChannel?.ChannelDescriptionString);
private string _description = "";
public string Description
{
get => _description;
set
{
if (value != _description)
{
_description = value;
OnPropertyChanged("Description");
OnPropertyChanged("IsModifiedDescription");
}
}
}
public bool IsModifiedEuMultiplier => !EuMultiplier.Equals(SelectedChannel?.Multiplier);
private double _euMultiplier = 0D;
public double EuMultiplier
{
get => _euMultiplier;
set
{
if (value != _euMultiplier)
{
_euMultiplier = value;
OnPropertyChanged("EuMultiplier");
OnPropertyChanged("IsModifiedEuMultiplier");
}
}
}
public bool IsModifiedEuOffset => !EuOffset.Equals(SelectedChannel?.UserOffsetEu);
private double _euOffset = 0D;
public double EuOffset
{
get => _euOffset;
set
{
if (value != _euOffset)
{
_euOffset = value;
OnPropertyChanged("EuOffset");
OnPropertyChanged("IsModifiedEuOffset");
}
}
}
public bool IsModifiedT0 => !T0.Equals(0D);
private double _t0 = 0D;
public double T0
{
get => _t0;
set
{
if (value != _t0)
{
_t0 = value;
OnPropertyChanged("T0");
OnPropertyChanged("IsModifiedT0");
}
}
}
private bool _isModified;
/// <summary>
/// indicates whether any values have changed from default for the selected channel
/// </summary>
public bool IsModified
{
get => _isModified;
private set
{
if (value != _isModified)
{
_isModified = value;
OnPropertyChanged("IsModified");
}
}
}
private bool _isModifiedLineFit;
public bool IsModifiedLineFit
{
get
{
_isModifiedLineFit = !T1.Equals(0D) || !T2.Equals(0D);
return _isModifiedLineFit;
}
set
{
if (value != _isModifiedLineFit)
{
_isModifiedLineFit = value;
OnPropertyChanged("IsModifiedLineFit");
}
}
}
/// <summary>
/// Line fit start point (ms)
/// </summary>
private double _t1 = 0D;
public double T1
{
get => _t1;
set
{
if (!_t1.Equals(value))
{
_t1 = value;
OnPropertyChanged("T1");
OnPropertyChanged("IsModifiedLineFit");
}
}
}
/// <summary>
/// Line fit end point (ms)
/// </summary>
private double _t2 = 0D;
public double T2
{
get => _t2;
set
{
if (!_t2.Equals(value))
{
_t2 = value;
OnPropertyChanged("T2");
OnPropertyChanged("IsModifiedLineFit");
}
}
}
public bool IsModifiedSensitivity => !Sensitivity.Equals(SelectedChannel?.Sensitivity);
private double _sensitivity = 0D;
public double Sensitivity
{
get => _sensitivity;
set
{
if (!_sensitivity.Equals(value))
{
_sensitivity = value;
OnPropertyChanged("Sensitivity");
OnPropertyChanged("IsModifiedSensitivity");
}
}
}
//FB 13120 updated the IsModifiedFilterto use filter class
public bool IsModifiedFilter
{
get
{
var sf = FilterClass.GetFilterClassFromString(SelectedChannel?.SoftwareFilter);
return !SelectedFilter.Equals(sf);
}
}
//FB 13120 selected filter
private IFilterClass _selectedFilter = new FilterClass(FilterClassType.Unfiltered);
public IFilterClass SelectedFilter
{
get => _selectedFilter;
set
{
if (!_selectedFilter.Equals(value))
{
_selectedFilter = value;
OnPropertyChanged("SelectedFilter");
OnPropertyChanged("IsModifiedFilter");
}
}
}
/// <summary>
/// T0 Mode indicates whether T0 changes will be applied to DAS or test
/// </summary>
private T0Mode _t0Mode = T0Mode.Test;
public T0Mode T0Mode
{
get => _t0Mode;
set
{
var newValue = !IsT0ModeTestOnly ? value : T0Mode.Test;
if (newValue != _t0Mode)
{
_t0Mode = newValue;
OnPropertyChanged("T0Mode");
}
}
}
private bool _isT0ModeTestOnly = false;
public bool IsT0ModeTestOnly
{
get => _isT0ModeTestOnly;
internal set
{
if (value != _isT0ModeTestOnly)
{
_isT0ModeTestOnly = value;
OnPropertyChanged("IsT0ModeTestOnly");
}
}
}
private bool _enableSensitivityControl = true;
public bool EnableSensitivityControl
{
get => _enableSensitivityControl;
internal set
{
if (value != _enableSensitivityControl)
{
_enableSensitivityControl = value;
OnPropertyChanged("EnableSensitivityControl");
}
}
}
private bool _enableLineFitControl = true;
public bool EnableLineFitControl
{
get => _enableLineFitControl;
internal set
{
if (value != _enableLineFitControl)
{
_enableLineFitControl = value;
OnPropertyChanged("EnableLineFitControl");
}
}
}
private bool _enableEUOffsetControl = true;
public bool EnableEUOffsetControl
{
get => _enableEUOffsetControl;
internal set
{
if (value != _enableEUOffsetControl)
{
_enableEUOffsetControl = value;
OnPropertyChanged("EnableEUOffsetControl");
}
}
}
private bool _enableEUMultiplierControl = true;
public bool EnableEUMultiplierControl
{
get => _enableEUMultiplierControl;
internal set
{
if (value != _enableEUMultiplierControl)
{
_enableEUMultiplierControl = value;
OnPropertyChanged("EnableEUMultiplierControl");
}
}
}
private bool _enableFilterControl = true;
public bool EnableFilterControl
{
get => _enableFilterControl;
internal set
{
if (value != _enableFilterControl)
{
_enableFilterControl = value;
OnPropertyChanged("EnableFilterControl");
}
}
}
private bool _enableDescriptionControl = true;
public bool EnableDescriptionControl
{
get => _enableDescriptionControl;
internal set
{
if (value != _enableDescriptionControl)
{
_enableDescriptionControl = value;
OnPropertyChanged("EnableDescriptionControl");
}
}
}
private bool _IsDataFlagEnabled = true;
public bool IsDataFlagEnabled
{
get => _IsDataFlagEnabled;
internal set
{
if (value != _IsDataFlagEnabled)
{
_IsDataFlagEnabled = value;
OnPropertyChanged("IsDataFlagEnabled");
}
}
}
private bool _IsT0Enabled = true;
public bool IsT0Enabled
{
get => _IsT0Enabled;
internal set
{
if (value != _IsT0Enabled)
{
_IsT0Enabled = value;
OnPropertyChanged("IsT0Enabled");
}
}
}
public bool IsModifiedDataFlag => null != SelectedChannel && !SelectedDataFlag.Equals((DataFlag)SelectedChannel.DataFlag);
private DataFlag _dataFlag = DataFlag.None;
public DataFlag SelectedDataFlag
{
get => _dataFlag;
set
{
if (value != _dataFlag)
{
_dataFlag = value;
OnPropertyChanged("SelectedDataFlag");
OnPropertyChanged("IsModifiedDataFlag");
}
}
}
private ITestModificationViewModel _parent;
public ITestModificationViewModel Parent
{
get => _parent;
set
{
if (value != _parent)
{
_parent = value;
OnPropertyChanged("Parent");
}
}
}
///<summary>
///Occurs when a property value changes.
///</summary>
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
if (propertyName != "IsModified" && propertyName != "LineFitModified" && propertyName != "SelectedDataFlag")
{
IsModified = (IsModifiedDescription || IsModifiedEuMultiplier
|| IsModifiedEuOffset || IsModifiedFilter
|| IsModifiedLineFit || IsModifiedSensitivity || IsModifiedT0)
&& SelectedChannel != null; //can't modify nothing
}
if (propertyName == "IsT0ModeTestOnly")
{
T0Mode = _t0Mode; //if we've changed the global setting, run a check of the current mode
}
Parent?.PublishChanges();
}
public bool IsSaved { get; }
#endregion Properties
#region Functions
/// <summary>
/// returns true if T0 is valid
/// right now this just means T0 falls between the start and end of the current channel's dataset
/// </summary>
/// <returns></returns>
public bool ValidateT0()
{
var valid = true;
if (null == SelectedChannel) { return true; }
if (null == SelectedChannel.ParentModule) { return true; }
var startRecordSampleNumber = Convert.ToDecimal(SelectedChannel.ParentModule.StartRecordSampleNumber);
var triggerSampleNumber = Convert.ToDecimal(SelectedChannel.ParentModule.TriggerSampleNumbers[0]);
var totalSamples = Convert.ToDecimal(SelectedChannel.ParentModule.NumberOfSamples);
var sampleRate = Convert.ToDecimal(SelectedChannel.ParentModule.SampleRateHz);
//note that T0 is always in ms, so we just deal in ms here (sampleRate is hz[sps])
var startTime = -1000 * (triggerSampleNumber - startRecordSampleNumber) / sampleRate;
var endTime = 1000 * (totalSamples - triggerSampleNumber + startRecordSampleNumber) / sampleRate;
var t0 = Convert.ToDecimal(T0);
if (t0 < startTime || t0 > endTime) { return false; }
return valid;
}
#endregion Functions
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Windows.Markup;
using DTS.Viewer.TestModification.Resources;
namespace DTS.Viewer.TestModification
{
[MarkupExtensionReturnType(typeof(string))]
public class TranslateExtension : MarkupExtension
{
private readonly string _key;
public TranslateExtension(string key) { _key = key; }
private const string NotFound = "#stringnotfound#";
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (string.IsNullOrEmpty(_key)) { return NotFound; }
return StringResources.ResourceManager.GetString(_key) ?? NotFound + " " + _key;
}
}
}

View File

@@ -0,0 +1,97 @@
<base:BaseView x:Class="DTS.Viewer.TestModification.TestModificationView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:base="clr-namespace:DTS.Common.Base;assembly=DTS.Common"
xmlns:toolkit="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:converters="clr-namespace:DTS.Common.Converters;assembly=DTS.Common"
xmlns:strings="clr-namespace:DTS.Viewer.TestModification" x:Name="testViewModification">
<base:BaseView.Resources>
<ResourceDictionary>
<!-- ReSharper disable once Xaml.RedundantResource -->
<converters:BooleanToOpacityConverter x:Key="BooleanToOpacityConverter" />
<!-- ReSharper disable once Xaml.RedundantResource -->
<converters:EnumBooleanConverter x:Key="EnumBooleanConverter" />
<converters:InverseBooleanToOpacityConverter x:Key="InverseBooleanToOpacityConverter" />
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<converters:InverseBooleanConverter x:Key="InverseBooleanConverter"/>
<converters:BooleanToColorConverter x:Key="InvertedBooleanToAttentionBackgroundColorConverter" Background="True" Inverted="True" AttentionBrush="True" />
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Resources/TestModificationResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</base:BaseView.Resources>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Top">
<Label Content="{strings:TranslateExtension PleaseLockHeader}" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center"
Visibility="{Binding Path=TestModificationVisability, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter=false}" />
<Grid Visibility="{Binding Path=TestModificationVisability, Converter={StaticResource BooleanToVisibilityConverter}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- DESCRIPTION -->
<Label Grid.Row="0" Grid.Column="0" Content="{strings:TranslateExtension Description}" AutomationProperties.AutomationId="LabelDescription" />
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding Path=Model.Description}" x:Name="tbDescription" AutomationProperties.AutomationId="TextBoxDescription" IsEnabled="{Binding Path=Model.EnableDescriptionControl}" Background="{Binding Path=Model.IsModifiedDescription, Converter={StaticResource InvertedBooleanToAttentionBackgroundColorConverter}}"/>
<!-- FILTER -->
<Label Grid.Row="1" Grid.Column="0" Content="{strings:TranslateExtension Filter}" AutomationProperties.AutomationId="LabelFilter" Background="{Binding Path=Model.IsModifiedFilter, Converter={StaticResource InvertedBooleanToAttentionBackgroundColorConverter}}" />
<ComboBox Grid.Row="1" Grid.Column="1" DisplayMemberPath="FilterName" ItemsSource="{Binding ElementName=testViewModification, Path=AvailableCFC}" SelectedItem="{Binding Path=Model.SelectedFilter}" AutomationProperties.AutomationId="ComboBoxFilter" IsEnabled="{Binding Path=Model.EnableFilterControl}" MinWidth="200" Grid.ColumnSpan="2"/>
<!-- DATAFLAG -->
<Label Grid.Row="2" Grid.Column="0" Content="{strings:TranslateExtension DataFlag}" AutomationProperties.AutomationId="LabelDataFlag" Background="{Binding Path=Model.IsModifiedDataFlag, Converter={StaticResource InvertedBooleanToAttentionBackgroundColorConverter}}"/>
<ComboBox Grid.Row="2" Grid.Column="1" ItemsSource="{Binding Source={StaticResource DataFlagProvider}}" SelectedItem="{Binding Path=Model.SelectedDataFlag}" AutomationProperties.AutomationId="ComboBoxDataFlag" IsEnabled="{Binding Path=Model.IsDataFlagEnabled}"/>
<!-- T0 -->
<Label Grid.Row="3" Grid.Column="0" Content="{strings:TranslateExtension ShiftT0ms}" AutomationProperties.AutomationId="TextBlockT0" />
<toolkit:DoubleUpDown Grid.Row="3" Grid.Column="1" FormatString="N3" Value="{Binding Path=Model.T0}" Background="{Binding Path=Model.IsModifiedT0, Converter={StaticResource InvertedBooleanToAttentionBackgroundColorConverter}}" AutomationProperties.AutomationId="DoubleUpDownT0" IsEnabled="{Binding Path=Model.IsT0Enabled}"/>
<ComboBox Grid.Row="3" Grid.Column="2" ItemsSource="{Binding Source={StaticResource T0ModeProvider}}" SelectedItem="{Binding Path=Model.T0Mode}" Width="80" IsEnabled="{Binding Path=Model.IsT0ModeTestOnly, Converter={StaticResource InverseBooleanConverter}}" AutomationProperties.AutomationId="ComboBoxT0Mode" />
<!-- EU MULTIPLIER -->
<Label Grid.Row="4" Grid.Column="0" Content="{strings:TranslateExtension EUMultiplier}" AutomationProperties.AutomationId="LabelEuMultiplier" />
<toolkit:DoubleUpDown Grid.Row="4" Grid.Column="1" FormatString="N8" Width="{Binding ActualWidth, ElementName=tbDescription}" Value="{Binding Path=Model.EuMultiplier}" Background="{Binding Path=Model.IsModifiedEuMultiplier, Converter={StaticResource InvertedBooleanToAttentionBackgroundColorConverter}}" AutomationProperties.AutomationId="DoubleUpDownEuMultiplier" IsEnabled="{Binding Path=Model.EnableEUMultiplierControl}"/>
<!-- EU OFFSET -->
<Label Grid.Row="5" Grid.Column="0" Content="{strings:TranslateExtension EUOffset}" AutomationProperties.AutomationId="LabelEuOffset" />
<toolkit:DoubleUpDown Grid.Row="5" Grid.Column="1" FormatString="N8" HorizontalAlignment="Stretch" Value="{Binding Path=Model.EuOffset}" AutomationProperties.AutomationId="DoubleUpDownEuOffset" IsEnabled="{Binding Path=Model.EnableEUOffsetControl}" Background="{Binding Path=Model.IsModifiedEuOffset, Converter={StaticResource InvertedBooleanToAttentionBackgroundColorConverter}}" />
<!-- SENSITIVITY -->
<Label Grid.Row="6" Grid.Column="0" Content="{strings:TranslateExtension Sensitivity}" AutomationProperties.AutomationId="LabelSensitivity" />
<toolkit:DoubleUpDown Grid.Row="6" Grid.Column="1" FormatString="N12" Value="{Binding Path=Model.Sensitivity}" AutomationProperties.AutomationId="DoubleUpDownSensitivity" IsEnabled="{Binding Path=Model.EnableSensitivityControl}" Background="{Binding Path=Model.IsModifiedSensitivity, Converter={StaticResource InvertedBooleanToAttentionBackgroundColorConverter}}" />
<!-- LINEFIT -->
<Label Grid.Row="7" Grid.Column="0" Content="{strings:TranslateExtension LineFit}" AutomationProperties.AutomationId="TextBlockLineFit" />
<Label Grid.Row="8" Grid.Column="0" Content="{strings:TranslateExtension T1ms}" AutomationProperties.AutomationId="TextBlockT1" />
<toolkit:DoubleUpDown Grid.Row="8" Grid.Column="1" FormatString="N3" Value="{Binding Path=Model.T1}" AutomationProperties.AutomationId="DoubleUpDownT1" IsEnabled="{Binding Path=Model.EnableLineFitControl}" Background="{Binding Path=Model.IsModifiedLineFit, Converter={StaticResource InvertedBooleanToAttentionBackgroundColorConverter}}"/>
<Label Grid.Row="9" Grid.Column="0" Content="{strings:TranslateExtension T2ms}" AutomationProperties.AutomationId="TextBlockT2" />
<toolkit:DoubleUpDown Grid.Row="9" Grid.Column="1" FormatString="N3" Value="{Binding Path=Model.T2}" AutomationProperties.AutomationId="DoubleUpDownT2" IsEnabled="{Binding Path=Model.EnableLineFitControl}" Background="{Binding Path=Model.IsModifiedLineFit, Converter={StaticResource InvertedBooleanToAttentionBackgroundColorConverter}}"/>
<Grid Grid.Row="10" Grid.Column="0" Grid.ColumnSpan="3">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Rectangle Grid.Row="0" Grid.ColumnSpan="2" Fill="Black" Height="1" Margin="5"/>
<Button Grid.Row="1" Grid.Column="0" Content="{strings:TranslateExtension Undo}" Command="{Binding Path=UndoCommand}" IsEnabled="{Binding Path=Model.IsModified}" AutomationProperties.AutomationId="ButtonUndoWrite"/>
<Button Grid.Row="1" Grid.Column="1" Content="{strings:TranslateExtension WriteFiles}" Command="{Binding Path=WriteCommand}" IsEnabled="{Binding Path=Model.IsModified}" AutomationProperties.AutomationId="ButtonWrite" />
<Button Grid.Row="2" Grid.Column="0" Content="{strings:TranslateExtension Preview}" Command="{Binding Path=PreviewCommand}" IsEnabled="{Binding Path=Model.IsModifiedLineFit}" AutomationProperties.AutomationId="ButtonPreview" Visibility="Collapsed"/>
<Button Grid.Row="2" Grid.Column="1" Content="{strings:TranslateExtension UndoAll}" Command="{Binding Path=UndoAllCommand}" IsEnabled="{Binding Path=IsBackedUp}" AutomationProperties.AutomationId="ButtonUndoAll"/>
</Grid>
</Grid>
</Grid>
</base:BaseView>

View File

@@ -0,0 +1,407 @@
using DTS.Common.Base;
using DTS.Common.Events;
using DTS.Common.Interactivity;
using DTS.Common.Interface;
using DTS.Common.Interface.Sensors;
using DTS.Common.Settings;
using DTS.Common.Storage;
using DTS.Common.Utilities.Logging;
using DTS.SensorDB;
using DTS.Viewer.ChartOptions.Model;
using DTS.Viewer.TestModification.Model;
using Prism.Commands;
using Prism.Events;
using Prism.Regions;
using System;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using Unity;
// ReSharper disable CheckNamespace
// ReSharper disable ConvertToAutoProperty
// ReSharper disable InconsistentNaming
// ReSharper disable UnusedAutoPropertyAccessor.Local
// ReSharper disable NotAccessedField.Local
// ReSharper disable RedundantDefaultMemberInitializer
namespace DTS.Viewer.TestModification
{
public class TestModificationViewModel : BaseViewModel<ITestModificationViewModel>, ITestModificationViewModel
{
/// <summary>
/// retrieves latest calibration from database (or null if not in database or an error occurs)
/// <summary>
private static ISensorCalDbRecord GetLatestCalibration(ISensorDbRecord sensor)
{
if ( null == sensor)
{
return null;
}
try
{
var records = GetCalibrationsDb(sensor.id, sensor.SerialNumber);
if ( null == records || 0 == records.Length) { return null; }
Array.Sort(records, CompareCalibrations);
//calibrations should now be in ascending order so take the "last" one
return records[records.Length - 1];
}
catch (Exception)
{
//there's no access to APILogger here, so rather than adding a reference, just eat the error
}
return null;
}
private static ISensorCalDbRecord[] GetCalibrationsDb(int sensorId, string serialNumber)
{
_ = DbOperations.SensorCalibrationsGet(sensorId, serialNumber, out var records);
return records;
}
/// <summary>
/// compares two sensor calibration records
/// </summary>
private static int CompareCalibrations(ISensorCalDbRecord left, ISensorCalDbRecord right)
{
if (left == right) { return 0; }
if (null == left) { return 1; }
if (null == right) { return -1; }
var res = left.CalibrationDate.CompareTo(right.CalibrationDate);
if (res != 0)
{
return res;
}
return left.ModifyDate.CompareTo(right.ModifyDate);
}
/// <summary>
/// returns sensor given serial number
/// </summary>
private static ISensorDbRecord GetSensor(ISensorDbRecord[] records)
{
if ( null == records || 0 == records.Length) { return null; }
return records[0];
}
/// <summary>
/// updates calibration in db
/// http://manuscript.dts.local/f/cases/43735/SW-NFR-DataPRO-Add-option-to-map-sensitivity-edit-in-Viewer-back-to-Sensor-Database
/// </summary>
public void UpdateDatabaseMethod()
{
try
{
var cal = Model.Cal;
if (null == cal) { return; }
cal.CalibrationId = -1;
cal.ModifyDate = DateTime.Now;
var sc = new SensorCalibration(cal);
DbOperations.SensorCalibrationsInsert(sc, 0, true);
Model.Cal = GetLatestCalibration(Model.Sensor);
}
catch (Exception ex)
{
_eventAggregator.GetEvent<PageErrorEvent>().Publish(new PageErrorArg(new[] { $"{Resources.StringResources.FailedToModifySensitivity} {ex.Message}" }, null));
}
}
public ITestModificationView View { get; set; }
public IBaseViewModel Parent { get; set; }
private IEventAggregator _eventAggregator { get; set; }
private IUnityContainer _unityContainer { get; set; }
public InteractionRequest<Notification> NotificationRequest { get; private set; }
public new InteractionRequest<Confirmation> ConfirmationRequest { get; private set; }
/// <summary>
/// Creates a new instance of the TestModificationViewListModel.
/// </summary>
/// <param name="view">The TestModification View interface.</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 TestModificationViewModel(ITestModificationView view, IRegionManager regionManager, IEventAggregator eventAggregator, IUnityContainer unityContainer)
: base(regionManager, eventAggregator, unityContainer)
{
Model = new TestModificationModel();
View = view;
View.DataContext = this;
NotificationRequest = new InteractionRequest<Notification>();
ConfirmationRequest = new InteractionRequest<Confirmation>();
_eventAggregator = eventAggregator;
_unityContainer = unityContainer;
_eventAggregator.GetEvent<RaiseNotification>().Subscribe(OnRaiseNotification);
_eventAggregator.GetEvent<GraphSelectedChannelsNotification>().Subscribe(OnGraphSelectedCountChanged);
_eventAggregator.GetEvent<ShiftT0Event>().Subscribe(OnShiftT0Event);
_eventAggregator.GetEvent<SetUseZeroForUnfilteredEvent>().Subscribe(OnSetUseZeroForUnfiltered);
}
private void OnSetUseZeroForUnfiltered(bool useZeroForUnfiltered)
{
UseZeroForUnfiltered = useZeroForUnfiltered;
}
private void OnShiftT0Event(ShiftT0EventArguments args)
{
if (args.IsInitialization) { return; }
if (args.IsKeyPress)
{
if (null == Model) { return; }
//if we can't modify no reason to continue, short circuit out
if (!TestModificationVisability) { return; }
if (null == Model.SelectedChannel || null == Model.SelectedChannel.ParentModule) { return; }
else
{
//go and modify the T0!
//calculate a new T0 given the sample rate
var t0 = Model.T0 + args.T0Steps * (1000D / Model.SelectedChannel.ParentModule.SampleRateHz);
Model.T0 = t0;
}
_eventAggregator.GetEvent<TestModificationChangedEvent>().Publish(Model);
}
else { Model.T0 = args.T0Time; }
}
#region Methods
public override void Initialize()
{
}
public override void Initialize(object parameter)
{
Parent = (IBaseViewModel)parameter;
}
/// <summary>
/// Private Event handler for RaiseNotification event.
/// </summary>
private void OnRaiseNotification(NotificationContentEventArgs eventArgsWithTitle)
{
// Notification object expects a NotificationContentEventArgsWithoutTitle object and a Title string.
var eventArgsWithoutTitle = new NotificationContentEventArgs(eventArgsWithTitle.Message, eventArgsWithTitle.MessageDetails, eventArgsWithTitle.Image);
NotificationRequest.Raise(new Notification
{
Content = eventArgsWithoutTitle,
Title = eventArgsWithTitle.Title
});
}
private static bool IsPopulating { get; set; }
private void OnGraphSelectedCountChanged(GraphSelectedChannelsNotificationArg arg)
{
if (Parent != arg?.ParentVM) return;
var channels = arg.SelectedChannels;
var canModify = channels.Count == 1 && channels.Count(x => x.IsSelected) == 1;
TestModificationVisability = canModify;
if (canModify)
{
IsPopulating = true;
Model.IsT0ModeTestOnly = SettingsDB.GetGlobalValueBool(Keys.ApplyShiftT0ModsTestOnly.ToString(), false);
var viewModel = _unityContainer.TryResolve<IViewerMainViewModel>();
Model.EnableSensitivityControl = viewModel.DoesUserHaveEditPermission;
Model.EnableLineFitControl = viewModel.DoesUserHaveEditPermission;
Model.EnableDescriptionControl = viewModel.DoesUserHaveEditPermission;
Model.EnableEUMultiplierControl = viewModel.DoesUserHaveEditPermission;
Model.EnableEUOffsetControl = viewModel.DoesUserHaveEditPermission;
Model.EnableFilterControl = viewModel.DoesUserHaveEditPermission;
Model.IsT0Enabled = viewModel.DoesUserHaveEditPermission;
Model.IsDataFlagEnabled = viewModel.DoesUserHaveEditPermission;
Model.SelectedChannel = channels[0];
TestModelManipulation.PopulateFromChannel(Model);
IsPopulating = false;
IsBackedUp = TestModelManipulation.BackupExists(Model);
_eventAggregator.GetEvent<ShowT0CursorEvent>().Publish(true);
DbOperations.SensorsGet(channels[0].SerialNumber, out var records);
var sensor = GetSensor(records);
if (null != sensor && 0 == sensor.SensorType)
{
//for now we only display analog sensor calibrations ...
Model.Sensor = sensor;
Model.Cal = GetLatestCalibration(Model.Sensor);
}
}
else if (channels.Count == 0)
{
Model.SelectedChannel = null;
}
else
{
_eventAggregator.GetEvent<ShowT0CursorEvent>().Publish(false);
}
}
#endregion
#region ContextRegion
#endregion
#region Commands
private DelegateCommand _previewCommand;
public DelegateCommand PreviewCommand => _previewCommand ?? (_previewCommand = new DelegateCommand(PreviewMethod));
private void PreviewMethod()
{
TestModelManipulation.PreviewLineFit(Model);
}
private DelegateCommand _writeCommand;
public DelegateCommand WriteCommand => _writeCommand ?? (_writeCommand = new DelegateCommand(WriteMethod));
private void WriteMethod()
{
var window = Application.Current.MainWindow;
if (null == window) { return; }
//14661 Shifting T zero beyond post trigger seconds crashes application
//don't allow T0 to shift beyond test data
if (Model.IsModifiedT0 && !Model.ValidateT0())
{
_eventAggregator.GetEvent<PageErrorEvent>()
.Publish(new PageErrorArg(new[] { Resources.StringResources.T0MustBeInDataset }, null));
return;
}
var dialogResult = MessageBox.Show(window, Resources.StringResources.WriteFilesPrompt, "", MessageBoxButton.YesNo);
try
{
UseISOCodeFilterMapping = SettingsDB.GetGlobalValueBool("UseISOCodeFilterMapping", true);
}
catch (Exception ex)
{
APILogger.Log(ex);
}
APILogger.Log(Resources.StringResources.WriteFilesPrompt, $"User pressed {dialogResult.ToString()}");
if (dialogResult == MessageBoxResult.Yes)
{
TestModelManipulation.SaveModification(Model, UseISOCodeFilterMapping, UseZeroForUnfiltered);
IsBackedUp = TestModelManipulation.BackupExists(Model);
}
}
private DelegateCommand _undoCommand;
public DelegateCommand UndoCommand => _undoCommand ?? (_undoCommand = new DelegateCommand(UndoMethod));
private void UndoMethod()
{
var window = Application.Current.MainWindow;
if (null == window) { return; }
var dialogResult = MessageBox.Show(window, Resources.StringResources.UndoPrompt, "", MessageBoxButton.YesNo);
APILogger.Log(Resources.StringResources.UndoPrompt, $"User pressed {dialogResult.ToString()}");
if (dialogResult == MessageBoxResult.Yes)
{
TestModelManipulation.UndoModification(Model);
}
}
private DelegateCommand _undoAllCommand;
public DelegateCommand UndoAllCommand => _undoAllCommand ?? (_undoAllCommand = new DelegateCommand(UndoAllMethod));
private void UndoAllMethod()
{
var window = Application.Current.MainWindow;
if (null == window) { return; }
var dialogResult = MessageBox.Show(window, Resources.StringResources.UndoAllPrompt, "", MessageBoxButton.YesNo);
APILogger.Log(Resources.StringResources.UndoAllPrompt, $"User pressed {dialogResult.ToString()}");
if (dialogResult == MessageBoxResult.Yes)
{
TestModelManipulation.UndoAllModification(Model);
IsBackedUp = TestModelManipulation.BackupExists(Model);
}
}
public void PublishChanges()
{
if (!IsPopulating && Model.IsModifiedDataFlag)
{
//Write this immediately
TestModelManipulation.SaveModificationDataFlag(Model);
IsBackedUp = TestModelManipulation.BackupExists(Model);
}
_eventAggregator?.GetEvent<TestModificationChangedEvent>().Publish(Model);
}
#endregion Commands
#region Properties
/// <summary>
/// when true forces the ISOCode field filter SoftwareFilter to always match the SoftwareFilter of the channel
/// Whenever the softwarefilter for a graph channel is modified it will check this value before
/// modifying the channel isocode
/// </summary>
public bool UseISOCodeFilterMapping { get; set; } = false;
private TestModificationModel _model = null; // new TestModificationModel();
public new TestModificationModel Model
{
get => _model;
set
{
if (_model != null) _model.Parent = null;
_model = value;
if (_model != null) _model.Parent = this;
OnPropertyChanged("Model");
}
}
public bool UseZeroForUnfiltered
{
get; set;
}
private bool _isFilterEnabled = false;
public bool IsFilterEnabled { get => _isFilterEnabled; set { _isFilterEnabled = value; OnPropertyChanged("IsFilterEnabled"); } }
private bool _testModificationVisability = false;
public bool TestModificationVisability { get => _testModificationVisability; set { _testModificationVisability = value; OnPropertyChanged("TestModificationVisability"); } }
///<summary>
///Occurs when a property value changes.
///</summary>
public new event PropertyChangedEventHandler PropertyChanged;
private new void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// Gets the HeaderInfo.
/// </summary>
public string HeaderInfo => "TestSummaryRegion";
private bool _isBusy = false;
public new bool IsBusy
{
get => _isBusy;
set { _isBusy = value; OnPropertyChanged("IsBusy"); }
}
private bool _isDirty;
public new bool IsDirty
{
get => _isDirty;
set => _isDirty = value;
}
private bool _isNavigationIncluded;
public new bool IsNavigationIncluded
{
get => _isNavigationIncluded;
set => _isNavigationIncluded = value;
}
private bool _isBackedUp;
public bool IsBackedUp
{
get => _isBackedUp;
private set
{
_isBackedUp = value;
OnPropertyChanged("IsBackedUp");
}
}
#endregion
}
}

View File

@@ -0,0 +1,10 @@
using System.ComponentModel;
using DTS.Common.Converters;
namespace DTS.Viewer.TestModification
{
public enum Keys
{
ApplyShiftT0ModsTestOnly //System Setting for whether to restrict "Shift T0" test modifications to "Test" only.
}
}

View File

@@ -0,0 +1,234 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace DTS.Viewer.TestModification.Resources {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class StringResources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal StringResources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DTS.Viewer.TestModification.Resources.StringResources", typeof(StringResources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Data Flag:.
/// </summary>
internal static string DataFlag {
get {
return ResourceManager.GetString("DataFlag", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Description:.
/// </summary>
internal static string Description {
get {
return ResourceManager.GetString("Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to EU Multiplier:.
/// </summary>
internal static string EUMultiplier {
get {
return ResourceManager.GetString("EUMultiplier", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to EU Offset:.
/// </summary>
internal static string EUOffset {
get {
return ResourceManager.GetString("EUOffset", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Filter:.
/// </summary>
internal static string Filter {
get {
return ResourceManager.GetString("Filter", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Line Fit:.
/// </summary>
internal static string LineFit {
get {
return ResourceManager.GetString("LineFit", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to To enable, please lock a single channel.
/// </summary>
internal static string PleaseLockHeader {
get {
return ResourceManager.GetString("PleaseLockHeader", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Preview.
/// </summary>
internal static string Preview {
get {
return ResourceManager.GetString("Preview", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Sensitivity:.
/// </summary>
internal static string Sensitivity {
get {
return ResourceManager.GetString("Sensitivity", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Shift T₀ (ms):.
/// </summary>
internal static string ShiftT0ms {
get {
return ResourceManager.GetString("ShiftT0ms", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Modification can not be made, T0 must be in the dataset..
/// </summary>
internal static string T0MustBeInDataset {
get {
return ResourceManager.GetString("T0MustBeInDataset", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to T₁ (ms):.
/// </summary>
internal static string T1ms {
get {
return ResourceManager.GetString("T1ms", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to T₂ (ms):.
/// </summary>
internal static string T2ms {
get {
return ResourceManager.GetString("T2ms", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cancel.
/// </summary>
internal static string Undo {
get {
return ResourceManager.GetString("Undo", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Restore All.
/// </summary>
internal static string UndoAll {
get {
return ResourceManager.GetString("UndoAll", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to This will revert your saved test modifications to the backup on file. Continue?.
/// </summary>
internal static string UndoAllPrompt {
get {
return ResourceManager.GetString("UndoAllPrompt", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to This will undo any change(s) to this channel made before saving. Continue?.
/// </summary>
internal static string UndoPrompt {
get {
return ResourceManager.GetString("UndoPrompt", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Write.
/// </summary>
internal static string WriteFiles {
get {
return ResourceManager.GetString("WriteFiles", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Are you sure you want to write these changes to disk?.
/// </summary>
internal static string WriteFilesPrompt {
get {
return ResourceManager.GetString("WriteFilesPrompt", resourceCulture);
}
}
}
}

View File

@@ -0,0 +1,30 @@
using DTS.Common.Interface;
using DTS.Common.Interface.Sensors.SoftwareFilters;
using DTS.SensorDB;
using System.Collections.Generic;
// ReSharper disable CheckNamespace
namespace DTS.Viewer.TestModification
{
/// <summary>
/// Interaction logic for TestModificationView.xaml
/// </summary>
public partial class TestModificationView : ITestModificationView
{
public TestModificationView()
{
InitializeComponent();
}
/// <summary>
/// FB 13120 available filter classes
/// </summary>
public List<IFilterClass> AvailableCFC
{
get
{
return new AnalogSettingDefaults().FilterOptions;
}
}
}
}

View File

@@ -0,0 +1,184 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="DataFlag" xml:space="preserve">
<value>Data Flag:</value>
</data>
<data name="Description" xml:space="preserve">
<value>Description:</value>
</data>
<data name="EUMultiplier" xml:space="preserve">
<value>EU Multiplier:</value>
</data>
<data name="EUOffset" xml:space="preserve">
<value>EU Offset:</value>
</data>
<data name="Filter" xml:space="preserve">
<value>Filter:</value>
</data>
<data name="LineFit" xml:space="preserve">
<value>Line Fit:</value>
</data>
<data name="PleaseLockHeader" xml:space="preserve">
<value>To enable, please lock a single channel</value>
<comment>Instructions asking user to lock a channel in order to see modification settings</comment>
</data>
<data name="Preview" xml:space="preserve">
<value>Preview</value>
</data>
<data name="Sensitivity" xml:space="preserve">
<value>Sensitivity:</value>
</data>
<data name="ShiftT0ms" xml:space="preserve">
<value>Shift T₀ (ms):</value>
</data>
<data name="T0MustBeInDataset" xml:space="preserve">
<value>Modification can not be made, T0 must be in the dataset.</value>
<comment>Prompt given when the user clicks on write but T0 is modified and not in the dataset</comment>
</data>
<data name="T1ms" xml:space="preserve">
<value>T₁ (ms):</value>
</data>
<data name="T2ms" xml:space="preserve">
<value>T₂ (ms):</value>
</data>
<data name="Undo" xml:space="preserve">
<value>Cancel</value>
<comment>Button for undoing change(s) made in UI before committing to disk</comment>
</data>
<data name="UndoAll" xml:space="preserve">
<value>Restore All</value>
<comment>Button for reverting Test Viewer settings to the backup on disk</comment>
</data>
<data name="UndoAllPrompt" xml:space="preserve">
<value>This will revert your saved test modifications to the backup on file. Continue?</value>
</data>
<data name="UndoPrompt" xml:space="preserve">
<value>This will undo any change(s) to this channel made before saving. Continue?</value>
<comment>Prompt given to user before undoing UI changes</comment>
</data>
<data name="WriteFiles" xml:space="preserve">
<value>Write</value>
<comment>Button for writing Test Modifications to disk</comment>
</data>
<data name="WriteFilesPrompt" xml:space="preserve">
<value>Are you sure you want to write these changes to disk?</value>
<comment>Prompt given to user before writing changes to disk</comment>
</data>
</root>

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{5EE7C61F-E9FE-479B-BE1F-78A142341C3B}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>DTS.Viewer.TestModification</RootNamespace>
<AssemblyName>DTS.Viewer.TestModification</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SccProjectName>
</SccProjectName>
<SccLocalPath>
</SccLocalPath>
<SccAuxPath>
</SccAuxPath>
<SccProvider>
</SccProvider>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Xaml.Behaviors">
<HintPath>..\..\..\Common\DTS.Common\lib\PrismLibrary\Microsoft.Xaml.Behaviors.dll</HintPath>
</Reference>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="Prism">
<HintPath>..\..\..\Common\DTS.Common\lib\PrismLibrary\Prism.dll</HintPath>
</Reference>
<Reference Include="Prism.Unity.Wpf">
<HintPath>..\..\..\Common\DTS.Common\lib\PrismLibrary\Prism.Unity.Wpf.dll</HintPath>
</Reference>
<Reference Include="Prism.Wpf">
<HintPath>..\..\..\Common\DTS.Common\lib\PrismLibrary\Prism.Wpf.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Core" />
<Reference Include="System.Xaml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="Unity.Abstractions">
<HintPath>..\..\..\Common\DTS.Common\lib\PrismLibrary\Unity.Abstractions.dll</HintPath>
</Reference>
<Reference Include="Unity.Container">
<HintPath>..\..\..\Common\DTS.Common\lib\PrismLibrary\Unity.Container.dll</HintPath>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="Xceed.Wpf.Toolkit">
<HintPath>..\..\..\Common\DTS.Common\lib\Xceed.Wpf.Toolkit\Xceed.Wpf.Toolkit.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Model\Enums.cs" />
<Compile Include="Model\TestModelManipulation.cs" />
<Compile Include="Model\TestModificationModel.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Resources\StringResources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>StringResources.resx</DependentUpon>
</Compile>
<Compile Include="Resources\TranslateExtension.cs" />
<Compile Include="TestModificationModule.cs" />
<Compile Include="ViewModel\TestModificationViewModel.cs" />
<Compile Include="View\TestModificationView.xaml.cs">
<DependentUpon>TestModificationView.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Page Include="Resources\TestModificationResources.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="View\TestModificationView.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Common\DTS.Common.Core\DTS.Common.Core.csproj">
<Project>{fab1f470-1574-4301-b56e-d3364aa93679}</Project>
<Name>DTS.Common.Core</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Common\DTS.Common.DAS.Concepts\DTS.Common.DAS.Concepts.csproj">
<Project>{03d8c736-36eb-4cd1-a6d9-130452b23239}</Project>
<Name>DTS.Common.DAS.Concepts</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Common\DTS.Common.Serialization\DTS.Common.Serialization.csproj">
<Project>{b7d50b14-fa61-4fe4-bff7-b257902b55b0}</Project>
<Name>DTS.Common.Serialization</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Common\DTS.Common.SettingsDB\DTS.Common.Settings.csproj">
<Project>{61017104-D8EE-41D1-B9CA-DAD863FF78B2}</Project>
<Name>DTS.Common.Settings</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Common\DTS.Common.Storage\DTS.Common.Storage.csproj">
<Project>{E3BE457C-0AC7-4A9C-BC81-EAFEB3217878}</Project>
<Name>DTS.Common.Storage</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Common\DTS.Common.Utilities\DTS.Common.Utilities.csproj">
<Project>{03eace47-ea59-44ac-b49d-956e4dc4d618}</Project>
<Name>DTS.Common.Utilities</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Common\DTS.Common\DTS.Common.csproj">
<Project>{114edc77-f3b5-4576-a91b-40818d503b55}</Project>
<Name>DTS.Common</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\DataPRO\SensorDB\SensorDB.csproj">
<Project>{444ef10c-046e-47ad-a9a5-17318d488723}</Project>
<Name>SensorDB</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\StringResources.ja.resx" />
<EmbeddedResource Include="Resources\StringResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>StringResources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,31 @@
using DTS.Common.Interface;
using DTS.Common.Interface.Sensors.SoftwareFilters;
using DTS.SensorDB;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Windows.Controls;
// ReSharper disable CheckNamespace
namespace DTS.Viewer.TestModification
{
/// <summary>
/// Interaction logic for TestModificationView.xaml
/// </summary>
public partial class TestModificationView : ITestModificationView
{
public TestModificationView()
{
InitializeComponent();
}
/// <summary>
/// FB 13120 available filter classes
/// </summary>
public List<IFilterClass> AvailableCFC
{
get
{
return new AnalogSettingDefaults().FilterOptions;
}
}
}
}

View File

@@ -0,0 +1,655 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using DTS.Common;
using DTS.Common.Classes.Sensors;
using DTS.Common.Events;
using DTS.Common.Interface;
using DTS.Common.Settings;
using DTS.Common.Utilities.Logging;
using DTS.Serialization;
using Prism.Ioc;
using Prism.Events;
// ReSharper disable InconsistentNaming
namespace DTS.Viewer.TestModification.Model
{
public class TestModelManipulation
{
#region constants
/// <summary>
/// parameters in test serialization
/// </summary>
public const double UNUSED_START_TIME = 0;
public const int UNUSED_DATA_COLLECTION_LENGTH = 0;
/// <summary>
/// .chn file can back up just the header or the entire file
/// if backed up as a header the extension will be .header.bak
/// </summary>
private const string BackupHeaderExtension = ".header.bak";
/// <summary>
/// the extension for full backup files (.chn.bak, .dts.bak)
/// </summary>
private const string BackupFileExtension = ".bak";
#endregion
/// <summary>
/// revert changes since original file, this includes the .DTS file and all binary files
/// </summary>
public static void UndoAllModification(ITestModificationModel model)
{
RestoreDTSFileIfModified(model);
var f = new Serialization.SliceRaw.File { DefaultEncoding = Encoding.Unicode.CodePage };
f.Importer.Read(model.SelectedChannel.BinaryFilePath, out var target);
foreach (var m in target.Modules)
{
foreach (var c in m.Channels) { RestoreChannelIfModified(c); }
foreach (var cc in m.CalculatedChannels) { RestoreChannelIfModified(cc); }
}
var testId = GetTestIdFromBinaryFileName(model.SelectedChannel.BinaryFileName);
var dtsFileName = Path.Combine(model.SelectedChannel.BinaryFilePath, testId + ".dts");
var eventAggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
//if DeriveROI from all is true, wait till the derive function is complete otherwise you could be
//reading and writing from the same files at the same time
if (!SettingsDB.GetGlobalValueBool("DeriveROIFromAll", false))
{
eventAggregator?.GetEvent<ChannelsModificationNotification>().Publish(new List<ITestChannel> { /*model.SelectedChannel*/ });
eventAggregator?.GetEvent<RefreshTestRequestEvent>().Publish(dtsFileName);
}
eventAggregator?.GetEvent<TestModificationEvent>()
.Publish(new TestModificationArgs(model.SelectedChannel.BinaryFilePath, testId));
}
/// <summary>
/// returns whether a backup file exists for the given model
/// </summary>
public static bool BackupExists(ITestModificationModel model)
{
if (model.SelectedChannel != null)
{
var testId = GetTestIdFromBinaryFileName(model.SelectedChannel.BinaryFileName);
var dtsFileName = Path.Combine(model.SelectedChannel.BinaryFilePath, testId + ".dts");
var dtsBackupFileName = dtsFileName + BackupFileExtension;
var chnBackupFileName = Path.Combine(model.SelectedChannel.BinaryFilePath, model.SelectedChannel.BinaryFileName + BackupFileExtension);
return System.IO.File.Exists(dtsBackupFileName) || System.IO.File.Exists(chnBackupFileName);
}
return false;
}
/// <summary>
/// returns the test id from a binary filename
/// it didn't seem the test id was available from any of the structures I had access to
/// but it's the portion of the file name without all the decoration
/// the filename is in the form of TestIdChxxx.yyy.chn
/// </summary>
/// <param name="filename"></param>
/// <returns></returns>
private static string GetTestIdFromBinaryFileName(string filename)
{
var index = filename.IndexOf('.');
filename = filename.Substring(0, index);
index = filename.LastIndexOf("Ch", StringComparison.Ordinal);
//we expect the Ch, but if we don't find it, use the what portion we have
return index >= 0 ? filename.Substring(0, index) : filename;
}
/// <summary>
/// restores DTS file (if a backup file exists)
/// </summary>
private static void RestoreDTSFileIfModified(ITestModificationModel model)
{
var testId = GetTestIdFromBinaryFileName(model.SelectedChannel.BinaryFileName);
var dtsFileName = Path.Combine(model.SelectedChannel.BinaryFilePath, testId + ".dts");
var dtsBackupFileName = dtsFileName + BackupFileExtension;
if (!System.IO.File.Exists(dtsBackupFileName)) return;
//Delete the current .dts file
Common.Utils.FileUtils.DeleteFileOrMove(dtsFileName, APILogger.Log);
System.IO.File.Move(dtsBackupFileName, dtsFileName);
var eventAggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
eventAggregator?.GetEvent<TestModificationEvent>()
.Publish(new TestModificationArgs(model.SelectedChannel.BinaryFilePath, testId));
}
/// <summary>
/// restores an individual channel binary file [assuming a backup exists]
/// note this does not restore any xml entries in the .dts file for the channel
/// </summary>
/// <param name="channelToRestore"></param>
private static void RestoreChannelIfModified(Test.Module.Channel channelToRestore)
{
var chnFileName = ((Test.Module.AnalogInputChannel)channelToRestore).PersistentChannelInfo.Filename;
var chnHeaderBackupFileName = chnFileName + BackupHeaderExtension;
var chnBackupFileName = chnFileName + BackupFileExtension;
//binary files can be backed up two different ways, a full backup or just the header
//if we find the header, we can restore be just overwriting the bytes from the backup header
if (System.IO.File.Exists(chnHeaderBackupFileName))
{
var originalFile = System.IO.File.ReadAllBytes(chnHeaderBackupFileName);
var headerLength = channelToRestore.PersistentChannelInfo.GetFileOffsetOf(Serialization.SliceRaw
.File.PersistentChannel.Field.BeginningOfData);
using (var fs = System.IO.File.OpenWrite(chnFileName))
{
fs.Write(originalFile, 0, headerLength); //146
}
//Delete the copy of the original header
Common.Utils.FileUtils.DeleteFileOrMove(chnHeaderBackupFileName, APILogger.Log);
}
//if there's a full backup, then just replace the entire file with the backup, if there's no full backup
//we are already done
if (!System.IO.File.Exists(chnBackupFileName)) return;
//Delete Current File
Common.Utils.FileUtils.DeleteFileOrMove(chnFileName, APILogger.Log);
//Restore original file
System.IO.File.Copy(chnBackupFileName, chnFileName);
//Delete the copy of the original header
Common.Utils.FileUtils.DeleteFileOrMove(chnBackupFileName, APILogger.Log);
}
/// <summary>
/// backs up a single binary channel
/// binary channels can be backed up two ways, either the entire binary file or just the header
/// we backup the entire binary file if we are modifying data
/// if we are just modifying the binary header, then we just back up the header (since it's significantly smaller usually)
/// </summary>
/// <param name="testModuleChannel"></param>
/// <param name="headerOnly"></param>
public static void BackupChannelIfNeeded(Test.Module.Channel testModuleChannel, bool headerOnly)
{
//Back up .chn file if necessary
var chnFilePath = testModuleChannel.PersistentChannelInfo.Filename;
var chnBackupFilePath = chnFilePath + BackupFileExtension;
// If we have a full backup of the CHN, quit
if (System.IO.File.Exists(chnBackupFilePath)) return;
var chnHeaderBackupFilePath = chnFilePath + BackupHeaderExtension;
byte[] originalFileHeader = null;
// If we are backing up the header and have a header backup, quit
// Else if we have to backup the whole file, get the header backup bytes then delete the header backup
if (System.IO.File.Exists(chnHeaderBackupFilePath))
{
if (headerOnly) { return; }
//Read contents of header backup
originalFileHeader = System.IO.File.ReadAllBytes(chnHeaderBackupFilePath);
//Delete the copy of the original header
Common.Utils.FileUtils.DeleteFileOrMove(chnHeaderBackupFilePath, APILogger.Log);
}
// Get the header bytes from the original chn and write the header backup
if (headerOnly)
{
var originalFile = System.IO.File.ReadAllBytes(chnFilePath);
// Get the end of the header byte offset index
var writeEndIndex = testModuleChannel.PersistentChannelInfo.GetFileOffsetOf(Serialization.SliceRaw.File.PersistentChannel.Field.BeginningOfData);
// open the header backup to write
using (var fs = System.IO.File.OpenWrite(chnHeaderBackupFilePath))
{
fs.Write(originalFile, 0, writeEndIndex);
}
}
else
{
// Copy the whole chn to the backup location
System.IO.File.Copy(chnFilePath, chnBackupFilePath);
// If we dont have a header backup, we are done
if (originalFileHeader == null) return;
// Get the end of the header byte offset index
var writeEndIndex = testModuleChannel.PersistentChannelInfo.GetFileOffsetOf(Serialization.SliceRaw.File.PersistentChannel.Field.BeginningOfData);
// open the full backup file to write the original header on top of
using (var fs = System.IO.File.OpenWrite(chnBackupFilePath))
{
fs.Write(originalFileHeader, 0, writeEndIndex);
}
}
}
/// <summary>
/// backs up the DTS file
/// if the backup file already exists we don't need to do any work, the backup file will be
/// the original version of the file
/// </summary>
/// <param name="directory"></param>
/// <param name="testId"></param>
private static void BackupDTSFile(string directory, string testId)
{
var dtsFilePath = Path.Combine(directory, testId + ".dts");
var dtsBackupFilePath = dtsFilePath + ".bak";
//Back up dts file only if a backup file does not exist. This should guarantee that the
//original file is the only one that is preserved.
if (false == System.IO.File.Exists(dtsBackupFilePath))
{
System.IO.File.Copy(dtsFilePath, dtsBackupFilePath);
}
}
/// <summary>
/// writes all modifications to binary files/dts file
/// </summary>
/// <param name="useISOCodeFilterMapping">
/// controls whether to change isocode field for software filter when the software filter is modified.
/// </param>
/// <param name="bUseZeroForUnfiltered"></param>
/// <param name="model"></param>
/// <returns></returns>
public static bool SaveModification(ITestModificationModel model, bool useISOCodeFilterMapping, bool bUseZeroForUnfiltered)
{
var bWriteDTSFile = model.IsModifiedDataFlag
|| model.IsModifiedFilter
|| model.IsModifiedDescription
|| model.IsModifiedT0
|| model.IsModifiedEuMultiplier
|| model.IsModifiedEuOffset
|| model.IsModifiedSensitivity;
var bWriteChnBackup = model.IsModifiedT0 || model.IsModifiedSensitivity || model.IsModifiedLineFit;
var bWriteHeaderOnly = model.IsModifiedSensitivity || model.IsModifiedT0 || (model.IsModifiedFilter && useISOCodeFilterMapping);
if (!bWriteDTSFile && !bWriteChnBackup)
{
return true;
}
var testId = GetTestIdFromBinaryFileName(model.SelectedChannel.BinaryFileName);
if (bWriteDTSFile)
{
BackupDTSFile(model.SelectedChannel.BinaryFilePath, testId);
}
var dtsFile = Path.Combine(model.SelectedChannel.BinaryFilePath, $"{testId}.dts");
Serialization.SliceRaw.File.ReadTestSetup(dtsFile, out var target, out var testSetup, false);
foreach (var module in target.Modules)
{
if (model.IsModifiedT0 && model.T0Mode == T0Mode.Test)
{
//if we are applying T0 channes to the entire test, then this module will be updated regardless of what channel is selected
ApplyT0Change(model, module);
}
if (model.IsModifiedT0 && model.T0Mode == T0Mode.DAS &&
module.Channels.TrueForAll(ch => ch.ChannelId != model.SelectedChannel.ChannelId) &&
module.BaseSerialNumber == target.Modules.First(m => m.Channels.Exists(ch => ch.ChannelId == model.SelectedChannel.ChannelId))?.BaseSerialNumber)
{
//the model has a modified T0, we're in DAS mode, the module does *not* have the selected channel, but is on the same DAS as the one that does
ApplyT0Change(model, module);
}
foreach (var channel in module.Channels)
{
//find the specific channel in the dts file/binaries that matches the selected channel in the UI
if ((channel.ChannelId != model.SelectedChannel.ChannelId || !channel.PersistentChannelInfo.Filename.EndsWith(model.SelectedChannel.BinaryFileName)) && !model.IsModifiedT0) continue;
if (model.IsModifiedFilter && channel.ChannelId == model.SelectedChannel.ChannelId && useISOCodeFilterMapping)
{
bWriteChnBackup = true;
}
if (bWriteChnBackup)
{
BackupChannelIfNeeded(channel, bWriteHeaderOnly);
}
var aic = (Test.Module.AnalogInputChannel)channel;
if (model.IsModifiedDescription && channel.ChannelId == model.SelectedChannel.ChannelId)
{
channel.ChannelDescriptionString = model.Description;
model.SelectedChannel.SetChannelDescriptionAndDisplayName(model.Description);
}
if (model.IsModifiedDataFlag && channel.ChannelId == model.SelectedChannel.ChannelId)
{
channel.DataFlag = (int)model.SelectedDataFlag;
model.SelectedChannel.DataFlag = channel.DataFlag;
}
if (model.IsModifiedFilter && channel.ChannelId == model.SelectedChannel.ChannelId)
{
//FB 13120
aic.SoftwareFilter = CFCFilterDTSFileStringConverter.FilterClassToString(model.SelectedFilter);
model.SelectedChannel.SoftwareFilter = aic.SoftwareFilter;
if (useISOCodeFilterMapping)
{
var iso = new Common.ISO.IsoCode(aic.IsoCode);
//FB 13120
iso.FilterClass = CFCFilterDTSFileStringConverter.FilterClassToCFC(model.SelectedFilter, bUseZeroForUnfiltered);
aic.IsoCode = iso.StringRepresentation;
var notUsed = aic.PersistentChannelInfo.NumberOfTriggers;
aic.PersistentChannelInfo.IsoCode = iso.StringRepresentation.ToArray();
model.SelectedChannel.IsoCode = iso.StringRepresentation;
}
}
if (model.IsModifiedEuMultiplier && channel.ChannelId == model.SelectedChannel.ChannelId)
{
aic.Multiplier = model.EuMultiplier;
model.SelectedChannel.Multiplier = model.EuMultiplier;
}
if (model.IsModifiedEuOffset && channel.ChannelId == model.SelectedChannel.ChannelId)
{
aic.UserOffsetEU = model.EuOffset;
model.SelectedChannel.UserOffsetEu = model.EuOffset;
}
if (model.IsModifiedSensitivity && channel.ChannelId == model.SelectedChannel.ChannelId)
{
//modifying sensitivity involves both changing the dts file and the .chn binary
var mvPerEU = Math.Abs(model.Sensitivity);
aic.Sensitivity = model.Sensitivity;
model.SelectedChannel.Sensitivity = model.Sensitivity;
var p = aic.PersistentChannelInfo;
//this is to force the values to be initialized before setting
// ReSharper disable once UnusedVariable
var notUsed = p.MvPerEu;
p.MvPerEu = mvPerEU;
}
if (model.IsModifiedT0 && model.T0Mode == T0Mode.DAS && channel.ChannelId == model.SelectedChannel.ChannelId)
{
ApplyT0Change(model, module);
}
if (model.IsModifiedLineFit && channel.ChannelId == model.SelectedChannel.ChannelId)
{
ApplyLineFit(model, channel);
}
if (bWriteChnBackup && channel.ChannelId == model.SelectedChannel.ChannelId || model.IsModifiedT0)
{
var p = aic.PersistentChannelInfo;
p.StampCrc();
}
}
}
if (bWriteDTSFile)
{
WriteDTSFileChanges(model, dtsFile, target, testSetup, testId);
}
//this is to publish that the selected channel has changed
var eventAggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
eventAggregator?.GetEvent<TestModificationEvent>()
.Publish(new TestModificationArgs(model.SelectedChannel.BinaryFilePath, testId));
return true;
}
// added TestId parameter to correct incorrect target.TestId values, function now uses passed in test id rather than target.testid
//14897 modifying T0 in view data creates a second dts file holding the name of the test setup and not the test ID
private static void WriteDTSFileChanges(ITestModificationModel model, string dtsFile, Test target, TestSetup testSetup, string testId)
{
var f = new Serialization.SliceRaw.File { DefaultEncoding = Encoding.Unicode.CodePage };
f.Exporter.Write(model.SelectedChannel.BinaryFilePath, testId, target, false, false, UNUSED_START_TIME, UNUSED_DATA_COLLECTION_LENGTH);
//Append the <TestSetup> DataPRO info to the end of the existing .dts file which already contains SLICEWare-compatible info
using (var writer = new StringWriter())
{
new System.Xml.Serialization.XmlSerializer(typeof(TestSetup)).Serialize(writer, testSetup);
Encoding encoder;
try
{
//force UTF-16 for the dts file, it contains "UTF-16" in the xml by default and isn't consumed by anything that requires
//codepage exports (CSV/excel)
encoder = Encoding.Unicode; //UTF-16
}
catch (Exception ex)
{
APILogger.Log("Problem getting encoder", ex);
encoder = Encoding.Default;
}
using (var fileWriter = new StreamWriter(dtsFile, true, encoder))
{
fileWriter.Write(fileWriter.NewLine + writer);
}
System.Threading.Thread.Sleep(10);
}
}
public static bool SaveModificationDataFlag(ITestModificationModel model)
{
if (!model.IsModifiedDataFlag)
{
return true;
}
var testId = GetTestIdFromBinaryFileName(model.SelectedChannel.BinaryFileName);
BackupDTSFile(model.SelectedChannel.BinaryFilePath, testId);
var dtsFile = Path.Combine(model.SelectedChannel.BinaryFilePath, $"{testId}.dts");
Serialization.SliceRaw.File.ReadTestSetup(dtsFile, out var target, out var testSetup, false);
foreach (var module in target.Modules)
{
foreach (var channel in module.Channels)
{
//find the specific channel in the dts file/binaries that matches the selected channel in the UI
if (channel.ChannelId != model.SelectedChannel.ChannelId) continue;
if (!model.IsModifiedDataFlag) continue;
channel.DataFlag = (int)model.SelectedDataFlag;
model.SelectedChannel.DataFlag = channel.DataFlag;
}
}
WriteDTSFileChanges(model, dtsFile, target, testSetup, testId);
//this is to publish that the selected channel has changed
var eventAggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
eventAggregator?.GetEvent<ChannelsModificationNotification>().Publish(new List<ITestChannel> { model.SelectedChannel });
PopulateFromChannel(model);
return true;
}
/// <summary>
/// this applies line fit on the selected channel
/// </summary>
/// <param name="model"></param>
/// <param name="channel"></param>
public static void ApplyLineFit(ITestModificationModel model, Test.Module.Channel channel)
{
BackupChannelIfNeeded(channel, false);
using (var p = channel.PersistentChannelInfo)
{
p.StampCrc(); //Force init of all fields
var adcData = p.Data.ToList();
var startIndex = GetSampleIndexFromMilliseconds(channel.ParentModule, model.T1);
var endIndex = GetSampleIndexFromMilliseconds(channel.ParentModule, model.T2);
//swap start and end if they are offset
if ( startIndex > endIndex)
{
var temp = endIndex;
endIndex = startIndex;
startIndex = temp;
}
var start = startIndex + 1;
var dataLength = (ulong)adcData.LongCount();
if ( start >= dataLength)
{
return;
}
if (start > int.MaxValue) { start = int.MaxValue; }
if (endIndex >= dataLength) { endIndex = dataLength - 1; }
if (startIndex > endIndex)
{
var temp = endIndex;
endIndex = startIndex;
startIndex = temp;
}
var end = endIndex - 1;
var deltaIndex = endIndex - startIndex;
var startADC = adcData[(int)startIndex];
var deltaADC = adcData[(int)endIndex] - startADC;
//http://manuscript.dts.local/f/cases/36697/Line-fit-feature-not-working-properly-after-modifying-data
//modified this to just do a straight line fit between two points - DTM 2024-02-05
for (var i = start; i <= end; i++)
{
adcData[(int)i] = (short)(startADC + (deltaADC * ((double)(i - start) / deltaIndex)));
}
p.ReplaceData(adcData.ToArray());
}
}
/// <summary>
/// preview line fit will update the data in the UI for the selected channel, but not on disk
/// </summary>
public static void PreviewLineFit(ITestModificationModel model)
{
var f = new Serialization.SliceRaw.File { DefaultEncoding = Encoding.Unicode.CodePage };
f.Importer.Read(model.SelectedChannel.BinaryFilePath, out var target);
Test.Module.Channel ch = null;
foreach (var module in target.Modules)
{
if (null != ch)
{
break;
}
foreach (var channel in module.Channels)
{
if (channel.ChannelId == model.SelectedChannel.ChannelId)
{
ch = channel;
}
}
}
if (null == ch)
{
return;
}
var startIndex = GetSampleIndexFromMilliseconds(ch.ParentModule, model.T1);
var endIndex = GetSampleIndexFromMilliseconds(ch.ParentModule, model.T2);
var eventAggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
eventAggregator?.GetEvent<ChannelsModificationLineFitNotification>().Publish(new LineFitArgs(model.SelectedChannel, startIndex, endIndex));
}
/// <summary>
/// Returns the sample index from the file based on the given milliseconds
/// </summary>
/// <param name="m"></param>
/// <param name="milliSeconds"></param>
/// <returns></returns>
private static ulong GetSampleIndexFromMilliseconds(Test.Module m, double milliSeconds)
{
try
{
var seconds = milliSeconds / 1000.0;
var triggerSampleNumber = Convert.ToDouble(m.TriggerSampleNumbers[0]);
var offset = m.SampleRateHz * seconds;
if (triggerSampleNumber + offset > m.StartRecordSampleNumber)
{
return Convert.ToUInt64(triggerSampleNumber + offset - m.StartRecordSampleNumber);
}
}
catch( OverflowException ex)
{
if (milliSeconds > 0) { return ulong.MaxValue; }
return 0UL;
}
return 0UL;
}
/// <summary>
/// applies an adjustment to T0 for the module provided
/// </summary>
/// <param name="model"></param>
/// <param name="module"></param>
private static void ApplyT0Change(ITestModificationModel model, Test.Module module)
{
//apply t0 changes
var sampleNumberChange = Convert.ToInt64(-1 * Math.Truncate(model.T0 * module.SampleRateHz / 1000D));
var timeChange = 0 != module.SampleRateHz
? -1D * sampleNumberChange / module.SampleRateHz
: model.T0 / 1000D;
if (module.TriggerSampleNumbers.Any())
{
var oldTrigger = module.TriggerSampleNumbers[0];
var newTrigger = 0UL;
if ((long)oldTrigger > sampleNumberChange)
{
newTrigger = Convert.ToUInt64((long)oldTrigger - sampleNumberChange);
module.RequestedPreTriggerSeconds =
module.RequestedPreTriggerSeconds - timeChange;
module.RequestedPostTriggerSeconds =
module.RequestedPostTriggerSeconds - timeChange;
}
module.TriggerSampleNumbers = new List<ulong> { newTrigger };
foreach (var channel in module.Channels)
{
channel.TimeOfFirstSampleSec -= timeChange;
BackupModifyT0AndStampChannel(channel, newTrigger);
}
foreach (var channel in module.CalculatedChannels)
{
channel.TimeOfFirstSampleSec -= timeChange;
BackupModifyT0AndStampChannel(channel, newTrigger);
}
}
}
private static void BackupModifyT0AndStampChannel(Test.Module.Channel channel, ulong newTrigger)
{
BackupChannelIfNeeded(channel, true);
var aic = (Test.Module.AnalogInputChannel)channel;
var p = aic.PersistentChannelInfo;
//this is done just to assure the property is initialized
// ReSharper disable once UnusedVariable
var notUsed = p.TriggerSampleNumbers;
p.TriggerSampleNumbers = new[] { newTrigger };
p.StampCrc();
}
/// <summary>
/// revert changes since last save
/// </summary>
public static void UndoModification(ITestModificationModel model)
{
PopulateFromChannel(model);
var eventAggregator = ContainerLocator.Container.Resolve<IEventAggregator>();
eventAggregator?.GetEvent<ChannelsModificationNotification>().Publish(new List<ITestChannel> { model.SelectedChannel });
}
/// <summary>
/// Populates/Initializes UI from selected channel
/// </summary>
public static void PopulateFromChannel(ITestModificationModel model)
{
model.Description = model.SelectedChannel?.ChannelDescriptionString;
model.EuMultiplier = model.SelectedChannel?.Multiplier ?? 1D;
model.T0 = 0D;
model.EuOffset = model.SelectedChannel?.UserOffsetEu ?? 0D;
//FB 13120
model.SelectedFilter = FilterClass.GetFilterClassFromString(model.SelectedChannel?.SoftwareFilter);
model.T1 = 0D;
model.T2 = 0D;
model.Sensitivity = model.SelectedChannel?.Sensitivity ?? 1D;
model.SelectedDataFlag = null == model.SelectedChannel ? DataFlag.None : (DataFlag)model.SelectedChannel.DataFlag;
}
}
}

View File

@@ -0,0 +1,141 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="CalDate" xml:space="preserve">
<value />
</data>
<data name="FailedToModifySensitivity" xml:space="preserve">
<value />
</data>
<data name="ModifyDate" xml:space="preserve">
<value />
</data>
<data name="NonLinear" xml:space="preserve">
<value />
</data>
<data name="ProportionalToExcitation" xml:space="preserve">
<value />
</data>
<data name="SensorCalibration" xml:space="preserve">
<value />
</data>
<data name="UpdateDatabase" xml:space="preserve">
<value />
</data>
</root>

View File

@@ -0,0 +1,138 @@
using System;
using System.Windows.Media.Imaging;
using DTS.Common;
using DTS.Common.Interface;
using DTS.Viewer.TestModification;
using Prism.Ioc;
using Prism.Modularity;
using Unity;
// ReSharper disable RedundantAttributeUsageProperty
// ReSharper disable UnusedParameter.Local
[assembly: TestModificationModuleName()]
[assembly: TestModificationModuleImageAttribute()]
namespace DTS.Viewer.TestModification
{
[Module(ModuleName = "TestModification")]
public class TestModificationModule : IModule
{
/// <summary>
/// Injected unity container
/// </summary>
private readonly IUnityContainer _unityContainer;
/// <summary>
/// Initializes a new instance of the <see cref="TestModificationModule"/> class.
/// </summary>
/// <param name="unityContainer">Obtained reference of the unity container by using dependency injection.</param>
public TestModificationModule(IUnityContainer unityContainer)
{
_unityContainer = unityContainer;
}
public void Initialize()
{
// Register View & View-Model with Unity dependency injection container as a singleton.
_unityContainer.RegisterType<ITestModificationView, TestModificationView>();
_unityContainer.RegisterType<ITestModificationViewModel, TestModificationViewModel>();
}
public void OnInitialized(IContainerProvider containerProvider)
{
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
Initialize();
}
}
/// <summary>
/// Attribute class contains assembly name
/// </summary>
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
public class TestModificationModuleNameAttribute : TextAttribute
{
public TestModificationModuleNameAttribute() : this(null) { }
public TestModificationModuleNameAttribute(string s)
{
AssemblyName = AssemblyNames.TestModification.ToString();
}
public override string AssemblyName { get; }
public override Type GetAttributeType()
{
return typeof(TextAttribute);
}
public override string GetAssemblyName()
{
return AssemblyName;
}
}
/// <summary>
/// Attribute class contains assembly image and name - used on the Main screen to display available components
/// </summary>
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = false)]
public class TestModificationModuleImageAttribute : ImageAttribute
{
private BitmapImage _img;
public TestModificationModuleImageAttribute() : this(null) { }
public override BitmapImage AssemblyImage
{
get { _img = AssemblyInfo.GetImage(AssemblyNames.TestModification.ToString()); return _img; }
}
public TestModificationModuleImageAttribute(string s)
{
_img = AssemblyInfo.GetImage(AssemblyNames.TestModification.ToString());
}
public override Type GetAttributeType()
{
return typeof(ImageAttribute);
}
public override BitmapImage GetAssemblyImage()
{
return AssemblyImage;
}
private string _name;
public override string AssemblyName
{
get { _name = AssemblyNames.TestModification.ToString(); return _name; }
}
public override string GetAssemblyName()
{
return AssemblyName;
}
private string _group;
public override string AssemblyGroup
{
get { _group = eAssemblyGroups.Viewer.ToString(); return _group; }
}
public override string GetAssemblyGroup()
{
return AssemblyGroup;
}
private eAssemblyRegion _region;
public override eAssemblyRegion AssemblyRegion
{
get { _region = eAssemblyRegion.TestModificationRegion; return _region; }
}
public override eAssemblyRegion GetAssemblyRegion()
{
return AssemblyRegion;
}
}
}

View File

@@ -0,0 +1,183 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:common="clr-namespace:DTS.Common;assembly=DTS.Common">
<!--<Style TargetType="Label">
<Setter Property="Width" Value="100"/>
</Style>-->
<Style TargetType="StackPanel">
<Setter Property="HorizontalAlignment" Value="Stretch"/>
</Style>
<Style TargetType="{x:Type RadioButton}" >
<Setter Property="Width" Value="100"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<BulletDecorator Background="White" Cursor="Hand">
<BulletDecorator.Bullet>
<Grid Height="16" Width="16">
<!--Define size of the Bullet-->
<!--The two borders-->
<Border Name="RadioOuter" Background="Transparent" BorderBrush="Gainsboro" BorderThickness="2" CornerRadius="2" />
<Border CornerRadius="0" Margin="4" Name="RadioMark" Background="#FFADADAD" Visibility="Hidden" />
</Grid>
</BulletDecorator.Bullet>
<!--Text element-->
<TextBlock Margin="3,1,0,0" Foreground="#FF3E3E3E" FontFamily="Calibri" FontSize="12">
<ContentPresenter />
</TextBlock>
</BulletDecorator>
<!--If item is checked, trigger the visibility of the mark-->
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<!--If item is checked, trigger the visibility of the mark and change the color of the selected bullet into a darker gray for better highlighting-->
<Setter TargetName="RadioMark" Property="Visibility" Value="Visible"/>
<Setter TargetName="RadioOuter" Property="BorderBrush" Value="#FFADADAD" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ButtonFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border>
<!--<Rectangle SnapsToDevicePixels="true" Margin="4" Stroke="Black" StrokeDashArray="1 2" StrokeThickness="1"/>-->
<Rectangle Margin="2" StrokeThickness="1" Stroke="LightGray" StrokeDashArray="1 2" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="RoundedButton" TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}"/>
<Setter Property="Background" Value="LightGray"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="BorderThickness" Value="3"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="0,0,1,1"/>
<Setter Property="Height" Value="25"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<ControlTemplate.Resources>
<Storyboard x:Key="ShowShine">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="Shine" Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00.5000000" Value="1"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="HideShine">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="Shine" Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00.3000000" Value="0"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</ControlTemplate.Resources>
<Border CornerRadius="5,5,5,5" BorderThickness="1,1,1,1" RenderTransformOrigin="0.5,0.5" x:Name="border" BorderBrush="Silver">
<Border.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1"/>
<SkewTransform AngleX="0" AngleY="0"/>
<RotateTransform Angle="0"/>
<TranslateTransform X="0" Y="0"/>
</TransformGroup>
</Border.RenderTransform>
<Border Background="{TemplateBinding Background}" CornerRadius="5,5,5,5" x:Name="border1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.5*"/>
<RowDefinition Height="0.5*"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" CornerRadius="5,5,0,0">
<Border.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#00FFFFFF" Offset="0"/>
<GradientStop Color="#7EFFFFFF" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
</Border>
<Border Grid.Row="1" Opacity="0" x:Name="Shine" Width="Auto" Height="Auto" CornerRadius="0,0,5,5" Margin="1,0,-1,0" Background="{TemplateBinding BorderBrush}"/>
<ContentPresenter VerticalAlignment="Center" Grid.Row="0" Grid.RowSpan="2" HorizontalAlignment="Center" x:Name="contentPresenter"/>
</Grid>
</Border>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" TargetName="border1" Value="0.5"/>
<Setter Property="Opacity" TargetName="border" Value="1"/>
<Setter Property="Opacity" TargetName="contentPresenter" Value="0.5"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="RenderTransform" TargetName="border">
<Setter.Value>
<TransformGroup>
<ScaleTransform ScaleX="0.9" ScaleY="0.9"/>
<SkewTransform AngleX="0" AngleY="0"/>
<RotateTransform Angle="0"/>
<TranslateTransform X="0" Y="0"/>
</TransformGroup>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource HideShine}" x:Name="HideShine_BeginStoryboard"/>
</Trigger.ExitActions>
<Trigger.EnterActions>
<BeginStoryboard x:Name="ShowShine_BeginStoryboard" Storyboard="{StaticResource ShowShine}"/>
</Trigger.EnterActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="TextBlock">
<Setter Property="FontFamily" Value="Calibri"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="TextAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<!--<Style TargetType="{x:Type ComboBox}">
<Setter Property="FontFamily" Value="Calibri"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
</Style>-->
<ObjectDataProvider x:Key="CfcFilterDataProvider" MethodName="GetValues" ObjectType="{x:Type system:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="common:CFCFilter"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<ObjectDataProvider x:Key="DataFlagProvider" MethodName="GetValues" ObjectType="{x:Type system:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="common:DataFlag"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<ObjectDataProvider x:Key="T0ModeProvider" MethodName="GetValues" ObjectType="{x:Type system:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="common:T0Mode"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</ResourceDictionary>

View File

@@ -0,0 +1,323 @@
using System;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using DTS.Common.Base;
using DTS.Common.Events;
using DTS.Common.Interface;
using DTS.Common.Utilities.Logging;
using DTS.Common.Settings;
using DTS.Viewer.ChartOptions.Model;
using DTS.Viewer.TestModification.Model;
using Prism.Commands;
using Prism.Events;
using Prism.Regions;
using DTS.Common.Interactivity;
using Unity;
// ReSharper disable CheckNamespace
// ReSharper disable ConvertToAutoProperty
// ReSharper disable InconsistentNaming
// ReSharper disable UnusedAutoPropertyAccessor.Local
// ReSharper disable NotAccessedField.Local
// ReSharper disable RedundantDefaultMemberInitializer
namespace DTS.Viewer.TestModification
{
public class TestModificationViewModel : BaseViewModel<ITestModificationViewModel>, ITestModificationViewModel
{
public ITestModificationView View { get; set; }
public IBaseViewModel Parent { get; set; }
private IEventAggregator _eventAggregator { get; set; }
private IUnityContainer _unityContainer { get; set; }
public InteractionRequest<Notification> NotificationRequest { get; private set; }
public new InteractionRequest<Confirmation> ConfirmationRequest { get; private set; }
/// <summary>
/// Creates a new instance of the TestModificationViewListModel.
/// </summary>
/// <param name="view">The TestModification View interface.</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 TestModificationViewModel(ITestModificationView view, IRegionManager regionManager, IEventAggregator eventAggregator, IUnityContainer unityContainer)
: base(regionManager, eventAggregator, unityContainer)
{
Model = new TestModificationModel();
View = view;
View.DataContext = this;
NotificationRequest = new InteractionRequest<Notification>();
ConfirmationRequest = new InteractionRequest<Confirmation>();
_eventAggregator = eventAggregator;
_unityContainer = unityContainer;
_eventAggregator.GetEvent<RaiseNotification>().Subscribe(OnRaiseNotification);
_eventAggregator.GetEvent<GraphSelectedChannelsNotification>().Subscribe(OnGraphSelectedCountChanged);
_eventAggregator.GetEvent<ShiftT0Event>().Subscribe(OnShiftT0Event);
_eventAggregator.GetEvent<SetUseZeroForUnfilteredEvent>().Subscribe(OnSetUseZeroForUnfiltered);
}
private void OnSetUseZeroForUnfiltered(bool useZeroForUnfiltered)
{
UseZeroForUnfiltered = useZeroForUnfiltered;
}
private void OnShiftT0Event(ShiftT0EventArguments args)
{
if (args.IsInitialization) { return; }
if (args.IsKeyPress)
{
if (null == Model) { return; }
//if we can't modify no reason to continue, short circuit out
if (!TestModificationVisability) { return; }
if (null == Model.SelectedChannel || null == Model.SelectedChannel.ParentModule) { return; }
else
{
//go and modify the T0!
//calculate a new T0 given the sample rate
var t0 = Model.T0 + args.T0Steps * (1000D / Model.SelectedChannel.ParentModule.SampleRateHz);
Model.T0 = t0;
}
_eventAggregator.GetEvent<TestModificationChangedEvent>().Publish(Model);
}
else { Model.T0 = args.T0Time; }
}
#region Methods
public override void Initialize()
{
}
public override void Initialize(object parameter)
{
Parent = (IBaseViewModel)parameter;
}
/// <summary>
/// Private Event handler for RaiseNotification event.
/// </summary>
private void OnRaiseNotification(NotificationContentEventArgs eventArgsWithTitle)
{
// Notification object expects a NotificationContentEventArgsWithoutTitle object and a Title string.
var eventArgsWithoutTitle = new NotificationContentEventArgs(eventArgsWithTitle.Message, eventArgsWithTitle.MessageDetails, eventArgsWithTitle.Image);
NotificationRequest.Raise(new Notification
{
Content = eventArgsWithoutTitle,
Title = eventArgsWithTitle.Title
});
}
private static bool IsPopulating { get; set; }
private void OnGraphSelectedCountChanged(GraphSelectedChannelsNotificationArg arg)
{
if (Parent != arg?.ParentVM) return;
var channels = arg.SelectedChannels;
var canModify = channels.Count == 1 && channels.Count(x => x.IsSelected) == 1;
TestModificationVisability = canModify;
if (canModify)
{
IsPopulating = true;
Model.IsT0ModeTestOnly = SettingsDB.GetGlobalValueBool(Keys.ApplyShiftT0ModsTestOnly.ToString(), false);
var viewModel = _unityContainer.TryResolve<IViewerMainViewModel>();
Model.EnableSensitivityControl = viewModel.DoesUserHaveEditPermission;
Model.EnableLineFitControl = viewModel.DoesUserHaveEditPermission;
Model.EnableDescriptionControl = viewModel.DoesUserHaveEditPermission;
Model.EnableEUMultiplierControl = viewModel.DoesUserHaveEditPermission;
Model.EnableEUOffsetControl = viewModel.DoesUserHaveEditPermission;
Model.EnableFilterControl = viewModel.DoesUserHaveEditPermission;
Model.IsT0Enabled = viewModel.DoesUserHaveEditPermission;
Model.IsDataFlagEnabled = viewModel.DoesUserHaveEditPermission;
Model.SelectedChannel = channels[0];
TestModelManipulation.PopulateFromChannel(Model);
IsPopulating = false;
IsBackedUp = TestModelManipulation.BackupExists(Model);
_eventAggregator.GetEvent<ShowT0CursorEvent>().Publish(true);
}
else if (channels.Count == 0)
{
Model.SelectedChannel = null;
}
else
{
_eventAggregator.GetEvent<ShowT0CursorEvent>().Publish(false);
}
}
#endregion
#region ContextRegion
#endregion
#region Commands
private DelegateCommand _previewCommand;
public DelegateCommand PreviewCommand => _previewCommand ?? (_previewCommand = new DelegateCommand(PreviewMethod));
private void PreviewMethod()
{
TestModelManipulation.PreviewLineFit(Model);
}
private DelegateCommand _writeCommand;
public DelegateCommand WriteCommand => _writeCommand ?? (_writeCommand = new DelegateCommand(WriteMethod));
private void WriteMethod()
{
var window = Application.Current.MainWindow;
if (null == window) { return; }
//14661 Shifting T zero beyond post trigger seconds crashes application
//don't allow T0 to shift beyond test data
if (Model.IsModifiedT0 && !Model.ValidateT0())
{
_eventAggregator.GetEvent<PageErrorEvent>()
.Publish(new PageErrorArg(new[] { Resources.StringResources.T0MustBeInDataset }, null));
return;
}
var dialogResult = MessageBox.Show(window, Resources.StringResources.WriteFilesPrompt, "", MessageBoxButton.YesNo);
try
{
UseISOCodeFilterMapping = SettingsDB.GetGlobalValueBool("UseISOCodeFilterMapping", true);
}
catch (Exception ex)
{
APILogger.Log(ex);
}
APILogger.Log(Resources.StringResources.WriteFilesPrompt, $"User pressed {dialogResult.ToString()}");
if (dialogResult == MessageBoxResult.Yes)
{
TestModelManipulation.SaveModification(Model, UseISOCodeFilterMapping, UseZeroForUnfiltered);
IsBackedUp = TestModelManipulation.BackupExists(Model);
}
}
private DelegateCommand _undoCommand;
public DelegateCommand UndoCommand => _undoCommand ?? (_undoCommand = new DelegateCommand(UndoMethod));
private void UndoMethod()
{
var window = Application.Current.MainWindow;
if (null == window) { return; }
var dialogResult = MessageBox.Show(window, Resources.StringResources.UndoPrompt, "", MessageBoxButton.YesNo);
APILogger.Log(Resources.StringResources.UndoPrompt, $"User pressed {dialogResult.ToString()}");
if (dialogResult == MessageBoxResult.Yes)
{
TestModelManipulation.UndoModification(Model);
}
}
private DelegateCommand _undoAllCommand;
public DelegateCommand UndoAllCommand => _undoAllCommand ?? (_undoAllCommand = new DelegateCommand(UndoAllMethod));
private void UndoAllMethod()
{
var window = Application.Current.MainWindow;
if (null == window) { return; }
var dialogResult = MessageBox.Show(window, Resources.StringResources.UndoAllPrompt, "", MessageBoxButton.YesNo);
APILogger.Log(Resources.StringResources.UndoAllPrompt, $"User pressed {dialogResult.ToString()}");
if (dialogResult == MessageBoxResult.Yes)
{
TestModelManipulation.UndoAllModification(Model);
IsBackedUp = TestModelManipulation.BackupExists(Model);
}
}
public void PublishChanges()
{
if (!IsPopulating && Model.IsModifiedDataFlag)
{
//Write this immediately
TestModelManipulation.SaveModificationDataFlag(Model);
IsBackedUp = TestModelManipulation.BackupExists(Model);
}
_eventAggregator?.GetEvent<TestModificationChangedEvent>().Publish(Model);
}
#endregion Commands
#region Properties
/// <summary>
/// when true forces the ISOCode field filter SoftwareFilter to always match the SoftwareFilter of the channel
/// Whenever the softwarefilter for a graph channel is modified it will check this value before
/// modifying the channel isocode
/// </summary>
public bool UseISOCodeFilterMapping { get; set; } = false;
private TestModificationModel _model = null; // new TestModificationModel();
public new TestModificationModel Model
{
get => _model;
set
{
if (_model != null) _model.Parent = null;
_model = value;
if (_model != null) _model.Parent = this;
OnPropertyChanged("Model");
}
}
public bool UseZeroForUnfiltered
{
get; set;
}
private bool _isFilterEnabled = false;
public bool IsFilterEnabled { get => _isFilterEnabled; set { _isFilterEnabled = value; OnPropertyChanged("IsFilterEnabled"); } }
private bool _testModificationVisability = false;
public bool TestModificationVisability { get => _testModificationVisability; set { _testModificationVisability = value; OnPropertyChanged("TestModificationVisability"); } }
///<summary>
///Occurs when a property value changes.
///</summary>
public new event PropertyChangedEventHandler PropertyChanged;
private new void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// Gets the HeaderInfo.
/// </summary>
public string HeaderInfo => "TestSummaryRegion";
private bool _isBusy = false;
public new bool IsBusy
{
get => _isBusy;
set { _isBusy = value; OnPropertyChanged("IsBusy"); }
}
private bool _isDirty;
public new bool IsDirty
{
get => _isDirty;
set => _isDirty = value;
}
private bool _isNavigationIncluded;
public new bool IsNavigationIncluded
{
get => _isNavigationIncluded;
set => _isNavigationIncluded = value;
}
private bool _isBackedUp;
public bool IsBackedUp
{
get => _isBackedUp;
private set
{
_isBackedUp = value;
OnPropertyChanged("IsBackedUp");
}
}
#endregion
}
}

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("DTS.Viewer.TestModification")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("DTS.Viewer.TestModification")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("5ee7c61f-e9fe-479b-be1f-78a142341c3b")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]