DIRECTORY IO, Lark USING [ bStar, bThorp, CommandEvent, CommandEvents, CommandEventSequence, Device, disabled, DTMFEvent, enabled, endNum, Event, Passel, reset, StatusEvent, StatusEvents, SHHH ], LarkSmarts, Log USING [ ProblemBool, Report, SLOG ], Nice, RPC USING [ CallFailed ], Rope USING [ ROPE ], Process USING [ Detach, MsecToTicks, SetTimeout, Ticks ], ThPartyPrivate USING [ SmartsData ], ThSmartsPrivate USING [ click, EnterLarkSt, GetSmartsInfo, HookState, LarkInfo, LarkState, RingDetState, SmartsInfo, TerminalType ], Thrush USING[ H, pERROR, ROPE, SHHH, SmartsHandle, ThHandle ], ThNet USING [ pd ] ; LarkInImpl: CEDAR MONITOR LOCKS info USING info: LarkInfo IMPORTS IO, Log, Nice, Process, RPC, ThNet, Thrush, ThSmartsPrivate EXPORTS LarkSmarts, ThSmartsPrivate= { OPEN IO; H: PROC[r: REF] RETURNS[Thrush.ThHandle] = INLINE {RETURN[Thrush.H[r]]; }; HookState: TYPE = ThSmartsPrivate.HookState; LarkInfo: TYPE = ThSmartsPrivate.LarkInfo; RingDetState: TYPE = ThSmartsPrivate.RingDetState; SmartsData: TYPE = ThPartyPrivate.SmartsData; SmartsInfo: TYPE = ThSmartsPrivate.SmartsInfo; SmartsHandle: TYPE = Thrush.SmartsHandle; TerminalType: TYPE = ThSmartsPrivate.TerminalType; ROPE: TYPE = Thrush.ROPE; bStar: Lark.Event = Lark.bStar; bThorp: Lark.Event = Lark.bThorp; enabled: Lark.Event = Lark.enabled; endNum: Lark.Event = Lark.endNum; disabled: Lark.Event = Lark.disabled; reset: Lark.Event = Lark.reset; PD: TYPE = RECORD [ callTimeoutOK: BOOL_FALSE -- set to keep Thrush alive when debugging a Lark. ]; pd: REF PD _ NEW[PD_[]]; DebEvent: PROC[info: SmartsInfo, ev: Lark.StatusEvent] = INLINE { s: IO.STREAM=IO.ROS[]; s.PutF["[%3B, ",card[LONG[LOOPHOLE[ev.device, CARDINAL]]]]; IF ev.event<='z THEN s.PutF["%g] ",char[ev.event]] ELSE s.PutF["<%3B>] ", card[LONG[LOOPHOLE[ev.event, CARDINAL]]]]; Log.Report[s.RopeFromROS[], $LarkDetailed, info.larkInfo]; }; crowbar: BOOL_FALSE; -- TRUE to check basic RPC performance from Lark. RecordEvent: PUBLIC PROC[shh: Thrush.SHHH, smartsID: Thrush.SmartsHandle, whatHappened: Lark.StatusEvents] RETURNS [ success: BOOL_TRUE ] = { smartsInfo: SmartsInfo = ThSmartsPrivate.GetSmartsInfo[smartsID: smartsID]; parseInfo: SmartsInfo _ smartsInfo; info: LarkInfo; parseSmarts: SmartsData; IF smartsInfo=NIL OR (info _ smartsInfo.larkInfo)=NIL THEN RETURN[FALSE]; parseSmarts _ smartsInfo.smarts; FOR i: Lark.Passel IN [0 .. whatHappened.length) DO sEvent: Lark.StatusEvent _ whatHappened[i]; IF ThNet.pd.debug THEN DebEvent[smartsInfo, sEvent]; IF crowbar OR info.larkState=failed OR info.larkState=recovering THEN LOOP; Log.SLOG[]; SELECT sEvent.device FROM ringDetect, tones => { parseSmarts _ smartsInfo.otherSmarts; parseInfo _ ThSmartsPrivate.GetSmartsInfo[smarts: parseSmarts]; }; ENDCASE; SELECT sEvent.device FROM speakerSwitch => sEvent _ InterpretSpeakerSwitch[info, sEvent]; ringDetect => sEvent _ InterpretRingDetect[info, H[parseSmarts], parseInfo, sEvent]; touchPad => IF sEvent.event=disabled THEN LOOP; -- Up transitions of DTMF pad. ENDCASE; IF sEvent.device=nothing THEN LOOP; parseInfo.ParseEvent[ smartsInfo: parseInfo, sEvent: sEvent ]; ENDLOOP; }; EventRope: PUBLIC PROC[ shh: Thrush.SHHH, smartsID: SmartsHandle, time: CARDINAL, device: Lark.Device, events: ROPE] RETURNS[success: BOOL] = { NULL; }; InterpretSpeakerSwitch: PROC[info: LarkInfo, sEvent: Lark.StatusEvent] RETURNS [ processedEvent: Lark.StatusEvent] = --<>-- { interval: INTEGER = LOOPHOLE[sEvent.time-info.swOnTime]; processedEvent _ sEvent; SELECT sEvent.event FROM enabled => info.swOnTime _ sEvent.time; disabled => NULL; ENDCASE => ERROR Thrush.pERROR; IF interval < 0 OR interval > spClickInterval THEN RETURN; processedEvent.event _ ThSmartsPrivate.click; }; spClickInterval: INTEGER _ 500; InterpretRingDetect: ENTRY PROC[info: LarkInfo, smartsID: SmartsHandle, smartsInfo: SmartsInfo, sEvent: Lark.StatusEvent] RETURNS [ processedEvent: Lark.StatusEvent _ [ 0, nothing, Lark.reset ] ] = --<>-- { ENABLE UNWIND=>NULL; interval: INTEGER = LOOPHOLE[sEvent.time-info.ringChangeTime]; event: Lark.Event = sEvent.event; info.ringChangeTime _ sEvent.time; SELECT event FROM enabled => SELECT info.ringDetState FROM idle, between=>NULL; ENDCASE=>RETURN; disabled => SELECT info.ringDetState FROM idle, between=>RETURN; ENDCASE; ENDCASE=>RETURN; SELECT info.ringDetState FROM idle => TRUSTED { -- known enabled -- info.ringDetState _ maybe; info.ringDetWaitState_idle; info.ringDetInstance _ info.ringDetInstance+1; Process.Detach[FORK RingDetProc[smartsID, smartsInfo, info.ringDetInstance]]; }; maybe => { -- known disabled -- IF interval { -- known disabled -- info.ringDetState _ between; }; ring => { -- known disabled -- info.ringDetState _ IF interval > ringInterval THEN between ELSE idle; }; between => { -- known enabled -- info.ringDetState _ IF interval < breakInterval THEN ring ELSE idle; }; ENDCASE; IF info.ringDetState#info.ringDetWaitState THEN NOTIFY info.ringDetCondition; }; debounceInterval: INTEGER _ 300; -- ms. ringInterval: INTEGER = 1700; -- ms., 2 sec. nominal, 300 ms. benefit of doubt breakInterval: INTEGER = 4500; -- ms., 4 sec. nominal, 500 ms. benefit of doubt debounceIntTicks: Process.Ticks = Process.MsecToTicks[debounceInterval]; ringIntTicks: Process.Ticks = Process.MsecToTicks[ringInterval]; breakIntTicks: Process.Ticks = Process.MsecToTicks[breakInterval]; RingDetProc: PROC[smartsID: SmartsHandle, smartsInfo: SmartsInfo, instance: CARDINAL] = { info: LarkInfo = smartsInfo.larkInfo; event: Lark.Event; RingDetProcEntry: ENTRY PROC[info: LarkInfo] RETURNS[stillRunning: BOOL] = --INLINE-- { ENABLE UNWIND=>NULL; newState: RingDetState = IF info.ringDetInstance#instance THEN idle ELSE info.ringDetState; event _ reset; IF newState#idle AND newState=info.ringDetWaitState THEN -- timed out SELECT info.ringDetState FROM idle => RETURN[FALSE];--??-- maybe => { info.ringDetState _ ring1; event _ enabled; RETURN[TRUE]; }; ring1, between => info.ringDetState _ idle; ring => info.ringDetState _ between; --??-- ENDCASE; info.ringDetWaitState _ info.ringDetState; TRUSTED { SELECT info.ringDetState FROM idle => { event _ disabled; RETURN[FALSE]; }; maybe => Process.SetTimeout[@info.ringDetCondition, debounceIntTicks]; ring1 => Process.SetTimeout[@info.ringDetCondition, ringIntTicks+breakIntTicks]; ring => Process.SetTimeout[@info.ringDetCondition, ringIntTicks]; between => Process.SetTimeout[@info.ringDetCondition, breakIntTicks]; ENDCASE; }; WAIT info.ringDetCondition; RETURN[ TRUE ]; }; DO stillRunning:BOOL_ RingDetProcEntry[info]; IF event#reset THEN smartsInfo.ParseEvent[smartsInfo, [info.ringChangeTime, ringDetect, event ] ]; IF ~stillRunning THEN RETURN; ENDLOOP; }; InterpretHookState: PUBLIC ENTRY PROC [ info: LarkInfo, rawEvent: Lark.StatusEvent ] RETURNS [ processedEvent: Lark.StatusEvent ] = { ENABLE UNWIND=>NULL; event: Lark.Event = rawEvent.event; ev: EvType _ SELECT rawEvent.device FROM hookSwitch => SELECT event FROM Lark.enabled => IF info.monitor THEN spMon ELSE tsOn, Lark.disabled => tsOff, ThSmartsPrivate.click => spClick, ENDCASE=> spNone, speakerSwitch => SELECT event FROM Lark.enabled => spOn, Lark.disabled => spOff, ThSmartsPrivate.click => spClick, ENDCASE => spNone, ENDCASE => spNone; newState: HookState; oldType: TerminalType_info.terminalType; processedEvent _ rawEvent; IF ev = spNone THEN { processedEvent.event _ SELECT rawEvent.device FROM keyboard => SELECT event FROM '[ => enabled, '] => disabled, '\n => endNum, ENDCASE => event, touchPad => SELECT event FROM bThorp => endNum, bStar => '*, IN Lark.DTMFEvent => event - (FIRST[Lark.DTMFEvent]-'0), ENDCASE=>event, ENDCASE => event; RETURN; }; newState _ newStates[info.hookState][ev]; processedEvent.device _ IF newState=onhook OR info.hookState=onhook THEN hookSwitch ELSE nothing; IF event=ThSmartsPrivate.click THEN processedEvent.event_disabled; info.hookState _ newState; info.terminalType _ SELECT newState FROM onhook => std, telset, both, bOth => IF info.radio THEN radio ELSE std, spkr, sPkr, spKr => IF info.radio THEN radio ELSE spkr, monitor => monitor, ENDCASE=> IF Log.ProblemBool[, $Lark, , info] THEN std ELSE std; IF oldType#info.terminalType THEN ThSmartsPrivate.EnterLarkSt[info, info.larkState, NIL]; }; unRevCommands: Lark.CommandEvents _ NEW[Lark.CommandEventSequence[2]]; CheckHookState: PUBLIC ENTRY PROC [info: LarkInfo ] RETURNS [ onHook: BOOL_TRUE ] = { ENABLE RPC.CallFailed => IF pd.callTimeoutOK THEN RESUME ELSE GOTO Failed; reverted, wasReverted: BOOL_FALSE; which: CARDINAL; DO which_0; reverted _ FALSE; DO events: Lark.StatusEvents; [which, events] _ info.interface.WhatIsStatus[info.shh, which]; FOR i: NAT IN [0..events.length) DO SELECT events[i].event FROM enabled => SELECT events[i].device FROM hookSwitch, speakerSwitch => onHook_FALSE; revertHookswitch => IF ~wasReverted THEN reverted _ wasReverted _ TRUE; ENDCASE; ENDCASE; ENDLOOP; IF which=0 THEN EXIT; ENDLOOP; IF reverted THEN { info.interface.Commands[shh: info.shh, events: unRevCommands]; LOOP; }; EXIT; ENDLOOP; EXITS Failed => onHook_FALSE; }; EvType: TYPE = { tsOn, tsOff, spOn, spOff, spClick, spMon, spNone }; newStates: ARRAY HookState OF ARRAY EvType OF HookState _ [ [ telset, onhook, sPkr, onhook, onhook, monitor, onhook ], -- onhook [ telset, onhook, bOth, telset, telset, monitor, onhook ], -- telset [ telset, spkr, spKr, onhook, onhook, monitor, onhook ], -- spkr [ bOth, sPkr, sPkr, onhook, spkr, bOth, onhook ], -- sPkr [ bOth, sPkr, sPkr, onhook, onhook, bOth, onhook ], -- spKr (sPkr, but click turns off.) [ both, spkr, both, telset, telset, both, onhook ], -- both [ bOth, sPkr, bOth, telset, both, bOth, onhook ], -- bOth [ monitor, onhook, bOth, sPkr, monitor, monitor, onhook ] ]; -- monitor unRevCommands[0] _ [aRelay, enabled]; unRevCommands[1] _ [revertHookswitch, disabled]; Nice.View[pd, "Lark In PD"]; }. ŠLarkInImpl.mesa Last modified by D. Swinehart, December 28, 1983 8:56 pm Actual hardware interface to Lark; includes process to keep tones going and eventually to deal with failure. ***************** External Procedures ******************** Handles smarts-independent filtering, events that need timing. Interpret event, possibly filter some out. newly entered state Filters only events whose device codes are hookswitch and speakerSwitch Implements another state machine via SELECT Translate characters from various devices into common set Determine if either of telset or speakerphone is activated. Assert aRelay and unrevert hookswitch Ê ˜Jšœ™Jšœ8™8Jšœl™lJ˜šÏk ˜ Jšœ˜Jšœœ¤œ˜µJ˜ Jšœœœ˜(J˜Jšœœ˜Jšœœœ˜Jšœœ,˜9Jšœœ˜$šœœ˜Jšœl˜l—Jš œœœ œœ˜>Jšœœ˜J˜J˜—šœ œœœ˜9š˜Jšœœ ˜;—Jšœ˜&—Jšœœ˜J˜Jšœœœœœœœ˜JJšœ œ˜,Jšœ œ˜*J˜2Jšœ œ˜-Jšœ œ˜.Jšœœ˜)Jšœœ ˜2Jšœœ œ˜J˜J˜!Jšœ#˜#J˜!Jšœ%˜%J˜J˜šœœœ˜JšœœœÏc2˜LJ˜—Jš œœœœœ˜J˜Jšœ=™=J˜J˜šÏnœœ+œ˜AJš œœœœœ˜Jšœœœ œ˜Jšœ˜JšœK˜KJ˜#Jšœ˜J˜Jšœ œœœœœœ˜IJ˜ šœœ˜3J˜+Jšœœ˜4Jš œ œœœœ˜KJ™*Jšœœ˜ šœ˜šœ˜J˜%J˜B—Jšœ˜—šœ˜J˜?Jšœ1œ"˜TJš œ œœœžœ˜OJšœ˜—Jšœœœ˜#Jšœ>˜>Jšœ˜ ——J˜šŸ œ œ˜Jšœ œ œœ˜\Jšœ œ˜Jšœ˜Jšœ˜—J˜šŸœœ*˜FJšœ'žœ˜>Jšœ œœ˜8J˜šœ˜J˜'Jšœ œ˜Jšœœ˜—Jšœœœœ˜:J˜0J˜—Jšœœ˜J˜šŸœœœZ˜yJšœEžœ˜\Jšœœœ˜Jšœ œœ"˜>J˜!J˜"šœœ˜Jš œ œœœœœ˜NJš œ œœœœ˜IJšœœ˜—šœ˜šœœž˜%J˜J˜J˜.Jšœœ=˜P—šœ ž˜ šœœœ˜?J˜J˜>——šœ žœ˜!J˜—šœ ž˜Jšœœœ œ ˜I—šœ ž˜ Jšœœœœ ˜G—Jšœ˜—Jšœ)œœ˜PJ˜—Jšœœž˜'Jšœœ ž0˜NJšœœ ž0˜OJ˜HJ˜@J˜BJ˜J˜šŸ œœ;œ˜YJ˜%J˜š Ÿœœœœœž œ˜XJšœœœ˜Jšœœœœ˜[J˜šœœ œž ˜Ešœ˜Jšœœœž˜Jšœ7œœ˜GJšœ+˜+Jšœ%ž˜+Jšœ˜——Jšœ™Jšœ*˜*šœœ˜'Jšœœœ˜-JšœF˜FJšœP˜PJšœA˜AJšœE˜EJšœ˜J˜—Jšœ˜Jšœœ˜—š˜Jšœ œ˜*šœ ˜JšœN˜N—Jšœœœœ˜)—J˜—šŸœœœ˜%šœ.˜.Jšœ)˜0—Jšœ+Ïb œ  ™GJšœ% ™+Jšœœœ˜J˜#šœ œ˜(šœœ˜Jšœœœœ˜MJšœ"œ ˜3—šœœ˜"Jšœ-˜-Jšœ"œ ˜4—Jšœ ˜—J˜J˜(J˜šœ œ˜J™9šœœ˜2šœ œ˜J˜J˜J˜Jšœ ˜—šœ œ˜J˜J˜ Jšœœ˜8Jšœ˜—Jšœ ˜—Jšœ˜šœ˜J˜——Jšœ)˜)šœœœ˜CJšœ œ ˜—Jšœœ˜BJ˜šœœ ˜(J˜Jšœœ œœ˜8Jšœœ œœ˜7Jšœ˜Jšœœ"œœ˜@—šœ˜!Jšœ2œ˜7—Jšœ˜—J˜Jšœ$œ˜FJ˜šŸœœœ˜!Jšœœ œœ˜3J™;Jšœœœœœœœ˜JJšœœœ˜"Jšœœ˜š˜J˜Jšœ œ˜š˜J˜J˜?šœœœ˜#šœ˜šœ œ˜'Jšœ$œ˜*Jšœœœœ˜GJšœ˜—Jšœ˜—Jšœ˜—Jšœ œœ˜Jšœ˜—šœ œ˜J™%Jšœ>˜>Jšœ˜J˜—Jšœ˜Jšœ˜—š˜Jšœœ˜—J˜J˜—Jšœœ8˜DJ˜š œ œ œœœ˜;Jšœ;ž ˜DJšœ=ž ˜FJšœ:ž˜AJšœ6ž˜=JšœY˜YJšœ:ž˜AJšœ8ž˜?Jšœ=ž ˜GJ˜—J˜J˜%J˜0J˜J˜J˜J˜—…—'@4ä