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
;
Reporting and Suspending
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𡤍oReport[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;
Determine whether any report limits have been exceeded. If so, return -1
crowbar ←
IF problem
THEN (problemCount←problemCount+1) >= pd.problemLimit
ELSE (reportCount←reportCount+1) >= pd.reportLimit;
IF crowbar
THEN {
reportCount←problemCount𡤀
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𡤀
};
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[];
Associates Where atoms with print times.
Envelope: TYPE = REF EnvelopeBody;
EnvelopeBody:
TYPE =
RECORD [
reportValue: ROPE,
priority: VoiceUtils.ProblemPriority
];
MailReport:
PROC[reportValue:
ROPE, priority: VoiceUtils.ProblemPriority] = {
Queue reports, since mailing takes time.
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;
Send no more than one report in one pd.maximumMailReportInterval period. Accumulate messages in the interim.
WHILE
(periodsicTime.Period[from: lastMailedReportTime, to: (nowsicTime.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𡤀
problemCount: INT𡤀
lastMailedReportTime: BasicTime.GMT ← BasicTime.earliestGMT;
currentMailPriority: VoiceUtils.ProblemPriority ← minimal;
cumulativeValue: ROPE;
mailbox: MBQueue.Queue ← MBQueue.Create[];
RnameToRspec:
PUBLIC
PROC[name: VoiceUtils.Rname, defaultRegistry:
ROPE←
NIL]
RETURNS [spec: VoiceUtils.Rspec] ={
j: INT𡤀
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];
};
Initialization
{
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."];
}.