using System.Diagnostics;
using System.Text.Json;
using XFEEDToolBox.Core.Data;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Native;
using XFEEDToolBox.Core.Navigation;
internal static class LocalSteeringAcceptance
{
public static async Task RunAsync(int pid,string exactName,string reportPath)
{
if(File.Exists(reportPath)||File.Exists(reportPath+".jsonl"))throw new IOException("Use a new report path.");
var settings=ToolboxSettings.Load(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox","settings.json"));
await using var journal=new JournalMonitor(settings.JournalDirectory);await journal.StartAsync();var raw=new NavigationPipeClient(pid);
var hash=(await raw.CallAsync(new{op="describe"})).GetProperty("gameSha256").GetString()!;
using var source=new NativeCurrentSystemSource(pid,hash,()=>journal.State);var snapshot=await source.ReadLocationsAsync();
var row=snapshot.Locations.Single(x=>x.Name.Equals(exactName,StringComparison.Ordinal));
var target=new SystemNavigationTarget(snapshot.SystemAddress,row.WorldAddress,row.Name,row.MarketId is >0?SystemTargetKind.Station:SystemTargetKind.Orbit,row.MarketId)
{NativeKey=row.Key,NativeKind=row.NativeKind,NativeIdentity=row.Identity};
var driver=new NativeSystemFlightControlDriver(pid,hash,()=>journal.State);
await using var log=new StreamWriter(new FileStream(reportPath+".jsonl",FileMode.CreateNew,FileAccess.Write,FileShare.Read));
var start=DateTimeOffset.UtcNow;var clock=Stopwatch.StartNew();var reached=false;var returned=false;var released=false;var inputs=0;double maxSpeed=0,maxDisplacement=0,maxHeat=0;
string? failure=null;
try
{
using var timeout=new CancellationTokenSource(TimeSpan.FromSeconds(40));await driver.SelectAsync(target,timeout.Token);
var first=await driver.ReadSystemFlightAsync(timeout.Token);
if(first.Mode!=LocalFlightMode.Normal||first.Speed>=1||first.HeatPercent>=60||first.PlayerOverride||first.ExternalThreat)throw new InvalidOperationException("Local steering acceptance requires a stationary safe ship in normal space.");
var original=first.Forward;var up=first.Up;var right=Vector3d.Cross(up,original).Unit();var offset=(original*Math.Cos(5*Math.PI/180)+right*Math.Sin(5*Math.PI/180)).Unit();DateTimeOffset? stableSince=null;
while(clock.Elapsed<TimeSpan.FromSeconds(30))
{
var frame=await driver.ReadSystemFlightAsync(timeout.Token);
maxSpeed=Math.Max(maxSpeed,frame.Speed);maxDisplacement=Math.Max(maxDisplacement,(frame.Position-first.Position).Length);maxHeat=Math.Max(maxHeat,frame.HeatPercent??0);
if(frame.Mode!=LocalFlightMode.Normal||maxSpeed>=1||maxDisplacement>1||maxHeat>=60||frame.PlayerOverride||frame.ExternalThreat)throw new InvalidOperationException("Local steering motion/heat/state bound exceeded.");
var direction=reached?original:offset;var error=Vector3d.AngleDegrees(frame.Forward,direction);
var decision=new SystemFlightDecision(SystemNavigationPhase.Aligning,reached?"回到原姿态":"验证局部转向",direction,0){UpDirection=up};
await driver.ApplySystemFlightAsync(decision,frame,target,frame.ObservedAt.AddMilliseconds(200),timeout.Token);inputs++;
await log.WriteLineAsync(JsonSerializer.Serialize(new{at=DateTimeOffset.UtcNow,reached,error,frame,decision,inputs}));await log.FlushAsync();
if(error<.3)stableSince??=DateTimeOffset.UtcNow;else stableSince=null;
if(stableSince is {} since&&DateTimeOffset.UtcNow-since>TimeSpan.FromMilliseconds(500))
{if(reached){returned=true;break;}reached=true;stableSince=null;}
await Task.Delay(40,timeout.Token);
}
if(!returned)throw new TimeoutException("Local steering did not settle at the offset and original orientations.");
}
catch(Exception error){failure=error.ToString();}
finally{try{await driver.ReleaseSystemFlightAsync();released=true;}catch(Exception error){failure=(failure??"")+" Release: "+error.Message;}}
var passed=reached&&returned&&released&&failure is null;
await File.WriteAllTextAsync(reportPath,JsonSerializer.Serialize(new{pid,start,finished=DateTimeOffset.UtcNow,passed,reached,returned,released,inputs,maxSpeed,maxDisplacement,maxHeat,failure,tradeOrdersSent=0,fsdInputsSent=0,completeNavigationAccepted=false},ToolboxSettings.JsonOptions));
Console.WriteLine(JsonSerializer.Serialize(new{passed,reached,returned,released,inputs,maxSpeed,maxDisplacement,maxHeat,failure}));
if(!passed)throw new InvalidOperationException("Local steering acceptance failed.");
}
}
using System.Diagnostics;
using System.Text.Json;
using XFEEDToolBox.Core.Data;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Native;
using XFEEDToolBox.Core.Navigation;
internal static class LocalSteeringAcceptance
{
public static async Task RunAsync(int pid,string exactName,string reportPath)
{
if(File.Exists(reportPath)||File.Exists(reportPath+".jsonl"))throw new IOException("Use a new report path.");
var settings=ToolboxSettings.Load(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),"XFEstudio","XFEEDToolBox","settings.json"));
await using var journal=new JournalMonitor(settings.JournalDirectory);await journal.StartAsync();var raw=new NavigationPipeClient(pid);
var hash=(await raw.CallAsync(new{op="describe"})).GetProperty("gameSha256").GetString()!;
using var source=new NativeCurrentSystemSource(pid,hash,()=>journal.State);var snapshot=await source.ReadLocationsAsync();
var row=snapshot.Locations.Single(x=>x.Name.Equals(exactName,StringComparison.Ordinal));
var target=new SystemNavigationTarget(snapshot.SystemAddress,row.WorldAddress,row.Name,row.MarketId is >0?SystemTargetKind.Station:SystemTargetKind.Orbit,row.MarketId)
{NativeKey=row.Key,NativeKind=row.NativeKind,NativeIdentity=row.Identity};
var driver=new NativeSystemFlightControlDriver(pid,hash,()=>journal.State);
await using var log=new StreamWriter(new FileStream(reportPath+".jsonl",FileMode.CreateNew,FileAccess.Write,FileShare.Read));
var start=DateTimeOffset.UtcNow;var clock=Stopwatch.StartNew();var reached=false;var returned=false;var released=false;var inputs=0;double maxSpeed=0,maxDisplacement=0,maxHeat=0;
string? failure=null;
try
{
using var timeout=new CancellationTokenSource(TimeSpan.FromSeconds(40));await driver.SelectAsync(target,timeout.Token);
var first=await driver.ReadSystemFlightAsync(timeout.Token);
if(first.Mode!=LocalFlightMode.Normal||first.Speed>=1||first.HeatPercent>=60||first.PlayerOverride||first.ExternalThreat)throw new InvalidOperationException("Local steering acceptance requires a stationary safe ship in normal space.");
var original=first.Forward;var up=first.Up;var right=Vector3d.Cross(up,original).Unit();var offset=(original*Math.Cos(5*Math.PI/180)+right*Math.Sin(5*Math.PI/180)).Unit();DateTimeOffset? stableSince=null;
while(clock.Elapsed<TimeSpan.FromSeconds(30))
{
var frame=await driver.ReadSystemFlightAsync(timeout.Token);
maxSpeed=Math.Max(maxSpeed,frame.Speed);maxDisplacement=Math.Max(maxDisplacement,(frame.Position-first.Position).Length);maxHeat=Math.Max(maxHeat,frame.HeatPercent??0);
if(frame.Mode!=LocalFlightMode.Normal||maxSpeed>=1||maxDisplacement>1||maxHeat>=60||frame.PlayerOverride||frame.ExternalThreat)throw new InvalidOperationException("Local steering motion/heat/state bound exceeded.");
var direction=reached?original:offset;var error=Vector3d.AngleDegrees(frame.Forward,direction);
var decision=new SystemFlightDecision(SystemNavigationPhase.Aligning,reached?"回到原姿态":"验证局部转向",direction,0){UpDirection=up};
await driver.ApplySystemFlightAsync(decision,frame,target,frame.ObservedAt.AddMilliseconds(200),timeout.Token);inputs++;
await log.WriteLineAsync(JsonSerializer.Serialize(new{at=DateTimeOffset.UtcNow,reached,error,frame,decision,inputs}));await log.FlushAsync();
if(error<.3)stableSince??=DateTimeOffset.UtcNow;else stableSince=null;
if(stableSince is {} since&&DateTimeOffset.UtcNow-since>TimeSpan.FromMilliseconds(500))
{if(reached){returned=true;break;}reached=true;stableSince=null;}
await Task.Delay(40,timeout.Token);
}
if(!returned)throw new TimeoutException("Local steering did not settle at the offset and original orientations.");
}
catch(Exception error){failure=error.ToString();}
finally{try{await driver.ReleaseSystemFlightAsync();released=true;}catch(Exception error){failure=(failure??"")+" Release: "+error.Message;}}
var passed=reached&&returned&&released&&failure is null;
await File.WriteAllTextAsync(reportPath,JsonSerializer.Serialize(new{pid,start,finished=DateTimeOffset.UtcNow,passed,reached,returned,released,inputs,maxSpeed,maxDisplacement,maxHeat,failure,tradeOrdersSent=0,fsdInputsSent=0,completeNavigationAccepted=false},ToolboxSettings.JsonOptions));
Console.WriteLine(JsonSerializer.Serialize(new{passed,reached,returned,released,inputs,maxSpeed,maxDisplacement,maxHeat,failure}));
if(!passed)throw new InvalidOperationException("Local steering acceptance failed.");
}
}