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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Text.Json;
using CommunityToolkit.Mvvm.ComponentModel;
using XFEEDToolBox.Core.Execution;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Planning;

namespace XFEEDToolBox.ViewModels;

public partial class ToolboxViewModel
{
    [ObservableProperty]public partial string AutomaticSupplySummary{get;set;}="";

    private async Task<AutomaticSupplyChoice?> SelectAutomaticSupplyAsync(AutomaticSupplyRequest value,CancellationToken token)
    {
        var config=value.Configuration;var player=value.Player;var settings=runtime.Settings;
        if(value.Credits<=value.Reserve)return null;
        if(player.MaxJumpRange is not (>0 and <=500))throw new InvalidOperationException("正在等待当前飞船的实际跳跃射程,未使用假定船型参数。");
        var pad=ShipPadPolicy.RequiredPad(player.Ship);
        var saved=config.SourcePlanning??settings.Planner;
        var center=config.SourceCenterFollowsPlayer?player.Position:saved.SearchCenterPosition??settings.MarketCacheCenterPosition;
        var request=saved with{StartMarketId=config.Destination.MarketId,Credits=value.Credits,ReserveCredits=value.Reserve,
            InvestmentCredits=config.InvestmentCredits,CargoCapacity=player.CargoCapacity,OccupiedCargo=player.CargoCount,
            ActualShipType=player.Ship,ShipRequiredPad=pad,RequiredPad=(LandingPad)Math.Max((int)pad,(int)saved.RequiredPad),
            CurrentPosition=player.Position,CurrentSystemName=player.SystemName,CurrentMarketId=player.MarketId,
            LadenJumpRangeLy=player.MaxJumpRange.Value,JumpRangeIsEstimate=true,SearchCenterPosition=center,
            SearchCenterName=config.SourceCenterFollowsPlayer?player.SystemName:saved.SearchCenterName,
            SearchRadiusLy=config.SourcePlanning?.SearchRadiusLy??settings.MarketCacheRadiusLy,AsOf=DateTimeOffset.UtcNow};
        var membershipPath=Path.Combine(config.WorkingDirectory,"membership","membership-latest.json");
        var membership=JsonSerializer.Deserialize<OriginalCommunityGoalRead>(await File.ReadAllTextAsync(membershipPath,token),ToolboxSettings.JsonOptions)
            ??throw new InvalidDataException("CG报名与接收商品尚未就绪。");
        var goal=CommunityGoalCatalogue.RequireDestinationTrade(membership.Goals,config.Destination.MarketId,config.CommunityGoalId,request.AsOf,true);
        var accepted=config.Commodities.Intersect(goal.AcceptedCommodities,StringComparer.Ordinal).ToArray();
        if(accepted.Length==0)throw new InvalidDataException("所选商品不在当前有效CG的实际接收范围内。");
        var ready=new TaskCompletionSource<IProgress<TradePlanningProgress>>(TaskCreationOptions.RunContinuationsAsynchronously);
        using var cancellation=token.Register(()=>ready.TrySetCanceled(token));
        if(!dispatcher.TryEnqueue(()=>{if(!token.IsCancellationRequested)ready.TrySetResult(BeginTradePlanningProgress(token));}))
            throw new OperationCanceledException("规划界面已关闭。",token);
        var progress=await ready.Task.ConfigureAwait(false);
        try
        {
            return await Task.Run(async()=>
            {
                var cached=runtime.Markets.LoadPlanningRegion(request,token:token,progress:progress);
                var stations=cached.Stations.Append(config.Source).Append(config.Destination).DistinctBy(s=>s.MarketId)
                    .Where(s=>s.MarketId==config.Destination.MarketId||!value.ExcludedSourceMarkets.Contains(s.MarketId)).ToArray();
                // A live station snapshot replaces its cached rows completely,
                // including zero stock and commodities no longer offered.
                var remaining=AutomaticSupplyPlanner.DeductConfirmedTrades(cached,value.ConfirmedTrades);
                var merged=AutomaticSupplyPlanner.MergeObservations(remaining with{Stations=stations},value.ObservedQuotes);
                var choices=AutomaticSupplyPlanner.Calculate(merged,request,config.Destination,accepted,token:token,progress:progress);
                var proof=new{at=request.AsOf,goalId=goal.Id,request,stations=cached.Stations.Count,quotes=merged.Quotes.Count,
                    supplyBasis="缓存供需估算;实际到站报价优先,采购前及每轮交付后重算",choices};
                var path=Path.Combine(config.WorkingDirectory,"supply-candidates-latest.json");
                await File.WriteAllTextAsync(path+".tmp",JsonSerializer.Serialize(proof,ToolboxSettings.JsonOptions),token);
                File.Move(path+".tmp",path,true);
                return choices.FirstOrDefault();
            },token).ConfigureAwait(false);
        }
        finally{dispatcher.TryEnqueue(EndTradePlanningProgress);}
    }
}