LarkSmartsImpl.mesa
Last modified by D. Swinehart, September 10, 1985 5:14:30 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
DIRECTORY
IO,
Lark,
LarkSmartsMonitorImpl,
Rope USING [ Concat, Equal, Fetch, FromChar ],
Log USING [ ProblemFR, ProblemHandle ],
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 ]
;
LarkSmartsImpl: CEDAR MONITOR LOCKS root
IMPORTS IO,
root: LarkSmartsMonitorImpl,
Rope,
Log,
Process,
ThParty,
Thrush,
ThSmartsPrivate,
TU
EXPORTS ThSmartsPrivate
SHARES LarkSmartsMonitorImpl = {
OPEN IO;
Copies
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;
Parsing
ParseEvent handles events from the EtherPhone
LarkParseEvent: PUBLIC ENTRY PROC[
smartsInfo: SmartsInfo, sEvent: Lark.StatusEvent] = {
ENABLE {
UNWIND=>NULL;
ThSmartsPrivate.LarkFailed => { Log.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𡤀
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]; };
Be sure state of conv. and tones agree with state of parsing.
<<This feels like in the wrong place. Oughta be done by a command proc of some sort?
Or get rid of the command procs?>>
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] = {
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.
info.haveOne ← TRUE;
SELECT event FROM
The biggies.
disabled => { info.haveArguments ← TRUE; info.Command ← CmdOnhook; };
enabled => { info.haveArguments ← TRUE; info.Command ← CmdOffhook; };
Termination of arguments, DEL
endNum => {
info.haveArguments ← TRUE;
IF info.parseState=idle AND info.Command=NIL THEN info.Command ← CmdQuietJay;
};
IO.DEL, IO.ESC => []←SetParserIdle[info, TRUE];
Command or recipient initiation
'* => {
IF info.cmdOrRecip THEN info.offset�
info.cmdOrRecip ← TRUE; info.parseState ← idle; };
Valid argument event
ENDCASE => {
IF info.cmdOrRecip THEN {
info.cmdOrRecip ← FALSE;
event𡤎vent+info.offset;
info.offset𡤀
IF event = '0 THEN {
[]←SetParserIdle[info, FALSE]; info.parseState ← getFeep; RETURN;};
[]←SetParserIdle[info, TRUE];
SELECT event FROM
Here are the commands
'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];
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+10, 'T, 't => { info.Command ← CmdBackDoor; info.haveArguments ← TRUE; };
**0
'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𡤌mdRingOnceTimed; info.parseState ← getNum; };
'9+10, 'R, 'r => info.Command ← CmdFlash;
ENDCASE => -- unassigned same as DEL -- NULL;
RETURN; };
A letter, digit, or other non-activation character.
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;
Require explicit termination of calls beginning with unrecognized prefixes
2 => IF info.arguments.Equal["90"] THEN info.parseState ← inNum;
Require explicit termination of international calls, calling card calls, calls to Telco Operator
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]; };
User-invoked actions
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�tive;
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;
};
Connection Management Utilities
GetConvDesc: PUBLIC PROC[info: SmartsInfo] RETURNS [ cDesc: ConvDesc←NIL ] = {
RETURN[GetCDesc[info]];
};
GetCDesc: PROC[info: SmartsInfo] RETURNS [ cDesc: ConvDesc←NIL ] = {
Not at present protected by monitor -- LarkStateImpl back-pointer problem.
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];
};
}.
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