summaryrefslogtreecommitdiff
path: root/opendc-core
diff options
context:
space:
mode:
Diffstat (limited to 'opendc-core')
-rw-r--r--opendc-core/build.gradle33
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/Scheduler.kt6
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/User.kt4
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/messaging/Pushable.kt18
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/simulator/Simulator.kt67
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/AbstractEntityKernel.kt65
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/ChannelContext.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/rack/Slot.kt)8
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Context.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/messaging/Channel.kt)37
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/EntityContext.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/simulator/Context.kt)32
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Kernel.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/simulator/AbstractSimulator.kt)30
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Simulator.kt222
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/clock/Clock.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/GraphBuilder.kt)28
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/impl/MachineKernel.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/simulator/impl/MachineSimulator.kt)40
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Channel.kt49
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/DuplexChannel.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/simulator/Simulates.kt)10
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/DuplexPort.kt33
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Envelope.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/messaging/Receivable.kt)18
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Port.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/messaging/Port.kt)7
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Readable.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/messaging/Pullable.kt)16
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/ReadableChannel.kt33
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/ReadablePort.kt33
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Writable.kt42
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/WritableChannel.kt33
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/WritablePort.kt33
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/AdjacencyList.kt104
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Component.kt40
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Edge.kt67
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Entity.kt17
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/ImmutableTopology.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Label.kt)15
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/MutableTopology.kt70
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Node.kt60
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Topology.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Graph.kt)18
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/TopologyBuilder.kt39
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/Datacenter.kt3
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/Rack.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/rack/Rack.kt)6
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/Room.kt (renamed from opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/room/Room.kt)7
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Cpu.kt1
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Gpu.kt4
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Machine.kt3
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/ProcessingUnit.kt2
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/network/NetworkUnit.kt2
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/power/PowerUnit.kt7
-rw-r--r--opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/storage/StorageUnit.kt2
-rw-r--r--opendc-core/src/test/kotlin/nl/atlarge/opendc/SmokeTest.kt73
44 files changed, 1114 insertions, 323 deletions
diff --git a/opendc-core/build.gradle b/opendc-core/build.gradle
index 05448aae..f2999d82 100644
--- a/opendc-core/build.gradle
+++ b/opendc-core/build.gradle
@@ -21,8 +21,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+
+/* Build configuration */
buildscript {
- ext.kotlin_version = '1.1.3-2'
+ ext.kotlin_version = '1.1.4-3'
+ ext.kotlin_version = '1.1.4'
repositories {
mavenCentral()
@@ -30,20 +33,31 @@ buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.0-RC3'
}
}
plugins {
id 'java'
- id 'org.jetbrains.kotlin.jvm' version '1.1.3'
+ id 'org.jetbrains.kotlin.jvm' version '1.1.4'
}
+apply plugin: 'org.junit.platform.gradle.plugin'
+apply plugin: 'kotlin'
+
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
kotlinOptions {
jvmTarget = "1.8"
}
}
+kotlin {
+ experimental {
+ coroutines 'enable'
+ }
+}
+
+/* Project configuration */
group 'nl.atlarge.opendc'
version '1.0'
@@ -53,4 +67,19 @@ repositories {
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.18'
+
+ testCompile "org.junit.jupiter:junit-jupiter-api:5.0.0-RC3"
+ testRuntime "org.junit.jupiter:junit-jupiter-engine:5.0.0-RC3"
+ compile "org.jetbrains.kotlin:kotlin-stdlib-jre8:$kotlin_version"
+}
+compileKotlin {
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+}
+compileTestKotlin {
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/Scheduler.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/Scheduler.kt
index b2845963..14a623a6 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/Scheduler.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/Scheduler.kt
@@ -24,14 +24,14 @@
package nl.atlarge.opendc.experiment
-import nl.atlarge.opendc.topology.Entity
+import nl.atlarge.opendc.topology.Node
/**
- * A task scheduler that is coupled to an [Entity] in the topology of the cloud network.
+ * A task scheduler that is coupled to an [Node] in the topology of the cloud network.
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface Scheduler<in E: Entity> {
+interface Scheduler<in E: Node<*>> {
/**
* Schedule the given jobs for the given entity.
*
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/User.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/User.kt
index 8bfba407..bb87a167 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/User.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/User.kt
@@ -24,7 +24,7 @@
package nl.atlarge.opendc.experiment
-import nl.atlarge.opendc.topology.Graph
+import nl.atlarge.opendc.topology.Topology
/**
* A user of a cloud network that provides [Job]s for the simulation.
@@ -37,4 +37,4 @@ import nl.atlarge.opendc.topology.Graph
* @param view The view of the user on the topology.
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-data class User(val id: Int, val name: String, val view: Graph)
+data class User(val id: Int, val name: String, val view: Topology)
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/messaging/Pushable.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/messaging/Pushable.kt
deleted file mode 100644
index 8ec14b6d..00000000
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/messaging/Pushable.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package nl.atlarge.opendc.experiment.messaging
-
-import nl.atlarge.opendc.topology.Entity
-
-/**
- * A [Pushable] instance allows objects to send messages to it.
- *
- * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
- */
-interface Pushable {
- /**
- * Push one message to downstream.
- *
- * @param msg The message to send downstream.
- * @param sender The sender of the message.
- */
- fun push(msg: Any?, sender: Entity)
-}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/simulator/Simulator.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/simulator/Simulator.kt
deleted file mode 100644
index 73747d08..00000000
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/simulator/Simulator.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * MIT License
- *
- * Copyright (c) 2017 atlarge-research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-package nl.atlarge.opendc.experiment.simulator
-
-import nl.atlarge.opendc.topology.Entity
-
-/**
- * A simulator that simulates a single [Entity] instance in a cloud network.
- *
- * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
- */
-interface Simulator<E: Entity> {
- /**
- * This method is invoked once at the start of the simulation to setup the initial state of the [Simulator].
- */
- fun Context<E>.setUp() {}
-
- /**
- * This method is invoked at least once before a tick. This allows a [Simulator] to setup its state before a tick
- * event.
- *
- * <p>The pre-tick consists of multiple sub-cycles in which all messages which have been sent
- * in the previous sub-cycle can optionally be processed in the sub-cycle by the receiving [Simulator].
- */
- fun Context<E>.preTick() {}
-
- /**
- * This method is invoked once per tick, which allows a [Simulator] to process events to simulate an entity in a
- * cloud network.
- */
- fun Context<E>.tick() {}
-
- /**
- * This method is invoked at least once per tick. This allows the [Simulator] to do work after a tick.
- *
- * <p>Like the pre-tick, the post-tick consists of multiple sub-cycles in which all messages which have been sent
- * in the previous sub-cycle can optionally be processed in the sub-cycle by the receiving [Simulator].
- */
- fun Context<E>.postTick() {}
-
- /**
- * This method is invoked once at the end of the simulation to tear down resources of the [Simulator].
- */
- fun Context<E>.tearDown() {}
-}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/AbstractEntityKernel.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/AbstractEntityKernel.kt
new file mode 100644
index 00000000..35b89e4e
--- /dev/null
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/AbstractEntityKernel.kt
@@ -0,0 +1,65 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package nl.atlarge.opendc.kernel
+
+import nl.atlarge.opendc.kernel.messaging.ReadablePort
+import nl.atlarge.opendc.kernel.messaging.WritableChannel
+import nl.atlarge.opendc.topology.Edge
+import nl.atlarge.opendc.topology.Entity
+import nl.atlarge.opendc.topology.Node
+
+/**
+ * A simulation kernel that simulates a single entity in the topology of a cloud network.
+ *
+ * @param ctx The context in which the simulation is run.
+ * @param E The shape of the component to simulate.
+ * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
+ */
+abstract class AbstractEntityKernel<E: Entity<*>>(private val ctx: EntityContext<E>): Kernel<EntityContext<E>> {
+ /**
+ * The [Node] that is simulated.
+ */
+ val self: Node<E> = ctx.component
+
+ /**
+ * Create a [WritableChannel] over the edge with the given tag.
+ *
+ * @param tag The tag of the edge to create a channel over.
+ * @return The channel that has been created or the cached result.
+ */
+ inline fun <reified T: Edge<*>> output(tag: String): WritableChannel<T> {
+ TODO()
+ }
+
+ /**
+ * Create a [ReadablePort] over the edges with the given tag.
+ *
+ * @param tag The tag of the edges to create a port over.
+ * @return The port that has been created or the cached result.
+ */
+ inline fun <reified T: Edge<*>> input(tag: String): ReadablePort<T> {
+ TODO()
+ }
+}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/rack/Slot.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/ChannelContext.kt
index 203f7f3b..aaf5abba 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/rack/Slot.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/ChannelContext.kt
@@ -22,14 +22,14 @@
* SOFTWARE.
*/
-package nl.atlarge.opendc.topology.container.rack
+package nl.atlarge.opendc.kernel
+import nl.atlarge.opendc.kernel.messaging.Writable
import nl.atlarge.opendc.topology.Edge
-import nl.atlarge.opendc.topology.Entity
/**
- * This class represents a slot in a [Rack] of [Machine]s.
+ * The context provided to a simulation kernel for communication channels between entities.
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-class Slot<T: Entity>(val rack: Rack<T>, val contents: T, val index: Int): Edge.Directed(rack, contents)
+interface ChannelContext<out T>: Context<Edge<T>>, Writable
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/messaging/Channel.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Context.kt
index b33b720d..81431e02 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/messaging/Channel.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Context.kt
@@ -22,34 +22,41 @@
* SOFTWARE.
*/
-package nl.atlarge.opendc.experiment.messaging
+package nl.atlarge.opendc.kernel
+import nl.atlarge.opendc.kernel.messaging.Readable
+import nl.atlarge.opendc.topology.Component
import nl.atlarge.opendc.topology.Entity
-import nl.atlarge.opendc.topology.Label
/**
- * A direct bi-directional communication channel between two [Entity] instances as seen from one of the entities.
+ * The [Context] interface provides a context for a simulation kernel, which defines the environment in which the
+ * simulation is run.
*
- * <p>A [Channel] is viewed as an edge that connects two entities in the topology of a cloud network.
- *
- * @param <E> The type of [Entity] this channel points to.
- * @param <T> The type of the label data of this channel.
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface Channel<out E: Entity, out T>: Pushable, Pullable {
+interface Context<out T: Component<*>>: Readable {
+ /**
+ * The [Component] that is simulated.
+ */
+ val component: T
+
/**
- * The [Entity] instance this channel is points to.
+ * The observable state of an [Entity] within the simulation is provided by context.
*/
- val entity: E
+ val <S> Entity<S>.state: S
/**
- * The label of the channel, possibly containing user-defined information.
+ * Suspend the simulation kernel until the next tick occurs in the simulation.
*/
- val label: Label<T>
+ suspend fun tick(): Boolean {
+ sleep(1)
+ return true
+ }
/**
- * The channel the message originates from.
+ * Suspend the simulation kernel for <code>n</code> ticks before resuming the execution.
+ *
+ * @param n The amount of ticks to suspend the simulation kernel.
*/
- val Receivable<Any?>.channel: Channel<E, T>
- get() = this@Channel
+ suspend fun sleep(n: Int)
}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/simulator/Context.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/EntityContext.kt
index 29ad4dc8..ea9c6b04 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/simulator/Context.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/EntityContext.kt
@@ -22,26 +22,17 @@
* SOFTWARE.
*/
-package nl.atlarge.opendc.experiment.simulator
+package nl.atlarge.opendc.kernel
import nl.atlarge.opendc.topology.Entity
+import nl.atlarge.opendc.topology.Node
/**
- * A context for [Simulator] instance.
+ * The context provided to a simulation kernel for stateful entities in the topology.
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface Context<E: Entity> {
- /**
- * The current tick of the experiment.
- */
- val tick: Long
-
- /**
- * The [Entity] that is simulated.
- */
- val entity: E
-
+interface EntityContext<out T: Entity<*>>: Context<Node<T>> {
/**
* Update the state of the entity being simulated.
*
@@ -50,18 +41,5 @@ interface Context<E: Entity> {
*
* @param next The next state of the entity.
*/
- fun update(next: E)
-
- /**
- * Push the given given tick handler on the stack and change the simulator's behaviour to become the new tick
- * handler.
- *
- * @param block The tick handler to push onto the stack.
- */
- fun become(block: Context<E>.() -> Unit)
-
- /**
- * Revert the behaviour of the simulator to the previous handler in the stack.
- */
- fun unbecome()
+ suspend fun <C: EntityContext<E>, E: Entity<S>, S> C.update(next: S)
}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/simulator/AbstractSimulator.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Kernel.kt
index 109d765e..5f399c57 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/simulator/AbstractSimulator.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Kernel.kt
@@ -22,31 +22,25 @@
* SOFTWARE.
*/
-package nl.atlarge.opendc.experiment.simulator
+package nl.atlarge.opendc.kernel
-import nl.atlarge.opendc.experiment.messaging.Port
-import nl.atlarge.opendc.topology.Entity
+import nl.atlarge.opendc.topology.Component
/**
- * A simulator that simulates a single entity in the topology of a cloud network.
+ * A simulation kernel that simulates a single [Component] instance in a cloud network.
*
- * @param ctx The context in which the simulation is run.
- * @param <E> The type of entity to simulate.
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-abstract class AbstractSimulator<E: Entity>(val ctx: Context<E>): Simulator<E> {
+interface Kernel<in C: Context<*>> {
/**
- * The [Entity] that is simulated.
- */
- val self: E = ctx.entity
-
- /**
- * Create a [Port] of the given type.
+ * This method is invoked to start the simulation of the [Component] associated with this [Kernel].
+ *
+ * <p>This method is assumed to be running during the experiment, but should hand back control to the simulator at
+ * some point by calling [Context.tick] to wait for the next tick to occur, which allows to allows other entity
+ * simulators to do work in the current tick of the simulation.
*
- * @param name The name of the label to create the port for.
- * @return The port that has been created or the cached result.
+ * <p>If this method exists early, before the simulation has finished, the entity is assumed to be shutdown and its
+ * simulation will not run any further.
*/
- inline fun <reified E: Entity, T> port(name: String): Port<E, T> {
- throw NotImplementedError()
- }
+ suspend fun C.run()
}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Simulator.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Simulator.kt
new file mode 100644
index 00000000..ee8d5072
--- /dev/null
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/Simulator.kt
@@ -0,0 +1,222 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package nl.atlarge.opendc.kernel
+
+import nl.atlarge.opendc.kernel.clock.Clock
+import nl.atlarge.opendc.kernel.clock.Tick
+import nl.atlarge.opendc.kernel.messaging.Envelope
+import nl.atlarge.opendc.topology.*
+import java.util.*
+import kotlin.coroutines.experimental.*
+
+/**
+ * A [Simulator] runs the simulation over the specified topology.
+ *
+ * @param topology The topology to run the simulation over.
+ * @param mapping The mapping of components in the topology to the simulation kernels the components should use.
+ * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
+ */
+class Simulator(val topology: Topology, private val mapping: Map<Component<*>, Class<out Kernel<*>>>): Iterator<Unit> {
+ /**
+ * The registry of the simulation kernels used in the experiment.
+ */
+ private val registry: MutableMap<Component<*>, Pair<Kernel<Context<*>>, Context<*>>?> = HashMap()
+
+ /**
+ * A mapping of the entities in the topology to their current state.
+ */
+ private val states: MutableMap<Entity<*>, Any?> = HashMap()
+
+ /**
+ * The clock of the simulator.
+ */
+ private val clock: DefaultClock = DefaultClock()
+
+ /**
+ * Initialize the simulator.
+ */
+ init {
+ topology.forEach { node ->
+ resolve(node)
+ node.outgoingEdges().forEach { resolve(it) }
+ }
+
+ registry.values.forEach {
+ it?.also { (kernel, ctx) ->
+ // Start all kernel co-routines
+ kernel.run {
+ val block: suspend () -> Unit = { ctx.run() }
+ block.startCoroutine(SimulationCoroutine())
+ }
+ }
+ }
+ }
+
+ /**
+ * Resolve the given [Component] to the [Kernel] of that component.
+ *
+ * @param component The component to resolve.
+ * @return The [Kernel] that simulates that [Component].
+ */
+ fun <T: Component<*>> resolve(component: T): Pair<Kernel<Context<T>>, Context<T>>? {
+ @Suppress("UNCHECKED_CAST")
+ return registry.computeIfAbsent(component, {
+ val constructor = mapping[it]?.constructors?.get(0)
+ val ctx = if (component is Node<*>) {
+ DefaultEntityContext(component as Node<*>)
+ } else {
+ DefaultChannelContext(component as Edge<*>)
+ }
+
+ if (constructor == null) {
+ println("warning: invalid constructor for kernel ${mapping[it]}")
+ null
+ } else {
+ Pair(constructor.newInstance(ctx) as Kernel<Context<*>>, ctx)
+ }
+ }) as? Pair<Kernel<Context<T>>, Context<T>>?
+ }
+
+ /**
+ * Determine whether the simulator has a next non-empty cycle available.
+ *
+ * @return <code>true</code> if the simulator has a next non-empty cycle, <code>false</code> otherwise.
+ */
+ override fun hasNext(): Boolean = clock.queue.isNotEmpty()
+
+ /**
+ * Run the next cycle in the simulation.
+ */
+ override fun next() {
+ clock.tick++
+ while (true) {
+ val (tick, block) = clock.queue.peek() ?: return
+
+ if (tick > clock.tick)
+ // Tick has yet to occur
+ break
+ else if (tick < clock.tick)
+ // Tick has already occurred
+ println("error: tick was not handled correctly")
+
+ clock.queue.poll()
+ block()
+ }
+ }
+
+ class SimulationCoroutine: Continuation<Unit> {
+ override val context: CoroutineContext = EmptyCoroutineContext
+ override fun resume(value: Unit) {}
+
+ override fun resumeWithException(exception: Throwable) {
+ val currentThread = Thread.currentThread()
+ currentThread.uncaughtExceptionHandler.uncaughtException(currentThread, exception)
+ }
+ }
+
+ /**
+ * The [Context] for an entity within the simulation.
+ */
+ private inner class DefaultEntityContext<out T: Entity<*>>(override val component: Node<T>): EntityContext<T> {
+ /**
+ * Retrieves and removes a single message from this channel suspending the caller while the channel is empty.
+ *
+ * @param block The block to process the message with.
+ * @return The processed message.
+ */
+ suspend override fun <T> receive(block: Envelope<*>.(Any?) -> T): T = suspendCoroutine {}
+
+
+ /**
+ * The observable state of an [Entity] within the simulation is provided by context.
+ */
+ @Suppress("UNCHECKED_CAST")
+ override val <S> Entity<S>.state: S
+ get() = states[this] as S
+
+ /**
+ * Update the state of the entity being simulated.
+ *
+ * <p>Instead of directly mutating the entity, we create a new instance of the entity to prevent other objects
+ * referencing the old entity having their data changed.
+ *
+ * @param next The next state of the entity.
+ */
+ suspend override fun <C: EntityContext<E>, E: Entity<S>, S> C.update(next: S) {
+ states.put(component.entity as Entity<*>, next)
+ }
+
+ /**
+ * Suspend the simulation kernel for <code>n</code> ticks before resuming the execution.
+ *
+ * @param n The amount of ticks to suspend the simulation kernel.
+ */
+ suspend override fun sleep(n: Int): Unit = suspendCoroutine { cont -> clock.scheduleAfter(n, { cont.resume(Unit) }) }
+ }
+
+ /**
+ * The [Context] for an edge within the simulation.
+ */
+ private inner class DefaultChannelContext<out T>(override val component: Edge<T>): ChannelContext<T> {
+ /**
+ * Retrieves and removes a single message from this channel suspending the caller while the channel is empty.
+ *
+ * @param block The block to process the message with.
+ * @return The processed message.
+ */
+ suspend override fun <T> receive(block: Envelope<*>.(Any?) -> T): T = suspendCoroutine {}
+
+ /**
+ * Send the given message downstream.
+ *
+ * @param msg The message to send.
+ * @param sender The sender of the message.
+ */
+ suspend override fun send(msg: Any?, sender: Node<*>): Unit = suspendCoroutine {}
+
+ /**
+ * The observable state of an [Entity] within the simulation is provided by context.
+ */
+ @Suppress("UNCHECKED_CAST")
+ override val <S> Entity<S>.state: S
+ get() = states[this] as S
+
+ /**
+ * Suspend the simulation kernel for <code>n</code> ticks before resuming the execution.
+ *
+ * @param n The amount of ticks to suspend the simulation kernel.
+ */
+ suspend override fun sleep(n: Int): Unit = suspendCoroutine { cont -> clock.scheduleAfter(n, { cont.resume(Unit) }) }
+ }
+
+ private inner class DefaultClock: Clock {
+ override var tick: Tick = 0
+ internal val queue: PriorityQueue<Pair<Tick, () -> Unit>> = PriorityQueue(Comparator.comparingLong { it.first })
+
+ override fun scheduleAt(tick: Tick, block: () -> Unit) {
+ queue.add(Pair(tick, block))
+ }
+ }
+}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/GraphBuilder.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/clock/Clock.kt
index a280e88a..db13eee1 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/GraphBuilder.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/clock/Clock.kt
@@ -22,32 +22,32 @@
* SOFTWARE.
*/
-package nl.atlarge.opendc.topology
+package nl.atlarge.opendc.kernel.clock
/**
- * A builder for [Graph] instances.
+ * A tick represents a moment of time in which some work is done by an entity.
+ */
+typealias Tick = Long
+
+/**
+ * The clock of a simulation manages the ticks that have elapsed and schedules the tick events.
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface GraphBuilder {
+interface Clock {
/**
- * Add the given [Entity] to this graph.
- *
- * @param entity The entity to add.
+ * The tick the clock is currently at.
*/
- fun add(entity: Entity)
+ val tick: Tick
/**
- * Add the given [Edge] to this graph.
*
- * @param edge The edge to add.
+ * @throws IllegalArgumentException
*/
- fun add(edge: Edge<*>)
+ fun scheduleAt(tick: Tick, block: () -> Unit)
/**
- * Build a [Graph] instance from the current state of this builder.
- *
- * @return The graph built from this builder.
+ * @throws IllegalArgumentException
*/
- fun build(): Graph
+ fun scheduleAfter(n: Int, block: () -> Unit) = scheduleAt(tick + n, block)
}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/simulator/impl/MachineSimulator.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/impl/MachineKernel.kt
index 4b351753..e34c7060 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/simulator/impl/MachineSimulator.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/impl/MachineKernel.kt
@@ -22,23 +22,37 @@
* SOFTWARE.
*/
-package nl.atlarge.opendc.experiment.simulator.impl
+package nl.atlarge.opendc.kernel.impl
-import nl.atlarge.opendc.experiment.simulator.AbstractSimulator
-import nl.atlarge.opendc.experiment.simulator.Context
-import nl.atlarge.opendc.experiment.simulator.Simulates
+import nl.atlarge.opendc.experiment.Task
+import nl.atlarge.opendc.kernel.AbstractEntityKernel
+import nl.atlarge.opendc.kernel.EntityContext
import nl.atlarge.opendc.topology.machine.Cpu
import nl.atlarge.opendc.topology.machine.Machine
-/**
- * A simulator for [Machine] entities.
- *
- * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
- */
-@Simulates<Machine>(Machine::class)
-class MachineSimulator(ctx: Context<Machine>): AbstractSimulator<Machine>(ctx) {
- val cpus = port<Cpu, Nothing>("cpu")
+class MachineKernel(ctx: EntityContext<Machine>): AbstractEntityKernel<Machine>(ctx) {
+
+ suspend override fun EntityContext<Machine>.run() {
+ println("${this}: Initialising!")
- override fun Context<Machine>.tick() {
+ val cpus = component.outgoingEdges().filter { it.tag == "cpu" }.map { it.to.entity as Cpu }
+ val speed = cpus.fold(0, { acc, (speed, cores) -> acc + speed * cores })
+ val task: Task
+
+ loop@while (true) {
+ val msg = receive()
+ when (msg) {
+ is Task -> {
+ task = msg
+ break@loop
+ }
+ else -> println("warning: unhandled message $msg")
+ }
+ }
+
+ while (tick()) {
+ task.consume(speed.toLong())
+ }
}
+
}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Channel.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Channel.kt
new file mode 100644
index 00000000..ad966719
--- /dev/null
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Channel.kt
@@ -0,0 +1,49 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package nl.atlarge.opendc.kernel.messaging
+
+import nl.atlarge.opendc.topology.Edge
+import nl.atlarge.opendc.topology.Node
+
+/**
+ * A unidirectional communication channel between two [Node] instances as seen from one of the entities.
+ *
+ * <p>A [Channel] is viewed as a directed edge that connects two entities in the topology of a cloud network.
+ *
+ * @param T The shape of the label of the edge of this channel.
+ * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
+ */
+interface Channel<out T> {
+ /**
+ * The directed edge between two nodes which represents this unidirectional communication channel.
+ */
+ val edge: Edge<T>
+
+ /**
+ * The channel the message originates from.
+ */
+ val Envelope<*>.channel: Channel<T>
+ get() = this@Channel
+}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/simulator/Simulates.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/DuplexChannel.kt
index c69c5eff..a4ef7409 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/simulator/Simulates.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/DuplexChannel.kt
@@ -22,14 +22,12 @@
* SOFTWARE.
*/
-package nl.atlarge.opendc.experiment.simulator
-
-import nl.atlarge.opendc.topology.Entity
-import kotlin.reflect.KClass
+package nl.atlarge.opendc.kernel.messaging
/**
- * Classes annotated by this annotation indicates that the annotated class simulates the given entity.
+ * A [DuplexChannel] instance allows bi-directional communication over the channel.
*
+ * @param T The shape of the label of the edge of this channel.
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-annotation class Simulates<T: Entity>(val entity: KClass<T>)
+interface DuplexChannel<out T>: Channel<T>, Readable, Writable
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/DuplexPort.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/DuplexPort.kt
new file mode 100644
index 00000000..5917ac71
--- /dev/null
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/DuplexPort.kt
@@ -0,0 +1,33 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package nl.atlarge.opendc.kernel.messaging
+
+/**
+ * A [DuplexPort] instance allows bi-directional communication with multiple channels.
+ *
+ * @param T The shape of the label of the edges of the channels of this port.
+ * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
+ */
+interface DuplexPort<out T>: Port<DuplexChannel<T>>, Readable, Writable
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/messaging/Receivable.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Envelope.kt
index f40ebd30..d4d363d5 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/messaging/Receivable.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Envelope.kt
@@ -22,23 +22,23 @@
* SOFTWARE.
*/
-package nl.atlarge.opendc.experiment.messaging
+package nl.atlarge.opendc.kernel.messaging
-import nl.atlarge.opendc.topology.Entity
+import nl.atlarge.opendc.topology.Node
/**
- * A message that is received from a [Channel], also containing the metadata of the message.
+ * The envelope of a message that is received from a [Channel], also containing the metadata of the message.
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface Receivable<out T> {
+data class Envelope<out T>(
/**
- * The value of this message.
+ * The message in this envelope.
*/
- val value: T
+ val message: T,
/**
- * The sender of this message.
+ * The sender of the message.
*/
- val sender: Entity
-}
+ val sender: Node<*>
+)
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/messaging/Port.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Port.kt
index a2cdba32..18ec1918 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/messaging/Port.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Port.kt
@@ -22,13 +22,12 @@
* SOFTWARE.
*/
-package nl.atlarge.opendc.experiment.messaging
-
-import nl.atlarge.opendc.topology.Entity
+package nl.atlarge.opendc.kernel.messaging
/**
* A port connects multiple [Channel]s to an entity in the topology of a cloud network.
*
+ * @param C The shape of the channels that are connected to this port.
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface Port<out E: Entity, out T>: Iterable<Channel<E, T>>, Pullable, Pushable
+interface Port<out C: Channel<*>>: Iterable<C>
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/messaging/Pullable.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Readable.kt
index 7a756a73..422c5668 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/experiment/messaging/Pullable.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Readable.kt
@@ -22,26 +22,26 @@
* SOFTWARE.
*/
-package nl.atlarge.opendc.experiment.messaging
+package nl.atlarge.opendc.kernel.messaging
/**
- * A [Pullable] instance allows objects to pull messages from the instance.
+ * A [Readable] instance allows objects to pull messages from the instance.
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface Pullable {
+interface Readable {
/**
- * Pull one message from this [Channel] for processing via the given block.
+ * Retrieves and removes a single message from this channel suspending the caller while the channel is empty.
*
* @param block The block to process the message with.
- * @return The result of the processed messaged.
+ * @return The processed message.
*/
- fun <T> pull(block: Receivable<Any?>.(Any?) -> T): T
+ suspend fun <T> receive(block: Envelope<*>.(Any?) -> T): T
/**
- * Pull one message from this [Channel].
+ * Retrieve a single message from this [Channel].
*
* @return The message that was received from the channel
*/
- fun pull(): Any? = pull { it }
+ suspend fun receive(): Any? = receive { it }
}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/ReadableChannel.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/ReadableChannel.kt
new file mode 100644
index 00000000..c291b1ea
--- /dev/null
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/ReadableChannel.kt
@@ -0,0 +1,33 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package nl.atlarge.opendc.kernel.messaging
+
+/**
+ * A [ReadableChannel] instance allows objects to receive messages from the channel.
+ *
+ * @param T The shape of the label of the edge of this channel.
+ * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
+ */
+interface ReadableChannel<out T>: Channel<T>, Readable
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/ReadablePort.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/ReadablePort.kt
new file mode 100644
index 00000000..bfad9490
--- /dev/null
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/ReadablePort.kt
@@ -0,0 +1,33 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package nl.atlarge.opendc.kernel.messaging
+
+/**
+ * A [ReadablePort] instance allows objects to receive messages from the channel.
+ *
+ * @param T The shape of the label of the edges of the channels of this port.
+ * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
+ */
+interface ReadablePort<out T>: Port<ReadableChannel<T>>, Readable
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Writable.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Writable.kt
new file mode 100644
index 00000000..6bd1ce30
--- /dev/null
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/Writable.kt
@@ -0,0 +1,42 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package nl.atlarge.opendc.kernel.messaging
+
+import nl.atlarge.opendc.topology.Node
+
+/**
+ * A [Writable] instance allows objects to send messages to it.
+ *
+ * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
+ */
+interface Writable {
+ /**
+ * Send the given message downstream.
+ *
+ * @param msg The message to send.
+ * @param sender The sender of the message.
+ */
+ suspend fun send(msg: Any?, sender: Node<*>)
+}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/WritableChannel.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/WritableChannel.kt
new file mode 100644
index 00000000..60f89a97
--- /dev/null
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/WritableChannel.kt
@@ -0,0 +1,33 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package nl.atlarge.opendc.kernel.messaging
+
+/**
+ * A [WritableChannel] instance allows objects to write messages to the channel.
+ *
+ * @param T The shape of the label of the edge of this channel.
+ * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
+ */
+interface WritableChannel<out T>: Channel<T>, Writable
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/WritablePort.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/WritablePort.kt
new file mode 100644
index 00000000..0dc92680
--- /dev/null
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/kernel/messaging/WritablePort.kt
@@ -0,0 +1,33 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package nl.atlarge.opendc.kernel.messaging
+
+/**
+ * A [WritablePort] instance allows objects to write messages to multiple channels.
+ *
+ * @param T The shape of the label of the edges of the channels of this port.
+ * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
+ */
+interface WritablePort<out T>: Port<WritableChannel<T>>, Writable
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/AdjacencyList.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/AdjacencyList.kt
new file mode 100644
index 00000000..bdf60056
--- /dev/null
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/AdjacencyList.kt
@@ -0,0 +1,104 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package nl.atlarge.opendc.topology
+
+import java.util.concurrent.atomic.AtomicInteger
+
+/**
+ * A builder for [Topology] instances, which is backed by an adjacency list.
+ *
+ * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
+ */
+class AdjacencyListTopologyBuilder: TopologyBuilder {
+ /**
+ * Build a [Topology] instance from the current state of this builder.
+ *
+ * @return The graph built from this builder.
+ */
+ override fun build(): MutableTopology = AdjacencyListTopology()
+}
+
+/**
+ * A [Topology] whose graph is represented as adjacency list.
+ */
+internal class AdjacencyListTopology: MutableTopology {
+ private val nextId: AtomicInteger = AtomicInteger(0)
+ private val nodes: MutableList<Node<*>> = ArrayList()
+
+ /**
+ * Create a [Node] in this [Topology] for the given [Entity].
+ *
+ * @param entity The entity to create a node for.
+ * @return The node created for the given entity.
+ */
+ override fun <T : Entity<*>> node(entity: T): Node<T> {
+ val node = AdjacencyListNode(nextId.incrementAndGet(), entity)
+ nodes.add(node)
+ return node
+ }
+
+ /**
+ * Create a directed edge between two [Node]s in the topology.
+ *
+ * @param from The source of the edge.
+ * @param to The destination of the edge.
+ * @param label The label of the edge.
+ * @param tag The tag of the edge that uniquely identifies the relationship the edge represents.
+ * @return The edge that has been created.
+ */
+ override fun <T> connect(from: Node<*>, to: Node<*>, label: T, tag: String?): Edge<T> {
+ if (from !is AdjacencyListNode<*> || to !is AdjacencyListNode<*>)
+ throw IllegalArgumentException()
+ if (!from.validate(this) || !to.validate(this))
+ throw IllegalArgumentException()
+ val edge: Edge<T> = AdjacencyListEdge(label, tag, from, to)
+ from.outgoingEdges.add(edge)
+ to.ingoingEdges.add(edge)
+ return edge
+ }
+
+ /**
+ * Returns an iterator over the elements of this object.
+ */
+ override fun iterator(): Iterator<Node<*>> = nodes.iterator()
+
+ internal inner class AdjacencyListNode<out T: Entity<*>>(override val id: Int, override val label: T): Node<T> {
+ internal var ingoingEdges: MutableSet<Edge<*>> = HashSet()
+ internal var outgoingEdges: MutableSet<Edge<*>> = HashSet()
+
+ override fun ingoingEdges(): Set<Edge<*>> = ingoingEdges
+ override fun outgoingEdges(): Set<Edge<*>> = outgoingEdges
+ override fun toString(): String = label.toString()
+
+ internal fun validate(instance: AdjacencyListTopology) = this@AdjacencyListTopology == instance
+ }
+
+ internal class AdjacencyListEdge<out T>(override val label: T,
+ override val tag: String?,
+ override val from: Node<*>,
+ override val to: Node<*>): Edge<T> {
+ override fun toString(): String = label.toString()
+ }
+}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Component.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Component.kt
new file mode 100644
index 00000000..79b35e86
--- /dev/null
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Component.kt
@@ -0,0 +1,40 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package nl.atlarge.opendc.topology
+
+/**
+ * A component within a [Topology], which is either an [Node] or an [Edge] representing the relationship between
+ * entities within a logical topology of a cloud network.
+ *
+ * <p>A [Component]'s label provides access to user-specified data.
+ *
+ * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
+ */
+interface Component<out T> {
+ /**
+ * The label of this [Component].
+ */
+ val label: T
+}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Edge.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Edge.kt
index 4344e1b5..22ad57c1 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Edge.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Edge.kt
@@ -24,72 +24,31 @@
package nl.atlarge.opendc.topology
-import java.util.*
-
/**
- * An undirected edge that represents a connection between exactly two instances of [Entity].
+ * An edge that represents a directed relationship between exactly two [Node]s in a logical topology of a cloud network.
*
- * @param from The first incident node.
- * @param to The second incident node.
- * @param label The label of the edge.
- * @param <T> The data type of the label value.
+ * @param T The relationship type the edge represents within a logical topology.
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-class Edge<out T>(val from: Entity, val to: Entity, val label: Label<T>) {
- /**
- * Return the [Entity] at the opposite end of this [Edge] from the
- * specified entity.
- *
- * Throws [IllegalArgumentException] if <code>entity</code> is
- * not incident to this edge.
- *
- * @param entity The entity to get the opposite of for this edge pair.
- * @return The entity at the opposite end of this edge from the specified entity.
- * @throws IllegalArgumentException if <code>entity</code> is not incident to this edge.
- */
- fun opposite(entity: Entity): Entity = when (entity) {
- from -> to
- to -> from
- else -> throw IllegalArgumentException()
- }
-
+interface Edge<out T>: Component<T> {
/**
- * Return a [Pair] representing this edge consisting of both incident nodes.
- * Note that the pair is in no particular order.
- *
- * @return The edge represented as pair of both incident nodes.
- */
- fun endpoints(): Pair<Entity, Entity> = Pair(from, to)
-
- /**
- * Determine whether the given object is equal to this instance.
- *
- * @param other The other object to compare against.
- * @return <code>true</code> both edges are equal, <code>false</code> otherwise.
+ * A tag to uniquely identify the relationship this edge represents.
*/
- override fun equals(other: Any?): Boolean =
- if (other is Edge<*>) {
- from == other.from && to == other.to ||
- from == other.to && to == other.from
- } else {
- false
- }
+ val tag: String?
/**
- * Return the hash code of this edge pair.
+ * The source of the edge.
*
- * @return The hash code of this edge pair.
+ * This property is not guaranteed to have a runtime complexity of <code>O(1)</code>, but must be at least
+ * <code>O(n)</code>, with respect to the size of the topology.
*/
- override fun hashCode(): Int {
- return Objects.hash(from, to)
- }
+ val from: Node<*>
/**
- * Return a string representation of this [Edge].
+ * The destination of the edge.
*
- * @return A string representation of this [Edge].
+ * This property is not guaranteed to have a runtime complexity of <code>O(1)</code>, but must be at least
+ * <code>O(n)</code>, with respect to the size of the topology.
*/
- override fun toString(): String {
- return "Edge($from<->$to)"
- }
+ val to: Node<*>
}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Entity.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Entity.kt
index c78cba3c..44264a35 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Entity.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Entity.kt
@@ -25,16 +25,15 @@
package nl.atlarge.opendc.topology
/**
- * An entity in the logical topology of a cloud network.
+ * An entity within a cloud network.
*
- * <p>Instances of the [Entity] interface provide direct access to observable state of the entity to other entities in
- * the topology connected to it.
+ * <p>A [Entity] contains the immutable properties of this component given by the topology configuration at the start
+ * of a simulation and remain unchanged during simulation.
*
+ * <p>In addition, other entities in a simulation have direct, immutable access to the observable state of this entity.
+ *
+ * @param S The shape of the observable state of this entity, which is directly accessible by other components within
+ * a simulation.
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface Entity {
- /**
- * A unique identifier of this entity within the topology represented as a [Int].
- */
- val id: Int
-}
+interface Entity<out S>
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Label.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/ImmutableTopology.kt
index 69174263..90ba5dc5 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Label.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/ImmutableTopology.kt
@@ -23,20 +23,9 @@
*/
package nl.atlarge.opendc.topology
-
/**
- * A label for an edge in the topology.
+ * A [Topology] whose elements and structural relationships will never change.
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface Label<out T> {
- /**
- * The name of the label.
- */
- val name: String
-
- /**
- * The user-specified data of the label.
- */
- val data: T
-}
+interface ImmutableTopology: Topology
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/MutableTopology.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/MutableTopology.kt
new file mode 100644
index 00000000..0aa0d1b5
--- /dev/null
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/MutableTopology.kt
@@ -0,0 +1,70 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package nl.atlarge.opendc.topology
+
+/**
+ * A subinterface of [Topology] which adds mutation methods. When mutation is not required, users
+ * should prefer the [Topology] interface.
+ *
+ * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
+ */
+interface MutableTopology: Topology {
+ /**
+ * Create a [Node] in this [Topology] for the given [Entity].
+ *
+ * @param entity The entity to create a node for.
+ * @return The node created for the given entity.
+ */
+ fun <T: Entity<*>> node(entity: T): Node<T>
+
+ /**
+ * Create a directed edge between two [Node]s in the topology.
+ *
+ * @param from The source of the edge.
+ * @param to The destination of the edge.
+ * @param label The label of the edge.
+ * @param tag The tag of the edge that uniquely identifies the relationship the edge represents.
+ * @return The edge that has been created.
+ */
+ fun <T> connect(from: Node<*>, to: Node<*>, label: T, tag: String? = null): Edge<T>
+
+ /**
+ * Create a directed edge between two [Node]s in the topology.
+ *
+ * @param from The source of the edge.
+ * @param to The destination of the edge.
+ * @param tag The tag of the edge that uniquely identifies the relationship the edge represents.
+ * @return The edge that has been created.
+ */
+ fun connect(from: Node<*>, to: Node<*>, tag: String? = null): Edge<Unit> = connect(from, to, Unit, tag)
+
+ /**
+ * Create a directed edge between two [Node]s in the topology.
+ *
+ * @param dest The destination of the edge.
+ * @return The edge that has been created.
+ */
+ infix fun Node<*>.to(dest: Node<*>): Edge<Unit> = connect(this, dest)
+}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Node.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Node.kt
new file mode 100644
index 00000000..5b7076ed
--- /dev/null
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Node.kt
@@ -0,0 +1,60 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package nl.atlarge.opendc.topology
+
+/**
+ * A labeled node of graph representing an entity in a specific logical topology of a cloud network.
+ *
+ * <p>A [Node] is instantiated and managed by a [Topology] instance containing user-specified data in its label.
+ *
+ * @param T The entity type the node represents in a logical topology.
+ * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
+ */
+interface Node<out T: Entity<*>>: Component<T> {
+ /**
+ * A unique identifier of this node within the topology.
+ */
+ val id: Int
+
+ /**
+ * Return the set of incoming edges of this node.
+ *
+ * @return All edges whose destination is this node.
+ */
+ fun ingoingEdges(): Set<Edge<*>>
+
+ /**
+ * Return the set of outgoing edges of this node.
+ *
+ * @return All edges whose source is this node.
+ */
+ fun outgoingEdges(): Set<Edge<*>>
+
+ /**
+ * The [Entity] this node represents within a logical topology of a cloud network.
+ */
+ val entity: T
+ get() = label
+}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Graph.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Topology.kt
index fe1714fc..d8f966d1 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Graph.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/Topology.kt
@@ -28,20 +28,8 @@ package nl.atlarge.opendc.topology
* A graph data structure which represents the logical topology of a cloud network consisting of one or more
* datacenters.
*
+ * <p>A topology is [Iterable] and allows implementation-dependent iteration of the nodes in the topology.
+ *
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface Graph {
- /**
- * Return the set of incoming edges of this node.
- *
- * @return All edges whose destination is this node.
- */
- fun Entity.incomingEdges(): Set<Edge<*>>
-
- /**
- * Return the set of outgoing edges of this node.
- *
- * @return All edges whose source is this node.
- */
- fun Entity.outgoingEdges(): Set<Edge<*>>
-}
+interface Topology: Iterable<Node<*>>
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/TopologyBuilder.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/TopologyBuilder.kt
new file mode 100644
index 00000000..5752eb89
--- /dev/null
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/TopologyBuilder.kt
@@ -0,0 +1,39 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package nl.atlarge.opendc.topology
+
+/**
+ * A builder for [Topology] instances.
+ *
+ * @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
+ */
+interface TopologyBuilder {
+ /**
+ * Build a [Topology] instance from the current state of this builder.
+ *
+ * @return The graph built from this builder.
+ */
+ fun build(): MutableTopology
+}
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/Datacenter.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/Datacenter.kt
index 5a57c70a..2b464f15 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/Datacenter.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/Datacenter.kt
@@ -29,7 +29,6 @@ import nl.atlarge.opendc.topology.Entity
/**
* A representation of a facility used to house computer systems and associated components.
*
- * @param id The unique identifier of this entity.
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-class Datacenter(override val id: Int): Entity
+class Datacenter: Entity<Unit>
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/rack/Rack.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/Rack.kt
index c40b1660..043ad31a 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/rack/Rack.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/Rack.kt
@@ -22,7 +22,7 @@
* SOFTWARE.
*/
-package nl.atlarge.opendc.topology.container.rack
+package nl.atlarge.opendc.topology.container
import nl.atlarge.opendc.topology.Entity
@@ -30,8 +30,6 @@ import nl.atlarge.opendc.topology.Entity
* A type of physical steel and electronic framework that is designed to house servers, networking devices, cables and
* other datacenter computing equipment.
*
- * @param id The unique identifier of this entity.
- * @param <T> The type of nodes that are placed in the rack.
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-data class Rack<T: Entity>(override val id: Int): Entity
+class Rack: Entity<Unit>
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/room/Room.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/Room.kt
index 638d59e3..844d96a0 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/room/Room.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/container/Room.kt
@@ -22,14 +22,13 @@
* SOFTWARE.
*/
-package nl.atlarge.opendc.topology.container.room
+package nl.atlarge.opendc.topology.container
import nl.atlarge.opendc.topology.Entity
/**
- * A physical room in a datacenter which contains [Entity]s.
+ * A physical room in a datacenter with relationships to the entities within the room.
*
- * @param id The unique identifier of this entity.
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-abstract class Room(override val id: Int): Entity
+interface Room: Entity<Unit>
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Cpu.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Cpu.kt
index 5c1b8b57..e06ad00c 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Cpu.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Cpu.kt
@@ -30,7 +30,6 @@ package nl.atlarge.opendc.topology.machine
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
data class Cpu(
- override val id: Int,
override val speed: Int,
override val cores: Int,
override val energyConsumption: Int
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Gpu.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Gpu.kt
index 91a7be6a..f15847e4 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Gpu.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Gpu.kt
@@ -29,9 +29,9 @@ package nl.atlarge.opendc.topology.machine
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-data class Gpu(
- override val id: Int,
+class Gpu(
override val speed: Int,
override val cores: Int,
override val energyConsumption: Int
): ProcessingUnit
+
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Machine.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Machine.kt
index 39b5267e..396339a2 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Machine.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/Machine.kt
@@ -30,7 +30,6 @@ import nl.atlarge.opendc.topology.Entity
* A Physical Machine (PM) inside a rack of a datacenter. It has a speed, and can be given a workload on which it will
* work until finished or interrupted.
*
- * @param id The unique identifier of this entity.
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-data class Machine(override val id: Int): Entity
+class Machine: Entity<Unit>
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/ProcessingUnit.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/ProcessingUnit.kt
index 095a0bb5..a235133f 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/ProcessingUnit.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/machine/ProcessingUnit.kt
@@ -31,7 +31,7 @@ import nl.atlarge.opendc.topology.Entity
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface ProcessingUnit: Entity {
+interface ProcessingUnit: Entity<Unit> {
/**
* The speed of this [ProcessingUnit] per core.
*/
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/network/NetworkUnit.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/network/NetworkUnit.kt
index fcb5f20a..9c294125 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/network/NetworkUnit.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/network/NetworkUnit.kt
@@ -31,4 +31,4 @@ import nl.atlarge.opendc.topology.Entity
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface NetworkUnit: Entity
+interface NetworkUnit: Entity<Unit>
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/power/PowerUnit.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/power/PowerUnit.kt
index cc047136..e016a12b 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/power/PowerUnit.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/power/PowerUnit.kt
@@ -27,10 +27,9 @@ package nl.atlarge.opendc.topology.power
import nl.atlarge.opendc.topology.Entity
/**
- * A [Entity] which provides power for other entities a cloud network to run.
+ * An [Entity] which provides power for other entities a cloud network to run.
*
- * @param id The unique identifier of the [Entity].
- * @param output The output of the power unit in Watts.
+ * @param output The power output of the power unit in Watt.
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-data class PowerUnit(override val id: Int, val output: Double): Entity
+class PowerUnit(val output: Double): Entity<Unit>
diff --git a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/storage/StorageUnit.kt b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/storage/StorageUnit.kt
index 6fac585f..8e53e365 100644
--- a/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/storage/StorageUnit.kt
+++ b/opendc-core/src/main/kotlin/nl/atlarge/opendc/topology/storage/StorageUnit.kt
@@ -31,4 +31,4 @@ import nl.atlarge.opendc.topology.Entity
*
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
-interface StorageUnit: Entity
+interface StorageUnit: Entity<Unit>
diff --git a/opendc-core/src/test/kotlin/nl/atlarge/opendc/SmokeTest.kt b/opendc-core/src/test/kotlin/nl/atlarge/opendc/SmokeTest.kt
new file mode 100644
index 00000000..300ffec8
--- /dev/null
+++ b/opendc-core/src/test/kotlin/nl/atlarge/opendc/SmokeTest.kt
@@ -0,0 +1,73 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2017 atlarge-research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+package nl.atlarge.opendc
+
+import nl.atlarge.opendc.kernel.Kernel
+import nl.atlarge.opendc.kernel.Simulator
+import nl.atlarge.opendc.kernel.impl.MachineKernel
+import nl.atlarge.opendc.topology.AdjacencyListTopologyBuilder
+import nl.atlarge.opendc.topology.Component
+import nl.atlarge.opendc.topology.container.Rack
+import nl.atlarge.opendc.topology.machine.Cpu
+import nl.atlarge.opendc.topology.machine.Machine
+import org.junit.jupiter.api.Test
+
+internal class SmokeTest {
+ @Test
+ fun smoke() {
+ val mapping: MutableMap<Component<*>, Class<out Kernel<*>>> = HashMap()
+ val builder = AdjacencyListTopologyBuilder()
+ val topology = builder.build().apply {
+ val rack = node(Rack())
+ val machineA = node(Machine())
+ val machineB = node(Machine())
+
+ connect(rack, machineA, tag = "machine")
+ connect(rack, machineB, tag = "machine")
+
+ val cpuA1 = node(Cpu(10, 2, 2))
+ val cpuA2 = node(Cpu(5, 3, 2))
+
+ connect(machineA, cpuA1, tag = "cpu")
+ connect(machineA, cpuA2, tag = "cpu")
+
+ val cpuB1 = node(Cpu(10, 2, 2))
+ val cpuB2 = node(Cpu(5, 3, 2))
+
+ connect(machineB, cpuB1, tag = "cpu")
+ connect(machineB, cpuB2, tag = "cpu")
+
+ mapping.apply {
+ put(machineA, MachineKernel::class.java)
+ put(machineB, MachineKernel::class.java)
+ }
+
+ val simulator = Simulator(this, mapping)
+ while (simulator.hasNext()) {
+ simulator.next()
+ }
+ }
+ }
+}