DIRECTORY
IO,
Lark
-- USING [
-- CommandEvents, ConnectionSpec, Event, LarkModel, Passel, SHHH, StatusEvents ]--,
LarkSmartsMonitorImpl,
Nice,
Log USING [ Problem, ProblemHandle, Report ],
Process USING [ SecondsToTicks, SetTimeout ],
Rope USING [ Concat ],
ThParty USING [ Advance, Alert, CreateParty ],
ThPartyPrivate
USING [
DehandleParty, DehandleSmarts, LocalSmartsInterface, RegisterLocal, SmartsBody, SmartsData ],
Thrush
USING [
ConversationHandle, ConvEvent, PartyHandle, H, MakeUnique, NB, nullConvHandle, nullHandle, pERROR, Reason, ROPE, SmartsHandle, StateInConv, ThHandle ],
ThSmartsPrivate
USING [
Apprise, ConvDesc, Deregister, EnterLarkState, GetConv, GetConvDesc, GetSIC, GetSmartsInfo, LarkFailed, LarkProgress, OpenConversations, SmartsInfo, SmartsInfoBody ],
ThSmartsRpcControl,
Triples USING [Any, Make, Select ],
TU
;
Types and Constants
ConvDesc: TYPE = ThSmartsPrivate.ConvDesc;
H: PROC[r: REF] RETURNS[Thrush.ThHandle] = INLINE {RETURN[Thrush.H[r]]; };
ROPE: TYPE = Thrush.ROPE;
SHHH: TYPE = Lark.SHHH; -- Encrypts conv. if first arg to RPC PROC
PartyHandle: TYPE = Thrush.PartyHandle;
nullHandle: Thrush.ThHandle = Thrush.nullHandle;
nullConvHandle: Thrush.ConversationHandle = Thrush.nullConvHandle;
SmartsInfo: TYPE = ThSmartsPrivate.SmartsInfo;
StateInConv: TYPE = Thrush.StateInConv;
PD:
TYPE =
RECORD [
timeoutNoAction: INT,
cSp: REF Ctr←NEW[Ctr←[0,1000000]],
cRs: REF Ctr←NEW[Ctr←[0,1000000]],
cZp: REF Ctr←NEW[Ctr←[0,1000000]],
doReports: BOOL←FALSE,
doNice: BOOL←FALSE
];
pd: REF PD ← NEW[PD←[]];
Ctr:
TYPE =
RECORD[
count: INT,
stop: INT
];
Report:
PROC[what:
ROPE, info: SmartsInfo, ctr:
REF Ctr] = {
IF NOT pd.doReports THEN RETURN;
Log.Report[what, $Lark, info.larkInfo];
ctr.count𡤌tr.count+1;
IF ctr.count>ctr.stop THEN { Log.Problem["Report overflow", $Lark, info.larkInfo]; };
};
BeNice:
PROC[r:
REF, d:
INT, info: SmartsInfo] = {
IF NOT pd.doNice THEN RETURN;
Nice.BeNice[r, d, $Lark, info.larkInfo];
};
Events
RecordTrunkEvent handles events from the EtherPhone Trunk connection.
The only interesting events at present are "ring" (place trunk->station call) and "tones F" (station->trunk call being "answered")
The upstream code produces a single "enabled" event when the line starts ringing, and a single "disabled" event when it appears that ringing has stopped. This "disabled" event must be anticipated and ignored if the station answers (calling trunk can't hang up on completed call!)
LarkTrunkParseEvent:
ENTRY
PROC[
smartsInfo: SmartsInfo, sEvent: Lark.StatusEvent] = {
ENABLE {
UNWIND=>NULL;
ThSmartsPrivate.LarkFailed => { Log.Problem["Lark Failed", $Smarts, smartsInfo]; GOTO Failed; };
ANY => { Log.Problem[, $Smarts, smartsInfo]; GOTO Failed; };
};
SELECT sEvent.device
FROM
ringDetect =>
SELECT sEvent.event
FROM
Lark.enabled => CmdCall[smartsInfo];
Lark.disabled => CmdOnhook[smartsInfo];
ENDCASE => ERROR Thrush.pERROR;
tones =>
IF sEvent.event = 'F THEN CmdAnswer[smartsInfo];
ENDCASE;
EXITS
Failed =>
ThSmartsPrivate.Deregister[ThSmartsPrivate.GetSmartsInfo[smarts: smartsInfo.otherSmarts]];
};
CmdCall:
INTERNAL
PROC[info: SmartsInfo] = {
calledPartyID: PartyHandle;
cDesc: ConvDesc;
SELECT ThSmartsPrivate.GetSIC[info]
FROM
idle => NULL;
ENDCASE => RETURN; -- not a valid time for this call. Ignore. Host system will handle.
cDesc ← ThSmartsPrivate.GetConv[info, nullConvHandle, TRUE];
calledPartyID ←
H[Triples.Select[$TrunkParty,
--owner--,
ThPartyPrivate.DehandleParty[cDesc.cState.credentials.partyID]]]; --<<UGH!>>
IF calledPartyID = nullHandle
THEN
RETURN;
Ignore the incoming call. It will be handled as unanswered by the host telephone system.
cDesc.desiredState ← active;
cDesc.desiredPartyID ← calledPartyID;
cDesc.desiredReason ← wontSay;
cDesc.desiredComment ← NIL;
cDesc.cState.state ← any; -- meaning "This is a new conversation to be made."
ThSmartsPrivate.Apprise[info];
};
CmdOnhook:
INTERNAL
PROC[info: SmartsInfo] = {
cDesc: ConvDesc=ThSmartsPrivate.GetConvDesc[info];
IF cDesc=NIL THEN RETURN;
SELECT cDesc.cState.state
FROM
initiating, maybe => NULL; -- ringing stopped before called party answered.
ENDCASE => RETURN; -- already active or something; ignore end of ringing
cDesc.desiredState←idle;
cDesc.desiredReason ← wontSay;
cDesc.desiredComment ← NIL;
ThSmartsPrivate.Apprise[info];
};
CmdAnswer:
INTERNAL
PROC[info: SmartsInfo] = {
cDesc: ConvDesc=ThSmartsPrivate.GetConvDesc[info];
IF cDesc=NIL THEN RETURN;
SELECT cDesc.cState.state
FROM
pending => NULL; -- this is the only state in which we expect any such action.
ENDCASE => RETURN;
cDesc.desiredState ← active;
ThSmartsPrivate.Apprise[info];
};
trkTransForStates:
ARRAY StateInConv
OF
ARRAY StateInConv
OF TrkTransition ← [
idle resrv pars init pend mayb ring canAc activ inact any -- desired
[ elim, invl, invl, invl, invl, invl, invl, invl, elim, ntiy, elim ], -- idle (current)
[ invl, invl, invl, invl, invl, invl, invl, invl, invl, invl, invl ], -- reserved
[ invl, invl, invl, invl, invl, invl, invl, invl, invl, invl, invl ], -- parsing
[ idle, invl, invl, noop, invl, invl, invl, invl, noop, ntiy, invl ], -- initiating
[ idle, invl, invl, invl, invl, invl, invl, invl, actv, ntiy, pend ], -- pending
[ idle, invl, invl, invl, invl, noop, invl, invl, noop, ntiy, invl ], -- maybe
[ invl, invl, invl, invl, invl, invl, invl, invl, invl, ntiy, invl ], -- ringing
[ ntiy, invl, invl, invl, invl, invl, invl, invl, ntiy, ntiy, invl ], -- canActivate
[ idle, invl, invl, invl, invl, invl, invl, invl, spvs, ntiy, invl ], -- active
[ ntiy, invl, invl, invl, invl, invl, invl, invl, ntiy, noop, invl ], -- inactive
[ elim, invl, invl, invl, invl, invl, invl, invl, alrt, ntiy, invl ] -- any (nonex)
}.