LarkSmartsInitImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Last modified by D. Swinehart, June 21, 1987 11:28:57 pm PDT
Last Edited by: Pier, May 3, 1984 2:52:59 pm PDT
DIRECTORY
CardTab USING [ Create, Fetch, Ref, Store ],
Commander USING [ CommandProc, Register ],
CommandTool USING [ NextArgument ],
Convert USING [ IntFromRope ],
IO,
Lark USING [ CommandEventSequence, LarkModel, Machine, SHHH ],
LarkControl USING [ GetLark, SetApplicationMode ],
LarkOpsRpcControl USING [ ImportNewInterface, InterfaceRecord ],
LarkSmarts,
LarkSmartsMonitorImpl,
LarkSmartsRpcControl USING [ExportInterface, InterfaceName, UnexportInterface],
LupineRuntime,
MBQueue USING [ Create, Flush, Queue, QueueClientAction ],
NameDB USING [ GetAttribute ],
NamesRPC USING [ StartConversation ],
Nice USING [ LarkConLogStream ],
Pup USING [ nullSocket ],
RefID USING [ ID, Reseal, Unseal ],
Rope USING [Equal],
RPC USING [ AuthenticateFailed, EncryptionKey, ExportFailed, GetCaller, ImportFailed ],
ThNet USING [ pd ],
ThPartyPrivate USING [ AssignSmartsID, RegisterLocal, UnsealSmarts ],
ThParty USING [ Deregister, Enable, SmartsInterfaceRecord ],
Thrush USING [
ActionClass, Credentials, epoch, Epoch, NB, NetAddress, noAddress, nullID, PartyID, PartyType, ROPE, SmartsID, unencrypted ],
ThSmartsPrivate USING [
CheckHookState, EnterLarkState, Fail, LarkCheckIn, LarkInfo, LarkInfoBody, LarkParseEvent, LarkProgress, LarkReportAction, LarkSubstitution, NoteNewState, RegisterTrunk, SetupTimeouts, SmartsInfo, SmartsInfoBody ],
ThSmartsRpcControl,
ThVersions,
Triples USING [ Make ],
TU,
VoiceUtils USING [ CmdOrToken, CurrentPasskey, DNFProc, InstanceFromNetAddress, MakeRName, Problem, ProblemFR, RegisterWhereToReport, ReportFR, RnameToRspec, Rspec, WhereProc ]
;
LarkSmartsInitImpl: CEDAR MONITOR LOCKS root
IMPORTS IO,
CardTab,
Commander,
CommandTool,
Convert,
LarkOpsRpcControl,
LarkControl,
LarkSmartsRpcControl,
NameDB,
NamesRPC,
root: LarkSmartsMonitorImpl,
LupineRuntime,
MBQueue,
Nice,
RefID,
Rope,
RPC,
ThNet,
ThPartyPrivate,
ThParty,
Thrush,
ThSmartsPrivate,
ThSmartsRpcControl,
ThVersions,
Triples,
TU,
VoiceUtils
EXPORTS LarkSmarts, ThSmartsPrivate
SHARES LarkSmartsMonitorImpl = {
OPEN IO;
larkInfos: CardTab.Ref ← NIL; -- Locate current or previous LarkInfos directly.
Copies
Reseal: PROC[r: REF] RETURNS[RefID.ID] = INLINE {RETURN[RefID.Reseal[r]]; };
nullID: RefID.ID = Thrush.nullID;
PartyID: TYPE = Thrush.PartyID;
ROPE: TYPE = Thrush.ROPE;
SHHH: TYPE = Lark.SHHH; -- Encrypts conv. if first arg to RPC PROC
LarkInfo: TYPE = ThSmartsPrivate.LarkInfo;
SmartsID: TYPE = Thrush.SmartsID;
SmartsInfo: TYPE = ThSmartsPrivate.SmartsInfo;
SmartsInfoBody: TYPE = ThSmartsPrivate.SmartsInfoBody;
Limited registration for supporting service registration. This handles only one service for each actionClass, replacing any previous one for the same class.
See LarkSmartsInitImpl for the point of invocation, LarkSmartsSynthImpl for an example of use.
potentialServiceProviders: ServiceProvidersList;
ServiceProvidersList: TYPE = LIST OF ServiceProviderRecord;
ServiceProviderRecord: TYPE = RECORD [
actionClass: Thrush.ActionClass,
potentialServiceProvider: PROC[credentials: Thrush.Credentials, smartsInfo: SmartsInfo]
];
Registration/Deregistration/Initialization
Register: PUBLIC PROC[
shh: SHHH, -- encrypts connection
oldSmartsID: Thrush.SmartsID,
oldEpoch: Thrush.Epoch,
machine: Lark.Machine, -- machine name for registering Lark --
model: Lark.LarkModel,
authenticated: BOOLFALSE,
clientInstance: ROPE
] RETURNS [ smartsID: Thrush.SmartsID←nullID, epoch: Thrush.Epoch←Thrush.epoch ] = {
This procedure is synchronous with the Lark. We do enough to get started, then serialize the actual registration action with input and fail events.
larkSh: Lark.SHHH;
smartsInfo: SmartsInfo;
larkInfo: LarkInfo;
inputQueue: MBQueue.Queue;
larkInterface: LarkOpsRpcControl.InterfaceRecord←NIL;
rName: ROPERPC.GetCaller[shh];
serviceName: ROPE;
partyType: Thrush.PartyType ← $telephone;
s: VoiceUtils.Rspec ← NIL;
netAddress: Thrush.NetAddress = [machine.net, machine.host, Pup.nullSocket];
The LarkSmarts interface definition suggests that a Lark could present enough information during registration to be simply re-validated (kind of like a hand stamp at a dance) without any great effort. That turns out not to be worth it. If a Lark registers when already registered, we get rid of any old information about it first. Don't assume that its type has not changed from $telephone to $service or vice/versa
larkInfo ← LarkInfoForNetAddress[netAddress].info;
IF larkInfo#NIL AND ~larkInfo.failed THEN {
inputQueue ← larkInfo.inputQueue;
ThSmartsPrivate.Fail[larkInfo, "Lark is reregistering", FALSE]; -- already rebooted
}
ELSE inputQueue ← MBQueue.Create[];
Develop rName: the actual RName to be used to represent this party, given one that might be of the form Name.reg or Name.reg1.reg2. The latter is converted to Name.reg1. Also, obtain the name of the service represented by this Rname, if any, from the database.
IF (s←VoiceUtils.RnameToRspec[rName])#NIL
AND VoiceUtils.RnameToRspec[s.simpleName] # NIL THEN rName←s.simpleName;
Above converts from, say, Swinehart.pa.lark to Swinehart.pa.
Also, from Swinehart.pa to Swinehart.pa. Strips one registry if there are two or more.
serviceName ← NameDB.GetAttribute[rName, $service];
IF serviceName#NIL THEN partyType ← $service;
Get encryption taken care of
larkSh ← IF NOT ThNet.pd.encryptionRequested THEN Thrush.unencrypted
ELSE NamesRPC.StartConversation [
caller: myName.instance,
callee: rName,
key: serverPassword,
level: --<<ECB>>--CBCCheck ! RPC.AuthenticateFailed=> {
VoiceUtils.ProblemFR["Can't authenticate %g to %g", $System, NIL,
rope[myName.instance], rope[rName]]; GOTO NotSmart; }];
Make sure we can talk to the Lark
larkInterface ← LarkOpsRpcControl.ImportNewInterface[
interfaceName: [type: "Lark.Lark", instance: clientInstance] ! RPC.ImportFailed=> {
VoiceUtils.ProblemFR["Can't import Lark interface from %g", $System, NIL, rope[clientInstance]]; GOTO NotSmart; }];
larkInfo ← NEW[ThSmartsPrivate.LarkInfoBody ← [
interface: larkInterface,
shh: larkSh,
netAddress: netAddress,
model: model,
inputQueue: inputQueue,
textToSpeech: Rope.Equal[serviceName, "Text-to-Speech", FALSE],
scratchEv: NEW[Lark.CommandEventSequence[15]]
]];
smartsID ← ThPartyPrivate.AssignSmartsID[];
smartsInfo ← NEW[SmartsInfoBody← [
smartsID: smartsID,
partyType: partyType,
ParseEvent: ThSmartsPrivate.LarkParseEvent,
NoteNewStateP: ThSmartsPrivate.NoteNewState,
requests: MBQueue.Create[],
larkInfo: larkInfo
]];
inputQueue.QueueClientAction[
QdLarkRegistration, NEW[RegistrationInfoSpec ←
[smartsInfo, rName, serviceName, clientInstance]]];
EXITS
NotSmart => RETURN;
};
RegistrationInfo: TYPE = REF RegistrationInfoSpec;
RegistrationInfoSpec: TYPE = RECORD [
smartsInfo: SmartsInfo,
rName: ROPE,
serviceName: ROPE,
clientInstance: ROPE
];
QdLarkRegistration: ENTRY PROC[r:REF] = {
ENABLE UNWIND => NULL;
nb: Thrush.NB;
registrationInfo: RegistrationInfo ← NARROW[r];
smartsInfo: SmartsInfo ← registrationInfo.smartsInfo;
larkInfo: LarkInfo ← smartsInfo.larkInfo;
credentials: Thrush.Credentials;
smartsID: Thrush.SmartsID;
smarts: REF;
localSmarts: ThParty.SmartsInterfaceRecord;
localSmarts ← ThSmartsRpcControl.NewInterfaceRecord[];
localSmarts.clientStubProgress ← ThSmartsPrivate.LarkProgress;
localSmarts.clientStubSubstitution ← ThSmartsPrivate.LarkSubstitution;
localSmarts.clientStubReportAction ← ThSmartsPrivate.LarkReportAction;
localSmarts.clientStubCheckIn ← ThSmartsPrivate.LarkCheckIn;
[nb, credentials] ← ThPartyPrivate.RegisterLocal[
rName: (SELECT smartsInfo.partyType FROM
$telephone => registrationInfo.rName,
ENDCASE=> registrationInfo.serviceName),
type: smartsInfo.partyType,
interfaceRecord: localSmarts,
smartsID: smartsInfo.smartsID, -- pre-allocated so Lark could know.
properties: [role: $voiceTerminal, netAddress: larkInfo.netAddress]
];
IF nb # $success OR (smartsID𡤌redentials.smartsID) = nullID THEN {
VoiceUtils.ProblemFR["Can't register Lark: %g, %g",$System,NIL,
rope[registrationInfo.rName], rope[registrationInfo.clientInstance]]; GOTO NotSmart; };
No further bad things are expected to happen.
larkInfo.larkSmartsInfo ← smartsInfo;
smarts ← RefID.Unseal[smartsID];
IF smarts=NIL THEN ERROR;
Triples.Make[$SmartsData, smarts, smartsInfo];
IF smartsInfo.partyType # $service THEN
[--nb--, smartsInfo.otherSmartsID]←
ThSmartsPrivate.RegisterTrunk[ smartsID, smartsInfo, registrationInfo.rName ];
VoiceUtils.ReportFR[" (%g = %g)", $Smarts, smartsInfo, rope[registrationInfo.clientInstance], TU.RefAddr[smarts]];
FOR pSP: ServiceProvidersList ← potentialServiceProviders, pSP.rest WHILE pSP#NIL DO
pSP.first.potentialServiceProvider[credentials, smartsInfo];
If anyone has registered, call them. They may want to register a service with our party. See LarkSmartsSynthImpl for an example.
ENDLOOP;
RegisterLarkInfo[larkInfo];
Subsequent input events must relate to new world.
NoteApplicationState[larkInfo, 'r];
[]←ThSmartsPrivate.CheckHookState[larkInfo];
If the phone is on-hook, mark us ready to go, and set up any once-only Lark params.
EXITS
NotSmart => RETURN;
};
EnableSmarts: PUBLIC ENTRY PROC[r: REF] = {
ENABLE UNWIND => NULL;
larkInfo: LarkInfo ← NARROW[r];
debugging: BOOL;
smartsInfo: SmartsInfo ← larkInfo.larkSmartsInfo;
IF larkInfo.failed THEN RETURN; -- Lost a race
IF ThParty.Enable[ smartsID: smartsInfo.smartsID] # $success
OR (smartsInfo.otherSmartsID # nullID AND
ThParty.Enable[smartsID: smartsInfo.otherSmartsID] # $success) THEN {
VoiceUtils.Problem["Could not enable", $Smarts, smartsInfo];
ThSmartsPrivate.Fail[larkInfo, "Lark failure due to failure to enable party", FALSE];
Notification would loop
};
ThSmartsPrivate.EnterLarkState[ larkInfo, idle ]; -- Reset the Lark
We're debugging if the lark's mode field in the database has the value "D" or "d"
debugging ← Rope.Equal[case: FALSE, s2: "D",
s1: NameDB.GetAttribute[
VoiceUtils.InstanceFromNetAddress[larkInfo.netAddress, NIL], $mode, "O", $larkhost]];
larkInfo.debugging ← debugging;
ThSmartsPrivate.SetupTimeouts[ larkInfo, debugging ]; -- Reset the Lark
NoteApplicationState[larkInfo, 'R];
};
Deregister: PUBLIC ENTRY PROC[r: REF] = {
Serialized with input events. Called (queued) only from LarkOutImpl.Fail.
larkInfo: LarkInfo ← NARROW[r];
smartsID: SmartsID ← nullID;
otherSmartsID: SmartsID ← nullID;
smartsInfo: SmartsInfo ← IF larkInfo#NIL THEN larkInfo.larkSmartsInfo ELSE NIL;
smarts: REF;
IF smartsInfo = NIL THEN {
VoiceUtils.Problem["No Smarts to Deregister", $System];
RETURN;
};
NoteApplicationState[larkInfo, 'U];
smartsID ← smartsInfo.smartsID;
smarts ← ThPartyPrivate.UnsealSmarts[smartsID];
VoiceUtils.ReportFR["Smarts %d (%g) is dead", $Smarts, smartsInfo,
card[Reseal[smarts]], TU.RefAddr[smarts]];
smartsInfo.failed ← TRUE;
smartsInfo.larkInfo.larkSmartsInfo ← NIL;
IF smartsInfo.larkInfo.larkTrunkSmartsInfo#NIL THEN {
smartsInfo.larkInfo.larkTrunkSmartsInfo.failed ← TRUE;
smartsInfo.larkInfo.larkTrunkSmartsInfo.larkInfo ← NIL;
};
smartsInfo.larkInfo.larkTrunkSmartsInfo ← NIL;
smartsInfo.larkInfo ← NIL;
otherSmartsID ← smartsInfo.otherSmartsID;
IF otherSmartsID # nullID THEN [--nb--]←ThParty.Deregister[smartsID: otherSmartsID];
[--nb--]←ThParty.Deregister[smartsID: smartsID];
smartsInfo.requests.Flush[]; -- Abandon progress-notification queue
};
NoteApplicationState: INTERNAL PROC[info: LarkInfo, state: CHAR] = {
LarkControl.SetApplicationMode[LarkControl.GetLark[info.netAddress], state];
};
Login: PUBLIC PROC[shh: SHHH←, smartsID: SmartsID←, authenticated: BOOLTRUE] = { NULL;};
Not implemented
Other Utilities
WhereIsSmartsLog: VoiceUtils.WhereProc -- [fixedWhereData: REF ANY, whereData: REF ANY] RETURNS [s: STREAM ← NIL] -- = CHECKED {
info: SmartsInfo=NARROW[whereData];
larkInfo: LarkInfo = IF info#NIL THEN info.larkInfo ELSE NIL;
IF larkInfo#NIL THEN s←Nice.LarkConLogStream[larkInfo.netAddress];
IF s#NIL AND s=Nice.LarkConLogStream[Thrush.noAddress] THEN s←NIL; -- use default stream unless debug viewer stream open.
};
WhereIsLarkLog: VoiceUtils.WhereProc -- [fixedWhereData: REF ANY, whereData: REF ANY] RETURNS [s: STREAM ← NIL] -- = CHECKED {
info: ThSmartsPrivate.LarkInfo=NARROW[whereData];
IF info#NIL THEN s←Nice.LarkConLogStream[info.netAddress];
IF s#NIL AND s=Nice.LarkConLogStream[Thrush.noAddress] THEN s←NIL; -- only want the debug viewer stream
};
WhereIsLarkLogFerSherr: VoiceUtils.WhereProc ← WhereIsLarkLog;
FerSherrDefault: VoiceUtils.DNFProc=TRUSTED{ RETURN[ThNet.pd.defaultLarkReports]; };
Initialization, export "LarkSmarts"
KillLark: Commander.CommandProc = {
netAddress: Thrush.NetAddress =
[[173B],[Convert.IntFromRope[CommandTool.NextArgument[cmd], 8]], Pup.nullSocket];
larkInfo: LarkInfo ← LarkInfoForNetAddress[netAddress].info;
IF larkInfo=NIL THEN RETURN;
ThSmartsPrivate.Fail[larkInfo, "Lark failed through operator request", TRUE];
};
myName: LarkSmartsRpcControl.InterfaceName;
serverPassword: RPC.EncryptionKey;
LarkSmartsInit: Commander.CommandProc = {
ENABLE {
RPC.ExportFailed => { VoiceUtils.Problem["LarkSmarts export failed", $System]; GOTO Failed; };
};
instance: ROPE = VoiceUtils.MakeRName[style: rName, name:
VoiceUtils.CmdOrToken[cmd: cmd, key: "ThrushServerInstance", default: "Strowger.Lark"]];
serverPassword ← VoiceUtils.CurrentPasskey[VoiceUtils.CmdOrToken[
cmd: cmd, key: "ThrushServerPassword", default: "MFLFLX"]];
myName ← [
type: "LarkSmarts.Lark",
instance: instance,
version: ThVersions.ThrushVR
];
VoiceUtils.RegisterWhereToReport[proc: WhereIsLarkLog, where: $Lark];
VoiceUtils.RegisterWhereToReport[proc: WhereIsLarkLogFerSherr, where: $LarkDetailed, defaultIfNotFound: FerSherrDefault];
Same as $Lark, except doesn't print at all if debugging viewer not found.
VoiceUtils.RegisterWhereToReport[proc: WhereIsSmartsLog, where: $Smarts];
LarkSmartsRpcControl.UnexportInterface[!LupineRuntime.BindingError=>CONTINUE];
LarkSmartsRpcControl.ExportInterface[
interfaceName: myName,
user: instance,
password: serverPassword
];
VoiceUtils.ReportFR["Export[LarkSmarts.Lark, %s]", $System, NIL, rope[myName.instance]];
EXITS
Failed =>
LarkSmartsRpcControl.UnexportInterface[!LupineRuntime.BindingError=>CONTINUE];
};
RegisterServiceProvider: PUBLIC PROC[
actionClass: Thrush.ActionClass, -- to prevent duplicates
serviceProvider: PROC[credentials: Thrush.Credentials, smartsInfo: SmartsInfo]
] = {
See LarkSmartsSynthImpl for an (the) example. See Register (above) for invocation of potentialServiceProvider.
RemoveServiceProvider[actionClass];
potentialServiceProviders ← CONS[[actionClass, serviceProvider], potentialServiceProviders];
};
RemoveServiceProvider: PROC[actionClass: Thrush.ActionClass] = {
IF potentialServiceProviders = NIL THEN RETURN;
IF potentialServiceProviders.first.actionClass=actionClass THEN
{ potentialServiceProviders ← potentialServiceProviders.rest; RETURN; };
FOR pSP: ServiceProvidersList ← potentialServiceProviders, pSP.rest WHILE pSP.rest#NIL DO
IF pSP.rest.first.actionClass = actionClass THEN {
pSP.rest ← pSP.rest.rest; RETURN; };
ENDLOOP;
};
RegisterLarkInfo: PROC[info: LarkInfo] = {
index: CARD ← LarkInfoForNetAddress[info.netAddress].index;
[] ← larkInfos.Store[index, info];
};
LarkInfoForNetAddress: PROC[netAddress: Thrush.NetAddress]
RETURNS[info: LarkInfo, index: CARD] = TRUSTED {
netAddress.socket ← Pup.nullSocket; -- only interested in host values
index ← LOOPHOLE[LONG[@netAddress], LONG POINTER TO CARD]^;
IF larkInfos=NIL THEN {
larkInfos ← CardTab.Create[mod: 59];
TRUSTED { Process.Detach[FORK LarkWatchDogTimerProcess[]]; };
};
info ← NARROW[larkInfos.Fetch[index].val];
};
LarkWatchDogTimerProcess: PROC = {
Polls active Larks at intervals, so that non-working ones can be discovered and removed from registered service quickly. This happens whether the Larks are involved in conversations or not.
LarkWatchDogTimer: CardTab.EachPairAction = {
[key: CardTab.Key, val: CardTab.Val] RETURNS [quit: BOOLEAN]
info: LarkInfo = NARROW[val];
IF info=NIL OR info.failed THEN RETURN[FALSE];
ThSmartsPrivate.QueueLarkAction[info, NEW[BOOLTRUE]];
Special case requesting simplest status check. If Lark doesn't return, will trigger
failure of LarkSmarts.
Process.Pause[Process.SecondsToTicks[ThNet.pd.pollPartiesInterval]];
RETURN[FALSE];
};
DO
IF ThNet.pd.pollParties THEN [] ← larkInfos.Pairs[LarkWatchDogTimer];
Process.Pause[Process.SecondsToTicks[ThNet.pd.pollPartiesInterval]];
ENDLOOP;
};
Commander.Register["LarkSmarts", LarkSmartsInit, "LarkSmarts <ExportInstance[Strowger]> <ServerPassword[...]>\nInitialize and Export LarkSmarts"];
Commander.Register["KillLark", KillLark, "KillLark 110 -- deregisters lark 110"];
}.
Swinehart, May 22, 1985 5:03:50 pm PDT
Cedar 6.0
New ways of defining service parties. autoAnswer (formerly hotLine), radio, and textToSpeech attributes obtained from data base. Text-To-Speech service contemplated.
Deregistration works in the face of duplicate service names, etc.
changes to: DIRECTORY, Register, DO
changes to: DIRECTORY, Register, DO, DeregisterIfRegistered, ConsiderOne (local of DeregisterIfRegistered), Deregister
Swinehart, October 28, 1985 9:55:38 am PST
Names, Log => VoiceUtils, Handle => ID
changes to: DIRECTORY, LarkSmartsInitImpl, ConversationID, Reseal, nullID, PartyID, SmartsID, Register, EnableSmarts, ConsiderOne (local of DeregisterIfRegistered), Deregister, SimpleDeregister, Login
Swinehart, November 8, 1985 3:32:57 pm PST
Accommodate new ThParty implementation
changes to: DIRECTORY, Reseal, SHHH, Register, interfaceName, EnableSmarts, DeregisterIfRegistered, Deregister
Swinehart, May 17, 1986 5:57:14 pm PDT
Cedar 6.1
changes to: DIRECTORY, WhereIsSmartsLog, WhereIsLarkLog, KillLark
Swinehart, May 25, 1986 10:09:37 pm PDT
Lark => LarkOps
changes to: DIRECTORY, LarkSmartsInitImpl, Register
Swinehart, June 21, 1987 10:48:03 pm PDT
Remove LarkWatchDogTimer stuff, in favor of more general CheckIn code in SupImpl
changes to: DIRECTORY, LarkSmartsInitImpl, LarkInfoForNetAddress