185 lines
11 KiB
Markdown
185 lines
11 KiB
Markdown
# 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)
|
|
|