代码模板引擎

功能介绍

mkTpl是一个基于Python编写的模板引擎包. 初始版本发布于2017年, 当时开发该模板引擎是为了在嵌入式仪表开发过程中梳理软件配置, 以便可以生成配置代码, 避免手动整理配置参数, 极大提升开发工作效率. 后续开发过程中有几次不同程度的优化升级, 引入了用户字定义代码与函数功能, 拓展了模板引擎的应用范围, 目前mkTpl包已在多个项目中引用, 对开发效率有显著提升.

环境搭建

  • 安装python version > 3.0以上版本
  • 安装mkTpl软件包
  • 安装需要使用的软件包(xlrd/PyYAML/xml等, 未使用可不安装)

代码实例

import mkTpl

ctx = {'测试1': 12345, '测试2': 67890}
template = {'test.tpl.c': ['./tpl', './gen', 'test.gen.c']}

# 实例化模板引擎
tplobj = mkTpl.mktpl()
# 加入数据库
tplobj.addlib('ctx', ctx)
# 导入模板, 生成代码
tplobj.setTemplate(template)

模板语法

代码实例中字典template有一个值: test.tpl.c, 这里的test.tpl.c就是需要生成的代码模板, 只有导入了模板, 代码引擎才能根据模板和数据库导出用户需要的代码. 模板需要提供所在位置与生成代码存放位置以及生成代码文件名, 模板引擎实际不会关注生成的文件格式, 这里只依赖用户定义, 理论上可以生成任意用户定义的文本文件.

针对模板的格式有一定要求, 只有按照要求定义的模板, 模板引擎才能解析. 实际代码生成的过程可以理解成对文件的读写过程, 单独对文件读写大家应该都接触过, 如下实例:

# 以a+属性打开一个文件 text.c
file = open('text.c', a+)

# 读取文本内容
lines = file.readlines()

# 写入hello world!
file.write('hello world!')

file.close()

而模板引擎的作用就是解析模板, 生成文件读写的代码, 再运行生成的代码即可获得想要的最终目标代码. 这里很有意思, 代码并不是事先写好的, 而是模板引擎写的. Python是一门解释性的语言, 刚好可以完成上述的功能.

  • 模板实例: test.tpl.c
// 1. 静态代码
void VfcMgr_vMain(void) {
    // user code.
}
// 2. 可迭代模式
<%test_sb = ['test0', 'test1']%>
<%for i in range(0, len(test_sb)):%>
<%    User Coding Symbol: @test_sb[i]%>
// 3. 单例模式
<%User Coding Symbol: test3%>
// 4. 参数导入模式
<%for key in ctx:%>
<%   ~test coding: ${key}, ${ctx[key]}%>
  • 生成代码: test.gen.c
// 1. 静态代码
void VfcMgr_vMain(void) {
    // user code.
}
// 2. 可迭代模式
/**********************************************************************************************************************
 * DO NOT CHANGE THIS COMMENT!           << Start of user code implementation >>            DO NOT CHANGE THIS COMMENT!
 * Symbol: test0
 *********************************************************************************************************************/
    随意输入
/**********************************************************************************************************************
 * DO NOT CHANGE THIS COMMENT!           << End of user code implementation >>              DO NOT CHANGE THIS COMMENT!
 *********************************************************************************************************************/
/**********************************************************************************************************************
 * DO NOT CHANGE THIS COMMENT!           << Start of user code implementation >>            DO NOT CHANGE THIS COMMENT!
 * Symbol: test1
 *********************************************************************************************************************/
    随意输入
/**********************************************************************************************************************
 * DO NOT CHANGE THIS COMMENT!           << End of user code implementation >>              DO NOT CHANGE THIS COMMENT!
 *********************************************************************************************************************/
// 3. 单例模式
/**********************************************************************************************************************
 * DO NOT CHANGE THIS COMMENT!           << Start of user code implementation >>            DO NOT CHANGE THIS COMMENT!
 * Symbol: test3
 *********************************************************************************************************************/
    随意输入
/**********************************************************************************************************************
 * DO NOT CHANGE THIS COMMENT!           << End of user code implementation >>              DO NOT CHANGE THIS COMMENT!
 *********************************************************************************************************************/
// 4. 参数导入模式
test coding: 测试1, 12345
test coding: 测试2, 67890

函数

<%%>中定义函数, 以def起始的代码会默认识别为函数定义, 函数直到出现一行静态代码结束. 在定义函数时, 函数存在一个默认参数info, 在不使用输出文件功能时可不定义, 若定义参数info使用的时候需要加上参数info, 其他用户参数可自行定义, 实例如下:

函数定义:
<%def test_func(info):%>
<%    print("hello world.")%>
<%    ~write in output file: hello world.%>

函数使用:
<%test_func(info)%>

特殊标识 <%%>

模板语法动态模板代码完全支持python语法, 但动态代码需要使用<%%>标记, 否则默认为静态代码.

example:

<%for key in ctx:%>
<%    print('ctx key: %s'% key)%>

特殊标识: ~ (info.write)

example:

<%for key in ctx:%>
<%   ~test coding: ${key}, ${ctx[key]}%>

or

<%for key in ctx:%>
<%   info.write("test coding: %s, %s\n"%(key, ctx[key]))%>

实例中给出了两段代码, 实际结果是一样的, ~info.write 拥有相同功能, ~是优化后的表现, 使模板编辑更方便高效.

关键字: User Coding Symbol

User Coding Symbol字段设计的时候参考了DaVinci生成代码的功能, 生成代码的时候可以识别User Coding Symbol字段, 在该字段注释内部用户可以编写自定义代码, 模板引擎会主动merge用户代码与生成的代码, 该功能的引入, 有效的扩大了mkTpl的应用范围, 有了该功能后, 再不局限于设计之初的想法, 不但能应用于配置代码的生成, 也可应用于大量重复逻辑代码的编写中. 实际开发过程中往往会遇到结构类似, 但是细节上又存在一些不确定性的差异, 原来的模板引擎功能很难实现这种代码的整理, 引入User Coding Symbol后, 使得该场景下的模板实现成为可能.

在使用User Coding Symbol的时候需要注意, 实际有两种方式使用:

  • 单例模式

<%User Coding Symbol: name%>
适用于单个User Coding Symbol

  • 迭代模式

<%User Coding Symbol: @nameVar%>
适用于多个User Coding Symbol

特殊标识: ${}

${}最常用, 变量的替换需要使用${}标记, 变量放入大括号内, 变量之前可以带数字, 用于定义写入字符长度与对其功能(正负分别表示不同方向的对其).

<%   ~test coding: ${10key}, ${-6ctx[key]}%>

使用实例

在仪表项目中, WarningVfcMgr模块中应用mkTpl做了部分代码生成工作, 以VfcMgr为例, 应用过程如下:

VFC功能开发中, 有众多的Activator, 每个都包含大量的信号逻辑判断功能, 为了解决开发者在这样巨量的逻辑中迷失, 引入yaml配置逻辑功能, 所有相关的信号逻辑表达式全部在yaml文件上统一配置. 再配合部分静态代码, 实现逻辑功能全配置, 不用用户单独编写额外代码.

有关LC -> VFC -> PNC之间的关系通过excel配置, 再由mkTpl加上模板一并导出关系配置表.

yaml配置实例:

ObjMntr:
    PinToDrvForHmiCen: 
        # VFC Activation Criteria
        # ( {PinToDrvCod}ChangeFromAnyToX (X=CodVld.Inact_Active) )
        # OR( {PinToDrvCodHmiReq}ChangeToOther ) )
        # AND{VehModMngtGlbSafe1}NotEqualToX(X=UsgModSts.UsgModSts1_UsgModAbdnd) )
        # VFC Deactivation Criteria
        # ( VFCTimeOutDelay [sec] = 3 )
        Delay: 3000
        Tx:
            PinToDrvCodCodVld:
                Type: uint8
                Condition: [ Prev.PinToDrvCodCodVld != Curr.PinToDrvCodCodVld, Curr.PinToDrvCodCodVld == Inact_Active ]
            PinToDrvCodHmiReq:
                Type: PinToDrvCodHmiReq
                Condition: [ Prev.PinToDrvCodHmiReq != Curr.PinToDrvCodHmiReq ]
        LogicalExpression:
            ActyLE0: CC_PinToDrvCodCodVld_Condition_0 && CC_PinToDrvCodCodVld_Condition_1
            ActyLE1: CC_PinToDrvCodHmiReq_Condition_0
        ActRule: LE_ActyLE0 || LE_ActyLE1

yaml软件模板:

/**
 *  Tx signals interface.
 */
<%sigsTab = []%>
<%for lc in mrkChgMap['ObjMntr']:%>
<%    if 'Tx' in mrkChgMap['ObjMntr'][lc]:%>
<%        for sg in mrkChgMap['ObjMntr'][lc]['Tx']:%>
<%            a = False%>
<%            if sg not in sigsTab:%>
<%                sigsTab.append(sg)%>
<%                if sg in sglib['sg']['tx']:%>
<%                    a = True%>
<%                    ~#define VfcMgr__Read_Write_${sg}() (SigSer_${sg})%>
<%                for gp in sglib['gp']['tx']:%>
<%                    if sg in sglib['gp']['tx'][gp]:%>
<%                        a = True%>
<%                        if sglib['gp']['tx'][gp][sg]['index'] != -1:%>
<%                            ~#define VfcMgr__Read_Write_${sg}() (SigSer_${gp}[${sglib['gp']['tx'][gp][sg]['index']}])%>
<%                        else:%>
<%                            ~#define VfcMgr__Read_Write_${sg}() (SigSer_${gp}.${sg})%>
<%            else:%>
<%                a = True%>
<%            if a == False:%>
<%                print("Sg: %s is error."%sg)%>
...

yaml生成代码:

#define VfcMgr__nTimeoutDly_PinToDrvForHmiCen ( (uint16)(3000ul / VfcMgr__nMainCycleCntr) )

#define VfcMgr__Read_Write_PinToDrvCodCodVld() (SigSer_PinToDrvCod.PinToDrvCodCodVld)
#define VfcMgr__Read_Write_PinToDrvCodHmiReq() (SigSer_PinToDrvCodHmiReq)

#define VfcMgr__boPinToDrvForHmiCen_PinToDrvCodCodVld_Condition_0  ( VfcMgr__PinToDrvCodCodVld != VfcMgr__Read_Write_PinToDrvCodCodVld() )
#define VfcMgr__boPinToDrvForHmiCen_PinToDrvCodCodVld_Condition_1  ( VfcMgr__Read_Write_PinToDrvCodCodVld() == Inact_Active )
#define VfcMgr__boPinToDrvForHmiCen_PinToDrvCodHmiReq_Condition_0  ( VfcMgr__PinToDrvCodHmiReq != VfcMgr__Read_Write_PinToDrvCodHmiReq() )

#define VfcMgr__boPinToDrvForHmiCen_ActyLE0 ( VfcMgr__boPinToDrvForHmiCen_PinToDrvCodCodVld_Condition_0 && VfcMgr__boPinToDrvForHmiCen_PinToDrvCodCodVld_Condition_1 )
#define VfcMgr__boPinToDrvForHmiCen_ActyLE1 ( VfcMgr__boPinToDrvForHmiCen_PinToDrvCodHmiReq_Condition_0 )

#define VfcMgr__boActRule_PinToDrvForHmiCen ( VfcMgr__boPinToDrvForHmiCen_ActyLE0 || VfcMgr__boPinToDrvForHmiCen_ActyLE1 )

excel配置:

在这里插入图片描述

excel软件模板:

<%~const VfcMgr__tstLCsAttrs VfcMgr__rastLCsAttrs_Tab[VfcMgr_nenNrOfLCs] = {%>
<%for lc in VFCInfo:%>
<%    u8VFCIndex = str(VFC2PNC[VFCInfo[lc]['VFCName']]['VFCID'] + 100)[1:]%>
<%    u8PNCIndex = str(VFC2PNC[VFCInfo[lc]['VFCName']]['PNC'] + 100)[1:]%>
<%    na = lc.replace('-', '')%>
<%    na = na.replace(' ', '')%>
<%    u8UsageMode = VFCInfo[lc]['Usagemode']%>
<%    u8CarMode = VFCInfo[lc]['CarMode']%>
<%    pfLCActyFnct = 'VfcMgr__v'+na+'Detect'%>
<%    ~    {VfcMgr_nVFC${u8VFCIndex}, VfcMgr_nPNC${u8PNCIndex}, ${3u8UsageMode}, ${3u8CarMode}, ${-52pfLCActyFnct}},   /*!> ${lc} */%>
};

excel生成代码:

const VfcMgr__tstLCsAttrs VfcMgr__rastLCsAttrs_Tab[VfcMgr_nenNrOfLCs] = {
    {VfcMgr_nVFC41, VfcMgr_nPNC16,   7,  31, VfcMgr__vV2XCtrlForHmiCenDetect                     },   /*!> V2XCtrlForHmiCen */
    {VfcMgr_nVFC41, VfcMgr_nPNC16,  15,  31, VfcMgr__vDrvrHmiMgrPlatform1ForDrvrHmiDetect        },   /*!> DrvrHmiMgrPlatform1ForDrvrHmi */
    {VfcMgr_nVFC41, VfcMgr_nPNC16,  15,  31, VfcMgr__vDrvrHmiMgrPlatform2ForDrvrHmiDetect        },   /*!> DrvrHmiMgrPlatform2ForDrvrHmi */
    {VfcMgr_nVFC41, VfcMgr_nPNC16,  15,  31, VfcMgr__vInfotainmentModMgr_InfotainmentPushDetect  },   /*!> InfotainmentModMgr_InfotainmentPush */
    {VfcMgr_nVFC41, VfcMgr_nPNC16,   7,   9, VfcMgr__vVehSurrndgsVisnCtrlDetect                  },   /*!> VehSurrndgsVisnCtrl */
    {VfcMgr_nVFC41, VfcMgr_nPNC16,  15,   9, VfcMgr__vVehSurrndgsVisnAddlDetect                  },   /*!> VehSurrndgsVisnAddl */
    {VfcMgr_nVFC41, VfcMgr_nPNC16,  15,  31, VfcMgr__vFaceIdnMgrDetect                           },   /*!> FaceIdnMgr */
    {VfcMgr_nVFC16, VfcMgr_nPNC17,  15,  31, VfcMgr__vLeReCenLockUnlockBtnDetect                 },   /*!> LeReCenLockUnlockBtn */
    {VfcMgr_nVFC16, VfcMgr_nPNC17,  31,  31, VfcMgr__vRiReCenLockUnlockBtnDetect                 },   /*!> RiReCenLockUnlockBtn */
    {VfcMgr_nVFC10, VfcMgr_nPNC18,   7,  31, VfcMgr__vExteriorLightShow_ExteriorLightingDetect   },   /*!> ExteriorLightShow_ExteriorLighting */
    {VfcMgr_nVFC10, VfcMgr_nPNC18,  15,  31, VfcMgr__vWelcomeLightDIYDetect                      },   /*!> WelcomeLightDIY */
    {VfcMgr_nVFC21, VfcMgr_nPNC19,  31,  31, VfcMgr__vLePwrSldgDoorBtnDetect                     },   /*!> LePwrSldgDoorBtn */
    {VfcMgr_nVFC21, VfcMgr_nPNC19,  31,  31, VfcMgr__vRiPwrSldgDoorBtnDetect                     },   /*!> RiPwrSldgDoorBtn */
    {VfcMgr_nVFC21, VfcMgr_nPNC19,  15,  31, VfcMgr__vLockgCtrlForHmiCen_PowerClosuresDetect     },   /*!> LockgCtrlForHmiCen_PowerClosures */
    {VfcMgr_nVFC21, VfcMgr_nPNC19,  15,  31, VfcMgr__vPwrDoorCtrlForHmiCen_PowerClosuresDetect   },   /*!> PwrDoorCtrlForHmiCen_PowerClosures */
    {VfcMgr_nVFC21, VfcMgr_nPNC19,  15,  31, VfcMgr__vActvReSplrForHmiCenDetect                  },   /*!> ActvReSplrForHmiCen */
};
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值