Лабораторное оборудование в Engee - легко!
Разработка пакета поддержки оборудования для Engee.Интеграции
Инвентаризация - всегда полезно, ведь можно найти много неожиданных вещей. Так, например, я нашел у себя платформу для сбора данных Analog Discovery 2. Это крайне интересное железо, и я подумал, что было бы неплохо подружить его с Engee. К счастью, Engee.Интеграции позволяет писать собственные расширения для поддержки оборудования, чем я и воспользовался. В этом проекте я покажу на примере Analog Discovery 2 как писать собственные расширения для Engee.Интеграции.
Что такое Analog Discovery?
Analog Discovery - это целая серия устройств от Digilent для сбора данных, генерации сигналов, анализаторов логики и протоколов. При этом это буквально карманное устройство, которое работает от USB! Вот оно:
Что понадобится установить?
Чтобы мое расширение заработало, мне понадобится установить WaveForms от Digilent. Если бы у меня был линукс, то мне бы понадобился дополнительно рантайм Adept2. Также мне понадобится клиентская программа Engee.Интеграции.
В самом Engee мне понадобится установить основной пакет поддержки Engee.Интеграции, универсальный для любого оборудования. После его установки будет доступна ссылка на скачивание клиента. Эти шаги описаны в документации
Пишем расширение - с чего начать?
Многие производители железа выпускают SDK - наборы библиотек и разных инструментов для работы с железом в пользовательском приложении. Digilent тоже сделал свой SDK, который является просто заголовочным файлом для библиотеки dwf. Однако, расширения для Engee.Интеграции создаются на языке Python, и заголовочный файл тут попросту неприменим. Но Python умеет загружать dll-ки напрямую при помощи модуля ctypes. Запомним это.
Архитектура расширения
Расширение представляет собой класс, который наследуется от встроенного класса BaseDevice из модуля devices.base_device. Этот модуль уже входит в клиентскую программу, искать его не надо. Далее, в самом Engee мы должны “зарегистрировать” этот класс и перезапустить ядро Engee. Подробнее про этот механизм рассказано в документации.
Для написания расширения можно как вызывать функции SDK напрямую из методов этого класса, так и сделать над SDK обертку и вызывать обертку из методов.
Однако, в первом случае, нам придется перезагружать расширение каждый раз, когда мы внесем изменения, а во втором - просто перезапускать клиентскую программу. Второй вариант выглядит более предпочтительным.
ВАЖНО! Чтобы расширение было "видно" в момент подгрузки в Engee, требуется соблюдать специальную структуру папок. Я добавил к проекту архив с моим расширением с соблюдением этой структуры.
Разработка расширения - на что обратить внимание
Разработка расширения не слишком отличается от обычной разработки, но хотелось бы обратить внимание на некоторые особенности написания кода и его отладки.
Пожалуй самое важное - это полная спецификация методов класса расширения: типы аргументов, типы возвращаемых значений. Это является требованием к методам расширения. Посмотрим на пример полностью специфицированного метода:
def get_sample(self,channel:int) -> float:
c_channel = c_int(int(channel))
measure = float()
measure =float(acq_single(self.hDevice,c_channel))
return measure
Как видите, мы явно указываем что вход channel - это целое, а выходом будет строго float.
Второе - это отладка. Чтобы посмотреть что происходит внутри кода надо добавить вывод, однако print() не сработает. Чтобы организовать вывод информации в окно логов клиентской программы, надо использовать модуль main_logger:
from main_logger import MainLogger
logger = MainLogger()
Использовать этот механизм очень просто. Посмотрим на код, обеспечивающий подключение к устройству:
dwf.FDwfDeviceOpen(devid_c,byref(hdwf))
dwf.FDwfGetLastError(byref(error))
match error.value:
case 0:
logger.info(f"Device with id {devid} connected" )
case 3:
logger.info(f"Device with id {devid} is already connected, skipping" )
case _:
dwf.FDwfGetLastErrorMsg(szerr)
logger.error(f"Error occured: {szerr.value}")
hdwf = c_int(-1)
return hdwf
Здесь мы выводим информацию при помощи методов объекта logger и вывод попадает в лог клиентской программы:
Затем Python удивил меня тем, что даже если аргумент функции задан как int, то при попытке передать в этот аргумент float интерпретатор выполнит такую операцию молча. Поэтому в методах класса мне пришлось делать явное приведение типов:
def start_sine(self, channel:int,freq:float,amp:float) -> None:
c_chan = c_int(int(channel))
c_freq = float(freq)
c_amp = float(amp)
sinewavegen_config(self.hDevice,c_chan, c_freq, c_amp)
pass
Тестирование расширения
После написания кода, можно загружать свое расширение. Для этого в Engee надо выполнить следующие команды:
module_path = "/path/to/module"
using Main.EngeeDeviceManager.Devices.UTILS
using Main.EngeeDeviceManager.UTILS_API
utils = UTILS.Utils()
UTILS_API.loadExtension(utils, module_path)
Здесь важно помнить следующее:
- Клиентская программа должна быть запущена, а соединение с Engee установлено.
- Для Python пути Windows задаются через двойной слэш. Например:
C:\\srcs\\EDM_Development" - После успешной загрузки расширения надо перезапустить ядро с помощью команды
engee.clear_all()и заново запустить серверную программу, выполнив командуengee.package.start("Engee-Device-Manager")и заново установить соединение в клиентской программе
Давайте проверим, что мое расширение работает. Для этого сначала надо подготовить саму Analog Discovery: Первый аналоговый выход соединить с плюсом первого аналогового входа, а минус первого аналогового входа соединить с землей. Для наглядности посмотрим на картинку распиновки устройства:
То есть W1 соединяется с 1+, а 1- соединяется с Ground.
Теперь перейдем в Engee и выполним следующий код (предполагается, что клиент запущен и связь с Engee установлена):
Теперь мы можем создать объект для работы с устройством:
using Main.EngeeDeviceManager.Devices.DIGILENT
mydig = DIGILENT.Digilent()
Обратите внимание, что устройство было названо по имени нашего класса и переведено в верхний регистр, а конструктор называется так же как класс.
Наконец, можно приступать к тестированию. Для этого будем генерировать синусоиду частотой 2 Гц и амплитудой 1 В. Настроим захват данных с частотой 10 кГц, и получим один отсчет:
mydig.connect(-1)
mydig.start_sine(0,2.0,1.0)
mydig.start_acq(0,10000.0)
sample = mydig.get_sample(0)
println("Получили данные: $sample" )
Отсчет АЦП получен, значит расширение работает. Попробуем получить большое количество отсчетов и выведем график:
using Plots
ACQ = Vector{Float64}()
for i in range(1,30)
sleep(0.01)
push!(ACQ,mydig.get_sample(0))
end
plot(ACQ, xlabel="№ Отсчета", ylabel = "Напряжение, В", seriestype = :steppre)
Если график не виден, то ниже приведен пример такого графика:
Выводы
Engee.Интеграции - это расширяемая пользователем платформа, которая позволяет работать с любым оборудованием. Для создания пользовательского расширения требуется SDK от производителя и навыки работы в Python.