Notes.txt
Swinehart, March 14, 1986 6:59:22 am PST
FinchTool Algorithms — new, paraphrased
Current assumption, subject to change as we develop the notion of multiple calls: Only one call is allowed, by Lark, to reach interesting states (>=failed, +notified). We can select any call that does go beyond an interesting state, to indicate the "current" call; all actions that deal with ongoing call will refer to that one. We will not, for the moment, allow any manual selection of conversation-log entries. When there isn't an entry, it's OK to place calls without hanging up first. So Lark and FinchTool/Directory know about the one-call philosophy; FinchSmarts does not, at present.
PhoneProc:
DoPhone[primarySelection, F[mouse button]];
CalledPartyProc:
DoPhone[contents of called party field, F[mouse button]];
CallingPartyProc:
DoPhone[contents of calling party field, F[mouse button]];
PhoneCmd:
DoPhone[rest of command line, FALSE];
RedialCmd:
DoPhone[contents of called party field, FALSE];
DoPhone[calledPartyText, wantResidence (T/F)]:
Return silently if Finch can't be made running or calledPartyText is NIL
[callee, newCalledPartyText, wantResidence] ← ParseCallee[calledPartyText, wantResidence]
Type "Placing call to <newCalledPartyText>.
Update calledParty field in viewer with newCalledPartyText
HangItUp[F, T] if already talking and pause the indicated number of milliseconds
FinchTool.CallByDescription[callee, residence] -- in FinchDirectory
DirectoryInstance: REF RECORD [rName, name, number, homeNumber, remarks, office/home ];
In other words, everything extracted from the directory, or a simulation of same
DirectoryButtonNotifier: select entry and schedule DirectoryButton[selected button].
DirectoryButton[button]: CallNumber[FillDirInst[button], instance.where]
CallByDescription[description, residence(home/office)]:
This is the route in for non-directory-originated calls.
(If it's alphabetic, rName, else number) ← description;
if rName, search any open directories via FindNamedInstance for first match on rName.
if instance found, CallNumber[instance, residence], complaining if residence and not found
else FinchSmarts.PlaceCall[rName, FeepValue[number]]
CallNumber[instance, home/office]:
Call based on instance, filled-in from caller information or extracted from directory
name ← instance.rName else instance.name.
number ← instance.(homeNumber or number), depending on instance.where.
Complain about insufficient information (home and no home number)
Type "Placing call to <name> (<number>)"
set finch tool called party text to <name>[||" at home"]
FinchSmarts.PlaceCall[name, FeepValue[number], useNumber: where=home]
FinchSmarts should accept directory instances, filling them in with additional information. For incoming calls, instance would be constructed from information about the call. FinchTool should call on directory package for directory instances, as a straight client of a package that might have Loganberry under it or might have the current TDIR. But the interfaces will have to be extended to match Doug's needs. (Worried about formatting hit of using Loganberry, and also performance/convenience)
For now, will retain above fractured logic and adapt to FinchSmarts as needed.
ReportConversationState[nb, cDesc, remark ]:
Type remark and quit if nb#$success, quit (with a complaint) if there's no cDesc.
If (button�sc.clientData)=NIL THEN button ← AddConvDesc[cDesc].
Builds a button and assigns the cDesc to it.
IF ~cDesc.originatorRecorded THEN -- find out who originated and make the best party description possible
If Finch originated, called-party is already set, and is better than we can get otherwise. Otherwise extract up the calling party for incoming calls, or called-party for calls initiated from the telset. originatorRecorded is FALSE until set TRUE by FinchSmarts Connect code or SetContents below.
SetContents[cDesc, finchToolHandle.(SELECT cDesc.whoOriginated FROM
unknown => don't do it,
us => calledPartyText,
them => callingPartyText)]
Deal with twiddling of icon.
If not reserved or parsing, build a "Call to/from x at time t" call status line.
If idle OR failed THEN -- Elaborate.
Add to status line "<whether the call completed><why it ended>, duration = <duration>".
else add to status line a a human-sensible version of the current state.
Add " (<cDesc event's comment>) " and " [<remark>] ", if they're non-nil
Put the status line in the current button, unless cDesc.reportComplete
IF failed OR idle THEN cDesc.reportComplete←TRUE;
SelectEntryInConversations[button, state >= reallyInConv, but not notified];
SetContents[viewer, cDesc]:
cDesc.otherPartyDesc can be of the form "xxx.pa (nnnn)", which confuses Phone commands.
Set viewer to RepairIntelnet[cDesc.otherPartyDesc], with any trailing (...) stripped.
cDesc.originatorRecorded ← TRUE;
RepairIntelnet[num] RETURNS[number]:
cDesc.otherPartyDesc can contain Intelnet pause characters and authentication code.
Intelnet pause characters are all characters < '\040
Translate pause => "*" remove auth-code.
FinchTool Algorithms — old style, paraphrased
PhoneProc:
DoPhone[primarySelection, F[mouse button]];
CalledPartyProc:
DoPhone[contents of called party field, F[mouse button]];
CallingPartyProc:
DoPhone[contents of calling party field, F[mouse button]];
PhoneCmd:
DoPhone[rest of command line, FALSE];
RedialCmd:
DoPhone[contents of called party field, FALSE];
DoPhone[calledPartyText, wantResidence (T/F)]:
Return silently if Finch not running or calledPartyText is NIL
[callee, newCalledPartyText, residence (T/F)] ← ParseCallee[calledPartyText]
newCalledPartyText ← newCalledPartyText||" at home" if still need to indicate residence.
Type "Placing call to <newCalledPartyText>.
Update calledParty field in viewer with newCalledPartyText
HangItUp[F, T] if already talking and pause the indicated number of milliseconds
FinchTool.CallByDescription[callee, residence] -- in FinchDirectory
DirectoryInstance: REF RECORD [rName, name, number, homeNumber, remarks, office/home ];
In other words, everything extracted from the directory, or a simulation of same
DirectoryButtonNotifier: select entry and schedule DirectoryButton[selected button].
DirectoryButton[button]: CallNumber[FillDirInst[button], instance.where]
CallByDescription[description, residence(home/office)]:
This is the route in for non-directory-originated calls.
(If it's alphabetic, rName, else number) ← description;
if rName, search any open directories via FindNamedInstance for first match on rName.
if instance found, CallNumber[instance, residence], complaining if residence and not found
else FinchSmarts.PlaceCall[rName, FeepValue[number]]
CallNumber[instance, home/office]:
Call based on instance, filled-in from caller information or extracted from directory
name ← instance.rName else instance.name.
number ← instance.(homeNumber or number), depending on instance.where.
Complain about insufficient information (home and no home number)
Type "Placing call to <name> (<number>)"
set finch tool called party text to <name>[||" at home"]
FinchSmarts.PlaceCall[name, FeepValue[number], useNumber: where=home]
FinchSmarts should accept directory instances, filling them in with additional information. For incoming calls, instance would be constructed from information about the call. FinchTool should call on directory package for directory instances, as a straight client of a package that might have Loganberry under it or might have the current TDIR. But the interfaces will have to be extended to match Doug's needs. (Worried about formatting hit of using Loganberry, and also performance/convenience)
For now, will retain above fractured logic and adapt to FinchSmarts as needed.
ReportConversationState[nb, cDesc, remark ]:
Type remark and quit if nb#$success, quit quietly if there's no cDesc.
If (button�sc.clientData)=NIL THEN AddConvDesc[cDesc].
Builds a button and assigns the cDesc to it.
SELECT state FROM ENDCASE => cDesc.failed←FALSE.
reserved, parsing =>
cDesc.whoOriginated ← us;
cDesc.conference ← FALSE;
SELECT cDesc.cState.reason FROM busy, error, noCircuits => difficulty←TRUE;
IF ~cDesc.weOriginated THEN -- find out who originated and make the best party description possible
If Finch originated, called-party is already set, and is better than we can get otherwise. Otherwise extract up the calling party for incoming calls, or called-party for calls initiated from the telset. weOriginated is assumed FALSE, set TRUE by FinchSmarts when initiating, and in SetContents below.
SetContents[cDesc, finchToolHandle.(SELECT state FROM ENDCASE => don't do it,
initiating, maybe => calledPartyText,
pending, ringing, active => callingPartyText)]
Deal with twiddling of icon.
If not reserved or parsing, or if difficulty, build a "Call to/from x at time t" call status line.
If idle OR failed THEN -- Elaborate.
Add to status line "<whether the call completed><why it ended>, duration = <duration>".
else add to status line a a human-sensible version of the current state.
Add " (<cDesc event's comment>) " and " [<remark>] ", if they're non-nil
Put the status line in the current button, unless cDesc.reportComplete
IF failed OR idle THEN cDesc.reportComplete←TRUE;
SelectEntryInConversations[button, state#idle];
SetContents[viewer, cDesc]:
cDesc.otherPartyDesc can be of the form "xxx.pa (nnnn)", which confuses Phone commands.
Set viewer to RepairIntelnet[cDesc.otherPartyDesc], with any trailing (...) stripped.
cDesc.weOriginated ← TRUE;
RepairIntelnet[num] RETURNS[number]:
cDesc.otherPartyDesc can contain Intelnet pause characters and authentication code.
Intelnet pause characters are all characters < '\040
Translate pause => "*" remove auth-code.
FinchSmarts Algorithms — old style, paraphrased
Progress
Progress[smartsID, event, yourParty, latestEvent] => [ disposition←pass ]:
cDesc ← GetConv[event.credentials.convID, FALSE];
If repeated event, based on stateID, ignore it.
Store credentials from report, also keyTable, intervalSpecs, proseSpecs, telephone number info if present.
Do the rest only if yourParty (rather than a report by someone else)
Store additional credentials from event, including state; remember comments, urgency, alertKind if explicitly presented.
Call on ThParty to describe the other party; also record in cDesc whether we or "they" originated the call; only "conference" is known if there are more than two parties [we can do better now]
Wake up Supervisor process (will only see last event if there were several, though)
};
Supervise[finchInfo]:
DO whenever apprised, by client or Progress code:
For all conversations presently known about, do
cDesc ← next conversation;
If the conv. we're interested in has gone idle, we're not interested any more.
If we've entered an interesting state in a conv., our Lark has decided to be interested in that one; record which one it is. Also keep track of the "ultimate" interesting state reached in this conversation, for use by clients once the conv. is idle.
select (trans←transForStates[stateNow][cDesc.desiredState]) from
transForStates indicates what to do to obtain a desired state, based on current one.
noop => NULL; -- Often nothing to do
elim =>
Our conv. has gone away. Clean up prose/interval lists.
alrt =>
We've been asked by FinchSmarts client to place a call.
IF NOT ours THEN Problem["What to do in a race?"]; -- ?? --
cDesc.weOriginated ← TRUE.
conv ← Alert the other party.
if ok, set interested conv ← conv.
idle, actv => We've been asked to hang up or activate this call (answer). Do it.
spvs =>
We're active already and something happened. Maybe an interval or prose spec.
Handling of all this must be rethought later. Not included now.
invl, ntiy =>
We can't go into the required state from the present state, or transition isn't implemented yet
Complain, then schedule to go idle.
What happens when transition to idle is considered invalid?
Analyze any results of trying to reach a different state.
SELECT nb FROM -- nb denotes success of transition attempt, $success if none made.
$success, $stateMismatch => NULL; -- $stateMismatch will cause retry
$noSuchParty2, $narcissism =>
Complain[$notFound, <appropriate remark>];
$invalidTransition, convNotActive, $convStillActive, $notInConv, $noSuchConv =>
First clear out currently-interesting conv in last two instances.
Complain[$error, comment];
$noSuchParty, noSuchSmarts =>
Fail, deregister, all that, reporting the problem in the process.
problem ← "NoSuchParty or NoSuchSmarts reported, must try to go away";
Set up switching and tones to match state.
IF newEvent THEN info.ReportConversationState[success, cDesc, NIL];
After reporting state of conversation (changeNoted set), there is no further use for finished record or playback intervals; delete the leading ones (should be all of the finished ones!) Same sort of thing for prose.
Also, if this cDesc describes an idle conversation, forget about it.
If we don't know about any conversations, quit: will be recreated next time;
DisconnectCall[convID, reason, comment]: -- convID not used!
Ignore if Finch not on, complain if not presently interested. (similarly in remaining procs)
Otherwise, Go idle with reason, reason ← busy if terminated during ringing or pending.
AnswerCall[convID]:
Advance present-interest conversation to active, whatever the convID.
PlaceCall[convID, rName, number, urgency, useNumber]:
Get calledPartyID: GetParty or GetPartyFromNumber depending on useNumber, looking in White pages and using number if not found as Etherphone [no longer necessary]
Complain if none found (useNumber and null number is **0 request)
Connect[calledPartyID, ... urgency]
Connect[calledID, rec/prose stuff, urgency, waitForActive] => cDesc:
Get new (if idle) or used (reserved) cDesc, complain if already active.
Set up some prose/bluejay stuff (not yet)
Set desired-fields in cDesc, and schedule for supervisor.
If waitForActive, then wait on progress/client supervisor apprisor n times until state is reasonable, complain if not connected after that time (fairly short.)
Complain[... cDesc: ConvDesc, reason, comment]:
cDesc.desiredState ← IF cDesc.cState.state#any THEN idle ELSE reserved;
If we were doing anything at all, this is how we used to get an error/busy signal.
cDesc.desiredReason ← reason;
cDesc.desiredComment ← comment;
And so on, including some Prose-specific stuff
Schedule for supervisor
FinchSmarts Algorithms — old style, extracted
Couldn't bring myself to throw this away! Note care about info not being there yet/still.
RepRet: PROC[bool: BOOL, nb: NB, remark: Rope.ROPE]
RETURNS[sameBool: BOOL] = {
IF bool=FALSE THEN
IF info#NIL THEN info.ReportConversationState[nb, NIL, remark]
ELSE VoiceUtils.Report[remark, $Finch];
sameBool𡤋ool;
};
These responses are still maybe reasonable ones; where should they be handled? Feels like more stuff is being foisted off onto the client, or ignored. But this was never right either.
Supervise: PUBLIC ENTRY PROC[info: FinchInfo ] = {
invl, ntiy =>
Problem["FinchSmarts: Invalid state transition request. [, or not yet implemented]"];
cDesc.desiredState ← idle;
cDesc.desiredReason ← error;
cDesc.desiredComment ← "Invalid state transition", or "Unimplemented state transition"
SELECT nb FROM
success, stateMismatch =>;
noSuchParty2 => -- Have to get to error tone here. No such party.
Request[cDesc, $idle, $notFound, "Called party not found"];
narcissism => -- Have to get to error tone here. No such party.
Request[cDesc, $notFound, "Attempt to call self rejected on philosophical grounds"];
partyNotEnabled =>
info.ReportConversationState[noSuchSmarts, cDesc, "Your Etherphone is not connected to the telephone server"];
invalidTransition, convNotActive, convStillActive =>
comment: ROPE="Party-level detected invalid state transition request";
Problem[comment];
Request[cDesc, $idle, $error, comment];
notInConv, noSuchConv => -- Complain, zap, go idle and repeat.
comment: ROPE="NotInConv or NoSuchConv";
Problem[comment];
IF ours THEN info.currentConvID ← nullConvID;
cDesc.cState.credentials.convID ← nullConvID;
cDesc.cState.credentials.stateID ← 0;
Request[cDesc, $idle, $error, comment];
noSuchParty, noSuchSmarts => { -- Complain, deregister, we gone! Needs tuning.
problem ← "NoSuchParty or NoSuchSmarts reported, must try to go away";
GOTO Failing;
Failing => UninitFinchSmarts[problem]; -- now Failed
};