DIRECTORY Atom USING [ DottedPairNode ], BasicTime USING [ GMT, Now, Pack, Period, Unpack, Unpacked ], IO, Lark, LarkFeepRpcControl, LarkSmartsMonitorImpl, MBQueue USING [ QueueClientAction ], NameDB USING [ SetAttribute, SetAttributeTimed ], RefID USING [ ID, Reseal, Unseal ], Rope USING [ Concat, Equal, Fetch, FromChar, Length ], SafeStorage USING [ NarrowRefFault ], ThParty USING [ Alert, CreateConversation, GetPartyFromNumber, GetPartyFromFeepNum, GetPartyInfo, LookupServiceInterface, PartyInfo ], Thrush USING [ ConversationID, ConvEvent, InterfaceSpec, PartyID, NB, none, nullConvID, nullID, ROPE, StateInConv ], ThSmartsPrivate USING [ ChangeState, ConvDesc, DBInfo, EnterLarkState, ForgetConv, GetConvDesc, InputEventSpec, GetConv, HardwareRequired, LarkInfo, NoteNewState, OpenConversations, ParseState, SmartsInfo ], VoiceUtils USING [ MakeAtom, Problem ] ; LarkSmartsImpl: CEDAR MONITOR LOCKS root IMPORTS BasicTime, IO, MBQueue, LarkFeepRpcControl, root: LarkSmartsMonitorImpl, NameDB, RefID, Rope, SafeStorage, ThParty, ThSmartsPrivate, VoiceUtils EXPORTS ThSmartsPrivate SHARES LarkSmartsMonitorImpl = { OPEN IO; CommandEvents: TYPE = Lark.CommandEvents; ConversationID: TYPE = Thrush.ConversationID; nullConvID: ConversationID=Thrush.nullConvID; ConvDesc: TYPE = ThSmartsPrivate.ConvDesc; disabled: Lark.Event = Lark.disabled; enabled: Lark.Event = Lark.enabled; endNum: Lark.Event = Lark.endNum; Reseal: PROC[r: REF] RETURNS[RefID.ID] = INLINE {RETURN[RefID.Reseal[r]]; }; LarkInfo: TYPE = ThSmartsPrivate.LarkInfo; NB: TYPE = Thrush.NB; nullID: RefID.ID = Thrush.nullID; OpenConversations: TYPE = ThSmartsPrivate.OpenConversations; PartyID: TYPE = Thrush.PartyID; ROPE: TYPE = Thrush.ROPE; SHHH: TYPE = Lark.SHHH; -- Encrypts conv. if first arg to RPC PROC SmartsInfo: TYPE = ThSmartsPrivate.SmartsInfo; StateInConv: TYPE = Thrush.StateInConv; StatusEvents: TYPE = Lark.StatusEvents; LarkParseEvent: PUBLIC ENTRY PROC[r: REF] = { -- r is the InputEventSpec ENABLE UNWIND=>NULL; eventSpec: ThSmartsPrivate.InputEventSpec = NARROW[r]; smartsInfo: SmartsInfo = eventSpec.smartsInfo; sEvent: Lark.StatusEvent _ eventSpec.sEvent; IF smartsInfo=NIL OR smartsInfo.failed THEN RETURN; -- Events directed at dead smarts SELECT sEvent.device FROM nothing => RETURN; speakerSwitch, touchPad, hookSwitch => NULL; ENDCASE => ERROR; DoParse[smartsInfo, sEvent.event]; -- Interpret this event in light of past ones. IF smartsInfo.haveArguments THEN { -- now are ready to execute an action routine val: INT_0; IF smartsInfo.Command=NIL THEN smartsInfo.Command _ CmdCall; -- was a phone number SELECT smartsInfo.parseState FROM inNum => val _ IO.GetInt[IO.RIS[smartsInfo.arguments]! IO.Error => SELECT ec FROM Failure, SyntaxError, Overflow => CONTINUE; ENDCASE]; ENDCASE; smartsInfo.Command[smartsInfo, val]; -- Execute the action routine []_SetParserIdle[smartsInfo, TRUE, TRUE]; }; }; DoParse: INTERNAL PROC[info: SmartsInfo, event: Lark.Event] = { IF info.haveOne=FALSE AND event#disabled AND GetSIC[info] = $reserved THEN IF ThSmartsPrivate.ChangeState[GetConvDesc[info], $parsing] # $success THEN { []_SetParserIdle[info, TRUE, FALSE]; RETURN; }; -- just get out! info.haveOne _ TRUE; SELECT event FROM disabled => { info.haveArguments _ TRUE; info.Command _ CmdOnhook; RETURN;}; enabled => { info.haveArguments _ TRUE; info.Command _ CmdOffhook; RETURN;}; endNum => { info.haveArguments _ TRUE; RETURN; }; -- '# IO.DEL, IO.ESC => { []_SetParserIdle[info, TRUE, TRUE]; RETURN; }; '* => { IF info.cmdOrRecip THEN info.offset_10; info.cmdOrRecip _ TRUE; info.parseState _ idle; RETURN; }; ENDCASE => NULL; -- continues below IF info.cmdOrRecip THEN { -- begins command or feepName recipient specification info.cmdOrRecip _ FALSE; event_event+info.offset; info.offset_0; IF event = '0 THEN { -- feepName recipient specification []_SetParserIdle[info, FALSE, FALSE]; info.parseState _ getFeep; RETURN; }; []_SetParserIdle[info, TRUE, FALSE]; SELECT event FROM '*, -- esc endNum -- del -- => []_SetParserIdle[info, TRUE, TRUE]; IN ['1..'9] => { []_SetParserIdle[info, FALSE, FALSE]; info.parseState _ getFeep; GOTO Continue; }; '0+10, 'T, 't => { info.Command _ CmdBackDoor; info.haveArguments _ TRUE; }; '1+10 => { info.Command _ CmdRingOff; info.haveArguments _ TRUE; }; '2+10 => { info.Command _ CmdRingOn; info.haveArguments _ TRUE; }; '3+10 => { info.Command _ CmdRingOffTimed; info.parseState _ getNum; }; '4+10 => { info.Command _ CmdRingOnce; info.haveArguments _ TRUE; }; '5+10 => { info.Command_CmdRingOnceTimed; info.parseState _ getNum; }; '7+10 => { info.Command_ CmdAudioSource; info.haveArguments _ TRUE; info.arguments _ "telset"; }; '8+10 => { info.Command_ CmdAudioSource; info.haveArguments _ TRUE; info.arguments _ "lineA"; }; '9+10 => { info.Command_ CmdAudioSource; info.haveArguments _ TRUE; info.arguments _ "lineB"; }; ENDCASE => -- unassigned same as DEL -- NULL; RETURN; EXITS Continue => NULL; }; info.arguments _ Rope.Concat[info.arguments, Rope.FromChar[event]]; info.parseState _ SELECT event FROM IN ['0..'9] => SELECT info.parseState FROM idle => inSeq, getFeep => inFeep, getNum => inNum, getStr => inStr, ENDCASE => info.parseState, ENDCASE => SELECT info.parseState FROM idle, getStr => inStr, getFeep, getNum, inFeep, inNum => inTossStr, inSeq => SetParserIdle[info, TRUE], ENDCASE => info.parseState; IF info.parseState = inSeq THEN { e1: Lark.Event = info.arguments.Fetch[0]; SELECT info.arguments.Length[] FROM 1 => SELECT event FROM '0 => info.haveArguments_TRUE; -- PARC Operator '4, '5, '6, '7, '8, '9 => NULL; -- seq-type number continues ENDCASE => info.parseState _ inNum; 2 => IF info.arguments.Equal["90"] THEN info.parseState _ inNum; 3 => IF info.arguments.Equal["911"] THEN info.haveArguments_TRUE; 4 => SELECT e1 FROM '9 => IF info.arguments.Equal["9911"] THEN info.haveArguments _ TRUE; '4, '5, '6 => info.haveArguments _ TRUE; -- local extensions ENDCASE; 5 => SELECT e1 FROM '7 => info.haveArguments _ TRUE; -- Xerox Sunnyvale extensions ENDCASE; 8 => IF info.arguments.Fetch[2] > '1 THEN info.haveArguments _ TRUE; 11 => info.haveArguments _ TRUE; -- DDD call, via ATT or Intelnet ENDCASE; -- keep on going }; }; SetParserIdle: INTERNAL PROC[info: SmartsInfo, clearCmd: BOOL, reallyIdle: BOOL_TRUE] RETURNS [ ThSmartsPrivate.ParseState ] = { info.haveArguments _ info.haveOne _ FALSE; info.parseState _ idle; info.arguments _ NIL; info.offset _ 0; info.cmdOrRecip _ FALSE; IF clearCmd THEN info.Command _ NIL; IF reallyIdle THEN SELECT GetSIC[info] FROM $parsing => IF ThSmartsPrivate.ChangeState[GetConvDesc[info], $reserved] # $success THEN info.Command _ NIL; -- Massive failure, control damage. ENDCASE; RETURN[idle]; }; CmdCall: INTERNAL PROC[info: SmartsInfo, val: INT] = { cDesc: ConvDesc; { OPEN now: cDesc.situation.self; nb: NB; partyID: PartyID; otherPartyID: PartyID; IF info.arguments=NIL OR info.parseState=$inTossStr THEN RETURN; SELECT GetSIC[info] FROM $reserved, $parsing => NULL; $active => RETURN; -- ignore further DTMF signalling activity after call is active ENDCASE => { VoiceUtils.Problem["LarkSmarts placing call from wrong state"]; RETURN; }; cDesc _ GetConvDesc[info]; IF cDesc=NIL THEN RETURN; -- Error already reported by GetConvDesc. partyID _ now.partyID; [nb, otherPartyID] _ GetPartiesForCall[info, partyID, info.parseState, info.arguments]; SELECT nb FROM $success => NULL; $noSuchParty2 => { [] _ ThSmartsPrivate.ChangeState[cDesc, $failed, $notFound]; RETURN; }; $voiceTerminalBusy => -- front/back door thing { [] _ ThSmartsPrivate.ChangeState[cDesc, $failed, $busy, "Only available trunk line is in use"]; RETURN; }; -- Unlikely to happen. ENDCASE => RETURN; -- Problems already reported, nothing more can be done. IF ThSmartsPrivate.ChangeState[cDesc, $initiating] = $success THEN { comment: ROPE _ NIL; SELECT (nb_ThParty.Alert[credentials: now, calledPartyID: otherPartyID]) FROM $success => RETURN; $narcissism => comment _ "Attempt to call self rejected on philosophical grounds"; $noSuchParty2, $alreadyInConv => comment _ "Called party cannot be contacted"; $noSuchParty, $noSuchSmarts, $noSuchConv, $notInConv, $convIdle, $stateMismatch, $interfaceError => { VoiceUtils.Problem["Serious Party error detected by LarkSmarts"]; ThSmartsPrivate.ForgetConv[cDesc]; RETURN; }; ENDCASE => ERROR; -- Unexpected [] _ ThSmartsPrivate.ChangeState[cDesc, $failed, $busy, comment]; }; }; }; CmdBackDoor: INTERNAL PROC[info: SmartsInfo, val: INT] = { cDesc: ConvDesc _ GetConvDesc[info]; IF cDesc#NIL AND cDesc.situation.self.state=$active THEN { pInfo: ThParty.PartyInfo; interfaceSpec: Thrush.InterfaceSpec; interface: LarkFeepRpcControl.InterfaceRecord_NIL; nb: NB; [nb, pInfo] _ ThParty.GetPartyInfo[credentials: cDesc.situation.self, nameReq: $none, allParties: TRUE]; IF nb # $success OR pInfo[0].partyID=0 THEN { VoiceUtils.Problem["No conversation info, or incomplete"]; RETURN; }; -- This is really bad! IF pInfo.conversationInfo.numActive#2 OR ~pInfo.conversationInfo.bilateralConv THEN RETURN; [nb, interfaceSpec] _ ThParty.LookupServiceInterface[ credentials: cDesc.situation.self, serviceParty: pInfo[1].partyID, type: "LarkFeep"]; IF nb#$success THEN RETURN; interface _ NARROW[RefID.Unseal[interfaceSpec.interfaceID]! SafeStorage.NarrowRefFault => CONTINUE]; -- Insist on a local implementation. IF interface=NIL THEN RETURN; -- flashing has no meaning in this conversation. IF interface.Flash[shhh: Thrush.none, serviceID: interfaceSpec.serviceID, convID: cDesc.situation.self.convID, requestingParty: cDesc.situation.self.partyID, actionID: 0, extraTime: 177777B] # $success THEN VoiceUtils.Problem["Switchhook flash failed"]; RETURN; }; info.arguments _ ""; -- non-nil, but no value either. info.parseState _ $inSeq; CmdCall[info, val]; }; CmdOnhook: INTERNAL PROC[info: SmartsInfo, val: INT] = { cDesc: ConvDesc; SELECT GetSIC[info] FROM $idle, $notified, $ringing, $neverWas => RETURN; -- could still be offhook from last call. ENDCASE => NULL; IF (cDesc _ GetConvDesc[info])#NIL THEN [] _ ThSmartsPrivate.ChangeState[cDesc, $idle, $terminating]; }; CmdOffhook: INTERNAL PROC[info: SmartsInfo, val: INT] = { desiredState: StateInConv _ $active; nb: NB; cDesc: ConvDesc; SELECT GetSIC[info] FROM $idle => { convEvent: Thrush.ConvEvent; cDesc _ ThSmartsPrivate.GetConv[info, [], TRUE]; [nb, convEvent] _ ThParty.CreateConversation[ credentials: cDesc.situation.self, state: $reserved, checkConflict: TRUE ]; SELECT nb FROM $success => NULL; $noSuchSmarts, $noSuchParty => { VoiceUtils.Problem["Serious party error noted by LarkSmarts"]; RETURN; }; $voiceTerminalBusy => { VoiceUtils.Problem["Lost an off-hook race?"]; RETURN; }; ENDCASE => ERROR; -- Unexpected ThSmartsPrivate.NoteNewState[cDesc, convEvent]; }; $ringing => { cDesc _ GetConvDesc[info]; IF cDesc=NIL THEN RETURN; -- Error has been reported [] _ ThSmartsPrivate.ChangeState[cDesc, $active]; }; ENDCASE }; CmdRingOff: INTERNAL PROC[info: SmartsInfo, val: INT] = { DoRingSpec[info, "O", $timedringmode]}; CmdRingOn: INTERNAL PROC[info: SmartsInfo, val: INT] = { DoRingSpec[info,"R", $ringmode]}; CmdRingOffTimed: INTERNAL PROC[info: SmartsInfo, val: INT] = { DoRingSpec[info,"O",$timedringmode, val]}; CmdRingOnce: INTERNAL PROC[info: SmartsInfo, val: INT] = { DoRingSpec[info,"S", $ringmode]; }; CmdRingOnceTimed: INTERNAL PROC[info: SmartsInfo, val: INT] ={ DoRingSpec[info,"S",$timedringmode, val]}; RingSpec: TYPE = REF RingObj; RingObj: TYPE = RECORD [ info: SmartsInfo, ringMode: ROPE, ringModeAttribute: ATOM, timeInMinutes: INT ]; DoRingSpec: INTERNAL PROC[info: SmartsInfo, ringMode: ROPE, ringModeAttribute: ATOM, timeInMinutes: INT_1441-- default one day and one second--] = { IF timeInMinutes=0 THEN timeInMinutes _ 30; -- default one day and one second info.requests.QueueClientAction[QdRing, NEW[RingObj _ [info, ringMode, ringModeAttribute, timeInMinutes]]]; }; QdRing: ENTRY PROC[r: REF] = { ringSpec: RingSpec _ NARROW[r]; partyID: PartyID; rName: ROPE; info: SmartsInfo = ringSpec.info; cDesc: ConvDesc = ThSmartsPrivate.GetConvDesc[info]; interval: INT _ ringSpec.timeInMinutes*60; IF cDesc=NIL THEN RETURN; -- should make error signal, but that's too hard. rName _ ThSmartsPrivate.DBInfo[partyID _ cDesc.situation.self.partyID].rName; IF rName=NIL THEN RETURN; -- should make error signals SELECT ringSpec.ringModeAttribute FROM $ringmode => NameDB.SetAttribute[rName, $ringmode, ringSpec.ringMode]; $timedringmode => { now: BasicTime.GMT; unp: BasicTime.Unpacked; unp _ BasicTime.Unpack[now _ BasicTime.Now[]]; unp.hour _ 23; unp.minute _ 59; unp.second _ 59; -- midnight interval _ MIN[interval, BasicTime.Period[from: now, to: BasicTime.Pack[unp]]+1]; NameDB.SetAttributeTimed[ rName: rName, attribute: $ringmode, value: ringSpec.ringMode, interval: interval]; }; ENDCASE => ERROR; }; CmdAudioSource: INTERNAL PROC[info: SmartsInfo, val: INT] = { IF info.arguments.Length[]=0 THEN info.arguments_"telset"; IF info.larkInfo=NIL THEN RETURN; ThSmartsPrivate.EnterLarkState[info.larkInfo, info.larkInfo.larkState, LIST[NEW[Atom.DottedPairNode_[$audioSource, VoiceUtils.MakeAtom[info.arguments]]]]]; }; GetConvDesc: PUBLIC INTERNAL PROC[info: SmartsInfo] RETURNS [ cDesc: ConvDesc_NIL ] = { FOR convs: OpenConversations _ info.conversations, convs.rest WHILE convs#NIL DO IF ThSmartsPrivate.HardwareRequired[convs.first] THEN { IF cDesc=NIL THEN cDesc _ convs.first ELSE VoiceUtils.Problem[ "Two conversations involving same smarts have Lark hardware reserved"]; }; ENDLOOP; }; GetSIC: PUBLIC INTERNAL PROC[info: SmartsInfo] RETURNS [ state: StateInConv ] = { cDesc: ConvDesc = GetConvDesc[info]; state _ IF cDesc=NIL THEN $idle ELSE cDesc.situation.self.state; }; GetPartiesForCall: PROC[ info: SmartsInfo, partyID: PartyID, parseState: ThSmartsPrivate.ParseState, args: ROPE] RETURNS[nb: NB_ $noSuchParty, calledPartyID: PartyID _ nullID ] = { IF partyID = nullID THEN RETURN; SELECT parseState FROM inSeq, inNum => [nb, calledPartyID] _ ThParty.GetPartyFromNumber[ partyID: partyID, phoneNumber: args]; inFeep => [nb, calledPartyID] _ ThParty.GetPartyFromFeepNum[partyID: partyID, feepNum: args]; ENDCASE => VoiceUtils.Problem["Don't know how to look for called party", $Smarts, info]; SELECT nb FROM $success, $voiceTerminalBusy, $noSuchParty2 => RETURN; ENDCASE; VoiceUtils.Problem["Serious Party problem detected at LarkSmarts"]; }; }. FLarkSmartsImpl.mesa Copyright Σ 1985, 1986, 1987 by Xerox Corporation. All rights reserved. Last modified by D. Swinehart, June 11, 1987 1:55:02 pm PDT Last Edited by: Pier, May 3, 1984 3:00:22 pm PDT Polle Zellweger (PTZ) August 23, 1985 2:21:19 pm PDT Copies Parsing ParseEvent handles events from the EtherPhone, by queuing them to synchronize with other activities. In the name of unexcess generality, this procedure parses all of the commands: call placement, message handling, call management, and so on. Could change to call sequential registered procedures and like that, if more flexibility is needed. Hairy touchpad user interfaces aren't a major goal of this work. See Log notes, November 29, 1985 11:18:59 am PST On/Offhook Termination of arguments, DEL Command or recipient initiation Valid argument event Here are the commands Continue to collect event as first character of feep name! **0 A letter, digit, or other non-activation character. Sequence of digits whose length is determined by the values of the first character or two Require explicit termination of calls beginning with unrecognized prefixes Require explicit termination of international calls, calling card calls, calls to Telco Operator Local or Intelnet User-invoked actions User has specified feep name or number; Consider removing some of these if other reports always preceed them. Might have to do ForgetConversation and/or ThSmarts.Fail. See what Advance does with these things, first. To work properly, this would have to be a communication to the "Feep" interface of the other party. Check out how LarkSmarts.Feep works. Fix Feep in the forwarded case. ThSmartsPrivate.ForgetConv[cDesc]; -- see above about possible Fail, Forget actions. ThSmartsPrivate.ForgetConv[cDesc]; -- I claim you need this line!! Data base changes -- ringing specifications and so on. Queue it to release user interface Set timed or standard ring mode attribute If timed, set time to minimum of now+(ringSpec.time)*60 and midnight in minutes Connection Management Utilities Request is to obtain the conversation descriptor for the most relevant current conversation. We define that to be the cDesc for which we have the Lark hardware reserved, if any, else NIL. Assertion: there is never more than one such cDesc; check it here, since that's convenient. inStr => complain  see below; GetPartiesForCall.nb~$noSuchParty GetPartiesForCall.GetPartyFromNumber/FeepNum.nb~~$noSuchParty GetPartiesForCall.GetRelatedParty.nb~~$voiceTerminalBusy GetPartiesForCall.GetPartyFromNumber/FeepNum.nb~~$noSuchParty2 GetPartiesForCall.GetRelatedParty.nb~~$convStillActive GetPartiesForCall.GetIdleParty/GetRnameFromParty.nb~~$noNameAvailable GetPartyFromFeepNum.GetActiveParty.nb~~$noIdentSupplied Swinehart, May 22, 1985 12:14:29 pm PDT hotLine => autoAnswer changes to: DoParse, CmdHotline, } Polle Zellweger (PTZ) August 23, 1985 2:21:19 pm PDT Consistency checking on devices. changes to: LarkParseEvent, DIRECTORY Swinehart, October 28, 1985 9:51:23 am PST Handle => ID, Log => VoiceUtils, pull ProblemHandle, ... changes to: DIRECTORY, LarkSmartsImpl, ConversationID, Reseal, nullID, PartyID, LarkParseEvent, CmdCall, CmdOffhook, CmdRingDoProc, GetCDesc, GetPartiesForCall, ProblemID Swinehart, November 29, 1985 11:19:02 am PST When event is disabled (hanging up), don't transition into $parsing because there will soon be a transition into $idle changes to: DoParse Swinehart, December 29, 1986 3:37:31 pm PST Remove obsolete $voiceTerminalBusy nb test changes to: CmdCall, GetPartiesForCall, CmdOffhook Swinehart, January 1, 1987 9:49:59 pm PST Still working on FD/BD conflict code. changes to: CmdCall, CmdAudioSource Swinehart, January 28, 1987 8:13:00 am PST A pass for error management, esp. nb-processing changes to: StatusEvents, LarkParseEvent, DoParse, SetParserIdle, CmdCall, CmdBackDoor, CmdOnhook, CmdOffhook, GetPartiesForCall Swinehart, April 6, 1987 8:14:37 am PDT Cedar 7.0; accommodate NamesGV => NameDB changes to: QdRing, DIRECTORY, LarkSmartsImpl Swinehart, May 29, 1987 2:21:52 pm PDT EngageParty (checkConflict) stuff incorporated changes to: CmdOffhook ΚΘ˜šœ™Icode™HJšœ;™;J™0K™4J™—šΟk œ˜ Jšœœ˜Jšœ œœ(˜=Jšœ˜Jšœ˜J˜J˜Jšœœ˜$Jšœœ%˜1Jšœœœ˜#Jšœœ,˜6Jšœ œ˜%Jšœœy˜†šœœ˜Jšœ3œœ˜e—šœœ˜Jšœ·˜·—Jšœ œ˜&J˜J˜—šœœœœ˜(š˜Jšœ ˜ Jšœ˜J˜J˜J˜Jšœ˜J˜J˜J˜ J˜J˜J˜ —Jšœ˜Jšœ˜ Jšœœ˜J˜—šœ™J˜Jšœœ˜)šœœ˜-Jšœ-˜-—Jšœ œ˜*Jšœ%˜%Jšœ#˜#Jšœ!˜!JšΟnœœœœœœœ˜LJšœ œ˜*Jšœœ œ˜J˜!Jšœœ%˜œ˜aJšœ>œ˜`Jšœ>œ˜`JšœŸœœ˜-—Jšœ˜Jšœ œ˜Jšœ˜J˜—J™3JšœC˜Cšœœ˜#šœ œ˜*J˜J˜J˜J˜Jšœ˜—šœœ˜&J˜J˜,Jšœœ˜#Jšœ˜——šœœ˜!JšœY™YJšœ)˜)šœ˜#šœœ ˜JšœœŸ˜/JšœœŸ˜<šœ˜#JšŸJ™J——šœœœ˜@Jšœ`™`—Jšœœœœ˜Ašœœ˜Jšœœœœ˜EJšœ#œŸ˜Jšœ˜—šœœœœ˜DJšœ™—JšœœŸ ˜AJšœŸ˜—J˜—J˜J˜—š ž œœœœœœ˜UJšœ#˜*Jšœ$œ˜*Jšœ)œ˜-Jšœ#œ˜)Jšœ œœ˜$šœ œœ˜+šœ œE˜TJšœœŸ#˜<—Jšœ˜—Jšœ˜ Jšœ˜J˜——™J˜šžœœœœ˜6J™(Jšœ˜JšΟb˜Jšœ˜Jšœœ˜J˜J˜Jš œœœœœ˜@šœ˜Jšœœ˜Jšœ œŸ?˜Ršœ˜ Jšœ@œ˜J——Jšœ˜Jš œœœœŸ)˜CJ˜JšœW˜Wšœ˜Jšœ œ˜˜Jšœ?œ˜I—šœ/˜/JšœbœŸ˜ƒ—JšœœŸ7˜J—šœ<œ˜DJšœ œœ˜šœC˜MJšœ œ˜JšœR˜R˜ J˜-—šœe˜eJšœA˜AJšœ#œ˜*Jšœ˜JšΟtœE™FJšœj‘™k—JšœœŸ ˜—JšœA˜AJ˜—Jš œ˜—J˜šž œœœœ˜:Jš‘œ‰‘"™¬J˜$šœœœ$œ˜:J˜J˜$Jšœ.œ˜2Jšœœ˜šœ ˜ JšœTœ˜Z—šœœœ˜-Jšœ;œŸ˜\—šœ$œ&˜NJšœœ˜ —˜˜J˜U——Jšœ œœ˜šœ œ)˜;JšœœŸ$˜M—Jš œ œœœŸ0˜NšœG˜IJšœS˜Sšœ,˜0Jšœ.˜.——Jšœ˜ —JšœŸ ˜5J˜J˜J˜J˜—šž œ œœ˜8J˜šœ˜Jšœ)œŸ)˜ZJšœœ˜—šœœ˜'Jšœ=˜=—Jšœ˜—J˜šž œœœœ˜9J˜$Jšœœ˜J˜šœ˜šœ ˜ J˜Jšœ*œ˜0˜-J˜"J˜Jšœ˜J˜—šœ˜Jšœ œ˜˜ Jšœ>˜>Jš‘œ"‘3™VJšœ˜Jšœ˜—šœ˜Jšœ-˜-Jš‘œ"‘!™DJšœ˜Jšœ˜—JšœœŸ ˜—Jšœ/˜/Jšœ˜—˜ J˜Jš œœœœŸ˜4Jšœ1˜1J˜—Jš˜—Jšœ˜—J˜—™6J™šž œ œœ˜9Jšœ'˜'—J˜šž œ œœ˜8Jšœ!˜!—J˜šžœœœœ˜>Jšœ*˜*—J˜šž œ œœ˜:Jšœ#˜#—J˜šžœ œœ˜>Jšœ*˜*—J˜Jšœ œœ ˜šœ œœ˜J˜Jšœ œ˜Jšœœ˜Jšœ˜J˜J˜—šž œœœœ˜;JšœœœŸ!œ˜XJ™"JšœœŸ!˜Mšœ'˜'Jšœ@˜C—J˜J˜—šžœœœœ˜Jšœœ˜J˜Jšœœ˜ J˜!Jšœ4˜4Jšœ œ˜*J˜Jš œœœœŸ3˜MJ˜MJš œœœœŸ˜8J™)šœ˜&J˜F˜Jšœœ˜J˜J™OJšœ.˜.J˜J˜J˜Jšœ œC˜Q˜J˜R—J˜—Jšœœ˜—J˜—J˜šžœ œœ˜=Jšœœ˜:Jšœœœœ˜!˜GJšœœL˜T—J˜—J˜—™J˜š ž œœœœœœ˜WJšœΈœ^™™šœ;œœ˜Pšœ/œ˜7šœœœœ˜>JšœG˜G—J˜—Jšœ˜—J˜J˜—šžœœœœ˜.Jšœ˜"J˜$Jš œœœœœ˜@Jšœ˜—J˜šžœœ˜JšœRœ˜WJšœœ5˜CJšœœœ˜ šœ ˜šœA˜AJšœ%˜%—˜ JšœS˜S—Jšœ Ÿ™JšœQ˜X—šœ˜Jšœ/œ˜6Jšœ˜—J˜CJšœ˜Jšœ!™!Jšœ=™=Jšœ8™8Jšœ>™>Jšœ6™6JšœE™EJšœ7™7—J˜—J˜™'K™Kšœ Οr™"—™4K™ Kšœ ’™%—K™™*K™8Kšœ ’ž™ͺ—K™™,K™vKšœ ’™—K™šœ+™+Kšœ*™*Kšœ ’&™2—K™™)K™%Kšœ ’™#—™*K™/Kšœ ’t™€—™'K™(Kšœ ’!™-—K™™&K™.Kšœ ’ ™—K™—…—82Y@