anycmd 4 tahun lalu
induk
melakukan
6deac49141
29 mengubah file dengan 368 tambahan dan 111 penghapusan
  1. 2 1
      src/AppModels/AppRoot.partials.KernelOutputKeywordViewModels.cs
  2. 3 3
      src/AppModels/AppRoot.partials.KernelOutputTranslaterViewModels.cs
  3. 3 3
      src/AppModels/Vms/CoinViewModel.cs
  4. 18 15
      src/AppModels/Vms/KernelOutputKeywordViewModel.cs
  5. 1 10
      src/AppModels/Vms/KernelOutputKeywordsViewModel.cs
  6. 12 12
      src/AppModels/Vms/KernelOutputTranslaterViewModel.cs
  7. 41 12
      src/AppModels/Vms/KernelOutputViewModel.cs
  8. 1 1
      src/AppModels/Vms/ServerMessagesViewModel.cs
  9. 3 0
      src/AppViews0/Views/Ucs/FragmentWriterEdit.xaml.cs
  10. 3 0
      src/AppViews0/Views/Ucs/GroupEdit.xaml.cs
  11. 3 0
      src/AppViews0/Views/Ucs/KernelInputEdit.xaml.cs
  12. 3 0
      src/AppViews0/Views/Ucs/KernelOutputKeywordEdit.xaml.cs
  13. 37 17
      src/AppViews0/Views/Ucs/KernelOutputKeywords.xaml
  14. 2 2
      src/AppViews0/Views/Ucs/KernelOutputKeywords.xaml.cs
  15. 130 2
      src/AppViews0/Views/Ucs/KernelOutputPage.xaml
  16. 14 0
      src/AppViews0/Views/Ucs/KernelOutputPage.xaml.cs
  17. 3 0
      src/AppViews0/Views/Ucs/PoolKernelEdit.xaml.cs
  18. 3 0
      src/AppViews0/Views/Ucs/ServerMessageEdit.xaml.cs
  19. 4 0
      src/NTMiner.Controllers/IKernelOutputKeywordController.cs
  20. 7 1
      src/NTMinerClient/Mine/MineContext.cs
  21. 5 6
      src/NTMinerRpcClient/Core/Impl/KernelOutputKeywordSet.cs
  22. 10 4
      src/NTMinerServer/ServerRoot.cs
  23. 9 0
      src/NTMinerlib/Messages.cs
  24. 1 1
      src/NTMinerlib/User/LoginedUserExtensions.cs
  25. 15 14
      src/NTMinerlib/VirtualRoot.partials.LocalMessage.cs
  26. 2 2
      src/WebApiServer/AppRoot.cs
  27. 17 3
      src/WebApiServer/Controllers/KernelOutputKeywordController.cs
  28. 14 0
      src/WebApiServer/Core/Impl/KernelOutputKeywordSet.cs
  29. 2 2
      src/WsServer/AppRoot.cs

+ 2 - 1
src/AppModels/AppRoot.partials.KernelOutputKeywordViewModels.cs

@@ -37,6 +37,7 @@ namespace NTMiner {
                                 _dicByKernelOutputId[item.KernelOutputId].Add(vm);
                             }
                         }
+                        // TODO:给事件处理程序引入执行顺序改变,引入几个典型的顺序后可以避免很多为了顺序而构建的事件类型
                         if (NTMinerContext.Instance.CurrentMineContext != null) {
                             if (KernelOutputVms.TryGetKernelOutputVm(NTMinerContext.Instance.CurrentMineContext.KernelOutput.GetId(), out KernelOutputViewModel kernelOutputVm)) {
                                 kernelOutputVm.OnPropertyChanged(nameof(kernelOutputVm.KernelOutputKeywords));
@@ -89,7 +90,7 @@ namespace NTMiner {
                 }
             }
 
-            public IEnumerable<KernelOutputKeywordViewModel> GetListByKernelId(Guid kernelOutputId) {
+            public IEnumerable<KernelOutputKeywordViewModel> GetListByKernelOutputId(Guid kernelOutputId) {
                 if (_dicByKernelOutputId.ContainsKey(kernelOutputId)) {
                     return _dicByKernelOutputId[kernelOutputId];
                 }

+ 3 - 3
src/AppModels/AppRoot.partials.KernelOutputTranslaterViewModels.cs

@@ -80,9 +80,9 @@ namespace NTMiner {
                 }
             }
 
-            public IEnumerable<KernelOutputTranslaterViewModel> GetListByKernelId(Guid kernelId) {
-                if (_dicByKernelOutputId.ContainsKey(kernelId)) {
-                    return _dicByKernelOutputId[kernelId];
+            public IEnumerable<KernelOutputTranslaterViewModel> GetListByKernelOutputId(Guid kernelOutputId) {
+                if (_dicByKernelOutputId.ContainsKey(kernelOutputId)) {
+                    return _dicByKernelOutputId[kernelOutputId];
                 }
                 return new List<KernelOutputTranslaterViewModel>();
             }

+ 3 - 3
src/AppModels/Vms/CoinViewModel.cs

@@ -638,15 +638,15 @@ namespace NTMiner.Vms {
                     this.KernelBrand = string.Empty;
                 }
                 else {
-                    this.KernelBrand = $"{GpuType.AMD.GetName()}:{a.Id.ToString()}";
+                    this.KernelBrand = $"{nameof(GpuType.AMD)}:{a.Id.ToString()}";
                 }
             }
             else {
                 if (a == null) {
-                    this.KernelBrand = $"{GpuType.NVIDIA.GetName()}:{n.Id.ToString()}";
+                    this.KernelBrand = $"{nameof(GpuType.NVIDIA)}:{n.Id.ToString()}";
                 }
                 else {
-                    this.KernelBrand = $"{GpuType.NVIDIA.GetName()}:{n.Id.ToString()};{GpuType.AMD.GetName()}:{a.Id.ToString()}";
+                    this.KernelBrand = $"{nameof(GpuType.NVIDIA)}:{n.Id.ToString()};{nameof(GpuType.AMD)}:{a.Id.ToString()}";
                 }
             }
         }

+ 18 - 15
src/AppModels/Vms/KernelOutputKeywordViewModel.cs

@@ -14,7 +14,6 @@ namespace NTMiner.Vms {
 
         public ICommand Remove { get; private set; }
         public ICommand Edit { get; private set; }
-        public ICommand ToServer { get; private set; }
         public ICommand Save { get; private set; }
 
         [Obsolete(message: NTKeyword.WpfDesignOnly, error: true)]
@@ -25,32 +24,32 @@ namespace NTMiner.Vms {
         }
 
         private LocalMessageType _messageTypeEnum;
-        public KernelOutputKeywordViewModel(IKernelOutputKeyword data) : this(data.GetId()) {
-            this._dataLevel = data.GetDataLevel();
-            _kernelOutputId = data.KernelOutputId;
-            _messageType = data.MessageType;
-            data.MessageType.TryParse(out _messageTypeEnum);
+        public KernelOutputKeywordViewModel(IKernelOutputKeyword data) : this(data.GetId(), data.KernelOutputId, data.MessageType, data.GetDataLevel()) {
             _keyword = data.Keyword;
             _description = data.Description;
         }
 
-        public KernelOutputKeywordViewModel(Guid id) {
+        public KernelOutputKeywordViewModel(Guid id, Guid kernelOutputId, string messageType, DataLevel dataLevel) {
             _id = id;
+            _kernelOutputId = kernelOutputId;
+            _messageType = messageType;
+            _dataLevel = dataLevel;
+            messageType.TryParse(out _messageTypeEnum);
             this.Save = new DelegateCommand(() => {
                 if (string.IsNullOrEmpty(this.Keyword)) {
                     VirtualRoot.Out.ShowError("关键字不能为空", autoHideSeconds: 4);
                     return;
                 }
-                if (AppRoot.KernelOutputKeywordVms.GetListByKernelId(this.KernelOutputId).Any(a => a.Id != this.Id && a.Keyword == this.Keyword)) {
+                if (AppRoot.KernelOutputKeywordVms.GetListByKernelOutputId(this.KernelOutputId).Any(a => a.Id != this.Id && a.Keyword == this.Keyword)) {
                     throw new ValidationException($"关键字 {this.Keyword} 已经存在");
                 }
-                if (DevMode.IsDevMode) {
+                if (ClientAppType.IsMinerStudio) {
                     MinerStudio.MinerStudioRoot.Login(() => {
                         VirtualRoot.Execute(new AddOrUpdateKernelOutputKeywordCommand(this));
                         VirtualRoot.Execute(new CloseWindowCommand(this.Id));
                     });
                 }
-                else {
+                else if (ClientAppType.IsMinerClient) {
                     VirtualRoot.Execute(new AddOrUpdateKernelOutputKeywordCommand(this));
                     VirtualRoot.Execute(new CloseWindowCommand(this.Id));
                 }
@@ -79,10 +78,6 @@ namespace NTMiner.Vms {
                     }
                 }));
             });
-            this.ToServer = new DelegateCommand(() => {
-                // 发送给服务器并删除本地
-                // TODO:这是在测试完了后在挖矿端将本条自定义的内核输出关键字发送到服务端变成供所有人使用的公共的内核输出关键字的地方
-            });
         }
 
         public LocalMessageType MessageTypeEnum {
@@ -123,7 +118,15 @@ namespace NTMiner.Vms {
 
         public bool IsReadOnly {
             get {
-                return !DevMode.IsDevMode && _dataLevel == DataLevel.Global;
+                if (ClientAppType.IsMinerClient) {
+                    return _dataLevel == DataLevel.Global;
+                }
+                else if (ClientAppType.IsMinerStudio) {
+                    return !MainMenuViewModel.Instance.IsMinerStudioOuterAdmin;
+                }
+                else {
+                    return true;
+                }
             }
         }
 

+ 1 - 10
src/AppModels/Vms/KernelOutputKeywordsViewModel.cs

@@ -1,5 +1,4 @@
 using NTMiner.Core;
-using NTMiner.Core.MinerClient;
 using System;
 using System.Windows.Input;
 
@@ -21,16 +20,8 @@ namespace NTMiner.Vms {
                 if (kernelOutputVm == null) {
                     return;
                 }
-                var data = new KernelOutputKeywordData {
-                    Id = Guid.NewGuid(),
-                    MessageType = LocalMessageType.Info.GetName(),
-                    Keyword = string.Empty,
-                    Description = string.Empty,
-                    KernelOutputId = kernelOutputVm.Id
-                };
                 // 新建的内核输出关键字时的工作流:默认为Profile级,测试没问题后,然后在界面上提供个按钮转化为Global级提交到服务器
-                data.SetDataLevel(DataLevel.Profile);
-                new KernelOutputKeywordViewModel(data).Edit.Execute(FormType.Add);
+                new KernelOutputKeywordViewModel(Guid.NewGuid(), kernelOutputVm.Id, nameof(LocalMessageType.Info), DataLevel.Profile).Edit.Execute(FormType.Add);
             });
         }
 

+ 12 - 12
src/AppModels/Vms/KernelOutputTranslaterViewModel.cs

@@ -27,27 +27,27 @@ namespace NTMiner.Vms {
             }
         }
 
-        public KernelOutputTranslaterViewModel(IKernelOutputTranslater data) : this(data.GetId()) {
-            _kernelOutputId = data.KernelOutputId;
+        public KernelOutputTranslaterViewModel(IKernelOutputTranslater data) : this(data.GetId(), data.KernelOutputId, data.SortNumber) {
             _regexPattern = data.RegexPattern;
             _id = data.GetId();
             _replacement = data.Replacement;
-            _sortNumber = data.SortNumber;
             _isPre = data.IsPre;
         }
 
-        public KernelOutputTranslaterViewModel(Guid id) {
+        public KernelOutputTranslaterViewModel(Guid id, Guid kernelOutputId, int sortNumber) {
             _id = id;
+            _kernelOutputId = kernelOutputId;
+            _sortNumber = sortNumber;
             _isPre = true;// 在UI上将IsPre属性视为只读的选中状态的复选框
             this.Save = new DelegateCommand(() => {
-                int sortNumber = this.SortNumber;
+                int oldSortNumber = this.SortNumber;
                 if (NTMinerContext.Instance.ServerContext.KernelOutputTranslaterSet.Contains(this.Id)) {
                     VirtualRoot.Execute(new UpdateKernelOutputTranslaterCommand(this));
                 }
                 else {
                     VirtualRoot.Execute(new AddKernelOutputTranslaterCommand(this));
                 }
-                if (sortNumber != this.SortNumber) {
+                if (oldSortNumber != this.SortNumber) {
                     if (AppRoot.KernelOutputVms.TryGetKernelOutputVm(this.KernelOutputId, out KernelOutputViewModel kernelOutputVm)) {
                         kernelOutputVm.OnPropertyChanged(nameof(kernelOutputVm.KernelOutputTranslaters));
                     }
@@ -66,12 +66,12 @@ namespace NTMiner.Vms {
                 }));
             });
             this.SortUp = new DelegateCommand(() => {
-                KernelOutputTranslaterViewModel upOne = AppRoot.KernelOutputTranslaterVms.GetListByKernelId(this.KernelOutputId).GetUpOne(this.SortNumber);
+                KernelOutputTranslaterViewModel upOne = AppRoot.KernelOutputTranslaterVms.GetListByKernelOutputId(this.KernelOutputId).GetUpOne(this.SortNumber);
                 if (upOne != null) {
-                    int sortNumber = upOne.SortNumber;
+                    int oldSortNumber = upOne.SortNumber;
                     upOne.SortNumber = this.SortNumber;
                     VirtualRoot.Execute(new UpdateKernelOutputTranslaterCommand(upOne));
-                    this.SortNumber = sortNumber;
+                    this.SortNumber = oldSortNumber;
                     VirtualRoot.Execute(new UpdateKernelOutputTranslaterCommand(this));
                     AppRoot.KernelOutputTranslaterVms.OnPropertyChanged(nameof(AppRoot.KernelOutputTranslaterViewModels.AllKernelOutputTranslaterVms));
                     if (AppRoot.KernelOutputVms.TryGetKernelOutputVm(this.KernelOutputId, out KernelOutputViewModel kernelOutputVm)) {
@@ -80,12 +80,12 @@ namespace NTMiner.Vms {
                 }
             });
             this.SortDown = new DelegateCommand(() => {
-                KernelOutputTranslaterViewModel nextOne = AppRoot.KernelOutputTranslaterVms.GetListByKernelId(this.KernelOutputId).GetNextOne(this.SortNumber);
+                KernelOutputTranslaterViewModel nextOne = AppRoot.KernelOutputTranslaterVms.GetListByKernelOutputId(this.KernelOutputId).GetNextOne(this.SortNumber);
                 if (nextOne != null) {
-                    int sortNumber = nextOne.SortNumber;
+                    int oldSortNumber = nextOne.SortNumber;
                     nextOne.SortNumber = this.SortNumber;
                     VirtualRoot.Execute(new UpdateKernelOutputTranslaterCommand(nextOne));
-                    this.SortNumber = sortNumber;
+                    this.SortNumber = oldSortNumber;
                     VirtualRoot.Execute(new UpdateKernelOutputTranslaterCommand(this));
                     AppRoot.KernelOutputTranslaterVms.OnPropertyChanged(nameof(AppRoot.KernelOutputTranslaterViewModels.AllKernelOutputTranslaterVms));
                     if (AppRoot.KernelOutputVms.TryGetKernelOutputVm(this.KernelOutputId, out KernelOutputViewModel kernelOutputVm)) {

+ 41 - 12
src/AppModels/Vms/KernelOutputViewModel.cs

@@ -38,6 +38,7 @@ namespace NTMiner.Vms {
         private string _dualRejectPercentPattern;
 
         private string _translaterKeyword;
+        private string _outputKeyword;
         private string _poolDelayPattern;
         private string _dualPoolDelayPattern;
         private string _speedUnit;
@@ -50,9 +51,11 @@ namespace NTMiner.Vms {
         public ICommand Save { get; private set; }
 
         public ICommand AddKernelOutputTranslater { get; private set; }
-
         public ICommand ClearTranslaterKeyword { get; private set; }
 
+        public ICommand AddKernelOutputKeyword { get; private set; }
+        public ICommand ClearOutputKeyword { get; private set; }
+
         [Obsolete(message: NTKeyword.WpfDesignOnly, error: true)]
         public KernelOutputViewModel() {
             if (!WpfUtil.IsInDesignMode) {
@@ -123,20 +126,24 @@ namespace NTMiner.Vms {
             });
             this.AddKernelOutputTranslater = new DelegateCommand(() => {
                 int sortNumber = this.KernelOutputTranslaters.Count == 0 ? 1 : this.KernelOutputTranslaters.Count + 1;
-                new KernelOutputTranslaterViewModel(Guid.NewGuid()) {
-                    KernelOutputId = this.Id,
-                    SortNumber = sortNumber
-                }.Edit.Execute(FormType.Add);
+                new KernelOutputTranslaterViewModel(Guid.NewGuid(), this.Id, sortNumber).Edit.Execute(FormType.Add);
             });
             this.ClearTranslaterKeyword = new DelegateCommand(() => {
                 this.TranslaterKeyword = string.Empty;
             });
-        }
-
-        public List<KernelOutputKeywordViewModel> KernelOutputKeywords {
-            get {
-                return new List<KernelOutputKeywordViewModel>(AppRoot.KernelOutputKeywordVms.GetListByKernelId(this.Id).OrderBy(a => a.Keyword));
-            }
+            this.AddKernelOutputKeyword = new DelegateCommand(() => {
+                DataLevel dataLevel = DataLevel.UnDefined;
+                if (ClientAppType.IsMinerClient) {
+                    dataLevel = DataLevel.Profile;
+                }
+                else if (ClientAppType.IsMinerStudio) {
+                    dataLevel = DataLevel.Global;
+                }
+                new KernelOutputKeywordViewModel(Guid.NewGuid(), this.Id, nameof(LocalMessageType.Error), dataLevel).Edit.Execute(FormType.Add);
+            });
+            this.ClearOutputKeyword = new DelegateCommand(() => {
+                this.OutputKeyword = string.Empty;
+            });
         }
 
         public string TranslaterKeyword {
@@ -150,9 +157,20 @@ namespace NTMiner.Vms {
             }
         }
 
+        public string OutputKeyword {
+            get { return _outputKeyword; }
+            set {
+                if (_outputKeyword != value) {
+                    _outputKeyword = value;
+                    OnPropertyChanged(nameof(OutputKeyword));
+                    OnPropertyChanged(nameof(KernelOutputKeywords));
+                }
+            }
+        }
+
         public List<KernelOutputTranslaterViewModel> KernelOutputTranslaters {
             get {
-                var query = AppRoot.KernelOutputTranslaterVms.GetListByKernelId(this.Id).AsQueryable();
+                var query = AppRoot.KernelOutputTranslaterVms.GetListByKernelOutputId(this.Id).AsQueryable();
                 if (!string.IsNullOrEmpty(TranslaterKeyword)) {
                     query = query.Where(a => (a.RegexPattern != null && a.RegexPattern.IgnoreCaseContains(TranslaterKeyword))
                         || (a.Replacement != null && a.Replacement.IgnoreCaseContains(TranslaterKeyword)));
@@ -161,6 +179,17 @@ namespace NTMiner.Vms {
             }
         }
 
+        public List<KernelOutputKeywordViewModel> KernelOutputKeywords {
+            get {
+                var query = AppRoot.KernelOutputKeywordVms.GetListByKernelOutputId(this.Id).AsQueryable();
+                if (!string.IsNullOrEmpty(OutputKeyword)) {
+                    query = query.Where(a => (a.Keyword != null && a.Keyword.IgnoreCaseContains(OutputKeyword))
+                        || (a.Description != null && a.Description.IgnoreCaseContains(OutputKeyword)));
+                }
+                return query.OrderBy(a => a.Keyword).ToList();
+            }
+        }
+
         public string GroupNames {
             get {
                 return string.Join("、", new string[] {

+ 1 - 1
src/AppModels/Vms/ServerMessagesViewModel.cs

@@ -29,7 +29,7 @@ namespace NTMiner.Vms {
             this.Add = new DelegateCommand(() => {
                 new ServerMessageViewModel(new ServerMessageData {
                     Id = Guid.NewGuid(),
-                    MessageType = ServerMessageType.Info.GetName(),
+                    MessageType = nameof(ServerMessageType.Info),
                     Provider = "admin",
                     Content = string.Empty,
                     Timestamp = DateTime.MinValue

+ 3 - 0
src/AppViews0/Views/Ucs/FragmentWriterEdit.xaml.cs

@@ -19,7 +19,10 @@ namespace NTMiner.Views.Ucs {
             }, fixedSize: true);
         }
 
+        public FragmentWriterViewModel Vm { get; private set; }
+
         public FragmentWriterEdit(FragmentWriterViewModel vm) {
+            this.Vm = vm;
             this.DataContext = vm;
             InitializeComponent();
         }

+ 3 - 0
src/AppViews0/Views/Ucs/GroupEdit.xaml.cs

@@ -19,7 +19,10 @@ namespace NTMiner.Views.Ucs {
             }, fixedSize: true);
         }
 
+        public GroupViewModel Vm { get; private set; }
+
         public GroupEdit(GroupViewModel vm) {
+            this.Vm = vm;
             this.DataContext = vm;
             InitializeComponent();
         }

+ 3 - 0
src/AppViews0/Views/Ucs/KernelInputEdit.xaml.cs

@@ -18,7 +18,10 @@ namespace NTMiner.Views.Ucs {
             }, fixedSize: true);
         }
 
+        public KernelInputViewModel Vm { get; private set; }
+
         public KernelInputEdit(KernelInputViewModel vm) {
+            this.Vm = vm;
             this.DataContext = vm;
             InitializeComponent();
         }

+ 3 - 0
src/AppViews0/Views/Ucs/KernelOutputKeywordEdit.xaml.cs

@@ -18,7 +18,10 @@ namespace NTMiner.Views.Ucs {
             }, fixedSize: true);
         }
 
+        public KernelOutputKeywordViewModel Vm { get; private set; }
+
         public KernelOutputKeywordEdit(KernelOutputKeywordViewModel vm) {
+            this.Vm = vm;
             this.DataContext = vm;
             InitializeComponent();
         }

+ 37 - 17
src/AppViews0/Views/Ucs/KernelOutputKeywords.xaml

@@ -11,12 +11,47 @@
 	xmlns:vm="clr-namespace:NTMiner.Vms;assembly=AppModels"
 	xmlns:app="clr-namespace:NTMiner;assembly=AppModels"
     d:DataContext="{d:DesignData Source=../Design/KernelOutputKeywordsViewModel.xaml}"
-	mc:Ignorable="d" d:DesignHeight="320" d:DesignWidth="520">
+	mc:Ignorable="d" d:DesignHeight="400" d:DesignWidth="800">
 	<Grid>
         <Grid.RowDefinitions>
-            <RowDefinition Height="Auto"></RowDefinition>
+            <RowDefinition Height="28"></RowDefinition>
             <RowDefinition Height="*"></RowDefinition>
         </Grid.RowDefinitions>
+        <Border Background="{StaticResource ToolbarBackground}">
+            <WrapPanel VerticalAlignment="Center">
+                <Path
+					Width="12"
+					Height="12"
+					Fill="{StaticResource BtnBackground}"
+					Stretch="Fill"
+					Data="{StaticResource Icon_Search}"></Path>
+                <TextBlock VerticalAlignment="Center" Text="搜索" Margin="0 0 2 0"></TextBlock>
+                <controls:KbTextBox 
+					x:Name="TbOutputKeyword" 
+					Width="134" 
+					Height="24"
+					Text="{Binding KernelOutputVm.OutputKeyword, UpdateSourceTrigger=PropertyChanged}" 
+					VerticalContentAlignment="Center"
+					Hint="请输入关键字"
+					BorderBrush="{StaticResource LightLineColor}"
+					BorderThickness="1"
+					Background="White"
+					SelectedColor="#409EFF" />
+                <controls:KbButton 
+					Background="Transparent" BorderThickness="0"
+					Height="20"
+					Command="{Binding KernelOutputVm.ClearOutputKeyword}"
+					Visibility="{Binding ElementName=TbOutputKeyword,Path=Text, Converter={StaticResource NotNullOrEmptyVisibilityConverter}}"
+					Margin="-14 0 0 0" HorizontalAlignment="Left">
+                    <Path
+						Width="8"
+						Height="8"
+						Data="{StaticResource Icon_Close}"
+						Fill="{StaticResource BtnBackground}"
+						Stretch="Fill" />
+                </controls:KbButton>
+            </WrapPanel>
+        </Border>
         <DataGrid 
             Grid.Row="1" 
             ItemsSource="{Binding KernelOutputVm.KernelOutputKeywords}"
@@ -40,21 +75,6 @@
 											Stretch="Fill" />
                                     </WrapPanel>
                                 </controls:KbButton>
-                                <controls:KbButton 
-                                    Visibility="{x:Static app:AppStatic.IsDevModeVisible}"
-                                    IsEnabled="{Binding IsReadOnly,Converter={StaticResource BoolInvertConverter}}"
-									Margin="2 0"
-									Command="{Binding ToServer}" Background="Transparent" BorderThickness="0"
-									CornerRadius="2" ToolTip="Profile->Global:发送给服务器并删除本地">
-                                    <WrapPanel>
-                                        <Path
-											Width="18"
-											Height="18"
-											Data="{StaticResource Icon_Update}"
-											Fill="{StaticResource BtnBackground}"
-											Stretch="Fill" />
-                                    </WrapPanel>
-                                </controls:KbButton>
                                 <controls:KbButton 
                                     IsEnabled="{Binding IsReadOnly,Converter={StaticResource BoolInvertConverter}}"
 									Margin="2 0"

+ 2 - 2
src/AppViews0/Views/Ucs/KernelOutputKeywords.xaml.cs

@@ -7,14 +7,14 @@ namespace NTMiner.Views.Ucs {
             ContainerWindow.ShowWindow(new ContainerWindowViewModel {
                 Title = "内核频道设置:基于关键字从内核输出中订阅消息",
                 IconName = "Icon_Message",
-                Width = 600,
+                Width = 800,
                 Height = 400,
                 CloseVisible = System.Windows.Visibility.Visible,
                 FooterVisible = System.Windows.Visibility.Collapsed
             }, ucFactory: (window) => {
                 window.Owner = WpfUtil.GetTopWindow();
                 return new KernelOutputKeywords();
-            }, fixedSize: true);
+            }, fixedSize: false);
         }
 
         public KernelOutputKeywordsViewModel Vm { get; private set; }

+ 130 - 2
src/AppViews0/Views/Ucs/KernelOutputPage.xaml

@@ -107,9 +107,10 @@
 				Margin="0 2 0 0"
 				Padding="0"
 				BorderThickness="1"
+                SelectionChanged="TabControl_SelectionChanged"
 				BorderBrush="{StaticResource LightLineColor}"
 				Background="Transparent">
-				<!--start翻译-->
+                <!--翻译start-->
 				<TabItem>
 					<TabItem.Header>
 						<TextBlock Padding="10 0" Text="翻译"></TextBlock>
@@ -283,7 +284,134 @@
                         </controls:KbButton>
                     </Grid>
 				</TabItem>
-				<!--end翻译-->
+                <!--翻译end-->
+                <!--订阅start-->
+                <TabItem x:Name="TabItemOutputKeywords">
+                    <TabItem.Header>
+                        <TextBlock Padding="10 0" Text="订阅"></TextBlock>
+                    </TabItem.Header>
+                    <Grid>
+                        <Grid.RowDefinitions>
+                            <RowDefinition Height="28"></RowDefinition>
+                            <RowDefinition Height="*"></RowDefinition>
+                        </Grid.RowDefinitions>
+                        <Border Background="{StaticResource ToolbarBackground}">
+                            <WrapPanel VerticalAlignment="Center">
+                                <Path
+									Width="12"
+									Height="12"
+									Fill="{StaticResource BtnBackground}"
+									Stretch="Fill"
+									Data="{StaticResource Icon_Search}"></Path>
+                                <TextBlock VerticalAlignment="Center" Text="搜索" Margin="0 0 2 0"></TextBlock>
+                                <controls:KbTextBox 
+									x:Name="TbOutputKeyword" 
+									Width="134" 
+									Height="24"
+									Text="{Binding CurrentKernelOutputVm.OutputKeyword, UpdateSourceTrigger=PropertyChanged}" 
+									VerticalContentAlignment="Center"
+									Hint="请输入关键字"
+									BorderBrush="{StaticResource LightLineColor}"
+									BorderThickness="1"
+									Background="White"
+									SelectedColor="#409EFF" />
+                                <controls:KbButton 
+									Background="Transparent" BorderThickness="0"
+									Height="20"
+									Command="{Binding CurrentKernelOutputVm.ClearOutputKeyword}"
+									Visibility="{Binding ElementName=TbOutputKeyword,Path=Text, Converter={StaticResource NotNullOrEmptyVisibilityConverter}}"
+									Margin="-14 0 0 0" HorizontalAlignment="Left">
+                                    <Path
+										Width="8"
+										Height="8"
+										Data="{StaticResource Icon_Close}"
+										Fill="{StaticResource BtnBackground}"
+										Stretch="Fill" />
+                                </controls:KbButton>
+                            </WrapPanel>
+                        </Border>
+                        <DataGrid
+							Grid.Row="1"
+							ItemsSource="{Binding CurrentKernelOutputVm.KernelOutputKeywords}" 
+							BorderThickness="1 1 0 0"
+							MouseDoubleClick="KernelOutputKeywordDataGrid_MouseDoubleClick">
+                            <DataGrid.Resources>
+                                <controls:BindingProxy x:Key="proxy" Data="{Binding}" />
+                            </DataGrid.Resources>
+                            <DataGrid.Columns>
+                                <DataGridTemplateColumn MinWidth="100" IsReadOnly="True">
+                                    <DataGridTemplateColumn.CellTemplate>
+                                        <DataTemplate>
+                                            <StackPanel HorizontalAlignment="Right" Background="Transparent" VerticalAlignment="Center" Orientation="Horizontal">
+                                                <controls:KbButton 
+													IsEnabled="{Binding IsReadOnly,Converter={StaticResource BoolInvertConverter}}"
+													Command="{Binding Edit}" Background="Transparent" BorderThickness="0"
+													Margin="2 0"
+													CornerRadius="2" ToolTip="编辑">
+                                                    <WrapPanel>
+                                                        <Path
+															Width="18"
+															Height="18"
+															Data="{StaticResource Icon_Edit}"
+															Fill="{StaticResource BtnBackground}"
+															Stretch="Fill" />
+                                                    </WrapPanel>
+                                                </controls:KbButton>
+                                                <controls:KbButton 
+													IsEnabled="{Binding IsReadOnly,Converter={StaticResource BoolInvertConverter}}"
+													Command="{Binding Remove}" Background="Transparent" BorderThickness="0"
+													Margin="2 0"
+													CornerRadius="2" ToolTip="删除">
+                                                    <WrapPanel>
+                                                        <Path
+															Width="18"
+															Height="18"
+															Data="{StaticResource Icon_Delete}"
+															Fill="{StaticResource BtnBackground}"
+															Stretch="Fill" />
+                                                    </WrapPanel>
+                                                </controls:KbButton>
+                                            </StackPanel>
+                                        </DataTemplate>
+                                    </DataGridTemplateColumn.CellTemplate>
+                                </DataGridTemplateColumn>
+                                <DataGridTemplateColumn MinWidth="20" Width="20" IsReadOnly="True">
+                                    <DataGridTemplateColumn.CellTemplate>
+                                        <DataTemplate>
+                                            <Path
+												Width="14"
+												Height="14"
+												Data="{Binding MessageTypeIcon}"
+												Fill="{Binding IconFill}"
+												Stretch="Fill" />
+                                        </DataTemplate>
+                                    </DataGridTemplateColumn.CellTemplate>
+                                </DataGridTemplateColumn>
+                                <DataGridTextColumn Header="关键字" Width="160" Binding="{Binding Keyword}"></DataGridTextColumn>
+                                <DataGridTextColumn Header="大意" Width="*" Binding="{Binding Description}"></DataGridTextColumn>
+                            </DataGrid.Columns>
+                        </DataGrid>
+                        <TextBlock Grid.Row="1" Foreground="Red" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="{Binding CurrentKernelOutputVm.KernelOutputKeywords, Converter={StaticResource NoRecordVisibilityConverter}}" Grid.Column="1" Text="沒有记录"></TextBlock>
+                        <controls:KbButton 
+                            Grid.Row="1"
+                            HorizontalAlignment="Left"
+                            VerticalAlignment="Top"
+							Command="{Binding CurrentKernelOutputVm.AddKernelOutputKeyword}" 
+							Background="Transparent" BorderThickness="0"
+							Margin="10 4"
+							CornerRadius="2" ToolTip="添加">
+                            <WrapPanel>
+                                <Path
+									Width="18"
+									Height="18"
+									Data="{StaticResource Icon_Add}"
+									Fill="{StaticResource BtnBackground}"
+									Stretch="Fill" />
+                            </WrapPanel>
+                        </controls:KbButton>
+                    </Grid>
+                </TabItem>
+                <!--订阅end-->
 			</TabControl>
 		</Grid>
 	</Grid>

+ 14 - 0
src/AppViews0/Views/Ucs/KernelOutputPage.xaml.cs

@@ -37,5 +37,19 @@ namespace NTMiner.Views.Ucs {
         private void KernelOutputTranslaterDataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
             WpfUtil.DataGrid_EditRow<KernelOutputTranslaterViewModel>(sender, e);
         }
+
+        private void KernelOutputKeywordDataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
+            WpfUtil.DataGrid_EditRow<KernelOutputKeywordViewModel>(sender, e);
+        }
+
+        private bool _isFirstSelectedOutputKeywords = true;
+        private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e) {
+            if (((TabControl)sender).SelectedItem == TabItemOutputKeywords) {
+                if (_isFirstSelectedOutputKeywords) {
+                    VirtualRoot.Execute(new LoadKernelOutputKeywordCommand());
+                }
+                _isFirstSelectedOutputKeywords = false;
+            }
+        }
     }
 }

+ 3 - 0
src/AppViews0/Views/Ucs/PoolKernelEdit.xaml.cs

@@ -19,7 +19,10 @@ namespace NTMiner.Views.Ucs {
             }, fixedSize: true);
         }
 
+        public PoolKernelViewModel Vm { get; private set; }
+
         public PoolKernelEdit(PoolKernelViewModel vm) {
+            this.Vm = vm;
             this.DataContext = vm;
             InitializeComponent();
         }

+ 3 - 0
src/AppViews0/Views/Ucs/ServerMessageEdit.xaml.cs

@@ -19,7 +19,10 @@ namespace NTMiner.Views.Ucs {
             }, fixedSize: true);
         }
 
+        public ServerMessageViewModel Vm { get; private set; }
+
         public ServerMessageEdit(ServerMessageViewModel vm) {
+            this.Vm = vm;
             this.DataContext = vm;
             InitializeComponent();
         }

+ 4 - 0
src/NTMiner.Controllers/IKernelOutputKeywordController.cs

@@ -13,5 +13,9 @@ namespace NTMiner.Controllers {
         /// 需签名
         /// </summary>
         ResponseBase RemoveKernelOutputKeyword(DataRequest<Guid> request);
+        /// <summary>
+        /// 需签名
+        /// </summary>
+        ResponseBase ClearByExceptedOutputIds(DataRequest<Guid[]> request);
     }
 }

+ 7 - 1
src/NTMinerClient/Mine/MineContext.cs

@@ -506,7 +506,13 @@ namespace NTMiner.Mine {
                                                     if (!string.IsNullOrEmpty(keyword.Description)) {
                                                         content = $" 大意:{keyword.Description} 详情:" + content;
                                                     }
-                                                    VirtualRoot.LocalMessage(LocalMessageChannel.Kernel, this.GetType().Name, messageType, content, OutEnum.None, toConsole: false);
+                                                    VirtualRoot.LocalMessage(
+                                                        LocalMessageChannel.Kernel, 
+                                                        this.GetType().Name, 
+                                                        messageType, 
+                                                        consoleLine: keyword.Description, 
+                                                        content, 
+                                                        OutEnum.None);
                                                 }
                                             }
                                         }

+ 5 - 6
src/NTMinerRpcClient/Core/Impl/KernelOutputKeywordSet.cs

@@ -1,5 +1,4 @@
 using LiteDB;
-using NTMiner.Core;
 using System;
 using System.Collections.Generic;
 using System.IO;
@@ -56,7 +55,7 @@ namespace NTMiner.Core.Impl {
             }, location: this.GetType());
             VirtualRoot.BuildCmdPath<AddOrUpdateKernelOutputKeywordCommand>(path: (message) => {
                 InitOnece();
-                if (!DevMode.IsDevMode) {
+                if (ClientAppType.IsMinerClient) {
                     DataLevel dataLevel = DataLevel.Profile;
                     if (_dicById.TryGetValue(message.Input.GetId(), out KernelOutputKeywordData exist)) {
                         exist.Update(message.Input);
@@ -83,7 +82,7 @@ namespace NTMiner.Core.Impl {
                         VirtualRoot.RaiseEvent(new UserKernelOutputKeywordAddedEvent(message.MessageId, entity));
                     }
                 }
-                else {
+                else if (ClientAppType.IsMinerStudio) {
                     message.Input.SetDataLevel(DataLevel.Global);
                     RpcRoot.OfficialServer.KernelOutputKeywordService.AddOrUpdateKernelOutputKeywordAsync(KernelOutputKeywordData.Create(message.Input), (response, e) => {
                         if (response.IsSuccess()) {
@@ -97,7 +96,7 @@ namespace NTMiner.Core.Impl {
             }, location: this.GetType());
             VirtualRoot.BuildCmdPath<RemoveKernelOutputKeywordCommand>(path: (message) => {
                 InitOnece();
-                if (!DevMode.IsDevMode) {
+                if (ClientAppType.IsMinerClient) {
                     if (message == null || message.EntityId == Guid.Empty) {
                         return;
                     }
@@ -115,7 +114,7 @@ namespace NTMiner.Core.Impl {
                     }
                     VirtualRoot.RaiseEvent(new UserKernelOutputKeywordRemovedEvent(message.MessageId, entity));
                 }
-                else {
+                else if (ClientAppType.IsMinerStudio) {
                     RpcRoot.OfficialServer.KernelOutputKeywordService.RemoveKernelOutputKeyword(message.EntityId, (response, e) => {
                         if (response.IsSuccess()) {
                             VirtualRoot.Execute(new LoadKernelOutputKeywordCommand());
@@ -174,7 +173,7 @@ namespace NTMiner.Core.Impl {
                     list.Add(item);
                 }
             }
-            if (!DevMode.IsDevMode) {
+            if (ClientAppType.IsMinerClient) {
                 using (LiteDatabase db = new LiteDatabase(_connectionString)) {
                     var col = db.GetCollection<KernelOutputKeywordData>();
                     foreach (var item in col.FindAll()) {

+ 10 - 4
src/NTMinerServer/ServerRoot.cs

@@ -9,10 +9,6 @@ using System.IO;
 
 namespace NTMiner {
     public static class ServerRoot {
-        private static IClientTestId _clientTestId;
-        public static IClientTestId ClientTestId {
-            get { return _clientTestId; }
-        }
         internal static void SetClientTestId(IClientTestId clientTestId) {
             _clientTestId = clientTestId;
         }
@@ -27,6 +23,15 @@ namespace NTMiner {
             System.Net.ServicePointManager.DefaultConnectionLimit = 512;
         }
 
+        #region 曳光弹
+        private static IClientTestId _clientTestId;
+        public static IClientTestId ClientTestId {
+            get { return _clientTestId; }
+        }
+
+        // [yè guāng dàn]
+        // 曳光弹是一种装有能发光的化学药剂的炮弹或枪弹。发射后发出红色﹑黄色或者绿色的光。用来指示弹道和目标。
+        // 曳光弹跟其他子弹弹头不同的是弹头在飞行中会发亮,并在光源不足或黑暗环境显示出弹道,协助射手进行弹道修正。
         public static void IfMinerClientTestIdLogElseNothing(Guid clientId, string msg) {
             if (_clientTestId != null && _clientTestId.MinerClientTestId != Guid.Empty && _clientTestId.MinerClientTestId == clientId) {
                 Logger.Debug($"{nameof(NTMinerAppType.MinerClient)} {clientId.ToString()} {msg}");
@@ -38,6 +43,7 @@ namespace NTMiner {
                 Logger.Debug($"{nameof(NTMinerAppType.MinerStudio)} {studioId.ToString()} {msg}");
             }
         }
+        #endregion
 
         private static IHostConfig _hostConfig;
         private static readonly object _locker = new object();

+ 9 - 0
src/NTMinerlib/Messages.cs

@@ -148,6 +148,15 @@ namespace NTMiner {
         public IKernelOutputKeyword Input { get; private set; }
     }
 
+    [MessageType(description: "清空给定输出标识之外的记录")]
+    public class ClearKernelOutputKeywordsCommand : Cmd {
+        public ClearKernelOutputKeywordsCommand(Guid[] exceptedOutputIds) {
+            this.ExceptedOutputIds = exceptedOutputIds;
+        }
+
+        public Guid[] ExceptedOutputIds { get; private set; }
+    }
+
     [MessageType(description: "添加了用户自定义内核输出关键字后")]
     public class UserKernelOutputKeywordAddedEvent : SourcedEvent<IKernelOutputKeyword> {
         public UserKernelOutputKeywordAddedEvent(PathId targetPathId, IKernelOutputKeyword source) : base(targetPathId, source) {

+ 1 - 1
src/NTMinerlib/User/LoginedUserExtensions.cs

@@ -7,7 +7,7 @@ namespace NTMiner.User {
             if (user == null || string.IsNullOrEmpty(user.Roles)) {
                 return false;
             }
-            return user.Roles.Split(',').Contains(Role.RoleEnum.Admin.GetName(), StringComparer.OrdinalIgnoreCase);
+            return user.Roles.Split(',').Contains(nameof(Role.RoleEnum.Admin), StringComparer.OrdinalIgnoreCase);
         }
     }
 }

+ 15 - 14
src/NTMinerlib/VirtualRoot.partials.LocalMessage.cs

@@ -1,6 +1,7 @@
 using NTMiner.Core;
 using NTMiner.Hub;
 using System;
+using System.Collections.Generic;
 
 namespace NTMiner {
     public enum OutEnum {
@@ -114,23 +115,23 @@ namespace NTMiner {
             LocalMessage(LocalMessageChannel.This, provider, LocalMessageType.Error, content, outEnum: outEnum, toConsole: toConsole);
         }
 
+        private static readonly Dictionary<LocalMessageType, Action<string>> _consoleAction = new Dictionary<LocalMessageType, Action<string>> {
+            [LocalMessageType.Info] = NTMinerConsole.UserInfo,
+            [LocalMessageType.Warn] = NTMinerConsole.UserWarn,
+            [LocalMessageType.Error] = NTMinerConsole.UserError
+        };
+        public static void LocalMessage(LocalMessageChannel channel, string provider, LocalMessageType messageType, string consoleLine, string content, OutEnum outEnum) {
+            if (_consoleAction.TryGetValue(messageType, out Action<string> action)) {
+                action(consoleLine);
+            }
+            LocalMessage(channel, provider, messageType, content, outEnum, toConsole: false);
+        }
+
         public static void LocalMessage(LocalMessageChannel channel, string provider, LocalMessageType messageType, string content, OutEnum outEnum, bool toConsole) {
             switch (outEnum) {
                 case OutEnum.None:
-                    if (toConsole) {
-                        switch (messageType) {
-                            case LocalMessageType.Info:
-                                NTMinerConsole.UserInfo(content);
-                                break;
-                            case LocalMessageType.Warn:
-                                NTMinerConsole.UserWarn(content);
-                                break;
-                            case LocalMessageType.Error:
-                                NTMinerConsole.UserError(content);
-                                break;
-                            default:
-                                break;
-                        }
+                    if (toConsole && _consoleAction.TryGetValue(messageType, out Action<string> action)) {
+                        action(content);
                     }
                     break;
                 case OutEnum.Info:

+ 2 - 2
src/WebApiServer/AppRoot.cs

@@ -54,7 +54,7 @@ namespace NTMiner {
                 if (mutexCreated) {
                     try {
                         // 用本节点的地址作为队列名,消费消息时根据路由键区分消息类型
-                        string queue = $"{ServerAppType.WebApiServer.GetName()}.{ServerRoot.HostConfig.ThisServerAddress}";
+                        string queue = $"{nameof(ServerAppType.WebApiServer)}.{ServerRoot.HostConfig.ThisServerAddress}";
                         string durableQueue = queue + MqKeyword.DurableQueueEndsWith;
                         string wsBreathQueue = queue + MqKeyword.WsBreathQueueEndsWith;
                         AbstractMqMessagePath[] mqMessagePaths = new AbstractMqMessagePath[] {
@@ -69,7 +69,7 @@ namespace NTMiner {
                             NTMinerConsole.UserError("启动失败,无法继续,因为服务器上下文创建失败");
                             return;
                         }
-                        Console.Title = $"{ServerAppType.WebApiServer.GetName()}_{ServerRoot.HostConfig.ThisServerAddress}";
+                        Console.Title = $"{nameof(ServerAppType.WebApiServer)}_{ServerRoot.HostConfig.ThisServerAddress}";
                         // 阿里云OSS坑爹比七牛Kodo贵一半
                         CloudFileUrlGenerater = new AliCloudOSSFileUrlGenerater();
                         IRedis redis = mqRedis;

+ 17 - 3
src/WebApiServer/Controllers/KernelOutputKeywordController.cs

@@ -44,9 +44,6 @@ namespace NTMiner.Controllers {
                 return ResponseBase.InvalidInput("参数错误");
             }
             try {
-                if (request.Data.GetDataLevel() != DataLevel.Global) {
-                    return ResponseBase.InvalidInput("添加到服务器的内核输出关键字记录的DataLevel属性必须赋值为Global");
-                }
                 VirtualRoot.Execute(new AddOrUpdateKernelOutputKeywordCommand(request.Data));
                 AppRoot.UpdateKernelOutputKeywordTimestamp(DateTime.Now);
                 return ResponseBase.Ok();
@@ -56,5 +53,22 @@ namespace NTMiner.Controllers {
                 return ResponseBase.ServerError(e.Message);
             }
         }
+
+        [Role.Admin]
+        [HttpPost]
+        public ResponseBase ClearByExceptedOutputIds(DataRequest<Guid[]> request) {
+            if (request == null || request.Data == null || request.Data.Length == 0) {
+                return ResponseBase.InvalidInput("参数错误");
+            }
+            try {
+                VirtualRoot.Execute(new ClearKernelOutputKeywordsCommand(request.Data));
+                AppRoot.UpdateKernelOutputKeywordTimestamp(DateTime.Now);
+                return ResponseBase.Ok();
+            }
+            catch (Exception e) {
+                Logger.ErrorDebugLine(e);
+                return ResponseBase.ServerError(e.Message);
+            }
+        }
     }
 }

+ 14 - 0
src/WebApiServer/Core/Impl/KernelOutputKeywordSet.cs

@@ -1,6 +1,7 @@
 using LiteDB;
 using System;
 using System.Collections.Generic;
+using System.Linq;
 
 namespace NTMiner.Core.Impl {
     public class KernelOutputKeywordSet : SetBase, IKernelOutputKeywordSet {
@@ -51,12 +52,25 @@ namespace NTMiner.Core.Impl {
                 _dicById.Remove(entity.GetId());
                 if (_dicByKernelOutputId.TryGetValue(entity.KernelOutputId, out List<IKernelOutputKeyword> list)) {
                     list.Remove(entity);
+                    if (list.Count == 0) {
+                        _dicByKernelOutputId.Remove(entity.KernelOutputId);
+                    }
                 }
                 using (LiteDatabase db = new LiteDatabase(_connectionString)) {
                     var col = db.GetCollection<KernelOutputKeywordData>();
                     col.Delete(message.EntityId);
                 }
             }, location: this.GetType());
+            VirtualRoot.BuildCmdPath<ClearKernelOutputKeywordsCommand>(message => {
+                InitOnece();
+                if (message == null || message.ExceptedOutputIds == null || message.ExceptedOutputIds.Length == 0) {
+                    return;
+                }
+                var toRemoves = _dicById.Where(a => !message.ExceptedOutputIds.Contains(a.Value.KernelOutputId)).Select(a => a.Key).ToArray();
+                foreach (var item in toRemoves) {
+                    VirtualRoot.Execute(new RemoveKernelOutputKeywordCommand(item));
+                }
+            }, this.GetType());
         }
 
         protected override void Init() {

+ 2 - 2
src/WsServer/AppRoot.cs

@@ -47,9 +47,9 @@ namespace NTMiner {
             Windows.ConsoleHandler.Register(Exit);
 
             string thisServerAddress = ServerRoot.HostConfig.ThisServerAddress;
-            Console.Title = $"{ServerAppType.WsServer.GetName()}_{thisServerAddress}";
+            Console.Title = $"{nameof(ServerAppType.WsServer)}_{thisServerAddress}";
             // 用本节点的地址作为队列名,消费消息时根据路由键区分消息类型
-            string queue = $"{ServerAppType.WsServer.GetName()}.{thisServerAddress}";
+            string queue = $"{nameof(ServerAppType.WsServer)}.{thisServerAddress}";
             string durableQueue = queue + MqKeyword.DurableQueueEndsWith;
             AbstractMqMessagePath[] mqMessagePaths = new AbstractMqMessagePath[] {
                 new ReadOnlyUserMqMessagePath(durableQueue),