DIRECTORY Atom USING [ DottedPair, DottedPairNode ], IO, Commander USING [ CommandProc, Register ], Lark USING [ bStar, bThorp, ConnectionSpec, ConnectionSpecRec, CommandEvent, CommandEvents, CommandEventSequence, Device, disabled, EchoParameters, EchoParameterRecord, enabled, endNum, Event, KeyTable, Milliseconds, o3i1, o2i2, o1i1, Passel, SHHH, StatusEvent, Tone, ToneSpec, ToneSpecRec, VoiceBuffer ], LarkOps USING [ LarkParameters, LarkParametersRec, ParamCode ], LarkPlay USING [ ToneList, ToneSpec, ToneSpecRec ], LarkRemoteControl USING [ BootLark ], LarkOpsRpcControl, LarkSmarts, MBQueue USING [ QueueClientAction ], Multicast USING [ HandleMulticast, StopHandlingMulticast ], Nice, Process USING [ Detach, EnableAborts, MsecToTicks, SetTimeout ], Pup USING [ nullSocket ], Rope USING [ Concat, Fetch, Length, ROPE ], RPC USING [ CallFailed, SetMaxTransmissions ], ThNet USING [ pd ], ThParty USING [ PartyInfo ], ThPartyPrivate USING [ SmartsData ], Thrush USING[ NetAddress, ROPE, SHHH, SmartsID ], ThSmarts USING [ noneScheduled ], ThSmartsPrivate USING [ ConvDesc, Deregister, FlashDone, LarkCall, LarkInfo, LarkState, LSwitches, LState, ProgressTones, SmartsInfo, SwitchState ], TU USING [ RefAddr ], VoiceUtils USING [ ProblemFR, Report, ReportFR ] ; LarkOutImpl: CEDAR MONITOR LOCKS info USING info: LarkInfo IMPORTS Commander, IO, LarkOpsRpcControl, LarkRemoteControl, MBQueue, Multicast, Nice, Process, Rope, RPC, ThNet, ThSmartsPrivate, TU, VoiceUtils EXPORTS ThSmartsPrivate= { OPEN IO; ConvDesc: TYPE = ThSmartsPrivate.ConvDesc; LarkInfo: TYPE = ThSmartsPrivate.LarkInfo; LarkState: TYPE = ThSmartsPrivate.LarkState; SmartsData: TYPE = ThPartyPrivate.SmartsData; SmartsInfo: TYPE = ThSmartsPrivate.SmartsInfo; SmartsID: TYPE = Thrush.SmartsID; ROPE: TYPE = Thrush.ROPE; firstTone: LarkState = FIRST[ThSmartsPrivate.ProgressTones]; bStar: Lark.Event = Lark.bStar; bThorp: Lark.Event = Lark.bThorp; enabled: Lark.Event = Lark.enabled; endNum: Lark.Event = Lark.endNum; disabled: Lark.Event = Lark.disabled; TimeoutPolicy: TYPE = { never, always, unlessDebugging}; PD: TYPE = RECORD [ waitForTelco: CARDINAL _ 500, -- default values for initial pause, on time, and off time telcoMinOn: CARDINAL _ 60, -- when generating touch-tones. telcoMinOff: CARDINAL _ 60, flashWaitTime: CARDINAL _ 1200, idleWaitTime: CARDINAL _ 20000, -- time for supervisor to wait for more events. keySynchTime: CARDINAL _ 3000, -- time for keys to be distributed, else give up blinkWaitTime: CARDINAL _ 500, -- if LED is blinking, don't wait so long. backDoorOH: BOOL_FALSE, toLarkTimeoutOK: TimeoutPolicy _ unlessDebugging, informLarkOK: TimeoutPolicy _ unlessDebugging, toLarkTimeoutTransmissions: CARDINAL _ 4, -- How long to give a lark to respond fromLarkTimeoutOK: TimeoutPolicy _ unlessDebugging, fromLarkTimeoutTransmissions: CARDINAL _ 5, -- how long to give the server. larkEventTimer: CARDINAL _ 6000, larkTimeoutEvents: TimeoutPolicy _ unlessDebugging, tonesInvalid: BOOL_TRUE, noisyFeeps: BOOL_FALSE -- TRUE if caller is to hear automatically-generated touchtones. ]; pd: REF PD _ NEW[PD_[]]; dialTone: LarkPlay.ToneSpec _ NIL; busyTone: LarkPlay.ToneSpec _ NIL; errorTone: LarkPlay.ToneSpec _ NIL; ringbackTone: LarkPlay.ToneSpec _ NIL; quenchSpec: Lark.ToneSpec _ NEW[Lark.ToneSpecRec _[volume: 0, totalTime: 0, tones: LIST[[0,0,0,0]]]]; EnterLarkState: PUBLIC ENTRY PROC[ info: LarkInfo, newState: LarkState, data: LIST OF REF ANY_NIL ] = { ENABLE UNWIND=>NULL; EnterLarkSt[info, newState, data]; }; EnterLarkSt: PUBLIC INTERNAL PROC[ info: LarkInfo, newState: LarkState, data: LIST OF REF ANY ] = { trans: LSTrans _ lsTrans[info.larkState][newState]; oldState: LarkState = info.larkState; sw: BOOL_FALSE; otherAction: REF_NIL; connectionSpec: ThParty.PartyInfo; toneSpec: LarkPlay.ToneSpec; ropeSpec: Rope.ROPE; keyTable: Lark.KeyTable; feepSpecs: REF AFeepType_NIL; keyTablesToDate: INT; larkCall: ThSmartsPrivate.LarkCall _ NIL; -- See ThSmartsPrivate.LarkCall IF info.failed THEN { FailInt[info, "Additional requests disregarded", FALSE]; RETURN; }; IF pd.tonesInvalid THEN SetTones[]; FOR dL: LIST OF REF ANY_data, dL.rest WHILE dL#NIL DO WITH dL.first SELECT FROM cS: ThParty.PartyInfo => connectionSpec _ cS; tS: LarkPlay.ToneSpec => toneSpec _ tS; kT: Lark.KeyTable => keyTable _ kT; lC: ThSmartsPrivate.LarkCall => larkCall _ lC; -- see ThSmartsPrivate.LarkCall dP: Atom.DottedPair => SELECT dP.key FROM $audioSource => {info.audioSource _ NARROW[dP.val]; sw_TRUE; }; $transmitOnly => {info.transmitOnly _ dP.val=$true;sw_TRUE; }; $textToSpeech => info.textToSpeech _ dP.val=$true; $phoneNumber => ropeSpec _ NARROW[dP.val]; ENDCASE; -- unknown ENDCASE; -- unknown ENDLOOP; IF connectionSpec # NIL AND info.forwardedCall THEN TRUSTED { IF info.blinkProcess=NIL THEN Process.Detach[info.blinkProcess _ FORK BlinkLED[info]]; }; SELECT trans FROM nop => NULL; -- Certifiably nothing at all to do, unless there's data or some submode has changed. set => { info.larkState_newState; RETURN; }; X => {info.audioSource _ NIL; info.transmitOnly _ FALSE; FailInt[info, "Invalid LarkState Transition", TRUE]; RETURN;}; zap, zpu, zpn => { info.audioSource _ NIL; info.transmitOnly _ FALSE; }; ENDCASE; IF tClrFwd[trans] = clrFwd THEN info.forwardedCall _ FALSE; IF tSetFwd[trans] = setFwd THEN { newState _ IF info.forwardedCall THEN trunkForwarding ELSE trunkTalking; trans _ lsTrans[info.larkState][newState]; }; info.larkState_newState; IF tDisconn[trans] = $disconnect THEN { IF ThNet.pd.debug THEN Deb[ info, "Rq disconnect" ]; QueueLarkAction[info, aDisconnect]; }; toneSpec _ SELECT trans FROM dia, diu => IF toneSpec#NIL THEN toneSpec ELSE dialTone, -- to handset receiver rbk, rbu => IF toneSpec#NIL THEN toneSpec ELSE ringbackTone, bzy, bzu => busyTone, err, eru => errorTone, rng, rgu => toneSpec, ENDCASE => NIL ; SELECT tDoTones[trans] FROM doTones => { IF ThNet.pd.debug THEN Deb[ info, "Rq tones" ]; IF toneSpec#NIL THEN QueueLarkAction[info, toneSpec]; }; stopTones => { IF ThNet.pd.debug THEN Deb[ info, "Rq stop tones" ]; QueueLarkAction[info, aNoTones]; }; ENDCASE; SELECT tHookState[trans] FROM reset => { SELECT info.switchState FROM $monitor, $speaker => info.switchState_$onhook; -- going totally idle ENDCASE; }; spkrTrans => IF info.switchState=$onhook THEN -- dialtone, direct connect or ringback info.switchState _ $speaker; ENDCASE; -- no state change cases. IF info.switchState#info.lastSwitchState THEN sw_TRUE; SELECT tSwitch[trans] FROM switch => sw_TRUE; switchIfDiff => IF oldState { IF ThNet.pd.debug THEN Deb[info, "Rq feeping"]; feepSpecs _ RopeToDTMF[ropeSpec]; pd.noisyFeeps _ feepSpecs.audible AND ~info.forwardedCall; }; ENDCASE; IF sw THEN TRUSTED { command: ROPE _ larkCommands[info.switchState][newState]; IF ThNet.pd.debug THEN Deb[info, "Rq switching", rope[command]]; otherAction _ QueueCommandSequence[info, command, @info.lState, info.scratchEv]; IF trans=fls THEN { Process.Detach[FORK FlashWait[info, oldState]]; info.larkState _ oldState; -- never really enter fls state, stay in forwarding or trunkTalking. }; info.lastSwitchState _ info.switchState; }; IF feepSpecs#NIL THEN QueueLarkAction[info, feepSpecs]; IF keyTable#NIL THEN { IF ThNet.pd.debug THEN Deb[info, "Rq key distribution"]; keyTablesToDate _ (info.keyTableDistrsRequested _ info.keyTableDistrsRequested+1); QueueLarkAction[info, keyTable]; }; IF connectionSpec#NIL THEN { IF ThNet.pd.debug THEN Deb[info, "Rq connection"]; SELECT trans FROM sup, spn, tlk, frd, frn => QueueLarkAction[info, connectionSpec]; sgl, sgn, ksp => IF info.forwardedCall THEN QueueLarkAction[info, connectionSpec]; ENDCASE; }; IF larkCall#NIL THEN SELECT tLarkCall[trans] FROM call => QueueLarkAction[info, larkCall]; ENDCASE; IF otherAction#NIL THEN { IF ThNet.pd.debug THEN Deb[info, "Rq other action"]; QueueLarkAction[info, otherAction]; }; IF keyTable#NIL THEN WHILE keyTablesToDate > info.keyTablesDistributed DO WAIT info.keySynch; -- Be sure keys have really been delivered ENDLOOP; }; TonesDone: PUBLIC ENTRY PROC[info: LarkInfo, commandEvent: Lark.StatusEvent ] = { Deb[info, "Tones done detected"]; SELECT commandEvent.event FROM 'F=> -- Feeping complete { IF info.larkState#trunkSignalling THEN RETURN; EnterLarkSt[info, trunkTalking, NIL]; }; ENDCASE => -- other tones finished. QueueLarkAction[info, NEW[ATonesDoneType _ [commandEvent.event]]]; }; FlashWait: PUBLIC ENTRY PROC[info: LarkInfo, oldState: LarkState] = TRUSTED { ENABLE UNWIND => NULL; flashWait: CONDITION; command: ROPE; IF info=NIL OR info.failed THEN RETURN; Process.SetTimeout[@flashWait, Process.MsecToTicks[pd.flashWaitTime]]; -- 600 ms or so. Deb[info, "Waiting for flash timeout"]; WAIT flashWait; command _ larkCommands[info.switchState][info.larkState]; Deb[info, "Flash timeout complete, restoring switching", rope[command]]; IF info.larkState#oldState THEN RETURN; -- something has gone wrong []_QueueCommandSequence[info, command, @info.lState, info.scratchEv]; info.inputQueue.QueueClientAction[ThSmartsPrivate.FlashDone, info]; }; BlinkLED: PUBLIC ENTRY PROC[info: LarkInfo] = TRUSTED { ENABLE UNWIND => NULL; blinkWait: CONDITION; IF info=NIL THEN RETURN; Deb[info, "Rq Begin blinking"]; WHILE info.forwardedCall AND info.larkState >= trunkSignalling AND info.larkState <= trunkFlashing AND info.larkProcess#NIL AND ~info.failed DO []_QueueLarkAction[info, aToggleLED]; Process.SetTimeout[@blinkWait, Process.MsecToTicks[pd.blinkWaitTime]]; -- 500 ms or so. WAIT blinkWait; ENDLOOP; Deb[info, "Rq End blinking"]; info.blinkProcess _ NIL; }; QueueFeeps: PUBLIC PROC[sInfo: SmartsInfo, feeps: Rope.ROPE ] = { EnterLarkState[sInfo.larkInfo, trunkSignalling, LIST[NEW[Atom.DottedPairNode_[$phoneNumber, feeps]]]]; }; prServerWDT: LarkOps.ParamCode=LOOPHOLE[10]; -- Both of these should go into LarkOps. prServerReps: LarkOps.ParamCode=LOOPHOLE[11]; larkRepsParam: LarkOps.LarkParameters _ NEW[LarkOps.LarkParametersRec[1]]; SetupTimeouts: PUBLIC ENTRY PROC[info: LarkInfo, debugging: BOOL ] = { larkParams: LarkOps.LarkParameters _ NEW[LarkOps.LarkParametersRec[5]]; RPC.SetMaxTransmissions[ conversation: info.shh, maxTransmissions: pd.toLarkTimeoutTransmissions, timeoutEnable: SELECT pd.toLarkTimeoutOK FROM always => always, never => never, unlessDebugging => IF debugging THEN never ELSE always, ENDCASE=>dontCare]; larkParams[0] _ [prDefaultMaxTransmissions, pd.fromLarkTimeoutTransmissions]; larkParams[1] _ [prSignalTimeout, LOOPHOLE[SELECT pd.fromLarkTimeoutOK FROM always => TRUE, never => FALSE, unlessDebugging => ~debugging, ENDCASE=>ERROR]]; larkParams[2] _ [prEventTimer, pd.larkEventTimer]; larkParams[3] _ [prTimeoutEvents, LOOPHOLE[SELECT pd.larkTimeoutEvents FROM always => TRUE, never => FALSE, unlessDebugging => ~debugging, ENDCASE=>ERROR]]; larkParams[4] _ [prServerWDT, 5000]; -- enabled by setting prServerReps, see below larkParams.numParams _ 5; []_QueueLarkAction[info, larkParams]; larkRepsParam.numParams _ 1; -- preparation for later true settings, see below. larkRepsParam[0] _ [prServerReps, 0]; -- preparation for later true settings, see below. }; Fail: PUBLIC ENTRY PROC[info: LarkInfo, reason: ROPE, informLark: BOOL_FALSE] = { ENABLE UNWIND => NULL; FailInt[info, reason, informLark]; }; FailInt: PUBLIC INTERNAL PROC[info: LarkInfo, reason: ROPE, informLark: BOOL_FALSE] = { IF info=NIL OR info.failed THEN { VoiceUtils.ReportFR[Rope.Concat["%g Lark failed: ", reason], $Lark, info, TU.RefAddr[info.larkSmartsInfo]]; RETURN; }; VoiceUtils.ProblemFR[Rope.Concat["%g Lark failed: ", reason], $Lark, info, TU.RefAddr[info.larkSmartsInfo]]; info.keyTablesDistributed _ info.keyTableDistrsRequested; BROADCAST info.keySynch; info.larkProcess _ NIL; info.audioSource _ NIL; info.transmitOnly _ FALSE; info.failed _ TRUE; NOTIFY info.stateChange; -- Be sure process notices failure and disappears. IF informLark AND (SELECT pd.informLarkOK FROM never =>FALSE, always =>TRUE, unlessDebugging => ~info.debugging, ENDCASE=>ERROR) THEN []_LarkRemoteControl.BootLark[info.netAddress]; -- best efforts only info.inputQueue.QueueClientAction[ThSmartsPrivate.Deregister, info]; }; LarkSupervisor: PROCEDURE[ info: LarkInfo ] = { ENABLE UNWIND => NULL; req: REF; WaitForAction: ENTRY PROC[info: LarkInfo] RETURNS [ref: REF_NIL] = TRUSTED { ENABLE UNWIND=>NULL; DO elt: LIST OF REF _ info.newActions; IF info.failed THEN RETURN[NIL]; IF elt # NIL THEN { ref_elt.first; info.newActions _ elt.rest; RETURN[ref]; }; Process.SetTimeout[@info.stateChange, Process.MsecToTicks[pd.idleWaitTime]]; IF info.larkState#$idle THEN WAIT info.stateChange; IF info.newActions = NIL THEN { IF info.nextToneList#NIL THEN FailInt[info, "Tones done notification was late", TRUE]; -- Notification is late. RETURN[NIL]; }; ENDLOOP; }; IF info.larkToneSpec=NIL THEN info.larkToneSpec _ NEW[Lark.ToneSpecRec _ [volume: 0, totalTime: 0, tones: NIL]]; IF info.cSpec=NIL THEN info.cSpec _ NEW[Lark.ConnectionSpecRec _ [ protocol: interactive, encoding: muLaw, sampleRate: 8000, packetSize: 160, buffer: in1, keyIndex: IF ThNet.pd.encryptVoice THEN 1 ELSE 0, localSocket: [[0],[0], Pup.nullSocket], remoteSocket: [[0],[0], Pup.nullSocket] ]]; TRUSTED { Process.EnableAborts[@info.stateChange]; Process.EnableAborts[@info.keySynch]; Process.SetTimeout[@info.stateChange, Process.MsecToTicks[pd.keySynchTime]]; }; IF ~StopMulticast[info].failed THEN -- If not, Fail has been called WHILE (req_WaitForAction[info])#NIL DO -- Deal with communications failure. ENABLE { RPC.CallFailed => { Fail[info, "Call Timed Out", TRUE]; GOTO Failed; }; ABORTED => { Fail[info, "LarkSupervisor aborted", TRUE]; GOTO Failed; }; }; DoTones: PROC[newTones: BOOL] = { -- Does one tone from current list of tones IF info.nextToneList=NIL THEN RETURN; info.larkToneSpec.volume _ info.toneSpec.volume; info.expectedNotification _ IF info.expectedNotification='z THEN 'a ELSE info.expectedNotification+1; info.larkToneSpec.notification _ [tones, info.expectedNotification]; info.larkToneSpec.tones _ info.nextToneList.first; []_info.interface.SpecifyTones[shh: info.shh, queueIt: ~newTones, tones: info.larkToneSpec]; }; WITH req SELECT FROM d: REF ADisconnectType => IF info.spec#NIL THEN { buf: Lark.VoiceBuffer _ out1; info.interface.Disconnect[ shh: info.shh, buffer: in1 ]; -- always stop in1 (tx1) FOR i: NAT IN [1..info.spec.conversationInfo.numActive) DO Deb[info, "Do disconnect per request", card[i]]; info.interface.Disconnect[ shh: info.shh, buffer: buf ]; -- out1 (tx1) through outn (txn) buf _ SUCC[buf]; ENDLOOP; IF info.multicasting AND StopMulticast[info].failed THEN GOTO Failed; info.spec _ NIL; }; spec: ThParty.PartyInfo => { cSpec: Lark.ConnectionSpec = info.cSpec; numActive: NAT _ spec.conversationInfo.numActive; conference: BOOL _ numActive>=3; IF ~conference AND info.multicasting AND StopMulticast[info].failed THEN GOTO Failed; IF info.spec#NIL THEN { -- Duplicate of ADisconnectType stuff, for now buf: Lark.VoiceBuffer _ out1; Deb[info, "Do disconnect transmit to connect"]; info.interface.Disconnect[ shh: info.shh, buffer: in1 ]; -- always stop in1 (tx1) FOR i:NAT IN [1..info.spec.conversationInfo.numActive) DO Deb[info, "Do disconnect receive prior to connect", card[i]]; info.interface.Disconnect[ shh: info.shh, buffer: buf ]; buf _ SUCC[buf]; ENDLOOP; }; IF numActive>=2 THEN { info.spec _ spec; IF conference THEN IF ([conference, ] _ StartMulticast[info, spec.conversationInfo.conferenceHost]).failed THEN GOTO Failed; cSpec.buffer _ in1; cSpec.remoteSocket _ spec[1].socket; cSpec.remoteSocket.socket _ spec[0].socket.socket; IF conference THEN cSpec.remoteSocket.host _ spec.conversationInfo.conferenceHost.host; cSpec.localSocket _ cSpec.remoteSocket; -- superstition Deb[info, "Connect transmit buf", card[cSpec.remoteSocket.host]]; info.interface.Connect[shh: info.shh, specs: cSpec ]; cSpec.buffer _ out1; FOR i: NAT IN [1..numActive) DO cSpec.localSocket _ spec[i].socket; IF conference THEN cSpec.localSocket.host _ spec.conversationInfo.conferenceHost.host; cSpec.remoteSocket _ cSpec.localSocket; -- superstition Deb[info, "Connect receive buf", card[i], card[cSpec.localSocket.host]]; info.interface.Connect[shh: info.shh, specs: cSpec ]; cSpec.buffer _ SUCC[cSpec.buffer]; ENDLOOP; }; }; keyTable: Lark.KeyTable => { UKT: ENTRY PROC[info: LarkInfo] = INLINE { info.keyTable _ keyTable; info.keyTablesDistributed _ info.keyTablesDistributed + 1; BROADCAST info.keySynch; }; info.interface.SetKeyTable[shh: info.shh, table: keyTable]; UKT[info]; }; ts: LarkPlay.ToneSpec => { info.toneSpec_ts; info.nextToneList _ info.toneSpec.tones; DoTones[TRUE]; }; lC: ThSmartsPrivate.LarkCall => lC.proc[info, lC.clientData]; td: REF ATonesDoneType => IF info.toneSpec#NIL THEN { IF td.event#info.expectedNotification THEN { Fail[info, "Tones notifications out of order", TRUE]; GOTO Failed; }; IF info.nextToneList#NIL AND info.nextToneList.rest#NIL THEN info.nextToneList _ info.nextToneList.rest ELSE IF NOT info.toneSpec.repeatIndefinitely THEN { info.toneSpec_NIL; info.nextToneList_NIL } ELSE info.nextToneList _ info.toneSpec.tones; IF info.nextToneList#NIL THEN DoTones[FALSE]; }; a: REF ANoTonesType => { info.toneSpec_NIL; info.nextToneList_NIL; quenchSpec.tones.first.on _ 0; []_info.interface.SpecifyTones[shh: info.shh, tones: quenchSpec, queueIt: FALSE]; }; led: REF AToggleLEDType => { info.lState.lSw[led] _ IF info.lState.lSw[led] = disabled THEN enabled ELSE disabled; ledToggler[0] _ [led, info.lState.lSw[led]]; []_info.interface.Commands[info.shh, ledToggler]; }; resetAction: REF ResetActionType => { []_info.interface.Reset[shh: info.shh, rName: "Don't revert"]; IF info.multicasting AND StopMulticast[info].failed THEN GOTO Failed; IF info.keyboardResetHandler#NIL THEN info.keyboardResetHandler[info]; }; larkParameters: LarkOps.LarkParameters => []_info.interface.SetParameters[shh: info.shh, parameters: larkParameters]; keepAlive: REF INT => { IF keepAlive^=ThSmarts.noneScheduled OR keepAlive^=0 THEN LOOP; IF keepAlive^#info.nextScheduledCheck THEN { info.nextScheduledCheck_keepAlive^; larkRepsParam[0] _ [prServerReps, (info.nextScheduledCheck+4)/5]; []_info.interface.SetParameters[shh: info.shh, parameters: larkRepsParam]; }; [] _ info.interface.WhatAreTones[shh: info.shh]; -- Actually do the ping. }; echoParameters: Lark.EchoParameters => []_info.interface.EchoSupression[shh: info.shh, echo: echoParameters]; commands: Lark.CommandEvents => IF commands#NIL THEN info.interface.Commands[ info.shh, commands ]; feepSpecs: REF AFeepType => { IF feepSpecs=NIL THEN LOOP; IF (quenchSpec.tones.first.on _ feepSpecs.initialDelay)#0 THEN []_info.interface.SpecifyTones[shh: info.shh, tones: quenchSpec, queueIt: FALSE]; []_info.interface.Feep[ shh: info.shh, on: feepSpecs.on, off: feepSpecs.off, notify: [tones, 'F], waveTable: ThNet.pd.feepVolume, queueIt: TRUE, events: feepSpecs.digits]; } ENDCASE; REPEAT Failed => NULL; ENDLOOP; info.larkProcess _ NIL; }; StartMulticast: PROC[info: LarkInfo, hostAddress: Thrush.NetAddress] RETURNS[ok: BOOL_FALSE, failed: BOOL_FALSE] = { ENABLE { RPC.CallFailed => { Fail[info, "Call Timed Out", TRUE]; GOTO Failed; }; ABORTED => { Fail[info, "LarkSupervisor aborted", TRUE]; GOTO Failed; }; }; IF info.multicasting AND StopMulticast[info].failed THEN RETURN[FALSE, TRUE]; IF info.spec=NIL OR info.spec.conversationInfo.numActive <=2 THEN RETURN[FALSE, FALSE]; ok _ Multicast.HandleMulticast[shh: info.shh, net: info.netAddress.net, realHost: info.netAddress.host, listeningTo: hostAddress.host]; IF ~ok THEN { VoiceUtils.ProblemFR["Couldn't set multicasting [%g => %g]", $Lark, info, int[info.netAddress.host], int[hostAddress.host]]; RETURN[ok, FALSE]; }; info.interface.SetHostNumber[shh: info.shh, host: [hostAddress.net, hostAddress.host]]; info.multicasting _ TRUE; EXITS Failed => { ok _ FALSE; failed _ TRUE; }; }; StopMulticast: PROC[info: LarkInfo, force: BOOL_FALSE] RETURNS[failed: BOOL_FALSE] = { ENABLE { RPC.CallFailed => { Fail[info, "Call Timed Out", TRUE]; GOTO Failed; }; ABORTED => { Fail[info, "LarkSupervisor aborted", TRUE]; GOTO Failed; }; }; IF ~info.multicasting THEN RETURN[FALSE]; info.interface.SetHostNumber[shh: info.shh, host: [info.netAddress.net, info.netAddress.host]]; Multicast.StopHandlingMulticast[shh: info.shh, realHost: info.netAddress.host]; info.multicasting _ FALSE; EXITS Failed => failed _ TRUE; }; QueueLarkAction: PUBLIC INTERNAL PROC[info: LarkInfo, ref: REF] = { elt: LIST OF REF = LIST[ref]; lst: LIST OF REF = info.lastAction; IF info.failed THEN FailInt[info, "Additional output requests disregarded", FALSE]; IF info.newActions=NIL THEN info.newActions _ elt ELSE { IF lst=NIL THEN ERROR; WITH ref SELECT FROM x: REF BOOL => IF info.newActions#NIL THEN RETURN; ENDCASE; lst.rest _ elt; }; info.lastAction _ elt; IF info.larkProcess=NIL THEN TRUSTED { Process.Detach[info.larkProcess _ FORK LarkSupervisor[ info ]]; }; NOTIFY info.stateChange; }; QueueCommandSequence: INTERNAL PROC[ info: LarkInfo, commands: ROPE, lState: LONG POINTER TO LState, scratchEv: Lark.CommandEvents] RETURNS[otherAction: REF_NIL]= TRUSTED { eventIndex: INTEGER_-1; c: CHAR; i: NAT; index: INTEGER; event: Lark.Event; events: Lark.CommandEvents _ scratchEv; nextState: LState _ []; len: NAT; IF commands=NIL THEN RETURN; -- status quo len _ commands.Length[]; FOR i IN [0..len) DO SELECT (c_commands.Fetch[i]) FROM 'J, 'j => IF ~pd.backDoorOH THEN LOOP; ENDCASE; SELECT c FROM '\040 => LOOP; -- ignore spaces 'Z => { lState^_ []; otherAction _ resetAction; }; -- Zap; reset hardware to full idle 'X, 'x => { -- Crossbar switch specification, possibly parameterized by next character param: CHAR _ commands.Fetch[i+1]; outputs: PACKED ARRAY [0..8) OF BOOLEAN; row: NAT; wasParam, doIt: BOOL_TRUE; SELECT param FROM IN ['0..'9] => wasParam _ FALSE; -- no parameter, just do it. 'A => IF info.audioSource#$linea THEN doIt_FALSE; 'B => IF info.audioSource#$lineb THEN doIt_FALSE; 'r => IF info.audioSource=$linea OR info.audioSource=$lineb THEN doIt_FALSE; 't => IF info.transmitOnly THEN doIt_FALSE; -- inhibit all receive switching. 'F => IF ~pd.noisyFeeps THEN doIt_FALSE; -- caller hears auto-feeping ENDCASE => doIt_FALSE; -- unrecognized parameter, don't do it; IF wasParam THEN { i_i+1; param _ commands.Fetch[i+1]; }; row _ Digit[param]; outputs _ LOOPHOLE[nextState.xbar[row]]; IF doIt THEN outputs[Digit[commands.Fetch[i+2]]] _ (c='X); nextState.xbar[row] _ LOOPHOLE[outputs]; i_i+2; LOOP; }; 'E, 'e => { nextState.echoStyle _ commands.Fetch[i_i+1]-'0; otherAction _ echosOn[nextState.echoStyle]; }; 'M, 'm => { nextState.voiceMode _ SELECT commands.Fetch[i_i+1] FROM '0 => Lark.o3i1, '1 => Lark.o2i2, '2 => Lark.o1i1, ENDCASE => ERROR; }; ENDCASE=> { IF c IN ['a..'z] THEN { event _ Lark.disabled; c_c-('a-'A); } ELSE event _ Lark.enabled; IF lStateForLetter[c]#none THEN nextState.lSw[lStateForLetter[c]] _ event; }; ENDLOOP; IF nextState=lState^ THEN RETURN; IF nextState.echoStyle#lState.echoStyle AND otherAction=NIL THEN otherAction _ echosOff[lState.echoStyle]; FOR iteration: NAT IN [0..1] DO -- 0: compute size; 1: fill in result sequence. index_-1; IF nextState.voiceMode#lState.voiceMode THEN events[index_index+1] _ [voiceMode, nextState.voiceMode]; FOR i: LSwitches DECREASING IN LSwitches DO IF nextState.lSw[i]#lState.lSw[i] THEN events[index_index+1] _ [lDevs[i], nextState.lSw[i]]; ENDLOOP; FOR i: NAT IN [0..8) DO IF nextState.xbar[i]#lState.xbar[i] THEN { outputs: PACKED ARRAY[0..8) OF BOOLEAN = LOOPHOLE[lState.xbar[i]]; nxtOutputs: PACKED ARRAY[0..8) OF BOOLEAN = LOOPHOLE[nextState.xbar[i]]; FOR j: NAT IN [0..8) DO IF outputs[j]#nxtOutputs[j] THEN events[index_index+1] _ [ Lark.Device[LOOPHOLE[IF nxtOutputs[j] THEN 23 ELSE 22]], LOOPHOLE[i*16+j] ]; ENDLOOP; }; ENDLOOP; IF index=-1 THEN { events_NIL; EXIT; }; IF iteration=0 THEN events _ NEW[Lark.CommandEventSequence[index+1]]; ENDLOOP; QueueLarkAction[info, events]; lState^ _ nextState; lState.lSw[xBarAll] _ Lark.disabled; }; Digit: PROC[c: CHAR] RETURNS [digit: NAT] = INLINE { RETURN[c-'0]; }; RopeToDTMF: PROC [r: Thrush.ROPE] RETURNS [fs: REF AFeepType] = { len: INT _ MIN[Lark.Passel.LAST, r.Length[]]; ce: Lark.CommandEvents; ceIndex: INT_0; number: INTEGER; fs _ NEW[AFeepType_[]]; FOR rIndex: INT IN [0..len) DO c: CHAR = r.Fetch[rIndex]; SELECT c FROM IN ['0..'9], IN ['a..'d], '#, '* => ceIndex _ ceIndex+1; 'P => IF ceIndex#0 THEN ceIndex _ ceIndex+1 ELSE fs.initialDelay _ number*100; 'A => fs.audible _ TRUE; 'O => fs.on _ number*10; 'F => fs.off _ number*10; IN ['\001..'\037] => number _ c-'\000; ENDCASE; -- unrecognized, so ignore it. ENDLOOP; ce _ NEW[Lark.CommandEventSequence[ceIndex]]; fs.digits _ ce; ceIndex _ 0; FOR rIndex: INT IN [0..len) DO c: CHAR = r.Fetch[rIndex]; val: CHAR _ '\000; SELECT c FROM IN ['0..'9] => val _ LOOPHOLE[128 + c - '0]; IN ['a..'d] => val _ LOOPHOLE[138 + c - 'a]; '* => val _ Lark.bStar; '# => val _ Lark.bThorp; IN ['\001..'\037] => number _ c-'\000; 'P => IF ceIndex#0 THEN val _ LOOPHOLE[number*10]; ENDCASE; IF val = '\000 THEN LOOP; ce[ceIndex] _ [touchPad, val]; ceIndex _ ceIndex+1; ENDLOOP; }; SetTones: INTERNAL PROC[] = { IF ~pd.tonesInvalid THEN RETURN; pd.tonesInvalid _ FALSE; dialTone _ NEW[LarkPlay.ToneSpecRec _ [repeatIndefinitely: TRUE, volume: ThNet.pd.tonesVolume, tones: LIST[LIST[ [f1: 350, f2: 440, on: 5000, off: 0], [f1: 350, f2: 440, on: 5000, off: 0]]]]]; busyTone _ NEW[LarkPlay.ToneSpecRec _ [repeatIndefinitely: TRUE, volume: ThNet.pd.tonesVolume, tones: LIST[LIST[ [f1: 480, f2: 620, on: 500, off: 500, repetitions: 5], [f1: 480, f2: 620, on: 500, off: 500, repetitions: 5]]]]]; errorTone _ NEW[LarkPlay.ToneSpecRec _ [repeatIndefinitely: TRUE, volume: ThNet.pd.tonesVolume, tones: LIST[LIST[ [f1: 480, f2: 620, on: 250, off: 250, repetitions: 10], [f1: 480, f2: 620, on: 250, off: 250, repetitions: 10]]]]]; ringbackTone _ NEW[LarkPlay.ToneSpecRec _ [repeatIndefinitely: TRUE, volume: ThNet.pd.tonesVolume, tones: LIST[LIST[ [f1: 440, f2: 480, on: 2000, off: 4000], [f1: 440, f2: 480, on: 2000, off: 4000]]]]]; }; Deb: PUBLIC PROC[info: LarkInfo, r: ROPE, p1, p2, p3, p4: IO.Value_[null[]]] = { s: IO.STREAM; IF ~ThNet.pd.debug THEN RETURN; s_IO.ROS[]; s.PutF["<%g", rope[r]]; IF p1#[null[]] THEN s.PutF["-- %g", p1]; IF p2#[null[]] THEN s.PutF[", %g", p2]; IF p3#[null[]] THEN s.PutF[", %g", p3]; IF p4#[null[]] THEN s.PutF[", %g", p4]; s.PutRope[">\r"]; VoiceUtils.Report[s.RopeFromROS[], $LarkDetailed, info]; }; QuickState: ARRAY LarkState OF IO.Value = [ rope["none"], rope["idle"], rope["talking"], rope["trunkSignalling"], rope["trunkTalking"], rope["trunkForwarding"], rope["trunkFlashing"], rope["ringing"], rope["silence"], rope["dialTone"], rope["ringBack"], rope["busyTone"], rope["errorTone"]]; CommandsForState: TYPE = REF CommandsForStateArray; CommandsForStateArray: TYPE = ARRAY LarkState OF ROPE; telsetCommands: CommandsForState _ NEW[CommandsForStateArray _ [ NIL, -- none "Z", -- idle "S Xt01 Xt06 Xr10 XA60 XB70 X14", -- talking "HI X02 X06 X14 XF01", -- trunkSignalling "HIST X02 Xr10 XA60 XB70 Xt21 Xt26 X14", -- trunkTalking, codec-assisted electronic mode "HIE3 X02 X20 X24", -- trunkForwarding, trunk to remote Lark connection. "ST X02 Xr10 XA60 XB70 Xt21 Xt26 X14", -- trunkFlashing, on-hook but otherwise trunkTalking. "R X03 X06 X14", -- ringing "S X14", -- silence toneStdCommand, toneStdCommand, toneStdCommand, toneStdCommand -- tones ]]; toneStdCommand: ROPE = "S X01 X14 X06"; monitorCommands: CommandsForState _ NEW[CommandsForStateArray _ [ NIL, -- none "Z", -- idle "LS Xt01 Xt06 Xt03 Xr10 XA60 XB70 X14", -- talking "HIL X02 X06 X03 X14 XF01", -- trunkSignalling "HILST X02 Xr10 XA60 XB70 Xt21 Xt26 Xt23 X14", -- trunkTalking, codec-assisted electronic mode "HILE3 X02 X20 X24", -- trunkForwarding, trunk to remote Lark connection. "LST X02 Xr10 XA60 XB70 Xt21 Xt26 Xt23 X14", --trunkFlashing, on-hook but otherwise trunkTalking. "R X03 X06 X14", -- ringing "LS X14", -- silence toneMonCommand, toneMonCommand, toneMonCommand, toneMonCommand -- tones ]]; toneMonCommand: ROPE = "LS X01 X03 X14 X06"; speakerCommands: CommandsForState _ NEW[CommandsForStateArray _ [ NIL, -- none "Z", -- idle "E1L Xt03 Xt06 Xr30 XA60 XB70 X14", -- talking "HIL X02 X06 XF03", -- trunkSignalling "HIE2M1TL Xr30 X02 Xt25 Xt26 X24 Xt53 XA60 XB70", -- trunkTalking, gain-controlled digital mode "HILE3 X02 X20 X24", -- trunkForwarding, trunk to remote Lark connection. (need gain setting for echo?) "E2M1TL Xr30 X02 Xt25 Xt26 X24 Xt53 XA60 XB70", --trunkFlashing, on-hook but otherwise trunkTalking. "R X03 X06", -- ringing "L X14", -- silence toneSpkrCommand, toneSpkrCommand, toneSpkrCommand, toneSpkrCommand -- tones ]]; toneSpkrCommand: ROPE = "L X03 X14 X06"; larkCommands: ARRAY ThSmartsPrivate.SwitchState OF CommandsForState _ [ telsetCommands, -- good as any for "onhook" condition telsetCommands, -- telset speakerCommands, -- speaker speakerCommands, -- sPEAKER monitorCommands, -- monitor monitorCommands -- mONITOR ]; echoStyleFD: REF _ NEW[Lark.EchoParameterRecord _[ buffer: out1, buffer2Controlled: FALSE, buffer1Controlled: TRUE, decayTime: 5, gain: [ 1024, 2048, 2048, 2048, 32767 ] ]]; echoStyleFwd: REF _ echoStyleFD; echoStyleBD: REF _ NEW[Lark.EchoParameterRecord _[ buffer: in2, buffer2Controlled: FALSE, buffer1Controlled: TRUE, decayTime: 10, gain: [ 2048, 4096, 8192, 16384, 32767 ] ]]; echoStyleNoFD: REF _ NEW[Lark.EchoParameterRecord _[ buffer: out1, -- not interesting buffer2Controlled: FALSE, buffer1Controlled: TRUE, -- FALSE, compensates for a Lark bug; restore when fixed decayTime: 5, gain: [ 32767, 32767, 32767, 32767, 32767 ] ]]; echoStyleNoFwd: REF _ echoStyleNoFD; echoStyleNoBD: REF _ NEW[Lark.EchoParameterRecord _[ buffer: in2, -- not interesting buffer2Controlled: FALSE, buffer1Controlled: TRUE, -- FALSE, compensates for a Lark bug; restore when fixed decayTime: 5, gain: [ 32767, 32767, 32767, 32767, 32767 ] ]]; echosOn: ARRAY [0..3] OF REF_[ NIL, echoStyleFD, echoStyleBD, echoStyleFwd ]; echosOff: ARRAY [0..3] OF REF_[ NIL, echoStyleNoFD, echoStyleNoBD, echoStyleNoFwd ]; LSwitches: TYPE = ThSmartsPrivate.LSwitches; LState: TYPE = ThSmartsPrivate.LState; lDevs: ARRAY LSwitches OF Lark.Device = [ crossBar, offHookRelay, aRelay, sideTone, ringEnable, revertRelay, revertHookswitch, led, spMode, crossBar--random...not used-- ]; lStateForLetter: ARRAY CHAR['A..'Z] OF LSwitches = [ none, none, none, none, none, -- A to E revert, revertHook, hook, aSwitch, hook, none, led, -- F to L none, none, none, none, none, -- M to Q ringO, sideTone, spMode, none, none, none, none, none, -- R to Y none -- Z -- ]; LSTrans: TYPE = { nop, -- nothing to do set, -- enter specified state (usu. step to recovery) without taking any other actions. zap, zpu, zpn, -- reset Lark hardware (u means unconnect first, n means silence tones first) trk, tkn, -- Set for electronic phone connection (n means silence tones first) frd, frn, -- Trunk-to-network forwarding versions of trk, tkn (frn probably doesn't exist; wrong end <>) tlk, -- like supervision, but must also adjust switching. sup, spn, -- supervision, OK to change connection, key table. (n means silence tones first) ksp, -- key supervision, OK to change key table. sgl, sgn, -- Do trunk signalling (n means silence tones first) fls, -- Flash the phone line rng, rgu, -- Set for ringing (u means unconnect first, r means repeating tone) dia, diu, -- Set for dial tone (tones should be more generic and user-programmable than this!) rbk, rbu, -- Set for ring back bzy, bzu, -- Set for busy tone err, eru, -- Set for error tone sil, -- silence tones, ksp obtains X -- invalid transition; complain, then remain in present state (go idle?) }; lsTrans: ARRAY LarkState OF ARRAY LarkState OF LSTrans = [[ nop, zap, X, X, X, X, X, X, X, X, X, X, err ],[--non (none) X, nop, spn, sgl, trk, frd, X, rng, nop, dia, rbk, bzy, err ],[--idl (idle) X, zpu, sup, X, X, X, X, rgu, nop, diu, rbu, bzu, eru ],[--tlk (talking) X, zpn, X, ksp, trk, frd, X, X, nop, X, X, X, X ],[--sig (trunkSignling) X, zap, X, sgl, ksp, X, fls,X, nop, X, X, X, X ],[--trk (trunkTalking) X, zpu, X, sgl, X, sup, fls, X, nop, diu, rbu, bzu, eru ],[--fwd (trunkFwding) X, zap, X, X, trk, X, sup,X, nop, X, X, X, X ],[--fls (trunkFlash) X, zpn, spn, sgn, tkn, frn, X, ksp, sil, dia, rbk, bzy, err ],[--rng (ringing) X, zap, tlk, sgl, trk, frd, X, rng, ksp, dia, rbk, bzy, err ],[--shh (silence) X, zpn, spn, sgn, tkn, frn, X, rng, sil, ksp, rbk, bzy, err ],[--dia (dialTone) X, zpn, spn, sgn, tkn, frn, X, rng, sil, dia, ksp, bzy, err ],[--rbk (ringBack) X, zpn, spn, sgn, tkn, frn, X, rng, sil, dia, rbk, ksp, err ],[--bzy (busyTone) X, zpn, spn, sgn, tkn, frn, X, rng, sil, dia, rbk, bzy, ksp ] --err (errTone) ]; TDisconn: TYPE = { X, disconnect }; tDisconn: ARRAY LSTrans OF TDisconn = [ -- zpu, rgu, diu, rbu, bzu, eru X, X, X, disconnect, X, X, X, X, X, X, X, X, X, X, X, X, X, disconnect, X, disconnect, X, disconnect, X, disconnect, X, disconnect, X, X ]; TDoTones: TYPE = { X, doTones, stopTones }; tDoTones: ARRAY LSTrans OF TDoTones = [ -- rng, rgu, dia, diu, rbk, rbu, bzy, bzu, err, eru; zpn, tkn, frn, spn, sgn, sil X, X, X, X, stopTones, X, stopTones, X, stopTones, X, X, stopTones, X, X, stopTones, X, doTones, doTones, doTones, doTones, doTones, doTones, doTones, doTones, doTones, doTones, stopTones, X ]; THookState: TYPE = { X, reset, spkrTrans }; tHookState: ARRAY LSTrans OF THookState = [ -- zap, zpu, zpn; trk, tkn, tlk, spn, fls, dia, diu, rbk, rbu, bzy, bzu, err, eru, sil X, X, reset, reset, reset, spkrTrans, spkrTrans, X, X, spkrTrans, X, spkrTrans, X, X, X, spkrTrans, X, X, spkrTrans, spkrTrans, spkrTrans, spkrTrans, spkrTrans, spkrTrans, spkrTrans, spkrTrans, spkrTrans, X ]; TSwitch: TYPE = { X, switch, switchIfDiff }; tSwitch: ARRAY LSTrans OF TSwitch = [ X, X, switch, switch, switch, switch, switch, switch, switch, switch, X, switch, X, switch, switch, switch, switchIfDiff, switchIfDiff, switchIfDiff, switchIfDiff, switchIfDiff, switchIfDiff, switchIfDiff, switchIfDiff, switchIfDiff, switchIfDiff, switch, X ]; TSetFwd: TYPE = { X, setFwd }; tSetFwd: ARRAY LSTrans OF TSetFwd = [ X, X, X, X, X, setFwd, setFwd, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X ]; TClrFwd: TYPE = { X, clrFwd }; tClrFwd: ARRAY LSTrans OF TClrFwd = [ X, X, clrFwd, clrFwd, clrFwd, X, X, X, X, clrFwd, X, X, X, X, X, X, clrFwd, clrFwd, clrFwd, clrFwd, clrFwd, clrFwd, clrFwd, clrFwd, clrFwd, clrFwd, clrFwd, clrFwd ]; TLarkCall: TYPE = { X, call }; tLarkCall: ARRAY LSTrans OF TLarkCall = [ call, X, X, X, X, call, call, call, call, call, call, call, call, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X ]; ADisconnectType: TYPE = { aDisconnect }; aDisconnect: REF ADisconnectType _ NEW[ADisconnectType_aDisconnect]; ResetActionType: TYPE = { resetAction }; resetAction: REF ResetActionType _ NEW[ResetActionType_resetAction]; AFeepType: TYPE = RECORD [ initialDelay: CARDINAL _ pd.waitForTelco, on: CARDINAL _ pd.telcoMinOn, off: CARDINAL _ pd.telcoMinOff, audible: BOOL_FALSE, digits: Lark.CommandEvents_NIL ]; ANoTonesType: TYPE = { aNoTones }; aNoTones: REF ANoTonesType _ NEW[ANoTonesType_aNoTones]; ATonesDoneType: TYPE = RECORD [ event: Lark.Event ]; AToggleLEDType: TYPE = { aToggleLED }; aToggleLED: REF AToggleLEDType _ NEW[AToggleLEDType_aToggleLED]; ledToggler: Lark.CommandEvents _ NEW[Lark.CommandEventSequence[1]]; ViewCmd: Commander.CommandProc = TRUSTED { Nice.View[pd, "Lark Out PD"]; }; Commander.Register["VuLarkOut", ViewCmd, "Program Management variables for Lark Output"]; }. (èLarkOutImpl.mesa Copyright Ó 1985, 1986, 1987 by Xerox Corporation. All rights reserved. Polle Zellweger (PTZ) November 4, 1985 8:42:01 pm PST Last modified by D. Swinehart, July 19, 1987 11:17:45 pm PDT Declarations Whether to let RPC calls from server to Lark time out. The default is to do so iff the database indicates the Lark is not in operational mode. When asked to boot the Lark during Lark failure, determines the conditions under which the request is granted: always, never, or only unless debugging, as with timeouts. Determines what to tell the Lark to do about server timeouts. External Procedures Relevant only for trunk calls. The hosts are different, so the local trunk is communicating to a remote Etherphone. See LarkSmartsSupImpl for more discussion of these connections. We don't handle conferences involving trunks, thus the numParties=2 test. Higher level code assures this. In this state, the LED flashes to let the user know why the switch hook and attempts to place calls don't work. Each select statement combines cases to execute a subset of the required actions efficiently. If trunkTalking was requested, we must enter either trunkTalking or trunkForwarding, depending on whether the other end of the connection is on the same machine. When leaving talking state but not going idle, must explicitly take down Ethernet connection. Does this ever happen? Queue up request to eliminate connections. Select tone/tune specifications. Go offhook and enter speakerphone mode if noises need to be heard and phone isn't offhook Go back onhook when idling from spkr (not sPkr) or monitor mode. Must follow switching, but calculations must precede it. Otherwise we'd be transmitting stuff for no reason. Revert to the previous switching condition Server to Lark Lark to Server Internal Procedures Issue an input "failed" event. In a loop, keep the state of the Lark up to date. Awakens itself whenever any pending tone is otherwise likely to time out. Awakened by EnterLarkState whenever the state of tones, switches, and the like might have to change. When initializing the supervisor process, multicasting should already be off for this host. But be as careful as possible here: stop any that's detectably going on, and in any case, turn off multicast forwarding for this host. If conferencing, set up multicasting for this host, first. Transmit buffer Receive buffers See ThsmartsPrivate.LarkCall < revert. Remember to change 2d parameter next time Lark.mesa changes.>> Just pings the Lark; will fail if the call times out. prServerWDT is already 5000 Queue is a FIFO list of REFs, with a lastAction pointer to aid in rapid enqueuing. Ref BOOL's are just keep-alive polls. Don't add them to a non-empty queue. See Hardware Switching Tables below for interpretations of these command sequences. Parameters determine whether this crossbar specification should be obeyed depending on outside influences, like "line(A/B) mode" or global indications of whether automatically-generated touch-tones should be heard by the caller. Hardware Switching Tables Interpretation of the command string characters: En-- echo mode; n IN [0..3); selects echoStyleStd (default), ...FD, ...BD, or ...Fwd F -- T/R lead reversion G -- A/A1 hookswitch control reversion H -- Assert Telewall hookswitch I -- Assert A/A1 to Telewall J -- Assert Telewall hookswitch, maybe (depends on boolean variable) L -- Lights led l -- Flashes led if trunk is forwarding a call. R -- overrides volume control for ringing through speaker S -- enables telset sidetone T -- spMode; configures codecs for electronic trunk action Xij -- connects crossbar input i to output j Xmij -- connects crossbar input i to output j, conditionally Conditions: A -- only in lineA mode (input comes from line 1 in instead of telset or microphone) B -- only in lineB mode (input comes from line 2 in instead of telset or microphone) r -- only in non-radio (standard, non-lineA/B) mode t -- inhibit in transmit-only mode (server stuff) F -- only if feeping (touch-tone signalling) is to be audible. Mn -- voiceMode; n IN [0..2); selects program O3I1 (default), O2I2, or O1I1 Z -- Resets hardware and crossbar Radio mode: input comes from line 1 in instead of telset or microphone Crossbar Connections: Port Input Output 0 dec1 co1 1 Xmtr Rcvr 2 from Telewall to Telewall 3 mike speaker 4 silence DTMF receiver 5 dec2 co2 6 line 1 in line 1 out 7 line 2 in line 2 out larkCommands: ARRAY ThSmartsPrivate.SwitchState OF CommandsForState _ [ telsetCommands, -- good as any for "onhook" condition telsetCommands, -- telset speakerCommands, -- speaker speakerCommands, -- sPEAKER monitorCommands, -- monitor monitorCommands -- mONITOR ]; Telset "Z", -- trunkFlashing, on-hook for a second. Monitoring Telset "Z", -- trunkFlashing, on-hook for a second. Speakerphone "Z", -- trunkFlashing, on-hook for a second. Front Door call using Speakerphone Back Door call using Speakerphone Standard FD or BD handset mode, no forwarding Standard FD or BD handset mode, no forwarding State tables non idl tlk sig trk fwd fls rng shh dia rbk bzy err _new old \/ Subtransition codes and tables zap, zpu, zpn, trk, tkn, frd, frn, tlk, spn, sgl, sgn, fls; rng, rgu, dia, diu, rbk, rbu, bzy, bzu, err, eru trk, tkn all but nop, set, trk, tkn, frd, frn, sup, spn, ksp, sgl, sgn, fls nop, trk, tkn, frd, frn, tlk, sup, spn, ksp Queued specifications for Supervisor ASpeechDoneType: TYPE = RECORD [ indexMarker: INT ]; Swinehart, June 14, 1985 5:22:47 pm PDT Repair Echo stuff changes to: resetAction (local of LarkSupervisor) explicitly request resets on Z, LarkSupervisor ditto, QueueCommandSequence larkCommands, echosOn, echosOff, ZapEchosType, zapEchos, ANoTonesType, echoStyleNoFD, echoStyleNoBD Swinehart, July 16, 1985 2:15:11 pm PDT Fixes to trunkSignalling, trunkForwarding changes to: EnterLarkSt Swinehart, August 6, 1985 2:12:11 pm PDT Merge with PTZ Prose additions changes to: EnterProseQueue, ASpeechDoneType, ViewCmd Polle Zellweger (PTZ) July 3, 1985 7:11:31 pm PDT changes to: DIRECTORY, EnterLarkSt, IntIDQueue, IntIDTranslate, IntIDTranslateBody, EnterIntIDQueue (local of LarkSupervisor), RemoveIntIDQueue (local of LarkSupervisor), LarkSupervisor, HandleProseOutput Polle Zellweger (PTZ) July 11, 1985 6:20:12 pm PDT changes to: indexMarkerEnd(public), maxClientMarker(public), LarkSupervisor, ps (local of LarkSupervisor), pd (local of LarkSupervisor), ProseControlDone, EnterProseQueue(replaced EnterIntIDQueue) removed: RemoveIntIDQueue (function now in LarkInImpl), minClientMarker, minControlMarker, maxControlMarker, speechDoneMarker, DIRECTORY, pResetConfirmOK, cmdLeader, indexMarkerLen, SpeakText (local of LarkSupervisor) Polle Zellweger (PTZ) July 16, 1985 6:40:42 pm PDT Fix multiple packets bug. changes to: LarkSupervisor -- add proseActive & handle incoming speechDoneMarkers correctly=> SpeakText (local of LarkSupervisor), ps (local of LarkSupervisor), pd (local of LarkSupervisor) Polle Zellweger (PTZ) July 18, 1985 5:18:49 pm PDT new version of SpeakText preloads Prose and then sends only 1 packet at a time. changes to: DIRECTORY, LarkOutImpl, LarkSupervisor, SpeakText (local of LarkSupervisor), pd (local of LarkSupervisor), SpeakText, ps (local of LarkSupervisor) Polle Zellweger (PTZ) July 19, 1985 6:34:34 pm PDT Make sure not to break in the middle of a client marker!! changes to: SpeakText (local of LarkSupervisor) Polle Zellweger (PTZ) July 30, 1985 4:41:39 pm PDT Lark now has its own copy of newProses; must prune out started and finished reports. changes to: EnterLarkSt Polle Zellweger (PTZ) August 19, 1985 5:03:15 pm PDT Handle Prose flushing. changes to: maxTextPktLen, maxPkts, numPkts, LarkSupervisor, SpeakText (local of LarkSupervisor), ps (local of LarkSupervisor), pd (local of LarkSupervisor), FilterText, ProseControlDone Polle Zellweger (PTZ) August 27, 1985 8:58:36 pm PDT Move local variables from LarkOutImpl.LarkSupervisor into LarkInfo record. These variables are locked by the serial nature of the Lark Supervisor rather than by explicit ENTRY procedures. Reset Prose at Lark startup & idle. changes to: DIRECTORY, ProseCmd, ProseControlDone, minControlMarker, LarkSupervisor, SpeakText (local of LarkSupervisor), ps (local of LarkSupervisor), pd (local of LarkSupervisor), resetAction (local of LarkSupervisor), FilterText, ProseFlush Swinehart, October 28, 1985 11:56:38 am PST Log => VoiceUtils, Handle => ID changes to: DIRECTORY, LarkOutImpl, ConvDesc, SmartsID, EnterLarkSt, EnterProseQueue, QueueLarkAction Zellweger (PTZ), November 4, 1985 8:39:52 pm PST Remove timeout waiting for text-to-speech to report; leave the one for tones in. changes to: WaitForAction (local of LarkSupervisor) Swinehart, April 10, 1986 10:05:07 am PST Revise telset/speakerswitch behavior to integrate $monitor mode, improve things. changes to: DIRECTORY, SmartsID, PD, EnterLarkSt, FlashWait, QueueCommandSequence, CommandsForState, CommandsForStateArray, larkCommands, telsetCommands, monitorCommands, speakerCommands Swinehart, May 17, 1986 5:58:28 pm PDT Cedar 6.1 changes to: LarkSupervisor, StopMulticast Swinehart, May 25, 1986 10:10:43 pm PDT Lark => LarkOps changes to: DIRECTORY, LarkOutImpl Swinehart, October 21, 1986 5:48:38 pm PDT Total eradication of remnants of Prose code. Introduction of LarkCall case, to allow things like synthesizers to participate. See LarkSynthesizer(impl) Swinehart, November 25, 1986 4:06:02 pm PST Remote $recovering state. It was never used. Added separate failed bit to LarkInfo. Use to determine when no action is needed. On failure, issue an input Failure event. changes to: EnterLarkSt, Fail, WaitForAction (local of LarkSupervisor), QuickState, telsetCommands, monitorCommands, speakerCommands, LSTrans, lsTrans Swinehart, December 30, 1986 2:35:44 pm PST General way to deal with conflicts in usage between telephone line and trunk in Lark. New LarkState (trkIdle), and all the reactions to it. ReportConflict allows the conflicting usage to result in an idling of the attempted conflicting conversation. changes to: EnterLarkSt, LSTrans, lsTrans, tDisconn, tDoTones, tHookState, tSwitch, tSetFwd, tLarkCall, FailInt, ReportConflict Swinehart, January 1, 1987 10:02:05 pm PST Push responsibility for FD/BD conflict stuff to higher levels again, remove from this level. Also improve Zap behavior, I think, wrt transmitOnly, audioSource. changes to: EnterLarkSt Ê'v˜šœ™IcodešœH™HK™5Jšœ<™J˜2Jšœœ ˜*Jšœ ˜—Jšœ ˜—Jšœ˜—š œœœœœ˜=Jšœœœ$œ˜VJ˜—J™’J˜J™]šœ˜JšœœžU˜bJšœ"œ˜,šœœœ˜8Jšœ.œœ˜>—Jšœ&œœ˜HJšœ˜—Jšœœœ˜;šœ˜!J™¡Jšœ œœœ˜HJ˜*J˜—J˜J™]J™šœœ˜'™*Jšœœ˜4Jšœ#˜#J˜——J™ šœ œ˜Jš œ œ œœ œ ž˜OJš œ œ œœ œ˜Jšœ˜—Jšœ˜—J˜šŸ œœœœ5˜QJ˜!šœ˜šœž˜Jšœ˜Jšœ œœ˜.Jšœ œ˜%J˜—šœž˜#Jšœœ)˜B——J˜J˜—š Ÿ œœœœ(œ˜MJšœœœ˜Jšœ œ˜Jšœ œ˜Jš œœœ œœ˜'JšœGž˜WJ˜'Jšœ ˜Jšœ9˜9J˜HJšœœœž˜CšœE˜EJ™*—JšœC˜CJ˜—J˜š Ÿœœœœœ˜7Jšœœœ˜Jšœ œ˜Jšœœœœ˜J˜š œœ#œ!œœœ˜Jšœ%˜%JšœGž˜WJšœ ˜Jšœ˜—J˜Jšœœ˜J˜J˜—šŸ œœœ œ˜Ašœ/˜/Jšœœ.˜6—J˜J˜—Jšœœž(˜VJšœ œ˜-šœ(œ˜JJ˜—š Ÿ œœœœœ˜FJšœ%œ˜G™˜J˜J˜0šœœ˜-šœ4˜4Jšœ œœ œ ˜8————™JšœM˜Mšœ"œ˜KJš œ œ œ!œœ˜P—Jšœ2˜2šœ"œ˜KJš œ œ œ!œœ˜P—Jšœ%ž-˜RJ˜Jšœ%˜%—Jšœž2˜OJšœ&ž2˜XJ˜—J™—šœ™J˜šŸœœœœœœœ˜QJšœœœ˜Jšœ"˜"Jšœ˜—š Ÿœœœœœœ˜Wšœœœ œ˜!JšœJœ˜kJšœ˜J˜—JšœKœ˜lJšœ9˜9Jš œ˜Jšœœ˜Jšœœ˜Jšœœ˜Jšœœ˜Jšœž2˜Kšœ œœ˜.Jš œœ œ&œœ˜QJšœ1ž˜I—J™J˜DJšœ˜J˜—šŸœ œ˜/JšœV™VJšœT™TJšœ6™6Jšœœœ˜Jšœœ˜ J˜šŸ œœœœœœœ˜LJšœœœ˜š˜Jšœœœœ˜#Jšœ œœœ˜ šœœœ˜Jšœ˜J˜Jšœ˜ J˜—JšœL˜LJšœœœ˜3šœœœ˜Jš œœœ3œž˜oJšœœ˜ J˜—Jšœ˜—J˜—J˜šœœ˜Jšœœ5œ˜R—šœ œ˜šœ œ˜+J˜J˜J˜J˜J˜ Jšœ œœœ˜1J˜'J˜'Jšœ˜——šœ˜ Jšœ(˜(Jšœ%˜%JšœL˜LJšœ˜—J™ãJšœœž˜Cšœœ˜'Jšž$˜$šœ˜Jšœ.œœ ˜GJšœ+œœ ˜HJšœ˜—J˜šŸœœ œž+˜MJšœœœœ˜%Jšœ0˜0šœ˜Jšœœœ˜I—JšœD˜DJšœ2˜2Jšœ\˜\J˜J˜—šœœ˜šœœ˜šœ œœ˜J˜Jšœ:ž˜Ršœœœ+˜:J˜0Jšœ:ž ˜ZJšœœ˜Jšœ˜Jšœœœœ˜E—Jšœ œ˜J˜——˜J˜(Jšœ œ#˜1Jšœ œ˜ Jš œ œœœœ˜Ušœ œœž.˜FJ˜J˜/Jšœ:ž˜Ršœœœ+˜9J˜=Jšœ9˜9Jšœœ˜Jšœ˜—J˜—šœœ˜J™:J˜Jš œ œœVœœ˜|J™J˜J˜$J˜2Jšœ œE˜WJšœ(ž˜7J˜AJ˜6J™J˜šœœœ˜J˜#Jšœ œD˜VJšœ(ž˜7JšœH˜HJ˜5Jšœœ˜"Jšœ˜—J˜—J˜—šœ˜šœœœœ˜*J˜J˜:Jš œ˜J˜—Jšœ;˜;Jšœ˜ J˜—šœ˜Jšœ˜Jšœ(˜(Jšœœ˜Jšœ˜—šœ=˜=Jšœ™—š œœœœœ˜5šœ$œ˜,Jšœ/œœ ˜E—š œœœœ˜˜>J™P—Jšœœœœ˜EJšœœœ!˜FJ˜—šœ)˜)JšœK˜K—šœ œœ˜J™5Jšœ#œœœ˜?šœ$œ˜,J˜#˜AJ™—JšœJ˜JJ˜—Jšœ1ž˜IJ˜—šœ&˜&JšœF˜F—˜Jšœ œœ/˜C—šœ œ˜Jšœ œœœ˜šœ8˜>JšœJœ˜Q—šœ˜JšœI˜IJšœ)œ˜I—J˜—Jšœ˜—Jšœ œ˜Jšœ˜—Jšœœ˜J˜J˜—šŸœœ0˜DJš œœœ œœ˜/šœ˜Jšœ.œœ ˜GJšœ+œœ ˜HJšœ˜—Jš œœœœœœ˜MJšœ œœ*œœœœ˜W˜GJšœ?˜?—šœœ˜ šœI˜IJšœ2˜2—Jšœœ˜J˜—JšœW˜WJšœœ˜š˜Jšœœ œ˜)—J˜J˜—šŸ œœœœ˜6Jšœ œœ˜šœ˜Jšœ.œœ ˜GJšœ+œœ ˜HJšœ˜—Jšœœœœ˜)Jšœ_˜_J˜OJšœœ˜š˜Jšœœ˜—J˜J™—Jšœ œD™SšŸœœœœ˜CJš œœœœœ˜Jšœœœœ˜#Jšœ œ9œ˜Sš œœœœ˜8Jšœœœ˜šœœœ œœœœœœ˜PJ™K—Jšœ˜J˜—J˜šœœœœ˜&Jšœ"œ˜B—Jšœ˜J˜—J˜šŸœœœ˜$Jšœœ˜Jšœœœœ'˜>Jšœœœœ˜(Jšœ œ˜Jšœœ˜Jšœœ˜Jšœœ˜J˜J˜'J˜Jšœœ˜ Jš œ œœœž ˜*J˜J™Sšœœ ˜Jš œœ œœœœ˜Qšœ˜ Jšœ œž˜Jšœ4ž#˜Wšœ žJ˜VJ˜"Jš œ œœœœ˜(Jšœœ˜ Jšœœœ˜šœ˜Jšœœž˜=J™äJšœœœœ˜1Jšœœœœ˜1Jš œœœœœ˜LJšœœœœž!˜MJšœœœœž˜EJšœ œž'˜>—Jšœ œ)˜9Jšœ˜Jšœ œ˜(Jšœœ.˜:Jšœœ ˜(J˜Jšœ˜Jšœ˜—˜ Jšœ/˜/J˜+Jšœ˜—˜ šœœ˜7J˜J˜J˜Jšœœ˜—Jšœ˜—šœ˜ šœœ œ(˜=Jšœ˜—Jšœœ+˜JJ˜——Jšœ˜—Jšœœœ˜!šœ&œ œ˜@Jšœ)˜)—š œ œœœž/˜OJ˜ šœ&˜,Jšœ9˜9—š œ œ œœ ˜RJšœ6œ˜>—š œœœœœ"œ˜BJš œ œœœœœ˜BJš œ œœœœœ˜Hš œœœœœ˜8˜Jš œ œœœœ˜8Jšœ ˜—Jšœ˜—Jšœœ˜ —Jšœ œ œœ˜'Jšœ œ œ&œ˜N—J˜J˜J˜$J˜J˜—JšŸœœœœ œœœ ˜EJ˜š Ÿ œœ œœœ˜AJšœœœ œ˜-Jšœ˜Jšœ œ˜Jšœœ˜Jšœœ˜šœ œœ ˜Jšœœ˜šœ˜ Jšœ œ)˜8Jšœœ œœ˜NJšœœ˜J˜J˜Jšœ$˜&Jšœž˜'—Jšœ˜—Jšœœ%˜-Jšœ˜J˜ šœ œœ ˜Jšœœ˜Jšœœ ˜šœ˜ Jšœœ˜,Jšœœ˜,Jšœ˜Jšœ˜Jšœ$˜&Jšœœ œœ ˜2Jšœ˜—Jšœ œœ˜J˜J˜Jšœ˜—J˜J˜—šŸœ œ˜Jšœœœ˜ Jšœœ˜šœ œ-œ˜@šœ% œ˜/Jšœ%˜%Jšœ*˜*——šœ œ-œ˜@šœ% œ˜/Jšœ6˜6Jšœ;˜;——šœ œ-œ˜Ašœ% œ˜/Jšœ7˜7Jšœ<˜<——šœœ-œ˜Dšœœ œ˜/Jšœ(˜(Jšœ-˜-——J˜J˜—š Ÿœœœœœ˜PJšœœœ˜ Jšœœœ˜Jšœœœ˜ J˜Jšœ œ˜(Jšœ œ˜'Jšœ œ˜'Jšœ œ˜'J˜J˜8J˜—šœ œ œœ ˜+J˜EJ˜gJ˜IJ˜——™J˜™0JšœžR™TJ™J™&J™J™J™DJ™J™/J™9J™J™:J™,™<™ J™TJ™TJ™3J™1J™>——J™K™!J™FJ™——™Itable2šÏi™Lš¡ ™ Lš¡ ™ Lš¡™Lš¡™Lš¡™Lš¡ ™ Lš¡™Lš¡™—J˜Jšœœœ˜3Jš œœœ œœ˜6J˜šœœœ™GJšœž%™5Jšœž ™Jšœž ™Jšœž ™Jšœž ™Jšœž ™Jšœ™J™—šœ#œ˜>Jšœ™šœœž˜Jšœž˜ Jšœ$ž ˜.Jšœž˜+Jšœ+ž/˜ZJšœž4˜Jšœ)ž5˜^Jšœž'™,—Jšœž ˜Jšœ ž ˜Jšœ?ž˜GJ˜Jšœœ˜)—J˜—šœ$œ˜?Jšœ™šœœž˜Jšœž˜ Jšœ+ž ˜5Jšœž˜0Jšœ1ž/˜`Jšœž4˜Kšœ/ž4˜cJšœž'™,—Jšœž ˜Jšœ ž ˜Jšœ?ž˜GJ˜Jšœœ˜.—J˜—šœ$œ˜?Jšœ ™ šœœž˜Jšœž˜ Jšœ&ž ˜0Jšœž˜(Jšœ4ž-˜aJšœžR˜iJšœ2ž4˜fJšœž'™,Jšœž ˜Jšœ ž ˜JšœCž˜KJšœ˜Jšœœ˜*—J˜—šœœœ˜GJšœž%˜5Jšœž ˜Jšœž ˜Jšœž ˜Jšœž ˜Jšœž ˜Jšœ˜J™—šœ œœ˜2J™"J˜ Jšœœ˜Jšœœ˜J˜ J˜'Jšœ˜—Jšœœ˜ J˜šœ œœ˜2J™!J˜ Jšœœ˜Jšœœ˜J˜J˜(J˜—J˜šœœœ˜4J™-J˜ Jšœœ˜Jšœœž:˜SJ˜ J˜+J˜—Jšœœ˜$J˜šœœœ˜4J™-J˜Jšœœ˜Jšœœž9œ˜SJ˜ J˜+J˜J˜—Jš œ œœœœ+˜MJš œ œœœœ1˜TJ˜—™ J˜Jšœ œ˜,Jšœœ˜&šœœ œ˜)Jšœjžœ˜‚—šœœœ œ˜4Jšœž ˜'Jšœ4ž ˜=Jšœž ˜'Jšœ7ž ˜@Jšœž˜ J˜J˜—šœ œ˜Jšœž˜JšœžR˜WJšœžM˜\Jšœ žD˜NJšœ žbÐckž¢ž˜tJšœž4˜9Jšœ žQ˜[Jšœž+˜0Jšœ ž4˜>Jšœž˜Jšœ žD˜NJšœ žT˜^Jšœ ž˜Jšœ ž˜Jšœ ž˜ Jšœž˜"JšœžH˜JJ˜J˜—š   Ðfk  £ £  £  ˜;Jš J™JJš BÐcf ˜NJš B¤ ˜NJš B¤˜QJš B¤˜WJš B¤˜VJš C¤˜VJš B¤˜TJš B¤˜QJš B¤˜QJš B¤˜RJš B¤˜RJš B¤˜RJš A¤˜QJ˜—J˜J™J˜#šœG˜GJ˜—J˜J˜+šœ„˜„J˜ÅJ˜—J˜+šœ„˜„J˜Õ—J˜J˜,šœ%˜%Jšœm™mJ˜ˆJ˜—J˜J˜šœ%˜%Jšœ™J˜cJ˜—J˜šœ%˜%JšœC™CJ˜©J˜—J˜šœ œ œ˜)Jšœ+™+J˜tJ˜——™$J˜J˜(Jšœ œœ˜DJ˜J˜(Jšœ œœ˜DJ˜šœ œœ˜Jšœ œ˜)Jšœœ˜Jšœœ˜Jšœ œœ˜Jšœ˜J˜J˜—J˜"Jšœ œœ˜8J˜šœœœ˜J˜J˜—J˜šœœœ™ Jšœ ™J™J™—J˜&Jšœ œœ˜@Jšœ!œ˜CJ˜šœ!œ˜*J˜Jšœ˜—JšœY˜YJ˜—™'K™KšœPÏrœ¥z™à—K™™'K™)Kšœ ¥ ™—™(K™Kšœ ¥)™5—™1Kšœ ¥Wœ¥œ¥#™Ì—™2Kš œ ¥œ¥œ¥œ¥œ¥#œ™ÄKšœ ¥œ¥‰œ™Ù—™2K™Kš œ ¥œC¥ œ¥œ¥œ™½—™2K™OKšœ ¥1œ¥œ¥œ™ž—™2K™9Kšœ ¥ œ™/—™2K™TKšœ ¥ ™—™4K™Kš œ ¥:œ¥œ¥œ¥ œ¥™º—™4K™áKš œ ¥Rœ¥œ¥œ¥ œ¥™ó—™+K™Kšœ ¥Y™e—™0K™PKšœ ¥ œ™3—™)K™PKšœ ¥®™º—™&K™ Kšœ ¥™)—™'K™Kšœ ¥™"—™*K™™—™+K™¬Kšœ ¥ œ¥P™–—šœ+™+Kšœû™ûKšœ ¥s™—™*K™ Kšœ ¥ ™—K™—…—ŒÜz