Failures: Insert iPartMember or getting iPartTableRow by Parameters

Failures: Insert iPartMember or getting iPartTableRow by Parameters

josh.nieman
Advocate Advocate
1,043 Views
12 Replies
Message 1 of 13

Failures: Insert iPartMember or getting iPartTableRow by Parameters

josh.nieman
Advocate
Advocate


We have an iPartFactory for fasteners.  That FullDocumentName is stored as string "ScrewSource"
I have no trouble inserting that iPartFactory with default values.

I have been hitting my head against the wall trying to insert the iPartFactory by specifying the iPartTableRow by values.

From the API help, I see that the  iPartTableRow index can be specified via string of identifiers, quoting:

If a string is input it should be in a part identifier format(i.e. "[Height=1.000 in][Length=2.000 in][Radius=0.250 in]"

 

However, I cannot get this to work.

It fails with an error that leaves me wondering if there is some parsing problem.
I've verified the column names I'm calling, as well as that there is a unique row with all the values I call.
Type:Code
BoltThread:Dia
Bolt:TotalLength
are all parameters/column names in the iPartFactory table.  The values I call out are all the values of a fastener I have active in another window.  I'm stumped at where to begin.  Error in attached screenshot (if it works)

Public Function InsertScrew() As ComponentOccurrence
        Dim ScrewDoc As Inventor.PartDocument = InvApp.Documents.ItemByName(ScrewSource)
        Dim PCD As PartComponentDefinition = ScrewDoc.ComponentDefinition
        ScrewFactory = PCD.iPartFactory

        Dim oMatrix As Matrix = InvTG.CreateMatrix
        oMatrix.SetTranslation(InvTG.CreateVector(0, 0, 30), True)

        Dim AsmCompDef As AssemblyComponentDefinition = AsmDoc.ComponentDefinition
        Dim AsmOccs As ComponentOccurrences = AsmCompDef.Occurrences
        Dim screwstring As String = "[Type:Code=SHCS][BoltThread:Dia=0.250][Bolt:TotalLength=0.5]"
        Dim ScrewTableRow As iPartTableRow = ScrewFactory.TableRows.Item(screwstring)

        Dim thisScrew As ComponentOccurrence = AsmOccs.AddiPartMember(ScrewSource, oMatrix, ScrewList)

        Return TheScrew
    End Function

 

 

Error:

Clipboard 2019-01-10 at 7.57.36 AM.png

 

0 Likes
1,044 Views
12 Replies
Replies (12)
Message 2 of 13

josh.nieman
Advocate
Advocate

Note, I've tried it with metric values such as "M8X1.25" and "55 mm" and it fails comparably.

0 Likes
Message 3 of 13

josh.nieman
Advocate
Advocate

Additional note:  🙂

I can iterate through the whole iPartTableRows collection and pass them to an array, and use string comparators to find the row, then insert the iPartFactory by iPartTableRow index as integer, but it's an undesirable delay (3-11sec... it varies a lot for unknown reasons) to pass the rows to an array.

0 Likes
Message 4 of 13

JamieVJohnson2
Collaborator
Collaborator

How many items do you have in that array?  If alot, then look at system.threading.parallel there are several for loop options.

Inventor does this (don't add or remove from inventor's data set without using synclock), I've posted some examples, and it will use all your cpu power to get the job done.

 

Jamie Johnson : Owner / Sisu Lissom, LLC https://sisulissom.com/
0 Likes
Message 5 of 13

josh.nieman
Advocate
Advocate

Already doing that, thanks for the tip.  Definitely good.

The row count is around 2500

 

I'd much rather not, though, and see what performance looks like with the above-mentioned method.

FWIW:

        Dim parScrewList = New List(Of iPartTableRow)
        Dim xPar = ScrewFactory.TableRows.AsParallel
        For Each i In xPar
            SyncLock parScrewList
                ScrewList.Add(i)
            End SyncLock
        Next

But again,

I really want to figure out what's going wrong in the iPartTableRow definition.

 

Thanks

0 Likes
Message 6 of 13

JamieVJohnson2
Collaborator
Collaborator

You are searching the entire list...  This is not necessary, and your not really getting much benefit from the asparallel simplicity.

    Public Function FindActiveNode(bnParent As Inventor.BrowserNode) As Inventor.BrowserNode
        Dim bnReturn As BrowserNode = Nothing
        System.Threading.Tasks.Parallel.For(1, bnParent.BrowserNodes.Count + 1,
            Sub(index, state)
                Dim bn As BrowserNode = bnParent.BrowserNodes(index)
                If bn.Selected = True Then
                    bnReturn = bn
                    state.Stop()
                End If
                If bnReturn Is Nothing AndAlso bn.BrowserNodes.Count > 0 AndAlso bn.Expanded = True Then
                    bn = FindActiveNode(bn)
                    If bn IsNot Nothing Then
                        bnReturn = bn
                        state.Stop()
                    End If
                End If
            End Sub)
        Return bnReturn
    End Function

Here is an example parallel structure to search browser nodes (also very ugly text search process).  This one is nested, so you probably don't need the second If statement structure for the nesting of deeper node tiers, because iparts is a single tier table.  Notice the control level of creating the state, and having the index.  The state allows me to call up a set of brakes for all threads to stop.  So you should be searching for the name you want, then stop.  Instead of collecting all the names up front.  Of course this depends on your other code structure, if caching the entire 2500 part list into memory is faster than performing fast searches repeatedly because you repeat the search too many times, then that changes everything.

 

Jamie Johnson : Owner / Sisu Lissom, LLC https://sisulissom.com/
0 Likes
Message 7 of 13

josh.nieman
Advocate
Advocate

That's not what I was doing but thanks.


Again.  I'm not unhappy with searching / casting rows to arrays or searching through...

As stated (clearly, I thought) in the OP, I'm getting inexplicable errors in what appears to be a straightforward call, as described in the API, and am curious about what's failing there.

I've got a couple alternate methods to get to the end result, but I'd like get the above method working, so I can benchmark it as well - or at least submit a bug report if there's something /actually/ wrong.

0 Likes
Message 8 of 13

JamieVJohnson2
Collaborator
Collaborator

back to the basics then.

 

iPartTableRows.Item property expects an integer (Index of row in table is a number in a count of rows, a 1 based array).

you are giving it a string (MemberName).

error is thrown.

 

IF you wish to search the list of rows quickly by name then I suggest an 'indexing' of the names.

dim lstName as new sortedlist(of string, integer)

for each row as iPartTableRow in iPartTableRows

lstName.add(row.MemberName, row.Index)

next

 

then when you want to find a row by its name, you can search your short list, to get the index

iPartTableRows.Index(lstName(ConstructedMemberName))

 

Clear enough?

 

Something a little more advanced for you to play with:

dim lstRows as new list(of iPartTableRow) 

for each row as iPartTableRow in iPartTableRows

lstRows.add(row)

next

'should be same as stating lstRows.addrange(iPartTableRows), but sometimes you need to spell it out

lstRows.Find(Function(x) x.MemberName = ConstructedMemberName)

 

using the .net list(of T) object's find function you can create custom inline functions to search for item by one of its properties.

 

Did we get you there yet?

 

Jamie Johnson : Owner / Sisu Lissom, LLC https://sisulissom.com/
0 Likes
Message 9 of 13

josh.nieman
Advocate
Advocate

Let me rephrase and simplify my question.

Does anyone have a functional sample that can show the optional input of "Row" as a string?  I tried that method and am getting errors I cannot get past.  I'm curious how the "Row" as a string should function.

From the API Help:

 

Syntax

ComponentOccurrences.AddiPartMember( FactoryFileName As String, Position As Matrix, [Row] As Variant ) As ComponentOccurrence

Parameters

NameTypeDescription
FactoryFileNameStringInput String that specifies the full filename of the iPart factory.

 

PositionMatrixInput Matrix that defines the position and orientation of the iPart placement within the assembly.

 

RowVariantOptional input Variant that specifies the row for the member within the factory. The row index is specified either by a Long (row index), a String (part identifier, i.e. ''[Height=1.000 in][Length=2.000 in][Radius=0.250 in]''), or an iPartTableRow object.

This is an optional argument whose default value is null.

 

 

 

 

0 Likes
Message 10 of 13

JamieVJohnson2
Collaborator
Collaborator

Not to sound rude, but I don't think your cluing into the API properly here, so I will attempt to spell it out.  So please forgive me.

Your code is throwing an error on GET the row by index.  (requires integer not string)

Your explanation is citing the desire to ADD a row by string.  (optional input is a string formatted for each property in the table)

These are separate processes with separate requirements and structures.

Your attempt for format the string is probably fine, but you CANT use that in the GET row by Item function.

You CAN use that in the ADD row function IF the string is formatted to match the table's field names and data value types properly.  However the ADD function never gets a change to run or throw an error, because you are erroring out on the GET function in front of it.

 

With that said, I will attempt to read your mind.  You want to CHECK to see if an existing row is there BEFORE you ADD one.  This is where that list(of T) would best come in handy.

At the very beginning of your code, GET the entire list of rows and KEEP them in your own list object lstRows(of iPartTableRow)

Then when checking for to see if you need to add an object, use the code I posted with the Find(Function(x) method.

With that you can expand the function by hitting enter after the function name, you will notice the code editor expands the one liner to look more like a standard function with a return statement and end function statement.

 

        Dim lstRows As List(Of iPartTableRow)
        lstRows.Find(Function(x)
                         If x.MemberName = strname Then
                             If x.Item("Height") = dblHeight Then
                                 If x.Item("SomethingElse") = strSomethingElse Then
                                     Return True
                                 End If
                             End If
                         End If
                         Return False
                     End Function)

You can use any combination of qualifiers to decide it is found.

 

Jamie Johnson : Owner / Sisu Lissom, LLC https://sisulissom.com/
0 Likes
Message 11 of 13

josh.nieman
Advocate
Advocate

Not rude at all.  You're just addressing the exact thing I was looking to figure out.

 

I did, however, post the wrong API sample.  As you'll see, though, the same premise exists when getting an iPartTableRow result via iPartTableRows.Item, as well.  So I'm still uncertain why it thinks it should only have an integer, given the description of ".iPartTableRows.Item(index)" below.  My thoughts are that there is an error on this page.  It specifies Type as Long, but describes it at Variant.

iPartTableRows.Item Property

Parent Object: iPartTableRows

Description

Returns the specified iPartTableRow object from the collection. This is the default property of the iPartTableRows collection object.

Property Value

This is a read only property whose value is an iPartTableRow.

Parameters

NameTypeDescription
IndexLongInput Variant value that specifies the iPartTableRow to return. The input can be either an Integer value or a string. In the case of an Integer, the value specifies the row where the first row has in index of 1. If a string is input it should be in a part identifier format (i.e. ''[Height=1.000 in][Length=2.000 in][Radius=0.250 in]''). If the part specified does not exist an error occurs. This index value is not the same as the index value indicated in the user interface dialog. Rows in the dialog may be re-ordered.
0 Likes
Message 12 of 13

JamieVJohnson2
Collaborator
Collaborator

The instructions contradict themselves (api documentation error for sure). 

You cant have a data type of Long (64bit integer) and feed it anything but something that converts directly to that long data type (aka smaller integers).  So they can't write instructions? 

 

Now let us assume they did something different than that in the actual API...  they did, but not in your favor.

The api object browser shows this (integer is a 32bit integer):

  <DispId(0)>
        Default ReadOnly Property Item(Index As Integer) As iPartTableRow

Meaning Autodesk Developers mislead you.  However you can still use the method I last posted to get what you want via inline find expression.

 

Jamie Johnson : Owner / Sisu Lissom, LLC https://sisulissom.com/
0 Likes
Message 13 of 13

josh.nieman
Advocate
Advocate

@JamieVJohnson2 wrote:

The instructions contradict themselves (api documentation error for sure). 



Thank you.

0 Likes