Graphics.WebCommandingCapability - lordmundi/wikidoctest GitHub Wiki

Web Commanding Capability

« Remote Commanding Capability | EDGE User’s Guide | Detecting When An EDGE Instance Is Finished Loading »

The web commanding server is a complete web server embedded into EDGE. It defaults to running on port 8080 and the connection information is printed out at startup. It can be very useful for remotely commanding an EDGE instance, getting health/status information, or even using web pages as GUIs for your project.

Simple directions

  1. Go to "<http://<wcs_address>>" in a web browser (where <wcs_address> is the URI printed out to the console during startup) and use the built in GUIs and XML data sources provided by default in EDGE.

or, if you are looking to host your own content through the web server:

  1. Place the files you want to serve in the web server under the "$USERDATA/gui/html/" directory
  2. Go to "<http://<wcs_address>/userdata/>" in your browser


XSLT transforms the returned XML from the EDGE WCS into HTML web pages

What's really going on

The web commanding server is some logic and features that are built on top of a standalone tcl web server known as "Dandelion". The web commanding server adds some unique capabilities, such as tcl callbacks on URI regular expressions, a URI alias capability, and a templating language allowing tcl procedure calls and variables to be easily added into the HTML or XML of the web server responses.

The web commanding server (usually abbreviated WCS) has some content it serves by default out of EDGE using xml and xslt transforms. You can see these if you go to the address printed out to the console at startup. Once nice thing about these pages is that they are returned in pure XML. In fact, if you do a "View Source" in your browser, you will see very simple and pretty xml responses. This makes it very easy to get status information from EDGE via the HTTP protocol using standard libraries and tools and get back easy-to-parse XML data. The transformation into the HTML you see in the browser is done via XSLT stylesheets and is done by the browser on the client side. These included pages can serve as some good examples if you are constructing your own pages to be served in EDGE.

Let's walk through some examples of how you might customize the WCS beyond the default content.

Example 0: Just serve a file

This is basically the steps outlined above. Go to the WCS uri printed out during startup and add "/userdata/README.txt" to the URI. The "/userdata" part of the URI is aliased by default to your "$USERDATA/gui/html" directory. Place whatever static files you want to serve in this directory and they will show up under "/userdata/".


Example 0 shows a static file being served from the userdata using the "/userdata" alias

Example 1: A generic tcl procedure callback

Here we assign a callback for the uri "/example1" to call our custom tcl procedure to provide the response. Look at the following example tcl code:

proc wcs_example1 { myarg sock headers settings body } {
    puts $sock [dict get $::dandelion::first_line 200]
    puts $sock "Content-Type: text/html; charset=utf-8"
    puts $sock [clock format [clock scan now] -format {Date: %a, %d %b %Y %T GMT} -timezone :UTC]
    puts $sock "\r\n"
    puts $sock "<html><head><title>Example 1</title></head><body>"
    puts $sock "<h1>Example1</h1><br />"
    puts $sock "This is a callback for a plain tcl procedure providing the entire HTTP response<br />"
    puts $sock "I can pass in arguments.  In this case, the argument was \"$myarg\" <br />"
    puts $sock "The headers, server settings, and request body are also passed to you.<br />"
    puts $sock "In this case, the headers were:<br />"
    puts $sock "<ul>"
    dict for {key value} $headers {
        puts $sock "    <li>$key: $value</li>"
    }
    puts $sock "</ul>"
    puts $sock "</body></html>"
}

wcs_callback $env(WCS_PORT) {^/example1[/]*$} "wcs_example1 somearg"

Here you can see that the last line simply assigns the callback any time the regular expression matches the requested item. The '[/]*' portion of the regular expression is allowing for an optional trailing slash.

Now, if we go to <http://<wcs_address>/example1> you should see your custom response:


Example 1 in a browser

Example 2: using a template to return the response

The code above can get a bit cumbersome, and often times you want to return mostly static content with only a few dynamic elements (tcl variables for example). In this case, it is useful to turn the previous example inside out, with a file that gets returned verbatim, except for the portions that need to be evaluated by the Tcl interpreter. If we implemented the same response as above, but using a template, we might have:

example2.tmpl:

% set tmpl_contains_header 1
[dict get $::dandelion::first_line 200]
Content-Type: text/html; charset=utf-8
[clock format [clock scan now] -format {Date: %a, %d %b %Y %T GMT} -timezone :UTC]

<html><head><title>Example 2</title></head><body>
<h1>Example2</h1><br />
This is a callback using a template to provide the response.  Notice that tcl<br />
variables and procedure calls can be used directly in the template.  Also tcl code<br />
not included in the response can be preceded by a percent (%) sign.  In the template<br />
you can see this used in a for loop to print out all of the headers.<br /><br />

You can also pass in an argument to the template (the variable name is "type").  In <br />
this case, the argument was "$type".  The headers, server settings, and request body<br />
are also passed to you.  In this case, the headers were:<br />

The template function will send a header for you, which defaults the content type to<br />
application/xml.  If you want to send the header yourself, set a variable called<br />
"tmpl_contains_header" to 1 so that the templating callback knows you will send it.  In this<br />
example, we customize the header so we can send back html instead of xml.<br />

<ul>
% dict for {key value} $headers {

    <li>$key: $value</li>
% }

</ul>
</body></html>

NOTE: This template provides the HTTP header on its own, allowing you to customize all elements of the response. For ease of use, you can also leave this out (and not set the variable "tmpl_contains_header") and the WCS will provide a header that assumes you are providing "Content-type: application/xml" and response code "200".

The syntax for the templating language is pretty simple. As the template above points out, tcl variables and procedure calls can be used directly in the template (via the $ and [] operators - the same as normal tcl). Also tcl code not included in the response can be preceded by a percent (%) sign. You can use this for setting variables, running for/while loops in the template, etc. You can see this being used in the template with a for loop to print out all of the headers being passed in.

This template needs to be placed under "$USERDATA/gui/html/templates/". To use this template, the only tcl code we would supply to EDGE would be a callback telling it to associate that template with a specific URI. In this example, we will tie it to the URI "/example2" with the following code:

wcs_callback $env(WCS_PORT) {^/example2[/]*$} "wcs_tmpl \"$env(USERDATA)/gui/html/templates/example2.tmpl\" somearg2"

Now if we go to <http://<wcs_address>/example2> you should see template response.


Example 2 is similar, but the code is much cleaner in a template

Example 3: A custom alias

As we saw above, there is a default alias for "/userdata" to go to your "$USERDATA/gui/html" directory. You may want to serve static content as we did in example 0, but not under this path.

With this line of tcl, we will create another alias, "/example3" which will also point to the "$USERDATA/gui/html" directory. To test, go to the URI "/example3/README.txt"

dict set ::dandelion::ports $env(WCS_PORT) aliases "/example3" "$env(USERDATA)/gui/html"


Example 3 is the same as Example 0, but under a different URI.

« Remote Commanding Capability | EDGE User’s Guide | Detecting When An EDGE Instance Is Finished Loading »

⚠️ **GitHub.com Fallback** ⚠️