Showing posts with label Revit MEP 2011. Show all posts
Showing posts with label Revit MEP 2011. Show all posts

Wednesday, December 8, 2010

Revit MEP Reuseable System Layouts Across Multiple Projects

Before anyone boo's me... this is a NON API post...

This one's for you HVAC guys out there struggling with the speed of Revit MEP. I know it can be a little painful at times, but boy is it nice to have an intelligent model as a finished product that you can really make use of throughout the CA and construction phase to help avoid change orders and SNAFU's in the field.

I'll have to warn you first about the performance hits that you may encounter if you fail to "UNGROUP" these systems after you place them into your model... PLEASE remember to ungroup all placements after they dropped into your model as these groups will really leave a mark in terms of performance if left as live groups.

OK... let's get started...

Have you ever worked on a building that uses repeatable VAV layouts or has numerous zones that for the most part are all basically the same in terms of elements placed to design it? This method can be used for just about anything you can imagine that utilizes system families, tags, text, and well... you get the idea.

I'm going to show you a way that you can build a warehouse of reusable system layouts complete with equipment, ductwork, and air terminals that you can reuse on all of your projects.

First let's start by drawing a full system complete with a VAV unit, all necessary ductwork, terminals and tags (you really could add anything you want to this).
Then let's select the items and create a group. Notice how you get two options for naming the groups? Tags will be placed into a nested group that you can name independently from the model group.

Now that you've got the group created, verify that your project browser displays the model group with a nested detail group beneath it like the image below.

Now to test if this thing works, right-click the model group from the above mentioned model group in the project browser and chose "Create Instance" and place one in your model (anywhere). It should look like the one below (without the tags... don't worry...).

Did you notice in the image above that you have an option button named "Attached Detail Group" when you select the model group that you just placed? Pick it and select the nested detail group name in the resulting dialog box.

Now your group should look a little more complete like the one below.

Now you can export your preconfigured system layout groups to a server where they can be shared across projects...


Use the "Load as Group" button in the "Insert" tab of the ribbon to add an externally saved group to a new project...


Monday, November 29, 2010

By the Book Part 2 - Harvesting Families from a Project Model

This post is part 2 in response to the sample Revit API application I wrote for the book entitled "Mastering Revit Architecture 2011"...  in Chapter 24 "Under the Hood of Revit."


Well, I promised I would show you the updated family export code in my previous post "By the Book Part 1 - Harvesting Families from a Project Model."... so without further procrastination...

We'll get started off by setting the code to our export button illustrated below. All this does is hide the lower buttons to make room for the progress bar and then runs the export routine. When we're all done, we'll call the close function for the form and exit out.


''' <summary>
    ''' Export the families and then quietly close
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub ButtonExport_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonExport.Click
        Me.ButtonCancel.Visible = False
        Me.ButtonExport.Visible = False
        Me.ButtonSelectAll.Visible = False
        Me.ButtonSelectNone.Visible = False
        doExport()
        ' We're all done
        Me.Close()
    End Sub

I guess we should get the last remaining function out of the way as well before we dive into the export function. The function below is used to verify that all characters used to create a file name are valid for the Windows OS.


''' <summary>
    ''' Make sure the path does not contain any invalid file naming characters
    ''' </summary>
    ''' <param name="fileName">The Filename to check</param>
    ''' <returns>A string</returns>
    ''' <remarks></remarks>
    Private Function CheckValidFileName(ByVal fileName As String) As String
        For Each c In Path.GetInvalidFileNameChars()
            If fileName.Contains(c) Then
                ' Invalid filename characters detected...
                ' Could either replace characters or return empty
                Return ""
            End If
        Next
        Return fileName
    End Function

The export function starts out by first verifying that at least one category has been checked for export. Then a hashtable is used as a means to reference each selection (hashtables are FAST).


''' <summary>
    ''' This routine performs the exports
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub doExport()
        ' Ony export families that belong to our selected categories!
        Dim m_SelectedCategories = Me.CheckedListBoxCategories.CheckedItems
        ' Do nothing if nothing selected
        If m_SelectedCategories.Count = 0 Then
            MsgBox("You did not select any categories..." & vbCr & "Nothing to do...", _
                   MsgBoxStyle.Information, "No Categories Selected! Exiting...")
            Exit Sub
        End If
        ' A hashtable comes in handy when verifying multiple situations... or 1
        Dim m_CatHash As New Hashtable
        For Each xItemC In m_SelectedCategories
            m_CatHash.Add(xItemC.ToString, "Category")
        Next

The next thing to do is make sure the target directory exists for the export.


Try ' If the parent export directory is missing, create it
            Directory.CreateDirectory(Replace(Me.LabelExportPath.Text, "/", "\", , , CompareMethod.Text))
        Catch ex As Exception
            ' Message to show any errors
            MsgBox(Err.Description, MsgBoxStyle.Information, Err.Source)
        End Try

With the main directory created, we can continue with the element collection and progress bar setup. The filter below grabs all "Type" elements from the model and turns the result into an easy to use list of DB.Element.


' Filter to get a set of elements that are elementType 
        Dim m_SymbFilter As New DB.ElementIsElementTypeFilter
        Dim collector As New DB.FilteredElementCollector(m_Doc)
        collector.WherePasses(m_SymbFilter)
        ' Create a list from the collector
        Dim FamilySymbols As New List(Of DB.Element)
        FamilySymbols = collector.ToElements
        ' Start the progressbar
        Dim iCnt As Integer = 0
        Dim iCntFam As Integer = FamilySymbols.Count
        Me.ProgressBar1.Visible = True
        Me.ProgressBar1.Minimum = 0
        Me.ProgressBar1.Maximum = iCntFam
        Me.ProgressBar1.Value = iCnt

Now we can iterate the element list and perform the necessary exports as external RFA files.


' The export process
For Each x As DB.Element In FamilySymbols
    If (TypeOf x Is DB.FamilySymbol) Then
        Dim m_category As DB.Category = x.Category
        If Not (m_category Is Nothing) Then
            ' Is it a selected category?
            If m_CatHash.Contains(m_category.Name) Then
                Dim m_ExportPath As String = ""
                Try ' Create the subdirectory
                    m_ExportPath = Me.LabelExportPath.Text & "\" & m_category.Name & "\"
                    Directory.CreateDirectory(Replace(m_ExportPath, "/", "\", , , CompareMethod.Text))
                Catch ex As Exception
                    ' Category subdirectory exists
                End Try
                Try ' The family element
                    Dim m_FamSymb As DB.FamilySymbol = x
                    Dim m_FamInst As DB.Family = m_FamSymb.Family
                    Dim m_FamName As String = m_FamInst.Name
                    ' Verify famname is valid filename and exists
                    If Dir$(m_ExportPath + m_FamName & ".rfa") = "" And CheckValidFileName(m_FamName) <> "" Then
                        Me.LabelFileName.Text = "...\" & m_category.Name & "\" & m_FamInst.Name
                        Dim famDoc As DB.Document = m_Doc.EditFamily(m_FamInst)
                        famDoc.SaveAs(m_ExportPath + m_FamName & ".rfa")
                        famDoc.Close(False)
                    End If
                Catch ex As Exception
                    ' Prevent hault on system families
                End Try
            End If
        End If
    End If
    ' Step the progress bar
    Me.ProgressBar1.Increment(1)
Next

That's it! Now you have the means to quickly harvest families from a Revit 2011 model.

If you have any questions or suggestions for other Revit 2011 code solutions, don't hesitate to leave a comment or ask a question.

Saturday, November 27, 2010

By the Book Part 1 - Harvesting Families from a Project Model

This post is part 1 in response to the sample Revit API application I wrote for the book entitled "Mastering Revit Architecture 2011"... in Chapter 24 "Under the Hood of Revit." The book has a five star amazon rating (I'm sure most of it is due to the insane quality of Chapter 24). It's also available on Kindle!!!


I'm sure you'll also be excited to know that ALL of the authors for the above mentioned book will be at Autodesk University 2010 this week in Las Vegas!... So if you like autographs, bring your book and start yourself a man hunt to find these guys!... weeeee

I'll have to admit, I wrote the original sample very quickly and could have done a better job in terms of its feature availability. For instance, I left out the ability to select categories to export! That's right, this post will add functionality for category selection!

First create a form named "form_Main" and add a checked listbox named "CheckedListBoxCategories" along with five buttons named "ButtonSelectAll", "ButtonSelectNone", "ButtonExport", "ButtonCancel", and "ButtonBrowse." Add a progress bar named "ProgressBar1" and a few labels named "LabelExportPath", "LabelFileName", and "LabelExport." When you're done, your form should resemble something close to the image below.


Now that we've got the interface all worked out, let's get the form class constructor put together. The code below shows the required imports along with the basic class constructor that will be called to eventually display the form.


Imports Autodesk.Revit
Imports System.Windows.Forms
Imports System.IO

Public Class form_FamilyExport

    Private m_App As UI.UIApplication = Nothing
    Private m_Doc As DB.Document

    ''' <summary>
    ''' Form class constructor, don't forget InitializeComponent()
    ''' </summary>
    ''' <param name="cmdData">The UI.ExternalCommandData object</param>
    ''' <param name="strAppVer">Application Version</param>
    ''' <remarks></remarks>
    Public Sub New(ByVal cmdData As UI.ExternalCommandData, ByVal strAppVer As String)
        InitializeComponent()
        ' Private variables
        m_App = cmdData.Application
        m_Doc = m_App.ActiveUIDocument.Document
        ' Form configurations
        Me.Text = "Batch Export Families - " & strAppVer
        Me.ProgressBar1.Visible = False
        Me.ButtonExport.Enabled = False
        ' Set default export path adjacent to model location
        ' If workshared, use the central model path
        If m_Doc.IsWorkshared = True Then
            Try
                Me.LabelExportPath.Text = Path.GetDirectoryName(m_Doc.WorksharingCentralFilename) & "\Exported Families\"
            Catch ex As Exception
                ' Detached model will not have a file path
            End Try
        Else
            Me.LabelExportPath.Text = Path.GetDirectoryName(m_Doc.PathName) & "\Exported Families\"
        End If
        ' Clear the list
        Me.CheckedListBoxCategories.CheckOnClick = True
        Me.CheckedListBoxCategories.Items.Clear()
        Me.LabelFileName.Text = ""
        Me.LabelExportPath.Text = ""
        ' Get all categories
        GetCategories()
    End Sub

End Class

Everything so far is the same as what we talk about in the book except for the call to a function named GetCategories(). This new category function is very basic and only collects the names of all categories available in your Revit environment. The results are then recorded into the checkedlistbox on our form. The function below collects the strings into a list first so we can sort them and then from the list to the listbox.


''' <summary>
    ''' Get a list of all 'non tag' categories
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub GetCategories()
        ' Full list of Categories
        Dim categoryList As New List(Of String)
        For Each category As DB.Category In m_Doc.Settings.Categories
            categoryList.Add(category.Name)
        Next
        ' Alpha sort the list
        categoryList.Sort()
        ' Add categories to the listbox
        For Each x As String In categoryList
            If InStr(UCase(x), "TAGS", CompareMethod.Text) = 0 Then
                ' Add the category
                Me.CheckedListBoxCategories.Items.Add(x)
            End If
        Next
    End Sub

Now that we have a massive list of categories we should probably provide a quick means for selecting all or none of the items in the list. The code illustrates how the ButtonSelectNone and ButtonSelectAll buttons to their thing.


''' <summary>
    ''' Uncheck all items in the listbox
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub ButtonSelectNone_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonSelectNone.Click
        For i As Integer = 0 To CheckedListBoxCategories.Items.Count - 1
            CheckedListBoxCategories.SetItemChecked(i, False)
        Next
    End Sub

    ''' <summary>
    ''' Check all items in listbox
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub ButtonSelectAll_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ButtonSelectAll.Click
        For i As Integer = 0 To CheckedListBoxCategories.Items.Count - 1
            CheckedListBoxCategories.SetItemChecked(i, True)
        Next
    End Sub

The updated export code will be demonstrated in "By the Book Part 2 - Harvesting Families from a Project Model", so stay tuned to see all that excitement!...

Thursday, November 25, 2010

A Classy Way to Handle Revit Parameters

You may have noticed some of my previous posts referencing a clsPara object. I've been getting lots of interesting questions regarding this object and how it's built and just what the heck it does. Well... I guess I'll share.

My clsPara class is entirely reusable and very handy for handling data transactions in and out of parameters. Not only does this object connect with built-in parameters, it also connects with element properties! I know, its SO super exciting (breathe).

Let's start from the beginning... create a new class named clsPara and add the basic Autodesk.Revit reference as illustrated below.


Imports Autodesk.Revit

''' <summary>
''' An awesome class used to define a parameter
''' </summary>
''' <remarks></remarks>
Public Class clsPara

End Class

The next thing we need to do is provide a constructor that we can use to build this class into a meaningful object. All we need to accept as an argument is a Revit parameter. We'll handle all the rest internally within the class in a very simple yet highly efficient manner.


Private m_parameter As DB.Parameter

''' <summary>
''' Constructor
''' </summary>
''' <param name="parameter">Revit Parameter Object</param>
''' <remarks></remarks>
Public Sub New(ByVal parameter As DB.Parameter)
   m_parameter = parameter
End Sub

Our new clsPara object can now be called from anywhere in the project with the following code and argument (where myParam is a valid Revit Parameter reference):


Dim myLittleParam As New clsPara(myParam)

The next step is to provide all the basic data interaction routines and properties. We'll start with the data interactions for retrieving data from a Revit Parameter within the class.


''' <summary>
    ''' Get a parameter's value
    ''' </summary>
    ''' <param name="parameter"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function GetParameterValue(ByVal parameter As DB.Parameter) As String
        'public static Object GetParameterValue(Parameter parameter) 
        Select Case parameter.StorageType
            Case DB.StorageType.[Double]
                'get value with unit, AsDouble() can get value without unit 
                Return parameter.AsDouble
            Case DB.StorageType.ElementId
                ' Returns Only the ElementID
                Return parameter.AsElementId.IntegerValue
            Case DB.StorageType.[Integer]
                'get value with unit, AsInteger() can get value without unit 
                Return parameter.AsInteger
            Case DB.StorageType.None
                Return parameter.AsValueString()
            Case DB.StorageType.[String]
                Return parameter.AsString()
            Case Else
                Return ""
        End Select
    End Function

Now another function to SET a value to a parameter:


''' <summary>
    ''' Set a Parameter's value
    ''' </summary>
    ''' <param name="parameter"></param>
    ''' <param name="value"></param>
    ''' <remarks></remarks>
    Public Shared Sub SetParameterValue(ByVal parameter As DB.Parameter, ByVal value As Object)
        'first,check whether this parameter is read only 
        If parameter.IsReadOnly Then
            Exit Sub
        End If
        Select Case parameter.StorageType
            Case DB.StorageType.[Double]
                'set value with unit, Set() can set value without unit 
                parameter.SetValueString(TryCast(value, String))
                Exit Select
            Case DB.StorageType.ElementId
                Dim myElementId As DB.ElementId = DirectCast((value), DB.ElementId)
                parameter.[Set](myElementId)
                'MsgBox("Reminder to finish elementid write routine...")
                Exit Select
            Case DB.StorageType.[Integer]
                'set value with unit, Set() can set value without unit 
                parameter.SetValueString(TryCast(value, String))
                Exit Select
            Case DB.StorageType.None
                parameter.SetValueString(TryCast(value, String))
                Exit Select
            Case DB.StorageType.[String]
                parameter.[Set](TryCast(value, String))
                Exit Select
            Case Else
                Exit Select
        End Select
    End Sub

Now that we can set and get a value for a Revit Parameter... let's add some functionality to react within the class using some handy properties!!! Exciting, I know....


''' <summary>
    ''' This property will return the value!!
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Value() As String
        Get
            Try
                Return GetParameterValue(m_parameter)
            Catch
                Return Nothing
            End Try
        End Get
        Set(ByVal value As String)
            Try
                SetParameterValue(m_parameter, value)
            Catch
            End Try
        End Set
    End Property

    ''' <summary>
    ''' What's the unit type, anyway?
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property DisplayUnitType() As String
        Get
            Try
                Return m_parameter.DisplayUnitType.ToString
            Catch
                Return Nothing
            End Try
        End Get
    End Property

    ''' <summary>
    ''' True if this is a Read Only parameter such as Area!!
    ''' Will not fail when trying to write to a read-only parameter!!
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property ParameterIsReadOnly() As Boolean
        Get
            Try
                Return m_parameter.IsReadOnly
            Catch
                Return Nothing
            End Try
        End Get
    End Property

    ''' <summary>
    ''' Returns true or false if this is a Shared Parameter!
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property ParameterIsShared() As Boolean
        Get
            Try
                Return m_parameter.IsShared
            Catch
                Return Nothing
            End Try
        End Get
    End Property

    ''' <summary>
    ''' Returns the type of parameter, sometimes useful
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property ParaType() As String
        Get
            Try
                Return m_parameter.GetType.Name
            Catch
                Return Nothing
            End Try
        End Get
    End Property

    ''' <summary>
    ''' Returns the parameter name
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property ParaName() As String
        Get
            Try
                Return m_parameter.Definition.Name
            Catch
                Return Nothing
            End Try
        End Get
    End Property

    ''' <summary>
    ''' This property will return the data format!
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property Format() As String
        Get
            Try
                Return m_parameter.StorageType.ToString
            Catch
                Return Nothing
            End Try
        End Get
    End Property

... I know!!! Don't forget to breathe!!

Saturday, November 20, 2010

Tracking Revision Clouds in Revit 2011 can be a bit... Cloudy

Have you ever tried to schedule Revision Clouds in Revit 2011?... not so fun when you find that it is impossible... not to mention that if you select a revision cloud in a sheet and try the ol "Select All Instances" that the option is NOT supported for Revision Clouds... WTF....

Imagine if you had a means to export all revision clouds in your model to a neat and easy to view Excel document where you could see the sheet number of the sheet that the cloud displays on along with even the comments and revision date and sequence information... that would be pretty cool, huh?

I think this is a real short coming of Revit 2011, so here is a bit of code I used to solve the problem. I obviously cannot give you ALL of the code (what fun would that be?)... but I will share with you how I got ahold of the elements and then queried their view names and target sheet numbers and names.

First create a form and drop a filesave dialog from the tools menu and name it "SaveFileDialogCSV"... set the filetype filter to CSV... bla bla bla... The code below demonstrates the verification on the resulting file saved by the dialog. Eventually when satisfied there is a little function called "doScan" that actually scans the model for the Revision Cloud elements.


Me.SaveFileDialogCSV.ShowDialog()
If SaveFileDialogCSV.FileName <> "" Then
    If File.Exists(SaveFileDialogCSV.FileName) = True Then
        Try
            ' Start with a clean file
            File.Delete(SaveFileDialogCSV.FileName)
        Catch ex As Exception
        End Try
    End If
    ' Scan for Revision Cloud Elements
    doScan()
Else
    MsgBox("Please select a valid file name and location", MsgBoxStyle.Exclamation, "Error")
    Me.Close()
End If

The next section of code generates a couple lists. One list for the Revision Cloud elements in the project selected by category and one for the sheets that exist in the model. This sample will ignore revision cloud elements that are on views that are not placed on sheets since if they aren't on a sheet, they are not part of the documentation (sounded logical at the time). At the end, we make sure the model has at least one sheet before we continue.


' Collect Sheets
m_Sheets = New List(Of DB.Element)
Dim m_SheetCollector As New DB.FilteredElementCollector(m_Doc)
m_SheetCollector.OfCategory(DB.BuiltInCategory.OST_Sheets)
m_Sheets = m_SheetCollector.ToElements

' Collect Revision Clouds
m_RevClouds = New List(Of DB.Element)
Dim m_RevCloudCollector As New DB.FilteredElementCollector(m_Doc)
m_RevCloudCollector.OfCategory(DB.BuiltInCategory.OST_RevisionClouds)
m_RevClouds = m_RevCloudCollector.ToElements

' No sheets... no revisions!
If m_Sheets.Count < 1 Then Me.Close()

Now the next section saves the data it finds for each ViewSheet into a class named clsSheetMapper (not shown) so I can maintain an easy reference to the data between the clouds found on sheets and their host sheets.


' List all sheets and views for easy reference
        For Each x As DB.ViewSheet In m_Sheets
            ' Sheet element
            Dim m_Sht As New clsSheetMapper(x.Id.ToString, True)
            ' Sheet Number
            Dim m_ShtNumber As String = x.SheetNumber
            m_Sht.SheetNumber = m_ShtNumber
            ' Sheet Name
            Dim m_ShtName As String = x.Name
            m_Sht.SheetName = x.Name
            ' Add the view to the master list
            m_ViewsList.Add(m_Sht)
            ' Add the Views
            For Each y As DB.Element In x.Views
                ' View element
                Dim m_View As New clsSheetMapper(y.Id.ToString, False)
                ' Sheet Number
                m_View.SheetNumber = m_ShtNumber
                ' Sheet Name
                m_View.SheetName = m_ShtName
                ' View Name
                m_View.ViewName = y.Name
                ' Add the view to the master list
                m_ViewsList.Add(m_View)
            Next
        Next

The next bit of code finishes it up by scanning the revision cloud elements into a class named clsRevCloud I use to collect all of the view naming, sheet data and Revision Cloud parameter data into a single class and saving their parameter data to yet another secret class not shown named clsPara.


' Write the title line in our CSV file
        writeCSVline("Sheet Number, Sheet Name, View Name, ElementID, Revision Number, Revision Date, Comments, Mark, Issued To, Issued By")
        m_Revs.Clear()
        ' Process Revision Cloud Elements
        For Each m_RevCloud As DB.Element In m_RevClouds
            ' Create a matching Rev Item
            Dim m_RevItem As New clsRevcloud(m_RevCloud.Id.ToString)
            ' Test for viewID
            For Each x As clsSheetMapper In m_ViewsList
                Try
                    If x.ViewID = m_Doc.Element(m_RevCloud.OwnerViewId).Id.ToString Then
                        ' This is the view item
                        If x.SheetNumber IsNot Nothing Then
                            m_RevItem.SheetNumber = x.SheetNumber
                        End If
                        If x.SheetName IsNot Nothing Then
                            m_RevItem.SheetName = x.SheetName
                        End If
                        If x.ViewName IsNot Nothing Then
                            m_RevItem.ViewName = x.ViewName
                        End If
                        For Each y As DB.Parameter In m_RevCloud.Parameters
                            Dim myPara As New clsPara(y)
                            If myPara.Value IsNot Nothing Then
                                Select Case y.Definition.Name.ToUpper
                                    Case "REVISION NUMBER"
                                        m_RevItem.RevisionNumber = myPara.Value
                                    Case "REVISION DATE"
                                        m_RevItem.RevisionDate = myPara.Value
                                    Case "COMMENTS"
                                        m_RevItem.Comments = myPara.Value
                                    Case "MARK"
                                        m_RevItem.Mark = myPara.Value
                                    Case "ISSUED TO"
                                        m_RevItem.IssuedTo = myPara.Value
                                    Case "ISSUED BY"
                                        m_RevItem.IssuedBy = myPara.Value
                                End Select
                            End If
                        Next
                        Exit For
                    End If
                Catch ex As Exception
                    ' Some may not have an ownerID
                End Try
            Next
            m_Revs.Add(m_RevItem)
        Next

Now that we've got this handy list of classes with all of the sheet, view, and parameter data we can iterate them all and write the results to an external file... in this case it is a CSV file.


' Write all of the records
        For Each x As clsRevcloud In m_Revs
            ' Skip items without a sheet number
            If x.SheetNumber IsNot Nothing And x.SheetNumber <> "" Then
                Dim LineItem As String = ""
                LineItem = x.SheetNumber & ","
                LineItem = LineItem & x.SheetName & ","
                LineItem = LineItem & x.ViewName & ","
                LineItem = LineItem & x.ElementID & ","
                LineItem = LineItem & x.RevisionNumber & ","
                LineItem = LineItem & x.RevisionDate & ","
                LineItem = LineItem & x.Comments & ","
                LineItem = LineItem & x.Mark & ","
                LineItem = LineItem & x.IssuedTo & ","
                LineItem = LineItem & x.IssuedBy
                ' Write the Line
                writeCSVline(LineItem)
            End If
        Next

That's it... now if you ever have a HUGE project and a needy client that wants this data each time to issue a revision set, you can export a handy report for their use as well as for you to help keep track of your changes in your models.

Monday, August 16, 2010

Stale Bread or Toast?

One of the longest burning issues between architects and engineers is ownership of engineering fixtures and the coordination that comes along with them. I can't tell you how many times I've seen a project go out for permit with the light fixture counts being out of whack between the Architectural RCP and the Electrical Lighting Plans...

Revit MEP now offers a solution for this, but it still takes some compromise between the two teams (this is usually where the break down occurs, but maybe technology can help bridge the gap)...

Revit MEP 2011 now offers the ability to "Copy Monitor" MEP fixture elements between referenced models!! MEP Fixture Elements include:
  • Lighting Fixtures
  • Plumbing Fixtures
  • Air Terminals (HVAC Grilles)
  • Mechanical Equipment (useful between Electrical and Mechanical model)
You may have noticed an extra button in the Collaborate tab of the Revit MEP 2011 ribbon that does not exist in Revit Architecture 2011... The "Coordination Settings" button


This new button gains access to the MEP Fixture monitoring settings as seen below...

You can map the architectural fixtures to your own custom fixtures!! It is important that the architect does not use length parameters to control the fixture mounting heights as this disrupts the location monitoring and will result in goofy coordination warnings. The architect must use the offset parameter to control mounting heights for non hosted fixtures.

There is a "Batch Copy" button in the main Copy Monitor feature making it easier than ever to coordinate fixtures between referenced models in Revit!

You now get notified when fixtures move, get deleted, or new ones get added to the Architectural model! When you select a monitored fixture, you will get a monitor icon above it as seen in the example below.


Opening a model with discrepancies identified between the link and the host model will be prompted as seen in the image below.


Thursday, July 15, 2010

Embedded Schedules in Revit 2011

Some of you expert Revit Architects might be wondering just what in the heck I'm talking about when I mention "Embedded Schedules" in Revit. This feature is currently only available in Revit MEP 2011.

It is possible however to generate a nested schedule in Revit MEP for use in the Revit Architecture product... you just wont be able to modify the nested schedule unless you open the model in Revit MEP 2011...

Revit MEP 2011 supports nesting schedules beneath Rooms, Spaces, and MEP Systems. This basically allows the user to report elements in the model grouped by Room, Space, or System while showing whatever data they choose for the parent as well as the child elements... confused yet?

First start by creating a new Room schedule in Revit MEP 2011. Select the parameters that you want to see for rooms and then select the "Embedded Schedule" tab...


To really see how this feature works, set the "Number" parameter as "Group By" and select "Header" and "Blank Line"

 
Then in the "Embedded Schedule" tab, check both the "Embedded Schedule" and "Show categories from all disciplines" checkboxes. Now you can choose the category that you want to schedule within rooms. Click the "Embedded Schedule Properties" button to select the parameters and general schedule settings that you want to use for your embedded schedule.

And finally... here's what we end up with...

Monday, June 28, 2010

Custom Electrical Panel Schedules??

Look for an updated post on this topic coming March 2011!... this post is very brief and doesn't contain much detail at all:...

This isn't quite dealing with any API, but notable nonetheless...

Perhaps one of the biggest reasons for electrical designers to upgrade to Revit MEP 2011 is the introduction of a fully customizable panel schedule management system. This may not sound like much, but in previous versions of Revit it wasn't even possible to show load classifications as well as several other information required by various jurisdictions. Those days are over...

Not only can you create and manage custom load classifications, but you can show them in your panel schedules if you choose! It is also possible to manage several different panel schedule templates and apply the one you choose to whatever panel schedule you want in your project independently!!

Panel Schedule Templates are managed from the "Panel Schedule Templates" button in the "Manage" tab of the ribbon.

This is where you get access to edit the format of the templates as well as apply the templates to the panels that live in your current project.

Selecting the "Manage Templates" button takes me to a dialog that I can duplicate, edit, create a new, or delete a panel schedule template. As you can see in the image to the right, there are various options for template type, configuration and along the top there is another tab to apply certain templates to panels that exist in my project.

Launching the edit mode for a panel schedule template will present a new mode for Revit where you can edit the arrangement and inclusions for your panel schedule data. You can actually control the font (type, size, style, and color) for each cell individually!

It is possible to add and remove columns and rows wherever you want them. You can also manage the display of the load cells by phase by picking the "Set Template Options" button while in the "Modify Panel Schedule Template" mode.

The options to set include the overall panel schedule width, quantity of circuit slots shown, display of header/load summary/footer, and the border styles used. There are also options under the "Circuit Table" options to control the display of the phase load cells per circuit. When a single-phase panel is active, there is an option to hide the third phase columns as well.

It is also possible to merge and unmerge cells, postcalculated values to a cell, adjust unit formats for a cell, individually manage borders for cells... the list goes on...

It is finally possible to display and document panel schedule information in a useful manner in Revit MEP!!