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. 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; potentialServiceProviders: ServiceProvidersList; ServiceProvidersList: TYPE = LIST OF ServiceProviderRecord; ServiceProviderRecord: TYPE = RECORD [ actionClass: Thrush.ActionClass, potentialServiceProvider: PROC[credentials: Thrush.Credentials, smartsInfo: SmartsInfo] ]; 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: BOOL_FALSE, clientInstance: ROPE ] RETURNS [ smartsID: Thrush.SmartsID_nullID, epoch: Thrush.Epoch_Thrush.epoch ] = { larkSh: Lark.SHHH; smartsInfo: SmartsInfo; larkInfo: LarkInfo; inputQueue: MBQueue.Queue; larkInterface: LarkOpsRpcControl.InterfaceRecord_NIL; rName: ROPE _ RPC.GetCaller[shh]; serviceName: ROPE; partyType: Thrush.PartyType _ $telephone; s: VoiceUtils.Rspec _ NIL; netAddress: Thrush.NetAddress = [machine.net, machine.host, Pup.nullSocket]; 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[]; IF (s_VoiceUtils.RnameToRspec[rName])#NIL AND VoiceUtils.RnameToRspec[s.simpleName] # NIL THEN rName_s.simpleName; serviceName _ NameDB.GetAttribute[rName, $service]; IF serviceName#NIL THEN partyType _ $service; larkSh _ IF NOT ThNet.pd.encryptionRequested THEN Thrush.unencrypted ELSE NamesRPC.StartConversation [ caller: myName.instance, callee: rName, key: serverPassword, level: --<>--CBCCheck ! RPC.AuthenticateFailed=> { VoiceUtils.ProblemFR["Can't authenticate %g to %g", $System, NIL, rope[myName.instance], rope[rName]]; GOTO NotSmart; }]; 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_credentials.smartsID) = nullID THEN { VoiceUtils.ProblemFR["Can't register Lark: %g, %g",$System,NIL, rope[registrationInfo.rName], rope[registrationInfo.clientInstance]]; GOTO NotSmart; }; 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]; ENDLOOP; RegisterLarkInfo[larkInfo]; NoteApplicationState[larkInfo, 'r]; []_ThSmartsPrivate.CheckHookState[larkInfo]; 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]; }; ThSmartsPrivate.EnterLarkState[ larkInfo, idle ]; -- Reset the Lark 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] = { 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: BOOL_TRUE] = { NULL;}; 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]; }; 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]; 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] ] = { 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]; }; info _ NARROW[larkInfos.Fetch[index].val]; }; Commander.Register["LarkSmarts", LarkSmartsInit, "LarkSmarts \nInitialize and Export LarkSmarts"]; Commander.Register["KillLark", KillLark, "KillLark 110 -- deregisters lark 110"]; }. ˜LarkSmartsInitImpl.mesa Copyright c 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 Copies 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. Registration/Deregistration/Initialization This procedure is synchronous with the Lark. We do enough to get started, then serialize the actual registration action with input and fail events. 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 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. 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. Get encryption taken care of Make sure we can talk to the Lark No further bad things are expected to happen. If anyone has registered, call them. They may want to register a service with our party. See LarkSmartsSynthImpl for an example. Subsequent input events must relate to new world. If the phone is on-hook, mark us ready to go, and set up any once-only Lark params. Notification would loop We're debugging if the lark's mode field in the database has the value "D" or "d" Serialized with input events. Called (queued) only from LarkOutImpl.Fail. Not implemented Other Utilities Initialization, export "LarkSmarts" Same as $Lark, except doesn't print at all if debugging viewer not found. See LarkSmartsSynthImpl for an (the) example. See Register (above) for invocation of potentialServiceProvider. TRUSTED { Process.Detach[FORK LarkWatchDogTimerProcess[]]; }; 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[BOOL_TRUE]]; 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; }; 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 Κ9˜šœ™Icodešœ Οmœ7™BJšœ<™Jšœ žœ!˜2Jšœžœ)˜@J˜ J˜Jšœžœ5˜OJ˜Jšœžœ-˜:Jšœžœ˜Jšœ žœ˜%Jšœžœ˜ Jšœžœ˜Jšœžœžœ˜#J˜JšžœžœN˜WJšœžœ˜Jšœžœ1˜EJšœ<˜J˜Jš’œžœž’˜Jšœž˜JšœžœK˜TJ™”Jšœ žœ˜J˜J˜J˜Jšœ1žœ˜5Jšœžœžœ˜!Jšœ žœ˜J˜)Jšœžœ˜J˜LJ˜J™£J˜2šžœ žœžœžœ˜+J˜!Jšœ8žœŸ˜SJ˜—Jšžœ˜#J˜J™†šžœ$ž˜)Jšžœ)žœ˜HJ™J˜FJ˜FJ˜<šœ1˜1šœžœž˜(Jšœ%˜%Jšžœ!˜(—J˜J˜JšœŸ$˜CJšœC˜CJšœ˜—šžœžœ*žœ˜CJšœ;žœ˜?JšœFžœ ˜W—Jšœ-™-Jšœ%˜%Jšœ ˜ Jšžœžœžœžœ˜Jšœ.˜.šžœ!ž˜'šœŸœ˜#JšœN˜N——Jšœr˜ršžœAžœžœž˜TJšœ#‘ œ ˜˜>šœ$žœžœ!˜TJ˜——šœ#™#J˜š œ˜#˜J˜Q—J˜žœ˜H—šžœAžœ žœž˜Yšžœ*žœ˜2Jšœžœ˜$—Jšžœ˜—J˜J˜—š œžœ˜*Jšœžœ0˜;J˜"J˜J˜—š œžœ˜:Jšžœžœžœ˜0Jšœ$Ÿ!˜EJš œžœžœžœžœžœžœ˜;˜J˜$Jšžœžœ ™=J˜—Jšœžœ˜*J˜J˜—š œžœ™"J™Ύ–D -- [key: CardTable.Key, val: CardTable.Val] RETURNS [quit: BOOLEAN]š œ™-Jšœ%žœžœ™