Сообщество Engee

Лабораторное оборудование в Engee - легко!

Автор
avatar-nikfilaretovnikfilaretov
Notebook

Разработка пакета поддержки оборудования для Engee.Интеграции

Инвентаризация - всегда полезно, ведь можно найти много неожиданных вещей. Так, например, я нашел у себя платформу для сбора данных Analog Discovery 2. Это крайне интересное железо, и я подумал, что было бы неплохо подружить его с Engee. К счастью, Engee.Интеграции позволяет писать собственные расширения для поддержки оборудования, чем я и воспользовался. В этом проекте я покажу на примере Analog Discovery 2 как писать собственные расширения для Engee.Интеграции.

Что такое Analog Discovery?

Analog Discovery - это целая серия устройств от Digilent для сбора данных, генерации сигналов, анализаторов логики и протоколов. При этом это буквально карманное устройство, которое работает от USB! Вот оно:

analog_discovery_2_obl_600.png

Что понадобится установить?

Чтобы мое расширение заработало, мне понадобится установить 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 и вывод попадает в лог клиентской программы:

image.png

Затем 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)

Здесь важно помнить следующее:

  1. Клиентская программа должна быть запущена, а соединение с Engee установлено.
  2. Для Python пути Windows задаются через двойной слэш. Например: C:\\srcs\\EDM_Development"
  3. После успешной загрузки расширения надо перезапустить ядро с помощью команды engee.clear_all() и заново запустить серверную программу, выполнив команду engee.package.start("Engee-Device-Manager") и заново установить соединение в клиентской программе

Давайте проверим, что мое расширение работает. Для этого сначала надо подготовить саму Analog Discovery: Первый аналоговый выход соединить с плюсом первого аналогового входа, а минус первого аналогового входа соединить с землей. Для наглядности посмотрим на картинку распиновки устройства:

analogdiscovery2_pinout_600.png

То есть 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" )
Получили данные: 0.725875469974313

Отсчет АЦП получен, значит расширение работает. Попробуем получить большое количество отсчетов и выведем график:

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)

Если график не виден, то ниже приведен пример такого графика:

newplot_1.png

Выводы

Engee.Интеграции - это расширяемая пользователем платформа, которая позволяет работать с любым оборудованием. Для создания пользовательского расширения требуется SDK от производителя и навыки работы в Python.