diff --git a/gradle.properties b/gradle.properties index 7b69c13..481057c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -application.version=1.3 \ No newline at end of file +application.version=1.4 \ No newline at end of file diff --git a/src/main/kotlin/bgp/BGP.kt b/src/main/kotlin/bgp/BGP.kt index eb128f7..8bcddfa 100644 --- a/src/main/kotlin/bgp/BGP.kt +++ b/src/main/kotlin/bgp/BGP.kt @@ -157,7 +157,7 @@ abstract class BaseBGP(val mrai: Time, routingTable: RoutingTable): Pr * * @param node the node exporting the node */ - protected fun export(node: Node, restartMRAITimer: Boolean = true) { + protected fun export(node: Node) { if (!mraiTimer.expired) { // The MRAI timer is still running: no route is exported while the MRAI timer is running @@ -167,9 +167,7 @@ abstract class BaseBGP(val mrai: Time, routingTable: RoutingTable): Pr val selectedRoute = routingTable.getSelectedRoute() if (selectedRoute == lastExportedRoute) { - // No need to export any route since the currently selected route corresponds to the - // last exported route - // This test prevents routes from being exported multiple times before and after the MRAI expires + // Do not export if the selected route is equal to the last route exported by the node return } @@ -178,11 +176,12 @@ abstract class BaseBGP(val mrai: Time, routingTable: RoutingTable): Pr BGPNotifier.notifyExport(ExportNotification(node, selectedRoute)) lastExportedRoute = selectedRoute - if (restartMRAITimer && mrai > 0) { + if (mrai > 0) { // Restart the MRAI timer mraiTimer = Timer.enabled(mrai) { - export(node, restartMRAITimer = false) - } // when the timer expires + export(node) // when the timer expires + } + mraiTimer.start() } } diff --git a/src/main/kotlin/core/simulator/DelayGenerator.kt b/src/main/kotlin/core/simulator/DelayGenerator.kt index b9f0aa9..922f3e5 100644 --- a/src/main/kotlin/core/simulator/DelayGenerator.kt +++ b/src/main/kotlin/core/simulator/DelayGenerator.kt @@ -8,10 +8,20 @@ package core.simulator interface DelayGenerator { /** - * Th seed used to generate the delays. + * The seed used to generate the delays. */ val seed: Long + /** + * Minimum delay that the generator will generate. + */ + val min: Time + + /** + * Maximum delay that the generator will generate. + */ + val max: Time + /** * Generates the next delay value and returns it. */ diff --git a/src/main/kotlin/core/simulator/Engine.kt b/src/main/kotlin/core/simulator/Engine.kt index c615789..65080c6 100644 --- a/src/main/kotlin/core/simulator/Engine.kt +++ b/src/main/kotlin/core/simulator/Engine.kt @@ -6,6 +6,7 @@ import core.simulator.notifications.BasicNotifier import core.simulator.notifications.EndNotification import core.simulator.notifications.StartNotification import core.simulator.notifications.ThresholdReachedNotification +import java.util.* /** * Created on 23-07-2017 @@ -76,6 +77,19 @@ object Engine { return terminatedBeforeThreshold } + /** + * Returns the simulator version. + * It obtains the version from the resource where the simulator's version is defined. + */ + fun version(): String { + + javaClass.getResourceAsStream("/version.properties").use { + val properties = Properties() + properties.load(it) + return properties.getProperty("application.version") + } + } + } /** diff --git a/src/main/kotlin/core/simulator/RandomDelayGenerator.kt b/src/main/kotlin/core/simulator/RandomDelayGenerator.kt index 5d1db81..a0684ce 100644 --- a/src/main/kotlin/core/simulator/RandomDelayGenerator.kt +++ b/src/main/kotlin/core/simulator/RandomDelayGenerator.kt @@ -15,7 +15,7 @@ import java.util.Random * @param seed the seed used to generate the random delays */ class RandomDelayGenerator -private constructor(val min: Time, val max: Time, seed: Long): DelayGenerator { +private constructor(override val min: Time, override val max: Time, seed: Long): DelayGenerator { /** * Initial seed used for the generator. When reset() is called this seed is reused. diff --git a/src/main/kotlin/core/simulator/ZeroDelayGenerator.kt b/src/main/kotlin/core/simulator/ZeroDelayGenerator.kt index 339eb1d..4c7f7ac 100644 --- a/src/main/kotlin/core/simulator/ZeroDelayGenerator.kt +++ b/src/main/kotlin/core/simulator/ZeroDelayGenerator.kt @@ -11,6 +11,10 @@ object ZeroDelayGenerator : DelayGenerator { override val seed = 0L + override val min: Time = 0 + + override val max: Time = 0 + override fun nextDelay(): Time = 0 override fun reset() = Unit diff --git a/src/main/kotlin/io/Metadata.kt b/src/main/kotlin/io/Metadata.kt new file mode 100644 index 0000000..d66af4a --- /dev/null +++ b/src/main/kotlin/io/Metadata.kt @@ -0,0 +1,56 @@ +package io + +import core.simulator.Time +import java.io.File +import java.io.FileWriter +import java.io.Writer +import java.time.Instant + +/** + * Created on 27-10-2017 + * + * @author David Fialho + * + * Data class to hold metadata information. + * It also provides methods to output the information to a file or a stream. + */ +data class Metadata( + val version: String, + val startInstant: Instant, + val finishInstant: Instant, + val topologyFilename: String, + val stubsFilename: String?, + val destinationID: Int, + val minDelay: Time, + val maxDelay: Time, + val threshold: Time +) { + + /** + * Prints metadata information using the specified writer. + */ + fun print(writer: Writer) { + + writer.apply { + write("version = $version\n") + write("start datetime = $startInstant\n") + write("finish datetime = $finishInstant\n") + write("topology file = $topologyFilename\n") + if (stubsFilename != null) write("stubs file = $stubsFilename\n") + write("destination = $destinationID\n") + write("min delay = $minDelay\n") + write("max delay = $maxDelay\n") + write("threshold = $threshold\n") + } + } + + /** + * Prints metadata information to the specified file. + */ + fun print(outputFile: File) { + FileWriter(outputFile).use { + print(it) + } + } + +} diff --git a/src/main/kotlin/simulation/Execution.kt b/src/main/kotlin/simulation/Execution.kt index 994fde4..b1b73f9 100644 --- a/src/main/kotlin/simulation/Execution.kt +++ b/src/main/kotlin/simulation/Execution.kt @@ -2,6 +2,7 @@ package simulation import core.routing.Node import core.routing.Topology +import core.simulator.Time /** * Created on 29-08-2017 @@ -13,6 +14,6 @@ interface Execution { /** * Performs a single simulation execution with the specified topology and destination. */ - fun execute(topology: Topology<*>, destination: Node<*>) + fun execute(topology: Topology<*>, destination: Node<*>, threshold: Time) } \ No newline at end of file diff --git a/src/main/kotlin/simulation/RepetitionRunner.kt b/src/main/kotlin/simulation/RepetitionRunner.kt index 65e2611..3b6c418 100644 --- a/src/main/kotlin/simulation/RepetitionRunner.kt +++ b/src/main/kotlin/simulation/RepetitionRunner.kt @@ -6,9 +6,12 @@ import core.routing.Route import core.routing.Topology import core.simulator.DelayGenerator import core.simulator.Engine +import core.simulator.Time +import io.Metadata import io.TopologyReaderHandler import ui.Application import java.io.File +import java.time.Instant /** * Created on 29-08-2017 @@ -21,7 +24,9 @@ class RepetitionRunner( private val destinationID: NodeID, private val repetitions: Int, private val messageDelayGenerator: DelayGenerator, - private val stubDB: StubDB? + private val stubDB: StubDB?, + private val threshold: Time, + private val metadataFile: File ): Runner { @@ -36,6 +41,8 @@ class RepetitionRunner( */ override fun run(execution: Execution, application: Application) { + val startInstant = Instant.now() + val topology: Topology = application.loadTopology(topologyFile, topologyReader) { topologyReader.read() } @@ -52,7 +59,7 @@ class RepetitionRunner( repeat(times = repetitions) { repetition -> application.execute(repetition + 1, destination, messageDelayGenerator.seed) { - execution.execute(topology, destination) + execution.execute(topology, destination, threshold) } // Cleanup for next execution @@ -66,5 +73,19 @@ class RepetitionRunner( Engine.resetToDefaults() } } + + // Output metadata + Metadata( + Engine.version(), + startInstant, + finishInstant = Instant.now(), + topologyFilename = topologyFile.name, + stubsFilename = stubDB?.stubsFile?.name, + destinationID = destinationID, + minDelay = messageDelayGenerator.min, + maxDelay = messageDelayGenerator.max, + threshold = threshold + ).print(metadataFile) + } } \ No newline at end of file diff --git a/src/main/kotlin/simulation/SimpleAdvertisementExecution.kt b/src/main/kotlin/simulation/SimpleAdvertisementExecution.kt index d67f6ea..e04980c 100644 --- a/src/main/kotlin/simulation/SimpleAdvertisementExecution.kt +++ b/src/main/kotlin/simulation/SimpleAdvertisementExecution.kt @@ -11,7 +11,7 @@ import java.io.IOException * * @author David Fialho */ -class SimpleAdvertisementExecution(val threshold: Time): Execution { +class SimpleAdvertisementExecution: Execution { val dataCollectors = DataCollectorGroup() @@ -24,7 +24,7 @@ class SimpleAdvertisementExecution(val threshold: Time): Execution { * @throws IOException If an I/O error occurs */ @Throws(IOException::class) - override fun execute(topology: Topology<*>, destination: Node<*>) { + override fun execute(topology: Topology<*>, destination: Node<*>, threshold: Time) { dataCollectors.clear() diff --git a/src/main/kotlin/ui/cli/InputArgumentsParser.kt b/src/main/kotlin/ui/cli/InputArgumentsParser.kt index 5f630ca..ad0de27 100644 --- a/src/main/kotlin/ui/cli/InputArgumentsParser.kt +++ b/src/main/kotlin/ui/cli/InputArgumentsParser.kt @@ -1,6 +1,7 @@ package ui.cli import bgp.BGP +import core.simulator.Engine import core.simulator.RandomDelayGenerator import io.InterdomainTopologyReaderHandler import io.parseInterdomainExtender @@ -65,12 +66,7 @@ class InputArgumentsParser { throw InputArgumentsException("when option -v/-version is specified, no more options are expected ") } - javaClass.getResourceAsStream("/version.properties").use { - val properties = Properties() - properties.load(it) - - println("SS-BGP Simulator: ${properties.getProperty("application.version")}") - } + println("SS-BGP Simulator: ${Engine.version()}") System.exit(0) } @@ -95,11 +91,11 @@ class InputArgumentsParser { // If the topology filename is `topology.nf` and the destination is 10 the report filename // is `topology_10.basic.csv` - val basicReportFile = File(reportDirectory, - topologyFile.nameWithoutExtension.plus("_$destination.basic.csv")) + val outputName = topologyFile.nameWithoutExtension - val nodesReportFile = File(reportDirectory, - topologyFile.nameWithoutExtension.plus("_$destination.nodes.csv")) + val basicReportFile = File(reportDirectory, outputName.plus("_$destination.basic.csv")) + val nodesReportFile = File(reportDirectory, outputName.plus("_$destination.nodes.csv")) + val metadataFile = File(reportDirectory, outputName.plus("_$destination.meta.txt")) val topologyReader = InterdomainTopologyReaderHandler(topologyFile) val messageDelayGenerator = RandomDelayGenerator.with(minDelay, maxDelay, seed) @@ -116,9 +112,12 @@ class InputArgumentsParser { destination, repetitions, messageDelayGenerator, - stubDB + stubDB, + threshold, + metadataFile ) - val execution = SimpleAdvertisementExecution(threshold).apply { + + val execution = SimpleAdvertisementExecution().apply { dataCollectors.add(BasicDataCollector(basicReportFile)) if (commandLine.hasOption(NODE_REPORT)) {