提问人:James 提问时间:7/18/2023 最后编辑:howlgerJames 更新时间:7/21/2023 访问量:48
使用复杂的比较规则通过 EML 合并项目模型
Merge Project models through EML using complex comparison rules
问:
问题:
我正在使用 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 程序以获得预期的合并模型?
任何帮助或建议将不胜感激!
答:
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
您可以将人员映射到预块中的任务,然后使用映射在规则中进行查找。
评论