Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ void main() async {

void _a(EventContext context) async {
await context.wait(10);
context.sim.process(event: _c, delay: 1, name: 'C');
context.process(event: _c, delay: 1, name: 'C');
}

void _b(EventContext context) async {
Expand Down
2 changes: 1 addition & 1 deletion example/example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ void main() async {

void _a(EventContext context) async {
await context.wait(2);
context.sim.process(event: _a, delay: 2, name: 'A');
context.process(event: _a, delay: 2, name: 'A');
}
8 changes: 4 additions & 4 deletions lib/simdart.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
export 'src/start_time_handling.dart';
export 'src/simdart.dart' hide SimDartHelper;
export 'src/event.dart';
export 'src/simulation_track.dart';
export 'src/execution_priority.dart';
export 'src/interval.dart';
export 'src/observable.dart';
export 'src/resource_configurator.dart' hide ResourcesConfiguratorHelper;
export 'src/execution_priority.dart';
export 'src/simdart.dart' hide SimDartHelper;
export 'src/simulation_track.dart';
export 'src/start_time_handling.dart';
7 changes: 2 additions & 5 deletions lib/src/event.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'dart:async';

import 'package:simdart/src/simdart.dart';
import 'package:simdart/src/internal/event_scheduler_interface.dart';

/// The event to be executed.
///
Expand All @@ -12,10 +12,7 @@ typedef Event = void Function(EventContext context);
///
/// Encapsulates the information and state of an event being executed
/// within the simulation.
mixin EventContext {
/// The simulation instance managing this event.
SimDart get sim;

abstract interface class EventContext implements EventSchedulerInterface {
/// Pauses the execution of the event for the specified [delay] in simulation time.
///
/// The event is re-added to the simulation's event queue and will resume after
Expand Down
65 changes: 52 additions & 13 deletions lib/src/internal/event_action.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,25 @@ import 'dart:async';

import 'package:meta/meta.dart';
import 'package:simdart/src/event.dart';
import 'package:simdart/src/internal/time_action.dart';
import 'package:simdart/src/internal/resource.dart';
import 'package:simdart/src/internal/time_action.dart';
import 'package:simdart/src/interval.dart';
import 'package:simdart/src/simdart.dart';
import 'package:simdart/src/simulation_track.dart';

@internal
class EventAction extends TimeAction with EventContext {
class EventAction extends TimeAction implements EventContext {
EventAction(
{required this.sim,
{required SimDart sim,
required super.start,
required String? eventName,
required this.event,
required this.resourceId,
required this.onTrack,
required this.onReject,
required this.secondarySortByName})
: _eventName = eventName;
: _sim = sim,
_eventName = eventName;

/// The name of the event.
final String? _eventName;
Expand All @@ -34,8 +36,7 @@ class EventAction extends TimeAction with EventContext {

final Function? onReject;

@override
final SimDart sim;
final SimDart _sim;

/// The resource id required by the event.
final String? resourceId;
Expand Down Expand Up @@ -64,15 +65,15 @@ class EventAction extends TimeAction with EventContext {
if (resume != null) {
if (onTrack != null) {
onTrack!(SimDartHelper.buildSimulationTrack(
sim: sim, eventName: eventName, status: Status.resumed));
sim: _sim, eventName: eventName, status: Status.resumed));
}
// Resume the event if it is waiting, otherwise execute its action.
resume.call();
return;
}

Resource? resource =
SimDartHelper.getResource(sim: sim, resourceId: resourceId);
SimDartHelper.getResource(sim: _sim, resourceId: resourceId);
if (resource != null) {
_resourceAcquired = resource.acquire(this);
}
Expand All @@ -83,7 +84,7 @@ class EventAction extends TimeAction with EventContext {
status = Status.rejected;
}
onTrack!(SimDartHelper.buildSimulationTrack(
sim: sim, eventName: eventName, status: status));
sim: _sim, eventName: eventName, status: status));
}

if (_canRun) {
Expand All @@ -94,12 +95,12 @@ class EventAction extends TimeAction with EventContext {
_resourceAcquired = false;
}
// Event released some resource, others events need retry.
SimDartHelper.restoreWaitingEventsForResource(sim: sim);
SimDartHelper.restoreWaitingEventsForResource(sim: _sim);
}
});
} else {
onReject?.call();
SimDartHelper.queueOnWaitingForResource(sim: sim, action: this);
SimDartHelper.queueOnWaitingForResource(sim: _sim, action: this);
}
}

Expand All @@ -109,9 +110,9 @@ class EventAction extends TimeAction with EventContext {
return;
}

start = sim.now + delay;
start = _sim.now + delay;
// Adds it back to the loop to be resumed in the future.
SimDartHelper.addAction(sim: sim, action: this);
SimDartHelper.addAction(sim: _sim, action: this);

final Completer<void> resume = Completer<void>();
_resume = () {
Expand All @@ -124,4 +125,42 @@ class EventAction extends TimeAction with EventContext {
Future<void> _runEvent() async {
return event(this);
}

@override
int get now => _sim.now;

@override
void process(
{required Event event,
String? resourceId,
String? name,
int? start,
int? delay}) {
_sim.process(
event: event,
resourceId: resourceId,
name: name,
start: start,
delay: delay);
}

@override
void repeatProcess(
{required Event event,
int? start,
int? delay,
required Interval interval,
RejectedEventPolicy rejectedEventPolicy =
RejectedEventPolicy.keepRepeating,
String? resourceId,
String? name}) {
_sim.repeatProcess(
event: event,
interval: interval,
start: start,
delay: delay,
rejectedEventPolicy: rejectedEventPolicy,
resourceId: resourceId,
name: name);
}
}
52 changes: 52 additions & 0 deletions lib/src/internal/event_scheduler_interface.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import 'package:meta/meta.dart';
import 'package:simdart/src/event.dart';
import 'package:simdart/src/internal/now_interface.dart';
import 'package:simdart/src/interval.dart';
import 'package:simdart/src/simdart.dart';

@internal
abstract interface class EventSchedulerInterface implements NowInterface {
/// Schedules a new event to occur repeatedly based on the specified interval configuration.
///
/// [event] is the function that represents the action to be executed when the event occurs.
/// [start] is the absolute time at which the event should occur. If null, the event will
/// occur at the [now] simulation time.
/// [delay] is the number of time units after the [now] when the event has been scheduled.
/// It cannot be provided if [start] is specified.
/// [interval] defines the timing configuration for the event, including its start time and
/// the interval between repetitions. The specific details of the interval behavior depend
/// on the implementation of the [Interval].
/// [resourceId] is an optional parameter that specifies the ID of the resource required by the event.
/// [name] is an optional identifier for the event.
/// [rejectedEventPolicy] defines the behavior of the interval after a newly created event has been rejected.
///
/// Throws an [ArgumentError] if the provided interval configuration is invalid, such as
/// containing negative or inconsistent timing values.
void repeatProcess(
{required Event event,
int? start,
int? delay,
required Interval interval,
RejectedEventPolicy rejectedEventPolicy =
RejectedEventPolicy.keepRepeating,
String? resourceId,
String? name});

/// Schedules a new event to occur at a specific simulation time or after a delay.
///
/// [event] is the function that represents the action to be executed when the event occurs.
/// [start] is the absolute time at which the event should occur. If null, the event will
/// occur at the [now] simulation time.
/// [delay] is the number of time units after the [now] when the event has been scheduled.
/// It cannot be provided if [start] is specified.
/// [resourceId] is an optional parameter that specifies the ID of the resource required by the event.
/// [name] is an optional identifier for the event.
///
/// Throws an [ArgumentError] if both [start] and [delay] are provided or if [delay] is negative.
void process(
{required Event event,
String? resourceId,
String? name,
int? start,
int? delay});
}
7 changes: 7 additions & 0 deletions lib/src/internal/now_interface.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import 'package:meta/meta.dart';

@internal
abstract interface class NowInterface {
/// Gets the current simulation time.
int get now;
}
4 changes: 2 additions & 2 deletions lib/src/internal/time_loop.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import 'package:collection/collection.dart';
import 'package:meta/meta.dart';
import 'package:simdart/src/execution_priority.dart';
import 'package:simdart/src/internal/time_action.dart';
import 'package:simdart/src/internal/time_loop_mixin.dart';
import 'package:simdart/src/internal/time_loop_interface.dart';
import 'package:simdart/src/start_time_handling.dart';

/// Represents the temporal loop in the algorithm, managing the execution of actions at specified times.
@internal
class TimeLoop with TimeLoopMixin {
class TimeLoop implements TimeLoopInterface {
TimeLoop(
{required int? now,
required this.beforeRun,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import 'package:meta/meta.dart';
import 'package:simdart/src/execution_priority.dart';
import 'package:simdart/src/internal/now_interface.dart';
import 'package:simdart/src/start_time_handling.dart';

/// Represents the temporal loop in the algorithm, managing the execution of actions at specified times.
@internal
mixin TimeLoopMixin {
abstract interface class TimeLoopInterface implements NowInterface {
/// Specifies how the simulation handles start times in the past.
StartTimeHandling get startTimeHandling;

Expand Down Expand Up @@ -32,7 +33,4 @@ mixin TimeLoopMixin {
///
/// The value will be `null` if the duration has not been calculated or set.
int? get duration;

/// Gets the current simulation time.
int get now;
}
36 changes: 6 additions & 30 deletions lib/src/simdart.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@ import 'package:meta/meta.dart';
import 'package:simdart/src/event.dart';
import 'package:simdart/src/execution_priority.dart';
import 'package:simdart/src/internal/event_action.dart';
import 'package:simdart/src/internal/event_scheduler_interface.dart';
import 'package:simdart/src/internal/repeat_event_action.dart';
import 'package:simdart/src/internal/resource.dart';
import 'package:simdart/src/internal/time_action.dart';
import 'package:simdart/src/internal/time_loop.dart';
import 'package:simdart/src/internal/time_loop_mixin.dart';
import 'package:simdart/src/internal/time_loop_interface.dart';
import 'package:simdart/src/interval.dart';
import 'package:simdart/src/internal/resource.dart';
import 'package:simdart/src/resource_configurator.dart';
import 'package:simdart/src/simulation_track.dart';
import 'package:simdart/src/start_time_handling.dart';

/// Represents a discrete-event simulation engine.
class SimDart with TimeLoopMixin {
class SimDart implements TimeLoopInterface, EventSchedulerInterface {
/// Creates a simulation instance.
///
/// - [now]: The starting time of the simulation. Defaults to `0` if null.
Expand Down Expand Up @@ -104,22 +105,7 @@ class SimDart with TimeLoopMixin {
return _loop.run(until: until);
}

/// Schedules a new event to occur repeatedly based on the specified interval configuration.
///
/// [event] is the function that represents the action to be executed when the event occurs.
/// [start] is the absolute time at which the event should occur. If null, the event will
/// occur at the [now] simulation time.
/// [delay] is the number of time units after the [now] when the event has been scheduled.
/// It cannot be provided if [start] is specified.
/// [interval] defines the timing configuration for the event, including its start time and
/// the interval between repetitions. The specific details of the interval behavior depend
/// on the implementation of the [Interval].
/// [resourceId] is an optional parameter that specifies the ID of the resource required by the event.
/// [name] is an optional identifier for the event.
/// [rejectedEventPolicy] defines the behavior of the interval after a newly created event has been rejected.
///
/// Throws an [ArgumentError] if the provided interval configuration is invalid, such as
/// containing negative or inconsistent timing values.
@override
void repeatProcess(
{required Event event,
int? start,
Expand All @@ -140,17 +126,7 @@ class SimDart with TimeLoopMixin {
rejectedEventPolicy: rejectedEventPolicy);
}

/// Schedules a new event to occur at a specific simulation time or after a delay.
///
/// [event] is the function that represents the action to be executed when the event occurs.
/// [start] is the absolute time at which the event should occur. If null, the event will
/// occur at the [now] simulation time.
/// [delay] is the number of time units after the [now] when the event has been scheduled.
/// It cannot be provided if [start] is specified.
/// [resourceId] is an optional parameter that specifies the ID of the resource required by the event.
/// [name] is an optional identifier for the event.
///
/// Throws an [ArgumentError] if both [start] and [delay] are provided or if [delay] is negative.
@override
void process(
{required Event event,
String? resourceId,
Expand Down
9 changes: 3 additions & 6 deletions test/process_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ void main() {
helper = TestHelper();
helper.sim.process(
event: (context) async {
context.sim
.process(event: TestHelper.emptyEvent, delay: 10, name: 'b');
context.process(event: TestHelper.emptyEvent, delay: 10, name: 'b');
},
start: 5,
name: 'a');
Expand All @@ -47,15 +46,13 @@ void main() {
helper = TestHelper();
helper.sim.process(
event: (context) async {
context.sim
.process(event: TestHelper.emptyEvent, delay: 10, name: 'b');
context.process(event: TestHelper.emptyEvent, delay: 10, name: 'b');
},
start: 0,
name: 'a');
helper.sim.process(
event: (context) async {
context.sim
.process(event: TestHelper.emptyEvent, delay: 2, name: 'd');
context.process(event: TestHelper.emptyEvent, delay: 2, name: 'd');
},
start: 2,
name: 'c');
Expand Down
Loading