本文的意图是使用Rodin和Event-B建模语言完成一个小型系统的开发,该例子来源于Jean-Raymond Abrial所编著的书,笔者主要是使用Rodin工具尝试完成了这个例子的建模,现将Rodin工具的建模过程一步一步描述如下(应老板要求,只能用英文总结了)。
Requirements Document
The system we are going to build is a piece of software, called the controller, connected to some equipment, called the environment. There are thus two kinds of requirements: those concerned with the functionalities of the controller labeled FUN, and those concerned with the environment labeled ENV.
Note that the model we are going to build is a closed model comprising the controller as well as its environment. The reason is that we want to define with great care the assumptions we are making concerning the environment. In other words, the controller we shall build eventually will be correct as long as these assumptions are fulfilled by the environment: outside these assumptions, the controller is not guaranteed to perform correctly. We shall come back to this in section 7.
Let us now turn our attention to the requirements of this system. The main function of this system is to control cars on a narrow bridge. This bridge is supposed to link the mainland to a small island.
FUN-1: The system is controlling cars on a bridge connecting the mainland to an island
This system is equipped with two traffic lights.
ENV-1: The system is equipped with two traffic lights with two colors: green and red
One of the traffic lights is situated on the mainland and the other one on the island. Both are close to the bridge.
ENV-2: The traffic lights control the entrance to the bridge at both ends of it
Drivers are supposed to obey the traffic lights by not passing when a traffic light is red
ENV-3: Cars are not supposed to pass on a red traffic light, only on a green one
There are also some car sensors situated at both ends of the bridge
ENV-4: The system is equipped with four sensors with two states: on or off
These sensors are supposed to detect the presence of cars intending to enter or leave the bridge. There are four such sensors. Two of them are situated on the bridge and the other two are situated on the mainland and on the island respectively
ENV-5: The sensors are used to detect the presence of a car entering or leaving the bridge: "on" means that a car is willing to enter the bridge or to leave it
The pieces of equipment which have been described are illustrated in Fig. 1. The system has two main additional constraints: the number of cars on the bridge and the island is limited
FUN-2: The number of cars on bridge and island is limited
and the bridge is one-way
FUN-3: The bridge is one-way or the other, not both at the same time
Fig. 1. The Bridge Control Equipment
Initial Model: Limiting the Number of Cars
Target: The number of cars on the bridge and the island is limited (FUN-2).
We start with a very simple model allowing us to take account of requirement FUN-2 concerned with the maximum number of cars on the island and the bridge.
We start to build a model that is far more abstract than the final system we want to construct. The idea is to take account initially of very few constraints only. We can observe the situation from very high in the sky. Although we cannot see the bridge, we suppose however that we can “see” the cars on the island-bridge compound and observe the two transitions, ML_out and ML_in, corresponding to cars entering and leaving the island-bridge compound.
Fig. 2. The Mainland and the Island-Bridge
Project Setup: Models typically consist of multiple files that are managed in a project. Create a new Event-B Project File New Event-B Project. Give the project the name ch2_car as shown in Fig. 3.
Fig. 3. New Event-B Project Wizard
The state of the model is made up of two parts: the static part and the dynamic part. The static part contains the definition and axioms associated with some constants. The static part is also called the context of our model. Let’s build the static part on Rodin.
Next, create a new Event-B Component. Either use File New Event-B Component or right-click on the newly created project and select New Event-B Component. Use cd as the component name, select Context as component-type, and click Finish as shown in Fig. 3. This will create a Context file.
Fig. 4-1. New Event-B Component Wizard
Fig. 4-2. Initial context file
You can also add elements by finding the name of the context under the CONTEXT heading. There is a small green arrow directly to the right of the name of the machine (in this case, the name of the machine is “m0"). Place your cursor directly to the left of the green arrow and right-click. Select the element that you would like to add from the Add Child menu. If an element of a certain type has already been created, you can also create more elements of that type by right-clicking on the type of the element you would like to add (e.g. VARIABLES) that is colored in purple and select Add Child.
Fig. 5. Add Constant
The context of our first model is very simple. In contains a single constant d, which is a natural number denoting the maximum number of cars allowed to be on the island-bridge compound at the same time.
Fig. 6. After adding a constant d
The constant d has a simple axiom, axiom: it is a natural number. We can give this axiom the name axm1. And we can also add axm2, which indicates the number of cars on the bridge and island is positive.
axm1: d∈ℕ
axm2: d>0
Again, we can add an axiom by finding the name of the context under the CONTEXT heading.
Fig. 7. Add axiom
And here is a quick look after adding two axioms, which were labeled ‘not theorem’, as shown in Fig. 8.
Fig. 8. After adding axm1 and axm2
Next, Let’s build the dynamic part. Create another Event-B Component. Either use File New Event-B Component or right-click on the newly created project and select New Event-B Component. Use m0 as the component name, select Machine as component-type, and click Finish as shown in Fig. 9. This will create a Machine file.
Fig. 9. New Event-B Machine Wizard
Here is the screenshot of the Initial machine file, as shown in Fig. 10.
Fig. 10. Initial Machine File
After creating the machine file. We need to add the relationship between machine file and context file and connect machine file m0 to context file cd.
Fig. 11. Add see Context Relationship
The dynamic part is made up of a single variable n, which denotes the actual number of cars in the island-bridge compound at a given moment. This is simply written as shown is the following invariants.
inv1: n∈ℕ
inv2: n ≤ d
So, we can add a variable n and two invariants in our Rodin project. Here is a screenshot of our machine file, as shown in Fig. 12.
Fig. 12. After adding variables and invariants
After saving, we should see that the INITIALISATION event is underlined in yellow with a small warning sign to the left as demonstrated. Again, the Rodin Problems view displays the error message: “Variable n is not initialized”. Every variable must be initialized in a way that is consistent with the model. To fix this problem, place your cursor to the left of the small green arrow next to the label INITIALISATION. Right-click and add an Event-B Action. Repeat to add another event. In the action fields, enter n := 0.
Fig. 13. Initialization
Our current model cannot yet change its state. To make this possible, we create two events in the manner described above and name them ML_in (the name ML_in stands for “going into mainland”) and ML_out (the name ML_out stands for “going out of mainland”). As can be seen, the number of cars in the compound is incremented as a result of these events:
Fig. 14. Event ML_in and ML_out
From now on, we won’t describe the individual steps in the editor anymore. Instead, we will simply show the resulting model, as shown in Fig. 15.
Fig. 15. Adding Event ML_in and ML_out into Rodin Project
Rodin will automatically generate proof obligation rule per event and per invariant.
Note that a proof obligation is something that has to be proven to show the consistency of the machine, the correctness of theorems, etc. A proof obligation consists of a label, a number of hypotheses that can be used in the proof and a goal – a predicate that must be proven. Have a look at the proof obligation labels. They indicate the origin in the model where they were generated. E.g. ML_out/inv1/INV is the proof obligation that must be verified to show that the event ML_out preserves the invariant (INV) with the label inv1. Obviously, this invariant can be violated, and Rodin informs us of this. The Event-B Explorer provides this information in various ways. Go the explorer and expand the project (ch2_car), the machine (m0) and the entry “Proof Obligations”. You should see six proof obligations, two of which are discharged (marked with yes) and four of which are not discharged (marked with ?), as shown in Fig. 16.
Fig. 16. Proof Obligations corresponding to Fig. 15
Note: To use Proof Obligations with external prover, we have to install Atelier B Provers. Use Help Install new software and select Atelier B Provers, as shown in Fig. 17.
Fig. 17. Install external prover
These two Proof Obligations cannot be proofed successfully: ML_out/inv2/INV and ML_in/inv1/INV. One needs to proof n+1≤d, another need to proof n-1∈ℕ, as shown in Fig. 18.
Fig. 18. The Goal of ML_out/inv2/INV and ML_in/inv1/INV
To prevent the invariant from being violated (and therefore to allow all proof obligations to be discharged), we need to strengthen the guards of the events. For event ML_out to be enabled, we shall require that n be strictly smaller than d, that is n < d. And for event ML_in to be enabled, we shall require that n be strictly positive, that is 0 < n. Adding guards to events is done as Fig. 19.
Fig. 19. After adding Guards
As can be seen, we have a simple piece of syntax here: the guard is situated between the keywords where and then, whereas the action is situated between the keywords then and end.
Since our two main events, ML_in and ML_out are now guarded, it means that our model might deadlock when both guards are false: none of the events would be enabled, the system would be blocked. To prevent the deadlock, we need to prove a theorem n>0 ∨ n<d. The model contains three INVARIANTS now, as shown in Fig. 20.
Fig. 20. Adding inv3 to prove the deadlock freedom
We can see a newly generated proof obligation thm1/THM, and it's not automatically proven to be correct, as shown in Fig. 21.
Fig. 21. The view of proof obligation
We can double click the thm1/THM and apply external prover to this proof obligations. After selecting “pp External prover: Predicate prover on all hypotheses” and save the project. thm1/THM can be automatically proved.
Fig. 22. Applying external prover pp
Finally, we can all proof obligations are discharged (marked with), as shown in Fig. 23.
Fig. 23. Adding inv3 to prove the deadlock freedom
As a whole, the initial model (context + machine) design by Rodin is shown as follows.
Context cd (final version):
CONTEXT
cd ›
CONSTANTS
⚬ d ›
AXIOMS
⚬ axm1: d∈ℕ not theorem ›
⚬ axm2: d>0 not theorem ›
END
Machine m0 (final version):
MACHINE
m0 ›Initial machine
SEES
⚬ cd
VARIABLES
⚬ n ›number of cars
INVARIANTS
⚬ inv1: n∈ℕ not theorem ›number of cars
⚬ inv2: n≤d not theorem ›
⚬ thm1: n>0 ∨ n<d theorem ›
EVENTS
⚬ INITIALISATION: not extended ordinary ›
THEN
⚬ act1: n≔0 ›
END
⚬ ML_out: not extended ordinary ›Leaving the mainland
WHERE
⚬ grd1: n<d not theorem ›
THEN
⚬ act1: n≔n+1 ›One more car in the island-bridge
END
⚬ ML_in: not extended ordinary ›Entering the mainland
WHERE
⚬ grd1: n>0 not theorem ›Possible only when some car is in the island/bridge
THEN
⚬ act1: n≔n−1 ›One car less in the island-bridge
END
END