/*
* 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);
}
}
}