Author Topic: Create xRecord inside AcDbDictionary (namedobjdict) and add ResultBuffer data  (Read 257 times)

0 Members and 1 Guest are viewing this topic.

Grrr1337

  • Swamp Rat
  • Posts: 660
Hey .NET gurus!  :-)
I have some (vital) request in order to pass some .NET data to LISP via xRecord inside of a dictionary (so it would be .dwg dependent) -

The simpliest way I found to store a LISP data into the dictionaries is this (via LISP (sorry its Vanilla, not Visual)) :
Code - Auto/Visual Lisp: [Select]
  1. ; (IncludeDataIntoMainDic "MyXrec" '("Custom" 2 "data")) << Will Include "MyXrec" xRecord to the "AcDbDictionary"
  2. ; (IncludeDataIntoMainDic "MyXrec" nil) << Will Erase the "MyXrec" xRecord, (if theres one) from the "AcDbDictionary"
  3. ; xRecName - xrecord name
  4. ; dataL - basically any type of data (usually its a list) if nil, then the xrecord will be deleted.
  5. ; http://www.theswamp.org/index.php?topic=5003.0
  6. (defun IncludeDataIntoMainDic ( xRecName dataL / maindic xrec )
  7.  (cond
  8.    ( (not (eq 'STR (type xRecName))) (prompt "\nxRecName is not STR type.") )
  9.    ( (vl-some (function (lambda (x) (wcmatch (strcase xRecName) x))) '("ACAD*" "AEC*" "Ac*")) ; Being paranoid
  10.      (prompt "\nInvalid xRecord name.")
  11.    )
  12.    (t
  13.      (setq maindic (namedobjdict))
  14.      (if (setq xrec (dictsearch maindic xRecName)) (entdel (cdr (assoc -1 xrec))))
  15.      (if dataL (dictadd maindic xRecName (entmakex (append '((0 . "XRECORD") (100 . "AcDbXrecord")) (list (cons 1 (vl-prin1-to-string dataL)))))))
  16.    ); t
  17.  ); cond
  18. ); defun IncludeDataIntoMainDic

Code - Auto/Visual Lisp: [Select]
  1. ; (GetDataFromMainDic "MyXrec")
  2. ; http://www.theswamp.org/index.php?topic=5003.0
  3. (defun GetDataFromMainDic ( xRecName / tmp r )
  4.  (and
  5.    (setq tmp (dictsearch (namedobjdict) xRecName))
  6.    (setq tmp (cdr (assoc 1 tmp)))
  7.    (setq r (read tmp))
  8.  ); and
  9.  r
  10. ); defun GetDataFromMainDic

But I have no idea how to translate the above algorithm to C# as methods (described with comments) :
Code - C#: [Select]
  1. [LispFunction("IncludeDataIntoMainDic")]
  2. public static void IncludeDataIntoMainDic ( string xRecordName, ResultBuffer ResBuf )
  3. {
  4.  // remove xRecord with xRecordName if theres existing one (from the main dictionary)
  5.  if (ResBuf)
  6.  {
  7.    // then create new xRecord with the xRecordName
  8.    // and somehow include the ResultBuffer to it
  9.  }
  10. }
  11.  
  12. [LispFunction("GetDataFromMainDic")]
  13. public static ResultBuffer GetDataFromMainDic ( string xRecordName )
  14. {
  15.  // obtain the xrecord and somehow return the first DXF code with key 1, as a new ResultBuffer
  16.  // return ResBuf; <- Should Match the 'ResBuf' from the 'IncludeDataIntoMainDic' method
  17. }
  18. // NOTE: if its not possible to append 'ResultBuffer' as data to the Xrecord, then any type of data that returns itself would do it:
  19. // IncludeDataIntoMainDic("TheXrecord" <any data>);
  20. // GetDataFromMainDic("TheXrecord"); >> should return <any data>

NOTE:
Its all about creating/removing a Xrecord object/class? from the AcDbDictionary class (I guess).
Including custom data to the Xrecord (in my case its a string that I dis-evaluate and re-evaluate)
Code - Auto/Visual Lisp: [Select]
  1. _$ (vl-prin1-to-string '("Custom" 2 "data")) >> "(\"Custom\" 2 \"data\")"
  2. _$ (read "(\"Custom\" 2 \"data\")") >> ("Custom" 2 "data")

Sorry in advance that I request and don't simply ask ( I'm still overwhelmed by the volume of .NET ) - will try to study your solution instead.
Thanks in advance!
(apply ''((a b c)(a b c))
  '(
    (( f L ) (apply 'strcat (f L)))
    (( L ) (if L (cons (chr (car L)) (f (cdr L)))))
    (72 101 108 108 111 32 87 111 114 108 100)
  )
)

dgorsman

  • Water Moccasin
  • Posts: 2355
Backing up a couple of steps, with a couple of questions - why not create a method to define a LISP function which gets the data directly?  Is it really necessary to store this data for later use, or is this just a method for transferring data?

Extension dictionaries and xrecords are no different whether you are doing C# or LISP.  It's a name-indexed, hierarchical storage system much like XML elements or even the folder system in Windows Explorer.  Dictionaries can contain other Dictionaries as well as xrecords, each of which can be accessed by name or by iteration.  Xrecords contain a non-structured list of data (through a ResultBuffer in dotNET or an assoc-list of dotted pairs in LISP).
If you are going to fly by the seat of your pants, expect friction burns.

try {GreatPower;}
   catch (notResponsible)
      {NextTime(PlanAhead);}
   finally
      {MasterBasics;}

Grrr1337

  • Swamp Rat
  • Posts: 660
Backing up a couple of steps, with a couple of questions - why not create a method to define a LISP function which gets the data directly?  Is it really necessary to store this data for later use, or is this just a method for transferring data?

Good question, main reason is that the data is stored and shared for processing with any language
In other words transfering/modifying data from the same 'place' that its easy obtainable/assignable/manipulatable via multiple languages (but you have to be good enough atleast at one, ofcourse).
So one could write his own custom function/method in his language, and use the data from there as arguments for the function/method to process (and even rewrite).

A practical reason would be to create a "to-do-list" for every individual .dwg file (so the data represents some notes for the building project).
Writing a dialog with LISP+DCL that should simulate a multiline textbox, results in a quite user-unfriendly result (demo).
But thank god .NET and your help .NET guys that make the prompting like it should be (its like switching from east to west europe).
As you can see in the 2nd demo it uses the approach you suggested, then uses the above LISP functions to store and reveal the data.

Last reason is just curiosity - with lisp I could access/modify the data, but IDK how its done in C#, so I could bridge the languages from there.



Extension dictionaries and xrecords are no different whether you are doing C# or LISP.  It's a name-indexed, hierarchical storage system much like XML elements or even the folder system in Windows Explorer.  Dictionaries can contain other Dictionaries as well as xrecords, each of which can be accessed by name or by iteration.  Xrecords contain a non-structured list of data (through a ResultBuffer in dotNET or an assoc-list of dotted pairs in LISP).

Unfortunately I'm not familiar which class of which library to explore, in order to create an Xrecord.. and perhaps calling the method correctly to asign the data.
So was looking for an example.
(apply ''((a b c)(a b c))
  '(
    (( f L ) (apply 'strcat (f L)))
    (( L ) (if L (cons (chr (car L)) (f (cdr L)))))
    (72 101 108 108 111 32 87 111 114 108 100)
  )
)

Atook

  • Swamp Rat
  • Posts: 914
You might find something useful in this:
Code - C#: [Select]
  1. /// <summary>
  2. /// Add a record to the named object dictionary. If the dictionary doesn't exist, create it
  3. /// </summary>
  4. /// <param name="dictionaryName"></param>
  5. /// <param name="key"></param>
  6. /// <param name="data"></param>
  7. public static void AddXrecordToNamedDictionary(string dictionaryName, string key, ResultBuffer data)
  8. {
  9.  if (Active.Document == null) return;
  10.  try
  11.  {
  12.    using (Document.LockDocument())
  13.    {
  14.      using (Transaction tr = Active.Document.TransactionManager.StartTransaction())
  15.      {
  16.        DBDictionary nod = tr.GetObject(Active.Database.NamedObjectsDictionaryId,
  17.          OpenMode.ForWrite) as DBDictionary;
  18.        if (nod == null) return;
  19.        DBDictionary myDbDictionary = new DBDictionary();
  20.        if (nod.Contains(dictionaryName))
  21.        {
  22.          // set the dictionary
  23.          myDbDictionary = (DBDictionary)tr.GetObject(nod.GetAt(dictionaryName), OpenMode.ForWrite);
  24.        }
  25.        else
  26.        {
  27.          // finish creating the dictionary
  28.          nod.SetAt(dictionaryName, myDbDictionary);
  29.          tr.AddNewlyCreatedDBObject(myDbDictionary, true);
  30.        }
  31.  
  32.        if (myDbDictionary.Contains(key))
  33.        {
  34.          //  edit the existing xrecord
  35.          var myXRec = (Xrecord)tr.GetObject(myDbDictionary.GetAt(key), OpenMode.ForWrite);
  36.          myXRec.Data = data;
  37.        }
  38.        else
  39.        {
  40.          // add the xrecord
  41.          Xrecord newXRec = new Xrecord() { Data = data };
  42.          myDbDictionary.SetAt(key, newXRec);
  43.          tr.AddNewlyCreatedDBObject(newXRec, true);
  44.        }
  45.        tr.Commit();
  46.      }
  47.    }
  48.  }
  49.  catch (Exception e)
  50.  {
  51.    AID_Application.HandleError(e);
  52.  }
  53. }
  54.  
  55.  
  56.  

Grrr1337

  • Swamp Rat
  • Posts: 660
You might find something useful in this:

Thank you, Atook!

But what TypedValue-s you are including to the ResultBuffer, so the xRecord would use them as data ?

I tried bunch of ways /aswell 1 key for string type, and 40 for int type.. i.e. '((1 . "Hello")(1 . "World")(40 . 888)) / :

Code - C#: [Select]
  1. AddXrecordToNamedDictionary
  2. ("CsharpDictionary", "xRecordKey", new ResultBuffer
  3.  (
  4.    new TypedValue((int)LispDataType.ListBegin),
  5.    new TypedValue((int)LispDataType.ListBegin),
  6.    new TypedValue((int)LispDataType.Int32, 8),
  7.    new TypedValue((int)LispDataType.Text, "Hello"),
  8.    new TypedValue((int)LispDataType.ListEnd),
  9.    new TypedValue((int)LispDataType.ListBegin),
  10.    new TypedValue((int)LispDataType.Int32, 8),
  11.    new TypedValue((int)LispDataType.Text, "World"),
  12.    new TypedValue((int)LispDataType.ListEnd),
  13.    new TypedValue((int)LispDataType.ListEnd)
  14.  )
  15. );
  16.  
  17. AddXrecordToNamedDictionary
  18. ("CsharpDictionary2", "xRecordKey2", new ResultBuffer
  19.  (
  20.    new TypedValue((int)LispDataType.ListBegin),
  21.    new TypedValue((int)LispDataType.ListBegin),
  22.    new TypedValue((int)LispDataType.Int32, 8),
  23.    new TypedValue((int)LispDataType.Text, "Hello"),
  24.    new TypedValue((int)LispDataType.ListEnd),
  25.    new TypedValue((int)LispDataType.ListBegin),
  26.    new TypedValue((int)LispDataType.Int32, 8),
  27.    new TypedValue((int)LispDataType.Text, "World"),
  28.    new TypedValue((int)LispDataType.ListEnd),
  29.    new TypedValue((int)LispDataType.ListBegin),
  30.    new TypedValue((int)LispDataType.Int32, 2),
  31.    new TypedValue((int)LispDataType.Int32, 888),
  32.    new TypedValue((int)LispDataType.ListEnd),
  33.    new TypedValue((int)LispDataType.ListEnd)
  34.  )
  35. );
  36.  
  37. AddXrecordToNamedDictionary
  38. ("CsharpDictionary3", "xRecordKey3", new ResultBuffer
  39.  (
  40.    new TypedValue((int)LispDataType.Text, "Hello"),
  41.    new TypedValue((int)LispDataType.Text, "World")
  42.  )
  43. );

But I always get eInvalidDxfCode :

Code: [Select]
Source: Acdbmgd
Message: eInvalidDxfCode
Data: System.Collections.ListDictionaryInternal

for catching:
Code - C#: [Select]
  1. catch (Autodesk.AutoCAD.Runtime.Exception e)
  2. {
  3.  Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage
  4.  (
  5.    "Source: " + e.Source +
  6.    "\nMessage: " + e.Message +
  7.    "\nData: " + e.Data
  8.  );
  9. }
(apply ''((a b c)(a b c))
  '(
    (( f L ) (apply 'strcat (f L)))
    (( L ) (if L (cons (chr (car L)) (f (cdr L)))))
    (72 101 108 108 111 32 87 111 114 108 100)
  )
)

MickD

  • Gator
  • Posts: 3223
  • !false...it's funny 'cause it's true!
Have a look into DxfCode enum's rather than lisp data types. Also, you don't need to create lists, here's an example from the doc's:
https://knowledge.autodesk.com/search-result/caas/CloudHelp/cloudhelp/2017/ENU/AutoCAD-NET/files/GUID-A43BA3F1-513E-42E5-A21F-633FAF97B5C9-htm.html

<edit>
I just realised you may need to store your data in a listed or 'tree' like structure. If you like you can store this info as an xml string instead, I do this for my app/object external data and I only need to store one TypedValue. If your xml data gets large you can use Daniel's StringToResultBuffer code which you can find on the board with a quick search. This compresses the info to save space and works well.
« Last Edit: November 23, 2018, 09:38:52 PM by MickD »
A programmer's wife tells him, "Run to the store and pick up a loaf of bread. If they have eggs, get a dozen."

The programmer comes home with 12 loaves of bread.

Atook

  • Swamp Rat
  • Posts: 914
Oh... if XML storage is what you're looking for I like MickD's idea of storing a single ResultBuffer.. I might take that one for myself if I need it!

I store database objects in the NOD. Here's my call to the function I posted earlier to store a sprinlker object:
Code - C#: [Select]
  1. Active.AddXrecordToNamedDictionary(AID_Strings.SprinkDictionary, spk.ID, spk.XRecordData());

And here's the XRecordData of the sprinkler object:
Code - C#: [Select]
  1. /// <summary>
  2. /// Returns a TypedValueList meant to be stored as xdata on an XRecord in a dictionary
  3. /// NOT to to be written as Xdata to a block, there is no APPNAME at [0]
  4. /// </summary>
  5. public TypedValueList XRecordData()
  6. {
  7.  TypedValueList result = new TypedValueList(
  8.    new TypedValue((int)DxfCode.Text, ID),
  9.    new TypedValue((int)DxfCode.Text, BlockName),
  10.    new TypedValue((int)DxfCode.Real, Pressure),
  11.    new TypedValue((int)DxfCode.Real, Radius),
  12.    new TypedValue((int)DxfCode.Real, Flow),
  13.    new TypedValue((int)DxfCode.Real, PlotSize));
  14.  
  15.  return result;
  16. }
  17.  

And here's Tony's TypedValueList class, also highly recommended.
Code - C#: [Select]
  1. public class TypedValueList : List<TypedValue>
  2. {
  3.  // With thanks to Tony Tanzillo
  4.  // http://www.theswamp.org/index.php?topic=14495.msg186823#msg186823
  5.  //
  6.  public TypedValueList(params TypedValue[] args)
  7.  {
  8.    AddRange(args);
  9.  }
  10.  
  11.  // Make it a bit easier to add items:
  12.  
  13.  public void Add(int typecode, object value)
  14.  {
  15.    base.Add(new TypedValue(typecode, value));
  16.  }
  17.  public void Add(LispDataType type, object value)
  18.  {
  19.    Add(new TypedValue((int)type, value));
  20.  }
  21.  public void Add(DxfCode code, object value)
  22.  {
  23.    Add(new TypedValue((int)code, value));
  24.  }
  25.  
  26.  // Implicit conversion to SelectionFilter
  27.  public static implicit operator SelectionFilter(TypedValueList src)
  28.  {
  29.    return src != null ? new SelectionFilter(src) : null;
  30.  }
  31.  
  32.  // Implicit conversion to ResultBuffer
  33.  public static implicit operator ResultBuffer(TypedValueList src)
  34.  {
  35.    return src != null ? new ResultBuffer(src) : null;
  36.  }
  37.  
  38.  // Implicit conversion to TypedValue[]
  39.  public static implicit operator TypedValue[] (TypedValueList src)
  40.  {
  41.    return src != null ? src.ToArray() : null;
  42.  }
  43.  
  44.  // Implicit conversion from TypedValue[]
  45.  public static implicit operator TypedValueList(TypedValue[] src)
  46.  {
  47.    return src != null ? new TypedValueList(src) : null;
  48.  }
  49.  
  50.  // Implicit conversion from SelectionFilter
  51.  public static implicit operator TypedValueList(SelectionFilter src)
  52.  {
  53.    return src != null ? new TypedValueList(src.GetFilter()) : null;
  54.  }
  55.  
  56.  // Implicit conversion from ResultBuffer
  57.  public static implicit operator TypedValueList(ResultBuffer src)
  58.  {
  59.    return src != null ? new TypedValueList(src.AsArray()) : null;
  60.  }
  61. }
  62.  

Hope this helps!
« Last Edit: November 24, 2018, 02:29:30 AM by Atook »

Grrr1337

  • Swamp Rat
  • Posts: 660
Have a look into DxfCode enum's rather than lisp data types. Also, you don't need to create lists, here's an example from the doc's:
https://knowledge.autodesk.com/search-result/caas/CloudHelp/cloudhelp/2017/ENU/AutoCAD-NET/files/GUID-A43BA3F1-513E-42E5-A21F-633FAF97B5C9-htm.html

Thanks Mick!
So I had to use the DxfCode enumerable, located in the DatabaseServices namespace.

A bit confused why they didn't put these enums in the same namespace, but I'll get used to it
Code: [Select]
Autodesk.AutoCAD.DatabaseServices.DxfCode
Autodesk.AutoCAD.Runtime.LispDataType


<edit>
I just realised you may need to store your data in a listed or 'tree' like structure. If you like you can store this info as an xml string instead, I do this for my app/object external data and I only need to store one TypedValue. If your xml data gets large you can use Daniel's StringToResultBuffer code which you can find on the board with a quick search. This compresses the info to save space and works well.

Not necessary, I'm just trying to start exploring with something simple, although it seems quite useful. Found it BTW. :)



I store database objects in the NOD. Here's my call to the function I posted earlier to store a sprinlker object:
Code - C#: [Select]
  1. Active.AddXrecordToNamedDictionary(AID_Strings.SprinkDictionary, spk.ID, spk.XRecordData());

And here's the XRecordData of the sprinkler object:
Code - C#: [Select]
  1. /// <summary>
  2. /// Returns a TypedValueList meant to be stored as xdata on an XRecord in a dictionary
  3. /// NOT to to be written as Xdata to a block, there is no APPNAME at [0]
  4. /// </summary>
  5. public TypedValueList XRecordData()
  6. {
  7.  TypedValueList result = new TypedValueList(
  8.    new TypedValue((int)DxfCode.Text, ID),
  9.    new TypedValue((int)DxfCode.Text, BlockName),
  10.    new TypedValue((int)DxfCode.Real, Pressure),
  11.    new TypedValue((int)DxfCode.Real, Radius),
  12.    new TypedValue((int)DxfCode.Real, Flow),
  13.  new TypedValue((int)DxfCode.Real, PlotSize));
  14.  
  15.  return result;
  16. }
  17.  

And here's Tony's TypedValueList class, also highly recommended.

Code - C#: [Select]
  1. public class TypedValueList : List<TypedValue>
  2. {
  3.  // With thanks to Tony Tanzillo
  4.  // http://www.theswamp.org/index.php?topic=14495.msg186823#msg186823
  5.  ...
  6. }
  7.  

Hope this helps!

Thanks Atook, you helped alot! :)


Have a nice weekend guys!
(apply ''((a b c)(a b c))
  '(
    (( f L ) (apply 'strcat (f L)))
    (( L ) (if L (cons (chr (car L)) (f (cdr L)))))
    (72 101 108 108 111 32 87 111 114 108 100)
  )
)