ThSmartsPrivate.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Last modified by D. Swinehart, February 6, 1987 10:39:29 am PST
Polle Zellweger (PTZ) August 29, 1985 5:35:30 pm PDT
Last Edited by: Pier, May 3, 1984 1:04:49 pm PDT
DIRECTORY
Atom USING [ PropList ],
IO USING [ Value ],
Lark USING [
CommandEvents, ConnectionSpec, disabled, enabled, Event, KeyTable, LarkModel, o3i1, StatusEvent, ToneSpec, ts0, ts12 ],
LarkPlay USING [ ToneList, ToneSpec ],
LarkOpsRpcControl USING [ InterfaceRecord ],
MBQueue USING [ Queue ],
RefID USING [ nullID ],
Rope USING [ROPE],
BasicTime USING [ GMT ],
Thrush USING [
ActionID, ActionReport, ConversationID, ConvEvent, ConvEventBody, Credentials, NB, NetAddress, nullID, nullConvID, PartyID, PartyType, Reason, ROPE, SHHH, SmartsID, StateInConv ],
ThParty USING [ PartyInfo ],
ThPartyPrivate USING [ SmartsData ],
ThSmartsRpcControl USING [ InterfaceRecord ]
;
ThSmartsPrivate: CEDAR DEFINITIONS = {
Copies
SHHH: TYPE = Thrush.SHHH;
SmartsData: TYPE = ThPartyPrivate.SmartsData;
Types
States (enumerated types) and other scalars
Parser is controlled by state sequence that's largely independent of the state of the smarts in any conversation. Parser represents the user's wishes. After parsing is complete, other processing determines whether these wishes can be granted.
ParseState: TYPE = {
idle, getStr, getSeq, getFeep, getNum, inStr, inSeq, inFeep, inNum, inTossStr };
This state is essentially a superset of Thrush.StateInConv. It is used to express the user's wishes; when wish and reality coincide, it is set to the corresponding StateInConv, and quiescence follows, at least temporarily.
Control of the Lark hardware state (remaining types)
LarkState: TYPE = {
none,     -- Initial state
idle,     -- Phone not in use
talking,    -- Etherphone conversation in progress
trunkSignalling,  -- "Feeping"
trunkTalking,  -- Standard telephone conversation in progress
trunkForwarding, -- Like trunkTalking, but call is forwarded to some other Lark
trunkFlashing,  -- Implementing special switchhook flash
ringing,    -- Ringing loudly through Lark speaker
silence,    -- Dial tone has been broken, dialing in progress.
dialTone,    -- Caller hears dial tone.
ringBack,    -- Caller hears ringing.
busyTone,   -- Caller hears busy signal.
errorTone    -- Caller hears fast busy signal.
};
ProgressTones: TYPE = LarkState[dialTone..errorTone];
SwitchState: TYPE = { onhook, telset, speaker, sPEAKER, monitor, mONITOR };
Encodes the progression of states of the telset switchhook (sh) and the speakerphone switch (sw) (which can progress from off to on, from on to off, or when on can be "clicked". The state machine is approximately:
onhook => telset (sh on), speaker (sw click), sPEAKER (sw on)
telset => onhook (sh off), monitor (sw click), mONITOR (sw on)
speaker => onhook (sw click), sPEAKER (sw on), telset (sh on)
sPEAKER => onhook (sw off), mONITOR (sh on)
monitor => telset (sw click), speaker (sh off), mONITOR (sh on)
mONITOR => telset (sw off), sPEAKER (sh off)
There are some other transitions that shouldn't happen, but are treated in as harmless a way as possible when they do. Outside events can also cause SwitchState to revert to onhook, whatever the settings of the switches. In this situation, the assumption is that all switches are off; subsequent off transitions are ignored.
RingDetState: TYPE = { idle, maybe, ring, between };
no ring, glitch interval, in-a-ring, break
Orig: TYPE = { unknown, us, them };
AudioSource: TYPE = ATOM; -- = {$telset (NIL), $lineA, $lineB};
click: Lark.Event = Lark.ts0; -- Momentary closure of speaker box switch.
spkrOn: Lark.Event = Lark.ts12; -- Longer-than-momentary closure.
LSwitches: TYPE = {
xBarAll, hook, aSwitch, sideTone, ringO, revert, revertHook, led, spMode, none };
LState: TYPE = RECORD [
voiceMode: Lark.Event←Lark.o3i1,
echoStyle: [0..3]𡤀,
xbar: PACKED ARRAY[0..8) OF [0..256) ← ALL[0],
lSw: ARRAY LSwitches OF Lark.Event ← ALL[Lark.disabled]
];
Structures
Information used by LarkSmarts and LarkTrunkSmarts implementations
They require similar information, although the trunk does not use all of it.
SmartsInfo: TYPE = REF SmartsInfoBody;
SmartsInfoBody: TYPE = MONITORED RECORD
[
smartsID: Thrush.SmartsID, -- back pointer to my smarts.
otherSmartsID: Thrush.SmartsID←Thrush.nullID,
Trunk or station smarts, depending on which this is.
partyType: Thrush.PartyType←NIL, -- type of party this smarts represents.
larkInfo: LarkInfo,
State of Lark hardware, shared between Lark and Trunk
conversations: OpenConversations←NIL,
currentConvID: Thrush.ConversationID←Thrush.nullConvID, -- the one we're dealing with.
requests: MBQueue.Queue←NIL, -- serialize progress reports, requests for actions
Parsing control fields
ParseEvent: PROC[r: REF], -- r is an EventSpecBody; MBQueued
Allows Lark, Lark trunk, ... smarts to parse different command languages
Command: PROC[info: SmartsInfo, val: INT𡤀]←NIL,
Command procedure to act on results of completely parsed command
NoteNewStateP: PROC[cDesc: ConvDesc, convEvent: Thrush.ConvEvent]←NIL,
Procedure to update conversation description based on event: smarts-specific
lastTouchpadChar: Lark.Event ← '\000,
parseState: ParseState ← $idle,
Independent of any notion of what's going on in eventInfo; desire of local user
arguments: Rope.ROPENIL,
argLength: NAT𡤀,
offset: NAT𡤀, -- kludge allowing more than ten touchpad commands
haveArguments, cmdOrRecip, haveOne: BOOLEANFALSE,
failed: BOOLFALSE
];
Input actions, registrations, and failure events are serialized on an MBQueue. Here are the REF types that carry the parameterization to the queued procedures.
1. Input events
InputEventSpec: TYPE = REF InputEventSpecBody;
InputEventSpecBody: TYPE = RECORD [
smartsInfo: SmartsInfo,
sEvent: Lark.StatusEvent ← [0, nothing, '\000]
];
2. Lark Failure -- just the LarkInfo
3. Registration -- see LarkSmartsInitImpl.Register
OpenConversations: TYPE = LIST OF ConvDesc;
ConvDesc: TYPE = REF ConvDescBody;
ConvDescBody: TYPE = RECORD [
info: SmartsInfo,        -- corresponding to credentials.smartsID (or else!)
situation: Thrush.ConvEventBody,   -- .self describes current state in conv.
keyTable: Lark.KeyTable ← NIL,
ringCheckProcess: PROCESSNIL, -- For ringing after someone has answered.
lastActionID: Thrush.ActionID ← RefID.nullID -- for remembering last-started request, see LarkSmartsSupImpl.LarkReportAction
];
ConvRequest: TYPE = REF ConvRequestBody;
ConvRequestBody: TYPE = RECORD [
cDesc: ConvDesc, -- present state,
desiredSituation: Thrush.ConvEventBody ← [],
bilateral: BOOL
];
Access to the Lark hardware via the Lark interface. This monitored record is shared by the Smarts info for both the LarkSmarts and LarkTrunkSmarts. There are also some random shared fields kept here.
LarkInfo: TYPE = REF LarkInfoBody;
LarkInfoBody: TYPE = MONITORED RECORD
[
interface: LarkOpsRpcControl.InterfaceRecord, -- here's how you make calls to Lark --
shh: SHHH, -- here's what you use to encrypt them --
larkSmartsInfo: SmartsInfo,  -- back pointers
larkTrunkSmartsInfo: SmartsInfo,  -- back pointers
netAddress: Thrush.NetAddress, -- <<needed when creating socket numbers?>> --
model: Lark.LarkModel, -- what does this Lark look like? --
larkState: LarkState ← $none,  -- tone/crossbar/connect state of actual Lark hardware
failed: BOOLFALSE,  -- set when this (instance of) Lark is no longer viable.
debugging: BOOLFALSE, -- if TRUE, Lark is in development mode: reaction to failure is less drastic.
newActions: LIST OF REFNIL, -- queue of low-level requests to supervisor process
lastAction: LIST OF REFNIL, -- used in request-queue maintenance,
larkProcess: PROCESSNIL, -- maintains same
blinkProcess: PROCESSNIL, -- created if LED must flash.
stateChange: CONDITION,  -- larkProcess should notice change
inputQueue: MBQueue.Queue ← NIL,
audioSource: AudioSource ← NIL,  -- e.g., $telset
switchState: SwitchState ← $onhook, -- state of switchhook/speakerphone switch...
textToSpeech: BOOLFALSE,-- Prose 2000 or DecTalk synthesizer is available on this Lark.
transmitOnly: BOOLFALSE, -- w/line(A/B), inhibit spkr, rcvr, line(A/B) out for svcs.
Some values that have to be preserved from one larkState to another
forwardedCall: BOOLFALSE,
multicasting: BOOLFALSE,
spec: ThParty.PartyInfo←NIL,
cSpec: Lark.ConnectionSpec←NIL,
keyTable: Lark.KeyTable ← NIL,
keySynch: CONDITION, -- used to lock pieces of LarkOutImpl together while keys are distributed
keyTableDistrsRequested: INT𡤀,
keyTablesDistributed: INT ← 0,
toneSpec: LarkPlay.ToneSpec←NIL,
nextToneList: LIST OF LarkPlay.ToneList←NIL,
larkToneSpec: Lark.ToneSpec←NIL,
Click and ring detect timings
swOnTime: CARDINAL𡤀,
Lark reports the time when a ringing voltage is detected on the telewall, and the time when the voltage goes away. There can be an on-off glitch when the phone is hung up, and ringing is an alternating thing, so we need to interpret these signals before passing on to higher levels. See LarkInImpl.
ringChangeTime: CARDINAL𡤀,
ringDetState: RingDetState ← $idle,
ringDetCondition: CONDITION,
ringDetInstance: INT𡤀, -- timeout routine is relevant only if this matches
Ringing control information
When a Note in a tune specifies "notify", expectedNotification is incremented (mod the lower-case letters) and sent along to the Lark. When the Lark finishes that note, it notifies the smarts, and eventually LarkOut, using that value. It's possible (though not desired) for this notification to precede the "WAIT" that pends notification. In this case, we mustn't wait. See LarkOutImpl.DoTones and ditto.WaitQ. receivedNotification is set to a value outside the range just after each WAIT opportunity terminates.
expectedNotification: Lark.Event ← 'z,
receivedNotification: Lark.Event ← 'Y,
LarkSupervisor Variables
lastSwitchState: SwitchState ← $onhook,
lState: LState ← [lSw: ALL[Lark.enabled]],
scratchEv: Lark.CommandEvents ← NIL,
For use by LarkCall Registrants
props: Atom.PropList ← NIL,
Limited registration for handling Lark-to-Server events for peripheral services, informing them of conversation termination, . . .
See LarkInImpl, LarkOutImpl for usage discussions.
keyboardEventHandler: PROC[info: LarkInfo, sEvent: Lark.StatusEvent],
keyboardResetHandler: PROC[info: LarkInfo]
];
LarkCall: TYPE = REF LarkCallBody;
There is increasing need for specifications, such as text-to-speech synthesis information and such, which needs to be communicated synchronously to the Lark, but which is too application-specific to be dealt with explicitly here. To achieve such a call, one issues EnterLarkState[...., sameState, [.... , LarkCall, ...]]. This will be queued for the Lark supervisor, which will invoke the LarkCall if the Lark is in one of the standard conversation states. It won't work during any of the tones states, failure states, idle state, etc. The callback proc should do very innocuous things, ultimately invoking info.interface.SomeLarkProc. Error recovery on failure is handled by the supervisor.
LarkCallBody: TYPE = RECORD [
proc: PROC[info: LarkInfo, clientData: REF],
clientData: REF
];
*SmartsImpl Inter-module communication (including TrunkSmarts)
RegisterTrunk: PROC[
Registration of Lark trunks (back doors)
Called from LarkSmarts.Register to set up Party, Smarts, SmartsInfo for Trunk
hostSmartsID: Thrush.SmartsID, hostInfo: SmartsInfo, partyRname: Thrush.ROPE ]
RETURNS [ nb: Thrush.NB, smartsID: Thrush.SmartsID ];
EnableSmarts: PROC[r: REF]; -- r is really a LarkInfo
Lark is asserted ready to go. Tell the party, and make Lark operational.
Deregister: PROC[r: REF];
r is a larkInfo, really. Larks, their smarts, and their parties, can only be decommissioned by calling ThSmartsPrivate.Fail, which disassembles them from the bottom up.
FlashDone: PROC[r: REF];
r is a larkInfo, really. Cause a report to be issued to the conversation in which this trunk is a party that switchhook flashing is complete.
LarkParseEvent: -- INTERNAL -- PROC[r: REF];
NoteNewState: PROC[cDesc: ConvDesc, convEvent: Thrush.ConvEvent];
ComputeConnection: PROC[cDesc: ConvDesc] RETURNS [pInfo: ThParty.PartyInfo ← NIL];
LarkAvailable: PROC[info: SmartsInfo] RETURNS [avail: BOOL];
 Crowbar: obsolete. See LarkSmartsImpl.LarkAvailable.
LarkProgress: PROC[
LarkSmartsInitImpl has to supply the stub version of these ThSmarts functions, implemented in LarkSmartsSupImpl, in an interface record.
interface: ThSmartsRpcControl.InterfaceRecord,
shh: SHHH,
convEvent: Thrush.ConvEvent
];
LarkSubstitution: PROC[
interface: ThSmartsRpcControl.InterfaceRecord,
shh: SHHH,
convEvent: Thrush.ConvEvent,
oldPartyID: Thrush.PartyID,
newPartyID: Thrush.PartyID
];
LarkReportAction: PROC[
interface: ThSmartsRpcControl.InterfaceRecord,
shh: SHHH,
report: Thrush.ActionReport
];
Return current state
GetSmartsInfo: PROC[smartsID: Thrush.SmartsID] RETURNS [info: SmartsInfo];
GetSIC: PROC[info: SmartsInfo] RETURNS [ state: Thrush.StateInConv ];
ChangeState: PROC[
cDesc: ConvDesc,
state: Thrush.StateInConv ← $idle,
reason: Thrush.Reason ← NIL, -- $wontSay
comment: Thrush.ROPENIL,
bilateral: BOOLFALSE -- See Thparty.Advance[...bilateral]
] RETURNS [nb: Thrush.NB]; -- advisory only, by this point
ForgetConv: PROC[cDesc: ConvDesc];
GetConvDesc: PROC[info: SmartsInfo] RETURNS [ cDesc: ConvDesc←NIL ];
GetConv: PROC[info: SmartsInfo, convID: Thrush.ConversationID, createOK: BOOLFALSE]
RETURNS [ cDesc: ConvDesc ];
DBInfo: PROC[partyID: Thrush.PartyID, attribute: ATOMNIL, prevDbAtom: ATOMNIL]
RETURNS [ dbRname: Rope.ROPE, dbAtom: ATOM, value: Rope.ROPE ];
Converts from "Sturgis.pa" to "Sturgis.pa.lark", produces $sturgis.pa as well, and if attribute is given, value ← the selected attribute for "Sturgis.pa.lark" in the GV database
If prevDBAtom is supplied, partyID need not be used to identify party.
Outboard Smarts services to Smarts Communications
RegisterServiceProvider: PROC[
serviceProvider: PROC[credentials: Thrush.Credentials, smartsInfo: SmartsInfo]];
Smarts-to-LarkOut Procedures
ENTRY version
EnterLarkState: PROC
[ info: LarkInfo, newState: LarkState, data: LIST OF REF ANYNIL ];
Put this lark into the indicated state, setting tones, crossbar, ethernet connections, whatever else is appropriate to the state. By this point, the higher levels are supposed to have sorted out any conflicts between the requirements of the two Smarts (Lark and LarkTrunk) that control the Lark. A single state may not be enough to do that, in the long run.
Data is a list of (at this writing):
Party.PartyInfo (containing connection specifications)
Lark.KeyTable
LarkPlay.toneSpec
Thrush.ProseSpecs
Atom.DottedPair (allows keyword values to be presented):
$audioSource: [$lineA, $lineB, NIL ($telset)]
$transmitOnly: [$TRUE, $FALSE]
$phoneNumber: "number to dial"
$textToSpeech: [$TRUE, $FALSE]
The KeyTable should be accepted and forwarded on to the Lark whenever it appears, while the others will be interpreted relative to newState.
Making everything a dotted pair would be less efficient but more uniform.
INTERNAL version
EnterLarkSt: PROC
[ info: LarkInfo, newState: LarkState, data: LIST OF REF ANYNIL ];
Fail: PROC[info: LarkInfo, reason: Rope.ROPE, informLark: BOOLFALSE];
Cause Lark to fail and deregister attached smarts, parties. If informLark is TRUE and Lark is operational, take steps to cause Lark to boot and re-register.
FailInt: PROC[info: LarkInfo, reason: Rope.ROPE, informLark: BOOLFALSE]; -- Internal version
QueueFeeps: PROC
[ sInfo: SmartsInfo, feeps: Rope.ROPE ];
SetupTimeouts: PROC[info: LarkInfo, debugging: BOOL ];
A number of timeout behaviors are different if the Lark is under development than if it is part of the operational system. Whether it's operational is determined by the database, properly the domain of Smarts-level code, but how to set the timeouts is the domain of the Lark-level code, thus this procedure to connect them together.
Deb: PROC[info: LarkInfo, r: Rope.ROPE, p1, p2, p3, p4: IO.Value←[null[]]];
Smarts-to-LarkIn Procedures
CheckHookState: PROC
[ info: LarkInfo]
RETURNS [ onHook: BOOLTRUE ];
Determines if user terminal appears to be offhook. May not be able to tell if there's
a "click-mode" speakerphone call in progress.
LarkIn-to-LarkOut Procedures
INTERNAL version
TonesDone: PROC[ info: LarkInfo, commandEvent: Lark.StatusEvent ];
A way to feed back tone completions, initiated by LarkOut, back to LarkOut from LarkIn.
Feep tones don't go through this path, since higher-levels need to know about the situation.
QueueLarkAction: PROC[ info: LarkInfo, ref: REF ];
If confident enough that it's OK to do it in the current state, can just queue for the Lark supervisor to issue.
}.
Swinehart, May 21, 1985 4:39:39 pm PDT
Cedar 6.0, adding Text-to-speech service
changes to: LarkInfoBody
Polle Zellweger (PTZ) August 8, 1985 8:11:29 pm PDT
changes to: pResetConfirmEnd, stopAndFlushEnd, incorrectClientMarker, flushMarker
Polle Zellweger (PTZ) August 19, 1985 2:52:15 pm PDT
Handle Prose flushing.
changes to: pResetConfirmEnd, stopAndFlushEnd, incorrectClientMarker, flushMarker, LarkInfoBody (added flushJustFinished)
Polle Zellweger (PTZ) August 20, 1985 7:50:19 pm PDT
changes to: ResetProse, ProseControlDone, ReportProseDone
Polle Zellweger (PTZ) August 29, 1985 5:31:56 pm PDT
Place local variables from LarkOutImpl.LarkSupervisor into LarkInfo record.
changes to: LarkInfoBody, maxClientMarker, indexMarkerEnd, maxControlMarker, pReset, pResetConfirmEnd, stopAndFlush, stopAndFlushEnd, flushMarker, proseFailure
Swinehart, October 25, 1985 6:00:46 pm PDT
Handle => ID
changes to: DIRECTORY, ConversationID, PartyID, SmartsID, SmartsInfoBody, ConvDescBody, LarkInfoBody, RegisterTrunk, LarkProgress, LarkSetInterval, GetSmartsInfo, GetConv
Swinehart, May 25, 1986 10:19:42 pm PDT
Lark => LarkOps
changes to: DIRECTORY, LarkInfoBody
Swinehart, January 1, 1987 9:39:28 pm PST
Still trying to get FD/BD conflict stuff right. Go back to doing it at the party level.
changes to: LarkState, QueueLarkAction