<> <> <> <<>> <> DIRECTORY Commander USING [ CommandProc, Register ], IO, Lark -- USING [ -- CommandEvents, ConnectionSpec, Event, LarkModel, Passel, SHHH, StatusEvents ]--, LarkSmartsMonitorImpl, Nice, Log USING [ Problem, ProblemFR, ProblemHandle, Report, ReportFR ], 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 ; LarkTrunkSmartsImpl: CEDAR MONITOR LOCKS root IMPORTS Commander, root: LarkSmartsMonitorImpl, IO, Log, Nice, Process, Rope, Thrush, ThParty, ThPartyPrivate, ThSmartsPrivate, ThSmartsRpcControl, Triples, TU 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; Log.Report[what, $Lark, info.larkInfo]; ctr.count_ctr.count+1; IF ctr.count>ctr.stop THEN { Log.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; Log.ReportFR[what, $Lark, info.larkInfo, a1, a2]; ctr.count_ctr.count+1; IF ctr.count>ctr.stop THEN { Log.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 RETURN[Log.ProblemHandle["TrunkSmarts didn't register", $Party, nullHandle, hostParty]]; 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]; }; }; <> <> <station call) and "tones F" (station->trunk call being "answered")>> <> 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] = { Log.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 _ [ << idle resrv pars init pend mayb ring canAc activ inact any -- desired>> <<>> [ 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"]; }. <<>> <<>> <<<< Comments left over from previous system. Think about it all sometime. was in section involving analysis of new pending state. >>>> <<<>>> <>