Skip to content

Commit

Permalink
give vessels their own forwarding event manager
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolaspayette committed Nov 14, 2024
1 parent aec32f0 commit bba9fb2
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,9 @@ public Action(

@Override
public final void step(final SimState simState) {
final Simulation simulation = (Simulation) simState;
final var schedule = simulation.getTemporalSchedule();
final var schedule = ((Simulation) simState).getTemporalSchedule();
final var nextAction = complete(schedule.getDateTime());
simulation.getEventManager().broadcast(this);
vessel.getEventManager().broadcast(this);
if (nextAction != null) {
schedule.scheduleOnceIn(nextAction.getDuration(), nextAction);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import sim.portrayal.Oriented2D;
import sim.util.Double2D;
import sim.util.Int2D;
import uk.ac.ox.poseidon.agents.behaviours.Behaviour;
import uk.ac.ox.poseidon.agents.fields.VesselField;
import uk.ac.ox.poseidon.core.events.EventManager;
import uk.ac.ox.poseidon.geography.ports.Port;

@Getter
Expand All @@ -36,22 +36,25 @@ public class Vessel implements Oriented2D {

private final String id;
private final VesselField vesselField;
private final EventManager eventManager;
private Behaviour initialBehaviour;
private Port homePort;
private double cruisingSpeed;
private double heading;

@SuppressFBWarnings("EI_EXPOSE_REP2")
public Vessel(
Vessel(
final String id,
final Port homePort,
final double cruisingSpeed,
final VesselField vesselField
final VesselField vesselField,
final EventManager eventManager
) {
this.id = id;
this.homePort = homePort;
this.cruisingSpeed = cruisingSpeed;
this.vesselField = vesselField;
this.eventManager = eventManager;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import uk.ac.ox.poseidon.agents.fields.VesselField;
import uk.ac.ox.poseidon.core.Factory;
import uk.ac.ox.poseidon.core.Simulation;
import uk.ac.ox.poseidon.core.events.ForwardingEventManager;
import uk.ac.ox.poseidon.core.utils.IdSupplier;
import uk.ac.ox.poseidon.geography.ports.Port;
import uk.ac.ox.poseidon.geography.ports.PortGrid;
Expand All @@ -50,7 +51,8 @@ public final Vessel get(final Simulation simulation) {
idSupplier.get(simulation).nextId(),
portFactory.get(simulation),
speed,
vesselField
vesselField,
new ForwardingEventManager(simulation.getEventManager())
);
vesselField
.setCell(
Expand Down
3 changes: 2 additions & 1 deletion core/src/main/java/uk/ac/ox/poseidon/core/Simulation.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import lombok.Getter;
import sim.engine.SimState;
import uk.ac.ox.poseidon.core.events.EventManager;
import uk.ac.ox.poseidon.core.events.SimpleEventManager;
import uk.ac.ox.poseidon.core.schedule.TemporalSchedule;

import java.io.Serial;
Expand All @@ -41,7 +42,7 @@ public class Simulation extends SimState {

@Serial private static final long serialVersionUID = -8162246985852120341L;
private static final AtomicLong idCounter = new AtomicLong();
private final EventManager eventManager = new EventManager();
private final EventManager eventManager = new SimpleEventManager();
private final TemporalSchedule temporalSchedule;
private final Scenario scenario;
private final long id = idCounter.getAndIncrement();
Expand Down
83 changes: 12 additions & 71 deletions core/src/main/java/uk/ac/ox/poseidon/core/events/EventManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,75 +19,16 @@

package uk.ac.ox.poseidon.core.events;

import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;

import java.util.HashSet;
import java.util.Set;

public class EventManager {

private final Multimap<Class<?>, Listener<?>> listeners =
MultimapBuilder.hashKeys().arrayListValues().build();

public <E> void addListener(
final Class<E> eventClass,
final Listener<E> listener
) {
listeners.put(eventClass, listener);
}

public <E> void removeListener(
final Class<E> eventClass,
final Listener<E> listener
) {
listeners.get(eventClass).removeIf(l -> l == listener);
}

public <E> void broadcast(final E event) {
if (event != null) {
Class<?> eventClass = event.getClass();
final Set<Class<?>> visitedClasses = new HashSet<>();
// Traverse class hierarchy, including interfaces and their superinterfaces
while (eventClass != null) {
if (visitedClasses.add(eventClass)) { // Only process if not visited
notifyListenersForClass(eventClass, event);
traverseInterfaces(eventClass, event, visitedClasses);
}
eventClass = eventClass.getSuperclass();
}
}
}

// Helper method to recursively traverse and notify listeners for interfaces and their
// superinterfaces
private <E> void traverseInterfaces(
final Class<?> clazz,
final E event,
final Set<Class<?>> visitedClasses
) {
for (final Class<?> interfaceClass : clazz.getInterfaces()) {
if (visitedClasses.add(interfaceClass)) { // Only process if not visited
notifyListenersForClass(interfaceClass, event);
traverseInterfaces(
interfaceClass,
event,
visitedClasses
);
}
}
}

@SuppressWarnings("unchecked")
private <E> void notifyListenersForClass(
final Class<?> eventClass,
final E event
) {
listeners
.get(eventClass)
.forEach(listener ->
((Listener<E>) listener).receive(event)
);
}

public interface EventManager {
<E> void addListener(
Class<E> eventClass,
Listener<E> listener
);

<E> void removeListener(
Class<E> eventClass,
Listener<E> listener
);

<E> void broadcast(E event);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* POSEIDON: an agent-based model of fisheries
* Copyright (c) 2024 CoHESyS Lab [email protected]
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package uk.ac.ox.poseidon.core.events;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class ForwardingEventManager implements EventManager {

private final EventManager primaryEventManager = new SimpleEventManager();
private final EventManager secondaryEventManager;

@Override
public <E> void addListener(
final Class<E> eventClass,
final Listener<E> listener
) {
primaryEventManager.addListener(eventClass, listener);
}

@Override
public <E> void removeListener(
final Class<E> eventClass,
final Listener<E> listener
) {
primaryEventManager.removeListener(eventClass, listener);
}

@Override
public <E> void broadcast(final E event) {
primaryEventManager.broadcast(event);
secondaryEventManager.broadcast(event);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* POSEIDON: an agent-based model of fisheries
* Copyright (c) 2024 CoHESyS Lab [email protected]
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/

package uk.ac.ox.poseidon.core.events;

import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;

import java.util.HashSet;
import java.util.Set;

public class SimpleEventManager implements EventManager {

private final Multimap<Class<?>, Listener<?>> listeners =
MultimapBuilder.hashKeys().arrayListValues().build();

@Override
public <E> void addListener(
final Class<E> eventClass,
final Listener<E> listener
) {
listeners.put(eventClass, listener);
}

@Override
public <E> void removeListener(
final Class<E> eventClass,
final Listener<E> listener
) {
listeners.get(eventClass).removeIf(l -> l == listener);
}

@Override
public <E> void broadcast(final E event) {
if (event != null) {
Class<?> eventClass = event.getClass();
final Set<Class<?>> visitedClasses = new HashSet<>();
// Traverse class hierarchy, including interfaces and their superinterfaces
while (eventClass != null) {
if (visitedClasses.add(eventClass)) { // Only process if not visited
notifyListenersForClass(eventClass, event);
traverseInterfaces(eventClass, event, visitedClasses);
}
eventClass = eventClass.getSuperclass();
}
}
}

// Helper method to recursively traverse and notify listeners for interfaces and their
// superinterfaces
private <E> void traverseInterfaces(
final Class<?> clazz,
final E event,
final Set<Class<?>> visitedClasses
) {
for (final Class<?> interfaceClass : clazz.getInterfaces()) {
if (visitedClasses.add(interfaceClass)) { // Only process if not visited
notifyListenersForClass(interfaceClass, event);
traverseInterfaces(
interfaceClass,
event,
visitedClasses
);
}
}
}

@SuppressWarnings("unchecked")
private <E> void notifyListenersForClass(
final Class<?> eventClass,
final E event
) {
listeners
.get(eventClass)
.forEach(listener ->
((Listener<E>) listener).receive(event)
);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class EventManagerTest {

@BeforeEach
void setUp() {
eventManager = new EventManager();
eventManager = new SimpleEventManager();
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import uk.ac.ox.poseidon.agents.behaviours.destination.FixedDestinationSupplierFactory;
import uk.ac.ox.poseidon.agents.behaviours.destination.HomePortDestinationSupplierFactory;
import uk.ac.ox.poseidon.agents.behaviours.destination.RandomDestinationSupplierFactory;
import uk.ac.ox.poseidon.agents.behaviours.fishing.DefaultFishingBehaviour;
import uk.ac.ox.poseidon.agents.behaviours.fishing.DefaultFishingBehaviourFactory;
import uk.ac.ox.poseidon.agents.behaviours.travel.TravelAlongPathBehaviourFactory;
import uk.ac.ox.poseidon.agents.fields.VesselField;
Expand Down Expand Up @@ -233,6 +234,12 @@ public static void main(final String[] args) {
Path.of("/home/nicolas/Desktop/scenario.yaml")
);
final Simulation simulation = scenario.newSimulation();
simulation
.getEventManager()
.addListener(
DefaultFishingBehaviour.Fishing.class,
System.out::println
);
simulation.start();
while (
simulation
Expand Down

0 comments on commit bba9fb2

Please sign in to comment.