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
66d66180
Commit
66d66180
authored
Jul 04, 2015
by
Martin Hellwig
Browse files
First version of osm-data integration
parent
ecd21692
Changes
8
Hide whitespace changes
Inline
Side-by-side
src/de/tud/kom/p2psim/impl/topology/movement/modularosm/ModularMovementModel.java
0 → 100644
View file @
66d66180
/*
* 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 @
66d66180
/*
* 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 @
66d66180
/*
* 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 @
66d66180
/*
* 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 @
66d66180
/*
* 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 @
66d66180
/*
* 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 @
66d66180
/*
* 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 @
66d66180
/*
* 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