diff --git a/CHANGELOG.md b/CHANGELOG.md index 842183b..f963e47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## 0.6.3 +- Support `ScheduledTask` management. see: example/jobs.dart. + ## 0.6.2 - Added support for intervals like `1-12/2`. [#55](https://github.com/agilord/cron/pull/55) by [ysahn-aplayz](https://github.com/ysahn-aplayz) diff --git a/example/jobs.dart b/example/jobs.dart new file mode 100644 index 0000000..cec6cd6 --- /dev/null +++ b/example/jobs.dart @@ -0,0 +1,124 @@ +import 'dart:async'; + +import 'package:cron/cron.dart'; + +enum JobAction { run, pause, stop, delete } + +enum JobTime { + hourly(expr: '0 * * * *'), + daily(expr: '0 0 * * *'), + weekly(expr: '0 0 * * 0'), + monthly(expr: '0 0 1 * *'), + yearly(expr: '0 0 1 1 *'), + midday12t(expr: '0 12 * * *'), + midday13t(expr: '0 13 * * * '), + afternoon15t(expr: '0 15 * * * '), + afternoon16t(expr: '0 16 * * * '), + every1mins(expr: '*/1 * * * *'), + every2mins(expr: '*/2 * * * *'), + every3mins(expr: '*/3 * * * *'), + every5mins(expr: '*/5 * * * *'), + expression(expr: '0 * * * *'), + ; + + final String expr; + const JobTime({ + required this.expr, + }); +} + +class Job { + JobTime time; + JobAction action; + String name; + String id; + + Job( + {required this.action, + required this.time, + required this.name, + required this.id}); + + void cancel() { + action = JobAction.stop; + print('job closed.'); + } + + Job call() { + print('begin:${DateTime.now()}'); + + Future.delayed(Duration(seconds: 5)).then((value) { + print('action:$action, name:$name'); + print('__end:${DateTime.now()}'); + }); + + return this; + } +} + +void main() { + final jobName = 'jobTest'; + final jobId = 'job1234'; + + final task = Job( + action: JobAction.run, + time: JobTime.every1mins, + name: jobName, + id: jobId, + ); + + final cron = Cron(); + final schedule = Schedule.parse(task.time.expr)..name = jobId; + + // ignore: implicit_call_tearoffs + cron.schedule(schedule, task); // append + + print('startup, state:${task.action}, name:${task.name}, ${DateTime.now()}'); + // no.1, runAt, 1mins = 60secs + // no.2, runAt, 2mins + Future.delayed(Duration(seconds: 70)).then((value) { + task.action = JobAction.pause; + task.name = jobName + task.action.name.toUpperCase(); + }); + + // no.3, runAt, 3mins + Future.delayed(Duration(seconds: 130)).then((value) { + final idx = cron.indexWhere((e) => e.schedule.name == jobId); + print('find jobId:$jobId, index:$idx, ${DateTime.now()}'); + + if (idx != -1) { + task.action = JobAction.run; + task.name = jobName + task.action.name.toUpperCase(); + task.name += '_UPDATE'; + task.time = JobTime.every2mins; + + final sched = Schedule.parse(task.time.expr); + // ignore: implicit_call_tearoffs + final st = cron.schedule(sched, task, false); // not append + cron.updateAt(idx, st); + } + }); + + // no.4, RunAt, 6mins + Future.delayed(Duration(minutes: 6)).then((value) { + final count = cron.count; + if (count > 0) { + task.action = JobAction.delete; + task.name = jobName + task.action.name.toUpperCase(); + task.name += '_DELETE'; + + final len1 = cron.count; + cron.removeAt(0); + final len2 = cron.count; + print('remove index:0, length $len1 to $len2. ${DateTime.now()}'); + } + }); + + // no.5, 10mins after close. + Future.delayed(Duration(minutes: 10)).then((value) { + print('shutdown. ${DateTime.now()}'); + cron.close().then((value) { + task.cancel(); + }); + }); +} diff --git a/lib/cron.dart b/lib/cron.dart index 6ec3ac0..c19501b 100644 --- a/lib/cron.dart +++ b/lib/cron.dart @@ -20,11 +20,28 @@ abstract class Cron { /// A cron-like time-based job scheduler. factory Cron() => _Cron(); - /// Schedules a [task] running specified by the [schedule]. - ScheduledTask schedule(Schedule schedule, Task task); + /// Schedules a [task] running specified by the [schedule], if [append]. + ScheduledTask schedule(Schedule schedule, Task task, [bool append = false]); /// Closes the cron instance and doesn't accept new tasks anymore. Future close(); + + /// Update ScheduledTask [st] at position [index] in this list _schedules. + void updateAt(int index, ScheduledTask st) {} + + /// Remove the ScheduledTask at position [index] from list _schedules. + void removeAt(int index) {} + + /// The first index in the list _schedules if [test] is true, + /// if not found returns -1. + /// ```dart + /// // search schedule name = 'sched1234' in cron instance. + /// final index = cron.indexWhere((e) => e.schedule.name == 'sched1234'); + /// ``` + int indexWhere(bool Function(ScheduledTask e) test, [int start = 0]) => -1; + + /// the number of ScheduledTask in the list _schedules. + int get count => 0; } /// The cron schedule. @@ -47,6 +64,9 @@ class Schedule { /// The weekdays a Task should be started. final List? weekdays; + /// extra data, schedule name + late String name = ''; + /// Test if this schedule should run at the specified time. bool shouldRunAt(DateTime time) { if (seconds?.contains(time.second) == false) return false; @@ -168,11 +188,27 @@ class _Cron implements Cron { final _schedules = <_ScheduledTask>[]; @override - ScheduledTask schedule(Schedule schedule, Task task) { + int get count => _schedules.length; + + @override + int indexWhere(bool Function(ScheduledTask) test, [int start = 0]) => + _schedules.indexWhere(test, start); + + @override + void removeAt(int index) => _schedules.removeAt(index); + + @override + void updateAt(int i, ScheduledTask st) => + _schedules[i] = st as _ScheduledTask; + + @override + ScheduledTask schedule(Schedule schedule, Task task, [bool append = true]) { if (_closed) throw Exception('Closed.'); final st = _ScheduledTask(schedule, task); - _schedules.add(st); - _scheduleNextTick(); + if (append) { + _schedules.add(st); + _scheduleNextTick(); + } return st; } diff --git a/lib/src/constraint_parser.dart b/lib/src/constraint_parser.dart index 10bc9df..0d675b1 100644 --- a/lib/src/constraint_parser.dart +++ b/lib/src/constraint_parser.dart @@ -57,5 +57,5 @@ List? parseConstraint(dynamic constraint) { /// format and cannot be parsed or processed. class ScheduleParseException extends FormatException { /// Creates a new `FormatException` with an optional error [message]. - ScheduleParseException([String message = '']) : super(message); + ScheduleParseException([super.message]); } diff --git a/pubspec.yaml b/pubspec.yaml index 76644dd..a5b99fd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: cron description: A time-based job scheduler similar to cron. Run tasks periodically at fixed times or intervals. -version: 0.6.2 +version: 0.6.3 repository: https://github.com/agilord/cron topics: @@ -11,7 +11,7 @@ topics: - cron-time environment: - sdk: '>=2.12.0 <4.0.0' + sdk: '>=2.17.0 <4.0.0' dependencies: clock: ^1.1.1