Jump to content

xdata to save info who last modified objects


Pietari

Recommended Posts

Every now and then I open my drawings and wonder "huh, who did that". Well of course nobody has done anything. Even "it must have been you who did that".

 

I have often wondered if there was a possibility to have some xData added to any changed object. I believe it would require a reactor and some xData code.

 

I am aware of these possibilities and of their power but I have never really understood the concept of reactors and xData.

 

Maybe any of you want to help out?

It would be cool that any object placed newly in the drawing and any modified object in the drawing would have an xData wich contains (getvar 'LOGINNAME). So at any given point I am able to retrieve who lastly modified the object.

 

If someone deletes objects, then of course it is lost, but thats okay.

 

Thanks for any help on this one, it may contribute to others as well.

 

(maybe there are other ways to explore but I have no clue)

Link to comment
Share on other sites

The following is an initial draft of a possible application to achieve this:

[color=GREEN];; Last Modification Logging Utility  -  Lee Mac[/color]
[color=GREEN];; Stores the username/date/command as xdata attached to every modified object.[/color]

([color=BLUE]setq[/color] lastmod:appid [color=MAROON]"LMAC_lastmod"[/color])

([color=BLUE]defun[/color] c:lastmod ( [color=BLUE]/[/color] ent lst )
   ([color=BLUE]while[/color]
       ([color=BLUE]progn[/color] ([color=BLUE]setvar[/color] 'errno 0) ([color=BLUE]setq[/color] ent ([color=BLUE]car[/color] ([color=BLUE]entsel[/color] [color=MAROON]"\nSelect object to view modification data <exit>: "[/color])))
           ([color=BLUE]cond[/color]
               (   ([color=BLUE]=[/color] 7 ([color=BLUE]getvar[/color] 'errno))
                   ([color=BLUE]princ[/color] [color=MAROON]"\nMissed, try again."[/color])
               )
               (   ([color=BLUE]null[/color] ent) [color=BLUE]nil[/color])
               (   ([color=BLUE]null[/color] ([color=BLUE]setq[/color] lst ([color=BLUE]mapcar[/color] '[color=BLUE]cdr[/color] ([color=BLUE]cdadr[/color] ([color=BLUE]assoc[/color] -3 ([color=BLUE]entget[/color] ent ([color=BLUE]list[/color] lastmod:appid)))))))
                   ([color=BLUE]princ[/color] [color=MAROON]"\nSelected object does not contain modification data."[/color])
               )
               (   ([color=BLUE]princ[/color] ([color=BLUE]strcat[/color] [color=MAROON]"\nObject last modified by "[/color] ([color=BLUE]car[/color] lst) [color=MAROON]" at "[/color] ([color=BLUE]cadr[/color] lst) [color=MAROON]" using the "[/color] ([color=BLUE]caddr[/color] lst) [color=MAROON]" command."[/color])))
           )
       )
   )
   ([color=BLUE]princ[/color])
)
([color=BLUE]defun[/color] c:lastmodclear ( [color=BLUE]/[/color] idx sel )
   ([color=BLUE]if[/color] ([color=BLUE]setq[/color] sel ([color=BLUE]ssget[/color] [color=MAROON]"_:L"[/color] ([color=BLUE]list[/color] ([color=BLUE]list[/color] -3 ([color=BLUE]list[/color] lastmod:appid)))))
       ([color=BLUE]repeat[/color] ([color=BLUE]setq[/color] idx ([color=BLUE]sslength[/color] sel))
           ([color=BLUE]entmod[/color] ([color=BLUE]append[/color] ([color=BLUE]entget[/color] ([color=BLUE]ssname[/color] sel ([color=BLUE]setq[/color] idx ([color=BLUE]1-[/color] idx)))) ([color=BLUE]list[/color] ([color=BLUE]list[/color] -3 ([color=BLUE]list[/color] lastmod:appid)))))
       )
   )
   ([color=BLUE]princ[/color])
)
([color=BLUE]defun[/color] c:lastmodon [color=BLUE]nil[/color]
   (lastmod:remove)
   ([color=BLUE]vlr-command-reactor[/color] [color=MAROON]"lastmod"[/color]
      '(
           ([color=BLUE]:vlr-commandwillstart[/color] . lastmod:com:start)
           ([color=BLUE]:vlr-commandended[/color]     . lastmod:com:ended)
           ([color=BLUE]:vlr-commandcancelled[/color] . lastmod:com:clear)
           ([color=BLUE]:vlr-commandfailed[/color]    . lastmod:com:clear)
       )
   )
   ([color=BLUE]setq[/color] lastmod:obj:rtr
       ([color=BLUE]vlr-object-reactor[/color]
           (   ([color=BLUE]lambda[/color] ( sel [color=BLUE]/[/color] idx lst )
                   ([color=BLUE]if[/color] sel
                       ([color=BLUE]repeat[/color] ([color=BLUE]setq[/color] idx ([color=BLUE]sslength[/color] sel))
                           ([color=BLUE]setq[/color] lst ([color=BLUE]cons[/color] ([color=BLUE]vlax-ename->vla-object[/color] ([color=BLUE]ssname[/color] sel ([color=BLUE]setq[/color] idx ([color=BLUE]1-[/color] idx)))) lst))
                       )
                   )
               )
               ([color=BLUE]ssget[/color] [color=MAROON]"_X"[/color])
           )
           [color=MAROON]"lastmod"[/color]
          '(
               ([color=BLUE]:vlr-modified[/color]       . lastmod:obj:modified)
               ([color=BLUE]:vlr-subobjmodified[/color] . lastmod:obj:modified)
           )
       )
   )
   ([color=BLUE]regapp[/color] lastmod:appid)
   ([color=BLUE]princ[/color] [color=MAROON]"\nObject modification logging enabled."[/color])
   ([color=BLUE]princ[/color])
)
([color=BLUE]defun[/color] c:lastmodoff [color=BLUE]nil[/color]
   (lastmod:remove)
   ([color=BLUE]princ[/color] [color=MAROON]"\nObject modification logging disabled."[/color])
   ([color=BLUE]princ[/color])
)
([color=BLUE]defun[/color] lastmod:remove [color=BLUE]nil[/color]
   ([color=BLUE]foreach[/color] obj ([color=BLUE]apply[/color] '[color=BLUE]append[/color] ([color=BLUE]mapcar[/color] '[color=BLUE]cdr[/color] ([color=BLUE]vlr-reactors[/color] [color=BLUE]:vlr-object-reactor[/color] [color=BLUE]:vlr-command-reactor[/color])))
       ([color=BLUE]if[/color] ([color=BLUE]=[/color] [color=MAROON]"lastmod"[/color] ([color=BLUE]vlr-data[/color] obj)) ([color=BLUE]vlr-remove[/color] obj))
   )
   ([color=BLUE]setq[/color] lastmod:obj:rtr [color=BLUE]nil[/color]
         lastmod:entlast [color=BLUE]nil[/color]
         lastmod:objlist [color=BLUE]nil[/color]
   )
)
([color=BLUE]defun[/color] lastmod:obj:modified ( obj rtr arg )
   ([color=BLUE]setq[/color] lastmod:objlist ([color=BLUE]cons[/color] obj lastmod:objlist))
   ([color=BLUE]princ[/color])
)
([color=BLUE]defun[/color] lastmod:com:start ( rtr com [color=BLUE]/[/color] tmp )
   ([color=BLUE]setq[/color] lastmod:entlast ([color=BLUE]entlast[/color]))
   ([color=BLUE]while[/color] ([color=BLUE]setq[/color] tmp ([color=BLUE]entnext[/color] lastmod:entlast))
       ([color=BLUE]setq[/color] lastmod:entlast tmp)
   )
   ([color=BLUE]princ[/color])
)
([color=BLUE]defun[/color] lastmod:com:ended ( rtr com [color=BLUE]/[/color] ent obj )
   ([color=BLUE]setq[/color] com ([color=BLUE]strcase[/color] ([color=BLUE]car[/color] com))
         ent ([color=BLUE]if[/color] lastmod:entlast ([color=BLUE]entnext[/color] lastmod:entlast) ([color=BLUE]entnext[/color]))
   )
   ([color=BLUE]foreach[/color] obj lastmod:objlist
       (lastmod:addxdata ([color=BLUE]vlax-vla-object->ename[/color] obj) com)
   )
   ([color=BLUE]while[/color] ent
       ([color=BLUE]if[/color] ([color=BLUE]vlax-write-enabled-p[/color] ([color=BLUE]setq[/color] obj ([color=BLUE]vlax-ename->vla-object[/color] ent)))
           ([color=BLUE]progn[/color]
               (lastmod:addxdata ent com)
               ([color=BLUE]vlr-owner-add[/color] lastmod:obj:rtr obj)
           )
       )
       ([color=BLUE]setq[/color] ent ([color=BLUE]entnext[/color] ent))
   )
   ([color=BLUE]setq[/color] lastmod:objlist [color=BLUE]nil[/color]
         lastmod:entlast [color=BLUE]nil[/color]
   )
   ([color=BLUE]princ[/color])
)
([color=BLUE]defun[/color] lastmod:clear ( rtr arg )
   ([color=BLUE]setq[/color] lastmod:objlist [color=BLUE]nil[/color]
         lastmod:entlast [color=BLUE]nil[/color]
   )
   ([color=BLUE]princ[/color])
)
([color=BLUE]defun[/color] lastmod:addxdata ( ent com [color=BLUE]/[/color] enx )
   ([color=BLUE]if[/color] ([color=BLUE]and[/color] ([color=BLUE]=[/color] 'ename ([color=BLUE]type[/color] ent))
            ([color=BLUE]setq[/color] enx ([color=BLUE]entget[/color] ent))
            ([color=BLUE]not[/color] ([color=BLUE]wcmatch[/color] ([color=BLUE]cdr[/color] ([color=BLUE]assoc[/color] 0 enx)) [color=MAROON]"VERTEX,ATTRIB,SEQEND"[/color]))
       )
       ([color=BLUE]entmod[/color]
           ([color=BLUE]append[/color] ([color=BLUE]entget[/color] ent)
               ([color=BLUE]list[/color]
                   ([color=BLUE]list[/color] -3
                       ([color=BLUE]list[/color] lastmod:appid
                           ([color=BLUE]cons[/color] 1000 ([color=BLUE]getvar[/color] 'loginname))
                           ([color=BLUE]cons[/color] 1000 ([color=BLUE]menucmd[/color] [color=MAROON]"m=$(edtime,0,yyyy-mo-dd hh:mm:ss)"[/color]))
                           ([color=BLUE]cons[/color] 1000 com)
                       )
                   )
               )
           )
       )
   )
)
([color=BLUE]vl-load-com[/color]) ([color=BLUE]princ[/color])

Type 'LASTMODON' to enable modification logging, and 'LASTMODOFF' to disable logging.

 

Type 'LASTMOD' to view the last modification data for an object.

 

Type 'LASTMODCLEAR' to remove the logged modification data for a set of objects.

 

When enabled, a Command Reactor & Object Reactor will run silently in the background and will attach xdata to modified objects containing the username, date/time of the modification, and the name of the last command used.

 

The code has only been briefly tested, so expect some bugs.

 

Lee

Edited by Lee Mac
Link to comment
Share on other sites

Hi Lee,

 

Thanks for the reply & possible solution.

 

I have tested it and used this code to retrieve the xData:

(Found here)

(defun c:xdread ( / en xd ) (if (setq en (car (entsel "\nSelect entity to read xdata:"))) (if (setq xd (cdr (assoc -3 (entget en '("*"))))) (foreach a xd (foreach x a (print x)) ) (princ "\nEntity has no xdata.") ) ) (princ) )

Your code works like expected / hoped for. Only the way to retrieve the xData back is kind of simple now but it is good enough. I can at least find out who has modified certain objects!

 

About possible bugs, I have found 1 so far: when te reactor is active it errors on any command:

 

Command: *Cancel*

Command: REC
RECTANG
Specify first corner point or [Chamfer/Elevation/Fillet/Thickness/Width]:
Specify other corner point or [Area/Dimensions/Rotation]: ; error: bad argument 
type: lentityp nil

Command: L
LINE Specify first point:
Specify next point or [Angle/Length/Undo]:
Specify next point or [Angle/Length/Undo]:
; error: bad argument type: lentityp nil

Command: PL
PLINE
Specify start point:
Current line-width is 0.0000
Specify next point or [Arc/Halfwidth/Length/Undo/Width]:
Specify next point or [Arc/Close/Halfwidth/Length/Undo/Width]:
Specify next point or [Arc/Close/Halfwidth/Length/Undo/Width]:
; error: bad argument type: lentityp nil

Command: C
CIRCLE Specify center point for circle or [3P/2P/Ttr (tan tan radius)]:
Specify radius of circle or [Diameter] <184.4649>: ; error: bad argument type: 
lentityp nil

Command: ARC
Specify start point of arc or [Center]:
Specify second point of arc or [Center/End]:
Specify end point of arc: ; error: bad argument type: lentityp nil

Command:
ARC Specify start point of arc or [Center]: *Cancel*

Command: *Cancel*

Command: XL
XLINE Specify a point or [Hor/Ver/Ang/Bisect/Offset]:
Specify through point:
Specify through point:
; error: bad argument type: lentityp nil

Command:
Command:
Command: _ellipse
Specify axis endpoint of ellipse or [Arc/Center]: _c
Specify center of ellipse:
Specify endpoint of axis:
Specify distance to other axis or [Rotation]: ; error: bad argument type: 
lentityp nil

Command: e
ERASE
Select objects: Specify opposite corner: 2 found

Select objects:
; error: bad argument type: lentityp nil

MOVE
Select objects: 1 found

Select objects:

Specify base point or [Displacement] <Displacement>:
Specify second point of displacement or <use first point as displacement>: ; 
error: bad argument type: lentityp nil

Command: CO
COPY
Select objects: 1 found

Select objects:

Current settings: Copy mode = Multiple
Specify base point or [Displacement/mOde] <Displacement>:
Specify second point of displacement or <use first point as displacement>:
Specify second point or [Exit/Undo] <Exit>: *Cancel*

Command:
COPY
Select objects: 1 found

Select objects:

Current settings: Copy mode = Multiple
Specify base point or [Displacement/mOde] <Displacement>:
Specify second point of displacement or <use first point as displacement>:
Specify second point or [Exit/Undo] <Exit>:
; error: bad argument type: lentityp nil

 

I dont know if it could be fixed / done... but it is a good start, thanks!

Link to comment
Share on other sites

Another possibility is you can record every time a dwg is opened who opened it and what commands were executed runs fine on a server based system, interesting to look at how many commands v's time in dwg commands hopefully = production.

 

Productivity_Analisys_Tool.lsp by Konstantin Gerasimov, kosio_gerasimov@abv.bg

Link to comment
Share on other sites

@ Bigal: that solution has crossed my mind as well but I'd rather explore the xData options, thanks anyway!

 

@ Lee:

First of all thank you for the effort, you have really understood what I mean.

The program does exactly what it is supposed to do.

I will put this code in our users acad.lsp file or my own startup routines.

At any given point I can look and say "YOU DID IT!"

 

This solution maybe combined with the solution suggested by Bigal would make it possible to build up a history.

Meaning: if a block is inserted, I can see who initially put it in the drawing.

If that block would be moved, etc. it would keep track of who did what and when.

 

By the way, I noticed that when you modify a blocks attibutes, there is no xData added to the blok itself.

So a change of attribute values is not working. Maybe xData can only be stored to an object itself and not a block.

 

Manu kudo's to you Lee!!

Link to comment
Share on other sites

First of all thank you for the effort, you have really understood what I mean.

The program does exactly what it is supposed to do.

I will put this code in our users acad.lsp file or my own startup routines.

At any given point I can look and say "YOU DID IT!"

 

Many thanks Pietari - it was an interesting application to write! :)

 

By the way, I noticed that when you modify a blocks attibutes, there is no xData added to the blok itself.

So a change of attribute values is not working.

 

I have now updated the above code to ensure changes to block attributes and 3D polyline vertices are also logged.

 

I have also added a new command: LASTMODCLEAR to enable you to remove the logged modification data for a selection of objects if necessary.

 

Manu kudo's to you Lee!!

 

Thanks!

Link to comment
Share on other sites

I have tested it and used this code to retrieve the xData:

(Found here)

(defun c:xdread ( / en xd ) (if (setq en (car (entsel "\nSelect entity to read xdata:"))) (if (setq xd (cdr (assoc -3 (entget en '("*"))))) (foreach a xd (foreach x a (print x)) ) (princ "\nEntity has no xdata.") ) ) (princ) )

 

You may also find MgdDbg to be useful for this and other querying tasks. :thumbsup:

 

Cheers

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...