Использование словарных записей
Понадобилось мне тут разобраться с возможностями записи, чтения и изменения словарных записей на графические примитивы. Раньше все было просто - я использовал 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 символов генерировали ошибку.
Внимательное рассмотрение 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
Ну и так далее. Учитывая, что группы могут повторяться, этого по идее должно быть достаточно. Кому мало - идем в справку или ставим эксперименты
1. как вариант, почему не организовать список списков как словари в словарях
2. в Xrecord жеж вроде можно список влить, на мой взгляд даже предпочтительней
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
(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
Спасибо, возьму на вооружение
реанимирую немножко тему
Столкнулся с "недостатком" работы 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 - еще тот вопрос. Ну, например, в качестве библиотеки, которую можно пользовать в разных случаях. Думаю, исправить это нельзя. Только принять к сведению.
Сугубо ИМХО:
1. Записывать указатель на объект бесполезно. ObjectID меняется от сессии к сесии, лучше бы использовать Handle. Правда, в таком случае возникает вопрос отслеживания корректности данных
- при копировании-вставке через буфер,
- после выполнения _.wblock
- при работе через xref
2. По результатам
нормальной работы не видать
Я сильно подозреваю, что это ограничение следующего порядка: ObjectID (черт с ним, будем с ним воевать) - все же целое число, и записывается оно с использованием разрядности AutoCAD. Т.е. в х64 будет оно Int64. А лисп как был 32-битным, так и остался. Соответственно "лишние" разряды обрубаются.
Может быть, проблема кроется в неверной обработке получения ObjectID: http://autolisp.ru/2011/07/07/x32x64objectid/.
Сейчас времени на проведение экспериментов нет, сорри.
... (-2 . )... там имя объекта (должно было быть), его парсер при отправке сообщения уничтожил
Да, предположение, именно, на неверную обработку ObjectID, но здесь она внутри vla-GetXrecordData, так что, забыть придется весь метод целиком. Буду прикручивать Handle, он там лучше будет смотреться.
Здравствуйте!
Возникает следующая ошибка!
ошибка: lisp-значение не может быть приведено к данному типу ВАРИАНТА:....
Для работы с переменными кое чего изменил...
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
(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 'eval (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[cc lang="lisp"]
Была задача - хранить в Х-записях чертежа определенную информацию.
В процессе работы считывать ее, записывать снова.
Информация представлена в виде списка, типа '((a1 b1 ... n1) ... (an bn ... nn)).
По представленным кодам так и не смог разместить ее.
Да простые списки добавляются без проблем, но необходимые мне - не получается.
Вопрос - возможно ли размещение подобных списков или стоит искать другой вариант работы с данными?
У меня бональная хотелка:
писать/читать/изменять информацию в примитивы на постоянном хранении в чертеже.
Раньше пользовался атрибутами - надоело.
Как пользоваться X записями понимаю не до конца.
Кто поможет готовыми функциями? - писать/читать/изменять
Андрей, я не сохранял в словарях сложноструктурированные списки. По-моему, подобное нереализуемо "в лоб". Можно создавать несколько словарей, можно еще как-то выкрутиться - но не записать "список в списке".
Хотя нет, вру... Среди кодов Alaspher'a (лежат где-то в Download на dwg.ru) что-то было по поводу записей в словари, если не ошибаюсь.
Евгений, так коды представлены - в чем собственно трудность?