DIRECTORY Atom USING [ DottedPairNode, GetPropFromList, PutPropOnList ], Commander USING [ CommandProc, Register ], IO, Lark -- USING [ -- CommandEvents, ConnectionSpec, Event, LarkModel, Passel, StatusEvents ]--, LarkFeep, LarkFeepRpcControl USING [ ExportInterface, InterfaceRecord, InterfaceRecordObject ], LarkSmartsMonitorImpl, MBQueue USING [ Create, QueueClientAction ], Nice, Process USING [ Detach, SecondsToTicks, SetTimeout ], RefID USING [ ID, nullID, Reseal, Seal, Unseal ], Rope USING [ ROPE ], RPC USING [ EncryptionKey, ExportFailed ], ThParty USING [ Alert, ConversationInfo, CreateConversation, DescribeParty, GetConversationInfo, GetKeyTable, GetParty, PartyInfo, RegisterServiceInterface, ReportAction, SmartsInterfaceRecord ], ThPartyPrivate USING [ NewId, RegisterLocal ], Thrush USING [ ActionID, ActionReport, ConversationID, ConvEvent, Credentials, InterfaceSpec, NetAddress, PartyID, NB, noAddress, nullConvID, nullID, Reason, ROPE, SHHH, SmartsID, StateInConv ], ThSmartsPrivate USING [ ChangeState, ComputeConnection, ConvDesc, EnterLarkState, Fail, ForgetConv, GetConv, GetConvDesc, GetSIC, GetSmartsInfo, HardwareInUse, HardwareRequired, InputEventSpec, LarkInfo, LarkReportAction, LarkState, OpenConversations, QueueFeeps, SmartsInfo, SmartsInfoBody ], ThSmartsRpcControl, Triples USING [Make ], TU, VoiceUtils USING [ CmdOrToken, CurrentPasskey, MakeRName, Problem, ProblemFR, ReportFR ] ; LarkTrunkSmartsImpl: CEDAR MONITOR LOCKS root IMPORTS Atom, Commander, root: LarkSmartsMonitorImpl, IO, LarkFeepRpcControl, MBQueue, Nice, Process, RefID, RPC, ThParty, ThPartyPrivate, ThSmartsPrivate, ThSmartsRpcControl, Triples, VoiceUtils EXPORTS ThSmartsPrivate, LarkFeep --, ThSmarts via Interface record -- SHARES LarkSmartsMonitorImpl = { OPEN IO; ConvDesc: TYPE = ThSmartsPrivate.ConvDesc; Reseal: PROC[r: REF] RETURNS[RefID.ID] = INLINE {RETURN[RefID.Reseal[r]]; }; ROPE: TYPE = Thrush.ROPE; SHHH: TYPE = Lark.SHHH; -- Encrypts conv. if first arg to RPC PROC PartyID: TYPE = Thrush.PartyID; nullID: RefID.ID = Thrush.nullID; nullConvID: Thrush.ConversationID = Thrush.nullConvID; SmartsInfo: TYPE = ThSmartsPrivate.SmartsInfo; StateInConv: TYPE = Thrush.StateInConv; PD: TYPE = RECORD [ timeoutNoAction: INT_30, timeoutUnstableState: INT _ 5, checkConflictAtConvCreate: BOOL_FALSE, -- safer, but not as useful. doReports: BOOL_FALSE, doNice: BOOL_FALSE, reportToAll: BOOL _ TRUE ]; pd: REF PD _ NEW[PD_[]]; larkFeepSpec: Thrush.InterfaceSpec _ [interfaceName: [type: "LarkFeep"], serviceID: RefID.nullID]; larkFeepUser: ROPE; larkFeepPassword: RPC.EncryptionKey; Problem: PROC[what: ROPE, info: SmartsInfo_NIL, v1: IO.Value _ [null[]], v2: IO.Value _ [null[]]] = { VoiceUtils.ProblemFR[what, $Lark, IF info#NIL THEN info.larkInfo ELSE NIL, v1, v2]; }; RegisterTrunk: PUBLIC INTERNAL PROC[ hostSmartsID: Thrush.SmartsID, hostInfo: SmartsInfo, partyRname: ROPE ] RETURNS [ nb: Thrush.NB_$success, smartsID: Thrush.SmartsID_nullID ] = { ENABLE UNWIND => NULL; localSmarts: ThParty.SmartsInterfaceRecord; info: SmartsInfo; smarts: REF; credentials: Thrush.Credentials; exportRequired: BOOL _ larkFeepSpec.interfaceName.instance = NIL; netAddress: Thrush.NetAddress _ hostInfo.larkInfo.netAddress; feepInterface: LarkFeepRpcControl.InterfaceRecord; netAddress.socket _ ThPartyPrivate.NewId[]; localSmarts _ ThSmartsRpcControl.NewInterfaceRecord[]; localSmarts.clientStubProgress _ LarkTrunkProgress; localSmarts.clientStubSubstitution _ LarkTrunkSubstitution; localSmarts.clientStubReportAction _ ThSmartsPrivate.LarkReportAction; localSmarts.clientStubCheckIn _ LarkTrunkCheckIn; [nb, credentials] _ ThPartyPrivate.RegisterLocal [ rName: partyRname, type: $trunk, interfaceRecord: localSmarts, properties: [role: $voiceTerminal, netAddress: hostInfo.larkInfo.netAddress] ]; IF nb # $success THEN { VoiceUtils.ProblemFR[ "Can't register Lark trunk: %g -- %g",$System,NIL, rope[partyRname], atom[nb]]; RETURN; }; smarts _ RefID.Unseal[smartsID_credentials.smartsID]; IF smarts = NIL THEN ERROR; info _ NEW[ThSmartsPrivate.SmartsInfoBody _ [ smartsID: smartsID, otherSmartsID: hostSmartsID, partyType: $trunk, ParseEvent: LarkTrunkParseEvent, NoteNewStateP: NoteNewTkState, requests: MBQueue.Create[], larkInfo: hostInfo.larkInfo ]]; info.larkInfo.larkTrunkSmartsInfo _ info; Triples.Make[$SmartsData, smarts, info]; larkFeepSpec.serviceID _ smartsID; -- identify self. feepInterface _ NEW[LarkFeepRpcControl.InterfaceRecordObject]; feepInterface.clientStubFeep _ FeepOp; feepInterface.clientStubFlash _ FlashOp; larkFeepSpec.interfaceID _ RefID.Seal[feepInterface]; IF ([, larkFeepSpec] _ ThParty.RegisterServiceInterface[credentials: credentials, interfaceSpecPattern: larkFeepSpec]).nb # $success THEN GOTO CantExportFeep; IF exportRequired THEN LarkFeepRpcControl.ExportInterface[ larkFeepSpec.interfaceName, larkFeepUser, larkFeepPassword! RPC.ExportFailed => GOTO CantExportFeep] EXITS CantExportFeep => { VoiceUtils.Problem[ "Couldn't export LarkFeep from TrunkSmarts, continuing"]; RETURN;}; }; LarkTrunkParseEvent: ENTRY PROC[r: REF] = { -- r is the EventSpec ENABLE UNWIND=>NULL; eventSpec: ThSmartsPrivate.InputEventSpec = NARROW[r]; smartsInfo: SmartsInfo = eventSpec.smartsInfo; sEvent: Lark.StatusEvent _ eventSpec.sEvent; IF smartsInfo.failed THEN RETURN; SELECT sEvent.device FROM ringDetect => SELECT sEvent.event FROM Lark.enabled => CmdRinging[smartsInfo]; Lark.disabled => CmdStopRinging[smartsInfo]; ENDCASE => ERROR; ENDCASE; }; ringProblem: ROPE = "Problem responding to phone line ringing"; ringProblemFR: ROPE = "Problem responding to phone line ringing %g"; CmdRinging: INTERNAL PROC[info: SmartsInfo] = { partyID: PartyID; -- self calledPartyRname: ROPE; calledPartyID: PartyID; cDesc: ConvDesc; convEvent: Thrush.ConvEvent; nb: Thrush.NB; { IF ThSmartsPrivate.GetSIC[info] # $idle THEN RETURN; cDesc _ ThSmartsPrivate.GetConv[info, [], TRUE]; partyID _ cDesc.situation.self.partyID; -- self [nb, calledPartyRname] _ ThParty.DescribeParty[partyID: partyID, nameReq: $owner]; IF nb # $success OR calledPartyRname=NIL THEN GOTO LarkFailed; [nb, calledPartyID] _ ThParty.GetParty[partyID: partyID, rName: calledPartyRname]; SELECT nb FROM $success => NULL; $noSuchParty2 => GOTO LarkFailed; -- Trunk with no Lark is a fatal error $voiceTerminalBusy, $convStillActive => GOTO Failed; -- Can't ring in right now. ENDCASE => GOTO LarkFailed; -- None of the remaining errors are healthy [nb, convEvent] _ ThParty.CreateConversation[ credentials: cDesc.situation.self, state: $initiating, checkConflict: pd.checkConflictAtConvCreate ]; SELECT nb FROM $success => NULL; $voiceTerminalBusy => GOTO Failed; -- Temporary problem ENDCASE => GOTO LarkFailed; -- All others are fatal NoteNewTkState[cDesc, convEvent]; nb_ThParty.Alert[credentials: cDesc.situation.self, calledPartyID: calledPartyID]; SELECT nb FROM $success => NULL; $noSuchParty2, $voiceTerminalBusy => GOTO ZapAndFail; -- Bitch a bit and give up $noSuchSmarts, $noSuchParty, $noSuchConv => GOTO LarkFailed; ENDCASE => ERROR; -- Fatally confused. EXITS LarkFailed => { ThSmartsPrivate.ForgetConv[cDesc]; ThSmartsPrivate.Fail[info.larkInfo, ringProblem, TRUE]; }; Failed => { VoiceUtils.ReportFR[ringProblemFR, $System, NIL, atom[nb]]; ThSmartsPrivate.ForgetConv[cDesc]; }; ZapAndFail => { VoiceUtils.ProblemFR[ringProblemFR, $System, NIL, atom[nb]]; CmdStopRinging[info]; }; }}; CmdStopRinging: INTERNAL PROC[info: SmartsInfo] = { cDesc: ConvDesc=ThSmartsPrivate.GetConvDesc[info]; IF cDesc=NIL THEN RETURN; SELECT cDesc.situation.self.state FROM $initiating, $ringback => NULL; -- ringing stopped before called party answered. ENDCASE => RETURN; -- already active or something; ignore end of ringing [] _ ThSmartsPrivate.ChangeState[cDesc, $idle, $terminating]; }; LarkTrunkProgress: ENTRY PROC[ interface: ThSmartsRpcControl.InterfaceRecord, shh: Thrush.SHHH, convEvent: Thrush.ConvEvent ] = { ENABLE UNWIND => NULL; -- RestoreInvariant; convID: Thrush.ConversationID = convEvent.self.convID; info: SmartsInfo = ThSmartsPrivate.GetSmartsInfo[smartsID: convEvent.self.smartsID]; cDesc: ConvDesc; whatNeedsDoing: WhatNeedsDoing; IF info=NIL THEN { Problem["No Smarts at TrunkProgress for SmartsID %g", NIL, card[convEvent.self.smartsID]]; RETURN; }; cDesc _ ThSmartsPrivate.GetConv[ info, convEvent.self, TRUE ]; IF convEvent.self.partyID = convEvent.other.partyID THEN { -- own state changed NoteNewTkState[cDesc, convEvent]; IF ThSmartsPrivate.HardwareInUse[cDesc] THEN DoRejectCall[cDesc, convEvent]; RETURN; }; whatNeedsDoing _ whatNeedsDoingIf[cDesc.situation.self.state][convEvent.other.state]; SELECT whatNeedsDoing FROM $noop, $ntiy => NULL; -- No action is needed, or we don't know what one to take. $imp => ERROR; -- This is supposed to be impossible! $xrep => Problem["Didn't expect state change report", info]; $frgt => ThSmartsPrivate.ForgetConv[cDesc]; $invl => { Problem["Invalid state transition", info]; []_ThSmartsPrivate.ChangeState[cDesc: cDesc, state: $idle, reason: $error, comment: "System Error: Invalid state transition"]; }; $idle => { nb2: Thrush.NB; cInfo: ThParty.ConversationInfo; [nb2, cInfo] _ ThParty.GetConversationInfo[convID: convEvent.self.convID]; SELECT nb2 FROM $success => NULL; $noSuchConv => { Problem["Conversation disappeared, forget the whole thing", info]; ThSmartsPrivate.ForgetConv[cDesc]; }; ENDCASE => ERROR; IF (cInfo.numParties-cInfo.numIdle) <= 1 THEN []_ThSmartsPrivate.ChangeState[cDesc, $idle, $terminating]; }; $actv => [] _ ThSmartsPrivate.ChangeState[cDesc: cDesc, state: $active, bilateral: TRUE]; $rbak => [] _ ThSmartsPrivate.ChangeState[cDesc: cDesc, state: $ringback]; $reac => { -- we're active and the other party goes active larkInfo: ThSmartsPrivate.LarkInfo _ cDesc.info.larkInfo; SetLarkTrunkState[cDesc, larkInfo.larkState, LIST[SetupConnection[cDesc, larkInfo]]]; }; $deac => NULL; ENDCASE => ERROR; }; LarkTrunkSubstitution: ENTRY PROC[ interface: ThSmartsRpcControl.InterfaceRecord, shh: Thrush.SHHH, convEvent: Thrush.ConvEvent, oldPartyID: Thrush.PartyID, newPartyID: Thrush.PartyID ] = { ENABLE UNWIND => NULL; -- RestoreInvariant; info: SmartsInfo = ThSmartsPrivate.GetSmartsInfo[smartsID: convEvent.self.smartsID]; cDesc: ConvDesc; IF info=NIL THEN { Problem["No Smarts at TrunkSubstitution for SmartsID %g", NIL, card[convEvent.self.smartsID]]; RETURN; }; cDesc _ ThSmartsPrivate.GetConv[ info, convEvent.self ]; IF cDesc=NIL THEN RETURN; NoteNewTkState[cDesc, convEvent]; }; LarkTrunkCheckIn: ENTRY PROC[ interface: ThSmartsRpcControl.InterfaceRecord, shh: Thrush.SHHH, credentials: Thrush.Credentials, reason: Thrush.Reason, nextScheduledCheck: INT _ LAST[INT] ] = { ENABLE UNWIND => NULL; }; QdNotification: ENTRY PROC [r: REF] ~ { ENABLE UNWIND => NULL; -- RestoreInvariant; cDesc: ConvDesc = NARROW[r]; IF cDesc=NIL OR cDesc.situation.self.state # $notified THEN RETURN; [] _ ThSmartsPrivate.ChangeState[cDesc: cDesc, state: $active, bilateral: TRUE]; }; QdIdle: ENTRY PROC [r: REF] ~ { ENABLE UNWIND => NULL; -- RestoreInvariant; cDesc: ConvDesc = NARROW[r]; IF cDesc=NIL OR cDesc.situation.self.state = $idle THEN RETURN; [] _ ThSmartsPrivate.ChangeState[cDesc: cDesc, state: $idle, reason: $noAnswer]; }; DoRejectCall: INTERNAL PROC[cDesc: ConvDesc, convEvent: Thrush.ConvEvent] = { []_ThSmartsPrivate.ChangeState[cDesc, $idle, $busy]; }; LimitSignalling: ENTRY PROC [cDesc: ConvDesc, unstableState: StateInConv] ~ TRUSTED { c: CONDITION; Process.SetTimeout[@c, Process.SecondsToTicks[pd.timeoutUnstableState]]; WAIT c; SELECT cDesc.situation.self.state FROM # unstableState => RETURN; -- we made it. $initiating => NULL; -- inherently unstable $active => { nb: Thrush.NB; cInfo: ThParty.ConversationInfo; [nb, cInfo] _ ThParty.GetConversationInfo[convID: cDesc.situation.self.convID]; IF nb#$success --we got problems!-- OR cInfo.numActive>1 THEN RETURN; }; ENDCASE => RETURN; cDesc.info.requests.QueueClientAction[QdIdle, cDesc]; }; NoteNewTkState: INTERNAL PROC[cDesc: ConvDesc, convEvent: Thrush.ConvEvent] ~ { OPEN now: cDesc.situation.self; nb: Thrush.NB; larkStateData: LIST OF REF ANY_NIL; state: StateInConv _ convEvent.self.state; previousState: StateInConv = cDesc.situation.self.state; info: ThSmartsPrivate.SmartsInfo = cDesc.info; larkInfo: ThSmartsPrivate.LarkInfo = info.larkInfo; larkState: ThSmartsPrivate.LarkState _ $none; cDesc.situation _ convEvent^; IF state = previousState THEN RETURN; -- No conceivable value in acting. SELECT state FROM $notified => info.requests.QueueClientAction[QdNotification, cDesc]; $initiating => TRUSTED {Process.Detach[FORK LimitSignalling[cDesc, state]]; }; $idle => { IF larkInfo.forwardedCall THEN larkState _ $idle; ThSmartsPrivate.ForgetConv[cDesc]; }; $neverWas => ThSmartsPrivate.ForgetConv[cDesc]; $active => { phoneNumber: ROPE; connectionSpec: ThParty.PartyInfo _ SetupConnection[cDesc, larkInfo]; [nb, phoneNumber] _ ThParty.DescribeParty[partyID: now.partyID, nameReq: $address]; larkState _ IF nb # $success OR phoneNumber=NIL OR connectionSpec.conversationInfo.originator=now.partyID THEN trunkTalking ELSE trunkSignalling; larkStateData _ LIST[ connectionSpec, NEW[Atom.DottedPairNode _ [$phoneNumber, phoneNumber]]]; TRUSTED {Process.Detach[FORK LimitSignalling[cDesc, state]];}; }; ENDCASE; IF state > $idle AND cDesc.keyTable=NIL THEN { [nb, cDesc.keyTable] _ ThParty.GetKeyTable[credentials: now]; IF nb # $success THEN cDesc.keyTable_NIL; }; SetLarkTrunkState[cDesc, larkState, larkStateData]; }; SetLarkTrunkState: INTERNAL PROC[ cDesc: ConvDesc, larkState: ThSmartsPrivate.LarkState, larkStateData: LIST OF REF ANY ] = { state: StateInConv _ cDesc.situation.self.state; larkInfo: ThSmartsPrivate.LarkInfo _ cDesc.info.larkInfo; IF ThSmartsPrivate.HardwareInUse[cDesc] THEN { IF ThSmartsPrivate.HardwareRequired[cDesc] THEN Problem["Conflicting use of Lark at SetLarkTrunkState: ", cDesc.info]; RETURN; }; IF ThSmartsPrivate.HardwareRequired[cDesc] AND cDesc.keyTable#cDesc.info.larkInfo.keyTable AND cDesc.keyTable#NIL THEN larkStateData _ CONS[cDesc.keyTable, larkStateData]; IF larkState = $none THEN larkState _ cDesc.info.larkInfo.larkState; ThSmartsPrivate.EnterLarkState[cDesc.info.larkInfo, larkState, larkStateData]; }; SetupConnection: INTERNAL PROC[cDesc: ConvDesc, larkInfo: ThSmartsPrivate.LarkInfo] RETURNS[connectionSpec: ThParty.PartyInfo] = { forwardedCall: BOOL_FALSE; connectionSpec _ ThSmartsPrivate.ComputeConnection[cDesc]; IF connectionSpec=NIL THEN ERROR; forwardedCall _ connectionSpec.conversationInfo.numParties>1 AND connectionSpec[0].socket.host # connectionSpec[1].socket.host; larkInfo.forwardedCall _ forwardedCall; }; Feep: PUBLIC ENTRY PROCEDURE [ -- for local instance shhh: Thrush.SHHH, serviceID: Thrush.SmartsID, convID: Thrush.ConversationID, requestingParty: Thrush.PartyID, number: Rope.ROPE, noisy: BOOLEAN _ FALSE, on, off: CARDINAL_ 177777B] RETURNS [nb: Thrush.NB_$success] = { RETURN[FeepOp[NIL, shhh, serviceID, convID, requestingParty, number, noisy, on, off]]; }; FeepOp: INTERNAL PROCEDURE [ -- Exports to voice service interface LarkFeep interface: LarkFeepRpcControl.InterfaceRecord, shhh: Thrush.SHHH, serviceID: Thrush.SmartsID, convID: Thrush.ConversationID, requestingParty: Thrush.PartyID, number: Rope.ROPE, noisy: BOOLEAN _ FALSE, on, off: CARDINAL_ 177777B] RETURNS [nb: Thrush.NB_$success] = { info: SmartsInfo = ThSmartsPrivate.GetSmartsInfo[smartsID: serviceID]; cDesc: ConvDesc; IF info = NIL THEN RETURN[$noSuchSmarts]; cDesc _ ThSmartsPrivate.GetConv[info, [convID: convID]]; IF cDesc = NIL THEN RETURN[$notInConv]; IF cDesc.situation.self.state # $active THEN RETURN[$convNotActive]; IF noisy THEN number _ IO.PutFR["A%g", rope[number]]; IF on#177777B THEN number _ IO.PutFR["%gO%g", char[LOOPHOLE[on/10]], rope[number]]; IF off#177777B THEN number _ IO.PutFR["%gF%g", char[LOOPHOLE[off/10]], rope[number]]; ThSmartsPrivate.QueueFeeps[info, number]; }; FlashInfo: TYPE = REF FlashInfoBody; FlashInfoBody: TYPE = RECORD [ requestingPartyID: Thrush.PartyID, actionID: Thrush.ActionID ]; Flash: PUBLIC ENTRY PROCEDURE [ -- Exports to voice service interface LarkFeep shhh: Thrush.SHHH, serviceID: Thrush.SmartsID, convID: Thrush.ConversationID, requestingParty: Thrush.PartyID, actionID: Thrush.ActionID, extraTime: CARDINAL_ 177777B] RETURNS [nb: Thrush.NB_$success] = { RETURN[FlashOp[NIL, shhh, serviceID, convID, requestingParty, extraTime]]; }; FlashOp: INTERNAL PROCEDURE [ -- Exports to voice service interface LarkFeep interface: LarkFeepRpcControl.InterfaceRecord, shhh: Thrush.SHHH, serviceID: Thrush.SmartsID, convID: Thrush.ConversationID, requestingParty: Thrush.PartyID, actionID: Thrush.ActionID, extraTime: CARDINAL_ 177777B] RETURNS [nb: Thrush.NB_$success] = { info: SmartsInfo = ThSmartsPrivate.GetSmartsInfo[smartsID: serviceID]; cDesc: ConvDesc; IF info = NIL THEN RETURN[$noSuchSmarts]; cDesc _ ThSmartsPrivate.GetConv[info, [convID: convID]]; IF cDesc = NIL THEN RETURN[$notInConv]; IF cDesc.situation.self.state # $active THEN RETURN[$convNotActive]; info.larkInfo.props _ Atom.PutPropOnList[info.larkInfo.props, $FlashInfo, NEW[FlashInfoBody _ [requestingParty, actionID]]]; SetLarkTrunkState[cDesc, $trunkFlashing, NIL]; }; FlashDone: PUBLIC ENTRY PROC[r: REF] = { -- r is a LarkInfo; flashing has finished nb: Thrush.NB; info: ThSmartsPrivate.LarkInfo = NARROW[r]; smartsInfo: SmartsInfo = IF info=NIL THEN NIL ELSE info.larkTrunkSmartsInfo; cDesc: ConvDesc = ThSmartsPrivate.GetConvDesc[smartsInfo]; flashInfo: FlashInfo _ IF info=NIL THEN NIL ELSE NARROW[Atom.GetPropFromList[info.props, $FlashInfo]]; IF flashInfo=NIL OR flashInfo.actionID=0 THEN RETURN; nb _ ThParty.ReportAction[ report: [ self: cDesc.situation.self, -- placeholder other: cDesc.situation.self, requestingParty: flashInfo.requestingPartyID, actionID: flashInfo.actionID, actionClass: $switchhookFlash, actionType: $flashed ], reportToAll: FALSE ].nb; IF nb#$success THEN IF info#NIL AND ~info.failed THEN ThSmartsPrivate.Fail[info, IO.PutFR["Couldn't report switchhook flash completion -- %g", atom[nb]], TRUE] ELSE VoiceUtils.ProblemFR["Couldn't report switchhook flash completion, probably due to earlier failure -- %g", $System, NIL, atom[nb]]; }; LarkTrunkSmartsInit: Commander.CommandProc = { larkFeepSpec.interfaceName.instance _ NIL; larkFeepSpec.hostHint _ Thrush.noAddress; larkFeepUser _ VoiceUtils.MakeRName[style: rName, name: VoiceUtils.CmdOrToken[cmd: cmd, key: "ThrushServerInstance", default: "Strowger.Lark"]]; larkFeepPassword _ VoiceUtils.CurrentPasskey[VoiceUtils.CmdOrToken[ cmd: cmd, key: "ThrushServerPassword", default: "MFLFLX"]]; VoiceUtils.ReportFR["Initialize[LarkTrunkSmarts.Lark, %s]", $System, NIL, rope[larkFeepUser]]; }; WhatNeedsDoing: TYPE = ATOM; -- { whatNeedsDoingIf: ARRAY StateInConv OF ARRAY StateInConv OF WhatNeedsDoing _ [ [ $imp, $frgt, $frgt, $frgt, $frgt, $frgt, $frgt, $frgt, $frgt, $frgt, $frgt, $frgt], --neverWas [ $imp, $noop, $frgt, $noop, $noop, $noop, $noop, $noop, $noop, $noop, $noop, $ntiy ], -- idle [ $imp, $imp, $frgt, $imp, $imp, $imp, $imp, $imp, $imp, $imp, $imp, $imp ], -- failed [ $imp, $imp, $frgt, $imp, $imp, $imp, $imp, $imp, $imp, $imp, $imp, $imp ], -- reserved [ $imp, $imp, $frgt, $imp, $imp, $imp, $imp, $imp, $imp, $imp, $imp, $imp ], -- parsing [ $imp, $idle,$frgt, $invl, $invl, $invl, $xrep, $xrep, $rbak,$actv, $actv, $actv ], -- initiating [ $imp, $idle,$frgt, $invl, $invl, $invl, $xrep, $noop, $noop, $ntiy, $noop, $ntiy ], -- notified [ $imp, $idle,$frgt, $invl, $invl, $invl, $xrep, $xrep, $noop,$actv, $actv, $actv ], -- ringback [ $imp, $idle,$frgt, $invl, $invl, $invl, $invl, $invl, $invl, $invl, $invl, $invl ], -- ringing [ $imp, $idle,$frgt, $invl, $invl, $invl, $xrep, $xrep, $noop, $ntiy, $noop, $ntiy ], -- canActivate [ $imp, $idle,$frgt, $invl, $invl, $invl, $xrep, $xrep, $noop, $ntiy, $reac, $deac ], -- active [ $imp, $idle,$frgt, $invl, $invl, $invl, $xrep, $xrep, $noop, $ntiy, $ntiy, $ntiy ] -- inactive (current ^) ]; ViewCmd: Commander.CommandProc = TRUSTED { Nice.View[pd, "Lark Trunk PD"]; }; Commander.Register["LarkTrunkSmarts", LarkTrunkSmartsInit, "LarkTrunkSmarts \nInitialize and Export LarkTrunkSmarts"]; Commander.Register["VuLarkTrunkSmarts", ViewCmd, "Program Management variables for Lark Trunk Smarts"]; }. ΖLarkTrunkSmartsImpl.mesa Copyright Σ 1986, 1987 by Xerox Corporation. All rights reserved. Last modified by D. Swinehart, June 21, 1987 9:21:23 pm PDT Note: Incoming calls are those from the Etherphone to the outside party, whom this system represents. Outgoing calls are those from the outside world to an Etherphone. Remember; trunk party is a representative of people in the public telephone system. See LarkSmartsImpl and LarkSmartsSupImpl for various comments guiding the structure of this module; it's simpler, but similar. Types and Constants Initialization There's no provision here at all for reregistration. Yet. RegisterTrunk.nb~$success RegisterTrunk.RegisterLocal.nb~~$noIdentSupplied -- interface error RegisterTrunk.RegisterLocal.nb~~$couldntAuthenticate -- communicationish failure RegisterTrunk.RegisterLocal.nb~~$couldntConnect -- communicationish failure Events RecordTrunkEvent handles events from the EtherPhone Trunk connection. The only interesting events at present are "ring" (place trunk->station call) and "tones F" (station->trunk call being "answered") The upstream code produces a single "enabled" event when the line starts ringing, and a single "disabled" event when it appears that ringing has stopped. This "disabled" event must be anticipated and ignored if the station answers (calling trunk can't hang up on completed call!) Not a valid time for this call. Ignore. Host system will handle. By here, we have to kill the conversation before forgetting it, unless the error is fatal ChangeState will give best-efforts, complaining in the system log and/or failing the hardware when there are serious problems. Some party has changed state in a conversation we know about. We have to reject this, since we're already actively dealing with another conv. We are (still) willing to seriously consider only one conversation at a time on the trunk side. Someone else's state changed in a conv. we're interested in. See if it means anything to us. A poacher has substituted for a poachee, or the other way around. Update state so that if it's us, we know who we are. I wonder how well this works. The corresponding LarkSmarts version will take care of everything. Idle after unstable-state timeout We have entered an unstable state, which should not be allowed to persist too long before we hang up and free our resources. "Too long" is determined by a pd.timeoutUnstableState. Feep.nb~$success Feep.nb~$noSuchSmarts -- really no such SmartsInfo Feep.nb~$notInConv -- really no cDesc Feep.nb~$convNotActive Flash.nb~$success Flash.nb~$noSuchSmarts -- really no such SmartsInfo Flash.nb~$notInConv -- really no cDesc Flash.nb~$convNotActive Just codes to dispatch on in Supervisor; explained there $noop, $idle, $actv, $frgt, $reac, $deac, -- cases explained in code $rbak, -- enter "ringback"; stable initiating state. $invl, -- considered an invalid request $xrep, -- we got a report we feel we shouldn't have got $ntiy, -- not implemented yet $imp -- this situation should not arise even in the face of invalid requests }; If we're in the state identified by the row, and someone else in the conversation reports a transition onto the state identified by the column, what should we do? never idle failed resrv pars init notif rback ring canAc activ inact -- _ (other) (Clip this table to view without these comments.) This situation arises when we've forgotten about the conversation that somebody else is still reporting on. The actions of other parties are not of interest to us yet, since they're not in this conv. Ditto. They can either enter ringing to indicate interest, or go active without ringing If they enter ringing, we ignore it: initiating is as good as ringback for trunks. We don't expect to hear from others while we're deciding whether to play They have earlier expressed interest noopringing), and are now joining the fray The only thing that interests us here is everybody else quitting. Debugging nonsense Swinehart, October 28, 1985 10:01:46 am PST Log => VoiceUtils, Handle => ID changes to: DIRECTORY, LarkTrunkSmartsImpl, Reseal, PartyID, nullID, nullConvID, RegisterTrunk, LarkTrunkParseEvent, CmdCall, TrunkSupervise Swinehart, May 17, 1986 5:37:22 pm PDT Cedar 6.1 changes to: RegisterTrunk Swinehart, June 1, 1986 7:42:22 pm PDT Eliminate what's considered to be spurious MBQueueing. There is more to go. changes to: RegisterTrunk, LarkTrunkProgress, LarkTrunkSubstitution, LarkTrunkReportAction, QdNotification Swinehart, June 11, 1986 7:42:07 am PDT Use LarkReportAction from LarkSmartsSupImpl to record new key tables changes to: DIRECTORY, RegisterTrunk, LarkTrunkSubstitution Swinehart, December 29, 1986 3:45:45 pm PST Remove obsolete $voiceTerminalBusy tests. changes to: CmdRinging, NoteNewTkState Swinehart, January 1, 1987 9:44:36 pm PST Continuing FD/BD resolution stuff. changes to: CmdRinging, NoteNewTkState Swinehart, January 24, 1987 5:32:09 pm PST General error-management review and repair, esp. NB-handling changes to: LarkTrunkParseEvent, ringProblemFR, CmdRinging, CmdStopRinging, }, LarkTrunkProgress, LarkTrunkSubstitution, QdNotification, NoteNewTkState, Feep Swinehart, June 11, 1987 11:56:49 am PDT Changes to ThSmartsPrivate.Getconv changes to: LarkTrunkProgress, LarkTrunkSubstitution Κ‡˜šœ™IcodešœB™BJšœ;™;—J™J™ύJ™J™~J˜šΟk œ˜ Jšœœ4˜>Jšœ œ˜*Jšœ˜šœΟc ˜JšžLœ˜M—J˜ Jšœœ=˜UJ˜Jšœœ˜,J˜Jšœœ(˜5Jšœœœ!˜1Jšœœœ˜Jšœœ!˜*JšœœΆ˜ΓJšœœ˜.šœœ˜Jšœdœ)œœ˜³—šœœ˜Jšœ˜—J˜Jšœœ ˜J˜JšœX˜XJ˜J˜—šœ œœ˜-š˜J˜Jšœ ˜ J˜Jšœ˜J˜J˜J˜J˜J˜Jšœ˜J˜J˜J˜J˜J˜J˜ —Jšœž$˜FJšœœœ˜)J˜—™J˜Jšœ œ˜*JšΟnœœœœœœœ˜LJšœœ œ˜Jšœœœž*˜CJšœ œ˜J˜!J˜6Jšœ œ˜.Jšœ œ˜'J˜šœœœ˜Jšœœ˜Jšœœ˜Jšœœœž˜CJšœ œœ˜Jšœœ˜Jšœ œ˜J˜—Jš œœœœœ˜J˜J˜bJšœœ˜Jšœœ˜$J˜šŸœ˜Jš œœœœœ˜\Jš œ"œœœœœ ˜SJ˜—J˜—™J˜šŸ œœ œ˜$JšœAœ˜GJšœœ1˜HJšœœœ˜J™:Jšœ+˜+J˜Jšœœ˜ J˜ Jšœœ)œ˜AJ˜=J˜2J˜J˜+Jšœ6˜6Jšœ3˜3Jšœ;˜;JšœF˜FJšœ1˜1šœ2˜2J˜J˜ J˜JšœL˜LJšœ˜—šœœ˜šœ˜Jšœ.œ˜O—Jšœ˜Jšœ˜—Jšœ5˜5Jšœ œœœ˜šœœ#˜-Jšœ˜J˜J˜J˜ J˜J˜Jšœ˜—J˜)J˜)Jšœ#ž˜4Jšœœ+˜>J˜&J˜(J˜5šœO˜QJšœ3œœ˜L—šœœ$˜:šœ;˜;Jšœœ˜(——š˜šœ˜šœ˜Jšœ9œ˜C———Jšœ˜J™JšœC™CJšœP™PšœK™KJ˜———™JšœE™EJšœ‚™‚J™˜J˜š Ÿœœœœž˜AJš œœ˜Jšœ,œ˜6Jšœ.˜.J˜,Jšœœœ˜!šœ˜šœœ˜&Jšœ'˜'J˜,Jšœœ˜—Jšœ˜—Jšœ˜J˜—Jšœ œ.˜?Jšœœ1˜DJ˜šŸ œœœ˜/Jšœž˜Jšœœ˜J˜J˜J˜Jšœ œΟb˜šœ&œ˜4JšœB™B—Jšœ*œ˜0Jšœ(ž˜/˜J˜9—Jš œœœœœ ˜>J˜Ršœ˜Jšœ œ˜Jšœœ ž&˜HJšœ(œ ž˜PJšœœž+˜H—˜-J˜"J˜Jšœ+˜+J˜—šœ˜Jšœ œ˜Jšœœ ž˜7Jšœœ ž˜3—Jšœ!˜!J˜RJ™Yšœ˜Jšœ œ˜Jšœ%œ ž˜PJšœ,œ ˜J˜šœ2œž˜OKšœ!˜!šœ&˜,Jšœ˜Jšœ°™°—Kšœ˜K˜—J˜Jšœ]™]KšœU˜Ušœ˜Kšœœž:˜PKšœœž%˜4Kšœ<˜Kšœ˜—Kšœ˜—šœœœœ˜.Kšœ=˜=Kšœœœ˜)K˜—Kšœ3˜3K˜K˜—šŸœœœ˜!JšœFœ˜[Jšœ0˜0Jšœ9˜9šœ&œ˜.šœ)˜/JšœF˜F—Jšœ˜Jšœ˜—š œ)œ-œœ˜vKšœœ ˜4—Jšœœ+˜DJšœN˜NJ˜J˜—šŸœœœ5˜SKšœ'˜.Kšœœœ˜Kšœ:˜:Kšœœœœ˜!šœ˜Kšœ-˜0Kšœ>˜>—K˜'K˜J˜—š Ÿœœœ œž˜4Jšœ œ]˜nJš œ œ œœ œ ˜FJšœ œ˜$JšœœE˜VJ˜J˜—šŸœœ œž.˜KJ˜.Jšœ œ]˜nJš œ œ œœ œ ˜FJšœ œ˜$JšœF˜FJšœ˜Jšœœœœ˜)Jšœ8˜8Jšœ œœœ ˜'Jšœ&œœ˜DJšœœ œ˜5Jšœ œ œœ˜SJšœ œ œœ˜UJ˜)J˜J™Jšœ2™2Jšœ%™%Jšœ™J˜—Jšœ œœ˜$šœœœ˜J˜"J˜J˜J˜—š Ÿœœœ œž.˜NJšœ œx˜‰Jšœ œ ˜Jšœ œ˜$Jšœ œ8˜JJ˜J˜—šŸœœ œž.˜LJ˜.Jšœ œx˜‰Jšœ œ ˜Jšœ œ˜$JšœF˜FJšœ˜Jšœœœœ˜)Jšœ8˜8Jšœ œœœ ˜'Jšœ&œœ˜D˜IJšœ/˜2—Jšœ)œ˜.J˜J™Jšœ3™3Jšœ&™&Jšœ™J˜—š Ÿ œœœœœž)˜RJšœ œ˜Jšœ!œ˜+Jš œœœœœœ˜LJšœ:˜:šœœœœ˜+Jšœœ/˜:—Jš œ œœœœ˜5šœ˜˜ Jšœž˜*Jšœ˜Jšœ-˜-Jšœ˜J˜Jšœ˜J˜—Jšœ ˜Jšœ˜—šœ ˜Kš œœœœœGœ˜‹Kšœuœ ˜ˆ—K˜J˜—šŸœ˜.Jšœ&œ˜*J˜)šœ7˜7JšœX˜X—šœC˜CJ˜;—JšœEœ˜^Jšœ˜J˜——šœœœž˜!J™8Jšœ*ž™DJšœž-™4Jšœž ™'Jšœž0™7Jšœž™JšœžH™MJ™J˜—š œœ œœ œ˜NJ™’J™JšΟfΠfsa™bš’ZΡcfs ˜dJšœ1™1Jšœk™k—Jš’[£˜bJš’Z£ ˜dš’Z£ ˜eJšœ[™[—š’Z£ ˜dJšœ™—š ’Ρbfs’%€’€ ’€’€’£ ˜dJ™PJ™R—š’€’M£ ˜eJ™H—š ’€’-€ ’€’€’£ ˜bJ™O—š’€’M£ ˜dJ™A—Jš’€’M£˜hJš’€’=€’€’£ ˜cJš’€’L£˜pJ˜—J˜J˜™šœ!œ˜*Jšœ˜Jšœ˜—šœ¦˜¦J˜—Jšœg˜g—J˜J™™+K™Kšœ Οr€™Œ—™&K™ Kšœ ₯ ™—™&K™LKšœ ₯^™j—™'K™DKšœ ₯/™;—K™šœ+™+Kšœ)™)Kšœ ₯™&—K™™)K™"Kšœ ₯™&—™*K™