DIRECTORY AMEvents USING [ CallDebugger ], Atom USING [ GetProp, MakeAtom, PropList, PutProp ], BasicTime USING [ earliestGMT, GMT, Now, nullGMT, Period, Unpack, Unpacked ], Commander USING [ CommandProc, GetProperty, Handle, Register ], CommandTool USING [ NextArgument ], Convert USING [ RopeFromInt ], IO, IOUtils USING [ CopyPFProcs, PFCodeProc, SetDefaultPFCodeProc, SetPFCodeProc ], MBQueue USING [ Create, Queue, QueueClientAction ], Process USING [ Pause, SecondsToTicks ], ProcessProps USING [ GetPropList ], Pup USING [ nullSocket ], PupName USING [ NameLookup ], RefTab USING [ Create, Fetch, Ref, Store ], Rope USING [ Cat, Concat, Equal, Fetch, Length, MakeRope, ROPE, Size, SkipTo, Substr ], RPC USING [ MakeKey, EncryptionKey ], SimpleMailer USING [ SendMessage ], UserCredentials USING [ Get ], UserProfile USING [ Token ], VoiceUtils ; VoiceUtilsImpl: CEDAR MONITOR -- For report mailing synchronization. IMPORTS AMEvents, Atom, BasicTime, Commander, CommandTool, Convert, IO, IOUtils, MBQueue, Process, ProcessProps, PupName, RefTab, Rope, RPC, SimpleMailer, UserCredentials, UserProfile EXPORTS VoiceUtils = { OPEN IO; pd: PUBLIC REF VoiceUtils.PD _ NEW[VoiceUtils.PD_[]]; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; WhereToReport: TYPE = VoiceUtils.WhereToReport; WhereProc: TYPE = VoiceUtils.WhereProc; WP: TYPE = REF WPRec; WPRec: TYPE = RECORD [ proc: WhereProc, fixedWhereData: REF, defaultIfNotFound: VoiceUtils.DNFProc_NIL ]; RegisterWhereToReport: PUBLIC PROC[ proc: WhereProc, where: WhereToReport, fixedWhereData: REF, defaultIfNotFound: VoiceUtils.DNFProc] = { Atom.PutProp[$ReportStreams, where, NEW[WPRec_[proc: proc, fixedWhereData: fixedWhereData, defaultIfNotFound: defaultIfNotFound]]]; }; FindWhere: PUBLIC PROC[where: WhereToReport, whereData: REF] RETURNS [s: IO.STREAM] = { wp: WP; IF where=NIL THEN RETURN[pd.sysOut]; wp _ NARROW[Atom.GetProp[$ReportStreams, where]]; IF wp=NIL THEN RETURN[pd.sysOut]; s_wp.proc[wp.fixedWhereData, whereData]; IF s#NIL THEN RETURN[s]; RETURN[IF s#NIL THEN s ELSE IF wp.defaultIfNotFound=NIL OR wp.defaultIfNotFound[where, whereData] THEN pd.sysOut ELSE NIL]; }; DoReport: PROC[remark: ROPE, where: WhereToReport, whereData: REF _NIL, problem: BOOL] RETURNS[reportValue: ROPE_NIL] = { -- NIL if report wasn't sent reportS: STREAM _ FindWhere[where, whereData]; seconds: INT; IF remark=NIL OR reportS = NIL THEN RETURN; seconds _ LogTime[reportS, where, problem]; IF seconds<0 THEN RETURN[NIL]; reportValue _ IO.PutFR[" %02d: %s\n", int[seconds], rope[remark]]; reportS.PutRope[reportValue]; -- wish I could afford looking up caller. }; Report: PUBLIC PROC[remark: ROPE, where: WhereToReport, whereData: REF _NIL] = { [] _ DoReport[remark, where, whereData, FALSE]; }; ReportFR: PUBLIC PROC[remark: ROPE, where: WhereToReport, whereData: REF, a1, a2, a3: IO.Value] = { Report[IO.PutFR[remark, a1, a2, a3], where, whereData]; }; Problem: PUBLIC PROC[ remark: ROPE, where: WhereToReport, whereData: REF, priority: VoiceUtils.ProblemPriority _ maximum] = TRUSTED { reportValue: ROPE; IF remark=NIL THEN remark_"Unspecified problem"; IF (reportValue_DoReport[remark, where, whereData, TRUE])#NIL AND pd.reportMethods[priority].reportByMail THEN MailReport[reportValue, priority]; IF pd.attended THEN AMEvents.CallDebugger[remark]; }; ProblemFR: PUBLIC PROC[ remark: ROPE, where: WhereToReport, whereData: REF, a1, a2: IO.Value, priority: VoiceUtils.ProblemPriority _ maximum] = TRUSTED { Problem[IO.PutFR[remark, a1, a2], where, whereData, priority]; }; CmdWhere: WhereProc = { pl: Atom.PropList _ ProcessProps.GetPropList[]; ch: Commander.Handle; IF pl=NIL THEN RETURN[NIL]; ch _ NARROW[Commander.GetProperty[$CommanderHandle, pl]]; IF ch=NIL THEN RETURN[NIL]; RETURN[ch.err]; }; SetAttended: Commander.CommandProc = { pd.attended _ TRUE; Report["Attended[TRUE]", $Cmd, NIL]; }; ClearAttended: Commander.CommandProc = { pd.attended _ FALSE; Report["Attended[FALSE]", $Cmd, NIL]; }; MailReports: Commander.CommandProc = { pd.reportMethods _ [ [FALSE, "Swinehart.pa"], -- minimal [FALSE, "Swinehart.pa"], -- low [FALSE, "Swinehart.pa"], -- medium [TRUE, "Swinehart.pa"], -- high (last two should be changed to LarkSupport.pa [TRUE, "Swinehart.pa"] -- maximum when operational) ]; Report["Mail reporting enabled (see VoiceUtilsImpl.pd.reportMethods)", $Cmd, NIL]; }; LogTime: PROC[s: IO.STREAM, where: ATOM, problem: BOOL] RETURNS [seconds: INT] = { oldT: REF BasicTime.Unpacked _ NARROW[RefTab.Fetch[logTimes, where].val]; now: BasicTime.GMT _ BasicTime.Now[]; nowU: BasicTime.Unpacked _ BasicTime.Unpack[now]; i1: INT; crowbar: BOOL_FALSE; crowbar _ IF problem THEN (problemCount_problemCount+1) >= pd.problemLimit ELSE (reportCount_reportCount+1) >= pd.reportLimit; IF crowbar THEN { reportCount_problemCount_0; IF pd.reportingEnabled THEN Problem["Report limits Exceeded", $System, NIL, maximum]; pd.reportingEnabled _ FALSE; intervalStartTime _ now; }; i1 _ BasicTime.Period[from: intervalStartTime, to: now]; IF i1 >= pd.limitInterval THEN { intervalStartTime _ now; IF (reportCount+problemCount) <= pd.problemLimit -- severe hysteresis! THEN pd.reportingEnabled _ TRUE; reportCount_problemCount_0; }; IF ~pd.reportingEnabled THEN RETURN[-1]; seconds _ nowU.second; nowU.second _ 0; nowU.secondsThisYear _ 0; IF oldT=NIL THEN { oldT_NEW[BasicTime.Unpacked]; []_RefTab.Store[logTimes, where, oldT]; }; IF nowU=oldT^ THEN RETURN; oldT^ _ nowU; s.PutF["%g\n", time[now]]; }; logTimes: RefTab.Ref _ RefTab.Create[]; Envelope: TYPE = REF EnvelopeBody; EnvelopeBody: TYPE = RECORD [ reportValue: ROPE, priority: VoiceUtils.ProblemPriority ]; MailReport: PROC[reportValue: ROPE, priority: VoiceUtils.ProblemPriority] = { MBQueue.QueueClientAction[mailbox, QdMailReport, NEW[EnvelopeBody_[reportValue, priority]]]; }; Q: TYPE = RECORD [ LOCK: WORD, firstEvent: LIST OF REF ]; QdMailReport: PROC[r: REF ANY] = { envelope: Envelope _ NARROW[r]; now: BasicTime.GMT; period: INT; to: ROPE; WHILE (period_BasicTime.Period[from: lastMailedReportTime, to: (now_BasicTime.Now[])]) < pd.maximumMailReportInterval DO Process.Pause[MIN[77777B, Process.SecondsToTicks[pd.maximumMailReportInterval-period]]]; ENDLOOP; cumulativeValue _ Rope.Concat[cumulativeValue, envelope.reportValue]; currentMailPriority _ MAX[currentMailPriority, envelope.priority]; TRUSTED {IF LOOPHOLE[mailbox, REF Q].firstEvent#NIL THEN RETURN;}; IF cumulativeValue=NIL THEN RETURN; to _ pd.reportMethods[currentMailPriority].reportTo; IF to#NIL THEN []_SimpleMailer.SendMessage[ to: LIST[to], subject: "** Voice Server Report", body: cumulativeValue, validate: FALSE]; lastMailedReportTime _ now; cumulativeValue _ NIL; currentMailPriority _ minimal; }; intervalStartTime: BasicTime.GMT; -- these are variables computing the limit algorithm reportCount: INT_0; problemCount: INT_0; lastMailedReportTime: BasicTime.GMT _ BasicTime.earliestGMT; currentMailPriority: VoiceUtils.ProblemPriority _ minimal; cumulativeValue: ROPE; mailbox: MBQueue.Queue _ MBQueue.Create[]; origPrintTime: IOUtils.PFCodeProc _ NIL; PrintTime: IOUtils.PFCodeProc = TRUSTED { ts: ROPE _ NIL; i: INT _ 0; zipTime: BasicTime.GMT _ LOOPHOLE[i]; WITH v: val SELECT FROM time => { SELECT v.value FROM BasicTime.nullGMT => ts _ ""; zipTime => ts_""; ENDCASE; IF ts#NIL THEN { stream.PutRope[ts]; RETURN; }; }; ENDCASE; IF origPrintTime#NIL THEN origPrintTime[stream, val, format, char]; }; Registrize: PUBLIC PROC[name: ROPE] RETURNS [ROPE] = { dot: INT; IF name=NIL THEN RETURN[NIL]; dot_Rope.SkipTo[name, 0, "."]; IF dot=name.Length[] THEN name_Rope.Concat[name, DefaultRegistry[]]; RETURN[name]; }; DefaultRegistry: PROC RETURNS [registry: ROPE] = INLINE { name: ROPE=CurrentRName[]; dot: INT=Rope.SkipTo[name, 0, "."]; IF dot=name.Length[] THEN ERROR; RETURN[name.Substr[dot]]; }; CurrentRName: PUBLIC PROC RETURNS [ROPE] = { RETURN[UserCredentials.Get[].name]; }; CurrentPasskey: PUBLIC PROC[passwordText: ROPE] RETURNS [RPC.EncryptionKey] = { IF passwordText=NIL THEN passwordText_UserCredentials.Get[].password; RETURN[RPC.MakeKey[passwordText]]; }; LowerCaseRope: PUBLIC PROC[r: ROPE] RETURNS [ROPE] = { RETURN[Rope.MakeRope[base: r, size: r.Size[], fetch: LCFetch]]}; LCFetch: SAFE PROC[data: REF, index: INT] RETURNS [c: CHAR] = TRUSTED { SELECT (c_NARROW[data,ROPE].Fetch[index]) FROM IN ['A..'Z]=>c_c+('a-'A); ENDCASE}; RnameToRspec: PUBLIC PROC[name: VoiceUtils.Rname, defaultRegistry: ROPE_NIL] RETURNS [spec: VoiceUtils.Rspec] ={ j: INT_0; i: INT; WHILE (i_Rope.SkipTo[s: name, pos: j, skip: "."])#Rope.Size[name] DO j_i+1; ENDLOOP; IF j#0 THEN defaultRegistry_Rope.Substr[base: name, start: j] ELSE j_i+1; IF Rope.Size[defaultRegistry]=0 THEN RETURN[NIL]; spec_NEW[VoiceUtils.RspecBody_[simpleName: Rope.Substr[name, 0, j-1], registry: defaultRegistry]]; }; RspecToRname: PUBLIC PROC[spec: VoiceUtils.Rspec] RETURNS [name: VoiceUtils.Rname] = { RETURN[Rope.Concat[spec.simpleName, Rope.Concat[".", spec.registry]]]; }; RspecToSortName: PUBLIC PROC[spec: VoiceUtils.Rspec] RETURNS [name: ROPE] ={ RETURN[Rope.Concat[spec.registry, Rope.Concat[".", spec.simpleName]]]; }; MakeRName: PUBLIC PROC[name: ROPE, style: VoiceUtils.RNameStyle_ rNameDotLark] RETURNS[rName: ROPE] = { s1: VoiceUtils.Rspec = RnameToRspec[name, "lark"]; s2: VoiceUtils.Rspec = RnameToRspec[s1.simpleName]; isDotLark: BOOL = s1.registry.Equal["lark", FALSE]; RETURN[SELECT style FROM rName => RspecToRname[s1], nameDotLark => IF isDotLark THEN RspecToRname[s1] ELSE RspecToRname[s1].Cat[".lark"], rNameDotLark => IF s2#NIL THEN IF isDotLark THEN RspecToRname[s1] ELSE ERROR ELSE RspecToRname[s1].Cat[".lark"], ENDCASE=>NIL]; }; OwnNetAddress: PUBLIC PROC RETURNS [netAddress: VoiceUtils.NetAddress] = { netAddress _ NetAddressFromRope[netAddressRope: "ME"]; }; NetAddressFromRope: PUBLIC PROC[netAddressRope: ROPE] RETURNS [netAddress: VoiceUtils.NetAddress_VoiceUtils.nullNetAddress] = { IF netAddressRope=NIL THEN RETURN; netAddress _ PupName.NameLookup[name: netAddressRope, default: Pup.nullSocket]; }; InstanceFromNetAddress: PUBLIC PROC[netAddress: VoiceUtils.NetAddress, suffix: ROPE_NIL] RETURNS [instance: ROPE] = { instance _ Rope.Cat[Convert.RopeFromInt[netAddress.net, 8, FALSE], "#", Convert.RopeFromInt[netAddress.host, 8, FALSE], "#", suffix]; }; MakeAtom: PUBLIC PROC[rName: VoiceUtils.Rname, case: BOOL_FALSE] RETURNS [ATOM] = { RETURN[Atom.MakeAtom[ IF ~case THEN LowerCaseRope[rName] ELSE rName]]; }; CmdOrToken: PUBLIC PROC[cmd: Commander.Handle, key: ROPE, default: ROPE] RETURNS [value: ROPE_NIL] = { value _ CommandTool.NextArgument[cmd]; IF value#NIL OR value.Equal["NIL", FALSE] THEN RETURN; value _ UserProfile.Token[key: key, default: default]; }; { r: REF = Atom.GetProp[$Interfaces, $PrintTime]; origPrintTime _ IF r=NIL THEN IOUtils.SetPFCodeProc[IOUtils.CopyPFProcs[NIL], 't, PrintTime].previous ELSE NARROW[r, REF IOUtils.PFCodeProc]^; IF r=NIL THEN Atom.PutProp[$Interfaces, $PrintTime, NEW[IOUtils.PFCodeProc _ origPrintTime]]; []_IOUtils.SetDefaultPFCodeProc['t, PrintTime]; }; pd.ch _ NARROW[Commander.GetProperty[$CommanderHandle, ProcessProps.GetPropList[]]]; IF pd.ch#NIL THEN { pd.sysIn _ pd.ch.in; pd.sysOut _ pd.ch.out; }; intervalStartTime _ BasicTime.Now[]; RegisterWhereToReport[CmdWhere, $Cmd, NIL, NIL]; Commander.Register["Attended", SetAttended, "Break on errors"]; Commander.Register["Unattended", ClearAttended, "Log on errors, then muddle on."]; Commander.Register["MailReports", MailReports, "Set up default mail-reporting methods for Problem routines. See pd.reportMethods."]; }. VoiceUtilsImpl.mesa Copyright Σ 1985, 1986, 1987 by Xerox Corporation. All rights reserved. Last modified by D. Swinehart, February 9, 1987 12:26:57 pm PST Variables Copies Reporting and Suspending Determine whether any report limits have been exceeded. If so, return -1 Associates Where atoms with print times. Queue reports, since mailing takes time. Send no more than one report in one pd.maximumMailReportInterval period. Accumulate messages in the interim. Render null times harmless in printouts RName management Net Address Functions Random Utilities case: if FALSE, case considered not important . . . all calls that expect results to match must use FALSE, since implementation is to convert rName to lower case. if TRUE, leave rope alone and make the ATOM as is. CommandTool parsing aid, to VoiceUtils If token read is "NIL" (or "nil"), return NIL -- allows arguments to be skipped. Initialization Swinehart, May 9, 1986 11:06:55 am PDT Convert to new Cedar communications package changes to: DIRECTORY, VoiceUtilsImpl, OwnNetAddress, NetAddressFromRope, InstanceFromNetAddress Swinehart, February 9, 1987 9:00:30 am PST Add mail message reporting for Problem calls. Also problem priority, so that not all problems need be reported (all will be, at present, since default is maximum.) Add control over runaway reports and problem reports. A message is posted terminating reports until the number drops below some minimum. changes to: LogTime, MailReport, Q, QdMailReport, problemCount, cumulativeValue, Commander, MailReports, IF Κ˜šœ™IcodešœH™HJšœ?™?—J˜šΟk ˜ Jšœ œ˜ Jšœœ*˜4Jšœ œœ+˜MJšœ œ1˜@Jšœ œ˜#Jšœœ˜Jšœ˜JšœœB˜OJšœœ&˜3Jšœœ˜(Jšœ œ˜#Jšœœ˜Jšœœ˜Jšœœ˜+Jšœœ0œ˜WJšœœ˜%Jšœ œ˜#Jšœœ ˜Jšœ œ ˜J˜ J˜J˜—šΟnœœœΟc&˜Dš˜J˜ Jšœ˜J˜ J˜ J˜ J˜Jšœ˜J˜J˜J˜Jšœ ˜ Jšœ˜Jšœ˜J˜Jšœ˜J˜ J˜J˜ —šœ˜Jšœœ˜—J˜—J™ J˜š œœœ œœ œ˜5J˜—šœ™J˜Jšœœœ˜Jšœœœœ˜Jšœœ˜/Jšœ œ˜'—J˜™J™Jšœœœ˜šœœœ˜J˜Jšœ˜Jšœ&˜)J˜J˜—šžœœœ˜#Jšœ7˜;Jšœ*˜*Jšœ$œ\˜ƒJšœ˜—J˜šž œœœ"œœœœ˜WJšœœ˜Jšœœœœ ˜$Jšœœ&˜1Jšœœœœ ˜!J˜(Jšœœœœ˜Jšœœœœœœœœ(œ œœ˜{Jšœ˜—J˜š žœœ œ#œœ œ˜VJšœœœŸ˜?Jšœ œ˜.Jšœ œ˜ Jš œœœ œœœ˜+J˜+Jšœ œœœ˜JšœD˜DJšœŸ)˜GJ˜J˜—š žœœœ œ#œœ˜PJšœ(œ˜/J˜—J˜š žœœœ œ#œ˜IJšœ œ ˜Jšœœ.˜7J˜J˜—šžœœœ˜Jšœœ#œ˜3Jšœ2œ˜;Jšœ œ˜Jšœœœ˜0šœ1œœ˜AJšœ(œ#˜O—Jšœ œ˜2J˜—J˜šž œ œ˜Jšœœ#œœ˜EJšœ2œ˜;Jšœœ4˜>J˜—J˜šžœ˜Jšœ/˜/Jšœ˜Jš œœœœœ˜Jšœœ.˜9Jš œœœœœ˜Jšœ ˜J˜—J˜šž œ˜&Jšœœ˜Jšœœ˜$Jšœ˜—J˜šž œ˜(Jšœœ˜Jšœ œ˜%Jšœ˜—J˜šž œ˜&˜JšœœŸ ˜#JšœœŸ˜JšœœŸ ˜"JšœœŸ5˜MJšœœŸ$˜;J˜—JšœMœ˜RJšœ˜J˜—šžœœœœ œ œœ œ˜RJšœœœ$˜IJšœœ˜%J˜1Jšœœ˜Jšœ œœ˜™Išœ œ œ1˜JJšœ/˜3—šœ œ˜J˜Jšœœ:˜UJšœœ˜J˜J˜—J˜8šœœ˜ J˜šœ/Ÿ˜FJšœœ˜ —J˜Jšœ˜—Jšœœœ˜(—J˜J˜J˜šœœœ˜Jšœœ˜J˜'J˜—Jšœ œœ˜J˜ J˜J˜J˜—˜'J™(J™—Jšœ œœ˜"šœœœ˜Jšœ œ˜Jšœ$˜$J˜J˜—šž œœœ+˜MJ™(˜0Jšœ(˜+—J˜J˜—šœœœ˜Jšœœ˜ Jšœ œœ˜J˜—J˜šž œœœœ˜"Jšœœ˜Jšœœ˜Jšœœ˜ Jšœœ˜ Jšœm™mš˜Jšœo˜ršœœ ˜Jšœ>˜>—Jšœ˜—JšœE˜EJšœœ)˜BJšœœœ œœœœ˜BJšœœœœ˜#Jšœ4˜4šœœœ˜+JšœœJœ˜Y—J˜Jšœœ˜J˜J˜J˜—JšœœŸ4˜VJšœ œ˜Jšœœ˜Jšœ œ˜