1(* Ulm's Oberon Library
2  Copyright (C) 1989-1994 by University of Ulm, SAI, D-89069 Ulm, Germany
3  ----------------------------------------------------------------------------
4  Ulm's Oberon Library is free software; you can redistribute it
5  and/or modify it under the terms of the GNU Library General Public
6  License as published by the Free Software Foundation; either version
7  2 of the License, or (at your option) any later version.
8
9  Ulm's Oberon Library is distributed in the hope that it will be
10  useful, but WITHOUT ANY WARRANTY; without even the implied warranty
11  of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  Library General Public License for more details.
13
14  You should have received a copy of the GNU Library General Public
15  License along with this library; if not, write to the Free Software
16  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  ----------------------------------------------------------------------------
18  E-mail contact: oberon@mathematik.uni-ulm.de
19  ----------------------------------------------------------------------------
20  $Id: Times.om,v 1.3 2001/04/30 14:54:44 borchert Exp $
21  ----------------------------------------------------------------------------
22  $Log: Times.om,v $
23  Revision 1.3  2001/04/30 14:54:44  borchert
24  bug fix: base type is TimeRec instead of Times.TimeRec
25        (invalid self-reference)
26
27  Revision 1.2  1995/04/07 13:25:07  borchert
28  fixes due to changed if of PersistentObjects
29
30  Revision 1.1  1994/02/22  20:12:02  borchert
31  Initial revision
32
33  ----------------------------------------------------------------------------
34  AFB 12/91
35  ----------------------------------------------------------------------------
36*)
37
38MODULE ulmTimes;
39
40  IMPORT NetIO := ulmNetIO, Objects := ulmObjects, Operations := ulmOperations, PersistentObjects := ulmPersistentObjects, Scales := ulmScales,
41    Services := ulmServices, Streams := ulmStreams, Types := ulmTypes;
42
43  CONST
44    relative* = Scales.relative;
45    absolute* = Scales.absolute;
46  TYPE
47    (* the common base type of all time measures *)
48    Time* = POINTER TO TimeRec;
49    TimeRec* = RECORD (Scales.MeasureRec) END;
50
51  CONST
52    usecsPerSec = 1000000; (* 10^6 *)
53  TYPE
54    (* units of the reference implementation:
55      epoch, second and usec
56    *)
57    TimeValueRec* =
58      RECORD
59        (Objects.ObjectRec)
60        (* epoch 0: Jan. 1, 1970;
61          each epoch has a length of MAX(Scales.Value) + 1 seconds;
62          epoch may be negative:
63          -1 is the epoch just before 1970
64        *)
65        epoch*: Scales.Value;
66        (* seconds and ... *)
67        second*: Scales.Value;
68        (* ... microseconds since the beginning of the epoch *)
69        usec*: Scales.Value;
70      END;
71
72  (* ==== private datatypes for the reference scale *)
73  TYPE
74    ReferenceTime = POINTER TO ReferenceTimeRec;
75    ReferenceTimeRec =
76      RECORD
77        (TimeRec)
78        timeval: TimeValueRec;
79      END;
80  VAR
81    absType, relType: Services.Type;
82  CONST
83    epochUnit = 0; secondUnit = 1; usecUnit = 2;
84  TYPE
85    Unit = POINTER TO UnitRec;
86    UnitRec =
87      RECORD
88        (Scales.UnitRec)
89        index: Types.Int8; (* epochUnit..usecUnit *)
90      END;
91
92  VAR
93    scale*: Scales.Scale; (* reference scale *)
94    family*: Scales.Family; (* family of time scales *)
95    if: Scales.Interface;
96
97  PROCEDURE Create*(VAR time: Time; type: Types.Int8);
98    (* type = absolute or relative *)
99    VAR
100      m: Scales.Measure;
101  BEGIN
102    Scales.CreateMeasure(scale, m, type);
103    time := m(Time);
104  END Create;
105
106  PROCEDURE Normalize(VAR timeval: TimeValueRec);
107    (* make sure that second and usec >= 0 *)
108    VAR
109      toomanysecs: Scales.Value;
110      secs: Scales.Value;
111  BEGIN
112    IF timeval.second < 0 THEN
113      INC(timeval.second, 1);
114      INC(timeval.second, MAX(Scales.Value));
115      DEC(timeval.epoch);
116    END;
117    IF timeval.usec < 0 THEN
118      toomanysecs := timeval.usec DIV usecsPerSec;
119      IF toomanysecs > timeval.second THEN
120        timeval.second := - toomanysecs + MAX(Scales.Value) + 1 +
121                    timeval.second;
122        DEC(timeval.epoch);
123      ELSE
124        DEC(timeval.second, toomanysecs);
125      END;
126      timeval.usec := timeval.usec MOD usecsPerSec;
127    ELSIF timeval.usec >= usecsPerSec THEN
128      secs := timeval.usec DIV usecsPerSec;
129      IF MAX(Scales.Value) - timeval.second <= secs THEN
130        INC(timeval.second, secs);
131      ELSE
132        timeval.second := secs - (MAX(Scales.Value) - timeval.second);
133        INC(timeval.epoch);
134      END;
135      timeval.usec := timeval.usec MOD usecsPerSec;
136    END;
137  END Normalize;
138
139  PROCEDURE SetValue*(time: Time; value: TimeValueRec);
140    VAR
141      refTime:     Time;
142      measure:     Scales.Measure;
143      scaleOfTime: Scales.Scale;
144  BEGIN
145    Normalize(value);
146    IF time IS ReferenceTime THEN
147      WITH time: ReferenceTime DO
148        time.timeval := value;
149      END;
150    ELSE
151      Create(refTime, Scales.MeasureType(time));
152      refTime(ReferenceTime).timeval := value;
153      Scales.GetScale(time, scaleOfTime);
154      measure := refTime;
155      Scales.ConvertMeasure(scaleOfTime, measure);
156      Operations.Copy(measure, time);
157    END;
158  END SetValue;
159
160  PROCEDURE CreateAndSet*(VAR time: Time; type: Types.Int8;
161                  epoch, second, usec: Scales.Value);
162    VAR
163      timeval: TimeValueRec;
164  BEGIN
165    Create(time, type);
166    timeval.epoch := epoch; timeval.second := second; timeval.usec := usec;
167    SetValue(time, timeval);
168  END CreateAndSet;
169
170  PROCEDURE GetValue*(time: Time; VAR value: TimeValueRec);
171    VAR mtime: Scales.Measure;
172  BEGIN
173    IF ~(time IS ReferenceTime) THEN
174      Scales.ConvertMeasure(scale, mtime); time := mtime(Time)
175    END;
176    value := time(ReferenceTime).timeval;
177  END GetValue;
178
179  (* ===== interface procedures =================================== *)
180
181  PROCEDURE InternalCreate(scale: Scales.Scale;
182                   VAR measure: Scales.Measure; abs: BOOLEAN);
183    VAR
184      time: ReferenceTime;
185  BEGIN
186    NEW(time);
187    time.timeval.epoch := 0;
188    time.timeval.second := 0;
189    time.timeval.usec := 0;
190    IF abs THEN
191      PersistentObjects.Init(time, absType);
192    ELSE
193      PersistentObjects.Init(time, relType);
194    END;
195    measure := time;
196  END InternalCreate;
197
198  PROCEDURE InternalGetValue(measure: Scales.Measure; unit: Scales.Unit;
199                    VAR value: Scales.Value);
200  BEGIN
201    WITH measure: ReferenceTime DO WITH unit: Unit DO
202      CASE unit.index OF
203      | epochUnit:  value := measure.timeval.epoch;
204      | secondUnit:  value := measure.timeval.second;
205      | usecUnit:    value := measure.timeval.usec;
206   ELSE
207      END;
208    END; END;
209  END InternalGetValue;
210
211  PROCEDURE InternalSetValue(measure: Scales.Measure; unit: Scales.Unit;
212                    value: Scales.Value);
213  BEGIN
214    WITH measure: ReferenceTime DO WITH unit: Unit DO
215      CASE unit.index OF
216      | epochUnit:   measure.timeval.epoch := value;
217      | secondUnit:  measure.timeval.second := value;
218      | usecUnit:    measure.timeval.usec := value;
219   ELSE
220      END;
221      Normalize(measure.timeval);
222    END; END;
223  END InternalSetValue;
224
225  PROCEDURE Assign(target: Scales.Measure; source: Scales.Measure);
226  BEGIN
227    WITH target: ReferenceTime DO WITH source: ReferenceTime DO
228      target.timeval := source.timeval;
229    END; END;
230  END Assign;
231
232  PROCEDURE Op(op: Scales.Operation; op1, op2, result: Scales.Measure);
233
234    PROCEDURE Add(op1, op2: TimeValueRec; VAR result: TimeValueRec);
235    BEGIN
236      result.epoch := op1.epoch + op2.epoch;
237      IF op1.second > MAX(Scales.Value) - op2.second THEN
238        INC(result.epoch);
239        result.second := op1.second - MAX(Scales.Value) - 1 +
240                    op2.second;
241      ELSE
242        result.second := op1.second + op2.second;
243      END;
244      result.usec := op1.usec + op2.usec;
245      IF result.usec > usecsPerSec THEN
246        DEC(result.usec, usecsPerSec);
247        IF result.second = MAX(Scales.Value) THEN
248          result.second := 0; INC(result.epoch);
249        ELSE
250          INC(result.second);
251        END;
252      END;
253    END Add;
254
255    PROCEDURE Sub(op1, op2: TimeValueRec; VAR result: TimeValueRec);
256    BEGIN
257      result.epoch := op1.epoch - op2.epoch;
258      IF op1.second >= op2.second THEN
259        result.second := op1.second - op2.second;
260      ELSE
261        DEC(result.epoch);
262        result.second := - op2.second + MAX(Scales.Value) + 1 + op1.second;
263      END;
264      result.usec := op1.usec - op2.usec;
265      IF result.usec < 0 THEN
266        INC(result.usec, usecsPerSec);
267        IF result.second = 0 THEN
268          result.second := MAX(Scales.Value);
269          DEC(result.epoch);
270        ELSE
271          DEC(result.second);
272        END;
273      END;
274    END Sub;
275
276  BEGIN
277    WITH op1: ReferenceTime DO WITH op2: ReferenceTime DO
278      WITH result: ReferenceTime DO
279        CASE op OF
280        | Scales.add:  Add(op1.timeval, op2.timeval, result.timeval);
281        | Scales.sub:  Sub(op1.timeval, op2.timeval, result.timeval);
282     ELSE
283        END;
284      END;
285    END; END;
286  END Op;
287
288  PROCEDURE Compare(op1, op2: Scales.Measure) : Types.Int32;
289
290    PROCEDURE ReturnVal(val1, val2: Scales.Value) : Types.Int32;
291    BEGIN
292          IF val1 < val2 THEN
293            RETURN -1
294          ELSIF val1 > val2 THEN
295            RETURN 1
296          ELSE
297            RETURN 0
298          END;
299    END ReturnVal;
300
301  BEGIN
302    WITH op1: ReferenceTime DO
303      WITH op2: ReferenceTime DO
304            IF op1.timeval.epoch # op2.timeval.epoch THEN
305              RETURN ReturnVal(op1.timeval.epoch, op2.timeval.epoch)
306            ELSIF op1.timeval.second # op2.timeval.second THEN
307              RETURN ReturnVal(op1.timeval.second, op2.timeval.second)
308            ELSE
309              RETURN ReturnVal(op1.timeval.usec, op2.timeval.usec)
310            END;
311      END;
312    END;
313    RETURN 0;
314  END Compare;
315
316  (* ========= initialization procedures ========================== *)
317
318  PROCEDURE InitInterface;
319    VAR
320      timeType: Services.Type;
321  BEGIN
322    NEW(if);
323    if.create := InternalCreate;
324    if.getvalue := InternalGetValue; if.setvalue := InternalSetValue;
325    if.assign := Assign; if.op := Op; if.compare := Compare;
326    (* conversion procedures are not necessary *)
327
328    PersistentObjects.RegisterType(timeType, "Times.Time", "Scales.Measure",
329                         NIL);
330  END InitInterface;
331
332  PROCEDURE CreateAbs(VAR object: PersistentObjects.Object);
333    VAR
334      measure: Scales.Measure;
335  BEGIN
336    Scales.CreateAbsMeasure(scale, measure);
337    object := measure;
338  END CreateAbs;
339
340  PROCEDURE CreateRel(VAR object: PersistentObjects.Object);
341    VAR
342      measure: Scales.Measure;
343  BEGIN
344    Scales.CreateRelMeasure(scale, measure);
345    object := measure;
346  END CreateRel;
347
348  PROCEDURE Write(s: Streams.Stream;
349             object: PersistentObjects.Object) : BOOLEAN;
350  BEGIN
351    WITH object: ReferenceTime DO
352      RETURN NetIO.WriteLongInt(s, object.timeval.epoch) &
353           NetIO.WriteLongInt(s, object.timeval.second) &
354           NetIO.WriteLongInt(s, object.timeval.usec)
355    END;
356  END Write;
357
358  PROCEDURE Read(s: Streams.Stream;
359            object: PersistentObjects.Object) : BOOLEAN;
360  BEGIN
361    WITH object: ReferenceTime DO
362      RETURN NetIO.ReadLongInt(s, object.timeval.epoch) &
363           NetIO.ReadLongInt(s, object.timeval.second) &
364           NetIO.ReadLongInt(s, object.timeval.usec)
365    END;
366  END Read;
367
368  PROCEDURE InitRefScale;
369
370    VAR
371      poif: PersistentObjects.Interface;
372
373    PROCEDURE InitUnit(unitIndex: Types.Int8; name: Scales.UnitName);
374      VAR
375        unit: Unit;
376    BEGIN
377      NEW(unit); unit.index := unitIndex;
378      Scales.InitUnit(scale, unit, name);
379    END InitUnit;
380
381  BEGIN
382    NEW(scale); Scales.Init(scale, NIL, if);
383    InitUnit(epochUnit, "epoch");
384    InitUnit(secondUnit, "second");
385    InitUnit(usecUnit, "usec");
386
387    NEW(poif); poif.read := Read; poif.write := Write;
388    poif.create := CreateAbs; poif.createAndRead := NIL;
389    PersistentObjects.RegisterType(absType,
390            "Times.AbsReferenceTime", "Times.Time", poif);
391    NEW(poif); poif.read := Read; poif.write := Write;
392    poif.create := CreateRel; poif.createAndRead := NIL;
393    PersistentObjects.RegisterType(relType,
394            "Times.RelReferenceTime", "Times.Time", poif);
395  END InitRefScale;
396
397BEGIN
398  InitInterface;
399  InitRefScale;
400  NEW(family); Scales.InitFamily(family, scale);
401END ulmTimes.
402