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

Работа с пользовательскими шинами BusSignal в блоках оборудования

Перед использованием пользовательских типов шин рекомендуется ознакомиться с блоками Создание шины и Выбор из шины.

В Engee предусмотрена поддержка пользовательских структурированных сигналов через тип BusSignal (подробнее см. здесь). Этот тип позволяет объединить логически связанные сигналы в единую структуру — шину, где каждому элементу соответствует имя, тип и размерность (например, temperature::Int, position::Vector{Float64}, status::Bool).

Такие структуры удобны при взаимодействии с внешним оборудованием, которое возвращает или принимает целые наборы параметров. Так, вместо того чтобы передавать данные как неструктурированный набор:

(10, 3.2, [1, 2, 3])

Можно использовать шину с явно заданными именами:

(temperature = 10, pressure = 3.2, position = [1, 2, 3])

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

Тип BusSignal записывается следующим образом:

BusSignal{Names, Types, Dimensions, :BusName}

где:

  • Names — кортеж имен сигналов ((:a, :b, :c));

  • Types — соответствующие типы (Tuple{Int, Float64, Vector{Float64}});

  • Dimensions — размерности каждого элемента ((), (), (3,)) для скаляров и вектора длины 3);

  • BusName — имя шины (заданное пользователем).

Тип BusSignal параметризуется именами сигналов (Names), базовыми типами (BaseTypes), размерностями (Dims), и именем шины (BusName). Например, шину с тремя сигналами можно описать так:

bus_type = BusSignal{(:s1, :s2, :s3), Tuple{Int, Float64, Float64}, ((), (), (3,)), :MyBus}
В BaseTypes указываются только базовые типы (например, Int, Float64), а не контейнеры (например, Vector{Int}). Размерности массивов задаются отдельно в Dims.

Здесь:

  • :s1, :s2, :s3 — имена сигналов;

  • Tuple{Int, Float64, Float64} — типы данных сигналов;

  • (), (), (3,) — размерности сигналов (например, s3 — массив длины 3).

  • MyBus — имя шины.

    В имени сигнала и шины допустимы любые символы, кроме кавычки (").

Пример простого использования:

# Задаем значение типа шины (структуру без данных, поэтому круглые скобки)
bus_type = BusSignal((:a, :b), (Int, Float64), ((), ()), :MyBus)

# Создаем объекты этой шины, у параметров типа NamedTuple фигурные скобки
bus1 = BusSignal{(:a, :b), Tuple{Int, Float64}, ((), ()), :MyBus}((a = 5, b = 6.4))

# Через ключевые аргументы
bus2 = BusSignal{(:a, :b), Tuple{Int, Float64}, ((), ()), :MyBus}(a = 5, b = 6.4)

# Через позиционные аргументы (varargs)
bus3 = BusSignal{(:a, :b), Tuple{Int, Float64}, ((), ()), :MyBus}(5, 6.4)

Шины могут быть вложенными, например:

Inner = BusSignal{(:x, :y), Tuple{Int, Int}, ((), ()), :InnerBus}
Outer = BusSignal{(:a, :b), Tuple{Float64, Inner}, ((), ()), :OuterBus}

Для анализа типа доступны функции:

get_names_types_dims(::Type{BusSignal{Names, Types, Dimensions, BusName}}) where {Names, Types, Dimensions, BusName}   # все параметры сразу
get_bus_names(::Type{BusSignal{Names, Types, Dimensions, BusName}}) where {Names, Types, Dimensions, BusName}         # кортеж имен сигналов
get_bus_types(::Type{BusSignal{Names, Types, Dimensions, BusName}}) where {Names, Types, Dimensions, BusName}         # кортеж базовых типов
get_bus_dimensions(::Type{BusSignal{Names, Types, Dimensions, BusName}}) where {Names, Types, Dimensions, BusName}    # кортеж размерностей
get_bus_name(::Type{BusSignal{Names, Types, Dimensions, BusName}}) where {Names, Types, Dimensions, BusName}          # имя шины
get_bus_signal_type(x::NamedTuple)

Функция get_bus_signal_type особенно удобна: она позволяет автоматически определить тип шины по значению, например:

bus = (s1 = 5, s2 = [4.3, 5.4], s3 = (a = 4, b = 5.5))
bus_type = get_bus_signal_type(bus)
# bus_type равен: BusSignal{(:s1, :s2, :s3), Tuple{Int64, Float64, BusSignal{(:a, :b), Tuple{Int64, Float64}, ((), ())}}, ((), (2,), ())}

Эта функциональность активно применяется в блоках оборудования. Чтобы не задавать типы шин вручную, такие блоки настраиваются масками — интерфейсом, в котором достаточно указать только имена и количество сигналов.

Далее рассмотрим, как работает эта схема: от параметров маски до автоматического формирования BusSignal внутри блока оборудования.

Пользовательские шины в блоках оборудования

Рассмотрим пример — динамическое формирование типов шин BusSignal на примере блока оборудования UM Cosimulation. Этот блок имеет следующие параметры:

hardware bussignal 1

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

Для того чтобы посмотреть как это работает — откройте маску блока (ПКМ по блоку/Маска/Посмотреть маску), перейдите во вкладку «Редактор кода» → Global и в обратном вызове blockChangedCallback найдите следующий код:

engee.set_param!(engee.gcb(), "InputPort1BusType" => "BusSignal($(mask.parameters.m_input_signal_names.value), Tuple(fill(Float64, $(mask.parameters.m_num_input_signals.value))), ntuple(_ -> (), $(mask.parameters.m_num_input_signals.value)), $(mask.parameters.m_input_bus_name.value))")

engee.set_param!(engee.gcb(), "OutputPort1BusType" => "BusSignal($(mask.parameters.m_output_signal_names.value), Tuple(fill(Float64, $(mask.parameters.m_num_output_signals.value))), ntuple(_ -> (), $(mask.parameters.m_num_output_signals.value)), $(mask.parameters.m_output_bus_name.value))")

В этом коде:

  • m_num_input_signals — количество входных сигналов;

  • m_num_output_signals — количество выходных сигналов;

  • m_input_signal_names — имена входных сигналов (например, (:a,:b,:c));

  • m_output_signal_names — имена выходных сигналов;

  • m_input_bus_nameимя входной шины (Symbol, например, :InputBus);

  • m_output_bus_nameимя выходной шины (Symbol, например, :OutputBus).

Значения m_input_bus_name и m_output_bus_name должны приходить в виде корректных литералов Julia Symbol, то есть с двоеточием: :InputBus, :MyHwIn, и т.д.

В коде устанавливается путь к текущему блоку с помощью функции gcb(), после чего считываются значения параметров маски m_input_signal_names, m_num_input_signals, m_output_signal_names, m_num_output_signals, а также m_input_bus_name и m_output_bus_name. Эти значения используются для формирования строкового представления типа BusSignal с заданными именами сигналов, базовыми типами (Float64), размерностями () и именем шины (четвертый аргумент типа Symbol). Чтобы значения считывались, имена параметров необходимо объявить в блоке Engee Function. Для этого перейдите в настройки блока оборудования и нажмите Посмотреть под маску, чтобы открыть параметры блока Engee Function. На вкладке Parameters указаны программные названия параметров из маски:

hardware bussignal 3

Затем функция engee.set_param! автоматически обновляет параметры Output bus type для Input port 1 (программное имя InputPort1BusType) и Output bus type (программное имя OutputPort1BusType) для Output port 1 на вкладке Ports, подставляя сгенерированные типы шин для входного и выходного портов:

hardware bussignal 4

Таким образом, вся настройка сводится к заполнению полей маски. Типы шин формируются автоматически, без ручного редактирования вкладки Ports. Это делает блок удобным, адаптивным и готовым к повторному использованию.