Introduction
The support for Finch users who are working away from their own workstations is incomplete. Some things work fine, some poorly, some not at all. At the same time, the data structures that handle the existing implementation are already too complicated.
I have in mind revamping the whole Party/Smarts/Conversation organization, as well as the internal data structures (replacing Triples) that represents it. This document represents a partway point. Notice that the party/smarts distinction has become essentially vestigial.
I'm assuming some familiarity with the Triples implementation, but not of the general Party/Smarts model. This discussion includes (or will eventually) both the definition of the static structure of Parties, Smartses, and Conversations, and also the dynamics of these objects (at the "party" level, anyway) when conversations are in progress.
Triples syntax:
<attribute-ref>[<object-ref>]=<value-ref> represents a "make" operation.
e.g., $telephone[$"sturgis.pa"]=<ref party structure>.
<attribute-ref>[<object-ref>] represents the resulting (unique) <value-ref>.
<attribute-ref>[]=<value-ref> represents the resulting <object-ref>.
x|<attribute-ref>[x]=<value-ref>, binds the selectted <object-ref> to x (similarly for value.)
The Party
A party is an object representing a participant in a conversation. The party represents a person or some other producer or consumer of voice or other noises, or a workstation user. There is a party corresponding to each active Etherphone (and another for each "back door" line), each active Finch user, and every available "channel" to services like Bluejay and Parrot (text to speech).
A party is represented internally as a REF, externally as a RefID.ID (a laundered REF). Parties come in several flavors:
$telephone: represents an Etherphone — more accurately, its default owner (the person whose office it's in — knowing just that gives one only limited reason to suspect who its user is though).
$individual: users of Finch and workstation programs. These users have authenticated themselves, so we're more confident they are who they say they are.
$trunk: one per Etherphone back door, ephemerally representing the caller or callee on the other end of the telewall connection.
$service: one per each available connection to each service such as Bluejay (Recording service) or Parrot (Text-to-speech service).
The code for creating parties depends to a small extent on the type of party being created. However, all the code at the "party level" of Thrush is almost entirely type-independent. The party level maintains the truth about the present state of each party in each conversation. It treats all participants about the same. (There is occasionally a distinction made between parties that represent voice terminals — those that can actually transmit and receive voice — and those that do not. Also, based on their structure in the $Poaching hierarchy, they are dealt with differently.) The major differences between various participants in Thrush conversations are represented by the Smarts objects, described below.
Identification
Individuals and telephone owners are identified by their Grapevine RNames, trunks by the RNames of the owners of the associated Etherphones. Services are similarly identified by RNames describing them (Text-to-Speech.Lark, Recording.Lark) in the Lark registry.
$telephone and $individual
When an Etherphone registers with the system, its owner's name, extracted from the GV database, is assigned to the $telephone party that is permanently associated with that Etherphone (rather with its smarts, see below). If later some user runs Finch on the adjacent workstation, or (unimplemented) provides an identity and password directly to the Etherphone, an $individual, authenticated party is created to represent the authenticated version of that individual. We will later see how the $Poaching attribute connects an $individual party to the appropriate $telephone party.
Access to Parties
Triples are used to link names and parties:
$telephone[$"Somebody.pa"] = <assumed individual's telephone party>
$individual[$"Somebody.pa"] = <authenticated individual's party>
$service[$"Service.lark"] = <associated service party>
$trunk[$"Somebody.pa"] =
<trunk party assoc. with Etherphone connected to Somebody's line>
GetParty and similar functions in the ThParty interface can be asked to look for first $individual, then $telephone, returning the first party handle and authentication/type information found. This allows the authenticated party to be found (below) first, if there is one. We'll see why this is desirable. When the called party is not a service or other Etherphone user, the GetParty functions fetch and condition an appropriate $trunk party to represent the other end of a conversation.
Visiting
We want it to be possible to sign-in at the workstation of someone who is running Finch when visiting that person — without taking the workstation away from its owner or current poacher. Such a user is called a visitor. Here is how that connection is made:
Access from visitor to person visited:
$Visiting[<$individual or $telephone party of visitor>] = <$individual visited party>
The visitee has to be the poacher for the given office. Visiting is not at present defined in an office where Finch or the equivalent is not running, so there will always be a poacher. The idea is that calls will now come to the visited location. But by connecting authenticated parties together, there is access to the $Smarts links to the visitor's own Finch as well as the one in the visited location (for reporting call progress — the idea is that both Finches record it so that the visitor gets a permanent record. Eventually it should be possible to make both phones ring in that situation, too. The algorithms described here don't support that.)
People will invariably forget to unvisit, so there needs to be (1) a way for the visited party to unvisit, preferably while a call is coming in and in such a way that the ringing is transferred back to the visitor's office; (2) for visiting to be revoked if the visitor issues commands to the home Finch. (1) may be difficult to get completely right this time around. (2) is up to Finch, using the Party functions to be described below.
I'm now convinced that this design is flawed. In order to do all that we want for visiting, we need the option of ringing both the visitor's own location (poaching or home) and the visited location, and for informing both Finches. The call should be answerable in either location. This involves putting both the visitor and visitee parties in the conversation explicitly, perhaps with some attributes that makes it clear what's going on. Similar to what has to happen when a call is forwarded/extended to a secretary! These decisions should almost certainly not be made at the party level. Maybe poaching shouldn't either — Finch and its telephone should be explicitly placed in the conversation from somewhere at the smarts level? Not so sure about that. I'm going to design and build a simple version of visiting done this way by the party level — by toying with some parameterizations having to do with ringing (when one callee answers, the other keeps ringing) we can test out conferencing in Thrush for the first time!! In the mean time, ignore/skim anything below having to do with visiting.
Access algorithms
Initializations
Registering a Lark
Given machine name, obtain RName and service designation, extract RName
NamesGV.GetAttribute[machine name as rope, $owner] yields "Swinehart.pa.lark"
Connections below should be checked, then made. Must beef up Thrush.MakeUnique to permit (a) insisting that any existing connections are the same as those being suggested, and/or (b) detecting when they're not, and calling finalization-type stuff to undo previous values.
$telephone[<rName>] = <new party>
$Smarts[<new party>] = <new smarts>, representing Lark
$SmartsForHost[$"173#110#telephone"] = <new smarts>
$trunk[<rName>] = <new trunk party>
$Smarts[<new trunk party>] = <new trunk smarts>
$SmartsForHost[$"173#110#trunk"] = <new trunk smarts>
(Clear and ?) recreate poacher designation for new assumed party (see below)
Registering a Finch
Given workstation host name, registration is similar to that for Lark, except for Poacher/visitor stuff.
Register, more or less as above, using $individual for $telephone.
(Clear and ?) recreate poacher designation from authenticated party (see below)
It's legal and reasonable to have a Finch running with no associated Etherphone, or to run Finch next to an Etherphone when you yourself don't own one.
Determining poacher designation, when a Lark registers.
Have decided that poacher or poachee should be explicitly designated because progress-distribution will have to follow this chain which, as can be seen here, is somewhat elaborate.
Given new assumed party, <rName> ← $telephone
-1[<$telephone party>]
If this isn't found, it's a serious problem, since we presumably just created this link. If anything below yields NIL, clear any existing Poacher designations and stop, because clearly there just ain't one.
<workstation address> ← NamesGV.GetAttribute[<rName>.Lark, $workstationhost]
This is a new entry, the inverse of the owner field in a workstation entry, represented as a rope for the host name, like 3#30#
<poaching smarts> ← $SmartsForHost[<workstation address>individual]
E.g., $"3#333#individual"
<poaching $individual party> ← $Smarts-1[<poaching smarts>]
erase $Poaching[<any party>] = <$telephone party>
erase $Poaching[<poaching $individual party>] = <any party>
$Poaching[<poaching $individual party>] = <$telephone party>
This can be done at frequent intervals, for no reason at all or whenever anything else changes, since the idea is to have the explicit link available for high-frequency activities.
Determining poacher designation, when a Finch registers.
On failure to find something that would be optional, quit, but be sure than any existing Poacher designation is removed, first.
<$telephone party's RName> ← NamesGV.GetAttribute[<workstation host>.lark, $owner]
<$telephone party> ← $telephone[<$telephone party's RName>]
set up poaching designations by calling on the previous algorithm.
Again, can do at paranoically-frequent intervals
Setting up a Visitors arrangement, from visited Finch
Accept and authenticate (?) rname of visiting user.
Clear any existing visitor designations for visiting user.
If visitee is self, quit (can use this to cancel visiting).
visited party ← $individual[rname for logged-in user]
Error if it's not there; this operation presupposes that Finch or equiv. is running.
visitor party ← $individual[visitor's rname], or else $telephone[visitor's rname]
Quit if it's not there.
$Visiting[visitor party] = visited party
Visiting can be cancelled explicitly, but should definitely be cancelled when the visitor's own machine leaves idle, or the visitor's machine can determine somehow that the visitor has returned home. Ideally, both visitor's and the visited party's phone would ring in a visiting situation, which would ameliorate the problem of a visiting designation overstaying its welcome, but that one's still hard to manage.
Call-placement
GetTrunk: Locating a trunk party to which a given party should place outgoing calls.
Follow $individual-1 or $telephone-1 or $trunk-1 (ERROR!) back to RName, then $trunk forward to trunk party. This gets the trunk in the party's own office.
If none found, try again by following $Visiting or $Poaching (once), then repeating. This gets the trunk in the office where the party is.
This doesn't properly deal with the case where the first-located trunk is busy and you'd like to select the next one. Almost needs smarts-participation at the GetParty level to do this right (what does busy mean?), which we don't have yet.
GetCaller: Given a smarts, locate the right party to use for initiating calls
Discussion: If a call is initiated from the handset and there is a poacher, the poaching party is the one that will officially be in the conversation. The poachee will be informed of progress as the call progress (see ThPartyOpsImpl).
<own party> ← $Smarts
-1[<given smarts>], default party for this smarts
If this fails, deal with the error: who's making this call, anyhow?
<own party> ← $Poaching-1[<own party>], if such exists.
GetCalled: Locating the called party, given the trunk smarts, when the phone rings
<trunk party> ← $Smarts-1[<trunk smarts>]
Follow $individual-1 (ERROR!) or $telephone-1 (ERROR!) or $trunk-1 back to RName, then $individual or (failing that) $telephone forward. Error (but essentially ignore and allow to ring or transfer normally) if none found.
Distribution algorithm
This is the heart of the "party level" of the code. There's a <conversation> object, which is connected to all of the parties in the conversation, and which remembers for each party the state that party is in with respect to this conversation. When state changes occur, reports are made either to all of the smarts associated with the party that makes the change (except the initiating smarts, which gets the report as a return parameter from the state-change call), or to all smarts associated with all the parties in the call (for events that would be of interest, like going-active, ringing, or going-idle. The initiating smarts decides, but had better decide right. See Thrush.mesa, ThParty.mesa, ThPartyOpsImpl.mesa, LarkSmarts*Impl.mesa.
Given a party and the need to report a state change:
If there's a $Visiting link,
If there's a $Smarts link and the smarts's role is as a $controller not a $voiceTerminal,
report progress to the (manager) smarts.
party ← $Visiting[party]
If there's a $Poaching link,
If there's a $Smarts link, report progress to the (manager) smarts.
party ← $Poaching[party]
If there's a $Smarts link, report progress to the (voice) smarts.
If no $voiceTerminal smarts has been located, report progress to one's own trunk party!
Using one's own trunk party allows calling Finch users who don't have an adjacent Etherphone -- through the back door. Their Finches should properly report the call attempts. Have to make sure this doesn't happen when reporting to the originating party, though, so be careful. I don't know if this will work. This must be fleshed out — not yet implemented and I don't understand it.
Placing a call from an active Lark to another Lark user (a smarts does this)
These aren't correct yet — there's some real silly stuff in here. I'm going to
retrofit these algorithms from the real code, which is largely easier for me to deal
with than this stuff.
Locate own (caller) party, using GetCaller.
If rname of own party = callee's rname, give up as calling self? (ThParty should check)
<called party> ← $individual[<callee's rname>] or else $telephone[<callee's rname>]
Failing that, proceed to back door call behavior (below).
ThParty.Alert[caller, called, ....] to begin the process. During progress reports, use the distribution algorithm to reach the proper smartses.
Calls to user whose phone one is poaching will be rejected via standard busy procedure (busy because we have it off-hook so to speak), rather than the identity check.
Placing a call from an active Lark to a non-Lark user (smarts does this)
Get here with a name known not to be a Lark-user's name, or a phone number known not to identify an active Lark.
Locate own (caller) party.
Look up name in white pages to get number. Keep both name (description) and number for Alert.
<called party> ← trunk party for own party, using GetTrunk.
ThParty.Alert[caller, called, ....] to begin the process. During progress reports, distribution is simple.
A Lark's back door rings (trunk smarts handles this)
<caller party> is trunk party for ringing <trunk smarts>
callee is own party corresponding to trunk party, via GetCalled. Error if none.
ThParty.Alert[caller, called, ....] to begin the process. Distribution is as in the front-door case.
It is implicit that calls cannot forward outside the Etherphone system; extension of this design to handle that seems possible. Requires the ability to locate idle trunks to use when one's own is already in use.
Problem, notes
The scheme for using one's own back door if the called party exists but doesn't have a voice terminal (e.g., is Running Finch but has no adjacent Lark or it's off) requires that the called-party's phone number be available. At present that's the case only if it's know from the start that the call will go outside. We'll have to arrange to make it available at a later time, either dynamically or by getting it originally and passing it along.
At present, when a call is placed by number from an Etherphone, a Leap triple is sought linking the number to the party owning that number. Probably should just use the white pages B-Tree directly instead. This is in line with removing as many of the explicitly-expressed relationships among objects as possible, computing the rest. That's not done at present because there's no index from phone numbers to names. When we install Loganberry, we'll have that. I was wrong, Doug; there is a reason for that index! This will let us indicate who's being called by number in many cases!