Здравствуйте, уважаемые хаброжители.
Сегодня я хочу поделиться небольшой наработкой, призванной конвертировать PCAD-овские карты сверления в G-код. Гибко, просто и open-source. Правда, прости-осспади, на Qt. Писать на нем, конечно, приятно, но вот деплоить и собирать чужие коды…
Некоторое время назад я отложил свой проект по голове для приуса, и вот для чего:
Пока ждал микросхемы, пока эксперементировал со схемами, отчетливо понял, что я хочу изготавливать печатные платы дома. Да-да, есть опыт лазерно-утюжной технологии, и даже рисования лаком, но хотелось чего-то настоящего. Решено было использовать пленочный фоторезист и УФ-лампу для ногтей. Естественно, встала проблема сверления и металлизации отверстий. А получается она оттуда, что отверстия нужно металлизировать до того, как на плате протравятся дорожки. Иначе подать ток к каждому отверстию — целая история.
Получается, что ручное сверление отпадает, т.к. ориентироваться просто не по чем (бумажки с картами отверстий давайте не будем предлагать).
Решено было навесить на имеющийся 3D-принтер вместо Direct-головы — шпиндель, и так и жить. И вот тут появились решения, которыми я хотел бы сегодня поделиться.
Для того, чтобы можно было легко превращать принтер в сверлилку и обратно, решено было сделать головку разъемной. Бегунок, прикрученный к ремням — отдельно, а все остальное — съемное. Учитывая, что это не бог весть, какая сложность, отдельной статьей выкладывать смысла нет. Тут просто фото и ссылки на STL-модели, если кто захочет себе точно такую же. В архиве так же присутствуют SLDPRT-исходники, если что подправить. Качается медленно — спасибо ADSL от белтелекома, но лежать должно долго.
Результат получился вот таким:
Тут все просто — после длительных попыток создать свой шпиндель, решил прикупить оный на AliExpress, и просто повесить на кронштейне. Фото нет, пока в процессе.
А вот тут начинается самое интересное.
С присущим мне глобализмом я пробежался по имеющимся решениям, и понял, что каждое из них способно не только создать кучу проблем при разворачивании технологии дома, но и доставлять их регулярно и методично, вплоть до пенсии. Что не нравилось? Негибкость. Все они больше под станки, с предопределенными характеристиками шаблонов, и т.п. Да, дело не сложное. Но очень не хотелось однажды столкнуться с ситуацией, когда нужно чуть видоизменить алгоритм, и не иметь возможности это сделать. К примеру, не встречал тулзу, способную повернуть отверстия вокруг оси. А ведь после металлизации плату 1:1 не уложишь. Но это мысли на будущее. Пока мне это не нужно. Но уже можно. В целом, хотелось чего-то простого, легкого, гибкого и… работоспособного. Решил накропать самостоятельно.
В качестве базы были использованы библиотеки Qt 5.11. Приложение написано в консольном стиле. Архитектура приложения выполнена в linux-стиле.
На вход приложению подается файл DRL, выдернутый из PCAD при создании Geber-комплекта. (возможно, придется доработать парсер, если захочется скормить ему что-нибудь из AltiumDesigner. Но лично я для себя решил снести этого Альтиум-монстра от греха подальше. За что теперь он является в страшных снах, и не дает забыть собственное имя).
В качестве параметра указывается файл XML. Описанию формата этого файла будет посвящана вторая половина статьи. Этот файл, по сути, определяет механизм формирования G-Code (а на самом деле — любого текстового файла) для передачи его (G-кода) 3D-принтеру.
Для того, чтобы сделать программу максимально гибкой, была использована библиотека ScriptEngine. Сам чуточку ошалел от того, что теперь реально можно сделать при конфигурации. Основной постулат таков: есть много вычисляемых параметров, работа с которыми ведется максимально прозрачно: текст передается модулю ScriptEngine, и используется результат. Та же ситуация происходит, если в шаблоне G-Code встретится комбинация ${бла-бла-бла}. При этом все, что внутри фигурных скобок, будет передано на вычисление, а весь шаблон заменен результатом.
На самом деле, ничего сложного нет, если вчитаться. Но давайте разберем посекционно:
В секции variables, как следует из названия, мы можем определить произвольный набор глобальных переменных. Они никак не влияют на работу программы, пока не встретятся в каком-нибудь вычисляемом выражении.
Функции. Ну, точнее, функция. Пока она, предопределенная, одна: вычисление реального диаметра сверла для металлизированных отверстий. Известно, что металлизация крадет диаметр, и это частенько приводит к казусам при попытке просунуть ногу компонента 0,8, которая не лезет в отверстие, заложенное, как 0,9. Чтобы не возиться с этим при проектировании я решил добавить этот функционал.
Смысл этой секции — определить функции, которые может использовать конвертер для определенных целей. Эти функции нельзя (пока?) использовать самостоятельно.
Сверла. Тут нужно сделать отсылку к команде скрипта «align tools», про которую ниже. Каждый элемент этой секции определяет ячейку, в которую будут собраны все инструменты, распознанные во входном файле. Идея такова, что частенько при проектировании случаются дюймовые диаметры, и множество инструментов с их значениями 0,478...0,492… и т.д. Чтобы не возиться с ними, мы задаем обязательные параметры range_min и range_max. Обязателен так же признак металлизации. Ноды XML просматриваются последовательно, и как только очередной инструмент из DRL подходит под определение — нода признается подходящей.
Можно задавать любые другие параметры в ноде. Их значение можно будет позже использовать в шаблонах.
Вы можете задать позицию в пенале или координаты, где захватить сверло, если у вас станок с автосменой инструмента. А можете описать инструмент буквами для вывода на экран принтера, если у вас, как у меня, Marlin и ручная смена сверл.
А вот теперь оцените всю прелесть скрипт-машины! Шаблоны. Конвертер, как я уже говорил, работает с шаблонами просто: ищет все кусочки вида ${...}, и отправляет в скрипт-машину. А там-то JS-подобный язык. Поэтому, собственно, можно даже чуточку программировать. В данном примере можно видеть, как при выводе шаблона start мы сначала определили пару переменных, которым присвоили значения глобальных. Ну а лишь потом написали константу, которая и будет значением выполнения этого куска.
Когда этот шаблон будет выведен в выходной файл, мы увидим:
Ну и не могу ж не похвастаться. Оцените кусочек из шаблона для сверления каждой дырдочки:
да-да… каждый раз, печатая комментарий Holes rest, мы будем декрементировать значение hcnt. А она, как мы помним, была определена, пока мы печатали start, а, стало быть, находится контекстом выше. А потом будем вычислять переменную percent, чтобы после использовать ее в другом куске — при передаче ее в команду M73 (эта команда заставляет марлин подвинуть полоску прогресса). G-Код, сгенерированный этим фрагментом:
кстати, toolsCount, minX — это предопределенные имена глобальных переменных.
Отмечу, что имена шаблонов не предопределены, т.е. вы можете использовать любые. Шаблон будет распечатан, когда в скрипте встретится команда print и его имя.
Внутри секции могут встречаться ноды с именами command и loop.
Формат ноды command:
Оператор действия — один из немногих операторов. Параметры для каждого описаны ниже. Могут быть дополнены любыми другими, которые, как вы уже поняли, можно использовать в шаблонах.
Формат ноды loop:
цикл — это секция, содержимое которой будет исполнено для каждого элемента, определенного типом цикла. Их два (пока):
tools — цикл выполнится для каджого инструмента, и
toolholes — цикл выполнится для каждой дырки, предназначенной для сверления этим инструментом. Очевидно, что цикл toolholes может быть только вложенным в tools.
При этом при выполнении вложенного цикла доступны все переменные для текущего инструмента. Зачем? Не знаю. Просто рассказал.
assign tools
Параметры: нет.
Проводит присвоение каждому сверлу из исходного файла инструмента из XML. Без него большинство других действий не имеют смысла.
join tools
Параметры: нет.
Больше организационный — объединяет все инструменты, которым был присвоен один и тот же из XML-файла. Имеет смысл сразу после assign tools, но я решил дать возможность пользователю сделать свои операции.
sort tools
Параметры: нет (пока).
Сортирует сверла по возрастанию диаметров
offset
Параметры:
xoffs,yoffs — значения смещений. Работает скрипт-машина.
Смещает все отверстия на указанные значения. Да, часто так бывает, что плата разводится далеко не в начале координат.
print
Параметр:
pattern Название шаблона.
Печатает шаблон с указанным названием на выходной поток.
print context
Параметры:
line_begin, line_end — начало и конец каждой строки.
Отладочная штука — позволяет в любом месте вывести на выход все доступные в данный момент переменные и их значения. Каждая переменная выводится отдельной строкой, начало и конец указаны в параметрах
holesCount, toolsCount — я очень, очень, очень надеюсь, что смысл этих переменных в пояснении не нуждается. Да-да. Это количество инструментов и количество отверстий.
minX, maxX, minY, maxY — и этих тоже. Нет, ну на всякий случай — это координаты поля сверления. Все дырочки находятся внутри этого прямоугольника. Пересчитывается после команды offset.
Вот, собственно, постарался вкратце, но максимально полно описать сотворенную тулзу.
Честно говоря, пока пытался представить сценарии использования, я отчетливо представлял себе, сколько раз проявится татаро-монгольское иго на землях русских (есть мнение, что именно они принесли нам мат).
Отсюда вопрос: стоит ли заморочится, и сделать простенькую веб-страничку, куда можно вставить вход и скрипт, и получить готовый G-Code, минуя стадии сборки из исходников?
UPD:
Спасибо проголосовавшим. Запилил. И… да: я же писал, что веб-страничка будет простенькой? Если кому-нибудь будет не лень привести это все в более эстетический вид — бросайте HTML в личку.
Сегодня я хочу поделиться небольшой наработкой, призванной конвертировать PCAD-овские карты сверления в G-код. Гибко, просто и open-source. Правда, прости-осспади, на Qt. Писать на нем, конечно, приятно, но вот деплоить и собирать чужие коды…
Часть первая. Механика.
Некоторое время назад я отложил свой проект по голове для приуса, и вот для чего:
Пока ждал микросхемы, пока эксперементировал со схемами, отчетливо понял, что я хочу изготавливать печатные платы дома. Да-да, есть опыт лазерно-утюжной технологии, и даже рисования лаком, но хотелось чего-то настоящего. Решено было использовать пленочный фоторезист и УФ-лампу для ногтей. Естественно, встала проблема сверления и металлизации отверстий. А получается она оттуда, что отверстия нужно металлизировать до того, как на плате протравятся дорожки. Иначе подать ток к каждому отверстию — целая история.
Получается, что ручное сверление отпадает, т.к. ориентироваться просто не по чем (бумажки с картами отверстий давайте не будем предлагать).
Решено было навесить на имеющийся 3D-принтер вместо Direct-головы — шпиндель, и так и жить. И вот тут появились решения, которыми я хотел бы сегодня поделиться.
Сменная головка для RepRap
Для того, чтобы можно было легко превращать принтер в сверлилку и обратно, решено было сделать головку разъемной. Бегунок, прикрученный к ремням — отдельно, а все остальное — съемное. Учитывая, что это не бог весть, какая сложность, отдельной статьей выкладывать смысла нет. Тут просто фото и ссылки на STL-модели, если кто захочет себе точно такую же. В архиве так же присутствуют SLDPRT-исходники, если что подправить. Качается медленно — спасибо ADSL от белтелекома, но лежать должно долго.
Результат получился вот таким:
Головка-шпиндель
Тут все просто — после длительных попыток создать свой шпиндель, решил прикупить оный на AliExpress, и просто повесить на кронштейне. Фото нет, пока в процессе.
Генератор G-Code
А вот тут начинается самое интересное.
С присущим мне глобализмом я пробежался по имеющимся решениям, и понял, что каждое из них способно не только создать кучу проблем при разворачивании технологии дома, но и доставлять их регулярно и методично, вплоть до пенсии. Что не нравилось? Негибкость. Все они больше под станки, с предопределенными характеристиками шаблонов, и т.п. Да, дело не сложное. Но очень не хотелось однажды столкнуться с ситуацией, когда нужно чуть видоизменить алгоритм, и не иметь возможности это сделать. К примеру, не встречал тулзу, способную повернуть отверстия вокруг оси. А ведь после металлизации плату 1:1 не уложишь. Но это мысли на будущее. Пока мне это не нужно. Но уже можно. В целом, хотелось чего-то простого, легкого, гибкого и… работоспособного. Решил накропать самостоятельно.
В качестве базы были использованы библиотеки Qt 5.11. Приложение написано в консольном стиле. Архитектура приложения выполнена в linux-стиле.
На вход приложению подается файл DRL, выдернутый из PCAD при создании Geber-комплекта. (возможно, придется доработать парсер, если захочется скормить ему что-нибудь из AltiumDesigner. Но лично я для себя решил снести этого Альтиум-монстра от греха подальше. За что теперь он является в страшных снах, и не дает забыть собственное имя).
В качестве параметра указывается файл XML. Описанию формата этого файла будет посвящана вторая половина статьи. Этот файл, по сути, определяет механизм формирования G-Code (а на самом деле — любого текстового файла) для передачи его (G-кода) 3D-принтеру.
Механизм работы приложения
- Читается и распознается формат DRL (который М48 или Excellon). В результате получаются инструменты, содержащие список дырок, которые этими инструментами сверлятся.
- С полученными из п.1 данными мы идем в XML, ищем там ноду script, и попросту исполняем все, что там написано. Есть пяток операторов, а большего нам и не нужно.
- В процессе исполнения п.2 случались операторы print. Результат печатается на выходной поток.
Часть вторая. Формат XML-файла
Для того, чтобы сделать программу максимально гибкой, была использована библиотека ScriptEngine. Сам чуточку ошалел от того, что теперь реально можно сделать при конфигурации. Основной постулат таков: есть много вычисляемых параметров, работа с которыми ведется максимально прозрачно: текст передается модулю ScriptEngine, и используется результат. Та же ситуация происходит, если в шаблоне G-Code встретится комбинация ${бла-бла-бла}. При этом все, что внутри фигурных скобок, будет передано на вычисление, а весь шаблон заменен результатом.
Исходные коды
Пример файла для моего принтера
<xml>
<variables>
<var name="ZChangeToolValue" value="30"/>
<var name="ZTravelValue" value="10"/>
<var name="ZDrillValue" value="0"/>
</variables>
<functions>
<!--
predefined function with single parameter:
a - source (requested) diameter
returns - suggested tool diameter for give requested after halvanic
if function nod defined, it assumed return=a
-->
<plate_increase_dia f="a+0.2"/>
</functions>
<tools>
<!--
"tool" node defines a real drill tool for make a hole
Depends on your technical process you can set up different
tools for plated or not holes or join same holes in single tool.
Required parameters for tool are:
1. range_min,range_max - diameters range to assign holes for this tool
You can joun different diameters (f.ex. 0.31-0.4) to single tool
2. plated="yes|no|both" - defines plated property to
Other parameters are optional and can be used later in G-Code patterns.
For example, you can define tool position or toolbox coords for the tool.
-->
<tool description="0,3mm" range_min="0" range_max="0.3" plated="both" position="0" />
<tool description="0,4mm" range_min="0.3" range_max="0.4" plated="both" position="1" />
<tool description="0,5mm" range_min="0.4" range_max="0.5" plated="both" position="2" />
<tool description="0,6mm" range_min="0.5" range_max="0.6" plated="both" position="3" />
<tool description="0,7mm" range_min="0.6" range_max="0.7" plated="both" position="4" />
<tool description="0,8mm" range_min="0.7" range_max="0.8" plated="both" position="5" />
<tool description="0,9mm" range_min="0.8" range_max="0.9" plated="both" position="6" />
<tool description="1,0mm" range_min="0.9" range_max="1.0" plated="both" position="7" />
<tool description="1,1mm" range_min="1.0" range_max="1.1" plated="both" position="8" />
<tool description="1,2mm" range_min="1.1" range_max="5" plated="both" position="9" />
</tools>
<patterns>
<!-- in any pattern you can use any variable from context where it's printing
Example (used inside 'tool' loop type):
Mnnn Please, change tool to ${description} ; message to lcd
Mnnn ; pause
Note : here ${description} is optional tag defined in <tool> node
Use this example outsite the tool loop will cause calculation error.
-->
<pattern name="start">
G90 ;${var hcnt=holesCount;var tcnt=toolsCount;"Hello"}
M117 Homing
G28 X Y
M117 Move Z to travel
G0 X${minX} Y${minY}
M76
G92 Z${ZTravelValue}
</pattern>
<pattern name="finish">
G0 Z${ZChangeToolValue}
M104 S0 ; disable spindle
G0 X0 Y220
M117 Drill finished
M300 S600 P1
; Stats:
; Holes : ${holesCount}
; Tools : ${toolsCount}
</pattern>
<pattern name="set_tool">
; Tools rest: ${tcnt--}
G0 Z${ZChangeToolValue}
G0 X100 Y0
M104 S0 ; disable spindle
M117 Change tool to ${description}
M300 S600 P1
M76 ; pause job
M117 Drilling
M104 S100 ; enable spindle
G28 X
</pattern>
<pattern name="go_drill">
; Holes rest: ${hcnt--}
; Percent rest: ${var percent=Math.round(hcnt*100/holesCount); percent}%
M73 P${100-percent}
G0 Z${ZTravelValue}
G0 X${Math.round(x*100)/100} Y${Math.round(y*100)/100}
G0 Z${ZDrillValue}
G0 Z${ZTravelValue}
</pattern>
</patterns>
<script>
<!--
"assign tools". No parameters
Just assign all tools declared in
DRL-file to tools described in <tools> node.
For each DRL-defined tool will be selected FIRST compatible
tool from <tools> node. I.e. if range 0.3..0.8 will be defined early,
<tool> node for diameter 0.4..0.5 will never be assigned.
Except 'plated' property will be different.
-->
<command verb="assign tools" />
<!--
"assign tools". No parameters
Join all DRL-file tools, assigned to same tool here
to one tool (also holes)
Just avoid multiply changing physical tool to same
-->
<command verb="join tools" />
<!--
"offset".
Offset ALL holes by defined values
xoffs, yoffs - values to offset. Before offset will be calculated
i.e. here you can use global variables.
-->
<command verb="offset" xoffs="-minX+10" yoffs="-minY+10"/>
<!--
loop for each DRL-tool (assigned and joined before).
Context inside will be filled also with tool's properties and
node's parameters
-->
<command verb="print" pattern="start"/>
<loop type="tools">
<command verb="print" pattern="set_tool"/>
<command verb="print context" line_begin=";"/>
<!--
loop for each hole inside the tool.
Context inside will be filled also with hole's properties(x&y) and
node's parameters
-->
<loop type="toolholes">
<command verb="print" pattern="go_drill"/>
<!--
"print context".
Anwhere in script you can use this verb.
It inserts all context variables available.
Usefull for debug but completely useless for
normal work
-->
<command verb="жprint context" line_begin=";"/>
</loop>
</loop>
<command verb="print" pattern="finish"/>
</script>
</xml>
И версия этого же файла после практических испытаний
<xml>
<variables>
<var name="ZChangeToolValue" value="10"/>
<var name="ZTravelValue" value="2"/>
<var name="ZDrillValue" value="-3"/>
<var name="FeedHorizontal" value="24000"/>
<var name="FeedDown" value="100"/>
<var name="FeedFree" value="2000"/>
<var name="StartOffsX" value="20"/>
<var name="StartOffsY" value="20"/>
<var name="ZZeroPosition" value="0.1"/>
<var name="first" value="0"/>
</variables>
<functions>
<!--
predefined function with single parameter:
a - source (requested) diameter
returns - suggested tool diameter for give requested after halvanic
if function nod defined, it assumed return=a
-->
<plate_increase_dia f="a+0.3"/>
</functions>
<tools>
<!--
"tool" node defines a real drill tool for make a hole
Depends on your technical process you can set up different
tools for plated or not holes or join same holes in single tool.
Required parameters for tool are:
1. range_min,range_max - diameters range to assign holes for this tool
You can joun different diameters (f.ex. 0.31-0.4) to single tool
2. plated="yes|no|both" - defines plated property to
Other parameters are optional and can be used later in G-Code patterns.
For example, you can define tool position or toolbox coords for the tool.
-->
<tool description="0,3mm" range_min="0" range_max="0.3" plated="both" position="0" />
<tool description="0,4mm" range_min="0.3" range_max="0.4" plated="both" position="1" />
<tool description="0,5mm" range_min="0.4" range_max="0.5" plated="both" position="2" />
<tool description="0,6mm" range_min="0.5" range_max="0.6" plated="both" position="3" />
<tool description="0,7mm" range_min="0.6" range_max="0.7" plated="both" position="4" />
<tool description="0,8mm" range_min="0.7" range_max="0.8" plated="both" position="5" />
<tool description="0,9mm" range_min="0.8" range_max="0.9" plated="both" position="6" />
<tool description="1,0mm" range_min="0.9" range_max="1.0" plated="both" position="7" />
<tool description="1,1mm" range_min="1.0" range_max="1.1" plated="both" position="8" />
<tool description="1,2mm" range_min="1.1" range_max="5" plated="both" position="9" />
</tools>
<patterns>
<!-- in any pattern you can use any variable from context where it's printing
Example (used inside 'tool' loop type):
Mnnn Please, change tool to ${description} ; message to lcd
Mnnn ; pause
Note : here ${description} is optional tag defined in <tool> node
Use this example outsite the tool loop will cause calculation error.
-->
<pattern name="start1">
; Start
</pattern>
<pattern name="set_tool1">
; Set tool ${description}
</pattern>
<pattern name="finish1">
; Finish
</pattern>
<pattern name="go_drill1">
; Drill X${Math.round(x*100)/100} Y${Math.round(y*100)/100}
</pattern>
<pattern name="start">
;${var hcnt=holesCount;var tcnt=toolsCount;"Hello"}
M117 Homing
G28
G0 Z0 F${FeedFree}
G92 Z1.6
</pattern>
<pattern name="finish">
G0 Z${ZChangeToolValue} F${FeedFree}
M400
M5 ; disable spindle
G0 X0 Y220 F${FeedHorizontal}
M117 Drill finished
M300 S600 P100
; Stats:
; Holes : ${holesCount}
; Tools : ${toolsCount}
</pattern>
<pattern name="set_tool">
; Tools rest: ${tcnt--}
G0 Z${ZChangeToolValue} F${FeedFree}
M400
G0 X100 Y0 F${FeedHorizontal}
M117 Stopping spindle
M5 ; disable spindle
M117 Change tool to ${description}
M300 S600 P100
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M25
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
G28 X Y
G0 X${StartOffsX-1} Y${StartOffsX-1} Z${ZTravelValue} F${FeedHorizontal}
G0 Z${ZZeroPosition} F${FeedFree}
M117 Check zero-hole
M300 S600 P100
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M25
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
M400 ; This strange line is just crutch to prevent Marlin from read-n-exec other commands begore do pause
G92 Z${ZZeroPosition} F${FeedDown}
M117 Starting spindle
M3 ; enable spindle
G0 Z${ZDrillValue} F${FeedDown/3}
G0 Z${ZTravelValue} F${FeedFree}
M117 Drilling
M117 Starting spindle
M3 ; enable spindle
</pattern>
<pattern name="go_drill">
; Holes rest: ${hcnt--}
; Percent rest: ${var percent=Math.round(hcnt*100/holesCount); percent}%
M73 P${100-percent}
M117 Drilling X${Math.round(x*100)/100} Y${Math.round(y*100)/100} Z${ZTravelValue}
G0 Z${ZTravelValue} F${FeedFree}
G0 X${(Math.round(x*100)/100)-2} Y${(Math.round(y*100)/100)-2} F${FeedHorizontal}
G0 X${Math.round(x*100)/100} Y${Math.round(y*100)/100} F${FeedHorizontal}
M400
G0 Z${Math.round((ZZeroPosition+0.2)*100)/100} F${FeedFree}
G0 Z${Math.round((ZZeroPosition-0.3)*100)/100} F${FeedDown/10}
G0 Z${ZDrillValue} F${FeedDown}
M117 Return
G0 Z${ZTravelValue} F${FeedFree}
</pattern>
<pattern name="second_time">
; ${var hcnt=holesCount;var tcnt=toolsCount;"SECOND!!!"}
</pattern>
</patterns>
<script>
<!--
"assign tools". No parameters
Just assign all tools declared in
DRL-file to tools described in <tools> node.
For each DRL-defined tool will be selected FIRST compatible
tool from <tools> node. I.e. if range 0.3..0.8 will be defined early,
<tool> node for diameter 0.4..0.5 will never be assigned.
Except 'plated' property will be different.
-->
<command verb="assign tools" />
<!--
"assign tools". No parameters
Join all DRL-file tools, assigned to same tool here
to one tool (also holes)
Just avoid multiply changing physical tool to same
-->
<command verb="join tools" />
<!--
"offset".
Offset ALL holes by defined values
xoffs, yoffs - values to offset. Before offset will be calculated
i.e. here you can use global variables.
-->
<command verb="offset" xoffs="-minX+StartOffsX" yoffs="-minY+StartOffsY"/>
<!--
loop for each DRL-tool (assigned and joined before).
Context inside will be filled also with tool's properties and
node's parameters
-->
<command verb="sort tools"/>
<command verb="print" pattern="start"/>
<loop type="tools">
<condition content="first++==0">
<command verb="print" pattern="set_tool"/>
</condition>
<command verb=";print context" line_begin=";"/>
<!--
loop for each hole inside the tool.
Context inside will be filled also with hole's properties(x&y) and
node's parameters
-->
<loop type="toolholes">
<command verb="print" pattern="go_drill"/>
<!--
"print context".
Anwhere in script you can use this verb.
It inserts all context variables available.
Usefull for debug but completely useless for
normal work
-->
<command verb=";print context" line_begin=";"/>
</loop>
</loop>
<condition content="first=0">
<command verb=";dummy"/>
</condition>
<command verb="print" pattern="second_time"/>
<loop type="tools">
<condition content="first++>0">
<command verb="print" pattern="set_tool"/>
<command verb=";print context" line_begin=";"/>
<!--
loop for each hole inside the tool.
Context inside will be filled also with hole's properties(x&y) and
node's parameters
-->
<loop type="toolholes">
<command verb="print" pattern="go_drill"/>
<!--
"print context".
Anwhere in script you can use this verb.
It inserts all context variables available.
Usefull for debug but completely useless for
normal work
-->
<command verb=";print context" line_begin=";"/>
</loop>
</condition>
</loop>
<command verb="print" pattern="finish"/>
</script>
</xml>
На самом деле, ничего сложного нет, если вчитаться. Но давайте разберем посекционно:
<variables>
<var name="Название переменной" value="Значение переменной"/>
</variables>
В секции variables, как следует из названия, мы можем определить произвольный набор глобальных переменных. Они никак не влияют на работу программы, пока не встретятся в каком-нибудь вычисляемом выражении.
<functions>
<plate_increase_dia f="a+0.2"/>
</functions>
Функции. Ну, точнее, функция. Пока она, предопределенная, одна: вычисление реального диаметра сверла для металлизированных отверстий. Известно, что металлизация крадет диаметр, и это частенько приводит к казусам при попытке просунуть ногу компонента 0,8, которая не лезет в отверстие, заложенное, как 0,9. Чтобы не возиться с этим при проектировании я решил добавить этот функционал.
Смысл этой секции — определить функции, которые может использовать конвертер для определенных целей. Эти функции нельзя (пока?) использовать самостоятельно.
<tools>
<tool description="0,3mm" range_min="0" range_max="0.3" plated="both" position="0" />
</tools>
Сверла. Тут нужно сделать отсылку к команде скрипта «align tools», про которую ниже. Каждый элемент этой секции определяет ячейку, в которую будут собраны все инструменты, распознанные во входном файле. Идея такова, что частенько при проектировании случаются дюймовые диаметры, и множество инструментов с их значениями 0,478...0,492… и т.д. Чтобы не возиться с ними, мы задаем обязательные параметры range_min и range_max. Обязателен так же признак металлизации. Ноды XML просматриваются последовательно, и как только очередной инструмент из DRL подходит под определение — нода признается подходящей.
Можно задавать любые другие параметры в ноде. Их значение можно будет позже использовать в шаблонах.
Вы можете задать позицию в пенале или координаты, где захватить сверло, если у вас станок с автосменой инструмента. А можете описать инструмент буквами для вывода на экран принтера, если у вас, как у меня, Marlin и ручная смена сверл.
<patterns>
<pattern name="start">
G90 ;${var hcnt=holesCount;var tcnt=toolsCount;"Hello"}
M117 Homing
G28 X Y
M117 Move Z to travel
G0 X${minX} Y${minY}
M76
G92 Z${ZTravelValue}
</pattern>
А вот теперь оцените всю прелесть скрипт-машины! Шаблоны. Конвертер, как я уже говорил, работает с шаблонами просто: ищет все кусочки вида ${...}, и отправляет в скрипт-машину. А там-то JS-подобный язык. Поэтому, собственно, можно даже чуточку программировать. В данном примере можно видеть, как при выводе шаблона start мы сначала определили пару переменных, которым присвоили значения глобальных. Ну а лишь потом написали константу, которая и будет значением выполнения этого куска.
Когда этот шаблон будет выведен в выходной файл, мы увидим:
G90 ;Hello
M117 Homing
G28 X Y
M117 Move Z to travel
G0 X10 Y10
M76
G92 Z10
Ну и не могу ж не похвастаться. Оцените кусочек из шаблона для сверления каждой дырдочки:
; Holes rest: ${hcnt--}
; Percent rest: ${var percent=Math.round(hcnt*100/holesCount); percent}%
M73 P${100-percent}
да-да… каждый раз, печатая комментарий Holes rest, мы будем декрементировать значение hcnt. А она, как мы помним, была определена, пока мы печатали start, а, стало быть, находится контекстом выше. А потом будем вычислять переменную percent, чтобы после использовать ее в другом куске — при передаче ее в команду M73 (эта команда заставляет марлин подвинуть полоску прогресса). G-Код, сгенерированный этим фрагментом:
; Holes rest: 6
; Percent rest: 13%
M73 P87
кстати, toolsCount, minX — это предопределенные имена глобальных переменных.
Отмечу, что имена шаблонов не предопределены, т.е. вы можете использовать любые. Шаблон будет распечатан, когда в скрипте встретится команда print и его имя.
<script>
<command verb="assign tools" />
И основа — секция script
Внутри секции могут встречаться ноды с именами command и loop.
Формат ноды command:
<command verb="оператор действия" .... параметры для оператора ... />
Оператор действия — один из немногих операторов. Параметры для каждого описаны ниже. Могут быть дополнены любыми другими, которые, как вы уже поняли, можно использовать в шаблонах.
Формат ноды loop:
<loop type="тип цикла">
.....
</loop>
цикл — это секция, содержимое которой будет исполнено для каждого элемента, определенного типом цикла. Их два (пока):
tools — цикл выполнится для каджого инструмента, и
toolholes — цикл выполнится для каждой дырки, предназначенной для сверления этим инструментом. Очевидно, что цикл toolholes может быть только вложенным в tools.
При этом при выполнении вложенного цикла доступны все переменные для текущего инструмента. Зачем? Не знаю. Просто рассказал.
Операторы
assign tools
Параметры: нет.
Проводит присвоение каждому сверлу из исходного файла инструмента из XML. Без него большинство других действий не имеют смысла.
join tools
Параметры: нет.
Больше организационный — объединяет все инструменты, которым был присвоен один и тот же из XML-файла. Имеет смысл сразу после assign tools, но я решил дать возможность пользователю сделать свои операции.
sort tools
Параметры: нет (пока).
Сортирует сверла по возрастанию диаметров
offset
Параметры:
xoffs,yoffs — значения смещений. Работает скрипт-машина.
Смещает все отверстия на указанные значения. Да, часто так бывает, что плата разводится далеко не в начале координат.
Параметр:
pattern Название шаблона.
Печатает шаблон с указанным названием на выходной поток.
print context
Параметры:
line_begin, line_end — начало и конец каждой строки.
Отладочная штука — позволяет в любом месте вывести на выход все доступные в данный момент переменные и их значения. Каждая переменная выводится отдельной строкой, начало и конец указаны в параметрах
Предопределенные имена глобальных переменных.
holesCount, toolsCount — я очень, очень, очень надеюсь, что смысл этих переменных в пояснении не нуждается. Да-да. Это количество инструментов и количество отверстий.
minX, maxX, minY, maxY — и этих тоже. Нет, ну на всякий случай — это координаты поля сверления. Все дырочки находятся внутри этого прямоугольника. Пересчитывается после команды offset.
Заключение
Вот, собственно, постарался вкратце, но максимально полно описать сотворенную тулзу.
Отсюда вопрос: стоит ли заморочится, и сделать простенькую веб-страничку, куда можно вставить вход и скрипт, и получить готовый G-Code, минуя стадии сборки из исходников?
UPD:
Спасибо проголосовавшим. Запилил. И… да: я же писал, что веб-страничка будет простенькой? Если кому-нибудь будет не лень привести это все в более эстетический вид — бросайте HTML в личку.
Only registered users can participate in poll. Log in, please.
Есть ли потребность запилить веб-версию этой утилиты
10.81% Да, буду пользоваться постоянно4
43.24% Да, хотелось бы использовать время от времени16
21.62% Нет, мне проще собрать, и работать локально8
24.32% Нет, этот функционал совершенно не интересен9
37 users voted. 17 users abstained.