Time Service
The Time Service provides APIs to access time in Coordinated Universal Time (UTC) and International Atomic Time (TAI) time scales with up to nanosecond precision when available. It also provides APIs for scheduling periodic and non-periodic tasks in the future, which are optimized for scheduling at up to 1KHz frequency.
TMT has standardized on the use of Precision Time Protocol (PTP) as the basis of time to achieve sub-microsecond accuracy and precision between computers. The Time Service provides each participating computer with access to time synchronized by PTP.
At the telescope site, the Global Positioning System (GPS) provides an absolute time base, and a PTP grand master clock (a hardware device) synchronized to the GPS broadcasts the PTP protocol. Each computer system participating in the PTP system is synchronized with GPS and each other using the PTP protocol. For higher accuracy in time measurements hardware time stamping is required, and those computers should be fitted with PTP capable Network Interface Cards (NIC).
In order to read the time with high precision, the Time Service relies on making native calls to the Linux kernel libraries, since Java 8 supports only millisecond precision. Java Native Access (JNA) is used internally in Time Service to make native calls that return the required precision. The implementation of Time Service Scheduler is based on the Akka Scheduler, which is designed for high-throughput tasks rather than long-term, cron-like scheduling of tasks.
Dependencies¶
The Time Service comes bundled with the Framework, no additional dependency needs to be added to your build.sbt
file if using it. To use the Time Service without using the framework, add this to your build.sbt
file:
libraryDependencies += "com.github.tmtsoftware.csw" %% "csw-time-scheduler" % "4.0.0-RC1"
Applicable only to Linux Users.
If your machines are not PTP enabled/synced, you need to set the tai_offset kernel variable on your machine. This value is used in APIs like TAITime.now()
or TAITime.offset
and in scheduling APIs which take TAI time.
To set the tai offset, run the following command.
sudo ntptime -T 37
Install ntp
, ntpdate
packages to get this command on your machine.
Time Service API Flavors¶
There are total three APIs provided by the Time Service:
- TMTTime API: This API provides a way to get current UTC or TAI time.
- TMTTimeHelper API: This API provides additional time zone related functionality on top of TMTTime.
- Scheduler API: This API provides various methods to schedule future or periodic tasks.
TMTTime API¶
TMTTime represents an instantaneous point in time with nanosecond precision. It is a wrapper around Instant and provides additional information about the timescale of the instant.
TMTTime supports two timescales:
Get Current Time¶
Gets the current UTC/TAI time with nanosecond precision.
source// get current UTC time
val utcTime: UTCTime = UTCTime.now()
// get current TAI time
val taiTime: TAITime = TAITime.now()
source// get current UTC time
private final UTCTime utcTime = UTCTime.now();
// get current TAI time
private final TAITime taiTime = TAITime.now();
Note that time is returned as a UTCTime or TAITime object so that it is possible to determine the time scale of the time value by inspection.
Creating custom time instances¶
To create custom time instances, you can use the default constructors of UTC and TAI times.
source//creating a UTCTime of an hour ago
val utcTimeOfHourAgo: UTCTime = UTCTime(Instant.now().minusSeconds(3600))
//creating a TAITime of an hour ago
val taiTimeOfHourAgo: TAITime = TAITime(Instant.now().minusSeconds(3600))
source//creating a UTCTime of an hour ago
private final UTCTime utcTimeOfHourAgo = new UTCTime(Instant.now().minusSeconds(3600));
//creating a TAITime of an hour ago
private final TAITime taiTimeOfHourAgo = new TAITime(Instant.now().minusSeconds(3600));
Converting from UTC to TAI Time and Vice-versa¶
Each time object provides a way to convert to the other.
source// UTC to TAI
val taiTime: TAITime = utcTime.toTAI
// TAI to UTC
val utcTime0: UTCTime = taiTime.toUTC
source// UTC to TAI
TAITime taiTime = utcTime.toTAI();
// TAI to UTC
UTCTime utcTime = taiTime.toUTC();
TMTTimeHelper API¶
This API provides additional time zone related functionality on top of TMTTime. It allows users to get a Java ZonedDateTime representation of a TMTTime.
At Local Time Zone¶
Gets the given TMTTime at the local time zone. The local time zone is fetched from the calling system’s default time zone.
source// Get UTCTime at local timezone
val utcLocalTime: ZonedDateTime = TMTTimeHelper.atLocal(utcTime)
// Get TAITime at local timezone
val taiLocalTime: ZonedDateTime = TMTTimeHelper.atLocal(taiTime)
source// Get UTCTime at local timezone
ZonedDateTime utcLocalTime = TMTTimeHelper.atLocal(utcTime);
// Get TAITime at local timezone
ZonedDateTime taiLocalTime = TMTTimeHelper.atLocal(taiTime);
At Hawaii (HST) Timezone¶
Gets the given TMTTime at the Hawaii time zone.
source// Get UTCTime at Hawaii (HST) timezone
val utcHawaiiTime: ZonedDateTime = TMTTimeHelper.atHawaii(utcTime)
// Get TAITime at Hawaii (HST) timezone
val taiHawaiiTime: ZonedDateTime = TMTTimeHelper.atHawaii(taiTime)
source// Get UTCTime at Hawaii (HST) timezone
ZonedDateTime utcHawaiiTime = TMTTimeHelper.atHawaii(utcTime);
// Get TAITime at Hawaii (HST) timezone
ZonedDateTime taiHawaiiTime = TMTTimeHelper.atHawaii(taiTime);
At Custom Timezone¶
Gets the given TMTTime at the specified time zone.
source// Get UTCTime at specified timezone
val utcKolkataTime: ZonedDateTime = TMTTimeHelper.atZone(utcTime, ZoneId.of("Asia/Kolkata"))
// Get TAITime at specified timezone
val taiKolkataTime: ZonedDateTime = TMTTimeHelper.atZone(taiTime, ZoneId.of("Asia/Kolkata"))
source// Get UTCTime at specified timezone
ZonedDateTime utcKolkataTime = TMTTimeHelper.atZone(utcTime, ZoneId.of("Asia/Kolkata"));
// Get TAITime at specified timezone
ZonedDateTime taiKolkataTime = TMTTimeHelper.atZone(taiTime, ZoneId.of("Asia/Kolkata"));
Scheduler API¶
This API provides various methods to schedule periodic or non-periodic, one-shot tasks in the future.
For component developers, the scheduler API is provided as a TimeServiceScheduler
object in the CswContext
object injected into the ComponentHandlers class provided by the framework.
If you are not using csw-framework, you can create TimeServiceScheduler
using TimeServiceSchedulerFactory
as follows:
source// create time service scheduler using the factory method
implicit val actorSystem: typed.ActorSystem[_] = ctx.system
implicit val scheduler: Scheduler = actorSystem.scheduler
implicit val executionContext: ExecutionContext = actorSystem.executionContext
private val timeServiceScheduler: TimeServiceScheduler = new TimeServiceSchedulerFactory().make()
source// create time service scheduler using the factory method
TimeServiceScheduler scheduler = new TimeServiceSchedulerFactory(actorSystem.scheduler()).make( actorSystem.executionContext());
For all scheduler calls, an instance of Cancellable
is returned which can be used to cancel the execution of the future tasks.
Schedule Once¶
Schedules a task to execute once at the given start time. The startTime
is a TMTTime and can be either a UTCTime or TAITime.
sourcetimeServiceScheduler.scheduleOnce(utcTime) {
// do something
}
sourceRunnable task = () -> {/* do something*/};
scheduler.scheduleOnce(utcTime, task);
Note that callbacks are asynchronous and can be potentially executed in an unknown thread. Therefore, if there is a need to mutate state when the time expires, it is recommended to send a message to an Actor, and keep any mutable state within the actor where it can be managed safely. This is true for any CSW API with a callback. The schedule once with ActorRef can often be used in this scenario.
Schedule Once With ActorRef¶
Schedules sending of the message to the provided actorRef
at the given start time. The startTime
can be either UTCTime or TAITime.
sourceobject SchedulingHandler {
def behavior: Behavior[UTCTime] =
Behaviors.setup { ctx =>
//setup required for the actor
Behaviors.receiveMessage { case _ => // handle the message to execute the task on scheduled time and return new behavior
Behaviors.same
}
}
}
private val actorRef: ActorRef = ctx.spawnAnonymous(SchedulingHandler.behavior).toClassic
timeServiceScheduler.scheduleOnce(utcTime, actorRef, UTCTime.now())
sourcestatic class SchedulingHandler {
public static Behavior<UTCTime> behavior() {
// handle the message to execute the task on scheduled time
return null;
}
}
Cancellable schedule() {
ActorRef actorRef = Adapter.toClassic(ctx.asJava().spawnAnonymous(SchedulingHandler.behavior()));
return scheduler.scheduleOnce(utcTime, actorRef, UTCTime.now());
}
Schedule Periodically¶
Schedules a task to execute periodically at the given interval. The first task is executed once immediately without any initial delay followed by periodic executions. In case you do not want to start scheduling immediately, you can use the overloaded method for schedulePeriodically()
with startTime
as shown in the next example.
sourcetimeServiceScheduler.schedulePeriodically(Duration.ofMillis(50)) { /* do something*/ }
sourceRunnable task = () -> {/* do something*/};
scheduler.schedulePeriodically(Duration.ofMillis(50), task);
Schedule Periodically with Start Time¶
Schedules a task to execute periodically at the given interval. The task is executed once at the given start time followed by execution of task at each interval. The startTime
can be either UTCTime or TAITime.
sourcetimeServiceScheduler.schedulePeriodically(utcTime, Duration.ofMillis(50)) { /* do something*/ }
sourceRunnable task = () -> {/* do something*/};
scheduler.schedulePeriodically(utcTime, Duration.ofMillis(50), task);
As with the schedule once API, there is also a periodic schedule API that takes a message and ActorRef.
Technical Description¶
See Time Service Technical Description.