Modeling a water tank ===================== This example shows a model of a small cyber-physical system consisting of a water tank with faucet and drain, and a controller opening and closing the exit valve of the drain. The model illustrates discrete-event simulation, timed semantics, and custom visualization in ABS. The complete code can be found at ``__. The following class shows the model of a water tank. The field ``level`` holds the current water level, the field ``valve_open`` records the status of an exit valve (open or not open). The water tank has *active behavior*. The ``run`` method calculates the new water level on every time tick: ``fillrate`` is always added to the current level (water is always flowing in), and the level decreases by ``drainrate * level`` in case the valve is open, since water pressure is proportional to the water level. :: module SingleWaterTank; data ValveCommand = Open | Close; interface WaterTank { Float getLevel(); Unit valvecontrol(ValveCommand command); [HTTPCallable] List> getValveAndLevelHistory(); } class WaterTank(Float fillrate, Float drainrate) implements WaterTank { Float level = 0.0; Bool valve_open = False; List> history = list[]; List> getValveAndLevelHistory() { return reverse(history); } Float getLevel() { return level; } Unit valvecontrol(ValveCommand command) { valve_open = command == Open; } Unit run() { while (True) { await duration(1, 1); // Water inflow is constant, water outflow is // proportional to the current tank level level = level + fillrate; if (valve_open) { level = max(0.0, level - drainrate * level); } history = Cons(Pair(when valve_open then 1 else 0, level), history); } } } The tank also holds the history of current level and valve status in the field ``history``. The method ``getValveAndLevelHistory`` is annotated to be callable via the :ref:`sec:model-api`, and will be used to visualize the tank level over time. Modeling the controller ----------------------- The task of the controller is to open and close a tank’s valve to keep the water level in a safe range. The controller object does not have any methods but is an active object as well. Its ``run`` method checks the tank’s water level once per clock cycle, and sends ``Open`` and ``Close`` commands as necessary:: interface Controller { } class Controller(WaterTank tank, Float minlevel, Float maxlevel) implements Controller { Unit run() { while (True) { Float level = await tank!getLevel(); if (level >= maxlevel) { tank!valvecontrol(Open); } else if (level <= minlevel) { tank!valvecontrol(Close); } await duration(1, 1); } } } { [HTTPName: "watertank"] WaterTank tank = new WaterTank(1.0, 0.2); Controller controller = new Controller(tank, 5.0, 10.0); } We also see, at the bottom, the main block that creates a water tank object, makes it visible to the Model API, and creates a controller object. Visualizing the water level over time ------------------------------------- The visualization uses the `Highcharts `__ visualization library, so needs an online connection to work. When connecting e.g. to ``localhost:8080``, the browser will make a connection to ``/call/watertank/getValveAndLevelHistory``, which results in a call to the ``getValveAndLevelHistory`` method of the water tank. The returned data is then converted into two lists of values which are passed to Highchart to be plotted. .. code:: html Watertank

Watertank

Running the example ------------------- As mentioned, the code of this example resides at ``__. Place the files ``Watertank.abs``, ``index.html`` and ``Makefile`` into the same directory and run the command ``make`` to compile and start the model. Then, connect a browser to the URL ``http://localhost:8080/`` to see the resulting graph. The resulting plot shows the water level decreasing when the valve is open, and increasing again when the valve is closed. .. figure:: images/single-watertank.png :alt: Plot of water level and valve status over time Plot of water level and valve status over time