<> <> <> <> < smartsHandle:>> <> <> <> <> <> <> <<>> <> <<$SmartsData[smarts]=smartsInfo>> <> <> <<$TrunkParty[party]=trunkParty>> <> <> <> <<>> <> IF pd.doReports THEN Report[ Rope.Concat[ IO.PutFR["---- LkProg: %t(%d) %g %g yr=%g ", time[event.credentials.convID], int[event.credentials.stateID], refAny[NEW[StateInConv_event.state]], TU.RefAddr[info], bool[yourParty]], IO.PutFR["lt=%g in=%g, ky=%g\n", bool[latestEvent], bool[event.intervalSpecs#NIL OR event.proseSpecs#NIL], bool[event.keyTable#NIL]]], info]; <> <> cDesc _ GetConv[info, event.credentials.convID, FALSE]; IF event.credentials.stateID <= cDesc.cState.credentials.stateID THEN RETURN[pass]; -- Old news! <<>> <> cDesc.cState.credentials.smartsID _ smartsID; cDesc.cState.credentials.stateID _ event.credentials.stateID; <<<> <>>> <> IF event.keyTable#NIL THEN { cDesc.cState.keyTable _ event.keyTable; cDesc.newKeys_TRUE; }; IF event.intervalSpecs#NIL THEN <> EnqueueIntervals[cDesc, event.intervalSpecs]; IF event.proseSpecs#NIL THEN { req: BOOL _ FALSE; FOR pSL: ProseSpecs _ event.proseSpecs, pSL.rest WHILE pSL#NIL DO IF pSL.first.type=request THEN { req _ TRUE; <> <> <> <> <<};>> }; ENDLOOP; IF req OR ~info.larkInfo.textToSpeech THEN EnqueueProses[cDesc, event.proseSpecs]; }; IF event.address#NIL THEN { cDesc.cState.address_event.address; cDesc.newAddress_TRUE; }; <<>> <> IF yourParty THEN { cDesc.descValid _ TRUE; cDesc.cState.credentials.partyID _ event.credentials.partyID; cDesc.cState.state _ event.state; cDesc.cState.comment _ event.comment; cDesc.cState.reason _ event.reason; <<<< When Conferencing is added, put this back in.>>>> <> IF event.spec#NIL THEN { cDesc.cState.spec _ event.spec; cDesc.newSpec_TRUE; }; <> IF event.comment#NIL THEN cDesc.cState.comment _ event.comment; IF event.urgency#normal THEN cDesc.cState.urgency _ event.urgency; IF event.alertKind#standard THEN cDesc.cState.alertKind _ event.alertKind; SELECT event.state FROM reserved, parsing, initiating => cDesc.originator _ us; maybe => { cDesc.originator _ us; <> IF info.Supervise = LarkSupervise THEN info.larkInfo.ringTune _ event.ringTune; }; pending => cDesc.originator _ them; ringing => { cDesc.originator _ them; info.larkInfo.ringTune _ event.ringTune; }; ENDCASE; }; BeNice[event, 4, info]; BeNice[cDesc, 6, info]; <<>> cDesc.newEvent _ TRUE; IF latestEvent THEN Apprise[info]; -- Wait for the last report to wake process. }; <> <> <> <> <<};>> <<>> <> <> <> <> <> <> <> <> <<};>> <<>> <> <> <> <> <> <> <<};>> <<>> <> Apprise: PUBLIC INTERNAL PROC[info: SmartsInfo] = TRUSTED --INLINE-- { IF info.thProcess=NIL THEN Process.Detach[info.thProcess _ FORK info.Supervise[info]]; info.apprise _ TRUE; NOTIFY info.thAction; }; LarkSupervise: PUBLIC ENTRY PROC[info: SmartsInfo ] = { TRUSTED { Process.SetTimeout[@info.thAction, Process.SecondsToTicks[pd.timeoutNoAction]]; }; IF info.apprise THEN DO ENABLE { UNWIND => NULL; ThSmartsPrivate.LarkFailed => GOTO Failing; }; prevL: OpenConversations _ NIL; nb: NB_success; convL: OpenConversations; trans: Transition; 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["**** LkSup: %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[Transition_transForStates[stateNow][cDesc.desiredState]]], rope[IF ours THEN " (ours)" ELSE ""]]], info]; BeNice[cDesc, 6, info]; <<>> IF NOT cDesc.descValid THEN LOOP; IF info.currentConvID = nullConvID --AND stateNow#idle-- THEN { <<<>>> ours_TRUE; info.currentConvID _ cDesc.cState.credentials.convID; }; IF stateNow=idle THEN { <> <> IF pd.doReports THEN ReportFR[" ** Zap %t\n", info, time[cDesc.cState.credentials.convID]]; IF prevL#NIL THEN { prevL.rest _ convL.rest; convL _ prevL; } ELSE info.conversations_convL.rest; IF ours THEN { reason: Thrush.Reason = cDesc.cState.reason; newConvID: ConversationID_cDesc.cState.credentials.convID; newDesc: ConvDesc; info.currentConvID _ nullConvID; SELECT info.larkInfo.hookState FROM onhook, spkr => SELECT reason FROM terminating, wontSay => GOTO Exit; ENDCASE => IF cDesc.originator#us THEN GOTO Exit; ENDCASE => SELECT TRUE FROM reason=terminating, reason=wontSay, cDesc.originator#us => { <> newConvID _ nullConvID; info.currentConvID _ nullConvID; }; ENDCASE; newDesc _ GetConv[info, newConvID, TRUE]; ChangeState[info, newDesc, reserved, cDesc.cState.reason, cDesc.cState.comment]; -- Now go around the loop once, if only to coerce Lark into idle state. EXITS Exit => NULL; }; } ELSE IF NOT ours THEN <> <> nb _ ThParty.Advance[ credentials: cDesc.cState.credentials, state: idle, reason: busy, comment: "One conversation at a time, please." ] ELSE { <> SELECT (trans_transForStates[stateNow][cDesc.desiredState]) FROM noop => NULL; elim => ERROR; -- handled in stateNow=idle case above. rsrv, rers, prsg, alrt => { -- placing call, or reserving conversation convID: ConversationID; calledPartyID: PartyID = IF trans#alrt THEN nullID ELSE cDesc.desiredPartyID; nb _ IF trans=alrt AND calledPartyID=nullID THEN noSuchParty2 ELSE success; IF NOT ours THEN Problem["What to do in a race?", info]; IF nb=success THEN [nb, convID] _ ThParty.Alert [ credentials: cDesc.cState.credentials, calledPartyID: calledPartyID, state: IF trans = alrt THEN initiating ELSE cDesc.desiredState, reason: cDesc.desiredReason, newConv: trans=rsrv, comment: cDesc.desiredComment ]; 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 => { -- Supervision while active. cDesc.desiredState_active; -- Reflect reality FOR iL: IntervalSpecs _ cDesc.newIntervals, iL.rest WHILE iL#NIL DO <> iS: Thrush.IntervalSpec = iL.first; IF iS.interval.length#0 OR iS.queueIt THEN -- else flush: ignore SELECT iS.type FROM started => cDesc.desiredIntID _ iS.intID; finished => IF iL.rest=NIL AND iS.intID = cDesc.desiredIntID THEN SELECT info.larkInfo.hookState FROM spkr => ChangeState[info, cDesc, idle]; ENDCASE; ENDCASE; ENDLOOP; cDesc.newIntervals _ NIL; <> <> IF cDesc.newProses#NIL AND info.larkInfo.textToSpeech THEN { copyNewProses: ProseSpecs _ cDesc.newProses; cDesc.newProses _ NIL; EnqueueProses[cDesc, copyNewProses]; <> ThSmartsPrivate.EnterLarkState[ info.larkInfo, LarkStateForState[cDesc, stateNow, info.larkInfo.larkState], info]; cDesc.newProses _ copyNewProses; }; FOR pL: ProseSpecs _ cDesc.newProses, pL.rest WHILE pL#NIL DO <> pS: Thrush.ProseSpec = pL.first; SELECT pS.type FROM request => IF info.larkInfo.textToSpeech THEN { pS.type _ started; pS.prose _ ""; <> }; started => cDesc.desiredProseID _ pS.intID; finished => IF ~info.larkInfo.textToSpeech AND pL.rest=NIL AND pS.intID = cDesc.desiredProseID THEN SELECT info.larkInfo.hookState FROM spkr => ChangeState[info, cDesc, idle]; ENDCASE; ENDCASE; ENDLOOP; IF cDesc.newProses#NIL AND info.larkInfo.textToSpeech THEN { nb _ ThParty.SetProse[ shhh: info.larkInfo.shh, credentials: cDesc.cState.credentials, proseSpecs: cDesc.newProses ]; IF nb=success THEN cDesc.newProses_NIL; } ELSE cDesc.newProses _ NIL; }; <> <> ring => -- This is how you say you'll ring an incoming call IF info.larkInfo.autoAnswer THEN { info.apprise_TRUE; cDesc.desiredState _ active; } ELSE nb _ ThParty.Advance [ credentials: cDesc.cState.credentials, state: ringing ]; invl => { comment: ROPE="Invalid state transition"; Problem[comment, info]; ChangeState[info, cDesc, idle, error, comment]; }; ntiy => { comment: ROPE= "Unimplemented state transition"; Problem[comment, info]; ChangeState[info, cDesc, idle, error, comment]; }; ENDCASE => ERROR; }; <> IF nb#success THEN ReportFR[" ** Results: nb=%g\n", info, refAny[NEW[Thrush.NB_nb]]]; <<>> SELECT nb FROM success, stateMismatch => NULL; noSuchParty2, narcissism => -- Have to get to error tone here. No such party. ChangeState[info, cDesc, idle, notFound, "Called party not found, or calling self"]; partyNotEnabled => ours_FALSE; invalidTransition, convNotActive, convStillActive => { <> comment: ROPE="Party-level detected invalid state transition request"; Problem[comment, info]; ChangeState[info, cDesc, idle, error, comment]; }; notInConv, noSuchConv => { -- Complain, zap, go idle and repeat. comment: ROPE="NotInConv or NoSuchConv"; Problem[comment, info]; IF ours THEN info.currentConvID _ nullConvID; cDesc.cState.credentials.convID _ nullConvID; cDesc.cState.credentials.stateID _ 0; ChangeState[info, cDesc, idle, error, comment]; }; noSuchParty, noSuchSmarts => { -- Complain, deregister, we gone! Needs tuning. Problem[ "LkSmts: NoSuchParty or NoSuchSmarts reported, must try to go away", info]; GOTO Failing; }; ENDCASE => ERROR; <> IF ours THEN ThSmartsPrivate.EnterLarkState[ info.larkInfo, LarkStateForState[cDesc, stateNow, info.larkInfo.larkState], info]; 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; }; DoGoIdle: INTERNAL PROC[ cDesc: ConvDesc, reason: Thrush.Reason_$terminating, comment: Rope.ROPE_NIL, conditions: { $unconditional, $orphan } _ $orphan] = { <> nb: NB; SELECT conditions FROM $unconditional => NULL; $orphan => { nb: NB; cInfo: ThParty.ConversationInfo; [nb, cInfo] _ ThParty.GetConversationInfo[credentials]; IF nb = $success AND (cInfo.numParties-cInfo.numIdle) > 1 THEN RETURN; -- We're not alone IF reason # NIL THEN { reason _ $error; comment _ "System Error: Inconsistent information"; Report["Inconsistent information", info]; }; }; ENDCASE => ERROR; ChangeState[cDesc, $idle, reason, comment]; }; <> RingMethod: TYPE = ATOM; -- { $standard=NIL, $ownTune, $bothTunes }; <> <> <<>> ringEnable: Thrush.RingEnable _ $on, -- are we noisily accepting calls? defaultRingEnable: Thrush.RingEnable _ $on, -- are we noisily accepting calls? ringDo: RingMethod _ NIL, -- ($standard) we use the ring tune? ringTime: BasicTime.GMT_NULL, -- if not, when again? ringTune: LarkPlay.ToneSpec, -- Specification of ring tune ringTuneRope: Thrush.ROPE_NIL, <> GetStdRingInfo: PUBLIC PROC [partyID: PartyID] = { party: PartyData_ThPartyPrivate.DehandleParty[partyID]; larkRname: ROPE; rdAtom: ATOM; IF party=NIL THEN RETURN; SELECT party.type FROM $individual, $telephone => { larkRname _ GetRnameFromParty[party].Concat[larkRegistry]; IF larkRname=larkRegistry THEN RETURN; party.ringEnable _ SELECT NamesGV.GVGetAttribute[rName: larkRname, attribute: $ringmode, default: "R"].Fetch[0] FROM 'R => $on, 'S => $subdued, 'O => $off, ENDCASE => $on; rdAtom _ VoiceUtils.MakeAtom[NamesGV.GVGetAttribute[rName: larkRname, attribute: $dotune, default: "false"]]; party.ringDo _ SELECT rdAtom FROM $true => ownTune, $false => standard, $tune => ownTune, $both => bothTunes, ENDCASE => standard; party.ringTuneRope _ Rope.Cat[NamesGV.GVGetAttribute[larkRname, $tunea, NIL], NamesGV.GVGetAttribute[larkRname, $tuneb, NIL], NamesGV.GVGetAttribute[larkRname, $tunec, NIL]]; }; ENDCASE; }; SetStdRingInfo: PUBLIC PROC[partyID: PartyID, update: BOOL_FALSE] = { party: PartyData_ThPartyPrivate.DehandleParty[partyID]; larkRname: ROPE; IF party=NIL THEN RETURN; SELECT party.type FROM $telephone, $individual => { r: ROPE; larkRname _ GetRnameFromParty[party].Concat[larkRegistry]; IF larkRname=larkRegistry THEN RETURN; r _ SELECT party.ringEnable FROM $on => "R", $subdued => "S", $off => "O", ENDCASE => NIL; IF r#NIL THEN NamesGV.GVSetAttribute[larkRname, $ringmode, r]; IF update THEN NamesGV.GVUpdate[larkRname]; }; ENDCASE; }; SetRingEnable: PUBLIC PROC[shh: SHHH_none, partyID: PartyID, ringEnable: Thrush.RingEnable_$noChange, ringInterval: INT_0, update: BOOL_FALSE] = { party: PartyData_ThPartyPrivate.DehandleParty[partyID]; IF party=NIL THEN RETURN; GetStdRingInfo[partyID]; SELECT party.type FROM $telephone, $individual => { IF ringEnable=$telephone, $noChange THEN ringEnable _ party.ringEnable; party.ringEnable _ ringEnable; party.ringTime _ BasicTime.Update[BasicTime.Now[], ringInterval]; [] _ PrepareRingTune[party]; SELECT ringEnable FROM $on, $subdued, $off => { IF update AND ringEnable#party.defaultRingEnable THEN SetStdRingInfo[partyID, ThNet.pd.autoGVUpdate]; party.defaultRingEnable _ ringEnable; }; ENDCASE; }; ENDCASE; }; PrepareRingTune: PROC[party: PartyData] RETURNS[ringTune: LarkPlay.ToneSpec] = { SELECT party.type FROM individual => { <> IF ThNet.pd.ringsInvalid THEN { ThNet.pd.ringsInvalid _ FALSE; ringTone _ NEW[LarkPlay.ToneSpecRec _ [ repeatIndefinitely: TRUE, volume: ThNet.pd.defaultRingVolume, tones: LIST[LIST[ [f1: 440, f2: 480, on: 2000, off: 4000], [f1: 440, f2: 480, on: 2000, off: 4000]]]]]; subduedRingTone _ NEW[LarkPlay.ToneSpecRec _ [ repeatIndefinitely: FALSE, volume: ThNet.pd.defaultRingVolume+ThNet.pd.subduedVolumeInterval, tones: LIST[LIST[[f1: 440, f2: 480, on: 500, off: 0]]] ]]; outsideRingTune _ LarkPlay.PlayString[outsideRingTuneRope, FALSE, ThNet.pd.defaultRingVolume]; }; IF party.ringDo#standard AND party.ringTuneRope#NIL THEN ringTune _ LarkPlay.PlayString[music: party.ringTuneRope, file: FALSE, volume: ThNet.pd.defaultRingVolume] ELSE { pattern: LarkPlay.ToneSpec = SELECT party.ringEnable FROM subdued, subduedTimed => subduedRingTone, ENDCASE => ringTone; ringTune _ NEW[LarkPlay.ToneSpecRec_pattern^]; }; IF ringTune#NIL THEN SELECT party.ringEnable FROM subdued, subduedTimed => { ringTune.volume _ ringTune.volume + ThNet.pd.subduedVolumeInterval; ringTune.repeatIndefinitely _ FALSE; }; ENDCASE; party.ringTune _ ringTune; }; ENDCASE; }; <> creatorPartyID: Thrush.PartyID _ Thrush.nullID, -- now expressed in Triple newKeyTable: BOOL_FALSE, -- IF key table has changed but not yet been posted log: Thrush.EventSequence -- no longer needed; MBQueues form log, and once-only events are no longer reported that way. <<>> <> CFRef: TYPE = REF CFRec; -- Ref to party/conversation record CFRec: TYPE = RECORD [ -- One such CFRec for each Party-Conversation pair event: Thrush.ConvEvent, -- describes a state and the event that put it there. <> voiceSmartsID: Thrush.SmartsID_Thrush.nullID, -- the one that will carry the conversation load for this Party in this conversation <> sockID: LONG CARDINAL_NULL, -- local socket ID for this participant. <> lastPostedID: Thrush.StateID_0, -- last time event referring to this party was posted lastNotedID: Thrush.StateID_0 -- last time this party's smarts were informed. ]; <smarts distribution declarations>> <<>> Supervisor: PROC[party: PartyData]; -- supervision process root. Supervise: PROC[party: PartyData]; -- Creates or joggles a Supervisor. Use where one would normally do a NOTIFY party.actionNeeded DistributionProc: TYPE = PROC[ smarts: SmartsData ] RETURNS [ d: Thrush.Disposition ]; -- controls whether distribution will progress. Distribute: PROC[ party: PartyData, proc: DistributionProc] RETURNS [ d: Thrush.Disposition ]; -- accept/reject/pass, depending on whether anybody did. <<>> GetEvent: PROC[conv: ConversationData, stateID: Thrush.StateID] RETURNS [ event: Thrush.ConvEvent ]; <<>> <<>> <> <<>> Activate: INTERNAL PROC[conv: ConversationData, myCfRef: CFRef, myParty: PartyData] <> RETURNS[isSpec: BOOL_FALSE] = { sockets: ARRAY[0..2) OF Lark.VoiceSocket; parties: ARRAY[0..2) OF PartyData; specIndex: NAT_0; GetOneActive: INTERNAL Triples.ForeachProc = { cfRef: CFRef; smarts: SmartsData; sockID: PupTypes.PupSocketID; IF specIndex>=2 THEN RETURN[FALSE]; WITH trip.att SELECT FROM r: CFRef => cfRef _ r; ENDCASE=> RETURN[TRUE]; IF cfRef.voiceSmartsID = nullID THEN RETURN[TRUE]; smarts _ UnsealSmarts[cfRef.voiceSmartsID]; IF smarts=NIL THEN RETURN--[FALSE]--; -- fail back sockID _ LOOPHOLE[cfRef.sockID]; WITH smarts.properties SELECT FROM $voiceTerminal => sockets[specIndex] _ [ [ machine.net], [machine.host], sockID ]; ENDCASE => VoiceUtils.Problem["Non-voice terminal trying to be voice smarts", $System]; parties[specIndex] _ NARROW[trip.val]; specIndex _ specIndex+1; }; myParty.partyActive_TRUE; conv.numActive _ conv.numActive + 1; Triples.Foreach[Triples.Any, conv, Triples.Any, GetOneActive]; IF specIndex#2 THEN RETURN[FALSE]; SELECT myParty FROM =parties[0] => specIndex _ 0; =parties[1] => specIndex _ 1; ENDCASE => VoiceUtils.Problem["Impossible", $System]; myCfRef.event.spec _ NEW[Lark.ConnectionSpecRec _ [ protocol: interactive, encoding: $muLaw, sampleRate: 8000, packetSize: 160, -- sample bytes, that is. buffer: out1, keyIndex: IF ThNet.pd.encryptVoice THEN 1 ELSE 0, localSocket: sockets[specIndex], remoteSocket: sockets[1-specIndex], blankA: 0 ]]; RETURN[TRUE]; }; <> <<>> OtherProc: Triples.ForeachProc _ SELECT state FROM $idle => IdleLast, $ringing, $active => FindCaller, ENDCASE => NIL; <<>> IdleLast: Triples.ForeachProc = { <> WITH trip.att SELECT FROM r: CFRef => { otherState _ $idle; SELECT r.event.state FROM $idle => RETURN[TRUE]; ENDCASE=>NULL; IF otherParty#NIL THEN { otherParty_NIL; RETURN[FALSE]; }; otherParty_NARROW[trip.val]; otherCfRef _ r; }; ENDCASE; }; FindCaller: Triples.ForeachProc = { <> WITH trip.att SELECT FROM r: CFRef => { otherState _ IF state = $ringing THEN $maybe ELSE $active; SELECT r.event.state FROM $initiating, $maybe => NULL; ENDCASE => RETURN[TRUE]; otherParty _ NARROW[trip.val]; otherCfRef _ r; RETURN[FALSE]; }; ENDCASE; }; IF OtherProc#NIL THEN Triples.Foreach[Triples.Any, conv, Triples.Any, OtherProc]; IF otherParty = NIL THEN RETURN; <> SELECT validTransitions[otherState][otherCfRef.event.state] FROM $no => { VoiceUtils.Problem[IO.PutFR["%g, %g, %g: State mismatch among parties", TU.RefAddr[party], TU.RefAddr[otherParty], TU.RefAddr[cfRef]], $System]; RETURN[nb: $stateMismatch]; }; <> $cantReq, $valid => NULL; $noop => RETURN; ENDCASE => ERROR; []_PostConvEvent[ <> conv: conv, party: otherParty, state: otherState, reason: reason, comment: comment]; validTransitions: ARRAY Thrush.StateInConv OF ARRAY Thrush.StateInConv OF Validity = [ <> [ noop, valid, valid, valid, valid, valid, valid, valid, valid, valid, no ], -- idle [ valid, noop, valid, no, no, no, no, no, no, no, no ], -- reserved [ valid, valid, noop, no, no, no, no, no, no, no, no ], -- parsing [ valid, valid, valid, noop, no, no, no, no, no, no, no ], -- initiating [ cantReq, cantReq, cantReq, no, noop, no, no, no, no, no, no ], -- pending [ no, no, no, cantReq, no, noop, no, no, no, no, no ], -- maybe [ no, no, no, no, valid, no, noop, no, no, no, no ], -- ringing [ cantReq, cantReq, cantReq, cantReq, cantReq, cantReq, cantReq, noop, no, cantReq, no ], -- canAct [ valid, valid, valid, cantReq, valid, cantReq, valid, valid, noop, valid, no ], -- active [ no, no, no, no, no, no, no, valid, valid, noop, no ], -- inactive [ no, no, no, no, no, no, no, no, no, no, no ] -- any ]; Validity: TYPE = { $noop, -- Already in this state; no transition action required $valid, -- This transition is all right if other tests check out. $no, -- This transition is not allowed, nohow. $cantReq -- This transition may not be requested by the Party-level client. }; <> <> SELECT state FROM $initiating, $active, $canActivate => IF cfRef.voiceSmartsID=nullID THEN { smarts: SmartsData _ UnsealSmarts[smartsID]; IF smarts#NIL THEN WITH smarts.properties SELECT FROM $backstop, $supervisor => NULL; -- << ERROR? >> voiceTerminal => cfRef.voiceSmartsID _ smartsID; $manager => { smarts _ NARROW[Triples.Select[$AdjacentTerminal, party, -- smarts --]]; IF smarts # NIL THEN cfRef.voiceSmartsID _ H[smarts]; }; ENDCASE; }; ENDCASE; <> lastEvent _ cfRef.event; lastState _ IF lastEvent=NIL THEN $idle ELSE lastEvent.state; cfRef.lastPostedID _ conv.currentStateID; IF state=$active THEN { IF lastState#$active THEN IF party.partyActive THEN event.state _ $canActivate ELSE [] _ Activate[conv, cfRef, party] } --<> <> GetEvent: PUBLIC INTERNAL PROC[conv: ConversationData, stateID: Thrush.StateID] RETURNS [ Thrush.ConvEvent_NIL ] = { IF conv=NIL THEN VoiceUtils.ProblemFR["%g: No such conversation", $System, NIL, TU.RefAddr[conv]] ELSE IF stateID=0 THEN RETURN[NIL] ELSE IF stateID>=conv.log.size THEN VoiceUtils.Problem["State ID out of range", $System] ELSE RETURN[conv.log[stateID]]; }; InsertEvent: INTERNAL PROC[conv: ConversationData, entry: ConvEvent] = { IF conv.currentStateID >= conv.log.size THEN { oldLog: Thrush.EventSequence = conv.log; newLog: Thrush.EventSequence = NEW[Thrush.EventSequenceBody[oldLog.size+ThPartyPrivate.logSizeIncrement]]; FOR i: NAT IN [0..oldLog.size) DO newLog[i]_oldLog[i]; ENDLOOP; conv.log _ newLog; }; conv.log[conv.currentStateID] _ entry; }; <> <> <> <> <> <> <<] RETURNS [ nb: Thrush.NB, events: Thrush.EventSequence_NIL ] = {>> < NULL;>> <> <> <<[conv, , , nb] _ ThPartyPrivate.Verify[credentials];>> <> <conv.currentStateID THEN lastState_conv.currentStateID;>> <