155 lines
4.6 KiB
Dart
155 lines
4.6 KiB
Dart
import 'dart:isolate';
|
|
|
|
import 'package:uto_manager/domain/entities/personnel_record.dart';
|
|
import 'package:uto_manager/domain/entities/position_slot.dart';
|
|
import 'package:uto_manager/domain/entities/unit_node.dart';
|
|
import 'package:uto_manager/domain/services/mtoe_validation_engine.dart';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Message types
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// Request message sent to the validation isolate.
|
|
class ValidationRequest {
|
|
const ValidationRequest({
|
|
required this.units,
|
|
required this.slots,
|
|
required this.personnel,
|
|
required this.replyPort,
|
|
});
|
|
|
|
final List<UnitNode> units;
|
|
final List<PositionSlot> slots;
|
|
final List<PersonnelRecord> personnel;
|
|
final SendPort replyPort;
|
|
}
|
|
|
|
/// Response message returned from the validation isolate.
|
|
class ValidationResponse {
|
|
const ValidationResponse({
|
|
required this.findings,
|
|
this.error,
|
|
});
|
|
|
|
final List<ValidationFinding> findings;
|
|
final String? error;
|
|
|
|
bool get hasError => error != null;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Isolate entry point
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// Entry point for the background validation isolate.
|
|
///
|
|
/// Receives [ValidationRequest] messages on [receivePort], runs the
|
|
/// [MtoeValidationEngine], and sends a [ValidationResponse] back via the
|
|
/// request's [ValidationRequest.replyPort].
|
|
void validationIsolateEntryPoint(SendPort mainSendPort) {
|
|
final receivePort = ReceivePort();
|
|
// Send our receive port to the main isolate so it can send us requests.
|
|
mainSendPort.send(receivePort.sendPort);
|
|
|
|
const engine = MtoeValidationEngine();
|
|
|
|
receivePort.listen((dynamic message) {
|
|
if (message is ValidationRequest) {
|
|
try {
|
|
final findings = engine.validate(
|
|
units: message.units,
|
|
slots: message.slots,
|
|
personnel: message.personnel,
|
|
);
|
|
message.replyPort.send(ValidationResponse(findings: findings));
|
|
} catch (e) {
|
|
message.replyPort.send(
|
|
ValidationResponse(findings: const [], error: e.toString()),
|
|
);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// IsolateValidationRunner
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// Manages a long-lived background [Isolate] for running MTOE validation
|
|
/// without blocking the UI thread.
|
|
///
|
|
/// Usage:
|
|
/// ```dart
|
|
/// final runner = IsolateValidationRunner();
|
|
/// await runner.start();
|
|
/// final findings = await runner.validate(units: ..., slots: ..., personnel: ...);
|
|
/// await runner.dispose();
|
|
/// ```
|
|
class IsolateValidationRunner {
|
|
Isolate? _isolate;
|
|
SendPort? _isolateSendPort;
|
|
bool _started = false;
|
|
|
|
/// Whether the isolate has been started and is ready to accept requests.
|
|
bool get isStarted => _started;
|
|
|
|
/// Starts the background validation isolate.
|
|
///
|
|
/// Must be called before [validate]. Safe to call multiple times — subsequent
|
|
/// calls are no-ops if the isolate is already running.
|
|
Future<void> start() async {
|
|
if (_started) return;
|
|
|
|
final receivePort = ReceivePort();
|
|
_isolate = await Isolate.spawn(
|
|
validationIsolateEntryPoint,
|
|
receivePort.sendPort,
|
|
debugName: 'MtoeValidationIsolate',
|
|
);
|
|
|
|
// Wait for the isolate to send back its receive port.
|
|
_isolateSendPort = await receivePort.first as SendPort;
|
|
_started = true;
|
|
}
|
|
|
|
/// Runs MTOE validation in the background isolate.
|
|
///
|
|
/// Returns a [ValidationResponse] with the findings, or an error response
|
|
/// if the isolate encountered an exception.
|
|
///
|
|
/// Throws [StateError] if [start] has not been called.
|
|
Future<ValidationResponse> validate({
|
|
required List<UnitNode> units,
|
|
required List<PositionSlot> slots,
|
|
required List<PersonnelRecord> personnel,
|
|
}) async {
|
|
if (!_started || _isolateSendPort == null) {
|
|
throw StateError(
|
|
'IsolateValidationRunner.start() must be called before validate().',
|
|
);
|
|
}
|
|
|
|
final replyPort = ReceivePort();
|
|
_isolateSendPort!.send(
|
|
ValidationRequest(
|
|
units: units,
|
|
slots: slots,
|
|
personnel: personnel,
|
|
replyPort: replyPort.sendPort,
|
|
),
|
|
);
|
|
|
|
final response = await replyPort.first as ValidationResponse;
|
|
replyPort.close();
|
|
return response;
|
|
}
|
|
|
|
/// Shuts down the background isolate and releases resources.
|
|
Future<void> dispose() async {
|
|
_isolate?.kill(priority: Isolate.immediate);
|
|
_isolate = null;
|
|
_isolateSendPort = null;
|
|
_started = false;
|
|
}
|
|
}
|