# Configuration File ## Simulation * `duration`:`int` - number of simulation ticks, after which everything will be reset to the initial configuration * `tickfactor`:`int` - used to artificially add ticks by repeating the result of a simulation tick by the given value * `loop`:`bool` - loop the simulation * `loopcount`:`int` - amount of loops, `0` for infinite ## Sensors Elements: * `group`:`String` - used to assign ground truth and behaviour to the sensors. Any String can be used, internally assigned an `int`, by adding all groups to an array and using their index * `cells`:`array` _[optional]_ - holds the x-/y-coordinates and count of the sensors in the group, only optional if `blocks` is present * `x`:`int` - x-coordinate * `y`:`int` - y-coordinate * `count`:`int` - count of the sensors in this cell Alternative condensed notation as `array`: `[x, y, count]` * `blocks`:`array` _[optional]_ - holds start-/end-coordinates and count of sensors in the group, only optional if `cells` is present * `start`:`[int, int]` - start coordinates `[x, y]` of a block * `end`:`[int, int]` - end coordinates `[x, y]` of a block, can be equal to `start` (but not desirable) * `count`:`int` - count of the sensors in this block _Notes:_ Overlaps of `cells` and/or `blocks` within a `group` are possible (but not desirable). The result will be the sum of all `count` values. The distribution in `blocks` goes from left to right (`x`-axis), bottom to top (`y`-axis): Examples: 2x2 block with 3 sensors (labeled 1 to n): | | | |:---:|:---:| | 3 | | | 1 | 2 | If the `count` of sensors is greater than the amount of `cells` in a `block`, they get evenly distributed with any leftovers being distributed like the example above: 2x2 block with 11 sensors (labeled 1 to n): * amount of cells: 4 * `count`: 11 * evenly distributed: 2 per cell * leftovers: 3 (marked with `()`) | | | |:---:|:---:| | 7, 8, (9) | 10, 11 | | 1, 2, (3) | 4, 5, (6) | ## Ground Truth Elements: * `group`:`String` - sensor group assignment * `parent`:`String` _[optional]_ - parent group assignment. The parents group ground truth for the current simulation tick is calculated before this grout and passed to it * `value`:`double` - ground truth value * `timeline`:`array` - sets simulation tick when this ground truth is active and turns inactive. * `start`:`int` - simulation tick when this function is active * `end`:`int` _[optional]_ - last simulation tick before this function is inactive Alternative condensed of `timeline` notation as `array`: `[start, end]` Alternative condensed of `value` notation as `array`: `[start, value or None, ...]` * `delta`:`array` _[optional]_ - function to calculate `value` of the next simulation tick, only called if no value is defined for the next tick. Result is saved as new `value` and passed again to the function for the next tick. If no function is given, the `value` remains * `name`:`String` - name of the function. Needs to exist at compile time in a map: `["name" -> (value: double, parentValue: double, args: ...) => double]` * `args`:`array` - arguments passed to the function in addition to the `value` * `timeline`:`array` - sets simulation tick when this function is active and turns inactive. * `start`:`int` - simulation tick when this function is active * `end`:`int` _[optional]_ - last simulation tick before this function is inactive Alternative condensed of `timeline` notation as `array`: `[start, end]` Alternative condensed of `delta` notation as `array`: `[start, name or None, [args] or []]` * `cells`:`array` _[optional]_ - holds the x-/y-coordinates and count of assigned sensors in the group, only optional if `blocks` is present * `x`:`int` - x-coordinate * `y`:`int` - y-coordinate * `count`:`int` _[optional]_ - count of assigned sensors in this cell, only optional if `percent` is present * `percent`:`double` _[optional]_ - percent of assigned sensors (rounded) in this cell, only optional if `count` is present Alternative condensed notation as `array`: `[x, y, count]` * `blocks`:`array` _[optional]_ - holds start-/end-coordinates and count of assigned sensors in the group, only optional if `cells` is present * `start`:`[int, int]` - start coordinates `[x, y]` of a block * `end`:`[int, int]` - end coordinates `[x, y]` of a block, can be equal to `start` (but not desirable) * `count`:`int` _[optional]_ - count of assigned sensors in this block, only optional if `percent` is present * `percent`:`double` _[optional]_ - percent of assigned sensors (rounded) in this block, only optional if `count` is present _Notes:_ Overlaps of `cells` and/or `blocks` within a `group` are possible (but not desirable). The result will be the sum of all `count`, `percent` values. The distribution in `blocks` goes from left to right (`x`-axis), bottom to top (`y`-axis) (see above) ## Behaviour Elements: * `group`:`String` - sensor group assignment * `sharedObject`:`String` _[optional]_ _[experimental idea]_ - an object shared between sensor `behaviours`, can be waited up and synchronized. Use case example: cascading failure of sensors of different groups. _More of an idea, as this might be complex to implement in an asynchronous environment._ * `functions`:`array` _[optional]_ - function to calculate the output of a sensor on a given simulation tick. Function receives `ground truth` which can be `None`. Function returns `double` or `None`. If no function is given, `None` is returned as result for the tick * `name`:`String` - name of the function. Needs to exist at compile time in a map: `["name" -> (groundTruth: double, args: ...) => double]` * `args`:`array` - arguments passed to the function in addition to the `value` * `timeline`:`array` - sets simulation tick when this function is active and turns inactive. * `start`:`int` - simulation tick when this function is active * `end`:`int` _[optional]_ - last simulation tick before this function is inactive Alternative condensed of `timeline` notation as `array`: `[start, end]` Alternative condensed of `functions` notation as `array`: `[start, name or None, [args] or []]` * `cells`:`array` _[optional]_ - holds the x-/y-coordinates and count of assigned sensors in the group, only optional if `blocks` is present * `x`:`int` - x-coordinate * `y`:`int` - y-coordinate * `count`:`int` _[optional]_ - count of assigned sensors in this cell, only optional if `percent` is present * `percent`:`double` _[optional]_ - percent of assigned sensors (rounded) in this cell, only optional if `count` is present Alternative condensed notation as `array`: `[x, y, count]` * `blocks`:`array` _[optional]_ - holds start-/end-coordinates and count of assigned sensors in the group, only optional if `cells` is present * `start`:`[int, int]` - start coordinates `[x, y]` of a block * `end`:`[int, int]` - end coordinates `[x, y]` of a block, can be equal to `start` (but not desirable) * `count`:`int` _[optional]_ - count of assigned sensors in this block, only optional if `percent` is present * `percent`:`double` _[optional]_ - percent of assigned sensors (rounded) in this block, only optional if `count` is present _Notes:_ Overlaps of `cells` and/or `blocks` within a `group` are possible (but not desirable). The result will be the sum of all `count`, `percent` values. The distribution in `blocks` goes from left to right (`x`-axis), bottom to top (`y`-axis) (see above) ## Implementation Ideas ### general * the result `None` from a sensor `behaviour` is treated as if the sensor did not output any data * as `group` entries are not _[unique]_ it is possible to have multiple definitions of the same `group`. If any element of the group conflicts with another definition, the last declaration takes precedence * configuring failing sensors in a type of sensor can be achieved by creating two groups (working, failing), assigning the working group as parent of the failing und using a `delta` function that just returns the parent `ground truth` ### storing function<->timeline relations All timeline dependend elements (e.g. `groundtruth`, `delta`, `behaviour`) are held in an array of their respective group. The array is initialized with an `Option[T]` that is set to `None`. All elements get inserted with their corresponding `timeline: start` value and the `None` gets inserted with the `timeline: end+1` value. A modified binary search (that will return either the value if found or the next lower value) is then used to get the element at any given simulation tick: `[0:None, 30:factor, 61:None, 120:factor]` would correspond to: ```yml delta: - name: factor args: - 1.1 timeline: - start: 30 end: 60 - start: 120 ``` a lookup of `delta` at tick 20 would result in `None`, at tick 42 in `factor`, at tick 110 in `None`, anytime after 119 in `factor`. For the `groundtruth` and `behaviour` a `None` results in no output of the effected sensors. A `None` in the `delta` would result in no change of the `value`, but normal sensor output. Alternatively a `None` in `behaviour` could result in a default `behaviour`of the sensor. This default would have to be added to `sensor`. ### adding a sensor block from config 1. get the smallest `x` and `y` from `start` and `end` from a `blocks` element, save as `bottomleft` 2. save other `x` and `y` as `topright` 3. calculate the amount of cells in the block by multiplying the distance of the `x` and `y` pairs: ```scala // no abs() necessary as topright is always greater than or equal to topleft cellCount = (topright.x - bottomleft.x + 1) * (topright.x - bottomleft.x + 1) ``` 4. set amount of sensors per cell in `sensorsPerCell` ```pseudo if cellCount < count: sensorsPerCell = roundDown(count/cellCount) extraSensors = count - (sensorsPerCell*cellCount) else: sensorsPerCell = 1 ``` ```scala for { y <- bottomleft.y to topright.y x <- bottomleft.x to topright.x } { // exit condition for count < cellCount if (count <= 0) break else count -= 1 var sensors: Int = sensorsPerCell if (extraSensors > 0) { sensors += 1 extraSensors -= 1 } addSensors((x, y), sensors, ...) } ``` _TODO:_ Decide which structure to use: `Array.ofDim` vs `Array(Array())` vs `map` ### applying ground truth/behaviour to block fast: uneven distribution, start left to right, top to bottom and grab all sensors in each cell until done assigning required sensors slow: even distribution, iterate over block left to right, top to bottom and assign a single sensor (if available) in each cell, continue until done (possible optimisation: get all counts of sensors per cell, sort by size, delete smallest and assign that count on all available cells, subtract from remaining counts, repeat until done)