LarkSmartsInitImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Last modified by D. Swinehart, December 17, 1986 10:39:25 pm PST
Last Edited by: Pier, May 3, 1984 2:52:59 pm PDT
DIRECTORY
CardTable USING [ Create, EachPairAction, Fetch, Pairs, 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 ],
NamesGV USING [ GVGetAttribute ],
NamesRPC USING [ StartConversation ],
Nice USING [ LarkConLogStream ],
Process USING [ Detach, Pause, SecondsToTicks ],
Pup USING [ nullSocket ],
RefID USING [ ID, Reseal, Unseal ],
Rope USING [Concat, Equal],
RPC USING [ AuthenticateFailed, EncryptionKey, ExportFailed, GetCaller, ImportFailed ],
ThNet USING [ pd ],
ThPartyPrivate USING [ AssignSmartsID, RegisterLocal, UnsealSmarts ],
ThParty USING [ Deregister, Enable, SmartsInterfaceRecord ],
Thrush USING [
Credentials, epoch, Epoch, NB, NetAddress, noAddress, nullID, PartyID, PartyType, ROPE, SmartsID, unencrypted ],
ThSmartsPrivate USING [
CheckHookState, EnterLarkState, Fail, LarkInfo, LarkInfoBody, LarkParseEvent, LarkProgress, LarkReportAction, LarkSubstitution, NoteNewState, QueueLarkAction, 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,
CardTable,
Commander,
CommandTool,
Convert,
LarkOpsRpcControl,
LarkControl,
LarkSmartsRpcControl,
NamesGV,
NamesRPC,
root: LarkSmartsMonitorImpl,
LupineRuntime,
MBQueue,
Nice,
Process,
RefID,
Rope,
RPC,
ThNet,
ThPartyPrivate,
ThParty,
Thrush,
ThSmartsPrivate,
ThSmartsRpcControl,
ThVersions,
Triples,
TU,
VoiceUtils
EXPORTS LarkSmarts, ThSmartsPrivate
SHARES LarkSmartsMonitorImpl = {
OPEN IO;
larkInfos: CardTable.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;
larkRegistry: ROPE←".lark";
Limited registration for supporting service registration. This handles only one service that would like to register with LarkSmarts parties (intended for synthesizer). If multiple registrations became necessary, would have to extend this.
See LarkSmartsInitImpl for the point of invocation, LarkSmartsSynthImpl for an example of use.
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;
fullRname: ROPE = RPC.GetCaller[shh];
partyRname: ROPE ← fullRname;
dbRname: ROPE;
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 partyRname: the actual RName to be used to represent this party; and dbRname: the name under which database items are stored in Grapevine. The rName obtained from the conversation handle may have either form initially, since Finches will supply the former and Larks will supply the latter. Also, obtain the name of the service represented by this Rname, if any, from the database.
IF (s←VoiceUtils.RnameToRspec[partyRname])#NIL
AND VoiceUtils.RnameToRspec[s.simpleName] # NIL THEN partyRname←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.
dbRname ← partyRname.Concat[larkRegistry];
serviceName ← NamesGV.GVGetAttribute[dbRname, $service, NIL];
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: fullRname,
key: serverPassword,
level: --<<ECB>>--CBCCheck ! RPC.AuthenticateFailed=> {
VoiceUtils.ProblemFR["Can't authenticate %g to %g", $System, NIL,
rope[myName.instance], rope[fullRname]]; 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, partyRname, serviceName, fullRname, clientInstance]]];
EXITS
NotSmart => RETURN;
};
RegistrationInfo: TYPE = REF RegistrationInfoSpec;
RegistrationInfoSpec: TYPE = RECORD [
smartsInfo: SmartsInfo,
partyRname: ROPE,
serviceName: ROPE,
fullRname: 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;
[nb, credentials] ← ThPartyPrivate.RegisterLocal[
rName: (SELECT smartsInfo.partyType FROM
$telephone => registrationInfo.partyRname,
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.fullRname], 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.partyRname ];
VoiceUtils.ReportFR[" (%g = %g)", $Smarts, smartsInfo, rope[registrationInfo.clientInstance], TU.RefAddr[smarts]];
IF potentialServiceProvider#NIL THEN potentialServiceProvider[credentials, smartsInfo];
If anyone has registered, call them. They may want to register a service with our party. See LarkSmartsSynthImpl for an (the) example.
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: NamesGV.GVGetAttribute[
VoiceUtils.InstanceFromNetAddress[larkInfo.netAddress, larkRegistry], $mode, "O"]];
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[
serviceProvider: PROC[credentials: Thrush.Credentials, smartsInfo: SmartsInfo]] = {
See LarkSmartsSynthImpl for an (the) example. See Register (above) for invocation of potentialServiceProvider.
potentialServiceProvider ← serviceProvider;
};
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 ← CardTable.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: CardTable.EachPairAction = {
[key: CardTable.Key, val: CardTable.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.larkWatchDogTimerInterval]];
RETURN[FALSE];
};
DO
IF ThNet.pd.runLarkWatchDogTimer THEN [] ← larkInfos.Pairs[LarkWatchDogTimer];
Process.Pause[Process.SecondsToTicks[1]];
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