Пользовательские пакеты поддержки Engee.Интеграции
Подсистема Engee.Интеграции обеспечивает взаимодействие между Engee и внешним оборудованием через клиентскую программу и пакеты поддержки.
Вы можете разрабатывать собственные пользовательские пакеты поддержки, расширяя функциональность подсистемы Engee.Интеграции. Пакет поддержки представляет собой модуль на языке Python, который выполняется в клиентской программе и возвращает результат обратно в Engee.
Чтобы подсистема Engee.Интеграции принимала пользовательские пакеты, Engee автоматически генерирует для них код на языке Julia. Это позволяет вызывать ваши функции прямо из командной строки Engee
или из блоков.
В основе этого механизма лежит технология 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 с рабочими примерами.
|
Давайте создадим пакет поддержки с собственной иерархией файлов. Для этого используем шаблон — папку extension, внутри которой есть директории devices и targets.
-
Устройство (Device) — это произвольный пользовательский класс, который не взаимодействует с моделями Engee и не использует их данные.
-
Таргет (Target) — это пользовательский класс, который взаимодействует с моделью Engee, получает из нее данные для обработки и исполняется на другой платформе (микроконтроллере, отдельном компьютере и т.д.).
В нашем примере мы создадим устройство с многофайловой структурой.
-
Создадим в папке
devicesновую папкуextdevice. -
Внутри
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}" -
Создадим файл
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-классов обязательно наследование от Для структур данных обязательно наследование от Все методы должны содержать полные аннотации типов для параметров и возвращаемого значения. Например:
Без аннотаций система не сможет корректно сгенерировать Julia-код и преобразовать типы данных между Python и Julia. Методы, предназначенные для вызова из блока, не должны начинаться с |
| Наглядно увидеть, как описанный выше механизм создания собственного пакета поддержки применяется на практике для работы с реальным оборудованием, можно в примере Сообщества: Разработка пакета поддержки оборудования для Engee.Интеграции. |
Для отладки пакета поддержки можно использовать встроенную систему логирования, подробнее см. в разделе Отладка пакетов поддержки. Для добавления информации о пакете поддержки в окно «Оборудование» реализуйте в классе устройства статический метод __get_opportunity_list, подробнее см. в разделе Отображение статуса пакета поддержки в интерфейсе клиентской программы.
Загрузка пользовательского пакета в Engee.Интеграции
В клиентской программе Engee.Интеграции выполните следующие шаги:
-
Откройте окно статусов оборудования.
-
Выберите вкладку «Расширения».

-
Нажмите «Добавить расширение».
-
Выберите папку с пользовательским пакетом поддержки, который хотите добавить в клиентскую программу.

В случае успешной загрузки в окне диагностики клиентской программы появится сообщение:
INFO: Расширение с именем extension было успешно загружено!
Чтобы получить в окне диагностики расширенную информацию о найденных классах пакета поддержки и его методах, запустите клиентскую программу в режиме разработчика:
engee-device-manager.exe -d
После успешной загрузки пакета поддержки в окне диагностики отобразится дополнительная информация:

Регистрация и использование пакета поддержки в Engee
Чтобы пользовательский пакет поддержки появился в EngeeDeviceManager.Devices.* или EngeeDeviceManager.Targets.*, выполните в командной строке Engee:
using Main.EngeeDeviceManager
using Main.EngeeDeviceManager.UTILS_API
utils = UTILS_API.Utils()
UTILS_API.syncExtensions()
Если изменили сигнатуры методов или добавили новые методы, вызовите syncExtensions() снова.
После регистрации пакет поддержки становится доступен для использования:
using Main.EngeeDeviceManager.Devices.EXTDEVICE
device = EXTDEVICE.Extdevice(123, 1.5)
Main.EngeeDeviceManager.Devices.EXTDEVICE.Extdevice("Extdevice", Base.UUID("0aac33b9-4ed5-4786-abb7-9855c0b80265"), ["complex_calculation", "get_status"], "Extdevice_0aac33b9-4ed5-4786-abb7-9855c0b80265_reply", Main.EngeeDeviceManager.Devices.EXTDEVICE.var"#complex_calculation#complex_calculation##0"{String, Base.UUID}("Extdevice", Base.UUID("0aac33b9-4ed5-4786-abb7-9855c0b80265")), Main.EngeeDeviceManager.Devices.EXTDEVICE.var"#get_status#get_status##0"{String, Base.UUID}("Extdevice", Base.UUID("0aac33b9-4ed5-4786-abb7-9855c0b80265")))
device.get_status()
"Device 123 operational with factor 1.5"
config = EXTDEVICE.DeviceConfig(2.5, 10, 3.14)
DeviceConfig(2.5, 10, 3.14)
result = device.complex_calculation(config)
CalculationResult(true, 42.21, 1.7851401913931458e9)
Ваш пакет поддержки будет автоматически загружаться при каждом запуске клиентской программы. Если он больше не нужен, то его можно удалить из автозагрузки в клиентской программе:

Отладка пакетов поддержки
Для отладки пакета поддержки используйте встроенную систему логирования. Добавьте в код:
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("сообщение")— ошибки.
Сообщения будут отображаться в окне диагностики клиентской программы. Примеры использования логирования вы также найдете в приложенном выше архиве.
Отображение статуса пакета поддержки в интерфейсе клиентской программы
Клиентская программа отображает статус установленного оборудования: загружен ли драйвер, подключено ли устройство. Для добавления информации о пакете поддержки в окно статусов оборудования реализуйте в классе устройства статический метод __get_opportunity_list.
Как это работает:
-
При запуске клиентская программа импортирует все модули из пакетов
devicesиtargets. -
Для каждого зарегистрированного класса проверяется наличие приватного метода
__get_opportunity_list. -
Если метод найден, он вызывается и возвращает список
OpportunityStatus. -
Клиентская программа опрашивает этот список каждые 3 секунды и отображает информацию в окне статусов оборудования.
Поля OpportunityStatus:
-
key— уникальный идентификатор строки. Пример:"my_driver" -
title_key— заголовок в интерфейсе. Пример:"MyDevice driver:" -
value_key— текст статуса. Пример:"Loaded"/"Not loaded" -
value_args— аргументы для форматирования значения. Пример:() -
color— цвет индикатора:"green","orange","red". Пример:"green" -
category— вкладка в окне статусов оборудования:-
"hardware"— «Оборудование» -
"protocols"— «Протоколы» -
"software"— «Интеграции с ПО» (по умолчанию) -
"embedded"— «Встраиваемые системы»
-
Если поле не указано, категория определяется автоматически по имени пакета устройства.
Пример для пользовательского пакета поддержки:
from devices.base_device import BaseDevice
class MyDevice(BaseDevice):
@staticmethod
def __get_opportunity_list() -> list[BaseDevice.OpportunityStatus]:
status = BaseDevice.opportunity_status
driver_loaded = False
try:
import some_library # замените на вашу зависимость
driver_loaded = True
except ImportError:
pass
results = []
if driver_loaded:
results.append(status(
key="my_driver",
title_key="MyDevice driver:",
value_key="Loaded",
color="green",
category="hardware",
))
else:
results.append(status(
key="my_driver",
title_key="MyDevice driver:",
value_key="Not loaded",
color="orange",
category="hardware",
))
results.append(status(
key="my_device",
title_key="MyDevice status:",
value_key="Available",
color="green",
category="hardware",
))
return results
После добавления этого метода пакет поддержки появится на вкладке «Оборудование» в клиентской программе Engee.Интеграции.