Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Simonstrator
PeerfactSim.KOM
Commits
cffdfb8d
Commit
cffdfb8d
authored
Jul 04, 2015
by
Martin Hellwig
Committed by
Björn Richerzhagen
Oct 27, 2015
Browse files
First version of osm-data integration
parent
c9a213fb
Changes
8
Hide whitespace changes
Inline
Side-by-side
src/de/tud/kom/p2psim/impl/topology/movement/modularosm/ModularMovementModel.java
0 → 100644
View file @
cffdfb8d
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
package
de.tud.kom.p2psim.impl.topology.movement.modularosm
;
import
java.util.LinkedHashMap
;
import
java.util.LinkedHashSet
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map.Entry
;
import
java.util.Random
;
import
java.util.Set
;
import
java.util.Vector
;
import
de.tud.kom.p2psim.api.scenario.ConfigurationException
;
import
de.tud.kom.p2psim.api.topology.movement.MovementListener
;
import
de.tud.kom.p2psim.api.topology.movement.MovementModel
;
import
de.tud.kom.p2psim.api.topology.movement.MovementSupported
;
import
de.tud.kom.p2psim.api.topology.movement.local.LocalMovementStrategy
;
import
de.tud.kom.p2psim.impl.simengine.Simulator
;
import
de.tud.kom.p2psim.impl.topology.PositionVector
;
import
de.tud.kom.p2psim.impl.topology.Topology
;
import
de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.IAttractionGenerator
;
import
de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.AttractionPoint
;
import
de.tud.kom.p2psim.impl.topology.movement.modularosm.transition.ITransitionStrategy
;
import
de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector
;
import
de.tud.kom.p2psim.impl.util.Either
;
import
de.tudarmstadt.maki.simonstrator.api.Event
;
import
de.tudarmstadt.maki.simonstrator.api.EventHandler
;
import
de.tudarmstadt.maki.simonstrator.api.Randoms
;
/**
* Modular Movement Model uses different models/strategies to create a movement
* model. In this implementation, it has 4 different models/strategies.
* <p>
* M0: AttractionGenerator -> Generates the {@link AttractionPoint}s and place
* them on the map. The {@link AttractionPoint}s can be moved!
* <p>
* M1: A general {@link MovementModel} is not used, because we use static attraction points.
* <p>
* M2: The {@link TransitionStrategy}! It takes the Hosts, which should be moved
* around, but calculates only the assignment to the {@link AttractionPoint}s.
* It doesn't move the Hosts! It will be only assignment a new AttractionPoint!
*
* <p>
* M3: The {@link LocalMovementStrategy} is responsible for the movement of the
* Hosts. It moves the hosts to the assigned AttractionPoint, and if the
* AttractionPoint has moved, then will be followed. The
* {@link LocalMovementStrategy} will be called from the
* {@link ModularMovementModel} to do a Movement!
* <p>
* This class contains all four components and manage the data exchange.
* Additionally it contains an periodic operation, which handle the movement of
* all hosts. This mean, that it will be call the {@link LocalMovementStrategy}
* with the destination. Please take care, that the handling of the movement of
* the AttractionPoints will be handled by the movement model in M1! <br>
* Further it contains an offset for every Host, which will be added to the
* destination point (AttractionPoint), so that not all hosts, which are
* assigned to one {@link AttractionPoint}, lies on the same point.<br>
*
*
*
* @author Christoph Muenker
* @version 1.0, 02.07.2013
*/
public
class
ModularMovementModel
implements
MovementModel
,
EventHandler
{
private
final
int
EVENT_MOVE
=
1
;
private
final
int
EVENT_INIT
=
2
;
protected
PositionVector
worldDimensions
;
protected
ITransitionStrategy
transition
;
protected
IAttractionGenerator
attractionGenerator
;
protected
LocalMovementStrategy
localMovementStrategy
;
private
Set
<
MovementListener
>
movementListeners
=
new
LinkedHashSet
<
MovementListener
>();
private
Set
<
MovementSupported
>
moveableHosts
=
new
LinkedHashSet
<
MovementSupported
>();
private
Map
<
MovementSupported
,
PositionVector
>
offsetPosition
=
new
LinkedHashMap
<
MovementSupported
,
PositionVector
>();
private
boolean
initialized
=
false
;
private
long
timeBetweenMoveOperation
=
Simulator
.
SECOND_UNIT
;
private
Random
rand
;
public
ModularMovementModel
()
{
this
.
worldDimensions
=
Topology
.
getWorldDimension
();
this
.
rand
=
Randoms
.
getRandom
(
ModularMovementModel
.
class
);
// scheduling initalization!
Event
.
scheduleImmediately
(
this
,
null
,
EVENT_INIT
);
}
/**
* This Method will be not called from the Components. So we call this
* manually!
*/
public
void
initialize
()
{
if
(!
initialized
)
{
VisualizationInjector
.
injectComponent
(
"AttractionPoints"
,
-
1
,
new
ModularMovementModelViz
(
this
),
false
);
checkConfiguration
();
// setWayPointModel
localMovementStrategy
.
setObstacleModel
(
Topology
.
getTopology
()
.
getObstacleModel
());
localMovementStrategy
.
setWaypointModel
(
Topology
.
getTopology
()
.
getWaypointModel
());
List
<
AttractionPoint
>
attractionPoints
=
attractionGenerator
.
getAttractionPoints
();
transition
.
setAttractionPoints
(
attractionPoints
);
for
(
MovementSupported
ms
:
moveableHosts
)
{
transition
.
addComponent
(
ms
);
}
setTimeBetweenMoveOperations
(
timeBetweenMoveOperation
);
// initial move
move
();
initialized
=
true
;
}
}
private
void
checkConfiguration
()
{
if
(
localMovementStrategy
==
null
)
{
throw
new
ConfigurationException
(
"LocalMovementStrategy is missing in ModularMovementModel!"
);
}
if
(
transition
==
null
)
{
throw
new
ConfigurationException
(
"TransitionStrategy is missing in ModularMovementModel!"
);
}
if
(
attractionGenerator
==
null
)
{
throw
new
ConfigurationException
(
"AttractionGenerator is missing in ModularMovementModel!"
);
}
}
@Override
public
void
addComponent
(
MovementSupported
comp
)
{
moveableHosts
.
add
(
comp
);
offsetPosition
.
put
(
comp
,
randomOffsetVector
());
}
@Override
public
void
addMovementListener
(
MovementListener
listener
)
{
movementListeners
.
add
(
listener
);
}
@Override
public
void
removeMovementListener
(
MovementListener
listener
)
{
movementListeners
.
add
(
listener
);
}
@Override
public
void
setTimeBetweenMoveOperations
(
long
time
)
{
if
(
time
>
0
)
{
this
.
timeBetweenMoveOperation
=
time
;
}
else
{
throw
new
ConfigurationException
(
"time is negative for the Move Operations"
);
}
}
private
PositionVector
randomOffsetVector
()
{
double
x
=
rand
.
nextGaussian
()
*
6
;
double
y
=
rand
.
nextGaussian
()
*
6
;
return
new
PositionVector
(
x
,
y
);
}
protected
void
move
()
{
Map
<
MovementSupported
,
AttractionPoint
>
assigns
=
transition
.
getAssignments
();
for
(
Entry
<
MovementSupported
,
AttractionPoint
>
entry
:
assigns
.
entrySet
())
{
MovementSupported
ms
=
entry
.
getKey
();
PositionVector
attractionCenter
=
entry
.
getValue
()
.
getRealPosition
();
PositionVector
destination
=
new
PositionVector
(
attractionCenter
);
destination
.
add
(
offsetPosition
.
get
(
ms
));
doLocalMovement
(
ms
,
destination
);
}
Event
.
scheduleWithDelay
(
timeBetweenMoveOperation
,
this
,
null
,
EVENT_MOVE
);
notifyRoundEnd
();
}
/**
*
* Ask the local movement strategy for the next position. It may return the
* next position or a boolean with true to notify the movement model that it
* can't get any closer to the current way point.
*
* @param ms
* @param destination
*/
private
void
doLocalMovement
(
MovementSupported
ms
,
PositionVector
destination
)
{
Either
<
PositionVector
,
Boolean
>
either
=
localMovementStrategy
.
nextPosition
(
ms
,
destination
);
if
(
either
.
hasLeft
())
{
ms
.
getRealPosition
().
replace
(
either
.
getLeft
());
notifyPositionChange
(
ms
);
}
}
/**
* Notify Listeners
*/
protected
void
notifyRoundEnd
()
{
for
(
MovementListener
listener
:
movementListeners
)
{
listener
.
afterComponentsMoved
();
}
}
protected
void
notifyPositionChange
(
MovementSupported
comp
)
{
for
(
MovementListener
listener
:
movementListeners
)
{
listener
.
afterComponentMoved
(
comp
);
}
comp
.
positionChanged
();
}
public
void
setIAttractionGenerator
(
IAttractionGenerator
attractionGenerator
)
{
this
.
attractionGenerator
=
attractionGenerator
;
}
public
void
setLocalMovementStrategy
(
LocalMovementStrategy
localMovementStrategy
)
{
this
.
localMovementStrategy
=
localMovementStrategy
;
}
public
void
setITransitionStrategy
(
ITransitionStrategy
transition
)
{
this
.
transition
=
transition
;
}
@Override
public
void
eventOccurred
(
Object
content
,
int
type
)
{
if
(
type
==
EVENT_INIT
)
{
initialize
();
}
else
if
(
type
==
EVENT_MOVE
)
{
move
();
}
}
/**
* Only for visualization!
*
* @return
*/
protected
List
<
AttractionPoint
>
getAttractionPoints
()
{
return
attractionGenerator
.
getAttractionPoints
();
//return new Vector<AttractionPoint>(transition.getAssignments().values());
}
}
src/de/tud/kom/p2psim/impl/topology/movement/modularosm/ModularMovementModelViz.java
0 → 100644
View file @
cffdfb8d
/*
* Copyright (c) 2005-2015 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 <http://www.gnu.org/licenses/>.
*
*/
package
de.tud.kom.p2psim.impl.topology.movement.modularosm
;
import
java.awt.BasicStroke
;
import
java.awt.Color
;
import
java.awt.Graphics
;
import
java.awt.Graphics2D
;
import
java.awt.Point
;
import
java.awt.RenderingHints
;
import
javax.swing.JComponent
;
import
de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.AttractionPoint
;
import
de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector
;
/**
* Visualization Component of the Attraction Points in the Modular Movement Model.
*
*
* @author Martin Hellwig
* @version 1.0, 02.07.2015
*/
public
class
ModularMovementModelViz
extends
JComponent
{
private
ModularMovementModel
movementModel
;
public
ModularMovementModelViz
(
ModularMovementModel
model
)
{
setBounds
(
0
,
0
,
VisualizationInjector
.
WORLD_X
,
VisualizationInjector
.
WORLD_Y
);
setOpaque
(
true
);
setVisible
(
true
);
this
.
movementModel
=
model
;
}
@Override
public
void
paint
(
Graphics
g
)
{
super
.
paintComponent
(
g
);
Graphics2D
g2
=
(
Graphics2D
)
g
;
g2
.
setRenderingHint
(
RenderingHints
.
KEY_ANTIALIASING
,
RenderingHints
.
VALUE_ANTIALIAS_ON
);
for
(
AttractionPoint
aPoint
:
movementModel
.
getAttractionPoints
())
{
Point
point
=
aPoint
.
getRealPosition
().
asPoint
();
// draw border
g2
.
setColor
(
Color
.
BLACK
);
g2
.
setStroke
(
new
BasicStroke
(
1
f
));
g2
.
drawOval
(
point
.
x
-
15
,
point
.
y
-
15
,
30
,
30
);
g2
.
drawString
(
aPoint
.
getName
(),
point
.
x
-
15
,
point
.
y
-
15
);
//System.out.println(movementModel.getAttractionPoints().size() + ";" + aPoint.getRealPosition().toString() + ";" + aPoint.getName());
g2
.
setColor
(
new
Color
(
0.2f
,
0.8f
,
0.2f
,
0.6f
));
g2
.
fillOval
(
point
.
x
-
15
,
point
.
y
-
15
,
30
,
30
);
}
}
}
src/de/tud/kom/p2psim/impl/topology/movement/modularosm/attraction/AttractionPoint.java
0 → 100644
View file @
cffdfb8d
/*
* Copyright (c) 2005-2015 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 <http://www.gnu.org/licenses/>.
*
*/
package
de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction
;
import
java.util.Random
;
import
de.tud.kom.p2psim.impl.topology.PositionVector
;
import
de.tudarmstadt.maki.simonstrator.api.Randoms
;
/**
* This is the implementation of a AttractionPoint. This type of {@link AttractionPoint} has not the
* ability to be moved. Its data come from osm-POIs, which are static locations
*
* @author Martin Hellwig
* @version 1.0, 02.07.2015
*/
public
class
AttractionPoint
{
protected
static
Random
rnd
=
Randoms
.
getRandom
(
AttractionPoint
.
class
);
private
PositionVector
posVec
;
private
String
name
;
public
AttractionPoint
(
PositionVector
posVec
,
String
name
)
{
this
.
posVec
=
posVec
;
this
.
name
=
name
;
}
public
PositionVector
getRealPosition
()
{
return
posVec
;
}
public
String
getName
()
{
return
name
;
}
}
src/de/tud/kom/p2psim/impl/topology/movement/modularosm/attraction/IAttractionGenerator.java
0 → 100644
View file @
cffdfb8d
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
package
de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction
;
import
java.util.List
;
/**
* This is the interface for the generator of the {@link AttractionPoint}s. It
* gets the set number of AttractionPoints back. This mean, it will be generate
* them and set the Position of them.
*
* @author Christoph Muenker
* @version 1.0, 02.07.2013
*/
public
interface
IAttractionGenerator
{
public
void
setNumberOfAttractionPoints
(
int
maxNumberOfAttractionPoints
);
public
List
<
AttractionPoint
>
getAttractionPoints
();
}
src/de/tud/kom/p2psim/impl/topology/movement/modularosm/attraction/JSONAttractionGenerator.java
0 → 100644
View file @
cffdfb8d
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
package
de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction
;
import
java.io.FileInputStream
;
import
java.io.FileNotFoundException
;
import
java.io.IOException
;
import
java.util.LinkedList
;
import
java.util.List
;
import
org.apache.commons.io.IOUtils
;
import
org.json.JSONArray
;
import
org.json.JSONException
;
import
org.json.JSONObject
;
import
de.tud.kom.p2psim.impl.topology.PositionVector
;
import
de.tud.kom.p2psim.impl.topology.Topology
;
/**
* Generates attraction points out of real data from osm
* The maximal number of attraction points can be set
*
* @author Martin Hellwig
* @version 1.0, 02.07.2015
*/
public
class
JSONAttractionGenerator
implements
IAttractionGenerator
{
private
PositionVector
worldDimensions
;
private
List
<
AttractionPoint
>
attractionPoints
;
private
int
maxNumberOfAttractionPoints
;
private
String
placementJsonFile
;
private
double
latLeft
;
//Values from -90 to 90; always smaller than latRight
private
double
latRight
;
//Values from -90 to 90
private
double
lonLeft
;
//Values from -180 to 180; Always smaller than lonRight
private
double
lonRight
;
//Values from -180 to 180
/**
*
* @param file
*/
public
JSONAttractionGenerator
()
{
this
.
worldDimensions
=
Topology
.
getWorldDimension
();
}
/**
* Projects the gps coordinates in the given gps window to the world-coordinates given in world-dimensions
* @param lat
* @param lon
* @return The projected position in world-dimensions
*/
private
PositionVector
transformGPSWindowToOwnWorld
(
double
lat
,
double
lon
)
{
double
x
=
worldDimensions
.
getX
()
*
(
lon
-
lonLeft
)/(
lonRight
-
lonLeft
);
double
y
=
worldDimensions
.
getY
()
*
(
lat
-
latLeft
)/(
latRight
-
latLeft
);
return
new
PositionVector
(
x
,
y
);
}
@Override
public
void
setNumberOfAttractionPoints
(
int
numberOfAttractionPoints
)
{
this
.
maxNumberOfAttractionPoints
=
numberOfAttractionPoints
;
}
@Override
public
List
<
AttractionPoint
>
getAttractionPoints
()
{
attractionPoints
=
new
LinkedList
<
AttractionPoint
>();
if
(
attractionPoints
.
size
()
==
0
)
{
String
poiString
=
""
;
JSONArray
allPOI
=
null
;
FileInputStream
inputStream
;
try
{
inputStream
=
new
FileInputStream
(
placementJsonFile
);
poiString
=
IOUtils
.
toString
(
inputStream
);
JSONObject
poiData
=
new
JSONObject
(
poiString
);
allPOI
=
poiData
.
getJSONArray
(
"elements"
);
}
catch
(
FileNotFoundException
e
)
{
e
.
printStackTrace
();
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
catch
(
JSONException
e
)
{
e
.
printStackTrace
();
}
if
(
allPOI
!=
null
)
{
for
(
int
i
=
0
;
i
<
allPOI
.
length
();
i
++)
{
try
{
String
barname
=
allPOI
.
getJSONObject
(
i
).
getJSONObject
(
"tags"
).
getString
(
"name"
);
double
lat
=
allPOI
.
getJSONObject
(
i
).
getDouble
(
"lat"
);
double
lon
=
allPOI
.
getJSONObject
(
i
).
getDouble
(
"lon"
);
attractionPoints
.
add
(
new
AttractionPoint
(
transformGPSWindowToOwnWorld
(
lat
,
lon
),
barname
));
}
catch
(
JSONException
e
)
{
//This bar had no name defined, so there was an error. Not so bad
}
}
}
}
System
.
out
.
println
(
attractionPoints
.
size
());
if
(
maxNumberOfAttractionPoints
==
0
)
maxNumberOfAttractionPoints
=
Integer
.
MAX_VALUE
;
List
<
AttractionPoint
>
result
=
new
LinkedList
<
AttractionPoint
>();
for
(
int
i
=
0
;
(
i
<
attractionPoints
.
size
()
&&
i
<
maxNumberOfAttractionPoints
);
i
++)
{
result
.
add
(
attractionPoints
.
get
(
i
));
}
return
result
;
}
public
void
setPlacementJsonFile
(
String
placementJsonFile
)
{
this
.
placementJsonFile
=
placementJsonFile
;
}
public
void
setLatLeft
(
double
latLeft
)
{
this
.
latLeft
=
latLeft
;
}
public
void
setLatRight
(
double
latRight
)
{
this
.
latRight
=
latRight
;
}
public
void
setLonLeft
(
double
lonLeft
)
{
this
.
lonLeft
=
lonLeft
;
}
public
void
setLonRight
(
double
lonRight
)
{
this
.
lonRight
=
lonRight
;
}
}
src/de/tud/kom/p2psim/impl/topology/movement/modularosm/transition/FixedAssignmentStrategy.java
0 → 100644
View file @
cffdfb8d
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
package
de.tud.kom.p2psim.impl.topology.movement.modularosm.transition
;
import
java.util.HashMap
;
import
java.util.LinkedHashMap
;
import
java.util.LinkedList
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Random
;
import
de.tud.kom.p2psim.api.common.SimHost
;
import
de.tud.kom.p2psim.api.common.SimHostComponent
;
import
de.tud.kom.p2psim.api.topology.movement.MovementSupported
;
import
de.tud.kom.p2psim.impl.simengine.Simulator
;
import
de.tud.kom.p2psim.impl.topology.PositionVector
;
import
de.tud.kom.p2psim.impl.topology.movement.CsvMovement
;
import
de.tud.kom.p2psim.impl.topology.movement.modularosm.ModularMovementModel
;
import
de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.AttractionPoint
;
import
de.tudarmstadt.maki.simonstrator.api.Randoms
;
/**
* A {@link TransitionStrategy} for a case in which nodes (
* {@link MovementSupported}) are affiliated to an {@link AttractionPoint} only
* in the beginning. No further transition will take place.
*
* @author Nils Richerzhagen
* @version 1.0, 04.08.2014
*/
public
class
FixedAssignmentStrategy
implements
ITransitionStrategy
{
private
Random
rnd
;
private
List
<
MovementSupported
>
comps
=
new
LinkedList
<
MovementSupported
>();
private
List
<
AttractionPoint
>
aPoints
=
new
LinkedList
<
AttractionPoint
>();
private
Map
<
MovementSupported
,
AttractionPoint
>
assignments
=
new
LinkedHashMap
<
MovementSupported
,
AttractionPoint
>();
private
Map
<
MovementSupported
,
SimHost
>
mappingMSHost
=
new
LinkedHashMap
<
MovementSupported
,
SimHost
>();
private
Map
<
SimHost
,
MovementSupported
>
mappingHostMS
=
new
LinkedHashMap
<
SimHost
,
MovementSupported
>();
private
Map
<
String
,
AttractionPoint
>
mappingGroupIdAP
=
new
LinkedHashMap
<
String
,
AttractionPoint
>();
private
Map
<
AttractionPoint
,
String
>
mappingAPGroupId
=
new
LinkedHashMap
<
AttractionPoint
,
String
>();
public
FixedAssignmentStrategy
()
{
rnd
=
Randoms
.
getRandom
(
FixedAssignmentStrategy
.
class
);
}
@Override
public
Map
<
MovementSupported
,
AttractionPoint
>
getAssignments
()
{
return
new
HashMap
<
MovementSupported
,
AttractionPoint
>(
assignments
);
}
@Override
public
void
setAttractionPoints
(
List
<
AttractionPoint
>
attractionPoints
)
{
aPoints
.
addAll
(
attractionPoints
);
}
@Override
public
void
addComponent
(
MovementSupported
ms
)
{
comps
.
add
(
ms
);
mappingHost
(
ms
);
// No assignments been done before.
if
(
assignments
.
isEmpty
())
{
AttractionPoint
aPoint
=
aPoints
.
iterator
().
next
();
assignments
.
put
(
ms
,
aPoint
);
mappingGroupId
(
ms
,
aPoint
);
setStartPosition
(
ms
,
aPoint
.
getRealPosition
());
}
// GroupId is not mapped.
else
if
(!
mappingGroupIdAP
.
containsKey
(
mappingMSHost
.
get
(
ms
)
.
getProperties
().
getGroupID
()))
{
for
(
AttractionPoint
actAP
:
aPoints
)
{
if
(!
mappingAPGroupId
.
containsKey
(
actAP
))
{
assignments
.
put
(
ms
,
actAP
);
mappingGroupId
(
ms
,
actAP
);
setStartPosition
(
ms
,
actAP
.
getRealPosition
());
break
;
}
}
}
// GroupId is already mapped.
else
if
(
mappingGroupIdAP
.
containsKey
(
mappingMSHost
.
get
(
ms
)
.
getProperties
().
getGroupID
()))
{
AttractionPoint
aPoint
=
mappingGroupIdAP
.
get
(
mappingMSHost
.
get
(
ms
)
.
getProperties
().
getGroupID
());
assignments
.
put
(
ms
,
aPoint
);
setStartPosition
(
ms
,
aPoint
.
getRealPosition
());
}
else
{
throw
new
Error
(
"Should not happen."
);
}
}
private
void
mappingHost
(
MovementSupported
ms
)
{
SimHostComponent
comp
=
(
SimHostComponent
)
ms
;
SimHost
host
=
comp
.
getHost
();
assert
host
!=
null
;
mappingHostMS
.
put
(
host
,
ms
);
mappingMSHost
.
put
(
ms
,
host
);
}
private
void
mappingGroupId
(
MovementSupported
ms
,
AttractionPoint
AP
)
{
SimHostComponent
comp
=
(
SimHostComponent
)
ms
;
SimHost
host
=
comp
.
getHost
();
assert
host
!=
null
;
mappingAPGroupId
.
put
(
AP
,
mappingMSHost
.
get
(
ms
).
getProperties
()
.
getGroupID
());
mappingGroupIdAP
.
put
(
mappingMSHost
.
get
(
ms
).
getProperties
().
getGroupID
(),
AP
);
}
private
void
setStartPosition
(
MovementSupported
ms
,
PositionVector
aPointReferencePosition
)
{
double
minJitter
=
50.0
;
double
maxJitter
=
100.0
;
PositionVector
nodePosition
=
ms
.
getRealPosition
();
nodePosition
.
replace
(
aPointReferencePosition
);
double
xJitter
=
(
rnd
.
nextDouble
()
*
(
maxJitter
-
minJitter
))
+
minJitter
;
double
yJitter
=
(
rnd
.
nextDouble
()
*
(
maxJitter
-
minJitter
))
+
minJitter
;
PositionVector
jitterVector
=
new
PositionVector
(
xJitter
,
yJitter
);
nodePosition
.
add
(
jitterVector
);
ms
.
positionChanged
();
}
/**
* Used by the MobilityModel (M1) of the {@link ModularMovementModel} to get
* the groupId of the affiliated nodes to that {@link AttractionPoint}. Once
* the groupId is known nodes can be set <b>offline</b> or <b>online</b>.
*
* @param attractionPoint
* @return
*/
public
String
getGroupIdOfAttractionPoint
(
AttractionPoint
attractionPoint
)
{
return
mappingAPGroupId
.
get
(
attractionPoint
);
}
}
src/de/tud/kom/p2psim/impl/topology/movement/modularosm/transition/ITransitionStrategy.java
0 → 100644
View file @
cffdfb8d
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
package
de.tud.kom.p2psim.impl.topology.movement.modularosm.transition
;
import
java.util.List
;
import
java.util.Map
;
import
de.tud.kom.p2psim.api.topology.movement.MovementSupported
;
import
de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.AttractionPoint
;
/**
* This is the interface for the Transition Strategy.<br>
*
* @author Martin Hellwig
* @version 1.0, 03.07.2015
*/
public
interface
ITransitionStrategy
{
/**
* Returns the assignments of the MovementSupported Objects to the
* AttractionPoints
*
* @return
*/
public
Map
<
MovementSupported
,
AttractionPoint
>
getAssignments
();
/**
* Should be called first, to add the Attraction Points for the assignment!
*
* @param attractionPoints
*/
public
void
setAttractionPoints
(
List
<
AttractionPoint
>
attractionPoints
);
/**
* Add the {@link MovementSupported} object and assign the MS to an
* {@link AttractionPoint}.
*
* @param ms
*/
public
void
addComponent
(
MovementSupported
ms
);
}
src/de/tud/kom/p2psim/impl/topology/movement/modularosm/transition/SocialTransitionStrategy.java
0 → 100644
View file @
cffdfb8d
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
package
de.tud.kom.p2psim.impl.topology.movement.modularosm.transition
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.Iterator
;
import
java.util.LinkedList
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map.Entry
;
import
java.util.Random
;
import
java.util.Set
;
import
java.util.Vector
;
import
de.tud.kom.p2psim.api.common.SimHost
;
import
de.tud.kom.p2psim.api.common.SimHostComponent
;
import
de.tud.kom.p2psim.api.scenario.ConfigurationException
;
import
de.tud.kom.p2psim.api.topology.movement.MovementSupported
;
import
de.tud.kom.p2psim.api.topology.social.SocialView
;
import
de.tud.kom.p2psim.impl.simengine.Simulator
;
import
de.tud.kom.p2psim.impl.topology.PositionVector
;
import
de.tud.kom.p2psim.impl.topology.Topology
;
import
de.tud.kom.p2psim.impl.topology.movement.modular.attraction.AttractionPoint
;
import
de.tudarmstadt.maki.simonstrator.api.Event
;
import
de.tudarmstadt.maki.simonstrator.api.EventHandler
;
import
de.tudarmstadt.maki.simonstrator.api.Randoms
;
/**
* This is a {@link TransitionStrategy} for the Social Case. It will be try to
* build groups based on the {@link SocialView} information. For this, it tries
* to assignment the given {@link MovementSupported} objects to the given
* {@link AttractionPoint}s. For the {@link SocialView}, it is required a
* {@link #socialId}, to find the right {@link SocialView}.
*
* <br>
*
* The Strategy has the parameter of {@link #socialFactor},
* {@link #minPauseTime} and {@link #maxPauseTime}. The socialFactor should be a
* value between 0 and 1. It gives the probability for a social based transition
* or the transition to a random {@link AttractionPoint}. If the social based
* transition is selected, then will be used a scoring to find the right
* {@link AttractionPoint}. For that, it will be used only the AttractionPoints,
* of the hosts, which are in the same SocialCluster or are SocialNeighbors. For
* this AttractionPoints it will be find the highest scoring, which is to found
* in {@link #score(MovementSupported, AttractionPoint, List, List, List, List)}
* .
*
* <br>
*
* After the finding of the next {@link AttractionPoint}, it will be scheduled
* an Event, with a delay between min- and maxPauseTime. After this delay, it
* will be tried to assign a new {@link AttractionPoint} like the described
* above.
*
*
* @author Christoph Muenker
* @version 1.0, 02.07.2013
*/
public
class
SocialTransitionStrategy
implements
TransitionStrategy
,
EventHandler
{
private
String
socialId
=
null
;
private
SocialView
socialView
;
private
List
<
MovementSupported
>
comps
=
new
Vector
<
MovementSupported
>();
private
List
<
AttractionPoint
>
aPoints
=
new
Vector
<
AttractionPoint
>();
private
Map
<
MovementSupported
,
AttractionPoint
>
assignments
=
new
HashMap
<
MovementSupported
,
AttractionPoint
>();
private
Map
<
MovementSupported
,
Set
<
AttractionPoint
>>
favoritePlaces
=
new
HashMap
<
MovementSupported
,
Set
<
AttractionPoint
>>();
private
Map
<
MovementSupported
,
SimHost
>
mapMsHost
=
new
HashMap
<
MovementSupported
,
SimHost
>();
private
Map
<
SimHost
,
MovementSupported
>
mapHostMs
=
new
HashMap
<
SimHost
,
MovementSupported
>();
private
double
minPauseTime
=
Simulator
.
MINUTE_UNIT
*
0.5
;
private
double
maxPauseTime
=
Simulator
.
MINUTE_UNIT
*
100
;
private
double
socialFactor
=
0.8
;
private
long
numberOfFavoritePlaces
=
4
;
private
Random
rand
;
private
PositionVector
worldDimension
;
private
boolean
init
=
false
;
public
SocialTransitionStrategy
()
{
this
.
rand
=
Randoms
.
getRandom
(
SocialTransitionStrategy
.
class
);
}
private
void
init
()
{
if
(!
init
)
{
if
(
socialId
==
null
)
{
throw
new
ConfigurationException
(
"SocialId is not set, to find the needed SocialView!"
);
}
socialView
=
Topology
.
getTopology
().
getSocialView
(
socialId
);
if
(
socialView
==
null
)
{
throw
new
ConfigurationException
(
"Cannot find the right socialView. Is the socialId correct?"
);
}
if
(
minPauseTime
>
maxPauseTime
)
{
throw
new
ConfigurationException
(
"MinPauseTime should be smaller then maxPauseTime."
);
}
worldDimension
=
Topology
.
getWorldDimension
();
init
=
true
;
}
}
public
void
setSocialId
(
String
socialId
)
{
this
.
socialId
=
socialId
;
}
@Override
public
Map
<
MovementSupported
,
AttractionPoint
>
getAssignments
()
{
return
new
HashMap
<
MovementSupported
,
AttractionPoint
>(
assignments
);
}
@Override
public
void
setAttractionPoints
(
List
<
AttractionPoint
>
attractionPoints
)
{
init
();
aPoints
.
addAll
(
attractionPoints
);
}
@Override
public
void
addComponent
(
MovementSupported
ms
)
{
comps
.
add
(
ms
);
mappingHost
(
ms
);
// assign the ms to an attractionPoint, which is near to the ms
// position.
// TODO: needed? We do Transition as next, and this will delete the
// assignment..
AttractionPoint
nearest
=
aPoints
.
iterator
().
next
();
for
(
AttractionPoint
aPoint
:
aPoints
)
{
if
(
nearest
.
getRealPosition
().
getDistance
(
ms
.
getRealPosition
())
>
aPoint
.
getRealPosition
().
getDistance
(
ms
.
getRealPosition
()))
{
nearest
=
aPoint
;
}
}
assignments
.
put
(
ms
,
nearest
);
assignFavoritePlaces
(
ms
);
doTransition
(
ms
);
}
public
void
doTransition
(
MovementSupported
ms
)
{
List
<
AttractionPoint
>
apFavorites
=
getFavoritePlaces
(
ms
);
List
<
AttractionPoint
>
apFriends
=
getFriendsPlaces
(
ms
);
List
<
AttractionPoint
>
apClusters
=
getClusterPlaces
(
ms
);
List
<
AttractionPoint
>
apRandom
=
getRandomPlaces
(
ms
,
(
int
)
Math
.
max
(
aPoints
.
size
()
*
0.2
,
5
));
AttractionPoint
ap
=
null
;
if
(
rand
.
nextDouble
()
<
socialFactor
)
{
ap
=
findHighestScore
(
ms
,
apFavorites
,
apFriends
,
apClusters
,
apRandom
);
}
else
{
List
<
AttractionPoint
>
aps
=
new
ArrayList
<
AttractionPoint
>();
aps
.
addAll
(
apRandom
);
aps
.
addAll
(
apFavorites
);
ap
=
aps
.
get
(
rand
.
nextInt
(
apRandom
.
size
()));
}
assignments
.
put
(
ms
,
ap
);
Event
.
scheduleWithDelay
(
getPauseTime
(),
this
,
ms
,
0
);
}
private
AttractionPoint
findHighestScore
(
MovementSupported
ms
,
List
<
AttractionPoint
>
apFavorites
,
List
<
AttractionPoint
>
apFriends
,
List
<
AttractionPoint
>
apClusters
,
List
<
AttractionPoint
>
apRandom
)
{
Set
<
AttractionPoint
>
aps
=
new
HashSet
<
AttractionPoint
>();
aps
.
addAll
(
apFavorites
);
aps
.
addAll
(
apFriends
);
aps
.
addAll
(
apClusters
);
aps
.
addAll
(
apRandom
);
double
maxScore
=
0
;
AttractionPoint
maxAp
=
null
;
for
(
AttractionPoint
ap
:
aps
)
{
double
score
=
score
(
ms
,
ap
,
apFavorites
,
apFriends
,
apClusters
,
apRandom
);
// System.out.println(score);
if
(
score
>
maxScore
)
{
maxScore
=
score
;
maxAp
=
ap
;
}
}
return
maxAp
;
}
/**
* Score the given AttractionPoint for the given {@link MovementSupported}. <br>
* (clusterScore/#NodesInAp + friendsScore + 1/#NodesInAp) * socialFactor +
* (distanceScore + penalty) + (1-socialFactor) <br>
*
* clusterScore = 1 if one is in the same cluster in this AP<br>
* friendsScore = 1 if one friend is in the same AP<br>
* penalty = -1 if AP the actually AP is <br>
* distance = 1 - (distance / maxDistance)
*
* @param ms
* @param ap
* @param apFavorites
* @param apFriends
* @param apClusters
* @param apRandom
* @return
*/
private
double
score
(
MovementSupported
ms
,
AttractionPoint
ap
,
List
<
AttractionPoint
>
apFavorites
,
List
<
AttractionPoint
>
apFriends
,
List
<
AttractionPoint
>
apClusters
,
List
<
AttractionPoint
>
apRandom
)
{
double
distance
=
ms
.
getRealPosition
()
.
getDistance
(
ap
.
getRealPosition
());
double
distanceScore
=
1
-
(
distance
/
worldDimension
.
getLength
());
double
clusterScore
=
0
;
double
friendsScore
=
0
;
if
(
apClusters
.
contains
(
ap
))
{
if
(
occurence
(
ap
,
apClusters
)
>
3
)
{
// occurence give the number of other peers in this AP
clusterScore
=
1.0
/
(
occurence
(
ap
,
apClusters
)
-
1
);
}
else
{
clusterScore
=
1.0
;
}
}
if
(
apFriends
.
contains
(
ap
))
{
if
(
occurence
(
ap
,
apFriends
)
>
3
)
{
// occurence give the number of other peers in this AP
friendsScore
=
1.0
/
(
occurence
(
ap
,
apFriends
)
-
1
);
}
else
{
friendsScore
=
1.0
;
}
}
// penalty for distance
double
penalty
=
0
;
if
(
ap
.
equals
(
assignments
.
get
(
ms
)))
{
penalty
=
-
1
;
}
return
(
clusterScore
/
assignedToAp
(
ap
)
+
friendsScore
+
1.0
/
assignedToAp
(
ap
))
*
socialFactor
+
(
distanceScore
+
penalty
)
*
(
1
-
socialFactor
);
}
// counts the number of the AttractionPoint in the list
private
int
occurence
(
AttractionPoint
ap
,
List
<
AttractionPoint
>
aps
)
{
int
i
=
0
;
for
(
AttractionPoint
a
:
aps
)
{
if
(
a
.
equals
(
ap
))
{
i
++;
}
}
return
i
;
}
private
int
assignedToAp
(
AttractionPoint
ap
)
{
int
i
=
1
;
for
(
Entry
<
MovementSupported
,
AttractionPoint
>
entry
:
assignments
.
entrySet
())
{
if
(
entry
.
getValue
().
equals
(
ap
))
{
i
++;
}
}
return
i
;
}
private
List
<
AttractionPoint
>
getRandomPlaces
(
MovementSupported
ms
,
int
number
)
{
Collections
.
shuffle
(
aPoints
);
List
<
AttractionPoint
>
result
=
new
Vector
<
AttractionPoint
>();
Iterator
<
AttractionPoint
>
iAP
=
aPoints
.
iterator
();
for
(
int
i
=
0
;
i
<
number
&&
iAP
.
hasNext
();
i
++)
{
result
.
add
(
iAP
.
next
());
}
return
result
;
}
private
List
<
AttractionPoint
>
getClusterPlaces
(
MovementSupported
ms
)
{
List
<
AttractionPoint
>
result
=
new
Vector
<
AttractionPoint
>();
SimHost
msHost
=
mapMsHost
.
get
(
ms
);
for
(
SimHost
host
:
socialView
.
getCluster
(
msHost
))
{
MovementSupported
temp
=
mapHostMs
.
get
(
host
);
if
(
assignments
.
get
(
temp
)
!=
null
)
{
result
.
add
(
assignments
.
get
(
temp
));
}
}
return
result
;
}
private
List
<
AttractionPoint
>
getFriendsPlaces
(
MovementSupported
ms
)
{
List
<
AttractionPoint
>
result
=
new
Vector
<
AttractionPoint
>();
SimHost
msHost
=
mapMsHost
.
get
(
ms
);
for
(
SimHost
host
:
socialView
.
getNeighbors
(
msHost
))
{
MovementSupported
temp
=
mapHostMs
.
get
(
host
);
if
(
assignments
.
get
(
temp
)
!=
null
)
{
result
.
add
(
assignments
.
get
(
temp
));
}
}
return
result
;
}
private
List
<
AttractionPoint
>
getFavoritePlaces
(
MovementSupported
ms
)
{
return
new
Vector
<
AttractionPoint
>(
favoritePlaces
.
get
(
ms
));
}
private
AttractionPoint
getRandomPlace
(
MovementSupported
ms
)
{
Collections
.
shuffle
(
aPoints
);
return
aPoints
.
iterator
().
next
();
}
private
AttractionPoint
getNearestPlace
(
MovementSupported
ms
,
List
<
AttractionPoint
>
aps
)
{
if
(
aps
.
isEmpty
())
{
return
null
;
}
AttractionPoint
nearest
=
aps
.
iterator
().
next
();
for
(
AttractionPoint
aPoint
:
aps
)
{
if
(
nearest
.
getRealPosition
().
getDistance
(
ms
.
getRealPosition
())
>
aPoint
.
getRealPosition
().
getDistance
(
ms
.
getRealPosition
())
&&
!
assignments
.
get
(
ms
).
equals
(
aPoint
))
{
nearest
=
aPoint
;
}
}
return
nearest
;
}
private
AttractionPoint
getNearestFavoritePlace
(
MovementSupported
ms
)
{
Set
<
AttractionPoint
>
fps
=
favoritePlaces
.
get
(
ms
);
AttractionPoint
nearest
=
fps
.
iterator
().
next
();
for
(
AttractionPoint
aPoint
:
fps
)
{
if
(
nearest
.
getRealPosition
().
getDistance
(
ms
.
getRealPosition
())
>
aPoint
.
getRealPosition
().
getDistance
(
ms
.
getRealPosition
())
&&
!
assignments
.
get
(
ms
).
equals
(
aPoint
))
{
nearest
=
aPoint
;
}
}
return
nearest
;
}
private
void
assignFavoritePlaces
(
MovementSupported
ms
)
{
Set
<
AttractionPoint
>
msFavoritePlaces
=
new
HashSet
<
AttractionPoint
>();
LinkedList
<
AttractionPoint
>
temp
=
new
LinkedList
<
AttractionPoint
>(
aPoints
);
Collections
.
shuffle
(
temp
,
rand
);
for
(
int
i
=
0
;
i
<
numberOfFavoritePlaces
;
i
++)
{
if
(!
temp
.
isEmpty
())
{
msFavoritePlaces
.
add
(
temp
.
removeFirst
());
}
}
favoritePlaces
.
put
(
ms
,
msFavoritePlaces
);
}
private
void
mappingHost
(
MovementSupported
ms
)
{
SimHostComponent
comp
=
(
SimHostComponent
)
ms
;
SimHost
host
=
comp
.
getHost
();
assert
host
!=
null
;
mapHostMs
.
put
(
host
,
ms
);
mapMsHost
.
put
(
ms
,
host
);
}
protected
long
getPauseTime
()
{
return
(
long
)
((
rand
.
nextDouble
()
*
(
maxPauseTime
-
minPauseTime
))
+
minPauseTime
);
}
public
void
setMinPauseTime
(
long
minPauseTime
)
{
if
(
minPauseTime
<
0
)
{
throw
new
ConfigurationException
(
"MinPauseTime should be bigger then 0!"
);
}
this
.
minPauseTime
=
minPauseTime
;
}
public
void
setMaxPauseTime
(
long
maxPauseTime
)
{
if
(
maxPauseTime
<
0
)
{
throw
new
ConfigurationException
(
"MaxPauseTime should be bigger then 0!"
);
}
this
.
maxPauseTime
=
maxPauseTime
;
}
@Override
public
void
eventOccurred
(
Object
se
,
int
type
)
{
doTransition
((
MovementSupported
)
se
);
}
public
void
setSocialFactor
(
double
socialFactor
)
{
if
(
socialFactor
<
0
||
socialFactor
>
1
)
{
throw
new
ConfigurationException
(
"socialFactor should be between 0 and 1!"
);
}
this
.
socialFactor
=
socialFactor
;
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment