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