ThPartyInitImpl.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Last modified by D. Swinehart, March 5, 1985 6:42:41 pm PST
DIRECTORY
Atom USING [ GetPName, MakeAtom ],
BasicTime USING [ Now, Update ],
Commander USING [ CommandProc, Register ],
CommandToolExtras USING [ NextArgument ],
ExplicitExport USING [ ExportExplicitly ],
IO,
LarkPlay USING [ PlayString, Tone, ToneSpec, ToneSpecRec ],
Log USING [ FindWhere, Problem, ProblemHandle, RegisterWhereToReport, Report, ReportFR, WhereProc ],
LupineRuntime USING [ BindingError ],
Names USING [ CmdOrToken, CurrentPasskey, InstanceFromNetAddress, MakeAtom, Registrize ],
NamesGV USING [ GVGetAttribute, GVSetAttribute, GVUpdate ],
NamesRPC USING [ StartConversation ],
Nice,
Rope USING [ Concat, Cat, Fetch ],
RPC USING [ AuthenticateFailed, EncryptionKey, ExportFailed, ImportFailed ],
SafeStorage USING [ GetCanonicalType, Type ],
Thrush USING [
AlertKind, CallUrgency, ConversationHandle, Enhandle, HandleFault, KillHandle, NB, PartyHandle, PartyType, H, nullHandle, Machine, MakeUnique, pERROR, PhoneNumber, Priorities, RingEnable, Rname, ROPE, SHHH, SmartsHandle, TBD, ThHandle, unencrypted ],
ThNet USING [ CloseWhitePagesDatabase, FeepName, HowToDial, InitWhitePagesDatabase, List, NewListing, pd, WhitePagesEnter, WhitePagesEntry, WPListing, WPState ],
ThParty,
ThPartyPrivate USING [ CFRef, DehandleParty, DehandleSmarts, DoAdvance, PartyBody, PartyData, SmartsBody, SmartsData, Supervise ],
ThPartyRpcControl,
ThPartyRpcServerImpl,
ThPartyMonitorImpl,
ThSmarts USING [ SmartsInterface, SmartsProperties ],
ThSmartsRpcControl USING [ ImportNewInterface, InterfaceRecord ],
ThVersions USING [ ThrushVR ],
Triples USING [ Any, Erase, Foreach, ForeachProc, Make, Select ],
TU USING [ RefAddr ]
;
ThPartyInitImpl: CEDAR MONITOR LOCKS root
IMPORTS Atom,
BasicTime,
IO,
Commander,
CommandToolExtras,
ExplicitExport,
LarkPlay,
Log,
LupineRuntime,
Names,
NamesGV,
NamesRPC,
Nice,
root: ThPartyMonitorImpl,
Rope,
RPC,
SafeStorage,
SmartsRpc: ThSmartsRpcControl,
Thrush,
ThNet,
ThPartyPrivate,
ThPartyRpcControl,
ThPartyRpcServerImpl,
ThVersions,
Triples,
TU
EXPORTS ThParty, ThPartyPrivate
SHARES ThPartyMonitorImpl, ThPartyRpcServerImpl = {
OPEN IO;
Copies 
AlertKind: TYPE = Thrush.AlertKind;
CallUrgency: TYPE = Thrush.CallUrgency;
CFRef: TYPE = ThPartyPrivate.CFRef;
ConversationHandle: TYPE = Thrush.ConversationHandle;
PartyBody: TYPE = ThPartyPrivate.PartyBody; -- Concrete
RTPartyType: SafeStorage.Type = SafeStorage.GetCanonicalType[CODE[PartyBody]];
PartyData: TYPE = ThPartyPrivate.PartyData; -- REF Concrete
PartyHandle: TYPE = Thrush.PartyHandle; -- Handle
ROPE: TYPE = Thrush.ROPE;
SmartsBody: TYPE = ThPartyPrivate.SmartsBody; -- Concrete
SmartsData: TYPE = ThPartyPrivate.SmartsData; -- REF Concrete
RTSmartsType: SafeStorage.Type =SafeStorage.GetCanonicalType[CODE[SmartsBody]];
SmartsHandle: TYPE = Thrush.SmartsHandle; -- Handle
SmartsInterface: TYPE = ThSmarts.SmartsInterface;
LocalSmartsInterface: TYPE = SmartsRpc.InterfaceRecord;
nullHandle: Thrush.PartyHandle = Thrush.nullHandle;
SHHH: TYPE = Thrush.SHHH;
none: SHHH = Thrush.unencrypted;
TBD: TYPE = Thrush.TBD;
pERROR: ERROR = Thrush.pERROR;
H: PROC[r: REF] RETURNS[Thrush.ThHandle] = INLINE {RETURN[Thrush.H[r]]; };
larkRegistry: ROPE=".Lark";
recordingRname: Thrush.Rname = "idle.jay.lark";
recordingRAtom: PUBLIC ATOM ← Atom.MakeAtom[recordingRname];
ringTone: LarkPlay.ToneSpec ← NIL;
subduedRingTone: LarkPlay.ToneSpec ← NIL;
outsideRingTune: PUBLIC LarkPlay.ToneSpec ← NIL;
outsideRingTuneRope: ROPE ← "@300;G%>G<%G%>G<%G%>G<%G%>G<%G%>*C";
thPartyExported: BOOLFALSE;
wpState: ThNet.WPState;
Implementation of ThParty
standardPriorities: Thrush.Priorities = LIST [
LIST [$Supervisor], LIST [$Manager], LIST[$LocatedTerminal], LIST [$AdjacentTerminal],
LIST [$VoiceTerminal, $LabPhone], LIST [$Secretary], LIST [$Receptionist] ];
GetParty: PUBLIC ENTRY PROC[shh: SHHHNIL, partyID: PartyHandle, rName: Thrush.Rname]
RETURNS [newPartyID: PartyHandle] = {
ENABLE UNWIND=>NULL;
|Need an NB return on failure|
RETURN[H[GetActiveParty[rName]]]; };
GetActiveParty: INTERNAL PROC[rName: Thrush.Rname]
RETURNS [newParty: PartyData] = {
rAtom: ATOM;
IF rName=NIL THEN RETURN[NIL];
rName ← Names.Registrize[rName];
rAtom ← Names.MakeAtom[rName];
RETURN[NARROW[Triples.Select[$RnameForParty, --party--, rAtom]]];
};
GetJayParty: PUBLIC ENTRY PROC[shh: SHHH←none, partyID: PartyHandle]
RETURNS [newPartyID: PartyHandle←nullHandle] = {
ENABLE UNWIND=>NULL;
party: PartyData← ThPartyPrivate.DehandleParty[partyID];
rAtom: ATOM;
IF party = NIL THEN RETURN;
rAtom ← NARROW[Triples.Select[$RnameForParty, party, -- rAtom --]];
rAtom ← Names.MakeAtom[Atom.GetPName[rAtom].Concat[".Jay"]];
party ← NARROW[Triples.Select[$RnameForParty, --party--, recordingRAtom]];
IF party = NIL OR party.numEnabled = 0 THEN RETURN;
Thrush.MakeUnique[$RnameForParty, party, rAtom];
RETURN[H[party]]; };
GetPartyFromFeepNum: PUBLIC ENTRY PROC[
shh: SHHH←none, partyID: PartyHandle, feepNum: Thrush.ROPENIL]
RETURNS [newPartyID: PartyHandle←nullHandle] = {
ENABLE UNWIND=>NULL;
party: PartyData ← NIL;
rName, officeNumber: ROPE;
listing: ThNet.WPListing;
[rName, officeNumber, listing] ← ThNet.WhitePagesEntry [
wpState: wpState, name: feepNum, feep: TRUE, key: $officeNumber];
IF rName=NIL THEN RETURN;
party ← GetActiveParty[rName];
IF party#NIL THEN RETURN[H[party]];
IF officeNumber=NIL THEN RETURN;
RETURN[GetPartyFromNumberInt[partyID, officeNumber, rName, TRUE]];
};
GetPartyFromNumber: PUBLIC ENTRY PROC[
shh: SHHH, partyID: PartyHandle, phoneNumber: Thrush.ROPE,
description: ROPE, trunkOK: BOOL]
RETURNS [newPartyID: PartyHandle] = {
ENABLE UNWIND => NULL;
RETURN[GetPartyFromNumberInt[partyID, phoneNumber, description, trunkOK]];
};
GetPartyFromNumberInt: INTERNAL PROC[
partyID: PartyHandle, phoneNumber: Thrush.ROPE,
description: ROPE, trunkOK: BOOL]
RETURNS [newPartyID: PartyHandle] = {
isExt: BOOLEANFALSE;
num: ROPENIL;
party: PartyData = ThPartyPrivate.DehandleParty[partyID];
myExt is used as Intelnet authorization code.
myExtAtom: ATOM = IF party=NIL THEN NIL ELSE NARROW[Triples.Select[$Extension, party,]];
myExt: ROPE = IF myExtAtom=NIL THEN NIL ELSE Atom.GetPName[myExtAtom];
IF phoneNumber#NIL THEN [num, isExt] ← ThNet.HowToDial[phoneNumber, myExt];
IF isExt THEN {
extAtom: ATOM=Atom.MakeAtom[num];
newPartyID ← H[NARROW[Triples.Select[$Extension, --party--, extAtom]]];
IF newPartyID#nullHandle THEN RETURN[newPartyID]; };
IF ~trunkOK THEN RETURN[nullHandle];
RETURN[H[GetTrunkParty[partyID: partyID, description: description, address: num]]];
};
GetTrunkParty: INTERNAL PROC[
partyID: PartyHandle, description: Thrush.ROPE, address: Thrush.ROPE]
RETURNS[trunkParty: PartyData←NIL] = {
party: PartyData = ThPartyPrivate.DehandleParty[partyID];
IF party#NIL THEN trunkParty ← NARROW[Triples.Select[$TrunkParty, party, Triples.Any]];
IF trunkParty = NIL OR trunkParty.numConvs#0 THEN RETURN;
<< This is still not right. Trunk parties ought to get invented as callers want them, deleted somehow (how?), connected to smarts when call attempt starts (one only at a time). Problem reserved for future reference (maybe a structure that the GC can hack.>>
TRUSTED {
WITH tp: trunkParty SELECT FROM
trunk => {
tp.outgoing ← address;
tp.reservedBy ← partyID;
Triples.Erase[$RnameForTrunk, trunkParty, Triples.Any];
IF description#NIL THEN
Triples.Make[$RnameForTrunk, trunkParty, Atom.MakeAtom[description]];
};
ENDCASE => ERROR;
};
};
GetNumbersForRName: PUBLIC PROC[shh: SHHH←none, rName: Thrush.Rname]
RETURNS [fullRName: ROPE, number: ROPE, homeNumber: ROPE] = {
listing: ThNet.WPListing;
[fullRName, number, listing] ← ThNet.WhitePagesEntry[
wpState: wpState, name: rName, key: $officeNumber];
IF listing#NIL THEN homeNumber ← ThNet.WhitePagesEntry[
wpState: wpState, key: $outsideNumber, listing: listing].entry;
};
Return the Rname for a given party
GetRname: PUBLIC PROC[shh: SHHH←none, partyID: PartyHandle]
RETURNS [rName: Thrush.Rname] = {
RETURN[GetRnameFromParty[ThPartyPrivate.DehandleParty[partyID]]]; };
CreateParty: PUBLIC PROC[shh: SHHHNIL, rName: Thrush.Rname, type: Thrush.PartyType]
RETURNS [partyID: PartyHandle←nullHandle] = {
|Need NB indicating why null handle returned.|
party: PartyData←NIL;
rn: Thrush.Rname = IF type#recording THEN rName ELSE recordingRname;
larkName: ROPE=rn.Concat[larkRegistry];
nameAttr: ATOM=$RnameForParty;
rAtom: ATOM=Names.MakeAtom[rn];
extension: ATOMNIL;
wpListing: ThNet.WPListing ← NIL;
ExistingParty: ENTRY PROC = -- INLINE -- {
ENABLE UNWIND=>NULL;
party←IF type=recording THEN NIL ELSE NARROW[Triples.Select[nameAttr, --party--, rAtom]];
IF party#NIL AND ~party.partyFailed AND party.type#type THEN {
Log.Problem[remark: "Same party name, new type", where: $System]; party←NIL; };
};
NewParty: ENTRY PROC = -- INLINE -- {
Party does not already exist. Make one if possible.
SELECT type FROM
individual => {
party←NEW[PartyBody ← [v: individual []]];
IF extension#NIL THEN Thrush.MakeUnique[$Extension, party, extension];
};
recording => party←NEW[PartyBody ← [v: recording []]];
trunk => party ← NEW [ PartyBody ← [ v: trunk [ NIL, NIL ] ] ];
ENDCASE => { Log.Problem["Unknown party type", $System]; RETURN; };
party.supervisor ← NIL;
IF rName#NIL THEN Thrush.MakeUnique[nameAttr, party, rAtom];
Thrush.MakeUnique[$Priorities, party, standardPriorities];
IF wpListing#NIL THEN {
ThNet.WhitePagesEnter[wpState: wpState, listing: wpListing];
[]←ThNet.CloseWhitePagesDatabase[ wpState: wpState];
};
partyID ← Thrush.Enhandle[party];
};
rName←rn;
IF rName=NIL AND type#trunk THEN RETURN;
ExistingParty[];
IF party#NIL THEN RETURN[H[party]];
Get data base information
SELECT type FROM
individual => {
Telephone system extension
extR: ROPE←NamesGV.GVGetAttribute[larkName, $extension, NIL];
feepNum: ROPE;
IF extR=NIL THEN {
extR ← GetNumbersForRName[rName: rName].number;
IF extR#NIL AND ~(([extR,]←ThNet.HowToDial[extR]).isLocalExtension) THEN extR←NIL;
};
IF extR#NIL THEN {
extension ← Names.MakeAtom[extR];
feepNum ← ThNet.FeepName[rName];
IF ThNet.WhitePagesEntry[ wpState: wpState, name: feepNum, feep: TRUE, key: $officeNumber].fullRName = NIL THEN {
wpListing ← ThNet.NewListing[];
ThNet.List[wpListing, $rName, rName];
ThNet.List[wpListing, $name, rName]; -- Just in case needed
ThNet.List[wpListing, $officeNumber, Rope.Cat["8923", extR]];
Will be entered in NewParty, under monitor
};
};
};
ENDCASE;
Party does not already exist. Make one if possible.
NewParty[];
SetRingEnable[partyID: partyID, update: TRUE]; -- Get defaults
Log.Report[IO.PutFR["CreateParty[%s, %s] -> [ %g, %g ]",
rope[rName], rope[SELECT type FROM
individual => "individual", trunk=>"trunk", recording=>"recording", ENDCASE=>NIL],
card[partyID], IF party=NIL THEN atom[$Unknown] ELSE TU.RefAddr[party]], $Party, party];
};
ReleaseTrunkParty: PUBLIC ENTRY PROC[shh: SHHH, partyID: PartyHandle] = { NULL };
<<Vestigial function. Pull next time ThParty changes.>>
Register: PUBLIC PROC[shh: SHHH, partyID: PartyHandle, interface: SmartsInterface,
properties: ThSmarts.SmartsProperties, oldSmartsID: SmartsHandle]
RETURNS [smartsID: SmartsHandle] = {
Doesn't handle oldHandle (ReRegister) yet.
RETURN[DoRegister[ThPartyPrivate.DehandleParty[partyID], NIL, interface, properties]]; };
Implementation of ThPartyPrivate
RegisterLocal: PUBLIC PROC[partyID: PartyHandle, interface: LocalSmartsInterface,
properties: ThSmarts.SmartsProperties, oldSmartsID: SmartsHandle]
RETURNS [smartsID: SmartsHandle] = {
|Need NB why failed|
Doesn't handle oldHandle (ReRegister) yet.
RETURN[DoRegister[ThPartyPrivate.DehandleParty[partyID], interface, NIL, properties]]; };
DoRegister: PROC[party: PartyData, localInterface: LocalSmartsInterface,
remoteInterface: SmartsInterface, properties: ThSmarts.SmartsProperties]
RETURNS [smartsID: Thrush.SmartsHandle←nullHandle] ={
|Need NB why failed|
rName: ROPE;
smarts: SmartsData;
shh: SHHH;
interface: SmartsRpc.InterfaceRecord;
IF party=NIL THEN RETURN[nullHandle];
IF properties.role = manager THEN {
Allow only one Manager. Allow it to be the new one.
smarts ← NARROW[Triples.Select[$Manager, party, Triples.Any]];
IF smarts#NIL THEN Deregister[shh: none, smartsID: H[smarts]];
};
rName←GetRnameFromParty[IF party.type#trunk THEN party ELSE GetHostParty[party]];
IF rName=NIL THEN RETURN[Log.ProblemHandle["No RName for own party", $System]];
Take care of encryption, and talking to calling smarts
shh ← NamesRPC.StartConversation[caller: myName.instance, callee: rName,
key: serverPassword, level: --<<ECB>>--CBCCheck ! RPC.AuthenticateFailed =>
{ smartsID ← Log.ProblemHandle["Authenticate failed", $System]; GOTO Failed}];
interface ← IF remoteInterface=NIL THEN localInterface
ELSE SmartsRpc.ImportNewInterface[interfaceName: remoteInterface^ ! RPC.ImportFailed =>
{ smartsID ← Log.ProblemHandle["Import failed", $System]; GOTO Failed}];
Create Smarts body, link with Party; we're rolling.
smarts←NEW[ThPartyPrivate.SmartsBody←[interface: interface, properties: properties,
type: party.type, shh: shh, authenticated: FALSE--, remote: remoteInterface#NIL--]];
smartsID ← EnterSmarts[party, smarts];
Log.Report[IO.PutFR["RegisterSmarts[%g, %s] -> [%g, %g]",
TU.RefAddr[party],
rope[SELECT properties.role FROM voiceTerminal=>"voice", manager=>"mgr", ENDCASE=>"NIY"],
card[smartsID], TU.RefAddr[smarts]], $Party, party];
EXITS
Failed => smartsID←nullHandle;
};
GetHostParty: PROC[party: PartyData] RETURNS [hostParty: PartyData ] = {
RETURN[NARROW[Triples.Select[$TrunkParty, Triples.Any, party]]]; };
RegisterClone: PUBLIC PROC[
shh: SHHH←none, partyID: PartyHandle,
clonePartyID: PartyHandle, oldSmartsID: SmartsHandle←nullHandle
] RETURNS [smartsID: SmartsHandle] = {
cloneParty: PartyData←ThPartyPrivate.DehandleParty[clonePartyID];
party: PartyData ← ThPartyPrivate.DehandleParty[partyID];
cloneSmarts: SmartsData;
smData: SmartsData;
IF party=NIL OR cloneParty=NIL THEN RETURN[nullHandle];
cloneSmarts ← NARROW[Triples.Select[$VoiceTerminal, cloneParty, -- Smarts --]];
IF cloneSmarts=NIL THEN RETURN[nullHandle];
smData←NEW[ ThPartyPrivate.SmartsBody ← [
interface: cloneSmarts.interface, properties: cloneSmarts.properties,
shh: cloneSmarts.shh, authenticated: cloneSmarts.authenticated--, remote: cloneSmarts.remote--]];
smartsID ← EnterSmarts[party, smData];
Log.Report[IO.PutFR["RegisterClone[%g, %g] -> [%g, %g]",
TU.RefAddr[party], TU.RefAddr[cloneParty], card[smartsID], TU.RefAddr[smData]],
$Party, party];
};
Deregister: PUBLIC ENTRY PROC[shh: SHHH, smartsID: SmartsHandle] = {
OPEN Triples;
ENABLE {
UNWIND => NULL;
};
party: PartyData;
cfRef: CFRef;
smarts: SmartsData;
CleanupParty: INTERNAL ForeachProc -- [trip: Triples.TripleRec]
-- RETURNS [continue: BOOLEAN ← TRUE] -- = {
WITH trip.att SELECT FROM
r: CFRef =>
IF (party.numEnabled=0 OR r.voiceSmartsID = smartsID) AND r.event.state#idle THEN {
cfRef ← r;
[] ← ThPartyPrivate.DoAdvance[
smartsID: smartsID,
party: party,
conv: NARROW[trip.obj],
cfRef: cfRef,
state: idle,
reason: terminating]; -- Idle party in conversation!!
RETURN[FALSE];
};
ENDCASE;
};
DeregisterOneParty: INTERNAL ForeachProc -- [trip: TripleRec]
-- RETURNS [continue: BOOLEAN ← TRUE] -- = {
WITH trip.obj SELECT FROM
p: PartyData => party ← p;
ENDCASE => RETURN; -- not a party to smarts connection
Erase[trip.att, party, smarts -- (trip.val) --];
party.numSmarts ← party.numSmarts - 1;
IF smarts.enablesParty THEN party.numEnabled ← party.numEnabled - 1;
SELECT NARROW[trip.att, ATOM] FROM
$Manager => {
adjSmarts: SmartsData=NARROW[Select[$AdjacentTerminal, party, Any]];
Erase[$ManagerOwner, trip.val, Any];
IF adjSmarts#NIL THEN {
Erase[$AdjacentTerminal, party, Any];
party.numSmarts ← party.numSmarts - 1;
IF adjSmarts.enablesParty THEN party.numEnabled ← party.numEnabled - 1;
};
};
ENDCASE;
DO
Remove party from each conversation in which smarts is the voiceSmarts, or to which no enabled smarts remain connected. Outside loop avoids Foreach/Erase conflicts.
cfRef←NIL;
Foreach[Any, Any, party, CleanupParty];
IF cfRef = NIL THEN EXIT;
ENDLOOP;
IF party.numSmarts = 0 THEN {
Party is serving no smarts; destroy the party itself.
party.partyFailed←TRUE;
ThPartyPrivate.Supervise[party];
Log.ReportFR["DeleteParty[%g, %g]", $System, NIL, card[H[party]], TU.RefAddr[party]];
};
RETURN[FALSE];
};
smarts ← ThPartyPrivate.DehandleSmarts[smartsID];
IF smarts=NIL THEN RETURN;
DO -- Outside loop avoids conflict when Deregister erases things.
party←NIL;
Foreach[Any, Any, smarts, DeregisterOneParty];
IF party = NIL THEN EXIT;
ENDLOOP;
Log.ReportFR["UnregisterSmarts[%g, %g]", $System, NIL, card[smartsID], TU.RefAddr[smarts]];
Thrush.KillHandle[smartsID, RTSmartsType!
Thrush.HandleFault=>{Log.Problem["Problem killing smarts handle", $System]; CONTINUE}];
};
EnterSmarts: PROC[party: PartyData, smarts: SmartsData] RETURNS[smartsID: SmartsHandle] = {
Discussion:
The idea is that as Smarts register with a Party they are connected by links whose attributes are derived from their properties and degree of authentication.
When Parties initiate actions that involve Smarts they will select the Smarts to respond in an order determined by the attributes, including SmartsRole, authentication (maybe, see notes), etc.
managerOwnerRAtom: ATOMNIL;
EnterSmartsE: ENTRY PROC RETURNS[smartsID: SmartsHandle] = -- INLINE -- {
Each kind of Smarts has its own link name to its Party
attr: ATOM=WITH props: smarts.properties SELECT FROM
backstop=> $BackStop,
manager=> $Manager,
supervisor=> $Supervisor,
voiceTerminal=> $VoiceTerminal,
ENDCASE=>ERROR pERROR;
smartsID ← Thrush.Enhandle[smarts];
Thrush.MakeUnique[attr, party, smarts];
party.numSmarts ← party.numSmarts + 1;
IF smarts.enablesParty THEN party.numEnabled ← party.numEnabled + 1;
WITH props: smarts.properties SELECT FROM
A Manager also needs to know the party to whom the physical workstation belongs, as an aid to locating the corresponding voice terminal. Set up the relationship, then locate the adjacent terminal, if one already exists.
manager => {
OPEN Triples;
Thrush.MakeUnique[$ManagerOwner, smarts, managerOwnerRAtom];
WITH Select[$RnameForParty, --party--, managerOwnerRAtom] SELECT FROM
managerOwnerParty: PartyData => {
mgrOwnSmarts: SmartsData ←
NARROW[Select[$VoiceTerminal, managerOwnerParty, --smarts--]];
IF mgrOwnSmarts#NIL THEN {
Thrush.MakeUnique[$AdjacentTerminal, party, mgrOwnSmarts];
party.numSmarts ← party.numSmarts + 1;
IF mgrOwnSmarts.enablesParty THEN party.numEnabled ← party.numEnabled + 1;
};
};
ENDCASE;
};
When the Manager registers first, the $Adjacent link must be sent when the Lark smarts arrives or dies and revives.
voiceTerminal => IF party.type=individual THEN {
OPEN Triples;
managerOwnerSmarts: REF;
managerOwnerParty: PartyData;
managerOwnerRAtom ← NARROW[Select[$RnameForParty, party, -- rAtom --]];
IF managerOwnerRAtom=NIL THEN RETURN; -- This is really an Error, though.
managerOwnerSmarts ← Select[$ManagerOwner, --smarts--, managerOwnerRAtom];
IF managerOwnerSmarts=NIL THEN RETURN;
managerOwnerParty ← NARROW[Select[$Manager, -- party--, managerOwnerSmarts]];
IF managerOwnerParty=NIL THEN RETURN;
Thrush.MakeUnique[$AdjacentTerminal, managerOwnerParty, smarts];
managerOwnerParty.numSmarts ← managerOwnerParty.numSmarts + 1;
IF smarts.enablesParty THEN
managerOwnerParty.numEnabled ← managerOwnerParty.numEnabled + 1;
};
ENDCASE;
};
WITH props: smarts.properties SELECT FROM
A Manager also needs to know the party to whom the physical workstation belongs, as an aid to locating the corresponding voice terminal. Set up the relationship, then locate the adjacent terminal, if one already exists.
manager => {
managerOwner: ROPE ← NamesGV.GVGetAttribute[
rName: Names.InstanceFromNetAddress[props.machine, larkRegistry],
attribute: $owner, default: NIL];
IF managerOwner#NIL THEN managerOwnerRAtom ← Names.MakeAtom[managerOwner];
};
ENDCASE;
RETURN[EnterSmartsE[]];
};
Enable: PUBLIC ENTRY PROC[shh: SHHH←none, smartsID: SmartsHandle]
RETURNS [nb: Thrush.NB] = {
smarts: SmartsData=ThPartyPrivate.DehandleSmarts[smartsID];
IF smarts=NIL THEN RETURN[noSuchSmarts];
IF smarts.enablesParty THEN RETURN[success];
smarts.enablesParty←TRUE;
RETURN[Able[smarts, 1]];
};
Disable: PUBLIC ENTRY PROC[shh: SHHH←none, smartsID: SmartsHandle]
RETURNS [nb: Thrush.NB] = {
smarts: SmartsData=ThPartyPrivate.DehandleSmarts[smartsID];
IF smarts=NIL THEN RETURN[noSuchSmarts];
IF ~smarts.enablesParty THEN RETURN[success];
smarts.enablesParty←FALSE;
RETURN[Able[smarts, -1]];
};
Able: INTERNAL PROC[smarts: SmartsData, direction: INT]
RETURNS[nb: Thrush.NB←success] = {
AbleOne: Triples.ForeachProc -- [trip: TripleRec]
-- RETURNS [continue: BOOLEAN ← TRUE] -- ={
WITH trip.obj SELECT FROM
party: PartyData => {
res: INT=party.numEnabled+direction;
IF res<0 THEN nb←invalidTransition
ELSE party.numEnabled ← res;
};
ENDCASE;
};
Triples.Foreach[Triples.Any, Triples.Any, smarts, AbleOne];
};
DescribeParty: PUBLIC ENTRY PROC[shh: SHHH←none,
partyID: PartyHandle]
RETURNS[ description: Thrush.ROPE] = {
ENABLE UNWIND=>NULL;
RETURN[DoDescribeParty[partyID]];
};
DoDescribeParty: PUBLIC INTERNAL PROC[ partyID: PartyHandle ]
RETURNS[ description: Thrush.ROPE] = TRUSTED {
party: PartyData = ThPartyPrivate.DehandleParty[partyID];
IF party=NIL THEN RETURN[NIL];
description ← GetRnameFromParty[party];
WITH p: party SELECT FROM
individual => RETURN[description];
trunk => {
more: ROPE=IF p.outgoing#NIL THEN p.outgoing ELSE "outside line";
IF description=NIL THEN description ← more
ELSE description ← IO.PutFR["%s (%s)", IO.rope[description], IO.rope[more]];
RETURN[description];
};
recording => RETURN["Recording service"];
ENDCASE;
};
GetRnameFromParty: PROC[party: PartyData] RETURNS [rName: Thrush.Rname] = {
rRef: REF ANY;
IF party=NIL THEN RETURN[NIL];
rRef ← Triples.Select[$RnameForParty, party, --rRef--];
IF rRef=NIL THEN rRef←Triples.Select[$RnameForTrunk, party, --rRef--];
IF rRef=NIL THEN RETURN[NIL];
RETURN[Atom.GetPName[NARROW[rRef]]]; };
See if it works just to get the first instance of the highest priority connection that exists,
using standardPriorities.
GetCurrentParty: PUBLIC ENTRY PROC[shh: Thrush.SHHH←none, smartsID: Thrush.SmartsHandle]
RETURNS [partyID: Thrush.PartyHandle←Thrush.nullHandle] = {
| Should have an NB |
ENABLE UNWIND=>NULL;
smarts: REF ← ThPartyPrivate.DehandleSmarts[smartsID];
IF smarts=NIL THEN RETURN;
FOR p: Thrush.Priorities ← standardPriorities, p.rest WHILE p#NIL DO
FOR s: Thrush.Priorities ← NARROW[p.first], s.rest WHILE s#NIL DO
att: ATOMNARROW[s.first];
party: REF←Triples.Select[att, Triples.Any, smarts];
IF party#NIL THEN RETURN[H[party]];
ENDLOOP;
ENDLOOP; };
GetPartySmarts: PUBLIC ENTRY PROC[partyID: Thrush.PartyHandle, kind: ATOM
] RETURNS [smartsID: Thrush.SmartsHandle]={
ENABLE UNWIND=>NULL;
party: PartyData←ThPartyPrivate.DehandleParty[partyID];
IF party=NIL THEN RETURN [Thrush.nullHandle];
RETURN[H[Triples.Select[kind, party, --smarts--]]]; };
GetStdRingInfo: PUBLIC PROC [partyID: PartyHandle] = TRUSTED {
party: PartyData←ThPartyPrivate.DehandleParty[partyID];
larkRname: ROPE;
rdAtom: ATOM;
IF party=NIL THEN RETURN;
WITH iParty: party^ SELECT FROM
individual => {
larkRname ← GetRnameFromParty[party].Concat[larkRegistry];
IF larkRname=larkRegistry THEN RETURN;
iParty.ringEnable ← SELECT
NamesGV.GVGetAttribute[rName: larkRname, attribute: $ringmode, default: "R"].Fetch[0] FROM
'R => on,
'S => subdued,
'O => off,
ENDCASE => on;
rdAtom ← Names.MakeAtom[NamesGV.GVGetAttribute[rName: larkRname, attribute: $dotune, default: "false"]];
iParty.ringDo ← SELECT rdAtom FROM
$true => ownTune,
$false => standard,
$tune => ownTune,
$both => bothTunes,
ENDCASE => standard;
iParty.ringTuneRope ← Rope.Cat[NamesGV.GVGetAttribute[larkRname, $tunea, NIL],
NamesGV.GVGetAttribute[larkRname, $tuneb, NIL],
NamesGV.GVGetAttribute[larkRname, $tunec, NIL]];
};
ENDCASE;
};
SetStdRingInfo: PUBLIC PROC[partyID: PartyHandle, update: BOOLFALSE] = TRUSTED {
party: PartyData←ThPartyPrivate.DehandleParty[partyID];
larkRname: ROPE;
IF party=NIL THEN RETURN;
WITH iParty: party^ SELECT FROM
individual => {
r: ROPE;
larkRname ← GetRnameFromParty[party].Concat[larkRegistry];
IF larkRname=larkRegistry THEN RETURN;
r ← SELECT iParty.ringEnable FROM on => "R", subdued => "S", off => "O", ENDCASE => NIL;
IF r#NIL THEN NamesGV.GVSetAttribute[larkRname, $ringmode, r];
IF update THEN NamesGV.GVUpdate[larkRname];
};
ENDCASE;
};
SetRingEnable: PUBLIC PROC[shh: SHHH←none, partyID: PartyHandle, ringEnable: Thrush.RingEnable←noChange, ringInterval: INT𡤀, update: BOOLFALSE] = TRUSTED {
party: PartyData←ThPartyPrivate.DehandleParty[partyID];
GetStdRingInfo[partyID];
WITH iParty: party^ SELECT FROM
individual => {
IF ringEnable=noChange THEN ringEnable ← iParty.ringEnable;
iParty.ringEnable ← ringEnable;
iParty.ringTime ← BasicTime.Update[BasicTime.Now[], ringInterval];
[] ← PrepareRingTune[party];
SELECT ringEnable FROM
on, subdued, off => {
IF update AND ringEnable#iParty.defaultRingEnable THEN
SetStdRingInfo[partyID, ThNet.pd.autoGVUpdate];
iParty.defaultRingEnable ← ringEnable;
};
ENDCASE;
};
ENDCASE;
};
PrepareRingTune: PROC[party: PartyData] RETURNS[ringTune: LarkPlay.ToneSpec] = TRUSTED {
WITH iParty: party^ SELECT FROM
individual => {
Set Defaults
IF ThNet.pd.ringsInvalid THEN {
ThNet.pd.ringsInvalid ← FALSE;
ringTone ← NEW[LarkPlay.ToneSpecRec ← [
repeatIndefinitely: TRUE, volume: ThNet.pd.defaultRingVolume,
tones: LIST[LIST[
[f1: 440, f2: 480, on: 2000, off: 4000],
[f1: 440, f2: 480, on: 2000, off: 4000]]]]];
subduedRingTone ← NEW[LarkPlay.ToneSpecRec ← [
repeatIndefinitely: FALSE,
volume: ThNet.pd.defaultRingVolume+ThNet.pd.subduedVolumeInterval,
tones: LIST[LIST[[f1: 440, f2: 480, on: 500, off: 0]]]
]];
outsideRingTune ← LarkPlay.PlayString[outsideRingTuneRope, FALSE, ThNet.pd.defaultRingVolume];
};
IF iParty.ringDo#standard AND iParty.ringTuneRope#NIL THEN
ringTune ← LarkPlay.PlayString[music: iParty.ringTuneRope, file: FALSE, volume: ThNet.pd.defaultRingVolume]
ELSE {
pattern: LarkPlay.ToneSpec = SELECT iParty.ringEnable FROM
subdued, subduedTimed => subduedRingTone, ENDCASE => ringTone;
ringTune ← NEW[LarkPlay.ToneSpecRec←pattern^];
};
IF ringTune#NIL THEN SELECT iParty.ringEnable FROM
subdued, subduedTimed => {
ringTune.volume ← ringTune.volume + ThNet.pd.subduedVolumeInterval;
ringTune.repeatIndefinitely ← FALSE;
};
ENDCASE;
iParty.ringTune ← ringTune;
};
ENDCASE;
};
ReportSystem: Log.WhereProc = CHECKED {
s←NIL;
};
ReportParty: Log.WhereProc = TRUSTED {
party: PartyData = NARROW[whereData];
smarts: SmartsData;
IF s=NIL THEN s ← Log.FindWhere[$System, NIL];
IF party=NIL THEN RETURN;
smarts ← NARROW[Triples.Select[$VoiceTerminal, party, Triples.Any]];
IF smarts=NIL THEN RETURN;
WITH props: smarts.properties SELECT FROM
voiceTerminal => s←Nice.LarkConLogStream[props.machine]; ENDCASE;
IF s#NIL AND s=Nice.LarkConLogStream[[[0],[0]]] THEN s←Log.FindWhere[$System, NIL];
};
WhitePagesInit: Commander.CommandProc = {
treeName, intExtName: ROPE;
IF wpState#NIL AND ThNet.CloseWhitePagesDatabase[wpState] THEN wpState←NIL;
treeName ← CommandToolExtras.NextArgument[cmd];
intExtName ← CommandToolExtras.NextArgument[cmd];
IF wpState=NIL THEN wpState ← ThNet.InitWhitePagesDatabase[
treeName, -- Optional, defaults supplied by WP impl.,
intExtName, -- so NILs are OK here
$write];
};
myName: ThPartyRpcControl.InterfaceName;
serverPassword: RPC.EncryptionKey;
ThPartyInit: Commander.CommandProc = {
ENABLE
RPC.ExportFailed => { Log.Problem["ThParty export failed", $System]; GOTO Failed; };
myName ← [
type: "ThParty.Lark",
instance: Names.CmdOrToken[cmd: cmd, key: "ThrushServerInstance", default: "Morley.Lark"],
version: ThVersions.ThrushVR];
serverPassword ← Names.CurrentPasskey[Names.CmdOrToken[
cmd: cmd, key: "ThrushServerPassword", default: "MFLFLX"]];
IF thPartyExported THEN RETURN;
Log.RegisterWhereToReport[ReportSystem, $System];
Log.RegisterWhereToReport[ReportParty, $Party];
ThPartyRpcControl.ExportInterface[
interfaceName: myName,
user: myName.instance,
password: serverPassword];
ExplicitExport.ExportExplicitly["ThParty.Lark", myName.instance, ThVersions.ThrushVR, ThPartyRpcControl.LupineProtocolVersion, ThPartyRpcServerImpl.ServerDispatcher];
Log.ReportFR["Export[ThParty.Lark, %s]", $System, NIL, rope[myName.instance]];
thPartyExported ← TRUE;
EXITS
Failed => ThPartyRpcControl.UnexportInterface[!LupineRuntime.BindingError=>CONTINUE];
};
Commander.Register["WhitePages", WhitePagesInit, "WhitePages <TreeName (opt.) <IntExtMapFileName (opt)>> -- Initialize and Open White Pages Database"];
Commander.Register["ThParty", ThPartyInit,
"ThParty <ExportInstance(opt.) <ServerPassword (opt)>> -- Initialize and Export ThParty"];
}.