Здравствуйте, друзья!

Сегодня я хочу рассказать вам, как открыл для себя новый язык программирования, среду исполнения, а ещё готовый фронт-энд. И всё это без кучи фреймворков и тысяч библиотек, чистое, непаханое поле…

Однако, давайте по порядку.

Список языков программирования обширен и уже устоялся, по ним много информации и всевозможных курсов. Среды разработки выросли и обзавелись множеством полезных функций, это радует.

Но мне всегда хочется чего-то нового. Долго искать не пришлось, всё уже давно было под рукой. Это Микротик. Вместе с его 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 режим. Сейчас о подробностях говорить рано, но прототип уже есть, он работает и пока непреодолимых препятствий я не вижу.

Конечно много чего ещё хочется написать, но тогда статья слишком распухнет. И так занял много вашего времени.

Спасибо, что дочитали до конца. Сильно не пинайте, публикую в первый раз. Пожелания и предложения приветствуются.