DIRECTORY IO, Commander USING [ CommandProc, Register ], 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, TonesDone ], Thrush USING[ H, pERROR, ROPE, SHHH, SmartsHandle, ThHandle ], ThNet USING [ pd ] ; LarkInImpl: CEDAR MONITOR LOCKS info USING info: LarkInfo IMPORTS Commander, 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, %3B, ", card[LONG[ IF info#NIL THEN LOOPHOLE[info.larkInfo.netAddress.host,CARDINAL] ELSE LOOPHOLE[777B, CARDINAL]]], 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 => { parseSmarts _ smartsInfo.otherSmarts; parseInfo _ ThSmartsPrivate.GetSmartsInfo[smarts: parseSmarts]; }; tones => SELECT sEvent.event FROM 'F=> -- Feeping complete { parseSmarts _ smartsInfo.otherSmarts; parseInfo _ ThSmartsPrivate.GetSmartsInfo[smarts: parseSmarts]; }; ENDCASE; -- Other tones finished ENDCASE; SELECT sEvent.device FROM speakerSwitch => sEvent _ InterpretSpeakerSwitch[info, sEvent]; ringDetect => sEvent _ InterpretRingDetect[info, H[parseSmarts], parseInfo, sEvent]; touchPad => { IF sEvent.event=disabled THEN sEvent.event _ smartsInfo.lastTouchpadChar ELSE { smartsInfo.lastTouchpadChar _ sEvent.event; LOOP; -- down transitions of DTMF pad. }; IF sEvent.event='\000 THEN LOOP; }; 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; newState: HookState; oldType: TerminalType_info.terminalType; processedEvent.device _ nothing; IF rawEvent.device = tones THEN { ThSmartsPrivate.TonesDone[info, rawEvent, NIL]; RETURN; }; ev _ 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; 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 => 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 ViewCmd: Commander.CommandProc = TRUSTED { Nice.View[pd, "Lark In PD"]; }; unRevCommands[0] _ [aRelay, enabled]; unRevCommands[1] _ [revertHookswitch, disabled]; Commander.Register["VuLarkIn", ViewCmd, "Program Management variables for Lark Input"]; }. âLarkInImpl.mesa Last modified by D. Swinehart, November 26, 1984 10:49:21 am PST 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. React to upstrokes, not downstrokes. 0-key Rollover is a side-effect. Try it. 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šœ@™@Jšœl™lJ˜šÏk ˜ Jšœ˜Jšœ œ˜*Jšœœ¤œ˜µJ˜ Jšœœœ˜(J˜Jšœœ˜Jšœœœ˜Jšœœ,˜9Jšœœ˜$šœœ˜Jšœw˜w—Jš œœœ œœ˜>Jšœœ˜J˜J˜—šœ œœœ˜9š˜Jšœ œœ ˜F—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š œœœœœ˜AJšœœœ˜ —Jšœœœ œ˜,—Jšœœ˜3Jšœœœ œ˜BJšœ:˜:Jšœ˜—J˜Jšœ œœž1˜FšŸ œœœ œA˜jJšœ œ˜J™>Jšœ˜JšœK˜KJ˜#Jšœ˜J˜Jšœ œœœœœœ˜IJ˜ šœœ˜3J˜+Jšœœ˜4Jš œ œœœœ˜KJ™*Jšœœ˜ šœ˜šœ˜J˜%J˜B—šœ œ˜!šœž˜Jšœ˜J˜%J˜?J˜—Jšœž˜ —Jšœ˜—šœ˜J˜?Jšœ1œ"˜Tšœ ˜ J™OJšœœ+˜Hšœ˜Jšœ,œž ˜RJ˜—Jšœœœ˜ J˜—Jšœ˜—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˜ J˜J˜(J˜ šœœ˜!Jšœ*œ˜/Jšœ˜J˜—šœœ˜ šœœ˜Jšœœœœ˜MJšœ"œ ˜3—šœœ˜"Jšœ-˜-Jšœ"œ ˜4—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šœ˜J˜—J˜%J˜0J˜JšœW˜WJ˜J˜—…—*69'