Share
 
 

Walkthrough: Working with AutoCAD and AS API

The Advance Steel API works with the AutoCAD API.

Since Advance Steel runs on top of AutoCAD, the AutoCAD API can be used in conjunction with the Advance Steel API in an Advance Steel plugin. Common uses of the AutoCAD API from an Advance Steel plugin include:
  • Using AutoCAD selection with Advance Steel objects
  • Using AutoCAD command reactors to work with Advance Steel objects
  • Exploding an Advance Steel object
  • Creating an Advance Steel special part using an AutoCAD solid

This tutorial will look at several examples. For more information on the AutoCAD API, see the online help for the AutoCAD Managed .NET API.

Using transactions with AutoCAD and AS API

  • You should never use the Advance Steel or AutoCAD objects outside the transaction they were opened in. This is not supported and may cause undefined behavior.
  • When starting an AutoCAD regular transaction, an Advance Steel transaction is also started.
    • This does not happen for AutoCAD OpenClose transactions.
  • Advance Steel does not support AutoCAD OpenClose transactions, not for reading and not for modifying them, the behavior is undefined. You should avoid using these transactions with Advance Steel.
  • You can use either AutoCAD regular transaction or an Advance Steel transaction to read or write Advance Steel objects.
  • Use DocumentAccess class for AdvanceSteel transactions. This class was introduced in AdvanceSteel API version 2017 and changed in version 2019. And must be used in different ways depending on the used AdvanceSteel API version:
    • AdvanceSteel API Version prior to 2019: Use it in conjunction with a "using" statement - on construction it starts transactions and locks the document, upon destruction it commits the transaction and releases the lock on the document.

      Code region: UsingDocumentAccessToStartAndCommitTransaction

      public void Create()
      {
                     using (DocumentAccess da = new DocumentAccess(null, false))
                     {
                     }
      }
      
    • AdvanceSteel API Version 2019 or later: Use it in conjunction with a "using" statement - on construction it starts transactions and locks the document, upon destruction clears all changes and releases the lock on the document. User must commit the transaction in order to save changes.

      Code region: UsingDocumentAccessToStartAndCommitTransaction

      public void Create()
      {
             using (DocumentAccess da = new DocumentAccess(null, false))
             {
               da.Commit();
             }
      }
      

Working with AutoCAD API

Create a new Advance Steel plugin as demonstrated in earlier tutorials. In addition to the Advance Steel assemblies that need to be referenced, several AutoCAD assemblies must be referenced from the project as well:
  • AcCoreMgd
  • AcDbMgd
  • AcMgd

Create a new class called WorkingWithAutoCADAPI and add the following code:

Code Region: WorkingWithAutoCADAPI command class

using Autodesk.AdvanceSteel.CADAccess;
using Autodesk.AdvanceSteel.DocumentManagement;
using Autodesk.AdvanceSteel.Modelling;
using Autodesk.AdvanceSteel.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using System.Windows.Forms;

namespace WorkingWithAutoCADandASAPI
{
    class WorkingWithAutoCADAPI
    {
        [CommandMethodAttribute("TEST_GROUP", "WorkingWithAutoCADAPI", "WorkingWithAutoCADAPI",
                                                       CommandFlags.Modal | CommandFlags.UsePickSet | CommandFlags.Redraw)]
        public void Create()
        {
            using (DocumentAccess da = new DocumentAccess(null, false))
            {
               da.Commit();
            }
        }
    }
}

Note that this tutorial will require 4 using statements for AutoCAD API namespaces. The AutoCAD API has several class names in common with Advance Steel, such as ObjectId. This requires disambiguation in the code. Rather than including the namespace in front of the class name, we can use a 'using alias directive' at the top of the file to create a shorter name for one of the classes. For this tutorial, we will use three using alias directives as shown in the rewritten code below.

Code Region: Disambiguating class names

using Autodesk.AdvanceSteel.CADAccess;
using Autodesk.AdvanceSteel.DocumentManagement;
using Autodesk.AdvanceSteel.Modelling;
using Autodesk.AdvanceSteel.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using System.Windows.Forms;
using ACTransaction = Autodesk.AutoCAD.DatabaseServices.Transaction;
using ASObjectId = Autodesk.AdvanceSteel.CADLink.Database.ObjectId;
using ACDocument = Autodesk.AutoCAD.ApplicationServices.Document;
using Autodesk.AutoCAD.Geometry;

namespace WorkingWithAutoCADandASAPI
{
    class WorkingWithAutoCADAPI
    {
        [CommandMethodAttribute("TEST_GROUP", "WorkingWithAutoCADAPI", "WorkingWithAutoCADAPI",
                                                       CommandFlags.Modal | CommandFlags.UsePickSet | CommandFlags.Redraw)]
        public void Create()
        {
            using (DocumentAccess da = new DocumentAccess(null, false))
            {
               da.Commit();
            }
        }
    }
}

The using alias directives will be used later in the tutorial.

Using AutoCAD area selection

In the Walkthrough: Create beam and plate features tutorial, we looked a using the UserInteraction class to have the user select an object on which to act. In this tutorial, we will look at using an AutoCAD selection window to automatically select some objects based on location.

Add the method below to the WorkingWithAutoCADAPI class. This method uses a crossing window to find objects within a rectangular area. It then checks if any of them are straight beams and returns he first straight beam it finds.

Code Region: Selecting a beam

private StraightBeam GetStraightBeam()
{
    StraightBeam beam = null;
    //Try to get a beam repr using AutoCad selection window
    PromptSelectionResult acSSPrompt;
    Editor ed = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor;
    acSSPrompt = ed.SelectCrossingWindow(new Point3d(0, 0, 0), new Point3d(1000, 0, 0));

    if (acSSPrompt.Status == PromptStatus.OK)
    {
        SelectionSet acSSet = acSSPrompt.Value;
        ObjectId[] ids = acSSet.GetObjectIds();

        //Iterate through the selected objects, and check if we have a straight beam
        foreach (ObjectId id in ids)
        {
            //Get the beam filer object
            ASObjectId idDbObject =
                DatabaseManager.GetFilerObjectId(new ASObjectId(id.OldIdPtr), false);
            FilerObject obj = DatabaseManager.Open(idDbObject);

            //If we have a straight beam
            if (obj.Type() == FilerObject.eObjectType.kStraightBeam)
            {
                beam = obj as StraightBeam;
                break;
            }
        }
    }

    return beam;
}

Duality of Advance Steel entities

In the sample code above, note that after getting the Autodesk.AutoCAD.DatabaseServices.ObjectId, it is converted to a Autodesk.AdvanceSteel.CADLink.Database.ObjectId before checking to see if it is a StraightBeam. This is due to a duality of Advance Steel entities. Advance Steel objects are, in general, composed of two AutoCAD entities: the "internal" entity and the "representation" entity. Normally an Advance Steel user can only interface with the "representation" entity. That is, when a user selects a beam inside Advance Seel they select the representation of the beam.

In order to successfully work with Advance Steel and AutoCAD API it is necessary to understand which entity is required. Whenever you want AutoCAD to act on an object you need to use the "representation" id, while in the cases when you want the AdvanceSteel API to act on the same object the "internal" id must be used. Whenever object ids are obtained by calling methods, Advance Steel methods always return ids of the "internal" objects, while AutoCAD API calls always return ids of the "representation" objects. The Advance Steel API has methods to go from one type of id to the other.

Exploding an Advance Steel entity

Next, let's look at using the AutoCAD API to explode an Advance Steel entity. This works similarly to selecting an Advance Steel beam and calling the "explode" command on it. It will return the exploded lines, but does not affect the model. The resulting simpler entities can be exported or used for calculations or other uses.

Code Region: Exploding a beam

private int ExplodeBeam(StraightBeam beam)
{
    int nExplodedEnts = 0;
    // get the "representation" id - the id that AutoCAD understands as being the object drawn on the screen
    ASObjectId reprId = DatabaseManager.GetReprId(beam);

    // convert this id from "AdvanceSteel" id to "Acad" id
    ObjectId acadIdBeam = new ObjectId(reprId.AsOldId());

    // start an AutoCAD transaction
    DocumentCollection docs = Autodesk.AutoCAD.ApplicationServices.Core.Application.DocumentManager;
    ACDocument currDoc = docs.CurrentDocument;
    Autodesk.AutoCAD.DatabaseServices.Transaction trans = currDoc.TransactionManager.StartTransaction();

    // use the transaction to gain access to the beam representation as an AutoCAD entity
    DBObject objBeam = trans.GetObject(acadIdBeam, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForWrite);
    if (objBeam is Autodesk.AutoCAD.DatabaseServices.Entity)
    {
        Entity ent = (Entity)objBeam;

        // ask autoCAD to "explode" this beam - equivalent of selecting an advance steel beam from the screen and calling "explode" command
        DBObjectCollection explodedEnts = new DBObjectCollection();
        ent.Explode(explodedEnts);
        nExplodedEnts = explodedEnts.Count;

        trans.Commit();
    }

    return nExplodedEnts;
}

Note that the method above first converts the Advance Steel id to an AutoCAD id since we want the AutoCAD Explode method to act on the entity.

Now return to the original Create() method for the WorkingWithAutoCADAPI and call our two methods - one to get an Advance Steel straight beam and one to explode it.

Code Region: Putting it together

public void Create()
{
    using (DocumentAccess da = new DocumentAccess(null, true))
    {
        StraightBeam beam = GetStraightBeam();
        if (beam != null)
        {
             ExplodeBeam(beam);
        }
    }
}

Creating an Advance Steel special part from an AutoCAD solid

Advance Steel special parts are generic 3d objects that can be added to an Advance Steel model to complement standard construction elements. Special parts can be created just like block references from external dwg files containing any valid AutoCAD objects including other Advance Steel objects. A solid is created first and then assigned to the special part.

In this example, we will create the AutoCAD 3d solid with code, but it could also be created manually. Add the following method to the class file to create a wedge.

Code Region: Creating an AutoCAD 3d solid

private ObjectId CreateAutoCADWedge()
{
    ObjectId ret;
    // Get the current document and database, and start an AutoCAD transaction
    ACDocument acDoc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
    Database acCurDb = acDoc.Database;

    using (ACTransaction acTrans = acCurDb.TransactionManager.StartTransaction())
    {
        // Open the Block table record for read
        BlockTable acBlkTbl;
        acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForWrite) as BlockTable;

        // Open the Block table record Model space for write
        BlockTableRecord acBlkTblRec = new BlockTableRecord();

        acBlkTblRec.Name = "wedge";

        // Create a 3D solid wedge
        Solid3d acSol3D = new Solid3d();
        acSol3D.SetDatabaseDefaults();
        acSol3D.CreateWedge(10, 15, 20);

        // Position the center of the 3D solid at (5,5,0) 
        acSol3D.TransformBy(Matrix3d.Displacement(new Point3d(5, 5, 0) - Point3d.Origin));

        // Add the new object to the block table record and the transaction
        acBlkTblRec.AppendEntity(acSol3D);
        acBlkTbl.Add(acBlkTblRec);
        acTrans.AddNewlyCreatedDBObject(acBlkTblRec, true);

        // Save the new objects to the database
        acTrans.Commit();

        ret = acBlkTblRec.Id;
    }

    return ret;
}

Note that the method returns an ObjectId for the BlockTableRecord. We will use this for our special part, after converting it to an Advance Steel ObjectId. Add the following method to create a new special part.

Code Region: Creating a special part

private void CreateSpecialPart()
{
    SpecialPart specialPart = new SpecialPart(new Autodesk.AdvanceSteel.Geometry.Matrix3d());
    specialPart.WriteToDb();

    ObjectId newBlock = CreateAutoCADWedge();
    specialPart.SetBlock(new ASObjectId(newBlock.OldIdPtr), 1.0);
}
Now add a call to this method to the Create() method.

Code Region: Putting it together

public void Create()
{
    using (DocumentAccess da = new DocumentAccess(null, false))
    {
        StraightBeam beam = GetStraightBeam();
        if (beam != null)
        {
            ExplodeBeam(beam);
        }

        CreateSpecialPart();
        da.Commit();
    }
}

Command Reactors

Sometimes it is useful to respond to a command in AutoCAD. An Advance Steel plugin can subscribe to the CommandEnded event to be notified with a command has completed so some action can be taken. In this example, we will create a method that looks for when a new beam has been added to the Advance Steel model and checks to make sure all beams (which would include the new one) meet some minimum beam length (and issue a warning if one does not).

Code Region: CommandEnded reactor

void CurrentDocument_CommandEnded(object sender, Autodesk.AutoCAD.ApplicationServices.CommandEventArgs e)
{
    // If a beam has just been created
    if (e.GlobalCommandName == "ASTM4CRBEAMBYCLASS")
    {
        using (DocumentAccess da = new DocumentAccess(null, true))
        {
           try
           {
               ASObjectId[] ids;
               ClassTypeFilter filter = new ClassTypeFilter();
               filter.AppendAcceptedClass(FilerObject.eObjectType.kStraightBeam);

               //Get all the beams from the database
               DatabaseManager.GetModelObjectIds(out ids, filter);

               foreach (ASObjectId id in ids)
               {
                   FilerObject obj = DatabaseManager.Open(id);

                   if (obj != null)
                   {
                       StraightBeam sb = obj as StraightBeam;
   
                       // check that beam meets minimum length for project
                       double length = sb.GetLength();
                       if (length < 305)
                       {
                           MessageBox.Show("Minimum beam length is 305 mm. Please adjust beam.");
                       }
                   }
               }
            }
            catch(System.Exception ex)
            {
               MessageBox.Show(ex.Message);
            }
        }
    }
}

Note that in this method, it checks the GlobalCommandName to see if it is ASTM4CRBEAMBYCLASS, the command indicating that a new Advance Steel beam has been created. The command name may be one created by another plugin, such as CREATEFEATURES, or any built in command from Advance Steel or AutoCAD. If you do not know the name of the command you want to create a reactor for, simply create a command reactor method similar to above and place a breakpoint in the method to check the value of e.GlobalCommandName when the command is triggered at the end of the desired command.

Since this method will be called when a command has ended and is not going to be within the scope of the transaction started in the Create() method, it is necessary for CurrentDocument_CommandEnded to have it's own transaction, as well as calls to lock and unlock the current document.

To have your command reactor called, it must be registered with the current document. Add the following code to the WorkingWithAutoCADAPI class to register the CurrentDocument_CommandEnded method.

Code Region: Registering a command reactor

private void AddCommandEndedReactor()
{
    DocumentCollection docs = Autodesk.AutoCAD.ApplicationServices.Core.Application.DocumentManager;
    ACDocument currDoc = docs.CurrentDocument;
    currDoc.CommandEnded += CurrentDocument_CommandEnded;
}
Now add a call to this method to the Create() method.

Code Region: Putting it together

public void Create()
{
    using (DocumentAccess da = new DocumentAccess(null, false))
    {

        StraightBeam beam = GetStraightBeam();
        if (beam != null)
        {
           ExplodeBeam(beam);
        }

        CreateSpecialPart();

        AddCommandEndedReactor();
    }
}

After invoking the WorkingWithAutoCADAPI method in Avance Steel, the CurrentDocument_CommandEnded method will be called whenever a command ends in AutoCAD or Advance Steel.

Was this information helpful?