.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

IsQuiescent or how to determine when AutoCAD is ready for a new command??

6 REPLIES 6
Reply
Message 1 of 7
bjhuffine
3643 Views, 6 Replies

IsQuiescent or how to determine when AutoCAD is ready for a new command??

I'm writing a .net application (netloaded dll) that will run multiple scripts on the active drawing.  I can get it to work wonderfully one at a time, but it crashes when calling the next script.  It struck me that since the call uses the command line, that when one script takes of, the other then gets triggered before AutocAD is ready to accept it and now we're stepping all over each other.  i.e. AutoCAD goes boom! 

 

So I was thinking that there's got to be a way to find out whether or not AutoCAD is ready to accept another command after a script run has been triggered.  And I figured that would be the IsQuiescent.  Unfortunately, everytime I think I've got this figured out I end up with an infinite loop and the quiescent state is never determined.  Any ideas on how to accomplish this with the AutoCAD .net API?

6 REPLIES 6
Message 2 of 7
Alfred.NESWADBA
in reply to: bjhuffine

Hi,

 

as long as you have access to the document-editor you have the chance to catch the event Autodesk.AutoCAD.Editor.EnteringQuiescentState

You can also try to catch a command-ended (if that fits to your workflow) ==> Autodesk.AutoCAD.ApplicationServices.Document.CommandEnded

Then sometimes the operatingsystem can help you as you can get process-info about the acad.exe ==> watch it's processor-usage for a few seconds.

 

That were some tips, if you give as info what you are doing that AutoCAD overhauls your app there may be some more tips (like e.g. switch off BACKGROUNDPLOT if you do any plotting in your script)

 

- alfred -

------------------------------------------------------------------------------------
Alfred NESWADBA
Ingenieur Studio HOLLAUS ... www.hollaus.at ... blog.hollaus.at ... CDay 2024
------------------------------------------------------------------------------------
(not an Autodesk consultant)
Message 3 of 7
bjhuffine
in reply to: Alfred.NESWADBA

Hi, thanks for your reply.

 

Essentially, I have a manager object that uses some handler objects to either call running of a single script or loops through and runs multiple scripts.  Whether looping or not, the gist is essentially:

 

CADCommand.Initialize();
CADCommand.Run(commandFile);
CADCommand.Reset();

 Then the handler object is using aecmd() (per comments by Tony Tanzillo on this method for a synchronous process:

 

        //DllImport Declaration for P/Invoke of acedCmd method
        [DllImport("acad.exe",
                  CallingConvention = CallingConvention.Cdecl,
                  CharSet = CharSet.Unicode,
                  EntryPoint = "acedCmd")]

        //Delegate for operation of the P/Ivoked acedCmd method
        public static extern int acedCmd(System.IntPtr resBuf);



private Dictionary<string, object> EnvironmentSettings;

        public override void Initialize()
        {
            //get system variable values
            short FileDia = (short)adskAppSrv.Application.GetSystemVariable("FileDia");
            short CmdEcho = (short)adskAppSrv.Application.GetSystemVariable("CmdEcho");
            short RecoveryMode = (short)adskAppSrv.Application.GetSystemVariable("RecoveryMode");

            //Save system variable values in Dictionary
            if (this.EnvironmentSettings != null)
                this.EnvironmentSettings.Clear();
            this.EnvironmentSettings.Add("FileDia", FileDia);
            this.EnvironmentSettings.Add("CmdEcho", CmdEcho);
            this.EnvironmentSettings.Add("RecoveryMode", RecoveryMode);

            //Change system variable values that could affect command operation
            adskAppSrv.Application.SetSystemVariable("FileDia", 0);
            adskAppSrv.Application.SetSystemVariable("CmdEcho", 0);
            adskAppSrv.Application.SetSystemVariable("RecoveryMode", 0);

            //Set initialized property
            this.IsInitialized = true;
        }

        public override void Run(CommandFile commandFile)
        {
            this.CommandFile = commandFile;

                //ensure this has been initialized
                if (this.IsInitialized == false)
                    return;
                

                //convert file path format to one usable with AutoCAD commands
                Uri uriPath = new Uri(this.CommandFile.FileFullPath.FullName);
                string filePath = uriPath.AbsolutePath.ToString();

                //Define command arguments
                using (ResultBuffer rb = new ResultBuffer())
                {
                    rb.Add(new TypedValue((int)LispDataType.Text, "_.script"));
                    rb.Add(new TypedValue((int)LispDataType.Text, "\"" + filePath + "\""));

                    //Run the command
                    acedCmd(rb.UnmanagedObject);
                }

            }

        public override void Reset()
        {
            foreach (KeyValuePair<string, object> Setting in this.EnvironmentSettings)
            {
                adskAppSrv.Application.SetSystemVariable(Setting.Key, Setting.Value);
            }

            //Indicate that this has been reset
            this.IsReset = true;
        }

 Like I said, this works great when just sending commands to run one script... but when using this to send for more, they all seem to step on each other.  Do you think it would be best to try to catch one of the events you mentioned? 

Message 4 of 7
Alfred.NESWADBA
in reply to: bjhuffine

Hi,

 

>> Do you think it would be best to try to catch one of the events you mentioned?

Yes, I would create an EventHandler for command-ended and hold it active unless the command "SCRIPT" gets ended, As I have not looked into the SCRIPT command yet I hope that you get "command ended: SCRIPT" back on the end of the whole script as I imagine that SCRIPT may be handled differently to other commands in AutoCAD, so try it first.

 

Good luck, - alfred -

------------------------------------------------------------------------------------
Alfred NESWADBA
Ingenieur Studio HOLLAUS ... www.hollaus.at ... blog.hollaus.at ... CDay 2024
------------------------------------------------------------------------------------
(not an Autodesk consultant)
Message 5 of 7
bjhuffine
in reply to: Alfred.NESWADBA

Okay, I'm already past my normal working hours, so I'll give that a try sometime tomorrow after a little morning meeting.  If I run into issues I may post back sometime tomorrow afternoon.  Thanks.

Message 6 of 7
Hallex
in reply to: bjhuffine

Might be a point of interest,

https://sites.google.com/site/bushmansnetlaboratory/sendbox/lab/pof

link to source code and to working solution at the very bottom of the page (2009)

_____________________________________
C6309D9E0751D165D0934D0621DFF27919
Message 7 of 7
bjhuffine
in reply to: Hallex

Okay, I finally got it to work Robot Happy .  I created a class called CADStatus (this may change) to implement the events formentioned.  BTW, I had posted something with ADN just in case and they sent the sample code for working with the EnteringQuiescentState event. What I found out (when testing your theory last night (just using message boxes in the event handlers) is that even though my sending commands is synchronous, the commands don't get sent until after the command is finished. This is why no matter what I did, I was still seeing an infinite loop. It's because the commands couldn't be sent until the current command was over and the current command was keeping AutoCAD from becoming quiescent.

 

Anyways, this time the main command is simply populating a generic queue collection property with the reference objects used to manage the script properties, then when it's finished, the quiescent event triggers and in the handler I check to see if there's any scripts to run in the queue. If so, pop one off and run it by calling a second command. This keeps it separated nicely. Here's a copy of what I've used to accomplish this if anyone is interested.  And thanks for everyone's responses!

 

Oh yeah, if anyone sees where I should handle this more efficiently, please don't hesitate to speak up... I feel like I stay in novice-land.

In the command class:

        private static Queue<CommandFile> _CommandsQueue = new Queue<CommandFile>();
        public static Queue<CommandFile> CommandsQueue
        {
            get { return _CommandsQueue; }
            set { _CommandsQueue = value; }
        }

        [CommandMethod("BatchScriptRun")]
        public void TVAPrint()
        {

            IAppConfig appConfig = ConfigInfo.ConfigInstance;

            CommandManager commandManager = CommandManager.CmdControllerInstance;

            foreach (ScriptItem ScriptItem in appConfig.Scripts)
            {
                
                CommandsQueue.Enqueue(new CommandFile(ScriptItem.Department, ScriptItem.DisplayName, ScriptItem.FullFilePath, ScriptItem.Type));

            }

        }

        [CommandMethod("RunQueuedScript")]
        public void RunQueuedScript()
        {
            CommandManager commandManager = CommandManager.CmdControllerInstance;
            CommandFile commandFile=CommandsQueue.Dequeue();
            commandManager.Run(commandFile);
        }

 

And now the CADStatus class... 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;

[assembly: ExtensionApplication(typeof(TVA.Utility.BatchMan.CADStatus))]

namespace TVA.Utility.BatchMan
{
    class CADStatus : IExtensionApplication
    {

        private bool _IsQuiescent = false;
        public bool IsQuiescent
        {
            get { return _IsQuiescent; }
        }

        void IExtensionApplication.Initialize()
        {
            Application.DocumentManager.DocumentBecameCurrent += new DocumentCollectionEventHandler(DocumentManager_DocumentBecameCurrent);
            Application.DocumentManager.DocumentToBeDeactivated += new DocumentCollectionEventHandler(DocumentManager_DocumentToBeDeactivated);
            Application.DocumentManager.DocumentToBeDestroyed += new DocumentCollectionEventHandler(DocumentManager_DocumentToBeDestroyed);

            Document activeDoc = Application.DocumentManager.MdiActiveDocument;
            activeDoc.Editor.EnteringQuiescentState += new EventHandler(ed_EnteringQuiescentState);
            activeDoc.Editor.LeavingQuiescentState += new EventHandler(ed_LeavingQuiescentState);
        }

        void DocumentManager_DocumentBecameCurrent(object sender, DocumentCollectionEventArgs e)
        {
            Editor ed = e.Document.Editor;
            ed.EnteringQuiescentState += new EventHandler(ed_EnteringQuiescentState);
            ed.LeavingQuiescentState += new EventHandler(ed_LeavingQuiescentState);
        }

        void DocumentManager_DocumentToBeDeactivated(object sender, DocumentCollectionEventArgs e)
        {
            Editor ed = e.Document.Editor;
            ed.EnteringQuiescentState -= new EventHandler(ed_EnteringQuiescentState);
            ed.LeavingQuiescentState -= new EventHandler(ed_LeavingQuiescentState);
        }

        void DocumentManager_DocumentToBeDestroyed(object sender, DocumentCollectionEventArgs e)
        {
            Editor ed = e.Document.Editor;
            ed.EnteringQuiescentState -= new EventHandler(ed_EnteringQuiescentState);
            ed.LeavingQuiescentState -= new EventHandler(ed_LeavingQuiescentState);
        }

        void ed_EnteringQuiescentState(object sender, EventArgs e)
        {
            Editor ed = sender as Editor;
            this._IsQuiescent = true;
            if (CADCommands.CommandsQueue.Count > 0)
            {
                Application.DocumentManager.MdiActiveDocument.SendStringToExecute("RunQueuedScript\n", true, false, true);
            }

        }

        void ed_LeavingQuiescentState(object sender, EventArgs e)
        {
            Editor ed = sender as Editor;
            this._IsQuiescent = false;
        }

        void IExtensionApplication.Terminate()
        {
            Application.DocumentManager.DocumentBecameCurrent -= new DocumentCollectionEventHandler(DocumentManager_DocumentBecameCurrent);
            Application.DocumentManager.DocumentToBeDeactivated -= new DocumentCollectionEventHandler(DocumentManager_DocumentToBeDeactivated);
            Application.DocumentManager.DocumentToBeDestroyed -= new DocumentCollectionEventHandler(DocumentManager_DocumentToBeDestroyed);
        }
    }
}

 

 

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report

”Boost