NewLife/NewLife.Remoting

web指令下发完全通过,由于没有命令表,command.Id为0,客户端不应该去重
大石头 authored at 2024-07-01 10:16:58
a2eb4da
Tree
1 Parent(s) dcbcc89
Summary: 6 changed files with 78 additions and 14 deletions.
Modified +1 -1
Modified +75 -13
Modified +1 -0
Renamed +0 -0
Samples/IoTZero/Areas/IoT/Views/NodeOnline/_List_Search.cshtml → Samples/IoTZero/Areas/IoT/Views/DeviceOnline/_List_Search.cshtml
Renamed +0 -0
Samples/IoTZero/Areas/IoT/Views/NodeOnline/_List_Toolbar_Batch.cshtml → Samples/IoTZero/Areas/IoT/Views/DeviceOnline/_List_Toolbar_Batch.cshtml
Modified +1 -0
Modified +1 -1
diff --git a/NewLife.Remoting.Extensions/Controllers/BaseDeviceController.cs b/NewLife.Remoting.Extensions/Controllers/BaseDeviceController.cs
index cc6dce6..75ba84e 100644
--- a/NewLife.Remoting.Extensions/Controllers/BaseDeviceController.cs
+++ b/NewLife.Remoting.Extensions/Controllers/BaseDeviceController.cs
@@ -232,7 +232,7 @@ public class BaseDeviceController : BaseController
                     var msg = JsonHelper.Convert<CommandModel>(dic);
                     span?.Detach(dic);
 
-                    if (msg == null || msg.Id == 0 || msg.Expire.Year > 2000 && msg.Expire < DateTime.Now)
+                    if (msg == null || /*msg.Id == 0 ||*/ msg.Expire.Year > 2000 && msg.Expire < DateTime.Now)
                     {
                         WriteLog("WebSocket发送", false, "消息无效或已过期。" + mqMsg);
                     }
Modified +75 -13
diff --git a/NewLife.Remoting/Clients/ClientBase.cs b/NewLife.Remoting/Clients/ClientBase.cs
index 8adc72e..74c199c 100644
--- a/NewLife.Remoting/Clients/ClientBase.cs
+++ b/NewLife.Remoting/Clients/ClientBase.cs
@@ -153,6 +153,8 @@ public abstract class ClientBase : DisposeBase, IApiClient, ICommandClient, IEve
             [Features.Upgrade] = prefix + "Upgrade",
             [Features.PostEvent] = prefix + "PostEvents"
         };
+
+        this.RegisterCommand(prefix + "Upgrade", s => _ = CheckUpgrade(null));
     }
 
     /// <summary>初始化</summary>
@@ -698,7 +700,7 @@ public abstract class ClientBase : DisposeBase, IApiClient, ICommandClient, IEve
                         _timer = new TimerX(OnPing, null, 1000, 60_000, "Client") { Async = true };
 
                     if (Features.HasFlag(Features.Upgrade))
-                        _timerUpgrade = new TimerX(s => Upgrade(null), null, 5_000, 600_000, "Client") { Async = true };
+                        _timerUpgrade = new TimerX(CheckUpgrade, null, 5_000, 600_000, "Client") { Async = true };
                 }
             }
         }
@@ -763,7 +765,7 @@ public abstract class ClientBase : DisposeBase, IApiClient, ICommandClient, IEve
         if (model == null) return null;
 
         // 去重,避免命令被重复执行
-        if (!_cache.Add($"cmd:{model.Id}", model, 3600)) return null;
+        if (model.Id > 0 && !_cache.Add($"cmd:{model.Id}", model, 3600)) return null;
 
         // 埋点,建立调用链
         using var span = Tracer?.NewSpan("cmd:" + model.Command, model);
@@ -935,6 +937,13 @@ public abstract class ClientBase : DisposeBase, IApiClient, ICommandClient, IEve
     #endregion
 
     #region 升级更新
+    private async Task CheckUpgrade(Object? data)
+    {
+        if (!NetworkInterface.GetIsNetworkAvailable()) return;
+
+        await Upgrade(null);
+    }
+
     private String? _lastVersion;
     /// <summary>获取更新信息。如有更新,则下载解压覆盖并重启应用</summary>
     /// <returns></returns>
@@ -953,23 +962,45 @@ public abstract class ClientBase : DisposeBase, IApiClient, ICommandClient, IEve
         {
             // _lastVersion避免频繁更新同一个版本
             WriteLog("发现更新:{0}", info.ToJson(true));
+            this.WriteInfoEvent("Upgrade", $"准备从[{_lastVersion}]更新到[{info.Version}],开始下载 {info.Source}");
 
             // 下载文件包
-            ug.Url = info.Source;
+            ug.Url = BuildUrl(info.Source!);
             await ug.Download(cancellationToken);
 
             // 检查文件完整性
-            if (info.FileHash.IsNullOrEmpty() || ug.CheckFileHash(info.FileHash))
+            if (!info.FileHash.IsNullOrEmpty() && !ug.CheckFileHash(info.FileHash))
+            {
+                this.WriteInfoEvent("Upgrade", "下载完成,哈希校验失败");
+            }
+            else
             {
-                // 执行更新,解压缩覆盖文件
-                var rs = ug.Update();
+                this.WriteInfoEvent("Upgrade", "下载完成,准备解压文件");
+                if (!ug.Extract())
+                {
+                    this.WriteInfoEvent("Upgrade", "解压失败");
+                }
+                else
+                {
+                    if (info is UpgradeInfo info2 && !info2.Preinstall.IsNullOrEmpty())
+                    {
+                        this.WriteInfoEvent("Upgrade", "执行预安装脚本");
+
+                        ug.Run(info2.Preinstall);
+                    }
+
+                    this.WriteInfoEvent("Upgrade", "解压完成,准备覆盖文件");
 
-                // 执行前置命令
-                if (rs && !info.Executor.IsNullOrEmpty()) ug.Run(info.Executor);
-                _lastVersion = info.Version;
+                    // 执行更新,解压缩覆盖文件
+                    var rs = ug.Update();
 
-                // 强制更新时,马上重启
-                if (rs && info.Force) Restart(ug);
+                    // 执行前置命令
+                    if (rs && !info.Executor.IsNullOrEmpty()) ug.Run(info.Executor);
+                    _lastVersion = info.Version;
+
+                    // 强制更新时,马上重启
+                    if (rs && info.Force) Restart(ug);
+                }
             }
         }
 
@@ -981,11 +1012,25 @@ public abstract class ClientBase : DisposeBase, IApiClient, ICommandClient, IEve
     protected virtual void Restart(Upgrade upgrade)
     {
         var asm = Assembly.GetEntryAssembly();
+        if (asm == null) return;
+
+        var name = asm.GetName().Name;
+        if (name.IsNullOrEmpty()) return;
 
         // 重新拉起进程
-        var rs = upgrade.Run("dotnet", $"{asm!.Location} -upgrade {Environment.CommandLine}");
+        var rs = upgrade.Run(name, $"-upgrade {Environment.CommandLine}");
+
+        if (rs)
+        {
+            var pid = Process.GetCurrentProcess().Id;
+            this.WriteInfoEvent("Upgrade", "强制更新完成,新进程已拉起,准备退出当前进程!PID=" + pid);
 
-        if (rs) upgrade.KillSelf();
+            upgrade.KillSelf();
+        }
+        else
+        {
+            this.WriteInfoEvent("Upgrade", "强制更新完成,但拉起新进程失败");
+        }
     }
 
     /// <summary>放弃更新异步请求。由Upgrade内部调用</summary>
@@ -1000,6 +1045,23 @@ public abstract class ClientBase : DisposeBase, IApiClient, ICommandClient, IEve
     #endregion
 
     #region 辅助
+    /// <summary>
+    /// 把Url相对路径格式化为绝对路径。常用于文件下载
+    /// </summary>
+    /// <param name="url"></param>
+    /// <returns></returns>
+    public virtual String BuildUrl(String url)
+    {
+        if (Client is ApiHttpClient client && !url.StartsWithIgnoreCase("http://", "https://"))
+        {
+            var svr = client.Services.FirstOrDefault(e => e.Name == client.Source) ?? client.Services.FirstOrDefault();
+            if (svr != null && svr.Address != null)
+                url = new Uri(svr.Address, url) + "";
+        }
+
+        return url;
+    }
+
     /// <summary>从服务提供者(对象容器)创建模型对象</summary>
     /// <typeparam name="T"></typeparam>
     /// <returns></returns>
Modified +1 -0
diff --git a/Samples/IoTZero/Areas/IoT/Controllers/DeviceOnlineController.cs b/Samples/IoTZero/Areas/IoT/Controllers/DeviceOnlineController.cs
index 6108d60..823a4d3 100644
--- a/Samples/IoTZero/Areas/IoT/Controllers/DeviceOnlineController.cs
+++ b/Samples/IoTZero/Areas/IoT/Controllers/DeviceOnlineController.cs
@@ -107,6 +107,7 @@ public class DeviceOnlineController : EntityController<DeviceOnline>
                 };
                 var queue = _deviceService.GetQueue(cmd.Code);
                 queue.Add(cmd.ToJson());
+                ts.Add(Task.FromResult(1));
             }
         }
 
Renamed +0 -0
Samples/IoTZero/Areas/IoT/Views/NodeOnline/_List_Search.cshtml → Samples/IoTZero/Areas/IoT/Views/DeviceOnline/_List_Search.cshtml
diff --git a/Samples/IoTZero/Areas/IoT/Views/NodeOnline/_List_Search.cshtml b/Samples/IoTZero/Areas/IoT/Views/DeviceOnline/_List_Search.cshtml
similarity index 100%
rename from Samples/IoTZero/Areas/IoT/Views/NodeOnline/_List_Search.cshtml
rename to Samples/IoTZero/Areas/IoT/Views/DeviceOnline/_List_Search.cshtml
Renamed +0 -0
Samples/IoTZero/Areas/IoT/Views/NodeOnline/_List_Toolbar_Batch.cshtml → Samples/IoTZero/Areas/IoT/Views/DeviceOnline/_List_Toolbar_Batch.cshtml
diff --git a/Samples/IoTZero/Areas/IoT/Views/NodeOnline/_List_Toolbar_Batch.cshtml b/Samples/IoTZero/Areas/IoT/Views/DeviceOnline/_List_Toolbar_Batch.cshtml
similarity index 100%
rename from Samples/IoTZero/Areas/IoT/Views/NodeOnline/_List_Toolbar_Batch.cshtml
rename to Samples/IoTZero/Areas/IoT/Views/DeviceOnline/_List_Toolbar_Batch.cshtml
Modified +1 -0
diff --git a/Samples/ZeroServer/Areas/Nodes/Controllers/NodeOnlineController.cs b/Samples/ZeroServer/Areas/Nodes/Controllers/NodeOnlineController.cs
index ef08431..5320030 100644
--- a/Samples/ZeroServer/Areas/Nodes/Controllers/NodeOnlineController.cs
+++ b/Samples/ZeroServer/Areas/Nodes/Controllers/NodeOnlineController.cs
@@ -116,6 +116,7 @@ public class NodeOnlineController : NodeEntityController<NodeOnline>
                 };
                 var queue = _deviceService.GetQueue(online.Node.Code);
                 queue.Add(cmd.ToJson());
+                ts.Add(Task.FromResult(1));
             }
         }