LarkTrunkSmartsImpl.mesa
Copyright Ó 1986, 1987 by Xerox Corporation. All rights reserved.
Last modified by D. Swinehart, June 21, 1987 9:21:23 pm PDT
Note: Incoming calls are those from the Etherphone to the outside party, whom this system represents. Outgoing calls are those from the outside world to an Etherphone. Remember; trunk party is a representative of people in the public telephone system.
See LarkSmartsImpl and LarkSmartsSupImpl for various comments guiding the structure of this module; it's simpler, but similar.
DIRECTORY
Atom USING [ DottedPairNode, GetPropFromList, PutPropOnList ],
Commander USING [ CommandProc, Register ],
IO,
Lark -- USING [
-- CommandEvents, ConnectionSpec, Event, LarkModel, Passel, StatusEvents ]--,
LarkFeep,
LarkFeepRpcControl USING [ ExportInterface, InterfaceRecord, InterfaceRecordObject ],
LarkSmartsMonitorImpl,
MBQueue USING [ Create, QueueClientAction ],
Nice,
Process USING [ Detach, SecondsToTicks, SetTimeout ],
RefID USING [ ID, nullID, Reseal, Seal, Unseal ],
Rope USING [ ROPE ],
RPC USING [ EncryptionKey, ExportFailed ],
ThParty USING [ Alert, ConversationInfo, CreateConversation, DescribeParty, GetConversationInfo, GetKeyTable, GetParty, PartyInfo, RegisterServiceInterface, ReportAction, SmartsInterfaceRecord ],
ThPartyPrivate USING [ NewId, RegisterLocal ],
Thrush USING [
ActionID, ActionReport, ConversationID, ConvEvent, Credentials, InterfaceSpec, NetAddress, PartyID, NB, noAddress, nullConvID, nullID, Reason, ROPE, SHHH, SmartsID, StateInConv ],
ThSmartsPrivate USING [
ChangeState, ComputeConnection, ConvDesc, EnterLarkState, Fail, ForgetConv, GetConv, GetConvDesc, GetSIC, GetSmartsInfo, HardwareInUse, HardwareRequired, InputEventSpec, LarkInfo, LarkReportAction, LarkState, OpenConversations, QueueFeeps, SmartsInfo, SmartsInfoBody ],
ThSmartsRpcControl,
Triples USING [Make ],
TU,
VoiceUtils USING [ CmdOrToken, CurrentPasskey, MakeRName, Problem, ProblemFR, ReportFR ]
;
LarkTrunkSmartsImpl: CEDAR MONITOR LOCKS root
IMPORTS
Atom,
Commander,
root: LarkSmartsMonitorImpl,
IO,
LarkFeepRpcControl,
MBQueue,
Nice,
Process,
RefID,
RPC,
ThParty,
ThPartyPrivate,
ThSmartsPrivate,
ThSmartsRpcControl,
Triples,
VoiceUtils
EXPORTS ThSmartsPrivate, LarkFeep --, ThSmarts via Interface record --
SHARES LarkSmartsMonitorImpl = { OPEN IO;
Types and Constants
ConvDesc: TYPE = ThSmartsPrivate.ConvDesc;
Reseal: PROC[r: REF] RETURNS[RefID.ID] = INLINE {RETURN[RefID.Reseal[r]]; };
ROPE: TYPE = Thrush.ROPE;
SHHH: TYPE = Lark.SHHH; -- Encrypts conv. if first arg to RPC PROC
PartyID: TYPE = Thrush.PartyID;
nullID: RefID.ID = Thrush.nullID;
nullConvID: Thrush.ConversationID = Thrush.nullConvID;
SmartsInfo: TYPE = ThSmartsPrivate.SmartsInfo;
StateInConv: TYPE = Thrush.StateInConv;
PD: TYPE = RECORD [
timeoutNoAction: INT�,
timeoutUnstableState: INT ← 5,
checkConflictAtConvCreate: BOOLFALSE, -- safer, but not as useful.
doReports: BOOLFALSE,
doNice: BOOLFALSE,
reportToAll: BOOLTRUE
];
pd: REF PDNEW[PD←[]];
larkFeepSpec: Thrush.InterfaceSpec ← [interfaceName: [type: "LarkFeep"], serviceID: RefID.nullID];
larkFeepUser: ROPE;
larkFeepPassword: RPC.EncryptionKey;
Problem:
PROC[what: ROPE, info: SmartsInfo←NIL, v1: IO.Value ← [null[]], v2: IO.Value ← [null[]]] = {
VoiceUtils.ProblemFR[what, $Lark, IF info#NIL THEN info.larkInfo ELSE NIL, v1, v2];
};
Initialization
RegisterTrunk: PUBLIC INTERNAL PROC[
hostSmartsID: Thrush.SmartsID, hostInfo: SmartsInfo, partyRname: ROPE ]
RETURNS [ nb: Thrush.NB←$success, smartsID: Thrush.SmartsID←nullID ] = {
ENABLE UNWIND => NULL;
There's no provision here at all for reregistration. Yet.
localSmarts: ThParty.SmartsInterfaceRecord;
info: SmartsInfo;
smarts: REF;
credentials: Thrush.Credentials;
exportRequired: BOOL ← larkFeepSpec.interfaceName.instance = NIL;
netAddress: Thrush.NetAddress ← hostInfo.larkInfo.netAddress;
feepInterface: LarkFeepRpcControl.InterfaceRecord;
netAddress.socket ← ThPartyPrivate.NewId[];
localSmarts ← ThSmartsRpcControl.NewInterfaceRecord[];
localSmarts.clientStubProgress ← LarkTrunkProgress;
localSmarts.clientStubSubstitution ← LarkTrunkSubstitution;
localSmarts.clientStubReportAction ← ThSmartsPrivate.LarkReportAction;
localSmarts.clientStubCheckIn ← LarkTrunkCheckIn;
[nb, credentials] ← ThPartyPrivate.RegisterLocal [
rName: partyRname,
type: $trunk,
interfaceRecord: localSmarts,
properties: [role: $voiceTerminal, netAddress: hostInfo.larkInfo.netAddress]
];
IF nb # $success THEN {
VoiceUtils.ProblemFR[
"Can't register Lark trunk: %g -- %g",$System,NIL, rope[partyRname], atom[nb]];
RETURN;
};
smarts ← RefID.Unseal[smartsID𡤌redentials.smartsID];
IF smarts = NIL THEN ERROR;
info ← NEW[ThSmartsPrivate.SmartsInfoBody ← [
smartsID: smartsID,
otherSmartsID: hostSmartsID,
partyType: $trunk,
ParseEvent: LarkTrunkParseEvent,
NoteNewStateP: NoteNewTkState,
requests: MBQueue.Create[],
larkInfo: hostInfo.larkInfo ]];
info.larkInfo.larkTrunkSmartsInfo ← info;
Triples.Make[$SmartsData, smarts, info];
larkFeepSpec.serviceID ← smartsID; -- identify self.
feepInterface ← NEW[LarkFeepRpcControl.InterfaceRecordObject];
feepInterface.clientStubFeep ← FeepOp;
feepInterface.clientStubFlash ← FlashOp;
larkFeepSpec.interfaceID ← RefID.Seal[feepInterface];
IF ([, larkFeepSpec] ← ThParty.RegisterServiceInterface[credentials: credentials,
interfaceSpecPattern: larkFeepSpec]).nb # $success THEN GOTO CantExportFeep;
IF exportRequired THEN LarkFeepRpcControl.ExportInterface[
larkFeepSpec.interfaceName, larkFeepUser, larkFeepPassword!
RPC.ExportFailed => GOTO CantExportFeep]
EXITS
CantExportFeep => {
VoiceUtils.Problem[
"Couldn't export LarkFeep from TrunkSmarts, continuing"]; RETURN;};
};
RegisterTrunk.nb~$success
RegisterTrunk.RegisterLocal.nb~~$noIdentSupplied -- interface error
RegisterTrunk.RegisterLocal.nb~~$couldntAuthenticate -- communicationish failure
RegisterTrunk.RegisterLocal.nb~~$couldntConnect -- communicationish failure
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[r: REF] = { -- r is the EventSpec
ENABLE UNWIND=>NULL;
eventSpec: ThSmartsPrivate.InputEventSpec = NARROW[r];
smartsInfo: SmartsInfo = eventSpec.smartsInfo;
sEvent: Lark.StatusEvent ← eventSpec.sEvent;
IF smartsInfo.failed THEN RETURN;
SELECT sEvent.device FROM
ringDetect => SELECT sEvent.event FROM
Lark.enabled => CmdRinging[smartsInfo];
Lark.disabled => CmdStopRinging[smartsInfo];
ENDCASE => ERROR;
ENDCASE;
};
ringProblem: ROPE = "Problem responding to phone line ringing";
ringProblemFR: ROPE = "Problem responding to phone line ringing %g";
CmdRinging: INTERNAL PROC[info: SmartsInfo] = {
partyID: PartyID; -- self
calledPartyRname: ROPE;
calledPartyID: PartyID;
cDesc: ConvDesc;
convEvent: Thrush.ConvEvent;
nb: Thrush.NB; {
IF ThSmartsPrivate.GetSIC[info] # $idle THEN RETURN;
Not a valid time for this call. Ignore. Host system will handle.
cDesc ← ThSmartsPrivate.GetConv[info, [], TRUE];
partyID ← cDesc.situation.self.partyID; -- self
[nb, calledPartyRname] ←
ThParty.DescribeParty[partyID: partyID, nameReq: $owner];
IF nb # $success OR calledPartyRname=NIL THEN GOTO LarkFailed;
[nb, calledPartyID] ← ThParty.GetParty[partyID: partyID, rName: calledPartyRname];
SELECT nb FROM
$success => NULL;
$noSuchParty2 => GOTO LarkFailed; -- Trunk with no Lark is a fatal error
$voiceTerminalBusy, $convStillActive => GOTO Failed; -- Can't ring in right now.
ENDCASE => GOTO LarkFailed; -- None of the remaining errors are healthy
[nb, convEvent] ← ThParty.CreateConversation[
credentials: cDesc.situation.self,
state: $initiating,
checkConflict: pd.checkConflictAtConvCreate
];
SELECT nb FROM
$success => NULL;
$voiceTerminalBusy => GOTO Failed; -- Temporary problem
ENDCASE => GOTO LarkFailed; -- All others are fatal
NoteNewTkState[cDesc, convEvent];
nb←ThParty.Alert[credentials: cDesc.situation.self, calledPartyID: calledPartyID];
By here, we have to kill the conversation before forgetting it, unless the error is fatal
SELECT nb FROM
$success => NULL;
$noSuchParty2, $voiceTerminalBusy => GOTO ZapAndFail; -- Bitch a bit and give up
$noSuchSmarts, $noSuchParty, $noSuchConv => GOTO LarkFailed;
ENDCASE => ERROR; -- Fatally confused.
EXITS
LarkFailed => {
ThSmartsPrivate.ForgetConv[cDesc];
ThSmartsPrivate.Fail[info.larkInfo, ringProblem, TRUE];
};
Failed => {
VoiceUtils.ReportFR[ringProblemFR, $System, NIL, atom[nb]];
ThSmartsPrivate.ForgetConv[cDesc];
};
ZapAndFail => {
VoiceUtils.ProblemFR[ringProblemFR, $System, NIL, atom[nb]];
CmdStopRinging[info];
};
}};
CmdStopRinging: INTERNAL PROC[info: SmartsInfo] = {
cDesc: ConvDesc=ThSmartsPrivate.GetConvDesc[info];
IF cDesc=NIL THEN RETURN;
SELECT cDesc.situation.self.state FROM
$initiating, $ringback => NULL; -- ringing stopped before called party answered.
ENDCASE => RETURN; -- already active or something; ignore end of ringing
[] ← ThSmartsPrivate.ChangeState[cDesc, $idle, $terminating];
ChangeState will give best-efforts, complaining in the system log and/or failing the hardware when there are serious problems.
};
LarkTrunkProgress: ENTRY PROC[
interface: ThSmartsRpcControl.InterfaceRecord,
shh: Thrush.SHHH,
convEvent: Thrush.ConvEvent
] = {
ENABLE UNWIND => NULL; -- RestoreInvariant;
Some party has changed state in a conversation we know about.
convID: Thrush.ConversationID = convEvent.self.convID;
info: SmartsInfo = ThSmartsPrivate.GetSmartsInfo[smartsID: convEvent.self.smartsID];
cDesc: ConvDesc;
whatNeedsDoing: WhatNeedsDoing;
IF info=NIL THEN { Problem["No Smarts at TrunkProgress for SmartsID %g", NIL, card[convEvent.self.smartsID]]; RETURN; };
cDesc ← ThSmartsPrivate.GetConv[ info, convEvent.self, TRUE ];
IF convEvent.self.partyID = convEvent.other.partyID THEN { -- own state changed
NoteNewTkState[cDesc, convEvent];
IF ThSmartsPrivate.HardwareInUse[cDesc] THEN
DoRejectCall[cDesc, convEvent];
We have to reject this, since we're already actively dealing with another conv. We are (still) willing to seriously consider only one conversation at a time on the trunk side.
RETURN;
};
Someone else's state changed in a conv. we're interested in. See if it means anything to us.
whatNeedsDoing ← whatNeedsDoingIf[cDesc.situation.self.state][convEvent.other.state];
SELECT whatNeedsDoing FROM
$noop, $ntiy => NULL; -- No action is needed, or we don't know what one to take.
$imp => ERROR; -- This is supposed to be impossible!
$xrep => Problem["Didn't expect state change report", info];
$frgt => ThSmartsPrivate.ForgetConv[cDesc];
$invl => {
Problem["Invalid state transition", info];
[]←ThSmartsPrivate.ChangeState[cDesc: cDesc, state: $idle,
reason: $error, comment: "System Error: Invalid state transition"];
};
$idle => {
nb2: Thrush.NB; cInfo: ThParty.ConversationInfo;
[nb2, cInfo] ← ThParty.GetConversationInfo[convID: convEvent.self.convID];
SELECT nb2 FROM
$success => NULL;
$noSuchConv => {
Problem["Conversation disappeared, forget the whole thing", info];
ThSmartsPrivate.ForgetConv[cDesc];
};
ENDCASE => ERROR;
IF (cInfo.numParties-cInfo.numIdle) <= 1 THEN
[]←ThSmartsPrivate.ChangeState[cDesc, $idle, $terminating];
};
$actv => [] ← ThSmartsPrivate.ChangeState[cDesc: cDesc, state: $active, bilateral: TRUE];
$rbak => [] ← ThSmartsPrivate.ChangeState[cDesc: cDesc, state: $ringback];
$reac => { -- we're active and the other party goes active
larkInfo: ThSmartsPrivate.LarkInfo ← cDesc.info.larkInfo;
SetLarkTrunkState[cDesc, larkInfo.larkState,
LIST[SetupConnection[cDesc, larkInfo]]];
};
$deac => NULL;
ENDCASE => ERROR;
};
LarkTrunkSubstitution: ENTRY PROC[
interface: ThSmartsRpcControl.InterfaceRecord,
shh: Thrush.SHHH,
convEvent: Thrush.ConvEvent,
oldPartyID: Thrush.PartyID,
newPartyID: Thrush.PartyID
] = {
ENABLE UNWIND => NULL; -- RestoreInvariant;
A poacher has substituted for a poachee, or the other way around. Update state so that if it's us, we know who we are. I wonder how well this works.
info: SmartsInfo = ThSmartsPrivate.GetSmartsInfo[smartsID: convEvent.self.smartsID];
cDesc: ConvDesc;
IF info=NIL THEN { Problem["No Smarts at TrunkSubstitution for SmartsID %g", NIL, card[convEvent.self.smartsID]]; RETURN; };
cDesc ← ThSmartsPrivate.GetConv[ info, convEvent.self ];
IF cDesc=NIL THEN RETURN;
NoteNewTkState[cDesc, convEvent];
};
LarkTrunkCheckIn: ENTRY PROC[
interface: ThSmartsRpcControl.InterfaceRecord,
shh: Thrush.SHHH,
credentials: Thrush.Credentials,
reason: Thrush.Reason,
nextScheduledCheck: INTLAST[INT]
] = {
ENABLE UNWIND => NULL;
The corresponding LarkSmarts version will take care of everything.
};
QdNotification: ENTRY PROC [r: REF] ~ {
ENABLE UNWIND => NULL; -- RestoreInvariant;
cDesc: ConvDesc = NARROW[r];
IF cDesc=NIL OR cDesc.situation.self.state # $notified THEN RETURN;
[] ← ThSmartsPrivate.ChangeState[cDesc: cDesc, state: $active, bilateral: TRUE];
};
QdIdle: ENTRY PROC [r: REF] ~ {
Idle after unstable-state timeout
ENABLE UNWIND => NULL; -- RestoreInvariant;
cDesc: ConvDesc = NARROW[r];
IF cDesc=NIL OR cDesc.situation.self.state = $idle THEN RETURN;
[] ← ThSmartsPrivate.ChangeState[cDesc: cDesc, state: $idle, reason: $noAnswer];
};
DoRejectCall: INTERNAL PROC[cDesc: ConvDesc, convEvent: Thrush.ConvEvent] = {
[]←ThSmartsPrivate.ChangeState[cDesc, $idle, $busy];
};
LimitSignalling: ENTRY PROC [cDesc: ConvDesc, unstableState: StateInConv] ~ TRUSTED {
We have entered an unstable state, which should not be allowed to persist too long before we hang up and free our resources. "Too long" is determined by a pd.timeoutUnstableState.
c: CONDITION;
Process.SetTimeout[@c, Process.SecondsToTicks[pd.timeoutUnstableState]];
WAIT c;
SELECT cDesc.situation.self.state FROM
# unstableState => RETURN; -- we made it.
$initiating => NULL; -- inherently unstable
$active => {
nb: Thrush.NB; cInfo: ThParty.ConversationInfo;
[nb, cInfo] ← ThParty.GetConversationInfo[convID: cDesc.situation.self.convID];
IF nb#$success --we got problems!-- OR cInfo.numActive>1 THEN RETURN;
};
ENDCASE => RETURN;
cDesc.info.requests.QueueClientAction[QdIdle, cDesc];
};
NoteNewTkState: INTERNAL PROC[cDesc: ConvDesc, convEvent: Thrush.ConvEvent] ~ {
OPEN now: cDesc.situation.self;
nb: Thrush.NB;
larkStateData: LIST OF REF ANYNIL;
state: StateInConv ← convEvent.self.state;
previousState: StateInConv = cDesc.situation.self.state;
info: ThSmartsPrivate.SmartsInfo = cDesc.info;
larkInfo: ThSmartsPrivate.LarkInfo = info.larkInfo;
larkState: ThSmartsPrivate.LarkState ← $none;
cDesc.situation ← convEvent^;
IF state = previousState THEN RETURN; -- No conceivable value in acting.
SELECT state FROM
$notified => info.requests.QueueClientAction[QdNotification, cDesc];
$initiating => TRUSTED {Process.Detach[FORK LimitSignalling[cDesc, state]]; };
$idle => {
IF larkInfo.forwardedCall THEN larkState ← $idle;
ThSmartsPrivate.ForgetConv[cDesc];
};
$neverWas => ThSmartsPrivate.ForgetConv[cDesc];
$active => {
phoneNumber: ROPE;
connectionSpec: ThParty.PartyInfo ← SetupConnection[cDesc, larkInfo];
[nb, phoneNumber] ← ThParty.DescribeParty[partyID: now.partyID, nameReq: $address];
larkState ←
IF nb # $success OR phoneNumber=NIL OR connectionSpec.conversationInfo.originator=now.partyID THEN trunkTalking ELSE trunkSignalling;
larkStateData ← LIST[
connectionSpec,
NEW[Atom.DottedPairNode ← [$phoneNumber, phoneNumber]]];
TRUSTED {Process.Detach[FORK LimitSignalling[cDesc, state]];};
};
ENDCASE;
IF state > $idle AND cDesc.keyTable=NIL THEN {
[nb, cDesc.keyTable] ← ThParty.GetKeyTable[credentials: now];
IF nb # $success THEN cDesc.keyTable←NIL;
};
SetLarkTrunkState[cDesc, larkState, larkStateData];
};
SetLarkTrunkState: INTERNAL PROC[
cDesc: ConvDesc, larkState: ThSmartsPrivate.LarkState, larkStateData: LIST OF REF ANY ] = {
state: StateInConv ← cDesc.situation.self.state;
larkInfo: ThSmartsPrivate.LarkInfo ← cDesc.info.larkInfo;
IF ThSmartsPrivate.HardwareInUse[cDesc] THEN {
IF ThSmartsPrivate.HardwareRequired[cDesc] THEN
Problem["Conflicting use of Lark at SetLarkTrunkState: ", cDesc.info];
RETURN;
};
IF ThSmartsPrivate.HardwareRequired[cDesc] AND cDesc.keyTable#cDesc.info.larkInfo.keyTable AND cDesc.keyTable#NIL THEN
larkStateData ← CONS[cDesc.keyTable, larkStateData];
IF larkState = $none THEN larkState ← cDesc.info.larkInfo.larkState;
ThSmartsPrivate.EnterLarkState[cDesc.info.larkInfo, larkState, larkStateData];
};
SetupConnection: INTERNAL PROC[cDesc: ConvDesc, larkInfo: ThSmartsPrivate.LarkInfo]
RETURNS[connectionSpec: ThParty.PartyInfo] = {
forwardedCall: BOOLFALSE;
connectionSpec ← ThSmartsPrivate.ComputeConnection[cDesc];
IF connectionSpec=NIL THEN ERROR;
forwardedCall ←
connectionSpec.conversationInfo.numParties>1 AND
connectionSpec[0].socket.host # connectionSpec[1].socket.host;
larkInfo.forwardedCall ← forwardedCall;
};
Feep: PUBLIC ENTRY PROCEDURE [ -- for local instance
shhh: Thrush.SHHH, serviceID: Thrush.SmartsID, convID: Thrush.ConversationID, requestingParty: Thrush.PartyID,
number: Rope.ROPE, noisy: BOOLEANFALSE, on, off: CARDINAL← 177777B]
RETURNS [nb: Thrush.NB←$success] = {
RETURN[FeepOp[NIL, shhh, serviceID, convID, requestingParty, number, noisy, on, off]];
};
FeepOp: INTERNAL PROCEDURE [ -- Exports to voice service interface LarkFeep
interface: LarkFeepRpcControl.InterfaceRecord,
shhh: Thrush.SHHH, serviceID: Thrush.SmartsID, convID: Thrush.ConversationID, requestingParty: Thrush.PartyID,
number: Rope.ROPE, noisy: BOOLEANFALSE, on, off: CARDINAL← 177777B]
RETURNS [nb: Thrush.NB←$success] = {
info: SmartsInfo = ThSmartsPrivate.GetSmartsInfo[smartsID: serviceID];
cDesc: ConvDesc;
IF info = NIL THEN RETURN[$noSuchSmarts];
cDesc ← ThSmartsPrivate.GetConv[info, [convID: convID]];
IF cDesc = NIL THEN RETURN[$notInConv];
IF cDesc.situation.self.state # $active THEN RETURN[$convNotActive];
IF noisy THEN number ← IO.PutFR["A%g", rope[number]];
IF on#177777B THEN number ← IO.PutFR["%gO%g", char[LOOPHOLE[on/10]], rope[number]];
IF off#177777B THEN number ← IO.PutFR["%gF%g", char[LOOPHOLE[off/10]], rope[number]];
ThSmartsPrivate.QueueFeeps[info, number];
};
Feep.nb~$success
Feep.nb~$noSuchSmarts -- really no such SmartsInfo
Feep.nb~$notInConv -- really no cDesc
Feep.nb~$convNotActive
FlashInfo: TYPE = REF FlashInfoBody;
FlashInfoBody: TYPE = RECORD [
requestingPartyID: Thrush.PartyID,
actionID: Thrush.ActionID
];
Flash: PUBLIC ENTRY PROCEDURE [ -- Exports to voice service interface LarkFeep
shhh: Thrush.SHHH, serviceID: Thrush.SmartsID, convID: Thrush.ConversationID, requestingParty: Thrush.PartyID, actionID: Thrush.ActionID,
extraTime: CARDINAL← 177777B]
RETURNS [nb: Thrush.NB←$success] = {
RETURN[FlashOp[NIL, shhh, serviceID, convID, requestingParty, extraTime]];
};
FlashOp: INTERNAL PROCEDURE [ -- Exports to voice service interface LarkFeep
interface: LarkFeepRpcControl.InterfaceRecord,
shhh: Thrush.SHHH, serviceID: Thrush.SmartsID, convID: Thrush.ConversationID, requestingParty: Thrush.PartyID, actionID: Thrush.ActionID,
extraTime: CARDINAL← 177777B]
RETURNS [nb: Thrush.NB←$success] = {
info: SmartsInfo = ThSmartsPrivate.GetSmartsInfo[smartsID: serviceID];
cDesc: ConvDesc;
IF info = NIL THEN RETURN[$noSuchSmarts];
cDesc ← ThSmartsPrivate.GetConv[info, [convID: convID]];
IF cDesc = NIL THEN RETURN[$notInConv];
IF cDesc.situation.self.state # $active THEN RETURN[$convNotActive];
info.larkInfo.props ← Atom.PutPropOnList[info.larkInfo.props, $FlashInfo,
NEW[FlashInfoBody ← [requestingParty, actionID]]];
SetLarkTrunkState[cDesc, $trunkFlashing, NIL];
};
Flash.nb~$success
Flash.nb~$noSuchSmarts -- really no such SmartsInfo
Flash.nb~$notInConv -- really no cDesc
Flash.nb~$convNotActive
FlashDone: PUBLIC ENTRY PROC[r: REF] = { -- r is a LarkInfo; flashing has finished
nb: Thrush.NB;
info: ThSmartsPrivate.LarkInfo = NARROW[r];
smartsInfo: SmartsInfo = IF info=NIL THEN NIL ELSE info.larkTrunkSmartsInfo;
cDesc: ConvDesc = ThSmartsPrivate.GetConvDesc[smartsInfo];
flashInfo: FlashInfo ← IF info=NIL THEN NIL
ELSE NARROW[Atom.GetPropFromList[info.props, $FlashInfo]];
IF flashInfo=NIL OR flashInfo.actionID=0 THEN RETURN;
nb ← ThParty.ReportAction[
report: [
self: cDesc.situation.self, -- placeholder
other: cDesc.situation.self,
requestingParty: flashInfo.requestingPartyID,
actionID: flashInfo.actionID,
actionClass: $switchhookFlash,
actionType: $flashed
],
reportToAll: FALSE
].nb;
IF nb#$success THEN
IF info#NIL AND ~info.failed THEN ThSmartsPrivate.Fail[info, IO.PutFR["Couldn't report switchhook flash completion -- %g", atom[nb]], TRUE]
ELSE VoiceUtils.ProblemFR["Couldn't report switchhook flash completion, probably due to earlier failure -- %g", $System, NIL, atom[nb]];
};
LarkTrunkSmartsInit: Commander.CommandProc = {
larkFeepSpec.interfaceName.instance ← NIL;
larkFeepSpec.hostHint ← Thrush.noAddress;
larkFeepUser ← VoiceUtils.MakeRName[style: rName, name:
VoiceUtils.CmdOrToken[cmd: cmd, key: "ThrushServerInstance", default: "Strowger.Lark"]];
larkFeepPassword ← VoiceUtils.CurrentPasskey[VoiceUtils.CmdOrToken[
cmd: cmd, key: "ThrushServerPassword", default: "MFLFLX"]];
VoiceUtils.ReportFR["Initialize[LarkTrunkSmarts.Lark, %s]", $System, NIL, rope[larkFeepUser]];
};
WhatNeedsDoing: TYPE = ATOM; -- {
Just codes to dispatch on in Supervisor; explained there
$noop, $idle, $actv, $frgt, $reac, $deac, -- cases explained in code
$rbak, -- enter "ringback"; stable initiating state.
$invl, -- considered an invalid request
$xrep, -- we got a report we feel we shouldn't have got
$ntiy, -- not implemented yet
$imp -- this situation should not arise even in the face of invalid requests
};
whatNeedsDoingIf: ARRAY StateInConv OF ARRAY StateInConv OF WhatNeedsDoing ← [
If we're in the state identified by the row, and someone else in the conversation reports a transition onto the state identified by the column, what should we do?
never idle failed resrv pars init notif rback ring canAc activ inact -- ← (other)
[ $imp, $frgt, $frgt, $frgt, $frgt, $frgt, $frgt, $frgt, $frgt, $frgt, $frgt, $frgt], --neverWas
(Clip this table to view without these comments.)
This situation arises when we've forgotten about the conversation that somebody else is still reporting on.
[ $imp, $noop, $frgt, $noop, $noop, $noop, $noop, $noop, $noop, $noop, $noop, $ntiy ], -- idle
[ $imp, $imp, $frgt, $imp, $imp, $imp, $imp, $imp, $imp, $imp, $imp, $imp ], -- failed
[ $imp, $imp, $frgt, $imp, $imp, $imp, $imp, $imp, $imp, $imp, $imp, $imp ], -- reserved
The actions of other parties are not of interest to us yet, since they're not in this conv.
[ $imp, $imp, $frgt, $imp, $imp, $imp, $imp, $imp, $imp, $imp, $imp, $imp ], -- parsing
Ditto.
[ $imp, $idle,$frgt, $invl, $invl, $invl, $xrep, $xrep, $rbak,$actv, $actv, $actv ], -- initiating
They can either enter ringing to indicate interest, or go active without ringing
If they enter ringing, we ignore it: initiating is as good as ringback for trunks.
[ $imp, $idle,$frgt, $invl, $invl, $invl, $xrep, $noop, $noop, $ntiy, $noop, $ntiy ], -- notified
We don't expect to hear from others while we're deciding whether to play
[ $imp, $idle,$frgt, $invl, $invl, $invl, $xrep, $xrep, $noop,$actv, $actv, $actv ], -- ringback
They have earlier expressed interest noopringing), and are now joining the fray
[ $imp, $idle,$frgt, $invl, $invl, $invl, $invl, $invl, $invl, $invl, $invl, $invl ], -- ringing
The only thing that interests us here is everybody else quitting.
[ $imp, $idle,$frgt, $invl, $invl, $invl, $xrep, $xrep, $noop, $ntiy, $noop, $ntiy ], -- canActivate
[ $imp, $idle,$frgt, $invl, $invl, $invl, $xrep, $xrep, $noop, $ntiy, $reac, $deac ], -- active
[ $imp, $idle,$frgt, $invl, $invl, $invl, $xrep, $xrep, $noop, $ntiy, $ntiy, $ntiy ] -- inactive (current ^)
];
Debugging nonsense
ViewCmd: Commander.CommandProc = TRUSTED {
Nice.View[pd, "Lark Trunk PD"];
};
Commander.Register["LarkTrunkSmarts", LarkTrunkSmartsInit, "LarkTrunkSmarts <ExportInstance[Strowger]> <ServerPassword[...]>\nInitialize and Export LarkTrunkSmarts"];
Commander.Register["VuLarkTrunkSmarts", ViewCmd, "Program Management variables for Lark Trunk Smarts"];
}.
Swinehart, October 28, 1985 10:01:46 am PST
Log => VoiceUtils, Handle => ID
changes to: DIRECTORY, LarkTrunkSmartsImpl, Reseal, PartyID, nullID, nullConvID, RegisterTrunk, LarkTrunkParseEvent, CmdCall, TrunkSupervise
Swinehart, May 17, 1986 5:37:22 pm PDT
Cedar 6.1
changes to: RegisterTrunk
Swinehart, June 1, 1986 7:42:22 pm PDT
Eliminate what's considered to be spurious MBQueueing. There is more to go.
changes to: RegisterTrunk, LarkTrunkProgress, LarkTrunkSubstitution, LarkTrunkReportAction, QdNotification
Swinehart, June 11, 1986 7:42:07 am PDT
Use LarkReportAction from LarkSmartsSupImpl to record new key tables
changes to: DIRECTORY, RegisterTrunk, LarkTrunkSubstitution
Swinehart, December 29, 1986 3:45:45 pm PST
Remove obsolete $voiceTerminalBusy tests.
changes to: CmdRinging, NoteNewTkState
Swinehart, January 1, 1987 9:44:36 pm PST
Continuing FD/BD resolution stuff.
changes to: CmdRinging, NoteNewTkState
Swinehart, January 24, 1987 5:32:09 pm PST
General error-management review and repair, esp. NB-handling
changes to: LarkTrunkParseEvent, ringProblemFR, CmdRinging, CmdStopRinging, }, LarkTrunkProgress, LarkTrunkSubstitution, QdNotification, NoteNewTkState, Feep
Swinehart, June 11, 1987 11:56:49 am PDT
Changes to ThSmartsPrivate.Getconv
changes to: LarkTrunkProgress, LarkTrunkSubstitution