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
Solved! Go to Solution.
Solved by RPTHOMAS108. Go to 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.
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
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.
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
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
Following up on some of your initial thoughts, I would assume that this can be totally automated after all, like this:
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/
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.