ThPartyInitImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Last modified by D. Swinehart, May 19, 1986 10:11:31 am PDT
DIRECTORY
Atom USING [ GetPName, MakeAtom ],
BasicTime USING [ Now, Update ],
Commander USING [ CommandProc, Register ],
CommandTool USING [ NextArgument ],
IO,
LarkPlay USING [ PlayString, Tone, ToneSpec, ToneSpecRec ],
LupineRuntime USING [ BindingError ],
NamesGV USING [ GVGetAttribute, GVSetAttribute, GVUpdate ],
NamesRPC USING [ StartConversation ],
Nice,
Pup USING [ nullSocket ],
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, NetAddress, noMachine, nullHandle, pERROR, 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 [ MakeUnique, RefAddr ],
VoiceUtils USING [ CmdOrToken, CurrentPasskey, InstanceFromNetAddress, MakeAtom, MakeRName, Registrize, FindWhere, Problem, RegisterWhereToReport, Report, ReportFR, WhereProc ]
;
ThPartyInitImpl:
CEDAR MONITOR
LOCKS root
IMPORTS Atom,
BasicTime,
IO,
Commander,
CommandTool,
LarkPlay,
LupineRuntime,
NamesGV,
NamesRPC,
Nice,
root: ThPartyMonitorImpl,
Rope,
RPC,
SafeStorage,
SmartsRpc: ThSmartsRpcControl,
Thrush,
ThNet,
ThPartyPrivate,
ThPartyRpcControl,
ThPartyRpcServerImpl,
ThVersions,
Triples,
TU,
VoiceUtils
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";
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: BOOL←FALSE;
wpState: ThNet.WPState;
standardPriorities: Thrush.Priorities =
LIST [
LIST [$Supervisor], LIST [$Manager], LIST[$LocatedTerminal], LIST [$AdjacentTerminal],
LIST [$VoiceTerminal, $LabPhone], LIST [$Secretary], LIST [$Receptionist] ];
Implementation
GetParty:
PUBLIC
ENTRY PROC[shh:
SHHH←
NIL, partyID: PartyHandle,
rName: Thrush.Rname, type: Thrush.PartyType]
RETURNS [newPartyID: PartyHandle] = {
ENABLE UNWIND=>NULL;
|Need an NB return on failure|
RETURN[H[GetActiveParty[partyID, rName, type]]]; };
GetActiveParty:
INTERNAL PROC[partyID: PartyHandle, rName: Thrush.Rname,
type: Thrush.PartyType]
RETURNS [newParty: PartyData] = {
rAtom: ATOM;
IF rName=NIL THEN RETURN[NIL];
IF type=service THEN RETURN[GetIdleParty[partyID, rName]];
rName ← VoiceUtils.Registrize[rName];
rAtom ← VoiceUtils.MakeAtom[rName];
RETURN[NARROW[Triples.Select[$RnameForParty, --party--, rAtom]]];
};
GetIdleParty:
INTERNAL
PROC[partyID: PartyHandle, serviceName:
ROPE]
RETURNS [newPartyData: PartyData ← NIL] = {
ENABLE UNWIND=>NULL;
party: PartyData← ThPartyPrivate.DehandleParty[partyID];
rAtom: ATOM;
serviceNameAtom: ATOM = MakeServiceRname[serviceName].serviceRnameAtom;
IF party = NIL THEN RETURN;
rAtom ← NARROW[Triples.Select[$RnameForParty, party, -- rAtom --]];
rAtom ← VoiceUtils.MakeAtom[Rope.Cat[Atom.GetPName[rAtom], ".", serviceName]];
party ← NARROW[Triples.Select[$RnameForParty, --party--, serviceNameAtom]];
IF party = NIL OR party.numEnabled = 0 THEN RETURN;
TU.MakeUnique[$RnameForParty, party, rAtom];
RETURN[party]; };
GetPartyFromFeepNum:
PUBLIC
ENTRY PROC[
shh: SHHH←none, partyID: PartyHandle, feepNum: Thrush.ROPE←NIL]
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[partyID, rName, individual];
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: BOOLEAN ← FALSE;
num: ROPE ← NIL;
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 {
SELECT trunkParty.type
FROM
trunk => {
trunkParty.outgoing ← address;
trunkParty.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:
SHHH←
NIL, rName: Thrush.Rname, type: Thrush.PartyType]
RETURNS [partyID: PartyHandle←nullHandle] = {
|Need NB indicating why null handle returned.|
rName is a misnomer; it's the name of a service, without registry, if type is service.
party: PartyData←NIL;
partyRname: Thrush.Rname = IF type#service THEN rName ELSE MakeServiceRname[rName].serviceRname;
larkName: ROPE= partyRname.Concat[larkRegistry];
rAtom: ATOM=VoiceUtils.MakeAtom[partyRname];
extension: ATOM←NIL;
wpListing: ThNet.WPListing ← NIL;
ExistingParty:
ENTRY
PROC =
-- INLINE -- {
ENABLE UNWIND=>NULL;
party←IF type=service THEN NIL ELSE NARROW[Triples.Select[$RnameForParty, --party--, rAtom]];
IF party#
NIL
AND ~party.partyFailed
AND party.type#type
THEN {
VoiceUtils.Problem[remark: "Same party name, new type", where: $System]; party←NIL; };
};
NewParty:
ENTRY
PROC =
-- INLINE -- {
Party does not already exist. Make one if possible.
party ← NEW[PartyBody ← [type: type]];
SELECT type
FROM
individual, service => {
IF extension#NIL THEN TU.MakeUnique[$Extension, party, extension];
party.serviceName ← rName; -- useful in service case only
};
ENDCASE;
party.supervisor ← NIL;
IF partyRname#NIL THEN TU.MakeUnique[$RnameForParty, party, rAtom];
TU.MakeUnique[$Priorities, party, standardPriorities];
IF wpListing#
NIL
THEN {
ThNet.WhitePagesEnter[wpState: wpState, listing: wpListing];
[]←ThNet.CloseWhitePagesDatabase[ wpState: wpState];
};
partyID ← Thrush.Enhandle[party];
IF type=individual
THEN
SetRingEnable[partyID: Thrush.Enhandle[party], update: TRUE]; -- Get defaults
IF partyRname=NIL AND type#trunk THEN RETURN;
ExistingParty[];
IF party#NIL THEN RETURN[H[party]];
Get data base information
SELECT type
FROM
individual, service => {
Telephone system extension
attr: ROPE←NamesGV.GVGetAttribute[larkName, $extension, NIL];
feepNum: ROPE;
IF attr=
NIL
THEN {
attr ← GetNumbersForRName[rName: partyRname].number;
IF attr#NIL AND ~(([attr,]←ThNet.HowToDial[attr]).isLocalExtension) THEN attr←NIL;
};
IF attr#
NIL
THEN {
extension ← VoiceUtils.MakeAtom[attr];
feepNum ← ThNet.FeepName[partyRname];
IF ThNet.WhitePagesEntry[ wpState: wpState, name: feepNum, feep:
TRUE, key: $officeNumber].fullRName =
NIL
THEN {
wpListing ← ThNet.NewListing[];
ThNet.List[wpListing, $rName, partyRname];
ThNet.List[wpListing, $name, partyRname]; -- Just in case needed
ThNet.List[wpListing, $officeNumber, Rope.Cat["8923", attr]];
Will be entered in NewParty, under monitor
};
};
};
ENDCASE;
Party does not already exist. Make one if possible.
NewParty[];
VoiceUtils.Report[
IO.PutFR["CreateParty[%s, %s] -> [ %g, %g ]",
rope[rName], rope[
SELECT type
FROM
individual => "individual", trunk=>"trunk", service=>"service", ENDCASE=>NIL],
card[partyID], IF party=NIL THEN atom[$Unknown] ELSE TU.RefAddr[party]], $Party, party];
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 { VoiceUtils.Problem["No RName for own party", $System]; RETURN; };
Take care of encryption, and talking to calling smarts
shh ← NamesRPC.StartConversation[caller: serverInterfaceName.instance, callee: rName,
key: serverPassword, level: --<<ECB>>--CBCCheck ! RPC.AuthenticateFailed =>
{ VoiceUtils.Problem["Authenticate failed", $System]; GOTO Failed}];
interface ←
IF remoteInterface=
NIL
THEN localInterface
ELSE SmartsRpc.ImportNewInterface[interfaceName: remoteInterface^ !
RPC.ImportFailed =>
{ VoiceUtils.Problem["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];
VoiceUtils.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];
VoiceUtils.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];
VoiceUtils.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;
VoiceUtils.ReportFR["UnregisterSmarts[%g, %g]", $System, NIL, card[smartsID], TU.RefAddr[smarts]];
Thrush.KillHandle[smartsID, RTSmartsType!
Thrush.HandleFault=>{VoiceUtils.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: ATOM←NIL;
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];
TU.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;
TU.MakeUnique[$ManagerOwner, smarts, managerOwnerRAtom];
WITH Select[$RnameForParty,
--party--, managerOwnerRAtom]
SELECT
FROM
managerOwnerParty: PartyData => {
mgrOwnSmarts: SmartsData ←
NARROW[Select[$VoiceTerminal, managerOwnerParty, --smarts--]];
IF mgrOwnSmarts#
NIL
THEN {
TU.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;
TU.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 => {
netAddress: Thrush.NetAddress ← [props.netAddress.net, props.netAddress.host, Pup.nullSocket];
managerOwner:
ROPE ← NamesGV.GVGetAttribute[
rName: VoiceUtils.InstanceFromNetAddress[netAddress, larkRegistry],
attribute: $owner, default: NIL];
IF managerOwner#NIL THEN managerOwnerRAtom ← VoiceUtils.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];
SELECT party.type
FROM
individual => RETURN[description];
trunk => {
more: ROPE=IF party.outgoing#NIL THEN party.outgoing ELSE "outside line";
IF description=NIL THEN description ← more
ELSE description ← IO.PutFR["%s (%s)", IO.rope[description], IO.rope[more]];
RETURN[description];
};
service => RETURN[IO.PutFR["%g service", rope[party.serviceName]]];
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]]]; };
MakeServiceRname:
PUBLIC
PROC[serviceName: Thrush.
ROPE]
RETURNS [serviceRname: Thrush.ROPE, serviceRnameAtom: ATOM] = {
serviceRname ← Rope.Cat[serviceName, larkRegistry];
serviceRnameAtom ← VoiceUtils.MakeAtom[serviceRname];
};
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: ATOM←NARROW[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;
SELECT party.type
FROM
individual => {
larkRname ← GetRnameFromParty[party].Concat[larkRegistry];
IF larkRname=larkRegistry THEN RETURN;
party.ringEnable ←
SELECT
NamesGV.GVGetAttribute[rName: larkRname, attribute: $ringmode, default: "R"].Fetch[0]
FROM
'R => on,
'S => subdued,
'O => off,
ENDCASE => on;
rdAtom ← VoiceUtils.MakeAtom[NamesGV.GVGetAttribute[rName: larkRname, attribute: $dotune, default: "false"]];
party.ringDo ←
SELECT rdAtom
FROM
$true => ownTune,
$false => standard,
$tune => ownTune,
$both => bothTunes,
ENDCASE => standard;
party.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:
BOOL←
FALSE] =
TRUSTED {
party: PartyData←ThPartyPrivate.DehandleParty[partyID];
larkRname: ROPE;
IF party=NIL THEN RETURN;
SELECT party.type
FROM
individual => {
r: ROPE;
larkRname ← GetRnameFromParty[party].Concat[larkRegistry];
IF larkRname=larkRegistry THEN RETURN;
r ← SELECT party.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:
BOOL←
FALSE] =
TRUSTED {
party: PartyData←ThPartyPrivate.DehandleParty[partyID];
GetStdRingInfo[partyID];
SELECT party.type
FROM
individual => {
IF ringEnable=noChange THEN ringEnable ← party.ringEnable;
party.ringEnable ← ringEnable;
party.ringTime ← BasicTime.Update[BasicTime.Now[], ringInterval];
[] ← PrepareRingTune[party];
SELECT ringEnable
FROM
on, subdued, off => {
IF update
AND ringEnable#party.defaultRingEnable
THEN
SetStdRingInfo[partyID, ThNet.pd.autoGVUpdate];
party.defaultRingEnable ← ringEnable;
};
ENDCASE;
};
ENDCASE;
};
PrepareRingTune:
PROC[party: PartyData]
RETURNS[ringTune: LarkPlay.ToneSpec] =
TRUSTED {
SELECT party.type 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 party.ringDo#standard
AND party.ringTuneRope#
NIL
THEN
ringTune ← LarkPlay.PlayString[music: party.ringTuneRope, file: FALSE, volume: ThNet.pd.defaultRingVolume]
ELSE {
pattern: LarkPlay.ToneSpec =
SELECT party.ringEnable
FROM
subdued, subduedTimed => subduedRingTone, ENDCASE => ringTone;
ringTune ← NEW[LarkPlay.ToneSpecRec←pattern^];
};
IF ringTune#
NIL
THEN
SELECT party.ringEnable
FROM
subdued, subduedTimed => {
ringTune.volume ← ringTune.volume + ThNet.pd.subduedVolumeInterval;
ringTune.repeatIndefinitely ← FALSE;
};
ENDCASE;
party.ringTune ← ringTune;
};
ENDCASE;
};
ReportSystem: VoiceUtils.WhereProc =
CHECKED {
s←NIL;
};
ReportParty: VoiceUtils.WhereProc =
TRUSTED {
party: PartyData = NARROW[whereData];
smarts: SmartsData;
IF s=NIL THEN s ← VoiceUtils.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 => {
netAddress: Thrush.NetAddress =
[ props.netAddress.net, props.netAddress.host, Pup.nullSocket];
s←Nice.LarkConLogStream[netAddress];
};
ENDCASE;
IF s#NIL AND s=Nice.LarkConLogStream[Thrush.noMachine] THEN s←VoiceUtils.FindWhere[$System, NIL];
};
WhitePagesInit: Commander.CommandProc = {
treeName, intExtName: ROPE;
IF wpState#NIL AND ThNet.CloseWhitePagesDatabase[wpState] THEN wpState←NIL;
treeName ← CommandTool.NextArgument[cmd];
intExtName ← CommandTool.NextArgument[cmd];
IF wpState=
NIL
THEN wpState ← ThNet.InitWhitePagesDatabase[
treeName, -- Optional, defaults supplied by WP impl.,
intExtName, -- so NILs are OK here
$write];
};
serverInterfaceName: ThPartyRpcControl.InterfaceName;
serverPassword: RPC.EncryptionKey;
ThPartyInit: Commander.CommandProc = {
ENABLE
RPC.ExportFailed => { VoiceUtils.Problem["ThParty export failed", $System]; GOTO Failed; };
instance:
ROPE = VoiceUtils.MakeRName[style: rName, name:
VoiceUtils.CmdOrToken[cmd: cmd, key: "ThrushServerInstance", default: "Strowger.Lark"]];
serverInterfaceName ← [
type: "ThParty.Lark",
instance: instance,
version: ThVersions.ThrushVR];
serverPassword ← VoiceUtils.CurrentPasskey[VoiceUtils.CmdOrToken[
cmd: cmd, key: "ThrushServerPassword", default: "MFLFLX"]];
VoiceUtils.RegisterWhereToReport[ReportSystem, $System];
VoiceUtils.RegisterWhereToReport[ReportParty, $Party];
ThPartyRpcControl.UnexportInterface[!LupineRuntime.BindingError=>CONTINUE];
ThPartyRpcControl.ExportInterface[
interfaceName: serverInterfaceName,
user: instance,
password: serverPassword];
VoiceUtils.ReportFR["Export[ThParty.Lark, %s]", $System, NIL, rope[serverInterfaceName.instance]];
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[Strowger]> <ServerPassword[...]>\nInitialize and Export ThParty"];
}.
Swinehart, May 15, 1985 10:24:49 am PDT
Cedar 6.0
changes to: larkRegistry, GetParty, GetActiveParty, GeIdleParty, CreateParty, NewParty
Swinehart, May 16, 1985 9:18:28 am PDT
CommandToolExtras zapped.
Swinehart, May 22, 1985 12:01:41 pm PDT
ServiceName changes
recording => service
Jay => serviceName, to generalize
individual and service treated similarly in places
changes to: GetIdleParty, CreateParty, NewParty, DoDescribeParty, MakeServiceRname
changes to: CreateParty, ExistingParty, NewParty, DoDescribeParty
changes to: GetIdleParty
changes to: CreateParty, ExistingParty, NewParty
Swinehart, May 16, 1986 4:19:57 pm PDT
Cedar 6.1
changes to: DIRECTORY, ThPartyInitImpl, GetActiveParty, GetIdleParty, CreateParty, ExistingParty, DoRegister, RegisterClone, Deregister, EnterSmarts, MakeServiceRname, GetStdRingInfo, ReportSystem, ReportParty, ThPartyInit