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 units; final List slots; final List personnel; final SendPort replyPort; } /// Response message returned from the validation isolate. class ValidationResponse { const ValidationResponse({ required this.findings, this.error, }); final List 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 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 validate({ required List units, required List slots, required List 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 dispose() async { _isolate?.kill(priority: Isolate.immediate); _isolate = null; _isolateSendPort = null; _started = false; } }