BluejaySmartsImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Last modified by D. Swinehart, May 22, 1986 4:28:27 pm PDT
DIRECTORY
BluejaySmarts,
Commander USING [ CommandProc, Register ],
CommandTool USING [ NextArgument ],
IO,
Jukebox  USING [ bytesPerChirp, CloseJukebox, CreateJukebox, CloseTune, CreateTune, EOF, Error, Handle, MissingChirp, OpenJukebox, OpenTune, Tune, TuneSize ],
Lark   USING [ VoiceSocket ],
Nice,
Process  USING [ Detach, MsecToTicks, Pause, SecondsToTicks, SetTimeout ],
PupSocket  USING [ Destroy, SetGetTimeout, SetRemoteAddress, Socket ],
RefID   USING [ Unseal ],
Rope,
ThParty  USING [ Advance, CreateParty, DescribeParty, Enable, RegisterClone, SetIntervals ],
Thrush  USING [ ConversationHandle, ConvEvent, Disposition, IntervalSpec, IntervalSpecBody, IntervalSpecs, NB, newTune, nullConvHandle, nullHandle, nullTune, PartyHandle, Reason, ROPE, SHHH, SmartsHandle, StateInConv, Tune ],
ThSmarts,
ThSmartsPrivate USING [ ConvDesc, ConvDescBody, OpenConversations ],
VoiceStream USING [ AddPiece, Close, FlushPieces, Handle, IsEmpty, NotifyProc, Open, SetSocket, wholeTune ],
VoiceUtils USING [ CmdOrToken, FindWhere, Problem, RegisterWhereToReport, Report, ReportFR, WhereProc ]
;
BluejaySmartsImpl: CEDAR MONITOR
IMPORTS BluejaySmarts, Commander, CommandTool, IO, Jukebox, Nice, Process, PupSocket, RefID, Rope, ThParty, VoiceStream, VoiceUtils
EXPORTS BluejaySmarts, ThSmarts = {
OPEN IO;
Types, Definitions
ConvDesc: TYPE = ThSmartsPrivate.ConvDesc;
ConversationHandle: TYPE = Thrush.ConversationHandle;
nullConvHandle: ConversationHandle = Thrush.nullConvHandle;
Disposition: TYPE = Thrush.Disposition;
PartyHandle: TYPE = Thrush.PartyHandle;
SHHH: TYPE = Thrush.SHHH;
SmartsHandle: TYPE = Thrush.SmartsHandle;
StateInConv: TYPE = Thrush.StateInConv;
IntervalSpec: TYPE = Thrush.IntervalSpec;
IntervalSpecs: TYPE = Thrush.IntervalSpecs;
JayInfo: TYPE = BluejaySmarts.JayInfo;
JayInfoBody: TYPE = BluejaySmarts.JayInfoBody;
jayShh: PUBLIC SHHH;
interfaceIsImported: PUBLIC BOOLEANFALSE;
encryptionRequested: PUBLIC BOOLEANTRUE;
handle: PUBLIC Jukebox.Handle;
debug: PUBLIC BOOLEANTRUE;
NB: TYPE = Thrush.NB;
infos: PUBLIC ARRAY[0..100) OF BluejaySmarts.JayInfo←ALL[NIL];
smartses: PUBLIC ARRAY[0..100) OF Thrush.SmartsHandle ← ALL[Thrush.nullHandle];
numParties: PUBLIC NAT𡤀
haveJuke: PUBLIC BOOLTRUE;
timeoutNoAction: INTEGER ← 30;
Ctr: TYPE = RECORD[
count: INT,
stop: INT
];
PD: TYPE = RECORD [
cPr: REF Ctr←NEW[Ctr←[0,1000000]],
cSp: REF Ctr←NEW[Ctr←[0,1000000]],
cRs: REF Ctr←NEW[Ctr←[0,1000000]],
cZp: REF Ctr←NEW[Ctr←[0,1000000]],
cNw: REF Ctr←NEW[Ctr←[0,1000000]],
numReportDones: INT𡤀,
numReportDoneEs: INT𡤀,
doReports: BOOLFALSE,
doNice: BOOLFALSE,
jayInitialized: BOOLFALSE
];
pd: REF PDNEW[PD←[]];
Report: PROC[what: ROPE, ctr: REF Ctr] = {
IF NOT pd.doReports THEN RETURN;
VoiceUtils.Report[what, $Bluejay];
ctr.count𡤌tr.count+1;
IF ctr.count>ctr.stop THEN { VoiceUtils.Problem["Report overflow", $Bluejay]; };
};
BeNice: PROC[r: REF, d: INT] = {
IF NOT pd.doNice THEN RETURN;
Nice.BeNice[r, d, $Bluejay, NIL];
};
Call Supervision
Progress: PUBLIC ENTRY PROC[
shh: SHHH,
smartsID: Thrush.SmartsHandle,
event: Thrush.ConvEvent,
yourParty: BOOL,
latestEvent: BOOL,
informationOnly: BOOL
] RETURNS [ d: Thrush.Disposition ] = {
info: JayInfo ← InfoForSmarts[smartsID: smartsID];
cDesc: ThSmartsPrivate.ConvDesc;
IF info=NIL THEN RETURN[pass];
d�tedAndStop;
Update local state information
cDesc ← GetConv[info, event.credentials.convID, FALSE];
IF pd.doReports THEN Report[
Rope.Concat[
IO.PutFR["---- JyProg: %t(%d) %g %g yr=%g ",
time[event.credentials.convID], int[event.credentials.stateID],
refAny[NEW[StateInConv𡤎vent.state]], DParty[info, cDesc], bool[yourParty]],
IO.PutFR["lt=%g in=%g, ky=%g\n",
bool[latestEvent], bool[event.intervalSpecs#NIL], bool[event.keyTable#NIL]]],
pd.cPr];
IF event.credentials.stateID <= cDesc.cState.credentials.stateID THEN RETURN[pass]; -- Old news!
Fields always extracted
cDesc.cState.credentials.smartsID ← smartsID;
cDesc.cState.credentials.stateID ← event.credentials.stateID;
<<Need to check for increasing stateID here, and reject old ones? If so, enable
test in alrt case below.>>
Extracted if present
IF event.keyTable#NIL THEN {
cDesc.cState.keyTable ← event.keyTable; cDesc.newKeys←TRUE; };
IF event.intervalSpecs#NIL AND event.intervalSpecs.first.type=request THEN
EnqueueIntervals[cDesc, event.intervalSpecs];
IF event.address#NIL THEN { cDesc.cState.address𡤎vent.address; cDesc.newAddress←TRUE; };
Extracted if the event changes our state.
IF yourParty THEN {
cDesc.descValid ← TRUE;
cDesc.cState.credentials.partyID ← event.credentials.partyID;
cDesc.cState.state ← event.state;
cDesc.cState.comment ← event.comment;
cDesc.cState.reason ← event.reason;
IF event.spec#NIL THEN { cDesc.cState.spec ← event.spec; cDesc.newSpec←TRUE; };
Extracted if non-standard?
IF event.comment#NIL THEN cDesc.cState.comment ← event.comment;
IF event.urgency#normal THEN cDesc.cState.urgency ← event.urgency;
IF event.alertKind#standard THEN cDesc.cState.alertKind ← event.alertKind;
};
BeNice[event, 4];
BeNice[cDesc, 6];
IF latestEvent THEN Apprise[info]; -- Wait for the last report to wake process.
};
Supervise: PUBLIC ENTRY PROC[info: JayInfo ] = {
OPEN Jukebox;
TRUSTED {Process.SetTimeout[@info.thAction, Process.SecondsToTicks[timeoutNoAction]]; };
IF info.apprise THEN DO
ENABLE {
UNWIND => NULL;
Jukebox.Error, Jukebox.MissingChirp, Jukebox.EOF => {
VoiceUtils.Problem["Error detected in Bluejay", $Bluejay]; GOTO Failing; };
};
prevL: ThSmartsPrivate.OpenConversations ← NIL;
nb: NB←success;
convL: ThSmartsPrivate.OpenConversations;
trans: Transition;
info.apprise ← FALSE;
FOR convL ← info.conversations,
convL.rest WHILE convL#NIL DO
cDesc: ConvDesc = convL.first;
stateNow: StateInConv = cDesc.cState.state;
ours: BOOL ← ( cDesc.cState.credentials.convID = info.currentConvID );
IF pd.doReports THEN Report[
Rope.Concat[
IO.PutFR["**** JySup: %t(%d) %g %g->%g",
time[cDesc.cState.credentials.convID], int[cDesc.cState.credentials.stateID],
DParty[info, cDesc], refAny[NEW[StateInConv←stateNow]], refAny[NEW[StateInConv�sc.desiredState]]],
IO.PutFR[" %g%g\n",
refAny[NEW[Transition←transForStates[stateNow][cDesc.desiredState]]],
rope[IF ours THEN " (ours)" ELSE ""]]],
pd.cSp];
BeNice[cDesc, 6];
IF NOT cDesc.descValid THEN LOOP;
IF info.currentConvID = nullConvHandle AND stateNow#idle THEN {
<<This stuff feels like it's in the wrong place. Will feel more so when multi-calls
are dealt with. >>
ours←TRUE; info.currentConvID ← cDesc.cState.credentials.convID;
};
IF stateNow#idle AND (NOT ours) THEN
Conv isn't the one we're interested in, and doesn't look like it's going idle: idle it.
Should probably consult transForStates for validity.
nb ← ThParty.Advance[
shhh: info.shh,
credentials: cDesc.cState.credentials,
state: idle,
reason: busy,
comment: "One conversation at a time, please."
]
ELSE {
State and substate (below) and desired state indicate there's something to do. Do it:
SELECT (trans←transForStates[stateNow][cDesc.desiredState]) FROM
noop => NULL;
elim => {
Conv. is now idle: forget about it or re-use it, as a reserved conversation.
Remove dead conversation from list.
Report[IO.PutFR[" ** Zap: %t\n", time[cDesc.cState.credentials.convID]], pd.cZp];
IF prevL#NIL THEN { prevL.rest ← convL.rest; convL ← prevL; }
ELSE info.conversations𡤌onvL.rest;
IF ours THEN {
info.currentConvID ← nullConvHandle;
CloseConnection[info];
};
};
idle => nb ← AdvanceToDesired[info, cDesc];
actv => {
IF cDesc.signallingStarted THEN LOOP;
cDesc.signallingStarted ← TRUE;
cDesc.desiredState ← active;
nb ← AdvanceToDesired[info, cDesc];
};
nrvl => { -- report new interval accepted
IF cDesc.newSpec THEN OpenConnection[info, cDesc];
IF cDesc.newIntervals#NIL THEN {
DoSetIntervals[info, cDesc];
nb ← ThParty.SetIntervals[
shhh: info.shh,
credentials: cDesc.cState.credentials,
intervalSpecs: cDesc.newIntervals
];
IF nb=success THEN cDesc.newIntervals←NIL;
};
};
invl, ntiy => {
VoiceUtils.Problem["BluejaySmarts: Invalid or not yet implemented state transition request.", $Bluejay];
info.apprise←TRUE;
cDesc.desiredState ← idle;
cDesc.desiredReason ← error;
cDesc.desiredComment ← "Invalid state transition";
};
ENDCASE => ERROR;
};
Analyze any results of trying to reach a different state.
IF nb#success THEN
Report[IO.PutFR[" ** Results: nb=%g\n", refAny[NEW[Thrush.NB←nb]]], pd.cRs];
SELECT nb FROM
success, stateMismatch => NULL;
partyNotEnabled => ours←FALSE;
invalidTransition, convNotActive, convStillActive => {
Complain and transition to idle
comment: ROPE="Bluejay: Party-level detected invalid state transition request";
VoiceUtils.Problem[comment, $Bluejay];
cDesc.desiredState ← idle;
cDesc.cState.comment ← comment;
cDesc.cState.reason ← error;
info.apprise←TRUE;
};
notInConv, noSuchConv => { -- Complain, zap, go idle and repeat.
comment: ROPE="Bluejay: NotInConv or NoSuchConv";
VoiceUtils.Problem[comment, $Bluejay];
IF ours THEN info.currentConvID ← nullConvHandle;
cDesc.cState.credentials.convID ← nullConvHandle;
cDesc.cState.credentials.stateID ← 0;
cDesc.desiredState ← idle;
cDesc.cState.comment ← comment;
cDesc.cState.reason ← error;
info.apprise←TRUE;
};
noSuchParty, noSuchSmarts => { -- Complain, deregister, we gone! Needs tuning.
VoiceUtils.Problem[
"Bluejay: NoSuchParty or NoSuchSmarts reported, must try to go away", $Bluejay];
GOTO Failing;
};
noSuchParty2, narcissism => ERROR; -- shouldn't be provoking these
ENDCASE => ERROR;
prevL ← convL;
ENDLOOP;
IF NOT info.apprise THEN WAIT info.thAction;
IF info.conversations = NIL THEN EXIT;
REPEAT Failing => NULL; -- ThSmartsPrivate.Deregister[info]; now Failed
ENDLOOP;
info.thProcess ← NIL;
};
Utilities
GetConv: PUBLIC INTERNAL PROC[info: JayInfo, convID: ConversationHandle, validIfNew: BOOL
] RETURNS [ cDesc: ConvDesc←NIL ] = --INLINE-- {
FOR convs: ThSmartsPrivate.OpenConversations ← info.conversations, convs.rest WHILE convs#NIL DO
IF convs.first.cState.credentials.convID = convID THEN RETURN[convs.first];
ENDLOOP;
cDesc ← NEW[ThSmartsPrivate.ConvDescBody←[]];
cDesc.descValid ← validIfNew;
cDesc.cState.credentials.convID ← convID;
cDesc.cState.credentials.smartsID ← info.smartsID;
cDesc.cState.credentials.partyID ← info.partyID;
info.conversations ← CONS[cDesc, info.conversations];
IF pd.doReports THEN
Report[IO.PutFR[" ** NewConv, %t %g, vl=%g\n", time[convID], DParty[info, cDesc], bool[validIfNew]], pd.cNw];
};
GetCDesc: PROC[info: JayInfo] RETURNS [ cDesc: ConvDesc←NIL ] = {
Not at present protected by monitor -- LarkStateImpl back-pointer problem.
convID: ConversationHandle=info.currentConvID;
IF convID=nullConvHandle THEN RETURN;
FOR convs: ThSmartsPrivate.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];
};
EnqueueIntervals: INTERNAL PROC[cDesc: ConvDesc, int: Thrush.IntervalSpecs] = {
FOR iL: IntervalSpecs ← int, iL.rest WHILE iL#NIL DO
Copies, so that changes can be made in order to report back to ThParty.
<<This is strictly necessary only when running Ja. In Jay, RPC will copy. Whatever.>>
newSpec: Thrush.IntervalSpecs ← LIST[NEW[Thrush.IntervalSpecBody ← iL.first^]];
IF cDesc.newIntervals#NIL THEN cDesc.iTail.rest ← newSpec
ELSE cDesc.newIntervals ← newSpec;
cDesc.iTail ← newSpec;
ENDLOOP;
};
GetSIC: PUBLIC INTERNAL PROC[info: JayInfo] RETURNS [ state: StateInConv ] = {
cDesc: ConvDesc = GetCDesc[info];
RETURN[IF cDesc=NIL THEN idle ELSE cDesc.cState.state];
};
Apprise: PUBLIC INTERNAL PROC[info: JayInfo] = TRUSTED --INLINE-- {
IF info.thProcess=NIL THEN
Process.Detach[info.thProcess ← FORK Supervise[info]];
info.apprise ← TRUE;
NOTIFY info.thAction;
};
AdvanceToDesired: INTERNAL PROC[info: JayInfo, cDesc: ConvDesc] RETURNS [nb: NB] = {
RETURN[nb ← ThParty.Advance[
shhh: info.shh,
credentials: cDesc.cState.credentials,
state: cDesc.desiredState,
reason: cDesc.desiredReason,
comment: cDesc.desiredComment
]];
};
InfoForSmarts: INTERNAL PROC [ smartsID: SmartsHandle ] RETURNS [ info: JayInfo ] = {
FOR i: NAT IN [0..numParties) DO IF smartsID=smartses[i] THEN RETURN [ infos[i] ]; ENDLOOP;
RETURN[ NIL ]; };
OpenConnection: INTERNAL PROC[info: JayInfo, cDesc: ConvDesc] = TRUSTED {
refToSocket: REF PupSocket.Socket;
IF NOT cDesc.newSpec THEN RETURN;
IF info.stream=NIL THEN info.stream ← VoiceStream.Open[jukebox: handle, proc: ReportDone, clientData: info];
VoiceUtils.ReportFR["C %d ", $Bluejay, NIL, card[info.smartsID]];
refToSocket ← NARROW[RefID.Unseal[LOOPHOLE[cDesc.cState.spec.localSocket.socket]]];
info.socket ← refToSocket^;
See ThPartyOpsImpl comments about Cedar 6.1 accommodation
IF info.socket = NIL THEN ERROR;
PupSocket.SetRemoteAddress[info.socket, cDesc.cState.spec.remoteSocket];
PupSocket.SetGetTimeout[info.socket, 100];
VoiceStream.SetSocket[socket: info.socket, handle: info.stream];
info.lastIntervalSpec ← NIL;
cDesc.newSpec ← FALSE;
};
CloseConnection: INTERNAL PROC[info: JayInfo] = TRUSTED {
IF info.stream#NIL THEN VoiceStream.Close[info.stream];
info.stream ← NIL;
IF info.socket#NIL THEN PupSocket.Destroy[info.socket];
info.lastIntervalSpec ← NIL;
VoiceUtils.ReportFR["D %d ", $Bluejay, NIL, card[info.smartsID]];
};
Tunes: control of recording and playback
DoSetIntervals: INTERNAL PROC[info: JayInfo, cDesc: ConvDesc] = TRUSTED {
FOR intervalSpecs: Thrush.IntervalSpecs ← cDesc.newIntervals, intervalSpecs.rest WHILE intervalSpecs#NIL DO
jTune: Jukebox.Tune;
intervalSpec: Thrush.IntervalSpec = intervalSpecs.first;
tune: Thrush.Tune;
flush: BOOL;
IF intervalSpec=NIL THEN ERROR;
flush ← intervalSpec.interval.length=0 AND (NOT intervalSpec.queueIt);
tune ← intervalSpec.tune;
SELECT intervalSpec.type FROM
request => NULL; -- Let's go start it.
started, finished => LOOP; -- was started before, but not successfully reported (mismatch?), or time to report finished. (does this happen?)
ENDCASE => ERROR;
intervalSpec.type ← started;
IF info.stream=NIL THEN info.stream ← VoiceStream.Open[jukebox: handle, proc: ReportDone, clientData: info];
IF flush THEN {
VoiceStream.FlushPieces[handle: info.stream];
LOOP;
};
SELECT tune FROM
Thrush.nullTune => ERROR;
Thrush.newTune =>
IF intervalSpec.direction = record THEN {
jTune ← Jukebox.CreateTune[handle, -1];
tune ← jTune.tuneId;
intervalSpec.tune←tune;
Jukebox.CloseTune[ handle, jTune ];
}
ELSE ERROR; -- Shouldn't try to play a nonexistent tune
ENDCASE;
<<This is REALLY a crock! I think it's to let the key get to the Lark. MUST fix.>>
IF intervalSpec.direction = play THEN Process.Pause[Process.MsecToTicks[100]];
VoiceStream.AddPiece[
handle: info.stream,
tuneId: tune,
firstByte: intervalSpec.interval.start,
nBytes: IF intervalSpec.interval.length=-1 THEN VoiceStream.wholeTune
ELSE intervalSpec.interval.length,
create: intervalSpec.direction = record,
playback: intervalSpec.direction # record,
keyIndex: intervalSpec.keyIndex,
flush: NOT intervalSpec.queueIt ];
info.lastIntervalSpec ← intervalSpec; -- Last time around it gets the proper value.
ENDLOOP;
<< Perhaps should be able to specify, in request, whether started/finished notes are needed.>>
};
ReportDone: VoiceStream.NotifyProc = TRUSTED {
info: JayInfo ← NARROW[clientData];
pd.numReportDones ← pd.numReportDones+1;
IF ~VoiceStream.IsEmpty[self] OR info=NIL THEN RETURN;
Process.Detach[FORK ReportDoneEntry[info]];
};
ReportDoneEntry: ENTRY PROC[info: JayInfo] = TRUSTED {
cDesc: ConvDesc;
intervalSpec: Thrush.IntervalSpec;
tune: Thrush.Tune←Thrush.nullTune;
totLen, start: INT𡤀
jTune: Jukebox.Tune←NIL;
pd.numReportDoneEs ← pd.numReportDoneEs+1;
cDesc ← GetCDesc[info];
IF cDesc=NIL OR cDesc.cState.state#active THEN RETURN;
intervalSpec ← info.lastIntervalSpec;
IF intervalSpec = NIL THEN { VoiceUtils.Problem["Bluejay: No interval spec.", $Bluejay]; RETURN; };
<<Need list of specs, else multiple requests can confuse this!!!!!>>
<< To do that requires passing the intID through to the Bluejay level. Think about reducing the amount of communications somehow, too.>>
tune ← intervalSpec.tune;
IF tune>=0 THEN jTune ← Jukebox.OpenTune[handle, tune, FALSE];
IF jTune#NIL THEN {
totLen ← Jukebox.TuneSize[jTune]*Jukebox.bytesPerChirp;
Jukebox.CloseTune[handle, jTune];
start ← MIN[intervalSpec.interval.start, totLen];
intervalSpec.interval ← [start: start, length: MAX[totLen-start, 0]];
};
intervalSpec.type ← finished;
info.lastIntervalSpec ← NIL;
EnqueueIntervals[cDesc, LIST[intervalSpec]]; -- Notify client
Apprise[info];
};
DParty: PROC[info: JayInfo, cDesc: ConvDesc] RETURNS [IO.Value] = {
RETURN[rope[ThParty.DescribeParty[shh: info.shh, partyID: cDesc.cState.credentials.partyID]]];
};
Initialization
InitJay: Commander.CommandProc = {
numTrunks: NAT ← 5;
bluejayInstance, thrushInstance, jukeboxName, serverPassword: Thrush.ROPE;
partyID: Thrush.PartyHandle;
smartsID: Thrush.SmartsHandle;
clonePartyID: Thrush.PartyHandle;
info: BluejaySmarts.JayInfo;
bluejayInstance ← CommandTool.NextArgument[cmd];
IF bluejayInstance#NIL THEN numTrunks ← IO.GetInt[IO.RIS[bluejayInstance]];
jukeboxName ← VoiceUtils.CmdOrToken[cmd: cmd, key: "BluejayJukeboxName",
default: "Strowger.Jukebox"];
bluejayInstance ←
VoiceUtils.CmdOrToken[cmd: cmd, key: "BluejayServerInstance", default: "Strowger.Lark"];
thrushInstance ←
VoiceUtils.CmdOrToken[cmd: cmd, key: "ThrushClientServerInstance", default: "Strowger.Lark"];
serverPassword ←
VoiceUtils.CmdOrToken[cmd: cmd, key: "BluejayServerPassword", default: "MFLFLX"];
IF pd.jayInitialized THEN RETURN;
Initialize anything that has to be set up on the Jay side.
BluejaySmarts.haveJuke←TRUE;
TRUSTED {
BluejaySmarts.handle ← Jukebox.OpenJukebox[name: jukeboxName!
Jukebox.Error => IF reason= NoJukebox THEN {
BluejaySmarts.haveJuke←FALSE; CONTINUE; }];
IF ~BluejaySmarts.haveJuke THEN {
Jukebox.CreateJukebox[jukeboxName, 5000, 100];
BluejaySmarts.handle ← Jukebox.OpenJukebox[jukeboxName];
};
};
BluejaySmarts.haveJuke←TRUE;
VoiceUtils.RegisterWhereToReport[ReportBluejay, $Bluejay];
Export/Import interfaces, create first Jay party and smarts.
[partyID, smartsID] ← BluejaySmarts.BluejayRegister[bluejayInstance, thrushInstance, serverPassword];
IF partyID=Thrush.nullHandle OR smartsID=Thrush.nullHandle THEN GOTO InitFailed;
VoiceUtils.Report["Bluejay Smarts Initialized and Exported, ThParty Imported", $Bluejay];
Get Jay Parties
FOR i: NAT IN [0..numTrunks) DO
IF i#0 THEN {
partyID←ThParty.CreateParty[type: service, rName: "Recording"];
IF partyID=Thrush.nullHandle THEN ERROR;
smartsID ← ThParty.RegisterClone[
shh: BluejaySmarts.jayShh, partyID: partyID, clonePartyID: clonePartyID];
}
ELSE clonePartyID ← partyID;
IF smartsID=Thrush.nullHandle THEN ERROR; -- <<try to delete the Party?>>
IF ThParty.Enable[shh: BluejaySmarts.jayShh, smartsID: smartsID]#success THEN ERROR; -- <<!!!!!!!!!>> --
info←NEW[JayInfoBody ← [smartsID: smartsID, partyID: partyID, shh: BluejaySmarts.jayShh]];
BluejaySmarts.infos[BluejaySmarts.numParties]←info;
BluejaySmarts.smartses[BluejaySmarts.numParties]←smartsID;
BluejaySmarts.numParties𡤋luejaySmarts.numParties+1;
ENDLOOP;
pd.jayInitialized ← TRUE;
EXITS
InitFailed => TRUSTED {
VoiceUtils.Problem["Bluejay: initialization failed", $Bluejay];
IF BluejaySmarts.haveJuke THEN BluejaySmarts.handle ← Jukebox.CloseJukebox[BluejaySmarts.handle!
Jukebox.Error, Jukebox.MissingChirp, Jukebox.EOF=>CONTINUE];
BluejaySmarts.jayShh←NIL;
};
};
ReportBluejay: VoiceUtils.WhereProc = TRUSTED {
s←VoiceUtils.FindWhere[$System, NIL];
};
State Transition Tables
Transition: TYPE = {
Just codes to dispatch on in Supervisor; explained there
noop, elim, idle, actv, nrvl, invl, ntiy
};
transForStates: ARRAY StateInConv OF ARRAY StateInConv OF Transition ← [
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)
[ idle, invl, invl, invl, invl, invl, invl, invl, invl, ntiy, invl ], -- reserved
[ idle, invl, invl, invl, invl, invl, invl, invl, invl, ntiy, invl ], -- parsing
[ idle, invl, invl, invl, invl, invl, invl, invl, invl, ntiy, invl ], -- initiating
[ idle, invl, invl, invl, invl, invl, invl, invl, actv, ntiy, actv ], -- pending
[ idle, invl, invl, invl, invl, invl, invl, invl, invl, ntiy, invl ], -- maybe
[ idle, invl, invl, invl, invl, invl, invl, invl, invl, ntiy, invl ], -- ringing
[ idle, invl, invl, invl, invl, invl, invl, invl, ntiy, ntiy, ntiy ], -- canActivate
[ idle, invl, invl, invl, invl, invl, invl, invl, nrvl, ntiy, nrvl ], -- active
[ idle, invl, invl, invl, invl, invl, invl, invl, ntiy, ntiy, noop ], -- inactive
[ invl, invl, invl, invl, invl, invl, invl, invl, invl, invl, invl ] -- any (nonex)
];
NB: examine relationship to validity table in PartyOpsImpl someday.
Debugging nonsense
ViewCmd: Commander.CommandProc = {
Nice.View[pd, "Bluejay PD"];
};
Commander.Register["VuJay", ViewCmd, "Program Management variables for Bluejay"];
Commander.Register["Jay", InitJay, "Jay <numSmarts> <jukeboxName> <instanceName> <thrushInstance> <serverPassword> (all defaulted) -- Start Bluejay server\nJay 5 ///Morley/Morley.jukebox Morley.Lark Morley.Lark MFLFLX"];
}.
Swinehart, May 15, 1985 10:30:42 am PDT
Cedar 6.0
changes to: InitJay
Swinehart, May 22, 1985 12:07:31 pm PDT
recording => service, Jay => Recording
changes to: InitJay
Swinehart, May 17, 1986 3:53:47 pm PDT
Cedar6.1
changes to: DIRECTORY, BluejaySmartsImpl, Report, Supervise, OpenConnection, CloseConnection, ReportDoneEntry, InitJay, ReportBluejay