/* * Copyright (c) 2005-2010 KOM – Multimedia Communications Lab * * This file is part of PeerfactSim.KOM. * * PeerfactSim.KOM is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * PeerfactSim.KOM is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PeerfactSim.KOM. If not, see . * */ package de.tud.kom.p2psim.impl.topology.movement; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Paths; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.stream.Stream; import de.tud.kom.p2psim.api.topology.movement.MovementModel; import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator; import de.tud.kom.p2psim.impl.topology.util.PositionVector; import de.tudarmstadt.maki.simonstrator.api.Event; import de.tudarmstadt.maki.simonstrator.api.EventHandler; import de.tudarmstadt.maki.simonstrator.api.Time; import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor; public class SumoTraceMovementModel implements MovementModel, EventHandler { private long timeBetweenMoveOperations; private final List components; private final Map> movements; private final String tracefile; private final long timestepConversion = Time.SECOND; private boolean initialized = false; @XMLConfigurableConstructor({ "timeBetweenMoveOperations", "tracefile" }) public SumoTraceMovementModel(long timeBetweenMoveOperations, String tracefile) { this.timeBetweenMoveOperations = timeBetweenMoveOperations; this.components = new LinkedList<>(); this.movements = new LinkedHashMap<>(); this.tracefile = tracefile; } @Override public final void addComponent(SimLocationActuator comp) { components.add(comp); } public long getTimeBetweenMoveOperations() { return timeBetweenMoveOperations; } @Override public void setTimeBetweenMoveOperations(long time) { this.timeBetweenMoveOperations = time; } @Override public void placeComponent(SimLocationActuator actuator) { if (!initialized) { initializeModel(); initialized = true; } // Initial placement actuator.updateCurrentLocation(new PositionVector(5, 5)); } /** * Initializes the movement model by executing BonnMotion and parsing the * resulting movement trace. */ protected void initializeModel() { // Schedule first step Event.scheduleWithDelay(timeBetweenMoveOperations, this, null, 0); // Read the if-file (.if) try (Stream lines = Files.lines(Paths.get(tracefile), Charset.defaultCharset())) { lines.forEachOrdered(line -> process(line)); } catch (IOException e) { e.printStackTrace(); } System.out.println("Initialization: done."); } /** * Invoked for each line within the input file. A line consists of the * following data, separated by a space: * * Node-ID Timestamp X-Coordinate Y-Coordinate * * @param line */ protected void process(String line) { String[] step = line.split(" "); int nodeId = Integer.valueOf(step[0]); //assert nodeId < components.size(); long time = Long.valueOf(step[1]); double x = Double.valueOf(step[2]); double y = Double.valueOf(step[3]); if (nodeId >= components.size()) { return; } // TODO make SUMO trace offsets configurable. time -= 21600; x -= 10000; y -= 10000; x/=10; y/=10; SimLocationActuator comp = components.get(nodeId); Queue steps = movements.get(comp); if (steps == null) { steps = new LinkedList<>(); movements.put(comp, steps); } steps.add(new Step(time, x, y)); } @Override public void eventOccurred(Object content, int type) { /* * One event for all nodes (synchronized movement), as this boosts * simulation performance due to less recalculations in the network * models. */ long currentTime = Time.getCurrentTime() / timestepConversion; movements.forEach((component, steps) -> { Step step = null; Step nextStep = null; do { step = steps.peek(); nextStep = null; // Valid step if (step != null && step.timestamp <= currentTime) { step = steps.poll(); // Look ahead, maybe we need to skip? nextStep = steps.peek(); } } while (step != null && nextStep != null && nextStep.timestamp < currentTime); if (step != null) { component.updateCurrentLocation(new PositionVector(step.x, step.y)); // System.out.println("Set component "+component.getHost().getId()+" location to "+step.x+","+step.y); } }); // Reschedule next step Event.scheduleWithDelay(timeBetweenMoveOperations, this, null, 0); } /** * A Step. * * @author Bjoern Richerzhagen * */ private class Step { public final double x; public final double y; public final long timestamp; public Step(long timestamp, double x, double y) { this.x = x; this.y = y; this.timestamp = timestamp; } } }