Visual LISP, AutoLISP and General Customization
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Reactors?

22 REPLIES 22
SOLVED
Reply
Message 1 of 23
mid-awe
2988 Views, 22 Replies

Reactors?

Hi all,

 

I have been avoiding this for a looong time. It is finally here. I need to begin working with reactors. Currently we are using none and I need to address some issues that reactors are likely the only solution.

 

Just now, I'm asking for advice on Reactors, Dos and Don'ts. 

 

The two reactors that I'll have to tackle first:

  • I need a reactor to force save on drawing close. (also nice to have an alert before the save takes place)
  • Also, I need a reactor to monitor changes to global / system variable. (for now I'd like to be alerted to changes)

Most of all I have fingers crossed that someone can shed some light in this darkness.

 

Thank you for any information, but usefull examples are greatly appreciated. Smiley Happy

22 REPLIES 22
Message 2 of 23
dgorsman
in reply to: mid-awe

Reactors only react, they don't actually postpone or cancel the event that call them (like, say, a drawing close).  If the save operation takes longer than it takes to close the drawing, then AutoCAD will throw an error as its trying to close a drawing while the save (or supporting) operations are still in progress.

----------------------------------
If you are going to fly by the seat of your pants, expect friction burns.
"I don't know" is the beginning of knowledge, not the end.


Message 3 of 23
mid-awe
in reply to: dgorsman

Do you have any suggestions? Can I just undefine CLOSE? Maybe if I cannot undefine, I can set all drawings as read only. Then once my command is triggered I can save a copy as temporary, delete the previous and save the copy to the original folder. There's a vlisp reference to temp, I believe.

Message 4 of 23
alexKoshman
in reply to: mid-awe

Hi, mid-awe!

 

I think we have to use Reactors if there are ABSOLUTELY NO WAY to get what we want without them ; ))

 

How about this simply code for CLOSE with SAVE:

(defun c:close ()
  (if (= 0 (getvar "dbmod"))
    (command "_.close")
    (progn
      (command "_.qsave" (strcat (getvar "dwgprefix") (getvar "dwgname")) "_y")
      (princ "\nDWG save forced.")
      (command "_.close")
    ) ; - 'progn'
  ) ; - 'if'

 (princ)
)

 

Message 5 of 23
mid-awe
in reply to: alexKoshman

Thanks Alex. I'll give that a try. 🙂
Message 6 of 23
Lee_Mac
in reply to: mid-awe


@mid-awe wrote:

 

Just now, I'm asking for advice on Reactors, dos and don'ts.

 


 

The main priority when working with Reactors (and to a degree, when writing any other program) is to account for every possible eventuality that the reactor may encounter - this is especially apparent when working with Object Reactors, since a modification event can be caused by numerous operations.

 

Furthermore, because Reactors operate autonomously in the background, monitoring for specific events and automatically evaluating the corresponding callback functions, code that is evaluated within a Reactor callback function must be absolutely watertight.

 

Callback functions which are not completely bulletproof risk crashing or locking up the AutoCAD application, since the callback function is in effect shoe-horning an operation into the standard flow of the application.

 

As far as dos & don'ts: it is quite well known that you cannot issue command calls from within a Reactor callback function, but you should also avoid any user input from the callback function since, as noted above, this is interupting the standard flow of the application.

 


@mid-awe wrote:

 

I need a reactor to force save on drawing close. (also nice to have an alert before the save takes place)

 


 

I would agree with the sentiments shared by those above in that if there is an alternative method to accomplishing a certain task that does not involve the use of a Reactor, in 9 out of 10 cases I would likely opt for the alternative (within reason) since Reactors can open a can of worms (or should I say, bugs) and can be a headache to manipulate.

 

With that said, to provide a simple example demonstrating how to accomplish the task in question, consider the following code:

 

(defun c:autosave-on ( )
    (if (= 'vlr-command-reactor (type autosave:reactor))
        (if (vlr-added-p autosave:reactor)
            (princ "\nAutosave reactor already running.")
            (progn
                (vlr-add autosave:reactor)
                (princ "\nAutosave reactor enabled.")
            )
        )
        (progn
            (setq autosave:reactor
                (vlr-command-reactor "autosave-reactor"
                   '(
                        (:vlr-commandwillstart . autosave:callback)
                    )
                )
            )
            (princ "\nAutosave reactor enabled.")
        )
    )
    (princ)
)

(defun c:autosave-off ( )
    (if (= 'vlr-command-reactor (type autosave:reactor))
        (progn
            (vlr-remove autosave:reactor)
            (setq autosave:reactor nil)
            (princ "\nAutosave reactor disabled.")
        )
        (princ "\nAutosave reactor not running.")
    )
    (princ)
)

(defun autosave:callback ( obj arg )
    (if
        (and
            (wcmatch (strcase (car arg)) "QUIT,CLOSE,CLOSEALL")
            (= 1 (getvar 'dwgtitled))
        )
        (vla-save (vla-get-activedocument (vlax-get-acad-object)))
    )
    (princ)
)

(vl-load-com) (princ)

 

To test the above application, load the code in the normal way and type autosave-on to enable the Command Reactor. The Command Reactor can be subsequently disabled at any time by typing autosave-off at the command-line.

 

The Command Reactor will evaluate the callback function for every command issued, before the command operation is initiated. The callback function will then test the name of the issued command against the wildcard pattern specified in the code, and, if a match is found, the callback function will automatically save the drawing.

 

When monitoring for a drawing close event, you could use either a Drawing Reactor (vlr-dwg-reactor) or Editor Reactor (vlr-editor-reactor), however, the events pertaining to drawing close for these reactors will fire after AutoCAD has issued the prompt for whether or not to save changes to the drawing, hence, a Command Reactor (vlr-command-reactor) must be used as the :vlr-commandwillstart event will be triggered before the 'Save Changes' dialog is issued.

 


@mid-awe wrote:

 

Also, I need a reactor to monitor changes to global / system variable. (for now I'd like to be alerted to changes)

 


 

For this task you could utilise a vlr-sysvar-reactor, however, note that not all system variables are monitored by this reactor. I don't have a complete reference for those that are, but it would be a simple matter of testing which system variable changes trigger evaluation of the Reactor callback function.

 


@mid-awe wrote:

 

Thank you for any information, but useful examples are greatly appreciated. Smiley Happy

 


 

Since you are looking for some examples, here are a selection of my programs which are driven by Visual LISP Reactors:

 

Associative Textbox - (also available at the Exchange App Store)

Associative Centerlines

Autosave Every 20 Commands

Layer Director

LISP Command Logger

Object Lock

UCS Reactor

Draw Order Reactor

Auto Label Attributes

AutoCAD Clock

 

I hope this provides some food for thought.

 

Lee

 

Message 7 of 23
dbroad
in reply to: mid-awe

Others who have replied to this thread have offered sound advice.  In general, I would avoid the autosave temptation.  There are times that you want to open a drawing, make changes and NOT save.  Even though that sounds crazy, it isn't.  Building in autosave takes the judgement away from the drafter.

 

The autosaveon and autosaveoff proposals that Lee Mac offers are a good compromise but require that someone deliberately turn the autosave off.  Accidentally saving is as much of a problem as accidentally not saving.

 

As far as sysvars, I have had this tracking my changes for the past several weeks.  I eventually turned it off to avoid the annoyance but you may use it to build yours.

 

;;initial settings
(acad-push-dbmod) ;don't let anything in this file affect drawing status
(vl-load-com)
(vlr-sysvar-reactor nil '((:vlr-sysvarchanged . notifysysvarchange)))
(defun notifysysvarchange (r l)
  (if (member (car l) '("CANNOSCALE" "CANNOSCALEVALUE" "DIMSCALE"))
    (alert (strcat (car l) "changed.")))
  )

;.......other stuff at startup

(acad-pop-dbmod)

 

Architect, Registered NC, VA, SC, & GA.
Message 8 of 23
alexKoshman
in reply to: dbroad

I agree with [b]dbroad[/b] and I understand him very very much. I don't think that Autosave must be with absolutely every Close. ...But our poor [b]mid-awe[/b] wanted it and asked it so much... ; ))
Message 9 of 23
mid-awe
in reply to: Lee_Mac

Lee, thank you.

I don't like not being able to trust the drafter to save their work, but apparently a drafter here has plotted projects and failed to save. Now we have plans floating around and no DWG.

hopefully this will be necessary temporarily. Mostly, I just want to learn and understand reactors. Thank you for the information. =D
Message 10 of 23
Lee_Mac
in reply to: mid-awe

You're welcome mid-awe Smiley Happy

 

Though, be careful with the solution that I have provided, the other members raise a good point about it being dangerous to automatically save a drawing without intervention from the user.

 

Lee

Message 11 of 23
dbroad
in reply to: mid-awe

For your application, I would recommend something like that proposed here:

 

http://www.afralisp.net/visual-lisp/tutorials/reactors-part-2.php

 

It saves the drawing as part of the plot command.  To be complete, you should also track the publish command.

Architect, Registered NC, VA, SC, & GA.
Message 12 of 23
mid-awe
in reply to: dbroad

That is a perfect solution. I think everyone will agree. Thank you. Funny thing is I've been through most of the tutorials on afralisp but I have found way around most issues. I keep putting off reactors when I just need to consider where they can be useful. The information that Lee shared has me thinking.

Again, thank you 🙂
Message 13 of 23
dbroad
in reply to: mid-awe

You're welcome. Glad to help.

Architect, Registered NC, VA, SC, & GA.
Message 14 of 23
Anonymous
in reply to: dbroad

Goodmorning

my 2 cents,

 

the afralisp website, shows a method to force a save upon -plot command being called. This code also uses a reactor, but is probably much safer than one that responds to every single command. Visible response or not, that seems like alot of processing.....as an alternative method to save with plotting, would be to redefine the plot command to include a qsave just before or just after the plot command, depending on your needs (some companies like the most current time/date stamp on their most recent plots, some don't). Or even better to use locally defined functions...

 

I know this pretty much takes it full circle but if you're still attempting to monitor system variable changes then hopefully you would see the light in not requiring a reactor to save your drawings as well- and just use the reactor on the system variable changes.

 

Here is a quickly adapted example using (verbatim at first only a few modifications) the examples given on the afralisp website, and kenny ramage's autosave function, also on the same webpage. This should put an autosave into drawings that are plotted, so that nothing gets plotted that is not saved.

 

(command "undefine" "-plot")

 

(defun C:PLOT ()
;define the function
 
    (autosave)
    ;call the Autosave function
 
    (command ".plot")
    ;call the plot command
 
  (princ)
 
);defun

Next we need to write our Autosave function :

 

(defun AUTOSAVE ( / T1 ECC)
;define the function
 
      (setq ECC (getvar "CMDECHO"))
      ;get the value of the CMDECHO system variable
 
      (setvar "CMDECHO" 0)
      ;switch it off
 
      (if (not T3) (setq T3 (getvar "TDUSRTIMER")))
      ;check if we have the value of the drawing timer
      ;if we haven't got it, then get it.
 
      (if (not T2) (setq T2 (getreal "\nHow many minutes between saves? : ")))
      ;check if we have an AutoSave time.
      ;if we haven't got it, then get it.
 
      (setq T1 (getvar "TDUSRTIMER"))
      ;get the drawing timer again for comparison purposes.
 
      (if (> (- T1 T3) (/ T2 60.0 24.0))
      ;compare the drawing timer values
 
      (progn
      ;if it is time to save
 
            (prompt "\nAutoSaving Drawing, Please Wait…")
            ;inform the user
 
            (command "QSAVE")
            ;save the drawing
 
            (setq T3 (getvar "TDUSRTIMER"))
            ;reset the timer
 
      );progn
 
      );if
 
      (setvar "CMDECHO" ECC)
      ;reset CMDECHO
 
   (princ)
 
);defun





 

I'm not all too familiar with his autosave function but it looks helpful, and I don't think mr. Ramage minds people adapting his code for their needs, isn't that the entire point of the afralisp website, to teach and provide example? Anyhow...hope this glaring simple method is somewhat useful , even if already considered or similar methods were already considered...

Message 15 of 23
mid-awe
in reply to: Anonymous

Thank you. Some nice considerations here.
Message 16 of 23
Anonymous
in reply to: Lee_Mac

Alright, I need a reactor that basically reacts with an alert telling people not to do edit when the drawing is Read-Only.

I can detect that a file is read-only, and the alert is obviously no problem either... but do I have other options then basically listing about all creating or editing commands?

In fact: shouldn't there be some "general" command disabling going on when read-only anyway? (or: can't someone just tell me the sysvar? 🙂 )

The plan is to just make edits impossible unless the file gets checked out (which is a command) - and yes: If anyone has ideas that do not involve a reactor... that should be better. 🙂
Message 17 of 23
mid-awe
in reply to: Anonymous

Why not just alert the drafter if the drawing is read only? If they ignore all notices then it's their time wasted and perhaps they'll pay closer attention next time.

BTW, the sysvar is "WRITESTAT" : 0 means the drawing read-only.

Message 18 of 23
Lee_Mac
in reply to: mid-awe


@mid-awe wrote:

Why not just alert the drafter if the drawing is read only? If they ignore all notices then it's their time wasted and perhaps they'll pay closer attention next time.

BTW, the sysvar is "WRITESTAT" : 0 means the drawing read-only.


I agree - a simple

 

(if (zerop (getvar 'writestat))
    (alert "Drawing is read-only")
)

Placed in the acaddoc.lsp would probably suffice for this.

 

Though, the reactor route is also pretty simple:

 

(defun c:roreactoron nil
    (if (= 'vlr-command-reactor (type readonly:reactor))
        (if (not (vlr-added-p readonly:reactor))
            (vlr-add readonly:reactor)
        )
        (setq readonly:reactor
            (vlr-command-reactor nil
               '((:vlr-commandwillstart . readonly:callback))
            )
        )
    )
    (princ "\nRead Only Reactor enabled.")
    (princ)
)
(defun c:roreactoroff nil
    (if (= 'vlr-command-reactor (type readonly:reactor))
        (vlr-remove readonly:reactor)
    )
    (setq readonly:reactor nil)
    (princ "\nRead Only Reactor disabled.")
    (princ)
)
(defun readonly:callback ( obj arg )
    (if (zerop (getvar 'writestat))
        (alert "WARNING: Drawing is read-only.")
    )
    (princ)
)
(vl-load-com)
(princ)

 

The reactor can be enabled or disabled using the roreactoron & roreactoroff commands respectively.

 

Lee

 

Message 19 of 23
Anonymous
in reply to: mid-awe

The issue is related to the fact that they need to check a drawing out before they start editing - but they don't... with related issues.

They do get a Read-only warning upon opening - which they ignore... Hell, I've seen people click away crashes every day for months without knowing what the crash alert actually told them. (more then daily)

So...a reactor that basically makes working impossible (alert popup upon every command) is what is needed at this point....

...I think... may just have to do a defun all but open, close and check out. (muhahahaha)
Message 20 of 23
Anonymous
in reply to: Lee_Mac

Thank you Lee! It now also makes quite a bit more sense to me 🙂

 

If I use the writestat method, I'll just create a double popup (saying the same) upon opening... besides the "Read-only" at the top of the screen, after the name... the lock on the drawing tab and all these other functions that AutoCAD already offers to show something is read-only.

 

The reactor method seems more effective... If only if I could find the right spot to start it from:

- When I put it through the startup suite, the drafter needs to press Ok 4 times besides the one time AutoCAD already asks for (the regular read-only warning).

- This is because upon opening I have a Standards script that makes sure some settings are correct, which apparently activates the reactor 4 times.

 

so:

 

I'll pass through my standards script and see if I vl-cmd or command anything in there and if there's other methods to accomplish the same. 🙂

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

Post to forums  

Autodesk Design & Make Report

”Boost