Модульное приложение для носимых устройств, позволяющее по запросу с устройства или с помощью носимого устройства (короткие команды - мнемоники) отправлять преимущественно текстовую информацию для просмотра на носимом устройстве
-
Базовый функционал: приложение должно обеспечить ввод запроса к модулю (см. ниже) и получение от него ответа. При этом ответ должен быть отправлен на определенное в базовом функционале устройство вывода. При этом у базового функционала должна существовать возможность подключения дополнительного устройства ввода/вывода. При этом неважно, как пользователь будет взаимодействовать с программой - терминал или GUI
-
Модули: приложение является модульным. Каждый модуль выполняет свою уникальную функцию, модулю предоставляется контекст приложения, модуль должен реализовать себя по определенному стандарту
-
Регистры: каждому модулю должен соответствовать регистр. Регистр - адрес модуля, по которому к нему можно будет легко обратиться
-
Расширения (Extensions): расширения представляют собой вспомогательный скрипты-библиотеки, которые свободно может требовать и использовать модуль (по задумке, должны располагаться локально). Расширения НЕ ДОЛЖНЫ выполнять функции модуля
-
Конвейер: промежуточное звено между приложением и сервисом доставки. Управляет разбиением на пакеты, задержками перед отправкой
-
Сервисы доставки и сервисы ввода: звено между устройствами ввода/вывода, отправляют или принимают данные с устройств ввода/вывода
- Регистр должен начинаться с 0 и иметь минимальную длину - 3 (пример: 001)
- Если регистр имеет длину более 3, то после регистра обязательно должен следовать спец.разделитель
- Если запрос начинается с 0 и имеет более 3 символов. Первые три символа - регистр
- Регистр можно разделить спец.разделителями (пробел, знак равно, двоеточие)
- Стандартный регистр определяется в регистрах как default и имеет значение регистра (по умолчанию - 000). Обращение к нему не требует номера регистра перед запросом
- Регистр модуля определяется в общей таблице регистров так: "регистр": "название модуля"
- Регистр может иметь строковое название, тогда после строкового регистра обязательно должен быть спец. разделитель, если дальше будет следовать запрос к модулю. Приложение должно уметь различать строковый регистр от запроса к стандартному регистру
- Регистров у одного модуля может быть неограниченное количество
Правила построения модулей:
Модули хранятся в отдельных папках в папке modules. Папка модуля должна содержать файл init.py с модулем, чтобы отделить модуль от вспомогательных скриптов
Модуль обязательно должен содержать функцию swallow(value). Разрешены и другие функции:
- init - первоначальная инициализация
- exit - вызывается всегда, когда модуль выходит из своего контекста (и при выходе из приложения, если он в контексте)
- help - получение справки о модуле
- interrupt_call(alias) - прерывающий вызов, при использовании модуля как внеконтекстной команды (см. ниже)
- swallow - точка входа модуля для приложения
- swallow принимает значение, очищенное от регистра, направленное от приложения. В ответ на функцию должен последовать ответ для приложения
ctx - контекст данного приложения. особый объект для каждого модуля, к ключам которого можно обращаться как к атрибутам: ctx.METHOD. Он виден модулю без объявления
API контекста модуля:
- app_verison - версия приложения
- internal_path(path=None) - получить путь внутри модуля, преобразует относительный путь в путь внутри папки модуля
- extension(name) - загрузить расширение приложения по имени
- api_version - версия API приложения
- set_cleanable_cache(bool) - разрешает или запрещает удалять приложению кэш в папке модуля (не работает при форсированной очистке кэша)
- put_cache(filename, data) - загружает байты или строку в кэш файл
- remove_cache(filename) - удалит файл из кэша
- is_cached(filename) - закэширован ли файл
- get_cache(filename) - получить данные файла из кэша
- import_submodule(module_name) - импортировать вспомогательный скрипт из папки модуля
- import_submodule_by_path(path) - импортировать скрипт по пути
- get_shared_cache() - возвращает путь к общему кэшу для всех модулей
- version_array - массив с номером версии
- logger() - логгер
- get_mnemonic(mnem) - получает мнемоники
- set_mnemonic(mnem, value) - устанавливает мнемонику для модуля
- mnemonics() - два списка. Один - глобальные мнемоники, Второй - мнемоники модуля
- is_mnemonic(mnem) - есть ли мнемоника
- absolute_cfg(cfg) - получение абсолютной конфигурации
- relative_cfg(cfg) - получение конфигурации относительно
- fork() - получает экземпляр приложения (см. экземпляр приложения)
- put_config(cfg, value) - добавляет новую конфигурацию времени выполнения в модуль
- clear_mnemonics() - очищает мнемоники модуля
- pipe(name) - создает канал с другим модулем
- get_cache_path(filename=None) - получает путь или файл filename кэша модуля
- get_tempdata(cache) - получает данные из кэша времени выполнения
- put_tempdata(cache, data) - кладет данные в кэш времени выполнения
- is_cached_tempdata(cache) - проверяе, есть ли данные в кэше времени выполнения
- remove_tempdata(cache) - удаляет кэш времени выполнения
- mnem_manipulator() - доступ к манипулятору мнемонического сервера
- chmnemmod(mod) - смена мнемонического режима
- quit() - выход из контекста ввода модуля
- current_user_action - действие пользователя, с которым был отправлен последний запрос к приложению
- redefine_vsuggestions(array) - переопределить вертикальные предложения у модуля при входе в контекст
- redefine_gsuggestions(array) - переопределить горизонтальные предложения у модуля при входе в контекст
- get_vsuggestions() - вертикальные предложения у модуля при входе в контекст
- get_gsuggestions() - горизонтальные предложения у модуля при входе в контекст
- extend_gsuggestions(extlist) - расширяет у модуля горизонтальные предложения, при этом эффект сразу, вне контекста
Параметры модуля: В каждом модуле можно определить его индивидуальный параметры, и приложение должно обработать известные ему параметры модуля и принять их: Для хранения такого объекта нужно создать глобальный словарь SETTINGS или CONFIGS внутри модуля
Общие обязательные параметры (также актуальны для сервиса ввода, вывода и расширения)
- DEPENDENCIES: list - зависимости локальных расширений. Содержат строки - названия и версию. Пример: ocr==1.0.0 или ocr (их также можно разместить в файле с модулем requirements.txt)
- NAME: str - имя модуля внутри файла
- VERSION: str - версия модуля
- BUILD: int - номер версии
- DEPENDENCIES - зависимости от других расширений
- MINIMAL_API_VERSION - минимальная версия API приложения для модуля
- TARGET_API_VERSION - целевая версия API приложения для модуля
- ISOLATED_MODULE - изоляция модулей - запрет на открытие каналов. Если объявлен в глобал-конфиге - для всех модулей
- PIPELINE_MANIP: dict - манипулятор конвейера. Словарь содержит собственные параметры конвейера
- ENTER_CONTEXT: bool - определяет вхождение в контекст (происходит только после обработки первой команды к модулю)
- QUIT_COMMANDS: list - определяет команды выхода из текущего контекста. Если не задан, то (quit, exit, quit(), exit())
- DENY_MNEMONIC - запрещает обрабатывать мнемоники в контексте данного модуля
- PREF_MNEMMOD - предпочитаемый режим мнемоник (шестнадцатеричное число в виде строки)
- NOCACHE: bool - запрещает кэширование приложению. По умолчанию - false
Вхождение в контекст - указывает приложению, что после первого обращения к модулю все последующие команды к приложению будут именно к этому модулю, регистр будет игнорироваться. Для выхода из него будут использоваться QUIT_COMMANDS
Возможны три варианта
I. Строка для отправки
II. Массив строк с произвольным предварительным разбиением на пакеты
III. Ответ в формате словаря JSON
- status: bool - True, если успешно, False - если произошла ошибка (обяз)
- warnings: list - список идентификаторов предупреждений (если есть)
- message: str - непосредственно ответ от модуля или подробное описание ошибки (обяз.)
- pipeline_manip: dict - манипулятор конвейера, параметры конвейера (необяз.)
- log_messages: list - лог сообщения для логгера приложения
IV. Любой объект языка, который умеет обрабатывать приложение (за исключением списка, словаря, кортежа и строки)
Каждое расширение хранится в отдельной папке в папке extensions. Внутри него должен быть главный файл init.py Расширения также имеют свой контекст
API контекста, аналогично модулям за исключением отсутствия extension и наличия: module_path(path=None) - путь к модулю расширения module - модуль, связанный с расширением А также отсутствует поддержка автодополнения
У расширения нет точки входа, однако приложение должно каким-то образом назначить контекст расширения Поэтому решено определить два способа получения контекста
- Необязательное наличие функции put_ctx(ctx), для передачи контекста расширению (расширение должно как-то сохранить переданный контекст)
- Приложение может автоматически самостоятельно положить контекст в переменную ctx
Сервисы ввода хранятся в папках input_services, представляют собой скрипт с уникальным названием. Внутри скрипта должна находиться функция raw_input(*args), которая получает из различных устройств ввода и передает его в приложение. А также функция инициализации init(). И user_action() для действия пользователя при исчерпании лимита отправки пакетов. exit() при завершении работы приложения. А также сервис ввода может иметь свою конфигурацию с помощью словаря CONFIGS или SETTINGS
Сервисы вывода хранятся в папках delivery_services, представляют собой скрипт с уникальным названием. Должны иметь функцию send(packet) и init() - инициализация, finished(int count) - завершена отправка порции пакетов (перед всеми user_action вызывается и в конце отправки всех пакетов), begin() - начата отправка порции пакетов. exit() при завершении работы приложения. Отправляют пакет от конвейера к нужным устройствам вывода.
Контекст сервисов ввода и сервисов вывода сходен с контекстом модуля
include.txt - определяет конечное число сервисов, если файла нет - все доступные
Сервис доставки, сервис ввода, модуль, расширение - имеют объектное представление в программе (т.е класс, который может их породить, это значит, что данные объекты можно объявить и внутри программы)
Конвейер выполняет разбивку единого текста на пакеты. Конвейер имеет многоступенчатую обработку, которую определяет приложение. Базовой реализацией стандарта является движок контейнеризации.
Движок конвейеризации - выполняет разбивку пакетов, их структуризацию. Является ядром конвейера. Делится на два этапа: прегенерация и генерация
Прегенерация - разбивает на части единый ответ на пакеты
Генерация - сортирует пакеты по порядку
Термины:
- Пакет - строковые данные определенной длины, определяемой конфигурацией
- Порция (партия) пакетов - определенное количество пакетов, которые будут отправлены прежде чем будет достигнут лимит отправки конвейером
- Действие пользователя (user action) - блокирующая функция, которая возвращает логическое значение о принятии и завершении ожидания. Должна активироваться конвейером, когда достигается лимит отправки конвейером
Движки:
Rose - движок, который сортирует пакеты по старт:стоп:шаг
Dandelion - движок, который выполняет сортировку не всех данных, а каждой порции пакетов (см. эталонную реализацию)
Движком можно управлять в глобальной конфигурации PIPELINE_ENGINE, его может выбрать модуль
Параметры конвейера:
- start, stop, step - аналогично list[start:stop:step]
- max_packet_length - максимальный размер пакета (в символах или байтах, см. limit_type)
- packets_count - количество пакетов перед задержкой
- packet_delay - задержка в миллисекундах перед отправкой каждого пакета
- initial_delay - задержка перед первой отправкой
- special_delay - специальная задержка, обычно нужна для длительного отдыха
- after_limit - что сделать после преодоления лимита пакетов. Варианты: finish, user_action, initial_delay, special_delay
- allow_part_number - отображать номера пакетов
- limit_type - тип лимитирования (в прегенераторе). Байтовый - разделяет на пакеты, ограничивая число байтов в пакете. Символьный - делит на пакеты ,ограничивая число символов в пакете
- clear_text - очищает текст от непечатаемых символов
Мнемоники - упрощенный способ ввода запросов к приложению.
Приложение поддерживает несколько способов обработки мнемоник:
- Мнемоники приложения
- Мнемонический сервер (сервис ввода)
Мнемоники хранятся в глобальном файле (например, mnemonic.json) для внеконтекстных запросов. Каждый модуль (только модуль) может переопределить мнемонику для своего контекста, с помощью соответствующих методов
В файле мнемоник данные должны располагаться в виде:
"мнемоника": "запрос"
Мнемоники приложения представляют собой псевдоним для определенного запроса (alias)
Мнемоники могут иметь только числовое значение.
Получая запрос, приложение может найти соответствующую мнемонику и сопоставить для нее запрос. Мнемоника может перекрыть запрос к стандартному модулю
Специальный сервер, который осуществляет прием мнемоник из какого-то удаленного места и преобразования их в команды. Мнемонический сервер активно работает с приемом мнемоник, буферизацией данных и отправкой данных в приложение
Режимы мнемонического сервера:
0х0 - стандартный мнемонический сервер
0х1 - мнемонический сервер набора
Стандартный мнемонический сервер позволяет принимать мнемоники приложения из удаленного места
Мнемонический сервер набора выделяет 3 базовых мнемоники (могут расширяться):
- мнемоника выбора вводимого символа. Каждое нажатие выбирает символ для ввода в буфер
- мнемоника сброса введенного текста. Если буфер пуст - меняет режимы ввода, иначе спрашивает какое действие нужно: сброс или смена режима. Для смены режима понадобится ввести еще раз мнемонику 4, для смены режима нужно ввести мнемонику 1
- мнемоника принятия символа в буфер. Если символ введен, то мнемоника отправит запрос, если символ не введен - введет символ. Расширенные мнемоники, их присутствие необязательно, но позволяют комфортно управлять мнемониками
- повторный ввод последнего символа
- стирает последний символ
- выводит информацию о состоянии манипулятора
- сбрасывает курсор режима ввода (если например вводилось 5, а нужно вернуться снова к 1, то нужно использовать эту функцию)
- сбрасывает контекст модуля
Режимы ввода:
- numberroll - ввод цифр, а также символ пробела и пустой символ, который ничего не введет
- reverse_numberroll - ввод цифр в обратном порядке
- previous_roll - фиксирует изменение буфера и запоминает его состояния и позволяет их вводить
Модули и расширения приложения могут получать доступ к специальному объекту с помощью метода fork() в контексте. Поэтому к возвращаемому объекту - экземпляру приложения предъявляются требования к наличию определенных методов
Методы:
- process(request, user_action, mnemonic_handle=False, deny_cache=False, handle_ctx=True) - отправить запрос request к приложению напрямую.
- mnemonic_handle - обрабатывать мнемоники
- user_action - пользовательское действие при исчерпании лимита пакетов, функция
- deny_cache - запретить кэширование запроса
- handle_ctx - обрабатывать запрос учитывая контекст модуля
- direct_message(text, timeout=0) - отправка сообщения напрямую через сервис доставки минуя конвейер (например, если он занят) timeout - задержка после отправки
- send_message(text, user_action) - отправка сообщения через конвейер
- user_action - пользовательское действие при исчерпании лимита пакетов, функция
- clear_cache() - очистка кэша приложения
Поля:
- input_context - объект контекста модуля. Должен иметь методы get, set(value: string, module: Module), null, sethook(hook_method) - установка обработчика при смене контекста modules - модули
- input_services - сервисы ввода
- delivery_services - сервисы доставки
- extensions - содержит объекты, которые можно превратить в расширение с помощью метода build(module: Module)