[add] added missing tickfactor parsing
- override Time compares to only use Time.time - added Option[Int] tickfactor to Time class - behaviours need to be selfaware and use Time.tickfactor - addded tickfactor to simulation time vector - added parser tickfactor parsing - modified output to use tickfactor aware duration
This commit is contained in:
@@ -83,9 +83,9 @@ package object behaviour {
|
|||||||
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.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.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)
|
||||||
}
|
}
|
||||||
@@ -163,7 +163,7 @@ package object behaviour {
|
|||||||
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.time)) {
|
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 }
|
||||||
@@ -179,7 +179,7 @@ package object behaviour {
|
|||||||
if(inTruth.isEmpty) return None
|
if(inTruth.isEmpty) return None
|
||||||
val range: Double = args.getParam("range", 1.0)
|
val range: Double = args.getParam("range", 1.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.time + 1)
|
val targetTime: Int = args.getParam("targetTime", time.getNormalized + 1) * time.tickfactor.getOrElse(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")
|
||||||
@@ -190,7 +190,7 @@ package object behaviour {
|
|||||||
} else { 0 }
|
} else { 0 }
|
||||||
|
|
||||||
// only change truth on offsetTimes
|
// only change truth on offsetTimes
|
||||||
val truth = if(!offsetTimes.isEmpty && offsetTimes.get.contains(time.time)) 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)
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import scim.lib.consoleUtil.Color._
|
|||||||
|
|
||||||
class Output(sharedMemory: SharedMemory, printOutput: Boolean = true) extends Runnable {
|
class Output(sharedMemory: SharedMemory, printOutput: Boolean = true) extends Runnable {
|
||||||
def run(): Unit = {
|
def run(): Unit = {
|
||||||
val duration = sharedMemory.config.duration
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector.tabulate(sharedMemory.config.duration)(t => Time(t, duration)).foreach(outputHelper)
|
Vector.tabulate(duration)(t => Time(t, duration)).foreach(outputHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = {
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu
|
|||||||
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.duration)(t => (Time(t, config.duration), 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()
|
||||||
@@ -61,7 +61,7 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu
|
|||||||
cell -> groupList.toMap
|
cell -> groupList.toMap
|
||||||
}
|
}
|
||||||
|
|
||||||
val sharedMemoryTruth = Vector.tabulate(config.duration)(t => (Time(t, config.duration), 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)
|
||||||
|
|
||||||
@@ -129,9 +129,9 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu
|
|||||||
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) * config.tickfactor, if(elementVector.size > 1) Some(elementVector(1) * config.tickfactor) 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") * config.tickfactor, if(elementMap.contains("end")) Some(elementMap("end") * config.tickfactor) else None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu
|
|||||||
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]
|
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
|
||||||
@@ -171,12 +171,12 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu
|
|||||||
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.duration).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.duration) next.get._1 else config.duration - 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
|
||||||
@@ -250,7 +250,7 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu
|
|||||||
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](Time(start, config.duration) -> truth)
|
Map[Time, Truth](config.time(start) -> truth)
|
||||||
}
|
}
|
||||||
case truthObject: Map[_, _] => {
|
case truthObject: Map[_, _] => {
|
||||||
val truth: Truth = Truth()
|
val truth: Truth = Truth()
|
||||||
@@ -262,10 +262,10 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu
|
|||||||
|
|
||||||
val output: mutable.Map[Time, Truth] = mutable.Map()
|
val output: mutable.Map[Time, Truth] = mutable.Map()
|
||||||
timeLineVector.map(tuple => {
|
timeLineVector.map(tuple => {
|
||||||
output(Time(tuple._1, config.duration)) = truth
|
output(config.time(tuple._1)) = truth
|
||||||
if (!tuple._2.isEmpty) {
|
if (!tuple._2.isEmpty) {
|
||||||
endTruth.set(None)
|
endTruth.set(None)
|
||||||
output(Time(tuple._2.get, config.duration)) = endTruth
|
output(config.time(tuple._2.get)) = endTruth
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
output.toMap
|
output.toMap
|
||||||
@@ -293,7 +293,7 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu
|
|||||||
} 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.duration)(_ => None)))
|
behaviourListSensor(group) = Behaviour(BehaviourVector(Vector.tabulate(config.getDuration)(_ => None)))
|
||||||
}
|
}
|
||||||
if(group.parent.isEmpty) {
|
if(group.parent.isEmpty) {
|
||||||
groupDone.add(group)
|
groupDone.add(group)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class Simulation(config: Configuration) {
|
|||||||
|
|
||||||
def start(): Unit = {
|
def start(): Unit = {
|
||||||
val duration = config.getDuration
|
val duration = config.getDuration
|
||||||
val timeVector = Vector.tabulate(duration)(t => Time(t, duration))
|
val timeVector = Vector.tabulate(duration)(t => Time(t, duration, Some(config.getTickFactor)))
|
||||||
def simTime(time: Time): Unit = {
|
def simTime(time: Time): Unit = {
|
||||||
// FIXME: really shouldn't have to .map(kv => kv._1 -> kv._2) to get an immutable map, in fact it shouldn't even be mutable in the first place...
|
// FIXME: really shouldn't have to .map(kv => kv._1 -> kv._2) to get an immutable map, in fact it shouldn't even be mutable in the first place...
|
||||||
val prevTruths: scala.collection.immutable.Map[Cell, Map[Group, Truth]] = if(time.start) sharedMemory.groundTruth(time).map(kv => kv._1 -> kv._2) else sharedMemory.groundTruth(time.prev).map(kv => kv._1 -> kv._2)
|
val prevTruths: scala.collection.immutable.Map[Cell, Map[Group, Truth]] = if(time.start) sharedMemory.groundTruth(time).map(kv => kv._1 -> kv._2) else sharedMemory.groundTruth(time.prev).map(kv => kv._1 -> kv._2)
|
||||||
|
|||||||
@@ -12,15 +12,31 @@ import scala.util.control.NonFatal
|
|||||||
|
|
||||||
// Note: case classes implement equality and compare their values and not the memory address
|
// Note: case classes implement equality and compare their values and not the memory address
|
||||||
// It only compares vals from the constructor
|
// It only compares vals from the constructor
|
||||||
case class Time(time: Int, duration: Int) extends Ordered[Time]{
|
case class Time(time: Int, duration: Int, tickfactor: Option[Int] = None) extends Ordered[Time]{
|
||||||
def prev: Time = { Time(time - 1, duration) }
|
def prev: Time = Time(time - 1, duration, tickfactor)
|
||||||
|
def prevTickfactor = Time(time - 1 * tickfactor.getOrElse(1), duration, tickfactor)
|
||||||
|
def next: Time = Time(time + 1, duration, tickfactor)
|
||||||
|
def nextTickfactor = Time(time + 1 * tickfactor.getOrElse(1), duration, tickfactor)
|
||||||
def start: Boolean = { time == 0 }
|
def start: Boolean = { time == 0 }
|
||||||
def get: Int = { time }
|
def get: Int = { time }
|
||||||
|
def getNormalized: Int = time / tickfactor.getOrElse(1)
|
||||||
|
def getNormalized(collision: Option[Int] = None): Option[Int] = {
|
||||||
|
if((time % tickfactor.getOrElse(1) )== 0) Some(time / tickfactor.getOrElse(1)) else collision
|
||||||
|
}
|
||||||
|
def newTime(t: Int, normalized: Boolean = true): Time = if(normalized) Time(t * tickfactor.getOrElse(1), duration, tickfactor) else Time(t, duration, tickfactor)
|
||||||
|
override def equals(obj: Any): Boolean = {
|
||||||
|
obj match {
|
||||||
|
case that: Time => time.equals(that.time)
|
||||||
|
case that: Int => time.equals(that * tickfactor.getOrElse(1))
|
||||||
|
case _ => false
|
||||||
|
}
|
||||||
|
}
|
||||||
override def compare(that: Time): Int = {
|
override def compare(that: Time): Int = {
|
||||||
time.compare(that.time)
|
time.compare(that.time)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def hashCode: Int = time.hashCode()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// while it would seem convinient to just throw the sensors/behaviours as value into these
|
// while it would seem convinient to just throw the sensors/behaviours as value into these
|
||||||
@@ -81,6 +97,11 @@ case class SimulationConfig(duration: Int, runs: Int, tickfactor: Int = 1,
|
|||||||
val cellThreadPool: ForkJoinTaskSupport = new ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(parCellThreads))
|
val cellThreadPool: ForkJoinTaskSupport = new ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(parCellThreads))
|
||||||
val groupThreadPool: ForkJoinTaskSupport = new ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(parGroupThreads))
|
val groupThreadPool: ForkJoinTaskSupport = new ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(parGroupThreads))
|
||||||
val sensorThreadPool: ForkJoinTaskSupport = new ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(parSensorThreads))
|
val sensorThreadPool: ForkJoinTaskSupport = new ForkJoinTaskSupport(new scala.concurrent.forkjoin.ForkJoinPool(parSensorThreads))
|
||||||
|
def time(t: Int, useTickfactor: Boolean = true): Time = {
|
||||||
|
if (useTickfactor) Time(t * tickfactor, getDuration * tickfactor, Some(tickfactor))
|
||||||
|
else Time(t, getDuration * tickfactor, Some(tickfactor))
|
||||||
|
}
|
||||||
|
def getDuration: Int = duration * tickfactor
|
||||||
}
|
}
|
||||||
case class SensorResult() extends Result[(Group, Cell, Option[Double])]
|
case class SensorResult() extends Result[(Group, Cell, Option[Double])]
|
||||||
/* rough size estimation
|
/* rough size estimation
|
||||||
@@ -195,7 +216,7 @@ case class SensorCount(count: Int) {
|
|||||||
|
|
||||||
// NOTE: The Group is actually obsolete in the Tuple3 as Sensor and GroundTruth hold theirs (unless removed from their case class)
|
// NOTE: The Group is actually obsolete in the Tuple3 as Sensor and GroundTruth hold theirs (unless removed from their case class)
|
||||||
case class Configuration(cells: Map[Cell, Map[Group, List[(Group, Sensor, Option[GroundTruth])]]], sharedMemory: SharedMemory) extends SimConfig {
|
case class Configuration(cells: Map[Cell, Map[Group, List[(Group, Sensor, Option[GroundTruth])]]], sharedMemory: SharedMemory) extends SimConfig {
|
||||||
def getDuration: Int = sharedMemory.config.duration
|
def getDuration: Int = sharedMemory.config.duration * sharedMemory.config.tickfactor
|
||||||
def getRuns: Int = sharedMemory.config.runs
|
def getRuns: Int = sharedMemory.config.runs
|
||||||
def getTickFactor: Int = sharedMemory.config.tickfactor
|
def getTickFactor: Int = sharedMemory.config.tickfactor
|
||||||
def getCellThreadConfig: (Int, ForkJoinTaskSupport) = (sharedMemory.config.parCell, sharedMemory.config.cellThreadPool)
|
def getCellThreadConfig: (Int, ForkJoinTaskSupport) = (sharedMemory.config.parCell, sharedMemory.config.cellThreadPool)
|
||||||
|
|||||||
@@ -34,6 +34,10 @@ package object configInterfaces {
|
|||||||
def getDuration: Int
|
def getDuration: Int
|
||||||
def getRuns: Int
|
def getRuns: Int
|
||||||
def getTickFactor: Int
|
def getTickFactor: Int
|
||||||
|
def newTime(t: Int, tickfactor: Boolean = true): Time = {
|
||||||
|
if (tickfactor) Time(t * getTickFactor, getDuration * getTickFactor, Some(getTickFactor))
|
||||||
|
else Time(t, getDuration * getTickFactor, Some(getTickFactor))
|
||||||
|
}
|
||||||
def getCellThreadConfig: (Int, ForkJoinTaskSupport)
|
def getCellThreadConfig: (Int, ForkJoinTaskSupport)
|
||||||
def getGroupThreadConfig: (Int, ForkJoinTaskSupport)
|
def getGroupThreadConfig: (Int, ForkJoinTaskSupport)
|
||||||
def getSensorThreadConfig: (Int, ForkJoinTaskSupport)
|
def getSensorThreadConfig: (Int, ForkJoinTaskSupport)
|
||||||
|
|||||||
Reference in New Issue
Block a user