使用复杂的比较规则通过 EML 合并项目模型

Merge Project models through EML using complex comparison rules

提问人:James 提问时间:7/18/2023 最后编辑:howlgerJames 更新时间:7/21/2023 访问量:48

问:

问题:

我正在使用 EML 和 Epsilon Language Workbench 将两个项目模型(由两个元模型(MM1 和 MM2)表示)合并到第三个元模型(目标)中。虽然我可以根据元素名称实现简单的合并,但我需要一个更复杂的规则来根据某些条件将第二个模型 (M2) 中的任务分配给第一个模型 (M1) 中的人员。

预期结果:

对于 M1 中人员 p 的每个实例,仅当 p 已经在 M1 中处理了 2 个以上的任务时,我才想在 M2 中分配任务 t

当前尝试:

我已经创建了 Epsilon 程序(program.eml 和 program.ecl)来执行合并,并且我正在使用 Epsilon playground 进行测试。我的例子被改编为与操场上标准使用的非常相似。

元模型:

  • MM1 (left.emf)

  • MM2 (右.emf)

  • 目标元模型 (target.emf)

模型:

- M1 (left.flexmi)
- M2 (right.flexmi)

厄普西隆程序:

- program.eml
- program.ecl

元模型 1 - MM1 (left.emf):

@namespace(uri="psl", prefix="")
package psl;

class Project {
    attr String title;
    attr String description;
    val Task[*] tasks;
    @diagram(direction="right")
    val Person[*] people;
}

class Task {
    attr String title;
    attr int start;
    attr int duration;
    @diagram(direction="right")
    val Effort[*] effort;
}

class Person {
    attr String name;
}

class Effort {
    @diagram(direction="up")
    ref Person person;
    attr int percentage = 100;
}

元模型 2 - MM2 (right.emf)

@namespace(uri="psl", prefix="")
package psl;

class Project {
    attr String title;
    attr String description;
    val Task[*] tasks;
    @diagram(direction="right")
    val Person[*] people;
}

class Task {
    attr String title;
    attr int start;
    attr int duration;
    @diagram(direction="right")
    val Effort[*] effort;
}

class Person {
    attr String name;
}

class Effort {
    @diagram(direction="up")
    ref Person person;
    attr int percentage = 100;
}

型号 1 - M1 (left.flexmi)

<?nsuri psl?>
<project title="ACME">
    <person name="Alice"/>
    <person name="Bob"/>
    <task title="Analysis" start="1" dur="3">
        <effort person="Alice"/>
    </task>
    <task title="Design" start="4" dur="6">
        <effort person="Bob"/>
    </task>
    <task title="Implementation" start="7" dur="3">
        <effort person="Bob" perc="50"/>
        <effort person="Alice" perc="50"/>
    </task>
</project>

型号 2 - M2 (right.flexmi)

<?nsuri psl?>
<project title="ACME">
    <person name="Alice"/>
    <person name="Bob"/>
    <task title="Testing" start="10" dur="3">
        <effort person="Alice" perc="50"/>
    </task>
</project>

目标元模型 (target.emf)

package psl;

class Project {
    attr String title;
    attr String description;
    val Task[*] tasks;
    @diagram(direction="right")
    val Person[*] people;
}

class Task {
    attr String title;
    attr int start;
    attr int duration;
    @diagram(direction="right")
    val Effort[*] effort;
}

class Person {
    attr String name;
}

class Effort {
    @diagram(direction="up")
    ref Person person;
    attr int percentage = 100;
}

程序.eml

// This EML program merges two 
// project plan models as follows:
// - Persons are merged based on name
// - Tasks are not merged

// Matched projects are merged
// into a single project
rule ProjectWithProject
    merge l : Left!Project
    with r : Right!Project
    into m : Merged!Project {

    m.title = l.title;
    m.people ::= l.people + r.people;
    m.tasks ::= l.tasks + r.tasks;
}

// Matched persons are merged
// into a single person
rule PersonWithPerson
    merge l : Left!Person
    with r : Right!Person
    into m : Merged!Person {

    m.name = l.name;
}

// Tasks are not merged
// They are copied from the left
// and the right model to the 
// merged model
rule TaskWithTask
    transform s : Source!Task
    to t : Target!Task {

    t.title = s.title;
    t.start = s.start;
    t.duration = s.duration;
    t.effort ::= s.effort;
}

//merge efforts in the task ONLY when the people in the right worked in at least one task (i.e. has effort) in the left
rule EffortWithEffort
    merge l : Left!Effort
    with r : Right!Effort
    into m : Merged!Effort {
    
    m.person ::= l.person;
    m.percentage = l.percentage;
}

// Persons and Efforts found in only one of the
// two models are copied across
// to the merged model
rule Person2Person 
    transform s : Source!Person
    to t : Target!Person {

    t.name = s.name;
}

rule Effort2Effort
    transform s : Source!Effort
    to t : Target!Effort {

    t.person ::= s.person;
    t.percentage = s.percentage;
}

程序.ecl

// We match persons by name
rule PersonWithPerson
    match l : Left!Person
    with r : Right!Person {

    compare: l.name = r.name
}


rule EffortWithEffort
    match l : Left!Person
    with r : Right!Person {

    compare: l.tasks->collect(e | e.effort)
            ->flatten()
            ->excluding(l)
            ->collect(e | e.effort)
            ->flatten()
            ->count(r) >= 1
}

// We expect only one project 
// in each model and therefore
// we match them unconditionally
rule ProjectWithProject
    match l : Left!Project
    with r : Right!Project {
    
    compare: true
}

我尝试使用现有规则,但它们没有达到预期的结果。如何修改 Epsilon 程序以获得预期的合并模型?

任何帮助或建议将不胜感激!

合并 OCL epsilon 模型驱动开发 MDE

评论


答:

1赞 Dimitris 7/21/2023 #1

按如下方式更新转换规则应该可以解决问题:Task2Task

// Tasks are not merged
// They are copied from the left
// and the right model to the 
// merged model
rule Task2Task
    transform s : Source!Task
    to t : Target!Task {

    t.title = s.title;
    t.start = s.start;
    t.duration = s.duration;
    
    // Persons participating in 2+ tasks
    // are assigned to all tasks in the model
    for (p in Source!Person.all) {
        if (Source!Task.all.select(st|st.effort.exists(e|e.person = p)).size() >= 2) {
            var e = new Merged!Effort;
            t.effort.add(e);
            e.person = p.equivalent();
        }
    }
}

更新代码的可运行版本如下:https://eclipse.dev/epsilon/playground/?53c08479

评论

0赞 James 7/25/2023
您知道是否可以在不遍历所有 Person 实例的情况下执行相同的任务吗?我认为对于大型模型来说会很慢,对吧?
1赞 Dimitris 7/26/2023
您可以将人员映射到预块中的任务,然后使用映射在规则中进行查找。