Redefinitions, Obligatory Concrete, simple types
PartyHandle: TYPE = Thrush.PartyHandle;
ConversationHandle: TYPE = Thrush.ConversationHandle;
SmartsHandle: TYPE = Thrush.SmartsHandle;
nullHandle: Thrush.ThHandle = Thrush.nullHandle;
AlertKind: TYPE = Thrush.AlertKind;
CallUrgency: TYPE = Thrush.CallUrgency;
ConvEventBody: TYPE = Thrush.ConvEventBody;
Credentials: TYPE = Thrush.Credentials;
IntervalSpecBody: TYPE = Thrush.IntervalSpecBody;
NB: TYPE = Thrush.NB;
Reason: TYPE = Thrush.Reason;
Rname: TYPE = Thrush.Rname;
ROPE: TYPE = Thrush.ROPE;
SHHH:
TYPE = Thrush.
SHHH;
none: SHHH = Thrush.unencrypted;
SmartsInterface: TYPE = ThSmarts.SmartsInterface;
StateID: TYPE = Thrush.StateID;
StateInConv: TYPE = Thrush.StateInConv;
convClassMask: CARDINAL = 377B;
Conversation Management
A Smarts must relay all call-placement requests through a Party it is associated with. This is because we don't trust all Smarts as much as we trust all Parties; the Parties should validate the requests.
Alert can either perform a feasibility study, or an actual attempt to connect. In either case, it provokes an answer indicating how likely it is that the connection would (will) be made. To perform the feasibility study, the alertKind parameter should be queryOnly (the conversation argument need not be supplied for this call.) This allows the calling client to determine whether the callee is busy, filtered out, missing in action, etc., before attempting to make the call. It also allows the calling client to obtain this information for some reason other than to place the call. If the call fails, perhaps it will succeed if the priority is increased.
When the call has been accepted or rejected, the result will be reported back to the caller of Alert via a call to ThSmarts.Progress. Alert the end client or user associated with EACH "PENDING" PARTY of the attempted connection.
Alert[..., calledPartyID: nullHandle, ... state: reserved, ...] reserves presence in conv. without attempting a connection (state reserved).
Alert:
PROC[
shhh: SHHH ← none,
credentials: Credentials,
state: StateInConv←initiating, -- IF reserved, reason specifies reason for cancelling prev.
reason: Reason←wontSay,
calledPartyID: PartyHandle←nullHandle,
urgency: CallUrgency←normal,
alertKind: AlertKind←standard,
newConv: BOOL←FALSE,
comment: ROPE←NIL
] RETURNS [nb: NB, convID: ConversationHandle ];
Called by Smarts to progress through a call; answer, indicate "ringing", reject, "hold", . . .
Advance:
PROC[
shhh: SHHH ← none,
credentials: Credentials,
state: StateInConv,
reason: Reason←wontSay,
comment: ROPE←NIL
] RETURNS [nb: NB];
The Party-owning Smarts calls Disconnect to terminate the Party's participation in this conversation, voluntarily. On return, the Party is probably no longer in the conversation. The comments may be useful to indicate why one is calling things off. <<May need to time out if no ConversationStatus arrives.>>
Disconnect is really "Advance[terminating]".
Disconnect:
PROC[
shhh: SHHH ← none,
credentials: Credentials,
comment: ROPE←NIL
] RETURNS [nb: NB] = INLINE {
RETURN[Advance[shhh, credentials, idle, terminating, comment]]};
Conversations
Create a conversation with nobody in it, state empty or something. Scavengers may reclaim it after a time if no Party joins it. conv, stateID fields of Credentials are ignored.
CreateConversation:
PROC[
shhh: SHHH ← none,
credentials: Credentials,
urgency: CallUrgency←normal,
alertKind: AlertKind←standard
] RETURNS [ nb: NB, convID: ConversationHandle ];
SameConvClass:
PROC[convID1: ConversationHandle, convID2: ConversationHandle]
RETURNS [sameClass: BOOL] = INLINE {
ln1: Basics.LongNumber=LOOPHOLE[convID1];
ln2: Basics.LongNumber=LOOPHOLE[convID2];
IF ln1.lowbits#ln2.lowbits THEN RETURN[FALSE];
RETURN[
Basics.BITAND[ln1.highbits,convClassMask]=Basics.BITAND[ln2.highbits,convClassMask]];
};
MergeConversations:
PROC[
shhh: SHHH ← none,
credentials: Credentials, -- of surviving conversation
otherStateID: StateID, -- of dissolving conversation
otherConvID: ConversationHandle
] RETURNS [ nb: NB ];
SetSubject: PROC[ shh: SHHH←none, convID: ConversationHandle, subject: ROPE ];
GetSubject: PROC[ shh: SHHH←none, convID: ConversationHandle ] RETURNS [subject: ROPE];
GetHistory: PROC[ -- No room, no room; for now
shhh: SHHH ← none,
credentials: Credentials,
firstState: StateID𡤁,
lastState: StateID ← 0 -- default: get latest
] RETURNS [ nb: NB, events: Thrush.EventSequence ]; -- if successful, will trigger new Progress reports for this range.
OtherParty:
PROC[
shhh: SHHH←none,
credentials: Credentials
] RETURNS[ nb: NB, partyID: PartyHandle, description: Thrush.ROPE, conference: BOOL ];
Produces ID of one other party, + description. If # parties > 2, description is best efforts description of who's in it with credentials.partyID, conference is TRUE.
DescribeParty:
PROC[
shh: SHHH←none,
partyID: PartyHandle]
RETURNS[ description: Thrush.ROPE
];
Like GetRName.... but produces best available user-oriented description.
Party Initialization
The PartyHandle is used by local or remote Smarts and other Parties to invoke the Party's functions. The Party is the dynamic representative of its Rname; most of the system functions traffic in PartyHandles. Clients should convert from Rname to Party as early as possible.
Returns NIL if no valid name found.
Owning Smarts identify (and authenticate) themselves to Parties using Register*[]. That's where the password action is. GetParty assumes that a Party for the specified party already exists, returning NIL if none can be found. The self argument is the caller's own Party; it's used to obtain the corresponding trunk, or "back door", Party when the callee must be reached via the public network.
CreateParty also returns an existing Party if there is one. If there isn't, it creates one instead, as long as the Rname is valid. This is for use by the Smarts registration procedures.
CreateParty:
PROC[
shh: SHHH←none, rName: Rname←NIL, type: Thrush.PartyType
] RETURNS [partyID: PartyHandle];
GetParty:
PROC[
shh: SHHH←none, partyID: PartyHandle, rName: Rname←NIL
] RETURNS [newPartyID: PartyHandle];
Raises Thrush.ServerError if fatal confusion arises.
For other troubles ~~ no such name, no network response ~~ returns NIL
<< Can change this if clients could use additional information. >>
Obtains an idle Jay Party, assigns it to self. << It is not at all clear how this will be set back to idle again, using current implementation. >>
GetJayParty:
PROC[
shh: SHHH←none, partyID: PartyHandle
] RETURNS [newPartyID: PartyHandle];
phoneNumber is a valid extension or outside telephone number. Description is probably a name, but may not be an RName; we can use it to help the user, but not to look up anything.
Unless trunkOK, number must be an internal extension representing an Etherphone user.
GetPartyFromNumber:
PROC[
shh: SHHH←none,
partyID: PartyHandle,
phoneNumber: Thrush.ROPE←NIL, description: ROPE←NIL, trunkOK: BOOL←TRUE
] RETURNS [newPartyID: PartyHandle];
FeepNum is a string of the form "*nnnnn", where "nnnnn" is a prefix of some rName, mapped with information loss into the corresponding DTMF buttons. The prefix should be long enough to be guaranteed unique within the system. This isn't at all good yet.
GetPartyFromFeepNum:
PROC[
shh: SHHH←none,
partyID: PartyHandle,
feepNum: Thrush.ROPE←NIL
] RETURNS [newPartyID: PartyHandle];
If party is a trunk party, with number and descriptive identification, get rid of same.
No-op if party is involved in ANY conversations.
ReleaseTrunkParty: PROC[shh: SHHH←none, partyID: PartyHandle];
Return the Rname for a given fone
GetRname:
PROC[shh:
SHHH←none, partyID: PartyHandle]
RETURNS [Rname: Thrush.Rname];
A Smarts uses one of these Register* functions to identify itself as the owner (implementor) of the specified Party, and to supply the Party with necessary attribute information. If a password is provided (and is OK), the smarts will be an authenticated smarts. This will carry additional privileges. At present, the attributes comprise a role that will determine the order in which multiple Smarts for a Party will be polled, when polling is necessary. The idea is that first workstations, then Larks, will get the oportunity to deal with each action. When the role is "voiceTerminal", the attributes also include a Thrush.Machine value identifying the machine that can carry out a voice conversation.
Since there can be more than one Smarts implementation per Party, the Smarts must also supply its interface as an RPC-style interface record.
A Thrush-based Smarts uses RegisterLocal to supply the interface record directly (type LocalSmartsInterface). A remote Smarts uses Register to supply an RPC InterfaceName that can be used to import the proper implementation (type SmartsInterface).
A Smarts re-registers when it wants to supply a new/different password, when the Lark or workstation it represents is reinitialized, or at any other time it chooses to. When re-registering, a Smarts should include its current handle, but be willing to believe the returned one if it's different. This is the Party-level analog of the LarkSmarts.Register[] approach to resiliency.
Register:
PROC[shh:
SHHH←none, partyID: PartyHandle, interface: SmartsInterface,
properties: ThSmarts.SmartsProperties, oldSmartsID: SmartsHandle←nullHandle]
RETURNS [smartsID: SmartsHandle];
RegisterClone:
PROC[shh:
SHHH←none, partyID: PartyHandle,
clonePartyID: PartyHandle, oldSmartsID: SmartsHandle←nullHandle]
RETURNS [smartsID: SmartsHandle];
Deregister: PROC[shh: SHHH←none, smartsID: SmartsHandle];
Enable:
PROC[shh:
SHHH←none, smartsID: SmartsHandle]
RETURNS [nb: Thrush.NB];
nb=success unless something went wrong. Any parties connected to this smarts will now be available to GetParty; this smarts is an active voice smarts.
Disable:
PROC[shh:
SHHH←none, smartsID: SmartsHandle]
RETURNS [nb: Thrush.NB];
This smarts can no longer supply a voice connection to its parties.
GetNumbersForRName:
PROC[shh:
SHHH←none, rName:
ROPE]
RETURNS [fullRName: ROPE, number: ROPE, homeNumber: ROPE];
fullRName is NIL if nothing found.
SetRingEnable: PROC[shh: SHHH←none, partyID: PartyHandle, ringEnable: Thrush.RingEnable←noChange, ringInterval: INT𡤀, update: BOOL←FALSE];
}.