在之前我们完成了几个有关Event-B建模的任务,这一次我们要学习一种新的建模思路或者说是方式,即通过设计设计模式,先将动作建模为抽象的模型,在实际模型中需要建模时,将抽象的模型转换为具体的模型。
需求分析
这一次我们要建一个冲压机的模型
系统的整体结构如下图所示:
该系统的需求为:
首先定义了各种部件,电动机、离合器和安全门,中间有一个控制器控制着它们
然后给出各个部件与控制器的连接方式
定义系统的安全需求:
最后离合器和安全门之间要增加更多的约束
强同步与弱同步
我们在需求这里看到了弱同步(weakly synchronized)和强同步(strongly synchronized),这是对于这个系统很重要的概念
我们首先在这一节中实现抽象的弱同步以及抽象的强同步,这样当我们对系统进行建模的时候,我们只要对构建的抽象交互模型进行实例化就可以了,这非常符合软件工程的思想。
那么啥叫弱同步啥叫强同步呢?
首先从弱同步开始
弱同步
现在有一个称为a的动作,用实线表示,然后有一个称为r的反应动作,用虚线表示,上升状态代表该动作处于执行状态,下降状态代表该动作处于关闭状态,看到下面的图形,a的动作执行关闭了三次,而r的动作只执行关闭了一次,但是如果发生了a的动作执行关闭,最终总会发生r的执行关闭(也就是说a可以执行关闭好几次后,r才执行关闭一次),那么我们可以这种反应模式为弱同步。
我们要对上述弱同步的动作执行模式进行建模,首先要有两个变量a和r,用0和1分别代表上升状态和下降状态,然后引入两个变量ca和cr,这两个变量是两个计数器,分别代表a和r上升的次数,显而易见,必须有
c
r
≤
c
a
cr\le ca
cr≤ca,因为r是随着a的上升而上升的,所以r的上升次数肯定不能大于a
开始建模,新建一个工程,对状态进行一下初始化
然后是定义新的事件
a_on:a执行,a的值变成1,然后计数器加1,
a_off:a关闭,a从1变成0
r_on:a为1且r为0的时候才能执行,r变为1,然后cr计数器加1
r_off:a为0且r为1的时候才能执行,r变为0,
弱同步事件中存在着依赖关系,如下图所示,箭头表示一个事件的发生依赖于另外某些事件的发生
强同步
然后建模强同步,如果发生了a的动作执行关闭,立即发生r的执行关闭,也就是说a动作上升的次数最多比r执行次数多1,即
c
a
≤
c
r
+
1
ca\le cr+1
ca≤cr+1
强同步的模型可以算是弱同步模型的一个精化,所以我们精化一下原有的模型
变量仍然不变,不过多了两条不变式约束
然后a动作执行时需要新的约束
精化策略
初始模型:连接控制器和电动机
第一次精化:连接电动机按钮和控制器
第二次精化:连接控制器和离合器
第三次精化:建立离合器和电动机之间的约束
第四次精化:连接控制器和安全门
第五次精化:建立离合器和安全门之间的约束
第六次精化:建立离合器和安全门之间的更多约束
第七次精化:连接离合器按钮和控制器
初始模型
在初始模型中,我们要先完成连接控制器和电动机
在需求中我们知道,控制器和各个部件之间是强同步关系
初始模型如图所示:
我们用变量motor_actuator表示电动机到控制器的连接,用变量motor_sensor表示控制器到电动机的连接,其实呢,现在完全可以按照上面的强同步设计模型,进行一对一的映射
在初始模型中,我们构建如下的映射关系
设计模式(抽象模型) | 具体模型 |
---|---|
a | motor_actuator |
r | motor_sensor |
0 | stopped |
1 | working |
a_on | treat_start_motor |
a_off | treat_stop_motor |
r_on | motor_start |
r_off | motor_stop |
看到了没有?其实我们只要把上面构建的抽象模型简单地更改一下变量的值,马上就变成了我们需要的模型了,这就是先一步设计设计模型的好处,就是这个照搬照抄的过程有点费力,或许可以通过编写一款插件来解决这个问题。下面的工作完全就是一个照搬照抄的过程了,没有什么特别的,唯一不同的地方是把计数器相关的内容去掉,因为这个具体模型没有提到计数器
首先我们定义一个上下文
然后是machine
第一次精化
按照计划,第一次精化这里,我们要建模电动机按钮和控制器,我们部分地考虑了需求FUN_1
这里说明,控制器对按钮是弱反应,也就是说,有可能按钮被按下了好几次,控制器还没有反应,我们记按钮按下的动作为start_motor_button,这个动作就是弱同步中的a了,反馈r定义为start_motor_impluse,代表按钮的物理上的回馈
我们同样构建一个映射表
设计模式(抽象模型) | 具体模型 |
---|---|
a | start_motor_button |
r | start_motor_impluse |
0 | FALSE |
1 | TRUE |
a_on | push_start_motor_button |
a_off | release_start_motor_button |
r_on | treat_start_motor |
r_off | treat_release_start_motor_button |
这样就完成了按下电动机启动按钮的弱同步映射,按照弱同步的设计模型一一照搬就行
新建mac2精化mac1
有关r_on这里的映射还是要注意一下,按下按钮的回馈反应是准备启动电动机,也就是将会执行初始模型中的treat_start_motor这个事件,我们把r_on的映射定义为treat_start_motor的精化事件,它也要同时满足初始模型中的约束
建模完了按下start按钮后的事件,然后开始建模按下stop按钮后的事件,和上面完全相同
同样,treat_push_stop_motor_button这边要重点处理一下
至此,第一次精化完成
第二次精化
第二次精化要把控制器接上离合器,这一步和初始模型中构建控制器和电动机是一模一样的
只不过我们把电动机的motor改成clutch离合器
设计模式(抽象模型) | 具体模型 |
---|---|
a | clutch_actuator |
r | clutch_sensor |
0 | disengaged |
1 | engaged |
a_on | treat_start_clutch |
a_off | treat_stop_clutch |
r_on | clutch_start |
r_off | clutch_stop |
先新建一个context ct2
再在machine里写上新的模型
两个强反应的弱同步
第三次精化
第三次精化中,我们考虑需求
为此我们添加下列不变式
说白了就是根据需求正着写两个条件,然后再写两个等价的逆反条件
然后再在两个地方添加上约束,这个约束完全按照上面的需求来,同样一个相同的条件,再加一个等价逆反条件
第三次精化就这样很简单的完成了
第四次精化
参考初始模型,只要将motor相关名字改成door就行
第五次精化
第五次精化中,我们要考虑需求:
不过我们研究后发现,其实有个关于安全门的需求需要添加上:
当电动机停止时,安全门必须是打开的 SAF_3
然后我们发现SAF_2和SAF_3结合起来就隐含了SAF_1的需求,所以我们完全可以删除第三次精化的内容,而只添加有关SAF_2和SAF_3相关的内容
SAF_2和SAF_3可以照着之前第三次精化照搬过来
(这件事情告诉我们,模型也是在建模过程中不断变化的,因为刚开始设计的时候不可能一蹴而就设计一个完美的模型,例如这里就精简了一次精化操作)
两个强反应的强同步
第六次精化
借用前面的两个强反应的强同步设计模式构建模型
第七次精化
完全参考第二次精化,把motor替换成clutch即可
总结
虽然这里长长长过程的建模,但是其实要学的点就只有一个,那就是利用设计模型来辅助我们构建Event-B模型,即先设计一个抽象的模型,然后具体实例化的时候只要稍微修改一下变量名就行了,这里的这么长的建模其实就是对这个过程的练习,想必你已经理解了这一个过程。题外话:下一章的文件传输建模还是相当有用的