Выдохнул...
Начну с того, что к программистам я отношения не имею, если вы такие же, то попали по адресу. Рассаживайтесь поудобней буду рассказывать как я пилил скрипт.
Пару слов о JScript for SCAD++
SCAD++ можно расширить, если чего то не хватает конкретно для вас. Для этого разработчики предусмотрели пользовательские расширения (скрипты) которые можно использовать при построении и анализе расчетных схем. Скрипт представляет собой определенные инструкции, которые СКАД выполняет после его запуска (создание/модификация элементов, нагрузок, узлов и.т.д.)
Полное руководство находится в папке с установленным SCAD++ в файле SCAD_Plugins.pdf
Руководство написано очень подробно и на русском языке. Поэтому постараюсь поменьше ссылаться на него. Откройте его и пусть оно будет под рукой если возникнут вопросы.
PreProcessor и PostProcessor
Все скрипты разделяются на 2 категории. Первая категория это скрипты, которые работают на этапе создания расчетной схемы (PreProcessor). Вторая категория это скрипты, которые работают с результатами расчета (PostProcessor). То есть от того куда мы поместим наш скрип будет зависеть то на каком этапе мы сможем его выполнить.
Папки Pre, PostProcessor хранятся по адресу: C:\ProgramData\SCAD Soft\Plugins\
C:\ProgramData изначально скрыта в Windows. Но если в адресной строке набрать этот путь, то проводник без проблем вас туда проводит
То есть наш скрипт нужно поместить в одну из папок в зависимости от категории и пиктограмма скрипта отобразится в SCAD++
Для отображения панели расширений в SCAD++ нужно нажать кнопку Вид->Панели инструментов->Расширения. После этого появится панелька с расширениями
Пользовательское расширение
Пользовательское расширение представляет собой набор файлов (в том числе скрипт) который лежит структурированно в папке с именем расширения. Пример структуры моего расширения приведу на скрине ниже(ну по сути структура одинакова для всех):
- Plugin.js-Собственно сам скрипт
- icon.bmp-Пиктограмма на панели расширений
- data-Вспомогательная папка о назначении которой расскажу позже
Редактировать будем только Plugin.js и icon.bmp. Остальное нам пока не нужно.
Постановка задачи
Существует потребность нагружать ряд пластинчатых элементов в плоских схемах трапециевидной нагрузкой. Всегда количество пластин одинаковое и равно 4м. Нагрузка может меняться в зависимости от шага стен, но в большинстве случаев 2-3 разновидности грузовых треугольников.
Сейчас есть возможность создать эту нагрузку через последовательный выбор каждой пластины. При этом нужно на каждом этапе загружения отслеживать где узел P1 и где P2. Мне бы хотелось, чтобы нагрузки прикладывались сразу на все 4 элемента при выборе 5 узлов.
Этап 1. Схема реализации
Входящими данными для программы является селекция глобальных узлов (5 узлов) которую выполнил пользователь перед запуском скрипта. То есть пользователь выделил 5 узлов и нажал кнопку расширения. Дальнейшую работу берет на себя программа (скрипт).
Скрипт должен отсортировать эти узлы в порядке, в котором будет прикладываться нагрузка. Найти соответствующие имена пластин (так как нагрузка передается на пластины). Определить локальные имена узлов каждого элемента и уже по ним назначить нагрузку.
У каждого элемента есть локальные номера узлов, количество которых зависит от вида КЭ. Если стержень-2 узла, Треугольная пластина-3 узла и.т.д. Нумерация каждого из них начинается с единицы. Номера которые отображает SCAD при нажатии на кнопочку "Номера узлов"- глобальные узлы
Этап 2. Сортировка на словах
Для того чтобы загрузить элементы и понимать на какой элемент прикладывать какую нагрузку нужно "выстроить" их в ряд. Тогда появится понимание кого как грузить. Условно раздать узлам фиктивные номера от 1 до 5-ти. В этом нам поможет Пифагор. Так как логика выстраивания их в ряд будет основываться на его научном труде, а именно "Теореме Пифагора": с^2=a^2+b^2. Ниже поясню скрином что я имею ввиду:
Для каждого из узла я строю отрезок, который соединяет узел с началом координат, то есть получаю что-то типа списка списков
[ [длина_отрезка1, номер узла1] , [длина_отрезкаN, номер узлаN] ]
затем отсортировав список списков по длинам отрезков я получу соответствующие отсортированные в ряд номера узлов.
Список-термин программирования, который представляет собой набор элементов разделенных запятой в квадратных скобках. Каждый элемент имеет свой порядковый номер в списке и обращаться к нему можно по этому номеру (индексу) . Элементами списка могут быть как числа, так и строки или даже списки (в моем случае). Нумерация элементов начинается с нуля.
Пара примеров:
[3, 4.5, 5] - Список чисел
["яблоко", "мандарин", "кокос"] -список строк
И как говорится, не отходя от кассы, расскажу про словари, так как они тоже будут использоваться.
Словари-по сути структурированные списки в виде "ключ : значение". Ключи списка обязательно должны быть уникальными. Значениями могут быть числа, строки, списки, словари и.т.д. Словари обрамляются фигурными скобками. Обращение к элементам словаря обычно происходит по ключу.
Пример словаря:
{"Майкл" : 31}
Этап 3. Сортировка на деле
Для сортировки в JScript используется метод sort. То есть он меняет местами элементы списка по возрастанию. То есть если приметить к списку [5, 4.5, 3] метод sort результат будет [3, 4.5, 5].
делается это так: [5, 4.5, 3].sort()
В нашем случае список списков. То есть сортировать нужно по первому элементу вложенного списка. Чтобы до него добраться при вызове метода я говорю методу, что сортировать нужно особенным образом (применяя функцию compare, которую я описал отдельно и которая проводит сортировку именно по первому вложенному элементу). Выглядит эта функция вот так:
Этап принятия
На этом этапе нужно понять что у нас есть все исходные данные. Отсортированные имена узлов. Имена элементов, которые нам отдаст SCAD++ при запросе скриптом, наши значения нагрузок (которые были у нас изначально и не имеют отношения к логике программы). Ну а дальше в бой!
Hello World
Говорят именно так должна называться и выглядеть первая программа. Чтобы не нарушать эту традицию я выложу по ссылке расширение, которое вы можете сразу запустить и убедиться в работоспособности скрипта. Так же использовать его позже как шаблон своих будущих программ.
1. Разархивировать
2. Папку HW положить в папку: C:\ProgramData\SCAD Soft\Plugins\PreProcessor
3. В режиме "создания расчетной схемы" вывести панель "Расширения". И нажать кнопку Обновить.
4. Нажать на пиктограмму "HW"
Выполнится скрипт, который выведет сообщение "Привет Мир"
Вернемся к нашей программе
Логика есть. Заготовка-шаблон есть. Нужно собрать все в одно целое.
Многофункциональная кнопка.
Для того чтобы не лепить несколько кнопок можно научить одну кнопку вести себя по разному, в зависимости от того что творится вокруг. В нашем случае кнопка будет иметь 2 режима.
Режим 1. Узлы не выбраны. Пользователь нажал кнопку. В этом случае вызывается окно настроек в которых указываются нагрузки.
Режим 2. Узлы выбраны. Пользователь нажал кнопку. В этом случае нагрузки добавляются в загружение.
Небольшое отступление. Вся активная часть программы будет располагаться в функции Plugin_Execute файла Plugin.js
То есть все что мы туда напишем будет выполнено при нажатии на кнопку.
Для разделения режимов нам нужно будет использовать конструкцию, которая определит выбраны ли узлы и поведет пользователю по одному из путей.
На JScript это будет выглядеть так:
1. Cоздали пустой словарь selection.
2. Наполнили значение ключа ListNode (список выделенных узлов).
3. Проверили что не пустой (!=null). Если пустой, то ушли в режим 1.
1 режим
Не хотелось бы на нем зацикливаться. Скажу лишь, что его функционал заключается в том, чтобы создать текстовый файл с 4-мя строками в папке data.
1 строка - F1 (Погонная нагрузка 1)
2 строка - F2 (Погонная нагрузка 2)
3 строка - F3 (Погонная нагрузка 3)
4 строка - L (Номер загружения)
Этот файл мы каждый запуск скрипта будем читать построчно и данные использовать для задания нагрузок.
Тут мы читаем построчно файл config.txt и помещаем в переменные данные для нагрузок.
2 режим
В этом режиме будет проходить непосредственно задание нагрузок на элементы.
Тут нужно пояснить, что для чтения данных из схемы используется объект model, а для редактирования схемы объект editor. Эти два объекта мы получим в самом начале программы и в дальнейшем будем через них взаимодействовать со схемой. Получение объектов привожу на скрине ниже:
Объявили словом var переменную model и передали ей объект методом GeTModel. То же с переменной editor.
Теперь если нам нужно что-то прочитать из схемы, то мы обращаемся к объекту model. Если что-то изменить, то к объекту editor.
К объекту editor можно обратиться только в режиме создания расчетной схемы (из PreProccesor)
В следующем листинге я закомментировал основные шаги, поэтому расписывать подробно не буду. В двух словах. Создаю пустой список lst_node, который последовательно (в цикле for) наполняю списками [длина; номер_узла]. Добавление в список элемента осуществляется методом push. Затем сортирую список по длинам и получаю отсортированный список списков вида: [ [leni,nodei] , [lenn,noden] ], то есть второй элемент каждого из вложенного списка это номер узла, а последовательность этих узлов задана позицией во внешнем списке.
В следующем листинге я показал как осуществляется задание нагрузки на один из элементов.
Тут храним глобальные номера узлов (которые отображает SCAD++)
Номера получаем из списка lst_node созданного ранее. Для индексного обращения к элементам списка используются квадратные скобки.
var node1_global=lst_node[0][1]
var node2_global=lst_node[1][1]
Далее получаем функцией get_num_elem номер элемента которому принадлежит и 1 и 2 узел. Листинг функции get_num_elem приведу ниже.
var num_el=get_num_elem(model,node1_global,node2_global)
Затем создаю переменные в которых буду хранить имена локальных узлов. Так как нагрузка задается именно по ним.
var node1_local=0
var node2_local=0
В цикле по списку узлов элемента ищу номера локальных узлов соответствующие номерам глобальных узлов.
for (i in info_el.ListNode.toArray())
{
if (info_el.ListNode.toArray()[i]==node1_global)
{node1_local=i*1+1}
if (info_el.ListNode.toArray()[i]==node2_global)
{node2_local=i*1+1}
}
Создаю словарь описывающий загружение и при помощи объекта editor назначаю нагрузки на локальные узлы элемента num_elem.
Эту же операцию проделываю с оставшимися узлами.
Заключение
Расширение можно скачать по этой ссылке
Спасибо всем кто помог разобраться (поддержке SCAD++ и программистам канала bablofil, которые отвечали на глупые вопросы)