Cancellation fixes and editor progress
This commit is contained in:
Родитель
d9f7895b3f
Коммит
b3c6b59721
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Editor",
|
||||
"rootNamespace": "",
|
||||
"name": "EditorTests",
|
||||
"rootNamespace": "Speckle.ConnectorUnity.Tests",
|
||||
"references": [
|
||||
"UnityEngine.TestRunner",
|
||||
"UnityEditor.TestRunner",
|
|
@ -1,96 +0,0 @@
|
|||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Objects.Utils;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Models;
|
||||
using Speckle.Core.Models.Extensions;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
|
||||
public class PerformanceTest
|
||||
{
|
||||
private static readonly string[] dataSource = new[]
|
||||
{
|
||||
"https://latest.speckle.dev/streams/24c3741255/commits/0925840e09"
|
||||
};
|
||||
|
||||
|
||||
//This method is much faster
|
||||
[Test, TestCaseSource(nameof(dataSource))]
|
||||
public void Receive_GetAwaiterResult(string stream)
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
Helpers.Receive(stream).GetAwaiter().GetResult();
|
||||
|
||||
stopwatch.Stop();
|
||||
Console.WriteLine(stopwatch.ElapsedMilliseconds);
|
||||
Assert.That(stopwatch.ElapsedMilliseconds, Is.Zero);
|
||||
}
|
||||
|
||||
|
||||
//This method takes around 46 seconds to complete
|
||||
[Test, TestCaseSource(nameof(dataSource))]
|
||||
public void Receive_TaskRunAsync(string stream)
|
||||
{
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Helpers.Receive(stream);
|
||||
}).GetAwaiter().GetResult();
|
||||
|
||||
stopwatch.Stop();
|
||||
Console.WriteLine(stopwatch.ElapsedMilliseconds);
|
||||
Assert.That(stopwatch.ElapsedMilliseconds, Is.Zero);
|
||||
}
|
||||
|
||||
// [UnityTest, TestCaseSource(nameof(dataSource))]
|
||||
// public IEnumerable Receive_Coroutine(string stream)
|
||||
// {
|
||||
// var stopwatch = Stopwatch.StartNew();
|
||||
//
|
||||
// Task t = Helpers.Receive(stream);
|
||||
// t.Start();
|
||||
//
|
||||
// yield return new WaitUntil(() => !t.IsCompleted || stopwatch.ElapsedMilliseconds >= 100000);
|
||||
//
|
||||
// stopwatch.Stop();
|
||||
// Console.WriteLine(stopwatch.ElapsedMilliseconds);
|
||||
// Assert.That(stopwatch.ElapsedMilliseconds, Is.Zero);
|
||||
// Assert.True(t.IsCompletedSuccessfully);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
//This method takes around 46 seconds to complete
|
||||
[Test]
|
||||
public void TestTriangulate()
|
||||
{
|
||||
|
||||
|
||||
Base b = Task.Run(async () =>
|
||||
{
|
||||
return await Helpers.Receive("https://speckle.xyz/streams/4a8fd0c6b6/commits/067bf723b1");
|
||||
}).GetAwaiter().GetResult();
|
||||
|
||||
|
||||
foreach (Base child in b.Traverse(b => b is Objects.Geometry.Mesh))
|
||||
{
|
||||
if(child is not Objects.Geometry.Mesh m) continue;
|
||||
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
|
||||
m.TriangulateMesh();
|
||||
|
||||
Console.WriteLine($"took {stopwatch.ElapsedMilliseconds:ms} to triangulate {child.id}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5a4f4baa829261d438b740c7d3028756
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,18 @@
|
|||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Speckle.ConnectorUnity.Tests
|
||||
{
|
||||
public abstract class ComponentTest<T> where T : Component
|
||||
{
|
||||
protected T sut;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
GameObject go = new();
|
||||
sut = go.AddComponent<T>();
|
||||
Assert.That(sut, Is.Not.Null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4e2fe277dd9c47ad998138dcdbb024ae
|
||||
timeCreated: 1686757093
|
|
@ -0,0 +1,55 @@
|
|||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using NUnit.Framework;
|
||||
using Speckle.ConnectorUnity.Components;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Models;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace Speckle.ConnectorUnity.Tests
|
||||
{
|
||||
[TestFixture, TestOf(typeof(RecursiveConverter))]
|
||||
public class ConvertToNativeTests : ComponentTest<RecursiveConverter>
|
||||
{
|
||||
private static IEnumerable<string> TestCases()
|
||||
{
|
||||
yield return @"https://latest.speckle.dev/streams/c1faab5c62/commits/704984e22d";
|
||||
}
|
||||
|
||||
private static Base Receive(string stream)
|
||||
{
|
||||
return Task.Run(async () => await Helpers.Receive(stream)).Result;
|
||||
}
|
||||
|
||||
[Test, TestCaseSource(nameof(TestCases))]
|
||||
public void ToNative_Sync_Passes(string stream)
|
||||
{
|
||||
Base testCase = Receive(stream);
|
||||
var resuts = sut.RecursivelyConvertToNative_Sync(testCase, null);
|
||||
Assert.That(resuts, Has.Count.GreaterThan(0));
|
||||
Assert.That(resuts, Has.Some.Matches<ConversionResult>(x => x.WasSuccessful())));
|
||||
AssertChildren();
|
||||
}
|
||||
|
||||
[UnityTest, TestCaseSource(nameof(TestCases))]
|
||||
public IEnumerator ToNative_Coroutine_Passes(string stream)
|
||||
{
|
||||
Base testCase = Receive(stream);
|
||||
GameObject parent = new("parent");
|
||||
|
||||
yield return sut.RecursivelyConvertToNative_Coroutine(testCase, parent);
|
||||
AssertChildren(parent);
|
||||
}
|
||||
|
||||
private void AssertChildren(IEnumerable<GameObject> children)
|
||||
{
|
||||
foreach (var res in children)
|
||||
{
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2dd598fed5008c44a815ba09e81a2d19
|
||||
guid: 3d9b0fc7baaf51a4a8e2bcefad8bd7b3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"name": "PlayModeTests",
|
||||
"rootNamespace": "Speckle.ConnectorUnity.Tests",
|
||||
"references": [
|
||||
"UnityEngine.TestRunner",
|
||||
"UnityEditor.TestRunner",
|
||||
"Speckle.ConnectorUnity.Components",
|
||||
"Utils"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": true,
|
||||
"precompiledReferences": [
|
||||
"nunit.framework.dll",
|
||||
"SpeckleCore2.dll",
|
||||
"Objects.dll"
|
||||
],
|
||||
"autoReferenced": false,
|
||||
"defineConstraints": [
|
||||
"UNITY_INCLUDE_TESTS"
|
||||
],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 79301723eb79d2745ab1e1a9360f6f2d
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,96 @@
|
|||
#nullable enable
|
||||
using System;
|
||||
using System.Collections;
|
||||
using NUnit.Framework;
|
||||
using Speckle.ConnectorUnity.Components;
|
||||
using Speckle.Core.Models;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace Speckle.ConnectorUnity.Tests
|
||||
{
|
||||
[TestFixture, TestOf(typeof(SpeckleReceiver))]
|
||||
public sealed class SpeckleReceiverTests : ComponentTest<SpeckleReceiver>
|
||||
{
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ReceiveAsync_Succeeds()
|
||||
{
|
||||
yield return null;
|
||||
|
||||
var task = new Utils.Utils.WaitForTask<Base>(async () => await sut.ReceiveAsync(default));
|
||||
yield return task;
|
||||
Base myBase = task.Result;
|
||||
Assert.That(myBase, Is.Not.Null);
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ReceiveAndConvert_Async_Succeeds()
|
||||
{
|
||||
Transform expectedParent = new GameObject("parent").transform;
|
||||
yield return null;
|
||||
|
||||
bool wasSuccessful = false;
|
||||
Transform? actualParent = null;
|
||||
|
||||
sut.OnComplete.AddListener(t =>
|
||||
{
|
||||
wasSuccessful = true;
|
||||
actualParent = t;
|
||||
});
|
||||
sut.OnErrorAction.AddListener((_, ex) => throw new Exception("Failed", ex));
|
||||
|
||||
sut.ReceiveAndConvert_Async(expectedParent);
|
||||
|
||||
yield return new WaitUntil(() => wasSuccessful);
|
||||
|
||||
Assert.That(actualParent, Is.EqualTo(expectedParent));
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ReceiveAndConvert_Routine_Succeeds()
|
||||
{
|
||||
Transform expectedParent = new GameObject("parent").transform;
|
||||
yield return null;
|
||||
|
||||
bool wasSuccessful = false;
|
||||
Transform? actualParent = null;
|
||||
|
||||
sut.OnComplete.AddListener(t =>
|
||||
{
|
||||
wasSuccessful = true;
|
||||
actualParent = t;
|
||||
});
|
||||
sut.OnErrorAction.AddListener((_, ex) => throw new Exception("Failed", ex));
|
||||
|
||||
yield return sut.ReceiveAndConvert_Routine(expectedParent);
|
||||
|
||||
yield return new WaitUntil(() => wasSuccessful);
|
||||
|
||||
Assert.That(actualParent, Is.EqualTo(expectedParent));
|
||||
}
|
||||
|
||||
[UnityTest]
|
||||
public IEnumerator ReceiveAndConvert_Coroutine_Succeeds()
|
||||
{
|
||||
Transform expectedParent = new GameObject("parent").transform;
|
||||
yield return null;
|
||||
|
||||
bool wasSuccessful = false;
|
||||
Transform? actualParent = null;
|
||||
|
||||
sut.OnComplete.AddListener(t =>
|
||||
{
|
||||
wasSuccessful = true;
|
||||
actualParent = t;
|
||||
});
|
||||
sut.OnErrorAction.AddListener((_, ex) => throw new Exception("Failed", ex));
|
||||
|
||||
yield return sut.StartCoroutine(sut.ReceiveAndConvert_Routine(expectedParent));
|
||||
|
||||
yield return new WaitUntil(() => wasSuccessful);
|
||||
|
||||
Assert.That(actualParent, Is.EqualTo(expectedParent));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1756c50dd28a4e341a70866daa68a8d5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -1,11 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Speckle.ConnectorUnity.Utils;
|
||||
using Speckle.Core.Api;
|
||||
using Speckle.Core.Logging;
|
||||
using Speckle.Core.Models;
|
||||
using Speckle.Core.Models.GraphTraversal;
|
||||
using UnityEditor;
|
||||
|
@ -21,51 +16,68 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
|||
private static bool generateAssets = false;
|
||||
private bool foldOutStatus = true;
|
||||
private Texture2D? previewImage;
|
||||
|
||||
|
||||
public override async void OnInspectorGUI()
|
||||
{
|
||||
var speckleReceiver = (SpeckleReceiver)target;
|
||||
|
||||
DrawDefaultInspector();
|
||||
|
||||
//Preview image
|
||||
foldOutStatus = EditorGUILayout.Foldout(foldOutStatus, "Preview Image");
|
||||
if (foldOutStatus)
|
||||
{
|
||||
Rect rect = GUILayoutUtility.GetAspectRect(7f / 4f);
|
||||
if (previewImage != null) GUI.DrawTexture(rect, previewImage);
|
||||
foldOutStatus = EditorGUILayout.Foldout(foldOutStatus, "Preview Image");
|
||||
if (foldOutStatus)
|
||||
{
|
||||
Rect rect = GUILayoutUtility.GetAspectRect(7f / 4f);
|
||||
if (previewImage != null) GUI.DrawTexture(rect, previewImage);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Receive button
|
||||
bool receive = GUILayout.Button("Receive!");
|
||||
|
||||
bool selection = EditorGUILayout.ToggleLeft("Generate Assets", generateAssets);
|
||||
if (generateAssets != selection)
|
||||
{
|
||||
generateAssets = selection;
|
||||
UpdateGenerateAssets();
|
||||
}
|
||||
|
||||
|
||||
//TODO: Draw events in a collapsed region
|
||||
|
||||
|
||||
if (receive)
|
||||
|
||||
//Receive settings
|
||||
{
|
||||
try
|
||||
bool prev = GUI.enabled;
|
||||
GUI.enabled = !speckleReceiver.IsReceiving;
|
||||
//Receive button
|
||||
bool receive = GUILayout.Button("Receive!");
|
||||
|
||||
bool selection = EditorGUILayout.ToggleLeft("Generate Assets", generateAssets);
|
||||
if (generateAssets != selection)
|
||||
{
|
||||
await ReceiveSelection();
|
||||
generateAssets = selection;
|
||||
UpdateGenerateAssets();
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
GUI.enabled = prev;
|
||||
|
||||
|
||||
if (receive && !speckleReceiver.IsReceiving)
|
||||
{
|
||||
Debug.Log($"Receive operation cancelled\n{ex}", this);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Failed to receive selection {ex}", this);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
int id = Progress.Start(
|
||||
"Receiving Speckle data",
|
||||
"Fetching commit data",
|
||||
Progress.Options.Indefinite | Progress.Options.Sticky);
|
||||
Progress.SetPriority(id, Progress.Priority.High);
|
||||
Progress.ShowDetails();
|
||||
try
|
||||
{
|
||||
await ReceiveSelection(id).ConfigureAwait(true);
|
||||
Progress.Finish(id);
|
||||
}
|
||||
catch (OperationCanceledException ex)
|
||||
{
|
||||
Progress.Finish(id, Progress.Status.Canceled);
|
||||
Debug.Log($"Receive operation cancelled\n{ex}", this);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Progress.Finish(id, Progress.Status.Failed);
|
||||
Debug.LogError($"Receive operation failed {ex}", this);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,32 +106,57 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
|||
previewImage = null;
|
||||
((SpeckleReceiver)target).GetPreviewImage(t => previewImage = t);
|
||||
}
|
||||
|
||||
private async Task ReceiveSelection()
|
||||
|
||||
private async Task ReceiveSelection(int progressId)
|
||||
{
|
||||
var speckleReceiver = (SpeckleReceiver)target;
|
||||
|
||||
Base commitObject = await ReceiveCommit();
|
||||
|
||||
int childrenConverted = 0;
|
||||
float totalChildren = commitObject.totalChildrenCount;
|
||||
|
||||
bool shouldCancel = false;
|
||||
|
||||
Progress.RegisterCancelCallback(progressId, () =>
|
||||
{
|
||||
speckleReceiver.Cancel();
|
||||
shouldCancel = true;
|
||||
return true;
|
||||
});
|
||||
|
||||
Base commitObject;
|
||||
try
|
||||
{
|
||||
var token = speckleReceiver.BeginOperation();
|
||||
commitObject = await Task.Run(async () => await ReceiveCommit(progressId).ConfigureAwait(false),
|
||||
token
|
||||
)
|
||||
.ConfigureAwait(true);
|
||||
}
|
||||
finally
|
||||
{
|
||||
speckleReceiver.FinishOperation();
|
||||
}
|
||||
|
||||
int childrenConverted = 0;
|
||||
int childrenFailed = 0;
|
||||
|
||||
int totalChildren = (int) Math.Min(commitObject.totalChildrenCount, int.MaxValue);
|
||||
float totalChildrenFloat = commitObject.totalChildrenCount;
|
||||
|
||||
var convertProgress = Progress.Start("Converting To Native", "Preparing...", Progress.Options.Indefinite | Progress.Options.Sticky, progressId);
|
||||
|
||||
bool BeforeConvert(TraversalContext context)
|
||||
{
|
||||
Base b = context.current;
|
||||
|
||||
//NOTE: progress wont reach 100% because not all objects are convertable
|
||||
float progress = childrenConverted / totalChildren;
|
||||
|
||||
float progress = (childrenConverted + childrenFailed) / totalChildrenFloat;
|
||||
|
||||
if (shouldCancel) return false;
|
||||
|
||||
shouldCancel = EditorUtility.DisplayCancelableProgressBar(
|
||||
"Converting To Native...",
|
||||
$"{b.speckle_type} - {b.id}",
|
||||
progress);
|
||||
|
||||
|
||||
return true;
|
||||
return !shouldCancel;
|
||||
}
|
||||
|
||||
foreach (var conversionResult in speckleReceiver.Converter.RecursivelyConvertToNative_Enumerable(
|
||||
|
@ -134,19 +171,27 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
|||
}
|
||||
else
|
||||
{
|
||||
childrenFailed++;
|
||||
Debug.LogWarning(
|
||||
$"Failed to convert Speckle object of type {speckleObject.speckle_type}\n{ex}",
|
||||
this);
|
||||
}
|
||||
|
||||
Progress.Report(progressId, childrenConverted + childrenFailed, totalChildren, "Receiving objects");
|
||||
|
||||
if (shouldCancel) break;
|
||||
}
|
||||
|
||||
Debug.Log(
|
||||
shouldCancel
|
||||
? $"Stopped converting to native. Created {childrenConverted} {nameof(GameObject)}s: Responding to cancel through editor"
|
||||
: $"Finished converting to native. Created {childrenConverted} {nameof(GameObject)}s ",
|
||||
this);
|
||||
var resultString = $"{childrenConverted}{nameof(GameObject)}s created, {childrenFailed} objects failed to convert";
|
||||
|
||||
Debug.Log(shouldCancel
|
||||
? $"Stopped converting to native: The operation has been cancelled - {resultString}\n "
|
||||
: $"Finished converting to native.\n{resultString}",
|
||||
speckleReceiver);
|
||||
|
||||
Progress.Finish(convertProgress);
|
||||
|
||||
if (shouldCancel) throw new OperationCanceledException("Conversion operation canceled through editor dialogue");
|
||||
}
|
||||
|
||||
private void UpdateGenerateAssets()
|
||||
|
@ -155,65 +200,73 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
|||
speckleReceiver.Converter.AssetCache.nativeCaches = NativeCacheFactory.GetDefaultNativeCacheSetup(generateAssets);
|
||||
}
|
||||
|
||||
private async Task<Base> ReceiveCommit()
|
||||
private async Task<Base> ReceiveCommit(int progressId)
|
||||
{
|
||||
var speckleReceiver = (SpeckleReceiver)target;
|
||||
|
||||
speckleReceiver.BeginOperation();
|
||||
|
||||
string serverLogName = speckleReceiver.Account.Client?.ServerUrl ?? "Speckle";
|
||||
string message = $"Receiving data from {serverLogName}...";
|
||||
|
||||
EditorUtility.DisplayProgressBar(message, "Fetching data", 0f);
|
||||
string serverLogName = speckleReceiver.Account.Client?.ServerUrl ?? "Speckle";
|
||||
|
||||
int transport = Progress.Start($"Downloading data from {serverLogName}", "Waiting...", Progress.Options.Sticky, progressId);
|
||||
int deserialize = Progress.Start("Deserializing data", "Waiting...", Progress.Options.Sticky, progressId);
|
||||
|
||||
var totalObjectCount = 1;
|
||||
void OnTotalChildrenKnown(int count)
|
||||
{
|
||||
totalObjectCount = count;
|
||||
EditorApplication.delayCall += () => EditorUtility.DisplayProgressBar(message, $"Fetching data {0}/{totalObjectCount}", 0f);
|
||||
Progress.Report(progressId, 0, totalObjectCount, "Receiving objects");
|
||||
}
|
||||
|
||||
void OnProgress(ConcurrentDictionary<string, int> dict)
|
||||
{
|
||||
var currentProgress = dict.Values.Average();
|
||||
var progress = (float) currentProgress / totalObjectCount;
|
||||
EditorApplication.delayCall += () =>
|
||||
bool r = dict.TryGetValue("RemoteTransport", out int rtProgress);
|
||||
bool l = dict.TryGetValue("SQLite", out int ltProgress);
|
||||
if (r || l)
|
||||
{
|
||||
bool shouldCancel = EditorUtility.DisplayCancelableProgressBar(message,
|
||||
$"Downloading data {currentProgress}/{totalObjectCount}",
|
||||
progress);
|
||||
|
||||
if (shouldCancel)
|
||||
{
|
||||
speckleReceiver.CancellationTokenSource!.Cancel();
|
||||
}
|
||||
};
|
||||
var fetched = (rtProgress + ltProgress);
|
||||
Progress.Report(transport, fetched, totalObjectCount, $"{fetched}/{totalObjectCount}");
|
||||
}
|
||||
|
||||
if (dict.TryGetValue("DS", out int tsProgress))
|
||||
{
|
||||
tsProgress--; //The root object isn't included, so we add an extra 1
|
||||
Progress.Report(deserialize,tsProgress, totalObjectCount, $"{tsProgress}/{totalObjectCount}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Base commitObject;
|
||||
try
|
||||
{
|
||||
speckleReceiver.OnTotalChildrenCountKnown.AddListener(OnTotalChildrenKnown);
|
||||
speckleReceiver.OnReceiveProgressAction.AddListener(OnProgress);
|
||||
commitObject = await speckleReceiver.ReceiveAsync(speckleReceiver.CancellationToken).ConfigureAwait(false);
|
||||
commitObject = await speckleReceiver.ReceiveAsync(speckleReceiver.CancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
Progress.Finish(transport);
|
||||
Progress.Finish(deserialize);
|
||||
}
|
||||
catch(Exception ex)
|
||||
catch(OperationCanceledException)
|
||||
{
|
||||
throw new SpeckleException("Failed to receive commit", ex);
|
||||
Progress.Finish(transport, Progress.Status.Canceled);
|
||||
Progress.Finish(deserialize, Progress.Status.Canceled);
|
||||
throw;
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
Progress.Finish(transport, Progress.Status.Failed);
|
||||
Progress.Finish(deserialize, Progress.Status.Failed);
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
speckleReceiver.OnTotalChildrenCountKnown.RemoveListener(OnTotalChildrenKnown);
|
||||
speckleReceiver.OnReceiveProgressAction.RemoveListener(OnProgress);
|
||||
EditorApplication.delayCall += EditorUtility.ClearProgressBar;
|
||||
speckleReceiver.FinishOperation();
|
||||
}
|
||||
|
||||
return commitObject;
|
||||
}
|
||||
|
||||
[MenuItem("GameObject/Speckle/Speckle Connector", false, 10)]
|
||||
static void CreateCustomGameObject(MenuCommand menuCommand) {
|
||||
static void CreateCustomGameObject(MenuCommand menuCommand)
|
||||
{
|
||||
// Create a custom game object
|
||||
GameObject go = new GameObject("Speckle Connector");
|
||||
// Ensure it gets reparented if this was a context click (otherwise does nothing)
|
||||
|
|
|
@ -106,12 +106,6 @@ namespace Speckle.ConnectorUnity.Components.Editor
|
|||
SceneManager.GetActiveScene().GetRootGameObjects(),
|
||||
go => go.activeInHierarchy);
|
||||
}
|
||||
|
||||
private void CancelSend()
|
||||
{
|
||||
((SpeckleReceiver)target).CancellationTokenSource?.Cancel();
|
||||
EditorApplication.delayCall += EditorUtility.ClearProgressBar;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,9 +78,11 @@ namespace Speckle.ConnectorUnity.Components
|
|||
{
|
||||
converted = this.converted;
|
||||
exception = this.exception;
|
||||
return this.exception == null;
|
||||
return WasSuccessful();
|
||||
}
|
||||
|
||||
public bool WasSuccessful() => this.exception == null;
|
||||
|
||||
public Base SpeckleObject => traversalContext.current;
|
||||
}
|
||||
|
||||
|
@ -136,10 +138,10 @@ namespace Speckle.ConnectorUnity.Components
|
|||
Dictionary<Base, GameObject?> created = new();
|
||||
foreach (var conversionResult in ConvertTree(objectsToConvert, parent, created))
|
||||
{
|
||||
if (!isActiveAndEnabled) throw new InvalidOperationException($"Cannot convert objects while {GetType()} is disabled");
|
||||
|
||||
yield return conversionResult;
|
||||
}
|
||||
|
||||
Debug.Log($"Finished converting {rootObject.id} to native. Created {created.Count} {nameof(GameObject)}s ",this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -40,29 +40,45 @@ namespace Speckle.ConnectorUnity.Components
|
|||
|
||||
public RecursiveConverter Converter { get; private set; }
|
||||
|
||||
#nullable enable
|
||||
[Header("Events")]
|
||||
[HideInInspector]
|
||||
public CommitSelectionEvent OnCommitSelectionChange;
|
||||
public CommitSelectionEvent OnCommitSelectionChange = new();
|
||||
[HideInInspector]
|
||||
public OperationProgressEvent OnReceiveProgressAction;
|
||||
public OperationProgressEvent OnReceiveProgressAction = new();
|
||||
[HideInInspector]
|
||||
public ErrorActionEvent OnErrorAction;
|
||||
public ErrorActionEvent OnErrorAction = new();
|
||||
[HideInInspector]
|
||||
public ChildrenCountHandler OnTotalChildrenCountKnown;
|
||||
public ChildrenCountHandler OnTotalChildrenCountKnown = new();
|
||||
[HideInInspector]
|
||||
public ReceiveCompleteHandler OnComplete;
|
||||
public ReceiveCompleteHandler OnComplete = new();
|
||||
|
||||
#nullable enable
|
||||
protected internal CancellationTokenSource? CancellationTokenSource { get; private set; }
|
||||
protected internal CancellationToken CancellationToken => CancellationTokenSource?.Token ?? default;
|
||||
protected CancellationTokenSource? CancellationTokenSource { get; private set; }
|
||||
public CancellationToken CancellationToken => CancellationTokenSource?.Token ?? default;
|
||||
public bool IsReceiving => CancellationTokenSource != null;
|
||||
|
||||
/// <summary>
|
||||
/// Cancels any current receive operations
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Note, this does not cancel any currently executing ConvertToNative, just the <see cref="Operations.Receive"/>.
|
||||
/// </remarks>
|
||||
/// <returns><see langword="true"/> if the cancellation request was made. <see langword="false"/> if there was no pending operation to cancel (see <see cref="IsReceiving"/>)</returns>
|
||||
public bool Cancel()
|
||||
{
|
||||
if (CancellationTokenSource == null) return false;
|
||||
CancellationTokenSource.Cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Receive the selected <see cref="Commit"/> object, and converts ToNative as children of <paramref name="parent"/>
|
||||
/// </summary>
|
||||
/// <param name="parent"></param>
|
||||
/// <param name="predicate"></param>
|
||||
/// <remarks>function does not throw, instead calls <see cref="OnErrorAction"/>, and calls <see cref="OnComplete"/> on complteion</remarks>
|
||||
/// <param name="parent">Optional parent <see cref="Transform"/> for the created root <see cref="GameObject"/>s</param>
|
||||
/// <param name="predicate">A filter function to allow for selectively excluding certain objects from being converted</param>
|
||||
/// <remarks>function does not throw, instead calls <see cref="OnErrorAction"/>, and calls <see cref="OnComplete"/> upon completion</remarks>
|
||||
/// <seealso cref="ReceiveAsync(System.Threading.CancellationToken)"/>
|
||||
/// <seealso cref="RecursiveConverter.RecursivelyConvertToNative_Enumerable"/>
|
||||
public IEnumerator ReceiveAndConvert_Routine(Transform? parent, Predicate<TraversalContext>? predicate = null)
|
||||
{
|
||||
if (IsReceiving)
|
||||
|
@ -172,18 +188,20 @@ namespace Speckle.ConnectorUnity.Components
|
|||
/// Starts a new receive operation with a <see cref="CancellationToken"/>
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">already receiving</exception>
|
||||
protected internal void BeginOperation()
|
||||
protected internal CancellationToken BeginOperation()
|
||||
{
|
||||
if (IsReceiving) throw new InvalidOperationException("A pending receive operation has already started");
|
||||
|
||||
CancellationTokenSource?.Dispose();
|
||||
CancellationTokenSource = new();
|
||||
|
||||
return CancellationTokenSource.Token;
|
||||
}
|
||||
|
||||
protected internal void FinishOperation()
|
||||
{
|
||||
if (!IsReceiving) throw new InvalidOperationException("No pending operations to finish");
|
||||
|
||||
|
||||
CancellationTokenSource!.Dispose();
|
||||
CancellationTokenSource = null;
|
||||
}
|
||||
|
@ -388,6 +406,8 @@ namespace Speckle.ConnectorUnity.Components
|
|||
Application.OpenURL(url);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
public string GetSelectedUrl()
|
||||
{
|
||||
string serverUrl = Account.Selected!.serverInfo.url;
|
||||
|
@ -423,12 +443,16 @@ namespace Speckle.ConnectorUnity.Components
|
|||
Account.RefreshOptions();
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
public void OnDisable()
|
||||
{
|
||||
CancellationTokenSource?.Cancel();
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
{
|
||||
CancellationTokenSource?.Dispose();
|
||||
}
|
||||
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
//pass
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#nullable enable
|
||||
using System;
|
||||
using System.Collections;
|
||||
using Speckle.Core.Models;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
|
@ -94,5 +94,33 @@ namespace Speckle.ConnectorUnity.Utils
|
|||
Texture2D? texture = DownloadHandlerTexture.GetContent(www);
|
||||
callback.Invoke(texture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine <see cref="CustomYieldInstruction"/> that starts and waits for an async <see cref="System.Threading.Tasks.Task"/>
|
||||
/// to complete.
|
||||
/// </summary>
|
||||
/// <remarks>Useful for running async tasks from coroutines</remarks>
|
||||
public class WaitForTask : CustomYieldInstruction
|
||||
{
|
||||
public readonly Task Task;
|
||||
public override bool keepWaiting => !Task.IsCompleted;
|
||||
|
||||
public WaitForTask(Func<Task> function)
|
||||
{
|
||||
Task = Task.Run(function);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="WaitForTask"/>
|
||||
public sealed class WaitForTask<TResult> : CustomYieldInstruction
|
||||
{
|
||||
public readonly Task<TResult> Task;
|
||||
public TResult Result => Task.Result;
|
||||
public override bool keepWaiting => !Task.IsCompleted;
|
||||
public WaitForTask(Func<Task<TResult>> function)
|
||||
{
|
||||
this.Task = System.Threading.Tasks.Task.Run(function);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
|
|||
{
|
||||
get
|
||||
{
|
||||
if (Options == null) return null;
|
||||
if (Options is null) return null;
|
||||
if (SelectedIndex < 0 || SelectedIndex >= Options.Length) return null;
|
||||
return Options[SelectedIndex];
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
|
|||
|
||||
protected void GenerateOptions(IList<TOption> source, Func<TOption, int, bool> isDefault)
|
||||
{
|
||||
List<TOption> optionsToAdd = new List<TOption>(source.Count);
|
||||
List<TOption> optionsToAdd = new (source.Count);
|
||||
int defaultOption = -1;
|
||||
int index = 0;
|
||||
foreach (TOption? a in source)
|
||||
|
@ -77,4 +77,4 @@ namespace Speckle.ConnectorUnity.Wrappers.Selection
|
|||
OnSelectionChange?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче