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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Dispatching;
using XFEEDToolBox.Core.Navigation;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Outfitting;
using XFEEDToolBox.Core.Presentation;

namespace XFEEDToolBox.ViewModels;

public partial class ToolboxViewModel
{
    public LiveCollection<ShipStatusLiveRow,ShipStatusValue,string> LiveFlightStatus { get; }=new(x=>x.Key,x=>new(x));
    public LiveCollection<ShipStatusLiveRow,ShipStatusValue,string> LiveCockpitStatus { get; }=new(x=>x.Key,x=>new(x));
    public LiveCollection<ShipAmmunitionLiveRow,ShipAmmunition,string> LiveShipAmmunition { get; }=new(x=>x.Slot,x=>new(x));
    public LiveCollection<ShipStatusLiveRow,ShipStatusValue,string> LiveShipSpecifications { get; }=new(x=>x.Key,x=>new(x));
    [ObservableProperty] public partial string ShipPowerCapacityText { get; set; }="—";
    [ObservableProperty] public partial string ShipPowerRetractedText { get; set; }="—";
    [ObservableProperty] public partial string ShipPowerDeployedText { get; set; }="—";
    [ObservableProperty] public partial string ShipPowerCurrentText { get; set; }="—";
    [ObservableProperty] public partial string ShipPowerMarginText { get; set; }="—";
    [ObservableProperty] public partial string ShipPowerSource { get; set; }="等待完整装配";
    [ObservableProperty] public partial double ShipPowerRetractedPercent { get; set; }
    [ObservableProperty] public partial double ShipPowerDeployedPercent { get; set; }
    [ObservableProperty] public partial bool ShipPowerRetractedOverloaded { get; set; }
    [ObservableProperty] public partial bool ShipPowerDeployedOverloaded { get; set; }
    [ObservableProperty] public partial bool ShipPowerCurrentOverloaded { get; set; }
    [ObservableProperty] public partial string ShipTelemetryStatus { get; set; }="等待飞船状态";
    [ObservableProperty] public partial string ShipTelemetryNote { get; set; }="弹药与船体完整度标明快照时间;未提供的数据保持未知。";
    private DispatcherQueueTimer? shipTelemetryTimer;
    private int shipStatusViews;
    private bool shipTelemetryReading;
    private ShipRecord? equipmentShip;
    private EquipmentStatistics? shipEquipment;
    internal void ShowShipStatus()
    {
        shipStatusViews++;
        if(shipTelemetryTimer is null)
        {
            shipTelemetryTimer=dispatcher.CreateTimer();shipTelemetryTimer.Interval=TimeSpan.FromMilliseconds(200);
            shipTelemetryTimer.Tick+=async(_,_)=>await RefreshShipStatusAsync();
        }
        shipTelemetryTimer.Start();_ = RefreshShipStatusAsync();
    }
    internal void HideShipStatus(){shipStatusViews=Math.Max(0,shipStatusViews-1);if(shipStatusViews==0)shipTelemetryTimer?.Stop();}
    private async Task RefreshShipStatusAsync()
    {
        if(shipTelemetryReading||shipStatusViews==0)return;
        shipTelemetryReading=true;
        try
        {
            var p=CurrentPlayer;
            LiveCockpitStatus.Apply(ShipStatusRows.Cockpit(p));LiveShipAmmunition.Apply(ShipStatusRows.Ammunition(p));
            await RefreshShipEquipmentAsync(p);
            if(!DemoMode&&p.InGame&&runtime.FlightDriver is NativeFlightControlDriver driver)
            {
                var raw=NavigationActive&&driver.LastRawTelemetry is {} cached?cached:await driver.ReadShipTelemetryAsync(lifetime.Token);
                var at=raw.GetProperty("observedAtUnixMilliseconds").GetInt64();
                if(DateTimeOffset.UtcNow-DateTimeOffset.FromUnixTimeMilliseconds(at)>TimeSpan.FromMilliseconds(600))throw new InvalidOperationException("遥测已过期");
                LiveFlightStatus.Apply(ShipStatusRows.Flight(raw));
                ShipTelemetryStatus="实时接入 · "+DateTimeOffset.FromUnixTimeMilliseconds(at).ToLocalTime().ToString("HH:mm:ss.fff");
            }
            else
            {
                LiveFlightStatus.Apply(ShipStatusRows.Flight(null));
                ShipTelemetryStatus=p.InGame?"Status 已接入 · 原生遥测待连接":"游戏未运行 · 显示最后快照";
            }
        }
        catch(OperationCanceledException)when(lifetime.IsCancellationRequested){}
        catch(Exception e){ShipTelemetryStatus="实时数据暂不可用 · "+e.Message;LiveFlightStatus.Apply(ShipStatusRows.Flight(null));}
        finally{shipTelemetryReading=false;}
    }
    private static bool SameEquipment(ShipRecord? a,ShipRecord? b)=>a?.ShipId==b?.ShipId&&a?.Type==b?.Type
        &&a?.ConfigurationAt==b?.ConfigurationAt&&a?.ConfigurationChanged==b?.ConfigurationChanged
        &&(ReferenceEquals(a?.Modules,b?.Modules)||a is not null&&b is not null&&a.Modules.SequenceEqual(b.Modules));
    private async Task RefreshShipEquipmentAsync(PlayerState player)
    {
        var ship=player.Ships.FirstOrDefault(s=>s.ShipId==player.CurrentShipId);
        if(!SameEquipment(equipmentShip,ship))
        {
            equipmentShip=ship;shipEquipment=null;
            ShipPowerSource="正在计算装配属性…";
            try
            {
                if(ship is {ConfigurationAt:not null,ConfigurationChanged:false})
                    shipEquipment=await Task.Run(()=>EdsyLinkCodec.Statistics(ShipRefit.Create(ship,outfitting)),lifetime.Token);
                ShipPowerSource=shipEquipment is not null?$"装配计算 · {ShipStatusRows.At(ship?.ConfigurationAt)} · 包含当前工程参数":"等待完整装配";
            }
            catch(OperationCanceledException)when(lifetime.IsCancellationRequested){throw;}
            catch(Exception){ShipPowerSource="装配属性暂不可用 · 等待完整且受支持的模块快照";}
        }
        var latest=CurrentPlayer;
        if(!SameEquipment(ship,latest.Ships.FirstOrDefault(s=>s.ShipId==latest.CurrentShipId)))return;
        player=latest;
        var capacity=shipEquipment?.Value("pwrcap");var retracted=shipEquipment?.Value("pwrdraw_ret");var deployed=shipEquipment?.Value("pwrdraw_dep");
        static string MW(double? n)=>n is {} value?$"{value:F2} MW":"—";
        ShipPowerCapacityText=MW(capacity);ShipPowerRetractedText=MW(retracted);ShipPowerDeployedText=MW(deployed);
        ShipPowerRetractedPercent=capacity>0&&retracted is {} r?Math.Clamp(r/capacity.Value*100,0,100):0;
        ShipPowerDeployedPercent=capacity>0&&deployed is {} d?Math.Clamp(d/capacity.Value*100,0,100):0;
        ShipPowerRetractedOverloaded=retracted>capacity;ShipPowerDeployedOverloaded=deployed>capacity;
        var current=player.Status is {} status?((status.Flags&(1L<<6))!=0?deployed:retracted):null;
        ShipPowerCurrentText=player.Status is null?"—":((player.Status.Flags&(1L<<6))!=0?"已部署 · ":"已收起 · ")+MW(current);
        ShipPowerCurrentOverloaded=current>capacity;
        ShipPowerMarginText=capacity is {} total&&current is {} draw?MW(total-draw):"—";
        LiveShipSpecifications.Apply(EquipmentStatusRows.Ship(shipEquipment,ShipPowerSource));
    }
}