In the previous post, we described pico channels. When you have the identifier of one of a pico's channels, you can use it to send events and queries to that pico.
When a pair of picos need to work together, each needs an ECI (event channel identifier) to the other. The bundle of that pair of ECIs reifies a relationship shared by the two picos. Each pico has its own copy of the bundle.
Historically, up to and including version 1 of the pico engine, such a relationship has been called a "subscription". Recently, we decided to use the term "relationship" instead, since subscription has connotations of one publisher and many subscribers, and here we're talking about pairwise relationships.
The io.picolabs.subscription
ruleset
Saying that "each pico has its own copy of" something implies that it is maintaining it as state information or data. The only way a pico can do that is by using an entity variable in a ruleset (see the earlier post on "Picos and persistent state").
The ruleset used to hold information about a pico's relationships is io.picolabs.subscription
which is installed in every pico. This means that pairwise relationships between picos is something fundamental, built in to every pico.
With this ruleset, a pico has events to propose, accept, decline, and use established relationships.
A subscription
The bundle* of information that defines a relationship (currently called a subscription) consists of:
- An identifier (named
Id
) - This pico's role in the relationship (named
Rx_role
) - The other pico's role (named
Tx_role
) - This pico's Event Channel Identifier (ECI) (named
Rx
) - The other pico's ECI (named
Tx
) - The channel tags (part of each channel's definition, and not part of the bundle)
This information is held by both picos. The identifier is the same in both of them, as are the channel tags. But the roles and ECIs are reversed as makes sense for each pico.
Proposing a relationship
This is done** by a pico raising the wrangler:subscription
event, with the following attributes:
wellKnown_Tx
an ECI for the other pico (often itswellKnown_Rx
)Rx_role
a string describing this pico's role in the proposed relationshipTx_role
a string describing the other party's rolename
a string naming the relationshipchannel_type
a string giving the type of relationship
The last two items can be chosen by the KRL programmer. They become the tags
of the channels created for the relationship in both picos.
A rule in the io.picolabs.subscription
ruleset reacts to this event, assigns a new identifier, creates a new channel for the pico, and sends an event to the other pico to have it do its part.
At this point, the first pico has the bundle of information in its internal array of outbound subscription requests, and the other pico has the same bundle of information (with roles and channels reversed) in its internal array of inbound subscription requests.
Accepting (or declining) a proposed relationship
Now the ball is in the other pico's court. It can accept*** by sending the wrangler:pending_subscription_approval
event (or decline by sending a wrangler:inbound_rejection
event) to the first pico.
Until the other sends one of these events, the relationship will remain in the proposed state.
Established relationships
Once a proposed subscription has been accepted by the other pico, it is said to be "established" and both picos can use it. The list (an array) of established subscriptions can be obtained in this manner:
meta {
use module io.picolabs.subscription alias subs
}
...
subs:established() // all established bundles for this pico
...
Using established relationships
In a system with a large number of picos, each will generally have many relationships.
For example, let's assume that one pico serves as a "controller" and wants to send an update event to all of the other picos it controls. That could be accomplished by a ruleset like this one:
ruleset control_something {
meta {
use module io.picolabs.subscription alias subs
}
...
rule broadcast_update {
select when control_something update_required
foreach subs:established("Rx_role","controller") setting(s)
event:send({
"eci": s{"Tx"},
"domain": "controlled",
"type": "update_required",
"attrs": event:attrs
})
}
}
When the controlling pico receives the control_something:update_required
event, it will obtain a list of the established subscriptions in which it plays the "controller" role, and forward the incoming attributes in a controlled:update_required
event to each of the other picos in those relationships.
Notes
"one publisher and many subscribers" yet it looks like the example at the end is very much like that, with one controller and many controlled. But each relationship is represented, pairwise, by a subscription. When a controlled pico contacts the controller, it does so using a channel just for it (from its point of view, the Tx
ECI), which will be different for each controlled pico.
* Inside the subscription ruleset, this bundle is called a "bus".
** The complete lifecycle is documented here, so this is just an overview. Where this post or that document differ from the subscription ruleset itself, the ruleset prevails.
*** A way of automatically accepting offered subscriptions is discussed in this document.
No comments:
Post a Comment