This simple demo provides a pair of appDefinition file app-def-config.json and corresponding docker-compose file docker-compose.yml as an example that could be deployed by Edge Builder.
The docker-compose file contains various Edge Xpert services, including a Virtual Device Service will various simulated devices that will periodically send events to EdgeX Core-Data service for persistence, and an EdgeX application service will subscribe for these events and then publishes these events to external HiveMQ MQTT broker.
To set up the virtual device service with simulated devices, including a loadcell device, a temperature device, and a powermeter device, this tutorial will build a special container to add device profiles and devices into EdgeX Core-Metadata service for provisioning. This special container--metadata-setup--is part of docker-compose file, and it will be automatically started up when being deployed by Edge Builder.
- This example requires basic knowledge about Edge Xpert, and you could refer to Edge Xpert documentaiton for reference.
- Before running this simple demo, you must have Edge Builder environment up and running:
- Edge Builder Server
- Edge Builder Node
- Edge Builder CLI
- Copy this sample into your local environment
This main purpose of this tutorial is to demonstrate a special purpose container, metadata-setup, to set up device profiles and three simulated devices--LoadCell-Device, Powermeter-Device, and Temperature-Device--into EdgeX Core-Metadata service.
Examine the setup folder of this sample, there are various files and folders:
- Dockerfile - the Dockerfile used to build metadata-setup docker image
- setup.sh - the entrypoint script inside the metadata-setup container to add profiles and devices into EdgeX Core-Metadata service
- build.sh - the shell script to build the docker image for metadata-setup container
- devices - folder containing payload files for adding simulated devices: LoadCell-Device, Powermeter-Device, and Temperature-Device
- profiles - folder containing payload files for adding device profiles
Dockerfile is used to build the docker image for metadata-setup container. The content of Dockerfile is quite simple: use alpine 3.12 as base image, install curl utility, set METADATA_HOST environment variable, copy payload files for adding device profiles from profiles folder, copy payload files for adding devices from devices folder, copy and set setup.sh as the entrypoint script:
FROM alpine:3.12
LABEL maintainer="iotech <[email protected]>"
# install necessary tools
RUN apk add --update --no-cache curl
ENV BASE_DIR=/metadata-setup METADATA_HOST=core-metadata
WORKDIR $BASE_DIR
# Copy device profiles into profiles under working dir
COPY ./profiles/profile.*.yaml profiles/
# Copy devices creation payload into devices under working dir
COPY ./devices/device-*.json devices/
# Copy setup script into working dir
COPY setup.sh .
RUN chmod 755 ./setup.sh
ENTRYPOINT ["/metadata-setup/setup.sh"]
The entrypoint script, setup.sh, hosts the necessary commands to set up the metadata service. In this simple example, setup.sh will issue several REST calls to metadata service to add new device profiles and new devices:
#!/bin/sh
set -x
# this script assumes that core services, and device-virtual have been up and running.
# For docker deployment, these containers must share the same docker network.
execute_command_until_success(){
max_attempts="$1"
shift
expect_resp="$1"
shift
cmd="$@"
attempts=0
cmd_status=1
cmd_resp=""
until [ $cmd_status -eq 0 ] && [ "$cmd_resp" = "$expect_resp" ]
do
echo "cmd_status: $cmd_status"
echo "cmd_resp: $cmd_resp"
echo "max_attempts: $max_attempts"
if [ ${attempts} -eq ${max_attempts} ];then
echo "max attempts reached"
exit 1
fi
sleep 5
cmd_resp=$($cmd)
cmd_status=$?
attempts=$(($attempts+1))
done
}
# Check if Metadata REST API is ready prior to adding device profiles/devices
# The check should be limited to 20 times to avoid infinite loop
execute_command_until_success 20 "pong" curl -s http://"$METADATA_HOST":48081/api/v1/ping
# Add simulated device profiles into Metadata service through REST API
for profile in profiles/*
do
execute_command_until_success 1 200 curl -s -o /dev/null -w "%{http_code}" -X POST -F "file=@$profile" http://$METADATA_HOST:48081/api/v1/deviceprofile/uploadfile
done
echo "Wait for 20 seconds for device services to be ready with newly added profiles"
sleep 20
# Add simulated devices into Metadata service through REST API
for device in devices/*
do
execute_command_until_success 1 200 curl -s -o /dev/null -w "%{http_code}" -X POST http://$METADATA_HOST:48081/api/v1/device --data-binary "@$device"
done
As most of Edge Xpert services provide REST API for manipulation, users could refer to setup.sh as a sample and then revise the script as needed to satisfy specific setup requirements. Please refer to API documentation for more details about Edge Xpert REST API.
Use command below to build the docker image for metadata-setup. Note that specifying true for the first argument to push the docker image to Docker Hub. This docker image on Docker Hub must be accessible to your target Edge Builder node, so that metadata-setup container could be properly started up on your target node to setup EdgeX Core-Metadata service. The second argument, with default value iotechsys/edgexpert-demo-lua, specifies the name of metadata-setup docker image.
cd setup
./build.sh true <image name for metadata-setup container>
Once the metadata-setup docker image is successfully built and pushed to Docker Hub, remember to update docker-compose.yml file with the correct image name, so that the target Edge Builder node can pull the image later:
metadata-setup:
image: <image name for metadata-setup container>:metadata-setup-x86_64
Once the metadata-setup docker image is ready, user can deploy the simple demo through Edge Builder.
This simple demo uses app-def-config.json file to define the app definition for Edge Builder:
{
"AppDefinitionConfig": [
{
"Name": "simple-demo",
"Description": "A simple demo to deploy virtual device and an app service to export events to HiveMQ mqtt broker",
"ComposeFile": "/vagrant/simple-demo/docker-compose.yml"
}
]
}
Make sure you update ComposeFile with the proper file location of docker-compose.yml accessible to edgebuilder-cli utility.
The docker-compose.yml defines all the services required to run this simple demo, including redis, device-virtual, core services, an application service, and metadata-setup service:
version: "3.7"
networks:
edgex-network:
driver: bridge
volumes:
db-data:
nodered-data:
license-data:
# all common shared environment variables defined here:
x-common-env-variables: &common-variables
EDGEX_SECURITY_SECRET_STORE: "false"
Clients_Data_Host: edgex-core-data
Clients_CoreData_Host: edgex-core-data
Clients_Metadata_Host: edgex-core-metadata
Clients_Command_Host: edgex-core-command
Clients_VirtualDevice_Host: device-virtual
Databases_Primary_Host: edgex-redis
# license
EDGEXPERT_LICENSE_PATH: /edgexpert/licenses/
services:
#################################################################
# Database Services
#################################################################
redis:
image: redis:6.2.3
container_name: redis
hostname: redis
networks:
edgex-network:
aliases:
- edgex-redis
read_only: true
security_opt:
- no-new-privileges:true
volumes:
- db-data:/data:z
logging:
driver: "json-file"
options:
max-size: "10mb"
max-file: "20"
#################################################################
# Core Services
#################################################################
core-metadata:
image: iotechsys/edgexpert-core-metadata:1.8
container_name: core-metadata
hostname: core-metadata
command: "--confdir=/res"
ports:
- "48081:48081"
networks:
edgex-network:
aliases:
- edgex-core-metadata
read_only: true
security_opt:
- no-new-privileges:true
environment:
<<: *common-variables
Service_Host: core-metadata
volumes:
- license-data:/edgexpert/licenses/:ro
logging:
driver: "json-file"
options:
max-size: "10mb"
max-file: "20"
depends_on:
- edgexpert-license
- redis
core-data:
image: iotechsys/edgexpert-core-data:1.8
container_name: core-data
hostname: core-data
command: "--confdir=/res"
ports:
- "48080:48080"
- "5563:5563"
networks:
edgex-network:
aliases:
- edgex-core-data
read_only: true
security_opt:
- no-new-privileges:true
environment:
<<: *common-variables
Service_Host: core-data
volumes:
- license-data:/edgexpert/licenses/:ro
logging:
driver: "json-file"
options:
max-size: "10mb"
max-file: "20"
depends_on:
- edgexpert-license
- redis
- core-metadata
core-command:
image: iotechsys/edgexpert-core-command:1.8
container_name: core-command
hostname: core-command
command: "--confdir=/res"
ports:
- "48082:48082"
networks:
edgex-network:
aliases:
- edgex-core-command
read_only: true
security_opt:
- no-new-privileges:true
environment:
<<: *common-variables
Service_Host: core-command
volumes:
- license-data:/edgexpert/licenses/:ro
logging:
driver: "json-file"
options:
max-size: "10mb"
max-file: "20"
depends_on:
- edgexpert-license
- redis
- core-metadata
#################################################################
# Device Services
#################################################################
device-virtual:
image: iotechsys/edgexpert-device-virtual:1.8
container_name: device-virtual
hostname: device-virtual
command: "--confdir=/res"
ports:
- "49990:49990"
networks:
edgex-network:
aliases:
- device-virtual
read_only: true
security_opt:
- no-new-privileges:true
environment:
<<: *common-variables
Service_Host: device-virtual
logging:
driver: "json-file"
options:
max-size: "10mb"
max-file: "20"
depends_on:
- edgexpert-license
- core-data
- core-metadata
#################################################################
# App Services
#################################################################
app-service-mqtt-export:
image: iotechsys/edgexpert-app-configurable:1.8
container_name: app-service-mqtt-export
hostname: app-service-mqtt-export
networks:
edgex-network:
aliases:
- app-service-mqtt-export
read_only: true
security_opt:
- no-new-privileges:true
environment:
<<: *common-variables
Service_Host: app-service-mqtt-export
Database_Host: edgex-redis
MessageBus_SubscribeHost_Host: edgex-core-data
# pipeline overrides
WRITABLE_PIPELINE_FUNCTIONS_MQTTSECRETSEND_PARAMETERS_BROKERADDRESS: tcp://broker.hivemq.com:1883
WRITABLE_PIPELINE_FUNCTIONS_MQTTSECRETSEND_PARAMETERS_TOPIC: iotech_topic
WRITABLE_PIPELINE_FUNCTIONS_MQTTSECRETSEND_PARAMETERS_CLIENTID: iotech
command: "--confdir=/res --profile=mqtt-export"
volumes:
- license-data:/edgexpert/licenses/:ro
logging:
driver: "json-file"
options:
max-size: "10mb"
max-file: "20"
depends_on:
- edgexpert-license
- core-data
#################################################################
# EdgeXpert License
#################################################################
edgexpert-license:
image: iotechsys/sys-mgmt-demo-license-prov:nova-demo
container_name: edgexpert-license
hostname: edgexpert-license
read_only: true
security_opt:
- no-new-privileges:true
environment:
<<: *common-variables
volumes:
- license-data:/lic:ro
logging:
driver: "json-file"
options:
max-size: "10mb"
max-file: "20"
#################################################################
# Metadata Setup
#################################################################
metadata-setup:
image: iotechsys/edgexpert-demo-lua:metadata-setup-x86_64
container_name: metadata-setup
hostname: metadata-setup
read_only: true
security_opt:
- no-new-privileges:true
networks:
edgex-network:
aliases:
- metadata-setup
logging:
driver: "json-file"
options:
max-size: "10mb"
max-file: "20"
depends_on:
- core-metadata
Please use edgebuilder-cli command below to create an app definition as defined by app-def-config file:
edgebuilder-cli appDefinition add -f app-def-config.json
Please use edgebuilder-cli command below to deploy the app simple-demo to the target node:
edgebuilder-cli app create -d simple-demo -n <name of target node>
Please use edgebuilder-cli command below to start the app on the target node:
edgebuilder-cli app start -a <app id>
Once this simple demo app is up and running, a virtual device service with simulated devices will periodically send events to EdgeX Core-Data service, and an application service will export these events to a public MQTT broker: HiveMQ MQTT broker. To verify if EdgeX events are being successfully exported, you could either use mosquitto_sub command to subscribe for messages from HiveMQ MQTT broker on iotech_topic topic:
mosquitto_sub -h broker.hivemq.com -d -v -t iotech_topic
or use HiveMQ Websocket Client online tool to subscribe for messages with iotech_topic topic.
If everything works fine, user shall expect to see new EdgeX events flows in periodically, and note that some of these events shall come from three simulated devices--LoadCell-Device, Powermeter-Device, and Temperature-Device--set up by metadata-setup container:
Client mosq-z5nNReb9pVlwK7NI4g received PUBLISH (d0, q0, r0, m0, 'iotech_topic', ... (362 bytes))
iotech_topic {"id":"370d528f-7148-4544-bc7a-ee281a93f05f","device":"LoadCell-Device","created":1622462358533,"origin":1622462358532334382,"readings":[{"id":"23e51991-938d-4465-b0dd-9df346a90dba","origin":1622462358532023810,"device":"LoadCell-Device","name":"ShelfWeight","value":"6.108322e+02","valueType":"Float32","floatEncoding":"eNotation"}],"commandName":"ShelfWeight"}
.
.
.
Client mosq-z5nNReb9pVlwK7NI4g received PUBLISH (d0, q0, r0, m0, 'iotech_topic', ... (328 bytes))
iotech_topic {"id":"53318f1f-1e8d-449c-8af6-f39aa1e22763","device":"Powermeter-Device","created":1622462363538,"origin":1622462363536622830,"readings":[{"id":"843013cf-8294-4b30-89ef-a5757c3ab0ef","origin":1622462363536250366,"device":"Powermeter-Device","name":"FridgePower","value":"341","valueType":"Uint16"}],"commandName":"FridgePower"}
.
.
.
Client mosq-z5nNReb9pVlwK7NI4g received PUBLISH (d0, q0, r0, m0, 'iotech_topic', ... (366 bytes))
iotech_topic {"id":"c1665144-624b-43c2-85d2-de1325844252","device":"Temperature-Device","created":1622462368569,"origin":1622462368567101268,"readings":[{"id":"813c12d8-2850-4983-b920-e05e177e073e","origin":1622462368566652602,"device":"Temperature-Device","name":"FridgeTemp","value":"1.003703e+01","valueType":"Float32","floatEncoding":"eNotation"}],"commandName":"FridgeTemp"}
Client mosq-z5nNReb9pVlwK7NI4g received PUBLISH (d0, q0, r0, m0, 'iotech_topic', ... (364 bytes))
iotech_topic {"id":"7d8affc5-c066-481b-85db-52be412c59c9","device":"Temperature-Device","created":1622462368569,"origin":1622462368565848561,"readings":[{"id":"57a2ab60-aa1a-4acf-968e-a7ba8044c53d","origin":1622462368564540533,"device":"Temperature-Device","name":"AisleTemp","value":"2.901214e+01","valueType":"Float32","floatEncoding":"eNotation"}],"commandName":"AisleTemp"}