/* * 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.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> 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>(); for(SimLocationActuator host : movementModel.getAllLocationActuators()) { stayDuration.put(host.getHost().getId(), new Tuple(0L, Time.getCurrentTime())); } if(!enableGroups) { return; } for(int g = 0; g < maxNumberOfGroups; g++) { long delay = Math.max(Time.MINUTE, (long) ((groupFormationSetupDelay + rand.nextGaussian() * groupFormationSetupDelay ))); Monitor.log(this.getClass(), Monitor.Level.INFO, "Group: "+g+" Initial 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 The members which want to form a group. */ protected void createGroup(LinkedHashSet 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; } }