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"

启用子系统

在本例中,我们要考虑的第一个子系统变体是启用子系统。这是一个有条件执行的子系统,只要控制信号为正值,每个主要时间步就启动一次。如果信号在一个较小的时间步长内过零,则子系统在下一个主要时间步长之前不会启用或禁用。

控制信号可以是标量或矢量。 1.如果标量值大于零,则执行子系统。 2.如果任何一个矢量值大于零,则执行子系统。

要使用此功能,请在子系统块或模型块引用的模型根层添加一个块。如果在模型根层使用 启用,则需要执行以下步骤。

1.对于多周期模型,将求解器设置为单任务模式。 2.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

2.切换案例

image_3.png

让我们运行这两个模型并分析结果。让我们先来看看带有if的模型。

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,我们将得到来自第二个子系统的数据图。否则,子系统的输出信号会冻结,等待控制信号。

触发子系统

启用子系统程序块不同,触发子系统程序块在两次触发之间始终将其输出参数保持为上次的值。此外,触发式子系统不能在运行时重置程序块状态;任何离散程序块的状态都会在两次触发之间保留。

触发器***块添加了一个外部信号,用于控制子系统的执行。当控制信号的值按指定方式发生变化时,子系统将每步执行一次。程序块图标的变化取决于触发类型参数的选择值。此外,还可在启用中添加触发块。本节将介绍触发子系统的两种应用。

首先,我们来看一个简单的触发子系统的例子。使用这种方法实现的模型如下图所示。 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]:

我们可以看到,Enable 信号的频率是Trigger 信号频率的三倍,因此这两个信号只交叉一次,从而启动了子系统。

每个子系统

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

该子系统中的每个程序块都为其处理的每个元素或子阵列保持一组单独的状态。当一组子系统块处理元素或子数组时,子系统会将处理结果合并形成输出信号。

下图显示了一个应用For Each模块的模型。

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 的分段相乘。

该程序块还可以为输入数据建立索引,并以端口形式输出。下图展示了For Each的应用。 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中遇到这些变体,并在您的项目中使用它们。