Использование словарных записей

Понадобилось мне тут разобраться с возможностями записи, чтения и изменения словарных записей на графические примитивы. Раньше все было просто - я использовал vlax-ldata-* функции, механизм был отлажен и прекрасно работал. Расширенные данные (по условиям задачи ;)) исключаются - объем записываемых данных может запросто превысить размер в 16 кб. Ограничение РД как было, так никуда и не делось.

Но вот сейчас встала задача не только записать / изменить / прочитать данные лиспом. Проблема в том, что файл dwg потом попадет на обработку в другую контору. А они уже будут читать эти данные, используя СОМ-доступ.

Ох уж этот СОМ... Если использовать vlax-ldata, то словарь как бы создается, но ни до него, ни до записей черезз СОМ лично мне добраться не удалось. Использовать entget, естественно, нельзя - а это единственный вариант добраться до записей словаря с объектным именем "vlo_vl"...

Значит, придется работать через GetExtensionDictionary и все, что с ним связано.

Сначала получаем указатель на графический примитив:

1
(setq ent (vlax-ename->vla-object (car (entsel))))

Далее получаем указатель на словарь. Даже если его не существовало, он будет создан после выполнения

1
(setq dict (vla-GetExtensionDictionary ent))

Отлично, словарь создан. Добавляем в него запись. Хотя бы одну :)

1
(setq rec (vla-AddXRecord dict "test_dict"))

И остается только добавить в запись данные:

1
2
(vla-SetXRecordData rec (vlax-make-variant (vlax-safearray-fill (vlax-make-safearray vlax-vbInteger '(0 . 2)) '(1 1 1)))
(vlax-make-variant (vlax-safearray-fill (vlax-make-safearray vlax-vbVariant '(0 . 2)) '("str1" "str2" "str3"))))

Тут стоит не забыть про один момент: в качестве кодов можно использовать числа от 1 до 369, исключая 5, 100 и 105. Как интерпретировать данные - вопрос уже отдельный.

Осталось сделать несколько экспериментов:
- попробовать записать в качестве данных массив из variant. Если сработает, то получается, что можно запросто загнать в словарь список списков. Вот только узнать бы ограничения :)
- изменить данные в словаре. Насколько я помню, надо сначала удалить запись (rec), а потом добавлять новую. Проверим...

Для дальнейших экспериментов договоримся о том, что будем использовать в качестве имени словаря "lispru-dict".

Скажу честно - сильно хотелось заодно и разбивку по кодам сделать, то есть для кода 11 будет храниться целое, для кода 12 - число двойной точности и т.д., т.е. использовать нечто типа

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
(vl-load-com)

(defun _lispru-ent-dictionary-add (ent datas / dict rec)
                                  ;|
*    Запись в примитив словарных данных
*    Параметры вызова:
  ent    указатель на примитив (графический или нет - неважно)
  datas  список записываемых данных
*    Примеры вызова:
(_lispru-ent-dictionary-add (car (entsel "\nSelect entity : ")) '("string" 15 nil -16.85))
|;

  (if (setq ent (cond ((= (type ent) 'vla-object) ent)
                      ((= (type ent) 'ename) (vlax-ename->vla-object ent))
                      ) ;_ end of cond
            ) ;_ end of setq
    (progn
      (setq dict  (vla-getextensiondictionary ent)
            rec   (vla-addxrecord dict "lispru-dict")
            datas (mapcar
                    (function
                      (lambda (x)
                        (cons (cond ((= (type x) 'int) 11)
                                    ((= (type x) 'real) 12)
                                    ((or (= x t)
                                         (not x)
                                         (member x (list :vlax-false :vlax-true))
                                         ) ;_ end of or
                                     13
                                     )
                                    ((= (type x) 'safearray) 14)
                                    ((= (type x) 'variant) 15)
                                    ((= (type x) 'str) 16)
                                    (t 90)
                                    ) ;_ end of cond
                              (cond
                                ((or (= x t)
                                     (equal x :vlax-true)
                                     ) ;_ end of or
                                 1
                                 )
                                ((or (not x) (equal x :vlax-false)) 0)
                                (t x)
                                ) ;_ end of cond
                              ) ;_ end of cons
                        ) ;_ end of lambda
                      ) ;_ end of function
                    datas
                    ) ;_ end of mapcar
            ) ;_ end of setq
      (vla-setxrecorddata
        rec
        (vlax-make-variant
          (vlax-safearray-fill
            (vlax-make-safearray vlax-vbinteger (cons 0 (1- (length datas))))
            (mapcar (function car) datas)
            ) ;_ end of vlax-safearray-fill
          ) ;_ end of vlax-make-variant
        (vlax-make-variant
          (vlax-safearray-fill
            (vlax-make-safearray vlax-vbvariant (cons 0 (1- (length datas))))
            (mapcar (function cdr) datas)
            ) ;_ end of vlax-safearray-fill
          ) ;_ end of vlax-make-variant
        ) ;_ end of vla-SetXRecordData
      ) ;_ end of progn
    ) ;_ end of if
  ) ;_ end of defun

но AutoCAD 2009 x64 начал сильно материться на попытки записи таких данных. Ну, то, что AutoCAD не хочет работать "в лоб", не означает невозможности выяснить, под какими кодами какие данные можно загнать :) Описывать свои эксперименты не буду, пока что для простых типов данных удалось выяснить следующее:

Код Тип хранимого объекта
1 .. 9 Строка
38 .. 59 Число двойной точности
60 .. 99 Целое
101 .. 102 Строка
140 .. 149 Целое
170 .. 299 Строка
300 .. 329 Строка

Ну или немного в другой форме:

Код Тип хранимого объекта
1 .. 9
101 .. 102
170 .. 299
300 .. 329
Строка
38 .. 59 Число двойной точности
60 .. 99
140 .. 149
Целое

Учитывая это, получается (для начала):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
(vl-load-com)

(defun _lispru-ent-dictionary-add (ent datas / dict rec)
                                  ;|
*    Запись в примитив словарных данных
*    Параметры вызова:
  ent    указатель на примитив (графический или нет - неважно)
  datas  список записываемых данных
*    Примеры вызова:
(_lispru-ent-dictionary-add (car (entsel "\nSelect entity : ")) '("string" 15 nil -16.85))
|;

  (if (setq ent (cond ((= (type ent) 'vla-object) ent)
                      ((= (type ent) 'ename) (vlax-ename->vla-object ent))
                      ) ;_ end of cond
            ) ;_ end of setq
    (progn
      (setq dict  (vla-getextensiondictionary ent)
            rec   (vla-addxrecord dict "lispru-dict")
            datas (mapcar
                    (function
                      (lambda (x)
                        (cons 1 ;_ end of cond
                              (cond
                                ((not x) "'0")
                                ((= x t) "'1")
                                (t x)
                                ) ;_ end of cond
                              ) ;_ end of cons
                        ) ;_ end of lambda
                      ) ;_ end of function
                    datas
                    ) ;_ end of mapcar
            ) ;_ end of setq
      (vla-setxrecorddata
        rec
        (vlax-make-variant
          (vlax-safearray-fill
            (vlax-make-safearray vlax-vbinteger (cons 0 (1- (length datas))))
            (mapcar (function car) datas)
            ) ;_ end of vlax-safearray-fill
          ) ;_ end of vlax-make-variant
        (vlax-make-variant
          (vlax-safearray-fill
            (vlax-make-safearray vlax-vbvariant (cons 0 (1- (length datas))))
            (mapcar (function cdr) datas)
            ) ;_ end of vlax-safearray-fill
          ) ;_ end of vlax-make-variant
        ) ;_ end of vla-SetXRecordData
      ) ;_ end of progn
    ) ;_ end of if
  ) ;_ end of defun

В финале добавим сортировку по ключам, чтобы работало нормально, и все :)

Для проверки корректности записи напишем другую программку:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
(defun _lispru-ent-dictionary-read (ent / xd xt)
                                   ;|
*    Функция чтения словарных данных из примитива
*    Параметры вызова:
(_lispru-ent-dictionary-read (car (entsel "\nSelect entity : ")))
|;

  (if
    (and (setq ent (cond ((= (type ent) 'vla-object) ent)
                         ((= (type ent) 'ename) (vlax-ename->vla-object ent))
                         ) ;_ end of cond
               ) ;_ end of setq
         (equal (vla-get-hasextensiondictionary ent) :vlax-true)
         (not
           (vl-catch-all-error-p
             (setq
               rec
                (vl-catch-all-apply
                  (function
                    (lambda ()
                      (vla-item (vla-getextensiondictionary ent) "lispru-dict")
                      ) ;_ end of lambda
                    ) ;_ end of function
                  ) ;_ end of vl-catch-all-apply
               ) ;_ end of setq
             ) ;_ end of vl-catch-all-error-p
           ) ;_ end of not
         ) ;_ end of and
     (progn
       (vla-getxrecorddata rec 'xd 'xt)
       (mapcar
         (function
           (lambda (a)
             (mapcar
               (function
                 (lambda (x)
                   (cond
                     ((= (type x) 'variant)
                      (vlax-variant-value x)
                      )
                     (t x)
                     ) ;_ end of cond
                   ) ;_ end of lambda
                 ) ;_ end of function
               (vlax-safearray->list a)
               ) ;_ end of mapcar
             ) ;_ end of lambda
           ) ;_ end of function
         (list xd xt)
         ) ;_ end of mapcar
       ) ;_ end of progn
     ) ;_ end of if
  ) ;_ end of defun

Данные записываются, все здорово. А если повторно вызвать код, да еще и данные другие засунуть...

Проведение экспериментов показало, что данные полностью заменяются. Но, блин, как и куда закидывать сложные списки - пока не представляю! :( Другой вопрос, что для текущих задач хватает и того, что уже есть, но, блин, хочется сделать нормальный вариант :)

P.S. Опять же, несколько экспериментов доказали, что количество групп с одинаковым кодом может быть любым. По крайней мере упереться в ограничение мне не удалось. Кроме одного - при некоторых условиях попытки сохранить строки длиной более 255 символов генерировали ошибку.

Размещено в Код LISP, Новости · Метки: ,



Комментарии

Есть 6 коммент. к “Использование словарных записей”
  1. Кулик Алексей aka kpblc пишет:

    Внимательное рассмотрение DXF Reference показало интересные вещи (учитывая ограничения XRecord):
    DXF Reference -> DXF Format -> Group Codes in Numerical Order:
    1,3,4 : Строка
    10-18 : координаты точек
    40-49 : числа двойной точности
    50-58 : положительные числа двойной точности, не превышающие (* 2 pi) - эти группы отвечают за хранение значений углов.
    60 : по идее булево значение (1 / 0)
    62 : положительное целое число от 1 до 255
    70-78 : неотрицательные целые числа
    90-99 : целые числа типа Int32
    Ну и так далее. Учитывая, что группы могут повторяться, этого по идее должно быть достаточно. Кому мало - идем в справку или ставим эксперименты :)

  2. Евгений пишет:

    1. как вариант, почему не организовать список списков как словари в словарях
    2. в Xrecord жеж вроде можно список влить, на мой взгляд даже предпочтительней

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    (defun kd:setxrecdata (xrec val / sa1 sa2)
      (if (listp val)
        (progn
          (setq sa1 (vlax-safearray-fill
          (vlax-make-safearray vlax-vbInteger (cons 0  (1- (length val))))
          (mapcar '(lambda (x) (cond
                 ((= (type x) 'STR) 301)
                 ((= (type x) 'INT) 451)
                 ((= (type x) 'REAL) 140)
                 )
               ) val)))
          (setq sa2 (vlax-safearray-fill
        (vlax-make-safearray vlax-vbvariant (cons 0 (1- (length val))))
        val))
          (vla-SetXRecordData xrec sa1 sa2)
          )
        (princ "\nkd:setxrecdata : value must be a list")
        )
      );defun
  3. Кулик Алексей aka kpblc пишет:

    Спасибо, возьму на вооружение :)

  4. andrew.makhov пишет:

    реанимирую немножко тему :)
    Столкнулся с "недостатком" работы vla-GetXrecordData на 64 битных системах. Если нужен dbx, то нужен vla-SetXrecordData, а коли так, то и читать хочется с помощью vla-GetXrecordData. Но не тут-то было. Хотим записать в XRECORD указатель на объект. Пишем (с кодом, например, -2, и преобразованием (vlax-make-variant (vlax-ename->vla-object x)) ) с помощью vla-SetXrecordData. Все хорошо. ((-1 . ) (0 . XRECORD) (5 . 123F) (102 . {ACAD_REACTORS) (330 . ) (102 . }) (330 . ) (100 . AcDbXrecord) (280 . 1) (-2 . )) Имя объекта формируется правильно.
    Читаем с помощью vla-GetXrecordData и видим, что вместо указателя на объект расположен массив LONG из двух отрицательных чисел. Похоже, это баг.
    Единственное, что нашел в сети по этому поводу - это форум на французском, откуда и понял, что дело в разрядности системы. (http://cadxp.com/topic/38804-safearry-of-long-a-la-place-dun-objectid/)
    Вариант теоретический, как сферический конь в вакууме: зачем нужно именно имя объекта в dbx - еще тот вопрос. Ну, например, в качестве библиотеки, которую можно пользовать в разных случаях. Думаю, исправить это нельзя. Только принять к сведению.

  5. Кулик Алексей aka kpblc пишет:

    Сугубо ИМХО:
    1. Записывать указатель на объект бесполезно. ObjectID меняется от сессии к сесии, лучше бы использовать Handle. Правда, в таком случае возникает вопрос отслеживания корректности данных
    - при копировании-вставке через буфер,
    - после выполнения _.wblock
    - при работе через xref
    2. По результатам

    ((-1 . ) (0 . XRECORD) (5 . 123F) (102 . {ACAD_REACTORS) (330 . ) (102 . }) (330 . ) (100 . AcDbXrecord) (280 . 1) (-2 . ))

    нормальной работы не видать ;)

    Я сильно подозреваю, что это ограничение следующего порядка: ObjectID (черт с ним, будем с ним воевать) - все же целое число, и записывается оно с использованием разрядности AutoCAD. Т.е. в х64 будет оно Int64. А лисп как был 32-битным, так и остался. Соответственно "лишние" разряды обрубаются.

    Может быть, проблема кроется в неверной обработке получения ObjectID: http://autolisp.ru/2011/07/07/x32x64objectid/.

    Сейчас времени на проведение экспериментов нет, сорри.

  6. andrew.makhov пишет:

    ... (-2 . )... там имя объекта (должно было быть), его парсер при отправке сообщения уничтожил :)
    Да, предположение, именно, на неверную обработку ObjectID, но здесь она внутри vla-GetXrecordData, так что, забыть придется весь метод целиком. Буду прикручивать Handle, он там лучше будет смотреться. :)

Поделитесь своим мнением


Я не робот.