Modeling time in ABS ==================== ABS provides a logical clock, a rational-valued global counter starting at 0 –- see :ref:`sec:timed-abs` in the language manual for details. This example shows how to use ABS to run simulations modeling real (calendar) time and visualize the resulting data in a timeline. The code can be found at ``__. The following short ABS model creates a list of values together with the time when the value was produced. The results are stored in a ``Calendar`` object that is accessible via the ABS Model API (see :ref:`sec:model-api`). During the simulation, the method ``addValue`` is called with random values. The calendar object keeps track of the value and the time when it was added. The calendar also makes two methods callable via the Model API: ``getValues`` returns a list of pairs of recorded time and value. The method ``getStartDate`` returns a string containing a date in ISO 8601 notation; this is the “real” start date that will be used in the simulation. The time values in the list returned by ``getValues`` will be interpreted as *offset* from that point in time:: module CalendarTime; def String start_date() = "2020-04-01"; interface Calendar { [HTTPCallable] String getStartDate(); [HTTPCallable] List> getValues(); Unit addValue(Int value); } class Calendar implements Calendar { List> values = list[]; String getStartDate() { return start_date(); } List> getValues() { return reverse(values); } Unit addValue(Int value) { values = Cons(Pair(timeValue(now()), value), values); } } { [HTTPName: "Calendar"] Calendar c = new Calendar(); Int nRounds = 16; while (nRounds > 0) { c!addValue(random(50)); await duration(1/4, 1/4); nRounds = nRounds - 1; } println(`Finished at $now()$`); } The main block records in-between advancing the clock by ¼ (0.25). Converting time values ---------------------- To visualize times properly, they must be converted to a format that the visualization library expects. In addition to the chosen offset (2020-04-01 in our case), we also need a *scaling factor* to convert the logical clock values of ABS to real date and time values. We choose to represent each day by an increment of :math:`1`; fractional values of the clock represent the time of day. Since our model advances the clock by ¼ sixteen times, the values are recorded at 6am, 12pm, 6pm and midnight for four days. As an example, here is a Javascript function that takes the startdate (as a string parseable via ``new Date``) and offset (as a floating point number, with the integer part the number of days and the fractional part the time of day, with ``0.5`` exactly noon) and returns a Javascript ``Date`` object: .. code:: javascript function absolute_date(startdate, offset) { let days = Math.floor(offset); let time = offset - days; let date = new Date(startdate); date.setDate(date.getDate() + days); date.setTime(date.getTime() + time * 24 * 60 * 60 * 1000); return date; } Visualizing the time series --------------------------- The following is a complete HTML file that uses the `Highcharts library `__ to draw a line diagram of the random values generated by ABS. The function ``drawChart`` retrieves the starting offset and value list via the Model API, then creates a Highcharts chart with the downloaded values. .. code:: html Date and Time in ABS

Date and Time in ABS

Running the example ------------------- The following small Makefile can be used to compile and start the model. Make sure to store the ABS model in a file ``CalendarTime.abs`` and the html in a file ``index.html``, then type ``make run`` and connect the browser to http://localhost:8080: .. code:: make .PHONY: all all: run ## Generate data and start model (default) .PHONY: compile compile: CalendarTime.abs index.html ## Compile the model absc --erlang CalendarTime.abs --modelapi-index-file index.html .PHONY: compile run: compile ## Execute the model gen/erl/run -p 8080 -v .PHONY: help help: ## Display this message @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'