Документация Engee

Пользовательские пакеты поддержки Engee.Интеграции

Страница в процессе разработки.

Подсистема Engee.Интеграции позволяет организовать взаимодействие между Engee и внешним оборудованием с помощью клиентской программы и пакетов поддержки оборудования.

Вы можете создавать свои собственные пользовательские пакеты поддержки, расширяя функциональность подсистемы Engee.Интеграции. Пакет поддержки представляет собой модуль на языке Python, который исполняется на клиентской программе и возвращает результат обратно в Engee.

Чтобы подсистема Engee.Интеграции принимала пользовательские пакеты, Engee автоматически генерирует для них код на языке Julia. Это позволяет вызывать ваши функции прямо из командной строки Engee img 41 1 2 или из блоков.

В основе этого механизма лежит технология RPC (Remote Procedure Call — удаленный вызов процедур). Вы вызываете функцию в Julia, которая отправляет аргументы по сети на клиентский компьютер. Там ваш пакет поддержки выполняет соответствующую Python-функцию с этими параметрами и возвращает результат обратно в Engee.

Архитектура выполнения

Важно понимать распределение выполнения кода:

  • Код на Python — выполняется непосредственно на компьютере пользователя в процессе клиентской программы;

  • Код на Julia — выполняется в подсистеме Engee и координирует взаимодействие.

Например, при вызове:

using Main.EngeeDeviceManager.Devices.EXTDEVICE
device = EXTDEVICE.Extdevice()
device.function()

Julia-код выполняется в подсистеме Engee, а Python-функция function() выполняется на клиентском компьютере, и ее результат возвращается в Engee.

Доступные пакеты Python

При разработке пользовательских пакетов поддержки вам доступны все стандартные пакеты Python, а также следующие сторонние пакеты:

Список доступных пакетов Python
Пакет Описание

aiohttp

Асинхронный HTTP клиент/сервер фреймворк (asyncio)

autoflake

Удаляет неиспользуемые импорты и переменные

bandit

Статический анализатор безопасности Python кода

beartype

Быстрая гибридная проверка типов во время выполнения

black

Бескомпромиссный форматировщик кода

certifi

Пакет для предоставления Mozilla’s CA Bundle

cffi

Интерфейс внешних функций для вызова Си кода из Python

docformatter

Форматирует строки документации в соответствии с PEP 257

flake8

Модульный проверщик исходного кода: pep8 pyflakes и другие

gpib-ctypes

Интерфейс GPIB для Python, реализованный с использованием ctypes

hid

Привязки ctypes для hidapi

httpx

HTTP клиент следующего поколения

intelhex

Библиотека Python для манипуляций с файлами Intel HEX

isort

Утилита/библиотека для сортировки импортов Python

jinja2

Очень быстрый и выразительный шаблонизатор

jupyter-client

Реализация протокола Jupyter и клиентские библиотеки

jupyter-core

Основной пакет Jupyter

jwcrypto

Реализация стандартов JOSE Web

mdurl

Утилиты для URL Markdown

msgpack

Сериализатор MessagePack

multidict

Реализация multidict

numpy

Фундаментальный пакет для вычислений с массивами в Python

ordered-set

OrderedSet, который запоминает свой порядок

patchelf

Утилита для изменения динамического linker и RPATH ELF исполняемых файлов

pathspec

Утилита для сопоставления путей файлов в стиле gitignore

platformdirs

Определение подходящих платформо-специфичных директорий

pydantic

Валидация данных с использованием подсказок типов Python

pydantic-core

Основная функциональность для валидации и сериализации Pydantic

pydantic-settings

Управление настройками с использованием Pydantic

pyduinocli

Обертка вокруг arduino-cli

pyflakes

Пассивный проверщик Python программ

pymodbus

Полнофункциональный стек протокола Modbus на python

pyserial

Расширение для последовательного порта Python

pyusb

Модуль доступа к USB из Python

pyvisa

Привязки Python VISA для инструментов GPIB, RS232, TCPIP и USB

pyvisa-py

Чистая Python реализация библиотеки VISA

pyyaml

Парсер и эмиттер YAML для Python

pyzmq

Привязки Python для 0MQ

redis

Python клиент для базы данных Redis и key-value хранилища

requests

HTTP для людей на Python

setuptools

Легко загружайте, собирайте, устанавливайте, обновляйте и удаляйте пакеты Python

toml

Библиотека Python для Tom’s Obvious, Minimal Language

untokenize

Преобразует токены в исходный код (с сохранением пробелов)

urllib3

HTTP библиотека с потокобезопасным пулом соединений, отправкой файлов и прочее

Пример создания мультифайлового пакета поддержки

Для вашего удобства мы подготовили архив с готовой структурой проекта и примерами кода. В архиве вы найдете полную структуру папок devices и targets с рабочими примерами.

Давайте создадим пакет поддержки с собственной иерархией файлов. Для этого используем шаблон — папку module, внутри которой есть директории devices и targets.

  • Девайс (Device) — это произвольный пользовательский класс, который не взаимодействует с моделями Engee и не использует их данные.

  • Таргет (Target) — это пользовательский класс, который взаимодействует с моделью Engee, получает из нее данные для обработки и исполняется на другой платформе (микроконтроллере, отдельном компьютере и т.д.).

В нашем примере мы создадим девайс с многофайловой структурой.

  1. Создадим в папке devices новую папку extdevice.

  2. Внутри extdevice создадим файл extdevice.py со следующим кодом:

import time
from devices.base_device import BaseDevice
from .models import DeviceConfig, CalculationResult

class Extdevice(BaseDevice):
    def __init__(self, device_id: int, calibration_factor: float) -> None:
        self.device_id = device_id
        self.calibration_factor = calibration_factor

    def __del__(self) -> None:
        pass

    def complex_calculation(self, config: DeviceConfig) -> CalculationResult:
        # Сложные вычисления с использованием конфигурации
        result_value = (config.parameter_a * config.parameter_b +
                       config.parameter_c) * self.calibration_factor

        return CalculationResult(
            success=True,
            value=result_value,
            timestamp=time.time()
        )

    def get_status(self) -> str:
        return f"Device {self.device_id} operational with factor {self.calibration_factor}"
  1. Создадим файл models.py в той же папке extdevice:

from devices.base_models import BaseModel

class DeviceConfig(BaseModel):
    parameter_a: float
    parameter_b: int
    parameter_c: float

class CalculationResult(BaseModel):
    success: bool
    value: float
    timestamp: float

Для пользовательских RPC-классов обязательно наследование от BaseDevice (если это девайс) или от BaseTarget (если это таргет)

Для структур данных обязательно наследование от BaseModel!

Все методы должны иметь полные аннотации типов для параметров и возвращаемого значения. Например:

# НЕПРАВИЛЬНО - без аннотаций
def __init__(self, param1, param2):
    pass

# ПРАВИЛЬНО - с полными аннотациями
def __init__(self, param1: int, param2: float) -> None:
    pass

def calculate(self, x: float, y: int) -> str:
    return "result"

Без аннотаций система не сможет корректно сгенерировать Julia-код и преобразовать типы данных между Python и Julia.

Наглядно увидеть, как описанный выше механизм создания собственного пакета поддержки применяется на практике для работы с реальным оборудованием, можно в примере Сообщества: Разработка пакета поддержки оборудования для Engee.Интеграции.

Отладка пакетов поддержки

Для отладки вашего расширения можно использовать встроенную систему логирования. Добавьте в ваш код:

from main_logger import MainLogger

class Extdevice(BaseDevice):
    def __init__(self, device_id: int, calibration_factor: float) -> None:
        self.logger = MainLogger()
        self.logger.info(f"Initializing device {device_id}")
        self.device_id = device_id
        self.calibration_factor = calibration_factor

    def complex_calculation(self, config: DeviceConfig) -> CalculationResult:
        self.logger.debug("Starting complex calculation")
        # ... ваш код ...
        self.logger.info("Calculation completed successfully")
        return result

Доступные уровни логирования:

  • logger.debug("сообщение") — отладочная информация;

  • logger.info("сообщение") — информационные сообщения;

  • logger.warning("сообщение") — предупреждения;

  • logger.error("сообщение") — ошибки.

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

Регистрация и использование пакета поддержки в Engee

Перед регистрацией убедитесь, что ваша структура папок соответствует примеру из архива.

После создания пакета поддержки нужно сгенерировать соответствующий код на Julia и зарегистрировать пакет в Engee.

Сначала посмотрим список существующих девайсов:

engee> using Main.EngeeDeviceManager.Devices.

CAN                   COM                   ECHO                  HID
HTTP                  L502BOARD             LOGITECHG29WHEEL      MODBUSMASTER
SOCKET                TFLEXDOCS             THRUSTMASTERJOYSTICK  THRUSTMASTERTHROTTLE
UM                    UTILS                 VISA

Нашего пакета поддержки EXTDEVICE здесь еще нет.

Укажем путь к нашей папке module и загрузим пакет (подробнее про используемые далее методы см. статью Программное управление для работы с файловой системой и пакетами поддержки):

engee> module_path = "/path/to/module"
engee> using Main.EngeeDeviceManager.Devices.UTILS
engee> using Main.EngeeDeviceManager.UTILS_API
engee> utils = UTILS.Utils()
engee> UTILS_API.loadExtension(utils, module_path)

Если все прошло успешно, то в логах клиентской программы появится сообщение:

INFO     | Extension with name: module was loaded successfully!

Процедура обновления пакета поддержки:

Если вы изменили только реализацию существующих функций (без изменения сигнатур, типов возврата или имен функций):

  • Достаточно вызвать UTILS_API.loadExtension(utils, module_path) повторно;

  • ИЛИ перезапустить клиентскую программу — пакет загрузится автоматически.

Если вы добавили новые функции, классы или модели (наследники BaseModel):

  • Вызовите UTILS_API.loadExtension(utils, module_path);

  • Затем выполните engee.clear_all();

  • Перезапустите подсистему Engee.Интеграции: engee.package.start("Engee-Device-Manager");

Теперь пакет поддержки зарегистрирован. Перезагрузим ядро Engee и перезапустим подсистему Engee.Интеграции:

engee> engee.clear_all()
# Ждем перезагрузки ядра
engee> engee.package.start("Engee-Device-Manager")

Снова посмотрим список девайсов:

engee> using Main.EngeeDeviceManager.Devices.

CAN                   COM                   ECHO                  EXTDEVICE
HID                   HTTP                  L502BOARD             LOGITECHG29WHEEL
MODBUSMASTER          SOCKET                TFLEXDOCS             THRUSTMASTERJOYSTICK
THRUSTMASTERTHROTTLE  UM                    UTILS                 VISA

Теперь пакет поддержки EXTDEVICE в списке и его можно использовать:

engee> using Main.EngeeDeviceManager.Devices.EXTDEVICE
engee> device = EXTDEVICE.Extdevice(123, 1.5)
engee> device.get_status()
"Device 123 operational with factor 1.5"

engee> config = EXTDEVICE.DeviceConfig(2.5, 10, 3.14)
engee> result = device.complex_calculation(config)

Ваш пакет поддержки будет автоматически загружаться при каждом запуске клиентской программы. Если он больше не нужен, то его можно удалить из автозагрузки:

engee> UTILS_API.deleteExtension(utils, module_path)
Функция deleteExtension удаляет пакет поддержки из списка автозагрузки. Чтобы изменения вступили в силу, необходимо полностью перезапустить подсистему Engee.Интеграции.

Таким образом, мы создали «мост» между Engee и пользовательским кодом на Python, выполнили вычисления на клиентской программе и получили результат в Engee.