返回提交历史
Modified
AutoMiningScript.Tests/SurveyNavigationTests.cs
+86
-0
Modified
AutoMiningScript/MinerController.cs
+7
-2
Modified
Localization/miner.en-US.json
+1
-0
Modified
Localization/miner.zh-CN.json
+1
-0
Modified
docs/VALIDATION.md
+8
-0
SpaceEngineersModDev/AutoMiningScript
修复不可达探索入口的持续绕行并记录实机调试
c5f2d22
代码差异
5 个文件
+103
-2
@@ -158,6 +158,92 @@ namespace AutoMiningScript.Tests
158
158
Assert.DoesNotContain(r.R.Sent,p=>p.Kind=="RESULT");Assert.Equal(P.L.MinerWaitSurveyScan,Read<string>(r.R.Miner,"reason"));
159
159
r.Ready=true;Assert.Equal("SurveyEmpty",r.Finish().Outcome);
160
160
}
161
162
[Fact]
163
public void SurveyEntryLoopReportsBlockedEvenWhileLocalFlightKeepsMoving()
164
{
165
var r=new SurveyRig();r.Job.Entry=Vector3D.Zero;
166
r.R.Camera.Method("CanScan",a=>true);
167
// Like the live trace, repeatedly fly useful-looking local legs while
168
// never getting meaningfully closer than the best approach to the entry.
169
for(int n=0;n<1300 && !r.R.Sent.Any(p=>p.Kind=="RESULT");n++)
170
{
171
double angle=n*.05;
172
r.R.Position=new Vector3D(Math.Cos(angle)*12,0,Math.Sin(angle)*12);
173
r.Velocity=new Vector3D(-Math.Sin(angle)*3,0,Math.Cos(angle)*3);
174
r.Tick(false);
175
Assert.False(r.Flight.Blocked);
176
}
177
var report=P.JobReport.FromIni(Assert.Single(r.R.Sent,p=>p.Kind=="RESULT").Body);
178
Assert.Equal("Blocked",report.Outcome);Assert.Equal(r.Job.Id,report.JobId);
179
Assert.Null(report.Scan);Assert.Equal(0,r.SurveyRays);
180
Assert.InRange(r.R.Program.Now,45,46);
181
Assert.Null(Read<P.Job>(r.R.Miner,"job"));
182
Assert.Equal(P.FlightState.Survey,r.R.StateNow);
183
Assert.True(Read<bool>(r.R.Miner,"continuing"));
184
}
185
186
[Fact]
187
public void SurveyTransitKeepsItsBudgetWhileMakingSlowCumulativeProgress()
188
{
189
var r=new SurveyRig();r.Job.Entry=Vector3D.Zero;r.R.Camera.Method("CanScan",a=>true);
190
for(int n=0;n<1800;n++)
191
{
192
r.R.Position=Vector3D.Backward*(100-n*.005);r.Velocity=Vector3D.Forward*.1;
193
r.Tick(false);
194
}
195
Assert.True(r.R.Program.Now>89);Assert.Equal(P.FlightState.Transit,r.R.StateNow);
196
Assert.DoesNotContain(r.R.Sent,p=>p.Kind=="RESULT");
197
}
198
199
[Fact]
200
public void SurveyBlockageBudgetUsesRecentProgressInsteadOfTotalTransitAge()
201
{
202
var r=new SurveyRig();r.R.Camera.Method("CanScan",a=>true);
203
r.R.Position=Vector3D.Backward*100;r.Tick(false);
204
r.R.Program.Now=50;r.R.Position=Vector3D.Backward*20;
205
foreach(var thrust in r.R.Hardware.Thrusters)thrust.Enabled=false;
206
r.Tick(false);
207
Assert.True(r.Flight.Blocked);Assert.Equal(P.FlightState.Transit,r.R.StateNow);
208
Assert.DoesNotContain(r.R.Sent,p=>p.Kind=="RESULT");
209
}
210
211
[Fact]
212
public void SurveyCanTemporarilyDetourAwayThenResumeApproachingTheEntry()
213
{
214
var r=new SurveyRig();r.Job.Entry=Vector3D.Zero;r.R.Camera.Method("CanScan",a=>true);
215
for(int n=0;n<700;n++)
216
{
217
r.R.Position=Vector3D.Backward*(n<500?20+n*.01:25-(n-500)*.05);
218
r.Tick(false);
219
}
220
Assert.Equal(P.FlightState.Transit,r.R.StateNow);Assert.DoesNotContain(r.R.Sent,p=>p.Kind=="RESULT");
221
r.R.Position=Vector3D.Zero;r.Velocity=Vector3D.Zero;
222
for(int n=0;n<50 && !r.R.Sent.Any(p=>p.Kind=="RESULT");n++)r.Tick();
223
Assert.Equal("SurveyEmpty",P.JobReport.FromIni(Assert.Single(r.R.Sent,p=>p.Kind=="RESULT").Body).Outcome);
224
}
225
226
[Fact]
227
public void AsteroidObservedAtTheProgressDeadlineStillBecomesASurfaceReport()
228
{
229
var r=new SurveyRig();r.R.Position=Vector3D.Backward*20;r.R.Camera.Method("CanScan",a=>true);
230
r.Tick(false);r.R.Program.Now=50;r.Flight.ResetRoute();
231
var surface=new Vector3D(0,0,-100);
232
r.R.Camera.Method("Raycast",a=>new MyDetectedEntityInfo(42,"Asteroid",MyDetectedEntityType.Asteroid,surface,
233
MatrixD.Identity,Vector3.Zero,VRage.Game.MyRelationsBetweenPlayerAndBlock.NoOwnership,
234
new BoundingBoxD(surface-Vector3D.One,surface+Vector3D.One),1));
235
r.Tick(false);
236
Assert.Equal("SurveyHit",P.JobReport.FromIni(Assert.Single(r.R.Sent,p=>p.Kind=="RESULT").Body).Outcome);
237
}
238
239
[Fact]
240
public void ResumingSurveyTransitStartsANewProgressWindow()
241
{
242
var r=new SurveyRig();r.R.Position=Vector3D.Backward*20;r.R.Camera.Method("CanScan",a=>true);
243
r.Tick(false);r.R.Program.Now=100;
244
r.R.Miner.Command("pause");r.R.Miner.Command("resume");r.Tick(false);
245
Assert.Equal(P.FlightState.Transit,r.R.StateNow);Assert.DoesNotContain(r.R.Sent,p=>p.Kind=="RESULT");
246
}
161
247
[Fact]
162
248
public void ProbeTransitReachesTheAlignmentToleranceBeforeTurningToTheBore()
163
249
{
@@ -68,7 +68,7 @@ namespace AutoMiningScript
68
68
bool ReturningToDock => state==FlightState.Returning || state==FlightState.Holding || state==FlightState.DockAlign || state==FlightState.DockApproach || state==FlightState.DockRetreat;
69
69
void Set(FlightState next,string text,bool keepRoute=false)
70
70
{
71
if(state!=next) {P.Log(L.State(next)+": "+text);state=next;stateSince=P.Now;progressAt=P.Now;lastProgress=0;waitingAligning=false;if(!keepRoute)flight.ResetRoute();if(next==FlightState.Align) {entryProbe=0;entryNearest=double.MaxValue;entryFarthest=double.MinValue;}if(next==FlightState.DockAlign || next==FlightState.DockApproach) {lastProgress=double.MaxValue;stableSince=-1;}}
71
if(state!=next) {P.Log(L.State(next)+": "+text);state=next;stateSince=P.Now;progressAt=P.Now;lastProgress=next==FlightState.Transit?double.MaxValue:0;waitingAligning=false;if(!keepRoute)flight.ResetRoute();if(next==FlightState.Align) {entryProbe=0;entryNearest=double.MaxValue;entryFarthest=double.MinValue;}if(next==FlightState.DockAlign || next==FlightState.DockApproach) {lastProgress=double.MaxValue;stableSince=-1;}}
72
72
reason=text;
73
73
}
74
74
public override void Tick(double dt)
@@ -231,6 +231,9 @@ namespace AutoMiningScript
231
231
var travel=Data.Unit(target-Position,f);
232
232
var cruiseUp=Data.LengthSquared(gravity)>0.001?Data.Unit(-gravity,u):u;
233
233
var distance=Data.Distance(Position,target);
234
// Local detour motion is not progress toward an observation entry.
235
// Keep the window alive only after gaining two metres on its best approach.
236
if(surveying && distance<lastProgress-2) {lastProgress=distance;progressAt=P.Now;}
234
237
var face=surveying || distance>ship.Radius*3?travel:f;
235
238
if(!surveying && !flight.CanSupport(face,Data.Perpendicular(face,cruiseUp),0.3)) {BeginReturn(L.MinerTargetAttitudeThrustLow);return;}
236
239
// Observation rays may point sideways from the arrival path. Keep the
@@ -243,7 +246,9 @@ namespace AutoMiningScript
243
246
reason=L.F(L.MinerDockNavigationProgress,L.MinerTravelTaskEntry,distance,Data.Length(ship.Controller.GetShipVelocities().LinearVelocity),flight.AttitudeError*180/Math.PI);
244
247
if(flight.Problem.Length>0)reason=flight.Problem+" / "+reason;
245
248
RecordBreadcrumb();
246
if(flight.Blocked && P.Now-stateSince>45) {FinishBlocked(L.MinerRouteBlocked+" / "+flight.Problem);return;}
249
if(surveying && !arrived && P.Now-progressAt>45)
250
{flight.Hold(dt);FinishBlocked(L.MinerSurveyEntryStalled);return;}
251
if(!surveying && flight.Blocked && P.Now-stateSince>45) {FinishBlocked(L.MinerRouteBlocked+" / "+flight.Problem);return;}
247
252
if(arrived)Set(surveying?FlightState.Survey:FlightState.Align,L.MinerTaskEntryReached);
248
253
}
249
254
void Align(double dt)
@@ -1,5 +1,6 @@
1
1
{
2
2
"MinerFindingRoute": "Scanning a route around the obstacle",
3
"MinerSurveyEntryStalled": "No progress toward survey entry; reported blocked for reassignment",
3
4
"MinerAutomaticReturn": "Initialized; returning to dock",
4
5
"MinerWaitNextTask": "Awaiting next regional task; probes first",
5
6
"MinerSearchCompleteReturn": "No pending regional work; returning",
@@ -1,5 +1,6 @@
1
1
{
2
2
"MinerFindingRoute": "正在扫描可行绕路",
3
"MinerSurveyEntryStalled": "探索入口持续无法接近,已报告受阻并等待续派",
3
4
"MinerAutomaticReturn": "矿机初始化完成,自动返航接港",
4
5
"MinerWaitNextTask": "本项作业完成,等待本矿区续派(优先试钻)",
5
6
"MinerSearchCompleteReturn": "本矿区暂无待执行任务,返航待命",
@@ -4,6 +4,14 @@
4
4
5
5
## 开发机检查
6
6
7
2026-09-14 不可达探索入口退出:在原仓库 `f6b7d8f` 的独立工作树中修复,不改动原工作树已有的主控修改。AgentBridge 对授权单机副本的实机采样显示,7号在同一探索任务346上多次停车、换绕行点再回头;从脚本时间204秒持续到503秒仍未进入扫描。导航能进行局部绕行,不能用单帧 `Blocked` 代表任务入口可达。现有命中点距目标约1.54米,矿机半径约4.63米,因此这条采样路径不足以安全抵达原入口。原始证据为 AgentBridge 项目的 `artifacts/seven-survey-navigation.jsonl` 和 `artifacts/seven-current.json`。
8
9
探索入口按累计接近至少2米刷新进展窗口;45秒没有有效接近时当次制动,沿现有可靠结果协议报告 `Blocked` 并在空中等待续派。当帧真实小行星命中仍优先报告 `SurveyHit`,合法绕行和缓慢累计接近保留。没有把任意绕行点冒充原扫描入口,以免错误完成覆盖任务;非探索任务保留原受阻处理。
10
11
六项回归覆盖不停移动的循环、慢速累计接近、长途后短暂阻挡、先远离再接近、截止时发现小行星,以及暂停恢复后的新窗口。循环回归在原逻辑下失败、修复后通过。完整 `./build.ps1` 成功,中英文各810/810项测试、428对语言资源及8项生成器检查通过;八份实际导出均通过编译和角色校验,0警告、0错误,均小于100000字符。日志为 `obj/agent-debug-build.log`,导出校验见 `dist/manifest.json`;中文矿机95590字符,SHA-256为 `b191430cf9ab7fe2b21569344ebcdc927bfa52847b84632440bdc68f3e5abc73`。本段记录的是修复前实机证据和修复后的自动化结果,修复后的实机续派仍待验证。
12
13
通过 AgentBridge 将上述中文导出部署到授权副本的7号,游戏返回已部署、已编译、有运行实例、无编译错误,并保留了源码备份。更新后的43个实机样本均在返航对齐/避让2号阶段,未重新进入探索入口,因此没有把这段运行计为45秒受阻续派验收。副本最终保存并正常退出;原存档25个文件摘要一致、无新增删除。
14
7
15
2026-09-14 停靠断推、发现优先派钻与六向摄像机绕障:只读核对14:36、14:41及15:01存档,7号处于Survey状态,28个镜头覆盖六向、16个推进器覆盖六轴。连续存档显示它更换了探索任务但只移动约3米;原导航遇障后反复受阻,不能据此认定为任务派发互锁。按85个方块的定义尺寸和安装姿态复算,远控相对船体范围为(-2.25,-1.75,-2.25)至(2.25,1.25,2.25),实际推力余量约5.24m/s²。原游戏存档未修改,证据见 `obj/miner7-save-audit.json` 和 `obj/miner7-body-audit.json`。
8
16
9
17
物理连接后清零并关闭本机同网格全部推进器,包括硬件组外的推进器;正常离港先恢复推进器再核算推力,连接锁定当次也执行关闭。新小行星的发现结果与完成遥测齐备后优先安排试钻,消息颠倒或重复不会重置调度,资源不足可交给其他可用矿机,其他正在执行的探索及钻孔不被抢占。