code refactoring
This commit is contained in:
parent
95c08139d7
commit
992689893c
10 changed files with 381 additions and 187 deletions
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
|
|
@ -81,6 +81,13 @@
|
|||
"program": "./bin/main.dart",
|
||||
"args": ["exe", "-f", "C:/Work/automation/iot/script/windows/startup.yaml"]
|
||||
},
|
||||
{
|
||||
"name": "scheduler list",
|
||||
"request": "launch",
|
||||
"type": "dart",
|
||||
"program": "./bin/main.dart",
|
||||
"args": ["scheduler", "-l"]
|
||||
},
|
||||
{
|
||||
"name": "scheduler regist",
|
||||
"request": "launch",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
scheduler:
|
||||
alias: test
|
||||
cron: '* * * * *'
|
||||
enableLog: false # (Optional) default: false
|
||||
enableLog: true # (Optional) default: false
|
||||
|
||||
property:
|
||||
workspace: .
|
||||
|
|
|
|||
20
assets/scheduler2.yaml
Normal file
20
assets/scheduler2.yaml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
scheduler:
|
||||
alias: test2
|
||||
cron: '*/5 * * * *'
|
||||
enableLog: true # (Optional) default: false
|
||||
|
||||
property:
|
||||
workspace: .
|
||||
|
||||
pipeline:
|
||||
id: main
|
||||
workflow:
|
||||
- exe: print
|
||||
|
||||
commands:
|
||||
### Print
|
||||
- command: Print
|
||||
id: print
|
||||
param:
|
||||
message: test
|
||||
|
|
@ -115,7 +115,7 @@ class CLI {
|
|||
color: color, background: background, style: style);
|
||||
}
|
||||
|
||||
static final commandNameSpace = 18;
|
||||
static final commandNameSpace = 21;
|
||||
static get serviceName => CLI.current.config.serviceName;
|
||||
static get executableFileName => CLI.current.config.executableFileName;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ class CommandScheduler extends CommandBase {
|
|||
Map<String, List<String>> get arguments => {
|
||||
'-l': ["Shows the currently operational schedulers."],
|
||||
'-r {file}': ["Register the defined file (yaml) with the scheduler."],
|
||||
'-u {alias}': ["Unregister schedules registered with the scheduler based on alias."]
|
||||
'-u {alias}': ["Unregister schedules registered with the scheduler based on alias."],
|
||||
'-e {alias} {bool}': ["Enables/disables the registered scheduler."]
|
||||
};
|
||||
@override
|
||||
String get name => 'scheduler';
|
||||
|
|
@ -25,24 +26,39 @@ class CommandScheduler extends CommandBase {
|
|||
await _scheduler.showList();
|
||||
break;
|
||||
} else if (item.startsWith('-r')) {
|
||||
var file = '';
|
||||
if (parameters.length > i + 1) {
|
||||
file = parameters[i + 1];
|
||||
var file = parameters[i + 1];
|
||||
await _scheduler.regist(file);
|
||||
} else {
|
||||
file = item.replaceFirst('-r', '').trimLeft();
|
||||
printHelp();
|
||||
}
|
||||
await _scheduler.regist(file);
|
||||
exit(0);
|
||||
} else if (item.startsWith('-u')) {
|
||||
var alias = '';
|
||||
if (parameters.length > i + 1) {
|
||||
alias = parameters[i + 1];
|
||||
var alias = parameters[i + 1];
|
||||
await _scheduler.unregist(alias);
|
||||
} else {
|
||||
alias = item.replaceFirst('-u', '').trimLeft();
|
||||
printHelp();
|
||||
}
|
||||
await _scheduler.unregist(alias);
|
||||
break;
|
||||
} else if (item.startsWith('-s')) { //hidden: start process
|
||||
} else if (item.startsWith('-e')) {
|
||||
if (parameters.length > i + 2) {
|
||||
var alias = parameters[i + 1];
|
||||
// ignore: sdk_version_since
|
||||
var enable = bool.tryParse(parameters[i + 2]);
|
||||
if(enable == null) {
|
||||
printHelp();
|
||||
} else {
|
||||
await _scheduler.enableScheduler(alias, enable);
|
||||
}
|
||||
} else {
|
||||
printHelp();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//================== for Debug ==================//
|
||||
else if (item.startsWith('-s')) { //hidden: start process
|
||||
await _scheduler.start();
|
||||
break;
|
||||
} else if (item.startsWith('-t')) { //hidden: terminate process
|
||||
|
|
|
|||
29
lib/cli/commands/scheduler/data/scheduler_data.dart
Normal file
29
lib/cli/commands/scheduler/data/scheduler_data.dart
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'scheduler_data.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class SchedulerListData {
|
||||
|
||||
late Map<String, SchedulerData> map = <String, SchedulerData>{};
|
||||
|
||||
SchedulerListData();
|
||||
|
||||
factory SchedulerListData.fromJson(Map<String, dynamic> json) =>
|
||||
_$SchedulerListDataFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$SchedulerListDataToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable()
|
||||
class SchedulerData {
|
||||
|
||||
late String alias;
|
||||
late bool enable = true;
|
||||
|
||||
SchedulerData();
|
||||
|
||||
factory SchedulerData.fromJson(Map<String, dynamic> json) =>
|
||||
_$SchedulerDataFromJson(json);
|
||||
Map<String, dynamic> toJson() => _$SchedulerDataToJson(this);
|
||||
}
|
||||
30
lib/cli/commands/scheduler/data/scheduler_data.g.dart
Normal file
30
lib/cli/commands/scheduler/data/scheduler_data.g.dart
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'scheduler_data.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
SchedulerListData _$SchedulerListDataFromJson(Map<String, dynamic> json) =>
|
||||
SchedulerListData()
|
||||
..map = (json['map'] as Map<String, dynamic>).map(
|
||||
(k, e) =>
|
||||
MapEntry(k, SchedulerData.fromJson(e as Map<String, dynamic>)),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$SchedulerListDataToJson(SchedulerListData instance) =>
|
||||
<String, dynamic>{
|
||||
'map': instance.map,
|
||||
};
|
||||
|
||||
SchedulerData _$SchedulerDataFromJson(Map<String, dynamic> json) =>
|
||||
SchedulerData()
|
||||
..alias = json['alias'] as String
|
||||
..enable = json['enable'] as bool;
|
||||
|
||||
Map<String, dynamic> _$SchedulerDataToJson(SchedulerData instance) =>
|
||||
<String, dynamic>{
|
||||
'alias': instance.alias,
|
||||
'enable': instance.enable,
|
||||
};
|
||||
120
lib/cli/commands/scheduler/scheduler_isolate.dart
Normal file
120
lib/cli/commands/scheduler/scheduler_isolate.dart
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
|
||||
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/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;
|
||||
late DataBuild build;
|
||||
late String cronExpress;
|
||||
|
||||
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 hash = await FileHasher.hash(file);
|
||||
if(hash != fileHash) {
|
||||
fileHash = hash;
|
||||
//파일 내용이 바뀔때 마다 새로 읽어 들임
|
||||
var map = Application.getMapFromYamlA(await file.readAsString());
|
||||
|
||||
//Validate yaml format
|
||||
try {
|
||||
build = DataBuild.fromJson(map!);
|
||||
} on Exception catch(e, stacktace) {
|
||||
onError('Failed to parse yaml file.');
|
||||
return;
|
||||
}
|
||||
if(build.scheduler == null) {
|
||||
onError('Scheduler entry does not exist within yaml.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Schedule.parse(build.scheduler!.cron);
|
||||
cronExpress = build.scheduler!.cron;
|
||||
} on Error catch(e, stacktace) {
|
||||
onError("Cron express '${build.scheduler!.cron}' is not a suitable format.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(cronClosed) {
|
||||
cronClosed = false;
|
||||
cron = Cron();
|
||||
cron!.schedule(Schedule.parse(cronExpress), () async {
|
||||
if(notProgress) {
|
||||
await execute(build);
|
||||
cronClosed = true;
|
||||
await cron!.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
//file이 없을시 종료
|
||||
onError("The file '${file.path}' does not exist.");
|
||||
if(cron != null) await cron?.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future execute(DataBuild data) async {
|
||||
notProgress = false;
|
||||
if(data.scheduler!.enableLog ?? false) {
|
||||
logFile = File('$logsPath/${alias}_${getDate()}_${getTime().replaceAll(':', '')}_[$pid].txt');
|
||||
log('Start process on pid [$pid]');
|
||||
}
|
||||
await Future.delayed(Duration(seconds: 30)); //To-do: delete test, exe task
|
||||
notProgress = true;
|
||||
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}));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
|
||||
import 'dart:collection';
|
||||
import 'dart:convert';
|
||||
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/platform/process.dart';
|
||||
import 'package:dart_framework/utils/os_startup.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/scheduler/data/scheduler_data.dart';
|
||||
import 'package:oto_cli/cli/commands/scheduler/scheduler_isolate.dart';
|
||||
import 'package:oto_cli/cli/commands/scheduler/scheduler_linux.dart';
|
||||
import 'package:oto_cli/cli/commands/scheduler/scheduler_osx.dart';
|
||||
import 'package:oto_cli/cli/commands/scheduler/scheduler_windows.dart';
|
||||
|
|
@ -19,21 +19,43 @@ import 'package:path/path.dart' as path;
|
|||
|
||||
class SchedulerManager {
|
||||
|
||||
static String get schedulerPath {
|
||||
static String get _schedulerPath {
|
||||
var debugPath = isDebug ? '.debug' : '';
|
||||
return '$dataPath$debugPath/scheduler';
|
||||
}
|
||||
|
||||
static String get logsPath {
|
||||
return '$schedulerPath/logs';
|
||||
static String get _logsPath {
|
||||
return '$_schedulerPath/logs';
|
||||
}
|
||||
|
||||
ProcessInfo? currentScheduler;
|
||||
ProcessInfo? _currentScheduler;
|
||||
final Map<String, IsolateHandler> _map = {};
|
||||
final logFile = File('$_logsPath/scheduler_${getDate()}_${getTime().replaceAll(':', '')}_[$pid].txt');
|
||||
|
||||
HashSet<String> get schedulers {
|
||||
String get _settingsPath {
|
||||
return '$_schedulerPath/settings.json';
|
||||
}
|
||||
|
||||
SchedulerListData get _localData {
|
||||
var file = File(_settingsPath);
|
||||
if(!file.existsSync()) {
|
||||
_localData = SchedulerListData();
|
||||
}
|
||||
return SchedulerListData.fromJson(jsonDecode(file.readAsStringSync()));
|
||||
}
|
||||
|
||||
set _localData(SchedulerListData data) {
|
||||
var file = File(_settingsPath);
|
||||
file.writeAsStringSync(JsonEncoder.withIndent(' ').convert(data.toJson()), mode: FileMode.write, flush: true);
|
||||
}
|
||||
|
||||
String get _startupID {
|
||||
return isDebug ? '$appIdendifier.debug' : appIdendifier;
|
||||
}
|
||||
|
||||
HashSet<String> get _schedulers {
|
||||
HashSet<String> hash = HashSet();
|
||||
var dir = Directory(schedulerPath);
|
||||
var dir = Directory(_schedulerPath);
|
||||
if(dir.existsSync()) {
|
||||
var fileList = dir.listSync();
|
||||
for(var item in fileList) {
|
||||
|
|
@ -47,10 +69,6 @@ class SchedulerManager {
|
|||
return hash;
|
||||
}
|
||||
|
||||
String get startupID {
|
||||
return isDebug ? '$appIdendifier.debug' : appIdendifier;
|
||||
}
|
||||
|
||||
SchedulerManager();
|
||||
|
||||
factory SchedulerManager.create() {
|
||||
|
|
@ -84,45 +102,50 @@ class SchedulerManager {
|
|||
return throw Exception('Scheduler entry does not exist within yaml.');
|
||||
}
|
||||
|
||||
var dirLogs = Directory(logsPath);
|
||||
var dirLogs = Directory(_logsPath);
|
||||
var alias = build.scheduler!.alias;
|
||||
var hash = schedulers;
|
||||
var hash = _schedulers;
|
||||
if(hash.contains(alias)) {
|
||||
return throw Exception('A scheduler with the same alias is registered. Please do it with a different alias.');
|
||||
}
|
||||
await checkStartup();
|
||||
await checkProcess();
|
||||
await startProcess();
|
||||
await _checkStartup();
|
||||
await _checkProcess();
|
||||
await _startProcess();
|
||||
|
||||
if(!dirLogs.existsSync()) {
|
||||
dirLogs.createSync(recursive: true);
|
||||
}
|
||||
await file.copy('$schedulerPath/$alias.yaml');
|
||||
await file.copy('$_schedulerPath/$alias.yaml');
|
||||
|
||||
//update settings
|
||||
var local = _localData;
|
||||
var schedulerData = SchedulerData();
|
||||
schedulerData.alias = alias;
|
||||
local.map[alias] = schedulerData;
|
||||
_localData = local;
|
||||
return simpleFuture;
|
||||
}
|
||||
|
||||
Future unregist(String alias) async {
|
||||
if(schedulers.contains(alias)) {
|
||||
var file = File('$schedulerPath/$alias.yaml');
|
||||
if(_schedulers.contains(alias)) {
|
||||
var file = File('$_schedulerPath/$alias.yaml');
|
||||
file.deleteSync();
|
||||
} else {
|
||||
return throw Exception("There is no scheduler called '$alias'.");
|
||||
}
|
||||
|
||||
await checkTerminate();
|
||||
}
|
||||
|
||||
Future checkTerminate() async {
|
||||
if(schedulers.isEmpty) {
|
||||
await unregistStartup();
|
||||
await terminate();
|
||||
var local = _localData;
|
||||
if(local.map.containsKey(alias)) {
|
||||
local.map.remove(alias);
|
||||
_localData = local;
|
||||
}
|
||||
return simpleFuture;
|
||||
|
||||
await _checkTerminate();
|
||||
}
|
||||
|
||||
Future showList() async {
|
||||
print('');
|
||||
var scheduleList = schedulers;
|
||||
var scheduleList = _schedulers;
|
||||
if(scheduleList.isEmpty) {
|
||||
print(':::: There are no registered schedulers.');
|
||||
print('');
|
||||
|
|
@ -130,53 +153,89 @@ class SchedulerManager {
|
|||
print(':::: List of currently registered schedulers.');
|
||||
print('');
|
||||
print('${' [Enable]'.padRight(20)}${'[Alias]'.padRight(20)}[Path]');
|
||||
for(var item in schedulers) {
|
||||
print('${' - true'.padRight(20)}${item.padRight(20)}$schedulerPath/$item.yaml');
|
||||
var local = _localData;
|
||||
for(var item in _schedulers) {
|
||||
var enable = true;
|
||||
if(local.map.containsKey(item)) {
|
||||
enable = local.map[item]!.enable;
|
||||
}
|
||||
print('${' - $enable'.padRight(20)}${item.padRight(20)}$_schedulerPath/$item.yaml');
|
||||
}
|
||||
print('');
|
||||
}
|
||||
checkProcess();
|
||||
_checkProcess();
|
||||
return simpleFuture;
|
||||
}
|
||||
|
||||
Future enableScheduler(String alias, bool enable) async {
|
||||
var local = _localData;
|
||||
if(_schedulers.contains(alias)) {
|
||||
var data = SchedulerData();
|
||||
data.alias = alias;
|
||||
data.enable = enable;
|
||||
local.map[alias] = data;
|
||||
_localData = local;
|
||||
var activate = enable ? 'activated' : 'deactivate';
|
||||
print('');
|
||||
print("Scheduler '$alias' has been $activate.");
|
||||
print('');
|
||||
|
||||
await _checkStartup();
|
||||
await _checkProcess();
|
||||
await _startProcess();
|
||||
|
||||
exit(0);
|
||||
} else {
|
||||
return throw Exception("There is no scheduler called '$alias'.");
|
||||
}
|
||||
}
|
||||
|
||||
Future terminate() async {
|
||||
await checkProcess();
|
||||
if(currentScheduler != null) {
|
||||
Process.killPid(currentScheduler!.pid);
|
||||
await _checkProcess();
|
||||
if(_currentScheduler != null) {
|
||||
Process.killPid(_currentScheduler!.pid);
|
||||
print('Scheduler operation stopped.');
|
||||
}
|
||||
return simpleFuture;
|
||||
}
|
||||
|
||||
Future startProcess() async {
|
||||
Future _checkTerminate() async {
|
||||
if(_schedulers.isEmpty) {
|
||||
await _unregistStartup();
|
||||
await terminate();
|
||||
}
|
||||
return simpleFuture;
|
||||
}
|
||||
|
||||
Future _startProcess() async {
|
||||
//Launchd로 자동 실행이 되지 않은경우 직접실행
|
||||
if(currentScheduler == null) {
|
||||
if(_currentScheduler == null) {
|
||||
var script = isDebug ? ' $scriptPath' : '';
|
||||
var shell = StringBuffer();
|
||||
shell.writeln('nohup $executablePath$script scheduler -s &');
|
||||
shell.writeln('disown');
|
||||
ProcessExecutor.run(shell, workspace: Directory.systemTemp.path, printStdout: false);
|
||||
await checkProcess();
|
||||
await _checkProcess();
|
||||
}
|
||||
return simpleFuture;
|
||||
}
|
||||
|
||||
Future checkProcess() async {
|
||||
Future _checkProcess() async {
|
||||
//process check
|
||||
var processes = await findProcess('scheduler -s');
|
||||
for(var process in processes) {
|
||||
var processName = process.name;
|
||||
if(processName.contains('main.dart scheduler -s') || processName.contains('$appName scheduler -s')) {
|
||||
currentScheduler = process;
|
||||
print('Scheduler is running on pid [${currentScheduler!.pid}].');
|
||||
_currentScheduler = process;
|
||||
print('Scheduler is running on pid [${_currentScheduler!.pid}].');
|
||||
break;
|
||||
}
|
||||
}
|
||||
return simpleFuture;
|
||||
}
|
||||
|
||||
Future checkStartup() async {
|
||||
if(!await OSStartup.isRegistedStartup(startupID)) {
|
||||
Future _checkStartup() async {
|
||||
if(!await OSStartup.isRegistedStartup(_startupID)) {
|
||||
List<String> param = [];
|
||||
var exe = Platform.resolvedExecutable;
|
||||
if(isDebug) {
|
||||
|
|
@ -193,168 +252,83 @@ class SchedulerManager {
|
|||
}
|
||||
param.add('scheduler');
|
||||
param.add('-s');
|
||||
await OSStartup.registStartup(exe, startupID, arguments: param);
|
||||
await OSStartup.registStartup(exe, _startupID, arguments: param);
|
||||
}
|
||||
return simpleFuture;
|
||||
}
|
||||
|
||||
Future unregistStartup() async {
|
||||
if(await OSStartup.isRegistedStartup(startupID)) {
|
||||
await OSStartup.unregistStartup(startupID);
|
||||
Future _unregistStartup() async {
|
||||
if(await OSStartup.isRegistedStartup(_startupID)) {
|
||||
await OSStartup.unregistStartup(_startupID);
|
||||
}
|
||||
return simpleFuture;
|
||||
}
|
||||
|
||||
//======================== other process ========================//
|
||||
final logFile = File('$logsPath/scheduler_${getDate()}_${getTime().replaceAll(':', '')}_[$pid].txt');
|
||||
Future start() async {
|
||||
log('Scheduler is running on pid [$pid].');
|
||||
_log('Scheduler is running on pid [$pid].');
|
||||
while(true) {
|
||||
//매초 마다 등록된 파일이 있나 체크
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
var scheduleList = schedulers;
|
||||
for(var alias in scheduleList) {
|
||||
if(!_map.containsKey(alias)) {
|
||||
log('Schduler started: $alias');
|
||||
var handler = IsolateManager.create(IsolateScheduler({'alias': alias, 'root': schedulerPath, 'logs': logsPath}));
|
||||
//To-do: 종료시 사유(by user, by error)에 따라 json에 내용을 기입하여 재실행 안하도록
|
||||
handler.addListener('terminated', onTerminated );
|
||||
var settings = _localData.map;
|
||||
for(var alias in _schedulers) {
|
||||
var enable = settings.containsKey(alias) ? settings[alias]!.enable : true;
|
||||
if(!_map.containsKey(alias) && enable) {
|
||||
_log('Schduler started: $alias');
|
||||
var handler = IsolateManager.create(IsolateScheduler({'alias': alias, 'root': _schedulerPath, 'logs': _logsPath}));
|
||||
handler.addListener('terminated', _onTerminated );
|
||||
_map[alias] = handler;
|
||||
}
|
||||
if(_map.containsKey(alias) && !enable) {
|
||||
_removeHandler(alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onTerminated(Object? data) async {
|
||||
void _removeHandler(String alias) {
|
||||
var handler = _map.remove(alias);
|
||||
handler!.exit();
|
||||
}
|
||||
|
||||
void _onTerminated(Object? data) async {
|
||||
var map = data as Map;
|
||||
var alias = map['alias'] as String;
|
||||
var type = map['type'] as String;
|
||||
var message = map['message'] as String;
|
||||
var handler = _map.remove(alias);
|
||||
handler!.exit();
|
||||
_removeHandler(alias);
|
||||
switch(type) {
|
||||
case 'error':
|
||||
log('[Error] $message');
|
||||
_log('[Error] $message');
|
||||
break;
|
||||
}
|
||||
|
||||
if(schedulers.isEmpty) log('Scheduler operation stopped.');
|
||||
await checkTerminate();
|
||||
}
|
||||
|
||||
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()}');
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
late DataBuild build;
|
||||
late String cronExpress;
|
||||
|
||||
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 hash = await FileHasher.hash(file);
|
||||
if(hash != fileHash) {
|
||||
fileHash = hash;
|
||||
//파일 내용이 바뀔때 마다 새로 읽어 들임
|
||||
var map = Application.getMapFromYamlA(await file.readAsString());
|
||||
|
||||
//Validate yaml format
|
||||
try {
|
||||
build = DataBuild.fromJson(map!);
|
||||
} on Exception catch(e, stacktace) {
|
||||
onError('Failed to parse yaml file.');
|
||||
logError(e, stacktace);
|
||||
return;
|
||||
}
|
||||
if(build.scheduler == null) {
|
||||
onError('Scheduler entry does not exist within yaml.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Schedule.parse(build.scheduler!.cron);
|
||||
cronExpress = build.scheduler!.cron;
|
||||
} on Error catch(e, stacktace) {
|
||||
onError("Cron express '${build.scheduler!.cron}' is not a suitable format.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(cronClosed) {
|
||||
cronClosed = false;
|
||||
cron = Cron();
|
||||
cron!.schedule(Schedule.parse(cronExpress), () async {
|
||||
if(notProgress) {
|
||||
await execute(build);
|
||||
cronClosed = true;
|
||||
await cron!.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
//file이 없을시 종료
|
||||
onError("The file '${file.path}' does not exist.");
|
||||
if(cron != null) await cron?.close();
|
||||
return;
|
||||
var local = _localData;
|
||||
var list = _schedulers;
|
||||
if(list.isEmpty) {
|
||||
_log('Scheduler operation stopped.');
|
||||
if(local.map.containsKey(alias)) {
|
||||
local.map.remove(alias);
|
||||
}
|
||||
} else {
|
||||
list.contains(alias);
|
||||
SchedulerData data = SchedulerData();
|
||||
if(local.map.containsKey(alias)) {
|
||||
data = local.map[alias]!;
|
||||
}
|
||||
data.alias = alias;
|
||||
data.enable = false;
|
||||
local.map[alias] = data;
|
||||
}
|
||||
_localData = local;
|
||||
await _checkTerminate();
|
||||
}
|
||||
|
||||
Future execute(DataBuild data) async {
|
||||
notProgress = false;
|
||||
logFile = File('$logsPath/${alias}_${getDate()}_${getTime().replaceAll(':', '')}_[$pid].txt');
|
||||
log('Start process on pid [$pid]');
|
||||
await Future.delayed(Duration(minutes: 1, seconds: 30)); //To-do: delete test, exe task
|
||||
notProgress = true;
|
||||
return simpleFuture;
|
||||
}
|
||||
|
||||
void log(String message) {
|
||||
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 _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}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
// ignore_for_file: non_constant_identifier_names, avoid_init_to_null
|
||||
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'package:dart_framework/utils/slack/slack_sender.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:dart_framework/core/app_data_manager.dart';
|
||||
|
|
|
|||
Loading…
Reference in a new issue