用AWTK和AWPLC快速开发嵌入式应用程序(4)自定义功能块(上)
AWPLC 目前还处于开发阶段的早期,写这个系列文章的目的,除了用来验证目前所做的工作外,还希望得到大家的指点和反馈。如果您有任何疑问和建议,请在评论区留言。 1. 背景
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 中 低代码开发方法 引入到嵌入式软件,从而提高嵌入式软件的开发效率和可靠性。 2. 简介
在前一篇文章中,我们说过,AWPLC 的重要特色之一就是高度可扩展,而且会内置 ZLG 多年在嵌入式系统开发中积累的功能块,包括各种算法、协议和实用功能,这将大大简化嵌入式软件的开发。
那怎么去开发自定义的功能块呢?本文以 ZTIMER 为例介绍一下开发自定义功能块的方法。ZTIMER 是一个带计数功能的定时器,在前一篇文章中,我们用它实现了一个走马灯的演示,其使用方法如下:
编辑切换为居中
添加图片注释,不超过 140 字(可选)
在 AWPLC 中,自定义功能块和内置功能块具有同等待遇,因为它们都是按同样的方式加入进来的。在进入正题前,我们先聊一下,系统的可扩展性以及实现方法。 2.1 可扩展性的好处
在设计一个复杂软件的架构时,可扩展性是必须考虑的因素。可扩展性至少带来以下几个好处: 可扩展性将软件的框架与具体的实现分离开来,有助于降低系统的复杂度。系统的复杂性太高,会带来一系列的问题,比如让可理解性、可维护性和可靠性的降低,很多项目因此陷入无法挣脱的焦油坑里,最后士气低落,人员流失,项目取消,公司蒙受巨大损失。在设计复杂软件时,一定要存有敬畏之心。 可扩展性将软件变化的部分隔离开来,不但可以让扩展的功能独立变化,也可以方便的扩展新功能。在 AWPLC 中, 以后会扩展各种协议和算法的功能块,必须保证 AWPLC 框架和这些扩展的功能块是独立的,才能让开发工作顺利进行。 可扩展性有利于团队的协作。 不同的通讯协议和算法,需要不同团队的专家去开发,可扩展性让大家只要按相应的接口去实现,就可以方便的集成起来,不需要太多跨团队的交互。 2.2 如何保证可扩展性
让软件系统具有可扩展性,通常并不是什么难事,只要做到下面两点就可以了: 针对接口编程。这个是大家都知道的,在《软件设计模式》等书里,都反复强调了,这里不再赘述。 利用工厂模式隔离组件的创建。工厂模式也是人人都知道的,而且大家都觉得很"简单"。但是能把工厂模式用好的程序员其实并不多见,一个主要原因就是很多人只会套用《软件设计模式》的工厂模式,而《软件设计模式》里几个工厂模式在现实中并不实用。利用这些这些工厂模式,无法满足 SOLID 原则中的开放封闭原则,增加一个新的扩展时,仍然需要修改对应的工厂。 3. AWPLC 功能块的接口
要让 AWPLC 支持扩展各种自定义的功能块,首要条件条件是定义好功能块的接口。 3.1 功能块的基类
在面向对象的 C 语言编程中,我们用结构 (struct) 来模拟类和接口。这里所说的接口是广义的接口,而不是 C++或其它语言中只包含纯虚函数的 interface,因为除了虚函数指针外,这里还有一些数据成员。 /** * @class aw_plc_fb_t * AWPLC 功能块接口。 */ struct _aw_plc_fb_t { /** * @property {bool_t} en * 是否启用。 */ uint8_t en : 1; /** * @property {bool_t} eno * 是否启用输出。 */ uint8_t eno : 1; /*private*/ const aw_plc_fb_vtable_t* vt; }; 3.2 功能块的虚函数
在功能块的虚函数表中,还定义了一些描述性的常量,让对象具有一点反射的能力,方便在运行时查询它的一些状态。顺便说一下,在定义接口的虚函数时,通常不会有创建函数,因为创建之前对象之前,是拿不到这个虚表对象的。但也不是绝对的,有时为了方便 clone,也可能提供一个 clone 函数或者 create 函数。
任何接口都要定义析构函数 (destroy),在对象需要销毁时,框架可以以统一的方式销毁它。 typedef struct _aw_plc_fb_vtable_t { /*功能块的类型名*/ const char* type; /*输入参数名称列表,以 NULL 结束的字符串数组*/ const char* const* ins; /*输出参数名称列表,以 NULL 结束的字符串数组*/ const char* const* outs; /*输入输出参数名称列表,以 NULL 结束的字符串数组*/ const char* const* in_outs; /*执行函数*/ aw_plc_fb_exec_t exec; /*执行函数(带参数)*/ aw_plc_fb_exec_ex_t exec_ex; /*获取属性(输入输出参数)的值*/ aw_plc_fb_get_prop_t get_prop; /*获取输出的值*/ aw_plc_fb_get_output_t get_output; /*设置输出的值*/ aw_plc_fb_set_input_t set_input; /*析构函数*/ aw_plc_fb_destroy_t destroy; } aw_plc_fb_vtable_t;
这个虚函数表和 AWTK/TKC 中的 object 虚函数表很相似,考虑到 object 为了做得通用,有点臃肿了,所以决定重新定义一套。4. AWPLC 功能块的工厂
前面我们说过,可扩展性除了针对接口编程外,离不开工厂模式的支持。功能块的工厂其任务当然是创建功能块了,所以提供了一个创建功能块的函数。参数 type 指定功能块的类型,函数返回对应类型的功能块: /** * @method aw_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(const char* type);
有了这个创建函数,确实把创建任务与功能块的实现分开了。但是请想一下,如果每次增加新的功能块,都要修改这个创建函数,而这个函数又属于框架的一部分,框架是不是还是依赖于具体实现了呢?为了解决这个问题,我们需要提供一种注册机制来实现依赖倒置,让功能块的实现者主动将创建函数注册进来: /** * @method aw_plc_fb_factory_register * 注册创建函数。 * @param {const char*} type 类型。 * @param {aw_plc_fb_create_t} create 创建函数。 * * @return {ret_t} 返回 RET_OK 表示成功,否则表示失败。 */ ret_t aw_plc_fb_factory_register(const char* type, aw_plc_fb_create_t create);
这种机制非常好用,真正满足了 SOLID 原则中的开放封闭原则 (OCP):扩展新的功能无需修改框架代码。在 ZLG 开源 GUI 引擎中,也大量使用了这种带注册功能的工厂模式,有兴趣的朋友可以去看看 AWTK 的代码。 5. ZTIMER5.1 ZTIMER 的结构
在 C 语言中,一般用结构来模拟类,把基类作为结构的第一个成员来模拟继承。这里必须让 aw_plc_fb_t 作为 aw_plc_fb_ztimer_t 的第一个成员。 /** * @class aw_plc_fb_ztimer_t * @parent aw_plc_fb_t * @annotation ["fb"] * 循环定时器。 * * > 当输入 IN 为 TRUE 时,开始计时,输出 Q 为 FALSE,ET 开始记录过去的时间。 * > 定时时间到时,COUNT 增加 1, 输出 Q 在本次循环为 TRUE,ET 重置为 0。 * > 输入 IN 为 FALSE 时重置定时器。 */ typedef struct _aw_plc_fb_ztimer_t { aw_plc_fb_t fb; /** * @property {bool_t} in * @annotation ["in"] * 为 TRUE 开始计时,为 FALSE 时重置定时器。 */ bool_t in : 1; /** * @property {iec_time_t} pt * @annotation ["in"] * 预设时间 (ms)。 */ iec_time_t pt; ... } aw_plc_fb_ztimer_t;
这里的 API 注释采用了 AWTK 中定义的格式,但是对 annotation 做了一点扩展,增加了 3 个新的取值: fb 表示这是一个功能块。 in 表示这是一个输入参数。 out 表示这是一个输出参数。 5.2 ZTIMER 的实现
每个功能块必须提供虚函数表中定义的函数,不过主要代码集中 exec 函数里(其它函数可以自动生成出来): static ret_t aw_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; } else if (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; } return RET_OK; } 5.3 注册 ZTIMER
功能块需要注册到前面介绍的功能块工厂: aw_plc_fb_factory_register(AW_PLC_FB_TYPE_ZTIMER, aw_plc_fb_ztimer_create);
坦白的讲,本文只是介绍了实现自定义功能块的关键步骤,实际工作要麻烦很多。如果手工去做这些工作,开发一个功能块还觉得好玩,而开发几十个甚至几百个功能块,人不会变疯就会变傻。下一篇文章会我们介绍一下,如何用代码生成器来完成这些单调的工作,让开发自定义功能块成为一项快乐的工作。
农村大集上,为何总有人高价收酒,过来人试了才知,是套路若是您住在农村,或者赶过农村大集,肯定见过马路边上,总会有小贩坐在那,身旁支个小摊,放个大喇叭,然后大肆宣传高价回收各种老酒名酒,一坐就是整天,甚至还提供上门服务。此种现象,笔者在
大实话女人在什么时候生孩子损失最小?前天,打开知乎后台,我发现有好几个人邀请我去回答这个问题我思考了两天,迟迟没下笔,还跟大象我俩讨论了半小时。首先我在思考一个问题,那就是生孩子这件事,对于女性来说,是必然会有损失的
体坛早七点(2022。7。21)球类运动足球北京时间7月20日,东亚杯小组赛首轮,中国对阵韩国。上半场,韩国从开场后就多次创造出进攻机会,韩佳奇多次贡献扑救,蒋光太屡次解围,但朱辰杰不慎自摆乌龙下半场,中国后防屡
除了米诺地尔之外,还有没有什么成本低生活中常见的平替?抛开原因去谈效果是耍流氓,你可能会经常刷到,吃什么什么东西对身体好对头发好,这就是瞎说。吃什么对头发好?胡萝卜番茄橘子橙子猕猴桃黑芝麻干果,这些东西还用特别去吃?你是因为头皮卫生的
王冰冰徐嘉余疑似公开恋情徐先生,余生请多忍耐我7月20日,网传央视主播王冰冰和游泳世界冠军徐嘉余疑似公开恋情,女方朋友圈两张图晒照,配文那就在一起,黄昏与四季,徐先生,余生请多忍耐我。目前徐嘉余还没有对此事进行正式回应,不过他
从拜登访问沙特看美元当前地位瞌睡乔拜登在7月1516日去了沙特,见了沙特王储穆罕默德本萨勒曼,表面上看是劝说沙特支持制裁俄罗斯和增产原油,实际上真的如此吗?我们先回顾下瞌睡乔出访前后的几件大事俄罗斯抛弃美元用
不炒作,不整容,这10位中年爆红的女演员,打了多少流量明星的脸海清曾道出中年女演员的心酸市场题材各种局限,常常让我们远离一些优秀的作品。诚然,中年危机从没有放过女演员,男演员四十岁还能和年轻演员演情侣,而女演员到了四十岁却要演婆婆妈妈了。不过
第九章息灭暗之社离这里有些远啊,8小时路程还是有点漫长。月光撒下,只见一个模糊的黑影缓缓前进。在黑影正当穿过迷幻之林与妖精之林的交界处时,不远处的火光引起它的注意。只见黑影逐渐凝聚,转变形态
袭人和贾宝玉偷试多年,为何一直没怀孕?看赵姨娘就是答案我国历史文化源远流长,远至秦朝,近至明清,每一朝代都留有传奇的历史文化供人们欣赏。就这些历史文化而言,有的值得我们学习,有的却是文化糟粕,而这些文化都可以通过书籍的撰写记录下来,供
斯里兰卡经济崩溃,究竟哪里出问题了?今年以来,素有印度洋明珠美称的南亚岛国斯里兰卡遭遇严重社会经济危机,进入7月份后情况更是急转直下,政府宣告破产,总统也于近日辞职并离境。斯里兰卡全国已进入紧急状态,然而外汇见底物价
满舒克巡演现场听到观众求助后暂停演出,指挥安保现场活捉色狼近几年,随着国内演出市场规模飞速扩大,进入Livehouse现场观演的观众变多,观众素质也变得参差不齐。其中最恶劣的,便是那些在演出现场借着现场嘈杂灯光昏暗而趁机做出性骚扰猥亵等行