Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: c6a22d28bbe2d442523a39e9d775adf955512ac1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
h1. Tutorial Pedestrian Lights

h2. Scope

The scope of this tutorial is to demonstrate how to receive model messages from outside the model. Calling methods which are not part of the model is simple and you have already done this within the blinky tutorial (this is the other way round model => external code). Receiving events from outside the model is a very common problem and a very frequently asked question. Therefore this tutorial shows how an external event (outside the model) can be received from the model.

This tutorial is not like hello world or blinky. Be familiar with the basic tool features is a precondition for this tutorial. The goal is to understand the mechanism not to learn the tool features.

The idea behind the exercise is, to control a Pedestrian crossing light. We will use the same GUI as for the blinky tutorial but now we will use the ??REQUEST?? button to start a FSM, which controls the traffic lights.

!images/020-Blinky08.PNG!

The ??REQUEST?? must lead to a model message which starts the activity of the lights.

There are a view possibilities to receive external events (e.g. TCP/UDP Socket, using OS messaging mechanism), but the easiest way is, to make a port usable from outside the model. To do that a view steps are necessary:
# specify the messages (within a protocol) which should be sent into the model
# model an actor with a port (which uses the specified protocol) and connect the port to the receiver 
# the external code should know the port (import of the port class)
# the external code should provide a registration method, so that the actor is able to allow access to this port
# the port can be used from the external code

h2. Setup the model

* Use the ??New Model Wizzard?? to create a new eTrice project and name it ??PedLightsController??.
* Copy the package ??de.protos.PedLightGUI?? to your ??src?? directory (see blinky tutorial).
* Uncoment line 15 (import), 36, 122 (usage) and 132-134 (registration).
* Copy the following model to your model file:

bc.. 
RoomModel PedLightsController {

	LogicalSystem LogSys_PedLights {
		SubSystemRef application: SubSys_PedLights
	}

	SubSystemClass SubSys_PedLights {
		ActorRef PedLightsTopRef: PedLightsTop
		ActorRef timingService: room.basic.service.timing.ATimingService
		LayerConnection ref PedLightsTopRef satisfied_by timingService.timer
		LayerConnection ref PedLightsTopRef satisfied_by timingService.timeout
	}

	ActorClass PedLightsTop {
		Structure {
			ActorRef adapter: GuiAdapter
			ActorRef controller: Controller
			Binding adapter.ControlPort and controller.ControlPort
		}
		Behavior { }
	}

	ActorClass GuiAdapter {
		Interface {
			conjugated Port ControlPort: PedControlProtocol
		}
		Structure {
			usercode1 {
				"import PedLightGUI.*;"
			}
			usercode2 {
				"private PedestrianLightWndNoTcp lights = new PedestrianLightWndNoTcp(\"Pedestrian Lights\",\"  external port connection \");"
				"private TrafficLight3 carLights;"
				"private TrafficLight2 pedLights;"
			}
			external Port ControlPort
		}
		Behavior {
			Operation destroyUser() {
				"lights.closeWindow();"
			}
			StateMachine {
				Transition init: initial -> running {
					action {
						"carLights=lights.getCarLights();"
						"pedLights=lights.getPedLights();"
						"carLights.setState(TrafficLight3.OFF);"
						"pedLights.setState(TrafficLight2.OFF);"
						"lights.setPort(ControlPort);"
					}
				}
				Transition tr0: running -> running {
					triggers {
						<setCarLights: ControlPort>
					}
					action {
						"carLights.setState(state);"
					}
				}
				Transition tr1: running -> running {
					triggers {
						<setPedLights: ControlPort>
					}
					action {
						"pedLights.setState(state);"
					}
				}
				State running
			}
		}
	}

	ActorClass Controller {
		Interface {
			Port ControlPort: PedControlProtocol
		}
		Structure {
			usercode1 {
				"import PedLightGUI.*;"
			}
			external Port ControlPort
			SAP timer: room.basic.service.timing.PTimeout
		}
		Behavior {
			StateMachine {
				Transition init: initial -> off { }
				Transition tr0: off -> carsGreen {
					triggers {
						<start: ControlPort>
					}
					action {
						"timer.Start(700);"
						"ControlPort.setCarLights(TrafficLight3.GREEN);"
						"ControlPort.setPedLights(TrafficLight2.RED);"
					}
				}
				Transition tr1: carsGreen -> carsYellow {
					triggers {
						<timeoutTick: timer>
					}
					action {
						"timer.Start(700);"
						"ControlPort.setCarLights(TrafficLight3.YELLOW);"
						"ControlPort.setPedLights(TrafficLight2.RED);"
					}
				}
				Transition tr2: carsYellow -> carsRed {
					triggers {
						<timeoutTick: timer>
					}
					action {
						"timer.Start(1500);"
						"ControlPort.setCarLights(TrafficLight3.RED);"
						"ControlPort.setPedLights(TrafficLight2.GREEN);"
					}
				}
				Transition tr3: carsRed -> carsYellowRed {
					triggers {
						<timeoutTick: timer>
					}
					action {
						"timer.Start(700);"
						"ControlPort.setCarLights(TrafficLight3.YELLOW_RED);"
						"ControlPort.setPedLights(TrafficLight2.RED);"
					}
				}
				Transition tr4: carsYellowRed -> carsGreen2 {
					triggers {
						<timeoutTick: timer>
					}
					action {
						"timer.Start(700);"
						"ControlPort.setCarLights(TrafficLight3.GREEN);"
						"ControlPort.setPedLights(TrafficLight2.RED);"
					}
				}
				Transition tr5: carsGreen2 -> off {
					triggers {
						<timeoutTick: timer>
					}
					action {
						"ControlPort.setCarLights(TrafficLight3.OFF);"
						"ControlPort.setPedLights(TrafficLight2.OFF);"
					}
				}
				State off
				State carsGreen
				State carsYellow
				State carsRed
				State carsYellowRed
				State carsGreen2
			}
		}
	}

	ProtocolClass PedControlProtocol {
		incoming {
			Message start()
		}
		outgoing {
			Message setCarLights(state: int32)
			Message setPedLights(state: int32)
		}
	}
}
bq. 

* Arrange the Structure and the Statemachines to understand the model 

!images/030-PedLights01.PNG!
The ??GuiAdapter?? represents the interface to the external code. It registers its ??ControlPort?? by the external code.

!images/030-PedLights02.PNG!
Visit the initial transition to understand the registration. The actor handles the incoming messages as usual and controls the traffic lights as known from blinky. 

!images/030-PedLights03.PNG!
The ??Controller?? receives the ??start?? message and controls the timing of the lights. Note that the ??start?? message will be sent from the external code whenever the ??REQUEST?? button is pressed.

*  Visit the model and take a closer look to the following elements:
# PedControlProtocol => notice that the start message is defined as usual
# Initial transition of the ??GuiAdapter?? => see the registration
# The ??Controller?? => notice that the ??Controller?? receives the external message (not the ??GuiAdapter??). The ??GuiAdapter?? just provides its port and handles the incoming messages.
# Visit the hand written code => see the import statement of the protocol class and the usage of the port.

* Generate and test the model
* Take a look at the generated MSC => notice that the start message will shown as if the ??GuiAdapter?? had sent it.

!images/030-PedLights04.PNG!

h2. Why does it work and why is it save?

The tutorial shows that it is generally possible to use every port from outside the model as long as the port knows its peer. This is guaranteed by describing protocol and the complete structure (especially the bindings) within the model. 
The only remaining question is: Why is it save and does not hurt the "run to completion" semantic. To answer this question, take a look at the ??MessageService.java?? from the runtime environment. There you will find the receive method which puts each message into the queue. 

bc.. 
	@Override
	public synchronized void receive(Message msg) {
		if (msg!=null) {
			messageQueue.push(msg);
			notifyAll(); // wake up thread to compute message
		}
	}
bq. 

This method is synchronized. That means, regardless who sends the message, the queue is secured. If we later on (e.g. for performance reasons in C/C++) distinguish between internal and external senders (same thread or not), care must be taken to use the external (secure) queue.



Back to the top