DIRECTORY BluejaySmarts, Commander USING [ CommandProc, Register ], CommandToolExtras USING [ NextArgument ], IO, Jukebox USING [ bytesPerChirp, CloseJukebox, CreateJukebox, CloseTune, CreateTune, EOF, Error, Handle, MissingChirp, OpenJukebox, OpenTune, Tune, TuneSize ], Lark USING [ noMachine, VoiceSocket ], Names USING [ CmdOrToken ], Nice, Process USING [ Detach, MsecToTicks, Pause, SecondsToTicks, SetTimeout ], PupDefs USING [ MsToTocks, PupSocket, PupSocketDestroy, PupSocketMake ], Rope, Log USING [ FindWhere, Problem, RegisterWhereToReport, Report, ReportFR, WhereProc ], 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 ] ; BluejaySmartsImpl: CEDAR MONITOR IMPORTS BluejaySmarts, Commander, CommandToolExtras, IO, Jukebox, Log, Names, Nice, Process, PupDefs, Rope, ThParty, VoiceStream EXPORTS BluejaySmarts, ThSmarts = { OPEN IO; 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 BOOLEAN_FALSE; encryptionRequested: PUBLIC BOOLEAN _ TRUE; handle: PUBLIC Jukebox.Handle; debug: PUBLIC BOOLEAN _ TRUE; NB: TYPE = Thrush.NB; noActiveSocket: Lark.VoiceSocket = [ net: [Lark.noMachine.net], host: [Lark.noMachine.host], socket: [0,0]]; infos: PUBLIC ARRAY[0..100) OF BluejaySmarts.JayInfo_ALL[NIL]; smartses: PUBLIC ARRAY[0..100) OF Thrush.SmartsHandle _ ALL[Thrush.nullHandle]; numParties: PUBLIC NAT_0; haveJuke: PUBLIC BOOL_TRUE; 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_0, numReportDoneEs: INT_0, doReports: BOOL_FALSE, doNice: BOOL_FALSE, jayInitialized: BOOL_FALSE ]; pd: REF PD _ NEW[PD_[]]; Report: PROC[what: ROPE, ctr: REF Ctr] = { IF NOT pd.doReports THEN RETURN; Log.Report[what, $Bluejay]; ctr.count_ctr.count+1; IF ctr.count>ctr.stop THEN { Log.Problem["Report overflow", $Bluejay]; }; }; BeNice: PROC[r: REF, d: INT] = { IF NOT pd.doNice THEN RETURN; Nice.BeNice[r, d, $Bluejay, NIL]; }; 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_actedAndStop; 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_event.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! cDesc.cState.credentials.smartsID _ smartsID; cDesc.cState.credentials.stateID _ event.credentials.stateID; 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_event.address; cDesc.newAddress_TRUE; }; 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; }; 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 => { Log.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_cDesc.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 { ours_TRUE; info.currentConvID _ cDesc.cState.credentials.convID; }; IF stateNow#idle AND (NOT ours) THEN nb _ ThParty.Advance[ shhh: info.shh, credentials: cDesc.cState.credentials, state: idle, reason: busy, comment: "One conversation at a time, please." ] ELSE { SELECT (trans_transForStates[stateNow][cDesc.desiredState]) FROM noop => NULL; elim => { 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_convL.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 => { Log.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; }; 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 => { comment: ROPE="Bluejay: Party-level detected invalid state transition request"; Log.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"; Log.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. Log.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; }; 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 ] = { 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 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 { IF NOT cDesc.newSpec THEN RETURN; IF info.stream=NIL THEN info.stream _ VoiceStream.Open[jukebox: handle, proc: ReportDone, clientData: info]; Log.ReportFR["C %d ", $Bluejay, NIL, card[info.smartsID]]; info.socket _ PupDefs.PupSocketMake[ local: cDesc.cState.spec.localSocket.socket, remote: cDesc.cState.spec.remoteSocket, ticks: PupDefs.MsToTocks[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 PupDefs.PupSocketDestroy[info.socket]; info.lastIntervalSpec _ NIL; Log.ReportFR["D %d ", $Bluejay, NIL, card[info.smartsID]]; }; 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; 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; }; 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_0; 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 { Log.Problem["Bluejay: No interval spec.", $Bluejay]; RETURN; }; 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]]]; }; 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 _ CommandToolExtras.NextArgument[cmd]; IF bluejayInstance#NIL THEN numTrunks _ IO.GetInt[IO.RIS[bluejayInstance]]; jukeboxName _ Names.CmdOrToken[cmd: cmd, key: "BluejayJukeboxName", default: "///Strowger/Strowger.Jukebox"]; bluejayInstance _ Names.CmdOrToken[cmd: cmd, key: "BluejayServerInstance", default: "Strowger.Lark"]; thrushInstance _ Names.CmdOrToken[cmd: cmd, key: "ThrushClientServerInstance", default: "Strowger.Lark"]; serverPassword _ Names.CmdOrToken[cmd: cmd, key: "BluejayServerPassword", default: "MFLFLX"]; IF pd.jayInitialized THEN RETURN; 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; Log.RegisterWhereToReport[ReportBluejay, $Bluejay]; [partyID, smartsID] _ BluejaySmarts.BluejayRegister[bluejayInstance, thrushInstance, serverPassword]; IF partyID=Thrush.nullHandle OR smartsID=Thrush.nullHandle THEN GOTO InitFailed; Log.Report["Bluejay Smarts Initialized and Exported, ThParty Imported", $Bluejay]; FOR i: NAT IN [0..numTrunks) DO IF i#0 THEN { partyID_ThParty.CreateParty[type: recording, rName: NIL]; 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; -- <> 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_BluejaySmarts.numParties+1; ENDLOOP; pd.jayInitialized _ TRUE; EXITS InitFailed => TRUSTED { Log.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: Log.WhereProc = TRUSTED { s_Log.FindWhere[$System, NIL]; }; Transition: TYPE = { noop, elim, idle, actv, nrvl, invl, ntiy }; transForStates: ARRAY StateInConv OF ARRAY StateInConv OF Transition _ [ [ 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) ]; ViewCmd: Commander.CommandProc = { Nice.View[pd, "Bluejay PD"]; }; Commander.Register["VuJay", ViewCmd, "Program Management variables for Bluejay"]; Commander.Register["Jay", InitJay, "Jay (all defaulted) -- Start Bluejay server\nJay 5 ///Morley/Morley.jukebox Morley.Lark Morley.Lark MFLFLX"]; }. ÖBluejaySmartsImpl.mesa Last modified by D. Swinehart, November 29, 1984 2:46:19 pm PST Types, Definitions Call Supervision Update local state information Fields always extracted <> Extracted if present Extracted if the event changes our state. Extracted if non-standard? <> 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. State and substate (below) and desired state indicate there's something to do. Do it: Conv. is now idle: forget about it or re-use it, as a reserved conversation. Remove dead conversation from list. Analyze any results of trying to reach a different state. Complain and transition to idle Utilities Not at present protected by monitor -- LarkStateImpl back-pointer problem. Copies, so that changes can be made in order to report back to ThParty. <> Tunes: control of recording and playback <> << Perhaps should be able to specify, in request, whether started/finished notes are needed.>> <> << To do that requires passing the intID through to the Bluejay level. Think about reducing the amount of communications somehow, too.>> Initialization Initialize anything that has to be set up on the Jay side. Export/Import interfaces, create first Jay party and smarts. Get Jay Parties State Transition Tables Just codes to dispatch on in Supervisor; explained there idle resrv pars init pend mayb ring canAc activ inact any -- desired NB: examine relationship to validity table in PartyOpsImpl someday. Debugging nonsense �Êó˜Jšœ™Jšœ?™?J™šÏk ˜ J˜Jšœ �œ˜*Jšœ�œ˜)Jš�œ˜Jšœ �œ�˜žJšœ�œ˜(Jšœ�œ˜J˜J˜J˜Jšœ �œ<˜JJšœ �œ;˜IJ˜Jšœ�œL˜WJšœ �œN˜\Jšœ�œ¦�œ�œ$˜áJ˜ Jšœ�œ/˜DJšœ �œZ˜kJ˜J˜—šœ� ˜ Jš�œy˜€Jš�œ˜$Jš�œ�œ˜J˜—™J˜Jšœ �œ˜*šœ�œ˜5Jšœ;˜;—Jšœ �œ˜'Jšœ �œ˜'Jš�œ�œ �œ˜Jšœ�œ˜)J˜'Jšœ�œ˜)Jšœ�œ˜+Jšœ �œ˜&Jšœ �œ˜.J˜Jšœ� œ˜Jšœ�œ�œ˜*Jšœ�œ�œ˜+Jšœ�œ˜Jšœ�œ�œ˜Jš�œ�œ �œ˜J˜˜$J˜G—J˜Jš œ� œ �œ�œ�œ˜>Jšœ � œ �œ�œ˜OJšœ � œ˜Jšœ �œ�œ�œ˜J˜Jšœ�œ˜šœ�œ�œ˜Jšœ�œ˜ Jšœ�˜ J˜—J˜š�œ�œ�œ˜Jšœ�œ�œ˜"Jšœ�œ�œ˜"Jšœ�œ�œ˜"Jšœ�œ�œ˜"Jšœ�œ�œ˜"Jšœ�œ˜Jšœ�œ˜Jšœ �œ�œ˜Jšœ�œ�œ˜Jšœ�œ�˜J˜—Jš œ�œ�œ�œ�œ˜J˜šÏnœ�œ�œ�œ ˜*Jš�œ�œ�œ�œ˜ J˜J˜Jš�œ�œ/˜IJ˜—J˜šžœ�œ�œ�œ˜ Jš�œ�œ �œ�œ˜Jšœ�œ˜!J˜—J˜—™J˜šžœ�œ�œ�œ˜Jšœ�œ˜ J˜Jšœ˜Jšœ �œ˜Jšœ �œ˜Jšœ�˜Jšœ�œ˜'Jšœ2˜2Jšœ ˜ Jš�œ�œ�œ�œ˜Jšœ˜J™J˜Jšœ0�œ˜7J˜š�œ�œ˜šœ ˜ š�œ*˜,J˜?Jšœ�œB˜L—š�œ˜ Jšœ,�œ�œ˜M——Jšœ˜—J˜Jš �œ?�œ�œ�œÏc ˜`J™J™J˜-˜=J™PJ™—J˜J™š�œ�œ�œ˜Jšœ6�œ˜>—š�œ�œ�œ(�˜JJšœ-˜-—Jš�œ�œ�œ8�œ˜YJ™J™*š�œ �œ˜Jšœ�œ˜Jšœ=˜=Jšœ!˜!Jšœ%˜%Jšœ#˜#Jš�œ �œ�œ1�œ˜OJ™Jš�œ�œ�œ&˜?Jš�œ�œ&˜BJš�œ�œ*˜JJ˜—J˜J˜J˜J™Jš�œ �œŸ,˜OJ˜J˜—šž œ� œ�œ˜0Jš�œ ˜ Jš�œQ˜Xš�œ�œ�˜š�œ˜Jš�œ�œ˜šœ-�œ˜5Jšœ4�œ ˜D—J˜—Jšœ+�œ˜/Jšœ�œ ˜Jšœ)˜)J˜Jšœ�œ˜š�œ˜Jšœ �œ�œ�˜J˜Jšœ+˜+Jšœ�œ<˜FJ˜š�œ�œ˜šœ ˜ š�œ&˜(J˜MJšœ�œ �œ#˜e—š�œ˜Jšœ�œ;˜EJšœ�œ�œ �œ˜'——Jšœ˜—J˜J™Jš�œ�œ�œ�œ˜!š�œ%�œ�œ˜?J™gJšœ�œ7˜@Jšœ˜—š�œ�œ�œ�˜$J™WJ™4˜J˜J˜&J˜ J˜ Jšœ.˜.J˜——š�œ˜J™Uš�œ6�˜@Jšœ�œ˜ šœ ˜ J™MJ™#J˜JšœR˜RJ™Jš�œ�œ�œ,˜=Jš�œ˜#š�œ�œ˜Jšœ$˜$J˜J˜—Jšœ˜—Jšœ+˜+šœ ˜ Jš�œ�œ�œ˜%Jšœ�œ˜J˜Jšœ#˜#J˜—šœ Ÿ˜)Jš�œ�œ˜2š�œ�œ�œ˜ Jšœ�œ˜šœ˜Jšœ˜Jšœ&˜&Jšœ!˜!Jšœ˜—Jš�œ �œ�œ˜*J˜—J˜—˜J˜aJšœ �œ˜J˜J˜J˜2J˜—Jš�œ ˜—J˜—J™9J˜š�œ �˜Jšœ�œ'�œ�œ˜M—J™š�œ�˜Jšœ�œ˜Jšœ�œ˜šœ6˜6Jšœ™Jšœ �œB˜OJ˜Jšœ˜J˜J˜Jšœ �œ˜Jšœ˜—šœŸ%˜@Jšœ �œ$˜1Jšœ˜Jš�œ�œ%˜1Jšœ1˜1J˜%Jšœ˜J˜J˜Jšœ �œ˜Jšœ˜—šœŸ/˜Nšœ ˜ JšœP˜P—Jš�œ ˜ J˜—Jšœ#Ÿ˜BJš�œ ˜—J˜Jš�œ˜—Jš�œ�œ�œ�œ˜,Jš�œ�œ�œ�œ˜&Jš�œ �œŸ%œ ˜GJš�œ˜—Jšœ�œ˜J˜J˜——™ J™šžœ�œ�œ8�˜YJšœ�œ�œŸ œ˜0š�œK�œ�œ�˜`Jš�œ0�œ�œ˜KJš�œ˜—Jšœ�œ"˜-J˜J˜)Jšœ2˜2Jšœ0˜0Jšœ�œ˜5J˜š�œ�˜Jšœ�œe˜n—J™J˜—J˜šžœ�œ�œ�œ˜AJ™JJšœ.˜.Jš�œ�œ�œ˜%š�œK�œ�œ�˜`š�œ0�œ˜8Jšœ�œ˜—Jš�œ˜—Jš�œ�œ�œ�œ�œ�œ�œ˜=J˜J˜—šžœ�œ�œ1˜Pš�œ"�œ�œ�˜4J™GJ™WJšœ �œ�œ'˜OJš�œ�œ�œ˜9Jš�œ˜"J˜Jš�œ˜—J˜—J˜šžœ�œ�œ˜NJ˜!Jš �œ�œ�œ�œ�œ˜7Jšœ˜J˜—š žœ�œ�œ�œ�œŸ œ˜Cš�œ�œ�˜Jšœ �œ˜6—Jšœ�œ˜Jš�œ˜J˜J˜—š žœ�œ�œ!�œ�œ˜Tš�œ˜J˜J˜&J˜J˜J˜J˜—˜J˜——šž œ�œ�œ�œ˜UJš�œ�œ�œ�œ�œ�œ�œ�œ˜[Jš�œ�œ˜—J˜šžœ�œ�œ#�œ˜IJš�œ�œ�œ�œ˜!Jš�œ �œ�œU˜lJšœ �œ˜:˜$Jšœ,˜,Jšœ'˜'J˜—Jšœ@˜@Jšœ�œ˜Jšœ�œ˜Jšœ˜—J˜šžœ�œ�œ�œ˜9Jš�œ �œ�œ ˜7Jšœ�œ˜Jš�œ �œ�œ'˜>Jšœ�œ˜Jšœ �œ˜:Jšœ˜—J˜—šœ(™(J˜šžœ�œ�œ#�œ˜Iš�œN�œ�œ�˜kJ˜Jšœ8˜8J˜Jšœ�œ˜ Jš�œ�œ�œ�œ˜Jšœ'�œ�œ˜FJ˜š�œ�˜Jšœ �œŸ˜&Jšœ�œŸq˜ŒJš�œ�œ˜—J˜Jš�œ �œ�œU˜lš�œ�œ˜Jšœ-˜-Jš�œ˜Jšœ˜—š�œ�˜Jšœ�œ˜˜š�œ!�œ˜)J˜'J˜J˜J˜#J˜—Jš�œ�œŸ+˜7—Jš�œ˜ —J™TJš�œ�œ)˜Nšœ˜J˜J˜ J˜'šœ�œ!�œ˜EJš�œ˜"—J˜(J˜*J˜ Jšœ�œ˜"—J˜SJš�œ˜—Jšœ^™^Jšœ˜—J˜šž œ�œ˜.Jšœ�œ ˜#J˜(Jš �œ�œ�œ�œ�œ˜6Jšœ�œ˜+J˜—J˜šžœ�œ�œ�œ˜7J˜J˜"J˜"Jšœ�œ˜Jšœ�œ˜J˜*J˜Jš �œ�œ�œ�œ�œ˜6Jšœ%˜%š�œ�œ�œ8�œ˜\J™DJ™‰—Jšœ˜Jš�œ �œ(�œ˜>š�œ�œ�œ˜Jšœ7˜7J˜!Jšœ�œ&˜1Jšœ/�œ˜EJ˜—J˜Jšœ�œ˜Jšœ�œŸ˜=J˜J˜—J˜šžœ�œ!�œ�œ ˜CJš�œX˜^J˜——J˜™J˜šžœ˜"Jšœ �œ˜JšœE�œ˜JJ˜J˜J˜!Jšœ˜Jšœ6˜6Jš �œ�œ�œ �œ�œ�œ˜KšœC˜CJšœ)˜)—šœ˜J˜S—šœ˜JšœX˜X—˜J˜L—Jš�œ�œ�œ˜!˜Jšœ:™:J™—Jšœ�œ˜š�œ˜ šœ=˜=šœ�œ�œ˜,Jšœ�œ�œ˜+——š�œ�œ˜!Jšœ.˜.Jšœ8˜8Jšœ˜—J˜—Jšœ�œ˜J˜3J™™