Tuesday, 23 June 2009

Multi-threading with the Revit API

One of my Revit plugins has been using a BackgroundWorker to run a resource-heavy and lengthy operation. Running an operation on a separate, dedicated thread like this enables you to keep the UI responsive where otherwise it might appear as though it has stopped responding, or at best be sluggish.

Until recently this all worked fine, until I came across a problem in a very particular scenario. I wanted to loop through RVTLinks elements in a model using a category filter, and then get the boundingbox data for them. The model was opened using the API's OpenDocumentFile. This resulted in the following complaint from Revit:

I sent a test app to A'desk and they replicated the problem. Apparently this is happening because the Revit API "doesn't support multi-threading", and there is an SPR logged for this. This is strange for several reasons:

1. I've been using multi-threading with no problems for a long time.
2. The same code works fine if I have opened the file manually, rather than through the API.
3. Surely every Revit programmer needs multi-threading, as inherently a lot of the things we do are at some point resource-heavy? Or do I have to go back to the old DoEvents()?

Hopefully I'll get a bit more info on this soon.

Thursday, 18 June 2009

Revit 2010: Behavioural changes to dual-category elements

Dual-category elements? What are these?

Well, following on from an earlier post about curtain panel doors I sought some support from the API team at A'desk and I got a very thorough response which I thought was worth sharing as it might be of some interest, and would save them having to repeat themselves :)

In Revit, elements usually belong to a single category. But in two special cases, both related to curtain panels, it was possible for an element to belong to two categories. Let's call them the "normal category" and the "schedule category." Some parts of the software would use the normal category and other parts would use the schedule category, resulting in inconsistent behavior for such elements.

The first case is curtain panels that schedule as doors or windows. Such panels have a normal category of Curtain Panels and a schedule category of Doors or Windows.

The second case has to do with the ability to select a curtain panel and change its type to a wall type. Such a wall always has a normal category of Walls, and if its parameter "Categorize as:" is set to panel, then the wall will have a schedule category of Curtain Panels.

This project is to eliminate the dual-category nature of the above elements, and make them behave consistently as belonging to their schedule category. Thus, parts of the software that previously used the normal category will have different behavior now.

Behavior changes:
- Status prompt and selection filter: For dual category elements, Revit will show the schedule category instead of the normal category before.

- Marks: Dual category elements will warn when they share a mark with elements in their schedule category rather than their normal category. Unique type marks will be assigned to curtain panel door and window types.

- Visibility: For curtain panel doors and windows, they are hidden only when the schedule category (doors or windows) is hidden.

- Project browser: Curtain panel door and window types will be listed under Doors and Windows rather than Curtain Panels.

- Family category and parameters dialog: In the family editor, the dialog shows the category as Doors or Windows for curtain panel doors and windows, and all three categories (Curtain Panels, Doors and Windows) are listed in the list box, providing UI to change it.

- Family types dialog: For curtain panel doors and windows, Revit will show the parameters from Doors (Rough Width, Rough Height, Thickness, Fire Rating, Operation) or Windows (Rough Width, Rough Height, Operation) and but not those from Curtain Panels (Finish).

- ODBC export: Dual category elements will show in the tables corresponding to their schedule categories. The foreign key constraint between the Curtain Panels table and the Curtain Panel Types table will not be created.

- Graphic overrides (including pattern, halftone, transparent, and detail level): Curtain panel doors/windows will show the Doors/Windows overrides instead of the Curtain Panels overrides. Wall panels categorized as panels will show the Curtain Panels overrides instead of the Walls overrides.

- View filters: Curtain panel doors/windows will be hidden or overridden if they match the Doors/Windows filter criteria instead of the Curtain Panels filter criteria. Wall panels categorized as panels will be hidden or overridden if they match the Curtain Panels filter criteria instead of the Walls filter criteria.

- Tab order: Curtain panel doors/windows get a higher priority, and can be picked without tabbing. Wall panels categorized as panels get a lower priority, and can be picked using tabbing.

So now you know! :)

Wednesday, 10 June 2009

'Edit and Continue' in Revit 2010

In my last post I was disappointed to learn that Visual Studio 2008 (or 2010) doesn't support 'Edit and Continue' in 64-bit, so I was wondering how I might install Revit 32 on my 64-bit machine so I could make use of this valuable functionality.

Well, thanks to a comment from Matt Mason and an experiment here on another machine my disappointment is now even greater; It looks like my simultaneous switch to a 64 bit machine and Revit 2010 was actually disguising an even greater issue - you can't Edit and Continue in Revit 2010 at all!.

If you try it, you'll see this:

I tried this with the same code using Revit 2009 and 2010, on a standard 32-bit machine. Both times I had the same reference to the 2010 RevitAPI.dll. I can Edit and Continue in 2009. In 2010 I can't. If I need to change my code I have to stop debugging, make my edit, start debugging again, wait for Revit to start up again, select my add-on from the external tools menu, etc... How laborious.

Is this the way it's always going to be? Or am I doing something wrong? Argh.

See comments for update...

Monday, 8 June 2009

Debugging in 64 bit

Oh dear. Not long ago I took the plunge and bought a nice new laptop with 64-bit Vista, knowing that the world was slowly shifting towards the world of 64. I'm normally a late adopter of new technology, preferring to leave the hassle to others, and I'm now regretting breaking my own policy.

I have since discovered that a valuable piece of debugging functionality in Visual Studio does not work in 64-bit, and will not be fixed with the VS 2010 release either. I'm referring to "edit and continue" - the ability to alter your code while stepping through. This is invaluable when debugging.

And while we're on the subject, did you know you can't install Revit 32-bit on your 64-bit machine? Despite the fact that the ADN offers two download links for Revit (one for 32, one for 64) they are actually the same packages, and at 1.4GB that's a lot of wasted time and bandwidth in downloading. When you run setup.exe it detects your OS, and if you're running 64 bit then you only get offered 64 bit Revit. I tried to be clever and navigate directly to the 32 bit MSI, but those boffins at A'desk have locked them down - you can't run them directly.

Hey, MicroSoft - please please please give us 'edit and continue' in 64-bit!

Hey, Autodesk - please please please give us a way to install Revit 32 on a 64 bit machine!

Friday, 5 June 2009

The RvtMgdDbg tool

I was reminded recently when seeking support from the ADN what a great tool RvtMgdDbg is. Anyone programming with the Revit API should have this installed by default.

Read this post on Jeremy Tammik's blog for download links and a good summary by Mikako Harada on how to use it.

Wednesday, 3 June 2009

Doors in curtain walls: 2009 vs 2010

In Revit 2009 if you place a door in a curtain wall it gets categorized as a Curtain Panel, and to select the door you need to highlight and right-click the wall, choose 'Select Panels on Host', and then click the door. Rolling over the door reveals its classification (you can click the image to zoom in):

In Revit 2010 however things have changed. A door in a curtain wall is understandably categorized as a Door. And this image shows the same file as previous that was created in 2009 but opened in (and converted to) 2010:

Monday, 1 June 2009

Comparing folder contents using dictionaries

Coming from a web programming background I properly cut my commercial programming teeth using PHP, although this was after a dabble with Fortran when studying engineering, and then later Turbo Pascal when studying geology.

I haven't touched PHP for a while, but there's two things I really miss - not having to declare variables (some call me agile, others lazy), and the simple associative arrays. They don't exist in VB.NET, but the dictionary is a pretty cool alternative. Like an associative array a dictionary allows you to add any object as an item with a string as a key to access it, and edit members later if you wish.

A common use, especially amongst BIM & CAD programmers, would be for handling file information, as I had to do today. The following code takes a 'master' and 'client' directory path, looks at all the files in them both and stores their names and modified dates as key/value pairs in dictionaries. Then we compare the dictionaries using SequenceEqual, and if there's a difference we loop through the master's keys looking for the keys and values in the client dictionary. Any files that are missing or different are returned in a collection, for action elsewhere.

Private Function compareTwoFolders(ByVal masterDir As String, ByVal clientDir As String)

Dim filesNeedingUdating As New Collection(Of String)
Dim currentFileInfo As FileInfo

'get all master files
Dim masterFileCollection As ReadOnlyCollection(Of String) = _
My.Computer.FileSystem.GetFiles(masterDir, FileIO.SearchOption.SearchAllSubDirectories)

'get all client files
Dim clientFilesCollection As ReadOnlyCollection(Of String) = _
My.Computer.FileSystem.GetFiles(clientDir, FileIO.SearchOption.SearchAllSubDirectories)

'create dictionary of master file names and mod dates
Dim masterFiles As New Dictionary(Of String, String)

Dim i As Integer = 0
Do While (i < masterFileCollection.Count)
currentFileInfo = My.Computer.FileSystem.GetFileInfo(masterFileCollection(i))
masterFiles.Add(fixFilePath(masterFileCollection(i)), currentFileInfo.LastWriteTimeUtc)
i = i + 1

'create dictionary of client file names and mod dates
Dim clientFiles As New Dictionary(Of String, String)

Dim z As Integer = 0
Do While (z < clientFilesCollection.Count)
currentFileInfo = My.Computer.FileSystem.GetFileInfo(clientFilesCollection(z))
clientFiles.Add(fixFilePath(clientFilesCollection(z)), currentFileInfo.LastWriteTimeUtc)
z = z + 1

'check if dictionaries are equal
If Not masterFiles.SequenceEqual(clientFiles) Then
'there are differences in the sequences, so lets investigate
'loop through master dictionary finding what's different
For Each masterKey As String In masterFiles.Keys
If clientFiles.ContainsKey(masterKey) Then
If Not clientFiles(masterKey) = masterFiles(masterKey) Then
'different mod dates, add file to collection
End If
'no file found, add file to collection
End If


End If

Return filesNeedingUdating

End Function

fixFilePath, which I haven't included, is just a function that reduces the full file path (C:\\ etc) to the directory that your interested in, so that they can properly be compared. If you're comparing between two machines with the identical file structures you won't need this.