# Creating BEMCom Applications Recalling the [application concept](https://bemcom.readthedocs.io/en/latest/01_concepts.html#application-concept), it is apparent that a functional and building specific HAL instance will require one API service (including the metadata database), one message broker, one or more connector services, and optionally a raw message database service. The BEMCom repository provides fully functional implementations of an API service, a message broker and a raw message database which should be sufficient for most applications, thus effectively removing the burden of implementing these services from the user. Furthermore, a limited set of connector services is available, including connectors to integrate devices communicating via Modbus/TCP, MQTT, or the Open Charge Point Protocol (OCPP). All available services can be found in the [services](https://github.com/fzi-forschungszentrum-informatik/BEMCom/tree/master/services) folder in this repository. Leveraging the design concepts of BEMCom, i.e. the service oriented approach and the execution of services as Docker containers, creating an application is reduced to simply configuring and starting the selected services. ## Creating Applications with Docker This is a minimal example showing how a functional BEMCom application can be created by manually starting each service via a shell command, which should work on any machine having Docker installed. Please note that the more convenient way to achieve the same result by utilizing Docker Compose is subject to the [following section](https://bemcom.readthedocs.io/en/latest/02_application_creation.html#creating-applications-with-docker-compose). ### Demo Device In order to demonstrate the full functionality of BEMCom it is certainly necessary to begin with a device to communicate with. To that end we provide a [Demo Device](https://bemcom.readthedocs.io/en/latest/05_service_reference.html#demo-device) service reflecting a simple Modbus device which can be interpreted as a single room for which the temperature is measured and an actor (say an AC system that can heat and cool) exists to manipulate the room temperature. However, before the first service is started it is necessary to create a Docker network to allow communication between the services. ```bash docker network create bemcom-demo ``` After the Docker network exists, it is possible to start the Demo Device with: ```bash docker run -d --network bemcom-demo --name bemcom-demo-device bemcom/demo-device-tool:0.1.0 ``` Here the `-d` flag will cause Docker to run the Demo Device in the background, `--network bemcom-demo` will connect the container to the previously created network and `--name bemcom-demo-device` will assign the network name `bemcom-demo-device` to the container. To confirm that the Demo Device has been started successfully execute: ```bash docker logs bemcom-demo-device ``` Which should create an output similar to this one: ``` docker-entrypoint.sh: Starting up 2022-02-03 09:06:04,884-run-INFO: Current temperature: 21.0 ``` ### Message Broker Each BEMCom application requires a central message broker service to allow communication between the remaining services. The broker can be launched using the following command: ```bash docker run -d --network bemcom-demo --name bemcom-demo-mqtt-broker bemcom/mosquitto-mqtt-broker:0.1.0 ``` Checking the container logs with `docker logs bemcom-demo-mqtt-broker` indicates the correct operation by displaying: ``` 1643880100: mosquitto version 1.6.10 starting 1643880100: Config loaded from /mosquitto/config/mosquitto.conf. 1643880100: Opening ipv4 listen socket on port 1883. 1643880100: Opening ipv6 listen socket on port 1883. ``` ### Connector In order to establish communication between the Demo Device and the message broker it is necessary to spin up the corresponding connector, in this particular case that is the [Modbus/TCP connector](https://bemcom.readthedocs.io/en/latest/05_service_reference.html#modbus-tcp-connector) provided in the BEMCom repository, by typing: ```bash docker run -d --network bemcom-demo --name bemcom-demo-modbus-tcp-connector -e MQTT_BROKER_HOST=bemcom-demo-mqtt-broker -e MQTT_BROKER_PORT=1883 -e CONNECTOR_NAME=bemcom-demo-modbus-tcp-connector -e MODBUS_MASTER_IP=bemcom-demo-device -e MODBUS_MASTER_PORT=502 -e POLL_SECONDS=5 -e MODBUS_CONFIG='{"read_input_registers": [{"address": 1,"count":1,"unit": 1,"datatypes": "e"}],"write_register": [{"address": 1,"unit": 1,"datatypes": " To allow the user to view datapoint metadata. * `api_main | datapoint last value | Can view datapoint last value` -> To allow the user to fetch the last known values of all active sensor and actuator datapoints. * `api_main | datapoint value | Can add datapoint value` -> To allow the user to send values to all active actuator datapoints. ![application_creation_13_user_permissions](graphics/application_creation_13_user_permissions.png) Finally click the `Save` button to proceed. While there should now exist a user with sufficient permissions one will usually not want to login with user name and password for automatic accessing of APIs. Hence, click `Home` to return to the main page and afterwards `Token` to view the Token list page, which is yet empty. ![application_creation_14_token_list](graphics/application_creation_14_token_list.png) Click on `ADD TOKEN` to generate a access token for the demo user, then select the user name from the `User` dropdown menu and hit `SAVE`. This should return a page like the following: ![application_creation_15_token_detail](graphics/application_creation_15_token_detail.png) Using the token value obtained it should now be possible to interact with the REST API. ### Interacting with the REST API The commands below demonstrate the interaction with the REST API using [curl](https://curl.se/) commands. Before we go on one should store the token in a environment variable for convenience. ```bash TOKEN= ``` The `/datapoint/` endpoint allows to interact with the metadata of the datapoints. To fetch the metadata of all (active) datapoints one can simply execute: ```bash curl -X "GET" \ "http://localhost:8080/datapoint/" \ -H "Authorization: Bearer $TOKEN" ``` The command prompts the following output: ```json [{"id":1,"connector":{"name":"bemcom-demo-modbus-tcp-connector"},"key_in_connector":"read_input_registers__1__1","type":"sensor","data_format":"generic_numeric","short_name":"T_demoroom_measured","description":"The simulated current temparture of demo device.","min_value":null,"max_value":null,"allowed_values":null,"unit":"°C"},{"id":2,"connector":{"name":"bemcom-demo-modbus-tcp-connector"},"key_in_connector":"write_register__1__1","type":"actuator","data_format":"continuous_numeric","short_name":"T_demoroom_setpoint","description":"The setpoint temperature used for simulation of demo device","min_value":15.0,"max_value":35.0,"allowed_values":null,"unit":"°C"}] ``` The output looks much nicer if formated to usual JSON convention: ```json [ { "allowed_values" : null, "connector" : { "name" : "bemcom-demo-modbus-tcp-connector" }, "data_format" : "generic_numeric", "description" : "The simulated current temparture of demo device.", "id" : 1, "key_in_connector" : "read_input_registers__1__1", "max_value" : null, "min_value" : null, "short_name" : "T_demoroom_measured", "type" : "sensor", "unit" : "°C" }, { "allowed_values" : null, "connector" : { "name" : "bemcom-demo-modbus-tcp-connector" }, "data_format" : "continuous_numeric", "description" : "The setpoint temperature used for simulation of demo device", "id" : 2, "key_in_connector" : "write_register__1__1", "max_value" : 35, "min_value" : 15, "short_name" : "T_demoroom_setpoint", "type" : "actuator", "unit" : "°C" } ] ``` Note that the values are precisely the information that has been edited with the graphical admin user interface before. In a similar manner one can retrieve the last values per datapoint by executing from the `/datapoint/last_value/` endpoint: ``` bash curl -X "GET" \ "http://localhost:8080/datapoint/last_value/" \ -H "Authorization: Bearer $TOKEN" ``` This command yields a raw response of: ```json {"msgs_by_datapoint_id":{"1":{"value":"24.0","timestamp":1645474710272}}} ``` And again as pretty printed: ```json { "msgs_by_datapoint_id" : { "1" : { "timestamp" : 1645474760276, "value" : "24.0" } } } ``` Note that there is no value for the second datapoint (the actuator datapoint) yet as no value has been pushed to it yet. In order to change this and write values to actuator datapoints one can use the `/datapoint/{dp_id}/value/` enpdoint like: ```bash curl -X "POST" \ "http://localhost:8080/datapoint/2/value/" \ -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ -d '{"value": "17.0", "timestamp": 1645470000000}' ``` If one now polls the `/datapoint/last_value/` endpoint again a result similar to this one should be returned: ```json { "msgs_by_datapoint_id" : { "1" : { "timestamp" : 1645475475333, "value" : "18.703125" }, "2" : { "timestamp" : 1645470000000, "value" : "17.0" } } } ``` After setting the value of the actuator datapoint to 17.0 degrees this value is now known to the system as last value of the actuator datapoint. Finally once can observe that the value of the simulated sensor datapoint has changed and now moves towards the setpoint. ### Documentation of the REST API Finally is worth mentioning that the REST API of the API service provides an extensive interactive documentation using [Swagger UI](https://swagger.io/tools/swagger-ui/). Assuming again that the containers are executed on the local machine, one can access the interactive API root at the following URL: ``` http://localhost:8080/ ``` This should show a page like this: ![application_creation_16_swagger_ui](graphics/application_creation_16_swagger_ui.png) ## Cleaning Up #### With Docker If the containers were created manually (i.e. by executing one `docker run` command per service) the following commands stop and remove the containers and the network. ```bash docker container stop bemcom-demo-device bemcom-demo-modbus-tcp-connector bemcom-demo-mqtt-broker bemcom-demo-django-api docker container rm bemcom-demo-device bemcom-demo-modbus-tcp-connector bemcom-demo-mqtt-broker bemcom-demo-django-api docker network rm bemcom-demo ``` ### With Docker Compose If the demo application has been started with Docker Compose the following command should be sufficient to achieve the same result: ```bash docker-compose down ``` ### Removing the Images Finally, one may wish to remove the docker images that have been loaded to run the demo application (to free the hard drive) with the following command: ``` docker image rm bemcom/demo-device-tool:0.1.0 bemcom/modbus-tcp-connector:0.5.0 bemcom/mosquitto-mqtt-broker:0.1.0 bemcom/django-api:0.6.10 ```