0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

【产品应用】用 AWTK 和 AWPLC 快速开发嵌入式应用程序 (4)- 自定义功能块(上)

ZLG致远电子 2022-11-02 09:56 次阅读

AWPLC 是 ZLG 自主研发的 PLC 系统(兼容 IEC61131-3),本文用定时器为例介绍一下如何扩展自定义功能块。

d64143d8-578f-11ed-b116-dac502259ad0.jpg 背景

AWTK 全称 Toolkit AnyWhere,是 ZLG 开发的开源 GUI 引擎,旨在为嵌入式系统、WEB、各种小程序、手机和 PC 打造的通用 GUI 引擎,为用户提供一个功能强大、高效可靠、简单易用、可轻松做出炫酷效果的 GUI 引擎。

AWPLC 是 ZLG 自主研发的 PLC 系统(兼容 IEC61131-3),其中 AWPLC 的运行时库(Runtime)基于 ZLG TKC 开发,可以移植到到任何主流 RTOS嵌入式系统。AWPLC 的集成开发环境(IDE)基于 AWTK 开发,可以运行在 Windows、MacOS 和 Linux 系统之上。AWPLC 的主要目标之一是把 PLC 中低代码开发方法引入到嵌入式软件,从而提高嵌入式软件的开发效率和可靠性。

d64143d8-578f-11ed-b116-dac502259ad0.jpg 简介

在前一篇文章中,我们说过,AWPLC 的重要特色之一就是高度可扩展,而且会内置 ZLG 多年在嵌入式系统开发中积累的功能块,包括各种算法、协议和实用功能,这将大大简化嵌入式软件的开发。

那怎么去开发自定义的功能块呢?本文以 ZTIMER 为例介绍一下开发自定义功能块的方法。ZTIMER 是一个带计数功能的定时器,在前一篇文章中,我们用它实现了一个走马灯的演示,其使用方法如下:

d64dfbe6-578f-11ed-b116-dac502259ad0.png

在 AWPLC 中,自定义功能块和内置功能块具有同等待遇,因为它们都是按同样的方式加入进来的。在进入正题前,我们先聊一下,系统的可扩展性以及实现方法。1.可扩展性的好处在设计一个复杂软件的架构时,可扩展性是必须考虑的因素。可扩展性至少带来以下几个好处:

  • 可扩展性将软件的框架与具体的实现分离开来,有助于降低系统的复杂度。系统的复杂性太高,会带来一系列的问题,比如让可理解性、可维护性和可靠性的降低,很多项目因此陷入无法挣脱的焦油坑里,最后士气低落,人员流失,项目取消,公司蒙受巨大损失。在设计复杂软件时,一定要存有敬畏之心;
  • 可扩展性将软件变化的部分隔离开来,不但可以让扩展的功能独立变化,也可以方便的扩展新功能。在 AWPLC 中,以后会扩展各种协议和算法的功能块,必须保证 AWPLC 框架和这些扩展的功能块是独立的,才能让开发工作顺利进行;
  • 可扩展性有利于团队的协作。不同的通讯协议和算法,需要不同团队的专家去开发,可扩展性让大家只要按相应的接口去实现,就可以方便的集成起来,不需要太多跨团队的交互。

2.如何保证可扩展性

让软件系统具有可扩展性,通常并不是什么难事,只要做到下面两点就可以了:

  • 针对接口编程。这个是大家都知道的,在《软件设计模式》等书里,都反复强调了,这里不再赘述;
  • 利用工厂模式隔离组件的创建。工厂模式也是人人都知道的,而且大家都觉得很"简单"。但是能把工厂模式用好的程序员其实并不多见,一个主要原因就是很多人只会套用《软件设计模式》的工厂模式,而《软件设计模式》里几个工厂模式在现实中并不实用。利用这些这些工厂模式,无法满足 SOLID 原则中的开放封闭原则,增加一个新的扩展时,仍然需要修改对应的工厂。

d64143d8-578f-11ed-b116-dac502259ad0.jpg AWPLC功能块的接口

要让 AWPLC 支持扩展各种自定义的功能块,首要条件条件是定义好功能块的接口。

1.功能块的基类在面向对象的 C 语言编程中,我们用结构(struct)来模拟类和接口。这里所说的接口是广义的接口,而不是 C++或其它语言中只包含纯虚函数的 interface,因为除了虚函数指针外,这里还有一些数据成员。

/**
*@classaw_plc_fb_t
* AWPLC 功能块接口。
*/
struct_aw_plc_fb_t{
/**
*@property{bool_t}en
*是否启用。
*/
uint8_ten:1;
/**
*@property{bool_t}eno
*是否启用输出。
*/
uint8_teno:1;

/*private*/
constaw_plc_fb_vtable_t*vt;

};

2.功能块的虚函数

在功能块的虚函数表中,还定义了一些描述性的常量,让对象具有一点反射的能力,方便在运行时查询它的一些状态。顺便说一下,在定义接口的虚函数时,通常不会有创建函数,因为创建之前对象之前,是拿不到这个虚表对象的。但也不是绝对的,有时为了方便 clone,也可能提供一个 clone 函数或者 create 函数。

任何接口都要定义析构函数(destroy),在对象需要销毁时,框架可以以统一的方式销毁它。

typedefstruct_aw_plc_fb_vtable_t{
/*功能块的类型名*/
constchar*type;
/*输入参数名称列表,以NULL结束的字符串数组*/
constchar*const*ins;
/*输出参数名称列表,以NULL结束的字符串数组*/
constchar*const*outs;
/*输入输出参数名称列表,以NULL结束的字符串数组*/
constchar*const*in_outs;
/*执行函数*/
aw_plc_fb_exec_texec;
/*执行函数(带参数)*/
aw_plc_fb_exec_ex_texec_ex;
/*获取属性(输入输出参数)的值*/
aw_plc_fb_get_prop_tget_prop;
/*获取输出的值*/
aw_plc_fb_get_output_tget_output;
/*设置输出的值*/
aw_plc_fb_set_input_tset_input;
/*析构函数*/
aw_plc_fb_destroy_tdestroy;

}aw_plc_fb_vtable_t;

* 这个虚函数表和 AWTK/TKC 中的 object 虚函数表很相似,考虑到 object 为了做得通用,有点臃肿了,所以决定重新定义一套。


d64143d8-578f-11ed-b116-dac502259ad0.jpg AWPLC功能块的工厂

前面我们说过,可扩展性除了针对接口编程外,离不开工厂模式的支持。功能块的工厂其任务当然是创建功能块了,所以提供了一个创建功能块的函数。参数 type 指定功能块的类型,函数返回对应类型的功能块:

/**
*@methodaw_plc_fb_factory_create_fb
*创建 fb。
*@param {const char*} type 类型。
*
*@return {aw_plc_fb_t*}返回 fb 对象。
*/

aw_plc_fb_t*aw_plc_fb_factory_create_fb(constchar*type);

有了这个创建函数,确实把创建任务与功能块的实现分开了。但是请想一下,如果每次增加新的功能块,都要修改这个创建函数,而这个函数又属于框架的一部分,框架是不是还是依赖于具体实现了呢?为了解决这个问题,我们需要提供一种注册机制来实现依赖倒置,让功能块的实现者主动将创建函数注册进来:

/**
*@methodaw_plc_fb_factory_register
*注册创建函数。
*@param {const char*} type 类型。
*@param {aw_plc_fb_create_t} create 创建函数。
*
*@return {ret_t}返回 RET_OK 表示成功,否则表示失败。
*/

ret_taw_plc_fb_factory_register(constchar*type,aw_plc_fb_create_tcreate);

这种机制非常好用,真正满足了 SOLID 原则中的开放封闭原则(OCP):扩展新的功能无需修改框架代码。在 ZLG 开源 GUI 引擎中,也大量使用了这种带注册功能的工厂模式,有兴趣的朋友可以去看看 AWTK 的代码。


d64143d8-578f-11ed-b116-dac502259ad0.jpg ZTIMER

前面我们说过,可扩展性除了针对接口编程外,离不开工厂模式的支持。功能块的工厂其任务当然是创建功能块了,所以提供了一个创建功能块的函数。参数 type 指定功能块的类型,函数返回对应类型的功能块:

1.ZTIMER的结构

在 C 语言中,一般用结构来模拟类,把基类作为结构的第一个成员来模拟继承。这里必须让 aw_plc_fb_t 作为 aw_plc_fb_ztimer_t 的第一个成员。

/**
*@classaw_plc_fb_ztimer_t
*@parentaw_plc_fb_t
*@annotation["fb"]
*循环定时器。
*
*>当输入 IN 为 TRUE 时,开始计时,输出 Q 为 FALSE,ET 开始记录过去的时间。
*>定时时间到时,COUNT 增加 1,输出 Q 在本次循环为 TRUE,ET 重置为0。
*>输入 IN 为 FALSE 时重置定时器。
*/
typedefstruct_aw_plc_fb_ztimer_t{
aw_plc_fb_tfb;

/**
*@property{bool_t}in
*@annotation["in"]
*为 TRUE 开始计时,为 FALSE 时重置定时器。
*/
bool_tin:1;

/**
*@property{iec_time_t}pt
*@annotation["in"]
*预设时间(ms)。
*/
iec_time_tpt;

...

}aw_plc_fb_ztimer_t;

这里的 API 注释采用了 AWTK 中定义的格式,但是对 annotation 做了一点扩展,增加了 3 个新的取值:

fb 表示这是一个功能块;

in 表示这是一个输入参数;

out 表示这是一个输出参数。

2.ZTIMER的实现

每个功能块必须提供虚函数表中定义的函数,不过主要代码集中 exec 函数里(其它函数可以自动生成出来):

staticret_taw_plc_fb_ztimer_exec(aw_plc_fb_t*fb){
aw_plc_fb_ztimer_t*ztimer=AW_PLC_FB_ZTIMER(fb);

if(aw_plc_fb_before_exec(fb)==RET_OK){
ztimer->current_time=aw_plc_now_ms();
if(ztimer->state==0&&!ztimer->prev_in&&ztimer->in){
ztimer->state=1;
ztimer->q=FALSE;

ztimer->et=0;
ztimer->count=0;
ztimer->start_time=ztimer->current_time;
}else{
if(!ztimer->in){
ztimer->q=FALSE;
ztimer->state=0;

ztimer->et=0;
ztimer->count=0;
ztimer->start_time=ztimer->current_time;
}elseif(ztimer->state==1){
if((ztimer->start_time+ztimer->pt)<= ztimer->current_time){
ztimer->q=TRUE;

ztimer->et=0;
ztimer->count++;
ztimer->start_time=ztimer->current_time;
}else{
ztimer->q=FALSE;
ztimer->et=ztimer->current_time-ztimer->start_time;
}
}
}
ztimer->prev_in=ztimer->in;
}

returnRET_OK;

}

3.注册ZTIMER

功能块需要注册到前面介绍的功能块工厂:

aw_plc_fb_factory_register(AW_PLC_FB_TYPE_ZTIMER,aw_plc_fb_ztimer_create);

坦白的讲,本文只是介绍了实现自定义功能块的关键步骤,实际工作要麻烦很多。如果手工去做这些工作,开发一个功能块还觉得好玩,而开发几十个甚至几百个功能块,人不会变疯就会变傻。下一篇文章会我们介绍一下,如何用代码生成器来完成这些单调的工作,让开发自定义功能块成为一项快乐的工作。

AWPLC 目前还处于开发阶段的早期,写这个系列文章的目的,除了用来验证目前所做的工作外,还希望得到大家的指点和反馈。如果您有任何疑问和建议,请在评论区留言。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式
    +关注

    关注

    5082

    文章

    19104

    浏览量

    304816
收藏 人收藏

    评论

    相关推荐

    AWTK-WEB 快速入门(2) - JS 应用程序

    导读AWTK可以使用相同的技术栈开发各种平台的应用程序。有时我们需要使用Web界面与设备进行交互,本文介绍一下如何使用JS语言开发AWTK-
    的头像 发表于 12-05 01:04 90次阅读
    <b class='flag-5'>AWTK</b>-WEB <b class='flag-5'>快速</b>入门(2) - JS <b class='flag-5'>应用程序</b>

    AWTK-WEB 快速入门(1) - C 语言应用程序

    导读AWTK可以使用相同的技术栈开发各种平台的应用程序。有时我们需要使用Web界面与设备进行交互,本文介绍一下如何使用C语言开发AWTK-W
    的头像 发表于 11-27 11:46 186次阅读
    <b class='flag-5'>AWTK</b>-WEB <b class='flag-5'>快速</b>入门(1) - C 语言<b class='flag-5'>应用程序</b>

    TPS659xx应用程序自定义工具

    电子发烧友网站提供《TPS659xx应用程序自定义工具.pdf》资料免费下载
    发表于 11-06 10:02 0次下载
    TPS659xx<b class='flag-5'>应用程序</b><b class='flag-5'>自定义</b>工具

    创建自定义的基于闪存的引导加载程序(BSL)

    电子发烧友网站提供《创建自定义的基于闪存的引导加载程序(BSL).pdf》资料免费下载
    发表于 09-19 10:50 0次下载
    创建<b class='flag-5'>自定义</b>的基于闪存的引导加载<b class='flag-5'>程序</b>(BSL)

    七大嵌入式GUI盘点

    采用纯C语言开发。它的作者是来自匈牙利的Gabor Kiss-Vamosikisvegabor,LVGLC语言编写,以实现最大的兼容性(与C++兼容),模拟器可在没有嵌入式硬件的PC
    发表于 09-02 10:58

    AWTK使用经验】如何添加中文输入法

    AWTK是基于C语言开发的跨平台GUI框架。《AWTK使用经验》系列文章将介绍开发AWTK过程中一些常见问题与解决方案,例如:如何加载外部资
    的头像 发表于 06-20 08:25 1035次阅读
    【<b class='flag-5'>AWTK</b>使用经验】如何添加中文输入法

    AWTK使用经验】如何响应物理按键

    AWTK是基于C语言开发的跨平台GUI框架。《AWTK使用经验》系列文章将介绍开发AWTK过程中一些常见问题与解决方案,例如:如何加载外部资
    的头像 发表于 06-06 08:25 790次阅读
    【<b class='flag-5'>AWTK</b>使用经验】如何响应物理按键

    AWTK使用经验】如何自定义combo_box下拉框样式

    AWTK是基于C语言开发的跨平台GUI框架。《AWTK使用经验》系列文章将介绍开发AWTK过程中一些常见问题与解决方案,例如:如何加载外部资
    的头像 发表于 05-23 08:25 453次阅读
    【<b class='flag-5'>AWTK</b>使用经验】如何<b class='flag-5'>自定义</b>combo_box下拉框样式

    HarmonyOS开发案例:【 自定义弹窗】

    基于ArkTS的声明开发范式实现了三种不同的弹窗,第一种直接使用公共组件,后两种使用CustomDialogController实现自定义弹窗
    的头像 发表于 05-16 18:18 1353次阅读
    HarmonyOS<b class='flag-5'>开发</b>案例:【 <b class='flag-5'>自定义</b>弹窗】

    AWTK 开源串口屏开发(18) - C 语言自定义命令

    如果AWTK-HMI内置模型无法满足需求,可以使用C语言来扩展默认模型。本文通过一个简单的例子,介绍一下C语言扩展默认模型的方法。AWTK-HMI内置了不少模型,利用这些模型开发
    的头像 发表于 05-11 08:24 436次阅读
    <b class='flag-5'>AWTK</b> 开源串口屏<b class='flag-5'>开发</b>(18) - <b class='flag-5'>用</b> C 语言<b class='flag-5'>自定义</b>命令

    HarmonyOS实战开发-深度探索与打造个性化自定义组件

    ,容器组件,媒体组件,绘制组件,画布组件组件等,如Button、Text 是基础组件。 由开发者在基础组件基础 添加一些封装和修饰 定义的组件称为自定义组件。
    发表于 05-08 16:30

    HarmonyOS开发实例:【自定义Emitter】

    使用[Emitter]实现事件的订阅和发布,使用[自定义弹窗]设置广告信息。
    的头像 发表于 04-14 11:37 995次阅读
    HarmonyOS<b class='flag-5'>开发</b>实例:【<b class='flag-5'>自定义</b>Emitter】

    鸿蒙ArkUI实例:【自定义组件】

    组件是 OpenHarmony 页面最小显示单元,一个页面可由多个组件组合而成,也可只由一个组件组合而成,这些组件可以是ArkUI开发框架自带系统组件,比如 `Text` 、 `Button` 等,也可以是自定义组件,本节笔者简单介绍一下
    的头像 发表于 04-08 10:17 632次阅读

    【从0开始创建AWTK应用程序】编译应用到RTOS平台

    AWTK是基于C语言开发的跨平台GUI框架。本系列文章介绍如何从0开始创建AWTK应用程序,包括搭建开发调试环境、使用
    的头像 发表于 03-21 08:23 606次阅读
    【从0开始创建<b class='flag-5'>AWTK</b><b class='flag-5'>应用程序</b>】编译应用到RTOS平台

    博途用户自定义库的使用

    中经常使用的函数/函数/数据类型等存放到自定义库中,方便自己使用及与别人共享。博途具有很强的库管理功能,包括:库版本管理,库的更新及清扫等等。本系列文章我将给大家介绍项目库、全局库、库的更新/清扫等
    的头像 发表于 12-25 10:08 914次阅读
    博途用户<b class='flag-5'>自定义</b>库的使用