Version:
v6.0
[release notes]

# Module 5.7: Water lock case hybrid model

In the first three modules, you modeled the plant for the Westsluis water lock. In the fourth module, you added the requirements and synthesized a supervisor. And you validated the supervisor using interactive simulation. However, the simulation model that you used was untimed. Now you'll apply what you've learned in this module, to add timing to the simulation model, making it a hybrid model. That way, for instance, opening a gate no longer instantaneous, but takes some time, like in the real system.

In the course files, in the folder `module5` folder, there is a `water-lock` folder. This folder contains the plant, requirements and simulation models as obtained during the previous modules. It also contains the scripts to synthesize the supervisor an perform a simulation. Use these files as the starting point for this module.

## Assignment 1

Synthesize the supervisor from the plants and requirements. Then simulate the system with the untimed model once more, to refamiliarize yourself with it.

##

Execute the `synthesize.tooldef` script to synthesize the supervisor. This results in a file named `lock.cif`. Then execute the `simulate.tooldef` script to simulate the controlled system. Read again the water lock case information from the introduction to the water lock case in the first module, if you need more information on the operation of the water lock and how to use the SVG image to control the lock.

## Assignment 2

The plant model specifies the behavior of the various components of the water lock. To add timing, we need to adapt this plant model. However, we still want to be able to synthesize a supervisor, and hybrid plant models are not supported by synthesis. Hence, we will have both an untimed and a hybrid plant model.

a. Make the required changes.

Execute the following changes, one by one:

1. Copy `plant.cif` to `hybrid_plant.cif`.
2. Copy `plant_definitions.cif` to `hybrid_plant_definitions.cif`.
3. Adapt the import in `hybrid_plant.cif` to import the hybrid plant definitions instead of the untimed plant definitions.
4. Adapt the import in `simulation.cif` to import the hybrid plant instead of the synthesized `lock.cif`.
5. Adapt the `synthesize.tooldef` script to write the synthesis result, the controlled system, to `controlled_system.cif` instead of `lock.cif`.
6. Adapt the `synthesize.tooldef` script to additionally supply the `--sup-namespace=sup` option to the synthesis tool.
7. Adapt the `simulate.tooldef` script to the following content:

                                           from "lib:cif" import *;

                                           cifmerge(
                                               "controlled_system.cif",
                                               "simulation.cif",
                                               "--output=simulation_merged.cif",
                                           );

                                           cifsim(
                                               "simulation_merged.cif",
                                               "--input-mode=svg",
                                               "--auto-algo=first",
                                               "--frame-rate=10",
                                               "--option-dialog=no",
                                           );

We then get the following setup:

The plant definitions and plant are copied, to allow us to change them to add timing, while preserving the untimed plant. We import the hybrid plant in the simulation model, to make sure we can animate the visualization based on the state of the hybrid plant.

We put the synthesis result, the controlled system, in `controlled_system.cif` instead of `lock.cif`. We have many files for different parts of the lock model, and this new name better indicates what part of the model is in that file.

Since the simulation model then only imports the hybrid plant, and not the synthesized controlled system model (`controlled_system.cif`) anymore, we need a different solution to get them both in the same model. We adapt the simulation script for that, to merge the synthesis result in `controlled_system.cif` with the simulation model in `simulation.cif`, and save the merge result to `simulation_merged.cif`. We use the CIF merger tool for this. After the merge, we simulate the merged CIF model instead of `simulation.cif`.

The synthesized controlled system includes both the plant and the supervisor. The hybrid plant model also includes the plant. They untimed plant and hybrid plant have groups and automata with the same names. We then can't merge them, as that would lead to name clashes. Hence, we adapt the synthesis script to put the result of synthesis in a namespace named `sup`. This adds a group named `sup` around the synthesis result. That way, the plant and supervisor components in the synthesis result no longer clash with the components in the hybrid plant model, and they can be merged using the CIF merger tool without issues. To ensure that the untimed plants and supervisor still synchronize with the hybrid plant through shared events, the plants and supervisor components are placed in the `sup` group, but the events are still in their original places. When merging, the events that are thus in the same places in both plant models are merged to single events, such that all automata synchronize over the same events again.

b. Try out the new setup.

Resynthesize the supervisor using the `synthesize.tooldef` script. Then simulate the controlled system together with the hybrid simulation model using the `simulate.tooldef` script.

## Assignment 3

In the next assignments, you will change some of the components of the water lock model, by adding timing behavior for them and adjusting the visualization in the simulation model to match those changes. Which of the following components of the water lock model do you think should be adjusted?

- The gates.
- The bridges.
- The culverts.
- The traffic barriers.
- The entering traffic lights.
- The leaving traffic lights.
- The bridge traffic lights.

Also explain your answer.

##

Opening and closing the gates takes time, so timing will be added to it. Similarly, timing will be added to the opening and closing of the bridges, culverts and traffic barriers. They are all open/closed components.

Since the bridge traffic lights have two red lights that alternatingly blink on and off while the bridge traffic lights are on, timing is needed for the alternation. Note however that this timing is actually already present in the simulation model. The simulation model is thus already partially a hybrid model.

The other traffic lights do not exhibit such alternating behavior. They change color practically instantly, and therefore there is no need to add timing to them.

## Assignment 4

First, we'll adapt the open/closed components, so the bridges, gates, culverts and traffic barriers. So far, the sensors of the open/closed components changed their state based on the state of the corresponding actuators. We need to change this to be dependent on the position of the component, which gradually moves from one place to another.

Change the `OpenClosedSensor`. Since different open/closed components may move to and from different positions, adapt the definition to have extra parameters for the open, closed and current positions. Adapt the edges as well, to ensure the correct state changes.

For now, don't update the instantiations of the definition just yet. The model will thus temporarily be invalid after this assignment.

Note that we'll later use smaller values for the closed position than for the open position of the various open/closed components.

##

The `OpenClosedSensor` definition in `hybrid_plant_definitions.cif` can be adapted as follows:

                                                plant def OpenClosedSensor(
                                                    alg bool initial_closed;
                                                    alg real closed_position, open_position, current_position
                                                ):
                                                    uncontrollable u_closed, u_not_closed, u_open, u_not_open;

                                                    location Closed:
                                                        initial initial_closed;
                                                        marked  initial_closed;
                                                        edge u_not_closed when current_position > closed_position  goto Intermediate;

                                                    location Intermediate:
                                                        edge u_closed     when current_position <= closed_position goto Closed;
                                                        edge u_open       when current_position >= open_position   goto Open;

                                                    location Open:
                                                        initial not initial_closed;
                                                        marked  not initial_closed;
                                                        edge u_not_open   when current_position < open_position    goto Intermediate;
                                                end

We add three parameters of type `real` to represent the open, closed and current positions. We put `closed_position` first, as it will have lower values than `open_position`. The `actuator` parameter will no longer be used, so we remove it.

We adapt the guards of the edges. An open/closed component is no longer closed when its position starts to rise above the closed position. Similarly, such a component is no longer open when its position starts to drop below the open position. In the `Intermediate` location, a component can be closed or opened (again) once it reaches the corresponding limit position.

## Assignment 5

Adapt the `OpenClosedComponent` definition, to ensure the open/closed sensor instantiation is valid again. The actual limits may differ for different open/closed components, so their values are not defined here just yet. However, do define the current position using a continuous variable. The speed by which different open/closed components move also differs, so those values can also not be defined here. Also account for the fact that some open/closed components start opened, while others start closed.

##

The `OpenClosedComponent` definition in `hybrid_plant_definitions.cif` can be adapted as follows:

                                                group def OpenClosedComponent(
                                                    alg bool initial_closed;
                                                    alg real closed_position, open_position, speed
                                                ):
                                                    Actuator: OpenClosedActuator(Sensor);
                                                    Sensor:   OpenClosedSensor(initial_closed, closed_position, open_position, position);

                                                    cont position = if initial_closed: closed_position else open_position end
                                                          der if   Actuator.Opening and position < open_position:   +speed
                                                              elif Actuator.Closing and position > closed_position: -speed
                                                              else                                                   0.0
                                                              end;

                                                    alg bool IsMoving = not Actuator.Idle;
                                                    alg bool IsOpen   = Sensor.Open;
                                                    alg bool IsClosed = Sensor.Closed;
                                                end

For `Sensor`, we remove the second argument of instantiation of `OpenClosedSensor`. We add `closed_position`, `open_position` and `position` as arguments. The first two come from new parameters of this definition, and are thus not decided locally. We opt to name the third argument `position` rather than `current_position`, as it is the position of the component and it is defined locally.

We define `position` a continuous variable. Initially, it is either at the `closed_position` or at the `open_position`, depending on the value of existing parameter `initial_closed`.

The derivative of the continuous variable indicates how the open/closed component moves as time progresses. If the `Actuator` is `Opening` and has not yet reached the open position, then the component will move according to its speed. We opt for `+` before the variable name to explicitly contrast it from the `-` sign for the movement in the opposite direction, but we could also leave out the `+` sign. For the movement in the other direction, if the `Actuator` is `Closing` and has not yet reached the closed position, then the component will move according to its negative speed. The speed differs between components, so it is added as a parameter. If the actuator is not `Opening` or `Closing`, or it has already reached the corresponding limit position, then the component stands still, and thus its derivative is zero.

## Assignment 6

Now that we've adapted the open/closed component's definitions, we should also adapt the instantiations in the hybrid plant model. Adapt the instantiations, using the following values:

Component | Closed position | Open position | Speed
--- | --- | --- | ---
Gate | 0 | 40 | 14
Bridge | 0 | 40 | 10
Culvert | 0 | 20 | 10
Traffic barrier | 0 | 90 | 30

The gates and bridges both move 40 meters. The gate does so in just under 3 seconds, while the bridge takes 4 seconds for the movement. The culverts fill and drain in 2 seconds. The traffic barriers open from zero to 90 degrees, with 30 degrees per second, for a total of 3 seconds per movement.

Note that in reality, opening and closing takes considerably longer. We set these relatively short times in order to not have to wait to long during simulations.

##

The `Side` definition in `hybrid_plant.cif` can be adapted as follows (only the relevant parts are shown, and the omitted parts are indicated by `...`):

                                                ...

                                                group def Side():
                                                    Gate: OpenClosedComponent(true, 0, 40, 14);

                                                    GateOpenCommand:  Command();
                                                    GateCloseCommand: Command();

                                                    Bridge: OpenClosedComponent(true, 0, 40, 10);

                                                    BridgeOpenCommand:  Command();
                                                    BridgeCloseCommand: Command();

                                                    group Culverts:
                                                        East: OpenClosedComponent(true, 0, 20, 10);
                                                        West: OpenClosedComponent(true, 0, 20, 10);

                                                        OpenCommand:  Command();
                                                        CloseCommand: Command();

                                                        alg bool IsOpen   = East.IsOpen   and West.IsOpen;
                                                        alg bool IsClosed = East.IsClosed and West.IsClosed;
                                                    end

                                                    group TrafficBarriers:
                                                        East: OpenClosedComponent(false, 0, 90, 30);
                                                        West: OpenClosedComponent(false, 0, 90, 30);

                                                        OpenCommand:  Command();
                                                        CloseCommand: Command();

                                                        alg bool IsOpen   = East.IsOpen   and West.IsOpen;
                                                        alg bool IsClosed = East.IsClosed and West.IsClosed;
                                                    end

                                                    ...

The values from the table are directly used as arguments of the instantiations.

## Assignment 7

Now that the hybrid plant model has been adapted, the visualization model is next. We start with the traffic barriers. Adapt the existing SVG output mappings for the traffic barriers.

To help you to get the rotations correct, here is a sketch of the new mappings. Adapt them by filling in the `...` parts, and put them in the simulation model. The `...` parts may be empty, depending on how to plan to fill in the rotation angles.

                                svgout id "TBNWred"   attr "transform" value ... "rotate(..., 233.511,  58.625)" ...;
                                svgout id "TBNWwhite" attr "transform" value ... "rotate(..., 233.511,  58.625)" ...;
                                svgout id "TBNEred"   attr "transform" value ... "rotate(..., 253.436, 129.589)" ...;
                                svgout id "TBNEwhite" attr "transform" value ... "rotate(..., 253.436, 129.589)" ...;
                                svgout id "TBSWred"   attr "transform" value ... "rotate(...,  59.659,  58.715)" ...;
                                svgout id "TBSWwhite" attr "transform" value ... "rotate(...,  59.659,  58.715)" ...;
                                svgout id "TBSEred"   attr "transform" value ... "rotate(...,  79.272, 129.511)" ...;
                                svgout id "TBSEwhite" attr "transform" value ... "rotate(...,  79.272, 129.511)" ...;

The SVG elements that need to be adapted have ids that start with `TB`. What follows is `NW` for North-West, `NE` for North-East, `SW` for South-West, or `SE` for South-East. The traffic barrier visualization consists of overlapping red and white parts. These parts need to be rotated in the same way.

Each element is given a transformation using the `transform` attribute. The transformation that is to be performed is a `rotate`. The first argument of the `rotate` is the number of degrees to rotate. The second and third argument, which are already given, are the x and y position around which to rotate.

Check whether your changes work, by using simulation.

##

We can fill in the number of degrees to rotate by using string casting to convert the `real` value to a string, and then string concatenation (`+`) to join the multiple parts of the string:

                                                svgout id "TBNWred"   attr "transform" value "rotate(" + <string>(-North.TrafficBarriers.West.position) + ", 233.511,  58.625)";
                                                svgout id "TBNWwhite" attr "transform" value "rotate(" + <string>(-North.TrafficBarriers.West.position) + ", 233.511,  58.625)";
                                                svgout id "TBNEred"   attr "transform" value "rotate(" + <string>( North.TrafficBarriers.East.position) + ", 253.436, 129.589)";
                                                svgout id "TBNEwhite" attr "transform" value "rotate(" + <string>( North.TrafficBarriers.East.position) + ", 253.436, 129.589)";
                                                svgout id "TBSWred"   attr "transform" value "rotate(" + <string>(-South.TrafficBarriers.West.position) + ",  59.659,  58.715)";
                                                svgout id "TBSWwhite" attr "transform" value "rotate(" + <string>(-South.TrafficBarriers.West.position) + ",  59.659,  58.715)";
                                                svgout id "TBSEred"   attr "transform" value "rotate(" + <string>( South.TrafficBarriers.East.position) + ",  79.272, 129.511)";
                                                svgout id "TBSEwhite" attr "transform" value "rotate(" + <string>( South.TrafficBarriers.East.position) + ",  79.272, 129.511)";

Alternatively, we can use the `fmt` function:

                                                svgout id "TBNWred"   attr "transform" value fmt("rotate(%s, 233.511,  58.625)", -North.TrafficBarriers.West.position);
                                                svgout id "TBNWwhite" attr "transform" value fmt("rotate(%s, 233.511,  58.625)", -North.TrafficBarriers.West.position);
                                                svgout id "TBNEred"   attr "transform" value fmt("rotate(%s, 253.436, 129.589)",  North.TrafficBarriers.East.position);
                                                svgout id "TBNEwhite" attr "transform" value fmt("rotate(%s, 253.436, 129.589)",  North.TrafficBarriers.East.position);
                                                svgout id "TBSWred"   attr "transform" value fmt("rotate(%s,  59.659,  58.715)", -South.TrafficBarriers.West.position);
                                                svgout id "TBSWwhite" attr "transform" value fmt("rotate(%s,  59.659,  58.715)", -South.TrafficBarriers.West.position);
                                                svgout id "TBSEred"   attr "transform" value fmt("rotate(%s,  79.272, 129.511)",  South.TrafficBarriers.East.position);
                                                svgout id "TBSEwhite" attr "transform" value fmt("rotate(%s,  79.272, 129.511)",  South.TrafficBarriers.East.position);

This function is given a pattern as first argument. The pattern contains `%s`, which represents a placeholder for the number of degrees to rotate. The value to be filled in is given as the second argument of the `fmt` function. You can read more about the `fmt` function in the CIF tutorial lesson on text formatting.

## Assignment 8

Adapt the SVG output mappings for the gates. Look at the existing mapping, and take inspiration from the previous assignment. Make use of the `fmt` and `scale` functions. When you're done, use simulation to validate your changes.

##

The existing mappings use a `translate` to move the elements. The first argument is the x direction movement, and the second argument is the y direction movement. The y direction movement goes from `64` when closed to `0` when open. However, the gate positions only move by `40`. The difference is a factor of `1.6`. We thus subtract the current position from `64`, accounting for the scale factor:

                                                svgout id "gateN" attr "transform" value fmt("translate(0,%s)", 64 - (North.Gate.position * 1.6));
                                                svgout id "gateS" attr "transform" value fmt("translate(0,%s)", 64 - (South.Gate.position * 1.6));

But, instead of doing the scale calculation ourselves, we can also use the `scale` function:

                                                svgout id "gateN" attr "transform" value fmt("translate(0,%s)", scale(North.Gate.position, 0, 40, 64, 0));
                                                svgout id "gateS" attr "transform" value fmt("translate(0,%s)", scale(South.Gate.position, 0, 40, 64, 0));

The value of `North.Gate.position` is interpreted as a value from `0` to `40` and then scaled to a value from `64` to `0`.

## Assignment 9

Adapt the SVG output mappings for the bridges. Use the following sketch, which again contains some `...` parts. When you're done, use simulation to validate your changes.

                                svgout id "bridgeNclosed" attr "transform" value fmt("scale(1, %s)", 1 - North.Bridge.position / 130);
                                svgout id "bridgeSclosed" attr "transform" value fmt("scale(1, %s)", 1 - South.Bridge.position / 130);

                                svgout id "bridgeNopen"   attr "display" value if ...: "block" else "none"  end;
                                svgout id "bridgeNclosed" attr "display" value if ...: "none"  else "block" end;
                                svgout id "bridgeSopen"   attr "display" value if ...: "block" else "none"  end;
                                svgout id "bridgeSclosed" attr "display" value if ...: "none"  else "block" end;

For the bridge, a `scale` transformation is applied. The first argument for the `scale` is `1`, indicating no scaling in the x direction. The second argument is the scaling in the y direction.

##

Being closed is no longer the inverse of being closed, as a bridge can also be in the intermediate position. The `if` conditions are updated to account for this:

                                                svgout id "bridgeNclosed" attr "transform" value fmt("scale(1, %s)", 1 - North.Bridge.position / 130);
                                                svgout id "bridgeSclosed" attr "transform" value fmt("scale(1, %s)", 1 - South.Bridge.position / 130);

                                                svgout id "bridgeNopen"   attr "display" value if North.Bridge.IsOpen: "block" else "none"  end;
                                                svgout id "bridgeNclosed" attr "display" value if North.Bridge.IsOpen: "none"  else "block" end;
                                                svgout id "bridgeSopen"   attr "display" value if South.Bridge.IsOpen: "block" else "none"  end;
                                                svgout id "bridgeSclosed" attr "display" value if South.Bridge.IsOpen: "none"  else "block" end;

## Assignment 10

Next up are the culverts. They should not immediately change color when they're filled with water, or when they are drained. Adapt the SVG output mappings of the culverts as follows:

                                svgout id "culvertNW" attr "fill" value if   North.Culverts.IsClosed: "#4094ff"
                                                                        elif North.Culverts.IsOpen:   "#0058f1"
                                                                        else                          "#4088ff"
                                                                        end;
                                svgout id "culvertNE" attr "fill" value if   North.Culverts.IsClosed: "#4094ff"
                                                                        elif North.Culverts.IsOpen:   "#0058f1"
                                                                        else                          "#4088ff"
                                                                        end;
                                svgout id "culvertSW" attr "fill" value if   South.Culverts.IsClosed: "#4094ff"
                                                                        elif South.Culverts.IsOpen:   "#0058f1"
                                                                        else                          "#4088ff"
                                                                        end;
                                svgout id "culvertSE" attr "fill" value if   South.Culverts.IsClosed: "#4094ff"
                                                                        elif South.Culverts.IsOpen:   "#0058f1"
                                                                        else                          "#4088ff"
                                                                        end;

## Assignment 11

Currently, when opening the gates, the water level in the chamber between the gates rises or drops instantly. The simulation model has an automaton named `WaterLevel`. Make the water level, represented by `H_Chamber`, time-dependent by making it a continuous variables instead of a discrete variable. Use a speed of 0.4 meters per second. Adapt the rest of the automaton as necessary. Do not yet update the visualization.

##

The `WaterLevel` automaton can be adapted as follows:

                                                automaton WaterLevel:
                                                    disc real H_North = 2, H_South = 1, speed = 0.4;
                                                    cont H_Chamber = H_South
                                                            der if   (OCCC or COCC or OOCC) and (H_Chamber < H_North): +speed
                                                                elif (CCCO or CCOC or CCOO) and (H_Chamber > H_South): -speed
                                                                else                                                    0.0
                                                                end;

                                                    location CCCC:
                                                        initial;
                                                        edge when North.Gate.IsOpen                                goto OCCC;
                                                        edge when North.Culverts.IsOpen                            goto COCC;
                                                        edge when South.Gate.IsOpen                                goto CCOC;
                                                        edge when South.Culverts.IsOpen                            goto CCCO;

                                                    location OCCC:
                                                        edge when North.Gate.IsClosed     and H_Chamber >= H_North goto CCCC;
                                                        edge when North.Culverts.IsOpen   and H_Chamber >= H_North goto OOCC;
                                                        edge when South.Gate.IsOpen       and H_Chamber >= H_North goto OCOC;
                                                        edge when South.Culverts.IsOpen   and H_Chamber >= H_North goto OCCO;

                                                    location COCC:
                                                        edge when North.Gate.IsOpen       and H_Chamber >= H_North goto OOCC;
                                                        edge when North.Culverts.IsClosed and H_Chamber >= H_North goto CCCC;
                                                        edge when South.Gate.IsOpen       and H_Chamber >= H_North goto COOC;
                                                        edge when South.Culverts.IsOpen   and H_Chamber >= H_North goto COCO;

                                                    location CCOC:
                                                        edge when North.Gate.IsOpen       and H_Chamber <= H_South goto OCOC;
                                                        edge when North.Culverts.IsOpen   and H_Chamber <= H_South goto COOC;
                                                        edge when South.Gate.IsClosed     and H_Chamber <= H_South goto CCCC;
                                                        edge when South.Culverts.IsOpen   and H_Chamber <= H_South goto CCOO;

                                                    location CCCO:
                                                        edge when North.Gate.IsOpen       and H_Chamber <= H_South goto OCCO;
                                                        edge when North.Culverts.IsOpen   and H_Chamber <= H_South goto COCO;
                                                        edge when South.Gate.IsOpen       and H_Chamber <= H_South goto CCOO;
                                                        edge when South.Culverts.IsClosed and H_Chamber <= H_South goto CCCC;

                                                    location OOCC:
                                                        edge when North.Gate.IsClosed     and H_Chamber >= H_North goto COCC;
                                                        edge when North.Culverts.IsClosed and H_Chamber >= H_North goto OCCC;
                                                        edge when South.Gate.IsOpen       and H_Chamber >= H_North goto OOOC;
                                                        edge when South.Culverts.IsOpen   and H_Chamber >= H_North goto OOCO;

                                                    location OCOC:
                                                        edge when North.Gate.IsClosed                              goto CCOC;
                                                        edge when North.Culverts.IsOpen                            goto OOOC;
                                                        edge when South.Gate.IsClosed                              goto OCCC;
                                                        edge when South.Culverts.IsOpen                            goto OCOO;

                                                    location OCCO:
                                                        edge when North.Gate.IsClosed                              goto CCCO;
                                                        edge when North.Culverts.IsOpen                            goto OOCO;
                                                        edge when South.Gate.IsOpen                                goto OCOO;
                                                        edge when South.Culverts.IsClosed                          goto OCCC;

                                                    location COOC:
                                                        edge when North.Gate.IsOpen                                goto OOOC;
                                                        edge when North.Culverts.IsClosed                          goto CCOC;
                                                        edge when South.Gate.IsClosed                              goto COCC;
                                                        edge when South.Culverts.IsOpen                            goto COOO;

                                                    location COCO:
                                                        edge when North.Gate.IsOpen                                goto OOCO;
                                                        edge when North.Culverts.IsClosed                          goto CCCO;
                                                        edge when South.Gate.IsOpen                                goto COOO;
                                                        edge when South.Culverts.IsClosed                          goto COCC;

                                                    location CCOO:
                                                        edge when North.Gate.IsOpen       and H_Chamber <= H_South goto OCOO;
                                                        edge when North.Culverts.IsOpen   and H_Chamber <= H_South goto COOO;
                                                        edge when South.Gate.IsClosed     and H_Chamber <= H_South goto CCCO;
                                                        edge when South.Culverts.IsClosed and H_Chamber <= H_South goto CCOC;

                                                    location OOOC:
                                                        edge when North.Gate.IsClosed                              goto COOC;
                                                        edge when North.Culverts.IsClosed                          goto OCOC;
                                                        edge when South.Gate.IsClosed                              goto OOCC;
                                                        edge when South.Culverts.IsOpen                            goto OOOO;

                                                    location OOCO:
                                                        edge when North.Gate.IsClosed                              goto COCO;
                                                        edge when North.Culverts.IsClosed                          goto OCCO;
                                                        edge when South.Gate.IsOpen                                goto OOOO;
                                                        edge when South.Culverts.IsClosed                          goto OOCC;

                                                    location OCOO:
                                                        edge when North.Gate.IsClosed                              goto CCOO;
                                                        edge when North.Culverts.IsOpen                            goto OOOO;
                                                        edge when South.Gate.IsClosed                              goto OCCO;
                                                        edge when South.Culverts.IsClosed                          goto OCOC;

                                                    location COOO:
                                                        edge when North.Gate.IsOpen                                goto OOOO;
                                                        edge when North.Culverts.IsClosed                          goto CCOO;
                                                        edge when South.Gate.IsClosed                              goto COCO;
                                                        edge when South.Culverts.IsClosed                          goto COOC;

                                                    location OOOO:
                                                        edge when North.Gate.IsClosed                              goto COOO;
                                                        edge when North.Culverts.IsClosed                          goto OCOO;
                                                        edge when South.Gate.IsClosed                              goto OOCO;
                                                        edge when South.Culverts.IsClosed                          goto OOOC;
                                                end

The type of the discrete variables is changed to `real`. Variable `H_Chamber` becomes a continuous variable. It is still initialized to `H_South`. For the locations where the water level can change, a speed it set, unless the final height is already reached. This is similar to the position of open/closed components. The assignments to `H_Chamber` are removed. Some edges get additional guards to ensure the correct time-dependent behavior of the transitions between the locations.

## Assignment 12

And finally, we visualize the water level. Change the color of the water chamber (SVG element with id `waterchamber`) and the two inlets near the culverts into which the gates close (SVG elements with ids `culvertNinlet` and `culvertSinlet`). Use the same three colors as for the culverts.

##

                                                svgout id "waterchamber"   attr "fill" value if   WaterLevel.H_Chamber <= 1: "#4094ff"
                                                                                             elif WaterLevel.H_Chamber >= 2: "#0058f1"
                                                                                             else                            "#4088ff"
                                                                                             end;
                                                svgout id "culvertNinlet"  attr "fill" value if   WaterLevel.H_Chamber <= 1: "#4094ff"
                                                                                             elif WaterLevel.H_Chamber >= 2: "#0058f1"
                                                                                             else                            "#4088ff"
                                                                                             end;
                                                svgout id "culvertSinlet"  attr "fill" value if   WaterLevel.H_Chamber <= 1: "#4094ff"
                                                                                             elif WaterLevel.H_Chamber >= 2: "#0058f1"
                                                                                             else                            "#4088ff"
                                                                                             end;

## Final remarks

And with that, we've come to the end of the water lock case. You have made plants, requirements, a supervisor and a hybrid simulation model of the Westsluis water lock. You can now simulate its correct and safe behavior. Congratulations!