(* Ulm's Oberon Library Copyright (C) 1989-1994 by University of Ulm, SAI, D-89069 Ulm, Germany ---------------------------------------------------------------------------- Ulm's Oberon Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Ulm's Oberon Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ---------------------------------------------------------------------------- E-mail contact: oberon@mathematik.uni-ulm.de ---------------------------------------------------------------------------- $Id: Times.om,v 1.3 2001/04/30 14:54:44 borchert Exp $ ---------------------------------------------------------------------------- $Log: Times.om,v $ Revision 1.3 2001/04/30 14:54:44 borchert bug fix: base type is TimeRec instead of Times.TimeRec (invalid self-reference) Revision 1.2 1995/04/07 13:25:07 borchert fixes due to changed if of PersistentObjects Revision 1.1 1994/02/22 20:12:02 borchert Initial revision ---------------------------------------------------------------------------- AFB 12/91 ---------------------------------------------------------------------------- *) MODULE ulmTimes; IMPORT NetIO := ulmNetIO, Objects := ulmObjects, Operations := ulmOperations, PersistentObjects := ulmPersistentObjects, Scales := ulmScales, Services := ulmServices, Streams := ulmStreams, SYSTEM; CONST relative* = Scales.relative; absolute* = Scales.absolute; TYPE (* the common base type of all time measures *) Time* = POINTER TO TimeRec; TimeRec* = RECORD (Scales.MeasureRec) END; CONST usecsPerSec = 1000000; (* 10^6 *) TYPE (* units of the reference implementation: epoch, second and usec *) TimeValueRec* = RECORD (Objects.ObjectRec) (* epoch 0: Jan. 1, 1970; each epoch has a length of MAX(Scales.Value) + 1 seconds; epoch may be negative: -1 is the epoch just before 1970 *) epoch*: Scales.Value; (* seconds and ... *) second*: Scales.Value; (* ... microseconds since the beginning of the epoch *) usec*: Scales.Value; END; (* ==== private datatypes for the reference scale *) TYPE ReferenceTime = POINTER TO ReferenceTimeRec; ReferenceTimeRec = RECORD (TimeRec) timeval: TimeValueRec; END; VAR absType, relType: Services.Type; CONST epochUnit = 0; secondUnit = 1; usecUnit = 2; TYPE Unit = POINTER TO UnitRec; UnitRec = RECORD (Scales.UnitRec) index: SHORTINT; (* epochUnit..usecUnit *) END; VAR scale*: Scales.Scale; (* reference scale *) family*: Scales.Family; (* family of time scales *) if: Scales.Interface; PROCEDURE Create*(VAR time: Time; type: SHORTINT); (* type = absolute or relative *) VAR m: Scales.Measure; BEGIN Scales.CreateMeasure(scale, m, type); time := m(Time); END Create; PROCEDURE Normalize(VAR timeval: TimeValueRec); (* make sure that second and usec >= 0 *) VAR toomanysecs: Scales.Value; secs: Scales.Value; BEGIN IF timeval.second < 0 THEN INC(timeval.second, 1); INC(timeval.second, MAX(Scales.Value)); DEC(timeval.epoch); END; IF timeval.usec < 0 THEN toomanysecs := timeval.usec DIV usecsPerSec; IF toomanysecs > timeval.second THEN timeval.second := - toomanysecs + MAX(Scales.Value) + 1 + timeval.second; DEC(timeval.epoch); ELSE DEC(timeval.second, toomanysecs); END; timeval.usec := timeval.usec MOD usecsPerSec; ELSIF timeval.usec >= usecsPerSec THEN secs := timeval.usec DIV usecsPerSec; IF MAX(Scales.Value) - timeval.second <= secs THEN INC(timeval.second, secs); ELSE timeval.second := secs - (MAX(Scales.Value) - timeval.second); INC(timeval.epoch); END; timeval.usec := timeval.usec MOD usecsPerSec; END; END Normalize; PROCEDURE SetValue*(time: Time; value: TimeValueRec); VAR refTime: Time; scaleOfTime: Scales.Scale; BEGIN Normalize(value); IF time IS ReferenceTime THEN WITH time: ReferenceTime DO time.timeval := value; END; ELSE Create(refTime, Scales.MeasureType(time)); refTime(ReferenceTime).timeval := value; Scales.GetScale(time, scaleOfTime); Scales.ConvertMeasure(scaleOfTime, SYSTEM.VAL(Scales.Measure, refTime)); Operations.Copy(refTime, time); END; END SetValue; PROCEDURE CreateAndSet*(VAR time: Time; type: SHORTINT; epoch, second, usec: Scales.Value); VAR timeval: TimeValueRec; BEGIN Create(time, type); timeval.epoch := epoch; timeval.second := second; timeval.usec := usec; SetValue(time, timeval); END CreateAndSet; PROCEDURE GetValue*(time: Time; VAR value: TimeValueRec); BEGIN IF ~(time IS ReferenceTime) THEN Scales.ConvertMeasure(scale, SYSTEM.VAL(Scales.Measure, time)); END; value := time(ReferenceTime).timeval; END GetValue; (* ===== interface procedures =================================== *) PROCEDURE InternalCreate(scale: Scales.Scale; VAR measure: Scales.Measure; abs: BOOLEAN); VAR time: ReferenceTime; BEGIN NEW(time); time.timeval.epoch := 0; time.timeval.second := 0; time.timeval.usec := 0; IF abs THEN PersistentObjects.Init(time, absType); ELSE PersistentObjects.Init(time, relType); END; measure := time; END InternalCreate; PROCEDURE InternalGetValue(measure: Scales.Measure; unit: Scales.Unit; VAR value: Scales.Value); BEGIN WITH measure: ReferenceTime DO WITH unit: Unit DO CASE unit.index OF | epochUnit: value := measure.timeval.epoch; | secondUnit: value := measure.timeval.second; | usecUnit: value := measure.timeval.usec; END; END; END; END InternalGetValue; PROCEDURE InternalSetValue(measure: Scales.Measure; unit: Scales.Unit; value: Scales.Value); BEGIN WITH measure: ReferenceTime DO WITH unit: Unit DO CASE unit.index OF | epochUnit: measure.timeval.epoch := value; | secondUnit: measure.timeval.second := value; | usecUnit: measure.timeval.usec := value; END; Normalize(measure.timeval); END; END; END InternalSetValue; PROCEDURE Assign(target: Scales.Measure; source: Scales.Measure); BEGIN WITH target: ReferenceTime DO WITH source: ReferenceTime DO target.timeval := source.timeval; END; END; END Assign; PROCEDURE Op(op: Scales.Operation; op1, op2, result: Scales.Measure); PROCEDURE Add(op1, op2: TimeValueRec; VAR result: TimeValueRec); BEGIN result.epoch := op1.epoch + op2.epoch; IF op1.second > MAX(Scales.Value) - op2.second THEN INC(result.epoch); result.second := op1.second - MAX(Scales.Value) - 1 + op2.second; ELSE result.second := op1.second + op2.second; END; result.usec := op1.usec + op2.usec; IF result.usec > usecsPerSec THEN DEC(result.usec, usecsPerSec); IF result.second = MAX(Scales.Value) THEN result.second := 0; INC(result.epoch); ELSE INC(result.second); END; END; END Add; PROCEDURE Sub(op1, op2: TimeValueRec; VAR result: TimeValueRec); BEGIN result.epoch := op1.epoch - op2.epoch; IF op1.second >= op2.second THEN result.second := op1.second - op2.second; ELSE DEC(result.epoch); result.second := - op2.second + MAX(Scales.Value) + 1 + op1.second; END; result.usec := op1.usec - op2.usec; IF result.usec < 0 THEN INC(result.usec, usecsPerSec); IF result.second = 0 THEN result.second := MAX(Scales.Value); DEC(result.epoch); ELSE DEC(result.second); END; END; END Sub; BEGIN WITH op1: ReferenceTime DO WITH op2: ReferenceTime DO WITH result: ReferenceTime DO CASE op OF | Scales.add: Add(op1.timeval, op2.timeval, result.timeval); | Scales.sub: Sub(op1.timeval, op2.timeval, result.timeval); END; END; END; END; END Op; PROCEDURE Compare(op1, op2: Scales.Measure) : INTEGER; PROCEDURE ReturnVal(val1, val2: Scales.Value) : INTEGER; BEGIN IF val1 < val2 THEN RETURN -1 ELSIF val1 > val2 THEN RETURN 1 ELSE RETURN 0 END; END ReturnVal; BEGIN WITH op1: ReferenceTime DO WITH op2: ReferenceTime DO IF op1.timeval.epoch # op2.timeval.epoch THEN RETURN ReturnVal(op1.timeval.epoch, op2.timeval.epoch) ELSIF op1.timeval.second # op2.timeval.second THEN RETURN ReturnVal(op1.timeval.second, op2.timeval.second) ELSE RETURN ReturnVal(op1.timeval.usec, op2.timeval.usec) END; END; END; END Compare; (* ========= initialization procedures ========================== *) PROCEDURE InitInterface; VAR timeType: Services.Type; BEGIN NEW(if); if.create := InternalCreate; if.getvalue := InternalGetValue; if.setvalue := InternalSetValue; if.assign := Assign; if.op := Op; if.compare := Compare; (* conversion procedures are not necessary *) PersistentObjects.RegisterType(timeType, "Times.Time", "Scales.Measure", NIL); END InitInterface; PROCEDURE CreateAbs(VAR object: PersistentObjects.Object); VAR measure: Scales.Measure; BEGIN Scales.CreateAbsMeasure(scale, measure); object := measure; END CreateAbs; PROCEDURE CreateRel(VAR object: PersistentObjects.Object); VAR measure: Scales.Measure; BEGIN Scales.CreateRelMeasure(scale, measure); object := measure; END CreateRel; PROCEDURE Write(s: Streams.Stream; object: PersistentObjects.Object) : BOOLEAN; BEGIN WITH object: ReferenceTime DO RETURN NetIO.WriteLongInt(s, object.timeval.epoch) & NetIO.WriteLongInt(s, object.timeval.second) & NetIO.WriteLongInt(s, object.timeval.usec) END; END Write; PROCEDURE Read(s: Streams.Stream; object: PersistentObjects.Object) : BOOLEAN; BEGIN WITH object: ReferenceTime DO RETURN NetIO.ReadLongInt(s, object.timeval.epoch) & NetIO.ReadLongInt(s, object.timeval.second) & NetIO.ReadLongInt(s, object.timeval.usec) END; END Read; PROCEDURE InitRefScale; VAR poif: PersistentObjects.Interface; PROCEDURE InitUnit(unitIndex: SHORTINT; name: Scales.UnitName); VAR unit: Unit; BEGIN NEW(unit); unit.index := unitIndex; Scales.InitUnit(scale, unit, name); END InitUnit; BEGIN NEW(scale); Scales.Init(scale, NIL, if); InitUnit(epochUnit, "epoch"); InitUnit(secondUnit, "second"); InitUnit(usecUnit, "usec"); NEW(poif); poif.read := Read; poif.write := Write; poif.create := CreateAbs; poif.createAndRead := NIL; PersistentObjects.RegisterType(absType, "Times.AbsReferenceTime", "Times.Time", poif); NEW(poif); poif.read := Read; poif.write := Write; poif.create := CreateRel; poif.createAndRead := NIL; PersistentObjects.RegisterType(relType, "Times.RelReferenceTime", "Times.Time", poif); END InitRefScale; BEGIN InitInterface; InitRefScale; NEW(family); Scales.InitFamily(family, scale); END ulmTimes.