AveragersImpl.Mesa
Spreitzer, June 16, 1986 6:03:05 pm PDT
DIRECTORY Averagers, BasicTime, Buttons, Containers, IO, Labels, Menus, RealFns, Rope, Sliders, ViewerClasses, ViewerOps;
AveragersImpl: CEDAR MONITOR
LOCKS a USING a: Averager
IMPORTS BasicTime, Buttons, Containers, IO, Labels, RealFns, Sliders, ViewerOps
EXPORTS Averagers
=
BEGIN OPEN Averagers;
Time: TYPE = BasicTime.GMT;
Averager: TYPE = REF AveragerPrivate;
AveragerPrivate: PUBLIC TYPE = MONITORED RECORD [
logMin, logMax: INT,
min, scale: REAL,
halfLife: Seconds,
w: REAL,
average: REAL,
lastTime: Time,
container, nameLabel, halfLifeButton, valueShower: Viewer ← NIL
];
logOneHalf: REAL = RealFns.Log[10, 0.5];
halfMaxHalfLife: Seconds = LONG[12*60]*60;
sep1: INT ← 1;
sep2: INT ← 2;
Create: PUBLIC
PROC
[
viewerInfo: ViewerClasses.ViewerRec ← [],
name: ROPE,
nameWidth: INT,
halfLife: Seconds,
logMin, logMax: INT,
initialValue: REAL ← 0.0
]
RETURNS [a: Averager]
= {
a ← NEW [AveragerPrivate ← [
logMin: logMin,
logMax: logMax,
min: RealFns.Power[10, logMin],
scale: 1.0/(logMax - logMin),
halfLife: halfLife,
w: logOneHalf/halfLife,
average: initialValue,
lastTime: BasicTime.Now[],
container: Containers.Create[viewerInfo]
]];
a.nameLabel ← Labels.Create[info: [parent: a.container, wx: 0, wy: 0, ww: nameWidth, name: name, border: FALSE], paint: FALSE];
a.halfLifeButton ← Buttons.Create[info: [parent: a.container, wx: a.nameLabel.wx+a.nameLabel.ww+sep1, wy: 0, border: FALSE, name: FmtPeriod[a.halfLife]], proc: TweakHalfLife, clientData: a, paint: FALSE];
a.valueShower ← Sliders.Create[info: [parent: a.container, wx: a.halfLifeButton.wx+a.halfLifeButton.ww+sep1, wy: 0, ww: 10, wh: a.container.ch, border: FALSE], orientation: horizontal, value: Normalize[a, initialValue], clientData: a, paint: FALSE];
Containers.ChildXBound[a.container, a.valueShower];
ViewerOps.PaintViewer[a.container, all];
};
Normalize: PROC [a: Averager, value: REAL] RETURNS [normalized: REAL] = INLINE {
normalized ← MIN[1.0, MAX[RealFns.Log[10, MAX[a.min, ABS[value]]]-a.logMin, 0] * a.scale]};
TweakHalfLife: PROC [parent: REF ANY, clientData: REF ANYNIL,
mouseButton: Menus.MouseButton ← red, shift, control: BOOLFALSE]--Buttons.ButtonProc-- = {
a: Averager = NARROW[clientData];
EnterAnd: ENTRY PROC [a: Averager] = {
newHalfLife: Seconds = SELECT mouseButton FROM
red => MAX[MIN[halfMaxHalfLife, a.halfLife]*2, 1],
yellow => a.halfLife,
blue => MAX[a.halfLife/2, 1],
ENDCASE => ERROR;
IF newHalfLife # a.halfLife THEN {
a.halfLife ← newHalfLife;
a.w ← logOneHalf/a.halfLife;
Buttons.ReLabel[a.halfLifeButton, FmtPeriod[a.halfLife]];
};
};
EnterAnd[a];
};
AsViewer: PUBLIC PROC [a: Averager] RETURNS [v: Viewer] = {
v ← a.container};
AddSample: PUBLIC ENTRY PROC [a: Averager, amount: REAL] = {
ENABLE UNWIND => NULL;
now: Time = BasicTime.Now[];
period: Seconds = BasicTime.Period[a.lastTime, now];
keep: REAL = RealFns.Exp[a.w * period];
a.average ← keep*(a.average-amount) + amount;
Sliders.SetContents[a.valueShower, Normalize[a, a.average]];
};
FmtPeriod: PROC [period: Seconds] RETURNS [asRope: ROPE] = {
hours: INT = period/3600;
rem1: INT = period - hours*3600;
minutes: INT = rem1 / 60;
seconds: INT = rem1 - minutes*60;
asRope ← IO.PutFR["%02g:%02g:%02g", [integer[hours]], [integer[minutes]], [integer[seconds]]];
};
END.