walkinside / sdk

Develop powerful desktop and web applications on top of Walkinside 3D engine
4 stars 4 forks source link

Preview: DataSDK - Copy CAD to FRT #32

Closed griemens closed 7 years ago

griemens commented 7 years ago

Hi,

I am trying to make an application that copies the CAD hierarchy information to the FRT hierarchy. It is based on the example in the repository, but much more simple code. But I have an exception thrown and I do not understand what I am doing wrong

The source code is here below. You can copy paste in a main.cs and run after you replaced the hard coded vrp file name @"D:\stabilizer\Stabilizer.vrp".

Any help is much appreciated Thanks

Code

using System;
using System.Collections.Generic;
using System.Linq;
using Comos.Walkinside;

namespace CopyFRT2CustomFRT
{
static class Program
{
    [STAThread]
    static void Main()
   {
        DoCopy(@"D:\stabilizer\Stabilizer.vrp");
   }

    static public void DoCopy(string vrpfile)
    {
        // Open walkinside project.
        IProject vrproject = ProjectManager.Open(vrpfile, AccessMode.ReadWrite);
        var branches = vrproject.Branches.GetRoots(BranchKind.Cad);
        var frtroot = new BranchCreationParams(BranchKind.Frt, "MyFRT");
        IEnumerable<BranchCreationParams> param = BuildCreationParameters(branches, frtroot,0);
        BranchCreationParams[] collection = param.ToArray(); // do actual calculations
        // Create the stuff.
        vrproject.Branches.Create(collection); // <- HERE IT THROWS EXCEPTION
    }

    static private IEnumerable<BranchCreationParams> BuildCreationParameters(IEnumerable<IBranch> branches, BranchCreationParams parent, int subrange)
    {
        foreach (var branch in branches)
        {
            var newbranch = new BranchCreationParams(
                referenceKey: Guid.NewGuid(),
                parentReference: parent.ReferenceKey,
                name: branch.Name,
                subtype: subrange,
                elementSources: new IBranch[] { branch });
            yield return newbranch;
            foreach (var child in BuildCreationParameters(branch.Children, newbranch, subrange + 1))
            {
                yield return child;
            }
        }
    }
}
}

Exception Information

Exception thrown: 'System.ArgumentException' in WalkinsideDataSDK.dll Additional information: Branch's ParentReference must point to a known branch, but was: <Comos.Walkinside.BranchCreationParams+RootBranchReference: Kind=Frt>. Please check that:

If there is a handler for this exception, the program may be safely continued.

kveretennicov commented 7 years ago

Try to include frtroot as the first element of your branch creation params. For example, add yield return parent; to the beginning of BuildCreationParameters().

When parentReference is specified, it must reference an earlier branch creation param from the same sequence.

griemens commented 7 years ago

Great it works now!

Below is the working code as reference.

Working Example Copy CAD to FRT using preview Walkinside DataSDK

using System;
using System.Collections.Generic;
using System.Linq;
using Comos.Walkinside;

namespace CopyFRT2CustomFRT
{
static class Program
{
    [STAThread]
    static void Main()
    {
        DoCopy(@"D:\stabilizer\Stabilizer.vrp");
    }

    static public void DoCopy(string vrpfile)
    {
        // Open walkinside project.
        IProject vrproject = ProjectManager.Open(vrpfile, AccessMode.ReadWrite);
        var branches = vrproject.Branches.GetRoots(BranchKind.Cad);
        IEnumerable<BranchCreationParams> param = BuildCreationParameters(branches);
        BranchCreationParams[] collection = param.ToArray(); // do actual calculations
        // Create the stuff.
        vrproject.Branches.Create(collection);
    }

    static private IEnumerable<BranchCreationParams> BuildCreationParameters(IEnumerable<IBranch> branches)
    {
        // Build a FRT root branch.
        var frtroot = new BranchCreationParams(BranchKind.Frt, "MyFRT");
        yield return frtroot; // Important to return also in the set else SDK will throw exception. 
        // Create all the children by making a copy of branches.
        IEnumerable<BranchCreationParams> param = BuildCreationParameters(branches, frtroot, 0);
        foreach (var p in param)
            yield return p;
    }

    static private IEnumerable<BranchCreationParams> BuildCreationParameters(IEnumerable<IBranch> branches, BranchCreationParams parent, int subrange)
    {
        foreach (var branch in branches)
        {
            var newbranch = new BranchCreationParams(
                referenceKey: Guid.NewGuid(),
                parentReference: parent.ReferenceKey,
                name: branch.Name,
                subtype: subrange,
                elementSources: new IBranch[] { branch });
            yield return newbranch;
            foreach (var child in BuildCreationParameters(branch.Children, newbranch, subrange + 1))
            {
                yield return child;
            }
        }
    }
}
}
kveretennicov commented 7 years ago

Glad it worked!

Be careful though with subrange + 1 that you use recursively. Branch subtype cannot exceed 999, so if you have deep nesting it will fail with another exception.

What do you use this subtype for, by the way? Do you query specific hierarchy levels by it?

griemens commented 7 years ago

The subtype I will use now only for debugging purposes.

But one day I would like to use it for fast isolation of a Process System.

Building a tree structure for operations and all branches of same subtype belong to the same system.

The tree would be organized by Unit > Sub Unit > Equipment/Line/…

The branches of a system will be the lines/Equipments etc … and a system can be located in different subunits or units.

So in Walkinside I would have a context menu item Isolate > Unit Sub Unit System

I know in 7.0 SDK there was a GetBranchesByRange(minrange,maxrange) or something. But I need to check if is not also deprecated or obsolete in recent versions.

Thanks again for fixing my bug ☺

griemens commented 7 years ago

Actualy I just found another bug in my code. ... IEnumerable param = BuildCreationParameters(branches, frtroot, 0); ... Must be ... IEnumerable param = BuildCreationParameters(branches, frtroot, 1); ... If using 0 the SDK BranchManager.GetRoots will return 2 FRT root nodes BranchManager.GetRoots(VRBranchKind.Frt).ToArray().Length; Conslusion if SubType = 0 Walkinside considers it a root branch.

kveretennicov commented 7 years ago

one day I would like to use [branch subtype] for fast isolation of a Process System

That should definitely work. Would a branch attribute "griemens/process-subsystem" work as well, while also being more explicit?

It's true that branch attribute support is a bit spotty at the moment, especially in Viewer SDK. But it's one of improvements we consider for upcoming release. The way I see it, we would need a good way to bulk-set branch attributes in Data SDK (such as via BranchCreationParams) and good querying APIs both in Viewer SDK and Data SDK.

Conslusion if SubType = 0 Walkinside considers it a root branch.

That's exactly the underlying logic used by Walkinside Viewer for several major releases and, possibly, before that. It is preserved mainly for legacy reasons.

In the future it will be changed to a more sane logic, where root branch is the one without parent, not the one with subtype 0. Meanwhile SDK clients are encouraged to retrieve root branches using GetRoots APIs, introduced to both Viewer SDK and Data SDK some time ago. When the logic finally changes, GetRoots will use it, improving both speed and correctness. Until then you can add .Where(b => b.Parent == null) to filter out "not true root" branches.

kveretennicov commented 7 years ago

Feel free to reopen if there is anything else to discuss.