[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
@@ -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]

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

@@ -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))

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

@@ -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]]

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

@@ -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()
}
}
}