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, ringTune: info.larkInfo.ringTune];
};

CmdRingOn: INTERNAL PROC[info: SmartsInfo, val: INT] = {
ThSmartsPrivate.SetRingingParameters[info: info.larkInfo, ringEnable: on, ringTune: info.larkInfo.ringTune];
};

CmdRingOffTimed: INTERNAL PROC[info: SmartsInfo, val: INT] = {
ThSmartsPrivate.SetRingingParameters[info: info.larkInfo, ringEnable: offTimed,
ringInterval: (IF val#0 THEN val ELSE 30)*60, ringTune: info.larkInfo.ringTune];
};

CmdRingOnce: INTERNAL PROC[info: SmartsInfo, val: INT] = {
ThSmartsPrivate.SetRingingParameters[info: info.larkInfo, ringEnable: subdued, ringTune: info.larkInfo.ringTune];
};

CmdRingOnceTimed: INTERNAL PROC[info: SmartsInfo, val: INT] = {
ThSmartsPrivate.SetRingingParameters[info: info.larkInfo, ringEnable: subduedTimed,
ringInterval: (IF val#0 THEN val ELSE 30)*60, ringTune: info.larkInfo.ringTune];
};

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];
};
}.
���N��LarkSmartsImpl.mesa
Last modified by D. Swinehart, November 27, 1983 12:53 am
Last Edited by: Pier, May 3, 1984 3:00:22 pm PDT

Copies


Parsing

ParseEvent handles events from the EtherPhone
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?>>
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.
�ÊF��˜�Jšœ™šœ9™9J™0J™�—šÏ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šœœœÏc*˜CJšœœ˜.J˜'Jšœœ˜'J™�Jšœœ˜—J™�šž™J™�Jšœ-™-šÏnœœœœ˜"Jšœ5˜5šœ˜Jšœœ˜
JšœQœ˜`Jšœœœ˜?J˜—J˜Jšœœœž&˜OJšœI˜IJšœœœ˜%Jšœœ˜Jšœ"˜"šœœ˜"Jšœœ˜Jšœœœ˜<šœ˜!Jš	œœœœœ˜@—Jšœ$˜$Jšœœ˜&—Jšœ=™=J™UJ™"J˜šœœœ˜šœ˜Jšœœž%˜EJš	œœœœœ˜NJš	œœœœœ˜OJšœœ˜—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šœ2œ˜:—šœž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šœœ˜——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šœœ˜šœ˜šœ	˜	Jšœ6œ˜<J˜Jšœ˜—J˜"Jšœ˜—Jšœ7˜7Jšœ˜—J˜�šŸ
œ
œœ˜9Jšœm˜mJ˜—J˜�šŸ	œ
œœ˜8J˜lJ˜—J˜�šŸœœœœ˜>˜OJšœœœœ+˜P—J˜—J˜�šŸœ
œœ˜:Jšœq˜qJ˜—J˜�šŸœ
œœ˜?˜SJšœœœœ+˜P—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˜—�…—����&è��7|��