
Здравствуйте, друзья!
Сегодня я хочу рассказать вам, как открыл для себя новый язык программирования, среду исполнения, а ещё готовый фронт-энд. И всё это без кучи фреймворков и тысяч библиотек, чистое, непаханое поле…
Однако, давайте по порядку.
Список языков программирования обширен и уже устоялся, по ним много информации и всевозможных курсов. Среды разработки выросли и обзавелись множеством полезных функций, это радует.
Но мне всегда хочется чего-то нового. Долго искать не пришлось, всё уже давно было под рукой. Это Микротик. Вместе с его RouterOS. Как оказалось тут есть практически всё, что нужно и для разработки, и для исполнения кода.
А фронтом стал Телеграм с его bot API. Тут можно создать приятный интерфейс и сделать взаимодействие с ботом, очень похожим на работу с обычной программой. Телеграм мультиплатформенный и с отличным бэк-эндом. Ему бы побольше элементов управления в API и цены б ему не было. Хотя, я уверен он будет развиваться.
Начать стоит с Микротика. Точнее, давайте рассмотрим его скриптовый язык. Он совсем не сложный и на первый взгляд не годится для серьёзной разработки. Но если копнуть глубже, то он раскрывается, хоть и содержит ряд ограничений.
Из самых досадных то, что из числовых имеем только целочисленный тип данных num. Пять разделим на два, получим два. В остальном вполне можно есть.
Я писал на нескольких популярных языках и там много различных структур данных: деревья, списки, мапы. Тут в явном виде ничего этого нет. Из структур есть только массив, правда реализована поддержка многомерных ассоциативных массивов, что позволяет создавать из него нужные структуры.
Рассмотрим наглядно. Обычный одномерный массив – это индексированный список. Элементы можно вставлять в любое место списка.
Одномерный ассоциативный, где элемент key=value, это тоже список, но сортированный по key.
Двумерный – это HashMap. Он же, но ассоциативный – сортированный map.
Из многомерного можно сделать сортированное дерево. Не сортированное тоже можно.
В одном массиве отлично живут и индексированные и именованные элементы. Первые доступны по индексу, вторые по ключу. Так каждый массив может содержать служебную информацию о себе. Например в индексированных элементах хранить имя и размер.
А массив в памяти, т.е. развернутый в глобальную переменную, это доступная для записи и чтения, оперативная память, содержимое которой можно сохранять на диск, если надо. Причем на глобальные переменные ограничений по размеру нет, в отличие от локальных. Ограничивается только оперативной памятью.
Элементом массива может быть даже код. И его можно выполнить, передав туда параметры, не разворачивая в скрипт или функцию, ещё и в отдельном потоке.
Кому нужны пруфы, прошу под кат.
В этом коде объявляются две глобальные переменные и инициализируется массив, элементом которого является код.
:global queryID []
:global answerText []
:global answer ({
"warning"=":global queryID; :global answerText; :log warning \"fQueryID=\$queryID fAnswerText=\$answerText\"; :delay 3; :log warning \"after delay\"";
})А тут мы инициализируем переменные, получаем код из массива и запускаем его на выполнение.
:global queryID 345554
:global answerText "Hello"
:global answer
:local exeText ($answer->"warning")
[[:parse $exeText]]
:log info "$exeText"Чтобы передать параметры, нужно чтобы переменные были глобальными.
В данном случае код запускается командой :parse. Это значит, что сначала выполнится код из массива, а затем то, что идет после него (:log info "$exeText")
Команда :execute работает по-другому. Она запускает выполнение кода в отдельном потоке.
Да, многопоточность тоже доступна. Зависит от того, как вызывать код. Есть несколько способов. Из скрипта можем вызвать функцию, развернутую в глобальной переменной, выполнить код из :parse, или вызвать другой скрипт. В этом случае код, который идет ниже места вызова, будет ждать пока отработает его товарищ и только потом продолжит выполняться, если первый чего-нибудь не выкинет.
Всё меняется, если вызывать код командой :execute. Товарищ в этом случае работает сам по себе и ждать его не надо, правда значение не вернет тут же. Всё что ниже такого вызова продолжит выполняться, а результат работы, вызванный выше код, может сохранить в массив или файл.
Под катом подробнее.
Тот же пример, что выше под катом, только с использованием :execute
:global queryID []
:global answerText []
:global answer ({
"warning"=":log warning \"fQueryID=\$queryID fAnswerText=\$answerText\"; :delay 3; :log warning \"after delay\"";
}):global queryID 345554
:global answerText "Hello"
:global answer
:local exeText ($answer->"warning")
:execute script=$exeText file=123.txt
:log info "$exeText"Теперь код из массива выполнится в отдельном потоке, всё, что после вызова, продолжит выполнение.
Обратите внимание на отсутствие объявления глобальных переменных в коде из массива. В случае с :execute объявлять их там не надо.
Код можно запускать как службу. К примеру, в цикле мониторить появление элементов в глобальном массиве и выполнять действия в зависимости от этого. Или проверять наличие сообщений на сервере Телеграм.
Благодаря стараниям уважаемого автора Хабра, Александра @Chupakabra303, Микротик теперь понимает JSON. Автор создал набор библиотек, который парсит JSON объекты в многомерный ассоциативный массив и с ним уже можно работать всеми доступными методами.
Ещё функции… Развернутые в глобальном окружении, они постоянно доступны в памяти. В любой момент можем вызвать их из кода или консоли, передав туда параметры, как именованные, так и по индексу. Об этом подробнее можно почитать у местных уважаемых авторов, здесь и здесь.
Есть проверка синтаксиса. Можно вывести в консоль содержимое скрипта командой [/system script print from=script] и в месте, где есть ошибка шрифт станет монохромным. Если ошибок нет, то будет разноцветным, как новогодняя ёлка.
Отладка реализуется выводом в лог или консоль значения любой переменной. Что ещё нужно?
Ах, да. Нужна нормальная среда разработки. Таких много. Для себя выбрал редактор Atom от команды GitHub. У него есть много плагинов, в том числе RouterOS plugin. Плюс нативная поддержка контроля версий Git. Работать вполне комфортно.
Всё это в совокупности позволяет создать на базе RouterOS полноценный бэк-энд для мобильных приложений. Для Телеграм вообще подходит идеально.
Многие правильно подметят, а как же базы данных? Где данные хранить, запросы куда отправлять?
Это тоже решаемо. Есть СУБД как SQL, так и noSQL, которые поддерживают HTTP(S) API и JSON. Для управления временными рядами есть InfluxData, которая из коробки работает с https и JSON. Она подходит для мониторинга парам��тров самого устройства, а также для сбора всевозможных метрик с фронта. Дружит с Grafana, есть дашборды и всё красиво.
А можно использовать массивы. И даже создать СУБД на их основе, с запросами и прочим. Когда будет время займусь этим плотнее.
И все это не только для управления параметрами маршрутизатора. Ничего не мешает обрабатывать данные других сервисов с JSON сериализацией и возвращать результат. Если развернуть RouterOS в виртуальной среде, то можно оперировать ресурсами. Захостить её в облаке не проблема.
Пока это только теория. А как на практике? Покажу на примере Телеграм бота, который разработан с использованием почти всех, описанных выше, возможностей. Здесь кода не будет. Скорее демонстрация работы связки Микротик плюс Телеграм. Архитектура приложения, интерфейс, bot API и немного о безопасности.
Так выглядит главное окно бота.
Начнем с архитектуры. Мне понравилась статья одного из авторов Хабра "Создание архитектуры программы или как проектировать табуретку".
Приведу небольшую выдержку из нее.
Начнем с архитектуры. Мне понравилась статья одного из авторов Хабра "Создание архитектуры программы или как проектировать табуретку".
Приведу небольшую выдержку из нее.
Не смотря на разнообразие критериев, все же главной при разработке ��ольших систем считается задача снижения сложности. А для снижения сложности ничего, кроме деления на части, пока не придумано. Иногда это называют принципом «разделяй и властвуй» (divide et impera), но по сути речь идет об иерархической декомпозиции. Сложная система должна строится из небольшого количества более простых подсистем, каждая из которых, в свою очередь, строится из частей меньшего размера, и т.д., до тех пор, пока самые небольшие части не будут достаточно просты для непосредственного понимания и создания.
С этим трудно не согласиться. Монолитный код замучаешься читать, об отладке и поддержке вообще молчу. Поэтому - модульность. В RouterOS реализуется с помощью функций. В боте их условно можно разделить на несколько типов.
Библиотечные. Часто используются другими модулями и реализуют работу с API Телеграм например. Отвечают за отправку сообщений или построение кнопок и клавиатур.
Функции создания пользовательских интерфейсов. Они "рисуют окна" исходя из назначения и переданных параметров.
Функции обработчики. Эти содержат логику и обрабатывают команды, полученные от клиента. Для каждого модуля свой обработчик.
И функции диспетчеры, которые определяют какому модулю предназначена команда и передают ему управление. Таких в программе три. Первый обрабатывает Callbackи - это нажатие на кнопку, второй текстовые команды и третий команды модуля Терминала.
Код, который отслеживает наличие сообщений, работает как служба. Никаких шедулеров, он постоянно в работе. В бесконечном цикле мониторит сервер Телеграм, получает сообщения и, в зависимости от типа, передаёт их функции диспетчеру, предварительно проверив права пользователя на работу с системой в целом.
Процессор такой подход совсем не нагружает, зато повышает скорость обработки сообщений.
Конечно все паттерны тут не реализуешь, но...
В этом случае программа из «спагетти-кода» превращается в конструктор, состоящий из набора модулей/подпрограмм, взаимодействующих друг с другом по хорошо определенным и простым правилам, что собственно и позволяет контролировать ее сложность, а также дает возможность получить все те преимущества, которые обычно соотносятся с понятием хорошая архитектура:
Масштабируемость (Scalability) - возможность расширять систему и увеличивать ее производительность, за счет добавления новых модулей.
Ремонтопригодность (Maintainability) - изменение одного модуля не требует изменения других модулей
Заменимость модулей (Swappability) - модуль легко заменить на другой
Возможность тестирования (Unit Testing) - модуль можно отсоединить от всех остальных и протестировать / починить
Переиспользование (Reusability) - модуль может быть переиспользован в других программах и другом окружении
Сопровождаемость (Maintenance) разбитую на модули программу легче понимать и сопровождать
Теперь про интерфейс. Я видел много Телеграм-ботов и большинство из них не заботятся о чистоте пользовательского пространства. Каждое новое действие там - это новое сообщение, что для работы совсем не удобно. Такое ощущение, что их разработчики просто ленятся сделать красиво. А ведь в Телеграм есть функции для редактирования messageй. Можно отдельно редактировать и сам текст и клавиатуру под ним. Конечно не все этим грешат но, повторюсь, большинство не заморачивается.
Чтобы не дублировать код в модулях, были написаны библиотечные функции для работы с bot API. Их я выложил на русскоязычном форуме Микротик, кому надо можете пользоваться. Там есть всё, чтобы создать и отправить сообщение с текстом, фото, клавиатурой и т.д. Для редактирования и удаления тоже есть.
Настройки самого бота хранятся в массиве и считываются оттуда по необходимости.
Безопасность обеспечивается управлением пользовательскими разрешениями. Они тоже хранятся в массиве. Проверяются в функции обработчике перед выполнением команды.
Базовая версия бота опубликована на моем Телеграм канале. Написана так, что позволяет использовать его, как основу для разработки и подключения своих модулей. Используя эти наработки, любой из вас может самостоятельно дописывать нужный функционал.
Резюмируя можно сказать, что RouterOS, на мой взгляд, является неплохой средой разработки и исполнения. Конечно со своими тараканами, но их не так много. Я уверен, что рано или поздно её "распробуют" и другие разработчики приложений.
Как оказалось Микротик подходит для разработки Телеграм ботов, вообще не связанных с управлением самим устройством. Чтобы подтвердить этот тезис, я начал работу над новым проектом. Это будет сервис, связанный с геопозиционированием. Тут будет задействован и inline режим. Сейчас о подробностях говорить рано, но прототип уже есть, он работает и пока непреодолимых препятствий я не вижу.
Конечно много чего ещё хочется написать, но тогда статья слишком распухнет. И так занял много вашего времени.
Спасибо, что дочитали до конца. Сильно не пинайте, публикую в первый раз. Пожелания и предложения приветствуются.