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
0c80da45
Commit
0c80da45
authored
Nov 06, 2015
by
Björn Richerzhagen
Browse files
Removed deprecated `Position`-Interface
parent
db566de9
Changes
64
Hide whitespace changes
Inline
Side-by-side
src/de/tud/kom/p2psim/impl/network/modular/st/positioning/AppJobberPositioning.java
View file @
0c80da45
...
...
@@ -9,12 +9,12 @@ import java.io.UnsupportedEncodingException;
import
java.util.ArrayList
;
import
java.util.Random
;
import
de.tud.kom.p2psim.api.common.Position
;
import
de.tud.kom.p2psim.api.common.SimHost
;
import
de.tud.kom.p2psim.impl.network.modular.db.NetMeasurementDB
;
import
de.tud.kom.p2psim.impl.network.modular.st.PositioningStrategy
;
import
de.tud.kom.p2psim.impl.util.positioning.GeoSpherePosition
;
import
de.tudarmstadt.maki.simonstrator.api.Randoms
;
import
de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location
;
public
class
AppJobberPositioning
implements
PositioningStrategy
{
...
...
@@ -25,7 +25,7 @@ public class AppJobberPositioning implements PositioningStrategy {
private
Random
rnd
=
Randoms
.
getRandom
(
AppJobberPositioning
.
class
);
@Override
public
Posi
tion
getPosition
(
public
Loca
tion
getPosition
(
SimHost
host
,
NetMeasurementDB
db
,
NetMeasurementDB
.
Host
hostMeta
)
{
...
...
src/de/tud/kom/p2psim/impl/network/modular/st/positioning/GNPPositioning.java
View file @
0c80da45
...
...
@@ -24,12 +24,12 @@ package de.tud.kom.p2psim.impl.network.modular.st.positioning;
import
java.util.List
;
import
java.util.Vector
;
import
de.tud.kom.p2psim.api.common.Position
;
import
de.tud.kom.p2psim.api.common.SimHost
;
import
de.tud.kom.p2psim.impl.network.modular.common.GNPToolkit
;
import
de.tud.kom.p2psim.impl.network.modular.db.NetMeasurementDB
;
import
de.tud.kom.p2psim.impl.network.modular.st.PositioningStrategy
;
import
de.tud.kom.p2psim.impl.topology.PositionVector
;
import
de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location
;
/**
* Applies the (virtual) GNP position as the host's position
...
...
@@ -40,7 +40,7 @@ import de.tud.kom.p2psim.impl.topology.PositionVector;
public
class
GNPPositioning
implements
PositioningStrategy
{
@Override
public
Posi
tion
getPosition
(
SimHost
host
,
NetMeasurementDB
db
,
public
Loca
tion
getPosition
(
SimHost
host
,
NetMeasurementDB
db
,
NetMeasurementDB
.
Host
hostMeta
)
{
if
(
hostMeta
==
null
)
...
...
@@ -51,7 +51,7 @@ public class GNPPositioning implements PositioningStrategy {
}
public
static
class
GNPPosition
extends
PositionVector
implements
Position
{
public
static
class
GNPPosition
extends
PositionVector
{
private
List
<
Double
>
coords
;
...
...
@@ -63,7 +63,7 @@ public class GNPPositioning implements PositioningStrategy {
}
@Override
public
double
getD
istance
(
Posi
tion
netPosition
)
{
public
double
d
istance
To
(
Loca
tion
netPosition
)
{
if
(!(
netPosition
instanceof
PositionVector
))
throw
new
AssertionError
(
"Can not calculate distances between different position classes: "
...
...
src/de/tud/kom/p2psim/impl/network/modular/st/positioning/GeographicalPositioning.java
View file @
0c80da45
...
...
@@ -21,12 +21,12 @@
package
de.tud.kom.p2psim.impl.network.modular.st.positioning
;
import
de.tud.kom.p2psim.api.common.Position
;
import
de.tud.kom.p2psim.api.common.SimHost
;
import
de.tud.kom.p2psim.impl.network.modular.common.GeoToolkit
;
import
de.tud.kom.p2psim.impl.network.modular.db.NetMeasurementDB
;
import
de.tud.kom.p2psim.impl.network.modular.st.PositioningStrategy
;
import
de.tud.kom.p2psim.impl.topology.PositionVector
;
import
de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location
;
/**
* Applies a geographical position as defined by the GeoIP project.
...
...
@@ -49,8 +49,7 @@ public class GeographicalPositioning implements PositioningStrategy {
}
public
class
GeographicalPosition
extends
PositionVector
implements
Position
{
public
class
GeographicalPosition
extends
PositionVector
{
private
double
latitude
;
private
double
longitude
;
...
...
@@ -70,7 +69,7 @@ public class GeographicalPositioning implements PositioningStrategy {
*
*/
@Override
public
double
getD
istance
(
Posi
tion
netPosition
)
{
public
double
d
istance
To
(
Loca
tion
netPosition
)
{
if
(!(
netPosition
instanceof
PositionVector
))
throw
new
AssertionError
(
"Can not calculate the distance between two different position classes: "
...
...
@@ -80,12 +79,6 @@ public class GeographicalPositioning implements PositioningStrategy {
return
GeoToolkit
.
getDistance
(
this
.
latitude
,
this
.
longitude
,
other
.
getEntry
(
1
),
other
.
getEntry
(
0
));
}
@Override
public
double
getAngle
(
Position
target
)
{
throw
new
AssertionError
(
"getAngle is not defined for this Position-Type"
);
}
@Override
public
int
getTransmissionSize
()
{
...
...
src/de/tud/kom/p2psim/impl/network/modular/st/positioning/PlanePositioning.java
View file @
0c80da45
...
...
@@ -20,11 +20,11 @@
package
de.tud.kom.p2psim.impl.network.modular.st.positioning
;
import
de.tud.kom.p2psim.api.common.Position
;
import
de.tud.kom.p2psim.api.common.SimHost
;
import
de.tud.kom.p2psim.impl.network.modular.db.NetMeasurementDB
;
import
de.tud.kom.p2psim.impl.network.modular.st.PositioningStrategy
;
import
de.tud.kom.p2psim.impl.topology.placement.PositionDistribution
;
import
de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location
;
/**
* Positioning on a 2D-Plane, used for all Movement-Enabled Routing-Scenarios
...
...
@@ -42,7 +42,7 @@ public class PlanePositioning implements PositioningStrategy {
}
@Override
public
Posi
tion
getPosition
(
public
Loca
tion
getPosition
(
SimHost
host
,
NetMeasurementDB
db
,
de
.
tud
.
kom
.
p2psim
.
impl
.
network
.
modular
.
db
.
NetMeasurementDB
.
Host
hostMeta
)
{
...
...
src/de/tud/kom/p2psim/impl/network/modular/st/positioning/SimpleEuclidianPositioning.java
View file @
0c80da45
...
...
@@ -22,13 +22,13 @@ package de.tud.kom.p2psim.impl.network.modular.st.positioning;
import
java.util.Random
;
import
de.tud.kom.p2psim.api.common.Position
;
import
de.tud.kom.p2psim.api.common.SimHost
;
import
de.tud.kom.p2psim.impl.network.modular.db.NetMeasurementDB
;
import
de.tud.kom.p2psim.impl.network.modular.st.PositioningStrategy
;
import
de.tud.kom.p2psim.impl.network.simple.SimpleSubnet
;
import
de.tud.kom.p2psim.impl.topology.PositionVector
;
import
de.tudarmstadt.maki.simonstrator.api.Randoms
;
import
de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location
;
/**
* Implementation of the <code>NetPosition</code> interface representing a two
...
...
@@ -48,7 +48,7 @@ public class SimpleEuclidianPositioning implements PositioningStrategy {
* @see de.tud.kom.p2psim.impl.network.modular.st.PositioningStrategy#getPosition(de.tud.kom.p2psim.api.common.Host, de.tud.kom.p2psim.impl.network.modular.db.NetMeasurementDB, de.tud.kom.p2psim.impl.network.modular.db.NetMeasurementDB.Host)
*/
@Override
public
Posi
tion
getPosition
(
public
Loca
tion
getPosition
(
SimHost
host
,
NetMeasurementDB
db
,
NetMeasurementDB
.
Host
hostMeta
)
{
...
...
@@ -59,7 +59,7 @@ public class SimpleEuclidianPositioning implements PositioningStrategy {
}
public
class
SimpleEuclidianPosition
extends
PositionVector
implements
Position
{
public
class
SimpleEuclidianPosition
extends
PositionVector
{
private
double
xPos
;
...
...
@@ -93,7 +93,8 @@ public class SimpleEuclidianPositioning implements PositioningStrategy {
* de.tud.kom.p2psim.api.network.NetPosition#getDistance(de.tud.kom.
* p2psim .api.network.NetPosition)
*/
public
double
getDistance
(
Position
ep
)
{
@Override
public
double
distanceTo
(
Location
ep
)
{
double
xDiff
=
0
;
double
yDiff
=
0
;
SimpleEuclidianPosition
point
=
(
SimpleEuclidianPosition
)
ep
;
...
...
@@ -118,11 +119,6 @@ public class SimpleEuclidianPositioning implements PositioningStrategy {
return
Math
.
pow
(
Math
.
pow
(
xDiff
,
2
)
+
Math
.
pow
(
yDiff
,
2
),
0.5
);
}
@Override
public
double
getAngle
(
Position
target
)
{
throw
new
AssertionError
(
"getAngle is not defined for this Position-Type"
);
}
@Override
public
int
getTransmissionSize
()
{
return
16
;
// 2 * double
...
...
src/de/tud/kom/p2psim/impl/network/modular/st/positioning/TorusPositioning.java
View file @
0c80da45
...
...
@@ -23,12 +23,12 @@ package de.tud.kom.p2psim.impl.network.modular.st.positioning;
import
java.util.Random
;
import
de.tud.kom.p2psim.api.common.Position
;
import
de.tud.kom.p2psim.api.common.SimHost
;
import
de.tud.kom.p2psim.impl.network.modular.db.NetMeasurementDB
;
import
de.tud.kom.p2psim.impl.network.modular.st.PositioningStrategy
;
import
de.tud.kom.p2psim.impl.topology.PositionVector
;
import
de.tudarmstadt.maki.simonstrator.api.Randoms
;
import
de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location
;
/**
* Applies a uniformly-distributed random position on a 2-or multi-dimensional
...
...
@@ -48,7 +48,7 @@ public class TorusPositioning implements PositioningStrategy {
Random
rand
=
Randoms
.
getRandom
(
TorusPosition
.
class
);
@Override
public
Posi
tion
getPosition
(
SimHost
host
,
NetMeasurementDB
db
,
public
Loca
tion
getPosition
(
SimHost
host
,
NetMeasurementDB
db
,
NetMeasurementDB
.
Host
hostMeta
)
{
double
[]
rawPos
=
getRawTorusPositionFor
(
host
);
...
...
@@ -93,7 +93,7 @@ public class TorusPositioning implements PositioningStrategy {
this
.
halfTorusDimensionSize
=
torusDimensionSize
*
0.5d
;
}
public
class
TorusPosition
extends
PositionVector
implements
Position
{
public
class
TorusPosition
extends
PositionVector
{
TorusPosition
(
double
[]
rawPos
)
{
...
...
@@ -101,7 +101,7 @@ public class TorusPositioning implements PositioningStrategy {
}
@Override
public
double
getD
istance
(
Posi
tion
netPosition
)
{
public
double
d
istance
To
(
Loca
tion
netPosition
)
{
if
(!(
netPosition
instanceof
PositionVector
))
throw
new
AssertionError
(
"Can not calculate distances between different position classes: "
...
...
src/de/tud/kom/p2psim/impl/network/routed/RoutedNetLayer.java
View file @
0c80da45
...
...
@@ -28,7 +28,6 @@ import java.util.Map;
import
de.tud.kom.p2psim.api.analyzer.ConnectivityAnalyzer
;
import
de.tud.kom.p2psim.api.analyzer.MessageAnalyzer.Reason
;
import
de.tud.kom.p2psim.api.analyzer.NetlayerAnalyzer
;
import
de.tud.kom.p2psim.api.common.Position
;
import
de.tud.kom.p2psim.api.common.SimHost
;
import
de.tud.kom.p2psim.api.common.SimHostComponent
;
import
de.tud.kom.p2psim.api.linklayer.LinkLayer
;
...
...
@@ -57,6 +56,7 @@ import de.tudarmstadt.maki.simonstrator.api.component.network.Bandwidth;
import
de.tudarmstadt.maki.simonstrator.api.component.network.NetID
;
import
de.tudarmstadt.maki.simonstrator.api.component.network.NetInterface
;
import
de.tudarmstadt.maki.simonstrator.api.component.network.NetworkComponent
;
import
de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location
;
import
de.tudarmstadt.maki.simonstrator.api.component.transport.ConnectivityListener
;
/**
...
...
@@ -548,7 +548,7 @@ public class RoutedNetLayer implements SimNetworkComponent, NetworkComponent,
}
@Override
public
Posi
tion
getNetPosition
()
{
public
Loca
tion
getNetPosition
()
{
return
host
.
getTopologyComponent
().
getRealPosition
();
}
...
...
src/de/tud/kom/p2psim/impl/network/simple/SimpleNetLayer.java
View file @
0c80da45
...
...
@@ -19,11 +19,10 @@
*/
package
de.tud.kom.p2psim.impl.network.simple
;
package
de.tud.kom.p2psim.impl.network.simple
;
import
de.tud.kom.p2psim.api.analyzer.MessageAnalyzer.Reason
;
import
de.tud.kom.p2psim.api.common.Position
;
import
de.tud.kom.p2psim.api.common.SimHost
;
import
de.tud.kom.p2psim.api.network.BandwidthImpl
;
import
de.tud.kom.p2psim.api.network.NetMessage
;
...
...
@@ -34,105 +33,106 @@ import de.tud.kom.p2psim.impl.transport.AbstractTransMessage;
import
de.tudarmstadt.maki.simonstrator.api.Message
;
import
de.tudarmstadt.maki.simonstrator.api.Monitor
;
import
de.tudarmstadt.maki.simonstrator.api.component.network.NetID
;
public
class
SimpleNetLayer
extends
AbstractNetLayer
{
private
double
currentDownBandwidth
;
private
double
currentUpBandwidth
;
private
final
SimpleSubnet
subNet
;
public
SimpleNetLayer
(
SimHost
host
,
SimpleSubnet
subNet
,
SimpleNetID
netID
,
Position
netPosition
,
BandwidthImpl
bandwidth
)
{
super
(
host
,
netID
,
bandwidth
,
netPosition
,
null
);
this
.
subNet
=
subNet
;
subNet
.
registerNetLayer
(
this
);
}
public
boolean
isSupported
(
TransProtocol
transProtocol
)
{
if
(
transProtocol
.
equals
(
TransProtocol
.
UDP
))
return
true
;
else
return
false
;
}
public
void
send
(
Message
msg
,
NetID
receiver
,
NetProtocol
netProtocol
)
{
// outer if-else-block is used to avoid sending although the host is
// offline
if
(
this
.
isOnline
())
{
TransProtocol
usedTransProtocol
=
((
AbstractTransMessage
)
msg
)
.
getProtocol
();
if
(
this
.
isSupported
(
usedTransProtocol
))
{
import
de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location
;
public
class
SimpleNetLayer
extends
AbstractNetLayer
{
private
double
currentDownBandwidth
;
private
double
currentUpBandwidth
;
private
final
SimpleSubnet
subNet
;
public
SimpleNetLayer
(
SimHost
host
,
SimpleSubnet
subNet
,
SimpleNetID
netID
,
Location
netPosition
,
BandwidthImpl
bandwidth
)
{
super
(
host
,
netID
,
bandwidth
,
netPosition
,
null
);
this
.
subNet
=
subNet
;
subNet
.
registerNetLayer
(
this
);
}
public
boolean
isSupported
(
TransProtocol
transProtocol
)
{
if
(
transProtocol
.
equals
(
TransProtocol
.
UDP
))
return
true
;
else
return
false
;
}
public
void
send
(
Message
msg
,
NetID
receiver
,
NetProtocol
netProtocol
)
{
// outer if-else-block is used to avoid sending although the host is
// offline
if
(
this
.
isOnline
())
{
TransProtocol
usedTransProtocol
=
((
AbstractTransMessage
)
msg
)
.
getProtocol
();
if
(
this
.
isSupported
(
usedTransProtocol
))
{
NetMessage
netMsg
=
new
SimpleNetMessage
(
msg
,
receiver
,
getLocalInetAddress
(),
getLocalInetAddress
(),
netProtocol
);
if
(
hasAnalyzer
)
{
netAnalyzerProxy
.
netMsgEvent
(
netMsg
,
getHost
(),
Reason
.
SEND
);
}
subNet
.
send
(
netMsg
);
}
else
{
throw
new
IllegalArgumentException
(
"Transport protocol "
+
usedTransProtocol
+
" not supported by this NetLayer implementation."
);
}
}
else
{
int
assignedMsgId
=
subNet
.
determineTransMsgNumber
(
msg
);
}
subNet
.
send
(
netMsg
);
}
else
{
throw
new
IllegalArgumentException
(
"Transport protocol "
+
usedTransProtocol
+
" not supported by this NetLayer implementation."
);
}
}
else
{
int
assignedMsgId
=
subNet
.
determineTransMsgNumber
(
msg
);
Monitor
.
log
(
SimpleNetLayer
.
class
,
Monitor
.
Level
.
DEBUG
,
"During send: Assigning MsgId "
+
assignedMsgId
+
" to dropped message"
);
((
AbstractTransMessage
)
msg
).
setCommId
(
assignedMsgId
);
"During send: Assigning MsgId "
+
assignedMsgId
+
" to dropped message"
);
((
AbstractTransMessage
)
msg
).
setCommId
(
assignedMsgId
);
NetMessage
netMsg
=
new
SimpleNetMessage
(
msg
,
receiver
,
getLocalInetAddress
(),
getLocalInetAddress
(),
netProtocol
);
if
(
hasAnalyzer
)
{
netAnalyzerProxy
.
netMsgEvent
(
netMsg
,
getHost
(),
Reason
.
DROP
);
}
}
}
@Override
public
String
toString
()
{
}
}
}
@Override
public
String
toString
()
{
return
"NetLayer(netID="
+
getLocalInetAddress
()
+
", "
+
(
isOffline
()
?
"online"
:
"offline"
)
+
")"
;
}
@Override
public
int
hashCode
()
{
final
int
PRIME
=
31
;
int
result
=
1
;
long
temp
;
temp
=
Double
.
doubleToLongBits
(
currentDownBandwidth
);
result
=
PRIME
*
result
+
(
int
)
(
temp
^
(
temp
>>>
32
));
temp
=
Double
.
doubleToLongBits
(
currentUpBandwidth
);
result
=
PRIME
*
result
+
(
int
)
(
temp
^
(
temp
>>>
32
));
result
=
PRIME
*
result
+
((
subNet
==
null
)
?
0
:
subNet
.
hashCode
());
return
result
;
}
@Override
public
boolean
equals
(
Object
obj
)
{
if
(
this
==
obj
)
return
true
;
if
(
obj
==
null
)
return
false
;
if
(
getClass
()
!=
obj
.
getClass
())
return
false
;
final
SimpleNetLayer
other
=
(
SimpleNetLayer
)
obj
;
if
(
Double
.
doubleToLongBits
(
currentDownBandwidth
)
!=
Double
.
doubleToLongBits
(
other
.
currentDownBandwidth
))
return
false
;
if
(
Double
.
doubleToLongBits
(
currentUpBandwidth
)
!=
Double
.
doubleToLongBits
(
other
.
currentUpBandwidth
))
return
false
;
if
(
subNet
==
null
)
{
if
(
other
.
subNet
!=
null
)
return
false
;
}
else
if
(!
subNet
.
equals
(
other
.
subNet
))
return
false
;
return
true
;
}
}
+
(
isOffline
()
?
"online"
:
"offline"
)
+
")"
;
}
@Override
public
int
hashCode
()
{
final
int
PRIME
=
31
;
int
result
=
1
;
long
temp
;
temp
=
Double
.
doubleToLongBits
(
currentDownBandwidth
);
result
=
PRIME
*
result
+
(
int
)
(
temp
^
(
temp
>>>
32
));
temp
=
Double
.
doubleToLongBits
(
currentUpBandwidth
);
result
=
PRIME
*
result
+
(
int
)
(
temp
^
(
temp
>>>
32
));
result
=
PRIME
*
result
+
((
subNet
==
null
)
?
0
:
subNet
.
hashCode
());
return
result
;
}
@Override
public
boolean
equals
(
Object
obj
)
{
if
(
this
==
obj
)
return
true
;
if
(
obj
==
null
)
return
false
;
if
(
getClass
()
!=
obj
.
getClass
())
return
false
;
final
SimpleNetLayer
other
=
(
SimpleNetLayer
)
obj
;
if
(
Double
.
doubleToLongBits
(
currentDownBandwidth
)
!=
Double
.
doubleToLongBits
(
other
.
currentDownBandwidth
))
return
false
;
if
(
Double
.
doubleToLongBits
(
currentUpBandwidth
)
!=
Double
.
doubleToLongBits
(
other
.
currentUpBandwidth
))
return
false
;
if
(
subNet
==
null
)
{
if
(
other
.
subNet
!=
null
)
return
false
;
}
else
if
(!
subNet
.
equals
(
other
.
subNet
))
return
false
;
return
true
;
}
}
src/de/tud/kom/p2psim/impl/topology/DefaultTopologyComponent.java
View file @
0c80da45
...
...
@@ -28,7 +28,6 @@ import java.util.Random;
import
java.util.Set
;
import
de.tud.kom.p2psim.api.common.HostProperties
;
import
de.tud.kom.p2psim.api.common.Position
;
import
de.tud.kom.p2psim.api.common.SimHost
;
import
de.tud.kom.p2psim.api.linklayer.mac.MacAddress
;
import
de.tud.kom.p2psim.api.linklayer.mac.MacLayer
;
...
...
@@ -178,7 +177,7 @@ public class DefaultTopologyComponent implements TopologyComponent {
@Override
public
void
positionChanged
()
{
for
(
PositionListener
listener
:
positionListener
)
{
if
(
listener
.
getPosition
().
getD
istance
(
position
)
<
listener
if
(
listener
.
getPosition
().
d
istance
To
(
position
)
<
listener
.
getRadius
())
{
listener
.
reachedPosition
();
}
...
...
@@ -212,14 +211,6 @@ public class DefaultTopologyComponent implements TopologyComponent {
return
position
;
}
@Override
public
Position
getPosition
(
PositionAccuracy
accuracy
)
{
/*
* TODO :)
*/
return
position
;
}
@Override
public
Topology
getTopology
()
{
return
topology
;
...
...
src/de/tud/kom/p2psim/impl/topology/PositionVector.java
View file @
0c80da45
...
...
@@ -25,7 +25,6 @@ import java.util.Arrays;
import
com.vividsolutions.jts.geom.Coordinate
;
import
de.tud.kom.p2psim.api.common.Position
;
import
de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location
;
/**
...
...
@@ -50,7 +49,7 @@ import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location;
* @author Bjoern Richerzhagen
* @version 1.0, 04/25/2011
*/
public
class
PositionVector
implements
Position
,
Location
{
public
class
PositionVector
implements
Location
{
private
int
dimensions
;
...
...
@@ -255,55 +254,6 @@ public class PositionVector implements Position, Location {
return
result
;
}
@Override
public
double
getDistance
(
Position
position
)
{
if
(
position
instanceof
PositionVector
)
{
PositionVector
pv
=
(
PositionVector
)
position
;
if
(
pv
.
getDimensions
()
==
getDimensions
())
{
double
dist
=
0
;
for
(
int
i
=
0
;
i
<
dimensions
;
i
++)
{
// faster as Math.pow
dist
+=
(
pv
.
getEntry
(
i
)
-
getEntry
(
i
))
*
(
pv
.
getEntry
(
i
)
-
getEntry
(
i
));
}
return
Math
.
sqrt
(
dist
);
}
else
{
throw
new
AssertionError
(
"Can not compute distance between Vectors of different length!"
);
}
}
else
{
throw
new
AssertionError
(
"Incompatible Types!"
);
}
}
@Override
public
double
getAngle
(
Position
target
)
{
if
(
target
instanceof
PositionVector
)
{
PositionVector
t
=
(
PositionVector
)
target
;
/*
* Calculates the angle using atan2 - this implies that the first
* two dimensions in your vector are the plane you are interested
* in.
*/
return
Math
.
atan2
(
t
.
getEntry
(
1
)
-
this
.
getEntry
(
1
),
t
.
getEntry
(
0
)
-
this
.
getEntry
(
0
));
}
else
{
throw
new
AssertionError
(
"Can only calculate an Angle on elements of type position vector"
);
}
}
@Override
public
Position
getTarget
(
double
distance
,
double
angle
)
{
/*
* The call to "clone" ensures correct typing
*/
PositionVector
newPos
=
clone
();
newPos
.
setEntry
(
0
,
newPos
.
getEntry
(
0
)
+
distance
*
Math
.
cos
(
angle
));
newPos
.
setEntry
(
1
,
newPos
.
getEntry
(
1
)
+
distance
*
Math
.
sin
(
angle
));
return
newPos
;
}
@Override
public
int
getTransmissionSize
()
{
return
getDimensions
()
*
8
;
...
...
@@ -496,13 +446,39 @@ public class PositionVector implements Position, Location {
@Override
public
double
distanceTo
(
Location
dest
)
{
assert
(
dest
instanceof
PositionVector
);
return
this
.
getDistance
((
PositionVector
)
dest
);
if
(
dest
instanceof
PositionVector
)
{
PositionVector
pv
=
(
PositionVector
)
dest
;
if
(
pv
.
getDimensions
()
==
getDimensions
())
{
double
dist
=
0
;
for
(
int
i
=
0
;
i
<
dimensions
;
i
++)
{
// faster as Math.pow
dist
+=
(
pv
.
getEntry
(
i
)
-
getEntry
(
i
))
*
(
pv
.
getEntry
(
i
)
-
getEntry
(
i
));
}
return
Math
.
sqrt
(
dist
);
}
else
{
throw
new
AssertionError
(
"Can not compute distance between Vectors of different length!"
);
}
}
else
{
throw
new
AssertionError
(
"Incompatible Types!"
);
}
}
@Override
public
float
bearingTo
(
Location
dest
)
{
assert
(
dest
instanceof
PositionVector
);
return
(
float
)
((
this
.
getAngle
((
PositionVector
)
dest
)
-
(
Math
.
PI
/
2
))
*
180
/
Math
.
PI
);
if
(
dest
instanceof
PositionVector
)
{
PositionVector
t
=
(
PositionVector
)
dest
;
/*
* Calculates the angle using atan2 - this implies that the first
* two dimensions in your vector are the plane you are interested
* in.
*/
return
(
float
)
Math
.
atan2
(
t
.
getEntry
(
1
)
-
this
.
getEntry
(
1
),
t
.
getEntry
(
0
)
-
this
.
getEntry
(
0
));
}
else
{
throw
new
AssertionError
(
"Can only calculate an Angle on elements of type position vector"
);
}
}
}
src/de/tud/kom/p2psim/impl/topology/TopologyFactory.java
View file @
0c80da45
...
...
@@ -39,6 +39,7 @@ import de.tud.kom.p2psim.impl.network.modular.db.NetMeasurementDB;
import
de.tud.kom.p2psim.impl.topology.movement.AbstractWaypointMovementModel
;
import
de.tud.kom.p2psim.impl.topology.placement.GNPPlacement
;
import
de.tud.kom.p2psim.impl.topology.views.latency.GNPLatency
;
import
de.tudarmstadt.maki.simonstrator.api.Binder
;
import
de.tudarmstadt.maki.simonstrator.api.Host
;
import
de.tudarmstadt.maki.simonstrator.api.Monitor
;
import
de.tudarmstadt.maki.simonstrator.api.Monitor.Level
;
...
...
@@ -88,7 +89,8 @@ public class TopologyFactory implements HostComponentFactory {
@XMLConfigurableConstructor
({
"worldX"
,
"worldY"
})
public
TopologyFactory
(
double
worldX
,
double
worldY
)
{
topo
=
new
DefaultTopology
(
new
PositionVector
(
worldX
,
worldY
));
de
.
tud
.
kom
.
p2psim
.
impl
.
topology
.
Topology
.
setTopology
(
topo
);
// Make the topology component available globally
Binder
.
registerComponent
(
topo
);
}
@Override
...
...
src/de/tud/kom/p2psim/impl/topology/movement/AbstractMovementModel.java
View file @
0c80da45
...
...
@@ -26,11 +26,12 @@ import java.util.List;
import
java.util.Random
;
import
java.util.Set
;
import
de.tud.kom.p2psim.api.topology.Topology
;
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.impl.topology.PositionVector
;
import
de.tud
.kom.p2psim.impl.topology.Topology
;
import
de.tud
armstadt.maki.simonstrator.api.Binder
;
import
de.tudarmstadt.maki.simonstrator.api.Event
;
import
de.tudarmstadt.maki.simonstrator.api.EventHandler
;
import
de.tudarmstadt.maki.simonstrator.api.Randoms
;
...
...
@@ -87,7 +88,8 @@ public abstract class AbstractMovementModel implements MovementModel {
@Override
public
void
addComponent
(
MovementSupported
component
)
{
if
(
worldDimensions
==
null
)
{
worldDimensions
=
Topology
.
getWorldDimension
();
worldDimensions
=
Binder
.
getComponentOrNull
(
Topology
.
class
)
.
getWorldDimensions
();
}
components
.
add
(
component
);
}
...
...
src/de/tud/kom/p2psim/impl/topology/movement/AbstractWaypointMovementModel.java
View file @
0c80da45
/*
* Copyright (c) 2005-2011 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
;
import
java.util.LinkedHashSet
;
import
java.util.LinkedList
;
import
java.util.List
;
import
java.util.Random
;
import
java.util.Set
;
import
java.util.WeakHashMap
;
import
com.google.common.eventbus.Subscribe
;
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.api.topology.waypoints.WaypointModel
;
import
de.tud.kom.p2psim.impl.scenario.simcfg2.annotations.After
;
import
de.tud.kom.p2psim.impl.scenario.simcfg2.annotations.Configure
;
import
de.tud.kom.p2psim.impl.topology.PositionVector
;
import
de.tud.kom.p2psim.impl.topology.events.ScaleWorldEvent
;
import
de.tud.kom.p2psim.impl.util.Either
;
import
de.tud.kom.p2psim.impl.util.geo.maps.MapLoader
;
import
de.tudarmstadt.maki.simonstrator.api.Event
;
import
de.tudarmstadt.maki.simonstrator.api.EventHandler
;
import
de.tudarmstadt.maki.simonstrator.api.Monitor
;
import
de.tudarmstadt.maki.simonstrator.api.Monitor.Level
;
import
de.tudarmstadt.maki.simonstrator.api.Randoms
;
import
de.tudarmstadt.maki.simonstrator.api.Time
;
/**
* The AbstractWaypointMovementModel can be used to implement movement models
* based on way point data. It uses an implementation of LocalMovementStrategy
* for the movement between selected way points.
*
* @author Fabio Zöllner
* @version 1.0, 27.03.2012
*/
public
abstract
class
AbstractWaypointMovementModel
implements
MovementModel
{
private
Set
<
MovementSupported
>
components
=
new
LinkedHashSet
<
MovementSupported
>();
protected
PositionVector
worldDimensions
;
protected
WeakHashMap
<
MovementSupported
,
PositionVector
>
destinations
;
protected
WeakHashMap
<
MovementSupported
,
Long
>
pauseTimes
;
protected
WeakHashMap
<
MovementSupported
,
Long
>
pauseInProgressTimes
;
private
List
<
MovementListener
>
listeners
;
protected
WaypointModel
waypointModel
;
protected
LocalMovementStrategy
localMovementStrategy
;
private
int
configurationCounter
=
100
;
private
long
timeBetweenMovement
=
1
*
Time
.
SECOND
;
private
double
speedLimit
=
1
;
private
double
unscaledSpeedLimit
=
speedLimit
;
private
Random
rnd
=
Randoms
.
getRandom
(
AbstractWaypointMovementModel
.
class
);
public
AbstractWaypointMovementModel
(
double
worldX
,
double
worldY
)
{
worldDimensions
=
new
PositionVector
(
worldX
,
worldY
);
listeners
=
new
LinkedList
<
MovementListener
>();
destinations
=
new
WeakHashMap
<
MovementSupported
,
PositionVector
>();
pauseTimes
=
new
WeakHashMap
<
MovementSupported
,
Long
>();
pauseInProgressTimes
=
new
WeakHashMap
<
MovementSupported
,
Long
>();
// Simulator.registerAtEventBus(this);
}
@Configure
()
@After
(
required
=
{
WaypointModel
.
class
,
MapLoader
.
class
})
public
boolean
configure
()
{
if
(
this
.
waypointModel
==
null
&&
configurationCounter
>
0
)
{
configurationCounter
--;
return
false
;
}
if
(
this
.
waypointModel
==
null
)
{
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
INFO
,
"No waypoint model has been configured. Thus the movement speed won't be adjusted for the scale of the waypoint model."
);
}
//
// if (waypointModel.getScaleFactor() != 1.0) {
// Simulator.postEvent(new ScaleWorldEvent(waypointModel
// .getScaleFactor()));
// }
return
true
;
}
@Subscribe
public
void
scaleWorld
(
ScaleWorldEvent
event
)
{
double
scaleFactor
=
event
.
getFactor
();
speedLimit
=
unscaledSpeedLimit
*
scaleFactor
;
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
INFO
,
"Movement speed of the local movement strategy has been adjusted for the waypoint model scale and is now "
+
speedLimit
+
"m/s"
);
}
/**
* Gets called periodically (after timeBetweenMoveOperations) or by an
* application and should be used to recalculate positions
*/
public
void
move
()
{
long
nrOfSteps
=
timeBetweenMovement
/
Time
.
SECOND
;
for
(
int
i
=
0
;
i
<
nrOfSteps
;
i
++)
{
step
();
}
notifyRoundEnd
();
}
private
void
step
()
{
Set
<
MovementSupported
>
comps
=
getComponents
();
for
(
MovementSupported
mcomp
:
comps
)
{
if
(!
mcomp
.
movementActive
())
{
continue
;
}
Long
currentPause
=
pauseInProgressTimes
.
get
(
mcomp
);
if
(
currentPause
!=
null
)
{
if
(
Time
.
getCurrentTime
()
>=
currentPause
)
{
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"Pause time ended..."
);
pauseInProgressTimes
.
remove
(
mcomp
);
}
else
continue
;
}
PositionVector
dst
=
getDestination
(
mcomp
);
// If the movement model gave null as a destination no move is
// executed
if
(
dst
==
null
)
{
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"No destination before reachedPosition check... continuing"
);
continue
;
}
// If the position has been reached last round
// set pause active and get a new destination
if
(
reachedPosition
(
mcomp
,
dst
))
{
pauseAndGetNextPosition
(
mcomp
);
continue
;
}
// 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.
Either
<
PositionVector
,
Boolean
>
either
=
localMovementStrategy
.
nextPosition
(
mcomp
,
dst
);
if
(
either
.
hasLeft
())
{
mcomp
.
getRealPosition
().
replace
(
either
.
getLeft
());
notifyPositionChange
(
mcomp
);
}
else
{
if
(
either
.
getRight
().
booleanValue
())
{
pauseAndGetNextPosition
(
mcomp
);
continue
;
}
else
{
// Pause this round
continue
;
}
}
}
}
/**
* Sets the pause time for the given component and calls nextPosition on it.
*
* @param comp
*/
private
void
pauseAndGetNextPosition
(
MovementSupported
comp
)
{
Long
pt
=
pauseTimes
.
get
(
comp
)
*
Time
.
SECOND
;
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"Position reached... pause time is "
+
pt
);
if
(
pt
!=
null
)
{
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"Simulator time: "
+
Time
.
getCurrentTime
());
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"Pause time: "
+
pt
);
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"Added up: "
+
(
Time
.
getCurrentTime
()
+
pt
));
pauseInProgressTimes
.
put
(
comp
,
Time
.
getCurrentTime
()
+
pt
);
}
nextPosition
(
comp
);
}
/**
* Checks if the given component has reached its destination
*
* @param comp
* @param dst
* @return Returns true if the destination was reached
*/
private
boolean
reachedPosition
(
MovementSupported
comp
,
PositionVector
dst
)
{
PositionVector
pos
=
comp
.
getRealPosition
();
double
distance
=
pos
.
getD
istance
(
dst
);
// FIXME: Better detection?
return
(
distance
<
getSpeedLimit
()
*
2
);
}
/**
* Returns the current destination of the given component and calls
* nextPosition if it hasn't been set yet.
*
* @param comp
* @return
*/
private
PositionVector
getDestination
(
MovementSupported
comp
)
{
PositionVector
dst
=
destinations
.
get
(
comp
);
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"Pos: "
+
comp
.
getRealPosition
());
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"Dst: "
+
dst
);
if
(
dst
==
null
)
{
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"No destination, calling nextPosition()"
);
nextPosition
(
comp
);
dst
=
destinations
.
get
(
comp
);
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"New destination is: "
+
dst
);
}
return
dst
;
}
/**
* This method can be called by the concrete implementation to let the
* AbstractWaypointMovementModel know the next destination and pause time.
*
* @param comp
* @param destination
* @param pauseTime
*/
protected
void
nextDestination
(
MovementSupported
comp
,
PositionVector
destination
,
long
pauseTime
)
{
destinations
.
put
(
comp
,
destination
);
pauseTimes
.
put
(
comp
,
pauseTime
);
}
/**
* Is to be implemented by the concrete movement model and will be called by
* the AbstractWaypointMovementModel to ask the concrete implementation for
* a new destination.
*
* @param component
*/
public
abstract
void
nextPosition
(
MovementSupported
component
);
/**
* Get all participating Components
*
* @return
*/
protected
Set
<
MovementSupported
>
getComponents
()
{
return
components
;
}
/**
* Add a component to this movement-Model (used to keep global information
* about all participants in a movement model). Each Component acts as a
* callback upon movement of this component
*
* @param component
*/
@Override
public
void
addComponent
(
MovementSupported
component
)
{
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"AbstractMovementModel: Adding component to the movement model"
);
components
.
add
(
component
);
}
@Override
public
void
addMovementListener
(
MovementListener
listener
)
{
if
(!
listeners
.
contains
(
listener
))
{
listeners
.
add
(
listener
);
}
}
@Override
public
void
removeMovementListener
(
MovementListener
listener
)
{
listeners
.
remove
(
listener
);
}
/**
* Notify Listeners
*/
protected
void
notifyRoundEnd
()
{
for
(
MovementListener
listener
:
listeners
)
{
listener
.
afterComponentsMoved
();
}
}
protected
void
notifyPositionChange
(
MovementSupported
comp
)
{
for
(
MovementListener
listener
:
listeners
)
{
listener
.
afterComponentMoved
(
comp
);
}
comp
.
positionChanged
();
}
/**
* Get a valid delta-Vector (does not cross world-boundaries and does not
* exceed moveSpeedLimit)
*
* @param oldPosition
* @return
*/
protected
PositionVector
getRandomDelta
(
PositionVector
oldPosition
)
{
return
getRandomDelta
(
oldPosition
,
getSpeedLimit
());
}
/**
* Get a valid delta-Vector, where each abs(entry) must not exceed maxLength
*
* @param oldPosition
* @param maxLength
* @return
*/
protected
PositionVector
getRandomDelta
(
PositionVector
oldPosition
,
double
maxLength
)
{
double
[]
delta
=
new
double
[
oldPosition
.
getDimensions
()];
for
(
int
i
=
0
;
i
<
oldPosition
.
getDimensions
();
i
++)
{
int
tries
=
0
;
do
{
delta
[
i
]
=
getRandomDouble
(-
maxLength
,
maxLength
);
if
(++
tries
>
50
)
{
delta
[
i
]
=
0
;
break
;
}
// delta[i] = (r.nextInt(2 * getMoveSpeedLimit() + 1) -
// getMoveSpeedLimit());
}
while
(
oldPosition
.
getEntry
(
i
)
+
delta
[
i
]
>
getWorldDimension
(
i
)
||
oldPosition
.
getEntry
(
i
)
+
delta
[
i
]
<
0
);
}
PositionVector
deltaVector
=
new
PositionVector
(
delta
);
return
deltaVector
;
}
/**
* Returns if this is a valid position within the boundaries
*
* @return
*/
protected
boolean
isValidPosition
(
PositionVector
vector
)
{
for
(
int
i
=
0
;
i
<
vector
.
getDimensions
();
i
++)
{
if
(
vector
.
getEntry
(
i
)
>
getWorldDimension
(
i
)
||
vector
.
getEntry
(
i
)
<
0
)
{
return
false
;
}
}
return
true
;
}
/**
* Returns a random int between from and to, including both interval ends!
*
* @param from
* @param to
* @return
*/
protected
int
getRandomInt
(
int
from
,
int
to
)
{
int
intervalSize
=
Math
.
abs
(
to
-
from
);
return
(
rnd
.
nextInt
(
intervalSize
+
1
)
+
from
);
}
protected
double
getRandomDouble
(
double
from
,
double
to
)
{
double
intervalSize
=
Math
.
abs
(
to
-
from
);
return
(
rnd
.
nextDouble
()
*
intervalSize
+
from
);
}
/**
* Move models can periodically calculate new positions and notify
* listeners, if a position changed. Here you can specify the interval
* between these notifications/calculations. If this is set to zero, there
* is no periodical execution, which may be useful if you want to call
* move() from within an application.
*
* @param timeBetweenMoveOperations
*/
public
void
setTimeBetweenMoveOperations
(
long
timeBetweenMoveOperations
)
{
this
.
timeBetweenMovement
=
timeBetweenMoveOperations
;
if
(
timeBetweenMoveOperations
>
0
)
{
reschedule
();
}
}
protected
void
reschedule
()
{
Event
.
scheduleWithDelay
(
timeBetweenMovement
,
new
PeriodicMove
(),
this
,
0
);
}
@Deprecated
public
void
setSpeedLimit
(
double
speedLimit
)
{
this
.
speedLimit
=
speedLimit
;
this
.
unscaledSpeedLimit
=
speedLimit
;
}
@Deprecated
public
double
getSpeedLimit
()
{
return
speedLimit
;
}
public
void
setWorldX
(
double
dimension
)
{
this
.
worldDimensions
.
setEntry
(
0
,
dimension
);
}
public
void
setWorldY
(
double
dimension
)
{
this
.
worldDimensions
.
setEntry
(
1
,
dimension
);
}
public
double
getWorldDimension
(
int
dim
)
{
return
worldDimensions
.
getEntry
(
dim
);
}
public
void
setWorldZ
(
double
dimension
)
{
this
.
worldDimensions
.
setEntry
(
2
,
dimension
);
}
public
void
setWaypointModel
(
WaypointModel
model
)
{
this
.
waypointModel
=
model
;
if
(
localMovementStrategy
!=
null
)
localMovementStrategy
.
setWaypointModel
(
getWaypointModel
());
}
public
WaypointModel
getWaypointModel
()
{
return
this
.
waypointModel
;
}
public
void
setLocalMovementStrategy
(
LocalMovementStrategy
strategy
)
{
strategy
.
setWaypointModel
(
getWaypointModel
());
this
.
localMovementStrategy
=
strategy
;
}
/**
* Triggers the periodic move
*
* @author Bjoern Richerzhagen
* @version 1.0, 20.03.2012
*/
protected
class
PeriodicMove
implements
EventHandler
{
@Override
public
void
eventOccurred
(
Object
content
,
int
type
)
{
move
();
reschedule
();
}
}
}
/*
* Copyright (c) 2005-2011 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
;
import
java.util.LinkedHashSet
;
import
java.util.LinkedList
;
import
java.util.List
;
import
java.util.Random
;
import
java.util.Set
;
import
java.util.WeakHashMap
;
import
com.google.common.eventbus.Subscribe
;
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.api.topology.waypoints.WaypointModel
;
import
de.tud.kom.p2psim.impl.scenario.simcfg2.annotations.After
;
import
de.tud.kom.p2psim.impl.scenario.simcfg2.annotations.Configure
;
import
de.tud.kom.p2psim.impl.topology.PositionVector
;
import
de.tud.kom.p2psim.impl.topology.events.ScaleWorldEvent
;
import
de.tud.kom.p2psim.impl.util.Either
;
import
de.tud.kom.p2psim.impl.util.geo.maps.MapLoader
;
import
de.tudarmstadt.maki.simonstrator.api.Event
;
import
de.tudarmstadt.maki.simonstrator.api.EventHandler
;
import
de.tudarmstadt.maki.simonstrator.api.Monitor
;
import
de.tudarmstadt.maki.simonstrator.api.Monitor.Level
;
import
de.tudarmstadt.maki.simonstrator.api.Randoms
;
import
de.tudarmstadt.maki.simonstrator.api.Time
;
/**
* The AbstractWaypointMovementModel can be used to implement movement models
* based on way point data. It uses an implementation of LocalMovementStrategy
* for the movement between selected way points.
*
* @author Fabio Zöllner
* @version 1.0, 27.03.2012
*/
public
abstract
class
AbstractWaypointMovementModel
implements
MovementModel
{
private
Set
<
MovementSupported
>
components
=
new
LinkedHashSet
<
MovementSupported
>();
protected
PositionVector
worldDimensions
;
protected
WeakHashMap
<
MovementSupported
,
PositionVector
>
destinations
;
protected
WeakHashMap
<
MovementSupported
,
Long
>
pauseTimes
;
protected
WeakHashMap
<
MovementSupported
,
Long
>
pauseInProgressTimes
;
private
List
<
MovementListener
>
listeners
;
protected
WaypointModel
waypointModel
;
protected
LocalMovementStrategy
localMovementStrategy
;
private
int
configurationCounter
=
100
;
private
long
timeBetweenMovement
=
1
*
Time
.
SECOND
;
private
double
speedLimit
=
1
;
private
double
unscaledSpeedLimit
=
speedLimit
;
private
Random
rnd
=
Randoms
.
getRandom
(
AbstractWaypointMovementModel
.
class
);
public
AbstractWaypointMovementModel
(
double
worldX
,
double
worldY
)
{
worldDimensions
=
new
PositionVector
(
worldX
,
worldY
);
listeners
=
new
LinkedList
<
MovementListener
>();
destinations
=
new
WeakHashMap
<
MovementSupported
,
PositionVector
>();
pauseTimes
=
new
WeakHashMap
<
MovementSupported
,
Long
>();
pauseInProgressTimes
=
new
WeakHashMap
<
MovementSupported
,
Long
>();
// Simulator.registerAtEventBus(this);
}
@Configure
()
@After
(
required
=
{
WaypointModel
.
class
,
MapLoader
.
class
})
public
boolean
configure
()
{
if
(
this
.
waypointModel
==
null
&&
configurationCounter
>
0
)
{
configurationCounter
--;
return
false
;
}
if
(
this
.
waypointModel
==
null
)
{
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
INFO
,
"No waypoint model has been configured. Thus the movement speed won't be adjusted for the scale of the waypoint model."
);
}
//
// if (waypointModel.getScaleFactor() != 1.0) {
// Simulator.postEvent(new ScaleWorldEvent(waypointModel
// .getScaleFactor()));
// }
return
true
;
}
@Subscribe
public
void
scaleWorld
(
ScaleWorldEvent
event
)
{
double
scaleFactor
=
event
.
getFactor
();
speedLimit
=
unscaledSpeedLimit
*
scaleFactor
;
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
INFO
,
"Movement speed of the local movement strategy has been adjusted for the waypoint model scale and is now "
+
speedLimit
+
"m/s"
);
}
/**
* Gets called periodically (after timeBetweenMoveOperations) or by an
* application and should be used to recalculate positions
*/
public
void
move
()
{
long
nrOfSteps
=
timeBetweenMovement
/
Time
.
SECOND
;
for
(
int
i
=
0
;
i
<
nrOfSteps
;
i
++)
{
step
();
}
notifyRoundEnd
();
}
private
void
step
()
{
Set
<
MovementSupported
>
comps
=
getComponents
();
for
(
MovementSupported
mcomp
:
comps
)
{
if
(!
mcomp
.
movementActive
())
{
continue
;
}
Long
currentPause
=
pauseInProgressTimes
.
get
(
mcomp
);
if
(
currentPause
!=
null
)
{
if
(
Time
.
getCurrentTime
()
>=
currentPause
)
{
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"Pause time ended..."
);
pauseInProgressTimes
.
remove
(
mcomp
);
}
else
continue
;
}
PositionVector
dst
=
getDestination
(
mcomp
);
// If the movement model gave null as a destination no move is
// executed
if
(
dst
==
null
)
{
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"No destination before reachedPosition check... continuing"
);
continue
;
}
// If the position has been reached last round
// set pause active and get a new destination
if
(
reachedPosition
(
mcomp
,
dst
))
{
pauseAndGetNextPosition
(
mcomp
);
continue
;
}
// 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.
Either
<
PositionVector
,
Boolean
>
either
=
localMovementStrategy
.
nextPosition
(
mcomp
,
dst
);
if
(
either
.
hasLeft
())
{
mcomp
.
getRealPosition
().
replace
(
either
.
getLeft
());
notifyPositionChange
(
mcomp
);
}
else
{
if
(
either
.
getRight
().
booleanValue
())
{
pauseAndGetNextPosition
(
mcomp
);
continue
;
}
else
{
// Pause this round
continue
;
}
}
}
}
/**
* Sets the pause time for the given component and calls nextPosition on it.
*
* @param comp
*/
private
void
pauseAndGetNextPosition
(
MovementSupported
comp
)
{
Long
pt
=
pauseTimes
.
get
(
comp
)
*
Time
.
SECOND
;
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"Position reached... pause time is "
+
pt
);
if
(
pt
!=
null
)
{
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"Simulator time: "
+
Time
.
getCurrentTime
());
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"Pause time: "
+
pt
);
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"Added up: "
+
(
Time
.
getCurrentTime
()
+
pt
));
pauseInProgressTimes
.
put
(
comp
,
Time
.
getCurrentTime
()
+
pt
);
}
nextPosition
(
comp
);
}
/**
* Checks if the given component has reached its destination
*
* @param comp
* @param dst
* @return Returns true if the destination was reached
*/
private
boolean
reachedPosition
(
MovementSupported
comp
,
PositionVector
dst
)
{
PositionVector
pos
=
comp
.
getRealPosition
();
double
distance
=
pos
.
d
istance
To
(
dst
);
// FIXME: Better detection?
return
(
distance
<
getSpeedLimit
()
*
2
);
}
/**
* Returns the current destination of the given component and calls
* nextPosition if it hasn't been set yet.
*
* @param comp
* @return
*/
private
PositionVector
getDestination
(
MovementSupported
comp
)
{
PositionVector
dst
=
destinations
.
get
(
comp
);
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"Pos: "
+
comp
.
getRealPosition
());
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"Dst: "
+
dst
);
if
(
dst
==
null
)
{
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"No destination, calling nextPosition()"
);
nextPosition
(
comp
);
dst
=
destinations
.
get
(
comp
);
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"New destination is: "
+
dst
);
}
return
dst
;
}
/**
* This method can be called by the concrete implementation to let the
* AbstractWaypointMovementModel know the next destination and pause time.
*
* @param comp
* @param destination
* @param pauseTime
*/
protected
void
nextDestination
(
MovementSupported
comp
,
PositionVector
destination
,
long
pauseTime
)
{
destinations
.
put
(
comp
,
destination
);
pauseTimes
.
put
(
comp
,
pauseTime
);
}
/**
* Is to be implemented by the concrete movement model and will be called by
* the AbstractWaypointMovementModel to ask the concrete implementation for
* a new destination.
*
* @param component
*/
public
abstract
void
nextPosition
(
MovementSupported
component
);
/**
* Get all participating Components
*
* @return
*/
protected
Set
<
MovementSupported
>
getComponents
()
{
return
components
;
}
/**
* Add a component to this movement-Model (used to keep global information
* about all participants in a movement model). Each Component acts as a
* callback upon movement of this component
*
* @param component
*/
@Override
public
void
addComponent
(
MovementSupported
component
)
{
Monitor
.
log
(
AbstractWaypointMovementModel
.
class
,
Level
.
DEBUG
,
"AbstractMovementModel: Adding component to the movement model"
);
components
.
add
(
component
);
}
@Override
public
void
addMovementListener
(
MovementListener
listener
)
{
if
(!
listeners
.
contains
(
listener
))
{
listeners
.
add
(
listener
);
}
}
@Override
public
void
removeMovementListener
(
MovementListener
listener
)
{
listeners
.
remove
(
listener
);
}
/**
* Notify Listeners
*/
protected
void
notifyRoundEnd
()
{
for
(
MovementListener
listener
:
listeners
)
{
listener
.
afterComponentsMoved
();
}
}
protected
void
notifyPositionChange
(
MovementSupported
comp
)
{
for
(
MovementListener
listener
:
listeners
)
{
listener
.
afterComponentMoved
(
comp
);
}
comp
.
positionChanged
();
}
/**
* Get a valid delta-Vector (does not cross world-boundaries and does not
* exceed moveSpeedLimit)
*
* @param oldPosition
* @return
*/
protected
PositionVector
getRandomDelta
(
PositionVector
oldPosition
)
{
return
getRandomDelta
(
oldPosition
,
getSpeedLimit
());
}
/**
* Get a valid delta-Vector, where each abs(entry) must not exceed maxLength
*
* @param oldPosition
* @param maxLength
* @return
*/
protected
PositionVector
getRandomDelta
(
PositionVector
oldPosition
,
double
maxLength
)
{
double
[]
delta
=
new
double
[
oldPosition
.
getDimensions
()];
for
(
int
i
=
0
;
i
<
oldPosition
.
getDimensions
();
i
++)
{
int
tries
=
0
;
do
{
delta
[
i
]
=
getRandomDouble
(-
maxLength
,
maxLength
);
if
(++
tries
>
50
)
{
delta
[
i
]
=
0
;
break
;
}
// delta[i] = (r.nextInt(2 * getMoveSpeedLimit() + 1) -
// getMoveSpeedLimit());
}
while
(
oldPosition
.
getEntry
(
i
)
+
delta
[
i
]
>
getWorldDimension
(
i
)
||
oldPosition
.
getEntry
(
i
)
+
delta
[
i
]
<
0
);
}
PositionVector
deltaVector
=
new
PositionVector
(
delta
);
return
deltaVector
;
}
/**
* Returns if this is a valid position within the boundaries
*
* @return
*/
protected
boolean
isValidPosition
(
PositionVector
vector
)
{
for
(
int
i
=
0
;
i
<
vector
.
getDimensions
();
i
++)
{
if
(
vector
.
getEntry
(
i
)
>
getWorldDimension
(
i
)
||
vector
.
getEntry
(
i
)
<
0
)
{
return
false
;
}
}
return
true
;
}
/**
* Returns a random int between from and to, including both interval ends!
*
* @param from
* @param to
* @return
*/
protected
int
getRandomInt
(
int
from
,
int
to
)
{
int
intervalSize
=
Math
.
abs
(
to
-
from
);
return
(
rnd
.
nextInt
(
intervalSize
+
1
)
+
from
);
}
protected
double
getRandomDouble
(
double
from
,
double
to
)
{
double
intervalSize
=
Math
.
abs
(
to
-
from
);
return
(
rnd
.
nextDouble
()
*
intervalSize
+
from
);
}
/**
* Move models can periodically calculate new positions and notify
* listeners, if a position changed. Here you can specify the interval
* between these notifications/calculations. If this is set to zero, there
* is no periodical execution, which may be useful if you want to call
* move() from within an application.
*
* @param timeBetweenMoveOperations
*/
public
void
setTimeBetweenMoveOperations
(
long
timeBetweenMoveOperations
)
{
this
.
timeBetweenMovement
=
timeBetweenMoveOperations
;
if
(
timeBetweenMoveOperations
>
0
)
{
reschedule
();
}
}
protected
void
reschedule
()
{
Event
.
scheduleWithDelay
(
timeBetweenMovement
,
new
PeriodicMove
(),
this
,
0
);
}
@Deprecated
public
void
setSpeedLimit
(
double
speedLimit
)
{
this
.
speedLimit
=
speedLimit
;
this
.
unscaledSpeedLimit
=
speedLimit
;
}
@Deprecated
public
double
getSpeedLimit
()
{
return
speedLimit
;
}
public
void
setWorldX
(
double
dimension
)
{
this
.
worldDimensions
.
setEntry
(
0
,
dimension
);
}
public
void
setWorldY
(
double
dimension
)
{
this
.
worldDimensions
.
setEntry
(
1
,
dimension
);
}
public
double
getWorldDimension
(
int
dim
)
{
return
worldDimensions
.
getEntry
(
dim
);
}
public
void
setWorldZ
(
double
dimension
)
{
this
.
worldDimensions
.
setEntry
(
2
,
dimension
);
}
public
void
setWaypointModel
(
WaypointModel
model
)
{
this
.
waypointModel
=
model
;
if
(
localMovementStrategy
!=
null
)
localMovementStrategy
.
setWaypointModel
(
getWaypointModel
());
}
public
WaypointModel
getWaypointModel
()
{
return
this
.
waypointModel
;
}
public
void
setLocalMovementStrategy
(
LocalMovementStrategy
strategy
)
{
strategy
.
setWaypointModel
(
getWaypointModel
());
this
.
localMovementStrategy
=
strategy
;
}
/**
* Triggers the periodic move
*
* @author Bjoern Richerzhagen
* @version 1.0, 20.03.2012
*/
protected
class
PeriodicMove
implements
EventHandler
{
@Override
public
void
eventOccurred
(
Object
content
,
int
type
)
{
move
();
reschedule
();
}
}
}
src/de/tud/kom/p2psim/impl/topology/movement/CsvMovement.java
View file @
0c80da45
...
...
@@ -27,26 +27,19 @@ import java.util.LinkedHashMap;
import
java.util.LinkedList
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Random
;
import
java.util.Set
;
import
org.jfree.data.io.CSV
;
import
de.tud.kom.p2psim.api.common.SimHost
;
import
de.tud.kom.p2psim.api.common.SimHostComponent
;
import
de.tud.kom.p2psim.api.network.SimNetInterface
;
import
de.tud.kom.p2psim.api.topology.Topology
;
import
de.tud.kom.p2psim.api.topology.movement.MovementInformation
;
import
de.tud.kom.p2psim.api.topology.movement.MovementModel
;
import
de.tud.kom.p2psim.api.topology.movement.MovementSupported
;
import
de.tud.kom.p2psim.impl.network.gnp.topology.Host
;
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.ModularMovementModel
;
import
de.tud.kom.p2psim.impl.topology.movement.modular.attraction.AttractionPoint
;
import
de.tud.kom.p2psim.impl.topology.movement.modular.transition.FixedAssignmentStrategy
;
import
de.tud.kom.p2psim.impl.topology.movement.modular.transition.TransitionStrategy
;
import
de.tudarmstadt.maki.simonstrator.api.Randoms
;
import
de.tudarmstadt.maki.simonstrator.api.Binder
;
import
de.tudarmstadt.maki.simonstrator.api.Time
;
import
de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor
;
...
...
@@ -82,7 +75,8 @@ public class CsvMovement extends AbstractMovementModel {
@XMLConfigurableConstructor
({
"movementPointsFile"
,
"minMovementSpeed"
,
"maxMovementSpeed"
})
public
CsvMovement
(
String
movementPointsFile
,
double
minMovementSpeed
,
double
maxMovementSpeed
)
{
super
();
this
.
worldDimensions
=
Topology
.
getWorldDimension
();
this
.
worldDimensions
=
Binder
.
getComponentOrNull
(
Topology
.
class
)
.
getWorldDimensions
();
this
.
file
=
movementPointsFile
;
this
.
readPathInfos
=
new
LinkedList
<
LinkedList
<
CsvPathInfo
>>();
this
.
stateInfo
=
new
LinkedHashMap
<
MovementSupported
,
CsvMovementInfo
>();
...
...
@@ -144,7 +138,7 @@ public class CsvMovement extends AbstractMovementModel {
double
distancePerMoveOperation
=
pathInfo
.
getSpeed
()
*
getTimeBetweenMoveOperations
()
/
Time
.
SECOND
;
double
distance
=
actPos
.
getD
istance
(
targetPos
);
double
distance
=
actPos
.
d
istance
To
(
targetPos
);
steps
=
(
int
)
Math
.
round
(
distance
/
distancePerMoveOperation
);
...
...
src/de/tud/kom/p2psim/impl/topology/movement/SLAWMovementModel.java
View file @
0c80da45
/*
* 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
;
import
java.awt.Color
;
import
java.awt.Font
;
import
java.awt.Graphics
;
import
java.awt.Graphics2D
;
import
java.util.ArrayList
;
import
java.util.Collection
;
import
java.util.Map
;
import
java.util.Random
;
import
java.util.Vector
;
import
java.util.WeakHashMap
;
import
javax.swing.JComponent
;
import
com.google.common.collect.Maps
;
import
de.tud.kom.p2psim.api.scenario.ConfigurationException
;
import
de.tud.kom.p2psim.api.topology.movement.MovementSupported
;
import
de.tud.kom.p2psim.api.topology.waypoints.WaypointModel
;
import
de.tud.kom.p2psim.impl.scenario.simcfg2.annotations.After
;
import
de.tud.kom.p2psim.impl.scenario.simcfg2.annotations.Configure
;
import
de.tud.kom.p2psim.impl.topology.PositionVector
;
import
de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector
;
import
de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector.DisplayString
;
import
de.tud.kom.p2psim.impl.topology.waypoints.graph.StrongWaypoint
;
import
de.tud.kom.p2psim.impl.topology.waypoints.graph.Waypoint
;
import
de.tudarmstadt.maki.simonstrator.api.Randoms
;
import
de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor
;
/**
* The SLAW Movement Model is based on the implementation in
* BonnMotion Copyright (C) 2002-2010 University of Bonn
* by Zia-Ul-Huda, Gufron Atokhojaev and Florian Schmitt
*
* @author Fabio Zöllner
* @version 1.0, 09.04.2012
*/
public
class
SLAWMovementModel
extends
AbstractWaypointMovementModel
{
private
static
final
int
powerlaw_step
=
1
;
private
static
int
levy_scale_factor
=
1
;
private
static
int
powerlaw_mode
=
1
;
protected
Random
rand
=
Randoms
.
getRandom
(
SLAWMovementModel
.
class
);
protected
Random
xorShiftRandom
=
new
XorShiftRandom
(
rand
.
nextLong
());
protected
double
beta
=
1
;
protected
long
minpause
=
10
;
protected
long
maxpause
=
50
;
protected
double
dist_alpha
=
3
;
protected
double
cluster_range
;
protected
int
cluster_ratio
=
3
;
protected
int
waypoint_ratio
=
3
;
protected
Cluster
[]
clusters
;
protected
WeakHashMap
<
MovementSupported
,
SLAWMovementInformation
>
movementInformation
;
// TODO: minpause and maxpause are currently in microseconds... should be
// changed to minutes
@XMLConfigurableConstructor
({
"worldX"
,
"worldY"
,
"minpause"
,
"maxpause"
,
"beta"
,
"distAlpha"
,
"clusterRange"
,
"clusterRatio"
,
"waypointRatio"
})
public
SLAWMovementModel
(
double
worldX
,
double
worldY
,
long
minpause
,
long
maxpause
,
double
beta
,
double
distAlpha
,
double
clusterRange
,
int
clusterRatio
,
int
waypointRatio
)
{
super
(
worldX
,
worldY
);
this
.
minpause
=
minpause
;
this
.
maxpause
=
maxpause
;
this
.
dist_alpha
=
distAlpha
;
this
.
cluster_range
=
clusterRange
;
this
.
cluster_ratio
=
clusterRatio
;
this
.
waypoint_ratio
=
waypointRatio
;
movementInformation
=
new
WeakHashMap
<
MovementSupported
,
SLAWMovementModel
.
SLAWMovementInformation
>();
}
@Override
public
void
nextPosition
(
MovementSupported
node
)
{
// These variables have values same as in the matlab implementation of
// SLAW model by Seongik Hong, NCSU, US (3/10/2009)
if
(
waypointModel
==
null
)
{
throw
new
ConfigurationException
(
"SLAWMovementModel requires a valid waypoint model which hasn't been provided, cannot execute"
);
}
SLAWMovementInformation
mi
=
movementInformation
.
get
(
node
);
if
(
mi
==
null
)
{
//System.err.println("Creating new movement information for node " + node.toString());
mi
=
new
SLAWMovementInformation
();
movementInformation
.
put
(
node
,
mi
);
}
// log.debug("Selecting new destination for node " + mi.nodeId);
// get random clusters and waypoints
if
(
mi
.
clts
==
null
)
mi
.
clts
=
make_selection
(
clusters
,
null
,
false
);
// get random clusters and waypoints
if
(
mi
.
wlist
==
null
)
mi
.
wlist
=
get_waypoint_list
(
mi
.
clts
);
// random source node
if
(
mi
.
srcIndex
==
-
1
)
{
int
old
=
mi
.
srcIndex
;
mi
.
srcIndex
=
(
int
)
Math
.
floor
(
randomNextDouble
()
*
mi
.
wlist
.
length
);
//System.err.println("mi.srcIndex was " + old + " but is now " + mi.srcIndex);
// Set the initial position to the selected waypoint
nextDestination
(
node
,
mi
.
wlist
[
mi
.
srcIndex
].
pos
,
0
);
//System.err.println("Default case? 0");
return
;
}
if
(
mi
.
dstIndex
!=
-
1
)
{
mi
.
srcIndex
=
mi
.
dstIndex
;
}
int
count
=
0
;
PositionVector
source
=
new
PositionVector
(
node
.
getRealPosition
()
.
getX
(),
node
.
getRealPosition
().
getY
());
mi
.
wlist
[
mi
.
srcIndex
].
is_visited
=
true
;
// get list of not visited locations
for
(
int
i
=
0
;
i
<
mi
.
wlist
.
length
;
i
++)
{
if
(!
mi
.
wlist
[
i
].
is_visited
)
{
count
++;
}
}
// if all waypoints are visited then select new clusters and
// waypoints. Destructive mode of original SLAW matlab
// implementation by Seongik Hong, NCSU, US (3/10/2009)
while
(
count
==
0
)
{
mi
.
clts
=
make_selection
(
clusters
,
mi
.
clts
,
true
);
mi
.
wlist
=
get_waypoint_list
(
mi
.
clts
);
for
(
int
i
=
0
;
i
<
mi
.
wlist
.
length
;
i
++)
{
if
(!
mi
.
wlist
[
i
].
is_visited
)
{
if
(
source
.
getD
istance
(
mi
.
wlist
[
i
].
pos
)
!=
0.0
)
{
count
++;
}
else
{
mi
.
wlist
[
i
].
is_visited
=
true
;
}
}
}
}
ClusterMember
[]
not_visited
=
new
ClusterMember
[
count
];
count
=
0
;
for
(
int
i
=
0
;
i
<
mi
.
wlist
.
length
;
i
++)
{
if
(!
mi
.
wlist
[
i
].
is_visited
)
{
not_visited
[
count
++]
=
mi
.
wlist
[
i
];
}
}
// get distance from source to all remaining waypoints
double
[]
dist
=
new
double
[
not_visited
.
length
];
for
(
int
i
=
0
;
i
<
not_visited
.
length
;
i
++)
{
dist
[
i
]
=
source
.
getD
istance
(
not_visited
[
i
].
pos
);
}
double
[]
weights
=
new
double
[
not_visited
.
length
];
// cumulative sum of distance weights
for
(
int
i
=
0
;
i
<
weights
.
length
;
i
++)
{
weights
[
i
]
=
0
;
for
(
int
j
=
0
;
j
<=
i
;
j
++)
{
weights
[
i
]
+=
1
/
Math
.
pow
(
dist
[
j
],
this
.
dist_alpha
);
}
}
for
(
int
i
=
0
;
i
<
weights
.
length
;
i
++)
{
weights
[
i
]
/=
weights
[
weights
.
length
-
1
];
}
double
r
=
randomNextDouble
();
int
index
;
for
(
index
=
0
;
index
<
weights
.
length
;
index
++)
{
if
(
r
<
weights
[
index
])
{
break
;
}
}
// select the next destination
for
(
int
i
=
0
;
i
<
mi
.
wlist
.
length
;
i
++)
{
if
(
mi
.
wlist
[
i
].
pos
.
equals
(
not_visited
[
index
].
pos
))
{
mi
.
dstIndex
=
i
;
double
pauseTime
=
random_powerlaw
(
powerlaw_step
,
levy_scale_factor
,
powerlaw_mode
)[
0
];
//System.err.println("Next pause time is " + pauseTime);
nextDestination
(
node
,
mi
.
wlist
[
mi
.
dstIndex
].
pos
,
(
long
)
pauseTime
);
break
;
}
}
// change destination to next source
// mi.srcIndex = mi.dstIndex;
}
/**
* returns aggregated list of cluster members from all passed clusters
*
* @param clusters
* clusters list
* @return array of ClusterMember type
*/
protected
ClusterMember
[]
get_waypoint_list
(
Cluster
[]
clusters
)
{
ArrayList
<
ClusterMember
>
result
=
new
ArrayList
<
ClusterMember
>();
for
(
Cluster
cluster
:
clusters
)
{
for
(
ClusterMember
clustermember
:
cluster
.
members
)
{
result
.
add
(
clustermember
);
}
}
return
result
.
toArray
(
new
ClusterMember
[
0
]);
}
/**
* makes the selection of new clusters and waypoints from passed clusters
*
* @param clusters
* array of clusters to make selection from
* @param change_one
* changes only one of the clusters randomly and then selects new
* waypoints from all clusters
* @return array of selected clusters
*/
protected
Cluster
[]
make_selection
(
Cluster
[]
clusters
,
Cluster
[]
cur_list
,
boolean
change_one
)
{
ArrayList
<
Integer
>
cluster_selection
;
if
(!
change_one
)
{
// Number of clusters to select
int
num_clusters
=
(
int
)
Math
.
ceil
((
double
)
clusters
.
length
/
(
double
)
cluster_ratio
);
cluster_selection
=
new
ArrayList
<
Integer
>(
num_clusters
);
for
(
int
i
=
0
;
i
<
num_clusters
;
i
++)
{
cluster_selection
.
add
(
i
,
-
1
);
}
int
[]
total_list
=
new
int
[
waypointModel
.
getNumberOfWaypoints
(
StrongWaypoint
.
class
)];
int
counter
=
0
;
// probability array
for
(
int
i
=
0
;
i
<
clusters
.
length
;
i
++)
{
for
(
int
j
=
0
;
j
<
clusters
[
i
].
members
.
length
;
j
++)
{
total_list
[
counter
++]
=
clusters
[
i
].
index
;
}
}
// select clusters randomly with weights
int
t
=
total_list
[(
int
)
Math
.
floor
(
this
.
randomNextDouble
()
*
waypointModel
.
getNumberOfWaypoints
(
StrongWaypoint
.
class
))];
for
(
int
i
=
0
;
i
<
cluster_selection
.
size
();
i
++)
{
while
(
cluster_selection
.
contains
(
t
))
{
t
=
total_list
[(
int
)
Math
.
floor
(
this
.
randomNextDouble
()
*
waypointModel
.
getNumberOfWaypoints
(
StrongWaypoint
.
class
))];
}
cluster_selection
.
set
(
i
,
t
);
}
}
else
{
// just need to change one randomly
cluster_selection
=
new
ArrayList
<
Integer
>(
cur_list
.
length
);
for
(
Cluster
cluster
:
cur_list
)
{
cluster_selection
.
add
(
cluster
.
index
);
}
}
// change one cluster without weight consideration
cluster_selection
=
change_one_random
(
cluster_selection
,
clusters
.
length
);
// select waypoints from selected clusters.
Cluster
[]
result
=
new
Cluster
[
cluster_selection
.
size
()];
double
numberOfWaypoints
;
Cluster
cluster_iterator
=
null
;
for
(
int
i
=
0
;
i
<
cluster_selection
.
size
();
i
++)
{
// find Cluster object in clusters array
for
(
int
j
=
0
;
j
<
clusters
.
length
;
j
++)
{
if
(
cluster_selection
.
get
(
i
)
==
clusters
[
j
].
index
)
{
cluster_iterator
=
clusters
[
j
];
break
;
}
}
result
[
i
]
=
new
Cluster
(
cluster_iterator
.
index
);
numberOfWaypoints
=
(
double
)
cluster_iterator
.
members
.
length
/
(
double
)
this
.
waypoint_ratio
;
int
[]
waypoint_selection
;
if
(
numberOfWaypoints
<
1
)
{
waypoint_selection
=
select_uniformly
(
cluster_iterator
.
members
.
length
,
1
);
}
else
{
if
(
this
.
randomNextDouble
()
<
numberOfWaypoints
%
1
)
{
waypoint_selection
=
select_uniformly
(
cluster_iterator
.
members
.
length
,
(
int
)
(
Math
.
floor
(
numberOfWaypoints
)
+
1
));
}
else
{
waypoint_selection
=
select_uniformly
(
cluster_iterator
.
members
.
length
,
(
int
)
Math
.
floor
(
numberOfWaypoints
));
}
}
result
[
i
].
members
=
new
ClusterMember
[
waypoint_selection
.
length
];
for
(
int
j
=
0
;
j
<
waypoint_selection
.
length
;
j
++)
{
result
[
i
].
members
[
j
]
=
cluster_iterator
.
members
[
waypoint_selection
[
j
]]
.
clone
();
}
}
return
result
;
}
/**
* changes one of the numbers randomly from the passed array. The new
* changed number is in range [1,n]
*
* @param list
* array of integers
* @param n
* range of numbers
* @return array of integers with one element changed randomly
*/
protected
ArrayList
<
Integer
>
change_one_random
(
ArrayList
<
Integer
>
list
,
int
n
)
{
int
index
=
(
int
)
Math
.
floor
(
this
.
randomNextDouble
()
*
list
.
size
());
int
value
=
(
int
)
Math
.
floor
(
this
.
randomNextDouble
()
*
n
)
+
1
;
while
(
list
.
contains
(
value
))
{
value
=
(
int
)
Math
.
floor
(
this
.
randomNextDouble
()
*
n
)
+
1
;
}
list
.
set
(
index
,
value
);
return
list
;
}
/**
* selects k numbers out of n uniformly
*
* @param n
* @param k
* @return array of k integers
*/
protected
int
[]
select_uniformly
(
int
n
,
int
k
)
{
if
(
k
>
n
)
throw
new
RuntimeException
(
"SLAW.select_uniformaly(): value of k must not be larger than n."
);
int
t
;
int
[]
list
=
new
int
[
k
];
for
(
int
i
=
0
;
i
<
k
;
i
++)
{
list
[
i
]
=
-
1
;
}
boolean
is_in
;
int
count
=
0
;
while
(
count
<
k
)
{
is_in
=
false
;
t
=
(
int
)
Math
.
floor
(
this
.
randomNextDouble
()
*
n
);
for
(
int
i
=
0
;
i
<
list
.
length
;
i
++)
{
if
(
list
[
i
]
==
t
)
{
is_in
=
true
;
break
;
}
}
if
(!
is_in
)
{
list
[
count
++]
=
t
;
}
}
return
list
;
}
/**
* Generates random values from power-law distribution.
*
* @param powerlaw_step
* the total numbers to generate. Returns an array of this size.
* @param levy_scale_factor
* levy scaling factor of distribution
* @param powerlaw_mode
* 1: stabrnd 2: reverse computation
* @return double array of powerlaw_step length
**/
protected
double
[]
random_powerlaw
(
int
powerlaw_step
,
int
levy_scale_factor
,
int
powerlaw_mode
)
{
double
[]
result
=
new
double
[
powerlaw_step
];
for
(
int
xi
=
0
;
xi
<
powerlaw_step
;)
{
if
(
powerlaw_mode
==
1
)
{
// stabrnd
double
[]
stabrnd_result
=
stabrnd
(
0
,
levy_scale_factor
,
0
,
powerlaw_step
);
ArrayList
<
Double
>
temp
=
new
ArrayList
<
Double
>();
for
(
int
i
=
0
;
i
<
stabrnd_result
.
length
;
i
++)
{
if
(
stabrnd_result
[
i
]
>
this
.
minpause
&&
stabrnd_result
[
i
]
<
this
.
maxpause
)
{
temp
.
add
(
new
Double
(
stabrnd_result
[
i
]));
}
}
if
(
temp
.
size
()
>
0
)
{
for
(
Double
d
:
temp
)
{
result
[
xi
++]
=
d
;
if
(
xi
>
powerlaw_step
)
break
;
}
}
}
else
if
(
powerlaw_mode
==
2
)
{
// reverse computation
double
temp
=
Math
.
pow
(
randomNextDouble
(),
1
/
(
1
-
(
this
.
beta
+
1
)))
*
this
.
minpause
;
if
(
temp
<
this
.
maxpause
)
{
result
[
xi
++]
=
temp
;
}
}
}
return
result
;
}
/**
* Returns array of randomly generated n numbers based on the method of J.M.
* Chambers, C.L. Mallows and B.W. Stuck,
* "A Method for Simulating Stable Random Variables," JASA 71 (1976): 340-4.
*
* @param b
* beta factor
* @param levy_scale_factor
* @param delta
* @param n
* count of random numbers to generate
* @return double array of n length
*/
private
double
[]
stabrnd
(
double
b
,
int
levy_scale_factor
,
double
delta
,
int
n
)
{
if
(
this
.
beta
<
.
1
||
this
.
beta
>
2
)
{
throw
new
RuntimeException
(
"SLAW.stabrnd(): Beta value must be in [.1,2]"
);
}
if
(
Math
.
abs
(
b
)
>
1
)
{
throw
new
RuntimeException
(
"SLAW.stabrnd(): local beta value must be in [-1,1]"
);
}
// Generate exponential w and uniform phi
double
[]
w
=
new
double
[
n
];
double
[]
x
=
new
double
[
n
];
double
[]
phi
=
new
double
[
n
];
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
w
[
i
]
=
-
Math
.
log
(
randomNextDouble
());
phi
[
i
]
=
(
randomNextDouble
()
-
0.5
)
*
Math
.
PI
;
}
// Gaussian case (Box-Muller)
if
(
this
.
beta
==
2
)
{
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
x
[
i
]
=
2
*
Math
.
sqrt
(
w
[
i
])
*
Math
.
sin
(
phi
[
i
]);
x
[
i
]
=
delta
+
levy_scale_factor
*
x
[
i
];
}
}
else
if
(
b
==
0
)
{
// Symmetrical cases
if
(
this
.
beta
==
1
)
{
// Cauchy case
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
x
[
i
]
=
Math
.
tan
(
phi
[
i
]);
}
}
else
{
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
x
[
i
]
=
Math
.
pow
(
Math
.
cos
((
1
-
this
.
beta
)
*
phi
[
i
])
/
w
[
i
],
1
/
this
.
beta
-
1
)
*
Math
.
sin
(
this
.
beta
*
phi
[
i
])
/
Math
.
pow
(
Math
.
cos
(
phi
[
i
]),
1
/
this
.
beta
);
}
}
}
else
{
// General cases
double
cosphi
,
zeta
,
aphi
,
a1phi
,
bphi
;
if
(
Math
.
abs
(
this
.
beta
-
1
)
>
0.00000001
)
{
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
cosphi
=
Math
.
cos
(
phi
[
i
]);
zeta
=
b
*
Math
.
tan
(
Math
.
PI
*
this
.
beta
/
2
);
aphi
=
this
.
beta
*
phi
[
i
];
a1phi
=
(
1
-
this
.
beta
)
*
phi
[
i
];
x
[
i
]
=
(
Math
.
sin
(
aphi
)
+
zeta
*
Math
.
cos
(
aphi
))
/
cosphi
*
Math
.
pow
(
(
Math
.
cos
(
a1phi
)
+
zeta
*
Math
.
sin
(
a1phi
))
/
(
w
[
i
]
*
cosphi
),
(
1
-
this
.
beta
)
/
this
.
beta
);
}
}
else
{
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
cosphi
=
Math
.
cos
(
phi
[
i
]);
bphi
=
Math
.
PI
/
2
+
b
*
phi
[
i
];
x
[
i
]
=
2
/
Math
.
PI
*
(
bphi
*
Math
.
tan
(
phi
[
i
])
-
b
*
Math
.
log
((
Math
.
PI
/
2
)
*
w
[
i
]
*
cosphi
/
bphi
));
}
if
(
this
.
beta
!=
1
)
{
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
x
[
i
]
+=
b
*
Math
.
tan
(
Math
.
PI
*
this
.
beta
/
2
);
}
}
}
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
x
[
i
]
=
delta
+
levy_scale_factor
*
x
[
i
];
}
}
return
x
;
}
protected
double
randomNextDouble
(
final
double
value
)
{
return
(
randomNextDouble
()
*
value
);
}
protected
double
randomNextDouble
()
{
return
xorShiftRandom
.
nextDouble
();
//return rand.nextDouble();
}
private
static
final
class
Cluster
{
public
ClusterMember
[]
members
;
public
int
index
=
-
1
;
public
Cluster
(
int
_index
)
{
index
=
_index
;
}
public
Cluster
(
int
_index
,
ClusterMember
[]
_members
)
{
index
=
_index
;
members
=
_members
;
}
public
String
toString
()
{
return
"Cluster[index: "
+
index
+
"]"
;
}
}
private
static
final
class
ClusterMember
{
public
int
cluster_index
=
-
1
;
public
PositionVector
pos
;
public
boolean
is_visited
=
false
;
public
ClusterMember
(
int
_cluster_index
,
PositionVector
_pos
,
boolean
_is_visited
)
{
this
.
cluster_index
=
_cluster_index
;
this
.
is_visited
=
_is_visited
;
this
.
pos
=
_pos
;
}
public
ClusterMember
clone
()
{
return
new
ClusterMember
(
this
.
cluster_index
,
this
.
pos
,
this
.
is_visited
);
}
}
private
static
class
SLAWMovementInformation
{
public
SLAWMovementInformation
()
{
//
}
public
int
srcIndex
=
-
1
;
public
int
dstIndex
=
-
1
;
public
Cluster
[]
clts
=
null
;
public
ClusterMember
[]
wlist
=
null
;
}
@Configure
()
@After
(
required
={
WaypointModel
.
class
})
public
void
_verifyConfig
()
{
/*
* FIXME: get rid of this method!
*/
Collection
<
Waypoint
>
strongWaypoints
=
waypointModel
.
getWaypoints
(
StrongWaypoint
.
class
);
if
(
strongWaypoints
==
null
)
throw
new
ConfigurationException
(
"The configured waypoint model hasn't generated any strong waypoints that are required by the SLAWMovementModel."
);
clusters
=
generate_clusters
(
waypointModel
);
if
(
clusters
.
length
<=
2
)
{
throw
new
ConfigurationException
(
"The SLAW movement model could only generate 1 cluster, please adjust the cluster range parameter"
);
}
VisualizationInjector
.
injectComponent
(
"Clusters"
,
-
1
,
new
SLAWClusterOverlay
(
clusters
,
waypointModel
),
false
);
VisualizationInjector
.
addDisplayString
(
new
DisplayString
()
{
@Override
public
String
getDisplayString
()
{
int
clusterWps
=
0
;
for
(
Cluster
c
:
clusters
)
{
clusterWps
+=
c
.
members
.
length
;
}
return
"SLAW[Clusters: "
+
clusters
.
length
+
", WPs in clusters: "
+
clusterWps
+
", Avg per cluster: "
+
(
clusterWps
/
clusters
.
length
)
+
"]"
;
}
});
}
@Override
public
void
setWaypointModel
(
WaypointModel
waypointModel
)
{
super
.
setWaypointModel
(
waypointModel
);
}
/**
* generates clusters
*
* @param waypoints
* list of waypoint s
* @return array of clusters
*/
protected
Cluster
[]
generate_clusters
(
WaypointModel
model
)
{
Vector
<
PositionVector
>
all_points
=
new
Vector
<
PositionVector
>();
Vector
<
PositionVector
>
new_points
=
new
Vector
<
PositionVector
>();
Vector
<
PositionVector
>
members
=
new
Vector
<
PositionVector
>();
Vector
<
Cluster
>
clusters
=
new
Vector
<
Cluster
>();
Vector
<
ClusterMember
>
cluster_members
=
new
Vector
<
ClusterMember
>();
Waypoint
[]
waypoints
=
new
Waypoint
[]{};
Collection
<
Waypoint
>
waypointList
=
model
.
getWaypoints
(
StrongWaypoint
.
class
);
if
(
waypointList
==
null
)
throw
new
ConfigurationException
(
"The configured waypoint model didn't generate any strong waypoints"
);
waypoints
=
waypointList
.
toArray
(
waypoints
);
for
(
int
i
=
0
;
i
<
waypoints
.
length
;
i
++)
{
all_points
.
add
(
waypoints
[
i
].
getPosition
());
}
PositionVector
init_pos
=
null
;
int
cluster_count
=
0
;
while
(!
all_points
.
isEmpty
())
{
if
(
init_pos
==
null
)
{
init_pos
=
all_points
.
firstElement
();
all_points
.
remove
(
0
);
members
.
add
(
init_pos
);
}
for
(
int
i
=
0
;
i
<
all_points
.
size
();
i
++)
{
PositionVector
new_pos
=
all_points
.
elementAt
(
i
);
if
(
init_pos
.
getD
istance
(
new_pos
)
<=
this
.
cluster_range
)
{
new_points
.
add
(
new_pos
);
members
.
add
(
new_pos
);
all_points
.
remove
(
i
--);
}
}
// for all_points
if
(!
new_points
.
isEmpty
()
&&
!
all_points
.
isEmpty
())
{
init_pos
=
new_points
.
firstElement
();
new_points
.
remove
(
0
);
}
else
{
for
(
int
i
=
0
;
i
<
members
.
size
();
i
++)
{
cluster_members
.
add
(
new
ClusterMember
(
cluster_count
,
members
.
elementAt
(
i
),
false
));
}
clusters
.
add
(
new
Cluster
(++
cluster_count
,
cluster_members
.
toArray
(
new
ClusterMember
[
0
])));
cluster_members
.
clear
();
new_points
.
clear
();
members
.
clear
();
init_pos
=
null
;
}
}
// while all_points
return
clusters
.
toArray
(
new
Cluster
[
0
]);
}
private
class
SLAWClusterOverlay
extends
JComponent
{
private
Map
<
Cluster
,
Color
>
clusterColors
=
Maps
.
newHashMap
();
private
Cluster
[]
clusters
;
private
Random
rnd
=
new
Random
(
System
.
nanoTime
());
private
WaypointModel
model
;
public
SLAWClusterOverlay
(
Cluster
[]
clusters
,
WaypointModel
model
)
{
super
();
this
.
clusters
=
clusters
;
this
.
model
=
model
;
PositionVector
dimension
=
model
.
getMap
().
getDimensions
();
setBounds
(
0
,
0
,
(
int
)
dimension
.
getX
(),
(
int
)
dimension
.
getY
());
setOpaque
(
false
);
setVisible
(
true
);
}
@Override
protected
void
paintComponent
(
Graphics
g
)
{
super
.
paintComponent
(
g
);
Graphics2D
g2
=
(
Graphics2D
)
g
;
g2
.
setFont
(
new
Font
(
"SansSerif"
,
Font
.
PLAIN
,
9
));
for
(
Cluster
c
:
clusters
)
{
PositionVector
lastPos
=
null
;
Color
randomColor
=
clusterColors
.
get
(
c
);
if
(
randomColor
==
null
)
{
final
float
hue
=
rnd
.
nextFloat
();
// Saturation between 0.1 and 0.3
final
float
saturation
=
(
rnd
.
nextInt
(
2000
)
+
1000
)
/
10000
f
;
final
float
luminance
=
0.9f
;
randomColor
=
Color
.
getHSBColor
(
hue
,
saturation
,
luminance
);
randomColor
=
new
Color
(
rnd
.
nextFloat
(),
rnd
.
nextFloat
(),
rnd
.
nextFloat
());
clusterColors
.
put
(
c
,
randomColor
);
}
g2
.
setColor
(
randomColor
);
for
(
ClusterMember
m
:
c
.
members
)
{
if
(
lastPos
==
null
)
lastPos
=
m
.
pos
;
g2
.
drawLine
((
int
)
lastPos
.
getX
(),
(
int
)
lastPos
.
getY
(),
(
int
)
m
.
pos
.
getX
(),
(
int
)
m
.
pos
.
getY
());
lastPos
=
m
.
pos
;
}
}
}
}
}
/*
* 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
;
import
java.awt.Color
;
import
java.awt.Font
;
import
java.awt.Graphics
;
import
java.awt.Graphics2D
;
import
java.util.ArrayList
;
import
java.util.Collection
;
import
java.util.Map
;
import
java.util.Random
;
import
java.util.Vector
;
import
java.util.WeakHashMap
;
import
javax.swing.JComponent
;
import
com.google.common.collect.Maps
;
import
de.tud.kom.p2psim.api.scenario.ConfigurationException
;
import
de.tud.kom.p2psim.api.topology.movement.MovementSupported
;
import
de.tud.kom.p2psim.api.topology.waypoints.WaypointModel
;
import
de.tud.kom.p2psim.impl.scenario.simcfg2.annotations.After
;
import
de.tud.kom.p2psim.impl.scenario.simcfg2.annotations.Configure
;
import
de.tud.kom.p2psim.impl.topology.PositionVector
;
import
de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector
;
import
de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector.DisplayString
;
import
de.tud.kom.p2psim.impl.topology.waypoints.graph.StrongWaypoint
;
import
de.tud.kom.p2psim.impl.topology.waypoints.graph.Waypoint
;
import
de.tudarmstadt.maki.simonstrator.api.Randoms
;
import
de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor
;
/**
* The SLAW Movement Model is based on the implementation in
* BonnMotion Copyright (C) 2002-2010 University of Bonn
* by Zia-Ul-Huda, Gufron Atokhojaev and Florian Schmitt
*
* @author Fabio Zöllner
* @version 1.0, 09.04.2012
*/
public
class
SLAWMovementModel
extends
AbstractWaypointMovementModel
{
private
static
final
int
powerlaw_step
=
1
;
private
static
int
levy_scale_factor
=
1
;
private
static
int
powerlaw_mode
=
1
;
protected
Random
rand
=
Randoms
.
getRandom
(
SLAWMovementModel
.
class
);
protected
Random
xorShiftRandom
=
new
XorShiftRandom
(
rand
.
nextLong
());
protected
double
beta
=
1
;
protected
long
minpause
=
10
;
protected
long
maxpause
=
50
;
protected
double
dist_alpha
=
3
;
protected
double
cluster_range
;
protected
int
cluster_ratio
=
3
;
protected
int
waypoint_ratio
=
3
;
protected
Cluster
[]
clusters
;
protected
WeakHashMap
<
MovementSupported
,
SLAWMovementInformation
>
movementInformation
;
// TODO: minpause and maxpause are currently in microseconds... should be
// changed to minutes
@XMLConfigurableConstructor
({
"worldX"
,
"worldY"
,
"minpause"
,
"maxpause"
,
"beta"
,
"distAlpha"
,
"clusterRange"
,
"clusterRatio"
,
"waypointRatio"
})
public
SLAWMovementModel
(
double
worldX
,
double
worldY
,
long
minpause
,
long
maxpause
,
double
beta
,
double
distAlpha
,
double
clusterRange
,
int
clusterRatio
,
int
waypointRatio
)
{
super
(
worldX
,
worldY
);
this
.
minpause
=
minpause
;
this
.
maxpause
=
maxpause
;
this
.
dist_alpha
=
distAlpha
;
this
.
cluster_range
=
clusterRange
;
this
.
cluster_ratio
=
clusterRatio
;
this
.
waypoint_ratio
=
waypointRatio
;
movementInformation
=
new
WeakHashMap
<
MovementSupported
,
SLAWMovementModel
.
SLAWMovementInformation
>();
}
@Override
public
void
nextPosition
(
MovementSupported
node
)
{
// These variables have values same as in the matlab implementation of
// SLAW model by Seongik Hong, NCSU, US (3/10/2009)
if
(
waypointModel
==
null
)
{
throw
new
ConfigurationException
(
"SLAWMovementModel requires a valid waypoint model which hasn't been provided, cannot execute"
);
}
SLAWMovementInformation
mi
=
movementInformation
.
get
(
node
);
if
(
mi
==
null
)
{
//System.err.println("Creating new movement information for node " + node.toString());
mi
=
new
SLAWMovementInformation
();
movementInformation
.
put
(
node
,
mi
);
}
// log.debug("Selecting new destination for node " + mi.nodeId);
// get random clusters and waypoints
if
(
mi
.
clts
==
null
)
mi
.
clts
=
make_selection
(
clusters
,
null
,
false
);
// get random clusters and waypoints
if
(
mi
.
wlist
==
null
)
mi
.
wlist
=
get_waypoint_list
(
mi
.
clts
);
// random source node
if
(
mi
.
srcIndex
==
-
1
)
{
int
old
=
mi
.
srcIndex
;
mi
.
srcIndex
=
(
int
)
Math
.
floor
(
randomNextDouble
()
*
mi
.
wlist
.
length
);
//System.err.println("mi.srcIndex was " + old + " but is now " + mi.srcIndex);
// Set the initial position to the selected waypoint
nextDestination
(
node
,
mi
.
wlist
[
mi
.
srcIndex
].
pos
,
0
);
//System.err.println("Default case? 0");
return
;
}
if
(
mi
.
dstIndex
!=
-
1
)
{
mi
.
srcIndex
=
mi
.
dstIndex
;
}
int
count
=
0
;
PositionVector
source
=
new
PositionVector
(
node
.
getRealPosition
()
.
getX
(),
node
.
getRealPosition
().
getY
());
mi
.
wlist
[
mi
.
srcIndex
].
is_visited
=
true
;
// get list of not visited locations
for
(
int
i
=
0
;
i
<
mi
.
wlist
.
length
;
i
++)
{
if
(!
mi
.
wlist
[
i
].
is_visited
)
{
count
++;
}
}
// if all waypoints are visited then select new clusters and
// waypoints. Destructive mode of original SLAW matlab
// implementation by Seongik Hong, NCSU, US (3/10/2009)
while
(
count
==
0
)
{
mi
.
clts
=
make_selection
(
clusters
,
mi
.
clts
,
true
);
mi
.
wlist
=
get_waypoint_list
(
mi
.
clts
);
for
(
int
i
=
0
;
i
<
mi
.
wlist
.
length
;
i
++)
{
if
(!
mi
.
wlist
[
i
].
is_visited
)
{
if
(
source
.
d
istance
To
(
mi
.
wlist
[
i
].
pos
)
!=
0.0
)
{
count
++;
}
else
{
mi
.
wlist
[
i
].
is_visited
=
true
;
}
}
}
}
ClusterMember
[]
not_visited
=
new
ClusterMember
[
count
];
count
=
0
;
for
(
int
i
=
0
;
i
<
mi
.
wlist
.
length
;
i
++)
{
if
(!
mi
.
wlist
[
i
].
is_visited
)
{
not_visited
[
count
++]
=
mi
.
wlist
[
i
];
}
}
// get distance from source to all remaining waypoints
double
[]
dist
=
new
double
[
not_visited
.
length
];
for
(
int
i
=
0
;
i
<
not_visited
.
length
;
i
++)
{
dist
[
i
]
=
source
.
d
istance
To
(
not_visited
[
i
].
pos
);
}
double
[]
weights
=
new
double
[
not_visited
.
length
];
// cumulative sum of distance weights
for
(
int
i
=
0
;
i
<
weights
.
length
;
i
++)
{
weights
[
i
]
=
0
;
for
(
int
j
=
0
;
j
<=
i
;
j
++)
{
weights
[
i
]
+=
1
/
Math
.
pow
(
dist
[
j
],
this
.
dist_alpha
);
}
}
for
(
int
i
=
0
;
i
<
weights
.
length
;
i
++)
{
weights
[
i
]
/=
weights
[
weights
.
length
-
1
];
}
double
r
=
randomNextDouble
();
int
index
;
for
(
index
=
0
;
index
<
weights
.
length
;
index
++)
{
if
(
r
<
weights
[
index
])
{
break
;
}
}
// select the next destination
for
(
int
i
=
0
;
i
<
mi
.
wlist
.
length
;
i
++)
{
if
(
mi
.
wlist
[
i
].
pos
.
equals
(
not_visited
[
index
].
pos
))
{
mi
.
dstIndex
=
i
;
double
pauseTime
=
random_powerlaw
(
powerlaw_step
,
levy_scale_factor
,
powerlaw_mode
)[
0
];
//System.err.println("Next pause time is " + pauseTime);
nextDestination
(
node
,
mi
.
wlist
[
mi
.
dstIndex
].
pos
,
(
long
)
pauseTime
);
break
;
}
}
// change destination to next source
// mi.srcIndex = mi.dstIndex;
}
/**
* returns aggregated list of cluster members from all passed clusters
*
* @param clusters
* clusters list
* @return array of ClusterMember type
*/
protected
ClusterMember
[]
get_waypoint_list
(
Cluster
[]
clusters
)
{
ArrayList
<
ClusterMember
>
result
=
new
ArrayList
<
ClusterMember
>();
for
(
Cluster
cluster
:
clusters
)
{
for
(
ClusterMember
clustermember
:
cluster
.
members
)
{
result
.
add
(
clustermember
);
}
}
return
result
.
toArray
(
new
ClusterMember
[
0
]);
}
/**
* makes the selection of new clusters and waypoints from passed clusters
*
* @param clusters
* array of clusters to make selection from
* @param change_one
* changes only one of the clusters randomly and then selects new
* waypoints from all clusters
* @return array of selected clusters
*/
protected
Cluster
[]
make_selection
(
Cluster
[]
clusters
,
Cluster
[]
cur_list
,
boolean
change_one
)
{
ArrayList
<
Integer
>
cluster_selection
;
if
(!
change_one
)
{
// Number of clusters to select
int
num_clusters
=
(
int
)
Math
.
ceil
((
double
)
clusters
.
length
/
(
double
)
cluster_ratio
);
cluster_selection
=
new
ArrayList
<
Integer
>(
num_clusters
);
for
(
int
i
=
0
;
i
<
num_clusters
;
i
++)
{
cluster_selection
.
add
(
i
,
-
1
);
}
int
[]
total_list
=
new
int
[
waypointModel
.
getNumberOfWaypoints
(
StrongWaypoint
.
class
)];
int
counter
=
0
;
// probability array
for
(
int
i
=
0
;
i
<
clusters
.
length
;
i
++)
{
for
(
int
j
=
0
;
j
<
clusters
[
i
].
members
.
length
;
j
++)
{
total_list
[
counter
++]
=
clusters
[
i
].
index
;
}
}
// select clusters randomly with weights
int
t
=
total_list
[(
int
)
Math
.
floor
(
this
.
randomNextDouble
()
*
waypointModel
.
getNumberOfWaypoints
(
StrongWaypoint
.
class
))];
for
(
int
i
=
0
;
i
<
cluster_selection
.
size
();
i
++)
{
while
(
cluster_selection
.
contains
(
t
))
{
t
=
total_list
[(
int
)
Math
.
floor
(
this
.
randomNextDouble
()
*
waypointModel
.
getNumberOfWaypoints
(
StrongWaypoint
.
class
))];
}
cluster_selection
.
set
(
i
,
t
);
}
}
else
{
// just need to change one randomly
cluster_selection
=
new
ArrayList
<
Integer
>(
cur_list
.
length
);
for
(
Cluster
cluster
:
cur_list
)
{
cluster_selection
.
add
(
cluster
.
index
);
}
}
// change one cluster without weight consideration
cluster_selection
=
change_one_random
(
cluster_selection
,
clusters
.
length
);
// select waypoints from selected clusters.
Cluster
[]
result
=
new
Cluster
[
cluster_selection
.
size
()];
double
numberOfWaypoints
;
Cluster
cluster_iterator
=
null
;
for
(
int
i
=
0
;
i
<
cluster_selection
.
size
();
i
++)
{
// find Cluster object in clusters array
for
(
int
j
=
0
;
j
<
clusters
.
length
;
j
++)
{
if
(
cluster_selection
.
get
(
i
)
==
clusters
[
j
].
index
)
{
cluster_iterator
=
clusters
[
j
];
break
;
}
}
result
[
i
]
=
new
Cluster
(
cluster_iterator
.
index
);
numberOfWaypoints
=
(
double
)
cluster_iterator
.
members
.
length
/
(
double
)
this
.
waypoint_ratio
;
int
[]
waypoint_selection
;
if
(
numberOfWaypoints
<
1
)
{
waypoint_selection
=
select_uniformly
(
cluster_iterator
.
members
.
length
,
1
);
}
else
{
if
(
this
.
randomNextDouble
()
<
numberOfWaypoints
%
1
)
{
waypoint_selection
=
select_uniformly
(
cluster_iterator
.
members
.
length
,
(
int
)
(
Math
.
floor
(
numberOfWaypoints
)
+
1
));
}
else
{
waypoint_selection
=
select_uniformly
(
cluster_iterator
.
members
.
length
,
(
int
)
Math
.
floor
(
numberOfWaypoints
));
}
}
result
[
i
].
members
=
new
ClusterMember
[
waypoint_selection
.
length
];
for
(
int
j
=
0
;
j
<
waypoint_selection
.
length
;
j
++)
{
result
[
i
].
members
[
j
]
=
cluster_iterator
.
members
[
waypoint_selection
[
j
]]
.
clone
();
}
}
return
result
;
}
/**
* changes one of the numbers randomly from the passed array. The new
* changed number is in range [1,n]
*
* @param list
* array of integers
* @param n
* range of numbers
* @return array of integers with one element changed randomly
*/
protected
ArrayList
<
Integer
>
change_one_random
(
ArrayList
<
Integer
>
list
,
int
n
)
{
int
index
=
(
int
)
Math
.
floor
(
this
.
randomNextDouble
()
*
list
.
size
());
int
value
=
(
int
)
Math
.
floor
(
this
.
randomNextDouble
()
*
n
)
+
1
;
while
(
list
.
contains
(
value
))
{
value
=
(
int
)
Math
.
floor
(
this
.
randomNextDouble
()
*
n
)
+
1
;
}
list
.
set
(
index
,
value
);
return
list
;
}
/**
* selects k numbers out of n uniformly
*
* @param n
* @param k
* @return array of k integers
*/
protected
int
[]
select_uniformly
(
int
n
,
int
k
)
{
if
(
k
>
n
)
throw
new
RuntimeException
(
"SLAW.select_uniformaly(): value of k must not be larger than n."
);
int
t
;
int
[]
list
=
new
int
[
k
];
for
(
int
i
=
0
;
i
<
k
;
i
++)
{
list
[
i
]
=
-
1
;
}
boolean
is_in
;
int
count
=
0
;
while
(
count
<
k
)
{
is_in
=
false
;
t
=
(
int
)
Math
.
floor
(
this
.
randomNextDouble
()
*
n
);
for
(
int
i
=
0
;
i
<
list
.
length
;
i
++)
{
if
(
list
[
i
]
==
t
)
{
is_in
=
true
;
break
;
}
}
if
(!
is_in
)
{
list
[
count
++]
=
t
;
}
}
return
list
;
}
/**
* Generates random values from power-law distribution.
*
* @param powerlaw_step
* the total numbers to generate. Returns an array of this size.
* @param levy_scale_factor
* levy scaling factor of distribution
* @param powerlaw_mode
* 1: stabrnd 2: reverse computation
* @return double array of powerlaw_step length
**/
protected
double
[]
random_powerlaw
(
int
powerlaw_step
,
int
levy_scale_factor
,
int
powerlaw_mode
)
{
double
[]
result
=
new
double
[
powerlaw_step
];
for
(
int
xi
=
0
;
xi
<
powerlaw_step
;)
{
if
(
powerlaw_mode
==
1
)
{
// stabrnd
double
[]
stabrnd_result
=
stabrnd
(
0
,
levy_scale_factor
,
0
,
powerlaw_step
);
ArrayList
<
Double
>
temp
=
new
ArrayList
<
Double
>();
for
(
int
i
=
0
;
i
<
stabrnd_result
.
length
;
i
++)
{
if
(
stabrnd_result
[
i
]
>
this
.
minpause
&&
stabrnd_result
[
i
]
<
this
.
maxpause
)
{
temp
.
add
(
new
Double
(
stabrnd_result
[
i
]));
}
}
if
(
temp
.
size
()
>
0
)
{
for
(
Double
d
:
temp
)
{
result
[
xi
++]
=
d
;
if
(
xi
>
powerlaw_step
)
break
;
}
}
}
else
if
(
powerlaw_mode
==
2
)
{
// reverse computation
double
temp
=
Math
.
pow
(
randomNextDouble
(),
1
/
(
1
-
(
this
.
beta
+
1
)))
*
this
.
minpause
;
if
(
temp
<
this
.
maxpause
)
{
result
[
xi
++]
=
temp
;
}
}
}
return
result
;
}
/**
* Returns array of randomly generated n numbers based on the method of J.M.
* Chambers, C.L. Mallows and B.W. Stuck,
* "A Method for Simulating Stable Random Variables," JASA 71 (1976): 340-4.
*
* @param b
* beta factor
* @param levy_scale_factor
* @param delta
* @param n
* count of random numbers to generate
* @return double array of n length
*/
private
double
[]
stabrnd
(
double
b
,
int
levy_scale_factor
,
double
delta
,
int
n
)
{
if
(
this
.
beta
<
.
1
||
this
.
beta
>
2
)
{
throw
new
RuntimeException
(
"SLAW.stabrnd(): Beta value must be in [.1,2]"
);
}
if
(
Math
.
abs
(
b
)
>
1
)
{
throw
new
RuntimeException
(
"SLAW.stabrnd(): local beta value must be in [-1,1]"
);
}
// Generate exponential w and uniform phi
double
[]
w
=
new
double
[
n
];
double
[]
x
=
new
double
[
n
];
double
[]
phi
=
new
double
[
n
];
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
w
[
i
]
=
-
Math
.
log
(
randomNextDouble
());
phi
[
i
]
=
(
randomNextDouble
()
-
0.5
)
*
Math
.
PI
;
}
// Gaussian case (Box-Muller)
if
(
this
.
beta
==
2
)
{
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
x
[
i
]
=
2
*
Math
.
sqrt
(
w
[
i
])
*
Math
.
sin
(
phi
[
i
]);
x
[
i
]
=
delta
+
levy_scale_factor
*
x
[
i
];
}
}
else
if
(
b
==
0
)
{
// Symmetrical cases
if
(
this
.
beta
==
1
)
{
// Cauchy case
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
x
[
i
]
=
Math
.
tan
(
phi
[
i
]);
}
}
else
{
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
x
[
i
]
=
Math
.
pow
(
Math
.
cos
((
1
-
this
.
beta
)
*
phi
[
i
])
/
w
[
i
],
1
/
this
.
beta
-
1
)
*
Math
.
sin
(
this
.
beta
*
phi
[
i
])
/
Math
.
pow
(
Math
.
cos
(
phi
[
i
]),
1
/
this
.
beta
);
}
}
}
else
{
// General cases
double
cosphi
,
zeta
,
aphi
,
a1phi
,
bphi
;
if
(
Math
.
abs
(
this
.
beta
-
1
)
>
0.00000001
)
{
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
cosphi
=
Math
.
cos
(
phi
[
i
]);
zeta
=
b
*
Math
.
tan
(
Math
.
PI
*
this
.
beta
/
2
);
aphi
=
this
.
beta
*
phi
[
i
];
a1phi
=
(
1
-
this
.
beta
)
*
phi
[
i
];
x
[
i
]
=
(
Math
.
sin
(
aphi
)
+
zeta
*
Math
.
cos
(
aphi
))
/
cosphi
*
Math
.
pow
(
(
Math
.
cos
(
a1phi
)
+
zeta
*
Math
.
sin
(
a1phi
))
/
(
w
[
i
]
*
cosphi
),
(
1
-
this
.
beta
)
/
this
.
beta
);
}
}
else
{
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
cosphi
=
Math
.
cos
(
phi
[
i
]);
bphi
=
Math
.
PI
/
2
+
b
*
phi
[
i
];
x
[
i
]
=
2
/
Math
.
PI
*
(
bphi
*
Math
.
tan
(
phi
[
i
])
-
b
*
Math
.
log
((
Math
.
PI
/
2
)
*
w
[
i
]
*
cosphi
/
bphi
));
}
if
(
this
.
beta
!=
1
)
{
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
x
[
i
]
+=
b
*
Math
.
tan
(
Math
.
PI
*
this
.
beta
/
2
);
}
}
}
for
(
int
i
=
0
;
i
<
n
;
i
++)
{
x
[
i
]
=
delta
+
levy_scale_factor
*
x
[
i
];
}
}
return
x
;
}
protected
double
randomNextDouble
(
final
double
value
)
{
return
(
randomNextDouble
()
*
value
);
}
protected
double
randomNextDouble
()
{
return
xorShiftRandom
.
nextDouble
();
//return rand.nextDouble();
}
private
static
final
class
Cluster
{
public
ClusterMember
[]
members
;
public
int
index
=
-
1
;
public
Cluster
(
int
_index
)
{
index
=
_index
;
}
public
Cluster
(
int
_index
,
ClusterMember
[]
_members
)
{
index
=
_index
;
members
=
_members
;
}
public
String
toString
()
{
return
"Cluster[index: "
+
index
+
"]"
;
}
}
private
static
final
class
ClusterMember
{
public
int
cluster_index
=
-
1
;
public
PositionVector
pos
;
public
boolean
is_visited
=
false
;
public
ClusterMember
(
int
_cluster_index
,
PositionVector
_pos
,
boolean
_is_visited
)
{
this
.
cluster_index
=
_cluster_index
;
this
.
is_visited
=
_is_visited
;
this
.
pos
=
_pos
;
}
public
ClusterMember
clone
()
{
return
new
ClusterMember
(
this
.
cluster_index
,
this
.
pos
,
this
.
is_visited
);
}
}
private
static
class
SLAWMovementInformation
{
public
SLAWMovementInformation
()
{
//
}
public
int
srcIndex
=
-
1
;
public
int
dstIndex
=
-
1
;
public
Cluster
[]
clts
=
null
;
public
ClusterMember
[]
wlist
=
null
;
}
@Configure
()
@After
(
required
={
WaypointModel
.
class
})
public
void
_verifyConfig
()
{
/*
* FIXME: get rid of this method!
*/
Collection
<
Waypoint
>
strongWaypoints
=
waypointModel
.
getWaypoints
(
StrongWaypoint
.
class
);
if
(
strongWaypoints
==
null
)
throw
new
ConfigurationException
(
"The configured waypoint model hasn't generated any strong waypoints that are required by the SLAWMovementModel."
);
clusters
=
generate_clusters
(
waypointModel
);
if
(
clusters
.
length
<=
2
)
{
throw
new
ConfigurationException
(
"The SLAW movement model could only generate 1 cluster, please adjust the cluster range parameter"
);
}
VisualizationInjector
.
injectComponent
(
"Clusters"
,
-
1
,
new
SLAWClusterOverlay
(
clusters
,
waypointModel
),
false
);
VisualizationInjector
.
addDisplayString
(
new
DisplayString
()
{
@Override
public
String
getDisplayString
()
{
int
clusterWps
=
0
;
for
(
Cluster
c
:
clusters
)
{
clusterWps
+=
c
.
members
.
length
;
}
return
"SLAW[Clusters: "
+
clusters
.
length
+
", WPs in clusters: "
+
clusterWps
+
", Avg per cluster: "
+
(
clusterWps
/
clusters
.
length
)
+
"]"
;
}
});
}
@Override
public
void
setWaypointModel
(
WaypointModel
waypointModel
)
{
super
.
setWaypointModel
(
waypointModel
);
}
/**
* generates clusters
*
* @param waypoints
* list of waypoint s
* @return array of clusters
*/
protected
Cluster
[]
generate_clusters
(
WaypointModel
model
)
{
Vector
<
PositionVector
>
all_points
=
new
Vector
<
PositionVector
>();
Vector
<
PositionVector
>
new_points
=
new
Vector
<
PositionVector
>();
Vector
<
PositionVector
>
members
=
new
Vector
<
PositionVector
>();
Vector
<
Cluster
>
clusters
=
new
Vector
<
Cluster
>();
Vector
<
ClusterMember
>
cluster_members
=
new
Vector
<
ClusterMember
>();
Waypoint
[]
waypoints
=
new
Waypoint
[]{};
Collection
<
Waypoint
>
waypointList
=
model
.
getWaypoints
(
StrongWaypoint
.
class
);
if
(
waypointList
==
null
)
throw
new
ConfigurationException
(
"The configured waypoint model didn't generate any strong waypoints"
);
waypoints
=
waypointList
.
toArray
(
waypoints
);
for
(
int
i
=
0
;
i
<
waypoints
.
length
;
i
++)
{
all_points
.
add
(
waypoints
[
i
].
getPosition
());
}
PositionVector
init_pos
=
null
;
int
cluster_count
=
0
;
while
(!
all_points
.
isEmpty
())
{
if
(
init_pos
==
null
)
{
init_pos
=
all_points
.
firstElement
();
all_points
.
remove
(
0
);
members
.
add
(
init_pos
);
}
for
(
int
i
=
0
;
i
<
all_points
.
size
();
i
++)
{
PositionVector
new_pos
=
all_points
.
elementAt
(
i
);
if
(
init_pos
.
d
istance
To
(
new_pos
)
<=
this
.
cluster_range
)
{
new_points
.
add
(
new_pos
);
members
.
add
(
new_pos
);
all_points
.
remove
(
i
--);
}
}
// for all_points
if
(!
new_points
.
isEmpty
()
&&
!
all_points
.
isEmpty
())
{
init_pos
=
new_points
.
firstElement
();
new_points
.
remove
(
0
);
}
else
{
for
(
int
i
=
0
;
i
<
members
.
size
();
i
++)
{
cluster_members
.
add
(
new
ClusterMember
(
cluster_count
,
members
.
elementAt
(
i
),
false
));
}
clusters
.
add
(
new
Cluster
(++
cluster_count
,
cluster_members
.
toArray
(
new
ClusterMember
[
0
])));
cluster_members
.
clear
();
new_points
.
clear
();
members
.
clear
();
init_pos
=
null
;
}
}
// while all_points
return
clusters
.
toArray
(
new
Cluster
[
0
]);
}
private
class
SLAWClusterOverlay
extends
JComponent
{
private
Map
<
Cluster
,
Color
>
clusterColors
=
Maps
.
newHashMap
();
private
Cluster
[]
clusters
;
private
Random
rnd
=
new
Random
(
System
.
nanoTime
());
private
WaypointModel
model
;
public
SLAWClusterOverlay
(
Cluster
[]
clusters
,
WaypointModel
model
)
{
super
();
this
.
clusters
=
clusters
;
this
.
model
=
model
;
PositionVector
dimension
=
model
.
getMap
().
getDimensions
();
setBounds
(
0
,
0
,
(
int
)
dimension
.
getX
(),
(
int
)
dimension
.
getY
());
setOpaque
(
false
);
setVisible
(
true
);
}
@Override
protected
void
paintComponent
(
Graphics
g
)
{
super
.
paintComponent
(
g
);
Graphics2D
g2
=
(
Graphics2D
)
g
;
g2
.
setFont
(
new
Font
(
"SansSerif"
,
Font
.
PLAIN
,
9
));
for
(
Cluster
c
:
clusters
)
{
PositionVector
lastPos
=
null
;
Color
randomColor
=
clusterColors
.
get
(
c
);
if
(
randomColor
==
null
)
{
final
float
hue
=
rnd
.
nextFloat
();
// Saturation between 0.1 and 0.3
final
float
saturation
=
(
rnd
.
nextInt
(
2000
)
+
1000
)
/
10000
f
;
final
float
luminance
=
0.9f
;
randomColor
=
Color
.
getHSBColor
(
hue
,
saturation
,
luminance
);
randomColor
=
new
Color
(
rnd
.
nextFloat
(),
rnd
.
nextFloat
(),
rnd
.
nextFloat
());
clusterColors
.
put
(
c
,
randomColor
);
}
g2
.
setColor
(
randomColor
);
for
(
ClusterMember
m
:
c
.
members
)
{
if
(
lastPos
==
null
)
lastPos
=
m
.
pos
;
g2
.
drawLine
((
int
)
lastPos
.
getX
(),
(
int
)
lastPos
.
getY
(),
(
int
)
m
.
pos
.
getX
(),
(
int
)
m
.
pos
.
getY
());
lastPos
=
m
.
pos
;
}
}
}
}
}
src/de/tud/kom/p2psim/impl/topology/movement/TargetMovement.java
View file @
0c80da45
...
...
@@ -59,7 +59,7 @@ public class TargetMovement extends AbstractMovementModel {
PositionVector
rVec
=
new
PositionVector
(
x
,
y
);
rVec
=
rVec
.
minus
(
pos
);
double
length
=
rVec
.
getD
istance
(
new
PositionVector
(
0
,
0
));
double
length
=
rVec
.
d
istance
To
(
new
PositionVector
(
0
,
0
));
PositionVector
vec
;
if
(
length
>
distance
)
{
...
...
src/de/tud/kom/p2psim/impl/topology/movement/local/LinearMovement.java
View file @
0c80da45
/*
* 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.local
;
import
de.tud.kom.p2psim.api.topology.movement.MovementSupported
;
import
de.tud.kom.p2psim.impl.topology.PositionVector
;
import
de.tud.kom.p2psim.impl.util.Either
;
import
de.tud.kom.p2psim.impl.util.Left
;
/**
* This movement strategy moves directly towards a given destination without
* regard for obstacles or way points.
*
* @author Fabio Zöllner
* @version 1.0, 09.04.2012
*/
public
class
LinearMovement
extends
AbstractLocalMovementStrategy
{
public
Either
<
PositionVector
,
Boolean
>
nextPosition
(
MovementSupported
comp
,
PositionVector
destination
)
{
PositionVector
newPosition
;
if
(
destination
.
getDistance
(
comp
.
getRealPosition
())
<
getMovementSpeed
(
comp
))
{
newPosition
=
destination
.
clone
();
}
else
{
newPosition
=
comp
.
getRealPosition
().
moveStep
(
destination
,
getMovementSpeed
(
comp
));
}
return
new
Left
<
PositionVector
,
Boolean
>(
newPosition
);
}
}
/*
* 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.local
;
import
de.tud.kom.p2psim.api.topology.movement.MovementSupported
;
import
de.tud.kom.p2psim.impl.topology.PositionVector
;
import
de.tud.kom.p2psim.impl.util.Either
;
import
de.tud.kom.p2psim.impl.util.Left
;
/**
* This movement strategy moves directly towards a given destination without
* regard for obstacles or way points.
*
* @author Fabio Zöllner
* @version 1.0, 09.04.2012
*/
public
class
LinearMovement
extends
AbstractLocalMovementStrategy
{
public
Either
<
PositionVector
,
Boolean
>
nextPosition
(
MovementSupported
comp
,
PositionVector
destination
)
{
PositionVector
newPosition
;
if
(
destination
.
distanceTo
(
comp
.
getRealPosition
())
<
getMovementSpeed
(
comp
))
{
newPosition
=
destination
.
clone
();
}
else
{
newPosition
=
comp
.
getRealPosition
().
moveStep
(
destination
,
getMovementSpeed
(
comp
));
}
return
new
Left
<
PositionVector
,
Boolean
>(
newPosition
);
}
}
src/de/tud/kom/p2psim/impl/topology/movement/local/OnlineMapQuestMovement.java
View file @
0c80da45
...
...
@@ -25,6 +25,7 @@ import java.io.InputStream;
import
java.net.MalformedURLException
;
import
java.net.URL
;
import
java.util.HashMap
;
import
org.apache.commons.io.IOUtils
;
import
org.json.JSONArray
;
import
org.json.JSONException
;
...
...
@@ -34,12 +35,13 @@ import com.graphhopper.util.PointList;
import
com.graphhopper.util.shapes.GHPoint
;
import
com.graphhopper.util.shapes.GHPoint3D
;
import
de.tud.kom.p2psim.api.topology.Topology
;
import
de.tud.kom.p2psim.api.topology.movement.MovementSupported
;
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.GPSCalculation
;
import
de.tud.kom.p2psim.impl.util.Either
;
import
de.tud.kom.p2psim.impl.util.Left
;
import
de.tudarmstadt.maki.simonstrator.api.Binder
;
/**
* This movement strategy uses the data from MapQuest
...
...
@@ -63,7 +65,8 @@ public class OnlineMapQuestMovement extends AbstractLocalMovementStrategy {
//if the distance is smaller than the given one, the node will choose the next point in the list
public
OnlineMapQuestMovement
()
{
this
.
worldDimensions
=
Topology
.
getWorldDimension
();
this
.
worldDimensions
=
Binder
.
getComponentOrNull
(
Topology
.
class
)
.
getWorldDimensions
();
latLeft
=
GPSCalculation
.
getLatLower
();
latRight
=
GPSCalculation
.
getLatUpper
();
lonLeft
=
GPSCalculation
.
getLonLeft
();
...
...
@@ -74,7 +77,8 @@ public class OnlineMapQuestMovement extends AbstractLocalMovementStrategy {
PositionVector
destination
)
{
PositionVector
newPosition
=
null
;
if
(
destination
.
getDistance
(
comp
.
getRealPosition
())
<
getMovementSpeed
(
comp
))
{
if
(
destination
.
distanceTo
(
comp
.
getRealPosition
())
<
getMovementSpeed
(
comp
))
{
newPosition
=
destination
.
clone
();
}
else
{
//if not set already for this node or new destination is different than last one
...
...
src/de/tud/kom/p2psim/impl/topology/movement/local/RealWorldStreetsMovement.java
View file @
0c80da45
...
...
@@ -31,12 +31,13 @@ import com.graphhopper.util.PointList;
import
com.graphhopper.util.shapes.GHPoint
;
import
com.graphhopper.util.shapes.GHPoint3D
;
import
de.tud.kom.p2psim.api.topology.Topology
;
import
de.tud.kom.p2psim.api.topology.movement.MovementSupported
;
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.GPSCalculation
;
import
de.tud.kom.p2psim.impl.util.Either
;
import
de.tud.kom.p2psim.impl.util.Left
;
import
de.tudarmstadt.maki.simonstrator.api.Binder
;
/**
* This movement strategy uses the data from osm and navigates the nodes throught streets to the destination
...
...
@@ -68,7 +69,8 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
private
double
tolerance
=
1
;
public
RealWorldStreetsMovement
()
{
this
.
worldDimensions
=
Topology
.
getWorldDimension
();
this
.
worldDimensions
=
Binder
.
getComponentOrNull
(
Topology
.
class
)
.
getWorldDimensions
();
latLeft
=
GPSCalculation
.
getLatLower
();
latRight
=
GPSCalculation
.
getLatUpper
();
lonLeft
=
GPSCalculation
.
getLonLeft
();
...
...
@@ -90,7 +92,8 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
if
(!
init
)
init
();
PositionVector
newPosition
=
null
;
if
(
destination
.
getDistance
(
comp
.
getRealPosition
())
<
getMovementSpeed
(
comp
))
{
if
(
destination
.
distanceTo
(
comp
.
getRealPosition
())
<
getMovementSpeed
(
comp
))
{
newPosition
=
destination
.
clone
();
}
else
{
//if not set already for this node or new destination is different than last one
...
...
src/de/tud/kom/p2psim/impl/topology/movement/local/ShortestPathWaypointMovement.java
View file @
0c80da45
/*
* 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.local
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.WeakHashMap
;
import
de.tud.kom.p2psim.api.topology.movement.MovementSupported
;
import
de.tud.kom.p2psim.impl.topology.PositionVector
;
import
de.tud.kom.p2psim.impl.topology.waypoints.graph.Path
;
import
de.tud.kom.p2psim.impl.topology.waypoints.graph.Waypoint
;
import
de.tud.kom.p2psim.impl.topology.waypoints.graph.WeakWaypoint
;
import
de.tud.kom.p2psim.impl.util.Either
;
import
de.tud.kom.p2psim.impl.util.Left
;
import
de.tud.kom.p2psim.impl.util.Right
;
/**
* This movement strategy uses the getShortestPath method implemented by the
* waypoint model and moves along path given by this method.
*
* @author Fabio Zöllner
* @version 1.0, 10.04.2012
*/
public
class
ShortestPathWaypointMovement
extends
AbstractLocalMovementStrategy
{
// Contains the shortest path and the currently used path
protected
final
WeakHashMap
<
MovementSupported
,
Integer
>
currentPath
=
new
WeakHashMap
<
MovementSupported
,
Integer
>();
protected
final
WeakHashMap
<
MovementSupported
,
List
<
Waypoint
>>
dstPaths
=
new
WeakHashMap
<
MovementSupported
,
List
<
Waypoint
>>();
// Used to check if the destination was altered by the waypoint movement
// model
protected
WeakHashMap
<
MovementSupported
,
PositionVector
>
currentDestination
=
new
WeakHashMap
<
MovementSupported
,
PositionVector
>();
/**
* Calculates the next position of the given movement supported component by
* using the shortest path to the waypoint closest to the destination.
*/
@Override
public
Either
<
PositionVector
,
Boolean
>
nextPosition
(
MovementSupported
comp
,
PositionVector
destination
)
{
if
(
currentDestination
.
get
(
comp
)
==
null
||
!
currentDestination
.
get
(
comp
).
equals
(
destination
))
{
currentDestination
.
put
(
comp
,
destination
);
calculateNextMovementPath
(
comp
,
destination
);
// If the list of the shortest path is empty the destination is the
// same
// as the current position. Thus tell the abstract waypoint model
// that we
// reached the destination.
if
(
dstPaths
.
get
(
comp
).
size
()
==
0
)
{
return
new
Right
<
PositionVector
,
Boolean
>(
true
);
}
}
int
currentPathIdx
=
currentPath
.
get
(
comp
);
Waypoint
currentWaypoint
=
dstPaths
.
get
(
comp
).
get
(
currentPathIdx
);
double
speed
=
getMovementSpeed
(
comp
);
PositionVector
newPosition
=
comp
.
getRealPosition
().
moveStep
(
currentWaypoint
.
getPosition
(),
speed
);
if
(
destinationWaypointReached
(
currentWaypoint
,
newPosition
,
speed
))
{
// We reached the next waypoint on the path to the destination
// move to the next waypoint
currentPath
.
put
(
comp
,
++
currentPathIdx
);
// The destination waypoint has been reached, tell the abstract
// waypoint model
if
(
dstPaths
.
get
(
comp
).
size
()
<=
currentPathIdx
)
{
currentPath
.
put
(
comp
,
--
currentPathIdx
);
return
new
Right
<
PositionVector
,
Boolean
>(
true
);
}
}
return
new
Left
<
PositionVector
,
Boolean
>(
newPosition
);
}
/**
* Finds the closest waypoints to the current position of the movement
* supported component as well as the final destination and searches for a
* path between the two waypoints using the underlying network of
* WeakWaypoints.
*
* @param comp
* @param finalDestination
*/
protected
void
calculateNextMovementPath
(
MovementSupported
comp
,
PositionVector
finalDestination
)
{
// Required for shortest path calculation
Waypoint
closestWaypointToCurrentPosition
=
waypointModel
.
getClosestWaypoint
(
comp
.
getRealPosition
(),
WeakWaypoint
.
class
);
Waypoint
closestWaypointToDestination
=
waypointModel
.
getClosestWaypoint
(
finalDestination
,
WeakWaypoint
.
class
);
List
<
Path
>
shortestPath
=
waypointModel
.
getShortestPath
(
closestWaypointToCurrentPosition
,
closestWaypointToDestination
);
List
<
Waypoint
>
waypointList
=
buildWaypointList
(
closestWaypointToCurrentPosition
,
shortestPath
);
dstPaths
.
put
(
comp
,
waypointList
);
currentPath
.
put
(
comp
,
Integer
.
valueOf
(
0
));
}
/**
* Build a list of waypoints that starts a the given starting waypoint based
* on the given list of paths.
*
* @param start
* @param shortestPath
* @return
*/
protected
List
<
Waypoint
>
buildWaypointList
(
Waypoint
start
,
List
<
Path
>
shortestPath
)
{
List
<
Waypoint
>
waypointList
=
new
ArrayList
<
Waypoint
>();
Waypoint
lastWaypoint
=
start
;
waypointList
.
add
(
start
);
for
(
Path
p
:
shortestPath
)
{
lastWaypoint
=
p
.
getOtherEnd
(
lastWaypoint
);
waypointList
.
add
(
lastWaypoint
);
}
return
waypointList
;
}
/**
* Checks if the current destination waypoint has been reached by testing
* the distance between the current position an the current destination
* waypoint for distance < speedLimit * 2.
*
* FIXME BR: why *2?
*
* @param currentWaypoint
* @param newPosition
* @return
*/
protected
boolean
destinationWaypointReached
(
Waypoint
currentWaypoint
,
PositionVector
newPosition
,
double
speed
)
{
double
distance
=
newPosition
.
getDistance
(
currentWaypoint
.
getPosition
());
return
(
distance
<
speed
*
2
);
}
}
/*
* 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.local
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.WeakHashMap
;
import
de.tud.kom.p2psim.api.topology.movement.MovementSupported
;
import
de.tud.kom.p2psim.impl.topology.PositionVector
;
import
de.tud.kom.p2psim.impl.topology.waypoints.graph.Path
;
import
de.tud.kom.p2psim.impl.topology.waypoints.graph.Waypoint
;
import
de.tud.kom.p2psim.impl.topology.waypoints.graph.WeakWaypoint
;
import
de.tud.kom.p2psim.impl.util.Either
;
import
de.tud.kom.p2psim.impl.util.Left
;
import
de.tud.kom.p2psim.impl.util.Right
;
/**
* This movement strategy uses the getShortestPath method implemented by the
* waypoint model and moves along path given by this method.
*
* @author Fabio Zöllner
* @version 1.0, 10.04.2012
*/
public
class
ShortestPathWaypointMovement
extends
AbstractLocalMovementStrategy
{
// Contains the shortest path and the currently used path
protected
final
WeakHashMap
<
MovementSupported
,
Integer
>
currentPath
=
new
WeakHashMap
<
MovementSupported
,
Integer
>();
protected
final
WeakHashMap
<
MovementSupported
,
List
<
Waypoint
>>
dstPaths
=
new
WeakHashMap
<
MovementSupported
,
List
<
Waypoint
>>();
// Used to check if the destination was altered by the waypoint movement
// model
protected
WeakHashMap
<
MovementSupported
,
PositionVector
>
currentDestination
=
new
WeakHashMap
<
MovementSupported
,
PositionVector
>();
/**
* Calculates the next position of the given movement supported component by
* using the shortest path to the waypoint closest to the destination.
*/
@Override
public
Either
<
PositionVector
,
Boolean
>
nextPosition
(
MovementSupported
comp
,
PositionVector
destination
)
{
if
(
currentDestination
.
get
(
comp
)
==
null
||
!
currentDestination
.
get
(
comp
).
equals
(
destination
))
{
currentDestination
.
put
(
comp
,
destination
);
calculateNextMovementPath
(
comp
,
destination
);
// If the list of the shortest path is empty the destination is the
// same
// as the current position. Thus tell the abstract waypoint model
// that we
// reached the destination.
if
(
dstPaths
.
get
(
comp
).
size
()
==
0
)
{
return
new
Right
<
PositionVector
,
Boolean
>(
true
);
}
}
int
currentPathIdx
=
currentPath
.
get
(
comp
);
Waypoint
currentWaypoint
=
dstPaths
.
get
(
comp
).
get
(
currentPathIdx
);
double
speed
=
getMovementSpeed
(
comp
);
PositionVector
newPosition
=
comp
.
getRealPosition
().
moveStep
(
currentWaypoint
.
getPosition
(),
speed
);
if
(
destinationWaypointReached
(
currentWaypoint
,
newPosition
,
speed
))
{
// We reached the next waypoint on the path to the destination
// move to the next waypoint
currentPath
.
put
(
comp
,
++
currentPathIdx
);
// The destination waypoint has been reached, tell the abstract
// waypoint model
if
(
dstPaths
.
get
(
comp
).
size
()
<=
currentPathIdx
)
{
currentPath
.
put
(
comp
,
--
currentPathIdx
);
return
new
Right
<
PositionVector
,
Boolean
>(
true
);
}
}
return
new
Left
<
PositionVector
,
Boolean
>(
newPosition
);
}
/**
* Finds the closest waypoints to the current position of the movement
* supported component as well as the final destination and searches for a
* path between the two waypoints using the underlying network of
* WeakWaypoints.
*
* @param comp
* @param finalDestination
*/
protected
void
calculateNextMovementPath
(
MovementSupported
comp
,
PositionVector
finalDestination
)
{
// Required for shortest path calculation
Waypoint
closestWaypointToCurrentPosition
=
waypointModel
.
getClosestWaypoint
(
comp
.
getRealPosition
(),
WeakWaypoint
.
class
);
Waypoint
closestWaypointToDestination
=
waypointModel
.
getClosestWaypoint
(
finalDestination
,
WeakWaypoint
.
class
);
List
<
Path
>
shortestPath
=
waypointModel
.
getShortestPath
(
closestWaypointToCurrentPosition
,
closestWaypointToDestination
);
List
<
Waypoint
>
waypointList
=
buildWaypointList
(
closestWaypointToCurrentPosition
,
shortestPath
);
dstPaths
.
put
(
comp
,
waypointList
);
currentPath
.
put
(
comp
,
Integer
.
valueOf
(
0
));
}
/**
* Build a list of waypoints that starts a the given starting waypoint based
* on the given list of paths.
*
* @param start
* @param shortestPath
* @return
*/
protected
List
<
Waypoint
>
buildWaypointList
(
Waypoint
start
,
List
<
Path
>
shortestPath
)
{
List
<
Waypoint
>
waypointList
=
new
ArrayList
<
Waypoint
>();
Waypoint
lastWaypoint
=
start
;
waypointList
.
add
(
start
);
for
(
Path
p
:
shortestPath
)
{
lastWaypoint
=
p
.
getOtherEnd
(
lastWaypoint
);
waypointList
.
add
(
lastWaypoint
);
}
return
waypointList
;
}
/**
* Checks if the current destination waypoint has been reached by testing
* the distance between the current position an the current destination
* waypoint for distance < speedLimit * 2.
*
* FIXME BR: why *2?
*
* @param currentWaypoint
* @param newPosition
* @return
*/
protected
boolean
destinationWaypointReached
(
Waypoint
currentWaypoint
,
PositionVector
newPosition
,
double
speed
)
{
double
distance
=
newPosition
.
distanceTo
(
currentWaypoint
.
getPosition
());
return
(
distance
<
speed
*
2
);
}
}
Prev
1
2
3
4
Next
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