GET/PUT requests to the core-command layer get refused

1. Background

Hi,

I am trying to use the command layer to see how it works.

Eventually I got ERROR messages from the layer’s log file like this:

I do think that this is because of my provision process and the value of the provision request so that I hope to have some help to confirm if my setup has the right value.

2. System up and running

I used below commands to have a clean containers.

docker-compose down -v
docker volume prune -f
docker-compose up -d

(Please note that my compose file doesn’t include the virtual device layer.)

3. Provisioning process

Below blocks are the provision requests that I made and its response from each layer (meta and data).

(The YAML and TOML files are attached to this posting)

printf "\n> Creating Device Profile\n"
curl http://localhost:48081/api/v1/deviceprofile/uploadfile -X POST -s -S -F "file=@./Simple-Driver.yaml"

5cfaad4c9f8fc20001a792a8
printf "> Provisioning Addressibles\n"
curl http://localhost:48081/api/v1/addressable -X POST -s -S -d @- <<EOF
{
    "name":"Simple-Device",
    "protocol":"HTTP",
    "address":"localhost",
    "port":49990,
    "path":"/Simple-Device",
    "publisher":"none",
    "user":"none",
    "password":"none",
    "topic":"none"
}
EOF

5cfaad4c9f8fc20001a792a9
###
printf "\n> Creating Device Service\n"
curl http://localhost:48081/api/v1/deviceservice -X POST -s -S -d @- <<EOF
{
    "name":"switch control device service",
    "description":"Control the switch",
    "labels":["switch"],
    "adminState":"unlocked",
    "operatingState":"enabled",
    "addressable": {
        "name":"Simple-Device"
    }
}
EOF

5cfaad4c9f8fc20001a792aa
curl http://localhost:48080/api/v1/valuedescriptor -X POST -s -S -d @- <<EOF
{
    "name":"Simple-Device",
    "description":"switch status",
    "min":"0","max":"1",
    "type":"B",
    "uomLabel":"switch",
    "defaultValue":"0",
    "formatting":"%b",
    "labels":["switch"]
}
EOF

5cfaad4ce9be3e0e174f4d7c
printf "\n> Creating Device\n"
curl http://localhost:48081/api/v1/device -X POST -s -S -d @- <<EOF
{
    "name":"Simple-Device",
    "description":"switch control",
    "adminState":"unlocked",
    "operatingState":"enabled",
    "addressable":{
        "name":"Simple-Device"
    },
    "labels":[
        "switch"
    ],
    "location":"",
    "service":{
        "name":"switch control device service"
    },
    "profile":{
        "name":"Simple-Device"
    }
}
EOF

5cfaad4c9f8fc20001a792ab

Since I got the IDs from each request, I assume that the layers accepted the request without issue.

4. Device Service layer up and running

The layer is in the native environment and being launched from the IDE.
From its debug console I got this lines:

DEBUG: 2019/06/07 11:31:00 Handler - execReadCmd: deviceObject: SwitchButton
DEBUG: 2019/06/07 11:31:00 Handler - execReadCmd: deviceObject: {"description":"Switch On/Off.","name":"SwitchButton","tag":null,"properties":{"value":{"type":"Bool","readWrite":"RW","minimum":null,"maximum":null,"defaultValue":null,"size":null,"word":"2","lsb":null,"mask":"0x00","shift":"0","scale":"1.0","offset":"0.0","base":"0","assertion":null,"signed":true,"precision":null},"units":{"type":"String","readWrite":"R","defaultValue":"On/Off"}},"attributes":null}

Therefore, I assume that the registration is done properly as well.

5. Checking the APIs

I wanted to see if the DS, meta, and data layers work properly.
Each request gets the following logs.

printf "> GET Provisioning Addressibles\n"
curl http://localhost:48081/api/v1/addressable/name/Simple-Device -X GET -s -S 

{"created":1559932236256,"modified":0,"origin":0,"id":"5cfaad4c9f8fc20001a792a9","name":"Simple-Device","protocol":"HTTP","method":null,"address":"localhost","port":49990,"path":"/Simple-Device","publisher":"none","user":"none","password":"none","topic":"none","baseURL":"HTTP://localhost:49990","url":"HTTP://localhost:49990/Simple-Device"}
printf "\n> GET Provisioning Value Descriptors\n"
curl http://localhost:48080/api/v1/valuedescriptor/name/Simple-Device -X GET -s -S 

{"id":"5cfaad4ce9be3e0e174f4d7c","created":1559932236295,"description":"switch status","modified":0,"origin":0,"name":"Simple-Device","min":"0","max":"1","defaultValue":"0","type":"B","uomLabel":"switch","formatting":"%b","labels":["switch"]}
printf "\n> GET Device Profile Created\n"
curl http://localhost:48081/api/v1/deviceprofile/name/Simple-Device -X GET -s -S

{"created":1559932236236,"modified":1559932236236,"origin":0,"description":"Example of Simple Device","id":"5cfaad4c9f8fc20001a792a8","name":"Simple-Device","manufacturer":"Simple Corp.","model":"SP-01","labels":["modbus"],"objects":null,"deviceResources":[{"description":"Switch On/Off.","name":"SwitchButton","tag":null,"properties":{"value":{"type":"Bool","readWrite":"RW","minimum":null,"maximum":null,"defaultValue":null,"size":null,"word":"2","lsb":null,"mask":"0x00","shift":"0","scale":"1.0","offset":"0.0","base":"0","assertion":null,"signed":true,"precision":null},"units":{"type":"String","readWrite":"R","defaultValue":"On/Off"}},"attributes":null}],"resources":[{"name":"Switch","get":[{"index":null,"operation":"get","object":"SwitchButton","property":"value","parameter":"Switch","resource":null,"secondary":[],"mappings":{}}],"set":[{"index":null,"operation":"set","object":"SwitchButton","property":"value","parameter":"Switch","resource":null,"secondary":[],"mappings":{}}]}],"commands":[{"created":1559932236223,"modified":0,"origin":0,"id":"5cfaad4c9f8fc20001a792a7","name":"Switch","get":{"path":"/api/v1/device/{deviceId}/Switch","responses":[{"code":"200","description":null,"expectedValues":["Switch"]},{"code":"503","description":"service unavailable","expectedValues":[]}]},"put":{"path":"/api/v1/device/{deviceId}/Switch","responses":[{"code":"200","description":null,"expectedValues":[]},{"code":"503","description":"service unavailable","expectedValues":[]}],"parameterNames":["Switch"]}}]}
printf "\n> GET Device Service Created\n"
curl http://localhost:48081/api/v1/deviceservice/addressablename/Simple-Device -X GET -s -S 

[{"created":1559932236273,"modified":1559932236273,"origin":0,"description":"Control the switch","id":"5cfaad4c9f8fc20001a792aa","name":"switch control device service","lastConnected":0,"lastReported":0,"operatingState":"ENABLED","labels":["switch"],"addressable":{"created":1559932236256,"modified":0,"origin":0,"id":"5cfaad4c9f8fc20001a792a9","name":"Simple-Device","protocol":"HTTP","method":null,"address":"localhost","port":49990,"path":"/Simple-Device","publisher":"none","user":"none","password":"none","topic":"none","baseURL":"HTTP://localhost:49990","url":"HTTP://localhost:49990/Simple-Device"},"adminState":"UNLOCKED"}]
printf "\n> GET Device Created from the meta data\n"
curl http://localhost:48081/api/v1/device/name/Simple-Device -X GET -s -S

{"created":1559932236332,"modified":1559932236332,"origin":0,"description":"switch control","id":"5cfaad4c9f8fc20001a792ab","name":"Simple-Device","adminState":"UNLOCKED","operatingState":"ENABLED","addressable":{"created":1559932236256,"modified":0,"origin":0,"id":"5cfaad4c9f8fc20001a792a9","name":"Simple-Device","protocol":"HTTP","method":null,"address":"localhost","port":49990,"path":"/Simple-Device","publisher":"none","user":"none","password":"none","topic":"none","baseURL":"HTTP://localhost:49990","url":"HTTP://localhost:49990/Simple-Device"},"lastConnected":0,"lastReported":0,"labels":["switch"],"location":"","service":{"created":1559932236273,"modified":1559932236273,"origin":0,"description":"Control the switch","id":"5cfaad4c9f8fc20001a792aa","name":"switch control device service","lastConnected":0,"lastReported":0,"operatingState":"ENABLED","labels":["switch"],"addressable":{"created":1559932236256,"modified":0,"origin":0,"id":"5cfaad4c9f8fc20001a792a9","name":"Simple-Device","protocol":"HTTP","method":null,"address":"localhost","port":49990,"path":"/Simple-Device","publisher":"none","user":"none","password":"none","topic":"none","baseURL":"HTTP://localhost:49990","url":"HTTP://localhost:49990/Simple-Device"},"adminState":"UNLOCKED"},"profile":{"created":1559932236236,"modified":1559932236236,"origin":0,"description":"Example of Simple Device","id":"5cfaad4c9f8fc20001a792a8","name":"Simple-Device","manufacturer":"Simple Corp.","model":"SP-01","labels":["modbus"],"objects":null,"deviceResources":[{"description":"Switch On/Off.","name":"SwitchButton","tag":null,"properties":{"value":{"type":"Bool","readWrite":"RW","minimum":null,"maximum":null,"defaultValue":null,"size":null,"word":"2","lsb":null,"mask":"0x00","shift":"0","scale":"1.0","offset":"0.0","base":"0","assertion":null,"signed":true,"precision":null},"units":{"type":"String","readWrite":"R","defaultValue":"On/Off"}},"attributes":null}],"resources":[{"name":"Switch","get":[{"index":null,"operation":"get","object":"SwitchButton","property":"value","parameter":"Switch","resource":null,"secondary":[],"mappings":{}}],"set":[{"index":null,"operation":"set","object":"SwitchButton","property":"value","parameter":"Switch","resource":null,"secondary":[],"mappings":{}}]}],"commands":[{"created":1559932236223,"modified":0,"origin":0,"id":"5cfaad4c9f8fc20001a792a7","name":"Switch","get":{"path":"/api/v1/device/{deviceId}/Switch","responses":[{"code":"200","description":null,"expectedValues":["Switch"]},{"code":"503","description":"service unavailable","expectedValues":[]}]},"put":{"path":"/api/v1/device/{deviceId}/Switch","responses":[{"code":"200","description":null,"expectedValues":[]},{"code":"503","description":"service unavailable","expectedValues":[]}],"parameterNames":["Switch"]}}]}}
printf "\n> GET events\n"
curl http://localhost:48080/api/v1/event -X GET -s -

{"id":"5cfab4a20e360800014bb5e9","pushed":0,"created":1559934114002,"origin":1559934114001,"modified":0,"device":"Simple-Device01","name":"Switch","value":"false"}
printf "\n> GET Reading Value\n"
curl http://localhost:48080/api/v1/reading -X GET -s -S

{"id":"5cfab4a20e360800014bb5e9","pushed":0,"created":1559934114002,"origin":1559934114001,"modified":0,"device":"Simple-Device01","name":"Switch","value":"false"}
printf "\n> GET first 5 readings of a target\n"
curl http://localhost:48080/api/v1/reading/device/Simple-Device01/5 -X GET -s -S

{"id":"5cfaae4e0e360800014bb5b3","pushed":0,"created":1559932494003,"origin":1559932494001,"modified":0,"device":"Simple-Device01","name":"Switch","value":"false"}
printf "\n> GET the value directly\n"
curl http://localhost:49990/api/v1/device/name/Simple-Device01/Switch -X GET -s -S

"id":"","pushed":0,"device":"Simple-Device01","created":0,"modified":0,"origin":1559934123844,"schedule":null,"event":null,"readings":[{"id":"","pushed":0,"created":0,"origin":1559934123844,"modified":0,"device":"Simple-Device01","name":"Switch","value":"false"}

To get some information (URL) for the command layer API, I requested this.

printf "\n> GET a command ID of the device from the meta data\n"
curl http://localhost:48082/api/v1/device/name/Simple-Device -X GET -s -S

{"id":"5cfaad4c9f8fc20001a792ab","name":"Simple-Device","adminState":"UNLOCKED","operatingState":"ENABLED","lastConnected":0,"lastReported":0,"labels":["switch"],"location":"","commands":[{"created":1559932236223,"modified":0,"origin":0,"id":"5cfaad4c9f8fc20001a792a7","name":"Switch","get":{"path":"/api/v1/device/{deviceId}/Switch","responses":[{"code":"200","description":null,"expectedValues":["Switch"]},{"code":"503","description":"service unavailable","expectedValues":[]}],"url":"http://edgex-core-command:48082/api/v1/device/5cfaad4c9f8fc20001a792ab/command/5cfaad4c9f8fc20001a792a7"},"put":{"path":"/api/v1/device/{deviceId}/Switch","responses":[{"code":"200","description":null,"expectedValues":[]},{"code":"503","description":"service unavailable","expectedValues":[]}],"parameterNames":["Switch"],"url":"http://edgex-core-command:48082/api/v1/device/5cfaad4c9f8fc20001a792ab/command/5cfaad4c9f8fc20001a792a7"}}]}

6. Requesting GET to the command layer

Finally I requested the GET to the URL I got from the last request.

curl GET localhost:48082/api/v1/device/5cfaad4c9f8fc20001a792ab/command/5cfaad4c9f8fc20001a792a7 -X GET -s -S

The request didn’t give me any response back but with the -v switch I could see the 503 error:

*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 48082 (#0)
> GET /api/v1/device/5cfaad4c9f8fc20001a792ab/command/5cfaad4c9f8fc20001a792a7 HTTP/1.1
> Host: localhost:48082
> User-Agent: curl/7.58.0
> Accept: */*
> 
< HTTP/1.1 502 Bad Gateway
< Date: Fri, 07 Jun 2019 19:11:34 GMT
< Content-Length: 0
< Content-Type: text/plain; charset=utf-8
< 
* Connection #0 to host localhost left intact

Also, the command layer showed me this log for the GET:

docker logs edgex-core-command

ERROR: 2019/06/07 19:11:34 Get http://localhost:49990/api/v1/device/5cfaad4c9f8fc20001a792ab/Switch: dial tcp 127.0.0.1:49990: getsockopt: connection refused

7. Call for help

Probably my provision doesn’t make sense for the layers so that please feel free to give me any advise!

Thanks,

YAML

name: "Simple-Device"
manufacturer: "Simple Corp."
model: "SP-01"
labels:
 - "modbus"
description: "Example of Simple Device"

deviceResources:
    -
        name: "SwitchButton"
        description: "Switch On/Off."
        properties:
            value:
                { type: "Bool", readWrite: "RW" }
            units:
                { type: "String", readWrite: "R", defaultValue: "On/Off" }

resources:
    -
        name: "Switch"
        get:
            - { operation: "get", object: "SwitchButton", property: "value", parameter: "Switch" }
        set:
            - { operation: "set", object: "SwitchButton", property: "value", parameter: "Switch" }

commands:
  -
    name: "Switch"
    get:
        path: "/api/v1/device/{deviceId}/Switch"
        responses:
          -
            code: "200"
            description: ""
            expectedValues: ["Switch"]
          -
            code: "503"
            description: "service unavailable"
            expectedValues: []
    put:
      path: "/api/v1/device/{deviceId}/Switch"
      parameterNames: ["Switch"]
      responses:
      -
        code: "200"
        description: ""
      -
        code: "503"
        description: "service unavailable"
        expectedValues: []

TOML

[Service]
Host = "localhost"
Port = 49990
ConnectRetries = 3
Labels = []
OpenMsg = "device simple started"
ReadMaxLimit = 256
Timeout = 5000
EnableAsyncReadings = true
AsyncBufferSize = 16

[Registry]
Host = "localhost"
Port = 8500
CheckInterval = "10s"
FailLimit = 3
FailWaitTime = 10

[Clients]
  [Clients.Data]
  Name = "edgex-core-data"
  Protocol = "http"
  Host = "localhost"
  Port = 48080
  Timeout = 5000

  [Clients.Metadata]
  Name = "edgex-core-metadata"
  Protocol = "http"
  Host = "localhost"
  Port = 48081
  Timeout = 5000

  [Clients.Logging]
  Name = "edgex-support-logging"
  Protocol = "http"
  Host = "localhost"
  Port = 48061

[Device]
  DataTransform = true
  InitCmd = ""
  InitCmdArgs = ""
  MaxCmdOps = 128
  MaxCmdValueLen = 256
  RemoveCmd = ""
  RemoveCmdArgs = ""
  ProfilesDir = "./res"

[Logging]
EnableRemote = false
File = "./device-simple.log"
Level = "DEBUG"

# Pre-define Devices

[[DeviceList]]
  Name = "Simple-Device01"
  Profile = "Simple-Device"
  Description = "Example of Simple Device"
  Labels = [ "industrial" ]
  [DeviceList.Addressable]
    Address = "simple01"
    Port = 300
    Protocol = "OTHER"

# Pre-define Schedule Configuration

# [[Schedules]]
# Name = "5sec-schedule"
# Frequency = "PT5S"

# [[ScheduleEvents]]
# Name = "readSwtich"
# Schedule = "5sec-schedule"
#   [ScheduleEvents.Addressable]
#   HTTPMethod = "GET"
#   Path = "/api/v1/device/name/Simple-Device01/Switch"

[[Schedules]]
Name = "60sec-schedule"
Frequency = "PT60S"

[[ScheduleEvents]]
Name = "readSwitch"
Schedule = "60sec-schedule"
  [ScheduleEvents.Addressable]
  HTTPMethod = "GET"
  Path = "/api/v1/device/name/Simple-Device01/Switch"

Hi, it looks like the problem is probably the Host = "localhost" line in the configuration. This setting is used when the device service registers itself, and core-command uses that information to find the device service. The trouble is that within the container that core-command is running, the name “localhost” doesn’t map back to the host system.
Depending on your network setup, using your local machine’s hostname (as returned by uname -n) instead of localhost may work, alternatively you can use the real IP address

1 Like

That makes sense!
I will try and keep posting the following circumstances shortly.

@iaina,

The key to inform core-command container about the IP of my device service layer was the configuration.toml file.
I changed all the host component in the file to be my own IP address.
Also, send a request as below to get the instance IDs of the device/switch (of my DS) as below.

printf "\n> GET a command ID of the device from the meta data\n"
curl http://localhost:48082/api/v1/device/name/Simple-Device01 -X GET -s -S

The result is like this (the IDs might be different time to time though…):

localhost:48082/api/v1/device/5cfe8c379f8fc200012321da/command/5cfe8c309f8fc200012321d2

Sending a GET to that address returned this:

{
    "id": "",
    "pushed": 0,
    "device": "Simple-Device01",
    "created": 0,
    "modified": 0,
    "origin": 1560185937266,
    "schedule": null,
    "event": null,
    "readings": [
        {
            "id": "",
            "pushed": 0,
            "created": 0,
            "origin": 1560185937266,
            "modified": 0,
            "device": "Simple-Device01",
            "name": "Switch",
            "value": "false"
        }
    ]
}

I didn’t try PUT request to the core-command yet but at least I could make sure that the layer knows how to interact with the DS.

Thanks!