Changes have been pushed for the repository "fawkes.git".
(Fawkes Robotics Software Framework)

Clone:  g...@git.fawkesrobotics.org:fawkes.git
Gitweb: http://git.fawkesrobotics.org/fawkes.git
Trac:   http://trac.fawkesrobotics.org

The branch, timn/webview-2.0 has been updated
        to  8c1aa9014abec21a9b6df8404ba59c11db9bbaf5 (commit)
       via  35a5f413386a8b2a9d565451152d4c3c94eda296 (commit)
       via  921c1dae4f690e16b027fa4f3607517338f2d71c (commit)
       via  b7d5db3008e1db496c0485c614cece30e4570526 (commit)
       via  891e7d8483f91cf397b1df3465d9a3af776be3dd (commit)
       via  480da4a8c46b1b3870f91d883812af05286d0de8 (commit)
       via  338d84a6aa01150f24b16dd5dd81bb6b8f93a695 (commit)
       via  b6d6ef265baa7eeefe326a0ab2fa1078cbc584de (commit)
       via  4abb38ff69c300a3c0097e378436ffb1965566eb (commit)
       via  dc53e8042b86fa4d8e9559cac217967c159320ee (commit)
       via  f54d615201b457eea660dd2a30c7c2f5a0577ea9 (commit)
       via  182676f9ae9f99399bccefb681f8f920483878ff (commit)
       via  11e641d8e65e763e61329cd8ed9d46731fc9a482 (commit)
       via  4d6b8b9217b49181769d718f8a7d2f8b2356a855 (commit)
       via  93c3f880af3d5d4748831c43848bad3cc649e030 (commit)
       via  d49761586d7c9693a74123c7e23674956393e009 (commit)
       via  c5ead7c94daaf9d6427721475fe88ad2bc3e10df (commit)
       via  edad2d54a1ccbcdaf1d69aced2ce23bef6f7e91c (commit)
       via  dcc41e581bd5889e28b62f2e8eba192ac30b5285 (commit)
       via  5e204dc515656db7957ddb5a7dbc1b05bf5b7cf7 (commit)
       via  fd8c527095b3be456fd0d02cf6b5d967cd019fd3 (commit)
       via  bcb351a807ceb963546e2ea4aa0d88eef971a2ed (commit)
       via  a06331bd0f4fcc5daec540b4f65372897d3324c5 (commit)
       via  5ae01f8a22ba112adaa7c81047244eec23c7578f (commit)
       via  86ab666a3fa2f7ab38620f3c040185e7e61571f4 (commit)
       via  2783e6cdf27c7030dbe5fb133954b291833a60ea (commit)
      from  250ded3bff67e2fd240009cef48314c4354d22e2 (commit)

http://git.fawkesrobotics.org/fawkes.git/timn/webview-2.0

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- *Log* ---------------------------------------------------------------
commit 2783e6cdf27c7030dbe5fb133954b291833a60ea
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Mon Apr 9 23:49:31 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Mon Apr 9 23:49:31 2018 +0200

    libwebview: add WebviewRestParams::has_query_arg()
    
    Check if a flag has been set (with any value, even an empty one).

http://git.fawkesrobotics.org/fawkes.git/commit/2783e6c
http://trac.fawkesrobotics.org/changeset/2783e6c

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit 86ab666a3fa2f7ab38620f3c040185e7e61571f4
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Mon Apr 9 23:50:07 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Mon Apr 9 23:50:07 2018 +0200

    libwebview: handle "pretty" as a REST API default parameter
    
    To reduce code clutter, globally handle the pretty parameter for the
    cases where we have an API for which we generate the WebviewRestReply.

http://git.fawkesrobotics.org/fawkes.git/commit/86ab666
http://trac.fawkesrobotics.org/changeset/86ab666

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit 5ae01f8a22ba112adaa7c81047244eec23c7578f
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Mon Apr 9 23:51:32 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Mon Apr 9 23:51:32 2018 +0200

    webview: adapt to new "pretty" parameter default handling

http://git.fawkesrobotics.org/fawkes.git/commit/5ae01f8
http://trac.fawkesrobotics.org/changeset/5ae01f8

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit a06331bd0f4fcc5daec540b4f65372897d3324c5
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Mon Apr 9 23:52:24 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Mon Apr 9 23:52:24 2018 +0200

    clips-executive-rest-api: adapt to new "pretty" default handling

http://git.fawkesrobotics.org/fawkes.git/commit/a06331b
http://trac.fawkesrobotics.org/changeset/a06331b

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit bcb351a807ceb963546e2ea4aa0d88eef971a2ed
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Mon Apr 9 23:53:03 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Mon Apr 9 23:53:03 2018 +0200

    clips: adapt to new "pretty" parameter default handling

http://git.fawkesrobotics.org/fawkes.git/commit/bcb351a
http://trac.fawkesrobotics.org/changeset/bcb351a

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit fd8c527095b3be456fd0d02cf6b5d967cd019fd3
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Mon Apr 9 23:53:28 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Mon Apr 9 23:53:28 2018 +0200

    skiller: adapt to new "pretty" parameter default handling

http://git.fawkesrobotics.org/fawkes.git/commit/fd8c527
http://trac.fawkesrobotics.org/changeset/fd8c527

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit 5e204dc515656db7957ddb5a7dbc1b05bf5b7cf7
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Mon Apr 9 23:55:30 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Mon Apr 9 23:55:30 2018 +0200

    clips-executive-rest-api: comment out add_goal API for now
    
    We do not use or support this API atm and there seems to be a bug in
    Angular preventing us from using it properly,
    cf. https://github.com/angular/angular/issues/23266

http://git.fawkesrobotics.org/fawkes.git/commit/5e204dc
http://trac.fawkesrobotics.org/changeset/5e204dc

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit dcc41e581bd5889e28b62f2e8eba192ac30b5285
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Mon Apr 9 23:59:25 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Mon Apr 9 23:59:25 2018 +0200

    webview: add backend info REST API
    
    Allow to retrieve configurable information about available backends.
    Enables supporting multi-robot scenarios from a single frontend. For
    now, we support configured backends. Later, backends might also be
    auto-discovered using mDNS-SD as we did before.

http://git.fawkesrobotics.org/fawkes.git/commit/dcc41e5
http://trac.fawkesrobotics.org/changeset/dcc41e5

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit edad2d54a1ccbcdaf1d69aced2ce23bef6f7e91c
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Tue Apr 10 00:02:09 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Tue Apr 10 00:02:09 2018 +0200

    webview-frontend: replace basic config with full backend config
    
    The basic configuration allowed for arbitrary values, but was rather
    basic, as it could only host one backend configuration.
    
    The new BackendConfigurationService automatically:
    - establishes a basic origin backend configuration for the site from
      which the page was retrieved
    - enables local access in a non-production environment
    - downloads backend configuration from the origin backend
    - retries downloads until they succeed
    - updates the backend configuration every 5 minutes from origin
    - emits an event when the backend is changed
    - performs sanity checks on incoming data
    - re-selects a backend if a backend with the same URL and basic
      API service configuration is received, but with a different name,
      i.e., rename the "Local"/origin service to, e.g., "Robot 1" if a
      configuration was received from there which matches our auto-detected
      origin config. This enables overriding the name from the remote.
    - supports special tokens in configuration to reference auto-detected
      origin, e.g., to configure auxiliary APIs

http://git.fawkesrobotics.org/fawkes.git/commit/edad2d5
http://trac.fawkesrobotics.org/changeset/edad2d5

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit c5ead7c94daaf9d6427721475fe88ad2bc3e10df
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Tue Apr 10 00:08:03 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Tue Apr 10 00:08:03 2018 +0200

    webview-frontend: add chrome to enable switching backends

http://git.fawkesrobotics.org/fawkes.git/commit/c5ead7c
http://trac.fawkesrobotics.org/changeset/c5ead7c

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit d49761586d7c9693a74123c7e23674956393e009
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Tue Apr 10 00:09:47 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Tue Apr 10 00:09:47 2018 +0200

    webview-frontend: adapt parts to refresh on backend change
    
    Have components automatically update once a new backend is selected to
    avoid stale data being visible.

http://git.fawkesrobotics.org/fawkes.git/commit/d497615
http://trac.fawkesrobotics.org/changeset/d497615

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit 93c3f880af3d5d4748831c43848bad3cc649e030
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Tue Apr 10 00:10:29 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Tue Apr 10 00:10:29 2018 +0200

    restapi-gen: adapt Angular API template to new backend config
    
    Newly generated APIs use the new BackendConfigurationService to
    determine the backend URL.

http://git.fawkesrobotics.org/fawkes.git/commit/93c3f88
http://trac.fawkesrobotics.org/changeset/93c3f88

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit 4d6b8b9217b49181769d718f8a7d2f8b2356a855
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Tue Apr 10 00:11:39 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Tue Apr 10 00:11:39 2018 +0200

    webview-frontend: re-generate API services with new template

http://git.fawkesrobotics.org/fawkes.git/commit/4d6b8b9
http://trac.fawkesrobotics.org/changeset/4d6b8b9

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit 11e641d8e65e763e61329cd8ed9d46731fc9a482
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Tue Apr 10 00:17:00 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Tue Apr 10 00:17:00 2018 +0200

    webview-frontend: adapt Prometheus chart to dynamic backend
    
    Refresh if the backend changes.

http://git.fawkesrobotics.org/fawkes.git/commit/11e641d
http://trac.fawkesrobotics.org/changeset/11e641d

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit 182676f9ae9f99399bccefb681f8f920483878ff
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Tue Apr 10 00:22:08 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Tue Apr 10 00:22:08 2018 +0200

    webview-frontend: automatically call "npm i" if node_modules missing

http://git.fawkesrobotics.org/fawkes.git/commit/182676f
http://trac.fawkesrobotics.org/changeset/182676f

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit f54d615201b457eea660dd2a30c7c2f5a0577ea9
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Tue Apr 10 11:52:06 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Tue Apr 10 11:52:06 2018 +0200

    webview-frontend: configure default prometheus url only in dev env
    
    For the production deployment do not set a prometheus URL but rely on
    the backend info api to provide the URL. It is likely that in production
    the prometheus URL is proxied, e.g., to add TLS.
    
    The, however, accessing a wrong URL may lead to issues installing the
    service worker. Therefore, do not give a URL at all.
    
    Add a method to query whether a URL is known for a given service.

http://git.fawkesrobotics.org/fawkes.git/commit/f54d615
http://trac.fawkesrobotics.org/changeset/f54d615

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit dc53e8042b86fa4d8e9559cac217967c159320ee
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Tue Apr 10 11:54:06 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Tue Apr 10 11:54:06 2018 +0200

    webview-frontend: anticipate missing prometheus URL
    
    The URL may not (yet) be set if not provided by the backend. Show a
    useful message in this case.

http://git.fawkesrobotics.org/fawkes.git/commit/dc53e80
http://trac.fawkesrobotics.org/changeset/dc53e80

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit 4abb38ff69c300a3c0097e378436ffb1965566eb
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Tue Apr 10 11:54:43 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Tue Apr 10 11:54:43 2018 +0200

    webview-frontend: improve error handling in prometheus chart

http://git.fawkesrobotics.org/fawkes.git/commit/4abb38f
http://trac.fawkesrobotics.org/changeset/4abb38f

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit b6d6ef265baa7eeefe326a0ab2fa1078cbc584de
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Tue Apr 10 11:56:20 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Tue Apr 10 11:56:20 2018 +0200

    webview-frontend: periodically check for frontend updates
    
    Implement service worker update notifier. Shows a snack bar if an update
    is available.

http://git.fawkesrobotics.org/fawkes.git/commit/b6d6ef2
http://trac.fawkesrobotics.org/changeset/b6d6ef2

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit 338d84a6aa01150f24b16dd5dd81bb6b8f93a695
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Tue Apr 10 18:55:30 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Tue Apr 10 18:55:30 2018 +0200

    libwebview: add convenience methods for body handling to request

http://git.fawkesrobotics.org/fawkes.git/commit/338d84a
http://trac.fawkesrobotics.org/changeset/338d84a

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit 480da4a8c46b1b3870f91d883812af05286d0de8
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Tue Apr 10 18:56:04 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Tue Apr 10 18:56:04 2018 +0200

    libwebview: body input handling for methods other than POST

http://git.fawkesrobotics.org/fawkes.git/commit/480da4a
http://trac.fawkesrobotics.org/changeset/480da4a

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit 891e7d8483f91cf397b1df3465d9a3af776be3dd
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Tue Apr 10 18:57:24 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Tue Apr 10 18:57:24 2018 +0200

    libwebview: change order of input and output paramter
    
    This is now in line with other approachens when binding parameters. Was
    confusing before.

http://git.fawkesrobotics.org/fawkes.git/commit/891e7d8
http://trac.fawkesrobotics.org/changeset/891e7d8

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit b7d5db3008e1db496c0485c614cece30e4570526
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Tue Apr 10 18:58:06 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Tue Apr 10 18:58:06 2018 +0200

    skiller-rest-api: adapt to changed parameter order

http://git.fawkesrobotics.org/fawkes.git/commit/b7d5db3
http://trac.fawkesrobotics.org/changeset/b7d5db3

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit 921c1dae4f690e16b027fa4f3607517338f2d71c
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Tue Apr 10 18:58:31 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Tue Apr 10 18:58:31 2018 +0200

    libwebview: support throwing JSON object exceptions
    
    Makes failure responses that respond with JSON easier.

http://git.fawkesrobotics.org/fawkes.git/commit/921c1da
http://trac.fawkesrobotics.org/changeset/921c1da

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit 35a5f413386a8b2a9d565451152d4c3c94eda296
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Tue Apr 10 18:59:16 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Tue Apr 10 18:59:16 2018 +0200

    libplugin: add convenience methods and make API nicer
    
    Ingest more std::string rather than const char*. Add methods to query if
    plugin is a meta plugin and if so what it's dependents/children are.

http://git.fawkesrobotics.org/fawkes.git/commit/35a5f41
http://trac.fawkesrobotics.org/changeset/35a5f41

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
commit 8c1aa9014abec21a9b6df8404ba59c11db9bbaf5
Author:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
AuthorDate: Tue Apr 10 19:00:26 2018 +0200
Commit:     Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
CommitDate: Tue Apr 10 19:00:26 2018 +0200

    webview: add plugin REST API

http://git.fawkesrobotics.org/fawkes.git/commit/8c1aa90
http://trac.fawkesrobotics.org/changeset/8c1aa90

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


- *Summary* -----------------------------------------------------------
 .../templates/typescript-angular.ts.api.template   |    6 +-
 src/libs/plugin/manager.cpp                        |   39 +++-
 src/libs/plugin/manager.h                          |   10 +-
 src/libs/webview/request.cpp                       |   24 ++
 src/libs/webview/request.h                         |    2 +
 src/libs/webview/request_dispatcher.cpp            |    8 +-
 src/libs/webview/rest_api.h                        |   55 ++++-
 src/plugins/clips-executive/rest-api/api.yaml      |   40 ++--
 .../rest-api/clips-executive-rest-api.cpp          |   51 +----
 .../rest-api/clips-executive-rest-api.h            |   27 +--
 src/plugins/clips/rest-api/clips-rest-api.cpp      |   11 +-
 src/plugins/clips/rest-api/clips-rest-api.h        |    3 +-
 src/plugins/skiller/rest-api/skiller-rest-api.cpp  |   27 +--
 src/plugins/skiller/rest-api/skiller-rest-api.h    |    5 +-
 src/plugins/webview/Makefile                       |    4 +-
 .../Makefile                                       |    0
 src/plugins/webview/backendinfo-rest-api/api.yaml  |   82 +++++++
 .../backendinfo-rest-api/backendinfo-rest-api.cpp  |  107 +++++++++
 .../backendinfo-rest-api/backendinfo-rest-api.h    |   52 +++++
 .../webview/backendinfo-rest-api/model/Backend.cpp |  180 +++++++++++++++
 .../webview/backendinfo-rest-api/model/Backend.h   |  220 +++++++++++++++++++
 .../webview/backendinfo-rest-api/model/Service.cpp |  122 +++++++++++
 .../webview/backendinfo-rest-api/model/Service.h   |  124 +++++++++++
 .../blackboard-rest-api/blackboard-rest-api.cpp    |   17 +--
 .../blackboard-rest-api/blackboard-rest-api.h      |    3 +-
 src/plugins/webview/frontend/Makefile              |    6 +-
 .../webview/frontend/src/app/app.component.ts      |    8 +-
 src/plugins/webview/frontend/src/app/app.module.ts |    5 +-
 .../webview/frontend/src/chrome/component.ts       |   19 ++-
 src/plugins/webview/frontend/src/chrome/style.scss |   15 ++
 .../webview/frontend/src/chrome/template.html      |   50 ++---
 .../frontend/src/components/promchart/component.ts |   61 ++++--
 .../src/components/promchart/template.html         |    2 +-
 .../blackboard/components/overview.component.html  |   38 ++--
 .../blackboard/components/overview.component.ts    |   64 +++++-
 .../src/parts/blackboard/services/api.service.ts   |   38 +++-
 .../clips-executive/components/domain.component.ts |   28 ++-
 .../components/goal-detail.component.ts            |   33 ++-
 .../components/goal-list.component.ts              |   19 ++-
 .../parts/clips-executive/services/api.service.ts  |   96 ++++++---
 .../parts/clips/components/clips-env.component.ts  |   26 ++-
 .../src/parts/clips/services/api.service.ts        |   20 +-
 .../dashboard/components/dashboard.component.ts    |   23 ++-
 .../parts/images/components/overview.component.ts  |   19 ++-
 .../src/parts/images/services/api.service.ts       |    8 +-
 .../parts/skiller/components/overview.component.ts |   21 ++-
 .../src/parts/skiller/services/api.service.ts      |   12 +-
 .../backend-config/backend-config.service.ts       |  209 ++++++++++++++++++
 .../src/services/backend-config/model/Backend.ts   |   31 +++
 .../src/services/backend-config/model/Service.ts   |   26 +++
 .../frontend/src/services/config.service.ts        |   58 -----
 .../update-notifier/update-notifier.service.ts     |   55 +++++
 src/plugins/webview/frontend/src/shared.module.ts  |    3 +-
 .../webview/image-rest-api/image-rest-api.cpp      |    8 +-
 .../webview/image-rest-api/image-rest-api.h        |    3 +-
 src/plugins/webview/plugin-rest-api/Makefile       |   20 ++
 src/plugins/webview/plugin-rest-api/api.yaml       |  183 ++++++++++++++++
 .../webview/plugin-rest-api/model/Plugin.cpp       |  175 +++++++++++++++
 src/plugins/webview/plugin-rest-api/model/Plugin.h |  230 ++++++++++++++++++++
 .../plugin-rest-api/model/PluginOpRequest.cpp      |  139 ++++++++++++
 .../plugin-rest-api/model/PluginOpRequest.h        |  160 ++++++++++++++
 .../plugin-rest-api/model/PluginOpResponse.cpp     |  148 +++++++++++++
 .../plugin-rest-api/model/PluginOpResponse.h       |  178 +++++++++++++++
 .../webview/plugin-rest-api/plugin-rest-api.cpp    |  150 +++++++++++++
 .../webview/plugin-rest-api/plugin-rest-api.h      |   58 +++++
 src/plugins/webview/webview_plugin.cpp             |    8 +-
 66 files changed, 3257 insertions(+), 415 deletions(-)
 copy src/plugins/webview/{blackboard-rest-api => 
backendinfo-rest-api}/Makefile (100%)
 create mode 100644 src/plugins/webview/backendinfo-rest-api/api.yaml
 create mode 100644 
src/plugins/webview/backendinfo-rest-api/backendinfo-rest-api.cpp
 create mode 100644 
src/plugins/webview/backendinfo-rest-api/backendinfo-rest-api.h
 create mode 100644 src/plugins/webview/backendinfo-rest-api/model/Backend.cpp
 create mode 100644 src/plugins/webview/backendinfo-rest-api/model/Backend.h
 create mode 100644 src/plugins/webview/backendinfo-rest-api/model/Service.cpp
 create mode 100644 src/plugins/webview/backendinfo-rest-api/model/Service.h
 create mode 100644 
src/plugins/webview/frontend/src/services/backend-config/backend-config.service.ts
 create mode 100644 
src/plugins/webview/frontend/src/services/backend-config/model/Backend.ts
 create mode 100644 
src/plugins/webview/frontend/src/services/backend-config/model/Service.ts
 delete mode 100644 src/plugins/webview/frontend/src/services/config.service.ts
 create mode 100644 
src/plugins/webview/frontend/src/services/update-notifier/update-notifier.service.ts
 create mode 100644 src/plugins/webview/plugin-rest-api/Makefile
 create mode 100644 src/plugins/webview/plugin-rest-api/api.yaml
 create mode 100644 src/plugins/webview/plugin-rest-api/model/Plugin.cpp
 create mode 100644 src/plugins/webview/plugin-rest-api/model/Plugin.h
 create mode 100644 
src/plugins/webview/plugin-rest-api/model/PluginOpRequest.cpp
 create mode 100644 src/plugins/webview/plugin-rest-api/model/PluginOpRequest.h
 create mode 100644 
src/plugins/webview/plugin-rest-api/model/PluginOpResponse.cpp
 create mode 100644 src/plugins/webview/plugin-rest-api/model/PluginOpResponse.h
 create mode 100644 src/plugins/webview/plugin-rest-api/plugin-rest-api.cpp
 create mode 100644 src/plugins/webview/plugin-rest-api/plugin-rest-api.h


- *Diffs* -------------------------------------------------------------

- *commit* 2783e6cdf27c7030dbe5fb133954b291833a60ea - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Mon Apr 9 23:49:31 2018 +0200
Subject: libwebview: add WebviewRestParams::has_query_arg()

 src/libs/webview/rest_api.h |   11 +++++++++++
 1 files changed, 11 insertions(+), 0 deletions(-)

_Diff for modified files_:
diff --git a/src/libs/webview/rest_api.h b/src/libs/webview/rest_api.h
index 8e175fb..9626a1a 100644
--- a/src/libs/webview/rest_api.h
+++ b/src/libs/webview/rest_api.h
@@ -139,6 +139,17 @@ class WebviewRestParams
                }
        }
 
+       /** Check if query argument is set.
+        * Retrieves a named query argument that was passed in the
+        * URL, e.g., retrieve "pretty" for "?pretty".
+        * @param what what to check
+        * @return true if the argument exists (with any value), false otherwise
+        */
+       bool has_query_arg(const std::string& what)
+       {
+               return (query_args_.find(what) != query_args_.end());
+       }
+
        /** Is pretty-printed JSON enabled?
         * @return true true to request enabling pretty mode
         */

- *commit* 86ab666a3fa2f7ab38620f3c040185e7e61571f4 - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Mon Apr 9 23:50:07 2018 +0200
Subject: libwebview: handle "pretty" as a REST API default parameter

 src/libs/webview/rest_api.h |    6 ++++++
 1 files changed, 6 insertions(+), 0 deletions(-)

_Diff for modified files_:
diff --git a/src/libs/webview/rest_api.h b/src/libs/webview/rest_api.h
index 9626a1a..1109d33 100644
--- a/src/libs/webview/rest_api.h
+++ b/src/libs/webview/rest_api.h
@@ -251,6 +251,9 @@ class WebviewRestApi
                                            } catch (std::runtime_error &e) {
                                                    
logger_->log_warn(("RestAPI|" + name_).c_str(), "%s", e.what());
                                            }
+                                           if (m.has_query_arg("pretty")) {
+                                                   m.set_pretty_json(true);
+                                           }
                                            return 
std::make_unique<WebviewRestReply>
                                                    (WebReply::HTTP_OK, 
output.to_json(pretty_json_ || m.pretty_json()));
                                    } catch (WebviewRestException &e) {
@@ -316,6 +319,9 @@ class WebviewRestApi
                                            } catch (std::runtime_error &e) {
                                                    
logger_->log_warn(("RestAPI|" + name_).c_str(), "%s", e.what());
                                            }
+                                           if (m.has_query_arg("pretty")) {
+                                                   m.set_pretty_json(true);
+                                           }
                                            return 
std::make_unique<WebviewRestReply>
                                                    (WebReply::HTTP_OK, 
output.to_json(pretty_json_ || m.pretty_json()));
                                    } catch (WebviewRestException &e) {

- *commit* 5ae01f8a22ba112adaa7c81047244eec23c7578f - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Mon Apr 9 23:51:32 2018 +0200
Subject: webview: adapt to new "pretty" parameter default handling

 .../blackboard-rest-api/blackboard-rest-api.cpp    |   17 +++--------------
 .../blackboard-rest-api/blackboard-rest-api.h      |    3 +--
 .../webview/image-rest-api/image-rest-api.cpp      |    8 ++------
 .../webview/image-rest-api/image-rest-api.h        |    3 +--
 4 files changed, 7 insertions(+), 24 deletions(-)

_Diff for modified files_:
diff --git a/src/plugins/webview/blackboard-rest-api/blackboard-rest-api.cpp 
b/src/plugins/webview/blackboard-rest-api/blackboard-rest-api.cpp
index d0fbf63..1f08fe9 100644
--- a/src/plugins/webview/blackboard-rest-api/blackboard-rest-api.cpp
+++ b/src/plugins/webview/blackboard-rest-api/blackboard-rest-api.cpp
@@ -54,7 +54,7 @@ BlackboardRestApi::init()
        rest_api_ = new WebviewRestApi("blackboard", logger);
        rest_api_->add_handler<WebviewRestArray<::InterfaceInfo>>
                (WebRequest::METHOD_GET, "/interfaces",
-                std::bind(&BlackboardRestApi::cb_list_interfaces, this, 
std::placeholders::_1));
+                std::bind(&BlackboardRestApi::cb_list_interfaces, this));
        rest_api_->add_handler<InterfaceData>
                (WebRequest::METHOD_GET, "/interfaces/{type}/{id+}/data",
                 std::bind(&BlackboardRestApi::cb_get_interface_data, this, 
std::placeholders::_1));
@@ -245,12 +245,8 @@ BlackboardRestApi::gen_interface_data(Interface *iface, 
bool pretty)
 
 
 WebviewRestArray<::InterfaceInfo>
-BlackboardRestApi::cb_list_interfaces(WebviewRestParams& params)
+BlackboardRestApi::cb_list_interfaces()
 {
-       if (params.query_arg("pretty") == "true") {
-               params.set_pretty_json(true);
-       }
-
        WebviewRestArray<::InterfaceInfo> rv;
 
        std::unique_ptr<InterfaceInfoList> ifls{blackboard->list_all()};
@@ -265,10 +261,6 @@ BlackboardRestApi::cb_list_interfaces(WebviewRestParams& 
params)
 ::InterfaceInfo
 BlackboardRestApi::cb_get_interface_info(WebviewRestParams& params)
 {
-       if (params.query_arg("pretty") == "true") {
-               params.set_pretty_json(true);
-       }
-
        if (params.path_arg("type").find_first_of("*?") != std::string::npos) {
                throw WebviewRestException(WebReply::HTTP_BAD_REQUEST, "Type 
may not contain any of [*?].");
        }
@@ -289,10 +281,7 @@ 
BlackboardRestApi::cb_get_interface_info(WebviewRestParams& params)
 InterfaceData
 BlackboardRestApi::cb_get_interface_data(WebviewRestParams& params)
 {
-       bool pretty = false;
-       if (params.query_arg("pretty") == "true") {
-               pretty = true;
-       }
+       bool pretty = params.has_query_arg("pretty");
        params.set_pretty_json(pretty);
 
        if (params.path_arg("type").find_first_of("*?") != std::string::npos) {
diff --git a/src/plugins/webview/blackboard-rest-api/blackboard-rest-api.h 
b/src/plugins/webview/blackboard-rest-api/blackboard-rest-api.h
index 21ca1c2..432510a 100644
--- a/src/plugins/webview/blackboard-rest-api/blackboard-rest-api.h
+++ b/src/plugins/webview/blackboard-rest-api/blackboard-rest-api.h
@@ -53,8 +53,7 @@ class BlackboardRestApi
        virtual void finalize();
 
  private:
-       WebviewRestArray<InterfaceInfo>
-               cb_list_interfaces(fawkes::WebviewRestParams& params);
+       WebviewRestArray<InterfaceInfo> cb_list_interfaces();
 
        InterfaceInfo
                cb_get_interface_info(fawkes::WebviewRestParams& params);
diff --git a/src/plugins/webview/image-rest-api/image-rest-api.cpp 
b/src/plugins/webview/image-rest-api/image-rest-api.cpp
index e11d8df..7582e68 100644
--- a/src/plugins/webview/image-rest-api/image-rest-api.cpp
+++ b/src/plugins/webview/image-rest-api/image-rest-api.cpp
@@ -52,7 +52,7 @@ ImageRestApi::init()
        rest_api_ = new WebviewRestApi("images", logger);
        rest_api_->add_handler<WebviewRestArray<ImageInfo>>
                (WebRequest::METHOD_GET, "/?",
-                std::bind(&ImageRestApi::cb_list_images, this, 
std::placeholders::_1));
+                std::bind(&ImageRestApi::cb_list_images, this));
        rest_api_->add_handler(WebRequest::METHOD_GET, "/{id+}",
                               std::bind(&ImageRestApi::cb_get_image, this, 
std::placeholders::_1));
        webview_rest_api_manager->register_api(rest_api_);
@@ -77,12 +77,8 @@ ImageRestApi::loop()
 
 
 WebviewRestArray<ImageInfo>
-ImageRestApi::cb_list_images(WebviewRestParams& params)
+ImageRestApi::cb_list_images()
 {
-       if (params.query_arg("pretty") == "true") {
-               params.set_pretty_json(true);
-       }
-
        WebviewRestArray<ImageInfo> rv;
 
        std::list<SharedMemoryImageBufferMetaData> meta_data =
diff --git a/src/plugins/webview/image-rest-api/image-rest-api.h 
b/src/plugins/webview/image-rest-api/image-rest-api.h
index 548b076..3e56116 100644
--- a/src/plugins/webview/image-rest-api/image-rest-api.h
+++ b/src/plugins/webview/image-rest-api/image-rest-api.h
@@ -58,8 +58,7 @@ class ImageRestApi
        virtual void finalize();
 
  private:
-       WebviewRestArray<ImageInfo>
-               cb_list_images(fawkes::WebviewRestParams& params);
+       WebviewRestArray<ImageInfo> cb_list_images();
 
        std::shared_ptr<fawkes::WebviewJpegStreamProducer>
                get_stream(const std::string& image_id);

- *commit* a06331bd0f4fcc5daec540b4f65372897d3324c5 - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Mon Apr 9 23:52:24 2018 +0200
Subject: clips-executive-rest-api: adapt to new "pretty" default handling

 .../rest-api/clips-executive-rest-api.cpp          |   51 +++++---------------
 .../rest-api/clips-executive-rest-api.h            |   27 +++--------
 2 files changed, 19 insertions(+), 59 deletions(-)

_Diff for modified files_:
diff --git a/src/plugins/clips-executive/rest-api/clips-executive-rest-api.cpp 
b/src/plugins/clips-executive/rest-api/clips-executive-rest-api.cpp
index 1ec2f13..ee48853 100644
--- a/src/plugins/clips-executive/rest-api/clips-executive-rest-api.cpp
+++ b/src/plugins/clips-executive/rest-api/clips-executive-rest-api.cpp
@@ -56,25 +56,25 @@ ClipsExecutiveRestApi::init()
        rest_api_ = new WebviewRestApi("clips-executive", logger);
        rest_api_->add_handler<WebviewRestArray<Goal>>
                (WebRequest::METHOD_GET, "/goals",
-                std::bind(&ClipsExecutiveRestApi::cb_list_goals, this, 
std::placeholders::_1));
+                std::bind(&ClipsExecutiveRestApi::cb_list_goals, this));
        rest_api_->add_handler<Goal>
                (WebRequest::METHOD_GET, "/goals/{id}",
                 std::bind(&ClipsExecutiveRestApi::cb_get_goal, this, 
std::placeholders::_1));
        rest_api_->add_handler<WebviewRestArray<DomainOperator>>
                (WebRequest::METHOD_GET, "/domain-operators",
-                std::bind(&ClipsExecutiveRestApi::cb_list_domain_operators, 
this, std::placeholders::_1));
+                std::bind(&ClipsExecutiveRestApi::cb_list_domain_operators, 
this));
        rest_api_->add_handler<WebviewRestArray<DomainObject>>
                (WebRequest::METHOD_GET, "/domain-objects",
-                std::bind(&ClipsExecutiveRestApi::cb_list_domain_objects, 
this, std::placeholders::_1));
+                std::bind(&ClipsExecutiveRestApi::cb_list_domain_objects, 
this));
        rest_api_->add_handler<WebviewRestArray<DomainPredicate>>
                (WebRequest::METHOD_GET, "/domain-predicates",
-                std::bind(&ClipsExecutiveRestApi::cb_list_domain_predicates, 
this, std::placeholders::_1));
+                std::bind(&ClipsExecutiveRestApi::cb_list_domain_predicates, 
this));
        rest_api_->add_handler<WebviewRestArray<DomainFact>>
                (WebRequest::METHOD_GET, "/domain-facts",
-                std::bind(&ClipsExecutiveRestApi::cb_list_domain_facts, this, 
std::placeholders::_1));
+                std::bind(&ClipsExecutiveRestApi::cb_list_domain_facts, this));
        rest_api_->add_handler<WebviewRestArray<Plan>>
                (WebRequest::METHOD_GET, "/plans",
-                std::bind(&ClipsExecutiveRestApi::cb_list_plans, this, 
std::placeholders::_1));
+                std::bind(&ClipsExecutiveRestApi::cb_list_plans, this));
        rest_api_->add_handler<Plan>
                (WebRequest::METHOD_GET, "/plans/{goal-id}/{id}",
                 std::bind(&ClipsExecutiveRestApi::cb_get_plan, this, 
std::placeholders::_1));
@@ -184,12 +184,8 @@ ClipsExecutiveRestApi::generate_goal(CLIPS::Fact::pointer 
fact)
 }
 
 WebviewRestArray<Goal>
-ClipsExecutiveRestApi::cb_list_goals(WebviewRestParams& params)
+ClipsExecutiveRestApi::cb_list_goals()
 {
-       if (params.query_arg("pretty") == "true") {
-               params.set_pretty_json(true);
-       }
-
        MutexLocker lock(clips_.objmutex_ptr());
        WebviewRestArray<Goal> rv;
 
@@ -214,9 +210,6 @@ Goal
 ClipsExecutiveRestApi::cb_get_goal(WebviewRestParams& params)
 {
        const std::string id = params.path_arg("id");
-       if (params.query_arg("pretty") == "true") {
-               params.set_pretty_json(true);
-       }
        
        MutexLocker lock(clips_.objmutex_ptr());
        CLIPS::Fact::pointer fact = clips_->get_facts();
@@ -240,12 +233,8 @@ ClipsExecutiveRestApi::cb_get_goal(WebviewRestParams& 
params)
 }
 
 WebviewRestArray<DomainOperator>
-ClipsExecutiveRestApi::cb_list_domain_operators(WebviewRestParams& params)
+ClipsExecutiveRestApi::cb_list_domain_operators()
 {
-       if (params.query_arg("pretty") == "true") {
-               params.set_pretty_json(true);
-       }
-
        MutexLocker lock(clips_.objmutex_ptr());
        WebviewRestArray<DomainOperator> rv;
 
@@ -297,12 +286,8 @@ 
ClipsExecutiveRestApi::cb_list_domain_operators(WebviewRestParams& params)
 }
 
 WebviewRestArray<DomainObject>
-ClipsExecutiveRestApi::cb_list_domain_objects(fawkes::WebviewRestParams& 
params)
+ClipsExecutiveRestApi::cb_list_domain_objects()
 {
-       if (params.query_arg("pretty") == "true") {
-               params.set_pretty_json(true);
-       }
-
        MutexLocker lock(clips_.objmutex_ptr());
        WebviewRestArray<DomainObject> rv;
 
@@ -324,12 +309,8 @@ 
ClipsExecutiveRestApi::cb_list_domain_objects(fawkes::WebviewRestParams& params)
 }
 
 WebviewRestArray<DomainPredicate>
-ClipsExecutiveRestApi::cb_list_domain_predicates(fawkes::WebviewRestParams& 
params)
+ClipsExecutiveRestApi::cb_list_domain_predicates()
 {
-       if (params.query_arg("pretty") == "true") {
-               params.set_pretty_json(true);
-       }
-
        MutexLocker lock(clips_.objmutex_ptr());
        WebviewRestArray<DomainPredicate> rv;
 
@@ -353,12 +334,8 @@ 
ClipsExecutiveRestApi::cb_list_domain_predicates(fawkes::WebviewRestParams& para
 }
 
 WebviewRestArray<DomainFact>
-ClipsExecutiveRestApi::cb_list_domain_facts(fawkes::WebviewRestParams& params)
+ClipsExecutiveRestApi::cb_list_domain_facts()
 {
-       if (params.query_arg("pretty") == "true") {
-               params.set_pretty_json(true);
-       }
-
        MutexLocker lock(clips_.objmutex_ptr());
        WebviewRestArray<DomainFact> rv;
 
@@ -528,12 +505,8 @@ ClipsExecutiveRestApi::gen_plan(const PlanKey &plan_key,
 
 
 WebviewRestArray<Plan>
-ClipsExecutiveRestApi::cb_list_plans(WebviewRestParams& params)
+ClipsExecutiveRestApi::cb_list_plans()
 {
-       if (params.query_arg("pretty") == "true") {
-               params.set_pretty_json(true);
-       }
-
        MutexLocker lock(clips_.objmutex_ptr());
        WebviewRestArray<Plan> rv;
 
diff --git a/src/plugins/clips-executive/rest-api/clips-executive-rest-api.h 
b/src/plugins/clips-executive/rest-api/clips-executive-rest-api.h
index 36a1258..6fcfbb9 100644
--- a/src/plugins/clips-executive/rest-api/clips-executive-rest-api.h
+++ b/src/plugins/clips-executive/rest-api/clips-executive-rest-api.h
@@ -67,31 +67,18 @@ class ClipsExecutiveRestApi
        typedef std::map<PlanKey, ClipsFactList> PlanActionMap;
 
  private:
-       WebviewRestArray<Goal>
-               cb_list_goals(fawkes::WebviewRestParams& params);
 
-       Goal cb_get_goal(fawkes::WebviewRestParams& params);
-
-       WebviewRestArray<DomainOperator>
-               cb_list_domain_operators(fawkes::WebviewRestParams& params);
-
-       WebviewRestArray<DomainObject>
-               cb_list_domain_objects(fawkes::WebviewRestParams& params);
-
-       WebviewRestArray<DomainPredicate>
-               cb_list_domain_predicates(fawkes::WebviewRestParams& params);
-
-       WebviewRestArray<DomainFact>
-               cb_list_domain_facts(fawkes::WebviewRestParams& params);
-
-       
-       WebviewRestArray<Plan>
-               cb_list_plans(fawkes::WebviewRestParams& params);
+       WebviewRestArray<Goal>            cb_list_goals();
+       WebviewRestArray<DomainOperator>  cb_list_domain_operators();
+       WebviewRestArray<DomainObject>    cb_list_domain_objects();
+       WebviewRestArray<DomainPredicate> cb_list_domain_predicates();
+       WebviewRestArray<DomainFact>      cb_list_domain_facts();
+       WebviewRestArray<Plan>            cb_list_plans();
 
+       Goal cb_get_goal(fawkes::WebviewRestParams& params);
        Plan cb_get_plan(fawkes::WebviewRestParams& params);
 
        Goal generate_goal(CLIPS::Fact::pointer fact);
-
        void gen_plan_precompute(std::map<PlanKey, CLIPS::Fact::pointer> &plans,
                                 std::map<PlanKey, ClipsFactList> &plan_actions,
                                 PreCompoundMap &prec,

- *commit* bcb351a807ceb963546e2ea4aa0d88eef971a2ed - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Mon Apr 9 23:53:03 2018 +0200
Subject: clips: adapt to new "pretty" parameter default handling

 src/plugins/clips/rest-api/clips-rest-api.cpp |   11 ++---------
 src/plugins/clips/rest-api/clips-rest-api.h   |    3 +--
 2 files changed, 3 insertions(+), 11 deletions(-)

_Diff for modified files_:
diff --git a/src/plugins/clips/rest-api/clips-rest-api.cpp 
b/src/plugins/clips/rest-api/clips-rest-api.cpp
index 0667f41..8185be1 100644
--- a/src/plugins/clips/rest-api/clips-rest-api.cpp
+++ b/src/plugins/clips/rest-api/clips-rest-api.cpp
@@ -53,7 +53,7 @@ ClipsRestApi::init()
                 std::bind(&ClipsRestApi::cb_get_facts, this, 
std::placeholders::_1));
        rest_api_->add_handler<WebviewRestArray<Environment>>
                (WebRequest::METHOD_GET, "/",
-                std::bind(&ClipsRestApi::cb_list_environments, this, 
std::placeholders::_1));
+                std::bind(&ClipsRestApi::cb_list_environments, this));
        webview_rest_api_manager->register_api(rest_api_);
 }
 
@@ -180,9 +180,6 @@ ClipsRestApi::gen_fact(LockPtr<CLIPS::Environment>& clips, 
CLIPS::Fact::pointer&
 WebviewRestArray<Fact>
 ClipsRestApi::cb_get_facts(WebviewRestParams& params)
 {
-       if (params.query_arg("pretty") == "true") {
-               params.set_pretty_json(true);
-       }
        bool formatted = (params.query_arg("formatted") == "true");
 
        WebviewRestArray<Fact> rv;
@@ -210,12 +207,8 @@ ClipsRestApi::cb_get_facts(WebviewRestParams& params)
 
 
 WebviewRestArray<Environment>
-ClipsRestApi::cb_list_environments(WebviewRestParams& params)
+ClipsRestApi::cb_list_environments()
 {
-       if (params.query_arg("pretty") == "true") {
-               params.set_pretty_json(true);
-       }
-
        WebviewRestArray<Environment> rv;
 
        MutexLocker lock(clips_env_mgr.objmutex_ptr());
diff --git a/src/plugins/clips/rest-api/clips-rest-api.h 
b/src/plugins/clips/rest-api/clips-rest-api.h
index 7c45cca..a8227ac 100644
--- a/src/plugins/clips/rest-api/clips-rest-api.h
+++ b/src/plugins/clips/rest-api/clips-rest-api.h
@@ -52,14 +52,13 @@ class ClipsRestApi
        virtual void finalize();
 
  private:
+       WebviewRestArray<Environment> cb_list_environments();
        WebviewRestArray<Fact>
                cb_get_facts(fawkes::WebviewRestParams& params);
 
        Fact gen_fact(fawkes::LockPtr<CLIPS::Environment>& clips,
                      CLIPS::Fact::pointer &fact, bool formatted);
 
-       WebviewRestArray<Environment>
-               cb_list_environments(fawkes::WebviewRestParams& params);
 
  private:
        fawkes::WebviewRestApi *rest_api_;

- *commit* fd8c527095b3be456fd0d02cf6b5d967cd019fd3 - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Mon Apr 9 23:53:28 2018 +0200
Subject: skiller: adapt to new "pretty" parameter default handling

 src/plugins/skiller/rest-api/skiller-rest-api.cpp |   25 +++-----------------
 src/plugins/skiller/rest-api/skiller-rest-api.h   |    5 +--
 2 files changed, 6 insertions(+), 24 deletions(-)

_Diff for modified files_:
diff --git a/src/plugins/skiller/rest-api/skiller-rest-api.cpp 
b/src/plugins/skiller/rest-api/skiller-rest-api.cpp
index 8d705a8..a9a19b1 100644
--- a/src/plugins/skiller/rest-api/skiller-rest-api.cpp
+++ b/src/plugins/skiller/rest-api/skiller-rest-api.cpp
@@ -55,7 +55,7 @@ SkillerRestApi::init()
        rest_api_ = new WebviewRestApi("skiller", logger);
        rest_api_->add_handler<WebviewRestArray<SkillInfo>>
                (WebRequest::METHOD_GET, "/skills",
-                std::bind(&SkillerRestApi::cb_list_skills, this, 
std::placeholders::_1));
+                std::bind(&SkillerRestApi::cb_list_skills, this));
        rest_api_->add_handler<Skill>
                (WebRequest::METHOD_GET, "/skills/{id}",
                 std::bind(&SkillerRestApi::cb_get_skill, this, 
std::placeholders::_1));
@@ -64,8 +64,7 @@ SkillerRestApi::init()
                 std::bind(&SkillerRestApi::cb_stop_skill, this, 
std::placeholders::_1));
        rest_api_->add_handler<SkillCall, Skill>
                (WebRequest::METHOD_POST, "/call",
-                std::bind(&SkillerRestApi::cb_exec_skill, this,
-                          std::placeholders::_1, std::placeholders::_2));
+                std::bind(&SkillerRestApi::cb_exec_skill, this, 
std::placeholders::_1));
        webview_rest_api_manager->register_api(rest_api_);
 }
 
@@ -107,12 +106,8 @@ SkillerRestApi::set_and_wait_graph(const char *graph)
 }
 
 WebviewRestArray<SkillInfo>
-SkillerRestApi::cb_list_skills(WebviewRestParams& params)
+SkillerRestApi::cb_list_skills()
 {
-       if (params.query_arg("pretty") == "true") {
-               params.set_pretty_json(true);
-       }
-
        WebviewRestArray<SkillInfo> rv;
 
        skdb_if_->read();
@@ -145,10 +140,6 @@ SkillerRestApi::cb_list_skills(WebviewRestParams& params)
 Skill
 SkillerRestApi::cb_get_skill(WebviewRestParams& params)
 {
-       if (params.query_arg("pretty") == "true") {
-               params.set_pretty_json(true);
-       }
-
        std::string skill_name{params.path_arg("id")};
 
        if (skill_name == "active") {
@@ -197,12 +188,8 @@ SkillerRestApi::cb_get_skill(WebviewRestParams& params)
 
 
 Skill
-SkillerRestApi::cb_exec_skill(const SkillCall &call, 
fawkes::WebviewRestParams& params)
+SkillerRestApi::cb_exec_skill(const SkillCall &call)
 {
-       if (params.query_arg("pretty") == "true") {
-               params.set_pretty_json(true);
-       }
-
        if (! call.skill_string()) {
                throw WebviewRestException(WebReply::HTTP_BAD_REQUEST,
                                           "Request lacks skill string");
@@ -244,10 +231,6 @@ SkillerRestApi::cb_exec_skill(const SkillCall &call, 
fawkes::WebviewRestParams&
 std::unique_ptr<fawkes::WebviewRestReply>
 SkillerRestApi::cb_stop_skill(WebviewRestParams& params)
 {
-       if (params.query_arg("pretty") == "true") {
-               params.set_pretty_json(true);
-       }
-
        std::string skill_name{params.path_arg("id")};
 
        if (skill_name != "active") {
diff --git a/src/plugins/skiller/rest-api/skiller-rest-api.h 
b/src/plugins/skiller/rest-api/skiller-rest-api.h
index 9f6e0fb..2e5804c 100644
--- a/src/plugins/skiller/rest-api/skiller-rest-api.h
+++ b/src/plugins/skiller/rest-api/skiller-rest-api.h
@@ -53,12 +53,11 @@ class SkillerRestApi
        virtual void finalize();
 
  private:
-       WebviewRestArray<SkillInfo>
-               cb_list_skills(fawkes::WebviewRestParams& params);
+       WebviewRestArray<SkillInfo> cb_list_skills();
 
        Skill   cb_get_skill(fawkes::WebviewRestParams& params);
 
-       Skill   cb_exec_skill(const SkillCall &call, fawkes::WebviewRestParams& 
params);
+       Skill   cb_exec_skill(const SkillCall &call);
 
        std::unique_ptr<fawkes::WebviewRestReply>
                cb_stop_skill(fawkes::WebviewRestParams& params);

- *commit* 5e204dc515656db7957ddb5a7dbc1b05bf5b7cf7 - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Mon Apr 9 23:55:30 2018 +0200
Subject: clips-executive-rest-api: comment out add_goal API for now

 src/plugins/clips-executive/rest-api/api.yaml |   40 ++++++++++++------------
 1 files changed, 20 insertions(+), 20 deletions(-)

_Diff for modified files_:
diff --git a/src/plugins/clips-executive/rest-api/api.yaml 
b/src/plugins/clips-executive/rest-api/api.yaml
index 6fda15e..f02d0de 100644
--- a/src/plugins/clips-executive/rest-api/api.yaml
+++ b/src/plugins/clips-executive/rest-api/api.yaml
@@ -43,26 +43,26 @@ paths:
         '400':
           description: bad input parameter
 
-    post:
-      tags:
-      - clips-executive
-      summary: Add a new goal.
-      operationId: add_goal
-      description: |
-        Add a new goal.
-      requestBody:
-        content:
-          application/json:
-            schema:
-              $ref: '#/components/schemas/Goal'
-        description: Inventory item to add
-      responses:
-        '201':
-          description: item created
-        '400':
-          description: 'invalid input, object invalid'
-        '409':
-          description: an existing item already exists
+    # post:
+    #   tags:
+    #   - clips-executive
+    #   summary: Add a new goal.
+    #   operationId: add_goal
+    #   description: |
+    #     Add a new goal.
+    #   requestBody:
+    #     content:
+    #       application/json:
+    #         schema:
+    #           $ref: '#/components/schemas/Goal'
+    #     description: Inventory item to add
+    #   responses:
+    #     '201':
+    #       description: item created
+    #     '400':
+    #       description: 'invalid input, object invalid'
+    #     '409':
+    #       description: an existing item already exists
 
   /clips-executive/goals/{id}:
     get:

- *commit* dcc41e581bd5889e28b62f2e8eba192ac30b5285 - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Mon Apr 9 23:59:25 2018 +0200
Subject: webview: add backend info REST API

 src/plugins/webview/Makefile                       |    3 +-
 .../Makefile                                       |    0
 src/plugins/webview/backendinfo-rest-api/api.yaml  |   82 +++++++++++++++
 .../backendinfo-rest-api/backendinfo-rest-api.cpp  |  107 ++++++++++++++++++++
 .../backendinfo-rest-api/backendinfo-rest-api.h}   |   38 ++-----
 .../backendinfo-rest-api/model/Backend.cpp}        |   84 ++++++++--------
 .../backendinfo-rest-api/model/Backend.h}          |  106 ++++++++++----------
 .../backendinfo-rest-api/model/Service.cpp}        |   40 ++++----
 .../backendinfo-rest-api/model/Service.h}          |   36 ++++----
 src/plugins/webview/webview_plugin.cpp             |    6 +-
 10 files changed, 341 insertions(+), 161 deletions(-)

_Diff for modified files_:
diff --git a/src/plugins/webview/Makefile b/src/plugins/webview/Makefile
index a38965f..99fded0 100644
--- a/src/plugins/webview/Makefile
+++ b/src/plugins/webview/Makefile
@@ -96,7 +96,8 @@ ifeq ($(HAVE_BOOST_LIBS)$(HAVE_LIBMICROHTTPD),11)
     LDFLAGS += $(LDFLAGS_CPP17) $(LDFLAGS_RAPIDJSON)
 
     OBJS_webview += blackboard-rest-api/blackboard-rest-api.o \
-                   $(patsubst %.cpp,%.o,$(subst $(SRCDIR)/,,$(realpath 
$(wildcard $(SRCDIR)/blackboard-rest-api/model/*.cpp))))
+                    backendinfo-rest-api/backendinfo-rest-api.o \
+                    $(patsubst %.cpp,%.o,$(subst $(SRCDIR)/,,$(realpath 
$(wildcard $(SRCDIR)/blackboard-rest-api/model/*.cpp 
$(SRCDIR)/backendinfo-rest-api/model/*.cpp))))
 
     ifeq ($(HAVE_JPEG),1)
       CFLAGS += -DHAVE_JPEG
diff --git a/src/plugins/webview/webview_plugin.cpp 
b/src/plugins/webview/webview_plugin.cpp
index 4e16c20..cc2ef31 100644
--- a/src/plugins/webview/webview_plugin.cpp
+++ b/src/plugins/webview/webview_plugin.cpp
@@ -27,6 +27,7 @@
 
 #ifdef HAVE_REST_APIS
 #  include "blackboard-rest-api/blackboard-rest-api.h"
+#  include "backendinfo-rest-api/backendinfo-rest-api.h"
 #  ifdef HAVE_JPEG
 #    include "image-rest-api/image-rest-api.h"
 #  endif
@@ -50,9 +51,12 @@ WebviewPlugin::WebviewPlugin(Configuration *config)
                config->get_bool("/webview/thread-pool/enable");
 
   thread_list.push_back(new WebviewThread(enable_thread_pool));
+#ifdef HAVE_REST_APIS
   thread_list.push_back(new BlackboardRestApi());
-#ifdef HAVE_JPEG
+  thread_list.push_back(new BackendInfoRestApi());
+#  ifdef HAVE_JPEG
   thread_list.push_back(new ImageRestApi());
+#  endif
 #endif
 }
 

- *commit* edad2d54a1ccbcdaf1d69aced2ce23bef6f7e91c - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Tue Apr 10 00:02:09 2018 +0200
Subject: webview-frontend: replace basic config with full backend config

 src/plugins/webview/frontend/src/app/app.module.ts |    4 +-
 .../backend-config/backend-config.service.ts       |  204 ++++++++++++++++++++
 .../backend-config/model/Backend.ts}               |   16 +-
 .../backend-config/model/Service.ts}               |   15 +-
 .../frontend/src/services/config.service.ts        |   58 ------
 5 files changed, 224 insertions(+), 73 deletions(-)

_Diff for modified files_:
diff --git a/src/plugins/webview/frontend/src/app/app.module.ts 
b/src/plugins/webview/frontend/src/app/app.module.ts
index bf7159a..5c89afc 100644
--- a/src/plugins/webview/frontend/src/app/app.module.ts
+++ b/src/plugins/webview/frontend/src/app/app.module.ts
@@ -13,7 +13,7 @@ import { environment } from '../environments/environment';
 import { AppRoutingModule } from './app-routing.module';
 import { AppComponent } from './app.component';
 import { ChromeModule } from '../chrome/module';
-import { ConfigurationService } from '../services/config.service';
+import { BackendConfigurationService } from 
'../services/backend-config/backend-config.service';
 import { AssetsService } from '../services/global/assets';
 
 import { ClipsExecutiveModule } from '../parts/clips-executive/module';
@@ -45,7 +45,7 @@ import { HttpClientInMemoryWebApiModule } from 
'angular-in-memory-web-api';
          // Keep the AppRoutingModule last
          AppRoutingModule,
        ],
-  providers: [ConfigurationService, AssetsService],
+  providers: [BackendConfigurationService, AssetsService],
        bootstrap: [AppComponent]
 })
 export class AppModule { }

- *commit* c5ead7c94daaf9d6427721475fe88ad2bc3e10df - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Tue Apr 10 00:08:03 2018 +0200
Subject: webview-frontend: add chrome to enable switching backends

 .../webview/frontend/src/chrome/component.ts       |   19 +++++++-
 src/plugins/webview/frontend/src/chrome/style.scss |   15 ++++++
 .../webview/frontend/src/chrome/template.html      |   50 ++++++--------------
 3 files changed, 48 insertions(+), 36 deletions(-)

_Diff for modified files_:
diff --git a/src/plugins/webview/frontend/src/chrome/component.ts 
b/src/plugins/webview/frontend/src/chrome/component.ts
index 6ce5884..996e198 100644
--- a/src/plugins/webview/frontend/src/chrome/component.ts
+++ b/src/plugins/webview/frontend/src/chrome/component.ts
@@ -8,6 +8,8 @@ import {Component} from '@angular/core';
 //import {NotificationsService} from '../common/services/global/notifications';
 import { AssetsService } from '../services/global/assets';
 
+import { BackendConfigurationService } from 
'../services/backend-config/backend-config.service';
+
 @Component({
   selector: 'ff-chrome',
   templateUrl: './template.html',
@@ -16,7 +18,9 @@ import { AssetsService } from '../services/global/assets';
 export class ChromeComponent {
   loading = false;
 
-  constructor(public assets: AssetsService) {}
+  constructor(public assets: AssetsService,
+              public backend_conf: BackendConfigurationService)
+  {}
 
   isSystemBannerVisible(): boolean {
     return false;
@@ -29,4 +33,17 @@ export class ChromeComponent {
   getSystemBannerMessage(): string {
     return `<b>System is going to be shut down in 5 min...</b>`;
   }
+
+  number_symbol(num: number)
+  {
+    switch (num+1) {
+      case 1: return 'looks_one';
+      case 2: return 'looks_two';
+      case 3: return 'looks_3';
+      case 4: return 'looks_4';
+      case 5: return 'looks_5';
+      case 6: return 'looks_6';
+      default: return 'add_box';
+    }   
+  }
 }
diff --git a/src/plugins/webview/frontend/src/chrome/style.scss 
b/src/plugins/webview/frontend/src/chrome/style.scss
index eb20647..576a47d 100644
--- a/src/plugins/webview/frontend/src/chrome/style.scss
+++ b/src/plugins/webview/frontend/src/chrome/style.scss
@@ -95,3 +95,18 @@
     //padding-bottom: 2 * $baseline-grid;
   }
 }
+
+.backend-name {
+               font-size: $title-font-size-base;
+}
+
+.select-icon {
+               vertical-align: middle;
+               padding-bottom: 2px;
+}
+
+.cloud-icon {
+               vertical-align: middle;
+               padding-bottom: 4px;
+               margin-right: .5 * $baseline-grid;
+}
diff --git a/src/plugins/webview/frontend/src/chrome/template.html 
b/src/plugins/webview/frontend/src/chrome/template.html
index fcd32f6..5b61199 100644
--- a/src/plugins/webview/frontend/src/chrome/template.html
+++ b/src/plugins/webview/frontend/src/chrome/template.html
@@ -7,41 +7,6 @@
 <div fxLayout="column"
      fxFill
      fxFlexFill>
-<!--
-  <mat-toolbar class="ff-toolbar ff-primary-toolbar">
-    <div class="ff-toolbar-tools"
-         fxLayout="row"
-         fxFlex="auto">
-      <div class="kd-logo-bar">
-        <a uiSref="{{getOverviewStateName()}}"
-           class="kd-toolbar-logo-link">
-          <mat-icon [svgIcon]="assets.getAppLogo()"
-                    class="kd-toolbar-logo">
-          </mat-icon>
-          <mat-icon [svgIcon]="assets.getAppLogoText()"
-                    class="kd-toolbar-logo-text">
-          </mat-icon>
-        </a>
-      </div>
-      <kd-search class="kd-search-bar"
-                 fxFlex>
-      </kd-search>
-      <div fxLayout="row">
-        <button mat-icon-button
-                color="primary"
-                class="kd-primary-button kd-global-action-margin"
-                i18n-matTooltip
-                matTooltip="Create any Kubernetes resource"
-                (click)="goToCreateState()">
-          <mat-icon>add</mat-icon>
-        </button>
-        <kd-notifications class="kd-global-action-margin"></kd-notifications>
-        <kd-user-panel class="kd-global-action-margin"></kd-user-panel>
-      </div>
-    </div>
-  </mat-toolbar>
-       -->
-
   <mat-toolbar class="ff-second-toolbar ff-bg-primary">
     <div class="ff-toolbar-tools"
          fxFlex>
@@ -53,6 +18,21 @@
           </mat-icon>
         </a>
       </div>
+                       <div class="ff-right" fxFlexLayout="row">
+                               <button mat-button 
[matMenuTriggerFor]="backend_menu"
+                                                               color="accent" 
class="backend-name">
+                                       <mat-icon 
class="cloud-icon">android</mat-icon>
+                                       {{ backend_conf.backend_name }}
+                                       <mat-icon 
class="select-icon">arrow_drop_down</mat-icon>
+                               </button>
+                               <mat-menu #backend_menu="matMenu">
+                                       <button mat-menu-item
+                                                                       
*ngFor="let backend of backend_conf.backend_list; let i = index"
+                                                                               
(click)="backend_conf.set_backend(backend.id)">
+                                               <mat-icon>{{ number_symbol(i) 
}}</mat-icon> {{ backend.name }}
+                                               </button>
+                               </mat-menu>
+                       </div>
       <!-- <ff-breadcrumbs class="ff-actionbar-breadcrumbs"></ff-breadcrumbs>
       <ff-actionbar fxFlex="end"></ff-actionbar> -->
     </div>

- *commit* d49761586d7c9693a74123c7e23674956393e009 - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Tue Apr 10 00:09:47 2018 +0200
Subject: webview-frontend: adapt parts to refresh on backend change

 .../blackboard/components/overview.component.html  |   38 ++++++------
 .../blackboard/components/overview.component.ts    |   64 +++++++++++++++++---
 .../clips-executive/components/domain.component.ts |   28 ++++++---
 .../components/goal-detail.component.ts            |   33 +++++++----
 .../components/goal-list.component.ts              |   19 +++++-
 .../parts/clips/components/clips-env.component.ts  |   26 ++++++--
 .../dashboard/components/dashboard.component.ts    |   23 ++++---
 .../parts/images/components/overview.component.ts  |   19 ++++--
 .../parts/skiller/components/overview.component.ts |   21 ++++++-
 9 files changed, 196 insertions(+), 75 deletions(-)

_Diff for modified files_:
diff --git 
a/src/plugins/webview/frontend/src/parts/blackboard/components/overview.component.html
 
b/src/plugins/webview/frontend/src/parts/blackboard/components/overview.component.html
index babb85f..98d2f3e 100644
--- 
a/src/plugins/webview/frontend/src/parts/blackboard/components/overview.component.html
+++ 
b/src/plugins/webview/frontend/src/parts/blackboard/components/overview.component.html
@@ -57,18 +57,18 @@
                        <mat-card class="interface-card" fxFlex="32" 
*ngFor="let i of selected_interfaces">
                                <mat-card-title>
                                        <div fxLayout="row">
-                                               <div>{{ interfaces[i[0]].type 
}}::{{ interfaces[i[0]].instances[i[1]].info.id }}</div>
+                                               <div>{{ known_types[i[0]] 
}}::{{ i[1] }}</div>
                                                <div class="ff-right">
                                                        <button mat-icon-button
                                                                                
        class="ff-card-title-button"
-                                                                               
        [disabled]="interfaces[i[0]].instances[i[1]].autosub != null || 
interfaces[i[0]].instances[i[1]].loading"
+                                                                               
        [disabled]="! iok(i) || ifc(i).loading"
                                                                                
        (click)="refresh_data(i[0], i[1])">
-                                                               <mat-icon 
*ngIf="!interfaces[i[0]].instances[i[1]].loading">refresh</mat-icon>
-                                                               <mat-spinner 
*ngIf="interfaces[i[0]].instances[i[1]].loading" [diameter]="20"></mat-spinner>
+                                                               <mat-icon 
*ngIf="iok(i) && !ifc(i).loading">refresh</mat-icon>
+                                                               <mat-spinner 
*ngIf="iok(i) && ifc(i).loading" [diameter]="20"></mat-spinner>
                                                        </button>
                                                        <button mat-icon-button
                                                                                
        class="ff-card-title-button"
-                                                                               
        [disabled]="!interfaces[i[0]].instances[i[1]].enabled"
+                                                                               
        [disabled]="! iok(i) || ! ifc(i).enabled"
                                                                                
        (click)="toggle_autorefresh()">
                                                                <mat-icon 
[ngClass]="{'ff-warning': auto_refresh_subscription != 
null}">autorenew</mat-icon>
                                                        </button>
@@ -81,17 +81,17 @@
                                        </div>
                                </mat-card-title>
                                <mat-divider></mat-divider>
-                               <div 
*ngIf="interfaces[i[0]].instances[i[1]].info"
+                               <div *ngIf="iok(i) && ifc(i).info"
                                                 fxLayout="column" 
class="reader-writer-info">
                                        <ff-property direction="vertical">
                                                <div key>Writer</div>
                                                <div value>
-                                                       <mat-chip-list 
*ngIf="interfaces[i[0]].instances[i[1]].info.writer">
+                                                       <mat-chip-list 
*ngIf="ifc(i).info.writer">
                                                                <mat-chip 
color="primary">
-                                                                       {{ 
interfaces[i[0]].instances[i[1]].info.writer }}
+                                                                       {{ 
ifc(i).info.writer }}
                                                                </mat-chip>
                                                        </mat-chip-list>
-                                                       <ng-container 
*ngIf="!interfaces[i[0]].instances[i[1]].info.writer">
+                                                       <ng-container 
*ngIf="!ifc(i).info.writer">
                                                                &mdash;
                                                        </ng-container>
                                                </div>
@@ -100,11 +100,11 @@
                                                <div key>Readers</div>
                                                <div value>
                                                        <mat-chip-list>
-                                                               <mat-chip 
*ngFor="let r of interfaces[i[0]].instances[i[1]].info.readers">
+                                                               <mat-chip 
*ngFor="let r of ifc(i).info.readers">
                                                                        {{ r }}
                                                                </mat-chip>
                                                        </mat-chip-list>
-                                                       <ng-container 
*ngIf="interfaces[i[0]].instances[i[1]].info.readers.length == 0">
+                                                       <ng-container 
*ngIf="ifc(i).info.readers.length == 0">
                                                                &mdash;
                                                        </ng-container>
                                                </div>
@@ -112,10 +112,10 @@
                                        <ff-property direction="vertical">
                                                <div key>Last Updated</div>
                                                <div value>
-                                                       <div 
*ngIf="interfaces[i[0]].instances[i[1]].data.timestamp != '0:0'">
-                                                               {{ 
interfaces[i[0]].instances[i[1]].data.timestamp }}
+                                                       <div *ngIf="iok(i) && 
ifc(i).data && ifc(i).data.timestamp != '0:0'">
+                                                               {{ 
ifc(i).data.timestamp }}
                                                        </div>
-                                                       <div 
*ngIf="interfaces[i[0]].instances[i[1]].data.timestamp == '0:0'">
+                                                       <div *ngIf="! iok(i) || 
! ifc(i).data || ifc(i).data.timestamp == '0:0'">
                                                                <span 
color="red">never</span>
                                                        </div>
                                                </div>
@@ -123,20 +123,20 @@
                                </div>
                                <mat-divider></mat-divider>
                                <mat-card-content class="ff-card-content 
maybe-disabled-card-content">
-                                       <div 
*ngIf="interfaces[i[0]].instances[i[1]].data"
+                                       <div *ngIf="iok(i) && ifc(i).data"
                                                         fxLayout="row wrap" 
fxLayoutAlign="space-between">
-                                               <div 
*ngIf="!interfaces[i[0]].instances[i[1]].enabled" class="disabled-overlay"
+                                               <div *ngIf="!ifc(i).enabled" 
class="disabled-overlay"
                                                                 fxLayout="row" 
fxLayoutAlign="center center">
                                                        <div>Not available</div>
                                                </div>
 
                                                <ff-property  
direction="vertical"
-                                                                               
                        *ngFor="let f of 
keys(interfaces[i[0]].instances[i[1]].data.data)">
+                                                                               
                        *ngFor="let f of keys(ifc(i).data.data)">
                                                        <div key>{{ f }}</div>
-                                                       <div value>{{ 
interfaces[i[0]].instances[i[1]].data.data[f] }}</div>
+                                                       <div value>{{ 
ifc(i).data.data[f] }}</div>
                                                </ff-property>
                                        </div>
-                                       <div 
*ngIf="!interfaces[i[0]].instances[i[1]].data">
+                                       <div *ngIf="!iok(i) || !ifc(i).data">
                                                <ff-list-zero-state message="No 
data received"></ff-list-zero-state>
                                        </div>
                                </mat-card-content>
diff --git 
a/src/plugins/webview/frontend/src/parts/blackboard/components/overview.component.ts
 
b/src/plugins/webview/frontend/src/parts/blackboard/components/overview.component.ts
index e35d45f..0833c51 100644
--- 
a/src/plugins/webview/frontend/src/parts/blackboard/components/overview.component.ts
+++ 
b/src/plugins/webview/frontend/src/parts/blackboard/components/overview.component.ts
@@ -2,10 +2,11 @@
 // Copyright  2018  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
 // License: Apache 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, OnDestroy } from '@angular/core';
 import { Observable } from 'rxjs/Observable';
 import 'rxjs/add/observable/interval';
 
+import { BackendConfigurationService } from 
'../../../services/backend-config/backend-config.service';
 import { BlackboardApiService } from '../services/api.service';
 import { InterfaceInfo } from '../models/InterfaceInfo';
 
@@ -14,25 +15,59 @@ import { InterfaceInfo } from '../models/InterfaceInfo';
   templateUrl: './overview.component.html',
   styleUrls: ['./overview.component.scss']
 })
-export class BlackboardOverviewComponent implements OnInit {
+export class BlackboardOverviewComponent implements OnInit, OnDestroy {
+
+  private backend_subscription = null;
 
   loading = false;
   auto_refresh_subscription = null;
   selected_interfaces = [];
   interfaces = null;
   zero_message = "No graph has been retrieved";
+
+  known_types = {};
   
-  constructor(private api_service: BlackboardApiService)
+  constructor(private api_service: BlackboardApiService,
+              private backendcfg: BackendConfigurationService)
   {}
 
   ngOnInit() {
     this.refresh();
+    this.backend_subscription = this.backendcfg.backend_changed.subscribe((b) 
=> {
+      this.refresh(true);
+    });
+  }
+
+  ngOnDestroy()
+  {
+    this.backend_subscription.unsubscribe();
+    this.backend_subscription = null;
   }
 
   keys(obj) {
     return Object.keys(obj);
   }
 
+  /** Check if interface is OK.
+   * An interface is ok if it is in the list of interfaces received from the 
remote.
+   * @param id_hash array of strings with two entries, id and hash
+   * @return true if interface can be used, false otherwise
+   */
+  iok(hash_id: string[]): boolean {
+    return ( this.interfaces &&
+             hash_id[0] in this.interfaces &&
+             hash_id[1] in this.interfaces[hash_id[0]].instances );
+  }
+
+  /** Get interface.
+   * Assumes that the interface validity has been checked before with iok().
+   * @param hash_id array of strings with two entries, id and hash
+   * @return interface instance descriptor
+   */
+  ifc(hash_id: string[]) {
+    return this.interfaces[hash_id[0]].instances[hash_id[1]];
+  }
+
   indexof_selected_interface(hash: string, id: string)
   {
     return this.selected_interfaces.findIndex(
@@ -59,9 +94,8 @@ export class BlackboardOverviewComponent implements OnInit {
 
   refresh_data(hash: string, id: string)
   {
-    if (! this.interfaces[hash] || ! this.interfaces[hash].instances[id]) {
-      return;
-    }
+    if (! this.iok([hash, id])) return;
+
     this.interfaces[hash].instances[id].loading = true;
     this.zero_message = "Retrieving interface data";
 
@@ -73,13 +107,15 @@ export class BlackboardOverviewComponent implements OnInit 
{
         this.interfaces[hash].instances[id].loading = false;
       },
       (err) => {
-        this.interfaces[hash].instances[id].enabled = false;
-        this.interfaces[hash].instances[id].loading = false;
+        if (this.iok([hash, id])) {
+          this.interfaces[hash].instances[id].enabled = false;
+          this.interfaces[hash].instances[id].loading = false;
+        }
       }
     );
   }
   
-  refresh()
+  refresh(refresh_data = false)
   {
     this.loading = true;
     this.zero_message = "Retrieving interface info";
@@ -92,6 +128,9 @@ export class BlackboardOverviewComponent implements OnInit {
           ifs = this.interfaces;
         }
         for (let i of interfaces) {
+          if (! (i.hash in this.known_types)) {
+            this.known_types[i.hash] = i.type;
+          }
           if (ifs[i.hash]) {
             if (ifs[i.hash].instances[i.id]) {
               ifs[i.hash].instances[i.id].info = i;
@@ -118,6 +157,13 @@ export class BlackboardOverviewComponent implements OnInit 
{
           }
         }
         this.interfaces = ifs;
+
+        if (refresh_data) {
+          for (let hash_id of this.selected_interfaces) {
+            this.refresh_data(hash_id[0], hash_id[1]);
+          }
+        }
+
         this.loading = false;
       },
       (err) => {
diff --git 
a/src/plugins/webview/frontend/src/parts/clips-executive/components/domain.component.ts
 
b/src/plugins/webview/frontend/src/parts/clips-executive/components/domain.component.ts
index 2bca4c3..7dcf157 100644
--- 
a/src/plugins/webview/frontend/src/parts/clips-executive/components/domain.component.ts
+++ 
b/src/plugins/webview/frontend/src/parts/clips-executive/components/domain.component.ts
@@ -2,16 +2,15 @@
 // Copyright  2018  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
 // License: Apache 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, OnDestroy } from '@angular/core';
 import { Router, ActivatedRoute, ParamMap } from '@angular/router';
 import { Observable } from 'rxjs/Observable';
-import { catchError, tap, switchMap } from 'rxjs/operators';
 import 'rxjs/add/observable/forkJoin';
 import 'rxjs/add/observable/interval';
-import 'rxjs/add/operator/catch';
-import { HttpClient, HttpHeaders } from '@angular/common/http';
+import 'rxjs/add/operator/map';
+
+import { BackendConfigurationService } from 
'../../../services/backend-config/backend-config.service';
 
-import { ConfigurationService } from '../../../services/config.service';
 import { ClipsExecutiveApiService } from '../services/api.service';
 import { Goal } from '../models/Goal';
 import { Plan } from '../models/Plan';
@@ -29,9 +28,11 @@ import { DomainPreconditionCompound } from 
'../models/DomainPreconditionCompound
 })
 export class DomainComponent implements OnInit {
 
-  constructor(private config: ConfigurationService,
-              private http: HttpClient,
-              private api_service : ClipsExecutiveApiService) { }
+  constructor(private api_service : ClipsExecutiveApiService,
+              private backendcfg: BackendConfigurationService)
+  {}
+
+  private backend_subscription = null;
 
   loading = false;
 
@@ -51,6 +52,17 @@ export class DomainComponent implements OnInit {
   ngOnInit() {
     this.reset_domain_data();
     this.refresh_domain_data();
+
+    this.backend_subscription = this.backendcfg.backend_changed.subscribe((b) 
=> {
+      this.reset_domain_data();
+      this.refresh_domain_data();
+    });
+  }
+
+  ngOnDestroy()
+  {
+    this.backend_subscription.unsubscribe();
+    this.backend_subscription = null;
   }
 
   reset_domain_data()
diff --git 
a/src/plugins/webview/frontend/src/parts/clips-executive/components/goal-detail.component.ts
 
b/src/plugins/webview/frontend/src/parts/clips-executive/components/goal-detail.component.ts
index 723c423..da878c5 100644
--- 
a/src/plugins/webview/frontend/src/parts/clips-executive/components/goal-detail.component.ts
+++ 
b/src/plugins/webview/frontend/src/parts/clips-executive/components/goal-detail.component.ts
@@ -2,16 +2,15 @@
 // Copyright  2018  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
 // License: Apache 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, OnDestroy } from '@angular/core';
 import { Router, ActivatedRoute, ParamMap } from '@angular/router';
 import { Observable } from 'rxjs/Observable';
-import { catchError, tap, switchMap } from 'rxjs/operators';
+import { tap, switchMap } from 'rxjs/operators';
 import 'rxjs/add/observable/forkJoin';
 import 'rxjs/add/observable/interval';
-import 'rxjs/add/operator/catch';
-import { HttpClient, HttpHeaders } from '@angular/common/http';
+import 'rxjs/add/operator/map';
 
-import { ConfigurationService } from '../../../services/config.service';
+import { BackendConfigurationService } from 
'../../../services/backend-config/backend-config.service';
 import { ClipsExecutiveApiService } from '../services/api.service';
 import { Goal } from '../models/Goal';
 import { Plan } from '../models/Plan';
@@ -27,12 +26,14 @@ import { DomainPreconditionCompound } from 
'../models/DomainPreconditionCompound
   templateUrl: './goal-detail.component.html',
   styleUrls: ['./goal-detail.component.scss']
 })
-export class GoalDetailComponent implements OnInit {
+export class GoalDetailComponent implements OnInit, OnDestroy {
 
-  constructor(private config: ConfigurationService,
-              private route: ActivatedRoute,
-              private http: HttpClient,
-              private api_service : ClipsExecutiveApiService) { }
+  private backend_subscription = null;
+
+  constructor(private route: ActivatedRoute,
+              private api_service : ClipsExecutiveApiService,
+              private backendcfg: BackendConfigurationService)
+  {}
 
   id = "";
   loading_goal   = false;
@@ -51,8 +52,18 @@ export class GoalDetailComponent implements OnInit {
   displayedPlanActionColumns = [ "operator_name", "params", "status", 
"executable", "preconditions" ];
 
   ngOnInit() {
-    this.refresh_goal();
     this.refresh_domain();
+    this.refresh_goal();
+    this.backend_subscription = this.backendcfg.backend_changed.subscribe((b) 
=> {
+      this.refresh_domain();
+      this.refresh_goal();
+    });
+  }
+
+  ngOnDestroy()
+  {
+    this.backend_subscription.unsubscribe();
+    this.backend_subscription = null;
   }
 
   refresh_domain()
diff --git 
a/src/plugins/webview/frontend/src/parts/clips-executive/components/goal-list.component.ts
 
b/src/plugins/webview/frontend/src/parts/clips-executive/components/goal-list.component.ts
index 0f63cdb..f558d29 100644
--- 
a/src/plugins/webview/frontend/src/parts/clips-executive/components/goal-list.component.ts
+++ 
b/src/plugins/webview/frontend/src/parts/clips-executive/components/goal-list.component.ts
@@ -2,10 +2,11 @@
 // Copyright  2018  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
 // License: Apache 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, OnDestroy } from '@angular/core';
 import { Router } from '@angular/router';
 import { MatTableDataSource } from '@angular/material';
 
+import { BackendConfigurationService } from 
'../../../services/backend-config/backend-config.service';
 import { ClipsExecutiveApiService } from '../services/api.service';
 import { Goal } from '../models/Goal';
 
@@ -17,7 +18,9 @@ import 'rxjs/add/observable/interval';
   templateUrl: './goal-list.component.html',
   styleUrls: ['./goal-list.component.scss']
 })
-export class GoalListComponent implements OnInit {
+export class GoalListComponent implements OnInit, OnDestroy {
+
+  private backend_subscription = null;
 
   dataSource = new MatTableDataSource();
   displayedColumns = ['mode', 'id', 'type', 'class'];
@@ -32,11 +35,19 @@ export class GoalListComponent implements OnInit {
   zero_message = "No goals received.";
   
   constructor(private readonly api_service: ClipsExecutiveApiService,
-              private router : Router)
-  { }
+              private router : Router,
+              private backendcfg: BackendConfigurationService)
+  {}
 
   ngOnInit() {
     this.refresh();
+    this.backend_subscription = this.backendcfg.backend_changed.subscribe((b) 
=> { this.refresh() });
+  }
+
+  ngOnDestroy()
+  {
+    this.backend_subscription.unsubscribe();
+    this.backend_subscription = null;
   }
 
   refresh()
diff --git 
a/src/plugins/webview/frontend/src/parts/clips/components/clips-env.component.ts
 
b/src/plugins/webview/frontend/src/parts/clips/components/clips-env.component.ts
index 8e3ff62..20e37fc 100644
--- 
a/src/plugins/webview/frontend/src/parts/clips/components/clips-env.component.ts
+++ 
b/src/plugins/webview/frontend/src/parts/clips/components/clips-env.component.ts
@@ -2,25 +2,26 @@
 // Copyright  2018  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
 // License: Apache 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 
-import { Component, OnInit, ViewChild } from '@angular/core';
+import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
 import { Router, ActivatedRoute, ParamMap } from '@angular/router';
 import { MatTableDataSource } from '@angular/material';
 
+import { BackendConfigurationService } from 
'../../../services/backend-config/backend-config.service';
 import { CardListFilterComponent } from '../../../components/filter/component';
 import { ClipsApiService } from '../services/api.service';
 import { Fact } from '../models/Fact';
 
 import { Observable } from 'rxjs/Observable';
 import 'rxjs/add/observable/interval';
-import 'rxjs/add/operator/switchMap';
-import { catchError, tap } from 'rxjs/operators';
 
 @Component({
   selector: 'clips-env',
   templateUrl: './clips-env.component.html',
   styleUrls: ['./clips-env.component.scss']
 })
-export class ClipsEnvComponent implements OnInit {
+export class ClipsEnvComponent implements OnInit, OnDestroy {
+
+  private backend_subscription = null;
 
   displayed_columns = ['index', 'formatted'];
 
@@ -38,9 +39,9 @@ export class ClipsEnvComponent implements OnInit {
 
   constructor(private readonly api_service: ClipsApiService,
               private route: ActivatedRoute,
-              private router: Router)
-  {
-  }
+              private router: Router,
+              private backendcfg: BackendConfigurationService)
+  {}
 
   ngOnInit() {
 
@@ -57,6 +58,17 @@ export class ClipsEnvComponent implements OnInit {
       .subscribe((query: string) => {
         this.apply_filter(query)
       });
+
+    this.backend_subscription = this.backendcfg.backend_changed.subscribe((b) 
=> {
+      this.refresh_envs();
+      this.refresh();
+    });
+  }
+
+  ngOnDestroy()
+  {
+    this.backend_subscription.unsubscribe();
+    this.backend_subscription = null;
   }
 
   refresh()
diff --git 
a/src/plugins/webview/frontend/src/parts/dashboard/components/dashboard.component.ts
 
b/src/plugins/webview/frontend/src/parts/dashboard/components/dashboard.component.ts
index 51da302..7cd206c 100644
--- 
a/src/plugins/webview/frontend/src/parts/dashboard/components/dashboard.component.ts
+++ 
b/src/plugins/webview/frontend/src/parts/dashboard/components/dashboard.component.ts
@@ -2,21 +2,21 @@
 // Copyright  2018  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
 // License: Apache 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 
-import { Component, OnInit, ViewChild } from '@angular/core';
-import { Router, ActivatedRoute, ParamMap } from '@angular/router';
+import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
 import { MatTableDataSource } from '@angular/material';
-
 import { Observable } from 'rxjs/Observable';
 import 'rxjs/add/observable/interval';
 
-//import { DashboardApiService } from '../services/api.service';
+import { BackendConfigurationService } from 
'../../../services/backend-config/backend-config.service';
 
 @Component({
   selector: 'dashboard',
   templateUrl: './dashboard.component.html',
   styleUrls: ['./dashboard.component.scss']
 })
-export class DashboardComponent implements OnInit {
+export class DashboardComponent implements OnInit, OnDestroy {
+
+  private backend_subscription = null;
 
   auto_refresh_subscription = null;
   loading = false;
@@ -29,12 +29,17 @@ export class DashboardComponent implements OnInit {
 
   query_mem = 'namedprocess_namegroup_memory_bytes{memtype="resident"}';
 
-  constructor(//private readonly api_service: DashboardApiService,
-              private router: Router)
-  {
-  }
+  constructor(private backendcfg: BackendConfigurationService)
+  {}
 
   ngOnInit() {
+    this.backend_subscription = this.backendcfg.backend_changed.subscribe((b) 
=> { this.refresh() });
+  }
+
+  ngOnDestroy()
+  {
+    this.backend_subscription.unsubscribe();
+    this.backend_subscription = null;
   }
 
   refresh()
diff --git 
a/src/plugins/webview/frontend/src/parts/images/components/overview.component.ts
 
b/src/plugins/webview/frontend/src/parts/images/components/overview.component.ts
index 398c876..1f67245 100644
--- 
a/src/plugins/webview/frontend/src/parts/images/components/overview.component.ts
+++ 
b/src/plugins/webview/frontend/src/parts/images/components/overview.component.ts
@@ -9,7 +9,7 @@ import 'rxjs/add/observable/interval';
 import { ImageApiService } from '../services/api.service';
 import { ImageInfo } from '../models/ImageInfo';
 
-import { ConfigurationService } from '../../../services/config.service';
+import { BackendConfigurationService } from 
'../../../services/backend-config/backend-config.service';
 
 @Component({
   selector: 'blackboard-overview',
@@ -49,20 +49,29 @@ export class ImageOverviewComponent implements OnInit, 
OnDestroy {
   image_src: string = null;
   image_selected: string[] = null;
   image_on_blur: string[] = null;
-  
-  constructor(private config: ConfigurationService,
-              private api_service: ImageApiService)
+
+  private backend_subscription = null;
+
+  constructor(private api_service: ImageApiService,
+              private backendcfg: BackendConfigurationService)
   {}
 
   ngOnInit() {
     this.refresh();
     this.image_on_blur = null;
+    this.backend_subscription = this.backendcfg.backend_changed.subscribe((b) 
=> {
+      this.disable_autorefresh();
+      this.deselect_image();
+      this.refresh();
+    });
   }
 
   ngOnDestroy()
   {
     this.disable_autorefresh();
     this.deselect_image();
+    this.backend_subscription.unsubscribe();
+    this.backend_subscription = null;
   }
 
   @HostListener('window:focus', ['$event'])
@@ -87,7 +96,7 @@ export class ImageOverviewComponent implements OnInit, 
OnDestroy {
     
     this.image_selected = [image, mode];
     let cache_bust = `${Date.now()}-${Math.random()}`;
-    this.image_src = 
`${this.config.get('apiurl')}/images/${image}.${mode}?cb=${cache_bust}`;
+    this.image_src = 
`${this.backendcfg.url_for('api')}/images/${image}.${mode}?cb=${cache_bust}`;
 
     // see @ViewChild comment why we do this
     let img_elem = document.createElement("img");
diff --git 
a/src/plugins/webview/frontend/src/parts/skiller/components/overview.component.ts
 
b/src/plugins/webview/frontend/src/parts/skiller/components/overview.component.ts
index 58ae77a..e5c4ba3 100644
--- 
a/src/plugins/webview/frontend/src/parts/skiller/components/overview.component.ts
+++ 
b/src/plugins/webview/frontend/src/parts/skiller/components/overview.component.ts
@@ -2,10 +2,11 @@
 // Copyright  2018  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
 // License: Apache 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, OnDestroy } from '@angular/core';
 import { Observable } from 'rxjs/Observable';
 import 'rxjs/add/observable/interval';
 
+import { BackendConfigurationService } from 
'../../../services/backend-config/backend-config.service';
 import { BehaviorEngineApiService } from '../services/api.service';
 import { SkillCall } from '../models/SkillCall';
 
@@ -14,7 +15,7 @@ import { SkillCall } from '../models/SkillCall';
   templateUrl: './overview.component.html',
   styleUrls: ['./overview.component.scss']
 })
-export class SkillerOverviewComponent implements OnInit {
+export class SkillerOverviewComponent implements OnInit, OnDestroy {
 
   loading = false;
   auto_refresh_subscription = null;
@@ -24,12 +25,26 @@ export class SkillerOverviewComponent implements OnInit {
   zero_message = "No graph has been retrieved";
   message_id = 0;
 
-  constructor(private api_service: BehaviorEngineApiService)
+  private backend_subscription = null;
+
+  constructor(private api_service: BehaviorEngineApiService,
+              private backendcfg: BackendConfigurationService)
   {}
 
   ngOnInit() {
     this.refresh();
     this.refresh_skills();
+    this.backend_subscription = this.backendcfg.backend_changed.subscribe((b) 
=> {
+      this.refresh();
+      this.refresh_skills();
+      this.select_skill('active');
+    });
+  }
+
+  ngOnDestroy()
+  {
+    this.backend_subscription.unsubscribe();
+    this.backend_subscription = null;
   }
 
   refresh()

- *commit* 93c3f880af3d5d4748831c43848bad3cc649e030 - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Tue Apr 10 00:10:29 2018 +0200
Subject: restapi-gen: adapt Angular API template to new backend config

 .../templates/typescript-angular.ts.api.template   |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)

_Diff for modified files_:
diff --git a/etc/restapi-gen/templates/typescript-angular.ts.api.template 
b/etc/restapi-gen/templates/typescript-angular.ts.api.template
index 125eba7..920f5d9 100644
--- a/etc/restapi-gen/templates/typescript-angular.ts.api.template
+++ b/etc/restapi-gen/templates/typescript-angular.ts.api.template
@@ -63,14 +63,14 @@ import { Injectable } from '@angular/core';
 import { HttpClient, HttpHeaders, HttpParams, HttpResponse } from 
'@angular/common/http';
 import { Observable } from 'rxjs/Observable';
 
-import { ConfigurationService } from '../../../services/config.service';
+import { BackendConfigurationService } from 
'../../../services/backend-config/backend-config.service';
 
 {{ gen_imports(spec) }}
 
 @Injectable()
 export class {{ name | sanitize }}ApiService
 {
-  constructor(private config: ConfigurationService,
+  constructor(private backend: BackendConfigurationService,
               private http: HttpClient) {}
 
 % for p in spec.paths
@@ -112,7 +112,7 @@ export class {{ name | sanitize }}ApiService
 % set response_type = 'json'
 % endif
 
-    return this.http.{{ method }}{% if response_type == 'json' %}<{{ 
return_type(op) }}>{% endif %}(`${this.config.get('apiurl')}{{ p | 
path_substargs(op, '${encodeURIComponent(String($$))}') }}`, {% for p in op | 
path_args(only_in='body') %}{{ p.name | sanitize }}, {% endfor %}
+    return this.http.{{ method }}{% if response_type == 'json' %}<{{ 
return_type(op) }}>{% endif %}(`${this.backend.url_for('api')}{{ p | 
path_substargs(op, '${encodeURIComponent(String($$))}') }}`, {% for p in op | 
path_args(only_in='body') %}{{ p.name | sanitize }}, {% endfor %}
                  { headers: headers, params: params,
                    observe: '{{ observe }}', responseType: '{{ response_type 
}}' })    ;
        }

- *commit* 4d6b8b9217b49181769d718f8a7d2f8b2356a855 - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Tue Apr 10 00:11:39 2018 +0200
Subject: webview-frontend: re-generate API services with new template

 .../src/parts/blackboard/services/api.service.ts   |   38 ++++++---
 .../parts/clips-executive/services/api.service.ts  |   96 ++++++++++++++------
 .../src/parts/clips/services/api.service.ts        |   20 +++--
 .../src/parts/images/services/api.service.ts       |    8 +-
 .../src/parts/skiller/services/api.service.ts      |   12 ++--
 5 files changed, 115 insertions(+), 59 deletions(-)

_Diff for modified files_:
diff --git 
a/src/plugins/webview/frontend/src/parts/blackboard/services/api.service.ts 
b/src/plugins/webview/frontend/src/parts/blackboard/services/api.service.ts
index 9b4c14c..ad71026 100644
--- a/src/plugins/webview/frontend/src/parts/blackboard/services/api.service.ts
+++ b/src/plugins/webview/frontend/src/parts/blackboard/services/api.service.ts
@@ -12,12 +12,10 @@
  ****************************************************************************/
 
 import { Injectable } from '@angular/core';
-import { HttpClient, HttpHeaders } from '@angular/common/http';
+import { HttpClient, HttpHeaders, HttpParams, HttpResponse } from 
'@angular/common/http';
 import { Observable } from 'rxjs/Observable';
-import 'rxjs/add/observable/of';
-import 'rxjs/add/operator/map';
 
-import { ConfigurationService } from '../../../services/config.service';
+import { BackendConfigurationService } from 
'../../../services/backend-config/backend-config.service';
 
 import { InterfaceData } from '../models/InterfaceData';
 import { InterfaceInfo } from '../models/InterfaceInfo';
@@ -26,15 +24,21 @@ import { InterfaceInfo } from '../models/InterfaceInfo';
 @Injectable()
 export class BlackboardApiService
 {
-  constructor(private config: ConfigurationService,
+  constructor(private backend: BackendConfigurationService,
               private http: HttpClient) {}
 
   public list_interfaces(pretty?: boolean): Observable<InterfaceInfo[]>
   {
+               let params = new HttpParams();
+               if (pretty) {
+                 params = params.set("pretty", pretty.toString());
+               }
     let headers = new HttpHeaders();
+               
     headers = headers.set('Accept', 'application/json');
-    let options = { headers: headers }
-    return 
this.http.get<InterfaceInfo[]>(`${this.config.get('apiurl')}/blackboard/interfaces`,
 options);
+    return 
this.http.get<InterfaceInfo[]>(`${this.backend.url_for('api')}/blackboard/interfaces`,
 
+                 { headers: headers, params: params,
+                   observe: 'body', responseType: 'json' })    ;
        }
 
   public get_interface_info(type: string, id: string, pretty?: boolean): 
Observable<InterfaceInfo>
@@ -45,10 +49,16 @@ export class BlackboardApiService
     if (id === null || id == undefined) {
       throw new Error("Required parameter id is null or undefined 
(get_interface_info)");
     }
+               let params = new HttpParams();
+               if (pretty) {
+                 params = params.set("pretty", pretty.toString());
+               }
     let headers = new HttpHeaders();
+               
     headers = headers.set('Accept', 'application/json');
-    let options = { headers: headers }
-    return 
this.http.get<InterfaceInfo>(`${this.config.get('apiurl')}/blackboard/interfaces/${encodeURIComponent(String(type))}/${encodeURIComponent(String(id))}`,
 options);
+    return 
this.http.get<InterfaceInfo>(`${this.backend.url_for('api')}/blackboard/interfaces/${encodeURIComponent(String(type))}/${encodeURIComponent(String(id))}`,
 
+                 { headers: headers, params: params,
+                   observe: 'body', responseType: 'json' })    ;
        }
 
   public get_interface_data(type: string, id: string, pretty?: boolean): 
Observable<InterfaceData>
@@ -59,10 +69,16 @@ export class BlackboardApiService
     if (id === null || id == undefined) {
       throw new Error("Required parameter id is null or undefined 
(get_interface_data)");
     }
+               let params = new HttpParams();
+               if (pretty) {
+                 params = params.set("pretty", pretty.toString());
+               }
     let headers = new HttpHeaders();
+               
     headers = headers.set('Accept', 'application/json');
-    let options = { headers: headers }
-    return 
this.http.get<InterfaceData>(`${this.config.get('apiurl')}/blackboard/interfaces/${encodeURIComponent(String(type))}/${encodeURIComponent(String(id))}/data`,
 options);
+    return 
this.http.get<InterfaceData>(`${this.backend.url_for('api')}/blackboard/interfaces/${encodeURIComponent(String(type))}/${encodeURIComponent(String(id))}/data`,
 
+                 { headers: headers, params: params,
+                   observe: 'body', responseType: 'json' })    ;
        }
 
 }
\ No newline at end of file
diff --git 
a/src/plugins/webview/frontend/src/parts/clips-executive/services/api.service.ts
 
b/src/plugins/webview/frontend/src/parts/clips-executive/services/api.service.ts
index 355ad97..56adcbe 100644
--- 
a/src/plugins/webview/frontend/src/parts/clips-executive/services/api.service.ts
+++ 
b/src/plugins/webview/frontend/src/parts/clips-executive/services/api.service.ts
@@ -12,12 +12,10 @@
  ****************************************************************************/
 
 import { Injectable } from '@angular/core';
-import { HttpClient, HttpHeaders } from '@angular/common/http';
+import { HttpClient, HttpHeaders, HttpParams, HttpResponse } from 
'@angular/common/http';
 import { Observable } from 'rxjs/Observable';
-import 'rxjs/add/observable/of';
-import 'rxjs/add/operator/map';
 
-import { ConfigurationService } from '../../../services/config.service';
+import { BackendConfigurationService } from 
'../../../services/backend-config/backend-config.service';
 
 import { DomainFact } from '../models/DomainFact';
 import { DomainObject } from '../models/DomainObject';
@@ -30,23 +28,21 @@ import { Plan } from '../models/Plan';
 @Injectable()
 export class ClipsExecutiveApiService
 {
-  constructor(private config: ConfigurationService,
+  constructor(private backend: BackendConfigurationService,
               private http: HttpClient) {}
 
   public list_goals(pretty?: boolean): Observable<Goal[]>
   {
+               let params = new HttpParams();
+               if (pretty) {
+                 params = params.set("pretty", pretty.toString());
+               }
     let headers = new HttpHeaders();
+               
     headers = headers.set('Accept', 'application/json');
-    let options = { headers: headers }
-    return 
this.http.get<Goal[]>(`${this.config.get('apiurl')}/clips-executive/goals`, 
options);
-       }
-
-  public add_goal(): Observable<any>
-  {
-    let headers = new HttpHeaders();
-    headers = headers.set('Content-type', 'application/json');
-    let options = { headers: headers }
-    return 
this.http.post<any>(`${this.config.get('apiurl')}/clips-executive/goals`, 
options);
+    return 
this.http.get<Goal[]>(`${this.backend.url_for('api')}/clips-executive/goals`, 
+                 { headers: headers, params: params,
+                   observe: 'body', responseType: 'json' })    ;
        }
 
   public get_goal(id: string, pretty?: boolean): Observable<Goal>
@@ -54,18 +50,30 @@ export class ClipsExecutiveApiService
     if (id === null || id == undefined) {
       throw new Error("Required parameter id is null or undefined (get_goal)");
     }
+               let params = new HttpParams();
+               if (pretty) {
+                 params = params.set("pretty", pretty.toString());
+               }
     let headers = new HttpHeaders();
+               
     headers = headers.set('Accept', 'application/json');
-    let options = { headers: headers }
-    return 
this.http.get<Goal>(`${this.config.get('apiurl')}/clips-executive/goals/${encodeURIComponent(String(id))}`,
 options);
+    return 
this.http.get<Goal>(`${this.backend.url_for('api')}/clips-executive/goals/${encodeURIComponent(String(id))}`,
 
+                 { headers: headers, params: params,
+                   observe: 'body', responseType: 'json' })    ;
        }
 
   public list_plans(pretty?: boolean): Observable<Plan[]>
   {
+               let params = new HttpParams();
+               if (pretty) {
+                 params = params.set("pretty", pretty.toString());
+               }
     let headers = new HttpHeaders();
+               
     headers = headers.set('Accept', 'application/json');
-    let options = { headers: headers }
-    return 
this.http.get<Plan[]>(`${this.config.get('apiurl')}/clips-executive/plans`, 
options);
+    return 
this.http.get<Plan[]>(`${this.backend.url_for('api')}/clips-executive/plans`, 
+                 { headers: headers, params: params,
+                   observe: 'body', responseType: 'json' })    ;
        }
 
   public get_plan(goal_id: string, id: string, pretty?: boolean): 
Observable<Plan>
@@ -76,42 +84,72 @@ export class ClipsExecutiveApiService
     if (id === null || id == undefined) {
       throw new Error("Required parameter id is null or undefined (get_plan)");
     }
+               let params = new HttpParams();
+               if (pretty) {
+                 params = params.set("pretty", pretty.toString());
+               }
     let headers = new HttpHeaders();
+               
     headers = headers.set('Accept', 'application/json');
-    let options = { headers: headers }
-    return 
this.http.get<Plan>(`${this.config.get('apiurl')}/clips-executive/plans/${encodeURIComponent(String(goal_id))}/${encodeURIComponent(String(id))}`,
 options);
+    return 
this.http.get<Plan>(`${this.backend.url_for('api')}/clips-executive/plans/${encodeURIComponent(String(goal_id))}/${encodeURIComponent(String(id))}`,
 
+                 { headers: headers, params: params,
+                   observe: 'body', responseType: 'json' })    ;
        }
 
   public list_domain_objects(pretty?: boolean): Observable<DomainObject[]>
   {
+               let params = new HttpParams();
+               if (pretty) {
+                 params = params.set("pretty", pretty.toString());
+               }
     let headers = new HttpHeaders();
+               
     headers = headers.set('Accept', 'application/json');
-    let options = { headers: headers }
-    return 
this.http.get<DomainObject[]>(`${this.config.get('apiurl')}/clips-executive/domain-objects`,
 options);
+    return 
this.http.get<DomainObject[]>(`${this.backend.url_for('api')}/clips-executive/domain-objects`,
 
+                 { headers: headers, params: params,
+                   observe: 'body', responseType: 'json' })    ;
        }
 
   public list_domain_predicates(pretty?: boolean): 
Observable<DomainPredicate[]>
   {
+               let params = new HttpParams();
+               if (pretty) {
+                 params = params.set("pretty", pretty.toString());
+               }
     let headers = new HttpHeaders();
+               
     headers = headers.set('Accept', 'application/json');
-    let options = { headers: headers }
-    return 
this.http.get<DomainPredicate[]>(`${this.config.get('apiurl')}/clips-executive/domain-predicates`,
 options);
+    return 
this.http.get<DomainPredicate[]>(`${this.backend.url_for('api')}/clips-executive/domain-predicates`,
 
+                 { headers: headers, params: params,
+                   observe: 'body', responseType: 'json' })    ;
        }
 
   public list_domain_facts(pretty?: boolean): Observable<DomainFact[]>
   {
+               let params = new HttpParams();
+               if (pretty) {
+                 params = params.set("pretty", pretty.toString());
+               }
     let headers = new HttpHeaders();
+               
     headers = headers.set('Accept', 'application/json');
-    let options = { headers: headers }
-    return 
this.http.get<DomainFact[]>(`${this.config.get('apiurl')}/clips-executive/domain-facts`,
 options);
+    return 
this.http.get<DomainFact[]>(`${this.backend.url_for('api')}/clips-executive/domain-facts`,
 
+                 { headers: headers, params: params,
+                   observe: 'body', responseType: 'json' })    ;
        }
 
   public list_domain_operators(pretty?: boolean): Observable<DomainOperator[]>
   {
+               let params = new HttpParams();
+               if (pretty) {
+                 params = params.set("pretty", pretty.toString());
+               }
     let headers = new HttpHeaders();
+               
     headers = headers.set('Accept', 'application/json');
-    let options = { headers: headers }
-    return 
this.http.get<DomainOperator[]>(`${this.config.get('apiurl')}/clips-executive/domain-operators`,
 options);
+    return 
this.http.get<DomainOperator[]>(`${this.backend.url_for('api')}/clips-executive/domain-operators`,
 
+                 { headers: headers, params: params,
+                   observe: 'body', responseType: 'json' })    ;
        }
 
 }
\ No newline at end of file
diff --git 
a/src/plugins/webview/frontend/src/parts/clips/services/api.service.ts 
b/src/plugins/webview/frontend/src/parts/clips/services/api.service.ts
index b62d7a3..ef335c7 100644
--- a/src/plugins/webview/frontend/src/parts/clips/services/api.service.ts
+++ b/src/plugins/webview/frontend/src/parts/clips/services/api.service.ts
@@ -12,12 +12,10 @@
  ****************************************************************************/
 
 import { Injectable } from '@angular/core';
-import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
+import { HttpClient, HttpHeaders, HttpParams, HttpResponse } from 
'@angular/common/http';
 import { Observable } from 'rxjs/Observable';
-import 'rxjs/add/observable/of';
-import 'rxjs/add/operator/map';
 
-import { ConfigurationService } from '../../../services/config.service';
+import { BackendConfigurationService } from 
'../../../services/backend-config/backend-config.service';
 
 import { Environment } from '../models/Environment';
 import { Fact } from '../models/Fact';
@@ -26,7 +24,7 @@ import { Fact } from '../models/Fact';
 @Injectable()
 export class ClipsApiService
 {
-  constructor(private config: ConfigurationService,
+  constructor(private backend: BackendConfigurationService,
               private http: HttpClient) {}
 
   public get_facts(env: string, pretty?: boolean, formatted?: boolean): 
Observable<Fact[]>
@@ -42,9 +40,11 @@ export class ClipsApiService
                  params = params.set("formatted", formatted.toString());
                }
     let headers = new HttpHeaders();
+               
     headers = headers.set('Accept', 'application/json');
-    let options = { headers: headers, params: params };
-    return 
this.http.get<Fact[]>(`${this.config.get('apiurl')}/clips/${encodeURIComponent(String(env))}/facts`,
 options);
+    return 
this.http.get<Fact[]>(`${this.backend.url_for('api')}/clips/${encodeURIComponent(String(env))}/facts`,
 
+                 { headers: headers, params: params,
+                   observe: 'body', responseType: 'json' })    ;
        }
 
   public list_environments(pretty?: boolean): Observable<Environment[]>
@@ -54,9 +54,11 @@ export class ClipsApiService
                  params = params.set("pretty", pretty.toString());
                }
     let headers = new HttpHeaders();
+               
     headers = headers.set('Accept', 'application/json');
-    let options = { headers: headers, params: params };
-    return this.http.get<Environment[]>(`${this.config.get('apiurl')}/clips/`, 
options);
+    return 
this.http.get<Environment[]>(`${this.backend.url_for('api')}/clips/`, 
+                 { headers: headers, params: params,
+                   observe: 'body', responseType: 'json' })    ;
        }
 
 }
\ No newline at end of file
diff --git 
a/src/plugins/webview/frontend/src/parts/images/services/api.service.ts 
b/src/plugins/webview/frontend/src/parts/images/services/api.service.ts
index 4a53a4e..067683c 100644
--- a/src/plugins/webview/frontend/src/parts/images/services/api.service.ts
+++ b/src/plugins/webview/frontend/src/parts/images/services/api.service.ts
@@ -15,7 +15,7 @@ import { Injectable } from '@angular/core';
 import { HttpClient, HttpHeaders, HttpParams, HttpResponse } from 
'@angular/common/http';
 import { Observable } from 'rxjs/Observable';
 
-import { ConfigurationService } from '../../../services/config.service';
+import { BackendConfigurationService } from 
'../../../services/backend-config/backend-config.service';
 
 import { ImageInfo } from '../models/ImageInfo';
 
@@ -23,7 +23,7 @@ import { ImageInfo } from '../models/ImageInfo';
 @Injectable()
 export class ImageApiService
 {
-  constructor(private config: ConfigurationService,
+  constructor(private backend: BackendConfigurationService,
               private http: HttpClient) {}
 
   public list_images(pretty?: boolean): Observable<ImageInfo[]>
@@ -35,7 +35,7 @@ export class ImageApiService
     let headers = new HttpHeaders();
                
     headers = headers.set('Accept', 'application/json');
-    return this.http.get<ImageInfo[]>(`${this.config.get('apiurl')}/images`, 
+    return this.http.get<ImageInfo[]>(`${this.backend.url_for('api')}/images`, 
                  { headers: headers, params: params,
                    observe: 'body', responseType: 'json' })    ;
        }
@@ -51,7 +51,7 @@ export class ImageApiService
                }
     let headers = new HttpHeaders();
                
-    return 
this.http.get(`${this.config.get('apiurl')}/images/${encodeURIComponent(String(id))}`,
 
+    return 
this.http.get(`${this.backend.url_for('api')}/images/${encodeURIComponent(String(id))}`,
 
                  { headers: headers, params: params,
                    observe: 'response', responseType: 'text' })        ;
        }
diff --git 
a/src/plugins/webview/frontend/src/parts/skiller/services/api.service.ts 
b/src/plugins/webview/frontend/src/parts/skiller/services/api.service.ts
index ec8863c..e711320 100644
--- a/src/plugins/webview/frontend/src/parts/skiller/services/api.service.ts
+++ b/src/plugins/webview/frontend/src/parts/skiller/services/api.service.ts
@@ -16,7 +16,7 @@ import { Injectable } from '@angular/core';
 import { HttpClient, HttpHeaders, HttpParams, HttpResponse } from 
'@angular/common/http';
 import { Observable } from 'rxjs/Observable';
 
-import { ConfigurationService } from '../../../services/config.service';
+import { BackendConfigurationService } from 
'../../../services/backend-config/backend-config.service';
 
 import { Skill } from '../models/Skill';
 import { SkillCall } from '../models/SkillCall';
@@ -26,7 +26,7 @@ import { SkillInfo } from '../models/SkillInfo';
 @Injectable()
 export class BehaviorEngineApiService
 {
-  constructor(private config: ConfigurationService,
+  constructor(private backend: BackendConfigurationService,
               private http: HttpClient) {}
 
   public list_skills(pretty?: boolean): Observable<SkillInfo[]>
@@ -38,7 +38,7 @@ export class BehaviorEngineApiService
     let headers = new HttpHeaders();
                
     headers = headers.set('Accept', 'application/json');
-    return 
this.http.get<SkillInfo[]>(`${this.config.get('apiurl')}/skiller/skills`, 
+    return 
this.http.get<SkillInfo[]>(`${this.backend.url_for('api')}/skiller/skills`, 
                  { headers: headers, params: params,
                    observe: 'body', responseType: 'json' })    ;
        }
@@ -55,7 +55,7 @@ export class BehaviorEngineApiService
     let headers = new HttpHeaders();
                
     headers = headers.set('Accept', 'application/json');
-    return 
this.http.get<Skill>(`${this.config.get('apiurl')}/skiller/skills/${encodeURIComponent(String(id))}`,
 
+    return 
this.http.get<Skill>(`${this.backend.url_for('api')}/skiller/skills/${encodeURIComponent(String(id))}`,
 
                  { headers: headers, params: params,
                    observe: 'body', responseType: 'json' })    ;
        }
@@ -68,7 +68,7 @@ export class BehaviorEngineApiService
                let params = new HttpParams();
     let headers = new HttpHeaders();
                
-    return 
this.http.delete(`${this.config.get('apiurl')}/skiller/skills/${encodeURIComponent(String(id))}`,
 
+    return 
this.http.delete(`${this.backend.url_for('api')}/skiller/skills/${encodeURIComponent(String(id))}`,
 
                  { headers: headers, params: params,
                    observe: 'response', responseType: 'text' })        ;
        }
@@ -82,7 +82,7 @@ export class BehaviorEngineApiService
     let headers = new HttpHeaders();
                
     headers = headers.set('Accept', 'application/json');
-    return this.http.post<Skill>(`${this.config.get('apiurl')}/skiller/call`, 
skill_call, 
+    return 
this.http.post<Skill>(`${this.backend.url_for('api')}/skiller/call`, 
skill_call, 
                  { headers: headers, params: params,
                    observe: 'body', responseType: 'json' })    ;
        }

- *commit* 11e641d8e65e763e61329cd8ed9d46731fc9a482 - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Tue Apr 10 00:17:00 2018 +0200
Subject: webview-frontend: adapt Prometheus chart to dynamic backend

 .../frontend/src/components/promchart/component.ts |   54 +++++++++++++-------
 .../src/components/promchart/template.html         |    2 +-
 2 files changed, 36 insertions(+), 20 deletions(-)

_Diff for modified files_:
diff --git a/src/plugins/webview/frontend/src/components/promchart/component.ts 
b/src/plugins/webview/frontend/src/components/promchart/component.ts
index 9a38111..8f0eb85 100644
--- a/src/plugins/webview/frontend/src/components/promchart/component.ts
+++ b/src/plugins/webview/frontend/src/components/promchart/component.ts
@@ -2,12 +2,15 @@
 // Copyright  2018  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
 // License: Apache 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 
-import {Component, Input, AfterViewInit, ViewChild, OnDestroy, HostListener} 
from '@angular/core';
+import {Component, Input, AfterViewInit, ViewChild,
+        OnInit, OnDestroy, HostListener} from '@angular/core';
 import { HttpClient } from '@angular/common/http';
 
 import { Observable } from 'rxjs/Observable';
 import 'rxjs/add/observable/interval';
 
+import { BackendConfigurationService } from 
'../../services/backend-config/backend-config.service';
+
 import * as c3 from 'c3';
 import * as d3 from 'd3';
 
@@ -32,7 +35,7 @@ const DEFAULT_LEGEND: c3.LegendOptions = {
   templateUrl: './template.html',
   styleUrls: ['./style.scss']
 })
-export class PrometheusChartComponent implements AfterViewInit, OnDestroy {
+export class PrometheusChartComponent implements AfterViewInit, OnInit, 
OnDestroy {
   @Input() time_range?: number = 900;
   @Input() step_sec?: number = 15;
   @Input() query?: string = null;
@@ -57,22 +60,11 @@ export class PrometheusChartComponent implements 
AfterViewInit, OnDestroy {
   zero_message: string = "No data received";
   auto_refresh_subscription = null;
 
-  constructor(private http: HttpClient) {}
-  
-  format_bytes(value: number)
-  {
-    let units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB']
-    let idx = 0;
-    while (value > 1024 && idx < units.length - 1) {
-      value /= 1024;
-      idx += 1;
-    }
-    if (value > 0 && value < 10) {
-      return d3.format('.1f')(value) + units[idx];
-    } else {
-      return d3.format('.0f')(value) + units[idx];
-    }
-  }
+  private backend_subscription = null;
+
+  constructor(private backendcfg: BackendConfigurationService,
+              private http: HttpClient)
+  {}
 
   ngAfterViewInit()
   {
@@ -83,9 +75,17 @@ export class PrometheusChartComponent implements 
AfterViewInit, OnDestroy {
     }
   }
 
+  ngOnInit() {
+    this.backend_subscription = 
this.backendcfg.backend_changed.subscribe((backend) => {
+      this.refresh();
+    });
+  }
+
   ngOnDestroy()
   {
     this.disable_autorefresh();
+    this.backend_subscription.unsubscribe();
+    this.backend_subscription = null;
   }
 
   @HostListener('window:focus', ['$event'])
@@ -105,7 +105,7 @@ export class PrometheusChartComponent implements 
AfterViewInit, OnDestroy {
     let end   = Math.floor(Date.now() / 1000);
     let start = end - this.time_range;
     let url =
-      
`http://localhost:9090/api/v1/query_range?query=${encodeURIComponent(String(this.query))}&start=${start}&end=${end}&step=${this.step_sec}s`;
+      
`${this.backendcfg.url_for('prometheus')}/api/v1/query_range?query=${encodeURIComponent(String(this.query))}&start=${start}&end=${end}&step=${this.step_sec}s`;
     this.http.get<any>(url)
       .subscribe(
         (obj) => {
@@ -263,6 +263,7 @@ export class PrometheusChartComponent implements 
AfterViewInit, OnDestroy {
         },
         (err) => {
           if (err.status == 0) {
+            this.have_data = false;
             this.zero_message="Prometheus server unavailable.";
           } else {
             this.zero_message=`Failed to retrieve data: ${err.error}`;
@@ -271,6 +272,21 @@ export class PrometheusChartComponent implements 
AfterViewInit, OnDestroy {
       );
   }
 
+  format_bytes(value: number)
+  {
+    let units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB']
+    let idx = 0;
+    while (value > 1024 && idx < units.length - 1) {
+      value /= 1024;
+      idx += 1;
+    }
+    if (value > 0 && value < 10) {
+      return d3.format('.1f')(value) + units[idx];
+    } else {
+      return d3.format('.0f')(value) + units[idx];
+    }
+  }
+
   private enable_autorefresh()
   {
     if (this.auto_refresh_subscription)  return;
diff --git 
a/src/plugins/webview/frontend/src/components/promchart/template.html 
b/src/plugins/webview/frontend/src/components/promchart/template.html
index 7cfe6b7..abed061 100644
--- a/src/plugins/webview/frontend/src/components/promchart/template.html
+++ b/src/plugins/webview/frontend/src/components/promchart/template.html
@@ -2,7 +2,7 @@
  Copyright  2018  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
  License: Apache 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 -->
-<div #chart></div>
+<div #chart [hidden]="!have_data"></div>
 <div *ngIf="!have_data">
        <ff-list-zero-state [message]="zero_message"></ff-list-zero-state>
 </div>

- *commit* 182676f9ae9f99399bccefb681f8f920483878ff - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Tue Apr 10 00:22:08 2018 +0200
Subject: webview-frontend: automatically call "npm i" if node_modules missing

 src/plugins/webview/frontend/Makefile |    6 +++++-
 1 files changed, 5 insertions(+), 1 deletions(-)

_Diff for modified files_:
diff --git a/src/plugins/webview/frontend/Makefile 
b/src/plugins/webview/frontend/Makefile
index 31b410b..dc6090b 100644
--- a/src/plugins/webview/frontend/Makefile
+++ b/src/plugins/webview/frontend/Makefile
@@ -32,10 +32,14 @@ else
     WARN_TARGETS += warning_ng_cli
 endif
 
-dist/index.html: $(SRCS_FRONTEND)
+dist/index.html: $(SRCS_FRONTEND) $(SRCDIR)/node_modules
        $(SILENT) echo -e "$(INDENT_PRINT)[Angular] $(TBOLDGRAY)Webview 
Frontend$(TNORMAL)"
        $(SILENT)ng build --prod --build-optimizer
 
+$(SRCDIR)/node_modules:
+       $(SILENT) echo -e "$(INDENT_PRINT)[npm] $(TBOLDGRAY)Installing 
dependencies via npm$(TNORMAL)"
+       $(SILENT)npm install
+
 deploy: $(RESDIR)/webview/index.html
 
 $(RESDIR)/webview/index.html: dist/index.html

- *commit* f54d615201b457eea660dd2a30c7c2f5a0577ea9 - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Tue Apr 10 11:52:06 2018 +0200
Subject: webview-frontend: configure default prometheus url only in dev env

 .../backend-config/backend-config.service.ts       |    7 ++++++-
 1 files changed, 6 insertions(+), 1 deletions(-)

_Diff for modified files_:
diff --git 
a/src/plugins/webview/frontend/src/services/backend-config/backend-config.service.ts
 
b/src/plugins/webview/frontend/src/services/backend-config/backend-config.service.ts
index c9d7144..77677be 100644
--- 
a/src/plugins/webview/frontend/src/services/backend-config/backend-config.service.ts
+++ 
b/src/plugins/webview/frontend/src/services/backend-config/backend-config.service.ts
@@ -32,10 +32,11 @@ export class BackendConfigurationService {
 
     let local_url = new URL(window.location.href);
     let api_url   = new URL(`${local_url.origin}/api`);
-    let prom_url  = new URL(`${api_url.protocol}//${api_url.hostname}:9090`);
+    let prom_url  = null;
 
     if (! environment.production) {
       api_url   = new URL(`http://${local_url.hostname}:8088/api`);
+      prom_url  = new URL(`${api_url.protocol}//${api_url.hostname}:9090`);
     }
 
     this.backends_['origin'] = {
@@ -111,6 +112,10 @@ export class BackendConfigurationService {
     return Object.keys(this.backends_[this.current_backend]);
   }
 
+  has_url_for(service: string): boolean {
+    return (service in this.backends_[this.current_backend].services);
+  }
+
   url_for(service : string) {
     if (service in this.backends_[this.current_backend].services) {
       let url = 
this.backends_[this.current_backend].services[service].toString();

- *commit* dc53e8042b86fa4d8e9559cac217967c159320ee - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Tue Apr 10 11:54:06 2018 +0200
Subject: webview-frontend: anticipate missing prometheus URL

 .../frontend/src/components/promchart/component.ts |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)

_Diff for modified files_:
diff --git a/src/plugins/webview/frontend/src/components/promchart/component.ts 
b/src/plugins/webview/frontend/src/components/promchart/component.ts
index 8f0eb85..34070f2 100644
--- a/src/plugins/webview/frontend/src/components/promchart/component.ts
+++ b/src/plugins/webview/frontend/src/components/promchart/component.ts
@@ -102,6 +102,11 @@ export class PrometheusChartComponent implements 
AfterViewInit, OnInit, OnDestro
 
   refresh()
   {
+    if (! this.backendcfg.has_url_for('prometheus')) {
+      this.zero_message = 'No Prometheus backend service known';
+      return;
+    }
+
     let end   = Math.floor(Date.now() / 1000);
     let start = end - this.time_range;
     let url =

- *commit* 4abb38ff69c300a3c0097e378436ffb1965566eb - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Tue Apr 10 11:54:43 2018 +0200
Subject: webview-frontend: improve error handling in prometheus chart

 .../frontend/src/components/promchart/component.ts |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

_Diff for modified files_:
diff --git a/src/plugins/webview/frontend/src/components/promchart/component.ts 
b/src/plugins/webview/frontend/src/components/promchart/component.ts
index 34070f2..80bfed5 100644
--- a/src/plugins/webview/frontend/src/components/promchart/component.ts
+++ b/src/plugins/webview/frontend/src/components/promchart/component.ts
@@ -267,11 +267,11 @@ export class PrometheusChartComponent implements 
AfterViewInit, OnInit, OnDestro
           }
         },
         (err) => {
+          this.have_data = false;
           if (err.status == 0) {
-            this.have_data = false;
             this.zero_message="Prometheus server unavailable.";
           } else {
-            this.zero_message=`Failed to retrieve data: ${err.error}`;
+            this.zero_message=`Failed to retrieve data: ${err.error || 
err.status}`;
           }
         }
       );

- *commit* b6d6ef265baa7eeefe326a0ab2fa1078cbc584de - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Tue Apr 10 11:56:20 2018 +0200
Subject: webview-frontend: periodically check for frontend updates

 .../webview/frontend/src/app/app.component.ts      |    8 +++-
 src/plugins/webview/frontend/src/app/app.module.ts |    3 +-
 .../update-notifier/update-notifier.service.ts     |   55 ++++++++++++++++++++
 src/plugins/webview/frontend/src/shared.module.ts  |    3 +-
 4 files changed, 66 insertions(+), 3 deletions(-)

_Diff for modified files_:
diff --git a/src/plugins/webview/frontend/src/app/app.component.ts 
b/src/plugins/webview/frontend/src/app/app.component.ts
index 51505eb..cc75c56 100644
--- a/src/plugins/webview/frontend/src/app/app.component.ts
+++ b/src/plugins/webview/frontend/src/app/app.component.ts
@@ -4,12 +4,18 @@
 
 import { Component } from '@angular/core';
 
+import { SwUpdateNotifierService } from 
'../services/update-notifier/update-notifier.service';
+
 @Component({
   selector: 'app-root',
   //templateUrl: './app.component.html',
   template: '<ff-chrome class="ff-light-theme"></ff-chrome>',
-  styleUrls: ['./app.component.scss']
+  styleUrls: ['./app.component.scss'],
+  providers: [SwUpdateNotifierService]
 })
 export class AppComponent {
   title = 'app';
+
+  constructor(private update_notifier: SwUpdateNotifierService)
+  {}
 }
diff --git a/src/plugins/webview/frontend/src/app/app.module.ts 
b/src/plugins/webview/frontend/src/app/app.module.ts
index 5c89afc..aa37e64 100644
--- a/src/plugins/webview/frontend/src/app/app.module.ts
+++ b/src/plugins/webview/frontend/src/app/app.module.ts
@@ -15,6 +15,7 @@ import { AppComponent } from './app.component';
 import { ChromeModule } from '../chrome/module';
 import { BackendConfigurationService } from 
'../services/backend-config/backend-config.service';
 import { AssetsService } from '../services/global/assets';
+import { SwUpdateNotifierService } from 
'../services/update-notifier/update-notifier.service';
 
 import { ClipsExecutiveModule } from '../parts/clips-executive/module';
 import { SkillerModule } from '../parts/skiller/module';
@@ -45,7 +46,7 @@ import { HttpClientInMemoryWebApiModule } from 
'angular-in-memory-web-api';
          // Keep the AppRoutingModule last
          AppRoutingModule,
        ],
-  providers: [BackendConfigurationService, AssetsService],
+  providers: [BackendConfigurationService, AssetsService, 
SwUpdateNotifierService],
        bootstrap: [AppComponent]
 })
 export class AppModule { }
diff --git a/src/plugins/webview/frontend/src/shared.module.ts 
b/src/plugins/webview/frontend/src/shared.module.ts
index 996cc36..2be098e 100644
--- a/src/plugins/webview/frontend/src/shared.module.ts
+++ b/src/plugins/webview/frontend/src/shared.module.ts
@@ -7,7 +7,7 @@ import {CommonModule} from '@angular/common';
 import {NgModule} from '@angular/core';
 import {FlexLayoutModule} from '@angular/flex-layout';
 import {FormsModule} from '@angular/forms';
-import {MAT_TOOLTIP_DEFAULT_OPTIONS, MatButtonModule, MatCardModule, 
MatChipsModule, MatDialogModule, MatDividerModule, MatFormFieldModule, 
MatGridListModule, MatIconModule, MatInputModule, MatMenuModule, 
MatPaginatorModule, MatProgressSpinnerModule, MatRadioModule, MatSelectModule, 
MatSidenavModule, MatSliderModule, MatSlideToggleModule, MatSortModule, 
MatTableModule, MatTabsModule, MatToolbarModule, MatTooltipModule, 
MatExpansionModule} from '@angular/material';
+import {MAT_TOOLTIP_DEFAULT_OPTIONS, MatButtonModule, MatCardModule, 
MatChipsModule, MatDialogModule, MatDividerModule, MatFormFieldModule, 
MatGridListModule, MatIconModule, MatInputModule, MatMenuModule, 
MatPaginatorModule, MatProgressSpinnerModule, MatRadioModule, MatSelectModule, 
MatSidenavModule, MatSliderModule, MatSlideToggleModule, MatSortModule, 
MatTableModule, MatTabsModule, MatToolbarModule, MatTooltipModule, 
MatExpansionModule, MatSnackBarModule} from '@angular/material';
 
 //import {PipesModule} from './common/pipes/module';
 //import {KD_TOOLTIP_DEFAULT_OPTIONS} from './index.config';
@@ -41,6 +41,7 @@ const SHARED_DEPENDENCIES = [
   MatMenuModule,
   MatSelectModule,
   MatExpansionModule,
+  MatSnackBarModule,
 
   // Other 3rd party modules
   FlexLayoutModule,

- *commit* 338d84a6aa01150f24b16dd5dd81bb6b8f93a695 - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Tue Apr 10 18:55:30 2018 +0200
Subject: libwebview: add convenience methods for body handling to request

 src/libs/webview/request.cpp |   24 ++++++++++++++++++++++++
 src/libs/webview/request.h   |    2 ++
 2 files changed, 26 insertions(+), 0 deletions(-)

_Diff for modified files_:
diff --git a/src/libs/webview/request.cpp b/src/libs/webview/request.cpp
index a16b6dd..db0c9e7 100644
--- a/src/libs/webview/request.cpp
+++ b/src/libs/webview/request.cpp
@@ -203,6 +203,30 @@ WebRequest::set_body(const char *data, size_t data_size)
   body_ = std::string(data, data_size);
 }
 
+/** Add to request body.
+ * The data is copied as is without assuming a human-readable string
+ * or even just zero-termination.
+ * @param data data to copy
+ * @param data_size size in bytes of \@p data
+ */
+void
+WebRequest::addto_body(const char *data, size_t data_size)
+{
+  body_ += std::string(data, data_size);
+}
+
+/** Finalize body handling.
+ * Check for zero termination of body, and if it does not exist, add it.
+ */
+void
+WebRequest::finish_body()
+{
+       if (body_.length() == 0)  return;
+       if (body_[body_.length()-1] != 0) {
+               body_ += '\0';
+       }
+}
+
 /** Increment reply bytes counter.
  * @param increment_by number of bytes sent
  */
diff --git a/src/libs/webview/request.h b/src/libs/webview/request.h
index 217b92f..bfb3f4b 100644
--- a/src/libs/webview/request.h
+++ b/src/libs/webview/request.h
@@ -269,6 +269,8 @@ class WebRequest {
    */
   void set_cookies(const std::map<std::string, std::string> &cookies) { 
cookies_ = cookies; }
   void set_body(const char *data, size_t data_size);
+  void addto_body(const char *data, size_t data_size);
+  void finish_body();
 
  private:
   bool is_setup() { return is_setup_; }

- *commit* 480da4a8c46b1b3870f91d883812af05286d0de8 - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Tue Apr 10 18:56:04 2018 +0200
Subject: libwebview: body input handling for methods other than POST

 src/libs/webview/request_dispatcher.cpp |    8 +++++++-
 1 files changed, 7 insertions(+), 1 deletions(-)

_Diff for modified files_:
diff --git a/src/libs/webview/request_dispatcher.cpp 
b/src/libs/webview/request_dispatcher.cpp
index 5de8ec3..5b44774 100644
--- a/src/libs/webview/request_dispatcher.cpp
+++ b/src/libs/webview/request_dispatcher.cpp
@@ -494,7 +494,7 @@ WebRequestDispatcher::process_request(struct MHD_Connection 
* connection,
 
   if (0 == strcmp(method, MHD_HTTP_METHOD_POST)) {
          if (MHD_post_process(request->pp_, upload_data, *upload_data_size) == 
MHD_NO) {
-                 request->set_body(upload_data, *upload_data_size);
+                 request->addto_body(upload_data, *upload_data_size);
          }
          if (0 != *upload_data_size) {
                  *upload_data_size = 0;
@@ -502,6 +502,12 @@ WebRequestDispatcher::process_request(struct 
MHD_Connection * connection,
          }
          MHD_destroy_post_processor(request->pp_);
          request->pp_ = NULL;
+  } else if (0 != *upload_data_size) {
+         request->addto_body(upload_data, *upload_data_size);
+         *upload_data_size = 0;
+         return MHD_YES;
+  } else {
+         request->finish_body();
   }
 
   try {

- *commit* 891e7d8483f91cf397b1df3465d9a3af776be3dd - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Tue Apr 10 18:57:24 2018 +0200
Subject: libwebview: change order of input and output paramter

 src/libs/webview/rest_api.h |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

_Diff for modified files_:
diff --git a/src/libs/webview/rest_api.h b/src/libs/webview/rest_api.h
index 1109d33..6a43b30 100644
--- a/src/libs/webview/rest_api.h
+++ b/src/libs/webview/rest_api.h
@@ -233,7 +233,7 @@ class WebviewRestApi
         * @param path path (after component base path) to react to
         * @param handler handler function
         */
-       template <class I, class O>
+       template <class O, class I>
        void
        add_handler(WebRequest::Method method, std::string path,
                            std::function<O (I, WebviewRestParams &)> handler)

- *commit* b7d5db3008e1db496c0485c614cece30e4570526 - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Tue Apr 10 18:58:06 2018 +0200
Subject: skiller-rest-api: adapt to changed parameter order

 src/plugins/skiller/rest-api/skiller-rest-api.cpp |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

_Diff for modified files_:
diff --git a/src/plugins/skiller/rest-api/skiller-rest-api.cpp 
b/src/plugins/skiller/rest-api/skiller-rest-api.cpp
index a9a19b1..d8b9eb4 100644
--- a/src/plugins/skiller/rest-api/skiller-rest-api.cpp
+++ b/src/plugins/skiller/rest-api/skiller-rest-api.cpp
@@ -62,7 +62,7 @@ SkillerRestApi::init()
        rest_api_->add_handler
                (WebRequest::METHOD_DELETE, "/skills/{id}",
                 std::bind(&SkillerRestApi::cb_stop_skill, this, 
std::placeholders::_1));
-       rest_api_->add_handler<SkillCall, Skill>
+       rest_api_->add_handler<Skill, SkillCall>
                (WebRequest::METHOD_POST, "/call",
                 std::bind(&SkillerRestApi::cb_exec_skill, this, 
std::placeholders::_1));
        webview_rest_api_manager->register_api(rest_api_);

- *commit* 921c1dae4f690e16b027fa4f3607517338f2d71c - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Tue Apr 10 18:58:31 2018 +0200
Subject: libwebview: support throwing JSON object exceptions

 src/libs/webview/rest_api.h |   36 ++++++++++++++++++++++++++++++------
 1 files changed, 30 insertions(+), 6 deletions(-)

_Diff for modified files_:
diff --git a/src/libs/webview/rest_api.h b/src/libs/webview/rest_api.h
index 6a43b30..76cc941 100644
--- a/src/libs/webview/rest_api.h
+++ b/src/libs/webview/rest_api.h
@@ -76,8 +76,9 @@ class WebviewRestException : public fawkes::Exception
         * @param code HTTP response code
         * @param format format string for error message (cf. printf)
         */
+       explicit
        WebviewRestException(WebReply::Code code, const char *format, ...)
-               : Exception(), code_(code)
+               : Exception(), code_(code), content_type_("text/plain")
        {
                va_list va;
                va_start(va, format);
@@ -85,6 +86,20 @@ class WebviewRestException : public fawkes::Exception
                va_end(va);
        }
 
+       /** Constructor.
+        * @param code HTTP response code
+        * @param o Object to convert to JSON
+        * @param pretty true to enable pretty printing of the JSON input
+        */
+       template<typename T,
+               typename = std::enable_if_t<std::is_class<T>::value>>
+       WebviewRestException(WebReply::Code code, const T& o,
+                                    bool pretty = false)
+        : Exception(), code_(code), content_type_("application/json")
+       {
+               append("%s", o.to_json(pretty).c_str());        
+       }
+
        /** Get HTTP response code.
         * @return HTTP response code
         */
@@ -92,9 +107,18 @@ class WebviewRestException : public fawkes::Exception
        {
                return code_;
        }
-       
+
+       /** Get content type of response.
+        * @return HTTP content type
+        */
+       const std::string &
+       content_type() const {
+               return content_type_;
+       }
+
  private:
        WebReply::Code code_;
+       std::string content_type_;
 };
 
 /** REST parameters to pass to handlers.
@@ -217,7 +241,7 @@ class WebviewRestApi
                                            return handler(m);
                                    } catch (WebviewRestException &e) {
                                            return 
std::make_unique<WebviewRestReply>
-                                                   (e.code(), 
e.what_no_backtrace(), "text/plain");
+                                                   (e.code(), 
e.what_no_backtrace(), e.content_type());
                                    } catch (Exception &e) {
                                            auto r = 
std::make_unique<WebviewRestReply>
                                                    
(WebReply::HTTP_INTERNAL_SERVER_ERROR);
@@ -258,7 +282,7 @@ class WebviewRestApi
                                                    (WebReply::HTTP_OK, 
output.to_json(pretty_json_ || m.pretty_json()));
                                    } catch (WebviewRestException &e) {
                                            return 
std::make_unique<WebviewRestReply>
-                                                   (e.code(), 
e.what_no_backtrace(), "text/plain");
+                                                   (e.code(), 
e.what_no_backtrace(), e.content_type());
                                    } catch (Exception &e) {
                                            auto r = 
std::make_unique<WebviewRestReply>
                                                    
(WebReply::HTTP_INTERNAL_SERVER_ERROR);
@@ -288,7 +312,7 @@ class WebviewRestApi
                                            return 
handler(std::forward<I>(input), m);
                                    } catch (WebviewRestException &e) {
                                            return 
std::make_unique<WebviewRestReply>
-                                                   (e.code(), 
e.what_no_backtrace(), "text/plain");
+                                                   (e.code(), 
e.what_no_backtrace(), e.content_type());
                                    } catch (Exception &e) {
                                            auto r = 
std::make_unique<WebviewRestReply>
                                                    
(WebReply::HTTP_INTERNAL_SERVER_ERROR);
@@ -326,7 +350,7 @@ class WebviewRestApi
                                                    (WebReply::HTTP_OK, 
output.to_json(pretty_json_ || m.pretty_json()));
                                    } catch (WebviewRestException &e) {
                                            return 
std::make_unique<WebviewRestReply>
-                                                   (e.code(), 
e.what_no_backtrace(), "text/plain");
+                                                   (e.code(), 
e.what_no_backtrace(), e.content_type());
                                    } catch (Exception &e) {
                                            auto r = 
std::make_unique<WebviewRestReply>
                                                    
(WebReply::HTTP_INTERNAL_SERVER_ERROR);

- *commit* 35a5f413386a8b2a9d565451152d4c3c94eda296 - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Tue Apr 10 18:59:16 2018 +0200
Subject: libplugin: add convenience methods and make API nicer

 src/libs/plugin/manager.cpp |   39 +++++++++++++++++++++++++++++++++------
 src/libs/plugin/manager.h   |   10 +++++++---
 2 files changed, 40 insertions(+), 9 deletions(-)

_Diff for modified files_:
diff --git a/src/libs/plugin/manager.cpp b/src/libs/plugin/manager.cpp
index dbe1397..26ead39 100644
--- a/src/libs/plugin/manager.cpp
+++ b/src/libs/plugin/manager.cpp
@@ -255,9 +255,9 @@ PluginManager::get_loaded_plugins()
  * @return true if the plugin is currently loaded, false otherwise
  */
 bool
-PluginManager::is_loaded(const char *plugin_name)
+PluginManager::is_loaded(const std::string& plugin_name)
 {
-  if (plugin_loader->is_loaded(plugin_name)) {
+       if (plugin_loader->is_loaded(plugin_name.c_str())) {
     return true;
   } else {
     // Could still be a meta plugin
@@ -265,6 +265,33 @@ PluginManager::is_loaded(const char *plugin_name)
   }
 }
 
+/** Check if plugin is a meta plugin.
+ * @param plugin_name plugin to check
+ * @return true if the plugin is a meta plugin, false otherwise
+ */
+bool
+PluginManager::is_meta_plugin(const std::string& plugin_name)
+{
+       try {
+               std::string meta_plugin_path = __meta_plugin_prefix + 
plugin_name;
+               return (__config->is_string(meta_plugin_path.c_str()));
+       } catch (ConfigEntryNotFoundException &e) {
+               return false;
+       }
+}
+
+/** Get meta plugin children.
+ * @param plugin_name plugin to check
+ * @return List of plugins which would be loaded for this plugin.
+ */
+std::list<std::string>
+PluginManager::get_meta_plugin_children(const std::string& plugin_name)
+{
+       std::string meta_plugin_path = __meta_plugin_prefix + plugin_name;
+       std::string meta_plugin_str  = 
__config->get_string(meta_plugin_path.c_str());
+       return parse_plugin_list(meta_plugin_str.c_str());
+}
+
 
 /** Parse a list of plugin types.
  * Takes a comma-separated list of plugins and parses them into the individual
@@ -299,9 +326,9 @@ PluginManager::parse_plugin_list(const char *plugin_list)
  * to load. The plugin list can contain meta plugins.
  */
 void
-PluginManager::load(const char *plugin_list)
+PluginManager::load(const std::string& plugin_list)
 {
-       std::list<std::string> pp = parse_plugin_list(plugin_list);
+       std::list<std::string> pp = parse_plugin_list(plugin_list.c_str());
 
        for (std::list<std::string>::iterator i = pp.begin(); i != pp.end(); 
++i) {
                if ( i->length() == 0 ) continue;
@@ -385,7 +412,7 @@ PluginManager::load(const char *plugin_list)
  * @param plugin_name plugin to unload, can be a meta plugin.
  */
 void
-PluginManager::unload(const char *plugin_name)
+PluginManager::unload(const std::string& plugin_name)
 {
   MutexLocker lock(plugins.mutex());
   if ( (pit = find_if(plugins.begin(), plugins.end(), plname_eq(plugin_name)))
@@ -395,7 +422,7 @@ PluginManager::unload(const char *plugin_name)
       plugin_loader->unload(*pit);
       plugins.erase(pit);
       plugin_ids.erase(plugin_name);
-      notify_unloaded(plugin_name);
+      notify_unloaded(plugin_name.c_str());
       // find all meta plugins that required this module, this can no longer
       // be considered loaded
       __meta_plugins.lock();
diff --git a/src/libs/plugin/manager.h b/src/libs/plugin/manager.h
index 288c8a3..67174dc 100644
--- a/src/libs/plugin/manager.h
+++ b/src/libs/plugin/manager.h
@@ -72,10 +72,14 @@ class PluginManager
   // for FamListener
   virtual void fam_event(const char *filename, unsigned int mask);
 
-  void load(const char *plugin_list);
-  void unload(const char *plugin_name);
+  void load(const std::string& plugin_list);
+  void unload(const std::string& plugin_name);
 
-  bool is_loaded(const char *plugin_name);
+  bool is_loaded(const std::string& plugin_name);
+  bool is_meta_plugin(const std::string& plugin_name);
+
+  std::list<std::string>
+         get_meta_plugin_children(const std::string& plugin_name);
 
   std::list<std::string>                           get_loaded_plugins();
   std::list<std::pair<std::string, std::string> >  get_available_plugins();

- *commit* 8c1aa9014abec21a9b6df8404ba59c11db9bbaf5 - - - - - - - - - -
Author:  Tim Niemueller <niemuel...@kbsg.rwth-aachen.de>
Date:    Tue Apr 10 19:00:26 2018 +0200
Subject: webview: add plugin REST API

 src/plugins/webview/Makefile                       |    3 +-
 .../Makefile                                       |    4 +-
 src/plugins/webview/plugin-rest-api/api.yaml       |  183 ++++++++++++++++++++
 .../plugin-rest-api/model/Plugin.cpp}              |   73 ++++++---
 .../Backend.h => plugin-rest-api/model/Plugin.h}   |  130 ++++++++-------
 .../plugin-rest-api/model/PluginOpRequest.cpp}     |   45 +++---
 .../plugin-rest-api/model/PluginOpRequest.h}       |   54 +++---
 .../plugin-rest-api/model/PluginOpResponse.cpp}    |   48 +++--
 .../plugin-rest-api/model/PluginOpResponse.h}      |   54 ++++--
 .../webview/plugin-rest-api/plugin-rest-api.cpp    |  150 ++++++++++++++++
 .../plugin-rest-api.h}                             |   26 ++-
 src/plugins/webview/webview_plugin.cpp             |    2 +
 12 files changed, 588 insertions(+), 184 deletions(-)

_Diff for modified files_:
diff --git a/src/plugins/webview/Makefile b/src/plugins/webview/Makefile
index 99fded0..d864562 100644
--- a/src/plugins/webview/Makefile
+++ b/src/plugins/webview/Makefile
@@ -97,7 +97,8 @@ ifeq ($(HAVE_BOOST_LIBS)$(HAVE_LIBMICROHTTPD),11)
 
     OBJS_webview += blackboard-rest-api/blackboard-rest-api.o \
                     backendinfo-rest-api/backendinfo-rest-api.o \
-                    $(patsubst %.cpp,%.o,$(subst $(SRCDIR)/,,$(realpath 
$(wildcard $(SRCDIR)/blackboard-rest-api/model/*.cpp 
$(SRCDIR)/backendinfo-rest-api/model/*.cpp))))
+                    plugin-rest-api/plugin-rest-api.o \
+                    $(patsubst %.cpp,%.o,$(subst $(SRCDIR)/,,$(realpath 
$(wildcard $(SRCDIR)/*-rest-api/model/*.cpp))))
 
     ifeq ($(HAVE_JPEG),1)
       CFLAGS += -DHAVE_JPEG
diff --git a/src/plugins/webview/webview_plugin.cpp 
b/src/plugins/webview/webview_plugin.cpp
index cc2ef31..ebfb326 100644
--- a/src/plugins/webview/webview_plugin.cpp
+++ b/src/plugins/webview/webview_plugin.cpp
@@ -28,6 +28,7 @@
 #ifdef HAVE_REST_APIS
 #  include "blackboard-rest-api/blackboard-rest-api.h"
 #  include "backendinfo-rest-api/backendinfo-rest-api.h"
+#  include "plugin-rest-api/plugin-rest-api.h"
 #  ifdef HAVE_JPEG
 #    include "image-rest-api/image-rest-api.h"
 #  endif
@@ -54,6 +55,7 @@ WebviewPlugin::WebviewPlugin(Configuration *config)
 #ifdef HAVE_REST_APIS
   thread_list.push_back(new BlackboardRestApi());
   thread_list.push_back(new BackendInfoRestApi());
+  thread_list.push_back(new PluginRestApi());
 #  ifdef HAVE_JPEG
   thread_list.push_back(new ImageRestApi());
 #  endif




-- 
Fawkes Robotics Framework                 http://www.fawkesrobotics.org
_______________________________________________
fawkes-commits mailing list
fawkes-commits@lists.kbsg.rwth-aachen.de
https://lists.kbsg.rwth-aachen.de/listinfo/fawkes-commits

Reply via email to