/*
* 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;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import de.tud.kom.p2psim.api.scenario.ConfigurationException;
import de.tud.kom.p2psim.api.topology.Topology;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.api.topology.movement.local.LocalMovementStrategy;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.MovementGroupContainer;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.SocialMovementGroup;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.groupencounter.IGroupEncounterBehavior;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.groups.groupforming.IGroupFormingBehavior;
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.topology.views.VisualizationTopologyView.VisualizationInjector;
import de.tud.kom.p2psim.impl.util.Either;
import de.tudarmstadt.maki.simonstrator.api.Binder;
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.Monitor;
import de.tudarmstadt.maki.simonstrator.api.Time;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.IAttractionPoint;
/**
*
* @author Julian Zobel
* @version 1.0, 28.01.2020
*/
public class SocialGroupMovementModel extends ModularMovementModel {
private final double MEETING_POINT_DISTANCE = 5;
private final double LEADER_GROUP_DISTANCE = 10;
protected MovementGroupContainer groupContainer;
protected IGroupFormingBehavior groupFormingBehavior;
protected IGroupEncounterBehavior groupEncounterBehavior;
protected IAttractionAssigmentStrategy groupAttractionAssignment;
private LinkedHashSet singleHosts = new LinkedHashSet();
private int numberOfSingleHosts;
@Override
public void initialize() {
if (!initialized) {
groupContainer = MovementGroupContainer.getInstance();
groupFormingBehavior.initialize(this);
groupEncounterBehavior.initialize(this);
// Choose single hosts
if(numberOfSingleHosts > 0) {
do {
singleHosts.add(getRandomActuator());
} while(singleHosts.size() < numberOfSingleHosts);
}
if (modelVisualisation == null) {
modelVisualisation = new ModularMovementModelViz(this);
}
VisualizationInjector.injectComponent(modelVisualisation);
if (mapVisualization != null) {
VisualizationInjector.injectComponent(mapVisualization);
}
if (attractionPointViz != null) {
System.out.println("insert AP viz");
VisualizationInjector.injectComponent(attractionPointViz);
}
checkConfiguration();
// setWayPointModel
localMovementStrategy.setObstacleModel(Binder
.getComponentOrNull(Topology.class).getObstacleModel());
localMovementStrategy.setWaypointModel(Binder
.getComponentOrNull(Topology.class).getWaypointModel());
/*
* Scale depending on calculation interval, if interval != 1 Second.
*/
localMovementStrategy
.setScaleFactor(timeBetweenMoveOperation / (double) Time.SECOND);
attractionAssigment.addAttractionAssignmentListener(this);
attractionAssigment.setAttractionProvider(attractionProvider);
groupAttractionAssignment.addAttractionAssignmentListener(this);
groupAttractionAssignment.setAttractionProvider(attractionProvider);
// This adds the mobile hosts (smartphones/users) to the transition
// strategy
for (SimLocationActuator ms : moveableHosts) {
if(singleHosts.contains(ms)) {
attractionAssigment.addComponent(ms);
if(placeNodesAtAP) {
IAttractionPoint assignment = attractionAssigment.getAssignment(ms);
ms.updateCurrentLocation(this.addOffset(new PositionVector(assignment),
(assignment.hasRadius() ? Math.max(assignment.getRadius(), 25.0) : 25.0)));
attractionAssigment.updateTargetAttractionPoint(ms, assignment);
}
}
else {
groupAttractionAssignment.addComponent(ms);
if(placeNodesAtAP) {
IAttractionPoint assignment = groupAttractionAssignment.getAssignment(ms);
ms.updateCurrentLocation(this.addOffset(new PositionVector(assignment),
(assignment.hasRadius() ? Math.max(assignment.getRadius(), 25.0) : 25.0)));
groupAttractionAssignment.updateTargetAttractionPoint(ms, assignment);
}
}
// //attractionAssigment.addComponent(ms);
// if(placeNodesAtAP) {
// IAttractionPoint assignment = attractionAssigment.getAssignment(ms);
// ms.updateCurrentLocation(this.addOffset(new PositionVector(assignment),
// (assignment.hasRadius() ? Math.max(assignment.getRadius(), 25.0) : 25.0)));
// attractionAssigment.updateTargetAttractionPoint(ms, assignment);
// }
}
setTimeBetweenMoveOperations(timeBetweenMoveOperation);
// Inform analyzer of resolved movement
if(Monitor.hasAnalyzer(IAttractionBasedMovementAnalyzer.class)) {
Monitor.getOrNull(IAttractionBasedMovementAnalyzer.class).onAllNodeInitializationCompleted(moveableHosts);
}
// initial move
move();
initialized = true;
}
}
/**
* Returns a random SimLocationActuator component from the set of moveable hosts.
*
* @return SimLocationActuator
*/
private SimLocationActuator getRandomActuator() {
int index = rand.nextInt(moveableHosts.size());
int i = 0;
for(SimLocationActuator actuator : moveableHosts) {
if(i == index) {
return actuator;
}
i++;
}
return null;
}
/**
* The movement model.
* 1) Updates the number of hosts within each attraction point.
* 2) Creates and deletes groups, if necessary.
* 3) Applies encounter action to meeting groups.
* 4) Moves the hosts using doGroupMovement, movement style depends on role of the host (Leader, Participant, Single)
*/
@Override
protected void move() {
// Check POIs if groups can be created. Delete groups, if destination reached.
groupFormingBehavior.manageGroups();
// Checks if groups encounter each other and applies a specified action to these groups.
groupEncounterBehavior.handleEncounters();
/*
* Moves all nodes according to definition in group movement method
*/
for (SimLocationActuator host : moveableHosts) {
assert currentTargets.containsKey(host);
// Single Host Movement
if(singleHosts.contains(host)) {
super.doLocalMovement(host, currentTargets.get(host));
}
else if( !groupContainer.isGroupMember(host)) {
doLocalMovement(host, currentTargets.get(host));
}
else if(groupContainer.isGroupMember(host)){
doGroupMovement(host, currentTargets.get(host));
}
else {
throw new UnsupportedOperationException("SimLocationActuator " + host + " is neither in a group nor a single node");
}
// TODO maybe remodel the group movement to do a whole group?
}
// TODO Move each group
Event.scheduleWithDelay(timeBetweenMoveOperation, this, null, EVENT_MOVE);
}
@Override
protected void doLocalMovement(SimLocationActuator ms, PositionVector destination) {
Either either = localMovementStrategy.nextPosition(ms, destination);
if (either.hasLeft()) {
ms.updateCurrentLocation(either.getLeft());
if(!checkBoundaries(ms.getRealPosition())) {
System.err.println("Social Group Movement Model: Host moved outside of simulated area!");
}
}
else {
groupAttractionAssignment.reachedAttractionPoint(ms, ms.getCurrentTargetAttractionPoint());
}
}
/**
* Applies movement of the host according to different circumstances:
*
* Towards Destination: Host is not a group member || Group has met at meeting point successfully.
* Towards MeetingPoint: Host if group member and has not reached meeting point yet.
* No movement: Host reached meeting point, but still waits for group members to arrive.
*
* @param SimLocationActuator The host to move.
* @param PositionVector Destination of the host.
*/
protected void doGroupMovement(SimLocationActuator host, PositionVector destination) {
// This host is a member of a group, thus...
SocialMovementGroup group = groupContainer.getGroupOfHost(host);
// if the group is currently gathering at a meeting point...
if(movesToGroupMeetingPoint(host)) {
// if this host has already arrived at the group meeting point..
if(hostIsAtMeetingPoint(host)) {
// ... and if the whole group is also at the meeting point...
if(groupGatheredAtMeetingPoint(group)) {
// remove the meeting point and thus, start moving towards the actual destination
group.setMeetingPoint(null);
}
// ... but the group is not gathered at the meeting point, do nothing and wait for the rest.
else {
// inform analyzer of waiting group members
if(Monitor.hasAnalyzer(ISocialGroupMovementAnalyzer.class)) {
Monitor.getOrNull(ISocialGroupMovementAnalyzer.class).onMemberWaitAtMeetingPoint(group, host);
}
}
}
// if the host has not reached the group meeting point, move towards it.
else {
PositionVector mp = groupContainer.getGroupOfHost(host).getMeetingPoint();
doLocalMovement(host, mp);
// inform analyzer group member moves to meeting point
if(Monitor.hasAnalyzer(ISocialGroupMovementAnalyzer.class)) {
Monitor.getOrNull(ISocialGroupMovementAnalyzer.class).onMoveToMeetingPoint(group, host);
}
}
}
// ... but if the group is currently on their way to the destination
else {
// the leader does a local movement
if(groupContainer.isLeader(host)) {
if(!groupContainer.isWaiting(groupContainer.getGroupOfHost(host))) {
// inform analyzer of movement to attraction point
if(Monitor.hasAnalyzer(ISocialGroupMovementAnalyzer.class)) {
Monitor.getOrNull(ISocialGroupMovementAnalyzer.class).onGroupMovesToAttractionPoint(group);
}
doLocalMovement(host, destination);
PositionVector leaderPos = host.getRealPosition();
LinkedHashSet groupMembers = groupContainer.getGroupMembers(host);
groupMembers.remove(host); // remove leader
for (SimLocationActuator groupMember : groupMembers) {
// Assign small offset to the host depending on the leaders position.
PositionVector offset = new PositionVector(rand.nextDouble() * LEADER_GROUP_DISTANCE, rand.nextDouble() * LEADER_GROUP_DISTANCE);
PositionVector newPos = leaderPos.plus(offset);
// Update location of host, which will be around the leaders location.
groupMember.updateCurrentLocation(newPos);
}
}
else {
// inform analyzer of waiting group
if(Monitor.hasAnalyzer(ISocialGroupMovementAnalyzer.class)) {
Monitor.getOrNull(ISocialGroupMovementAnalyzer.class).onGroupWait(group);
}
}
}
}
}
@Override
public void changeTargetLocation(SimLocationActuator actuator, IAttractionPoint ap) {
if(singleHosts.contains(actuator)) {
super.changeTargetLocation(actuator, ap);
}
else {
groupAttractionAssignment.updateTargetAttractionPoint(actuator, ap);
actuator.setMovementSpeed(this.speedProvider.calculateSpeed());
}
}
@Override
public IAttractionPoint getTargetLocation(SimLocationActuator actuator) {
if(singleHosts.contains(actuator)) {
return attractionAssigment.getAssignment(actuator);
}
else {
return groupAttractionAssignment.getAssignment(actuator);
}
}
/*
* =====================================================================================================
* === MEETING POINT FUNCTIONS
* =====================================================================================================
*/
/**
* Checks if a host is currently on its way to a meeting point or to its destination.
* Returns true if moving towards meeting point, false else.
*
* @param SimLocationActuator The host to be checked.
* @return Boolean
*/
private boolean movesToGroupMeetingPoint(SimLocationActuator host) {
PositionVector meetingpoint = groupContainer.getGroupOfHost(host).getMeetingPoint();
if(meetingpoint == null)
return false;
else if(meetingpoint != groupContainer.getGroupOfHost(host).getDestination())
return true;
else
return false;
}
/**
* Returns true, if all group members of a host including the host itself reached the meeting point, false else.
*
* @param SimLocationActuator The host to be checked.
* @return Boolean
*/
private boolean groupGatheredAtMeetingPoint(SocialMovementGroup group) {
for(SimLocationActuator participant : group.getMembers()) {
if(!hostIsAtMeetingPoint(participant)) {
return false;
}
}
return true;
}
/**
* Returns true if the host reached its current meeting point, false else.
*
* @param SimLocationActuator The host to be checked.
* @return Boolean
*
*/
private boolean hostIsAtMeetingPoint(SimLocationActuator host) {
PositionVector meetingpoint = groupContainer.getGroupOfHost(host).getMeetingPoint();
if(host.getLastLocation().distanceTo(meetingpoint) <= MEETING_POINT_DISTANCE)
return true;
else
return false;
}
/*
* =====================================================================================================
* === MOVEMENT FUNCTIONS
* =====================================================================================================
*/
/**
* Host position adapts to leaders position by taking the leader position and adding a small offset
*
* @param SimLocationActuator The host to be moved.
*/
private void followLeader(SimLocationActuator host) {
SimLocationActuator leader = groupContainer.getGroupOfHost(host).getLeader();
// Assign small offset to the host depending on the leaders position.
PositionVector leaderPos = leader.getRealPosition();
PositionVector offset = new PositionVector(rand.nextDouble() * LEADER_GROUP_DISTANCE, rand.nextDouble() * LEADER_GROUP_DISTANCE);
PositionVector newPos = leaderPos.plus(offset);
// Update location of host, which will be around the leaders location.
host.updateCurrentLocation(newPos);
}
/*
* =====================================================================================================
* === GETTER AND SETTER FUNCTIONS
* =====================================================================================================
*/
public void setGroupFormingBehavior(IGroupFormingBehavior defaultGroupForming) {
if (defaultGroupForming == null) {
throw new ConfigurationException(
"GroupFormingStrategy is missing in ModularMovementModel!");
}
this.groupFormingBehavior = defaultGroupForming;
}
public void setGroupEncounterBehavior(IGroupEncounterBehavior defaultGroupEncounterBehavior) {
if (defaultGroupEncounterBehavior == null) {
throw new ConfigurationException(
"GroupEncounterStrategy is missing in ModularMovementModel!");
}
this.groupEncounterBehavior = defaultGroupEncounterBehavior;
}
public IAttractionAssigmentStrategy getAttractionAssignmentStrategy() {
return this.groupAttractionAssignment;
}
public IGroupFormingBehavior getGroupFormingBehavior() {
return groupFormingBehavior;
}
public LocalMovementStrategy getMovementStrategy() {
return localMovementStrategy;
}
public void setNumberOfSingleHosts(int numberOfSingleHosts) {
this.numberOfSingleHosts = numberOfSingleHosts;
}
public LinkedHashSet getSingleHosts(){
return singleHosts;
}
public LinkedHashMap getCurrentTargets(){
return currentTargets;
}
public void setGroupAttractionAssignmentStrategy(IAttractionAssigmentStrategy groupAttractionAssignment) {
if (groupAttractionAssignment == null) {
throw new ConfigurationException(
"IAttractionAssigmentStrategy for Groups is missing in SocialGroupMovementModel!");
}
this.groupAttractionAssignment = groupAttractionAssignment;
}
}