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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Diagnostics;
using System.Text.Json;
using System.Runtime.InteropServices.WindowsRuntime;
using Microsoft.UI.Xaml.Automation.Peers;
using Microsoft.UI.Xaml.Automation.Provider;
using Microsoft.UI.Xaml.Controls.Primitives;
using XFEEDToolBox.Controls;
using XFEEDToolBox.Core.Engineering;
using XFEEDToolBox.Core.Outfitting;
using XFEEDToolBox.Views;
namespace XFEEDToolBox;
public partial class App
{
    private static async Task RunRefinementProbeAsync(string reportPath)
    {
        var directory=Path.GetDirectoryName(Path.GetFullPath(reportPath))!;Directory.CreateDirectory(directory);
        var initialSystem=Runtime.Player.Navigation.CurrentSystemAddress;var initialJump=Runtime.Player.Navigation.LastJumpAt;
        object? editor=null,progress=null,list=null,profile=null,logs=null,shield=null,interfaceChanges=null;string? failure=null;
        static void Require(bool condition,string message){if(!condition)throw new InvalidOperationException(message);}
        static void Invoke(Button button)=>((IInvokeProvider)new ButtonAutomationPeer(button).GetPattern(PatternInterface.Invoke)).Invoke();
        async Task<Page> Open(Type type){AppShellPage.Current!.Navigate(type);await Task.Delay(250);var page=AppShellPage.Current.CurrentPage!;page.UpdateLayout();return page;}
        try
        {
            MainWindow.AppWindow.Move(new(-30000,-30000));MainWindow.AppWindow.Resize(new(1900,1160));MainWindow.AppWindow.Show(false);
            var page=(RefitPage)await Open(typeof(RefitPage));ViewModel.RebuildRefitCommand.Execute(null);await Task.Delay(300);
            var slots=(GridView)page.FindName("SlotList");var sample=ViewModel.RefitVisibleSlots.First(s=>s.Module?.Engineering is not null);
            slots.SelectedItem=sample;await Task.Delay(200);
            var wait=Stopwatch.StartNew();while(Descendants(slots).OfType<ModuleIcon>().Count(i=>i.IsRealGameIcon)<slots.Items.Count&&wait.Elapsed<TimeSpan.FromSeconds(15))await Task.Delay(150);
            var icons=Descendants(slots).OfType<ModuleIcon>().Select(i=>new{i.Item,i.ResourceHash,i.IsRealGameIcon,i.Failure}).ToArray();
            var container=(GridViewItem)slots.ContainerFromItem(sample);var presenter=Descendants(container).OfType<GridViewItemPresenter>().Single();
            var ring=(ProgressRing)page.FindName("CurrentEngineeringProgress");var actualProgress=sample.Module!.Engineering!.Quality!.Value*100;
            editor=new{cards=slots.Items.Count,icons,realIcons=icons.Count(i=>i.IsRealGameIcon),squareCorners=container.CornerRadius.TopLeft==0&&presenter.CornerRadius.TopLeft==0,
                noWholeShipTotals=page.FindName("RefitTotals") is null&&!Descendants(page).OfType<TextBlock>().Any(t=>t.Text.Contains("整船材料总表")),currentRing=ring.Value,actualProgress,currentRingReadOnly=!ring.IsHitTestVisible,
                slotSize=sample.SlotSize,currentGrade=sample.CurrentGrade,cardName=sample.CardName};
            Require(icons.Length>0&&icons.All(i=>i.IsRealGameIcon)&&container.CornerRadius.TopLeft==0,"Real module icons or square card style failed.");
            Require(Math.Abs(ring.Value-actualProgress)<.01&&!ring.IsHitTestVisible&&!ring.IsIndeterminate,"Current progress must be a read-only ring.");
            var rating=(XFEEDToolBox.Controls.EngineeringGradePicker)page.FindName("EngineeringGrade");rating.Value=5;await Task.Delay(100);
            Require(ViewModel.RefitGrade==5&&Math.Abs(ring.Value-actualProgress)<.01,"Target grade changed current progress.");
            await CapturePageAsync(page,Path.Combine(directory,"ship-editor.png"));
            var before=ViewModel.UpgradeEntries.Count;
            Invoke((Button)page.FindName("AddShipToListButton"));
            wait.Restart();while((ViewModel.RefitBusy||ViewModel.UpgradeBusy||!((Button)AppShellPage.Current!.FindName("UpgradeListButton")).Flyout.IsOpen)&&wait.Elapsed<TimeSpan.FromSeconds(20))await Task.Delay(100);
            var shell=AppShellPage.Current!;var listButton=(Button)shell.FindName("UpgradeListButton");var listFlyout=(Flyout)listButton.Flyout;
            var ownsEntry=ViewModel.UpgradeEntries.Any(e=>e.SourceKey==sample.InstanceKey&&e.Target.Grade==5);
            ((FrameworkElement)listFlyout.Content).UpdateLayout();
            var compactRows=Descendants(listFlyout.Content).OfType<Grid>().Where(g=>g.Tag as string=="compact-material-row").ToArray();
            Require(compactRows.Length>0&&compactRows.All(g=>g.ActualHeight is >0 and <=56),"Global list preview rows must use the compact layout.");
            list=new{before,after=ViewModel.UpgradeEntries.Count,ownsEntry,flyoutOpened=listFlyout.IsOpen,stayedOnEditor=shell.CurrentPage is RefitPage,materialRows=ViewModel.UpgradeNeeds.Count,
                flyoutRows=Descendants(listFlyout.Content).OfType<ItemsControl>().FirstOrDefault()?.Items.Count,compactRowHeight=compactRows.Max(g=>g.ActualHeight),previewWidth=((FrameworkElement)listFlyout.Content).ActualWidth,summary=ViewModel.UpgradeSummary,result=ViewModel.RefitResult};
            Require(ownsEntry&&listFlyout.IsOpen&&shell.CurrentPage is RefitPage&&ViewModel.UpgradeNeeds.Count>0,"Adding ship targets must open global materials Flyout.");
            await CaptureRefinementElementAsync((FrameworkElement)listFlyout.Content,Path.Combine(directory,"materials-flyout.png"));listFlyout.Hide();
            var original=ViewModel.RefitDocument!;
            var fixture=original with{EdsyHash="ui-probe",Slots=original.Slots.Select(s=>s.Slot==sample.Slot?s with{InstanceKey="",Origin="界面验收夹具",Target=null,Module=s.Module! with{Engineering=s.Module.Engineering! with{Level=3,Quality=.65}}}:s with{Target=null}).ToArray()};
            ViewModel.LoadRefitProbeDocument(fixture);ViewModel.RefitSlot=ViewModel.RefitVisibleSlots.Single(s=>s.Slot==sample.Slot);await Task.Delay(120);rating.Value=5;await Task.Delay(100);
            var module=ViewModel.RefitSlot!.Module!;var engineer=ViewModel.RefitEngineer!.Record;
            var plan=EngineeringPlanner.Calculate(EngineeringCatalog.Default,module,ViewModel.RefitBlueprint!,5,1,null,engineer,[]);
            progress=new{currentLevel=module.Engineering!.Level,currentQuality=module.Engineering.Quality,target=ViewModel.RefitGrade,ring=ring.Value,firstRollGrade=plan.Rolls.First().Grade,firstRollQuality=plan.Rolls.First().BeforeQuality,lowerGradesCharged=plan.Rolls.Any(r=>r.Grade<3)};
            Require(module.Engineering.Quality==.65&&ring.Value==65&&plan.Rolls.First().Grade==3&&plan.Rolls.First().BeforeQuality==.65&&!plan.Rolls.Any(r=>r.Grade<3),"G3 65% to G5 regression failed in the real controls.");
            ViewModel.RebuildRefitCommand.Execute(null);
            var engineeringPage=await Open(typeof(EngineeringPage));
            ViewModel.EngineeringType="Multi-cannon";
            ViewModel.EngineeringSource=ViewModel.EngineeringSources.First(s=>s.Key.Length==0);
            ViewModel.EngineeringBlueprint=ViewModel.EngineeringBlueprints.First(b=>b!="Overcharged Weapon");
            ViewModel.EngineeringSource=ViewModel.EngineeringSources.First(s=>s.Key==sample.InstanceKey);
            ViewModel.EngineeringGrade=5;await Task.Delay(100);
            var engineeringRing=(ProgressRing)engineeringPage.FindName("CurrentEngineeringProgress");
            Require(ViewModel.EngineeringBlueprint=="Overcharged Weapon"&&Math.Abs(engineeringRing.Value-actualProgress)<.01,"Owned engineering source retained another module's blueprint.");
            await CapturePageAsync(engineeringPage,Path.Combine(directory,"module-engineering.png"));
            var profileButton=(Button)shell.FindName("ProfileButton");var beforePage=shell.CurrentPage;profileButton.Flyout.ShowAt(profileButton);await Task.Delay(180);
            var profileFlyout=(Flyout)profileButton.Flyout;var profileContent=(FrameworkElement)profileFlyout.Content;
            var avatarFirst=((StackPanel)profileButton.Content).Children.First() is PersonPicture;
            await ViewModel.RefreshWalletAsync();await CaptureRefinementElementAsync(profileContent,Path.Combine(directory,"profile-flyout.png"));
            var profileStayed=ReferenceEquals(beforePage,shell.CurrentPage);
            Invoke((Button)shell.FindName("ProfileDetailsButton"));await Task.Delay(200);
            Require(shell.CurrentPage is ProfilePage,"Profile Flyout details must navigate to personal page.");
            var profilePage=(ProfilePage)shell.CurrentPage!;var map=(GalaxyMapView)profilePage.FindName("ExploredMap");
            var walletExpected=false;
            foreach(var process in System.Diagnostics.Process.GetProcessesByName("EliteDangerous64"))
                using(process)if(Runtime.Player.LoadGameAt is {} loaded&&loaded.UtcTicks>=process.StartTime.ToUniversalTime().Ticks-TimeSpan.FromSeconds(5).Ticks)walletExpected=true;
            profile=new{avatarFirst,profileStayed,profileFlyoutClosed=!profileFlyout.IsOpen,personalPage=shell.CurrentPage is ProfilePage,commanderIdBound=ViewModel.ProfileCommanderId==Runtime.Player.CommanderId,
                arx=ViewModel.ProfileArxText,wallet=ViewModel.WalletSnapshot,walletExpected,explored=map.Systems?.Count,recordedVisits=Runtime.Player.Profile.VisitedSystems.Count,statistics=ViewModel.ProfileStatistics.Count,ranks=ViewModel.ProfileRanks.Count};
            Require(avatarFirst&&profileStayed&&ViewModel.ProfileCommanderId==Runtime.Player.CommanderId&&map.Systems?.Count==Runtime.Player.Profile.VisitedSystems.Count,"Profile layout or explorer source failed.");
            Require((walletExpected?ViewModel.WalletSnapshot is not null:ViewModel.WalletSnapshot is null&&ViewModel.ProfileArxText=="未读取")&&ViewModel.ProfileStatistics.Count>0,"Wallet must belong to the fresh live commander session, or explicitly remain unread.");
            await CapturePageAsync(profilePage,Path.Combine(directory,"profile.png"));
            await Open(typeof(RefitPage));Invoke((Button)shell.FindName("ProfileFooterButton"));await Task.Delay(120);Require(shell.CurrentPage is ProfilePage,"Bottom profile entry failed.");
            var logPage=await Open(typeof(LogViewPage));await ViewModel.RefreshGameEventsAsync();
            var first=ViewModel.GameEventRows.Select(r=>r.Id).ToArray();await ViewModel.NextGameLogPageCommand.ExecuteAsync(null);var second=ViewModel.GameEventRows.Select(r=>r.Id).ToArray();
            ViewModel.GameEventSearch="FSDJump";await Task.Delay(350);await ViewModel.RefreshGameEventsAsync();var searchCorrect=ViewModel.GameEventRows.Count>0&&ViewModel.GameEventRows.All(r=>r.Record.Json.Contains("FSDJump"));
            ViewModel.GameEventSearch="";await Task.Delay(300);await ViewModel.LatestGameLogCommand.ExecuteAsync(null);
            logs=new{firstPage=first.Length,secondPage=second.Length,noOverlap=!first.Intersect(second).Any(),searchCorrect,total=Runtime.GameEvents.Query().Count,displayed=ViewModel.GameEventRows.Count};
            Require(first.Length==200&&second.Length>0&&!first.Intersect(second).Any()&&searchCorrect,"Full game log paging or search failed.");
            await CapturePageAsync(logPage,Path.Combine(directory,"game-events.png"));
            var home=await Open(typeof(MainPage));var toggle=(ToggleSwitch)home.FindName("ShieldRestartToggle");var defaultOff=!toggle.IsOn;toggle.IsOn=true;await Task.Delay(100);var disconnected=ViewModel.ShieldRestartStatus.Contains("验收");toggle.IsOn=false;
            shield=new{defaultOff,settingBound=ViewModel.ShieldZeroRestartEnabled==false,qaActionsDisconnected=disconnected,actualRestartInvoked=false};
            Require(defaultOff&&disconnected,"Shield switch must be off by default and isolated in UI QA.");
            await CapturePageAsync(home,Path.Combine(directory,"home.png"));
            interfaceChanges=await RunInterfaceRefinementProbeAsync(directory);
        }
        catch(Exception error){failure=error.ToString();}
        await File.WriteAllTextAsync(reportPath,JsonSerializer.Serialize(new{editor,progress,list,profile,logs,shield,interfaceChanges,failure,uiError=ViewModel.ErrorMessage,
            gamePositionUnchanged=initialSystem==Runtime.Player.Navigation.CurrentSystemAddress&&initialJump==Runtime.Player.Navigation.LastJumpAt,
            gameFlightCommandsInvoked=false,gameRestartInvoked=false,walletRequestedWriteAccess=false,walletNativeFunctionsInvoked=false,osInputSent=false,screenUse=false},new JsonSerializerOptions{WriteIndented=true}));
        MainWindow.Close();
    }
    private static async Task CaptureRefinementElementAsync(FrameworkElement element,string path)
    {
        var content=element;
        var parent=Microsoft.UI.Xaml.Media.VisualTreeHelper.GetParent(element);
        while(parent is not null)
        {
            if(parent is FlyoutPresenter presenter){element=presenter;break;}
            parent=Microsoft.UI.Xaml.Media.VisualTreeHelper.GetParent(parent);
        }
        await Task.Delay(250);
        element.UpdateLayout();var bitmap=new Microsoft.UI.Xaml.Media.Imaging.RenderTargetBitmap();await bitmap.RenderAsync(element);
        if(bitmap.PixelWidth==0||bitmap.PixelHeight==0){bitmap=new();await bitmap.RenderAsync(content);}
        var pixels=await bitmap.GetPixelsAsync();
        using var reader=Windows.Storage.Streams.DataReader.FromBuffer(pixels);var bytes=new byte[pixels.Length];reader.ReadBytes(bytes);
        using var output=File.Open(path,FileMode.Create);var encoder=await Windows.Graphics.Imaging.BitmapEncoder.CreateAsync(Windows.Graphics.Imaging.BitmapEncoder.PngEncoderId,output.AsRandomAccessStream());
        encoder.SetPixelData(Windows.Graphics.Imaging.BitmapPixelFormat.Bgra8,Windows.Graphics.Imaging.BitmapAlphaMode.Premultiplied,(uint)bitmap.PixelWidth,(uint)bitmap.PixelHeight,96,96,bytes);await encoder.FlushAsync();
    }
}