本文最后更新于 2024-10-18,文章内容距离上一次更新已经过去了很久啦,可能已经过时了,请谨慎参考喵。

title: 单元模块开发入门2
top_img: false
tags:
  - CAPE-OPEN
categories:
  - 化工流程模拟开发
cover: '/upload/cdn5files/202401271421205.png'
abbrlink: 5cfbb4ff
date: 2024-01-27 14:17:14
copyright:
comments:

前言

入门篇1中成功的注册了所开发的单元模块,并在COFE软件中成功实现了一个端口的连接,但在aspen软件中无法进行流股连接

本篇将继续来完善这个单元模块

完善ICapeUnit接口

上一篇中我们在 MyBlock.h 文件中写了获取端口数组的函数 get_ports ,现在来继续完善这个接口的其他函数

按照我们之前在物性包开发入门篇的方法,引入下列两个头文件:

在单元模块开发入门第一篇中我们下载的示例文件中就有这两个头文件,然后复制到项目根目录下,右键项目解决方案管理器,添加现有项,添加进来即可

添加头文件:

补充下方函数:

此时编译有错误,是wstring类型语法错误,这里我们物性包开发篇也遇到过,需要修改 Variant.h 文件:

再更改第318行:

现在编译已经没有问题了,但是此时还是有问题的,在aspen无法连接流股,双击没有反应

双击模块的逻辑

我们在aspen中新建一个别的软件的CAPE-OPEN模块:

双击这个模块之后可以看到,弹出来了一个弹窗用来输入参数,但是我们的模块丝毫没有反应,是因为我们并没有实现这个功能

双击模块打开参数输入窗口的逻辑就是我们上文提到过的 ICapeUtilities 接口来实现的

同样,这个接口也是有对应的文档的,文档文件名是:Utilities_Common_Interface.pdf

接口解释图如下:

添加该接口:

添加完成之后重新编译一下,没什么问题,这个接口导入进来终于是正常的了

继续在 MyBlock.h 文件中编写该接口的函数:

继续完善 ICapeUtilities 接口的函数:(还是本文件)

编译一下,没毛病,用COFE测试一下也没问题:

ICapeUtilities接口

继续来完善 ICapeUtilities 接口的 get_parameters 函数,这个函数的参数和前文中的 MyBlockPortsArray 一样,是个数组,所以还是需要创建一个ATL对象来实现这个数组

添加ATL对象:

报错解决不再赘述,看前文

添加完毕之后编译保存一下,和之前的 MyBlockPortsArray 一样,还需要给它添加 ICapeCollection 接口:

添加完再编译保存一下,没啥毛病

为了方便理解和学习,我们现在只是给流程模拟软件返回这个数组即可,所以这里只返回一个空数组

在新建的 MyParameterArray.h 文件中:

回到 MyBlock.h 文件中,首先添加头文件:

创建parameters参数数组:(当前文件)

实例化:(当前文件)

实际上在这里要强调一下,在当前文件中 ICapeUnit 接口的 myPortArray 数组和刚才写的 ICapeUtilities 接口的 parameterArray 数组都是不能在函数内进行实例化的,必须要在头部的 pbulic:CMyBlock(){} 里进行实例化

但是我们为了演示其用法临时在函数内进行了实例化,这是不对的,但能临时骗过编译器,后面我们会更改这个错误用法的

这个时候编译是没问题的,同样模拟软件获取到端口1也是没问题,但是我们这里用了取巧的办法骗过了编译器和模拟软件,接下来通过断点调试来找一下正确写法里我们的bug出在哪里了,给大家提供一种找错误的思路

简单断点调试

首先呢,我们把代码恢复到一个正常的写法里,我们把 MyBlock.h 文件中 ICapeUnit 接口的 myPortArray 数组和 ICapeUtilities 接口的 parameterArray 数组实例化转移到头部的 pbulic:CMyBlock(){} 里:

注释掉函数内的实例化:

添加断点信息:

现在重新编译,并使用COFE软件进行添加测试,当我们在COFE软件中点击了添加单元模块之后,就会弹出每一个步骤相对应的信息:

前几个步骤都没有问题,但是当到Get_ports步骤的时候,我们会发现Get_ports弹出了两次,并且在两次之后COFE软件闪退,那么问题就是出现在了Get_ports步骤

那么我们在Get_ports步骤再添加一个断点信息:

重新编译,测试:

这个时候我们发现,当第一个 Get_ports, Start 出现之后紧接着已经出现了 Get_ports, End ,也就是说第一个端口确实是获取到了,紧接着又出现了 Get_ports, Start 但没有出现 Get_ports, End 软件就闪退了

也就是说,错误出现在了get_ports这个过程中,对应的也就是我们之前添加的 MyBlockPortsArray 数组对象中出现了问题

添加端口2

在上文中,我们在 MyBlockPortsArray.h 文件中只定义了一个端口1,那么在模拟软件获取端口数组的过程中两次返回了同一个端口,但是在模拟软件的定义中,一个单元模块应该至少具有两个端口,也就是至少应该是一进一出

前文中为了方便理解我们只添加了一个端口1,现在就来完善它,添加第二个端口

我们定义端口数组是在 MyBlockPortsArray.h 文件中,但是端口的实例是由 BlockPort.h 文件传入的,所以需要在 BlockPort.h 文件中添加另一个端口实例

创建私有成员端口方向:

创建端口的时候,就需要给端口定义一个方向,也就是这个端口是进口还是出口,给定一个方向的参数:

编译一下,哦吼,报错了,原来是 CapePortDirection myPortDirection 参数不能这么传递,稍稍修改一下:

接下来自然就要在 MyBlockPortsArray.h 文件中添加端口2:

修改端口数:

接下来就应该是给端口赋值获取端口了,那么我们如何让 Item 函数知道获取的是哪个接口呢,就需要给端口进行命名

给端口进行命名就是 ICapeIdentification 这个接口所要干的活儿了

在官方文档集里对应的是这个文件: Identification_Common_Interface.pdf 文件中该接口的内容如下:

实际上就是定义了一个对象所具有的名字和描述,现在来添加这个接口:

添加完毕之后编译一下,没啥毛病,继续

接下来在 BlockPort.h 文件中定义上述端口的名字和描述,首先添加头文件支持:

创建名字和描述变量:

创建设置名字和变量的函数:

ICapeIdentification 接口中获取值:(还是本文件)

注:这里需要添加下列两个头文件以提供 CA2W 支持:

// 添加string转const OLECCHAR*类型支持
#include <atlbase.h>
#include <atlconv.h>

接下来回到 MyBlockPortsArray.h 文件中继续完善两个端口

首先为了方便学习和理解,我们直接指定两个端口的名字和描述:

完善 ICapeCollection 接口的 Item() 函数获取端口的逻辑:

编译一下,没啥毛病,到这里我们的端口2就已经添加完了,但是呢,在获取第二个端口的时候依然会崩溃闪退,那么下面就来分析一下到底是什么原因导致的

断点调试

首先对比了一下官方提供的示例,发现是 MyBlock.h 文件中 myPortArray 的获取方式有问题,我们来修改一下,直接做一个强制转换:

其次 myPortArray 也应该具有一个 ICapeIdentification 接口定义这个数组的名字,添加接口:

然后在 MyBlockPortsArray.h 文件中:

这里就不需要引入支持 CA2W 的头文件了,因为已经在 BlockPort.h 文件中引用过了,而当前文件已经引入了 BlockPort.h

回到 BlockPort.h 文件,在获取端口的语句上添加一个断点:

启动COFE软件,将COFE附加到进程中:

然后在COFE中添加我们的单元模块,当COFE闪退时触发断点:

可以看到传过来的 *ports 多余了一个空值,那么我们直接拦截住这个空值:

重新编译一下,再用COFE测试,发现已经可以添加了:

修复流股连接

如上文所述,我们虽然成功创建了单元模块,但是呢发现流股连接不上了,出现新的bug了

查找了一下示例代码,发现我们在 MyBlock.h 文件中实例化 myPortArrayparameterArray 数组的时候忘了添加引用计数函数 AddRef() 了,现在添加上:

上文中,我们拦截了第二次返回空值的 *post 参数,这样是不行的,不能一刀切直接拦截,修改一下:

然后呢,把我们的 parameterArray 数组赋值的方式改成和 myPortArray 一样:

再修改 MyBlockPortsArray.h 文件中匹配端口id的方式:

编译一下,再测试一下:

可以看到,现在出入口已经可以成功的连接上了,并且也获取到了我们的两个端口信息,同样的,Aspen也是可以的:

完美!

添加模块标识符

在上文中,我们给端口数组,端口都加上了唯一标识符,为了保险起见,我们也需要给单元模块加上标识符这个接口:

同样,在 MyBlock.h 文件中添加上模块名字和描述:

编译一下,再测试一下:

没什么问题,完美!