DIRECTORY FinchSmarts USING [ConvDesc, CurrentRName, ObtainServiceInterface, ReportSystemStateProc, ReportConversationStateProc, ReportRequestStateProc, RegisterForReports, ServiceConnect], Atom USING [GetPName, GetPropFromList, PutPropOnList], IO, Process USING [SecondsToTicks, SetTimeout, Ticks], RefID USING [ID, nullID], Rope USING [ROPE, Concat], ThParty USING [ PartyInfo ], Thrush USING [ActionClass, ActionType, ConversationID, InterfaceSpec, none, nullConvID, nullID, PartyID, SHHH], VoiceRopeServer, VoiceRopeServerSunImport USING [ImportInterface, ReportProblem], VoiceUtils USING [FindWhere, Problem, ProblemFR, RegisterWhereToReport, WhereProc ], VoiceUtilsExtras USING [GetUniqueID], VoiceRope; VoiceRopeImpl: CEDAR MONITOR IMPORTS Atom, FinchSmarts, IO, Process, Rope, VoiceRopeServer, VoiceRopeServerSunImport, VoiceUtils, VoiceUtilsExtras EXPORTS VoiceRope ~ BEGIN OPEN IO; ROPE: TYPE ~ Rope.ROPE; recordingService: ROPE _ "recording"; recordingServiceInterface: ROPE _ "VoiceRopeServer"; RequestID: TYPE = VoiceRope.RequestID; PendingRequest: TYPE = REF PendingRequestBody; PendingRequestBody: TYPE = RECORD [ id: RequestID, class: Thrush.ActionClass _ $unknown, state: Thrush.ActionType _ $unknown ]; PendingRequests: TYPE = LIST OF PendingRequest; VoiceRopeInfo: TYPE = REF VoiceRopeInfoBody; VoiceRopeInfoBody: TYPE = RECORD [ serviceID: RefID.ID _ RefID.nullID, shhh: Thrush.SHHH _ Thrush.none ]; VoiceRopeDesc: TYPE = REF VoiceRopeDescBody; VoiceRopeDescBody: TYPE = RECORD [ cDesc: FinchSmarts.ConvDesc _ NIL, -- only valid during operations below. serviceID: RefID.ID _ RefID.nullID, shhh: Thrush.SHHH _ Thrush.none, pendingRequests: PendingRequests _ NIL, reportArrived: CONDITION, clientData: REF_NIL, paused: BOOL _ FALSE ]; info: VoiceRopeInfo _ NEW[VoiceRopeInfoBody]; Handle: TYPE = VoiceRope.Handle; -- handles are no longer used for anything Open: PUBLIC PROC [voiceRopeDBName: ROPE _ NIL, localName: ROPE _ NIL, Complain: PROC[complaint: ROPE] _ NIL] RETURNS [handle: Handle] ~ { RETURN[NIL]; -- this routine is no longer used, but it probably should be! }; Record: PUBLIC PROC[handle: Handle_NIL] RETURNS [voiceRope: VoiceRope.VoiceRope_NIL] ~ { [voiceRope: voiceRope] _ RecordNB[handle: handle]; }; Play: PUBLIC PROC[handle: Handle_NIL, voiceRope: VoiceRope.VoiceRope, queueIt: BOOL_TRUE, wait: BOOL_FALSE] ~ { [] _ PlayNB[handle: handle, voiceRope: voiceRope, queueIt: queueIt, wait: wait]; }; Stop: PUBLIC PROC[handle: Handle_NIL] ~ { [] _ StopNB[handle: handle]; }; RecordNB: PUBLIC PROC [handle: Handle_NIL, requestID: RequestID_VoiceRope.nullRequestID, convID: Thrush.ConversationID_ Thrush.nullConvID, clientData: REF_NIL, selfElseOther: BOOL_TRUE] RETURNS [nb: VoiceRope.NB, voiceRope: VoiceRope.VoiceRope_NIL, newConvID: Thrush.ConversationID_ Thrush.nullConvID] ~ { vrDesc: VoiceRopeDesc; recordedPartyID: Thrush.PartyID _ Thrush.nullID; pInfo: ThParty.PartyInfo; [nb, vrDesc] _ GetConversation[convID: convID, clientData: clientData, addOK: TRUE]; IF nb # $success THEN RETURN; newConvID _ vrDesc.cDesc.situation.self.convID; requestID _ AddRequest[vrDesc, requestID, $record]; pInfo _ vrDesc.cDesc.partyInfo; IF pInfo#NIL THEN { recordedPartyID _ pInfo[pInfo.ixSelf].partyID; IF ~selfElseOther THEN FOR ix: NAT IN [1..pInfo.numParties] DO IF ix=pInfo.ixSelf OR pInfo[ix].type=$service THEN LOOP; recordedPartyID _ pInfo[ix].partyID; EXIT; ENDLOOP; }; [nb, voiceRope] _ VoiceRopeServer.Record[shhh: vrDesc.shhh, credentials: vrDesc.cDesc.situation.self, recordedParty: recordedPartyID, serviceID: vrDesc.serviceID, intID: requestID, queueIt: TRUE]; IF nb#$success THEN { VoiceUtils.Problem[Rope.Concat["Attempt to record failed - ",Atom.GetPName[nb]], $VoiceRope]; RETURN; }; WaitForRequestState[vrDesc: vrDesc, state: $finished, id: requestID]; [nb, voiceRope.length] _ VoiceRopeServer.Length[vrDesc.shhh, voiceRope]; IF nb#$success THEN { VoiceUtils.Problem[Rope.Concat["Attempt to get voicerope length failed - ",Atom.GetPName[nb]], $VoiceRope]; nb _ $noLength; RETURN; }; }; PlayNB: PUBLIC PROC [handle: Handle_NIL, voiceRope: VoiceRope.VoiceRope, queueIt: BOOL_TRUE, wait: BOOL_FALSE, requestID: RequestID_VoiceRope.nullRequestID, convID: Thrush.ConversationID_Thrush.nullConvID, clientData: REF_NIL] RETURNS [nb: VoiceRope.NB, newConvID: Thrush.ConversationID _ Thrush.nullConvID] ~ { vrDesc: VoiceRopeDesc; [nb, vrDesc] _ GetConversation[convID, clientData]; IF nb # $success THEN RETURN; newConvID _ vrDesc.cDesc.situation.self.convID; requestID _ AddRequest[vrDesc, requestID, $playback]; nb _ VoiceRopeServer.Play[ vrDesc.shhh, voiceRope, vrDesc.cDesc.situation.self, vrDesc.serviceID, requestID, queueIt]; IF nb#$success THEN { VoiceUtils.Problem[Rope.Concat["Attempt to play voicerope failed - ",Atom.GetPName[nb]], $VoiceRope]; RETURN; }; IF wait THEN WaitForRequestState[vrDesc: vrDesc, state: $started, id: requestID]; }; StopNB: PUBLIC PROC [handle: Handle _ NIL, convID: Thrush.ConversationID_ Thrush.nullConvID] RETURNS [nb: VoiceRope.NB] ~ { vrDesc: VoiceRopeDesc; [nb, vrDesc] _ GetConversation[convID, NIL, FALSE]; IF nb#$success THEN RETURN; nb _ VoiceRopeServer.Stop[vrDesc.shhh, vrDesc.cDesc.situation.self, vrDesc.serviceID]; IF nb#$success THEN { VoiceUtils.Problem[Rope.Concat["Attempt to stop failed - ",Atom.GetPName[nb]], $VoiceRope]; RETURN; }; }; PauseNB: PUBLIC PROC [handle: Handle _ NIL, convID: Thrush.ConversationID_ Thrush.nullConvID] RETURNS [nb: VoiceRope.NB] ~ { vrDesc: VoiceRopeDesc; [nb, vrDesc] _ GetConversation[convID, NIL, FALSE]; IF nb#$success THEN RETURN; nb _ VoiceRopeServer.Pause[vrDesc.shhh, vrDesc.cDesc.situation.self, vrDesc.serviceID]; IF nb#$success THEN { VoiceUtils.Problem[Rope.Concat["Attempt to pause failed - ",Atom.GetPName[nb]], $VoiceRope]; RETURN; }; vrDesc.paused _ TRUE; }; ResumeNB: PUBLIC PROC [handle: Handle _ NIL, convID: Thrush.ConversationID_ Thrush.nullConvID] RETURNS [nb: VoiceRope.NB] ~ { vrDesc: VoiceRopeDesc; [nb, vrDesc] _ GetConversation[convID, NIL, FALSE]; IF nb#$success THEN RETURN; nb _ VoiceRopeServer.Resume[vrDesc.shhh, vrDesc.cDesc.situation.self, vrDesc.serviceID]; IF nb#$success THEN { VoiceUtils.Problem[Rope.Concat["Attempt to resume failed - ",Atom.GetPName[nb]], $VoiceRope]; RETURN; }; vrDesc.paused _ FALSE; }; Retain: PUBLIC PROC [handle: Handle _ NIL, vr: VoiceRope.VoiceRope, class: VoiceRope.InterestClass, refID: ROPE, other: ROPE _ NIL] ~ { nb: VoiceRope.NB; IF NOT RecordingServiceInterface[] THEN RETURN; nb _ VoiceRopeServer.Retain[info.shhh, FinchSmarts.CurrentRName[], vr, class, refID, other]; IF nb#$success THEN { VoiceUtils.Problem[Rope.Concat["Attempt to retain voicerope failed - ",Atom.GetPName[nb]], $VoiceRope]; RETURN; }; }; Forget: PUBLIC PROC [handle: Handle _ NIL, vr: VoiceRope.VoiceRope, class: VoiceRope.InterestClass, refID: ROPE] ~ { nb: VoiceRope.NB; IF NOT RecordingServiceInterface[] THEN RETURN; nb _ VoiceRopeServer.Forget[info.shhh, vr, class, refID]; IF nb#$success THEN { VoiceUtils.Problem[Rope.Concat["Attempt to forget voicerope failed - ",Atom.GetPName[nb]], $VoiceRope]; RETURN; }; }; GetByInterest: PUBLIC PROC [handle: Handle _ NIL, class: VoiceRope.InterestClass, refID: ROPE] RETURNS [voiceRope: VoiceRope.VoiceRope] ~ { nb: VoiceRope.NB; IF NOT RecordingServiceInterface[] THEN RETURN; [nb, voiceRope] _ VoiceRopeServer.GetByInterest[info.shhh, class, refID]; IF nb#$success THEN { VoiceUtils.Problem[Rope.Concat["Attempt to lookup voicerope by interest failed - ",Atom.GetPName[nb]], $VoiceRope]; RETURN; }; }; Cat: PUBLIC PROC [handle: Handle _ NIL, vr1, vr2, vr3, vr4, vr5: VoiceRope.VoiceRope _ NIL] RETURNS [new: VoiceRope.VoiceRope_NIL] ~ { nb: VoiceRope.NB; IF NOT RecordingServiceInterface[] THEN RETURN; [nb, new] _ VoiceRopeServer.Cat[info.shhh, FinchSmarts.CurrentRName[], vr1, vr2, vr3, vr4, vr5]; IF nb#$success THEN { VoiceUtils.Problem[Rope.Concat["Attempt to cat voiceropes failed - ",Atom.GetPName[nb]], $VoiceRope]; RETURN; }; }; Substr: PUBLIC PROC [handle: Handle _ NIL, vr: VoiceRope.VoiceRope, start: INT _ 0, len: INT _ LAST[INT]] RETURNS [new: VoiceRope.VoiceRope_NIL] ~ { nb: VoiceRope.NB; IF NOT RecordingServiceInterface[] THEN RETURN; [nb, new] _ VoiceRopeServer.Substr[info.shhh, FinchSmarts.CurrentRName[], vr, start, len]; IF nb#$success THEN { VoiceUtils.Problem[Rope.Concat["Attempt to substring voicerope failed - ",Atom.GetPName[nb]], $VoiceRope]; RETURN; }; }; Replace: PUBLIC PROC [handle: Handle _ NIL, vr: VoiceRope.VoiceRope, start: INT _ 0, len: INT _ LAST[INT], with: VoiceRope.VoiceRope _ NIL] RETURNS [new: VoiceRope.VoiceRope_NIL] ~ { nb: VoiceRope.NB; IF NOT RecordingServiceInterface[] THEN RETURN; [nb, new] _ VoiceRopeServer.Replace[info.shhh, FinchSmarts.CurrentRName[], vr, start, len, with]; IF nb#$success THEN { VoiceUtils.Problem[Rope.Concat["Attempt to replace part of a voicerope failed - ",Atom.GetPName[nb]], $VoiceRope]; RETURN; }; }; Length: PUBLIC PROC [handle: Handle _ NIL, vr: VoiceRope.VoiceRope] RETURNS [len: INT_1] ~ { nb: VoiceRope.NB; IF NOT RecordingServiceInterface[] THEN RETURN; [nb, len] _ VoiceRopeServer.Length[info.shhh, vr]; IF nb#$success THEN { VoiceUtils.Problem[Rope.Concat["Attempt to get voicerope length failed - ",Atom.GetPName[nb]], $VoiceRope]; RETURN; }; }; DescribeRope: PUBLIC PROC [ handle: Handle _ NIL, vr: VoiceRope.VoiceRope, minSilence: INT _ -1] RETURNS [noise: VoiceRope.IntervalSpecs_NIL] ~ { nb: VoiceRope.NB; len: INT; IF NOT RecordingServiceInterface[] THEN RETURN; [nb, len, noise] _ VoiceRopeServer.DescribeRope[info.shhh, vr, minSilence]; IF nb#$success THEN { VoiceUtils.Problem[Rope.Concat["Attempt to describe voicerope failed - ",Atom.GetPName[nb]], $VoiceRope]; RETURN; }; }; SystemReport: FinchSmarts.ReportSystemStateProc ~ { NULL; }; ConversationReport: FinchSmarts.ReportConversationStateProc ~ { vrDesc: VoiceRopeDesc _ GetVRDesc[cDesc]; playbacksPending: BOOL _ FALSE; IF vrDesc = NIL THEN RETURN; IF vrDesc.pendingRequests#NIL THEN FOR pR: PendingRequests _ vrDesc.pendingRequests, pR.rest WHILE pR#NIL DO IF pR.first.class = $record THEN EXIT; REPEAT FINISHED => playbacksPending _ TRUE; ENDLOOP; SELECT cDesc.situation.self.state FROM $active => IF playbacksPending THEN [] _ ResumeNB[convID: cDesc.situation.self.convID]; $inactive => IF playbacksPending THEN [] _ PauseNB[convID: cDesc.situation.self.convID]; $idle, $neverWas, $failed => { -- throw away pending requests RemoveRequests[vrDesc, NIL]; NotifyWaiters[vrDesc]; }; ENDCASE; }; RequestReport: FinchSmarts.ReportRequestStateProc ~ { request: PendingRequest; vrDesc: VoiceRopeDesc _ GetVRDesc[cDesc]; betterActionRequest _ actionRequest; IF vrDesc = NIL THEN RETURN; -- leave actionRequest alone SELECT actionReport.actionClass FROM $recording, $playback => { request _ GetRequestByID[vrDesc, actionReport.actionID]; IF request = NIL THEN RETURN[NIL]; request.state _ actionReport.actionType; SELECT request.state FROM $scheduled, $started => NULL; $finished, $flushed => RemoveRequests[vrDesc, request]; ENDCASE; NotifyWaiters[vrDesc]; }; ENDCASE; RETURN; -- leave actionRequest alone }; RelayProblems: VoiceRopeServerSunImport.ReportProblem ~ { VoiceUtils.ProblemFR["%g: %g", $VoiceRope, NIL, atom[ec], rope[expl]]; }; GetRequestByID: ENTRY PROC [vrDesc: VoiceRopeDesc, id: RequestID] RETURNS [request: PendingRequest _ NIL] ~ { IF vrDesc=NIL THEN RETURN; FOR q: PendingRequests _ vrDesc.pendingRequests, q.rest WHILE q # NIL DO IF q.first.id = id THEN RETURN[q.first]; ENDLOOP; }; AddRequest: ENTRY PROC [vrDesc: VoiceRopeDesc, id: RequestID, class: Thrush.ActionClass _ $unknown] RETURNS [requestID: RequestID] ~ { request: PendingRequest _ NEW[PendingRequestBody _ [requestID _ IF id=VoiceRope.nullRequestID THEN NewRequestID[] ELSE id, class]]; vrDesc.pendingRequests _ CONS[request, vrDesc.pendingRequests]; }; RemoveRequests: ENTRY PROC [vrDesc: VoiceRopeDesc, request: PendingRequest] RETURNS [] ~ { IF vrDesc=NIL THEN RETURN; IF request = NIL OR vrDesc.pendingRequests = NIL OR vrDesc.pendingRequests.first = request THEN vrDesc.pendingRequests _ NIL ELSE FOR q: PendingRequests _ vrDesc.pendingRequests, q.rest WHILE q.rest # NIL DO IF q.rest.first = request THEN { q.rest _ NIL; -- truncate queue to remove this and all previous requests EXIT; }; ENDLOOP; }; NotifyWaiters: ENTRY PROC [vrDesc: VoiceRopeDesc] RETURNS [] ~ { ENABLE UNWIND => NULL; IF vrDesc=NIL THEN RETURN; BROADCAST vrDesc.reportArrived; }; Wait: ENTRY PROC [vrDesc: VoiceRopeDesc] RETURNS [] ~ { ENABLE UNWIND => NULL; IF vrDesc=NIL THEN RETURN; WAIT vrDesc.reportArrived; }; WaitForRequestState: PROC [vrDesc: VoiceRopeDesc, state: Thrush.ActionType, id: RequestID] RETURNS [] ~ { request: PendingRequest; timeout: Process.Ticks _ IF state = $started THEN Process.SecondsToTicks[2] ELSE Process.SecondsToTicks[10]; IF vrDesc=NIL THEN RETURN; TRUSTED { Process.SetTimeout[@vrDesc.reportArrived, timeout]; }; DO request _ GetRequestByID[vrDesc, id]; IF request = NIL THEN EXIT; -- completed or conv went idle SELECT state FROM $scheduled => SELECT request.state FROM $scheduled, $started, $finished, $flushed => EXIT; ENDCASE => NULL; -- unknown? $started => SELECT request.state FROM $started, $finished, $flushed => EXIT; ENDCASE => NULL; -- probably $scheduled $finished => SELECT request.state FROM $finished, $flushed => EXIT; ENDCASE => NULL; -- probably $scheduled or $started ENDCASE => EXIT; Wait[vrDesc]; ENDLOOP; }; GetClientData: PUBLIC PROC[handle: Handle_NIL, cDesc: FinchSmarts.ConvDesc] RETURNS[clientData: REF_NIL] = { vrDesc: VoiceRopeDesc _ GetVRDesc[cDesc]; IF vrDesc#NIL THEN { -- vrDesc.cDesc_NIL; -- RETURN[vrDesc.clientData]; }; }; GetVRDesc: PROC[cDesc: FinchSmarts.ConvDesc] RETURNS[vrDesc: VoiceRopeDesc_NIL] = { IF cDesc#NIL THEN vrDesc _ NARROW[Atom.GetPropFromList[cDesc.props, $VoiceRopeDesc]]; IF vrDesc#NIL THEN vrDesc.cDesc _ cDesc; }; GetConversation: PROC [ convID: Thrush.ConversationID, clientData: REF_NIL, createOK: BOOL_TRUE, addOK: BOOL_FALSE] RETURNS [nb: VoiceRope.NB_ $success, vrDesc: VoiceRopeDesc _ NIL] ~ { cDesc: FinchSmarts.ConvDesc; [nb, cDesc] _ FinchSmarts.ServiceConnect[recordingService, convID, createOK, addOK]; IF nb#$success THEN { VoiceUtils.Problem[ Rope.Concat["Can't establish conversation - ",Atom.GetPName[nb]], $VoiceRope]; RETURN; }; vrDesc _ GetVRDesc[cDesc]; IF vrDesc=NIL THEN { vrDesc _ NEW[VoiceRopeDescBody _ []]; cDesc.props _ Atom.PutPropOnList[cDesc.props, $VoiceRopeDesc, vrDesc]; vrDesc.cDesc _ cDesc; }; IF clientData#NIL THEN vrDesc.clientData _ clientData; IF ~RecordingServiceInterface[vrDesc] THEN RETURN[$noServiceInterface, NIL]; }; RecordingServiceInterface: PROC[vrDesc: VoiceRopeDesc _ NIL] RETURNS [imported: BOOLEAN _ TRUE] = { nb: VoiceRope.NB; interfaceSpec: Thrush.InterfaceSpec; cDesc: FinchSmarts.ConvDesc _ IF vrDesc=NIL THEN NIL ELSE vrDesc.cDesc; IF vrDesc#NIL AND vrDesc.serviceID#RefID.nullID THEN RETURN[TRUE]; [nb, interfaceSpec] _ FinchSmarts.ObtainServiceInterface[recordingService, recordingServiceInterface, cDesc]; IF nb#$success THEN RETURN[FALSE]; IF vrDesc=NIL AND interfaceSpec.serviceID=info.serviceID THEN RETURN[TRUE]; info.shhh _ VoiceRopeServerSunImport.ImportInterface[instance: interfaceSpec.interfaceName.instance, reportProblem: RelayProblems, reportData: info]; info.serviceID _ interfaceSpec.serviceID; IF vrDesc#NIL THEN { vrDesc.serviceID _ info.serviceID; vrDesc.shhh _ info.shhh; }; RETURN[TRUE]; }; NewRequestID: PUBLIC PROC RETURNS [VoiceRope.RequestID] = { RETURN[LOOPHOLE[VoiceUtilsExtras.GetUniqueID[]]]}; VoiceRopeWhere: VoiceUtils.WhereProc = { RETURN[VoiceUtils.FindWhere[$Finch, whereData]]; }; FinchSmarts.RegisterForReports[key: $VoiceRope, c: ConversationReport, r: RequestReport]; VoiceUtils.RegisterWhereToReport[proc: VoiceRopeWhere, where: $VoiceRope]; END. !VoiceRopeImpl.mesa Copyright ำ 1986, 1988, 1989, 1990 by Xerox Corporation. All rights reserved. Doug Terry, October 14, 1986 3:59:55 pm PDT Venkat Rangan October 27, 1989 5:13:05 pm PDT Swinehart, September 20, 1990 5:44:37 pm PDT Polle Zellweger, December 27, 1990 6:03 pm PST Client code for interacting with the Voice Rope Service. Probably needs more reporting when RPC calls fail and reconnects fail, too. VoiceRope client interface If voiceRopeDBName or localName is omitted, a default based on the user profile choice of Thrush Server is invented. If Complain is omitted, Log.Problem[...$Finch] is used. Records a voice rope, registers it , and returns its ID. A NIL return value indicates that something went wrong. Create or find conversation, import service interface, record client data if provided OK to add service provider to an existing conversation (in order to record it.) Perhaps this should result in the termination of the conversation? It doesn't, automatically, now. This comment should be read implicitly after all other nb tests. wait for report that says it finished (or was abandoned) vrDesc.cDesc _ NIL; -- Break cycles when conversation goes away Play a specified voice rope. The boolean arguments are interpreted as follows: queueIt => play after all other record/playback requests are satisfied. wait => wait until things appear to be started properly, or have failed. vrDesc.cDesc _ NIL; Stops any recording or playback in progress. vrDesc.cDesc _ NIL; Stops any recording or playback in progress. Stops any recording or playback in progress. Registers a new interest in the voice rope. The voice rope will be retained until either a corresponding Forget is done or the class' garbage collection process determines that the voice rope is no longer referenced, e.g. refID no longer exists. Taken together, the vr, class, and refID must be unique. Repeated calls of Retain with the same parameters (ignoring other) will only register a single interest. The specified refID of the specified class drops its interest in the voice rope. The voice rope is not necessarily deleted, however, since other interests in the same voice rope may exist. Returns any voice rope that is of interest to the given class and refID; returns NIL if no such voice rope exists. Concatenates together the non-NIL voice ropes to produce a new voice rope. Creates a new voice rope that is a substring of an existing voice rope. Creates a new voice rope in which the given interval of the voice rope "vr" is replaced by the voice rope "with". Returns the actual length of the voice rope. This operation ignores the start and length values specified in the voice rope. Thus, vr.start _ 0; vr.length _ Length[handle, vr] will restore a voice rope to its full contents. Report handling Action reports are generated by the voice rope server when a request for $recording/$playback is $scheduled, $started, $finished, or $flushed. Reports are also available from FinchSmarts concerning the state of a conversation or Finch in general. A list of pending requests is maintained as part of VoiceRopeInfo. This list is sorted so that the most recent request is at the head of the list. Upon receipt of a $finished or $flushed report, the associated request (and all previous requests) are removed from the pending request queue. All pending requests are flushed if the established conversation goes idle (i.e. the user hangs up). The condition, reportArrived, is raised whenever a new report is received. Procedures that wish to wait for a given action report may wait on this condition; they should then check the state of the pending request queue and take appropriate action (such as waiting again). Don't care about changes in system state at the moment. [ nb: NB, cDesc: ConvDesc, remark: ROPE_NIL ] [ cDesc: ConvDesc, actionReport: Thrush.ActionReport, actionRequest: REF ] RETURNS [betterActionRequest: REF] Throw away pending requests that are complete. This includes requests preceding the one being reported on. The following routines manage the queue of pending requests. (They are the only monitor entry procedures in this module.) Establishing conversations and RPC connections Initializations Doug Terry, August 14, 1986 9:40:42 am PDT changes to: DIRECTORY, VoiceRopeClientImpl, EXPORTS, ~, Open, Record, Play, Stop, Retain, Forget, GetByInterest, Cat, Substr, Replace, Length, DescribeRope, Proc, Proc Doug Terry, August 14, 1986 9:58:55 am PDT changes to: DIRECTORY, VoiceRopeClientImpl, EXPORTS, ~, Open, Record, Play, Stop, Retain, Forget, GetByInterest, Cat, Substr, Replace, Length, DescribeRope, VoiceRopeInterface, Proc, Proc, END Doug Terry, August 27, 1986 11:43:29 am PDT changes to: DIRECTORY, VoiceRopeImpl, ~, Open, Play, Stop, VoiceRopeInterface, DescribeRope Doug Terry, August 29, 1986 4:13:15 pm PDT changes to: ~, Play, VoiceRopeImpl, Record, Stop, NewID, Proc, Retain, Forget, GetByInterest, Cat, Substr, Replace, Length, DescribeRope, RecordingServiceInterface Doug Terry, September 5, 1986 4:28:37 pm PDT changes to: DIRECTORY, IMPORTS, EXPORTS, ~, Handle, Open, Proc Doug Terry, September 25, 1986 2:59:31 pm PDT changes to: DIRECTORY, SystemReport, ConversationReport, RequestReport, FinchSmarts, Record Doug Terry, September 29, 1986 5:56:59 pm PDT changes to: SystemReport, Record, ConversationReport, RequestReport, ~ Doug Terry, October 3, 1986 3:51:47 pm PDT changes to: ~, GetRequestByID, GetRequestByRef, NewRequest, RemoveRequests, NewID, Record, Play, ConversationReport, RequestReport, WaitForRequestState, DIRECTORY, IMPORTS, Retain, Forget, GetByInterest, Cat, Substr, Replace, Length, DescribeRope, RecordingServiceInterface, Stop, WaitForRequestState, Handle, Open Doug Terry, October 3, 1986 4:45:36 pm PDT changes to: RequestReport Doug Terry, October 6, 1986 1:44:44 pm PDT changes to: DIRECTORY, ~, Record, Play, Stop, Retain, Forget, GetByInterest, Cat, Substr, Replace, Length, DescribeRope, RecordingServiceInterface, IMPORTS Doug Terry, October 9, 1986 8:00:18 pm PDT changes to: Record, Stop, Retain, Forget, GetByInterest, Cat, Substr, Replace, Length, DescribeRope Doug Terry, October 9, 1986 8:45:25 pm PDT changes to: Wait, WaitForRequestState Doug Terry, October 14, 1986 3:59:55 pm PDT changes to: ConversationReport Polle Zellweger (PTZ) July 20, 1987 5:57:28 pm PDT Changes to allow TiogaVoice to monitor conversation state/reports about voicerope operations. changes to: Record, Play, Stop, NewID=>NewRequestID Polle Zellweger (PTZ) July 20, 1987 6:08:42 pm PDT changes to: DIRECTORY, Record, Play, Stop, Retain, Forget, GetByInterest, Cat, Substr, Replace, Length, DescribeRope, GetConversation, RecordingServiceInterface, NewRequestID Polle Zellweger (PTZ) July 21, 1987 12:04:32 pm PDT changes to: DIRECTORY, Record, Play, Record, Play, Stop, StopNB Swinehart, July 24, 1987 12:12:38 pm PDT Merge disparate changes to VoiceRope. Improve error and connection recovery; adapt to interface changes in FinchSmarts. changes to: DIRECTORY, ~, Open, Record, Play, queueIt, Stop, RecordNB, PlayNB, StopNB, Retain, Forget, GetByInterest, Cat, Substr, Replace, Length, DescribeRope, GetConversation, RecordingServiceInterface, NewRequestID Polle Zellweger (PTZ) July 27, 1987 12:20:16 pm PDT changes to: PlayNB, RecordNB, StopNB, GetClientData, GetConversation Polle Zellweger (PTZ) October 30, 1987 7:11:17 pm PST RequestReport was mistakenly returning a NIL betterRequestReport rather than returning its input. Didn't used to matter, but report callback registration now occurs in a different order than it used to. changes to: RequestReport Swinehart, February 26, 1988 2:52:44 pm PST Adding ability to record an ongoing call. changes to: RecordNB, PlayNB, StopNB, GetConversation Swinehart, February 28, 1988 11:29:32 pm PST Add (dangerous) Pause and Resume functions changes to: PauseNB, ResumeNB, Retain Polle Zellweger (PTZ) August 10, 1990 3:45:06 pm PDT Change to call via Sun RPC rather than Pup RPC. changes to: DIRECTORY, IMPORTS, RecordNB, PlayNB, StopNB, PauseNB, ResumeNB, Retain, Forget, GetByInterest, Cat, Substr, Replace, Length, DescribeRope, RecordingServiceInterface, NewRequestID Polle Zellweger (PTZ) December 27, 1990 5:58:33 pm PST Fix problem whereby all new voiceropes had voicerope ids listing Growler's last-logged-in-user as creator. Required widening VoiceRopeServer's interface. changes to: DIRECTORY, Retain, Cat, Substr, Replace ส]˜codešœ™KšœN™NK™+K™-Kšœ,™,Kšœ.™.—K˜K™8K™šฯk ˜ Kšœ œข˜ณKšœœ,˜6K˜Kšœœ%˜2Kšœœœ ˜Kšœœœ ˜Kšœœ˜Kšœœ]œ˜oKšœ˜Kšœœ"˜@Kšœ œD˜TKšœœ˜%Kšœ ˜ —K˜Kšะbl œœ˜Kšœn˜uKšœ ˜šœ˜K˜Kšœœœ˜K˜Kšœœ˜%Kšœœ˜4K˜Kšœ œ œ ˜&Kšœœœ˜.šœœœ˜#Kšœ˜Kšœ%˜%Kšœ#˜#K˜—Kšœœœœ˜/K˜Kšœœœ˜,šœœœ˜"Kšœœ˜#Kšœ œ˜K˜K˜—Kšœœœ˜,šœœœ˜"Kšœœฯc&˜IKšœœ˜#Kšœ œ˜ Jšœ#œ˜'Jšœ ˜Kšœ œœ˜Kšœœ˜K˜K˜—Kšœœ˜-J™JšœM™M—head™Kšœœ œ Ÿ*˜LK˜šฯnœœœœœ œœ œœ œœœ˜ŠJšœญ™ญKšœœŸ=˜KK˜K™—š œœœœ˜'Jšœ!œ;˜fJ˜—š œ œœ!˜Eš œ œœœœ˜)JšœS˜S—J˜—š œ œœ%˜IJ˜—š œœœœnœœœœœœ!œ:˜ฑK™qJšœ˜J˜0J˜šœNœ˜TJšœU™UJ™O—Jšœœœ˜J˜/Jšœ3˜3J˜šœœœ˜Jšœ.˜.š œœœœœ˜>Jšœœœœ˜8Jšœ$˜$Jšœœ˜—J˜—šœฯtœ ˜šœก˜)Jšก9œกœ กœก˜ˆ——šœ œ˜Kšœ]˜]Jšœ˜JšกœcกC™งJ˜—Jšœ8™8JšœE˜EJšœ6กœ ˜Hšœ œ˜Kšœk˜kJ˜Jšœ˜J˜—JšœœŸกŸ(ก™AK˜K™—š œœœœ+œœœœmœœœœ;˜ทšœO™OJšœG™GJšœH™H—Jšœ˜Jšœ3˜3Jšœœœ˜Jšœ/˜/Jšœ5˜5šœก˜Jš œกœ กœกœกœ กœ ก ˜[—šœ œ˜Jšœe˜eJšœ˜J˜—JšœœE˜QJšœœ™Kšœ˜K™—š  œœœœ4œœ˜{K™,Jšœ˜Jšœ'œœ˜3Jšœ œœ˜Jš œกœกœกœกœ ก˜Všœ œ˜Jšœ[˜[Jšœ˜J˜—Jšœœ™˜K™——š  œœœœ4œœ˜|K™,Jšœ˜Jšœ'œœ˜3Jšœ œœ˜Jš œกœกœกœกœ ก˜Wšœ œ˜Jšœ\˜\Jšœ˜J˜—Jšœœ˜˜K™——š  œœœœ4œœ˜}K™,Jšœ˜Jšœ'œœ˜3Jšœ œœ˜Jš œกœกœกœกœ ก˜Xšœ œ˜Jšœ]˜]Jšœ˜J˜—Jšœœ˜˜K™——š œœœœBœ œœ˜‡Kšœš™šJšœ˜Jšœœœœ˜0Jš œกœก œกœกœกœก˜\šœ œ˜Jšœg˜gJšœ˜J˜—K˜K™—š  œœœœBœ˜tKšœฝ™ฝJšœ˜Jšœœœœ˜0Jš œกœก œกœกœก˜9šœ œ˜Jšœg˜gJšœ˜J˜—K˜K™—š   œœœœ)œœ%˜‹Kšœr™rJšœ˜Jšœœœœ˜0Jšœ!กœ ก œกœก˜Išœ œ˜Jšœs˜sJšœ˜J˜—K˜K™—š œœœœ1œœœ˜†K™JJšœœ˜Jšœœœœ˜0Jšœกœก œ3ก˜`šœ œ˜Jšœe˜eJšœ˜J˜—K˜K™—š œœœœ"œ œœœœœ˜”K™GJšœ˜Jšœœœœ˜0Jšœกœก œ*ก˜Zšœ œ˜Jšœj˜jJšœ˜J˜—K˜K™—š œœœœ"œ œœœœœœ˜ถK™qJšœ˜Jšœœœœ˜0Jšœกœก œ0ก˜ašœ œ˜Jšœr˜rJšœ˜J˜—K˜K™—š  œœœœœœ˜\K™แJšœ˜Jšœœœœ˜0Jšœกœก œก˜2šœ œ˜Jšœk˜kJšœ˜Jšœ˜—Kšœ˜K™—š  œœœ˜Kšœœ'œ˜DKšœ!œ˜0Jšœ˜Jšœœ˜ Jšœœœœ˜0Jšœ"กœ ก œก˜Kšœ œ˜Jšœi˜iJšœ˜J˜—K˜K™——™Kšœ๗™๗K™Kšœœ™œK˜š  œ'˜3Kšœ7™7Kšœ˜K˜K˜—š œ-˜?Kšœœœœ™-Jšœ)˜)Jšœœœ˜Jšœ œœœ˜šœœ˜"šœ7œœ˜IJšœœœ˜&Jšœœœ˜+Jšœ˜——šœ˜&Jšœ œœ4˜WJšœ ะktกขกœ2˜Xšœ Ÿ˜>Kšœœ˜Kšœ˜Jšœ˜—Jšœ˜—K˜K˜—š  œ(˜5KšœEœœœ™mJšœ˜Jšœ)˜)Jšœ$˜$Jš œ œœœŸ˜:šœ˜$šœ˜Kšœ8˜8Kš œ œœœœ˜"Kšœ(˜(šœ˜Kšœœ˜K™kšœ˜Kšœ!˜!—Kšœ˜—Kšœ˜K˜—Kšœ˜—KšœŸ˜%Kšœ˜K˜—š  œ,˜9Jšœ+œ˜FJ˜K˜K˜—K™zK˜š œ œ(œœ˜mKšœœœœ˜šœ5œœ˜HKšœœœ ˜(Kšœ˜—K˜K˜—š  œœœNœ˜†šœœ˜2Kšœ œœœ ˜P—Kšœœ"˜?K˜K˜—š œ œ2œ˜ZKšœœœœ˜š œ œœœœ(˜_Kšœ˜—š˜šœ5œ œ˜Mšœœ˜ Kšœ œŸ:˜IKšœ˜K˜—Kšœ˜——K˜K˜—š  œ œœ˜@Kšœœœ˜Kšœœœœ˜Kš œ˜K˜—K˜š œ œœ˜7Kšœœœ˜Kšœœœœ˜Kšœ˜K˜—K˜š œœBœ˜iJšœ˜Kšœœœœ˜lJšœœœœ˜Jšœ9˜@š˜Kšœ%˜%Kš œ œœœŸ˜;šœ˜šœœ˜'Kšœ-œ˜2KšœœŸ ˜—šœ œ˜%Kšœ!œ˜&KšœœŸ˜(—šœ œ˜&Kšœœ˜KšœœŸ"˜4—Kšœœ˜—Kšœ ˜ Kšœ˜—K˜K˜—š  œœœœ˜KKšœ œ˜ Jšœ)˜)Kš œœœŸœœ˜JK˜——™.š  œœœœ˜Sšœœ˜Jšœ œ4˜C—Kšœœœ˜(K˜K˜—š œœ˜Kšœ+œœ˜3Kš œ œœ œœ˜'Kšœœ$œ˜EJ˜JšœT˜Tšœ œ˜šœ˜JšœN˜N—Jšœ˜J˜—Kšœ˜šœœœ˜Kšœ œ˜%JšœF˜FJ˜J˜—Jšœ œœ ˜6Kšœ$œœœ˜LK˜—J˜š  œœœœ œœ˜cJšœ˜Jšœ$˜$Jš œœœœœœ˜GJš œœœœœœ˜Bšœ˜JšœW˜W—Jšœ œœœ˜"Jš œœœ(œœœ˜KKšœtก œก˜•Jšœ)˜)JšœœœA˜SJšœœ˜ J˜J˜—š  œœœœ˜;Jšœœ#˜2J˜—•StartOfExpansionK -- [fixedWhereData: REF ANY, whereData: REF ANY] RETURNS [s: STREAM _ NIL]š œ˜(Jšœ*˜0J˜——™KšœY˜YJ˜J—K˜Kšœ˜™*Kšœ ะkr ฯrฃคt™ง—™*Kšœ ฃ คฃคŠฃ™ภ—™+Kšœ ฃ คF™[—™*Kšœ ค—™ฃ—™,Kšœ ฃ คฃคฃค™>—™-Kšœ ฃ คF™[—™-Kšœ ค:™F—™*Kšœ คฃ คฃค™บ—™*Kšœ ค ™—™*Kšœ ฃ คฃ™›—™*Kšœ คW™c—™*Kšœ ค™%—™+Kšœ ค™—šœ2™2Kšœ]™]Kšœ ค'™3—šœ2™2Kšœ ฃ ค™™ฎ—šœ3™3Kšœ ฃ ค*™?—™(K™xKšœ ฃ คล™ฺ—™3Kšœ ค8™D—™5Kšค œพ™หKšœ ค ™—™+K™)Kšœ ค)™5—™,K™*Kšœ ค™%—™4K™/Kšœ ฃ คฃคก™ฟ—™6K™šKšœ ฃ ค™3——…—=สtC