LarkSmartsInitImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Last modified by D. Swinehart, June 1, 1986 7:34:37 pm PDT
Last Edited by: Pier, May 3, 1984 2:52:59 pm PDT
DIRECTORY
Atom USING [ GetPName ],
Commander USING [ CommandProc, Register ],
CommandTool USING [ NextArgument ],
Convert USING [ IntFromRope ],
IO,
Lark USING [ CommandEventSequence, LarkModel, Machine, SHHH ],
LarkOpsRpcControl USING [ ImportNewInterface, InterfaceRecord ],
LarkSmarts,
LarkSmartsMonitorImpl,
LarkSmartsRpcControl USING [ExportInterface, InterfaceName, UnexportInterface],
LupineRuntime,
MBQueue USING [ Create, Flush ],
NamesGV USING [ GVGetAttribute ],
NamesRPC USING [ StartConversation ],
Nice USING [ LarkConLogStream ],
Pup USING [ nullSocket ],
RefID USING [ ID, Reseal, Unseal ],
Rope USING [Concat, Equal],
RPC USING [ AuthenticateFailed, EncryptionKey, ExportFailed, GetCaller, ImportFailed ],
ThNet USING [ pd ],
ThPartyPrivate USING [ RegisterLocal ],
ThParty USING [ Deregister, Enable, SmartsInterfaceRecord ],
Thrush USING [
Credentials, epoch, Epoch, NB, NetAddress, noAddress, nullID, PartyID, PartyType, ROPE, SmartsID, unencrypted ],
ThSmartsPrivate USING [
CheckHookState, EnterLarkState, LarkFailed, LarkInfo, LarkInfoBody, LarkParseEvent, LarkProgress, LarkReportAction, LarkSubstitution, NoteNewState, RegisterTrunk, SmartsInfo, SmartsInfoBody ],
ThSmartsRpcControl,
ThVersions,
Triples USING [ Any, Make, Select ],
TU,
VoiceUtils USING [ CmdOrToken, CurrentPasskey, DNFProc, InstanceFromNetAddress, MakeAtom, MakeRName, Problem, ProblemFR, RegisterWhereToReport, ReportFR, RnameToRspec, Rspec, WhereProc ]
;
LarkSmartsInitImpl: CEDAR MONITOR LOCKS root
IMPORTS IO,
Atom,
Commander,
CommandTool,
Convert,
LarkOpsRpcControl,
LarkSmartsRpcControl,
NamesGV,
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;
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
SmartsID: TYPE = Thrush.SmartsID;
SmartsInfo: TYPE = ThSmartsPrivate.SmartsInfo;
SmartsInfoBody: TYPE = ThSmartsPrivate.SmartsInfoBody;
larkRegistry: ROPE←".lark";
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 ] = {
larkSh: Lark.SHHH;
localSmarts: ThParty.SmartsInterfaceRecord;
info: SmartsInfo;
larkInterface: LarkOpsRpcControl.InterfaceRecord←NIL;
smarts: REF;
fullRname: ROPE = RPC.GetCaller[shh];
partyRname: ROPE ← fullRname;
dbRname: ROPE;
serviceName: ROPE;
partyType: Thrush.PartyType ← $telephone;
s: VoiceUtils.Rspec ← NIL;
nb: Thrush.NB;
credentials: Thrush.Credentials;
netAddress: Thrush.NetAddress = [machine.net, machine.host, Pup.nullSocket];
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;
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
DeregisterIfRegistered[netAddress, $telephone];
DeregisterIfRegistered[netAddress, $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; }];
localSmarts ← ThSmartsRpcControl.NewInterfaceRecord[];
localSmarts.clientStubProgress ← ThSmartsPrivate.LarkProgress;
localSmarts.clientStubSubstitution ← ThSmartsPrivate.LarkSubstitution;
localSmarts.clientStubReportAction ← ThSmartsPrivate.LarkReportAction;
[nb, credentials] ← ThPartyPrivate.RegisterLocal[
rName: (SELECT partyType FROM
$telephone => partyRname,
ENDCASE=> serviceName),
type: partyType,
interfaceRecord: localSmarts,
properties: [role: $voiceTerminal, netAddress: netAddress]
];
IF nb # $success OR (smartsID𡤌redentials.smartsID) = nullID THEN {
VoiceUtils.ProblemFR["Can't register Lark: %g, %g",$System,NIL,
rope[fullRname], rope[clientInstance]]; GOTO NotSmart; };
No further bad things are expected to happen.
smarts ← RefID.Unseal[smartsID];
IF smarts=NIL THEN ERROR;
info ← NEW[SmartsInfoBody← [
smartsID: smartsID,
partyType: partyType,
ParseEvent: ThSmartsPrivate.LarkParseEvent,
NoteNewStateP: ThSmartsPrivate.NoteNewState,
requests: MBQueue.Create[],
larkInfo: NEW[ThSmartsPrivate.LarkInfoBody ← [
interface: larkInterface,
shh: larkSh,
netAddress: netAddress,
model: model,
textToSpeech: Rope.Equal[serviceName, "Text-to-Speech", FALSE]
]] ]];
info.larkInfo.larkSmartsInfo ← info;
info.larkInfo.scratchEv ← NEW[Lark.CommandEventSequence[15]];
Triples.Make[$SmartsData, smarts, info];
IF partyType # $service THEN
[--nb--, info.otherSmartsID]←ThSmartsPrivate.RegisterTrunk[ smartsID, info, partyRname ];
VoiceUtils.ReportFR[" (%g = %g)", $Smarts, info, rope[clientInstance], TU.RefAddr[smarts]];
EnableSmartsE[info]; -- Ready to go, as long as phone is on hook
EXITS
NotSmart => RETURN;
};
EnableSmartsE: ENTRY PROC[info: SmartsInfo] = { []𡤎nableSmarts[info]; };
EnableSmarts: PUBLIC INTERNAL PROC[info: SmartsInfo] RETURNS[enabled: BOOL] = {
ENABLE UNWIND, ThSmartsPrivate.LarkFailed =>GOTO Failed;
SELECT info.larkInfo.larkState FROM
$none, $failed, $recovering => NULL;
ENDCASE=> RETURN[TRUE];
IF ThSmartsPrivate.CheckHookState[info.larkInfo].onHook=FALSE THEN RETURN[FALSE];
IF ThParty.Enable[ smartsID: info.smartsID] # $success THEN {
VoiceUtils.Problem["Could not enable", $Smarts, info]; RETURN[FALSE]; };
IF ThParty.Enable[ smartsID: info.otherSmartsID] # $success THEN
{ VoiceUtils.Problem["Impossible", $Smarts, info]; ERROR; };
ThSmartsPrivate.EnterLarkState[ info.larkInfo, none ]; -- Clear any previous failure.
ThSmartsPrivate.EnterLarkState[ info.larkInfo, idle ]; -- Reset the Lark
RETURN[TRUE];
EXITS Failed => RETURN[FALSE];
};
DeregisterIfRegistered: ENTRY PROC[
netAddress: Thrush.NetAddress, partyType: Thrush.PartyType] = {
If registered, undo everything at the smarts level and below, then deregister at the Party level
hostAtom: ATOM = VoiceUtils.MakeAtom[VoiceUtils.InstanceFromNetAddress[netAddress,
Atom.GetPName[partyType]]];
smarts: REF←Triples.Select[$SmartsForHost, hostAtom, -- smarts --];
IF smarts=NIL THEN RETURN;
Deregister[Reseal[smarts]];
};
Deregister: PUBLIC INTERNAL PROC[smartsID: SmartsID] = {
smarts: REF ← RefID.Unseal[smartsID];
otherSmartsID: SmartsID ← nullID;
info: SmartsInfo ← IF smarts=NIL THEN NIL
ELSE NARROW[Triples.Select[$SmartsData, smarts, -- smartsInfo -- ]];
VoiceUtils.ReportFR["Smarts %d (%g) is dead", $Smarts, info,
card[Reseal[smarts]], TU.RefAddr[smarts]];
IF info#NIL THEN {
otherSmartsID ← info.otherSmartsID;
IF info.larkInfo#NIL THEN {
ThSmartsPrivate.EnterLarkState[ info.larkInfo, failed ]; -- prevent further action.
info.larkInfo.larkSmartsInfo ← NIL;
IF info.larkInfo.larkTrunkSmartsInfo#NIL THEN
info.larkInfo.larkTrunkSmartsInfo.larkInfo ← NIL;
info.larkInfo.larkTrunkSmartsInfo ← NIL;
info.larkInfo ← NIL;
};
}
ELSE
VoiceUtils.ProblemFR["Smarts %d (%g) has no SmartsInfo", $System, NIL, card[Reseal[smarts]], TU.RefAddr[smarts]];
IF otherSmartsID # nullID THEN [--nb--]←ThParty.Deregister[smartsID: otherSmartsID];
[--nb--]←ThParty.Deregister[smartsID: smartsID];
IF info#NIL THEN info.requests.Flush[]; -- Abandon progress-notification queue
};
Other Utilities
WhereIsSmartsLog: VoiceUtils.WhereProc -- [fixedWhereData: REF ANY, whereData: REF ANY] RETURNS [s: STREAM ← NIL] -- = CHECKED {
info: SmartsInfo=NARROW[whereData];
IF info#NIL THEN s←Nice.LarkConLogStream[info.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];
DeregisterIfRegistered[netAddress, $telephone];
DeregisterIfRegistered[netAddress, $service];
};
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];
};
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