Subject: Minor redesign of Thrush to handle poachers and visitors To: (VoiceProject^) Cc: swinehart 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: []= represents a "make" operation. e.g., $telephone[$"sturgis.pa"]=. [] represents the resulting (unique) . []= represents the resulting . x|[x]=, binds the selectted to x (similarly for value.) Definitions Owner: the individual permanently-assigned to an Etherphone or workstation. Poacher: a logged-in user of a workstation; its owner or otherwise. Visitor: a user but not owner of an Etherphone, whose location is known to the system (by locator or explicit "x is visiting" command.) 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"] = $individual[$"Somebody.pa"] = $service[$"Service.lark"] = $trunk[$"Somebody.pa"] = 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. The Smarts Corresponding to every party there is a smarts. A smarts represents the interface between a user or service and the Etherphone system. A smarts object includes operations that are specific to the type of voice terminal or control program being represented. Examples: LarkSmarts representing a specific Etherphone's keypad, switchhook, and voice transmission faciliites, LarkTrunkSmarts representing back door signalling and transmission activities, FinchSmarts representing the workstation operations, BluejaySmarts representing the recording service. Smarts and Parties should probably now be merged; they were not always 1-1. Access to smarts Triples are used to link smarts and parties, smarts and the hosts they run on: $Smarts[] = $SmartsForHost[< host/type address as atom like $"173#155#trunk" >] = In the server, the smarts object itself includes a role  voiceTerminal or controller (meaning not-voiceTerminal)  and a field containing the net and host addresses for the host (Etherphone, workstation, or server) associated with the smarts. $The smarts objects in these triples live in the server and describe the smarts to the party level (including a ThSmarts procedural interface record). Their RefID.ID representations are provided to the actual smarts implementations, which must associate their own smarts-dependent data with these IDs. $SmartsForHost includes a party type value in the host number to distinguish between the smarts for the front door ($"173#155#telephone") and back door ($"173#155#trunk") smartses. Yet to be settled is how multiple channels representing a service smarts should be identified. Poaching Someone who uses a workstation adjacent to an active Etherphone is called a poacher (one can, and usually does, poach on one's own Etherphone). Given the information that we maintain in the Grapevine database about each machine, we could compute this relationship each time using only the triple structures described above, but we instead add additional structure to represent it, for efficiency reasons. Informally, when one runs Finch or re-boots an Etherphone, the following connection is made: Access from poacher to poachee $Poaching[] = This connection will be broken and remade as parties come and go and as users move around. 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 $telephone[] = $Smarts[] = , representing Lark $SmartsForHost[$"173#110#telephone"] = $trunk[] = $Smarts[] = $SmartsForHost[$"173#110#trunk"] = (Clear and ?) recreate poacher designation for new assumed party (see below) Registering a Finch Register, more or less as above, using $individual for $telephone. (Clear and ?) recreate poacher designation from authenticated party (see below) Determining poacher designation, when a Lark registers. Given new assumed party, _ $telephone-1[<$telephone party>] _ NamesGV.GetAttribute[.Lark, $workstationhost] _ $SmartsForHost[individual] _ $Smarts-1[] erase $Poaching[] = <$telephone party> erase $Poaching[] = $Poaching[] = <$telephone party> Determining poacher designation, when a Finch registers. <$telephone party's RName> _ NamesGV.GetAttribute[.lark, $owner] <$telephone party> _ $telephone[<$telephone party's RName>] set up poaching designations by calling on the previous algorithm. 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] visitor party _ $individual[visitor's rname], or else $telephone[visitor's rname] Quit if it's not there. $Visiting[visitor party] = visited party 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. 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). _ $Smarts-1[], default party for this smarts _ $Poaching-1[], if such exists. GetCalled: Locating the called party, given the trunk smarts, when the phone rings _ $Smarts-1[] 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 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. 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) _ $individual[] or else $telephone[] 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. Placing a call from an active Lark to a non-Lark user (smarts does this) Locate own (caller) party. Look up name in white pages to get number. Keep both name (description) and number for Alert. _ 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) is trunk party for ringing 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. 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! Real Algorithms, paraphrased from the real system, then extended. Error tests and returns are suppressed. Mostly, they're nb's that say what happened, and usually what happened was that something wasn't at the end of a suggested link. ThPartyInitImpl GetParty[party, rName, type] => [nb, newParty]: newParty _ SELECT type FROM NIL => try $individual, then $telephone; $trunk => error; -- you don't get them this way. $individual => If $individual[rName]=party THEN try $telephone, else go on to common case. $telephone, $individual => $telephone[rName] or $individual[rName]; [If the result has no outgoing field, look up phone number in the white pages and record in outgoing. This is actually done by CreateParty, at no extra charge.] If there's no party, look up phone number in white pages and GetPartyFromNumber[party, phoneNumber, rName, TRUE] SetPoaching[newParty, party] $service => $service[recording.lark], and also $service[partyName.pa.recording]=newParty GetPartyFromFeepNum[party, feepNum] => [nb, newParty]: [rName, officeNumber] _ WhitePagesLookup[feepNum]; newParty _ GetParty[party, rName, NIL, TRUE]; GetPartyFromNumber[party, phoneNumber, description, trunkReqd] => [nb, newParty]: IF ~trunkReqd [num, isExt] _ ThNet.HowToDial[phoneNumber, (e.g., "4473")]; If isExt and there is an rName|$Extension[rName]=$4473, return GetParty[party, rName, NIL, TRUE]  Etherphone result! newPartyID _ $trunk[], failing if there isn't one or the trunk is already involved in something or it isn't enabled yet. If we still don't have one, do the same thing for $Poaching[] if there is one. Store the phoneNumber, reserving partyID, and description in the resulting party info. GetCurrentParty[smarts] => [nb, party]: party _ $Smarts[]=smarts, (do SetPoaching[party],) then $Poaching[]=party if there is one. SetPoaching[party, callingParty]: SELECT party.type FROM $individual => poacher _ party; rName _ Lookup GV $owner attribute of ( $SmartsForHost[]=($Smarts[party]).lark ) poachee _ $telephone[rName], return if found. poachee _ GetPartyFromNumber[callingParty, party.outgoing, rName, TRUE] $telephone => poachee _ party; workstation _ ( Lookup GV $workstationhost of (.lark) )individual poacher _ $Smarts[]=($SmartsForHost[workstation]); Erase old $Poaching connections for both, then $Poaching[poacher]=poachee Ü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. Given workstation host name, registration is similar to that for Lark, except for Poacher/visitor stuff. 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. 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. 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. 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# E.g., $"3#333#individual" 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. On failure to find something that would be optional, quit, but be sure than any existing Poacher designation is removed, first. Again, can do at paranoically-frequent intervals Error if it's not there; this operation presupposes that Finch or equiv. is running. 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. 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. If this fails, deal with the error: who's making this call, anyhow? This is the heart of the "party level" of the code. There's a 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. 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. 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. 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. 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. We want the trunk belonging most completely to partyID (the caller of GetParty...). So assume this is an $individual (Finch) party, find the trunk belonging to it. That would get the trunk connected to the adjacent Etherphone. Who are we representing if we initiate a call? When things aren't found, just quit unless otherwise indicated; poaching is optional. No voice path for this individual. Try to create 1 using calling party's trunk. Worried about recursion here, later on when extensions done right. E.g., #3#30#individual Ê8˜JšÐbsœ:˜AJšœ˜šœ ˜ head˜ Iblock˜ýL˜‘L˜×˜code˜HM˜6—M˜MM˜DM˜^——˜ LšœK˜KLšœD˜DLšœˆ˜ˆ—˜ LšœÏeœü˜„˜yIindent˜ÀN˜™N˜€N˜ƒ—Lšœ­žœ˜Í˜Lšœ…˜…—˜Lš œsž œLžœžž œ žœgž œM˜Å—˜šœ+˜+LšœC˜CLšœ@˜@Lšœ6˜6šœ˜LšœA˜A——Lšœé˜é——˜ Lšœ(žœ žœ¼˜ö˜˜NL˜)L˜N—Lšœ3ž#œ˜óL˜®LšœµÏta˜–——˜LšœLžœ ˜ó˜Lšœ1˜1L˜Z——šœ˜Lšœƒ˜ƒšœ&˜&LšœU˜U—Lšœ–Ïsû˜‘Lšœ¶˜¶LšŸÔ˜Ô—˜KšÏb˜˜˜GM™M—M™M˜!M˜6Mšœ3˜3M˜#M˜/Mšœ5˜5M˜L—˜M™hM˜BM˜OM™——˜7M™´šœ-Ïuœ˜CM™Ï—šœL˜LMšœ™—šœC˜CM™—Mšœ&¢œ˜;Mšœ1˜1Mšœ;˜;Mšœ<˜J˜VJ˜—š£œ ¤œ ˜'J™.JšœZ˜ZJ˜—š£ œ˜!J™Uš¤œ ¤˜˜Jšœ¤œ¡œ(¡˜QJšœ-˜-JšŸP™PšœB¤œ˜GJšœB™B——˜š œ ¡œ¤œ¡œ¡œ ˜PJ™—Jšœ2˜2——J˜IJ˜—J˜————…—H_0