Rod Howarth

Tuesday, November 15, 2011

[Revit API]- Revit Remote Boot - A technique to run an API add-in on a particular Revit model without user interaction

At last years Autodesk University, someone mentioned that they were looking for a way to run an export on their model once a day, on a schedule. This is not something that the Revit API is generally used for, but it  got me thinking of ways that I could make it happen. While flying around the USA after AU I hacked together some prototype code which did just this, but left it shelved. For my AU 2011 virtual class, I decided to finish it off and make it public, in case someone else wants to use it.

The program is called ‘Revit Remote Boot’, and is really more of a ‘system’ than a program, there are a number of moving parts to make it all happen. Put simply, it is a couple of interacting programs that allow you to perform an operation on a Revit model from a script.

Take a look at this video, which is a small part of my AU Virtual class, for a full demonstration of what it does. Note that while it talks about using Revit Server to create a local model, if you are running just a pure file server, this is fine as well - you could just directly open the file. It’s just that running Revit Server opens up some interesting possibilities and was the topic of my AU presentation.

 

Revit Remote Boot Demonstration from Rod Howarth

(you should head to the Vimeo page and watch it in high definition there to read the text)

Basically your application can save an xml file in a certain directory, which ads a ‘job’ to the queue. Then, using the RevitServerToolCommand, you can, from a batch file, create a local copy of the Revit Model you want to work on, and open it in Revit. From there, a Revit API add-in will detect that you’ve opened a model that corresponds to a job, and will run the other Revit API add-in that was specified in the job file. After this is done, the job is marked complete, and Revit is closed.

In my example, I’ve done a simple export to DWF of the model, the idea being you could set this up to run at 1am every night, and export the DWF model to a certain location - perhaps for viewing in Design Review on mobile devices. However, you could use it for any Revit API add-in. For instance, you could have a high powered server which you setup as a local Revit Server just purely to run this Revit Remote Boot. On this server you could create a job to run a structural analysis add-in, or other computationally expensive stuff. This way you can setup your own “cloud”, running Revit directly. You could also run certain audits on a model, for example, you could create an add-in that counts the number of warnings present in the model and saves this to a database, for displaying in a ‘hall of shame’ on your company intranet.

This is highly experimental, and has some pitfalls, so should be considered as a proof of concept, rather than a production ready program. The main pitfall is that any errors that come up are shown as dialog boxes, and there is no easy way to deal with this in Revit. To get around this I’ve used AutoHotKey (http://www.autohotkey.com/) to detect certain dialogs and close them, but if there are unexpected ones, it will fall over.

If you are interested in the code behind this, check it out here: https://github.com/RodH257/RevitRemoteBoot you can use the ‘zip’ button there to download it if you don’t have git.

I’d love to hear your thoughts on this, and whether you think Autodesk should work towards making this kind of thing easier to do?

Saturday, November 5, 2011

[General Dev]-How to push to two Git remote locations at once

Now that BitBucket supports Git, I’ve been utilizing both GitHub and BitBucket for my project hosting, and have been looking into migrating to BitBucket for my private repositories (as they are free). This is part of the beauty of using a distributed version control system, you can have your code in multiple locations, rather than one central Subversion server.

In Git, I could add two remotes and each time I want to push/pull code I would type something like:

git push github master
git push bitbucket master

However, I want to make things as easy as possible for myself, and I’m simply using these two repositories as a mirror of each other, so I’ll be telling git to push to multiple URL’s with the same remote.

To do this, I need to edit the .git/config file for my project. You can navigate here via the command line, or in Windows you can turn on hidden file display and open the .git folder in Windows Explorer

In this file, I’ve already added one of my remotes using the git remote add command, as you can see here:

To make it push to BitBucket at the same time as GitHub, you simply duplicate this URL line and add the BitBucket URL to it

Then if you do a git push origin master (where origin is name of your branch), you will see it will push to these URLs in succession:

Unfortunately if you have a password protected ssh key it will still ask twice for your password.

Now each time you push to your origin remote, you’ll be taking full advantage of gits distributed nature, and will have two remote copies of your code.

Monday, October 3, 2011

[Revit]- HTML Version of the Silverlight Revit Server Administrator Tool

If you’ve used Revit Server Before, you might recognize this:

It’s the Revit Server Administrator Tool which you access by going to http://servername/revitserveradmin it lets you see what Revit Server projects you have present, what files are stored in them, and their version history. It also lets you create, move, copy, delete or lock files and folders.

It’s a vital tool for anyone who runs a Revit Server, and it generally works well, but it has one problem - it’s written in Silverlight. Whatever your opinion on Silverlight may be, it requires an extra installation and won’t run on any mobile devices. This is where the Revit Server API comes in. Using this REST API, I was able to recreate this website in HTML and Javascript. The result looks like this:

As my main motivation for this was to give the Revit Server REST API a run through, I’ve not made it absolutely the same, there are some minor visual and functional differences, but for the most part, it behaves in the same way. All of the action buttons are functional, you can see a submission history and folder structure.

For the moment, the main limitations are:

  • When you create or paste a folder, the treeview on the left gets recreated and starts collapsed again. This is fixable, and if there is interest, I’ll polish this up (probably by replacing the treeview library with a new one)
  • The submission history isn’t paged or sortable, its just a table with all the information in it. This should be fixable as well.

I’m interested to hear if anyone is genuinely interested in using this tool in their environment, if so, I’ll keep improving it, if not, then it will serve as a demonstration of the Revit Server API’s capabilities, and will form a part of my AU Virtual session this year.

The website is available on Github, to install it on your server simply download the zip file from that site, and create a folder called ‘html’ in ‘C:\Program Files\Autodesk\Revit Server 2012\Sites\RevitServerAdmin’ on the machine you have Revit Server installed. Put all the files in there, and you should be able to navigate to http://localhost/RevitServeradmin/html/ to access it.

For those interested in the technical details, the page is simple HTML (it’s named Default.aspx even though it isn’t an ASP.NET web page, it’s named this way because this allows us to access it by going to /html/ instead of /html/default.htm), with a CSS stylesheet and some images to replicate the interface of the Silverlight tool. The real work happens in the RevitServer.js file, which, with the help of Jquery, calls the REST web service for each of the various actions that the user performs, and updates the DOM with the results. There is no server side ASP.NET code involved, it’s all Javascript with the Revit Server REST API doing the heavy lifting.

Of course, if you want to use the Revit Server API, you don’t need to use Javascript, you could write a similar thing in C# or VB code and service up in an ASP.NET web page, or just about any language you like as long as they can make HTTP Requests.

If you want to know more about how it all works, or more about the Revit Server API, I’ll be going into more detail in my AU 2011 Virtual session.

Friday, September 30, 2011

[Revit Server]- How to get a listing of all Revit Server projects through the REST interface

I’ve been writing some code that utilizes the Revit Server REST API, and in particular, I’m working on a HTML replacement for the Revit Server Admin page, which is currently written in Silverlight. More details on that will come (and will be included in my AU virtual class), but for now I ran into a bit of a stumbling block that others may encounter.

The REST API has an endpoint that lets you list the contents of a certain folder. This endpoint requires a GET request to the path ‘/{folderpath}/contents’ with folderpath being the path of your folder, for example a request to ‘/My_Project/contents’ would list the contents of the My_Project folder. But how do you find out that there is a My_Project folder in the first place? You need to get a listing of all of the folders underneath the parent folder on the server, but how would you do that? You can’t send you request to ‘/contents’, as you are missing the folder path section, and thus it’s not a valid endpoint according to the API. I tried a number of different combinations of URL’s before I finally found one that seems to work.

To get a listing of all the folders underneath the parent folder, send your GET request to:

‘/ /contents’

That is, use a space instead of a folder path, like so:

http://localhost/RevitServerAdminRESTService/AdminRESTService.svc/ /contents”

This will be treated as if it is the root folder, and will list the folders underneath that.

[Revit]- Autodesk University 2011 Virtual Class: Automating Autodesk Revit Server

The Autodesk University conference in Las Vegas is approaching us once again. I’ve had the great privilege of presenting at this event for the past two years on topics concerning the Revit API. At this stage it doesn’t look like I’ll be making the trip from Australia this year to attend the physical conference, but that hasn’t stopped me from being involved virtually! I’ve been accepted to present a class on “Automating Autodesk Revit Server”.  Here is the class description:

Class Description

The latest version of Revit Server offers a number of automation capabilities, from locking files and creating directories to generating a new local file for a Revit central model. This class will explore these options and consider some scenarios where they can be leveraged along with existing Microsoft® Windows® and Revit APIs to perform time-saving tasks. A number of examples will be showcased, including how to create a HTML replacement for the Silverlight® admin tool, how to create a Revit Server folder from an existing system, and how it is possible to have a scheduled task that runs once a day, creates a local file, opens it, and exports the model to a DWF™ without user interaction.

Key Learning

  • Explain how Revit server automation can be integrated into existing software processes
  • Describe the REST interface to Revit Server
  • Combine the Revit Server command line and the Revit API to perform a task in a Revit model on a schedule
  • Use the supplied command line utilities to automate Revit Server

The idea for this class came about when I was playing around with Revit Server and noticed that there is a document on the REST API in the Revit SDK. I had a few ideas for interesting things to do with this API, and Revit Server in general, and what better motivation to put them into action than the pressure of presenting an AU class on the topic :)

Virtual classes only go for 45 minutes, so the code will mostly be read through and explained rather than written live, but I’ll have a bunch of code available for download and in the handout.

If you are interested in Revit Server, be sure to check it out! If you aren’t, but are interested in Revit API development, check out the various other Revit API classes that will be a part of the event, the Revit API team have once again put together a great curriculum to help you get as much API knowledge as you can out of AU.

Monday, August 22, 2011

[Revit API]-How to create an installer for your Revit Addins

Often I see Revit API utilities distributed with instructions to copy manifest files around and put the files in a certain directory (I’ve done it myself in the past), which works fine, but there is a more user friendly way of setting up your addins. You can use Visual Studios ‘Setup Project’ functions to create an installer, which isn’t the pretties installer going around, but it gets the job done.
Doing this is a little harder than you’d think, there’s a few gotchas, so this post aims to step you through it.
Add a new project to your solution, under ‘Other Project Types’, ‘Setup and Deployment’ choose ‘Setup Project’.

You’ll see a view like this, delete ‘Users Desktop’, ‘User Programs Menu’

Right click on Application folder and go to add > project output. Choose the Addin project.

Right click on application folder again, go to properties and change the DefaultLocation to be:
[ProgramFiles64Folder][Manufacturer]\[ProductName]
This is so that it will always be in a consistent install location, rather than program files (x86).
Now, we don’t want to distribute RevitAPI and RevitAPIUI dlls, so in the solution explorer, expand detected dependencies and right click on those and click exclude.

Click on the installer project, then click the properties tab, and set the ‘Manufacturer’ and ‘ProductName’ to match your company/addin name. Also, while you are here, change the ‘TargetPlatform’ to X64. Unfortunately with these types of installers you need a separate install for X86 and X64, but I think most of you will be building API addins for X64 systems, as Revit generally needs at least 4gb of RAM, and if you are enough of a power user to be installing add-ins, you are going to be running a 64bit operating system I’m sure. But keep in mind that it may vary for you.
We need to create a .addin manifest file that points to the program files folder as follows, make sure you generate a new GUID.
<RevitAddIns>
  <AddIn Type="Command">
    <Text>My Revit Util</Text>
    <Assembly>C:\Program Files\COMPANY NAME\PRODUCT NAME\Addin.dll</Assembly>
    <AddInId>18520534-3ee1-473e-ae97-a68dfbda3754</AddInId>
      <FullClassName>MyRevitUtil.Command</FullClassName>
  <VendorId>COMP </VendorId>
     <VendorDescription>MY COMPANY </VendorDescription>  
  </AddIn>
</RevitAddIns>

In The primary output screen, add a new custom folder, called Autodesk Addins. Right click on it and go to properties and set the DefaultLocation to
[CommonAppDataFolder]\Autodesk\Revit\Addins\2012
and Property to AUTODESKADDINS
This will install it for all users. Right click in the middle panel and go to add file, and choose your manifest.

At this point you’re almost done, there’s just a couple of .NET settings to tweak. First of all, we don’t need to bundle an installer for .NET framework as Revit installs it by default, so right click on your installer project, go to properties and hit prerequisites

Then untick the first checkbox

Finally, right click on your project, click View > Launch Conditions, click .NET framework and change it to .NET Framework 4 (or lower if you are in Revit 2011 mode still).

Now you are done, you can right click your project and go to Build and your .msi installer will be output.

Monday, August 1, 2011

[WPF]-How to select checkbox of items in ListBox by clicking text

In WPF, you can create a checked list box by creating a normal ListBox and using an ItemTemplate as follows:
<ListBox Margin="10" HorizontalAlignment="Stretch" Name="lbSheets" 
              VerticalAlignment="Stretch" Width="Auto" Grid.Row="1" MinWidth="321"
              MinHeight="40" HorizontalContentAlignment="Left" 
              ItemTemplate="{StaticResource ListBoxItemTemplate}" VerticalContentAlignment="Top" Background="#FFDCEBEE" SelectionMode="Single" SelectionChanged="lbSheets_SelectionChanged">
 
     </ListBox>


<DataTemplate x:Key="ListBoxItemTemplate" >
 
     <WrapPanel>
         <CheckBox Focusable="False" IsChecked="{Binding Selected}" VerticalAlignment="Center" />
         <ContentPresenter  Content="{Binding FullName, Mode=OneTime}"  Margin="2,0" />
     </WrapPanel>
 
 </DataTemplate>

In my example, I am then binding a domain model object that implements INotifyPropertyChanged, and I have a boolean property for Selected, when the checkbox is checked, it will toggle this value, and vice versa. The text that is displayed is from a property called ‘FullName’, as specified in the DataTemplate.


However, I wanted to be able to check the checkboxes by clicking on the text next to them, not by pinpointing the check box. I tried a number of ways of doing this before finally arriving at a solution.
I ended up suing the ‘MouseUp’ event on the ContentPresenter from my DataTemplate
<DataTemplate x:Key="ListBoxItemTemplate" >
 
      <WrapPanel>
          <CheckBox Focusable="False" IsChecked="{Binding Selected}" VerticalAlignment="Center" />
          <ContentPresenter  Content="{Binding FullName, Mode=OneTime}"  Margin="2,0" MouseUp="ContentPresenter_MouseUp" />
      </WrapPanel>
 
  </DataTemplate>
And in this method I did the following:
private void ContentPresenter_MouseUp(object sender, MouseButtonEventArgs e)
{
    string text = ((TextBlock) e.OriginalSource).Text;
 
    foreach (var item in this.lbSheets.Items)
    {
        PrintableSheet sheetItem = (PrintableSheet) item;
        if (sheetItem.FullName.Equals(text))
        {
            sheetItem.Selected = !sheetItem.Selected;
        }
    }
 
}
I used the OriginalSource proprety from the event arguments to get the TextBlock, I used its text, and matched it up with my domain object (PrintableSheet) using the property that was bound. So now when you click the text on the item, it will toggle the checkbox.