Найти тему
Эксперименты с Renga

4. Нестандартное оборудование с портами через IFC (Renga). Начинаем погружение.

Оглавление

Для работы с файлом IFC используем любой вменяемый текстовый редактор. Лично я предпочитаю Notepad++.

4.1. Наличие "сущностей" и ссылки на них в файле IFC

В файле IFC каждая строка, содержащая запись о сущности (классе IFC), выглядит так:

#<индекс_сущности>= <сущность>;

где <индекс_сущности> - это целое положительное число, иначе данная запись не будет валидна; <сущность> - некий класс IFC с определенными атрибутами.
Далее в тексте я буду специально выделять их жирным текстом, чтобы пользователи обращали свое внимание на использование их, как ссылок на сущности в IFC файле.

В IFC файле сущности могут располагаться в разном порядке из-за того, что обращение к ним происходит по индексу сущности (как по ссылке).
Причем сами индексы сущностей также могут не соблюдать очередность в строках, например: первая строка идет с
#5, следующая с #3, потом #10 и т.д.. НО ЭТО УЖЕ ДУРНОЙ ТОН. Именно отсутствие необходимости записи в строках индексов сущностей строго в определенной очередности мы будем использовать далее.

Писать мы будем в самый конец файла IFC, перед двумя последними записями:

<....... ВОТ ЗДЕСЬ ......>
ENDSEC;
END-ISO-10303-21;

Так как индексов сущностей в рабочем файле IFC может оказаться очень много (как правило, из-за сложной геометрии), то предлагаемая мною далее очередность сущностей сделана для удобства ручной корректировки записей:
в своей заготовки для создания одного порта
начнем нумерацию индексов сущностей от миллиона.

Поехали.....

4.2. Заготовка для ручного добавления в IFC одного порта у оборудования

Делю на этапы с пояснением, далее под конец все сведу воедино.

4.2.1. Задаем расположение точки порта в пространстве

Первые пять строк:

#1000001 = IFCCARTESIANPOINT((<X>,<Y>,<Z>));

#1000002= IFCDIRECTION((<X>,<Y>,<Z>));

#1000003= IFCDIRECTION((<X>,<Y>,<Z>));

#1000004= IFCAXIS2PLACEMENT3D(#1000001,#1000002,#1000003);

#1000005= IFCLOCALPLACEMENT($,#1000004);

  • Класс IFCCARTESIANPOINT задает точку, класс IFCDIRECTION задает вектор (не нормализованный, так что все просто, т.е. длина не важна - важно направление вектора).
  • <X>,<Y>,<Z> - дробные числа, задающие координаты в декартовой системе. ВАЖНО: дробные - это всегда присутствие делителя разрядов (точки в данном случае), соответственно, при целых значениях будет, например, "10.", а не "10"; "250.", а не "250" и т.д.
  • Про классы IFCAXIS2PLACEMENT3D и IFCLOCALPLACEMENT можете почитать потом самостоятельно, а сейчас вам особо вникать в это не надо. Достаточно увидеть, что в IFCAXIS2PLACEMENT3D указываются ссылки на точку (IFCCARTESIANPOINT) и два вектора (IFCDIRECTION). А в IFCLOCALPLACEMENT указывается ссылка на IFCAXIS2PLACEMENT3D.

Итак. В сущности с индексом #1000001 задаем координаты точки, где будет располагаться порт. Координаты точки вычисляем от начала координат в проекте. Сразу вспоминаем, что обозначение положительного направления осей в Renga: Красная - X, Зеленая - Y, Синяя - Z. Вычислять можете любыми доступными способами (линии модели, размеры, одиночная арматура со свойством, показывающим ее длину и т.д.). Планирую в статье с практической частью выложить свой плагин-помощники, а пока используем только стандартные возможности Renga.

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

В сущности с индексом #1000002 задаем вектор, который будет по итогу определять направление присоединения от порта.

В сущности с индексом #1000003 задаем любой вектор, так как на задание направления присоединения у порта он не влияет.

Пример: хотим порт в координатах (100, 200, 0). Направление присоединения от порта: в отрицательном направлении оси Z. Тогда:

#1000001= IFCCARTESIANPOINT((100.,200.,0.));

#1000002= IFCDIRECTION((0.,0.,-1.));

#1000003= IFCDIRECTION((1.,0.,0.));

#1000004= IFCAXIS2PLACEMENT3D(#1000001,#1000002,#1000003);

#1000005= IFCLOCALPLACEMENT($,#1000004);

Хотим, чтобы подключение располагалось в плоскости XZ под 45 градусов относительно плоскости XY? Пожалуйста...
#1000002= IFCDIRECTION((1.,0.,1.));

Суть, надеюсь, ясна для тех, кто дружит с геометрией.

4.2.2. Создаем объект "распределительный порт" и заодно погружаемся в темную магию IFC GUID

Далее не обращаем внимание на автоматические переносы строк в местах пробелов (строка просто не умещается по ширине в статье Дзен).

#1000006= IFCDISTRIBUTIONPORT(<IFC GUID>,$,<номер порта>,$,$,#1000005,$,<поток>.,$,$);

Начнем с простого:

  • <поток> - имеет всего два значения: SINK - вход потока, SOURCE - выход потока. Записываются значения как .SINK. или .SOURCE. (можно и прописными писать; обратите внимание, слева и справа стоят точки!!). Renga в окне работы с системами визуально разделяет входные и выходные порты у оборудования следующим образом:
Пример отображения тестового оборудования в окне системы.
Пример отображения тестового оборудования в окне системы.
  • <номер порта> - строка, содержащая десятичное значение (начиная с нуля), задающее номер порта, т.е. '0', '1' и т.д.

Также здесь имеется ссылка на IFCLOCALPLACEMENT, указанный ранее в индексе #1000005.

А теперь поговорим немного про IFC GUID

  • <IFC GUID> - здесь должен находиться GUID в формате IFC GUID (в справке по IFC это IfcGloballyUniqueId). Это строковое значение. По первой ссылке в конце статьи есть даже ссылки на код конвертера (делал для себя заодно консольное приложение на C# под анализ выгрузки IFC из Renga). Для нас сейчас самое важное то, что значения IFC GUID нам понадобятся в работе. Какой брать GUID? Как его конвертировать? И так для каждого создаваемого объекта? А мы же думали, что все просто....
В начале я еще раз напомню вам нашу главную задачу: сделать в файле IFC оборудование с портами так, чтобы Renga при открытии (импорте) данного файла определила оборудование, как объект Renga. Вспомнили?
Ключевое здесь слово - Renga.

А теперь читаем очень внимательно:

Да, все GUID (подразумеваем IFC GUID) должны быть уникальными!
Но анализируя длительное время реэкспорт IFC через Renga я пришел к одному простому заключению:
Renga при открытии IFC файла (по сути создается новый проект, куда импортируется объекты IFC) руководствуется, в первую очередь, своей структурой (типами, свойствами и т.д.). И там, где ей нужно, она сама присвоит нужные ей GUID. Слишком сложно написано?
Хорошо! Скажу проще - не будем усложнять себе жизнь там, где Renga все равно сделает все по своему. Просто воспользуемся ЛЮБЫМ
IFC GUID. ОДНИМ ДЛЯ ВСЕХ ИСПОЛЬЗУЕМЫХ ДАЛЕЕ КЛАССОВ IFC!

Берем просто "от фонаря" из файла IFC вот такой IFC GUID:
3qtOK3NPvARh8jM1cnM3oC

Можете использовать другой IFC GUID (найдете их в IFC файле).
А мне сейчас важно показать вам на примере, что это РЕАЛЬНО РАБОТАЕТ благодаря Renga.

Итак. Пусть у нас на разрабатываем порту будет вход потока и этот порт будет первым (номер 0), тогда имеем:

#1000006= IFCDISTRIBUTIONPORT('3qtOK3NPvARh8jM1cnM3oC',$,'0',$,$,#1000005,$,.SINK.,$,$);

4.2.3. Создаем свойства для порта

Renga в свойствах порта при записи в IFC пишет номер порта и тип соединения, поэтому нам нужно следующее:

1). Свойство, содержащее номер порта

#1000007= IFCPROPERTYSINGLEVALUE('PortNumber',$,<Номер порта>),$);

Здесь <номер порта> - это десятичное значение. Соответственно, запись должна выглядеть как IFCINTEGER(0), или IFCINTEGER(1), или IFCINTEGER(2)... ну и т.д..

У нас в пример порт с номером 0, поэтому формируем следующую строку:

#1000007= IFCPROPERTYSINGLEVALUE('PortNumber',$,IFCINTEGER(0),$);

2). Свойство, содержащее тип соединения порта

#1000008= IFCPROPERTYENUMERATEDVALUE('ConnectionType',$,(IFCLABEL(<Тип соединения>)),$);

Здесь <Тип соединения> - это строка (выражение будет в кавычках ' '), содержащая следующие значения из перечисления (ориентируемся на Renga):

Для санитарно-технического оборудования и просто оборудования:
FLANGED - Фланцевое;
WELDED - Сварное;
COMPRESSION - Обжимное;
THREADED - Резьбовое;
GROOVED - Замковое;
OTHER - сюда попадают прочие типы: Быстроразъемное, Диффузионная сварка, Клеевое и Раструбное.

Для вентиляционного оборудования:
DRAWBAND - Бандажное;
SLIPON - Ниппельное;
DRIVESLIP - Реечное;
WELDED - Сварное;
FLANGED - Фланцевое;
OTHER - любой прочий тип.

Мы будем делать порт для трубного соединения, поэтому зададим для примера резьбовое соединение. Получаем:

#1000008= IFCPROPERTYENUMERATEDVALUE('ConnectionType',$,(IFCLABEL('THREADED')),$);

4.2.4. Создаем набор свойств

#1000009= IFCPROPERTYSET(<IFC GUID>,$,<Имя набора>,$,(#1000007,#1000008));

где:

  • <IFC GUID> - уже обсуждали ранее, принимаю его равным '3qtOK3NPvARh8jM1cnM3oC'.
  • <Имя набора> - строковое значение, представляющее имя набора строго из значений, принятых в IFC для портов.

Вспоминаем вначале из предыдущей статьи, что мы можем работать всего с 3 типами оборудования Renga: санитарно-техническое оборудование, оборудование и вентиляционное оборудование. Первые два служат для подключения трубопроводных систем. Значит нас интересует всего два значения для порта:

'Pset_DistributionPortPipe' - для санитарно-технического оборудования и просто оборудования;
'
Pset_DistributionPortDuct' - для вентиляционного оборудования.

В этом примере мы делаем порт для подключения трубопроводных систем, поэтому создаем набор свойств 'Pset_DistributionPortPipe'. Ну и в самом IFCPROPERTYSET ссылаемся на ранние созданные свойства 'PortNumber' (в IFCPROPERTYSINGLEVALUE) и 'ConnectionType' (в IFCPROPERTYENUMERATEDVALUE) через указание индекса "сущности".

Получаем:

#1000009= IFCPROPERTYSET('3qtOK3NPvARh8jM1cnM3oC',$,'Pset_DistributionPortPipe',$,(#1000007,#1000008));

4.2.5. Задаем (определяем) связь между набором свойств и объектом

У нас теперь есть набор свойств для порта и определен сам объект - распределительный порт. Теперь нам надо сделать связь между ними через класс IFCRELDEFINESBYPROPERTIES. Данный класс может устанавливать связь "многие к многим", т.е. можно применить несколько наборов свойств к нескольким объектам.

Но так как у нас для каждого порта есть своя нумерация (#1000007= IFCPROPERTYSINGLEVALUE('PortNumber',....);), а также может отличаться тип порта (#1000008= IFCPROPERTYENUMERATEDVALUE ('ConnectionType',...);), то мы будем каждый порт связывать со своим набором свойств.

Используем принятый ранее IFC GUID '3qtOK3NPvARh8jM1cnM3oC'. Ссылаемся на созданный порт (#1000006= IFCDISTRIBUTIONPORT(...);) и на созданный набор свойств (#1000009= IFCPROPERTYSET(...);) через указание индекса "сущности". Получаем:

#1000010= IFCRELDEFINESBYPROPERTIES('3qtOK3NPvARh8jM1cnM3oC',$,$,$,(#1000006),#1000009);

4.2.6. ИТОГОВАЯ заготовка по одному порту

Сводим все вместе:

#1000001 = IFCCARTESIANPOINT((0.,0.,1000.));

#1000002= IFCDIRECTION((0.,0.,1.));

#1000003= IFCDIRECTION((0.,1.,0.));

#1000004= IFCAXIS2PLACEMENT3D(#1000001,#1000002,#1000003);

#1000005= IFCLOCALPLACEMENT($,#1000004);

#1000006= IFCDISTRIBUTIONPORT('3qtOK3NPvARh8jM1cnM3oC',$,'0',$,$,#1000005,$,.SINK.,$,$);

#1000007= IFCPROPERTYSINGLEVALUE('PortNumber',$,IFCINTEGER(0),$);

#1000008= IFCPROPERTYENUMERATEDVALUE('ConnectionType',$,(IFCLABEL('THREADED')),$);

#1000009= IFCPROPERTYSET('3qtOK3NPvARh8jM1cnM3oC',$,'Pset_DistributionPortPipe',$,(#1000007,#1000008));

#1000010= IFCRELDEFINESBYPROPERTIES('3qtOK3NPvARh8jM1cnM3oC',$,$,$,(#1000006),#1000009);

4.2.6. Окончательный штрих (важная запись)

А теперь нам остается вложить созданный порт в оборудование, используя класс IFCRELNESTS. Итак, мы сделаем это в самой первой строке нашего шаблона:

#1000000= IFCRELNESTS(<IFC GUID>,$,$,$,<индекс сущности с классом оборудования>,(<перечисление индексов сущностей с классом порта>));

  • <IFC GUID> - уже обсуждали ранее, также принимаю его равным '3qtOK3NPvARh8jM1cnM3oC'.
  • <индекс сущности с классом оборудования> - вспоминаем вначале из предыдущей статьи нужные нам значения классов оборудования, задаваемые для переопределения типа объекта через свойство IfcEntityType классов. Вспомнили? Так вот, нам теперь нужно найти в файле IFC соответствующий класс и указать в данном значении индекс сущности с эти классом (ссылку на него).
Например, мы вносим порт в объект, который был переопределен, как IfcTank. Значит ищем в файле строку с классом IFCTANK. Задавали тип IfcAirTerminal, значит ищем IFCAIRTERMINAL. Задавали IfcPump, значит ищем IFCPUMP. И т.д....

Проблем никаких не будет в поиске, так как мы ранее в предыдущих статьях определили: одно оборудование (как элемент) - один проект для выгрузки в IFC.

Для примера я использую файл с оборудованием IfcTank. Поэтому ищу текст IFCTANK через окно поиска Notepad++ и нахожу его в строке с индексом сущности #53.

  • <перечисление индексов сущностей с классом порта> - здесь указываем индексы сущностей (ссылки на них), где мы задавали распределительный порт. А задавали мы его в классе IFCDISTRIBUTIONPORT. Так как мы сейчас создаем пока только один порт, то нас интересует индекс сущности #1000006.

Получаем:

#1000000= IFCRELNESTS('3qtOK3NPvARh8jM1cnM3oC',$,$,$,#53,(#1000006));

4.3. Добавляем несколько портов в оборудование

У нас уже есть заготовка (шаблон) для одного порта, вмещающаяся в 10 строк. Значит просто копируем эту заготовку для следующего порта, меняем все индексы сущностей, прибавляя к ним 10. И корректируем нужные нам данные. IFC GUID не меняем.
ВАЖНО: НЕ ЗАБЫВАЕМ при этом в классе IFCDISTRIBUTIONPORT исправить номер порта (указать следующий), т.е. был '0', стал '1' и т.д..
После того, как добавили нужное количество портов, возвращаемся к индексу сущности
#1000000 и добавляем в класс IFCRELNESTS все ссылки на созданные порты (в классе IFCDISTRIBUTIONPORT).

Например, если следовать нумерации сущностей в заготовке, то при добавлении 5-ти портов (просто как пример) мы получим следующую запись:

#1000000= IFCRELNESTS('3qtOK3NPvARh8jM1cnM3oC',$,$,$,#53,(#1000006,#1000016,#1000026,#1000036,#1000046));

Вот, как пример, скрин конца файла IFC при добавлении 3-х портов.

-3

А вот, как пример, файл IFC для оборудования с 3-мя портами на Яндекс.Диске. Там оборудование представлено обычным кубом.
Потренируйтесь на данном файле, чтобы окончательно разобраться с ручным добавлением портов в IFC.

4.3.1. Не забываем "въехать в тему" при работе со сложной геометрией

Только не смейтесь :))) Решил таки добавить данный пункт для тех, кто не сразу сообразит, как исправить заготовку под оборудование со сложной геометрией (в таком файле IFC может быть огромное количество записей). Так вот....

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

ОБЩИЙ ИТОГ части № 4 (добавление портов в оборудование через ручное редактирование файла IFC):

  • Используем заготовку, как пример шаблона, и никаких трудностей не будет.
  • Ряд неудобств возможен при вычислении координат и вектора точки для порта стандартными средствами. Плагин выложу позже в практической части.

На текущий момент вы уже можете экспериментировать и создавать оборудование с портами.

Только не забывайте, что оно всегда будет находиться в прочих системах! В текущем релизе Renga поменять на другой тип нельзя, так как кнопка блокируется. Напишу об этом свои мысли в завершении данного цикла статей.

А я пока займусь подготовкой практической части..........