Developer Tips for Pico Engine

There are a few things that will make managing your pico engine and writing KRL code easier. 

Managing your Pico Engine

If you need to remove your pico engine and/or change to a different version, follow these instructions.

Installation

To install and run the pico engine:

$ npm install -g pico-engine
$ pico-engine

Install a Specific Version

Install a specific version of the pico engine code. In this example, we're asking for the engine at version 1.0.0:

npm install -g pico-engine@1.0.0

Update to the Latest Version

You also use the npm install command to get the latest version, or the variant shown above if you want a specific version rather than the latest, using the command:

npm install -g pico-engine

Check Your Version

You can see which version you are using near the bottom-right corner of the developer UI page (localhost:3000):

or by running the command:

npm ls -g pico-engine

or using the --version flag:

pico-engine --version

Remove Pico Engine

Remove the pico engine code.

npm rm -g pico-engine

Initialize all picos

If you wish to remove all picos and start over, use one or more of these commands:

rm -r ~/.pico-engine/db          #removes all picos
rm -r ~/.pico-engine/rulesets-db #removes the relationship between URLs and rulesets
rm -r ~/.pico-engine/rulesets    #removes compiled rulesets (node modules)
rm -r ~/.pico-engine             #shortcut to do all of the above

Note that these commands assume your PICO_ENGINE_HOME has been left at the default value, which is ~/.pico-engine. Change them accordingly if you set your home elsewhere.

there is no undo

Removing these folders cannot be undone! If you remove you picos, they will be gone. Removing the compiled rulesets will be undone if you simply install them again.

Finding your IP address

By default, your pico engine will set its base URL as http://localhost:3000 and use this as its host, available from KRL code as meta:host

Instead of using localhost, you could use the local IP address of your machine. Discovery of this IP address depends on your operation system.

If you are running your engine locally, then this IP address will be the private IP address given to you by your router, and will not be globally available on the Internet. However, other machines or devices on the same router/subnet will be able to find your engine using this IP address.

If you are running your engine on a cloud service such as Amazon's EC2 services, then this IP address will be globally available.

Switching to a different wifi or router will change your IP address, but the engine will use whatever address it found when it was booted up, NOT your current IP address. Restart your engine to use the new IP address.

Configuring your default base URL

You have the option to configure your pico engine's host on startup like this:

PICO_ENGINE_BASE_URL=http://localhost:3000 pico-engine

This is particularly useful if you expect to develop locally and jump between WiFis without wanting to restart your engine. This is also useful if you want to set the host to a domain name (like http://www.picolabs.io) instead of an IP address.

Using the pico engine with WSL

WSL 2 is a Linux virtualization environment for Windows. WSL is a great way to run the pico engine on Windows. There can be some problem bridging ports between Windows and the virtualized Linux environment. Rogerio Cruz has written a PowerShell script that allows you to bridge ports between your WSL 2 environment and Windows host.

Working with More than One Image

By default the pico engine stores the state of the engine, including logs and compiled KRL, in the ~/.pico-engine/rulesets directory. You can change this to a different directory by setting the PICO_ENGINE_HOME environment variable.

Because the pico engine manages the state of all the picos running in it, this directory and its contents represent an image of the current state of all the picos in the engine at any given time. 

By changing the PICO_ENGINE_HOME environment variable, you can keep different development environments or projects separate from each other, and go back to the place you left off in a particular pico application. 

For example, you could have a different pico engine image for a game project and an IoT project and start up the pico engine in either environment like so:

# work on my game project
PICO_ENGINE_HOME=~/.dnd_game_image pico-engine

# work on IoT project
PICO_ENGINE_HOME=~/.iot_image pico-engine

Running multiple pico engines

To run multiple pico engines on the same host simultaneously, you need to assign each instance of the engine a different port and a different directory for its image state. For example, while running a pico engine listening on the default port, and using the default location for its database, you could start an additional instance with a command like this one:

PORT=3001 PICO_ENGINE_HOME=~/.pico-engine-1 pico-engine

Using Docker

The pico engine repository contains a Dockerfile that can be used to build a Docker image for use locally or in a cloud service like AWS Fargate

To build a container use docker build:

docker build -t <username>/<container-name> github.com/picolab/pico-engine

For example:

docker build -t pjw/pico-engine github.com/picolab/pico-engine

builds a container named pjw/pico-engine for your use. 

To run the image use docker run:

docker run -p <port>:3000 -v <mount-point>:/var/pico-image -d <username>/container-name>

For example:

docker run -p 3001:3000 -v ~/images/pico-image:/var/pico-image -d pjw/pico-engine

runs the docker with a port of 3001 and the image files located at ~/images/pico-image on the local machine.

If you need to set the URL for your engine, you can add the PICO_ENGINE_BASE_URL env variable. For example:

docker run -p 443:3000 -v ~/images/pico-image:/var/pico-image -d pjw/pico-engine -e PICO_ENGINE_BASE_URL=https://picos.picolabs.io

would use https://picos.picolabs.io as the engine URL. 

You can run the same container multiple times with different ports and mount points to have multiple engines running at the same time.

docker run -p 3000:3000 -v ~/images/dnd_game_image:/var/pico-image -d pjw/pico-engine
docker run -p 3001:3000 -v ~/images/iot_image:/var/pico-image -d pjw/pico-engine

would run two different pico engines, each with their own collection of picos, one on port 3000 and one on port 3001. 

Hosting Code

Because the rules engine reads your KRL program from the Web, it has to be available online. You can host it anywhere. There are a few options:

  1. GitHub works well, with one caveat. See these instructions for more information about how to use GitHub to host KRL. The caveat is that GitHub caches the resources on raw URLs for about 5 minutes. If you're making frequent changes, it can be frustrating to wait out the cache. Alternatively, GitLab should give you fewer caching problems. (raw URLs to rulesets in private repositories need an account access token)
  2. AWS S3 works well. Be sure to make the URL for the ruleset is readable by the engine. 

The pico engine compiles your KRL into a Node.js module, which it stores in a folder within the ~/.pico-engine/rulesets folder (or the folder specified by PICO_ENGINE_HOME).

Flushing the Ruleset

Wherever you host your code, the pico-engine caches it after compiling it for better performance. That means that you need to flush your code from the cache whenever you update it. After pushing a change to your ruleset, you will need to instruct the pico-engine to retrieve the source code again and re-compile it.

Manual flush

Using the developer UI, select the RID in the "Rulesets" tab, and click on the "flush" button:

Flush from your program

Use the wrangler API in the postlude of a rule:

    raise wrangler event "rulesets_need_flushing"

which will check the URL for all of the rules installed in your pico and flush those that have changed. When wrangler has completed the operation, it will raise the wrangler:rulesets_flushed event, with no attributes.

Flush with a URL

Use a URL like this one, from a browser, from Postman, or with a curl command:

http://localhost:3000/api/flush?url=https://raw.githubusercontent.com/Picolab/pico_lessons/master/hello_world.krl

You will need to give the correct domain and port for your pico engine, and provide the URL of the KRL source file you want to flush. This is the same as the URL which installed the ruleset.

Note: the engine keeps one compiled version of each URL, so flushing a ruleset using any of these methods flushes it in all picos that have installed that ruleset with the same URL. 

Parsing KRL

You need to be able to check your ruleset for parse errors before checking it in. Debugging syntax errors on the rules engine is a messy business. 

There are four ways to do it.

  1. You can try installing the ruleset into a pico. If there are errors, the first one encountered will be shown in red. 
  2. You can download and run the node pico engine compiler on your own machine and run it from the command line. 
  3. You can install a pre-commit hook for git, so that your code is automatically parsed when you try to commit changes. If your code fails to parse, the change will not be committed.
  4. You can flush the code from your pico engine using the developer UI. This will cause the engine to re-compile the ruleset and show any errors. 

The second and third methods are recommended for any serious development.

To validate using the second method, Install the KRL Compiler using this command:

npm install -g krl-compiler
krl-compiler -v                # v1.0.1

Ensure that it is the same version as your pico-engine. After installing it, run the command krl-compiler --verify < <path to krl ruleset>. If this command doesn't output anything, the code parsed correctly.

To validate using the fourth method, open up the ruleset in your code editor. Make your changes and save the ruleset.

You'll notice that at line 12 I typed gibberish. This ruleset should fail to parse and flush.

Open up the pico engine and navigate to the ruleset tab of the pico that has the ruleset installed. Flush the ruleset. If it parses correctly the ruleset will flush. If it doesn't, you will receive a parse error and the ruleset won't flush.

Initializing Rulesets

Rulesets often need to be initialized. The most common actions are setting the default values of entity variables, setting up scheduled events, and creating channels. Wrangler raises the wrangler:ruleset_installed even whenever a new ruleset is installed in a pico. An initialization rule selects on that event. You should include an event expression so that the rule only runs for the ruleset it's in. For example, the following rule sets defaults for two entity variables and sets a repeating scheduled event:

Ruleset Initialization
rule inialize_ruleset {
  select when wrangler ruleset_installed where event:attr("rids") >< meta:rid
  pre {
    period = ent:heartbeat_period
             .defaultsTo(event:attr("heartbeat_period") || default_heartbeat_period)
             .klog("Initilizing heartbeat period: "); // in seconds

  }
  if ( ent:heartbeat_period.isnull() && schedule:list().length() == 0) then send_directive("Initializing sensor pico");
  fired {
    ent:heartbeat_period := period if ent:heartbeat_period.isnull();
    ent:emitter_state := "running" if ent:emitter_state.isnull();
      
    schedule emitter event "new_sensor_reading" repeat << */#{period} * * * * * >>  attributes { }
  }  
}

Initializing rulesets can save a lot of effort getting a pico set up properly and automate much of the work.