├── app-services
├── simple-filter-xml-mqtt
│ ├── mosquitto.conf
│ ├── res
│ │ └── configuration.toml
│ └── main.go
├── http-command-service
│ ├── status-on-request.json
│ ├── status-off-request.json
│ ├── iot-hub
│ │ └── Quickstarts
│ │ │ ├── proxy-device
│ │ │ ├── run.sh
│ │ │ ├── src
│ │ │ │ └── main
│ │ │ │ │ └── java
│ │ │ │ │ └── com
│ │ │ │ │ └── microsoft
│ │ │ │ │ └── docs
│ │ │ │ │ └── iothub
│ │ │ │ │ └── samples
│ │ │ │ │ ├── Switch.java
│ │ │ │ │ └── ProxyDevice.java
│ │ │ └── pom.xml
│ │ │ ├── back-end-application
│ │ │ ├── off.sh
│ │ │ ├── on.sh
│ │ │ ├── pom.xml
│ │ │ └── src
│ │ │ │ └── main
│ │ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── microsoft
│ │ │ │ └── docs
│ │ │ │ └── iothub
│ │ │ │ └── samples
│ │ │ │ └── BackEndApplication.java
│ │ │ └── pom.xml
│ ├── Southbound.png
│ ├── res
│ │ └── configuration.toml
│ ├── functions
│ │ ├── console_print.go
│ │ └── switch_cmd.go
│ ├── main.go
│ └── README.md
├── advanced-target-type
│ ├── res
│ │ └── configuration.toml
│ ├── functions
│ │ ├── output.go
│ │ └── convert.go
│ ├── Post Person to Trigger.postman_collection.json
│ ├── README.md
│ └── main.go
├── simple-cbor-filter
│ ├── res
│ │ └── configuration.toml
│ ├── Device Simple Switch commands.postman_collection.json
│ ├── README.md
│ └── main.go
├── simple-filter-xml
│ ├── EdgeX Applications Function SDK.postman_collection.json
│ ├── Dockerfile
│ ├── res
│ │ └── configuration.toml
│ ├── main.go
│ └── EdgeX Application Function SDK Device Name.postman_collection.json
├── advanced-filter-convert-publish
│ ├── res
│ │ └── configuration.toml
│ ├── functions
│ │ ├── output.go
│ │ └── convert.go
│ ├── main.go
│ └── README.md
└── simple-filter-xml-post
│ ├── res
│ └── configuration.toml
│ └── main.go
├── .gitignore
├── go.mod
├── Makefile
├── README.md
└── LICENSE
/app-services/simple-filter-xml-mqtt/mosquitto.conf:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app-services/http-command-service/status-on-request.json:
--------------------------------------------------------------------------------
1 | {
2 | "status": "on"
3 | }
4 |
--------------------------------------------------------------------------------
/app-services/http-command-service/status-off-request.json:
--------------------------------------------------------------------------------
1 | {
2 | "status" : "off"
3 | }
4 |
--------------------------------------------------------------------------------
/app-services/http-command-service/iot-hub/Quickstarts/proxy-device/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | java -jar target/proxy-device-1.0.0-with-deps.jar
3 |
--------------------------------------------------------------------------------
/app-services/http-command-service/Southbound.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnwalicki/app-service-examples/master/app-services/http-command-service/Southbound.png
--------------------------------------------------------------------------------
/app-services/http-command-service/iot-hub/Quickstarts/back-end-application/off.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | java -jar target/back-end-application-1.0.0-with-deps.jar turn-off
3 |
--------------------------------------------------------------------------------
/app-services/http-command-service/iot-hub/Quickstarts/back-end-application/on.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | java -jar target/back-end-application-1.0.0-with-deps.jar turn-on
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.out
2 | examples/bin
3 | .idea/
4 | .vscode
5 | **/debug
6 | **/debug.test
7 | app-service
8 | *.log
9 | *.txt
10 | *.html
11 | *.out
12 | *.dll
13 | *.exe
14 | go.sum
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/edgexfoundry-holding/app-service-examples
2 |
3 | go 1.12
4 |
5 | require (
6 | github.com/edgexfoundry/app-functions-sdk-go v1.0.0-dev.2
7 | github.com/edgexfoundry/go-mod-core-contracts v0.1.25
8 | )
9 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: build clean
2 |
3 | GO=CGO_ENABLED=1 GO111MODULE=on go
4 |
5 | APP_SERVICES=app-services/*
6 |
7 | GIT_SHA=$(shell git rev-parse HEAD)
8 |
9 | .PHONY: build $(APP_SERVICES)
10 |
11 | build: $(APP_SERVICES)
12 |
13 | $(APP_SERVICES):
14 | $(GO) build $(GOFLAGS) -o $@/app-service ./$@
15 |
16 | clean:
17 | rm -f app-services/*/app-service
18 |
19 | docker:
20 | docker build \
21 | --build-arg http_proxy \
22 | --build-arg https_proxy \
23 | -f app-services/simple-filter-xml/Dockerfile \
24 | --label "git_sha=$(GIT_SHA)" \
25 | -t edgexfoundry/docker-simple-filter-xml:$(GIT_SHA) \
26 | -t edgexfoundry/docker-simple-filter-xml:dev \
27 | .
28 |
--------------------------------------------------------------------------------
/app-services/http-command-service/iot-hub/Quickstarts/proxy-device/src/main/java/com/microsoft/docs/iothub/samples/Switch.java:
--------------------------------------------------------------------------------
1 | package com.microsoft.docs.iothub.samples;
2 |
3 | import com.google.api.client.json.GenericJson;
4 | import com.google.api.client.util.Key;
5 |
6 | public class Switch extends GenericJson {
7 | @Key
8 | private String SwitchButton;
9 |
10 | public String getSwitchButton() {
11 | return SwitchButton;
12 | }
13 |
14 | public void setSwitchButton(String s) {
15 | this.SwitchButton = s;
16 | }
17 |
18 | @Override
19 | public String toString() {
20 | return "SwitchButton is " + SwitchButton;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app-services/http-command-service/res/configuration.toml:
--------------------------------------------------------------------------------
1 | [Writable]
2 | LogLevel = 'DEBUG'
3 | [Writable.StoreAndForward]
4 | Enabled = false
5 | RetryInterval = '5m'
6 | MaxRetryCount = 10
7 |
8 | [Service]
9 | BootTimeout = '30s'
10 | ClientMonitor = '15s'
11 | CheckInterval = '10s'
12 | Host = 'localhost'
13 | Port = 48095
14 | Protocol = 'http'
15 | ReadMaxLimit = 100
16 | StartupMsg = 'This is a sample Application Service to receive HTTP request as trigger'
17 | Timeout = '30s'
18 |
19 | [Registry]
20 | Host = 'localhost'
21 | Port = 8500
22 | Type = 'consul'
23 |
24 | [Logging]
25 | EnableRemote = false
26 | File = './logs/http-command-service.log'
27 |
28 | [Clients]
29 | [Clients.Logging]
30 | Protocol = "http"
31 | Host = "localhost"
32 | Port = 48061
33 | [Clients.Command]
34 | Protocol = 'http'
35 | Host = 'localhost'
36 | Port = 48082
37 |
38 | # This example expect custom type via HTTP Trigger
39 | [Binding]
40 | Type="http"
41 |
42 | [ApplicationSettings]
43 | DeviceID = "a0fa03eb-1ecc-4726-9017-fa21444020d9"
44 | CommandID = "384afb70-ead6-4ff8-a7a0-d96690002f76"
45 |
--------------------------------------------------------------------------------
/app-services/advanced-target-type/res/configuration.toml:
--------------------------------------------------------------------------------
1 | [Writable]
2 | LogLevel = 'DEBUG'
3 | [Writable.StoreAndForward]
4 | Enabled = false
5 | RetryInterval = '5m'
6 | MaxRetryCount = 10
7 |
8 | [Service]
9 | BootTimeout = '30s'
10 | ClientMonitor = '15s'
11 | CheckInterval = '10s'
12 | Host = 'localhost'
13 | Port = 48095
14 | Protocol = 'http'
15 | ReadMaxLimit = 100
16 | StartupMsg = 'This is a sample Application Service which use of TargetType'
17 | Timeout = '30s'
18 |
19 | [Registry]
20 | Host = 'localhost'
21 | Port = 8500
22 | Type = 'consul'
23 |
24 | [Logging]
25 | EnableRemote = false
26 | File = './logs/simple-filter-xml-mqtt.log'
27 |
28 | [Database]
29 | Type = "mongodb"
30 | Host = "localhost"
31 | Port = 27017
32 | Timeout = "30s"
33 | Username = ""
34 | Password = ""
35 |
36 | [Clients]
37 | [Clients.CoreData]
38 | Protocol = 'http'
39 | Host = 'localhost'
40 | Port = 48080
41 |
42 | [Clients.Logging]
43 | Protocol = "http"
44 | Host = "localhost"
45 | Port = 48061
46 |
47 | # This example expect custom type via HTTP Trigger
48 | [Binding]
49 | Type="http"
50 |
51 | [ApplicationSettings]
52 |
--------------------------------------------------------------------------------
/app-services/advanced-target-type/functions/output.go:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Intel Corporation
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 | //
16 |
17 | package functions
18 |
19 | import (
20 | "fmt"
21 |
22 | "github.com/edgexfoundry/app-functions-sdk-go/appcontext"
23 | )
24 |
25 | func PrintXmlToConsole(edgexcontext *appcontext.Context, params ...interface{}) (bool, interface{}) {
26 | edgexcontext.LoggingClient.Debug("PrintXmlToConsole")
27 |
28 | if len(params) < 1 {
29 | // We didn't receive a result
30 | return false, nil
31 | }
32 |
33 | fmt.Println(params[0].(string))
34 |
35 | return true, params[0]
36 | }
37 |
--------------------------------------------------------------------------------
/app-services/http-command-service/functions/console_print.go:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Intel Corporation
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 | //
16 |
17 | package functions
18 |
19 | import (
20 | "fmt"
21 |
22 | "github.com/edgexfoundry/app-functions-sdk-go/appcontext"
23 | )
24 |
25 | func PrintToConsole(edgexcontext *appcontext.Context, params ...interface{}) (bool, interface{}) {
26 | edgexcontext.LoggingClient.Debug("PrintToConsole")
27 |
28 | if len(params) < 1 {
29 | // We didn't receive a result
30 | return false, nil
31 | }
32 |
33 | fmt.Println(params[0].(string))
34 |
35 | return true, params[0]
36 | }
37 |
--------------------------------------------------------------------------------
/app-services/advanced-target-type/Post Person to Trigger.postman_collection.json:
--------------------------------------------------------------------------------
1 | {
2 | "info": {
3 | "_postman_id": "c8645c7d-0b6a-48ce-81a0-4fd0760f1334",
4 | "name": "Post Person to Trigger",
5 | "description": "Sends Person JSON to http trigger of Application Service",
6 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
7 | },
8 | "item": [
9 | {
10 | "name": "Person Trigger",
11 | "request": {
12 | "method": "POST",
13 | "header": [
14 | {
15 | "key": "Content-Type",
16 | "name": "Content-Type",
17 | "value": "application/json",
18 | "type": "text"
19 | }
20 | ],
21 | "body": {
22 | "mode": "raw",
23 | "raw": "{\n \"first_name\": \"Sam\",\n \"last_name\": \"Smith\",\n \"event\": null,\n \"phone\": {\n \"country_code\": 1,\n \"area_code\": 480,\n \"local_prefix\": 970,\n \"local_number\": 3476\n }\n}"
24 | },
25 | "url": {
26 | "raw": "127.0.0.1:48095/trigger",
27 | "host": [
28 | "127",
29 | "0",
30 | "0",
31 | "1"
32 | ],
33 | "port": "48095",
34 | "path": [
35 | "trigger"
36 | ]
37 | }
38 | },
39 | "response": []
40 | }
41 | ]
42 | }
--------------------------------------------------------------------------------
/app-services/http-command-service/iot-hub/Quickstarts/back-end-application/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | com.microsoft.docs.iothub.samples
6 | quickstarts
7 | 1.0
8 |
9 | com.microsoft.docs.iothub.samples
10 | back-end-application
11 | 1.0.0
12 | Back end application
13 |
14 |
15 | com.microsoft.azure.sdk.iot
16 | iot-service-client
17 | 1.13.0
18 |
19 |
20 |
21 | UTF-8
22 |
23 |
24 |
25 |
26 | org.apache.maven.plugins
27 | maven-jar-plugin
28 | 2.6
29 |
30 |
31 |
32 | true
33 | com.microsoft.docs.iothub.samples.BackEndApplication
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app-services/advanced-target-type/README.md:
--------------------------------------------------------------------------------
1 | # Advanced Target Type
2 |
3 | This **advanced-target-type** Application Service demonstrates how to create an Application Service that expects a custom type to feed to the functions pipeline. For more detail refer to the SDK's main README's section on [TargetType](https://github.com/edgexfoundry/app-functions-sdk-go/blob/master/README.md#target-type)
4 |
5 | To run this example:
6 |
7 | 1. Clone **[app-functions-sdk-go](https://github.com/edgexfoundry/app-functions-sdk-go)** repo
8 |
9 | 2. run `make build`
10 |
11 | 3. cd `examples/advance-target-type`
12 |
13 | 4. run `./advance-target-type`
14 |
15 | 5. Start PostMan
16 |
17 | 6. Load `Post Person to Trgger.postman_collection.json` collection in PostMan
18 |
19 | 7. Run the `Person Trigger` request
20 |
21 | - The following XML will be printed to the console by the Application Service and will be returned as the trigger HTTP response in PostMan.
22 |
23 | ```
24 |
25 | Sam
26 | Smith
27 |
28 | 1
29 | 480
30 | 970
31 | 3476
32 |
33 | +01(480) 970-3476
34 |
35 | ```
36 |
37 | - Note that the PhoneDisplay field that is not present in the XML sent from PostMan is now present and filled out.
38 |
39 |
40 |
--------------------------------------------------------------------------------
/app-services/simple-cbor-filter/res/configuration.toml:
--------------------------------------------------------------------------------
1 | [Writable]
2 | LogLevel = 'INFO'
3 | [Writable.StoreAndForward]
4 | Enabled = false
5 | RetryInterval = '5m'
6 | MaxRetryCount = 10
7 |
8 | [Service]
9 | BootTimeout = '30s'
10 | ClientMonitor = '15s'
11 | CheckInterval = '10s'
12 | Host = 'localhost'
13 | Port = 48095
14 | Protocol = 'http'
15 | ReadMaxLimit = 100
16 | StartupMsg = 'Simple CBOR Filter Application Service started'
17 | Timeout = '30s'
18 |
19 | [Registry]
20 | Host = 'localhost'
21 | Port = 8500
22 | Type = 'consul'
23 |
24 | [Logging]
25 | EnableRemote = false
26 | File = './logs/simple-filter-xml-mqtt.log'
27 |
28 | [Database]
29 | Type = "mongodb"
30 | Host = "localhost"
31 | Port = 27017
32 | Timeout = "30s"
33 | Username = ""
34 | Password = ""
35 |
36 | [Clients]
37 | [Clients.CoreData]
38 | Protocol = 'http'
39 | Host = 'localhost'
40 | Port = 48080
41 |
42 | [Clients.Logging]
43 | Protocol = "http"
44 | Host = "localhost"
45 | Port = 48061
46 |
47 | [MessageBus]
48 | Type = 'zero'
49 | [MessageBus.PublishHost]
50 | Host = '*'
51 | Port = 5564
52 | Protocol = 'tcp'
53 | [MessageBus.SubscribeHost]
54 | Host = 'localhost'
55 | Port = 5563
56 | Protocol = 'tcp'
57 |
58 | # Choose either an HTTP trigger or MessageBus trigger (aka Binding)
59 |
60 | #[Binding]
61 | #Type="http"
62 |
63 | [Binding]
64 | Type="messagebus"
65 | SubscribeTopic="events"
66 | PublishTopic="somewhere"
67 |
68 | [ApplicationSettings]
69 | ValueDescriptors = "Image"
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/app-services/simple-filter-xml/EdgeX Applications Function SDK.postman_collection.json:
--------------------------------------------------------------------------------
1 | {
2 | "info": {
3 | "_postman_id": "f6c30e8c-639b-49a5-853f-fdf4145a6274",
4 | "name": "EdgeX Applications Function SDK",
5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
6 | },
7 | "item": [
8 | {
9 | "name": "CoreData Event Trigger (Random-Float-Device)",
10 | "request": {
11 | "auth": {
12 | "type": "noauth"
13 | },
14 | "method": "POST",
15 | "header": [],
16 | "body": {
17 | "mode": "raw",
18 | "raw": "{\n \"id\": \"5bd794de0e36080001f53eab\",\n \"pushed\": 0,\n \"device\": \"Random-Float-Device\",\n \"created\": 1540855006481,\n \"modified\": 0,\n \"origin\": 1540855006469,\n \"schedule\": null,\n \"event\": null,\n \"readings\": [\n {\n \"id\": \"5bd794de0e36080001f53eac\",\n \"pushed\": 0,\n \"created\": 1540855006481,\n \"origin\": 1540855006469,\n \"modified\": 0,\n \"device\": \"Random-Float-Device\",\n \"name\": \"RandomValue_Float64\",\n \"value\": \"QAFk2HxRUOo=\"\n }\n ]\n}"
19 | },
20 | "url": {
21 | "raw": "127.0.0.1:9090",
22 | "host": [
23 | "127",
24 | "0",
25 | "0",
26 | "1"
27 | ],
28 | "port": "9090"
29 | },
30 | "description": "This request will send a valid EdgeX event to Applications Function SDK via HTTP in order to trigger a configure pipeline. The default is :9090."
31 | },
32 | "response": []
33 | }
34 | ]
35 | }
--------------------------------------------------------------------------------
/app-services/simple-filter-xml/Dockerfile:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright (c) 2019 Intel Corporation
3 | #
4 | # Licensed under the Apache License, Version 2.0 (the "License");
5 | # you may not use this file except in compliance with the License.
6 | # You may obtain a copy of the License at
7 | #
8 | # http://www.apache.org/licenses/LICENSE-2.0
9 | #
10 | # Unless required by applicable law or agreed to in writing, software
11 | # distributed under the License is distributed on an "AS IS" BASIS,
12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | # See the License for the specific language governing permissions and
14 | # limitations under the License.
15 | #
16 |
17 | FROM golang:1.12-alpine AS builder
18 |
19 | LABEL license='SPDX-License-Identifier: Apache-2.0' \
20 | copyright='Copyright (c) 2019: Intel'
21 |
22 | # add git for go modules
23 | RUN apk update && apk add --no-cache make git gcc libc-dev libsodium-dev zeromq-dev
24 | WORKDIR /app-service-examples
25 |
26 | COPY go.mod .
27 |
28 | RUN go mod download
29 |
30 | COPY . .
31 | RUN apk info -a zeromq-dev
32 |
33 | RUN make app-services/simple-filter-xml
34 |
35 | # Next image - Copy built Go binary into new workspace
36 | FROM alpine
37 |
38 | LABEL license='SPDX-License-Identifier: Apache-2.0' \
39 | copyright='Copyright (c) 2019: Intel'
40 |
41 | RUN apk --no-cache add zeromq
42 |
43 | COPY --from=builder /app-service-examples/app-services/simple-filter-xml/res /res
44 | COPY --from=builder /app-service-examples/app-services/simple-filter-xml/app-service /simple-filter-xml
45 |
46 | CMD [ "/simple-filter-xml" ,"--registry","--confdir=/res"]
--------------------------------------------------------------------------------
/app-services/advanced-filter-convert-publish/res/configuration.toml:
--------------------------------------------------------------------------------
1 | [Writable]
2 | LogLevel = 'INFO'
3 | [Writable.StoreAndForward]
4 | Enabled = false
5 | RetryInterval = '5m'
6 | MaxRetryCount = 10
7 |
8 | [Service]
9 | BootTimeout = '30s'
10 | ClientMonitor = '15s'
11 | CheckInterval = '10s'
12 | Host = 'localhost'
13 | Port = 48095
14 | Protocol = 'http'
15 | ReadMaxLimit = 100
16 | StartupMsg = 'This is a sample Application Service which filters, custom converts, prints and publishes back to message bus'
17 | Timeout = '30s'
18 |
19 | [Registry]
20 | Host = 'localhost'
21 | Port = 8500
22 | Type = 'consul'
23 |
24 | [Logging]
25 | EnableRemote = false
26 | File = './logs/simple-filter-xml-mqtt.log'
27 |
28 | [Database]
29 | Type = "mongodb"
30 | Host = "localhost"
31 | Port = 27017
32 | Timeout = "30s"
33 | Username = ""
34 | Password = ""
35 |
36 | [Clients]
37 | [Clients.CoreData]
38 | Protocol = 'http'
39 | Host = 'localhost'
40 | Port = 48080
41 |
42 | [Clients.Logging]
43 | Protocol = "http"
44 | Host = "localhost"
45 | Port = 48061
46 |
47 | [MessageBus]
48 | Type = 'zero'
49 | [MessageBus.PublishHost]
50 | Host = '*'
51 | Port = 5564
52 | Protocol = 'tcp'
53 | [MessageBus.SubscribeHost]
54 | Host = 'localhost'
55 | Port = 5563
56 | Protocol = 'tcp'
57 |
58 |
59 | # This example depends on events generated by Device-Virtual-Go, so must use MessageBus trigger.
60 | # It will publish back to the bus on the "converted" topic
61 | [Binding]
62 | Type="messagebus"
63 | SubscribeTopic="events"
64 | PublishTopic="converted"
65 |
66 | [ApplicationSettings]
67 | ApplicationName = "advanced-filter-convert-publish"
68 | ValueDescriptors = "RandomValue_Float32, RandomValue_Float64"
69 |
--------------------------------------------------------------------------------
/app-services/simple-filter-xml/res/configuration.toml:
--------------------------------------------------------------------------------
1 | [Writable]
2 | LogLevel = 'INFO'
3 | [Writable.StoreAndForward]
4 | Enabled = false
5 | RetryInterval = '5m'
6 | MaxRetryCount = 10
7 |
8 | [Service]
9 | BootTimeout = '30s'
10 | ClientMonitor = '15s'
11 | CheckInterval = '10s'
12 | Host = 'localhost'
13 | Port = 48095
14 | Protocol = 'http'
15 | ReadMaxLimit = 100
16 | StartupMsg = 'This is a sample Filter/XML Transform Application Service'
17 | Timeout = '30s'
18 |
19 | [Registry]
20 | Host = 'localhost'
21 | Port = 8500
22 | Type = 'consul'
23 |
24 | [Logging]
25 | EnableRemote = false
26 | File = './logs/simple-filter-xml-mqtt.log'
27 |
28 | [Database]
29 | Type = "mongodb"
30 | Host = "localhost"
31 | Port = 27017
32 | Timeout = "30s"
33 | Username = ""
34 | Password = ""
35 |
36 | [SecretStore]
37 | Host = 'localhost'
38 | Port = 8200
39 | Path = '/v1/secret/edgex/appservice/'
40 | Protocol = 'https'
41 |
42 | [SecretStore.Authentication]
43 | AuthType = 'X-Vault-Token'
44 | AuthToken = 'edgex'
45 |
46 | [Clients]
47 | [Clients.CoreData]
48 | Protocol = 'http'
49 | Host = 'localhost'
50 | Port = 48080
51 |
52 | [Clients.Logging]
53 | Protocol = "http"
54 | Host = "localhost"
55 | Port = 48061
56 |
57 | [MessageBus]
58 | Type = 'zero'
59 | [MessageBus.PublishHost]
60 | Host = '*'
61 | Port = 5564
62 | Protocol = 'tcp'
63 | [MessageBus.SubscribeHost]
64 | Host = 'localhost'
65 | Port = 5563
66 | Protocol = 'tcp'
67 |
68 | # Choose either an HTTP trigger or MessageBus trigger (aka Binding)
69 |
70 | #[Binding]
71 | #Type="http"
72 |
73 | [Binding]
74 | Type="messagebus"
75 | SubscribeTopic="events"
76 | PublishTopic="somewhere"
77 |
78 | [ApplicationSettings]
79 | ApplicationName = "simple-filter-xml"
80 |
--------------------------------------------------------------------------------
/app-services/http-command-service/iot-hub/Quickstarts/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.microsoft.docs.iothub.samples
5 | quickstarts
6 | IoT Hub Java quickstart samples
7 | 1.0
8 | pom
9 |
10 |
12 | proxy-device
13 | back-end-application
14 |
15 |
16 |
17 |
18 | org.apache.maven.plugins
19 | maven-compiler-plugin
20 | 3.3
21 |
22 | 1.8
23 | 1.8
24 |
25 |
26 |
27 | maven-shade-plugin
28 | 2.4
29 |
30 |
31 | package
32 |
33 | shade
34 |
35 |
36 |
37 |
38 | *:*
39 |
40 | META-INF/*.SF
41 | META-INF/*.RSA
42 |
43 |
44 |
45 | true
46 | with-deps
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/app-services/http-command-service/iot-hub/Quickstarts/proxy-device/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | com.microsoft.docs.iothub.samples
6 | quickstarts
7 | 1.0
8 |
9 | com.microsoft.docs.iothub.samples
10 | proxy-device
11 | 1.0.0
12 | Proxy device
13 |
14 |
15 | com.microsoft.azure.sdk.iot
16 | iot-device-client
17 | 1.16.0
18 |
19 |
20 | com.google.code.gson
21 | gson
22 | 2.3.1
23 |
24 |
25 | com.google.http-client
26 | google-http-client
27 | 1.23.0
28 |
29 |
30 | com.google.http-client
31 | google-http-client-gson
32 | 1.23.0
33 |
34 |
35 |
36 | UTF-8
37 |
38 |
39 |
40 |
41 | org.apache.maven.plugins
42 | maven-jar-plugin
43 | 2.6
44 |
45 |
46 |
47 | true
48 | com.microsoft.docs.iothub.samples.ProxyDevice
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/app-services/advanced-filter-convert-publish/functions/output.go:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Intel Corporation
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 | //
16 |
17 | package functions
18 |
19 | import (
20 | "encoding/json"
21 | "fmt"
22 |
23 | "github.com/edgexfoundry/app-functions-sdk-go/appcontext"
24 | "github.com/edgexfoundry/go-mod-core-contracts/models"
25 | )
26 |
27 | func PrintFloatValuesToConsole(edgexcontext *appcontext.Context, params ...interface{}) (bool, interface{}) {
28 | if len(params) < 1 {
29 | // We didn't receive a result
30 | return false, nil
31 | }
32 |
33 | event := params[0].(models.Event)
34 |
35 | for _, eventReading := range event.Readings {
36 | fmt.Printf("%s readable value from %s is %s\n", eventReading.Name, event.Device, eventReading.Value)
37 | }
38 |
39 | return true, event
40 |
41 | }
42 |
43 | func Publish(edgexcontext *appcontext.Context, params ...interface{}) (bool, interface{}) {
44 |
45 | edgexcontext.LoggingClient.Debug("Publish")
46 |
47 | if len(params) < 1 {
48 | // We didn't receive a result
49 | return false, nil
50 | }
51 |
52 | event := params[0].(models.Event)
53 | payload, _ := json.Marshal(event)
54 |
55 | // By calling Complete, the filtered and converted events will be posted back to the message bus on the new topic defined in the configuration.
56 | edgexcontext.Complete(payload)
57 |
58 | return false, nil
59 | }
60 |
--------------------------------------------------------------------------------
/app-services/simple-filter-xml-mqtt/res/configuration.toml:
--------------------------------------------------------------------------------
1 | [Writable]
2 | LogLevel = 'INFO'
3 | [Writable.StoreAndForward]
4 | Enabled = false
5 | RetryInterval = '5m'
6 | MaxRetryCount = 10
7 |
8 | [Service]
9 | BootTimeout = '30s'
10 | ClientMonitor = '15s'
11 | CheckInterval = '10s'
12 | Host = 'localhost'
13 | Port = 48095
14 | Protocol = 'http'
15 | ReadMaxLimit = 100
16 | StartupMsg = 'This is a sample Filter/XML/MQTT Transform Application Service'
17 | Timeout = '30s'
18 |
19 | [Registry]
20 | Host = 'localhost'
21 | Port = 8500
22 | Type = 'consul'
23 |
24 | [Logging]
25 | EnableRemote = false
26 | File = './logs/simple-filter-xml-mqtt.log'
27 |
28 | # Database is require when Store and Forward is enabled
29 | [Database]
30 | Type = "mongodb"
31 | Host = "localhost"
32 | Port = 27017
33 | Timeout = "30s"
34 | Username = ""
35 | Password = ""
36 |
37 | # SecretStore is required when Store and Forward is enabled and running with security
38 | # so Databse credentails can be pulled from Vault.
39 | [SecretStore]
40 | Host = 'localhost'
41 | Port = 8200
42 | Path = '/v1/secret/edgex/application-service/'
43 | Protocol = 'https'
44 |
45 | [SecretStore.Authentication]
46 | AuthType = 'X-Vault-Token'
47 | AuthToken = 'edgex'
48 |
49 | [Clients]
50 | [Clients.CoreData]
51 | Protocol = 'http'
52 | Host = 'localhost'
53 | Port = 48080
54 |
55 | [Clients.Logging]
56 | Protocol = "http"
57 | Host = "localhost"
58 | Port = 48061
59 |
60 | [MessageBus]
61 | Type = 'zero'
62 | [MessageBus.PublishHost]
63 | Host = '*'
64 | Port = 5564
65 | Protocol = 'tcp'
66 | [MessageBus.SubscribeHost]
67 | Host = 'localhost'
68 | Port = 5563
69 | Protocol = 'tcp'
70 |
71 | # Choose either an HTTP trigger or MessageBus trigger (aka Binding)
72 |
73 | #[Binding]
74 | #Type="http"
75 |
76 | [Binding]
77 | Type="messagebus"
78 | SubscribeTopic="events"
79 | PublishTopic="somewhere"
80 |
81 | [ApplicationSettings]
82 | ApplicationName = "simple-filter-xml-mqtt"
83 |
--------------------------------------------------------------------------------
/app-services/simple-filter-xml-post/res/configuration.toml:
--------------------------------------------------------------------------------
1 | [Writable]
2 | LogLevel = 'INFO'
3 | [Writable.StoreAndForward]
4 | Enabled = false
5 | RetryInterval = '5m'
6 | MaxRetryCount = 10
7 |
8 | [Service]
9 | BootTimeout = '30s'
10 | ClientMonitor = '15s'
11 | CheckInterval = '10s'
12 | Host = 'localhost'
13 | Port = 48095
14 | Protocol = 'http'
15 | ReadMaxLimit = 100
16 | StartupMsg = 'This is a sample Filter/XML/Post Transform Application Service'
17 | Timeout = '30s'
18 |
19 | [Registry]
20 | Host = 'localhost'
21 | Port = 8500
22 | Type = 'consul'
23 |
24 | # Database is require when Store and Forward is enabled
25 | [Database]
26 | Type = "mongodb"
27 | Host = "localhost"
28 | Port = 27017
29 | Timeout = "30s"
30 | Username = ""
31 | Password = ""
32 |
33 | # SecretStore is required when Store and Forward is enabled and running with security
34 | # so Databse credentails can be pulled from Vault.
35 | [SecretStore]
36 | Host = 'localhost'
37 | Port = 8200
38 | Path = '/v1/secret/edgex/application-service/'
39 | Protocol = 'https'
40 |
41 | [SecretStore.Authentication]
42 | AuthType = 'X-Vault-Token'
43 | AuthToken = 'edgex'
44 |
45 | [Logging]
46 | EnableRemote = false
47 | File = './logs/simple-filter-xml-post.log'
48 |
49 | [Clients]
50 | [Clients.CoreData]
51 | Protocol = 'http'
52 | Host = 'localhost'
53 | Port = 48080
54 |
55 | [Clients.Logging]
56 | Protocol = "http"
57 | Host = "localhost"
58 | Port = 48061
59 |
60 | # Required when using Store and Forward
61 | [Clients.Scheduler]
62 | Protocol = 'http'
63 | Host = 'localhost'
64 | Port = 48085
65 |
66 | [MessageBus]
67 | Type = 'zero'
68 | [MessageBus.PublishHost]
69 | Host = '*'
70 | Port = 5563
71 | Protocol = 'tcp'
72 | [MessageBus.SubscribeHost]
73 | Host = 'localhost'
74 | Port = 5563
75 | Protocol = 'tcp'
76 |
77 | # Choose either an HTTP trigger or MessageBus trigger (aka Binding)
78 |
79 | #[Binding]
80 | #Type="http"
81 |
82 | [Binding]
83 | Type="messagebus"
84 | SubscribeTopic="events"
85 | PublishTopic="somewhere"
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # app-service-examples
2 |
3 | ### Overview
4 |
5 | This repo contains various examples of Application Services based on the App Functions SDK. See the App Functions SDK for [README](https://github.com/edgexfoundry/app-functions-sdk-go/blob/v1.0.0-dev.2/README.md) for complete details on the SDK.
6 |
7 | ### Build Prerequisites
8 |
9 | Please see the [edgex-go README](https://github.com/edgexfoundry/edgex-go/blob/master/README.md).
10 |
11 | ### Building Examples
12 |
13 | The `makefile` is designed to build all/any examples under the `app-services` folder. Thus the makefile does not need to be updated when a new example is added to the `app-services` folder
14 |
15 | run `make build` to build all examples.
16 |
17 | run `make app-services/` to build a specific example, i.e. `make app-services/simple-filter-xml`
18 |
19 | For simplicity, the executable create for each example is named `app-service` and placed in that examples sub-folder.
20 |
21 | ### Running an Example
22 |
23 | After building examples you simply cd to the folder for the example you want to run and run the executable for that example with or without any of the supported command line options.
24 |
25 | The following commands will run the `simple-filter-xml` example
26 |
27 | run `cd app-services/simple-filter-xml`
28 |
29 | run `./app-service`
30 |
31 | ### Building App Service Docker Image
32 |
33 | The `simple-filter-xml` example contains an example `Dockerfile` to demonstrate how to build a **Docker Image** for your Application Service.
34 |
35 | The makefile also contains the `docker` target which will build the **Docker Image** for the `simple-filter-xml` example.
36 |
37 | run `make docker`
38 |
39 | > *Note that Application Services no longer use docker profiles. They use Environment Overrides in the docker compose file to make the necessary changes to the configuration for running in Docker. See the **Environment Variable Overrides For Docker** section in [App Service Configurable's README](https://github.com/edgexfoundry/app-service-configurable/blob/master/README.md#environment-variable-overrides-for-docker)* for more details and an example.
40 |
41 | ### Profiles
42 |
43 | The profiles folder contains example profiles for use with App Service Configurable.
--------------------------------------------------------------------------------
/app-services/http-command-service/main.go:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Intel Corporation
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 | //
16 |
17 | package main
18 |
19 | import (
20 | "fmt"
21 | "os"
22 |
23 | "github.com/edgexfoundry-holding/app-service-examples/app-services/http-command-service/functions"
24 | "github.com/edgexfoundry/app-functions-sdk-go/appsdk"
25 | "github.com/edgexfoundry/app-functions-sdk-go/pkg/transforms"
26 | )
27 |
28 | const (
29 | serviceKey = "httpCommandService"
30 | )
31 |
32 | func main() {
33 | // 1) First thing to do is to create an instance of the EdgeX SDK with TargetType Switch
34 | // and initialize it.
35 | edgexSdk := &appsdk.AppFunctionsSDK{ServiceKey: serviceKey, TargetType: &functions.Switch{}}
36 | if err := edgexSdk.Initialize(); err != nil {
37 | edgexSdk.LoggingClient.Error(fmt.Sprintf("SDK initialization failed: %v\n", err))
38 | os.Exit(-1)
39 | }
40 |
41 | // 2) This is our functions pipeline configuration, the collection of functions to
42 | // execute every time an event is triggered.
43 | err := edgexSdk.SetFunctionsPipeline(
44 | functions.SendSwitchCommand,
45 | functions.PrintToConsole,
46 | transforms.NewOutputData().SetOutputData,
47 | )
48 |
49 | if err != nil {
50 | edgexSdk.LoggingClient.Error("Setting Functions Pipeline failed: " + err.Error())
51 | os.Exit(-1)
52 | }
53 |
54 | // 3) Lastly, we'll go ahead and tell the SDK to "start" and begin listening for Persons
55 | // to trigger the pipeline.
56 | err = edgexSdk.MakeItRun()
57 | if err != nil {
58 | edgexSdk.LoggingClient.Error("MakeItRun returned error: ", err.Error())
59 | os.Exit(-1)
60 | }
61 |
62 | // Do any required cleanup here
63 | os.Exit(0)
64 | }
65 |
--------------------------------------------------------------------------------
/app-services/simple-filter-xml-post/main.go:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Intel Corporation
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 | //
16 |
17 | package main
18 |
19 | import (
20 | "fmt"
21 | "os"
22 |
23 | "github.com/edgexfoundry/app-functions-sdk-go/pkg/transforms"
24 |
25 | "github.com/edgexfoundry/app-functions-sdk-go/appsdk"
26 | )
27 |
28 | const (
29 | serviceKey = "sampleFilterXmlPost"
30 | )
31 |
32 | func main() {
33 | // 1) First thing to do is to create an instance of the EdgeX SDK and initialize it.
34 | edgexSdk := &appsdk.AppFunctionsSDK{ServiceKey: serviceKey}
35 | if err := edgexSdk.Initialize(); err != nil {
36 | edgexSdk.LoggingClient.Error(fmt.Sprintf("SDK initialization failed: %v\n", err))
37 | os.Exit(-1)
38 | }
39 |
40 | // 2) Since our FilterByDeviceName Function requires the list of device names we would
41 | // like to search for, we'll go ahead and define that now.
42 | deviceNames := []string{"Random-Float-Device"}
43 | // 3) This is our pipeline configuration, the collection of functions to
44 | // execute every time an event is triggered.
45 | edgexSdk.SetFunctionsPipeline(
46 | transforms.NewFilter(deviceNames).FilterByDeviceName,
47 | transforms.NewConversion().TransformToXML,
48 | transforms.NewHTTPSender("", "application/xml", false).HTTPPost,
49 | )
50 |
51 | // 4) This example doesn't have any application's specific configuration settings. Skipping call to sdk.ApplicationSettings
52 |
53 | // 5) Lastly, we'll go ahead and tell the SDK to "start" and begin listening for events
54 | // to trigger the pipeline.
55 | err := edgexSdk.MakeItRun()
56 | if err != nil {
57 | edgexSdk.LoggingClient.Error("MakeItRun returned error: ", err.Error())
58 | os.Exit(-1)
59 | }
60 |
61 | // Do any required cleanup here
62 |
63 | os.Exit(0)
64 | }
65 |
--------------------------------------------------------------------------------
/app-services/advanced-target-type/functions/convert.go:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Intel Corporation
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 | //
16 |
17 | package functions
18 |
19 | import (
20 | "encoding/xml"
21 | "fmt"
22 |
23 | "github.com/edgexfoundry/app-functions-sdk-go/appcontext"
24 | )
25 |
26 | type PhoneInfo struct {
27 | CountryCode int `json:"country_code"`
28 | AreaCode int `json:"area_code"`
29 | LocalPrefix int `json:"local_prefix"`
30 | LocalNumber int `json:"local_number"`
31 | }
32 |
33 | type Person struct {
34 | FirstName string `json:"first_name"`
35 | LastName string `json:"last_name"`
36 | Phone PhoneInfo `json:"phone"`
37 | PhoneDisplay string `json:"phone_display"`
38 | }
39 |
40 | func FormatPhoneDisplay(edgexcontext *appcontext.Context, params ...interface{}) (bool, interface{}) {
41 |
42 | edgexcontext.LoggingClient.Debug("Format Phone Number")
43 |
44 | if len(params) < 1 {
45 | // We didn't receive a result
46 | return false, nil
47 | }
48 |
49 | person, ok := params[0].(Person)
50 | if !ok {
51 | edgexcontext.LoggingClient.Error("type received is not a Person")
52 | }
53 |
54 | person.PhoneDisplay = fmt.Sprintf("+%02d(%03d) %03d-%04d",
55 | person.Phone.CountryCode, person.Phone.AreaCode, person.Phone.LocalPrefix, person.Phone.LocalNumber)
56 |
57 | return true, person
58 | }
59 |
60 | func ConvertToXML(edgexcontext *appcontext.Context, params ...interface{}) (bool, interface{}) {
61 | edgexcontext.LoggingClient.Debug("Convert to XML")
62 |
63 | if len(params) < 1 {
64 | // We didn't receive a result
65 | return false, nil
66 | }
67 |
68 | person, ok := params[0].(Person)
69 | if !ok {
70 | edgexcontext.LoggingClient.Error("type received is not a Person")
71 | }
72 |
73 | result, err := xml.MarshalIndent(person, "", " ")
74 | if err != nil {
75 | return false, fmt.Sprintf("Error parsing XML. Error: %s", err.Error())
76 | }
77 |
78 | return true, string(result)
79 | }
80 |
--------------------------------------------------------------------------------
/app-services/advanced-target-type/main.go:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Intel Corporation
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 | //
16 |
17 | package main
18 |
19 | import (
20 | "fmt"
21 | "os"
22 |
23 | "github.com/edgexfoundry/app-functions-sdk-go/appsdk"
24 | "github.com/edgexfoundry/app-functions-sdk-go/examples/advanced-target-type/functions"
25 | "github.com/edgexfoundry/app-functions-sdk-go/pkg/transforms"
26 | )
27 |
28 | const (
29 | serviceKey = "advancedTargetType"
30 | )
31 |
32 | func main() {
33 | // 1) First thing to do is to create an instance of the EdgeX SDK with your TargetType set
34 | // and initialize it. Note that the TargetType is a pointer to an instance of the type.
35 | edgexSdk := &appsdk.AppFunctionsSDK{ServiceKey: serviceKey, TargetType: &functions.Person{}}
36 | if err := edgexSdk.Initialize(); err != nil {
37 | edgexSdk.LoggingClient.Error(fmt.Sprintf("SDK initialization failed: %v\n", err))
38 | os.Exit(-1)
39 | }
40 |
41 | // 2) This is our functions pipeline configuration, the collection of functions to
42 | // execute every time an event is triggered.
43 | err := edgexSdk.SetFunctionsPipeline(
44 | functions.FormatPhoneDisplay, // Expects a Person as set by TargetType
45 | functions.ConvertToXML, // Expects a Person
46 | functions.PrintXmlToConsole, // Expects XML string
47 | transforms.NewOutputData().SetOutputData, // Expects string or []byte. Returns XML formatted Person with PhoneDisplay set sent as the trigger response
48 | )
49 |
50 | if err != nil {
51 | edgexSdk.LoggingClient.Error("Setting Functions Pipeline failed: " + err.Error())
52 | os.Exit(-1)
53 | }
54 |
55 | // 3) Lastly, we'll go ahead and tell the SDK to "start" and begin listening for Persons
56 | // to trigger the pipeline.
57 | err = edgexSdk.MakeItRun()
58 | if err != nil {
59 | edgexSdk.LoggingClient.Error("MakeItRun returned error: ", err.Error())
60 | os.Exit(-1)
61 | }
62 |
63 | // Do any required cleanup here
64 |
65 | os.Exit(0)
66 | }
67 |
--------------------------------------------------------------------------------
/app-services/http-command-service/functions/switch_cmd.go:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Intel Corporation
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 | //
16 |
17 | package functions
18 |
19 | import (
20 | "context"
21 |
22 | "github.com/edgexfoundry/app-functions-sdk-go/appcontext"
23 | )
24 |
25 | const (
26 | jsonSwitchOn = "{\"SwitchButton\": \"true\"}"
27 | jsonSwitchOff = "{\"SwitchButton\": \"false\"}"
28 |
29 | appConfigDeviceID = "DeviceID"
30 | appConfigCommandID = "CommandID"
31 | )
32 |
33 | type Switch struct {
34 | Status string `json:"status"`
35 | }
36 |
37 | func SendSwitchCommand(edgexcontext *appcontext.Context, params ...interface{}) (bool, interface{}) {
38 | edgexcontext.LoggingClient.Debug("Sending Switch Command")
39 |
40 | if len(params) < 1 {
41 | // We didn't receive a result
42 | return false, nil
43 | }
44 |
45 | if edgexcontext.CommandClient == nil {
46 | edgexcontext.LoggingClient.Error("Command client is available")
47 | return false, nil
48 | }
49 |
50 | sw, ok := params[0].(Switch)
51 |
52 | if !ok {
53 | edgexcontext.LoggingClient.Error("Invalid switch")
54 | return false, nil
55 | }
56 |
57 | deviceId := edgexcontext.Configuration.ApplicationSettings[appConfigDeviceID]
58 | commandId := edgexcontext.Configuration.ApplicationSettings[appConfigCommandID]
59 | var cmd string
60 |
61 | ctx := context.WithValue(context.Background(), "", "")
62 |
63 | switch status := sw.Status; status {
64 | case "on":
65 | edgexcontext.LoggingClient.Info("Switch On")
66 | cmd = jsonSwitchOn
67 | case "off":
68 | edgexcontext.LoggingClient.Info("Switch Off")
69 | cmd = jsonSwitchOff
70 | default:
71 | edgexcontext.LoggingClient.Error("Invalid switch status: " + status)
72 | return false, nil
73 | }
74 |
75 | edgexcontext.LoggingClient.Info("Device ID: " + deviceId)
76 | edgexcontext.LoggingClient.Info("Command ID: " + commandId)
77 | r, err := edgexcontext.CommandClient.Put(deviceId, commandId, cmd, ctx)
78 |
79 | if err == nil {
80 | edgexcontext.LoggingClient.Debug("Response : " + r)
81 | } else {
82 | edgexcontext.LoggingClient.Error("Error sending request: " + err.Error())
83 |
84 | }
85 |
86 | return true, cmd
87 | }
88 |
--------------------------------------------------------------------------------
/app-services/advanced-filter-convert-publish/functions/convert.go:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Intel Corporation
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 | //
16 |
17 | package functions
18 |
19 | import (
20 | "bytes"
21 | "encoding/base64"
22 | "encoding/binary"
23 | "fmt"
24 | "strconv"
25 |
26 | "github.com/edgexfoundry/app-functions-sdk-go/appcontext"
27 | "github.com/edgexfoundry/go-mod-core-contracts/models"
28 | )
29 |
30 | var precision = 4
31 |
32 | func ConvertToReadableFloatValues(edgexcontext *appcontext.Context, params ...interface{}) (bool, interface{}) {
33 |
34 | edgexcontext.LoggingClient.Debug("Convert to Readable Float Values")
35 |
36 | if len(params) < 1 {
37 | // We didn't receive a result
38 | return false, nil
39 | }
40 |
41 | event := params[0].(models.Event)
42 | for index := range event.Readings {
43 | eventReading := &event.Readings[index]
44 | edgexcontext.LoggingClient.Debug(fmt.Sprintf("Event Reading for %s: %s is '%s'", event.Device, eventReading.Name, eventReading.Value))
45 |
46 | data, err := base64.StdEncoding.DecodeString(eventReading.Value)
47 | if err != nil {
48 | edgexcontext.LoggingClient.Error(fmt.Sprintf("Unable to Base 64 decode float32/64 value ('%s'): %s", eventReading.Value, err.Error()))
49 | }
50 |
51 | switch eventReading.Name {
52 | case "RandomValue_Float32":
53 | var value float32
54 | err = binary.Read(bytes.NewReader(data), binary.BigEndian, &value)
55 | if err != nil {
56 | edgexcontext.LoggingClient.Error("Unable to decode float32 value bytes" + err.Error())
57 | }
58 |
59 | eventReading.Value = strconv.FormatFloat(float64(value), 'f', precision, 32)
60 |
61 | case "RandomValue_Float64":
62 | var value float64
63 | err := binary.Read(bytes.NewReader(data), binary.BigEndian, &value)
64 | if err != nil {
65 | edgexcontext.LoggingClient.Error("Unable to decode float64 value bytes: " + err.Error())
66 | }
67 |
68 | eventReading.Value = strconv.FormatFloat(value, 'f', precision, 64)
69 | }
70 |
71 | edgexcontext.LoggingClient.Debug(fmt.Sprintf("Converted Event Reading for %s: %s is '%s'", event.Device, eventReading.Name, eventReading.Value))
72 | }
73 |
74 | return true, event
75 | }
76 |
--------------------------------------------------------------------------------
/app-services/simple-cbor-filter/Device Simple Switch commands.postman_collection.json:
--------------------------------------------------------------------------------
1 | {
2 | "info": {
3 | "_postman_id": "7c5f760b-8466-46a1-afde-f2c7c0227620",
4 | "name": "Device Simple Switch commands",
5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
6 | },
7 | "item": [
8 | {
9 | "name": "Get Switch status",
10 | "protocolProfileBehavior": {
11 | "disableBodyPruning": true
12 | },
13 | "request": {
14 | "method": "GET",
15 | "header": [],
16 | "body": {
17 | "mode": "raw",
18 | "raw": ""
19 | },
20 | "url": {
21 | "raw": "http://localhost:48082/api/v1/device/name/Simple-Device01/command/Switch",
22 | "protocol": "http",
23 | "host": [
24 | "localhost"
25 | ],
26 | "port": "48082",
27 | "path": [
28 | "api",
29 | "v1",
30 | "device",
31 | "name",
32 | "Simple-Device01",
33 | "command",
34 | "Switch"
35 | ]
36 | }
37 | },
38 | "response": []
39 | },
40 | {
41 | "name": "Turn Switch on",
42 | "request": {
43 | "method": "PUT",
44 | "header": [
45 | {
46 | "key": "Content-Type",
47 | "name": "Content-Type",
48 | "value": "application/json",
49 | "type": "text"
50 | }
51 | ],
52 | "body": {
53 | "mode": "raw",
54 | "raw": "{\n\t\"SwitchButton\": \"true\"\n}"
55 | },
56 | "url": {
57 | "raw": "http://localhost:48082/api/v1/device/name/Simple-Device01/command/Switch",
58 | "protocol": "http",
59 | "host": [
60 | "localhost"
61 | ],
62 | "port": "48082",
63 | "path": [
64 | "api",
65 | "v1",
66 | "device",
67 | "name",
68 | "Simple-Device01",
69 | "command",
70 | "Switch"
71 | ]
72 | },
73 | "description": "Sends Switch command to turn on the switch"
74 | },
75 | "response": []
76 | },
77 | {
78 | "name": "Turn Switch off",
79 | "request": {
80 | "method": "PUT",
81 | "header": [
82 | {
83 | "key": "Content-Type",
84 | "name": "Content-Type",
85 | "value": "application/json",
86 | "type": "text"
87 | }
88 | ],
89 | "body": {
90 | "mode": "raw",
91 | "raw": "{\n\t\"SwitchButton\": \"false\"\n}"
92 | },
93 | "url": {
94 | "raw": "http://localhost:48082/api/v1/device/name/Simple-Device01/command/Switch",
95 | "protocol": "http",
96 | "host": [
97 | "localhost"
98 | ],
99 | "port": "48082",
100 | "path": [
101 | "api",
102 | "v1",
103 | "device",
104 | "name",
105 | "Simple-Device01",
106 | "command",
107 | "Switch"
108 | ]
109 | },
110 | "description": "Sends Switch command to turn off the switch"
111 | },
112 | "response": []
113 | }
114 | ]
115 | }
--------------------------------------------------------------------------------
/app-services/http-command-service/iot-hub/Quickstarts/back-end-application/src/main/java/com/microsoft/docs/iothub/samples/BackEndApplication.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 |
4 | // This application uses the Azure IoT Hub service SDK for Java
5 | // For samples see: https://github.com/Azure/azure-iot-sdk-java/tree/master/service/iot-service-samples
6 |
7 | package com.microsoft.docs.iothub.samples;
8 |
9 | import com.microsoft.azure.sdk.iot.service.devicetwin.DeviceMethod;
10 | import com.microsoft.azure.sdk.iot.service.devicetwin.MethodResult;
11 | import com.microsoft.azure.sdk.iot.service.exceptions.IotHubException;
12 |
13 | import java.io.IOException;
14 | import java.util.concurrent.TimeUnit;
15 |
16 | public class BackEndApplication {
17 |
18 | // Connection string for your IoT Hub
19 | // az iot hub show-connection-string --hub-name {your iot hub name} --policy-name service
20 | public static final String iotHubConnectionString = "{Your service connection string here}";
21 |
22 | // Device to call direct method on.
23 | public static final String deviceId = "{Your device Id here}";
24 |
25 | private static final String TURN_ON = "turn-on";
26 | private static final String TURN_OFF = "turn-off";
27 |
28 | // Name of direct method and payload.
29 | private static String methodName = "SetTelemetryInterval";
30 | public static final int payload = 10; // Number of seconds for telemetry interval.
31 |
32 | public static final Long responseTimeout = TimeUnit.SECONDS.toSeconds(30);
33 | public static final Long connectTimeout = TimeUnit.SECONDS.toSeconds(5);
34 |
35 | public static void main(String[] args) {
36 | try {
37 | // Create a DeviceMethod instance to call a direct method.
38 | DeviceMethod methodClient = DeviceMethod.createFromConnectionString(iotHubConnectionString);
39 |
40 | MethodResult result = null;
41 |
42 | if (args.length == 1) {
43 | switch (args[0]) {
44 | case TURN_ON:
45 | methodName = TURN_ON;
46 | break;
47 | case TURN_OFF:
48 | methodName = TURN_OFF;
49 | break;
50 | }
51 | }
52 |
53 | // Call the direct method.
54 | result = methodClient.invoke(deviceId, methodName, responseTimeout, connectTimeout, payload);
55 |
56 | System.out.println("Calling direct method " + methodName);
57 |
58 | if (result == null) {
59 | throw new IOException("Direct method invoke returns null");
60 | }
61 |
62 | // Show the acknowledgement from the device.
63 | System.out.println("Status: " + result.getStatus());
64 | System.out.println("Response: " + result.getPayload());
65 | } catch (IotHubException e) {
66 | System.out.println("IotHubException calling direct method:");
67 | System.out.println(e.getMessage());
68 | } catch (IOException e) {
69 | System.out.println("IOException calling direct method:");
70 | System.out.println(e.getMessage());
71 | }
72 | System.out.println("Done!");
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/app-services/simple-filter-xml/main.go:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Intel Corporation
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 | //
16 |
17 | package main
18 |
19 | import (
20 | "fmt"
21 | "os"
22 |
23 | "github.com/edgexfoundry/app-functions-sdk-go/appcontext"
24 | "github.com/edgexfoundry/app-functions-sdk-go/appsdk"
25 | "github.com/edgexfoundry/app-functions-sdk-go/pkg/transforms"
26 | )
27 |
28 | const (
29 | serviceKey = "sampleFilterXml"
30 | )
31 |
32 | var counter int
33 |
34 | func main() {
35 |
36 | // 1) First thing to do is to create an instance of the EdgeX SDK and initialize it.
37 | edgexSdk := &appsdk.AppFunctionsSDK{ServiceKey: serviceKey}
38 | if err := edgexSdk.Initialize(); err != nil {
39 | edgexSdk.LoggingClient.Error(fmt.Sprintf("SDK initialization failed: %v\n", err))
40 | os.Exit(-1)
41 | }
42 |
43 | // 2) Since our FilterByDeviceName Function requires the list of device names we would
44 | // like to search for, we'll go ahead and define that now.
45 | deviceNames := []string{"Random-Float-Device"}
46 |
47 | // 3) This is our pipeline configuration, the collection of functions to
48 | // execute every time an event is triggered.
49 | edgexSdk.SetFunctionsPipeline(
50 | transforms.NewFilter(deviceNames).FilterByDeviceName,
51 | transforms.NewConversion().TransformToXML,
52 | printXMLToConsole,
53 | )
54 |
55 | // 4) shows how to access the application's specific configuration settings.
56 | appSettings := edgexSdk.ApplicationSettings()
57 | if appSettings != nil {
58 | appName, ok := appSettings["ApplicationName"]
59 | if ok {
60 | edgexSdk.LoggingClient.Info(fmt.Sprintf("%s now running...", appName))
61 | } else {
62 | edgexSdk.LoggingClient.Error("ApplicationName application setting not found")
63 | os.Exit(-1)
64 | }
65 | } else {
66 | edgexSdk.LoggingClient.Error("No application settings found")
67 | os.Exit(-1)
68 | }
69 |
70 | // 5) Lastly, we'll go ahead and tell the SDK to "start" and begin listening for events
71 | // to trigger the pipeline.
72 | err := edgexSdk.MakeItRun()
73 | if err != nil {
74 | edgexSdk.LoggingClient.Error("MakeItRun returned error: ", err.Error())
75 | os.Exit(-1)
76 | }
77 |
78 | // Do any required cleanup here
79 |
80 | os.Exit(0)
81 | }
82 |
83 | func printXMLToConsole(edgexcontext *appcontext.Context, params ...interface{}) (bool, interface{}) {
84 | if len(params) < 1 {
85 | // We didn't receive a result
86 | return false, nil
87 | }
88 |
89 | fmt.Println(params[0].(string))
90 |
91 | // Leverage the built in logging service in EdgeX
92 | edgexcontext.LoggingClient.Debug("XML printed to console")
93 |
94 | edgexcontext.Complete([]byte(params[0].(string)))
95 | return false, nil
96 | }
97 |
--------------------------------------------------------------------------------
/app-services/advanced-filter-convert-publish/main.go:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Intel Corporation
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 | //
16 |
17 | package main
18 |
19 | import (
20 | "fmt"
21 | "os"
22 | "strings"
23 |
24 | "github.com/edgexfoundry/app-functions-sdk-go/pkg/transforms"
25 |
26 | "github.com/edgexfoundry/app-functions-sdk-go/appsdk"
27 | "github.com/edgexfoundry/app-functions-sdk-go/examples/advanced-filter-convert-publish/functions"
28 | )
29 |
30 | const (
31 | serviceKey = "advancedFilterConvertPublish"
32 | )
33 |
34 | func main() {
35 | // 1) First thing to do is to create an instance of the EdgeX SDK and initialize it.
36 | edgexSdk := &appsdk.AppFunctionsSDK{ServiceKey: serviceKey}
37 | if err := edgexSdk.Initialize(); err != nil {
38 | edgexSdk.LoggingClient.Error(fmt.Sprintf("SDK initialization failed: %v\n", err))
39 | os.Exit(-1)
40 | }
41 |
42 | // 2) shows how to access the application's specific configuration settings.
43 | appSettings := edgexSdk.ApplicationSettings()
44 | if appSettings == nil {
45 | edgexSdk.LoggingClient.Error("No application settings found")
46 | os.Exit(-1)
47 | }
48 |
49 | appName, ok := appSettings["ApplicationName"]
50 | if ok {
51 | edgexSdk.LoggingClient.Info(fmt.Sprintf("%s now running...", appName))
52 | } else {
53 | edgexSdk.LoggingClient.Error("ApplicationName application setting not found")
54 | os.Exit(-1)
55 | }
56 |
57 | valueDescriptorList, ok := appSettings["ValueDescriptors"]
58 | if !ok {
59 | edgexSdk.LoggingClient.Error("ValueDescriptors application setting not found")
60 | os.Exit(-1)
61 | }
62 |
63 | // 3) Since our FilterByValueDescriptor Function requires the list of ValueDescriptor's we would
64 | // like to search for, we'll go ahead create that list from the corresponding configuration setting.
65 | valueDescriptorList = strings.Replace(valueDescriptorList, " ", "", -1)
66 | valueDescriptors := strings.Split(valueDescriptorList, ",")
67 | edgexSdk.LoggingClient.Info(fmt.Sprintf("Filtering for %v value descriptors...", valueDescriptors))
68 |
69 | // 4) This is our functions pipeline configuration, the collection of functions to
70 | // execute every time an event is triggered.
71 | err := edgexSdk.SetFunctionsPipeline(
72 | transforms.NewFilter(valueDescriptors).FilterByValueDescriptor,
73 | functions.ConvertToReadableFloatValues,
74 | functions.PrintFloatValuesToConsole,
75 | functions.Publish,
76 | )
77 |
78 | if err != nil {
79 | edgexSdk.LoggingClient.Error("SDK initialization failed: " + err.Error())
80 | os.Exit(-1)
81 | }
82 |
83 | // 5) Lastly, we'll go ahead and tell the SDK to "start" and begin listening for events
84 | // to trigger the pipeline.
85 | err = edgexSdk.MakeItRun()
86 | if err != nil {
87 | edgexSdk.LoggingClient.Error("MakeItRun returned error: ", err.Error())
88 | os.Exit(-1)
89 | }
90 |
91 | // Do any required cleanup here
92 |
93 | os.Exit(0)
94 | }
95 |
--------------------------------------------------------------------------------
/app-services/simple-cbor-filter/README.md:
--------------------------------------------------------------------------------
1 | # Simple CBOR Filter Application Service
2 |
3 | This **simple-cbor-filter** Application Service demonstrates end to end `CBOR` integration. It depends on the **device-simple** example device service from **Device SDK Go** to generate `CBOR` encode events.
4 |
5 | This **simple-cbor-filter** Application Service uses two application functions:
6 |
7 | - Built in **Filter by Value Descriptor** function to filter just for the **Image** values.
8 | - Custom **Process Images** function which re-encodes the `binary value` as an Image and prints stats about the image to the console.
9 |
10 | The end result from this application service is that it shows that the Application Functions SDK is un-marshaling `CBOR` encode events sent from the **device-simple** device service. These event can be processed by functions similar to `JSON` encoded events. The only difference is the `CBOR` encode events have the `BinaryValue` field set, while the `JSON` encoded events have the `Value` field set.
11 |
12 | #### Follow these steps to run the end to end CBOR demonstration
13 |
14 | 1. Start EdgeX Mongo
15 |
16 | - [ ] clone **[developer-scripts](https://github.com/edgexfoundry/developer-scripts)** repo
17 | - [ ] cd **compose-files** folder
18 | - [ ] run "**docker-compose up mongo**"
19 | - This uses the default compose file to start the EdgeX Mongo service which exposes it's port to apps running on localhost
20 |
21 | 2. Run EdgeX cores services
22 |
23 | - [ ] Clone **[edgex-go](https://github.com/edgexfoundry/edgex-go)** repo
24 | - [ ] run "**make build**"
25 | - [ ] run "**make run**"
26 | - This starts all the required EdgeX services
27 |
28 | 3. Run **simple-cbor-filter** example
29 |
30 | - [ ] Clone **[app-functions-sdk-go](https://github.com/edgexfoundry/app-functions-sdk-go)** repo
31 | - [ ] run "**make build**"
32 | - [ ] cd to **examples/simple-cbor-filter** folder
33 | - [ ] run "./**simple-cbor-filter**"
34 |
35 | 4. Run **device-simple** device service
36 |
37 | - [ ] Clone **** repo
38 |
39 | - [ ] run "**make build**"
40 |
41 | - [ ] cd to **example/cmd/device-simple** folder
42 |
43 | - [ ] run "./**device-simple**"
44 |
45 | This sample device service will send a `png` (light bulb on) or `jpeg` (light bulb off) image every 30 seconds. The image it sends depends on the value of its `switch` resource, which is `off` (false) by default.
46 |
47 | 5. Now data will be flowing due to auto-events configured in **device-simple**.
48 |
49 | - In the terminal that you ran **simple-cbor-filter** you will see the messages like this:
50 |
51 | ```text
52 | Received Image from Device: Simple-Device01, ReadingName: Image, Image Type: jpeg, Image Size: (1000,1307), Color in middle: {0 128 128}
53 | ```
54 |
55 | Note that the image received is a jpeg since the `switch` resource in **device-simple** is set to `off ` (false)
56 |
57 | - The `switch` resource can be queried and changed using commands sent via PostMan by doing the following:
58 |
59 | 1. Start PostMan
60 |
61 | 2. Load the postman collection from the **simple-cbor-filter** example
62 |
63 | `Device Simple Switch commands.postman_collection.json`
64 |
65 | 3. This collection contains 3 commands
66 |
67 | - `Get Switch status`
68 | - `Turn Switch on`
69 | - `Turn Switch off`
70 |
71 | 4. Run `Turn Switch on`
72 |
73 | - Now see how the **simple-cbor-filter** output has changed
74 |
75 | ```
76 | Received Image from Device: Simple-Device01, ReadingName: Image, Image Type: png, Image Size: (1000,1307), Color in middle: {255 246 0 255}
77 | ```
78 |
79 |
80 |
--------------------------------------------------------------------------------
/app-services/simple-cbor-filter/main.go:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Intel Corporation
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 | //
16 |
17 | package main
18 |
19 | import (
20 | "bytes"
21 | "errors"
22 | "fmt"
23 | "image"
24 | _ "image/jpeg"
25 | _ "image/png"
26 | "os"
27 | "strings"
28 |
29 | "github.com/edgexfoundry/app-functions-sdk-go/pkg/transforms"
30 |
31 | "github.com/edgexfoundry/go-mod-core-contracts/models"
32 |
33 | "github.com/edgexfoundry/app-functions-sdk-go/appcontext"
34 | "github.com/edgexfoundry/app-functions-sdk-go/appsdk"
35 | )
36 |
37 | const (
38 | serviceKey = "sampleCborFilter"
39 | )
40 |
41 | var counter int = 0
42 |
43 | func main() {
44 | // 1) First thing to do is to create an instance of the EdgeX SDK and initialize it.
45 | edgexSdk := &appsdk.AppFunctionsSDK{ServiceKey: serviceKey}
46 | if err := edgexSdk.Initialize(); err != nil {
47 | edgexSdk.LoggingClient.Error(fmt.Sprintf("SDK initialization failed: %v\n", err))
48 | os.Exit(-1)
49 | }
50 |
51 | // 2) shows how to access the application's specific configuration settings.
52 | appSettings := edgexSdk.ApplicationSettings()
53 | if appSettings == nil {
54 | edgexSdk.LoggingClient.Error("No application settings found")
55 | os.Exit(-1)
56 | }
57 |
58 | valueDescriptorList, ok := appSettings["ValueDescriptors"]
59 | if !ok {
60 | edgexSdk.LoggingClient.Error("ValueDescriptors application setting not found")
61 | os.Exit(-1)
62 | }
63 |
64 | // 3) Since our FilterByValueDescriptor Function requires the list of ValueDescriptor's we would
65 | // like to search for, we'll go ahead create that list from the corresponding configuration setting.
66 | valueDescriptorList = strings.Replace(valueDescriptorList, " ", "", -1)
67 | valueDescriptors := strings.Split(valueDescriptorList, ",")
68 | edgexSdk.LoggingClient.Info(fmt.Sprintf("Filtering for %v value descriptors...", valueDescriptors))
69 |
70 | // 4) This is our pipeline configuration, the collection of functions to
71 | // execute every time an event is triggered.
72 | edgexSdk.SetFunctionsPipeline(
73 | transforms.NewFilter(valueDescriptors).FilterByValueDescriptor,
74 | processImages,
75 | )
76 |
77 | // 5) Lastly, we'll go ahead and tell the SDK to "start" and begin listening for events
78 | // to trigger the pipeline.
79 | err := edgexSdk.MakeItRun()
80 | if err != nil {
81 | edgexSdk.LoggingClient.Error("MakeItRun returned error: ", err.Error())
82 | os.Exit(-1)
83 | }
84 |
85 | // Do any required cleanup here
86 |
87 | os.Exit(0)
88 | }
89 |
90 | func processImages(edgexcontext *appcontext.Context, params ...interface{}) (bool, interface{}) {
91 | if len(params) < 1 {
92 | // We didn't receive a result
93 | return false, nil
94 | }
95 |
96 | event, ok := params[0].(models.Event)
97 | if !ok {
98 | return false, errors.New("processImages didn't receive expect models.Event type")
99 |
100 | }
101 |
102 | for _, reading := range event.Readings {
103 | // For this to work the image/jpeg & image/png packages must be imported to register their decoder
104 | imageData, imageType, err := image.Decode(bytes.NewReader(reading.BinaryValue))
105 |
106 | if err != nil {
107 | return false, errors.New("unable to decode image: " + err.Error())
108 | }
109 |
110 | // Since this is a example, we will just print put some stats from the images received
111 | fmt.Printf("Received Image from Device: %s, ReadingName: %s, Image Type: %s, Image Size: %s, Color in middle: %v\n",
112 | reading.Device, reading.Name, imageType, imageData.Bounds().Size().String(),
113 | imageData.At(imageData.Bounds().Size().X/2, imageData.Bounds().Size().Y/2))
114 | }
115 |
116 | return false, nil
117 | }
118 |
--------------------------------------------------------------------------------
/app-services/simple-filter-xml-mqtt/main.go:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Intel Corporation
3 | //
4 | // Licensed under the Apache License, Version 2.0 (the "License");
5 | // you may not use this file except in compliance with the License.
6 | // You may obtain a copy of the License at
7 | //
8 | // http://www.apache.org/licenses/LICENSE-2.0
9 | //
10 | // Unless required by applicable law or agreed to in writing, software
11 | // distributed under the License is distributed on an "AS IS" BASIS,
12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | // See the License for the specific language governing permissions and
14 | // limitations under the License.
15 | //
16 |
17 | package main
18 |
19 | import (
20 | "fmt"
21 | "os"
22 |
23 | "github.com/edgexfoundry/app-functions-sdk-go/pkg/transforms"
24 |
25 | "github.com/edgexfoundry/app-functions-sdk-go/appcontext"
26 | "github.com/edgexfoundry/app-functions-sdk-go/appsdk"
27 | "github.com/edgexfoundry/go-mod-core-contracts/models"
28 | )
29 |
30 | const (
31 | serviceKey = "sampleFilterXmlMqtt"
32 | )
33 |
34 | func main() {
35 | // 1) First thing to do is to create an instance of the EdgeX SDK and initialize it.
36 | edgexSdk := &appsdk.AppFunctionsSDK{ServiceKey: serviceKey}
37 | if err := edgexSdk.Initialize(); err != nil {
38 | edgexSdk.LoggingClient.Error(fmt.Sprintf("SDK initialization failed: %v\n", err))
39 | os.Exit(-1)
40 | }
41 |
42 | // 2) Since our FilterByDeviceName Function requires the list of device names we would
43 | // like to search for, we'll go ahead and define that now.
44 | deviceNames := []string{"Random-Float-Device"}
45 | // Since we are using MQTT, we'll also need to set up the addressable model to
46 | // configure it to send to our broker. If you don't have a broker setup you can pull one from docker i.e:
47 | // docker run -it -p 1883:1883 -p 9001:9001 eclipse-mosquitto
48 | addressable := models.Addressable{
49 | Address: "localhost",
50 | Port: 1883,
51 | Protocol: "tcp",
52 | Publisher: "MyApp",
53 | User: "",
54 | Password: "",
55 | Topic: "sampleTopic",
56 | }
57 |
58 | // Using default settings, so not changing any fields in MqttConfig
59 | mqttConfig := transforms.MqttConfig{}
60 |
61 | // Make sure you change KeyFile and CertFile here to point to actual key/cert files
62 | // or an error will be logged for failing to load key/cert files
63 | // If you don't use key/cert for MQTT authentication, just pass nil to NewMQTTSender() as following:
64 | // mqttSender := transforms.NewMQTTSender(edgexSdk.LoggingClient, addressable, nil, mqttConfig)
65 | pair := transforms.KeyCertPair{
66 | KeyFile: "PATH_TO_YOUR_KEY_FILE",
67 | CertFile: "PATH_TO_YOUR_CERT_FILE",
68 | }
69 |
70 | mqttSender := transforms.NewMQTTSender(edgexSdk.LoggingClient, addressable, &pair, mqttConfig, false)
71 |
72 | // 3) This is our pipeline configuration, the collection of functions to
73 | // execute every time an event is triggered.
74 | edgexSdk.SetFunctionsPipeline(
75 | transforms.NewFilter(deviceNames).FilterByDeviceName,
76 | transforms.NewConversion().TransformToXML,
77 | printXMLToConsole,
78 | mqttSender.MQTTSend,
79 | )
80 |
81 | // 4) shows how to access the application's specific configuration settings.
82 | appSettings := edgexSdk.ApplicationSettings()
83 | if appSettings != nil {
84 | appName, ok := appSettings["ApplicationName"]
85 | if ok {
86 | edgexSdk.LoggingClient.Info(fmt.Sprintf("%s now running...", appName))
87 | } else {
88 | edgexSdk.LoggingClient.Error("ApplicationName application setting not found")
89 | os.Exit(-1)
90 | }
91 | } else {
92 | edgexSdk.LoggingClient.Error("No application settings found")
93 | os.Exit(-1)
94 | }
95 |
96 | // 5) Lastly, we'll go ahead and tell the SDK to "start" and begin listening for events
97 | // to trigger the pipeline.
98 | err := edgexSdk.MakeItRun()
99 | if err != nil {
100 | edgexSdk.LoggingClient.Error("MakeItRun returned error: ", err.Error())
101 | os.Exit(-1)
102 | }
103 |
104 | // Do any required cleanup here
105 |
106 | os.Exit(0)
107 | }
108 |
109 | func printXMLToConsole(edgexcontext *appcontext.Context, params ...interface{}) (bool, interface{}) {
110 | if len(params) < 1 {
111 | // We didn't receive a result
112 | return false, nil
113 | }
114 |
115 | // Leverage the built in logging service in EdgeX
116 | edgexcontext.LoggingClient.Debug(params[0].(string))
117 | edgexcontext.Complete(([]byte)(params[0].(string)))
118 | return true, params[0].(string)
119 | }
120 |
--------------------------------------------------------------------------------
/app-services/simple-filter-xml/EdgeX Application Function SDK Device Name.postman_collection.json:
--------------------------------------------------------------------------------
1 | {
2 | "info": {
3 | "_postman_id": "ea5bca34-040c-406e-885f-49842aea97a1",
4 | "name": "EdgeX Application Function SDK Device Name",
5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
6 | },
7 | "item": [
8 | {
9 | "name": "CoreData Event Trigger (Random-Float-Device)",
10 | "event": [
11 | {
12 | "listen": "test",
13 | "script": {
14 | "id": "6c139344-5a75-4dec-ae3c-04d94454a6c2",
15 | "exec": [
16 | "pm.test(\"Status code is 200\", function () {",
17 | " pm.response.to.have.status(200);",
18 | "});",
19 | "",
20 | "tests[\"Response time is less than 200ms\"] = responseTime < 200;",
21 | "",
22 | "tests[\"Body contains\"] = responseBody.has(\"Random-Float-Device\");",
23 | "",
24 | ""
25 | ],
26 | "type": "text/javascript"
27 | }
28 | }
29 | ],
30 | "request": {
31 | "method": "POST",
32 | "header": [],
33 | "body": {
34 | "mode": "raw",
35 | "raw": "{\n \"id\": \"5bd794de0e36080001f53eab\",\n \"pushed\": 0,\n \"device\": \"Random-Float-Device\",\n \"created\": 1540855006481,\n \"modified\": 0,\n \"origin\": 1540855006469,\n \"schedule\": null,\n \"event\": null,\n \"readings\": [\n {\n \"id\": \"5bd794de0e36080001f53eac\",\n \"pushed\": 0,\n \"created\": 1540855006481,\n \"origin\": 1540855006469,\n \"modified\": 0,\n \"device\": \"Random-Float-Device\",\n \"name\": \"RandomValue_Float32\",\n \"value\": \"P63Kqg==\"\n }\n ]\n}"
36 | },
37 | "url": {
38 | "raw": "127.0.0.1:9090",
39 | "host": [
40 | "127",
41 | "0",
42 | "0",
43 | "1"
44 | ],
45 | "port": "9090"
46 | },
47 | "description": "This request will send a valid EdgeX event to Applications Function SDK via HTTP in order to trigger a configure pipeline. The default is :9090."
48 | },
49 | "response": []
50 | },
51 | {
52 | "name": "CoreData Event Trigger (Random-Integer-Generator01)",
53 | "event": [
54 | {
55 | "listen": "test",
56 | "script": {
57 | "id": "6c139344-5a75-4dec-ae3c-04d94454a6c2",
58 | "exec": [
59 | "pm.test(\"Status code is 200\", function () {",
60 | " pm.response.to.have.status(200);",
61 | "}); ",
62 | "",
63 | "pm.test(\"Response time is less than 200ms\", function () {",
64 | " pm.expect(pm.response.responseTime).to.be.below(200);",
65 | "});",
66 | "",
67 | "",
68 | "pm.test(\"No data in responseBody\", function () {",
69 | " pm.expect(responseBody.length).to.be.equal(0);",
70 | "});"
71 | ],
72 | "type": "text/javascript"
73 | }
74 | }
75 | ],
76 | "request": {
77 | "method": "POST",
78 | "header": [],
79 | "body": {
80 | "mode": "raw",
81 | "raw": "{\n \"id\": \"5bd794de0e36080001f53eab\",\n \"pushed\": 0,\n \"device\": \"Random-Integer-Generator0\",\n \"created\": 1540855006481,\n \"modified\": 0,\n \"origin\": 1540855006469,\n \"schedule\": null,\n \"event\": null,\n \"readings\": [\n {\n \"id\": \"5bd794de0e36080001f53eac\",\n \"pushed\": 0,\n \"created\": 1540855006481,\n \"origin\": 1540855006469,\n \"modified\": 0,\n \"device\": \"Random-Integer-Generator0\",\n \"name\": \"RandomValue_Int32\",\n \"value\": \"-1406268274\"\n }\n ]\n}"
82 | },
83 | "url": {
84 | "raw": "127.0.0.1:9090",
85 | "host": [
86 | "127",
87 | "0",
88 | "0",
89 | "1"
90 | ],
91 | "port": "9090"
92 | },
93 | "description": "This request will send a valid EdgeX event to Applications Function SDK via HTTP in order to trigger a configure pipeline. The default is :9090."
94 | },
95 | "response": []
96 | },
97 | {
98 | "name": "CoreData Event Trigger (empty string)",
99 | "event": [
100 | {
101 | "listen": "test",
102 | "script": {
103 | "id": "6c139344-5a75-4dec-ae3c-04d94454a6c2",
104 | "exec": [
105 | "pm.test(\"Status code is 200\", function () {",
106 | " pm.response.to.have.status(200);",
107 | "}); ",
108 | "",
109 | "pm.test(\"Response time is less than 200ms\", function () {",
110 | " pm.expect(pm.response.responseTime).to.be.below(200);",
111 | "});",
112 | "",
113 | "",
114 | "pm.test(\"No data in responseBody\", function () {",
115 | " pm.expect(responseBody.length).to.be.equal(0);",
116 | "});"
117 | ],
118 | "type": "text/javascript"
119 | }
120 | }
121 | ],
122 | "request": {
123 | "method": "POST",
124 | "header": [],
125 | "body": {
126 | "mode": "raw",
127 | "raw": "{\n \"id\": \"5bd794de0e36080001f53eab\",\n \"pushed\": 0,\n \"device\": \"\",\n \"created\": 1540855006481,\n \"modified\": 0,\n \"origin\": 1540855006469,\n \"schedule\": null,\n \"event\": null,\n \"readings\": [\n {\n \"id\": \"5bd794de0e36080001f53eac\",\n \"pushed\": 0,\n \"created\": 1540855006481,\n \"origin\": 1540855006469,\n \"modified\": 0,\n \"device\": \"Random-Float-Device\",\n \"name\": \"RandomValue_Float64\",\n \"value\": \"QAFk2HxRUOo=\\\"\n }\n ]\n}"
128 | },
129 | "url": {
130 | "raw": "127.0.0.1:9090",
131 | "host": [
132 | "127",
133 | "0",
134 | "0",
135 | "1"
136 | ],
137 | "port": "9090"
138 | },
139 | "description": "This request will send a valid EdgeX event to Applications Function SDK via HTTP in order to trigger a configure pipeline. The default is :9090."
140 | },
141 | "response": []
142 | }
143 | ]
144 | }
--------------------------------------------------------------------------------
/app-services/advanced-filter-convert-publish/README.md:
--------------------------------------------------------------------------------
1 | # Example Advanced App Functions Service
2 |
3 | This **advanced-filter-convert-publish** Application Service depends on the new Device Virtual Go device service to be generating random number events. It uses the following functions in its pipeline:
4 |
5 | - Built in **Filter by Value Descriptor** function to filter just for the random **float32** & **float64** values.
6 | - Custom **Value Converter** function which converts the encoded float values to human readable string values.
7 | - Custom **Print to Console** function which simply prints the human readable strings to the console.
8 | - Custom **Publish** function which prepares the modified event with converted values and outputs it to be published back to the message bus using the configured publish host/topic.
9 |
10 | The end result from this application service is random float values in human readable format are published back to the message bus for another App Service to consume.
11 |
12 | #### End to end Edgex integration proof point for App Functions
13 |
14 | Using the following setup, this example advanced **App Functions Service** can be used to demonstrate an EdgeX end to end proof point with **App Functions**.
15 |
16 | 1. Start **EdgeX Mongo**
17 |
18 | - [ ] clone **[developer-scripts](https://github.com/edgexfoundry/developer-scripts)** repo
19 | - [ ] cd **compose-files** folder
20 | - [ ] run "**docker-compose up mongo**"
21 | - This uses the default compose file to start the EdgeX Mongo service which exposes it's port to apps running on localhost
22 |
23 | 2. Run **EdgeX Core Services**
24 |
25 | - [ ] Clone **[edgex-go](https://github.com/edgexfoundry/edgex-go)** repo
26 | - [ ] run "**make build**"
27 | - [ ] run "**make run**"
28 | - This starts all the required Edgex core, export and support services
29 |
30 | 3. Run **Advanced App Functions** example
31 |
32 | - [ ] Clone **[app-service-examples](https://github.com/edgexfoundry-holding/app-service-examples)** repo
33 | - [ ] run "**make build**"
34 | - [ ] cd to **advanced-filter-convert-publish** folder
35 | - [ ] run "./**advanced-filter-convert-publish**"
36 |
37 | 4. Configure and Run **Simple Filter XML** App Functions example
38 |
39 | - [ ] cd **simple-filter-xml** folder
40 |
41 | - [ ] edit **res/configuration.toml** so the **MessageBus** and **Binding** sections are as follows:
42 |
43 | ```toml
44 | [MessageBus]
45 | Type = 'zero'
46 | [MessageBus.PublishHost]
47 | Host = '*'
48 | Port = 5565
49 | Protocol = 'tcp'
50 | [MessageBus.SubscribeHost]
51 | Host = 'localhost'
52 | Port = 5564
53 | Protocol = 'tcp'
54 |
55 | [Binding]
56 | Type="messagebus"
57 | SubscribeTopic="converted"
58 | PublishTopic="xml"
59 | ```
60 |
61 | - [ ] Run "**./simple-filter-xml**"
62 |
63 | 5. Run **Device Virtual** service
64 |
65 | - [ ] Clone **** repo
66 |
67 | - [ ] run "**make build**"
68 |
69 | - [ ] cd to **cmd**/res folder
70 |
71 | - [ ] Edit the `device.virtual.float.yaml` file
72 |
73 | This app functions example expects the float encoding for all random floats to be `Base64` and needs to restrict the range of values that are generated so they are easy to read. By default the device-virtual is using `Base64` for Float32 & `eNotation` for Float64 and doesn't set any range limits. Make the following changes to the `deviceResources` section to meet these needs.
74 |
75 | For `RandomValue_Float32` change the `value` property to:
76 |
77 | ```
78 | { type: "Float32", readWrite: "R", defaultValue: "0", floatEncoding: "Base64", minimum: "1.0", maximum: "1.9" }
79 | ```
80 |
81 | For `RandomValue_Float64` change the `value` property to:
82 |
83 | ```
84 | { type: "Float64", readWrite: "R", defaultValue: "0", floatEncoding: "Base64", minimum: "2.0", maximum: "2.9" }
85 | ```
86 |
87 | - [ ] If you previously ran the Device Virtual service, run the follow `curl` commands to clear the old profile so that the new changes are used when the device service is started.
88 |
89 | ```
90 | curl -X DELETE http://localhost:48081/api/v1/deviceservice/name/device-virtual
91 | curl -X DELETE http://localhost:48081/api/v1/deviceprofile/name/Random-Float-Device
92 | ```
93 |
94 | - [ ] cd to **cmd** folder
95 |
96 | - [ ] run "./**device-virtual**"
97 |
98 | first time this is run, the output will have these messages :
99 | ```text
100 | level=INFO ts=2019-04-17T22:42:08.238390389Z app=device-virtual source=service.go:138 msg="**Device Service doesn't exist, creating a new one**"
101 | level=INFO ts=2019-04-17T22:42:08.277064025Z app=device-virtual source=service.go:196 msg="**Addressable device-virtual doesn't exist, creating a new one**"
102 | ```
103 |
104 | One subsequent runs you will see this message:
105 |
106 | ```text
107 | level=INFO ts=2019-04-18T17:37:18.304805374Z app=device-virtual source=service.go:145 msg="Device Service device-virtual exists"
108 | ```
109 |
110 | 6. Now data will be flowing due to the auto-events configured in Device Virtual Go.
111 |
112 | - In the terminal that you ran **advanced-filter-convert-publish** you will see the random float values printed.
113 |
114 | ```text
115 | RandomValue_Float64 readable value from Random-Float-Device is '2.1742
116 | RandomValue_Float32 readable value from Random-Float-Device is '1.3577
117 | ```
118 |
119 | - In the terminal that you ran **simple-filter-xml** you will see the xml representation of the events printed. Note the human readable float values in the event XML.
120 | ```xml
121 | 0Random-Float-Device001555609767442835c5541-d4d2-42a8-8937-8b24b4308d3f0015556097674110Random-Float-DeviceRandomValue_Float642.1742
122 | 0Random-Float-Device00155560979745221c8ccdc-3438-4baa-8fab-23a63bf4fa180015556097974190Random-Float-DeviceRandomValue_Float321.3577
123 | ```
124 |
--------------------------------------------------------------------------------
/app-services/http-command-service/README.md:
--------------------------------------------------------------------------------
1 | # HTTP Command Service #
2 |
3 | #### Overview ####
4 |
5 | Many IoT deployments require some form of integration with the cloud. The integration will be required for both north- and south bound integration. For south-bound services, after analytic processing from the cloud, some device actions are notmally triggered as a result of gathered intelligence. This document demonstrates a sample EdgeX application service–HTTP Command Service that can receive commands from the Azure IoT Hub, consume the device data and invoke commands against devices. The entire technical architecture is illustrated below:
6 |
7 | 
8 |
9 | We sometimes want to trigger device actions from the cloud; that is, from the Azure IoT Hub to EdgeX. The EdgeX Core Command Service provides a comprehensive set of APIs to achieve this. However, in some cases, you might prefer not to expose all APIs and therefore require a finer-grained control over the APIs to be exposed. For example, you might want to control the commands on specific devices that can receive commands from outside of EdgeX, or to allow only certain values for a specific command. HTTP Command Service provides a sample implementations to achieve such control.
10 |
11 | #### Prerequisites ####
12 |
13 | * Obtain the code from the https://github.com/edgexfoundry-holding/app-service-examples repo
14 | * Ensure that EdgeX is running with mandatory services, including core services and logging service
15 | * Ensure that the Virtual Device Service is running and managed by EdgeX with at least one pre-defined device, such as Random-Boolean-Device
16 |
17 | If you are unfamiliar with the Azure IoT Hub, read the following documents first, as this document intentionally omits some details on Azure:
18 | * [Create an Azure IoT Hub on the Azure portal](https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-create-through-portal)
19 | * [Set up X.509 security on Azure IoT Hub](https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-security-x509-get-started)
20 |
21 | #### Steps ####
22 |
23 | 1. The HTTP Command Service exposes an HTTP service for the client to switch on / off of the device without knowing the underlying EdgeX APIs. For the service to send commands to correct device, you must set DeviceID and CommandID in the [res/configuration.toml](./res/configuration.toml) file.
24 | ```
25 | [ApplicationSettings]
26 | DeviceID = "9f178953-84e7-49f6-9829-5b86b7cbbcda"
27 | CommandID = "15786a22-d89b-474b-a7de-18371c3d22c5"
28 | ```
29 | **Note:** For real use cases, the DeviceID must be associated with the actual device that is managed by EdgeX; the CommandID must be associated with the required command registered under the EdgeX Core Command Service, and the command must provide correct responses to consume the [switch on / off JSON document](./status-on-request.json). For simplicity, we can use the Virtual Device Service in this sample.
30 |
31 | 2. Build the HTTP command service through steps as described [here](https://github.com/edgexfoundry-holding/app-service-examples#building-examples).
32 | 3. Run the http-command service through steps as described [here](https://github.com/edgexfoundry-holding/app-service-examples#running-an-example), and you have an HTTP service that can switch on/off your device by sending a [JSON payload](./status-on-request.json) to the http://127.0.0.1:48095/api/v1/trigger endpoint.
33 | 4. With the HTTP Command Service ready, your Azure IoT Hub can use a direct method to control the device. Azure provides an IoT device SDK in various programming languages. Refer to [Quickstart: Control a device connected to an Azure IoT hub with Java](https://docs.microsoft.com/en-us/azure/iot-hub/quickstart-control-device-java) for more details of using the direct method. To simplify the implementation, we supply modified code, which is ready to run with the Azure IoT hub:
34 | * Under [iot-hub](./iot-hub) directory, there are two sample modules:
35 | * [Quickstarts/proxy-device](./iot-hub/Quickstarts/proxy-device)
36 | This simulates an Azure device that is associated with the proxy device as defined in step 1, and this proxy device could be used to handle remote methods.
37 | * [Quickstarts/back-end-application](./iot-hub/Quickstarts/back-end-application)
38 | This is a simulated Azure back-end application that issues remote method requests.
39 | * Follow the steps as described [here](https://docs.microsoft.com/en-us/azure/iot-hub/quickstart-control-device-java#register-a-device) to register a proxy device on your Azure IoT hub
40 | * Update the **connection string** and **device Id** as obtained from previous step for both following Java classes:
41 | * [Quickstarts/proxy-device/src/main/java/com/microsoft/docs/iothub/samples/ProxyDevice.java](./iot-hub/Quickstarts/proxy-device/src/main/java/com/microsoft/docs/iothub/samples/ProxyDevice.java)
42 | ```java
43 | public class ProxyDevice {
44 | // The device connection string to authenticate the device with your IoT hub.
45 | // Using the Azure CLI:
46 | // az iot hub device-identity show-connection-string --hub-name {YourIoTHubName} --device-id {YourDeviceId} --output table
47 | private static String connString = "{Your device connection string here}";
48 | ```
49 | * [Quickstarts/back-end-application/src/main/java/com/microsoft/docs/iothub/samples/BackEndApplication.java](./iot-hub/Quickstarts/back-end-application/src/main/java/com/microsoft/docs/iothub/samples/BackEndApplication.java)
50 | ```java
51 | public class BackEndApplication {
52 |
53 | // Connection string for your IoT Hub
54 | // az iot hub show-connection-string --hub-name {your iot hub name} --policy-name service
55 | public static final String iotHubConnectionString = "{Your service connection string here}";
56 |
57 | // Device to call direct method on.
58 | public static final String deviceId = "{Your device Id here}";
59 | ```
60 | * Run ``mvn clean package`` in both 2 modules
61 | * Use following scripts to run the applications:
62 | * [Quickstarts/proxy-device/run.sh](./iot-hub/Quickstarts/proxy-device/run.sh)
63 | This initiates the proxy device that is defined on the Azure IoT Hub. This device acts as a proxy to invoke the HTTP Command Service using the http://127.0.0.1:48095/api/v1/trigger endpoint
64 | * [Quickstarts/back-end-application/on.sh](./iot-hub/Quickstarts/back-end-application/on.sh)
65 | This simulates the switch-on request from the Azure IoT Hub. A successful request makes a device method invocation to switch on the device.
66 | * [Quickstarts/back-end-application/off.sh](./iot-hub/Quickstarts/back-end-application/off.sh)
67 | This simulates the switch-off request from the Azure IoT Hub. A successful request makes a device method invocation to switch off the device.
--------------------------------------------------------------------------------
/app-services/http-command-service/iot-hub/Quickstarts/proxy-device/src/main/java/com/microsoft/docs/iothub/samples/ProxyDevice.java:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft. All rights reserved.
2 | // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 |
4 | // This application uses the Azure IoT Hub device SDK for Java
5 | // For samples see: https://github.com/Azure/azure-iot-sdk-java/tree/master/device/iot-device-samples
6 |
7 | package com.microsoft.docs.iothub.samples;
8 |
9 | import com.microsoft.azure.sdk.iot.device.*;
10 | import com.microsoft.azure.sdk.iot.device.DeviceTwin.*;
11 | import com.google.gson.Gson;
12 |
13 | import java.io.*;
14 | import java.net.URISyntaxException;
15 | import java.util.Random;
16 | import java.util.concurrent.Executors;
17 | import java.util.concurrent.ExecutorService;
18 |
19 | import java.lang.reflect.Type;
20 | import java.util.List;
21 | import java.util.Map;
22 | import java.util.HashMap;
23 | import com.google.api.client.http.GenericUrl;
24 | import com.google.api.client.http.HttpRequest;
25 | import com.google.api.client.http.HttpRequestFactory;
26 | import com.google.api.client.http.HttpTransport;
27 | import com.google.api.client.http.HttpContent;
28 | import com.google.api.client.http.HttpMediaType;
29 | import com.google.api.client.http.json.JsonHttpContent;
30 | import com.google.api.client.http.javanet.NetHttpTransport;
31 | import com.google.api.client.json.JsonFactory;
32 | import com.google.api.client.json.JsonObjectParser;
33 | import com.google.api.client.json.gson.GsonFactory;
34 | import com.google.gson.reflect.TypeToken;
35 |
36 | public class ProxyDevice {
37 | // The device connection string to authenticate the device with your IoT hub.
38 | // Using the Azure CLI:
39 | // az iot hub device-identity show-connection-string --hub-name {YourIoTHubName} --device-id {YourDeviceId} --output table
40 | private static String connString = "{Your device connection string here}";
41 |
42 | // Using the MQTT protocol to connect to IoT Hub
43 | private static IotHubClientProtocol protocol = IotHubClientProtocol.MQTT;
44 | private static DeviceClient client;
45 |
46 | private static final String TURN_ON = "turn-on";
47 | private static final String TURN_OFF = "turn-off";
48 |
49 | // Define method response codes
50 | private static final int METHOD_SUCCESS = 200;
51 | private static final int METHOD_NOT_DEFINED = 404;
52 | private static final int INVALID_PARAMETER = 400;
53 |
54 | private static int telemetryInterval = 1000;
55 |
56 | // For HTTP client
57 | static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
58 | static final JsonFactory JSON_FACTORY = new GsonFactory();
59 | //
60 |
61 | // Specify the telemetry to send to your IoT hub.
62 | private static class TelemetryDataPoint {
63 | public double temperature;
64 | public double humidity;
65 |
66 | // Serialize object to JSON format.
67 | public String serialize() {
68 | Gson gson = new Gson();
69 | return gson.toJson(this);
70 | }
71 | }
72 |
73 | // Print the acknowledgement received from IoT Hub for the method acknowledgement sent.
74 | protected static class DirectMethodStatusCallback implements IotHubEventCallback
75 | {
76 | public void execute(IotHubStatusCode status, Object context)
77 | {
78 | System.out.println("Direct method # IoT Hub responded to device method acknowledgement with status: " + status.name());
79 | }
80 | }
81 |
82 | // Print the acknowledgement received from IoT Hub for the telemetry message sent.
83 | private static class EventCallback implements IotHubEventCallback {
84 | public void execute(IotHubStatusCode status, Object context) {
85 | System.out.println("IoT Hub responded to message with status: " + status.name());
86 |
87 | if (context != null) {
88 | synchronized (context) {
89 | context.notify();
90 | }
91 | }
92 | }
93 | }
94 |
95 | protected static class DirectMethodCallback implements DeviceMethodCallback
96 | {
97 | private void setTelemetryInterval(int interval)
98 | {
99 | System.out.println("Direct method # Setting telemetry interval (seconds): " + interval);
100 | telemetryInterval = interval * 1000;
101 | }
102 |
103 | private void switchStatus(boolean s) {
104 | try {
105 | HttpRequestFactory requestFactory = HTTP_TRANSPORT.createRequestFactory((HttpRequest request) -> {
106 | request.setParser(new JsonObjectParser(JSON_FACTORY));
107 | });
108 |
109 | GenericUrl url = new GenericUrl("http://127.0.0.1:48095/api/v1/trigger");
110 | Map data = new HashMap<>();
111 | data.put("status", s ? "on" : "off");
112 | HttpContent content = new JsonHttpContent(JSON_FACTORY, data).setMediaType(new HttpMediaType("application/json"));
113 | HttpRequest request = requestFactory.buildPostRequest(url, content);
114 |
115 | Switch sw = (Switch) request.execute().parseAs(Switch.class);
116 |
117 | System.out.println(sw);
118 | } catch (IOException ioe) {
119 | ioe.printStackTrace();
120 | }
121 | }
122 |
123 | @Override
124 | public DeviceMethodData call(String methodName, Object methodData, Object context)
125 | {
126 | DeviceMethodData deviceMethodData;
127 | String payload = new String((byte[])methodData);
128 | switch (methodName)
129 | {
130 | case "SetTelemetryInterval" :
131 | {
132 | int interval;
133 | try {
134 | int status = METHOD_SUCCESS;
135 | interval = Integer.parseInt(payload);
136 | System.out.println(payload);
137 | setTelemetryInterval(interval);
138 | deviceMethodData = new DeviceMethodData(status, "Executed direct method " + methodName);
139 | } catch (NumberFormatException e) {
140 | int status = INVALID_PARAMETER;
141 | deviceMethodData = new DeviceMethodData(status, "Invalid parameter " + payload);
142 | }
143 | break;
144 | }
145 | case TURN_ON:
146 | {
147 | int status = METHOD_SUCCESS;
148 | //
149 | switchStatus(true);
150 | //
151 | deviceMethodData = new DeviceMethodData(status, "Executed direct method " + methodName);
152 | break;
153 | }
154 | case TURN_OFF:
155 | {
156 | int status = METHOD_SUCCESS;
157 | //
158 | switchStatus(false);
159 | //
160 | deviceMethodData = new DeviceMethodData(status, "Executed direct method " + methodName);
161 | break;
162 | }
163 | default:
164 | {
165 | int status = METHOD_NOT_DEFINED;
166 | deviceMethodData = new DeviceMethodData(status, "Non-defined direct method " + methodName);
167 | }
168 | }
169 |
170 | System.out.println("Executed direct method " + methodName);
171 |
172 | return deviceMethodData;
173 | }
174 | }
175 |
176 | private static class MessageSender implements Runnable {
177 | public void run() {
178 | try {
179 | // Initialize the simulated telemetry.
180 | double minTemperature = 20;
181 | double minHumidity = 60;
182 | Random rand = new Random();
183 |
184 | while (true) {
185 | Thread.sleep(telemetryInterval);
186 | }
187 | } catch (InterruptedException e) {
188 | System.out.println("Finished.");
189 | }
190 | }
191 | }
192 |
193 | public static void main(String[] args) throws IOException, URISyntaxException {
194 | // Connect to the IoT hub.
195 | client = new DeviceClient(connString, protocol);
196 | client.open();
197 |
198 | // Register to receive direct method calls.
199 | client.subscribeToDeviceMethod(new DirectMethodCallback(), null, new DirectMethodStatusCallback(), null);
200 |
201 | // Create new thread and start sending messages
202 | MessageSender sender = new MessageSender();
203 | ExecutorService executor = Executors.newFixedThreadPool(1);
204 | executor.execute(sender);
205 |
206 | // Stop the application.
207 | System.out.println("Press ENTER to exit.");
208 | System.in.read();
209 | executor.shutdownNow();
210 | client.closeNow();
211 | }
212 | }
213 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | https://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
--------------------------------------------------------------------------------