Revit API Forum
Welcome to Autodesk’s Revit API Forums. Share your knowledge, ask questions, and explore popular Revit API topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Way to check if family is corrupt?

7 REPLIES 7
SOLVED
Reply
Message 1 of 8
PhillipM
486 Views, 7 Replies

Way to check if family is corrupt?

PhillipM
Advocate
Advocate

Good afternoon everyone.

 

I have a simple addon that iterates over all the families in a document and exports them to a folder amongst other things, using fam.SaveAs(path, options)

 

All works as expected until in the very rare case it hits a family that is corrupt.  I know this doesn't happen often but it does happen.  When it hits a family like this there are no warning messages, doesn't crash, just hangs.

 

My question.  Is there a way that I can check for this situation or a way to skip on to the next family?

 

Attached is a project with a bad family in it as a reference.

 

Regards

 

Phillip Miller

0 Likes

Way to check if family is corrupt?

Good afternoon everyone.

 

I have a simple addon that iterates over all the families in a document and exports them to a folder amongst other things, using fam.SaveAs(path, options)

 

All works as expected until in the very rare case it hits a family that is corrupt.  I know this doesn't happen often but it does happen.  When it hits a family like this there are no warning messages, doesn't crash, just hangs.

 

My question.  Is there a way that I can check for this situation or a way to skip on to the next family?

 

Attached is a project with a bad family in it as a reference.

 

Regards

 

Phillip Miller

7 REPLIES 7
Message 2 of 8
RPTHOMAS108
in reply to: PhillipM

RPTHOMAS108
Mentor
Mentor
Accepted solution

I don't think so.

 

You can't cancel the process either so also no possibility of setting a time limit. Only thing you could do is create a logging system where the last item that caused the issue along with those already exported up to that point can be skipped on the next run of the Add-in.

 

For these kind of issues all you can really do from a API developer perspective is get end user to open a support case with Autodesk regarding the attached project file (or you could open one). A brief review of the journal indicates an issue with many short curves.

 

Regarding end user support case they can easily replicate the issue outside of the API by attempting to edit the family in the UI. It seems to be getting stuck in a regeneration loop and may eventually resolve. I wouldn't classify it as document corruption because that tends to have an abrupt end i.e. file would not open and error message would be shown in UI. Seems to be an issue with how Revit responds to a certain circumstance that the file causes to arise i.e. problem with Revit.

0 Likes

I don't think so.

 

You can't cancel the process either so also no possibility of setting a time limit. Only thing you could do is create a logging system where the last item that caused the issue along with those already exported up to that point can be skipped on the next run of the Add-in.

 

For these kind of issues all you can really do from a API developer perspective is get end user to open a support case with Autodesk regarding the attached project file (or you could open one). A brief review of the journal indicates an issue with many short curves.

 

Regarding end user support case they can easily replicate the issue outside of the API by attempting to edit the family in the UI. It seems to be getting stuck in a regeneration loop and may eventually resolve. I wouldn't classify it as document corruption because that tends to have an abrupt end i.e. file would not open and error message would be shown in UI. Seems to be an issue with how Revit responds to a certain circumstance that the file causes to arise i.e. problem with Revit.

Message 3 of 8
PhillipM
in reply to: RPTHOMAS108

PhillipM
Advocate
Advocate

Thanks for your reply.

 

It was what I thought but just wanted to make sure I wasn't missing anything. 

 

This is meant to be a totally automated process with no user interaction so is a bit of a shame I can't get around this catch and move on.  What I will have to do is set a timer and if there is a long period of time opening a family, pop up a message box of some sort to notify the user of the issue when they get back to their workstation.

 

Regards

 

Phillip Miller

0 Likes

Thanks for your reply.

 

It was what I thought but just wanted to make sure I wasn't missing anything. 

 

This is meant to be a totally automated process with no user interaction so is a bit of a shame I can't get around this catch and move on.  What I will have to do is set a timer and if there is a long period of time opening a family, pop up a message box of some sort to notify the user of the issue when they get back to their workstation.

 

Regards

 

Phillip Miller

Message 4 of 8
RPTHOMAS108
in reply to: PhillipM

RPTHOMAS108
Mentor
Mentor

@PhillipM 

 

Should also mention the ProgressChangedEventArgs.Cancel of Application.ProgressChanged

 

This reports the progress of the little green bar that appears in the bottom left. This has cancel button next to it which is available when opening a family. So it may be possible to cancel via that if you spot a recurring pattern of progress as appears to happen with this family. i.e. how many times it goes to 25% after 50% etc.

 

ProgressChangedEventArgs.Caption may also report 'Regenerating' so you could also count those and set limits on that.

 

This all hinges on if ProgressChangedEventArgs.Cancel works i.e. it works in the UI immediately for the family in question.

0 Likes

@PhillipM 

 

Should also mention the ProgressChangedEventArgs.Cancel of Application.ProgressChanged

 

This reports the progress of the little green bar that appears in the bottom left. This has cancel button next to it which is available when opening a family. So it may be possible to cancel via that if you spot a recurring pattern of progress as appears to happen with this family. i.e. how many times it goes to 25% after 50% etc.

 

ProgressChangedEventArgs.Caption may also report 'Regenerating' so you could also count those and set limits on that.

 

This all hinges on if ProgressChangedEventArgs.Cancel works i.e. it works in the UI immediately for the family in question.

Message 5 of 8
RPTHOMAS108
in reply to: RPTHOMAS108

RPTHOMAS108
Mentor
Mentor

So below is an example that seems to work well for English Language Revit.

 

When you call cancel on the event it throws an exception for the calling method so you can catch that and move onto the next item. The below works by counting the occurrences of "Regenerating", a count of around 2200 is about right for that family (when it starts to loop).

 

You could as an alternative use the progress Position property but to get a percentage you need to divide the Position property by the UpperRange property. This approach doesn't seem as good as the regen caption approach because the percentages encountered are more random and not a good indicator of a pattern of repetition (would have to store more in an array to check against).

 

 Private IntRegenCount As Integer = 0
    Private Sub ProgressChanged(a As Object, e As Autodesk.Revit.DB.Events.ProgressChangedEventArgs)
        If e.Caption = "Regenerating" Then
            IntRegenCount += 1
        End If
        If IntRegenCount > 2200 Then
            'IntRegenCount = 0
            e.Cancel()
        End If
    End Sub

    Public Function Obj_220516d(commandData As ExternalCommandData, ByRef message As String, elements As ElementSet) As Result
        Dim IntUIApp As UIApplication = commandData.Application
        Dim IntUIDoc As UIDocument = commandData.Application.ActiveUIDocument
        Dim IntDoc As Document = IntUIDoc.Document

        AddHandler commandData.Application.Application.ProgressChanged, AddressOf ProgressChanged
        Const NM As String = "Vantage Metro Series Hinged Door Jamb"

        Dim FEC As New FilteredElementCollector(IntDoc)
        Dim ECF As New ElementClassFilter(GetType(Family))
        Dim F As Family = FEC.WherePasses(ECF) _
                                            .Where(Function(x) x.Name = NM) _
                                            .Cast(Of Family) _
                                            .FirstOrDefault

         Dim FDoc As Document = Nothing

        Try
            FDoc = IntDoc.EditFamily(F)
        Catch ex As Exception
            Return Result.Cancelled
        Finally
            RemoveHandler commandData.Application.Application.ProgressChanged, AddressOf ProgressChanged
        End Try

        Return Result.Succeeded

    End Function

 

So below is an example that seems to work well for English Language Revit.

 

When you call cancel on the event it throws an exception for the calling method so you can catch that and move onto the next item. The below works by counting the occurrences of "Regenerating", a count of around 2200 is about right for that family (when it starts to loop).

 

You could as an alternative use the progress Position property but to get a percentage you need to divide the Position property by the UpperRange property. This approach doesn't seem as good as the regen caption approach because the percentages encountered are more random and not a good indicator of a pattern of repetition (would have to store more in an array to check against).

 

 Private IntRegenCount As Integer = 0
    Private Sub ProgressChanged(a As Object, e As Autodesk.Revit.DB.Events.ProgressChangedEventArgs)
        If e.Caption = "Regenerating" Then
            IntRegenCount += 1
        End If
        If IntRegenCount > 2200 Then
            'IntRegenCount = 0
            e.Cancel()
        End If
    End Sub

    Public Function Obj_220516d(commandData As ExternalCommandData, ByRef message As String, elements As ElementSet) As Result
        Dim IntUIApp As UIApplication = commandData.Application
        Dim IntUIDoc As UIDocument = commandData.Application.ActiveUIDocument
        Dim IntDoc As Document = IntUIDoc.Document

        AddHandler commandData.Application.Application.ProgressChanged, AddressOf ProgressChanged
        Const NM As String = "Vantage Metro Series Hinged Door Jamb"

        Dim FEC As New FilteredElementCollector(IntDoc)
        Dim ECF As New ElementClassFilter(GetType(Family))
        Dim F As Family = FEC.WherePasses(ECF) _
                                            .Where(Function(x) x.Name = NM) _
                                            .Cast(Of Family) _
                                            .FirstOrDefault

         Dim FDoc As Document = Nothing

        Try
            FDoc = IntDoc.EditFamily(F)
        Catch ex As Exception
            Return Result.Cancelled
        Finally
            RemoveHandler commandData.Application.Application.ProgressChanged, AddressOf ProgressChanged
        End Try

        Return Result.Succeeded

    End Function

 

Message 6 of 8
RPTHOMAS108
in reply to: RPTHOMAS108

RPTHOMAS108
Mentor
Mentor

This below non-language dependant example appears to work also. In below I attempt to edit the problem family before moving on to a second family in the project file that does open.

 

Again you have to look at the numbers for variables N and T below. The limit of T below often occurs first and seems to give adequate time allowance to edit problem family.

 

For either case you have to handle the DialogBoxShowing event to cancel the dialogue that appears after cancelling the progress changed event.

 

Private IntProgressLog As Integer() = New Integer(20) {}
    Private Sub ProgressChanged(s As Object, e As Autodesk.Revit.DB.Events.ProgressChangedEventArgs)
        Dim ULim As Double = e.UpperRange
        Dim Pos As Double = e.Position
        Dim Perc As Integer = ((Pos / ULim) * 20)
        IntProgressLog(Perc) += 1

        Dim N As Integer = IntProgressLog.Max
        Dim T As Integer = IntProgressLog.Sum
        If N > 2000 OrElse T > 15000 Then
            e.Cancel()
        End If
    End Sub
    Private Sub MsgBoxShowing(s As Object, e As Autodesk.Revit.UI.Events.DialogBoxShowingEventArgs)
        Dim IntUIApp As UIApplication = s
        IntProgressLog = New Integer(20) {} 'reset the variable for next family
        e.OverrideResult(2)
    End Sub

    Public Function Obj_220516d(commandData As ExternalCommandData, ByRef message As String, elements As ElementSet) As Result
        Dim IntUIApp As UIApplication = commandData.Application
        Dim IntUIDoc As UIDocument = commandData.Application.ActiveUIDocument
        Dim IntDoc As Document = IntUIDoc.Document

        AddHandler IntUIApp.Application.ProgressChanged, AddressOf ProgressChanged
        AddHandler IntUIApp.DialogBoxShowing, AddressOf MsgBoxShowing

        Dim NM As String() = New String(1) {"Vantage Metro Series Hinged Door Jamb", "_ Callout Head"}

        Dim FDoc As Document = Nothing
        For i = 0 To 1
            Dim K As Integer = i
            Dim FEC As New FilteredElementCollector(IntDoc)
            Dim ECF As New ElementClassFilter(GetType(Family))
            Dim F As Family = FEC.WherePasses(ECF) _
                                            .Where(Function(x) x.Name = NM(K)) _
                                            .Cast(Of Family) _
                                            .FirstOrDefault

            Try
                FDoc = IntDoc.EditFamily(F)
            Catch ex As Exception
            End Try
            If FDoc IsNot Nothing Then
                Debug.WriteLine("Edit of: " & NM(i))
            End If
        Next

        RemoveHandler IntUIApp.Application.ProgressChanged, AddressOf ProgressChanged
        RemoveHandler IntUIApp.DialogBoxShowing, AddressOf MsgBoxShowing

        Return Result.Succeeded
    End Function

 

 

This below non-language dependant example appears to work also. In below I attempt to edit the problem family before moving on to a second family in the project file that does open.

 

Again you have to look at the numbers for variables N and T below. The limit of T below often occurs first and seems to give adequate time allowance to edit problem family.

 

For either case you have to handle the DialogBoxShowing event to cancel the dialogue that appears after cancelling the progress changed event.

 

Private IntProgressLog As Integer() = New Integer(20) {}
    Private Sub ProgressChanged(s As Object, e As Autodesk.Revit.DB.Events.ProgressChangedEventArgs)
        Dim ULim As Double = e.UpperRange
        Dim Pos As Double = e.Position
        Dim Perc As Integer = ((Pos / ULim) * 20)
        IntProgressLog(Perc) += 1

        Dim N As Integer = IntProgressLog.Max
        Dim T As Integer = IntProgressLog.Sum
        If N > 2000 OrElse T > 15000 Then
            e.Cancel()
        End If
    End Sub
    Private Sub MsgBoxShowing(s As Object, e As Autodesk.Revit.UI.Events.DialogBoxShowingEventArgs)
        Dim IntUIApp As UIApplication = s
        IntProgressLog = New Integer(20) {} 'reset the variable for next family
        e.OverrideResult(2)
    End Sub

    Public Function Obj_220516d(commandData As ExternalCommandData, ByRef message As String, elements As ElementSet) As Result
        Dim IntUIApp As UIApplication = commandData.Application
        Dim IntUIDoc As UIDocument = commandData.Application.ActiveUIDocument
        Dim IntDoc As Document = IntUIDoc.Document

        AddHandler IntUIApp.Application.ProgressChanged, AddressOf ProgressChanged
        AddHandler IntUIApp.DialogBoxShowing, AddressOf MsgBoxShowing

        Dim NM As String() = New String(1) {"Vantage Metro Series Hinged Door Jamb", "_ Callout Head"}

        Dim FDoc As Document = Nothing
        For i = 0 To 1
            Dim K As Integer = i
            Dim FEC As New FilteredElementCollector(IntDoc)
            Dim ECF As New ElementClassFilter(GetType(Family))
            Dim F As Family = FEC.WherePasses(ECF) _
                                            .Where(Function(x) x.Name = NM(K)) _
                                            .Cast(Of Family) _
                                            .FirstOrDefault

            Try
                FDoc = IntDoc.EditFamily(F)
            Catch ex As Exception
            End Try
            If FDoc IsNot Nothing Then
                Debug.WriteLine("Edit of: " & NM(i))
            End If
        Next

        RemoveHandler IntUIApp.Application.ProgressChanged, AddressOf ProgressChanged
        RemoveHandler IntUIApp.DialogBoxShowing, AddressOf MsgBoxShowing

        Return Result.Succeeded
    End Function

 

 

Message 7 of 8
jeremy_tammik
in reply to: RPTHOMAS108

jeremy_tammik
Autodesk
Autodesk

Following up on some of your initial thoughts, I would assume that this can be totally automated after all, like this:

 

  • Run an external Windows app that monitors progress and can kill Revit.exe by terminating the process via native OS calls
  • Loop through all the families and call SaveAs on each, logging progress to an external file (or wherever you like)
  • The log file helps keep track of where you are and where you need to proceed next
  • If the external file has not been updated for a while (a minute? ten minutes?), kill Revit.exe and restart with the next family to export, logging and skipping the corrupt one

 

Sound feasible? It is a pretty standard approach to run a batch process. Revit is not designed for batch processing dozens of files, let alone hundreds or thousands, so crashes of all kinds are to be expected, perfectly normal, and need to be handled gracefully by batch processing workflows. Check out The Building Coder 'batch' category:

 

https://thebuildingcoder.typepad.com/blog/batch/

   

 

 

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes

Following up on some of your initial thoughts, I would assume that this can be totally automated after all, like this:

 

  • Run an external Windows app that monitors progress and can kill Revit.exe by terminating the process via native OS calls
  • Loop through all the families and call SaveAs on each, logging progress to an external file (or wherever you like)
  • The log file helps keep track of where you are and where you need to proceed next
  • If the external file has not been updated for a while (a minute? ten minutes?), kill Revit.exe and restart with the next family to export, logging and skipping the corrupt one

 

Sound feasible? It is a pretty standard approach to run a batch process. Revit is not designed for batch processing dozens of files, let alone hundreds or thousands, so crashes of all kinds are to be expected, perfectly normal, and need to be handled gracefully by batch processing workflows. Check out The Building Coder 'batch' category:

 

https://thebuildingcoder.typepad.com/blog/batch/

   

 

 

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
Message 8 of 8
PhillipM
in reply to: RPTHOMAS108

PhillipM
Advocate
Advocate

Thank you so much.

 

You have gone way above and beyond with providing examples etc, and I thank you for that.

 

I'm back working on this project next week and I will let you know how I get on.

 

Cheers

 

Phillip

0 Likes

Thank you so much.

 

You have gone way above and beyond with providing examples etc, and I thank you for that.

 

I'm back working on this project next week and I will let you know how I get on.

 

Cheers

 

Phillip

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report