DIRECTORY Commander USING [ CommandProc, Register ], IO, Lark -- USING [ -- CommandEvents, ConnectionSpec, Event, LarkModel, Passel, SHHH, StatusEvents ]--, LarkSmartsMonitorImpl, Nice, Process USING [ SecondsToTicks, SetTimeout ], Rope USING [ Concat ], ThParty USING [ Advance, Alert, CreateParty ], ThPartyPrivate USING [ DehandleParty, DehandleSmarts, LocalSmartsInterface, RegisterLocal, SmartsBody, SmartsData ], Thrush USING [ ConversationHandle, ConvEvent, PartyHandle, H, NB, nullConvHandle, nullHandle, pERROR, Reason, ROPE, SmartsHandle, StateInConv, ThHandle ], ThSmartsPrivate USING [ Apprise, ConvDesc, Deregister, EnterLarkState, GetConv, GetConvDesc, GetSIC, GetSmartsInfo, LarkFailed, LarkProgress, OpenConversations, QueueFeeps, SmartsInfo, SmartsInfoBody ], ThSmartsRpcControl, Triples USING [Any, Make, Select ], TU, VoiceUtils USING [ Problem, ProblemFR, Report, ReportFR ] ; LarkTrunkSmartsImpl: CEDAR MONITOR LOCKS root IMPORTS Commander, root: LarkSmartsMonitorImpl, IO, Nice, Process, Rope, Thrush, ThParty, ThPartyPrivate, ThSmartsPrivate, ThSmartsRpcControl, Triples, TU, VoiceUtils EXPORTS ThSmartsPrivate --, ThSmarts via Interface record -- SHARES LarkSmartsMonitorImpl = { OPEN IO; ConvDesc: TYPE = ThSmartsPrivate.ConvDesc; H: PROC[r: REF] RETURNS[Thrush.ThHandle] = INLINE {RETURN[Thrush.H[r]]; }; ROPE: TYPE = Thrush.ROPE; SHHH: TYPE = Lark.SHHH; -- Encrypts conv. if first arg to RPC PROC PartyHandle: TYPE = Thrush.PartyHandle; nullHandle: Thrush.ThHandle = Thrush.nullHandle; nullConvHandle: Thrush.ConversationHandle = Thrush.nullConvHandle; SmartsInfo: TYPE = ThSmartsPrivate.SmartsInfo; StateInConv: TYPE = Thrush.StateInConv; PD: TYPE = RECORD [ timeoutNoAction: INT_30, cSp: REF Ctr_NEW[Ctr_[0,1000000]], cRs: REF Ctr_NEW[Ctr_[0,1000000]], cZp: REF Ctr_NEW[Ctr_[0,1000000]], doReports: BOOL_FALSE, doNice: BOOL_FALSE ]; pd: REF PD _ NEW[PD_[]]; Ctr: TYPE = RECORD[ count: INT, stop: INT ]; Report: PROC[what: ROPE, info: SmartsInfo, ctr: REF Ctr] = { IF NOT pd.doReports THEN RETURN; VoiceUtils.Report[what, $Lark, info.larkInfo]; ctr.count_ctr.count+1; IF ctr.count>ctr.stop THEN { VoiceUtils.Problem["Report overflow", $Lark, info.larkInfo]; }; }; ReportFR: PROC[what: ROPE, info: SmartsInfo, ctr: REF Ctr, a1, a2: IO.Value_rope[NIL]] = { IF NOT pd.doReports THEN RETURN; VoiceUtils.ReportFR[what, $Lark, info.larkInfo, a1, a2]; ctr.count_ctr.count+1; IF ctr.count>ctr.stop THEN { VoiceUtils.Problem["Report overflow", $Lark, info.larkInfo]; }; }; BeNice: PROC[r: REF, d: INT, info: SmartsInfo] = { IF NOT pd.doNice THEN RETURN; Nice.BeNice[r, d, $Lark, info.larkInfo]; }; RegisterTrunk: PUBLIC ENTRY PROC[ hostPartyID: PartyHandle, hostSmarts: ThPartyPrivate.SmartsData, hostInfo: SmartsInfo ] RETURNS [ smartsID: Thrush.SmartsHandle ] = { { ENABLE UNWIND => NULL; localSmarts: ThPartyPrivate.LocalSmartsInterface; info: SmartsInfo; partyID: PartyHandle = ThParty.CreateParty[type: trunk]; hostParty: REF = ThPartyPrivate.DehandleParty[hostPartyID]; party: REF = ThPartyPrivate.DehandleParty[partyID]; smarts: ThPartyPrivate.SmartsData; IF hostParty=NIL OR party = NIL THEN { VoiceUtils.Problem["TrunkSmarts didn't register", $Party, hostParty]; RETURN[nullHandle]; }; TU.MakeUnique[$TrunkParty, hostParty, party]; localSmarts _ ThSmartsRpcControl.NewInterfaceRecord[]; localSmarts.clientStubProgress _ ThSmartsPrivate.LarkProgress; smartsID _ ThPartyPrivate.RegisterLocal [ partyID: partyID, interface: localSmarts, properties: hostSmarts.properties ]; IF smartsID = nullHandle THEN GOTO NotSmart; smarts _ ThPartyPrivate.DehandleSmarts[smartsID, TRUE]; info _ NEW[ThSmartsPrivate.SmartsInfoBody _ [ smarts: smarts, otherSmarts: hostSmarts, ParseEvent: LarkTrunkParseEvent, Supervise: TrunkSupervise, larkInfo: hostInfo.larkInfo ]]; Triples.Make[$SmartsData, smarts, info]; EXITS NotSmart => RETURN[nullHandle]; }; }; LarkTrunkParseEvent: ENTRY PROC[ smartsInfo: SmartsInfo, sEvent: Lark.StatusEvent] = { ENABLE { UNWIND=>NULL; ThSmartsPrivate.LarkFailed => { Problem["Lark Failed", smartsInfo]; GOTO Failed; }; }; SELECT sEvent.device FROM ringDetect => SELECT sEvent.event FROM Lark.enabled => CmdCall[smartsInfo]; Lark.disabled => CmdOnhook[smartsInfo]; ENDCASE => ERROR Thrush.pERROR; ENDCASE; EXITS Failed => ThSmartsPrivate.Deregister[ThSmartsPrivate.GetSmartsInfo[smarts: smartsInfo.otherSmarts]]; }; CmdCall: INTERNAL PROC[info: SmartsInfo] = { calledPartyID: PartyHandle; cDesc: ConvDesc; SELECT ThSmartsPrivate.GetSIC[info] FROM idle => NULL; ENDCASE => RETURN; -- not a valid time for this call. Ignore. Host system will handle. cDesc _ ThSmartsPrivate.GetConv[info, nullConvHandle, TRUE]; calledPartyID _ H[Triples.Select[$TrunkParty, --owner--, ThPartyPrivate.DehandleParty[cDesc.cState.credentials.partyID]]]; --<> IF calledPartyID = nullHandle THEN RETURN; cDesc.desiredState _ active; cDesc.desiredPartyID _ calledPartyID; cDesc.desiredReason _ wontSay; cDesc.desiredComment _ NIL; cDesc.cState.state _ any; -- meaning "This is a new conversation to be made." ThSmartsPrivate.Apprise[info]; }; CmdOnhook: INTERNAL PROC[info: SmartsInfo] = { cDesc: ConvDesc=ThSmartsPrivate.GetConvDesc[info]; IF cDesc=NIL THEN RETURN; SELECT cDesc.cState.state FROM initiating, maybe => NULL; -- ringing stopped before called party answered. ENDCASE => RETURN; -- already active or something; ignore end of ringing cDesc.desiredState_idle; cDesc.desiredReason _ wontSay; cDesc.desiredComment _ NIL; ThSmartsPrivate.Apprise[info]; }; TrunkSupervise: ENTRY PROC[info: SmartsInfo ] = { TRUSTED { Process.SetTimeout[@info.thAction, Process.SecondsToTicks[pd.timeoutNoAction]]; }; IF info.apprise THEN DO ENABLE { UNWIND => NULL; ThSmartsPrivate.LarkFailed => { Problem["Lark Failed", info]; GOTO Failing; }; }; prevL: ThSmartsPrivate.OpenConversations _ NIL; nb: Thrush.NB_success; convL: ThSmartsPrivate.OpenConversations; trans: TrkTransition; info.apprise _ FALSE; FOR convL _ info.conversations, convL.rest WHILE convL#NIL DO cDesc: ConvDesc = convL.first; stateNow: StateInConv = cDesc.cState.state; ours: BOOL _ ( cDesc.cState.credentials.convID = info.currentConvID ); IF pd.doReports THEN Report[ Rope.Concat[ IO.PutFR["**** TkSup: %t(%d) %g %g->%g", time[cDesc.cState.credentials.convID], int[cDesc.cState.credentials.stateID], TU.RefAddr[info], refAny[NEW[StateInConv_stateNow]], refAny[NEW[StateInConv_cDesc.desiredState]]], IO.PutFR[" %g%g\n", refAny[NEW[TrkTransition_trkTransForStates[stateNow][cDesc.desiredState]]], rope[IF ours THEN " (ours)" ELSE ""]]], info, pd.cSp]; BeNice[cDesc, 6, info]; IF NOT cDesc.descValid THEN LOOP; IF info.currentConvID = nullConvHandle AND stateNow#idle THEN { ours_TRUE; info.currentConvID _ cDesc.cState.credentials.convID; }; IF stateNow#idle AND (NOT ours) THEN nb _ ThParty.Advance[ credentials: cDesc.cState.credentials, state: idle, reason: busy, comment: "One conversation at a time, please." ] ELSE { SELECT (trans_trkTransForStates[stateNow][cDesc.desiredState]) FROM noop => NULL; elim => { IF pd.doReports THEN Report[IO.PutFR[" ** Zap: %t\n", time[cDesc.cState.credentials.convID]], info, pd.cZp]; IF prevL#NIL THEN { prevL.rest _ convL.rest; convL _ prevL; } ELSE info.conversations_convL.rest; IF ours THEN { info.currentConvID _ nullConvHandle; IF info.larkInfo.larkState=trunkForwarding THEN ThSmartsPrivate.EnterLarkState[info.larkInfo, idle, info]; }; }; alrt => { -- placing call convID: Thrush.ConversationHandle; info.phoneNumber _ NIL; [nb, convID] _ ThParty.Alert [ credentials: cDesc.cState.credentials, calledPartyID: cDesc.desiredPartyID, state: initiating ]; IF nb=success THEN { cDesc.cState.credentials.convID _ info.currentConvID _ convID; ours_TRUE; }; }; idle, actv => -- Simple transitions nb _ ThParty.Advance [ credentials: cDesc.cState.credentials, state: cDesc.desiredState, reason: cDesc.desiredReason, comment: cDesc.desiredComment ]; spvs => { IF cDesc.newEvent THEN { IF NOT cDesc.signallingStarted THEN { cDesc.signallingStarted _ TRUE; ThSmartsPrivate.EnterLarkState[info.larkInfo, IF info.phoneNumber=NIL THEN trunkTalking ELSE trunkSignalling, info, info.phoneNumber]; } ELSE IF cDesc.newProses#NIL AND cDesc.newProses.first.type=request THEN ThSmartsPrivate.QueueFeeps[info, cDesc.newProses]; }; cDesc.newIntervals _ NIL; -- Ignore interval Specs. cDesc.newProses _ NIL; -- Ignore prose Specs. }; pend => { -- Handle an incoming (from Etherphone) call. info.phoneNumber _ cDesc.cState.address; cDesc.desiredState_active; info.apprise _ TRUE; }; invl => { Problem["LarkSmarts: Invalid state transition request.", info]; info.apprise_TRUE; cDesc.desiredState _ idle; cDesc.desiredReason _ error; cDesc.desiredComment _ "Invalid state transition"; }; ntiy => { Problem["State transition not yet implemented.", info]; info.apprise_TRUE; cDesc.desiredState _ idle; cDesc.desiredReason _ error; cDesc.desiredComment _ "Unimplemented state transition"; }; ENDCASE => Thrush.pERROR; }; IF nb#success THEN ReportFR[" ** LKResults: nb=%g\n", info, pd.cRs, refAny[NEW[Thrush.NB_nb]]]; SELECT nb FROM success, stateMismatch => NULL; noSuchParty2, narcissism => { -- Have to get to error tone here. No such party. Problem["Called party not found, or calling self", info]; cDesc.cState.comment _ NIL; cDesc.cState.reason _ notFound; cDesc.desiredState_idle; info.apprise_TRUE; }; partyNotEnabled => { -- See old version; vamp until enabled. Problem["Party not yet enabled, ignore as much as possible.", info]; }; invalidTransition, convNotActive, convStillActive => { comment: ROPE="Party-level detected invalid state transition request"; Problem[comment, info]; cDesc.desiredState _ idle; cDesc.cState.comment _ comment; cDesc.cState.reason _ error; info.apprise_TRUE; }; notInConv, noSuchConv => { -- Complain, zap, go idle and repeat. comment: ROPE="NotInConv or NoSuchConv"; Problem[comment, info]; IF ours THEN info.currentConvID _ nullConvHandle; cDesc.cState.credentials.convID _ nullConvHandle; cDesc.cState.credentials.stateID _ 0; cDesc.desiredState _ idle; cDesc.cState.comment _ comment; cDesc.cState.reason _ error; info.apprise_TRUE; }; noSuchParty, noSuchSmarts => { -- Complain, deregister, we gone! Needs tuning. Problem[ "LarkTrunkSmarts: NoSuchParty or NoSuchSmarts reported, must try to go away", info]; GOTO Failing; }; ENDCASE => Thrush.pERROR; prevL _ convL; ENDLOOP; IF NOT info.apprise THEN WAIT info.thAction; IF info.conversations = NIL THEN EXIT; REPEAT Failing => ThSmartsPrivate.Deregister[info]; -- now Failed ENDLOOP; info.thProcess _ NIL; }; Problem: PROC[comment: ROPE, info: SmartsInfo] = { VoiceUtils.ProblemFR[Rope.Concat["LarkKSmarts(%g): ", comment], $Smarts, info, TU.RefAddr[info]]; }; TrkTransition: TYPE = { noop, elim, idle, alrt, pend, actv, spvs, invl, ntiy }; trkTransForStates: ARRAY StateInConv OF ARRAY StateInConv OF TrkTransition _ [ [ elim, invl, invl, invl, invl, invl, invl, invl, elim, ntiy, elim ], -- idle (current) [ invl, invl, invl, invl, invl, invl, invl, invl, invl, invl, invl ], -- reserved [ invl, invl, invl, invl, invl, invl, invl, invl, invl, invl, invl ], -- parsing [ idle, invl, invl, noop, invl, invl, invl, invl, noop, ntiy, invl ], -- initiating [ idle, invl, invl, invl, invl, invl, invl, invl, actv, ntiy, pend ], -- pending [ idle, invl, invl, invl, invl, noop, invl, invl, noop, ntiy, invl ], -- maybe [ invl, invl, invl, invl, invl, invl, invl, invl, invl, ntiy, invl ], -- ringing [ ntiy, invl, invl, invl, invl, invl, invl, invl, ntiy, ntiy, invl ], -- canActivate [ idle, invl, invl, invl, invl, invl, invl, invl, spvs, ntiy, invl ], -- active [ ntiy, invl, invl, invl, invl, invl, invl, invl, ntiy, noop, invl ], -- inactive [ elim, invl, invl, invl, invl, invl, invl, invl, alrt, ntiy, invl ] -- any (nonex) ]; ViewCmd: Commander.CommandProc = TRUSTED { Nice.View[pd, "Lark Trunk PD"]; }; Commander.Register["VuLarkTrunkSmarts", ViewCmd, "Program Management variables for Lark Trunk Smarts"]; }. ΤLarkTrunkSmartsImpl.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Smarts for the Lark "Back Door" Last modified by D. Swinehart, May 17, 1986 4:17:14 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. Types and Constants Initialization There's no provision here at all for reregistration. Yet. 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!) Ignore the incoming call. It will be handled as unanswered by the host telephone system. Supervision <> Conv isn't the one we're interested in, and doesn't look like it's going idle: idle it. Should probably consult transForStates for validity. State and substate (below) and desired state indicate there's something to do. Do it: Conv. is now idle: forget about it. Remove dead conversation from list. cDesc.cState.credentials.stateID _ 0; Analyze any results of trying to reach a different state. Complain and transition to idle Just codes to dispatch on in Supervisor; explained there idle resrv pars init pend mayb ring canAc activ inact any -- desired Debugging nonsense << Comments left over from previous system. Think about it all sometime. was in section involving analysis of new pending state. >> <> nb _ ChangeState[info, busy, "Sorry, trunk in use"]; RETURN; }; Swinehart, May 16, 1986 4:15:42 pm PDT Cedar 6.1 changes to: DIRECTORY, LarkTrunkSmartsImpl, Report, ReportFR, RegisterTrunk, Problem Κ ‚˜šœ™Icodešœ Οmœ1™˜>˜)J˜J˜Jšœ!˜!Jšœ˜—Jšžœžœžœ ˜,Jšœ1žœ˜7šœžœ#˜-J˜J˜J˜ Jšœ˜Jšœ˜—J˜)šž˜Jšœ žœ˜%—J˜——™JšœE™EJšœ‚™‚J™˜J˜š œžœžœ˜ Jšœ5˜5šžœ˜Jšžœžœ˜ JšœDžœ ˜SJ˜—šžœž˜šœžœž˜&J˜$J˜'Jšžœžœ˜—Jšžœ˜—šž˜˜ J˜Z——Jšœ˜J˜—š œžœžœ˜,J˜J˜šžœž˜(Jšœžœ˜ JšžœžœŸE˜X—Jšœ6žœ˜<šœžœŸ œ˜8JšœBŸ ˜L—šžœžœžœ˜*J™Y—J˜Jšœ%˜%J˜Jšœžœ˜JšœŸ3˜MJ˜Jšœ˜—J˜š  œžœžœ˜.J˜2Jšžœžœžœžœ˜šžœž˜JšœžœŸ0˜KJšžœžœŸ5˜H—J˜J˜Jšœžœ˜J˜J˜J˜——™ J˜š œžœžœ˜1šžœ˜ JšœR˜R—šžœžœž˜šžœ˜Jšžœžœ˜Jšœ>žœ ˜NJ˜—Jšœ+žœ˜/Jšœ žœ ˜Jšœ)˜)J˜Jšœžœ˜šžœ˜Jšœ žœžœž˜J˜Jšœ+˜+Jšœžœ<˜FJ˜šžœžœ˜šœ ˜ šžœ&˜(J˜MJšœžœ žœ#˜b—šžœ˜JšœžœA˜KJšœžœžœ žœ˜'——Jšœ˜—J˜J™Jšžœžœžœžœ˜!šžœ%žœžœ˜?J™gJšœžœ7˜@Jšœ˜—šžœžœžœž˜$J™WJ™4˜J˜&J˜ J˜ Jšœ.˜.—J˜—šžœ˜J™Ušžœ9ž˜CJšœžœ˜ šœ ˜ J™$J™#šžœžœžœ˜6Jšœ6˜6—šžœžœžœ˜Jšœ)˜)—Jšžœ˜#šžœžœ˜Jšœ$˜$šžœ)ž˜/J˜:—J˜—Jšœ˜—šœ Ÿ˜J˜"Jšœžœ˜˜J˜&Jšœ$˜$Jšœ˜Jšœ˜—˜J˜>Jšœ ˜ JšΟs%™%J˜—J˜—šœŸ˜#šœ˜J˜&Jšœ˜J˜Jšœ˜Jšœ˜——šœ ˜ šžœžœ˜šžœžœžœ˜%Jšœžœ˜˜.Jšžœžœžœžœ˜?J˜—J˜—š žœžœžœžœ$ž˜GJ˜2—J˜—JšœžœŸ˜3JšœžœŸ˜-J˜—šœ Ÿ-˜7J˜(J˜Jšœžœ˜J˜—˜ J˜?Jšœ žœ˜J˜J˜J˜2J˜—˜ J˜7Jšœ žœ˜J˜J˜J˜8J˜—Jšžœ˜—J˜—J™9J˜šžœ ž˜Jšœ9žœžœ˜M—J™šžœž˜Jšœžœ˜šœŸ2˜PJšœ9˜9Jšœžœ˜J˜Jšœ˜Jšœ žœ˜J˜—šœŸ'˜