import 'dart:io'; import 'package:cron/cron.dart'; import 'package:dart_framework/data/isolate_data.dart'; import 'package:dart_framework/platform/isolate_manager.dart'; import 'package:dart_framework/utils/string_util.dart'; import 'package:dart_framework/utils/system_util.dart'; import 'package:file_hasher/file_hasher.dart'; import 'package:oto_cli/cli/commands/command_exe.dart'; import 'package:oto_cli/cli/commands/scheduler/scheduler_interval.dart'; import 'package:oto_cli/oto/application.dart'; import 'package:oto_cli/oto/data/command_data.dart'; class IsolateScheduler extends IsolateBase { late String schedulerPath; late String logsPath; late String alias; late File file; late File logFile; int fileHash = 0; bool logEnable = false; bool notProgress = true; bool cronClosed = true; int? interval; String? cronExpress; late DataBuild build; SchedulerInterval? schedulerInterval; CommandExe commandExe = CommandExe(); Cron? cron; int nextStartTime = 0; IsolateScheduler(Map data) { alias = data['alias']; schedulerPath = data['root']; logsPath = data['logs']; file = File('$schedulerPath/$alias.yaml'); } @override void onReady(Object? initData) async { while (true) { await Future.delayed(Duration(seconds: 1)); if (file.existsSync()) { var updated = false; var hash = await FileHasher.hash(file); if (hash != fileHash) { updated = true; fileHash = hash; //파일 내용이 바뀔때 마다 새로 읽어 들임 var map = Application.getMapFromYamlA(await file.readAsString()); //============ Validate yaml format ============// try { build = DataBuild.fromJson(map!); } on Exception { onError('Failed to parse yaml file.'); return; } if (build.scheduler == null) { onError('Scheduler entry does not exist within yaml.'); return; } var scheduler = build.scheduler!; if (scheduler.cron == null && scheduler.interval == null) { onError( 'A scheduler script must have either a cron entry or an interval entry.'); return; } if (scheduler.cron != null) { try { Schedule.parse(scheduler.cron!); cronExpress = scheduler.cron!; } on Error { onError( "Cron express '${build.scheduler!.cron}' is not a suitable format."); return; } } if (scheduler.interval != null) { interval = scheduler.interval!; } logEnable = scheduler.enableLog == null ? false : scheduler.enableLog!; schedulerInterval?.activate = false; } //============ execute cron/interval ============// if (cronExpress != null) { //Use cron if (cronClosed) { cronClosed = false; cron = Cron(); cron!.schedule(Schedule.parse(cronExpress!), () async { if (notProgress) { await execute(build); cronClosed = true; await cron!.close(); } }); } } else { //Use interval if (updated) { schedulerInterval = SchedulerInterval(interval!, build, execute); } } } else { //file이 없을시 종료 onError("The file '${file.path}' does not exist."); if (cron != null) await cron?.close(); return; } } } Future execute(DataBuild data) async { send(IsoMessege('log', data: "Scheduler '$alias' has started.")); notProgress = false; logFile = File( '$logsPath/[$pid]_${alias}_${getDate()}_${getTime().replaceAll(':', '')}.txt'); if (data.scheduler!.enableLog ?? false) { log('Start process on pid [$pid]'); } await commandExe.executeScheduler(data, logEnable, logFile.path); notProgress = true; send(IsoMessege('log', data: "Scheduler '$alias' is done.")); return simpleFuture; } void log(String message) { logFile.writeAsStringSync('[${getDates()}] $message\n', mode: FileMode.append, flush: true); } void logError(Exception e, StackTrace stacktace) { log('[${e.toString()}]\n${stacktace.toString()}'); } void onErrorStacktrace(Exception e, StackTrace stacktrace) { onError('$e\n$stacktrace'); } void onError(String message) { sendTerminated('error', message); } void sendTerminated(String type, String message) { send(IsoMessege('terminated', data: {'type': type, 'message': message, 'alias': alias})); } }