ソースを参照

Update Client UICLient Server Communicator Line

Zixuan 3 日 前
コミット
c568e28d97

+ 2 - 1
Data/GeneralData/Enums.cs

@@ -52,7 +52,8 @@ public enum DeviceStatus
     ChargeProcessDischarging = 10,
     LoadProcessStockering,
     LoadProcessUnloading,
-    ReutrnWafer
+    ReutrnWafer,
+    Undefinde = 100
 }
 
 public enum TubeStatus

+ 1 - 1
EEMSCenterUI/Views/MainWindow.xaml

@@ -9,7 +9,7 @@
         mc:Ignorable="d"
         WindowChrome.WindowChrome="{DynamicResource WindowChromeKey}"
         ResizeMode="CanMinimize" WindowStyle="SingleBorderWindow"
-        Title="MainWindow" Height="450" Width="800">
+        Title="EEMS Server" Height="450" Width="800">
 
 
     <Grid>

+ 12 - 1
EEMSMain/Service/UIProvider.cs

@@ -56,8 +56,19 @@ internal class UIProvider(DeviceCollection deviceCollection) : IUIProvider
 
         App.Current.Dispatcher?.Invoke(() =>
         {
+            deviceData.OtherInfo.Clear();
             foreach (var item in data)
-                deviceData.OtherInfo[item.Key] = item.Value;
+            {
+                switch (item.Key)
+                {
+                    case "Status":
+                        deviceData.DeviceStatus = item.Value.ToString();
+                        break;
+                    default:
+                        deviceData.OtherInfo[item.Key] = item.Value;
+                        break;
+                }
+            }
         });
 
         return Task.FromResult(true);

+ 12 - 0
EEMSMain/ViewModels/MainWindowViewModel.cs

@@ -3,6 +3,7 @@ using EEMSUIClientCore;
 using Mapster;
 using ServiceBase;
 using System;
+using System.Windows.Controls;
 
 namespace EEMSMain.ViewModels;
 
@@ -178,6 +179,17 @@ public partial class MainWindowViewModel : ObservableObject
                     if (device.Guid is null)
                         continue;
                     this._deviceCollection.DeviceList[device.Guid.Value] = device.Adapt<DeviceInfo_VM>();
+
+                    DeviceData_VM deviceData = new()
+                    {
+                        DeviceId = device.Guid.Value,
+                        DeviceModel = device.DeviceModel,
+                        DeviceStatus = (DeviceStatus.Undefinde).ToString(),
+                        Alarms = []
+                    };
+
+                    this._deviceCollection.DeviceDataList[device.Guid.Value] = deviceData;
+
                 }
             });
 

+ 1 - 1
EEMSMain/Views/MainWindow.xaml

@@ -7,7 +7,7 @@
         mc:Ignorable="d"
         xmlns:prism="http://prismlibrary.com/"
         prism:ViewModelLocator.AutoWireViewModel="True"
-        Title="MainWindow" Height="450" Width="800">
+        Title="EEMS Device Management Tool" Height="450" Width="800">
     <Grid>
         <Grid.ColumnDefinitions>
             <ColumnDefinition Width="auto"/>

+ 1 - 1
EEMSUIClient/App.xaml.cs

@@ -14,7 +14,7 @@ public partial class App : PrismApplication
     {
         containerRegistry.RegisterSingleton<TrayController>();
         containerRegistry.RegisterSingleton<ClientService>();
-        containerRegistry.RegisterSingleton<DeviceInfo_VM>();
+
         containerRegistry.RegisterSingleton<ConfigPath>();
         containerRegistry.RegisterSingleton<RunningData>();
         containerRegistry.RegisterSingleton<AddressInfo>();

+ 43 - 1
EEMSUIClient/Converter/DeviceModelEnumConvert.cs

@@ -18,4 +18,46 @@ public class DeviceModelEnumConvert : IValueConverter
 
         return result;
     }
-}
+}
+
+public class DeviceStatusEnumConvert : IValueConverter
+{
+    public object? Convert(object value, Type targetType, object parameter, CultureInfo culture)
+    {
+        return value?.ToString();
+    }
+
+    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+    {
+        if (value is not string s)
+            return value;
+
+        if (!Enum.TryParse(s, out DeviceStatus result))
+            return value;
+
+
+        return result;
+    }
+}
+
+
+public class RecipeTypeEnumConvert : IValueConverter
+{
+    public object? Convert(object value, Type targetType, object parameter, CultureInfo culture)
+    {
+        return value?.ToString();
+    }
+
+    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+    {
+        if (value is not string s)
+            return value;
+
+        if (!Enum.TryParse(s, out RecipeType result))
+            return value;
+
+
+        return result;
+    }
+}
+

+ 0 - 26
EEMSUIClient/Data/LocalFilePathInfo.cs

@@ -2,35 +2,9 @@
 
 public partial class LocalFilePathInfo : ObservableObject
 {
-    public LocalFilePathInfo()
-    {
-
-    }
-
-    public LocalFilePathInfo(IClientBaseProvider clientBaseProvider)
-    {
-        this._Client = clientBaseProvider;
-    }
-    private readonly IClientBaseProvider? _Client;
-
-
     [ObservableProperty]
     private string? _RecipePath;
 
     [ObservableProperty]
     private string? _ConfigPath;
-
-    partial void OnRecipePathChanged(string? value)
-    {
-        if (_Client is null)
-            return;
-        _Client.RecipePath = value;
-    }
-
-    partial void OnConfigPathChanged(string? value)
-    {
-        if (_Client is null)
-            return;
-        _Client.ConfigPath = value;
-    }
 }

+ 7 - 3
EEMSUIClient/Services/ClientBaseProvider.cs

@@ -1,6 +1,9 @@
-namespace EEMSUIClient.Services;
+using EEMSUIClient.Data;
+using System.Net;
 
-public class ClientBaseProvider : IClientBaseProvider
+namespace EEMSUIClient.Services;
+
+public class ClientBaseProvider(ConfigService configService, RunningData runningData) : IClientBaseProvider
 {
     string? IClientBaseProvider.RecipePath { get; set; }
     string? IClientBaseProvider.ConfigPath { get; set; }
@@ -23,6 +26,7 @@ public class ClientBaseProvider : IClientBaseProvider
 
     void IClientBaseProvider.UpdateDeviceInfo(DeviceInfo deviceInfo)
     {
-
+        deviceInfo.Adapt(runningData.DeviceInfo);
+        configService.SaveDeviceInfo();
     }
 }

+ 55 - 2
EEMSUIClient/Services/ClientService.cs

@@ -2,6 +2,7 @@
 
 public class ClientService(
      AddressInfo address,
+     RunningData runningData,
     IClientBaseProvider provider)
 {
     private IClientCaller? clientCaller;
@@ -47,15 +48,67 @@ public class ClientService(
 
     public bool StartDataCollectionService()
     {
+        _fakeRecipe.DeviceId = runningData.DeviceInfo.Guid ?? Guid.Empty;
         _timer?.Dispose();
-        _timer = new(TimerCallback, null, 0, 3000);
-
+        _timer = new(TimerCallback, null, 0, 1000 * interval);
+        //TimerCallback(null);
         return true;
     }
 
+    Recipe _fakeRecipe = new()
+    {
+        DeviceId = runningData.DeviceInfo.Guid ?? Guid.Empty,
+        RecipeInfo = [],
+        CurrentStepName = $"Step {coutner}",
+        NextStepName = $"Step {coutner + 1}",
+        CurrentStepRemainTime = 0,
+        CurrentStepTotalTime = max,
+        TotalRemainTime = 0,
+        TotalTime = max * cycle
+    };
+    static int interval = 1;
+    static int coutner = 1;
+    static int max = 10;
+    static int cycle = 3;
+
     private void TimerCallback(object? state)
     {
+        if (clientCaller is null)
+            return;
+
+        _fakeRecipe.CurrentStepRemainTime += interval;
+        _fakeRecipe.TotalRemainTime += interval;
+        clientCaller.UpdateRecipeInfo(_fakeRecipe);
+
+        if (_fakeRecipe.CurrentStepRemainTime >= max)
+        {
+            coutner++;
+            _fakeRecipe.CurrentStepRemainTime = 0;
+            _fakeRecipe.CurrentStepName = $"Step {coutner}";
+            if (coutner == 3)
+                _fakeRecipe.NextStepName = "Finish";
+            else
+                _fakeRecipe.NextStepName = $"Step {coutner + 1}";
+        }
+
 
+        if (_fakeRecipe.TotalRemainTime >= max * cycle)
+        {
+            _timer?.Dispose();
+            coutner = 1;
+            _fakeRecipe = new()
+            {
+                DeviceId = runningData.DeviceInfo.Guid ?? Guid.Empty,
+                RecipeInfo = [],
+                CurrentStepName = $"Step {coutner}",
+                NextStepName = $"Step {coutner + 1}",
+                CurrentStepRemainTime = 0,
+                CurrentStepTotalTime = max,
+                TotalRemainTime = 0,
+                TotalTime = max * cycle
+            };
+            return;
+        }
     }
 
 

+ 35 - 6
EEMSUIClient/ViewModels/MainWindowViewModel.cs

@@ -5,12 +5,16 @@ public partial class MainWindowViewModel
     RunningData running,
     AddressInfo address,
     ConfigService configService,
-    LocalFilePathInfo localFilePathInfo) : ObservableObject
+    LocalFilePathInfo localFilePathInfo,
+    IClientBaseProvider clientBaseProvider) : ObservableObject
 {
 
     private Dictionary<string, object> _realtimeData = [];
 
     [ObservableProperty]
+    private TempTestData _TempTest = new();
+
+    [ObservableProperty]
     private LocalFilePathInfo _localFilePathInfo = localFilePathInfo;
 
     [ObservableProperty]
@@ -34,6 +38,7 @@ public partial class MainWindowViewModel
             return;
 
         this.LocalFilePathInfo.RecipePath = dialog.FolderName;
+        clientBaseProvider.RecipePath = dialog.FolderName;
         if (!configService.SaveDirectoryInfo())
             MessageBox.Show("路径保存失败!");
     }
@@ -45,6 +50,7 @@ public partial class MainWindowViewModel
         if (dialog.ShowDialog() != true)
             return;
         this.LocalFilePathInfo.ConfigPath = dialog.FolderName;
+        clientBaseProvider.ConfigPath = dialog.FolderName;
         if (!configService.SaveDirectoryInfo())
             MessageBox.Show("路径保存失败!");
     }
@@ -94,13 +100,14 @@ public partial class MainWindowViewModel
     [RelayCommand]
     private void Trigger()
     {
+        _realtimeData.Clear();
+        _realtimeData.Add("PJobID", TempTest.PJobID);
+        _realtimeData.Add("CJboID", TempTest.CJobID);
+        _realtimeData.Add("RecipeType", TempTest.RecipeType.ToString());
+        _realtimeData.Add("Status", TempTest.DeviceStatus.ToString());
+
         try
         {
-            _realtimeData.Clear();
-            for (int i = 0; i < 10; i++)
-            {
-                _realtimeData[i.ToString()] = DateTime.Now.ToString();
-            }
             clientService.UpdateRealTimeData(_realtimeData);
         }
         catch (Exception ex)
@@ -108,4 +115,26 @@ public partial class MainWindowViewModel
             MessageBox.Show(ex.Message);
         }
     }
+
+    [RelayCommand]
+    private void StartDataService()
+    {
+        clientService.StartDataCollectionService();
+    }
 }
+
+
+public partial class TempTestData : ObservableObject
+{
+    [ObservableProperty]
+    private DeviceStatus _DeviceStatus;
+
+    [ObservableProperty]
+    private string _PJobID = string.Empty;
+
+    [ObservableProperty]
+    private string _CJobID = string.Empty;
+
+    [ObservableProperty]
+    private RecipeType _RecipeType;
+}

+ 179 - 72
EEMSUIClient/Views/MainWindow.xaml

@@ -10,7 +10,7 @@
         xmlns:sys="clr-namespace:System;assembly=mscorlib"
         xmlns:GeneralData="clr-namespace:GeneralData;assembly=GeneralData"
         xmlns:Convert="clr-namespace:EEMSUIClient.Converter"
-        Title="MainWindow" Height="450" Width="800"
+        Title="EEMS Client" Height="450" Width="900"
         WindowStartupLocation="CenterScreen" ResizeMode="CanMinimize" WindowStyle="SingleBorderWindow">
     <Window.Resources>
         <ObjectDataProvider x:Key="DeviceModelProvider" MethodName="GetNames" ObjectType="{x:Type sys:Enum}">
@@ -18,83 +18,190 @@
                 <x:Type TypeName="GeneralData:DeviceModel"/>
             </ObjectDataProvider.MethodParameters>
         </ObjectDataProvider>
+        <ObjectDataProvider x:Key="DeviceStatusProvider" MethodName="GetNames" ObjectType="{x:Type sys:Enum}">
+            <ObjectDataProvider.MethodParameters>
+                <x:Type TypeName="GeneralData:DeviceStatus"/>
+            </ObjectDataProvider.MethodParameters>
+        </ObjectDataProvider>
+        <ObjectDataProvider x:Key="RecipeTypeProvider" MethodName="GetNames" ObjectType="{x:Type sys:Enum}">
+            <ObjectDataProvider.MethodParameters>
+                <x:Type TypeName="GeneralData:RecipeType"/>
+            </ObjectDataProvider.MethodParameters>
+        </ObjectDataProvider>
         <Convert:DeviceModelEnumConvert x:Key="EnumConverter"/>
+        <Convert:DeviceStatusEnumConvert x:Key="StatusEnumConverter"/>
+        <Convert:RecipeTypeEnumConvert x:Key="RecipeEnumConverter"/>
     </Window.Resources>
-    <Grid Margin="5,5,5,5" >
+    <Grid>
         <Grid.RowDefinitions>
-            <RowDefinition Height="1*"/>
-            <RowDefinition Height="3*"/>
+            <RowDefinition Height="auto"/>
+            <RowDefinition Height="4"/>
+            <RowDefinition Height="auto"/>
+            <RowDefinition Height="4"/>
+            <RowDefinition Height="*"/>
         </Grid.RowDefinitions>
-        <Grid Grid.Row="0" Background="#FFCECECE">
-            <Grid.RowDefinitions>
-                <RowDefinition Height="1*"/>
-                <RowDefinition Height="1*"/>
-            </Grid.RowDefinitions>
-            <Grid.ColumnDefinitions>
-                <ColumnDefinition Width="1*"/>
-                <ColumnDefinition Width="1*"/>
-                <ColumnDefinition Width="1*"/>
-                <ColumnDefinition Width="1*"/>
-            </Grid.ColumnDefinitions>
-            <StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal" Margin="3,3,3,3" HorizontalAlignment="Center" VerticalAlignment="Center">
-                <Label Content="IP:" Height="30"/>
-                <TextBox Text="{Binding Address.Ip}" Width="150" Height="30" IsEnabled="{Binding IsNotConnected}"/>
-            </StackPanel>
-            <StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal" Margin="3,3,3,3" HorizontalAlignment="Center" VerticalAlignment="Center">
-                <Label Content="Port:" Height="30"/>
-                <TextBox Text="{Binding Address.Port}" Width="150" Height="30" IsEnabled="{Binding IsNotConnected}"/>
-            </StackPanel>
-            <StackPanel Grid.Row="0" Grid.Column="2" Orientation="Horizontal" Margin="3,3,3,3" HorizontalAlignment="Center" VerticalAlignment="Center">
-                <Label Content="HubName:" Height="30"/>
-                <TextBox Text="{Binding Address.HubName}" Width="100" Height="30" IsEnabled="{Binding IsNotConnected}"/>
-            </StackPanel>
-            <StackPanel Grid.Row="0" Grid.Column="3" Orientation="Horizontal" Margin="3,3,3,3" HorizontalAlignment="Center" VerticalAlignment="Center">
-                <Button Content="Connect" Command="{Binding ConnectCommand}" Height="30" Width="60" IsEnabled="{Binding IsNotConnected}"/>
-            </StackPanel>
-            <StackPanel Grid.Row="1" Grid.Column="0" Orientation="Horizontal" Margin="3,3,3,3" HorizontalAlignment="Right" VerticalAlignment="Center">
-                <Button Content="Recipe" Command="{Binding SelectRecipeCommand}" Height="30" Width="60"/>
-            </StackPanel>
-            <StackPanel Grid.Row="1" Grid.Column="1" Orientation="Horizontal" Margin="3,3,3,3" HorizontalAlignment="Left" VerticalAlignment="Center">
-                <TextBox Text="{Binding LocalFilePathInfo.RecipePath}" IsReadOnly="True" Height="30" Width="160"/>
-            </StackPanel>
-            <StackPanel Grid.Row="1" Grid.Column="2" Orientation="Horizontal" Margin="3,3,3,3" HorizontalAlignment="Right" VerticalAlignment="Center">
-                <Button Content="Config" Command="{Binding SelectConfigCommand}" Height="30" Width="60"/>
-            </StackPanel>
-            <StackPanel Grid.Row="1" Grid.Column="3" Orientation="Horizontal" Margin="3,3,3,3" HorizontalAlignment="Center" VerticalAlignment="Center">
-                <TextBox Text="{Binding LocalFilePathInfo.ConfigPath}" IsReadOnly="True" Height="30" Width="160"/>
-            </StackPanel>
-        </Grid>
-        <Grid Grid.Row="1" Margin="3,5,3,5" Background="#FFCECECE">
-            <Grid.RowDefinitions>
-                <RowDefinition Height="1*"/>
-                <RowDefinition Height="1*"/>
-                <RowDefinition Height="1*"/>
-                <RowDefinition Height="1*"/>
-                <RowDefinition Height="1*"/>
-                <RowDefinition Height="1*"/>
-                <RowDefinition Height="1*"/>
-            </Grid.RowDefinitions>
+        <Border Grid.Row="0" Background="#FFCECECE" IsEnabled="{Binding IsNotConnected}">
+            <Grid Margin="8">
+                <Grid.ColumnDefinitions>
+                    <ColumnDefinition Width="auto"/>
+                    <ColumnDefinition Width="8"/>
+                    <ColumnDefinition/>
+                    <ColumnDefinition Width="8"/>
+
+                    <ColumnDefinition Width="auto"/>
+                    <ColumnDefinition Width="8"/>
+                    <ColumnDefinition/>
+                    <ColumnDefinition Width="8"/>
+
+                    <ColumnDefinition Width="auto"/>
+                    <ColumnDefinition Width="8"/>
+                    <ColumnDefinition/>
+                    <ColumnDefinition Width="8"/>
+
+                    <ColumnDefinition/>
+                </Grid.ColumnDefinitions>
+
+                <Label Grid.Column="0" Content="IP:" />
+                <TextBox Grid.Column="2" Text="{Binding Address.Ip}" VerticalContentAlignment="Center" />
+
+                <Label Grid.Column="4" Content="Port:" />
+                <TextBox Grid.Column="6" Text="{Binding Address.Port}" VerticalContentAlignment="Center" />
+
+                <Label Grid.Column="8" Content="HubName:" />
+                <TextBox Grid.Column="10" Text="{Binding Address.HubName}"  VerticalContentAlignment="Center" />
+
+                <Button Grid.Column="12" Content="Connect" Command="{Binding ConnectCommand}"/>
+
+            </Grid>
+        </Border>
+
+        <Border Grid.Row="2" Background="#FFCECECE">
+            <Grid Margin="8" Height="30">
+                <Grid.ColumnDefinitions>
+                    <ColumnDefinition Width="60"/>
+                    <ColumnDefinition Width="8"/>
+                    <ColumnDefinition/>
+                    <ColumnDefinition Width="16"/>
+
+                    <ColumnDefinition Width="60"/>
+                    <ColumnDefinition Width="8"/>
+                    <ColumnDefinition/>
+                    <ColumnDefinition Width="8"/>
+
+                </Grid.ColumnDefinitions>
+                <Button Content="Recipe" Command="{Binding SelectRecipeCommand}" />
+                <TextBox Grid.Column="2" Text="{Binding LocalFilePathInfo.RecipePath}" IsReadOnly="True" VerticalContentAlignment="Center" />
+                <Button Grid.Column="4" Content="Config" Command="{Binding SelectConfigCommand}" />
+                <TextBox Grid.Column="6" Text="{Binding LocalFilePathInfo.ConfigPath}" IsReadOnly="True" VerticalContentAlignment="Center" />
+            </Grid>
+        </Border>
+
+        <Grid Grid.Row="4" >
             <Grid.ColumnDefinitions>
-                <ColumnDefinition Width="0.8*"/>
-                <ColumnDefinition Width="1*"/>
-                <ColumnDefinition Width="1*"/>
+                <ColumnDefinition Width="420"/>
+                <ColumnDefinition Width="8"/>
+                <ColumnDefinition Width="300"/>
+                <ColumnDefinition Width="8"/>
+                <ColumnDefinition Width="*"/>
             </Grid.ColumnDefinitions>
-            <Label Grid.Row="0" Grid.Column="0" Content="Device Model:" HorizontalAlignment="Right" Height="30"/>
-            <ComboBox Grid.Row="0" Grid.Column="1" ItemsSource="{Binding Source={StaticResource DeviceModelProvider} }" SelectedItem="{Binding Running.DeviceInfo.DeviceModel, Converter={StaticResource EnumConverter}}" Height="30"/>
-            <Label Grid.Row="1" Grid.Column="0" Content="Device SubModel:" HorizontalAlignment="Right" Height="30"/>
-            <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Running.DeviceInfo.DeviceSubModel}" Height="30"/>
-            <Label Grid.Row="2" Grid.Column="0" Content="Device Name:" HorizontalAlignment="Right" Height="30"/>
-            <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Running.DeviceInfo.DeviceName}" Height="30"/>
-            <Label Grid.Row="3" Grid.Column="0" Content="Position:" HorizontalAlignment="Right" Height="30"/>
-            <TextBox Grid.Row="3" Grid.Column="1" Text="{Binding Running.DeviceInfo.Position}" Height="30"/>
-            <Label Grid.Row="4" Grid.Column="0" Content="Software Version:" HorizontalAlignment="Right" Height="30"/>
-            <TextBox Grid.Row="4" Grid.Column="1" Text="{Binding Running.DeviceInfo.SoftwareVersion}" Height="30"/>
-            <Label Grid.Row="5" Grid.Column="0" Content="DB Connection String:" HorizontalAlignment="Right" Height="30"/>
-            <TextBox Grid.Row="5" Grid.Column="1" Text="{Binding Running.DeviceInfo.DBConnectionString}" Height="30"/>
-            <Label Grid.Row="6" Grid.Column="0" Content="Guid:" HorizontalAlignment="Right" Height="30"/>
-            <TextBox Grid.Row="6" Grid.Column="1" Text="{Binding Running.DeviceInfo.Guid}" Height="30" IsReadOnly="True"/>
-            <Button Grid.Row="6" Grid.Column="2" Content="Register" Command="{Binding RegisterCommand}" Height="30" Width="100"/>
-            <Button Grid.Row="0" Grid.Column="2" Content="Trigger" Command="{Binding TriggerCommand}" Height="30" Width="100"/>
+            <Border Background="#FFCECECE">
+                <Grid  Margin="8">
+                    <Grid.RowDefinitions>
+                        <RowDefinition Height="Auto"/>
+                        <RowDefinition Height="*"/>
+                        <RowDefinition Height="Auto"/>
+                        <RowDefinition Height="*"/>
+                        <RowDefinition Height="Auto"/>
+                        <RowDefinition Height="*"/>
+                        <RowDefinition Height="Auto"/>
+                        <RowDefinition Height="*"/>
+                        <RowDefinition Height="Auto"/>
+                        <RowDefinition Height="*"/>
+                        <RowDefinition Height="Auto"/>
+                        <RowDefinition Height="*"/>
+                        <RowDefinition Height="Auto"/>
+                        <RowDefinition Height="*"/>
+                        <RowDefinition Height="Auto"/>
+                    </Grid.RowDefinitions>
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition Width="auto"/>
+                        <ColumnDefinition Width="8"/>
+                        <ColumnDefinition Width="*"/>
+                    </Grid.ColumnDefinitions>
+                    <Label Grid.Row="0" Grid.Column="0" Content="Device Model:" VerticalAlignment="Center"/>
+                    <ComboBox Grid.Row="0" Grid.Column="2" VerticalContentAlignment="Center"
+                          ItemsSource="{Binding Source={StaticResource DeviceModelProvider} }" SelectedItem="{Binding Running.DeviceInfo.DeviceModel, Converter={StaticResource EnumConverter}}"/>
+
+                    <Label Grid.Row="2" Grid.Column="0" Content="Device SubModel:"  VerticalAlignment="Center"/>
+                    <TextBox Grid.Row="2" Grid.Column="2" Text="{Binding Running.DeviceInfo.DeviceSubModel}" VerticalContentAlignment="Center"/>
+
+                    <Label Grid.Row="4" Grid.Column="0" Content="Device Name:" VerticalAlignment="Center" />
+                    <TextBox Grid.Row="4" Grid.Column="2" Text="{Binding Running.DeviceInfo.DeviceName}" VerticalContentAlignment="Center"/>
+
+                    <Label Grid.Row="6" Grid.Column="0" Content="Position:"  VerticalAlignment="Center"/>
+                    <TextBox Grid.Row="6" Grid.Column="2" Text="{Binding Running.DeviceInfo.Position}" VerticalContentAlignment="Center"/>
+
+                    <Label Grid.Row="8" Grid.Column="0" Content="Software Version:"  VerticalAlignment="Center"/>
+                    <TextBox Grid.Row="8" Grid.Column="2" Text="{Binding Running.DeviceInfo.SoftwareVersion}" VerticalContentAlignment="Center"/>
+
+                    <Label Grid.Row="10" Grid.Column="0" Content="DB Connection String:"  VerticalAlignment="Center"/>
+                    <TextBox Grid.Row="10" Grid.Column="2" Text="{Binding Running.DeviceInfo.DBConnectionString}" VerticalContentAlignment="Center"/>
+
+                    <Label Grid.Row="12" Grid.Column="0" Content="Guid:"  VerticalAlignment="Center"/>
+                    <TextBox Grid.Row="12" Grid.Column="2" Text="{Binding Running.DeviceInfo.Guid}" IsReadOnly="True" VerticalContentAlignment="Center"/>
+
+                    <Button Grid.Row="14" Grid.ColumnSpan="3" Height="28" Content="Register" Command="{Binding RegisterCommand}"/>
+                </Grid>
+            </Border>
+
+            <Border Grid.Column="2" Background="#FFCECECE">
+                <Grid Margin="8">
+                    <Grid.RowDefinitions>
+                        <RowDefinition Height="Auto"/>
+                        <RowDefinition Height="*"/>
+                        <RowDefinition Height="Auto"/>
+                        <RowDefinition Height="*"/>
+                        <RowDefinition Height="Auto"/>
+                        <RowDefinition Height="*"/>
+                        <RowDefinition Height="Auto"/>
+                        <RowDefinition Height="*"/>
+                        <RowDefinition Height="Auto"/>
+                    </Grid.RowDefinitions>
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition Width="auto"/>
+                        <ColumnDefinition Width="8"/>
+                        <ColumnDefinition Width="*"/>
+                    </Grid.ColumnDefinitions>
+
+                    <Label Grid.Row="0" Grid.Column="0" Content="Device Status:" VerticalAlignment="Center"/>
+                    <ComboBox Grid.Row="0" Grid.Column="2" VerticalContentAlignment="Center"
+                        ItemsSource="{Binding Source={StaticResource DeviceStatusProvider} }"  SelectedItem="{Binding TempTest.DeviceStatus, Converter={StaticResource StatusEnumConverter}}"/>
+
+                    <Label Grid.Row="2" Grid.Column="0" Content="PJob ID:"  VerticalAlignment="Center"/>
+                    <TextBox Grid.Row="2" Grid.Column="2" Text="{Binding TempTest.PJobID}" VerticalContentAlignment="Center"/>
+
+                    <Label Grid.Row="4" Grid.Column="0" Content="CJob ID:" VerticalAlignment="Center" />
+                    <TextBox Grid.Row="4" Grid.Column="2" Text="{Binding TempTest.CJobID}" VerticalContentAlignment="Center"/>
+
+                    <Label Grid.Row="6" Grid.Column="0" Content="Recipe Type:"  VerticalAlignment="Center"/>
+                    <ComboBox Grid.Row="6" Grid.Column="2" VerticalContentAlignment="Center"
+                        ItemsSource="{Binding Source={StaticResource RecipeTypeProvider} }"   SelectedItem="{Binding TempTest.RecipeType, Converter={StaticResource RecipeEnumConverter}}"/>
+
+                    <Button Grid.Row="8" Grid.ColumnSpan="3" Content="Update" Command="{Binding TriggerCommand}" Height="28"/>
+                </Grid>
+            </Border>
+
+            <Border Grid.Column="4" Background="#FFCECECE">
+
+                <Grid Margin="8">
+                    <Grid.RowDefinitions>
+                        <RowDefinition/>
+                        <RowDefinition Height="4"/>
+                        <RowDefinition Height="auto"/>
+                    </Grid.RowDefinitions>
+                    <Button Grid.Row="2" Height="28" Content="Start Data Service" Command="{Binding StartDataServiceCommand}"/>
+                </Grid>
+            </Border>
         </Grid>
     </Grid>
 </Window>

+ 3 - 1
EEMSUIClient/Views/MainWindow.xaml.cs

@@ -32,8 +32,10 @@ public partial class MainWindow : Window
 
     private void MainWindow_Closing(object? sender, System.ComponentModel.CancelEventArgs e)
     {
-        MessageBoxResult result = MessageBox.Show("Quit?", "", MessageBoxButton.YesNo);
+        MessageBoxResult result = MessageBox.Show("确认退出 EEMS Client?", "", MessageBoxButton.YesNo);
         if (result == MessageBoxResult.No)
             e.Cancel = true;
+
+        App.Current.Shutdown();
     }
 }

+ 9 - 2
Module/DeviceManagement/ViewModels/DeviceManagerViewModel.cs

@@ -45,7 +45,14 @@ public partial class DeviceManagerViewModel : ObservableObject
     [RelayCommand]
     private void CheckDetail(DeviceInfo_VM deviceInfo)
     {
-        string path = Path.Combine(System.Environment.CurrentDirectory, "Analizer");
+        DirectoryInfo directory = new(AppDomain.CurrentDomain.BaseDirectory);
+        if (directory.Parent is not DirectoryInfo mainFolder)
+        {
+            MessageBox.Show("Folder not exist");
+            return;
+        }
+
+        string path = Path.Combine(mainFolder.FullName, "Analizer");
         try
         {
             object o = deviceInfo.DeviceModel switch
@@ -57,7 +64,7 @@ public partial class DeviceManagerViewModel : ObservableObject
         }
         catch
         {
-
+            MessageBox.Show("Folder not exist");
         }
 
     }

+ 19 - 1
Server/EEMSClientCore/ClientCaller.cs

@@ -1,4 +1,7 @@
-namespace EEMSClientCore;
+
+using Device;
+
+namespace EEMSClientCore;
 
 public partial class ClientCaller : IClientCaller
 {
@@ -20,4 +23,19 @@ public partial class ClientCaller : IClientCaller
         return this.Send("UpdateRealTimeData", realtimeData);
     }
 
+    Task<bool> IClientCaller.UpdateAlarm(IEnumerable<Alarm> alarms)
+    {
+        return this.Send("UpdateAlarm", alarms);
+
+    }
+
+    Task<bool> IClientCaller.UpdateRecipeInfo(Recipe recipeInfo)
+    {
+        return this.Send("UpdateRecipeInfo", recipeInfo);
+    }
+
+    void IDisposable.Dispose()
+    {
+        throw new NotImplementedException();
+    }
 }

+ 1 - 1
Server/EEMSService/EEMSBaseServer.cs

@@ -101,6 +101,6 @@ public interface IEEMSBaseServerProvider
 public enum ServiceHub
 {
     Undefined,
-    ClinetHub,
+    ClientHub,
     UIHub
 }

+ 0 - 4
Server/EEMSService/HostLifeTime.cs

@@ -23,16 +23,12 @@ internal class HostLifeTime(IEEMSBaseServerProvider provider, SqlSugarCustom orm
             Application.Current.Shutdown();
         }
 
-
-
-
         provider.Started();
         return Task.CompletedTask;
     }
 
     public Task StopAsync(CancellationToken cancellationToken)
     {
-
         return Task.CompletedTask;
     }
 }

+ 17 - 2
Server/EEMSService/Hubs/ClientsMainHub.cs

@@ -7,7 +7,7 @@ internal partial class ClientsMainHub(DeviceManager deviceManager, IEEMSBaseServ
     public override Task OnConnectedAsync()
     {
         if (Context.Features.Get<IHttpConnectionFeature>() is IHttpConnectionFeature feature)
-            provider?.OnConnected(feature.RemoteIpAddress!.ToString(), (ushort)feature.RemotePort, ServiceHub.ClinetHub);
+            provider?.OnConnected(feature.RemoteIpAddress!.ToString(), (ushort)feature.RemotePort, ServiceHub.ClientHub);
 
         if (deviceManager.TryGetDevice(Context.ConnectionId, out DeviceInfo? device) && device is not null && device.Guid is not null)
         {
@@ -31,7 +31,7 @@ internal partial class ClientsMainHub(DeviceManager deviceManager, IEEMSBaseServ
         }
 
         if (Context.Features.Get<IHttpConnectionFeature>() is IHttpConnectionFeature feature)
-            provider?.OnDisConnected(feature.RemoteIpAddress!.ToString(), (ushort)feature.RemotePort, ServiceHub.ClinetHub);
+            provider?.OnDisConnected(feature.RemoteIpAddress!.ToString(), (ushort)feature.RemotePort, ServiceHub.ClientHub);
 
         return base.OnDisconnectedAsync(exception);
     }
@@ -59,4 +59,19 @@ internal partial class ClientsMainHub(DeviceManager deviceManager, IEEMSBaseServ
 
         return uiCaller.UpdateRealtimeData(device.Guid.Value, realtimeData);
     }
+
+    public Task<bool> UpdateAlarm(IEnumerable<Alarm> alarms)
+    {
+        if (!deviceManager.TryGetDevice(Context.ConnectionId, out DeviceInfo? device) || device is null || device.Guid is null)
+            return Task.FromResult(false);
+
+        return uiCaller.UpdateAlarm(alarms);
+    }
+
+    public Task<bool> UpdateRecipeInfo(Recipe recipeInfo)
+    {
+        if (!deviceManager.TryGetDevice(Context.ConnectionId, out DeviceInfo? device) || device is null || device.Guid is null)
+            return Task.FromResult(false);
+        return uiCaller.UpdateRecipeInfo(recipeInfo);
+    }
 }

+ 1 - 1
Server/EEMSService/Hubs/UIHub.cs

@@ -1,6 +1,6 @@
 namespace EEMSServerCore.Hubs;
 
-internal class UIHub(DeviceManager deviceManager) : Hub, IUICaller
+internal class UIHub(DeviceManager deviceManager, ClientCaller clientCaller) : Hub, IUICaller
 {
     public override Task OnConnectedAsync()
     {

+ 3 - 1
Server/ServiceBase/ClientInterfaces.cs

@@ -3,11 +3,13 @@
 /// <summary>
 /// EEMS Client -> EEMS Server
 /// </summary>
-public interface IClientCaller:IDisposable
+public interface IClientCaller : IDisposable
 {
     Task<Guid> RegisterDevice(DeviceInfo deviceInfo);
     Task<bool> FilePack(Guid guid, byte[] buffer, int current, int total);
     Task<bool> UpdateRealTimeData(Dictionary<string, object> realtimeData);
+    Task<bool> UpdateAlarm(IEnumerable<Alarm> alarms);
+    Task<bool> UpdateRecipeInfo(Recipe recipeInfo);
 }
 
 /// <summary>