Examples

cURL examples

The following examples uses curl which is available in nearly all Linux distributions. It also shows parts of the communication, e.g. the received HTTP headers in the response.

Query common XPL Rail device information

This example queries common device properties.

curl -v http://192.168.178.191/api/device
* Hostname was NOT found in DNS cache
*   Trying 192.168.178.191...
* Connected to 192.168.178.191 (192.168.178.191) port 80 (#0)
> GET /api/device HTTP/1.1
> User-Agent: curl/7.35.0
> Host: 192.168.178.191
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: lwIP/1.4.1
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS
< Access-Control-Allow-Headers: Content-Type
< Content-Length: 288
< Content-Type: application/json
< Pragma: no-cache
<
* Closing connection 0
{
    "product" : "I2XPLR4-IO600",
    "modelname" : "XPL Rail",
    "hardware_version" : "1.0",
    "software_version" : "0.12",
    "vcs_version" : "r1132",
    "hostname" : "j-m-io600-2",
    "mac_address" : "00:01:87:0A:00:15",
    "mac_address_plc" : "00:01:87:0A:00:14",
    "serial" : "0000000815",
    "uuid" : "444d5314-1000-4b00-9400-0001870a0015"
}

Check configuration status of a physical channel

This example queries the current configuration of the physical channel 1.

curl -v http://192.168.178.191/api/channel/physical/io/1
* Hostname was NOT found in DNS cache
*   Trying 192.168.178.191...
* Connected to 192.168.178.191 (192.168.178.191) port 80 (#0)
> GET /api/channel/physical/io/1 HTTP/1.1
> User-Agent: curl/7.35.0
> Host: 192.168.178.191
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: lwIP/1.4.1
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS
< Access-Control-Allow-Headers: Content-Type
< Content-Length: 73
< Content-Type: application/json
< Pragma: no-cache
<
* Closing connection 0
{
    "label" : "Channel 1",
    "supported_types" : ["di", "do", "ai", "s0"],
    "enabled" : 0
}

Configure physical channel 1 as Digitial Output

This example reconfigures the physical channel 1 as digital output.

curl -v -X PUT -H "Content-Type: application/json" -d '{"type":"do","mode":"normal"}' http://192.168.178.191/api/channel/physical/io/1
* Hostname was NOT found in DNS cache
*   Trying 192.168.178.191...
* Connected to 192.168.178.191 (192.168.178.191) port 80 (#0)
> PUT /api/channel/physical/io/1 HTTP/1.1
> User-Agent: curl/7.35.0
> Host: 192.168.178.191
> Accept: */*
> Content-Type: application/json
> Content-Length: 29
>
* upload completely sent off: 29 out of 29 bytes
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: lwIP/1.4.1
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS
< Access-Control-Allow-Headers: Content-Type
< Content-Length: 175
< Content-Type: application/json
< Pragma: no-cache
<
* Closing connection 0
{
    "label" : "Channel 1",
    "supported_types" : ["di", "do", "ai", "s0"],
    "type" : "do",
    "enabled" : 1,
    "mode" : "normal",
    "virtual_channel" : 0,
    "level" : "direct",
    "delay_on" : 0,
    "delay_off" : 0,
    "value" : 0
}

Switch channel 1 (Digitial Output) on

This example switches the physical digital channel on.

curl -v -X PUT -H "Content-Type: application/json" -d '{"value":1}' http://192.168.178.191/api/channel/physical/io/1
* Hostname was NOT found in DNS cache
*   Trying 192.168.178.191...
* Connected to 192.168.178.191 (192.168.178.191) port 80 (#0)
> PUT /api/channel/physical/io/1 HTTP/1.1
> User-Agent: curl/7.35.0
> Host: 192.168.178.191
> Accept: */*
> Content-Type: application/json
> Content-Length: 11
>
* upload completely sent off: 11 out of 11 bytes
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: lwIP/1.4.1
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS
< Access-Control-Allow-Headers: Content-Type
< Content-Length: 175
< Content-Type: application/json
< Pragma: no-cache
<
* Closing connection 0
{
    "label" : "Channel 1",
    "supported_types" : ["di", "do", "ai", "s0"],
    "type" : "do",
    "enabled" : 1,
    "mode" : "normal",
    "virtual_channel" : 0,
    "level" : "direct",
    "delay_on" : 0,
    "delay_off" : 0,
    "value" : 1
}

Configure physical channel 1 as Digitial Input

curl -v -X PUT -H "Content-Type: application/json" -d '{"type":"di","mode":"normal"}' http://192.168.178.191/api/channel/physical/io/1
* Hostname was NOT found in DNS cache
*   Trying 192.168.178.191...
* Connected to 192.168.178.191 (192.168.178.191) port 80 (#0)
> PUT /api/channel/physical/io/1 HTTP/1.1
> User-Agent: curl/7.35.0
> Host: 192.168.178.191
> Accept: */*
> Content-Type: application/json
> Content-Length: 29
>
* upload completely sent off: 29 out of 29 bytes
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: lwIP/1.4.1
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS
< Access-Control-Allow-Headers: Content-Type
< Content-Length: 173
< Content-Type: application/json
< Pragma: no-cache
<
* Closing connection 0
{
    "label" : "Channel 1",
    "supported_types" : ["di", "do", "ai", "s0"],
    "type" : "di",
    "enabled" : 1,
    "mode" : "normal",
    "pullup" : 0,
    "virtual_channel" : 0,
    "level" : "direct",
    "threshold" : 0,
    "value" : 0
}

Query state of physical channel 1 (Digitial Input)

In this example, the JSON response should only contain the actual state, but not configuration properties of the channel.

curl -v http://192.168.178.191/api/channel/physical/io/1?filter=config
* Hostname was NOT found in DNS cache
*   Trying 192.168.178.191...
* Connected to 192.168.178.191 (192.168.178.191) port 80 (#0)
> GET /api/channel/physical/io/1?filter=config HTTP/1.1
> User-Agent: curl/7.35.0
> Host: 192.168.178.191
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: lwIP/1.4.1
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS
< Access-Control-Allow-Headers: Content-Type
< Content-Length: 11
< Content-Type: application/json
< Pragma: no-cache
<
* Closing connection 0
{
    "value" : 1
}

Query current readings of physical channel 1 (S0 Input)

In this example, the physical channel 1 is configured as S0 input and the query retrieves the current meter reading.

curl -v http://192.168.178.191/api/channel/physical/io/1
* Hostname was NOT found in DNS cache
*   Trying 192.168.178.191...
* Connected to 192.168.178.191 (192.168.178.191) port 80 (#0)
> GET /api/channel/physical/io/1 HTTP/1.1
> User-Agent: curl/7.35.0
> Host: 192.168.178.191
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: lwIP/1.4.1
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS
< Access-Control-Allow-Headers: Content-Type
< Content-Length: 162
< Content-Type: application/json
< Pragma: no-cache
<
* Closing connection 0
{
    "label" : "Channel 1",
    "supported_types" : ["di", "do", "ai", "s0"],
    "type" : "s0",
    "enabled" : 1,
    "pulses_per_unit" : 1000,
    "unit" : "kWh",
    "pulse_counter" : 2173327,
    "value" : 2173.327
}

Query current configuration and state of all physical channels

curl -v http://192.168.178.191/api/channel/physical/io
* Hostname was NOT found in DNS cache
*   Trying 192.168.178.191...
* Connected to 192.168.178.191 (192.168.178.191) port 80 (#0)
> GET /api/channel/physical/io HTTP/1.1
> User-Agent: curl/7.35.0
> Host: 192.168.178.191
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: lwIP/1.4.1
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS
< Access-Control-Allow-Headers: Content-Type
< Content-Length: 1056
< Content-Type: application/json
< Pragma: no-cache
<
* Closing connection 0
{
    "1" : {
        "label" : "Channel 1",
        "supported_types" : ["di", "do", "ai", "s0"],
        "type" : "s0",
        "enabled" : 1,
        "pulses_per_unit" : 1000,
        "unit" : "kWh",
        "pulse_counter" : 2173327,
        "value" : 2173.327
    },
    "2" : {
        "label" : "Channel 2",
        "supported_types" : ["di", "do", "ai", "s0"],
        "type" : "di",
        "enabled" : 1,
        "mode" : "normal",
        "pullup" : 1,
        "virtual_channel" : 0,
        "level" : "direct",
        "value" : 1
    },
    "3" : {
        "label" : "Channel 3",
        "supported_types" : ["di", "do", "ai", "s0"],
        "type" : "di",
        "enabled" : 1,
        "mode" : "normal",
        "pullup" : 0,
        "virtual_channel" : 0,
        "level" : "direct",
        "threshold" : 0,
        "value" : 0
    },
    "4" : {
        "label" : "Channel 4",
        "supported_types" : ["di", "do", "ai", "s0"],
        "type" : "do",
        "enabled" : 1,
        "mode" : "normal",
        "virtual_channel" : 0,
        "level" : "direct",
        "delay_on" : 0,
        "delay_off" : 0,
        "value" : 0
    },
    "5" : {
        "label" : "Channel 5",
        "supported_types" : ["di", "do", "ai", "s0"],
        "type" : "do",
        "enabled" : 1,
        "mode" : "normal",
        "virtual_channel" : 0,
        "level" : "direct",
        "delay_on" : 0,
        "delay_off" : 0,
        "value" : 0
    },
    "6" : {
        "label" : "Channel 6",
        "supported_types" : ["di", "do", "ai", "s0"],
        "type" : "ai",
        "enabled" : 1,
        "mode" : "0-10 V",
        "virtual_channel" : 0,
        "threshold" : 0,
        "normalized_value" : 0,
        "value" : 0.000,
        "unit" : "V"
    }
}

Query only current state of all physical channels

This is an example of an optimized query, where only current state properties are retrieved.

curl -v http://192.168.178.191/api/channel/physical/io?filter=config
* Hostname was NOT found in DNS cache
*   Trying 192.168.178.191...
* Connected to 192.168.178.191 (192.168.178.191) port 80 (#0)
> GET /api/channel/physical/io?filter=config HTTP/1.1
> User-Agent: curl/7.35.0
> Host: 192.168.178.191
> Accept: */*
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: lwIP/1.4.1
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS
< Access-Control-Allow-Headers: Content-Type
< Content-Length: 164
< Content-Type: application/json
< Pragma: no-cache
<
* Closing connection 0
{
    "1" : {
        "pulse_counter" : 2173327,
        "value" : 2173.327
    },
    "2" : {
        "value" : 1
    },
    "3" : {
        "value" : 0
    },
    "4" : {
        "value" : 0
    },
    "5" : {
        "value" : 0
    },
    "6" : {
        "normalized_value" : 0,
        "value" : 0.000,
        "unit" : "V"
    }
}

Python example

The following Python example relies on the Python requests library. It assumes that you already configured a given channel on your XPL Rail device as digitial output and allows to switch such a channel on or off. Intended as example, it does not do much error checking - this is left as an exercise to the reader ;-)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#!/usr/bin/env python
#
# Usage: xpl-on-off.py <ip> <channel> [on|off]
#
# Example: $ xpl-on-off.py 192.168.178.191 1 on
#
from __future__ import print_function
import sys
import json
import requests

if len(sys.argv) < 3:
    print("Usage: %s [-v] <ip> <channel> [on|off]" % (sys.argv[0]), file = sys.stderr)
    sys.exit(2)

params = sys.argv[1:]

verbose = False
if params[0] == '-v':
    params.pop(0)
    verbose = True

host = params.pop(0)
path = "/api/channel/physical/io/%d" % (int(params.pop(0)))

on_off = params.pop(0).lower() == "on"
payload = { 'value': int(on_off) }

url = "http://" + host + path
headers = { 'Content-Type' : 'application/json' }
data = json.dumps(payload)

if verbose:
    print("URL: " + url, file = sys.stderr)
    print("JSON: " + data, file = sys.stderr)

r = requests.put(url, data = data, headers = headers)
try:
    r.raise_for_status()
except requests.exceptions.HTTPError:
    if verbose:
        print("ERROR", file = sys.stderr)
    sys.exit(1)

response = r.json()
if 'result' in response:
    if verbose:
        print("ERROR: %d" % (response['result']), file = sys.stderr)
    sys.exit(1)

if verbose:
    print("OK", file = sys.stderr)

sys.exit(0)

PHP example

The following example written in PHP uses a raw socket to connect to an XPL Rail. It assumes that you already configured a given channel on your XPL Rail device as digitial output and allows to switch such a channel on or off. Here too, there is no much error checking and you should really use some library.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php

    function xpl_on_off($host, $channel, $on_off)
    {
        $body = '{ "value": ' . strval(intval($on_off)) . ' }';

        $header  = "PUT /api/channel/physical/io/" . strval($channel) . " HTTP/1.0\r\n";
        $header .= "Host: $host\r\n";
        $header .= "Content-Type: application/json\r\n";
        $header .= "Content-Length: " . strlen($body) . "\r\n";
        $header .= "\r\n";

        $fp = @fsockopen($host, 80);
        if ($fp === FALSE)
            return FALSE;

        $rv = fputs($fp, $header . $body);
        if ($rv === FALSE)
        {
            fclose($fp);
            return FALSE;
        }

        $response = "";
        while (!feof($fp))
            $response .= fgets($fp, 10 * 1024);
        fclose($fp);

        // search HTTP body
        $jsonstr = ltrim(strstr($response, "\r\n\r\n"));
        $json = json_decode($jsonstr, TRUE);

        // check for result properties, indicates error
        if (array_key_exists('result', $json))
            return $json['result'];

        // everthing is fine, return success to caller
        return TRUE;
    }

    xpl_on_off('192.168.178.191', 1, FALSE);

JavaScript/NodeJS example

The following example is written in JavaScript. You can embedd it within a custom web page, or use it in a NodeJS server environment. It also assumes that you already configured a given channel on your XPL Rail device as digitial output and allows to switch such a channel on or off.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#!/usr/bin/nodejs
/*
 * This JavaScript example shows how to build a JSON request to switch a
 * channel on an XPL Rail device on or off.
 * It can be used which NodeJS or within a browser environment. In a NodeJS
 * environment, install 'xhr2' library first, using:
 *  $ npm install xhr2
 * This library emulates the XMLHttpRequest object which is present in browser
 * environments by default.
 * For usage in browsers, you can comment out/remove also the code which is used
 * to pass the command line arguments to the functions.
 */

/* remove the following line when using this example within a browser environment */
var XMLHttpRequest = require('xhr2');

/**
 * A sample callback function which can be used with xpl_request.
 *
 * This callback is called when the request is finished or an error occurred.
 * @param {object} request - the XMLHttpRequest object of the request. which is called after the request finished:
 * @param {object} error - Null if the request was successfull, an Error object otherwise.
 */
function example_callback(request, error)
{
    if (error)
        console.log(error.toString());
}

/**
 * Helper function to send a request to a given XPL Rail.
 *
 * @param {String} method - passed to the XMLHttpRequest object, i.e. 'GET' or 'PUT'
 * @param {String} url - URL on the XPL Rail to access
 * @param {Object} data - a JavaScript object containing data to send, is
 *                        converted to JSON string and send within the HTTP request
 * @param {Function} callback - callback function to be called on success/error
 * @param {Number} timeout - timeout in milliseconds for the request to complete
 * @return {bool} False on error, true otherwise.
 */
function xpl_request(method, url, data, callback, timeout)
{
    var r, r_timeout;

    /* sanitize and check parameters */
    method = method.toUpperCase();
    if (method != "GET" && method != "PUT")
        return false;

    /*
     * Because of a caching bug in IE, we must generate a unique URL for each 
     * request, so we simply append a timestamp.
     */
    if (method == "GET")
    {
        /* look for existing url parameters and add required delimiter */
        if (url.search("?") >= 0)
            url += "&";
        else
            url += "?";

        url += "_ts=" + new Date().getTime();
    }

    /* create new request object */
    r = new XMLHttpRequest();

    /* register a callback in case request timeouts */
    r_timeout = setTimeout(function() {
        /* first abort the request */
        r.abort();

        /* then call user-defined callback with error information */
        callback(r, new Error("xpl_request: timeout occured"));
    }, timeout);

    /* register a callback to observer request progress */
    r.onreadystatechange = function() {
        if (r.readyState != 4)
            return;

        /* request finished, so cancel timeout */
        clearTimeout(r_timeout);

        /* check HTTP status code returned from server and call user-defined callback */
        if (r.status != 200)
            callback(r, new Error("xpl_request: server returned: " + r.status));
        else
            callback(r, null);
    }

    /* open url */
    r.open(method, url, true);

    /* if data is given convert to JSON string representation */
    if (data !== null)
    {
        r.setRequestHeader('Content-Type', 'application/json');
        data = JSON.stringify(data);
    }

    /* send out the request */
    r.send(data);
    return true;
}

/**
 * Helper function to switch a given channel (Digital Output) on a given XPL Rail.
 *
 * @param {String} host - hostname/ip address of the XPL Rail
 * @param {Number} channel - physical channel number to switch
 * @param {bool} on - True switches the channel on, False off.
 * @return {bool} False on error, true otherwise.
 */
function xpl_switch_channel(host, channel, on)
{
    var data = { 'value': on ? 1 : 0 };
    var url = "http://" + host + "/api/channel/physical/io/" + channel;

    return xpl_request("PUT", url, data, example_callback, 5000);
}

/* Remove the following lines when your run this example in a browser environment.
 * In a nodejs environment, it passes the command line arguments to our helper
 * functions.
 */
if (process.argv.length == 5)
{
    xpl_switch_channel(process.argv[2], process.argv[3], process.argv[4] == 'on');
}
else
{
    console.log("Usage: " + process.argv[2] + "<ip-address> <channel> on|off");
}