Engee Function
在模型中使用 Julia 代码。
类型: EngeeFunction
图书馆中的路径:
|
说明
Engee Function 程序块允许在 Engee 模型中使用 Julia 语言代码。
有关 Julia 编程语言的更多信息,请参阅编程 。 |
在 Engee Function 代码块中,允许使用*Julia*语言的大部分功能。但不允许在程序块中使用 Pkg 软件包管理器。 |
使用
要将 Julia 代码集成到 Engee 模型中,您需要
-
将块库
中 Basic/User Functions 部分的块 Engee Function 添加到模型中;
-
在设置窗口
块 Engee Function 的 Main 选项卡上点击 Edit Source Code 按钮打开源代码编辑器(EngeeFunctionCode):
源代码单元格
EngeeFunctionCode 源代码编辑器由包含 Julia 代码的函数单元组成。默认情况下,有三种单元格可用:辅助单元格(不可编辑)、 Component struct code 和 Step method code (单元格可隐藏):
您可以使用 Common code 单元中的
|
Engee Function 程序块的全部代码都可以在 Common code 单元中编写,从而使您可以完全控制组件结构、签名和函数数量。 |
要添加/删除其他功能单元,请点击 "Manage Methods(管理方法)"按钮 ,然后选中/取消选中所需的单元:
->
每个单元格负责区块 Engee Function 的一个独特功能。让我们详细了解一下它们:
-
信息单元格(不可编辑)--自动显示 Engee Function 区块的变量、输入和输出信号的属性(尺寸、类型、离散度)以及用户设置的其他参数。其内容根据程序块设置动态更新。单元格始终处于激活状态,但在方法控制菜单
中无法选择。它具有半透明文本,用于显示工具提示。
更改块参数不仅会影响信息单元格的内容,还会影响其他单元格中的工具提示,这些提示也会以半透明文本显示。 -
Define component struct - 添加单元格 Component struct code ,其中定义了块组件 Engee Function 的结构(从
AbstractCausalComponent
类型继承)。结构字段定义在不可编辑的行struct Block <: AbstractCausalComponent
和end
之间。默认情况下,会创建一个g
参数,并用gain
块的值进行初始化,而且Block()
构造函数不需要任何参数。
-
使用普通代码 - 添加单元格 Common code ,其中的代码以自由格式书写。默认情况下,单元格为空。例如,如果 Component struct code 中的标准结构声明不合适(由于不可编辑的 "struct Block <: AbstractCausalComponent"),可以禁用*Define component struct* 删除单元格,并在 Common code 中手动定义组件结构。
方法管理菜单中的任何函数也是如此—您可以编写自己的代码来代替 Common code 中的标准单元。
要使 Engee Function 正常工作,必须声明组件和函数。如果 Define component struct 和 Define step method 被禁用,则必须在 Common code 中设置它们的代码,否则程序块将无法运行。 要覆盖继承函数(覆盖,见下文),必须首先启用相应的单元格,清除其内容,然后在 Common code 中编写新代码。 -
Define step method - 添加单元格 Step method code ,该单元格定义了计算程序块 Engee Function 输出信号的 Step 方法。方法签名根据 Ports 标签中端口的相应标签(Label)值自动生成。该方法以函数形式表示(详见此处),并在每个仿真步骤中被调用。字段定义在不可编辑的行
function (c::Block)(t::Real, in1)
和end
之间。t::Real "的第一个参数是模拟时间,然后输入变量。输出的计算值通过关键字return
返回。 -
Define update method - 添加 Update method 代码*单元,其中的
update!
方法会在每个仿真步骤更新块 *Engee Function 的内部状态。第一个参数c::Block
是程序块结构,第二个参数t::Real
是模拟时间,然后传递输入信号。如果程序块没有内部状态,该方法可以保持为空,只返回c
。如果您需要定义多个 "更新!"方法或指定具有不同签名的方法,可以禁用*更新方法代码*单元格,并在 Common code 单元格中编写代码。编译器会自动检测到 update!
方法的存在,并将其用于模拟。 -
定义终止方法代码 - 添加*终止方法代码*单元,当块 Engee Function 的模拟终止时(使用`terminate!
方法),该单元将被执行。c::Block
的第一个参数是程序块的结构。默认情况下,该方法不会执行任何其他操作,只会返回c
。 -
覆盖类型继承方法 - 添加覆盖类型继承方法的*类型继承方法*单元格。
有关类型继承方法的更多信息_。
指定类型继承方法:
-
如果未选中(默认) - 则根据输入/输出端口说明中指定的规则继承输入/输出端口类型。
-
如果选中复选框 - 则根据源代码中*类型继承方法*单元的 `propagate_types`函数中指定的规则继承输入/输出端口类型。
-
propagate_types "函数接收一个参数—类型向量,每个输入信号一个类型,并返回一个输出类型向量。
-
默认单元代码:
function propagate_types(inputs_types::Vector{DataType})::Vector{DataType} # Функция, возвращающая массив типов сигналов на выходе. # Игнорируется, если используется алгоритм по умолчанию. # В данном случае учитываются тип входного сигнала и тип # параметра блока `gain`. input_type = first(inputs_types) # promote_type возвращает тип, к которому приводятся типы-аргументы # при арифметических операциях с объектами этих типов. output_type = promote_type(input_type, eltype(gain)) return [output_type] end
这里的类型继承是指输入信号的共用元素和在 "模块设置 "部分中设置的参数的元素。 参数设置.
-
-
覆盖尺寸继承方法 - 添加*尺寸继承方法*单元,覆盖尺寸继承方法。
有关尺寸继承方法的更多信息_.
指定尺寸继承方法:
-
如果未选中(默认) - 输入/输出端口的尺寸将根据输入/输出端口说明中指定的规则继承。
-
如果选中复选框 - 则根据源代码中 * 尺寸继承方法 * 单元的
propagate_dimensions
函数中指定的规则继承输入/输出端口尺寸。-
propagate_dimensions "函数接受每个输入信号的元组(尺寸)数组,并返回输出信号的尺寸数组。
-
默认单元代码:
function propagate_dimensions(inputs_dimensions::Vector{<:Dimensions})::Vector{<:Dimensions} # Функция, возвращающая массив размерностей сигналов на выходе. # Игнорируется, если используется алгоритм по умолчанию. # В данном случае учитываются размерности входного сигнала и # параметра блока `gain`. input_dimensions = first(inputs_dimensions) mock_input = zeros(input_dimensions) mock_output = mock_input .* gain return [size(mock_output)] end
在这里,为了进行维度继承,我们使用一个具有所需维度的数组(mock_input)(由零组成),并乘以在章节 "数据块设置 "中设置的参数元素。 参数设置中设置的参数元素,然后取其维度。
-
-
Use common method for types and dimensions inheritance - 添加 Common types and dimensions inheritance method 单元,使用通用方法同时覆盖类型和维度的继承。
有关类型和尺寸继承方法的更多信息_.
与类型(类型继承方法)或维度(维度继承方法)的特定方法不同,通用方法同时包含类型和维度:
-
如果未选中(默认)--则忽略通用方法,尺寸和输入/输出端口类型将根据输入/输出端口说明或 覆盖类型继承方法(如果启用)和 覆盖尺寸继承方法(如果启用)中指定的规则继承。
-
如果复选框已启用 - 则输入/输出端口的尺寸和类型将根据源代码中*常用类型和尺寸继承方法*单元的
propagate_types_and_dimensions
函数中指定的规则继承。
默认单元代码:
function propagate_types_and_dimensions(inputs_types::Vector{DataType}, inputs_dimensions::Vector{<:Dimensions})::Tuple{Vector{DataType}, Vector{<:Dimensions}} # Функция, возвращающая массив типов сигналов и массив # размерностей сигналов на выходе. Эту функцию можно использовать # если необходимо одновременно переопределить и алгоритм наследования # типов сигналов, и алгоритм наследования размерностей. outputs_types = propagate_types(inputs_types) outputs_dimensions = propagate_dimensions(inputs_dimensions) return outputs_types, outputs_dimensions end
依赖关系
要使用该单元格,请选择 覆盖类型继承方法 和 覆盖维度继承方法 复选框。
-
-
覆盖样本时间继承方法 - 添加*样本时间继承方法*单元格,覆盖计算步骤继承方法。
有关计算步骤继承方法的更多信息_。
SampleTime "计算步骤结构和 "propagate_sample_times "函数的代码不会自动添加到旧*Engee*模型的 EngeeFunctionCode 源代码中。要最终确定旧模型,请自行添加计算步骤结构和函数。 指定计算步骤的继承方法:
-
如果未选中(默认)--则使用 "高级 "选项卡中*采样时间继承方法*参数的预设计算步骤继承方法(默认为 "默认")。有关预设方法的更多信息,请参阅下文。
-
如果选中复选框,则忽略 "高级 "选项卡的预设方法(该参数不可用),使用 EngeeFunctionCode 源代码中*采样时间继承方法*单元格的独立方法。
要使用独立方法,必须找到
propagate_sample_times
功能行,并手动设置所需的计算步骤。默认单元格代码:
function propagate_sample_times(inputs_sample_times::Vector{SampleTime}, fixed_solver::Bool)::SampleTime # Функция, возвращающая время дискретизации блока. # Используется только в режиме наследования `Custom`. # Параметр fixed_solver говорит о том, используется решатель # с постоянным шагом (true) или с переменным (false). # Более сложный пример работы с наследованием времени # дискретизации блока можно посмотреть в документации. return first(inputs_sample_times) end
其中计算步骤有一个结构:
const SampleTime = NamedTuple{(:period, :offset, :mode), Tuple{Rational{Int64}, Rational{Int64}, Symbol}}
-
-
覆盖直接穿通设置方法 - 添加一个*直接穿通设置方法*单元,用于定义直接穿通连接。
有关定义直接穿通连接的更多信息
定义直接穿通连接:
-
如果未选中(默认),则无法使用直通连接。这意味着输出信号将不受输入端口值的监控,并允许设备打开回路。
-
如果选中复选框,则可使用直通连接。这意味着输出信号将直接受到输入端口值的监控。
默认单元代码:
function direct_feedthroughs()::Vector{Bool} # Функция, возвращающая массив булевых значений, определяющих, # сквозные соединения. Если i-ый элемент массива равен true, # то i-ый порт имеет сквозное соединение. # Игнорируется, если используется алгоритм по умолчанию. return [true] end
举例说明
function direct_feedthroughs()::Vector{Bool} if gain == 2 return [false] else return [true] end end
-
用于获取属性的常量和函数
要在块可执行代码中查找类型、大小和其他辅助信息,请在代码中使用以下常量 Engee Function :
-
BLOCK_NAME
- 块的名称。添加到 Engee 画布的每个程序块都有一个名称,可以通过此常量引用。例如,您可以在初始化错误时引用 BLOCK_NAME,以打印其中的代码块名称。 -
START_TIME
- 模型设置中的模拟开始时间。 -
STOP_TIME` - 模型设定的模拟结束时间。
-
INPUT_SIGNAL_ATTRIBUTES
- 每个输入端口的属性列表。例如,要查找第一个输入信号的属性,请使用INPUT_SIGNAL_ATTRIBUTES[1]
,其中1
是模块 Engee Function 的第一个输入端口。 -
OUTPUT_SIGNAL_ATTRIBUTES` - 列出每个输出端口的属性。例如,要查找第一个输出端口的属性,请使用
OUTPUT_SIGNAL_ATTRIBUTES[1]
,其中1
是程序块的第一个输出端口 Engee Function 。
要了解某个模块端口的其他信息,可以在常数 INPUT_SIGNAL_SIGNAL_ATTRIBUTES[i]
(其中 [i]
是输入端口编号)和 OUTPUT_SIGNAL_ATTRIBUTES[i]
(其中 [i]
是输出端口编号)之后添加点. ,以参考其信号属性。您可以通过以下调用函数了解更多信息:
-
dimensions
- 信号的维数。可缩写为dims
。 -
type
- 信号的类型。可缩写为tp
。 -
sample_time
- 计算步骤。它表示的结构类似于信号的属性,可以通过. 点访问。有两个访问函数可用:-
period
- 计算步骤的周期。完整的参考函数是sample_time.period
。可缩写为st.p
。 -
offset
- 计算步骤的偏移量。完整的调用函数是sample_time.offset
。可缩写为st.o
。
-
-
direct_feedthrough
- 指示循环端口是否打开。仅用于输入端口(仅检查输入端口的属性)。可缩写为df
。
包含所有常量和地址函数的示例模型 Engee Function :
struct Block <: AbstractCausalComponent end
function (c::Block)(t::Real, x1, x2)
y1 = [START_TIME, STOP_TIME]
y2 = collect(INPUT_SIGNAL_ATTRIBUTES[1].dimensions)
y3 = OUTPUT_SIGNAL_ATTRIBUTES[1].dims[1]
y4 = (INPUT_SIGNAL_ATTRIBUTES[2].type == Int64)
y5 = (OUTPUT_SIGNAL_ATTRIBUTES[4].tp == Bool)
y6 = INPUT_SIGNAL_ATTRIBUTES[1].sample_time.period
y7 = OUTPUT_SIGNAL_ATTRIBUTES[1].st.p
y8 = INPUT_SIGNAL_ATTRIBUTES[1].sample_time.offset
y9 = OUTPUT_SIGNAL_ATTRIBUTES[2].st.o
y10 = INPUT_SIGNAL_ATTRIBUTES[1].direct_feedthrough
y11 = INPUT_SIGNAL_ATTRIBUTES[2].df
return (y1, y2, y3, y4, y5, y6, y7, y8, y9, y10, y11)
end
使用定点和自定义总线
程序块 Engee Function 以及命令行 Engee,支持定点(
Fixed
)和用户定义的总线类型(BusSignal
)。这些结构有助于控制处理整数、实数、固定和复杂数据类型时的操作行为。
定点类型和函数 (Fixed-Point
)
文章Engee 中的定点算术(固定点 介绍了 Engee 中的定点操作。文章中给出的结构也适用于程序块 Engee Function ,因此程序块支持:
-
FixedPoint` - 所有定点数的抽象类型;
-
Fixed` - 固定数的具体类型,使用位表示法手动创建;
-
fi(…)`-使用数值和表示参数创建`Fixed`类型的值;
-
fixdt(…)
- 创建固定类型,并指定符号、宽度和小数位数。
例如
a = fi(5, 1, 16, 4) # Знаковое число, 16 бит, 4 дробных
b = fi(10, 0, 8, 0) # Беззнаковое целое 8 бит
T = fixdt(1, 24, 8) # Тип Fixed с 24 битами, 8 из них — дробные
c = Fixed(0x01ff, T) # Создание фиксированного числа напрямую по битовому представлению
Engee Function 中的固定数字可以:
-
作为参数传递;
-
在组件结构中使用它们(*组件结构代码*单元);
-
在*步骤方法代码*单元格中通过`emul`、
esum
、`ediv`对它们进行运算(更多详情请参见章节 转换函数和类型化算术运算:econvert`、esum
、emul
、ediv
。); -
应用于
propagate_types(…)
、 `propagate_dimensions(…)`中的计算。
使用自定义母线类型 (BusSignal
)
自定义总线类型允许您将输入和输出信号类型指定为结构化元组(NamedTuple
),并在 Engee Function 中说明名称、类型和尺寸。可用功能:
-
总线信号{...}` - 包含名称、类型和尺寸的总线类型;
-
get_bus_names(type)
- 获取信号名称列表; -
get_bus_types(type)
- 获取信号类型; -
get_bus_dimensions(type)
- 获取信号尺寸; -
get_names_types_dims(type)
- 一次获取所有信号; -
get_bus_signal_type(::NamedTuple)
- 用值定义BusSignal
类型。
总线类型定义和分析示例:
bus = (a = 1, b = [2.0, 3.0], c = (x = 3, y = 4))
bus_type = get_bus_signal_type(bus)
get_bus_names(bus_type) # => (:a, :b, :c)
get_bus_types(bus_type) # => (Int64, Vector{Float64}, NamedTuple{...})
get_bus_dimensions(bus_type) # => ((), (2,), ((), ()))
明确描述轮胎是可能的:
MyBus = BusSignal{(:s1, :s2), Tuple{Int64, Float64}, ((), ())}
signal = MyBus((s1 = 5, s2 = 6.4))
还支持嵌套总线:
Inner = BusSignal{(:x, :y), Tuple{Int, Int}, ((), ())}
Outer = BusSignal{(:a, :b), Tuple{Float64, Inner}, ((), ())}
在区块中使用 Engee Function
固定 "和 "总线信号 "类型可用于程序块 Engee Function 的不同部分:
-
在程序块参数中 - 可以通过
fi(…)`指定定点值,总线结构可以作为 `NamedTuple
传递。总线类型可通过 `get_bus_signal_type(…)`自动定义。 -
在 Common code 单元中,您可以定义数据类型(
Fixed
,BusSignal{…}
)、组件结构(struct Block
)、创建辅助函数或初始化值。如果*步骤方法代码*或*组件结构代码*被禁用,还可以将基本逻辑移至此处。 -
在 Component struct code 单元中,在描述结构的字段时,可以使用
Fixed
类型的值,如果字段表示复合信号,也可以使用BusSignal
类型的值。 -
在*步骤方法代码*单元中—执行程序块的基本逻辑。在这里,固定数字和总线信号可以参与计算、比较和结构处理。
-
在*类型继承方法*单元中—可以使用 "固定 "和 "总线信号 "类型来分析输入并设置组件的输出类型。
-
在 Dimensions inheritance method 单元中,您可以使用来自总线的
Fixed
数据和值来定义输出尺寸。 -
在*更新方法代码*单元中—如果程序块有内部状态,可使用`Fixed`或`BusSignal`等字段在每个模拟步骤中存储或更改该状态。
-
在*终止方法代码*单元中—如果结构包含相应字段,则允许使用这些类型记录最终状态。
-
在*常用类型和维数继承方法*单元中—如果要同时处理两种类型和维数,`Fixed`和`BusSignal`也可以参与计算逻辑。
因此,Fixed
、fi(…)
、fixdt(…)
、BusSignal
和 get_bus
函数适用于 Engee Function 模块配置和运行的所有方面—无论是运行时还是信号类型和尺寸生成时。它们可以在所有单元的参数和源代码中自由使用(如果它们的操作得到正确的遵守)。
转换函数和类型化算术运算:econvert`、esum
、emul
、ediv
。
在程序块 Engee Function 以及命令行 Engee 中都提供了函数,允许您执行指定输出类型的算术运算,通过四舍五入和溢出控制在不同类型间转换数值,以及使用专门规则(
_promote_type
)预定义结果的类型。这些函数在逻辑 Engee Function 中使用,以源代码单元*步骤方法代码*、*通用代码*和其他方式编写。
可用函数:
-
econvert
- 在类型间转换数值; -
esum
,esub
,emul
,ediv
- 指定输出类型的算术运算; -
emul!
- 带有结果缓存的矩阵乘法运算; -
*_promote_type
- 按输入类型计算输出结果的函数。
这些函数可用于标量和数组。结果总是以用户明确指定的类型返回(或自动输出),这使得程序块行为稳定且符合预期。 |
支持的类型
本程序套件中的所有函数都适用于以下类型:
-
实型:Float16"、"Float32"、"Float64"。
-
整数Int8`、Int16`、Int32`、Int64`、Int128`、UInt8`、UInt16`、UInt32`、UInt64`、UInt128`。
-
逻辑:
Bool
. -
固定的Fixed`(通过
fi(…)
或fixdt(…)
创建) -
复数:
Complex{T}
,其中T
是上述任何类型
使用点语法 (.
),函数还可以与数组和矢量化表达式一起使用。下面让我们更详细地讨论函数。
使用 econvert
转换数字
使用 econvert
函数,您可以通过指定四舍五入方法和溢出处理方式,将数值转换为给定类型:
econvert(type::Type{T1}, var::T2; rounding_mode::RoundingMode=RoundDown, overflow::Bool=true)
给你
-
type
是将数值转换成的类型。如果var
是复数,则指定基实数类型。例如,econvert(Float32, Complex{Int16}(1 + 2im))`将返回`Complex{Float32}
; -
var
- 要转换的值; -
rounding_mode
- 舍入方法(RoundDown
,RoundUp
,RoundNearest
,RoundNearestTiesAway
,RoundToZero
); -
overflow
- 如果为true
,则使用换行;如果为false
,则启用饱和 - 值受类型范围限制。econvert
函数可用于比较前的类型转换、算术表达式内的类型转换、在条件中处理信号时的类型转换以及其他情况。
使用 esum
、esub
、emul
、ediv
、`emul!
类型化算术运算 esum
, esub
, emul
, ediv
, emul!
的函数具有相同的接口:
esum(x1, x2, out_type, overflow=true, rm=RoundDown)
esub(x1, x2, out_type, overflow=true, rm=RoundDown)
emul(x1, x2, out_type, overflow=true, rm=RoundDown)
ediv(x1, x2, out_type, overflow=true, rm=RoundDown)
emul!(cache, x1, x2, out_type, overflow=true, rm=RoundDown)
参数:
-
x1
,x2
- 运算的参数(可以是标量或数组); -
out_type
- 生成和返回结果的类型; -
overflow
- 启用溢出(true
)或饱和(false
); -
rm
- 舍入方法(请参阅上面的列表)。
函数 esum
, esub
, emul
, ediv
返回与指定的 out_type
相对应的值。
emul!"函数用于加快处理矩阵的速度:它不会创建一个新数组,而是将结果写入已分配的 "缓存 "中。重要的是 eltype(cache)
必须与 out_type
一致。
结果取决于 out_type
:它不仅会影响最终结果,还会影响中间计算的行为,这对 Fixed
和 Complex
尤为重要。
使用 *_promote_type
输出结果类型
如果块参数(例如 SumType
或 MulType
)被指定为 Inherit`
字符串,结果类型将根据输入数据类型使用 *_promote_type
函数自动确定。
如果需要计算操作的结果类型,请使用上述函数:
-
确定添加相同类型
T
的n
值时的输出类型:sum_promote_type(::Type{T}, n::Integer=1, hdlsetup::Bool=false)
-
确定两个不同类型
T1
和T2
的n
值相加时的输出类型:sum_promote_type(::Type{T1}, ::Type{T2}, n::Integer=1, hdlsetup::Bool=false)
-
定义添加任意多个不同类型参数时的输出类型:
sum_promote_type_from_inputs(types::Type...; hdlsetup::Bool=false)
-
确定累加器在累加相同类型
T
的n
值时的类型:sum_accumulator_promote_type(::Type{T}, n::Integer=1, hdlsetup::Bool=false)
-
确定两个不同类型的
T1
和T2
值相加时累加器的类型:sum_accumulator_promote_type(::Type{T1}, ::Type{T2}, n::Integer=1, hdlsetup::Bool=false)
-
确定任意多个不同类型参数相加时的累加器类型:
sum_accumulator_promote_type_from_inputs(types::Type...; hdlsetup::Bool=false)
-
确定相同类型
T
数值相乘时的输出类型:mul_promote_type(::Type{T}, hdlsetup::Bool=false)
-
定义两个不同类型
T1
和T2
相乘时的输出类型:mul_promote_type(::Type{T1}, ::Type{T2}, hdlsetup::Bool=false)
-
确定除以
T
类型的单个值时的输出类型:div_promote_type(::Type{T}, hdlsetup::Bool=false)
-
确定 T1 类型的值除以 T2 类型的值时的输出类型:
div_promote_type(::Type{T1}, ::Type{T2}, hdlsetup::Bool=false)
给你
-
hdlsetup "是一个布尔参数,用于确定是否应考虑特定硬件平台("TargetHardware")。默认值:
hdlsetup = TargetHardware == "C" ? false : true
-
函数可方便地用于 * 类型继承方法 * 单元,以根据输入和参数准确确定程序块的输出类型。
使用示例
下面是一个实现表达式 "a*x + b "的组件示例。通过 MulType
和 SumType
参数指定了中间乘法和最终加法的类型。如果指定了继承
,则类型会自动派生。
使用 a*x + b`
的示例
参数 Engee Function :
a = fi(5, 1, 16, 4)
b = fi(11, 1, 24, 7)
MulType = "Inherit"
SumType = "Inherit"
区块代码(单元格 通用代码、尺寸继承方法 和 类型继承方法):
-
*通用代码*单元格:
struct Block{SumType, MulType, aType, bType} <: AbstractCausalComponent a::aType b::bType function Block() sum_type = OUTPUT_SIGNAL_ATTRIBUTES[1].type mul_type = if MulType == "Inherit" mul_promote_type(INPUT_SIGNAL_ATTRIBUTES[1].type, eltype(a)) else MulType end new{sum_type, mul_type, typeof(a), typeof(b)}(a, b) end end
-
单元格 尺寸继承方法:
function propagate_dimensions(inputs_dimensions::Vector{<:Dimensions})::Vector{<:Dimensions} input_dimensions = first(inputs_dimensions) mock_input = zeros(input_dimensions) mock_output = mock_input .* a .* b return [size(mock_output)] end
-
单元格 类型继承方法:
function propagate_types(inputs_types::Vector{DataType})::Vector{DataType} mul_type = if MulType == "Inherit" mul_promote_type(inputs_types[1], eltype(a)) else MulType end sum_type = if SumType == "Inherit" sum_promote_type(mul_type, eltype(b)) else SumType end return [sum_type] end
因此,"econvert"、"esum"、"emul"、"ediv "函数和 "promote_type "规则允许您控制块 Engee Function 中的计算类型和行为。它们支持所有主要数字类型,包括定点值和复数值,可用于计算、比较、继承逻辑以及使用 Engee Function 块自定义组件行为。
诊断函数 "警告"、"停止模拟"、"暂停模拟"、"信息
在 Engee Function 块的源代码中还提供了 warning
、stop_simulation
、pause_simulation
和 info
函数,用于在仿真阶段与模型诊断系统交互,允许在诊断窗口 中显示信息。这些函数可在以下源代码单元中使用:
-
*组件结构代码。
-
*步骤方法代码
-
*更新方法代码
-
*终止方法代码
-
*通用代码
警告"、"停止仿真"、"暂停仿真 "和 "信息 "函数只能在模型仿真期间执行的代码段中使用。 因此,尽管形式上允许在 Component struct code 单元中使用这些函数,但实际上将它们放在这里毫无意义:该单元仅用于描述程序块结构,而非可执行逻辑。为使诊断功能正常工作,应将其置于受支持的模拟方法中:步方法、更新方法*、终止方法*,如果相应的方法被重载,则应放在*通用代码*中。正确位置示例
|
以此类推,如果禁用相应的标准单元格(如*定义更新方法*、*定义步进方法*等),则可以手动覆盖*通用代码*单元格中在模拟过程中调用的任何支持方法(如`更新!、`终止
、步进
)。
警告"、"停止模拟"、"暂停模拟 "和 "信息 "函数可在*通用代码*单元中使用,以便正确操作:
-
在覆盖负责执行模型的方法行为的函数内—如
step
、update!
、terminate!
。例如,如果在 Common code 中定义了通常在 Step method code 单元中定义的函数,那么其中的诊断函数就会正常工作。 -
在负责执行模型的方法中调用的辅助函数内部。也就是说,如果在 Common code 中定义了辅助函数,并在
step
中使用了该函数,那么在该函数内部调用诊断函数也将正常工作。重写 update!
或其他方法并不能取代step
的强制实现(function (c::Block)(t, in…)
)。*Engee*需要该函数作为模拟的入口。
在*通用代码*中正确覆盖`update!`的示例:
# Структура с Component struct code (если отключена, то должна быть обязательно вынесена в Common code)
struct Block <: AbstractCausalComponent
g::Real
function Block()
new(gain)
end
end
# Step method (обязательный метод, вызываемый на каждом шаге симуляции)
function (c::Block)(t::Real, in1)
c = update!(c, t, in1)
return c.g .* in1
end
# Переопределенный метод update!
function update!(c::Block, t::Real, in1)
if t == 5.0
info("update triggered at t = $t") # диагностическое сообщение
end
return c
end
这将导致在模型诊断窗口中出现以下信息:
接下来,让我们仔细看看诊断功能本身:
-
警告"--"warning(msg::String) "函数输出一条警告信息。此时模拟仍在继续。这对于指出非关键问题或需要注意但不会停止执行的状态非常有用。示例
if t == 5.0 || t == 7.5 warning("time == $t") end
-
stop_simulation` -
stop_simulation(msg::String)
函数会立即终止仿真并显示终止信息。该函数用于指示不可能或不希望继续仿真的临界状态。示例if t == 5.0 stop_simulation("time == $t") end
-
pause_simulation
- 函数pause_simulation(msg::String)
暂停仿真执行并显示指定的暂停信息。可以使用 待续 按钮手动恢复仿真:该功能可用于分析模型在相关时间点的状态。例如
if t == 5.0 pause_simulation("time == $t") end
-
info` -
info(msg::String)
函数输出一条信息消息。它用于显示中间值而不影响模拟执行。示例if t == 5.0 || t == 7.5 info("time == $t") end
端口
输入
输入端口 -
输入端口
标量 | 向量 | 矩阵
Details
以标量、向量或矩阵形式指定的输入端口。
在程序块的 Ports 选项卡中使用以下选项配置输入端口:
-
Label - 设置输入端口的名称。默认情况下,*标签*单元格未填入(未设置名称)。
-
类型 - 输入信号数据类型。选择其中一个选项:
-
无限类型(除 "继承 "外的所有类型)- 检查输入端口是否应用了特定类型的信号。为输入端口选择特定数据类型。支持的信号:
Float16
、Float32
、Float64
、ComplexF32
、ComplexF64
、Bool
、Int8
、Int16
、Int32
、Int64
、Int128
、UInt8
、UInt16
、UInt32
、UInt64
、UInt128
。 -
Inherit
(默认)- 从链接块继承数据类型。可以是任何数据类型。
-
-
Size - 输入信号的尺寸:
-
继承所有尺寸(默认为
-1
)- 继承应用于输入端口的信号尺寸(信号可以有任何尺寸)。 -
定义维数 - 输入信号必须有指定数量的元素。维数以 Julia 注释(元组)形式指定,例如,"(2,) "表示包含两个元素的一维信号,"(2, 3, 4) "表示多维信号。如果指定了"-1",维数将被继承。
-
继承其中一个维数 - 继承输入端口信号的维数,并明确指出数据结构。例如,
(-1, 2)
- 继承第一个维度,明确指定第二个维度。
-
-
输出总线类型 - 输入总线类型,在输入信号数据类型类型选择了`BusSignal`(总线)类型的情况下,取代输入信号维度*Size*。默认值为 "BusSignal{(), Tuple{}, ()}"。要使 Engee Function 块了解输入的是哪条总线,只需将 类型 设为 `继承`即可。只有输出信号才需要明确指定类型。
Engee Function 程序块不会继承输出端口上的总线,尽管它可以接收输入端口上的总线。要继承输出端口的总线(用于传递给其他程序块),必须在*输出总线类型*参数中明确设置总线类型。 -
直接穿通 - 定义直接穿通连接:
-
如果选中(默认),则可使用直接穿通连接。这意味着输出信号直接由输入端口的值控制。
-
如果取消选中复选框,则无法使用直接穿通连接。这意味着输出信号不受输入端口值的控制,并允许设备打开回路。
-
输出
输出端口 -
输出端口
标量 | 向量 | 矩阵
Details
以标量、向量或矩阵形式指定的输出端口。
在程序块的 Ports 选项卡中使用以下选项配置输出端口:
-
Label - 设置输出端口的名称。默认情况下,Label 单元未填充(未设置名称)。
-
类型 - 输出信号的数据类型。请选择其中一个选项:
-
定义类型(除`继承`外的所有类型) - 定义输出信号的数据类型。为输出信号选择特定的数据类型。支持的信号:
Float16
、Float32
、Float64
、ComplexF32
、ComplexF64
、Bool
、Int8
、Int16
、Int32
、Int64
、Int128
、UInt8
、UInt16
、UInt32
、UInt64
、UInt128
。 -
Inherit
(默认)- 继承输出数据类型。当有多个不同类型的输入信号时,计算最小的公共类型。可以是任何数据类型。
-
-
Size - 输出信号的尺寸:
-
继承所有尺寸(默认为
-1
)--继承应用于输出端口的信号尺寸。输出信号的维度来自广播机制—Julia 会自动将较小数据集的维度扩展为较大数据集的维度,以正确继承维度。 -
Defined dimensions - 输出必须有指定数量的元素。维数以 Julia 符号(元组)指定,例如,"(2,) "表示包含两个元素的一维信号,"(2, 3, 4) "表示多维信号。如果指定了"-1",维数将被继承。
-
Inherit one of the dimensions - 继承应用到输出端口的信号的维数,并明确指出数据结构。例如,
(-1, 2)
- 继承第一个维度,明确指定第二个维度。
-
-
输出总线类型 - 输出总线类型,在输出信号数据类型类型选择了`BusSignal`(总线)类型的情况下,替换输入信号尺寸*Size*。默认值为 "BusSignal{(), Tuple{}, ()}"。要使 Engee Function 块输出总线,必须明确指定总线类型。
参数
主要参数
输入端口数 -
定义输入端口数
1(默认)
Details
定义程序块的输入端口数。输入端口数*的值将与输入端口数相对应。
输出端口数 -
决定输出端口的数量
1(默认)
Details
定义程序块的输出端口数。输出端口数*的值将与输出端口数相对应。
取样时间 -
计算步骤之间的间隔
`-1(默认值)
Details
以非负数指定计算步骤之间的间隔。要继承计算步骤,请将此参数设置为 -1
。
高级
为非标量输出使用外部缓存 — 为非标量输出使用外部缓存
已禁用(默认) | 已启用
Details
指定对非标量(多维)输出信号使用外部缓存,以节省 Engee RAM。当 Engee Function 块只有一个输出端口时使用:
-
如果未选中(默认)--则不使用外部缓存。
-
如果选中复选框 - 输出信号将能够接受额外的 "缓存 "参数,这必须在源 EngeeFunctionCode 中加以考虑。如果程序块有一个输出端口,"缓存 "参数会自动添加到 Step method code 单元的函数参数列表中,除非端口维度被明确设置为"()"(标量)。在源代码中,需要根据输出信号的维数编写函数。函数可以在 Common code 中定义:
-
如果输出信号是标量:
function (c::Block)(t::Real, x) return c.g * x end
-
如果输出信号为非标量信号
function (c::Block)(t::Real, cache, x) cache .= c.g .* x nothing end
其中
t
为时间,x
为参数(来自输入端口的信息)。即使程序块参数中没有时间参数,也必须指定时间参数。
-
采样时间继承法 — 计算步骤继承法的定义
默认` | 离散
| `连续
Details
如果在 EngeeFunctionCode 源代码中选择了*覆盖取样时间继承方法*复选框,则*高级*选项卡中的*取样时间继承方法*设置将消失。在这种情况下,计算步骤继承方法由函数单元的*采样时间继承方法*代码决定。 |
根据所选值定义计算步骤的继承方法:
如果在*采样时间*字段中指定了"-1",计算步骤将根据所选值("默认"、"离散"、"连续")按照*采样时间继承方法*字段中指定的方法继承。在其他情况下(采样时间不等于 -1 ,采样时间大于或等于 0 )--程序块 Engee Function 使用*采样时间*字段的指定值,忽略*采样时间继承方法*。
|
-
Default
- 默认计算步骤继承方法。当程序块 Engee Function 不是离散或连续时,总是使用Default
继承方法。该方法可获取任何类型的计算步长。选择此方法后, Engee Function 区块将按如下方式继承计算步长:-
如果程序块没有输入端口,则输出为连续计算步骤。
-
如果输入具有所有相同的计算步骤,则输出具有与输入相同的计算步骤。
-
如果输入计算步骤中有连续计算步骤,则输出也有连续计算步骤。
-
如果输入计算步数中包含定中微分(FiM,Fixed-in-Minor),没有连续计算步数,且求解器具有可变步数,则输出为定中微分。
-
如果输入中没有连续计算步长和定小计算步长,且并非所有计算步长都相等,则只考虑输入中的离散计算步长,其中一个变式为真:
-
如果离散计算步长的最大公约数与输入计算步长相同或使用了恒定步长求解器,则输出为具有最大公约数步长的离散计算步长。
-
如果输入离散计算步数的可变步长求解器和最大公约数与输入计算步数不一致,则输出为固定小数。
-
-
-
Discrete
- 获得离散计算步长的继承方法。如果选择此方法,程序块 Engee Function 将根据以下原则继承计算步骤:-
如果输入具有连续或固定的小计算步长 - 则输出为具有求解器步长的离散计算步长(即使求解器为可变步长)。
-
如果输入有离散计算步长,则输出为离散计算步长与输入离散计算步长的最大公除数。
-
-
Continuous
- 用于获得连续计算步骤的继承方法,与输入计算步骤无关。
重载 propagate_sample_times
函数的示例,其工作类似于 Default
继承方法:
+
function propagate_sample_times(inputs_sample_times::Vector{SampleTime}, fixed_solver::Bool)::SampleTime
nonnegative_sample_times = filter(
st -> st.period >= 0,
collect(values(inputs_sample_times)),
)
finite_periods = filter(
st -> !isinf(st.period),
nonnegative_sample_times,
) .|> (st -> st.period)
output_sample_time = if !isempty(nonnegative_sample_times)
if allequal(nonnegative_sample_times)
first(nonnegative_sample_times)
elseif any(st -> st.period == 0 // 1 && st.mode == :Continuous, nonnegative_sample_times)
(period = 0 // 1, offset = 0 // 1, mode = :Continuous)
elseif any(st -> st.mode == :FiM, nonnegative_sample_times) && !fixed_solver
(period = 0 // 1, offset = 0 // 1, mode = :FiM)
elseif (
all(x -> x.period > 0 // 1, nonnegative_sample_times) &&
(fixed_solver || gcd(finite_periods) in finite_periods)
)
(period = gcd(finite_periods), offset = 0 // 1, mode = :Discrete)
else
(period = 0 // 1, offset = 0 // 1, mode = :FiM)
end
else
(period = 0 // 1, offset = 0 // 1, mode = :Continuous)
end
return output_sample_time
end
如果`propagate_sample_times`函数返回`(period = 0 // 1, offset = 0 // 1, mode = :Discrete)`,则计算步长将与求解步长一起被视为离散。 |
参数设置
参数数量 -
指定参数个数
1(默认)
Details
区块中使用的参数个数。
参数 -
将参数定义为变量
标量 | 向量 | 数组
Details
定义一个参数,用于块 Engee Function 的源代码。您可以在参数中指定
-
Name
- 参数名称; -
Value
- 参数的值。作为值,您可以设置 Julia 语言中的任何表达式。
参数的值和名称可以在 "参数 "选项卡中更改。第一个参数的名称(默认存在)是 gain,其值是 `2
。新参数的名称为 parameter2
,然后按升序排列。新参数的默认值为 0
。
在 Engee 变量窗口 中可用的全局变量可以设置为参数选项卡变量,以便插入到程序块的源代码中 Engee Function 。如果参数名称和全局变量名称相同,参数值将自动从全局变量中替换。
要设置总线参数,可将 Value
参数设置为命名元组的总线值,例如 (s1 = 5, s2 = 4)
。
在 Engee Function 源代码中,在参数选项卡中设置的块参数是全局变量,而在![]() |
让我们考虑一下全局变量与源代码中的变量完全对应的情况。例如,我们定义了三个全局变量 , , ,其值分别为 1
、2
、3
。这三个全局变量都被用作块 Engee Function 的参数:
这样,添加参数后的源代码将如下所示:
struct Block <: AbstractCausalComponent
a::Real
b::Real
c::Real
function Block()
new(a_param, b_param, c_param)
end
end
function (c::Block)(t::Real, x::Vector{<:Real})
return (c.a .* x .+ c.b) ./ c.c
end
该源代码定义了 Block
结构,可用于自定义组件的行为。示例使用块参数名称 a_param
、b_param
和 c_param
分别指定结构 、 和 的参数。代码还定义了一个方法 function(c::Block)(t::Real, x::Vector{<:Real})
用块参数 对向量 x
的每个元素进行缩放,然后加上 ,并将结果除以 。这样就可以根据块参数的值灵活地修改和归一化向量 x
。
考虑只使用参数选项卡参数的情况:
struct Block <: AbstractCausalComponent end
function (c::Block)(t::Real, x::Vector{<:Real})
return (a_param .* x .+ b_param) ./ c_param
end
这些参数将成为代码块中的全局变量。这意味着它们在程序块代码的任何部分都是可用的,而无需在每个函数或代码块中重复定义。这大大简化了代码,使您可以轻松更改参数选项卡中的参数,而不会影响源代码。
让我们考虑一下参数与源代码不完全一致的情况。例如,参数 a_param
等于 100
。在源代码中有一个结构域 :
struct Block <: AbstractCausalComponent
a::Real
function Block()
new(a_param/10)
end
end
function (c::Block)(t::Real, x::Vector{<:Real})
c.a
end
在这段代码中,"a_param "参数用于通过构造函数初始化 "struct Block "结构的 字段,构造函数将参数值除以 "10"。在这种情况下,block 函数会返回字段 。
变量可以在源代码中直接设置为全局变量:
a = 1;
b = 2;
с = 3;
struct Block <: AbstractCausalComponent; end
function (c::Block)(t::Real, x::Vector{<:Real})
return (a .* x .+ b) ./ c
end
代码创建了一个 Block
结构,并定义了一个函数,对全局变量 、 和 执行数学运算,并将其应用于 x
变量。
如果*不想使用 "参数 "选项卡中的参数,可以直接在源代码中初始化变量:
struct Block <: AbstractCausalComponent; end
function (c::Block)(t::Real, x)
a = 1;
b = 2;
c = 3;
return (a .* x .+ b) ./ c
end
代码创建了一个 Block
结构,并定义了一个函数,对本地变量 、 和 执行数学运算,并将它们应用到 x
变量。
让我们考虑一下最有效、最正确的执行方式。为使 Engee Function 程序块正常工作,请选中 Engee Function 程序块的 "主要 "选项卡上的 "为非标量输出使用外部缓存 "选项。源代码将如下所示
struct Block{Ta, Tb, Tc} <: AbstractCausalComponent
a::Ta
b::Tb
c::Tc
function Block()
Ta = typeof(a_param); Tb = typeof(b_param); Tc = typeof(c_param)
all(isreal, (a_param, b_param, c_param)) ||
error("Параметры блока должны быть вещественными")
all(x->isempty(size(x)), (a_param, b_param, c_param)) ||
error("Параметры блока должны быть 标量ами")
new{Ta, Tb, Tc}(a_param, b_param, c_param)
end
end
function (c::Block)(t::Real, cache::Vector{<:Real}, x::Vector{<:Real})
cache .= (c.a .* x .+ c.b) ./ c.c
nothing
end
此类代码只能在 Common code 单元中编写,因为标准单元不允许编辑组件结构定义和函数签名。 |
结构体的 、 和 字段代表在构造函数中经过严格类型检查的块参数。每个参数都必须是实数标量(Real
),以确保运行时计算的准确性。
块() "构造函数会检查传入参数 、 和 的类型。如果其中至少有一个参数不是实数标量或尺寸不匹配(必须是标量),构造函数就会生成错误并给出相应的提示信息。经过检查后,构造函数会用指定类型 Ta
、Tb
和 Tc
的值 , 和 初始化结构字段。
为 Block 实例定义的可计算函数需要时间 t
、外部缓存和实数向量 x
。该函数使用 "Block "结构的字段 、 和 计算值,然后将其写入缓存。这样可以通过重复使用提供的缓存来避免不必要的内存分配。
因此,"Block "结构通过使用外部缓存来存储计算结果,提供了严格的数据类型管理和高效的资源利用。
如果要更改块的参数,必须使用`可变`结构。让我们来看一个例子:
mutable struct Counter{T} <: AbstractCausalComponent
limit::T
iter::T
function Counter()
isempty(size(limit)) || error("Предел блока $BLOCK_NAME должен быть 标量ом")
isreal(limit) || error("Предел блока $BLOCK_NAME должен быть вещественным числом")
T = typeof(limit)
iter = zero(T)
new{T}(limit, iter)
end
end
function (c::Counter)(t::Real)
return c.iter
end
function update!(c::Counter, t::Real)
c.iter += 1
if c.iter > c.limit
c.iter = zero(c.iter)
end
c
end
计数器 "结构是一种 "可变 "数据类型,用于对给定限制的迭代进行计数,并且是严格类型的。结构中的 limit
和 iter
字段代表块参数:
-
limit "是计数器的极限值;
-
iter`是计数器的当前值。
在结构体构造函数中,将检查(验证)limit`参数是否为标量和实数数据类型。之后,`iter
字段将被初始化为相应`T`类型的空值。函数 update!
会更新计数器 的状态,每次调用都会将 iter
的值增加一个。如果 iter
的当前值超过了 limit
,它将被重置为零,并允许计数器循环回到初始状态。
在 Engee Function 代码块的源代码中,可以使用 include 来引用外部代码。这样就可以在源代码中初始化外部代码中的变量(如果有的话)。
|
实际数据类型以及对可能数据类型的支持取决于代码块中的自定义代码。 |
代码示例
本示例介绍了基于 Julia 代码与 Engee 模型整合的程序块Discrete-Time Integrator 的简化实现。集成方法选择直接欧拉法。 在 Engee Function 程序块的 "高级 "选项卡上,设置 "采样时间继承方法 "参数的 "离散 "值。然后在源代码单元格中填写如下内容:
-
在单元格 Common code :
mutable struct Block{T} <: AbstractCausalComponent const dt::Float64 state::T gain::Float64 function Block() dt = OUTPUT_SIGNAL_ATTRIBUTES[1].sample_time.period state = initial_condition gain = k new{typeof(state)}(dt, state, gain) end end
-
在细胞 Step method code 中:
return c.state
-
在 * 更新方法代码 * 单元格中:
c.state += in1 * c.dt * c.gain return c
将产生以下源代码:
参数 initial_condition
和 k
已在程序块设置 Engee Function 的 Parameters 选项卡中初始化:
在模型模拟的第一步,"c.state "模块的内部状态由 "initial_condition "参数值初始化。
然后,在每一步计算中,程序块都会将内部状态 c.state
作为输出返回,并在 update!
方法中重新计算其值。
组件的结构是在 Common code 中重新定义的,而不是在 Component struct code 中,因为需要更灵活的定义:结构必须是可变的,并由与状态类型相对应的 T 类型参数化。 Component struct code 中的标准定义只适用于不可变和无参数化的结构。 |
摘要
Engee Function 中的注释允许您在模型中直接在块名称下显示块参数。要添加注释,请打开设置窗口 区块 Engee Function 并单击*注释*选项卡。选择所需的区块属性标记,并在文本编辑器中添加。
在该选项卡上:
-
左侧是可用参数列表(隐藏参数除外)。
-
右侧是文本编辑器,您可以在这里以
%<ИмяПараметра>
的格式指定带有标记的注释。 -
可以通过手动、自动完成或
按钮传输参数。
离开编辑器后(例如在编辑器外单击),注释将被应用:标记将自动替换为实际参数值,生成的文本将显示在块名称下方(如果块名称位于顶部,则显示在块名称上方)。
要删除注释,必须删除编辑器中相应的标记。
可用标记
属性标记会被当前参数值自动替换。可用的标记如下
-
*端口:
%<Inputs>
,%<Outputs>
- 输入和输出端口的数量;%<InputPort1Type>
,%<OutputPort1Type>
,%<InputPort1Size>
,%<OutputPort1Size>
- 数据类型和信号维度。 -
*时间特性:
%<SampleTime>
- 离散化;%<SampleTimeInheritanceMethod>
- 离散化继承方法。 -
*代码块:
%<ComponentStructCode>
,%<StepMethodCode>
- 步骤结构和方法代码。 -
参数:
%<Parameters>
,%<Parameter1Name>
,%<Parameter1Value>
- 参数名称和值。 -
Inclusion flags:
%<DefineComponentStruct>
,%<UseCommonCode>
,%<DefineStepMethod>
,%<DefineUpdateMethod>
,%<DefineTerminateMethod>
- 包含相应的代码部分。 -
*参考方法:
%<OverrideTypesInhMethod>
、%<OverrideDimsInhMethod>
、%<OverrideSampleTimeInhMethod>
- 类型继承设置。 -
其他:
%<UseExternalCache>
- 使用外部缓存。