Форум пользователей ПО АСКОН

Профессиональные вопросы => Программирование приложений => Delphi => Тема начата: andno от 19.01.06, 15:59:36

Название: Доступ к списку файлов внешних ссылок 3D модели
Отправлено: andno от 19.01.06, 15:59:36
Приветствую всех!
Стоит задача получения списка всех файлов внешних ссылок 3D модели.
Это тот список, который доступен после открытия файла a3d через опцию оновного меню Компаса:
Файл -> Свойства-> Закладка "Внешние ссылки".

Пытался добраться до него через интерфейс ksPart.
Однако список получается не полным. Перечисляются только файлы физически прописанные в данном файле a3d.
При открытии же свойств через меню, Компас дает общее перечисление файлов, которые записаны как в файле a3d, так и в файлах внешних ссылок, которые записаны файле a3d.
При этом у меня создается впечатление, что при открытии в меню свойств, Компас не получает этот список заново, а просто выдает уже готовый список (операция проходит очень быстро). Мне кажется, что этот список создается во время открытия файла a3d и сохраняется вплоть до его закрытия.

Вопрос! Как можно добраться до этого списка?
Название: Re:Доступ к списку файлов внешних ссылок 3D модели
Отправлено: IronMaxxx от 19.01.06, 19:18:37
Как добраться до списка не знаю, но можно создать точно такой же свой, своими силами.
При открытом активном документе 3D (сборке) нужно запустить цикл от 0 до количества деталей в сборке и для каждого компонента определять имя файла, т.е. (пример на Delphi) :

var iPart : ksPart;
     doc3D : ksDocument3D;
     kompas : KompasObject;
     i : integer;
     PColl : ksPartCollection;
     PathList : TStringList;
begin
   doc3D := ksDocument3D(kompas.ActiveDocument3D);
   if (doc3D.IsDetail) then exit;

   PColl := ksPartCollection(doc3D.PartCollection(true));
   PathList := TStringList.Create;

   for i := 0 to PColl.GetCount-1 do
      begin
         iPart := ksPart(doc3D.GetPart(i));
         PathList.Add(iPart.fileName);
                           // если среди деталей есть подсборки, для определения их имен нужно
                           // рекурсивно вызвать эту же процедуру
                           // и передать в нее ссылку на файл, в котором хранится подсборка
                           // таким образом Вы определите все имена файлов (учитывая вложенность деталей и подсборок)
      end;

   PathList.Sort();
      ...  // нужные действия с PathList
   PathList.Free;
end;

Свойство ksPart::fileName вернет полный путь к файлу сборки или детали, и путь к библиотеке моделей для стандартного компонента.
Необязательно имена файлов заносить в список строк. Если Вы желаете отсортировать список по типу документа (сборка, деталь, библиотечный элемент), можно объявить структуру данных, например

type TPathParam = record
   path : WideString;
   Detail : boolean;
end;

и динамический массив:

type TPathArray = array of TPathParam;

ну или массив указателей (быстрее работать будет для больших сборок):

type PPathArray = ^TPathArray;
       TPathArray = array [1..1000000] of TPathParam;

В TPathParam::Detail нужно будет заносить свойство ksPart::IsDetail, по которому потом и сортировать массив. Получится точно такой список, как при выполнении команды "Файл -> Свойства->Внешние ссылки".
Название: Re:Доступ к списку файлов внешних ссылок 3D модели
Отправлено: Gek от 20.01.06, 16:15:03
Ironmaxxx, а почему массив указателей будет работать быстрее? И насколько ощутимо быстрее?
Название: Re:Доступ к списку файлов внешних ссылок 3D модели
Отправлено: andno от 20.01.06, 17:30:57
Ironmaxxx-су публичное спасибо за совет!!!
Способ сработал.

Сам я его думал, но не решался пробовать из-за боязни, что повторное открытие файлов ссылок, будет занимать много времени (основная сборка открывается около 1 минуты). Оказалось, что после открытия первого a3d, последующие открываются гораздо шустрее. В общем вся сборка (294 объекта) обработалась за 2 с лишком минуты.

Андрей Носов (NIC: andno)

P.S.
Вместо
     iPart := ksPart(doc3D.GetPart(i));
следует использовать
     iPart := ksPart(doc3D.GetByIndex(i));
Название: Re:Доступ к списку файлов внешних ссылок 3D модели
Отправлено: IronMaxxx от 20.01.06, 23:15:27
Цитата: andno от 20.01.06, 17:30:57
Ironmaxxx-су публичное спасибо за совет!!!
Способ сработал.

Всегда рад помочь!  :fr:

Цитата: andno от 20.01.06, 17:30:57
P.S.
Вместо
     iPart := ksPart(doc3D.GetPart(i));
следует использовать
     iPart := ksPart(doc3D.GetByIndex(i));

Вот здесь Вы немного заблуждаетесь. В интерфейсе 3D-документа нет метода GetByIndex (т.е. ksDocument3D::GetByIndex() - неверная запись), такой метод есть в ksPartCollection. Так как мы получаем указатель на колекцию объектов перед началом цикла, то следущие строки равносильны:

iPart := ksPart(doc3D.GetPart(i));      ==        iPart := ksPart(PColl.GetByIndex(i));

Т.е. метод ksDocument3D::GetPart и ksPartCollection::GetByIndex при однаковых индексах вернут указатель на один и тот же объект сборки.
Вообще в данном случае можно обойтись без использования ksPartCollection, т.е. не определять заранее количество деталей в сборке. При этом следует организовать цикл с передусловием, и перед каждым шагом проверять iPart на nil.

       ...
i := 0;
iPart := ksPart(doc3D.GetPart(i));
while (iPart <> nil) do
begin
      ...
   i := i + 1;
   iPart := ksPart(doc3D.GetPart(i));
end;

И еще... Относительно боязни больших затрат времени на окрытие файлов по ссылках. Да тут нечего бояться! Посмотрите код: где-нибудь окрывается какой-либо файл, кроме главного файла сборки? Нет... метод ksDocument3D::GetPart не окрывет документ, а лишь дает к нему доступ. Поэтому время затрачивается только на собстенно считывание путей (не может занимать много) и на сортировку (если таковая есть, конечно).
Название: Re:Доступ к списку файлов внешних ссылок 3D модели
Отправлено: IronMaxxx от 20.01.06, 23:47:34
Цитата: Gek от 20.01.06, 16:15:03
Ironmaxxx, а почему массив указателей будет работать быстрее? И насколько ощутимо быстрее?

Потому что указатель - это переменная, которая указывает на начало области оперативной памяти, где хранятся данные. Как правило, указатель занимает в памяти 4 байта.
Теперь посмотрим на структуру (запись), что используется в примере:
type TPathParam = record
   path : WideString;
   Detail : boolean;
end;

Переменная типа boolean занимает 1 байт, в строке типа WideString приходится по 2 байта на символ. Очень редко бывает, что путь к файлу состоит менее чем из 10 символов, случается, 30...40 и даже больше. Ну, возьмем в среднем 20 символов, которым будет отвечать 40 байт. Т.е. в среднем структура TPathParam будет занимать в памяти 41 байт.
Как Вы думаете, Gek, что будет быстрее сортироваться: массив записей TPathParam (по 41 байту) или массив указателей на записи (по 4 байта)?
В принципе, в данном случае для небольших сборок  разница вряд ли будет ощутимой, но что если в записи не 2 переменные, а десятки, или же массив состоит из объектов какого-нибудь класса? Тут с указателями не просто работать быстрее, тут без них не обойтись.
Хотя, если массив ссылок не сортировать, то необходимость в указателях отпадает.
Название: Re:Доступ к списку файлов внешних ссылок 3D модели
Отправлено: IronMaxxx от 20.01.06, 23:57:14
Я вот тут подумал, в нашем случае можно обойтись без записей, массивов и указателей. Нужно создать три объекта TStringList (по одному для сборок, деталей и библиотечных элементов), для всех установить значение свойства TStringList::Sorted в true, и в каждый заносить ссылки для моделей соответсвующего типа. Класс TStringList сам позаботится о том, чтобы вставлять каждый новый элемент в соответсвующую позицию списка. А по завершению рекурсии все три списка можно объединить в один.
Не думаю, что этот алгоритм будет работать намного дольше, зато никакой возни с указателями и записями...
Название: Re:Доступ к списку файлов внешних ссылок 3D модели
Отправлено: andno от 21.01.06, 11:05:10
Цитата: IronMaxxx от 20.01.06, 23:57:14
Я вот тут подумал, в нашем случае можно обойтись без записей, массивов и указателей. Нужно создать три объекта TStringList (по одному для сборок, деталей и библиотечных элементов), для всех установить значение свойства TStringList::Sorted в true, и в каждый заносить ссылки для моделей соответсвующего типа. Класс TStringList сам позаботится о том, чтобы вставлять каждый новый элемент в соответсвующую позицию списка. А по завершению рекурсии все три списка можно объединить в один.
Не думаю, что этот алгоритм будет работать намного дольше, зато никакой возни с указателями и записями...

На самом деле я так и сделал. Ниже идет Дельфевый рабочий код. Если у кого не заработает, я съем свою шляпу!
Да изящьного сдесь мало, зато работает.

А вот как быть с 2D? Для них такой способ не работает. А у них тоже есть внешние ссылки. Может кто знает все-таки - как добраться до готового списка? Или как этот список можно автоматически записать на диск (там есть кнопка в меню)?


var
  Form1: TForm1;
  Kompas: KompasObject;
  pList : array [1..3] of TStringList;
  nst: array[1..3] of string = ('Детали','Сборки','Библиотеки');

// Нажатие кнопки выбора файла
procedure TForm1.ComboEdit1ButtonClick(Sender: TObject);
var
   idx,il: integer;
begin
    if (OpenDialog3D.Execute) then // Выбрать файл в диалоге
            Memo1.Lines.Clear();        // Сюда выведем окончательный список файлов
            pList[1] := TStringList.Create;
            pList[2] := TStringList.Create;
            pList[3] := TStringList.Create;
            pList[1].Sorted := True;
            pList[2].Sorted := True;
            pList[3].Sorted := True;

            if Kompas = nil then begin
                Kompas := KompasObject(CreateOleObject('Kompas.Application.5'));
            end;
            ComboEdit1.Text := OpenDialog3D.FileName;
            Form1.Update;
            if Form2.TreeView1.Items.Count <> 0 then Form2.TreeView1.Items.Clear; // В другом окне построим дерево проекта
            GetListModelFiles(OpenDialog3D.FileName, Form2.TreeView1.Items.Add(nil,OpenDialog3D.FileName)); // Рекурсивно по всем файлам

            // Соединить в единый список
            for il := 1 to 3 do begin
                //Memo1.Lines.Add('');
                Memo1.Lines.Add(nst[il]);
                for idx := 0 to TStringList(pList[il]).Count-1 do begin
                    Memo1.Lines.Add(TStringList(pList[il]).Strings[idx]);
                end;
            end;
            StatusBar1.Panels[0].Text := IntToStr(Memo1.Lines.Count);
            pList[1].Free;
            pList[2].Free;
            pList[3].Free;
end;


// Рекурсивная процедура
procedure TForm1.GetListModelFiles(PathModelFile: string; parentNode: TTreeNode);
var
  doc_3D : ksDocument3D;
  Parts : ksPartCollection;
  pCnt, idx,il: integer;
  st: string;
begin
    if Kompas <> nil then begin
        doc_3D := ksDocument3D(Kompas.Document3D);
        if doc_3D <> nil then begin
            StatusBar1.Panels[1].Text := 'Open...'+ExtractFileName(PathModelFile); // Просто для отслеживания фазы обработки
            Form1.Update;
            if doc_3D.Open(PathModelFile, false) then begin
                StatusBar1.Panels[1].Text := PathModelFile; // Просто для отслеживания фазы обработки
                Form1.Update;
                Parts := ksPartCollection(doc_3D.PartCollection(True));
                Parts.refresh();
                if Parts <> nil then begin
                    pCnt:= Parts.GetCount;
                    StatusBar1.Panels.Items[0].Text := IntToStr(pCnt);
                    for idx := 0 to pCnt-1 do begin
                        st := ksPart(Parts.GetByIndex(idx)).fileName;
                        if ksPart(Parts.GetByIndex(idx)).standardComponent then begin
                            Delete(st, Pos('|', st), Length(st)-Pos('|',st)+1); // оставить только путь и имя библиотеки
                            //TStringList(pList[3]).Sort();
                            if not TStringList(pList[3]).Find(st,il) then // добавить если имя уникально
                                TStringList(pList[3]).Add(st);
                            Form2.TreeView1.Items.AddChildObject(parentNode,st, nil); // В дерево как ребенка для текущего файла
                        end
                        else  begin
                            if ExtractFileExt(st) = '.a3d' then begin
                                if not TStringList(pList[2]).Find(st,il) then // добавить если имя уникально
                                    TStringList(pList[2]).Add(st);
                                GetListModelFiles(st, Form2.TreeView1.Items.AddChild(parentNode, st)); // В дерево как узел содержащий ссылки
                            end
                            else begin
                                if not TStringList(pList[1]).Find(st,il) then // добавить если имя уникально
                                    TStringList(pList[1]).Add(st);
                                Form2.TreeView1.Items.AddChildObject(parentNode,st, nil);  // В дерево как ребенка для текущего файла
                            end;
                        end; // if standartComponent
                    end; //for
                end; // Parts <> nil
            end; // Open
            doc_3D.close; // эту строчку можно и закомментировать. работать будет.
        end;
    end;
end;

andno
Название: Re:Доступ к списку файлов внешних ссылок 3D модели
Отправлено: IronMaxxx от 21.01.06, 12:19:42
Цитата: andno от 21.01.06, 11:05:10
А вот как быть с 2D? Для них такой способ не работает. А у них тоже есть внешние ссылки. Может кто знает все-таки - как добраться до готового списка?

Это, как я понимаю, ссылки на внешние франменты и опять же на файлы библиотек для стандартных элементов. Да, здесь нужно уже хорошенько подумать.
А зачем Вам это, если не секрет?

Цитата: andno от 21.01.06, 11:05:10
Или как этот список можно автоматически записать на диск (там есть кнопка в меню)?

Для 3D уже заполенный список можно сохранить с помощью метода TStringlist::SaveToFile.
Название: Re:Доступ к списку файлов внешних ссылок 3D модели
Отправлено: andno от 21.01.06, 13:06:36
Цитата: IronMaxxx от 21.01.06, 12:19:42
Это, как я понимаю, ссылки на внешние франменты и опять же на файлы библиотек для стандартных элементов. Да, здесь нужно уже хорошенько подумать.
А зачем Вам это, если не секрет?

Не секрет.
Имеется структурированный каталог (дерево в файловой системе) в котором хранятся разработки. При проектировании нового изделия конструктора создают в этом каталоге новую папку и начинают заимствовать много элементов из ранее созданных разработок по ссылкам из других папок в этом же каталоге.
В результате, создается ситуаци, при которой невозможно переместить разработку из каталога, в котором она проектировалась, в любое другое место, так как теряются ссылки на заимствованные элементы.
Поэтому когда возникает необходимость передать разработку в наш собственный филлиал в другом городе, возникает геммор с восстановлением ссылок. Если это узлы, то еще ничего, а вот со сложными объектами был случай когда потребовалось 3 дня, для того, чтобы выдрать разработку.

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

Это касается не только 3D моделей, но и двумерных чертежей (например сборочных) и спецификаций, которые тоже имеют зависимости от внешних ссылок.

С 3D моделями, понятно. Есть FileName и есть метод SetFileName, который позволяет корректно изменить местоположение файла подсборки. Построй дерево проекта, перемести файлы, установи новый путь к ним и все.
А как быть с 2D документами?
Вроде можно тоже устроить со спецификацией, хотя подробно еще не смотрел, а вот со сборочными чертежами, уже уперся.

Последняя жиденькая идейка заключается в том, чтобы открыть файл сборочного чертежа и каким-то образом попытаться сохранить уже построенный Компасом список внешних ссылок, автоматически активировав меню Файл->Свойства->Закладка "Внешние ссылки". Там есть кнопка "Записать в файл". Получив таким образом список, обработать его, а дальше еще сам незнаю. Искать в бинарнике строчки и резать по живому? Ну в общем пока полный абзац.

Название: Re:Доступ к списку файлов внешних ссылок 3D модели
Отправлено: IronMaxxx от 21.01.06, 13:49:09
Да, абзац... причем полный. У меня есть одна полубредовая идея, не знаю, может Вы уже пробовали, а может она ни к чему не приведет, но все равно выскажу. Можно попробовать запустить итератор по 2D-документу и в зависимости от возвращаемых им значений как-то действовать... Т.е. возможно он не будет разбивать стандартные элементы на объекты, а вернет указатель на весь компонент... Вот с ним можно будет попробовать работать дальше...
Но это всего лишь идейка - я сам еще смутно представляю, как это все будет работать и будет ли вообще.
Название: Re:Доступ к списку файлов внешних ссылок 3D модели
Отправлено: andno от 21.01.06, 15:31:24
Цитата: IronMaxxx от 21.01.06, 13:49:09
Да, абзац... причем полный. У меня есть одна полубредовая идея, не знаю, может Вы уже пробовали, а может она ни к чему не приведет, но все равно выскажу. Можно попробовать запустить итератор по 2D-документу и в зависимости от возвращаемых им значений как-то действовать... Т.е. возможно он не будет разбивать стандартные элементы на объекты, а вернет указатель на весь компонент... Вот с ним можно будет попробовать работать дальше...
Но это всего лишь идейка - я сам еще смутно представляю, как это все будет работать и будет ли вообще.

Спасибо за совет!
Попробую посмотреть итераторы поподробнее.
Я действительно пробовал итераторы на 3D документе. Расчитывал на то, что подсборки тоже являются 3D документами и соответственно могут быть представлены ими. 

var
  iter : ksIterator;
  ref :  Reference;
  count: integer;
...
     iter := ksIterator(Kompas.GetIterator());
     if iter <> nil then begin
         iter.ksCreateIterator(D3_DOCUMENT_OBJ, 0);
         ref := iter.ksMoveIterator('F');  // смещаем итератор на первый элемент
         Сount :=0;
         while bool(ref) do begin
                Inc(Count);
                ref := iter.ksMoveIterator('N');  // смещаем итератор на следующую позицию
          end;
          iter.ksDeleteIterator(); // удалить итератор
     end;
...

При одном открытом файле основной сборки результирующий Count всегда 1.

Однако попробую проверить с другими опциями ksCreateIterator. Может что и получится...
Название: Re: Доступ к списку файлов внешних ссылок 3D модели
Отправлено: IgorRUtver от 04.12.13, 09:40:36
А может подскажет кто, как добраться до коллекции элементов сборки в API7. Задача таже самае, получать полный путь к файлам сборки и устанавливать их количество. Пример приведенный выше работает и в принципето подходит, но из любопытства попытался найти для API7 и чет не накопал. Нашел только IPart7::FileName. Может кто знает?
Название: Re: Доступ к списку файлов внешних ссылок 3D модели
Отправлено: Sabahs от 04.12.13, 09:59:52
IParts7 - интерфейс коллекции компонентов 3D документа.
Название: Re: Доступ к списку файлов внешних ссылок 3D модели
Отправлено: IgorRUtver от 04.12.13, 11:17:11
Действительно)) Извиняюсь, утром мозг не пашет видимо
Название: Re: Доступ к списку файлов внешних ссылок 3D модели
Отправлено: Bordes от 07.03.14, 14:10:07
Цитата: andno от 19.01.06, 15:59:36
Приветствую всех!
Стоит задача получения списка всех файлов внешних ссылок 3D модели.
Это тот список, который доступен после открытия файла a3d через опцию оновного меню Компаса:
Файл -> Свойства-> Закладка "Внешние ссылки".
....
Вопрос! Как можно добраться до этого списка?

возможно поможет IKompasDocument1::GetExternalFilesNamesEx

Название: Re: Доступ к списку файлов внешних ссылок 3D модели
Отправлено: q от 11.04.14, 11:59:37
А возможно получить ссылку на файл Детали-заготовки без использования списка Внешних ссылок?