package org.lucci.madhoc.network;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import org.lucci.madhoc.bining.Cel;
import org.lucci.madhoc.env.NetworkEnvironment;
import org.lucci.madhoc.messaging.Message;
import org.lucci.madhoc.network.net.NetworkInterface;
import org.lucci.madhoc.network.net.NetworkingTechnology;
import org.lucci.madhoc.simulation.Configurable;
import org.lucci.madhoc.simulation.Monitor;
import org.lucci.madhoc.simulation.Simulation;
import org.lucci.madhoc.simulation.projection.Projection;
import org.lucci.util.assertion.Assertions;
public class Network implements Configurable
{
public Map<String, NetworkingTechnology> networkTypes = new HashMap<String, NetworkingTechnology>();
private Simulation simulation;
private List<Station> stations = new Vector<Station>();
private Collection<Connection> connections;
private Map<Class, Monitor> simulationApplicationMap = new HashMap<Class, Monitor>();
private Collection<Application> computerApplicationList;
private NetworkEnvironment networkEnvironment;
private Map<Class, Projection> projectionMap = new HashMap<Class, Projection>();
private boolean simulateSynchronization;
private boolean useDeliveryFailureMessages = true;
public List<Station> getStations()
{
return stations;
}
public void setStations(List<Station> stations)
{
this.stations = stations;
}
public Map<Class, Monitor> getSimulationApplicationMap()
{
return simulationApplicationMap;
}
public void setSimulationApplicationMap(Map<Class, Monitor> simulationApplicationMap)
{
this.simulationApplicationMap = simulationApplicationMap;
}
public void setConnections(Collection<Connection> connections)
{
this.connections = connections;
}
public void setProjectionMap(Map<Class, Projection> projectionMap)
{
this.projectionMap = projectionMap;
}
public void deployApplication(Monitor monitor)
throws Throwable
{
monitor.setNetwork(this);
simulationApplicationMap.put(monitor.getClass(), monitor);
if (monitor.getStationApplicationClass() != null)
{
monitor.configure();
monitor.deploy();
if (monitor.getInitializer() != null)
{
monitor.getInitializer().initialize();
}
}
}
public Map<Class, Monitor> getMonitorMap()
{
return Collections.unmodifiableMap(simulationApplicationMap);
}
public Collection<Connection> findConnections()
{
Collection<Connection> connections = new HashSet<Connection>();
for (Station station : getStations())
{
for (NetworkInterface ni : station.getNetworkingUnit().getNetworkInterfaces())
{
connections.addAll(ni.getConnections());
}
}
return connections;
}
public Collection<Connection> getConnections()
{
if (this.connections == null)
{
this.connections = findConnections();
}
return this.connections;
}
public Collection<Connection> findConnections(NetworkingTechnology type)
{
Collection<Connection> connections = new Vector<Connection>();
for (Connection connection : findConnections())
{
if (connection.getNetworkType() == type)
{
connections.add(connection);
}
}
return connections;
}
public List<Station> findComputersAcceptableIn(Projection r)
{
List<Station> collection = new Vector<Station>();
for (Station c : getStations())
{
if (r.acceptComputer(c))
{
collection.add(c);
}
}
return collection;
}
public Station findComputer(int id)
{
for (Station c : getStations())
{
if (c.getIdentifier() == id)
{
return c;
}
}
return null;
}
public NetworkEnvironment getNetworkEnvironment()
{
return networkEnvironment;
}
public void setNetworkEnvironment(NetworkEnvironment networkModel)
{
if (!getStations().isEmpty())
throw new IllegalStateException("the network environment has to be defined before the stations are created, otherwise it has no effect");
if (networkModel == null)
throw new IllegalArgumentException();
this.networkEnvironment = networkModel;
}
public Collection<Connection> removeInvalidConnections()
{
Collection<Connection> invalidConnections = new HashSet<Connection>();
for (Station station : getStations())
{
if (station.isSwitchedOn())
{
for (NetworkInterface ni : station.getNetworkingUnit().getNetworkInterfaces())
{
for (Connection connection : ni.getConnections())
{
if (!invalidConnections.contains(connection))
{
if (connection.getNetworkType().isAutomatic())
{
if (connection.getDistance() < connection.getGreatestDistancePossible())
{
connection.setEnvironmentPerturbation(getNetworkEnvironment().getRadioPropagationModel().getEnvironmentPerturbation(connection));
if (connection.getQuality() < 0.01)
{
invalidConnections.add(connection);
}
}
else
{
invalidConnections.add(connection);
}
}
}
}
}
}
else
{
invalidConnections.addAll(station.getNetworkingUnit().getConnections());
}
}
for (Connection c : invalidConnections)
{
c.remove();
}
return invalidConnections;
}
public Collection<Connection> createNewConnections()
{
Collection<Connection> newConnections = new Vector<Connection>();
for (Station station : getStations())
{
if (station.isSwitchedOn())
{
for (NetworkInterface networkAdapter : station.getNetworkingUnit().getNetworkInterfaces())
{
if (networkAdapter.getNetworkingTechnology().isAutomatic())
{
for (NetworkInterface surroundingNetworkAdapter : getSurroundingCompliantNetworkInterfaces(networkAdapter))
{
if (networkAdapter.getConnections().size() < networkAdapter.getNetworkingTechnology().getMaximumNumberOfConnectionsPossible()
&& surroundingNetworkAdapter.getConnections().size() < surroundingNetworkAdapter.getNetworkingTechnology().getMaximumNumberOfConnectionsPossible())
{
if (!simulateSynchronization || networkAdapter.canSyncTo(surroundingNetworkAdapter))
{
Connection connection = new Connection(networkAdapter, surroundingNetworkAdapter);
if (connection.getDistance() > connection.getGreatestDistancePossible())
throw new IllegalStateException();
if (!networkAdapter.getConnections().contains(connection) && !surroundingNetworkAdapter.getConnections().contains(connection))
{
connection.setEnvironmentPerturbation(getNetworkEnvironment().getRadioPropagationModel().getEnvironmentPerturbation(connection));
if (connection.getQuality() > 0.01)
{
networkAdapter.getConnections().add(connection);
surroundingNetworkAdapter.getConnections().add(connection);
connection.setCreationDate(getSimulation().getSimulatedTime());
newConnections.add(connection);
}
}
}
}
}
}
}
}
}
return newConnections;
}
public void createBidirectionalConnection(Station s1, Station s2, String type)
{
NetworkingTechnology nt = getNetworkTypes().get(type);
if (nt == null)
throw new IllegalStateException(type + " connections are not supported");
NetworkInterface ni1 = s1.getNetworkingUnit().getNetworkInterface(nt);
NetworkInterface ni2 = s2.getNetworkingUnit().getNetworkInterface(nt);
if (ni1 == null || ni2 == null)
throw new IllegalStateException("at least one of the two computers does not have a " + type + " adapter");
addConnection(ni1, ni2);
}
public Collection<Connection> findUnidirectionalConnections()
{
Collection<Connection> connections = new Vector<Connection>();
for (Connection c : getConnections())
{
if (!c.getNetworkInterface1().getConnections().contains(c) || !c.getNetworkInterface2().getConnections().contains(c))
{
connections.add(c);
}
}
return connections;
}
public Connection addConnection(NetworkInterface ni1, NetworkInterface ni2)
{
Connection connection = new Connection(ni1, ni2);
ni1.getConnections().add(connection);
ni2.getConnections().add(connection);
return connection;
}
private Collection<NetworkInterface> getSurroundingCompliantNetworkInterfaces(final NetworkInterface networkAdapter)
{
Collection<NetworkInterface> surroundingNetworkAdapters = new Vector<NetworkInterface>();
Collection<Station> nodesInTheSurroundingCels = getNodesInTheSurroundingCels(networkAdapter);
for (Station otherStation : nodesInTheSurroundingCels)
{
if (otherStation.isSwitchedOn())
{
if (otherStation != networkAdapter.getNetworkingUnit().getStation())
{
NetworkInterface otherNetworkAdapter = otherStation.getNetworkingUnit().getNetworkInterface(networkAdapter.getNetworkingTechnology());
if (otherNetworkAdapter != null)
{
double distance = networkAdapter.getNetworkingUnit().getStation().getLocation().getDistanceTo(otherStation.getLocation());
if (distance < networkAdapter.getCoverageRadius() && distance < otherNetworkAdapter.getCoverageRadius())
{
surroundingNetworkAdapters.add(otherNetworkAdapter);
}
}
}
}
}
return surroundingNetworkAdapters;
}
private Collection<Station> getNodesInTheSurroundingCels(final NetworkInterface networkAdapter)
{
Collection<Station> nodes = new Vector<Station>();
Cel localCel = networkAdapter.getNetworkingUnit().getStation().getLocation().getCel();
int numberOfCelsInRadius = (int) (networkAdapter.getCoverageRadius() / 10d) + 1;
for (int ioffset = -numberOfCelsInRadius; ioffset < numberOfCelsInRadius; ++ioffset)
{
int i = localCel.getX() + ioffset;
if (0 < i && i < getNetworkEnvironment().getGrid().getCels().length)
{
for (int joffset = -numberOfCelsInRadius; joffset < numberOfCelsInRadius; ++joffset)
{
int j = localCel.getY() + joffset;
if (0 < j && j < getNetworkEnvironment().getGrid().getCels()[i].length)
{
nodes.addAll(getNetworkEnvironment().getGrid().getCels()[i][j].getStations());
}
}
}
}
return nodes;
}
public void createComputers()
throws Throwable
{
int computerId = 0;
double squareKms = getNetworkEnvironment().getGrid().getEdgeLenght() * getNetworkEnvironment().getGrid().getEdgeLenght() / 1000000d;
double laptopDensity = simulation.getConfiguration().getDouble("network_laptop_density");
double laptopCount = laptopDensity * squareKms;
for (int i = 0; i < laptopCount; ++i)
{
Station computer = new Station();
computer.setType(ComputerType.LAPTOP);
stations.add(computer);
}
double pagerDensity = simulation.getConfiguration().getDouble("network_pager_density");
double pagerCount = pagerDensity * squareKms;
for (int i = 0; i < pagerCount; ++i)
{
Station computer = new Station();
computer.setType(ComputerType.PAGER);
stations.add(computer);
}
double phoneDensity = simulation.getConfiguration().getDouble("network_phone_density");
double phoneCount = phoneDensity * squareKms;
for (int i = 0; i < phoneCount; ++i)
{
Station computer = new Station();
computer.setType(ComputerType.MOBILE_PHONE);
stations.add(computer);
}
double hotspotDensity = simulation.getConfiguration().getDouble("network_hotspot_density");
double hotspotCount = hotspotDensity * squareKms;
for (int i = 0; i < hotspotCount; ++i)
{
Station computer = new Station();
computer.setType(ComputerType.HOTSPOT);
stations.add(computer);
}
for (Station node : stations)
{
node.setIdentifier(computerId++);
node.setNetwork(this);
node.configure();
}
}
public Collection getStationsWithMobilityMedium(Class mm)
{
Collection<Station> c = new Vector<Station>();
Iterator stationsIterator = getStations().iterator();
while (stationsIterator.hasNext())
{
Station station = (Station) stationsIterator.next();
if (station.getMobilityModel().getMobilityMedium().getClass() == mm)
{
c.add(station);
}
}
return c;
}
public Collection<Application> getStationApplications()
{
if (computerApplicationList == null)
{
computerApplicationList = new HashSet<Application>();
for (Station station : getStations())
{
computerApplicationList.addAll(station.getApplicationMap().getInverseRelation().getKeys());
}
computerApplicationList = Collections.unmodifiableCollection(computerApplicationList);
}
return computerApplicationList;
}
public void resetIterationScopedValues()
{
computerApplicationList = null;
for (Station station : getStations())
{
for (NetworkInterface ni : station.getNetworkingUnit().getNetworkInterfaces())
{
for (Connection connection : ni.getConnections())
{
connection.resetIterationScopedValues();
}
}
}
this.connections = null;
for (Station s : getStations())
{
s.getNetworkingUnit().resetIterationScopedValues();
}
}
class MessageTransfer
{
public Message message;
public Station recipient;
public Connection connection;
}
public void flushOutgoingMessageQueues()
{
for (Station station : getStations())
{
Iterator<Message> i = station.getNetworkingUnit().getOutgoingMessageQueue().internalList().iterator();
while (i.hasNext())
{
Message message = i.next();
Collection<Station> recipientStations = message.findRecipientStations();
recipientStations.retainAll(station.getNetworkingUnit().getNeighborhood());
if (recipientStations.isEmpty())
{
i.remove();
}
}
}
List<MessageTransfer> messageTransfers = new Vector<MessageTransfer>();
for (Station station : getStations())
{
Collection<Message> expiredMessages = new HashSet<Message>();
Collection<Message> completedMessages = new HashSet<Message>();
for (Message message : (List<Message>) station.getNetworkingUnit().getOutgoingMessageQueue().internalList())
{
Assertions.ensure(message.getMessageInformation() != null, "no message information");
if (message.getMessageInformation().getCreationDate() < 0)
{
message.getMessageInformation().setCreationDate(getSimulation().getSimulatedTime());
}
double messageAge = getSimulation().getSimulatedTime() - message.getMessageInformation().getCreationDate();
if (messageAge > message.getMessageInformation().getValidity())
{
expiredMessages.add(message);
}
else
{
Collection<Station> recipientStations = message.findRecipientStations();
List<MessageTransfer> messageTransfersForThisMessage = new Vector<MessageTransfer>();
for (Station recipientNode : recipientStations)
{
if (message.getNumberOfBytesDeliveredTo(recipientNode) < message.getSizeInBytes())
{
Collection<Connection> connectionsToRecipient = message.getSourceStationApplication().getComputer().getNetworkingUnit().getConnectionsTo(recipientNode);
for (Connection connection : connectionsToRecipient)
{
MessageTransfer entry = new MessageTransfer();
entry.message = message;
entry.recipient = recipientNode;
entry.connection = connection;
messageTransfersForThisMessage.add(entry);
}
}
}
if (messageTransfersForThisMessage.isEmpty())
{
completedMessages.add(message);
}
messageTransfers.addAll(messageTransfersForThisMessage);
}
}
for (Message message : completedMessages)
{
message.getSourceStationApplication().getComputer().getNetworkingUnit().getOutgoingMessageQueue().remove(message);
}
if (this.useDeliveryFailureMessages )
{
for (Message expiredMessage : expiredMessages)
{
Message deliveryFailureMessage = new Message();
deliveryFailureMessage.setContent(expiredMessage);
deliveryFailureMessage.getRecipientApplications().add(expiredMessage.getSourceStationApplication());
deliveryFailureMessage.setType(Message.DELIVERY_FAILURE);
try
{
station.getNetworkingUnit().getIncomingMessageQueue().add(deliveryFailureMessage);
station.getNetworkingUnit().getOutgoingMessageQueue().remove(expiredMessage);
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
processMessageTransfers(messageTransfers);
}
private void processMessageTransfers(Collection<MessageTransfer> messageTransfers)
{
while (!messageTransfers.isEmpty())
{
Iterator messageTransferIterator = messageTransfers.iterator();
while (messageTransferIterator.hasNext())
{
MessageTransfer messageTransfer = (MessageTransfer) messageTransferIterator.next();
if (messageTransfer.message.getNumberOfBytesDeliveredTo(messageTransfer.recipient) == messageTransfer.message.getSizeInBytes())
{
messageTransferIterator.remove();
}
else
{
int numberOfBytesTransfered = pursue(messageTransfer);
messageTransfer.connection.getNetworkType().charge(messageTransfer.message.getSourceStationApplication().getComputer(), numberOfBytesTransfered);
if (numberOfBytesTransfered == 0)
{
messageTransferIterator.remove();
}
else
{
if (messageTransfer.message.getNumberOfBytesDeliveredTo(messageTransfer.recipient) == messageTransfer.message.getSizeInBytes())
{
if (messageTransfer.message.getNumberOfCollisions() == 0)
{
try
{
messageTransfer.recipient.getNetworkingUnit().getIncomingMessageQueue().add((Message) messageTransfer.message.clone());
}
catch (IOException ex)
{
throw new IllegalStateException();
}
}
messageTransferIterator.remove();
}
}
}
}
}
}
public int pursue(MessageTransfer messageTransfer)
{
double availableBandwidth = messageTransfer.connection.getAvailableBandwith();
double availableInputBufferSize = messageTransfer.recipient.getNetworkingUnit().getIncomingMessageQueue().getAvailableSize();
int amountOfDataToSendNow = (int) Math.min(availableBandwidth, availableInputBufferSize);
int packetSize = messageTransfer.connection.getNetworkType().getPacketSize();
amountOfDataToSendNow = Math.min(amountOfDataToSendNow, packetSize);
int remainingDataSize = messageTransfer.message.getSizeInBytes() - messageTransfer.message.getNumberOfBytesDeliveredTo(messageTransfer.recipient);
if (remainingDataSize == 0)
throw new IllegalStateException();
amountOfDataToSendNow = Math.min(amountOfDataToSendNow, remainingDataSize);
if (amountOfDataToSendNow > 0)
{
messageTransfer.connection.loadBandwith(amountOfDataToSendNow);
double totalNumberOfBytesOnTheConnections = 0;
Collection<Connection> sibblingConnections = messageTransfer.message.getSourceStationApplication().getComputer().getNetworkingUnit().getNetworkInterface(messageTransfer.connection.getNetworkType()).getConnections();
for (Connection sibblingConnection : sibblingConnections)
{
totalNumberOfBytesOnTheConnections += sibblingConnection.getUsedBandwith();
sibblingConnection.loadBandwith((int) Math.min(amountOfDataToSendNow / getSimulation().getResolution(), sibblingConnection.getAvailableBandwith()));
}
remainingDataSize -= amountOfDataToSendNow;
messageTransfer.message.addNumberOfBytesDeliveredTo(messageTransfer.recipient, amountOfDataToSendNow);
}
return amountOfDataToSendNow;
}
public void configure()
throws Throwable
{
this.useDeliveryFailureMessages = getSimulation().getConfiguration().getBoolean("use_delivery_failure_messages");
this.simulateSynchronization = getSimulation().getConfiguration().getBoolean("simulate_synchronization");
Collection<String> networkTechnologyNames = getSimulation().getConfiguration().getStrings("network_technologies_available");
for (String networkTechnologyName : networkTechnologyNames)
{
NetworkingTechnology type = new NetworkingTechnology();
type.setName(networkTechnologyName);
type.setNetwork(this);
type.configure();
this.networkTypes.put(type.getName(), type);
}
for (Projection projection : (List<Projection>) getSimulation().getConfiguration().getInstantiatedClasses("network_projections"))
{
projection.setSourceNetwork(this);
projection.configure();
projectionMap.put(projection.getClass(), projection);
}
NetworkEnvironment environment = (NetworkEnvironment) simulation.getConfiguration().getInstantiatedClass("network_environment");
environment.setNetwork(this);
setNetworkEnvironment(environment);
getNetworkEnvironment().configure();
createComputers();
int iterationCount = simulation.getConfiguration().getInteger("mobility_prerun_iteration_count");
double timeStep = simulation.getConfiguration().getDouble("mobility_prerun_time_step");
getNetworkEnvironment().initializeNodesLocation(getStations(), iterationCount, timeStep);
createNewConnections();
}
public Simulation getSimulation()
{
return simulation;
}
public Map<Class, Projection> getProjectionMap()
{
return projectionMap;
}
public void setSimulation(Simulation simulation)
{
if (this.simulation != null)
throw new IllegalStateException("simulation already defined");
this.simulation = simulation;
}
public Map<String, NetworkingTechnology> getNetworkTypes()
{
return networkTypes;
}
}