Author Topic: .NET for AutoCAD MEP  (Read 16434 times)

0 Members and 1 Guest are viewing this topic.

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
.NET for AutoCAD MEP
« on: October 31, 2012, 06:29:36 PM »

LIBRARY THREAD for  AutoCAD MEP .NET
Members are encouraged to post any functions, methods, snips regarding
AutoCAD MEP in .NET : C# ,  VB , F# , Python , etc

Feel free to include comments, descriptive notes, limitations,  and images to document your post.

Please post questions in a regular thread.
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

IDabble

  • Guest
Re: .NET for AutoCAD MEP
« Reply #1 on: November 01, 2012, 10:48:59 PM »
Hey...alright...cool...LET THE ONSLAUGHT BEGIN !!!

Is it OK to post stuff credited to others here ?
Maybe we could start out with something simple.
My contribution(s) generally compile(s) in VS 2010 for MEP 2010.

Code: [Select]
/// <credit> Martin Schmid </credit>
/// <source> PDF - Introduction to the AutoCAD MEP (2007) .NET API </source>
/// <summary> set some MEP-specific database variables
/// notice the API has been subdivided since Martin first wrote this code </summary>
public static void MepSetup()
{
    Database db = HostApplicationServices.WorkingDatabase;
    using (Transaction trans = db.TransactionManager.StartTransaction())
    {
        AecBldgAppSvcs.BuildingDBVariables varsBldgDb =
            trans.GetObject(AecBldgAppSvcs.BuildingDBVariables.GetInstance(db, false), OpenMode.ForWrite) as AecBldgAppSvcs.BuildingDBVariables;
        // Options > MEP Display Control > B - Gap Width
        varsBldgDb.HaloedLineGap = 4;
        // Options > MEP Layout Rules > Collision Detection > Alert
        varsBldgDb.InterferenceDetection = true;
        // View > Show Disconnect markers
        varsBldgDb.ShowBrokenConnectionMarkers = true;
        // set DuctPreferences
        HvacDBVariables varsHvacDb = trans.GetObject(HvacDBVariables.GetInstance(db, false), OpenMode.ForWrite) as HvacDBVariables;
        varsHvacDb.InsulationThickness = 2; // varsBldgDb.DuctInsulationThickness = 2;
        varsHvacDb.EnableInsulation = true; // varsBldgDb.EnableDuctInsulation = true;
        // set PipePreferences
        PipeDBVariables varsPipeDb = trans.GetObject(PipeDBVariables.GetInstance(db, false), OpenMode.ForWrite) as PipeDBVariables;
        varsPipeDb.InsulationThickness = 1; // varsBldgDb.PipeInsulationThickness = 1;
        varsPipeDb.EnableInsulation = false; // varsBldgDb.EnablePipeInsulation = false;
        trans.Commit();
    }
}

I've got various tested methods from others, and not of my own since I've moved on.
I've got some working stuff from Jeff H too...if that's cool with Jeff H

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: .NET for AutoCAD MEP
« Reply #2 on: November 02, 2012, 07:35:57 AM »
Hey...alright...cool...LET THE ONSLAUGHT BEGIN !!!

Is it OK to post stuff credited to others here ?
Maybe we could start out with something simple.
My contribution(s) generally compile(s) in VS 2010 for MEP 2010.

Code: [Select]
/// <credit> Martin Schmid </credit>
/// <source> PDF - Introduction to the AutoCAD MEP (2007) .NET API </source>
/// <summary> set some MEP-specific database variables
/// notice the API has been subdivided since Martin first wrote this code </summary>
public static void MepSetup()
{
    Database db = HostApplicationServices.WorkingDatabase;
    using (Transaction trans = db.TransactionManager.StartTransaction())
    {
        AecBldgAppSvcs.BuildingDBVariables varsBldgDb =
            trans.GetObject(AecBldgAppSvcs.BuildingDBVariables.GetInstance(db, false), OpenMode.ForWrite) as AecBldgAppSvcs.BuildingDBVariables;
        // Options > MEP Display Control > B - Gap Width
        varsBldgDb.HaloedLineGap = 4;
        // Options > MEP Layout Rules > Collision Detection > Alert
        varsBldgDb.InterferenceDetection = true;
        // View > Show Disconnect markers
        varsBldgDb.ShowBrokenConnectionMarkers = true;
        // set DuctPreferences
        HvacDBVariables varsHvacDb = trans.GetObject(HvacDBVariables.GetInstance(db, false), OpenMode.ForWrite) as HvacDBVariables;
        varsHvacDb.InsulationThickness = 2; // varsBldgDb.DuctInsulationThickness = 2;
        varsHvacDb.EnableInsulation = true; // varsBldgDb.EnableDuctInsulation = true;
        // set PipePreferences
        PipeDBVariables varsPipeDb = trans.GetObject(PipeDBVariables.GetInstance(db, false), OpenMode.ForWrite) as PipeDBVariables;
        varsPipeDb.InsulationThickness = 1; // varsBldgDb.PipeInsulationThickness = 1;
        varsPipeDb.EnableInsulation = false; // varsBldgDb.EnablePipeInsulation = false;
        trans.Commit();
    }
}

I've got various tested methods from others, and not of my own since I've moved on.
I've got some working stuff from Jeff H too...if that's cool with Jeff H

This code is only for 2010 and previous.  It will not work with any version after 2010.  Just want to clarify that before someone tried to use it for something post 2010.
Revit 2019, AMEP 2019 64bit Win 10

Keith Brown

  • Swamp Rat
  • Posts: 601
Property Set Utilities
« Reply #3 on: November 02, 2012, 08:13:34 AM »
Here are a couple methods for attaching property sets to objects.  I have pieced together the code with help from theswamp.org members, the adn developer blog, and the samples included with Autocad Mep.

bool CreatePropertySetOnObject(ObjectId MepObjectId, ObjectId PropertySetId)
bool CreatePropertySetOnObject(ObjectIdCollection MepObjectIds, ObjectId PropertySetId)
bool IsPropertySetAttachedToObject(AcadDb.DBObject MepObj, ObjectId PropertySetId)
bool IsPropertySetAttachedToObject(AcadDb.DBObject MepObj, String PropertySetName)
objectId GetPropertySetDefinitionIdByName(string PropertySetName)


I tend to heavily comment my code because I usually forget what I did as soon as I do it.   :D

Code - C#: [Select]
  1. #region References
  2. // Standard References
  3.  
  4. using System;
  5.  
  6. // AutoCAD MEP References
  7.  
  8. using Autodesk.AutoCAD.ApplicationServices;
  9. using Autodesk.AutoCAD.DatabaseServices;
  10. using Autodesk.AutoCAD.EditorInput;
  11. using Autodesk.AutoCAD.Runtime;
  12.  
  13. using Autodesk.Aec.DatabaseServices;
  14. using Autodesk.Aec.PropertyData.DatabaseServices;
  15.  
  16. using AcadDb = Autodesk.AutoCAD.DatabaseServices;
  17. using AecDb = Autodesk.Aec.DatabaseServices;
  18. using AecPropDb = Autodesk.Aec.PropertyData.DatabaseServices;
  19.  
  20. using ObjectId = Autodesk.AutoCAD.DatabaseServices.ObjectId;
  21. using ObjectIdCollection = Autodesk.AutoCAD.DatabaseServices.ObjectIdCollection;
  22.  
  23.  
  24. #endregion
  25.  
  26. namespace MEPUtilities
  27. {
  28.     /// <summary>
  29.     /// A collection of methods related to property sets.
  30.     /// </summary>
  31.     public static class PropertySetUtilities
  32.     {
  33.         #region CreatePropertySetOnObject
  34.  
  35.         /// <summary>
  36.         /// Creates a property set on a given object.
  37.         /// Requires that the property set exists in the current database and
  38.         /// that the property set applies to the object.
  39.         /// </summary>
  40.         /// <param name="MepObjectId">The objectID of the object to create the property set on.</param>
  41.         /// <param name="PropertySetObjectId">The objectID of the property set to create on the object </param>
  42.         /// <returns> True if the property set was created on the object, or false if there was a failure. </returns>
  43.         public static bool CreatePropertySetOnObject(ObjectId MepObjectId, ObjectId PropertySetId)
  44.         {
  45.             Database db = Application.DocumentManager.MdiActiveDocument.Database;
  46.             AcadDb.TransactionManager tm = db.TransactionManager;
  47.             using (Transaction trans = tm.StartTransaction())
  48.             {
  49.                 // Try to attach the property set to the object.
  50.                 try
  51.                 {
  52.                     // First check to see if the property set already exists on the object
  53.                        
  54.                     // create a database object based on the ObjectId of the object
  55.                     AcadDb.Entity dbObject = (AcadDb.Entity)tm.GetObject(MepObjectId, OpenMode.ForRead, true);
  56.                      
  57.                     // Check to see if the property set is already attached to the object
  58.                     if (!IsPropertySetAttachedToObject(dbObject, PropertySetId))
  59.                     {
  60.                         // Attach the property set to the object
  61.                         AcadDb.DBObject dbobj = tm.GetObject(MepObjectId, OpenMode.ForWrite, false, false);
  62.                         AecPropDb.PropertyDataServices.AddPropertySet(dbobj, PropertySetId);
  63.                     } // End If Block
  64.                 }  // End Try Block
  65.  
  66.                 // If there is an error catch it and return false
  67.                 catch (Autodesk.AutoCAD.Runtime.Exception)
  68.                 {
  69.                     return false;
  70.                 }  // End Catch Block
  71.  
  72.                 // At this point the property set was successfully attached so commit the transaction
  73.                 trans.Commit();
  74.             }  // End Try-Catch Block
  75.  
  76.             // The property set has been attached and the transaction committed
  77.             // So we can finish up and return true
  78.             return true;
  79.  
  80.         }  // End CreatePropertySetOnObject Method
  81.         #endregion
  82.  
  83.         #region CreatePropertySetOnObjects
  84.        
  85.         /// <summary>
  86.         /// Creates a property set on a set of given objects.
  87.         /// Requires that the property set exists in the current database and
  88.         /// that the property set applies to the objects.
  89.         /// </summary>
  90.         /// <param name="MepObjectIds">The objectIDs of the objects to create the property set on.</param>
  91.         /// <param name="PropertySetId">The objectID of the property set to create on the object </param>
  92.         /// <returns> True if the property set was created on the objects, or false if there was a failure. </returns>
  93.         public static bool CreatePropertySetOnObject(ObjectIdCollection MepObjectIds, ObjectId PropertySetId)
  94.         {
  95.             Database db = HostApplicationServices.WorkingDatabase;
  96.             AcadDb.TransactionManager tm = db.TransactionManager;
  97.             using (Transaction trans = tm.StartTransaction())
  98.             {
  99.                 // Try to attach the property set to the objects.
  100.                 try
  101.                 {
  102.  
  103.                     // Iterate through the objects, open them for write and then attach the property
  104.                     // Set to the object
  105.                     foreach (ObjectId MepObjectId in MepObjectIds)
  106.                     {
  107.                         // create a database object based on the ObjectId of the object
  108.                         AcadDb.Entity dbObject = (AcadDb.Entity)tm.GetObject(MepObjectId, OpenMode.ForRead, true);
  109.                      
  110.                         // Check to see if the property set is already attached to the object
  111.                         if (!IsPropertySetAttachedToObject(dbObject, PropertySetId))
  112.                         {
  113.                             // Attach the property set to the object
  114.                             AcadDb.DBObject dbobj = tm.GetObject(MepObjectId, OpenMode.ForWrite, false, false);
  115.                             AecPropDb.PropertyDataServices.AddPropertySet(dbobj, PropertySetId);
  116.                         } // End If Block
  117.                     } // End ForEach Block
  118.                 }  // End Try Block
  119.  
  120.                 // If there is an error catch it and return false
  121.                 catch (Autodesk.AutoCAD.Runtime.Exception)
  122.                 {
  123.                     return false;
  124.                 }  // End Catch Block
  125.  
  126.                 // At this point the property set was successfully attached so commit the transaction
  127.                 trans.Commit();
  128.             }  // End Try-Catch Block
  129.  
  130.             // The property set has been attached and the transaction committed
  131.             // So we can finish up and return true
  132.             return true;
  133.  
  134.         }  // End CreatePropertySetOnObject Method
  135.         #endregion
  136.  
  137.         #region IsPropertySetAttachedToObject
  138.         /// <summary>
  139.         /// Finds a property set by its ObjectId on a given object.
  140.         /// </summary>
  141.         /// <param name="MepObj">The object to find the property set on.</param>
  142.         /// <param name="PropertySetId">The property set ObjectId to find on the object.</param>
  143.         /// <returns> True if the property set with the given ObjectId was found, or false otherwise. </returns>
  144.         public static bool IsPropertySetAttachedToObject(AcadDb.DBObject MepObj, ObjectId PropertySetId)
  145.         {
  146.  
  147.             // Create and set a temporary ObjectID to Null
  148.             ObjectId TempId = ObjectId.Null;
  149.  
  150.             try
  151.             {
  152.                 // Attempt to set the temporary ObjectID equal to the objectID of the
  153.                 // of the property set we are attempting to find on the object.
  154.                 // If the property set exists on the object then the temporary ObjectId
  155.                 // will be assigned a value
  156.                 TempId = AecPropDb.PropertyDataServices.GetPropertySet(MepObj, PropertySetId);
  157.             } // End Try Block
  158.  
  159.             // If the property set does not exist on the object then an error will be raised
  160.             catch (Autodesk.AutoCAD.Runtime.Exception)
  161.             {
  162.                 // Catching an error is not the best way to find out if a property set is
  163.                 // Attached to an object. A better way would be to query the objects extension
  164.                 // dictionary and see if the property set is in the extension dictionary
  165.             } // End Catch Block
  166.  
  167.             // If the temp ObjectId is not null then we found the property set on the object
  168.             if (!TempId.IsNull)
  169.                 return true;
  170.  
  171.             // If we are at this point then the property set was not found on the object
  172.             // and we can return false.
  173.             return false;
  174.  
  175.         } // End IsPropertySetAttachedToObject Method
  176.         #endregion
  177.  
  178.         #region IsPropertySetAttachedToObject
  179.         /// <summary>
  180.         /// Finds the property set by its name on a given object.
  181.         /// </summary>
  182.         /// <param name="dbobj">The object to find the property set on.</param>
  183.         /// <param name="PropertySetName">The property name to find on the object.</param>
  184.         /// <returns> true if the property set with the given name was found, or false otherwise. </returns>
  185.         public static bool IsPropertySetAttachedToObject(AcadDb.DBObject MepObj, String PropertySetName)
  186.         {
  187.  
  188.             // Get the property set ID from the property set name
  189.             ObjectId PropertySetId = GetPropertySetDefinitionIdByName(PropertySetName);
  190.  
  191.             // Create and set a temporary ObjectId to null
  192.             ObjectId TempId = ObjectId.Null;
  193.  
  194.             // Check to see if we actually got a property set ObjectId from the name
  195.             // If we did not then we know the property set does not exist.
  196.             if (!PropertySetId.IsNull)
  197.             {
  198.                 try
  199.                 {
  200.                     // Attempt to set the temporary ObjectID equal to the objectID of the
  201.                     // of the property set we are attempting to find on the object.
  202.                     // If the property set exists on the object then the temporary ObjectId
  203.                     // will be assigned a value
  204.                     TempId = AecPropDb.PropertyDataServices.GetPropertySet(MepObj, PropertySetId);
  205.                 } // End Try Block
  206.  
  207.                 // If the property set does not exist on the object then an error will be raised
  208.                 catch (Autodesk.AutoCAD.Runtime.Exception)
  209.                 {  
  210.                     // Catching an error is not the best way to find out if a property set is
  211.                     // Attached to an object. A better way would be to query the objects extension
  212.                     // dictionary and see if the property set is in the extension dictionary
  213.  
  214.                 } // End Catch Block
  215.             } // End If Block
  216.  
  217.             // If the temp ObjectId is not null then we found the property set on the object
  218.             if (!TempId.IsNull)
  219.                 return true;
  220.            
  221.             // If we are at this point then the property set was not found on the object
  222.             // or the property set does not exists and we can return false.
  223.             return false;
  224.  
  225.         } //End IsPropertySetAttachedToObject
  226.         #endregion
  227.  
  228.         #region GetPropertySetDefinitionIdbyName
  229.         /// <summary>
  230.         /// Finds the ObjectId of the property set with the given name.
  231.         /// </summary>
  232.         /// <param name="PropertySetName">The name of the property set.</param>
  233.         /// <returns>The ObjectId of the property set if found.  If the property</returns>
  234.         /// <returns> set does not exist then returns null</returns>
  235.         public static ObjectId GetPropertySetDefinitionIdByName(string PropertySetName)
  236.         {
  237.             // Create objectId to hold the property set objectId and set the default value
  238.             // equal to null.  
  239.             ObjectId PropertySetId = ObjectId.Null;
  240.             Database db = Application.DocumentManager.MdiActiveDocument.Database;
  241.             AcadDb.TransactionManager tm = db.TransactionManager;
  242.             using (Transaction trans = tm.StartTransaction())
  243.             {
  244.                 // Create a Property Set Dictionary
  245.                 AecPropDb.DictionaryPropertySetDefinitions psdDict = new AecPropDb.DictionaryPropertySetDefinitions(db);
  246.  
  247.                 // Check to see if the dictionary contains the name of the property set we are looking for
  248.                 if (psdDict.Has(PropertySetName, trans))
  249.                 {
  250.                     // Assign the return variable the ObjectId of the property set
  251.                     PropertySetId = psdDict.GetAt(PropertySetName);
  252.                 } // End If Block
  253.  
  254.                 trans.Commit();
  255.             } // End Using Statement
  256.  
  257.             return PropertySetId;
  258.         } // End GetPropertySetDefintionIdByName Method
  259.         #endregion
  260.  
  261.     }  // End PropertySetUtilities Class
  262. }  // End MEPUtilities Namespace
  263.  
Keith Brown | AutoCAD MEP Blog | RSS Feed
AutoCAD MEP 2014 / Revit MEP 2014 / EastCoast CAD/CAM addon / Visual Studio 2013

IDabble

  • Guest
Re: .NET for AutoCAD MEP
« Reply #4 on: November 02, 2012, 07:56:00 PM »
Good eye, Mex!  Although I have versions 2009 through 2012, I was not aware of that fact.

Nice work, Keith!  I have a few PropertySet goodies (probably same as you all have), but I could learn a lot from your post and commented code.

I'm currently ironing out some bugs in a future post, but for now I have...a...BONUS
What in the h_|| could it possibly be, you ask?

... wait for it ...

Flippable...Trane Single Duct VAV Boxes...anyone?
These come in eight varieties, and are of my own making...free to download...
You'll be oblivious to the usage fees...until the credit card statement comes in!   :wink:

I've got some code and possibly some interesting documents where that came from.  Enjoy!

Keith Brown

  • Swamp Rat
  • Posts: 601
Re: .NET for AutoCAD MEP
« Reply #5 on: November 04, 2012, 10:32:57 AM »
This will check and see if a property set applies to an object.  This is needed because .net will go ahead and attach a property set to an object even if it does not belong if you tell it to.

Code - C#: [Select]
  1.         /// <summary>
  2.         /// Checks to see if a given property set applies to the given object.
  3.         /// </summary>
  4.         /// <param name="MepObjectId">The objectID of the object to to be checked.</param>
  5.         /// <param name="PropertySetId">The objectId of the property set.</param>
  6.         /// <returns>True if the given property set applies to the given object, false otherwise.</returns>
  7.         public static bool AppliesToObject(ObjectId MepObjectId, ObjectId PropertySetId)
  8.         {
  9.             Database db = HostApplicationServices.WorkingDatabase;
  10.             AcadDb.TransactionManager tm = db.TransactionManager;
  11.             using (Transaction trans = tm.StartTransaction())
  12.             {
  13.  
  14.                 AecPropDb.PropertySetDefinition PropertySet = trans.GetObject(PropertySetId, OpenMode.ForRead) as AecPropDb.PropertySetDefinition;
  15.                 System.Collections.Specialized.StringCollection AppliesTo = PropertySet.AppliesToFilter;
  16.  
  17.                 // First check to see if the property set applies to all objects.  If it does then there is
  18.                 // no reason to go any further.  If the property set does not apply to all objects then iterate
  19.                 // through the filter looking for our object.  Return true if we find it, otherwise return false.
  20.                 if (!PropertySet.AppliesToAll)
  21.                 {
  22.                     foreach (string Filter in AppliesTo)
  23.                     {
  24.                         if (Filter == MepObjectId.ObjectClass.Name)
  25.                         {
  26.                             return true;
  27.                         }
  28.                     }
  29.                     return false;
  30.                 }
  31.                 return true;
  32.             }
  33.         }  
  34.  
Keith Brown | AutoCAD MEP Blog | RSS Feed
AutoCAD MEP 2014 / Revit MEP 2014 / EastCoast CAD/CAM addon / Visual Studio 2013

IDabble

  • Guest
Re: .NET for AutoCAD MEP
« Reply #6 on: November 04, 2012, 11:42:32 AM »
Good one, Keith.  I like these methods, I have nothing like them in place.  I may have spotted an optimization in the CreatePropertySetOnObject methods, but I'd like to test if I'm correct before I go preaching about it.

Anchors is a fairly popular and damned vexxing subject.
It took me nearly a lifetime of research to know squadouche about 'em.
Here's one I took and modified a bit from its source.  It seems to work well from either 2D or 3D views.
I can see where this could be applied to Duct likewise.  I might try that one soon.
I noticed the anchored fitting doesn't want to flip or rotate.
So, either you release the anchor to adjust orientation or modify orientation through the anchor itself.
I actually had fun playing around with this one.

Code: [Select]
/// <source> Autodesk AEC DevCamp 2010 download - 4.4 AutoCAD MEP .NET API Programming - Commands.cs </source>
/// <summary> takes a duct fitting or pipe fitting, which can be floating out in free space, and "pops, not snaps" it onto the nearest end of a host duct or pipe,
/// effectively anchoring the fitting to the host segment </summary>
/// <param name="oidPipeFitting"> ObjectId of PipeFitting.  Can pass ObjectId.Null for testing or user interaction, or a valid ObjectId for full on automation </param>
/// <param name="oidPipe"> ObjectId of host Pipe.  Can pass ObjectId.Null for testing or user interaction, or a valid ObjectId for full on automation </param>
/// <returns> anchor ObjectId, which could be a Null ObjectId </returns>
public static ObjectId AddFittingAnchor(ObjectId oidFitting, ObjectId oidSegment)
{
    ObjectId oidAnchor = ObjectId.Null;
    Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
    if (oidFitting.IsNull || (oidFitting.ObjectClass.DxfName != "AECB_DUCTFITTING" && oidFitting.ObjectClass.DxfName != "AECB_PIPEFITTING"))
    {
        // for simplicity, let's pick a pipe fitting which does not have an anchor attached.  Bah, pick any!
        PromptEntityOptions optFit = new PromptEntityOptions("Select a duct fitting or pipe fitting: ");
        optFit.SetRejectMessage("\nSelected entity is NOT a duct fitting or pipe fitting.\n");
        optFit.AddAllowedClass(typeof(AecBldgHvacDbSvcs.DuctFitting), true);
        optFit.AddAllowedClass(typeof(AecBldgPipeDbSvcs.PipeFitting), true);
        PromptEntityResult resFit = ed.GetEntity(optFit);
        if (resFit.Status != PromptStatus.OK)
            return oidAnchor;
        oidFitting = resFit.ObjectId;
    }
    if (oidSegment.IsNull || (oidSegment.ObjectClass.DxfName != "AECB_DUCT" && oidSegment.ObjectClass.DxfName != "AECB_PIPE"))
    {
        // pick a pipe
        PromptEntityOptions optHost = new PromptEntityOptions("\nSelect a duct or pipe to anchor fitting: ");
        optHost.SetRejectMessage("\nSelected entity is NOT a duct or pipe.");
        optHost.AddAllowedClass(typeof(AecBldgHvacDbSvcs.Duct), true);
        optHost.AddAllowedClass(typeof(AecBldgPipeDbSvcs.Pipe), true);
        PromptEntityResult resHost = ed.GetEntity(optHost);
        if (resHost.Status != PromptStatus.OK)
            return oidAnchor;
        oidSegment = resHost.ObjectId;
    }
    // bail out if duct or pipe is paired with the wrong type fitting
    if ((oidFitting.ObjectClass.DxfName == "AECB_DUCTFITTING" && oidSegment.ObjectClass.DxfName == "AECB_PIPE") ||
        (oidFitting.ObjectClass.DxfName == "AECB_PIPEFITTING" && oidSegment.ObjectClass.DxfName == "AECB_DUCT"))
        return oidAnchor;
    Database db = HostApplicationServices.WorkingDatabase;
    using (Transaction trans = db.TransactionManager.StartTransaction())
    {
        AecBldgDbSvcs.Fitting fitting = trans.GetObject(oidFitting, OpenMode.ForRead) as AecBldgDbSvcs.Fitting;
        try
        {
           
            // check if there is an anchor, if not, we want to add it
            if (fitting.AnchorId.IsNull)
            {
                ed.WriteMessage("\nAnchoring fitting to host duct or pipe.\n");
                // no anchor, so create one
                AecBldgDbSvcs.AnchorEntityToCurve anchor = new AecBldgDbSvcs.AnchorEntityToCurve();
                anchor.SubSetDatabaseDefaults(db);
                anchor.SetToStandard(db);
                // open hosting duct or pipe segment for write
                AecBldgDbSvcs.Segment segment = trans.GetObject(oidSegment, OpenMode.ForWrite) as AecBldgDbSvcs.Segment;
                // set the anchor position distance and the closest end of the host duct or pipe
                anchor.AnchorX.OffsetDistance = 0;
                if (fitting.Location.GetVectorTo(segment.StartPoint).Length < fitting.Location.GetVectorTo(segment.EndPoint).Length)
                {
                    anchor.AnchorX.OffsetType = AecDbSvcs.CurveXOffsetType.FromStartOfCurve;
                    // the fitting wanted to "mount this end of the segment backwards" until I added the following line
                    anchor.Rotation = Math.PI;
                }
                else
                    anchor.AnchorX.OffsetType = AecDbSvcs.CurveXOffsetType.FromEndOfCurve;
                anchor.AnchorX.MeasureToType = AecDbSvcs.CurveXMeasureToType.ToCenter;
                // finally set the owner reference
                anchor.CurveId = segment.ObjectId;
                fitting.UpgradeOpen();
                fitting.SetAnchor(anchor);
                // if above is not enought for the anchor to appear, add these two lines
                //anchor.Rotation = Autodesk.Aec.Utility.AngleConstants.Rad90; // this is in the AecBaseUtilsMgd.dll
                anchor.ForceUpdateToAnchorEntityToCurve = true;
            }
            else
                ed.WriteMessage("\nAnchor is already there.\n");
        }
        catch (System.Exception se)
        {
            Application.ShowAlertDialog(se.GetBaseException().ToString());
        }
        trans.Commit();
        oidAnchor = fitting.AnchorId;
    }
    return oidAnchor;
}

Oops, spotted where Automation was going to fail.  Corrected now.
Thinking more about this, if I edit it again, I might could make it work for Pipe AND Duct.

Duct AND Pipe delivered...^ up there ^.
« Last Edit: November 09, 2012, 05:35:23 PM by IDabble »

IDabble

  • Guest
Re: .NET for AutoCAD MEP
« Reply #7 on: November 04, 2012, 02:05:17 PM »
Keith, I had mentioned an optimization in the CreatePropertySetOnObject methods.
So, I believe I owe you an explanation, with all due respect, Sir!

In the first method, what I noticed was MepObjectId was opened up twice in the Try block, once ForRead once ForWrite.
And, a slight technicality, you were somewhat forced to open the ForRead as an Entity and mismatched Type call to IsPropertySetAttachedToObject.
You could try this:

Code: [Select]
try
{
    // First check to see if the property set already exists on the object
    // create a database object based on the ObjectId of the object
    AcadDb.DBObject dbObject = tm.GetObject(MepObjectId, OpenMode.ForRead, true);
    // Check to see if the property set is already attached to the object
    if ( ! IsPropertySetAttachedToObject(dbObject, PropertySetId) )
   {
        // Attach the property set to the object
        dbObject.UpgradeOpen();
        AecPropDb.PropertyDataServices.AddPropertySet(dbObject, PropertySetId);
    } // End If Block
}  // End Try Block

I did a quick test, and I think this will work as you intended but mo' better.

In the second method, it's compounded a bit more because it's inside a foreach.
Opening twice per object in the collection...I dunno what that does!
(edit:  to the system, i mean.  Not stepping on Keith here.)
It could be done about the same as above.


You guys may already know this, but I discovered recently that PropertySets can be applied to Objects and Styles.
I think this means, for example, you could put them in the correct style in a template drawing.
And thereafter, they'll be associated with all your objects using that style if you choose to do it that way.
Then again, I could be off my rocker and way too far out there to be reached.
« Last Edit: November 04, 2012, 03:09:56 PM by IDabble »

Keith Brown

  • Swamp Rat
  • Posts: 601
Adding a classification to an entity
« Reply #8 on: December 10, 2013, 03:52:48 PM »
This is the routine that I use to add the "MvPart Type" classification to a multi-view part when I am adding it to the database with .net.  It can be modified/overloaded to add other classifications to other entity types.
 
Code - C#: [Select]
  1. private static void SetMultiViewPartStyleClassification(MultiViewPartStyle multiViewPartStyle,
  2.                                                                 string multiViewPartClassification)
  3.         {
  4.             var dictionaryClassificationDefinition = new DictionaryClassificationDefinition(_database);
  5.             ObjectId owningSystemId = dictionaryClassificationDefinition.GetAt("MvPart Type");
  6.             var classificationDefinition =
  7.                 _transaction.GetObject(owningSystemId, OpenMode.ForRead) as ClassificationDefinition;
  8.             if (classificationDefinition != null)
  9.             {
  10.                 ClassificationTree classificationTree = classificationDefinition.ClassificationTree;
  11.                 if (classificationTree != null)
  12.                 {
  13.                     ImpTreeCollection treeCollection = classificationTree.Children;
  14.                     if (treeCollection != null)
  15.                     {
  16.                         int maximum = treeCollection.Count;
  17.                         for (int i = 0; i < maximum; i++)
  18.                         {
  19.                             ObjectId classificationId = classificationTree.GetIdAt(i);
  20.                             var classification =
  21.                                 _transaction.GetObject(classificationId, OpenMode.ForRead) as Classification;
  22.                             if (classification != null)
  23.                             {
  24.                                 if (classification.Name == multiViewPartClassification)
  25.                                 {
  26.                                     ClassificationCollection classificationIds = multiViewPartStyle.Classifications;
  27.                                     classificationIds.Add(classificationId);
  28.                                     break;
  29.                                 }
  30.                             }
  31.                         }
  32.                     }
  33.                 }
  34.             }

There is ample opportunity for improvement and error checking on the method.  For instance currently the method will not report success or failure.  In other words it assumes that the multiViewPartClassification variable contains a valid string name and is neither null or empty.
 
I am not sure where I got the original method but I think credit probably goes to Jeff H if I remember correctly.
 
« Last Edit: December 10, 2013, 03:59:29 PM by Keith Brown »
Keith Brown | AutoCAD MEP Blog | RSS Feed
AutoCAD MEP 2014 / Revit MEP 2014 / EastCoast CAD/CAM addon / Visual Studio 2013

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: .NET for AutoCAD MEP
« Reply #9 on: December 13, 2013, 01:41:04 PM »
For those of you like me who find the built in propertyset methods a little to slow.  I wrote this a while ago and finally got around to formatting it for posting.  Its meant to use on known propertysets.  If nothing else you can see how AutocadACA/MEP maintains propertysets on each object.

Use:
Code - C#: [Select]
  1. var propertySetHelper = new PropertySetHelper(database, "PipeObject")
  2. var abbreviation = propertySetHelper.GetProperty<string>(member, "Abbreviation");
  3. propertySetHelper.SetProperty(member, "Abbreviation", abbreviation)

I mostly use it in LINQ method chains like so.
Code - C#: [Select]
  1. var fittings = db.ModelSpace().GetObjects<Member>()
  2.     .Where(m=>propertSetHelper.GetProperty<string>(m, "MyProperty") == myString);
  3.  

Code - C#: [Select]
  1. public class PropertySetHelper
  2.     {
  3.         private const string AEC_PROPERTY_SETS = "AEC_PROPERTY_SETS";
  4.  
  5.         private readonly Database _db;
  6.         private ObjectId _dictPropertySet;
  7.         private Dictionary<string, int> _dictionary;
  8.  
  9.         public ObjectId PropertySetDictionary
  10.         {
  11.             get { return _dictPropertySet; }
  12.         }
  13.  
  14.         /// <summary>
  15.         /// Constructs new PropertySetHelper to use a PropertySetDefinition
  16.         /// </summary>
  17.         /// <param name="db">The AutoCAD database that contains the PropertSetDefinitionDictionary</param>
  18.         /// <param name="propertySetName">The name of the PropertySet to find in the PropertySetDefinitionDictionary</param>
  19.         public PropertySetHelper(Database db, string propertySetName)
  20.         {
  21.             _db = db;
  22.             var psDict = new DictionaryPropertySetDefinitions(db);
  23.  
  24.             using (var tr = db.TransactionManager.StartOpenCloseTransaction())
  25.             {
  26.                 if (psDict.Has(propertySetName, tr))
  27.                 {
  28.                     _dictPropertySet = psDict.GetAt(propertySetName);
  29.                     var psd = (PropertySetDefinition)tr.GetObject(_dictPropertySet, OpenMode.ForRead);
  30.                     _dictionary = psd.Definitions.Cast<PropertyDefinition>().ToDictionary(pd => pd.Name, pd => pd.Id);
  31.                 }
  32.                 tr.Commit();  //Because its faster than aborting
  33.             }
  34.         }
  35.  
  36.         /// <summary>
  37.         /// Gets a property value from the PropertySetDefinition
  38.         /// </summary>
  39.         /// <typeparam name="T">Type of property value</typeparam>
  40.         /// <param name="member">Member to return value from</param>
  41.         /// <param name="propertyName">The name of the property</param>
  42.         /// <returns>property value as Type T</returns>
  43.         public T GetProperty<T>(Member member, string propertyName)
  44.         {
  45.             if (_dictPropertySet == ObjectId.Null || member.Database != _db ||
  46.                 member.ExtensionDictionary == ObjectId.Null || string.IsNullOrEmpty(propertyName))
  47.                 return default(T);
  48.  
  49.             if (_dictionary == null || !_dictionary.ContainsKey(propertyName))
  50.                 return default(T);
  51.  
  52.             var index = _dictionary[propertyName];
  53.             var result = default(T);
  54.             using (var tr = _db.TransactionManager.StartOpenCloseTransaction())
  55.             {
  56.                 var xDict = (DBDictionary)tr.GetObject(member.ExtensionDictionary, OpenMode.ForRead);
  57.                 if (xDict.Contains(AEC_PROPERTY_SETS))
  58.                 {
  59.                     var psDict = (DBDictionary)tr.GetObject(xDict.GetAt(AEC_PROPERTY_SETS), OpenMode.ForRead);
  60.                     var propertySet = psDict.Cast<DictionaryEntry>()
  61.                         .Select(e => (PropertySet)((ObjectId)e.Value).GetObject(OpenMode.ForRead))
  62.                         .FirstOrDefault(ps => ps.PropertySetDefinition == _dictPropertySet);
  63.                     if (propertySet != null)
  64.                     {
  65.                         var propData = propertySet.PropertySetData.Cast<PropertySetData>().FirstOrDefault(d => d.Id == index);
  66.                         if (propData != null)
  67.                             result = (T)propData.GetData();
  68.                     }
  69.                 }
  70.                 tr.Commit();
  71.             }
  72.             return result;
  73.         }
  74.  
  75.         /// <summary>
  76.         /// Sets a property value from the PropertySetDefinition
  77.         /// </summary>
  78.         /// <param name="member">Member to set value to</param>
  79.         /// <param name="propertyName">The name of the property</param>
  80.         /// <param name="value">The value to set the property to</param>
  81.         public void SetProperty(Member member, string propertyName, object value)
  82.         {
  83.             if (_dictPropertySet == ObjectId.Null || member.Database != _db || string.IsNullOrEmpty(propertyName))
  84.                 return;
  85.  
  86.             if (_dictionary == null || !_dictionary.ContainsKey(propertyName))
  87.                 return;
  88.  
  89.             var index = _dictionary[propertyName];
  90.             using (var tr = _db.TransactionManager.StartOpenCloseTransaction())
  91.             {
  92.                 if (member.ExtensionDictionary == ObjectId.Null)
  93.                 {
  94.                     member.UpgradeOpen();
  95.                     PropertyDataServices.AddPropertySet(member, _dictPropertySet);
  96.                 }
  97.  
  98.                 var xDict = (DBDictionary)tr.GetObject(member.ExtensionDictionary, OpenMode.ForRead);
  99.                 if (!xDict.Contains(AEC_PROPERTY_SETS))
  100.                 {
  101.                     member.UpgradeOpen();
  102.                     PropertyDataServices.AddPropertySet(member, _dictPropertySet);
  103.                 }
  104.                 var psDict = (DBDictionary)tr.GetObject(xDict.GetAt(AEC_PROPERTY_SETS), OpenMode.ForRead);
  105.                 var propertySet = psDict.Cast<DictionaryEntry>()
  106.                     .Select(e => (PropertySet)((ObjectId)e.Value).GetObject(OpenMode.ForRead))
  107.                     .FirstOrDefault(ps => ps.PropertySetDefinition == _dictPropertySet);
  108.                 if (propertySet == null)
  109.                 {
  110.                     member.UpgradeOpen();
  111.                     PropertyDataServices.AddPropertySet(member, _dictPropertySet);
  112.                 }
  113.  
  114.                 propertySet = psDict.Cast<DictionaryEntry>()
  115.                     .Select(e => (PropertySet)((ObjectId)e.Value).GetObject(OpenMode.ForRead))
  116.                     .FirstOrDefault(ps => ps.PropertySetDefinition == _dictPropertySet);
  117.                 if (propertySet != null)
  118.                 {
  119.                     propertySet.UpgradeOpen();
  120.                     var propData = propertySet.PropertySetData.Cast<PropertySetData>().FirstOrDefault(d => d.Id == index);
  121.                     if (propData != null)
  122.                         propData.SetData(value);
  123.                 }
  124.  
  125.                 tr.Commit();
  126.             }
  127.         }
  128.  
  129.         /// <summary>
  130.         /// Removes the PropertySetDefinition from Member
  131.         /// </summary>
  132.         /// <param name="member">Member to remove property set from</param>
  133.         public void RemovePropertySet(Member member)
  134.         {
  135.             if (_dictPropertySet == ObjectId.Null || member.Database != _db || member.ExtensionDictionary == ObjectId.Null)
  136.                 return;
  137.             using (var tr = _db.TransactionManager.StartOpenCloseTransaction())
  138.             {
  139.                 var xDict = (DBDictionary)tr.GetObject(member.ExtensionDictionary, OpenMode.ForRead);
  140.                 if (!xDict.Contains(AEC_PROPERTY_SETS))
  141.                     return;
  142.                 xDict.UpgradeOpen();
  143.                 xDict.Remove(AEC_PROPERTY_SETS);
  144.  
  145.                 tr.Commit();
  146.             }
  147.         }
  148.  
  149.         /// <summary>
  150.         /// Copies a PropertySetDefinition to the database
  151.         /// defined in the constructor
  152.         /// </summary>
  153.         /// <param name="sourcePath">The Path of the source database</param>
  154.         /// <param name="propertySetDefName">The name of the PropertySetDefinition</param>
  155.         public void CopyPropertySetDefintion(string sourcePath, string propertySetDefName)
  156.         {
  157.             if (!File.Exists(sourcePath))
  158.                 return;
  159.  
  160.             var dbSource = new Database(false, true);
  161.             dbSource.ReadDwgFile(sourcePath, FileShare.Read, true, null);
  162.  
  163.             var sourceDefs = new DictionaryPropertySetDefinitions(dbSource);
  164.             using (var tr = dbSource.TransactionManager.StartOpenCloseTransaction())
  165.             {
  166.                 if (sourceDefs.Has(propertySetDefName, tr))
  167.                 {
  168.                     var oic = new ObjectIdCollection(new[] { sourceDefs.GetAt(propertySetDefName) });
  169.                     var helper = new CloningHelper { MergeType = DictionaryRecordMergeBehavior.Overwrite };
  170.                     helper.Clone(dbSource, _db, oic, sourceDefs.RecordType, true);
  171.                 }
  172.                 tr.Commit();
  173.             }
  174.  
  175.             var psDict = new DictionaryPropertySetDefinitions(_db);
  176.             using (var tr = _db.TransactionManager.StartOpenCloseTransaction())
  177.             {
  178.                 if (psDict.Has(propertySetDefName, tr))
  179.                 {
  180.                     _dictPropertySet = psDict.GetAt(propertySetDefName);
  181.                     var psd = (PropertySetDefinition)tr.GetObject(_dictPropertySet, OpenMode.ForRead);
  182.                     _dictionary = psd.Definitions.Cast<PropertyDefinition>().ToDictionary(pd => pd.Name, pd => pd.Id);
  183.                 }
  184.                 tr.Commit();  //Because its faster than aborting
  185.             }
  186.         }
  187.  
  188.         /// <summary>
  189.         /// Checks if the PropertySetDefinition was instantiated
  190.         /// during the constructor
  191.         /// </summary>
  192.         /// <returns>True if the PropertySetDefntion exists</returns>
  193.         public bool IsValid()
  194.         {
  195.             return _dictPropertySet.IsValid;
  196.         }
  197.     }
  198.  
Revit 2019, AMEP 2019 64bit Win 10

Keith Brown

  • Swamp Rat
  • Posts: 601
Re: .NET for AutoCAD MEP
« Reply #10 on: December 13, 2013, 04:27:36 PM »
Hi MexicanCustard,
 
Thanks for sharing.  That is some good stuff.  I do have a question though.  The only way that I see that you are attaching a property set to a member is if you are setting the value of a property definition of a property set. If that property set is not attached to the member then you attach it.   What do you use to attach to a member if there are no manual property definitions to set?  i.e. The property set is full of all automatic definitions or formulas or other auto population definitions?
 
I can follow the code of the SetProperty Method and see where you are attaching the property set if the member doesn't have an extension dictionary, doesn't have a property set container in the extension dictionary (terminology might be incorrect), or if the property set is not already attached.  Would it be correct to say that this code could be copied into an AttachPropertySet method of the PropertySetHelper class to in order to attach the property set to the member?
 
Again thanks for sharing, this code will go a long way to help me learn and understand linq and extension dictionaries.
« Last Edit: December 14, 2013, 08:43:38 AM by Keith Brown »
Keith Brown | AutoCAD MEP Blog | RSS Feed
AutoCAD MEP 2014 / Revit MEP 2014 / EastCoast CAD/CAM addon / Visual Studio 2013

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: .NET for AutoCAD MEP
« Reply #11 on: December 16, 2013, 07:53:42 AM »
Hi MexicanCustard,
 
Thanks for sharing.  That is some good stuff.  I do have a question though.  The only way that I see that you are attaching a property set to a member is if you are setting the value of a property definition of a property set. If that property set is not attached to the member then you attach it.   What do you use to attach to a member if there are no manual property definitions to set?  i.e. The property set is full of all automatic definitions or formulas or other auto population definitions?

The line in SetProperty, PropertyDataServices.AddPropertySet(member, _dictPropertySet);, is the built in call to create and fill the propertyset.  It will complete all non manual properties.

Quote

I can follow the code of the SetProperty Method and see where you are attaching the property set if the member doesn't have an extension dictionary, doesn't have a property set container in the extension dictionary (terminology might be incorrect), or if the property set is not already attached.  Would it be correct to say that this code could be copied into an AttachPropertySet method of the PropertySetHelper class to in order to attach the property set to the member?
 
Again thanks for sharing, this code will go a long way to help me learn and understand linq and extension dictionaries.

It could but you would have to iterate all the property sets and fill in all the non manual properties.  By making a call to AddProperySet, AutoCAD does all that for you without a noticeable time increase.
Revit 2019, AMEP 2019 64bit Win 10

Keith Brown

  • Swamp Rat
  • Posts: 601
Re: .NET for AutoCAD MEP
« Reply #12 on: September 16, 2014, 02:18:43 PM »
Here is a quick command that i put together that will list out the piping system definition style names in the current drawing to the command line.  This method could be easily modified to return a collection of the system definition names.  You could even change the dictionary name and use it to print out other items that are in AutoCAD MEP such as:

Plumbing System Definitions - AECB_PLUMBINGSYSTEMDEFS
Duct System Definitions - AECB_DUCTSYSTEMDEFS
Electrical System Definitions - AECB_ELECSYSTEMDEFS
Pipe Routing Preferences - AECB_PIPE_PARTROUTINGPREFERENCES_STYLES
Label Curve Styles - AECB_LABELCURVE_STYLES
and so on..... 


Use a tool like MgdDbg to see all of the dictionaries in the drawing.

Code - C#: [Select]
  1. /// <summary>
  2. /// Prints a list of the Piping System Definitions in the current drawing to the command line.
  3. /// </summary>
  4. [CommandMethod("ListPipingSystemDefinitions")]
  5. public static void ListPipingSystemDefinitions()
  6. {
  7.    const string dictionaryName = "AECB_PIPESYSTEMDEFS";
  8.    var document = Application.DocumentManager.MdiActiveDocument;
  9.    var database = document.Database;
  10.    var editor = document.Editor;
  11.  
  12.    using (Transaction transaction = database.TransactionManager.StartOpenCloseTransaction())
  13.    {
  14.       var nod = transaction.GetObject(database.NamedObjectsDictionaryId, OpenMode.ForRead) as DBDictionary;
  15.  
  16.       if (nod == null || !nod.Contains(dictionaryName))
  17.       {
  18.          editor.WriteMessage("** Error ** Unable to obtain the Pipe System Definitions from the Named Object Dictionary.");
  19.          return;
  20.       }
  21.  
  22.       var pipeSystemDefinitions = transaction.GetObject(nod.GetAt(dictionaryName), OpenMode.ForRead) as DBDictionary;
  23.  
  24.       if (pipeSystemDefinitions != null)
  25.       {
  26.          foreach (DBDictionaryEntry definition in pipeSystemDefinitions)
  27.          {
  28.             editor.WriteMessage(string.Format("Pipe System Definition Name: {0}\n", definition.Key));
  29.          }
  30.       }
  31.  
  32.       transaction.Commit();
  33.    }
  34. }
Keith Brown | AutoCAD MEP Blog | RSS Feed
AutoCAD MEP 2014 / Revit MEP 2014 / EastCoast CAD/CAM addon / Visual Studio 2013