it.unibo.alchemist.actions.RunCollektiveProgram.kt Maven / Gradle / Ivy
Show all versions of alchemist-incarnation-collektive Show documentation
package it.unibo.alchemist.actions
import it.unibo.alchemist.collektive.device.CollektiveDevice
import it.unibo.alchemist.model.Action
import it.unibo.alchemist.model.Context
import it.unibo.alchemist.model.Node
import it.unibo.alchemist.model.Node.Companion.asProperty
import it.unibo.alchemist.model.NodeProperty
import it.unibo.alchemist.model.Position
import it.unibo.alchemist.model.Reaction
import it.unibo.alchemist.model.actions.AbstractAction
import it.unibo.alchemist.model.molecules.SimpleMolecule
import it.unibo.collektive.Collektive
import it.unibo.collektive.aggregate.api.Aggregate
import java.lang.reflect.Method
import java.lang.reflect.Parameter
import kotlin.reflect.jvm.kotlinFunction
/**
* An Alchemist [Action] that runs a [Collektive] program.
* Requires a [node], a program [name], and the actual [program] to execute.
*/
class RunCollektiveProgram>(
node: Node,
val name: String,
val program: Aggregate.(CollektiveDevice) -> Any?,
) : AbstractAction(node) {
private val programIdentifier = SimpleMolecule(name)
/**
* The [CollektiveDevice] associated with the [node].
*/
val localDevice: CollektiveDevice = node.asProperty()
/**
* The [Collektive] program on which cycles will be executed.
*/
val collektiveProgram: Collektive
init {
declareDependencyTo(programIdentifier)
collektiveProgram =
Collektive(localDevice.id, network = localDevice) {
program(localDevice)
}
}
/**
* Create a [RunCollektiveProgram] with a specific [entrypoint] and a [node].
*/
constructor(
node: Node,
entrypoint: String,
) : this(node, entrypoint, findEntrypoint(entrypoint))
/**
* Create a [RunCollektiveProgram] with a specific [entrypoint] and a [node].
*/
@JvmOverloads
constructor(
node: Node,
entrypoint: Method,
name: String = entrypoint.name,
) : this(node, name, buildEntryPoint(entrypoint))
override fun cloneAction(
node: Node,
reaction: Reaction,
): Action = RunCollektiveProgram(node, name)
override fun execute() {
collektiveProgram.cycle().also {
node.setConcentration(programIdentifier, it)
}
}
override fun getContext(): Context = Context.NEIGHBORHOOD
private companion object {
private fun > findEntrypoint(
entrypoint: String,
): Aggregate.(CollektiveDevice) -> Any? {
val className = entrypoint.substringBeforeLast(".")
val methodName = entrypoint.substringAfterLast(".")
val clazz = Class.forName(className)
val method =
clazz.methods.find { it.name == methodName }
?: error("Entrypoint $entrypoint not found, no method $methodName found in class $className")
return buildEntryPoint(method)
}
private fun
> buildEntryPoint(method: Method): Aggregate.(CollektiveDevice) -> Any? {
val ktFunction =
checkNotNull(method.kotlinFunction) {
"Method ${method.name} in class ${method.declaringClass.name}" +
" cannot be converted to a Kotlin function"
}
// Build the lambda function to be executed
return { device: CollektiveDevice
->
val parameters =
method.parameters
.map {
when {
it.type.isAssignableFrom(Aggregate::class.java) -> this
it.type.isAssignableFrom(CollektiveDevice::class.java) -> device
it.type.isAssignableFrom(Node::class.java) -> device.node
device.node.hasPropertyCompatibleWith(it) -> device.node.getPropertyCompatibleWith(it)
else -> error("Unsupported type ${it.type} in entrypoint ${ktFunction.name}")
}
}.toTypedArray()
ktFunction.call(*parameters)
}
}
private fun Node<*>.hasPropertyCompatibleWith(parameter: Parameter): Boolean =
properties.any { parameter.type.isAssignableFrom(it::class.java) }
private fun Node<*>.getPropertyCompatibleWith(property: Parameter): NodeProperty<*> =
properties.first { property.type.isAssignableFrom(it::class.java) }
}
}