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

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

3天内不再提示

用AWTK和AWPLC快速开发自定义功能块

AGk5_ZLG_zhiyua 来源:ZLG致远电子 作者:ZLG开发者社区 2022-11-02 13:22 次阅读

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

背景

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 中低代码开发方法引入到嵌入式软件,从而提高嵌入式软件的开发效率和可靠性。

简介

在前一篇文章中,我们介绍了自定义 AWPLC 功能块的基本方法,但是有些部分的内容并没有提到,比如:

1. 功能块的部分虚函数的实现。这些函数在不同功能块中的实现是不同的,所以要做成虚函数,但是在各个功能块中的实现又是相似的,不得不去写一遍。比如 get_prop 这个函数,它在 ZTIMER 中的实现如下:

staticret_taw_plc_fb_ztimer_get_prop(aw_plc_fb_t*fb,constchar*name,value_t*v){
aw_plc_fb_ztimer_t*ztimer=AW_PLC_FB_ZTIMER(fb);

if(tk_str_eq(name,AW_PLC_FB_ZTIMER_PROP_IN)){
value_set_bool(v,ztimer->in);
returnRET_OK;
}

if(tk_str_eq(name,AW_PLC_FB_ZTIMER_PROP_PT)){
value_set_uint64(v,ztimer->pt);
returnRET_OK;
}

if(tk_str_eq(name,AW_PLC_FB_ZTIMER_PROP_Q)){
value_set_bool(v,ztimer->q);
returnRET_OK;
}

if(tk_str_eq(name,AW_PLC_FB_ZTIMER_PROP_ET)){
value_set_uint64(v,ztimer->et);
returnRET_OK;
}

if(tk_str_eq(name,AW_PLC_FB_ZTIMER_PROP_COUNT)){
value_set_uint32(v,ztimer->count);
returnRET_OK;
}

returnRET_NOT_FOUND;

}

*这样的代码看起来很简单,但是恰恰容易出错,更容易让人厌倦,没有什么乐趣。

2. API 和结构的注释。我们来看看 ZTIMER 的结构注释:

/**
*@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;

/**
*@property{bool_t}q
*@annotation["default","out"]
*定时时间是否到(仅在时间到的当次循环为 TRUE)。
*/
bool_tq:1;

/**
*@property{iec_time_t}et
*@annotation["out"]
*过去时间(ms)。
*/
iec_time_tet;

/**
*@property{uint32_t}count
*@annotation["out"]
*定时器时间到的次数。
*/
uint32_tcount;

/**
*@property{bool_t}prev_in
*@annotation["private"]
*前一次的输入。
*/
bool_tprev_in:1;

/**
*@property{uint8_t}state
*@annotation["private"]
*状态。
*/
uint8_tstate;

/**
*@property{iec_time_t}current_time
*@annotation["private"]
*当前时间(ms)。
*/
iec_time_tcurrent_time;

/**
*@property{iec_time_t}start_time
*@annotation["private"]
*开始时间(ms)。
*/
iec_time_tstart_time;

}aw_plc_fb_ztimer_t;

*上面的代码看起来很美观,读起来很舒服,但是写起来却是有些费劲。3. IDE 需要功能块的描述信息,以方便把它呈现到界面上。比如 ZTIMER 的描述信息如下:

{
"type":"fb_zlg_misc.ztimer",
"real_type":"ZTIMER",
"helpUrl":"https://developer.zlg.cn",
"style":"fb",
"desc":"循环定时器。 >当输入 IN 为 TRUE 时,开始计时,输出 Q 为 FALSE,ET 开始记录过去的时间。
>定时时间到时,COUNT 增加 1,输出 Q 在本次循环为 TRUE,ET 重置为0。 >输入 IN 为 FALSE 时重置定时器。",
"ins":[
{
"name":"IN",
"desc":"为 TRUE 开始计时,为 FALSE 时重置定时器。",
"min_connections":1,
"max_connections":1,
"data_type":"BOOL"
},
{
"name":"PT",
"desc":"预设时间(ms)。",
"min_connections":1,
"max_connections":1,
"data_type":"TIME"
}
],
"outs":[
{
"name":"Q",
"desc":"定时时间是否到(仅在时间到的当次循环为 TRUE)。",
"data_type":"BOOL"
},
{
"name":"ET",
"desc":"过去时间(ms)。",
"data_type":"TIME"
},
{
"name":"COUNT",
"desc":"定时器时间到的次数。",
"data_type":"DWORD"
}
]

}

*这个 JSON 文件中的内容,和前面结构的注释很相似,除了呈现的格式不同,同时还加了一些新内容。4. IDE 需要的文档。功能块需要提供一个 markdown 文档,这个文档会被转换成 html,在用户查看帮助时显示给用户。ZTIMER 的文档内容如下:

#ZTIMER

##功能

循环定时器。

>当输入 IN 为 TRUE 时,开始计时,输出 Q 为 FALSE,ET 开始记录过去的时间。
>定时时间到时,COUNT 增加 1,输出 Q 在本次循环为 TRUE,ET 重置为0。
>输入 IN 为 FALSE 时重置定时器。

##输入

* IN **BOOL**为 TRUE 开始计时,为 FALSE 时重置定时器。
* PT **TIME**预设时间(ms)。

##输出

* Q **BOOL**定时时间是否到(仅在时间到的当次循环为 TRUE)。
* ET **TIME**过去时间(ms)。

* COUNT **DWORD**定时器时间到的次数。 *这个文档的内容和前面结构的注释,除了形式不同,内容是差不多的。很抱歉贴了这么代码,希望您并没仔细去读它们。不要被这些代码吓到,它们都是自动生成的。如果手工去写这些代码,一天能写一个功能块就不错了,不但辛苦而且容易出错。这些工作必须自动完成!所以 AWPLC 中提供了一个代码生成器,实测这个代码生成器让工作效率提高 10倍,幸福指数提高 10倍。

在进入正题前,我们先聊一下代码生成器的基本知识。

代码生成器基本知识

* 编写能编写代码的代码。-- 《程序员修炼之道》

代码生成器是一个普通程序,它能够生成另外的目标代码。可以不要代码生成器,直接编写目标代码吗?通常情况下是可以的,但是这违背了优秀程序员的第一美德-懒惰。因为懒惰,所以能让计算机做的事,优秀程序员是不会自己去做的。

这里所说的目标代码,也并非一定是严格意义上的代码,也可能是另外一些数据。当然,有时候要严格区分数据和代码,本身就是一件困难的事情。不过,这不是我们要说的重点,重点是通过代码生成器提升我们的工作效率。*一个人的数据就是另外一个人的代码。--《编程珠玑 II》1.代码生成器的分类要说分类,就要先说分类的标准,在不同的分类标准和分类依据下,分出的类别迥异。《程序员修炼之道》里提到的一个分类标准具有极强的实用意义,这里我们重点介绍一下。它根据生成的目标代码是否需要二次修改来分类,将代码生成器分为两类:
  • 被动代码生成器目标代码生成之后,需要进行修改和完善,然后独立发展和维护,与代码生成器再与关系。比如 IDE 的 Wizard 就是此例。前面提到的自定义控件生成器,代码生成之后,你需要在上面添加需要的功能。如果过了一段时间,你想为控件添加一个新的属性,可能会遇到一点麻烦,要么手工添加;要么重新生成代码,然后把之前修改的代码重新加上,无论哪种方式都不是愉快的方式。被动代码生成器虽然有它的缺陷,但是仍然可以给我们带来很大帮助。

  • 主动代码生成器目标代码生成之后,不需要进行修改和完善,每次都重新生成,如果需要修改,修改元数据和代码生成器。比如编译器就是此例。前面提到的 MVVM 的 ViewModel 和 AWFlow 应用代码生成也属于此类。如果可以,优先使用主动代码生成器。

2.基本形式71186a28-5a63-11ed-a3b6-dac502259ad0.png

这是代码生成器的基本形式:代码生成器读取元数据,生成目标代码。元数据是描述数据的数据,这里是描述目标代码的数据,也就是控制目标代码的参数。一般情况下,目标代码整体结构由代码生成器决定,而变化的部分由元数据决定。

代码生成器本身一个很有意思的话题,有机会可以专门来聊聊,本文就不扯远了。

AWPLC中的代码生成器

按前面代码生成器的分类方式,AWPLC 里实现了一个主动代码生成器,实现成主动代码生成器是很重要的,AWPLC 还在快速迭代中,有些接口可能会变化,主动代码生成器保证,即使接口有变化,也只需要运行一些脚本,重新生成目标文件即可。

1.基本架构

AWPLC 功能块代码生成器架构如下图所示。其中功能块描述文件就是前面所说的元数据,代码生成器用它生成前面介绍的各种代码和数据。713afc82-5a63-11ed-a3b6-dac502259ad0.png

2.功能块描述文件格式

描述文件用标准的 JSON 格式,其内容包括两个部分:

2.1基本信息

基本信息包括:

  • name 功能块的名称。英文小写,必须是合格的 C 语言变量名;
  • category 功能块所属的分类。各层级之间用/分隔,它决定了生成文件的位置;
  • is_function_block true 表示功能块,false 表示函数;
  • impl 具体实现的源文件;
  • author 作者联系方式;
  • version 版本号;;
  • date 更新时间;
  • desc 功能描述;
  • properties 属性列表。具体定义如下。

示例:

"name":"ztimer",
"category":"zlg/misc",
"is_function_block":true,
"impl":"input/zlg/misc/ztimer.c",
"author":"LiXianJing",
"desc":"循环定时器。 >当输入 IN 为 TRUE 时,开始计时,输出 Q 为 FALSE,ET 开始记录过去的时间>。 >定时时间到时,COUNT 增加 1,输出 Q 在本次循环为 TRUE,ET 重置为0。 >输入 IN 为 FALSE 时重
置定时器。",

2.2属性描述对于每个属性,又包括下列信息:
  • name 属性名;
  • desc 属性描述;
  • type 实际的数据类型;
  • data_type(可选)用于在 IDE 中时类型检查,缺省为 type 对应的 IEC 的数据类型,但是有时可用 ANY_INT 和 ANY_NUM 等来放宽类型检查;
  • annotation 用于额外的标识。目前主要用于指定输入输出等特性。
示例:

{
"name":"count",
"desc":"定时器时间到的次数。",
"type":"uint32_t",
"annotation":{
"out":true
}

},

2.3使用方法

代码生成器用 nodejs 编写,需要安装 nodejs。具体用法如下:

node gen.js 描述文件名。

如:

nodegen.jsinput/zlg/misc/ztimer.json

上面介绍了用 C 语言开发原生功能块的方法。当然,也可以用 IEC 61131-3 中一些语言开发功能块,除此之外,AWPLC 还会支持用 AWBlock 开发功能块,在后续文章中,我们将一一介绍,敬请关注。AWPLC 目前还处于开发阶段的早期,写这个系列文章的目的,除了用来验证目前所做的工作外,还希望得到大家的指点和反馈。如果您有任何疑问和建议,请在评论区留言。

审核编辑:汤梓红

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

    关注

    5010

    文章

    13275

    浏览量

    463080
  • 定时器
    +关注

    关注

    23

    文章

    3246

    浏览量

    114725
  • zlg
    zlg
    +关注

    关注

    1

    文章

    63

    浏览量

    38156
  • 功能块
    +关注

    关注

    0

    文章

    18

    浏览量

    6082
收藏 人收藏

    评论

    相关推荐

    如何使用BMLang开发自定义的算子?

    如何使用BMLang开发自定义的算子
    发表于 09-18 06:57

    怎么样去开发自定义应用程序?

    Atmel小贴士 如何开发自定义应用程序
    的头像 发表于 07-11 00:05 2359次阅读

    如何用AWTKAWPLC快速开发嵌入式应用程序

    AWPLC 是 ZLG 自主研发的 PLC 系统(兼容 IEC61131-3),本文一个简单的温度控制系统,来演示如何用 AWTKAWPLC
    的头像 发表于 09-26 11:46 1395次阅读

    基于AWTKAWPLC开发走马灯程序

    AWPLC 是 ZLG 自主研发的 PLC 系统(兼容 IEC61131-3),本文一个简单的走马灯程序,来演示如何用 AWTKAWPLC
    的头像 发表于 10-12 11:48 1044次阅读

    基于AWTKAWPLC开发自定义功能块

    AWPLC 是 ZLG 自主研发的 PLC 系统(兼容 IEC61131-3),本文定时器为例介绍一下如何扩展自定义功能块
    的头像 发表于 10-26 11:50 929次阅读

    【产品应用】 AWTKAWPLC 快速开发嵌入式应用程序 (8)- AWBlock

    AWBlock是ZLG开发的可视化编程语言,具有功能块图的易用性和结构文本语言的灵活性,支持AWBlock是AWPLC的一个重要特色,充分利用AWBlock,可以大大提高开发效率。  
    的头像 发表于 12-02 11:45 658次阅读

    【产品应用】 AWTKAWPLC 快速开发嵌入式应用程序 (9)- 模块化

    AWPLC 为模块化编程提供了良好支持,本文以简化版的红绿灯为例,把状态转换逻辑封装成独立的功能块,演示了AWPLC模块化编程的基本方法。    背景 AWTK  全称 Toolkit
    的头像 发表于 12-07 11:40 793次阅读

    【产品应用】 AWTKAWPLC 快速开发嵌入式应用程序 (2)-走马灯

    AWPLC 是 ZLG 自主研发的 PLC 系统(兼容 IEC61131-3),本文一个简单的走马灯程序,来演示如何用 AWTKAWPLC
    的头像 发表于 05-31 18:05 508次阅读
    【产品应用】<b class='flag-5'>用</b> <b class='flag-5'>AWTK</b> 和 <b class='flag-5'>AWPLC</b> <b class='flag-5'>快速</b><b class='flag-5'>开发</b>嵌入式应用程序 (2)-走马灯

    AWTKAWPLC快速开发嵌入式应用程序 (1)-温度控制器

    AWPLC是ZLG自主研发的PLC系统(兼容IEC61131-3),本文一个简单的温度控制系统,来演示如何用AWTKAWPLC快速
    的头像 发表于 09-28 09:57 709次阅读
    <b class='flag-5'>用</b><b class='flag-5'>AWTK</b>和<b class='flag-5'>AWPLC</b><b class='flag-5'>快速</b><b class='flag-5'>开发</b>嵌入式应用程序 (1)-温度控制器

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

    AWPLC是ZLG自主研发的PLC系统(兼容IEC61131-3),本文定时器为例介绍一下如何扩展自定义功能块。背景AWTK全称Toolk
    的头像 发表于 11-02 09:56 742次阅读
    【产品应用】<b class='flag-5'>用</b> <b class='flag-5'>AWTK</b> 和 <b class='flag-5'>AWPLC</b> <b class='flag-5'>快速</b><b class='flag-5'>开发</b>嵌入式应用程序 (4)- <b class='flag-5'>自定义</b><b class='flag-5'>功能块</b>(上)

    【产品应用】 AWTKAWPLC 快速开发嵌入式应用程序 (3)- 定时器

    AWPLC是ZLG自主研发的PLC系统(兼容IEC61131-3),本文一个定时器实现的走马灯程序,来演示如何用AWTKAWPLC
    的头像 发表于 11-02 10:02 740次阅读
    【产品应用】<b class='flag-5'>用</b> <b class='flag-5'>AWTK</b> 和 <b class='flag-5'>AWPLC</b> <b class='flag-5'>快速</b><b class='flag-5'>开发</b>嵌入式应用程序 (3)- 定时器

    【产品应用】 AWTKAWPLC 快速开发嵌入式应用程序 (5)- 自定义功能块(下)

    AWPLC是ZLG自主研发的PLC系统(兼容IEC61131-3),本文以定时器为例介绍一下如何扩展自定义功能块,以及代码生成器的用法。背景AWTK全称ToolkitAnyWhere,
    的头像 发表于 11-04 10:12 835次阅读
    【产品应用】<b class='flag-5'>用</b> <b class='flag-5'>AWTK</b> 和 <b class='flag-5'>AWPLC</b> <b class='flag-5'>快速</b><b class='flag-5'>开发</b>嵌入式应用程序 (5)- <b class='flag-5'>自定义</b><b class='flag-5'>功能块</b>(下)

    【产品应用】 AWTKAWPLC 快速开发嵌入式应用程序 (8)- AWBlock

    AWBlock是ZLG开发的可视化编程语言,具有功能块图的易用性和结构文本语言的灵活性,支持AWBlock是AWPLC的一个重要特色,充分利用AWBlock,可以大大提高开发效率。背景
    的头像 发表于 12-05 15:26 494次阅读
    【产品应用】<b class='flag-5'>用</b> <b class='flag-5'>AWTK</b> 和 <b class='flag-5'>AWPLC</b> <b class='flag-5'>快速</b><b class='flag-5'>开发</b>嵌入式应用程序 (8)- AWBlock

    【产品应用】 AWTKAWPLC 快速开发嵌入式应用程序 (9)- 模块化

    AWPLC为模块化编程提供了良好支持,本文以简化版的红绿灯为例,把状态转换逻辑封装成独立的功能块,演示了AWPLC模块化编程的基本方法。背景AWTK全称ToolkitAnyWhere,
    的头像 发表于 12-09 11:11 668次阅读
    【产品应用】<b class='flag-5'>用</b> <b class='flag-5'>AWTK</b> 和 <b class='flag-5'>AWPLC</b> <b class='flag-5'>快速</b><b class='flag-5'>开发</b>嵌入式应用程序 (9)- 模块化

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

    编写代码即可实现常见的应用。但是,有时候我们需要自定义一些命令,以实现一些特殊的功能。本文档介绍如何使用C语言自定义命令。1.实现hmi_model_cmd_t接口
    的头像 发表于 05-11 08:24 437次阅读
    <b class='flag-5'>AWTK</b> 开源串口屏<b class='flag-5'>开发</b>(18) - <b class='flag-5'>用</b> C 语言<b class='flag-5'>自定义</b>命令