[add][mod] added output files and modified config

- added OutputFile class to library
 - [mod] use file from arg as config if exists
 - [mod] added readme text
This commit is contained in:
2020-01-29 02:31:50 -06:00
parent 8f47875cd7
commit 112683ea42
8 changed files with 667 additions and 606 deletions

View File

@@ -1,4 +1,6 @@
# untitled # Sensor Simulation
Sensor simulation that focuses on generating high volumes of sensor output. The output can be used to simulate faulty and correct behaviour, based on a ground truth. To remove the requirement of collecting and compose ground truth values for specific scenarios, the simulation also allows for a ground truth to be automatically generated during the simulation. The simulation is fully controllable and creates reproducible outputs with a given configuration.
## Wiki ## Wiki

View File

@@ -1,6 +1,6 @@
simulation: simulation:
duration: 24 duration: 24
tickfactor: 10 tickfactor: 1
runs: 1 runs: 1
parCell: 0 parCell: 0
parCellThreads: 5 parCellThreads: 5
@@ -19,10 +19,7 @@ sensors:
- [1, 1, 5] - [1, 1, 5]
- [2, 2, 5] - [2, 2, 5]
- [3, 3, 5] - [3, 3, 5]
blocks: - [4, 4, 5]
- start: [0, 4]
end: [6, 6]
count: 210
- group: faultyTemp - group: faultyTemp
parent: temperature parent: temperature
@@ -36,39 +33,47 @@ sensors:
cells: cells:
- [3, 3, 5] - [3, 3, 5]
# all cells are declared to output the gt # all cells are declared to output the gt
# of baseline for each into the console # of baseline for each into the console
- group: baseline - group: baseline
blocks: blocks:
- start: [0, 0] - [0, 0, 0]
end: [6, 6] - [1, 1, 0]
count: 0 - [2, 2, 0]
- [3, 3, 0]
- [4, 4, 0]
groundtruths: groundtruths:
- group: baseline - group: baseline
truth: truth:
- value: 10.0 - value: 7.0
timeline: timeline:
- start: 0 - start: 0
end: 7 end: 2
- [18, 24] - [19, 22]
- [7, 10] - [2, 7.0]
- [12, 16.0] - [22, 7.0]
delta: delta:
- name: fluctuate - name: fluctuate
args: {range: 0.0, center: [3, 3], distOffset: -0.5, time:[0, 12, 18]} args: {range: 0.0, center: [3, 3], distOffset: -0.5, time:[0, 19, 22]}
timeline: timeline:
- [0, 7] - [0, 2]
- [12, 16] - [3, 5]
- [18, 24] - [8, 9]
- [7, change, {range: 0.0, target: 16.0, targetTime: 12, center: [3, 3], distOffset: -0.5, time:[7]}] - [19, 22]
- [16, change, {range: 0.0, target: 10.0, targetTime: 18, center: [3, 3], distOffset: -0.5, time:[]}] - [23, 24]
- [2, change, {range: 0.0, target: 6.0, targetTime: 3, center: [3, 3], distOffset: -0.5, time:[2]}]
- [5, change, {range: 0.0, target: 4.0, targetTime: 7, center: [3, 3], distOffset: -0.5, time:[]}]
- [7, change, {range: 0.0, target: 5.0, targetTime: 8, center: [3, 3], distOffset: -0.5, time:[]}]
- [9, change, {range: 0.0, target: 9.0, targetTime: 17, center: [3, 3], distOffset: -0.5, time:[]}]
- [17, change, {range: 0.0, target: 7.0, targetTime: 19, center: [3, 3], distOffset: -0.5, time:[]}]
- [22, change, {range: 0.0, target: 6.0, targetTime: 23, center: [3, 3], distOffset: -0.5, time:[22]}]
- group: temperature - group: temperature
parent: baseline parent: baseline
delta: delta:
- name: fluctuate - name: fluctuate
args: {range: 1.0} args: {range: 0.3}
timeline: timeline:
- [0, 7] - [0, 7]
- [12, 16] - [12, 16]
@@ -97,23 +102,23 @@ behaviours:
- group: faultyTemp - group: faultyTemp
functions: functions:
- name: measureTemp - name: measureTemp
args: args:
range: [-50.0, 150.0] range: [-50.0, 150.0]
resolution: 0.1 resolution: 0.1
accuracy: 1.0 accuracy: 1.0
offset: 10.0 offset: 10.0
timeline: timeline:
- [0, 12] - [0, 12]
- [12, noOutput, {}] - [12, noOutput, {}]
- name: measureTemp - name: measureTemp
args: args:
range: [-50.0, 150.0] range: [-50.0, 150.0]
resolution: 0.1 resolution: 0.1
accuracy: 20.0 accuracy: 20.0
offset: 0.0 offset: 0.0
timeline: timeline:
- [15, 24] - [15, 24]
- group: humidity - group: humidity
functions: functions:

View File

@@ -8,11 +8,12 @@ import scim.lib.simInterfaces.SimulationBehaviour
object Main { object Main {
// o7 // o7
def main(args: Array[String]): Unit = { def main(args: Array[String]): Unit = {
startSimulation() val file = if(args.length > 0) args(0) else "docs/wiki/config/config_due.yml"
startSimulation(file)
} }
def startSimulation(): Unit = { def startSimulation(file: String): Unit = {
val config = new Parser().parse("docs/wiki/config/config_preview.yml", scim.components.behaviour.behaviours) val config = new Parser().parse(file, 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

@@ -1,214 +1,214 @@
package scim.components package scim.components
import scim.datastruct.{Cell, FunctionArgs, SimulationBehaviours, Time} import scim.datastruct.{Cell, FunctionArgs, SimulationBehaviours, Time}
import scim.lib.numberUtil._ import scim.lib.numberUtil._
import scim.lib.mapUtil._ import scim.lib.mapUtil._
import scim.lib.behaviourUtil._ import scim.lib.behaviourUtil._
import scim.lib.simInterfaces.SimulationBehaviour import scim.lib.simInterfaces.SimulationBehaviour
import scala.util.Random import scala.util.Random
package object behaviour { package object behaviour {
val behaviours = SimulationBehaviours(Map[String, SimulationBehaviour]( val behaviours = SimulationBehaviours(Map[String, SimulationBehaviour](
"measureGeneric" -> measureGeneric, "measureGeneric" -> measureGeneric,
"measureTemp" -> measureTemp, "measureTemp" -> measureTemp,
"measureHumid" -> measureHumid, "measureHumid" -> measureHumid,
"noOutput" -> noOutput, "noOutput" -> noOutput,
"randomOutput" -> randomOutput, "randomOutput" -> randomOutput,
"measureTempMod" -> measureTempMod, "measureTempMod" -> measureTempMod,
"fluctuate" -> fluctuate, "fluctuate" -> fluctuate,
"change" -> change, "change" -> change,
"humidFromTemp" -> humidFromTemp "humidFromTemp" -> humidFromTemp
)) ))
def measureGeneric: SimulationBehaviour = { def measureGeneric: SimulationBehaviour = {
sensorFactory(Vector(0, 1), 0.1, 0.1, 0.0) sensorFactory(Vector(0, 1), 0.1, 0.1, 0.0)
} }
def measureTemp: SimulationBehaviour = { def measureTemp: SimulationBehaviour = {
sensorFactory(Vector(-50, 150), 0.1, 1.0, 0.0) sensorFactory(Vector(-50, 150), 0.1, 1.0, 0.0)
} }
def measureHumid: SimulationBehaviour = { def measureHumid: SimulationBehaviour = {
sensorFactory(Vector(5, 100), 1.0, 2.0, 0.0) sensorFactory(Vector(5, 100), 1.0, 2.0, 0.0)
} }
def measureTempMod: SimulationBehaviour = { def measureTempMod: SimulationBehaviour = {
sensorModifierFactory("accuracy", Vector(-10, 10), 5, 15.0, 20.0, measureTemp) sensorModifierFactory("accuracy", Vector(-10, 10), 5, 15.0, 20.0, measureTemp)
} }
def noOutput: SimulationBehaviour = { def noOutput: SimulationBehaviour = {
(_: Cell, _: Time, _: Option[Double], _: Option[FunctionArgs]) => { None } (_: Cell, _: Time, _: Option[Double], _: Option[FunctionArgs]) => { None }
} }
def randomOutput: SimulationBehaviour = { def randomOutput: SimulationBehaviour = {
randomOutputFactory(Vector(-50, 50)) randomOutputFactory(Vector(-50, 50))
} }
def randomOutputFactory(defaultRange: Vector[Double]) : SimulationBehaviour = { def randomOutputFactory(defaultRange: Vector[Double]) : SimulationBehaviour = {
(_: Cell, _: Time, _: Option[Double], args: Option[FunctionArgs]) => { (_: Cell, _: Time, _: Option[Double], args: Option[FunctionArgs]) => {
val range: Vector[Double] = args.getParam("range", defaultRange) val range: Vector[Double] = args.getParam("range", defaultRange)
val rng = new Random() val rng = new Random()
val start = range(0).intValue() val start = range(0).intValue()
val end = range(1).intValue() val end = range(1).intValue()
val rngRange = start + rng.nextInt((end - start) + 1) val rngRange = start + rng.nextInt((end - start) + 1)
Some((rngRange.doubleValue() + rng.nextDouble() * (if (rng.nextBoolean()) -1 else 1)).range(range(0), range(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, def sensorModifierFactory(defaultMod: String, modRange: Vector[Double], modResolution: Double, modAccuracy: Double, modOffset: Double,
behaviour: SimulationBehaviour): SimulationBehaviour = { behaviour: SimulationBehaviour): SimulationBehaviour = {
def modifyArgs(args: Option[FunctionArgs]): Option[FunctionArgs] = { def modifyArgs(args: Option[FunctionArgs]): Option[FunctionArgs] = {
val modifier: Option[Map[String, Any]] = args.getParam("modifier") val modifier: Option[Map[String, Any]] = args.getParam("modifier")
if (!modifier.isEmpty) { if (!modifier.isEmpty) {
val modType = modifier.get.getKeyOrElse[String]("type", defaultMod) val modType = modifier.get.getKeyOrElse[String]("type", defaultMod)
// TODO: use interface to replace args // TODO: use interface to replace args
val newArgs = modType match { val newArgs = modType match {
case "range" => args.get.args + ("range" -> modifier.get.getKeyOrElse[Vector[Double]]("value", modRange)) 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 "resolution" => args.get.args + ("resolution" -> modifier.get.getKeyOrElse[Double]("value", modResolution))
case "accuracy" => args.get.args + ("accuracy" -> modifier.get.getKeyOrElse[Double]("value", modAccuracy)) case "accuracy" => args.get.args + ("accuracy" -> modifier.get.getKeyOrElse[Double]("value", modAccuracy))
case "offset" => args.get.args + ("offset" -> modifier.get.getKeyOrElse[Double]("value", modOffset)) case "offset" => args.get.args + ("offset" -> modifier.get.getKeyOrElse[Double]("value", modOffset))
} }
Some(FunctionArgs(newArgs)) Some(FunctionArgs(newArgs))
} else { } else {
None None
} }
} }
val modifierWrapper: SimulationBehaviour = (cell: Cell, time: Time, inTruth: Option[Double], args: Option[FunctionArgs]) => { val modifierWrapper: SimulationBehaviour = (cell: Cell, time: Time, inTruth: Option[Double], args: Option[FunctionArgs]) => {
val modifier: Option[Map[String, Any]] = args.getParam("modifier") val modifier: Option[Map[String, Any]] = args.getParam("modifier")
if (!modifier.isEmpty) { if (!modifier.isEmpty) {
val x = modifier.get.getKey[Int]("x") val x = modifier.get.getKey[Int]("x")
val y = modifier.get.getKey[Int]("y") val y = modifier.get.getKey[Int]("y")
val timeMod = modifier.get.getKey[Int]("time") val timeMod = modifier.get.getKey[Int]("time")
(timeMod, x, y) match { (timeMod, x, y) match {
case (timeFault, None, None) if (!timeFault.isEmpty && (timeFault.get * time.tickfactor.getOrElse(1)) == time.time) => behaviour(cell, time, inTruth, modifyArgs(args)) case (timeFault, None, None) if (!timeFault.isEmpty && (timeFault.get * time.tickfactor.getOrElse(1)) == 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 (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.tickfactor.getOrElse(1)) == time.time && !xVal.isEmpty && !yVal.isEmpty && Cell(xVal.get, yVal.get) == cell) => case (timeFault, xVal, yVal) if (!timeFault.isEmpty && (timeFault.get * time.tickfactor.getOrElse(1)) == time.time && !xVal.isEmpty && !yVal.isEmpty && Cell(xVal.get, yVal.get) == cell) =>
behaviour(cell, time, inTruth, modifyArgs(args)) behaviour(cell, time, inTruth, modifyArgs(args))
case _ => behaviour(cell, time, inTruth, args) case _ => behaviour(cell, time, inTruth, args)
} }
} else { } else {
behaviour(cell, time, inTruth, args) behaviour(cell, time, inTruth, args)
} }
} }
modifierWrapper modifierWrapper
} }
def sensorFactory(defaultRange: Vector[Double], defaultResolution: Double, defaultAccuracy: Double, defaultOffset: Double, def sensorFactory(defaultRange: Vector[Double], defaultResolution: Double, defaultAccuracy: Double, defaultOffset: Double,
altFunction: Option[SimulationBehaviour] = None, altFunction: Option[SimulationBehaviour] = None,
preFunction: Option[SimulationBehaviour] = None, preFunction: Option[SimulationBehaviour] = None,
postFunction: Option[SimulationBehaviour] = None): SimulationBehaviour = { postFunction: Option[SimulationBehaviour] = None): SimulationBehaviour = {
val genericSensor: SimulationBehaviour = (cell: Cell, time: Time, inTruth: Option[Double], args: Option[FunctionArgs]) => { val genericSensor: SimulationBehaviour = (cell: Cell, time: Time, inTruth: Option[Double], args: Option[FunctionArgs]) => {
// can use altFunction to entirely disable normal behaviour (including pre/postFunction) // can use altFunction to entirely disable normal behaviour (including pre/postFunction)
// useful when trying to use different params based on cell/time // useful when trying to use different params based on cell/time
val altResult = if (!altFunction.isEmpty) altFunction.get(cell, time, inTruth, args) else None val altResult = if (!altFunction.isEmpty) altFunction.get(cell, time, inTruth, args) else None
if (!altResult.isEmpty) { if (!altResult.isEmpty) {
altResult altResult
} else { } else {
// preFunction can modify ground truth // preFunction can modify ground truth
// can e.g. be used to return None or modify truth based on cell/time // 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 val preResult = if (!preFunction.isEmpty) preFunction.get(cell, time, inTruth, args) else inTruth
if (preResult.isEmpty) { if (preResult.isEmpty) {
None None
} else { } else {
val truth = preResult.get val truth = preResult.get
val range: Vector[Double] = args.getParam("range", defaultRange) // using a vector to match yml parsing val range: Vector[Double] = args.getParam("range", defaultRange) // using a vector to match yml parsing
val resolution: Double = args.getParam("resolution", defaultResolution) val resolution: Double = args.getParam("resolution", defaultResolution)
val accuracy: Double = args.getParam("accuracy", defaultAccuracy) val accuracy: Double = args.getParam("accuracy", defaultAccuracy)
val offset: Double = args.getParam("offset", defaultOffset) val offset: Double = args.getParam("offset", defaultOffset)
val rng = new Random() val rng = new Random()
val rngAcc = rng.nextDouble() * accuracy * (if (rng.nextBoolean()) -1 else 1) 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))) val result: Option[Double] = Some((truth + rngAcc + offset).resolution(resolution).range(range(0), range(1)))
// postFunction can modify result // postFunction can modify result
// can e.g. be used to modify result based on cell, time and result itself // 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) // (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 if (!postFunction.isEmpty) postFunction.get(cell, time, result, args) else result
} }
} }
} }
genericSensor genericSensor
} }
def sensorFactoryBehaviour(default: SimulationBehaviour, def sensorFactoryBehaviour(default: SimulationBehaviour,
altFunction: Option[SimulationBehaviour] = None, altFunction: Option[SimulationBehaviour] = None,
preFunction: Option[SimulationBehaviour] = None, preFunction: Option[SimulationBehaviour] = None,
postFunction: Option[SimulationBehaviour] = None): SimulationBehaviour = { postFunction: Option[SimulationBehaviour] = None): SimulationBehaviour = {
val genericSensor: SimulationBehaviour = (cell: Cell, time: Time, inTruth: Option[Double], args: Option[FunctionArgs]) => { 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 val altResult = if (!altFunction.isEmpty) altFunction.get(cell, time, inTruth, args) else None
if (!altResult.isEmpty) { if (!altResult.isEmpty) {
altResult altResult
} else { } else {
val preResult = if (!preFunction.isEmpty) preFunction.get(cell, time, inTruth, args) else inTruth val preResult = if (!preFunction.isEmpty) preFunction.get(cell, time, inTruth, args) else inTruth
if (preResult.isEmpty) { if (preResult.isEmpty) {
None None
} else { } else {
val result = default(cell, time, inTruth, args) val result = default(cell, time, inTruth, args)
if (!postFunction.isEmpty) postFunction.get(cell, time, result, args) else result if (!postFunction.isEmpty) postFunction.get(cell, time, result, args) else result
} }
} }
} }
genericSensor genericSensor
} }
def fluctuate (cell: Cell, time: Time, inTruth: Option[Double], args: Option[FunctionArgs]): Option[Double] = { def fluctuate (cell: Cell, time: Time, inTruth: Option[Double], args: Option[FunctionArgs]): Option[Double] = {
if(inTruth.isEmpty) return None if(inTruth.isEmpty) return None
val range: Double = args.getParam("range", 1.0) val range: Double = args.getParam("range", 0.0)
val center: Vector[Int] = args.getParam("center", Vector(0, 0)) val center: Vector[Int] = args.getParam("center", Vector(0, 0))
val distOffset: Option[Double] = args.getParam[Double]("distOffset") val distOffset: Option[Double] = args.getParam[Double]("distOffset")
val offsetTimes: Option[Vector[Int]] = args.getParam[Vector[Int]]("time") val offsetTimes: Option[Vector[Int]] = args.getParam[Vector[Int]]("time")
val offset = if(!distOffset.isEmpty && offsetTimes.get.contains(time.getNormalized().getOrElse(-1))) { val offset = if(!distOffset.isEmpty && offsetTimes.get.contains(time.getNormalized().getOrElse(-1))) {
val centerCell = Cell(center(0), center(1)) val centerCell = Cell(center(0), center(1))
centerCell.distance(cell) * distOffset.get centerCell.distance(cell) * distOffset.get
} else { 0 } } else { 0 }
val rng = new Random() val rng = new Random()
val rngRange = rng.nextDouble() * range * (if (rng.nextBoolean()) -1 else 1) val rngRange = rng.nextDouble() * range * (if (rng.nextBoolean()) -1 else 1)
Some(inTruth.get + rngRange + offset) Some(inTruth.get + rngRange + offset)
} }
def change (cell: Cell, time: Time, inTruth: Option[Double], args: Option[FunctionArgs]): Option[Double] = { def change (cell: Cell, time: Time, inTruth: Option[Double], args: Option[FunctionArgs]): Option[Double] = {
if(inTruth.isEmpty) return None if(inTruth.isEmpty) return None
val range: Double = args.getParam("range", 1.0) val range: Double = args.getParam("range", 0.0)
val target: Double = args.getParam("target", inTruth.get + 5) val target: Double = args.getParam("target", inTruth.get + 5)
val targetTime: Int = args.getParam("targetTime", time.getNormalized + 1) * time.tickfactor.getOrElse(1) + 1 val targetTime: Int = args.getParam("targetTime", time.getNormalized + 1) * time.tickfactor.getOrElse(1) + 1
val center: Vector[Int] = args.getParam("center", Vector(0, 0)) val center: Vector[Int] = args.getParam("center", Vector(0, 0))
val distOffset: Option[Double] = args.getParam[Double]("distOffset") val distOffset: Option[Double] = args.getParam[Double]("distOffset")
val offsetTimes: Option[Vector[Int]] = args.getParam[Vector[Int]]("time") val offsetTimes: Option[Vector[Int]] = args.getParam[Vector[Int]]("time")
// always calculate offset for target // always calculate offset for target
val offset = if(!distOffset.isEmpty) { val offset = if(!distOffset.isEmpty) {
val centerCell = Cell(center(0), center(1)) val centerCell = Cell(center(0), center(1))
centerCell.distance(cell) * distOffset.get centerCell.distance(cell) * distOffset.get
} else { 0 } } else { 0 }
// only change truth on offsetTimes // only change truth on offsetTimes
val truth = if(!offsetTimes.isEmpty && offsetTimes.get.contains(time.getNormalized().getOrElse(-1))) inTruth.get + offset else inTruth.get val truth = if(!offsetTimes.isEmpty && offsetTimes.get.contains(time.getNormalized().getOrElse(-1))) inTruth.get + offset else inTruth.get
val rng = new Random() val rng = new Random()
val rngRange = rng.nextDouble() * range * (if (rng.nextBoolean()) -1 else 1) val rngRange = rng.nextDouble() * range * (if (rng.nextBoolean()) -1 else 1)
val step: Double = if(time.time < targetTime) { val step: Double = if(time.time < targetTime) {
(target + offset - truth) / (targetTime - time.time).toDouble (target + offset - truth) / (targetTime - time.time).toDouble
} else { 0 } } else { 0 }
Some(truth + rngRange + step) Some(truth + rngRange + step)
} }
def humidFromTemp (cell: Cell, time: Time, inTruth: Option[Double], args: Option[FunctionArgs]): Option[Double] = { def humidFromTemp (cell: Cell, time: Time, inTruth: Option[Double], args: Option[FunctionArgs]): Option[Double] = {
if(inTruth.isEmpty) return None if(inTruth.isEmpty) return None
val dewpoint: Double = args.getParam("dewpoint", 12.0) val dewpoint: Double = args.getParam("dewpoint", 12.0)
val temp: Double = inTruth.get val temp: Double = inTruth.get
// source: https://bmcnoldy.rsmas.miami.edu/Humidity.html // 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))) val rh = 100*(Math.exp((17.625*dewpoint)/(243.04+dewpoint))/Math.exp((17.625*temp)/(243.04+temp)))
Some(rh) Some(rh)
} }
} }

View File

@@ -2,16 +2,27 @@ package scim.components
import scim.datastruct.{Cell, Group, SensorResult, SharedMemory, Time, Truth} import scim.datastruct.{Cell, Group, SensorResult, SharedMemory, Time, Truth}
import scim.lib.consoleUtil.Color._ import scim.lib.consoleUtil.Color._
import scim.lib.io._
class Output(sharedMemory: SharedMemory, printOutput: Boolean = true) extends Runnable { class Output(sharedMemory: SharedMemory, printOutput: Boolean = true) extends Runnable {
val outputFileBaseline = OutputFile("baseline.txt")
val outputFileTemperature = OutputFile("temperature.txt")
val outputFileFaultyTemp = OutputFile("faultyTemp.txt")
val outputFileSensor = OutputFile("sensor_temp.txt")
val outputFileFaultySensor = OutputFile("sensor_fault.txt")
def run(): Unit = { def run(): Unit = {
val duration = sharedMemory.config.getDuration val duration = sharedMemory.config.getDuration
def outputHelper(time: Time): Unit = { def outputHelper(time: Time): Unit = {
val simOutput = sharedMemory.subscribe(time) val simOutput = sharedMemory.subscribe(time)
outputData(time, simOutput._1, simOutput._2) outputData(time, simOutput._1, simOutput._2)
} }
//outputFile.buffer("group,time,x,y,gt\n")
Vector.tabulate(duration)(t => Time(t, duration)).foreach(outputHelper) Vector.tabulate(duration)(t => Time(t, duration)).foreach(outputHelper)
outputFileBaseline.write()
outputFileTemperature.write()
outputFileFaultyTemp.write()
outputFileSensor.write()
outputFileFaultySensor.write()
} }
def outputData(time: Time, sensors: Map[Cell, Map[Group, Vector[SensorResult]]], groundTruths: Map[Cell, Map[Group, Truth]]): Unit = { def outputData(time: Time, sensors: Map[Cell, Map[Group, Vector[SensorResult]]], groundTruths: Map[Cell, Map[Group, Truth]]): Unit = {
@@ -28,6 +39,22 @@ class Output(sharedMemory: SharedMemory, printOutput: Boolean = true) extends Ru
groups._2.foreach(res => print(res.get._3.getOrElse("None") + (if(res.eq(lastElement.get)) "" else " | "))) groups._2.foreach(res => print(res.get._3.getOrElse("None") + (if(res.eq(lastElement.get)) "" else " | ")))
if(groundTruths.contains(cell) && groundTruths(cell).contains(group)) print(GREEN("\n Ground Truth: ") + groundTruths(cell)(group).peak().getOrElse("None")) if(groundTruths.contains(cell) && groundTruths(cell).contains(group)) print(GREEN("\n Ground Truth: ") + groundTruths(cell)(group).peak().getOrElse("None"))
print("\n") print("\n")
val x = cell.x; val y = cell.y
val groupName = group.name
val groupParent = group.parent.getOrElse("");
val value = groundTruths(cell)(group).peak().getOrElse(None).getOrElse(None)
val sResult = if(groups._2.size > 0) groups._2(0).get._3.getOrElse(None) else None
if(groupName == "baseline") {
outputFileBaseline.buffer(time.time + "," + x + "," + y + "," + value.toString + "\n")
} else if(groupName == "temperature") {
outputFileTemperature.buffer(time.time + "," + x + "," + y + "," + value.toString + "\n")
outputFileSensor.buffer(time.time + "," + x + "," + y + "," + sResult.toString + "\n")
} else if(groupName == "faultyTemp") {
outputFileFaultyTemp.buffer(time.time + "," + x + "," + y + "," + value.toString + "\n")
outputFileFaultySensor.buffer(time.time + "," + x + "," + y + "," + sResult.toString + "\n")
}
}) })
}) })
} }

View File

@@ -1,348 +1,348 @@
package scim.components package scim.components
import scim.datastruct.{Behaviour, BehaviourVector, Cell, Configuration, FunctionArgs, GroundTruth, Group, Sensor, SensorCount, SensorResult, SharedMemory, SimulationBehaviours, SimulationConfig, Time, Truth} import scim.datastruct.{Behaviour, BehaviourVector, Cell, Configuration, FunctionArgs, GroundTruth, Group, Sensor, SensorCount, SensorResult, SharedMemory, SimulationBehaviours, SimulationConfig, Time, Truth}
import scim.lib.debugUtil.{DebugType, debugf} import scim.lib.debugUtil.{DebugType, debugf}
import scim.lib.mapUtil._ import scim.lib.mapUtil._
import scim.lib.simInterfaces.SimulationBehaviour import scim.lib.simInterfaces.SimulationBehaviour
import scim.lib.yml import scim.lib.yml
import scim.lib.parserUtil._ import scim.lib.parserUtil._
import scala.collection.mutable import scala.collection.mutable
class Parser() { class Parser() {
def parse(file: String, simulationBehaviours: SimulationBehaviours): Configuration = { def parse(file: String, simulationBehaviours: SimulationBehaviours): Configuration = {
val parser = new SimulationConfigParser(yml.parse(file), simulationBehaviours) val parser = new SimulationConfigParser(yml.parse(file), simulationBehaviours)
parser.parse() parser.parse()
} }
} }
// when creating (Group, Sensor, Option[GroundTruth]), keep ref in Map[Group, Sensor, Option[GroundTruth]] for all, to find // when creating (Group, Sensor, Option[GroundTruth]), keep ref in Map[Group, Sensor, Option[GroundTruth]] for all, to find
// root group via group.parent (while !isempty get next) (maybe tree instead) // root group via group.parent (while !isempty get next) (maybe tree instead)
// for cells s-> create Map[Group, Map[Cell, Count]] // for cells s-> create Map[Group, Map[Cell, Count]]
// for gt g-> create Map[Group, GroundTruth] // for gt g-> create Map[Group, GroundTruth]
// for behaviour b-> create Map[Group, Behaviour] // for behaviour b-> create Map[Group, Behaviour]
// sensors = s.map(kv => (kv._1, kv._2.map(kv2 => (kv2._1, SENSOR, TRUTH))) // sensors = s.map(kv => (kv._1, kv._2.map(kv2 => (kv2._1, SENSOR, TRUTH)))
// sensors.foreach(kv => kv._1 // sensors.foreach(kv => kv._1
class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: SimulationBehaviours) { class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: SimulationBehaviours) {
private val config: SimulationConfig = parseSimulationConfig(input("simulation").asInstanceOf[Map[String, Any]]) private val config: SimulationConfig = parseSimulationConfig(input("simulation").asInstanceOf[Map[String, Any]])
private val groups = mutable.Map[String, Group]().empty private val groups = mutable.Map[String, Group]().empty
private val sensorList = mutable.Map[Cell, mutable.Map[Group, SensorCount]]().empty private val sensorList = mutable.Map[Cell, mutable.Map[Group, SensorCount]]().empty
private val behaviourListSensor = mutable.Map[Group, Behaviour]().empty private val behaviourListSensor = mutable.Map[Group, Behaviour]().empty
private val behaviourListGroundTruth = mutable.Map[Group, Behaviour]().empty private val behaviourListGroundTruth = mutable.Map[Group, Behaviour]().empty
private val groundTruthValues = mutable.Map[Time, mutable.Map[Group, Truth]]() // FIXME: are not parsed correctly private val groundTruthValues = mutable.Map[Time, mutable.Map[Group, Truth]]() // FIXME: are not parsed correctly
// this will throw if the map doesn't contain what it should // this will throw if the map doesn't contain what it should
@throws(classOf[Exception]) @throws(classOf[Exception])
def parse(): Configuration = { def parse(): Configuration = {
val sensors = input("sensors").asInstanceOf[Vector[Map[String, Any]]] val sensors = input("sensors").asInstanceOf[Vector[Map[String, Any]]]
val groundTruths = input("groundtruths").asInstanceOf[Vector[Map[String, Any]]] val groundTruths = input("groundtruths").asInstanceOf[Vector[Map[String, Any]]]
val behaviours = input("behaviours").asInstanceOf[Vector[Map[String, Any]]] val behaviours = input("behaviours").asInstanceOf[Vector[Map[String, Any]]]
sensors.foreach(sensor => parseSensor(sensor)) sensors.foreach(sensor => parseSensor(sensor))
behaviours.foreach(behaviour => parseBehaviour(behaviour)) behaviours.foreach(behaviour => parseBehaviour(behaviour))
groundTruths.foreach(groundTruth => parseGroundTruth(groundTruth)) groundTruths.foreach(groundTruth => parseGroundTruth(groundTruth))
// TODO: insert dummy sensors if none exists at cells of the parent group where this group exists // TODO: insert dummy sensors if none exists at cells of the parent group where this group exists
// TODO: when inserting time based values, only overwrite None, dont overwrite Some() with None! // TODO: when inserting time based values, only overwrite None, dont overwrite Some() with None!
// e.g. truth was declared at tick 9 with value 11, but later again with None at tick 9, DONT OVERWRITE // e.g. truth was declared at tick 9 with value 11, but later again with None at tick 9, DONT OVERWRITE
// e.g. truth was declared at tick 9 with value none, but later again with value 9, OVERWRITE // e.g. truth was declared at tick 9 with value none, but later again with value 9, OVERWRITE
//populate configuration //populate configuration
val configurationMapping = sensorList.map(cell => cell._1 -> cellMapper(cell._1, cell._2.toMap)).toMap val configurationMapping = sensorList.map(cell => cell._1 -> cellMapper(cell._1, cell._2.toMap)).toMap
// construct shared memory sensor // construct shared memory sensor
val sharedMemorySensor = Vector.tabulate(config.getDuration)(t => (Time(t, config.getDuration), sensorList.map(kv => kv._1 -> kv._2.map(kv2 => kv2._1 -> Vector.tabulate(kv2._2.count)(_ => SensorResult())).toMap).toMap)) val sharedMemorySensor = Vector.tabulate(config.getDuration)(t => (Time(t, config.getDuration), sensorList.map(kv => kv._1 -> kv._2.map(kv2 => kv2._1 -> Vector.tabulate(kv2._2.count)(_ => SensorResult())).toMap).toMap))
def truthHelper(cell: Cell, list: Map[Group, List[(Group, Sensor, Option[GroundTruth])]]): (Cell, Map[Group, Truth]) = { def truthHelper(cell: Cell, list: Map[Group, List[(Group, Sensor, Option[GroundTruth])]]): (Cell, Map[Group, Truth]) = {
val groupList: mutable.Map[Group, Truth] = mutable.Map() val groupList: mutable.Map[Group, Truth] = mutable.Map()
list.foreach(kv => kv._2.foreach(e => groupList(e._1) = Truth())) list.foreach(kv => kv._2.foreach(e => groupList(e._1) = Truth()))
cell -> groupList.toMap cell -> groupList.toMap
} }
val sharedMemoryTruth = Vector.tabulate(config.getDuration)(t => (Time(t, config.getDuration), configurationMapping.map(a => truthHelper(a._1, a._2)))) val sharedMemoryTruth = Vector.tabulate(config.getDuration)(t => (Time(t, config.getDuration), configurationMapping.map(a => truthHelper(a._1, a._2))))
val sharedMemorySensorMap = traversableToMap[Time, Map[Cell, Map[Group, Vector[SensorResult]]]](sharedMemorySensor) val sharedMemorySensorMap = traversableToMap[Time, Map[Cell, Map[Group, Vector[SensorResult]]]](sharedMemorySensor)
val sharedMemoryTruthMap = traversableToMap[Time, Map[Cell, Map[Group, Truth]]](sharedMemoryTruth) val sharedMemoryTruthMap = traversableToMap[Time, Map[Cell, Map[Group, Truth]]](sharedMemoryTruth)
val sharedMemory = SharedMemory(sharedMemorySensorMap, sharedMemoryTruthMap, groundTruthValues.map(kv => kv._1 -> kv._2.toMap).toMap, config, groups.toMap) val sharedMemory = SharedMemory(sharedMemorySensorMap, sharedMemoryTruthMap, groundTruthValues.map(kv => kv._1 -> kv._2.toMap).toMap, config, groups.toMap)
Configuration(configurationMapping, sharedMemory) Configuration(configurationMapping, sharedMemory)
} }
private def parseSimulationConfig(configInput: Map[String, Any]): SimulationConfig = { private def parseSimulationConfig(configInput: Map[String, Any]): SimulationConfig = {
val config = configInput.asInstanceOf[Map[String, Int]] val config = configInput.asInstanceOf[Map[String, Int]]
val duration = config("duration") val duration = config("duration")
val runs = config("runs") val runs = config("runs")
val tickfactor = if(config.contains("tickfactor")) config("tickfactor") else 1 val tickfactor = if(config.contains("tickfactor")) config("tickfactor") else 1
val parCell = if(config.contains("parCell")) config("parCell") else 0 val parCell = if(config.contains("parCell")) config("parCell") else 0
val parCellThreads = if(config.contains("parCellThreads")) config("parCellThreads") else 5 val parCellThreads = if(config.contains("parCellThreads")) config("parCellThreads") else 5
val parGroup = if(config.contains("parGroup")) config("parGroup") else 0 val parGroup = if(config.contains("parGroup")) config("parGroup") else 0
val parGroupThreads = if(config.contains("parGroupThreads")) config("parGroupThreads") else 5 val parGroupThreads = if(config.contains("parGroupThreads")) config("parGroupThreads") else 5
val parSensor = if(config.contains("parSensor")) config("parSensor") else 0 val parSensor = if(config.contains("parSensor")) config("parSensor") else 0
val parSensorThreads = if(config.contains("parSensorThreads")) config("parSensorThreads") else 5 val parSensorThreads = if(config.contains("parSensorThreads")) config("parSensorThreads") else 5
SimulationConfig(duration, runs, tickfactor, parCell, parCellThreads, parGroup, parGroupThreads, parSensor, parSensorThreads) SimulationConfig(duration, runs, tickfactor, parCell, parCellThreads, parGroup, parGroupThreads, parSensor, parSensorThreads)
} }
private def parseSensor(sensor: Map[String, Any]): Unit = { private def parseSensor(sensor: Map[String, Any]): Unit = {
val groupString = sensor("group").asInstanceOf[String] val groupString = sensor("group").asInstanceOf[String]
val parentString: Option[String] = if(sensor.contains("parent")) Some(sensor("parent").asInstanceOf[String]) else None val parentString: Option[String] = if(sensor.contains("parent")) Some(sensor("parent").asInstanceOf[String]) else None
val cellsParsed: Map[Cell, SensorCount] = if(sensor.contains("cells")) parseCells(sensor("cells").asInstanceOf[Vector[Any]]) else Map() val cellsParsed: Map[Cell, SensorCount] = if(sensor.contains("cells")) parseCells(sensor("cells").asInstanceOf[Vector[Any]]) else Map()
val blocksParsed: Map[Cell, SensorCount] = if(sensor.contains("blocks")) parseBlocks(sensor("blocks").asInstanceOf[Vector[Any]]) else Map() val blocksParsed: Map[Cell, SensorCount] = if(sensor.contains("blocks")) parseBlocks(sensor("blocks").asInstanceOf[Vector[Any]]) else Map()
val cells = mergeMaps[Cell, SensorCount](blocksParsed, cellsParsed, (a: SensorCount, b: SensorCount) => a.add(b), SensorCount(0)) val cells = mergeMaps[Cell, SensorCount](blocksParsed, cellsParsed, (a: SensorCount, b: SensorCount) => a.add(b), SensorCount(0))
val group = Group(groupString, parentString) val group = Group(groupString, parentString)
if(!groups.contains(groupString)) { if(!groups.contains(groupString)) {
groups(groupString) = group groups(groupString) = group
cells.foreach(kv => { cells.foreach(kv => {
val cell = kv._1 val cell = kv._1
val count = kv._2 val count = kv._2
if(sensorList.contains(cell)) { if(sensorList.contains(cell)) {
if(sensorList(cell).contains(group)) { if(sensorList(cell).contains(group)) {
// just add sensor count if one was already defined // just add sensor count if one was already defined
sensorList(cell)(group) = sensorList(cell)(group).add(count) sensorList(cell)(group) = sensorList(cell)(group).add(count)
} else { } else {
sensorList(cell)(group) = count sensorList(cell)(group) = count
} }
} else { } else {
sensorList(cell) = mutable.Map(group -> count) sensorList(cell) = mutable.Map(group -> count)
} }
}) })
} else { } else {
debugf(DebugType.WARNING, "Duplicate Sensors in Group [%s]. Ignoring Sensor: %s", groupString, sensor.toString()) debugf(DebugType.WARNING, "Duplicate Sensors in Group [%s]. Ignoring Sensor: %s", groupString, sensor.toString())
} }
} }
private def parseCells(cells: Vector[Any]): Map[Cell, SensorCount] = { private def parseCells(cells: Vector[Any]): Map[Cell, SensorCount] = {
val parseCell: Any => (Cell, SensorCount) = (inCell: Any) => inCell match { val parseCell: Any => (Cell, SensorCount) = (inCell: Any) => inCell match {
case cell: Vector[_] => val cellVector = cell.asInstanceOf[Vector[Int]] case cell: Vector[_] => val cellVector = cell.asInstanceOf[Vector[Int]]
(Cell(cellVector(0), cellVector(1)), SensorCount(cellVector(2))) (Cell(cellVector(0), cellVector(1)), SensorCount(cellVector(2)))
case cell: Map[_, _] => val cellMap = cell.asInstanceOf[Map[String, Int]] case cell: Map[_, _] => val cellMap = cell.asInstanceOf[Map[String, Int]]
(Cell(cellMap("x"), cellMap("y")), SensorCount(cellMap("count"))) (Cell(cellMap("x"), cellMap("y")), SensorCount(cellMap("count")))
case _ => throw new Exception("Invalid Cell: " + inCell.toString) case _ => throw new Exception("Invalid Cell: " + inCell.toString)
} }
// traversableToMap iterates over the vector while parseCell returns a key value // traversableToMap iterates over the vector while parseCell returns a key value
// pair for each element which than gets added to the returned map // pair for each element which than gets added to the returned map
traversableToMap[Cell, SensorCount](cells, parseCell) traversableToMap[Cell, SensorCount](cells, parseCell)
} }
private def parseBlocks(blocks: Vector[Any]): Map[Cell, SensorCount] = { private def parseBlocks(blocks: Vector[Any]): Map[Cell, SensorCount] = {
val parseBlock: Any => Map[Cell, SensorCount] = (inBlock: Any) => inBlock match { val parseBlock: Any => Map[Cell, SensorCount] = (inBlock: Any) => inBlock match {
case block: Map[_, _] => { case block: Map[_, _] => {
val blockMap = block.asInstanceOf[Map[String, Any]] val blockMap = block.asInstanceOf[Map[String, Any]]
val start = blockMap("start").asInstanceOf[Vector[Int]] val start = blockMap("start").asInstanceOf[Vector[Int]]
val end = blockMap("end").asInstanceOf[Vector[Int]] val end = blockMap("end").asInstanceOf[Vector[Int]]
val count = blockMap("count").asInstanceOf[Int] val count = blockMap("count").asInstanceOf[Int]
val blockCells = Cell(start(0), start(1)).block(Cell(end(0), end(1))) val blockCells = Cell(start(0), start(1)).block(Cell(end(0), end(1)))
val countPerCell: Int = count / blockCells.length val countPerCell: Int = count / blockCells.length
var countRest: Int = count % blockCells.length var countRest: Int = count % blockCells.length
// TODO: filter out empty cells and return early if countPerCell == 0
val result: Vector[(Cell, SensorCount)] = blockCells.map(cell => { val result: Vector[(Cell, SensorCount)] = blockCells.map(cell => {
val sensorCount = SensorCount(countPerCell + (if(countRest > 0) 1 else 0)) val sensorCount = SensorCount(countPerCell + (if(countRest > 0) 1 else 0))
if(countRest > 0) countRest -= 1 if(countRest > 0) countRest -= 1
cell -> sensorCount cell -> sensorCount
}) })
result.toMap result.toMap
} }
case _ => throw new Exception("Invalid Block: " + inBlock.toString) case _ => throw new Exception("Invalid Block: " + inBlock.toString)
} }
mergeMaps[Cell, SensorCount](blocks.map(parseBlock), (a: SensorCount, b: SensorCount) => a.add(b), SensorCount(0)) mergeMaps[Cell, SensorCount](blocks.map(parseBlock), (a: SensorCount, b: SensorCount) => a.add(b), SensorCount(0))
} }
// don't add tickfacor here, this is done in parseTruth with config.time() // don't add tickfacor here, this is done in parseTruth with config.time() and parseFunction with config.tickfactor
private def parseTimeLineElement(timeLineElement: Any): (Int, Option[Int]) = { private def parseTimeLineElement(timeLineElement: Any): (Int, Option[Int]) = {
timeLineElement match { timeLineElement match {
case timeLineElement: Vector[_] => val elementVector = timeLineElement.asInstanceOf[Vector[Int]] case timeLineElement: Vector[_] => val elementVector = timeLineElement.asInstanceOf[Vector[Int]]
(elementVector(0), if(elementVector.size > 1) Some(elementVector(1)) else None) (elementVector(0), if(elementVector.size > 1) Some(elementVector(1)) else None)
case timeLineElement: Map[_, _] => val elementMap = timeLineElement.asInstanceOf[Map[String, Int]] case timeLineElement: Map[_, _] => val elementMap = timeLineElement.asInstanceOf[Map[String, Int]]
(elementMap("start"), if(elementMap.contains("end")) Some(elementMap("end")) else None) (elementMap("start"), if(elementMap.contains("end")) Some(elementMap("end")) else None)
} }
} }
private def parseFunction(functions: Vector[Any]): Behaviour = { private def parseFunction(functions: Vector[Any]): Behaviour = {
val parseFunctionHelper = (function: Any) => { val parseFunctionHelper = (function: Any) => {
var funcPointer: Option[(SimulationBehaviour, Option[FunctionArgs])] = None var funcPointer: Option[(SimulationBehaviour, Option[FunctionArgs])] = None
function match { function match {
case function: Vector[_] => { case function: Vector[_] => {
val start: Int = function(0).asInstanceOf[Int] * config.tickfactor val start: Int = function(0).asInstanceOf[Int] * config.tickfactor
// val end: Option[Int] = None // Not used // val end: Option[Int] = None // Not used
val name: String = function(1).asInstanceOf[String] val name: String = function(1).asInstanceOf[String]
val args: Option[FunctionArgs] = if (function.size > 2) Some(FunctionArgs(function(2).asInstanceOf[Map[String, Any]])) else None val args: Option[FunctionArgs] = if (function.size > 2) Some(FunctionArgs(function(2).asInstanceOf[Map[String, Any]])) else None
if (!simulationBehaviours.get(name).isEmpty) funcPointer = Some((simulationBehaviours.get(name).get, args)) if (!simulationBehaviours.get(name).isEmpty) funcPointer = Some((simulationBehaviours.get(name).get, args))
Map[Int, Option[(SimulationBehaviour, Option[FunctionArgs])]](start -> funcPointer) // using a map here since the second case can produces multiple keys Map[Int, Option[(SimulationBehaviour, Option[FunctionArgs])]](start -> funcPointer) // using a map here since the second case can produces multiple keys
} }
case function: Map[_, _] => { case function: Map[_, _] => {
val functionMap = function.asInstanceOf[Map[String, Any]] val functionMap = function.asInstanceOf[Map[String, Any]]
val timeline: Vector[Any] = functionMap("timeline").asInstanceOf[Vector[Any]] val timeline: Vector[Any] = functionMap("timeline").asInstanceOf[Vector[Any]]
val timeLineVector: Vector[(Int, Option[Int])] = timeline.map(e => parseTimeLineElement(e)) val timeLineVector: Vector[(Int, Option[Int])] = timeline.map(e => parseTimeLineElement(e))
val name: String = functionMap("name").asInstanceOf[String] val name: String = functionMap("name").asInstanceOf[String]
val args: Option[FunctionArgs] = if (functionMap.contains("args")) Some(FunctionArgs(functionMap("args").asInstanceOf[Map[String, Any]])) else None val args: Option[FunctionArgs] = if (functionMap.contains("args")) Some(FunctionArgs(functionMap("args").asInstanceOf[Map[String, Any]])) else None
if (!simulationBehaviours.get(name).isEmpty) funcPointer = Some((simulationBehaviours.get(name).get, args)) if (!simulationBehaviours.get(name).isEmpty) funcPointer = Some((simulationBehaviours.get(name).get, args))
val output: mutable.Map[Int, Option[(SimulationBehaviour, Option[FunctionArgs])]] = mutable.Map() val output: mutable.Map[Int, Option[(SimulationBehaviour, Option[FunctionArgs])]] = mutable.Map()
timeLineVector.map(tuple => { timeLineVector.map(tuple => {
output(tuple._1 * config.tickfactor) = funcPointer output(tuple._1 * config.tickfactor) = funcPointer
if (!tuple._2.isEmpty && !output.contains(tuple._2.get)) output(tuple._2.get * config.tickfactor) = None // only set None when it doesnt exist yet if (!tuple._2.isEmpty && !output.contains(tuple._2.get)) output(tuple._2.get * config.tickfactor) = None // only set None when it doesnt exist yet
}) })
output.toMap output.toMap
} }
case _ => throw new Exception("Invalid Function: " + function.toString) case _ => throw new Exception("Invalid Function: " + function.toString)
} }
} }
val parsedFunction: mutable.Map[Int, Option[(SimulationBehaviour, Option[FunctionArgs])]] = mutable.Map().empty val parsedFunction: mutable.Map[Int, Option[(SimulationBehaviour, Option[FunctionArgs])]] = mutable.Map().empty
// TODO: This will currently overwrite already assigned values; output warning // TODO: This will currently overwrite already assigned values; output warning
val a = functions.map(function => parseFunctionHelper(function)) val a = functions.map(function => parseFunctionHelper(function))
a.foreach(e => e.foreach(kv => { a.foreach(e => e.foreach(kv => {
if(!kv._2.isEmpty || !parsedFunction.contains(kv._1)) // only set None when it doesnt exist yet if(!kv._2.isEmpty || !parsedFunction.contains(kv._1)) // only set None when it doesnt exist yet
parsedFunction(kv._1) = kv._2 parsedFunction(kv._1) = kv._2
} )) } ))
val timeVector: mutable.ArraySeq[Option[(SimulationBehaviour, Option[FunctionArgs])]] = new mutable.ArraySeq[Option[(SimulationBehaviour, Option[FunctionArgs])]](config.getDuration).map(_ => None) val timeVector: mutable.ArraySeq[Option[(SimulationBehaviour, Option[FunctionArgs])]] = new mutable.ArraySeq[Option[(SimulationBehaviour, Option[FunctionArgs])]](config.getDuration).map(_ => None)
val it = parsedFunction.toList.sortBy(_._1).iterator val it = parsedFunction.toList.sortBy(_._1).iterator
var current = if(it.hasNext) Some(it.next) else None var current = if(it.hasNext) Some(it.next) else None
while(!current.isEmpty) { while(!current.isEmpty) {
val next = if(it.hasNext) Some(it.next) else None val next = if(it.hasNext) Some(it.next) else None
for(time <- current.get._1 to (if(!next.isEmpty && next.get._1 < config.getDuration) next.get._1 else config.getDuration - 1)) { for(time <- current.get._1 to (if(!next.isEmpty && next.get._1 < config.getDuration) next.get._1 else config.getDuration - 1)) {
timeVector(time) = current.get._2 timeVector(time) = current.get._2
} }
current = next current = next
} }
Behaviour(BehaviourVector(timeVector.toVector)) Behaviour(BehaviourVector(timeVector.toVector))
} }
private def parseBehaviour(behaviourMap: Map[String, Any]): Unit = { private def parseBehaviour(behaviourMap: Map[String, Any]): Unit = {
val groupString = behaviourMap("group").asInstanceOf[String] val groupString = behaviourMap("group").asInstanceOf[String]
// only continue parsing if sensors in this group exist // only continue parsing if sensors in this group exist
if(groups.contains(groupString)) { if(groups.contains(groupString)) {
val group: Group = groups(groupString) val group: Group = groups(groupString)
if(!behaviourListSensor.contains(group)) { if(!behaviourListSensor.contains(group)) {
behaviourListSensor(group) = parseFunction(behaviourMap("functions").asInstanceOf[Vector[Any]]) behaviourListSensor(group) = parseFunction(behaviourMap("functions").asInstanceOf[Vector[Any]])
} else { } else {
debugf(DebugType.WARNING, "Duplicate Sensor Behaviour in Group [%s]. Ignoring Behaviour: %s", groupString, behaviourMap.toString()) debugf(DebugType.WARNING, "Duplicate Sensor Behaviour in Group [%s]. Ignoring Behaviour: %s", groupString, behaviourMap.toString())
} }
} else { } else {
debugf(DebugType.WARNING, "Unused Sensor Behaviour, no sensor for group [%s] exists. Ignoring Behaviour: %s", groupString, behaviourMap.toString()) debugf(DebugType.WARNING, "Unused Sensor Behaviour, no sensor for group [%s] exists. Ignoring Behaviour: %s", groupString, behaviourMap.toString())
} }
} }
private def parseGroundTruth(groundTruthMap: Map[String, Any]): Unit = { private def parseGroundTruth(groundTruthMap: Map[String, Any]): Unit = {
val groupString = groundTruthMap("group").asInstanceOf[String] val groupString = groundTruthMap("group").asInstanceOf[String]
// only continue parsing if sensors in this group exist // only continue parsing if sensors in this group exist
if(groups.contains(groupString)) { if(groups.contains(groupString)) {
val group: Group = groups(groupString) val group: Group = groups(groupString)
if(group.parent.isEmpty) { if(group.parent.isEmpty) {
if(!behaviourListGroundTruth.contains(group)) { if(!behaviourListGroundTruth.contains(group)) {
group.parent = if(groundTruthMap.contains("parent")) Some(groundTruthMap("parent").asInstanceOf[String]) else None group.parent = if(groundTruthMap.contains("parent")) Some(groundTruthMap("parent").asInstanceOf[String]) else None
behaviourListGroundTruth(group) = parseFunction(groundTruthMap("delta").asInstanceOf[Vector[Any]]) behaviourListGroundTruth(group) = parseFunction(groundTruthMap("delta").asInstanceOf[Vector[Any]])
} else { } else {
debugf(DebugType.WARNING, "Duplicate Ground Truth in Group [%s]. Ignoring Ground Truth: %s", groupString, groundTruthMap.toString()) debugf(DebugType.WARNING, "Duplicate Ground Truth in Group [%s]. Ignoring Ground Truth: %s", groupString, groundTruthMap.toString())
} }
if(groundTruthMap.contains("truth")){ if(groundTruthMap.contains("truth")){
parseTruth(groundTruthMap("truth").asInstanceOf[Vector[Any]]).map(kv => { parseTruth(groundTruthMap("truth").asInstanceOf[Vector[Any]]).map(kv => {
if(groundTruthValues.contains(kv._1)) { if(groundTruthValues.contains(kv._1)) {
groundTruthValues(kv._1)(group) = kv._2 groundTruthValues(kv._1)(group) = kv._2
} else { } else {
groundTruthValues(kv._1) = mutable.Map[Group, Truth](group -> kv._2) groundTruthValues(kv._1) = mutable.Map[Group, Truth](group -> kv._2)
} }
}) })
} else if(group.parent.isEmpty) { } else if(group.parent.isEmpty) {
debugf(DebugType.WARNING, "Sensor Group [%s] has no parent and no ground truth values", groupString) debugf(DebugType.WARNING, "Sensor Group [%s] has no parent and no ground truth values", groupString)
} }
} else { } else {
debugf(DebugType.WARNING, "Unused Ground Truth in Group [%s] because the Sensor has Parent [%s]. Ignoring Ground Truth: %s", groupString, group.parent.get, groundTruthMap.toString()) debugf(DebugType.WARNING, "Unused Ground Truth in Group [%s] because the Sensor has Parent [%s]. Ignoring Ground Truth: %s", groupString, group.parent.get, groundTruthMap.toString())
} }
} else { } else {
debugf(DebugType.WARNING, "Unused Ground Truth, no sensor for group [%s] exists. Ignoring Ground Truth: %s", groupString, groundTruthMap.toString()) debugf(DebugType.WARNING, "Unused Ground Truth, no sensor for group [%s] exists. Ignoring Ground Truth: %s", groupString, groundTruthMap.toString())
} }
} }
private def getParentRoot(group: Group, groups: Map[String, Group], seen: Vector[String] = Vector()): Option[Group] = { private def getParentRoot(group: Group, groups: Map[String, Group], seen: Vector[String] = Vector()): Option[Group] = {
if(seen.contains(group.name)) { if(seen.contains(group.name)) {
debugf(DebugType.WARNING, "Circular dependency of groups: [%s]", seen.toString()) debugf(DebugType.WARNING, "Circular dependency of groups: [%s]", seen.toString())
None None
} else if(!groups.contains(group.name)) { // this should not happen and be caught earlier } else if(!groups.contains(group.name)) { // this should not happen and be caught earlier
debugf(DebugType.WARNING, "Group [%s] ignored, it is not part of the sensor groups [%s]", group.toString, groups.toString()) debugf(DebugType.WARNING, "Group [%s] ignored, it is not part of the sensor groups [%s]", group.toString, groups.toString())
None None
} else { } else {
if(group.parent.isEmpty) Some(group) else getParentRoot(groups(group.parent.get), groups, seen ++ Vector(group.name)) if(group.parent.isEmpty) Some(group) else getParentRoot(groups(group.parent.get), groups, seen ++ Vector(group.name))
} }
} }
private def parseTruth(truthElements: Vector[Any]): Map[Time, Truth] = { private def parseTruth(truthElements: Vector[Any]): Map[Time, Truth] = {
val parseTruthHelper = (truthElement: Any) => { val parseTruthHelper = (truthElement: Any) => {
truthElement match { truthElement match {
case truthObject: Vector[_] => { case truthObject: Vector[_] => {
val truth: Truth = Truth() val truth: Truth = Truth()
val start: Int = truthObject(0).asInstanceOf[Int] val start: Int = truthObject(0).asInstanceOf[Int]
// val end: Option[Int] = None // Not used // val end: Option[Int] = None // Not used
truth.set(parseDouble(truthObject(1))) truth.set(parseDouble(truthObject(1)))
Map[Time, Truth](config.time(start) -> truth) Map[Time, Truth](config.time(start) -> truth)
} }
case truthObject: Map[_, _] => { case truthObject: Map[_, _] => {
val truth: Truth = Truth() val truth: Truth = Truth()
lazy val endTruth: Truth = Truth() lazy val endTruth: Truth = Truth()
val truthObjectMap = truthObject.asInstanceOf[Map[String, Any]] val truthObjectMap = truthObject.asInstanceOf[Map[String, Any]]
val timeline: Vector[Any] = truthObjectMap("timeline").asInstanceOf[Vector[Any]] val timeline: Vector[Any] = truthObjectMap("timeline").asInstanceOf[Vector[Any]]
val timeLineVector: Vector[(Int, Option[Int])] = timeline.map(e => parseTimeLineElement(e)) val timeLineVector: Vector[(Int, Option[Int])] = timeline.map(e => parseTimeLineElement(e))
truth.set(parseDouble(truthObjectMap("value"))) truth.set(parseDouble(truthObjectMap("value")))
val output: mutable.Map[Time, Truth] = mutable.Map() val output: mutable.Map[Time, Truth] = mutable.Map()
timeLineVector.map(tuple => { timeLineVector.map(tuple => {
output(config.time(tuple._1)) = truth output(config.time(tuple._1)) = truth
if (!tuple._2.isEmpty) { if (!tuple._2.isEmpty) {
endTruth.set(None) endTruth.set(None)
output(config.time(tuple._2.get)) = endTruth output(config.time(tuple._2.get)) = endTruth
} }
}) })
output.toMap output.toMap
} }
case _ => throw new Exception("Invalid Truth: " + truthElement.toString) case _ => throw new Exception("Invalid Truth: " + truthElement.toString)
} }
} }
val parsedTruth: mutable.Map[Time,Truth] = mutable.Map().empty val parsedTruth: mutable.Map[Time,Truth] = mutable.Map().empty
// TODO: This will currently overwrite already assigned values; output warning // TODO: This will currently overwrite already assigned values; output warning
truthElements.map(truth => parseTruthHelper(truth)).foreach(e => e.foreach(kv => parsedTruth(kv._1) = kv._2)) truthElements.map(truth => parseTruthHelper(truth)).foreach(e => e.foreach(kv => parsedTruth(kv._1) = kv._2))
parsedTruth.toMap parsedTruth.toMap
} }
def cellMapper(cell: Cell, groups: Map[Group, SensorCount]): Map[Group, List[(Group, Sensor, Option[GroundTruth])]] = { def cellMapper(cell: Cell, groups: Map[Group, SensorCount]): Map[Group, List[(Group, Sensor, Option[GroundTruth])]] = {
val groupMap: mutable.Map[Group, List[(Group, Sensor, Option[GroundTruth])]] = mutable.Map() val groupMap: mutable.Map[Group, List[(Group, Sensor, Option[GroundTruth])]] = mutable.Map()
val groupDone: mutable.HashSet[Group] = mutable.HashSet() val groupDone: mutable.HashSet[Group] = mutable.HashSet()
def groupMapper(group: Group, count: SensorCount): List[(Group, Sensor, Option[GroundTruth])] = { def groupMapper(group: Group, count: SensorCount): List[(Group, Sensor, Option[GroundTruth])] = {
if(groupDone.contains(group)) { if(groupDone.contains(group)) {
// if we've already seen the group, get the list so we can add a depended group // if we've already seen the group, get the list so we can add a depended group
val root = getParentRoot(group, this.groups.toMap) // get root if already added val root = getParentRoot(group, this.groups.toMap) // get root if already added
if(!root.isEmpty) { if(!root.isEmpty) {
groupMap(root.get) groupMap(root.get)
} else { List() } // circular dependency, just return empty list } else { List() } // circular dependency, just return empty list
} else { } else {
if(!behaviourListSensor.contains(group)) { if(!behaviourListSensor.contains(group)) {
debugf(DebugType.WARNING, "Group [%s] does not have a behaviour, using default", group.toString()) debugf(DebugType.WARNING, "Group [%s] does not have a behaviour, using default", group.toString())
behaviourListSensor(group) = Behaviour(BehaviourVector(Vector.tabulate(config.getDuration)(_ => None))) behaviourListSensor(group) = Behaviour(BehaviourVector(Vector.tabulate(config.getDuration)(_ => None)))
} }
if(group.parent.isEmpty) { if(group.parent.isEmpty) {
groupDone.add(group) groupDone.add(group)
List((group, Sensor(group, cell, count, behaviourListSensor(group), config.parSensor, config.sensorThreadPool), List((group, Sensor(group, cell, count, behaviourListSensor(group), config.parSensor, config.sensorThreadPool),
if(behaviourListGroundTruth.contains(group)) Some(GroundTruth(group, Some(behaviourListGroundTruth(group)))) else None)) if(behaviourListGroundTruth.contains(group)) Some(GroundTruth(group, Some(behaviourListGroundTruth(group)))) else None))
} else { } else {
groupDone.add(group) groupDone.add(group)
val parentGroup = this.groups(group.parent.get) // this should really exist at this point val parentGroup = this.groups(group.parent.get) // this should really exist at this point
val parentCount = if(groups.contains(parentGroup)) groups(parentGroup) else SensorCount(0) // insert dummy parent group if not in cell val parentCount = if(groups.contains(parentGroup)) groups(parentGroup) else SensorCount(0) // insert dummy parent group if not in cell
groupMapper(parentGroup, parentCount) ++ List((group, Sensor(group, cell, count, behaviourListSensor(group), config.parSensor, config.sensorThreadPool), groupMapper(parentGroup, parentCount) ++ List((group, Sensor(group, cell, count, behaviourListSensor(group), config.parSensor, config.sensorThreadPool),
if(behaviourListGroundTruth.contains(group)) Some(GroundTruth(group, Some(behaviourListGroundTruth(group)))) else None)) if(behaviourListGroundTruth.contains(group)) Some(GroundTruth(group, Some(behaviourListGroundTruth(group)))) else None))
} }
} }
} }
groups.foreach(kv => { groups.foreach(kv => {
val group = kv._1 val group = kv._1
val count = kv._2 val count = kv._2
val root = getParentRoot(group, this.groups.toMap) val root = getParentRoot(group, this.groups.toMap)
if(!groupDone.contains(group) && !root.isEmpty) { if(!groupDone.contains(group) && !root.isEmpty) {
groupMap(root.get) = groupMapper(group, count) groupMap(root.get) = groupMapper(group, count)
} }
}) })
groupMap.toMap groupMap.toMap
} }
} }

View File

@@ -163,7 +163,7 @@ case class FunctionArgs(args: Map[String, Any]) {
} }
case class BehaviourVector(function: Vector[Option[(SimulationBehaviour, Option[FunctionArgs])]], case class BehaviourVector(function: Vector[Option[(SimulationBehaviour, Option[FunctionArgs])]],
defaultFunc: SimulationBehaviour = (_: Cell, _: Time, v: Option[Double], _: Option[FunctionArgs]) => v, defaultFunc: SimulationBehaviour = (_: Cell, _: Time, _: Option[Double], _: Option[FunctionArgs]) => None,
defaultArgs: Option[FunctionArgs] = None) { defaultArgs: Option[FunctionArgs] = None) {
def get(t: Time): (SimulationBehaviour, Option[FunctionArgs]) = { def get(t: Time): (SimulationBehaviour, Option[FunctionArgs]) = {
function(t.time).getOrElse((defaultFunc, defaultArgs)) function(t.time).getOrElse((defaultFunc, defaultArgs))

View File

@@ -438,4 +438,30 @@ package object parserUtil {
case _ => None case _ => None
} }
} }
}
package object io {
import java.io._
case class OutputFile(fileName: String = "output.txt", newFile: Boolean = true) {
val file = new File(fileName)
val buffer: mutable.StringBuilder = new mutable.StringBuilder()
if(newFile) {
val fw = new FileWriter(file)
fw.write("")
fw.close()
}
def buffer(text: String): Unit = {
buffer.append(text)
}
def write(): Unit = {
val fwAppend = new FileWriter(file, true)
fwAppend.write(buffer.toString())
fwAppend.close()
buffer.clear()
}
}
} }