图形化编程语言中的OOP。 第2部分MOS和OOP

第一部分中,我试图证明存在于图形语言暗室中的黑色OOP猫,即使它不是猫,而是Schroedinger劫掠者的半死猫,也就是说,它不是。 当程序不是C ++或Java代码,而是Simulink,SimInTech,SimulationX或SCADE Esterel图(算法描述的任何图形表示)时,都显示了面向对象编程方法的实现示例。


在广告材料中,Matlab Simulink经常使用术语MOS-基于模型的设计。 在许多文章中,他们强调算法的图形图是一个模型,这当然是正确的。 但是,在MOS的初始定义中,模型主要是控制系统要开发到的对象的模型,包括控制软件。 这里介绍更多的MOS方法。 因此,当根据MOS方法开发控制系统时,可能并且有必要使用OOP方法来开发管理软件。 为了完全解决模型问题,下面是一张图片,其中有一个彼此之间的差异。 如果一切都清楚了,那么您将无法阅读。




让我们回到适用于工业控制程序的图形语言和OOP。 图形化编程语言以图表的形式从通过通讯线连接的一组块中提供程序记录。 通常,在工业编程中,通过自动代码生成器从图形电路创建用于编译器的代码以及随后加载到目标设备中的代码。


作为我专业的一部分,我必须与核电厂管理软件一起工作,因为该软件禁止使用具有安全标准的C ++,仅纯C,在某些情况下甚至不能使用C,而是“铁”逻辑,在该算法中,算法被实现为在晶体管和晶体管上的电子电路。中继。 严格的监督人员负责监督标准的遵守情况。



但是在这里,无论听起来多么令人惊讶,开发仍在使用OOP方法。 因为当您不能使用OOP,但是您确实想使用时,您可以使用。 没错,那么所有内容都需要返回,并且出于安全和überalles标准的考虑,这不会是任何C ++和最终代码。 正如他们所说,要吃一条鱼,而不是因为违规而坐下。


为了通过OOP定义创建真实的对象,我们将数据结构和处理方案绑定到单个对象中,这称为封装。 而且由于我们不能将C ++用于可靠的NPP系统,因此在生成代码时必须分析所有这些信息。 如前一篇文章的注释中所述,第一个C ++Front编译器以相同的方式工作,它将OOP C ++代码转换为纯C。


在以图形语言实现OOP第一个版本中 ,我们创建了一个包含图形计算方案的特殊块。 在初始化期间,此块将类方案(方法)绑定到特定的“类实例”-以特殊方式命名的一组变量。


考虑以图形编程语言的OOP方法的第二实施例。 图1显示了传感器处理算法的操作。



图1.传感器处理程序。


这是一个类方法。 它在数据库中对应于其自己的类别“传感器” -具有给定字段集的抽象类和KBA31CFO1类特定传感器的实例。 对于此传感器,这些字段具有特定的值,某些字段由用户设置,某些字段是在程序执行期间计算的。 看图片 2



图2.带有开放类别“ Sensor”的信号数据库。


到目前为止,一切都与第一实施例中的情况相同,在第一实施例中,当将单元安装在电路上时,我们将设计方案绑定到特定传感器。 “有什么区别?” -你问。 区别在于块内部。 如果在第一个版本中有一个设计图,该图随模块的每次安装一起复制,则在此版本中,内部看起来像这样:



图3.类实例的块实例的内部。


代替设计方案,仅在块内“显示”数据发送和接收。
计算本身在另一位置进行,如图1所示。在某些情况下,在计算图中完全不使用块是可能的,在singles数据库中包含传感器类的实例就足够了。 这是用图形语言实现封装的第二种方法。 诀窍在于,图1的示意图中的所有块都是矢量块,它们不处理一个信号,而是处理计算方案中来自所有此类传感器的信号矢量。 如果启用了在通讯线上显示结果的模式,那么我们将看到每条通讯线都不包含一个数字,而是一个包含4个数字的向量(根据数据库中的传感器数量)。



图4.在查看值模式下处理传感器信号的图。


因此,一种处理方案实现了项目中所有传感器的处理,每个传感器都使用其自身在数据库中指定的参数作为类的特定实例的特征进行处理。 例如,限制器从数据库中获取最大值,前三个传感器的最大值设置为相同,而第四个传感器的最大值则不同。 (见图5)



图5.计算方案中限制器块的参数。


根据这种出色的方案自动生成的结果代码又如何呢?如何避免OOP伪像呢? 很简单:没有作弊也没有OOP,代码中纯C。 对于矢量处理方案的每个块,将形成一个循环,该循环提供与项目中的类实例一样多的计算。 在我们的例子中,有4个传感器,因此我们首先通过读取传感器的信号来形成尺寸为“ 4”的数组:


/* Index=104 UID=104 GeneratorClassName=TSignalReader Name=buz1.sensor.Macro6.Macro3.Macro157.SignalReader3 Type=    */ }; state_vars->kbastdv104_out_0_[0] = kba31cf001_mf_type; state_vars->kbastdv104_out_0_[1] = kba32cf001_mf_type; state_vars->kbastdv104_out_0_[2] = kba33cf001_mf_type; state_vars->kbastdv104_out_0_[3] = uf40y329084320_mf_type 

然后,我们按顺序对所有块进行排序,然后循环运行它们。 对于类型数组的每个元素,将对所有传感器执行每个计算块。


 /* Index=211 UID=211 GeneratorClassName=TAndSrc Name=buz1.sensor.Macro6.And61 Type=  */ for(i=0;i<4;i++){ locals->v211_out_0_[i] = state_vars->kbastdv125_out_0_[i] && (!(locals->v191_out_7_[i] > 0.5)); /* Index=212 UID=212 GeneratorClassName=TMulDbl Name=buz1.sensor.Macro6.Mul_oper1 Type= */ locals->v209_out_2_[i] = consts->kbastdv121_a_[i]*state_vars->kbastdv127_out_0_[i]; /* Index=213 UID=213 GeneratorClassName=TSumSrc Name=buz1.sensor.Macro6.Add_oper1 Type= */ locals->v209_out_3_[i] = (1)*consts->kbastdv122_a_[i]+(1)*locals->v209_out_2_[i]; … } 

计算后,我们记录该类的每个实例的信号:

 /* Index=776 UID=776 GeneratorClassName=TSignalWriter Name=buz1.sensor.Macro6.Macro3.SignalWriter4 Type=    */ kba31cf001_mf_xb01 = state_vars->kbastdv207_out_0_[0]; kba32cf001_mf_xb01 = state_vars->kbastdv207_out_0_[1]; kba33cf001_mf_xb01 = state_vars->kbastdv207_out_0_[2]; uf40y329084320_mf_xb01 = state_vars->kbastdv207_out_0_[3]; 

如您所见,最终代码中没有对象。 清洁,纯真和安全的SI。 在以上示例中,以图形语言实现OOP,矢量电路计算所有相同类型的传感器。 此技术使您可以更改一种方案来更改所有传感器的处理。


这种方法的另一个附加好处是错误保险。 想象一下:您手动添加了一个传感器,却忘记了在某个地方增加一个循环中的重复次数。 没有静态代码分析器将能够检测到此错误,代码是正确的。 即使在工作中,这也可能不会立即产生明显影响。


好吧,最后,就是承诺的多态性和继承。 在第一种方法中,用户收到许多相同的方案,可以在安装子模型块后对其进行编辑,从而执行多态性,从而更改类的特定实例的行为。 我想每个人都认为可以更改特定传感器的处理方案,并且我们将获得一个具有相同字段但方法不同的新类。 您还可以添加新字段,并获得具有不同字段的新类,其中包含父对象的所有字段和父方法的不同字段。


图6显示了“父”和“继承人”类的两个块的示例。 在该块内部,保存了父类计算方案。 所有数据都进入一个公共矢量块,类似于图2中的块。 4.完全重复父类方法。 然后,后继类具有一个附加字段Yatm和一个使用线性插值块的值重新计算。


因此,仍然可以更改父级的处理方法,该方法将在所有类别的继承人中发生变化,并且可以分别自定义继承人的行为。



图6.类继承人中的多态性。


总而言之,我们可以辩称OOP方法论可用于以图形化编程语言创建软件。 抽象,封装,继承,多态-所有这些原则都可以通过正确的开发工具轻松自然地实现。 重要的是要注意,从图形语言自动生成后,最终代码仍然是纯安全的C,没有任何OOP


在某些情况下,以图形形式开发控制软件的结果不是要加载到控制器中的C代码,而是电路图“铁逻辑”,但是上述OOP的技术也可以正常工作。

Source: https://habr.com/ru/post/zh-CN454232/


All Articles