Ortaklarımız: Ant Mekanik , Teknova Shop , Taliasoft

AutoCAD Gunlugu

Üye Girişi

RSS

Autocad Günlüğü RSS besleyicisine üye ol.


Eyl 23
Salı
AutoCAD, Programlama
Autocad .NET API ile Veri Depolama

Merhaba,

Bu yazıma AutoCAD.NET, ObjectARX ve ObjectDBX kaynaklarından bahsederek başlamak istiyorum.

AutoCAD programlama (ARX ve .NET API) ile ilgilenmeye başlayanların ilk dikkatini çeken bu konuda fazla yayımın olmayışıdır. AutoCAD için en etkili ve yetkin programlama dili olan ObjectARX için bile bugüne kadar iki kitap ve bir öğretim CD'si yayımlanmıştır, Türkçe kaynak olmadığını söylemeyeyse gerek yok:

  • Programming AutoCAD in ObjectARX, Charles McAuley, Thomson Learning, 2000.
  • ObjectARX Primer, Bill Kramer, Thomson Learning, 2000.
  • Ransen Software's ObjectARX multimedia CD-ROM, 2000.

Yeni sayılabilecek AutoCAD.NET API'si içinde VB.NET diliyle yazılmış tek bir kitap var:

  • VB.NET Programming for AutoCAD Customization, Level 1, Jerry Winter, 2007.

Web kaynakları gelince, yayımlanmış kaynaklara göre daha zengin bir içeriğe sahipler.

Autodesk kaynakları:

Bunların dışında diğer birkaç kaynak:

Artık AutoCAD.NET API ile nasıl veri depolanacağına dönelim.

Bir önceki yazımda sembol tablolarının (Symbol Tables) türlerinden ve kullanımından bahsetmiştim. Bu yazımda ağırlıklı olarak bahsedeceğim sözlüklerinse, bu tabloların daha genel halleri olduğunu söylemek sanırım yanlış olmaz.

AutoCAD .NET API'si,  AutoCAD içinde veri depolamak için önemli araçlar içerir. NOD'lar, ExtensionDictionary'ler ve  XData'lar en çok kullandıklarımızdır. NOD kayıtları, DBDictionary nesnesinden türemiş sözlükleri,  ExtensionDictionary ve XData'larsa DBObject nesnesinin iki önemli özelliğini temsil eder. ExtensionDictionary bir DBDictionary nesne kimliği (ObectId), XData ise bir ResultBuffer nesnesidir.

Bu araçlar ile AutoCAD nesneleri üzerinde kısmi bir özgürlük kazanırız; istediğimiz bilgileri nesnelere ekleyebilir, silebilir, güncelleyebilir hatta nesne kimliklerini (ObjectId) birbirine bağlayabiliriz. Tüm bu olanaklara rağmen, gerçek özgürlükle özel ObjectARX(C++) nesnelerini kullanmaya başladığımızda tanışırız. Ama bu tür nesnelerin, daha ileri düzeyde, ObjectARX ve ObjectDBX ile ilişkileri göz önünde tutularak ele alınması gerektiğinden şimdilik aşağıda sıraladıklarımızla yetineceğiz:

a) Extended Entity Data -  XData (Ek nesne verisi, AutoCAD varlıklarına iliştirilir)

b) Named Object Dictionary - NOD (Adlandırılmış nesne sözlüğü)

c) Extension Entity Dictionary - ExtensionDictionary (Nesne sözlük eklentisi , AutoCAD varlıklarına iliştirilir)

XData'nın kullanımı diğerlerine göre daha kolay olmakla beraber, büyük verilerin depolanması için en uygun olanları, NOD'lar ve ExtensionDictionary'lerdir. Her AutoCAD grafik varlığı, XData özelliğinde en fazla 16 Kb, 2 Gb veri kapasiteli XRecord nesneleriyle kurulabilen ExtensionDictionary'lerindeyse büyük miktarlarda veri depolanabilir.

Hepinizin bildiği gibi, yerleşik veritabanı nesneleriyle bu nesnelere ait anahtar adlarını ikili bir düzende saklamaya yarayan haritalar, bağlı listeler (Linked List) olarak tanımlanabilecek AutoCAD sözlükleri, veri depolamada önemli bir rol oynamaktadır. Sembol tablolarında olduğu gibi sözlüklerde de kayıt anahtarları benzersiz (unique)olmak zorundadır; bir sözlük aynı adda iki kayıt anahtarı içeremez. Ayrıca sözlüklerde depolanabilecek kimliklerin (ObjectId) ait olduğu nesne türlerinde de önemli bir sınırlama vardır: sözlükler, sadece DBObject sınıfından ya da bu sınıftan türemiş nesnelere ait kimlikleri içerebilir.

Birçoğunuzun yabancı olmadığına inandığım bu kısa ve basit bilgilerin ardından yukarda andığımız veri depolama nesne ve özelliklerinin AutoCAD.NET API'si ile nasıl kullanılacağına gösteren örneklere artık geçebiliriz.

Aşağıdaki C# kod örneklerinde sıkça geçecek olan TypedValue yapısı, ResBuffer ve XRecord nesneleri hakkında bilgi vermeyeceğim, detaylı bilgiyi ObjextArx SDK yardım dosyalarında bulabilirsiniz.

Veri depolama yöntemlerini bir düzen içerisinde anlatabilmek için öncelikle, IPEProfile adlı bir C# sınıfı oluşturacağız. Bu sınıfın Draw() metodu sırasıyla şu işlemleri gerçekleştirecektir:

1) ŞEKİL-1'de gördüğünüz IPE çelik profiline ait bilgileri kullanıcıdan alacak

2) Profil çizim noktalarını hesaplayarak bir Polyline nesnesi oluşturacak ve

3) Profil bilgilerini, "AG_IEP_PROFILE" anahtarını kullanarak depolayacak.

Draw() metodu kod listesi:

C:
  1. public void Draw(DataStoreType StoreType)
  2. {
  3. if (!GetData()) return;
  4.  
  5. #region Noktaların hesaplanması
  6. Point2d Pnt1 = new Point2d(LeftLowerPnt.X, LeftLowerPnt.Y) + b * Vector2d.XAxis;
  7. Point2d Pnt2 = Pnt1 + t * Vector2d.YAxis;
  8. Point2d Pnt3 = Pnt2 - 0.5 * (b - s1) * Vector2d.XAxis;
  9. Point2d Pnt4 = Pnt3 - r * (Vector2d.XAxis - Vector2d.YAxis);
  10. Point2d Pnt5 = Pnt4 + d * Vector2d.YAxis;
  11. Point2d Pnt6 = Pnt5 + r * (Vector2d.XAxis + Vector2d.YAxis);
  12. Point2d Pnt7 = Pnt6 + 0.5 * (b - s1) * Vector2d.XAxis;
  13. Point2d Pnt8 = Pnt7 + t * Vector2d.YAxis;
  14. Point2d Pnt9 = Pnt8 - b * Vector2d.XAxis;
  15. Point2d Pnt10 = Pnt9 - t * Vector2d.YAxis;
  16. Point2d Pnt11 = Pnt10 + 0.5 * (b - s1) * Vector2d.XAxis;
  17. Point2d Pnt12 = Pnt11 + r * (Vector2d.XAxis - Vector2d.YAxis);
  18. Point2d Pnt13 = Pnt12 - d * Vector2d.YAxis;
  19. Point2d Pnt14 = Pnt13 - r * (Vector2d.XAxis + Vector2d.YAxis);
  20. Point2d Pnt15 = Pnt14 - 0.5 * (b - s1) * Vector2d.XAxis;
  21. #endregion
  22.  
  23. double bulge = Math.Tan(0.5 * Math.PI / 4.0);
  24.  
  25. #region Polyline'ın oluşturulması
  26. Polyline pline = new Polyline();
  27. pline.SetDatabaseDefaults();
  28. pline.Closed = true;
  29. pline.AddVertexAt(0, new Point2d(LeftLowerPnt.X, LeftLowerPnt.Y), 0.0, 0.0, 0.0);
  30. pline.AddVertexAt(1, Pnt1, 0.0, 0.0, 0.0);
  31. pline.AddVertexAt(2, Pnt2, 0.0, 0.0, 0.0);
  32. pline.AddVertexAt(3, Pnt3, -bulge, 0.0, 0.0);
  33. pline.AddVertexAt(4, Pnt4, 0.00, 0.0, 0.0);
  34. pline.AddVertexAt(5, Pnt5, -bulge, 0.0, 0.0);
  35. pline.AddVertexAt(6, Pnt6, 0.00, 0.0, 0.0);
  36. pline.AddVertexAt(7, Pnt7, 0.0, 0.0, 0.0);
  37. pline.AddVertexAt(8, Pnt8, 0.0, 0.0, 0.0);
  38. pline.AddVertexAt(9, Pnt9, 0.0, 0.0, 0.0);
  39. pline.AddVertexAt(10, Pnt10, 0.0, 0.0, 0.0);
  40. pline.AddVertexAt(11, Pnt11, -bulge, 0.0, 0.0);
  41. pline.AddVertexAt(12, Pnt12, 0.00, 0.0, 0.0);
  42. pline.AddVertexAt(13, Pnt13, -bulge, 0.0, 0.0);
  43. pline.AddVertexAt(14, Pnt14, 0.00, 0.0, 0.0);
  44. pline.AddVertexAt(15, Pnt15, 0.0, 0.0, 0.0);
  45. #endregion
  46.  
  47. ObjectId plineId = ObjectId.Null;
  48. Database db = Application.DocumentManager.MdiActiveDocument.Database;
  49. Autodesk.AutoCAD.DatabaseServices.TransactionManager transactionManager =
  50. db.TransactionManager;
  51. using (Transaction transaction = transactionManager.StartTransaction())
  52. {
  53. BlockTable blkTable = (BlockTable)transactionManager.GetObject(db.BlockTableId,
  54. OpenMode.ForRead, false);
  55. BlockTableRecord blkTableRec =
  56. (BlockTableRecord)transactionManager.GetObject(blkTable[BlockTableRecord.ModelSpace],
  57. OpenMode.ForWrite, false);
  58. plineId = blkTableRec.AppendEntity(pline);
  59. transactionManager.AddNewlyCreatedDBObject(pline, true);
  60. transaction.Commit();
  61. }
  62.  
  63. if (plineId != ObjectId.Null)
  64. {
  65. switch (StoreType)
  66. {
  67. case DataStoreType.NamedObjDict:
  68. AddNodRecord(AppName, plineId);
  69. break;
  70. case DataStoreType.ExtObjDict:
  71. AddExtEntDict(AppName, plineId);
  72. break;
  73. case DataStoreType.ExtData:
  74. AddXData(AppName, plineId);
  75. break;
  76. default:
  77. break;
  78. }
  79. }
  80. else
  81. {
  82. Application.ShowAlertDialog("Kayıt eklenemedi.");
  83. }
  84. }

IPEProfile sınıf yapısını gösteren diyagram için lütfen ŞEKİL-2'ye bakınız.


ŞEKİL-1


ŞEKİL-2

b (profil başlık genişliği), h (profil yüksekliği),  t (profil başlık et kalınlığı), s (profil gövde et kalınlığı) ve r değerlerinin, sözlüklerde ya da XData'larda depolayacağımız kayıtları temsil ettiğini unutmayarak devam edelim.

XDATA ile veri saklama:

C:
  1. private void AddXData(string RegAppName, ObjectId ObjId)
  2. {
  3. Document doc =Application.DocumentManager.MdiActiveDocument;
  4. Database db = doc.Database;
  5. Editor ed = doc.Editor;
  6. using (Transaction tr = doc.TransactionManager.StartTransaction())
  7. {
  8. DBObject obj = tr.GetObject(ObjId, OpenMode.ForWrite);
  9. RegAppTable regAppTable =
  10. (RegAppTable)tr.GetObject(db.RegAppTableId,
  11. OpenMode.ForRead, false);
  12. if (!regAppTable.Has(RegAppName))
  13. {
  14. regAppTable.UpgradeOpen();
  15. RegAppTableRecord regAppTableRec = new RegAppTableRecord();
  16. regAppTableRec.Name = RegAppName;
  17. regAppTable.Add(regAppTableRec);
  18. tr.AddNewlyCreatedDBObject(regAppTableRec, true);
  19. }
  20. ResultBuffer rb = new ResultBuffer(
  21. new TypedValue((int)DxfCode.ExtendedDataRegAppName,
  22. RegAppName),
  23. new TypedValue((int)DxfCode.ExtendedDataReal, b),
  24. new TypedValue((int)DxfCode.ExtendedDataReal, h),
  25. new TypedValue((int)DxfCode.ExtendedDataReal, t),
  26. new TypedValue((int)DxfCode.ExtendedDataReal, s),
  27. new TypedValue((int)DxfCode.ExtendedDataReal, r)
  28. );
  29. obj.XData = rb;
  30. rb.Dispose();
  31. tr.Commit();
  32. }
  33. }

Uygulama kayıt adını ve veritabanı model uzayına eklediğimiz Polyline'ın kimliğini parametre olarak kabul eden AddXData() metodunu dikkatle incelersek, iki önemli işlevi yerine getirdiğini görürüz. Bunlardan ilki uygulama kayıt adını, kayıtlı uygulamalar tablosunda (RegAppTable) araması (RegAppTable.Has() metodu) ve bulamaması durumunda eklemesi (RegAppTable.Add(...) metodu). İkincisi ise profil verisinden oluşturduğu kayıt bilgilerini nesneye iliştirmesi (DBObject.XData özelliği).

XData'dan kurtulmak istediğimizde kullanacağımız metot RemoveXData() olacaktır:

C:
  1. public static void RemoveXData(string RegAppName)
  2. {
  3. Document doc = Application.DocumentManager.MdiActiveDocument;
  4. Database db = doc.Database;
  5. Editor ed = doc.Editor;
  6. PromptEntityOptions opt = new PromptEntityOptions("\nIEP profil seçin: ");
  7. PromptEntityResult res = ed.GetEntity(opt);
  8. if (res.Status == PromptStatus.OK)
  9. {
  10. using (Transaction tr = doc.TransactionManager.StartTransaction())
  11. {
  12. DBObject obj = tr.GetObject(res.ObjectId, OpenMode.ForWrite);
  13. if (obj.GetXDataForApplication(RegAppName) != null)
  14. {
  15. ResultBuffer rb = new ResultBuffer(
  16. new TypedValue((int)DxfCode.ExtendedDataRegAppName,
  17. RegAppName));
  18. obj.XData = rb;
  19. rb.Dispose();
  20. tr.Commit();
  21. }
  22. else
  23. {
  24. ed.WriteMessage("\nNesne {0} uygulamasına
  25. ait XDATA içermiyor.",                       RegAppName);
  26. }
  27. }
  28. }
  29. }

Seçilen bir profilin uygulama adıyla kayıtlı XData'sını ise GetXData() metodu ile AutoCAD komut satırına kolayca yazdırabiliriz:

C:
  1. public static void GetXData(string RegAppName)
  2. {
  3. Document doc = Application.DocumentManager.MdiActiveDocument;
  4. Database db = doc.Database;
  5. Editor ed = doc.Editor;
  6. PromptEntityOptions opt = new PromptEntityOptions("\nIEP profil seçin: ");
  7. PromptEntityResult res = ed.GetEntity(opt);
  8. if (res.Status == PromptStatus.OK)
  9. {
  10. using (Transaction tr = doc.TransactionManager.StartTransaction())
  11. {
  12. DBObject obj = tr.GetObject(res.ObjectId, OpenMode.ForRead);;
  13. ResultBuffer rb = obj.XData;
  14.  
  15. if (rb == null)
  16. {
  17. ed.WriteMessage("\nNesne XDATA içermiyor.");
  18. }
  19. else
  20. {
  21. int i = 1;
  22. foreach (TypedValue tv in rb)
  23. {
  24. ed.WriteMessage("\nVeri No {0} ->> Tip: {1}, Değer: {2}",
  25. i++, ((DxfCode)tv.TypeCode).ToString(), tv.Value);
  26. }
  27. rb.Dispose();
  28. }
  29. }
  30. }
  31. }

NOD’da veri saklama:

C:
  1. private void AddNodRecord(string NodRecName, ObjectId ObjId)
  2. {
  3. Database db =
  4. Application.DocumentManager.MdiActiveDocument.Database;
  5. using (Transaction tr = db.TransactionManager.StartTransaction())
  6. {
  7. DBDictionary AppDict = null;
  8. DBDictionary NODict =
  9. (DBDictionary)tr.GetObject(db.NamedObjectsDictionaryId,
  10. OpenMode.ForRead);
  11. if (!NODict.Contains(NodRecName))
  12. {
  13. NODict.UpgradeOpen();
  14. AppDict = new DBDictionary();
  15. NODict.SetAt(NodRecName, AppDict);
  16. tr.AddNewlyCreatedDBObject(AppDict, true);
  17. }
  18. AppDict =
  19. (DBDictionary)tr.GetObject(NODict.GetAt(NodRecName),
  20. OpenMode.ForWrite);
  21. DBObject plineObj = tr.GetObject(ObjId, OpenMode.ForRead);
  22. string recName = plineObj.Handle.Value.ToString();
  23. Xrecord rec = new Xrecord();
  24. ResultBuffer rb = new ResultBuffer(
  25. new TypedValue((int)DxfCode.Real, b),
  26. new TypedValue((int)DxfCode.Real, h),
  27. new TypedValue((int)DxfCode.Real, t),
  28. new TypedValue((int)DxfCode.Real, s),
  29. new TypedValue((int)DxfCode.Real, r)
  30. );
  31. rec.Data = rb;
  32. AppDict.SetAt(recName, rec);
  33. tr.AddNewlyCreatedDBObject(rec, true);
  34. tr.Commit();
  35. rb.Dispose();
  36. }
  37. }

Kod listesini yukarda bulabileceğiniz AddNodRecord() metodu, öncelikle AutoCAD NOD'una ulaşarak uygulama sözlüğünün (AppDict) NOD'a kayıtlı olup olmadığını (DBDictionary.Contains() metodu) kontrol edecek, kayıtlı değilse yeni sözlüğü oluşturup NOD'a ekleyecek ve Polyline'ın kimliğinden benzersiz bir kayıt anahtarı (recName) yaratarak profil bilgilerini NOD'a (AppDict) kaydedecektir.

NOD'u ve NOD kaydını silen metotlar ise sırasıyla şöyle:

C:
  1. public static void RemoveNod(string NodName)
  2. {
  3. Document doc = Application.DocumentManager.MdiActiveDocument;
  4. Database db = doc.Database;
  5. Editor ed = doc.Editor;
  6. using (Transaction tr = db.TransactionManager.StartTransaction())
  7. {
  8. DBDictionary NODict =
  9. (DBDictionary)tr.GetObject(db.NamedObjectsDictionaryId,
  10. OpenMode.ForWrite);
  11.  
  12. if (NODict.Contains(NodName))
  13. {
  14. NODict.Remove(NodName);
  15. tr.Commit();
  16. ed.WriteMessage("\"{0}\" sözlüğü başarıyla silindi.", NodName);
  17. }
  18. }
  19. }
  20.  
  21. public static void RemoveNodRecord(string NodRecName)
  22. {
  23. Document doc = Application.DocumentManager.MdiActiveDocument;
  24. Database db = doc.Database;
  25. Editor ed = doc.Editor;
  26. PromptEntityOptions opt =
  27. new PromptEntityOptions("\nNOD'dan IEP profili seçin: ");
  28. PromptEntityResult res = ed.GetEntity(opt);
  29. if (res.Status == PromptStatus.OK)
  30. {
  31. using (Transaction tr = doc.TransactionManager.StartTransaction())
  32. {
  33. DBObject obj =
  34. tr.GetObject(res.ObjectId, OpenMode.ForRead);
  35. DBDictionary NODict =
  36. (DBDictionary)tr.GetObject(db.NamedObjectsDictionaryId,
  37. OpenMode.ForRead);
  38. if (!NODict.Contains(NodRecName))
  39. {
  40. ed.WriteMessage("\nNOD bulunamadı.");
  41. }
  42. else
  43. {
  44. DBDictionary AppDict =
  45. (DBDictionary)tr.GetObject(NODict.GetAt(NodRecName),
  46. OpenMode.ForWrite);
  47. NODict.Dispose();
  48. string kayitAd = obj.Handle.Value.ToString();
  49. if (!AppDict.Contains(kayitAd))
  50. {
  51. ed.WriteMessage("\nSeçilen nesne bir profil bilgisi içermiyor.");
  52. }
  53. else
  54. {
  55. AppDict.Remove(kayitAd);
  56. tr.Commit();
  57. ed.WriteMessage("\nProfil bilgisi NOD'dan başarıyla temizlendi.");
  58. }
  59. }
  60. }
  61. }
  62. }

Kimliği AppDic sözlüğüne kaydedilmiş bir nesne grafik ekrandan silindiğinde, tahmin edebileceğiniz gibi, bu nesneye ait kayıt AppDict sözlüğünden silinmeyecektir. AutoCAD.NET API'sinin, grafik olarak bir karşılığı kalmayan bu tür kayıtlardan korunmak için NOD ile ilişkilendirilmiş nesneler silindiğinde sözlükleri uyaran reaktörleri vardır. API, uygulama sözlüğüne kayıt eklemek için DBDictionary.SetAt() metodu çağrıldığında, sözlüğü nesnenin sahibi olarak kabul edip nesneye persistent reactor olarak tutturur. Bu ayrıntıyı da vurguladıktan sonra NOD kaydının nasıl elde edileceğini gösteren GetNodRecord() metodunu listeleyerek ExtensionDictionary kullanımına geçelim:

C:
  1. public static void GetNodRecord(string NodRecName)
  2. {
  3. Document doc = Application.DocumentManager.MdiActiveDocument;
  4. Database db = doc.Database;
  5. Editor ed = doc.Editor;
  6. PromptEntityOptions opt = new PromptEntityOptions("\nIEP profil seçin: ");
  7. PromptEntityResult res = ed.GetEntity(opt);
  8. if (res.Status == PromptStatus.OK)
  9. {
  10. using (Transaction tr = doc.TransactionManager.StartTransaction())
  11. {
  12. DBObject obj =
  13. tr.GetObject(res.ObjectId, OpenMode.ForRead);
  14. DBDictionary NODict =
  15. (DBDictionary)tr.GetObject(db.NamedObjectsDictionaryId,
  16. OpenMode.ForRead);
  17. if (!NODict.Contains(NodRecName))
  18. {
  19. ed.WriteMessage("\nSözlük kaydı bulunamadı.");
  20. }
  21. else
  22. {
  23. DBDictionary AppDict =
  24. (DBDictionary)tr.GetObject(NODict.GetAt(NodRecName),
  25. OpenMode.ForRead);
  26. string recName = obj.Handle.Value.ToString();
  27. if (!AppDict.Contains(recName))
  28. {
  29. ed.WriteMessage("\nSeçilen nesne profil bilgisi içermiyor.");
  30. }
  31. else
  32. {
  33. ObjectId recId = AppDict.GetAt(recName);
  34. Xrecord rec = (Xrecord)tr.GetObject(recId, OpenMode.ForRead);
  35. ResultBuffer rb = rec.Data;
  36.  
  37. if (rb == null)
  38. {
  39. ed.WriteMessage("\nProfil bilgileri bulunamadı.");
  40. }
  41. else
  42. {
  43. int i = 1;
  44. foreach (TypedValue tv in rb)
  45. {
  46. ed.WriteMessage("\nNo {0} ->> Tip: {1}, Değer: {2}",
  47. i++, ((DxfCode)tv.TypeCode).ToString(), tv.Value);
  48. }
  49. rb.Dispose();
  50. }
  51. }
  52. }
  53. }
  54. }
  55. }

ExtensionDictionary’de veri saklama:

C:
  1. private void AddExtEntDict(string ExtDictName, ObjectId ObjId)
  2. {
  3. Database db = Application.DocumentManager.MdiActiveDocument.Database;
  4. using (Transaction tr = db.TransactionManager.<