using System.Text.Json;
using XFEEDToolBox.Core.Data;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Native;
using XFEEDToolBox.Core.Navigation;
internal static class LocalAssistAcceptance
{
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 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};
await using var protection=await FlightTestProtectionScope.ArmAsync(pid);
var driver=new NativeSystemFlightControlDriver(pid,hash,()=>journal.State);var start=DateTimeOffset.UtcNow;var engaged=false;var cancelled=false;var released=false;
DateTimeOffset? engagedAt=null;var requests=0;var inputs=0;string? failure=null;double maxHeat=0,maxSpeed=0;
await using var log=new StreamWriter(new FileStream(reportPath+".jsonl",FileMode.CreateNew,FileAccess.Write,FileShare.Read));
try
{
using var stop=new CancellationTokenSource(TimeSpan.FromSeconds(55));await driver.SelectAsync(target,stop.Token);
while(true)
{
var state=journal.State;if(state.CurrentShipId!=initial.CurrentShipId||state.CommanderId!=initial.CommanderId||state.LoadGameAt!=initial.LoadGameAt||!NavigationAssists.From(state).Supercruise)
throw new InvalidOperationException("Assist acceptance ship/session or fitted module changed.");
var frame=await driver.ReadSystemFlightAsync(stop.Token);maxHeat=Math.Max(maxHeat,frame.HeatPercent??100);maxSpeed=Math.Max(maxSpeed,frame.Speed);
if(frame.Mode!=LocalFlightMode.Supercruise||frame.PlayerOverride||frame.ExternalThreat||maxHeat>=65)throw new InvalidOperationException("Assist acceptance no longer has safe SC conditions.");
if(frame.SupercruiseAssist==NativeAssistState.Engaged){engaged=true;engagedAt??=DateTimeOffset.UtcNow;}
var direction=(frame.TargetPosition-frame.Position).Unit();var aligned=Vector3d.AngleDegrees(frame.Forward,direction)<=3;
SystemFlightDecision decision;
if(engagedAt is {} observed&&DateTimeOffset.UtcNow-observed>TimeSpan.FromSeconds(1))
{
decision=new(SystemNavigationPhase.Waiting,"验证取消原生辅助",null,0,SystemNavigationAction.CancelOwnedAssists);
if(driver.LastTelemetry is {} local&&!local.GetProperty("supercruiseAssist").GetProperty("enabled").GetBoolean()&&frame.SupercruiseAssist!=NativeAssistState.Engaged&&frame.Speed<=100000)
{cancelled=true;break;}
}
else if(engaged)decision=new(SystemNavigationPhase.Supercruise,"验证原生辅助接管",null,null,SystemNavigationAction.ReleaseFlightAxes);
else
{
if(DateTimeOffset.UtcNow-start>TimeSpan.FromSeconds(30))throw new TimeoutException("Original fitted assist did not engage.");
var enable=aligned&&requests==0;decision=new(SystemNavigationPhase.Aligning,"验证原生超巡辅助",direction,aligned?.75:0,enable?SystemNavigationAction.EnableSupercruiseAssist:SystemNavigationAction.None);
if(enable)requests++;
}
await driver.ApplySystemFlightAsync(decision,frame,target,frame.ObservedAt.AddMilliseconds(250),stop.Token);inputs++;
await log.WriteLineAsync(JsonSerializer.Serialize(new{at=DateTimeOffset.UtcNow,engaged,cancelled,requests,inputs,frame,driver.LastTelemetry}));await log.FlushAsync();
await Task.Delay(50,stop.Token);
}
}
catch(Exception error){failure=error.ToString();}
finally
{
try
{
var frame=await driver.ReadSystemFlightAsync(CancellationToken.None);
await driver.ApplySystemFlightAsync(new(SystemNavigationPhase.Waiting,"结束辅助验证并减速",null,0,SystemNavigationAction.CancelOwnedAssists),frame,target,frame.ObservedAt.AddMilliseconds(250),CancellationToken.None);
}
catch(Exception error){failure=(failure??"")+" Cleanup: "+error.Message;}
try{await driver.ReleaseSystemFlightAsync();released=true;}catch(Exception error){failure=(failure??"")+" Release: "+error.Message;}
}
var passed=engaged&&cancelled&&released&&failure is null;
await File.WriteAllTextAsync(reportPath,JsonSerializer.Serialize(new{pid,start,finished=DateTimeOffset.UtcNow,passed,engaged,cancelled,released,requests,inputs,maxHeat,maxSpeed,failure,tradeOrdersSent=0,dockingRequestsSent=0},ToolboxSettings.JsonOptions));
Console.WriteLine(JsonSerializer.Serialize(new{passed,engaged,cancelled,released,requests,inputs,maxHeat,maxSpeed,failure}));
if(!passed)throw new InvalidOperationException("Native SC assist 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 LocalAssistAcceptance
{
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 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};
await using var protection=await FlightTestProtectionScope.ArmAsync(pid);
var driver=new NativeSystemFlightControlDriver(pid,hash,()=>journal.State);var start=DateTimeOffset.UtcNow;var engaged=false;var cancelled=false;var released=false;
DateTimeOffset? engagedAt=null;var requests=0;var inputs=0;string? failure=null;double maxHeat=0,maxSpeed=0;
await using var log=new StreamWriter(new FileStream(reportPath+".jsonl",FileMode.CreateNew,FileAccess.Write,FileShare.Read));
try
{
using var stop=new CancellationTokenSource(TimeSpan.FromSeconds(55));await driver.SelectAsync(target,stop.Token);
while(true)
{
var state=journal.State;if(state.CurrentShipId!=initial.CurrentShipId||state.CommanderId!=initial.CommanderId||state.LoadGameAt!=initial.LoadGameAt||!NavigationAssists.From(state).Supercruise)
throw new InvalidOperationException("Assist acceptance ship/session or fitted module changed.");
var frame=await driver.ReadSystemFlightAsync(stop.Token);maxHeat=Math.Max(maxHeat,frame.HeatPercent??100);maxSpeed=Math.Max(maxSpeed,frame.Speed);
if(frame.Mode!=LocalFlightMode.Supercruise||frame.PlayerOverride||frame.ExternalThreat||maxHeat>=65)throw new InvalidOperationException("Assist acceptance no longer has safe SC conditions.");
if(frame.SupercruiseAssist==NativeAssistState.Engaged){engaged=true;engagedAt??=DateTimeOffset.UtcNow;}
var direction=(frame.TargetPosition-frame.Position).Unit();var aligned=Vector3d.AngleDegrees(frame.Forward,direction)<=3;
SystemFlightDecision decision;
if(engagedAt is {} observed&&DateTimeOffset.UtcNow-observed>TimeSpan.FromSeconds(1))
{
decision=new(SystemNavigationPhase.Waiting,"验证取消原生辅助",null,0,SystemNavigationAction.CancelOwnedAssists);
if(driver.LastTelemetry is {} local&&!local.GetProperty("supercruiseAssist").GetProperty("enabled").GetBoolean()&&frame.SupercruiseAssist!=NativeAssistState.Engaged&&frame.Speed<=100000)
{cancelled=true;break;}
}
else if(engaged)decision=new(SystemNavigationPhase.Supercruise,"验证原生辅助接管",null,null,SystemNavigationAction.ReleaseFlightAxes);
else
{
if(DateTimeOffset.UtcNow-start>TimeSpan.FromSeconds(30))throw new TimeoutException("Original fitted assist did not engage.");
var enable=aligned&&requests==0;decision=new(SystemNavigationPhase.Aligning,"验证原生超巡辅助",direction,aligned?.75:0,enable?SystemNavigationAction.EnableSupercruiseAssist:SystemNavigationAction.None);
if(enable)requests++;
}
await driver.ApplySystemFlightAsync(decision,frame,target,frame.ObservedAt.AddMilliseconds(250),stop.Token);inputs++;
await log.WriteLineAsync(JsonSerializer.Serialize(new{at=DateTimeOffset.UtcNow,engaged,cancelled,requests,inputs,frame,driver.LastTelemetry}));await log.FlushAsync();
await Task.Delay(50,stop.Token);
}
}
catch(Exception error){failure=error.ToString();}
finally
{
try
{
var frame=await driver.ReadSystemFlightAsync(CancellationToken.None);
await driver.ApplySystemFlightAsync(new(SystemNavigationPhase.Waiting,"结束辅助验证并减速",null,0,SystemNavigationAction.CancelOwnedAssists),frame,target,frame.ObservedAt.AddMilliseconds(250),CancellationToken.None);
}
catch(Exception error){failure=(failure??"")+" Cleanup: "+error.Message;}
try{await driver.ReleaseSystemFlightAsync();released=true;}catch(Exception error){failure=(failure??"")+" Release: "+error.Message;}
}
var passed=engaged&&cancelled&&released&&failure is null;
await File.WriteAllTextAsync(reportPath,JsonSerializer.Serialize(new{pid,start,finished=DateTimeOffset.UtcNow,passed,engaged,cancelled,released,requests,inputs,maxHeat,maxSpeed,failure,tradeOrdersSent=0,dockingRequestsSent=0},ToolboxSettings.JsonOptions));
Console.WriteLine(JsonSerializer.Serialize(new{passed,engaged,cancelled,released,requests,inputs,maxHeat,maxSpeed,failure}));
if(!passed)throw new InvalidOperationException("Native SC assist acceptance failed.");
}
}