DIRECTORY IO, Lark, LarkSmartsMonitorImpl, Rope USING [ Concat, Equal, Fetch, FromChar, Substr ], Log USING [ Problem, ProblemHandle, SLOG ], ThParty USING [ GetPartyFromNumber, GetPartyFromFeepNum ], Thrush USING [ ConversationHandle, H, IntervalSpec, PartyHandle, nullConvHandle, nullHandle, ROPE, StateInConv, ThHandle ], ThSmartsPrivate USING [ Apprise, ChangeState, ConvDesc, Deregister, EnableSmarts, EnterLarkState, GetConv, InterpretHookState, LarkFailed, OpenConversations, ParseState, SetRingingParameters, SmartsInfo ] ; LarkSmartsImpl: CEDAR MONITOR LOCKS root IMPORTS IO, root: LarkSmartsMonitorImpl, Rope, Log, ThParty, Thrush, ThSmartsPrivate 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; 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; defaultRecordLength: INT _ -1; LarkParseEvent: PUBLIC ENTRY PROC[ smartsInfo: SmartsInfo, sEvent: Lark.StatusEvent] = { ENABLE { UNWIND=>NULL; ThSmartsPrivate.LarkFailed => { Log.Problem["Lark Failed", $Smarts, smartsInfo]; GOTO Failed; }; ANY => { Log.Problem[NIL, $Smarts, smartsInfo]; GOTO Failed; }; }; cDesc: ConvDesc; IF ~LarkEnabled[smartsInfo] THEN RETURN; -- don't interfere with reverted call! sEvent _ ThSmartsPrivate.InterpretHookState[smartsInfo.larkInfo, sEvent]; IF sEvent.device=nothing THEN RETURN; Log.SLOG[]; 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]]; 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 cDesc.desiredState_reserved ELSE RETURN; 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 _ CmdHotline; 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 info.haveArguments _ SELECT info.argLength FROM 1 => event='0, 2 => info.arguments.Fetch[0]='9 AND info.arguments.Fetch[1]='0, 4 => (SELECT info.arguments.Fetch[0] FROM '9 => info.arguments.Substr[start: 2, len: 2].Equal["11"], '8 => info.arguments.Fetch[1] = '1, ENDCASE => TRUE), 8 => info.arguments.Fetch[2] > '1, 11 => TRUE, ENDCASE=>FALSE; }; 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; Log.SLOG[]; 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] = { ThSmartsPrivate.SetRingingParameters[info: info.larkInfo, ringEnable: off]; }; CmdRingOn: INTERNAL PROC[info: SmartsInfo, val: INT] = { ThSmartsPrivate.SetRingingParameters[info: info.larkInfo, ringEnable: on]; }; CmdRingOffTimed: INTERNAL PROC[info: SmartsInfo, val: INT] = { ThSmartsPrivate.SetRingingParameters[info: info.larkInfo, ringEnable: offTimed, ringInterval: (IF val#0 THEN val ELSE 30)*60]; }; CmdRingOnce: INTERNAL PROC[info: SmartsInfo, val: INT] = { ThSmartsPrivate.SetRingingParameters[info: info.larkInfo, ringEnable: subdued]; }; CmdRingOnceTimed: INTERNAL PROC[info: SmartsInfo, val: INT] = { ThSmartsPrivate.SetRingingParameters[info: info.larkInfo, ringEnable: subduedTimed, ringInterval: (IF val#0 THEN val ELSE 30)*60]; }; CmdFlash: INTERNAL PROC[info: SmartsInfo, val: INT] = { NULL; }; CmdRadio: INTERNAL PROC[info: SmartsInfo, val: INT] = { info.larkInfo.radio _ NOT info.larkInfo.radio; }; CmdHotline: INTERNAL PROC[info: SmartsInfo, val: INT] = { info.larkInfo.hotLine _ NOT info.larkInfo.hotLine; }; 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; calledPartyID _ SELECT parseState FROM inSeq, inNum => ThParty.GetPartyFromNumber[ partyID: partyID, phoneNumber: args, trunkOK: trunkOK], inFeep => ThParty.GetPartyFromFeepNum[partyID: partyID, feepNum: args], inStr => Log.ProblemHandle[NIL, $Smarts, nullHandle, info], -- not these days! ENDCASE => Log.ProblemHandle[NIL, $Smarts, nullHandle, info]; }; }. LarkSmartsImpl.mesa Last modified by D. Swinehart, November 27, 1983 12:53 am 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. User-invoked actions Connection Management Utilities Not at present protected by monitor -- LarkStateImpl back-pointer problem. Ê ;˜Jšœ™šœ9™9J™—šÏk œ˜ Jšœ˜Jšœ˜J˜Jšœœ,˜6Jšœœœ˜+Jšœœ-˜:šœœ˜Jšœœ9œ˜l—šœœ˜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˜0Jšœœ%˜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šœœ˜ šœ˜šœ ˜ Jšœ6œ˜˜OJšœœœœ ˜.—J˜—J˜šŸ œ œœ˜:J˜OJ˜—J˜šŸœ œœ˜?˜SJšœœœœ ˜.—J˜—J˜šŸœ œœ˜7Jšœ˜J˜—J˜šŸœ œœ˜7Jšœœ˜.J˜—J˜šŸ œ œœ˜9Jšœœ˜2J˜J˜—šŸ œ œœ˜9Jšœœ˜2J˜—J˜—™J˜šŸ œ œœœ˜NJšœ˜J˜J˜—šŸœœœœ˜DJ™JJšœ.˜.Jšœœœ˜%šœ;œœ˜Pšœ0œ˜8Jšœœ˜—Jšœ˜—Jšœœœœœœœ˜=J˜J˜—šŸœœœ˜QJ˜!Jš œœœœœ˜7Jšœ˜—J˜šŸœœP˜gJšœœ œ˜Jšœ.˜5Jšœœœ˜$šœœ ˜&šœ+˜+Jšœ7˜7—˜ Jšœ=˜=—Jšœœž˜NJšœœ˜=—Jšœ˜——J˜—…—&>6•