功能
在模型中使用Julia代码。
类型: EngeeFunction
图书馆中的路径:
|
资料描述
座 功能 允许您在*Engee*模型中使用Julia代码。
| 在页面上阅读更多关于Julia编程语言的信息 编程. |
| 在街区里 功能 使用*Julia*语言的大部分功能是可以接受的。 但是,不提供在块中使用*Pkg*包管理器。 |
使用
要将Julia代码集成到*Engee*模型中,您必须:
-
向模型添加块 功能 从*基本部分/自定义函数*块库
; -
在 设置窗口
在块的*主*选项卡上 功能 点击*编辑源代码*按钮打开源代码编辑器(EngeeFunctionCode):

源代码单元格
EngeeFunctionCode源代码编辑器由带有Julia代码的功能单元组成。 默认情况下,有三个单元格可用:辅助(不可编辑), 元件结构代码 和 步骤方法代码 (单元格可以隐藏):

|
要连接其他源代码文件,您可以使用该函数
|
| 整个块代码 功能 你可以在单元格中写 通用代码 通过获得对组件结构、签名和函数数量的完全控制。 |
要添加/删除其他功能单元,请单击"管理方法"按钮
并选中/取消选中相应的单元格。:
→ 
每个单元负责块的独特功能 功能 . 让我们更详细地看看它们:
-
信息单元格(不可编辑)-自动显示块变量 功能 ,输入和输出信号的属性(尺寸,类型,离散度)和其他用户定义的参数。 其内容根据块设置动态更新。 单元格始终处于活动状态,但未在"方法管理"菜单中选择该单元格。
. 它有一个半透明的文本,显示提示。
更改块参数不仅会影响信息单元格的内容,还会影响其他单元格中的提示,这些提示也会以半透明文本显示。 -
定义组件结构-添加单元格 元件结构代码 ,其中定义了块组件的结构 功能 (继承自类型
AbstractCausalComponent). 结构的字段在不可编辑的行之间定义结构块<:AbstractCausalComponent和结束. 默认情况下,将创建参数g,由块值初始化增益,以及构造函数座()它不接受论点。
-
使用通用代码—-添加单元格 *通用代码 ,其中代码以自由形式编写。 默认情况下,单元格为空。 例如,如果标准结构在 元件结构代码 不适合(由于不可编辑
结构块<:AbstractCausalComponent),然后您可以禁用*定义组件结构*删除单元格,并在手动定义组件结构 通用代码 . 这同样适用于方法管理菜单中的任何功能。
-而不是标准单元格,你可以编写自己的代码 通用代码 .
声明一个组件和一个函子是需要工作的。 功能 . 如果*Define component structure*和*Define step method*被禁用,那么它们的代码必须设置在 通用代码 否则,该块将不起作用。 要重新定义继承函数(复盖,见下文),您首先需要包含相应的单元格,擦除其内容,然后在 通用代码 . -
定义步骤方法-添加单元格 步骤方法代码 ,其中定义了计算块的输出信号的步进方法 功能 . 方法签名是根据端口选项卡中相应端口标签的值自动生成的。 该方法表示为函子(有关详细信息,请参阅 这里)并且在模拟的每个步骤中被调用。 字段在不可编辑的行之间定义
函数(c::块)(t::实,in1)和结束. 第一个论点真的-仿真时间,然后输入信号变量被发送。 通过关键字返回计算的输出值回来吧.
-
定义更新方法-添加一个单元格*更新方法代码*其中的方法
更新!更新块的内部状态 功能 在模拟的每一步。 第一个论点c::座-块结构,第二真的-仿真时间,然后输入信号被发送。 如果块没有内部状态,那么该方法可以保持为空并简单地返回c.
如果需要定义多个方法 更新!或者设置具有不同签名的方法,然后您可以禁用*更新方法代码*单元格并将代码写入单元格中。 通用代码 . 编译器将自动检测方法的存在更新!并将其用于仿真。 -
定义终止方法代码-添加*终止方法代码*单元格,该单元格在块模拟结束时执行 功能 (使用方法
终止!). 第一个论点c::座-块结构。 默认情况下,该方法不执行任何其他操作,只需返回c.
-
复盖类型继承方法-添加*Types继承方法*单元格,该单元格复盖类型继承方法。
详细了解类型继承方法
设置类型继承方法:
-
如果未选中该复选框(默认情况下),则根据输入/输出端口说明中指定的规则继承输入/输出端口的类型。
-
如果选中该框,则根据函数中指定的规则继承输入/输出端口类型
传播_类型源代码中的*类型继承方法*单元格。-
功能
传播_类型它接受一个参数,一个类型的向量,每个输入信号都有一个类型,并返回一个输出类型的向量。
-
默认单元格代码:
function propagate_types(inputs_types::Vector{DataType})::Vector{DataType} # 返回输出信号类型数组的函数。 # 如果使用默认算法,它将被忽略。 # 在这种情况下,输入信号的类型和 # '增益'块的参数。 input_type = first(inputs_types) # promote_type返回给出参数类型的类型。 # 在与这些类型的对象进行算术运算期间。 output_type = promote_type(input_type, eltype(gain)) return [output_type] end这里,输入信号的公共元素和在设置参数部分的块设置中指定的参数的元素被取为继承类型。
-
-
Override dimensions继承方法-添加一个cell*Dimensions继承方法*,它重新定义了维度的继承方法。
详细了解维度继承的方法
设置维度的继承方法:
-
如果未选中该复选框(默认情况下),则根据输入/输出端口描述中指定的规则继承输入/输出端口的尺寸。
-
如果选中该框,则根据函数中指定的规则继承输入/输出端口的尺寸
传播_dimensions源代码中的*维度继承方法*单元格。-
功能
传播_dimensions它为每个输入信号获取一个元组(维度)数组,并为输出返回一个维度数组。
-
默认单元格代码:
function propagate_dimensions(inputs_dimensions::Vector{<:Dimensions})::Vector{<:Dimensions} # 返回输出信号维度数组的函数。 # 如果使用默认算法,它将被忽略。 # 在这种情况下,输入信号的尺寸被考虑在内和 # '增益'块的参数。 input_dimensions = first(inputs_dimensions) mock_input = zeros(input_dimensions) mock_output = mock_input .* gain return [size(mock_output)] end在这里,要继承维度,需要一个具有必要维度(mock_input)的数组,由零组成,并乘以设置参数部分的块设置中指定的参数元素,然后取其维度。
-
-
使用通用方法进行类型和维度继承-添加*通用类型和维度继承方法*单元格,该单元格使用通用方法同时重新定义类型和维度的继承。
详细了解类型和维度的继承方法
与类型(类型继承方法)或维度(维度继承方法)的特定方法不同,一般方法会同时包括类型和维度:
-
如果未选中(默认情况下),则忽略常规方法,输入/输出端口的维度和类型将根据输入/输出端口描述或继承方法*复盖类型继承方法*(如果启用)和*复盖维度继承
-
如果选中该框,则根据函数中指定的规则继承输入/输出端口的尺寸和类型
传播_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依赖关系
若要使用此单元格,请选中*Override type inheritance method*和*Override dimensions inheritance method*复选框。
-
-
Override sample time inheritance method-添加单元格*Sample times inheritance method*,该单元格重新定义计算步骤的继承方法。
详细了解计算步骤的继承方法
计算步骤的结构代码 采样时间和功能传播_sample_times它们不会自动添加到旧版*Engee*模型的EngeeFunctionCode源代码中。 要细化旧模型,请自行添加计算步骤的结构和函数。设置计算步骤的继承方法:
-
如果未选中该复选框(默认情况下),则使用计算步骤的预设继承方法(默认情况下
违约情况)来自高级选项卡的*样本时间继承方法*参数。 阅读有关预安装方法的更多信息。 下面。 -
如果选中该复选框,则忽略高级选项卡的预设方法(该参数不可用),则从EngeeFunctionCode源代码中的*Sample times inheritance method*单元格中使用独立方法。
要使用独立的方法,您需要找到函数字符串。
传播_sample_times并手动设置所需的计算步骤。默认单元格代码:
function propagate_sample_times(inputs_sample_times::Vector{SampleTime}, fixed_solver::Bool)::SampleTime # 返回块的采样时间的函数。 # 它仅在"自定义"继承模式下使用。 # 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画布的每个块都有一个可以通过此常量访问的名称。 例如,您可以在错误初始化期间引用BLOCK_NAME来输出其中的块名称。 -
开始时间-从模型设置开始模拟。 -
停止时间-结束模拟从模型设置。 -
输入_SIGNAL_ATTRIBUTES-每个输入端口的属性列表。 例如,要找出第一个输入信号的属性,使用输入_SIGNAL_ATTRIBUTES[1],在哪里1-单元的第一输入端口 功能 . -
输出_SIGNAL_ATTRIBUTES-每个输出端口的属性列表。 例如,要找出第一输出信号的属性,使用输出_SIGNAL_ATTRIBUTES[1],在哪里1-块的第一输出端口 功能 .
要了解有关特定块端口的更多信息,您可以通过添加点来参考其信号的属性 . 常数之后 输入_SIGNAL_ATTRIBUTES[i],在哪里 [i] -输入端口的编号,以及 输出_SIGNAL_ATTRIBUTES[i],在哪里 [i] -分别输出端口的编号。 您可以通过以下联系功能了解更多信息:
-
尺寸-信号的维度。 可缩短至暗淡无光. -
类型-信号的类型。 可缩短至tp. -
采样时间-计算步骤。 它是一种类似于信号属性的结构,可以通过点访问。 .. 提供两种转换功能:-
期间-计算步骤的周期。 全转换功能 —sample_time。期间. 可缩短至圣.p. -
偏移量-计算步骤的偏移。 全转换功能 —sample_time.偏移量. 可缩短至圣.o.
-
-
direct_feedthrough-指示循环端口是否打开。 它仅用于输入端口(仅检查输入端口的属性)。 可缩短至df.
模型示例 功能 具有所有常量和转换函数:

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,支持定点操作(固定)和定制轮胎类型(N.公共标志). 这些构造有助于控制处理整数、实数、固定和复杂数据类型时的操作行为。
定点类型和功能(定点)
在文章中 Engee中的定点算术 它描述了如何在*Engee*中使用固定点。 文章中给出的构造对块也有效 功能 ,这是块支持的方式:
-
固定点,固定点-所有固定数字的抽象类型; -
固定-特定类型的固定数字,使用指定的位表示手动创建; -
菲(。..)-创建类型值固定使用数值和表示参数; -
fixdt(。..)-创建固定类型,并指示符号、宽度和小数位数。
例如:
a = fi(5, 1, 16, 4) # 有符号数,16位,4分数
b = fi(10, 0, 8, 0) # 8位的无符号整数
T = fixdt(1, 24, 8) # 24位的固定类型,其中8位是小数
c = Fixed(0x01ff, T) # 直接从位表示创建固定数字
固定号码 功能 五月:
-
作为参数发送;
-
在组件结构中使用(cell*Component struct code*);
-
使用它们执行算术
emul,埃苏姆,伊迪夫在*Step方法代码*单元格中(有关详细信息,请参阅转换函数和类型化算术运算:[医]经济型,埃苏姆,emul,伊迪夫); -
适用于计算
propagate_types(…),propagate_dimensions(。..).
使用自定义轮胎类型(N.公共标志)
自定义总线类型允许您以结构化元组的形式指定输入和输出信号的类型(命名的,命名的)与里面的名称,类型和尺寸的描述 功能 . 可用功能:
-
BusSignal{…}-具有名称,类型和尺寸的总线类型; -
get_bus_names(类型)-获取信号名称列表; -
get_bus_types(类型)-获取信号的类型; -
get_bus_dimensions(类型)-获取信号的尺寸; -
get_names_types_dims(类型)-立即得到一切; -
get_bus_signal_type(::NamedTuple)-识别类型N.公共标志按价值计算。
一个确定和分析轮胎类型的例子:
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},
# BusSignal{(:x, :y), Tuple{Int64, Float64}, ((), ())})
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}, ((), ())}
块中的用法 功能
类别 固定 和 N.公共标志 它可以用于块的不同部分。 功能 :
-
在块参数中,您可以使用
菲(。..),并将总线结构转移为命名的,命名的. 总线的类型可以通过以下方式自动确定get_bus_signal_type(。..). -
在*通用代码*单元格中,您可以定义数据类型(
固定,BusSignal{…}),组件结构(结构块),创建辅助函数或初始化值。 如果禁用*步骤方法代码*或*组件结构代码*,也可以在此处移动主逻辑。 -
在单元格*组件结构代码*-描述结构的字段时,您可以使用如下值
固定,以及类型N.公共标志如果字段是复合信号。 -
*步骤方法代码*单元实现块的主要逻辑。 在这里,固定数字和总线信号可以参与计算、比较和结构处理。
-
在*类型继承方法*单元格中,您可以使用类型
固定和N.公共标志分析输入并指定组件的输出类型。 -
在*维度继承方法*单元格中,您可以使用数据
固定和来自总线的值来确定输出信号的尺寸。 -
在*更新方法代码*单元格中,如果块具有内部状态,则字段如下
固定或N.公共标志它可用于在仿真的每个步骤存储或更改此状态。 -
在*Terminate方法代码*单元格中,如果结构包含适当的字段,这些类型可用于记录最终状态。
-
在*Common types and dimensions inheritance method*单元格中,如果需要同时处理两种类型和维度。,
固定和N.公共标志他们也可以参与计算逻辑。
因此, 固定, 菲(。..), fixdt(。..), N.公共标志 和 get_总线 这些功能适用于机组配置和运行的各个方面. 功能 -无论是在执行阶段还是在信号的类型和维度的生成阶段。 它们可以在所有单元格的参数和源代码中自由使用(如果它们正常工作)。
转换函数和类型化算术运算: [医]经济型, 埃苏姆, emul, 伊迪夫
在街区里 功能 ,以及在 命令行
Engee*功能可用,允许您执行指定输出类型的算术运算,使用舍入和溢出控制在类型之间转换值,并使用专门的规则预先确定结果类型(_promote_类型). 这些函数用于逻辑 *功能 ,写在源代码单元格*步骤方法代码*,*通用代码*和其他人。
可用功能:
-
[医]经济型-类型之间的值转换; -
埃苏姆,[医]esub,emul,伊迪夫-输出类型赋值的算术运算; -
艾缪尔!-带有结果缓存的矩阵乘法; -
*_promote_type-基于输入类型的输出功能。
| 这些函数可以应用于标量和数组。 结果总是以用户显式指定的类型返回(或自动输出),这使得块行为稳定和预期。 |
支持的类型
此集中的所有函数都适用于以下类型:
-
真正的:
漂浮物16,漂浮物32,漂浮64 -
整数:
Int8,Int16,Int32,Int64,Int128,UInt8,UInt16,UInt32,UInt64,UInt128 -
合乎逻辑的:
布尔 -
固定:
固定(通过创建菲(。..)或fixdt(。..)) -
综合:
综合体{T},在哪里T—任何上述类型
函数也可以使用点语法与数组和矢量化表达式一起使用(.). 接下来,让我们更详细地看一下这些功能。
使用以下方法转换数字 [医]经济型
功能 [医]经济型 允许您通过指定舍入方法和溢出处理方法将值转换为指定类型。:
econvert(type::Type{T1}, var::T2; rounding_mode::RoundingMode=RoundDown, overflow::Bool=true)
这里:
-
类型-要将值转换为的类型。 如果瓦尔-一个复数,基本实数表示。 例如,经济型(Float32,综合大楼){Int16}(1+2im))它会回来的综合体{Float32}; -
瓦尔-转换价值; -
圆角/圆角-舍入法(四舍五入,综述,RoundNearest拢潞,往返旅行,圆托泽罗); -
溢出-如果真的,则使用溢出行为(wrap);如果错误饱和启用-值受类型范围限制。
功能 [医]经济型 它可用于比较前的类型转换,在算术表达式内部,在条件和其他场景中处理信号时。
算术与类型控制使用 埃苏姆, [医]esub, emul, 伊迪夫, 艾缪尔!
类型化算术运算的函数 埃苏姆, [医]esub, 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-生成和返回结果的类型; -
溢出-启用溢出(真的)或饱和度(错误); -
rm-舍入方法(见上面的列表)。
功能 埃苏姆, [医]esub, emul, 伊迪夫 返回与指定的值对应的值 out_type.
功能 艾缪尔! 它用于在处理矩阵时加快速度:它不会创建新数组,而是将结果写入已经分配的数组。 缓存. 重要的是 eltype(缓存) 恰逢 out_type.
结果取决于类型 out_type:它不仅影响最终结果,还影响中间计算的行为—这对于 固定 和 综合体.
使用以下方法推断结果的类型 *_promote_type
如果块参数(例如, [医]相类型 或 多类型,多类型)被设置为字符串 "继承",结果的类型是根据使用函数的输入数据类型自动确定的 *_promote_type.
如果您需要计算操作的结果类型,请使用列出的函数:
-
定义添加时的输出类型
n相同类型的值T: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) -
确定累加器加起来时的类型
n相同类型的值T:sum_accumulator_promote_type(::Type{T}, n::Integer=1, hdlsetup::Bool=false) -
确定将两种不同类型的电池添加在一起时的电池类型
T1和T2由n价值: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-一个逻辑参数,决定是否考虑到硬件平台的细节(目标器皿). 默认情况下:hdlsetup = TargetHardware == "C" ? false : true
-
这些函数方便地用于*类型继承方法*单元格中,以根据输入和参数准确确定输出块类型。
使用示例
下面是实现表达式的组件的示例 a*x+b. 使用参数设置中间乘法和最终加法的类型 多类型,多类型 和 [医]相类型. 如果指定 "继承",然后自动输出该类型。
使用a*x+b的例子_

参数 功能 :
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 -
Cell*Dimensions继承方法*:
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 -
Cell*Types继承方法*:
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
因此,功能 [医]经济型, 埃苏姆, emul, 伊迪夫 和规则 promote_type 它们允许您控制块内计算的类型和行为。 功能 . 它们支持所有主要的数值类型,包括固定点和复杂值,并可用于计算、比较、继承逻辑和使用块的组件行为调整。 功能 .
诊断功能 警告, 停止模拟, 暂停模拟, 资料
块的源代码中 功能 功能也可用 警告, 停止模拟, 暂停模拟 和 资料,它允许您在模拟阶段与模型的诊断系统进行交互,允许您在 诊断窗口
. 这些函数可以在以下源代码单元中使用:
-
组件结构代码
-
步骤方法代码
-
更新方法代码
-
终止方法代码
-
通用代码
|
功能 因此,尽管这些函数在*Component struct code*单元格中被正式允许,但实际上它们在那里的放置没有意义:此单元格仅用于描述块结构,而不是用于可执行逻辑。 为了使诊断函数正常工作,必须将它们放在支持的仿真方法中:步骤方法、更新方法、*终止方法*或*公共代码*中,如果在那里重新定义了相应的方法。 正确放置的例子:
|
通过类推,您可以复盖在模拟期间调用的任何受支持的方法(例如, 更新!, 终止;终止, 步骤),在*通用代码*单元格手动-如果禁用相应的标准单元格(例如,定义更新方法,*定义步骤方法*等。).
为了在单元格中正确操作*公共代码*功能 警告, 停止模拟, 暂停模拟 和 资料 可以使用:
-
内部函数重新定义负责执行模型的方法的行为,例如
步骤,更新!,终止!. 例如,如果在*步骤方法代码*单元格中正常指定的函子是在*公共代码*中定义的,那么它内部的诊断功能将正常工作。 -
从负责执行模型的方法调用的辅助函数内部。 也就是说,如果辅助函数在*公共代码*中定义,并且例如在
步骤,那么对其内部诊断函数的调用也将正确执行。重新定义 更新!或其他方法不替换所要求的实现。步骤(函数(c::块)(t,in…)). *Engee*需要此功能作为模拟的入口点。
正确重新定义的一个例子 更新! 在*通用代码*:
# 具有组件结构代码的结构(如果禁用,则必须以通用代码呈现)
struct Block <: AbstractCausalComponent
g::Real
function Block()
new(gain)
end
end
# 步骤方法(在模拟的每个步骤中调用的必需方法)
function (c::Block)(t::Real, in1)
c = update!(c, t, in1)
return c.g .* in1
end
# 重新定义的更新方法!
function update!(c::Block, t::Real, in1)
if t == 5.0
info("update triggered at t = $t") # 诊断信息
end
return c
end
因此,将在模型的诊断窗口中收到以下消息:

接下来,让我们仔细看看诊断功能本身。:
-
警告-功能警告(msg::String)显示警告消息。 模拟仍在继续。 这对于指出需要注意但不会停止执行的非关键问题或条件非常有用。 例子::if t == 5.0 || t == 7.5 warning("time == $t") end -
停止模拟-功能stop_simulation(msg::String)立即结束模拟并显示完成消息。 它用于指示一个临界条件,在该条件下,继续建模是不可能的或不希望的。 例子::if t == 5.0 stop_simulation("time == $t") end -
暂停模拟-功能pause_simulation(msg::String)暂停模拟并显示指定的暂停消息。 可以使用按钮手动恢复模拟 待续 :
此函数对于分析给定时间的模型状态非常有用。 例子::
if t == 5.0 pause_simulation("time == $t") end -
资料-功能信息(msg::字符串)显示信息消息。 它用于在不影响仿真执行的情况下显示中间值。 例子::if t == 5.0 || t == 7.5 info("time == $t") end
港口
入口
输入端口 — 输入端口
+
标量,标量 | 向量资料 | 矩阵
Details
指定为标量、矢量或矩阵的输入端口。
使用以下选项在块的*Ports*选项卡中配置输入端口:
-
标签-指定输入端口的名称。 默认情况下,*Label*单元格为空(未指定名称)。
-
类型-输入信号数据类型。 选择其中一个选项:
-
某种类型(除
继承)-检查某种类型的信号正在发送到输入端口。 为输入端口选择特定的数据类型。 支持的信号:漂浮物16,漂浮物32,漂浮64,复杂的32,复杂的f64,布尔,Int8,Int16,Int32,Int64,Int128,UInt8,UInt16,UInt32,UInt64,UInt128. -
继承(默认情况下)—从关联块继承数据类型。 它可以是任何类型的数据。
-
-
尺寸-输入信号的尺寸:
-
所有维度的继承(
-1默认情况下)-继承施加到输入端口的信号的维度(信号可以具有任何维度)。 -
定义的尺寸-输入信号必须具有指定数量的元素。 维度以Julia表示法(作为元组)指定,例如,
(2,)为由两个元素组成的一维信号,或(2, 3, 4)为多维。 如果指定-1,则维度被继承。 -
维度之一的继承-继承发送到输入端口的信号的维度与数据结构的显式指示。 例如,
(-1, 2)-预计第一个维度是继承的,第二个维度是显式设置的。
-
-
输出总线类型-输入总线类型,替换输入信号的维度*大小*如果选择了输入信号的数据类型*类型*
N.公共标志(轮胎)。 默认值为BusSignal{(),元组{},()}. 阻止 功能 我明白了哪条总线将来到输入,将类型设置为值就足够了继承. 显式类型指示仅对输出信号是必要的。座 功能 它不继承总线到输出端口,虽然它可以接收它们到输入。 要继承输出端口上的总线(用于传输到其他块),必须在*Output bus type*参数中显式指定总线类型。 -
直接馈通-定义直接端到端连接:
-
如果选中该复选框(默认情况下),则直接端到端连接可用。 这意味着输出信号直接由输入端口的值控制。
-
如果未选中该复选框,则直接端到端连接不可用。 这意味着输出信号将不受输入端口的值控制,并允许单元开环。
-
出口;出口
输出端口 — 输出端口
+
标量,标量 | 向量资料 | 矩阵
Details
指定为标量、矢量或矩阵的输出端口。
使用以下选项在块的*Ports*选项卡中配置输出端口:
-
标签-指定输出端口的名称。 默认情况下,*Label*单元格为空(未指定名称)。
-
类型-输出信号的数据类型。 选择其中一个选项:
-
某一类型(除
继承)-我们确定输出信号的数据类型。 为输出信号选择特定的数据类型。 支持的信号:漂浮物16,漂浮物32,漂浮64,复杂的32,复杂的f64,布尔,Int8,Int16,Int32,Int64,Int128,UInt8,UInt16,UInt32,UInt64,UInt128. -
继承(默认情况下)—继承输出信号的数据类型。 当有多个不同类型的输入信号时,计算最小的公共类型。 它可以是任何类型的数据。
-
-
尺寸-输出信号的尺寸:
-
所有维度的继承(
-1默认情况下)-继承施加到输出端口的信号的维度。 输出信号将具有作为广播机制的结果而获得的维度-Julia将自动将较小数据数组的维度扩展到较大的维度,以便正确继承维度。 -
定义的尺寸-输出信号必须具有指定数量的元素。 维度以Julia表示法(作为元组)指定,例如,
(2,)为由两个元素组成的一维信号,或(2, 3, 4)对于多维。 如果指定-1,则维度被继承。 -
继承其中一个维度-继承发送到输出端口的信号的维度,并带有数据结构的显式指示。 例如,
(-1, 2)-预计第一个维度是继承的,第二个维度是显式设置的。
-
-
输出总线类型-输出总线类型,替换输入信号的尺寸*尺寸*如果选择了输出信号的数据类型*类型*
N.公共标志(轮胎)。 默认值为BusSignal{(),元组{},()}. 阻止 功能 如果您已向出口发出轮胎,则必须明确指定其类型。
参数
主要
输入端口数 — 定义输入端口的数量
+
1(默认)
Details
定义块的输入端口数。 *输入端口数*参数的值将对应于输入端口数。
输出端口数 — 定义输出端口的数量
+
1(默认)
Details
定义块的输出端口数。 *输出端口数*参数的值将对应于输出端口数。
采样时间 — 计算步骤之间的间隔
+
-1(默认)
Details
将计算步骤之间的间隔指定为非负数。 要继承计算步骤,请将此参数设置为 −1.
高级
使用外部缓存进行非标量输出 — 对非标量输出使用外部缓存
+
禁用(默认情况下) | 已启用
Details
指定对非标量(多维)输出信号使用外部高速缓存来保存*Engee*RAM。 使用,如果块有 功能 只有一个输出端口:
-
如果未选中该复选框(默认情况下),则不使用外部缓存。
-
如果标志被检查,输出信号可以接受一个额外的参数。
缓存,在EngeeFunctionCode源代码中必须考虑到这一点。 如果单元具有一个输出端口,则在单元 步骤方法代码 论点缓存它会自动添加到函数参数列表中,除非将端口维度显式指定为()(标量)。 在源代码中,要求根据输出信号的维度编写函子。 函子可以在单元格中定义 通用代码 :-
如果输出信号是标量:
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-参数(来自输入端口的信息)。 必须指定time参数,即使它不包含在块参数中。
-
样本时间继承方法 — 计算步骤的继承方法的定义
+
默认(默认情况下) | 离散的,离散的 | 连续不断
Details
| 如果在EngeeFunctionCode源代码中选中了*Override sample time inheritance method*复选框,则*Sample time inheritance method*设置从*Advanced*选项卡中消失。 在这种情况下,计算步骤的继承方法由函数单元格的代码*样本时间继承方法*确定。 |
根据所选值定义计算步骤的继承方法:
如果在*采样时间*字段中指定了该值 -1,然后根据*样本时间继承方法*字段中指定的方法继承计算步骤,具体取决于所选值(违约情况, 离散的,离散的, 连续不断). 在所有其他情况下(采样时间不等于 -1 且采样时间大于等于 0)-区块 功能 它使用*Sample Time*字段的指定值,忽略*Sample time继承方法*。
|
-
违约情况-计算步骤的默认继承方法。 继承的方法违约情况它总是在块时使用 功能 它不是离散的或连续的。 法得到任何一种计算步骤。 选择此方法时,块 功能 它将根据以下原则继承计算步骤:-
如果块没有输入端口,则在输出处有一个连续的计算步骤。
-
如果所有计算步骤在输入处相同,则输出处的计算步骤与输入相同。
-
如果在输入步骤中存在连续的计算步骤,则在输出处也存在连续的计算步骤。
-
如果在输入计算步骤中存在固定次要(FiM,Fixed-In-Minor),则不存在连续计算步骤和 求解器与可变间距-输出是一个固定的小一个。
-
如果在输入处没有连续且固定数量的计算步骤并且并非所有计算步骤都相等,则只考虑输入处的离散计算步骤,对于其中一个选项是有效的。:
-
如果离散计算步骤的最大公约数与输入计算步骤之一重合或使用恒定步长求解器,则输出是具有最大公约数的步长的离散计算步骤。
-
如果变步求解器和输入离散计算步骤的最大公约数与任何输入计算步骤不匹配,则输出为固定小的。
-
-
-
离散的,离散的-种获得离散计算步骤的继承方法。 选择此方法时,块 功能 它将根据以下原则继承计算步骤:-
如果输入端有连续或固定的小计算步长,则输出为带有求解器步长的离散计算步长(即使求解器带有可变步长)。
-
如果在输入处存在离散计算步骤,则输出是与输入离散计算步骤具有最大公约数的离散计算步骤。
-
-
连续不断-一种继承方法,以获得与输入计算步骤无关的连续计算步骤。
一个重新定义函数的例子 传播_sample_times 具有类似于继承方法的作业 违约情况:
+
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
如果功能 传播_sample_times 申报表 (周期=0//1,偏移=0//1,模式=:离散),那么这样的计算步骤将被视为具有求解器步骤的离散。
|
设置参数
参数数量 — 指定参数的数量
+
1(默认)
Details
块中使用的参数的数量。
参数 — 将参数定义为变量
+
标量,标量 | 向量资料 | 阵列
Details
定义要在块源代码中使用的参数 功能 . 在参数中,您可以设置:
-
姓名-参数名称; -
价值-参数的值。 任何Julia表达式都可以设置为值。
参数的值和名称可以在参数选项卡中更改。 第一个参数的名称(默认存在) — 增益,其值为 2. 新参数被调用 参数2 然后上升。 新参数的默认值为 0.
在*Engee变量窗口中可用的全局变量*
您可以将参数选项卡设置为插入块源代码的变量。 功能 . 如果参数的名称和全局变量匹配,参数值将自动从全局变量中替换。
要设置总线参数,请为参数设置 价值 以命名元组形式的总线值,例如 (s1=5,s2=4).
|
区分在"参数"选项卡中设置的块参数和源代码中的全局变量非常重要。 功能 ,来自变量窗口中的全局变量*Engee* |
让我们考虑全局变量完全对应于源代码中的变量的情况。 例如,设置了三个全局变量 , , 与价值观 1, 2, 3 相应地。 所有三个全局变量都用作块参数。 功能 :

然后添加参数的源代码将如下所示:
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
此源代码定义了结构 座,它允许您自定义组件的行为。 该示例使用块参数的名称。 a_param, b_帕拉姆,而 c_param 设置结构参数 , ,而 相应地。 代码还定义了一个方法 function(c::Block)(t::Real,x::Vector{<:Real}),其中缩放向量的每个元素 x 到块参数 ,补充 并将结果除以 . 这允许您灵活地更改和规范化矢量。 x 据块参数的值。
让我们考虑一个仅使用参数选项卡的参数的情况。:
struct Block <: AbstractCausalComponent end
function(c::Block)(t::Real,x::Vector{<:Real})
返回(a_param.*x.+b_param)。/c_param
结束
这些参数将是块代码中的全局变量。 这意味着它们将始终在块代码的任何部分中可用,而不必在每个函数或代码块中重复定义它们。 这大大简化了代码,并且可以轻松更改参数选项卡中的参数,而不会影响源代码。
让我们考虑参数与源代码不完全匹配的情况。 例如,有一个参数 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 用于初始化字段 结构 结构块 通过它的构造函数,它将参数值划分为 10. 在这种情况下,该字段在块的函子中返回 .
变量可以在源代码中全局化。:
a = 1;
b = 2;
c=3;
struct Block <: AbstractCausalComponent; end
function (c::Block)(t::Real, x::Vector{<:Real})
return (a .* x .+ b) ./ c
end
在代码中创建一个结构 座 定义了一个函数,它用全局变量执行数学运算。 , 和 通过将它们应用于变量 x.
如果您*不想*使用参数选项卡中的参数,则可以直接在源代码中初始化变量。:
struct Block <: AbstractCausalComponent; end
function (c::Block)(t::Real, x)
a = 1;
b = 2;
c = 3;
return (a .* x .+ b) ./ c
end
在代码中创建一个结构 座 并定义了一个用局部变量进行数学运算的函数。 , 和 通过将它们应用于变量 x.
让我们考虑最有效和正确的方法来做到这一点。 阻止 功能 它工作正常,选中选项框*使用外部缓存进行非标量输出*在块的主选项卡上 功能 . 源代码将如下所示:
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
| 此代码只能在单元格中编写。 通用代码 ,由于标准单元格不允许编辑组件结构的定义和函子的签名。 |
字段 , 和 结构是在构造函数中严格检查类型的块参数。 这些参数中的每一个都必须是一个真正的标量(真实的),从而保证了执行过程中计算的准确性。
设计师 座() 检查传递参数的类型 , 和 . 如果其中至少有一个不是真正的标量或具有不适当的维度(它们必须是标量),则构造函数会生成带有相应消息的错误。 验证后,构造函数使用值初始化结构的字段 , 和 指定的类型 Ta, 结核病 和 Tc技术.
为块实例定义的计算函数需要时间 t,外部缓存和矢量 x 实数。 字段用于此函数 , 和 结构 座 计算值,然后将其写入缓存。 这通过重用提供的缓存来避免不必要的内存分配。
因此,结构 座 通过使用外部缓存来存储计算结果,提供对数据类型的严格管理和资源的有效使用。
如果要更改块参数,则必须使用 可变的 的结构。 考虑一个例子:
mutable struct Counter{T} <: AbstractCausalComponent
limit::T
iter::T
功能计数器()
isempty(size(limit))//error("$BLOCK_NAME的块限制必须是标量")
isreal(limit)||error("$BLOCK_NAME的块限制必须是实数")
T=类型(限制)
iter=零(T)
新的{T}(限制,iter)
结束
结束
函数(C::计数器)(t::真实)
返回c.iter
结束
功能更新!(C::Counter,t::Real)
c.iter+=1
如果c.iter>c.limit
c.iter=零(c.iter)
结束
c
结束
结构 柜台 -这是 可变的 用于计算具有指定限制的迭代的数据类型,为强类型。 字段 限额 和 伊特尔 结构表示块参数:
-
限额-这是计数器的极限值; -
伊特尔-计数器的当前值。
在结构构造函数中检查(验证)参数。 限额 参数是否为标量和真实数据类型。 之后,该字段被初始化。 伊特尔 具有相应类型的零值 T. 功能 更新! 更新标签状态 ,增加价值 伊特尔 每个电话一个。 如果当前值为 伊特尔 超过 限额,然后将其重置为零,并允许计数器循环回到其初始状态。
块的源代码中 功能 您可以使用 包括,参考外部代码。 这允许您从源代码中的外部代码(如果有的话)初始化变量。
|
| 实际的数据类型以及对可能的数据类型的支持取决于块内的用户代码。 |
示例代码
该示例示出了块的简化实现。 离散时间积分器,基于Julia代码集成到*Engee*模型中。 选择直接欧拉法作为积分法。 在块的*高级*选项卡上 功能 设置值 离散的,离散的 *样本时间继承方法*参数。 接下来,填写源代码单元格,如下所示:
-
在牢房里 通用代码 :
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 -
在牢房里 步骤方法代码 :
return c.state -
在*更新方法代码单元格中*:
c.state += in1 * c.dt * c.gain return c
因此,将获得以下源代码:

参数 初始化_条件 和 k 它们在块设置的*参数*选项卡中初始化 功能 :

在模型仿真的第一步,块的内部状态为 c.国家 由参数值初始化 初始化_条件.
之后,在每个计算步骤,块返回内部状态。 c.国家 作为输出信号并重新计算其值的方法 更新!.
| 组件结构在单元格中重新定义 通用代码 ,而不是在 元件结构代码 ,因为需要更灵活的定义:结构必须是可变的,并且由与状态类型对应的类型*T*参数化。 标准定义在 元件结构代码 它仅适用于不可变和非参数化结构。 |
说明
注释在 功能 它们允许您在模型中直接在其名称下显示块参数。 要添加它们,请打开 设置窗口
街区 功能 然后转到*Annotation*选项卡。 选择所需的块属性标记并将其添加到文本编辑器中。

在这个标签上:
-
左侧显示可用选项列表(隐藏选项除外)。
-
右边是一个文本编辑器,您可以在其中设置带有格式标记的注释
%<参数名称>. -
参数可以手动传输,通过自动完成,或使用按钮
.
退出编辑器后(例如,在其外部单击时),将应用注释:标记将自动替换为实际参数值,并且最终文本显示在块名称下(或其上方,如果名称放在顶部)。
要删除批注,必须在编辑器中删除相应的标记。
可用标记
属性标记将自动替换为当前参数值。 以下标记可用:
-
港口:
%<输入>,%<输出>-输入输出端口数;%<输入端口1类型>,%<输出端口1类型>,%<输入端口1大小>,%<输出端口1大小>-数据的类型和信号的维度。 -
临时特性:
%<采样时间>-离散化;%<采样时间>-离散继承的方法。 -
代码块:
%<组件结构码>,%<StepMethodCode>-步骤结构和方法的代码。 -
参数:
%<参数>,%<参数名称>,%<参数1值>-参数的名称和值。 -
启用标志:
%<定义组件结构>,%<UseCommonCode>,%<DefineStepMethod>,%<DefineUpdateMethod>,%<DefineTerminateMethod>-纳入守则的有关部分。 -
重新定义方法:
%<超负荷类型>,%<OverrideDimsInhMethod>,%<重写时间>-类型继承设置。 -
其他:
%<UseExternalCache>-使用外部缓存。