Ver código fonte

feat(d2-02): 命令分发 Execute(读数/阀/LED)+ 会话校验单测

huangjie 2 dias atrás
pai
commit
cdc497647e

+ 45 - 0
ivf_tl_operate_2.0/control/IvfTl.ControlHost.Tests/DebugExecuteTests.cs

@@ -0,0 +1,45 @@
+using IvfTl.ControlHost.Debug;
+using IvfTl.ControlHost.Tests.Fakes;
+using Newtonsoft.Json.Linq;
+using System;
+using Xunit;
+namespace IvfTl.ControlHost.Tests
+{
+    public class DebugExecuteTests
+    {
+        private DateTime _now = new DateTime(2026, 6, 23);
+        private (DebugSessionManager mgr, FakeSerial serial, string sid) New()
+        {
+            var serial = new FakeSerial();
+            var gate = new FakeGate(5, serial);
+            var mgr = new DebugSessionManager(sn => gate, () => _now, 10000, _ => { });
+            string sid = (string)mgr.Acquire(5).Result;
+            return (mgr, serial, sid);
+        }
+        [Fact] public void Execute_ReadTemp_Dispatches()
+        {
+            var (mgr, serial, sid) = New();
+            var r = mgr.Execute(sid, "ReadTemp", null);
+            Assert.True(r.Ok);
+            Assert.Contains("Temp", serial.Calls);
+        }
+        [Fact] public void Execute_OpenLed_Dispatches()
+        {
+            var (mgr, serial, sid) = New();
+            Assert.True(mgr.Execute(sid, "OpenLed", null).Ok);
+            Assert.Contains("OpenLed", serial.Calls);
+        }
+        [Fact] public void Execute_Expired_Session_Rejected()
+        {
+            var (mgr, _, _) = New();
+            Assert.Equal("SESSION_EXPIRED", mgr.Execute("ghost", "ReadTemp", null).Code);
+        }
+        [Fact] public void Execute_Unknown_Op_Rejected()
+        {
+            var (mgr, _, sid) = New();
+            var r = mgr.Execute(sid, "NoSuchOp", null);
+            Assert.False(r.Ok);
+            Assert.Equal("BAD_OP", r.Code);
+        }
+    }
+}

+ 35 - 0
ivf_tl_operate_2.0/control/ivf_tl_ControlHost/Debug/DebugSessionManager.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Concurrent;
 using IvfTl.Hardware;
+using Newtonsoft.Json.Linq;
 namespace IvfTl.ControlHost.Debug
 {
     /// <summary>
@@ -61,5 +62,39 @@ namespace IvfTl.ControlHost.Debug
             }
             return n;
         }
+
+        public DebugCommandResult Execute(string sid, string op, JObject args)
+        {
+            if (sid == null || !_sessions.TryGetValue(sid, out var s))
+                return DebugCommandResult.Fail("SESSION_EXPIRED", "会话不存在或已过期");
+            s.LastSeen = _clock();
+            var ser = s.Lease.Serial;
+            if (ser == null) return DebugCommandResult.Fail("NO_HANDLE", "借用串口句柄为空");
+            try
+            {
+                switch (op)
+                {
+                    case "ReadTemp": return DebugCommandResult.Okay(ser.TemperatureWait());
+                    case "ReadPressure": return DebugCommandResult.Okay(ser.PressureWait());
+                    case "ReadDoor": return DebugCommandResult.Okay(ser.DoorStatusWait().ToString());
+                    case "ReadVentTime": return DebugCommandResult.Okay(ser.ReadOpenVentTimeWait());
+                    case "ShakeHands": return DebugCommandResult.Okay(ser.ShakeHandsWait());
+                    case "OpenLed": return DebugCommandResult.Okay(ser.OpenLedWait());
+                    case "CloseLed": return DebugCommandResult.Okay(ser.CloseLedWait());
+                    case "OpenIntake": return DebugCommandResult.Okay(ser.OpenIntakeValveWait());
+                    case "CloseIntake": return DebugCommandResult.Okay(ser.CloseIntakeValveWait());
+                    case "OpenExhaust": return DebugCommandResult.Okay(ser.OpenExhaustValveWait());
+                    case "CloseExhaust": return DebugCommandResult.Okay(ser.CloseExhaustValveWait());
+                    case "HouseAeration": return DebugCommandResult.Okay(ser.HouseAerationWait());
+                    case "HouseVent": return DebugCommandResult.Okay(ser.HouseVentWait());
+                    default:
+                        return ExecuteMotorOrEeprom(s, ser, op, args);
+                }
+            }
+            catch (Exception ex) { return DebugCommandResult.Fail("HARDWARE_ERROR", ex.Message); }
+        }
+        // Task7 先占位:返回 BAD_OP,让"未知 op"测试通过;Task7 替换为真实电机/EEPROM 分发。
+        private DebugCommandResult ExecuteMotorOrEeprom(DebugSession s, IvfTl.Hardware.ISerialChannel ser, string op, JObject args)
+            => DebugCommandResult.Fail("BAD_OP", $"未知 op: {op}");
     }
 }