using System;
using System.Linq;
using System.Reflection;
using Sandbox.ModAPI.Ingame;
using VRage.Game.ModAPI.Ingame.Utilities;
using VRageMath;
using Xunit;
using P=AutoMiningScript.Program;
namespace AutoMiningScript.Tests
{
public class FleetTrafficTests
{
static FleetStateTests.Rig Arrivals(int count)
{
var r=new FleetStateTests.Rig(count,false);
foreach(var t in r.Miners){t.State=P.FlightState.Holding;t.Position+=Vector3D.Forward*100;}
r.RegisterAll();r.Tick(30);return r;
}
static void FreshTicks(FleetStateTests.Rig r,int count)
{for(int n=0;n<count;n++){if(n%5==0)foreach(var t in r.Miners)r.Receive("TELEMETRY",t);r.Tick(1);}}
[Theory]
[InlineData(4)]
[InlineData(16)]
public void IndependentParallelBerthsCanRemainGrantedAtTheSameTime(int count)
{
var r=Arrivals(count);foreach(var t in r.Miners)r.Receive("DOCK_REQUEST",t);FreshTicks(r,count*12);
var grants=r.Sent.Where(p=>p.Kind=="DOCK_GRANT").Select(p=>P.DockFrame.FromIni(p.Body)).ToList();
Assert.Equal(count,grants.Count);Assert.Equal(count,grants.Select(g=>g.ConnectorId).Distinct().Count());
Assert.Equal(count,grants.Select(g=>g.Token).Distinct().Count());Assert.Equal(count,P.Data.ReadLong(r.Save(),"fleet","LaneCount"));
}
[Fact]
public void OverlappingPortCorridorsSerializeAndDoNotExpireAnUnknownShip()
{
var r=Arrivals(2);r.SetPort(1,Vector3D.Right*4,Vector3D.Forward);r.Miners[1].Position=new Vector3D(4,0,-130);
FreshTicks(r,30);foreach(var t in r.Miners)r.Receive("DOCK_REQUEST",t);FreshTicks(r,30);
var first=Assert.Single(r.Sent,p=>p.Kind=="DOCK_GRANT");r.Sent.Clear();r.Tick(1000);
int other=first.Target==r.Miners[0].Id?1:0;r.Receive("DOCK_REQUEST",r.Miners[other]);r.Tick(20);
Assert.DoesNotContain(r.Sent,p=>p.Kind=="DOCK_GRANT");Assert.Equal(1,P.Data.ReadLong(r.Save(),"fleet","LaneCount"));
}
[Fact]
public void ArrivalAndDepartureOnSeparateCorridorsRunTogether()
{
var r=new FleetStateTests.Rig(2,true);r.SetConnected(1,false);r.Miners[1].Position=new Vector3D(40,0,-100);r.Miners[1].State=P.FlightState.Holding;
r.RegisterAll();r.Fleet.Command("mark 6 6 20 GPS:entry:0:0:-200: GPS:inward:0:0:-201:");r.Fleet.Command("start");FreshTicks(r,30);
r.Miners[0].JobId=r.Fleet.GetJobs().Single().Id;r.Receive("DEPART_REQUEST",r.Miners[0]);r.Receive("DOCK_REQUEST",r.Miners[1]);FreshTicks(r,30);
Assert.Single(r.Sent,p=>p.Kind=="DOCK_GRANT");Assert.Single(r.Sent,p=>p.Kind=="DEPART_GRANT");Assert.Equal(2,P.Data.ReadLong(r.Save(),"fleet","LaneCount"));
}
[Fact]
public void StoppedFaultAwayFromThePortReleasesItsLaneAndItsRemoteTask()
{
var r=Arrivals(2);r.Receive("DOCK_REQUEST",r.Miners[0]);FreshTicks(r,15);
r.Fleet.Command("mark 6 6 20 GPS:entry:0:0:-300: GPS:inward:0:0:-301:");var job=r.Fleet.GetJobs().Single();r.Planner.SetOwner(job,r.Miners[0].Id);
r.Miners[0].State=P.FlightState.Fault;r.Miners[0].Position=new Vector3D(-50,0,-100);r.Receive("TELEMETRY",r.Miners[0]);
r.Receive("DOCK_REQUEST",r.Miners[1]);FreshTicks(r,20);
Assert.Equal("",job.Owner);Assert.Equal(1,P.Data.ReadLong(r.Save(),"fleet","LaneCount"));
Assert.Equal(r.Miners[1].Id,P.Data.Text(r.Save(),"fleet","LaneOwner"));Assert.Contains(r.Sent,p=>p.Kind=="DOCK_GRANT" && p.Target==r.Miners[1].Id);
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void FaultAtAContactOnlyBlocksTheAffectedCorridor(bool stale)
{
var r=Arrivals(2);r.Receive("DOCK_REQUEST",r.Miners[0]);FreshTicks(r,15);
r.Miners[0].State=P.FlightState.Fault;r.Miners[0].Position=Vector3D.Forward*20;r.Receive("TELEMETRY",r.Miners[0]);
if(stale)r.Program.Now+=10;else r.Tick(12);
r.Receive("DOCK_REQUEST",r.Miners[1]);r.Tick(12);
var second=Assert.Single(r.Sent,p=>p.Kind=="DOCK_GRANT" && p.Target==r.Miners[1].Id);
Assert.Equal(101,P.DockFrame.FromIni(second.Body).ConnectorId);
Assert.Equal(stale?2:1,P.Data.ReadLong(r.Save(),"fleet","LaneCount"));
}
[Fact]
public void DockRetreatReleaseAcceptsAStoppedShipBeforeTheFarWaitingPoint()
{
var r=Arrivals(1);r.Receive("DOCK_REQUEST",r.Miners[0]);FreshTicks(r,15);var grant=P.DockFrame.FromIni(r.Sent.First(p=>p.Kind=="DOCK_GRANT").Body);
r.Miners[0].Position=Vector3D.Forward*35;r.Miners[0].State=P.FlightState.DockRetreat;r.Receive("TELEMETRY",r.Miners[0]);
var body=new MyIni();body.Set("lease","Token",grant.Token);r.Fleet.Receive(new P.Packet {Kind="LANE_RELEASE",From=r.Miners[0].Id,Source=5000,Body=body});
Assert.Equal(0,P.Data.ReadLong(r.Save(),"fleet","LaneCount"));
}
[Fact]
public void MultipleUnknownLanesSurviveSaveRemovalAndReloadWithTokensRevoked()
{
var r=Arrivals(2);r.Checkpoint();foreach(var t in r.Miners)r.Receive("DOCK_REQUEST",t);FreshTicks(r,30);Assert.Equal(2,P.Data.ReadLong(r.Save(),"fleet","LaneCount"));
r.Program.Now+=10;r.Fleet.Command("clear-offline");var after=new FleetStateTests.Rig(2,false);after.Restore(r.Program.Storage);
Assert.Empty(after.Fleet.GetTelemetry());var saved=after.Save();Assert.Equal(2,P.Data.ReadLong(saved,"fleet","LaneCount"));
Assert.Empty(P.Data.Text(saved,"fleet","LaneToken"));Assert.Empty(P.Data.Text(saved,"fleetLane1","LaneToken"));
Assert.Equal(2,P.Data.ReadLong(saved,"fleet","ProtectedOwnerCount"));
}
static P.Packet Traffic(FleetStateTests.Rig r,int index)
{
typeof(P.FleetController).GetMethod("SendTraffic",BindingFlags.NonPublic|BindingFlags.Instance).Invoke(r.Fleet,new object[]{r.Fleet.GetTelemetry().Single(t=>t.Id==r.Miners[index].Id)});
return r.Sent.Last(p=>p.Kind=="TRAFFIC" && p.Target==r.Miners[index].Id);
}
[Fact]
public void CrossingCruisePathsHaveDeterministicYieldPriority()
{
var r=Arrivals(2);var a=r.Miners[0];var b=r.Miners[1];a.State=b.State=P.FlightState.Transit;a.HasRoute=b.HasRoute=true;
a.Position=Vector3D.Left*20;a.RouteTarget=Vector3D.Right*20;a.Velocity=Vector3D.Right*3;
b.Position=Vector3D.Forward*20;b.RouteTarget=Vector3D.Backward*20;b.Velocity=Vector3D.Backward*3;
a.ThrustMargin=b.ThrustMargin=2;r.Receive("TELEMETRY",a);r.Receive("TELEMETRY",b);
Assert.False(P.Data.Flag(Traffic(r,0).Body,"traffic","Wait"));Assert.True(P.Data.Flag(Traffic(r,1).Body,"traffic","Wait"));
}
[Fact]
public void StationaryFaultOnTheCruiseLineCreatesACameraCheckedDetour()
{
var r=Arrivals(2);var a=r.Miners[0];var b=r.Miners[1];a.State=P.FlightState.Transit;a.HasRoute=true;
a.Position=Vector3D.Forward*100;a.RouteTarget=Vector3D.Zero;a.ThrustMargin=2;b.Position=Vector3D.Forward*80;b.State=P.FlightState.Fault;
r.Receive("TELEMETRY",a);r.Receive("TELEMETRY",b);var body=Traffic(r,0).Body;
Assert.True(P.Data.Flag(body,"traffic","Avoid"));Assert.False(P.Data.Flag(body,"traffic","Wait"));
var point=P.Data.ReadVector(body,"traffic","Point",Vector3D.Zero);Assert.True(Vector3D.Distance(point,b.Position)>a.Radius+b.Radius+4);
}
[Fact]
public void StaleFaultPositionCannotAuthorizeAnUnverifiedDetour()
{
var r=Arrivals(2);var a=r.Miners[0];var b=r.Miners[1];a.State=P.FlightState.Transit;a.HasRoute=true;
a.Position=Vector3D.Forward*100;a.RouteTarget=Vector3D.Zero;b.Position=Vector3D.Forward*80;b.State=P.FlightState.Fault;r.Receive("TELEMETRY",b);
r.Program.Now+=5;r.Receive("TELEMETRY",a);var body=Traffic(r,0).Body;Assert.True(P.Data.Flag(body,"traffic","Wait"));Assert.False(P.Data.Flag(body,"traffic","Avoid"));
}
[Fact]
public void AStoppedFaultStillProtectsItsPointAfterOfflineRegistryRemoval()
{
var r=Arrivals(2);r.Checkpoint();r.Miners[0].State=P.FlightState.Fault;r.Miners[0].Position=Vector3D.Forward*20;
r.Receive("TELEMETRY",r.Miners[0]);r.Tick(12);r.Program.Now+=10;r.Receive("TELEMETRY",r.Miners[1]);r.Fleet.Command("clear-offline");
var after=new FleetStateTests.Rig(2,false);after.Restore(r.Program.Storage);
Assert.Equal(1,P.Data.ReadLong(after.Save(),"fleet","ClearanceCount"));Assert.Equal(1,P.Data.ReadLong(after.Save(),"fleet","ProtectedOwnerCount"));
}
[Fact]
public void FrontShipDoesNotYieldToItsFollower()
{
var r=Arrivals(2);var rear=r.Miners[0];var front=r.Miners[1];rear.HasRoute=front.HasRoute=true;rear.State=front.State=P.FlightState.Transit;
rear.Position=Vector3D.Forward*80;front.Position=Vector3D.Forward*60;rear.RouteTarget=front.RouteTarget=Vector3D.Zero;
rear.Velocity=front.Velocity=Vector3D.Backward*3;r.Receive("TELEMETRY",rear);r.Receive("TELEMETRY",front);
Assert.False(P.Data.Flag(Traffic(r,1).Body,"traffic","Wait"));
}
sealed class MovingShip
{
public readonly ReturnNavigationTests.Rig R=new ReturnNavigationTests.Rig();
public Vector3D Position,Velocity,Target;
public bool Arrived;
public MovingShip(Vector3D start,Vector3D target)
{
Position=start;Target=target;R.Orientation=MatrixD.CreateWorld(start,Vector3D.Normalize(target-start),Vector3D.Up);
R.Controller.Method("GetPosition",a=>Position).Method("GetShipVelocities",a=>new MyShipVelocities(Velocity,Vector3D.Zero));
R.Camera.Method("GetPosition",a=>Position).Method("CanScan",a=>true);
}
public void Step(double now)
{
R.Program.Now=now;Arrived=R.Flight.Move(Target,Vector3D.Zero,Vector3D.Normalize(Target-Position),Vector3D.Up,5,.5,true,.05,true);
Vector3D force=Vector3D.Zero;foreach(var thrust in R.Ship.Thrusters)force+=thrust.WorldMatrix.Backward*thrust.ThrustOverridePercentage*thrust.MaxEffectiveThrust;
Velocity+=force/1000*.05;Position+=Velocity*.05;
var desired=(MatrixD)typeof(P.FlightController).GetField("previousDesired",BindingFlags.NonPublic|BindingFlags.Instance).GetValue(R.Flight);
R.Orientation=MatrixD.CreateWorld(Position,desired.Forward,desired.Up);
}
}
[Fact]
public void CrossingShipsBothReachTheirGoalsWithPhysicalSeparationInTheControlModel()
{
var r=Arrivals(2);var ships=new[]{new MovingShip(Vector3D.Left*40,Vector3D.Right*40),new MovingShip(Vector3D.Forward*40,Vector3D.Backward*40)};
double separation=double.MaxValue;bool yielded=false;
for(int n=0;n<2000 && ships.Any(s=>!s.Arrived);n++)
{
r.Program.Now+=.05;
if(n%10==0)
{
for(int i=0;i<2;i++)
{
var t=r.Miners[i];var s=ships[i];t.State=P.FlightState.Transit;t.Position=s.Position;t.Velocity=s.Velocity;t.Radius=2;t.ThrustMargin=100;
t.HasRoute=!s.Arrived;t.RouteTarget=s.R.Flight.HasRoute?s.R.Flight.RouteTarget:s.Target;r.Receive("TELEMETRY",t);
}
for(int i=0;i<2;i++)if(!ships[i].Arrived)
{
var body=Traffic(r,i).Body;var f=ships[i].R.Flight;f.TrafficWait=P.Data.Flag(body,"traffic","Wait");f.TrafficAvoid=P.Data.Flag(body,"traffic","Avoid");
f.TrafficPoint=P.Data.ReadVector(body,"traffic","Point",Vector3D.Zero);f.TrafficAt=r.Program.Now;yielded|=f.TrafficWait;
}
}
foreach(var s in ships)s.Step(r.Program.Now);
separation=Math.Min(separation,Vector3D.Distance(ships[0].Position,ships[1].Position));
}
Assert.True(yielded);Assert.True(ships.All(s=>s.Arrived));Assert.True(separation>=5,"Minimum separation: "+separation);
}
[Fact]
public void FinalDockingDoesNotUseAnOldTrafficWaitOrDetour()
{
var r=new ReturnNavigationTests.Rig();r.Flight.TrafficWait=r.Flight.TrafficAvoid=true;r.Flight.TrafficPoint=Vector3D.Right*100;r.Flight.TrafficAt=0;
r.Flight.Move(Vector3D.Forward*3,Vector3D.Zero,Vector3D.Forward,Vector3D.Up,.2,.03,false,.05,false,false);
Assert.True(r.Flight.CommandSpeed>0);Assert.Equal(0,r.Rays);Assert.Equal(Vector3D.Forward*3,r.Flight.RouteTarget);
}
}
}
using System;
using System.Linq;
using System.Reflection;
using Sandbox.ModAPI.Ingame;
using VRage.Game.ModAPI.Ingame.Utilities;
using VRageMath;
using Xunit;
using P=AutoMiningScript.Program;
namespace AutoMiningScript.Tests
{
public class FleetTrafficTests
{
static FleetStateTests.Rig Arrivals(int count)
{
var r=new FleetStateTests.Rig(count,false);
foreach(var t in r.Miners){t.State=P.FlightState.Holding;t.Position+=Vector3D.Forward*100;}
r.RegisterAll();r.Tick(30);return r;
}
static void FreshTicks(FleetStateTests.Rig r,int count)
{for(int n=0;n<count;n++){if(n%5==0)foreach(var t in r.Miners)r.Receive("TELEMETRY",t);r.Tick(1);}}
[Theory]
[InlineData(4)]
[InlineData(16)]
public void IndependentParallelBerthsCanRemainGrantedAtTheSameTime(int count)
{
var r=Arrivals(count);foreach(var t in r.Miners)r.Receive("DOCK_REQUEST",t);FreshTicks(r,count*12);
var grants=r.Sent.Where(p=>p.Kind=="DOCK_GRANT").Select(p=>P.DockFrame.FromIni(p.Body)).ToList();
Assert.Equal(count,grants.Count);Assert.Equal(count,grants.Select(g=>g.ConnectorId).Distinct().Count());
Assert.Equal(count,grants.Select(g=>g.Token).Distinct().Count());Assert.Equal(count,P.Data.ReadLong(r.Save(),"fleet","LaneCount"));
}
[Fact]
public void OverlappingPortCorridorsSerializeAndDoNotExpireAnUnknownShip()
{
var r=Arrivals(2);r.SetPort(1,Vector3D.Right*4,Vector3D.Forward);r.Miners[1].Position=new Vector3D(4,0,-130);
FreshTicks(r,30);foreach(var t in r.Miners)r.Receive("DOCK_REQUEST",t);FreshTicks(r,30);
var first=Assert.Single(r.Sent,p=>p.Kind=="DOCK_GRANT");r.Sent.Clear();r.Tick(1000);
int other=first.Target==r.Miners[0].Id?1:0;r.Receive("DOCK_REQUEST",r.Miners[other]);r.Tick(20);
Assert.DoesNotContain(r.Sent,p=>p.Kind=="DOCK_GRANT");Assert.Equal(1,P.Data.ReadLong(r.Save(),"fleet","LaneCount"));
}
[Fact]
public void ArrivalAndDepartureOnSeparateCorridorsRunTogether()
{
var r=new FleetStateTests.Rig(2,true);r.SetConnected(1,false);r.Miners[1].Position=new Vector3D(40,0,-100);r.Miners[1].State=P.FlightState.Holding;
r.RegisterAll();r.Fleet.Command("mark 6 6 20 GPS:entry:0:0:-200: GPS:inward:0:0:-201:");r.Fleet.Command("start");FreshTicks(r,30);
r.Miners[0].JobId=r.Fleet.GetJobs().Single().Id;r.Receive("DEPART_REQUEST",r.Miners[0]);r.Receive("DOCK_REQUEST",r.Miners[1]);FreshTicks(r,30);
Assert.Single(r.Sent,p=>p.Kind=="DOCK_GRANT");Assert.Single(r.Sent,p=>p.Kind=="DEPART_GRANT");Assert.Equal(2,P.Data.ReadLong(r.Save(),"fleet","LaneCount"));
}
[Fact]
public void StoppedFaultAwayFromThePortReleasesItsLaneAndItsRemoteTask()
{
var r=Arrivals(2);r.Receive("DOCK_REQUEST",r.Miners[0]);FreshTicks(r,15);
r.Fleet.Command("mark 6 6 20 GPS:entry:0:0:-300: GPS:inward:0:0:-301:");var job=r.Fleet.GetJobs().Single();r.Planner.SetOwner(job,r.Miners[0].Id);
r.Miners[0].State=P.FlightState.Fault;r.Miners[0].Position=new Vector3D(-50,0,-100);r.Receive("TELEMETRY",r.Miners[0]);
r.Receive("DOCK_REQUEST",r.Miners[1]);FreshTicks(r,20);
Assert.Equal("",job.Owner);Assert.Equal(1,P.Data.ReadLong(r.Save(),"fleet","LaneCount"));
Assert.Equal(r.Miners[1].Id,P.Data.Text(r.Save(),"fleet","LaneOwner"));Assert.Contains(r.Sent,p=>p.Kind=="DOCK_GRANT" && p.Target==r.Miners[1].Id);
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void FaultAtAContactOnlyBlocksTheAffectedCorridor(bool stale)
{
var r=Arrivals(2);r.Receive("DOCK_REQUEST",r.Miners[0]);FreshTicks(r,15);
r.Miners[0].State=P.FlightState.Fault;r.Miners[0].Position=Vector3D.Forward*20;r.Receive("TELEMETRY",r.Miners[0]);
if(stale)r.Program.Now+=10;else r.Tick(12);
r.Receive("DOCK_REQUEST",r.Miners[1]);r.Tick(12);
var second=Assert.Single(r.Sent,p=>p.Kind=="DOCK_GRANT" && p.Target==r.Miners[1].Id);
Assert.Equal(101,P.DockFrame.FromIni(second.Body).ConnectorId);
Assert.Equal(stale?2:1,P.Data.ReadLong(r.Save(),"fleet","LaneCount"));
}
[Fact]
public void DockRetreatReleaseAcceptsAStoppedShipBeforeTheFarWaitingPoint()
{
var r=Arrivals(1);r.Receive("DOCK_REQUEST",r.Miners[0]);FreshTicks(r,15);var grant=P.DockFrame.FromIni(r.Sent.First(p=>p.Kind=="DOCK_GRANT").Body);
r.Miners[0].Position=Vector3D.Forward*35;r.Miners[0].State=P.FlightState.DockRetreat;r.Receive("TELEMETRY",r.Miners[0]);
var body=new MyIni();body.Set("lease","Token",grant.Token);r.Fleet.Receive(new P.Packet {Kind="LANE_RELEASE",From=r.Miners[0].Id,Source=5000,Body=body});
Assert.Equal(0,P.Data.ReadLong(r.Save(),"fleet","LaneCount"));
}
[Fact]
public void MultipleUnknownLanesSurviveSaveRemovalAndReloadWithTokensRevoked()
{
var r=Arrivals(2);r.Checkpoint();foreach(var t in r.Miners)r.Receive("DOCK_REQUEST",t);FreshTicks(r,30);Assert.Equal(2,P.Data.ReadLong(r.Save(),"fleet","LaneCount"));
r.Program.Now+=10;r.Fleet.Command("clear-offline");var after=new FleetStateTests.Rig(2,false);after.Restore(r.Program.Storage);
Assert.Empty(after.Fleet.GetTelemetry());var saved=after.Save();Assert.Equal(2,P.Data.ReadLong(saved,"fleet","LaneCount"));
Assert.Empty(P.Data.Text(saved,"fleet","LaneToken"));Assert.Empty(P.Data.Text(saved,"fleetLane1","LaneToken"));
Assert.Equal(2,P.Data.ReadLong(saved,"fleet","ProtectedOwnerCount"));
}
static P.Packet Traffic(FleetStateTests.Rig r,int index)
{
typeof(P.FleetController).GetMethod("SendTraffic",BindingFlags.NonPublic|BindingFlags.Instance).Invoke(r.Fleet,new object[]{r.Fleet.GetTelemetry().Single(t=>t.Id==r.Miners[index].Id)});
return r.Sent.Last(p=>p.Kind=="TRAFFIC" && p.Target==r.Miners[index].Id);
}
[Fact]
public void CrossingCruisePathsHaveDeterministicYieldPriority()
{
var r=Arrivals(2);var a=r.Miners[0];var b=r.Miners[1];a.State=b.State=P.FlightState.Transit;a.HasRoute=b.HasRoute=true;
a.Position=Vector3D.Left*20;a.RouteTarget=Vector3D.Right*20;a.Velocity=Vector3D.Right*3;
b.Position=Vector3D.Forward*20;b.RouteTarget=Vector3D.Backward*20;b.Velocity=Vector3D.Backward*3;
a.ThrustMargin=b.ThrustMargin=2;r.Receive("TELEMETRY",a);r.Receive("TELEMETRY",b);
Assert.False(P.Data.Flag(Traffic(r,0).Body,"traffic","Wait"));Assert.True(P.Data.Flag(Traffic(r,1).Body,"traffic","Wait"));
}
[Fact]
public void StationaryFaultOnTheCruiseLineCreatesACameraCheckedDetour()
{
var r=Arrivals(2);var a=r.Miners[0];var b=r.Miners[1];a.State=P.FlightState.Transit;a.HasRoute=true;
a.Position=Vector3D.Forward*100;a.RouteTarget=Vector3D.Zero;a.ThrustMargin=2;b.Position=Vector3D.Forward*80;b.State=P.FlightState.Fault;
r.Receive("TELEMETRY",a);r.Receive("TELEMETRY",b);var body=Traffic(r,0).Body;
Assert.True(P.Data.Flag(body,"traffic","Avoid"));Assert.False(P.Data.Flag(body,"traffic","Wait"));
var point=P.Data.ReadVector(body,"traffic","Point",Vector3D.Zero);Assert.True(Vector3D.Distance(point,b.Position)>a.Radius+b.Radius+4);
}
[Fact]
public void StaleFaultPositionCannotAuthorizeAnUnverifiedDetour()
{
var r=Arrivals(2);var a=r.Miners[0];var b=r.Miners[1];a.State=P.FlightState.Transit;a.HasRoute=true;
a.Position=Vector3D.Forward*100;a.RouteTarget=Vector3D.Zero;b.Position=Vector3D.Forward*80;b.State=P.FlightState.Fault;r.Receive("TELEMETRY",b);
r.Program.Now+=5;r.Receive("TELEMETRY",a);var body=Traffic(r,0).Body;Assert.True(P.Data.Flag(body,"traffic","Wait"));Assert.False(P.Data.Flag(body,"traffic","Avoid"));
}
[Fact]
public void AStoppedFaultStillProtectsItsPointAfterOfflineRegistryRemoval()
{
var r=Arrivals(2);r.Checkpoint();r.Miners[0].State=P.FlightState.Fault;r.Miners[0].Position=Vector3D.Forward*20;
r.Receive("TELEMETRY",r.Miners[0]);r.Tick(12);r.Program.Now+=10;r.Receive("TELEMETRY",r.Miners[1]);r.Fleet.Command("clear-offline");
var after=new FleetStateTests.Rig(2,false);after.Restore(r.Program.Storage);
Assert.Equal(1,P.Data.ReadLong(after.Save(),"fleet","ClearanceCount"));Assert.Equal(1,P.Data.ReadLong(after.Save(),"fleet","ProtectedOwnerCount"));
}
[Fact]
public void FrontShipDoesNotYieldToItsFollower()
{
var r=Arrivals(2);var rear=r.Miners[0];var front=r.Miners[1];rear.HasRoute=front.HasRoute=true;rear.State=front.State=P.FlightState.Transit;
rear.Position=Vector3D.Forward*80;front.Position=Vector3D.Forward*60;rear.RouteTarget=front.RouteTarget=Vector3D.Zero;
rear.Velocity=front.Velocity=Vector3D.Backward*3;r.Receive("TELEMETRY",rear);r.Receive("TELEMETRY",front);
Assert.False(P.Data.Flag(Traffic(r,1).Body,"traffic","Wait"));
}
sealed class MovingShip
{
public readonly ReturnNavigationTests.Rig R=new ReturnNavigationTests.Rig();
public Vector3D Position,Velocity,Target;
public bool Arrived;
public MovingShip(Vector3D start,Vector3D target)
{
Position=start;Target=target;R.Orientation=MatrixD.CreateWorld(start,Vector3D.Normalize(target-start),Vector3D.Up);
R.Controller.Method("GetPosition",a=>Position).Method("GetShipVelocities",a=>new MyShipVelocities(Velocity,Vector3D.Zero));
R.Camera.Method("GetPosition",a=>Position).Method("CanScan",a=>true);
}
public void Step(double now)
{
R.Program.Now=now;Arrived=R.Flight.Move(Target,Vector3D.Zero,Vector3D.Normalize(Target-Position),Vector3D.Up,5,.5,true,.05,true);
Vector3D force=Vector3D.Zero;foreach(var thrust in R.Ship.Thrusters)force+=thrust.WorldMatrix.Backward*thrust.ThrustOverridePercentage*thrust.MaxEffectiveThrust;
Velocity+=force/1000*.05;Position+=Velocity*.05;
var desired=(MatrixD)typeof(P.FlightController).GetField("previousDesired",BindingFlags.NonPublic|BindingFlags.Instance).GetValue(R.Flight);
R.Orientation=MatrixD.CreateWorld(Position,desired.Forward,desired.Up);
}
}
[Fact]
public void CrossingShipsBothReachTheirGoalsWithPhysicalSeparationInTheControlModel()
{
var r=Arrivals(2);var ships=new[]{new MovingShip(Vector3D.Left*40,Vector3D.Right*40),new MovingShip(Vector3D.Forward*40,Vector3D.Backward*40)};
double separation=double.MaxValue;bool yielded=false;
for(int n=0;n<2000 && ships.Any(s=>!s.Arrived);n++)
{
r.Program.Now+=.05;
if(n%10==0)
{
for(int i=0;i<2;i++)
{
var t=r.Miners[i];var s=ships[i];t.State=P.FlightState.Transit;t.Position=s.Position;t.Velocity=s.Velocity;t.Radius=2;t.ThrustMargin=100;
t.HasRoute=!s.Arrived;t.RouteTarget=s.R.Flight.HasRoute?s.R.Flight.RouteTarget:s.Target;r.Receive("TELEMETRY",t);
}
for(int i=0;i<2;i++)if(!ships[i].Arrived)
{
var body=Traffic(r,i).Body;var f=ships[i].R.Flight;f.TrafficWait=P.Data.Flag(body,"traffic","Wait");f.TrafficAvoid=P.Data.Flag(body,"traffic","Avoid");
f.TrafficPoint=P.Data.ReadVector(body,"traffic","Point",Vector3D.Zero);f.TrafficAt=r.Program.Now;yielded|=f.TrafficWait;
}
}
foreach(var s in ships)s.Step(r.Program.Now);
separation=Math.Min(separation,Vector3D.Distance(ships[0].Position,ships[1].Position));
}
Assert.True(yielded);Assert.True(ships.All(s=>s.Arrived));Assert.True(separation>=5,"Minimum separation: "+separation);
}
[Fact]
public void FinalDockingDoesNotUseAnOldTrafficWaitOrDetour()
{
var r=new ReturnNavigationTests.Rig();r.Flight.TrafficWait=r.Flight.TrafficAvoid=true;r.Flight.TrafficPoint=Vector3D.Right*100;r.Flight.TrafficAt=0;
r.Flight.Move(Vector3D.Forward*3,Vector3D.Zero,Vector3D.Forward,Vector3D.Up,.2,.03,false,.05,false,false);
Assert.True(r.Flight.CommandSpeed>0);Assert.Equal(0,r.Rays);Assert.Equal(Vector3D.Forward*3,r.Flight.RouteTarget);
}
}
}