Accessing a Function Shared by Another Pico

Often we feel a need to call a function which is shared by a ruleset installed in a different pico. There are two ways to do this.

Do not use these techniques within a single pico

Picos are single-threaded, and the techniques shown here use a synchronous operation. If, while your pico is evaluating, it makes a synchronous request to itself, the request will just be added to the pico's incoming queue. The current thread will wait forever (before it continues and evaluates the next thing on its incoming queue).

If this happens, you will have to stop your pico engine (Ctrl-C). You can then restart your engine and no state will be lost.

Use the picoQuery() function in Wrangler

If we are using io.picolabs.wrangler as a module in our ruleset, we can take advantage of its general purpose function picoQuery to invoke a function shared by a different pico.

Assuming that we have this statement in the meta block of our ruleset:

use module io.picolabs.wrangler alias wrangler

We can use picoQuery(), provided by Wrangler, as shown in the following rule fragment:

rule query_rule { pre { eci = eci_to_other_pico; args = {"arg1": val1, "arg2": val2}; answer = wrangler:picoQuery(eci,"my.ruleset.id","myFunction",{}.put(args)); } if answer{"error"}.isnull() then noop(); fired { // process using answer } }

This will only succeed if my.ruleset.id has shared myFunction in its meta block.

Wrangler's picoQuery() function will use ctx:query() to query the other pico if it is on the same pico engine. If it is not, picoQuery() will send an HTTP GET request to the other pico, which includes the RID and function name, and the named argument values which the remote function expects. It will check the HTTP return code, and decode the returned content, finally returning it to our code as KRL data. If an error is detected, the return value will instead be a map with key error. HTTP request may also include the keys httpStatuspicoQueryErrorpicoQueryErrorMsg, and picoQueryReturnValue.

Use http:get() directly

Since each pico is a first-class Internet citizen, it has an API, so we can use techniques that are applicable to any external API, as discussed in the Modules and External APIs Lesson. The same example might be written this way:

pre { eci = eci_to_other_pico; args = {"arg1": val1, "arg2": val2}; host = "http://localhost:8080"; url = host + "/sky/cloud/" + eci + "/my.ruleset.id/myFunction"; response = http:get(url,args); answer = response{"content"}.decode(); } if response{"status_code"} == 200 then noop(); fired { // process using answer }

Note that while you can use this to query picos on the same engine, it is not very efficient. In addition, you will need to ensure the channel represented by the ECI has a policy that allows queries. HTTP queries are specifically disallowed on family channels (the channels between a parent and its children).

Copyright Picolabs | Licensed under Creative Commons.