/* * 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.LinkedHashSet; import java.util.Map; import java.util.Random; import java.util.Set; 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.movement.modularosm.transition.ITransitionStrategy; import de.tud.kom.p2psim.impl.topology.util.PositionVector; import de.tud.kom.p2psim.impl.util.Tuple; 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; /** * Abstract class implementation for implementing GroupFormingStrategies. * * @author Marcel Verst * @version 1.0, 22.11.2018 */ 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 waitTime; protected long setupTime; protected IAttractionPointHostCounter hostCounter; /** * Creates a group with the given set of participants. * * @param Set The participants which want to form a group. * * @author Marcel Verst */ public void createGroup(Set candidates) { SocialMovementGroup group = new SocialMovementGroup(candidates); // Guarantee that the new destination of a group is not equal to the current AttractionPoint where the group is created // This would be the case if the destination of the group leader is the current position of the group. AttractionPoint leaderTarget = group.getLeader().getCurrentTargetAttractionPoint(); AttractionPoint poiDestination = null; do { poiDestination = getRandomAttractionPoint(); } while(poiDestination == leaderTarget); // Add Offset to destination PositionVector destination = new PositionVector(poiDestination.getLongitudeOrX(), poiDestination.getLatitudeOrY()); double apRadius = poiDestination.getRadius(); PositionVector offset = new PositionVector(rand.nextDouble() * apRadius / 3, rand.nextDouble() * apRadius / 3); destination.plus(offset); group.setDestination(destination); setMP(group); for(SimLocationActuator member : group.getMembers()) { member.setTargetAttractionPoint(poiDestination); groupCon.addHasBeenInGroupEntry(member, true); if(groupCon.getLeftGroupAtTime().containsKey(member)) { // System.out.println(groupCon.getLeftGroupAtTime().get(member)); groupCon.removeLeftGroupAtTimeEntry(member); } } groupCon.addGroup(group); } /** * Returns a random {@link AttractionPoint} out of the set of all AttractionPoints * * @return AttractionPoint * * @author Marcel Verst */ protected AttractionPoint getRandomAttractionPoint() { int item = rand.nextInt(IAttractionGenerator.attractionPoints.size()); return IAttractionGenerator.attractionPoints.get(item); } /** * Removes a group. * * @param SocialMovementGroup The group to be removed. * * @author Marcel Verst */ public void removeGroup(SocialMovementGroup group) { groupCon.removeGroup(group); } /** * Returns the first AttractionPoint which contains the host with the greatest stayDuration of all hosts located in AttractionPoints. * * @return AttractionPoint * * @author Marcel Verst, Julian Zobel */ 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; } /** * Returns an Array of SimLocationActuators sorted in ascending order of stayDuration values. * * @param Set The candidates to sort. * @return SimLocationActuator[] * * @author Marcel Verst */ public SimLocationActuator[] getHostsSortedByStayDurationAscending(Set candidates) { int size = candidates.size(); SimLocationActuator[] youngestHostsSorted = new SimLocationActuator[size]; for(int i = 0; i < size; i++) { long largestDuration = Integer.MAX_VALUE; SimLocationActuator youngesHost = null; for(SimLocationActuator candidate : candidates) { INodeID id = candidate.getHost().getId(); long duration = stayDuration.get(id).getA(); if(duration <= largestDuration) { largestDuration = duration; youngesHost = candidate; } } youngestHostsSorted[i] = youngesHost; candidates.remove(youngesHost); } return youngestHostsSorted; } /** * Returns an Array of SimLocationActuators sorted in descending order of stayDuration values. * * @param Set The candidates to sort. * @return SimLocationActuator[] * * @author Marcel Verst */ public SimLocationActuator[] getHostsSortedByStayDurationDescending(Set candidates) { int size = candidates.size(); SimLocationActuator[] oldestHostsSorted = new SimLocationActuator[size]; for(int i = 0; i < size; i++) { long largestDuration = 0; SimLocationActuator oldestHost = null; for(SimLocationActuator candidate : candidates) { if(!groupCon.isGroupMember(candidate)) { INodeID id = candidate.getHost().getId(); long duration = stayDuration.get(id).getA(); if(duration >= largestDuration) { largestDuration = duration; oldestHost = candidate; } } } if(oldestHost != null) { oldestHostsSorted[i] = oldestHost; } candidates.remove(oldestHost); } return oldestHostsSorted; } 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 for a set of candidates which candidate is able to join a group. Returns a subset of group candidates which * are able to form a group with other candidates. * * @param Set Set of candidates. * @return Set Set of group candidates. * * @author Marcel Verst */ public Set getGroupCandidates(Set candidates){ Set groupCandidates = new LinkedHashSet<>(); for(SimLocationActuator candidate : candidates) { // Candidate can only be a group candidate if he is not yet in a group if(!groupCon.isGroupMember(candidate)){ // Further check if the candidate even wants to join (based on probability) if(wantsToJoin()) { /* * Candidate is not in a group yet and wants to be in a group: * Check if the candidate has been in a group before. */ // If not, add the candidate to the set of group candidates. if(!beenInGroup(candidate)) { groupCandidates.add(candidate); } // Host has already been in a group. Check if the host has waited long enough to join another group. else { if(waitedLongEnough(candidate, Time.getCurrentTime() / 1000000)) { groupCandidates.add(candidate); } } } else { /* * The host does not want to join. This decision is counted as an action which has the impact, * that the host can not perform any other action until he waited for a specified amount of time. * This procedure ensures that the host is not asked again every second if he wants to join a group. */ groupCon.addHasBeenInGroupEntry(candidate, true); groupCon.addLeftGroupAtTimeEntry(candidate, Time.getCurrentTime() / 1000000); } } } return groupCandidates; } /** * 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 * * @author Marcel Verst */ 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 else. * * @param SimLocationActuator The host to be checked. * @param int The current time. * @return boolean * * @author Marcel Verst */ protected boolean waitedLongEnough(SimLocationActuator host, long time) { return (getWaitingTime(host, time) >= waitTime); } /** * Returns the time a host has waited since he left a group the last time. * * @param SimLocacationActuator The host to be checked. * @param Long The current time. * @return Long * * @author Marcel Verst */ private long getWaitingTime(SimLocationActuator host, long currentTime) { return currentTime - groupCon.getLeftGroupAtTime().get(host); } /** * 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 * * @author Marcel Verst */ protected boolean beenInGroup(SimLocationActuator host) { if(!groupCon.getHasBeenInAGroup().containsKey(host)) { groupCon.addHasBeenInGroupEntry(host, false); } return groupCon.getHasBeenInAGroup().get(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. * * @author Marcel Verst, Julian Zobel */ public void setMP(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()) { ITransitionStrategy transition = movementModel.getTransition(); transition.updateTargetAttractionPoint(participant, meetingPoint); } } }