Преглед на файлове

新增了实验数据保存功能

l2736 преди 2 седмици
родител
ревизия
9847f9ad29
променени са 63 файла, в които са добавени 12775 реда и са изтрити 12367 реда
  1. 2 42
      Avalonia/ShakerApp/App.axaml.cs
  2. 1 1
      Avalonia/ShakerApp/Convert/EnumToDescription.cs
  3. 0 1
      Avalonia/ShakerApp/Convert/MenuItemConverter.cs
  4. 26 0
      Avalonia/ShakerApp/Models/DataAxisType.cs
  5. 4 2
      Avalonia/ShakerApp/ShakerApp.csproj
  6. 3 1
      Avalonia/ShakerApp/Styles/ResourceDictionary.axaml
  7. 1 1
      Avalonia/ShakerApp/ViewModels/About/AboutViewModel.cs
  8. 14 2
      Avalonia/ShakerApp/ViewModels/CommunicationViewModel.cs
  9. 26 2
      Avalonia/ShakerApp/ViewModels/DeviceManger/DeviceInfoViewModel.cs
  10. 1 1
      Avalonia/ShakerApp/ViewModels/DeviceManger/DeviceMangerViewModel.cs
  11. 1 1
      Avalonia/ShakerApp/ViewModels/File/LoadConfigViewModel.cs
  12. 1 1
      Avalonia/ShakerApp/ViewModels/File/SaveConfigViewModel.cs
  13. 1 1
      Avalonia/ShakerApp/ViewModels/IndexValueItemViewModel.cs
  14. 1 1
      Avalonia/ShakerApp/ViewModels/InputPassWord/InputPassWordViewModel.cs
  15. 1 1
      Avalonia/ShakerApp/ViewModels/Log/LogViewModel.cs
  16. 5 2
      Avalonia/ShakerApp/ViewModels/MainPage/IMainPageViewModel.cs
  17. 1 1
      Avalonia/ShakerApp/ViewModels/MainPage/MainPageItemViewModel.cs
  18. 11 2
      Avalonia/ShakerApp/ViewModels/MainPage/MainPageViewModel.cs
  19. 31 17
      Avalonia/ShakerApp/ViewModels/MainPage/OutSignalMainPageViewModel.cs
  20. 62 14
      Avalonia/ShakerApp/ViewModels/MainPage/RandomMainPageViewModel.cs
  21. 39 29
      Avalonia/ShakerApp/ViewModels/MainPage/SineMainPageViewModel.cs
  22. 2 22
      Avalonia/ShakerApp/ViewModels/MainPage/StartPageViewModel.cs
  23. 40 8
      Avalonia/ShakerApp/ViewModels/MainViewModel.cs
  24. 12 9
      Avalonia/ShakerApp/ViewModels/Menu/MenuItemViewModel.cs
  25. 149 0
      Avalonia/ShakerApp/ViewModels/Menu/MenuViewModel.cs
  26. 1 1
      Avalonia/ShakerApp/ViewModels/Oil/CircuitViewModel.cs
  27. 1 2
      Avalonia/ShakerApp/ViewModels/Oil/OilErrorInfoViewModel.cs
  28. 1 1
      Avalonia/ShakerApp/ViewModels/Oil/OilSourceAnalogViewModel.cs
  29. 1 1
      Avalonia/ShakerApp/ViewModels/Oil/OilSourceStatusViewModel.cs
  30. 3 1
      Avalonia/ShakerApp/ViewModels/Setting/ShakerSettingViewModel.cs
  31. 1 1
      Avalonia/ShakerApp/ViewModels/ShakerConfig/RandomConfigViewModel.cs
  32. 1 1
      Avalonia/ShakerApp/ViewModels/ShakerConfig/RandomIdentifyViewModel.cs
  33. 1 1
      Avalonia/ShakerApp/ViewModels/ShakerConfig/RandomSpectrumItemViewModel.cs
  34. 1 1
      Avalonia/ShakerApp/ViewModels/ShakerConfig/RandomTransferFunctionViewModel.cs
  35. 6 24
      Avalonia/ShakerApp/ViewModels/ShakerConfig/ShakerConfigViewModel.cs
  36. 1 1
      Avalonia/ShakerApp/ViewModels/ShakerConfig/SweepConfigViewModel.cs
  37. 1 1
      Avalonia/ShakerApp/ViewModels/ShakerConfig/SweepItemViewModel.cs
  38. 8 26
      Avalonia/ShakerApp/ViewModels/ShakerControl/ShakerControlViewModel.cs
  39. 1 1
      Avalonia/ShakerApp/ViewModels/ShakerControl/ValveConfigItemViewModel.cs
  40. 21 19
      Avalonia/ShakerApp/ViewModels/ShakerDataViewModel.cs
  41. 1 1
      Avalonia/ShakerApp/ViewModels/ShakerStatus/ShakerStatusViewModel.cs
  42. 1 1
      Avalonia/ShakerApp/ViewModels/SignalPreview/AnalogSignalPreviewViewModel.cs
  43. 1 1
      Avalonia/ShakerApp/ViewModels/SignalPreview/SignalPreviewViewModel.cs
  44. 1 1
      Avalonia/ShakerApp/ViewModels/StatisticsViewModel.cs
  45. 1 1
      Avalonia/ShakerApp/ViewModels/SweepControlItemViewModel.cs
  46. 119 102
      Avalonia/ShakerApp/ViewModels/ViewModelBase.cs
  47. 8 4
      Avalonia/ShakerApp/Views/MainPage/RandomMainPage.axaml
  48. 2 2
      Avalonia/ShakerApp/Views/MainWindow.axaml
  49. 10 5
      Avalonia/ShakerApp/Views/ShakerConfig/RandomConfigView.axaml
  50. 18 0
      Avalonia/ShakerApp/Window1.axaml
  51. 13 0
      Avalonia/ShakerApp/Window1.axaml.cs
  52. 18 0
      Calc/FxpConvert.Common/ICalc.cs
  53. 229 0
      Calc/SIMDFxpConvert/SIMDCalc.cs
  54. 1 1
      Language/Zh-CN/Language.axaml
  55. 16 11
      Shaker.Model/Models/RandomDataModel.cs
  56. 1 0
      Shaker.Model/Topic.cs
  57. 11711 11987
      Shaker/Shaker.lvbitx
  58. 9 1
      Shaker/ShakerService.Control.cs
  59. 33 0
      Shaker/ShakerService.ReadFifo.cs
  60. 8 1
      Shaker/ShakerService.WriteConfig.cs
  61. 14 1
      Shaker/ViewModel/ServiceRandomConfigViewModel.cs
  62. 76 2
      Shaker/ViewModel/ServiceRandomDataViewModel.cs
  63. 0 1
      ShakerControl.sln

+ 2 - 42
Avalonia/ShakerApp/App.axaml.cs

@@ -20,7 +20,6 @@ public partial class App : Application
 {
     public unsafe override void Initialize()
     {
-        Test();
 #if DEBUG
         if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
         {
@@ -29,47 +28,6 @@ public partial class App : Application
 #endif
         AvaloniaXamlLoader.Load(this);
     }
-    private void Test()
-    {
-        IFxpConvert convert = new SIMDFxpConvert.SIMDFxpConverter();
-        uint count = 4;
-        double[] f = new double[] { -1, -2, 1, 2 };
-        ulong[] f2 = new ulong[count];
-        NiFpga_FxpTypeInfo typeInfo = new NiFpga_FxpTypeInfo()
-        {
-            integerWordLength = 5,
-            Delta = 0.000031,
-            isSigned = true,
-            Max = 15.999969,
-            Min = -16,
-            wordLength = 20
-        };
-        convert.DoubleConvertToDxp(ref f[0], typeInfo, ref f2[0], count);
-        byte[] bytes = new byte[(int)Math.Ceiling(typeInfo.wordLength * count / 8.0)];
-        string s = string.Empty;
-        
-        for(int i=0;i<f2.Length;i++)
-        {
-            s += f2[i].ToString("b").PadLeft(64,'0').Substring(64 - typeInfo.wordLength, typeInfo.wordLength);
-        }
-        for(int i = 0; i < bytes.Length;i++)
-        {
-            bytes[i] = System.Convert.ToByte(s.Substring(i * 8, Math.Min(8, s.Length - i * 8)), 2);
-        }
-
-        s = string.Empty;
-        for(int i=0;i<bytes.Length;i++)
-        {
-            s += bytes[i].ToString("b").PadLeft(8, '0');
-        }
-        ulong[] f3 = new ulong[count];
-        for(int i=0;i<f3.Length;i++)
-        {
-            f3[i] = System.Convert.ToUInt64(s.Substring(i * typeInfo.wordLength, typeInfo.wordLength),2);
-        }
-        double[] f4 = new double[count];
-        convert.FxpConvertToDouble(ref f3[0], typeInfo, ref f4[0], count);
-    }
     public override void OnFrameworkInitializationCompleted()
     {
         BindingPlugins.DataValidators.RemoveAt(0);
@@ -84,6 +42,8 @@ public partial class App : Application
             desktop.MainWindow = new MainWindow();
         }
         base.OnFrameworkInitializationCompleted();
+        Window1 window = new Window1();
+        window.Show();
     }
 
 }

+ 1 - 1
Avalonia/ShakerApp/Convert/EnumToDescription.cs

@@ -77,7 +77,7 @@ namespace ShakerApp.Convert
         }
     }
 
-    internal class ValueDescription : ViewModelBase<IModel>
+    internal class ValueDescription : DisplayViewModelBase<IModel>
     {
         private Enum? value;
         private string key = string.Empty;

+ 0 - 1
Avalonia/ShakerApp/Convert/MenuItemConverter.cs

@@ -18,7 +18,6 @@ namespace ShakerApp.Convert
             var list = new AvaloniaList<MenuItem>();
             if(value is IList<ViewModels.MenuItemViewModel> menuItems)
             {
-                
                 menuItems.ToList().ForEach(x =>list.Add(x.ConverterToMenuItem()));
             }
             return list;

+ 26 - 0
Avalonia/ShakerApp/Models/DataAxisType.cs

@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ShakerApp.Models
+{
+    /// <summary>
+    /// 波形数据类型
+    /// </summary>
+    internal enum DataAxisType
+    {
+        /// <summary>
+        /// 线性
+        /// </summary>
+        [Description("Linear")]
+        Linear,
+        /// <summary>
+        /// 对数
+        /// </summary>
+        [Description("Logarithm")]
+        Logarithm,
+    }
+}

+ 4 - 2
Avalonia/ShakerApp/ShakerApp.csproj

@@ -41,6 +41,7 @@
     <PackageReference Condition="'$(Configuration)'=='Debug'" Include="HotAvalonia.Extensions" Version="2.0.0" />
     <PackageReference Include="MessagePack" Version="3.0.308" />
     <PackageReference Condition="'$(Configuration)'=='Debug'" Include="Microsoft.Extensions.Logging.Console" Version="9.0.0" />
+    <PackageReference Include="ParquetSharp" Version="16.1.0" />
     <PackageReference Include="System.ServiceModel.Primitives" Version="8.1.1" />
   </ItemGroup>
 
@@ -51,10 +52,11 @@
     <ProjectReference Include="..\..\Language\ILanguage\ILanguage.csproj" />
     <ProjectReference Include="..\..\OxyPlot\OxyPlot.Avalonia\OxyPlot.Avalonia.csproj" />
     <ProjectReference Include="..\..\Shaker.Model\Shaker.Models.csproj" />
-    <ProjectReference Include="..\..\Tdms\FSharp.Data.Tdms.fsproj" />
     <ProjectReference Include="..\Avalonia.Xaml.Behaviors\Avalonia.Xaml.Behaviors.csproj" />
     <ProjectReference Include="..\SukiUI\SukiUI.csproj" />
   </ItemGroup>
 
-  <Import Project="..\..\TdmsFile\TdmsFile.projitems" Label="Shared" />
+  <ItemGroup>
+    <Folder Include="ViewModels\DataReview\" />
+  </ItemGroup>
 </Project>

+ 3 - 1
Avalonia/ShakerApp/Styles/ResourceDictionary.axaml

@@ -5,6 +5,8 @@
     <Design.PreviewWith>
         <Border Padding="20" />
     </Design.PreviewWith>
+    <s:String x:Key="RandomValueUnit">g&#x00B2;&#x002F;Hz</s:String>
+
     <!--  Add Resources Here  -->
     <s:Double x:Key="ItemFontSize">12</s:Double>
     <s:Double x:Key="ControlButtonWidth">120</s:Double>
@@ -40,5 +42,5 @@
     <StreamGeometry x:Key="ResetGeometry">m789.886,527.571c-6.992,-3.244 -14.423,-4.889 -22.085,-4.889c-20.374,0 -39.079,11.877 -47.653,30.258c-51.433,110.272 -170.339,184.362 -295.883,184.362c-176.01,0 -319.204,-143.147 -319.204,-319.1c0,-84.963 33.387,-164.167 94.011,-223.022c59.937,-58.187 139.912,-90.231 225.193,-90.231c73.003,0 144.49,25.381 201.293,71.467l33.854,27.465l74.178,-73.062l-27.19,-23.884c-20.63,-18.122 -42.676,-34.156 -65.525,-47.658l-0.209,-0.124c-29.979,-17.958 -62.771,-32.202 -97.471,-42.34c-39.035,-11.156 -79.049,-16.813 -118.929,-16.813c-233.941,0.001 -424.266,187.605 -424.266,418.202c0,233.945 190.324,424.272 424.264,424.272c165.506,0 322.678,-98.49 391.104,-245.082c12.23,-26.188 0.768,-57.524 -25.482,-69.821zm-216.038,-236.804c0.662,1.598 2.314,4.278 5.956,4.278l120.712,0.181l113.305,0.182c1.855,0 3.464,-0.975 4.52,-1.836c1.629,-1.475 2.478,-3.244 2.523,-5.259l0,-34.744l0.734,-199.289l0,-0.043l0.002,-0.044c0.015,-1.766 -0.553,-3.207 -1.737,-4.404c-1.415,-1.427 -3.555,-2.314 -5.585,-2.314l-239.206,235.729c-2.779,2.776 -1.781,6.214 -1.224,7.563z</StreamGeometry>
     <StreamGeometry x:Key="UpbitfileGeometry">m773.87,299.18l0,-50l-48.12,0l0,-203.99l-204.61,0l0,-45.19l-50,0l0,45.19l-168.42,0l0,-45.19l-50,0l0,45.19l-204.6,0l0,203.99l-48.12,0l0,50l48.12,0l0,169.66l-46.69,0l0,50l46.69,0l0,203.99l204.6,0l0,44.91l50,0l0,-44.91l168.42,0l0,45.19l50,0l0,-45.19l204.61,0l0,-203.99l48.12,0l0,-50l-48.12,0l0,-169.66l48.12,0zm-98.12,373.65l-577.64,0l0,-577.64l577.63,0l0,577.64l0.01,0zm-313.82,-345.48l0,211.06l50,0l0,-211.06l84.65,79.1l34.13,-36.53l-143.78,-134.37l-143.78,134.37l34.13,36.53l84.65,-79.1z</StreamGeometry>
     <StreamGeometry x:Key="LayoutGeometry">m640.4804,67.42419l-539.32434,0c-9.30926,0 -17.22931,3.30743 -23.84417,9.86792c-6.57532,6.62476 -9.87781,14.5448 -9.87781,23.85406l0,101.12641l606.74853,0l0,-101.12146c0,-9.30927 -3.30743,-17.22931 -9.8778,-23.85407c-6.62476,-6.56048 -14.5448,-9.86792 -23.84418,-9.86792l0.01977,0l0,-0.00494zm33.70221,573.05127l0,-370.80835l-370.8034,0l0,404.50562l337.08142,0c9.29938,0 17.22436,-3.30744 23.84418,-9.87781c6.57531,-6.60993 9.8778,-14.5448 9.8778,-23.84418l0,0.01978l0,0.00494zm-438.20782,33.70221l0,-404.50562l-168.53577,0l0,370.80341c0,9.31421 3.30249,17.26392 9.87287,23.82935c6.61486,6.61981 14.53491,9.89263 23.84417,9.89263l134.81873,0l0,-0.01977zm-134.8385,-674.17767l539.32434,0c27.89319,0 51.74725,9.8778 71.50287,29.62353c19.74572,19.75562 29.61364,43.58002 29.61364,71.48804l0,539.33917c0,27.89319 -9.86792,51.73737 -29.61364,71.49298c-19.75562,19.75562 -43.60968,29.63342 -71.50287,29.63342l-539.32434,0c-27.91297,0 -51.74726,-9.8778 -71.50287,-29.63342c-19.75562,-19.75561 -29.63342,-43.60473 -29.63342,-71.49298l0,-539.33423c0,-27.91296 9.8778,-51.73736 29.63342,-71.49298c19.75561,-19.74573 43.5899,-29.62353 71.50287,-29.62353z</StreamGeometry>
-  <StreamGeometry x:Key="LogGeometry">m640.96011,890.3337l-615.34881,0c-14.14516,0 -25.61131,-11.46615 -25.61131,-25.61131l0,-839.11108c0,-14.14517 11.46717,-25.61131 25.61131,-25.61131l615.34778,0c14.14517,0 25.61131,11.46717 25.61131,25.61131l0,839.11108c0.00102,14.14516 -11.46512,25.61131 -25.61028,25.61131zm-589.73751,-51.22364l564.12518,0l0,-787.88744l-564.12518,0l0,787.88744zm477.85739,-441.36629l-387.62736,0c-14.14516,0 -25.61131,-11.46717 -25.61131,-25.61131s11.46717,-25.61131 25.61131,-25.61131l387.62634,0c14.14516,0 25.61131,11.46717 25.61131,25.61131s-11.46513,25.61131 -25.61029,25.61131zm0,-143.7428l-387.62736,0c-14.14516,0 -25.61131,-11.46717 -25.61131,-25.61131s11.46717,-25.6113 25.61131,-25.6113l387.62634,0c14.14516,0 25.61131,11.46717 25.61131,25.6113s-11.46513,25.61131 -25.61029,25.61131zm0,287.4856l-387.62736,0c-14.14516,0 -25.61131,-11.46615 -25.61131,-25.61131s11.46717,-25.61131 25.61131,-25.61131l387.62634,0c14.14516,0 25.61131,11.46615 25.61131,25.61131s-11.46513,25.61131 -25.61029,25.61131zm0,143.7428l-387.62736,0c-14.14516,0 -25.61131,-11.46615 -25.61131,-25.61131s11.46717,-25.61131 25.61131,-25.61131l387.62634,0c14.14516,0 25.61131,11.46615 25.61131,25.61131s-11.46513,25.61131 -25.61029,25.61131z</StreamGeometry>
+    <StreamGeometry x:Key="LogGeometry">m640.96011,890.3337l-615.34881,0c-14.14516,0 -25.61131,-11.46615 -25.61131,-25.61131l0,-839.11108c0,-14.14517 11.46717,-25.61131 25.61131,-25.61131l615.34778,0c14.14517,0 25.61131,11.46717 25.61131,25.61131l0,839.11108c0.00102,14.14516 -11.46512,25.61131 -25.61028,25.61131zm-589.73751,-51.22364l564.12518,0l0,-787.88744l-564.12518,0l0,787.88744zm477.85739,-441.36629l-387.62736,0c-14.14516,0 -25.61131,-11.46717 -25.61131,-25.61131s11.46717,-25.61131 25.61131,-25.61131l387.62634,0c14.14516,0 25.61131,11.46717 25.61131,25.61131s-11.46513,25.61131 -25.61029,25.61131zm0,-143.7428l-387.62736,0c-14.14516,0 -25.61131,-11.46717 -25.61131,-25.61131s11.46717,-25.6113 25.61131,-25.6113l387.62634,0c14.14516,0 25.61131,11.46717 25.61131,25.6113s-11.46513,25.61131 -25.61029,25.61131zm0,287.4856l-387.62736,0c-14.14516,0 -25.61131,-11.46615 -25.61131,-25.61131s11.46717,-25.61131 25.61131,-25.61131l387.62634,0c14.14516,0 25.61131,11.46615 25.61131,25.61131s-11.46513,25.61131 -25.61029,25.61131zm0,143.7428l-387.62736,0c-14.14516,0 -25.61131,-11.46615 -25.61131,-25.61131s11.46717,-25.61131 25.61131,-25.61131l387.62634,0c14.14516,0 25.61131,11.46615 25.61131,25.61131s-11.46513,25.61131 -25.61029,25.61131z</StreamGeometry>
 </ResourceDictionary>

+ 1 - 1
Avalonia/ShakerApp/ViewModels/About/AboutViewModel.cs

@@ -9,7 +9,7 @@ using System.Threading.Tasks;
 
 namespace ShakerApp.ViewModels
 {
-    internal class AboutViewModel:ViewModelBase<IModel>
+    internal class AboutViewModel:DisplayViewModelBase<IModel>
     {
         private AboutViewModel()
         {

+ 14 - 2
Avalonia/ShakerApp/ViewModels/CommunicationViewModel.cs

@@ -12,7 +12,7 @@ using System.Threading.Tasks;
 
 namespace ShakerApp.ViewModels
 {
-    public sealed class CommunicationViewModel:ViewModelBase<IModel>
+    public sealed class CommunicationViewModel:DisplayViewModelBase<IModel>
     {
         private readonly string PlugnsPath =Path.Combine(AppDomain.CurrentDomain.BaseDirectory , "Communication");
         private Dictionary<bool, Type> CommunicationViewModelTypes = new Dictionary<bool, Type>();
@@ -172,7 +172,19 @@ namespace ShakerApp.ViewModels
         [AllowNull]
 
         public ICommunication.ICommunication ServiceCommunication { get; private set; }
-        public bool LocalIsConnect { get => localIsConnect; set =>SetProperty(ref localIsConnect , value); }
+        public bool LocalIsConnect 
+        { 
+            get => localIsConnect;
+            set
+            {
+                SetProperty(ref localIsConnect, value);
+                MenuViewModel.Instance["MenuManger"]!.IsEnabled = !value;
+                MenuViewModel.Instance["MenuConnect"]!.IsEnabled = !value;
+                MenuViewModel.Instance["MenuDisConnect"]!.IsEnabled = value;
+                MenuViewModel.Instance["MenuDeviceConfig"]!.IsEnabled = value;
+                MenuViewModel.Instance["MenuDebug"]!.IsEnabled = value;
+            }
+        }
         public bool ServiceIsStart 
         { 
             get => serviceIsStart;

+ 26 - 2
Avalonia/ShakerApp/ViewModels/DeviceManger/DeviceInfoViewModel.cs

@@ -1,7 +1,9 @@
 using CommunityToolkit.Mvvm.Input;
 using Shaker.Models;
 using ShakerApp.Models;
+using ShakerApp.Tools;
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
@@ -15,9 +17,31 @@ namespace ShakerApp.ViewModels
         public DeviceInfoViewModel()
         {
         }
-        public DeviceInfoViewModel(DeviceInfoModel model)
+        public DeviceInfoViewModel(DeviceInfoModel model):base(model)
         {
-            UpDateModel(model);
+        }
+
+        public void SaveTdmsConfig(Dictionary<string, string> config)
+        {
+            if (config == null) config = new Dictionary<string, string>();
+            typeof(DeviceInfoModel).GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
+                .Select(x => (x, x.GetValue(Model)))
+                .ToList()
+                .ForEach(x =>
+                {
+                    if (x.Item2 == null)
+                    {
+                        return;
+                    }
+                    if (x.x.FieldType.IsAssignableTo(typeof(IList)) || x.x.FieldType.IsArray)
+                    {
+                        config[$"{nameof(DeviceInfoModel)}_{x.x.Name}"] = string.Join("", x.Item2.GetBytes().Select(x => $"{x:X2}"));
+                    }
+                    else
+                    {
+                        config[$"{nameof(DeviceInfoModel)}_{x.x.Name}"] = x.Item2?.ToString() ?? string.Empty;
+                    }
+                });
         }
         [PropertyAssociation(nameof(DeviceInfoModel.Name))]
         public string Name { get => Model.Name; set => SetProperty(ref Model.Name, value); }

+ 1 - 1
Avalonia/ShakerApp/ViewModels/DeviceManger/DeviceMangerViewModel.cs

@@ -15,7 +15,7 @@ using System.Windows.Input;
 
 namespace ShakerApp.ViewModels
 {
-    public class DeviceMangerViewModel : ViewModelBase<IModel>
+    public class DeviceMangerViewModel : DisplayViewModelBase<IModel>
     {
         public override bool CanResize => false;
         public override double Width => 960;

+ 1 - 1
Avalonia/ShakerApp/ViewModels/File/LoadConfigViewModel.cs

@@ -13,7 +13,7 @@ using System.Windows.Input;
 
 namespace ShakerApp.ViewModels
 {
-    internal class LoadConfigViewModel:ViewModelBase<IModel>
+    internal class LoadConfigViewModel:DisplayViewModelBase<IModel>
     {
         public override string OKContent => "Confirm";
         public override bool CanResize => false;

+ 1 - 1
Avalonia/ShakerApp/ViewModels/File/SaveConfigViewModel.cs

@@ -13,7 +13,7 @@ using System.Windows.Input;
 
 namespace ShakerApp.ViewModels
 {
-    internal class SaveConfigViewModel:ViewModelBase<IModel>
+    internal class SaveConfigViewModel:DisplayViewModelBase<IModel>
     {
         public override string OKContent => "Confirm";
         public override bool CanResize => false;

+ 1 - 1
Avalonia/ShakerApp/ViewModels/IndexValueItemViewModel.cs

@@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis;
 
 namespace ShakerApp.ViewModels
 {
-    public class IndexValueItemViewModel<T>:ViewModelBase<IndexValueItemModel<T>>
+    public class IndexValueItemViewModel<T>:DisplayViewModelBase<IndexValueItemModel<T>>
     {
         public IndexValueItemViewModel(int index,T value)
         {

+ 1 - 1
Avalonia/ShakerApp/ViewModels/InputPassWord/InputPassWordViewModel.cs

@@ -10,7 +10,7 @@ using System.Threading.Tasks;
 
 namespace ShakerApp.ViewModels
 {
-    internal class InputPassWordViewModel:ViewModelBase<IModel>
+    internal class InputPassWordViewModel:DisplayViewModelBase<IModel>
     {
         private string passWord = string.Empty;
 

+ 1 - 1
Avalonia/ShakerApp/ViewModels/Log/LogViewModel.cs

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
 
 namespace ShakerApp.ViewModels
 {
-    internal class LogViewModel:ViewModelBase<IModel>
+    internal class LogViewModel:DisplayViewModelBase<IModel>
     {
         private object locker = new object();
         public AvaloniaList<IndexValueItemViewModel<LogItemViewModel>> Logs { get; } = new AvaloniaList<IndexValueItemViewModel<LogItemViewModel>>();

+ 5 - 2
Avalonia/ShakerApp/ViewModels/MainPage/IMainPageViewModel.cs

@@ -1,4 +1,5 @@
 
+using ParquetSharp;
 using Shaker.Models;
 using ShakerApp.Views;
 using System;
@@ -13,10 +14,12 @@ namespace ShakerApp.ViewModels
     {
         public MainPageType PageType { get; }
 
+        public void UpdataColnumInfo();
         public void Start();
         public void Stop();
+        public Column[] Columns { get; }
         public void UpdateData(List<IResultDataModel> model);
-        public void SaveTdmsConfig(FSharp.Data.Tdms.File file);
-        public void SaveTestData(FSharp.Data.Tdms.File file);
+        public void SaveTdmsConfig(Dictionary<string,string> config);
+        public void SaveTestData(ParquetFileWriter file);
     }
 }

+ 1 - 1
Avalonia/ShakerApp/ViewModels/MainPage/MainPageItemViewModel.cs

@@ -8,7 +8,7 @@ using System.Threading.Tasks;
 
 namespace ShakerApp.ViewModels
 {
-    internal class MainPageItemViewModel:ViewModelBase<IModel>
+    internal class MainPageItemViewModel:DisplayViewModelBase<IModel>
     {
         private bool cache;
 

+ 11 - 2
Avalonia/ShakerApp/ViewModels/MainPage/MainPageViewModel.cs

@@ -1,4 +1,5 @@
 using Avalonia.Collections;
+using ParquetSharp;
 using Shaker.Models;
 using ShakerApp.Views;
 using System;
@@ -11,18 +12,19 @@ using System.Threading.Tasks;
 
 namespace ShakerApp.ViewModels
 {
-    internal class MainPageViewModel:ViewModelBase<IModel>
+    internal class MainPageViewModel:DisplayViewModelBase<IModel>
     {
         private MainPageType mainPageType = Shaker.Models.MainPageType.StartPage;
 
         public AvaloniaList<MainPageItemViewModel> MainPages { get; } = new AvaloniaList<MainPageItemViewModel>();
-        private IReadOnlyList<IMainPageViewModel> MainPageViewModels { get; } = new List<IMainPageViewModel>()
+        public IReadOnlyList<IMainPageViewModel> MainPageViewModels { get; } = new List<IMainPageViewModel>()
         {
             StartPageViewModel.Instance,
             SineMainPageViewModel.Instance,
             RandomMainPageViewModel.Instance,
             OutSignalMainPageViewModel.Instance,
         };
+        public Column[] Columns => MainPage.Columns;
         private MainPageViewModel()
         {
             this.GetType().Assembly.GetTypes()
@@ -48,6 +50,13 @@ namespace ShakerApp.ViewModels
         {
 
         }
+
+        public void SaveTdmsConfig(Dictionary<string, string> config)
+        {
+            MainPage.SaveTdmsConfig(config);
+            config[nameof(MainPageType)] = MainPageType.ToString();
+        }
+        public void SaveTestData(ParquetFileWriter file)=>MainPage.SaveTestData(file);
         public MainPageType MainPageType 
         {
             get => mainPageType;

+ 31 - 17
Avalonia/ShakerApp/ViewModels/MainPage/OutSignalMainPageViewModel.cs

@@ -1,5 +1,6 @@
 using Avalonia.Collections;
 using Avalonia.Controls;
+using ParquetSharp;
 using Shaker.Models;
 using ShakerApp.Models;
 using ShakerApp.Tools;
@@ -14,17 +15,41 @@ using System.Threading.Tasks;
 
 namespace ShakerApp.ViewModels
 {
-    internal class OutSignalMainPageViewModel:ViewModelBase<IModel>,IMainPageViewModel
+    internal abstract class BaseMainPageViewModel<TModel>:DisplayViewModelBase<TModel>,IMainPageViewModel where TModel:IModel
     {
-        public void SaveTdmsConfig(FSharp.Data.Tdms.File file)
+        private protected Column[] columns = new Column[0];
+        public abstract MainPageType PageType { get; }
+        public virtual void Start() { }
+        public virtual void Stop() { }
+        public Column[] Columns => columns;
+        public virtual void UpdataColnumInfo()
         {
-
+            columns = new Column[ViewModels.ShakerConfigViewModel.Instance.Model.AnalogSignalConfigs.Count];
+            for (int i = 0; i < columns.Length; i++)
+            {
+                columns[i] = new Column<double>($"{ShakerConfigViewModel.Instance.Model.AnalogSignalConfigs[i].Name}_{ShakerConfigViewModel.Instance.Model.AnalogSignalConfigs[i].Unit}_{ShakerConfigViewModel.Instance.Model.AnalogSignalConfigs[i].AnalogType}_{Models.DataAxisType.Linear}");
+            }
         }
-        public void SaveTestData(FSharp.Data.Tdms.File file)
+        public virtual void SaveTestData(ParquetFileWriter file)
         {
-
+            var writer = file.AppendBufferedRowGroup();
+            for(int i=0;i<ShakerConfigViewModel.Instance.Model.AnalogSignalConfigs.Count;i++)
+            {
+                writer.Column(i).LogicalWriter<double>().WriteBatch(ShakerDataViewModel.Instance.GetAnalogRawData(i));
+            }
         }
-        public MainPageType PageType => MainPageType.OutSignal;
+        public virtual void SaveTdmsConfig(Dictionary<string, string> config)
+        {
+            ShakerConfigViewModel.Instance.SaveTdmsConfig(config);
+        }
+        public virtual void UpdateData(List<IResultDataModel> model)
+        {
+        }
+
+    }
+    internal class OutSignalMainPageViewModel: BaseMainPageViewModel<IModel>
+    {
+        public  override MainPageType PageType => MainPageType.OutSignal;
 
         public ViewModels.AnalogSignalPreviewViewModel AnalogSignal { get; } = new AnalogSignalPreviewViewModel()
         {
@@ -39,17 +64,6 @@ namespace ShakerApp.ViewModels
         {
 
         }
-        public void Start()
-        {
-        }
-
-        public void Stop()
-        {
-        }
-
-        public void UpdateData(List<IResultDataModel> model)
-        {
-        }
 
         
         public static OutSignalMainPageViewModel Instance { get; } = new OutSignalMainPageViewModel();

+ 62 - 14
Avalonia/ShakerApp/ViewModels/MainPage/RandomMainPageViewModel.cs

@@ -4,10 +4,12 @@ using Avalonia.Controls;
 using Avalonia.Controls.ApplicationLifetimes;
 using OxyPlot;
 using OxyPlot.Series;
+using ParquetSharp;
 using Shaker.Models;
 using ShakerApp.Tools;
 using ShakerApp.Views;
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
@@ -15,16 +17,8 @@ using System.Threading.Tasks;
 
 namespace ShakerApp.ViewModels
 {
-    internal class RandomMainPageViewModel:ViewModelBase<RandomDataModel>,IMainPageViewModel
+    internal class RandomMainPageViewModel:BaseMainPageViewModel<RandomDataModel>
     {
-        public void SaveTdmsConfig(FSharp.Data.Tdms.File file)
-        {
-
-        }
-        public void SaveTestData(FSharp.Data.Tdms.File file)
-        {
-
-        }
         [PropertyAssociation(nameof(RandomDataModel.CurrentIdentifyDisplacement))]
         public double CurrentIdentifyDisplacement => Model.CurrentIdentifyDisplacement;
         [PropertyAssociation(nameof(RandomDataModel.CurrentIdentifyRms))]
@@ -68,7 +62,7 @@ namespace ShakerApp.ViewModels
             {
                 Key = "Ampt",
                 Title = App.Current?.FindResource(ShakerConstant.AmptAxisTitleKey) + "",
-                Unit = "g",
+                Unit = App.Current?.FindResource("RandomValueUnit")?.ToString() ?? string.Empty,
                 MajorGridlineStyle = LineStyle.Solid,
                 Position = OxyPlot.Axes.AxisPosition.Left,
             });
@@ -143,16 +137,70 @@ namespace ShakerApp.ViewModels
         {
 
         }
-        public void Start()
+        public override void SaveTdmsConfig(Dictionary<string, string> config)
+        {
+            base.SaveTdmsConfig(config); 
+            typeof(RandomConfigViewModel).GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
+                .Select(x => (x, x.GetValue(RandomConfigViewModel.Instance.Model)))
+                .ToList()
+                .ForEach(x =>
+                {
+                    if (x.Item2 == null)
+                    {
+                        return;
+                    }
+                    if (x.x.FieldType.IsAssignableTo(typeof(IList)) || x.x.FieldType.IsArray)
+                    {
+                        config[$"{nameof(RandomConfigViewModel)}_{x.x.Name}"] = string.Join("", x.Item2.GetBytes().Select(x => $"{x:X2}"));
+                    }
+                    else
+                    {
+                        config[$"{nameof(RandomConfigViewModel)}_{x.x.Name}"] = x.Item2?.ToString() ?? string.Empty;
+                    }
+                });
+        }
+        public override void UpdataColnumInfo()
+        {
+            /*
+             * 保存时域信号和各加速度通道psd数据、加速度合成PSD数据和驱动PSD数据
+             */
+            columns = new Column[ShakerConfigViewModel.Instance.ChannelCount + ShakerConfigViewModel.Instance.AccelerationSensorCount + 2];
+            for(int i=0;i<columns.Length;i++)
+            {
+                if(i<ShakerConfigViewModel.Instance.ChannelCount)
+                {
+                    columns[i] = new Column<double>($"{ShakerConfigViewModel.Instance.Model.AnalogSignalConfigs[i].Name}_{ShakerConfigViewModel.Instance.Model.AnalogSignalConfigs[i].Unit}_{ShakerConfigViewModel.Instance.Model.AnalogSignalConfigs[i].AnalogType}_{Models.DataAxisType.Linear}");
+                }
+                else
+                {
+                    columns[i] = new Column<double>($"{ShakerConfigViewModel.Instance.Model.AnalogSignalConfigs[i].Name}_{App.Current?.FindResource("RandomValueUnit")}_{""}_{Models.DataAxisType.Logarithm}");
+                }
+            }
+        }
+        public override void SaveTestData(ParquetFileWriter file)
+        {
+            base.SaveTestData(file);
+            if (lastdata == null) return;
+            var writer = file.AppendBufferedRowGroup();
+            for(int i=0;i<ShakerConfigViewModel.Instance.AccelerationSensorCount;i++)
+            {
+                writer.Column(ShakerConfigViewModel.Instance.AnalogSignals.Count + i).LogicalWriter<double>().WriteBatch(lastdata.CurrentAccelerationPSD[i]);
+            }
+            writer.Column(ShakerConfigViewModel.Instance.AccelerationSensorCount + ShakerConfigViewModel.Instance.AnalogSignals.Count).LogicalWriter<double>().WriteBatch(lastdata.CurrentAccelerationSynthesisPSD);
+            writer.Column(ShakerConfigViewModel.Instance.AccelerationSensorCount + ShakerConfigViewModel.Instance.Accelerations.Count + 1).LogicalWriter<double>().WriteBatch(lastdata.DriverPSD);
+        }
+        public override void Start()
         {
             SetRefSpectrum(RandomConfigViewModel.Instance.RefSpectrum);
         }
 
-        public void Stop()
+        public override void Stop()
         {
         }
+        private RandomDataModel lastdata;
         public override void UpDateModel(RandomDataModel model)
         {
+            lastdata = model;
             base.UpDateModel(model);
             int startindex = (int)(RandomConfigViewModel.Instance.MinFrequency / RandomConfigViewModel.Instance.FrequencyResolution);
             for(int i=0;i<datas.Count;i++)
@@ -168,7 +216,7 @@ namespace ShakerApp.ViewModels
             PlotModel.InvalidatePlot(true);
         }
 
-        public void UpdateData(List<IResultDataModel> model)
+        public override void UpdateData(List<IResultDataModel> model)
         {
             if(model !=null &&model.Count >0 && model.First() is RandomDataModel randomdata)
             {
@@ -186,7 +234,7 @@ namespace ShakerApp.ViewModels
             }
             PlotModel.InvalidatePlot(true);
         }
-        public MainPageType PageType => MainPageType.RandomPage;
+        public override MainPageType PageType => MainPageType.RandomPage;
 
         public OxyPlot.PlotModel PlotModel { get; private set; } = new OxyPlot.PlotModel();
         public static RandomMainPageViewModel Instance { get; } = new RandomMainPageViewModel();

+ 39 - 29
Avalonia/ShakerApp/ViewModels/MainPage/SineMainPageViewModel.cs

@@ -2,6 +2,7 @@
 using Avalonia.Controls;
 using OxyPlot;
 using OxyPlot.Series;
+using ParquetSharp;
 using Shaker.Models;
 using Shaker.Models.Tools;
 using ShakerApp.Tools;
@@ -17,7 +18,7 @@ using System.Threading.Tasks;
 
 namespace ShakerApp.ViewModels
 {
-    internal class SineMainPageViewModel : ViewModelBase<SineDataModel>, IMainPageViewModel
+    internal class SineMainPageViewModel : BaseMainPageViewModel<SineDataModel>
     {
         public const string TDMSConfigName = "SweepConfig";
         public const string TDMSDataName = "SweepData";
@@ -100,18 +101,25 @@ namespace ShakerApp.ViewModels
                 });
             });
         }
+
         static SineMainPageViewModel()
         {
 
         }
-        public void SaveTdmsConfig(FSharp.Data.Tdms.File file)
+        public override void UpdataColnumInfo()
         {
-            if (!file.Groups.Any(x => x.Name == TDMSConfigName))
+            columns = new Column[ViewModels.ShakerConfigViewModel.Instance.Model.AnalogSignalConfigs.Count+3];
+            for (int i = 0; i < ShakerConfigViewModel.Instance.Model.AnalogSignalConfigs.Count; i++)
             {
-                var g = new FSharp.Data.Tdms.Group(TDMSConfigName, new List<FSharp.Data.Tdms.Property>(), new List<FSharp.Data.Tdms.Channel>());
-                file.Groups.Append(g);
+                 columns[i] = new Column<double>($"{ShakerConfigViewModel.Instance.Model.AnalogSignalConfigs[i].Name}_{ShakerConfigViewModel.Instance.Model.AnalogSignalConfigs[i].Unit}_{ShakerConfigViewModel.Instance.Model.AnalogSignalConfigs[i].AnalogType}_{Models.DataAxisType.Linear}");
             }
-            var group = file.Groups.First(x => x.Name == TDMSConfigName);
+            columns[^3] = new Column<double>($"Acceleration_g_{" "}_{Models.DataAxisType.Logarithm}");
+            columns[^2] = new Column<double>($"Frequency_g_{" "}_{Models.DataAxisType.Logarithm}");
+            columns[^1] = new Column<uint>($"SweepIndex_ _{" "}_{Models.DataAxisType.Logarithm}");
+        }
+        public override void SaveTdmsConfig(Dictionary<string, string> config)
+        {
+            base.SaveTdmsConfig(config);
             typeof(SweepConfigModel) .GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
                 .Select(x => (x, x.GetValue(SweepConfigViewModel.Instance.Model)))
                 .ToList()
@@ -123,31 +131,15 @@ namespace ShakerApp.ViewModels
                     }
                     if (x.x.FieldType.IsAssignableTo(typeof(IList)) || x.x.FieldType.IsArray)
                     {
-                        string value = string.Join("", x.Item2.GetBytes().Select(x => $"{x:X2}"));
-                        if (!group.Properties.Any(y => y.Name == x.x.Name))
-                        {
-                            group.Properties.Append(new FSharp.Data.Tdms.Property(x.x.Name, typeof(string), value));
-                        }
-                    }
-                    else if (x.x.FieldType.IsEnum)
-                    {
-                        if (!group.Properties.Any(y => y.Name == x.x.Name))
-                        {
-                            group.Properties.Append(new FSharp.Data.Tdms.Property(x.x.Name, typeof(int), (int)x.Item2));
-                        }
+                        config[$"{nameof(SweepConfigModel)}_{x.x.Name}"]  = string.Join("", x.Item2.GetBytes().Select(x => $"{x:X2}"));
                     }
                     else
                     {
-                        if (!group.Properties.Any(y => y.Name == x.x.Name))
-                        {
-                            group.Properties.Append(new FSharp.Data.Tdms.Property(x.x.Name, x.x.FieldType, x.Item2));
-                        }
+                        config[$"{nameof(SweepConfigModel)}_{x.x.Name}"] = x.Item2?.ToString() ?? string.Empty;
                     }
                 });
         }
-        public void SaveTestData(FSharp.Data.Tdms.File file)
-        {
-        }
+
         public void SetRefSpectrum(List<SweepData> data)
         {
             datas.Clear();
@@ -160,16 +152,17 @@ namespace ShakerApp.ViewModels
             PlotModel.InvalidatePlot(true);
         }
 
-        public void Start()
+        public override void Start()
         {
             SetRefSpectrum(SweepConfigViewModel.Instance.RefSpectrum);
+            lastwritefreq = 0;
         }
 
-        public void Stop()
+        public override void Stop()
         {
         }
 
-        public void UpdateData(List<IResultDataModel> model)
+        public override void UpdateData(List<IResultDataModel> model)
         {
             if (model.Last() is SineDataModel sine)
             {
@@ -182,6 +175,7 @@ namespace ShakerApp.ViewModels
                 UpPlot();
             }
         }
+        private double lastwritefreq = 0;
         private void UpDataModels(List<SineDataModel> models)
         {
             models.ForEach(sine =>
@@ -261,6 +255,22 @@ namespace ShakerApp.ViewModels
                 SweepConfigViewModel.Instance.Model.CalcAmpt(sine.CurrentFrequency, ref value, ref upstop, ref upwarn, ref downstop, ref downwarn);
                 datas.Insert(index + 1, new SweepData(sine.CurrentFrequency,currentacc, value,  upstop, downstop, upwarn, downwarn));
             });
+            var file = ShakerDataViewModel.Instance.Writer;
+            if (file == null) return;
+            var savemodels = models.DistinctBy(x => x.CurrentFrequency).Where(x=>x.CurrentFrequency!=lastwritefreq).ToList();
+            if (savemodels.Count == 0) return;
+            lastwritefreq = savemodels[^1].CurrentFrequency;
+            try
+            {
+                var writer = file.AppendBufferedRowGroup();
+                writer.Column(ShakerConfigViewModel.Instance.AccelerationSensorCount).LogicalWriter<double>().WriteBatch(savemodels.Select(x => x.CurrentAcceleration).ToArray());
+                writer.Column(ShakerConfigViewModel.Instance.AccelerationSensorCount + 1).LogicalWriter<double>().WriteBatch(savemodels.Select(x => x.CurrentFrequency).ToArray());
+                writer.Column(ShakerConfigViewModel.Instance.AccelerationSensorCount + 2).LogicalWriter<uint>().WriteBatch(savemodels.Select(x => x.SweepIndex).ToArray());
+            }
+            catch
+            {
+
+            }
         }
         private void UpPlot()
         {
@@ -303,7 +313,7 @@ namespace ShakerApp.ViewModels
         [PropertyAssociation(nameof(SineDataModel.SweepIndex))]
         public uint SweepIndex { get => Model.SweepIndex; set => SetProperty(ref Model.SweepIndex, value); }
         [PropertyAssociation(nameof(SineDataModel.MainPageType))]
-        public MainPageType PageType => Model.MainPageType;
+        public override MainPageType PageType => Model.MainPageType;
 
         public OxyPlot.PlotModel PlotModel { get; private set; } = new OxyPlot.PlotModel();
         public static SineMainPageViewModel Instance { get; } = new SineMainPageViewModel();

+ 2 - 22
Avalonia/ShakerApp/ViewModels/MainPage/StartPageViewModel.cs

@@ -8,18 +8,10 @@ using System.Threading.Tasks;
 
 namespace ShakerApp.ViewModels
 {
-    internal class StartPageViewModel:ViewModelBase<IModel>,IMainPageViewModel
+    internal class StartPageViewModel:BaseMainPageViewModel<IModel>
     {
-        public MainPageType PageType => MainPageType.StartPage;
+        public override MainPageType PageType => MainPageType.StartPage;
 
-        public void SaveTdmsConfig(FSharp.Data.Tdms.File file)
-        {
-
-        }
-        public void SaveTestData(FSharp.Data.Tdms.File file)
-        {
-
-        }
         private StartPageViewModel()
         {
 
@@ -28,18 +20,6 @@ namespace ShakerApp.ViewModels
         {
 
         }
-        public void Start()
-        {
-        }
-
-        public void Stop()
-        {
-        }
-
-        void IMainPageViewModel.UpdateData(List<IResultDataModel> model)
-        {
-        }
-
         public static StartPageViewModel Instance { get; } = new StartPageViewModel();
     }
 }

+ 40 - 8
Avalonia/ShakerApp/ViewModels/MainViewModel.cs

@@ -16,16 +16,14 @@ using SukiUI.Toasts;
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Reflection;
 using System.Runtime.CompilerServices;
 using System.Windows.Input;
 
 namespace ShakerApp.ViewModels;
 
-public class MainViewModel : ViewModelBase<IModel>
+public class MainViewModel : DisplayViewModelBase<IModel>
 {
-    private bool isMenuVisible = true;
-    public bool IsMenuVisible { get => isMenuVisible; set => SetProperty(ref isMenuVisible, value); }
-    public AvaloniaList<MenuItemViewModel> Menus { get; } = new AvaloniaList<MenuItemViewModel>();
     public ICalc Calc { get; } = new SIMDFxpConvert.SIMDCalc();
     private AvaloniaDictionary<string, Window?> OpenedWindows = new AvaloniaDictionary<string, Window?>();
     private bool canDebug = false;
@@ -68,14 +66,28 @@ public class MainViewModel : ViewModelBase<IModel>
                 .FirstOrDefault(y => y.PropertyType == x);
                 val?.GetValue(null);
             });
+
+        List<(MethodInfo Method, string Key)> infos = this.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
+            .Where(x => x.GetCustomAttribute<MenuClickAttribute>() != null)
+            .Select(x =>
+            {
+                var att = x.GetCustomAttribute<MenuClickAttribute>();
+                return (x, att!.Key);
+            }).ToList();
+        MenuViewModel.Instance.MenuClick += (menu) =>
+        {
+            if (menu == null) return;
+            infos.FirstOrDefault(x => x.Key == menu.Header).Method?.Invoke(Default, new[] { menu.Header });
+        };
     }
     static MainViewModel()
     {
 
     }
     public Avalonia.Media.IBrush TitleColor => CommunicationViewModel.Instance.LocalIsConnect ? Brushes.Black : Brushes.Red;
-    public ICommand ExitCommand => new RelayCommand(Exit);
-    private void Exit()
+    public ICommand ExitCommand => new RelayCommand(()=>Exit(""));
+    [MenuClick("MenuExit")]
+    private void Exit(string? p)
     {
         ShowAsk(App.Current?.FindResource("PromptExitMsg")+"",()=>
         {
@@ -111,7 +123,16 @@ public class MainViewModel : ViewModelBase<IModel>
 
     public override string Title => GetTitle();
 
-    public bool CanDebug { get => canDebug; set =>SetProperty(ref canDebug, value); }
+    public bool CanDebug
+    {
+        get => canDebug;
+        set
+        {
+            SetProperty(ref canDebug, value); 
+            MenuViewModel.Instance["CanDebug"]!.IsVisible = value;
+            MenuViewModel.Instance["MenuDebug"]!.IsVisible = value;
+        }
+    }
 
 
     public ICommand SettingCommand => new RelayCommand<string?>(Setting);
@@ -148,6 +169,7 @@ public class MainViewModel : ViewModelBase<IModel>
         MainPageViewModel.Instance.MainPageType = type;
     }
     public ICommand SaveConfigCommand => new RelayCommand<string?>(SaveConfig);
+    [MenuClick("MenuSaveConfig")]
     private async void SaveConfig(string? p)
     {
         if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop && !string.IsNullOrEmpty(p))
@@ -185,6 +207,7 @@ public class MainViewModel : ViewModelBase<IModel>
         window.Show();
     }
     public ICommand LoadConfigCommand => new RelayCommand<string?>(LoadConfig);
+    [MenuClick("MenuLoadConfig")]
     private async void LoadConfig(string? p)
     {
         if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop && !string.IsNullOrEmpty(p))
@@ -483,6 +506,11 @@ public class MainViewModel : ViewModelBase<IModel>
                 CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.OilEmergencyStop)?.Publish(this, null, state);
             }
         });
+
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.RANDOMNEXTLEVEL)?.Subscrip((_, _) =>
+        {
+            CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.RANDOMNEXTLEVEL)?.Publish(this);
+        });
     }
     public ICommand RandomConfigCommand => new RelayCommand<string?>(RandomConfig);
     private async void RandomConfig(string? p)
@@ -586,7 +614,11 @@ public class MainViewModel : ViewModelBase<IModel>
         OnPropertyChanged(nameof(Title));
         OnPropertyChanged(nameof(TitleColor));
         CommunicationViewModel.Instance.ServiceCommunication?.GetEvent<DeviceInfoModel?>()?.Publish(this, DeviceMangerViewModel.Instance.CurrentDevice?.Model);
-        
+        foreach (var item in MainPageViewModel.Instance.MainPageViewModels)
+        {
+            item.UpdataColnumInfo();
+        }
+
     }
     public static MainViewModel Default { get; } = new MainViewModel();
 

+ 12 - 9
Avalonia/ShakerApp/ViewModels/Menu/MenuItemViewModel.cs

@@ -30,11 +30,10 @@ namespace ShakerApp.ViewModels
         public bool IconVisibile { get => iconVisibile; set =>SetProperty(ref iconVisibile , value); }
         public string IconKey { get => iconKey; set =>SetProperty(ref iconKey , value); }
 
-        public ICommand Command => new RelayCommand<string?>((p) => Action?.Invoke(p));
-        [AllowNull]
-        public Action<string?> Action { get; set; }
         public bool IsEnabled { get => isEnabled; set =>SetProperty(ref isEnabled , value); }
         public bool IsVisible { get => isVisible; set =>SetProperty(ref isVisible , value); }
+        [AllowNull]
+        public MenuItem MenuItem { get; private set; }
         public AvaloniaList<MenuItemViewModel> Items { get; } = new AvaloniaList<MenuItemViewModel>();
         public Separator GetSeparator()
         {
@@ -42,7 +41,7 @@ namespace ShakerApp.ViewModels
             separator.DataContext = this;
             separator.Bind(Separator.IsVisibleProperty, new Binding()
             {
-                Path = nameof(IsSeparator),
+                Path = nameof(IsVisible),
                 Mode = BindingMode.TwoWay,
             });
             separator.Bind(Separator.IsEnabledProperty, new Binding()
@@ -52,15 +51,17 @@ namespace ShakerApp.ViewModels
             });
             return separator;
         }
+        public MenuItemViewModel(params MenuItemViewModel[] menus)
+        {
+            if (menus == null) return;
+            Items.AddRange(menus);
+        }
         public MenuItem ConverterToMenuItem()
         {
             var menuItem = new Avalonia.Controls.MenuItem();
+            this.MenuItem = menuItem;
             menuItem.DataContext = this;
             menuItem.Bind(Avalonia.Controls.MenuItem.HeaderProperty, new DynamicResourceExtension(Header));
-            menuItem.Bind(Avalonia.Controls.MenuItem.CommandParameterProperty, new Binding()
-            {
-                Path=nameof(Header),
-            });
             if (!string.IsNullOrEmpty(IconKey))
             {
                 var pathicon = new PathIcon();
@@ -75,9 +76,11 @@ namespace ShakerApp.ViewModels
             }
             menuItem.Bind(MenuItem.CommandProperty, new Binding()
             {
-                Path = nameof(Command),
+                Path = nameof(MenuViewModel.Command),
                 Mode = BindingMode.TwoWay,
+                Source = MenuViewModel.Instance,
             });
+            menuItem.CommandParameter = this;
             menuItem.Bind(MenuItem.IsEnabledProperty, new Binding()
             {
                 Path = nameof(IsEnabled),

+ 149 - 0
Avalonia/ShakerApp/ViewModels/Menu/MenuViewModel.cs

@@ -0,0 +1,149 @@
+using Avalonia.Collections;
+using CommunityToolkit.Mvvm.Input;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Input;
+
+namespace ShakerApp.ViewModels
+{
+    [AttributeUsage(AttributeTargets.Method)]
+    public sealed class MenuClickAttribute:Attribute
+    {
+        public MenuClickAttribute(string key = "") { Key = key; }
+        public string Key { get; } = string.Empty;
+    }
+    internal class MenuViewModel:ViewModelBase
+    {
+        public ICommand Command => new RelayCommand<MenuItemViewModel?>((p) => MenuClick?.Invoke(p));
+        [AllowNull]
+        public Action<MenuItemViewModel?> MenuClick { get; set; }
+
+        private bool isMenuVisible = true;
+        public bool IsMenuVisible { get => isMenuVisible; set => SetProperty(ref isMenuVisible, value); }
+        public MenuItemViewModel? this[string key]
+        {
+            get
+            {
+                MenuItemViewModel? menu = null;
+                foreach (var item in Menus)
+                {
+                    menu = GetMenuItem(item, key);
+                    if(menu!=null)return menu;
+                }
+                return menu;
+            }
+        }
+        private MenuItemViewModel? GetMenuItem(MenuItemViewModel menuItem,string key)
+        {
+            if (menuItem == null ) return null;
+            if(menuItem.Header == key)return menuItem;
+            if( menuItem.Items == null || menuItem.Items.Count == 0)return null;
+            foreach (var item in menuItem.Items)
+            {
+                var tempmenu= GetMenuItem(item, key);
+                if(tempmenu!=null)return tempmenu;
+            }
+            return null;
+        }
+        public AvaloniaList<MenuItemViewModel> Menus { get; } = new AvaloniaList<MenuItemViewModel>();
+        private MenuViewModel()
+        {
+            Menus.Add(GetFileMenu());
+            Menus.Add(GetDeviceMenu());
+        }
+        private MenuItemViewModel GetFileMenu()
+        {
+            return new MenuItemViewModel(new MenuItemViewModel()
+            {
+                Header = "MenuSaveConfig",
+                IconKey = "SaveGeometry",
+                IconVisibile = true,
+                IsEnabled = true,
+                IsSeparator = false,
+                IsVisible = true,
+            },
+            new MenuItemViewModel()
+            {
+                Header = "MenuLoadConfig",
+                IconKey = "OpenGeometry",
+                IconVisibile = true,
+                IsEnabled = true,
+                IsSeparator = false,
+                IsVisible = true,
+            },
+            new MenuItemViewModel()
+            {
+                IsSeparator = true,
+            },
+            new MenuItemViewModel()
+            {
+                Header = "MenuExit",
+                IconKey = "ExitGeometry",
+                IconVisibile = true,
+                IsEnabled = true,
+                IsSeparator = false,
+                IsVisible = true,
+            })
+            {
+                Header = "MenuFile",
+            };
+        }
+        private MenuItemViewModel GetDeviceMenu()
+        {
+            return new MenuItemViewModel(new MenuItemViewModel()
+            {
+                Header = "MenuManger",
+                IsEnabled= true,
+                IsVisible= true,
+            },new MenuItemViewModel()
+            {
+                Header = "MenuConnect",
+                IsEnabled= true,
+                IsVisible = false,
+            },new MenuItemViewModel()
+            {
+                Header= "MenuDisConnect",
+                IsEnabled = false,
+            },new MenuItemViewModel()
+            {
+                Header = "MenuDeviceConfig",
+                IsEnabled = false,
+            },new MenuItemViewModel()
+            {
+                IsSeparator= true,
+            },new MenuItemViewModel()
+            {
+                Header = "MenuShakerControl",
+                IsEnabled = false,
+            },new MenuItemViewModel()
+            {
+                IsSeparator = true,
+            },new MenuItemViewModel()
+            {
+                Header = "MenuOilSourceControl",
+                IsEnabled =false,
+            },new MenuItemViewModel()
+            {
+                Header= "CanDebug",
+                IsSeparator =true,
+                IsVisible = true,
+            },new MenuItemViewModel()
+            {
+                Header= "MenuDebug",
+                IsEnabled = false,
+            })
+            {
+                Header = "MenuDevice",
+            };
+        }
+        static MenuViewModel()
+        {
+
+        }
+        public static MenuViewModel Instance { get; } = new MenuViewModel();
+    }
+}

+ 1 - 1
Avalonia/ShakerApp/ViewModels/Oil/CircuitViewModel.cs

@@ -10,7 +10,7 @@ using System.Windows.Input;
 
 namespace ShakerApp.ViewModels
 {
-    public class CircuitViewModel : ViewModelBase<CircuitModel>
+    public class CircuitViewModel : DisplayViewModelBase<CircuitModel>
     {
         private int waitTime = 2000;
         public CircuitViewModel()

+ 1 - 2
Avalonia/ShakerApp/ViewModels/Oil/OilErrorInfoViewModel.cs

@@ -10,9 +10,8 @@ namespace ShakerApp.ViewModels
         {
 
         }
-        public OilErrorInfoViewModel(ErrorInfoModel model):this()
+        public OilErrorInfoViewModel(ErrorInfoModel model):base(model)
         {
-            UpDateModel(model);
         }
         [PropertyAssociation(nameof(ErrorInfoModel.Name))]
         public string Name => Model.Name;

+ 1 - 1
Avalonia/ShakerApp/ViewModels/Oil/OilSourceAnalogViewModel.cs

@@ -4,7 +4,7 @@ using System.Collections.Generic;
 
 namespace ShakerApp.ViewModels
 {
-    public class OilSourceAnalogViewModel : ViewModelBase<OilSourceAnalogModel>
+    public class OilSourceAnalogViewModel : DisplayViewModelBase<OilSourceAnalogModel>
     {
         public OilSourceAnalogViewModel()
         {

+ 1 - 1
Avalonia/ShakerApp/ViewModels/Oil/OilSourceStatusViewModel.cs

@@ -9,7 +9,7 @@ using System.Threading.Tasks;
 
 namespace ShakerApp.ViewModels
 {
-    public class OilSourceStatusViewModel : ViewModelBase<OilSourceStatusModel>
+    public class OilSourceStatusViewModel : DisplayViewModelBase<OilSourceStatusModel>
     {
         public override double Width => 1300;
         public override double Height => 800;

+ 3 - 1
Avalonia/ShakerApp/ViewModels/Setting/ShakerSettingViewModel.cs

@@ -17,7 +17,7 @@ using System.Windows.Input;
 
 namespace ShakerApp.ViewModels
 {
-    public class ShakerSettingViewModel : ViewModelBase<Models.ShakerSettingModel>
+    public class ShakerSettingViewModel : DisplayViewModelBase<Models.ShakerSettingModel>
     {
         public override bool CanResize => false;
         public override double Width => 960;
@@ -41,6 +41,8 @@ namespace ShakerApp.ViewModels
                 else
                 {
                 }
+                MenuViewModel.Instance["MenuManger"]!.IsVisible = value == WorkingMode.Local;
+                MenuViewModel.Instance["MenuConnect"]!.IsVisible = value == WorkingMode.Remote;
             }
         }
         [PropertyAssociation(nameof(ShakerSettingModel.RemoteIP))]

+ 1 - 1
Avalonia/ShakerApp/ViewModels/ShakerConfig/RandomConfigViewModel.cs

@@ -17,7 +17,7 @@ using System.Windows.Input;
 
 namespace ShakerApp.ViewModels
 {
-    internal class RandomConfigViewModel : ViewModelBase<Shaker.Models.RandomConfigModel>
+    internal class RandomConfigViewModel : DisplayViewModelBase<Shaker.Models.RandomConfigModel>
     {
         public List<RandomData> RefSpectrum => datas;
         private RandomConfigViewModel()

+ 1 - 1
Avalonia/ShakerApp/ViewModels/ShakerConfig/RandomIdentifyViewModel.cs

@@ -2,7 +2,7 @@
 
 namespace ShakerApp.ViewModels
 {
-    public class RandomIdentifyViewModel:ViewModelBase<RandomIdentifyModel>
+    public class RandomIdentifyViewModel:DisplayViewModelBase<RandomIdentifyModel>
     {
         [PropertyAssociation(nameof(RandomIdentifyModel.StartDisplacement))]
         public float StartDisplacement { get => Model.StartDisplacement; set => SetProperty(ref Model.StartDisplacement, value); }

+ 1 - 1
Avalonia/ShakerApp/ViewModels/ShakerConfig/RandomSpectrumItemViewModel.cs

@@ -2,7 +2,7 @@
 
 namespace ShakerApp.ViewModels
 {
-    public class RandomSpectrumItemViewModel:ViewModelBase<RandomSpectrumItemModel>
+    public class RandomSpectrumItemViewModel:DisplayViewModelBase<RandomSpectrumItemModel>
     {
         public RandomSpectrumItemViewModel()
         {

+ 1 - 1
Avalonia/ShakerApp/ViewModels/ShakerConfig/RandomTransferFunctionViewModel.cs

@@ -13,7 +13,7 @@ using System.Windows.Input;
 
 namespace ShakerApp.ViewModels
 {
-    internal class RandomTransferFunctionViewModel:ViewModelBase<IModel>
+    internal class RandomTransferFunctionViewModel:DisplayViewModelBase<IModel>
     {
 
         public void InitTransferFunctionData(double[] transferFunction)

+ 6 - 24
Avalonia/ShakerApp/ViewModels/ShakerConfig/ShakerConfigViewModel.cs

@@ -1,6 +1,7 @@
 using Avalonia.Collections;
 using Avalonia.Controls;
 using Avalonia.Markup.Xaml.MarkupExtensions;
+using ParquetSharp;
 using Shaker.Models;
 using ShakerApp.Tools;
 using System;
@@ -12,7 +13,7 @@ using System.Threading.Tasks;
 
 namespace ShakerApp.ViewModels
 {
-    internal class ShakerConfigViewModel : ViewModelBase<Shaker.Models.ShakerConfigModel>
+    internal class ShakerConfigViewModel : DisplayViewModelBase<Shaker.Models.ShakerConfigModel>
     {
         public const string TDMSConfigName = "ShakerConfig";
         [PropertyAssociation(nameof(ShakerConfigModel.DisplacementBias))]
@@ -127,14 +128,9 @@ namespace ShakerApp.ViewModels
         {
 
         }
-        public void SaveTdmsConfig(FSharp.Data.Tdms.File file)
+        public void SaveTdmsConfig(Dictionary<string,string> config)
         {
-            if(!file.Groups.Any(x=>x.Name ==TDMSConfigName))
-            {
-                var g = new FSharp.Data.Tdms.Group(TDMSConfigName, new List<FSharp.Data.Tdms.Property>(),new List<FSharp.Data.Tdms.Channel>());
-                file.Groups.Append(g);
-            }
-            var group = file.Groups.First(x=>x.Name == TDMSConfigName);
+            if (config == null) config = new Dictionary<string, string>();
             typeof(ShakerConfigModel).GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
                 .Select(x =>(x, x.GetValue(Model)))
                 .ToList()
@@ -146,25 +142,11 @@ namespace ShakerApp.ViewModels
                     }
                     if(x.x.FieldType.IsAssignableTo(typeof(IList)) || x.x.FieldType.IsArray)
                     {
-                        string value = string.Join("", x.Item2.GetBytes().Select(x => $"{x:X2}"));
-                        if (!group.Properties.Any(y=>y.Name == x.x.Name))
-                        {
-                            group.Properties.Append(new FSharp.Data.Tdms.Property(x.x.Name, typeof(string), value));
-                        }
-                    }
-                    else if(x.x.FieldType.IsEnum)
-                    {
-                        if (!group.Properties.Any(y => y.Name == x.x.Name))
-                        {
-                            group.Properties.Append(new FSharp.Data.Tdms.Property(x.x.Name, typeof(int), (int)x.Item2));
-                        }
+                        config[$"{nameof(ShakerConfigModel)}_{x.x.Name}"] = string.Join("", x.Item2.GetBytes().Select(x => $"{x:X2}")); 
                     }
                     else
                     {
-                        if (!group.Properties.Any(y => y.Name == x.x.Name))
-                        {
-                            group.Properties.Append(new FSharp.Data.Tdms.Property(x.x.Name, x.x.FieldType, x.Item2));
-                        }
+                        config[$"{nameof(ShakerConfigModel)}_{x.x.Name}"] = x.Item2?.ToString() ?? string.Empty;
                     }
                 });
         }

+ 1 - 1
Avalonia/ShakerApp/ViewModels/ShakerConfig/SweepConfigViewModel.cs

@@ -16,7 +16,7 @@ using System.Windows.Input;
 
 namespace ShakerApp.ViewModels
 {
-    internal class SweepConfigViewModel:ViewModelBase<Shaker.Models.SweepConfigModel>
+    internal class SweepConfigViewModel:DisplayViewModelBase<Shaker.Models.SweepConfigModel>
     {
         private float interval = 0.1f;
         List<OxyPlot.Series.LineSeries> lineSeries = new List<OxyPlot.Series.LineSeries>();

+ 1 - 1
Avalonia/ShakerApp/ViewModels/ShakerConfig/SweepItemViewModel.cs

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
 
 namespace ShakerApp.ViewModels
 {
-    internal class SweepItemViewModel:ViewModelBase<SweepItemModel>
+    internal class SweepItemViewModel:DisplayViewModelBase<SweepItemModel>
     {
         public SweepItemViewModel()
         {

+ 8 - 26
Avalonia/ShakerApp/ViewModels/ShakerControl/ShakerControlViewModel.cs

@@ -1,4 +1,5 @@
-using Shaker.Models;
+using ParquetSharp;
+using Shaker.Models;
 using ShakerApp.Tools;
 using System;
 using System.Collections;
@@ -11,7 +12,7 @@ using System.Windows.Input;
 
 namespace ShakerApp.ViewModels
 {
-    public class ShakerControlViewModel : ViewModelBase<ShakerControlModel>
+    public class ShakerControlViewModel : DisplayViewModelBase<ShakerControlModel>
     {
         public const string TDMSConfigName = "ShakerControl";
         public override bool CanResize => false;
@@ -22,15 +23,10 @@ namespace ShakerApp.ViewModels
         {
 
         }
-        public void SaveTdmsConfig(FSharp.Data.Tdms.File file)
+        public void SaveTdmsConfig(Dictionary<string,string> config)
         {
-            if (!file.Groups.Any(x => x.Name == TDMSConfigName))
-            {
-                var g = new FSharp.Data.Tdms.Group(TDMSConfigName, new List<FSharp.Data.Tdms.Property>(), new List<FSharp.Data.Tdms.Channel>());
-                file.Groups.Append(g);
-            }
-            var group = file.Groups.First(x => x.Name == TDMSConfigName);
-            Model.GetType().GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
+            if (config == null) config = new Dictionary<string, string>();
+            typeof(ShakerControlModel).GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
                 .Select(x => (x, x.GetValue(Model)))
                 .ToList()
                 .ForEach(x =>
@@ -41,25 +37,11 @@ namespace ShakerApp.ViewModels
                     }
                     if (x.x.FieldType.IsAssignableTo(typeof(IList)) || x.x.FieldType.IsArray)
                     {
-                        string value = string.Join("", x.Item2.GetBytes().Select(x => $"{x:X2}"));
-                        if (!group.Properties.Any(y => y.Name == x.x.Name))
-                        {
-                            group.Properties.Append(new FSharp.Data.Tdms.Property(x.x.Name, typeof(string), value));
-                        }
-                    }
-                    else if (x.x.FieldType.IsEnum)
-                    {
-                        if (!group.Properties.Any(y => y.Name == x.x.Name))
-                        {
-                            group.Properties.Append(new FSharp.Data.Tdms.Property(x.x.Name, typeof(int), (int)x.Item2));
-                        }
+                        config[$"{nameof(ShakerControlModel)}_{x.x.Name}"] = string.Join("", x.Item2.GetBytes().Select(x => $"{x:X2}"));
                     }
                     else
                     {
-                        if (!group.Properties.Any(y => y.Name == x.x.Name))
-                        {
-                            group.Properties.Append(new FSharp.Data.Tdms.Property(x.x.Name, x.x.FieldType, x.Item2));
-                        }
+                        config[$"{nameof(ShakerControlModel)}_{x.x.Name}"] = x.Item2?.ToString() ?? string.Empty;
                     }
                 });
         }

+ 1 - 1
Avalonia/ShakerApp/ViewModels/ShakerControl/ValveConfigItemViewModel.cs

@@ -3,7 +3,7 @@ using Shaker.Models;
 
 namespace ShakerApp.ViewModels
 {
-    public class ValveConfigItemViewModel:ViewModelBase<ValveConfigItemModel>
+    public class ValveConfigItemViewModel:DisplayViewModelBase<ValveConfigItemModel>
     {
         public ValveConfigItemViewModel():base()
         {

+ 21 - 19
Avalonia/ShakerApp/ViewModels/ShakerDataViewModel.cs

@@ -8,15 +8,15 @@ using System.Linq;
 using System.Runtime.CompilerServices;
 using System.Text;
 using System.Threading.Tasks;
+using ParquetSharp;
 
 namespace ShakerApp.ViewModels
 {
-    internal class ShakerDataViewModel:ViewModelBase<IModel>
+    internal class ShakerDataViewModel:DisplayViewModelBase<IModel>
     {
-        //[AllowNull]
-        //private NationalInstruments.Tdms.File file;
+        public ParquetSharp.ParquetFileWriter Writer => tdmsfile;
         [AllowNull]
-        private FSharp.Data.Tdms.File tdmsfile;
+        private ParquetFileWriter tdmsfile;
         private object _datalocker = new object();
         private Dictionary<Shaker.Models.AnalogType, List<(List<DataPoint>,Models.StatisticsModel)>> AnalogDataCache = new Dictionary<AnalogType, List<(List<DataPoint>, Models.StatisticsModel)>>();
         private ShakerDataViewModel()
@@ -40,27 +40,29 @@ namespace ShakerApp.ViewModels
             {
                 case RTStatus.SignalGen:
                     {
-                        //if (tdmsfile == null)
-                        //{
-                        //    tdmsfile = new FSharp.Data.Tdms.File(System.IO.Path.Combine(ViewModels.ShakerSettingViewModel.Instance.DataDirectory, $"{DateTime.Now:yyyy-MM-dd HH-mm-ss}.tdms"), new List<FSharp.Data.Tdms.Property>(), new List<FSharp.Data.Tdms.Group>());
-                        //    ShakerConfigViewModel.Instance.SaveTdmsConfig(tdmsfile);
-                        //    ShakerControlViewModel.Instance.SaveTdmsConfig(tdmsfile);
-                        //    MainPageViewModel.Instance.MainPage.SaveTdmsConfig(tdmsfile);
-                        //}
+                        if (tdmsfile == null)
+                        {
+                            Dictionary<string, string> config = new Dictionary<string, string>();
+                            ShakerControlViewModel.Instance.SaveTdmsConfig(config);
+                            MainPageViewModel.Instance.SaveTdmsConfig(config);
+                            DeviceMangerViewModel.Instance.CurrentDevice?.SaveTdmsConfig(config);
+                            tdmsfile = new ParquetFileWriter(System.IO.Path.Combine(ViewModels.ShakerSettingViewModel.Instance.DataDirectory, $"{DateTime.Now:yyyy-MM-dd HH-mm-ss}.dat"),MainPageViewModel.Instance.MainPage.Columns, Compression.Uncompressed,config);
+                        }
                     }
                     break;
                 default:
-                    //if (tdmsfile != null)
-                    //{
-                    //    tdmsfile = null;
-                    //}
+                    if (tdmsfile != null)
+                    {
+                        tdmsfile.Dispose();
+                        tdmsfile = null;
+                    }
                     break;
             }
             CalcAnalog(data, row, col);
-            //if(ShakerStatusViewModel.Instance.RTStatus == RTStatus.SignalGen && tdmsfile!=null)
-            //{
-            //    MainPageViewModel.Instance.MainPage.SaveTestData(tdmsfile);
-            //}
+            if (ShakerStatusViewModel.Instance.RTStatus == RTStatus.SignalGen && tdmsfile != null)
+            {
+                MainPageViewModel.Instance.SaveTestData(tdmsfile);
+            }
             CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.DATA)?.Publish(this, null);
         }
 

+ 1 - 1
Avalonia/ShakerApp/ViewModels/ShakerStatus/ShakerStatusViewModel.cs

@@ -18,7 +18,7 @@ using System.Windows.Input;
 
 namespace ShakerApp.ViewModels
 {
-    public sealed class ShakerStatusViewModel:ViewModelBase<ShakerStatusModel>
+    public sealed class ShakerStatusViewModel:DisplayViewModelBase<ShakerStatusModel>
     {
         private float givenDisplacement;
         private float measuredDisplacement;

+ 1 - 1
Avalonia/ShakerApp/ViewModels/SignalPreview/AnalogSignalPreviewViewModel.cs

@@ -15,7 +15,7 @@ using System.Diagnostics.CodeAnalysis;
 
 namespace ShakerApp.ViewModels
 {
-    public class AnalogSignalPreviewViewModel:ViewModelBase<IModel>,IDataPreview
+    public class AnalogSignalPreviewViewModel:DisplayViewModelBase<IModel>,IDataPreview
     {
         public string AttachTitle 
         { 

+ 1 - 1
Avalonia/ShakerApp/ViewModels/SignalPreview/SignalPreviewViewModel.cs

@@ -11,7 +11,7 @@ using System.Windows.Input;
 
 namespace ShakerApp.ViewModels
 {
-    internal class SignalPreviewViewModel:ViewModelBase<IModel>, IDataPreview
+    internal class SignalPreviewViewModel:DisplayViewModelBase<IModel>, IDataPreview
     {
         
         public AvaloniaList<AnalogSignalPreviewViewModel> SignalPreviews { get; } = new AvaloniaList<AnalogSignalPreviewViewModel>();

+ 1 - 1
Avalonia/ShakerApp/ViewModels/StatisticsViewModel.cs

@@ -8,7 +8,7 @@ using System.Threading.Tasks;
 
 namespace ShakerApp.ViewModels
 {
-    public class StatisticsViewModel:ViewModelBase<StatisticsModel>
+    public class StatisticsViewModel:DisplayViewModelBase<StatisticsModel>
     {
         public StatisticsViewModel():base()
         {

+ 1 - 1
Avalonia/ShakerApp/ViewModels/SweepControlItemViewModel.cs

@@ -8,7 +8,7 @@ using System.Threading.Tasks;
 
 namespace ShakerApp.ViewModels
 {
-    public class SweepControlItemViewModel : ViewModelBase<SweepControlItemModel>
+    public class SweepControlItemViewModel : DisplayViewModelBase<SweepControlItemModel>
     {
         public SweepControlItemViewModel()
         {

+ 119 - 102
Avalonia/ShakerApp/ViewModels/ViewModelBase.cs

@@ -19,17 +19,132 @@ using System.Runtime.CompilerServices;
 using System.Windows.Input;
 
 namespace ShakerApp.ViewModels;
+public abstract class ViewModelBase:ObservableObject
+{
+    [return: NotNull]
+    protected EventBus.EventBroker.EventData<TData> GetEvent<TData>() => (EventBroker.EventData<TData>)EventBroker.Instance.GetEvent<TData>();
 
-public abstract class ViewModelBase<TModel> : ObservableObject where TModel : IModel
+    [return: NotNull]
+    protected EventBus.EventBroker.EventData<TData, T> GetEvent<TData, T>() => (EventBroker.EventData<TData, T>)EventBroker.Instance.GetEvent<TData, T>();
+
+    [return: NotNull]
+    public EventBus.EventBroker.AnonymousEventData GetEvent([NotNull] string eventName) => (EventBroker.AnonymousEventData)EventBroker.Instance.GetEvent(eventName);
+
+    [return: NotNull]
+    public EventBus.EventBroker.AnonymousEventData<T> GetEvent<T>([NotNull] string eventName) => (EventBroker.AnonymousEventData<T>)EventBroker.Instance.GetEvent<T>(eventName);
+
+}
+public abstract class ViewModelBase<TModel>:ViewModelBase where TModel:IModel
 {
+    public ViewModelBase(TModel model) : this()
+    {
+        UpDateModel(model);
+    }
+    private List<FieldInfo> AllFields = new List<FieldInfo>();
+    private List<(string, IReadOnlyList<string>)> AllProperties = new List<(string, IReadOnlyList<string>)>();
+    public ViewModelBase()
+    {
+        if (typeof(TModel).IsAnsiClass && !typeof(TModel).IsAbstract)
+        {
+            Model = Activator.CreateInstance<TModel>();
+            AllFields = typeof(TModel).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToList();
+            this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
+                .Where(x => x.GetCustomAttribute<ViewModels.PropertyAssociationAttribute>() != null)
+                .ToList()
+                .ForEach(x =>
+                {
+                    var att = x.GetCustomAttribute<PropertyAssociationAttribute>();
+                    if (att == null || att.Names.Count == 0) return;
+                    AllProperties.Add((x.Name, att.Names));
+                });
+        }
+    }
+    [AllowNull]
+    private TModel model = default;
+
+    public virtual TModel Model { get => model; set => SetProperty(ref model, value); }
+    public virtual void ReceiveServiceModel(TModel model)
+    {
+        if (typeof(TModel).IsAbstract || typeof(TModel).IsInterface) return;
+        if (lastModel != null)
+        {
+            lastModel = (TModel)model.Clone();
+        }
+        UpDateModel(model);
+    }
+    public virtual void UpDateModel(TModel model)
+    {
+        if (typeof(TModel).IsAbstract || typeof(TModel).IsInterface) return;
+        List<string> changedNames = new List<string>();
+
+        if (model == null || AllFields.Count == 0) return;
+        if (Model == null)
+        {
+            changedNames = AllFields.Select(x => x.Name).ToList();
+        }
+        else
+        {
+            AllFields.ForEach(x =>
+            {
+                var lastvalue = x.GetValue(Model);
+                var fieldvalue = x.GetValue(model);
+                if (!Object.Equals(lastvalue, fieldvalue))
+                {
+                    changedNames.Add(x.Name);
+                }
+            });
+        }
+        Model = model;
+        if (changedNames.Count == 0) return;
+        List<string> nochanged = new List<string>();
+        changedNames.ForEach(x =>
+        {
+            bool state = false;
+            AllProperties.ForEach(y =>
+            {
+                if (y.Item2.Contains(x))
+                {
+                    OnPropertyChanged(y.Item1);
+                    state = true;
+                }
+            });
+            if (!state) nochanged.Add(x);
+        });
+        if (nochanged.Count == 0) return;
+        RefreshUI(nochanged);
+    }
+
+    public virtual void Init()
+    {
+    }
+
+    [AllowNull]
+    private protected TModel lastModel;
+    protected virtual void RefreshUI(List<string> changedNames)
+    {
+
+    }
+}
+
+
+
+
+
+
+
+
+
+
+public abstract class DisplayViewModelBase<TModel> : ViewModelBase<TModel> where TModel : IModel
+{
+    public DisplayViewModelBase() : base() { }
+    public DisplayViewModelBase(TModel model):base(model) { }
     private string Caption => (string)(App.Current?.FindResource("PromptTitle") ?? "");
     private string Yes => (string)(App.Current?.FindResource("PromptYes") ?? "");
     private string No => (string)(App.Current?.FindResource("PromptNo") ?? "");
     public virtual double Width => double.NaN;
     public virtual double Height => double.NaN;
     public virtual bool CanResize => true;
-    private List<FieldInfo> AllFields = new List<FieldInfo>();
-    private List<(string,IReadOnlyList<string>)> AllProperties = new List<(string, IReadOnlyList<string>)>();
     [AllowNull]
     public Action CloseWindowAction { get; set; }
     public ISukiToastManager ToastManager { get; } = new SukiToastManager();
@@ -51,23 +166,7 @@ public abstract class ViewModelBase<TModel> : ObservableObject where TModel : IM
     [AllowNull]
     public virtual bool CanCancel { get; } = typeof(TModel).IsAnsiClass && !typeof(TModel).IsAbstract;
 
-    public ViewModelBase()
-    {
-        if (typeof(TModel).IsAnsiClass && !typeof(TModel).IsAbstract)
-        {
-            Model = Activator.CreateInstance<TModel>();
-            AllFields = typeof(TModel).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToList();
-            this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
-                .Where(x => x.GetCustomAttribute<ViewModels.PropertyAssociationAttribute>() != null)
-                .ToList()
-                .ForEach(x =>
-                {
-                    var att = x.GetCustomAttribute<PropertyAssociationAttribute>();
-                    if (att == null || att.Names.Count ==0) return;
-                    AllProperties.Add((x.Name, att.Names));
-                });
-        }
-    }
+
     public bool ShowError(string msg)
     {
         
@@ -127,92 +226,12 @@ public abstract class ViewModelBase<TModel> : ObservableObject where TModel : IM
             .Dismiss().After(TimeSpan.FromSeconds(3))
             .Dismiss().ByClicking()
             .Queue());
-    }
-    public ViewModelBase(TModel model):this()
-    {
-        UpDateModel(model);
-    }
-
-
-    public virtual void ReceiveServiceModel(TModel model)
-    {
-        if (typeof(TModel).IsAbstract || typeof(TModel).IsInterface) return;
-        if(lastModel!=null)
-        {
-            lastModel = (TModel)model.Clone();
-        }
-        UpDateModel(model);
-    }
-    public virtual void UpDateModel(TModel model)
-    {
-        if (typeof(TModel).IsAbstract || typeof(TModel).IsInterface) return;
-        List<string> changedNames = new List<string>();
-
-        if (model == null || AllFields.Count==0) return;
-        if (Model == null)
-        {
-            changedNames = AllFields.Select(x => x.Name).ToList();
-        }
-        else
-        {
-            AllFields.ForEach(x =>
-            {
-                var lastvalue = x.GetValue(Model);
-                var fieldvalue = x.GetValue(model);
-                if (!Object.Equals(lastvalue, fieldvalue))
-                {
-                    changedNames.Add(x.Name);
-                }
-            });
-        }
-        Model = model;
-        if (changedNames.Count == 0) return;
-        List<string> nochanged = new List<string>();
-        changedNames.ForEach(x =>
-        {
-            bool state = false;
-            AllProperties.ForEach(y =>
-            {
-                if(y.Item2.Contains(x))
-                {
-                    OnPropertyChanged(y.Item1);
-                    state = true;
-                }
-            });
-            if (!state) nochanged.Add(x);
-        });
-        if (nochanged.Count == 0) return;
-        RefreshUI(nochanged);
-    }
-
-    public virtual void Init()
-    {
-    }
-
-    protected virtual void RefreshUI(List<string> changedNames)
-    {
-        
     }
     private bool cansave = false;
 
     public virtual string OKContent => "Save";
     public virtual string CancelContent =>"Cancel";
-    [AllowNull]
-    private TModel model = default;
-
-    public virtual TModel Model { get => model; set => SetProperty(ref model, value); }
 
-    [return: NotNull]
-    protected EventBus.EventBroker.EventData<TData> GetEvent<TData>() => (EventBroker.EventData<TData>)EventBroker.Instance.GetEvent<TData>();
-
-    [return: NotNull]
-    protected EventBus.EventBroker.EventData<TData, T> GetEvent<TData, T>() => (EventBroker.EventData<TData, T>)EventBroker.Instance.GetEvent<TData, T>();
-
-    [return: NotNull]
-    public EventBus.EventBroker.AnonymousEventData GetEvent([NotNull] string eventName) => (EventBroker.AnonymousEventData)EventBroker.Instance.GetEvent(eventName);
-
-    [return: NotNull]
-    public EventBus.EventBroker.AnonymousEventData<T> GetEvent<T>([NotNull] string eventName) => (EventBroker.AnonymousEventData<T>)EventBroker.Instance.GetEvent<T>(eventName);
 
     public ICommand SaveCommand => new RelayCommand(()=>
     {
@@ -230,8 +249,6 @@ public abstract class ViewModelBase<TModel> : ObservableObject where TModel : IM
         lastModel = default;
     }
 
-    [AllowNull]
-    protected TModel lastModel;
     [AllowNull]
     private Type content;
 

+ 8 - 4
Avalonia/ShakerApp/Views/MainPage/RandomMainPage.axaml

@@ -109,7 +109,8 @@
                                 <TextBlock IsVisible="{Binding Source={x:Static vm:SineMainPageViewModel.Instance}, Path=LineSeries[1].IsVisible}">
                                     <Run Text="{DynamicResource TargetAcceleration}" />
                                     <Run Text=":" />
-                                    <Run Text="{Binding Item.TargetAcceleration, StringFormat='{}{0:F4}g'}" />
+                                    <Run Text="{Binding Item.TargetAcceleration, StringFormat='{}{0:F4}'}" />
+                                    <Run Text="{StaticResource RandomValueUnit}" />
                                 </TextBlock>
                                 <TextBlock IsVisible="{Binding Source={x:Static vm:SineMainPageViewModel.Instance}, Path=LineSeries[2].IsVisible}">
                                     <Run Text="{DynamicResource UpStopAcceleration}" />
@@ -119,17 +120,20 @@
                                 <TextBlock IsVisible="{Binding Source={x:Static vm:SineMainPageViewModel.Instance}, Path=LineSeries[3].IsVisible}">
                                     <Run Text="{DynamicResource UpWarnAcceleration}" />
                                     <Run Text=":" />
-                                    <Run Text="{Binding Item.UpWarnAcceleration, StringFormat='{}{0:F4}g'}" />
+                                    <Run Text="{Binding Item.UpWarnAcceleration, StringFormat='{}{0:F4}'}" />
+                                    <Run Text="{StaticResource RandomValueUnit}" />
                                 </TextBlock>
                                 <TextBlock IsVisible="{Binding Source={x:Static vm:SineMainPageViewModel.Instance}, Path=LineSeries[4].IsVisible}">
                                     <Run Text="{DynamicResource DownStopAcceleration}" />
                                     <Run Text=":" />
-                                    <Run Text="{Binding Item.DownStopAcceleration, StringFormat='{}{0:F4}g'}" />
+                                    <Run Text="{Binding Item.DownStopAcceleration, StringFormat='{}{0:F4}'}" />
+                                    <Run Text="{StaticResource RandomValueUnit}" />
                                 </TextBlock>
                                 <TextBlock IsVisible="{Binding Source={x:Static vm:SineMainPageViewModel.Instance}, Path=LineSeries[5].IsVisible}">
                                     <Run Text="{DynamicResource DownWarnAcceleration}" />
                                     <Run Text=":" />
-                                    <Run Text="{Binding Item.DownWarnAcceleration, StringFormat='{}{0:F4}g'}" />
+                                    <Run Text="{Binding Item.DownWarnAcceleration, StringFormat='{}{0:F4}'}" />
+                                    <Run Text="{StaticResource RandomValueUnit}" />
                                 </TextBlock>
                             </StackPanel>
                         </oxy:TrackerControl.Content>

+ 2 - 2
Avalonia/ShakerApp/Views/MainWindow.axaml

@@ -82,8 +82,8 @@
             <Separator />
             <MenuItem
                 Command="{Binding OilControlCommand}"
-                CommandParameter="OilSourceControl"
-                Header="{DynamicResource OilSourceControl}">
+                CommandParameter="MenuOilSourceControl"
+                Header="{DynamicResource MenuOilSourceControl}">
                 <MenuItem.IsEnabled>
                     <MultiBinding Converter="{StaticResource MutliBoolConverter}">
                         <MultiBinding.Bindings>

+ 10 - 5
Avalonia/ShakerApp/Views/ShakerConfig/RandomConfigView.axaml

@@ -40,27 +40,32 @@
                                     <TextBlock>
                                         <Run Text="{DynamicResource Acceleration}" />
                                         <Run Text=":" />
-                                        <Run Text="{Binding Item.TargetAcceleration, StringFormat='{}{0:F4}g&#0178;/Hz'}" />
+                                        <Run Text="{Binding Item.TargetAcceleration, StringFormat='{}{0:F4}'}" />
+                                        <Run Text="{StaticResource RandomValueUnit}" />
                                     </TextBlock>
                                     <TextBlock>
                                         <Run Text="{DynamicResource UpStopAcceleration}" />
                                         <Run Text=":" />
-                                        <Run Text="{Binding Item.UpStopAcceleration, StringFormat='{}{0:F4}g&#0178;/Hz'}" />
+                                        <Run Text="{Binding Item.UpStopAcceleration, StringFormat='{}{0:F4}'}" />
+                                        <Run Text="{StaticResource RandomValueUnit}" />
                                     </TextBlock>
                                     <TextBlock>
                                         <Run Text="{DynamicResource UpWarnAcceleration}" />
                                         <Run Text=":" />
-                                        <Run Text="{Binding Item.UpWarnAcceleration, StringFormat='{}{0:F4}g&#0178;/Hz'}" />
+                                        <Run Text="{Binding Item.UpWarnAcceleration, StringFormat='{}{0:F4}'}" />
+                                        <Run Text="{StaticResource RandomValueUnit}" />
                                     </TextBlock>
                                     <TextBlock>
                                         <Run Text="{DynamicResource DownStopAcceleration}" />
                                         <Run Text=":" />
-                                        <Run Text="{Binding Item.DownStopAcceleration, StringFormat='{}{0:F4}g&#0178;/Hz'}" />
+                                        <Run Text="{Binding Item.DownStopAcceleration, StringFormat='{}{0:F4}'}" />
+                                        <Run Text="{StaticResource RandomValueUnit}" />
                                     </TextBlock>
                                     <TextBlock>
                                         <Run Text="{DynamicResource DownWarnAcceleration}" />
                                         <Run Text=":" />
-                                        <Run Text="{Binding Item.DownWarnAcceleration, StringFormat='{}{0:F4}g&#0178;/Hz'}" />
+                                        <Run Text="{Binding Item.DownWarnAcceleration, StringFormat='{}{0:F4}'}" />
+                                        <Run Text="{StaticResource RandomValueUnit}" />
                                     </TextBlock>
                                 </StackPanel>
                             </oxy:TrackerControl.Content>

+ 18 - 0
Avalonia/ShakerApp/Window1.axaml

@@ -0,0 +1,18 @@
+<suki:SukiWindow
+    x:Class="ShakerApp.Window1"
+    xmlns="https://github.com/avaloniaui"
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+    xmlns:suki="https://github.com/kikipoulet/SukiUI"
+    xmlns:vm="using:ShakerApp.ViewModels"
+    Title="Window1"
+    d:DesignHeight="450"
+    d:DesignWidth="800"
+    x:DataType="vm:MenuViewModel"
+    DataContext="{Binding Source={x:Static vm:MenuViewModel.Instance}}"
+    IsMenuVisible="{Binding IsMenuVisible}"
+    MenuItems="{Binding Menus, Converter={StaticResource MenuItemConverter}}"
+    mc:Ignorable="d">
+    Welcome to Avalonia!
+</suki:SukiWindow>

+ 13 - 0
Avalonia/ShakerApp/Window1.axaml.cs

@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace ShakerApp;
+
+public partial class Window1 : SukiUI.Controls.SukiWindow
+{
+    public Window1()
+    {
+        InitializeComponent();
+    }
+}

+ 18 - 0
Calc/FxpConvert.Common/ICalc.cs

@@ -1,5 +1,7 @@
 using System;
 using System.Collections.Generic;
+using System.Numerics;
+using System.Runtime.Intrinsics;
 using System.Text;
 
 namespace FxpConvert.Common
@@ -27,6 +29,10 @@ namespace FxpConvert.Common
         /// </summary>
         public IFFT FFT { get; }
         /// <summary>
+        /// Clamp
+        /// </summary>
+        public IClamp Clamp { get; }
+        /// <summary>
         /// 数组求和
         /// </summary>
         public IArraySum Sum { get; }
@@ -42,6 +48,18 @@ namespace FxpConvert.Common
         public float Rms(ref float value, uint count);
         public double Rms(ref double value,uint count);
     }
+    public interface IClamp
+    {
+        public void Clamp(ref float value, float min, float max, uint count);
+        public void Clamp(ref double value, double min, double max, uint count);
+        public void Clamp(ref float value,ref float min,ref float max, uint count);
+        public void Clamp(ref double value,ref double min,ref double max, uint count);
+
+        public void In(ref float value, float min, float max, uint count,ref int result);
+        public void In(ref double value, double min, double max, uint count, ref long result);
+        public void In(ref float value, ref float min, ref float max, uint count, ref int result);
+        public void In(ref double value, ref double min, ref double max, uint count, ref long result);
+    }
     public interface IFFT
     {
         public void FFT(double[] real, double[] imaginary);

+ 229 - 0
Calc/SIMDFxpConvert/SIMDCalc.cs

@@ -24,6 +24,7 @@ namespace SIMDFxpConvert
         public IFFT FFT { get; } = new SIMDFFT();
 
         public IArraySum Sum { get; } = new SIMDArraySum();
+        public IClamp Clamp { get; } = new SIMDClamp();
 
         public unsafe void Fill(ref float result, float value, uint count)
         {
@@ -74,6 +75,234 @@ namespace SIMDFxpConvert
             }
         }
     }
+
+    public sealed class SIMDClamp : IClamp
+    {
+        public unsafe void Clamp(ref float value, float min, float max, uint count)
+        {
+            if (count == 0) return;
+            ref var source = ref Unsafe.As<float, System.Runtime.Intrinsics.Vector512<float>>(ref value);
+            var minv = Vector512<float>.One * min;
+            var maxv = Vector512<float>.One * max;
+            uint onecount = (uint)(512 / (Unsafe.SizeOf<float>() * 8));
+            uint c = count / onecount;
+            uint c1 = count % onecount;
+            uint start = c * onecount;
+            for (int i = 0; i < c; i++)
+            {
+                ref var tempdesc = ref Unsafe.Add(ref source, i);
+                tempdesc =Vector512.Min(Vector512.Max(tempdesc,minv),maxv);
+            }
+            if (c1 > 0)
+            {
+                float* resultptr = (float*)Unsafe.AsPointer(ref value);
+                for (int i = 0; i < c1; i++)
+                {
+                    resultptr[start + i] = Math.Clamp(resultptr[start + i], min, max);
+                }
+            }
+        }
+
+        public unsafe void Clamp(ref double value, double min, double max, uint count)
+        {
+            ref var source = ref Unsafe.As<double, System.Runtime.Intrinsics.Vector512<double>>(ref value);
+            var minv = Vector512<double>.One * min;
+            var maxv = Vector512<double>.One * max;
+            uint onecount = (uint)(512 / (Unsafe.SizeOf<double>() * 8));
+            uint c = count / onecount;
+            uint c1 = count % onecount;
+            uint start = c * onecount;
+            for (int i = 0; i < c; i++)
+            {
+                ref var tempdesc = ref Unsafe.Add(ref source, i);
+                tempdesc = Vector512.Min(Vector512.Max(tempdesc, minv), maxv);
+            }
+            if (c1 > 0)
+            {
+                double* resultptr = (double*)Unsafe.AsPointer(ref value);
+                for (int i = 0; i < c1; i++)
+                {
+                    resultptr[start + i] = Math.Clamp(resultptr[start + i], min, max);
+                }
+            }
+        }
+
+        public unsafe void Clamp(ref float value, ref float min, ref float max, uint count)
+        {
+            if (count == 0) return;
+            ref var source = ref Unsafe.As<float, System.Runtime.Intrinsics.Vector512<float>>(ref value);
+            var minv = Unsafe.As<float,Vector512<float>>(ref min);
+            var maxv = Unsafe.As<float, Vector512<float>>(ref max);
+            uint onecount = (uint)(512 / (Unsafe.SizeOf<float>() * 8));
+            uint c = count / onecount;
+            uint c1 = count % onecount;
+            uint start = c * onecount;
+            for (int i = 0; i < c; i++)
+            {
+                ref var tempdesc = ref Unsafe.Add(ref source, i);
+                tempdesc = Vector512.Min(Vector512.Max(tempdesc, Unsafe.Add(ref minv, i)), Unsafe.Add(ref maxv, i));
+            }
+            if (c1 > 0)
+            {
+                float* resultptr = (float*)Unsafe.AsPointer(ref value);
+                for (int i = 0; i < c1; i++)
+                {
+                    resultptr[start + i] = Math.Clamp(resultptr[start + i], ((float*)Unsafe.AsPointer(ref min))[start + i], ((float*)Unsafe.AsPointer(ref max))[start + i]);
+                }
+            }
+        }
+
+        public unsafe void Clamp(ref double value, ref double min, ref double max, uint count)
+        {
+            if (count == 0) return;
+            ref var source = ref Unsafe.As<double, System.Runtime.Intrinsics.Vector512<double>>(ref value);
+            var minv = Unsafe.As<double, Vector512<double>>(ref min);
+            var maxv = Unsafe.As<double, Vector512<double>>(ref max);
+            uint onecount = (uint)(512 / (Unsafe.SizeOf<double>() * 8));
+            uint c = count / onecount;
+            uint c1 = count % onecount;
+            uint start = c * onecount;
+            for (int i = 0; i < c; i++)
+            {
+                ref var tempdesc = ref Unsafe.Add(ref source, i);
+                tempdesc = Vector512.Min(Vector512.Max(tempdesc, Unsafe.Add(ref minv, i)), Unsafe.Add(ref maxv, i));
+            }
+            if (c1 > 0)
+            {
+                double* resultptr = (double*)Unsafe.AsPointer(ref value);
+                for (int i = 0; i < c1; i++)
+                {
+                    resultptr[start + i] = Math.Clamp(resultptr[start + i], ((double*)Unsafe.AsPointer(ref min))[start + i], ((double*)Unsafe.AsPointer(ref max))[start + i]);
+                }
+            }
+        }
+
+
+
+        public unsafe void In(ref float value, float min, float max, uint count, ref int result)
+        {
+            if (count == 0) return ;
+            ref var source = ref Unsafe.As<float, System.Runtime.Intrinsics.Vector512<float>>(ref value);
+            var minv = Vector512<float>.One * min;
+            var maxv = Vector512<float>.One * max;
+            ref var resultv = ref Unsafe.As<int, Vector512<int>>(ref result);
+            uint onecount = (uint)(512 / (Unsafe.SizeOf<float>() * 8));
+            uint c = count / onecount;
+            uint c1 = count % onecount;
+            uint start = c * onecount;
+            for (int i = 0; i < c; i++)
+            {
+                ref var tempdesc = ref Unsafe.Add(ref source, i);
+                var r1 = Vector512.LessThan(tempdesc, minv).AsInt32();
+                var r2 = Vector512.GreaterThan(tempdesc, maxv).AsInt32();
+                ref var tempresv = ref Unsafe.Add(ref resultv, i);
+                tempresv = r1 | r2;
+            }
+            if (c1 > 0)
+            {
+                float* valueptr = (float*)Unsafe.AsPointer(ref value);
+                int* resultptr = (int*)Unsafe.AsPointer(ref result);
+                for (int i = 0; i < c1; i++)
+                {
+                    resultptr[start + i] = valueptr[start + i] < min || valueptr[start + i] > max ? -1 : 0;
+                }
+            }
+        }
+
+        public unsafe void In(ref double value, double min, double max, uint count, ref long result)
+        {
+            if (count == 0) return;
+            ref var source = ref Unsafe.As<double, System.Runtime.Intrinsics.Vector512<double>>(ref value);
+            var minv = Vector512<double>.One * min;
+            var maxv = Vector512<double>.One * max;
+            ref var resultv = ref Unsafe.As<long, Vector512<long>>(ref result);
+            uint onecount = (uint)(512 / (Unsafe.SizeOf<double>() * 8));
+            uint c = count / onecount;
+            uint c1 = count % onecount;
+            uint start = c * onecount;
+            for (int i = 0; i < c; i++)
+            {
+                ref var tempdesc = ref Unsafe.Add(ref source, i);
+                var r1 = Vector512.LessThan(tempdesc, minv).AsInt64();
+                var r2 = Vector512.GreaterThan(tempdesc, maxv).AsInt64();
+                ref var tempresv = ref Unsafe.Add(ref resultv, i);
+                tempresv = r1 | r2;
+            }
+            if (c1 > 0)
+            {
+                double* valueptr = (double*)Unsafe.AsPointer(ref value);
+                long* resultptr = (long*)Unsafe.AsPointer(ref result);
+                for (int i = 0; i < c1; i++)
+                {
+                    resultptr[start + i] = valueptr[start + i] < min || valueptr[start + i] > max ? -1 : 0;
+                }
+            }
+        }
+
+        public unsafe void In(ref float value, ref float min, ref float max, uint count, ref int result)
+        {
+            if (count == 0) return;
+            ref var source = ref Unsafe.As<float, System.Runtime.Intrinsics.Vector512<float>>(ref value);
+            var minv = Unsafe.As<float, Vector512<float>>(ref min);
+            var maxv = Unsafe.As<float, Vector512<float>>(ref max);
+            ref var resultv = ref Unsafe.As<int, Vector512<int>>(ref result);
+            uint onecount = (uint)(512 / (Unsafe.SizeOf<float>() * 8));
+            uint c = count / onecount;
+            uint c1 = count % onecount;
+            uint start = c * onecount;
+            for (int i = 0; i < c; i++)
+            {
+                ref var tempdesc = ref Unsafe.Add(ref source, i);
+                var r1 = Vector512.LessThan(tempdesc, Unsafe.Add(ref minv, i)).AsInt32();
+                var r2 = Vector512.GreaterThan(tempdesc, Unsafe.Add(ref maxv, i)).AsInt32();
+                ref var tempresv = ref Unsafe.Add(ref resultv, i);
+                tempresv = r1 | r2;
+            }
+            if (c1 > 0)
+            {
+                float* valueptr = (float*)Unsafe.AsPointer(ref value);
+                float* mintr = (float*)Unsafe.AsPointer(ref min);
+                float* maxptr = (float*)Unsafe.AsPointer(ref max);
+                int* resultptr = (int*)Unsafe.AsPointer(ref result);
+                for (int i = 0; i < c1; i++)
+                {
+                    resultptr[start + i] = valueptr[start + i] < mintr[start + i] || valueptr[start + i] > maxptr[start + i] ? -1 : 0;
+                }
+            }
+        }
+
+        public unsafe void In(ref double value, ref double min, ref double max, uint count, ref long result)
+        {
+            if (count == 0) return;
+            ref var source = ref Unsafe.As<double, System.Runtime.Intrinsics.Vector512<double>>(ref value);
+            var minv = Unsafe.As<double, Vector512<double>>(ref min);
+            var maxv = Unsafe.As<double, Vector512<double>>(ref max);
+            ref var resultv = ref Unsafe.As<long, Vector512<long>>(ref result);
+            uint onecount = (uint)(512 / (Unsafe.SizeOf<double>() * 8));
+            uint c = count / onecount;
+            uint c1 = count % onecount;
+            uint start = c * onecount;
+            for (int i = 0; i < c; i++)
+            {
+                ref var tempdesc = ref Unsafe.Add(ref source, i);
+                var r1 = Vector512.LessThan(tempdesc, Unsafe.Add(ref minv, i)).AsInt64();
+                var r2 = Vector512.GreaterThan(tempdesc, Unsafe.Add(ref maxv, i)).AsInt64();
+                ref var tempresv = ref Unsafe.Add(ref resultv, i);
+                tempresv = r1 | r2;
+            }
+            if (c1 > 0)
+            {
+                double* valueptr = (double*)Unsafe.AsPointer(ref value);
+                double* mintr = (double*)Unsafe.AsPointer(ref min);
+                double* maxptr = (double*)Unsafe.AsPointer(ref max);
+                long* resultptr = (long*)Unsafe.AsPointer(ref result);
+                for (int i = 0; i < c1; i++)
+                {
+                    resultptr[start + i] = valueptr[start + i] < mintr[start + i] || valueptr[start + i] > maxptr[start + i] ? -1 : 0;
+                }
+            }
+        }
+    }
     public sealed class SIMDArraySum : IArraySum
     {
         public unsafe float Rms(ref float value, uint count)

+ 1 - 1
Language/Zh-CN/Language.axaml

@@ -102,7 +102,7 @@
     <s:String x:Key="DeviceConnect">连接设备</s:String>
     <s:String x:Key="DisConnect">连接断开</s:String>
     <s:String x:Key="ConnectError">连接错误</s:String>
-    <s:String x:Key="OilSourceControl">油源控制</s:String>
+    <s:String x:Key="MenuOilSourceControl">油源控制</s:String>
     <s:String x:Key="MenuAbout">关于</s:String>
     <s:String x:Key="MenuSetting">设置</s:String>
     <s:String x:Key="Language">语言</s:String>

+ 16 - 11
Shaker.Model/Models/RandomDataModel.cs

@@ -17,6 +17,7 @@ namespace Shaker.Models
         public double[] TransferFunction = new double[0];
         public double[] DriverPSD = new double[0];
         public double DriverSpectrumRMS = 0;
+        public int CurrentLevelIndex = 0;
         public double TimeDomainRMS = 0;
         public RandomStatus RandomStatus = RandomStatus.Start;
         public RandomTestStep RandomTestStep = RandomTestStep.Start;
@@ -29,27 +30,31 @@ namespace Shaker.Models
         }
         public MainPageType MainPageType = MainPageType.RandomPage;
     }
-    public enum RandomStatus
+    public enum RandomStatus : uint
     {
         [Description("RandomStatusStart")]
-        Start,
+        Start = 1,
         [Description("RandomStatusUp")]
-        Up,
+        Up = 1 << 1,
         [Description("RandomStatusIdentify")]
-        Identify,
+        Identify = 1 << 2,
         [Description("RandomStatusStop")]
-        Stop,
+        Stop = 1 << 3,
         [Description("RandomStatusTest")]
-        Test,
+        Test = 1 << 4,
         [Description("RandomStatusIdentifyMaxLevel")]
-        IdentifyMaxLevel,
+        IdentifyMaxLevel = 1 << 5,
         [Description("RandomStatusIdentifyOpenLoop")]
-        IdentifyOpenLoop,
+        IdentifyOpenLoop = 1 << 6,
         [Description("RandomStatusTestUpStop")]
-        TestUpStop,
+        TestUpStop = 1 << 7,
         [Description("RandomStatusTestDownStop")]
-        TestDownStop,
+        TestDownStop = 1 << 8,
+        [Description("RandomStatusTestUpWarn")]
+        TestUpWarn = 1 << 9,
+        [Description("RandomStatusTestDownWarn")]
+        TestDownWarn = 1 << 10,
         [Description("RandomStatusTestRMSError")]
-        TestRMSError,
+        TestRMSError = 1 << 100,
     }
 }

+ 1 - 0
Shaker.Model/Topic.cs

@@ -43,6 +43,7 @@ namespace Shaker.Models
         public const string RESETERROR = "ResetError";
         public const string STARTSIGNALGEN = "StartSignalGen";
         public const string STARTRANDOMTEST = "StartRandomTest";
+        public const string RANDOMNEXTLEVEL = "RandomNextLevel";
         public const string DisConnect = "DisConnect";
         /// <summary>
         /// 连接后的初始消息

Файловите разлики са ограничени, защото са твърде много
+ 11711 - 11987
Shaker/Shaker.lvbitx


+ 9 - 1
Shaker/ShakerService.Control.cs

@@ -82,7 +82,15 @@ namespace ShakerService
         {
             Communication.Instance.Context.GetEvent(Topic.STARTRANDOMTEST)?.Subscrip((_, _) =>
             {
-
+                if (ServiceRandomConfigViewModel.Instance.PlanItems.Count == 0) return;
+                ServiceDataCacheViewModel.Instance.RandomStartStep = RandomTestStep.Test;
+                ServiceRandomConfigViewModel.Instance.InitRandomTest();
+                StartGen();
+            });
+            Communication.Instance.Context.GetEvent(Topic.RANDOMNEXTLEVEL)?.Subscrip((_, _) =>
+            {
+                if (ServiceRandomConfigViewModel.Instance.PlanItems.Count <= 1) return;
+                ServiceRandomConfigViewModel.Instance.NextLevel();
             });
         }
         [PropertyInit]

+ 33 - 0
Shaker/ShakerService.ReadFifo.cs

@@ -131,6 +131,7 @@ namespace ShakerService
                                     Log.Default.Debug(nameof(RandomTestStep.Stop));
                                     ShakerFpga.Instance.SignalStart.Value = false;
                                     ShakerFpga.Instance.SignalStop.Value = true;
+                                    ServiceDataCacheViewModel.Instance.IsSignalStart = false;
                                 }
                                 break;
                             case RandomTestStep.Identify:
@@ -172,6 +173,38 @@ namespace ShakerService
                                 }
                                 break;
                             case RandomTestStep.Test:
+                                {
+                                    ServiceRandomConfigViewModel.Instance.RandomData.RandomStatus = RandomStatus.Test;
+                                    bool isstop = false;
+                                    double[] drver = new double[ServiceRandomConfigViewModel.Instance.FFTFrameLength];
+                                    int index = 0;
+                                    for (int i = 0; i < ServiceConfigViewModel.Instance.AnalogCount; i++)
+                                    {
+                                        if (ServiceShakerConfigViewModel.Instance.AnalogSignals[i].AnalogType == AnalogType.DivideAcceleration)
+                                        {
+                                            var tempdata = ServiceDataCacheViewModel.Instance.GetAnalogData(i).Select(x => (double)x).ToArray();
+                                            tempdata = ServiceRandomConfigViewModel.Instance.RandomData.CalcPSD(tempdata);
+                                            tempdata = ServiceRandomConfigViewModel.Instance.RandomData.AddAccelerationPSD(index, tempdata, true);
+                                            index++;
+                                            ServiceRandomConfigViewModel.Instance.SpectrumRMS(tempdata);
+                                        }
+                                    }
+                                    ServiceRandomConfigViewModel.Instance.RandomData.SynthesisAcceleration();
+                                    index = ServiceShakerConfigViewModel.Instance.AnalogSignals.FindIndex(x => x.AnalogType == AnalogType.GivenDriver);
+                                    var data = ServiceDataCacheViewModel.Instance.GetAnalogData(index).Select(x => (double)x).ToArray();
+                                    data = ServiceRandomConfigViewModel.Instance.RandomData.CalcPSD(data);
+                                    ServiceRandomConfigViewModel.Instance.RandomData.AddDriverPSD(data, true);
+                                    ServiceRandomConfigViewModel.Instance.RandomData.CalcRandomTestDriver(ref isstop, ref drver);
+                                    ServiceRandomConfigViewModel.Instance.RandomData.CurrentTestTime += ServiceConfigViewModel.Instance.ReadFrameCount;
+                                    uint count = 0;
+                                    ShakerFpga.Instance.Read.Write(ref drver[0],0,(uint)drver.Length,ref count);
+                                    if(isstop)
+                                    {
+                                        ServiceRandomConfigViewModel.Instance.RandomData.RandomTestStep = RandomTestStep.Stop;
+                                        ServiceRandomConfigViewModel.Instance.RandomData.RandomStatus = RandomStatus.Stop;
+                                    }
+
+                                }
                                 break;
                         }
 

+ 8 - 1
Shaker/ShakerService.WriteConfig.cs

@@ -47,7 +47,14 @@ namespace ShakerService
                     ServiceDataCacheViewModel.Instance.CurrentTestType = Data.TestType.Random;
                     ServiceDataCacheViewModel.Instance.IsSignalStart = true;
                     ServiceRandomConfigViewModel.Instance.RandomData.RandomTestStep = ServiceDataCacheViewModel.Instance.RandomStartStep;
-                    ServiceRandomConfigViewModel.Instance.RandomData.ClearCache();
+                    if (ServiceDataCacheViewModel.Instance.RandomStartStep != Shaker.Models.RandomTestStep.Test)
+                    {
+                        ServiceRandomConfigViewModel.Instance.RandomData.ClearCache();
+                    }
+                    else
+                    {
+                        ServiceRandomConfigViewModel.Instance.RandomData.ClearAvgCache();
+                    }
                     ShakerFpga.Instance.OutSignal.Value = false;
                     ShakerFpga.Instance.TestType.Value = (ushort)Data.TestType.Random;
                     ShakerFpga.Instance.SignalStop.Value = false;

+ 14 - 1
Shaker/ViewModel/ServiceRandomConfigViewModel.cs

@@ -12,6 +12,7 @@ namespace ShakerService.ViewModel
 {
     internal class ServiceRandomConfigViewModel:BaseServiceViewModel<RandomConfigModel>
     {
+        
         public double[] PSDWindow { get;private set;} = new double[0];
         public double[] FixedWindow { get; private set; } = new double[0];
         public double HanningWindowCompensationCoefficient => Model.HanningWindowCompensationCoefficient;
@@ -39,6 +40,14 @@ namespace ShakerService.ViewModel
         public uint FFTFrameLength { get; private set; }
         public uint FFTHalfFrameLength { get; private set; }
         public double StopRMS =>Model.StopRMS;
+        public void NextLevel()
+        {
+            if (RandomData.CurrentLevelIndex >= PlanItems.Count - 1) return;
+            RandomData.CurrentLevelIndex++;
+            RandomData.CurrentTestTime = 0;
+            RandomData.CurrentLevelTestMaxTime = PlanItems[RandomData.CurrentLevelIndex].Time;
+            RandomData.CurrentTestLevel = PlanItems[RandomData.CurrentLevelIndex].Level;
+        }
         private ServiceRandomConfigViewModel()
         {
             Communication.Instance.DbConnection.CreateTable<RandomConfigModel>();
@@ -140,7 +149,11 @@ namespace ShakerService.ViewModel
             }
             return window;
         }
-        
+        public void InitRandomTest()
+        {
+            if (PlanItems.Count == 0) return;
+            RandomData.InitRandomTest();
+        }
         public ServiceRandomDataViewModel RandomData { get; } = new ServiceRandomDataViewModel();
         public static ServiceRandomConfigViewModel Instance { get; } = new ServiceRandomConfigViewModel();
     }

+ 76 - 2
Shaker/ViewModel/ServiceRandomDataViewModel.cs

@@ -7,6 +7,39 @@ namespace ShakerService.ViewModel
 {
     internal class ServiceRandomDataViewModel : BaseServiceViewModel<RandomDataModel>, IData
     {
+        public void CalcRandomTestDriver(ref bool isstop,ref double[] driver)
+        {
+            isstop = GetNextLevel();
+            CalcRandomTestDriver(CurrentTestLevel, ref driver);
+        }
+        public bool WarnJudge()
+        {
+        }
+        public void InitRandomTest()
+        {
+            if (ServiceRandomConfigViewModel.Instance.PlanItems.Count == 0) return;
+            CurrentTestLevel = ServiceRandomConfigViewModel.Instance.PlanItems.First().Level;
+            CurrentLevelIndex = 0;
+            CurrentTestTime = 0;
+            CurrentLevelTestMaxTime = ServiceRandomConfigViewModel.Instance.PlanItems.First().Time;
+        }
+        private bool GetNextLevel()
+        {
+            if (CurrentLevelIndex >= ServiceRandomConfigViewModel.Instance.PlanItems.Count - 1)
+            {
+                return CurrentTestTime>=CurrentLevelTestMaxTime;
+            }
+            if (CurrentTestTime < CurrentLevelTestMaxTime) return false;
+            CurrentLevelIndex++;
+            CurrentLevelTestMaxTime = ServiceRandomConfigViewModel.Instance.PlanItems[CurrentLevelIndex].Time;
+            CurrentTestLevel = ServiceRandomConfigViewModel.Instance.PlanItems[CurrentLevelIndex].Level;
+            CurrentTestTime = 0;
+            return false;
+        }
+        private double[] UpWarnLevel = new double[0];
+        private double[] UpStopLevel = new double[0];
+        private double[] DownWarnLevel = new double[0];
+        private double[] DownStopLevel = new double[0];
         private List<double> lastrms = new List<double>();
         public double[] IdentifyFirstDriver { get; set; } = new double[0];
         public bool HastIdentifyFirstDriver { get; private set; }
@@ -24,6 +57,7 @@ namespace ShakerService.ViewModel
         public double[] TransferFunction { get => Model.TransferFunction; set => Model.TransferFunction = value; }
         public double[] DriverPSD { get => Model.DriverPSD; set => Model.DriverPSD = value; }
         public double DriverSpectrumRMS { get=>Model.DriverSpectrumRMS;  set=>Model.DriverSpectrumRMS= value; }
+        public int CurrentLevelIndex { get=>Model.CurrentLevelIndex; set=>Model.CurrentLevelIndex = value; }
         public double CurrentTestLevel { get => Model.CurrentTestLevel; set => Model.CurrentTestLevel = value; }
         public uint CurrentTestTime { get => Model.CurrentTestTime; set => Model.CurrentTestTime = value; }
         public uint CurrentLevelTestMaxTime { get => Model.CurrentLevelTestMaxTime; set => Model.CurrentLevelTestMaxTime = value; }
@@ -44,6 +78,7 @@ namespace ShakerService.ViewModel
             CurrentAccelerationSpectrumRMS[index] = ServiceRandomConfigViewModel.Instance.SpectrumRMS(CurrentAccelerationPSD[index]);
             return CurrentAccelerationPSD[index];
         }
+
         public double[] SynthesisAcceleration()
         {
             switch (ServiceRandomConfigViewModel.Instance.SynthesisType)
@@ -110,6 +145,12 @@ namespace ShakerService.ViewModel
             DriverCache.Clear();
             lastrms.Clear();
         }
+        public void ClearAvgCache()
+        {
+            AccelerationPSDCache.Clear();
+            AccelerationPSDCache.AddRange(Enumerable.Range(0, ServiceShakerConfigViewModel.Instance.AccelerationSensorCount).Select(x => new PSDCache()));
+            DriverCache.Clear();
+        }
         public double[] CalcIdentifyFirstDriver()
         {
             //if (HastIdentifyFirstDriver) return IdentifyFirstDriver;
@@ -143,11 +184,10 @@ namespace ShakerService.ViewModel
             Complex[] random = Enumerable.Range(0, (int)ServiceRandomConfigViewModel.Instance.FFTHalfFrameLength).Select(x => Complex.Exp(new Complex(0,(0.5- rdm.NextDouble()) * 4 * Math.PI)) * tempvalue[x]).ToArray();
             Complex[] value = new Complex[ServiceRandomConfigViewModel.Instance.FFTFrameLength];
             value[0] = new Complex(tempvalue[0], 0);
-            var tempva = random.Select(x => x.Real).ToArray();
             Unsafe.CopyBlock(ref Unsafe.As<Complex, byte>(ref value[1]), ref Unsafe.As<Complex, byte>(ref random[0]), (uint)(random.Length * Unsafe.SizeOf<Complex>()));
             for (int i = 1; i < random.Length; i++)
             {
-                value[^i] = new Complex(random[i].Real, -random[i].Imaginary);
+                value[^i] = new Complex(random[i - 1].Real, -random[i - 1].Imaginary);
             }
             double[] real = value.Select(x => x.Real).ToArray();
             double[] img = value.Select(x => x.Imaginary).ToArray();
@@ -181,6 +221,40 @@ namespace ShakerService.ViewModel
             };
             return result;
         }
+        private void CalcRandomTestDriver(double level,ref double[] result)
+        {
+            double[] real = new double[TransferFunction.Length];
+            double[] img = new double[TransferFunction.Length];
+            ServiceDataCacheViewModel.Instance.Calc.Division.Division(ref Model.TransferFunction[0], ref Model.CurrentAccelerationSynthesisPSD[0], (uint)TransferFunction.Length,ref real[0]);
+            ServiceDataCacheViewModel.Instance.Calc.Multiply.Multiply(ref real[0], level, (uint)real.Length);
+            double[] whitenoise = CalcWhiteNoiseFFT().Select(x=>x.Magnitude).ToArray();
+            ServiceDataCacheViewModel.Instance.Calc.Multiply.Multiply(ref real[0], ref whitenoise[0], (uint)real.Length);
+            double rms = Math.Abs(ServiceDataCacheViewModel.Instance.Calc.Sum.Rms(ref real[0], (uint)real.Length))*ServiceRandomConfigViewModel.Instance.Sigma;
+            ServiceDataCacheViewModel.Instance.Calc.Clamp.Clamp(ref real[0], -rms, rms, (uint)real.Length);
+            ServiceDataCacheViewModel.Instance.Calc.FFT.IFFT(real, img);
+            ServiceDataCacheViewModel.Instance.Calc.Multiply.Multiply(ref real[0], ref ServiceRandomConfigViewModel.Instance.PSDWindow[0], (uint)real.Length);
+            ServiceDataCacheViewModel.Instance.Calc.FFT.FFT(real, img);
+            result = new double[real.Length * ServiceShakerConfigViewModel.Instance.SampleRate / ServiceRandomConfigViewModel.Instance.RandomSampleRate];
+            var linear = MathNet.Numerics.Interpolate.Linear(Enumerable.Range(0, real.Length).Select(x => (double)x), real);
+            for (int i = 0; i < result.Length; i++)
+            {
+                result[i] = linear.Interpolate((double)i / result.Length * real.Length);
+            };
+        }
+        private Complex[] CalcWhiteNoiseFFT()
+        {
+            Complex[] data = new Complex[ServiceRandomConfigViewModel.Instance.FFTFrameLength];
+            Random rdm = new Random();
+            for (int i = 0; i < ServiceRandomConfigViewModel.Instance.FFTHalfFrameLength; i++)
+            {
+                data[i] = Complex.FromPolarCoordinates(1, (0.5 - rdm.NextDouble()) * 2 * Math.PI) * ServiceRandomConfigViewModel.Instance.FFTFrameLength;
+                if (i == 0) continue;
+                data[^i] = Complex.FromPolarCoordinates(data[i].Magnitude, -data[i].Phase) * ServiceRandomConfigViewModel.Instance.FFTFrameLength;
+            }
+            data[0] = new Complex(1E-12 * ServiceRandomConfigViewModel.Instance.FFTFrameLength, 0);
+            data[ServiceRandomConfigViewModel.Instance.FFTHalfFrameLength] = data[0];
+            return data;
+        }
         public double[] CalcPSD(double[] data)
         {
             double[] result = new double[ServiceRandomConfigViewModel.Instance.FFTHalfFrameLength];

+ 0 - 1
ShakerControl.sln

@@ -308,7 +308,6 @@ Global
 		Communication\ActiveMQ\EasyMQ.Common\EasyMQ.Common.projitems*{4bc05ed0-2cea-4726-a0dc-e86810ad3fc3}*SharedItemsImports = 5
 		OxyPlot\OxyPlot\OxyPlot.projitems*{5c686af2-4400-492e-a4d9-bfdc1e77f332}*SharedItemsImports = 5
 		Communication\NetMQ\NetMQ.projitems*{73f9ac9a-8990-43af-b6d6-8bb5ec6f5be2}*SharedItemsImports = 13
-		TdmsFile\TdmsFile.projitems*{851f11b0-7e93-4c27-8225-88a1f6a2e9be}*SharedItemsImports = 5
 		Communication\ActiveMQ\ActiveMQ\Apache.NMS\Apache.NMS.projitems*{85bc49ac-7e23-4f13-894e-1669253f00bf}*SharedItemsImports = 13
 		Communication\ActiveMQ\ActiveMQ\Apache.NMS.ActiveMQ\Apache.NMS.ActiveMQ.projitems*{99cb1122-4d3a-434b-a11f-f997c940abc0}*SharedItemsImports = 13
 		Communication\ActiveMQ\ActiveMQ\Apache.NMS.ActiveMQ\Apache.NMS.ActiveMQ.projitems*{9ecbf78d-6e8e-4d13-88fd-27a8c8652dbf}*SharedItemsImports = 5

Някои файлове не бяха показани, защото твърде много файлове са промени