[add] added SimulationBehaviour functions

- this allows to simulate the new config file
This commit is contained in:
2020-01-29 01:19:11 -06:00
parent 4073a93f44
commit c96e8a4a0c
2 changed files with 127 additions and 1 deletions

View File

@@ -21,7 +21,7 @@ object Main {
} }
def startSimulation(): Unit = { def startSimulation(): Unit = {
val config = new Parser().parse("docs/wiki/config/config.yml", simBehavirous) val config = new Parser().parse("docs/wiki/config/config_preview.yml", scim.components.behaviour.behaviours)
val sharedMemory = config.sharedMemory val sharedMemory = config.sharedMemory
val output = new Output(sharedMemory) val output = new Output(sharedMemory)
val simulator = new Simulation(config) val simulator = new Simulation(config)

View File

@@ -0,0 +1,126 @@
package scim.components
import scim.datastruct.{Cell, FunctionArgs, SimulationBehaviours, Time}
import scim.lib.numberUtil._
import scim.lib.behaviourUtil._
import scim.lib.simInterfaces.SimulationBehaviour
import scala.util.Random
package object behaviour {
val behaviours = SimulationBehaviours(Map[String, SimulationBehaviour](
"measureTemp" -> measureTemp,
"measureHumid" -> measureHumid,
"noOutput" -> noOutput,
"fluctuate" -> fluctuate,
"change" -> change,
"humidFromTemp" -> humidFromTemp
))
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)
}
def noOutput: SimulationBehaviour = {
(_: Cell, _: Time, _: Option[Double], _: Option[FunctionArgs]) => { None }
}
def sensorFactory(defaultRange: Vector[Double], defaultResolution: Double, defaultAccuracy: Double, defaultOffset: Double,
preFunction: Option[SimulationBehaviour] = None,
postFunction: Option[SimulationBehaviour] = None,
altFunction: 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)
// useful when trying to use different params based on cell/time
val altResult = if (!altFunction.isEmpty) altFunction.get(cell, time, inTruth, args) else None
if (!altResult.isEmpty) {
altResult
} else {
// preFunction can modify ground truth
// can e.g. be used to return None or modify truth based on cell/time
val preResult = if (!preFunction.isEmpty) preFunction.get(cell, time, inTruth, args) else inTruth
if (preResult.isEmpty) {
None
} else {
val truth = preResult.get
val range: Vector[Double] = args.getParam("range", defaultRange) // using a vector to match yml parsing
val resolution: Double = args.getParam("resolution", defaultResolution)
val accuracy: Double = args.getParam("accuracy", defaultAccuracy)
val offset: Double = args.getParam("offset", defaultOffset)
val rng = new Random()
val rngAcc = rng.nextDouble() * accuracy * (if (rng.nextBoolean()) -1 else 1)
val result: Option[Double] = Some((truth + rngAcc + offset).resolution(resolution).range(range(0), range(1)))
// postFunction can modify result
// can e.g. be used to modify result based on cell, time and result itself
// (e.g. return garbage when result is above threshold to simulate failure above threshold)
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 centerCell = Cell(center(0), center(1))
centerCell.distance(cell) * distOffset
} 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)
}
def change (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 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))
// always calculate offset for target
val offset = if(distOffset != 0) {
val centerCell = Cell(center(0), center(1))
centerCell.distance(cell) * distOffset
} else { 0 }
// only change truth on offsetTimes
val truth = if(offsetTimes.contains(time.time)) inTruth.get + offset else inTruth.get
val rng = new Random()
val rngRange = rng.nextDouble() * range * (if (rng.nextBoolean()) -1 else 1)
val step: Double = if(time.time < targetTime) {
(target + offset - truth) / (targetTime - time.time).toDouble
} else { 0 }
Some(inTruth.get + rngRange + step)
}
def humidFromTemp (cell: Cell, time: Time, inTruth: Option[Double], args: Option[FunctionArgs]): Option[Double] = {
if(inTruth.isEmpty) return None
val dewpoint: Double = args.getParam("dewpoint", 12.0)
val temp: Double = inTruth.get
// source: https://bmcnoldy.rsmas.miami.edu/Humidity.html
val rh = 100*(Math.exp((17.625*dewpoint)/(243.04+dewpoint))/Math.exp((17.625*temp)/(243.04+temp)))
Some(rh)
}
}