Kean Walmsley


  • About the Author
    Kean on Google+

July 2014

Sun Mon Tue Wed Thu Fri Sat
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    








« Iterating AutoCAD system variables using .NET – Part 1 | Main | 3 things I learned at last week’s Technical Summit »

June 12, 2014

Iterating AutoCAD system variables using .NET – Part 2

In the last post we saw some code to exercise to two available system variable enumeration mechanisms in AutoCAD, SystemObjects.Variables and the new SystemVariableEnumerator class.

Today we’re going to take a closer look at these two mechanisms, briefly comparing their performance and results. I took some code from this previous post to measure elapsed time, deleting the “runs” database to simplify the code once I’d realised the performance was basically comparable.

Here’s the C# code, with the updated ESV and ESV2 commands which now create correspondingly named .txt files in c:\temp, rather than printing the results to the command-line (and with Primary/SecondaryType information ignored, to make the output consistent – and therefore diffable – between the two commands):

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.ApplicationServices.Core;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.Runtime;

using System;

using System.Diagnostics;

using System.IO;

using System.Text;

 

namespace SystemVariableEnumeration

{

  public class Commands

  {

    public void MeasureTime(

      Document doc, Func<int> func, string name

    )

    {

      // Get the name of the running command(s)

      // (might also have queried the CommandMethod attribute

      // via reflection, but that would be a lot more work)

 

      var cmd = (string)Application.GetSystemVariable("CMDNAMES");

 

      // Start a Stopwatch to time the execution

 

      var sw = new Stopwatch();

      sw.Start();

 

      // Run the function, getting back the count of the results

 

      var cnt = func();

 

      // Stop the Stopwatch and print the results to the command-line

 

      sw.Stop();

      doc.Editor.WriteMessage(

        "\n{0} found {1} {2} in {3}.", cmd, cnt, name, sw.Elapsed

      );

    }

 

    [CommandMethod("ESV")]

    public void EnumerateSysVars()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      if (doc == null)

        return;

 

      MeasureTime(

        doc,

        () =>

        {

          int numVars = 0;

 

          using (var sw = new StreamWriter("c:\\temp\\esv.txt"))

          {

            // Use the existing SystemObjects iteration mechanism

 

            foreach (var v in SystemObjects.Variables)

            {

              sw.WriteLine(GetVariableInfo(v));

              numVars++;

            }

          }

          return numVars;

        },

        "variables"

      );

    }

 

    [CommandMethod("ESV2")]

    public void EnumerateSysVars2()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      if (doc == null)

        return;

 

      MeasureTime(

        doc,

        () =>

        {

          int numVars = 0;

 

          using (var sw = new StreamWriter("c:\\temp\\esv2.txt"))

          {

            // Use the new system variable enumerator

 

            var sve = new SystemVariableEnumerator();

 

            while (sve.MoveNext())

            {

              var v = sve.Current;

              if (v != null)

              {

                sw.WriteLine(GetVariableInfo(v));

                numVars++;

              }

            }

          }

 

          return numVars;

        },

        "variables"

      );

    }

 

    // Helper function to get the information for a particular

    // variable

 

    private static string GetVariableInfo(Variable v)

    {

      var t = GetType(v.PrimaryType);

 

      var sb = new StringBuilder();

      sb.AppendFormat(

        "{0} ({1}): {2}", // Skip the additional type info

        v.Name,

        t == null ? "null" : t.Name,

        /*v.PrimaryType, v.SecondaryType,*/ v.TypeFlags

      );

 

      if (v.Range != null)

      {

        sb.AppendFormat(

          " [{0}...{1}]",

          v.Range.LowerBound, v.Range.UpperBound

        );

      }

      return sb.ToString();

    }

 

    // Determine the type of a system variable based on

    // the internal representation

 

    private static System.Type GetType(short v)

    {

      Type ret = null;

 

      switch (v)

      {

        case 1:

        case 5001: // RTREAL real number

          {

            ret = typeof(Double);

            break;

          }

        case 2:

        case 5002: // RTPOINT: 2D point X and Y only

          {

            ret = typeof(Point2d);

            break;

          }

        case 3:

        case 5003: // RTSHORT: short integer

          {

            ret = typeof(Int16);

            break;

          }

        case 4:

        case 5004: // RTANG: angle

          {

            ret = null; // Angle

            break;

          }

        case 5:

        case 5005: // RTSTR: string

          {

            ret = typeof(String);

            break;

          }

        case 6:

        case 5006: // RTENAME: entity name

          {

            ret = null;

            break;

          }

        case 7:

        case 5007: // RTPICKS: pick set

          {

            ret = null;

            break;

          }

        case 8:

        case 5008: // RTORIENT: orientation

          {

            ret = null; // Orientation

            break;

          }

        case 9:

        case 5009: // RT3DPOINT: 3D point - X, Y and Z

          {

            ret = typeof(Point3d);

            break;

          }

        case 10:

        case 5010: // RTLONG: long integer

          {

            ret = typeof(Int32);

            break;

          }

        case 11:

        case 5011: // 2D extents of some kind

          {

            ret = typeof(Point2d);

            break;

          }

      }

      return ret;

    }

  }

}

When we run the code we see that the ESV command – the implementation using SystemObjects.Variables – returns the results more quickly but finds only 274 (or 275 with AutoCAD 2015 SP1 installed, as we have the new CURSORBADGE sysvar), whereas the ESV2 command finds 912 (or 913 with SP1) in a timespan that’s basically consistent with the fact there are more variables found. So performance isn’t an issue.

It is interesting (and somewhat reassuring) that the new enumerator finds so many more sysvars than the former mechanism. On the one hand the old collection allows you to modify a system variable’s value via the returned object…

[CommandMethod("TOGCB")]

public void ToggleCursorBadge()

{

  var doc = Application.DocumentManager.MdiActiveDocument;

  if (doc == null)

    return;

  var ed = doc.Editor;

 

  const string curbadge = "CURSORBADGE";

 

  // Get our CURSORBADGE system variable object

 

  var cb = SystemObjects.Variables[curbadge];

 

  // Report its initial value

 

  ed.WriteMessage(

    "\nInitial value of {0} is {1}.", curbadge, cb.Value

  );

 

  // Set the new value, toggling between 1 & 2

  // (with too many casts for my liking, but hey)

 

  cb.Value = (short)((short)cb.Value == 1 ? 2 : 1);

 

  // And report the new value to make sure it worked

 

  ed.WriteMessage(

    "\nNew value of {0} is {1}.", curbadge, cb.Value

  );

}

… but on the other hand this is really only for a subset of system variables (and I haven’t yet worked out which subset… I do know the new enumerator skips anonymous sysvars, and yet it still finds more than 3x the number).

In case you want to dig into the sysvars returned by each, here are the results for the ESV and the ESV2 commands. I did my own diff – and the results are returned in a consistent order, in that the ones found by ESV are at the end of the ones found by ESV2 – but I haven’t yet spent much time working out which ones are excluded from the old mechanism and why.

If anyone does look into this, please do post a comment: I’ll also post an update if I find out something further from my side.

blog comments powered by Disqus

10 Random Posts