DIRECTORY IO, Commander USING [ CommandProc, Register ], Lark USING [ bStar, bThorp, CommandEvent, CommandEvents, CommandEventSequence, Device, disabled, DTMFEvent, enabled, endNum, Event, Passel, reset, StatusEvent, StatusEvents, SHHH ], LarkOpsRpcControl, LarkSmarts, Nice, RefID USING [ ID, Reseal ], RPC USING [ CallFailed ], Rope USING [ ROPE ], Process USING [ Detach, MsecToTicks, SetTimeout ], ThPartyPrivate USING [ SmartsData ], ThSmartsPrivate USING [ click, EnterLarkSt, --flushMarker, --GetSmartsInfo, --indexMarkerEnd, --LarkInfo, --LarkProseQueue, --LarkState, --maxClientMarker, pResetConfirmEnd, ProseControlDone, proseFailure, ReportProseDone, --RingDetState, SmartsInfo, spkrOn, --stopAndFlushEnd, --SwitchState, TonesDone ], Thrush USING[ --ProseSpec,-- ROPE, SHHH, SmartsID ], ThNet USING [ pd ], VoiceUtils USING [ Report ] ; LarkInImpl: CEDAR MONITOR LOCKS info USING info: LarkInfo IMPORTS LarkOpsRpcControl, Commander, --Convert,-- IO, Nice, Process, RefID, --Rope, --RPC, ThNet, ThSmartsPrivate, VoiceUtils EXPORTS LarkSmarts, ThSmartsPrivate= { OPEN IO; Reseal: PROC[r: REF] RETURNS[RefID.ID] = INLINE {RETURN[RefID.Reseal[r]]; }; LarkInfo: TYPE = ThSmartsPrivate.LarkInfo; RingDetState: TYPE = ThSmartsPrivate.RingDetState; SmartsData: TYPE = ThPartyPrivate.SmartsData; SmartsInfo: TYPE = ThSmartsPrivate.SmartsInfo; SmartsID: TYPE = Thrush.SmartsID; SwitchState: TYPE = ThSmartsPrivate.SwitchState; 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. debounceInterval: INTEGER _ 300, -- ms. spClickInterval: INTEGER _ 300, breakInterval: INTEGER _ 4500, -- ms., 4 sec. nominal, 500 ms. benefit of doubt crowbar: BOOL_FALSE -- TRUE to check Lark command timeout. ]; 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]]]]; VoiceUtils.Report[s.RopeFromROS[], $LarkDetailed, info.larkInfo]; }; RecordEvent: PUBLIC PROC[shh: Thrush.SHHH, smartsID: Thrush.SmartsID, whatHappened: Lark.StatusEvents] RETURNS [ success: BOOL_TRUE ] = { smartsInfo: SmartsInfo = ThSmartsPrivate.GetSmartsInfo[smartsID: smartsID]; parseInfo: SmartsInfo _ smartsInfo; info: LarkInfo; parseSmartsID: SmartsID; IF smartsInfo=NIL OR (info _ smartsInfo.larkInfo)=NIL THEN RETURN[FALSE]; parseSmartsID _ smartsInfo.smartsID; 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 THEN LOOP; SELECT sEvent.device FROM speakerSwitch => sEvent _ InterpretSpeakerSwitch[info, sEvent]; ringDetect => { parseSmartsID _ smartsInfo.otherSmartsID; parseInfo _ ThSmartsPrivate.GetSmartsInfo[smartsID: parseSmartsID]; sEvent _ InterpretRingDetect[info, sEvent]; }; tones => { ThSmartsPrivate.TonesDone[smartsInfo.larkInfo, sEvent]; LOOP; }; 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; }; keyboard => { IF info.keyboardEventHandler#NIL THEN info.keyboardEventHandler[info, sEvent]; LOOP; }; hookSwitch => NULL; ENDCASE => ERROR; IF sEvent.device=nothing THEN LOOP; parseInfo.ParseEvent[ smartsInfo: parseInfo, sEvent: sEvent ]; ENDLOOP; }; EventRope: PUBLIC PROC[ shh: Thrush.SHHH, smartsID: SmartsID, time: CARDINAL, device: Lark.Device, events: ROPE] RETURNS[success: BOOL] = { NULL; }; InterpretSpeakerSwitch: ENTRY PROC[info: LarkInfo, sEvent: Lark.StatusEvent] RETURNS [ processedEvent: Lark.StatusEvent] = { interval: INTEGER = LOOPHOLE[sEvent.time-info.swOnTime]; processedEvent _ sEvent; info.swOnTime _ sEvent.time; SELECT sEvent.event FROM enabled => { TRUSTED { Process.Detach[FORK SpeakerSwitchTimeout[info, info.swOnTime]]; }; processedEvent.device _ nothing; RETURN; }; disabled => IF interval >= 0 AND interval <= pd.spClickInterval THEN processedEvent.event _ ThSmartsPrivate.click; ThSmartsPrivate.spkrOn => RETURN; -- a timed-out ON event, which is now being reported ENDCASE => ERROR; }; SpeakerSwitchTimeout: PROC[info: LarkInfo, onTime: CARDINAL] = { SSTE: ENTRY PROC[info: LarkInfo, onTime: CARDINAL] RETURNS [doIt: BOOL] = TRUSTED { wait: CONDITION; Process.SetTimeout[@wait, Process.MsecToTicks[pd.spClickInterval]]; WAIT wait; RETURN[onTime=info.swOnTime]; }; IF ~SSTE[info, onTime] THEN RETURN; -- click happened or something, forget it. info.larkSmartsInfo.ParseEvent[info.larkSmartsInfo, [onTime+pd.spClickInterval, speakerSwitch, ThSmartsPrivate.spkrOn ] ]; }; InterpretRingDetect: ENTRY PROC[info: LarkInfo, 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; timeout: BOOL _ FALSE; 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 => info.ringDetState _ maybe; between => { info.ringDetState _ ring; processedEvent _ sEvent; }; -- generate new offhook maybe => IF interval info.ringDetState _ between; ENDCASE; info.ringDetInstance _ info.ringDetInstance+1; BROADCAST info.ringDetCondition; -- earlier timeouts zap themselves. IF info.ringDetState = $idle THEN RETURN; TRUSTED { Process.Detach[FORK RingTimeout[info, info.ringDetInstance]]; }; }; RingTimeout: PROC[info: LarkInfo, instance: INT] = { RTEntry: ENTRY PROC[info: LarkInfo] RETURNS[event: Lark.Event_reset] = TRUSTED { -- Process is unsafe ENABLE UNWIND=>NULL; timeout: INTEGER _ pd.breakInterval; IF instance#info.ringDetInstance THEN RETURN; SELECT info.ringDetState FROM $idle => RETURN; $maybe => timeout _ pd.debounceInterval; ENDCASE; Process.SetTimeout[@info.ringDetCondition, Process.MsecToTicks[timeout]]; WAIT info.ringDetCondition; IF info.ringDetInstance#instance THEN RETURN; -- events intervened SELECT info.ringDetState FROM -- timed out $idle => RETURN; -- ?? $maybe => { event _ enabled; info.ringDetState _ $ring; }; -- now ringing ENDCASE => { event _ disabled; info.ringDetState _ $idle; }; -- too long }; event: Lark.Event _ RTEntry[info]; IF event # reset THEN info.larkTrunkSmartsInfo.ParseEvent[ info.larkTrunkSmartsInfo, [info.ringChangeTime, ringDetect, event ] ]; IF event = enabled THEN RingTimeout[info, instance]; -- wait for no-longer-ringing timeout }; InterpretHookState: PUBLIC ENTRY PROC [ info: LarkInfo, rawEvent: Lark.StatusEvent, sInfo: SmartsInfo ] RETURNS [ processedEvent: Lark.StatusEvent ] = { ENABLE UNWIND=>NULL; event: Lark.Event = rawEvent.event; ev: EvType; newState: SwitchState; oldState: SwitchState_info.switchState; ev _ SELECT rawEvent.device FROM hookSwitch => SELECT event FROM Lark.enabled => tsOn, Lark.disabled => tsOff, ENDCASE=> spNone, speakerSwitch => SELECT event FROM ThSmartsPrivate.spkrOn => spOn, Lark.disabled => spOff, ThSmartsPrivate.click => spClick, ENDCASE => spNone, ENDCASE => spNone; processedEvent _ rawEvent; IF ev = spNone THEN { processedEvent.event _ SELECT rawEvent.device FROM touchPad => SELECT event FROM bThorp => endNum, bStar => '*, IN Lark.DTMFEvent => event - (FIRST[Lark.DTMFEvent]-'0), ENDCASE=>event, ENDCASE => event; RETURN; }; newState _ newStates[oldState][ev]; info.switchState _ newState; processedEvent.device _ hookSwitch; IF newState=oldState THEN processedEvent.device_nothing -- no op? ELSE IF newState = $onhook THEN processedEvent.event _ disabled -- really going onhook ELSE { IF oldState = $onhook THEN processedEvent.event _ enabled -- really going offhook ELSE processedEvent.device_nothing; ThSmartsPrivate.EnterLarkSt[info, info.larkState]; }; }; 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; info.interface.Commands[shh: info.shh, events: assertARelay]; }; 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: unRevHS]; LOOP; }; EXIT; ENDLOOP; EXITS Failed => onHook_FALSE; }; EvType: TYPE = { tsOff, tsOn, spOff, spOn, spClick, spNone }; newStates: ARRAY SwitchState OF ARRAY EvType OF SwitchState _ [ [ $onhook, $telset, $onhook, $sPEAKER, $speaker, $onhook ], -- onhook [ $onhook, $telset, $telset, $mONITOR, $monitor, $telset ], -- telset [ $speaker, $telset, $onhook, $sPEAKER, $onhook, $speaker ], -- speaker [ $sPEAKER, $mONITOR, $onhook, $sPEAKER, $onhook, $sPEAKER ], -- sPEAKER [ $speaker, $monitor, $telset, $mONITOR, $telset, $monitor ], -- monitor [ $sPEAKER, $mONITOR, $telset, $mONITOR, $telset, $mONITOR ] -- mONITOR ]; ViewCmd: Commander.CommandProc = TRUSTED { Nice.View[pd, "Lark In PD"]; }; unRevHS: Lark.CommandEvents _ NEW[Lark.CommandEventSequence[1]]; assertARelay: Lark.CommandEvents _ NEW[Lark.CommandEventSequence[1]]; unRevHS[0] _ [revertHookswitch, disabled]; assertARelay[0] _ [aRelay, enabled]; Commander.Register["VuLarkIn", ViewCmd, "Program Management variables for Lark Input"]; }. φLarkInImpl.mesa Copyright c 1985, 1986 by Xerox Corporation. All rights reserved. Last modified by D. Swinehart, November 25, 1986 4:00:06 pm PST Polle Zellweger (PTZ) August 29, 1985 5:46:32 pm PDT Convert USING [ IntFromRope ], Rope USING [ Concat, FromChar, ROPE ], ***************** 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. There should probably be a generalized registration for input events, so that other things could register special input from Lark situations. Right now text-to-speech synthesizers form the only example, and there's only one of them, so the registration is limited to this case. Generates one "enabled" for every ring detect (with a delay for possible glitches the first time), and a single "disabled" when the last ring has been gone too long. See ThSmartsPrivate.LarkInfo. Bold states occur only when event is enabled, others only when disabled ELSE: The debounce timeout will soon occur and generate the offhook event Filters only events whose device codes are hookswitch and speakerSwitch Implements another state machine via SELECT Translate characters from various devices into common set When newState is onhook, the lark state will soon be idle, and this is just a distraction. Determine if either of telset or speakerphone is activated. Assert aRelay and unrevert hookswitch SwitchState: TYPE = { onhook, telset, speaker, sPEAKER, monitor, mONITOR }; spOn is transition away from middle on speaker switch that lasts more than a few hundred ms. spOff is transition to middle when spOn. spClick is transition away from and back to middle after a few hundred ms. Note: spNone is used to indicate that no interesting action took place. See ThSmartsPrivate.SwitchState for the legal state transitions; others are made harmless here. Scenario: user picks up telset and talks for a while, then clicks the speaker switch; the speaker comes on but the telset is still active. Hanging up switches to $speaker mode; picking up the telset reverts to $telset mode, whence hanging up would terminate the conversation unless the user clicks the switch again. Scenario: user picks up telset and talks for a while, then turns on the speaker switch; the speaker comes on but the telset is still active. Hanging up switches to $sPEAKER mode; picking up the telset reverts to $mONITOR mode, and hanging up alternates back to sPEAKER mode, since the switch is still on. When calls are terminated from Finch or the other party, the terminal state goes $onhook anyhow, so the distinctions between switch-on modes and click modes are not as great as they once were. Swinehart, August 6, 1985 12:10:40 pm PDT Incorporate PTZ changes changes to: DIRECTORY, LarkInImpl, RecordEvent, HandleProseOutput, InterpretSpeakerSwitch Polle Zellweger (PTZ) August 7, 1985 6:36:35 pm PDT Comments only. changes to: EvType, newStates Polle Zellweger (PTZ) August 22, 1985 5:20:05 pm PDT Handle Prose flushing; remove deadlock from HandleProseOutput. changes to: DIRECTORY, RecordEvent, HandleProseOutput, HandleAndReport Polle Zellweger (PTZ) August 23, 1985 2:10:02 pm PDT Input events from the Prose Lark are being interpreted as smarts-level parseable events. Raise error for odd devices. changes to: RecordEvent Polle Zellweger (PTZ) August 23, 1985 2:34:28 pm PDT changes to: RecordEvent, HandleProseOutput Polle Zellweger (PTZ) August 27, 1985 8:57:45 pm PDT Allow for Prose Reset command. changes to: DIRECTORY, HandleProseOutput Polle Zellweger (PTZ) August 29, 1985 5:46:33 pm PDT changes to: HandleProseOutput Swinehart, October 28, 1985 11:54:20 am PST Handle => ID, Log => VoiceUtils changes to: DIRECTORY, LarkInImpl, Reseal, SmartsID, RecordEvent, EventRope, HandleProseOutput, InterpretSpeakerSwitch, InterpretRingDetect, RingDetProc, InterpretHookState, ProblemBool Swinehart, November 29, 1985 11:17:56 am PST When going on hook, don't react to changes in terminal type, since the terminal will soon be put idle by other actions. changes to: InterpretHookState Swinehart, April 10, 1986 11:55:03 am PST Simplify and improve speaker and telset switch handling, add monitor mode as first-class citizen changes to: DIRECTORY, Reseal, SmartsID, SwitchState, ROPE, PD, InterpretSpeakerSwitch, SpeakerSwitchTimeout, SSTE (local of SpeakerSwitchTimeout), InterpretHookState, EvType, newStates Swinehart, May 25, 1986 10:10:16 pm PDT Lark => LarkOps changes to: DIRECTORY, LarkInImpl Κ ˜šœ™Icodešœ Οmœ7™BJšœ?™?K™4—J˜šΟk ˜ Jšžœ˜Jšœ žœ˜*Jšœžœ™Jšœžœ€žœ˜΅J˜J˜ J˜Jšœžœžœ ˜Jšžœžœ˜Jšœžœžœ™&Jšœžœžœ˜Jšœžœ%˜2Jšœžœ˜$šœžœ˜Jšœ™˜™—Jšœžœžœžœ ˜4Jšœžœ˜Jšœ žœ ˜J˜J˜—šœ ž œžœžœ˜9šž˜Jšœ+žœžœžœ$˜v—Jšžœ˜&—Jšžœžœ˜J˜JšΟnœžœžœžœžœžœžœ˜LJšœ žœ˜*J˜2Jšœ žœ˜-Jšœ žœ˜.Jšœ žœ˜!Jšœ žœ˜0Jšžœžœ žœ˜J˜J˜!Jšœ#˜#J˜!Jšœ%˜%J˜J˜šžœžœžœ˜JšœžœžœΟc2˜MJšœžœ ˜'Jšœžœ˜Jšœžœ  0˜OJšœ žœžœ &˜:J˜J˜—Jš œžœžœžœžœ˜J˜Jšœ=™=J˜J˜šŸœžœ+žœ˜AJš œžœžœžœžœ˜šœ˜šœžœ˜ Jš žœžœžœžœžœ˜AJšžœžœžœ˜ —Jšœžœžœ žœ˜,—Jšžœžœ˜3Jšžœžœžœ žœ˜BJšœA˜AJšœ˜—J˜šŸ œžœžœž œ=˜fJšžœ ž œ˜J™>Jšœ˜JšœK˜KJšœ#˜#Jšœ˜J˜Jšžœ žœžœžœžœžœžœ˜IJ˜$šžœžœž˜3J˜+Jšžœžœ˜4Jšžœ žœžœžœ˜.J™*šžœž˜J˜?šœ˜J˜)JšœC˜CJšœ+˜+J˜—šœ ˜ Jšœ7˜7Jšžœ˜J˜—šœ ˜ J™OJšžœžœ+˜Hšžœ˜Jšœ,žœ  ˜RJ˜—Jšžœžœžœ˜ J˜—šœ ˜ KšΟtœ–‘™˜Kšžœžœžœ)˜NKšžœ˜K˜—Jšœžœ˜Jšžœžœ˜—Jšžœžœžœ˜#Jšœ>˜>Jšžœ˜ ——J˜šŸ œž œ˜Jšœ žœžœžœ˜XJšžœ žœ˜Jšžœ˜Jšœ˜—J˜šŸœžœžœ*˜LJšžœ(˜/Jšœ žœžœ˜8J˜J˜šžœž˜˜ Jšžœžœ/˜LJ˜ Jšžœ˜J˜—šœ žœžœ ž˜DJšœ-˜-—Jšœžœ 4˜VJšžœžœ˜—J˜J˜—šŸœžœž œ˜@šžœžœžœžœžœžœžœ˜SJšœž œ˜JšœC˜CJšžœ˜ Jšžœ˜J˜—Jš žœžœžœžœ *˜Nšœ3˜3JšœF˜F—J˜J˜J˜—šŸœžœžœ*˜IJšžœF˜MJ™ΔJšžœžœžœ˜Jšœ žœžœ"˜>J˜!Jšœ žœžœ˜J˜"šžœžœ˜Jš œ žœžœžœžœžœ˜NJš œ žœžœžœžœ˜IJšžœžœ˜—šžœž˜JšœG™GJšΟbœ˜"Jš’œ< ˜Zš œ žœžœžœžœ˜QJšžœE™I—Jšœ$˜$Jšžœ˜—J˜.Jšž œ;˜DJšžœžœžœ˜)Jšžœžœ-˜JJšœ˜—J˜J˜šŸ œžœžœ˜4š Ÿœžœžœžœžœ ˜eJšžœžœžœ˜Jšœ žœ˜$Jšžœžœžœ˜-šžœž˜Jšœ žœ˜Jšœ(˜(Jšžœ˜—JšœI˜IJšžœ˜Jšžœžœžœ ˜Bšžœžœ  ˜*Jšœ žœ ˜Jšœ; ˜IJšžœ6  ˜H—J˜—Jšœ"˜"šžœž˜šœ$˜$JšœF˜F——Jšžœžœ %˜ZJšœ˜J˜—šŸœžœžœž˜%šœA˜AJšžœ)˜0—Jšœ+’ œ’ ™GJšœ%’™+Jšžœžœžœ˜J˜#J˜ J˜J˜'šœžœž˜ šœžœž˜Jšœ˜Jšœ˜Jšžœ ˜—šœžœž˜"Jšœ˜Jšœ˜Jšœ!˜!Jšžœ ˜—Jšžœ ˜—J˜šžœ žœ˜J™9šœžœž˜2šœ žœž˜J˜J˜ Jšžœžœ˜8Jšžœ˜—Jšžœ ˜—Jšžœ˜šœ˜J˜——Jšœ#˜#Jšœ˜J˜#Jšžœžœ  ˜AJšžœžœžœ! ˜VJ™Zšžœž˜šžœžœ  ˜QJšžœ˜#—Jšœ2˜2J˜—Jšœ˜—J˜J˜šŸœžœžœž˜!Jšœžœ žœžœ˜3J™;Jšžœžœžœžœžœžœžœ˜JJšœžœžœ˜"Jšœžœ˜šž˜J˜Jšœ žœ˜šž˜J˜J˜?šžœžœžœž˜#šžœž˜šœ žœž˜'šœ˜Jšœžœ˜ Jšœ=˜=J˜—Jšœžœžœžœ˜GJšžœ˜—Jšžœ˜—Jšžœ˜—Jšžœ žœžœ˜Jšžœ˜—šžœ žœ˜J™%Jšœ8˜8Jšžœ˜J˜—Jšžœ˜Jšžœ˜—šž˜Jšœžœ˜—J˜J˜—Jšœ žœ:™KJ™šœžœ1˜=J™\J™(J™JJ™G—J™š Οf Πfk£ €£€£€£˜?J™_Jš£@Πcf ˜IJš£@₯ ˜IJš£@₯ ˜JJš£@₯ ˜JJš£@₯ ˜JJš£@₯ ˜JJ˜Jš Ό™ΌJš ¦œ )œ )œ $™±J™ΐ—J˜šœ!žœ˜*J˜Jšœ˜J˜—Jšœžœ˜@Jšœ#žœ˜EJšœ*˜*Jšœ$˜$J˜JšœW˜WJ˜™)K™Kšœ ΟrM™Y—™3K™Kšœ ¦™—™4Kšœ>™>Kšœ ¦:™F—™4K™vKšœ ¦ ™—™4Kšœ ¦™*—™4K™Kšœ ¦™(—™4Kšœ ¦™—™+K™Kšœ ¦­™Ή—™,K™wKšœ ¦™—™)K™`Kšœ ¦fœ ¦'™Ή—™'K™Kšœ ¦™!—K™—…—'ΪFβ