返回提交历史
Modified
AutoMiningScript.Tests/DockingNavigationTests.cs
+60
-4
Modified
AutoMiningScript/MinerController.cs
+22
-8
Modified
AutoMiningScript/Program.cs
+3
-3
Modified
AutoMiningScript/Settings.cs
+1
-0
Modified
Localization/common.en-US.json
+2
-0
Modified
Localization/common.zh-CN.json
+2
-0
Modified
Localization/docking.en-US.json
+1
-0
Modified
Localization/docking.zh-CN.json
+1
-0
Modified
README.md
+6
-3
Modified
docs/PARAMETERS.zh-CN.md
+2
-0
Modified
docs/VALIDATION.md
+16
-8
SpaceEngineersModDev/AutoMiningScript
优化磁吸锁定逻辑与自动命名,完善测试与文档
修复返航、等候、对齐、贴合及接港退避等状态下,连接器报告“可连接”即清除推力与陀螺覆盖并尝试锁定,锁定未完成时显示原因并重试,接错接口时保持连接并报告错误,人工暂停、交还控制和正常离港不自动锁定。新增磁吸锁定相关单元测试,覆盖多场景,确保锁定逻辑正确。主控和矿机等脚本加载配置后自动设置自身名称,主控固定为“XFEAMS系统/ System”,其他类型按编号命名,命名资源加入中英文语言包。更新文档,详细说明自动命名和锁定行为变更,补充验收与测试说明。资源文件增加相关提示文本。结构优化,统一返航等状态判断为 ReturningToDock,减少重复。所有导出脚本字符数合规,SHA-256 校验通过。
8481168
代码差异
11 个文件
+116
-26
@@ -24,7 +24,8 @@ namespace AutoMiningScript.Tests
24
24
public readonly P.DockFrame Dock;
25
25
public MatrixD Orientation=MatrixD.Identity,Mount=MatrixD.CreateWorld(Vector3D.Zero,Vector3D.Backward,Vector3D.Up);
26
26
public Vector3D Velocity,AngularVelocity=Vector3D.Zero;
27
public bool CamerasReady=true,Connected;
27
public bool CamerasReady=true,Connected,Magnetized,LockSucceeds=true;
28
public long PeerId=51;
28
29
public int ConnectCalls,Rays;
29
30
public double Mass=1000,ContactSpacing=1;
30
31
public readonly List<P.FlightState> States=new List<P.FlightState>();
@@ -46,13 +47,13 @@ namespace AutoMiningScript.Tests
46
47
}
47
48
var gyro=(Stub<IMyGyro>)RemotingServices.GetRealProxy(R.Hardware.Gyros[0]);
48
49
gyro.Values.Remove("WorldMatrix");gyro.Method("get_WorldMatrix",a=>Orientation);
49
var peer=new Stub<IMyShipConnector>().Set("EntityId",51L).Set("CubeGrid",new Stub<IMyCubeGrid>().Set("EntityId",50L).Value)
50
var peer=new Stub<IMyShipConnector>().Method("get_EntityId",a=>PeerId).Set("CubeGrid",new Stub<IMyCubeGrid>().Set("EntityId",50L).Value)
50
51
.Method("get_WorldMatrix",a=>MatrixD.CreateWorld(Dock.Position,Dock.Forward,Dock.Up)).Method("GetPosition",a=>Dock.Position);
51
52
R.Connector.Values.Remove("Status");
52
53
R.Connector.Method("GetPosition",a=>PortPosition).Method("get_WorldMatrix",a=>PortPose)
53
.Method("get_Status",a=>Connected?MyShipConnectorStatus.Connected:CanConnect?MyShipConnectorStatus.Connectable:MyShipConnectorStatus.Unconnected)
54
.Method("get_Status",a=>Connected?MyShipConnectorStatus.Connected:Magnetized||CanConnect?MyShipConnectorStatus.Connectable:MyShipConnectorStatus.Unconnected)
54
55
.Method("get_OtherConnector",a=>Connected?peer.Value:null)
55
.Method("Connect",a=>{ConnectCalls++;Connected=CanConnect;return null;});
56
.Method("Connect",a=>{ConnectCalls++;Connected=LockSucceeds&&(Magnetized||CanConnect);return null;});
56
57
MountPort(Mount,new Vector3D(0,0,2),MatrixD.Identity,Vector3D.Zero);
57
58
R.Hardware.Cameras.Clear();
58
59
foreach(Vector3D direction in new[]{Vector3D.Forward,Vector3D.Backward})AddCamera(direction);
@@ -124,6 +125,61 @@ namespace AutoMiningScript.Tests
124
125
r.Finish();Assert.True(r.Gap<start);Assert.Contains(P.FlightState.DockAlign,r.States);Assert.Contains(P.FlightState.DockApproach,r.States);
125
126
}
126
127
128
[Theory]
129
[InlineData(P.FlightState.Returning)]
130
[InlineData(P.FlightState.Holding)]
131
[InlineData(P.FlightState.DockAlign)]
132
[InlineData(P.FlightState.DockApproach)]
133
[InlineData(P.FlightState.DockRetreat)]
134
public void MagneticCaptureLocksBeforeGeometricCorrectionsOrTelemetryWaits(P.FlightState state)
135
{
136
var r=new DockRig();r.R.State(state);r.Magnetized=true;
137
r.R.Position=r.Contact+Vector3D.Right*.4;r.Orientation=MatrixD.CreateFromAxisAngle(Vector3D.Forward,.1);
138
r.Velocity=Vector3D.Right*.4;r.AngularVelocity=Vector3D.Forward*.02;r.Dock.ReceivedAt=r.R.Program.Now-10;
139
r.R.Hardware.Thrusters[0].ThrustOverridePercentage=.5f;r.R.Hardware.Gyros[0].GyroOverride=true;
140
r.R.Miner.Tick(.05);
141
Assert.True(r.Connected);Assert.Equal(1,r.ConnectCalls);Assert.Equal(P.FlightState.Servicing,r.R.StateNow);
142
Assert.All(r.R.Hardware.Thrusters,t=>Assert.Equal(0f,t.ThrustOverridePercentage));
143
Assert.All(r.R.Hardware.Gyros,g=>Assert.False(g.GyroOverride));Assert.False(r.Flight.HasRoute);
144
Assert.Contains(r.R.Sent,p=>p.Kind=="TELEMETRY" && P.Telemetry.FromIni(p.Body).Connected);
145
}
146
147
[Fact]
148
public void PendingMagneticLockStopsCorrectionAndRetriesUntilActuallyConnected()
149
{
150
var r=new DockRig();r.R.State(P.FlightState.DockApproach);r.Magnetized=true;r.LockSucceeds=false;
151
r.Tick();Assert.False(r.Connected);Assert.Equal(1,r.ConnectCalls);
152
Assert.Equal(P.FlightState.DockApproach,r.R.StateNow);Assert.Equal(P.L.MinerDockLockPending,r.Reason);
153
Assert.False(r.Flight.HasRoute);Assert.Equal(0,r.Rays);
154
r.LockSucceeds=true;r.Tick();Assert.True(r.Connected);Assert.Equal(2,r.ConnectCalls);
155
Assert.Equal(P.FlightState.Servicing,r.R.StateNow);
156
}
157
158
[Theory]
159
[InlineData("pause")]
160
[InlineData("release")]
161
public void MagneticCaptureDoesNotOverrideAnOperatorStop(string command)
162
{
163
var r=new DockRig();r.Magnetized=true;r.R.Miner.Command(command);r.R.Miner.Tick(.05);
164
Assert.False(r.Connected);Assert.Equal(0,r.ConnectCalls);
165
}
166
167
[Fact]
168
public void DepartureDoesNotRelockWhileConnectorsAreStillMagneticallyInRange()
169
{
170
var r=new DockRig();r.Magnetized=true;r.R.State(P.FlightState.Departing);Write(r.R.Miner,"laneKind","depart");
171
for(int n=0;n<3;n++)r.R.Miner.Tick(.05);
172
Assert.False(r.Connected);Assert.Equal(0,r.ConnectCalls);Assert.Equal(P.FlightState.Departing,r.R.StateNow);
173
}
174
175
[Fact]
176
public void APhysicallyConnectedUnexpectedPortIsReportedWithoutContinuingThrust()
177
{
178
var r=new DockRig();r.Magnetized=true;r.PeerId=999;r.R.State(P.FlightState.DockApproach);r.R.Miner.Tick(.05);
179
Assert.True(r.Connected);Assert.Equal(P.FlightState.Fault,r.R.StateNow);Assert.Equal(P.L.MinerWrongDock,r.Reason);
180
Assert.False(r.Flight.HasRoute);Assert.All(r.R.Hardware.Thrusters,t=>Assert.Equal(0f,t.ThrustOverridePercentage));
181
}
182
127
183
[Theory]
128
184
[InlineData(5d)]
129
185
[InlineData(20d)]
@@ -61,6 +61,7 @@ namespace AutoMiningScript
61
61
double DockDistance => Math.Max(P.Config.Number("Dock","ApproachDistance",30,10,300),ship.Radius*2);
62
62
bool InHole => job!=null && job.Kind!=JobKind.Survey && (insideBore || state==FlightState.Drilling || state==FlightState.Retreat);
63
63
bool Idle => state==FlightState.Paused || state==FlightState.Boot || state==FlightState.Docked || state==FlightState.Servicing || state==FlightState.Ready;
64
bool ReturningToDock => state==FlightState.Returning || state==FlightState.Holding || state==FlightState.DockAlign || state==FlightState.DockApproach || state==FlightState.DockRetreat;
64
65
void Set(FlightState next,string text,bool keepRoute=false)
65
66
{
66
67
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;}}
@@ -109,8 +110,9 @@ namespace AutoMiningScript
109
110
}
110
111
if(Idle)
111
112
{flight.Hold(dt);return;}
113
if(ReturningToDock && TryConnect())return;
112
114
string returnReason;
113
if(state!=FlightState.Returning && state!=FlightState.Holding && state!=FlightState.DockAlign && state!=FlightState.DockApproach && state!=FlightState.DockRetreat && state!=FlightState.Retreat && resources.MustReturn(out returnReason))BeginReturn(returnReason);
115
if(!ReturningToDock && state!=FlightState.Retreat && resources.MustReturn(out returnReason))BeginReturn(returnReason);
114
116
if(home!=null && P.Now-home.ReceivedAt>5)
115
117
{
116
118
if(state==FlightState.DockApproach || state==FlightState.DockAlign || state==FlightState.Departing)AbortDock(L.MinerBaseTimeoutAbortDock);
@@ -362,7 +364,7 @@ namespace AutoMiningScript
362
364
reason=text;pausedAfterRetreat=false;
363
365
if(InHole) {Set(FlightState.Retreat,text);return;}
364
366
if(state==FlightState.Departing) {AbortDock(text);return;}
365
if(state==FlightState.Returning || state==FlightState.Holding || state==FlightState.DockAlign || state==FlightState.DockApproach || state==FlightState.DockRetreat)return;
367
if(ReturningToDock)return;
366
368
retry=0;
367
369
ship.SetDrills(false);ship.SetSampling(false);returnIndex=breadcrumbs.Count-1;Set(FlightState.Returning,text);
368
370
}
@@ -546,6 +548,7 @@ namespace AutoMiningScript
546
548
}
547
549
void DockApproach(double dt)
548
550
{
551
if(TryConnect())return;
549
552
var permit=DockProblem("dock");if(permit.Length>0) {AbortDock(permit);return;}
550
553
Vector3D f,u;var target=DockTarget(0,out f,out u);
551
554
var contact=target;
@@ -579,13 +582,24 @@ namespace AutoMiningScript
579
582
// connector geometry directly. Camera coverage and hits no longer gate it.
580
583
flight.Move(target,dock.PointVelocity(target),f,u,speed,0.1,false,dt,false,false);
581
584
reason=DockNavigationReason(synchronizing?L.MinerDockSyncPose:depth>captureDistance?L.MinerDockFollowAxis:L.MinerSlowRelativeApproach,contact)+" / "+L.F(L.MinerDockPrecision,lateral.Length(),angular);
582
if(ship.Connector.Status==MyShipConnectorStatus.Connectable && aligned && relative.Length()<0.3)
585
if(state==FlightState.DockApproach)CheckDockProgress(contact,L.MinerDockTimeout);
586
}
587
bool TryConnect()
588
{
589
if(ship.Connector==null || ship.Connector.Status!=MyShipConnectorStatus.Connectable)return false;
590
// Magnetic capture is authoritative. Do not fight it with geometric
591
// corrections, or wait for stale approach telemetry before locking.
592
flight.Release();ship.Connector.Connect();reason=L.MinerDockLockPending;
593
if(Connected)
583
594
{
584
ship.Connector.Connect();
585
if(Connected && ship.Connector.OtherConnector!=null && ship.Connector.OtherConnector.EntityId==dock.ConnectorId)
586
{ReleaseLane();flight.ClearDockContact();ship.Release();Set(FlightState.Servicing,L.MinerDockConfirmedService);}
595
ReleaseLane();retry=0;
596
var other=ship.Connector.OtherConnector;
597
if(dock!=null && other!=null && other.EntityId!=dock.ConnectorId)
598
{running=false;Set(FlightState.Fault,L.MinerWrongDock);}
599
else Set(FlightState.Servicing,L.MinerDockConfirmedService);
600
Publish();
587
601
}
588
if(state==FlightState.DockApproach)CheckDockProgress(contact,L.MinerDockTimeout);
602
return true;
589
603
}
590
604
void AbortDock(string why)
591
605
{
@@ -761,7 +775,7 @@ namespace AutoMiningScript
761
775
void ContinueCancelledReturn()
762
776
{
763
777
if(state==FlightState.Departing)AbortDock(L.MinerTaskCancelledReturn);
764
else if(state!=FlightState.DockAlign && state!=FlightState.DockApproach && state!=FlightState.DockRetreat && state!=FlightState.Holding && state!=FlightState.Returning)BeginReturn(L.MinerTaskCancelledReturn);
778
else if(!ReturningToDock)BeginReturn(L.MinerTaskCancelledReturn);
765
779
}
766
780
void CancelThrough(long watermark)
767
781
{
@@ -46,7 +46,7 @@ namespace AutoMiningScript
46
46
{
47
47
var defaultRole=CompiledRole();var id=defaultRole=="fleet"?"base":defaultRole+"-"+Me.EntityId;
48
48
if(string.IsNullOrWhiteSpace(Me.CustomData)) {id=Settings.DefaultId(defaultRole,Me.EntityId);Me.CustomData=Settings.Template(defaultRole,id);}
49
Config=new Settings(Me.CustomData,defaultRole,id);dashboard=new Dashboard(this);Bus=new Wire(this);
49
Config=new Settings(Me.CustomData,defaultRole,id);Me.CustomName=Config.BlockName;dashboard=new Dashboard(this);Bus=new Wire(this);
50
50
#if FLEET
51
51
role=new FleetController(this);
52
52
#elif MARKER
@@ -110,7 +110,7 @@ namespace AutoMiningScript
110
110
#endif
111
111
if(!string.IsNullOrWhiteSpace(argument) && argument!="igc")
112
112
{
113
if(argument=="init") {role.Command("rescan");Log(L.CommonConfigReload);}
113
if(argument=="init") {Me.CustomName=Config.BlockName;role.Command("rescan");Log(L.CommonConfigReload);}
114
114
else if(argument=="page" || argument.StartsWith("page ",Data.Ordinal))dashboard.Command(argument);
115
115
else role.Command(argument.Trim());
116
116
}
@@ -165,7 +165,7 @@ namespace AutoMiningScript
165
165
if(Bus!=null)Bus.CancelPending(null);
166
166
var id=string.IsNullOrWhiteSpace(Me.CustomData)?Settings.DefaultId("fleet",Me.EntityId):"base";
167
167
if(string.IsNullOrWhiteSpace(Me.CustomData))Me.CustomData=Settings.Template("fleet",id);
168
Config=new Settings(Me.CustomData,"fleet",id);
168
Config=new Settings(Me.CustomData,"fleet",id);Me.CustomName=Config.BlockName;
169
169
if(Bus==null)Bus=new Wire(this);else Bus.Reset();
170
170
var fleet=new FleetController(this);role=fleet;fleet.BeginRestoreStorage(Storage);dashboard=new Dashboard(this);
171
171
nextDraw=0;nextSave=Now+2;BudgetDeferrals=0;nextBudgetNotice=0;
@@ -12,6 +12,7 @@ namespace AutoMiningScript
12
12
{
13
13
readonly MyIni ini;
14
14
public readonly string Id, FleetId, BaseId, Role;
15
public string BlockName => Role=="fleet"?L.CommonFleetBlockName:L.F(L.CommonNodeBlockName,Id);
15
16
public Settings(string source, string defaultRole, string defaultId)
16
17
{
17
18
ini = new MyIni(); MyIniParseResult result;
@@ -1,4 +1,6 @@
1
1
{
2
"CommonFleetBlockName": "XFEAMS System",
3
"CommonNodeBlockName": "XFEAMS-{0}",
2
4
"CommonReady": "{0} {1} / {2}: ready; diagnose first",
3
5
"CommonInitError": "INIT ERROR: {0}",
4
6
"CommonFixConfig": "INIT ERROR: {0}\nFix CustomData, then run init",
@@ -1,4 +1,6 @@
1
1
{
2
"CommonFleetBlockName": "XFEAMS系统",
3
"CommonNodeBlockName": "XFEAMS-{0}",
2
4
"CommonReady": "{0} {1} / {2} 已就绪;可运行“诊断”查看状态",
3
5
"CommonInitError": "初始化错误:{0}",
4
6
"CommonFixConfig": "初始化错误:{0}\n请修正自定义数据后运行“初始化”",
@@ -1,4 +1,5 @@
1
1
{
2
"MinerDockLockPending": "Magnetic capture; locking connector",
2
3
"MinerDockOuterEntry": "Flying to port entry",
3
4
"MinerDockOuterClearance": "Moving out to entry; holding attitude",
4
5
"MinerDockSyncPose": "Synchronizing at entry",
@@ -1,4 +1,5 @@
1
1
{
2
"MinerDockLockPending": "连接器已吸附,正在锁定连接",
2
3
"MinerDockOuterEntry": "前往对接口前方",
3
4
"MinerDockOuterClearance": "保持姿态退至外部入口平面",
4
5
"MinerDockSyncPose": "在引导点同步停靠姿态",
@@ -25,6 +25,8 @@ XFE AMS 是 Space Engineers 原版编程块采矿舰队系统:自主推进器
25
25
26
26
**中文版的自定义数据、运行参数和枚举值均支持中文,以下部署示例以中文版为准。** 新生成模板使用“采矿舰队”“主控”“采矿泊位”“基地货仓”等默认名称;旧英文配置和原英文命令继续兼容,已有 `CustomData` 不会被自动重写。参数及命令完整对照见 [中文参数手册](docs/PARAMETERS.zh-CN.md)。中英文混用时,必须手动统一舰队编号和主控编号;“采矿舰队”与 `mining`、“主控”与 `base` 是不同的真实身份,不会按含义自动合并。旧部署可以继续使用原来的英文 ID 和方块组名,不必为翻译而更名。
27
27
28
编程块加载有效配置后自动设置自身名称,重新编译时会同步更新。中文主控固定为 `XFEAMS系统`,英文主控固定为 `XFEAMS System`;矿机、标点终端及看门狗均使用 `XFEAMS-各自ID`,例如编号“深空工业3号”的矿机编程块叫 `XFEAMS-深空工业3号`。ID 使用配置原文,不翻译、不重复添加“矿机”等前缀。仅修改编程块名称,舰船名称、通信编号、任务和自定义数据保持原值;无需新增命名配置。
29
28
30
## 硬件与建造要求
29
31
30
32
每台矿机需要:
@@ -207,11 +209,11 @@ XFE AMS 是 Space Engineers 原版编程块采矿舰队系统:自主推进器
207
209
208
210
获得有效进港许可后,矿机直接前往接口前方的引导点,再连续进入对接;已经位于接口前方的矿机就近接入轴线。等待点只在没有可用许可时使用,取得许可后无需先飞到远处等待点、停稳,再返回接口。引导点距离由 `[对接] 接近距离` 控制(默认30米,至少两倍矿机半径),用于从侧面汇入。前往引导点的途中仍扫描避障,沿轴线到达入口时连续转入正式对接;已在入口以内的矿机就近接入,不向外绕行。近港重启返航会核对新许可后就近进港,不先飞回旧采矿航迹的远端。
209
211
210
“进港对接”全程按两个连接器的实际安装关系控制姿态,使开口相向、上方向一致;自动标定保留连接器中心的对接间距,修正人工停靠时的横向和角度偏差。**进入正式对接后关闭摄像机扫描、障碍判断和扫描限速,直接沿预约通道贴合**,并清除上一段的绕行目标与扫描等待。许可、遥测、动力与人工接管检查继续生效。诊断显示“直接对接:扫描与避障已关闭 / 指令速度”,不再把旧扫描净空显示为当前对接条件。
212
“进港对接”全程按两个连接器的实际安装关系控制姿态,使开口相向、上方向一致;自动标定保留连接器中心的对接间距,修正人工停靠时的横向和角度偏差。**进入正式对接后关闭摄像机扫描、障碍判断和扫描限速,直接沿预约通道贴合**,并清除上一段的绕行目标与扫描等待。尚未吸附时继续检查许可、遥测和动力;人工接管始终优先。诊断显示“直接对接:扫描与避障已关闭 / 指令速度”,不再把旧扫描净空显示为当前对接条件。
211
213
212
已标定的正常直线进港随剩余距离逐步减速,最后3米才采用 `最终接近速度`(默认0.2m/s)。首次尚未测得贴合间距时,在距连接器中心至少6米、且不小于两倍矿机半径的范围内先稳定低速,再沿接口轴线接近;游戏报告连接器可连接且姿态、相对速度合格时立即尝试锁定,不等待两个方块中心重合。连接状态由[官方连接器接口](https://keensoftwarehouse.github.io/SpaceEngineersModAPI/api/Sandbox.ModAPI.Ingame.IMyShipConnector.html)提供。
214
已标定的正常直线进港随剩余距离逐步减速,最后3米才采用 `最终接近速度`(默认0.2m/s)。首次尚未测得贴合间距时,在距连接器中心至少6米、且不小于两倍矿机半径的范围内先稳定低速,再沿接口轴线接近。返航、进港等候、对齐、贴合或接港退避时,游戏一旦报告连接器“可连接”,就立即清除推力与陀螺覆盖并尝试锁定;不再等待姿态误差、相对速度、稳定计时或进港遥测门槛,也不等待两个方块中心重合。连接状态由[官方连接器接口](https://keensoftwarehouse.github.io/SpaceEngineersModAPI/api/Sandbox.ModAPI.Ingame.IMyShipConnector.html)提供。
213
215
214
贴合需横向偏移小于0.15m、姿态误差小于1°、横向速度小于0.1m/s、角速度差小于0.1°/s,连续稳定2秒;可在接近途中累计。进入后采用较宽的退出门限(0.25m、2°、0.2m/s、0.2°/s),避免轻微波动反复重置2秒等待;实际连接仍核对严格对齐与相对速度。超过退出门限时暂停轴向接近、修正姿态后重新取得稳定窗口。两段进港状态都以连续60秒无进展触发退避,持续接近不会因为整段超过旧版4分钟限制而退出。
216
尚未进入可连接范围时,末段轴向接近需横向偏移小于0.15m、姿态误差小于1°、横向速度小于0.1m/s、角速度差小于0.1°/s,连续稳定2秒;可在接近途中累计。进入后采用较宽的退出门限(0.25m、2°、0.2m/s、0.2°/s),避免轻微波动反复重置2秒等待。超过退出门限时暂停轴向接近、修正姿态后重新取得稳定窗口;已可连接则直接锁定。真正连接成功后立即上报“卸货补能”并核对实际泊位;连接尚未完成时显示“连接器已吸附,正在锁定连接”,保持停止飞控修正并重试。主动暂停、人工接管及正常离港不会触发这项自动锁定。未吸附的两段进港状态都以连续60秒无进展触发退避,持续接近不会因为整段超过旧版4分钟限制而退出。
215
217
216
218
扫描以待检测区域为单位,按镜头安装位置、朝向、可用距离分配检测工作,把所有镜头的有效结果合并为一轮扫描。某个镜头扫到自身船体时,仅在当前检测点尝试其他镜头,不清空已有的新鲜净空,也不把这个镜头从其他检测点永久排除。例如前方三个镜头中间一个被挡住,左右镜头仍可共同完成扫描;中间镜头在能看见的其他区域仍会被使用。
217
219
@@ -399,6 +401,7 @@ XFE AMS 是 Space Engineers 原版编程块采矿舰队系统:自主推进器
399
401
| 到2.8米附近停住,姿态与偏移接近零 | 更新矿机脚本并从主控运行 `返航 矿机编号`。末端稳定窗口加入退出容差,避免角速度差轻微越过0.1°/s就反复刹停。诊断中的指令速度为0表示仍在调整或同步;指令速度非零而实际速度持续接近0时,应核对推进器实际输出、动力和船体接触情况 |
400
402
| 到接口附近又向外飞,或30多米就以0.1m/s挪动 | 更新矿机脚本并从主控运行 `返航 矿机编号`。新版有效许可下不强制绕回等待点,已在入口以内时就近进港;`最终接近速度` 只用于最后3米。直线航迹连续衔接,正式对接不再受扫描限制;无需重新手动标定 |
401
403
| 对接反复退出 | 母舰加速/旋转、遥测过期、泊位阻塞、连接器几何/钻尖标定错误 |
404
| 已被连接器吸住,仍显示进港对接 | 更新矿机脚本;新版在连接器报告可连接时直接锁定,成功后立即上报卸货补能,不再被姿态、速度或稳定计时拦住 |
402
405
| 自动探索未检出 | 可见表面扫描不代表地下矿床;检查试钻深度、采样有效性与目标矿种配置 |
403
406
| 旧矿机失联后任务不派出 | 保守保留未知占用;先找到失联矿机并安全回收,不能仅等待超时 |
404
407
| 改名后大屏保留旧矿机 | 身份取自 `[系统] 编号`;新身份上线、旧条目显示离线后,在主控运行 `清理离线矿机`。只清名单,旧任务和未知通道占用仍保留 |
@@ -8,6 +8,8 @@
8
8
9
9
编号、舰队编号、主控编号、真实方块名称及方块组名称属于用户数据,保持原文。中英文舰队混用时必须填写完全一致的舰队编号与主控编号;默认“采矿舰队”和 `mining` 不等价,默认“主控”和 `base` 不等价。不要直接改 ID 来迁移旧存档。
10
10
11
编程块初始化时自动命名:中文主控为 `XFEAMS系统`,英文主控为 `XFEAMS System`,其它类型为 `XFEAMS-` 加本节点的“编号”。更改编号并重新编译后,名称随生效配置更新;命名不改变通信身份,也没有额外的名称或角色参数。
12
11
13
硬件参数放在对应角色实际使用的节中;此表是名称对照,不表示每个参数能放入任意节。常用配置及部署步骤见 [README](../README.md)。默认 `[硬件] 自动标定=是`:硬件就绪的待机矿机自动请求接港,用实际连接器安装位置和朝向完成首次对接,真实连接后保存标定。设为 `否` 时,未标定矿机仍需人工接港并执行标定;主动暂停或交还控制不会被自动接港覆盖。`[飞行] 起飞质量=0` 自动测量;`[货物] 起飞载荷上限=0.05`。
12
14
13
15
中文版的两套默认组名互相兼容:找不到“采矿泊位”时查找 `AMS Docks`,找不到“基地货仓”时查找 `AMS Cargo`,反向同样适用。精确匹配的已有组优先,自定义组名不做替换;只收集选定组内的母舰本体方块,配置原文不变。
@@ -4,6 +4,12 @@
4
4
5
5
## 开发机检查
6
6
7
2026-09-14 编程块自动命名:配置解析成功后设置自身名称,重新编译及主控重置同样生效。中文主控使用 `XFEAMS系统`,英文主控使用 `XFEAMS System`,其它类型使用 `XFEAMS-` 加配置编号;命名格式来自两份共用语言资源。只写入本编程块的 `CustomName`,不更改配置编号、舰船名称或存档身份。
8
9
同轮修复吸附后仍校正姿态:返航、等候、对齐、贴合及接港退避时,`Connectable` 优先清除控制覆盖并执行 `Connect()`,不再用姿态、相对速度、稳定计时和进港遥测阻止锁定;真正连接成功才进入服务并立即发布遥测。锁定未完成时显示具体原因并重试,接错接口时保持连接、停止飞控并报告原有泊位错误;人工暂停、交还控制和正常离港不自动锁定。新增10项回归,125项返航、矿机状态及对接定向检查通过,日志为 `obj/magnetic-lock-targeted.log`。模拟连接器状态不等同于游戏内吸力实测,新增实机步骤见 S44。
10
11
最终执行 `./build.ps1 -NoRestore`:中英文各586/586项测试、416对资源及8项生成器检查通过;八份源工程和实际导出均编译0警告、0错误,导出角色检查通过。逐项核对了命名资源、文件字符数及SHA-256,均与 `dist/manifest.json` 一致且不超过100000字符。当前交付日志为 `obj/block-names-magnetic-lock-build.log`;仅命名修改的上一轮记录为 `obj/block-names-final-build.log`,已被本次合并产物替代。游戏内吸附锁定和方块命名验收尚未执行。
12
7
13
2026-09-14 本轮修复配置后自动接港。用户诊断显示主控有6台在线矿机、0个泊位、2台等待,3号“标定=否 / 等待泊位与外部等候点”。只读核对08:51存档:母舰实际组名为 `AMS Docks`、`AMS Cargo`,中文配置引用“采矿泊位”“基地货仓”;3/4号仍处于返航,其余4台已停靠。母舰共8个连接器,采矿组只管理6个,不能用接管全部连接器代替修复组名。原游戏存档未修改。
8
14
9
15
新增21项检查覆盖默认中英文组名兼容、自定义组名精确匹配、母舰网格过滤、缺失泊位原因回传与重扫恢复;主控暂停时为两台空中矿机分配不同泊位;初始自动返航恢复电池供电及储氢罐供气、暂停/人工接管持久化、返航各阶段重新获取许可、孔内沿原轴退出。首次未标定对接以实际控制输出积分,分别测试后置/侧置连接器、20/130米来向及1/2.8/5米实际中心间距;锁定后才测量保存标定。5米间距模型在修正首次低速窗口前两项失败,修复后通过。115项返航、矿机状态与对接定向检查通过,日志为 `obj/auto-home-targeted.log`。模型使用理想姿态、空旷相机和模拟连接器状态,不能替代真实碰撞、吸力、充能与多人联机验证。
@@ -60,14 +66,14 @@
60
66
61
67
| 本次导出产物 | 字符数 | 限额 |
62
68
| --- | ---: | ---: |
63
| `dist/zh-CN/AutoMiningScript/script.cs` | 95,329 | 100,000 |
64
| `dist/zh-CN/AutoMiningFleet/script.cs` | 94,402 | 100,000 |
65
| `dist/zh-CN/AutoMiningMarker/script.cs` | 38,055 | 100,000 |
66
| `dist/zh-CN/AutoMiningWatchdog/script.cs` | 35,368 | 100,000 |
67
| `dist/en-US/AutoMiningScript/script.cs` | 99,194 | 100,000 |
68
| `dist/en-US/AutoMiningFleet/script.cs` | 95,958 | 100,000 |
69
| `dist/en-US/AutoMiningMarker/script.cs` | 38,431 | 100,000 |
70
| `dist/en-US/AutoMiningWatchdog/script.cs` | 35,578 | 100,000 |
69
| `dist/zh-CN/AutoMiningScript/script.cs` | 95,488 | 100,000 |
70
| `dist/zh-CN/AutoMiningFleet/script.cs` | 94,531 | 100,000 |
71
| `dist/zh-CN/AutoMiningMarker/script.cs` | 38,166 | 100,000 |
72
| `dist/zh-CN/AutoMiningWatchdog/script.cs` | 35,480 | 100,000 |
73
| `dist/en-US/AutoMiningScript/script.cs` | 99,380 | 100,000 |
74
| `dist/en-US/AutoMiningFleet/script.cs` | 96,092 | 100,000 |
75
| `dist/en-US/AutoMiningMarker/script.cs` | 38,547 | 100,000 |
76
| `dist/en-US/AutoMiningWatchdog/script.cs` | 35,694 | 100,000 |
71
77
72
78
已核对八份文件的 SHA-256 与 `dist/manifest.json` 一致。中文产物包含中文字串,英文产物没有中文字串;每份脚本只包含对应的一种语言标识。后续修改应以重新执行构建产生的清单和测试报告为准。
73
79
@@ -152,6 +158,7 @@
152
158
| S41 | 在入口前耗尽相机充能超过60s,随后恢复;从旧靠泊故障下达全部返航 | 入口等待不累计三次靠泊失败,只有主控核实停稳后才释放通道;新返航重置旧计数;真实贴合无进展仍退避并保留故障保护 | 未执行 |
153
159
| S42 | 新矿机在空中加载正确配置,分别使用后置/侧置连接器与不同大小接口,主控保持暂停 | 无需单机初始化或手工标定,自动请求泊位、进港及锁定;首次慢速窗口在实际接触前取得,真正连接后才显示已标定并保存测量值 | 未执行 |
154
160
| S43 | 在返航、等候、进港对齐、贴合及退避中重新编译,再对照主动暂停和交还控制 | 原返航自动重新获取母舰遥测和许可,不重复下令;不使用旧目标直接施加推力;暂停/人工接管持续生效;孔内恢复始终先沿原轴退钻 | 未执行 |
161
| S44 | 返航时进入连接器吸附范围,保留轻微姿态/角速度偏差并让泊位遥测过期;再测试锁定延迟、正常离港和人工接管 | 报告可连接即尝试锁定,清除飞控覆盖;真正连接后立即上报卸货补能。延迟不假报成功或继续推挤;正常离港不重新锁定,暂停/人工接管仍由用户控制 | 未执行 |
155
162
156
163
## 探索与手动采矿验收
157
164
@@ -211,6 +218,7 @@
211
218
| F36 | 多机并行预约中保存恢复,母舰缓慢平移旋转,再恢复原矿机请求 | 各泊位占用都保留,旧token失效;新鲜请求按当前母舰位姿续约,不能从默认世界原点生成旧接近路线 | 未执行 |
212
219
| F37 | 中文默认配置配合原 `AMS Docks`/`AMS Cargo` 组,6台矿机中4台停靠、2台返航,母舰另有其它用途连接器 | 识别采矿组内6个泊位和母舰库存,不接管其它接口或矿机库存;主控暂停仍发接港许可,独立通道可同时接收两台返航矿机 | 未执行 |
213
220
| F38 | 输入不存在的自定义泊位/货仓组名,再修正配置并重新编译;对照只增减组内方块后重扫 | 主控分别说明缺失字段及名称,矿机显示缺失泊位的原因;修复后无需清空存档即可重新请求接港;自定义组不被默认组替换 | 未执行 |
221
| F39 | 分别加载中英文四种脚本,检查编程块名称;保留原编号重新编译,并在主控运行初始化 | 中文主控名为 `XFEAMS系统`、英文主控为 `XFEAMS System`,矿机/标点/看门狗名为 `XFEAMS-各自ID`;配置编号及舰船名称不受命名影响,主控初始化仍按原有规则清档 | 未执行 |
214
222
215
223
## 双语和编程块自带屏验收
216
224