[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:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -40,35 +37,43 @@ sensors:
|
|||||||
# 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]
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ package object behaviour {
|
|||||||
|
|
||||||
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")
|
||||||
@@ -177,7 +177,7 @@ package object behaviour {
|
|||||||
|
|
||||||
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))
|
||||||
|
|||||||
@@ -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")
|
||||||
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu
|
|||||||
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
|
||||||
@@ -150,7 +150,7 @@ class SimulationConfigParser(input: Map[String, Any], simulationBehaviours: Simu
|
|||||||
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]]
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -439,3 +439,29 @@ package object parserUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user