精华内容
下载资源
问答
  • ATL模型转换技术详解

    千次阅读 2016-07-20 09:10:57
    ATL是ATLAS Transformation Language的简称,它是ATLAS研究组开发出来的一种符合OMG的一个QVT提案的模型转换语言。本文根据ATL的官方资料及个人使用经验理解并整理,详细讲解ATL语言的语法、特性及若干使用建议,为...

    转载请注明出处:http://blog.csdn.net/tyhj_sf/article/details/51965026

    因涉及ATL内容较多,本文将持续更新,如果觉得有价值建议关注。

    引言

    ATL是ATLAS Transformation Language的简称,它是ATLAS研究组开发出来的一种符合OMG的一个QVT提案的模型转换语言。目前ATL已经实现为ADT的一个组成部分,ADT是一个Eclipse插件,他是著名的Eclipse项目GMT的子项目。ATL是基于EMF(Eclipse模型框架)的,其元模型、模型都是用EMF来描述的。从本质上来说,ATL属于基于规则的模型转换语言,其中使用了OCL约束描述语言。
    本文根据ATL的官方资料及个人使用经验理解并整理,详细讲解ATL语言的语法、特性及若干使用建议,为学习和使用ATL模型转换技术提供一定的参考。

    ATL的数据类型

    ATL中定义的数据类型继承关系全图:
    这里写图片描述
    在详细介绍上图数据类型之前,必须注意每一个OCL表达,包括与每一个数据类型相关的操作及某个类型的实例的上下文。
    注意:
    (1)self关键词用于引用上下文的实例。
    (2)ATL数据类型与OCL的非常相近,但是继承关系与OCL类型关系差别很大,不可相互套用。

    OclType operations

    OclType类相当于由OCL指定的类型实例的定义,它与特定的OCL操作allInstances()相关。
    allInstances() :无参数,返回self的所有实例的集合。
    allInstancesFrom(model : String) :用于获取给定元模型包含的self的所有实例的集合。

    OclAny operations

    这部分描述了所有数据类型的一系列通用操作。
    语法结构:
    self.operation_name(parameters)

    被ATL支持的OCL定义的操作:
    比较操作:=, <>;
    oclIsUndefined() :判定 self 是否是未定义的,返回布尔值;
    oclIsKindOf(t : oclType) :判断self 是否是t类型或t的子类型的数据类型,返回布尔值。
    oclIsTypeOf(t : oclType) :判断调用者是否是 t类型的实例,返回布尔值,用法类似Java中的instanceof。
    注意:OCL定义的oclIsNew() 和 oclAsType()是不被ATL引擎支持的。
    但是,ATL实现了大量的额外操作:
    toString() :返回self的字符串表示,某些类型可能返回与此类型不相关的字符串值。
    oclType() :返回self的数据类型。
    asSequence(), asSet(), asBag() 返回含有self的sequence、set 、bag 。
    output(s : String) 向eclipse控制台输出字符,无返回值,仅能用在ATL imperative blocks中。
    debug(s : String)返回self的值,并向eclipse控制台输出格式为“s : self_value”的字符串。
    refSetValue(name : String, val : oclAny)设置可以用name_value对识别的self的特征,并返回self本身。
    refGetValue(name : String) 通过name获得self含有的name_value对的value,并返回value。
    refImmediateComposite() 返回self的直接组合,比如self的直接容器。
    refInvokeOperation(opName : String, args : Sequence)通过反射方式调用self的名叫opName的操作,并传递给它包含在args中的若干参数。

    ATL的 Module数据类型

    Module表示当前ATL引擎运行的ATL单元,开发者可以通过变量thisModule获取这个Module类型的单实例。变量thisModule可用于访问ATL module的上下文中声明的helpers和attributes。
    Module数据类型提供了操作:resolveTemp(var, target_pattern_name),可以用于指向由源模型通过ATL匹配规则生成的目标模型的任何一个元素。其中,var变量代表源模型元素,target_pattern_name变量代表目标模式元素的名字,即target_pattern_name表示var到目标模型元素的映射关系。

    注意:
    resolveTemp(var, target_pattern_name)必须在matching phase完成之后才可以调用。
    resolveTemp(var, target_pattern_name)操作可以从以下地方调用:

    1. 匹配规则的target pattern 和 do 部分;
    2. 调用的规则的target pattern 和 do
      部分,这里的调用规则指matching phase完成之后的被执行的调用规则。 一个源模型元素不能被超过一个匹配规则匹配。

    resolveTemp(var, target_pattern_name)的使用的例子:
    第一个转换规则:

    rule AtoAnnotedB {
        from
            a : MMA!A
        to
            ann : MMB!Annotation (),
            b : MMB!B (
                annotation <- ann
            )
    }

    第二个转换规则:
    我们假设了ARef有一个引用名为ref指向A元素,BRef有一个引用名为ref指向B元素。

    rule ARefToBRef {
        from
            aRef : MMA!ARef
        to
            bRef : MMB!BRef (
                ref <- thisModule.resolveTemp(aRef.ref, 'b')
            )
    }

    基本数据类型

    OCL定义了四个基本数据类型:
    Boolean :true、false;
    对应的逻辑操作:and, or, xor, not;
    implies(b : Boolean) :如果self是true且b是false,则返回false,其他情况返回true;
    注意:表达式(exp1 and exp2),exp1即使为false,exp2仍然会进行判断。
    Integer (例如1、-5、34、25688)和Real (例如1.5、 3.145)
    两者都可执行的操作:
    操作符:<, >, >=, <=
    二元操作符:*, +, -, /, max(), min()
    一元操作符:abs()
    toString()操作:返回self的字符串值。
    注意:一元操作符 –并没有实现,所以例如 -2 要表示成 0-2 形式。
    integer可以执行的操作:div(), mod();
    real可以执行的操作:
    floor(), round(),cos(), sin(), tan(), acos(), asin(),
    toDegrees(), toRadians(),
    exp(), log(), sqrt()。

    String:例如 ’to be or not to be’;
    由OCL定义的String相关操作:
    size(),
    concat(s : String) ,
    substring(lower : Integer, upper : Integer),
    toInteger() 和 toReal().
    由ATL实现的String相关的操作(用法与Java中String方法的相同):
    comparison operators: <, >, >=, <=
    字符合并操作符:+
    toUpper(), toLower(),
    toSequence(),
    startsWith(s : String), endsWith(s : String),
    indexOf(s : String), lastIndexOf(s : String),
    split(regex : String),
    replaceAll(c1 : String, c2 : String),
    regexReplaceAll(regex : String, replacement : String),
    对于ATL程序中的查询和调试很有用的两个方法:
    writeTo(fileName : String),
    println(),

    Collection data types

    该类型包括:
    Set, 无序集合,元素不能重复;
    OrderedSet, 有序集合,元素不能重复;
    Bag ,无序,元素可重复;
    Sequence,有序,元素可重复。

    ATL Helpers

    Helper类似于Java和C/C++语言中的函数,它可以在ATL转换程序中的不同地方被调用。
    定义为:
    helper [context context_type]? def : helper_name(parameters) : return_type = exp;
    其中,
    context表示哪种元素类型可以调用这个helper,为可选项。缺省时,表示helper关联到全局context;
    def关键词表示helper定义的开始;
    Helper_name表示helper的名字;
    Parameters表示参数集,参数声明方式为 parameter_name : parameter_type,
    多个参数声明之间用逗号隔开;
    return_type 返回值类型;
    exp 表达式;
    下面是一个helper定义的例子:

    helper def : averageLowerThan(s : Sequence(Integer), value : Real) : Boolean = let avg : Real = s->sum()/s->size() in avg < value;
    

    此外,super可以用来调用父类的同名方法,类似于Java中的super用法。

    Attributes

    atrtibute在特定上下文中相当于常量,定义:
    helper [context context_type]? def : attribute_name : return_type = exp;
    attribute与helper的唯一区别是,attribute不能带任何参数。
    下面是attribute的一个例子:

    helper def : getYoungest : MMPerson!Person =
        let allPersons : Sequence(MMPerson!Person) =
            MMPerson!Person.allInstances()->asSequence() in
        allPersons->iterate(p; y : MMPerson!Person = allPersons->first() |
            if p.age < y.age
            then
                p
            else
                y
            endif
        );
    

    Attribute和helper的局限性:
    局限一:目前的ATL版本中,Attribute和helper只能通过他们的name和context两个属性来区分不同的attribute和helper,parameter不能用于区分不同的helper。
    局限二:ATL引擎不支持在collection类型的context中定义helper。
    例如下面这个定义是不行的:
    helper context Set(MMPerson!Person) def : getYoungPersons(age : Integer) :
    Set(MMPerson!Person) =
    self->select(p | p.age < age);

    应该定义成这样:
    helper def : getYoungPersons(s : Set(MMPerson!Person), age : Integer) :
    Set(MMPerson!Person) =
    s->select(p | p.age < age);

    局限三:ATL库中不能定义attribute。替代方案是,在自定义库中用无参数的helper的定义代替attribute的定义。

    ATL Rules

    ATL定义了两种转换规则:
    matched rule:匹配源模型的模型元素,生成目标模型的模型元素。
    called rule:可以调用ATL代码块(ATL imperative block)生成目标模型元素。

    ATL imperative code

    赋值语句
    语法:

    target <- exp;

    例如:

    thisModule.counter <- thisModule.counter + 1;
    aPerson.father.age <- aPerson.age + 25;
    aPerson.father <- anotherPerson;

    if语句
    语法:

    if(condition) {
        statements1
    }
    [else {
        statements2
    }]?

    例子1:

    if(aPerson.gender = #male) {
        thisModule.menNb <- thisModule.menNb + 1;
        thisModule.men->including(aPerson);
    }

    例子2:

    if(aPerson.gender = #male) {
        thisModule.fullName <- 'Mr. ' + aPerson.name + ' ' + aPerson.surname;
    }
    else {
        if(aPerson.isSingle) {
            thisModule.fullName <- 'Miss ' + aPerson.name;
            thisModule.surname <- aPerson.surname;
        }
        else {
            thisModule.fullName <- 'Mrs. ' + aPerson.name;
            thisModule.surname <- aPerson.marriedTo.surname;
        }
        thisModule.fullName <- thisModule.fullName + ' ' + thisModule.surname;
    }

    for语句
    语法:

    for(iterator in collection) {
        statements
    }

    例子:

    for(p in MMPerson!Person.allInstances()) {
        if(p.gender = #male)
            thisModule.men->including(aPerson);
        else
            thisModule.women->including(aPerson);
    }

    注意:在if和for语句块中时不能定义变量的。
    在if和for语句块中可以使用的变量包括:
    (1) if和for所在的matched rule中的源模型和目标模型元素;
    (2) if和for所在的called rule中的目标模型元素;
    (3) matched rule和called rule中定义的局部变量;
    (4) ATL模块上下文中定义的属性。

    Matched Rules

    语法:

    rule rule_name {
        from
            in_var : in_type [in model_name]? [(
                condition
            )]?
        [using {
            var1 : var_type1 = init_exp1;
            ...
            varn : var_typen = init_expn;
        }]?
        to
            out_var1 : out_type1 [in model_name]? (
                bindings1
            ),
            out_var2 : distinct out_type2 foreach(e in collection)(
                bindings2
            ),
            ...
            out_varn : out_typen [in model_name]? (
                bindingsn
            )
        [do {
            statements
        }]?
    }

    注意:
    (1) rule_name是rule的识别符,在ATL转换中必须是唯一的;
    (2) 在rule中,from和to语句块必须有,using和do语句块为可选项。

    下面对matched rule各部分分别讲解

    from语句

    作用:声明输入的源模型元素,及其初始化。
    例子1:

    from
        p : MMPerson!Person (
            p.name = 'Smith'
        )

    注意下面的例子2到例子3是等价的。
    例子2:

    from
        p : MMPerson!Person (
            true
        )

    例子3:

    from
        p : MMPerson!Person

    注意,若干个输入模型来自同一个元模型时,可以采用如下方式:
    一个转换的header:

    create ... from IN1 : MMPerson, IN2 : MMPerson;

    只使用IN2时,可以这样:

    from 
           p : MMPerson!Person in IN2

    局部变量定义在using语句块中,如下例子:

    from
        c : GeometricElement!Circle
    using {
        pi : Real = 3.14;
        area : Real = pi * c.radius.square();
    }

    to语句

    作用:定义目标模型元素,及其与源模型元素的关系。
    注意,生成的目标模型元素必须初始化,形式如下:

    feature_name <- exp

    其中,feature_name为元素名,exp为表达式。
    下面是Biblio元模型上下文中定义的ATL rule例子:

    rule Journal2Book {
        from
            j : Biblio!Journal
        to
            b : Biblio!Book (
                title <- j.title + '_(' + j.vol + '):' + j.num,
                authors <- j.articles
                        ->collect(e | e.authors)->flatten()->asSet()
                chapters <- j.articles,
                pagesNb <- j.articles->collect(e | e.pagesNb)->sum()
            )
    }

    Journal模型转换成了Book模型,Book模型含有title、authors、chapters、pagesNb元素,注意它们的赋值。

    关于元素初始化,有三种情况需要考虑的情况:
    (1)引用了当前rule生成的目标模型元素

    例子:

    rule Case1 {
        from
            i : MM_A!ClassA
        to
            o_1 : MM_B!Class1 (
                linkToClass2 <- o_2
            ),
            o_2 : MM_B!Class2 (
                ...
            )
    }

    (2)引用了另外的rule中的默认模型元素

    例子:

    rule Case2_R1 {
        from
            i : MM_A!ClassA
        to
            o_1 : MM_B!Class1 (
                linkToClass2 <- i.linkToClassB
            )
    }
    rule Case2_R2 {
        from
            i : MM_A!ClassB
        to
            o_1 : MM_B!Class2 (
                ...
            ),
            ...
    }

    (3)引用了另外的rule中的非默认模型元素

    例子:

    rule Case3_R1 {
        from
            i : MM_A!ClassA
        to
            o_1 : MM_B!Class1 (
                linkToClass2 <- thisModule.resolveTemp(i.linkToClassB, 'o_n')
            )
    }
    rule Case3_R2 {
        from
            in : MM_A!ClassB
        to
            o_1 : MM_B!Class3 (
                ...
            ),
            ...
            o_n : MM_B!Class2 (
                ...
            ),
            ...
    }

    注意,当若干个输出模型来自同一个目标元模型时,可以使用in关键字来区分是哪一个输出模型。例如:
    一个转换的header:

    create OUT1 : MM_B, OUT2 : MM_B from ... ;

    如果在模型OUT2中创建元素,则使用in像这样:

    to
        o : MM_B!Class2 in OUT2

    do语句

    作用:在目标元模型生成之后(即执行do 语句后),可以执行一段ATL语句,这段语句可以用于初始化一些模型元素或者修改一些模型元素的特征。
    例子:

    helper def : id : Integer = 0;
    ...
    rule Journal2Book {
        from
            j : Biblio!Journal
        to
            b : Biblio!Book (
                ...
            )
        do {
            thisModule.id <- thisModule.id + 1;
            b.id <- thisModule.id;
        }
    }

    在上述例子中,先是在ATL模型的上下文中定义了id变量,并初始化为0。do语句块中以递增方式生成了每一模型元素的统一id。

    Lazy Rules

    给出一个lazy rule的例子:

    lazy rule getCross {
       from
         i: ecore!EObject
       to 
         rel: metamodel!Relationship (
         )
     }

    我们可以从matched rule中调用它:

    rule Example {
       from 
         s : ecore!EObject
       to 
         t : metamodel!Node (
           name <- s.toString(),
           edges <- thisModule.getCross(s)
         )
     }

    也可以多次调用lazy rule:

    rule Example {
       from 
         s : ecore!EObject
       to 
         t : metamodel!Node (
           name <- s.toString(),
           edges <- ecore!EClass.allInstancesFrom('yourmodel')->collect(e | thisModule.getCross(e))
         )
      }

    Called rules

    语法:

    [entrypoint | endpoint]? rule rule_name(parameters){
        [using {
            var1 : var_type1 = init_exp1;
            ...
            varn : var_typen = init_expn;
        }]?
        [to
            out_var1 : out_type1 (
                bindings1
            ),
            out_var2 : distinct out_type2 foreach(e in collection)(
                bindings2
            ),
            ...
            out_varn : out_typen (
                bindingsn
            )]?
        [do {
            statements
        }]?
    }

    注意:
    (1) called rule 通过rule_name来识别,在一个ATL转换中是唯一的并且不能与helper的名字冲突;
    (2) entrypoint called rule在模块初始化之后,转换开始之前的地方首先被调用执行。相当于Java程序中的main函数;endpoint called rule在转换结束之后的地方被调用执行。
    (3) called rule可以像helper一样拥有参数。
    (4) using中声明和初始化的局部变量在using、to、do语句块中都是可用的。
    (5) using、to、do语句的用法与在matched rule中的用法一致。

    使用范例:

    helper def: metamodel : KM3!Metamodel = OclUndefined;
    ...
    entrypoint rule Metamodel() {
        to
            t : KM3!Metamodel
        do {
            thisModule.metamodel <- t;
        }
    }

    在上面的例子中,在helper的变量声明和初始化完成后,Metamodel作为entrypoint rule,是第一个被执行的rule。

    Rule的继承

    继承需要两个关键词:abstract 和extends。父规则前用abstract,子规则用extends继承父规则。
    例子:

    abstract rule A {
       from [fromA]
       using [usingA]
       to [toA]
       do [doA]
     }
     rule B extends A {
       from [fromB]
       using [usingB]
       to [toB]
       do [doB]
     }
     rule C extends B {
       from [fromC]
       using [usingC]
       to [toC]
       do [doC]
     }

    ATL将上述例子编译后,是等效为这样的:

    rule B {
       from [fromB]
       using [usingB]
       to [toA.bindings union toB.bindings]
       do [doB]
     }
     rule C {
       from [fromC]
       using [usingC]
       to [toA.bindings union toB.bindings union toC.bindings]
       do [doC]
     }

    注意仔细分析下面的实例:

    module Copy;
     create OUT : MM from IN : MM;
    
     rule CopyDataType extends CopyClassifier {
       from
         s : MM!DataType
       to
         t : MM!DataType
     }
    
     rule CopyEnumeration extends CopyClassifier {
       from
         s : MM!Enumeration
       to
         t : MM!Enumeration (
           literals <- s.literals
         )
     }
    
     rule CopyParameter extends CopyTypedElement {
       from
         s : MM!Parameter
       to
         t : MM!Parameter
     }
    
     rule CopyReference extends CopyStructuralFeature {
       from
         s : MM!Reference
       to
         t : MM!Reference (
           isContainer <- s.isContainer,
           opposite <- s.opposite
         )
     }
    
     rule CopyTypedElement extends CopyModelElement {
       from
         s : MM!TypedElement
       to
         t : MM!TypedElement (
           lower <- s.lower,
           upper <- s.upper,
           isOrdered <- s.isOrdered,
           isUnique <- s.isUnique,
           type <- s.type
         )
     }
    
     rule CopyOperation extends CopyTypedElement {
       from
         s : MM!Operation
       to
         t : MM!Operation (
           parameters <- s.parameters
         )
     }
    
     rule CopyAttribute extends CopyStructuralFeature {
       from
         s : MM!Attribute
       to
         t : MM!Attribute
     }
    
     rule CopyEnumLiteral extends CopyModelElement {
       from
         s : MM!EnumLiteral
       to
         t : MM!EnumLiteral
     }
    
     rule CopyPackage extends CopyModelElement {
       from
         s : MM!Package
       to
         t : MM!Package (
           contents <- s.contents
         )
     }
    
     rule CopyClass extends CopyClassifier {
       from
         s : MM!Class
       to
         t : MM!Class (
           isAbstract <- s.isAbstract,
           supertypes <- s.supertypes,
           structuralFeatures <- s.structuralFeatures,
           operations <- s.operations
         )
     }
    
     rule CopyClassifier extends CopyModelElement {
       from
         s : MM!Classifier
       to
         t : MM!Classifier
     }
    
     abstract rule CopyModelElement extends CopyLocatedElement {
       from
         s : MM!ModelElement
       to
         t : MM!ModelElement (
           name <- s.name
         )
     }
    
     rule CopyMetamodel extends CopyLocatedElement {
       from
         s : MM!Metamodel
       to
         t : MM!Metamodel (
           contents <- s.contents
         )
     }
    
     abstract rule CopyLocatedElement {
       from
         s : MM!LocatedElement
       to
         t : MM!LocatedElement (
           location <- s.location
         )
     }
    
     rule CopyStructuralFeature extends CopyTypedElement {
       from
         s : MM!StructuralFeature
       to
         t : MM!StructuralFeature (
           subsetOf <- s.subsetOf,
           derivedFrom <- s.derivedFrom
         )
     }

    由上面的例子可以发现一些规律:
    (1) rule不支持多继承,但是一个abstract rule可以被多个rule继承;
    (2) 被继承的父rule必须带abstract,一个abstract rule可以继承另一个abstract rule;

    三种rule的可以被使用的次数:
    (1) Matched rules 每次匹配只能使用一次;
    (2) Lazy rules 在每一次match时,可以被另外的rules引用多次;
    (3) Unique lazy rules只能被另外的rules引用一次。

    编写ATL转换规则的若干建议:

    (1) 在保证转换结构尽量简单的情况下,优先使用standard rules,其次考虑使用unique lazy rules,必要时使用lazy rules;
    (2) 除非必要,否则尽量只使用resolveTemp;
    (3) 优先使用iterators (比如select, collect)。
    (4) Action blocks, Lazy rules,多输入元素,迭代输出模式等这些ATL标准模式的高级特征不再被官方支持了,因此尽量不要使用。

    ATL语言中的所有关键词

    (1) 常量关键词:true, false;
    (2) 类型关键词:Bag, Set, OrderedSet, Sequence, Tuple, Integer, Real, Boolean, String, TupleType, Map;
    (3) 语言关键词:not, and, or, xor, implies, module, create, from, uses, helper, def, context, rule, using, derived, to, mapsTo, distinct, foreach, in, do, if, then, else, endif, let, library, query, for, div, refining, entrypoint;
    注意,有时候,模型元素名可能恰好是ATL关键词,此时用双引号可以将关键词变成普通字符串。如下例子:

    rule AtoB {
           from
                   a : MM!A
           to
                   b : MM!B (
                      "from" <- a.source        
                   )
    }

    ATL编程若干问题与建议

    (1) 在ATL中,输入模型元素至多被匹配一次,目前这个限制还不能在编译时间检查。输入模型元素多次匹配问题出现在继承关系中,如下例子:
    这里写图片描述
    ruleB规则继承ruleA规则,当通过ruleB匹配模型元素时,就会发生多次匹配问题。这个问题可以通过如下方法解决:
    rule ruleA {
    from
    a : MM!A (
    a.oclIsTypeOf(MM!A)
    )

    函数oclIsTypeOf()在这里是用来测试a 是否是输入参数MM!A的实例。

    展开全文
  • Atlas 200 DK 系列 --高级篇--模型转换

    千次阅读 2020-03-20 09:34:01
    一、模型转换工具的介绍与操作演示–模型转换OMG介绍 Atlas200DK平台中提供了模型转换工具(OMG)。可以将caffe、Tensorflow等开源框架模型转换成Atlas200DK支持的模型,从而能够更方便快捷地把其他平台地模型放到...

    一、模型转换工具的介绍与操作演示–模型转换OMG介绍
    Atlas200DK平台中提供了模型转换工具(OMG)。可以将caffe、Tensorflow等开源框架模型转换成Atlas200DK支持的模型,从而能够更方便快捷地把其他平台地模型放到Atlas200DK平台进行调试并拓展相关业务
    1)OMG的功能介绍
    2)OMG命令使用及命令参数解析
    3)Mind Studio模型转换实际操作
    4)OMG与整体结构关系、OMG转换流程以及整体业务流程
    5)模型转换报错定位思想

    1.OMG常用功能介绍
    在这里插入图片描述
    OME,离线模型执行,支持主流NN框架能够在Atlas200DK上进行推理,即用于离线模型转换完的管理和推理。主要功能如上图的右边所列。
    OMG,离线模型生成,主要功能把训练好的模型脱离训练框架直接在昇腾处理器上进行执行。
    omg使用:主要在Host测,一般我们的主机Linux Ubuntu上单独离线执行, 以omg可执行程序的方式提供给IDE,命令行方式调用;也可以在MindStudio上进行模型转换,实际上MindStudio也是调用的DDK中的omg可执行程序,使用命令行方式调用的。
    2. 模型转换-OMG
    在这里插入图片描述
    OMG当前主要支持caffe和TF,也许后期升级会对更多的开源框架的进行支持。
    主流开源框架的模型存储格式是protobuf,omg集成了protobuf的解析。对不同的框架所需要的东西也是不一样的,一般来说,都包括网络模型文件和权值文件。对caffe来说,它就包括网络结构文件和权值文件。TF来说,只需要一个,pb文件(网络和权值都在这个文件里)。
    转成离线模型,看右边,也就是下图所示,先对模型和权重进行解析,后续经过graph,经过图处理之后,调用modelBuilder会生成模型,接着会对这个模型进行调验,最终才会生成我们的一个.om离线模型。
    在这里插入图片描述
    上面详细说明了OMG和OME的功能,以及omg的简易流程,下面看一下omg的运行环境及其命令的使用
    3. 运行依赖库
    先看一下当前运行环境所需要的依赖库情况。因为我们在使用的时候,会报缺少库这种情况。第一种情况,我们忘记配置环境变量,可以使用export LD_LIBARY_PATH环境变量配上。第二种情况,被其他软件做了升级,导致跟我们版本的库不匹配,我们可以把DDK中的so拿过来替换,那么这种问题就可以相对而言可以排除。
    看一下库都做了哪些事,如下图所示:
    在这里插入图片描述
    还有一点需要注意的是,我们不需要去管库版本升级的问题,不需要做手动升级,因为omg所需依赖库都会根据版本做统一升级,已确保so库一致性
    4. OMG命令使用及命令参数解析
    在这里插入图片描述
    示例1:alexnet转换,将caffemodel转换为昇腾处理器支持的离线模型了。
    在这里插入图片描述
    示例2:resNet18模型转换
    在这里插入图片描述
    input_shape在TF中使用的是比较多的,因为TF的网络有很多动态shape的模型,比如某一个网络,它的输入是[1,?,?,3],像这种动态shape,当前升腾处理器其实是不支持的,但我们在转模型的时候,需要把它转成固定的shape,比如说[1, ??,3] -->> [1, 224, 224,3],然后再去做离线转换。
    aipp使用的好处:一般来说,它可以提高我们的性能,通常输入是图片的,并且数据是U8的,(比如说yuv数据,rgb数据,它相对来说,可以去开启),并且aipp本身也只支持U8的数据输入。
    那么它的性能是怎么会提升呢?因为我们在做这个aipp的时候,我们很多处理会放到aicore里去做,比如,减均值以及相对应的格式转换的处理,还有omg模型在输入数据的时候,它会去做一个4D转5D的一个操作。而这一块东西,如果在cpu做,是比较耗时的,但我们aicore里面也是可以做的,放在aicore里去做,就可以大大提升推理时的性能。

    通过上面两个命令,
    在这里插入图片描述
    –weight,caffe有,但tf是没有的,对于tf,weight是不需要设置的。
    –plugin_path 自定义算子插件路径:我们做自定义算子的自定义文件路径指定。当我们可以把自己实现的算子加入到整网中,然后转成om离线模型,就需要用到pligin_path这个参数。
    –insert_op_conf,输入预处理数据的配置文件路径,这个文件就是我们所说的aipp的配置路径。
    –fp16_high_prec,是否生成高精度/高性能模型,默认是高精度FP16模型。’
    –model,是一个可选项,默认0,即生成模型。
    在这里插入图片描述
    比如说,量化场景,我们就需要用的cal_conf这个量化配置。量化时所需要设置的一个参数。
    其他的参数,其余不常见的参数可根据具体查看Atlas200DK模型转换指导。
    还有相对来说,有些网络都是带后处理的,带后处理,我们需要一个op_name_map去设置后处理对应的名称。在caffe里面,比如说会有ssd detection output或者fastrcnn detection output这几类。
    –out_nodes,我们在调试的时候,用的比较多,它可以downp指定层的数据。用于我们做精度分析,或者是其他的一些分析。
    还有很多不常用的。
    omg参数介绍的差不多
    那我们如何能把各个模型高性能地运行起来,想必就是aipp这一块一些参数需要做一些了解。
    5. 模型转换AIPP介绍
    在这里插入图片描述
    下面来看一下aipp配置文件。
    在这里插入图片描述
    上图,我在mindstudio转换模型时,通过cafmodel.node里面提取出的aipp配置,主要参数如下图所示。
    在这里插入图片描述
    aipp_mode,必选项,一般使用静态配置,动态的可以根据场景需要去配置。
    related_input_rank:可选项。
    src_image_size_w/h,进入aipp实际图片的宽高配置,如果在我们的流程代码里有dvpp的预处理,这里我们就需要考虑dvpp对齐,在dvpp课程里就会学到。比如:其实,我这个数据输入的是608X608,但,是经过dvpp处理后的。实际输入640X608,dvpp对齐导致产生的滤边,aipp会把相应的滤边裁去,最后,实际进入一个模型的是608X608的一个大小,然后去做推理。如果不设置或者设置为0,它是直接读取到模型里面的。所以我们就会碰到一种情况,经过了dvpp处理,并且对齐后有滤边的,放进模型去推理,导致推理出错,原因就是输入的数据大小和模型要求的数据的大小不匹配。
    crop,aipp处理图片时是否支持抠图,也就是把滤边裁剪掉。
    csc_switch:色域转换开关,静态aipp配置。它可以把yuv数据转换为模型所需的rgb/bgr的数据。
    rbuv_swap_switch:色域转换钱,R通道与B通道交换开关/U通道与V通道交换开关。
    rbuv_swap_switch:色域转换钱,R通道与B通道交换开关/U通道与V通道交换开关。再回过去看input_format,其实我们并不仅支持YUV420SP_U8/XRGB8888_U8/RGB888_U8/YUV400_U8,还可以支持BGR、NV11等等。
    在这里插入图片描述
    这些数据,通过mindstudio不需要修改的,实际修改的机率也不大。
    在使用mindstudio的时候,aipp设置会根据开关状态回去自动生成。
    还有没有提到的参数,比如说减均值、归一化的参数配置,其余的参数可以根据具体场景使用,去查看《atlas200dk的模型转换指导》文档。
    atlas200DK还提供了更快捷的模型转换工具,那就是我们的mindstudio,下一章节,我们就去看看模型在mindstudio上时如何转换的。
    6. Mind Studio模型转换实际操作
    1)首先打开谷歌,登入mindstudio
    在这里插入图片描述
    然后去看模型转换的入口(tools–>Convert Model)
    在这里插入图片描述
    2)file–new–new project创建一个工程,创建界面如下:
    在这里插入图片描述
    在界面左边,双击resnet_50.mind,打开工程看一下,中间其实就是拖拽的界面,右边也是控件。如下图所示:
    在这里插入图片描述
    下图就是模型转换的入口,如下图所示,此时就不再是灰的了,点击进入就可以。
    在这里插入图片描述
    其实在我们工程也是可以进入模型转换的入口的,如下图所示:
    在这里插入图片描述
    在界面的右边,也就是控件部分model–>my model[+],也是可以进入模型转换的入口的,如下图所示:
    在这里插入图片描述
    进入模型转换界面之后,
    在这里插入图片描述
    offlinemodel,其实就是说,通过命令行方式,或者其他地方转过的模型,可以加载到当前的mindstudio中来,
    在这里插入图片描述
    量化模型,是需要输入图片校准级的,放在image_file。最后会生成一个量化模型。
    Optional Optional是一个比较通用的。
    在这里插入图片描述
    在这里插入图片描述
    operator plugin path,是自定义算子的一个插件。
    很多图片的预处理,都是可以放到aicore去做。
    input preprocessmodel是aipp的一个功能,可以不用,如果想要模型是fp32输入,那么就直接关掉。也是ok的,也是可以转的。但,如果我们能用这些aipp,我们还是尽可能去用。因为像aipp里面,你把它生了之后,我们很多一些图片数据的预处理。都可以放到aicore的去做,这样就可以大大加大我们的效率,就是在做推理的时候,那我们推理的性能肯定会比我们fp32速度快很多。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    实际输入图片大小和模型所需模型大小不一样,原因在于受dvpp影响,dvpp的yuv数据输出做了padding,也就是会把我们的数据做对齐,这是自动的,我们不需要管。在运行的时候,aipp也会相应的把多出来的滤边裁剪掉。所以对我们是相对无感的。
    model image format,作用就是上面(input image format)输入图片yuv420转换到BGR。如下图:
    在这里插入图片描述
    crop start loc:抠图
    mean less:减均值
    multipulying factor是做归一化的
    encryption是加密功能,也很少去用。
    在这里插入图片描述
    生成之后界面,如下图所示:
    在这里插入图片描述
    点击close,出现如下界面,此时转换好的模型就放到了右边,mymodel里面了,此时左边也多出一个convertmodel.log
    在这里插入图片描述
    双击convertmodel.log打开看看,在中间部分可以看到,它给我们生成了一个aipp的配置说明以及模型转换的命令,其实它会做每个算子的遍历和处理,也可以看到一些报错。
    在这里插入图片描述
    以上是mindstudio转换,下面是在命令行的模型
    先找到mindstudio中omg的工具,这个工具在~/tools/che/ddk/bin.x86_64-linux-gcc5.4
    在这里插入图片描述

    在这里插入图片描述
    由于每次使用都需要指定路径,这样做比较费事,可以做一个软链接。这样在任何终端都可以使用,就不需要指定路径了。
    在终端输入命令:ln -s omg /usr/bin/omg
    在这里插入图片描述
    在这里插入图片描述
    输出模型为fp32的命令:
    omg --model=resnet50.prototxt --weigth=resnet50.caffemodel --framwork=0 --output=resnet_noaipp
    在这里插入图片描述
    下图为fp32的输出模型:
    在这里插入图片描述
    下图带aipp的模型,也就是int8的模型。命令方式,如下图:
    omg --model=resnet50.prototxt --weigth=resnet50.caffemodel --framwork=0 --insert_op_conf=aipp.cfg --output=resnet_aipp
    在这里插入图片描述

    展开全文
  • 模型转换对MDA的成功至关重要,但对于同传统软件开发过程中的测试部分同等重要的模型转换中的验证和有效性确认(V&V),目前的研究比较少。为此,尝试提供一个基于MDA上下文的模型转换性能的验证框架。该框架用于验证...
  • 使用IBM模型转换框架进行模型转换

    千次阅读 2012-03-28 15:11:06
    本文实际上是一个阐明如何为一个具体例子书写规则的循序渐进的指南,而这个具体例子是指从一个UML(统一建模语言)转变为关联模型。每一步表达了一个新的映射规则,并解释了相关的概念;在需要的地方有类图描述规则。 ...

    首先,你将了解对MTF的简要概述(它是什么,有哪些重要的用例,等等)。然后你将了解贯穿这篇文章的基本概念(映射关联以及映射语言的语法)。

    本文实际上是一个阐明如何为一个具体例子书写规则的循序渐进的指南,而这个具体例子是指从一个UML(统一建模语言)转变为关联模型。每一步表达了一个新的映射规则,并解释了相关的概念;在需要的地方有类图描述规则。

    指南结束于一个MTF安装工具的表达,它使你能够运行例子。最后,结论部分概括了本文中涉及的概念并推荐了一些其他资源。

    在软件开发中使用建模

    使用建模是软件工业的一个上升趋势,正如模型驱动工程的兴起和对象管理组织(OMG)最新的产品——模型驱动体系架构(MDA)——的发布所指出的。欲知更多信息,请参考本文后面的资源一章。模型转换在本文中起了关键作用,因为它能使你:

    • 把已有模型映射到其他模型
    • 在不同模型之间建立通讯和可追溯性

    尽管如此,为了取得完全的有效性,这些技术需要相关工具和运行时系统的支持。EMF为Eclipse的建模平台提供了基础,它允许你定义模型知识库和相关的围绕一个通用模型的编辑器。

    在2002年,作为Meta Object Facility (MOF) 2.0标准版的一部分,OMG提出了建立允许用户查询、建立并维护视图以及转换MOF模型的标准的建议。这一MOF 2.0标准被称为MOF 2.0 Queries/Views/Transformations (简称QVT)。在2004年11月,一些愿意遵从该标准的公司(包括IBM)开始合作定义QVT标准。当时的规约还需要大量的修订。如果需要关于OMG初始版本的更多信息,请参看本文末尾的资源部分。

    什么是模型转换框架?

    作为参与QVT标准的一部分,IBM已经开发了一个模型转换工具包的原型,称为MTF,现在可以从alphaWorks获得(见资源)。MTF实现了QVT的一些概念,并且是基于EMF的。它提供了一种简单的声明语言,可为定义模型之间的映射而用,另外为了完成转换还提供了一个转换引擎,它解释了映射的定义。

    MTF的目的是通过支持递增的更新、双向的检查、调和以及可追溯性简化你开发转换工具的工作。最常见的相关用例包括:

    • 模型、代码和文档产生
    • 模型比较和合并
    • 设计模板扩展
    • 模型一致性

    MTF的外在形式是一组你可以在Eclipse或者IBM软件开发平台产品上部署的插件。它为EMF模型和一组帮助你书写、运行和调试映射规则的工具提供转换引擎。规则编辑器提供了基本编辑特性(比如语法检查)。调试器允许你一步步地跟踪规则的运行。映射视图使你能够在转换完成时监视转换的结果。

    有趣的是,转换引擎并不是只期望在一个Eclipse环境内部运行的。它只依赖于EMF运行时库,因此它可以用于任何Java™应用以及Eclipse插件中。

    MTF基础

    模型转换概念

    术语模型转换背后的主要思想是根据预先定义元素间的关联,根据输入的其它模型产生模型。它假定为了使用一致性方式表达通讯,这些模型是用兼容的通用模型描述的。在QVT的作用域内,模型转换与MOF模型相关。MTF把这些概念应用于EMF模型。

    一个MTF转换的结果是一组映射,它们把不同模型中的对象联系起来;一个与任何映射都无关的对象被认为是位于转换域外的。指定模型间的映射提供了定义转换结果的声明方式。比如说,如果一个映射需要目标模型中存在一个对象,转换引擎将试图创建该对象来满足映射的需要。

    关联个体实例的映射也许不是指明一个转换最简明和方便的方式。你可能偏向于在类的级别上定义通讯,而不是为每一个实例定义。为了做到这一点,你可以指明关联相同类型的对象的映射,并把它们命名为关联。在MTF中的一个关系(也称为一个映射规则)定义了一种可应用于给定模型类的实例的映射类型。在某种意义上,一个关联的语义与一个限制十分相似。

    下面的图1是一个例子,指明了两个模型(分别用红色和蓝色表示)之间的映射,其中包含了用于相关实例产生三个映射(m1, m2和m3,用白色表示)的两个关联(A2C和A2D)。


    图1 两个模型间的映射的例子
    Example of mappings between two models  

    MTF下的转换是用一种声明的方式定义的:你指明模型类间的一组关联,然后让MTF引擎使用关联作为输入完成转换操作。转换引擎的工作分为两个阶段,分别是映射调和。在映射阶段,引擎评估关联,并通过在相关模型实例中进行迭代产生映射。在这一阶段的最后,一些映射可能与关联不一致。换句话说,不是所有关联要求的限制都被满足了。

    调和试图通过创建缺失的元素,修改现有的元素,或者删除元素来满足所有关联。在某些情况下可能不需要调和(比如模型已经是一致的了,或者你只想检查模型的一致性而不想改变它们)。此外,调和不是永远可能的(如果从映射没有可引用的值调和就是不可能的)。这些概念现在看来有点抽象,但是在后面的章节中我们将给出详细的解释。需要记住的主要思想是下面这个过程:

    关联 > 映射 > 映射 > 调和 > 更改的模型

    关联的组织方式是符合模型的结构的。通常你会在每个模型类中找到至少一个关联,从联系顶级模型类的关联开始。与基于规则的系统相似,从一个关联中调用另一个关联是可能的,而且可以把映射的执行传递到有联系的模型类。这种机制称为通讯,它允许MTF通过把从顶级关联开始的所有通讯变为可到达的(直接或间接)横跨一个完整模型。

    MTF支持一个灵活的转换模型,它可以接受多个模型,联系多个元素,并更新多个模型。关联没有指定评估的任何方向,它们可以在多个方向下执行,这在你处理双向问题时是很有用的。转换的方向(源和目的)实际上是在你调用转换引擎时被定义的。

    为MTF编写规则

    正如你可能已经猜到的,驱动转换的关联在MTF中起着重要作用。它们是用一种叫做关联定义语言(RDL)的语言来表达的,该语言被转换引擎分词和评估。基于结构化特性之间的通讯,RDL允许类之间的关联的定义和应用。比如,一个典型的关联会用一个匹配标志符属性联系两个类。语言的语法很简单,只包含几个关键词和标准布尔操作符。

    关键词relate允许使用表1中的格式定义关联。名字和形式参数都在关联中定义。在一些情况下你可以限制这些参数来过滤匹配的值。关联的主体通常包含需要用于匹配的值属性的通讯,这样可以传递转换的执行。基于类的EMF结构化特性可以通过查询匹配值查找属性。关键词equals是指一个内嵌的通讯,它允许对字符串和其他原始类型的相等测试。


    表1 定义关联
    				
    relate MyRelation(s:S source, t:T target)
    {
    	equals(source.name, target.name),
    	MyOtherRelation(source.value, target.value)
    }
    

    RDL语言尽管很简洁,却使你能够表达很多模型转换中使用的关键概念(比如,通讯,表达式,以及条件)。由于关联只定义元素间的通讯关系,它执行的上下文(换句话说,输入的实际参数)就十分重要了。不同的执行可能会产生不同的结果。下一节中出现的转换将有助于详细解释这些概念。

    例子:一个UML到关联模型的转换

    必要准备

    这一节讨论如何在一个具体用例中使用MTF:一个UML到关联数据库(RDB)模型的转换。关于这个主题已经有很多讨论和若干转换算法。在本文中我们基于J. Rumbaugh et al.(参见资源)介绍的原则实现了一个部分映射。该映射的主要规则如下:

    把类映射到表

    • 每个UML类对应一个表。
    • 类的每个简单属性对应表的一列。

    映射关联到表

    • 每个多对多的关联映射到唯一的表。
    • 每个一对一的关联在表中任一类中充当外键。
    • 每个一对多的关联在表中多个类中充当外键。
    • 集合与关联遵循相同的规则。

    为了简单起见,这个例子做了几个简化假设。它不:

    • 支持非通用的关联(每个关联的结束属于一个类)
    • 为其它UML概念,比如包,概括链接,等等提供映射

    你将使用Eclipse.org UML2项目(参见资源)的UML 2.0通用模型的一个EMF实现。IBM® Rational® Software Modeler的UML建模工具和IBM® Rational® Software Architect的产品都是基于这个EMF通用模型的。

    由于没有关联计划的标准通用模型,而且还是为了简单起见,我们决定基于EMF来定义我们自己的通用模型,我们称它为SimpleRDB见图2。这张图定义了一个包含一组表的计划,它们可以用简单键值限制联系起来(细节如下)。

    另一个解决方案是平衡RDBSchema通用模型,该模型被用于实现IBM? Rational? Application Developer (IRAD) SQL工具的一部分。在这种情况下,从UML模型中产生的计划可以被IRAD工具消耗——比如说,为了产生SQL代码。这是基于MTF的一个很好的工具集成的例子。


    图2 SimpleRDB通用模型
    The SimpleRDB meta-model  

    MTF运行在Eclipse 3.0或更高版本上,配合相应的EMF级别。它也可以安装于IRAD,Rational Software Modeler,以及其他IBM软件开发平台工具上。它作为一组插件被封装,你只需把它置于你的插件目录下(关于安装的更多细节请参见MTF程序员指南——见Resources)。为了运行本转换例子,你还需要安装以下软件:

    • UML2 1.0: 可在www.eclipse.org获得,点击下载链接并参看Eclipse Tools Project一节
    • SimpleRDB 1.0: 包含在本文中

    这个例子解释了RDL语言的基础,这将帮助你开始学习MTF。这里解释的很多关联用UML类图进行了说明。由于没有标准图形注释来描述MTF用到的概念(关联,通讯,条件),我们将使用一个变化的标准UML类图,我们向其中加入了我们自己的格式和限制注解。

    在你的工作空间中建立一个项目,然后创建一个名为simplerdb.rdl的新文件。使用MTF映射规则编辑器打开该文件(.rdl是编辑器默认的文件扩展名)。现在,你已经准备好编写你的第一个关联了,如表2所示。

    步骤1:定义导入陈述

    RDL文件以一系列导入陈述开始,它们被用来声明转换中涉及的EMF包。一个导入把一个URI包绑定到一个逻辑名,作为关联中指明的类的前缀,如表2所示。


    表2 导入陈述
    				
    import uml "http://www.eclipse.org/uml2/1.0.0/UML"
    import rdb "http:///com/ibm/mtf/simplerdb.ecore"
    import ecore "http:///com/ibm/mtf/model/emf.ecore"
    import ws "http:///com/ibm/mtf/model/workspace.ecore"
    import util "http:///com/ibm/mtf/util.ecore"
    

    这些逻辑名提供了与XML名字空间相似的一种唯一的识别机制。如果你不知道你确切需要导入哪些包的URI,映射规则编辑器提供了一个内容辅助特性,它列出了工作环境中注册的包。

    在你的例子中,为了把UML模型联系到关联计划,你需要导入umlrdb包。你还需要导入ecorewsutil实用程序包,它们被用来加载模型(将在下一步中作解释)并导入特殊结构。

    步骤2:把UML2资源映射到SimpleRDB资源

    转换从顶级关联开始,如表3所示。它为加载模型对象和在工作环境下运行转换提供了一个方便的机制(参见运行!一节)。如果你已经编写了Eclipse插件,你可能对类名IFile比较熟悉。这里,你要处理的不是 org.eclipse.core.resources.IFile类本身,而是它周围的EMF包装。


    表3 映射资源
    				
    relate Uml2SimpleRdb(
    ws:IFile source,
    ws:IFile target)
    {
    	UmlModel2Schema
    (over source.resource.contents,
    over target.resource.contents)
    }
    

    MTF的设计是对EMF模型进行转换,但是它也提供了改写非EMF类的方法。一种称为扩展的机制使你能够采用任何Java类并在转换引擎中运用用户定义语义进行操作。MTF包含一些对文件和EMF资源提供默认支持的扩展包,ecorews就是其中两个。并不一定非要使用这些包,但是它们确实在你定义变化时很有帮助。

    你可以分开编写加载相关的EMF资源和对模型对象调用映射引擎的代码,但是在这个例子中所有任务的完成都是透明的。如果你对RDL的语法还不熟悉的话不要担心,在例子的下一步中我们将会加以详细解释。

    RDL中没有定义用例或命名的规则。本例子试图遵循Java的命名规则按照自顶向下的方法组织关联(映射,表达式,以及条件)。

    步骤3:把模型映射到计划

    列表4表示的关联把任何给定UML模型映射到同名的一个计划。形式参数uml:Modelrdb:Schema被用于匹配标准;因此,只有当用于关联的值是Model或者Schema的实例时才会创建映射。


    列表4 映射一个模型
    				
    relate UmlModel2Schema(
    uml:Model model,
    rdb:Schema schema)
    {
    	equals (model.name, schema.name)
    
                          UmlClass2Table
    (over model.ownedMember, match over schema.tables),
    	
    	ManyToManyUmlAssociation2Table
    (over model.ownedMember, match over schema.tables)
                          
    }
    

    如果你仔细研究一下前面的关联,你就会发现它为了传播从EMF资源到它们的内容的转换是如何调用这一关联的。一个EMF资源定义了一个E对象的集合,这一集合在模型中是可获得的,并可以通过contents属性访问。关联UmlModel2Schema将同左面的资源(UML模型)的内容一起被例化。实际上,你永远有把关联应用到任何对象(或者一组对象)的能力。如果对象不符合类型标准,那么什么都不会发生。如果符合,对每一个符合的值的组合都会有一个映射被例化,然后转换将被传播到子元素。

    关联签名主要关注UML模型和关联计划之间的映射,但是并不表达它们的内容应如何映射。用于联系模型元素的通讯是在关联体里的,当关联被应用时,转换引擎将搜索通讯——类之间的和UML模型内定义的关联,以及关联计划中定义的表——并为它们产生相关映射。

    我们确信计划的名字与使用equals内嵌通讯的模型的名字是相同的;而且ModelSchema类的名字属性是相同的。注意用于查询EMF对象的属性的基于点的语法。RDL中使用的属性名是基于EMF类的结构特性的。

    在UML 2.0的通用模型中,类和关联是PackageableElement的子类,而且通过一个称为ownedMember的浏览属性从Model类中是可以访问的。在你的转换中你使用这个属性来联系类/关联和关联计划的表。由于ownedMembertables属性指的是一个集合,我们使用关键词over来指出通讯将被用于集合所有的元素,而不是把集合本身当作一个对象。

    在这个特定用例中,关联的执行将会在所有表和所有符合后面规则中解释的标准的用户之间产生一个衔接产品。注意到关键词match指出了领域中的一些元素,比如列表4中的schema.tables,被允许保持不匹配。为了防止没有带有前缀的匹配关键词的情况(比如列表4中的model.ownedMember)出现,集合中所有类型符合的元素都必须通过关联UmlClass2Table进行映射。


    图3 关联UmlModel2Schema及其通讯
    The UmlModel2Schema relation and its correspondences  

    示例的这一步骤中表达的关联只表明了典型的UML到RDB的映射的一部分,因为它只处理二元关联。一个完整的映射将考虑n元关联,以及类之间的概括关联。

    步骤Step 4:把类映射到表

    你的下一步是在关联计划中定义UML类如何联系到表,如列表5所示。映射与前面的关联很相似;你将找到一个与每一个类同名的相关表。这里关键词when保证了类和表的名字的同一性。关联的执行将把转换传播到每个类的属性,并把它们映射到相关列。这一关联还处理一对一和一对多关联。


    列表5 映射一个类
    				
    relate UmlClass2Table(
    	uml:Class class,
    	rdb:Table table)
    	when equals(class.name, table.name)
    {
    	TablePrimaryKeyColumn [1] (match over table.columns,
    over table.constraints),
    
                         UmlAttribute2Column(over class.ownedAttribute,
    match over table.columns),
    
                        ClassOwnedEnd2ForeignKeyColumn(over class.ownedAttribute,
    match over table.columns, over table.constraints)
    }
    

    由于这个例子只处理可浏览的关联,它假设关联的结束永远属于类。因此,所有实现关联结束的属性在关联计划中将同一个外键相关。多对多关联需要一个特殊映射,它在另一个关联中被单独处理。

    关联TablePrimaryKeyColumn应用于通讯,它向每一个同UML类相关的表添加一个主键.主键定义了一列或一组列,它们是表的唯一标识。它是SimpleRDB的通用模型的第一个类实体,但是在UML 2.0中没有它的对等体.这就是关联的签名没有与UML元素相关的形式参数的原因。它还解释了我们在通讯中使用特定多重性的原因。

    此前RDL中的多重性概念还没有被介绍到;它使得通过指定上下限限制映射的数量——从一个关联中进行的例化——成为可能。在这个特定例子中,与一个关联且只与一个关联对应的多重性被定义为与关联TablePrimaryKeyColumn对应(你可以看到列表5中的通讯介绍的括号符号)。如果没有指明,默认的多重性是从0到n,这表示映射尽可能多,在这里这是无关的,因为主键意味着唯一。

    关联UmlAttribute2Column把一个类的简单类型属性映射到表的一列。在这里使用匹配关键词是因为不是所有列都与UML属性相关;正如前文介绍过的,添加主键和外键是为了同时映射UML关联。在关联ClassOwnedEnd2ForeignKeyColumn的例子中,我们把关联的结束——表示为类型的属性——映射到包含一个列和一个外键限制的外键。


    图4 UmlClass2Table关联及其通讯
    The UmlClass2Table relation and its correspondences  

    这个例子中贯穿的类图是RDL语法之外的概念的又一种可能表示。在这些类图中,关联被定义为带有特殊标记的类,它们的属性代表了输入中指明的形式参数。关联涉及多个模型类,它带有表示用于参数的值的属性。定义在关联的属性上的限制表明了限制候选值的作用域的条件。正如你在图4中看到的,propertycolumn的角色是关联UmlAttribute2Column的形式参数,而ownedAttributescolumns表示的是通讯涉及的实际值

    通讯是由两个关联之间的关联表明的,并带有详细的类型标记。到目前为止这个例子集中于由包含关联(属性包含于一个类,表包含于一个计划,等等)联系起来的类之间的通讯,并已经在类图中使用了累计符号来表示它们。尽管如此,在下一步骤中你将看到,有些通讯还可以用于由简单关联联系起来的类。在这种情况下,表示将是不同的,不带有累计符号。

    步骤5:向表添加主键

    列表6定义了一个列与一个主键之间的关联,后面的图5也表示了这点。该关联保证:

    • 列的名称与主键一致——在这个例子中名字是 ID
    • 实际上关键是表的主键的指向
    • 键指向列
    • 列的类型是INTEGER

    列表6 添加主键
    				
    relate TablePrimaryKeyColumn(?
    rdb:Column column, 
    rdb:PrimaryKey key)
    	when equals (column.name, "ID")
    {
    	equals (key.table.primary, key),
    	equals (column, over key.column),
    	equals (column.type, "INTEGER")
    }
    

    指明列类型的内嵌通讯表现了RDL的声明性。在你的转换中,关联计划是目标模型,调和将为它产生值。对目标模型应用通讯是一种声明性规约,目标值就属此类。在我们的具体例子中,这意味着作为主键的每一列的类型都是INTEGER(整数)。这是本例子第一次把关联作为一个定义表达式(换句话说,一个赋值语句)的声明性陈述来使用,而不是用它作模型到模型的映射。


    图5 TablePrimaryKeyColumn及其通讯
    The TablePrimaryKeyColumn and its correspondences  

    步骤6:把一个属性映射到一个列

    列表7中的关联把一个UML属性映射到一个同名的列。在UML 2.0通用模型中,属性和可浏览关联结尾都被表达为类的属性。


    列表7 映射一个属性
    				
    relate UmlAttribute2Column(
    	uml:Property property 
    when util:InstanceOf "uml:DataType" (property.type),
    	rdb:Column column)
    	when equals(property.name, column.name)
    {
    	equals(column.type, "VARCHAR")
    }
    

    关键词when有多种应用并可以用于不同上下文。尽管所有这些使用都表达了一种条件,它们各自的含义是有区别的。定义于一个参数声明之内(比如,上面关联中的property 参数),when从句定义了过滤用于通讯的候选值的方式。它只与参数域的属性有关,当它用在关联的签名结尾时,基于相关参数之间的特定关联,同一个从句定义了不同候选值组之间的共同标准(比如,属性和列的名称)。这称为一个识别条件

    在上面的关联中,你只想映射简单类型属性,因此你要确定应用于属性参数的过滤条件InstanceOf限制,它保证了属性的类型是Datatype。这一用户限制扩展了RDL语言,是MTF附带的util扩展的一部分。

    在关联体中,你用一个通讯来指明每个与匹配的属性相关的列的类型。为了简单起见,考虑把VARCHAR作为除主键和外键外的所有列的默认类型。


    图6 UmlAttribute2Column关联
    The UmlAttribute2Column relation  

    步骤7:把关联结尾映射到外键

    列表8中的关联定义了一个关联结尾和一个外键之间的映射。根据前文介绍的原则,Rumbaugh et al. 认为按照它们的多重性,不同的关联映射用例:

    • 一个一对一关联被映射到表中的一个外键(无所谓哪个)
    • 一个一对多关联被映射到"many"表的一个外键
    • 一个多对多关联被映射到一个单独的表

    列表8 映射一个关联结尾
    				
    relate ClassOwnedEnd2ForeignKeyColumn(
    uml:Property property when CheckOne(property)
    & util:InstanceOf "uml:Class" (property.type), 
    	rdb:Column column, rdb:ForeignKey fkey)	
    when equals (property.name, column.name)
    & equals (column, over fkey.column)
    {
    	equals(column.type, "INTEGER"),
    	ref UmlClass2Table(property.type, fkey.refTable)
    }
    

    这里你将集中于一对一和一对多的多重性,你将把它们作为单一的用例处理。多对多关联(由另一个关联处理)不在本文讨论范围之内。具体到本例,考虑所有关联结尾的外键的建立都带有多重性“1”。这样做的结果,将会出现下面两种情况:

    • 在一对多关联的情况下,你的转换在所有"many"表中建立了一个外键
    • 在一对一关联的情况下,你的转换在所有表中都建立了一个外键

    你所追求的与Rumbaugh et al计划的稍有不同。

    你将注意到UML通用模型的结构暗示了关联映射的不同策略,无论它们的结尾是可浏览的(属于一个相关类)还是不可浏览的(属于关联本身)。为了访问关联结尾,你需要访问模型拥有的类,或者访问关联,或者两者都访问(如果可浏览的和不可浏览的结尾都要处理)。

    为了简单起见,这个例子只支持两路可浏览关联。这使你能够通过访问类很容易地访问关联结尾,然后把相关属性映射到外键。

    列表8表现的关联的一个特殊方面是使用了另一个关联(比如,CheckOne)作为过滤值的条件。嵌套于域声明中,通讯可用于建立基于关联多重性的强大的过滤器。这里,它允许你定义更多表达能力很强的符号,而不止是严格的相等(比如,检查一个组中某个元素的存在性)。

    这里使用关联CheckOne是为了保证在关联执行时多对多关联不会被考虑进去。其他预声明的定义是为了保证作为参数提供的属性是一个关联结尾(还是以InstanceOf为限制)。它们还检查了属性和列的名称的一致性是否被满足了。

    当我们能够清楚无二义地联系任何关联结尾和外键时,我们可以把通讯应用于指明外键指向的是哪个主键。在SimpleRDB中,主键和外键被定义为一个和指向该列的键值限制相关联的列,以及一个同时拥有列和限制的表。此外,外键拥有一个refTable属性,它指向另一个表定义的主键。在该通讯中,你需要表达主键和外键之间的联系,并指明该属性的值。

    应用于属性property.typefkey.refTableUMLClassToTable关联可被用来指明主键和外键之间的联系。在前面关联曾被用在通讯中来把模型拥有的每个类映射到一个关联计划。在两种情况下,目标都是把UML类(uml:Class的实例)映射到关联表(rdb:Table的实例)。

    你在这里所作的有一点不同,主要是你把简单引用映射到已经相关的元素了(换句话说,映射到UML类)。这不同于映射累计关联,那样实际是建立新的元素。从映射的角度来看,这意味着你需要引用已有的映射(UmlClassToTable的实例并联系一个已有类和表)而不是例化新的映射。

    这就是本例在通讯中使用关键词ref来表达对引用元素映射应该已经存在而无须再建立一个映射的含义。


    图7 ClassOwnedEndToForeignKeyColumn关联及其通讯 
    The ClassOwnedEndToForeignKeyColumn relation and its correspondences  

    列表9定义的关联是MTF中关联多种使用的又一个例子。这里你的主要兴趣是从关联建立相关的映射,然后对它们计数作为条件的一部分。从这个关联中例化的映射在调和时将不被考虑。


    列表9 从一个关联建立映射
    				
    relate CheckOne(
    	uml:Property property when equals(property.upper, "1")
    

    此关联的目的是过滤掉多对多关联,它由另一个关联处理。你在寻找多重性为1的关联结尾,记为1in UML2 (请见下面的图8)。


    图8 CheckOne关联 
    The CheckOne relation  

    本文中表现的关联实现了基于简单假设的从UML类到表的转换——以及UML一对一和一对多关联到键值限制的转换。它使你能够理解RDL语言的主要概念,即使此刻一个UML到关联的映射还未完全说明。多对多关联的情况这里没有讨论,因为它的实现与我们前面介绍的大同小异。整体原则是为每个相关关联建立第三个表,然后定义相关键值限制。这里没有详述的额外关联将作为例子的源代码的一部分出现。

    更深入的概念,如抽象关联,或者抽象继承,也是RDL语言的一部分,但是本文将不作介绍。关于这些主题的更多资料可在MTF程序员指南中获得——请参见资源

    运行!

    一旦关联定义完毕,你就可以在实际模型上执行转换了。本文使用了Primer Purchase Order模型,一个EMF书中(参见资源)描述的经典例子,如图9所示。UML2指南(参见资源)使用了一个变化版本,叫做Extended Purchase Order。我们使用UML2定义Primer Purchase Order模型,指明PurchaseOrderUSAddressItem类之间的双向关联,它在本文中可以获得。你将需要复制你工作区中的文件,最好是你为这个例子创建的项目。使用这个模型作为输入,你将能够运行一个MTF任务并产生相关的关联模型。


    图9 改写Adapted Primer Purchase Order模型 
    Adapted Primer Purchase Order model  

    MTF提供了一个高级发布界面,它允许你从Eclipse工作环境中触发转换。你只需要创建一个新的运行配置并输入需要的参数:

    • 顶级规则
    • 源文件和目标文件
    • 转换的目录

    发布程序只有在顶级规则有能够从字符串创建键值的参数的时候才接受变量。这也是你在Uml2SimpleRdb关联中使用ws扩展的又一个原因,因为这样你在实例化IFile类的时候可以用一个字符串指明文件。

    MTF任务的配置画面如图10所示。你需要输入simplerdb.rdl文件的路径,并把Uml2SimpleRdb定义为输入文件。然后你需要指明源和目的变量的值;源变量是Primer Purchase Order文件的路径(将是不可修改的),而目标变量则指向要创建的关联计划的路径(必须被修改)。这个组合,包括可修改的参数,使你能够定义转换的方向。如果这一方面配置不当,调和将不会产生相关的结果。


    图10 作为一个任务运行的Uml2SimpleRdb转换 
    Running the Uml2SimpleRdb transformation as a task  

    规则应被顺利执行,一个对应于关联计划的新文件应在你的工作环境中出现。

    MTF在Eclipse工作环境中的作用是提供映射视图,通过它你可以检查由转换引擎产生的映射。这个特性确实帮助你跟踪和理解转换的执行。当任务完成后,你可以点击出现在属性列底部的按钮button。你将会看到和图11相似的结果。


    图11 检查MTF产生的映射 
    Inspection of the mappings generated by MTF  

    结论

    MTF为模型转换提供了一种声明的方式。在这篇文章中,我们简要描述了MTF语言并示范了它在从一个UML模型到关联计划的映射转换中的使用。MTF为自动模型转换和同步活动提供了各种可能性:

    • UML到UML
    • 模式扩展
    • 基于不同Profile(EJB, CORBA, 等等)的独立于平台模型(PIM)到特定平台模型(PSM)转换

    从工具的角度看,MTF提供了基于EMF模型的低成本、成功集成的工具。

    展开全文
  • 1 内容简介2 模型转换3 RSA模型转换框架4 基于RSA模型转换框架的开发5 扩展模型转换6 总结参考资料 本文首先介绍模型转换的基本概念,然后介绍RSA模型转换框架,之后本文以两个具体的例子介绍如何在RSA开发...

    2008-07-03 作者:何蕾 孙瑛霖 来源:IBM

     

    本文内容包括:
    本文首先介绍模型转换的基本概念,然后介绍RSA模型转换框架,之后本文以两个具体的例子介绍如何在RSA开发平台中以模型转换框架为基础创建和扩展模型转换。

    1 内容简介

    模型转换框架(Model Transformation Framework)是IBM最新开发平台RSA(Rational Software Architect)中的重要组成部分,其主要功能是在模型驱动的开发过程中,为实现各种模型之间的转换提供基础平台的支持。基于这个框架,能够很容易地实现模型之间的转换程序,以及扩展已有的转换。

    本文首先介绍模型转换的基本概念,然后介绍RSA模型转换框架,之后本文以两个具体的例子介绍如何在RSA开发平台中以模型转换框架为基础创建和扩展模型转换。

    2 模型转换

    MDA(Model Driven Architecture,模型驱动的体系结构)和MDD(Model Driven Development,模型驱动的开发)是当今软件领域最热门的话题。相比于传统的以代码为中心的开发模式,MDA和MDD则是以模型为中心,在开发的各个阶段,都使用模型来描述系统特征。

    模型转换贯穿于MDD的全过程。系统开发初期,为了刻画系统特征,一般会创建系统的分析模型。分析模型是一个比较粗糙的模型,相当于草图的作用,只用于辅助分析。随着系统的特征越来越清晰,系统的设计模型会慢慢形成。设计模型能够比较精确地描述系统,是系统实现的基础。从设计模型可以很容易地导出系统的实现模型。实现模型包括具体的代码、脚本、配置文件等等。这是一个自顶向下、逐层细化的过程,从高层的抽象模型开始,经过一系列的模型转换,最终生成底层的系统实现。

    图1 模型转换
    图1 模型转换

    由此可见,模型转换在模型驱动的开发和设计中起着非常重要的作用。

    3 RSA模型转换框架

    RSA(Rational Software Architect)是IBM的新一代软件开发平台,代号为Atlantic。RSA基于开放/可扩展的Eclipse 3.0构建,全面支持UML 2.0标准和模型驱动的开发方法。RSA为开发当今日益复杂的应用系统提供了一个强大的的开发环境,整合了UML建模、J2EE、XML、web services、C++开发工具以及RUP过程指南等诸多功能,是软件架构师/开发人员的首选工具。

    为了支持模型驱动开发过程中各种模型之间的转换,RSA提供了一个功能强大、易于扩展的模型转换框架(Model Transformation Framework),该框架是一个基于规则的执行引擎,基于该框架,可以很方便地定义模型转换规则,实现各种模型之间的转换。RSA模型转换框架实现了模型转换的注册和配置管理,提供了统一的运行界面,以及各种开发向导。

    RSA预装有一些常用的模型转换,包括UML2JAVA、UML2CPP、UML2EJB,这三个转换分别以UML模型为输入,生成对应的Java程序、CPP程序和EJB。关于如何使用RSA预装的模型转换,请读者参阅RSA的相关文档。

    下面简单介绍RSA模型转换相关的一些概念:

    • Transformation(转换):以源模型对象为输入,目标模型对象为输出,实现模型之间的转换。每个转换由若干条转换规则组成。
    • Rule(转换规则):以源模型对象中某部分为输入,目标模型对象的对应部分为输出,实现了功能逻辑上相对独立的一部分转换任务。
    • Transformation Context(转换上下文):转换上下文是转换过程中转换规则之间共享数据的容器。在转换的执行过程中,转换上下文会在规则的实现之间传递,每一个规则可以向上下文中存放需要共享的数据,也可以从中获取所需的数据。
    • Transformation UI:模型转换框架会为每个转换提供一个默认的配置管理界面,让用户指定源模型,目标模型,以及所需的一些转换属性。如果需要定制更加复杂的界面元素,则需要模型转换的开发人员对默认的界面加以扩展。

    至此,读者应该已经对RSA模型转换框架有了总体的了解,本文余下部分将结合具体的例子,介绍如何在RSA中基于模型转换框架开发新的模型转换和扩展已有的模型转换。

    4 基于RSA模型转换框架的开发

    本节以一个具体的模型转换为例,介绍如何在RSA中基于模型转换框架开发新的模型转换。这个转换名为Model2Text,接受的输入为UML模型中的Class、Package,或者Model,如果输入为Class,则遍历类的属性、方法、参数,将这些信息输出到控制台,如果输入为package或者model,则遍历package和model下面的每一个类,将类的信息打印到控制台。

    4.1 创建新的插件工程

    通过新建工程向导创建一个插件工程。由于模型转换框架是作为eclipse插件集成在RSA中,因此模型转换工程也必须是一个eclipse插件工程,这样才能够使用转换框架提供的组件。

    4.2 添加TransformationProviders扩展点

    为了创建一个新的模型转换,需要从TransformationProvider扩展点进行扩展。首先添加所需的插件:

    图2 开发模型转换所需的插件
    图2 开发模型转换所需的插件

    然后添加TransformationProviders扩展点,该扩展点的ID为com.ibm.xtools.transform.core.transformationProviders。

    4.3 创建模型转换的入口:TransformationProvider

    从TransformationProviders扩展点扩展,添加TransformationProvider:

    图3 创建TransformationProvider
    图3 创建TransformationProvider

    添加TransformationProvider之后需要创建对应的实现类,这个类需要继承自模型转换框架提供的接口com.ibm.xtools.transform.core.AbstractTransformationProvider,是模型转换框架执行引擎调用模型转换的入口点,主要提供两个方法:

    createTransformation创建模型转换的实例,返回给调用引擎
    validateContext验证模型转换的执行上下文,从上下文中可以获取源模型和目标容器,进行验证。验证的结果会反映在模型转换的配置管理界面上。本例验证源模型是否Class、Package或者Model。

    4.4 创建Transformation

    本例的Model-to-Text转换在plug-in.xml中定义如下:

    <Transformation
     version="1.0"
     name="%Transformation.name.classtotext.console"
    transformGUI="com.ibm.xtools.transform.samples.modeltotext.TransformGUI"
     keywords="%Transformation.keywords.classtotext.console"
     author="%Transformation.author"
     groupPath="%Transformation.groupPath"
     sourceModelType="UML2"
    description="%Transformation.description.classtotext.console"
     document="doc/classToTextToConsole.html"
     targetModelType="None"
    id="com.ibm.xtools.transform.samples.classtotext.console.root">
    </Transformation>
    

    Transformation的主要属性:

    Id注册在模型转换框架中唯一的id。可以通过这个id引用这个模型转换
    name模型转换的名字,也即最终显示在Transformation弹出菜单中菜单项的内容
    sourceModelType源模型的类型,如UML2、Resource等等。本例选择UML2。
    targetModelType目标模型的类型,由于本例是将源模型中类的信息输出到控制台,所以选择None
    transformGUI自行定制的配置界面的实现类名。如果不指定,则转换框架会提供一个缺省的界面
    document该模型转换相关文档存放的位置

    4.5 为transformation定义规则

    每个模型转换都由若干转换规则组成,每条规则实现了逻辑上相对独立的一部分转换功能,若干条规则组合在一起实现整个转换功能。转换规则的实现类应该实现模型转换框架中提供的com.ibm.xtools.transform.core.AbstractRule接口,一般需要实现如下两个方法:

    createTarget从转换上下文对象中获取源模型对象,生成目标模型中相应的对象
    canAccept该方法会在执行转换规则之前调用,验证源模型,通过验证则调用规则,否则执行引擎会忽略该规则。

    本例定义了4条转换规则:ClassRule、OperationRule、ParameterRule、PropertyRule,分别操作源模型中的类、操作、参数和属性对象,并输出信息到控制台。

    转换规则需要在模型转换实例初始化的时候添加:

    UML2Package uml2 = UML2Package.eINSTANCE;
    addByKind(uml2.getClass_(), new ClassRule());
    addByKind(uml2.getProperty(), new PropertyRule());
    addByKind(uml2.getOperation(), new OperationRule());
    addByKind(uml2.getParameter(), new ParameterRule());
    

    这段代码将4条规则添加到模型转换中,并指定每条规则所能够接收的源模型对象的类型。

    4.6 运行测试

    至此,Model-to-Text转换已经编写完成,下面开始运行测试。由于模型转换本身是一个Eclipse插件工程,所以必须从RSA中再启动一个RSA Workbench实例,将Model-to-Text插件加载运行。

    workbench启动之后,切换到modeling视图,选择Modeling菜单->Transform->Configure Transformations,打开模型转换配置界面,可以看到,Model-to-Text转换已经注册到模型转换框架中:

    图4 Model-to-Text转换已经注册到模型转换框架中
    图4 Model-to-Text转换已经注册到模型转换框架中

    创建一个UML模型,新建一个类Account,并添加一些属性和操作:

    图5 Account类
    图5 Account类

    然后在Account类上右击,从弹出菜单中选择Transformations -> Class to Text -> console,开始执行转换。该转换接收Account类作为源模型对象,将Account类的信息输出到控制台:

    Class: Account
    	Attribute: id
    	Attribute: balance
    	Operation: setID
    		Parameter: id
    	Operation: getID
    		Parameter: ReturnResult
    	Operation: setBalance
    		Parameter: balance
    	Operation: getBalance
    		Parameter: ReturnResult
    

    在模型编辑器中创建一个Component,作为输入再次执行Model-to-Text转换,由于Component不能通过源模型认证,因此模型转换框架在配置界面上报告错误:

    图6 源模型对象验证错误
    图6 源模型对象验证错误

    如果需要对模型转换进行调试,则需要以Debug模式启动RSA Workbench,之后便可以在源代码中设置断点,进行调试。

    5 扩展模型转换

    RSA模型转换框架提供了良好的可扩展性,用户可以根据具体的应用需求,在已有模型转换的基础上增加一些转换规则进行扩展。例如,RSA预装的UML2Java转换是一种基础性的转换,在实际应用中,可以对其进行扩展,在生成的Java代码中增加一些版权声明。

    一般来说,一个模型转换通常会根据功能划分成若干个转换,每个转换由若干规则组成,实现一部分独立的功能,例如,UML2JAVA总的转换程序为UML2JavaTransform,按照转换的不同阶段,划分为PropertyTransform,OperationTransform等等,在这些转换之间,通过转换上下文共享数据。这样,在对其进行扩展的时候,需要根据具体的需要,在某些阶段的模型转换中增加转换规则,并通过转换上下文共享数据。

    对于一个已有的模型转换,可能存在很多扩展,在转换执行的时候,所有的这些扩展转换规则都会被加载执行,那么用户如何只选择执行他们需要的扩展规则,而忽略掉其他扩展规则呢?在模型转换的开发过程中,一般使用UML Profile来解决这个问题。在扩展模型转换的时候,开发者往往会提供一个UML Profile,用户使用这个profile中定义的stereotype对需要执行扩展转换规则的源模型对象作标记,扩展转换规则会根据特定的stereotype来过滤源模型对象,选择执行还是不执行。

    本节以一个具体的例子来介绍如何基于RSA模型转换框架扩展系统已有的模型转换。本例将扩展UML2JAVA转换,扩展之后,用户对UML模型执行UML2JAVA的时候,扩展规则会为Class中的属性添加Get和Set方法,对于只读属性,只添加Get方法。这个扩展的实现包括两条扩展规则和一个UML profile,profile提供了一个stereotype,用户在源模型中对需要生成get和set方法的属性上应用这个stereotype。DefineGetterSetterRule添加到UML2JAVA转换中的PropertyTransform,用于收集源模型中需要生成get和set方法的属性,并存放到转换上下文中,CreateGetterSetterRule添加到ClassTransform中,该规则从转换上下文中获取DefineGetterSetterRule之前设置的属性信息,创建对应的get和set方法,如果属性只读,则只创建get方法。

    下面介绍主要的开发步骤:

    5.1 新建插件工程

    模型转换的扩展也必须实现为Eclipse的插件。

    5.2 创建UML Profile

    关于如何创建UML Profile,请读者参阅RSA相关的帮助文档。本例所用profile的id为GetSetProfile,包含一个名为GetterAndSetter的stereotype,扩展自Property:

    图7 GetSetProfile
    图7 GetSetProfile

    5.3 实现扩展规则

    DefineGetterSetterRule的实现:

    public boolean canAccept(ITransformContext context) {
      // 本规则接受的源模型对象为应用了GetterAndSetter Stereotype的Property
    if (context.getSource() instanceof Property){
    	  Property attribute = (Property)context.getSource();
    	  if(attribute.getAppliedStereotype(GETTER_SETTER_STEREOTYPE) != null){
    	    return true;
    	  }
    }
    return false;
    }
    protected Object createTarget(ITransformContext context) {
      /**
       * 记录需要处理的property的信息,并存放在转换上下文中,供CreateGetterSetterRule 使用
       */
    Object[] propertyInfo = new Object[2]; 
    propertyInfo[0] = context.getSource();
    propertyInfo[1] = context.getTarget();
    List properties = (List)context.getPropertyValue("createGetAndSet");
    if (properties == null){
    ITransformContext classContext = context.getParentContext();
    List list = new ArrayList();
    	list.add(propertyInfo);
    	classContext.setPropertyValue("createGetAndSet",list);
    }else{
    properties.add(propertyInfo);
    }
    return propertyInfo[1];
    }

    CreateGetterSetterRule的实现请参考本文附带的代码。

    5.4 扩展UML2JAVA转换

    为了扩展模型转换,需要从com.ibm.xtools.transform.core.transformationExtensions扩展点进行扩展,将之前实现的转换规则添加到需要被扩展的模型转换中。本文以DefineGetterSetterRule为例,介绍如何添加扩展规则:

    <TransformationExtension
    version="1.0.0"
      name="DefineGetterAndSetter"
      enabled="true"
    targetTransformation="com.ibm.xtools.transform.uml2.java.internal.UML2JavaTransform" 
    id="com.ibm.xtools.transform.samples.uml2java.getterSetter.extension.defineOperation">
    <RuleDefinition
    name="DefineGetterAndSetterOperation"
    class="com.ibm.xtools.transform.samples.uml2java.getterSetter.DefineGetterSetterRule"
    id="com.ibm.xtools.transform.samples.uml2java.getterSetter.DefineGetterSetterRule">
    </RuleDefinition>
    <ExtendTransform
       targetTransform="com.ibm.xtools.transform.uml2.java.internal.PropertyTransform">
       <AddRule
    id="com.ibm.xtools.transform.samples.uml2java.getterSetter.DefineGetterSetterRule">
       </AddRule>
    </ExtendTransform>
    </TransformationExtension>
    

    targetTransformation属性指定了被扩展的模型转换,RuleDefinition定义了扩展规则,ExtendTransform则将扩展规则添加到具体的转换中。

    5.5 运行测试

    至此,对UML2JAVA转换的扩展实现已经完成,从RSA再启动一个RSA Workbench实例,加载扩展转换插件。创建一个UML模型,在模型中创建Account类:

    图8 Account类
    图8 Account类

    Account类包含四个属性,其中password和balance属性应用了GetterAndSetter Stereotype,在转换之后会生成对应的Get和Set方法,id属性也应用了GetterAndSetter Stereotype,但id为只读属性,因此在转换之后只会生成对应的Get方法。userName没有应用这个Stereotype,则不会生成Get和Set方法。以Account类为源模型,执行UML2JAVA转换,生成Java程序:

    由生成的代码可以看出,在转换框架执行UML2JAVA转换的时候,会将两条扩展规则加载执行。

    6 总结

    模型转换在模型驱动的开发过程中起着重要的作用。RSA提供了一个功能强大、易于扩展的模型转换框架。该框架是一个基于规则的执行引擎,模型转换的开发者只需将转换定义为一系列转换规则即可,模型转换框架会负责加载执行规则,并提供统一的模型转换配置管理,在很大程度上简化模型转换的开发过程。

    参考资料

     


     

    展开全文
  • MDA 的一个重要思想就是将系统模型划分成与平台无关的模型( PIM) 和与平台有关的模型( PSM) , 并建立这两种模型之间的映射关系。给出了一个属于PIM 的Domain Model 和一个属于PSM 的Application Model, 并基于J2EE ...
  • 模型转换语言ATL使用感想

    千次阅读 2012-03-28 15:08:48
    ATL是ATLAS转换语言的简称,它是ATLAS研究组开发出来的一种符合OMG的一个QVT提案的模型转换语言。目前ATL已经实现为ADT的一个组成部分,ADT是一个Eclipse插件,他是著名的Eclipse项目GMT的子项目。ATL是基于EMF...
  • MDA 的一个重要思想就是将系统模型划分成与平台无关的模型(PIM) 和与平台有关的模型(PSM),并建立这两种模型之间的映射关系。给出了一个属于PIM的Domain Model 和一个属于PSM的Application Model,并基于J2EE ...
  • 近期基于hikey970的深度学习模型开发过程中,在华为开发者联盟中下载了 DDK,但解压出来的包中发现部分文件缺失。 因而再次搜罗了一下,找到了tools等文件。 注:在V300的版本中是包含了tools_omg的,不知道...
  • 纵观过去写的文章,我觉得最有...像这种可能比较小众但是能确确实实帮到其他人的文章,对我来说价值才是最大的,而这次写的关于Atlas500的模型转化以及算子的修改也是如此。 首先来谈一下模型如何进行转化,第一步...
  • MDA是以模型为中心的,模型是其基本元素,所以关于模型的存储、查询、表现和转换是对于基本元素的操作。OMG已经有一些既定规范或者正在制订的规范是用来讨论这些问题的。例如XMI规范规定了模型的存储格式、QVT规范...
  • 最新版 OMG ( MOF Query View Transformation Specification Version 1.3 是MDA关键技术之一,用于模型转换
  • OMG 接口定义语言

    千次阅读 2014-09-03 19:13:47
    IDL是一种规范语言。它允许我们从实现中剥离对象的规范...OMG IDL 看上去很像 C语言。这就很容易将这两种语言以及它们的关键字做比较。但是,这种相似性只流于表面。每种语言的目的是完全不同的。我们在讨论这种语言
  • 该方法基于模型转换,并使用标准的众所周知的技术和开源工具对SCS进行建模和分析。 更具体地说,使用OMG的标准系统建模语言SysML建模的系统会自动转换为故障树(FT)模型,可以使用现有的FT工具进行分析。 建议的...
  • 转载自 OMG!又一个频繁FullGC的案例 将用户已安装APP数据从MySQL中迁移到MongoDB中。MySQL中存储方式比较简单,每个用户每个已安装的APP一行记录,且数据模型对应AppFromMySQL。迁移到MongoDB中,我们想更好的...
  • OMG CORBA构件技术体系(1)

    千次阅读 2002-10-25 16:24:00
    OMG CORBA构件技术体系作者: 贾育email: jia_yu@263.net [版权所有 任何形式的拷贝和引用必须得到作者的许可] 1. CORBA概述 对象管理集团OMG (Object Management Group)是一个以制定实用的分布式对象技术...
  • namespace.id Spring有几种方式定义Bean git你用过哪些命令 linux怎么在查找目录下的一个文件 redis基本数据结构 redis单线程模型 事务的四大特性 解释线程池的三个主要参数 具体的问项目中线程池如何使用的(这里...
  • OMG Anaheim会议消息:四个新的小组成立;架构委员会选举 [2004/2/17] NEEDHAM, Mass.--国际对象管理集团(OMG(TM)) 成员12月2日到5日在Anaheim, CA召开会议,会议由Compuware公司赞助...
  • 与传统的UML模型相比,MDA能够创建出机器可读和高度抽象的模型,这种模型通过转换(Transformation)技术可自动转换为代码、测试脚本、数据库定义以及各种平台部署描述。 1MDA概述 模型驱动架构(MDA)是OMG提出的新...
  • 模型驱动架构

    2007-07-06 05:49:53
    模型驱动架构(Model Driven Architecture,MDA)是由OMG定义的一个软件开发框架。它是一种基于UML以及其他工业标准的框架,支持软件设计和模型的可视化、存储和交换。和UML相比,MDA能够创建出机器可读和高度抽象的...
  • 模型驱动开发

    千次阅读 2004-03-01 10:01:00
    OptimalJ:模型驱动开发如何提高生产力介绍:OptimalJ是一个高级的企业...使用OMG模型驱动架构标准,OptimalJ帮助简化开发,使架构师、设计人员和开发人员快速开发可靠的应用系统。OptimalJ以五个关键基础概念为特性
  • 最近几个月,许多组织开始将注意力集中在模型... 正如对象管理组(OMG)定义的那样,MDA是一种组织和管理由自动化工具和服务支持的企业体系结构的方法,用于定义模型和促进不同模型类型之间的转换。 尽管OMG定义...
  • 问题:什么是模型驱动的体系MDA?模型驱动的体系MDA是什么...模型驱动的体系 MDA(Model Driven Architecture)是由全球最大的软件工业标准化组织OMG(Object Manage Group)提出,MDA是经过OMG推出CORBA、OOP、UML等标
  • MDA(模型驱动架构)

    2010-09-09 13:27:44
    MDA(Model Driven Architecture)是模型驱动架构,它是由OMG定义的一个软件开发框架。它是一种基于UML以及其他工业标准的框架,支持软件设计和模型的可视化、存储和交换。和UML相比,MDA能够创建出机器可读和高度抽象...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 6,139
精华内容 2,455
关键字:

omg模型转换