alielie
2025-06-12 c1c3772a11b1f3fbad8846cc714120771d85fa53
新增SeqEditor
25个文件已修改
32个文件已添加
2600 ■■■■■ 已修改文件
HMI.MenuManage/RoleManage.xaml 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Controls/MultiCombox.xaml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Controls/MultiCombox.xaml.cs 201 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Controls/treegrid/Controls/TreeDataGrid.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Controls/treegrid/Controls/TreeItemData.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Controls/treegrid/Themes/TreeItemContentControl.xaml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Controls/treegrid/TreeItemConverter.cs 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/HMI.UI.csproj 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/MaskWindow.xaml 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Style/FormStyle.xaml 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Style/PanuonResource.xaml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UI/Util/MsgBoxHelper.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.UnitTest/MainWindow.xaml.cs 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.JarUI/JarUI.xaml 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.JarUI/JarUI.xaml.cs 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/AssemblyInfo.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/Constant/DefaultStepName.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/Constant/StepTypeStr.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/Entity/EditablePO/IEPO.cs 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/Entity/EditablePO/NumValTestEPO.cs 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/Entity/EditablePO/StringValTestEPO.cs 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/Entity/EditablePO/WaitEPO.cs 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/Entity/SeqUI.cs 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/Entity/StepUI.cs 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/Entity/VarUI.cs 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/Plugin.SeqEditor.csproj 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/SeqEditor.xaml 254 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/SeqEditor.xaml.cs 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/SettingPanel/ActionPanel.xaml 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/SettingPanel/ActionPanel.xaml.cs 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/SettingPanel/CommonProperty.xaml 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/SettingPanel/CommonProperty.xaml.cs 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/SettingPanel/NumValTestPanel.xaml 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/SettingPanel/NumValTestPanel.xaml.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/SettingPanel/StringValTestPanel.xaml 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/SettingPanel/StringValTestPanel.xaml.cs 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/SettingPanel/WaitPanel.xaml 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/SettingPanel/WaitPanel.xaml.cs 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/Util/ItemData.cs 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/Util/PoExplain.cs 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/Util/Step2EPO.cs 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.SeqEditor/ViewModel/SeqEditorViewModel.cs 454 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/SingleCell.xaml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/SingleCell.xaml.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TestResult/TestResultView.xaml.cs 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TsExecution/SingleControl.xaml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TsExecution/SingleControl.xaml.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Plugin.TsExecution/TsExecution.xaml.cs 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/App.config 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/BuildCmd.bat 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Lang/en-US/SeqEditor.json 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Lang/zh-CN/SeqEditor.json 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/Lang/zh-CN/app.json 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/LoadingWindow.xaml.cs 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/WPFProject.csproj 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/WPFProject.sln 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
WPFProject/packages.config 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HMI.MenuManage/RoleManage.xaml
@@ -99,8 +99,8 @@
Command="{Binding CheckCommand}"
CommandParameter="{Binding Path=IsChecked,RelativeSource={RelativeSource Mode=Self}}"
/>
                                <Image Width="{StaticResource ContentFontSize}"
                                       Height="{StaticResource ContentFontSize}" Margin="5" Source="{Binding MenuIcon,Converter={StaticResource path2Image},Mode=OneWay}"/>
                                <!--<Image Width="{StaticResource ContentFontSize}"
                                       Height="{StaticResource ContentFontSize}" Margin="5" Source="{Binding MenuIcon,Converter={StaticResource path2Image},Mode=OneWay}"/>-->
                                <TextBlock VerticalAlignment="Center" Text="{Binding MenuName}"/>
                            </StackPanel>
                        </HierarchicalDataTemplate>
HMI.UI/Controls/MultiCombox.xaml
New file
@@ -0,0 +1,10 @@
<UserControl x:Class="HMI.UI.Controls.MultiCombox"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:HMI.UI.Controls"
             mc:Ignorable="d"
             d:DesignHeight="450" d:DesignWidth="800">
    <ComboBox ></ComboBox>
</UserControl>
HMI.UI/Controls/MultiCombox.xaml.cs
New file
@@ -0,0 +1,201 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
namespace HMI.UI
{
    public class MultiComboBoxItem : ComboBoxItem, INotifyPropertyChanged
    {
        #region ...
        public event PropertyChangedEventHandler PropertyChanged;
        protected void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion
        private MultiComboBox parent;
        public MultiComboBoxItem(MultiComboBox parent)
        {
            this.parent = parent;
            this.DataContextChanged += MultiComboBoxItem_DataContextChanged;
        }
        protected override void OnMouseMove(MouseEventArgs e)
        {
            this.PreviewMouseUp += MultiComboBoxItem_PreviewMouseUp;
            base.OnMouseMove(e);
        }
        protected override void OnMouseLeave(MouseEventArgs e)
        {
            this.PreviewMouseUp -= MultiComboBoxItem_PreviewMouseUp;
            base.OnMouseLeave(e);
        }
        protected override void OnLostFocus(RoutedEventArgs e)
        {
            this.PreviewMouseUp -= MultiComboBoxItem_PreviewMouseUp;
            base.OnLostFocus(e);
        }
        private void MultiComboBoxItem_PreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            if (DataContext is MultiComboBoxData data)
            {
                data.IsChecked = !data.IsChecked;
                e.Handled = true;
            }
        }
        private void MultiComboBoxItem_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (e.OldValue != null && e.OldValue is MultiComboBoxData oldData)
            {
                oldData.IsCheckedChanged -= DataIsExpandedChange;
            }
            if (DataContext != null && DataContext is MultiComboBoxData newData)
            {
                newData.IsCheckedChanged += DataIsExpandedChange;
            }
        }
        private void DataIsExpandedChange(object sender, bool IsExpanded)
        {
            parent.Item_IsCheckedChanged((MultiComboBoxData)sender, this, IsExpanded);
        }
    }
    public class MultiComboBoxDataConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is IEnumerable<object> items)
            {
                return items.Select(item => new MultiComboBoxData(item, "DisplayName", "DataValue")).ToList();
            }
            return null;
        }
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value;
        }
    }
    public class MultiComboBoxData : INotifyPropertyChanged
    {
        #region ...
        public event PropertyChangedEventHandler PropertyChanged;
        protected void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion
        public event EventHandler<bool> IsCheckedChanged;
        private bool isChecked;
        public bool IsChecked
        {
            get { return isChecked; }
            set
            {
                isChecked = value;
                RaisePropertyChanged("IsChecked");
                IsCheckedChanged?.Invoke(this, value);
            }
        }
        public string DisplayName
        {
            get => showName?.GetValue(Data).ToString();
        }
        public string DataValue
        {
            get => valueName?.GetValue(Data).ToString();
        }
        private PropertyInfo showName;
        private PropertyInfo valueName;
        public MultiComboBoxData(object data, string showName, string valueName)
        {
            this.Data = data;
            this.showName = data.GetType().GetProperty(showName);
            this.valueName = data.GetType().GetProperty(valueName);
        }
        public object Data { get; private set; }
    }
    /// <summary>
    /// MultiCombox.xaml 的交互逻辑
    /// </summary>
    public partial class MultiComboBox : ComboBox
    {
        protected override DependencyObject GetContainerForItemOverride()
        {
            var item = new MultiComboBoxItem(this);
            return item;
        }
        [Bindable(true)]
        [Category("Appearance")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        [Localizability(LocalizationCategory.NeverLocalize)]
        public object SelectedValues
        {
            get
            {
                return GetValue(SelectedValuesProperty);
            }
            set
            {
                SetValue(SelectedValuesProperty, value);
            }
        }
        public static readonly DependencyProperty SelectedValuesProperty =
            DependencyProperty.RegisterAttached("SelectedValues", typeof(object), typeof(MultiComboBox),
                new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
        public void Item_IsCheckedChanged(MultiComboBoxData data, MultiComboBoxItem item, bool isChecked)
        {
            var selectedValues = SelectedValues?.ToString() ?? "";
            var showNames = this.Text?.ToString() ?? "";
            List<string> values = string.IsNullOrEmpty(selectedValues) ? new List<string>() : selectedValues.Split(',').ToList();
            List<string> names = string.IsNullOrEmpty(showNames) ? new List<string>() : showNames.Split(',').ToList();
            if (isChecked)
            {
                values.Add(data.DataValue);
                names.Add(data.DisplayName);
            }
            else
            {
                values.Remove(data.DataValue);
                names.Remove(data.DisplayName);
            }
            Text = string.Join(",", names);
            SelectedValues = string.Join(",", values);
        }
    }
}
HMI.UI/Controls/treegrid/Controls/TreeDataGrid.cs
@@ -48,21 +48,36 @@
        {
            if (e.OldValue != null && e.OldValue is TreeItemData oldTreeData)
            {
                oldTreeData.IsExpandedChange -= DataIsExpandedChange;
                oldTreeData.VisibleChange -= DataVisiableChange;
            }
            if (DataContext != null && DataContext is TreeItemData treeData)
            {
                treeData.IsExpandedChange += DataIsExpandedChange;
                treeData.VisibleChange += DataVisiableChange;
            }
        }
        private void DataIsExpandedChange(object sender, bool IsExpanded)
        {
            object parent = ItemsControl.ItemsControlFromItemContainer(this);
            if (sender is TreeItemData tid)
            {
                foreach (var data in tid.Items)
                {
                    data.IsVisible = IsExpanded;
                }
            }
        }
        private void DataVisiableChange(object sender, bool visiable)
        {
            object parent = ItemsControl.ItemsControlFromItemContainer(this);
            if (parent != null && parent is TreeDataGrid treeDataGrid)
            {
                treeDataGrid.OnExpandedChange(visiable);
            }
            this.Visibility = visiable ? Visibility.Visible : Visibility.Collapsed;
            //if (parent != null && parent is TreeDataGrid treeDataGrid)
            //{
            //    treeDataGrid.OnExpandedChange(visiable);
            //}
        }
    }
}
HMI.UI/Controls/treegrid/Controls/TreeItemData.cs
@@ -13,6 +13,7 @@
    public class TreeItemData : NotifyBase
    {
        public event EventHandler<bool> IsExpandedChange;
        public event EventHandler<bool> VisibleChange;
        protected override void Set(string name)
@@ -38,7 +39,7 @@
                _IsExpanded = value;
                Set("IsExpanded");
                SetItemsVisible(value);
                VisibleChange?.Invoke(this, value);
                IsExpandedChange?.Invoke(this, value);
            }
        }
        #endregion
@@ -206,7 +207,7 @@
            {
                return _IsVisible;
            }
            private set
            set
            {
                if (_IsVisible == value)
                {
@@ -215,6 +216,7 @@
                _IsVisible = value;
                Set("IsVisible");
                SetItemsVisible(value);
                VisibleChange?.Invoke(this, value);
            }
        }
HMI.UI/Controls/treegrid/Themes/TreeItemContentControl.xaml
@@ -114,6 +114,8 @@
        <Setter Property="VerticalContentAlignment" Value="Center"/>
        <Setter Property="BorderThickness" Value="0"/>
        <Setter Property="BorderBrush" Value="LightGray"/>
        <Setter Property="Foreground" Value="{Binding Foreground,RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=DataGridCell,AncestorLevel=1}}"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:TreeItemContentControl}">
@@ -156,7 +158,10 @@
                                </ListBox>
                            </Grid>
                            <ToggleButton x:Name="ExpanderButton" Grid.Column="1" Style="{StaticResource ClearToggleStyle}" HorizontalAlignment="Left"
                            <ToggleButton x:Name="ExpanderButton"
                                          Cursor="Hand"
                                          Grid.Column="1" Style="{StaticResource ClearToggleStyle}" HorizontalAlignment="Left"
                                          IsChecked="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=TreeData.IsExpanded,Mode=TwoWay}"
                                          Content="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=TreeData.IconType}"
                                          ContentTemplate="{StaticResource Icon_Template}"
HMI.UI/Controls/treegrid/TreeItemConverter.cs
New file
@@ -0,0 +1,28 @@
using System;
using System.Collections;
using System.Globalization;
using System.Windows.Data;
namespace HMI.UI
{
    public class TreeItemConverter : IValueConverter
    {
        object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null || parameter == null)
            {
                return value;
            }
            if (value is IEnumerable items)
            {
                return new TreeItemDataCollection(items, parameter.ToString(), true);
            }
            return value;
        }
        object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value;
        }
    }
}
HMI.UI/HMI.UI.csproj
@@ -83,6 +83,9 @@
    <Compile Include="Controls\KeyboradWindow.xaml.cs">
      <DependentUpon>KeyboradWindow.xaml</DependentUpon>
    </Compile>
    <Compile Include="Controls\MultiCombox.xaml.cs">
      <DependentUpon>MultiCombox.xaml</DependentUpon>
    </Compile>
    <Compile Include="Controls\NumberPanel.xaml.cs">
      <DependentUpon>NumberPanel.xaml</DependentUpon>
    </Compile>
@@ -90,6 +93,7 @@
    <Compile Include="Controls\treegrid\Controls\TreeGrid.cs" />
    <Compile Include="Controls\treegrid\Controls\TreeItemContentControl.cs" />
    <Compile Include="Controls\treegrid\Controls\TreeItemData.cs" />
    <Compile Include="Controls\treegrid\TreeItemConverter.cs" />
    <Compile Include="Controls\treegrid\TreeNodeExtend.cs" />
    <Compile Include="Converter\VisibilityBoolConverter.cs" />
    <Compile Include="Converter\DataGridRowFlagConverter.cs" />
@@ -127,6 +131,10 @@
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
    </Page>
    <Page Include="Controls\MultiCombox.xaml">
      <SubType>Designer</SubType>
      <Generator>MSBuild:Compile</Generator>
    </Page>
    <Page Include="Controls\NumberPanel.xaml">
      <Generator>MSBuild:Compile</Generator>
      <SubType>Designer</SubType>
HMI.UI/MaskWindow.xaml
@@ -11,15 +11,16 @@
        pu:WindowXCaption.Height="0"
        ResizeMode="NoResize"
        SizeToContent="WidthAndHeight"
        Height="20"
        CanClose="False"
        WindowStyle="None"
        MinWidth="300"
        MinHeight="0"
        Width="Auto"
        x:Name="self"
        WindowStartupLocation="CenterOwner">
    <WindowChrome.WindowChrome>
        <WindowChrome GlassFrameThickness="-1"/>
    </WindowChrome.WindowChrome>
    <Window.Style>
    <!--<Window.Style>
        <Style TargetType="Window">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Status}" Value="Info">
@@ -27,12 +28,50 @@
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Window.Style>
    </Window.Style>-->
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition />
        </Grid.RowDefinitions>
        <Border Padding="10,5,0,5" Background="{StaticResource PrimaryColor}" CornerRadius="5,5,0,0">
            <Label Padding="5" FontSize="14" Margin="0" Foreground="#FFF" Content="{DynamicResource App.PleaseWait}"/>
        </Border>
        <StackPanel Grid.Row="1" Margin="20" Orientation="Horizontal" VerticalAlignment="Center"
                    HorizontalAlignment="Center">
            <TextBlock FontSize="45"
                       FontFamily="{StaticResource iconfont}" Foreground="{StaticResource PrimaryColor}"
                       RenderTransformOrigin="0.5,0.5"
                       VerticalAlignment="Center" Text="&#xe891;">
                <TextBlock.RenderTransform>
                    <RotateTransform x:Name="MyAnimatedTransform" Angle="0" />
                </TextBlock.RenderTransform>
                <TextBlock.Triggers>
                    <EventTrigger RoutedEvent="TextBlock.Loaded">
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation
                                Storyboard.TargetName="MyAnimatedTransform"
                                Storyboard.TargetProperty="(RotateTransform.Angle)"
                                        From="0" To="360" Duration="0:0:3"
                                        RepeatBehavior="Forever"/>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger>
                </TextBlock.Triggers>
            </TextBlock>
            <Label x:Name="label" VerticalContentAlignment="Center" HorizontalContentAlignment="Left"
                   FontSize="20" FontWeight="Bold" Margin="20,0,0,0" />
        </StackPanel>
    </Grid>
    <!--<Grid>
        <Label x:Name="label" 
               Margin="20"
               VerticalContentAlignment="Center"
               HorizontalContentAlignment="Center"
               FontSize="20" FontWeight="Bold" Foreground="White"/>
    </Grid>
    </Grid>-->
</pu:WindowX>
HMI.UI/Style/FormStyle.xaml
@@ -67,6 +67,8 @@
        <Setter Property="pu:TextBoxHelper.FocusedWatermarkForeground" Value="#AAAAAA" />
        <Setter Property="Height" Value="35" />
        <Setter Property="Padding" Value="5,0" />
        <Setter Property="BorderThickness" Value="1" />
        <Setter Property="BorderBrush" Value="Gray" />
        <Setter Property="Background" Value="White" />
        <Setter Property="Foreground" Value="Black" />
        <Setter Property="VerticalContentAlignment" Value="Center"/>
@@ -85,7 +87,6 @@
        <Setter Property="pu:TextBoxHelper.FocusedWatermarkForeground" Value="#AAAAAA" />
        <Setter Property="Height" Value="35" />
        <Setter Property="Padding" Value="5,0" />
        <Setter Property="Background" Value="Transparent" />
        <Setter Property="Foreground" Value="Black" />
        <Setter Property="VerticalContentAlignment" Value="Center"/>
        <Style.Triggers>
@@ -151,6 +152,14 @@
        <Setter Property="HorizontalAlignment" Value="Stretch"/>
        <Setter Property="Foreground" Value="Black" />
        <Setter Property="SelectedValuePath" Value="Content"/>
        <Setter Property="BorderThickness" Value="1" />
        <Setter Property="BorderBrush" Value="Gray" />
        <Style.Triggers>
            <Trigger Property="IsEnabled" Value="False">
                <Setter Property="Background" Value="#e8e8e8"/>
                <Setter Property="Opacity" Value="1"/>
            </Trigger>
        </Style.Triggers>
    </Style>
    <Style x:Key="TimePicker" TargetType="pu:DateTimePicker">
HMI.UI/Style/PanuonResource.xaml
@@ -12,7 +12,7 @@
             xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI">
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="/HMI.UI;component/Style/Colors.xaml"/>
        <pu:StyleDictionary Includes="PasswordBox,TextBox,ComboBox,Button,DataGrid,TreeView,Menu,ListBox" />
        <pu:StyleDictionary Includes="PasswordBox,TextBox,ComboBox,Button,DataGrid,TreeView,Menu,ListBox,ContextMenu" />
        <ResourceDictionary Source="/HMI.UI;component/Resources/Font/Font.xaml"/>
    </ResourceDictionary.MergedDictionaries>
    <sys:Double x:Key="TitleFontSize">20</sys:Double>
@@ -157,7 +157,7 @@
        <Setter Property="BorderBrush" Value="{StaticResource CommonBoarder}"/>
        <Setter Property="BorderThickness" Value="2"/>
        <Setter Property="Background" Value="#f3f9ff"/>
        <Setter Property="Margin" Value="10"/>
        <Setter Property="Margin" Value="5"/>
        <Setter Property="Padding" Value="10"/>
    </Style>
@@ -172,13 +172,13 @@
    </Style>
    <Style x:Key="TableTextBlock" TargetType="TextBlock">
        <Setter Property="Padding" Value="0"/>
        <Setter Property="Padding" Value="0,2"/>
        <Setter Property="Margin" Value="0"/>
        <Setter Property="VerticalAlignment" Value="Center"/>
        <Setter Property="TextWrapping" Value="Wrap"/>
    </Style>
    
    <Style x:Key="DefaultDataGrid" TargetType="DataGrid" BasedOn="{StaticResource {x:Static pu:StyleKeys.DataGridStyle}}">
        <Style.Resources>
            <Style TargetType="DataGridCell" 
                   BasedOn="{StaticResource {x:Static pu:StyleKeys.DataGridCellStyle}}">
                <Setter Property="Background" Value="Transparent"/>
@@ -187,15 +187,20 @@
                <Setter Property="BorderThickness" Value="0"/>
                <Setter Property="VerticalAlignment" Value="Stretch"/>
            </Style>
    <Style x:Key="DefaultDataGrid" TargetType="DataGrid" BasedOn="{StaticResource {x:Static pu:StyleKeys.DataGridStyle}}">
        <Style.Resources>
            <Style TargetType="DataGridColumnHeader" 
       BasedOn="{StaticResource {x:Static pu:StyleKeys.DataGridColumnHeaderStyle}}">
                <Setter Property="Padding" Value="10"/>
                <Setter Property="BorderThickness" Value="0"/>
            </Style>
        </Style.Resources>
        <Setter Property="pu:DataGridHelper.ColumnHeaderPadding" Value="0"/>
        <Setter Property="RowHeight" Value="30"/>
        <Setter Property="ColumnHeaderHeight" Value="36"/>
        <Setter Property="pu:DataGridHelper.ColumnHeaderPadding" Value="0"/>
        <Setter Property="ForceCursor" Value="True"/>
        <Setter Property="SelectionMode" Value="Single"/>
        <Setter Property="RowStyle" Value="{StaticResource DefaultDataGridRow}"/>
@@ -286,13 +291,6 @@
                Value="{DynamicResource App.Save}"/>
        <Setter Property="pu:WindowXModalDialog.ButtonPanelMargin" Value="0,30,10,10"/>
    </Style>
    <Popup x:Key="testPop" Placement="Relative" StaysOpen="True" Width="1920" Height="1080">
        <Rectangle x:Name="RectMask"
           Fill="Red"
           Opacity="0"
           Visibility="Visible" />
    </Popup>
    <Style x:Key="DefaultListBox" TargetType="ListBox" BasedOn="{StaticResource {x:Static pu:StyleKeys.ListBoxStyle}}">
        <Setter Property="pu:ListBoxHelper.ItemsHoverBackground" Value="{StaticResource SecondPrimaryColor}"/>
HMI.UI/Util/MsgBoxHelper.cs
@@ -74,8 +74,11 @@
                owner.Dispatcher.Invoke(() =>
                {
                    window = Window.GetWindow(owner) ?? Application.Current.MainWindow;
                    window.Dispatcher.Invoke(() =>
                    {
                    res = MessageBoxX.Show(window, message, caption, button, icon, defaultButton, setting, countdown);
                });
                });
            }
            else
            {
HMI.UnitTest/MainWindow.xaml.cs
@@ -38,7 +38,8 @@
                for (int j = 0; j < 1000; j++)
                {
                    string status = (j % 2 == 0) ? "Passed" : "Failed";
                    tsExecution.AddTestData(0, $"{j},2,3,4,5,6,{status}");
                    string d = (j % 2 == 0) ? "9" : "9999999999999999999999999999999999999999999999999999999999999999995";
                    tsExecution.AddTestData(0, $"ConnectivityCANRec,2,3,4,{d},6,{status}");
                }
            });
@@ -46,6 +47,7 @@
            tsExecution.UpdatePositionProcessValue(0, 12);
            tsExecution.PausePosition(0);
            tsExecution.SetPositionBarcode(0, "123456789");
            tsExecution.SavePositionCSV(0);
            //tsExecution.UpdatePositionStatus(1, "Failed");
Plugin.JarUI/JarUI.xaml
@@ -21,14 +21,23 @@
            </Grid.RowDefinitions>
            <WindowsFormsHost x:Name="host" Visibility="Collapsed"/>
            <DockPanel>
                <DockPanel.Resources>
                    <Style TargetType="CheckBox">
                        <Setter Property="FontSize" Value="20"/>
                        <Setter Property="VerticalContentAlignment" Value="Center"/>
                    </Style>
                </DockPanel.Resources>
                <StackPanel DockPanel.Dock="Right" Orientation="Horizontal" dp:StackPanelHelper.ChildSpace="20">
                    <Button Style="{StaticResource SuccessButton}" x:Name="startBtn" Click="Button_Click"
                            Content="{DynamicResource App.StopSequence}"/>
                    <Button Style="{StaticResource DangerButton}" x:Name="stopBtn" Click="Button_Click"
                            Content="{DynamicResource App.StartSequence}"/>
                    <Button Style="{StaticResource DangerButton}" x:Name="stopBtn" Click="Button_Click"
                            Content="{DynamicResource App.StopSequence}"/>
                    <Button Style="{StaticResource PrimaryButton}" x:Name="scannerSettingBtn" Click="Button_Click"
                            Content="{DynamicResource App.ScannerSetting}"/>
                </StackPanel>
                <StackPanel Orientation="Horizontal" VerticalAlignment="Center" dp:StackPanelHelper.ChildSpace="20">
                <pu:FormGroup HeaderHeight="Auto" HeaderPadding="0" HorizontalContentAlignment="Left">
                    <pu:FormGroup.Header>
                        <TextBlock FontSize="20" Foreground="#000" Text="{DynamicResource Ts.SelectProductModel}"/>
@@ -38,6 +47,12 @@
                              MaxWidth="200"
SelectedItem="{Binding SelectedProduct}" x:Name="CbProduct"/>
                </pu:FormGroup>
                    <CheckBox Grid.Column="1"
IsChecked="{Binding EnableMes,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Content="{DynamicResource Ts.EnableMes}"
/>
                </StackPanel>
            </DockPanel>
            <UniformGrid Grid.Row="1" x:Name="uGrid" dp:UniformGridHelper.InnerBorder="1" Columns="6">
Plugin.JarUI/JarUI.xaml.cs
@@ -29,8 +29,8 @@
        {
            DataContext = this;
            InitializeComponent();
            InitProducts();
            InitTsEngine();
            InitProducts();
        }
        public override void InitFinish()
@@ -97,8 +97,21 @@
            get { return _selectedProduct; }
            set
            {
                SetTestSeqFile(value);
                Set(ref _selectedProduct, value);
                SetTestSeqFile(value);
            }
        }
        private bool _enableMes;
        public bool EnableMes
        {
            get => _enableMes;
            set
            {
                if (WriteConfig("EnableMes", value))
                {
                    Set(ref _enableMes, value);
                }
            }
        }
@@ -156,17 +169,130 @@
                if (!string.IsNullOrEmpty(lastModel))
                {
                    var product = Products.FirstOrDefault(p => p.Name == lastModel);
                    if (product != null) SelectedProduct = product;
                    goto Write;
                    if (product != null)
                    {
                        SelectedProduct = product;
                        RefershUI();
                        return;
                }
                goto Write;
            Write:
                }
                SelectedProduct = Products[0];
                engine?.Globals.SetValString("StationState.ProductModel", 1, Products[0].Name);
                RefershUI();
            }
        }
        private bool WriteConfig(string name, bool value)
        {
            //IniFileHelper.WriteIniData("Config", name, value.ToString(), CountIniPath);
            try
            {
                if (ParameterManager.UpdateCommonParameter(name, value, out string errMsg))
                {
                    SequenceContext?.StationGlobals.SetValBoolean($"SystemSetting.{name}", 1, value);
                    return true;
                }
                else
                {
                    throw new Exception(errMsg);
                }
            }
            catch (Exception ex)
            {
                MsgBoxHelper2.Error(ex.Message);
                return false;
            }
        }
        /// <summary>
        /// 读取配置文件里的几个属性
        /// </summary>
        /// <param name="pass"></param>
        /// <param name="fail"></param>
        private void ReadConfig()
        {
            ParameterData value;
            try
            {
                value = ParameterManager.GetCommonParameterByName("EnableMes");
                _enableMes = bool.Parse(value.Value);
                Set("EnableMes");
            }
            catch
            {
                _enableMes = false;
                Set("EnableMes");
            }
            //try
            //{
            //    value = ParameterManager.GetCommonParameterByName("EnableNGPause");
            //    _enableNGPause = bool.Parse(value.Value);
            //    Set("EnableNGPause");
            //}
            //catch
            //{
            //    _enableNGPause = false;
            //    Set("EnableNGPause");
            //}
            //try
            //{
            //    value = ParameterManager.GetCommonParameterByName("EnableCanLog");
            //    _enableCanLog = bool.Parse(value.Value);
            //    Set("EnableCanLog");
            //}
            //catch
            //{
            //    _enableCanLog = false;
            //    Set("EnableCanLog");
            //}
            //try
            //{
            //    value = ParameterManager.GetCommonParameterByName("EnableDebugMode");
            //    _enableDebugMode = bool.Parse(value.Value);
            //    Set("EnableDebugMode");
            //}
            //catch
            //{
            //    _enableDebugMode = false;
            //    Set("EnableDebugMode");
            //}
            //try
            //{
            //    value = ParameterManager.GetCommonParameterByName("EnableSimpleMode");
            //    EnableSimpleMode = bool.Parse(value.Value);
            //}
            //catch
            //{
            //    EnableSimpleMode = false;
            //}
            //try
            //{
            //    value = ParameterManager.GetCommonParameterByName("UI_ShowInfoDock");
            //    UI_ShowInfoDock = bool.Parse(value.Value);
            //}
            //catch
            //{
            //    UI_ShowInfoDock = false;
            //}
            //try
            //{
            //    value = ParameterManager.GetCommonParameterByName("TestReportFolder");
            //    csvDir = value.Value;
            //    if (!Directory.Exists(csvDir))
            //    {
            //        Directory.CreateDirectory(csvDir);
            //    }
            //}
            //catch
            //{
            //    csvDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestReport");
            //}
        }
        #region 扫码枪相关
@@ -203,7 +329,10 @@
            catch (Exception ex)
            {
                log.Error("Init Scan Gun Failed.", ex);
                Dispatcher.Invoke(() =>
                {
                MsgBoxHelper.Error(this, $"{this.lang("Tr.InitGunFailed")}: {ex.StackTrace}");
                });
            }
        }
        private string ReadDataFrom(SerialPort sp)
@@ -343,7 +472,10 @@
                    catch (Exception ex)
                    {
                        log.Error("TsExecution Loaded Failed.", ex);
                        Dispatcher.Invoke(() =>
                        {
                        MsgBoxHelper.Error(this, $"{LangUtil.Get("Ts.UserControlLoadFailed")}: {ex.Message}");
                        });
                    }
                    Dispatcher.Invoke(() =>
                    {
@@ -362,6 +494,8 @@
            try
            {
                if (!isLoadOk) return;
                engine?.Globals.SetValString("StationState.ProductModel", 1, SelectedProduct?.Name);
                engine?.CommitGlobalsToDisk(promptOnSaveConflicts: false);
                this.modeSeq = this.engine.GetSequenceFileEx(@"C:\Program Files\National Instruments\TestStand 2020\Components\Models\TestStandModels\ParallelModel.seq");
                this.testSeq = this.engine.GetSequenceFileEx(this.testSeqFile, 0, TypeConflictHandlerTypes.ConflictHandler_UseGlobalType);
                this.execution = this.engine.NewExecution(this.testSeq, "Test UUTs", this.modeSeq, false, 0, null, null, null);
Plugin.SeqEditor/AssemblyInfo.cs
New file
@@ -0,0 +1,10 @@
using System.Windows;
[assembly:ThemeInfo(
    ResourceDictionaryLocation.None,            //where theme specific resource dictionaries are located
                                                //(used if a resource is not found in the page,
                                                // or application resource dictionaries)
    ResourceDictionaryLocation.SourceAssembly   //where the generic resource dictionary is located
                                                //(used if a resource is not found in the page,
                                                // app, or any theme specific resource dictionaries)
)]
Plugin.SeqEditor/Constant/DefaultStepName.cs
New file
@@ -0,0 +1,12 @@
namespace Plugin.SeqEditor.Constant
{
    public abstract class DefaultStepName
    {
        public const string PassFailTest = "Pass/Fail Test";
        public const string NumericLimitTest = "Numeric Limit Test";
        public const string NI_MultipleNumericLimitTest = "Multiple Numeric Limit Test";
        public const string StringValueTest = "String Value Test";
        public const string Action = "Action";
    }
}
Plugin.SeqEditor/Constant/StepTypeStr.cs
New file
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Plugin.SeqEditor.Constant
{
    public abstract class StepTypeStr
    {
        public const string PassFailTest = "PassFailTest";
        public const string NumericLimitTest = "NumericLimitTest";
        public const string NI_MultipleNumericLimitTest = "NI_MultipleNumericLimitTest";
        public const string StringValueTest = "StringValueTest";
        public const string NI_Wait = "NI_Wait";
        public const string Action = "Action";
        public const string SequenceCall = "SequenceCall";
    }
}
Plugin.SeqEditor/Entity/EditablePO/IEPO.cs
New file
@@ -0,0 +1,46 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace Plugin.SeqEditor.Entity.EditablePO
{
    public class BaseEPO : ObservableObject
    {
        public StepUI StepUI { get; protected set; }
        public string StepName
        {
            get => StepUI?.Name;
            set
            {
                if (StepUI != null)
                {
                    StepUI.Name = value;
                    OnPropertyChanged(nameof(StepName));
                }
            }
        }
        public string StepType
        {
            get => StepUI?.Type;
        }
        public string RunMode
        {
            get => StepUI?.RunMode;
            set
            {
                if (StepUI != null)
                {
                    StepUI.RunMode = value;
                    OnPropertyChanged(nameof(RunMode));
                }
            }
        }
        public virtual void CustomPropertyChanged(string name)
        {
            OnPropertyChanged(name);
            //StepUI?.UpdatePropertyChanged("Description");
        }
    }
}
Plugin.SeqEditor/Entity/EditablePO/NumValTestEPO.cs
New file
@@ -0,0 +1,57 @@
using CommunityToolkit.Mvvm.ComponentModel;
using NationalInstruments.TestStand.Interop.API;
namespace Plugin.SeqEditor.Entity.EditablePO
{
    public class NumValTestEPO : BaseEPO
    {
        public Step Step { get; private set; }
        public PropertyObject PropertyObject { get; private set; }
        public NumValTestEPO(StepUI stepUI)
        {
            StepUI = stepUI;
            Step = stepUI?.step;
            PropertyObject = Step?.AsPropertyObject();
        }
        public string ComparisonType
        {
            get => PropertyObject?.GetValString("Comp", 0);
            set
            {
                if (PropertyObject != null)
                {
                    PropertyObject.SetValString("Comp", 0, value);
                    CustomPropertyChanged(nameof(ComparisonType));
                }
            }
        }
        public double? Low
        {
            get => PropertyObject?.GetValNumber("Limits.Low", 0);
            set
            {
                if (PropertyObject != null)
                {
                    PropertyObject.SetValNumber("Limits.Low", 0, value ?? 0);
                    CustomPropertyChanged(nameof(Low));
                }
            }
        }
        public double? High
        {
            get => PropertyObject?.GetValNumber("Limits.High", 0);
            set
            {
                if (PropertyObject != null)
                {
                    PropertyObject.SetValNumber("Limits.High", 0, value ?? 0);
                    CustomPropertyChanged(nameof(Low));
                }
            }
        }
    }
}
Plugin.SeqEditor/Entity/EditablePO/StringValTestEPO.cs
New file
@@ -0,0 +1,66 @@
using CommunityToolkit.Mvvm.ComponentModel;
using NationalInstruments.TestStand.Interop.API;
namespace Plugin.SeqEditor.Entity.EditablePO
{
    public class StringValTestEPO : BaseEPO
    {
        public Step Step { get; private set; }
        public PropertyObject PropertyObject { get; private set; }
        public StringValTestEPO(StepUI stepUI)
        {
            StepUI = stepUI;
            Step = StepUI?.step;
            PropertyObject = Step?.AsPropertyObject();
        }
        public string ComparisonType
        {
            get => PropertyObject?.GetValString("Comp", 0);
            set
            {
                if (PropertyObject != null)
                {
                    PropertyObject.SetValString("Comp", 0, value);
                    CustomPropertyChanged(nameof(ComparisonType));
                }
            }
        }
        public bool IsReadOnly
        {
            get
            {
                if (PropertyObject == null) return false;
                bool flag = PropertyObject.GetValBoolean("Limits.UseStringExpr", 0);
                return flag == true;
            }
        }
        public string StringValue
        {
            get
            {
                if (PropertyObject == null) return null;
                var value = PropertyObject.GetValString("Limits.String", 0);
                if (string.IsNullOrEmpty(value))
                {
                    value = PropertyObject.GetValString("Limits.StringExpr", 0);
                }
                return value;
            }
            set
            {
                if (PropertyObject != null)
                {
                    PropertyObject.SetValString("Limits.String", 0, value);
                    PropertyObject.SetValString("Limits.StringExpr", 0, null);
                    PropertyObject.SetValBoolean("Limits.UseStringExpr", 0, false);
                    CustomPropertyChanged(nameof(StringValue));
                }
            }
        }
    }
}
Plugin.SeqEditor/Entity/EditablePO/WaitEPO.cs
New file
@@ -0,0 +1,37 @@
using NationalInstruments.TestStand.Interop.API;
namespace Plugin.SeqEditor.Entity.EditablePO
{
    public class WaitEPO : BaseEPO
    {
        public Step Step { get; private set; }
        public PropertyObject PropertyObject { get; private set; }
        public WaitEPO(StepUI stepUI)
        {
            StepUI = stepUI;
            Step = StepUI?.step;
            PropertyObject = Step?.AsPropertyObject();
        }
        public string TimeInterval
        {
            get
            {
                //var str = PropertyObject?.GetValString("TimeExpr", 0);
                //if (string.IsNullOrEmpty(str)) return null;
                //double.TryParse(str, out double value);
                //return value;
                return PropertyObject?.GetValString("TimeExpr", 0);
            }
            set
            {
                if (PropertyObject != null)
                {
                    PropertyObject.SetValString("TimeExpr", 0, value?.ToString());
                    CustomPropertyChanged(nameof(TimeInterval));
                }
            }
        }
    }
}
Plugin.SeqEditor/Entity/SeqUI.cs
New file
@@ -0,0 +1,27 @@
using CommunityToolkit.Mvvm.ComponentModel;
using NationalInstruments.TestStand.Interop.API;
namespace Plugin.SeqEditor.Entity
{
    public partial class SeqUI : ObservableObject
    {
        public Sequence sequence { get; private set; }
        public SeqUI(Sequence sequence)
        {
            this.sequence = sequence;
        }
        public string Name
        {
            get => sequence?.Name;
            set
            {
                if (sequence != null)
                {
                    sequence.Name = value;
                    OnPropertyChanged(Name);
                }
            }
        }
    }
}
Plugin.SeqEditor/Entity/StepUI.cs
New file
@@ -0,0 +1,91 @@
using CommunityToolkit.Mvvm.ComponentModel;
using NationalInstruments.TestStand.Interop.API;
using System.Collections.ObjectModel;
namespace Plugin.SeqEditor.Entity
{
    public partial class StepUI : ObservableObject
    {
        public Step step { get; private set; }
        public StepUI(Step step)
        {
            this.step = step;
        }
        public void UpdatePropertyChanged(string name)
        {
            OnPropertyChanged(name);
        }
        public string Name
        {
            get => step?.Name;
            set
            {
                if (step != null)
                {
                    step.Name = value;
                    OnPropertyChanged(nameof(Name));
                }
            }
        }
        public string Description
        {
            get => step?.Description;
        }
        public string RunMode
        {
            get => step?.GetRunModeEx();
            set
            {
                if (step != null)
                {
                    step.SetRunModeEx(value);
                    OnPropertyChanged(nameof(RunMode));
                }
            }
        }
        public string Type
        {
            get => step?.StepType?.Name;
        }
        public bool CanChangeAdapter
        {
            get => step?.CanChangeAdapter() ?? true;
        }
        public string Adapter
        {
            get => step?.AdapterKeyName;
            set
            {
                if (step != null)
                {
                    step.ChangeAdapter(value);
                    OnPropertyChanged(nameof(RunMode));
                }
            }
        }
        public StepGroups? StepGroup
        {
            get
            {
                return step?.StepGroup;
            }
        }
        [ObservableProperty]
        private string group;
        [ObservableProperty]
        public ObservableCollection<StepUI> children = new();
    }
}
Plugin.SeqEditor/Entity/VarUI.cs
New file
@@ -0,0 +1,58 @@
using CommunityToolkit.Mvvm.ComponentModel;
using NationalInstruments.TestStand.Interop.API;
using Plugin.SeqEditor.Util;
using System.Collections.ObjectModel;
namespace Plugin.SeqEditor.Entity
{
    public partial class VarUI : ObservableObject
    {
        private string prefix;
        private PropertyObject propertyObject;
        public VarUI(PropertyObject po)
        {
            this.propertyObject = po;
        }
        [ObservableProperty]
        private ObservableCollection<VarUI> subProperties = new ObservableCollection<VarUI>();
        public string Name
        {
            get => propertyObject?.Name;
            set
            {
                if (propertyObject != null)
                {
                    propertyObject.Name = value;
                    OnPropertyChanged(Name);
                }
            }
        }
        public string Value
        {
            get => propertyObject?.GetValueString();
            set
            {
                propertyObject?.SetValueString(value);
            }
        }
        public PropertyValueTypes? Type
        {
            get => propertyObject?.Type?.ValueType;
        }
        public PropertyValueTypes? ElementType
        {
            get => propertyObject?.Type?.ElementType?.ValueType;
        }
        public string DisplayString
        {
            get => propertyObject?.Type.DisplayString;
        }
    }
}
Plugin.SeqEditor/Plugin.SeqEditor.csproj
New file
@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFramework>net472</TargetFramework>
        <Nullable>enable</Nullable>
        <LangVersion>10.0</LangVersion>
        <ImplicitUsings>enable</ImplicitUsings>
        <UseWPF>true</UseWPF>
        <Platforms>AnyCPU;x64</Platforms>
    </PropertyGroup>
    <ItemGroup>
      <PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
      <PackageReference Include="log4net" Version="3.0.4" />
      <PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.135" />
      <PackageReference Include="Panuon.WPF.UI" Version="1.3.0.2" />
    </ItemGroup>
    <ItemGroup>
      <ProjectReference Include="..\HMI.Base\HMI.Base.csproj" />
      <ProjectReference Include="..\HMI.Core\HMI.Core.csproj" />
      <ProjectReference Include="..\HMI.UI\HMI.UI.csproj" />
      <ProjectReference Include="..\HMI.Util\HMI.Util.csproj" />
    </ItemGroup>
    <ItemGroup>
      <Reference Include="NationalInstruments.TestStand.Interop.AdapterAPI">
        <HintPath>..\..\..\..\Program Files\National Instruments\TestStand 2020\API\DotNET\Assemblies\CurrentVersion\NationalInstruments.TestStand.Interop.AdapterAPI.dll</HintPath>
      </Reference>
      <Reference Include="NationalInstruments.TestStand.Interop.API">
        <HintPath>..\..\..\..\Program Files\National Instruments\TestStand 2020\API\DotNET\Assemblies\CurrentVersion\NationalInstruments.TestStand.Interop.API.dll</HintPath>
      </Reference>
      <Reference Include="NationalInstruments.TestStand.Interop.UI.Support">
        <HintPath>..\..\..\..\Program Files\National Instruments\TestStand 2020\API\DotNET\Assemblies\CurrentVersion\NationalInstruments.TestStand.Interop.UI.Support.dll</HintPath>
      </Reference>
    </ItemGroup>
</Project>
Plugin.SeqEditor/SeqEditor.xaml
New file
@@ -0,0 +1,254 @@
<hb:PluginControl x:Class="Plugin.SeqEditor.SeqEditor"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:Plugin.SeqEditor"
             mc:Ignorable="d"
             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
xmlns:pw="https://opensource.panuon.com/wpf"
xmlns:pu="https://opensource.panuon.com/wpf-ui"
xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
xmlns:util="clr-namespace:Plugin.SeqEditor.Util"
        xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
             d:DesignHeight="450" d:DesignWidth="800">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <i:InvokeCommandAction Command="{Binding LoadedCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/HMI.UI;component/Controls/TreeGrid/Themes/TreeItemContentControl.xaml"/>
                <ResourceDictionary Source="/HMI.UI;component/Controls/TreeGrid/Themes/TreeGrid.xaml"/>
            </ResourceDictionary.MergedDictionaries>
            <Style TargetType="pu:FormGroup">
                <Setter Property="Padding" Value="0,10"/>
                <Setter Property="HeaderFontSize" Value="14"/>
                <Setter Property="HeaderFontWeight" Value="Bold"/>
            </Style>
            <!-- 树Grid组件 -->
            <ui:TreeItemDataConverter x:Key="TreeItemDataConverter"/>
            <CollectionViewSource x:Key="TreeGridFilter"
                                  Source="{Binding AllSteps,Converter={StaticResource TreeItemDataConverter},ConverterParameter=Children}">
                <CollectionViewSource.GroupDescriptions>
                    <PropertyGroupDescription PropertyName="Data.Group" />
                </CollectionViewSource.GroupDescriptions>
            </CollectionViewSource>
        </ResourceDictionary>
    </UserControl.Resources>
    <Border Style="{StaticResource CardBoarder}" Padding="0">
        <Grid dp:GridHelper.Space="10" Cursor="">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="0"/>
                <ColumnDefinition Width="8*"/>
                <ColumnDefinition Width="2*"/>
            </Grid.ColumnDefinitions>
            <DockPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Margin="0,0,0,10">
                <StackPanel DockPanel.Dock="Right"  Orientation="Horizontal"
        dp:StackPanelHelper.ChildSpace="10">
                    <Button Style="{StaticResource AddBtn}" Content="{DynamicResource se.open}" Command="{Binding OpenSeqFileCommand}"/>
                    <Button Style="{StaticResource SaveBtn}" Content="{DynamicResource se.save}" Command="{Binding SaveSeqFileCommand}"/>
                    <Button Style="{StaticResource SaveBtn}" pu:ButtonHelper.Icon="&#xe60b;"
                            Content="{DynamicResource se.saveTo}"
                            Command="{Binding SaveSeqFileCommand}"/>
                    <Button Style="{StaticResource DelBtn}" Content="{DynamicResource se.close}"
                            pu:ButtonHelper.Icon="&#xe609;"
                            Command="{Binding CloseSeqFileCommand}"/>
                </StackPanel>
                <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource TitleFontSize}"
           VerticalAlignment="Center" HorizontalAlignment="Left" Text="{DynamicResource se.title}"/>
                <TextBlock DockPanel.Dock="Left" FontSize="{StaticResource ContentFontSize2}"
                           Margin="20,0,0,0"
VerticalAlignment="Bottom" LineHeight="{StaticResource TitleFontSize}" HorizontalAlignment="Left" Text="{Binding CurrentFileName}"/>
            </DockPanel>
            <!--<pu:FormGroup Header="{DynamicResource se.seq}" Orientation="Vertical" Grid.Row="1" Grid.Column="0">
                <DataGrid ItemsSource="{Binding Sequences}" SelectedItem="{Binding CurrentSeqUI}">
                    <DataGrid.Columns>
                        <DataGridTextColumn Width="*" Binding="{Binding Name}">
                            <DataGridTextColumn.Header>
                                <TextBlock Text="{DynamicResource se.name}"/>
                            </DataGridTextColumn.Header>
                        </DataGridTextColumn>
                    </DataGrid.Columns>
                </DataGrid>
            </pu:FormGroup>-->
            <pu:FormGroup Grid.Row="1" Grid.Column="1" Header="步骤类型" Style="{StaticResource InputFormGroupStyle}">
                <StackPanel Orientation="Horizontal">
                    <!--<ComboBox Text="{Binding TargetType}"
                              MinWidth="180" Margin="0,0,10,0" x:Name="CbFilter"
                        SelectedValuePath="DataValue" IsEditable="True" >
                        <ComboBox.ItemTemplate>
                            <DataTemplate DataType="util:ItemData">
                                <StackPanel Orientation="Horizontal">
                                    <CheckBox IsChecked="{Binding IsChecked}" />
                                    <TextBlock Text="{Binding DisplayName}" />
                                </StackPanel>
                            </DataTemplate>
                        </ComboBox.ItemTemplate>
                    </ComboBox>-->
                    <ui:MultiComboBox SelectedValues="{Binding TargetType}"
                                      Style="{StaticResource FormComboxStyle}"
                                      pu:ComboBoxHelper.ClearButtonVisibility="Collapsed"
                                      MinWidth="180" Margin="0,0,10,0" x:Name="CbFilter" IsEditable="True" IsReadOnly="True" >
                        <ui:MultiComboBox.ItemTemplate>
                            <DataTemplate DataType="util:ItemData">
                                <StackPanel Orientation="Horizontal">
                                    <CheckBox IsChecked="{Binding IsChecked,UpdateSourceTrigger=PropertyChanged}" />
                                    <TextBlock Text="{Binding DisplayName}" />
                                </StackPanel>
                            </DataTemplate>
                        </ui:MultiComboBox.ItemTemplate>
                    </ui:MultiComboBox>
                    <!--<ComboBox Style="{StaticResource FormComboxStyle}" MinWidth="180" Margin="0,0,10,0" x:Name="CbFilter"/>-->
                    <Button Style="{StaticResource RfhBtn}" Command="{Binding RefreshCommand}"/>
                </StackPanel>
            </pu:FormGroup>
            <pu:FormGroup Header="{Binding CurrentSeqTitle}" Orientation="Vertical" Grid.Row="2" Grid.Column="1">
                <!--<DataGrid ItemsSource="{Binding Source={StaticResource cvs}}"
                          RowStyle="{StaticResource StepRow}"
                          SelectedItem="{Binding CurrentStepUI}">
                    <DataGrid.GroupStyle>
                        <GroupStyle>
                            <GroupStyle.Panel>
                                <ItemsPanelTemplate>
                                    <DataGridRowsPresenter/>
                                </ItemsPanelTemplate>
                            </GroupStyle.Panel>
                            <GroupStyle.ContainerStyle>
                                <Style TargetType="{x:Type GroupItem}" BasedOn="{StaticResource DataGridGroupStyle}"/>
                            </GroupStyle.ContainerStyle>
                        </GroupStyle>
                    </DataGrid.GroupStyle>
                    <DataGrid.Columns>
                        <DataGridTextColumn Width="2*" Binding="{Binding Name}">
                            <DataGridTextColumn.Header>
                                <TextBlock Text="{DynamicResource se.name}"/>
                            </DataGridTextColumn.Header>
                        </DataGridTextColumn>
                        <DataGridTextColumn Width="1*" Binding="{Binding Type}">
                            <DataGridTextColumn.Header>
                                <TextBlock Text="{DynamicResource se.name}"/>
                            </DataGridTextColumn.Header>
                        </DataGridTextColumn>
                        <DataGridTextColumn Width="6*" Binding="{Binding Description}">
                            <DataGridTextColumn.Header>
                                <TextBlock Text="{DynamicResource se.description}"/>
                            </DataGridTextColumn.Header>
                        </DataGridTextColumn>
                        <DataGridTemplateColumn Width="1*">
                            <DataGridTemplateColumn.Header>
                                <TextBlock Text="{DynamicResource se.runMode}"/>
                            </DataGridTemplateColumn.Header>
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <Grid x:Name="border" Margin="0">
                                        <TextBlock x:Name="text" Margin="-1" Padding="0" VerticalAlignment="Center"/>
                                    </Grid>
                                    <DataTemplate.Triggers>
                                        <DataTrigger Binding="{Binding RunMode}" Value="Normal">
                                            <Setter TargetName="text" Property="Text" Value="{DynamicResource se.normal}"/>
                                        </DataTrigger>
                                        <DataTrigger Binding="{Binding RunMode}" Value="Skip">
                                            <Setter TargetName="text" Property="Text" Value="{DynamicResource se.skip}"/>
                                        </DataTrigger>
                                        <DataTrigger Binding="{Binding RunMode}" Value="Pass">
                                            <Setter TargetName="text" Property="Text" Value="{DynamicResource se.pass}"/>
                                        </DataTrigger>
                                        <DataTrigger Binding="{Binding RunMode}" Value="Fail">
                                            <Setter TargetName="text" Property="Text" Value="{DynamicResource se.fail}"/>
                                        </DataTrigger>
                                    </DataTemplate.Triggers>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                    </DataGrid.Columns>
                                 ItemsSource="{Binding AllSteps,Converter={StaticResource treeC}}"
                </DataGrid>-->
                <ui:TreeDataGrid Grid.Row="1" x:Name="treeDataGrid"
                                 VirtualizingPanel.IsContainerVirtualizable="True"
                                VirtualizingPanel.IsVirtualizing="True"
                                VirtualizingPanel.VirtualizationMode="Recycling"
                                ScrollViewer.CanContentScroll="False"
                                 VirtualizingPanel.IsVirtualizingWhenGrouping="True"
                                EnableColumnVirtualization="True"
                                EnableRowVirtualization="True"
                                 PreviewMouseWheel="treeDataGrid_PreviewMouseWheel"
                                 AutoGenerateColumns="False"
                                 IsReadOnly="True"
                                 Style="{StaticResource DefaultDataGrid}"
                ItemsSource="{Binding Source={StaticResource TreeGridFilter}}"
                                 SelectedItem="{Binding TreeItemData}">
                    <!--<DataGrid.GroupStyle>
                        <GroupStyle>
                            <GroupStyle.ContainerStyle>
                                <Style TargetType="{x:Type GroupItem}">
                                    <Setter Property="Margin" Value="0,0,0,5" />
                                    <Setter Property="Template">
                                        <Setter.Value>
                                            <ControlTemplate TargetType="{x:Type GroupItem}">
                                                <GroupBox Header="{Binding Name}">
                                                    <ItemsPresenter />
                                                </GroupBox>
                                            </ControlTemplate>
                                        </Setter.Value>
                                    </Setter>
                                </Style>
                            </GroupStyle.ContainerStyle>
                        </GroupStyle>
                    </DataGrid.GroupStyle>-->
                    <ui:TreeDataGrid.Columns>
                        <DataGridTemplateColumn IsReadOnly="True" Width="Auto">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <ui:TreeItemContentControl IconLineStroke="Black" TreeData="{Binding }"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTextColumn Header="名称" Width="1*" Binding="{Binding Data.Name}"></DataGridTextColumn>
                        <DataGridTextColumn Header="类型" Width="1*" Binding="{Binding Data.Type}"></DataGridTextColumn>
                        <DataGridTextColumn Header="描述" Width="7*" Binding="{Binding  Data.Description}"></DataGridTextColumn>
                        <DataGridTextColumn Header="RunMode" Width="*" Binding="{Binding Data.RunMode}"></DataGridTextColumn>
                    </ui:TreeDataGrid.Columns>
                </ui:TreeDataGrid>
            </pu:FormGroup>
            <pu:FormGroup Header="{DynamicResource se.propertySetting}" Orientation="Vertical"
                          Grid.Row="2" Padding="0,10" Grid.Column="2">
                <Border x:Name="editPanelContainer" Background="#FFF">
                </Border>
            </pu:FormGroup>
        </Grid>
    </Border>
</hb:PluginControl>
Plugin.SeqEditor/SeqEditor.xaml.cs
New file
@@ -0,0 +1,70 @@
using HMI.Base;
using HMI.UI;
using Panuon.WPF.UI;
using Plugin.SeqEditor.Util;
using Plugin.SeqEditor.ViewModel;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
namespace Plugin.SeqEditor;
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class SeqEditor : PluginControl
{
    public SeqEditor()
    {
        InitializeComponent();
        CbFilter.ItemsSource = (IEnumerable<MultiComboBoxData>)
            new MultiComboBoxDataConverter().Convert(FilterTypeData.ItemSource,
            null,null,null
            );
    }
    public override void InitFinish()
    {
        this.DataContext = new SeqEditorViewModel(this, log);
    }
    public void BackgroundAction(Action action, string message)
    {
        new MaskWindow((WindowX)Window.GetWindow(this),
                  (win) =>
                  {
                      action?.Invoke();
                  }
                  ).Info(message);
    }
    /// <summary>
    /// 树数据展示过滤
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void CollectionViewSource_Filter(object sender, FilterEventArgs e)
    {
        if (e.Item is TreeItemData data)
        {
            e.Accepted = data.IsVisible;
        }
    }
    // <summary>
    /// 树展开/关闭事件
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void treeDataGrid_ExpandedChange(object sender, bool e)
    {
        //CollectionViewSource cvs = this.Resources["TreeGridFilter"] as CollectionViewSource;
        //cvs?.View.Refresh();
    }
    private void treeDataGrid_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
    {
        e.Handled = false;
    }
}
Plugin.SeqEditor/SettingPanel/ActionPanel.xaml
New file
@@ -0,0 +1,45 @@
<UserControl x:Class="Plugin.SeqEditor.SettingPanel.ActionPanel"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:Plugin.SeqEditor.SettingPanel"
             mc:Ignorable="d"
             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
             xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
             xmlns:pw="https://opensource.panuon.com/wpf"
             xmlns:pu="https://opensource.panuon.com/wpf-ui"
             xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
             Padding="5"
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <StackPanel>
            <pu:FormGroup Header="{DynamicResource se.stepName}" Orientation="Vertical"
                          Style="{StaticResource InputFormGroupStyle}">
                <TextBox IsReadOnly="True" Style="{StaticResource EditorTextBoxStyle}" Text="{Binding Name}" />
            </pu:FormGroup>
            <pu:FormGroup Header="{DynamicResource se.stepType}" Orientation="Vertical"
                          Style="{StaticResource InputFormGroupStyle}">
                <TextBox IsReadOnly="True" Style="{StaticResource EditorTextBoxStyle}" Text="{Binding Type,Mode=OneWay}" />
            </pu:FormGroup>
            <pu:FormGroup Header="{DynamicResource se.runMode}" Orientation="Vertical"
                          Style="{StaticResource InputFormGroupStyle}">
                <ComboBox x:Name="CbRunMode" Style="{StaticResource FormComboxStyle}"
                          VirtualizingPanel.IsVirtualizing="True"
                SelectedValue="{Binding RunMode}"
            DisplayMemberPath="DisplayName" SelectedValuePath="DataValue"/>
            </pu:FormGroup>
            <!--<pu:FormGroup Header="{DynamicResource se.adapter}"
        Orientation="Vertical"
        Style="{StaticResource InputFormGroupStyle}">
                <ComboBox x:Name="CbAdapter" Style="{StaticResource FormComboxStyle}" IsEnabled="False" SelectedValue="{Binding Adapter}"
                          DisplayMemberPath="DisplayName" SelectedValuePath="DataValue"/>
            </pu:FormGroup>-->
        </StackPanel>
    </Grid>
</UserControl>
Plugin.SeqEditor/SettingPanel/ActionPanel.xaml.cs
New file
@@ -0,0 +1,39 @@
using CommunityToolkit.Mvvm.ComponentModel;
using NationalInstruments.TestStand.Interop.API;
using Plugin.SeqEditor.Entity;
using Plugin.SeqEditor.Util;
using System.ComponentModel;
using System.Windows.Controls;
namespace Plugin.SeqEditor.SettingPanel
{
    /// <summary>
    /// CommonProperty.xaml 的交互逻辑
    /// </summary>
    [INotifyPropertyChanged]
    public partial class ActionPanel : UserControl
    {
        public event EventHandler SettingChanged;
        public StepUI stepUI;
        public Step step;
        public PropertyObject propertyObject;
        protected void CustomPropertyChanged(string name)
        {
            OnPropertyChanged(name);
            SettingChanged?.Invoke(this, EventArgs.Empty);
        }
        public ActionPanel(StepUI stepUI)
        {
            this.stepUI = stepUI;
            this.step = stepUI.step;
            propertyObject = this.step?.AsPropertyObject();
            this.DataContext = stepUI;
            InitializeComponent();
            CbRunMode.ItemsSource = RunModeData.ItemSource;
            //CbAdapter.ItemsSource = StepAdapterData.ItemSource;
        }
    }
}
Plugin.SeqEditor/SettingPanel/CommonProperty.xaml
New file
@@ -0,0 +1,25 @@
<UserControl x:Class="Plugin.SeqEditor.SettingPanel.CommonProperty"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:Plugin.SeqEditor.SettingPanel"
             mc:Ignorable="d"
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <StackPanel>
            <Label>Name</Label>
            <TextBox Text="{Binding Name}"/>
            <Label>Type</Label>
            <TextBox IsEnabled="False" Text="{Binding Type,Mode=OneWay}"/>
            <Label>Adapter</Label>
            <ComboBox x:Name="CbAdapter"
                      IsEnabled="{Binding CanChangeAdapter}"
                      SelectedValue="{Binding Adapter}"
                      DisplayMemberPath="DisplayName" SelectedValuePath="DataValue"/>
        </StackPanel>
    </Grid>
</UserControl>
Plugin.SeqEditor/SettingPanel/CommonProperty.xaml.cs
New file
@@ -0,0 +1,45 @@
using CommunityToolkit.Mvvm.ComponentModel;
using NationalInstruments.TestStand.Interop.API;
using System.ComponentModel;
using System.Windows.Controls;
using Plugin.SeqEditor.Constant;
using Plugin.SeqEditor.Entity;
using Plugin.SeqEditor.Util;
namespace Plugin.SeqEditor.SettingPanel
{
    /// <summary>
    /// CommonProperty.xaml 的交互逻辑
    /// </summary>
    [INotifyPropertyChanged]
    public partial class CommonProperty : UserControl
    {
        public event EventHandler SettingChanged;
        public StepUI stepUI;
        public Step step;
        public PropertyObject propertyObject;
        protected void CustomPropertyChanged(string name)
        {
            OnPropertyChanged(name);
            SettingChanged?.Invoke(this, EventArgs.Empty);
        }
        public CommonProperty()
        {
            DataContext = this;
            InitializeComponent();
            CbAdapter.ItemsSource = StepAdapterData.ItemSource;
        }
        public void RefreshStep(StepUI stepUI)
        {
            this.stepUI = stepUI;
            this.step = stepUI.step;
            propertyObject = this.step?.AsPropertyObject();
            this.DataContext = stepUI;
        }
    }
}
Plugin.SeqEditor/SettingPanel/NumValTestPanel.xaml
New file
@@ -0,0 +1,50 @@
<UserControl x:Class="Plugin.SeqEditor.SettingPanel.NumValTestPanel"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:Plugin.SeqEditor.SettingPanel"
             mc:Ignorable="d"
             Padding="5"
             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
             xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
             xmlns:pw="https://opensource.panuon.com/wpf"
             xmlns:pu="https://opensource.panuon.com/wpf-ui"
             xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
             d:DesignHeight="450" d:DesignWidth="800">
    <StackPanel>
        <pu:FormGroup Header="{DynamicResource se.runMode}"
       Orientation="Vertical"
       Style="{StaticResource InputFormGroupStyle}">
            <ComboBox x:Name="CbRunMode"
       Style="{StaticResource FormComboxStyle}"
       SelectedValue="{Binding RunMode}"
   DisplayMemberPath="DisplayName" SelectedValuePath="DataValue"/>
        </pu:FormGroup>
        <pu:FormGroup Header="{DynamicResource se.compType}"
       Orientation="Vertical"
       Style="{StaticResource InputFormGroupStyle}">
            <ComboBox x:Name="CbType"
       Style="{StaticResource FormComboxStyle}" SelectedValue="{Binding ComparisonType}"
          DisplayMemberPath="DisplayName"
          SelectedValuePath="DataValue"/>
        </pu:FormGroup>
        <pu:FormGroup Header="Low"
Orientation="Vertical"
Style="{StaticResource InputFormGroupStyle}">
            <ui:NumberInput Style="{StaticResource EditorTextBoxStyle}" Text="{Binding Low}"/>
        </pu:FormGroup>
        <pu:FormGroup Header="High"
Orientation="Vertical"
Style="{StaticResource InputFormGroupStyle}">
            <ui:NumberInput Style="{StaticResource EditorTextBoxStyle}" Text="{Binding High}"/>
        </pu:FormGroup>
    </StackPanel>
</UserControl>
Plugin.SeqEditor/SettingPanel/NumValTestPanel.xaml.cs
New file
@@ -0,0 +1,20 @@
using Plugin.SeqEditor.Entity.EditablePO;
using Plugin.SeqEditor.Util;
using System.Windows.Controls;
namespace Plugin.SeqEditor.SettingPanel
{
    /// <summary>
    /// NumValTestPanel.xaml 的交互逻辑
    /// </summary>
    public partial class NumValTestPanel : UserControl
    {
        public NumValTestPanel(NumValTestEPO epo)
        {
            DataContext = epo;
            InitializeComponent();
            CbRunMode.ItemsSource = RunModeData.ItemSource;
            CbType.ItemsSource = NumCompTypeData.ItemSource;
        }
    }
}
Plugin.SeqEditor/SettingPanel/StringValTestPanel.xaml
New file
@@ -0,0 +1,57 @@
<UserControl x:Class="Plugin.SeqEditor.SettingPanel.StringValTestPanel"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:Plugin.SeqEditor.SettingPanel"
             mc:Ignorable="d"
             Padding="5"
             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
             xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
             xmlns:pw="https://opensource.panuon.com/wpf"
             xmlns:pu="https://opensource.panuon.com/wpf-ui"
             xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
             d:DesignHeight="450" d:DesignWidth="800">
    <StackPanel>
        <pu:FormGroup Header="{DynamicResource se.stepName}"
        Orientation="Vertical"
        Style="{StaticResource InputFormGroupStyle}">
            <TextBox IsReadOnly="True" Style="{StaticResource EditorTextBoxStyle}" Text="{Binding StepName}" />
        </pu:FormGroup>
        <pu:FormGroup Header="{DynamicResource se.stepType}"
Orientation="Vertical"
Style="{StaticResource InputFormGroupStyle}">
            <TextBox IsReadOnly="True" Style="{StaticResource EditorTextBoxStyle}" Text="{Binding StepType,Mode=OneWay}" />
        </pu:FormGroup>
        <pu:FormGroup Header="{DynamicResource se.runMode}"
               Orientation="Vertical"
               Style="{StaticResource InputFormGroupStyle}">
            <ComboBox x:Name="CbRunMode"
               Style="{StaticResource FormComboxStyle}"
               SelectedValue="{Binding RunMode}"
           DisplayMemberPath="DisplayName" SelectedValuePath="DataValue"/>
        </pu:FormGroup>
        <pu:FormGroup Header="{DynamicResource se.compType}"
       Orientation="Vertical"
       Style="{StaticResource InputFormGroupStyle}">
            <ComboBox x:Name="CbType"
                      Style="{StaticResource FormComboxStyle}" SelectedValue="{Binding ComparisonType}"
          DisplayMemberPath="DisplayName" SelectedValuePath="DataValue"/>
        </pu:FormGroup>
        <pu:FormGroup Header="{DynamicResource se.targetVal}"
Orientation="Vertical"
Style="{StaticResource InputFormGroupStyle}">
            <TextBox IsReadOnly="{Binding IsReadOnly}" Style="{StaticResource EditorTextBoxStyle}" Text="{Binding StringValue}"/>
        </pu:FormGroup>
    </StackPanel>
</UserControl>
Plugin.SeqEditor/SettingPanel/StringValTestPanel.xaml.cs
New file
@@ -0,0 +1,21 @@
using System.Windows.Controls;
using Plugin.SeqEditor.Entity.EditablePO;
using Plugin.SeqEditor.Util;
namespace Plugin.SeqEditor.SettingPanel
{
    /// <summary>
    /// StringValueTest.xaml 的交互逻辑
    /// </summary>
    public partial class StringValTestPanel : UserControl
    {
        public StringValTestPanel(StringValTestEPO data)
        {
            this.DataContext = data;
            InitializeComponent();
            CbRunMode.ItemsSource = RunModeData.ItemSource;
            CbType.ItemsSource = StringCompTypeData.ItemSource;
        }
    }
}
Plugin.SeqEditor/SettingPanel/WaitPanel.xaml
New file
@@ -0,0 +1,48 @@
<UserControl x:Class="Plugin.SeqEditor.SettingPanel.WaitPanel"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:Plugin.SeqEditor.SettingPanel"
             mc:Ignorable="d"
             xmlns:hb="clr-namespace:HMI.Base;assembly=HMI.Base"
             xmlns:uic="clr-namespace:HMI.UI.Converter;assembly=HMI.UI"
             xmlns:ui="clr-namespace:HMI.UI;assembly=HMI.UI"
             xmlns:pw="https://opensource.panuon.com/wpf"
             xmlns:pu="https://opensource.panuon.com/wpf-ui"
             xmlns:dp="clr-namespace:HMI.UI.DpUtil;assembly=HMI.UI"
             Padding="5"
             d:DesignHeight="450" d:DesignWidth="800">
    <StackPanel>
        <pu:FormGroup Header="{DynamicResource se.stepName}"
              Orientation="Vertical"
              Style="{StaticResource InputFormGroupStyle}">
            <TextBox IsReadOnly="True" Style="{StaticResource EditorTextBoxStyle}" Text="{Binding StepName}" />
        </pu:FormGroup>
        <pu:FormGroup Header="{DynamicResource se.stepType}"
      Orientation="Vertical"
      Style="{StaticResource InputFormGroupStyle}">
            <TextBox IsReadOnly="True" Style="{StaticResource EditorTextBoxStyle}" Text="{Binding StepType,Mode=OneWay}" />
        </pu:FormGroup>
        <pu:FormGroup Header="{DynamicResource se.runMode}"
                      Orientation="Vertical"
                      Style="{StaticResource InputFormGroupStyle}">
            <ComboBox x:Name="CbRunMode"
                      Style="{StaticResource FormComboxStyle}"
                      SelectedValue="{Binding RunMode}"
                  DisplayMemberPath="DisplayName" SelectedValuePath="DataValue"/>
        </pu:FormGroup>
        <pu:FormGroup Header="{DynamicResource se.waitInterval}"
              Orientation="Vertical"
              Style="{StaticResource InputFormGroupStyle}">
            <ui:NumberInput Style="{StaticResource EditorTextBoxStyle}"
                            Text="{Binding TimeInterval,UpdateSourceTrigger=PropertyChanged}"/>
        </pu:FormGroup>
    </StackPanel>
</UserControl>
Plugin.SeqEditor/SettingPanel/WaitPanel.xaml.cs
New file
@@ -0,0 +1,19 @@
using Plugin.SeqEditor.Entity.EditablePO;
using Plugin.SeqEditor.Util;
using System.Windows.Controls;
namespace Plugin.SeqEditor.SettingPanel
{
    /// <summary>
    /// WaitPanel.xaml 的交互逻辑
    /// </summary>
    public partial class WaitPanel : UserControl
    {
        public WaitPanel(WaitEPO epo)
        {
            this.DataContext = epo;
            InitializeComponent();
            CbRunMode.ItemsSource = RunModeData.ItemSource;
        }
    }
}
Plugin.SeqEditor/Util/ItemData.cs
New file
@@ -0,0 +1,147 @@
using HMI.Core.Util;
using NationalInstruments.TestStand.Interop.API;
namespace Plugin.SeqEditor.Util
{
    public class ItemData
    {
        public string DisplayName { get; private set; }
        public string DataValue { get; private set; }
        public ItemData(string displayName, string dataValue)
        {
            this.DisplayName = displayName;
            this.DataValue = dataValue;
        }
    }
    public abstract class StringCompTypeData
    {
        public static readonly ItemData None =
            new(LangUtil.Get("se.data.none"), "LOG");
        public static readonly ItemData SCCS =
            new(LangUtil.Get("se.strComp.SCCS"), "CaseSensitive");
        public static readonly ItemData SCIC =
            new(LangUtil.Get("se.strComp.SCIC"), "IgnoreCase");
        public static readonly ItemData RECS =
            new(LangUtil.Get("se.strComp.RECS"), "RegularExpressionCaseSensitive");
        public static readonly ItemData REIC =
            new(LangUtil.Get("se.strComp.REIC"), "IgnoreCase");
        public static readonly List<ItemData> ItemSource =
            new List<ItemData> {
                None,SCCS,SCIC,//RECS,REIC
            };
    }
    public abstract class FilterTypeData
    {
        public static readonly ItemData Wait =
            new(LangUtil.Get("Wait"), "NI_Wait");
        public static readonly ItemData Action =
            new(LangUtil.Get("Action"), "Action");
        public static readonly ItemData StrValTest =
            new(LangUtil.Get("String Val Test"), "StringValueTest");
        public static readonly ItemData NumValTest =
            new(LangUtil.Get("Numeric Limit Test"), "NumericLimitTest");
        public static readonly ItemData RECS =
            new(LangUtil.Get("SequenceCall"), "SequenceCall");
        public static readonly List<ItemData> ItemSource =
            new List<ItemData> {
                Wait,Action,StrValTest,//RECS,REIC
            };
    }
    public abstract class NumCompTypeData
    {
        public static readonly ItemData EQ =
            new("= Low", "EQ");
        public static readonly ItemData NE =
            new("!= Low", "NE");
        public static readonly ItemData GT =
            new("> Low", "GT");
        public static readonly ItemData LT =
            new("< Low", "LT");
        public static readonly ItemData GE =
            new(">= Low", "GE");
        public static readonly ItemData LE =
            new("<= Low", "LE");
        public static readonly ItemData GTLT =
            new("> Low,< High", "GTLT");
        public static readonly ItemData GELE =
            new(">=Low,<= High", "GELE");
        public static readonly ItemData GELT =
            new(">= Low,< High", "GELT");
        public static readonly ItemData GTLE =
            new("> Low,<= High", "GTLE");
        public static readonly ItemData LTGT =
            new("< Low OR > High", "LTGT");
        public static readonly ItemData LEGE =
            new("<= Low OR >= High", "LEGE");
        public static readonly ItemData LEGT =
            new("<= Low OR > High", "LEGT");
        public static readonly ItemData LTGE =
            new("< Low OR >= High", "LTGE");
        public static readonly List<ItemData> ItemSource =
            new List<ItemData> {
                EQ,NE,GT,LT,GE,LE,GTLT,GELE,GELT,GTLE,LTGT,LEGE,LEGT,LTGE
            };
    }
    public abstract class RunModeData
    {
        public static readonly ItemData Normal =
            new(LangUtil.Get("se.normal"), "Normal");
        public static readonly ItemData Skip =
            new(LangUtil.Get("se.skip"), "Skip");
        public static readonly ItemData ForcePass =
            new(LangUtil.Get("se.pass"), "Pass");
        public static readonly ItemData ForceFail =
            new(LangUtil.Get("se.fail"), "Fail");
        public static readonly List<ItemData> ItemSource =
            new List<ItemData> {
                Normal,Skip,ForcePass,ForceFail
            };
    }
    public abstract class StepAdapterData
    {
        public static readonly ItemData LabView = new("LabVIEW", AdapterKeyNames.FlexLVAdapterKeyName);
        public static readonly ItemData LabWindows = new("LabWindows/CVI",
            AdapterKeyNames.FlexCVIAdapterKeyName);
        public static readonly ItemData CCPlus = new("C/C++ DLL", AdapterKeyNames.FlexCAdapterKeyName);
        public static readonly ItemData DotNet = new(".NET", AdapterKeyNames.DotNetAdapterKeyname);
        public static readonly ItemData ActiveX = new("ActiveX/COM", AdapterKeyNames.AutomationAdapterKeyName);
        public static readonly ItemData None = new("None", AdapterKeyNames.NoneAdapterKeyName);
        public static readonly ItemData LabViewNGX = new("LabVIEW NXG", AdapterKeyNames.LabVIEWNXGAdapterKeyName);
        public static readonly ItemData Python = new("Python", AdapterKeyNames.PythonAdapterKeyName);
        public static readonly ItemData SequenceCall =
            new("SequenceCall", AdapterKeyNames.SequenceAdapterKeyName);
        public static readonly List<ItemData> ItemSource =
            new List<ItemData> {
                LabView,LabWindows,CCPlus,DotNet,ActiveX,None,LabViewNGX,Python,SequenceCall
            };
    }
}
Plugin.SeqEditor/Util/PoExplain.cs
New file
@@ -0,0 +1,44 @@
using NationalInstruments.TestStand.Interop.API;
namespace Plugin.SeqEditor.Util
{
    public static class PoExtensions
    {
        public static string GetValueString(this PropertyObject po)
        {
            var lookUpStr = string.Empty;
            switch (po.Type.ValueType)
            {
                case PropertyValueTypes.PropValType_String:
                    return po.GetValString(lookUpStr, 0);
                case PropertyValueTypes.PropValType_Boolean:
                    return po.GetValBoolean(lookUpStr, 0).ToString();
                case PropertyValueTypes.PropValType_Number:
                    return po.GetValNumber(lookUpStr, 0).ToString();
                default:
                    return string.Empty;
            }
        }
        public static void SetValueString(this PropertyObject po, string value)
        {
            var lookUpStr = string.Empty;
            switch (po.Type.ValueType)
            {
                case PropertyValueTypes.PropValType_String:
                    po.SetValString(lookUpStr, 0, value);
                    break;
                case PropertyValueTypes.PropValType_Boolean:
                    bool boolVal = bool.Parse(value);
                    po.SetValBoolean(lookUpStr, 0, boolVal);
                    break;
                case PropertyValueTypes.PropValType_Number:
                    double doubleVal = double.Parse(value);
                    po.SetValNumber(lookUpStr, 0, doubleVal);
                    break;
                default:
                    return;
            }
        }
    }
}
Plugin.SeqEditor/Util/Step2EPO.cs
New file
@@ -0,0 +1,44 @@
using System.Windows.Controls;
using Plugin.SeqEditor.Constant;
using Plugin.SeqEditor.Entity;
using Plugin.SeqEditor.Entity.EditablePO;
using Plugin.SeqEditor.SettingPanel;
namespace Plugin.SeqEditor.Util
{
    public class StepUI2EPO
    {
        public static object Convert2EPO(StepUI step)
        {
            switch (step.Type)
            {
                case StepTypeStr.StringValueTest:
                    return new StringValTestEPO(step);
                default:
                    return null;
            }
        }
        public static UserControl Convert2EditPanel(StepUI step)
        {
            if (step == null) return null;
            object epo;
            switch (step.Type)
            {
                case StepTypeStr.StringValueTest:
                    epo = new StringValTestEPO(step);
                    return new StringValTestPanel((StringValTestEPO)epo);
                case StepTypeStr.NI_Wait:
                    epo = new WaitEPO(step);
                    return new WaitPanel((WaitEPO)epo);
                case StepTypeStr.NumericLimitTest:
                    epo = new NumValTestEPO(step);
                    return new NumValTestPanel((NumValTestEPO)epo);
                case StepTypeStr.Action:
                    return new ActionPanel(step);
                default:
                    return null;
            }
        }
    }
}
Plugin.SeqEditor/ViewModel/SeqEditorViewModel.cs
New file
@@ -0,0 +1,454 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using HMI.Core.Util;
using HMI.UI;
using HMI.UI.Util;
using log4net;
using Microsoft.Win32;
using NationalInstruments.TestStand.Interop.API;
using Plugin.SeqEditor.Constant;
using Plugin.SeqEditor.Entity;
using Plugin.SeqEditor.Util;
using System.Collections.ObjectModel;
using System.IO;
namespace Plugin.SeqEditor.ViewModel
{
    public partial class SeqEditorViewModel : ObservableObject
    {
        private ObservableCollection<StepUI> setupSteps = new ObservableCollection<StepUI>();
        private ObservableCollection<StepUI> mainSteps = new ObservableCollection<StepUI>();
        private ObservableCollection<StepUI> cleanupSteps = new ObservableCollection<StepUI>();
        [ObservableProperty]
        private ObservableCollection<StepUI> allSteps = new ObservableCollection<StepUI>();
        [ObservableProperty]
        private ObservableCollection<SeqUI> sequences = new ObservableCollection<SeqUI>();
        [ObservableProperty]
        private SeqUI currentSeqUI;
        [ObservableProperty]
        private StepUI currentStepUI;
        [ObservableProperty]
        private string currentFileName;
        [ObservableProperty]
        private string currentSeqTitle = LangUtil.Get("se.steps");
        [ObservableProperty]
        private string targetType = null;
        private string currentFilePath;
        private Engine engine;
        private SequenceFile sequenceFile;
        private bool isLoaded;
        private ILog log;
        public SeqEditor control { get; private set; }
        public List<ItemData> Types = FilterTypeData.ItemSource;
        private HashSet<string> edits = new HashSet<string> {
            StepTypeStr.PassFailTest,
            StepTypeStr.NumericLimitTest,
            StepTypeStr.NI_MultipleNumericLimitTest,
            StepTypeStr.StringValueTest,
            StepTypeStr.Action,
            StepTypeStr.NI_Wait,
            StepTypeStr.SequenceCall,
        };
        public SeqEditorViewModel(SeqEditor control, ILog log)
        {
            this.log = log;
            this.control = control;
            engine = new Engine();
            engine.LoadTypePaletteFilesEx();
        }
        ~SeqEditorViewModel()
        {
            if (engine != null)
            {
                if (sequenceFile != null)
                {
                    engine.ReleaseSequenceFileEx(sequenceFile);
                }
                engine.ShutDown(true);
                // 不加延时会弹窗md
                System.Threading.Thread.Sleep(2500);
            }
        }
        private ObservableCollection<StepUI> LoadSteps(Sequence seq, string group = null)
        {
            //setupSteps.Clear();
            //var steps = new ObservableCollection<StepUI>();
            //int num = seq.GetNumSteps(StepGroups.StepGroup_Setup);
            //for (int i = 0; i < num; i++)
            //{
            //    Step step = seq.GetStep(i, StepGroups.StepGroup_Setup);
            //    var ui = new StepUI(step);
            //    if (edits.Contains(ui.Type))
            //    {
            //        setupSteps.Add(ui);
            //        steps.Add(ui);
            //    }
            //}
            ////SetupSteps = steps;
            ////steps = new ObservableCollection<StepUI>();
            //mainSteps.Clear();
            //num = seq.GetNumSteps(StepGroups.StepGroup_Main);
            //for (int i = 0; i < num; i++)
            //{
            //    Step step = seq.GetStep(i, StepGroups.StepGroup_Main);
            //    var ui = new StepUI(step);
            //    if (edits.Contains(ui.Type))
            //    {
            //        mainSteps.Add(ui);
            //        steps.Add(ui);
            //    }
            //}
            ////MainSteps = steps;
            ////steps = new ObservableCollection<StepUI>();
            //cleanupSteps.Clear();
            //num = seq.GetNumSteps(StepGroups.StepGroup_Cleanup);
            //for (int i = 0; i < num; i++)
            //{
            //    Step step = seq.GetStep(i, StepGroups.StepGroup_Cleanup);
            //    var ui = new StepUI(step);
            //    if (edits.Contains(ui.Type))
            //    {
            //        cleanupSteps.Add(ui);
            //        steps.Add(ui);
            //    }
            //}
            //AllSteps = steps;
            //CurrentStepUI = null;
            var steps = new ObservableCollection<StepUI>();
            int num = seq.GetNumSteps(StepGroups.StepGroup_Setup);
            for (int i = 0; i < num; i++)
            {
                Step step = seq.GetStep(i, StepGroups.StepGroup_Setup);
                var ui = Convert2StepUI(step, group);
                if (ui != null)
                {
                    steps.Add(ui);
                }
            }
            num = seq.GetNumSteps(StepGroups.StepGroup_Main);
            for (int i = 0; i < num; i++)
            {
                Step step = seq.GetStep(i, StepGroups.StepGroup_Main);
                var ui = Convert2StepUI(step, group);
                if (ui != null)
                {
                    steps.Add(ui);
                }
            }
            num = seq.GetNumSteps(StepGroups.StepGroup_Cleanup);
            for (int i = 0; i < num; i++)
            {
                Step step = seq.GetStep(i, StepGroups.StepGroup_Cleanup);
                var ui = Convert2StepUI(step, group);
                if (ui != null)
                {
                    steps.Add(ui);
                }
            }
            return steps;
        }
        private StepUI Convert2StepUI(Step step, string group)
        {
            var ui = new StepUI(step);
            if (!edits.Contains(ui.Type))
            {
                return null;
            }
            if (string.IsNullOrEmpty(group))
            {
                if (ui.StepGroup == StepGroups.StepGroup_Setup)
                {
                    group = "Setup";
                }
                else if (ui.StepGroup == StepGroups.StepGroup_Main)
                {
                    group = "Main";
                }
                else if (ui.StepGroup == StepGroups.StepGroup_Cleanup)
                {
                    group = "Cleanup";
                }
                else
                {
                    group = "Unkown";
                }
            }
            ui.Group = group;
            if (ui.Type == StepTypeStr.SequenceCall)
            {
                var po = step.Module.AsPropertyObject();
                bool useCurFile = po.GetValBoolean("UseCurFile", 0);
                if (useCurFile)
                {
                    string seqName = po.GetValString("SeqName", 0);
                    Sequence sequence = sequenceFile.GetSequenceByName(seqName);
                    if (sequence == null)
                    {
                        return ui;
                    }
                    ui.Children = LoadSteps(sequence, group);
                }
            }
            return ui;
        }
        [RelayCommand]
        private void Loaded()
        {
            if (!isLoaded)
            {
            }
        }
        [RelayCommand]
        private void OpenSeqFile()
        {
            OpenFileDialog ofd = new OpenFileDialog()
            {
                Filter = $"{LangUtil.Get("se.seq")}|*.seq;"
            };
            if (ofd.ShowDialog() == true)
            {
                control.BackgroundAction(() =>
                {
                    try
                    {
                        var start = DateTime.Now;
                        currentFilePath = ofd.FileName;
                        CurrentFileName = $"{LangUtil.Get("se.currentFile")}{Path.GetFileName(currentFilePath)}";
                        if (sequenceFile != null)
                        {
                            engine.ReleaseSequenceFileEx(sequenceFile);
                        }
                        sequenceFile = engine.GetSequenceFileEx(currentFilePath);
                        Sequence main = sequenceFile.GetSequenceByName("MainSequence");
                        CurrentSeqUI = new SeqUI(main);
                        //var seqs = new ObservableCollection<SeqUI>();
                        //for (int i = 0; i < sequenceFile.NumSequences; i++)
                        //{
                        //    var tsSequence = sequenceFile.GetSequence(i);
                        //    seqs.Add(new SeqUI(tsSequence));
                        //}
                        //Sequences = seqs;
                        if (Sequences != null && Sequences.Count > 0)
                        {
                            CurrentSeqUI = Sequences[0];
                        }
                        log.Info($"Open File [{currentFilePath}] In {(DateTime.Now - start).TotalMilliseconds}ms");
                    }
                    catch (Exception ex)
                    {
                    }
                }, LangUtil.Get("se.seqIsLoading"));
            }
        }
        [RelayCommand]
        private void SaveSeqFile()
        {
            sequenceFile?.Save(currentFilePath);
        }
        [RelayCommand]
        private void Refresh()
        {
            if (CurrentSeqUI == null) return;
            if (string.IsNullOrEmpty(TargetType))
            {
                edits = new HashSet<string> {
                    StepTypeStr.PassFailTest,
                    StepTypeStr.NumericLimitTest,
                    StepTypeStr.NI_MultipleNumericLimitTest,
                    StepTypeStr.StringValueTest,
                    StepTypeStr.Action,
                    StepTypeStr.NI_Wait,
                    StepTypeStr.SequenceCall,
                };
            }
            else
            {
                edits = new HashSet<string>(TargetType.Split(','))
                {
                    StepTypeStr.SequenceCall
                };
            }
            AllSteps = LoadSteps(CurrentSeqUI.sequence);
        }
        [RelayCommand]
        private void CloseSeqFile()
        {
            //AllSteps = new ObservableCollection<StepUI>();
            if (engine != null && sequenceFile != null)
            {
                engine.ReleaseSequenceFileEx(sequenceFile);
                sequenceFile = null;
            }
            setupSteps.Clear();
            mainSteps.Clear();
            cleanupSteps.Clear();
            AllSteps = new();
            Sequences.Clear();
            currentFilePath = string.Empty;
            CurrentFileName = string.Empty;
            CurrentSeqUI = null;
            CurrentStepUI = null;
            CurrentSeqTitle = LangUtil.Get("se.steps");
        }
        [RelayCommand]
        private void AddStep(string type)
        {
            try
            {
                var group = CurrentStepUI.step.StepGroup;
                ObservableCollection<StepUI> source;
                if (group == StepGroups.StepGroup_Setup)
                {
                    source = setupSteps;
                }
                else if (group == StepGroups.StepGroup_Main)
                {
                    source = mainSteps;
                }
                else
                {
                    source = cleanupSteps;
                }
                int index = source.IndexOf(CurrentStepUI);
                if (index < 0)
                {
                    return;
                }
                Step newStep;
                switch (type)
                {
                    case "Tests.PassFail":
                        newStep = engine.NewStep(AdapterKeyNames.FlexLVAdapterKeyName, StepTypeStr.PassFailTest);
                        newStep.Name = DefaultStepName.PassFailTest;
                        var p = newStep.AsPropertyObject();
                        string name = p.Name;
                        var ps = p.GetSubProperties("", 0);
                        foreach (var pp in ps)
                        {
                            string sname = pp.Name;
                        }
                        break;
                    case "Tests.NumericLimitTest":
                        newStep = engine.NewStep(AdapterKeyNames.FlexLVAdapterKeyName, StepTypeStr.NumericLimitTest);
                        newStep.Name = DefaultStepName.NumericLimitTest;
                        break;
                    case "Tests.NI_MultipleNumericLimitTest":
                        newStep = engine.NewStep(AdapterKeyNames.FlexLVAdapterKeyName, StepTypeStr.NI_MultipleNumericLimitTest);
                        newStep.Name = DefaultStepName.NI_MultipleNumericLimitTest;
                        break;
                    case "Tests.StringValueTest":
                        newStep = engine.NewStep(AdapterKeyNames.FlexLVAdapterKeyName, StepTypeStr.StringValueTest);
                        newStep.Name = DefaultStepName.StringValueTest;
                        break;
                    case "Actions.LabVIEW":
                        newStep = engine.NewStep(AdapterKeyNames.FlexLVAdapterKeyName, StepTypeStr.Action);
                        newStep.Name = DefaultStepName.Action;
                        break;
                    case "Actions.LabWindows/CVI":
                        newStep = engine.NewStep(AdapterKeyNames.FlexCVIAdapterKeyName, StepTypeStr.Action);
                        newStep.Name = DefaultStepName.Action;
                        break;
                    case "Actions.C/C++ DLL":
                        newStep = engine.NewStep(AdapterKeyNames.FlexCAdapterKeyName, StepTypeStr.Action);
                        newStep.Name = DefaultStepName.Action;
                        break;
                    case "Actions..NET":
                        newStep = engine.NewStep(AdapterKeyNames.DotNetAdapterKeyname, StepTypeStr.Action);
                        newStep.Name = DefaultStepName.Action;
                        break;
                    case "Actions.ActiveX/COM":
                        newStep = engine.NewStep(AdapterKeyNames.AutomationAdapterKeyName, StepTypeStr.Action);
                        newStep.Name = DefaultStepName.Action;
                        break;
                    case "Actions.None":
                        newStep = engine.NewStep(AdapterKeyNames.NoneAdapterKeyName, StepTypeStr.Action);
                        newStep.Name = DefaultStepName.Action;
                        break;
                    case "Actions.LabVIEW NXG":
                        newStep = engine.NewStep(AdapterKeyNames.LabVIEWNXGAdapterKeyName, StepTypeStr.Action);
                        newStep.Name = DefaultStepName.Action;
                        break;
                    case "Actions.Python":
                        newStep = engine.NewStep(AdapterKeyNames.PythonAdapterKeyName, StepTypeStr.Action);
                        newStep.Name = DefaultStepName.Action;
                        break;
                    default:
                        return;
                }
                CurrentSeqUI.sequence.InsertStep(newStep, index + 1, group);
                AllSteps = LoadSteps(CurrentSeqUI.sequence);
            }
            catch (Exception ex)
            {
                log.Error("Insert Step Error.", ex);
                MsgBoxHelper2.Error(ex.Message);
            }
        }
        [ObservableProperty]
        private TreeItemData treeItemData;
        partial void OnTreeItemDataChanged(TreeItemData data)
        {
            CurrentStepUI = data?.Data as StepUI;
        }
        partial void OnCurrentStepUIChanged(StepUI stepUI)
        {
            var panel = StepUI2EPO.Convert2EditPanel(stepUI);
            control.editPanelContainer.Child = panel;
            if (stepUI != null)
            {
                //window.commonP.RefreshStep(stepUI);
            }
        }
        partial void OnCurrentSeqUIChanged(SeqUI seqUI)
        {
            if (seqUI != null)
            {
                CurrentStepUI = null;
                AllSteps = LoadSteps(seqUI.sequence);
            }
            CurrentSeqTitle = $"{seqUI?.Name} {LangUtil.Get("se.steps")}";
        }
    }
}
Plugin.TestResult/SingleCell.xaml
@@ -5,6 +5,8 @@
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Plugin.TestResult_WPF"
             mc:Ignorable="d" 
             xmlns:pw="https://opensource.panuon.com/wpf"
xmlns:pu="https://opensource.panuon.com/wpf-ui"
             x:Name="uc"
             Padding="5"
             MouseDoubleClick="UserControl_MouseDoubleClick"
@@ -17,6 +19,12 @@
        <ToolTip Padding="5" BorderThickness="1" BorderBrush="Black" Background="Yellow" Content="{Binding tooltipContent}"/>
    </UserControl.ToolTip>-->
    <UserControl.ContextMenu>
        <ContextMenu>
            <MenuItem IsCheckable="True" IsChecked="{Binding CellIsDisabled,Mode=TwoWay}"  Header="禁用"/>
        </ContextMenu>
    </UserControl.ContextMenu>
    <UserControl.Style>
        <Style TargetType="UserControl">
            <Style.Triggers>
Plugin.TestResult/SingleCell.xaml.cs
@@ -78,6 +78,18 @@
            }
        }
        private bool cellIsDisabled;
        public bool CellIsDisabled
        {
            get { return cellIsDisabled; }
            set
            {
                RaiseAndSetIfchanged(ref cellIsDisabled, value);
            }
        }
        public string title => $"{parentName}-{index + 1}";
        private string _status = CommonStatus.WAITING;
Plugin.TestResult/TestResultView.xaml.cs
@@ -1,5 +1,6 @@
using HMI.Core.Util;
using HMI.UI.Util;
using NationalInstruments.TestStand.Interop.AdapterAPI;
using NationalInstruments.TestStand.Interop.API;
using NationalInstruments.TestStand.Interop.UI.Ax;
using NationalInstruments.TestStand.Utility;
Plugin.TsExecution/SingleControl.xaml
@@ -8,6 +8,7 @@
             xmlns:pu="https://opensource.panuon.com/wpf-ui"
             xmlns:local="clr-namespace:Plugin.TsExecution"
             mc:Ignorable="d" 
             xmlns:sys="clr-namespace:System;assembly=mscorlib"
             Loaded="UserControl_Loaded"
             Background="#ccc6c7"
             FontSize="{Binding ActualHeight,RelativeSource={RelativeSource AncestorType=UserControl}, 
@@ -15,6 +16,8 @@
             x:Name="control"
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <sys:Double x:Key="nanD">NaN</sys:Double>
        <Style TargetType="Label" x:Key="baseLabel">
            <Setter Property="HorizontalContentAlignment" Value="Left"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
@@ -207,8 +210,7 @@
                  VirtualizingPanel.IsContainerVirtualizable="True"
                  VirtualizingPanel.IsVirtualizing="True" 
                  VirtualizingPanel.VirtualizationMode="Recycling"
                  RowHeight="{Binding ActualWidth,ElementName=control,
Converter={StaticResource fc}, ConverterParameter='35,1093,-1,45'}"
                  RowHeight="{StaticResource nanD}"
                  ColumnHeaderHeight="{Binding ActualWidth,ElementName=control,
Converter={StaticResource fc}, ConverterParameter='30,1093,-1,45'}"
                  ItemsSource="{Binding Datasource}"
@@ -249,7 +251,7 @@
                            <TextBlock Text="{DynamicResource Test.Name}"/>
                        </DataGridTextColumn.Header>
                    </DataGridTextColumn>
                    <DataGridTextColumn Width="*" Binding="{Binding Unit}"
                    <DataGridTextColumn Width="100" Binding="{Binding Unit}"
                                        ElementStyle="{StaticResource TableTextBlock}">
                        <DataGridTextColumn.Header>
                            <TextBlock Text="{DynamicResource Test.Unit}"/>
@@ -273,7 +275,7 @@
                            <TextBlock Text="{DynamicResource Test.LimitUp}"/>
                        </DataGridTextColumn.Header>
                    </DataGridTextColumn>
                    <DataGridTemplateColumn Width="*">
                    <DataGridTemplateColumn Width="120">
                        <DataGridTemplateColumn.Header>
                            <TextBlock Text="{DynamicResource Test.Result}"/>
                        </DataGridTemplateColumn.Header>
Plugin.TsExecution/SingleControl.xaml.cs
@@ -408,6 +408,8 @@
        public void WriteToCSV(string path)
        {
            Dispatcher.BeginInvoke(new Action(() =>
            {
            try
            {
@@ -436,6 +438,7 @@
            {
                TsExecution.logger.Error("Write CSV File Failed.", ex);
            }
            }));
        }
        public void ShowPausePanel()
Plugin.TsExecution/TsExecution.xaml.cs
@@ -212,7 +212,6 @@
            }
        }
        public TsExecution()
        {
            DataContext = this;
@@ -283,15 +282,14 @@
                if (!string.IsNullOrEmpty(lastModel))
                {
                    var product = Products.FirstOrDefault(p => p.Name == lastModel);
                    if (product != null) SelectedProduct = product;
                    goto Write;
                    if (product != null)
                    {
                        SelectedProduct = product;
                        RefershUI();
                        return;
                }
                goto Write;
            Write:
                }
                SelectedProduct = Products[0];
                engine?.Globals.SetValString("StationState.ProductModel", 1, Products[0].Name);
                RefershUI();
            }
            SampleStatus = false;
@@ -461,6 +459,8 @@
            fail = Convert.ToInt32(IniFileHelper.ReadIniData("Count", "Fail", "0", CountIniPath));
        }
        private string csvDir;
        /// <summary>
        /// 读取配置文件里的几个属性
        /// </summary>
@@ -535,6 +535,21 @@
            catch
            {
                UI_ShowInfoDock = false;
            }
            try
            {
                value = ParameterManager.GetCommonParameterByName("TestReportFolder");
                csvDir = value.Value;
                if (!Directory.Exists(csvDir))
                {
                    Directory.CreateDirectory(csvDir);
                }
            }
            catch
            {
                csvDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestReport");
            }
            //Convert.ToBoolean(IniFileHelper.ReadIniData("Config", "EnableMes", "true", CountIniPath));
@@ -647,8 +662,14 @@
            //MaskWindow mask = new MaskWindow();
            try
            {
                engine?.Globals.SetValString("StationState.ProductModel", 1, SelectedProduct?.Name);
                engine?.CommitGlobalsToDisk(promptOnSaveConflicts: false);
                this.modeSeq = this.engine.GetSequenceFileEx(@"C:\Program Files\National Instruments\TestStand 2020\Components\Models\TestStandModels\ParallelModel.seq");
                this.testSeq = this.engine.GetSequenceFileEx(this.testSeqFile, 0, TypeConflictHandlerTypes.ConflictHandler_UseGlobalType);
                //var sb = testSeq.GetSequenceByName("MainSequence");
                //sb.
                this.execution = this.engine.NewExecution(this.testSeq, "Test UUTs", this.modeSeq, false, 0, null, null, null);
                IsTesting = true;
            }
@@ -831,6 +852,15 @@
            this.tsSockets[index].Barcode = barcode;
        }
        public void SavePositionCSV(int index)
        {
            var time = DateTime.Now;
            string status = this.tsSockets[index].Status == "Passed" ? "PASS" : "FAIL";
            string csvPath = Path.Combine(csvDir, SelectedProduct?.Name ?? "未知产品", time.ToString("yyyyMMdd"), status,
                $"{this.tsSockets[index].Barcode}-{time.ToString("HHmmss")}.csv");
            this.tsSockets[index].WriteToCSV(csvPath);
        }
        /// <summary>
        /// TestStand用户交互事件
        /// </summary>
@@ -939,7 +969,7 @@
                        this.tsSockets[Index].SetTestProcess(ProcessVal);
                        break;
                    case 10031://导出CSV
                        this.tsSockets[Index].WriteToCSV(e.uiMsg.StringData);
                        SavePositionCSV(Index);
                        break;
                    case 10032://按下标暂停测试
                        this.tsSockets[Index].ShowPausePanel();
WPFProject/App.config
WPFProject/BuildCmd.bat
@@ -1,8 +1,15 @@
move /y "$(TargeDir)HMI.Core.dll" $(TargeDir)Libs\HMI.Core.dll"
IF "$(ConfigurationName)"=="Release" Confuser.CLI.exe $(TargeDir)..\ConfuserEx\$(TargetFileName).crproj
move /y "$(TargeDir)HMI.Core.dll" $(TargeDir)Libs\HMI.Core.dll"
move /y "$(TargeDir)HMI.Base.dll" $(TargeDir)Libs\HMI.Base.dll"
move /y "$(TargeDir)HMI.UI.dll" $(TargeDir)Libs\HMI.UI.dll"
move /y "$(TargeDir)HMI.Util.dll" $(TargeDir)Libs\HMI.Util.dll"
move /y "$(TargeDir)ConnectionUtil.dll" $(TargeDir)Libs\ConnectionUtil.dll"
move /y "$(TargeDir)HMI.LogUtil.dll" $(TargeDir)Api\HMI.LogUtil.dll"
move /y "$(TargeDir)HMI.SerialData.Util.dll" $(TargeDir)Api\HMI.SerialData.Util.dll"
move /y "$(TargeDir)HMI.Simple.Util.dll" $(TargeDir)Api\HMI.Simple.Util.dll"
move /y "$(TargeDir)HMI.Util.Base.dll" $(TargeDir)Api\HMI.Util.Base.dll"
move /y "$(TargeDir)HMI.AlarmInfoManage.dll" $(TargeDir)Plugin\HMI.AlarmInfoManage.dll"
move /y "$(TargeDir)HMI.DataViewer.dll" $(TargeDir)Plugin\HMI.DataViewer.dll"
@@ -18,6 +25,7 @@
move /y "$(TargeDir)Plugin.TestBuilder.dll" $(TargeDir)Plugin\Plugin.TestBuilder.dll"
move /y "$(TargeDir)Plugin.DataBoard.dll" $(TargeDir)Plugin\Plugin.DataBoard.dll"
move /y "$(TargeDir)Plugin.JarUI.dll" $(TargeDir)Plugin\Plugin.JarUI.dll"
move /y "$(TargeDir)Plugin.SeqEditor.dll" $(TargeDir)Plugin\Plugin.SeqEditor.dll"
setlocal enabledelayedexpansion
WPFProject/Lang/en-US/SeqEditor.json
New file
@@ -0,0 +1,34 @@
{
  "se.title": "Seq Config",
  "se.file": "File",
  "se.open": "Open",
  "se.save": "Save",
  "se.saveTo": "Save To",
  "se.close": "Close",
  "se.stepName": "Step",
  "se.stepType": "Type",
  "se.runMode": "Run Mode",
  "se.waitInterval": "Interval (s)",
  "se.adapter": "Adapter",
  "se.seq": "Sequence",
  "se.name": "Name",
  "se.type": "Type",
  "se.description": "Description",
  "se.propertySetting": "Setting",
  "se.normal": "Normal",
  "se.skip": "Skip",
  "se.pass": "Force to PASS",
  "se.fail": "Force to FAIL",
  "se.steps": "Steps",
  "se.currentFile": "Current File: ",
  "se.seqIsLoading": "Sequence is loading",
  "se.compType": "Comparison Type",
  "se.targetVal": "Target Val",
  "se.data.none": "None",
  "se.strComp.SCCS": "String Comparsion - Case Sensitive",
  "se.strComp.SCIC": "String Comparsion - Ignore Case",
  "se.strComp.RECS": "Regular Expression - Case Sensitive",
  "se.strComp.REIC": "Regular Expression - Ignore Case"
}
WPFProject/Lang/zh-CN/SeqEditor.json
New file
@@ -0,0 +1,34 @@
{
  "se.title": "序列配置",
  "se.file": "文件",
  "se.open": "打开",
  "se.save": "保存",
  "se.saveTo": "另存",
  "se.close": "关闭",
  "se.stepName": "步骤名称",
  "se.stepType": "步骤类型",
  "se.runMode": "运行设置",
  "se.waitInterval": "等待时间(s)",
  "se.adapter": "适配器",
  "se.seq": "测试序列",
  "se.name": "名称",
  "se.type": "类型",
  "se.description": "描述",
  "se.propertySetting": "属性设置",
  "se.normal": "正常",
  "se.skip": "跳过",
  "se.pass": "强制PASS",
  "se.fail": "强制FAIL",
  "se.steps": "测试步骤",
  "se.currentFile": "当前文件:",
  "se.seqIsLoading": "正在加载测试序列",
  "se.compType": "比较方式",
  "se.targetVal": "期望值",
  "se.data.none": "无",
  "se.strComp.SCCS": "比较字符串-大小写敏感",
  "se.strComp.SCIC": "比较字符串-忽略大小写",
  "se.strComp.RECS": "正则表达式-大小写敏感",
  "se.strComp.REIC": "正则表达式-忽略大小写"
}
WPFProject/Lang/zh-CN/app.json
@@ -5,7 +5,7 @@
  "App.Password": "密码",
  "App.UsernameHolder": "请输入用户名",
  "App.PasswordHolder": "请输入密码",
  "App.PleaseWait": "请等待",
  "App.PleaseWait": "请稍候",
  "App.LoginFailed": "登陆失败",
  "App.ErrorUsernameOrPassword": "用户名或密码错误",
  "App.IconMustInFolder": "请将图标移动至程序Icon目录下",
@@ -156,5 +156,5 @@
  "App.ScannerSetting": "扫码枪设置",
  "App.SequenceIsStarting": "正在启动测试序列,请稍后...",
  "App.Status": "状态",
  "App.Info": "信息"
  "App.Info": "信息",
}
WPFProject/LoadingWindow.xaml.cs
@@ -1,5 +1,4 @@
using HMI.Base;
using HMI.Core.Api;
using HMI.UI.Util;
using Panuon.WPF.UI;
using System;
@@ -85,9 +84,11 @@
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            this.Closed -= ShutDownWhen_Closed;
            window.Show();
            this.Close();
        }
        private PanuonWindow window;
        private double total;
        private double finished;
@@ -116,9 +117,15 @@
                ShowConsole = true;
                BtnNext.Visibility = Visibility.Visible;
                MsgBoxHelper.Error(this, "加载出错,请确认");
                this.Closed += ShutDownWhen_Closed; ;
            }
        }
        private void ShutDownWhen_Closed(object sender, EventArgs e)
        {
            Application.Current.Shutdown();
        }
        public void Add(string text, bool? result)
        {
            Message = text;
WPFProject/WPFProject.csproj
@@ -107,6 +107,9 @@
    <Reference Include="Microsoft.DwayneNeed.Win32">
      <HintPath>..\Libs\Microsoft.DwayneNeed.Win32.dll</HintPath>
    </Reference>
    <Reference Include="Microsoft.Xaml.Behaviors, Version=1.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
      <HintPath>packages\Microsoft.Xaml.Behaviors.Wpf.1.1.135\lib\net462\Microsoft.Xaml.Behaviors.dll</HintPath>
    </Reference>
    <Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
      <HintPath>packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
    </Reference>
@@ -395,6 +398,12 @@
    <Content Include="Lang\en-US\DataBoard.json">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
    <Content Include="Lang\zh-CN\SeqEditor.json">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
    <Content Include="Lang\en-US\SeqEditor.json">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </Content>
    <None Include="packages.config" />
    <None Include="Properties\app.manifest" />
    <None Include="Properties\Settings.settings">
@@ -458,6 +467,10 @@
      <Project>{968fe602-22d6-42bd-981c-601ad518a896}</Project>
      <Name>Plugin.JarUI</Name>
    </ProjectReference>
    <ProjectReference Include="..\Plugin.SeqEditor\Plugin.SeqEditor.csproj">
      <Project>{717fd815-ff95-4f30-b4d9-9cea978ca40e}</Project>
      <Name>Plugin.SeqEditor</Name>
    </ProjectReference>
    <ProjectReference Include="..\Plugin.ServoManage\Plugin.ServoManage.csproj">
      <Project>{1853e00f-6865-4bc6-974b-6a5e1c00bafe}</Project>
      <Name>Plugin.ServoManage</Name>
@@ -507,11 +520,17 @@
  <ItemGroup />
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
  <PropertyGroup>
    <PostBuildEvent>move /y "$(TargeDir)HMI.Core.dll" $(TargeDir)Libs\HMI.Core.dll"
    <PostBuildEvent>
move /y "$(TargeDir)HMI.Core.dll" $(TargeDir)Libs\HMI.Core.dll"
move /y "$(TargeDir)HMI.Base.dll" $(TargeDir)Libs\HMI.Base.dll"
move /y "$(TargeDir)HMI.UI.dll" $(TargeDir)Libs\HMI.UI.dll"
move /y "$(TargeDir)HMI.Util.dll" $(TargeDir)Libs\HMI.Util.dll"
move /y "$(TargeDir)ConnectionUtil.dll" $(TargeDir)Libs\ConnectionUtil.dll"
move /y "$(TargeDir)HMI.LogUtil.dll" $(TargeDir)Api\HMI.LogUtil.dll"
move /y "$(TargeDir)HMI.SerialData.Util.dll" $(TargeDir)Api\HMI.SerialData.Util.dll"
move /y "$(TargeDir)HMI.Simple.Util.dll" $(TargeDir)Api\HMI.Simple.Util.dll"
move /y "$(TargeDir)HMI.Util.Base.dll" $(TargeDir)Api\HMI.Util.Base.dll"
move /y "$(TargeDir)HMI.AlarmInfoManage.dll" $(TargeDir)Plugin\HMI.AlarmInfoManage.dll"
move /y "$(TargeDir)HMI.DataViewer.dll" $(TargeDir)Plugin\HMI.DataViewer.dll"
@@ -527,6 +546,7 @@
move /y "$(TargeDir)Plugin.TestBuilder.dll" $(TargeDir)Plugin\Plugin.TestBuilder.dll"
move /y "$(TargeDir)Plugin.DataBoard.dll" $(TargeDir)Plugin\Plugin.DataBoard.dll"
move /y "$(TargeDir)Plugin.JarUI.dll" $(TargeDir)Plugin\Plugin.JarUI.dll"
move /y "$(TargeDir)Plugin.SeqEditor.dll" $(TargeDir)Plugin\Plugin.SeqEditor.dll"
setlocal enabledelayedexpansion
WPFProject/WPFProject.sln
@@ -63,6 +63,8 @@
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugin.JarUI", "..\Plugin.JarUI\Plugin.JarUI.csproj", "{968FE602-22D6-42BD-981C-601AD518A896}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugin.SeqEditor", "..\Plugin.SeqEditor\Plugin.SeqEditor.csproj", "{717FD815-FF95-4F30-B4D9-9CEA978CA40E}"
EndProject
Global
    GlobalSection(SolutionConfigurationPlatforms) = preSolution
        Debug|Any CPU = Debug|Any CPU
@@ -303,6 +305,14 @@
        {968FE602-22D6-42BD-981C-601AD518A896}.Release|Any CPU.Build.0 = Release|Any CPU
        {968FE602-22D6-42BD-981C-601AD518A896}.Release|x64.ActiveCfg = Release|Any CPU
        {968FE602-22D6-42BD-981C-601AD518A896}.Release|x64.Build.0 = Release|Any CPU
        {717FD815-FF95-4F30-B4D9-9CEA978CA40E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
        {717FD815-FF95-4F30-B4D9-9CEA978CA40E}.Debug|Any CPU.Build.0 = Debug|Any CPU
        {717FD815-FF95-4F30-B4D9-9CEA978CA40E}.Debug|x64.ActiveCfg = Debug|x64
        {717FD815-FF95-4F30-B4D9-9CEA978CA40E}.Debug|x64.Build.0 = Debug|x64
        {717FD815-FF95-4F30-B4D9-9CEA978CA40E}.Release|Any CPU.ActiveCfg = Release|Any CPU
        {717FD815-FF95-4F30-B4D9-9CEA978CA40E}.Release|Any CPU.Build.0 = Release|Any CPU
        {717FD815-FF95-4F30-B4D9-9CEA978CA40E}.Release|x64.ActiveCfg = Release|Any CPU
        {717FD815-FF95-4F30-B4D9-9CEA978CA40E}.Release|x64.Build.0 = Release|Any CPU
    EndGlobalSection
    GlobalSection(SolutionProperties) = preSolution
        HideSolutionNode = FALSE
WPFProject/packages.config
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="log4net" version="3.0.4" targetFramework="net472" />
  <package id="Microsoft.Xaml.Behaviors.Wpf" version="1.1.135" targetFramework="net472" />
  <package id="Newtonsoft.Json" version="13.0.3" targetFramework="net472" />
  <package id="Panuon.WPF" version="1.1.3" targetFramework="net472" />
  <package id="Panuon.WPF.UI" version="1.3.0.2" targetFramework="net472" />