Unit AlertBase; { All alert definitions must inherit from AlertBase. } Interface Uses GenericDataNodes; Type TAlert = Class(TGenericDataNode) Public Function IsValid : Boolean; Override; // This is not actually used by // the alert mechanism. The // properties of an alert are // valid during and only during // a notify listeners callback. // Calling these at another time // will successfully return // values, but the values will // not be defined. Procedure GetQuality(Out Quality : Double; // Alerts can specify a Out Valid : Boolean); // quality. The default // value is "not valid." Function GetCount : Integer; Class Function DurationString(FromTime, ToTime : TDateTime) : String; // DurationString is a common way to convert the // time between two times into English, so all alerts // will use similar words. The assumption is that // ToTime > FromTime. If FromTime >= ToTime, the // wording will suggest a very short time, asuming // we are looking at roundoff error. Private // This info is set by Report and reported by GetString and GetQuality. Msg : String; FQuality : Double; FCount : Integer; FQualityValid : Boolean; Protected // This is how an alert anounces that it has information. Quality // defaults to "not valid". Procedure Report(Alert : String); Overload; Procedure Report(Alert : String; Quality : Double); Overload; Published Function GetString : String; Override; End; // This is used by numerous alerts. We start with scaler data, then convert // it to an alert when two values cross each other. TAboveOrBelow = (ccEqual, ccAbove, ccBelow); Implementation Uses DebugOutput, SysUtils, DataNodes; //////////////////////////////////////////////////////////////////////// // TAlert //////////////////////////////////////////////////////////////////////// Function TAlert.GetCount : Integer; Begin Result := FCount End; Procedure TAlert.GetQuality(Out Quality : Double; Out Valid : Boolean); Begin Valid := FQualityValid; If FQualityValid Then Quality := FQuality End; Procedure TAlert.Report(Alert : String; Quality : Double); Begin Inc(FCount); Msg := Alert; FQuality := Quality; FQualityValid := True; NotifyListeners{Soon} End; Procedure TAlert.Report(Alert : String); Begin Inc(FCount); Msg := Alert; FQualityValid := False; NotifyListeners{Soon} End; Function TAlert.GetString; Begin Result := Msg End; Function TAlert.IsValid : Boolean; Begin Result := True End; // Seems like this should be in some utility unit. Class Function TAlert.DurationString(FromTime, ToTime : TDateTime) : String; Var Duration : Double; Days, Hours, Minutes, Seconds : Integer; Begin Duration := ToTime - FromTime; If Duration * 24 >= 25 Then Begin // At least 25 hours. Report days and hours. Hours := Round(Duration * 24); Days := Hours Div 24; Hours := Hours Mod 24; If Days = 1 Then Result := '1 day' Else Result := Format('%d days', [Days]); If Hours > 1 Then Result := Format('%s %d hours', [Result, Hours]) Else If Hours = 1 Then Result := Result + ' 1 hour' End Else If Duration * 24 * 60 >= 90 Then Begin // At least 90 minutes. Report hours and minutes. Minutes := Round(Duration * 24 * 60); Hours := Minutes Div 60; Minutes := Minutes Mod 60; If Hours = 1 Then Result := '1 hour' Else Result := Format('%d hours', [Hours]); If Minutes > 1 Then Result := Format('%s %d minutes', [Result, Minutes]) Else If Minutes = 1 Then Result := Result + ' 1 minute' End Else If Duration * 24 * 60 * 60 >= 90 Then Begin // At least 90 minutes. Reports minutes and seconds. Seconds := Round(Duration * 24 * 60 * 60); Minutes := Seconds Div 60; Seconds := Seconds Mod 60; If Minutes = 1 Then Result := '1 minute' Else Result := Format('%d minutes', [Minutes]); If Seconds > 1 Then Result := Format('%s %d seconds', [Result, Seconds]) Else If Seconds = 1 Then Result := Result + ' 1 second' End Else Begin // Report only seconds. Seconds := Round(Duration * 24 * 60 * 60); If Seconds > 1 Then Result := Format('%d seconds', [Seconds]) Else If Seconds = 1 Then Result := '1 second' Else Result := 'Less than 1 second' End End; End.