/*
* 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.movement.modularosm.transition.IAttractionAssigmentStrategy;
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
* @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 groupRejoinWaitTime;
protected long groupFormationSetupDelay;
protected 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);
}
protected abstract void triggerGroupFormation();
/**
* Creates a group with the given set of participants.
*
* @param Set The participants which want to form a group.
*
* @author Marcel Verst
*/
protected 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);
setGroupMeetingPoint(group);
for(SimLocationActuator member : group.getMembers()) {
member.setTargetAttractionPoint(poiDestination);
if(groupCon.hostWasGroupMember(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
*/
protected 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
*/
protected 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
*/
protected 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 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 groupRejoinTimerExpired(SimLocationActuator host) {
long t = groupCon.getTimeSinceHostLeftLastGroup(host);
System.out.println(host.getHost().getId() + " | "+Time.getFormattedTime()
+" | Wait for:" +Time.getFormattedTime(t) + ", req is " + Time.getFormattedTime(groupRejoinWaitTime) );
boolean dec = t >= groupRejoinWaitTime;
return dec;
}
/**
* 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) {
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.
*
* @author Marcel Verst, Julian Zobel
*/
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()) {
IAttractionAssigmentStrategy transition = movementModel.getAttractionAssignmentStrategy();
transition.updateTargetAttractionPoint(participant, meetingPoint);
}
}
@Override
public int getMinGroupSize() {
return minGroupSize;
}
@Override
public int getMaxGroupSize() {
return maxGroupSize;
}
}