Skip to content

History Entries and Named Interfaces

We already discussed the default entry behavior for regions and how sub states are activated. Histories provide an extension to this mechanism and this section clarifies how history entries work, what they do, and especially what they don’t do. To illustrate this, we introduce another mode to our light switch: light sensing. When operating in light sensing mode, the light control automatically turns on the light when it is dark and turns it off when it is bright. For this purpose a luminosity sensor is used. It permanently senses the ambient brightness and fires luminosity.bright or luminosity.dark events all the time. It is out of scope how “dark” and “bright” are defined and how they are configured in the luminosity sensor. Suffice to say that the sensor is always raising events – say, multiple times per second.

As before the light switch is either operated manually or operates automatically. When in automatic mode, the light switch functions either as a twilight switch as explained above or as the already introduced motion sense switch (see the previous iteration). The user interface gets another button to toggle between the two modes via the user.mode_button event. The light switch is starting to change into a full light control module.

The statechart interface is defined as:

interface:
	var brightness: integer
interface hmi:
	in event switch
	in event toggleMode
	in event changeBrightness
interface motion:
	const timeout: integer = 30
	in event detected
interface luminosity:
	const delay: integer = 10
 	in event bright
 	in event dark

As we added a couple of additional declarations we now use named interfaces to group the different events and variables. This grouping makes the design more clear and provides a better overview. The usage is not different except for the fact that the interface name must be used when referencing its declarations.

play stop

luminosity sensor:
bright dark

motion sensor:
motion



The automatic modes are grouped into the Automatic composite state. That state is subdivided into a composite state for the twilight functionality and another one for motion sensing. We have discussed motion sensing before, but the Light Sensing composite state deserves some explanation. When the ambient brightness is changing from day to night or from night to day, the luminosity sensor doesn’t deliver consistent values. Rather small fluctuations are causing a succession of quickly alternating luminosity.dark and luminosity.bright events. Having each of these events turn the light on or off, respectively, would result in a flickering lamp for some time during twilight. To avoid this, the state machine requires a certain permanence in the luminosity sensor’s results before actually toggling the light’s state.

This is done like this: During the day the Light Sensing composite state’s Bright sub state is active. In this state the state machine listens for luminosity.dark events. These events are starting to be raised when it is about to get dark. First they come in spuriously, later there are more luminosity.dark than luminosity.bright events within a given period of time, until only luminosity.dark events are raised. The first luminosity.dark changes the state from Bright to Getting Dark. In this state we are waiting until a certain time has passed, e. g. 10 seconds, to be configured as constant luminosity.wait_time. However, each luminosity.bright event brings the state machine back to the Off. Only if the waiting time has passed without any lum_sensor.bright interfering, the Dark state is reached and the light is turned on. The analogous happens in the morning when the first light of the day is close. The twilight switch is also robust against short disruptions like overshadowing the sensor during the day or illuminating it briefly by a car’s headlamps during the night.

An important requirement is that any of the automatic modes can be interrupted at any time by manually operating the light switch via . When the user later raises a event, the state machine should return to that particular sub state somewhere down in the Automatic composite state that was active before the manual intervention. Similarly, when toggling between Light Sensing and Motion Sensing the state machine should remember the respective active sub state and return there when control is turned back.

That’s what history entries are good for. The statechart contains a couple of shallow history entries. A shallow history state, contained in a composite state with a set of sub states, remembers which of these sub states was active when that composite state was left.

The shallow history state in Automatic remembers whether Light Sensing or Motion_Sensing was active when Automatic was left, but it doesn’t remember which state was active within the active one of these two. On re-entry, the history state would activate either Twilight or MotionSensing, but these would use their respective entries. This is why there is a history state in each of them as well.

If you have worked with history states before, you might be inclined now to say: "A deep history state would have been the correct thing here." However, you would only be partially right. A deep history state remembers the active state in its own region and everything that was active inside of this state, recursively down to the lowest level. We might spare the two shallow history states in Light Sensing and Motion Sensing. But it would not behave in the same way – a history state is activated only when its containing region is actually left. Using one deep history state would not allow to remember the active state in MotionSensing when switching to Light Sensing, and vice versa. The deep history state would be activated only if we left the whole Automatic composite state – and when switching between automatic modes this won’t happen.

More information on history states can also be found in the History States example or the corresponding chapter in our documentation.