TheSwamp
Code Red => .NET => Topic started by: David Hall on January 09, 2008, 01:57:20 PM
-
I need to bind 25000 xref'd drawings :cry: And before everyone starts screaming about why I dont want to do this, I already know. Its out of my hands, and I was instructed I would do this, so get started :pissed:
That being said, I am open to suggestions for how I should do this in the most productive way. I am thinking a megga huge script file that calls either a LISP or VBA function to search the Block Table and bind my files. Heres the kicker, I only want to bind 1 of 4 files that it finds. There should only be 1 of the 4 versions, but I must check for all 4 and bind whatever I find. ALL OTHER XREFS ARE TO BE MAINTAINED! (now everyone breathe a sigh of relief , I haven't completely lost my mind. Also, sorry for shouting, just helping everyone know the world is still spinning in CAD land :-D :lol:)
So anyone want to through together a quick version of how they would do it?
-
use a MAP query
-
dont have Map, so I guess that wont work. Great Idea though
-
Since you are learning .Net, why not go that route. I think the question about binding has been asked here, and Glenn helped said person.
If not, then I would use ObjectDBX with Lisp, but you can use VBA to do it also. This way you won't have to open the drawings in the editor, and it will make it faster. I would do it over night, and put in some code to write to a text file so you know if there were any problems.
Edit: Here is said .Net link. (http://www.theswamp.org/index.php?topic=12272.0)
-
Thanks Tim. I am learning .Net, and that's kinda what I'm leaning towards as well, but since I'm learning, I'm hoping to have someone throw me a bone. If not, I will probably write it in VBA because my LISP skills are seriously lacking. Way seriously lacking!
-
Some of Tim's Routines:
http://www.theswamp.org/index.php?topic=12147.
http://www.theswamp.org/index.php?topic=11407.msg149665#msg149665
-
Some of Tim's Routines:
http://www.theswamp.org/index.php?topic=12147.
http://www.theswamp.org/index.php?topic=11407.msg149665#msg149665
Thanks Alan. I relearned somethings from those, but that isn't good news for CmdrDuh. It looks like he will have to go the .Net route, or a script file so that all the xref's get loaded into memory so they can be worked on.
-
Just my halfacent:
http://www.theswamp.org/index.php?topic=18781.msg230055#msg230055
-
Regardless how you do it David, VBA, DOT NET, LISP ... here's a couple things off the top of me noggin that I think you might be mindful to consider ...
- The hosting requirements are going to balloon, and by magnitudes, so it would be advisable to assess how much headroom you have before you start (to determine if it's even feasible to proceed) and then monitor server free space as you process the files, elegantly terminating (and flagging where you left off) if there appears to be inadequate storage space rather than just crashing on a document save instruction.
- It would be advisable to save each drawing to a new document, or backup all the originals first. You know you will have to have to restore an original at some point, the only question is when and how many. If you can't make a copy, say because of the storage requirements, log your activities to the degree that if you had to recreate any given drawing from a processed one you have adequate information to easily do so. See next item.
- I would log the all activities (and errors, exceptions etc), either as dictionary entries in each drawing (i.e. date, time, xref block name, path, flags etc) or as one massive appended (simple csv) log that tracks everything including the successful / complete save of the document after all the binding activities.
- Assume that a massive batch process in the order of 25000 drawings will most certainly crash at some point, possibly multiple times, so you will need to know where in the grand batch AutoCAD gave up the ghost in order to be able to easily and intelligently restart the batch. The underpinning to this would be the creation of a massive drawing list, rather than processing the drawings dynamically in a loop that's dynamically calling findfirst, findnext etc. See last sentence in preceding item.
- If using the editor (as opposed to objectdbx, the latter which would be my preference) disable undo ability at the start of each new drawing to be processed. This will speed up operations as well as keep AutoCAD memory requirements more modest.
Good luck man.
-
I would very much like to use ObjectDBX, but I never have. So any suggestions are welcome. Since .Net seems to be the flavor of choice, could a MOD move this to the .net forum, and I will begin posting questions and code as I write it.
-
Also, I forgot to mention that I dont have to have this done til July, so we have plenty of time to learn how to do this in .Net, and get a functional process defined
-
... could a MOD move this to the .net forum ...
Done.
-
Also, I forgot to mention that I dont have to have this done til July, so we have plenty of time to learn how to do this in .Net, and get a functional process defined
I'm willing to help where/when I can. I think this could be a good learning for a .Net process.
-
As far as any dot net coding goes I'm completely useless, however I will try to contribute by way of floating ideas and asking relevant questions.
-
The beauty of .net is it's like using arx in that you can open a db and molest it as much as you want without opening the drawing to the editor, similar as dbx only faster I'd imagine.
Opening the db/s is the easy bit, the xref part will be the kicker!
-
Duh,
More info please, especially on the 1/4 rule that is in effect.
Cheers,
Glenn.
-
I was reading in the Arx doc's to see how one would do this about the dll import, and it says that xref bind uses the 'wblockclone' method. I was wondering if there would be any redeeming qualities for going this route with .Net instead of using 'dll import'. The only thing I can think of is you wouldn't have to change the code per new release of Acad, as you wouldn't have to find the new entry point.
-
Glenn, I have 4 titleblocks named TEP.dwg, UES.dwg, VTEP.dwg, and VUES.dwg. There should be only 1 of the four in the dwg file. There could be a definition to one of the others, i.e. it was a TEP dwg then became a UES dwg, and the drafter erased the xref instead of detaching it. Does that make sense?
-
Yep.
What version of AutoCAD?
Cheers,
Glenn.
-
Im using 08, but the dwgs were created in 2002-> 08
-
Cool, no problems there.
-
Well doing some initial research, I found this in the object browser
public void BindXrefs(Autodesk.AutoCAD.DatabaseServices.ObjectIdCollection xrefIds, bool insertBind)
Member of Autodesk.AutoCAD.DatabaseServices.Database
So it looks like we can get there from here. Now just to figure out how. Im thinking we have to do a selection set w/ filter for blocks with the correct names, and then check if its a Xref. then grab id and pass to the function. OR we browse the BlockTable for the names, and check the individual BTR for is it a xref, then pass it to function. Just thinking out loud
-
Look at 'XrefGraph' to find the xrefs. If you need to find the ObjectId's of the inserted xref, then you can get that from the BlockTableRecord.
-
would xrefgraph be faster than just browsing the BTR for the names Im looking for?
-
would xrefgraph be faster than just browsing the BTR for the names Im looking for?
Yes because it will only list the xrefs. Think if you have a drawing that has one xref, and a thousand blocks. You can get the BTR from the XrefGraphNode of the XrefGraph. Just note that the first item in the XrefGraph is the drawing itself.
Tried to post this earlier, but got an error that the site couldn't be reached.
-
Well Im finding some good stuff, so I will go think on this and post something hopefully tomorrow
-
WOOOOOOOOHHHHHOOOOOOOOO .Net kicks butt!!!!
Ok, I made some progress. I was able to bind 1 xref fairly simply. Please critique what you can, and see if I could do it better. Thanks to Glenn R for the idea, and TW for pointing me in the direction of Glenn's code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Autodesk.AutoCAD;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;
namespace XrefBind
{
public class DSHClass
{
[CommandMethod("BindTep")]
static public void BindTitleBlocks()
{
Document Doc = AcadApp.DocumentManager.MdiActiveDocument;
Database Db = Doc.Database;
Editor Ed = Doc.Editor;
XrefGraph XG = Db.GetHostDwgXrefGraph(true);
if (XG.IsEmpty || XG.NumNodes == 1)
{
return;
}
using (Transaction Trans = Doc.TransactionManager.StartTransaction())
{
BlockTable BT = Trans.GetObject(Db.BlockTableId, OpenMode.ForRead) as BlockTable;
for (int i = 0; i < XG.NumNodes; i++)
{
XrefGraphNode XGN = XG.GetXrefNode(i) as XrefGraphNode;
if (XGN.Name.ToUpper() == "TEP")
{
BlockTableRecord BTR = Trans.GetObject(XGN.BlockTableRecordId, OpenMode.ForRead, false) as BlockTableRecord;
ObjectIdCollection OIC = new ObjectIdCollection();
OIC.Add(BTR.Id);
Db.BindXrefs(OIC, true);
}
}
Trans.Commit();
}
}
}
}
-
Looks good to me. Now to write it so you don't have to open the drawing in the editor so it will be faster. You will only open the database, since that is all you need. This snippet from Glenn should get you going in the right direction. (http://www.theswamp.org/index.php?topic=12257.msg151595#msg151595)
Glad your having fun with it! It is addicting. :-D
-
Thanks Tim. Before I move on to the hard pard of not opening the dwg, what would be the best way to test for the 4 options. Coming from VBA, I'm thinking Select Case. But, I noticed the || in the IF statement, and was wondering if there might be a way to use that?
-
I think that is how I would do it. I also would use ' string.Compare(XGN.Name, "TEP", true).Equals(0) '. Where the 'true' tell it to ignore case. If two strings are equal, the method returns a 0 ( less that 0 if a < b, greater than 0 if a > b), hence the ' .Equals(0) '.
Edit: Link to string.Compare method on MSDN. (http://msdn2.microsoft.com/en-us/library/zkcaxw5y.aspx)
-
I think that is how I would do it.
........ How would you do it?
I tried putting 3 sets of || in an IF and it worked, so I think Im going with that instead of SelectCase.
Next issue, if a bondhead, <cough> I mean drafter erased an xref instead of detaching, I need to try and fix that problem as well. On to more studing
-
I think that is how I would do it.
........ How would you do it?
Sorry about that. I would go with the || and if.
I tried putting 3 sets of || in an IF and it worked, so I think Im going with that instead of SelectCase.
Next issue, if a bondhead, <cough> I mean drafter erased an xref instead of detaching, I need to try and fix that problem as well. On to more studing
If you want to make sure that an xref is inserted in the drawing, then you can check the length of the ObjectIdCollection returned by the method 'GetBlockReferenceId' of the BTR. This will list all the ObjectId's of the blocks that reference the that particular BTR.
-
ok, I tried this
if (string.Compare(XGN.Name.ToString(), "TEP", true) == 0 ||
string.Compare(XGN.Name.ToString(), "UES", true) == 0 ||
string.Compare(XGN.Name.ToString(), "VTEP", true) == 0 ||
string.Compare(XGN.Name.ToString(), "VUES", true) == 0)
{
BlockTableRecord BTR = Trans.GetObject(XGN.BlockTableRecordId, OpenMode.ForRead, false) as BlockTableRecord;
ObjectIdCollection OIC = new ObjectIdCollection();
OIC.Add(BTR.Id);
Db.BindXrefs(OIC, true);
}
and it works. Is this better than the UCase?
-
Why did you use the method ToString? No need since the Name property (of the XrefGraphNode) returns a string.
Better than UCase..... not sure. I guess in this case it wouldn't matter so much since you are providing one of the strings, and you know the case.
Why did you use == instead of .Equals? I think that .Equals is the way to go, and I thought I heard about it from Tony T. here, but didn't find anything, but I did find this one a google search.
Found quote here. (http://www.velocityreviews.com/forums/showpost.php?s=c8213c72276a46eb6a95e8b0cddd8dc6&p=594391&postcount=2)
== compares identity, equals() compares equality. Two object references
will only ever return true from == if they refer to the *EXACT* *SAME*
object. "Exact same" meaning there being only one object, with two
references referring to it. OTOH, equals() will return also true if the
references refer to two objects that are considered equal. The
consideration depends on the object class. It defaults to being the
same as ==. String overrides it by comparing the contents of the string.
-
1. let me get back to you on that, I need to go see why
2. Because it worked, and I have never used .Equals(), I guess I should try that.
On to where I am so far, Im so close, I just cant figure out what I should be looking for
XrefGraphNode XGN = XG.GetXrefNode(i) as XrefGraphNode;
if (string.Compare(XGN.Name.ToString(), "TEP", true) == 0 ||
string.Compare(XGN.Name.ToString(), "UES", true) == 0 ||
string.Compare(XGN.Name.ToString(), "VTEP", true) == 0 ||
string.Compare(XGN.Name.ToString(), "VUES", true) == 0)
{
BlockTableRecord BTR = Trans.GetObject(XGN.BlockTableRecordId, OpenMode.ForRead, false) as BlockTableRecord;
ObjectIdCollection OIC = new ObjectIdCollection();
if (BTR.XrefStatus.ToString() == "Resolved")
{
//ObjectIdCollection OIC = new ObjectIdCollection();
OIC.Add(BTR.Id);
Db.BindXrefs(OIC, true);
}
else
{
Db.DetachXref(BTR.Id);
}
}
If you erase an Xref, it still evals to Resolved in the code
-
OK, I got rid of the ToString() and it still worked, so Im not sure why I had it
-
Why did you use == instead of .Equals? I think that .Equals is the way to go, and I thought I heard about it from Tony T. here, but didn't find anything, but I did find this one a google search.
FYI sometimes the == operator and Equals() call the same method to determine equality.
In the case of System.String the == operator calls Equals() which calls EqualsHelper(). :-)
Edit, Also have a looks at the StringComparer Class
http://msdn2.microsoft.com/en-us/library/system.stringcomparer.aspx
-
Thanks Daniel.
If you erase an Xref, it still evals to Resolved in the code
That is what I meant by checking the length of the ObjectIdCollection returned by the GetBlockReferenceIds method of the BTR. So in your code you would do something like
XrefGraphNode XGN = XG.GetXrefNode(i) as XrefGraphNode;
if (string.Compare(XGN.Name.ToString(), "TEP", true) == 0 ||
string.Compare(XGN.Name.ToString(), "UES", true) == 0 ||
string.Compare(XGN.Name.ToString(), "VTEP", true) == 0 ||
string.Compare(XGN.Name.ToString(), "VUES", true) == 0)
{
BlockTableRecord BTR = Trans.GetObject(XGN.BlockTableRecordId, OpenMode.ForRead, false) as BlockTableRecord;
[color=red] ObjectIdCollection BlkRefCol = BTR.GetBlockReferenceIds(true, true);
if (BlkRefCol.Count > 0)
{
// do what you want since the xref is inserted into the drawing.
}
else
{
// do what you want since the xref is NOT inserted into the drawing.
}[/color]
ObjectIdCollection OIC = new ObjectIdCollection();
if (BTR.XrefStatus.ToString() == "Resolved")
{
//ObjectIdCollection OIC = new ObjectIdCollection();
OIC.Add(BTR.Id);
Db.BindXrefs(OIC, true);
}
else
{
Db.DetachXref(BTR.Id);
}
}
-
Daniel, that was so far over my head..... but thanks for the headspin
TW, its time for a Duhism question: If I had an xref, I erased it, I attached a new one, wouldn't the BlkRef.Count be >0? Or am I just missing the boat completely
-
TW, I got it! that Rocks. :lol: :lol: :lol:
So we are looking for instances of the block
-
:-)
Good to hear!
-
I really hate to ask this, but I cant find it anywhere. How the heck do you save a dwg file thru .Net? Not Saveas, I can find tons of examples of that.
-
I really hate to ask this, but I cant find it anywhere. How the heck do you save a dwg file thru .Net? Not Saveas, I can find tons of examples of that.
You can't do a Save, only a SaveAs to the same name.
Tim - It's not a bug.
You can't save a database to the same file it was read from, because it may not have been completely read in initially, so saving to the same file poses the risk of concurrently reading/writing to the same file.
The SAVE command uses saveAs() to save to a different (e.g., temporary) file and then either deletes or renames the original to *.bak, and then renames the saved file to the original filename.
After reading Tony description, I went and read about AcDbDatabase Class in
the ARX help - for the first time.
AcDbDatabase::save Function
Acad::ErrorStatus
save();
Currently not implemented. <<<=
From this recent thread. (http://discussion.autodesk.com/thread.jspa?threadID=631691)
-
OK, I guess I should visit the A.NG more often. I just dont like the peeps there, so I avoid it.
-
OK, I guess I should visit the A.NG more often. I just dont like the peeps there, so I avoid it.
There are knowledgeable people both here and there, so I post here, but look there also. :-)
-
yea, I know I should as well. KB and I have had that discussion, and I just never go there.
-
OK, here is version 2 which works. Now to try and figure out how to do it without opening the dwg in the editor
[CommandMethod("BindTep2")]
static public void BindTitleBlocks2()
{
Document Doc = AcadApp.DocumentManager.MdiActiveDocument;
Database Db = Doc.Database;
Editor Ed = Doc.Editor;
XrefGraph XG = Db.GetHostDwgXrefGraph(true);
if (XG.IsEmpty || XG.NumNodes == 1)
{
return;
}
using (Transaction Trans = Doc.TransactionManager.StartTransaction())
{
BlockTable BT = Trans.GetObject(Db.BlockTableId, OpenMode.ForWrite) as BlockTable;
for (int i = 0; i < XG.NumNodes; i++)
{
XrefGraphNode XGN = XG.GetXrefNode(i) as XrefGraphNode;
if (string.Compare(XGN.Name, "TEP", true).Equals(0) ||
string.Compare(XGN.Name, "UES", true).Equals(0) ||
string.Compare(XGN.Name, "VTEP", true).Equals(0) ||
string.Compare(XGN.Name, "VUES", true).Equals(0))
{
BlockTableRecord BTR = Trans.GetObject(XGN.BlockTableRecordId, OpenMode.ForWrite, false) as BlockTableRecord;
ObjectIdCollection BlkRefCol = BTR.GetBlockReferenceIds(true, true);
if (BlkRefCol.Count > 0)
{
ObjectIdCollection OIC = new ObjectIdCollection();
OIC.Add(BTR.Id);
Db.BindXrefs(OIC, true);
}
else
{
Db.DetachXref(BTR.Id);
}
}
}
Trans.Commit();
}
Db.SaveAs(Doc.Name , DwgVersion.Current);
}
-
yea, I know I should as well. KB and I have had that discussion, and I just never go there.
I understand. If you can find what you need here, no need to really go over there.
Code wise, it looks good to me. Let see what the masters think when they log on.
-
I read the thread where Glenn opened files from a dialog box, but I have never done that either. Any ideas? I did a search on batching, but couldn't find anything that looked close enough to make sure I was looking at the correct thing
-
I read the thread where Glenn opened files from a dialog box, but I have never done that either. Any ideas? I did a search on batching, but couldn't find anything that looked close enough to make sure I was looking at the correct thing
Sure. Here is some snippets of code that I use to open multiply drawing databases.
Document Doc = AcadApp.DocumentManager.MdiActiveDocument;
Autodesk.AutoCAD.Windows.OpenFileDialog OpenDia =
new Autodesk.AutoCAD.Windows.OpenFileDialog("Select drawings to update title blocks.",
"",
"dwg",
"",
Autodesk.AutoCAD.Windows.OpenFileDialog.OpenFileDialogFlags.AllowMultiple
);
if (OpenDia.ShowDialog() != DialogResult.OK) return;
foreach (string Path in OpenDia.GetFilenames()) {
try {
using (Database db = new Database(false, true)) {
db.ReadDwgFile (Path, System.IO.FileShare.Read, true, null);
[color=red] // Don't need the red if not updating attributes or text
if (db != HostApplicationServices.WorkingDatabase)
HostApplicationServices.WorkingDatabase = db;[/color]
GetTitleBlock(db);
//MessageBox.Show(Doc.Name);
HostApplicationServices.WorkingDatabase = Doc.Database;
db.RetainOriginalThumbnailBitmap = true;
db.SaveAs (Path, DwgVersion.Current);
}
}
catch (System.Exception ex) {
MessageBox.Show("Error in drawing: " + Path + "\n\n" + ex.Message);
}
}
-
Thanks Tim, that points me in the right direction.
-
Thanks Tim, that points me in the right direction.
You're welcome. Let us know if you need any explanation.
-
Oh I need LOTS of explanation, I'm just trying to struggle through it w/o asking you to throw me a fish