using System.Text.Json;
using XFEEDToolBox.Core.Data;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Native;
using XFEEDToolBox.Core.Navigation;
internal static class LocalCruiseExitAcceptance
{
public static async Task RunAsync(int pid,string exactName,string reportPath)
{
if(File.Exists(reportPath))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 initial=journal.State;var pipe=new NavigationPipeClient(pid);
var hash=(await pipe.CallAsync(new{op="describe"})).GetProperty("gameSha256").GetString()!;
using var source=new NativeCurrentSystemSource(pid,hash,()=>journal.State);var catalogue=await source.ReadLocationsAsync();
var row=catalogue.Locations.Single(x=>x.Name==exactName);var target=new SystemNavigationTarget(catalogue.SystemAddress,row.WorldAddress,row.Name,SystemTargetKind.Station,row.MarketId)
{NativeKey=row.Key,NativeKind=row.NativeKind,NativeIdentity=row.Identity};
var driver=new NativeSystemFlightControlDriver(pid,hash,()=>journal.State);await using var protection=await FlightTestProtectionScope.ArmAsync(pid);
var start=DateTimeOffset.UtcNow;var exited=false;var released=false;var exitRequests=0;string? failure=null;var samples=new List<object>();
try
{
using var timeout=new CancellationTokenSource(TimeSpan.FromSeconds(45));await driver.SelectAsync(target,timeout.Token);
var first=await driver.ReadSystemFlightAsync(timeout.Token);
if(first.Mode!=LocalFlightMode.Supercruise||first.Speed>100000||first.HeatPercent>=60||first.SupercruiseAssist==NativeAssistState.Engaged)
throw new InvalidOperationException("Exit acceptance requires low-speed, cool SC without an engaged assist.");
await driver.LeaveSupercruiseSlowlyAsync(first,timeout.Token);exitRequests++;
while(true)
{
timeout.Token.ThrowIfCancellationRequested();
var state=journal.State;if(state.CommanderId!=initial.CommanderId||state.CurrentShipId!=initial.CurrentShipId||state.LoadGameAt!=initial.LoadGameAt||state.Navigation.CurrentSystemAddress!=catalogue.SystemAddress)
throw new InvalidOperationException("Session changed during exit.");
try
{
var raw=await pipe.CallAsync(new{op="read"},timeout.Token);var health=raw.GetProperty("shipHealth");
samples.Add(new{at=DateTimeOffset.UtcNow,raw,exitAt=state.Navigation.SupercruiseExitedAt,flags=state.Status?.Flags});
if(state.Navigation.SupercruiseExitedAt>=start.AddSeconds(-1)&&TravelPreparation.StatusConfirmed(state,DateTimeOffset.UtcNow)
&&(state.Status!.Flags&(1L<<4))==0&&health.GetProperty("verified").GetBoolean()&&health.GetProperty("hullFraction").GetDouble()>=.5
&&raw.GetProperty("speedMetresPerSecond").GetDouble()<1){exited=true;break;}
}
catch(Exception error)when(NavigationRecovery.CanRetry(error,timeout.Token)){}
await Task.Delay(80,timeout.Token);
}
}
catch(Exception error){failure=error.ToString();}
finally{try{await driver.ReleaseSystemFlightAsync();released=true;}catch(Exception error){failure=(failure??"")+" Release: "+error.Message;}}
var passed=exited&&released&&failure is null;
await File.WriteAllTextAsync(reportPath,JsonSerializer.Serialize(new{pid,start,finished=DateTimeOffset.UtcNow,passed,exited,released,exitRequests,samples,failure,tradeOrdersSent=0},ToolboxSettings.JsonOptions));
Console.WriteLine(JsonSerializer.Serialize(new{passed,exited,released,exitRequests,failure}));
if(!passed)throw new InvalidOperationException("Low-speed SC exit acceptance failed.");
}
}
using System.Text.Json;
using XFEEDToolBox.Core.Data;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Native;
using XFEEDToolBox.Core.Navigation;
internal static class LocalCruiseExitAcceptance
{
public static async Task RunAsync(int pid,string exactName,string reportPath)
{
if(File.Exists(reportPath))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 initial=journal.State;var pipe=new NavigationPipeClient(pid);
var hash=(await pipe.CallAsync(new{op="describe"})).GetProperty("gameSha256").GetString()!;
using var source=new NativeCurrentSystemSource(pid,hash,()=>journal.State);var catalogue=await source.ReadLocationsAsync();
var row=catalogue.Locations.Single(x=>x.Name==exactName);var target=new SystemNavigationTarget(catalogue.SystemAddress,row.WorldAddress,row.Name,SystemTargetKind.Station,row.MarketId)
{NativeKey=row.Key,NativeKind=row.NativeKind,NativeIdentity=row.Identity};
var driver=new NativeSystemFlightControlDriver(pid,hash,()=>journal.State);await using var protection=await FlightTestProtectionScope.ArmAsync(pid);
var start=DateTimeOffset.UtcNow;var exited=false;var released=false;var exitRequests=0;string? failure=null;var samples=new List<object>();
try
{
using var timeout=new CancellationTokenSource(TimeSpan.FromSeconds(45));await driver.SelectAsync(target,timeout.Token);
var first=await driver.ReadSystemFlightAsync(timeout.Token);
if(first.Mode!=LocalFlightMode.Supercruise||first.Speed>100000||first.HeatPercent>=60||first.SupercruiseAssist==NativeAssistState.Engaged)
throw new InvalidOperationException("Exit acceptance requires low-speed, cool SC without an engaged assist.");
await driver.LeaveSupercruiseSlowlyAsync(first,timeout.Token);exitRequests++;
while(true)
{
timeout.Token.ThrowIfCancellationRequested();
var state=journal.State;if(state.CommanderId!=initial.CommanderId||state.CurrentShipId!=initial.CurrentShipId||state.LoadGameAt!=initial.LoadGameAt||state.Navigation.CurrentSystemAddress!=catalogue.SystemAddress)
throw new InvalidOperationException("Session changed during exit.");
try
{
var raw=await pipe.CallAsync(new{op="read"},timeout.Token);var health=raw.GetProperty("shipHealth");
samples.Add(new{at=DateTimeOffset.UtcNow,raw,exitAt=state.Navigation.SupercruiseExitedAt,flags=state.Status?.Flags});
if(state.Navigation.SupercruiseExitedAt>=start.AddSeconds(-1)&&TravelPreparation.StatusConfirmed(state,DateTimeOffset.UtcNow)
&&(state.Status!.Flags&(1L<<4))==0&&health.GetProperty("verified").GetBoolean()&&health.GetProperty("hullFraction").GetDouble()>=.5
&&raw.GetProperty("speedMetresPerSecond").GetDouble()<1){exited=true;break;}
}
catch(Exception error)when(NavigationRecovery.CanRetry(error,timeout.Token)){}
await Task.Delay(80,timeout.Token);
}
}
catch(Exception error){failure=error.ToString();}
finally{try{await driver.ReleaseSystemFlightAsync();released=true;}catch(Exception error){failure=(failure??"")+" Release: "+error.Message;}}
var passed=exited&&released&&failure is null;
await File.WriteAllTextAsync(reportPath,JsonSerializer.Serialize(new{pid,start,finished=DateTimeOffset.UtcNow,passed,exited,released,exitRequests,samples,failure,tradeOrdersSent=0},ToolboxSettings.JsonOptions));
Console.WriteLine(JsonSerializer.Serialize(new{passed,exited,released,exitRequests,failure}));
if(!passed)throw new InvalidOperationException("Low-speed SC exit acceptance failed.");
}
}