From a7817d04ecd188a92a6319afe65cdfc512fd66e3 Mon Sep 17 00:00:00 2001 From: Pascal Date: Wed, 29 Jan 2020 01:19:11 -0600 Subject: [PATCH] [add] added configuration mapping to parser --- docs/wiki/config/config.yml | 2 +- src/main/scala/scim/components/Parser.scala | 94 +++++++++++++++++++-- src/main/scala/scim/lib/Library.scala | 18 +++- 3 files changed, 104 insertions(+), 10 deletions(-) diff --git a/docs/wiki/config/config.yml b/docs/wiki/config/config.yml index 636fde9..0bd8d5e 100644 --- a/docs/wiki/config/config.yml +++ b/docs/wiki/config/config.yml @@ -65,4 +65,4 @@ behaviours: end: 166 - [100, normalValue, {v: 2}] - [120, addValuePercent, {}] - - [180, locationDefect, {x: 0, y: 2}] \ No newline at end of file + - [180, normalValue, {x: 0, y: 2}] \ No newline at end of file diff --git a/src/main/scala/scim/components/Parser.scala b/src/main/scala/scim/components/Parser.scala index ffb2fc9..6f818d5 100644 --- a/src/main/scala/scim/components/Parser.scala +++ b/src/main/scala/scim/components/Parser.scala @@ -1,9 +1,10 @@ package scim.components -import scim.datastruct.{Behaviour, BehaviourVector, Cell, Configuration, FunctionArgs, Group, SensorCount, SharedMemory, SimulationBehaviours, SimulationConfig, Time, Truth} +import scim.datastruct.{Behaviour, BehaviourVector, Cell, Configuration, FunctionArgs, GroundTruth, Group, Sensor, SensorCount, SharedMemory, SimulationBehaviours, SimulationConfig, Time, Truth} import scim.lib.ConcurrentBroadcast.BroadcastObject import scim.lib.debugUtil.{DebugType, debugf} import scim.lib.mapUtil._ +import scim.lib.parser._ import scim.lib.simulation.SimulationBehaviour import scim.lib.yml @@ -31,7 +32,7 @@ class Parser() { class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: SimulationBehaviours) { private val config: SimulationConfig = parseSimulationConfig(input("simulation").asInstanceOf[Map[String, Any]]) private val groups = mutable.Map[String, Group]().empty - private val sensorList = mutable.Map[Group, Map[Cell, SensorCount]]().empty + private val sensorList = mutable.Map[Cell, mutable.Map[Group, SensorCount]]().empty private val behaviourListSensor = 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 @@ -50,6 +51,16 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu // 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 none, but later again with value 9, OVERWRITE + + //populate configuration + val configurationMapping = sensorList.map(cell => cell._1 -> cellMapper(cell._1, cell._2.toMap)).toMap + + + + + //TODO: initialize shared memory + //TODO: add ground truth preconfigured to shared memory + Unit } @@ -74,7 +85,20 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu val group = Group(groupString, parentString) if(!groups.contains(groupString)) { groups(groupString) = group - sensorList(group) = cells + cells.foreach(kv => { + val cell = kv._1 + val count = kv._2 + if(sensorList.contains(cell)) { + if(sensorList(cell).contains(group)) { + // just add sensor count if one was already defined + sensorList(cell)(group) = sensorList(cell)(group).add(count) + } else { + sensorList(cell)(group) = count + } + } else { + sensorList(cell) = mutable.Map(group -> count) + } + }) } else { debugf(DebugType.WARNING, "Duplicate Sensors in Group [%s]. Ignoring Sensor: %s", groupString, sensor.toString()) } @@ -113,7 +137,7 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu 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 if (!simulationBehaviours.get(name).isEmpty) funcPointer = Some((simulationBehaviours.get(name).get, args)) - Map[Int, Option[(SimulationBehaviour, Option[FunctionArgs])]](start -> funcPointer) + Map[Int, Option[(SimulationBehaviour, Option[FunctionArgs])]](start -> funcPointer) // using a map here since the second case can produces multiple keys } case function: Map[_, _] => { val functionMap = function.asInstanceOf[Map[String, Any]] @@ -125,7 +149,7 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu val output: mutable.Map[Int, Option[(SimulationBehaviour, Option[FunctionArgs])]] = mutable.Map() timeLineVector.map(tuple => { output(tuple._1) = funcPointer - if (!tuple._2.isEmpty) output(tuple._2.get) = None + if (!tuple._2.isEmpty && !output.contains(tuple._2.get)) output(tuple._2.get) = None // only set None when it doesnt exist yet }) output.toMap } @@ -134,7 +158,11 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu } val parsedFunction: mutable.Map[Int, Option[(SimulationBehaviour, Option[FunctionArgs])]] = mutable.Map().empty // TODO: This will currently overwrite already assigned values; output warning - functions.map(function => parseFunctionHelper(function)).foreach(e => e.foreach(kv => parsedFunction(kv._1) = kv._2)) + val a = functions.map(function => parseFunctionHelper(function)) + a.foreach(e => e.foreach(kv => { + if(!kv._2.isEmpty || !parsedFunction.contains(kv._1)) // only set None when it doesnt exist yet + parsedFunction(kv._1) = kv._2 + } )) val timeVector: mutable.ArraySeq[Option[(SimulationBehaviour, Option[FunctionArgs])]] = new mutable.ArraySeq[Option[(SimulationBehaviour, Option[FunctionArgs])]](config.duration).map(_ => None) val it = parsedFunction.toList.sortBy(_._1).iterator println(parsedFunction.toList.sortBy(_._1)) @@ -191,6 +219,18 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu } } + private def getParentRoot(group: Group, groups: Map[String, Group], seen: Vector[String] = Vector()): Option[Group] = { + if(seen.contains(group.name)) { + debugf(DebugType.WARNING, "Circular dependency of groups: [%s]", seen.toString()) + None + } 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()) + None + } else { + 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] = { val parseTruthHelper = (truthElement: Any) => { truthElement match { @@ -199,7 +239,7 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu println(truthElement) val start: Int = truthObject(0).asInstanceOf[Int] // val end: Option[Int] = None // Not used - truth.set(Some(truthObject(1).asInstanceOf[Double])) + truth.set(parseDouble(truthObject(1))) Map[Time, Truth](Time(start, config.duration) -> truth) } case truthObject: Map[_, _] => { @@ -208,7 +248,7 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu val truthObjectMap = truthObject.asInstanceOf[Map[String, Any]] val timeline: Vector[Any] = truthObjectMap("timeline").asInstanceOf[Vector[Any]] val timeLineVector: Vector[(Int, Option[Int])] = timeline.map(e => parseTimeLineElement(e)) - truth.set(Some(truthObjectMap("value").asInstanceOf[Double])) + truth.set(parseDouble(truthObjectMap("value"))) val output: mutable.Map[Time, Truth] = mutable.Map() timeLineVector.map(tuple => { @@ -229,4 +269,42 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu parsedTruth.toMap } + 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 groupDone: mutable.HashSet[Group] = mutable.HashSet() + + def groupMapper(group: Group, count: SensorCount): List[(Group, Sensor, Option[GroundTruth])] = { + if(!behaviourListSensor.contains(group)) { + debugf(DebugType.WARNING, "Group [%s] does not have a behaviour, using default", group.toString()) + behaviourListSensor(group) = Behaviour(BehaviourVector(Vector.tabulate(config.duration)(_ => None))) + } + if(group.parent.isEmpty) { + groupDone.add(group) + List((group, Sensor(group, cell, count, behaviourListSensor(group), config.parSensor, config.sensorThreadPool), + if(behaviourListGroundTruth.contains(group)) Some(GroundTruth(group, Some(behaviourListGroundTruth(group)))) else None)) + } else { + if(groupDone.contains(group)) { + debugf(DebugType.WARNING, "Circular dependency of groups: [%s]", groupDone.toString()) + List() + } else { + groupDone.add(group) + 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 + 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)) + } + } + } + + groups.foreach(kv => { + val group = kv._1 + val count = kv._2 + val root = getParentRoot(group, this.groups.toMap) + if(!groupDone.contains(group) && !root.isEmpty) { + groupMap(root.get) = groupMapper(group, count) + } + }) + groupMap.toMap + } + } \ No newline at end of file diff --git a/src/main/scala/scim/lib/Library.scala b/src/main/scala/scim/lib/Library.scala index bbe78b0..07ce64f 100644 --- a/src/main/scala/scim/lib/Library.scala +++ b/src/main/scala/scim/lib/Library.scala @@ -290,7 +290,8 @@ package object mapUtil { val currentKey = currentKV.get._1 if(ordering.compare(key, currentKey) >= 0) { - // TODO: find out if scala actually does not check the second condition if first one fails and always in that order + // find out if scala actually does not check the second condition if first one fails and always in that order + // Scala does Short-circuit evaluation (like java) [Programming in Scala: Updated for Scala 2.12] page 126 while(!nextKV.isEmpty && ordering.compare(key, nextKV.get._1) >= 0) { // when trying to do this with recursion (shift than call this function again) resulted in a non reproducible stack overflow shift() @@ -325,4 +326,19 @@ package object simulation { import scim.datastruct._ // maybe change to by-name parameters? have to check performance gain type SimulationBehaviour = (Cell, Time, Option[Double], Option[FunctionArgs]) => Option[Double] +} + +package object parser { + def parseDouble(number: Any): Option[Double] = { + number match { + case t: java.lang.Number => Some(t.doubleValue()) + case _ => None + } + } + def parseInt(number: Any): Option[Int] = { + number match { + case t: java.lang.Number => Some(t.intValue()) + case _ => None + } + } } \ No newline at end of file