【Codesys-Runtime】-下位机组件代码结构初探
上一篇文章是从固件部署的角度来观察,看一下所谓的 Runtime 是由哪些文件构成的,虽然从参数配置文件中能猜测出一些处理逻辑,但是这距离下位机的实际开发工作还有一段距离。Codesys 的下位机 Runtime 的实现,是由非常多的、负责不同功能的模块构成的,这些模块被称作:组件(Component)。今天我们把镜头再拉进一些,初步探索一下组件的相关内容:[*]Codesys Runtime 软件架构;
[*]Runtime 中包含哪些功能 or 组件;
[*]组件内部的代码结构长什么样;
01-Codesys Runtime 软件架构上一篇文章中说过,Runtime 最主要的职责就是完成下面这些任务:
这七个功能是非常宏观、笼统的总结。为了完成这些功能,在 PLC 中有很多个组件,一起协同配合,各司其职。先来看一下官网中提供的一张组件结构图,图中最小的粒度就是组件:
可以看到,这是一个符合常规思路的分层、分模块软件架构。每一个方块都代表一个组件。有些功能模块是由一个组件独立完成的,有些功能模块则是由几个组件一起配合完成的。Codesys Runtime 在不同的硬件平台上部署的固件是有差异的,我们这里的测试平台是 x86 Linux,下载的 Runtime 固件是 CODESYS Control for Linux SL,所以下面就来看一下这个版本的固件中都包含了哪些固件。
02-Runtime 中包含哪些功能 or 组件方法一最直接的方式就是在下位机的命令行窗口中,进入 /opt/codesyscontrol/ 目录,手动启动 bin/codesyscontrol.bin 可执行程序,在启动阶段的打印信息中,可以看到所有加载的组件。因为组件比较多,这里只截取一部分:
上图中每一行都是加载的一个组件,包括:名称,Id号和版本信息,从名字上也可以大概猜测出来对应的功能。方法二:通过上位机集成开发环境(IDE)来查看下位机中加载了哪些组件。扫描设备-成功连接到下位机之后,在 【Device】标签页的左面,有【日志】选项,单击一下就可以看到下位机的日志了。在其中可以看到下位机中 Runtime 加载的所有组件(如果没有显示任何信息,点击一下组件旁边的绿色刷新按钮):
我这里显示加载的组件有(显式的顺序与加载的顺序是相反的):
我们在上位机 IDE 中的每一次操作,只要该功能需要下位机来执行,就一定需要其中的某一个或某几个组件来配合完成。举个例子-在上位机中执行【热复位】动作:1. 首先要通过网络传输到下位机吧,这是由网络部分的组件完成的,包括:CmpBlkDrvTcp/CmpBlkDrvUdp/CmpChannelXXX等组件;2. 传输的数据一定是满足特定协议的数据结构吧,数据解析工作是由组件 CmpBinTagUtil 完成的;3. 数据解析之后,就是执行具体的功能了,比如:对程序里的变量进行复位,这就需要 CmpApp 等组件来完成。【热复位】这个功能,就像一个纵向的逻辑链条,这个链条把不同层次上的组件串接起来,一起配合完成具体的功能。
03- 组件内部的代码结构长什么样在看代码之前,有两个前置问题需要说明一下,方便后续的理解。为什么要关心组件里的代码结构?Codesys Runtime 的软件架构设计的非常精巧,每一个组件之所以能完美的相互配合,就是因为每个组件都必须按照官方设计的代码规则来编写,也就是需要按照一套定义好的代码结构来编写、编译,然后部署到下位机中才能被加载、执行。Codesys 是一家非常牛X的方案提供商,当一个 PLC 厂家使用这套方案来开发 PLC 产品时,是需要进行二次开发的,让 Codesys 的这套方案能够与每一个不同型号的 PLC 硬件设备进行完美的融合。所以,作为嵌入式开发工程师,一定要理解组件里的代码结构。当然了,刚开始的时候只需要照葫芦画瓢,遵守既定的代码规则就行了。熟悉之后,可以再研究一下这些规则背后的设计思想。什么是 Codesys Runtime 开发 SDK?SDK 就是常规的意思:软件开发包,里面包含的就是一些头文件(.h),静态库(.a) 或者动态库(.so)文件。此外,为了方便开发者编写的组件满足特定的规则,SDK中还提供了一个助手工具(M4脚本程序),来简化组件的编写过程。在 Codesys 帮助页面上(https://content.helpme-codesys.com/zh-CHS/CODESYS%20Control/_rtsl_extension_extension_sdk.html),告诉我们在哪里可以找到 SDK:默认情况下,您可以在以下位置找到扩展 SDK:C:\Program Files\CODESYS <version>\CODESYS\CODESYS Control SL Extension Package\<version>\ExtensionSDK. SDK中内容如下:
在 src 目录下,有一个模板文件 CmpFrame.c 。此文件内容就是组件的基本代码结构,如下图所示(为了减少篇幅,这里只列出函数):/*****************************************************************************
*Copyright:Copyright CODESYS Development GmbH
*Program:Extension API for the Linux SL products
******************************************************************************/
#include "CmpStd.h"
#include DEP_H
USE_STMT
DLL_DECL int CDECL ComponentEntry(INIT_STRUCT *pInitStruct)
{
return ERR_OK;
}
static int CDECL ExportFunctions(void)
{
/* Macro to export functions */
EXPORT_STMT;
return ERR_OK;
}
static int CDECL ImportFunctions(void)
{
/* Macro to import functions */
IMPORT_STMT;
return ERR_OK;
}
static RTS_UI32 CDECL CmpGetVersion(void)
{
return CMP_VERSION;
}
static RTS_RESULT CDECL HookFunction(RTS_UI32 ulHook, RTS_UINTPTR ulParam1, RTS_UINTPTR ulParam2)
{
return ERR_OK;
}
看起来似乎也没有想象中那么复杂,是吧?那是因为很多细节都隐藏在几个宏定义中了,例如:USE_STMT,EXPORT_STMT,IMPORT_STMT。当然了,作为刚入门的开发者,是没有必要把这些规则背后的原理都理解清楚的。我们只需要遵守这些规则,把注意力放在我们自己要实现的功能代码上即可。另外,上面的显示的代码结构只是一个组件需要满足的最基本规则。如果是一个功能比较特殊的组件,例如:一个IO设备驱动组件,那么它需要遵守的规则就会更多,也就是说:有更多的特定函数需要添加到组件中。
页:
[1]