Quellcode durchsuchen

完成了plc通信调试,包括modbus协议和s7协议

luo vor 1 Monat
Ursprung
Commit
45202b2d45
44 geänderte Dateien mit 1116 neuen und 311 gelöschten Zeilen
  1. 8 6
      Avalonia/ShakerApp/App.axaml
  2. 31 0
      Avalonia/ShakerApp/Convert/BrushToColorConverter.cs
  3. 20 4
      Avalonia/ShakerApp/Convert/MathAddConverter.cs
  4. 1 1
      Avalonia/ShakerApp/Convert/MutliBoolConverter.cs
  5. 32 6
      Avalonia/ShakerApp/ViewModels/CommunicationViewModel.cs
  6. 3 7
      Avalonia/ShakerApp/ViewModels/DeviceManger/DeviceMangerViewModel.cs
  7. 3 3
      Avalonia/ShakerApp/ViewModels/MainPage/RandomMainPageViewModel.cs
  8. 1 1
      Avalonia/ShakerApp/ViewModels/MainPage/SineMainPageViewModel.cs
  9. 196 10
      Avalonia/ShakerApp/ViewModels/MainViewModel.cs
  10. 59 12
      Avalonia/ShakerApp/ViewModels/Oil/CircuitViewModel.cs
  11. 10 0
      Avalonia/ShakerApp/ViewModels/Oil/OilErrorInfoViewModel.cs
  12. 16 0
      Avalonia/ShakerApp/ViewModels/Oil/OilSourceAnalogViewModel.cs
  13. 95 18
      Avalonia/ShakerApp/ViewModels/Oil/OilSourceStatusViewModel.cs
  14. 2 2
      Avalonia/ShakerApp/ViewModels/Setting/ShakerSettingViewModel.cs
  15. 6 6
      Avalonia/ShakerApp/ViewModels/ShakerConfig/RandomConfigViewModel.cs
  16. 1 1
      Avalonia/ShakerApp/ViewModels/ShakerConfig/RandomTransferFunctionViewModel.cs
  17. 6 6
      Avalonia/ShakerApp/ViewModels/ShakerConfig/ShakerConfigViewModel.cs
  18. 6 6
      Avalonia/ShakerApp/ViewModels/ShakerConfig/SweepConfigViewModel.cs
  19. 7 7
      Avalonia/ShakerApp/ViewModels/ShakerControl/ShakerControlViewModel.cs
  20. 2 1
      Avalonia/ShakerApp/ViewModels/ShakerDataViewModel.cs
  21. 14 14
      Avalonia/ShakerApp/ViewModels/ShakerStatus/ShakerStatusViewModel.cs
  22. 40 0
      Avalonia/ShakerApp/Views/LEDControl.axaml
  23. 13 0
      Avalonia/ShakerApp/Views/LEDControl.axaml.cs
  24. 10 9
      Avalonia/ShakerApp/Views/MainWindow.axaml
  25. 7 4
      Avalonia/ShakerApp/Views/Oil/OilAnalogView.axaml
  26. 87 58
      Avalonia/ShakerApp/Views/Oil/OilControlView.axaml
  27. 1 2
      Avalonia/ShakerApp/Views/Oil/OilMinView.axaml
  28. 27 6
      Avalonia/ShakerApp/Views/Oil/PumpControlView.axaml
  29. 1 0
      Avalonia/ShakerApp/Views/Setting/LanguageView.axaml
  30. 1 1
      Communication/TcpEventBus/NettyServer.cs
  31. 2 2
      DBHelper/SQLite.cs
  32. 22 1
      Language/Zh-CN/Language.axaml
  33. 3 1
      PLCConnect/ModbusConnect/Modbus.cs
  34. 42 9
      PLCConnect/S7Connect/S7.cs
  35. 1 4
      Shaker.Model/Models/CircuitModel.cs
  36. 0 30
      Shaker.Model/Models/OilSourceModel.cs
  37. 14 7
      Shaker.Model/Topic.cs
  38. 217 0
      Shaker/ClassDiagram1.cd
  39. 59 52
      Shaker/OilSource/OilSource.cs
  40. 39 3
      Shaker/OilSource/OilSourcePumpAddressConfig.cs
  41. 8 6
      Shaker/Service.cs
  42. 0 2
      Shaker/ViewModel/ServiceDataCacheViewModel.cs
  43. 2 2
      ShakerControl.sln
  44. 1 1
      Timer/Timer/MultimediaTimer.cs

+ 8 - 6
Avalonia/ShakerApp/App.axaml

@@ -3,6 +3,7 @@
     xmlns="https://github.com/avaloniaui"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:convert="using:ShakerApp.Convert"
+    xmlns:local="using:ShakerApp"
     xmlns:s="using:System"
     xmlns:suki="https://github.com/kikipoulet/SukiUI"
     RequestedThemeVariant="Default">
@@ -29,19 +30,20 @@
         <convert:MenuItemConverter x:Key="MenuItemConverter" />
         <convert:GreaterThanEqualConverter x:Key="GreaterThanEqualConverter" />
         <convert:MultiConverter x:Key="MultiConverter" />
+        <convert:BrushToColorConverter x:Key="BrushToColorConverter" />
     </Application.Resources>
     <Application.Styles>
         <Style Selector=":is(Button):pointerover">
             <Setter Property="Cursor" Value="Hand" />
         </Style>
-        <Style Selector="Ellipse">
-            <Setter Property="Fill" Value="Green" />
+        <Style Selector="local|LEDControl">
+            <Setter Property="Foreground" Value="Green" />
         </Style>
-        <Style Selector="Ellipse.Error">
-            <Setter Property="Fill" Value="Red" />
+        <Style Selector="local|LEDControl.Error">
+            <Setter Property="Foreground" Value="Red" />
         </Style>
-        <Style Selector="Ellipse.Warn">
-            <Setter Property="Fill" Value="Yellow" />
+        <Style Selector="local|LEDControl.Warn">
+            <Setter Property="Foreground" Value="Yellow" />
         </Style>
         <Style Selector="ComboBox:pointerover">
             <Setter Property="Cursor" Value="Hand" />

+ 31 - 0
Avalonia/ShakerApp/Convert/BrushToColorConverter.cs

@@ -0,0 +1,31 @@
+using Avalonia.Controls;
+using Avalonia.Data.Converters;
+using Avalonia.Media;
+using Avalonia.Media.Immutable;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace ShakerApp.Convert
+{
+    internal class BrushToColorConverter : IValueConverter
+    {
+        public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+        {
+            byte transparency = 255;
+            if(parameter!= null && byte.TryParse(parameter.ToString(), out transparency))
+            {
+            }
+            if(value is ImmutableSolidColorBrush brush) return Color.FromArgb(transparency, brush.Color.R, brush.Color.G, brush.Color.B);
+            return Avalonia.Media.Colors.Transparent;
+        }
+
+        public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 20 - 4
Avalonia/ShakerApp/Convert/MathAddConverter.cs

@@ -14,13 +14,21 @@ namespace ShakerApp.Convert
         {
             // For add this is simple. just return the sum of the value and the parameter.
             // You may want to validate value and parameter in a real world App
-            return (double?)value + (double?)parameter;
+            if (value is double v && double.TryParse(parameter!.ToString(), out var val))
+            {
+                return v + val;
+            }
+            return 0;
         }
 
         public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
         {
             // If we want to convert back, we need to subtract instead of add.
-            return (double?)value - (double?)parameter;
+            if (value is double v && double.TryParse(parameter!.ToString(), out var val))
+            {
+                return v - val;
+            }
+            return 0;
         }
     }
     public class SubtractionConverter : IValueConverter
@@ -29,13 +37,21 @@ namespace ShakerApp.Convert
         {
             // For add this is simple. just return the sum of the value and the parameter.
             // You may want to validate value and parameter in a real world App
-            return (double?)value - (double?)parameter;
+            if (value is double v && double.TryParse(parameter!.ToString(), out var val))
+            {
+                return v - val;
+            }
+            return 0;
         }
 
         public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
         {
             // If we want to convert back, we need to subtract instead of add.
-            return (double?)value + (double?)parameter;
+            if (value is double v && double.TryParse(parameter!.ToString(), out var val))
+            {
+                return v + val;
+            }
+            return 0;
         }
     }
 

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

@@ -22,7 +22,7 @@ namespace ShakerApp.Convert
                      return false;
                  }
              }).ToList();
-            bool v = false;
+            bool v =  true;
             bools.ForEach(x => v &= x);
             if (parameter == null) return v;
             else return !v;

+ 32 - 6
Avalonia/ShakerApp/ViewModels/CommunicationViewModel.cs

@@ -45,7 +45,7 @@ namespace ShakerApp.ViewModels
                         LogViewModel.Instance.AddLog($"{LocalCommunication.IP}:{LocalCommunication.Port}连接断开", LogType.Error);
                         LocalIsConnect = false;
                         MainViewModel.Default.ShowToast(App.Current?.FindResource("DisConnect") + "", Avalonia.Controls.Notifications.NotificationType.Error);
-                        GetEvent(Topic.DisConnect).Publish(this, null);
+                        GetEvent(Topic.DisConnect).Publish(this, null);                        
                     };
                 }
                 if (LocalCommunication!.IsLan == (ShakerSettingViewModel.Instance.RemoteControlModel == Models.RemoteControlModel.Lan))
@@ -113,7 +113,17 @@ namespace ShakerApp.ViewModels
         {
             ServiceIsStart = false;
             if (ShakerSettingViewModel.Instance.WorkingMode == Models.WorkingMode.Remote) return;
-            if (ServiceCommunication == null) return;
+            if (ServiceCommunication == null)
+            {
+                ServiceCommunication = (ICommunication.ICommunication)Activator.CreateInstance(CommunicationViewModelTypes[ShakerSettingViewModel.Instance.RemoteControlModel == Models.RemoteControlModel.Lan])!;
+                ServiceCommunication.Disconnected += (_, _) =>
+                {
+                    LogViewModel.Instance.AddLog($"{ServiceCommunication.IP}:{ServiceCommunication.Port}连接断开", LogType.Error);
+                    ServiceIsStart = false;
+                    MainViewModel.Default.ShowToast(App.Current?.FindResource("DisConnect") + "", Avalonia.Controls.Notifications.NotificationType.Error);
+                    GetEvent(Topic.DisConnect).Publish(this, null);
+                };
+            }
             try
             {
                 if (ServiceCommunication.IsLan == (ShakerSettingViewModel.Instance.RemoteControlModel == Models.RemoteControlModel.Lan))
@@ -125,7 +135,11 @@ namespace ShakerApp.ViewModels
                     }
                     else
                     {
-                        if (ServiceCommunication.IsConnected) return;
+                        if (!ServiceCommunication.IsConnected)
+                        {
+                            ServiceCommunication.StartService(ip, port);
+                        }
+
                     }
                 }
                 else
@@ -139,7 +153,11 @@ namespace ShakerApp.ViewModels
             {
             }
             if (ServiceCommunication == null) ServiceIsStart = false;
-            else ServiceIsStart = ServiceCommunication.IsConnected;
+            else ServiceIsStart = true;
+            if(ServiceIsStart)
+            {
+                MainViewModel.Default.InitServiceMsg();
+            }
         }
         public void StopService()
         {
@@ -148,13 +166,21 @@ namespace ShakerApp.ViewModels
             if (ShakerSettingViewModel.Instance.WorkingMode == Models.WorkingMode.Remote || ServiceCommunication ==null || !ServiceCommunication.IsConnected) return;
             ServiceCommunication?.Close();
         }
-        public static CommunicationViewModel Intance { get; } = new CommunicationViewModel();
+        public static CommunicationViewModel Instance { get; } = new CommunicationViewModel();
         [AllowNull]
         public ICommunication.ICommunication LocalCommunication { get; private set; }
         [AllowNull]
 
         public ICommunication.ICommunication ServiceCommunication { get; private set; }
         public bool LocalIsConnect { get => localIsConnect; set =>SetProperty(ref localIsConnect , value); }
-        public bool ServiceIsStart { get => serviceIsStart; set =>SetProperty(ref serviceIsStart , value); }
+        public bool ServiceIsStart 
+        { 
+            get => serviceIsStart;
+            set
+            {
+                SetProperty(ref serviceIsStart, value);
+                ShakerSettingViewModel.Instance.ServieIsStart = value;
+            }
+        }
     }
 }

+ 3 - 7
Avalonia/ShakerApp/ViewModels/DeviceManger/DeviceMangerViewModel.cs

@@ -153,8 +153,8 @@ namespace ShakerApp.ViewModels
             if (CurrentDevice == null) return;
             Msg = App.Current?.FindResource("DeviceConnecting") + "";
             PopupVisibily = true;
-            await Task.Run(() => CommunicationViewModel.Intance.Connect(CurrentDevice.IP, CurrentDevice.Port));
-            if (CommunicationViewModel.Intance.LocalIsConnect)
+            await Task.Run(() => CommunicationViewModel.Instance.Connect(CurrentDevice.IP, CurrentDevice.Port));
+            if (CommunicationViewModel.Instance.LocalIsConnect)
             {
                 CloseWindowAction?.Invoke();
                 ShowToast(App.Current?.FindResource("DeviceConnectSuccess") + "", Avalonia.Controls.Notifications.NotificationType.Error);
@@ -165,7 +165,7 @@ namespace ShakerApp.ViewModels
         public ICommand DisConnectDeviceCommand => new CommunityToolkit.Mvvm.Input.RelayCommand(DisConnectDevice);
         private void DisConnectDevice()
         {
-            CommunicationViewModel.Intance.DisConnect();
+            CommunicationViewModel.Instance.DisConnect();
             CurrentDevice = null;
 
         }
@@ -175,10 +175,6 @@ namespace ShakerApp.ViewModels
             set => SetProperty(ref currentDevice, value);
         }
 
-        private void SendBroadcastMsg()
-        {
-            
-        }
 
     }
 }

+ 3 - 3
Avalonia/ShakerApp/ViewModels/MainPage/RandomMainPageViewModel.cs

@@ -97,17 +97,17 @@ namespace ShakerApp.ViewModels
             });
             GetEvent<AllConfig>().Subscrip((_, _) =>
             {
-                CommunicationViewModel.Intance.LocalCommunication?.GetEvent<RandomDataModel>().Subscrip((_, args) =>
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent<RandomDataModel>().Subscrip((_, args) =>
                 {
                     UpdateData(new List<IResultDataModel>() { args.Data });
                     
                 });
-                CommunicationViewModel.Intance.LocalCommunication?.GetEvent(nameof(RandomDataModel.TransferFunction)).Subscrip((sender, args) =>
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent(nameof(RandomDataModel.TransferFunction)).Subscrip((sender, args) =>
                 {
                     if (args.Data.Length > 0 && args.Data[0] is double[] func)
                     {
                         Tools.DispatherInovke.Inovke(()=> ShowTransferFunction(func));
-                        CommunicationViewModel.Intance.ServiceCommunication?.GetEvent(nameof(RandomDataModel.TransferFunction)).Publish(this, null, func);
+                        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(nameof(RandomDataModel.TransferFunction)).Publish(this, null, func);
                     }
                 });
             });

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

@@ -90,7 +90,7 @@ namespace ShakerApp.ViewModels
             });
             GetEvent<AllConfig>().Subscrip((_, _) =>
             {
-                CommunicationViewModel.Intance.LocalCommunication?.GetEvent<List<SineDataModel>>().Subscrip((_, args) =>
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent<List<SineDataModel>>().Subscrip((_, args) =>
                 {
                     UpdateData(args.Data.Cast<IResultDataModel>().ToList());
                 });

+ 196 - 10
Avalonia/ShakerApp/ViewModels/MainViewModel.cs

@@ -55,6 +55,10 @@ public class MainViewModel : ViewModelBase<IModel>
                     break;
             }
         };
+        GetEvent(Topic.DisConnect).Subscrip((sender, args) =>
+        {
+            CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.DisConnect).Publish(this);
+        });
         this.GetType().Assembly.GetTypes()
             .Where(x => x.IsAnsiClass && !x.IsAbstract && x.IsAssignableTo(typeof(ObservableObject)))
             .ToList()
@@ -69,24 +73,24 @@ public class MainViewModel : ViewModelBase<IModel>
     {
 
     }
-    public Avalonia.Media.IBrush TitleColor => CommunicationViewModel.Intance.LocalIsConnect ? Brushes.Black : Brushes.Red;
+    public Avalonia.Media.IBrush TitleColor => CommunicationViewModel.Instance.LocalIsConnect ? Brushes.Black : Brushes.Red;
     public ICommand ExitCommand => new RelayCommand(Exit);
     private void Exit()
     {
         ShowAsk(App.Current?.FindResource("PromptExitMsg")+"",()=>
         {
-            CommunicationViewModel.Intance.LocalCommunication?.GetEvent(Topic.STOP)?.Publish(this, null);
+            CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.STOP)?.Publish(this, null);
             Environment.Exit(0);
         });
     }
     private string GetTitle()
     {
         string s = string.Empty;
-        if(ViewModels.CommunicationViewModel.Intance.LocalIsConnect)
+        if(ViewModels.CommunicationViewModel.Instance.LocalIsConnect)
         {
             if(MainPageViewModel.Instance.MainPageType == MainPageType.StartPage)
             {
-                s = $"{App.Current?.FindResource("Title")}-{ViewModels.DeviceMangerViewModel.Instance.CurrentDevice!.Name}[{CommunicationViewModel.Intance.LocalCommunication.IP}:{CommunicationViewModel.Intance.LocalCommunication.Port}]";
+                s = $"{App.Current?.FindResource("Title")}-{ViewModels.DeviceMangerViewModel.Instance.CurrentDevice!.Name}[{CommunicationViewModel.Instance.LocalCommunication.IP}:{CommunicationViewModel.Instance.LocalCommunication.Port}]";
             }
             else
             {
@@ -99,6 +103,11 @@ public class MainViewModel : ViewModelBase<IModel>
         }
         return s;
     }
+    public ICommand ConnectCommand => new RelayCommand(Connect);
+    private void Connect()
+    {
+        CommunicationViewModel.Instance.Connect(ViewModels.ShakerSettingViewModel.Instance.RemoteIP, ViewModels.ShakerSettingViewModel.Instance.RemotePort);
+    }
 
     public override string Title => GetTitle();
 
@@ -229,7 +238,7 @@ public class MainViewModel : ViewModelBase<IModel>
     public ICommand CloseCommand => new RelayCommand(Close);
     private void Close()
     {
-        CommunicationViewModel.Intance.LocalCommunication?.GetEvent(Topic.STOP)?.Publish(this, null);
+        CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.STOP)?.Publish(this, null);
         Environment.Exit(0);
     }
     public ICommand DebugCommand => new RelayCommand<string?>(Debug);
@@ -287,6 +296,168 @@ public class MainViewModel : ViewModelBase<IModel>
             await window.ShowDialog(desktop.MainWindow!);
         }
     }
+    public void InitServiceMsg()
+    {
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent<string>(Topic.SERVICERESULT).Subscrip((_, _) => Topic.SERVICERESULT);
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent<Shaker.Models.AllConfig>(Shaker.Models.Topic.SYNCCONFIG).Subscrip((_, _) =>
+        {
+            return new AllConfig()
+            {
+                OilSource = OilSourceStatusViewModel.Instance.Model,
+                RandomConfig = RandomConfigViewModel.Instance.Model,
+                ShakerConfig = ShakerConfigViewModel.Instance.Model,
+                ShakerControl = ShakerControlViewModel.Instance.Model,
+                ShakerStatus = ShakerStatusViewModel.Instance.Model,
+                SweepConfig = SweepConfigViewModel.Instance.Model,
+            };
+        });
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent<DeviceInfoModel?>().Publish(this, ViewModels.DeviceMangerViewModel.Instance.CurrentDevice?.Model);
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent<RandomTestStep>().Subscrip((sender, args) =>
+        {
+            CommunicationViewModel.Instance.LocalCommunication?.GetEvent<RandomTestStep>().Publish(this, args.Data);
+        });
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent<MainPageType>().Subscrip((sender, args) =>
+        {
+            CommunicationViewModel.Instance.LocalCommunication?.GetEvent<MainPageType>().Publish(this, args.Data);
+        });
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Shaker.Models.Topic.START).Subscrip((_, _) =>
+        {
+            CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.START).Publish(this);
+        });
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.STARTRANDOMTEST).Subscrip((_, _) =>
+        {
+            CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.STARTRANDOMTEST).Publish(this);
+        });
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.STOP).Subscrip((_, _) =>
+        {
+            CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.STOP).Publish(this);
+        });
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.RISETABLE).Subscrip((_, _) =>
+        {
+            CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.RISETABLE).Publish(this);
+        });
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.ZEROCHANGE).Subscrip((_, e) =>
+        {
+            CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.ZEROCHANGE).Publish(this, null, e.Data[0]);
+        });
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.DROPTABLE).Subscrip((_, _) =>
+        {
+            CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.DROPTABLE).Publish(this);
+        });
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.STOPSIGNALGEN).Subscrip((_, _) =>
+        {
+            CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.STOPSIGNALGEN).Publish(this);
+        });
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.VALVEPOWER).Subscrip((_, e) =>
+        {
+            CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.STOPSIGNALGEN).Publish(this, null, e.Data[0]);
+        });
+
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.STARTSIGNALGEN).Subscrip((_, _) =>
+        {
+            CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.STARTSIGNALGEN).Publish(this);
+        });
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.RESETERROR).Subscrip((_, _) =>
+        {
+            CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.RESETERROR).Publish(this);
+        });
+
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.CircuitPressure).Subscrip((sender, args) =>
+        {
+            if (args.Data.Length >= 2 && args.Data[0] is int index && args.Data[1] is float pressure)
+            {
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.CircuitPressure).Publish(this, null, index, pressure);
+            }
+        });
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.CircuitStart).Subscrip((sender, args) =>
+        {
+            if (args.Data.Length >= 2 && args.Data[0] is int index && args.Data[1] is bool state)
+            {
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.CircuitStart).Publish(this, null, index, state);
+            }
+        });
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.CircuitLoad).Subscrip((sender, args) =>
+        {
+            if (args.Data.Length >= 2 && args.Data[0] is int index && args.Data[1] is bool state)
+            {
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.CircuitLoad).Publish(this, null, index, state);
+            }
+        });
+
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.ForerunnerPressure).Subscrip((sender, args) =>
+        {
+            if (args.Data.Length >= 1 && args.Data[0] is float pressure)
+            {
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.ForerunnerPressure).Publish(this, null, pressure);
+            }
+        });
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.ForerunnerStart).Subscrip((sender, args) =>
+        {
+            if (args.Data.Length >= 1 && args.Data[0] is bool state)
+            {
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.ForerunnerStart).Publish(this, null, state);
+            }
+        });
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.ForerunnerLoad).Subscrip((sender, args) =>
+        {
+            if (args.Data.Length >= 1 && args.Data[0] is bool state)
+            {
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.ForerunnerLoad).Publish(this, null, state);
+            }
+        });
+
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.AssistantPressure).Subscrip((sender, args) =>
+        {
+            if (args.Data.Length >= 1 && args.Data[0] is float pressure)
+            {
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.AssistantPressure).Publish(this, null, pressure);
+            }
+        });
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.AssistantStart).Subscrip((sender, args) =>
+        {
+            if (args.Data.Length >= 1 && args.Data[0] is bool state)
+            {
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.AssistantStart).Publish(this, null, state);
+            }
+        });
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.AssistantLoad).Subscrip((sender, args) =>
+        {
+            if (args.Data.Length >= 1 && args.Data[0] is bool state)
+            {
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.AssistantLoad).Publish(this, null, state);
+            }
+        });
+
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.CirculatePressure).Subscrip((sender, args) =>
+        {
+            if (args.Data.Length >= 1 && args.Data[0] is float pressure)
+            {
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.CirculatePressure).Publish(this, null, pressure);
+            }
+        });
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.CirculateStart).Subscrip((sender, args) =>
+        {
+            if (args.Data.Length >= 1 && args.Data[0] is bool state)
+            {
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.CirculateStart).Publish(this, null, state);
+            }
+        });
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.CirculateLoad).Subscrip((sender, args) =>
+        {
+            if (args.Data.Length >= 1 && args.Data[0] is bool state)
+            {
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.CirculateLoad).Publish(this, null, state);
+            }
+        });
+
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.OilEmergencyStop).Subscrip((sender, args) =>
+        {
+            if (args.Data.Length >= 1 && args.Data[0] is bool state)
+            {
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.OilEmergencyStop).Publish(this, null, state);
+            }
+        });
+    }
     public ICommand RandomConfigCommand => new RelayCommand<string?>(RandomConfig);
     private async void RandomConfig(string? p)
     {
@@ -316,8 +487,8 @@ public class MainViewModel : ViewModelBase<IModel>
     {
         ShowAsk(App.Current?.FindResource("AskDisconnect") + "", () =>
         {
-            CommunicationViewModel.Intance.LocalCommunication?.GetEvent(Topic.STOP).Publish(this, null);
-            CommunicationViewModel.Intance.DisConnect();
+            CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.STOP).Publish(this, null);
+            CommunicationViewModel.Instance.DisConnect();
         });
     }
     public ICommand ShakerControlCommand => new RelayCommand<string?>(ShakerControl);
@@ -359,7 +530,19 @@ public class MainViewModel : ViewModelBase<IModel>
     public void SyncConfig()
     {
         LogViewModel.Instance.AddLog($"同步控制器设置参数");
-        var config= CommunicationViewModel.Intance.LocalCommunication.GetEvent<Shaker.Models.AllConfig>(Shaker.Models.Topic.SYNCCONFIG).Publish(this);
+        CommunicationViewModel.Instance.LocalCommunication?.GetEvent<DeviceInfoModel?>().Subscrip((sender, args) =>
+        {
+            if (args.Data == null)
+            {
+                DeviceMangerViewModel.Instance.CurrentDevice = null;
+            }
+            else
+            {
+                if (DeviceMangerViewModel.Instance.CurrentDevice == null) DeviceMangerViewModel.Instance.CurrentDevice = new DeviceInfoViewModel();
+                DeviceMangerViewModel.Instance.CurrentDevice.UpDateModel(args.Data);
+            }
+        });
+        var config= CommunicationViewModel.Instance.LocalCommunication?.GetEvent<Shaker.Models.AllConfig>(Shaker.Models.Topic.SYNCCONFIG).Publish(this);
         if(config ==null)
         {
             LogViewModel.Instance.AddLog($"同步控制器设置参数失败", LogType.Error);
@@ -367,14 +550,17 @@ public class MainViewModel : ViewModelBase<IModel>
                 .WithTitle(App.Current?.FindResource("PromptTitle") + "")
                 .WithContent(App.Current?.FindResource("GetConfigError")+"")
                 .Queue();
-            CommunicationViewModel.Intance.LocalCommunication.Close();
+            CommunicationViewModel.Instance.LocalCommunication?.Close();
             return;
         }
         LogViewModel.Instance.AddLog($"同步控制器设置参数成功");
         GetEvent<AllConfig>().Publish(this, config);
-        CommunicationViewModel.Intance.LocalCommunication.GetEvent(Topic.START).Publish(this);
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent<AllConfig>(Topic.SYNCCONFIG).Publish(this,null, config);
+        CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.START).Publish(this);
         OnPropertyChanged(nameof(Title));
         OnPropertyChanged(nameof(TitleColor));
+        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent<DeviceInfoModel?>().Publish(this, DeviceMangerViewModel.Instance.CurrentDevice?.Model);
+        
     }
     public static MainViewModel Default { get; } = new MainViewModel();
 

+ 59 - 12
Avalonia/ShakerApp/ViewModels/Oil/CircuitViewModel.cs

@@ -1,14 +1,18 @@
 using Avalonia.Controls;
 using CommunityToolkit.Mvvm.Input;
 using Shaker.Models;
+using System;
 using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
 using System.Linq.Expressions;
+using System.Threading.Tasks;
 using System.Windows.Input;
 
 namespace ShakerApp.ViewModels
 {
     public class CircuitViewModel : ViewModelBase<CircuitModel>
     {
+        private int waitTime = 2000;
         public CircuitViewModel()
         {
             Content = typeof(Views.PumpControlView);
@@ -23,34 +27,76 @@ namespace ShakerApp.ViewModels
         [PropertyAssociation(nameof(CircuitModel.IsEnablePressure))]
         public bool IsEnablePressure => Model.IsEnablePressure; 
         [PropertyAssociation(nameof(CircuitModel.Name))]
-        public string Name => Model.Name; 
-        [PropertyAssociation(nameof(CircuitModel.Pressure))]
-        public float Pressure { get => Model.Pressure; set => SetProperty(ref Model.Pressure, value); }
+        public string Name => Model.Name;
+        private float _Pressure = 0;
+        public float Pressure { get => _Pressure; set => SetProperty(ref _Pressure, value); }
         [PropertyAssociation(nameof(CircuitModel.IsStart))]
         public bool IsStart { get => Model.IsStart; set => SetProperty(ref Model.IsStart, value); }
 
         [PropertyAssociation(nameof(CircuitModel.Name))]
         public bool IsVisible => !string.IsNullOrEmpty(Name);
         public ICommand StartCommand => new RelayCommand(Start);
-        private void Start()
+        private async void Start()
         {
-            IsStart = true;
+            StartAction?.Invoke(true);
+            await Task.Delay(waitTime);
+            if(!IsStart)
+            {
+                string msg = string.Format(App.Current?.FindResource("PumpStartError")+"",App.Current?.FindResource(Name));
+                OilSourceStatusViewModel.Instance.ShowToast(msg, Avalonia.Controls.Notifications.NotificationType.Error);
+                LogViewModel.Instance.AddLog(msg, LogType.Error);
+                return;
+            }
+            LogViewModel.Instance.AddLog(string.Format(App.Current?.FindResource("PumpStartSuccess")+"",App.Current?.FindResource(Name)), LogType.Info);
         }
         public ICommand StopCommand => new RelayCommand(Stop);
-        private void Stop()
+        private async void Stop()
         {
-            IsStart = false;
+            StartAction?.Invoke(false); 
+            await Task.Delay(waitTime);
+            if (IsStart)
+            {
+                string msg = string.Format(App.Current?.FindResource("PumpStopError") + "", App.Current?.FindResource(Name));
+                OilSourceStatusViewModel.Instance.ShowToast(msg, Avalonia.Controls.Notifications.NotificationType.Error);
+                LogViewModel.Instance.AddLog(msg, LogType.Error);
+                return;
+            }
+            LogViewModel.Instance.AddLog(string.Format(App.Current?.FindResource("PumpStopSuccess") + "", App.Current?.FindResource(Name)), LogType.Info);
         }
         public ICommand LoadPressureCommand => new RelayCommand(LoadPressure);
-        private void LoadPressure()
+        private async void LoadPressure()
         {
-            IsLoadPressure = true;
+            LoadAction?.Invoke(true); 
+            await Task.Delay(waitTime);
+            if (!IsLoadPressure)
+            {
+                string msg = string.Format(App.Current?.FindResource("PumpLoadError") + "", App.Current?.FindResource(Name));
+                OilSourceStatusViewModel.Instance.ShowToast(msg, Avalonia.Controls.Notifications.NotificationType.Error);
+                LogViewModel.Instance.AddLog(msg, LogType.Error);
+                return;
+            }
+            LogViewModel.Instance.AddLog(string.Format(App.Current?.FindResource("PumpLoadSuccess") + "", App.Current?.FindResource(Name)), LogType.Info);
         }
+        [AllowNull]
+        public Action<bool> StartAction { get; set; }
+        [AllowNull]
+        public Action<bool> LoadAction { get; set; }
+        [AllowNull]
+        public Action<float> PressureAction { get; set; }
         public ICommand UnloadPressureCommand => new RelayCommand(UnloadPressure);
-        private void UnloadPressure()
+        private async void UnloadPressure()
         {
             Pressure = 0;
-            IsLoadPressure = false;
+            LoadAction?.Invoke(false); 
+            await Task.Delay(waitTime);
+            if (IsLoadPressure)
+            {
+                string msg = string.Format(App.Current?.FindResource("PumpUnloadError") + "", App.Current?.FindResource(Name));
+                OilSourceStatusViewModel.Instance.ShowToast(msg, Avalonia.Controls.Notifications.NotificationType.Error);
+                LogViewModel.Instance.AddLog(msg, LogType.Error);
+                return;
+            }
+            LogViewModel.Instance.AddLog(string.Format(App.Current?.FindResource("PumpUnloadSuccess") + "", App.Current?.FindResource(Name)), LogType.Info);
         }
         protected override void OnPropertyChanged(PropertyChangedEventArgs e)
         {
@@ -62,7 +108,8 @@ namespace ShakerApp.ViewModels
         }
         private void SetPressure(float pressure)
         {
-
+            PressureAction?.Invoke(pressure);
+            LogViewModel.Instance.AddLog(string.Format(App.Current?.FindResource("PumpPressure") + "", App.Current?.FindResource(Name), pressure,"MPa"), LogType.Info);
         }
     }
 }

+ 10 - 0
Avalonia/ShakerApp/ViewModels/Oil/OilErrorInfoViewModel.cs

@@ -1,5 +1,6 @@
 
 using Shaker.Models;
+using Avalonia.Controls;
 
 namespace ShakerApp.ViewModels
 {
@@ -19,5 +20,14 @@ namespace ShakerApp.ViewModels
         public bool Status { get => Model.Status; set => SetProperty(ref Model.Status, value); }
         [PropertyAssociation(nameof(ErrorInfoModel.Name))]
         public bool IsVisibily => !string.IsNullOrEmpty(Name);
+        public override void UpDateModel(ErrorInfoModel model)
+        {
+            bool lastStatus = Model.Status;
+            base.UpDateModel(model);
+            if(model.Status!= lastStatus && model.Status)
+            {
+                LogViewModel.Instance.AddLog($"{App.Current?.FindResource(Name)}{App.Current?.FindResource("OilError")}");
+            }
+        }
     }
 }

+ 16 - 0
Avalonia/ShakerApp/ViewModels/Oil/OilSourceAnalogViewModel.cs

@@ -1,5 +1,6 @@
 using Avalonia.Controls;
 using Shaker.Models;
+using System.Collections.Generic;
 
 namespace ShakerApp.ViewModels
 {
@@ -38,5 +39,20 @@ namespace ShakerApp.ViewModels
         public bool IsUpper => IsUpperError || IsUpperWarn;
         [PropertyAssociation(nameof(OilSourceAnalogModel.IsLowerWarn), nameof(OilSourceAnalogModel.IsLowerError))]
         public bool IsLower => IsLowerError || IsLowerWarn;
+
+        private readonly string[] errornames = new string[4] { "UpperWarn", "LowerWarn", "UpperError", "LowerError" };
+        public override void UpDateModel(OilSourceAnalogModel model)
+        {
+            bool[] laststate = new bool[4] { IsUpperWarn, IsLowerWarn, IsUpperError, IsLowerError };
+            base.UpDateModel(model);
+            bool[] currentstate = new bool[4] { IsUpperWarn, IsLowerWarn, IsUpperError, IsLowerError };
+            for(int i=0;i<laststate.Length;i++)
+            {
+                if (laststate[i]!= currentstate[i] && !laststate[i])
+                {
+                    LogViewModel.Instance.AddLog($"{App.Current?.FindResource(Name)}{App.Current?.FindResource(errornames[i])}", i >= 2 ? LogType.Error : LogType.Warn);
+                }
+            }
+        }
     }
 }

+ 95 - 18
Avalonia/ShakerApp/ViewModels/Oil/OilSourceStatusViewModel.cs

@@ -1,4 +1,5 @@
 using Avalonia.Collections;
+using Avalonia.Controls;
 using Shaker.Models;
 using System;
 using System.Collections.Generic;
@@ -35,26 +36,48 @@ namespace ShakerApp.ViewModels
             GetEvent<AllConfig>().Subscrip((sender, args) =>
             {
                 UpDateModel(args.Data.OilSource);
-                if(CommunicationViewModel.Intance.ServiceIsStart)
-                {
-                    CommunicationViewModel.Intance.ServiceCommunication.GetEvent<OilSourceStatusModel>().Publish(this, args.Data.OilSource);
-                }
-                CommunicationViewModel.Intance.LocalCommunication?.GetEvent<OilSourceStatusModel>()?.Subscrip((sender, args) =>
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent<OilSourceStatusModel>()?.Subscrip((sender, args) =>
                 {
                     UpDateModel(args.Data);
-                    CommunicationViewModel.Intance.ServiceCommunication?.GetEvent<OilSourceStatusModel>()?.Publish(this, args.Data);
+                    CommunicationViewModel.Instance.ServiceCommunication?.GetEvent<OilSourceStatusModel>()?.Publish(this, args.Data);
+                });
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.OilStatusChanged).Subscrip((sender, args) =>
+                {
+                    if(args.Data.Length>=1 && args.Data[0] is bool state)
+                    {
+                        string msg = App.Current?.FindResource(state ? "OilConnect" : "OilDisConnect") + "";
+                        if(state)
+                        {
+                            MainViewModel.Default.ShowToast(msg, Avalonia.Controls.Notifications.NotificationType.Information);
+                        }
+                        else
+                        {
+                            MainViewModel.Default.ShowToast(msg, Avalonia.Controls.Notifications.NotificationType.Error);
+                        }
+                        LogViewModel.Instance.AddLog(msg,state ? LogType.Info : LogType.Error);
+                        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.OilStatusChanged).Publish(this,null, state);
+                    }
                 });
             });
         }
         static OilSourceStatusViewModel()
         {
 
+        }
+        public void EmergencyStop()
+        {
+            CommunicationViewModel.Instance.LocalCommunication.GetEvent(Topic.OilEmergencyStop).Publish(this, null, true);
         }
         public override bool CanCancel => false;
         public static OilSourceStatusViewModel Instance { get; } = new OilSourceStatusViewModel();
 
 
-        public CircuitViewModel Circulate { get; } = new CircuitViewModel();
+        public CircuitViewModel Circulate { get; } = new CircuitViewModel()
+        {
+            PressureAction = (pressure) => CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.CirculatePressure).Publish(OilSourceStatusViewModel.Instance.Forerunner, null, pressure),
+            StartAction = (state) => CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.CirculateStart).Publish(OilSourceStatusViewModel.Instance.Forerunner, null, state),
+            LoadAction = (state) => CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.CirculateLoad).Publish(OilSourceStatusViewModel.Instance.Forerunner, null, state)
+        };
         [PropertyAssociation(nameof(OilSourceStatusModel.IsRemote))]
         public bool IsRemote
         {
@@ -72,46 +95,100 @@ namespace ShakerApp.ViewModels
         /// <summary>
         /// 先导油路
         /// </summary>
-        public CircuitViewModel Forerunner { get; } = new CircuitViewModel();
+        public CircuitViewModel Forerunner { get; } = new CircuitViewModel()
+        {
+            PressureAction = (pressure)=> CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.ForerunnerPressure).Publish(OilSourceStatusViewModel.Instance.Forerunner, null, pressure),
+            StartAction = (state) => CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.ForerunnerStart).Publish(OilSourceStatusViewModel.Instance.Forerunner, null, state),
+            LoadAction = (state) => CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.ForerunnerLoad).Publish(OilSourceStatusViewModel.Instance.Forerunner, null, state)
+        };
         /// <summary>
         /// 主油路
         /// </summary>
         public AvaloniaList<IndexValueItemViewModel<CircuitViewModel>> Circuit { get; } = new AvaloniaList<IndexValueItemViewModel<CircuitViewModel>>();
         public AvaloniaList<OilSourceAnalogViewModel> OilSourceAnalogs { get; } = new AvaloniaList<OilSourceAnalogViewModel>();
 
-        public CircuitViewModel Assistant { get; } = new CircuitViewModel();
+        public CircuitViewModel Assistant { get; } = new CircuitViewModel()
+        {
+            PressureAction = (pressure) => CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.AssistantPressure).Publish(OilSourceStatusViewModel.Instance.Forerunner, null, pressure),
+            StartAction = (state) => CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.AssistantStart).Publish(OilSourceStatusViewModel.Instance.Forerunner, null, state),
+            LoadAction = (state) => CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.AssistantLoad).Publish(OilSourceStatusViewModel.Instance.Forerunner, null, state)
+        };
 
         public AvaloniaList<OilErrorInfoViewModel> OilErrors { get; } = new AvaloniaList<OilErrorInfoViewModel>();
+        public bool IsError => (OilErrors.Count > 0 ? OilErrors.Any(x => x.Status) : false) | (OilSourceAnalogs.Count > 0 ? OilSourceAnalogs.Any(x => x.IsError) : false);
         protected override void Cancel()
         {
         }
         public override void UpDateModel(OilSourceStatusModel model)
         {
+            bool lasterror = IsError;
             base.UpDateModel(model);
             Forerunner?.UpDateModel(model.Forerunner);
             Circulate?.UpDateModel(model.Circulate);
-            if (Circuit.Count > 0)
+            int count = Circuit.Count;
+            if (count < model.Circuit.Count)
             {
-                for (int i = 0; i < Circuit.Count; i++)
+                for (int i = 0; i <  model.Circuit.Count-count; i++)
                 {
-                    Circuit[i]?.Value?.UpDateModel(model.Circuit[i]);
+                    Circuit.Add(new IndexValueItemViewModel<CircuitViewModel>(Circuit.Count + 1, new CircuitViewModel()));
+                }
+            }
+            else if (count > model.Circuit.Count)
+            {
+                Circuit.RemoveRange(model.Circuit.Count, count - model.Circuit.Count);
+            }
+            if (model.Circuit.Count > 0)
+            {
+                for (int i = 0; i < model.Circuit.Count; i++)
+                {
+                    Circuit[i].Value?.UpDateModel(model.Circuit[i]);
+                    Circuit[i].Value.StartAction = (state) => CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.CircuitStart).Publish(this, null,i, state);
+                    Circuit[i].Value.LoadAction = (state) => CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.CircuitLoad).Publish(this, null,i, state);
+                    Circuit[i].Value.PressureAction = (pressure) => CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.CircuitPressure).Publish(this, null,i, pressure);
                 }
             }
             Assistant?.UpDateModel(model.Assistant);
-            if (OilSourceAnalogs.Count > 0)
+            count = OilSourceAnalogs.Count;
+            if (count < model.OilSourceAnalogs.Count)
+            {
+                for (int i = 0; i < model.OilSourceAnalogs.Count-count; i++)
+                {
+                    OilSourceAnalogs.Add(new OilSourceAnalogViewModel());
+                }
+            }
+            else if (count > model.OilSourceAnalogs.Count)
+            {
+                OilSourceAnalogs.RemoveRange(model.OilSourceAnalogs.Count, count - model.OilSourceAnalogs.Count);
+            }
+            if (model.OilSourceAnalogs.Count > 0)
             {
-                for (int i = 0; i < OilSourceAnalogs.Count; i++)
+                for (int i = 0; i < model.OilSourceAnalogs.Count; i++)
                 {
-                    OilSourceAnalogs[i]?.UpDateModel(model.OilSourceAnalogs[i]);
+                    OilSourceAnalogs[i].UpDateModel(model.OilSourceAnalogs[i]);
                 }
             }
-            if (OilErrors.Count > 0)
+            count = OilErrors.Count;
+            if (count < model.OilErrors.Count)
+            {
+                for (int i = 0; i < model.OilErrors.Count-count; i++)
+                {
+                    OilErrors.Add(new OilErrorInfoViewModel());
+                }
+            }
+            else if (count > model.OilErrors.Count)
+            {
+                OilErrors.RemoveRange(model.OilErrors.Count, count - model.OilErrors.Count);
+            }
+            if (model.OilErrors.Count > 0)
             {
-                for (int i = 0; i < OilErrors.Count; i++)
+                for (int i = 0; i < model.OilErrors.Count; i++)
                 {
-                    OilErrors[i]?.UpDateModel(model.OilErrors[i]);
+                    OilErrors[i].UpDateModel(model.OilErrors[i]);
                 }
             }
+            OnPropertyChanged(nameof(IsError));
+            if(IsError!=lasterror)OnPropertyChanged(nameof(OilStatus));
         }
+        public string OilStatus => App.Current?.FindResource(IsError ? "OilError" : "OilSuccess") + "";
     }
 }

+ 2 - 2
Avalonia/ShakerApp/ViewModels/Setting/ShakerSettingViewModel.cs

@@ -179,11 +179,11 @@ namespace ShakerApp.ViewModels
         public ICommand StopServiceCommand => new RelayCommand(StopService);
         private void StartService()
         {
-            ServieIsStart = true;
+            CommunicationViewModel.Instance.StartService("0.0.0.0", RemotePort);
         }
         public void StopService()
         {
-            ServieIsStart = false;
+            CommunicationViewModel.Instance.StopService();
         }
         protected override void Save()
         {

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

@@ -163,16 +163,16 @@ namespace ShakerApp.ViewModels
             GetEvent<AllConfig>().Subscrip((sender, args) =>
             {
                 UpDateModel(args.Data.RandomConfig);
-                if (CommunicationViewModel.Intance.ServiceIsStart)
+                if (CommunicationViewModel.Instance.ServiceIsStart)
                 {
-                    CommunicationViewModel.Intance.ServiceCommunication.GetEvent<RandomConfigModel>().Publish(this, args.Data.RandomConfig);
+                    CommunicationViewModel.Instance.ServiceCommunication.GetEvent<RandomConfigModel>().Publish(this, args.Data.RandomConfig);
                 }
-                CommunicationViewModel.Intance.LocalCommunication?.GetEvent<RandomConfigModel>()?.Subscrip((sender, args) =>
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent<RandomConfigModel>()?.Subscrip((sender, args) =>
                 {
                     UpDateModel(args.Data);
                     Refresh();
                     RandomMainPageViewModel.Instance.SetRefSpectrum(datas);
-                    CommunicationViewModel.Intance.ServiceCommunication?.GetEvent<RandomConfigModel>()?.Publish(this, Model);
+                    CommunicationViewModel.Instance.ServiceCommunication?.GetEvent<RandomConfigModel>()?.Publish(this, Model);
                 });
                 
             });
@@ -563,8 +563,8 @@ namespace ShakerApp.ViewModels
         protected override void Save()
         {
             base.Save();
-            CommunicationViewModel.Intance.LocalCommunication?.GetEvent<RandomConfigModel>()?.Publish(this, Model);
-            CommunicationViewModel.Intance.ServiceCommunication?.GetEvent<RandomConfigModel>()?.Publish(this, Model);
+            CommunicationViewModel.Instance.LocalCommunication?.GetEvent<RandomConfigModel>()?.Publish(this, Model);
+            CommunicationViewModel.Instance.ServiceCommunication?.GetEvent<RandomConfigModel>()?.Publish(this, Model);
             RandomMainPageViewModel.Instance.SetRefSpectrum(datas);
         }
 

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

@@ -90,7 +90,7 @@ namespace ShakerApp.ViewModels
                 ShowError(App.Current?.FindResource("RandomPlanTimeIsZero") + "");
                 return;
             }
-            CommunicationViewModel.Intance.LocalCommunication?.GetEvent(Topic.STARTRANDOMTEST).Publish(this, null);
+            CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.STARTRANDOMTEST).Publish(this, null);
             CloseWindowAction?.Invoke();
         }
         protected override void Cancel()

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

@@ -107,16 +107,16 @@ namespace ShakerApp.ViewModels
             {
                 UpDateModel(args.Data.ShakerConfig);
                 OnPropertyChanged(nameof(AccelerationSensorCount));
-                if(CommunicationViewModel.Intance.ServiceIsStart)
+                if(CommunicationViewModel.Instance.ServiceIsStart)
                 {
-                    CommunicationViewModel.Intance.ServiceCommunication.GetEvent<Shaker.Models.ShakerConfigModel>().Publish(this, args.Data.ShakerConfig);
+                    CommunicationViewModel.Instance.ServiceCommunication.GetEvent<Shaker.Models.ShakerConfigModel>().Publish(this, args.Data.ShakerConfig);
                 }
-                CommunicationViewModel.Intance.LocalCommunication?.GetEvent<Shaker.Models.ShakerConfigModel>()?.Subscrip((sender, args) =>
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent<Shaker.Models.ShakerConfigModel>()?.Subscrip((sender, args) =>
                 {
                     if (args.Data == null) return;
                     UpDateModel(args.Data);
                     OnPropertyChanged(nameof(AccelerationSensorCount));
-                    CommunicationViewModel.Intance.ServiceCommunication?.GetEvent<Shaker.Models.ShakerConfigModel>()?.Publish(this, args.Data);
+                    CommunicationViewModel.Instance.ServiceCommunication?.GetEvent<Shaker.Models.ShakerConfigModel>()?.Publish(this, args.Data);
                 });
                 GetEvent(Models.Topic.InitSeries).Publish(this, null);
             });
@@ -152,8 +152,8 @@ namespace ShakerApp.ViewModels
                 return;
             }
             base.Save();
-            CommunicationViewModel.Intance.LocalCommunication?.GetEvent<Shaker.Models.ShakerConfigModel>()?.Publish(this, Model);
-            CommunicationViewModel.Intance.ServiceCommunication?.GetEvent<Shaker.Models.ShakerConfigModel>()?.Publish(this, Model);
+            CommunicationViewModel.Instance.LocalCommunication?.GetEvent<Shaker.Models.ShakerConfigModel>()?.Publish(this, Model);
+            CommunicationViewModel.Instance.ServiceCommunication?.GetEvent<Shaker.Models.ShakerConfigModel>()?.Publish(this, Model);
         }
     }
 }

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

@@ -177,16 +177,16 @@ namespace ShakerApp.ViewModels
             GetEvent<AllConfig>().Subscrip((sender, args) =>
             {
                 UpDateModel(args.Data.SweepConfig);
-                if(CommunicationViewModel.Intance.ServiceIsStart)
+                if(CommunicationViewModel.Instance.ServiceIsStart)
                 {
-                    CommunicationViewModel.Intance.ServiceCommunication.GetEvent<SweepConfigModel>().Publish(this, args.Data.SweepConfig);
+                    CommunicationViewModel.Instance.ServiceCommunication.GetEvent<SweepConfigModel>().Publish(this, args.Data.SweepConfig);
                 }
-                CommunicationViewModel.Intance.LocalCommunication?.GetEvent<SweepConfigModel>()?.Subscrip((sender, args) =>
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent<SweepConfigModel>()?.Subscrip((sender, args) =>
                 {
                     UpDateModel(args.Data);
                     Refresh();
                     SineMainPageViewModel.Instance.SetRefSpectrum(datas);
-                    CommunicationViewModel.Intance.ServiceCommunication?.GetEvent<SweepConfigModel>()?.Publish(this, Model);
+                    CommunicationViewModel.Instance.ServiceCommunication?.GetEvent<SweepConfigModel>()?.Publish(this, Model);
                 });
             });
         }
@@ -553,8 +553,8 @@ namespace ShakerApp.ViewModels
         {
             base.Save();
             SineMainPageViewModel.Instance.SetRefSpectrum(datas);
-            CommunicationViewModel.Intance.LocalCommunication?.GetEvent<SweepConfigModel>()?.Publish(this, Model);
-            CommunicationViewModel.Intance.ServiceCommunication?.GetEvent<SweepConfigModel>()?.Publish(this, Model);
+            CommunicationViewModel.Instance.LocalCommunication?.GetEvent<SweepConfigModel>()?.Publish(this, Model);
+            CommunicationViewModel.Instance.ServiceCommunication?.GetEvent<SweepConfigModel>()?.Publish(this, Model);
         }
         public List<SweepData> RefSpectrum => datas;
         public OxyPlot.PlotModel AccelerationModel { get; } = new OxyPlot.PlotModel();

+ 7 - 7
Avalonia/ShakerApp/ViewModels/ShakerControl/ShakerControlViewModel.cs

@@ -28,14 +28,14 @@ namespace ShakerApp.ViewModels
             GetEvent<AllConfig>().Subscrip((sender, args) =>
             {
                 UpDateModel(args.Data.ShakerControl);
-                if(CommunicationViewModel.Intance.ServiceIsStart)
+                if(CommunicationViewModel.Instance.ServiceIsStart)
                 {
-                    CommunicationViewModel.Intance.ServiceCommunication.GetEvent<ShakerControlModel>().Publish(this, args.Data.ShakerControl);
+                    CommunicationViewModel.Instance.ServiceCommunication.GetEvent<ShakerControlModel>().Publish(this, args.Data.ShakerControl);
                 }
-                CommunicationViewModel.Intance.LocalCommunication?.GetEvent<ShakerControlModel>()?.Subscrip((sender, args) =>
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent<ShakerControlModel>()?.Subscrip((sender, args) =>
                 {
                     UpDateModel(args.Data);
-                    CommunicationViewModel.Intance.ServiceCommunication?.GetEvent<ShakerControlModel>()?.Publish(this, args.Data);
+                    CommunicationViewModel.Instance.ServiceCommunication?.GetEvent<ShakerControlModel>()?.Publish(this, args.Data);
                 });
             });
         }
@@ -69,7 +69,7 @@ namespace ShakerApp.ViewModels
             set
             {
                 SetProperty(ref Model.PageType, value);
-                CommunicationViewModel.Intance.LocalCommunication?.GetEvent<MainPageType>()?.Publish(this, value);
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent<MainPageType>()?.Publish(this, value);
             }
         }
         [PropertyAssociation(nameof(ShakerControlModel.MaxControlItemCount))]
@@ -157,8 +157,8 @@ namespace ShakerApp.ViewModels
         protected override void Save()
         {
             base.Save();
-            CommunicationViewModel.Intance.LocalCommunication?.GetEvent<Shaker.Models.ShakerControlModel>()?.Publish(this, Model);
-            CommunicationViewModel.Intance.ServiceCommunication?.GetEvent<Shaker.Models.ShakerControlModel>()?.Publish(this, Model);
+            CommunicationViewModel.Instance.LocalCommunication?.GetEvent<Shaker.Models.ShakerControlModel>()?.Publish(this, Model);
+            CommunicationViewModel.Instance.ServiceCommunication?.GetEvent<Shaker.Models.ShakerControlModel>()?.Publish(this, Model);
         }
     }
 }

+ 2 - 1
Avalonia/ShakerApp/ViewModels/ShakerDataViewModel.cs

@@ -18,7 +18,7 @@ namespace ShakerApp.ViewModels
         {
             GetEvent<AllConfig>().Subscrip((sender, args) =>
             {
-                CommunicationViewModel.Intance.LocalCommunication.GetEvent(Topic.DATA).Subscrip((sender, args) =>
+                CommunicationViewModel.Instance.LocalCommunication.GetEvent(Topic.DATA).Subscrip((sender, args) =>
                 {
                     if(args.Data.Length>0 && args.Data[0] is float[,] v)
                     {
@@ -43,6 +43,7 @@ namespace ShakerApp.ViewModels
                                 break;
                         }
                         CalcAnalog();
+                        CommunicationViewModel.Instance.ServiceCommunication?.GetEvent(Topic.DATA).Publish(this, null,v);
                     }
                 });
             });

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

@@ -101,7 +101,7 @@ namespace ShakerApp.ViewModels
             set
             {
                 SetProperty(ref workPosition, value);
-                CommunicationViewModel.Intance.LocalCommunication?.GetEvent(Topic.ZEROCHANGE).Publish(this, null, value);
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent(Topic.ZEROCHANGE).Publish(this, null, value);
             }
         }
         private ShakerStatusViewModel()
@@ -112,14 +112,14 @@ namespace ShakerApp.ViewModels
             {
                 valvePower = false;
                 UpDateModel(args.Data.ShakerStatus);
-                if(CommunicationViewModel.Intance.ServiceIsStart)
+                if(CommunicationViewModel.Instance.ServiceIsStart)
                 {
-                    CommunicationViewModel.Intance.ServiceCommunication.GetEvent<ShakerStatusModel>().Publish(this, args.Data.ShakerStatus);
+                    CommunicationViewModel.Instance.ServiceCommunication.GetEvent<ShakerStatusModel>().Publish(this, args.Data.ShakerStatus);
                 }
-                CommunicationViewModel.Intance.LocalCommunication?.GetEvent<ShakerStatusModel>()?.Subscrip((sender, args) =>
+                CommunicationViewModel.Instance.LocalCommunication?.GetEvent<ShakerStatusModel>()?.Subscrip((sender, args) =>
                 {
                     UpDateModel(args.Data);
-                    CommunicationViewModel.Intance.ServiceCommunication?.GetEvent<ShakerStatusModel>()?.Publish(this, args.Data);
+                    CommunicationViewModel.Instance.ServiceCommunication?.GetEvent<ShakerStatusModel>()?.Publish(this, args.Data);
                 });
             });
             GetEvent(Topic.DATA).Subscrip((_, _) =>
@@ -156,17 +156,17 @@ namespace ShakerApp.ViewModels
         public ICommand RiseTableCommand => new RelayCommand(RiseTableCommandExecute);
         private void RiseTableCommandExecute()
         {
-            CommunicationViewModel.Intance.LocalCommunication.GetEvent(Topic.RISETABLE).Publish(this, null);
+            CommunicationViewModel.Instance.LocalCommunication.GetEvent(Topic.RISETABLE).Publish(this, null);
         }
         public ICommand DropTableCommand => new RelayCommand(DropTableCommandExecute);
         private void DropTableCommandExecute()
         {
-            CommunicationViewModel.Intance.LocalCommunication.GetEvent(Topic.DROPTABLE).Publish(this, null);
+            CommunicationViewModel.Instance.LocalCommunication.GetEvent(Topic.DROPTABLE).Publish(this, null);
         }
         public ICommand ResetCommand => new RelayCommand(Reset);
         private void Reset()
         {
-            CommunicationViewModel.Intance.LocalCommunication.GetEvent(Topic.RESETERROR).Publish(this, null);
+            CommunicationViewModel.Instance.LocalCommunication.GetEvent(Topic.RESETERROR).Publish(this, null);
         }
 
         public ICommand EmergencyStopCommand => new RelayCommand(EmergencyStopCommandExecute);
@@ -182,20 +182,20 @@ namespace ShakerApp.ViewModels
             {
                 case MainPageType.SinePage:
                     SineMainPageViewModel.Instance.Start();
-                    CommunicationViewModel.Intance.LocalCommunication.GetEvent<MainPageType>().Publish(this, MainPageType.SinePage);
+                    CommunicationViewModel.Instance.LocalCommunication.GetEvent<MainPageType>().Publish(this, MainPageType.SinePage);
                     break;
                 case MainPageType.RandomPage:
-                    CommunicationViewModel.Intance.LocalCommunication.GetEvent<RandomTestStep>().Publish(this, RandomTestStep.Start);
-                    CommunicationViewModel.Intance.LocalCommunication.GetEvent<MainPageType>().Publish(this, MainPageType.RandomPage);
+                    CommunicationViewModel.Instance.LocalCommunication.GetEvent<RandomTestStep>().Publish(this, RandomTestStep.Start);
+                    CommunicationViewModel.Instance.LocalCommunication.GetEvent<MainPageType>().Publish(this, MainPageType.RandomPage);
                     RandomMainPageViewModel.Instance.Start();
                     break;
             }
-            CommunicationViewModel.Intance.LocalCommunication.GetEvent(Topic.STARTSIGNALGEN).Publish(this, null);
+            CommunicationViewModel.Instance.LocalCommunication.GetEvent(Topic.STARTSIGNALGEN).Publish(this, null);
         }
         public ICommand StopCommand => new RelayCommand(StopCommandExecute);
         private void StopCommandExecute()
         {
-            CommunicationViewModel.Intance.LocalCommunication.GetEvent(Topic.STOPSIGNALGEN).Publish(this, null);
+            CommunicationViewModel.Instance.LocalCommunication.GetEvent(Topic.STOPSIGNALGEN).Publish(this, null);
         }
 
         public static ShakerStatusViewModel Instance { get; } = new ShakerStatusViewModel();
@@ -208,7 +208,7 @@ namespace ShakerApp.ViewModels
             {
                 //if (valvePower == value) return;
                 SetProperty(ref valvePower, value);
-                CommunicationViewModel.Intance.LocalCommunication.GetEvent(Topic.VALVEPOWER).Publish(this, null, value);
+                CommunicationViewModel.Instance.LocalCommunication.GetEvent(Topic.VALVEPOWER).Publish(this, null, value);
             }
         }
     }

+ 40 - 0
Avalonia/ShakerApp/Views/LEDControl.axaml

@@ -0,0 +1,40 @@
+<UserControl
+    x:Class="ShakerApp.LEDControl"
+    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"
+    d:DesignHeight="450"
+    d:DesignWidth="450"
+    mc:Ignorable="d">
+    <Viewbox Stretch="Fill">
+        <Grid
+            Width="56"
+            Height="56"
+            Margin="0"
+            HorizontalAlignment="Center"
+            VerticalAlignment="Center">
+            <Ellipse
+                Width="46"
+                Height="46"
+                HorizontalAlignment="Center"
+                VerticalAlignment="Center">
+                <Ellipse.Fill>
+                    <RadialGradientBrush Center="50% 50%" GradientOrigin="50% 50%">
+                        <GradientStop Offset="0" Color="{Binding $parent[UserControl].Foreground, Converter={StaticResource BrushToColorConverter}, ConverterParameter=10}" />
+                        <GradientStop Offset="0.7" Color="{Binding $parent[UserControl].Foreground, Converter={StaticResource BrushToColorConverter}, ConverterParameter=180}" />
+                        <GradientStop Offset="1" Color="{Binding $parent[UserControl].Foreground, Converter={StaticResource BrushToColorConverter}}" />
+                    </RadialGradientBrush>
+                </Ellipse.Fill>
+            </Ellipse>
+            <Ellipse
+                Width="56"
+                Height="56"
+                HorizontalAlignment="Center"
+                VerticalAlignment="Center"
+                Fill="Transparent"
+                Stroke="{Binding $parent[UserControl].Foreground}"
+                StrokeThickness="2" />
+        </Grid>
+    </Viewbox>
+</UserControl>

+ 13 - 0
Avalonia/ShakerApp/Views/LEDControl.axaml.cs

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

+ 10 - 9
Avalonia/ShakerApp/Views/MainWindow.axaml

@@ -56,29 +56,30 @@
                 Command="{Binding DeviceMangerCommand}"
                 CommandParameter="MenuManger"
                 Header="{DynamicResource MenuManger}"
-                IsEnabled="{Binding Source={x:Static vm:CommunicationViewModel.Intance}, Path=!LocalIsConnect}"
+                IsEnabled="{Binding Source={x:Static vm:CommunicationViewModel.Instance}, Path=!LocalIsConnect}"
                 IsVisible="{Binding Source={x:Static vm:ShakerSettingViewModel.Instance}, Path=WorkingMode, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static model:WorkingMode.Local}}" />
             <MenuItem
+                Command="{Binding ConnectCommand}"
                 CommandParameter="MenuConnect"
                 Header="{DynamicResource MenuConnect}"
-                IsEnabled="{Binding Source={x:Static vm:CommunicationViewModel.Intance}, Path=!LocalIsConnect}"
+                IsEnabled="{Binding Source={x:Static vm:CommunicationViewModel.Instance}, Path=!LocalIsConnect}"
                 IsVisible="{Binding Source={x:Static vm:ShakerSettingViewModel.Instance}, Path=WorkingMode, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static model:WorkingMode.Remote}}" />
             <MenuItem
                 Command="{Binding DisConnectCommand}"
                 Header="{DynamicResource MenuDisConnect}"
-                IsEnabled="{Binding Source={x:Static vm:CommunicationViewModel.Intance}, Path=LocalIsConnect}" />
+                IsEnabled="{Binding Source={x:Static vm:CommunicationViewModel.Instance}, Path=LocalIsConnect}" />
 
             <MenuItem
                 Command="{Binding ShakerConfigCommand}"
                 CommandParameter="MenuDeviceConfig"
                 Header="{DynamicResource MenuDeviceConfig}"
-                IsEnabled="{Binding Source={x:Static vm:CommunicationViewModel.Intance}, Path=LocalIsConnect}" />
+                IsEnabled="{Binding Source={x:Static vm:CommunicationViewModel.Instance}, Path=LocalIsConnect}" />
             <Separator />
             <MenuItem
                 Command="{Binding ShakerControlCommand}"
                 CommandParameter="MenuShakerControl"
                 Header="{DynamicResource MenuShakerControl}"
-                IsEnabled="{Binding Source={x:Static vm:CommunicationViewModel.Intance}, Path=LocalIsConnect}" />
+                IsEnabled="{Binding Source={x:Static vm:CommunicationViewModel.Instance}, Path=LocalIsConnect}" />
             <Separator />
             <MenuItem
                 Command="{Binding OilControlCommand}"
@@ -87,7 +88,7 @@
                 <MenuItem.IsEnabled>
                     <MultiBinding Converter="{StaticResource MutliBoolConverter}">
                         <MultiBinding.Bindings>
-                            <Binding Path="LocalIsConnect" Source="{x:Static vm:CommunicationViewModel.Intance}" />
+                            <Binding Path="LocalIsConnect" Source="{x:Static vm:CommunicationViewModel.Instance}" />
                             <Binding Path="IsEnabled" Source="{x:Static vm:OilSourceStatusViewModel.Instance}" />
                         </MultiBinding.Bindings>
                     </MultiBinding>
@@ -98,10 +99,10 @@
                 Command="{Binding DebugCommand}"
                 CommandParameter="MenuDebug"
                 Header="{DynamicResource MenuDebug}"
-                IsEnabled="{Binding Source={x:Static vm:CommunicationViewModel.Intance}, Path=LocalIsConnect}"
+                IsEnabled="{Binding Source={x:Static vm:CommunicationViewModel.Instance}, Path=LocalIsConnect}"
                 IsVisible="{Binding CanDebug}" />
         </MenuItem>
-        <MenuItem Header="{DynamicResource MenuTest}" IsEnabled="{Binding Source={x:Static vm:CommunicationViewModel.Intance}, Path=LocalIsConnect}">
+        <MenuItem Header="{DynamicResource MenuTest}" IsEnabled="{Binding Source={x:Static vm:CommunicationViewModel.Instance}, Path=LocalIsConnect}">
             <MenuItem.Styles>
                 <Style Selector="MenuItem.Selected">
                     <Setter Property="Icon">
@@ -195,7 +196,7 @@
                 Command="{Binding UpbitfileCommand}"
                 CommandParameter="UpBitfile"
                 Header="{DynamicResource UpBitfile}"
-                IsEnabled="{Binding Source={x:Static vm:CommunicationViewModel.Intance}, Path=LocalIsConnect}">
+                IsEnabled="{Binding Source={x:Static vm:CommunicationViewModel.Instance}, Path=LocalIsConnect}">
                 <MenuItem.Icon>
                     <PathIcon Data="{StaticResource UpbitfileGeometry}" />
                 </MenuItem.Icon>

+ 7 - 4
Avalonia/ShakerApp/Views/Oil/OilAnalogView.axaml

@@ -15,7 +15,10 @@
         MinWidth="220"
         HorizontalAlignment="Center"
         VerticalAlignment="Center">
-        <Grid>
+        <StackPanel
+            HorizontalAlignment="Center"
+            VerticalAlignment="Center"
+            Orientation="Horizontal">
             <TextBlock
                 HorizontalAlignment="Center"
                 VerticalAlignment="Center"
@@ -25,7 +28,7 @@
             <PathIcon
                 Width="32"
                 Height="24"
-                HorizontalAlignment="Right"
+                Margin="10,0,0,0"
                 VerticalAlignment="Center"
                 Classes.IsError="{Binding IsError}"
                 Classes.IsWarn="{Binding IsWarn}"
@@ -64,7 +67,7 @@
                     </Style>
                 </PathIcon.Styles>
             </PathIcon>
-        </Grid>
+        </StackPanel>
         <TextBlock
             HorizontalAlignment="Center"
             VerticalAlignment="Center"
@@ -72,7 +75,7 @@
             Classes.IsWarn="{Binding IsWarn}"
             FontSize="74"
             FontWeight="Bold">
-            <Run Text="{Binding Value}" />
+            <Run Text="{Binding Value, StringFormat='{}{0:0.00} '}" />
             <Run Text="{Binding Unit}" />
             <TextBlock.Styles>
                 <Style Selector="TextBlock">

+ 87 - 58
Avalonia/ShakerApp/Views/Oil/OilControlView.axaml

@@ -11,18 +11,15 @@
     d:DesignHeight="450"
     d:DesignWidth="800"
     x:DataType="vm:OilSourceStatusViewModel"
-    Classes.IsConnect="{Binding IsConnect}"
-    Classes.IsRemote="{Binding IsRemote}"
     DataContext="{Binding Source={x:Static vm:OilSourceStatusViewModel.Instance}}"
     mc:Ignorable="d">
-    <UserControl.Styles>
-        <Style Selector="UserControl.IsRemote">
-            <Setter Property="IsEnabled" Value="False" />
-        </Style>
-        <Style Selector="UserControl.IsConnect">
-            <Setter Property="IsEnabled" Value="False" />
-        </Style>
-    </UserControl.Styles>
+    <UserControl.IsEnabled>
+        <MultiBinding Converter="{StaticResource MutliBoolConverter}">
+            <Binding Path="IsConnect" />
+            <Binding Path="IsRemote" />
+            <Binding Path="IsEnabled" />
+        </MultiBinding>
+    </UserControl.IsEnabled>
     <Grid>
         <Grid.RowDefinitions>
             <RowDefinition />
@@ -49,7 +46,7 @@
                 <ItemsControl ItemsSource="{Binding Circuit}">
                     <ItemsControl.ItemTemplate>
                         <DataTemplate>
-                            <suki:GroupBox>
+                            <suki:GroupBox IsVisible="{Binding IsVisible}">
                                 <suki:GroupBox.Header>
                                     <StackPanel Orientation="Horizontal">
                                         <TextBlock
@@ -70,7 +67,7 @@
                     </ItemsControl.ItemTemplate>
                 </ItemsControl>
 
-                <suki:GroupBox DataContext="{Binding Assistant}">
+                <suki:GroupBox DataContext="{Binding Assistant}" IsVisible="{Binding IsVisible}">
                     <suki:GroupBox.Header>
                         <TextBlock
                             VerticalAlignment="Center"
@@ -80,7 +77,7 @@
                     </suki:GroupBox.Header>
                     <view:PumpControlView IsVisible="{Binding IsVisible}" />
                 </suki:GroupBox>
-                <suki:GroupBox DataContext="{Binding Forerunner}">
+                <suki:GroupBox DataContext="{Binding Forerunner}" IsVisible="{Binding IsVisible}">
                     <suki:GroupBox.Header>
                         <TextBlock
                             VerticalAlignment="Center"
@@ -91,7 +88,7 @@
                     <view:PumpControlView IsVisible="{Binding IsVisible}" />
                 </suki:GroupBox>
 
-                <suki:GroupBox DataContext="{Binding Circulate}">
+                <suki:GroupBox DataContext="{Binding Circulate}" IsVisible="{Binding IsVisible}">
                     <suki:GroupBox.Header>
                         <TextBlock
                             VerticalAlignment="Center"
@@ -107,54 +104,86 @@
         <Grid
             Grid.Row="1"
             Height="86"
-            Margin="10">
-            <Grid.ColumnDefinitions>
-                <ColumnDefinition />
-                <ColumnDefinition Width="86" />
-                <ColumnDefinition Width="86" />
-            </Grid.ColumnDefinitions>
+            Margin="10"
+            ColumnDefinitions="*,2*">
             <Border
-                BorderBrush="Gray"
-                BorderThickness="1"
-                CornerRadius="6"
-                IsVisible="{Binding OilErrors.Count}">
-                <ItemsControl ItemsSource="{Binding OilErrors}">
-                    <ItemsControl.ItemTemplate>
-                        <DataTemplate>
-                            <StackPanel>
-                                <TextBlock
-                                    HorizontalAlignment="Center"
-                                    VerticalAlignment="Center"
-                                    Text="{local:ResourceBinding Name}" />
-                                <Ellipse
-                                    Width="56"
-                                    Height="56"
-                                    Classes.Error="{Binding Status}" />
-                            </StackPanel>
-                        </DataTemplate>
-                    </ItemsControl.ItemTemplate>
-                </ItemsControl>
-            </Border>
-            <StackPanel Grid.Column="1">
+                Margin="20,10,20,10"
+                Classes.Error="{Binding IsError}"
+                CornerRadius="10">
+                <Border.Styles>
+                    <Style Selector="Border">
+                        <Setter Property="Background" Value="Green" />
+                    </Style>
+                    <Style Selector="Border.Error">
+                        <Setter Property="Background" Value="Red" />
+                    </Style>
+                </Border.Styles>
                 <TextBlock
                     HorizontalAlignment="Center"
                     VerticalAlignment="Center"
-                    Text="{DynamicResource PLCConnectStatus}" />
-                <Ellipse
-                    Width="56"
-                    Height="56"
-                    Classes.Error="{Binding IsConnect}" />
-            </StackPanel>
-            <StackPanel Grid.Column="2">
-                <TextBlock
-                    HorizontalAlignment="Center"
-                    VerticalAlignment="Center"
-                    Text="{DynamicResource PLCControlModel}" />
-                <Ellipse
-                    Width="56"
-                    Height="56"
-                    Classes.Warn="{Binding IsRemote}" />
-            </StackPanel>
+                    FontSize="36"
+                    FontStyle="Normal"
+                    FontWeight="Bold"
+                    Text="{Binding OilStatus}" />
+            </Border>
+            <Grid Grid.Column="1" Margin="10">
+                <Grid.ColumnDefinitions>
+                    <ColumnDefinition />
+                    <ColumnDefinition Width="86" />
+                    <ColumnDefinition Width="86" />
+                </Grid.ColumnDefinitions>
+                <Border
+                    BorderBrush="Gray"
+                    BorderThickness="1"
+                    CornerRadius="6"
+                    IsVisible="{Binding OilErrors.Count}">
+                    <ItemsControl ItemsSource="{Binding OilErrors}">
+                        <ItemsControl.ItemTemplate>
+                            <DataTemplate>
+                                <StackPanel>
+                                    <TextBlock
+                                        HorizontalAlignment="Center"
+                                        VerticalAlignment="Center"
+                                        Text="{local:ResourceBinding Name}" />
+                                    <local:LEDControl
+                                        Width="56"
+                                        Height="56"
+                                        Classes.Error="{Binding Status}" />
+                                </StackPanel>
+                            </DataTemplate>
+                        </ItemsControl.ItemTemplate>
+                    </ItemsControl>
+                </Border>
+                <StackPanel Grid.Column="1">
+                    <TextBlock
+                        HorizontalAlignment="Center"
+                        VerticalAlignment="Center"
+                        Text="{DynamicResource PLCConnectStatus}" />
+                    <local:LEDControl
+                        Width="56"
+                        Height="56"
+                        Classes.Error="{Binding !IsConnect}" />
+                </StackPanel>
+                <StackPanel Grid.Column="2">
+                    <TextBlock
+                        HorizontalAlignment="Center"
+                        VerticalAlignment="Center"
+                        Classes.Warn="{Binding IsRemote}">
+                        <TextBlock.Styles>
+                            <Style Selector="TextBlock">
+                                <Setter Property="Text" Value="{DynamicResource OilLocalControl}" />
+                            </Style>
+                            <Style Selector="TextBlock.Warn">
+                                <Setter Property="Text" Value="{DynamicResource OilRemoteControl}" />
+                            </Style>
+                        </TextBlock.Styles>
+                    </TextBlock>
+                    <local:LEDControl
+                        Width="56"
+                        Height="56"
+                        Classes.Warn="{Binding !IsRemote}" />
+                </StackPanel>
+            </Grid>
         </Grid>
     </Grid>
 </UserControl>

+ 1 - 2
Avalonia/ShakerApp/Views/Oil/OilMinView.axaml

@@ -24,7 +24,6 @@
             <ItemsControl.ItemTemplate>
                 <DataTemplate>
                     <StackPanel>
-
                         <TextBlock
                             HorizontalAlignment="Center"
                             VerticalAlignment="Center"
@@ -38,7 +37,7 @@
                             Classes.IsWarn="{Binding IsWarn}"
                             FontSize="26"
                             FontWeight="Bold">
-                            <Run Text="{Binding Value}" />
+                            <Run Text="{Binding Value, StringFormat='{}{0:0.00}'}" />
                             <Run Text="{Binding Unit}" />
                             <TextBlock.Styles>
                                 <Style Selector="TextBlock">

+ 27 - 6
Avalonia/ShakerApp/Views/Oil/PumpControlView.axaml

@@ -85,8 +85,15 @@
                 Grid.Column="1"
                 Width="120"
                 Height="42"
-                Command="{Binding LoadPressureCommand}"
-                IsEnabled="{Binding !IsLoadPressure}">
+                Command="{Binding LoadPressureCommand}">
+                <Button.IsEnabled>
+                    <MultiBinding Converter="{StaticResource MutliBoolConverter}">
+                        <MultiBinding.Bindings>
+                            <Binding Path="!IsLoadPressure" />
+                            <Binding Path="IsStart" />
+                        </MultiBinding.Bindings>
+                    </MultiBinding>
+                </Button.IsEnabled>
                 <StackPanel Orientation="Horizontal">
                     <PathIcon Data="{StaticResource LoadGeometry}" RenderTransform="rotate(180deg)" />
                     <TextBlock
@@ -99,8 +106,15 @@
                 Grid.Column="2"
                 Width="120"
                 Height="42"
-                Command="{Binding UnloadPressureCommand}"
-                IsEnabled="{Binding IsLoadPressure}">
+                Command="{Binding UnloadPressureCommand}">
+                <Button.IsEnabled>
+                    <MultiBinding Converter="{StaticResource MutliBoolConverter}">
+                        <MultiBinding.Bindings>
+                            <Binding Path="IsLoadPressure" />
+                            <Binding Path="IsStart" />
+                        </MultiBinding.Bindings>
+                    </MultiBinding>
+                </Button.IsEnabled>
                 <StackPanel Orientation="Horizontal">
                     <PathIcon Data="{StaticResource LoadGeometry}" />
                     <TextBlock
@@ -125,8 +139,15 @@
             Grid.Column="3"
             Width="120"
             Height="42"
-            Command="{Binding StopCommand}"
-            IsEnabled="{Binding IsStart}">
+            Command="{Binding StopCommand}">
+            <Button.IsEnabled>
+                <MultiBinding Converter="{StaticResource MutliBoolConverter}">
+                    <MultiBinding.Bindings>
+                        <Binding Path="!IsLoadPressure" />
+                        <Binding Path="IsStart" />
+                    </MultiBinding.Bindings>
+                </MultiBinding>
+            </Button.IsEnabled>
             <StackPanel Orientation="Horizontal">
                 <PathIcon Data="{StaticResource StopGeometry}" />
                 <TextBlock

+ 1 - 0
Avalonia/ShakerApp/Views/Setting/LanguageView.axaml

@@ -3,6 +3,7 @@
     xmlns="https://github.com/avaloniaui"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+    xmlns:local="using:ShakerApp"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
     xmlns:vm="using:ShakerApp.ViewModels"
     d:DesignHeight="450"

+ 1 - 1
Communication/TcpEventBus/NettyServer.cs

@@ -169,7 +169,7 @@ namespace TcpEventBus
             if (_Context == null || data == null || data.Length == 0 || !IsConnect) return;
            var buffer =  _Context.Allocator.Buffer(data.Length);
             buffer.WriteBytes(data);
-            _Context.WriteAndFlushAsync(buffer);
+            _Context?.WriteAndFlushAsync(buffer);
         }
     }
 

+ 2 - 2
DBHelper/SQLite.cs

@@ -4618,8 +4618,8 @@ namespace SQLite
 			Serialized = 3
 		}
 
-        const string LibraryPath = "libsqlite3.so.0";
-        //const string LibraryPath = "sqlite3.dll";
+        //const string LibraryPath = "libsqlite3.so.0";
+        const string LibraryPath = "e_sqlite3.dll";
 #if !USE_CSHARP_SQLITE && !USE_WP8_NATIVE_SQLITE && !USE_SQLITEPCL_RAW
         [DllImport(LibraryPath, EntryPoint = "sqlite3_threadsafe", CallingConvention=CallingConvention.Cdecl)]
 		public static extern int Threadsafe ();

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

@@ -281,10 +281,29 @@
 
     <s:String x:Key="MainPressure">主油压</s:String>
     <s:String x:Key="Pressure">油压</s:String>
-    <s:String x:Key="AuxiliaryPressure">油压</s:String>
+    <s:String x:Key="AuxiliaryPressure">辅助油压</s:String>
     <s:String x:Key="LiquidLevel">液位</s:String>
     <s:String x:Key="OilTemperature">油温</s:String>
     <s:String x:Key="OilStatus">油源状态</s:String>
+    <s:String x:Key="OilConnect">油源已连接</s:String>
+    <s:String x:Key="OilDisConnect">油源已断开连接</s:String>
+    <s:String x:Key="PumpStartError">{0}启动失败</s:String>
+    <s:String x:Key="PumpStopError">{0}停止失败</s:String>
+    <s:String x:Key="PumpLoadError">{0}加载失败</s:String>
+    <s:String x:Key="PumpUnloadError">{0}卸载失败</s:String>
+
+    <s:String x:Key="PumpStartSuccess">{0}启动</s:String>
+    <s:String x:Key="PumpStopSuccess">{0}停止</s:String>
+    <s:String x:Key="PumpLoadSuccess">{0}加载</s:String>
+    <s:String x:Key="PumpUnloadSuccess">{0}卸载</s:String>
+    <s:String x:Key="PumpPressure">{0}压力设置为{1}{2}</s:String>
+
+    <s:String x:Key="UpperWarn">上限警告</s:String>
+    <s:String x:Key="LowerWarn">下限警告</s:String>
+    <s:String x:Key="UpperError">超上限</s:String>
+    <s:String x:Key="LowerError">超下限</s:String>
+    <s:String x:Key="OilError">错误</s:String>
+    <s:String x:Key="OilSuccess">正常</s:String>
 
     <s:String x:Key="MainPump">主泵</s:String>
     <s:String x:Key="AssistantPump">辅助油泵</s:String>
@@ -294,4 +313,6 @@
     <s:String x:Key="UnloadPressure">卸载</s:String>
     <s:String x:Key="PLCConnectStatus">连接状态</s:String>
     <s:String x:Key="PLCControlModel">近控/远控</s:String>
+    <s:String x:Key="OilLocalControl">近控</s:String>
+    <s:String x:Key="OilRemoteControl">远控</s:String>
 </ResourceDictionary>

+ 3 - 1
PLCConnect/ModbusConnect/Modbus.cs

@@ -10,15 +10,17 @@ namespace ModbusConnect
         private System.Net.Sockets.TcpClient? tcpClient;
         private IModbusMaster? modbus;
         private bool isConnected;
+        private bool isfirst = true;
 
         public bool IsConnected 
         {
             get => isConnected;
             set
             {
-                if (isConnected == value) return;
+                if (!isfirst && isConnected == value) return;
                 isConnected = value;
                 StatusChanged?.Invoke(this, value);
+                isfirst = false;
             }
         }
 

+ 42 - 9
PLCConnect/S7Connect/S7.cs

@@ -1,6 +1,7 @@
 using IPLCConnect;
 using S7.Net;
 using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
 namespace S7Connect
 {
     [PLCProtocol( PLCProtocol.S7)]
@@ -10,6 +11,7 @@ namespace S7Connect
         [AllowNull]
         private Plc plc;
         private bool isConnected = false;
+        private bool isfirst = true;
 
         public CpuType CpuType { get; set; } = CpuType.S71200;
         public Int16 Rack { get; set; } = 0;
@@ -20,9 +22,12 @@ namespace S7Connect
             get => isConnected;
             set
             {
-                if (isConnected = value) return;
-                isConnected = value;
-                StatusChanged?.Invoke(this, value);
+                if (isConnected != value || isfirst)
+                {
+                    isConnected = value;
+                    StatusChanged?.Invoke(this, value);
+                }
+                isfirst = false;
             }
         }
 
@@ -88,13 +93,34 @@ namespace S7Connect
 
         public T Read<T>(string addr) where T : unmanaged
         {
-            if(plc ==null || !plc.IsConnected)
+            try
             {
-                IsConnected = false;
-                return default;
+                if (plc == null || !plc.IsConnected)
+                {
+                    IsConnected = false;
+                    return default;
+                }
+                if (string.IsNullOrEmpty(addr)) return default;
+                object? value = plc.Read(addr);
+                if (value == null) return default;
+                if (typeof(T) == value.GetType()) return (T)value;
+                switch (value)
+                {
+                    case byte b:
+                        return Unsafe.As<byte, T>(ref b);
+
+                    case ushort us:
+                        return Unsafe.As<ushort, T>(ref us);
+                    case uint ui:
+                        return Unsafe.As<uint, T>(ref ui);
+                }
+                return (T)plc.Read(addr)!;
+            }
+            catch
+            {
+                DisConnect();
             }
-            if (string.IsNullOrEmpty(addr)) return default;
-            return (T)plc.Read(addr)!;
+            return default;
         }
 
         public void Write<T>(string addr, T value) where T : unmanaged
@@ -105,7 +131,14 @@ namespace S7Connect
                 return;
             }
             if (string.IsNullOrEmpty(addr)) return;
-            plc.Write(addr, value);
+            try
+            {
+                plc.Write(addr, value);
+            }
+            catch
+            {
+                DisConnect();
+            }
         }
 
         public bool ReadBit(string addr, byte bitindex)

+ 1 - 4
Shaker.Model/Models/CircuitModel.cs

@@ -5,10 +5,7 @@ namespace Shaker.Models
     public class CircuitModel : BaseModel
     {
         public string Name = "";
-        /// <summary>
-        /// 当前压力
-        /// </summary>
-        public float Pressure = 0;
+
         /// <summary>
         /// 是否启动
         /// </summary>

+ 0 - 30
Shaker.Model/Models/OilSourceModel.cs

@@ -10,36 +10,6 @@ namespace Shaker.Models
     {
         public OilSourceStatusModel()
         {
-            OilSourceAnalogs.Add(new OilSourceAnalogModel()
-            {
-                Name = "MainPressure",
-                Unit = "MPa",
-                IsLowerError = true,
-            });
-            OilSourceAnalogs.Add(new OilSourceAnalogModel()
-            {
-                Name = "AuxiliaryPressure",
-                Unit = "MPa",
-                IsLowerWarn = true,
-            });
-            OilSourceAnalogs.Add(new OilSourceAnalogModel()
-            {
-                Name = "LiquidLevel",
-                Unit = "mm"
-            });
-            OilSourceAnalogs.Add(new OilSourceAnalogModel()
-            {
-                Name = "OilTemperature",
-                Unit = "℃"
-            });
-            Circuit.Add(new CircuitModel()
-            {
-                Name = "MainPump",
-            });
-            Circuit.Add(new CircuitModel()
-            {
-                Name = "MainPump"
-            });
         }
         public bool IsConnect = false;
 

+ 14 - 7
Shaker.Model/Topic.cs

@@ -8,6 +8,20 @@ namespace Shaker.Models
 {
     public class Topic
     {
+        public const string CircuitPressure = nameof(OilSourceStatusModel.Circuit) + "_Pressure";
+        public const string CircuitLoad = nameof(OilSourceStatusModel.Circuit) + "_Load";
+        public const string CircuitStart = nameof(OilSourceStatusModel.Circuit) + "_Start";
+        public const string ForerunnerPressure = nameof(OilSourceStatusModel.Forerunner) + "_Pressure";
+        public const string ForerunnerLoad = nameof(OilSourceStatusModel.Forerunner) + "_Load";
+        public const string ForerunnerStart = nameof(OilSourceStatusModel.Forerunner) + "_Start";
+        public const string CirculatePressure = nameof(OilSourceStatusModel.Circulate) + "_Pressure";
+        public const string CirculateLoad = nameof(OilSourceStatusModel.Circulate) + "_Load";
+        public const string CirculateStart = nameof(OilSourceStatusModel.Circulate) + "_Start";
+        public const string AssistantPressure = nameof(OilSourceStatusModel.Assistant) + "_Pressure";
+        public const string AssistantLoad = nameof(OilSourceStatusModel.Assistant) + "_Load";
+        public const string AssistantStart = nameof(OilSourceStatusModel.Assistant) + "_Start";
+        public const string OilEmergencyStop = "OilEmergencyStop";
+        public const string OilStatusChanged = "OilStatusChanged";
         /// <summary>
         /// The port number of the discovery server.
         /// </summary>
@@ -28,13 +42,6 @@ namespace Shaker.Models
         public const string VALVEPOWER = "ValvePower";
         public const string RESETERROR = "ResetError";
         public const string STARTSIGNALGEN = "StartSignalGen";
-        public const string OILMAINPUMP = "OilSourceMainPump";
-        public const string OILMAINPRESSURE = "OilSourceMainPressure";
-        public const string OILFORERUNNERPUMP = "OilSourceForerunnerPump";
-        public const string OILFORERUNNERPRESSURE = "OilSourceForerunnerPressure";
-        public const string OILSOURCEENABLED = "OilSourceEnabled";
-        public const string OILLOADPRESSOURCE = "OilSourceLoadPressure";
-        public const string OILEMERGENCYSTOP = "OilSourceEmergencyStop";
         public const string STARTRANDOMTEST = "StartRandomTest";
         public const string DisConnect = "DisConnect";
         /// <summary>

+ 217 - 0
Shaker/ClassDiagram1.cd

@@ -0,0 +1,217 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ClassDiagram MajorVersion="1" MinorVersion="1">
+  <Class Name="ShakerService.ViewModel.BaseServiceViewModel&lt;TModel&gt;" Collapsed="true">
+    <Position X="21.5" Y="0.5" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AgAAAAAAAIAAAAAAAAAAAAAQAAECAAAAAAkAAAAAAAA=</HashCode>
+      <FileName>ViewModel\BaseServiceViewModel.cs</FileName>
+    </TypeIdentifier>
+    <Lollipop Position="0.2" />
+  </Class>
+  <Class Name="ShakerService.ViewModel.PSDCache" Collapsed="true">
+    <Position X="42.5" Y="2.5" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAABAAAAAAACAAAAAJAAAA=</HashCode>
+      <FileName>ViewModel\PSDCache.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.ViewModel.SerivceValveConfigItemViewModel" Collapsed="true">
+    <Position X="22.5" Y="1.75" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAAAAQAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
+      <FileName>ViewModel\SerivceValveConfigItemViewModel.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.ViewModel.ServiceAccelerationConfigViewModel" Collapsed="true">
+    <Position X="29.25" Y="1.75" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAAAAAAABAAAAAAAAAAAACgAAAAAAAAAAAAAAAA=</HashCode>
+      <FileName>ViewModel\ServiceAccelerationConfigViewModel.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.ViewModel.ServiceAnalogSignalConfigViewModel" Collapsed="true">
+    <Position X="36" Y="1.75" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAAAAAAABAAAIAAAAAQAAAAAAAAAAAAAAAAAAAA=</HashCode>
+      <FileName>ViewModel\ServiceAnalogSignalConfigViewModel.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.ViewModel.ServiceRandomConfigViewModel" Collapsed="true">
+    <Position X="9" Y="1.75" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AEACkBAAGBASIAAAEBgIAAAUxBAAAQEAwQEAAQEIABE=</HashCode>
+      <FileName>ViewModel\ServiceRandomConfigViewModel.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.ViewModel.ServiceRandomDataViewModel" Collapsed="true">
+    <Position X="15.75" Y="1.75" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>ABAABAAAYCBAABAIQAAAMAAAABABBiAEAgQUSIEAAAw=</HashCode>
+      <FileName>ViewModel\ServiceRandomDataViewModel.cs</FileName>
+    </TypeIdentifier>
+    <Lollipop Position="0.2" />
+  </Class>
+  <Class Name="ShakerService.ViewModel.ServiceRandomIdentifyViewModel" Collapsed="true">
+    <Position X="24.75" Y="1.75" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAAQAQBAAAAAAAACAAABAAAAAAAAAAAAAAAACAA=</HashCode>
+      <FileName>ViewModel\ServiceRandomIdentifyViewModel.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.ViewModel.ServiceRandomPlanItemViewModel" Collapsed="true">
+    <Position X="31.5" Y="1.75" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAQA=</HashCode>
+      <FileName>ViewModel\ServiceRandomPlanItemViewModel.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.ViewModel.ServiceRandomSpectralTableViewModel" Collapsed="true">
+    <Position X="38.25" Y="1.75" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AgAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAEAgAAAAAA=</HashCode>
+      <FileName>ViewModel\ServiceRandomSpectralTableViewModel.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.ViewModel.ServiceRandomSpectrumItemViewModel" Collapsed="true">
+    <Position X="4.5" Y="1.75" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAAAAAQAAAAAAAAAAAAAAIAAMAAAEAAgAAAAEAA=</HashCode>
+      <FileName>ViewModel\ServiceRandomSpectrumItemViewModel.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.ViewModel.ServiceShakerConfigViewModel" Collapsed="true">
+    <Position X="11.25" Y="1.75" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>hgCCAAAUQEAEKCACARBCAAAQCEkCJAAgwEEABIAQAQA=</HashCode>
+      <FileName>ViewModel\ServiceShakerConfigViewModel.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.ViewModel.ServiceShakerControlViewModel" Collapsed="true">
+    <Position X="18" Y="1.75" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>BggBIBAAAAAAAKAAAAAAAACYIAAACoEARAEgAIwAAgA=</HashCode>
+      <FileName>ViewModel\ServiceShakerControlViewModel.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.ViewModel.ServiceShakerStatusViewModel" Collapsed="true">
+    <Position X="27" Y="1.75" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>DBAACAAAAAAgAAAAAAEIAAAAVAgkAAAAQAAAJAAAAAA=</HashCode>
+      <FileName>ViewModel\ServiceShakerStatusViewModel.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.ViewModel.ServiceSineDataViewModel" Collapsed="true">
+    <Position X="33.75" Y="1.75" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>ABAAAIAAAIAAACAEAAAAIAgABAAIAQUAIAQACAAAAAA=</HashCode>
+      <FileName>ViewModel\ServiceSineDataViewModel.cs</FileName>
+    </TypeIdentifier>
+    <Lollipop Position="0.2" />
+  </Class>
+  <Class Name="ShakerService.ViewModel.ServiceSweepConfigViewModel" Collapsed="true">
+    <Position X="6.75" Y="1.75" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AgAAAZACIACBBBAAAKAAAGAQAAAAA0CQQAEAABAQAAA=</HashCode>
+      <FileName>ViewModel\ServiceSweepConfigViewModel.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.ViewModel.ServiceSweepControlItemViewModel" Collapsed="true">
+    <Position X="13.5" Y="1.75" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAAAACABQAAAAAAAAAAAAIAAAAAAAAAAAAAAgAA=</HashCode>
+      <FileName>ViewModel\ServiceSweepControlItemViewModel.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.ViewModel.ServiceSweepItemViewModel" Collapsed="true">
+    <Position X="20.25" Y="1.75" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAAAAEQAAAAAAAAAAAAAAIAAMAAAEAAgAAAAAAA=</HashCode>
+      <FileName>ViewModel\ServiceSweepItemViewModel.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.OilSource.BitAddressConfig">
+    <Position X="39" Y="0.5" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAAAAAAAAAAAACAAAAAAAAAAAAACAAAAAAAAABA=</HashCode>
+      <FileName>OilSource\BitAddressConfig.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.OilSource.OilSource" Collapsed="true">
+    <Position X="44.25" Y="0.5" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AAQAAAAAAAAggABAAAESIIAAAEAAEAIAAABGgAAQAQA=</HashCode>
+      <FileName>OilSource\OilSource.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.OilSource.OilSourceAnalogAddressConfig" Collapsed="true">
+    <Position X="40.75" Y="1.5" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>ABAAAAAAAAEAECEAIAAAAAQYAAAAAAAAAAAAAAAgABA=</HashCode>
+      <FileName>OilSource\OilSourceAnalogAddressConfig.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.OilSource.OilSourceConfig">
+    <Position X="42.5" Y="1.5" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>ACAAAAEAAIAAAAAAAAEAAABCKEABAAAAAABACAACgBA=</HashCode>
+      <FileName>OilSource\OilSourceConfig.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.OilSource.OilSourcePumpAddressConfig">
+    <Position X="46.5" Y="1" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AIAAAACAABAAAAACIAUgAAQAEAACAAACAEIABEAAAgA=</HashCode>
+      <FileName>OilSource\OilSourcePumpAddressConfig.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.Tools.Log" Collapsed="true">
+    <Position X="42.5" Y="0.5" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAAAAAAAAYAAAAAAKAAAAAAAAAAAAAAAAEAAAAA=</HashCode>
+      <FileName>Tools\Log.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.Tools.PluginsLoader" Collapsed="true">
+    <Position X="40.75" Y="2.5" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AAABgAAEAAAAAAAAAAAACAAAAAAAAAAAABAMQAIAAAA=</HashCode>
+      <FileName>Tools\PluginsLoader.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Class Name="ShakerService.Tools.ServiceConfigTool" Collapsed="true">
+    <Position X="44.5" Y="1.75" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AECAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</HashCode>
+      <FileName>Tools\ServiceConfigTool.cs</FileName>
+    </TypeIdentifier>
+  </Class>
+  <Struct Name="ShakerService.ViewModel.ServiceDataCacheViewModel" Collapsed="true">
+    <Position X="40.75" Y="3.75" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AAAAAgAAAAAAAAAABABAAAEAFAIQAAAgQAABgAAAAAA=</HashCode>
+      <FileName>ViewModel\ServiceDataCacheViewModel.cs</FileName>
+    </TypeIdentifier>
+  </Struct>
+  <Interface Name="ShakerService.ViewModel.IServiceViewModel&lt;TModel&gt;" Collapsed="true">
+    <Position X="42.5" Y="5" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkAAAAAAAA=</HashCode>
+      <FileName>ViewModel\IServiceViewModel.cs</FileName>
+    </TypeIdentifier>
+  </Interface>
+  <Interface Name="ShakerService.ViewModel.IData" Collapsed="true">
+    <Position X="40.75" Y="5" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>ABAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAACAAAAAA=</HashCode>
+      <FileName>ViewModel\ServiceSineDataViewModel.cs</FileName>
+    </TypeIdentifier>
+  </Interface>
+  <Enum Name="ShakerService.Data.TestType" Collapsed="true">
+    <Position X="40.75" Y="6" Width="1.5" />
+    <TypeIdentifier>
+      <HashCode>AAAAAAAAAAAAgAAAAAAAAAAAAAAABAAAAAAAAAAAAAA=</HashCode>
+      <FileName>Data\TestType.cs</FileName>
+    </TypeIdentifier>
+  </Enum>
+  <Font Name="Microsoft YaHei UI" Size="9" />
+</ClassDiagram>

+ 59 - 52
Shaker/OilSource/OilSource.cs

@@ -16,15 +16,15 @@ namespace ShakerService.OilSource
     internal class OilSource
     {
         public event EventHandler<bool> StatusChanged;
-        private bool _IsEnabled = true;
 
-        public bool IsEnabled => _IsEnabled;
+        public bool IsEnabled => OilSourceStatus.IsEnabled;
         [AllowNull]
         private IPLCConnect.IPLCConnect _PLCConnect;
         private static string PluginPath = System.AppDomain.CurrentDomain.BaseDirectory + "PLCConnect";
         private OilSourceConfig oilSourceConfig = new OilSourceConfig();
         public OilSourceStatusModel OilSourceStatus { get; } = new OilSourceStatusModel();
         public static OilSource Default { get; } = new OilSource();
+        private object locker = new object();
         /// <summary>
         /// PLC断开连接后定时重连PLC计时器
         /// </summary>
@@ -75,26 +75,26 @@ namespace ShakerService.OilSource
             {
                 OilSourceStatus.OilErrors.AddRange(Enumerable.Repeat(new ErrorInfoModel(), oilSourceConfig.ErrorAddress.Count));
             }
-            _IsEnabled = oilSourceConfig.IsEnabled;
-            if (!_IsEnabled) return;
+            OilSourceStatus.IsEnabled = oilSourceConfig.IsEnabled;
+            if (!IsEnabled) return;
             var plcs = Tools.PluginsLoader.Defalut.Load<IPLCConnect.IPLCConnect>(PluginPath);
             _PLCConnect = plcs.FirstOrDefault(x => x.Protocol == oilSourceConfig.Protocol);
             if (_PLCConnect == null)
             {
                 OilSourceStatus.IsEnabled = false;
-                _IsEnabled = false;
                 return;
             }
             try
             {
                 _PLCConnect.Init(oilSourceConfig.IP, oilSourceConfig.Port);
-                _PLCConnect.Connect();
                 _PLCConnect.StatusChanged += PLCConnect_StatusChanged;
+                _PLCConnect.Connect();
             }
             catch
             {
                 
             }
+            OilSourceStatus.IsConnect = _PLCConnect.IsConnected;
             if(!_PLCConnect.IsConnected)
             {
                 RepeatConnectPLC();
@@ -112,7 +112,7 @@ namespace ShakerService.OilSource
             _timer.Enabled = false;
             _timer.AutoReset = true;
             _timer.Stop();
-            Communication.Instance.Context.GetEvent(nameof(OilSourceStatusModel.Circuit) + "_" + nameof(CircuitModel.Pressure)).Subscrip((sender, args) =>
+            Communication.Instance.Context.GetEvent(Topic.CircuitPressure).Subscrip((sender, args) =>
             {
                 if(args.Data.Length>=2 && args.Data[0] is int index && args.Data[1] is float pressure)
                 {
@@ -120,7 +120,7 @@ namespace ShakerService.OilSource
                     _PLCConnect?.Write(oilSourceConfig.MainPumpAddress[index].Address, pressure);
                 }
             });
-            Communication.Instance.Context.GetEvent(nameof(OilSourceStatusModel.Circuit) + "_Start").Subscrip((sender, args) =>
+            Communication.Instance.Context.GetEvent(Topic.CircuitStart).Subscrip((sender, args) =>
             {
                 if (args.Data.Length >= 2 && args.Data[0] is int index && args.Data[1] is bool state)
                 {
@@ -135,7 +135,7 @@ namespace ShakerService.OilSource
                     }
                 }
             });
-            Communication.Instance.Context.GetEvent(nameof(OilSourceStatusModel.Circuit) + "_Load").Subscrip((sender, args) =>
+            Communication.Instance.Context.GetEvent(Topic.CircuitLoad).Subscrip((sender, args) =>
             {
                 if (args.Data.Length >= 2 && args.Data[0] is int index && args.Data[1] is bool state)
                 {
@@ -151,7 +151,7 @@ namespace ShakerService.OilSource
                 }
             });
 
-            Communication.Instance.Context.GetEvent(nameof(OilSourceStatusModel.Forerunner) + "_" + nameof(CircuitModel.Pressure)).Subscrip((sender, args) =>
+            Communication.Instance.Context.GetEvent(Topic.ForerunnerPressure).Subscrip((sender, args) =>
             {
                 if (args.Data.Length >= 1 && args.Data[0] is float pressure)
                 {
@@ -159,7 +159,7 @@ namespace ShakerService.OilSource
                     _PLCConnect?.Write(oilSourceConfig.ForerunnerAddress.Address, pressure);
                 }
             });
-            Communication.Instance.Context.GetEvent(nameof(OilSourceStatusModel.Forerunner) + "_Start").Subscrip((sender, args) =>
+            Communication.Instance.Context.GetEvent(Topic.ForerunnerStart).Subscrip((sender, args) =>
             {
                 if (args.Data.Length >= 1  && args.Data[0] is bool state)
                 {
@@ -174,7 +174,7 @@ namespace ShakerService.OilSource
                     }
                 }
             });
-            Communication.Instance.Context.GetEvent(nameof(OilSourceStatusModel.Forerunner) + "_Load").Subscrip((sender, args) =>
+            Communication.Instance.Context.GetEvent(Topic.ForerunnerLoad).Subscrip((sender, args) =>
             {
                 if (args.Data.Length >= 1  && args.Data[0] is bool state)
                 {
@@ -190,7 +190,7 @@ namespace ShakerService.OilSource
                 }
             });
 
-            Communication.Instance.Context.GetEvent(nameof(OilSourceStatusModel.Assistant) + "_" + nameof(CircuitModel.Pressure)).Subscrip((sender, args) =>
+            Communication.Instance.Context.GetEvent(Topic.AssistantPressure).Subscrip((sender, args) =>
             {
                 if (args.Data.Length >= 1 && args.Data[0] is float pressure)
                 {
@@ -198,7 +198,7 @@ namespace ShakerService.OilSource
                     _PLCConnect?.Write(oilSourceConfig.AssistantAddress.Address, pressure);
                 }
             });
-            Communication.Instance.Context.GetEvent(nameof(OilSourceStatusModel.Assistant) + "_Start").Subscrip((sender, args) =>
+            Communication.Instance.Context.GetEvent(Topic.AssistantStart).Subscrip((sender, args) =>
             {
                 if (args.Data.Length >= 1  && args.Data[0] is bool state)
                 {
@@ -213,7 +213,7 @@ namespace ShakerService.OilSource
                     }
                 }
             });
-            Communication.Instance.Context.GetEvent(nameof(OilSourceStatusModel.Assistant) + "_Load").Subscrip((sender, args) =>
+            Communication.Instance.Context.GetEvent(Topic.AssistantLoad).Subscrip((sender, args) =>
             {
                 if (args.Data.Length >= 1  && args.Data[0] is bool state)
                 {
@@ -229,7 +229,7 @@ namespace ShakerService.OilSource
                 }
             });
 
-            Communication.Instance.Context.GetEvent(nameof(OilSourceStatusModel.Circulate) + "_" + nameof(CircuitModel.Pressure)).Subscrip((sender, args) =>
+            Communication.Instance.Context.GetEvent(Topic.CirculatePressure).Subscrip((sender, args) =>
             {
                 if (args.Data.Length >= 1  && args.Data[0] is float pressure)
                 {
@@ -237,7 +237,7 @@ namespace ShakerService.OilSource
                     _PLCConnect?.Write(oilSourceConfig.CirculateAddress.Address, pressure);
                 }
             });
-            Communication.Instance.Context.GetEvent(nameof(OilSourceStatusModel.Circulate) + "_Start").Subscrip((sender, args) =>
+            Communication.Instance.Context.GetEvent(Topic.CirculateStart).Subscrip((sender, args) =>
             {
                 if (args.Data.Length >= 1  && args.Data[0] is bool state)
                 {
@@ -252,7 +252,7 @@ namespace ShakerService.OilSource
                     }
                 }
             });
-            Communication.Instance.Context.GetEvent(nameof(OilSourceStatusModel.Circulate) + "_Load").Subscrip((sender, args) =>
+            Communication.Instance.Context.GetEvent(Topic.CirculateLoad).Subscrip((sender, args) =>
             {
                 if (args.Data.Length >= 1  && args.Data[0] is bool state)
                 {
@@ -268,7 +268,7 @@ namespace ShakerService.OilSource
                 }
             });
 
-            Communication.Instance.Context.GetEvent("EmergencyStop").Subscrip((sender, args) =>
+            Communication.Instance.Context.GetEvent(Topic.OilEmergencyStop).Subscrip((sender, args) =>
             {
                 if (args.Data.Length >= 1 && args.Data[0] is bool state)
                 {
@@ -292,8 +292,11 @@ namespace ShakerService.OilSource
                 Ping ping = new Ping();
                 if(ping.Send(oilSourceConfig.IP).Status == IPStatus.Success)
                 {
-                    _PLCConnect.Init(oilSourceConfig.IP, oilSourceConfig.Port);
-                    _PLCConnect.Connect();
+                    //_PLCConnect.Init(oilSourceConfig.IP, oilSourceConfig.Port);
+                    lock (locker)
+                    {
+                        _PLCConnect.Connect();
+                    }
                 }
             }
             catch
@@ -318,61 +321,65 @@ namespace ShakerService.OilSource
         public bool IsConnected => _PLCConnect == null ? false : _PLCConnect.IsConnected;
         public void Connect()
         {
-            if (!_IsEnabled) return;
+            if (!IsEnabled) return;
             _PLCConnect?.Connect();
         }
         public void Disconnect()
         {
-            if (!_IsEnabled) return;
+            if (!IsEnabled) return;
             _PLCConnect?.DisConnect();
         }
         private void PLCConnect_StatusChanged(object? sender, bool e)
         {
-            _IsEnabled = e;
+            OilSourceStatus.IsConnect = e;
             StatusChanged?.Invoke(this, e);
+            Log.Default.Debug("油源状态改变:" + e);
             if(e)
             {
                 _timer.Stop();
             }
             else
             {
-                ReadOilSourceData();
+                RepeatConnectPLC();
             }
         }
 
         public void ReadOilSourceData()
         {
-            if(!_IsEnabled || _PLCConnect ==null)
+            lock (locker)
             {
-                OilSourceStatus.IsEnabled = false;
-                return;
-            }
-            for(int i=0;i<OilSourceStatus.Circuit.Count;i++)
-            {
-                OilSourceStatus.Circuit[i].IsStart = _PLCConnect.ReadBit(oilSourceConfig.MainPumpAddress[i].IsStartAddress, oilSourceConfig.MainPumpAddress[i].IsStartBitIndex);
-                //OilSourceStatus.Circuit[i].Pressure = _PLCConnect.Read<float>(oilSourceConfig.MainPumpAddress[i].Address);
-                OilSourceStatus.Circuit[i].IsLoadPressure = _PLCConnect.ReadBit(oilSourceConfig.MainPumpAddress[i].IsLoadAddress, oilSourceConfig.MainPumpAddress[i].IsLoadBitIndex);
-            }
+                if (!IsEnabled || _PLCConnect == null || !_PLCConnect.IsConnected)
+                {
+                    return;
+                }
+                for (int i = 0; i < OilSourceStatus.Circuit.Count; i++)
+                {
+                    OilSourceStatus.Circuit[i].IsStart = _PLCConnect.ReadBit(oilSourceConfig.MainPumpAddress[i].IsStartAddress, oilSourceConfig.MainPumpAddress[i].IsStartBitIndex);
+                    //OilSourceStatus.Circuit[i].Pressure = _PLCConnect.Read<float>(oilSourceConfig.MainPumpAddress[i].Address);
+                    OilSourceStatus.Circuit[i].IsLoadPressure = _PLCConnect.ReadBit(oilSourceConfig.MainPumpAddress[i].IsLoadAddress, oilSourceConfig.MainPumpAddress[i].IsLoadBitIndex);
+                }
 
-            OilSourceStatus.Circulate.IsLoadPressure = _PLCConnect.ReadBit(oilSourceConfig.CirculateAddress.IsLoadAddress, oilSourceConfig.CirculateAddress.IsLoadBitIndex);
+                OilSourceStatus.Circulate.IsLoadPressure = _PLCConnect.ReadBit(oilSourceConfig.CirculateAddress.IsLoadAddress, oilSourceConfig.CirculateAddress.IsLoadBitIndex);
 
-            OilSourceStatus.Circulate.IsStart = _PLCConnect.ReadBit(oilSourceConfig.CirculateAddress.IsStartAddress, oilSourceConfig.CirculateAddress.IsStartBitIndex);
-            
+                OilSourceStatus.Circulate.IsStart = _PLCConnect.ReadBit(oilSourceConfig.CirculateAddress.IsStartAddress, oilSourceConfig.CirculateAddress.IsStartBitIndex);
 
-            OilSourceStatus.Forerunner.IsStart = _PLCConnect.ReadBit(oilSourceConfig.ForerunnerAddress.IsStartAddress, oilSourceConfig.ForerunnerAddress.IsStartBitIndex);
-            OilSourceStatus.Forerunner.IsLoadPressure = _PLCConnect.ReadBit(oilSourceConfig.ForerunnerAddress.IsLoadAddress, oilSourceConfig.ForerunnerAddress.IsLoadBitIndex);
-            for(int i=0;i<OilSourceStatus.OilErrors.Count;i++)
-            {
-                OilSourceStatus.OilErrors[i].Status = _PLCConnect.ReadBit(oilSourceConfig.ErrorAddress[i].Address, oilSourceConfig.ErrorAddress[i].BitIndex);
-            }
-            OilSourceStatus.IsRemote = _PLCConnect.ReadBit(oilSourceConfig.IsRemoteAddress.Address, oilSourceConfig.IsRemoteAddress.BitIndex);
-            for(int i=0;i<OilSourceStatus.OilSourceAnalogs.Count;i++)
-            {
-                OilSourceStatus.OilSourceAnalogs[i].Value = _PLCConnect.Read<float>(oilSourceConfig.AnalogAddress[i].ValueAddress);
-                OilSourceStatus.OilSourceAnalogs[i].IsLowerError = _PLCConnect.ReadBit(oilSourceConfig.AnalogAddress[i].LowerErrorAddress, oilSourceConfig.AnalogAddress[i].LowerErrorBitIndex);
-                OilSourceStatus.OilSourceAnalogs[i].IsLowerWarn = _PLCConnect.ReadBit(oilSourceConfig.AnalogAddress[i].LowerWarnAddress, oilSourceConfig.AnalogAddress[i].LowerWarnBitIndex);
-                OilSourceStatus.OilSourceAnalogs[i].IsUpperError = _PLCConnect.ReadBit(oilSourceConfig.AnalogAddress[i].UpperErrorAddress, oilSourceConfig.AnalogAddress[i].UpperErrorBitIndex);
-                OilSourceStatus.OilSourceAnalogs[i].IsUpperWarn = _PLCConnect.ReadBit(oilSourceConfig.AnalogAddress[i].UpperWarnAddress, oilSourceConfig.AnalogAddress[i].UpperWarnBitIndex);
+
+                OilSourceStatus.Forerunner.IsStart = _PLCConnect.ReadBit(oilSourceConfig.ForerunnerAddress.IsStartAddress, oilSourceConfig.ForerunnerAddress.IsStartBitIndex);
+                OilSourceStatus.Forerunner.IsLoadPressure = _PLCConnect.ReadBit(oilSourceConfig.ForerunnerAddress.IsLoadAddress, oilSourceConfig.ForerunnerAddress.IsLoadBitIndex);
+                for (int i = 0; i < OilSourceStatus.OilErrors.Count; i++)
+                {
+                    OilSourceStatus.OilErrors[i].Status = _PLCConnect.ReadBit(oilSourceConfig.ErrorAddress[i].Address, oilSourceConfig.ErrorAddress[i].BitIndex);
+                }
+                OilSourceStatus.IsRemote = _PLCConnect.ReadBit(oilSourceConfig.IsRemoteAddress.Address, oilSourceConfig.IsRemoteAddress.BitIndex);
+                for (int i = 0; i < OilSourceStatus.OilSourceAnalogs.Count; i++)
+                {
+                    OilSourceStatus.OilSourceAnalogs[i].Value = _PLCConnect.Read<float>(oilSourceConfig.AnalogAddress[i].ValueAddress);
+                    OilSourceStatus.OilSourceAnalogs[i].IsLowerError = _PLCConnect.ReadBit(oilSourceConfig.AnalogAddress[i].LowerErrorAddress, oilSourceConfig.AnalogAddress[i].LowerErrorBitIndex);
+                    OilSourceStatus.OilSourceAnalogs[i].IsLowerWarn = _PLCConnect.ReadBit(oilSourceConfig.AnalogAddress[i].LowerWarnAddress, oilSourceConfig.AnalogAddress[i].LowerWarnBitIndex);
+                    OilSourceStatus.OilSourceAnalogs[i].IsUpperError = _PLCConnect.ReadBit(oilSourceConfig.AnalogAddress[i].UpperErrorAddress, oilSourceConfig.AnalogAddress[i].UpperErrorBitIndex);
+                    OilSourceStatus.OilSourceAnalogs[i].IsUpperWarn = _PLCConnect.ReadBit(oilSourceConfig.AnalogAddress[i].UpperWarnAddress, oilSourceConfig.AnalogAddress[i].UpperWarnBitIndex);
+                }
+                Communication.Instance.Context.GetEvent<OilSourceStatusModel>().Publish(this, OilSourceStatus);
             }
         }
         static OilSource()

+ 39 - 3
Shaker/OilSource/OilSourcePumpAddressConfig.cs

@@ -4,17 +4,20 @@ namespace ShakerService.OilSource
 {
     public class OilSourcePumpAddressConfig
     {
+        /// <summary>
+        /// 名字
+        /// </summary>
         public string Name = "";
         /// <summary>
-        /// 模拟量地址
+        /// 压力设定地址
         /// </summary>
         public string Address = "";
         /// <summary>
-        /// 启动地址
+        /// 是否启动地址
         /// </summary>
         public string IsStartAddress = "";
         /// <summary>
-        /// 启动位索引
+        /// 是否启动位索引
         /// </summary>
         public byte IsStartBitIndex = 0;
         /// <summary>
@@ -25,23 +28,56 @@ namespace ShakerService.OilSource
         /// 是否加载位索引
         /// </summary>
         public byte IsLoadBitIndex = 0;
+        /// <summary>
+        /// 是否启用
+        /// </summary>
         public bool IsEnabled = true;
+        /// <summary>
+        /// 是否启用压力控制
+        /// </summary>
         public bool IsPressureEnabled = true;
+        /// <summary>
+        /// 是否可加载
+        /// </summary>
         public bool IsLoadEnabled = true;
 
+        /// <summary>
+        /// 开始地址
+        /// </summary>
         public string StartAddress = "";
+        /// <summary>
+        /// 开始位
+        /// </summary>
         public byte StartBitIndex = 0;
 
+        /// <summary>
+        /// 加载地址
+        /// </summary>
         public string LoadAddress = "";
 
+        /// <summary>
+        /// 加载地址位序号
+        /// </summary>
         public byte LoadBitIndex = 0;
 
+        /// <summary>
+        /// 停止地址
+        /// </summary>
         public string StopAddress = "";
+        /// <summary>
+        /// 停止位
+        /// </summary>
         public byte StopBitIndex = 0;
 
 
+        /// <summary>
+        /// 卸载地址
+        /// </summary>
         public string UnLoadAddress = "";
 
+        /// <summary>
+        /// 卸载地址位
+        /// </summary>
         public byte UnLoadBitIndex = 0;
     }
 }

+ 8 - 6
Shaker/Service.cs

@@ -112,18 +112,19 @@ namespace ShakerService
         }
         public void Init()
         {
-            OilSource.OilSource.Default.Init();
             OilSource.OilSource.Default.StatusChanged += (_, args) =>
             {
+                Communication.Instance.Context?.GetEvent(Topic.OilStatusChanged).Publish(this, null, args);
                 if(args)
                 {
-                    OilLock.Reset();
+                    OilLock.Set();
                 }
                 else
                 {
-                    OilLock.Set();
+                    OilLock.Reset();
                 }
             };
+            OilSource.OilSource.Default.Init();
             string path = System.IO.Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.lvbitx").FirstOrDefault()?? "Shaker.lvbitx";
             ShakerFpga.CreateFpga(new SIMDFxpConvert.SIMDFxpConverter());
             ShakerFpga.Instance.Open(path, "RIO0", NIFPGA.NiFpga_OpenAttribute.NiFpga_OpenAttribute_NoRun);
@@ -156,15 +157,16 @@ namespace ShakerService
         {
             shakertokenSource = new CancellationTokenSource();
             maintokenSource = new CancellationTokenSource();
-            WaitRandomDataLocker.Reset();
-            Task.Run(() => RandomControl());
             Task.Run(() => DeviceDiscovery());
 
             OilSource.OilSource.Default.Connect();
-            if(!OilSource.OilSource.Default.IsConnected)
+            if (!OilSource.OilSource.Default.IsConnected)
             {
                 OilLock.Reset();
             }
+            else OilLock.Set();
+            WaitRandomDataLocker.Reset();
+            Task.Run(() => RandomControl());
             UpAmpttimer = new Haukcode.HighResolutionTimer.HighResolutionTimer();
             UpAmpttimer.SetPeriod(5);
             UpAmpttimer.Stop();

+ 0 - 2
Shaker/ViewModel/ServiceDataCacheViewModel.cs

@@ -14,11 +14,9 @@ namespace ShakerService.ViewModel
     {
         public ServiceDataCacheViewModel()
         {
-            Log.Default.Debug("public ServiceDataCacheViewModel");
         }
         static ServiceDataCacheViewModel()
         {
-            Log.Default.Debug("static ServiceDataCacheViewModel");
         }
         public FxpConvert.Common.ICalc Calc { get; } = new SIMDFxpConvert.SIMDCalc();
         public int GetLength(int index)

+ 2 - 2
ShakerControl.sln

@@ -115,7 +115,7 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "FFTW", "Calc\FFTW\FFTW.shpr
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModBus", "PLCConnect\ModBus\ModBus.csproj", "{ED35D74A-E634-4CE3-A57A-DA1B3E1840A7}"
 EndProject
-Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "TdmsFile", "TdmsFile\TdmsFile.shproj", "{6A45FECC-B176-47B8-89D1-D3BCC4FBF12E}"
+Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "ShakerFile", "ShakerFile\ShakerFile\ShakerFile.shproj", "{1B007F83-8D00-4C26-AD5B-27634A5CCCFC}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -306,6 +306,7 @@ Global
 	GlobalSection(SharedMSBuildProjectFiles) = preSolution
 		PLCConnect\NModbus\NModbus.projitems*{0cc4fd0b-b835-4789-9175-f6fbc74cc356}*SharedItemsImports = 5
 		NIFPGA\NIFPGA.projitems*{0fc2485c-345e-41bf-bbc7-876e25af92f4}*SharedItemsImports = 13
+		ShakerFile\ShakerFile\ShakerFile.projitems*{1b007f83-8d00-4c26-ad5b-27634a5cccfc}*SharedItemsImports = 13
 		NativeLoader\NativeLoader.projitems*{2b1dd602-d589-4709-965b-2a3ae83257d9}*SharedItemsImports = 13
 		OxyPlot\OxyPlot\OxyPlot.projitems*{317bfa99-dc31-45dd-81f8-1002ecd0e909}*SharedItemsImports = 5
 		NativeLoader\NativeLoader.projitems*{32592e1e-e305-4241-a454-d19c56c158f2}*SharedItemsImports = 5
@@ -318,7 +319,6 @@ Global
 		Communication\ActiveMQ\ActiveMQ\Apache.NMS\Apache.NMS.projitems*{4bc05ed0-2cea-4726-a0dc-e86810ad3fc3}*SharedItemsImports = 5
 		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
-		TdmsFile\TdmsFile.projitems*{6a45fecc-b176-47b8-89d1-d3bcc4fbf12e}*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

+ 1 - 1
Timer/Timer/MultimediaTimer.cs

@@ -109,7 +109,7 @@ namespace Haukcode.HighResolutionTimer
             CheckDisposed();
 
             if (!IsRunning)
-                throw new InvalidOperationException("Timer has not been started");
+                return;
 
             StopInternal();
         }