/* * 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 . * */ package de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.groupforming; import java.util.LinkedHashMap; 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.SocialGroupMovementModel; import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.BasicAttractionPoint; import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.IAttractionGenerator; import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.hostcount.IAttractionPointHostCounter; 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.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.AttractionPoint; 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> 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; protected IAttractionPointHostCounter hostCounter; @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) { 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 < 0) { 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(); // Initialize instances of other classes this.hostCounter = movementModel.getAttractionPointHostCounter(); stayDuration = new LinkedHashMap>(); for(SimLocationActuator host : movementModel.getAllLocationActuators()) { stayDuration.put(host.getHost().getId(), new Tuple(0L, Time.getCurrentTime())); } Event.scheduleWithDelay(groupFormationSetupDelay, new EventHandler() { @Override public void eventOccurred(Object content, int type) { triggerGroupFormation(); } }, 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() { return rand.nextInt(maxGroupSize - minGroupSize) + minGroupSize; } protected abstract void triggerGroupFormation(); /** * Creates a group with the given set of members. * * @param Set The members which want to form a group. */ protected void createGroup(Set members) { SocialMovementGroup group = new SocialMovementGroup(members); movementModel.getAttractionAssignmentStrategy().addComponent(group.getLeader()); AttractionPoint nextDestination = movementModel.getAttractionAssignmentStrategy().getAssignment(group.getLeader()); // Add Offset to group destination PositionVector destination = movementModel.addGaussianOffsetToPosition(new PositionVector(nextDestination), nextDestination.getRadius() / 3); group.setDestination(destination); setGroupMeetingPoint(group); for(SimLocationActuator member : group.getMembers()) { member.setTargetAttractionPoint(nextDestination); if(groupCon.hostWasGroupMember(member)) { groupCon.removeLeftGroupAtTimeEntry(member); } } groupCon.addGroup(group); } /** * Removes a group. * * @param SocialMovementGroup The group to be removed. * */ @Override public void removeGroup(SocialMovementGroup group) { groupCon.removeGroup(group); Event.scheduleWithDelay(rndGroupFormationDelay(), new EventHandler() { @Override public void eventOccurred(Object content, int type) { triggerGroupFormation(); } }, null, 0); } /** * Returns the first AttractionPoint which contains the host with the greatest stayDuration of all hosts located in AttractionPoints. * * @return AttractionPoint * */ protected AttractionPoint getAttractionPointWithOldestHost() { AttractionPoint result = null; long maxDuration = 0; for(AttractionPoint ap : IAttractionGenerator.attractionPoints) { for(SimLocationActuator host : hostCounter.getHostsOfAttractionPoint(ap)) { INodeID id = host.getHost().getId(); long duration = stayDuration.get(id).getA(); if(duration > maxDuration) { result = ap; maxDuration = duration; } } } return result; } protected AttractionPoint getAttractionPointWithMostHosts() { AttractionPoint apCandidate = null; int size = 0; for(AttractionPoint ap : IAttractionGenerator.attractionPoints) { int numberOfHostsInAP = hostCounter.getHostCountOfAttractionPoint(ap); 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); // Update target for all group members to meeting point for(SimLocationActuator participant : group.getMembers()) { movementModel.getAttractionAssignmentStrategy().updateTargetAttractionPoint(participant, meetingPoint); } } @Override public int getMinGroupSize() { return minGroupSize; } @Override public int getMaxGroupSize() { return maxGroupSize; } }