Building a web application

In this post, we'll build a simple web application using the pico stack.

This example is built by layering a new ruleset onto a pico hosted by the Pico Labs Affiliate Network (PLAN), where applications are only for private viewing (whilst logged in to an account therein).

Here we show how to program a public web page within that same pico.

Because my pico stores data (its "state"), it is simple to grab the latest temperature readings from each of the Wovyn sensors on my property in Emery County, Utah, and display them.

We will be able to add this new functionality to my pico with no need to stop and restart the underlying pico engine, and with no disruption to any of the other picos (which belong to other people) hosted by that same pico engine.

Comparison to a heavier stack

If, instead of using pico stack, I had been using a stack with a separate database layer, this might involve a lot of work:

  • obtain permission to access the database
  • make a connection to the database server
  • write a query against that database
  • interpolate the results into a web page

The query itself might look something like this (for those who know the SQL (declarative) programming language (if you don't, don't worry, you won't need it when you use the pico stack)):

SELECT loc.device_name, readings.time, readings.temperature
  FROM ent_mappings loc, ent_record readings
 WHERE loc.device_id = readings.device_id
 ORDER BY readings.time DESC
 LIMIT 20

And, then you'd have to have code in your application to select the latest reading from each of the four sensors (the database query above asks for the latest twenty readings because sometimes an individual sensor reading doesn't get recorded). Or, you could use a more complicated, nested SQL query.

The new ruleset

Instead, we'll just add a new KRL ruleset to the pico:

ruleset com.vcpnews.w {
  meta {
    use module io.picolabs.wrangler alias wrangler
    use module io.picolabs.plan.wovyn-sensors alias ws
    shares now
  }
  global {
    now = function(_headers){
      <<<!DOCTYPE HTML>
<html>
  <head>
    <title>Wovyn sensors now</title>
    <meta charset="UTF-8">
<style type="text/css">
body { font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; }
th { text-align: left; }
th:first-child { min-width: 100px; }
th:nth-child(2) { min-width: 80px; }
</style>
  </head>
  <body>
<h1>Wovyn sensors now</h1>
<h2>#{time:now().split("T").head()}</h2>
#{ws:summary()}  </body>
</html>
>>
    }
    tags = ["wovyn-sensors","summary"]
  }
  rule initialize {
    select when wrangler ruleset_installed where event:attr("rids") >< meta:rid
    if wrangler:channels(tags).length() == 0 then
      wrangler:createChannel(
        tags,
        {"allow":[{"domain":"com_vcpnews_w","name":"*"}],"deny":[]},
        {"allow":[{"rid":meta:rid,"name":"*"}],"deny":[]}
      ) setting(channel)
  }
}

The initialize rule creates a new channel for this public application (assuming one hasn't already been created).

The now function generates a complete HTML page, including a table generated by the underlying (pre-existing) ruleset, the one that has been gathering the data for months now. We use CSS to make the provided table look better.

Installing the new ruleset

This is super easy if you are hosting your own pico engine because the developer UI has a box where you can enter the raw URL of the ruleset and add it to a pico.

In this case, PLAN is hosting my pico. But its landing page has a box where you can install modules (a ruleset can also be a module), which works fine even for rulesets which are not modules.

Once it is installed, you can build a URL for the web application, which will look like this:

https://plan.picolabs.io/c/ECI/query/com.vcpnews.w/now.html

Obtaining the ECI for the web application

That URL, unlike the ones for PLAN applications, can be used by anyone. The only problem is that you need to know the Event Channel Identifier (shown as ECI in the above, but must be replaced by the actual identifier before it is usable).

Fortunately, PLAN has an app for that, io.picolabs.plan.introspect, which can be installed in my PLAN pico so that I can look at all my channels and copy the identifier for the new channel, the one with tags "wovyn-sensors","summary"  as given to the wrangler:createChannel action in the initialize rule.

Getting the actual data

The sensor data is held in my pico, but by a different ruleset. Rulesets do not have direct access to the state held by another ruleset. So the io.picolabs.plan.wovyn-sensors ruleset had to be modified (as shown in this commit (for those familiar with GitHub who want to see the details) and the few commits following it).

It was a simple matter of adding a function named summary that generates a snippet of HTML code and then that ruleset provides it for the use of the new ruleset. It took twenty lines of code in that ruleset to generate the HTML table (like the one shown in the screenshot at the start of this post) with current information.

The ruleset that has custody of the data from the Wovyn sensors provides needed summary information, but does not need to grant access to the the data itself.

Notes

"a public web page" https://plan.picolabs.io/c/clqvdoh470z5ivvpr3n0udhvt/query/com.vcpnews.w/now.html

If that is too long, you could use a URL shortener: https://bruceatbyu.com/s/LatestTemps which redirects to the less readable and less memorable URL.

"a URL shortener" whose full implementation is in a GitHub repo named Shorten

"with no disruption" means that we have a running system which we are improving while it continues to run. Not only is my own pico improved, but other picos running on the same system (the PLAN pico engine) are not impacted by the changes.

"the [ruleset] that has been gathering the data for months now" and which I use each month to create charts like these for January 2024.

No comments:

Post a Comment