DIRECTORY IO, Lark, LarkSmartsMonitorImpl, Rope USING [ Concat, Equal, Fetch, FromChar ], Process USING [ Detach ], ThParty USING [ GetPartyFromNumber, GetPartyFromFeepNum, SetRingEnable ], Thrush USING [ ConversationHandle, H, IntervalSpec, PartyHandle, pERROR, nullConvHandle, nullHandle, RingEnable, ROPE, StateInConv, ThHandle ], ThSmartsPrivate USING [ Apprise, ChangeState, ConvDesc, Deregister, EnableSmarts, EnterLarkState, GetConv, InterpretHookState, LarkFailed, LarkInfo, OpenConversations, ParseState, SmartsInfo ], TU USING [ RefAddr ], VoiceUtils USING [ Problem, ProblemFR ] ; LarkSmartsImpl: CEDAR MONITOR LOCKS root IMPORTS IO, root: LarkSmartsMonitorImpl, Rope, Process, ThParty, Thrush, ThSmartsPrivate, TU, VoiceUtils EXPORTS ThSmartsPrivate SHARES LarkSmartsMonitorImpl = { OPEN IO; CommandEvents: TYPE = Lark.CommandEvents; ConversationHandle: TYPE = Thrush.ConversationHandle; nullConvHandle: ConversationHandle=Thrush.nullConvHandle; ConvDesc: TYPE = ThSmartsPrivate.ConvDesc; disabled: Lark.Event = Lark.disabled; enabled: Lark.Event = Lark.enabled; endNum: Lark.Event = Lark.endNum; H: PROC[r: REF] RETURNS[Thrush.ThHandle] = INLINE {RETURN[Thrush.H[r]]; }; IntervalSpec: TYPE = Thrush.IntervalSpec; LarkInfo: TYPE = ThSmartsPrivate.LarkInfo; nullHandle: Thrush.ThHandle = Thrush.nullHandle; OpenConversations: TYPE = ThSmartsPrivate.OpenConversations; PartyHandle: TYPE = Thrush.PartyHandle; 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; larkRegistry: ROPE_".lark"; defaultRecordLength: INT _ -1; LarkParseEvent: PUBLIC ENTRY PROC[ smartsInfo: SmartsInfo, sEvent: Lark.StatusEvent] = { ENABLE { UNWIND=>NULL; ThSmartsPrivate.LarkFailed => { VoiceUtils.ProblemFR["%g: Lark Failed", $Smarts, smartsInfo, TU.RefAddr[smartsInfo]]; GOTO Failed; }; }; cDesc: ConvDesc; IF ~LarkEnabled[smartsInfo] THEN RETURN; -- don't interfere with reverted call! sEvent _ ThSmartsPrivate.InterpretHookState[smartsInfo.larkInfo, sEvent, smartsInfo]; SELECT sEvent.device FROM nothing => RETURN; speakerSwitch, touchPad, hookSwitch => NULL; ENDCASE => Thrush.pERROR; DoParse[smartsInfo, sEvent.event]; IF smartsInfo.haveArguments THEN { val: INT_0; IF smartsInfo.Command=NIL THEN smartsInfo.Command _ CmdCall; 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]; []_SetParserIdle[smartsInfo, TRUE]; }; cDesc _ GetCDesc[smartsInfo]; IF cDesc#NIL THEN { SELECT cDesc.cState.state FROM # cDesc.desiredState => RETURN; -- some request already in the works. reserved => IF smartsInfo.haveOne THEN cDesc.desiredState_parsing ELSE RETURN; parsing => IF smartsInfo.haveOne THEN RETURN ELSE { cDesc.desiredState_reserved; cDesc.desiredReason _ wontSay; }; ENDCASE=>RETURN; ThSmartsPrivate.Apprise[smartsInfo]; }; EXITS Failed => ThSmartsPrivate.Deregister[smartsInfo]; }; LarkEnabled: INTERNAL PROC[info: SmartsInfo] RETURNS [enabled: BOOL] = INLINE { RETURN[SELECT info.larkInfo.larkState FROM none => ThSmartsPrivate.EnableSmarts[info], failed, recovering => FALSE, ENDCASE => TRUE]; }; DoParse: INTERNAL PROC[info: SmartsInfo, event: Lark.Event] = { info.haveOne _ TRUE; SELECT event FROM disabled => { info.haveArguments _ TRUE; info.Command _ CmdOnhook; }; enabled => { info.haveArguments _ TRUE; info.Command _ CmdOffhook; }; endNum => { info.haveArguments _ TRUE; }; IO.DEL, IO.ESC => []_SetParserIdle[info, TRUE]; '* => { IF info.cmdOrRecip THEN info.offset_10; info.cmdOrRecip _ TRUE; info.parseState _ idle; }; ENDCASE => { IF info.cmdOrRecip THEN { info.cmdOrRecip _ FALSE; event_event+info.offset; info.offset_0; IF event = '0 THEN { []_SetParserIdle[info, FALSE]; info.parseState _ getFeep; RETURN;}; []_SetParserIdle[info, TRUE]; SELECT event FROM '1, 'H, 'h => -- hold << for now, toggle "radio" state >> { info.Command _ CmdRadio; info.haveArguments _ TRUE; }; '3, 'C, 'c => -- conference << for now, toggle "hot line" state >> { info.Command _ CmdAutoAnswer; info.haveArguments _ TRUE; }; '7, 'F, 'f => -- forward << for now, toggle "monitored" state >> { info.Command _ CmdMonitor; info.haveArguments _ TRUE; }; '*, -- esc endNum -- del -- => []_SetParserIdle[info, TRUE]; '0+10, 'T, 't => { info.Command _ CmdBackDoor; info.haveArguments _ TRUE; }; '1+10, 'O, 'o => { info.Command _ CmdRingOff; info.haveArguments _ TRUE; }; '2+10, 'N, 'n => { info.Command _ CmdRingOn; info.haveArguments _ TRUE; }; '3+10, 'L, 'l => { info.Command _ CmdRingOffTimed; info.parseState _ getNum; }; '4+10, 'A, 'a => { info.Command _ CmdRingOnce; info.haveArguments _ TRUE; }; '5+10, 'B, 'b => { info.Command_CmdRingOnceTimed; info.parseState _ getNum; }; '9+10, 'R, 'r => info.Command _ CmdFlash; ENDCASE => -- unassigned same as DEL -- NULL; RETURN; }; ParseArgument[info, event]; }; }; ParseArgument: INTERNAL PROC[info: SmartsInfo, event: Lark.Event] = { AppendEvent[info, 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.argLength FROM 1 => SELECT event FROM '0 => info.haveArguments_TRUE; '4, '5, '6, '8, '9 => NULL; 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; ENDCASE; 8 => IF info.arguments.Fetch[2] > '1 THEN info.haveArguments _ TRUE; 11 => info.haveArguments _ TRUE; ENDCASE; }; }; AppendEvent: INTERNAL PROC[info: SmartsInfo, event: Lark.Event] = { info.arguments _ Rope.Concat[info.arguments, Rope.FromChar[event]]; info.argLength _ info.argLength + 1; }; SetParserIdle: INTERNAL PROC[info: SmartsInfo, clearCmd: BOOL] RETURNS [ ThSmartsPrivate.ParseState ] = { info.haveArguments _ info.haveOne _ FALSE; info.parseState _ idle; info.arguments _ NIL; info.offset _ 0; info.cmdOrRecip _ FALSE; info.argLength _ 0; IF clearCmd THEN info.Command _ NIL; RETURN[idle]; }; CmdCall: INTERNAL PROC[info: SmartsInfo, val: INT] = { partyID: PartyHandle; cDesc: ConvDesc; IF info.argLength=0 OR info.parseState=inTossStr THEN RETURN; SELECT GetSIC[info] FROM reserved, parsing => NULL; ENDCASE => RETURN; cDesc _ ThSmartsPrivate.GetConv[info, info.currentConvID, TRUE]; partyID _ cDesc.cState.credentials.partyID; cDesc.desiredPartyID _ GetPartiesForCall[info, partyID, info.parseState, info.arguments, TRUE]; ThSmartsPrivate.ChangeState[info, cDesc, active]; }; CmdBackDoor: INTERNAL PROC[info: SmartsInfo, val: INT] = { IF GetSIC[info]=active AND info.larkInfo.larkState = trunkTalking THEN { -- Hack ThSmartsPrivate.EnterLarkState[info.larkInfo, trunkFlashing, info]; RETURN; }; info.arguments _ NIL; info.parseState _ inSeq; info.argLength _ 1; CmdCall[info, val]; }; CmdOnhook: INTERNAL PROC[info: SmartsInfo, val: INT] = { ThSmartsPrivate.ChangeState[info, GetCDesc[info], idle, terminating]; }; CmdOffhook: INTERNAL PROC[info: SmartsInfo, val: INT] = { cDesc: ConvDesc; desiredState: StateInConv_active; SELECT GetSIC[info] FROM idle => { cDesc _ ThSmartsPrivate.GetConv[info, nullConvHandle, TRUE]; desiredState _ reserved; }; ringing => cDesc _ GetCDesc[info]; ENDCASE => RETURN; ThSmartsPrivate.ChangeState[info, cDesc, desiredState]; }; CmdRingOff: INTERNAL PROC[info: SmartsInfo, val: INT] = { CmdRingDo[info,off,val]; }; CmdRingOn: INTERNAL PROC[info: SmartsInfo, val: INT] = { CmdRingDo[info,on,val]; }; CmdRingOffTimed: INTERNAL PROC[info: SmartsInfo, val: INT] = { CmdRingDo[info,offTimed,val]; }; CmdRingOnce: INTERNAL PROC[info: SmartsInfo, val: INT] = { CmdRingDo[info,subdued,val]; }; CmdRingOnceTimed: INTERNAL PROC[info: SmartsInfo, val: INT] = { CmdRingDo[info,subduedTimed,val]; }; CmdRingDo: INTERNAL PROC[info: SmartsInfo, enable: Thrush.RingEnable, val: INT] = TRUSTED INLINE { Process.Detach[FORK CmdRingDoProc[info, enable, val]]; }; CmdRingDoProc: ENTRY PROC[info: SmartsInfo, enable: Thrush.RingEnable, val: INT] = { partyID: PartyHandle; cDesc: ConvDesc = ThSmartsPrivate.GetConv[info, info.currentConvID, FALSE]; IF cDesc=NIL THEN RETURN; partyID _ cDesc.cState.credentials.partyID; ThParty.SetRingEnable[ partyID: partyID, ringEnable: enable, ringInterval: (IF val#0 THEN val ELSE 30)*60, update: TRUE]; }; CmdFlash: INTERNAL PROC[info: SmartsInfo, val: INT] = { NULL; }; CmdRadio: INTERNAL PROC[info: SmartsInfo, val: INT] = { info.larkInfo.radio _ NOT info.larkInfo.radio; }; CmdAutoAnswer: INTERNAL PROC[info: SmartsInfo, val: INT] = { info.larkInfo.autoAnswer _ NOT info.larkInfo.autoAnswer; }; CmdMonitor: INTERNAL PROC[info: SmartsInfo, val: INT] = { info.larkInfo.monitor _ NOT info.larkInfo.monitor; }; GetConvDesc: PUBLIC PROC[info: SmartsInfo] RETURNS [ cDesc: ConvDesc_NIL ] = { RETURN[GetCDesc[info]]; }; GetCDesc: PROC[info: SmartsInfo] RETURNS [ cDesc: ConvDesc_NIL ] = { convID: ConversationHandle=info.currentConvID; IF convID=nullConvHandle THEN RETURN; FOR convs: OpenConversations _ info.conversations, convs.rest WHILE convs#NIL DO IF convs.first.cState.credentials.convID = convID THEN { cDesc _ convs.first; EXIT; }; ENDLOOP; RETURN[IF cDesc#NIL AND cDesc.descValid THEN cDesc ELSE NIL]; }; GetSIC: PUBLIC INTERNAL PROC[info: SmartsInfo] RETURNS [ state: StateInConv ] = { cDesc: ConvDesc = GetCDesc[info]; RETURN[IF cDesc=NIL THEN idle ELSE cDesc.cState.state]; }; GetPartiesForCall: PROC[info: SmartsInfo, partyID: PartyHandle, parseState: ThSmartsPrivate.ParseState, args: ROPE, trunkOK: BOOL] RETURNS[calledPartyID: PartyHandle _ nullHandle ] = { IF partyID = nullHandle THEN RETURN; SELECT parseState FROM inSeq, inNum => calledPartyID _ ThParty.GetPartyFromNumber[ partyID: partyID, phoneNumber: args, trunkOK: trunkOK]; inFeep => calledPartyID _ ThParty.GetPartyFromFeepNum[partyID: partyID, feepNum: args]; inStr => VoiceUtils.Problem[NIL, $Smarts, info]; -- not these days! ENDCASE => VoiceUtils.Problem[NIL, $Smarts, info]; }; }. LarkSmartsImpl.mesa Last modified by D. Swinehart, May 17, 1986 3:48:13 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 Be sure state of conv. and tones agree with state of parsing. <> 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. The biggies. Termination of arguments, DEL IF info.parseState=idle AND info.Command=NIL THEN info.Command _ CmdQuietJay; Command or recipient initiation Valid argument event Here are the commands Message system commands '2, 'R, 'r => { info.Command _ CmdRecord; info.haveArguments _ TRUE; }; '4, 'P, 'p => { info.Command _ CmdPlayOwn; info.haveArguments _ TRUE; }; '5, 'D, 'd => { info.Command _ CmdDelete; info.haveArguments _ TRUE; }; '6, 'G, 'g => { info.Command _ CmdPlayRecd; info.parseState _ getNum; }; '8, 'M, 'm => info.Command _ CmdMail; **0 A letter, digit, or other non-activation character. Require explicit termination of calls beginning with unrecognized prefixes Require explicit termination of international calls, calling card calls, calls to Telco Operator User-invoked actions Connection Management Utilities Not at present protected by monitor -- LarkStateImpl back-pointer problem. 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 Κ t˜Jšœ™šœ:™:J™0Icode™4J™—šΟk œ˜ Jšœ˜Jšœ˜J˜Jšœœ$˜.Jšœœ ˜Jšœœ<˜Išœœ˜JšœœMœ˜€—šœœ˜Jšœ©˜©—Jšœœ ˜Jšœ'˜'J˜J˜—šœœœœ˜(šœœ˜ J˜J˜J˜J˜J˜J˜Jš˜J˜ —Jšœ˜Jšœ˜ Jšœœ˜J˜—šœ™J˜Jšœœ˜)šœœ˜5Jšœ9˜9—Jšœ œ˜*Jšœ%˜%Jšœ#˜#Jšœ!˜!Jšœœœœœœœ˜JJšœœ˜)Jšœ œ˜*J˜0Jšœœ%˜˜>—Jšœœ˜—J˜$J˜—š˜Jšœ1˜1—Jšœ˜J˜—š Ÿ œœœœ œœ˜Ošœœ˜*J˜+Jšœœ˜Jšœœ˜—J˜J˜—šŸœœœ)˜?J™ρJšœœ˜šœ˜J™J™ Jšœ#œ˜EJšœ"œ˜EJ˜J™šœ ˜ Jšœœ˜Jšœœœœ™MJšœ˜—Jš œœœœœ˜/J˜J™šœ˜Jšœœ˜'Jšœœ˜2—J˜J™šœ˜ šœœ˜Jšœœ˜J˜J˜šœ œ˜Jšœœœ˜C—Jšœœ˜šœ˜J™šœž+˜9Jšœ0œ˜8—šœž4˜BJšœ5œ˜=—šœž2˜@Jšœ2œ˜:—Jšœž˜ Jšœž œœ˜1J˜J™Jšœ?œ™GJšœ@œ™HJšœ?œ™GJ™HJ™%šœDœ˜LJ™—JšœCœ˜KJšœBœ˜JJšœO˜OJšœL˜LJšœN˜NJ˜)Jšœžœœ˜-—Jšœ˜ —J˜J™3J˜!——J˜—šŸ œœœ)˜EJšœ˜šœœ˜#šœ œ˜*J˜J˜J˜J˜Jšœ˜—šœœ˜&J˜J˜,Jšœœ˜#Jšœ˜——šœœ˜!Jšœ)˜)šœ˜šœœ ˜Jšœ˜J˜šœ˜#JšžJ™J——šœœœ˜@Jšœ`™`—Jšœœœœ˜Ašœœ˜Jšœœœœ˜EJšœ#œ˜(Jšœ˜—Jšœœœœ˜DJšœœ˜ Jšœ˜—J˜—Jšœ˜—J˜šŸ œœœ)˜CJšœC˜CJ˜(—J˜šŸ œœœœ˜>Jšœ#˜*Jšœ$œ˜*Jšœ)œ˜-Jšœ#œ˜)Jšœœ œœ˜8Jšœ ˜J˜——™J™J˜šŸœœœœ˜6J˜J˜Jšœœœœ˜=Jš œœœœœ˜FJšœ:œ˜@J˜+JšœYœ˜_J˜1Jšœ˜—J˜šŸ œœœœ˜:šœœ(œž˜PJšœDœ˜N—Jšœœ˜J˜J˜J˜J˜J˜—šŸ œ œœ˜8J˜EJšœ˜—J˜šŸ œœœœ˜9J˜J˜!šœ˜šœ ˜ Jšœ6œ˜Jšœ ˜ —J˜šŸ œ œœ˜:Jšœ˜—J˜šŸœ œœ˜?Jšœ$˜$—J˜Jš Ÿ œœœ3œœœ&˜œJ˜šŸ œœœ3œ˜TJ˜JšœDœ˜KJšœœœœ˜J˜+šœ˜Jš œ5œœœœ˜b—J˜—J˜šŸœ œœ˜7Jšœ˜J˜—J˜šŸœ œœ˜7Jšœœ˜.J˜—J˜šŸ œ œœ˜