Date: Fri, 29 Mar 2024 15:23:01 +0000 (UTC) Message-ID: <1097067077.7.1711725781008@dd9b455409b3> Subject: Exported From Confluence MIME-Version: 1.0 Content-Type: multipart/related; boundary="----=_Part_6_140201039.1711725781007" ------=_Part_6_140201039.1711725781007 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Content-Location: file:///C:/exported.html
After completing this lesson, you will be able to:
explain how a pico is a first-class Internet citizen
explain channels and use the developer UI to manage them
send events and queries to a pico using a browser, and the "Testing" tab=
use logs
use the klog() operator
When thinking about the idea of a pico, you have to understand that a pi=
co is essentially a digital representation of "something". That "somet=
hing" could be physical (like a temperature sensor) or conceptual (like a c=
ollection of sensors). The KRL rulesets installed on a given pico perf=
orm the magic that makes this digital representation behave the way you exp=
ect. And yet, how does a pico stay in sync with its physical device? How do=
es it keep track of the new sensor you just added to the collection? How do=
es it provide data to outside sources like web apps, or even a terminal usi=
ng curl
?
Here enter Events and Queries. Queries are very simple because the only = thing they can do is call a function. This function is defined in the globa= l block of a KRL ruleset installed on your pico. As a result, a query is no= t capable of changing state or taking action (like sending a text message).= On the other hand, events can modify state and perform side = effects. Incoming events are handled by the rules defined in a KRL ruleset.=
This lesson aims to teach you how to send Events and Queries to a given = pico.
complete the Pico-Engine Quickst= art
have the hello_world
ruleset from the Quickstart installed =
in a pico
have installed Postman, or some other HTTP app.
It is still correct in its discussion of picos and KRL, but doesn't matc= h the current developer UI
A pico (quoting from Rebuild= ing KRL):
is capable of receiving events and queries on Internet channels,
interacts with other picos, even if they're hosted on another instance o= f the evaluator somewhere else on the Internet, and
can call and be called from Internet-hosted APIs.
Picos respond to ev= ents and queries using two different URL formats. Events are sent to a = pico using the Sky Event API. Queries are made using the= Sky Cloud API.
The Sky Event API describes the event API p= attern for a pico including what components are important and how those com= ponents are encoded in an HTTP method (GET or POST). The specific API for a= given pico, however, depends on which rulesets are installed since it is r= ules that respond to events. Similarly, the Sky Cloud API defines the patterns for queries that a pico understands. Again this is a= meta-API since the queries that any given pico responds to depend on the&n= bsp;rulesets installed. Queries are thus implementable with different code = than the event processing and, in practice, tend to be much faster.
A pico has multiple URL's that can be used to query its state and send i= t events.
Here is a sample URL that sends an event to a pico like the one you= created in the Pico-Engine Quickst= art:
http://= localhost:3000/sky/event/ckcvuri6r0017conl4siq0q3r/1556/echo/hello
Breaking this URL into components:
"http://
" identifies HTTP as the protocol
"localhost:3000
" is the domain name and port of the pico en=
gine which hosts the pico
"sky/event
" identifies this as an event for the pi=
co
"ckcvuri6r0017conl4siq0q3r
" is one of the pico's event chan=
nel identifiers (ECI) (yours will be different)
"1556
" is an arbitrary string which serves as an event iden=
tifier (EID) and correlation ID. (useful when looking at logs)
"echo
" identifies the domain of the event
"hello
" identifies the type of the event
Here is a sample URL that sends a query to a pico:
http://= localhost:3000/sky/cloud/ckcvuri6r0017conl4siq0q3r/hello_world/hello?obj=3D= Bob
This URL shares some components with the URL above for an event. The new= components are:
"sky/cloud
" identifies this as a query for the pic=
o
"ckcvuri6r0017conl4siq0q3r
" is one of the pico's event chan=
nel identifiers (ECI) (yours will be different)
"hello_world
" is the ruleset identifier or RID
"hello
" is the name of a shared function from that ruleset<=
/p>
"obj
" is the name of an argument that the function expects<=
/p>
"Bob
" is the value for that argument
As you can see in the URLs for events and queries, channels are the way = in which a pico can be identified by the pico engine.
A pico can have multiple channels. Best practice is to create a channel = for each purpose or correspondent. There are several reasons for this:
A single-purpose channel can be revoked without affecting other relation= ships the pico has.
Unique channels prevent correlation by third parties (protecting privacy= )
The main channels, created at the same time as the pico, cannot be repla= ced except by deleting the pico and creating a replacement pico.
<= /li>Each channel has a unique identifier and one or more tags, along with a = policy (which will be discussed in a later lesson).
To create a new channel, visit the "Channels" tab:
Note that the channel identifiers you will see are assigned by = your pico engine, and will differ from the ones seen in these scre= enshots.
Use "hello_world" as a tag, "allow echo:*" as the event policy, "hello_w= orld/*" as the query policy, and click the "Add" button to create a new cha= nnel.
Congratulations! You have created your first channel. You can confirm it= s policy by checking the box beside its identifier.
Note that you are able to delete channels that you create.
Make a note of the event channel identifier (ECI) assigned to your tempo= rary channel. You will be using this "hello_world" channel throughout the r= est of this lesson.
Since a pico is a first-class internet citizen, there are many ways in w= hich you can send it an event. We will look at two of these three ways:
Directly using a URL through a client (ex. a browser, or the `curl` comm= and at the command line)
Using the "Testing" tab of the UI
Using Postman or some other console for RESTful APIs
Construct a URL that identifies your Pico by the channel id that you cre= ated in the previous section. Remember to replace the channel identifier in= the sample URL with the identifier of your "hello_world" channel.= Also, you may choose your own event identifier (rather than use "5", as we= did for this screenshot).
http://= localhost:3000/sky/event/ckcvuri6r0017conl4siq0q3r/5/echo/hello
You can use the URL in any Internet client. For example, you could use i= t in the `curl` command in a Bash shell.
Here we will use a tab in the browser. Place the URL in the location are= a of your browser, and press the Enter key.
The result will look something like this:
Congratulations! You just received your first directive from an event yo= u raised.
Directives allow picos to direct e= vent generators to react a certain way to an event. Our rule's direct= ive just says hello, but it could be used to do more complex things. Direct= ives allow the program's logic to be placed in the rules, not in the end po= ints. Placing logic in the rules provides loose coupling with easier = scaling and maintenance.
When you develop a ruleset, you may choose to use the "Testing" tab of the developer UI which can provide a si=
mple UI for the events and queries of the ruleset. The "Testing" tab works =
by using a map named __testing
which is created automatic=
ally by the compiler when the ruleset is compiled. The tab builds a simple =
form to test each of the queries and events which the map defines (again, a=
s generated by the compiler).
This __testing
name may also be shared, and if you wan=
t to do this you can add it to the "shares" entry in the "meta" block of th=
e ruleset. The "shares" keyword is followed by a comma-separated list of na=
mes defined in the globals section which we wish to use outside of the rule=
set. Any names not mentioned in "shares" are for the private use o=
f the the ruleset and are not exposed outside the ruleset. Edit the "meta" =
block of your ruleset to look like this:
meta = { name "Hello World" description << A first ruleset for the Quickstart >> author "Phil Windley" logging on shares hello, __testing }
Be sure to validate your source code, using one of the techniques in Developer Tips for Pico Engine to e= nsure that you have introduced no syntax errors into your ruleset. Once you= get the "ok", go ahead and push your changes and flush the ruleset in= your pico. (Review the Pico-Engine= Quickstart page if necessary)
Click on the "Testing" tab of the developer UI. Choose the "hello_world"= channel from the ECI dropdown. Notice under your ruleset identifier that t= here are three forms, one for each of the shared functions and the event th= at you defined in your ruleset.
In the next example, the name __testing
is not sha=
red, so there is no button for it in the Testing tab.
Notice the button "echo/hello", representing this event for your ruleset= named "hello_world". Click on this button to send the event.
Notice the directive returned by the event.
You can enter the event URL in postman, and click the Send button. Note the result below:
Create a URL for your Pico which uses your "hello_world" channel identif= ier. It will look something like
http://= localhost:3000/sky/cloud/ckcvuri6r0017conl4siq0q3r/hello_world/hello?obj=3D= Bob
Places where your URL will differ from the one shown above:
you may be using a different domain (other than "localhost")
you may be using a different port (other than "3000")
you will be using a different channel identifier (your ECI will= not be "ckcvuri6r0017conl4siq0q3r")
you may be using a different value for the expected argument na= med "obj" (other than "Bob")
Enter your URL into the location line of your browser, or use it in a `c= url` command in a Bash shell.
You can also enter the value for the argument in the "Testing" tab, in t= he box whose placeholder is "obj", and click on the "hello" button.
Congratulations! You know how to send queries to a pico, either directly= using a URL, or through the "Testing" tab= of the UI, or through Postman.
KRL provides an operator, klog(), which allows = us to print information into the logs. It is an operator, rat= her than a statement, so it is applied to an expression. It prints into the= log the message passed to it as an argument, followed by the value of the = expression on which it operates. Finally, it returns the value of the expre= ssion on which it operates, unchanged. This makes it easy to add to or remo= ve from your KRL code without changing its meaning.
As an example, modify your "hello_world" rule to expect an attribute, named "name" and use it to cus= tomize the message returned in the directive.
Modify your rule so that it looks like this:
rule = hello_world { select when echo hello pre { name =3D event:attr("name").klog("our passed in name: ") } send_directive("say", {"something":"Hello " + name}) }
This is the first use in these lessons of a prelude block wi= thin a rule. This is where we bind values to names which can then be used w= ithin the rule. Here, we obtain the value of the event attribute named "nam= e" and bind that value to a name which is also named "name". We apply the "= klog" operator to that value, which simply passes the value along, but as a= side-effect logs its argument followed by the value.
After making these changes to your ruleset, be sure to validate it, then= push your change, and flush it in your pico.
With these changes, refresh the "Testing" tab and you can now enter a va= lue into the box whose placeholder is "name" before clicking on the "echo/h= ello" button to send the event.
Examine the terminal or console from which you launched your engine. You=
will see the expected message from the klog
operator, which w=
ill look like this (embedded in other logging messages):
{"level= ":50,"time":"2020-07-21T19:28:25.484Z","picoId":"ckc...",...,"msg":"rule se= lected"} {"level":40,"time":"2020-07-21T19:28:25.485Z","picoId":"ckc...",...,"val":"= Bob","msg":"our passed in name: "} {"level":40,"time":"2020-07-21T19:28:25.485Z","picoId":"ckc...",...,"msg":"= fired"}
Click on the "Logging" tab for your pico.
You should see all of the recent transactions for your pico. Click to op= en the ones for the "echo:hello" event and the "hello" query:
Here we see the logging for a query and an event.
Note that since the UI works by sending events and queries to the io.pio= clabs.pico-engine-ui ruleset, you=E2=80=99ll also see those events and quer= ies in the output. If anyone is interested in contributing to the React cod= e that implements the UI to filter the logs, it is open source.
You now know how to send events to, and make queries of, a pico. You hav= e also learned some debugging techniques.
Next, learn about persistence, the "P" of pico, by working through Pico = State Lesson (version 1.0.0).
Do the following:
Create a new channel (pick any tags you like, but set up the same policy= ).
Send an event to your pico using the new channel.
Do you get the same result? Why or why not?
Delete the channel.
Resend the event using the deleted channel.
What happens? Why?
Send the event ecco/hello
to your pico. What do you observe=
? Why?
Add a new rule that selects on echo/monkey
and responds wit=
h "Hello Monkey" unless an event attribute named name
is given=
, in which case it will respond with "Hello " followed by the given name.=
=20
Hint: use the.defaultsTo()
operator. Use the =
.klog()
operator to log the value that is used.
Repeat the previous exercise using the ternary =
conditional instead of the .defaultsTo()
operator.
Do the following:
Write and register a ruleset named track_trips
th=
at contains a rule called process_trips
that respond=
s to the car:new_trip
event with an attribute <=
code>mileage. This rule should return a directive named t=
rip
with the option trip_length
set to t=
he value of the mileage
attribute.
Using the Pico Engine UI, create a pico that represents a car and instal=
l your track_trips
ruleset in it.
Using one of the techniques in Raisi= ng Events, test your ruleset. Try it with and without the mi= leage attribute.
Modify the select statement in the process_trips
=
rule so that it will not fire unless there is a mileage attribute with=
the string value of a positive number. Hint: read the docs for the un=
iversal operator as()
.
Modify the process_trips
rule to raise an event.with domain explicit
and type trip_processed
. The explic=
it event should include any attributes tha=
t were in the car:new_trip
event. Hint: e=
vent:attrs()
returns all attributes of the current event.
In the same ruleset, write a new rule named find_lo=
ng_trips
that selects on the explicit:trip_processed=
event. It should read the mileage
attri=
bute and, if it contains a value greater than the numeric value of the glob=
al variable named long_trip
, raises another explicit even=
t with domain explicit
, type found_long_trip=
code>, and any atrributes passed along. Note, there are multiple ways to ac=
complish this. You can pick any positive value for
long_tr=
ip
that you like.
In the same ruleset, write another rule called trip_fuel_usag=
e
that selects on the explicit:trip_processed
=
event. The rule should use the VIN event attribute as input to the ve=
hicle information function you created in Modules and External APIs Lesson to retrieve information about this =
vehicle. Using the number in the mileage attribute, calculate the gallons o=
f gas used for this trip (assume it was highway). Your rule action should r=
eturn a directive that contains the miles driven and gallons of gas used fo=
r the trip.
Use ruleset logging and debugging tools to convince yourself that your r= ules work.