2380x
001596
2019-05-13

Metody wyboru elementów za pośrednictwem interfejsu COM

Podczas edycji elementów za pośrednictwem interfejsu COM ich wybór często stanowi problem, ponieważ nie można go przeprowadzić wizualnie w oknie roboczym programu. Wybór elementów może być szczególnie trudny w przypadku modeli utworzonych za pomocą interfejsu programu, które następnie mają być modyfikowane przez oddzielny program. Poza sytuacją, gdy elementy zostały wcześniej zaznaczone w programie RFEM, istnieje kilka alternatywnych metod realizacji tego zadania w kodzie programu.

Wybór na podstawie komentarza

Prawdopodobnie najprostszą metodą jest przypisanie komentarzy do tworzonych elementów, które można następnie wyszukać. Poniżej przedstawiono przykładową funkcję, która przeszukuje pręty według ich komentarzy i zwraca numery znalezionych prętów:

Function getMemberNosByComment(members() As RFEM5.Member, comment As String) As Integer()
    Dim intArr() As Integer
    Dim i As Long
    Dim j As Long
    j = 0
    ReDim intArr(0 To 0)
    ' loop over members
    For i = 0 To UBound(members, 1)
        If (members(i).comment = comment) Then
            ' size if integer array is increased by 1
            ReDim Preserve intArr(0 To j)
            intArr(j) = members(i).no
            j = j + 1
        End If
    Next i
    ' return integer array
    getMemberNosByComment = intArr
End Function

Funkcja wykonuje pętlę po przekazanych prętach. Jeżeli ciąg znaków podany w parametrze „comment” jest zgodny z komentarzem pręta, jego numer zostaje dodany do tablicy typu integer. Na końcu zwracana jest tablica zawierająca wszystkie znalezione numery prętów.

Wybór węzłów końcowych pręta

Wybór elementów w programie, a tym samym za pośrednictwem interfejsu COM, odbywa się przy użyciu ciągów znaków. Na przykład w przypadku linii powiązane węzły są przekazywane jako ciąg znaków, w którym numery węzłów są oddzielone przecinkami. Stosowanie ciągów znaków podczas programowania wymaga konwersji wartości liczbowych na tekstowe i odwrotnie. Dlatego poniżej przedstawiono funkcję, która zamienia numery prętów uzyskane za pomocą opisanej wcześniej funkcji w ciąg znaków. W tym celu numery prętów są kolejno konwertowane na znaki za pomocą funkcji CStr, a po każdej liczbie do ciągu znaków dodawany jest przecinek. Nadmiarowy przecinek na końcu ciągu jest ignorowany przez RFEM/RSTAB, dlatego może pozostać bez zmian.

Function intArrToStr(intArr() As Integer) As String
    Dim str As String
    Dim i As Long
    
    For i = 0 To UBound(intArr, 1)
        str = str + CStr(intArr(i)) + ","
    Next i
    
    intArrToStr = str
End Function

Dzięki tej funkcji pręty przefiltrowane na podstawie komentarza mogą być następnie wybrane za pomocą interfejsu COM

    ' select members by commen
    Dim mems() As RFEM5.Member
    Dim mem_nos() As Integer
    Dim str As String
    
    str = "test comment"
    mem_nos = getMemberNosByComment(mems, str)
    
    iModelData.EnableSelections (True)
    
    str = intArrToStr(mem_nos)
    iModelData.SelectObjects MemberObject, str

Często nie wystarcza samo zaznaczenie określonych elementów – potrzebny jest również dostęp do elementów podrzędnych z nimi powiązanych. Jako przykład poniżej przedstawiono sposób wyznaczania węzłów początkowych pręta. Ponieważ w programie RFEM procedura ta jest nieco bardziej złożona niż w RSTAB, gdyż do każdego pręta przypisana jest tu dodatkowo linia, jako przykład wybrano właśnie ten sposób.

Najpierw należy znaleźć numery linii przypisanych do prętów. Poniższa funkcja zakłada, że numery prętów już istnieją i wyszukuje odpowiadające im numery linii.

Function getLineNosByMemberNos(members() As RFEM5.Member, member_nos() As Integer) As Integer()
    
    Dim intArr() As Integer
    Dim i As Long
    Dim j As Long
    Dim k As Long
    
    k = 0
    ReDim intArr(0 To 0)
    
    For i = 0 To UBound(members, 1)
        For j = 0 To UBound(member_nos, 1)
            If (members(i).no = member_nos(j)) Then
                ' zwiększ rozmiar tablicy o 1
                ReDim Preserve intArr(0 To k)
                intArr(k) = members(i).LineNo
                k = k + 1
                ' exit loop over member_nos
                Exit For
            End If
        
        Next j
    Next i
    
    getLineNosByMemberNos = intArr
    
End Function

Ta funkcja zawiera dwie zagnieżdżone pętle. Główna pętla przegląda pręty, a podrzędna - numery prętów. Dla każdego pręta przeszukiwane jest całe pole zawierające numery prętów. Aby przyspieszyć ten proces, pętla podrzędna jest przerywana w momencie znalezienia zgodności numerów prętów. W przypadku zgodności pole z numerami linii jest każdorazowo rozszerzane o jeden element i dodawany jest nowy numer (k jest indeksem dla znalezionych numerów linii).

Aby znaleźć węzeł początkowy linii lub pręta, potrzebna jest jeszcze jedna funkcja. Musi ona przejść przez linie i – w przypadku zgodności z podanymi numerami linii – odczytać węzeł początkowy. Ponieważ numery węzłów są zapisane w formie ciągu znaków, konieczna jest również funkcja, która przekształci taki ciąg znaków w tablicę wartości liczbowych.

Function strToIntArr(intList As String) As Integer()
    '  possible chars "1-9 ; - ,"
    '  example: 1-4,5;100 > 1,2,3,4,5,100
    Dim ints() As Integer
    Dim tmpInts() As Integer
    ReDim ints(0)
    
    Dim span As Boolean
    Dim curInt As String
    curInt = ""
    Dim i As Integer
    i = 0
    Dim j As Integer
    Dim curChar As String
    
    Do While (i < Len(intList))
        curChar = Mid(intList, i + 1, 1)
        
        ' if string contains "-" a span is noted
        If (curChar = "-") Then
        
            span = True
            tmpInts = ints
            ReDim Preserve tmpInts(0 To UBound(tmpInts, 1) + 1)
            tmpInts(UBound(tmpInts, 1) - 1) = CInt(curInt)
            ints = tmpInts
            curInt = ""
        ' if last char is reached or a comma or a semicolon is the next char
        ElseIf ((curChar = ",") Or (curChar = ";") Or (i = Len(intList) - 1)) Then
        
            ' last char is reached, integer or span are terminated
            If (i = Len(intList) - 1) Then
                curInt = curInt & curChar
            End If
            
            ' treat the span
            If span Then
                ' create all integers between the span
                Dim firstNum As Integer
                Dim lastNum As Integer
                firstNum = ints(UBound(ints, 1) - 1)
                lastNum = CInt(curInt)
                curInt = ""
                
                If (firstNum > lastNum) Then
                    Dim tmp1 As Integer
                    tmp1 = lastNum
                    lastNum = firstNum
                    firstNum = tmp1
                    ints(UBound(ints, 1) - 1) = firstNum
                    
                End If
                
                ' extend ints and add new numbers to array
                tmpInts = ints
                ReDim Preserve tmpInts(0 To UBound(tmpInts, 1) + (lastNum - firstNum))
                
                For j = 0 To (lastNum - firstNum) - 1
                    tmpInts(UBound(ints, 1) + j) = j + firstNum + 1
                Next j
                
                ints = tmpInts
                span = False
            
            ' add new digit
            Else
                'extend ints and add new number to ints
                tmpInts = ints
                ReDim Preserve tmpInts(0 To UBound(tmpInts, 1) + 1)
                tmpInts(UBound(tmpInts, 1) - 1) = CInt(curInt)
                ints = tmpInts
                curInt = ""
            End If
        
        Else
        
            curInt = curInt & curChar
        End If
    
        i = i + 1
    Loop
    
    ' array is one element too long and is decreased
    ReDim Preserve ints(0 To UBound(ints, 1) - 1)
    
    strToIntArr = ints
End Function

Ta funkcja przechodzi przez ciąg znaków i analizuje każdy znak. Jeśli napotkany zostanie znak będący liczbą (jedną lub wieloma cyframi), są one zbierane aż do momentu osiągnięcia końca ciągu lub wystąpienia innego znaku. W przypadku napotkania myślnika rozpoznawany jest zakres (przedział) liczb, a wszystkie liczby znajdujące się pomiędzy wartościami granicznymi są generowane automatycznie.

Właściwa funkcja służąca do wyodrębnienia punktu początkowego linii może zostać teraz utworzona i dzięki temu jest bardzo przejrzysta

Function getLineStartNodeNosByLineNos(lines() As RFEM5.RfLine, line_nos() As Integer) As Integer()
    Dim intArr() As Integer
    Dim tmpIntArr() As Integer
    Dim str As String
    Dim i As Long
    Dim j As Long
    Dim k As Long
    
    k = 0
    ReDim intArr(0 To 0)
    
    For i = 0 To UBound(line_nos, 1)
        For j = 0 To UBound(lines, 1)
            If (lines(j).no = line_nos(i)) Then
                ' dodaj znaleziony numer linii do tablicy
                ReDim Preserve intArr(0 To k)
                str = lines(j).NodeList
                tmpIntArr = strToIntArr(str)
                intArr(k) = tmpIntArr(0)
                k = k + 1
                ' zakończ pętlę po line_nos_cpy
                Exit For
            End If
        
        Next j
    Next i
    
    getLineStartNodeNosByLineNos = intArr
End Function

Również tutaj występują dwie zagnieżdżone pętle, podobnie jak w funkcji „getLineNosByMemberNos”. Tym razem kolejność ma kluczowe znaczenie, ponieważ w przeciwnym razie utracone zostałoby przyporządkowanie węzłów do linii. Zewnętrzna pętla przechodzi przez numery linii, a gdy w pętli wewnętrznej zostanie znalezione dopasowanie do danej linii, za pomocą funkcji strToIntArr wyodrębniane są numery węzłów, z których w tym przypadku wykorzystywany jest pierwszy.

Cały proces pobrania węzłów początkowych wygląda następująco. Najpierw dla odpowiednich numerów prętów pobierane są numery linii, a następnie wyznaczane są węzły początkowe tych linii.

    ' select start nodes from members
    '   get line numbers from all members
    Dim line_nos() As Integer
    line_nos = getLineNosByMemberNos(mems, mem_nos)
    '   get start numbers from all lines
    Dim stNodes_nos() As Integer
    stNodes_nos = getLineStartNodeNosByLineNos(lines, line_nos)

Podsumowanie i perspektywy

Funkcja getLineNosByMemberNos stanowi podstawę dla innych funkcji selekcji, podobnie jak funkcja getLineStartNodeNosByLineNos. Na bazie tego schematu można na przykład również wyszukiwać obciążenia przypisane do prętów. Jako narzędzia pomocnicze dostępne są funkcje strToIntArr oraz intArrToStr, dzięki którym selekcja oparta na ciągach znaków w RFEM może zostać przekształcona w operacje na tablicach liczbowych.

Inną możliwością jest wybór na podstawie współrzędnych. Można na przykład zdefiniować określoną przestrzeń i zaznaczyć wszystkie elementy znajdujące się w jej obrębie. Ten sposób selekcji zostanie opisany w późniejszym artykule.


Autor

Thomas zajmuje się w Customer Support zapytaniami technicznymi dotyczącymi oprogramowania. Rzetelnie wdraża się w różne zagadnienia i opracowuje odpowiednie rozwiązania.

Odnośniki
Pobrane


;