Commit eff5d429 authored by Julian Zobel's avatar Julian Zobel 🦄
Browse files

Merge branch 'master' into 'cherry-pick-7698d9d7'

# Conflicts:
#   src/de/tud/kom/p2psim/impl/analyzer/metric/output/MetricOutputDAO.java
#   src/de/tud/kom/p2psim/impl/util/db/dao/DAO.java
parents 1c7f20ec 37020b44
/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM 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
* any later version.
*
* PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction;
import java.util.Random;
import de.tud.kom.p2psim.api.scenario.ConfigurationException;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
import de.tudarmstadt.maki.simonstrator.api.Monitor;
import de.tudarmstadt.maki.simonstrator.api.Monitor.Level;
import de.tudarmstadt.maki.simonstrator.api.Randoms;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/**
* Implementation of the interface {@link AttractionGenerator}.
*
* Generates a random number (out of a given interval) of attraction points. The radius is chosen randomly from a given interval.
* Generated attraction points will most likely not overlay and be completely within the world dimensions.
* With a certain lifetime chosen from a given lifetime interval, attraction points are removed after time and the number of
* attraction points that will be in the area are recalculated. If necessary, attraction points are either removed
* or added.
*
* @author Julian Zobel
* @version 1.0, March 2019
*/
public class RandomDynamicAttractionGenerator extends AbstractAttractionProvider {
private Random rand;
private int minNumberOfAttractionPoints;
private int maxNumberOfAttractionPoints;
private long minDynamicIntervall;
private long maxDynamicIntervall;
private double minimumDistance = 100;
private double maximumRadius = 100;
private double minimumRadius = 10;
@XMLConfigurableConstructor({"minNumberOfAttractionPoints", "maxNumberOfAttractionPoints", "minimumRadius",
"maximumRadius", "minDynamicIntervall", "maxDynamicIntervall", "minimumDistance" })
public RandomDynamicAttractionGenerator(int minNumberOfAttractionPoints, int maxNumberOfAttractionPoints,
double minimumRadius, double maximumRadius, long minDynamicIntervall, long maxDynamicIntervall,
double minimumDistance) {
this.rand = Randoms.getRandom(RandomDynamicAttractionGenerator.class);
if (minNumberOfAttractionPoints <= 0) {
throw new ConfigurationException(
"NumberOfAttractionPoints should be at least 1!");
}
if(minNumberOfAttractionPoints > maxNumberOfAttractionPoints) {
throw new ConfigurationException(
"Minimum and maximum numbers of attraction points not correctly set!");
}
this.minNumberOfAttractionPoints = minNumberOfAttractionPoints;
this.maxNumberOfAttractionPoints = maxNumberOfAttractionPoints;
this.minimumRadius = minimumRadius;
this.maximumRadius = maximumRadius;
this.minimumDistance = minimumDistance;
this.minDynamicIntervall = minDynamicIntervall;
this.maxDynamicIntervall = maxDynamicIntervall;
updateAttractionPoints();
}
private int randomNumberOfAttractionPoints() {
return minNumberOfAttractionPoints + rand.nextInt((maxNumberOfAttractionPoints - minNumberOfAttractionPoints) + 1);
}
private void scheduleDynamicEvent(AttractionPoint attractionPoint) {
long r = (long) (rand.nextDouble() * (maxDynamicIntervall - minDynamicIntervall)) + minDynamicIntervall;
Event.scheduleWithDelay(r, new EventHandler() {
@Override
public void eventOccurred(Object content, int type) {
// maybe was already removed...
if(hasAttractionPoint(attractionPoint)) {
removeAttractionPoint(attractionPoint);
updateAttractionPoints();
}
}
}, null, 0);
}
protected void updateAttractionPoints() {
int numberOfAttractionPoints = randomNumberOfAttractionPoints();
int currentSize = getAttractionPoints().size();
// do nothing if this is the required amount of attraction points
if(numberOfAttractionPoints == currentSize) {
return;
}
// remove until this number fits
else if(numberOfAttractionPoints < currentSize) {
int deltaAP = currentSize - numberOfAttractionPoints;
for(int i = 0; i < deltaAP; i++) {
int random = rand.nextInt(currentSize);
removeAttractionPoint(getAttractionPoints().get(random));
}
return;
}
// add more attraction points until it fits
else {
int deltaAP = numberOfAttractionPoints - currentSize;
for(int i = 0; i < deltaAP; i++) {
AttractionPoint newAP = createAttractionPoint();
scheduleDynamicEvent(newAP);
addAttractionPoint(newAP);
}
}
}
/**
* Create an attraction point that is conform to all other currently saved attraction points.
*
* @return
*/
private AttractionPoint createAttractionPoint() {
// make a break counter to prevent more than 100 iterations and an infinity loop in general.
int c = 100;
create: for(int i = 0; i < c; i++) {
PositionVector posVec = createPosVec();
// set the radius of this attraction point
// minimum radius is 10 meters
double radius = Math.max(minimumRadius, rand.nextDouble() * maximumRadius);
if(i < c) {
// check if the attraction points would be completely within world dimensions (including radius!)
if((posVec.getX() + radius) >= worldDimension.getX() || (posVec.getY() + radius) >= worldDimension.getY()
|| (posVec.getX() - radius) <= 0 || (posVec.getY() - radius) <= 0) {
continue create;
}
// if within world dimensions, continue checking against other attraction points
for (AttractionPoint ap : getAttractionPoints()) {
// if this point is closer than the given minimum distance to another point, or the radii of the points would overlap,
// or if the radius would exceed the simulation area
// then discard this attraction point and create a new one
if(posVec.distanceTo(ap) < minimumDistance || (posVec.distanceTo(ap) - radius - ap.getRadius()) < 0) {
continue create;
}
}
}
else {
radius = 0;
}
AttractionPoint aPoint = new AttractionPoint("AP-" + rand.nextInt(), posVec);
aPoint.setRadius(radius);
return aPoint;
}
AttractionPoint ap = new AttractionPoint("AP-ERROR", new PositionVector(worldDimension.getX() / 2, worldDimension.getY() / 2));
ap.setRadius(0);
ap.setWeight(0);
Monitor.log(RandomDynamicAttractionGenerator.class, Level.WARN, "RandomDynamicAttractionGenerator could not find a suitable location for a new attraction point within 100 iterations.");
return ap;
}
private PositionVector createPosVec() {
double x = rand.nextDouble() * worldDimension.getX();
double y = rand.nextDouble() * worldDimension.getY();
return new PositionVector(x, y);
}
}
/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM 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
* any later version.
*
* PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
public class TemporalAttractionPoint extends AttractionPoint {
private long placementTime;
private long removalTime;
@XMLConfigurableConstructor({"name", "x", "y", "radius", "weight", "placementTime", "removalTime" })
public TemporalAttractionPoint(String name, double x, double y, double radius, double weight, long placementTime, long removalTime) {
this(name, new PositionVector(x,y), radius, weight, placementTime, removalTime);
}
public TemporalAttractionPoint(String name, PositionVector pos, double radius, double weight, long placementTime, long removalTime) {
super(name, pos);
setWeight(weight);
setRadius(radius);
this.placementTime = placementTime;
this.removalTime = removalTime;
}
public long getPlacementTime() {
return placementTime;
}
public long getRemovalTime() {
return removalTime;
}
}
/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM 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
* any later version.
*
* PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/**
* @author Julian Zobel
* @version 1.0, Nov 2018
*/
public class TwoPointFixedAttractionGenerator extends AbstractAttractionProvider {
private double x1, x2, y1, y2;
@XMLConfigurableConstructor({ "x1", "y1", "x2", "y2"})
public TwoPointFixedAttractionGenerator(double x1, double y1, double x2, double y2) {
super();
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
createAttractionPoints();
}
private void createAttractionPoints() {
PositionVector p1 = new PositionVector(x1, y1);
AttractionPoint ap1 = new AttractionPoint("AP1", p1);
ap1.setRadius(100);
addAttractionPoint(ap1);
PositionVector p2 = new PositionVector(x2, y2);
AttractionPoint ap2 = new AttractionPoint("AP2", p2);
ap2.setRadius(100);
addAttractionPoint(ap2);
}
}
/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM 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
* any later version.
*
* PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.hostcount;
import java.util.LinkedHashSet;
import java.util.Set;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tudarmstadt.maki.simonstrator.api.Host;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.IAttractionPoint;
/**
*
*
*
* @author Julian Zobel
* @version 1.1, 27.01.2020
*
*/
public class HostAtAttractionPointCounter {
/**
* Compares the locations of a given {@link Host} and a given {@link IAttractionPoint} and checks if the host lies within the radius of the {@link IAttractionPoint}.
* Returns true, if the hosts location is within the radius of the {@link IAttractionPoint}, false otherwise.
*
* @param SimLocationActuator The host to be checked.
* @param IAttractionPoint
* @return Boolean
*/
public static boolean isHostInAttractionPointArea(SimLocationActuator host, IAttractionPoint attractionPoint) {
PositionVector hostLocation = host.getRealPosition();
if(!attractionPoint.hasRadius())
return false;
if(attractionPoint.distanceTo(hostLocation) <= attractionPoint.getRadius())
return true;
return false;
}
/**
* Returns the number of hosts within a specific {@link IAttractionPoint}
*
* @param IAttractionPoint The {@link IAttractionPoint} to be checked.
* @return Integer
*/
public static int getHostCountOfAttractionPoint(IAttractionPoint attractionPoint, LinkedHashSet<SimLocationActuator> hostsToConsider) {
return getHostsOfAttractionPoint(attractionPoint, hostsToConsider).size();
}
/**
* Returns a set of all hosts currently located within a specific {@link IAttractionPoint} area.
*
* @param IAttractionPoint The {@link IAttractionPoint} to be checked.
* @return Set<SimLocationActuator>
*/
public static LinkedHashSet<SimLocationActuator> getHostsOfAttractionPoint(IAttractionPoint attractionPoint, Set<SimLocationActuator> hostsToConsider) {
LinkedHashSet<SimLocationActuator> hosts = new LinkedHashSet<SimLocationActuator>();
for(SimLocationActuator h : hostsToConsider) {
if(isHostInAttractionPointArea(h, attractionPoint)) {
hosts.add(h);
}
}
return hosts;
}
}
/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM 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
* any later version.
*
* PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.groups;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.impl.util.Tuple;
import de.tudarmstadt.maki.simonstrator.api.Time;
/**
* This class serves as a container to store variables of groups, which are used by multiple classes and interfaces.
*
* @author Marcel Verst
* @version 1.0, 22.11.2018
*/
public class MovementGroupContainer {
private static MovementGroupContainer instance;
private LinkedHashSet<SocialMovementGroup> groups;
// Mappings
private LinkedHashMap<SimLocationActuator, Long> leftGroupAtTime;
private LinkedHashMap<SimLocationActuator, Boolean> hasMerged;
private LinkedHashMap<SimLocationActuator, Long> mergedAtTime;
// Waiting Groups, used for encounter strategy Wait. Includes remaining waiting time and last update time
public LinkedHashMap<SocialMovementGroup, Tuple<Long, Long>> waitingGroups;
private MovementGroupContainer() {
groups = new LinkedHashSet<>();
leftGroupAtTime = new LinkedHashMap<>();
hasMerged = new LinkedHashMap<>();
mergedAtTime = new LinkedHashMap<>();
waitingGroups = new LinkedHashMap<SocialMovementGroup, Tuple<Long,Long>>();
}
/**
* Returns the one and only instance of this class.
*
* @return GroupContainer
*
* @author Marcel Verst
*/
public static MovementGroupContainer getInstance() {
if(instance == null) {
synchronized (MovementGroupContainer.class) {
if (instance == null) {
instance = new MovementGroupContainer();
}
}
}
return instance;
}
/*
* =====================================================================================================
* === GROUP MODIFIER FUNCTIONS
* =====================================================================================================
*/
/**
* Adds a group to the set of groups.
*
* @param SocialMovementGroup The group to add.
*
* @author Marcel Verst
*/
public void addGroup(SocialMovementGroup group) {
groups.add(group);
}
public boolean hasGroup(SocialMovementGroup group) {
return groups.contains(group);
}
/**
* Removes a group from the set of groups and sets the leftGroupAtTime variable for all group members.
*
* @param SocialMovementGroup The group to be removed.
*
* @author Marcel Verst
*/
public void removeGroup(SocialMovementGroup group) {
for(SimLocationActuator participant : group.getMembers()) {
leftGroupAtTime.put(participant, Time.getCurrentTime());
}
waitingGroups.remove(group);
groups.remove(group);
}
/**
* If a group is changed (e.g. a host left the group) the new group is updated here.
*
* @param SocialMovementGroup The group which had an update.
*
* @author Marcel Verst
*/
public void updateGroupEntry(SocialMovementGroup group) {
if(groups.contains(group)) {
groups.remove(group);
}
groups.add(group);
}
/*
* =====================================================================================================
* === MAP MODIFIER FUNCTIONS
* =====================================================================================================
*/
public void addLeftGroupAtTimeEntry(SimLocationActuator host, Long time) {
leftGroupAtTime.put(host, time);
}
public void removeLeftGroupAtTimeEntry(SimLocationActuator host) {
if(leftGroupAtTime.containsKey(host)) {
leftGroupAtTime.remove(host);
}
}
public void addHasMergedEntry(SimLocationActuator host, Boolean value) {
hasMerged.put(host, value);
}
public void removeHasMergedEntry(SimLocationActuator host) {
if(hasMerged.containsKey(host)) {
hasMerged.remove(host);
}
}
public void addMergedAtTimeEntry(SimLocationActuator host, Long time) {
mergedAtTime.put(host, time);
}
public void removeMergedAtTimeEntry(SimLocationActuator host) {
if(mergedAtTime.containsKey(host)) {
mergedAtTime.remove(host);
}
}
/*
* =====================================================================================================
* === HELPER FUNCTIONS
* =====================================================================================================
*/
/**
* Returns true, if the host is the leader of a group, false else.
*
* @param SimLocationActuator The host to be checked.
* @return Boolean
*
* @author Marcel Verst
*/
public boolean isLeader(SimLocationActuator host) {
boolean result = false;
SocialMovementGroup group = getGroupOfHost(host);
if(group != null && group.getLeader() == host) {
result = true;
}
return result;
}
/**
* Returns true, if a host is a group member, false else.
*
* @param SimLocationActuator The host to be checked.
* @return Boolean
*
* @author Marcel Verst
*/
public boolean isGroupMember(SimLocationActuator host) {
for(SocialMovementGroup group : groups) {
if(group.hasMember(host)) {
return true;
}
}
return false;
}
/**
* Returns true, if the host is a group member, but not a leader.
*
* @param SimLocationActuator The host to be checked.
* @return Boolean
*
* @author Marcel Verst
*/
public boolean isDependent(SimLocationActuator host) {
return isGroupMember(host) && !isLeader(host);
}
/**
* Returns the GroupInstance of which a host is a member of.
*
* @param SimLocationActuator The host to be checked.
* @return GroupInstance
*
* @author Marcel Verst
*/
public SocialMovementGroup getGroupOfHost(SimLocationActuator host) {
SocialMovementGroup result = null;
for(SocialMovementGroup group : groups) {
if(group.hasMember(host)) {
result = group;
}
}
return result;
}
/**
* Updates the variables if a host has already merged before and when the last merging process happened.
* This function HAS to be called before doing any encounter process.
*
* @param SocialMovementGroup The first group.
* @param SocialMovementGroup The second group.
*
*/
public void updateMergeVars(SocialMovementGroup group1, SocialMovementGroup group2) {
updateMergeVars(group1);
updateMergeVars(group2);
}
public void updateMergeVars(SocialMovementGroup group) {
if(group == null || group.getMembers().isEmpty()) {
return;
}
for(SimLocationActuator host : group.getMembers()) {
addHasMergedEntry(host, true);
addMergedAtTimeEntry(host, Time.getCurrentTime());
}
}
/**
* Returns true, if the given group is currently waiting.
*
* @param SocialMovementGroup The group to check.
*
* @return Boolean
*
* @author Marcel Verst
*/
public boolean isWaiting(SocialMovementGroup group) {
if(waitingGroups.containsKey(group)) {
return true;
}
return false;
}
/*
* =====================================================================================================
* === GETTER AND SETTER FUNCTIONS
* =====================================================================================================
*/
/**
* Returns a set of SimLocationActuators including the host itself which belong to the group of the host.
*
* @param SimLocationActuator The host to be checked.
* @return Set<SimLocationActuator>
*
* @author Marcel Verst
*/
public LinkedHashSet<SimLocationActuator> getGroupMembers(SimLocationActuator host){
return getGroupOfHost(host).getMembers();
}
public LinkedHashSet<SocialMovementGroup> getGroups() {
return groups;
}
public void setGroups(LinkedHashSet<SocialMovementGroup> groups) {
this.groups = groups;
}
public boolean hostWasGroupMember(SimLocationActuator host) {
return this.leftGroupAtTime.containsKey(host);
}
public long getTimeSinceHostLeftLastGroup(SimLocationActuator host) {
if(hostWasGroupMember(host)) {
return Time.getCurrentTime() - this.leftGroupAtTime.get(host);
}
else {
return 0;
}
}
public void setLeftGroupAtTime(LinkedHashMap<SimLocationActuator, Long> leftGroupAtTime) {
this.leftGroupAtTime = leftGroupAtTime;
}
public LinkedHashMap<SimLocationActuator, Boolean> getHasMerged() {
return hasMerged;
}
public void setHasMerged(LinkedHashMap<SimLocationActuator, Boolean> hasMerged) {
this.hasMerged = hasMerged;
}
public LinkedHashMap<SimLocationActuator, Long> getMergedAtTime() {
return mergedAtTime;
}
public void setMergedAtTime(LinkedHashMap<SimLocationActuator, Long> mergedAtTime) {
this.mergedAtTime = mergedAtTime;
}
}
\ No newline at end of file
/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM 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
* any later version.
*
* PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.groups;
import java.util.LinkedHashSet;
import java.util.Random;
import java.util.UUID;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tudarmstadt.maki.simonstrator.api.Randoms;
/**
* This class handles the instance of a group. Each group instance has a leader which sets the behavior. All other
* participants follow the behavior of the leader in terms of speed, velocity, heading etc.
*
* @author Julian Zobel
*/
public class SocialMovementGroup {
public final UUID groupID;
private Random rand = Randoms.getRandom(SocialMovementGroup.class);
private SimLocationActuator leader;
private LinkedHashSet<SimLocationActuator> members;
private PositionVector destination;
private PositionVector meetingPoint;
/**
* Initializes the group with a given leader.
*
* @param SimLocationActuator New leader of the group
* @param int The ID of the group.
*/
public SocialMovementGroup(SimLocationActuator leader) {
this.groupID = UUID.randomUUID();
this.leader = leader;
this.members = new LinkedHashSet<SimLocationActuator>();
this.members.add(leader);
}
/**
* Initializes the group, sets the participants and groupId, finally chooses a random leader.
*
* @param Set<SimLocationActuator> All participants of the group.
* @param int The ID of the group.
*/
public SocialMovementGroup(LinkedHashSet<SimLocationActuator> participants) {
this.groupID = UUID.randomUUID();
setMembers(participants);
setRandomLeader();
}
/**
* Adds a host to the group. Manually check in {@link ModularMovementModel} class, if group size is exceeded by adding another host.
*
* @param SimLocationActuator The host to be added.
*/
public void addHostToGroup(SimLocationActuator host) {
members.add(host);
}
/**
* Removes a host from the group. If the host was the current leader, a random new leader is chosen from the remaining members.
*
* @param SimLocationActuator The host to be removed.
*/
public void removeHostFromGroup(SimLocationActuator host) {
if(members.contains(host)) {
if(host == leader) {
members.remove(host);
setRandomLeader();
}
else {
members.remove(host);
}
}
}
/**
* Sets a randomly chosen participant of the group as leader.
*/
private void setRandomLeader() {
this.leader = getRandomMember();
}
/**
* Returns a random participant of all group members.
*
* @return SimLocationActuator
*/
public SimLocationActuator getRandomMember() {
if(members.size() > 1) {
int item = rand.nextInt(members.size() - 1);
int i = 0;
for(SimLocationActuator host : members) {
if(i == item) {
return host;
}
i++;
}
}
else if(members.size() == 1) {
return members.iterator().next();
}
return null;
}
/**
* Returns true if this group has a specific host as member, false otherwise
*
* @param SimLocationActuator The host to be checked.
* @return boolean
*/
public boolean hasMember(SimLocationActuator host) {
if(members.contains(host)) {
return true;
}
return false;
}
/**
* Returns the position of the group leader.
*
* @return PositionVector The position of the group leader.
*/
public PositionVector getLeaderPosition() {
return leader.getRealPosition();
}
@Override
public String toString() {
return "Social Group of #" + leader.getHost().getId() + " ("+ members.size() +")";
}
// ===================
// Getters and Setters
// ===================
public int getGroupSize() {
return members.size();
}
public LinkedHashSet<SimLocationActuator> getMembers() {
return new LinkedHashSet<SimLocationActuator>(members);
}
public void setMembers(LinkedHashSet<SimLocationActuator> participants) {
this.members = new LinkedHashSet<>(participants);
}
public SimLocationActuator getLeader() {
return leader;
}
public PositionVector getDestination() {
return destination;
}
public void setDestination(PositionVector destination) {
this.destination = destination;
}
public PositionVector getMeetingPoint() {
return meetingPoint;
}
public void setMeetingPoint(PositionVector meetingPoint) {
this.meetingPoint = meetingPoint;
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof SocialMovementGroup)) {
return false;
}
SocialMovementGroup toCompare = (SocialMovementGroup) obj;
if(this.groupID == toCompare.groupID) {
if(this.getLeader() == toCompare.getLeader()) {
return true;
}
else {
System.out.println("Leader seems to be different, but still the same UUID");
return false;
}
}
else return false;
}
@Override
public int hashCode() {
//FIXME hope this is correct -- should we include the leader and/or other fields as well?
return groupID.hashCode();
}
}
/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM 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
* any later version.
*
* PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.groupencounter;
import java.util.LinkedHashSet;
import java.util.Random;
import de.tud.kom.p2psim.api.scenario.ConfigurationException;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.ISocialGroupMovementAnalyzer;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.SocialGroupMovementModel;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.MovementGroupContainer;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.SocialMovementGroup;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tud.kom.p2psim.impl.util.Tuple;
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
import de.tudarmstadt.maki.simonstrator.api.Monitor;
import de.tudarmstadt.maki.simonstrator.api.Randoms;
import de.tudarmstadt.maki.simonstrator.api.Time;
import de.tudarmstadt.maki.simonstrator.api.common.datastructures.Pair;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/**
* This class contains methods used by all encounter strategies.
*
* @author Marcel Verst, Julian Zobel
* @version 1.1, 30.01.2020
*/
public abstract class AbstractGroupEncounter implements IGroupEncounterBehavior {
//--- COMMON VARIABLES TO BE DECLARED IN CONFIG ---
protected boolean enableGroupEncounters;
protected double groupEncounterMeetingDistance;
protected long groupReencounterWaitTime;
//-------------------------------------------------
protected final int DECISION_NONE = 0;
protected final int DECISION_MERGE_FULL = 1;
protected final int DECISION_MERGE_PARTIAL = 2;
protected final int DECISION_DISSOLVE = 3;
protected final int DECISION_WAIT = 3;
protected SocialGroupMovementModel movementModel;
protected MovementGroupContainer groupCon;
protected Random rand = Randoms.getRandom(AbstractGroupEncounter.class);
@XMLConfigurableConstructor({"enableGroupEncounters","groupEncounterMeetingDistance","groupReencounterWaitTime"})
public AbstractGroupEncounter(boolean enableGroupEncounters, double groupEncounterMeetingDistance, long groupReencounterWaitTime) {
if(enableGroupEncounters) {
if (groupEncounterMeetingDistance < 0) {
throw new ConfigurationException(
"AbstractGroupEncounter: Variable groupEncounterMeetingDistance must be >= 0!");
}
if(groupReencounterWaitTime < 0) {
throw new ConfigurationException(
"AbstractGroupEncounter: Variable groupReencounterWaitTime must be >= 0!");
}
}
this.enableGroupEncounters = enableGroupEncounters;
this.groupEncounterMeetingDistance = groupEncounterMeetingDistance;
this.groupReencounterWaitTime = groupReencounterWaitTime;
}
@Override
public void initialize(SocialGroupMovementModel movementModel) {
this.movementModel = movementModel;
groupCon = MovementGroupContainer.getInstance();
for(SimLocationActuator host : movementModel.getAllLocationActuators()) {
groupCon.addHasMergedEntry(host, false);
}
}
/**
* Checks if any of the current groups come in a close range which is equivalent to a group meeting.
* Calls the handleGroupEncounters(Group, Group) method to define the group behavior.
*
* @return
*/
private LinkedHashSet<Pair<SocialMovementGroup>> getEncounteringGroups() {
if(!enableGroupEncounters)
return null;
LinkedHashSet<Pair<SocialMovementGroup>> encounteringGroups = new LinkedHashSet<>();
LinkedHashSet<SocialMovementGroup> alreadyProcessed = new LinkedHashSet<>();
LinkedHashSet<SocialMovementGroup> allGroups = groupCon.getGroups();
for(SocialMovementGroup group1 : allGroups) {
for(SocialMovementGroup group2 : allGroups) {
if(group1 == group2) {
continue;
}
// skip if at least one of the groups is in the area of an attraction point
if(movementModel.getAttractionAssignmentStrategy().hostInAttractionPointArea(group1.getLeader())
|| movementModel.getAttractionAssignmentStrategy().hostInAttractionPointArea(group2.getLeader())) {
continue;
}
if(!(alreadyProcessed.contains(group1) && alreadyProcessed.contains(group2))) {
if(getDistanceBetweenGroups(group1, group2) <= groupEncounterMeetingDistance) {
SimLocationActuator g1Leader = group1.getLeader();
if(!groupCon.getHasMerged().get(g1Leader) || waitedLongEnoughAfterMerging(g1Leader) ) {
Pair<SocialMovementGroup> encounter = new Pair<SocialMovementGroup>(group1, group2);
encounteringGroups.add(encounter);
alreadyProcessed.add(group1);
alreadyProcessed.add(group2);
}
}
}
}
}
return encounteringGroups;
}
public void handleEncounters() {
if(!enableGroupEncounters)
return;
LinkedHashSet<Pair<SocialMovementGroup>> encounteringGroups = getEncounteringGroups();
if(encounteringGroups == null || encounteringGroups.isEmpty()) {
return;
}
for(Pair<SocialMovementGroup> encounter : encounteringGroups) {
if(groupCon.hasGroup(encounter.getFirst()) && groupCon.hasGroup(encounter.getSecond())) {
// Inform analyzer of resolved movement
if(Monitor.hasAnalyzer(ISocialGroupMovementAnalyzer.class)) {
Monitor.getOrNull(ISocialGroupMovementAnalyzer.class).onGroupEncounter(encounter.getFirst(), encounter.getSecond());
}
handleGroupEncounter(encounter.getFirst(), encounter.getSecond());
}
}
}
/**
* Defines what to do with both groups in case they encountered.
*
* @param SocialMovementGroup The first group.
* @param SocialMovementGroup The second group.
*
*/
protected abstract void handleGroupEncounter(SocialMovementGroup group1, SocialMovementGroup group2);
/**
* Returns the distance between two groups based on their leaders position.
*
* @param SocialMovementGroup The first group.
* @param SocialMovementGroup The second group.
*
* @return Double Distance between group leaders of both groups.
*
*/
public double getDistanceBetweenGroups(SocialMovementGroup group1, SocialMovementGroup group2) {
PositionVector l1Pos = group1.getLeaderPosition();
PositionVector l2Pos = group2.getLeaderPosition();
return l1Pos.distanceTo(l2Pos);
}
/**
* Compares the size of two groups and returns the larger group.
*
* @param SocialMovementGroup The first group.
* @param SocialMovementGroup The second group.
* @return GroupInstance The larger group, or the first group if both are the same size.
*
*/
protected SocialMovementGroup getLargerGroup(SocialMovementGroup group1, SocialMovementGroup group2) {
if(group1.getGroupSize() >= group2.getGroupSize()) {
return group1;
}
else {
return group2;
}
}
/**
* Compares the size of two groups and returns the smaller group.
*
* @param SocialMovementGroup The first group.
* @param SocialMovementGroup The second group.
* @return GroupInstance The smaller group, or the second group if both are the same size.
*
*/
protected SocialMovementGroup getSmallerGroup(SocialMovementGroup group1, SocialMovementGroup group2) {
if(group1.getGroupSize() < group2.getGroupSize()) {
return group1;
}
else {
return group2;
}
}
/**
* Returns true, if a host has waited long enough after its last merging process.
*
* @param SimLocationActuator The host to be checked.
* @param int The time a host has to wait until it can merge again.
* @return boolean
*
*/
protected boolean waitedLongEnoughAfterMerging(SimLocationActuator host) {
return ((Time.getCurrentTime() - groupCon.getMergedAtTime().get(host)) >= groupReencounterWaitTime);
}
/**
* Removes both groups on encounter.
*
*/
protected void dissolveGroups(SocialMovementGroup group1, SocialMovementGroup group2) {
groupCon.updateMergeVars(group1, group2);
if(groupCon.getGroups().contains(group1)) {
movementModel.getGroupFormingBehavior().removeGroup(group1);
}
if(groupCon.getGroups().contains(group2)) {
movementModel.getGroupFormingBehavior().removeGroup(group2);
}
}
/**
* Merges two groups together into one single group. The smaller group is merged into the larger group.
*
* @param SocialMovementGroup The first group.
* @param SocialMovementGroup The second group.
*
* @return Returns the merged group
*/
protected SocialMovementGroup mergeGroupsFully(SocialMovementGroup group1, SocialMovementGroup group2) {
groupCon.updateMergeVars(group1, group2);
SocialMovementGroup large = getLargerGroup(group1, group2);
SocialMovementGroup small = getSmallerGroup(group1, group2);
LinkedHashSet<SimLocationActuator> toRemove = new LinkedHashSet<>(small.getMembers());
for(SimLocationActuator participant : toRemove) {
small.removeHostFromGroup(participant);
groupCon.addLeftGroupAtTimeEntry(participant, Time.getCurrentTime());
large.addHostToGroup(participant);
}
// Inform analyzer of resolved movement
if(Monitor.hasAnalyzer(ISocialGroupMovementAnalyzer.class)) {
Monitor.getOrNull(ISocialGroupMovementAnalyzer.class).onGroupMerge(group1, group2);
}
movementModel.getGroupFormingBehavior().removeGroup(small);
return large;
}
/**
* Merges two groups partially. A specified number of hosts leaves the larger group and joins the smaller group.
*
* @param SocialMovementGroup The first group.
* @param SocialMovementGroup The second group.
*
*/
protected void mergeGroupsPartially(SocialMovementGroup group1, SocialMovementGroup group2, int numberOfHostsToLeave) {
if(numberOfHostsToLeave <= 0)
return;
SocialMovementGroup large = getLargerGroup(group1, group2);
SocialMovementGroup small = getSmallerGroup(group1, group2);
if((large.getGroupSize() - numberOfHostsToLeave) >= movementModel.getGroupFormingBehavior().getMinGroupSize()) {
groupCon.updateMergeVars(group1, group2);
for(int i = 0; i < numberOfHostsToLeave; i++) {
SimLocationActuator randomParticipant = large.getRandomMember();
groupCon.addLeftGroupAtTimeEntry(randomParticipant, Time.getCurrentTime());
large.removeHostFromGroup(randomParticipant);
small.addHostToGroup(randomParticipant);
groupCon.updateGroupEntry(small);
groupCon.updateGroupEntry(large);
}
// Inform analyzer of resolved movement
if(Monitor.hasAnalyzer(ISocialGroupMovementAnalyzer.class)) {
Monitor.getOrNull(ISocialGroupMovementAnalyzer.class).onGroupMerge(group1, group2);
}
}
}
protected void wait(SocialMovementGroup group, long timeToWait) {
// only start the waiting process if not already waiting (duh!)
if(!groupCon.waitingGroups.containsKey(group)) {
// Inform analyzer of resolved movement
if(Monitor.hasAnalyzer(ISocialGroupMovementAnalyzer.class)) {
Monitor.getOrNull(ISocialGroupMovementAnalyzer.class).onGroupWait(group);
}
groupCon.waitingGroups.put(group, new Tuple<Long, Long>(timeToWait, Time.getCurrentTime()));
Event.scheduleWithDelay(timeToWait, new EventHandler() {
@Override
public void eventOccurred(Object content, int type) {
SocialMovementGroup g = (SocialMovementGroup) content;
// if group was deleted in the meantime (e.g., by dissolving/merging), just do nothing
if(!groupCon.hasGroup(g)) {
return;
}
groupCon.updateMergeVars(g);
groupCon.waitingGroups.remove(g);
}
}, group, 0);
}
}
protected abstract int decide();
}
/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM 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
* any later version.
*
* PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.groupencounter;
import de.tud.kom.p2psim.api.scenario.ConfigurationException;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.SocialMovementGroup;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/**
* This class checks if there is any pair of groups which encounter each other. Randomly decides
* if the groups dissolve or not after the encounter.
*
* @author Marcel Verst
* @version 1.0, 22.11.2018
*/
public class Dissolve extends AbstractGroupEncounter {
@XMLConfigurableConstructor({"enableGroupEncounters","groupEncounterMeetingDistance","groupReencounterWaitTime"})
public Dissolve(boolean enableGroupEncounters,
double groupEncounterMeetingDistance,
long groupReencounterWaitTime) {
super(enableGroupEncounters, groupEncounterMeetingDistance,
groupReencounterWaitTime);
}
//--- VARIABLES TO BE DECLARED IN CONFIG ---
private double probabilityToDissolve;
//------------------------------------------
/**
*
* Lets both groups dissolve with a certain probability in case they encounter.
*/
@Override
public void handleGroupEncounter(SocialMovementGroup group1, SocialMovementGroup group2) {
if(decide() == DECISION_DISSOLVE) {
System.out.println("Dissolve groups");
dissolveGroups(group1, group2);
}
}
public void setProbabilityToDissolve(double probabilityToDissolve) {
if (probabilityToDissolve < 0 || probabilityToDissolve > 1.0) {
throw new ConfigurationException(
"probabilityToDissolve should be between 0.0 and 1.0!");
}
this.probabilityToDissolve = probabilityToDissolve;
}
protected int decide() {
// mergePartially
if(rand.nextDouble() < probabilityToDissolve) {
return DECISION_DISSOLVE;
}
else
return DECISION_NONE;
}
}
/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM 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
* any later version.
*
* PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.groupencounter;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.SocialGroupMovementModel;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.SocialMovementGroup;
/**
* Handles group encounters. Check group encounters based on the leaders location of {@link SocialMovementGroup}s.
* Encounter takes part if the position between two leaders falls below a specified threshold.
*
* @author Marcel Verst
* @version 1.0, 22.11.2018
*/
public interface IGroupEncounterBehavior {
/**
* Initializes class variables.
*
*/
void initialize(SocialGroupMovementModel movementModel);
/**
* Applies an encountering action to each detected encountering groups.
*
*/
public void handleEncounters();
}
\ No newline at end of file
/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM 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
* any later version.
*
* PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.groupencounter;
import de.tud.kom.p2psim.api.scenario.ConfigurationException;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.SocialMovementGroup;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/**
* This class checks if there is any pair of groups which encounter each other. Randomly decides
* if the groups merge fully or not after the encounter.
*
* @author Marcel Verst
* @version 1.0, 22.11.2018
*/
public class Merge extends AbstractGroupEncounter {
//--- VARIABLES TO BE DECLARED IN CONFIG ---
private double probabilityToMergeFully;
//------------------------------------------
@XMLConfigurableConstructor({"enableGroupEncounters","groupEncounterMeetingDistance","groupReencounterWaitTime"})
public Merge(boolean enableGroupEncounters,
double groupEncounterMeetingDistance,
long groupReencounterWaitTime) {
super(enableGroupEncounters, groupEncounterMeetingDistance,
groupReencounterWaitTime);
}
/**
*
* Lets both groups merge fully with a certain probability in case they encounter.
*/
@Override
public void handleGroupEncounter(SocialMovementGroup group1, SocialMovementGroup group2) {
if(decide() == DECISION_MERGE_FULL) {
mergeGroupsFully(group1, group2);
}
}
public void setProbabilityToMergeFully(double probabilityToMergeFully) {
if (probabilityToMergeFully < 0 || probabilityToMergeFully > 1.0) {
throw new ConfigurationException(
"probabilityToMergeFully should be between 0.0 and 1.0!");
}
this.probabilityToMergeFully = probabilityToMergeFully;
}
protected int decide() {
// mergePartially
if(rand.nextDouble() < probabilityToMergeFully) {
return DECISION_MERGE_FULL;
}
else
return DECISION_NONE;
}
}
/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM 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
* any later version.
*
* PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.groupencounter;
import de.tud.kom.p2psim.api.scenario.ConfigurationException;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.ModularMovementModel;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.SocialGroupMovementModel;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.SocialMovementGroup;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/**
* This class checks if there is any pair of groups which encounter each other. Randomly decides
* if the groups merge fully, partially or do nothing after the encounter.
*
* @author Marcel Verst
* @version 1.0, 22.11.2018
*/
public class MergeFullOrMergePartial extends AbstractGroupEncounter {
//--- VARIABLES TO BE DECLARED IN CONFIG ---
private double probabilityToMergeFully;
private double probabilityToMergePartially;
private int numberOfHostsToLeave;
//------------------------------------------
@XMLConfigurableConstructor({"enableGroupEncounters","groupEncounterMeetingDistance","groupReencounterWaitTime"})
public MergeFullOrMergePartial(boolean enableGroupEncounters,
double groupEncounterMeetingDistance,
long groupReencounterWaitTime) {
super(enableGroupEncounters, groupEncounterMeetingDistance,
groupReencounterWaitTime);
}
@Override
public void initialize(SocialGroupMovementModel movementModel) {
super.initialize(movementModel);
checkConfiguration();
}
/**
* {@inheritDoc}
* Applies dependent on probability values if the groups merge fully, merge partially or do nothing.
*/
@Override
public void handleGroupEncounter(SocialMovementGroup group1, SocialMovementGroup group2) {
int decision = decide();
if(decision == DECISION_MERGE_FULL) {
mergeGroupsFully(group1, group2);
}
else if(decision == DECISION_MERGE_PARTIAL) {
mergeGroupsPartially(group1, group2, numberOfHostsToLeave);
}
}
/**
* Decider to set the function which a leader should call if its group is in meeting range with another group.
*
* @return int
*/
protected int decide() {
double decider = rand.nextDouble();
double maxMergeFully = probabilityToMergeFully;
double maxMergePartially = probabilityToMergeFully + probabilityToMergePartially;
// mergeFully
if(decider >= 0.0 && decider < maxMergeFully) {
return DECISION_MERGE_FULL;
}
// mergePartially
else if(decider >= maxMergeFully && decider < maxMergePartially) {
return DECISION_MERGE_PARTIAL;
}
else
return DECISION_NONE;
}
public void setProbabilityToMergeFully(double probabilityToMergeFully) {
if (probabilityToMergeFully < 0 || probabilityToMergeFully > 1.0) {
throw new ConfigurationException(
"probabilityToMergeFully should be between 0.0 and 1.0!");
}
this.probabilityToMergeFully = probabilityToMergeFully;
}
public void setProbabilityToMergePartially(double probabilityToMergePartially) {
if (probabilityToMergePartially < 0 || probabilityToMergePartially > 1.0) {
throw new ConfigurationException(
"probabilityToMergePartially should be between 0.0 and 1.0!");
}
this.probabilityToMergePartially = probabilityToMergePartially;
}
public void setNumberOfHostsToLeave(int numberOfHostsToLeave) {
if (numberOfHostsToLeave < 0) {
throw new ConfigurationException(
"numberOfHostsToLeave should be greater or equal than 0!");
}
this.numberOfHostsToLeave = numberOfHostsToLeave;
}
/**
* Checks for the correct configuration.
*
* @author Marcel Verst
*/
private void checkConfiguration() {
if(probabilityToMergeFully + probabilityToMergePartially > 1.0) {
throw new ConfigurationException(
"probabilityToMergeFully + probabilityToMergePartially should not exceed 1.0!");
}
}
}
/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM 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
* any later version.
*
* PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.groupencounter;
import de.tud.kom.p2psim.api.scenario.ConfigurationException;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.ModularMovementModel;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.SocialGroupMovementModel;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.SocialMovementGroup;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/**
* This class checks if there is any pair of groups which encounter each other. Randomly decides
* if the groups merge fully, merge partially, dissolve or do nothing after the encounter.
*
* @author Marcel Verst
* @version 1.0, 22.11.2018
*/
public class MergeFullOrMergePartialOrDissolve extends AbstractGroupEncounter {
//--- VARIABLES TO BE DECLARED IN CONFIG ---
private double probabilityToMergeFully;
private double probabilityToMergePartially;
private double probabilityToDissolve;
private int numberOfHostsToLeave;
//------------------------------------------
@XMLConfigurableConstructor({"enableGroupEncounters","groupEncounterMeetingDistance","groupReencounterWaitTime"})
public MergeFullOrMergePartialOrDissolve(boolean enableGroupEncounters,
double groupEncounterMeetingDistance,
long groupReencounterWaitTime) {
super(enableGroupEncounters, groupEncounterMeetingDistance,
groupReencounterWaitTime);
}
@Override
public void initialize(SocialGroupMovementModel movementModel) {
super.initialize(movementModel);
checkConfiguration();
}
/**
* {@inheritDoc}
* Applies dependent on probability values if the groups merge fully, merge partially, dissolve or do nothing.
*/
@Override
public void handleGroupEncounter(SocialMovementGroup group1, SocialMovementGroup group2) {
int decision = decide();
if(decision == DECISION_MERGE_FULL) {
mergeGroupsFully(group1, group2);
}
else if(decision == DECISION_MERGE_PARTIAL) {
mergeGroupsPartially(group1, group2, numberOfHostsToLeave);
}
else if(decision == DECISION_DISSOLVE) {
dissolveGroups(group1, group2);
}
}
/**
* Decider to set the function which a leader should call if its group is in meeting range with another group.
*
* @return int
*/
protected int decide() {
double decider = rand.nextDouble();
double maxMergeFully = probabilityToMergeFully;
double maxMergePartially = probabilityToMergeFully + probabilityToMergePartially;
double maxDissolve = probabilityToMergeFully + probabilityToMergePartially + probabilityToDissolve;
// mergeFully
if(decider >= 0.0 && decider < maxMergeFully) {
return DECISION_MERGE_FULL;
}
// mergePartially
else if(decider >= maxMergeFully && decider < maxMergePartially) {
return DECISION_MERGE_PARTIAL;
}
// dissolve
else if(decider >= maxMergePartially && decider <= maxDissolve) {
return DECISION_DISSOLVE;
}
else
return DECISION_NONE;
}
public void setProbabilityToMergeFully(double probabilityToMergeFully) {
if (probabilityToMergeFully < 0 || probabilityToMergeFully > 1.0) {
throw new ConfigurationException(
"probabilityToMergeFully should be between 0.0 and 1.0!");
}
this.probabilityToMergeFully = probabilityToMergeFully;
}
public void setProbabilityToMergePartially(double probabilityToMergePartially) {
if (probabilityToMergePartially < 0 || probabilityToMergePartially > 1.0) {
throw new ConfigurationException(
"probabilityToMergePartially should be between 0.0 and 1.0!");
}
this.probabilityToMergePartially = probabilityToMergePartially;
}
public void setProbabilityToDissolve(double probabilityToDissolve) {
if (probabilityToDissolve < 0 || probabilityToDissolve > 1.0) {
throw new ConfigurationException(
"probabilityToDissolve should be between 0.0 and 1.0!");
}
this.probabilityToDissolve = probabilityToDissolve;
}
public void setNumberOfHostsToLeave(int numberOfHostsToLeave) {
if (numberOfHostsToLeave < 0) {
throw new ConfigurationException(
"numberOfHostsToLeave should be greater or equal than 0!");
}
this.numberOfHostsToLeave = numberOfHostsToLeave;
}
/**
* Checks for the correct configuration.
*
* @author Marcel Verst
*/
private void checkConfiguration() {
if(probabilityToMergeFully + probabilityToMergePartially + probabilityToDissolve > 1.0) {
throw new ConfigurationException(
"probabilityToMergeFully + probabilityToMergePartially + probabilityToDissolve should not exceed 1.0!");
}
}
}
/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM 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
* any later version.
*
* PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.groupencounter;
import de.tud.kom.p2psim.api.scenario.ConfigurationException;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.SocialGroupMovementModel;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.SocialMovementGroup;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/**
* This class checks if there is any pair of groups which encounter each other. Randomly decides
* if the groups merge fully, dissolve or do nothing after the encounter.
*
* @author Marcel Verst
* @version 1.0, 22.11.2018
*/
public class MergeOrDissolve extends AbstractGroupEncounter {
//--- VARIABLES TO BE DECLARED IN CONFIG ---
private double probabilityToMergeFully;
private double probabilityToDissolve;
//------------------------------------------
@XMLConfigurableConstructor({"enableGroupEncounters","groupEncounterMeetingDistance","groupReencounterWaitTime"})
public MergeOrDissolve(boolean enableGroupEncounters,
double groupEncounterMeetingDistance,
long groupReencounterWaitTime) {
super(enableGroupEncounters, groupEncounterMeetingDistance,
groupReencounterWaitTime);
}
@Override
public void initialize(SocialGroupMovementModel movementModel) {
super.initialize(movementModel);
checkConfiguration();
}
/**
* {@inheritDoc}
* Applies dependent on probability values if the groups merge fully, dissolve or do nothing.
*/
@Override
public void handleGroupEncounter(SocialMovementGroup group1, SocialMovementGroup group2) {
int decision = decide();
if(decision == DECISION_MERGE_FULL) {
mergeGroupsFully(group1, group2);
}
else if(decision == DECISION_DISSOLVE) {
dissolveGroups(group1, group2);
}
}
/**
* Decider to set the function which a leader should call if its group is in meeting range with another group.
*
* @return int
*/
protected int decide() {
double decider = rand.nextDouble();
double maxMergeFully = probabilityToMergeFully;
double maxDissolve = probabilityToMergeFully + probabilityToDissolve;
// mergeFully
if(decider >= 0.0 && decider < maxMergeFully) {
return DECISION_MERGE_FULL;
}
// dissolve
else if(decider >= maxMergeFully && decider < maxDissolve) {
return DECISION_DISSOLVE;
}
else {
return DECISION_NONE;
}
}
public void setProbabilityToMergeFully(double probabilityToMergeFully) {
if (probabilityToMergeFully < 0 || probabilityToMergeFully > 1.0) {
throw new ConfigurationException(
"probabilityToMergeFully should be between 0.0 and 1.0!");
}
this.probabilityToMergeFully = probabilityToMergeFully;
}
public void setProbabilityToDissolve(double probabilityToDissolve) {
if (probabilityToDissolve < 0 || probabilityToDissolve > 1.0) {
throw new ConfigurationException(
"probabilityToDissolve should be between 0.0 and 1.0!");
}
this.probabilityToDissolve = probabilityToDissolve;
}
/**
* Checks for the correct configuration.
*
* @author Marcel Verst
*/
private void checkConfiguration() {
if(probabilityToMergeFully + probabilityToDissolve > 1.0) {
throw new ConfigurationException(
"FullyDissolve Group Encounter: probabilityToMergeFully + probabilityToDissolve must not exceed 1.0!");
}
}
}
/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM 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
* any later version.
*
* PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.groupencounter;
import de.tud.kom.p2psim.api.scenario.ConfigurationException;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.SocialMovementGroup;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/**
* This class checks if there is any pair of groups which encounter each other. Randomly decides
* if the groups merge partially or not after the encounter.
*
* @author Marcel Verst
* @version 1.0, 22.11.2018
*/
public class MergePartial extends AbstractGroupEncounter {
//--- VARIABLES TO BE DECLARED IN CONFIG ---
private double probabilityToMergePartially;
private int numberOfHostsToLeave;
//------------------------------------------
@XMLConfigurableConstructor({"enableGroupEncounters","groupEncounterMeetingDistance","groupReencounterWaitTime"})
public MergePartial(boolean enableGroupEncounters,
double groupEncounterMeetingDistance,
long groupReencounterWaitTime) {
super(enableGroupEncounters, groupEncounterMeetingDistance,
groupReencounterWaitTime);
}
/**
*
* Lets both groups merge partially with a certain probability in case they encounter.
*/
@Override
public void handleGroupEncounter(SocialMovementGroup group1, SocialMovementGroup group2) {
if(decide() == DECISION_MERGE_PARTIAL) {
mergeGroupsPartially(group1, group2, numberOfHostsToLeave);
}
}
public void setProbabilityToMergePartially(double probabilityToMergePartially) {
if (probabilityToMergePartially < 0 || probabilityToMergePartially > 1.0) {
throw new ConfigurationException(
"probabilityToMergePartially should be between 0.0 and 1.0!");
}
this.probabilityToMergePartially = probabilityToMergePartially;
}
public void setNumberOfHostsToLeave(int numberOfHostsToLeave) {
if (numberOfHostsToLeave < 0) {
throw new ConfigurationException(
"numberOfHostsToLeave should be greater or equal than 0!");
}
this.numberOfHostsToLeave = numberOfHostsToLeave;
}
protected int decide() {
// mergePartially
if(rand.nextDouble() < probabilityToMergePartially) {
return DECISION_MERGE_PARTIAL;
}
else
return DECISION_NONE;
}
}
/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM 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
* any later version.
*
* PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.groupencounter;
import de.tud.kom.p2psim.api.scenario.ConfigurationException;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.ModularMovementModel;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.SocialGroupMovementModel;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.SocialMovementGroup;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/**
* This class checks if there is any pair of groups which encounter each other. Randomly decides
* if the groups merge partially, dissolve or do nothing after the encounter.
*
* @author Marcel Verst
* @version 1.0, 22.11.2018
*/
public class MergePartialOrDissolve extends AbstractGroupEncounter {
//--- VARIABLES TO BE DECLARED IN CONFIG ---
private double probabilityToMergePartially;
private double probabilityToDissolve;
private int numberOfHostsToLeave;
//------------------------------------------
@XMLConfigurableConstructor({"enableGroupEncounters","groupEncounterMeetingDistance","groupReencounterWaitTime"})
public MergePartialOrDissolve(boolean enableGroupEncounters,
double groupEncounterMeetingDistance,
long groupReencounterWaitTime) {
super(enableGroupEncounters, groupEncounterMeetingDistance,
groupReencounterWaitTime);
}
@Override
public void initialize(SocialGroupMovementModel movementModel) {
super.initialize(movementModel);
checkConfiguration();
}
/**
* {@inheritDoc}
* Applies dependent on probability values if the groups merge partially, dissolve or do nothing.
*/
@Override
public void handleGroupEncounter(SocialMovementGroup group1, SocialMovementGroup group2) {
int decision = decide();
if(decision == DECISION_MERGE_PARTIAL) {
mergeGroupsPartially(group1, group2, numberOfHostsToLeave);
}
else if(decision == DECISION_DISSOLVE) {
dissolveGroups(group1, group2);
}
}
/**
* Decider to set the function which a leader should call if its group is in meeting range with another group.
*
* @return int
*/
protected int decide() {
double decider = rand.nextDouble();
double maxMergePartially = probabilityToMergePartially;
double maxDissolve = probabilityToMergePartially + probabilityToDissolve;
// mergePartially
if(decider >= 0.0 && decider < maxMergePartially) {
return DECISION_MERGE_PARTIAL;
}
// dissolve
else if(decider >= maxMergePartially && decider < maxDissolve) {
return DECISION_DISSOLVE;
}
else
return DECISION_NONE;
}
public void setProbabilityToMergePartially(double probabilityToMergePartially) {
if (probabilityToMergePartially < 0 || probabilityToMergePartially > 1.0) {
throw new ConfigurationException(
"probabilityToMergePartially should be between 0.0 and 1.0!");
}
this.probabilityToMergePartially = probabilityToMergePartially;
}
public void setProbabilityToDissolve(double probabilityToDissolve) {
if (probabilityToDissolve < 0 || probabilityToDissolve > 1.0) {
throw new ConfigurationException(
"probabilityToDissolve should be between 0.0 and 1.0!");
}
this.probabilityToDissolve = probabilityToDissolve;
}
public void setNumberOfHostsToLeave(int numberOfHostsToLeave) {
if (numberOfHostsToLeave < 0) {
throw new ConfigurationException(
"numberOfHostsToLeave should be greater or equal than 0!");
}
this.numberOfHostsToLeave = numberOfHostsToLeave;
}
/**
* Checks for the correct configuration.
*
* @author Marcel Verst
*/
private void checkConfiguration() {
if(probabilityToMergePartially + probabilityToDissolve > 1.0) {
throw new ConfigurationException(
"probabilityToMergePartially + probabilityToDissolve should not exceed 1.0!");
}
}
}
/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM 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
* any later version.
*
* PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.groupencounter;
import de.tud.kom.p2psim.api.scenario.ConfigurationException;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.SocialMovementGroup;
import de.tudarmstadt.maki.simonstrator.api.Time;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/**
* This class checks if there is any pair of groups which encounter each other. Randomly decides
* if the groups wait for a specified of time during encounter or not.
*
* @author Marcel Verst
* @version 1.0, 22.11.2018
*/
public class Wait extends AbstractGroupEncounter{
//--- VARIABLES TO BE DECLARED IN CONFIG ---
protected double probabilityToWait;
protected long timeToWait;
//------------------------------------------
@XMLConfigurableConstructor({"enableGroupEncounters","groupEncounterMeetingDistance","groupReencounterWaitTime"})
public Wait(boolean enableGroupEncounters,
double groupEncounterMeetingDistance,
long groupReencounterWaitTime) {
super(enableGroupEncounters, groupEncounterMeetingDistance,
groupReencounterWaitTime);
}
@Override
public void handleGroupEncounter(SocialMovementGroup group1, SocialMovementGroup group2) {
if(decide() == DECISION_WAIT) {
long waittime = getRandomWaitTime();
wait(group1, waittime);
wait(group2, waittime);
}
}
protected long getRandomWaitTime() {
return (long) (rand.nextDouble() * (timeToWait + 1 - Time.MINUTE ) + Time.MINUTE);
}
/*
* GETTERS AND SETTERS
*/
public void setProbabilityToWait(double probabilityToWait) {
if (probabilityToWait < 0 || probabilityToWait > 1.0) {
throw new ConfigurationException(
"probabilityToWait should be between 0.0 and 1.0!");
}
this.probabilityToWait = probabilityToWait;
}
public void setTimeToWait(long timeToWait) {
if(timeToWait < 0) {
throw new ConfigurationException(
"timeToWait should be greater or equal than 0!");
}
this.timeToWait = timeToWait;
}
@Override
protected int decide() {
// mergePartially
if(rand.nextDouble() < probabilityToWait) {
return DECISION_WAIT;
}
else
return DECISION_NONE;
}
}
/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM 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
* any later version.
*
* PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.groupencounter;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.SocialMovementGroup;
import de.tudarmstadt.maki.simonstrator.api.Time;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/**
*
* @author Julian Zobel
* @version 1.0, 04.02.2020
*/
public class WaitSmartMerge extends Wait{
@XMLConfigurableConstructor({"enableGroupEncounters","groupEncounterMeetingDistance","groupReencounterWaitTime"})
public WaitSmartMerge(boolean enableGroupEncounters,
double groupEncounterMeetingDistance,
long groupReencounterWaitTime) {
super(enableGroupEncounters, groupEncounterMeetingDistance,
groupReencounterWaitTime);
}
@Override
public void handleGroupEncounter(SocialMovementGroup group1, SocialMovementGroup group2) {
if(group1.getLeader().getCurrentTargetAttractionPoint() == group2.getLeader().getCurrentTargetAttractionPoint()) {
SocialMovementGroup merged = mergeGroupsFully(group1, group2);
wait(merged, Time.MINUTE);
}
else if(decide() == DECISION_WAIT) {
long pauseTime = 0;
if(groupCon.waitingGroups.containsKey(group1) && !groupCon.waitingGroups.containsKey(group2)) {
pauseTime = getRemainingGroupWaitTime(group1);
}
else if(!groupCon.waitingGroups.containsKey(group1) && groupCon.waitingGroups.containsKey(group2)) {
pauseTime = getRemainingGroupWaitTime(group2);
}
else {
pauseTime = getRandomWaitTime();
}
wait(group1, pauseTime);
wait(group2, pauseTime);
}
}
private long getRemainingGroupWaitTime(SocialMovementGroup group) {
long pausetime = groupCon.waitingGroups.get(group).getA();
long issueTime = groupCon.waitingGroups.get(group).getB();
return issueTime + pausetime - Time.getCurrentTime();
}
}
/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM 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
* any later version.
*
* PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.groupforming;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import de.tud.kom.p2psim.api.scenario.ConfigurationException;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.ISocialGroupMovementAnalyzer;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.SocialGroupMovementModel;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.BasicAttractionPoint;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.IAttractionProvider;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.hostcount.HostAtAttractionPointCounter;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.SocialMovementGroup;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.MovementGroupContainer;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tud.kom.p2psim.impl.util.Tuple;
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
import de.tudarmstadt.maki.simonstrator.api.Monitor;
import de.tudarmstadt.maki.simonstrator.api.Randoms;
import de.tudarmstadt.maki.simonstrator.api.Time;
import de.tudarmstadt.maki.simonstrator.api.common.graph.INodeID;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.IAttractionPoint;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/**
* Abstract class implementation for implementing GroupFormingStrategies.
*
* @author Marcel Verst, Julian Zobel
* @version 1.1, 30.01.2020
*/
public abstract class AbstractGroupForming implements IGroupFormingBehavior {
protected Random rand = Randoms.getRandom(AbstractGroupForming.class);
protected SocialGroupMovementModel movementModel;
protected MovementGroupContainer groupCon;
// Holds time information of nodes for the groups (stay duration, last update)
protected Map<INodeID, Tuple<Long,Long>> stayDuration;
// GROUP VARIABLES FROM CONFIGS
protected boolean enableGroups;
protected int minGroupSize;
protected int maxGroupSize;
protected double probabilityToJoin;
protected int maxNumberOfGroups;
protected long groupRejoinWaitTime;
protected long groupFormationSetupDelay;
private long groupFormationDelay;
public static AbstractGroupForming instance;
@XMLConfigurableConstructor({"enableGroups", "maxNumberOfGroups", "minGroupSize", "maxGroupSize", "probabilityToJoin", "groupFormationSetupDelay", "groupFormationDelay","groupRejoinWaitTime"})
public AbstractGroupForming(boolean enableGroups, int maxNumberOfGroups, int minGroupSize, int maxGroupSize, double probabilityToJoin, long groupFormationSetupDelay, long groupFormationDelay, long groupRejoinWaitTime) {
instance = this;
this.enableGroups = enableGroups;
if(enableGroups) {
if (probabilityToJoin < 0 || probabilityToJoin > 1.0) {
throw new ConfigurationException(
"GroupForming: probabilityToJoin must be between 0.0 and 1.0!");
}
if (maxNumberOfGroups <= 0) {
throw new ConfigurationException(
"GroupForming: maxNumberOfGroups must be >= 1!");
}
if (minGroupSize < 0) {
throw new ConfigurationException(
"GroupForming: minGroupSize must be >= 1!");
}
if (maxGroupSize < minGroupSize) {
throw new ConfigurationException(
"GroupForming: maxGroupSize cannot be bigger than minGroupSize!");
}
if (groupFormationSetupDelay <= 0) {
throw new ConfigurationException(
"GroupForming: groupFormationSetupDelay must be > 0!");
}
if (groupFormationDelay < -1) {
throw new ConfigurationException(
"GroupForming: groupFormationDelay must be >= -1!");
}
if (groupRejoinWaitTime < 0) {
throw new ConfigurationException(
"GroupForming: groupRejoinWaitTime must be >= 1!");
}
}
this.maxNumberOfGroups = maxNumberOfGroups;
this.minGroupSize = minGroupSize;
this.maxGroupSize = maxGroupSize;
this.probabilityToJoin = probabilityToJoin;
this.groupFormationSetupDelay = groupFormationSetupDelay;
this.groupFormationDelay = groupFormationDelay;
this.groupRejoinWaitTime = groupRejoinWaitTime;
}
@Override
public void initialize(SocialGroupMovementModel movementModel) {
this.movementModel = movementModel;
groupCon = MovementGroupContainer.getInstance();
stayDuration = new LinkedHashMap<INodeID, Tuple<Long,Long>>();
for(SimLocationActuator host : movementModel.getAllLocationActuators()) {
stayDuration.put(host.getHost().getId(), new Tuple<Long, Long>(0L, Time.getCurrentTime()));
}
if(!enableGroups) {
return;
}
for(int g = 0; g < maxNumberOfGroups; g++) {
long delay = Math.max(Time.MINUTE, (long) ((rand.nextDouble() * (Time.MINUTE * 45) + Time.MINUTE)));
//System.out.println("("+g+") Init Group Formation Time " + Time.getFormattedTime(delay) );
Event.scheduleWithDelay(delay, new EventHandler() {
@Override
public void eventOccurred(Object content, int type) {
assembleGroup();
}
}, null, 0);
}
}
/**
* Returns a time delay between removal and creation of a group. A random part of up to half of the
* stated formation delay is added to/substracted from the formation delay.
*
* @return
*/
protected long rndGroupFormationDelay() {
return (long) ((rand.nextDouble() + 0.5) * groupFormationDelay);
}
/**
* Returns a random integer value between the minimum and maximumn group size boundaries
* with an additional upper boundary.
*
* @return
*/
protected int rndGroupSize(int max) {
return Math.min(rndGroupSize(), max);
}
/**
* Returns a random integer value between the minimum and maximumn group size boundaries.
*
* @return
*/
protected int rndGroupSize() {
if(maxGroupSize == minGroupSize)
return minGroupSize;
else {
// int groupsize = (int) Math.max(1, rand.nextGaussian() * 0.93 + 2.76);
int groupsize = rand.nextInt(maxGroupSize - minGroupSize + 1) + minGroupSize;
//System.out.println("[AbstractGroupForming] Group Size: " + groupsize + " ("+minGroupSize+"/"+maxGroupSize+")");
return groupsize;
}
//return rand.nextInt(maxGroupSize - minGroupSize) + minGroupSize;
}
/**
* Creates a group with the given set of members.
*
* @param Set<SimLocationActuator> The members which want to form a group.
*/
protected void createGroup(LinkedHashSet<SimLocationActuator> members) {
SocialMovementGroup group = new SocialMovementGroup(members);
// force a new attraction point assignment on the leader
IAttractionPoint currentDestination = movementModel.getAttractionAssignmentStrategy().getAssignment(group.getLeader());
movementModel.getAttractionAssignmentStrategy().addComponent(group.getLeader());
IAttractionPoint targetDestination = movementModel.getAttractionAssignmentStrategy().getAssignment(group.getLeader());
// Add Offset to group destination
PositionVector destination = movementModel.addOffset(new PositionVector(targetDestination),
targetDestination.getRadius() / 3);
group.setDestination(destination);
setGroupMeetingPoint(group);
for(SimLocationActuator member : group.getMembers()) {
// only set the attraction point for non-leaders, as the leader was already assigned an attraction point above
if(group.getLeader() != member) {
member.setTargetAttractionPoint(targetDestination);
}
// remove any left-over entries
if(groupCon.hostWasGroupMember(member)) {
groupCon.removeLeftGroupAtTimeEntry(member);
}
}
groupCon.addGroup(group);
// Inform analyzer of new group
if(Monitor.hasAnalyzer(ISocialGroupMovementAnalyzer.class)) {
Monitor.getOrNull(ISocialGroupMovementAnalyzer.class).onGroupForming(group, currentDestination, targetDestination);
}
}
/**
* Removes a group.
*
* @param SocialMovementGroup The group to be removed.
*
*/
@Override
public void removeGroup(SocialMovementGroup group) {
// Inform analyzer of group removal
if(Monitor.hasAnalyzer(ISocialGroupMovementAnalyzer.class)) {
Monitor.getOrNull(ISocialGroupMovementAnalyzer.class).onGroupDisbanding(group);
}
// If this group is removed, reassign the group target attraction point as new target attraction point
// for each former group member (prevents errors when routing was not reset correctly)
if(group.getGroupSize() > 0 || group.getLeader() != null) {
IAttractionPoint currentAP = group.getLeader().getCurrentTargetAttractionPoint();
for (SimLocationActuator host : group.getMembers()) {
host.setTargetAttractionPoint(currentAP);
}
}
groupCon.removeGroup(group);
long delay = 0;
if(groupFormationDelay == -1) {
if(group.getLeader() == null) {
delay = movementModel.getAttractionAssignmentStrategy().getPauseTime(null);
}
else {
delay = movementModel.getAttractionAssignmentStrategy().getPauseTime(group.getLeader().getCurrentTargetAttractionPoint());
}
}
else {
delay = rndGroupFormationDelay();
}
Event.scheduleWithDelay(delay, new EventHandler() {
@Override
public void eventOccurred(Object content, int type) {
assembleGroup();
}
}, null, 0);
}
public static void nodeready() {
instance.assembleGroup();
}
protected abstract void assembleGroup();
/**
* Returns the first AttractionPoint which contains the host with the greatest stayDuration of all hosts located in AttractionPoints.
*
* @return AttractionPoint
*
*/
protected IAttractionPoint getAttractionPointWithOldestHost() {
IAttractionPoint result = null;
long maxDuration = 0;
for(IAttractionPoint ap : movementModel.getAttractionPoints()) {
for(SimLocationActuator host : HostAtAttractionPointCounter.getHostsOfAttractionPoint(ap, movementModel.getAllLocationActuators())) {
INodeID id = host.getHost().getId();
long duration = stayDuration.get(id).getA();
if(duration > maxDuration) {
result = ap;
maxDuration = duration;
}
}
}
return result;
}
protected IAttractionPoint getAttractionPointWithMostHosts() {
IAttractionPoint apCandidate = null;
int size = 0;
for(IAttractionPoint ap : movementModel.getAttractionPoints()) {
int numberOfHostsInAP = HostAtAttractionPointCounter.getHostCountOfAttractionPoint(ap, movementModel.getAllLocationActuators());
if(numberOfHostsInAP > size) {
apCandidate = ap;
size = numberOfHostsInAP;
}
}
return apCandidate;
}
/**
* Checks if a host wants to join a group or not based on a given probability.
* Returns true if willing to join, false else.
*
* @return boolean
*/
protected boolean wantsToJoin() {
return (rand.nextDouble() <= probabilityToJoin);
}
/**
* Checks if a host has waited long enough before he is allowed to join groups again.
* Returns true, if the host has waited long enough, false otherwise.
*
* @param SimLocationActuator The host to be checked.
* @return boolean
*/
protected boolean groupRejoinTimerExpired(SimLocationActuator host) {
return groupCon.getTimeSinceHostLeftLastGroup(host) >= groupRejoinWaitTime;
}
/**
* Returns true, if a host has been in a group before at least once. False else.
*
* @param SimLocacationActuator The host to be checked.
* @return Boolean
*/
protected boolean beenInGroup(SimLocationActuator host) {
return groupCon.hostWasGroupMember(host);
}
/*
* =====================================================================================================
* === MEETING POINT FUNCTIONS
* =====================================================================================================
*/
/**
* Sets the meeting point of a group after the group has been created. Before walking towards the destination,
* the group members first have to meet at the meeting point.
*
* @param SocialMovementGroup The group to set the meeting point.
*/
protected void setGroupMeetingPoint(SocialMovementGroup group) {
PositionVector leaderPos = group.getLeader().getRealPosition();
BasicAttractionPoint meetingPoint =
new BasicAttractionPoint("Group Movement Meeting Point of Leader #"+group.getLeader().getHost().getId(), leaderPos);
group.setMeetingPoint(meetingPoint);
}
@Override
public int getMinGroupSize() {
return minGroupSize;
}
@Override
public int getMaxGroupSize() {
return maxGroupSize;
}
}
/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM 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
* any later version.
*
* PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.groupforming;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.api.topology.movement.local.LocalMovementStrategy;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.ISocialGroupMovementAnalyzer;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.hostcount.HostAtAttractionPointCounter;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.SocialMovementGroup;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.transition.IAttractionAssigmentStrategy;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tud.kom.p2psim.impl.util.Either;
import de.tud.kom.p2psim.impl.util.Tuple;
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
import de.tudarmstadt.maki.simonstrator.api.Monitor;
import de.tudarmstadt.maki.simonstrator.api.Time;
import de.tudarmstadt.maki.simonstrator.api.common.graph.INodeID;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.IAttractionPoint;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/**
* This class is responsible for creating or deleting groups. Therefore the amount of hosts in each POI
* is checked and from this set of hosts candidates are chosen which then form a group.
* Deletion of groups happens only in the case when they reached their group leaders destination.
*
* @author Marcel Verst
* @version 1.0, 09.11.2018
*
*
* Updated the group forming to use the simulation time units instead of integer +1 counters.
* Simplified code and bug fixes.
*
* @author Julian Zobel
* @version 1.1, 29.01.2020
*/
public class DefaultGroupForming extends AbstractGroupForming {
@XMLConfigurableConstructor({"enableGroups", "maxNumberOfGroups", "minGroupSize", "maxGroupSize", "probabilityToJoin", "groupFormationSetupDelay", "groupFormationDelay", "groupRejoinWaitTime"})
public DefaultGroupForming(boolean enableGroups, int maxNumberOfGroups, int minGroupSize, int maxGroupSize, double probabilityToJoin,
long groupFormationSetupDelay, long groupFormationDelay, long groupRejoinWaitTime) {
super(enableGroups, maxNumberOfGroups, minGroupSize, maxGroupSize,
probabilityToJoin, groupFormationSetupDelay, groupFormationDelay,
groupRejoinWaitTime);
}
public void manageGroups() {
// group forming may be deactivated
if(!enableGroups || probabilityToJoin == 0) {
return;
}
// setup delay before forming the first group...
if(Time.getCurrentTime() <= groupFormationSetupDelay) {
return;
}
runGroupDeletionProcess();
}
/*
* =====================================================================================================
* === GROUP BUILDING PROCESS
* =====================================================================================================
*/
@Override
protected void assembleGroup() {
IAttractionPoint apCandidate = movementModel.getAttractionPoints().get(rand.nextInt(movementModel.getAttractionPoints().size()));
if(apCandidate == null) {
return;
}
LinkedHashSet<SimLocationActuator> hostsAtAttractionPoint = HostAtAttractionPointCounter.getHostsOfAttractionPoint(apCandidate, movementModel.getAllLocationActuators());
int numberOfHostsInAttractionPoint = hostsAtAttractionPoint.size();
int groupSize = this.rndGroupSize(numberOfHostsInAttractionPoint);
LinkedHashSet<SimLocationActuator> groupCandidates = new LinkedHashSet<>();
// shuffle the hosts at the attraction point (more randomness ftw!)
LinkedList<SimLocationActuator> randomShuffledHostsAtAttractionPoint = new LinkedList<>(hostsAtAttractionPoint);
Collections.shuffle(randomShuffledHostsAtAttractionPoint, rand);
for (SimLocationActuator m : randomShuffledHostsAtAttractionPoint) {
// do not continue if enough group members are gathered
if(groupCandidates.size() == groupSize) {
break;
}
// skip already added hosts (should never happen)
if(groupCandidates.contains(m)) {
continue;
}
// skip single hosts
if(movementModel.getSingleHosts().contains(m)) {
continue;
}
// skip hosts that are already part of a group
if(groupCon.isGroupMember(m)) {
continue;
}
// test if the host wants and also is allowed to join this group
if(wantsToJoin()) {
if(!beenInGroup(m) || groupRejoinTimerExpired(m)) {
groupCandidates.add(m);
}
}
}
// if there are no candidates, wait a minute until asking again
if(groupCandidates.isEmpty() || groupCandidates == null || groupCandidates.size() < minGroupSize) {
Event.scheduleWithDelay(Time.MINUTE, new EventHandler() {
@Override
public void eventOccurred(Object content, int type) {
assembleGroup();
}
}, null, 0);
return;
}
for(SimLocationActuator candidate : groupCandidates) {
INodeID id = candidate.getHost().getId();
stayDuration.put(id, new Tuple<Long, Long>(0L, Time.getCurrentTime())); // FIXME why?
}
createGroup(groupCandidates);
}
/*
* =====================================================================================================
* === GROUP DELETION PROCESS
* =====================================================================================================
*/
public void runGroupDeletionProcess() {
LocalMovementStrategy movementStrategy = movementModel.getMovementStrategy();
IAttractionAssigmentStrategy transition = movementModel.getAttractionAssignmentStrategy();
LinkedHashSet<SocialMovementGroup> groupsToRemove = new LinkedHashSet<>();
LinkedHashSet<SocialMovementGroup> groups = groupCon.getGroups();
for(SocialMovementGroup group : groups) {
SimLocationActuator leader = group.getLeader();
PositionVector target = movementModel.getCurrentTargets().get(leader);
Either<PositionVector, Boolean> either = movementStrategy.nextPosition(leader, target);
if (either.hasLeft()) {
// Do nothing
} else {
transition.reachedAttractionPoint(leader, leader.getCurrentTargetAttractionPoint());
// Inform analyzer of group removal
if(Monitor.hasAnalyzer(ISocialGroupMovementAnalyzer.class)) {
Monitor.getOrNull(ISocialGroupMovementAnalyzer.class).onGroupArriveAtAttractionPoint(group);
}
groupsToRemove.add(group);
}
}
for(SocialMovementGroup group : groupsToRemove) {
removeGroup(group);
}
}
/*
* =====================================================================================================
* === HELPER FUNCTIONS
* =====================================================================================================
*/
/**
* Updates the stayDuration of each Host
*/
public void updateStayDuration() {
for(SimLocationActuator component : movementModel.getAllLocationActuators()) {
INodeID id = component.getHost().getId();
// In AP, increase stayDuration
if(movementModel.getAttractionAssignmentStrategy().hostInAttractionPointArea(component)) {
Tuple<Long, Long> timeInfo = stayDuration.get(id);
long delta = Time.getCurrentTime() - timeInfo.getB();
// update information (increase overall stay duration and set current time as update time)
stayDuration.put(id, new Tuple<Long, Long>(timeInfo.getA() + delta, Time.getCurrentTime()));
}
// Not in AP, reset stayDuration
else {
stayDuration.put(id, new Tuple<Long, Long>(0L, Time.getCurrentTime()));
}
}
}
}
/*
* Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
*
* This file is part of PeerfactSim.KOM.
*
* PeerfactSim.KOM 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
* any later version.
*
* PeerfactSim.KOM 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 PeerfactSim.KOM. If not, see <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.groupforming;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.SocialGroupMovementModel;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.SocialMovementGroup;
/**
* This interface specifies the GroupFormingStrategy, which basically handles the creation and deletion of groups.
* @author Marcel Verst
* @version 1.0, 22.11.2018
*/
public interface IGroupFormingBehavior {
/**
* Initializes class variables.
*
* @author Marcel Verst
*/
void initialize(SocialGroupMovementModel movementModel);
/**
* Manages the group handling. Calls processes for creating and deleting groups.
*
* Groups are only created if conditions are met and enough candidates are there to form a group.
* Groups are deleted if the group leader reached its destination and thus all other members also
* reached their destination.
*
* @param SimLocationActuator The host to be checked.
* @param PositionVector The destination of the host.
*
* @author Marcel Verst
*/
void manageGroups();
/**
* Checks if the given host is a leader and reached the its destination. If the destination is reached,
* the group of the leader is removed.
*
* @param SimLocationActuator The host to be checked.
* @param PositionVector The destination of the host.
*
* @author Marcel Verst
*/
void runGroupDeletionProcess();
public int getMinGroupSize();
public int getMaxGroupSize();
public void removeGroup(SocialMovementGroup group);
}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment