LarkSmartsImpl.mesa
Last modified by D. Swinehart, November 27, 1983 12:53 am
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;
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;
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;
Parsing
ParseEvent handles events from the EtherPhone
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𡤀
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]; };
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 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] = {
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 ← 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];
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
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]; };
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;
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;
};
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];
};
}.