XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Collections.Concurrent;
using XFEEDToolBox.Core.Execution;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Planning;
using XFEEDToolBox.Views;

namespace XFEEDToolBox;

public partial class App
{
    private static async Task<object> RunTradePlanningFixtureProbeAsync(PlannerPage page,string directory,Func<string,Task> capture,Action<string,bool?,string> check)
    {
        var isolated=Path.GetFullPath(Path.Combine(directory,"isolated-data"));
        if(!ViewModel.OfflinePreview||!Path.GetFullPath(Runtime.DataDirectory).Equals(isolated,StringComparison.OrdinalIgnoreCase))
            return new{skipped=true,reason="Synthetic planning visuals are restricted to the explicit isolated offline profile."};
        var player=ViewModel.CurrentPlayer;var advanced=ViewModel.TradeAdvancedEditing;
        var at=DateTimeOffset.UtcNow;
        Station Station(long id,string name,double x)=>new(){MarketId=id,Name=name,SystemName="Synthetic planner fixture",Position=new(x,0,0),
            Type="Coriolis",MaxLandingPad=LandingPad.Large,Security=SecurityLevel.High,DistanceToArrivalLs=100,UpdatedAt=at};
        var origin=Station(990001,"Synthetic supply",0);var destination=Station(990002,"Synthetic CG delivery",5);
        CommodityQuote Quote(Station station,string key,long buy,long sell)=>new(){MarketId=station.MarketId,Commodity=key,DisplayName=key,
            BuyPrice=buy,SellPrice=sell,Supply=200,Demand=200,CollectedAt=at,Source=DataSource.Demonstration};
        var book=new MarketBook([origin,destination],[Quote(origin,"uranium",100,100),Quote(destination,"uranium",200,200),
            Quote(origin,"coffee",200,200),Quote(destination,"coffee",100,100)]);
        var request=new PlannerRequest{StartMarketId=origin.MarketId,CurrentMarketId=origin.MarketId,CurrentPosition=new(0,0,0),Credits=100000,ReserveCredits=0,CargoCapacity=64,LadenJumpRangeLy=20,
            ActualShipType="Type9",ShipRequiredPad=LandingPad.Large,RequiredPad=LandingPad.Large,AllowedSourceKinds=StationSourceKinds.AllKnown,
            MaxLegs=2,Loop=true,AsOf=at,CostPerJump=0,CostPerMinute=0};
        var samples=new ConcurrentQueue<TradePlanningProgress>();
        var result=await Task.Run(()=>new TradeRoutePlanner().CalculateCandidatesWithDiagnostics(book,request,3,progress:new PlannerFixtureProgress(samples)));
        if(result.Candidates.Count==0)throw new InvalidOperationException("The isolated synthetic planning fixture did not produce a feasible route.");
        var goal=new CommunityGoalInfo(990001,"Synthetic CG activity fixture","tradelist","trade",destination.MarketId,destination.SystemName,destination.Name,
            at.AddHours(1),true,false,null){AcceptedCommodities=["uranium"]};
        var observed=samples.LastOrDefault(value=>value.Total is >0)??throw new InvalidOperationException("The synthetic calculation supplied no measurable progress sample.");
        var unknown=new TradePlanningProgress("总量未知的读库状态",0,null,"显式测试总量未知时的不定进度样式");
        object? knownLayout=null;object? unknownLayout=null;object? kinds=null;object? cg=null;
        using(ViewModel.EnterTradePlanningFixtureForProbe(result,[goal],observed,isolated))
        {
            try
            {
                ViewModel.TradeAdvancedEditing=false;page.UpdateLayout();await Task.Delay(80);
                var candidate=ViewModel.TradeCandidates.FirstOrDefault(value=>value.IsCommunityGoal);
                var list=(ListView)page.FindName("TradeRouteCandidates");
                if(candidate is not null){list.ScrollIntoView(candidate);page.UpdateLayout();await Task.Delay(100);((FrameworkElement)list.ContainerFromItem(candidate)).StartBringIntoView(new BringIntoViewOptions{AnimationDesired=false});await Task.Delay(100);}
                await capture("trade-planner-synthetic-cg");
                check("isolated CG badge and dedicated colour",candidate is not null&&candidate.CommunityGoalVisibility==Visibility.Visible&&candidate.CommunityGoalLabel.Contains("合成夹具")
                    &&candidate.RouteAccentBrush is Microsoft.UI.Xaml.Media.SolidColorBrush{Color.R:195,Color.G:166,Color.B:255},
                    "Explicit synthetic route/CG only; the production template receives a purple badge with text. Extra CG rewards are absent from NetProfit and Cr/h.");
                cg=ViewModel.TradeCandidates.Select(value=>new{value.IsCommunityGoal,value.CommunityGoalLabel,value.CommunityGoalDescription,value.Plan.NetProfit,value.Plan.CreditsPerHour}).ToArray();
                var panel=(FrameworkElement)page.FindName("TradePlanningProgressPanel");var bar=(ProgressBar)page.FindName("TradePlanningProgressBar");
                panel.StartBringIntoView(new BringIntoViewOptions{AnimationDesired=false});page.UpdateLayout();await Task.Delay(80);
                knownLayout=new{observed.Stage,observed.Processed,observed.Total,bar.Value,bar.IsIndeterminate,panel.ActualWidth,panel.ActualHeight};
                await capture("trade-planner-synthetic-progress-known");
                check("isolated progress uses observed processed and total",!bar.IsIndeterminate&&observed.Total>0&&Math.Abs(bar.Value-Math.Clamp(observed.Processed*100d/observed.Total.Value,0,100))<.001,
                    "Determinate visual replays an actual Core progress sample from the synthetic book, not an elapsed-time percentage.");
                ViewModel.ReplayTradePlanningProgressForProbe(unknown);page.UpdateLayout();await Task.Delay(80);
                unknownLayout=new{ViewModel.TradeProgressStage,ViewModel.TradeProgressProcessed,ViewModel.TradeProgressTotal,bar.IsIndeterminate};
                await capture("trade-planner-synthetic-progress-unknown");
                check("isolated unknown total is indeterminate",bar.IsIndeterminate&&ViewModel.TradeProgressTotal is null,"Unknown-total style is an explicit component-state fixture; no fabricated percentage is displayed.");
                ViewModel.TradeAdvancedEditing=true;page.UpdateLayout();await Task.Delay(80);
                var sources=(FrameworkElement)page.FindName("TradeSourceKindsPanel");sources.StartBringIntoView(new BringIntoViewOptions{AnimationDesired=false});await Task.Delay(100);
                kinds=ViewModel.TradeSourceKinds.Select(value=>new{value.Label,value.Kind,value.IsSelected}).ToArray();
                await capture("trade-planner-synthetic-source-kinds");
                check("isolated source kinds support multiple choices",ViewModel.TradeSourceKinds.Count==9&&ViewModel.TradeSourceKinds.Count(value=>value.IsSelected)==2
                    &&ViewModel.SelectedTradeSourceKinds==(StationSourceKinds.Starport|StationSourceKinds.FleetCarrier),"Nine explicit options; starport and carrier selected together, unknown remains separate. Destination eligibility is independent.");
                var distance=ViewModel.MaxDistance;var version=ViewModel.TradePlanningInputVersionForProbe;
                try
                {
                    ViewModel.MaxDistance=distance+1;
                    check("isolated advanced edits invalidate old candidates",ViewModel.TradeCandidates.Count==0&&ViewModel.Plan is null&&ViewModel.TradePlanningInputVersionForProbe>version,
                        "Changing an actual advanced input clears the synthetic candidate collection and changes its input generation.");
                    version=ViewModel.TradePlanningInputVersionForProbe;ViewModel.MaxDistance=distance+2;
                    check("isolated in-flight input changes remain observable with no candidates",ViewModel.TradePlanningInputVersionForProbe>version,
                        "The generation changes even after candidates are empty, so a running calculation cannot publish results for the old inputs.");
                }
                finally{ViewModel.MaxDistance=distance;}
            }
            finally{ViewModel.TradeAdvancedEditing=advanced;}
        }
        check("isolated planning progress is cleared",!ViewModel.TradePlanningBusy&&ViewModel.TradeProgressVisibility==Visibility.Collapsed&&ViewModel.TradeProgressTotal is null,"Fixture cleanup uses the same completion reset and invalidates late operation updates.");
        check("isolated planning leaves actual player unchanged",ViewModel.CurrentPlayer==player&&!ViewModel.RunActive,"Only an isolated UI route collection was replaced temporarily; the actual player, wallet, cargo and session were never substituted.");
        return new{synthetic=true,originalPlayerUnchanged=ViewModel.CurrentPlayer==player,knownLayout,unknownLayout,kinds,cg,
            actualSyntheticCalculationProgress=samples.ToArray(),nativeReadsSent=0,gameInputsSent=0,tradeOrdersSubmitted=0,settingWrites=0};
    }
    private sealed class PlannerFixtureProgress(ConcurrentQueue<TradePlanningProgress> samples):IProgress<TradePlanningProgress>
    {public void Report(TradePlanningProgress value)=>samples.Enqueue(value);}
}