Skip to content

Commit

Permalink
Observer to receive data throughout the simulation. #14
Browse files Browse the repository at this point in the history
  • Loading branch information
caduandrade committed Feb 28, 2025
1 parent 1c3ee0f commit a7a4cdb
Show file tree
Hide file tree
Showing 21 changed files with 323 additions and 264 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Added
* `runState` getter to determine whether the simulation has not started, is running, or has completed.
* `stop` method to manually stop the simulation before it completes.
* Observer to receive data throughout the simulation.

## 0.2.0

Expand Down
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
It is designed to model and simulate systems where events occur at discrete points in time,
allowing for the analysis of complex processes and workflows.

Explore and learn more by clicking [here](https://simdart.github.io/simdart-demo/).

## Why Dart?

[Dart](https://dart.dev/) was chosen for this project due to its fast execution, single-threaded nature, and ease of use.
Expand Down Expand Up @@ -125,7 +123,7 @@ import 'package:simdart/simdart.dart';
void main() async {
final SimDart sim = SimDart();
sim.resources.limited(id: 'resource', capacity: 1);
sim.resources.limited(name: 'resource', capacity: 1);
sim.process(event: _eventResource, name: 'A');
sim.process(event: _eventResource, name: 'B');
Expand Down
3 changes: 1 addition & 2 deletions example/example.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import 'package:simdart/simdart.dart';

void main() async {
final SimDart sim = SimDart(includeTracks: true);
final SimDart sim = SimDart(observer: ConsoleEventObserver());

sim.process(event: _eventA, name: 'A');

SimResult result = await sim.run();

result.tracks?.forEach((track) => print(track));
print('startTime: ${result.startTime}');
print('duration: ${result.duration}');
}
Expand Down
3 changes: 2 additions & 1 deletion lib/simdart.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
export 'src/event.dart';
export 'src/interval.dart';
export 'src/observable.dart';
export 'src/sim_observer.dart';
export 'src/event_phase.dart';
export 'src/resources.dart' hide ResourcesFactory, ResourceStore;
export 'src/simdart.dart' hide SimDartHelper;
export 'src/simulation_track.dart';
export 'src/start_time_handling.dart';
export 'src/sim_result.dart';
export 'src/sim_num.dart';
Expand Down
21 changes: 21 additions & 0 deletions lib/src/event_phase.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/// Enum representing the possible event phases.
///
/// This enumeration is used to track and distinguish different event status
/// during the lifecycle of the simulation.
enum EventPhase {
/// The event was called for the first time.
called,

/// The event was resumed after being paused.
resumed,

yielded,

finished;

/// Returns the string representation of the status.
@override
String toString() {
return name;
}
}
3 changes: 3 additions & 0 deletions lib/src/internal/completer_action.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ class CompleterAction extends TimeAction {
void execute() {
complete.call();
}

@override
void dispose() {}
}
43 changes: 30 additions & 13 deletions lib/src/internal/event_action.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:async';

import 'package:meta/meta.dart';
import 'package:simdart/src/event.dart';
import 'package:simdart/src/event_phase.dart';
import 'package:simdart/src/internal/completer_action.dart';
import 'package:simdart/src/internal/time_action.dart';
import 'package:simdart/src/interval.dart';
Expand All @@ -10,7 +11,6 @@ import 'package:simdart/src/sim_context.dart';
import 'package:simdart/src/sim_counter.dart';
import 'package:simdart/src/sim_num.dart';
import 'package:simdart/src/simdart.dart';
import 'package:simdart/src/simulation_track.dart';

@internal
class EventAction extends TimeAction implements SimContext {
Expand Down Expand Up @@ -52,10 +52,11 @@ class EventAction extends TimeAction implements SimContext {
throw StateError('This event is yielding');
}

if (sim.includeTracks) {
SimDartHelper.addSimulationTrack(
sim: sim, eventName: eventName, status: Status.called);
}
sim.observer?.onEvent(
name: eventName,
time: sim.now,
phase: EventPhase.called,
executionHash: hashCode);

_runEvent().then((_) {
if (_eventCompleter != null) {
Expand All @@ -65,7 +66,15 @@ class EventAction extends TimeAction implements SimContext {
"Next event is being scheduled, but the current one is still paused waiting for continuation. Did you forget to use 'await'?");
return;
}
sim.observer?.onEvent(
name: eventName,
time: sim.now,
phase: EventPhase.finished,
executionHash: hashCode);

SimDartHelper.scheduleNextAction(sim: sim);
}).catchError((e) {
// Sim already marked to finish. Let the last event finalize.
});
}

Expand All @@ -82,10 +91,12 @@ class EventAction extends TimeAction implements SimContext {
return;
}

if (sim.includeTracks) {
SimDartHelper.addSimulationTrack(
sim: sim, eventName: eventName, status: Status.yielded);
}
sim.observer?.onEvent(
name: eventName,
time: sim.now,
phase: EventPhase.yielded,
executionHash: hashCode);

_eventCompleter = EventCompleter(event: this);

// Schedule a complete to resume this event in the future.
Expand Down Expand Up @@ -131,6 +142,11 @@ class EventAction extends TimeAction implements SimContext {
SimNum num(String name) {
return sim.num(name);
}

@override
void dispose() {
_eventCompleter?.complete();
}
}

class EventCompleter {
Expand All @@ -143,10 +159,11 @@ class EventCompleter {
Future<void> get future => _completer.future;

void complete() {
if (event.sim.includeTracks) {
SimDartHelper.addSimulationTrack(
sim: event.sim, eventName: event.eventName, status: Status.resumed);
}
event.sim.observer?.onEvent(
name: event.eventName,
time: event.sim.now,
phase: EventPhase.resumed,
executionHash: hashCode);
_completer.complete();
event._eventCompleter = null;
}
Expand Down
3 changes: 3 additions & 0 deletions lib/src/internal/repeat_event_action.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,7 @@ class RepeatEventAction extends TimeAction {
}
SimDartHelper.scheduleNextAction(sim: sim);
}

@override
void dispose() {}
}
2 changes: 2 additions & 0 deletions lib/src/internal/time_action.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ abstract class TimeAction {
final int order;

void execute();

void dispose();
}
11 changes: 6 additions & 5 deletions lib/src/resources.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import 'dart:collection';

import 'package:meta/meta.dart';
import 'package:simdart/src/event_phase.dart';
import 'package:simdart/src/internal/completer_action.dart';
import 'package:simdart/src/internal/event_action.dart';
import 'package:simdart/src/simdart.dart';
import 'package:simdart/src/simulation_track.dart';

@internal
class ResourceStore {
Expand Down Expand Up @@ -163,10 +163,11 @@ class ResourcesContext extends Resources {
if (resource != null) {
bool acquired = resource.acquire(_event);
if (!acquired) {
if (_sim.includeTracks) {
SimDartHelper.addSimulationTrack(
sim: _sim, eventName: _event.eventName, status: Status.yielded);
}
_sim.observer?.onEvent(
name: _event.eventName,
time: _sim.now,
phase: EventPhase.yielded,
executionHash: _event.hashCode);
_event.buildCompleter();
resource._waiting.add(_event);
SimDartHelper.scheduleNextAction(sim: _sim);
Expand Down
50 changes: 50 additions & 0 deletions lib/src/sim_observer.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import 'package:simdart/src/event_phase.dart';

/// A base class for observing simulation.
abstract class SimObserver {
/// Called when there is a change in resource usage.
///
/// [name] - The name of the resource whose usage is being reported.
/// [usage] - The percentage of resource usage, typically between 0 and 100.
void onResourceUsage({required String name, required double usage});

/// Called when an event occurs in the simulation.
///
/// [name] - The name of the event being processed. This can be null if the event does not have a name.
/// [time] - The simulation time when the event occurred, usually in ticks or arbitrary units depending on the simulation.
/// [phase] - The phase during which the event occurred. This helps categorize the event's context in the simulation lifecycle.
/// [executionHash] - The hash of the event execution.
void onEvent(
{required String name,
required int time,
required EventPhase phase,
required int executionHash});

void onStart() {}
}

mixin SimObserverMixin implements SimObserver {
@override
void onStart() {}
@override
void onResourceUsage({required String name, required double usage}) {}
@override
void onEvent(
{required String name,
required int time,
required EventPhase phase,
required int executionHash}) {}
}

class ConsoleEventObserver with SimObserverMixin {
ConsoleEventObserver();

@override
void onEvent(
{required String name,
required int time,
required EventPhase phase,
required int executionHash}) {
print('[time:$time][event:$name][phase:${phase.name}]');
}
}
7 changes: 1 addition & 6 deletions lib/src/sim_result.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,15 @@ import 'dart:collection';

import 'package:simdart/src/sim_counter.dart';
import 'package:simdart/src/sim_num.dart';
import 'package:simdart/src/simulation_track.dart';

/// Represents the simulation result.
class SimResult {
SimResult(
{required this.duration,
required this.startTime,
required List<SimulationTrack>? tracks,
required Map<String, SimNum> numProperties,
required Map<String, SimCounter> counterProperties})
: tracks = tracks != null ? UnmodifiableListView(tracks) : null,
numProperties = UnmodifiableMapView(numProperties),
: numProperties = UnmodifiableMapView(numProperties),
counterProperties = UnmodifiableMapView(counterProperties);

/// The duration, in simulated time units, that the simulation took to execute.
Expand All @@ -31,8 +28,6 @@ class SimResult {
/// the simulation officially begins its execution in terms of the simulation time.
final int startTime;

final List<SimulationTrack>? tracks;

final Map<String, SimNum> numProperties;
final Map<String, SimCounter> counterProperties;
}
Loading

0 comments on commit a7a4cdb

Please sign in to comment.