Engee 文档
Notebook

子系统

本演示的目的是展示如何使用在Engee中实现的各种子系统。 要做到这一点,让我们来看看几个简单的模型。

In [ ]:
using Plots # Библиотека для отрисовки результатов моделирования

# Подключение вспомогательной функции запуска модели.
function run_model( name_model, path_to_folder )
    
    Path = path_to_folder * "/" * name_model * ".engee"
    
    if name_model in [m.name for m in engee.get_all_models()] # Проверка условия загрузки модели в ядро
        model = engee.open( name_model ) # Открыть модель
        model_output = engee.run( model, verbose=true ); # Запустить модель
    else
        model = engee.load( Path, force=true ) # Загрузить модель
        model_output = engee.run( model, verbose=true ); # Запустить модель
        engee.close( name_model, force=true ); # Закрыть модель
    end

    return model_output
end

# Путь до папки с моделями
path = "$(@__DIR__)/models"
Out[0]:
"/user/subsystems/models"

启用的子系统

我们将在此示例中考虑的第一个子系统选项是Enabled Subsystem。 我们正在谈论一个有条件执行的子系统,只要控制信号具有正值,该子系统在每个主要时间步长启动一次。 如果信号在较小的时间步长期间过零,则子系统直到下一个主要时间步长才打开或关闭。

控制信号可以是标量或矢量。

  1. 如果标量值大于零,则执行子系统。
  2. 如果向量元素的任何值大于零,则执行子系统。

要使用此功能,请将块添加到子系统块或模型块引用的模型的根级别。 如果在模型的根级别使用Enable,请按照以下步骤操作。

  1. 对于多速模型,将求解器设置为单任务模式。
  2. 对于具有固定步长的模型,模型中至少有一个块必须以固定步长的给定速度运行。

下面的图片是这个例子的说明。

image.png

在这个例子中,控制信号是正弦波,而输入信号是计数器。

In [ ]:
run_model("enabled_subsystem",path) # Запуск модели.
Building...
Progress 0%
Progress 0%
Progress 5%
Progress 10%
Progress 15%
Progress 20%
Progress 25%
Progress 30%
Progress 35%
Progress 40%
Progress 45%
Progress 50%
Progress 55%
Progress 60%
Progress 65%
Progress 70%
Progress 75%
Progress 80%
Progress 85%
Progress 90%
Progress 95%
Progress 100%
Out[0]:
Dict{String, DataFrame} with 2 entries:
  "SubSystem.Out1" => 1001×2 DataFrame…
  "Sine Wave.1"    => 1001×2 DataFrame
In [ ]:
# Считывание из simout залогированных сигналов
data1 = simout["enabled_subsystem/SubSystem.Out1"];
data1 = collect(data1);
en1 = simout["enabled_subsystem/Sine Wave.1"];
en1 = collect(en1);

让我们绘制输出数据对控制信号的依赖关系。

In [ ]:
plot(data1.time,data1.value) 
plot!(en1.time,en1.value) 
Out[0]:

从图中我们可以看到,在控制信号降到0以下的那些时刻,输出信号变淡。

行动子系统

操作端口控制子系统执行。 块为子系统增加动作信号的外部输入端口。 动作信号控制与子系统块的动作端口连接的信号。

用于包含块的模型:

  1. 如果
image.png
  1. 开关盒
image_3.png

让我们运行这两个模型并分析结果。 让我们从cif模型开始。

In [ ]:
run_model("if_subsystem",path) # Запуск модели.
Building...
Progress 0%
Progress 0%
Progress 5%
Progress 10%
Progress 15%
Progress 20%
Progress 25%
Progress 30%
Progress 35%
Progress 40%
Progress 45%
Progress 50%
Progress 55%
Progress 60%
Progress 65%
Progress 70%
Progress 75%
Progress 80%
Progress 85%
Progress 90%
Progress 95%
Progress 100%
Out[0]:
Dict{String, DataFrame} with 2 entries:
  "SysOutput_1" => 1001×2 DataFrame…
  "SysOutput_2" => 1001×2 DataFrame
In [ ]:
# Считывание из simout залогированных сигналов
SysOutput_1 = simout["if_subsystem/SysOutput_1"];
SysOutput_1 = collect(SysOutput_1);
SysOutput_2 = simout["if_subsystem/SysOutput_2"];
SysOutput_2 = collect(SysOutput_2);
In [ ]:
plot(SysOutput_1.time,SysOutput_1.value) 
plot!(SysOutput_2.time,SysOutput_2.value) 
Out[0]:

正如我们所看到的,在if的情况下,输入正弦被分成两个信号:根据大于0和小于0的条件。

In [ ]:
run_model("switch_case_subsystem",path) # Запуск модели.
Building...
Progress 0%
Progress 0%
Progress 5%
Progress 10%
Progress 15%
Progress 20%
Progress 25%
Progress 30%
Progress 35%
Progress 40%
Progress 45%
Progress 50%
Progress 55%
Progress 60%
Progress 65%
Progress 70%
Progress 75%
Progress 80%
Progress 85%
Progress 90%
Progress 95%
Progress 100%
Out[0]:
Dict{String, DataFrame} with 3 entries:
  "SysOutput_1"       => 1001×2 DataFrame…
  "Pulse Generator.1" => 1001×2 DataFrame…
  "SysOutput_2"       => 1001×2 DataFrame
In [ ]:
# Считывание из simout залогированных сигналов
SysOutput_1 = simout["switch_case_subsystem/SysOutput_1"];
SysOutput_1 = collect(SysOutput_1);
SysOutput_2 = simout["switch_case_subsystem/SysOutput_2"];
SysOutput_2 = collect(SysOutput_2);
En = simout["switch_case_subsystem/Pulse Generator.1"];
En = collect(En);

让我们绘制输出信号对控制信号的依赖关系。

In [ ]:
plot(SysOutput_1.time,SysOutput_1.value) 
plot!(SysOutput_2.time,SysOutput_2.value)
plot!(En.time,En.value)
Out[0]:

可以看到,当控制信号为1时,从上层子系统写入数据。 如果数据是0,那么我们从第二个子系统得到一个数据图。 否则,子系统上的输出信号在等待控制信号时冻结。

触发子系统

Enabled Subsystem块不同,Triggered Subsystem块始终将其输出参数保持在触发器之间的最后一个值。 此外,正在运行的子系统不能在执行期间重置块状态;任何离散块的状态都保存在触发器之间。

Trigger块添加外部信号以控制子系统执行。 当控制信号的值以指定的方式改变时,子系统将在每个步骤执行一次。 块图标会根据为触发器类型参数选择的值而更改。 此外,触发器块可以添加到启用。 在本节中,我们将分析触发子系统的两个应用程序。

首先,让我们看一个简单的触发子系统的例子。 使用这种方法的模型的实现如下图所示。
image.png

In [ ]:
run_model("triggered_subsystem",path) # Запуск модели.
Building...
Progress 0%
Progress 0%
Progress 5%
Progress 10%
Progress 15%
Progress 20%
Progress 25%
Progress 30%
Progress 35%
Progress 40%
Progress 45%
Progress 50%
Progress 55%
Progress 60%
Progress 65%
Progress 70%
Progress 75%
Progress 80%
Progress 85%
Progress 90%
Progress 95%
Progress 100%
Out[0]:
Dict{String, DataFrame} with 2 entries:
  "SubSystem.Out1" => 1001×2 DataFrame…
  "Sine Wave-1.1"  => 1001×2 DataFrame
In [ ]:
# Считывание из simout залогированных сигналов
SysOutput_1 = simout["triggered_subsystem/SubSystem.Out1"];
SysOutput_1 = collect(SysOutput_1);
En = simout["triggered_subsystem/Sine Wave-1.1"];
En = collect(En);
In [ ]:
plot(SysOutput_1.time,SysOutput_1.value) 
plot!(En.time,En.value)
Out[0]:

根据所得到的数据,我们可以看到,这样的子系统仅在正弦0相交的时刻被触发。

现在让我们考虑在同一子系统中使用TriggerEnable块。 下图显示了我们实现的模型。

image_2.png
In [ ]:
run_model("triggered_enabled_subsystem",path) # Запуск модели.
Building...
Progress 0%
Progress 0%
Progress 5%
Progress 10%
Progress 15%
Progress 20%
Progress 25%
Progress 30%
Progress 35%
Progress 40%
Progress 45%
Progress 50%
Progress 55%
Progress 60%
Progress 65%
Progress 70%
Progress 75%
Progress 80%
Progress 85%
Progress 90%
Progress 95%
Progress 100%
Out[0]:
Dict{String, DataFrame} with 3 entries:
  "SubSystem.Out1" => 1001×2 DataFrame…
  "Enable.1"       => 1001×2 DataFrame…
  "Trigger.1"      => 1001×2 DataFrame
In [ ]:
# Считывание из simout залогированных сигналов
Data = simout["triggered_enabled_subsystem/SubSystem.Out1"];
Data = collect(Data);
Trigger = simout["triggered_enabled_subsystem/Trigger.1"];
Trigger = collect(Trigger);
Enable = simout["triggered_enabled_subsystem/Enable.1"];
Enable = collect(Enable);

让我们分析相对于两个控制信号的输出。

In [ ]:
plot(Data.time,Data.value) 
plot!(Trigger.time,Trigger.value)
plot!(Enable.time,Enable.value)
Out[0]:

我们可以看到,使能信号的频率比触发器的频率高三倍,因此这两个信号只相交一次,从而启动了子系统。

对于每个子系统

每个块的是每个子系统的控制块。 该子系统允许您处理部分输入信号。

此子系统中的每个块为其处理的每个元素或子阵列维护一组单独的状态。 作为一组子系统块处理元件或子阵列,子系统组合结果以形成输出信号。

下图显示了使用每个块的的模型。

image.png image_3.png
In [ ]:
run_model("for_each_model1",path) # Запуск модели.
Building...
Progress 10%
Progress 20%
Progress 30%
Progress 40%
Progress 50%
Progress 60%
Progress 70%
Progress 80%
Progress 90%
Progress 100%
Progress 100%
Out[0]:
Dict{String, DataFrame} with 2 entries:
  "SysOutput_1" => 11×2 DataFrame…
  "Constant.1"  => 11×2 DataFrame
In [ ]:
# Считывание из simout залогированных сигналов
Data_in = simout["for_each_model1/Constant.1"];
Data_in = collect(Data_in);
Data_out = simout["for_each_model1/SysOutput_1"];
Data_out = collect(Data_out);
print(string(Data_in.value[1]) *" --> " * string(Data_out.value[1]))
[1.0, 2.0, 3.0] --> [2.0, 4.0, 6.0]

正如我们所看到的,结果是输入数据的零碎乘以2

此块还允许您为输入数据创建索引并将其作为端口输出。 下图显示了每个的此类应用程序
image.png

image_2.png

计数器设置还指示迭代次数为3

image_4.png
In [ ]:
run_model("for_each_model2",path) # Запуск модели
Building...
Progress 10%
Progress 20%
Progress 30%
Progress 40%
Progress 50%
Progress 60%
Progress 70%
Progress 80%
Progress 90%
Progress 100%
Progress 100%
Out[0]:
Dict{String, DataFrame} with 2 entries:
  "SysOutput_1"   => 11×2 DataFrame…
  "SubSystem.idx" => 11×2 DataFrame
In [ ]:
# Считывание из simout залогированных сигналов
Data_out = simout["for_each_model2/SysOutput_1"];
Data_out = collect(Data_out);
idx = simout["for_each_model2/SubSystem.idx"];
idx = collect(idx);
In [ ]:
Data_out.value[1]
Out[0]:
2×6 Matrix{Float64}:
 0.0  0.0  1.0  1.0  2.0  2.0
 0.0  0.0  1.0  1.0  2.0  2.0
In [ ]:
print(string(idx.value[1]))
[0.0, 1.0, 2.0]

正如我们可以看到的,在这种情况下,输入单元矩阵乘以计数器端口,考虑到只有三次迭代处理输入数据的事实。

函数调用子系统

函数调用子系统块是有条件执行的子系统,每次控制端口接收到函数调用事件时启动。 图表、函数调用生成器块或Engee函数块可以提供函数调用事件。

调用函数的子系统类似于过程编程语言中的函数。 调用函数调用子系统按照执行顺序触发子系统内的块输出方法。 下图显示了使用函数调用生成器和函数调用子系统的示例。

image.png image_2.png
In [ ]:
run_model("function_call",path) # Запуск модели.
Building...
Progress 100%
Out[0]:
Dict{String, DataFrames.DataFrame} with 2 entries:
  "SysOutput_1" => 1001×2 DataFrame…
  "Sine Wave.1" => 1001×2 DataFrame
In [ ]:
# Считывание из simout залогированных сигналов
out = simout["function_call/SysOutput_1"];
out = collect(out);
inp = simout["function_call/Sine Wave.1"];
inp = collect(inp);

的输入正弦曲线。

In [ ]:
plot(inp.time,inp.value) 
Out[0]:

的输出量。

In [ ]:
plot(out.time,out.value) 
Out[0]:

正如我们在这个例子中看到的,根据控制功能的条件,计数器增量在子系统内部执行。

结论

在本演示中,我们分析了您可以在Engee中找到并在项目中使用的所有子系统选项。