浏览代码

feat(d2-02): 命令分发电机(红线钳位)/EEPROM写 + 越界拒绝单测

huangjie 2 天之前
父节点
当前提交
592279786d

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

@@ -41,5 +41,32 @@ namespace IvfTl.ControlHost.Tests
             Assert.False(r.Ok);
             Assert.Equal("BAD_OP", r.Code);
         }
+        [Fact] public void Vertical_MoveTo_InRange_Dispatches()
+        {
+            var (mgr, serial, sid) = New();
+            var r = mgr.Execute(sid, "VerticalMoveTo", JObject.FromObject(new { pos = 90000 }));
+            Assert.True(r.Ok);
+            Assert.Contains("VMoveTo(90000)", serial.Calls);
+        }
+        [Fact] public void Vertical_MoveTo_OutOfRange_Rejected_NotDispatched()
+        {
+            var (mgr, serial, sid) = New();
+            var r = mgr.Execute(sid, "VerticalMoveTo", JObject.FromObject(new { pos = 130000 }));
+            Assert.Equal("OUT_OF_RANGE", r.Code);
+            Assert.DoesNotContain("VMoveTo(130000)", serial.Calls);
+        }
+        [Fact] public void Horizontal_Forward_Relative_Clamped()
+        {
+            var (mgr, serial, sid) = New();
+            mgr.Execute(sid, "HorizontalMoveTo", JObject.FromObject(new { pos = 215000 }));
+            var r = mgr.Execute(sid, "HorizontalForward", JObject.FromObject(new { value = 10000 }));
+            Assert.Equal("OUT_OF_RANGE", r.Code);
+        }
+        [Fact] public void WriteScanStep_Dispatches()
+        {
+            var (mgr, serial, sid) = New();
+            Assert.True(mgr.Execute(sid, "WriteScanStep", JObject.FromObject(new { value = 300 })).Ok);
+            Assert.Contains("WriteScanStep(300)", serial.Calls);
+        }
     }
 }

+ 46 - 1
ivf_tl_operate_2.0/control/ivf_tl_ControlHost/Debug/DebugSessionManager.cs

@@ -95,6 +95,51 @@ namespace IvfTl.ControlHost.Debug
         }
         // 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}");
+        {
+            int Arg(string k, int def = 0) => args?[k] != null ? args[k].Value<int>() : def;
+            int delay = -1;
+            switch (op)
+            {
+                case "VerticalReset": { bool ok = ser.VerticalResetWait(delay); s.CurrentVer = 0; return DebugCommandResult.Okay(ok); }
+                case "VerticalMoveTo":
+                {
+                    int pos = Arg("pos");
+                    if (!MotorClamp.IsVerticalInRange(pos)) return DebugCommandResult.Fail("OUT_OF_RANGE", $"垂直目标{pos}越界[0,{MotorClamp.VerMax}]");
+                    bool ok = ser.VerticalMoveToWait(pos, delay); if (ok) s.CurrentVer = pos; return DebugCommandResult.Okay(ok);
+                }
+                case "VerticalForward":
+                case "VerticalBackward":
+                {
+                    int delta = Arg("value") * (op == "VerticalBackward" ? -1 : 1);
+                    int basePos = s.CurrentVer < 0 ? 0 : s.CurrentVer;
+                    int target = MotorClamp.RelativeTarget(basePos, delta);
+                    if (!MotorClamp.IsVerticalInRange(target)) return DebugCommandResult.Fail("OUT_OF_RANGE", $"垂直目标{target}越界");
+                    bool ok = op == "VerticalBackward" ? ser.VerticalBackwardWait(Arg("value"), delay) : ser.VerticalForwardWait(Arg("value"), delay);
+                    if (ok) s.CurrentVer = target; return DebugCommandResult.Okay(ok);
+                }
+                case "HorizontalReset": { bool ok = ser.HorizontalResetWait(delay); s.CurrentHor = 0; return DebugCommandResult.Okay(ok); }
+                case "HorizontalMoveTo":
+                {
+                    int pos = Arg("pos");
+                    if (!MotorClamp.IsHorizontalInRange(pos)) return DebugCommandResult.Fail("OUT_OF_RANGE", $"水平目标{pos}越界[0,{MotorClamp.HorMax}]");
+                    bool ok = ser.HorizontalMoveToWait(pos, delay); if (ok) s.CurrentHor = pos; return DebugCommandResult.Okay(ok);
+                }
+                case "HorizontalForward":
+                case "HorizontalBackward":
+                {
+                    int delta = Arg("value") * (op == "HorizontalBackward" ? -1 : 1);
+                    int basePos = s.CurrentHor < 0 ? 0 : s.CurrentHor;
+                    int target = MotorClamp.RelativeTarget(basePos, delta);
+                    if (!MotorClamp.IsHorizontalInRange(target)) return DebugCommandResult.Fail("OUT_OF_RANGE", $"水平目标{target}越界");
+                    bool ok = op == "HorizontalBackward" ? ser.HorizontalBackwardWait(Arg("value"), delay) : ser.HorizontalForwardWait(Arg("value"), delay);
+                    if (ok) s.CurrentHor = target; return DebugCommandResult.Okay(ok);
+                }
+                case "WriteScanStep": return DebugCommandResult.Okay(ser.WriteScanStepWait(Arg("value")));
+                case "WriteOpenIntakeTime": return DebugCommandResult.Okay(ser.WriteOpenIntakeTimeWait(Arg("value")));
+                case "WriteOpenVentTime": return DebugCommandResult.Okay(ser.WriteOpenVentTimeWait(Arg("value")));
+                case "WriteWellHorizontalPos": return DebugCommandResult.Okay(ser.WriteWellHorizontalPosWait(Arg("well"), Arg("hor")));
+                default: return DebugCommandResult.Fail("BAD_OP", $"未知 op: {op}");
+            }
+        }
     }
 }