[add][mod] added missing behaviours from example and some more factories

- measureGeneric, randomOutput, measureTempFault
 - randomOutputFactory, sensorModifierFactory
 - [mod] optional args fomr FunctionArgs now use Option[A]
This commit is contained in:
2020-01-29 01:19:11 -06:00
parent 4e5d9570ad
commit 56b46d4dfb

View File

@@ -2,6 +2,7 @@ package scim.components
import scim.datastruct.{Cell, FunctionArgs, SimulationBehaviours, Time}
import scim.lib.numberUtil._
import scim.lib.mapUtil._
import scim.lib.behaviourUtil._
import scim.lib.simInterfaces.SimulationBehaviour
@@ -10,30 +11,96 @@ import scala.util.Random
package object behaviour {
val behaviours = SimulationBehaviours(Map[String, SimulationBehaviour](
"measureGeneric" -> measureGeneric,
"measureTemp" -> measureTemp,
"measureHumid" -> measureHumid,
"noOutput" -> noOutput,
"randomOutput" -> randomOutput,
"measureTempFault" -> measureTempFault,
"fluctuate" -> fluctuate,
"change" -> change,
"humidFromTemp" -> humidFromTemp
))
def measureGeneric: SimulationBehaviour = {
sensorFactory(Vector(0, 1), 0.1, 0.1, 0.0)
}
def measureTemp: SimulationBehaviour = {
sensorFactory(Vector(-50, 150), 0.1, 1.0, 0.0)
}
def measureHumid: SimulationBehaviour = {
sensorFactory(Vector(0, 100), 1.0, 2.0, 0.0)
sensorFactory(Vector(5, 100), 1.0, 2.0, 0.0)
}
def measureTempFault: SimulationBehaviour = {
sensorModifierFactory("accuracy", Vector(-10, 10), 5, 15.0, 20.0, measureTemp)
}
def noOutput: SimulationBehaviour = {
(_: Cell, _: Time, _: Option[Double], _: Option[FunctionArgs]) => { None }
}
def randomOutput: SimulationBehaviour = {
randomOutputFactory(Vector(-50, 50))
}
def randomOutputFactory(defaultRange: Vector[Double]) : SimulationBehaviour = {
(_: Cell, _: Time, _: Option[Double], args: Option[FunctionArgs]) => {
val range: Vector[Double] = args.getParam("range", defaultRange)
val rng = new Random()
val start = range(0).intValue()
val end = range(1).intValue()
val rngRange = start + rng.nextInt((end - start) + 1)
Some((rngRange.doubleValue() + rng.nextDouble() * (if (rng.nextBoolean()) -1 else 1)).range(range(0), range(1)))
}
}
def sensorModifierFactory(defaultMod: String, modRange: Vector[Double], modResolution: Double, modAccuracy: Double, modOffset: Double,
behaviour: SimulationBehaviour): SimulationBehaviour = {
def modifyArgs(args: Option[FunctionArgs]): Option[FunctionArgs] = {
val modifier: Option[Map[String, Any]] = args.getParam("modifier")
if (!modifier.isEmpty) {
val modType = modifier.get.getKeyOrElse[String]("type", defaultMod)
// TODO: use interface to replace args
val newArgs = modType match {
case "range" => args.get.args + ("range" -> modifier.get.getKeyOrElse[Vector[Double]]("value", modRange))
case "resolution" => args.get.args + ("resolution" -> modifier.get.getKeyOrElse[Double]("value", modResolution))
case "accuracy" => args.get.args + ("accuracy" -> modifier.get.getKeyOrElse[Double]("value", modAccuracy))
case "offset" => args.get.args + ("offset" -> modifier.get.getKeyOrElse[Double]("value", modOffset))
}
Some(FunctionArgs(newArgs))
} else {
None
}
}
val modifierWrapper: SimulationBehaviour = (cell: Cell, time: Time, inTruth: Option[Double], args: Option[FunctionArgs]) => {
val modifier: Option[Map[String, Any]] = args.getParam("modifier")
if (!modifier.isEmpty) {
val x = modifier.get.getKey[Int]("x")
val y = modifier.get.getKey[Int]("y")
val timeMod = modifier.get.getKey[Int]("time")
(timeMod, x, y) match {
case (timeFault, None, None) if (!timeFault.isEmpty && timeFault.get == time.time) => behaviour(cell, time, inTruth, modifyArgs(args))
case (None, xVal, yVal) if (!xVal.isEmpty && !yVal.isEmpty && Cell(xVal.get, yVal.get) == cell) => behaviour(cell, time, inTruth, modifyArgs(args))
case (timeFault, xVal, yVal) if (!timeFault.isEmpty && timeFault.get == time.time && !xVal.isEmpty && !yVal.isEmpty && Cell(xVal.get, yVal.get) == cell) =>
behaviour(cell, time, inTruth, modifyArgs(args))
case _ => behaviour(cell, time, inTruth, args)
}
} else {
behaviour(cell, time, inTruth, args)
}
}
modifierWrapper
}
def sensorFactory(defaultRange: Vector[Double], defaultResolution: Double, defaultAccuracy: Double, defaultOffset: Double,
altFunction: Option[SimulationBehaviour] = None,
preFunction: Option[SimulationBehaviour] = None,
postFunction: Option[SimulationBehaviour] = None,
altFunction: Option[SimulationBehaviour] = None): SimulationBehaviour = {
postFunction: Option[SimulationBehaviour] = None): SimulationBehaviour = {
val genericSensor: SimulationBehaviour = (cell: Cell, time: Time, inTruth: Option[Double], args: Option[FunctionArgs]) => {
// can use altFunction to entirely disable normal behaviour (including pre/postFunction)
@@ -67,22 +134,43 @@ package object behaviour {
genericSensor
}
def sensorFactoryBehaviour(default: SimulationBehaviour,
altFunction: Option[SimulationBehaviour] = None,
preFunction: Option[SimulationBehaviour] = None,
postFunction: Option[SimulationBehaviour] = None): SimulationBehaviour = {
val genericSensor: SimulationBehaviour = (cell: Cell, time: Time, inTruth: Option[Double], args: Option[FunctionArgs]) => {
val altResult = if (!altFunction.isEmpty) altFunction.get(cell, time, inTruth, args) else None
if (!altResult.isEmpty) {
altResult
} else {
val preResult = if (!preFunction.isEmpty) preFunction.get(cell, time, inTruth, args) else inTruth
if (preResult.isEmpty) {
None
} else {
val result = default(cell, time, inTruth, args)
if (!postFunction.isEmpty) postFunction.get(cell, time, result, args) else result
}
}
}
genericSensor
}
def fluctuate (cell: Cell, time: Time, inTruth: Option[Double], args: Option[FunctionArgs]): Option[Double] = {
if(inTruth.isEmpty) return None
val range: Double = args.getParam("range", 1.0)
val center: Vector[Int] = args.getParam("center", Vector(0, 0))
val distOffset: Double = args.getParam("distOffset", 0)
val offsetTimes: Vector[Int] = args.getParam("time", Vector(-1))
val offset = if(distOffset != 0 && offsetTimes.contains(time.time)) {
val distOffset: Option[Double] = args.getParam[Double]("distOffset")
val offsetTimes: Option[Vector[Int]] = args.getParam[Vector[Int]]("time")
val offset = if(!distOffset.isEmpty && offsetTimes.get.contains(time.time)) {
val centerCell = Cell(center(0), center(1))
centerCell.distance(cell) * distOffset
centerCell.distance(cell) * distOffset.get
} else { 0 }
val rng = new Random()
val rngRange = rng.nextDouble() * range * (if (rng.nextBoolean()) -1 else 1)
//if(time.time == 0 || time.time == 11) println("\nXXX: " + cell + " :: " + inTruth.get + " :OFF: " + offset + " :RES: " + (inTruth.get + rngRange + offset))
Some(inTruth.get + rngRange + offset)
}
@@ -93,16 +181,16 @@ package object behaviour {
val target: Double = args.getParam("target", inTruth.get + 5)
val targetTime: Int = args.getParam("targetTime", time.time + 1)
val center: Vector[Int] = args.getParam("center", Vector(0, 0))
val distOffset: Double = args.getParam("distOffset", 0)
val offsetTimes: Vector[Int] = args.getParam("time", Vector(-1))
val distOffset: Option[Double] = args.getParam[Double]("distOffset")
val offsetTimes: Option[Vector[Int]] = args.getParam[Vector[Int]]("time")
// always calculate offset for target
val offset = if(distOffset != 0) {
val offset = if(!distOffset.isEmpty) {
val centerCell = Cell(center(0), center(1))
centerCell.distance(cell) * distOffset
centerCell.distance(cell) * distOffset.get
} else { 0 }
// only change truth on offsetTimes
val truth = if(offsetTimes.contains(time.time)) inTruth.get + offset else inTruth.get
val truth = if(!offsetTimes.isEmpty && offsetTimes.get.contains(time.time)) inTruth.get + offset else inTruth.get
val rng = new Random()
val rngRange = rng.nextDouble() * range * (if (rng.nextBoolean()) -1 else 1)